]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge tag 'v3.16-rc1' into i2c/for-next
authorWolfram Sang <wsa@the-dreams.de>
Tue, 17 Jun 2014 12:36:41 +0000 (14:36 +0200)
committerWolfram Sang <wsa@the-dreams.de>
Tue, 17 Jun 2014 12:37:31 +0000 (14:37 +0200)
Merge a stable base (Linux 3.16-rc1)

Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
2435 files changed:
.gitignore
Documentation/ABI/testing/sysfs-class-net
Documentation/ABI/testing/sysfs-class-net-cdc_ncm [new file with mode: 0644]
Documentation/ABI/testing/sysfs-class-net-queues [new file with mode: 0644]
Documentation/ABI/testing/sysfs-class-net-statistics [new file with mode: 0644]
Documentation/DocBook/80211.tmpl
Documentation/DocBook/drm.tmpl
Documentation/EDID/1024x768.S
Documentation/EDID/1280x1024.S
Documentation/EDID/1600x1200.S
Documentation/EDID/1680x1050.S
Documentation/EDID/1920x1080.S
Documentation/EDID/800x600.S [new file with mode: 0644]
Documentation/EDID/HOWTO.txt
Documentation/EDID/edid.S
Documentation/cpu-freq/cpu-drivers.txt
Documentation/devicetree/bindings/clock/sunxi.txt
Documentation/devicetree/bindings/clock/ti/apll.txt
Documentation/devicetree/bindings/clock/ti/dpll.txt
Documentation/devicetree/bindings/clock/ti/dra7-atl.txt [new file with mode: 0644]
Documentation/devicetree/bindings/clock/ti/gate.txt
Documentation/devicetree/bindings/clock/ti/interface.txt
Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt
Documentation/devicetree/bindings/leds/leds-lp55xx.txt
Documentation/devicetree/bindings/leds/leds-pwm.txt
Documentation/devicetree/bindings/mfd/twl4030-power.txt
Documentation/devicetree/bindings/net/amd-xgbe-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/amd-xgbe.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/broadcom-bcmgenet.txt
Documentation/devicetree/bindings/net/broadcom-systemport.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/can/xilinx_can.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/cpsw-phy-sel.txt
Documentation/devicetree/bindings/net/fixed-link.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/fsl-tsec-phy.txt
Documentation/devicetree/bindings/net/hisilicon-hix5hd2-gmac.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/ieee802154/at86rf230.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/micrel-ks8851.txt
Documentation/devicetree/bindings/net/micrel-ksz9021.txt [deleted file]
Documentation/devicetree/bindings/net/micrel-ksz90x1.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/nfc/pn544.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/nfc/st21nfca.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/nfc/trf7970a.txt
Documentation/devicetree/bindings/net/via-rhine.txt [new file with mode: 0644]
Documentation/devicetree/bindings/panel/auo,b133xtn01.txt [new file with mode: 0644]
Documentation/devicetree/bindings/panel/edt,et057090dhu.txt [new file with mode: 0644]
Documentation/devicetree/bindings/panel/edt,et070080dh6.txt [new file with mode: 0644]
Documentation/devicetree/bindings/panel/edt,etm0700g0dh6.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pci/designware-pcie.txt
Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pci/samsung,exynos5440-pcie.txt [new file with mode: 0644]
Documentation/devicetree/bindings/video/exynos_dp.txt
Documentation/devicetree/bindings/video/exynos_hdmi.txt
Documentation/driver-model/devres.txt
Documentation/filesystems/Locking
Documentation/filesystems/vfs.txt
Documentation/hwmon/shtc1 [new file with mode: 0644]
Documentation/kbuild/modules.txt
Documentation/kernel-parameters.txt
Documentation/kprobes.txt
Documentation/mutex-design.txt
Documentation/networking/bonding.txt
Documentation/networking/can.txt
Documentation/networking/cdc_mbim.txt [new file with mode: 0644]
Documentation/networking/filter.txt
Documentation/power/suspend-and-cpuhotplug.txt
Documentation/vDSO/parse_vdso.c
Documentation/vDSO/vdso_standalone_test_x86.c [new file with mode: 0644]
Documentation/vDSO/vdso_test.c
MAINTAINERS
Makefile
arch/arm/boot/dts/am33xx.dtsi
arch/arm/boot/dts/am4372.dtsi
arch/arm/boot/dts/armada-xp-matrix.dts
arch/arm/boot/dts/dra7xx-clocks.dtsi
arch/arm/boot/dts/omap54xx-clocks.dtsi
arch/arm/boot/dts/vt8500.dtsi
arch/arm/boot/dts/wm8650.dtsi
arch/arm/boot/dts/wm8850.dtsi
arch/arm/kernel/perf_event.c
arch/arm/kernel/perf_event_cpu.c
arch/arm/kernel/topology.c
arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c
arch/arm/mach-omap2/clock.h
arch/arm/mach-omap2/clock2xxx.h
arch/arm/mach-omap2/dpll3xxx.c
arch/arm/mach-tegra/board-paz00.c
arch/arm/net/bpf_jit_32.c
arch/blackfin/configs/BF526-EZBRD_defconfig
arch/blackfin/configs/BF527-EZKIT-V2_defconfig
arch/blackfin/configs/BF527-EZKIT_defconfig
arch/blackfin/configs/BF548-EZKIT_defconfig
arch/blackfin/configs/BF609-EZKIT_defconfig
arch/blackfin/configs/BlackStamp_defconfig
arch/blackfin/configs/H8606_defconfig
arch/blackfin/include/asm/dma.h
arch/blackfin/mach-bf533/boards/stamp.c
arch/mips/bcm47xx/sprom.c
arch/powerpc/Kconfig.debug
arch/powerpc/boot/Makefile
arch/powerpc/configs/chroma_defconfig [deleted file]
arch/powerpc/include/asm/cpm2.h
arch/powerpc/include/asm/eeh.h
arch/powerpc/include/asm/eeh_event.h
arch/powerpc/include/asm/mmu-book3e.h
arch/powerpc/include/asm/opal.h
arch/powerpc/include/asm/reg_a2.h
arch/powerpc/include/asm/switch_to.h
arch/powerpc/include/asm/wsp.h [deleted file]
arch/powerpc/include/uapi/asm/cputable.h
arch/powerpc/kernel/Makefile
arch/powerpc/kernel/cpu_setup_a2.S [deleted file]
arch/powerpc/kernel/cpu_setup_power.S
arch/powerpc/kernel/cputable.c
arch/powerpc/kernel/eeh.c
arch/powerpc/kernel/eeh_driver.c
arch/powerpc/kernel/eeh_event.c
arch/powerpc/kernel/eeh_pe.c
arch/powerpc/kernel/entry_64.S
arch/powerpc/kernel/exceptions-64e.S
arch/powerpc/kernel/exceptions-64s.S
arch/powerpc/kernel/head_40x.S
arch/powerpc/kernel/process.c
arch/powerpc/kernel/setup-common.c
arch/powerpc/kernel/smp.c
arch/powerpc/kernel/time.c
arch/powerpc/kernel/traps.c
arch/powerpc/kernel/udbg.c
arch/powerpc/kernel/udbg_16550.c
arch/powerpc/kvm/book3s_hv_ras.c
arch/powerpc/kvm/book3s_hv_rmhandlers.S
arch/powerpc/lib/sstep.c
arch/powerpc/net/bpf_jit_64.S
arch/powerpc/net/bpf_jit_comp.c
arch/powerpc/platforms/Kconfig
arch/powerpc/platforms/Kconfig.cputype
arch/powerpc/platforms/Makefile
arch/powerpc/platforms/cell/spufs/spufs.h
arch/powerpc/platforms/powernv/Kconfig
arch/powerpc/platforms/powernv/Makefile
arch/powerpc/platforms/powernv/eeh-ioda.c
arch/powerpc/platforms/powernv/opal-msglog.c
arch/powerpc/platforms/powernv/opal-sysparam.c
arch/powerpc/platforms/powernv/pci.c
arch/powerpc/platforms/powernv/setup.c
arch/powerpc/platforms/powernv/smp.c
arch/powerpc/platforms/pseries/Kconfig
arch/powerpc/platforms/wsp/Kconfig [deleted file]
arch/powerpc/platforms/wsp/Makefile [deleted file]
arch/powerpc/platforms/wsp/chroma.c [deleted file]
arch/powerpc/platforms/wsp/h8.c [deleted file]
arch/powerpc/platforms/wsp/ics.c [deleted file]
arch/powerpc/platforms/wsp/ics.h [deleted file]
arch/powerpc/platforms/wsp/msi.c [deleted file]
arch/powerpc/platforms/wsp/msi.h [deleted file]
arch/powerpc/platforms/wsp/opb_pic.c [deleted file]
arch/powerpc/platforms/wsp/psr2.c [deleted file]
arch/powerpc/platforms/wsp/scom_smp.c [deleted file]
arch/powerpc/platforms/wsp/scom_wsp.c [deleted file]
arch/powerpc/platforms/wsp/setup.c [deleted file]
arch/powerpc/platforms/wsp/smp.c [deleted file]
arch/powerpc/platforms/wsp/wsp.c [deleted file]
arch/powerpc/platforms/wsp/wsp.h [deleted file]
arch/powerpc/platforms/wsp/wsp_pci.c [deleted file]
arch/powerpc/platforms/wsp/wsp_pci.h [deleted file]
arch/powerpc/sysdev/fsl_soc.c
arch/powerpc/sysdev/xics/icp-native.c
arch/powerpc/xmon/nonstdio.c
arch/s390/net/bpf_jit_comp.c
arch/sparc/include/asm/checksum_32.h
arch/sparc/include/asm/checksum_64.h
arch/sparc/net/bpf_jit_comp.c
arch/tile/include/asm/thread_info.h
arch/tile/kernel/setup.c
arch/tile/kernel/signal.c
arch/tile/kernel/traps.c
arch/tile/kernel/unaligned.c
arch/tile/mm/init.c
arch/um/Makefile
arch/x86/Kconfig
arch/x86/include/asm/asm.h
arch/x86/include/asm/checksum_64.h
arch/x86/include/asm/kprobes.h
arch/x86/include/asm/qrwlock.h [new file with mode: 0644]
arch/x86/include/asm/spinlock.h
arch/x86/include/asm/spinlock_types.h
arch/x86/include/asm/traps.h
arch/x86/include/asm/uprobes.h
arch/x86/kernel/alternative.c
arch/x86/kernel/apic/hw_nmi.c
arch/x86/kernel/apic/io_apic.c
arch/x86/kernel/cpu/common.c
arch/x86/kernel/cpu/mshyperv.c
arch/x86/kernel/cpu/perf_event.c
arch/x86/kernel/cpu/perf_event_amd_ibs.c
arch/x86/kernel/cpu/perf_event_intel_lbr.c
arch/x86/kernel/dumpstack.c
arch/x86/kernel/early-quirks.c
arch/x86/kernel/entry_32.S
arch/x86/kernel/entry_64.S
arch/x86/kernel/hw_breakpoint.c
arch/x86/kernel/i8259.c
arch/x86/kernel/irq.c
arch/x86/kernel/kprobes/core.c
arch/x86/kernel/kprobes/ftrace.c
arch/x86/kernel/kprobes/opt.c
arch/x86/kernel/kvm.c
arch/x86/kernel/nmi.c
arch/x86/kernel/paravirt.c
arch/x86/kernel/process_64.c
arch/x86/kernel/traps.c
arch/x86/kernel/uprobes.c
arch/x86/lib/thunk_32.S
arch/x86/lib/thunk_64.S
arch/x86/mm/fault.c
arch/x86/net/bpf_jit.S
arch/x86/net/bpf_jit_comp.c
arch/x86/vdso/Makefile
arch/x86/vdso/vdso-fakesections.c [new file with mode: 0644]
arch/x86/vdso/vdso2c.c
arch/x86/vdso/vdso2c.h
block/blk-core.c
drivers/acpi/osl.c
drivers/acpi/sleep.c
drivers/acpi/video.c
drivers/atm/fore200e.c
drivers/atm/idt77252.c
drivers/base/power/main.c
drivers/base/syscore.c
drivers/block/nvme-core.c
drivers/block/nvme-scsi.c
drivers/block/rbd.c
drivers/bluetooth/ath3k.c
drivers/bluetooth/btmrvl_drv.h
drivers/bluetooth/btmrvl_main.c
drivers/bluetooth/btmrvl_sdio.c
drivers/bluetooth/btmrvl_sdio.h
drivers/bluetooth/btusb.c
drivers/bluetooth/hci_h4.c
drivers/char/hw_random/virtio-rng.c
drivers/char/raw.c
drivers/clk/sunxi/Makefile
drivers/clk/sunxi/clk-a10-hosc.c [new file with mode: 0644]
drivers/clk/sunxi/clk-a20-gmac.c [new file with mode: 0644]
drivers/clk/sunxi/clk-sun6i-apb0-gates.c [new file with mode: 0644]
drivers/clk/sunxi/clk-sun6i-apb0.c [new file with mode: 0644]
drivers/clk/sunxi/clk-sun6i-ar100.c [new file with mode: 0644]
drivers/clk/sunxi/clk-sunxi.c
drivers/clk/ti/Makefile
drivers/clk/ti/apll.c
drivers/clk/ti/clk-2xxx.c [new file with mode: 0644]
drivers/clk/ti/clk-43xx.c
drivers/clk/ti/clk-54xx.c
drivers/clk/ti/clk-7xx.c
drivers/clk/ti/clk-dra7-atl.c [new file with mode: 0644]
drivers/clk/ti/dpll.c
drivers/clk/ti/gate.c
drivers/clk/ti/interface.c
drivers/cpufreq/Kconfig
drivers/cpufreq/Kconfig.arm
drivers/cpufreq/cpufreq-cpu0.c
drivers/cpufreq/cpufreq.c
drivers/cpufreq/cpufreq_governor.c
drivers/cpufreq/cpufreq_governor.h
drivers/cpufreq/intel_pstate.c
drivers/cpufreq/ppc-corenet-cpufreq.c
drivers/cpufreq/tegra-cpufreq.c
drivers/cpuidle/cpuidle-powernv.c
drivers/cpuidle/driver.c
drivers/crypto/Kconfig
drivers/gpio/gpiolib.c
drivers/gpu/Makefile
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/armada/armada_drv.c
drivers/gpu/drm/armada/armada_fbdev.c
drivers/gpu/drm/armada/armada_gem.c
drivers/gpu/drm/ast/Makefile
drivers/gpu/drm/ast/ast_dp501.c [new file with mode: 0644]
drivers/gpu/drm/ast/ast_drv.c
drivers/gpu/drm/ast/ast_drv.h
drivers/gpu/drm/ast/ast_main.c
drivers/gpu/drm/ast/ast_mode.c
drivers/gpu/drm/ast/ast_post.c
drivers/gpu/drm/ast/ast_tables.h
drivers/gpu/drm/bochs/bochs_mm.c
drivers/gpu/drm/bridge/ptn3460.c
drivers/gpu/drm/cirrus/cirrus_main.c
drivers/gpu/drm/cirrus/cirrus_mode.c
drivers/gpu/drm/drm_bufs.c
drivers/gpu/drm/drm_cache.c
drivers/gpu/drm/drm_crtc.c
drivers/gpu/drm/drm_crtc_helper.c
drivers/gpu/drm/drm_dp_helper.c
drivers/gpu/drm/drm_edid.c
drivers/gpu/drm/drm_edid_load.c
drivers/gpu/drm/drm_fb_cma_helper.c
drivers/gpu/drm/drm_fb_helper.c
drivers/gpu/drm/drm_fops.c
drivers/gpu/drm/drm_gem.c
drivers/gpu/drm/drm_info.c
drivers/gpu/drm/drm_ioctl.c
drivers/gpu/drm/drm_irq.c
drivers/gpu/drm/drm_mipi_dsi.c
drivers/gpu/drm/drm_modes.c
drivers/gpu/drm/drm_modeset_lock.c [new file with mode: 0644]
drivers/gpu/drm/drm_pci.c
drivers/gpu/drm/drm_plane_helper.c
drivers/gpu/drm/drm_platform.c
drivers/gpu/drm/drm_probe_helper.c
drivers/gpu/drm/drm_stub.c
drivers/gpu/drm/drm_sysfs.c
drivers/gpu/drm/drm_usb.c
drivers/gpu/drm/exynos/Kconfig
drivers/gpu/drm/exynos/exynos_ddc.c [deleted file]
drivers/gpu/drm/exynos/exynos_dp_core.c
drivers/gpu/drm/exynos/exynos_dp_core.h
drivers/gpu/drm/exynos/exynos_dp_reg.c
drivers/gpu/drm/exynos/exynos_drm_core.c
drivers/gpu/drm/exynos/exynos_drm_crtc.c
drivers/gpu/drm/exynos/exynos_drm_crtc.h
drivers/gpu/drm/exynos/exynos_drm_dpi.c
drivers/gpu/drm/exynos/exynos_drm_drv.c
drivers/gpu/drm/exynos/exynos_drm_drv.h
drivers/gpu/drm/exynos/exynos_drm_dsi.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_gem.c
drivers/gpu/drm/exynos/exynos_drm_gsc.c
drivers/gpu/drm/exynos/exynos_drm_ipp.c
drivers/gpu/drm/exynos/exynos_drm_ipp.h
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_hdmi.h [deleted file]
drivers/gpu/drm/exynos/exynos_hdmiphy.c [deleted file]
drivers/gpu/drm/exynos/exynos_mixer.c
drivers/gpu/drm/exynos/regs-hdmi.h
drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c
drivers/gpu/drm/gma500/psb_drv.c
drivers/gpu/drm/i2c/tda998x_drv.c
drivers/gpu/drm/i810/i810_dma.c
drivers/gpu/drm/i915/Kconfig
drivers/gpu/drm/i915/Makefile
drivers/gpu/drm/i915/dvo_ch7xxx.c
drivers/gpu/drm/i915/dvo_ivch.c
drivers/gpu/drm/i915/dvo_ns2501.c
drivers/gpu/drm/i915/dvo_sil164.c
drivers/gpu/drm/i915/dvo_tfp410.c
drivers/gpu/drm/i915/i915_cmd_parser.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_dmabuf.c
drivers/gpu/drm/i915/i915_gem_execbuffer.c
drivers/gpu/drm/i915/i915_gem_gtt.c
drivers/gpu/drm/i915/i915_gem_gtt.h [new file with mode: 0644]
drivers/gpu/drm/i915/i915_gem_render_state.c [new file with mode: 0644]
drivers/gpu/drm/i915/i915_gem_userptr.c [new file with mode: 0644]
drivers/gpu/drm/i915/i915_gpu_error.c
drivers/gpu/drm/i915/i915_ioc32.c
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/i915_params.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/i915_suspend.c
drivers/gpu/drm/i915/i915_sysfs.c
drivers/gpu/drm/i915/i915_trace.h
drivers/gpu/drm/i915/intel_bios.c
drivers/gpu/drm/i915/intel_bios.h
drivers/gpu/drm/i915/intel_crt.c
drivers/gpu/drm/i915/intel_ddi.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_dsi.c
drivers/gpu/drm/i915/intel_dsi.h
drivers/gpu/drm/i915/intel_dsi_cmd.c
drivers/gpu/drm/i915/intel_dsi_cmd.h
drivers/gpu/drm/i915/intel_dsi_panel_vbt.c [new file with mode: 0644]
drivers/gpu/drm/i915/intel_dvo.c
drivers/gpu/drm/i915/intel_fbdev.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_renderstate.h [new file with mode: 0644]
drivers/gpu/drm/i915/intel_renderstate_gen6.c [new file with mode: 0644]
drivers/gpu/drm/i915/intel_renderstate_gen7.c [new file with mode: 0644]
drivers/gpu/drm/i915/intel_renderstate_gen8.c [new file with mode: 0644]
drivers/gpu/drm/i915/intel_ringbuffer.c
drivers/gpu/drm/i915/intel_ringbuffer.h
drivers/gpu/drm/i915/intel_sdvo.c
drivers/gpu/drm/i915/intel_sideband.c
drivers/gpu/drm/i915/intel_sprite.c
drivers/gpu/drm/i915/intel_tv.c
drivers/gpu/drm/i915/intel_uncore.c
drivers/gpu/drm/mga/mga_ioc32.c
drivers/gpu/drm/mga/mga_state.c
drivers/gpu/drm/mgag200/mgag200_main.c
drivers/gpu/drm/msm/Kconfig
drivers/gpu/drm/msm/Makefile
drivers/gpu/drm/msm/adreno/a3xx_gpu.c
drivers/gpu/drm/msm/hdmi/hdmi_connector.c
drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
drivers/gpu/drm/msm/msm_drv.c
drivers/gpu/drm/msm/msm_drv.h
drivers/gpu/drm/msm/msm_gem.h
drivers/gpu/drm/msm/msm_gem_submit.c
drivers/gpu/drm/msm/msm_gpu.c
drivers/gpu/drm/msm/msm_gpu.h
drivers/gpu/drm/msm/msm_perf.c [new file with mode: 0644]
drivers/gpu/drm/msm/msm_rd.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/Makefile
drivers/gpu/drm/nouveau/core/core/event.c
drivers/gpu/drm/nouveau/core/core/object.c
drivers/gpu/drm/nouveau/core/engine/device/gm100.c
drivers/gpu/drm/nouveau/core/engine/device/nv04.c
drivers/gpu/drm/nouveau/core/engine/device/nv10.c
drivers/gpu/drm/nouveau/core/engine/device/nv20.c
drivers/gpu/drm/nouveau/core/engine/device/nv30.c
drivers/gpu/drm/nouveau/core/engine/device/nv40.c
drivers/gpu/drm/nouveau/core/engine/device/nv50.c
drivers/gpu/drm/nouveau/core/engine/device/nvc0.c
drivers/gpu/drm/nouveau/core/engine/device/nve0.c
drivers/gpu/drm/nouveau/core/engine/disp/base.c
drivers/gpu/drm/nouveau/core/engine/disp/conn.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/disp/conn.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/disp/dport.c
drivers/gpu/drm/nouveau/core/engine/disp/dport.h
drivers/gpu/drm/nouveau/core/engine/disp/gm107.c
drivers/gpu/drm/nouveau/core/engine/disp/nv04.c
drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
drivers/gpu/drm/nouveau/core/engine/disp/nv50.h
drivers/gpu/drm/nouveau/core/engine/disp/nv84.c
drivers/gpu/drm/nouveau/core/engine/disp/nv94.c
drivers/gpu/drm/nouveau/core/engine/disp/nva0.c
drivers/gpu/drm/nouveau/core/engine/disp/nva3.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/disp/outp.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/disp/outp.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/disp/outpdp.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c
drivers/gpu/drm/nouveau/core/engine/disp/priv.h
drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c
drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c
drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c
drivers/gpu/drm/nouveau/core/engine/fifo/base.c
drivers/gpu/drm/nouveau/core/engine/fifo/gk20a.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c
drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c
drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c
drivers/gpu/drm/nouveau/core/engine/fifo/nve0.h
drivers/gpu/drm/nouveau/core/engine/graph/ctxgk20a.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/engine/graph/ctxnv108.c
drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.h
drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c
drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c
drivers/gpu/drm/nouveau/core/engine/graph/gk20a.c [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/nve4.c
drivers/gpu/drm/nouveau/core/engine/software/nv50.c
drivers/gpu/drm/nouveau/core/engine/software/nv50.h
drivers/gpu/drm/nouveau/core/engine/software/nvc0.c
drivers/gpu/drm/nouveau/core/include/core/class.h
drivers/gpu/drm/nouveau/core/include/core/event.h
drivers/gpu/drm/nouveau/core/include/engine/disp.h
drivers/gpu/drm/nouveau/core/include/engine/fifo.h
drivers/gpu/drm/nouveau/core/include/engine/graph.h
drivers/gpu/drm/nouveau/core/include/subdev/bios/conn.h
drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h
drivers/gpu/drm/nouveau/core/include/subdev/clock.h
drivers/gpu/drm/nouveau/core/include/subdev/fb.h
drivers/gpu/drm/nouveau/core/include/subdev/gpio.h
drivers/gpu/drm/nouveau/core/include/subdev/i2c.h
drivers/gpu/drm/nouveau/core/include/subdev/ibus.h
drivers/gpu/drm/nouveau/core/subdev/bar/base.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/conn.c
drivers/gpu/drm/nouveau/core/subdev/bios/dp.c
drivers/gpu/drm/nouveau/core/subdev/bios/init.c
drivers/gpu/drm/nouveau/core/subdev/clock/base.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/nvaa.c
drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c
drivers/gpu/drm/nouveau/core/subdev/clock/nve0.c
drivers/gpu/drm/nouveau/core/subdev/fb/gk20a.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/priv.h
drivers/gpu/drm/nouveau/core/subdev/fb/ramgk20a.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c
drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c
drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c
drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c
drivers/gpu/drm/nouveau/core/subdev/gpio/base.c
drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c
drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c
drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c
drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c
drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h
drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c
drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c
drivers/gpu/drm/nouveau/core/subdev/i2c/base.c
drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c
drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c
drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c
drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c
drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h
drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c
drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c
drivers/gpu/drm/nouveau/core/subdev/i2c/nve0.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/pad.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/pad.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/padnv04.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/padnv94.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/port.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/ibus/gk20a.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c
drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c
drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c
drivers/gpu/drm/nouveau/core/subdev/mxm/nv50.c
drivers/gpu/drm/nouveau/core/subdev/therm/ic.c
drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c
drivers/gpu/drm/nouveau/dispnv04/dac.c
drivers/gpu/drm/nouveau/dispnv04/dfp.c
drivers/gpu/drm/nouveau/dispnv04/disp.c
drivers/gpu/drm/nouveau/dispnv04/tvnv04.c
drivers/gpu/drm/nouveau/dispnv04/tvnv17.c
drivers/gpu/drm/nouveau/nouveau_connector.c
drivers/gpu/drm/nouveau/nouveau_connector.h
drivers/gpu/drm/nouveau/nouveau_crtc.h
drivers/gpu/drm/nouveau/nouveau_display.c
drivers/gpu/drm/nouveau/nouveau_dp.c
drivers/gpu/drm/nouveau/nouveau_encoder.h
drivers/gpu/drm/nouveau/nouveau_fence.c
drivers/gpu/drm/nouveau/nouveau_ioc32.c
drivers/gpu/drm/nouveau/nouveau_vga.c
drivers/gpu/drm/nouveau/nv50_display.c
drivers/gpu/drm/omapdrm/omap_crtc.c
drivers/gpu/drm/omapdrm/omap_drv.c
drivers/gpu/drm/omapdrm/omap_fb.c
drivers/gpu/drm/panel/panel-ld9040.c
drivers/gpu/drm/panel/panel-s6e8aa0.c
drivers/gpu/drm/panel/panel-simple.c
drivers/gpu/drm/qxl/qxl_display.c
drivers/gpu/drm/qxl/qxl_drv.c
drivers/gpu/drm/qxl/qxl_ioctl.c
drivers/gpu/drm/qxl/qxl_irq.c
drivers/gpu/drm/qxl/qxl_ttm.c
drivers/gpu/drm/r128/r128_ioc32.c
drivers/gpu/drm/r128/r128_state.c
drivers/gpu/drm/radeon/Makefile
drivers/gpu/drm/radeon/atombios_crtc.c
drivers/gpu/drm/radeon/atombios_dp.c
drivers/gpu/drm/radeon/atombios_encoders.c
drivers/gpu/drm/radeon/atombios_i2c.c
drivers/gpu/drm/radeon/cik.c
drivers/gpu/drm/radeon/cik_sdma.c
drivers/gpu/drm/radeon/cikd.h
drivers/gpu/drm/radeon/clearstate_cayman.h
drivers/gpu/drm/radeon/clearstate_ci.h
drivers/gpu/drm/radeon/clearstate_si.h
drivers/gpu/drm/radeon/dce3_1_afmt.c [new file with mode: 0644]
drivers/gpu/drm/radeon/evergreen.c
drivers/gpu/drm/radeon/evergreen_hdmi.c
drivers/gpu/drm/radeon/evergreend.h
drivers/gpu/drm/radeon/ni.c
drivers/gpu/drm/radeon/nid.h
drivers/gpu/drm/radeon/r100.c
drivers/gpu/drm/radeon/r300.c
drivers/gpu/drm/radeon/r600.c
drivers/gpu/drm/radeon/r600_hdmi.c
drivers/gpu/drm/radeon/r600d.h
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_agp.c
drivers/gpu/drm/radeon/radeon_asic.c
drivers/gpu/drm/radeon/radeon_asic.h
drivers/gpu/drm/radeon/radeon_bios.c
drivers/gpu/drm/radeon/radeon_connectors.c
drivers/gpu/drm/radeon/radeon_cs.c
drivers/gpu/drm/radeon/radeon_device.c
drivers/gpu/drm/radeon/radeon_display.c
drivers/gpu/drm/radeon/radeon_drv.c
drivers/gpu/drm/radeon/radeon_fence.c
drivers/gpu/drm/radeon/radeon_i2c.c
drivers/gpu/drm/radeon/radeon_ioc32.c
drivers/gpu/drm/radeon/radeon_irq_kms.c
drivers/gpu/drm/radeon/radeon_kms.c
drivers/gpu/drm/radeon/radeon_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_state.c
drivers/gpu/drm/radeon/radeon_uvd.c
drivers/gpu/drm/radeon/radeon_vce.c
drivers/gpu/drm/radeon/radeon_vm.c
drivers/gpu/drm/radeon/rs400.c
drivers/gpu/drm/radeon/rs600.c
drivers/gpu/drm/radeon/rv770.c
drivers/gpu/drm/radeon/si.c
drivers/gpu/drm/radeon/si_dma.c
drivers/gpu/drm/radeon/si_dpm.c
drivers/gpu/drm/radeon/sid.h
drivers/gpu/drm/radeon/uvd_v2_2.c
drivers/gpu/drm/rcar-du/Kconfig
drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
drivers/gpu/drm/savage/savage_bci.c
drivers/gpu/drm/shmobile/Kconfig
drivers/gpu/drm/shmobile/shmob_drm_crtc.c
drivers/gpu/drm/shmobile/shmob_drm_drv.c
drivers/gpu/drm/sis/sis_mm.c
drivers/gpu/drm/tegra/Makefile
drivers/gpu/drm/tegra/bus.c [deleted file]
drivers/gpu/drm/tegra/dc.c
drivers/gpu/drm/tegra/dc.h
drivers/gpu/drm/tegra/dpaux.c
drivers/gpu/drm/tegra/drm.c
drivers/gpu/drm/tegra/drm.h
drivers/gpu/drm/tegra/dsi.c
drivers/gpu/drm/tegra/dsi.h
drivers/gpu/drm/tegra/fb.c
drivers/gpu/drm/tegra/gem.c
drivers/gpu/drm/tegra/gr2d.c
drivers/gpu/drm/tegra/gr3d.c
drivers/gpu/drm/tegra/hdmi.c
drivers/gpu/drm/tegra/hdmi.h
drivers/gpu/drm/tegra/rgb.c
drivers/gpu/drm/tegra/sor.c
drivers/gpu/drm/tegra/sor.h
drivers/gpu/drm/tilcdc/tilcdc_drv.c
drivers/gpu/drm/udl/udl_main.c
drivers/gpu/drm/via/via_dma.c
drivers/gpu/drm/via/via_mm.c
drivers/gpu/drm/vmwgfx/Kconfig
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
drivers/gpu/host1x/bus.c
drivers/gpu/ipu-v3/Kconfig [new file with mode: 0644]
drivers/gpu/ipu-v3/Makefile [moved from drivers/staging/imx-drm/ipu-v3/Makefile with 51% similarity]
drivers/gpu/ipu-v3/ipu-common.c [moved from drivers/staging/imx-drm/ipu-v3/ipu-common.c with 94% similarity]
drivers/gpu/ipu-v3/ipu-dc.c [moved from drivers/staging/imx-drm/ipu-v3/ipu-dc.c with 99% similarity]
drivers/gpu/ipu-v3/ipu-di.c [moved from drivers/staging/imx-drm/ipu-v3/ipu-di.c with 99% similarity]
drivers/gpu/ipu-v3/ipu-dmfc.c [moved from drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c with 99% similarity]
drivers/gpu/ipu-v3/ipu-dp.c [moved from drivers/staging/imx-drm/ipu-v3/ipu-dp.c with 99% similarity]
drivers/gpu/ipu-v3/ipu-prv.h [moved from drivers/staging/imx-drm/ipu-v3/ipu-prv.h with 96% similarity]
drivers/gpu/ipu-v3/ipu-smfc.c [new file with mode: 0644]
drivers/gpu/vga/vga_switcheroo.c
drivers/hid/hid-lg4ff.c
drivers/hid/hid-picolcd_fb.c
drivers/hsi/clients/Kconfig
drivers/hsi/controllers/omap_ssi_port.c
drivers/hv/channel_mgmt.c
drivers/hv/hyperv_vmbus.h
drivers/hv/vmbus_drv.c
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/atxp1.c
drivers/hwmon/ina2xx.c
drivers/hwmon/lm85.c
drivers/hwmon/ltc4151.c
drivers/hwmon/shtc1.c [new file with mode: 0644]
drivers/hwmon/vexpress.c
drivers/infiniband/hw/cxgb4/cm.c
drivers/infiniband/hw/cxgb4/cq.c
drivers/infiniband/hw/cxgb4/iw_cxgb4.h
drivers/infiniband/hw/cxgb4/provider.c
drivers/infiniband/hw/cxgb4/t4.h
drivers/infiniband/hw/cxgb4/t4fw_ri_api.h
drivers/infiniband/ulp/ipoib/ipoib_ethtool.c
drivers/infiniband/ulp/iser/iser_initiator.c
drivers/infiniband/ulp/isert/ib_isert.c
drivers/infiniband/ulp/isert/ib_isert.h
drivers/isdn/capi/Kconfig
drivers/isdn/capi/capi.c
drivers/isdn/capi/capidrv.c
drivers/isdn/capi/capiutil.c
drivers/isdn/hisax/hfc4s8s_l1.c
drivers/isdn/i4l/isdn_ppp.c
drivers/isdn/mISDN/l1oip_core.c
drivers/leds/Kconfig
drivers/leds/Makefile
drivers/leds/dell-led.c
drivers/leds/leds-88pm860x.c
drivers/leds/leds-adp5520.c
drivers/leds/leds-bd2802.c
drivers/leds/leds-da903x.c
drivers/leds/leds-da9052.c
drivers/leds/leds-lp5523.c
drivers/leds/leds-pca9685.c [deleted file]
drivers/leds/leds-pwm.c
drivers/leds/leds-s3c24xx.c
drivers/leds/leds-sunfire.c
drivers/leds/trigger/ledtrig-cpu.c
drivers/md/dm-bio-prison.c
drivers/md/dm-bio-prison.h
drivers/md/dm-era-target.c
drivers/md/dm-mpath.c
drivers/md/dm-snap.c
drivers/md/dm-table.c
drivers/md/dm-thin.c
drivers/md/dm.c
drivers/media/platform/Kconfig
drivers/media/platform/omap3isp/Makefile
drivers/media/platform/omap3isp/isp.c
drivers/media/platform/omap3isp/isp.h
drivers/media/platform/omap3isp/ispccdc.c
drivers/media/platform/omap3isp/ispccdc.h
drivers/media/platform/omap3isp/ispccp2.c
drivers/media/platform/omap3isp/ispcsi2.c
drivers/media/platform/omap3isp/isph3a_aewb.c
drivers/media/platform/omap3isp/isph3a_af.c
drivers/media/platform/omap3isp/isppreview.c
drivers/media/platform/omap3isp/ispqueue.c [deleted file]
drivers/media/platform/omap3isp/ispqueue.h [deleted file]
drivers/media/platform/omap3isp/ispresizer.c
drivers/media/platform/omap3isp/ispstat.c
drivers/media/platform/omap3isp/ispstat.h
drivers/media/platform/omap3isp/ispvideo.c
drivers/media/platform/omap3isp/ispvideo.h
drivers/media/v4l2-core/videobuf2-core.c
drivers/mfd/twl4030-power.c
drivers/mmc/host/Kconfig
drivers/mmc/host/atmel-mci.c
drivers/mmc/host/mmc_spi.c
drivers/mmc/host/mvsdio.c
drivers/mmc/host/sdhci-msm.c
drivers/mmc/host/usdhi6rol0.c
drivers/mtd/devices/docg3.c
drivers/mtd/nand/nandsim.c
drivers/net/bonding/bond_3ad.c
drivers/net/bonding/bond_alb.c
drivers/net/bonding/bond_alb.h
drivers/net/bonding/bond_debugfs.c
drivers/net/bonding/bond_main.c
drivers/net/bonding/bond_netlink.c
drivers/net/bonding/bond_options.c
drivers/net/bonding/bond_options.h
drivers/net/bonding/bond_procfs.c
drivers/net/bonding/bond_sysfs.c
drivers/net/bonding/bond_sysfs_slave.c
drivers/net/bonding/bonding.h
drivers/net/can/Kconfig
drivers/net/can/Makefile
drivers/net/can/c_can/c_can.c
drivers/net/can/c_can/c_can.h
drivers/net/can/c_can/c_can_pci.c
drivers/net/can/c_can/c_can_platform.c
drivers/net/can/mscan/Kconfig
drivers/net/can/rcar_can.c [new file with mode: 0644]
drivers/net/can/softing/softing_main.c
drivers/net/can/spi/Kconfig [new file with mode: 0644]
drivers/net/can/spi/Makefile [new file with mode: 0644]
drivers/net/can/spi/mcp251x.c [moved from drivers/net/can/mcp251x.c with 96% similarity]
drivers/net/can/usb/Kconfig
drivers/net/can/usb/Makefile
drivers/net/can/usb/gs_usb.c [new file with mode: 0644]
drivers/net/can/usb/kvaser_usb.c
drivers/net/can/xilinx_can.c [new file with mode: 0644]
drivers/net/dsa/mv88e6123_61_65.c
drivers/net/dsa/mv88e6131.c
drivers/net/dsa/mv88e6xxx.c
drivers/net/ethernet/3com/3c509.c
drivers/net/ethernet/3com/3c589_cs.c
drivers/net/ethernet/3com/typhoon.c
drivers/net/ethernet/8390/ax88796.c
drivers/net/ethernet/Kconfig
drivers/net/ethernet/Makefile
drivers/net/ethernet/adaptec/starfire.c
drivers/net/ethernet/alteon/acenic.c
drivers/net/ethernet/altera/altera_sgdma.c
drivers/net/ethernet/altera/altera_tse_ethtool.c
drivers/net/ethernet/amd/Kconfig
drivers/net/ethernet/amd/Makefile
drivers/net/ethernet/amd/amd8111e.c
drivers/net/ethernet/amd/ariadne.c
drivers/net/ethernet/amd/au1000_eth.c
drivers/net/ethernet/amd/hplance.c
drivers/net/ethernet/amd/mvme147.c
drivers/net/ethernet/amd/nmclan_cs.c
drivers/net/ethernet/amd/xgbe/Makefile [new file with mode: 0644]
drivers/net/ethernet/amd/xgbe/xgbe-common.h [new file with mode: 0644]
drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c [new file with mode: 0644]
drivers/net/ethernet/amd/xgbe/xgbe-desc.c [new file with mode: 0644]
drivers/net/ethernet/amd/xgbe/xgbe-dev.c [new file with mode: 0644]
drivers/net/ethernet/amd/xgbe/xgbe-drv.c [new file with mode: 0644]
drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c [new file with mode: 0644]
drivers/net/ethernet/amd/xgbe/xgbe-main.c [new file with mode: 0644]
drivers/net/ethernet/amd/xgbe/xgbe-mdio.c [new file with mode: 0644]
drivers/net/ethernet/amd/xgbe/xgbe.h [new file with mode: 0644]
drivers/net/ethernet/arc/emac_main.c
drivers/net/ethernet/atheros/alx/main.c
drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c
drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c
drivers/net/ethernet/atheros/atlx/atl1.c
drivers/net/ethernet/atheros/atlx/atl2.c
drivers/net/ethernet/broadcom/Kconfig
drivers/net/ethernet/broadcom/Makefile
drivers/net/ethernet/broadcom/b44.c
drivers/net/ethernet/broadcom/bcm63xx_enet.c
drivers/net/ethernet/broadcom/bcmsysport.c [new file with mode: 0644]
drivers/net/ethernet/broadcom/bcmsysport.h [new file with mode: 0644]
drivers/net/ethernet/broadcom/bgmac.c
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_ethtool.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_file_hdr.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_init.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_init_ops.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
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/genet/bcmgenet.c
drivers/net/ethernet/broadcom/genet/bcmmii.c
drivers/net/ethernet/broadcom/tg3.c
drivers/net/ethernet/broadcom/tg3.h
drivers/net/ethernet/brocade/bna/bnad_ethtool.c
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/cxgb4/cxgb4.h
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
drivers/net/ethernet/chelsio/cxgb4/sge.c
drivers/net/ethernet/chelsio/cxgb4/t4_hw.h
drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
drivers/net/ethernet/chelsio/cxgb4vf/sge.c
drivers/net/ethernet/cisco/enic/enic.h
drivers/net/ethernet/cisco/enic/enic_dev.c
drivers/net/ethernet/cisco/enic/enic_dev.h
drivers/net/ethernet/cisco/enic/enic_ethtool.c
drivers/net/ethernet/cisco/enic/enic_main.c
drivers/net/ethernet/cisco/enic/vnic_cq.h
drivers/net/ethernet/cisco/enic/vnic_dev.c
drivers/net/ethernet/cisco/enic/vnic_dev.h
drivers/net/ethernet/davicom/dm9000.c
drivers/net/ethernet/dec/tulip/tulip_core.c
drivers/net/ethernet/dec/tulip/uli526x.c
drivers/net/ethernet/dlink/dl2k.c
drivers/net/ethernet/dlink/sundance.c
drivers/net/ethernet/ec_bhf.c
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/ftgmac100.c
drivers/net/ethernet/faraday/ftmac100.c
drivers/net/ethernet/freescale/Kconfig
drivers/net/ethernet/freescale/fec.h
drivers/net/ethernet/freescale/fec_main.c
drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
drivers/net/ethernet/freescale/gianfar.c
drivers/net/ethernet/freescale/ucc_geth.c
drivers/net/ethernet/freescale/ucc_geth_ethtool.c
drivers/net/ethernet/freescale/xgmac_mdio.c
drivers/net/ethernet/fujitsu/fmvj18x_cs.c
drivers/net/ethernet/hisilicon/Kconfig [new file with mode: 0644]
drivers/net/ethernet/hisilicon/Makefile [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hix5hd2_gmac.c [new file with mode: 0644]
drivers/net/ethernet/ibm/ehea/ehea_ethtool.c
drivers/net/ethernet/ibm/ehea/ehea_main.c
drivers/net/ethernet/ibm/ehea/ehea_qmr.c
drivers/net/ethernet/ibm/emac/core.c
drivers/net/ethernet/icplus/ipg.c
drivers/net/ethernet/intel/e100.c
drivers/net/ethernet/intel/e1000/e1000_ethtool.c
drivers/net/ethernet/intel/e1000/e1000_hw.c
drivers/net/ethernet/intel/e1000/e1000_main.c
drivers/net/ethernet/intel/e1000e/80003es2lan.c
drivers/net/ethernet/intel/e1000e/82571.c
drivers/net/ethernet/intel/e1000e/e1000.h
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/mac.c
drivers/net/ethernet/intel/e1000e/mac.h
drivers/net/ethernet/intel/e1000e/netdev.c
drivers/net/ethernet/intel/e1000e/nvm.c
drivers/net/ethernet/intel/e1000e/param.c
drivers/net/ethernet/intel/e1000e/phy.c
drivers/net/ethernet/intel/i40e/i40e.h
drivers/net/ethernet/intel/i40e/i40e_adminq.c
drivers/net/ethernet/intel/i40e/i40e_adminq.h
drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
drivers/net/ethernet/intel/i40e/i40e_common.c
drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c
drivers/net/ethernet/intel/i40e/i40e_debugfs.c
drivers/net/ethernet/intel/i40e/i40e_diag.c
drivers/net/ethernet/intel/i40e/i40e_ethtool.c
drivers/net/ethernet/intel/i40e/i40e_hmc.c
drivers/net/ethernet/intel/i40e/i40e_hmc.h
drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c
drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/i40e/i40e_prototype.h
drivers/net/ethernet/intel/i40e/i40e_ptp.c
drivers/net/ethernet/intel/i40e/i40e_register.h
drivers/net/ethernet/intel/i40e/i40e_txrx.c
drivers/net/ethernet/intel/i40e/i40e_txrx.h
drivers/net/ethernet/intel/i40e/i40e_type.h
drivers/net/ethernet/intel/i40e/i40e_virtchnl.h
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
drivers/net/ethernet/intel/i40evf/Makefile
drivers/net/ethernet/intel/i40evf/i40e_adminq.c
drivers/net/ethernet/intel/i40evf/i40e_adminq.h
drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
drivers/net/ethernet/intel/i40evf/i40e_alloc.h
drivers/net/ethernet/intel/i40evf/i40e_common.c
drivers/net/ethernet/intel/i40evf/i40e_hmc.h
drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h
drivers/net/ethernet/intel/i40evf/i40e_osdep.h
drivers/net/ethernet/intel/i40evf/i40e_prototype.h
drivers/net/ethernet/intel/i40evf/i40e_register.h
drivers/net/ethernet/intel/i40evf/i40e_status.h
drivers/net/ethernet/intel/i40evf/i40e_txrx.c
drivers/net/ethernet/intel/i40evf/i40e_txrx.h
drivers/net/ethernet/intel/i40evf/i40e_type.h
drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h
drivers/net/ethernet/intel/i40evf/i40evf.h
drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c
drivers/net/ethernet/intel/i40evf/i40evf_main.c
drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
drivers/net/ethernet/intel/igb/e1000_82575.c
drivers/net/ethernet/intel/igb/e1000_82575.h
drivers/net/ethernet/intel/igb/e1000_defines.h
drivers/net/ethernet/intel/igb/e1000_hw.h
drivers/net/ethernet/intel/igb/e1000_i210.c
drivers/net/ethernet/intel/igb/e1000_i210.h
drivers/net/ethernet/intel/igb/e1000_mac.c
drivers/net/ethernet/intel/igb/e1000_mac.h
drivers/net/ethernet/intel/igb/e1000_mbx.c
drivers/net/ethernet/intel/igb/e1000_mbx.h
drivers/net/ethernet/intel/igb/e1000_nvm.c
drivers/net/ethernet/intel/igb/e1000_nvm.h
drivers/net/ethernet/intel/igb/e1000_phy.c
drivers/net/ethernet/intel/igb/e1000_phy.h
drivers/net/ethernet/intel/igb/e1000_regs.h
drivers/net/ethernet/intel/igb/igb.h
drivers/net/ethernet/intel/igb/igb_ethtool.c
drivers/net/ethernet/intel/igb/igb_hwmon.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/ethernet/intel/igb/igb_ptp.c
drivers/net/ethernet/intel/igbvf/ethtool.c
drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c
drivers/net/ethernet/intel/ixgbe/ixgbe.h
drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c
drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
drivers/net/ethernet/intel/ixgbe/ixgbe_common.h
drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c
drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c
drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c
drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h
drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c
drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c
drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h
drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c
drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h
drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h
drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h
drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c
drivers/net/ethernet/intel/ixgbevf/ethtool.c
drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
drivers/net/ethernet/marvell/mv643xx_eth.c
drivers/net/ethernet/marvell/mvmdio.c
drivers/net/ethernet/marvell/mvneta.c
drivers/net/ethernet/marvell/pxa168_eth.c
drivers/net/ethernet/marvell/sky2.c
drivers/net/ethernet/mellanox/mlx4/cmd.c
drivers/net/ethernet/mellanox/mlx4/cq.c
drivers/net/ethernet/mellanox/mlx4/en_cq.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/mcg.c
drivers/net/ethernet/mellanox/mlx4/mlx4.h
drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
drivers/net/ethernet/mellanox/mlx4/mr.c
drivers/net/ethernet/mellanox/mlx4/port.c
drivers/net/ethernet/mellanox/mlx4/profile.c
drivers/net/ethernet/mellanox/mlx4/qp.c
drivers/net/ethernet/mellanox/mlx4/reset.c
drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
drivers/net/ethernet/mellanox/mlx5/core/cmd.c
drivers/net/ethernet/mellanox/mlx5/core/eq.c
drivers/net/ethernet/mellanox/mlx5/core/main.c
drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
drivers/net/ethernet/mellanox/mlx5/core/mr.c
drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
drivers/net/ethernet/mellanox/mlx5/core/qp.c
drivers/net/ethernet/micrel/ks8695net.c
drivers/net/ethernet/micrel/ks8851.c
drivers/net/ethernet/micrel/ksz884x.c
drivers/net/ethernet/microchip/enc28j60.c
drivers/net/ethernet/myricom/myri10ge/myri10ge.c
drivers/net/ethernet/natsemi/natsemi.c
drivers/net/ethernet/natsemi/ns83820.c
drivers/net/ethernet/neterion/s2io.c
drivers/net/ethernet/neterion/vxge/vxge-config.c
drivers/net/ethernet/neterion/vxge/vxge-ethtool.c
drivers/net/ethernet/neterion/vxge/vxge-main.c
drivers/net/ethernet/nvidia/forcedeth.c
drivers/net/ethernet/nxp/lpc_eth.c
drivers/net/ethernet/oki-semi/pch_gbe/Kconfig
drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c
drivers/net/ethernet/packetengines/hamachi.c
drivers/net/ethernet/packetengines/yellowfin.c
drivers/net/ethernet/qlogic/Kconfig
drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
drivers/net/ethernet/qlogic/qla3xxx.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_ctx.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
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/realtek/r8169.c
drivers/net/ethernet/renesas/sh_eth.c
drivers/net/ethernet/renesas/sh_eth.h
drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c
drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
drivers/net/ethernet/samsung/sxgbe/sxgbe_reg.h
drivers/net/ethernet/sfc/efx.c
drivers/net/ethernet/sfc/ethtool.c
drivers/net/ethernet/sfc/io.h
drivers/net/ethernet/sfc/siena_sriov.c
drivers/net/ethernet/sfc/tx.c
drivers/net/ethernet/sis/sis190.c
drivers/net/ethernet/smsc/smc91c92_cs.c
drivers/net/ethernet/smsc/smsc911x.c
drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.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/tehuti/tehuti.c
drivers/net/ethernet/ti/cpmac.c
drivers/net/ethernet/ti/cpsw-phy-sel.c
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/ti/cpts.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/tile/tilegx.c
drivers/net/ethernet/toshiba/ps3_gelic_net.c
drivers/net/ethernet/via/Kconfig
drivers/net/ethernet/via/via-rhine.c
drivers/net/ethernet/xilinx/ll_temac_main.c
drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
drivers/net/ethernet/xilinx/xilinx_emaclite.c
drivers/net/hyperv/hyperv_net.h
drivers/net/hyperv/netvsc.c
drivers/net/hyperv/netvsc_drv.c
drivers/net/hyperv/rndis_filter.c
drivers/net/ieee802154/at86rf230.c
drivers/net/ieee802154/fakelb.c
drivers/net/ieee802154/mrf24j40.c
drivers/net/irda/Kconfig
drivers/net/irda/via-ircc.c
drivers/net/irda/w83977af_ir.c
drivers/net/macvlan.c
drivers/net/ntb_netdev.c
drivers/net/phy/Kconfig
drivers/net/phy/Makefile
drivers/net/phy/amd-xgbe-phy.c [new file with mode: 0644]
drivers/net/phy/at803x.c
drivers/net/phy/fixed.c
drivers/net/phy/mdio_bus.c
drivers/net/phy/micrel.c
drivers/net/phy/phy_device.c
drivers/net/phy/realtek.c
drivers/net/phy/smsc.c
drivers/net/phy/vitesse.c
drivers/net/ppp/ppp_generic.c
drivers/net/ppp/pptp.c
drivers/net/rionet.c
drivers/net/team/team.c
drivers/net/team/team_mode_loadbalance.c
drivers/net/tun.c
drivers/net/usb/catc.c
drivers/net/usb/cdc_mbim.c
drivers/net/usb/cdc_ncm.c
drivers/net/usb/hso.c
drivers/net/usb/huawei_cdc_ncm.c
drivers/net/usb/ipheth.c
drivers/net/usb/kaweth.c
drivers/net/usb/pegasus.c
drivers/net/usb/qmi_wwan.c
drivers/net/usb/r8152.c
drivers/net/usb/rtl8150.c
drivers/net/virtio_net.c
drivers/net/vmxnet3/vmxnet3_ethtool.c
drivers/net/vxlan.c
drivers/net/wan/farsync.c
drivers/net/wan/sdla.c
drivers/net/wimax/i2400m/control.c
drivers/net/wimax/i2400m/driver.c
drivers/net/wireless/at76c50x-usb.c
drivers/net/wireless/at76c50x-usb.h
drivers/net/wireless/ath/ar5523/ar5523.c
drivers/net/wireless/ath/ath10k/bmi.c
drivers/net/wireless/ath/ath10k/bmi.h
drivers/net/wireless/ath/ath10k/ce.c
drivers/net/wireless/ath/ath10k/ce.h
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/debug.c
drivers/net/wireless/ath/ath10k/htc.c
drivers/net/wireless/ath/ath10k/htt.c
drivers/net/wireless/ath/ath10k/htt.h
drivers/net/wireless/ath/ath10k/htt_rx.c
drivers/net/wireless/ath/ath10k/htt_tx.c
drivers/net/wireless/ath/ath10k/hw.h
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/pci.c
drivers/net/wireless/ath/ath10k/pci.h
drivers/net/wireless/ath/ath10k/txrx.c
drivers/net/wireless/ath/ath10k/txrx.h
drivers/net/wireless/ath/ath10k/wmi.c
drivers/net/wireless/ath/ath10k/wmi.h
drivers/net/wireless/ath/ath5k/phy.c
drivers/net/wireless/ath/ath6kl/Kconfig
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath6kl/core.c
drivers/net/wireless/ath/ath6kl/debug.c
drivers/net/wireless/ath/ath6kl/debug.h
drivers/net/wireless/ath/ath6kl/hif.c
drivers/net/wireless/ath/ath6kl/hif.h
drivers/net/wireless/ath/ath6kl/htc_mbox.c
drivers/net/wireless/ath/ath6kl/htc_pipe.c
drivers/net/wireless/ath/ath6kl/init.c
drivers/net/wireless/ath/ath6kl/main.c
drivers/net/wireless/ath/ath6kl/sdio.c
drivers/net/wireless/ath/ath6kl/target.h
drivers/net/wireless/ath/ath6kl/txrx.c
drivers/net/wireless/ath/ath6kl/usb.c
drivers/net/wireless/ath/ath6kl/wmi.c
drivers/net/wireless/ath/ath6kl/wmi.h
drivers/net/wireless/ath/ath9k/Makefile
drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h
drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h
drivers/net/wireless/ath/ath9k/ar9330_1p2_initvals.h
drivers/net/wireless/ath/ath9k/ar9340_initvals.h
drivers/net/wireless/ath/ath9k/ar953x_initvals.h
drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/beacon.c
drivers/net/wireless/ath/ath9k/common-debug.c [new file with mode: 0644]
drivers/net/wireless/ath/ath9k/common-debug.h [new file with mode: 0644]
drivers/net/wireless/ath/ath9k/common.h
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/debug.h
drivers/net/wireless/ath/ath9k/dfs.c
drivers/net/wireless/ath/ath9k/htc.h
drivers/net/wireless/ath/ath9k/htc_drv_debug.c
drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/init.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/carl9170/main.c
drivers/net/wireless/ath/carl9170/usb.c
drivers/net/wireless/ath/dfs_pattern_detector.c
drivers/net/wireless/ath/wcn36xx/smd.c
drivers/net/wireless/ath/wil6210/cfg80211.c
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/pcie_bus.c
drivers/net/wireless/ath/wil6210/rx_reorder.c
drivers/net/wireless/ath/wil6210/txrx.c
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wmi.c
drivers/net/wireless/ath/wil6210/wmi.h
drivers/net/wireless/b43/Kconfig
drivers/net/wireless/b43/b43.h
drivers/net/wireless/b43/bus.h
drivers/net/wireless/b43/main.c
drivers/net/wireless/b43/phy_common.c
drivers/net/wireless/b43/phy_common.h
drivers/net/wireless/b43/phy_g.c
drivers/net/wireless/b43/phy_n.c
drivers/net/wireless/b43/phy_n.h
drivers/net/wireless/b43/radio_2056.c
drivers/net/wireless/b43/tables_nphy.c
drivers/net/wireless/b43/tables_nphy.h
drivers/net/wireless/b43/wa.c
drivers/net/wireless/b43/xmit.c
drivers/net/wireless/brcm80211/brcmfmac/Makefile
drivers/net/wireless/brcm80211/brcmfmac/dhd.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
drivers/net/wireless/brcm80211/brcmfmac/firmware.c [new file with mode: 0644]
drivers/net/wireless/brcm80211/brcmfmac/firmware.h [moved from drivers/net/wireless/brcm80211/brcmfmac/nvram.h with 52% similarity]
drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
drivers/net/wireless/brcm80211/brcmfmac/nvram.c [deleted file]
drivers/net/wireless/brcm80211/brcmfmac/usb.c
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
drivers/net/wireless/brcm80211/brcmsmac/main.c
drivers/net/wireless/brcm80211/brcmutil/d11.c
drivers/net/wireless/brcm80211/include/brcmu_d11.h
drivers/net/wireless/brcm80211/include/brcmu_wifi.h
drivers/net/wireless/cw1200/sta.c
drivers/net/wireless/cw1200/sta.h
drivers/net/wireless/hostap/hostap_main.c
drivers/net/wireless/iwlegacy/3945.c
drivers/net/wireless/iwlegacy/4965-mac.c
drivers/net/wireless/iwlegacy/common.c
drivers/net/wireless/iwlegacy/common.h
drivers/net/wireless/iwlwifi/Kconfig
drivers/net/wireless/iwlwifi/dvm/Makefile
drivers/net/wireless/iwlwifi/dvm/calib.c
drivers/net/wireless/iwlwifi/dvm/debugfs.c
drivers/net/wireless/iwlwifi/dvm/dev.h
drivers/net/wireless/iwlwifi/dvm/devices.c
drivers/net/wireless/iwlwifi/dvm/led.h
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/rxon.c
drivers/net/wireless/iwlwifi/dvm/scan.c
drivers/net/wireless/iwlwifi/dvm/sta.c
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-8000.c
drivers/net/wireless/iwlwifi/iwl-agn-hw.h
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-debug.c
drivers/net/wireless/iwlwifi/iwl-debug.h
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h [moved from drivers/net/wireless/iwlwifi/mvm/fw-error-dump.h with 82% similarity]
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/iwl-io.c
drivers/net/wireless/iwlwifi/iwl-io.h
drivers/net/wireless/iwlwifi/iwl-modparams.h
drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/iwlwifi/iwl-op-mode.h
drivers/net/wireless/iwlwifi/iwl-phy-db.c
drivers/net/wireless/iwlwifi/iwl-prph.h
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/iwlwifi/mvm/Makefile
drivers/net/wireless/iwlwifi/mvm/coex.c
drivers/net/wireless/iwlwifi/mvm/d3.c
drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c
drivers/net/wireless/iwlwifi/mvm/debugfs.c
drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h
drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
drivers/net/wireless/iwlwifi/mvm/fw-api-sta.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/sf.c
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/mvm/sta.h
drivers/net/wireless/iwlwifi/mvm/time-event.c
drivers/net/wireless/iwlwifi/mvm/tt.c
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/cfg.c
drivers/net/wireless/libertas/defs.h
drivers/net/wireless/libertas/rx.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwifiex/11ac.c
drivers/net/wireless/mwifiex/11n.c
drivers/net/wireless/mwifiex/11n.h
drivers/net/wireless/mwifiex/11n_aggr.c
drivers/net/wireless/mwifiex/README
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/cmdevt.c
drivers/net/wireless/mwifiex/debugfs.c
drivers/net/wireless/mwifiex/decl.h
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/ioctl.h
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/pcie.c
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_rx.c
drivers/net/wireless/mwifiex/sta_tx.c
drivers/net/wireless/mwifiex/tdls.c
drivers/net/wireless/mwifiex/uap_cmd.c
drivers/net/wireless/mwifiex/usb.c
drivers/net/wireless/mwifiex/util.c
drivers/net/wireless/mwifiex/wmm.c
drivers/net/wireless/mwifiex/wmm.h
drivers/net/wireless/orinoco/hw.c
drivers/net/wireless/orinoco/hw.h
drivers/net/wireless/orinoco/orinoco_usb.c
drivers/net/wireless/orinoco/wext.c
drivers/net/wireless/p54/main.c
drivers/net/wireless/ray_cs.c
drivers/net/wireless/rndis_wlan.c
drivers/net/wireless/rsi/rsi_91x_mac80211.c
drivers/net/wireless/rsi/rsi_91x_mgmt.c
drivers/net/wireless/rsi/rsi_common.h
drivers/net/wireless/rsi/rsi_mgmt.h
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rt2x00/rt2x00mac.c
drivers/net/wireless/rt2x00/rt2x00usb.c
drivers/net/wireless/rt2x00/rt61pci.c
drivers/net/wireless/rt2x00/rt73usb.c
drivers/net/wireless/rtl818x/rtl8180/Makefile
drivers/net/wireless/rtl818x/rtl8180/dev.c
drivers/net/wireless/rtl818x/rtl8187/dev.c
drivers/net/wireless/rtl818x/rtl818x.h
drivers/net/wireless/rtlwifi/core.c
drivers/net/wireless/rtlwifi/rtl8188ee/hw.c
drivers/net/wireless/rtlwifi/rtl8188ee/hw.h
drivers/net/wireless/rtlwifi/rtl8188ee/sw.c
drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
drivers/net/wireless/rtlwifi/rtl8192ce/hw.h
drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
drivers/net/wireless/rtlwifi/rtl8192se/hw.c
drivers/net/wireless/rtlwifi/rtl8192se/hw.h
drivers/net/wireless/rtlwifi/rtl8192se/sw.c
drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.c
drivers/net/wireless/rtlwifi/rtl8723ae/hw.c
drivers/net/wireless/rtlwifi/rtl8723ae/hw.h
drivers/net/wireless/rtlwifi/rtl8723ae/sw.c
drivers/net/wireless/rtlwifi/rtl8723be/hw.c
drivers/net/wireless/rtlwifi/rtl8723be/hw.h
drivers/net/wireless/rtlwifi/rtl8723be/sw.c
drivers/net/wireless/rtlwifi/rtl8723be/trx.c
drivers/net/wireless/rtlwifi/wifi.h
drivers/net/wireless/ti/wl1251/acx.c
drivers/net/wireless/ti/wl1251/cmd.c
drivers/net/wireless/ti/wl1251/event.c
drivers/net/wireless/ti/wl1251/main.c
drivers/net/wireless/ti/wl1251/spi.c
drivers/net/wireless/ti/wlcore/debugfs.h
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/sdio.c
drivers/net/wireless/ti/wlcore/spi.c
drivers/net/wireless/ti/wlcore/wlcore_i.h
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/pn544/i2c.c
drivers/nfc/port100.c
drivers/nfc/st21nfca/Kconfig [new file with mode: 0644]
drivers/nfc/st21nfca/Makefile [new file with mode: 0644]
drivers/nfc/st21nfca/i2c.c [new file with mode: 0644]
drivers/nfc/st21nfca/st21nfca.c [new file with mode: 0644]
drivers/nfc/st21nfca/st21nfca.h [new file with mode: 0644]
drivers/nfc/trf7970a.c
drivers/of/of_mdio.c
drivers/pci/access.c
drivers/pci/bus.c
drivers/pci/host/pci-exynos.c
drivers/pci/host/pci-imx6.c
drivers/pci/host/pci-mvebu.c
drivers/pci/host/pcie-designware.c
drivers/pci/host/pcie-designware.h
drivers/pci/host/pcie-rcar.c
drivers/pci/hotplug/acpiphp.h
drivers/pci/hotplug/acpiphp_core.c
drivers/pci/hotplug/acpiphp_glue.c
drivers/pci/hotplug/cpci_hotplug.h
drivers/pci/hotplug/cpci_hotplug_core.c
drivers/pci/hotplug/cpci_hotplug_pci.c
drivers/pci/hotplug/cpcihp_generic.c
drivers/pci/hotplug/cpcihp_zt5550.c
drivers/pci/hotplug/cpqphp.h
drivers/pci/hotplug/cpqphp_core.c
drivers/pci/hotplug/cpqphp_ctrl.c
drivers/pci/hotplug/cpqphp_nvram.c
drivers/pci/hotplug/cpqphp_pci.c
drivers/pci/hotplug/cpqphp_sysfs.c
drivers/pci/hotplug/ibmphp_core.c
drivers/pci/hotplug/ibmphp_ebda.c
drivers/pci/hotplug/ibmphp_hpc.c
drivers/pci/hotplug/ibmphp_pci.c
drivers/pci/hotplug/ibmphp_res.c
drivers/pci/hotplug/pci_hotplug_core.c
drivers/pci/hotplug/pciehp_acpi.c
drivers/pci/hotplug/pciehp_core.c
drivers/pci/hotplug/pciehp_ctrl.c
drivers/pci/hotplug/pciehp_hpc.c
drivers/pci/hotplug/pciehp_pci.c
drivers/pci/hotplug/pcihp_skeleton.c
drivers/pci/hotplug/rpaphp_core.c
drivers/pci/hotplug/sgi_hotplug.c
drivers/pci/hotplug/shpchp.h
drivers/pci/hotplug/shpchp_core.c
drivers/pci/hotplug/shpchp_ctrl.c
drivers/pci/hotplug/shpchp_hpc.c
drivers/pci/hotplug/shpchp_pci.c
drivers/pci/hotplug/shpchp_sysfs.c
drivers/pci/htirq.c
drivers/pci/msi.c
drivers/pci/pci-driver.c
drivers/pci/pci-label.c
drivers/pci/pci-stub.c
drivers/pci/pci-sysfs.c
drivers/pci/pci.c
drivers/pci/pcie/aer/aer_inject.c
drivers/pci/pcie/aer/aerdrv_core.c
drivers/pci/pcie/aer/aerdrv_errprint.c
drivers/pci/pcie/pme.c
drivers/pci/pcie/portdrv_pci.c
drivers/pci/probe.c
drivers/pci/proc.c
drivers/pci/quirks.c
drivers/pci/rom.c
drivers/pci/search.c
drivers/pci/setup-bus.c
drivers/pci/setup-irq.c
drivers/pci/setup-res.c
drivers/pci/syscall.c
drivers/ptp/ptp_clock.c
drivers/regulator/virtual.c
drivers/s390/kvm/virtio_ccw.c
drivers/s390/net/claw.c
drivers/s390/net/ctcm_main.c
drivers/s390/net/ctcm_sysfs.c
drivers/s390/net/lcs.c
drivers/s390/net/qeth_core.h
drivers/s390/net/qeth_core_main.c
drivers/s390/net/qeth_core_sys.c
drivers/s390/net/qeth_l2_main.c
drivers/s390/net/qeth_l3_main.c
drivers/scsi/Kconfig
drivers/scsi/hpsa.c
drivers/scsi/hpsa.h
drivers/scsi/hpsa_cmd.h
drivers/scsi/iscsi_tcp.c
drivers/scsi/libiscsi.c
drivers/scsi/lpfc/lpfc.h
drivers/scsi/lpfc/lpfc_attr.c
drivers/scsi/lpfc/lpfc_bsg.c
drivers/scsi/lpfc/lpfc_bsg.h
drivers/scsi/lpfc/lpfc_crtn.h
drivers/scsi/lpfc/lpfc_debugfs.c
drivers/scsi/lpfc/lpfc_els.c
drivers/scsi/lpfc/lpfc_hbadisc.c
drivers/scsi/lpfc/lpfc_hw.h
drivers/scsi/lpfc/lpfc_hw4.h
drivers/scsi/lpfc/lpfc_init.c
drivers/scsi/lpfc/lpfc_mem.c
drivers/scsi/lpfc/lpfc_scsi.c
drivers/scsi/lpfc/lpfc_scsi.h
drivers/scsi/lpfc/lpfc_sli.c
drivers/scsi/lpfc/lpfc_sli.h
drivers/scsi/lpfc/lpfc_sli4.h
drivers/scsi/lpfc/lpfc_version.h
drivers/scsi/pm8001/pm8001_ctl.c
drivers/scsi/qla2xxx/qla_def.h
drivers/scsi/qla2xxx/qla_target.c
drivers/scsi/qla2xxx/qla_target.h
drivers/scsi/qla2xxx/tcm_qla2xxx.c
drivers/scsi/qla2xxx/tcm_qla2xxx.h
drivers/scsi/virtio_scsi.c
drivers/staging/et131x/et131x.c
drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c
drivers/staging/imx-drm/Kconfig
drivers/staging/imx-drm/Makefile
drivers/staging/imx-drm/imx-drm-core.c
drivers/staging/imx-drm/imx-drm.h
drivers/staging/imx-drm/imx-hdmi.c
drivers/staging/imx-drm/imx-ldb.c
drivers/staging/imx-drm/imx-tve.c
drivers/staging/imx-drm/ipuv3-crtc.c
drivers/staging/imx-drm/ipuv3-plane.c
drivers/staging/imx-drm/parallel-display.c
drivers/staging/lustre/lustre/include/lclient.h
drivers/staging/lustre/lustre/lclient/lcommon_cl.c
drivers/staging/lustre/lustre/llite/file.c
drivers/staging/lustre/lustre/llite/llite_internal.h
drivers/staging/lustre/lustre/llite/rw.c
drivers/staging/lustre/lustre/llite/rw26.c
drivers/staging/lustre/lustre/llite/vvp_io.c
drivers/staging/media/omap4iss/iss_video.c
drivers/staging/netlogic/xlr_net.c
drivers/staging/octeon/ethernet.c
drivers/staging/rtl8192ee/core.c
drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c
drivers/staging/rtl8821ae/core.c
drivers/staging/wlan-ng/cfg80211.c
drivers/target/iscsi/iscsi_target.c
drivers/target/iscsi/iscsi_target_auth.c
drivers/target/iscsi/iscsi_target_auth.h
drivers/target/iscsi/iscsi_target_login.c
drivers/target/iscsi/iscsi_target_nego.c
drivers/target/iscsi/iscsi_target_parameters.c
drivers/target/iscsi/iscsi_target_tpg.c
drivers/target/iscsi/iscsi_target_tpg.h
drivers/target/loopback/tcm_loop.c
drivers/target/target_core_sbc.c
drivers/target/target_core_spc.c
drivers/target/target_core_transport.c
drivers/target/target_core_xcopy.c
drivers/target/tcm_fc/tfc_cmd.c
drivers/target/tcm_fc/tfc_io.c
drivers/tty/hvc/hvc_tile.c
drivers/usb/gadget/storage_common.c
drivers/usb/gadget/u_ether.c
drivers/vhost/net.c
drivers/vhost/scsi.c
drivers/vhost/test.c
drivers/vhost/vhost.c
drivers/vhost/vhost.h
drivers/video/Kconfig
drivers/video/backlight/Kconfig
drivers/video/backlight/gpio_backlight.c
drivers/video/backlight/s6e63m0.c
drivers/video/fbdev/sm501fb.c
drivers/virtio/virtio_ring.c
firmware/Makefile
fs/9p/vfs_addr.c
fs/9p/vfs_file.c
fs/adfs/file.c
fs/affs/file.c
fs/afs/file.c
fs/afs/internal.h
fs/afs/write.c
fs/aio.c
fs/bfs/file.c
fs/block_dev.c
fs/btrfs/extent_io.c
fs/btrfs/extent_io.h
fs/btrfs/file.c
fs/btrfs/inode.c
fs/btrfs/ioctl.c
fs/btrfs/qgroup.c
fs/btrfs/reada.c
fs/btrfs/tests/btrfs-tests.c
fs/btrfs/tests/qgroup-tests.c
fs/btrfs/transaction.c
fs/ceph/acl.c
fs/ceph/addr.c
fs/ceph/caps.c
fs/ceph/export.c
fs/ceph/file.c
fs/ceph/inode.c
fs/ceph/mds_client.c
fs/ceph/mds_client.h
fs/ceph/super.h
fs/cifs/cifsfs.c
fs/cifs/cifsfs.h
fs/cifs/file.c
fs/dcache.c
fs/direct-io.c
fs/dlm/lowcomms.c
fs/ecryptfs/file.c
fs/exec.c
fs/exofs/file.c
fs/exofs/inode.c
fs/ext2/file.c
fs/ext2/inode.c
fs/ext3/file.c
fs/ext3/inode.c
fs/ext4/ext4.h
fs/ext4/file.c
fs/ext4/indirect.c
fs/ext4/inode.c
fs/f2fs/data.c
fs/f2fs/file.c
fs/fat/file.c
fs/fat/inode.c
fs/file.c
fs/file_table.c
fs/fuse/cuse.c
fs/fuse/file.c
fs/fuse/fuse_i.h
fs/gfs2/aops.c
fs/gfs2/file.c
fs/hfs/inode.c
fs/hfsplus/inode.c
fs/hostfs/hostfs_kern.c
fs/hpfs/file.c
fs/jffs2/file.c
fs/jfs/file.c
fs/jfs/inode.c
fs/logfs/file.c
fs/minix/file.c
fs/nfs/direct.c
fs/nfs/file.c
fs/nfs/internal.h
fs/nfs/nfs4file.c
fs/nilfs2/file.c
fs/nilfs2/inode.c
fs/ntfs/file.c
fs/ocfs2/aops.c
fs/ocfs2/file.c
fs/omfs/file.c
fs/open.c
fs/pipe.c
fs/ramfs/file-mmu.c
fs/ramfs/file-nommu.c
fs/read_write.c
fs/reiserfs/file.c
fs/reiserfs/inode.c
fs/romfs/mmap-nommu.c
fs/splice.c
fs/sysv/file.c
fs/ubifs/file.c
fs/udf/file.c
fs/udf/inode.c
fs/ufs/file.c
fs/xfs/xfs_aops.c
fs/xfs/xfs_file.c
fs/xfs/xfs_trace.h
include/asm-generic/qrwlock.h [new file with mode: 0644]
include/asm-generic/qrwlock_types.h [new file with mode: 0644]
include/asm-generic/vmlinux.lds.h
include/drm/drmP.h
include/drm/drm_crtc.h
include/drm/drm_crtc_helper.h
include/drm/drm_dp_helper.h
include/drm/drm_edid.h
include/drm/drm_fb_helper.h
include/drm/drm_flip_work.h
include/drm/drm_mipi_dsi.h
include/drm/drm_modes.h
include/drm/drm_modeset_lock.h [new file with mode: 0644]
include/drm/drm_plane_helper.h
include/drm/i915_pciids.h
include/drm/ttm/ttm_bo_api.h
include/dt-bindings/clk/ti-dra7-atl.h [new file with mode: 0644]
include/linux/ath9k_platform.h
include/linux/blk_types.h
include/linux/can/core.h
include/linux/can/dev.h
include/linux/can/led.h
include/linux/can/platform/cc770.h
include/linux/can/platform/mcp251x.h
include/linux/can/platform/rcar_can.h [new file with mode: 0644]
include/linux/can/platform/sja1000.h
include/linux/can/platform/ti_hecc.h
include/linux/can/skb.h
include/linux/ceph/ceph_fs.h
include/linux/ceph/libceph.h
include/linux/ceph/mon_client.h
include/linux/clk/ti.h
include/linux/compiler.h
include/linux/cpufreq.h
include/linux/cpumask.h
include/linux/crc7.h
include/linux/dell-led.h [new file with mode: 0644]
include/linux/device-mapper.h
include/linux/ethtool.h
include/linux/filter.h
include/linux/fs.h
include/linux/i2c/twl.h
include/linux/ieee80211.h
include/linux/if_bridge.h
include/linux/if_link.h
include/linux/if_macvlan.h
include/linux/if_vlan.h
include/linux/isdn/capiutil.h
include/linux/kprobes.h
include/linux/ktime.h
include/linux/kvm_host.h
include/linux/mlx4/device.h
include/linux/moduleparam.h
include/linux/netdev_features.h
include/linux/netdevice.h
include/linux/netfilter/nfnetlink_acct.h
include/linux/netlink.h
include/linux/nfs_fs.h
include/linux/nl802154.h
include/linux/nvme.h
include/linux/of_mdio.h
include/linux/pci.h
include/linux/perf_event.h
include/linux/phy.h
include/linux/phy_fixed.h
include/linux/platform_data/leds-pca9685.h [deleted file]
include/linux/platform_data/shtc1.h [new file with mode: 0644]
include/linux/platform_data/st21nfca.h [new file with mode: 0644]
include/linux/rfkill-gpio.h
include/linux/ring_buffer.h
include/linux/rwsem.h
include/linux/sched.h
include/linux/skbuff.h
include/linux/spi/at86rf230.h
include/linux/splice.h
include/linux/ssb/ssb.h
include/linux/tcp.h
include/linux/udp.h
include/linux/uio.h
include/linux/uprobes.h
include/linux/usb/cdc_ncm.h
include/linux/virtio.h
include/linux/virtio_scsi.h
include/media/videobuf2-core.h
include/net/6lowpan.h
include/net/addrconf.h
include/net/af_ieee802154.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/mgmt.h
include/net/bluetooth/rfcomm.h
include/net/cfg80211.h
include/net/checksum.h
include/net/dsa.h
include/net/gre.h
include/net/ieee802154.h
include/net/ieee802154_netdev.h
include/net/inet_ecn.h
include/net/inet_hashtables.h
include/net/inet_sock.h
include/net/inetpeer.h
include/net/ip.h
include/net/ip6_checksum.h
include/net/ip6_route.h
include/net/ipv6.h
include/net/mac80211.h
include/net/net_namespace.h
include/net/netfilter/nf_nat.h
include/net/netfilter/nf_tables.h
include/net/netfilter/nft_meta.h [new file with mode: 0644]
include/net/netns/ipv4.h
include/net/netns/ipv6.h
include/net/nfc/digital.h
include/net/nfc/hci.h
include/net/nfc/nfc.h
include/net/pkt_cls.h
include/net/pkt_sched.h
include/net/protocol.h
include/net/regulatory.h
include/net/sch_generic.h
include/net/sctp/structs.h
include/net/secure_seq.h
include/net/snmp.h
include/net/sock.h
include/net/tcp.h
include/net/tso.h [new file with mode: 0644]
include/net/udp.h
include/net/vxlan.h
include/net/xfrm.h
include/scsi/scsi_cmnd.h
include/sound/pcm.h
include/target/iscsi/iscsi_transport.h
include/target/target_core_backend.h
include/trace/events/power.h
include/trace/events/sched.h
include/uapi/drm/drm_mode.h
include/uapi/drm/i915_drm.h
include/uapi/drm/radeon_drm.h
include/uapi/linux/audit.h
include/uapi/linux/btrfs.h
include/uapi/linux/can.h
include/uapi/linux/can/bcm.h
include/uapi/linux/can/error.h
include/uapi/linux/can/gw.h
include/uapi/linux/can/netlink.h
include/uapi/linux/can/raw.h
include/uapi/linux/capability.h
include/uapi/linux/ethtool.h
include/uapi/linux/filter.h
include/uapi/linux/if_fddi.h
include/uapi/linux/if_link.h
include/uapi/linux/if_tunnel.h
include/uapi/linux/l2tp.h
include/uapi/linux/neighbour.h
include/uapi/linux/netfilter/nf_tables.h
include/uapi/linux/netfilter/nfnetlink.h
include/uapi/linux/netfilter/nfnetlink_acct.h
include/uapi/linux/nfc.h
include/uapi/linux/nl80211.h
include/uapi/linux/nvme.h
include/uapi/linux/openvswitch.h
include/uapi/linux/perf_event.h
include/uapi/linux/tipc.h
include/uapi/linux/tipc_config.h
include/uapi/linux/udp.h
include/uapi/sound/compress_offload.h
include/video/imx-ipu-v3.h [moved from drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h with 95% similarity]
include/xen/interface/io/netif.h
init/main.c
kernel/Kconfig.locks
kernel/audit.c
kernel/cpu.c
kernel/events/core.c
kernel/events/uprobes.c
kernel/kprobes.c
kernel/locking/Makefile
kernel/locking/qrwlock.c [new file with mode: 0644]
kernel/locking/rwsem-xadd.c
kernel/locking/rwsem.c
kernel/module.c
kernel/notifier.c
kernel/params.c
kernel/power/hibernate.c
kernel/power/process.c
kernel/power/suspend.c
kernel/sched/core.c
kernel/sched/deadline.c
kernel/sched/fair.c
kernel/sched/features.h
kernel/sched/idle.c
kernel/sched/rt.c
kernel/sched/sched.h
kernel/seccomp.c
kernel/sysctl.c
kernel/trace/ring_buffer.c
kernel/trace/trace.c
kernel/trace/trace.h
kernel/trace/trace_event_perf.c
kernel/trace/trace_kprobe.c
kernel/trace/trace_probe.c
kernel/trace/trace_probe.h
kernel/trace/trace_uprobe.c
lib/Kconfig
lib/Kconfig.debug
lib/Makefile
lib/cpumask.c
lib/crc7.c
lib/interval_tree.c
lib/interval_tree_test.c [moved from lib/interval_tree_test_main.c with 100% similarity]
lib/test_bpf.c [new file with mode: 0644]
mm/filemap.c
mm/iov_iter.c
mm/page_io.c
mm/process_vm_access.c
mm/shmem.c
mm/vmscan.c
net/8021q/vlan_core.c
net/8021q/vlan_dev.c
net/appletalk/ddp.c
net/atm/svc.c
net/batman-adv/debugfs.c
net/batman-adv/distributed-arp-table.c
net/batman-adv/main.h
net/batman-adv/network-coding.c
net/batman-adv/soft-interface.c
net/batman-adv/sysfs.c
net/bluetooth/6lowpan.c
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hci_sock.c
net/bluetooth/l2cap_core.c
net/bluetooth/l2cap_sock.c
net/bluetooth/lib.c
net/bluetooth/mgmt.c
net/bluetooth/rfcomm/core.c
net/bluetooth/rfcomm/tty.c
net/bluetooth/smp.c
net/bluetooth/smp.h
net/bridge/Makefile
net/bridge/br.c
net/bridge/br_device.c
net/bridge/br_fdb.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 [deleted file]
net/bridge/br_private.h
net/bridge/br_sysfs_br.c
net/bridge/br_sysfs_if.c
net/bridge/br_vlan.c
net/bridge/netfilter/Kconfig
net/bridge/netfilter/Makefile
net/bridge/netfilter/nft_meta_bridge.c [new file with mode: 0644]
net/can/af_can.c
net/can/af_can.h
net/can/proc.c
net/ceph/ceph_common.c
net/ceph/debugfs.c
net/ceph/mon_client.c
net/ceph/osd_client.c
net/ceph/pagevec.c
net/core/Makefile
net/core/datagram.c
net/core/dev.c
net/core/dev_addr_lists.c
net/core/ethtool.c
net/core/filter.c
net/core/net_namespace.c
net/core/pktgen.c
net/core/ptp_classifier.c
net/core/rtnetlink.c
net/core/secure_seq.c
net/core/skbuff.c
net/core/sock.c
net/core/tso.c [new file with mode: 0644]
net/dccp/ipv4.c
net/dccp/proto.c
net/dccp/sysctl.c
net/dccp/timer.c
net/decnet/af_decnet.c
net/dns_resolver/dns_query.c
net/dsa/slave.c
net/ieee802154/6lowpan_rtnl.c
net/ieee802154/dgram.c
net/ieee802154/header_ops.c
net/ieee802154/ieee802154.h
net/ieee802154/netlink.c
net/ieee802154/nl-mac.c
net/ieee802154/nl_policy.c
net/ieee802154/reassembly.c
net/ipv4/af_inet.c
net/ipv4/datagram.c
net/ipv4/devinet.c
net/ipv4/gre_demux.c
net/ipv4/gre_offload.c
net/ipv4/icmp.c
net/ipv4/igmp.c
net/ipv4/inet_connection_sock.c
net/ipv4/inet_hashtables.c
net/ipv4/inetpeer.c
net/ipv4/ip_forward.c
net/ipv4/ip_gre.c
net/ipv4/ip_options.c
net/ipv4/ip_output.c
net/ipv4/ip_tunnel.c
net/ipv4/ip_tunnel_core.c
net/ipv4/ip_vti.c
net/ipv4/ipip.c
net/ipv4/ipmr.c
net/ipv4/netfilter/iptable_nat.c
net/ipv4/netfilter/nf_defrag_ipv4.c
net/ipv4/netfilter/nft_chain_nat_ipv4.c
net/ipv4/proc.c
net/ipv4/raw.c
net/ipv4/route.c
net/ipv4/syncookies.c
net/ipv4/sysctl_net_ipv4.c
net/ipv4/tcp.c
net/ipv4/tcp_bic.c
net/ipv4/tcp_cong.c
net/ipv4/tcp_cubic.c
net/ipv4/tcp_fastopen.c
net/ipv4/tcp_highspeed.c
net/ipv4/tcp_htcp.c
net/ipv4/tcp_hybla.c
net/ipv4/tcp_illinois.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_lp.c
net/ipv4/tcp_metrics.c
net/ipv4/tcp_minisocks.c
net/ipv4/tcp_offload.c
net/ipv4/tcp_output.c
net/ipv4/tcp_scalable.c
net/ipv4/tcp_vegas.c
net/ipv4/tcp_veno.c
net/ipv4/tcp_yeah.c
net/ipv4/udp.c
net/ipv4/udp_offload.c
net/ipv4/udplite.c
net/ipv4/xfrm4_mode_tunnel.c
net/ipv4/xfrm4_output.c
net/ipv6/addrconf.c
net/ipv6/addrconf_core.c
net/ipv6/af_inet6.c
net/ipv6/icmp.c
net/ipv6/inet6_connection_sock.c
net/ipv6/ip6_checksum.c
net/ipv6/ip6_fib.c
net/ipv6/ip6_flowlabel.c
net/ipv6/ip6_gre.c
net/ipv6/ip6_offload.c
net/ipv6/ip6_output.c
net/ipv6/ip6_tunnel.c
net/ipv6/ip6_vti.c
net/ipv6/netfilter/ip6table_nat.c
net/ipv6/netfilter/nf_conntrack_reasm.c
net/ipv6/netfilter/nft_chain_nat_ipv6.c
net/ipv6/output_core.c
net/ipv6/ping.c
net/ipv6/proc.c
net/ipv6/raw.c
net/ipv6/route.c
net/ipv6/sit.c
net/ipv6/syncookies.c
net/ipv6/sysctl_net_ipv6.c
net/ipv6/tcp_ipv6.c
net/ipv6/udp.c
net/ipv6/udp_offload.c
net/ipv6/udplite.c
net/ipv6/xfrm6_output.c
net/ipx/af_ipx.c
net/ipx/ipx_route.c
net/iucv/af_iucv.c
net/key/af_key.c
net/l2tp/l2tp_core.c
net/l2tp/l2tp_core.h
net/l2tp/l2tp_ip.c
net/l2tp/l2tp_ip6.c
net/l2tp/l2tp_netlink.c
net/mac80211/Makefile
net/mac80211/aes_ccm.c
net/mac80211/cfg.c
net/mac80211/chan.c
net/mac80211/debugfs.c
net/mac80211/debugfs.h
net/mac80211/debugfs_netdev.c
net/mac80211/debugfs_netdev.h
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/main.c
net/mac80211/mesh.c
net/mac80211/mesh_hwmp.c
net/mac80211/mesh_pathtbl.c
net/mac80211/mesh_sync.c
net/mac80211/michael.h
net/mac80211/mlme.c
net/mac80211/rc80211_minstrel.c
net/mac80211/rc80211_minstrel_ht.c
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/sta_info.c
net/mac80211/status.c
net/mac80211/tdls.c [new file with mode: 0644]
net/mac80211/trace.h
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/wpa.c
net/mac802154/Kconfig
net/mac802154/Makefile
net/mac802154/llsec.c [new file with mode: 0644]
net/mac802154/llsec.h [new file with mode: 0644]
net/mac802154/mac802154.h
net/mac802154/mac_cmd.c
net/mac802154/mib.c
net/mac802154/monitor.c
net/mac802154/rx.c
net/mac802154/wpan.c
net/mpls/mpls_gso.c
net/netfilter/ipset/ip_set_core.c
net/netfilter/ipvs/ip_vs_core.c
net/netfilter/ipvs/ip_vs_xmit.c
net/netfilter/nf_nat_core.c
net/netfilter/nf_tables_api.c
net/netfilter/nfnetlink.c
net/netfilter/nfnetlink_acct.c
net/netfilter/nft_ct.c
net/netfilter/nft_hash.c
net/netfilter/nft_lookup.c
net/netfilter/nft_meta.c
net/netfilter/nft_rbtree.c
net/netfilter/xt_bpf.c
net/netfilter/xt_nfacct.c
net/netfilter/xt_recent.c
net/netlink/af_netlink.c
net/netlink/af_netlink.h
net/netlink/genetlink.c
net/nfc/digital.h
net/nfc/digital_core.c
net/nfc/digital_dep.c
net/nfc/digital_technology.c
net/nfc/hci/command.c
net/nfc/hci/core.c
net/nfc/llcp_commands.c
net/nfc/llcp_core.c
net/nfc/nci/core.c
net/nfc/nci/ntf.c
net/nfc/nfc.h
net/nfc/rawsock.c
net/openvswitch/actions.c
net/openvswitch/datapath.c
net/openvswitch/datapath.h
net/openvswitch/flow.c
net/openvswitch/flow.h
net/openvswitch/flow_netlink.c
net/openvswitch/flow_netlink.h
net/openvswitch/flow_table.c
net/openvswitch/flow_table.h
net/openvswitch/vport-gre.c
net/openvswitch/vport-internal_dev.c
net/openvswitch/vport-vxlan.c
net/openvswitch/vport.h
net/rds/ib_send.c
net/rds/iw_send.c
net/rds/iw_sysctl.c
net/rds/rdma_transport.c
net/rds/sysctl.c
net/rds/tcp_listen.c
net/rfkill/rfkill-gpio.c
net/sched/cls_api.c
net/sched/cls_basic.c
net/sched/cls_bpf.c
net/sched/cls_cgroup.c
net/sched/cls_flow.c
net/sched/cls_fw.c
net/sched/cls_route.c
net/sched/cls_rsvp.h
net/sched/cls_tcindex.c
net/sched/cls_u32.c
net/sched/sch_api.c
net/sched/sch_choke.c
net/sched/sch_drr.c
net/sched/sch_fq.c
net/sched/sch_fq_codel.c
net/sched/sch_hhf.c
net/sched/sch_netem.c
net/sched/sch_sfq.c
net/sctp/associola.c
net/sctp/endpointola.c
net/sctp/ipv6.c
net/sctp/output.c
net/sctp/proc.c
net/sctp/protocol.c
net/sctp/sm_make_chunk.c
net/sctp/socket.c
net/sctp/sysctl.c
net/sctp/transport.c
net/sctp/ulpqueue.c
net/sunrpc/socklib.c
net/sunrpc/xprtsock.c
net/tipc/Makefile
net/tipc/bcast.c
net/tipc/bcast.h
net/tipc/bearer.c
net/tipc/bearer.h
net/tipc/config.c
net/tipc/core.c
net/tipc/core.h
net/tipc/discover.c
net/tipc/discover.h
net/tipc/eth_media.c
net/tipc/handler.c [deleted file]
net/tipc/ib_media.c
net/tipc/link.c
net/tipc/link.h
net/tipc/msg.c
net/tipc/msg.h
net/tipc/name_distr.c
net/tipc/name_distr.h
net/tipc/name_table.c
net/tipc/net.c
net/tipc/net.h
net/tipc/node.c
net/tipc/node.h
net/tipc/node_subscr.c
net/tipc/node_subscr.h
net/tipc/port.c
net/tipc/port.h
net/tipc/socket.c
net/tipc/socket.h
net/unix/af_unix.c
net/wireless/Kconfig
net/wireless/ap.c
net/wireless/chan.c
net/wireless/core.c
net/wireless/core.h
net/wireless/ethtool.c
net/wireless/genregdb.awk
net/wireless/ibss.c
net/wireless/mesh.c
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/nl80211.h
net/wireless/rdev-ops.h
net/wireless/reg.c
net/wireless/reg.h
net/wireless/scan.c
net/wireless/sme.c
net/wireless/trace.h
net/wireless/util.c
net/wireless/wext-compat.c
net/wireless/wext-compat.h
net/wireless/wext-sme.c
net/xfrm/xfrm_output.c
net/xfrm/xfrm_policy.c
net/xfrm/xfrm_proc.c
net/xfrm/xfrm_state.c
net/xfrm/xfrm_user.c
samples/kobject/kobject-example.c
samples/kobject/kset-example.c
scripts/Makefile
scripts/Makefile.asm-generic
scripts/Makefile.build
scripts/Makefile.extrawarn [new file with mode: 0644]
scripts/Makefile.fwinst
scripts/Makefile.host
scripts/Makefile.lib
scripts/basic/fixdep.c
scripts/checkstack.pl
scripts/coccinelle/misc/of_table.cocci [new file with mode: 0644]
scripts/coccinelle/misc/returnvar.cocci [new file with mode: 0644]
scripts/config
scripts/conmakehash.c
scripts/docproc.c
scripts/dtc/.gitignore
scripts/dtc/fstree.c
scripts/dtc/libfdt/fdt_empty_tree.c
scripts/dtc/treesource.c
scripts/headers.sh
scripts/kallsyms.c
scripts/kconfig/Makefile
scripts/kconfig/check.sh
scripts/kconfig/conf.c
scripts/kconfig/gconf.c
scripts/kconfig/lxdialog/checklist.c
scripts/kconfig/lxdialog/inputbox.c
scripts/kconfig/lxdialog/menubox.c
scripts/kconfig/lxdialog/util.c
scripts/kconfig/mconf.c
scripts/kconfig/menu.c
scripts/kconfig/nconf.c
scripts/kconfig/streamline_config.pl
scripts/kconfig/util.c
scripts/kconfig/zconf.l
scripts/kconfig/zconf.lex.c_shipped
scripts/kconfig/zconf.tab.c_shipped
scripts/kconfig/zconf.y
scripts/markup_oops.pl
scripts/mkcompile_h
scripts/mkmakefile
scripts/mksysmap
scripts/mod/.gitignore
scripts/mod/file2alias.c
scripts/mod/mk_elfconfig.c
scripts/mod/modpost.c
scripts/mod/sumversion.c
scripts/objdiff
scripts/package/Makefile
scripts/package/builddeb
scripts/package/buildtar
scripts/patch-kernel
scripts/pnmtologo.c
scripts/recordmcount.c
scripts/rt-tester/check-all.sh
scripts/rt-tester/rt-tester.py
scripts/selinux/install_policy.sh
scripts/show_delta
scripts/tags.sh
security/integrity/evm/Kconfig
security/integrity/evm/evm.h
security/integrity/evm/evm_crypto.c
security/integrity/evm/evm_main.c
security/integrity/ima/ima_appraise.c
security/integrity/ima/ima_crypto.c
security/integrity/ima/ima_main.c
security/selinux/include/classmap.h
sound/core/seq/seq_clientmgr.c
sound/core/seq/seq_fifo.c
sound/core/timer.c
sound/firewire/bebob/bebob.h
sound/firewire/bebob/bebob_stream.c
sound/firewire/fireworks/fireworks.c
sound/firewire/fireworks/fireworks.h
sound/firewire/fireworks/fireworks_hwdep.c
sound/firewire/fireworks/fireworks_stream.c
sound/firewire/fireworks/fireworks_transaction.c
sound/pci/hda/hda_intel.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/intel8x0.c
tools/lib/api/fs/fs.c
tools/net/bpf_exp.l
tools/net/bpf_exp.y
tools/net/bpf_jit_disasm.c
tools/perf/Documentation/perf-record.txt
tools/perf/Documentation/perf-report.txt
tools/perf/Documentation/perf-top.txt
tools/perf/Makefile.perf
tools/perf/builtin-annotate.c
tools/perf/builtin-diff.c
tools/perf/builtin-record.c
tools/perf/builtin-report.c
tools/perf/builtin-sched.c
tools/perf/builtin-top.c
tools/perf/config/Makefile
tools/perf/perf.c
tools/perf/tests/builtin-test.c
tools/perf/tests/hists_common.c
tools/perf/tests/hists_common.h
tools/perf/tests/hists_cumulate.c [new file with mode: 0644]
tools/perf/tests/hists_filter.c
tools/perf/tests/hists_link.c
tools/perf/tests/hists_output.c
tools/perf/tests/tests.h
tools/perf/ui/browser.c
tools/perf/ui/browsers/hists.c
tools/perf/ui/gtk/hists.c
tools/perf/ui/hist.c
tools/perf/ui/stdio/hist.c
tools/perf/util/callchain.c
tools/perf/util/callchain.h
tools/perf/util/hist.c
tools/perf/util/hist.h
tools/perf/util/sort.c
tools/perf/util/sort.h
tools/perf/util/symbol.c
tools/perf/util/symbol.h
tools/testing/selftests/net/Makefile
tools/testing/selftests/powerpc/Makefile
tools/testing/selftests/powerpc/harness.c
tools/testing/selftests/powerpc/pmu/Makefile
tools/testing/selftests/powerpc/pmu/ebb/Makefile [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/close_clears_pmcc_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/ebb.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/ebb.h [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/ebb_handler.S [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/ebb_on_child_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/event_attributes_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/fixed_instruction_loop.S [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/fork_cleanup_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/no_handler_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/reg.h [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/reg_access_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/task_event_pinned_vs_ebb_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/task_event_vs_ebb_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/trace.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/trace.h [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/event.c
tools/testing/selftests/powerpc/pmu/event.h
tools/testing/selftests/powerpc/pmu/lib.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/lib.h [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/loop.S
tools/testing/selftests/powerpc/subunit.h
tools/testing/selftests/powerpc/tm/Makefile [new file with mode: 0644]
tools/testing/selftests/powerpc/tm/tm-resched-dscr.c [new file with mode: 0644]
tools/testing/selftests/powerpc/utils.h
virt/kvm/kvm_main.c

index 42fa0d5626a9560d74d16b2df5250b300543b67e..f4c0b091dcf4e6413cbe70f0ec345a5894e0885e 100644 (file)
@@ -22,7 +22,6 @@
 *.lst
 *.symtypes
 *.order
-modules.builtin
 *.elf
 *.bin
 *.gz
@@ -33,6 +32,8 @@ modules.builtin
 *.lzo
 *.patch
 *.gcno
+modules.builtin
+Module.symvers
 
 #
 # Top-level generic files
@@ -44,7 +45,6 @@ modules.builtin
 /vmlinuz
 /System.map
 /Module.markers
-/Module.symvers
 
 #
 # Debian directory (make deb-pkg)
index d922060e455d5647e06c4f1182306d42c514a604..416c5d59f52eaf02081b88901cb0f165713e7e6a 100644 (file)
@@ -169,6 +169,14 @@ Description:
                "unknown", "notpresent", "down", "lowerlayerdown", "testing",
                "dormant", "up".
 
+What:          /sys/class/net/<iface>/phys_port_id
+Date:          July 2013
+KernelVersion: 3.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the interface unique physical port identifier within
+               the NIC, as a string.
+
 What:          /sys/class/net/<iface>/speed
 Date:          October 2009
 KernelVersion: 2.6.33
diff --git a/Documentation/ABI/testing/sysfs-class-net-cdc_ncm b/Documentation/ABI/testing/sysfs-class-net-cdc_ncm
new file mode 100644 (file)
index 0000000..5cedf72
--- /dev/null
@@ -0,0 +1,149 @@
+What:          /sys/class/net/<iface>/cdc_ncm/min_tx_pkt
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               The driver will pad NCM Transfer Blocks (NTBs) longer
+               than this to tx_max, allowing the device to receive
+               tx_max sized frames with no terminating short
+               packet. NTBs shorter than this limit are transmitted
+               as-is, without any padding, and are terminated with a
+               short USB packet.
+
+               Padding to tx_max allows the driver to transmit NTBs
+               back-to-back without any interleaving short USB
+               packets.  This reduces the number of short packet
+               interrupts in the device, and represents a tradeoff
+               between USB bus bandwidth and device DMA optimization.
+
+               Set to 0 to pad all frames. Set greater than tx_max to
+               disable all padding.
+
+What:          /sys/class/net/<iface>/cdc_ncm/rx_max
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               The maximum NTB size for RX.  Cannot exceed the
+               maximum value supported by the device. Must allow at
+               least one max sized datagram plus headers.
+
+               The actual limits are device dependent.  See
+               dwNtbInMaxSize.
+
+               Note: Some devices will silently ignore changes to
+               this value, resulting in oversized NTBs and
+               corresponding framing errors.
+
+What:          /sys/class/net/<iface>/cdc_ncm/tx_max
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               The maximum NTB size for TX.  Cannot exceed the
+               maximum value supported by the device.  Must allow at
+               least one max sized datagram plus headers.
+
+               The actual limits are device dependent.  See
+               dwNtbOutMaxSize.
+
+What:          /sys/class/net/<iface>/cdc_ncm/tx_timer_usecs
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               Datagram aggregation timeout in µs. The driver will
+               wait up to 3 times this timeout for more datagrams to
+               aggregate before transmitting an NTB frame.
+
+               Valid range: 5 to 4000000
+
+               Set to 0 to disable aggregation.
+
+The following read-only attributes all represent fields of the
+structure defined in section 6.2.1 "GetNtbParameters" of "Universal
+Serial Bus Communications Class Subclass Specifications for Network
+Control Model Devices" (CDC NCM), Revision 1.0 (Errata 1), November
+24, 2010 from USB Implementers Forum, Inc.  The descriptions are
+quoted from table 6-3 of CDC NCM: "NTB Parameter Structure".
+
+What:          /sys/class/net/<iface>/cdc_ncm/bmNtbFormatsSupported
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               Bit 0: 16-bit NTB supported (set to 1)
+               Bit 1: 32-bit NTB supported
+               Bits 2 – 15: reserved (reset to zero; must be ignored by host)
+
+What:          /sys/class/net/<iface>/cdc_ncm/dwNtbInMaxSize
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               IN NTB Maximum Size in bytes
+
+What:          /sys/class/net/<iface>/cdc_ncm/wNdpInDivisor
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               Divisor used for IN NTB Datagram payload alignment
+
+What:          /sys/class/net/<iface>/cdc_ncm/wNdpInPayloadRemainder
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               Remainder used to align input datagram payload within
+               the NTB: (Payload Offset) mod (wNdpInDivisor) =
+               wNdpInPayloadRemainder
+
+What:          /sys/class/net/<iface>/cdc_ncm/wNdpInAlignment
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               NDP alignment modulus for NTBs on the IN pipe. Shall
+               be a power of 2, and shall be at least 4.
+
+What:          /sys/class/net/<iface>/cdc_ncm/dwNtbOutMaxSize
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               OUT NTB Maximum Size
+
+What:          /sys/class/net/<iface>/cdc_ncm/wNdpOutDivisor
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               OUT NTB Datagram alignment modulus
+
+What:          /sys/class/net/<iface>/cdc_ncm/wNdpOutPayloadRemainder
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               Remainder used to align output datagram payload
+               offsets within the NTB: Padding, shall be transmitted
+               as zero by function, and ignored by host.  (Payload
+               Offset) mod (wNdpOutDivisor) = wNdpOutPayloadRemainder
+
+What:          /sys/class/net/<iface>/cdc_ncm/wNdpOutAlignment
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               NDP alignment modulus for use in NTBs on the OUT
+               pipe. Shall be a power of 2, and shall be at least 4.
+
+What:          /sys/class/net/<iface>/cdc_ncm/wNtbOutMaxDatagrams
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               Maximum number of datagrams that the host may pack
+               into a single OUT NTB. Zero means that the device
+               imposes no limit.
diff --git a/Documentation/ABI/testing/sysfs-class-net-queues b/Documentation/ABI/testing/sysfs-class-net-queues
new file mode 100644 (file)
index 0000000..5e9aeb9
--- /dev/null
@@ -0,0 +1,79 @@
+What:          /sys/class/<iface>/queues/rx-<queue>/rps_cpus
+Date:          March 2010
+KernelVersion: 2.6.35
+Contact:       netdev@vger.kernel.org
+Description:
+               Mask of the CPU(s) currently enabled to participate into the
+               Receive Packet Steering packet processing flow for this
+               network device queue. Possible values depend on the number
+               of available CPU(s) in the system.
+
+What:          /sys/class/<iface>/queues/rx-<queue>/rps_flow_cnt
+Date:          April 2010
+KernelVersion: 2.6.35
+Contact:       netdev@vger.kernel.org
+Description:
+               Number of Receive Packet Steering flows being currently
+               processed by this particular network device receive queue.
+
+What:          /sys/class/<iface>/queues/tx-<queue>/tx_timeout
+Date:          November 2011
+KernelVersion: 3.3
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of transmit timeout events seen by this
+               network interface transmit queue.
+
+What:          /sys/class/<iface>/queues/tx-<queue>/xps_cpus
+Date:          November 2010
+KernelVersion: 2.6.38
+Contact:       netdev@vger.kernel.org
+Description:
+               Mask of the CPU(s) currently enabled to participate into the
+               Transmit Packet Steering packet processing flow for this
+               network device transmit queue. Possible vaules depend on the
+               number of available CPU(s) in the system.
+
+What:          /sys/class/<iface>/queues/tx-<queue>/byte_queue_limits/hold_time
+Date:          November 2011
+KernelVersion: 3.3
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the hold time in milliseconds to measure the slack
+               of this particular network device transmit queue.
+               Default value is 1000.
+
+What:          /sys/class/<iface>/queues/tx-<queue>/byte_queue_limits/inflight
+Date:          November 2011
+KernelVersion: 3.3
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of bytes (objects) in flight on this
+               network device transmit queue.
+
+What:          /sys/class/<iface>/queues/tx-<queue>/byte_queue_limits/limit
+Date:          November 2011
+KernelVersion: 3.3
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the current limit of bytes allowed to be queued
+               on this network device transmit queue. This value is clamped
+               to be within the bounds defined by limit_max and limit_min.
+
+What:          /sys/class/<iface>/queues/tx-<queue>/byte_queue_limits/limit_max
+Date:          November 2011
+KernelVersion: 3.3
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the absolute maximum limit of bytes allowed to be
+               queued on this network device transmit queue. See
+               include/linux/dynamic_queue_limits.h for the default value.
+
+What:          /sys/class/<iface>/queues/tx-<queue>/byte_queue_limits/limit_min
+Date:          November 2011
+KernelVersion: 3.3
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the absolute minimum limit of bytes allowed to be
+               queued on this network device transmit queue. Default value is
+               0.
diff --git a/Documentation/ABI/testing/sysfs-class-net-statistics b/Documentation/ABI/testing/sysfs-class-net-statistics
new file mode 100644 (file)
index 0000000..397118d
--- /dev/null
@@ -0,0 +1,201 @@
+What:          /sys/class/<iface>/statistics/collisions
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of collisions seen by this network device.
+               This value might not be relevant with all MAC layers.
+
+What:          /sys/class/<iface>/statistics/multicast
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of multicast packets received by this
+               network device.
+
+What:          /sys/class/<iface>/statistics/rx_bytes
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of bytes received by this network device.
+               See the network driver for the exact meaning of when this
+               value is incremented.
+
+What:          /sys/class/<iface>/statistics/rx_compressed
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of compressed packets received by this
+               network device. This value might only be relevant for interfaces
+               that support packet compression (e.g: PPP).
+
+What:          /sys/class/<iface>/statistics/rx_crc_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of packets received with a CRC (FCS) error
+               by this network device. Note that the specific meaning might
+               depend on the MAC layer used by the interface.
+
+What:          /sys/class/<iface>/statistics/rx_dropped
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of packets received by the network device
+               but dropped, that are not forwarded to the upper layers for
+               packet processing. See the network driver for the exact
+               meaning of this value.
+
+What:          /sys/class/<iface>/statistics/rx_fifo_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of receive FIFO errors seen by this
+               network device. See the network driver for the exact
+               meaning of this value.
+
+What:          /sys/class/<iface>/statistics/rx_frame_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of received frames with error, such as
+               alignment errors. Note that the specific meaning depends on
+               on the MAC layer protocol used. See the network driver for
+               the exact meaning of this value.
+
+What:          /sys/class/<iface>/statistics/rx_length_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of received error packet with a length
+               error, oversized or undersized. See the network driver for the
+               exact meaning of this value.
+
+What:          /sys/class/<iface>/statistics/rx_missed_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of received packets that have been missed
+               due to lack of capacity in the receive side. See the network
+               driver for the exact meaning of this value.
+
+What:          /sys/class/<iface>/statistics/rx_over_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of received packets that are oversized
+               compared to what the network device is configured to accept
+               (e.g: larger than MTU). See the network driver for the exact
+               meaning of this value.
+
+What:          /sys/class/<iface>/statistics/rx_packets
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the total number of good packets received by this
+               network device.
+
+What:          /sys/class/<iface>/statistics/tx_aborted_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of packets that have been aborted
+               during transmission by a network device (e.g: because of
+               a medium collision). See the network driver for the exact
+               meaning of this value.
+
+What:          /sys/class/<iface>/statistics/tx_bytes
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of bytes transmitted by a network
+               device. See the network driver for the exact meaning of this
+               value, in particular whether this accounts for all successfully
+               transmitted packets or all packets that have been queued for
+               transmission.
+
+What:          /sys/class/<iface>/statistics/tx_carrier_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of packets that could not be transmitted
+               because of carrier errors (e.g: physical link down). See the
+               network driver for the exact meaning of this value.
+
+What:          /sys/class/<iface>/statistics/tx_compressed
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of transmitted compressed packets. Note
+               this might only be relevant for devices that support
+               compression (e.g: PPP).
+
+What:          /sys/class/<iface>/statistics/tx_dropped
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of packets dropped during transmission.
+               See the driver for the exact reasons as to why the packets were
+               dropped.
+
+What:          /sys/class/<iface>/statistics/tx_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of packets in error during transmission by
+               a network device. See the driver for the exact reasons as to
+               why the packets were dropped.
+
+What:          /sys/class/<iface>/statistics/tx_fifo_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of packets having caused a transmit
+               FIFO error. See the driver for the exact reasons as to why the
+               packets were dropped.
+
+What:          /sys/class/<iface>/statistics/tx_heartbeat_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of packets transmitted that have been
+               reported as heartbeat errors. See the driver for the exact
+               reasons as to why the packets were dropped.
+
+What:          /sys/class/<iface>/statistics/tx_packets
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of packets transmitted by a network
+               device. See the driver for whether this reports the number of all
+               attempted or successful transmissions.
+
+What:          /sys/class/<iface>/statistics/tx_window_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of packets not successfully transmitted
+               due to a window collision. The specific meaning depends on the
+               MAC layer used.  On Ethernet this is usually used to report
+               late collisions errors.
index 044b76436e8373ae601f9c60bd274754f3df6033..d9b9416c989fd81f0a9a338b59fc9093d96479a4 100644 (file)
 !Finclude/net/cfg80211.h wdev_priv
 !Finclude/net/cfg80211.h ieee80211_iface_limit
 !Finclude/net/cfg80211.h ieee80211_iface_combination
+!Finclude/net/cfg80211.h cfg80211_check_combinations
       </chapter>
       <chapter>
       <title>Actions and configuration</title>
index ba60d93c18551af17db0e1bcbca7670662c025f9..7df3134ebc0e1d4b88985132eda82f26b267f4f3 100644 (file)
       and then pass it to one of the <function>drm_*_init()</function> functions
       to register it with the DRM subsystem.
     </para>
+    <para>
+      Newer drivers that no longer require a <structname>drm_bus</structname>
+      structure can alternatively use the low-level device initialization and
+      registration functions such as <function>drm_dev_alloc()</function> and
+      <function>drm_dev_register()</function> directly.
+    </para>
     <para>
       The <structname>drm_driver</structname> structure contains static
       information that describes the driver and features it supports, and
@@ -281,6 +287,36 @@ char *date;</synopsis>
         </para>
       </sect3>
     </sect2>
+    <sect2>
+      <title>Device Registration</title>
+      <para>
+        A number of functions are provided to help with device registration.
+        The functions deal with PCI, USB and platform devices, respectively.
+      </para>
+!Edrivers/gpu/drm/drm_pci.c
+!Edrivers/gpu/drm/drm_usb.c
+!Edrivers/gpu/drm/drm_platform.c
+      <para>
+        New drivers that no longer rely on the services provided by the
+        <structname>drm_bus</structname> structure can call the low-level
+        device registration functions directly. The
+        <function>drm_dev_alloc()</function> function can be used to allocate
+        and initialize a new <structname>drm_device</structname> structure.
+        Drivers will typically want to perform some additional setup on this
+        structure, such as allocating driver-specific data and storing a
+        pointer to it in the DRM device's <structfield>dev_private</structfield>
+        field. Drivers should also set the device's unique name using the
+        <function>drm_dev_set_unique()</function> function. After it has been
+        set up a device can be registered with the DRM subsystem by calling
+        <function>drm_dev_register()</function>. This will cause the device to
+        be exposed to userspace and will call the driver's
+        <structfield>.load()</structfield> implementation. When a device is
+        removed, the DRM device can safely be unregistered and freed by calling
+        <function>drm_dev_unregister()</function> followed by a call to
+        <function>drm_dev_unref()</function>.
+      </para>
+!Edrivers/gpu/drm/drm_stub.c
+    </sect2>
     <sect2>
       <title>Driver Load</title>
       <para>
@@ -341,14 +377,6 @@ char *date;</synopsis>
         </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
@@ -356,7 +384,7 @@ char *date;</synopsis>
             clearing all pending interrupt flags or disabling the interrupt.
           </para>
           <para>
-            The IRQ will then be requested by a call to
+            The passed-in 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.
@@ -1799,6 +1827,12 @@ void intel_crt_init(struct drm_device *dev)
       <title>KMS API Functions</title>
 !Edrivers/gpu/drm/drm_crtc.c
     </sect2>
+    <sect2>
+      <title>KMS Locking</title>
+!Pdrivers/gpu/drm/drm_modeset_lock.c kms locking
+!Iinclude/drm/drm_modeset_lock.h
+!Edrivers/gpu/drm/drm_modeset_lock.c
+    </sect2>
   </sect1>
 
   <!-- Internals: kms helper functions -->
@@ -1903,8 +1937,8 @@ void intel_crt_init(struct drm_device *dev)
           <para>
             The function filters out modes larger than
             <parameter>max_width</parameter> and <parameter>max_height</parameter>
-            if specified. It then calls the connector
-            <methodname>mode_valid</methodname> helper operation for  each mode in
+            if specified. It then calls the optional connector
+            <methodname>mode_valid</methodname> helper operation for each mode in
             the probed list to check whether the mode is valid for the connector.
           </para>
         </listitem>
@@ -2265,7 +2299,7 @@ void intel_crt_init(struct drm_device *dev)
           <para>
             Verify whether a mode is valid for the connector. Return MODE_OK for
             supported modes and one of the enum drm_mode_status values (MODE_*)
-            for unsupported modes. This operation is mandatory.
+            for unsupported modes. This operation is optional.
           </para>
           <para>
             As the mode rejection reason is currently not used beside for
@@ -2450,6 +2484,863 @@ void intel_crt_init(struct drm_device *dev)
       pointer to the target object, a pointer to the previously created property
       and an initial instance value.
     </para>
+    <sect2>
+       <title>Existing KMS Properties</title>
+       <para>
+       The following table gives description of drm properties exposed by various
+       modules/drivers.
+       </para>
+       <table border="1" cellpadding="0" cellspacing="0">
+       <tbody>
+       <tr style="font-weight: bold;">
+       <td valign="top" >Owner Module/Drivers</td>
+       <td valign="top" >Group</td>
+       <td valign="top" >Property Name</td>
+       <td valign="top" >Type</td>
+       <td valign="top" >Property Values</td>
+       <td valign="top" >Object attached</td>
+       <td valign="top" >Description/Restrictions</td>
+       </tr>
+       <tr>
+       <td rowspan="20" valign="top" >DRM</td>
+       <td rowspan="2" valign="top" >Generic</td>
+       <td valign="top" >“EDID”</td>
+       <td valign="top" >BLOB | IMMUTABLE</td>
+       <td valign="top" >0</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >Contains id of edid blob ptr object.</td>
+       </tr>
+       <tr>
+       <td valign="top" >“DPMS”</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ “On”, “Standby”, “Suspend”, “Off” }</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >Contains DPMS operation mode value.</td>
+       </tr>
+       <tr>
+       <td rowspan="1" valign="top" >Plane</td>
+       <td valign="top" >“type”</td>
+       <td valign="top" >ENUM | IMMUTABLE</td>
+       <td valign="top" >{ "Overlay", "Primary", "Cursor" }</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >Plane type</td>
+       </tr>
+       <tr>
+       <td rowspan="2" valign="top" >DVI-I</td>
+       <td valign="top" >“subconnector”</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ “Unknown”, “DVI-D”, “DVI-A” }</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“select subconnector”</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ “Automatic”, “DVI-D”, “DVI-A” }</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td rowspan="13" valign="top" >TV</td>
+       <td valign="top" >“subconnector”</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "Unknown", "Composite", "SVIDEO", "Component", "SCART" }</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“select subconnector”</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "Automatic", "Composite", "SVIDEO", "Component", "SCART" }</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“mode”</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "NTSC_M", "NTSC_J", "NTSC_443", "PAL_B" } etc.</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“left margin”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=100</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“right margin”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=100</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“top margin”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=100</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“bottom margin”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=100</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“brightness”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=100</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“contrast”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=100</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“flicker reduction”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=100</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“overscan”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=100</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“saturation”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=100</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“hue”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=100</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td rowspan="2" valign="top" >Optional</td>
+       <td valign="top" >“scaling mode”</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "None", "Full", "Center", "Full aspect" }</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“dirty”</td>
+       <td valign="top" >ENUM | IMMUTABLE</td>
+       <td valign="top" >{ "Off", "On", "Annotate" }</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td rowspan="21" valign="top" >i915</td>
+       <td rowspan="3" valign="top" >Generic</td>
+       <td valign="top" >"Broadcast RGB"</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "Automatic", "Full", "Limited 16:235" }</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“audio”</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "force-dvi", "off", "auto", "on" }</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >Standard name as in DRM</td>
+       <td valign="top" >Standard type as in DRM</td>
+       <td valign="top" >Standard value as in DRM</td>
+       <td valign="top" >Standard Object as in DRM</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td rowspan="17" valign="top" >SDVO-TV</td>
+       <td valign="top" >“mode”</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "NTSC_M", "NTSC_J", "NTSC_443", "PAL_B" } etc.</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"left_margin"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"right_margin"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"top_margin"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"bottom_margin"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“hpos”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“vpos”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“contrast”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“saturation”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“hue”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“sharpness”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“flicker_filter”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“flicker_filter_adaptive”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“flicker_filter_2d”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“tv_chroma_filter”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“tv_luma_filter”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“dot_crawl”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=1</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >SDVO-TV/LVDS</td>
+       <td valign="top" >“brightness”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td rowspan="3" valign="top" >CDV gma-500</td>
+       <td rowspan="3" valign="top" >Generic</td>
+       <td valign="top" >"Broadcast RGB"</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ “Full”, “Limited 16:235” }</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"Broadcast RGB"</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ “off”, “auto”, “on” }</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >Standard name as in DRM</td>
+       <td valign="top" >Standard type as in DRM</td>
+       <td valign="top" >Standard value as in DRM</td>
+       <td valign="top" >Standard Object as in DRM</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td rowspan="20" valign="top" >Poulsbo</td>
+       <td rowspan="2" valign="top" >Generic</td>
+       <td valign="top" >“backlight”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=100</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >Standard name as in DRM</td>
+       <td valign="top" >Standard type as in DRM</td>
+       <td valign="top" >Standard value as in DRM</td>
+       <td valign="top" >Standard Object as in DRM</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td rowspan="17" valign="top" >SDVO-TV</td>
+       <td valign="top" >“mode”</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "NTSC_M", "NTSC_J", "NTSC_443", "PAL_B" } etc.</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"left_margin"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"right_margin"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"top_margin"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"bottom_margin"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“hpos”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“vpos”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“contrast”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“saturation”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“hue”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“sharpness”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“flicker_filter”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“flicker_filter_adaptive”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“flicker_filter_2d”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“tv_chroma_filter”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“tv_luma_filter”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“dot_crawl”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=1</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >SDVO-TV/LVDS</td>
+       <td valign="top" >“brightness”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max= SDVO dependent</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td rowspan="11" valign="top" >armada</td>
+       <td rowspan="2" valign="top" >CRTC</td>
+       <td valign="top" >"CSC_YUV"</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "Auto" , "CCIR601", "CCIR709" }</td>
+       <td valign="top" >CRTC</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"CSC_RGB"</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "Auto", "Computer system", "Studio" }</td>
+       <td valign="top" >CRTC</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td rowspan="9" valign="top" >Overlay</td>
+       <td valign="top" >"colorkey"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=0xffffff</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"colorkey_min"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=0xffffff</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"colorkey_max"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=0xffffff</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"colorkey_val"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=0xffffff</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"colorkey_alpha"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=0xffffff</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"colorkey_mode"</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "disabled", "Y component", "U component"
+       , "V component", "RGB", “R component", "G component", "B component" }</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"brightness"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=256 + 255</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"contrast"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=0x7fff</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"saturation"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=0x7fff</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td rowspan="2" valign="top" >exynos</td>
+       <td valign="top" >CRTC</td>
+       <td valign="top" >“mode”</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "normal", "blank" }</td>
+       <td valign="top" >CRTC</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >Overlay</td>
+       <td valign="top" >“zpos”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=MAX_PLANE-1</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td rowspan="3" valign="top" >i2c/ch7006_drv</td>
+       <td valign="top" >Generic</td>
+       <td valign="top" >“scale”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=2</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td rowspan="2" valign="top" >TV</td>
+       <td valign="top" >Standard names as in DRM</td>
+       <td valign="top" >Standard types as in DRM</td>
+       <td valign="top" >Standard Values as in DRM</td>
+       <td valign="top" >Standard object as in DRM</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“mode”</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "PAL", "PAL-M","PAL-N"}, ”PAL-Nc"
+       , "PAL-60", "NTSC-M", "NTSC-J" }</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td rowspan="16" valign="top" >nouveau</td>
+       <td rowspan="6" valign="top" >NV10 Overlay</td>
+       <td valign="top" >"colorkey"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=0x01ffffff</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“contrast”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=8192-1</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“brightness”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=1024</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“hue”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=359</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“saturation”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=8192-1</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“iturbt_709”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=1</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td rowspan="2" valign="top" >Nv04 Overlay</td>
+       <td valign="top" >“colorkey”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=0x01ffffff</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“brightness”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=1024</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td rowspan="7" valign="top" >Display</td>
+       <td valign="top" >“dithering mode”</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "auto", "off", "on" }</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“dithering depth”</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "auto", "off", "on", "static 2x2", "dynamic 2x2", "temporal" }</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“underscan”</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "auto", "6 bpc", "8 bpc" }</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“underscan hborder”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=128</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“underscan vborder”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=128</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“vibrant hue”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=180</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“color vibrance”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=200</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >Generic</td>
+       <td valign="top" >Standard name as in DRM</td>
+       <td valign="top" >Standard type as in DRM</td>
+       <td valign="top" >Standard value as in DRM</td>
+       <td valign="top" >Standard Object as in DRM</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td rowspan="2" valign="top" >omap</td>
+       <td rowspan="2" valign="top" >Generic</td>
+       <td valign="top" >“rotation”</td>
+       <td valign="top" >BITMASK</td>
+       <td valign="top" >{ 0, "rotate-0" },
+       { 1, "rotate-90" },
+       { 2, "rotate-180" },
+       { 3, "rotate-270" },
+       { 4, "reflect-x" },
+       { 5, "reflect-y" }</td>
+       <td valign="top" >CRTC, Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >“zorder”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=3</td>
+       <td valign="top" >CRTC, Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >qxl</td>
+       <td valign="top" >Generic</td>
+       <td valign="top" >“hotplug_mode_update"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=1</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td rowspan="10" valign="top" >radeon</td>
+       <td valign="top" >DVI-I</td>
+       <td valign="top" >“coherent”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=1</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >DAC enable load detect</td>
+       <td valign="top" >“load detection”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=1</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >TV Standard</td>
+       <td valign="top" >"tv standard"</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "ntsc", "pal", "pal-m", "pal-60", "ntsc-j"
+       , "scart-pal", "pal-cn", "secam" }</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >legacy TMDS PLL detect</td>
+       <td valign="top" >"tmds_pll"</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "driver", "bios" }</td>
+       <td valign="top" >-</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td rowspan="3" valign="top" >Underscan</td>
+       <td valign="top" >"underscan"</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "off", "on", "auto" }</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"underscan hborder"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=128</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"underscan vborder"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=128</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >Audio</td>
+       <td valign="top" >“audio”</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "off", "on", "auto" }</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >FMT Dithering</td>
+       <td valign="top" >“dither”</td>
+       <td valign="top" >ENUM</td>
+       <td valign="top" >{ "off", "on" }</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >Generic</td>
+       <td valign="top" >Standard name as in DRM</td>
+       <td valign="top" >Standard type as in DRM</td>
+       <td valign="top" >Standard value as in DRM</td>
+       <td valign="top" >Standard Object as in DRM</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td rowspan="3" valign="top" >rcar-du</td>
+       <td rowspan="3" valign="top" >Generic</td>
+       <td valign="top" >"alpha"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=255</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"colorkey"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=0x01ffffff</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       <tr>
+       <td valign="top" >"zpos"</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=1, Max=7</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >TBD</td>
+       </tr>
+       </tbody>
+       </table>
+    </sect2>
   </sect1>
 
   <!-- Internals: vertical blanking -->
@@ -2527,6 +3418,10 @@ void (*disable_vblank) (struct drm_device *dev, int crtc);</synopsis>
       with a call to <function>drm_vblank_cleanup</function> in the driver
       <methodname>unload</methodname> operation handler.
     </para>
+    <sect2>
+      <title>Vertical Blanking and Interrupt Handling Functions Reference</title>
+!Edrivers/gpu/drm/drm_irq.c
+    </sect2>
   </sect1>
 
   <!-- Internals: open/close, file operations and ioctls -->
@@ -2869,17 +3764,16 @@ int num_ioctls;</synopsis>
             <term>DRM_IOCTL_MODESET_CTL</term>
             <listitem>
               <para>
-                This should be called by application level drivers before and
-                after mode setting, since on many devices the vertical blank
-                counter is reset at that time.  Internally, the DRM snapshots
-                the last vblank count when the ioctl is called with the
-                _DRM_PRE_MODESET command, so that the counter won't go backwards
-                (which is dealt with when _DRM_POST_MODESET is used).
+               This was only used for user-mode-settind drivers around
+               modesetting changes to allow the kernel to update the vblank
+               interrupt after mode setting, since on many devices the vertical
+               blank counter is reset to 0 at some point during modeset. Modern
+               drivers should not call this any more since with kernel mode
+               setting it is a no-op.
               </para>
             </listitem>
           </varlistentry>
         </variablelist>
-<!--!Edrivers/char/drm/drm_irq.c-->
       </para>
     </sect1>
 
@@ -2942,6 +3836,96 @@ int num_ioctls;</synopsis>
          probing, so those sections fully apply.
         </para>
       </sect2>
+      <sect2>
+        <title>DPIO</title>
+!Pdrivers/gpu/drm/i915/i915_reg.h DPIO
+       <table id="dpiox2">
+         <title>Dual channel PHY (VLV/CHV)</title>
+         <tgroup cols="8">
+           <colspec colname="c0" />
+           <colspec colname="c1" />
+           <colspec colname="c2" />
+           <colspec colname="c3" />
+           <colspec colname="c4" />
+           <colspec colname="c5" />
+           <colspec colname="c6" />
+           <colspec colname="c7" />
+           <spanspec spanname="ch0" namest="c0" nameend="c3" />
+           <spanspec spanname="ch1" namest="c4" nameend="c7" />
+           <spanspec spanname="ch0pcs01" namest="c0" nameend="c1" />
+           <spanspec spanname="ch0pcs23" namest="c2" nameend="c3" />
+           <spanspec spanname="ch1pcs01" namest="c4" nameend="c5" />
+           <spanspec spanname="ch1pcs23" namest="c6" nameend="c7" />
+           <thead>
+             <row>
+               <entry spanname="ch0">CH0</entry>
+               <entry spanname="ch1">CH1</entry>
+             </row>
+           </thead>
+           <tbody valign="top" align="center">
+             <row>
+               <entry spanname="ch0">CMN/PLL/REF</entry>
+               <entry spanname="ch1">CMN/PLL/REF</entry>
+             </row>
+             <row>
+               <entry spanname="ch0pcs01">PCS01</entry>
+               <entry spanname="ch0pcs23">PCS23</entry>
+               <entry spanname="ch1pcs01">PCS01</entry>
+               <entry spanname="ch1pcs23">PCS23</entry>
+             </row>
+             <row>
+               <entry>TX0</entry>
+               <entry>TX1</entry>
+               <entry>TX2</entry>
+               <entry>TX3</entry>
+               <entry>TX0</entry>
+               <entry>TX1</entry>
+               <entry>TX2</entry>
+               <entry>TX3</entry>
+             </row>
+             <row>
+               <entry spanname="ch0">DDI0</entry>
+               <entry spanname="ch1">DDI1</entry>
+             </row>
+           </tbody>
+         </tgroup>
+       </table>
+       <table id="dpiox1">
+         <title>Single channel PHY (CHV)</title>
+         <tgroup cols="4">
+           <colspec colname="c0" />
+           <colspec colname="c1" />
+           <colspec colname="c2" />
+           <colspec colname="c3" />
+           <spanspec spanname="ch0" namest="c0" nameend="c3" />
+           <spanspec spanname="ch0pcs01" namest="c0" nameend="c1" />
+           <spanspec spanname="ch0pcs23" namest="c2" nameend="c3" />
+           <thead>
+             <row>
+               <entry spanname="ch0">CH0</entry>
+             </row>
+           </thead>
+           <tbody valign="top" align="center">
+             <row>
+               <entry spanname="ch0">CMN/PLL/REF</entry>
+             </row>
+             <row>
+               <entry spanname="ch0pcs01">PCS01</entry>
+               <entry spanname="ch0pcs23">PCS23</entry>
+             </row>
+             <row>
+               <entry>TX0</entry>
+               <entry>TX1</entry>
+               <entry>TX2</entry>
+               <entry>TX3</entry>
+             </row>
+             <row>
+               <entry spanname="ch0">DDI2</entry>
+             </row>
+           </tbody>
+         </tgroup>
+       </table>
+      </sect2>
     </sect1>
 
     <sect1>
@@ -2950,6 +3934,11 @@ int num_ioctls;</synopsis>
        This sections covers all things related to the GEM implementation in the
        i915 driver.
       </para>
+      <sect2>
+        <title>Batchbuffer Parsing</title>
+!Pdrivers/gpu/drm/i915/i915_cmd_parser.c batch buffer command parser
+!Idrivers/gpu/drm/i915/i915_cmd_parser.c
+      </sect2>
     </sect1>
   </chapter>
 </part>
index 4b486fe31b322f5267d1b67ccdad012a1c4cf85c..6f3e4b75e49ea639cc45d65d5dbdb9daac9e790f 100644 (file)
@@ -36,7 +36,7 @@
 #define DPI 72
 #define VFREQ 60 /* Hz */
 #define TIMING_NAME "Linux XGA"
-#define ESTABLISHED_TIMINGS_BITS 0x08 /* Bit 3 -> 1024x768 @60 Hz */
+#define ESTABLISHED_TIMING2_BITS 0x08 /* Bit 3 -> 1024x768 @60 Hz */
 #define HSYNC_POL 0
 #define VSYNC_POL 0
 #define CRC 0x55
index a2799fe33a4d71e6ae0be9d3260ed1773aeb3bd1..bd9bef2a65af7204bb8541fa195871ec5459e480 100644 (file)
@@ -36,7 +36,7 @@
 #define DPI 72
 #define VFREQ 60 /* Hz */
 #define TIMING_NAME "Linux SXGA"
-#define ESTABLISHED_TIMINGS_BITS 0x00 /* none */
+/* No ESTABLISHED_TIMINGx_BITS */
 #define HSYNC_POL 1
 #define VSYNC_POL 1
 #define CRC 0xa0
index 0ded64cfd1f5e2e39af00f70c498dca802c02dc6..a45101c6160c5c418c5a038218d590193f95d6ca 100644 (file)
@@ -36,7 +36,7 @@
 #define DPI 72
 #define VFREQ 60 /* Hz */
 #define TIMING_NAME "Linux UXGA"
-#define ESTABLISHED_TIMINGS_BITS 0x00 /* none */
+/* No ESTABLISHED_TIMINGx_BITS */
 #define HSYNC_POL 1
 #define VSYNC_POL 1
 #define CRC 0x9d
index 96f67cafcf2ef962888bd0098576276e10e5f359..b0d7c69282b46433741ba922e48d42776ec582bd 100644 (file)
@@ -36,7 +36,7 @@
 #define DPI 96
 #define VFREQ 60 /* Hz */
 #define TIMING_NAME "Linux WSXGA"
-#define ESTABLISHED_TIMINGS_BITS 0x00 /* none */
+/* No ESTABLISHED_TIMINGx_BITS */
 #define HSYNC_POL 1
 #define VSYNC_POL 1
 #define CRC 0x26
index 36ed5d571d0a9101fd535d40d0afb92e440ed4d7..3084355e81e76103ce7f29e0ff12d26c7e097746 100644 (file)
@@ -36,7 +36,7 @@
 #define DPI 96
 #define VFREQ 60 /* Hz */
 #define TIMING_NAME "Linux FHD"
-#define ESTABLISHED_TIMINGS_BITS 0x00 /* none */
+/* No ESTABLISHED_TIMINGx_BITS */
 #define HSYNC_POL 1
 #define VSYNC_POL 1
 #define CRC 0x05
diff --git a/Documentation/EDID/800x600.S b/Documentation/EDID/800x600.S
new file mode 100644 (file)
index 0000000..6644e26
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+   800x600.S: EDID data set for standard 800x600 60 Hz monitor
+
+   Copyright (C) 2011 Carsten Emde <C.Emde@osadl.org>
+   Copyright (C) 2014 Linaro Limited
+
+   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.
+*/
+
+/* EDID */
+#define VERSION 1
+#define REVISION 3
+
+/* Display */
+#define CLOCK 40000 /* kHz */
+#define XPIX 800
+#define YPIX 600
+#define XY_RATIO XY_RATIO_4_3
+#define XBLANK 256
+#define YBLANK 28
+#define XOFFSET 40
+#define XPULSE 128
+#define YOFFSET (63+1)
+#define YPULSE (63+4)
+#define DPI 72
+#define VFREQ 60 /* Hz */
+#define TIMING_NAME "Linux SVGA"
+#define ESTABLISHED_TIMING1_BITS 0x01 /* Bit 0: 800x600 @ 60Hz */
+#define HSYNC_POL 1
+#define VSYNC_POL 1
+#define CRC 0xc2
+
+#include "edid.S"
index 7146db1d9e8cf1e855b86002e34820dc270665c9..835db332289b35aa0d126dc7065572dfd82e801e 100644 (file)
@@ -18,7 +18,7 @@ CONFIG_DRM_LOAD_EDID_FIRMWARE was introduced. It allows to provide an
 individually prepared or corrected EDID data set in the /lib/firmware
 directory from where it is loaded via the firmware interface. The code
 (see drivers/gpu/drm/drm_edid_load.c) contains built-in data sets for
-commonly used screen resolutions (1024x768, 1280x1024, 1600x1200,
+commonly used screen resolutions (800x600, 1024x768, 1280x1024, 1600x1200,
 1680x1050, 1920x1080) as binary blobs, but the kernel source tree does
 not contain code to create these data. In order to elucidate the origin
 of the built-in binary EDID blobs and to facilitate the creation of
index ea97ae275fca2fac741d04dc60cb0632faa70bbc..7ac03276d7a2363200f21b09135da008e8b02da9 100644 (file)
 #define XY_RATIO_5_4   0b10
 #define XY_RATIO_16_9  0b11
 
+/* Provide defaults for the timing bits */
+#ifndef ESTABLISHED_TIMING1_BITS
+#define ESTABLISHED_TIMING1_BITS 0x00
+#endif
+#ifndef ESTABLISHED_TIMING2_BITS
+#define ESTABLISHED_TIMING2_BITS 0x00
+#endif
+#ifndef ESTABLISHED_TIMING3_BITS
+#define ESTABLISHED_TIMING3_BITS 0x00
+#endif
+
 #define mfgname2id(v1,v2,v3) \
        ((((v1-'@')&0x1f)<<10)+(((v2-'@')&0x1f)<<5)+((v3-'@')&0x1f))
 #define swap16(v1) ((v1>>8)+((v1&0xff)<<8))
@@ -139,7 +150,7 @@ white_x_y_msb:      .byte   0x50,0x54
    Bit 2       640x480 @ 75 Hz
    Bit 1       800x600 @ 56 Hz
    Bit 0       800x600 @ 60 Hz */
-estbl_timing1: .byte   0x00
+estbl_timing1: .byte   ESTABLISHED_TIMING1_BITS
 
 /* Bit 7       800x600 @ 72 Hz
    Bit 6       800x600 @ 75 Hz
@@ -149,11 +160,11 @@ estbl_timing1:    .byte   0x00
    Bit 2       1024x768 @ 72 Hz
    Bit 1       1024x768 @ 75 Hz
    Bit 0       1280x1024 @ 75 Hz */
-estbl_timing2: .byte   ESTABLISHED_TIMINGS_BITS
+estbl_timing2: .byte   ESTABLISHED_TIMING2_BITS
 
 /* Bit 7       1152x870 @ 75 Hz (Apple Macintosh II)
    Bits 6-0    Other manufacturer-specific display mod */
-estbl_timing3: .byte   0x00
+estbl_timing3: .byte   ESTABLISHED_TIMING3_BITS
 
 /* Standard timing */
 /* X resolution, less 31, divided by 8 (256-2288 pixels) */
index b045fe54986a077792c80e4ad2563b2e38118a86..14f4e6336d88b7eab800163a665c38e34bdd7bb3 100644 (file)
@@ -26,6 +26,7 @@ Contents:
 1.4  target/target_index or setpolicy?
 1.5  target/target_index
 1.6  setpolicy
+1.7  get_intermediate and target_intermediate
 2.   Frequency Table Helpers
 
 
@@ -79,6 +80,10 @@ cpufreq_driver.attr -                A pointer to a NULL-terminated list of
                                "struct freq_attr" which allow to
                                export values to sysfs.
 
+cpufreq_driver.get_intermediate
+and target_intermediate                Used to switch to stable frequency while
+                               changing CPU frequency.
+
 
 1.2 Per-CPU Initialization
 --------------------------
@@ -151,7 +156,7 @@ Some cpufreq-capable processors switch the frequency between certain
 limits on their own. These shall use the ->setpolicy call
 
 
-1.4. target/target_index
+1.5. target/target_index
 -------------
 
 The target_index call has two arguments: struct cpufreq_policy *policy,
@@ -160,6 +165,9 @@ and unsigned int index (into the exposed frequency table).
 The CPUfreq driver must set the new frequency when called here. The
 actual frequency must be determined by freq_table[index].frequency.
 
+It should always restore to earlier frequency (i.e. policy->restore_freq) in
+case of errors, even if we switched to intermediate frequency earlier.
+
 Deprecated:
 ----------
 The target call has three arguments: struct cpufreq_policy *policy,
@@ -179,7 +187,7 @@ Here again the frequency table helper might assist you - see section 2
 for details.
 
 
-1.5 setpolicy
+1.6 setpolicy
 ---------------
 
 The setpolicy call only takes a struct cpufreq_policy *policy as
@@ -190,6 +198,23 @@ setting when policy->policy is CPUFREQ_POLICY_PERFORMANCE, and a
 powersaving-oriented setting when CPUFREQ_POLICY_POWERSAVE. Also check
 the reference implementation in drivers/cpufreq/longrun.c
 
+1.7 get_intermediate and target_intermediate
+--------------------------------------------
+
+Only for drivers with target_index() and CPUFREQ_ASYNC_NOTIFICATION unset.
+
+get_intermediate should return a stable intermediate frequency platform wants to
+switch to, and target_intermediate() should set CPU to to that frequency, before
+jumping to the frequency corresponding to 'index'. Core will take care of
+sending notifications and driver doesn't have to handle them in
+target_intermediate() or target_index().
+
+Drivers can return '0' from get_intermediate() in case they don't wish to switch
+to intermediate frequency for some target frequency. In that case core will
+directly call ->target_index().
+
+NOTE: ->target_index() should restore to policy->restore_freq in case of
+failures as core would send notifications for that.
 
 
 2. Frequency Table Helpers
index a5160d8cbb5f7e1c020e925da23d491ca3b3a0a4..b9ec668bfe6263de85f09ccd2eb06769849c0178 100644 (file)
@@ -20,12 +20,15 @@ Required properties:
        "allwinner,sun5i-a13-ahb-gates-clk" - for the AHB gates on A13
        "allwinner,sun5i-a10s-ahb-gates-clk" - for the AHB gates on A10s
        "allwinner,sun7i-a20-ahb-gates-clk" - for the AHB gates on A20
+       "allwinner,sun6i-a31-ar100-clk" - for the AR100 on A31
        "allwinner,sun6i-a31-ahb1-mux-clk" - for the AHB1 multiplexer on A31
        "allwinner,sun6i-a31-ahb1-gates-clk" - for the AHB1 gates on A31
        "allwinner,sun4i-a10-apb0-clk" - for the APB0 clock
+       "allwinner,sun6i-a31-apb0-clk" - for the APB0 clock on A31
        "allwinner,sun4i-a10-apb0-gates-clk" - for the APB0 gates on A10
        "allwinner,sun5i-a13-apb0-gates-clk" - for the APB0 gates on A13
        "allwinner,sun5i-a10s-apb0-gates-clk" - for the APB0 gates on A10s
+       "allwinner,sun6i-a31-apb0-gates-clk" - for the APB0 gates on A31
        "allwinner,sun7i-a20-apb0-gates-clk" - for the APB0 gates on A20
        "allwinner,sun4i-a10-apb1-clk" - for the APB1 clock
        "allwinner,sun4i-a10-apb1-mux-clk" - for the APB1 clock muxing
@@ -41,6 +44,7 @@ Required properties:
        "allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31
        "allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20
        "allwinner,sun5i-a13-usb-clk" - for usb gates + resets on A13
+       "allwinner,sun6i-a31-usb-clk" - for usb gates + resets on A31
 
 Required properties for all clocks:
 - reg : shall be the control register address for the clock.
index 7faf5a68b3beeba44a767836fdeb6dc64653a5b3..ade4dd4c30f0e12804a94845b71ee462e30f1d99 100644 (file)
@@ -14,18 +14,32 @@ a subtype of a DPLL [2], although a simplified one at that.
 [2] Documentation/devicetree/bindings/clock/ti/dpll.txt
 
 Required properties:
-- compatible : shall be "ti,dra7-apll-clock"
+- compatible : shall be "ti,dra7-apll-clock" or "ti,omap2-apll-clock"
 - #clock-cells : from common clock binding; shall be set to 0.
 - clocks : link phandles of parent clocks (clk-ref and clk-bypass)
 - reg : address and length of the register set for controlling the APLL.
   It contains the information of registers in the following order:
-       "control" - contains the control register base address
-       "idlest" - contains the idlest register base address
+       "control" - contains the control register offset
+       "idlest" - contains the idlest register offset
+       "autoidle" - contains the autoidle register offset (OMAP2 only)
+- ti,clock-frequency : static clock frequency for the clock (OMAP2 only)
+- ti,idlest-shift : bit-shift for the idlest field (OMAP2 only)
+- ti,bit-shift : bit-shift for enable and autoidle fields (OMAP2 only)
 
 Examples:
-       apll_pcie_ck: apll_pcie_ck@4a008200 {
+       apll_pcie_ck: apll_pcie_ck {
                #clock-cells = <0>;
                clocks = <&apll_pcie_in_clk_mux>, <&dpll_pcie_ref_ck>;
-               reg = <0x4a00821c 0x4>, <0x4a008220 0x4>;
+               reg = <0x021c>, <0x0220>;
                compatible = "ti,dra7-apll-clock";
        };
+
+       apll96_ck: apll96_ck {
+               #clock-cells = <0>;
+               compatible = "ti,omap2-apll-clock";
+               clocks = <&sys_ck>;
+               ti,bit-shift = <2>;
+               ti,idlest-shift = <8>;
+               ti,clock-frequency = <96000000>;
+               reg = <0x0500>, <0x0530>, <0x0520>;
+       };
index 30bfdb7c9f18a7137a5e9e7de12accfb4e82f6f2..df57009ff8e74ff48693220908f3a8f1581e15f9 100644 (file)
@@ -24,12 +24,14 @@ Required properties:
                "ti,omap4-dpll-core-clock",
                "ti,omap4-dpll-m4xen-clock",
                "ti,omap4-dpll-j-type-clock",
+               "ti,omap5-mpu-dpll-clock",
                "ti,am3-dpll-no-gate-clock",
                "ti,am3-dpll-j-type-clock",
                "ti,am3-dpll-no-gate-j-type-clock",
                "ti,am3-dpll-clock",
                "ti,am3-dpll-core-clock",
                "ti,am3-dpll-x2-clock",
+               "ti,omap2-dpll-core-clock",
 
 - #clock-cells : from common clock binding; shall be set to 0.
 - clocks : link phandles of parent clocks, first entry lists reference clock
@@ -41,6 +43,7 @@ Required properties:
        "mult-div1" - contains the multiplier / divider register base address
        "autoidle" - contains the autoidle register base address (optional)
   ti,am3-* dpll types do not have autoidle register
+  ti,omap2-* dpll type does not support idlest / autoidle registers
 
 Optional properties:
 - DPLL mode setting - defining any one or more of the following overrides
@@ -73,3 +76,10 @@ Examples:
                clocks = <&sys_clkin_ck>, <&sys_clkin_ck>;
                reg = <0x90>, <0x5c>, <0x68>;
        };
+
+       dpll_ck: dpll_ck {
+               #clock-cells = <0>;
+               compatible = "ti,omap2-dpll-core-clock";
+               clocks = <&sys_ck>, <&sys_ck>;
+               reg = <0x0500>, <0x0540>;
+       };
diff --git a/Documentation/devicetree/bindings/clock/ti/dra7-atl.txt b/Documentation/devicetree/bindings/clock/ti/dra7-atl.txt
new file mode 100644 (file)
index 0000000..585e8c1
--- /dev/null
@@ -0,0 +1,96 @@
+Device Tree Clock bindings for ATL (Audio Tracking Logic) of DRA7 SoC.
+
+The ATL IP is used to generate clock to be used to synchronize baseband and
+audio codec. A single ATL IP provides four ATL clock instances sharing the same
+functional clock but can be configured to provide different clocks.
+ATL can maintain a clock averages to some desired frequency based on the bws/aws
+signals - can compensate the drift between the two ws signal.
+
+In order to provide the support for ATL and it's output clocks (which can be used
+internally within the SoC or external components) two sets of bindings is needed:
+
+Clock tree binding:
+This binding uses the common clock binding[1].
+To be able to integrate the ATL clocks with DT clock tree.
+Provides ccf level representation of the ATL clocks to be used by drivers.
+Since the clock instances are part of a single IP this binding is used as a node
+for the DT clock tree, the IP driver is needed to handle the actual configuration
+of the IP.
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible : shall be "ti,dra7-atl-clock"
+- #clock-cells : from common clock binding; shall be set to 0.
+- clocks : link phandles to functional clock of ATL
+
+Binding for the IP driver:
+This binding is used to configure the IP driver which is going to handle the
+configuration of the IP for the ATL clock instances.
+
+Required properties:
+- compatible : shall be "ti,dra7-atl"
+- reg : base address for the ATL IP
+- ti,provided-clocks : List of phandles to the clocks associated with the ATL
+- clocks : link phandles to functional clock of ATL
+- clock-names : Shall be set to "fck"
+- ti,hwmods : Shall be set to "atl"
+
+Optional properties:
+Configuration of ATL instances:
+- atl{0/1/2/3} {
+       - bws : Baseband word select signal selection
+       - aws : Audio word select signal selection
+};
+
+For valid word select signals, see the dt-bindings/clk/ti-dra7-atl.h include
+file.
+
+Examples:
+/* clock bindings for atl provided clocks */
+atl_clkin0_ck: atl_clkin0_ck {
+       #clock-cells = <0>;
+       compatible = "ti,dra7-atl-clock";
+       clocks = <&atl_gfclk_mux>;
+};
+
+atl_clkin1_ck: atl_clkin1_ck {
+       #clock-cells = <0>;
+       compatible = "ti,dra7-atl-clock";
+       clocks = <&atl_gfclk_mux>;
+};
+
+atl_clkin2_ck: atl_clkin2_ck {
+       #clock-cells = <0>;
+       compatible = "ti,dra7-atl-clock";
+       clocks = <&atl_gfclk_mux>;
+};
+
+atl_clkin3_ck: atl_clkin3_ck {
+       #clock-cells = <0>;
+       compatible = "ti,dra7-atl-clock";
+       clocks = <&atl_gfclk_mux>;
+};
+
+/* binding for the IP */
+atl: atl@4843c000 {
+       compatible = "ti,dra7-atl";
+       reg = <0x4843c000 0x3ff>;
+       ti,hwmods = "atl";
+       ti,provided-clocks = <&atl_clkin0_ck>, <&atl_clkin1_ck>,
+                               <&atl_clkin2_ck>, <&atl_clkin3_ck>;
+       clocks = <&atl_gfclk_mux>;
+       clock-names = "fck";
+       status = "disabled";
+};
+
+#include <dt-bindings/clk/ti-dra7-atl.h>
+
+&atl {
+       status = "okay";
+
+       atl2 {
+               bws = <DRA7_ATL_WS_MCASP2_FSX>;
+               aws = <DRA7_ATL_WS_MCASP3_FSX>;
+       };
+};
index 125281aaa4ca0390713d92b6714c135098604b6d..03f8fdee62a7e3e2c559789eb501f9f12d6542a5 100644 (file)
@@ -25,6 +25,11 @@ Required properties:
                          to map clockdomains properly
   "ti,hsdiv-gate-clock" - gate clock with OMAP36xx specific hardware handling,
                          required for a hardware errata
+  "ti,composite-gate-clock" - composite gate clock, to be part of composite
+                             clock
+  "ti,composite-no-wait-gate-clock" - composite gate clock that does not wait
+                                     for clock to be active before returning
+                                     from clk_enable()
 - #clock-cells : from common clock binding; shall be set to 0
 - clocks : link to phandle of parent clock
 - reg : offset for register controlling adjustable gate, not needed for
@@ -41,7 +46,7 @@ Examples:
                #clock-cells = <0>;
                compatible = "ti,gate-clock";
                clocks = <&core_96m_fck>;
-               reg = <0x48004a00 0x4>;
+               reg = <0x0a00>;
                ti,bit-shift = <25>;
        };
 
@@ -57,7 +62,7 @@ Examples:
                #clock-cells = <0>;
                compatible = "ti,dss-gate-clock";
                clocks = <&dpll4_m4x2_ck>;
-               reg = <0x48004e00 0x4>;
+               reg = <0x0e00>;
                ti,bit-shift = <0>;
        };
 
@@ -65,7 +70,7 @@ Examples:
                #clock-cells = <0>;
                compatible = "ti,am35xx-gate-clock";
                clocks = <&ipss_ick>;
-               reg = <0x4800259c 0x4>;
+               reg = <0x059c>;
                ti,bit-shift = <1>;
        };
 
@@ -80,6 +85,22 @@ Examples:
                compatible = "ti,hsdiv-gate-clock";
                clocks = <&dpll4_m2x2_mul_ck>;
                ti,bit-shift = <0x1b>;
-               reg = <0x48004d00 0x4>;
+               reg = <0x0d00>;
                ti,set-bit-to-disable;
        };
+
+       vlynq_gate_fck: vlynq_gate_fck {
+               #clock-cells = <0>;
+               compatible = "ti,composite-gate-clock";
+               clocks = <&core_ck>;
+               ti,bit-shift = <3>;
+               reg = <0x0200>;
+       };
+
+       sys_clkout2_src_gate: sys_clkout2_src_gate {
+               #clock-cells = <0>;
+               compatible = "ti,composite-no-wait-gate-clock";
+               clocks = <&core_ck>;
+               ti,bit-shift = <15>;
+               reg = <0x0070>;
+       };
index 064e8caccac37141428ca2ce0cfaed80fb4daacf..3111a409fea6cebb739a01592874d2c88d56328b 100644 (file)
@@ -21,6 +21,8 @@ Required properties:
   "ti,omap3-dss-interface-clock" - interface clock with DSS specific HW handling
   "ti,omap3-ssi-interface-clock" - interface clock with SSI specific HW handling
   "ti,am35xx-interface-clock" - interface clock with AM35xx specific HW handling
+  "ti,omap2430-interface-clock" - interface clock with OMAP2430 specific HW
+                                 handling
 - #clock-cells : from common clock binding; shall be set to 0
 - clocks : link to phandle of parent clock
 - reg : base address for the control register
index efa8b8451f93be2600cbb41fa5c8275c7ea5ddaf..b48f4ef31d937ff8c4944e379d55357045653f02 100644 (file)
@@ -136,6 +136,7 @@ of the following host1x client modules:
   - compatible: "nvidia,tegra<chip>-hdmi"
   - reg: Physical base address and length of the controller's registers.
   - interrupts: The interrupt outputs from the controller.
+  - hdmi-supply: supply for the +5V HDMI connector pin
   - vdd-supply: regulator for supply voltage
   - pll-supply: regulator for PLL
   - clocks: Must contain an entry for each entry in clock-names.
@@ -180,6 +181,7 @@ of the following host1x client modules:
     See ../reset/reset.txt for details.
   - reset-names: Must include the following entries:
     - dsi
+  - avdd-dsi-supply: phandle of a supply that powers the DSI controller
   - nvidia,mipi-calibrate: Should contain a phandle and a specifier specifying
     which pads are used by this DSI output and need to be calibrated. See also
     ../mipi/nvidia,tegra114-mipi.txt.
index c55b8c016a9e2bc55490efb3228a285a129725fc..1b66a413fb9dfed9c6bb2c94d5cc02b1e7e26532 100644 (file)
@@ -1,7 +1,13 @@
 Binding for TI/National Semiconductor LP55xx Led Drivers
 
 Required properties:
-- compatible: "national,lp5521" or "national,lp5523" or "ti,lp5562" or "ti,lp8501"
+- compatible: one of
+       national,lp5521
+       national,lp5523
+       ti,lp55231
+       ti,lp5562
+       ti,lp8501
+
 - reg: I2C slave address
 - clock-mode: Input clock mode, (0: automode, 1: internal, 2: external)
 
index 7297107cf83285d5fe7b6d8254eafc7c64cc2c85..6c6583c35f2ff6bfe41ca3e9747dc720d6d72d84 100644 (file)
@@ -13,6 +13,8 @@ LED sub-node properties:
   For the pwms and pwm-names property please refer to:
   Documentation/devicetree/bindings/pwm/pwm.txt
 - max-brightness : Maximum brightness possible for the LED
+- active-low : (optional) For PWMs where the LED is wired to supply
+  rather than ground.
 - label :  (optional)
   see Documentation/devicetree/bindings/leds/common.txt
 - linux,default-trigger :  (optional)
index 8e15ec35ac99339564b4bf3333ea7c104cdd04d6..b9ee7b98d3e234195ede810f6c32e6afa5a22411 100644 (file)
@@ -5,7 +5,22 @@ to control the power resources, including power scripts. For now, the
 binding only supports the complete shutdown of the system after poweroff.
 
 Required properties:
-- compatible : must be "ti,twl4030-power"
+- compatible : must be one of the following
+       "ti,twl4030-power"
+       "ti,twl4030-power-reset"
+       "ti,twl4030-power-idle"
+       "ti,twl4030-power-idle-osc-off"
+
+The use of ti,twl4030-power-reset is recommended at least on
+3530 that needs a special configuration for warm reset to work.
+
+When using ti,twl4030-power-idle, the TI recommended configuration
+for idle modes is loaded to the tlw4030 PMIC.
+
+When using ti,twl4030-power-idle-osc-off, the TI recommended
+configuration is used with the external oscillator being shut
+down during off-idle. Note that this does not work on all boards
+depending on how the external oscillator is wired.
 
 Optional properties:
 - ti,use_poweroff: With this flag, the chip will initiates an ACTIVE-to-OFF or
diff --git a/Documentation/devicetree/bindings/net/amd-xgbe-phy.txt b/Documentation/devicetree/bindings/net/amd-xgbe-phy.txt
new file mode 100644 (file)
index 0000000..d01ed63
--- /dev/null
@@ -0,0 +1,17 @@
+* AMD 10GbE PHY driver (amd-xgbe-phy)
+
+Required properties:
+- compatible: Should be "amd,xgbe-phy-seattle-v1a" and
+  "ethernet-phy-ieee802.3-c45"
+- reg: Address and length of the register sets for the device
+   - SerDes Rx/Tx registers
+   - SerDes integration registers (1/2)
+   - SerDes integration registers (2/2)
+
+Example:
+       xgbe_phy@e1240800 {
+               compatible = "amd,xgbe-phy-seattle-v1a", "ethernet-phy-ieee802.3-c45";
+               reg = <0 0xe1240800 0 0x00400>,
+                     <0 0xe1250000 0 0x00060>,
+                     <0 0xe1250080 0 0x00004>;
+       };
diff --git a/Documentation/devicetree/bindings/net/amd-xgbe.txt b/Documentation/devicetree/bindings/net/amd-xgbe.txt
new file mode 100644 (file)
index 0000000..ea0c790
--- /dev/null
@@ -0,0 +1,34 @@
+* AMD 10GbE driver (amd-xgbe)
+
+Required properties:
+- compatible: Should be "amd,xgbe-seattle-v1a"
+- reg: Address and length of the register sets for the device
+   - MAC registers
+   - PCS registers
+- interrupt-parent: Should be the phandle for the interrupt controller
+  that services interrupts for this device
+- interrupts: Should contain the amd-xgbe interrupt
+- clocks: Should be the DMA clock for the amd-xgbe device (used for
+  calculating the correct Rx interrupt watchdog timer value on a DMA
+  channel for coalescing)
+- clock-names: Should be the name of the DMA clock, "dma_clk"
+- phy-handle: See ethernet.txt file in the same directory
+- phy-mode: See ethernet.txt file in the same directory
+
+Optional properties:
+- mac-address: mac address to be assigned to the device. Can be overridden
+  by UEFI.
+
+Example:
+       xgbe@e0700000 {
+               compatible = "amd,xgbe-seattle-v1a";
+               reg = <0 0xe0700000 0 0x80000>,
+                     <0 0xe0780000 0 0x80000>;
+               interrupt-parent = <&gic>;
+               interrupts = <0 325 4>;
+               clocks = <&xgbe_clk>;
+               clock-names = "dma_clk";
+               phy-handle = <&phy>;
+               phy-mode = "xgmii";
+               mac-address = [ 02 a1 a2 a3 a4 a5 ];
+       };
index f2febb94550e8868b32497001f3a6514feddddfb..451fef26b4dfaf05b6782099e54816d52a52baa8 100644 (file)
@@ -24,7 +24,7 @@ Optional properties:
 - fixed-link: When the GENET interface is connected to a MoCA hardware block or
   when operating in a RGMII to RGMII type of connection, or when the MDIO bus is
   voluntarily disabled, this property should be used to describe the "fixed link".
-  See Documentation/devicetree/bindings/net/fsl-tsec-phy.txt for information on
+  See Documentation/devicetree/bindings/net/fixed-link.txt for information on
   the property specifics
 
 Required child nodes:
diff --git a/Documentation/devicetree/bindings/net/broadcom-systemport.txt b/Documentation/devicetree/bindings/net/broadcom-systemport.txt
new file mode 100644 (file)
index 0000000..c183ea9
--- /dev/null
@@ -0,0 +1,29 @@
+* Broadcom BCM7xxx Ethernet Systemport Controller (SYSTEMPORT)
+
+Required properties:
+- compatible: should be one of "brcm,systemport-v1.00" or "brcm,systemport"
+- reg: address and length of the register set for the device.
+- interrupts: interrupts for the device, first cell must be for the the rx
+  interrupts, and the second cell should be for the transmit queues
+- local-mac-address: Ethernet MAC address (48 bits) of this adapter
+- phy-mode: Should be a string describing the PHY interface to the
+  Ethernet switch/PHY, see Documentation/devicetree/bindings/net/ethernet.txt
+- fixed-link: see Documentation/devicetree/bindings/net/fixed-link.txt for
+  the property specific details
+
+Optional properties:
+- systemport,num-tier2-arb: number of tier 2 arbiters, an integer
+- systemport,num-tier1-arb: number of tier 1 arbiters, an integer
+- systemport,num-txq: number of HW transmit queues, an integer
+- systemport,num-rxq: number of HW receive queues, an integer
+
+Example:
+ethernet@f04a0000 {
+       compatible = "brcm,systemport-v1.00";
+       reg = <0xf04a0000 0x4650>;
+       local-mac-address = [ 00 11 22 33 44 55 ];
+       fixed-link = <0 1 1000 0 0>;
+       phy-mode = "gmii";
+       interrupts = <0x0 0x16 0x0>,
+               <0x0 0x17 0x0>;
+};
diff --git a/Documentation/devicetree/bindings/net/can/xilinx_can.txt b/Documentation/devicetree/bindings/net/can/xilinx_can.txt
new file mode 100644 (file)
index 0000000..fe38847
--- /dev/null
@@ -0,0 +1,44 @@
+Xilinx Axi CAN/Zynq CANPS controller Device Tree Bindings
+---------------------------------------------------------
+
+Required properties:
+- compatible           : Should be "xlnx,zynq-can-1.0" for Zynq CAN
+                         controllers and "xlnx,axi-can-1.00.a" for Axi CAN
+                         controllers.
+- reg                  : Physical base address and size of the Axi CAN/Zynq
+                         CANPS registers map.
+- interrupts           : Property with a value describing the interrupt
+                         number.
+- interrupt-parent     : Must be core interrupt controller
+- clock-names          : List of input clock names - "can_clk", "pclk"
+                         (For CANPS), "can_clk" , "s_axi_aclk"(For AXI CAN)
+                         (See clock bindings for details).
+- clocks               : Clock phandles (see clock bindings for details).
+- tx-fifo-depth                : Can Tx fifo depth.
+- rx-fifo-depth                : Can Rx fifo depth.
+
+
+Example:
+
+For Zynq CANPS Dts file:
+       zynq_can_0: can@e0008000 {
+                       compatible = "xlnx,zynq-can-1.0";
+                       clocks = <&clkc 19>, <&clkc 36>;
+                       clock-names = "can_clk", "pclk";
+                       reg = <0xe0008000 0x1000>;
+                       interrupts = <0 28 4>;
+                       interrupt-parent = <&intc>;
+                       tx-fifo-depth = <0x40>;
+                       rx-fifo-depth = <0x40>;
+               };
+For Axi CAN Dts file:
+       axi_can_0: axi-can@40000000 {
+                       compatible = "xlnx,axi-can-1.00.a";
+                       clocks = <&clkc 0>, <&clkc 1>;
+                       clock-names = "can_clk","s_axi_aclk" ;
+                       reg = <0x40000000 0x10000>;
+                       interrupt-parent = <&intc>;
+                       interrupts = <0 59 1>;
+                       tx-fifo-depth = <0x40>;
+                       rx-fifo-depth = <0x40>;
+               };
index 7ff57a119f81f449cfc2afb5ef988183071a8aa8..764c0c79b43d391435b847614c8d6c7aa4f06653 100644 (file)
@@ -2,7 +2,9 @@ TI CPSW Phy mode Selection Device Tree Bindings
 -----------------------------------------------
 
 Required properties:
-- compatible           : Should be "ti,am3352-cpsw-phy-sel"
+- compatible           : Should be "ti,am3352-cpsw-phy-sel" for am335x platform and
+                         "ti,dra7xx-cpsw-phy-sel" for dra7xx platform
+                         "ti,am43xx-cpsw-phy-sel" for am43xx platform
 - reg                  : physical base address and size of the cpsw
                          registers map
 - reg-names            : names of the register map given in "reg" node
diff --git a/Documentation/devicetree/bindings/net/fixed-link.txt b/Documentation/devicetree/bindings/net/fixed-link.txt
new file mode 100644 (file)
index 0000000..82bf7e0
--- /dev/null
@@ -0,0 +1,42 @@
+Fixed link Device Tree binding
+------------------------------
+
+Some Ethernet MACs have a "fixed link", and are not connected to a
+normal MDIO-managed PHY device. For those situations, a Device Tree
+binding allows to describe a "fixed link".
+
+Such a fixed link situation is described by creating a 'fixed-link'
+sub-node of the Ethernet MAC device node, with the following
+properties:
+
+* 'speed' (integer, mandatory), to indicate the link speed. Accepted
+  values are 10, 100 and 1000
+* 'full-duplex' (boolean, optional), to indicate that full duplex is
+  used. When absent, half duplex is assumed.
+* 'pause' (boolean, optional), to indicate that pause should be
+  enabled.
+* 'asym-pause' (boolean, optional), to indicate that asym_pause should
+  be enabled.
+
+Old, deprecated 'fixed-link' binding:
+
+* A 'fixed-link' property in the Ethernet MAC node, with 5 cells, of the
+  form <a b c d e> with the following accepted values:
+  - a: emulated PHY ID, choose any but but unique to the all specified
+    fixed-links, from 0 to 31
+  - b: duplex configuration: 0 for half duplex, 1 for full duplex
+  - c: link speed in Mbits/sec, accepted values are: 10, 100 and 1000
+  - d: pause configuration: 0 for no pause, 1 for pause
+  - e: asymmetric pause configuration: 0 for no asymmetric pause, 1 for
+    asymmetric pause
+
+Example:
+
+ethernet@0 {
+       ...
+       fixed-link {
+             speed = <1000>;
+             full-duplex;
+       };
+       ...
+};
index 737cdef4f9036eb6069b9f536f137351dc42b137..be6ea8960f208c72b7d69e0286c56712661ccb2c 100644 (file)
@@ -42,10 +42,7 @@ Properties:
     interrupt.  For TSEC and eTSEC devices, the first interrupt is
     transmit, the second is receive, and the third is error.
   - phy-handle : See ethernet.txt file in the same directory.
-  - fixed-link : <a b c d e> where a is emulated phy id - choose any,
-    but unique to the all specified fixed-links, b is duplex - 0 half,
-    1 full, c is link speed - d#10/d#100/d#1000, d is pause - 0 no
-    pause, 1 pause, e is asym_pause - 0 no asym_pause, 1 asym_pause.
+  - fixed-link : See fixed-link.txt in the same directory.
   - phy-connection-type : See ethernet.txt file in the same directory.
     This property is only really needed if the connection is of type
     "rgmii-id", as all other connection types are detected by hardware.
diff --git a/Documentation/devicetree/bindings/net/hisilicon-hix5hd2-gmac.txt b/Documentation/devicetree/bindings/net/hisilicon-hix5hd2-gmac.txt
new file mode 100644 (file)
index 0000000..75d398b
--- /dev/null
@@ -0,0 +1,36 @@
+Hisilicon hix5hd2 gmac controller
+
+Required properties:
+- compatible: should be "hisilicon,hix5hd2-gmac".
+- reg: specifies base physical address(s) and size of the device registers.
+  The first region is the MAC register base and size.
+  The second region is external interface control register.
+- interrupts: should contain the MAC interrupt.
+- #address-cells: must be <1>.
+- #size-cells: must be <0>.
+- phy-mode: see ethernet.txt [1].
+- phy-handle: see ethernet.txt [1].
+- mac-address: see ethernet.txt [1].
+- clocks: clock phandle and specifier pair.
+
+- PHY subnode: inherits from phy binding [2]
+
+[1] Documentation/devicetree/bindings/net/ethernet.txt
+[2] Documentation/devicetree/bindings/net/phy.txt
+
+Example:
+       gmac0: ethernet@f9840000 {
+               compatible = "hisilicon,hix5hd2-gmac";
+               reg = <0xf9840000 0x1000>,<0xf984300c 0x4>;
+               interrupts = <0 71 4>;
+               #address-cells = <1>;
+               #size-cells = <0>;
+               phy-mode = "mii";
+               phy-handle = <&phy2>;
+               mac-address = [00 00 00 00 00 00];
+               clocks = <&clock HIX5HD2_MAC0_CLK>;
+
+               phy2: ethernet-phy@2 {
+                       reg = <2>;
+               };
+       };
diff --git a/Documentation/devicetree/bindings/net/ieee802154/at86rf230.txt b/Documentation/devicetree/bindings/net/ieee802154/at86rf230.txt
new file mode 100644 (file)
index 0000000..d3bbdde
--- /dev/null
@@ -0,0 +1,23 @@
+* AT86RF230 IEEE 802.15.4 *
+
+Required properties:
+  - compatible:                should be "atmel,at86rf230", "atmel,at86rf231",
+                       "atmel,at86rf233" or "atmel,at86rf212"
+  - spi-max-frequency: maximal bus speed, should be set to 7500000 depends
+                       sync or async operation mode
+  - reg:               the chipselect index
+  - interrupts:                the interrupt generated by the device
+
+Optional properties:
+  - reset-gpio:                GPIO spec for the rstn pin
+  - sleep-gpio:                GPIO spec for the slp_tr pin
+
+Example:
+
+       at86rf231@0 {
+               compatible = "atmel,at86rf231";
+               spi-max-frequency = <7500000>;
+               reg = <0>;
+               interrupts = <19 1>;
+               interrupt-parent = <&gpio3>;
+       };
index d54d0cc794871b29cbbbf9fbedd6242defa63677..bbdf9a7359a2ef021c3d6f780e2c14e7f3434740 100644 (file)
@@ -1,9 +1,18 @@
-Micrel KS8851 Ethernet mac
+Micrel KS8851 Ethernet mac (MLL)
 
 Required properties:
-- compatible = "micrel,ks8851-ml" of parallel interface
+- compatible = "micrel,ks8851-mll" of parallel interface
 - reg : 2 physical address and size of registers for data and command
 - interrupts : interrupt connection
 
+Micrel KS8851 Ethernet mac (SPI)
+
+Required properties:
+- compatible = "micrel,ks8851" or the deprecated "ks8851"
+- reg : chip select number
+- interrupts : interrupt connection
+
 Optional properties:
-- vdd-supply:  supply for Ethernet mac
+- vdd-supply: analog 3.3V supply for Ethernet mac
+- vdd-io-supply: digital 1.8V IO supply for Ethernet mac
+- reset-gpios: reset_n input pin
diff --git a/Documentation/devicetree/bindings/net/micrel-ksz9021.txt b/Documentation/devicetree/bindings/net/micrel-ksz9021.txt
deleted file mode 100644 (file)
index 997a63f..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-Micrel KSZ9021 Gigabit Ethernet PHY
-
-Some boards require special tuning values, particularly when it comes to
-clock delays.  You can specify clock delay values by adding
-micrel-specific properties to an Ethernet OF device node.
-
-All skew control options are specified in picoseconds.  The minimum
-value is 0, and the maximum value is 3000.
-
-Optional properties:
- - rxc-skew-ps : Skew control of RXC pad
- - rxdv-skew-ps : Skew control of RX CTL pad
- - txc-skew-ps : Skew control of TXC pad
- - txen-skew-ps : Skew control of TX_CTL pad
- - rxd0-skew-ps : Skew control of RX data 0 pad
- - rxd1-skew-ps : Skew control of RX data 1 pad
- - rxd2-skew-ps : Skew control of RX data 2 pad
- - rxd3-skew-ps : Skew control of RX data 3 pad
- - txd0-skew-ps : Skew control of TX data 0 pad
- - txd1-skew-ps : Skew control of TX data 1 pad
- - txd2-skew-ps : Skew control of TX data 2 pad
- - txd3-skew-ps : Skew control of TX data 3 pad
-
-Examples:
-
-       /* Attach to an Ethernet device with autodetected PHY */
-       &enet {
-               rxc-skew-ps = <3000>;
-               rxdv-skew-ps = <0>;
-               txc-skew-ps = <3000>;
-               txen-skew-ps = <0>;
-               status = "okay";
-       };
-
-       /* Attach to an explicitly-specified PHY */
-       mdio {
-               phy0: ethernet-phy@0 {
-                       rxc-skew-ps = <3000>;
-                       rxdv-skew-ps = <0>;
-                       txc-skew-ps = <3000>;
-                       txen-skew-ps = <0>;
-                       reg = <0>;
-               };
-       };
-       ethernet@70000 {
-               status = "okay";
-               phy = <&phy0>;
-               phy-mode = "rgmii-id";
-       };
diff --git a/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt b/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt
new file mode 100644 (file)
index 0000000..692076f
--- /dev/null
@@ -0,0 +1,83 @@
+Micrel KSZ9021/KSZ9031 Gigabit Ethernet PHY
+
+Some boards require special tuning values, particularly when it comes to
+clock delays. You can specify clock delay values by adding
+micrel-specific properties to an Ethernet OF device node.
+
+Note that these settings are applied after any phy-specific fixup from
+phy_fixup_list (see phy_init_hw() from drivers/net/phy/phy_device.c),
+and therefore may overwrite them.
+
+KSZ9021:
+
+  All skew control options are specified in picoseconds. The minimum
+  value is 0, the maximum value is 3000, and it is incremented by 200ps
+  steps.
+
+  Optional properties:
+
+    - rxc-skew-ps : Skew control of RXC pad
+    - rxdv-skew-ps : Skew control of RX CTL pad
+    - txc-skew-ps : Skew control of TXC pad
+    - txen-skew-ps : Skew control of TX CTL pad
+    - rxd0-skew-ps : Skew control of RX data 0 pad
+    - rxd1-skew-ps : Skew control of RX data 1 pad
+    - rxd2-skew-ps : Skew control of RX data 2 pad
+    - rxd3-skew-ps : Skew control of RX data 3 pad
+    - txd0-skew-ps : Skew control of TX data 0 pad
+    - txd1-skew-ps : Skew control of TX data 1 pad
+    - txd2-skew-ps : Skew control of TX data 2 pad
+    - txd3-skew-ps : Skew control of TX data 3 pad
+
+KSZ9031:
+
+  All skew control options are specified in picoseconds. The minimum
+  value is 0, and the maximum is property-dependent. The increment
+  step is 60ps.
+
+  Optional properties:
+
+    Maximum value of 1860:
+
+      - rxc-skew-ps : Skew control of RX clock pad
+      - txc-skew-ps : Skew control of TX clock pad
+
+    Maximum value of 900:
+
+      - rxdv-skew-ps : Skew control of RX CTL pad
+      - txen-skew-ps : Skew control of TX CTL pad
+      - rxd0-skew-ps : Skew control of RX data 0 pad
+      - rxd1-skew-ps : Skew control of RX data 1 pad
+      - rxd2-skew-ps : Skew control of RX data 2 pad
+      - rxd3-skew-ps : Skew control of RX data 3 pad
+      - txd0-skew-ps : Skew control of TX data 0 pad
+      - txd1-skew-ps : Skew control of TX data 1 pad
+      - txd2-skew-ps : Skew control of TX data 2 pad
+      - txd3-skew-ps : Skew control of TX data 3 pad
+
+Examples:
+
+       /* Attach to an Ethernet device with autodetected PHY */
+       &enet {
+               rxc-skew-ps = <3000>;
+               rxdv-skew-ps = <0>;
+               txc-skew-ps = <3000>;
+               txen-skew-ps = <0>;
+               status = "okay";
+       };
+
+       /* Attach to an explicitly-specified PHY */
+       mdio {
+               phy0: ethernet-phy@0 {
+                       rxc-skew-ps = <3000>;
+                       rxdv-skew-ps = <0>;
+                       txc-skew-ps = <3000>;
+                       txen-skew-ps = <0>;
+                       reg = <0>;
+               };
+       };
+       ethernet@70000 {
+               status = "okay";
+               phy = <&phy0>;
+               phy-mode = "rgmii-id";
+       };
diff --git a/Documentation/devicetree/bindings/net/nfc/pn544.txt b/Documentation/devicetree/bindings/net/nfc/pn544.txt
new file mode 100644 (file)
index 0000000..dab69f3
--- /dev/null
@@ -0,0 +1,35 @@
+* NXP Semiconductors PN544 NFC Controller
+
+Required properties:
+- compatible: Should be "nxp,pn544-i2c".
+- clock-frequency: I²C work frequency.
+- reg: address on the bus
+- interrupt-parent: phandle for the interrupt gpio controller
+- interrupts: GPIO interrupt to which the chip is connected
+- enable-gpios: Output GPIO pin used for enabling/disabling the PN544
+- firmware-gpios: Output GPIO pin used to enter firmware download mode
+
+Optional SoC Specific Properties:
+- pinctrl-names: Contains only one value - "default".
+- pintctrl-0: Specifies the pin control groups used for this controller.
+
+Example (for ARM-based BeagleBone with PN544 on I2C2):
+
+&i2c2 {
+
+       status = "okay";
+
+       pn544: pn544@28 {
+
+               compatible = "nxp,pn544-i2c";
+
+               reg = <0x28>;
+               clock-frequency = <400000>;
+
+               interrupt-parent = <&gpio1>;
+               interrupts = <17 GPIO_ACTIVE_HIGH>;
+
+               enable-gpios = <&gpio3 21 GPIO_ACTIVE_HIGH>;
+               firmware-gpios = <&gpio3 19 GPIO_ACTIVE_HIGH>;
+       };
+};
diff --git a/Documentation/devicetree/bindings/net/nfc/st21nfca.txt b/Documentation/devicetree/bindings/net/nfc/st21nfca.txt
new file mode 100644 (file)
index 0000000..e4faa2e
--- /dev/null
@@ -0,0 +1,33 @@
+* STMicroelectronics SAS. ST21NFCA NFC Controller
+
+Required properties:
+- compatible: Should be "st,st21nfca_i2c".
+- clock-frequency: I²C work frequency.
+- reg: address on the bus
+- interrupt-parent: phandle for the interrupt gpio controller
+- interrupts: GPIO interrupt to which the chip is connected
+- enable-gpios: Output GPIO pin used for enabling/disabling the ST21NFCA
+
+Optional SoC Specific Properties:
+- pinctrl-names: Contains only one value - "default".
+- pintctrl-0: Specifies the pin control groups used for this controller.
+
+Example (for ARM-based BeagleBoard xM with ST21NFCA on I2C2):
+
+&i2c2 {
+
+       status = "okay";
+
+       st21nfca: st21nfca@1 {
+
+               compatible = "st,st21nfca_i2c";
+
+               reg = <0x01>;
+               clock-frequency = <400000>;
+
+               interrupt-parent = <&gpio5>;
+               interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
+
+               enable-gpios = <&gpio5 29 GPIO_ACTIVE_HIGH>;
+       };
+};
index 8dd3ef7bc56b560b1b4eb29c25feaf9f5d1785cc..1e436133685f91f470ba7f63e302503422bd818e 100644 (file)
@@ -12,6 +12,7 @@ Required properties:
 Optional SoC Specific Properties:
 - pinctrl-names: Contains only one value - "default".
 - pintctrl-0: Specifies the pin control groups used for this controller.
+- autosuspend-delay: Specify autosuspend delay in milliseconds.
 
 Example (for ARM-based BeagleBone with TRF7970A on SPI1):
 
@@ -29,6 +30,7 @@ Example (for ARM-based BeagleBone with TRF7970A on SPI1):
                ti,enable-gpios = <&gpio2 2 GPIO_ACTIVE_LOW>,
                                  <&gpio2 5 GPIO_ACTIVE_LOW>;
                vin-supply = <&ldo3_reg>;
+               autosuspend-delay = <30000>;
                status = "okay";
        };
 };
diff --git a/Documentation/devicetree/bindings/net/via-rhine.txt b/Documentation/devicetree/bindings/net/via-rhine.txt
new file mode 100644 (file)
index 0000000..334eca2
--- /dev/null
@@ -0,0 +1,17 @@
+* VIA Rhine 10/100 Network Controller
+
+Required properties:
+- compatible : Should be "via,vt8500-rhine" for integrated
+       Rhine controllers found in VIA VT8500, WonderMedia WM8950
+       and similar. These are listed as 1106:3106 rev. 0x84 on the
+       virtual PCI bus under vendor-provided kernels
+- reg : Address and length of the io space
+- interrupts : Should contain the controller interrupt line
+
+Examples:
+
+ethernet@d8004000 {
+       compatible = "via,vt8500-rhine";
+       reg = <0xd8004000 0x100>;
+       interrupts = <10>;
+};
diff --git a/Documentation/devicetree/bindings/panel/auo,b133xtn01.txt b/Documentation/devicetree/bindings/panel/auo,b133xtn01.txt
new file mode 100644 (file)
index 0000000..7443b7c
--- /dev/null
@@ -0,0 +1,7 @@
+AU Optronics Corporation 13.3" WXGA (1366x768) TFT LCD panel
+
+Required properties:
+- compatible: should be "auo,b133xtn01"
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.
diff --git a/Documentation/devicetree/bindings/panel/edt,et057090dhu.txt b/Documentation/devicetree/bindings/panel/edt,et057090dhu.txt
new file mode 100644 (file)
index 0000000..4903d7b
--- /dev/null
@@ -0,0 +1,7 @@
+Emerging Display Technology Corp. 5.7" VGA TFT LCD panel
+
+Required properties:
+- compatible: should be "edt,et057090dhu"
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.
diff --git a/Documentation/devicetree/bindings/panel/edt,et070080dh6.txt b/Documentation/devicetree/bindings/panel/edt,et070080dh6.txt
new file mode 100644 (file)
index 0000000..20cb38e
--- /dev/null
@@ -0,0 +1,10 @@
+Emerging Display Technology Corp. ET070080DH6 7.0" WVGA TFT LCD panel
+
+Required properties:
+- compatible: should be "edt,et070080dh6"
+
+This panel is the same as ETM0700G0DH6 except for the touchscreen.
+ET070080DH6 is the model with resistive touch.
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.
diff --git a/Documentation/devicetree/bindings/panel/edt,etm0700g0dh6.txt b/Documentation/devicetree/bindings/panel/edt,etm0700g0dh6.txt
new file mode 100644 (file)
index 0000000..ee4b180
--- /dev/null
@@ -0,0 +1,10 @@
+Emerging Display Technology Corp. ETM0700G0DH6 7.0" WVGA TFT LCD panel
+
+Required properties:
+- compatible: should be "edt,etm0700g0dh6"
+
+This panel is the same as ET070080DH6 except for the touchscreen.
+ETM0700G0DH6 is the model with capacitive multitouch.
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.
index d6fae13ff0628bef56a69d671a9a684fa7ab2280..d0d15ee42834089abfd2ffad2bad205b28e33d18 100644 (file)
@@ -1,15 +1,7 @@
 * Synopsys Designware PCIe interface
 
 Required properties:
-- compatible: should contain "snps,dw-pcie" to identify the
-       core, plus an identifier for the specific instance, such
-       as "samsung,exynos5440-pcie" or "fsl,imx6q-pcie".
-- reg: base addresses and lengths of the pcie controller,
-       the phy controller, additional register for the phy controller.
-- interrupts: interrupt values for level interrupt,
-       pulse interrupt, special interrupt.
-- clocks: from common clock binding: handle to pci clock.
-- clock-names: from common clock binding: should be "pcie" and "pcie_bus".
+- compatible: should contain "snps,dw-pcie" to identify the core.
 - #address-cells: set to <3>
 - #size-cells: set to <2>
 - device_type: set to "pci"
@@ -19,65 +11,11 @@ Required properties:
        to define the mapping of the PCIe interface to interrupt
        numbers.
 - num-lanes: number of lanes to use
+- clocks: Must contain an entry for each entry in clock-names.
+       See ../clocks/clock-bindings.txt for details.
+- clock-names: Must include the following entries:
+       - "pcie"
+       - "pcie_bus"
 
 Optional properties:
 - reset-gpio: gpio pin number of power good signal
-
-Optional properties for fsl,imx6q-pcie
-- power-on-gpio: gpio pin number of power-enable signal
-- wake-up-gpio: gpio pin number of incoming wakeup signal
-- disable-gpio: gpio pin number of outgoing rfkill/endpoint disable signal
-
-Example:
-
-SoC specific DT Entry:
-
-       pcie@290000 {
-               compatible = "samsung,exynos5440-pcie", "snps,dw-pcie";
-               reg = <0x290000 0x1000
-                       0x270000 0x1000
-                       0x271000 0x40>;
-               interrupts = <0 20 0>, <0 21 0>, <0 22 0>;
-               clocks = <&clock 28>, <&clock 27>;
-               clock-names = "pcie", "pcie_bus";
-               #address-cells = <3>;
-               #size-cells = <2>;
-               device_type = "pci";
-               ranges = <0x00000800 0 0x40000000 0x40000000 0 0x00001000   /* configuration space */
-                         0x81000000 0 0          0x40001000 0 0x00010000   /* downstream I/O */
-                         0x82000000 0 0x40011000 0x40011000 0 0x1ffef000>; /* non-prefetchable memory */
-               #interrupt-cells = <1>;
-               interrupt-map-mask = <0 0 0 0>;
-               interrupt-map = <0x0 0 &gic 53>;
-               num-lanes = <4>;
-       };
-
-       pcie@2a0000 {
-               compatible = "samsung,exynos5440-pcie", "snps,dw-pcie";
-               reg = <0x2a0000 0x1000
-                       0x272000 0x1000
-                       0x271040 0x40>;
-               interrupts = <0 23 0>, <0 24 0>, <0 25 0>;
-               clocks = <&clock 29>, <&clock 27>;
-               clock-names = "pcie", "pcie_bus";
-               #address-cells = <3>;
-               #size-cells = <2>;
-               device_type = "pci";
-               ranges = <0x00000800 0 0x60000000 0x60000000 0 0x00001000   /* configuration space */
-                         0x81000000 0 0          0x60001000 0 0x00010000   /* downstream I/O */
-                         0x82000000 0 0x60011000 0x60011000 0 0x1ffef000>; /* non-prefetchable memory */
-               #interrupt-cells = <1>;
-               interrupt-map-mask = <0 0 0 0>;
-               interrupt-map = <0x0 0 &gic 56>;
-               num-lanes = <4>;
-       };
-
-Board specific DT Entry:
-
-       pcie@290000 {
-               reset-gpio = <&pin_ctrl 5 0>;
-       };
-
-       pcie@2a0000 {
-               reset-gpio = <&pin_ctrl 22 0>;
-       };
diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
new file mode 100644 (file)
index 0000000..9455fd0
--- /dev/null
@@ -0,0 +1,38 @@
+* Freescale i.MX6 PCIe interface
+
+This PCIe host controller is based on the Synopsis Designware PCIe IP
+and thus inherits all the common properties defined in designware-pcie.txt.
+
+Required properties:
+- compatible: "fsl,imx6q-pcie"
+- reg: base addresse and length of the pcie controller
+- interrupts: A list of interrupt outputs of the controller. Must contain an
+  entry for each entry in the interrupt-names property.
+- interrupt-names: Must include the following entries:
+       - "msi": The interrupt that is asserted when an MSI is received
+- clock-names: Must include the following additional entries:
+       - "pcie_phy"
+
+Example:
+
+       pcie@0x01000000 {
+               compatible = "fsl,imx6q-pcie", "snps,dw-pcie";
+               reg = <0x01ffc000 0x4000>;
+               #address-cells = <3>;
+               #size-cells = <2>;
+               device_type = "pci";
+               ranges = <0x00000800 0 0x01f00000 0x01f00000 0 0x00080000
+                         0x81000000 0 0          0x01f80000 0 0x00010000
+                         0x82000000 0 0x01000000 0x01000000 0 0x00f00000>;
+               num-lanes = <1>;
+               interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
+               interrupt-names = "msi";
+               #interrupt-cells = <1>;
+               interrupt-map-mask = <0 0 0 0x7>;
+               interrupt-map = <0 0 0 1 &intc GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>,
+                               <0 0 0 2 &intc GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>,
+                               <0 0 0 3 &intc GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>,
+                               <0 0 0 4 &intc GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
+               clocks = <&clks 144>, <&clks 206>, <&clks 189>;
+               clock-names = "pcie", "pcie_bus", "pcie_phy";
+       };
diff --git a/Documentation/devicetree/bindings/pci/samsung,exynos5440-pcie.txt b/Documentation/devicetree/bindings/pci/samsung,exynos5440-pcie.txt
new file mode 100644 (file)
index 0000000..4f9d23d
--- /dev/null
@@ -0,0 +1,65 @@
+* Samsung Exynos 5440 PCIe interface
+
+This PCIe host controller is based on the Synopsis Designware PCIe IP
+and thus inherits all the common properties defined in designware-pcie.txt.
+
+Required properties:
+- compatible: "samsung,exynos5440-pcie"
+- reg: base addresses and lengths of the pcie controller,
+       the phy controller, additional register for the phy controller.
+- interrupts: A list of interrupt outputs for level interrupt,
+       pulse interrupt, special interrupt.
+
+Example:
+
+SoC specific DT Entry:
+
+       pcie@290000 {
+               compatible = "samsung,exynos5440-pcie", "snps,dw-pcie";
+               reg = <0x290000 0x1000
+                       0x270000 0x1000
+                       0x271000 0x40>;
+               interrupts = <0 20 0>, <0 21 0>, <0 22 0>;
+               clocks = <&clock 28>, <&clock 27>;
+               clock-names = "pcie", "pcie_bus";
+               #address-cells = <3>;
+               #size-cells = <2>;
+               device_type = "pci";
+               ranges = <0x00000800 0 0x40000000 0x40000000 0 0x00001000   /* configuration space */
+                         0x81000000 0 0          0x40001000 0 0x00010000   /* downstream I/O */
+                         0x82000000 0 0x40011000 0x40011000 0 0x1ffef000>; /* non-prefetchable memory */
+               #interrupt-cells = <1>;
+               interrupt-map-mask = <0 0 0 0>;
+               interrupt-map = <0 0 0 0 &gic GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
+               num-lanes = <4>;
+       };
+
+       pcie@2a0000 {
+               compatible = "samsung,exynos5440-pcie", "snps,dw-pcie";
+               reg = <0x2a0000 0x1000
+                       0x272000 0x1000
+                       0x271040 0x40>;
+               interrupts = <0 23 0>, <0 24 0>, <0 25 0>;
+               clocks = <&clock 29>, <&clock 27>;
+               clock-names = "pcie", "pcie_bus";
+               #address-cells = <3>;
+               #size-cells = <2>;
+               device_type = "pci";
+               ranges = <0x00000800 0 0x60000000 0x60000000 0 0x00001000   /* configuration space */
+                         0x81000000 0 0          0x60001000 0 0x00010000   /* downstream I/O */
+                         0x82000000 0 0x60011000 0x60011000 0 0x1ffef000>; /* non-prefetchable memory */
+               #interrupt-cells = <1>;
+               interrupt-map-mask = <0 0 0 0>;
+               interrupt-map = <0 0 0 0 &gic GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
+               num-lanes = <4>;
+       };
+
+Board specific DT Entry:
+
+       pcie@290000 {
+               reset-gpio = <&pin_ctrl 5 0>;
+       };
+
+       pcie@2a0000 {
+               reset-gpio = <&pin_ctrl 22 0>;
+       };
index 57ccdde02c3ab9fb8deb5c60dd72fcf7d3081390..53dbccfa80caf7f0d1650b15c85d12afd2be518f 100644 (file)
@@ -62,6 +62,10 @@ Optional properties for dp-controller:
        -hsync-active-high:
                HSYNC polarity configuration.
                        High if defined, Low if not defined
+       -samsung,hpd-gpio:
+               Hotplug detect GPIO.
+                       Indicates which GPIO should be used for hotplug
+                       detection
 
 Example:
 
index f9187a259259f4a25dc80dfe08e66457228fa1fe..1fd8cf9cbfaca44d2bc023c76261719832071b48 100644 (file)
@@ -5,6 +5,7 @@ Required properties:
        1) "samsung,exynos5-hdmi" <DEPRECATED>
        2) "samsung,exynos4210-hdmi"
        3) "samsung,exynos4212-hdmi"
+       4) "samsung,exynos5420-hdmi"
 - reg: physical base address of the hdmi and length of memory mapped
        region.
 - interrupts: interrupt number to the cpu.
@@ -27,6 +28,7 @@ Required properties:
        "hdmi", "sclk_hdmi", "sclk_pixel", "sclk_hdmiphy" and "mout_hdmi".
 - ddc: phandle to the hdmi ddc node
 - phy: phandle to the hdmi phy node
+- samsung,syscon-phandle: phandle for system controller node for PMU.
 
 Example:
 
@@ -37,4 +39,5 @@ Example:
                hpd-gpio = <&gpx3 7 1>;
                ddc = <&hdmi_ddc_node>;
                phy = <&hdmi_phy_node>;
+               samsung,syscon-phandle = <&pmu_system_controller>;
        };
index 89472558011ef295a98667745aa313cf4e6187f1..1525e30483fda44188d22b22548dd5d3068fe8e6 100644 (file)
@@ -318,3 +318,8 @@ GPIO
   devm_gpiod_get_optional()
   devm_gpiod_get_index_optional()
   devm_gpiod_put()
+
+MDIO
+  devm_mdiobus_alloc()
+  devm_mdiobus_alloc_size()
+  devm_mdiobus_free()
index eba7901342531d2dc089c9a39d990aa924b86526..b18dd17790299de1b01053117a065af1ed7f251f 100644 (file)
@@ -196,8 +196,7 @@ prototypes:
        void (*invalidatepage) (struct page *, unsigned int, unsigned int);
        int (*releasepage) (struct page *, int);
        void (*freepage)(struct page *);
-       int (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
-                       loff_t offset, unsigned long nr_segs);
+       int (*direct_IO)(int, struct kiocb *, struct iov_iter *iter, loff_t offset);
        int (*get_xip_mem)(struct address_space *, pgoff_t, int, void **,
                                unsigned long *);
        int (*migratepage)(struct address_space *, struct page *, struct page *);
@@ -431,6 +430,8 @@ prototypes:
        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
+       ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
+       ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
        int (*iterate) (struct file *, struct dir_context *);
        unsigned int (*poll) (struct file *, struct poll_table_struct *);
        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
index 617f6d70c0778ce37716d25fde6f0c158f492707..a1d0d7a301657d674c653648534ee5be527919b9 100644 (file)
@@ -589,8 +589,7 @@ struct address_space_operations {
        void (*invalidatepage) (struct page *, unsigned int, unsigned int);
        int (*releasepage) (struct page *, int);
        void (*freepage)(struct page *);
-       ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
-                       loff_t offset, unsigned long nr_segs);
+       ssize_t (*direct_IO)(int, struct kiocb *, struct iov_iter *iter, loff_t offset);
        struct page* (*get_xip_page)(struct address_space *, sector_t,
                        int);
        /* migrate the contents of a page to the specified target */
@@ -807,6 +806,8 @@ struct file_operations {
        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
+       ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
+       ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
        int (*iterate) (struct file *, struct dir_context *);
        unsigned int (*poll) (struct file *, struct poll_table_struct *);
        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
@@ -837,11 +838,15 @@ otherwise noted.
 
   read: called by read(2) and related system calls
 
-  aio_read: called by io_submit(2) and other asynchronous I/O operations
+  aio_read: vectored, possibly asynchronous read
+
+  read_iter: possibly asynchronous read with iov_iter as destination
 
   write: called by write(2) and related system calls
 
-  aio_write: called by io_submit(2) and other asynchronous I/O operations
+  aio_write: vectored, possibly asynchronous write
+
+  write_iter: possibly asynchronous write with iov_iter as source
 
   iterate: called when the VFS needs to read the directory contents
 
diff --git a/Documentation/hwmon/shtc1 b/Documentation/hwmon/shtc1
new file mode 100644 (file)
index 0000000..6b1e054
--- /dev/null
@@ -0,0 +1,43 @@
+Kernel driver shtc1
+===================
+
+Supported chips:
+  * Sensirion SHTC1
+    Prefix: 'shtc1'
+    Addresses scanned: none
+    Datasheet: http://www.sensirion.com/file/datasheet_shtc1
+
+  * Sensirion SHTW1
+    Prefix: 'shtw1'
+    Addresses scanned: none
+    Datasheet: Not publicly available
+
+Author:
+  Johannes Winkelmann <johannes.winkelmann@sensirion.com>
+
+Description
+-----------
+
+This driver implements support for the Sensirion SHTC1 chip, a humidity and
+temperature sensor. Temperature is measured in degrees celsius, relative
+humidity is expressed as a percentage. Driver can be used as well for SHTW1
+chip, which has the same electrical interface.
+
+The device communicates with the I2C protocol. All sensors are set to I2C
+address 0x70. See Documentation/i2c/instantiating-devices for methods to
+instantiate the device.
+
+There are two options configurable by means of shtc1_platform_data:
+1. blocking (pull the I2C clock line down while performing the measurement) or
+   non-blocking mode. Blocking mode will guarantee the fastest result but
+   the I2C bus will be busy during that time. By default, non-blocking mode
+   is used. Make sure clock-stretching works properly on your device if you
+   want to use blocking mode.
+2. high or low accuracy. High accuracy is used by default and using it is
+   strongly recommended.
+
+sysfs-Interface
+---------------
+
+temp1_input - temperature input
+humidity1_input - humidity input
index 69372fb98cf89e4971e1520cd335ff66f27f7a6f..3fb39e0116b4c8e42d40009357ed5cf13c1f2888 100644 (file)
@@ -470,7 +470,7 @@ build.
 
        Sometimes, an external module uses exported symbols from
        another external module. kbuild needs to have full knowledge of
-       all symbols to avoid spliitting out warnings about undefined
+       all symbols to avoid spitting out warnings about undefined
        symbols. Three solutions exist for this situation.
 
        NOTE: The method with a top-level kbuild file is recommended
index b9f67781c577d04118de98dee652baa32568618b..6eaa9cdb7094b5785aecc54e55d4cb5bd08a8de9 100644 (file)
@@ -1,27 +1,37 @@
                           Kernel Parameters
                           ~~~~~~~~~~~~~~~~~
 
-The following is a consolidated list of the kernel parameters as implemented
-(mostly) by the __setup() macro and sorted into English Dictionary order
-(defined as ignoring all punctuation and sorting digits before letters in a
-case insensitive manner), and with descriptions where known.
-
-Module parameters for loadable modules are specified only as the
-parameter name with optional '=' and value as appropriate, such as:
-
-       modprobe usbcore blinkenlights=1
-
-Module parameters for modules that are built into the kernel image
-are specified on the kernel command line with the module name plus
-'.' plus parameter name, with '=' and value if appropriate, such as:
-
-       usbcore.blinkenlights=1
+The following is a consolidated list of the kernel parameters as
+implemented by the __setup(), core_param() and module_param() macros
+and sorted into English Dictionary order (defined as ignoring all
+punctuation and sorting digits before letters in a case insensitive
+manner), and with descriptions where known.
+
+The kernel parses parameters from the kernel command line up to "--";
+if it doesn't recognize a parameter and it doesn't contain a '.', the
+parameter gets passed to init: parameters with '=' go into init's
+environment, others are passed as command line arguments to init.
+Everything after "--" is passed as an argument to init.
+
+Module parameters can be specified in two ways: via the kernel command
+line with a module name prefix, or via modprobe, e.g.:
+
+       (kernel command line) usbcore.blinkenlights=1
+       (modprobe command line) modprobe usbcore blinkenlights=1
+
+Parameters for modules which are built into the kernel need to be
+specified on the kernel command line.  modprobe looks through the
+kernel command line (/proc/cmdline) and collects module parameters
+when it loads a module, so the kernel command line can be used for
+loadable modules too.
 
 Hyphens (dashes) and underscores are equivalent in parameter names, so
        log_buf_len=1M print-fatal-signals=1
 can also be entered as
        log-buf-len=1M print_fatal_signals=1
 
+Double-quotes can be used to protect spaces in values, e.g.:
+       param="spaces in here"
 
 This document may not be entirely up to date and comprehensive. The command
 "modinfo -p ${modulename}" shows a current list of all parameters of a loadable
index 0cfb00fd86ffd7834a395df7688879d42132fb0e..4bbeca8483ed339f7efd5b6314da77f9b4a99f0d 100644 (file)
@@ -22,8 +22,9 @@ Appendix B: The kprobes sysctl interface
 
 Kprobes enables you to dynamically break into any kernel routine and
 collect debugging and performance information non-disruptively. You
-can trap at almost any kernel code address, specifying a handler
+can trap at almost any kernel code address(*), specifying a handler
 routine to be invoked when the breakpoint is hit.
+(*: some parts of the kernel code can not be trapped, see 1.5 Blacklist)
 
 There are currently three types of probes: kprobes, jprobes, and
 kretprobes (also called return probes).  A kprobe can be inserted
@@ -273,6 +274,19 @@ using one of the following techniques:
  or
 - Execute 'sysctl -w debug.kprobes_optimization=n'
 
+1.5 Blacklist
+
+Kprobes can probe most of the kernel except itself. This means
+that there are some functions where kprobes cannot probe. Probing
+(trapping) such functions can cause a recursive trap (e.g. double
+fault) or the nested probe handler may never be called.
+Kprobes manages such functions as a blacklist.
+If you want to add a function into the blacklist, you just need
+to (1) include linux/kprobes.h and (2) use NOKPROBE_SYMBOL() macro
+to specify a blacklisted function.
+Kprobes checks the given probe address against the blacklist and
+rejects registering it, if the given address is in the blacklist.
+
 2. Architectures Supported
 
 Kprobes, jprobes, and return probes are implemented on the following
index 1dfe62c3641d5d9087388c0255f2a9775b520faa..ee231ed09ec6fd96bbc75f6b7a3a76272ffd9500 100644 (file)
 Generic Mutex Subsystem
 
 started by Ingo Molnar <mingo@redhat.com>
+updated by Davidlohr Bueso <davidlohr@hp.com>
 
-  "Why on earth do we need a new mutex subsystem, and what's wrong
-   with semaphores?"
+What are mutexes?
+-----------------
 
-firstly, there's nothing wrong with semaphores. But if the simpler
-mutex semantics are sufficient for your code, then there are a couple
-of advantages of mutexes:
+In the Linux kernel, mutexes refer to a particular locking primitive
+that enforces serialization on shared memory systems, and not only to
+the generic term referring to 'mutual exclusion' found in academia
+or similar theoretical text books. Mutexes are sleeping locks which
+behave similarly to binary semaphores, and were introduced in 2006[1]
+as an alternative to these. This new data structure provided a number
+of advantages, including simpler interfaces, and at that time smaller
+code (see Disadvantages).
 
- - 'struct mutex' is smaller on most architectures: E.g. on x86,
-   'struct semaphore' is 20 bytes, 'struct mutex' is 16 bytes.
-   A smaller structure size means less RAM footprint, and better
-   CPU-cache utilization.
+[1] http://lwn.net/Articles/164802/
 
- - tighter code. On x86 i get the following .text sizes when
-   switching all mutex-alike semaphores in the kernel to the mutex
-   subsystem:
+Implementation
+--------------
 
-        text    data     bss     dec     hex filename
-     3280380  868188  396860 4545428  455b94 vmlinux-semaphore
-     3255329  865296  396732 4517357  44eded vmlinux-mutex
+Mutexes are represented by 'struct mutex', defined in include/linux/mutex.h
+and implemented in kernel/locking/mutex.c. These locks use a three
+state atomic counter (->count) to represent the different possible
+transitions that can occur during the lifetime of a lock:
 
-   that's 25051 bytes of code saved, or a 0.76% win - off the hottest
-   codepaths of the kernel. (The .data savings are 2892 bytes, or 0.33%)
-   Smaller code means better icache footprint, which is one of the
-   major optimization goals in the Linux kernel currently.
+         1: unlocked
+         0: locked, no waiters
+   negative: locked, with potential waiters
 
- - the mutex subsystem is slightly faster and has better scalability for
-   contended workloads. On an 8-way x86 system, running a mutex-based
-   kernel and testing creat+unlink+close (of separate, per-task files)
-   in /tmp with 16 parallel tasks, the average number of ops/sec is:
+In its most basic form it also includes a wait-queue and a spinlock
+that serializes access to it. CONFIG_SMP systems can also include
+a pointer to the lock task owner (->owner) as well as a spinner MCS
+lock (->osq), both described below in (ii).
 
-    Semaphores:                        Mutexes:
+When acquiring a mutex, there are three possible paths that can be
+taken, depending on the state of the lock:
 
-    $ ./test-mutex V 16 10             $ ./test-mutex V 16 10
-    8 CPUs, running 16 tasks.          8 CPUs, running 16 tasks.
-    checking VFS performance.          checking VFS performance.
-    avg loops/sec:      34713          avg loops/sec:      84153
-    CPU utilization:    63%            CPU utilization:    22%
+(i) fastpath: tries to atomically acquire the lock by decrementing the
+    counter. If it was already taken by another task it goes to the next
+    possible path. This logic is architecture specific. On x86-64, the
+    locking fastpath is 2 instructions:
 
-   i.e. in this workload, the mutex based kernel was 2.4 times faster
-   than the semaphore based kernel, _and_ it also had 2.8 times less CPU
-   utilization. (In terms of 'ops per CPU cycle', the semaphore kernel
-   performed 551 ops/sec per 1% of CPU time used, while the mutex kernel
-   performed 3825 ops/sec per 1% of CPU time used - it was 6.9 times
-   more efficient.)
-
-   the scalability difference is visible even on a 2-way P4 HT box:
-
-    Semaphores:                        Mutexes:
-
-    $ ./test-mutex V 16 10             $ ./test-mutex V 16 10
-    4 CPUs, running 16 tasks.          8 CPUs, running 16 tasks.
-    checking VFS performance.          checking VFS performance.
-    avg loops/sec:      127659         avg loops/sec:      181082
-    CPU utilization:    100%           CPU utilization:    34%
-
-   (the straight performance advantage of mutexes is 41%, the per-cycle
-    efficiency of mutexes is 4.1 times better.)
-
- - there are no fastpath tradeoffs, the mutex fastpath is just as tight
-   as the semaphore fastpath. On x86, the locking fastpath is 2
-   instructions:
-
-    c0377ccb <mutex_lock>:
-    c0377ccb:       f0 ff 08                lock decl (%eax)
-    c0377cce:       78 0e                   js     c0377cde <.text..lock.mutex>
-    c0377cd0:       c3                      ret
+    0000000000000e10 <mutex_lock>:
+    e21:   f0 ff 0b                lock decl (%rbx)
+    e24:   79 08                   jns    e2e <mutex_lock+0x1e>
 
    the unlocking fastpath is equally tight:
 
-    c0377cd1 <mutex_unlock>:
-    c0377cd1:       f0 ff 00                lock incl (%eax)
-    c0377cd4:       7e 0f                   jle    c0377ce5 <.text..lock.mutex+0x7>
-    c0377cd6:       c3                      ret
-
- - 'struct mutex' semantics are well-defined and are enforced if
-   CONFIG_DEBUG_MUTEXES is turned on. Semaphores on the other hand have
-   virtually no debugging code or instrumentation. The mutex subsystem
-   checks and enforces the following rules:
-
-   * - only one task can hold the mutex at a time
-   * - only the owner can unlock the mutex
-   * - multiple unlocks are not permitted
-   * - recursive locking is not permitted
-   * - a mutex object must be initialized via the API
-   * - a mutex object must not be initialized via memset or copying
-   * - task may not exit with mutex held
-   * - memory areas where held locks reside must not be freed
-   * - held mutexes must not be reinitialized
-   * - mutexes may not be used in hardware or software interrupt
-   *   contexts such as tasklets and timers
-
-   furthermore, there are also convenience features in the debugging
-   code:
-
-   * - uses symbolic names of mutexes, whenever they are printed in debug output
-   * - point-of-acquire tracking, symbolic lookup of function names
-   * - list of all locks held in the system, printout of them
-   * - owner tracking
-   * - detects self-recursing locks and prints out all relevant info
-   * - detects multi-task circular deadlocks and prints out all affected
-   *   locks and tasks (and only those tasks)
+    0000000000000bc0 <mutex_unlock>:
+    bc8:   f0 ff 07                lock incl (%rdi)
+    bcb:   7f 0a                   jg     bd7 <mutex_unlock+0x17>
+
+
+(ii) midpath: aka optimistic spinning, tries to spin for acquisition
+     while the lock owner is running and there are no other tasks ready
+     to run that have higher priority (need_resched). The rationale is
+     that if the lock owner is running, it is likely to release the lock
+     soon. The mutex spinners are queued up using MCS lock so that only
+     one spinner can compete for the mutex.
+
+     The MCS lock (proposed by Mellor-Crummey and Scott) is a simple spinlock
+     with the desirable properties of being fair and with each cpu trying
+     to acquire the lock spinning on a local variable. It avoids expensive
+     cacheline bouncing that common test-and-set spinlock implementations
+     incur. An MCS-like lock is specially tailored for optimistic spinning
+     for sleeping lock implementation. An important feature of the customized
+     MCS lock is that it has the extra property that spinners are able to exit
+     the MCS spinlock queue when they need to reschedule. This further helps
+     avoid situations where MCS spinners that need to reschedule would continue
+     waiting to spin on mutex owner, only to go directly to slowpath upon
+     obtaining the MCS lock.
+
+
+(iii) slowpath: last resort, if the lock is still unable to be acquired,
+      the task is added to the wait-queue and sleeps until woken up by the
+      unlock path. Under normal circumstances it blocks as TASK_UNINTERRUPTIBLE.
+
+While formally kernel mutexes are sleepable locks, it is path (ii) that
+makes them more practically a hybrid type. By simply not interrupting a
+task and busy-waiting for a few cycles instead of immediately sleeping,
+the performance of this lock has been seen to significantly improve a
+number of workloads. Note that this technique is also used for rw-semaphores.
+
+Semantics
+---------
+
+The mutex subsystem checks and enforces the following rules:
+
+    - Only one task can hold the mutex at a time.
+    - Only the owner can unlock the mutex.
+    - Multiple unlocks are not permitted.
+    - Recursive locking/unlocking is not permitted.
+    - A mutex must only be initialized via the API (see below).
+    - A task may not exit with a mutex held.
+    - Memory areas where held locks reside must not be freed.
+    - Held mutexes must not be reinitialized.
+    - Mutexes may not be used in hardware or software interrupt
+      contexts such as tasklets and timers.
+
+These semantics are fully enforced when CONFIG DEBUG_MUTEXES is enabled.
+In addition, the mutex debugging code also implements a number of other
+features that make lock debugging easier and faster:
+
+    - Uses symbolic names of mutexes, whenever they are printed
+      in debug output.
+    - Point-of-acquire tracking, symbolic lookup of function names,
+      list of all locks held in the system, printout of them.
+    - Owner tracking.
+    - Detects self-recursing locks and prints out all relevant info.
+    - Detects multi-task circular deadlocks and prints out all affected
+      locks and tasks (and only those tasks).
+
+
+Interfaces
+----------
+Statically define the mutex:
+   DEFINE_MUTEX(name);
+
+Dynamically initialize the mutex:
+   mutex_init(mutex);
+
+Acquire the mutex, uninterruptible:
+   void mutex_lock(struct mutex *lock);
+   void mutex_lock_nested(struct mutex *lock, unsigned int subclass);
+   int  mutex_trylock(struct mutex *lock);
+
+Acquire the mutex, interruptible:
+   int mutex_lock_interruptible_nested(struct mutex *lock,
+                                      unsigned int subclass);
+   int mutex_lock_interruptible(struct mutex *lock);
+
+Acquire the mutex, interruptible, if dec to 0:
+   int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock);
+
+Unlock the mutex:
+   void mutex_unlock(struct mutex *lock);
+
+Test if the mutex is taken:
+   int mutex_is_locked(struct mutex *lock);
 
 Disadvantages
 -------------
 
-The stricter mutex API means you cannot use mutexes the same way you
-can use semaphores: e.g. they cannot be used from an interrupt context,
-nor can they be unlocked from a different context that which acquired
-it. [ I'm not aware of any other (e.g. performance) disadvantages from
-using mutexes at the moment, please let me know if you find any. ]
-
-Implementation of mutexes
--------------------------
-
-'struct mutex' is the new mutex type, defined in include/linux/mutex.h and
-implemented in kernel/locking/mutex.c. It is a counter-based mutex with a
-spinlock and a wait-list. The counter has 3 states: 1 for "unlocked", 0 for
-"locked" and negative numbers (usually -1) for "locked, potential waiters
-queued".
-
-the APIs of 'struct mutex' have been streamlined:
-
- DEFINE_MUTEX(name);
+Unlike its original design and purpose, 'struct mutex' is larger than
+most locks in the kernel. E.g: on x86-64 it is 40 bytes, almost twice
+as large as 'struct semaphore' (24 bytes) and 8 bytes shy of the
+'struct rw_semaphore' variant. Larger structure sizes mean more CPU
+cache and memory footprint.
 
- mutex_init(mutex);
+When to use mutexes
+-------------------
 
- void mutex_lock(struct mutex *lock);
- int  mutex_lock_interruptible(struct mutex *lock);
- int  mutex_trylock(struct mutex *lock);
- void mutex_unlock(struct mutex *lock);
- int  mutex_is_locked(struct mutex *lock);
- void mutex_lock_nested(struct mutex *lock, unsigned int subclass);
- int  mutex_lock_interruptible_nested(struct mutex *lock,
-                                      unsigned int subclass);
- int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock);
+Unless the strict semantics of mutexes are unsuitable and/or the critical
+region prevents the lock from being shared, always prefer them to any other
+locking primitive.
index a383c00392d03f2e316f7fe7dd954c7d8b71f274..9c723ecd00251534a0b011c4e0ffbd053242454e 100644 (file)
@@ -585,13 +585,19 @@ mode
        balance-tlb or 5
 
                Adaptive transmit load balancing: channel bonding that
-               does not require any special switch support.  The
-               outgoing traffic is distributed according to the
-               current load (computed relative to the speed) on each
-               slave.  Incoming traffic is received by the current
-               slave.  If the receiving slave fails, another slave
-               takes over the MAC address of the failed receiving
-               slave.
+               does not require any special switch support.
+
+               In tlb_dynamic_lb=1 mode; the outgoing traffic is
+               distributed according to the current load (computed
+               relative to the speed) on each slave.
+
+               In tlb_dynamic_lb=0 mode; the load balancing based on
+               current load is disabled and the load is distributed
+               only using the hash distribution.
+
+               Incoming traffic is received by the current slave.
+               If the receiving slave fails, another slave takes over
+               the MAC address of the failed receiving slave.
 
                Prerequisite:
 
@@ -736,6 +742,28 @@ primary_reselect
 
        This option was added for bonding version 3.6.0.
 
+tlb_dynamic_lb
+
+       Specifies if dynamic shuffling of flows is enabled in tlb
+       mode. The value has no effect on any other modes.
+
+       The default behavior of tlb mode is to shuffle active flows across
+       slaves based on the load in that interval. This gives nice lb
+       characteristics but can cause packet reordering. If re-ordering is
+       a concern use this variable to disable flow shuffling and rely on
+       load balancing provided solely by the hash distribution.
+       xmit-hash-policy can be used to select the appropriate hashing for
+       the setup.
+
+       The sysfs entry can be used to change the setting per bond device
+       and the initial value is derived from the module parameter. The
+       sysfs entry is allowed to be changed only if the bond device is
+       down.
+
+       The default value is "1" that enables flow shuffling while value "0"
+       disables it. This option was added in bonding driver 3.7.1
+
+
 updelay
 
        Specifies the time, in milliseconds, to wait before enabling a
@@ -769,7 +797,7 @@ use_carrier
 xmit_hash_policy
 
        Selects the transmit hash policy to use for slave selection in
-       balance-xor and 802.3ad modes.  Possible values are:
+       balance-xor, 802.3ad, and tlb modes.  Possible values are:
 
        layer2
 
index 4f7ae5261364ac4a2c09b0063e900e5c05a0fda3..2236d6dcb7dadb0b77fa889046eeb8c55bc6e06d 100644 (file)
@@ -469,6 +469,41 @@ solution for a couple of reasons:
   having this 'send only' use-case we may remove the receive list in the
   Kernel to save a little (really a very little!) CPU usage.
 
+  4.1.1.1 CAN filter usage optimisation
+
+  The CAN filters are processed in per-device filter lists at CAN frame
+  reception time. To reduce the number of checks that need to be performed
+  while walking through the filter lists the CAN core provides an optimized
+  filter handling when the filter subscription focusses on a single CAN ID.
+
+  For the possible 2048 SFF CAN identifiers the identifier is used as an index
+  to access the corresponding subscription list without any further checks.
+  For the 2^29 possible EFF CAN identifiers a 10 bit XOR folding is used as
+  hash function to retrieve the EFF table index.
+
+  To benefit from the optimized filters for single CAN identifiers the
+  CAN_SFF_MASK or CAN_EFF_MASK have to be set into can_filter.mask together
+  with set CAN_EFF_FLAG and CAN_RTR_FLAG bits. A set CAN_EFF_FLAG bit in the
+  can_filter.mask makes clear that it matters whether a SFF or EFF CAN ID is
+  subscribed. E.g. in the example from above
+
+    rfilter[0].can_id   = 0x123;
+    rfilter[0].can_mask = CAN_SFF_MASK;
+
+  both SFF frames with CAN ID 0x123 and EFF frames with 0xXXXXX123 can pass.
+
+  To filter for only 0x123 (SFF) and 0x12345678 (EFF) CAN identifiers the
+  filter has to be defined in this way to benefit from the optimized filters:
+
+    struct can_filter rfilter[2];
+
+    rfilter[0].can_id   = 0x123;
+    rfilter[0].can_mask = (CAN_EFF_FLAG | CAN_RTR_FLAG | CAN_SFF_MASK);
+    rfilter[1].can_id   = 0x12345678 | CAN_EFF_FLAG;
+    rfilter[1].can_mask = (CAN_EFF_FLAG | CAN_RTR_FLAG | CAN_EFF_MASK);
+
+    setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
+
   4.1.2 RAW socket option CAN_RAW_ERR_FILTER
 
   As described in chapter 3.4 the CAN interface driver can generate so
diff --git a/Documentation/networking/cdc_mbim.txt b/Documentation/networking/cdc_mbim.txt
new file mode 100644 (file)
index 0000000..a15ea60
--- /dev/null
@@ -0,0 +1,339 @@
+     cdc_mbim - Driver for CDC MBIM Mobile Broadband modems
+    ========================================================
+
+The cdc_mbim driver supports USB devices conforming to the "Universal
+Serial Bus Communications Class Subclass Specification for Mobile
+Broadband Interface Model" [1], which is a further development of
+"Universal Serial Bus Communications Class Subclass Specifications for
+Network Control Model Devices" [2] optimized for Mobile Broadband
+devices, aka "3G/LTE modems".
+
+
+Command Line Parameters
+=======================
+
+The cdc_mbim driver has no parameters of its own.  But the probing
+behaviour for NCM 1.0 backwards compatible MBIM functions (an
+"NCM/MBIM function" as defined in section 3.2 of [1]) is affected
+by a cdc_ncm driver parameter:
+
+prefer_mbim
+-----------
+Type:          Boolean
+Valid Range:   N/Y (0-1)
+Default Value: Y (MBIM is preferred)
+
+This parameter sets the system policy for NCM/MBIM functions.  Such
+functions will be handled by either the cdc_ncm driver or the cdc_mbim
+driver depending on the prefer_mbim setting.  Setting prefer_mbim=N
+makes the cdc_mbim driver ignore these functions and lets the cdc_ncm
+driver handle them instead.
+
+The parameter is writable, and can be changed at any time. A manual
+unbind/bind is required to make the change effective for NCM/MBIM
+functions bound to the "wrong" driver
+
+
+Basic usage
+===========
+
+MBIM functions are inactive when unmanaged. The cdc_mbim driver only
+provides an userspace interface to the MBIM control channel, and will
+not participate in the management of the function. This implies that a
+userspace MBIM management application always is required to enable a
+MBIM function.
+
+Such userspace applications includes, but are not limited to:
+ - mbimcli (included with the libmbim [3] library), and
+ - ModemManager [4]
+
+Establishing a MBIM IP session reequires at least these actions by the
+management application:
+ - open the control channel
+ - configure network connection settings
+ - connect to network
+ - configure IP interface
+
+Management application development
+----------------------------------
+The driver <-> userspace interfaces are described below.  The MBIM
+control channel protocol is described in [1].
+
+
+MBIM control channel userspace ABI
+==================================
+
+/dev/cdc-wdmX character device
+------------------------------
+The driver creates a two-way pipe to the MBIM function control channel
+using the cdc-wdm driver as a subdriver.  The userspace end of the
+control channel pipe is a /dev/cdc-wdmX character device.
+
+The cdc_mbim driver does not process or police messages on the control
+channel.  The channel is fully delegated to the userspace management
+application.  It is therefore up to this application to ensure that it
+complies with all the control channel requirements in [1].
+
+The cdc-wdmX device is created as a child of the MBIM control
+interface USB device.  The character device associated with a specific
+MBIM function can be looked up using sysfs.  For example:
+
+ bjorn@nemi:~$ ls /sys/bus/usb/drivers/cdc_mbim/2-4:2.12/usbmisc
+ cdc-wdm0
+
+ bjorn@nemi:~$ grep . /sys/bus/usb/drivers/cdc_mbim/2-4:2.12/usbmisc/cdc-wdm0/dev
+ 180:0
+
+
+USB configuration descriptors
+-----------------------------
+The wMaxControlMessage field of the CDC MBIM functional descriptor
+limits the maximum control message size. The managament application is
+responsible for negotiating a control message size complying with the
+requirements in section 9.3.1 of [1], taking this descriptor field
+into consideration.
+
+The userspace application can access the CDC MBIM functional
+descriptor of a MBIM function using either of the two USB
+configuration descriptor kernel interfaces described in [6] or [7].
+
+See also the ioctl documentation below.
+
+
+Fragmentation
+-------------
+The userspace application is responsible for all control message
+fragmentation and defragmentaion, as described in section 9.5 of [1].
+
+
+/dev/cdc-wdmX write()
+---------------------
+The MBIM control messages from the management application *must not*
+exceed the negotiated control message size.
+
+
+/dev/cdc-wdmX read()
+--------------------
+The management application *must* accept control messages of up the
+negotiated control message size.
+
+
+/dev/cdc-wdmX ioctl()
+--------------------
+IOCTL_WDM_MAX_COMMAND: Get Maximum Command Size
+This ioctl returns the wMaxControlMessage field of the CDC MBIM
+functional descriptor for MBIM devices.  This is intended as a
+convenience, eliminating the need to parse the USB descriptors from
+userspace.
+
+       #include <stdio.h>
+       #include <fcntl.h>
+       #include <sys/ioctl.h>
+       #include <linux/types.h>
+       #include <linux/usb/cdc-wdm.h>
+       int main()
+       {
+               __u16 max;
+               int fd = open("/dev/cdc-wdm0", O_RDWR);
+               if (!ioctl(fd, IOCTL_WDM_MAX_COMMAND, &max))
+                       printf("wMaxControlMessage is %d\n", max);
+       }
+
+
+Custom device services
+----------------------
+The MBIM specification allows vendors to freely define additional
+services.  This is fully supported by the cdc_mbim driver.
+
+Support for new MBIM services, including vendor specified services, is
+implemented entirely in userspace, like the rest of the MBIM control
+protocol
+
+New services should be registered in the MBIM Registry [5].
+
+
+
+MBIM data channel userspace ABI
+===============================
+
+wwanY network device
+--------------------
+The cdc_mbim driver represents the MBIM data channel as a single
+network device of the "wwan" type. This network device is initially
+mapped to MBIM IP session 0.
+
+
+Multiplexed IP sessions (IPS)
+-----------------------------
+MBIM allows multiplexing up to 256 IP sessions over a single USB data
+channel.  The cdc_mbim driver models such IP sessions as 802.1q VLAN
+subdevices of the master wwanY device, mapping MBIM IP session Z to
+VLAN ID Z for all values of Z greater than 0.
+
+The device maximum Z is given in the MBIM_DEVICE_CAPS_INFO structure
+described in section 10.5.1 of [1].
+
+The userspace management application is responsible for adding new
+VLAN links prior to establishing MBIM IP sessions where the SessionId
+is greater than 0. These links can be added by using the normal VLAN
+kernel interfaces, either ioctl or netlink.
+
+For example, adding a link for a MBIM IP session with SessionId 3:
+
+  ip link add link wwan0 name wwan0.3 type vlan id 3
+
+The driver will automatically map the "wwan0.3" network device to MBIM
+IP session 3.
+
+
+Device Service Streams (DSS)
+----------------------------
+MBIM also allows up to 256 non-IP data streams to be multiplexed over
+the same shared USB data channel.  The cdc_mbim driver models these
+sessions as another set of 802.1q VLAN subdevices of the master wwanY
+device, mapping MBIM DSS session A to VLAN ID (256 + A) for all values
+of A.
+
+The device maximum A is given in the MBIM_DEVICE_SERVICES_INFO
+structure described in section 10.5.29 of [1].
+
+The DSS VLAN subdevices are used as a practical interface between the
+shared MBIM data channel and a MBIM DSS aware userspace application.
+It is not intended to be presented as-is to an end user. The
+assumption is that an userspace application initiating a DSS session
+also takes care of the necessary framing of the DSS data, presenting
+the stream to the end user in an appropriate way for the stream type.
+
+The network device ABI requires a dummy ethernet header for every DSS
+data frame being transported.  The contents of this header is
+arbitrary, with the following exceptions:
+ - TX frames using an IP protocol (0x0800 or 0x86dd) will be dropped
+ - RX frames will have the protocol field set to ETH_P_802_3 (but will
+   not be properly formatted 802.3 frames)
+ - RX frames will have the destination address set to the hardware
+   address of the master device
+
+The DSS supporting userspace management application is responsible for
+adding the dummy ethernet header on TX and stripping it on RX.
+
+This is a simple example using tools commonly available, exporting
+DssSessionId 5 as a pty character device pointed to by a /dev/nmea
+symlink:
+
+  ip link add link wwan0 name wwan0.dss5 type vlan id 261
+  ip link set dev wwan0.dss5 up
+  socat INTERFACE:wwan0.dss5,type=2 PTY:,echo=0,link=/dev/nmea
+
+This is only an example, most suitable for testing out a DSS
+service. Userspace applications supporting specific MBIM DSS services
+are expected to use the tools and programming interfaces required by
+that service.
+
+Note that adding VLAN links for DSS sessions is entirely optional.  A
+management application may instead choose to bind a packet socket
+directly to the master network device, using the received VLAN tags to
+map frames to the correct DSS session and adding 18 byte VLAN ethernet
+headers with the appropriate tag on TX.  In this case using a socket
+filter is recommended, matching only the DSS VLAN subset. This avoid
+unnecessary copying of unrelated IP session data to userspace.  For
+example:
+
+  static struct sock_filter dssfilter[] = {
+       /* use special negative offsets to get VLAN tag */
+       BPF_STMT(BPF_LD|BPF_B|BPF_ABS, SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT),
+       BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 1, 0, 6), /* true */
+
+       /* verify DSS VLAN range */
+       BPF_STMT(BPF_LD|BPF_H|BPF_ABS, SKF_AD_OFF + SKF_AD_VLAN_TAG),
+       BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 256, 0, 4),     /* 256 is first DSS VLAN */
+       BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 512, 3, 0),     /* 511 is last DSS VLAN */
+
+       /* verify ethertype */
+        BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 2 * ETH_ALEN),
+        BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ETH_P_802_3, 0, 1),
+
+        BPF_STMT(BPF_RET|BPF_K, (u_int)-1),    /* accept */
+        BPF_STMT(BPF_RET|BPF_K, 0),            /* ignore */
+  };
+
+
+
+Tagged IP session 0 VLAN
+------------------------
+As described above, MBIM IP session 0 is treated as special by the
+driver.  It is initially mapped to untagged frames on the wwanY
+network device.
+
+This mapping implies a few restrictions on multiplexed IPS and DSS
+sessions, which may not always be practical:
+ - no IPS or DSS session can use a frame size greater than the MTU on
+   IP session 0
+ - no IPS or DSS session can be in the up state unless the network
+   device representing IP session 0 also is up
+
+These problems can be avoided by optionally making the driver map IP
+session 0 to a VLAN subdevice, similar to all other IP sessions.  This
+behaviour is triggered by adding a VLAN link for the magic VLAN ID
+4094.  The driver will then immediately start mapping MBIM IP session
+0 to this VLAN, and will drop untagged frames on the master wwanY
+device.
+
+Tip: It might be less confusing to the end user to name this VLAN
+subdevice after the MBIM SessionID instead of the VLAN ID.  For
+example:
+
+  ip link add link wwan0 name wwan0.0 type vlan id 4094
+
+
+VLAN mapping
+------------
+
+Summarizing the cdc_mbim driver mapping described above, we have this
+relationship between VLAN tags on the wwanY network device and MBIM
+sessions on the shared USB data channel:
+
+  VLAN ID       MBIM type   MBIM SessionID           Notes
+  ---------------------------------------------------------
+  untagged      IPS         0                        a)
+  1 - 255       IPS         1 - 255 <VLANID>
+  256 - 511     DSS         0 - 255 <VLANID - 256>
+  512 - 4093                                         b)
+  4094          IPS         0                        c)
+
+    a) if no VLAN ID 4094 link exists, else dropped
+    b) unsupported VLAN range, unconditionally dropped
+    c) if a VLAN ID 4094 link exists, else dropped
+
+
+
+
+References
+==========
+
+[1] USB Implementers Forum, Inc. - "Universal Serial Bus
+      Communications Class Subclass Specification for Mobile Broadband
+      Interface Model", Revision 1.0 (Errata 1), May 1, 2013
+      - http://www.usb.org/developers/docs/devclass_docs/
+
+[2] USB Implementers Forum, Inc. - "Universal Serial Bus
+      Communications Class Subclass Specifications for Network Control
+      Model Devices", Revision 1.0 (Errata 1), November 24, 2010
+      - http://www.usb.org/developers/docs/devclass_docs/
+
+[3] libmbim - "a glib-based library for talking to WWAN modems and
+      devices which speak the Mobile Interface Broadband Model (MBIM)
+      protocol"
+      - http://www.freedesktop.org/wiki/Software/libmbim/
+
+[4] ModemManager - "a DBus-activated daemon which controls mobile
+      broadband (2G/3G/4G) devices and connections"
+      - http://www.freedesktop.org/wiki/Software/ModemManager/
+
+[5] "MBIM (Mobile Broadband Interface Model) Registry"
+       - http://compliance.usb.org/mbim/
+
+[6] "/proc/bus/usb filesystem output"
+       - Documentation/usb/proc_usb_info.txt
+
+[7] "/sys/bus/usb/devices/.../descriptors"
+       - Documentation/ABI/stable/sysfs-bus-usb
index e3ba753cb714949c4e26f898da31b6d0e6b03737..ee78eba78a9dd2ba5a25ace8f597c8c8f53c9de5 100644 (file)
@@ -281,6 +281,7 @@ Possible BPF extensions are shown in the following table:
   cpu                                   raw_smp_processor_id()
   vlan_tci                              vlan_tx_tag_get(skb)
   vlan_pr                               vlan_tx_tag_present(skb)
+  rand                                  prandom_u32()
 
 These extensions can also be prefixed with '#'.
 Examples for low-level BPF:
@@ -308,6 +309,18 @@ Examples for low-level BPF:
   ret #-1
   drop: ret #0
 
+** icmp random packet sampling, 1 in 4
+  ldh [12]
+  jne #0x800, drop
+  ldb [23]
+  jneq #1, drop
+  # get a random uint32 number
+  ld rand
+  mod #4
+  jneq #1, drop
+  ret #-1
+  drop: ret #0
+
 ** SECCOMP filter example:
 
   ld [4]                  /* offsetof(struct seccomp_data, arch) */
@@ -548,42 +561,43 @@ toolchain for developing and testing the kernel's JIT compiler.
 
 BPF kernel internals
 --------------------
-Internally, for the kernel interpreter, a different BPF instruction set
+Internally, for the kernel interpreter, a different instruction set
 format with similar underlying principles from BPF described in previous
 paragraphs is being used. However, the instruction set format is modelled
 closer to the underlying architecture to mimic native instruction sets, so
-that a better performance can be achieved (more details later).
+that a better performance can be achieved (more details later). This new
+ISA is called 'eBPF' or 'internal BPF' interchangeably. (Note: eBPF which
+originates from [e]xtended BPF is not the same as BPF extensions! While
+eBPF is an ISA, BPF extensions date back to classic BPF's 'overloading'
+of BPF_LD | BPF_{B,H,W} | BPF_ABS instruction.)
 
 It is designed to be JITed with one to one mapping, which can also open up
-the possibility for GCC/LLVM compilers to generate optimized BPF code through
-a BPF backend that performs almost as fast as natively compiled code.
+the possibility for GCC/LLVM compilers to generate optimized eBPF code through
+an eBPF backend that performs almost as fast as natively compiled code.
 
 The new instruction set was originally designed with the possible goal in
-mind to write programs in "restricted C" and compile into BPF with a optional
+mind to write programs in "restricted C" and compile into eBPF with a optional
 GCC/LLVM backend, so that it can just-in-time map to modern 64-bit CPUs with
-minimal performance overhead over two steps, that is, C -> BPF -> native code.
+minimal performance overhead over two steps, that is, C -> eBPF -> native code.
 
 Currently, the new format is being used for running user BPF programs, which
 includes seccomp BPF, classic socket filters, cls_bpf traffic classifier,
 team driver's classifier for its load-balancing mode, netfilter's xt_bpf
 extension, PTP dissector/classifier, and much more. They are all internally
 converted by the kernel into the new instruction set representation and run
-in the extended interpreter. For in-kernel handlers, this all works
-transparently by using sk_unattached_filter_create() for setting up the
-filter, resp. sk_unattached_filter_destroy() for destroying it. The macro
-SK_RUN_FILTER(filter, ctx) transparently invokes the right BPF function to
-run the filter. 'filter' is a pointer to struct sk_filter that we got from
-sk_unattached_filter_create(), and 'ctx' the given context (e.g. skb pointer).
-All constraints and restrictions from sk_chk_filter() apply before a
-conversion to the new layout is being done behind the scenes!
-
-Currently, for JITing, the user BPF format is being used and current BPF JIT
-compilers reused whenever possible. In other words, we do not (yet!) perform
-a JIT compilation in the new layout, however, future work will successively
-migrate traditional JIT compilers into the new instruction format as well, so
-that they will profit from the very same benefits. Thus, when speaking about
-JIT in the following, a JIT compiler (TBD) for the new instruction format is
-meant in this context.
+in the eBPF interpreter. For in-kernel handlers, this all works transparently
+by using sk_unattached_filter_create() for setting up the filter, resp.
+sk_unattached_filter_destroy() for destroying it. The macro
+SK_RUN_FILTER(filter, ctx) transparently invokes eBPF interpreter or JITed
+code to run the filter. 'filter' is a pointer to struct sk_filter that we
+got from sk_unattached_filter_create(), and 'ctx' the given context (e.g.
+skb pointer). All constraints and restrictions from sk_chk_filter() apply
+before a conversion to the new layout is being done behind the scenes!
+
+Currently, the classic BPF format is being used for JITing on most of the
+architectures. Only x86-64 performs JIT compilation from eBPF instruction set,
+however, future work will migrate other JIT compilers as well, so that they
+will profit from the very same benefits.
 
 Some core changes of the new internal format:
 
@@ -592,35 +606,35 @@ Some core changes of the new internal format:
   The old format had two registers A and X, and a hidden frame pointer. The
   new layout extends this to be 10 internal registers and a read-only frame
   pointer. Since 64-bit CPUs are passing arguments to functions via registers
-  the number of args from BPF program to in-kernel function is restricted
+  the number of args from eBPF program to in-kernel function is restricted
   to 5 and one register is used to accept return value from an in-kernel
   function. Natively, x86_64 passes first 6 arguments in registers, aarch64/
   sparcv9/mips64 have 7 - 8 registers for arguments; x86_64 has 6 callee saved
   registers, and aarch64/sparcv9/mips64 have 11 or more callee saved registers.
 
-  Therefore, BPF calling convention is defined as:
+  Therefore, eBPF calling convention is defined as:
 
-    * R0       - return value from in-kernel function
-    * R1 - R5  - arguments from BPF program to in-kernel function
+    * R0       - return value from in-kernel function, and exit value for eBPF program
+    * R1 - R5  - arguments from eBPF program to in-kernel function
     * R6 - R9  - callee saved registers that in-kernel function will preserve
     * R10      - read-only frame pointer to access stack
 
-  Thus, all BPF registers map one to one to HW registers on x86_64, aarch64,
-  etc, and BPF calling convention maps directly to ABIs used by the kernel on
+  Thus, all eBPF registers map one to one to HW registers on x86_64, aarch64,
+  etc, and eBPF calling convention maps directly to ABIs used by the kernel on
   64-bit architectures.
 
   On 32-bit architectures JIT may map programs that use only 32-bit arithmetic
   and may let more complex programs to be interpreted.
 
-  R0 - R5 are scratch registers and BPF program needs spill/fill them if
-  necessary across calls. Note that there is only one BPF program (== one BPF
-  main routine) and it cannot call other BPF functions, it can only call
-  predefined in-kernel functions, though.
+  R0 - R5 are scratch registers and eBPF program needs spill/fill them if
+  necessary across calls. Note that there is only one eBPF program (== one
+  eBPF main routine) and it cannot call other eBPF functions, it can only
+  call predefined in-kernel functions, though.
 
 - Register width increases from 32-bit to 64-bit:
 
   Still, the semantics of the original 32-bit ALU operations are preserved
-  via 32-bit subregisters. All BPF registers are 64-bit with 32-bit lower
+  via 32-bit subregisters. All eBPF registers are 64-bit with 32-bit lower
   subregisters that zero-extend into 64-bit if they are being written to.
   That behavior maps directly to x86_64 and arm64 subregister definition, but
   makes other JITs more difficult.
@@ -631,8 +645,8 @@ Some core changes of the new internal format:
 
   Operation is 64-bit, because on 64-bit architectures, pointers are also
   64-bit wide, and we want to pass 64-bit values in/out of kernel functions,
-  so 32-bit BPF registers would otherwise require to define register-pair
-  ABI, thus, there won't be able to use a direct BPF register to HW register
+  so 32-bit eBPF registers would otherwise require to define register-pair
+  ABI, thus, there won't be able to use a direct eBPF register to HW register
   mapping and JIT would need to do combine/split/move operations for every
   register in and out of the function, which is complex, bug prone and slow.
   Another reason is the use of atomic 64-bit counters.
@@ -646,14 +660,145 @@ Some core changes of the new internal format:
 - Introduces bpf_call insn and register passing convention for zero overhead
   calls from/to other kernel functions:
 
-  After a kernel function call, R1 - R5 are reset to unreadable and R0 has a
-  return type of the function. Since R6 - R9 are callee saved, their state is
-  preserved across the call.
-
-Also in the new design, BPF is limited to 4096 insns, which means that any
+  Before an in-kernel function call, the internal BPF program needs to
+  place function arguments into R1 to R5 registers to satisfy calling
+  convention, then the interpreter will take them from registers and pass
+  to in-kernel function. If R1 - R5 registers are mapped to CPU registers
+  that are used for argument passing on given architecture, the JIT compiler
+  doesn't need to emit extra moves. Function arguments will be in the correct
+  registers and BPF_CALL instruction will be JITed as single 'call' HW
+  instruction. This calling convention was picked to cover common call
+  situations without performance penalty.
+
+  After an in-kernel function call, R1 - R5 are reset to unreadable and R0 has
+  a return value of the function. Since R6 - R9 are callee saved, their state
+  is preserved across the call.
+
+  For example, consider three C functions:
+
+  u64 f1() { return (*_f2)(1); }
+  u64 f2(u64 a) { return f3(a + 1, a); }
+  u64 f3(u64 a, u64 b) { return a - b; }
+
+  GCC can compile f1, f3 into x86_64:
+
+  f1:
+    movl $1, %edi
+    movq _f2(%rip), %rax
+    jmp  *%rax
+  f3:
+    movq %rdi, %rax
+    subq %rsi, %rax
+    ret
+
+  Function f2 in eBPF may look like:
+
+  f2:
+    bpf_mov R2, R1
+    bpf_add R1, 1
+    bpf_call f3
+    bpf_exit
+
+  If f2 is JITed and the pointer stored to '_f2'. The calls f1 -> f2 -> f3 and
+  returns will be seamless. Without JIT, __sk_run_filter() interpreter needs to
+  be used to call into f2.
+
+  For practical reasons all eBPF programs have only one argument 'ctx' which is
+  already placed into R1 (e.g. on __sk_run_filter() startup) and the programs
+  can call kernel functions with up to 5 arguments. Calls with 6 or more arguments
+  are currently not supported, but these restrictions can be lifted if necessary
+  in the future.
+
+  On 64-bit architectures all register map to HW registers one to one. For
+  example, x86_64 JIT compiler can map them as ...
+
+    R0 - rax
+    R1 - rdi
+    R2 - rsi
+    R3 - rdx
+    R4 - rcx
+    R5 - r8
+    R6 - rbx
+    R7 - r13
+    R8 - r14
+    R9 - r15
+    R10 - rbp
+
+  ... since x86_64 ABI mandates rdi, rsi, rdx, rcx, r8, r9 for argument passing
+  and rbx, r12 - r15 are callee saved.
+
+  Then the following internal BPF pseudo-program:
+
+    bpf_mov R6, R1 /* save ctx */
+    bpf_mov R2, 2
+    bpf_mov R3, 3
+    bpf_mov R4, 4
+    bpf_mov R5, 5
+    bpf_call foo
+    bpf_mov R7, R0 /* save foo() return value */
+    bpf_mov R1, R6 /* restore ctx for next call */
+    bpf_mov R2, 6
+    bpf_mov R3, 7
+    bpf_mov R4, 8
+    bpf_mov R5, 9
+    bpf_call bar
+    bpf_add R0, R7
+    bpf_exit
+
+  After JIT to x86_64 may look like:
+
+    push %rbp
+    mov %rsp,%rbp
+    sub $0x228,%rsp
+    mov %rbx,-0x228(%rbp)
+    mov %r13,-0x220(%rbp)
+    mov %rdi,%rbx
+    mov $0x2,%esi
+    mov $0x3,%edx
+    mov $0x4,%ecx
+    mov $0x5,%r8d
+    callq foo
+    mov %rax,%r13
+    mov %rbx,%rdi
+    mov $0x2,%esi
+    mov $0x3,%edx
+    mov $0x4,%ecx
+    mov $0x5,%r8d
+    callq bar
+    add %r13,%rax
+    mov -0x228(%rbp),%rbx
+    mov -0x220(%rbp),%r13
+    leaveq
+    retq
+
+  Which is in this example equivalent in C to:
+
+    u64 bpf_filter(u64 ctx)
+    {
+        return foo(ctx, 2, 3, 4, 5) + bar(ctx, 6, 7, 8, 9);
+    }
+
+  In-kernel functions foo() and bar() with prototype: u64 (*)(u64 arg1, u64
+  arg2, u64 arg3, u64 arg4, u64 arg5); will receive arguments in proper
+  registers and place their return value into '%rax' which is R0 in eBPF.
+  Prologue and epilogue are emitted by JIT and are implicit in the
+  interpreter. R0-R5 are scratch registers, so eBPF program needs to preserve
+  them across the calls as defined by calling convention.
+
+  For example the following program is invalid:
+
+    bpf_mov R1, 1
+    bpf_call foo
+    bpf_mov R0, R1
+    bpf_exit
+
+  After the call the registers R1-R5 contain junk values and cannot be read.
+  In the future an eBPF verifier can be used to validate internal BPF programs.
+
+Also in the new design, eBPF is limited to 4096 insns, which means that any
 program will terminate quickly and will only call a fixed number of kernel
 functions. Original BPF and the new format are two operand instructions,
-which helps to do one-to-one mapping between BPF insn and x86 insn during JIT.
+which helps to do one-to-one mapping between eBPF insn and x86 insn during JIT.
 
 The input context pointer for invoking the interpreter function is generic,
 its content is defined by a specific use case. For seccomp register R1 points
@@ -661,7 +806,26 @@ to seccomp_data, for converted BPF filters R1 points to a skb.
 
 A program, that is translated internally consists of the following elements:
 
-  op:16, jt:8, jf:8, k:32    ==>    op:8, a_reg:4, x_reg:4, off:16, imm:32
+  op:16, jt:8, jf:8, k:32    ==>    op:8, dst_reg:4, src_reg:4, off:16, imm:32
+
+So far 87 internal BPF instructions were implemented. 8-bit 'op' opcode field
+has room for new instructions. Some of them may use 16/24/32 byte encoding. New
+instructions must be multiple of 8 bytes to preserve backward compatibility.
+
+Internal BPF is a general purpose RISC instruction set. Not every register and
+every instruction are used during translation from original BPF to new format.
+For example, socket filters are not using 'exclusive add' instruction, but
+tracing filters may do to maintain counters of events, for example. Register R9
+is not used by socket filters either, but more complex filters may be running
+out of registers and would have to resort to spill/fill to stack.
+
+Internal BPF can used as generic assembler for last step performance
+optimizations, socket filters and seccomp are using it as assembler. Tracing
+filters may use it as assembler to generate code from kernel. In kernel usage
+may not be bounded by security considerations, since generated internal BPF code
+may be optimizing internal code path and not being exposed to the user space.
+Safety of internal BPF can come from a verifier (TBD). In such use cases as
+described, it may be used as safe instruction set.
 
 Just like the original BPF, the new format runs within a controlled environment,
 is deterministic and the kernel can easily prove that. The safety of the program
@@ -670,6 +834,181 @@ loops and other CFG validation; second step starts from the first insn and
 descends all possible paths. It simulates execution of every insn and observes
 the state change of registers and stack.
 
+eBPF opcode encoding
+--------------------
+
+eBPF is reusing most of the opcode encoding from classic to simplify conversion
+of classic BPF to eBPF. For arithmetic and jump instructions the 8-bit 'code'
+field is divided into three parts:
+
+  +----------------+--------+--------------------+
+  |   4 bits       |  1 bit |   3 bits           |
+  | operation code | source | instruction class  |
+  +----------------+--------+--------------------+
+  (MSB)                                      (LSB)
+
+Three LSB bits store instruction class which is one of:
+
+  Classic BPF classes:    eBPF classes:
+
+  BPF_LD    0x00          BPF_LD    0x00
+  BPF_LDX   0x01          BPF_LDX   0x01
+  BPF_ST    0x02          BPF_ST    0x02
+  BPF_STX   0x03          BPF_STX   0x03
+  BPF_ALU   0x04          BPF_ALU   0x04
+  BPF_JMP   0x05          BPF_JMP   0x05
+  BPF_RET   0x06          [ class 6 unused, for future if needed ]
+  BPF_MISC  0x07          BPF_ALU64 0x07
+
+When BPF_CLASS(code) == BPF_ALU or BPF_JMP, 4th bit encodes source operand ...
+
+  BPF_K     0x00
+  BPF_X     0x08
+
+ * in classic BPF, this means:
+
+  BPF_SRC(code) == BPF_X - use register X as source operand
+  BPF_SRC(code) == BPF_K - use 32-bit immediate as source operand
+
+ * in eBPF, this means:
+
+  BPF_SRC(code) == BPF_X - use 'src_reg' register as source operand
+  BPF_SRC(code) == BPF_K - use 32-bit immediate as source operand
+
+... and four MSB bits store operation code.
+
+If BPF_CLASS(code) == BPF_ALU or BPF_ALU64 [ in eBPF ], BPF_OP(code) is one of:
+
+  BPF_ADD   0x00
+  BPF_SUB   0x10
+  BPF_MUL   0x20
+  BPF_DIV   0x30
+  BPF_OR    0x40
+  BPF_AND   0x50
+  BPF_LSH   0x60
+  BPF_RSH   0x70
+  BPF_NEG   0x80
+  BPF_MOD   0x90
+  BPF_XOR   0xa0
+  BPF_MOV   0xb0  /* eBPF only: mov reg to reg */
+  BPF_ARSH  0xc0  /* eBPF only: sign extending shift right */
+  BPF_END   0xd0  /* eBPF only: endianness conversion */
+
+If BPF_CLASS(code) == BPF_JMP, BPF_OP(code) is one of:
+
+  BPF_JA    0x00
+  BPF_JEQ   0x10
+  BPF_JGT   0x20
+  BPF_JGE   0x30
+  BPF_JSET  0x40
+  BPF_JNE   0x50  /* eBPF only: jump != */
+  BPF_JSGT  0x60  /* eBPF only: signed '>' */
+  BPF_JSGE  0x70  /* eBPF only: signed '>=' */
+  BPF_CALL  0x80  /* eBPF only: function call */
+  BPF_EXIT  0x90  /* eBPF only: function return */
+
+So BPF_ADD | BPF_X | BPF_ALU means 32-bit addition in both classic BPF
+and eBPF. There are only two registers in classic BPF, so it means A += X.
+In eBPF it means dst_reg = (u32) dst_reg + (u32) src_reg; similarly,
+BPF_XOR | BPF_K | BPF_ALU means A ^= imm32 in classic BPF and analogous
+src_reg = (u32) src_reg ^ (u32) imm32 in eBPF.
+
+Classic BPF is using BPF_MISC class to represent A = X and X = A moves.
+eBPF is using BPF_MOV | BPF_X | BPF_ALU code instead. Since there are no
+BPF_MISC operations in eBPF, the class 7 is used as BPF_ALU64 to mean
+exactly the same operations as BPF_ALU, but with 64-bit wide operands
+instead. So BPF_ADD | BPF_X | BPF_ALU64 means 64-bit addition, i.e.:
+dst_reg = dst_reg + src_reg
+
+Classic BPF wastes the whole BPF_RET class to represent a single 'ret'
+operation. Classic BPF_RET | BPF_K means copy imm32 into return register
+and perform function exit. eBPF is modeled to match CPU, so BPF_JMP | BPF_EXIT
+in eBPF means function exit only. The eBPF program needs to store return
+value into register R0 before doing a BPF_EXIT. Class 6 in eBPF is currently
+unused and reserved for future use.
+
+For load and store instructions the 8-bit 'code' field is divided as:
+
+  +--------+--------+-------------------+
+  | 3 bits | 2 bits |   3 bits          |
+  |  mode  |  size  | instruction class |
+  +--------+--------+-------------------+
+  (MSB)                             (LSB)
+
+Size modifier is one of ...
+
+  BPF_W   0x00    /* word */
+  BPF_H   0x08    /* half word */
+  BPF_B   0x10    /* byte */
+  BPF_DW  0x18    /* eBPF only, double word */
+
+... which encodes size of load/store operation:
+
+ B  - 1 byte
+ H  - 2 byte
+ W  - 4 byte
+ DW - 8 byte (eBPF only)
+
+Mode modifier is one of:
+
+  BPF_IMM  0x00  /* classic BPF only, reserved in eBPF */
+  BPF_ABS  0x20
+  BPF_IND  0x40
+  BPF_MEM  0x60
+  BPF_LEN  0x80  /* classic BPF only, reserved in eBPF */
+  BPF_MSH  0xa0  /* classic BPF only, reserved in eBPF */
+  BPF_XADD 0xc0  /* eBPF only, exclusive add */
+
+eBPF has two non-generic instructions: (BPF_ABS | <size> | BPF_LD) and
+(BPF_IND | <size> | BPF_LD) which are used to access packet data.
+
+They had to be carried over from classic to have strong performance of
+socket filters running in eBPF interpreter. These instructions can only
+be used when interpreter context is a pointer to 'struct sk_buff' and
+have seven implicit operands. Register R6 is an implicit input that must
+contain pointer to sk_buff. Register R0 is an implicit output which contains
+the data fetched from the packet. Registers R1-R5 are scratch registers
+and must not be used to store the data across BPF_ABS | BPF_LD or
+BPF_IND | BPF_LD instructions.
+
+These instructions have implicit program exit condition as well. When
+eBPF program is trying to access the data beyond the packet boundary,
+the interpreter will abort the execution of the program. JIT compilers
+therefore must preserve this property. src_reg and imm32 fields are
+explicit inputs to these instructions.
+
+For example:
+
+  BPF_IND | BPF_W | BPF_LD means:
+
+    R0 = ntohl(*(u32 *) (((struct sk_buff *) R6)->data + src_reg + imm32))
+    and R1 - R5 were scratched.
+
+Unlike classic BPF instruction set, eBPF has generic load/store operations:
+
+BPF_MEM | <size> | BPF_STX:  *(size *) (dst_reg + off) = src_reg
+BPF_MEM | <size> | BPF_ST:   *(size *) (dst_reg + off) = imm32
+BPF_MEM | <size> | BPF_LDX:  dst_reg = *(size *) (src_reg + off)
+BPF_XADD | BPF_W  | BPF_STX: lock xadd *(u32 *)(dst_reg + off16) += src_reg
+BPF_XADD | BPF_DW | BPF_STX: lock xadd *(u64 *)(dst_reg + off16) += src_reg
+
+Where size is one of: BPF_B or BPF_H or BPF_W or BPF_DW. Note that 1 and
+2 byte atomic increments are not supported.
+
+Testing
+-------
+
+Next to the BPF toolchain, the kernel also ships a test module that contains
+various test cases for classic and internal BPF that can be executed against
+the BPF interpreter and JIT compiler. It can be found in lib/test_bpf.c and
+enabled via Kconfig:
+
+  CONFIG_TEST_BPF=m
+
+After the module has been built and installed, the test suite can be executed
+via insmod or modprobe against 'test_bpf' module. Results of the test cases
+including timings in nsec can be found in the kernel log (dmesg).
+
 Misc
 ----
 
index e13dafc8e8f15c681e485e1fe79423be0904ff2c..2850df3bf957b2460fbd22bb74af20161b613367 100644 (file)
@@ -1,6 +1,6 @@
 Interaction of Suspend code (S3) with the CPU hotplug infrastructure
 
-     (C) 2011 Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com>
+     (C) 2011 - 2014 Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com>
 
 
 I. How does the regular CPU hotplug code differ from how the Suspend-to-RAM
index 85870208edcfd42be8e583459ba17046266ed441..1dbb4b87268facd59768d54746dded852fbfb131 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * parse_vdso.c: Linux reference vDSO parser
- * Written by Andrew Lutomirski, 2011.
+ * Written by Andrew Lutomirski, 2011-2014.
  *
  * This code is meant to be linked in to various programs that run on Linux.
  * As such, it is available with as few restrictions as possible.  This file
  * it starts a program.  It works equally well in statically and dynamically
  * linked binaries.
  *
- * This code is tested on x86_64.  In principle it should work on any 64-bit
+ * This code is tested on x86.  In principle it should work on any
  * architecture that has a vDSO.
  */
 
 #include <stdbool.h>
 #include <stdint.h>
 #include <string.h>
+#include <limits.h>
 #include <elf.h>
 
 /*
@@ -45,11 +46,18 @@ extern void *vdso_sym(const char *version, const char *name);
 
 
 /* And here's the code. */
-
-#ifndef __x86_64__
-# error Not yet ported to non-x86_64 architectures
+#ifndef ELF_BITS
+# if ULONG_MAX > 0xffffffffUL
+#  define ELF_BITS 64
+# else
+#  define ELF_BITS 32
+# endif
 #endif
 
+#define ELF_BITS_XFORM2(bits, x) Elf##bits##_##x
+#define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x)
+#define ELF(x) ELF_BITS_XFORM(ELF_BITS, x)
+
 static struct vdso_info
 {
        bool valid;
@@ -59,14 +67,14 @@ static struct vdso_info
        uintptr_t load_offset;  /* load_addr - recorded vaddr */
 
        /* Symbol table */
-       Elf64_Sym *symtab;
+       ELF(Sym) *symtab;
        const char *symstrings;
-       Elf64_Word *bucket, *chain;
-       Elf64_Word nbucket, nchain;
+       ELF(Word) *bucket, *chain;
+       ELF(Word) nbucket, nchain;
 
        /* Version table */
-       Elf64_Versym *versym;
-       Elf64_Verdef *verdef;
+       ELF(Versym) *versym;
+       ELF(Verdef) *verdef;
 } vdso_info;
 
 /* Straight from the ELF specification. */
@@ -92,9 +100,14 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)
 
        vdso_info.load_addr = base;
 
-       Elf64_Ehdr *hdr = (Elf64_Ehdr*)base;
-       Elf64_Phdr *pt = (Elf64_Phdr*)(vdso_info.load_addr + hdr->e_phoff);
-       Elf64_Dyn *dyn = 0;
+       ELF(Ehdr) *hdr = (ELF(Ehdr)*)base;
+       if (hdr->e_ident[EI_CLASS] !=
+           (ELF_BITS == 32 ? ELFCLASS32 : ELFCLASS64)) {
+               return;  /* Wrong ELF class -- check ELF_BITS */
+       }
+
+       ELF(Phdr) *pt = (ELF(Phdr)*)(vdso_info.load_addr + hdr->e_phoff);
+       ELF(Dyn) *dyn = 0;
 
        /*
         * We need two things from the segment table: the load offset
@@ -108,7 +121,7 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)
                                + (uintptr_t)pt[i].p_offset
                                - (uintptr_t)pt[i].p_vaddr;
                } else if (pt[i].p_type == PT_DYNAMIC) {
-                       dyn = (Elf64_Dyn*)(base + pt[i].p_offset);
+                       dyn = (ELF(Dyn)*)(base + pt[i].p_offset);
                }
        }
 
@@ -118,7 +131,7 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)
        /*
         * Fish out the useful bits of the dynamic table.
         */
-       Elf64_Word *hash = 0;
+       ELF(Word) *hash = 0;
        vdso_info.symstrings = 0;
        vdso_info.symtab = 0;
        vdso_info.versym = 0;
@@ -131,22 +144,22 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)
                                 + vdso_info.load_offset);
                        break;
                case DT_SYMTAB:
-                       vdso_info.symtab = (Elf64_Sym *)
+                       vdso_info.symtab = (ELF(Sym) *)
                                ((uintptr_t)dyn[i].d_un.d_ptr
                                 + vdso_info.load_offset);
                        break;
                case DT_HASH:
-                       hash = (Elf64_Word *)
+                       hash = (ELF(Word) *)
                                ((uintptr_t)dyn[i].d_un.d_ptr
                                 + vdso_info.load_offset);
                        break;
                case DT_VERSYM:
-                       vdso_info.versym = (Elf64_Versym *)
+                       vdso_info.versym = (ELF(Versym) *)
                                ((uintptr_t)dyn[i].d_un.d_ptr
                                 + vdso_info.load_offset);
                        break;
                case DT_VERDEF:
-                       vdso_info.verdef = (Elf64_Verdef *)
+                       vdso_info.verdef = (ELF(Verdef) *)
                                ((uintptr_t)dyn[i].d_un.d_ptr
                                 + vdso_info.load_offset);
                        break;
@@ -168,8 +181,8 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)
        vdso_info.valid = true;
 }
 
-static bool vdso_match_version(Elf64_Versym ver,
-                              const char *name, Elf64_Word hash)
+static bool vdso_match_version(ELF(Versym) ver,
+                              const char *name, ELF(Word) hash)
 {
        /*
         * This is a helper function to check if the version indexed by
@@ -188,7 +201,7 @@ static bool vdso_match_version(Elf64_Versym ver,
 
        /* First step: find the version definition */
        ver &= 0x7fff;  /* Apparently bit 15 means "hidden" */
-       Elf64_Verdef *def = vdso_info.verdef;
+       ELF(Verdef) *def = vdso_info.verdef;
        while(true) {
                if ((def->vd_flags & VER_FLG_BASE) == 0
                    && (def->vd_ndx & 0x7fff) == ver)
@@ -197,11 +210,11 @@ static bool vdso_match_version(Elf64_Versym ver,
                if (def->vd_next == 0)
                        return false;  /* No definition. */
 
-               def = (Elf64_Verdef *)((char *)def + def->vd_next);
+               def = (ELF(Verdef) *)((char *)def + def->vd_next);
        }
 
        /* Now figure out whether it matches. */
-       Elf64_Verdaux *aux = (Elf64_Verdaux*)((char *)def + def->vd_aux);
+       ELF(Verdaux) *aux = (ELF(Verdaux)*)((char *)def + def->vd_aux);
        return def->vd_hash == hash
                && !strcmp(name, vdso_info.symstrings + aux->vda_name);
 }
@@ -213,10 +226,10 @@ void *vdso_sym(const char *version, const char *name)
                return 0;
 
        ver_hash = elf_hash(version);
-       Elf64_Word chain = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket];
+       ELF(Word) chain = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket];
 
        for (; chain != STN_UNDEF; chain = vdso_info.chain[chain]) {
-               Elf64_Sym *sym = &vdso_info.symtab[chain];
+               ELF(Sym) *sym = &vdso_info.symtab[chain];
 
                /* Check for a defined global or weak function w/ right name. */
                if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
@@ -243,7 +256,7 @@ void *vdso_sym(const char *version, const char *name)
 
 void vdso_init_from_auxv(void *auxv)
 {
-       Elf64_auxv_t *elf_auxv = auxv;
+       ELF(auxv_t) *elf_auxv = auxv;
        for (int i = 0; elf_auxv[i].a_type != AT_NULL; i++)
        {
                if (elf_auxv[i].a_type == AT_SYSINFO_EHDR) {
diff --git a/Documentation/vDSO/vdso_standalone_test_x86.c b/Documentation/vDSO/vdso_standalone_test_x86.c
new file mode 100644 (file)
index 0000000..d462402
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * vdso_test.c: Sample code to test parse_vdso.c on x86
+ * Copyright (c) 2011-2014 Andy Lutomirski
+ * Subject to the GNU General Public License, version 2
+ *
+ * You can amuse yourself by compiling with:
+ * gcc -std=gnu99 -nostdlib
+ *     -Os -fno-asynchronous-unwind-tables -flto -lgcc_s
+ *      vdso_standalone_test_x86.c parse_vdso.c
+ * to generate a small binary.  On x86_64, you can omit -lgcc_s
+ * if you want the binary to be completely standalone.
+ */
+
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdint.h>
+
+extern void *vdso_sym(const char *version, const char *name);
+extern void vdso_init_from_sysinfo_ehdr(uintptr_t base);
+extern void vdso_init_from_auxv(void *auxv);
+
+/* We need a libc functions... */
+int strcmp(const char *a, const char *b)
+{
+       /* This implementation is buggy: it never returns -1. */
+       while (*a || *b) {
+               if (*a != *b)
+                       return 1;
+               if (*a == 0 || *b == 0)
+                       return 1;
+               a++;
+               b++;
+       }
+
+       return 0;
+}
+
+/* ...and two syscalls.  This is x86-specific. */
+static inline long x86_syscall3(long nr, long a0, long a1, long a2)
+{
+       long ret;
+#ifdef __x86_64__
+       asm volatile ("syscall" : "=a" (ret) : "a" (nr),
+                     "D" (a0), "S" (a1), "d" (a2) :
+                     "cc", "memory", "rcx",
+                     "r8", "r9", "r10", "r11" );
+#else
+       asm volatile ("int $0x80" : "=a" (ret) : "a" (nr),
+                     "b" (a0), "c" (a1), "d" (a2) :
+                     "cc", "memory" );
+#endif
+       return ret;
+}
+
+static inline long linux_write(int fd, const void *data, size_t len)
+{
+       return x86_syscall3(__NR_write, fd, (long)data, (long)len);
+}
+
+static inline void linux_exit(int code)
+{
+       x86_syscall3(__NR_exit, code, 0, 0);
+}
+
+void to_base10(char *lastdig, uint64_t n)
+{
+       while (n) {
+               *lastdig = (n % 10) + '0';
+               n /= 10;
+               lastdig--;
+       }
+}
+
+__attribute__((externally_visible)) void c_main(void **stack)
+{
+       /* Parse the stack */
+       long argc = (long)*stack;
+       stack += argc + 2;
+
+       /* Now we're pointing at the environment.  Skip it. */
+       while(*stack)
+               stack++;
+       stack++;
+
+       /* Now we're pointing at auxv.  Initialize the vDSO parser. */
+       vdso_init_from_auxv((void *)stack);
+
+       /* Find gettimeofday. */
+       typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz);
+       gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday");
+
+       if (!gtod)
+               linux_exit(1);
+
+       struct timeval tv;
+       long ret = gtod(&tv, 0);
+
+       if (ret == 0) {
+               char buf[] = "The time is                     .000000\n";
+               to_base10(buf + 31, tv.tv_sec);
+               to_base10(buf + 38, tv.tv_usec);
+               linux_write(1, buf, sizeof(buf) - 1);
+       } else {
+               linux_exit(ret);
+       }
+
+       linux_exit(0);
+}
+
+/*
+ * This is the real entry point.  It passes the initial stack into
+ * the C entry point.
+ */
+asm (
+       ".text\n"
+       ".global _start\n"
+       ".type _start,@function\n"
+       "_start:\n\t"
+#ifdef __x86_64__
+       "mov %rsp,%rdi\n\t"
+       "jmp c_main"
+#else
+       "push %esp\n\t"
+       "call c_main\n\t"
+       "int $3"
+#endif
+       );
index fff633432dffe5412d5ccb8990e025bf745d1229..8daeb7d7032c2a0b79802bb9f8674a0d0d6aeb74 100644 (file)
 /*
- * vdso_test.c: Sample code to test parse_vdso.c on x86_64
- * Copyright (c) 2011 Andy Lutomirski
+ * vdso_test.c: Sample code to test parse_vdso.c
+ * Copyright (c) 2014 Andy Lutomirski
  * Subject to the GNU General Public License, version 2
  *
- * You can amuse yourself by compiling with:
- * gcc -std=gnu99 -nostdlib
- *     -Os -fno-asynchronous-unwind-tables -flto
- *      vdso_test.c parse_vdso.c -o vdso_test
- * to generate a small binary with no dependencies at all.
+ * Compile with:
+ * gcc -std=gnu99 vdso_test.c parse_vdso.c
+ *
+ * Tested on x86, 32-bit and 64-bit.  It may work on other architectures, too.
  */
 
-#include <sys/syscall.h>
-#include <sys/time.h>
-#include <unistd.h>
 #include <stdint.h>
+#include <elf.h>
+#include <stdio.h>
+#include <sys/auxv.h>
+#include <sys/time.h>
 
 extern void *vdso_sym(const char *version, const char *name);
 extern void vdso_init_from_sysinfo_ehdr(uintptr_t base);
 extern void vdso_init_from_auxv(void *auxv);
 
-/* We need a libc functions... */
-int strcmp(const char *a, const char *b)
+int main(int argc, char **argv)
 {
-       /* This implementation is buggy: it never returns -1. */
-       while (*a || *b) {
-               if (*a != *b)
-                       return 1;
-               if (*a == 0 || *b == 0)
-                       return 1;
-               a++;
-               b++;
+       unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR);
+       if (!sysinfo_ehdr) {
+               printf("AT_SYSINFO_EHDR is not present!\n");
+               return 0;
        }
 
-       return 0;
-}
-
-/* ...and two syscalls.  This is x86_64-specific. */
-static inline long linux_write(int fd, const void *data, size_t len)
-{
-
-       long ret;
-       asm volatile ("syscall" : "=a" (ret) : "a" (__NR_write),
-                     "D" (fd), "S" (data), "d" (len) :
-                     "cc", "memory", "rcx",
-                     "r8", "r9", "r10", "r11" );
-       return ret;
-}
-
-static inline void linux_exit(int code)
-{
-       asm volatile ("syscall" : : "a" (__NR_exit), "D" (code));
-}
-
-void to_base10(char *lastdig, uint64_t n)
-{
-       while (n) {
-               *lastdig = (n % 10) + '0';
-               n /= 10;
-               lastdig--;
-       }
-}
-
-__attribute__((externally_visible)) void c_main(void **stack)
-{
-       /* Parse the stack */
-       long argc = (long)*stack;
-       stack += argc + 2;
-
-       /* Now we're pointing at the environment.  Skip it. */
-       while(*stack)
-               stack++;
-       stack++;
-
-       /* Now we're pointing at auxv.  Initialize the vDSO parser. */
-       vdso_init_from_auxv((void *)stack);
+       vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR));
 
        /* Find gettimeofday. */
        typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz);
        gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday");
 
-       if (!gtod)
-               linux_exit(1);
+       if (!gtod) {
+               printf("Could not find __vdso_gettimeofday\n");
+               return 1;
+       }
 
        struct timeval tv;
        long ret = gtod(&tv, 0);
 
        if (ret == 0) {
-               char buf[] = "The time is                     .000000\n";
-               to_base10(buf + 31, tv.tv_sec);
-               to_base10(buf + 38, tv.tv_usec);
-               linux_write(1, buf, sizeof(buf) - 1);
+               printf("The time is %lld.%06lld\n",
+                      (long long)tv.tv_sec, (long long)tv.tv_usec);
        } else {
-               linux_exit(ret);
+               printf("__vdso_gettimeofday failed\n");
        }
 
-       linux_exit(0);
+       return 0;
 }
-
-/*
- * This is the real entry point.  It passes the initial stack into
- * the C entry point.
- */
-asm (
-       ".text\n"
-       ".global _start\n"
-        ".type _start,@function\n"
-        "_start:\n\t"
-        "mov %rsp,%rdi\n\t"
-        "jmp c_main"
-       );
index b4a66b9d6b4d2b873c912af253ef47f2b56a34ca..134483f206e42661947e0bc4b84ef09ab07abd1b 100644 (file)
@@ -604,6 +604,13 @@ L: amd64-microcode@amd64.org
 S:     Maintained
 F:     arch/x86/kernel/microcode_amd.c
 
+AMD XGBE DRIVER
+M:     Tom Lendacky <thomas.lendacky@amd.com>
+L:     netdev@vger.kernel.org
+S:     Supported
+F:     drivers/net/ethernet/amd/xgbe/
+F:     drivers/net/phy/amd-xgbe-phy.c
+
 AMS (Apple Motion Sensor) DRIVER
 M:     Michael Hanselmann <linux-kernel@hansmi.ch>
 S:     Supported
@@ -1894,7 +1901,7 @@ F:        drivers/net/ethernet/broadcom/bnx2.*
 F:     drivers/net/ethernet/broadcom/bnx2_*
 
 BROADCOM BNX2X 10 GIGABIT ETHERNET DRIVER
-M:     Ariel Elior <ariele@broadcom.com>
+M:     Ariel Elior <ariel.elior@qlogic.com>
 L:     netdev@vger.kernel.org
 S:     Supported
 F:     drivers/net/ethernet/broadcom/bnx2x/
@@ -1974,6 +1981,12 @@ S:       Maintained
 F:     drivers/bcma/
 F:     include/linux/bcma/
 
+BROADCOM SYSTEMPORT ETHERNET DRIVER
+M:     Florian Fainelli <f.fainelli@gmail.com>
+L:     netdev@vger.kernel.org
+S:     Supported
+F:     drivers/net/ethernet/broadcom/bcmsysport.*
+
 BROCADE BFA FC SCSI DRIVER
 M:     Anil Gurumurthy <anil.gurumurthy@qlogic.com>
 M:     Sudarsana Kalluru <sudarsana.kalluru@qlogic.com>
@@ -2230,9 +2243,8 @@ F:        drivers/platform/chrome/
 CISCO VIC ETHERNET NIC DRIVER
 M:     Christian Benvenuti <benve@cisco.com>
 M:     Sujith Sankar <ssujith@cisco.com>
-M:     Govindarajulu Varadarajan <govindarajulu90@gmail.com>
+M:     Govindarajulu Varadarajan <_govind@gmx.com>
 M:     Neel Patel <neepatel@cisco.com>
-M:     Nishank Trivedi <nistrive@cisco.com>
 S:     Supported
 F:     drivers/net/ethernet/cisco/enic/
 
@@ -2582,7 +2594,7 @@ S:        Supported
 F:     drivers/infiniband/hw/cxgb3/
 
 CXGB4 ETHERNET DRIVER (CXGB4)
-M:     Dimitris Michailidis <dm@chelsio.com>
+M:     Hariprasad S <hariprasad@chelsio.com>
 L:     netdev@vger.kernel.org
 W:     http://www.chelsio.com
 S:     Supported
@@ -2952,6 +2964,7 @@ L:        dri-devel@lists.freedesktop.org
 T:     git git://people.freedesktop.org/~airlied/linux
 S:     Maintained
 F:     drivers/gpu/drm/
+F:     drivers/gpu/vga/
 F:     include/drm/
 F:     include/uapi/drm/
 
@@ -6167,6 +6180,7 @@ F:        include/uapi/linux/netdevice.h
 F:     tools/net/
 F:     tools/testing/selftests/net/
 F:     lib/random32.c
+F:     lib/test_bpf.c
 
 NETWORKING [IPv4/IPv6]
 M:     "David S. Miller" <davem@davemloft.net>
index c761fb1abfb6e283ff1e08fb1f039f084f7648cd..97b286128c1e869a2c69bc4a411ea19f6ef74d8b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 3
-PATCHLEVEL = 15
+PATCHLEVEL = 16
 SUBLEVEL = 0
-EXTRAVERSION =
+EXTRAVERSION = -rc1
 NAME = Shuffling Zombie Juror
 
 # *DOCUMENTATION*
@@ -105,10 +105,6 @@ ifeq ("$(origin O)", "command line")
   KBUILD_OUTPUT := $(O)
 endif
 
-ifeq ("$(origin W)", "command line")
-  export KBUILD_ENABLE_EXTRA_GCC_CHECKS := $(W)
-endif
-
 # That's our default target when none is given on the command line
 PHONY := _all
 _all:
@@ -153,8 +149,18 @@ else
 _all: modules
 endif
 
-srctree                := $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR))
-objtree                := $(CURDIR)
+ifeq ($(KBUILD_SRC),)
+        # building in the source tree
+        srctree := .
+else
+        ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))
+                # building in a subdirectory of the source tree
+                srctree := ..
+        else
+                srctree := $(KBUILD_SRC)
+        endif
+endif
+objtree                := .
 src            := $(srctree)
 obj            := $(objtree)
 
@@ -166,7 +172,7 @@ export srctree objtree VPATH
 # SUBARCH tells the usermode build what the underlying arch is.  That is set
 # first, and if a usermode build is happening, the "ARCH=um" on the command
 # line overrides the setting of ARCH below.  If a native build is happening,
-# then ARCH is assigned, getting whatever value it gets normally, and 
+# then ARCH is assigned, getting whatever value it gets normally, and
 # SUBARCH is subsequently ignored.
 
 SUBARCH := $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ \
@@ -259,18 +265,18 @@ endif
 KBUILD_MODULES :=
 KBUILD_BUILTIN := 1
 
-#      If we have only "make modules", don't compile built-in objects.
-#      When we're building modules with modversions, we need to consider
-#      the built-in objects during the descend as well, in order to
-#      make sure the checksums are up to date before we record them.
+# If we have only "make modules", don't compile built-in objects.
+# When we're building modules with modversions, we need to consider
+# the built-in objects during the descend as well, in order to
+# make sure the checksums are up to date before we record them.
 
 ifeq ($(MAKECMDGOALS),modules)
   KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1)
 endif
 
-#      If we have "make <whatever> modules", compile modules
-#      in addition to whatever we do anyway.
-#      Just "make" or "make all" shall build modules as well
+# If we have "make <whatever> modules", compile modules
+# in addition to whatever we do anyway.
+# Just "make" or "make all" shall build modules as well
 
 ifneq ($(filter all _all modules,$(MAKECMDGOALS)),)
   KBUILD_MODULES := 1
@@ -294,7 +300,7 @@ export KBUILD_CHECKSRC KBUILD_SRC KBUILD_EXTMOD
 #         cmd_cc_o_c       = $(CC) $(c_flags) -c -o $@ $<
 #
 # If $(quiet) is empty, the whole command will be printed.
-# If it is set to "quiet_", only the short version will be printed. 
+# If it is set to "quiet_", only the short version will be printed.
 # If it is set to "silent_", nothing will be printed at all, since
 # the variable $(silent_cmd_cc_o_c) doesn't exist.
 #
@@ -346,7 +352,6 @@ $(srctree)/scripts/Kbuild.include: ;
 include $(srctree)/scripts/Kbuild.include
 
 # Make variables (CC, etc...)
-
 AS             = $(CROSS_COMPILE)as
 LD             = $(CROSS_COMPILE)ld
 CC             = $(CROSS_COMPILE)gcc
@@ -395,8 +400,8 @@ KBUILD_CPPFLAGS := -D__KERNEL__
 KBUILD_CFLAGS   := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
                   -fno-strict-aliasing -fno-common \
                   -Werror-implicit-function-declaration \
-                  -Wno-format-security \
-                  $(call cc-option,-fno-delete-null-pointer-checks,)
+                  -Wno-format-security
+
 KBUILD_AFLAGS_KERNEL :=
 KBUILD_CFLAGS_KERNEL :=
 KBUILD_AFLAGS   := -D__ASSEMBLY__
@@ -504,8 +509,16 @@ ifeq ($(mixed-targets),1)
 # We're called with mixed targets (*config and build targets).
 # Handle them one by one.
 
-%:: FORCE
-       $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= $@
+PHONY += $(MAKECMDGOALS) __build_one_by_one
+
+$(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one
+       @:
+
+__build_one_by_one:
+       $(Q)set -e; \
+       for i in $(MAKECMDGOALS); do \
+               $(MAKE) -f $(srctree)/Makefile $$i; \
+       done
 
 else
 ifeq ($(config-targets),1)
@@ -520,11 +533,9 @@ include $(srctree)/arch/$(SRCARCH)/Makefile
 export KBUILD_DEFCONFIG KBUILD_KCONFIG
 
 config: scripts_basic outputmakefile FORCE
-       $(Q)mkdir -p include/linux include/config
        $(Q)$(MAKE) $(build)=scripts/kconfig $@
 
 %config: scripts_basic outputmakefile FORCE
-       $(Q)mkdir -p include/linux include/config
        $(Q)$(MAKE) $(build)=scripts/kconfig $@
 
 else
@@ -594,14 +605,16 @@ endif # $(dot-config)
 # Defaults to vmlinux, but the arch makefile usually adds further targets
 all: vmlinux
 
+include $(srctree)/arch/$(SRCARCH)/Makefile
+
+KBUILD_CFLAGS  += $(call cc-option,-fno-delete-null-pointer-checks,)
+
 ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
 KBUILD_CFLAGS  += -Os $(call cc-disable-warning,maybe-uninitialized,)
 else
 KBUILD_CFLAGS  += -O2
 endif
 
-include $(srctree)/arch/$(SRCARCH)/Makefile
-
 ifdef CONFIG_READABLE_ASM
 # Disable optimizations that make assembler listings hard to read.
 # reorder blocks reorders the control in the function
@@ -731,6 +744,8 @@ ifeq ($(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-goto.sh $(CC)), y)
        KBUILD_CFLAGS += -DCC_HAVE_ASM_GOTO
 endif
 
+include $(srctree)/scripts/Makefile.extrawarn
+
 # Add user supplied CPPFLAGS, AFLAGS and CFLAGS as the last assignments
 KBUILD_CPPFLAGS += $(KCPPFLAGS)
 KBUILD_AFLAGS += $(KAFLAGS)
@@ -775,10 +790,10 @@ MODLIB    = $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)
 export MODLIB
 
 #
-#  INSTALL_MOD_STRIP, if defined, will cause modules to be
-#  stripped after they are installed.  If INSTALL_MOD_STRIP is '1', then
-#  the default option --strip-debug will be used.  Otherwise,
-#  INSTALL_MOD_STRIP value will be used as the options to the strip command.
+# INSTALL_MOD_STRIP, if defined, will cause modules to be
+# stripped after they are installed.  If INSTALL_MOD_STRIP is '1', then
+# the default option --strip-debug will be used.  Otherwise,
+# INSTALL_MOD_STRIP value will be used as the options to the strip command.
 
 ifdef INSTALL_MOD_STRIP
 ifeq ($(INSTALL_MOD_STRIP),1)
@@ -863,7 +878,7 @@ ifdef CONFIG_BUILD_DOCSRC
 endif
        +$(call if_changed,link-vmlinux)
 
-# The actual objects are generated when descending, 
+# The actual objects are generated when descending,
 # make sure no implicit rule kicks in
 $(sort $(vmlinux-deps)): $(vmlinux-dirs) ;
 
@@ -1021,11 +1036,11 @@ ifdef CONFIG_MODULES
 
 all: modules
 
-#      Build modules
+# Build modules
 #
-#      A module can be listed more than once in obj-m resulting in
-#      duplicate lines in modules.order files.  Those are removed
-#      using awk while concatenating to the final file.
+# A module can be listed more than once in obj-m resulting in
+# duplicate lines in modules.order files.  Those are removed
+# using awk while concatenating to the final file.
 
 PHONY += modules
 modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux) modules.builtin
@@ -1054,10 +1069,10 @@ _modinst_:
        @rm -rf $(MODLIB)/kernel
        @rm -f $(MODLIB)/source
        @mkdir -p $(MODLIB)/kernel
-       @ln -s $(srctree) $(MODLIB)/source
+       @ln -s `cd $(srctree) && /bin/pwd` $(MODLIB)/source
        @if [ ! $(objtree) -ef  $(MODLIB)/build ]; then \
                rm -f $(MODLIB)/build ; \
-               ln -s $(objtree) $(MODLIB)/build ; \
+               ln -s $(CURDIR) $(MODLIB)/build ; \
        fi
        @cp -f $(objtree)/modules.order $(MODLIB)/
        @cp -f $(objtree)/modules.builtin $(MODLIB)/
@@ -1104,7 +1119,7 @@ CLEAN_DIRS  += $(MODVERDIR)
 
 # Directories & files removed with 'make mrproper'
 MRPROPER_DIRS  += include/config usr/include include/generated          \
-                  arch/*/include/generated .tmp_objdiff
+                 arch/*/include/generated .tmp_objdiff
 MRPROPER_FILES += .config .config.old .version .old_version $(version_h) \
                  Module.symvers tags TAGS cscope* GPATH GTAGS GRTAGS GSYMS \
                  signing_key.priv signing_key.x509 x509.genkey         \
@@ -1478,7 +1493,7 @@ endif
        $(build)=$(build-dir) $(@:.ko=.o)
        $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost
 
-# FIXME Should go into a make.lib or something 
+# FIXME Should go into a make.lib or something
 # ===========================================================================
 
 quiet_cmd_rmdirs = $(if $(wildcard $(rm-dirs)),CLEAN   $(wildcard $(rm-dirs)))
index 9f53e824b037a75ab8055226a792cf2ccd154eb4..4a4e02d0ce9e408b16b3c1bb57222fad963a2766 100644 (file)
                mac: ethernet@4a100000 {
                        compatible = "ti,cpsw";
                        ti,hwmods = "cpgmac0";
+                       clocks = <&cpsw_125mhz_gclk>, <&cpsw_cpts_rft_clk>;
+                       clock-names = "fck", "cpts";
                        cpdma_channels = <8>;
                        ale_entries = <1024>;
                        bd_ram_size = <0x2000>;
index db464d7eaca847217260319ed3b064f2834eac6d..49fa596222547d646c79a031b260097cf9a8bf8e 100644 (file)
                        #address-cells = <1>;
                        #size-cells = <1>;
                        ti,hwmods = "cpgmac0";
+                       clocks = <&cpsw_125mhz_gclk>, <&cpsw_cpts_rft_clk>;
+                       clock-names = "fck", "cpts";
                        status = "disabled";
                        cpdma_channels = <8>;
                        ale_entries = <1024>;
index 25674fe81f703d279dc154ff75d02d366b34cef6..7e291e2ef4b38dc81a39d404b4c8953b673604e7 100644 (file)
                        ethernet@30000 {
                                status = "okay";
                                phy-mode = "sgmii";
+                               fixed-link {
+                                       speed = <1000>;
+                                       full-duplex;
+                               };
                        };
 
                        pcie-controller {
index c7676871d9c0240032261ed40f0db7c431a583d8..b03cfe49d22be4bce3809d606ccec17658f4d07c 100644 (file)
@@ -26,7 +26,7 @@
                clock-frequency = <0>;
        };
 
-       atlclkin3_ck: atlclkin3_ck {
+       atl_clkin3_ck: atl_clkin3_ck {
                #clock-cells = <0>;
                compatible = "fixed-clock";
                clock-frequency = <0>;
 
        dpll_mpu_ck: dpll_mpu_ck {
                #clock-cells = <0>;
-               compatible = "ti,omap4-dpll-clock";
+               compatible = "ti,omap5-mpu-dpll-clock";
                clocks = <&sys_clkin1>, <&mpu_dpll_hs_clk_div>;
                reg = <0x0160>, <0x0164>, <0x016c>, <0x0168>;
        };
        mcasp1_ahclkr_mux: mcasp1_ahclkr_mux {
                #clock-cells = <0>;
                compatible = "ti,mux-clock";
-               clocks = <&abe_24m_fclk>, <&abe_sys_clk_div>, <&func_24m_clk>, <&atlclkin3_ck>, <&atl_clkin2_ck>, <&atl_clkin1_ck>, <&atl_clkin0_ck>, <&sys_clkin2>, <&ref_clkin0_ck>, <&ref_clkin1_ck>, <&ref_clkin2_ck>, <&ref_clkin3_ck>, <&mlb_clk>, <&mlbp_clk>;
+               clocks = <&abe_24m_fclk>, <&abe_sys_clk_div>, <&func_24m_clk>, <&atl_clkin3_ck>, <&atl_clkin2_ck>, <&atl_clkin1_ck>, <&atl_clkin0_ck>, <&sys_clkin2>, <&ref_clkin0_ck>, <&ref_clkin1_ck>, <&ref_clkin2_ck>, <&ref_clkin3_ck>, <&mlb_clk>, <&mlbp_clk>;
                ti,bit-shift = <28>;
                reg = <0x0550>;
        };
        mcasp1_ahclkx_mux: mcasp1_ahclkx_mux {
                #clock-cells = <0>;
                compatible = "ti,mux-clock";
-               clocks = <&abe_24m_fclk>, <&abe_sys_clk_div>, <&func_24m_clk>, <&atlclkin3_ck>, <&atl_clkin2_ck>, <&atl_clkin1_ck>, <&atl_clkin0_ck>, <&sys_clkin2>, <&ref_clkin0_ck>, <&ref_clkin1_ck>, <&ref_clkin2_ck>, <&ref_clkin3_ck>, <&mlb_clk>, <&mlbp_clk>;
+               clocks = <&abe_24m_fclk>, <&abe_sys_clk_div>, <&func_24m_clk>, <&atl_clkin3_ck>, <&atl_clkin2_ck>, <&atl_clkin1_ck>, <&atl_clkin0_ck>, <&sys_clkin2>, <&ref_clkin0_ck>, <&ref_clkin1_ck>, <&ref_clkin2_ck>, <&ref_clkin3_ck>, <&mlb_clk>, <&mlbp_clk>;
                ti,bit-shift = <24>;
                reg = <0x0550>;
        };
        mcasp2_ahclkr_mux: mcasp2_ahclkr_mux {
                #clock-cells = <0>;
                compatible = "ti,mux-clock";
-               clocks = <&abe_24m_fclk>, <&abe_sys_clk_div>, <&func_24m_clk>, <&atlclkin3_ck>, <&atl_clkin2_ck>, <&atl_clkin1_ck>, <&atl_clkin0_ck>, <&sys_clkin2>, <&ref_clkin0_ck>, <&ref_clkin1_ck>, <&ref_clkin2_ck>, <&ref_clkin3_ck>, <&mlb_clk>, <&mlbp_clk>;
+               clocks = <&abe_24m_fclk>, <&abe_sys_clk_div>, <&func_24m_clk>, <&atl_clkin3_ck>, <&atl_clkin2_ck>, <&atl_clkin1_ck>, <&atl_clkin0_ck>, <&sys_clkin2>, <&ref_clkin0_ck>, <&ref_clkin1_ck>, <&ref_clkin2_ck>, <&ref_clkin3_ck>, <&mlb_clk>, <&mlbp_clk>;
                ti,bit-shift = <28>;
                reg = <0x1860>;
        };
        mcasp2_ahclkx_mux: mcasp2_ahclkx_mux {
                #clock-cells = <0>;
                compatible = "ti,mux-clock";
-               clocks = <&abe_24m_fclk>, <&abe_sys_clk_div>, <&func_24m_clk>, <&atlclkin3_ck>, <&atl_clkin2_ck>, <&atl_clkin1_ck>, <&atl_clkin0_ck>, <&sys_clkin2>, <&ref_clkin0_ck>, <&ref_clkin1_ck>, <&ref_clkin2_ck>, <&ref_clkin3_ck>, <&mlb_clk>, <&mlbp_clk>;
+               clocks = <&abe_24m_fclk>, <&abe_sys_clk_div>, <&func_24m_clk>, <&atl_clkin3_ck>, <&atl_clkin2_ck>, <&atl_clkin1_ck>, <&atl_clkin0_ck>, <&sys_clkin2>, <&ref_clkin0_ck>, <&ref_clkin1_ck>, <&ref_clkin2_ck>, <&ref_clkin3_ck>, <&mlb_clk>, <&mlbp_clk>;
                ti,bit-shift = <24>;
                reg = <0x1860>;
        };
        mcasp3_ahclkx_mux: mcasp3_ahclkx_mux {
                #clock-cells = <0>;
                compatible = "ti,mux-clock";
-               clocks = <&abe_24m_fclk>, <&abe_sys_clk_div>, <&func_24m_clk>, <&atlclkin3_ck>, <&atl_clkin2_ck>, <&atl_clkin1_ck>, <&atl_clkin0_ck>, <&sys_clkin2>, <&ref_clkin0_ck>, <&ref_clkin1_ck>, <&ref_clkin2_ck>, <&ref_clkin3_ck>, <&mlb_clk>, <&mlbp_clk>;
+               clocks = <&abe_24m_fclk>, <&abe_sys_clk_div>, <&func_24m_clk>, <&atl_clkin3_ck>, <&atl_clkin2_ck>, <&atl_clkin1_ck>, <&atl_clkin0_ck>, <&sys_clkin2>, <&ref_clkin0_ck>, <&ref_clkin1_ck>, <&ref_clkin2_ck>, <&ref_clkin3_ck>, <&mlb_clk>, <&mlbp_clk>;
                ti,bit-shift = <24>;
                reg = <0x1868>;
        };
        mcasp4_ahclkx_mux: mcasp4_ahclkx_mux {
                #clock-cells = <0>;
                compatible = "ti,mux-clock";
-               clocks = <&abe_24m_fclk>, <&abe_sys_clk_div>, <&func_24m_clk>, <&atlclkin3_ck>, <&atl_clkin2_ck>, <&atl_clkin1_ck>, <&atl_clkin0_ck>, <&sys_clkin2>, <&ref_clkin0_ck>, <&ref_clkin1_ck>, <&ref_clkin2_ck>, <&ref_clkin3_ck>, <&mlb_clk>, <&mlbp_clk>;
+               clocks = <&abe_24m_fclk>, <&abe_sys_clk_div>, <&func_24m_clk>, <&atl_clkin3_ck>, <&atl_clkin2_ck>, <&atl_clkin1_ck>, <&atl_clkin0_ck>, <&sys_clkin2>, <&ref_clkin0_ck>, <&ref_clkin1_ck>, <&ref_clkin2_ck>, <&ref_clkin3_ck>, <&mlb_clk>, <&mlbp_clk>;
                ti,bit-shift = <24>;
                reg = <0x1898>;
        };
        mcasp5_ahclkx_mux: mcasp5_ahclkx_mux {
                #clock-cells = <0>;
                compatible = "ti,mux-clock";
-               clocks = <&abe_24m_fclk>, <&abe_sys_clk_div>, <&func_24m_clk>, <&atlclkin3_ck>, <&atl_clkin2_ck>, <&atl_clkin1_ck>, <&atl_clkin0_ck>, <&sys_clkin2>, <&ref_clkin0_ck>, <&ref_clkin1_ck>, <&ref_clkin2_ck>, <&ref_clkin3_ck>, <&mlb_clk>, <&mlbp_clk>;
+               clocks = <&abe_24m_fclk>, <&abe_sys_clk_div>, <&func_24m_clk>, <&atl_clkin3_ck>, <&atl_clkin2_ck>, <&atl_clkin1_ck>, <&atl_clkin0_ck>, <&sys_clkin2>, <&ref_clkin0_ck>, <&ref_clkin1_ck>, <&ref_clkin2_ck>, <&ref_clkin3_ck>, <&mlb_clk>, <&mlbp_clk>;
                ti,bit-shift = <24>;
                reg = <0x1878>;
        };
        mcasp6_ahclkx_mux: mcasp6_ahclkx_mux {
                #clock-cells = <0>;
                compatible = "ti,mux-clock";
-               clocks = <&abe_24m_fclk>, <&abe_sys_clk_div>, <&func_24m_clk>, <&atlclkin3_ck>, <&atl_clkin2_ck>, <&atl_clkin1_ck>, <&atl_clkin0_ck>, <&sys_clkin2>, <&ref_clkin0_ck>, <&ref_clkin1_ck>, <&ref_clkin2_ck>, <&ref_clkin3_ck>, <&mlb_clk>, <&mlbp_clk>;
+               clocks = <&abe_24m_fclk>, <&abe_sys_clk_div>, <&func_24m_clk>, <&atl_clkin3_ck>, <&atl_clkin2_ck>, <&atl_clkin1_ck>, <&atl_clkin0_ck>, <&sys_clkin2>, <&ref_clkin0_ck>, <&ref_clkin1_ck>, <&ref_clkin2_ck>, <&ref_clkin3_ck>, <&mlb_clk>, <&mlbp_clk>;
                ti,bit-shift = <24>;
                reg = <0x1904>;
        };
        mcasp7_ahclkx_mux: mcasp7_ahclkx_mux {
                #clock-cells = <0>;
                compatible = "ti,mux-clock";
-               clocks = <&abe_24m_fclk>, <&abe_sys_clk_div>, <&func_24m_clk>, <&atlclkin3_ck>, <&atl_clkin2_ck>, <&atl_clkin1_ck>, <&atl_clkin0_ck>, <&sys_clkin2>, <&ref_clkin0_ck>, <&ref_clkin1_ck>, <&ref_clkin2_ck>, <&ref_clkin3_ck>, <&mlb_clk>, <&mlbp_clk>;
+               clocks = <&abe_24m_fclk>, <&abe_sys_clk_div>, <&func_24m_clk>, <&atl_clkin3_ck>, <&atl_clkin2_ck>, <&atl_clkin1_ck>, <&atl_clkin0_ck>, <&sys_clkin2>, <&ref_clkin0_ck>, <&ref_clkin1_ck>, <&ref_clkin2_ck>, <&ref_clkin3_ck>, <&mlb_clk>, <&mlbp_clk>;
                ti,bit-shift = <24>;
                reg = <0x1908>;
        };
        mcasp8_ahclk_mux: mcasp8_ahclk_mux {
                #clock-cells = <0>;
                compatible = "ti,mux-clock";
-               clocks = <&abe_24m_fclk>, <&abe_sys_clk_div>, <&func_24m_clk>, <&atlclkin3_ck>, <&atl_clkin2_ck>, <&atl_clkin1_ck>, <&atl_clkin0_ck>, <&sys_clkin2>, <&ref_clkin0_ck>, <&ref_clkin1_ck>, <&ref_clkin2_ck>, <&ref_clkin3_ck>, <&mlb_clk>, <&mlbp_clk>;
+               clocks = <&abe_24m_fclk>, <&abe_sys_clk_div>, <&func_24m_clk>, <&atl_clkin3_ck>, <&atl_clkin2_ck>, <&atl_clkin1_ck>, <&atl_clkin0_ck>, <&sys_clkin2>, <&ref_clkin0_ck>, <&ref_clkin1_ck>, <&ref_clkin2_ck>, <&ref_clkin3_ck>, <&mlb_clk>, <&mlbp_clk>;
                ti,bit-shift = <22>;
                reg = <0x1890>;
        };
index aeb142ce8e9d34ffa0dd1565a21f2211a8b11917..e67a23b5d7884725290b6348a54a97f3427ccbc3 100644 (file)
 
        dpll_mpu_ck: dpll_mpu_ck {
                #clock-cells = <0>;
-               compatible = "ti,omap4-dpll-clock";
+               compatible = "ti,omap5-mpu-dpll-clock";
                clocks = <&sys_clkin>, <&mpu_dpll_hs_clk_div>;
                reg = <0x0160>, <0x0164>, <0x016c>, <0x0168>;
        };
index 51d0e912c8f585b1acb51edc9f47fc4270a1a988..1929ad390d88feb0ec42bce29ad1b174823ed55b 100644 (file)
                        reg = <0xd8100000 0x10000>;
                        interrupts = <48>;
                };
+
+               ethernet@d8004000 {
+                       compatible = "via,vt8500-rhine";
+                       reg = <0xd8004000 0x100>;
+                       interrupts = <10>;
+               };
        };
 };
index 7525982262ac9896285031462e45b23b4020d9c7..b1c59a766a13381a693d897eb3349db4ac9d3c16 100644 (file)
                        reg = <0xd8100000 0x10000>;
                        interrupts = <48>;
                };
+
+               ethernet@d8004000 {
+                       compatible = "via,vt8500-rhine";
+                       reg = <0xd8004000 0x100>;
+                       interrupts = <10>;
+               };
        };
 };
index d98386dd2882500bd71ecf726d8ac9bb26b777a7..8fbccfbe75f33df7be79ea7be37c15b9f5bf2535 100644 (file)
                        bus-width = <4>;
                        sdon-inverted;
                };
+
+               ethernet@d8004000 {
+                       compatible = "via,vt8500-rhine";
+                       reg = <0xd8004000 0x100>;
+                       interrupts = <10>;
+                };
        };
 };
index a6bc431cde701037ca6146d6931a56aa96838cb1..4238bcba9d60fc0aaa697a2a83818556db7c66cf 100644 (file)
@@ -410,7 +410,7 @@ __hw_perf_event_init(struct perf_event *event)
         */
        hwc->config_base            |= (unsigned long)mapping;
 
-       if (!hwc->sample_period) {
+       if (!is_sampling_event(event)) {
                /*
                 * For non-sampling runs, limit the sample_period to half
                 * of the counter width. That way, the new counter value
index a71ae1523620afc4cd149c26092d16d69cd858d8..af9e35e8836f1f3de2d9a4aaeee0c9445ce23740 100644 (file)
@@ -126,8 +126,8 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
 
        irqs = min(pmu_device->num_resources, num_possible_cpus());
        if (irqs < 1) {
-               pr_err("no irqs for PMUs defined\n");
-               return -ENODEV;
+               printk_once("perf/ARM: No irqs for PMU defined, sampling events not supported\n");
+               return 0;
        }
 
        irq = platform_get_irq(pmu_device, 0);
@@ -191,6 +191,10 @@ static void cpu_pmu_init(struct arm_pmu *cpu_pmu)
        /* Ensure the PMU has sane values out of reset. */
        if (cpu_pmu->reset)
                on_each_cpu(cpu_pmu->reset, cpu_pmu, 1);
+
+       /* If no interrupts available, set the corresponding capability flag */
+       if (!platform_get_irq(cpu_pmu->plat_device, 0))
+               cpu_pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
 }
 
 /*
index 3997c411c1403659123d188c1c0750c58f59af3f..9d853189028bb0c79ad72557b018c12d5416aa67 100644 (file)
 #include <asm/topology.h>
 
 /*
- * cpu power scale management
+ * cpu capacity scale management
  */
 
 /*
- * cpu power table
+ * cpu capacity table
  * This per cpu data structure describes the relative capacity of each core.
  * On a heteregenous system, cores don't have the same computation capacity
- * and we reflect that difference in the cpu_power field so the scheduler can
- * take this difference into account during load balance. A per cpu structure
- * is preferred because each CPU updates its own cpu_power field during the
- * load balance except for idle cores. One idle core is selected to run the
- * rebalance_domains for all idle cores and the cpu_power can be updated
- * during this sequence.
+ * and we reflect that difference in the cpu_capacity field so the scheduler
+ * can take this difference into account during load balance. A per cpu
+ * structure is preferred because each CPU updates its own cpu_capacity field
+ * during the load balance except for idle cores. One idle core is selected
+ * to run the rebalance_domains for all idle cores and the cpu_capacity can be
+ * updated during this sequence.
  */
 static DEFINE_PER_CPU(unsigned long, cpu_scale);
 
-unsigned long arch_scale_freq_power(struct sched_domain *sd, int cpu)
+unsigned long arch_scale_freq_capacity(struct sched_domain *sd, int cpu)
 {
        return per_cpu(cpu_scale, cpu);
 }
 
-static void set_power_scale(unsigned int cpu, unsigned long power)
+static void set_capacity_scale(unsigned int cpu, unsigned long capacity)
 {
-       per_cpu(cpu_scale, cpu) = power;
+       per_cpu(cpu_scale, cpu) = capacity;
 }
 
 #ifdef CONFIG_OF
@@ -62,11 +62,11 @@ struct cpu_efficiency {
  * Table of relative efficiency of each processors
  * The efficiency value must fit in 20bit and the final
  * cpu_scale value must be in the range
- *   0 < cpu_scale < 3*SCHED_POWER_SCALE/2
+ *   0 < cpu_scale < 3*SCHED_CAPACITY_SCALE/2
  * in order to return at most 1 when DIV_ROUND_CLOSEST
  * is used to compute the capacity of a CPU.
  * Processors that are not defined in the table,
- * use the default SCHED_POWER_SCALE value for cpu_scale.
+ * use the default SCHED_CAPACITY_SCALE value for cpu_scale.
  */
 static const struct cpu_efficiency table_efficiency[] = {
        {"arm,cortex-a15", 3891},
@@ -83,9 +83,9 @@ static unsigned long middle_capacity = 1;
  * Iterate all CPUs' descriptor in DT and compute the efficiency
  * (as per table_efficiency). Also calculate a middle efficiency
  * as close as possible to  (max{eff_i} - min{eff_i}) / 2
- * This is later used to scale the cpu_power field such that an
- * 'average' CPU is of middle power. Also see the comments near
- * table_efficiency[] and update_cpu_power().
+ * This is later used to scale the cpu_capacity field such that an
+ * 'average' CPU is of middle capacity. Also see the comments near
+ * table_efficiency[] and update_cpu_capacity().
  */
 static void __init parse_dt_topology(void)
 {
@@ -141,15 +141,15 @@ static void __init parse_dt_topology(void)
         * cpu_scale because all CPUs have the same capacity. Otherwise, we
         * compute a middle_capacity factor that will ensure that the capacity
         * of an 'average' CPU of the system will be as close as possible to
-        * SCHED_POWER_SCALE, which is the default value, but with the
+        * SCHED_CAPACITY_SCALE, which is the default value, but with the
         * constraint explained near table_efficiency[].
         */
        if (4*max_capacity < (3*(max_capacity + min_capacity)))
                middle_capacity = (min_capacity + max_capacity)
-                               >> (SCHED_POWER_SHIFT+1);
+                               >> (SCHED_CAPACITY_SHIFT+1);
        else
                middle_capacity = ((max_capacity / 3)
-                               >> (SCHED_POWER_SHIFT-1)) + 1;
+                               >> (SCHED_CAPACITY_SHIFT-1)) + 1;
 
 }
 
@@ -158,20 +158,20 @@ static void __init parse_dt_topology(void)
  * boot. The update of all CPUs is in O(n^2) for heteregeneous system but the
  * function returns directly for SMP system.
  */
-static void update_cpu_power(unsigned int cpu)
+static void update_cpu_capacity(unsigned int cpu)
 {
        if (!cpu_capacity(cpu))
                return;
 
-       set_power_scale(cpu, cpu_capacity(cpu) / middle_capacity);
+       set_capacity_scale(cpu, cpu_capacity(cpu) / middle_capacity);
 
-       printk(KERN_INFO "CPU%u: update cpu_power %lu\n",
-               cpu, arch_scale_freq_power(NULL, cpu));
+       printk(KERN_INFO "CPU%u: update cpu_capacity %lu\n",
+               cpu, arch_scale_freq_capacity(NULL, cpu));
 }
 
 #else
 static inline void parse_dt_topology(void) {}
-static inline void update_cpu_power(unsigned int cpuid) {}
+static inline void update_cpu_capacity(unsigned int cpuid) {}
 #endif
 
  /*
@@ -267,7 +267,7 @@ void store_cpu_topology(unsigned int cpuid)
 
        update_siblings_masks(cpuid);
 
-       update_cpu_power(cpuid);
+       update_cpu_capacity(cpuid);
 
        printk(KERN_INFO "CPU%u: thread %d, cpu %d, socket %d, mpidr %x\n",
                cpuid, cpu_topology[cpuid].thread_id,
@@ -297,7 +297,7 @@ void __init init_cpu_topology(void)
 {
        unsigned int cpu;
 
-       /* init core mask and power*/
+       /* init core mask and capacity */
        for_each_possible_cpu(cpu) {
                struct cputopo_arm *cpu_topo = &(cpu_topology[cpu]);
 
@@ -307,7 +307,7 @@ void __init init_cpu_topology(void)
                cpumask_clear(&cpu_topo->core_sibling);
                cpumask_clear(&cpu_topo->thread_sibling);
 
-               set_power_scale(cpu, SCHED_POWER_SCALE);
+               set_capacity_scale(cpu, SCHED_CAPACITY_SCALE);
        }
        smp_wmb();
 
index b935ed2922d806725cd6916ce18bd429acf8e926..85e0b0c06718f0e8f1127192bfd65d5a2d4210ce 100644 (file)
@@ -208,3 +208,56 @@ void omap2xxx_clkt_vps_late_init(void)
                clk_put(c);
        }
 }
+
+#ifdef CONFIG_OF
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+
+static const struct clk_ops virt_prcm_set_ops = {
+       .recalc_rate    = &omap2_table_mpu_recalc,
+       .set_rate       = &omap2_select_table_rate,
+       .round_rate     = &omap2_round_to_table_rate,
+};
+
+/**
+ * omap2xxx_clkt_vps_init - initialize virt_prcm_set clock
+ *
+ * Does a manual init for the virtual prcm DVFS clock for OMAP2. This
+ * function is called only from omap2 DT clock init, as the virtual
+ * node is not modelled in the DT clock data.
+ */
+void omap2xxx_clkt_vps_init(void)
+{
+       struct clk_init_data init = { NULL };
+       struct clk_hw_omap *hw = NULL;
+       struct clk *clk;
+       const char *parent_name = "mpu_ck";
+       struct clk_lookup *lookup = NULL;
+
+       omap2xxx_clkt_vps_late_init();
+       omap2xxx_clkt_vps_check_bootloader_rates();
+
+       hw = kzalloc(sizeof(*hw), GFP_KERNEL);
+       lookup = kzalloc(sizeof(*lookup), GFP_KERNEL);
+       if (!hw || !lookup)
+               goto cleanup;
+       init.name = "virt_prcm_set";
+       init.ops = &virt_prcm_set_ops;
+       init.parent_names = &parent_name;
+       init.num_parents = 1;
+
+       hw->hw.init = &init;
+
+       clk = clk_register(NULL, &hw->hw);
+
+       lookup->dev_id = NULL;
+       lookup->con_id = "cpufreq_ck";
+       lookup->clk = clk;
+
+       clkdev_add(lookup);
+       return;
+cleanup:
+       kfree(hw);
+       kfree(lookup);
+}
+#endif
index bda767a9dea862d7223d86d1a59c0b1505e091f9..12f54d428d7c6f3ece2e4c5c6244c8cb9841d194 100644 (file)
@@ -178,17 +178,6 @@ struct clksel {
        const struct clksel_rate *rates;
 };
 
-struct clk_hw_omap_ops {
-       void                    (*find_idlest)(struct clk_hw_omap *oclk,
-                                       void __iomem **idlest_reg,
-                                       u8 *idlest_bit, u8 *idlest_val);
-       void                    (*find_companion)(struct clk_hw_omap *oclk,
-                                       void __iomem **other_reg,
-                                       u8 *other_bit);
-       void                    (*allow_idle)(struct clk_hw_omap *oclk);
-       void                    (*deny_idle)(struct clk_hw_omap *oclk);
-};
-
 unsigned long omap_fixed_divisor_recalc(struct clk_hw *hw,
                                        unsigned long parent_rate);
 
@@ -279,8 +268,6 @@ extern const struct clk_hw_omap_ops clkhwops_omap3430es2_hsotgusb_wait;
 extern const struct clk_hw_omap_ops clkhwops_am35xx_ipss_module_wait;
 extern const struct clk_hw_omap_ops clkhwops_apll54;
 extern const struct clk_hw_omap_ops clkhwops_apll96;
-extern const struct clk_hw_omap_ops clkhwops_omap2xxx_dpll;
-extern const struct clk_hw_omap_ops clkhwops_omap2430_i2chs_wait;
 
 /* clksel_rate blocks shared between OMAP44xx and AM33xx */
 extern const struct clksel_rate div_1_0_rates[];
index 539dc08afbbaf3cb93536f600a5bb930b35d7d55..45f41a4116031be4cbb722bc2db63cc33841c31f 100644 (file)
@@ -21,10 +21,6 @@ unsigned long omap2xxx_sys_clk_recalc(struct clk_hw *clk,
                                      unsigned long parent_rate);
 unsigned long omap2_osc_clk_recalc(struct clk_hw *clk,
                                   unsigned long parent_rate);
-unsigned long omap2_dpllcore_recalc(struct clk_hw *hw,
-                                   unsigned long parent_rate);
-int omap2_reprogram_dpllcore(struct clk_hw *clk, unsigned long rate,
-                            unsigned long parent_rate);
 void omap2xxx_clkt_dpllcore_init(struct clk_hw *hw);
 unsigned long omap2_clk_apll54_recalc(struct clk_hw *hw,
                                      unsigned long parent_rate);
index fcd8036af91041750f529c28f99d665dce86a04f..6d7ba37e225735d49e262723bc6927e0e0b52ed1 100644 (file)
@@ -319,6 +319,15 @@ static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 freqsel)
 
        /* Set DPLL multiplier, divider */
        v = omap2_clk_readl(clk, dd->mult_div1_reg);
+
+       /* Handle Duty Cycle Correction */
+       if (dd->dcc_mask) {
+               if (dd->last_rounded_rate >= dd->dcc_rate)
+                       v |= dd->dcc_mask; /* Enable DCC */
+               else
+                       v &= ~dd->dcc_mask; /* Disable DCC */
+       }
+
        v &= ~(dd->mult_mask | dd->div1_mask);
        v |= dd->last_rounded_m << __ffs(dd->mult_mask);
        v |= (dd->last_rounded_n - 1) << __ffs(dd->div1_mask);
index e4dec9fcb084afe014d8beb09706f946f7b8e734..9c6029ba526fff85005d8195c311138be7074a94 100644 (file)
@@ -23,9 +23,7 @@
 #include "board.h"
 
 static struct rfkill_gpio_platform_data wifi_rfkill_platform_data = {
-       .name           = "wifi_rfkill",
-       .reset_gpio     = 25, /* PD1 */
-       .shutdown_gpio  = 85, /* PK5 */
+       .name   = "wifi_rfkill",
        .type   = RFKILL_TYPE_WLAN,
 };
 
index 6f879c319a9dbe9fc406946565da49a0e99f5eb8..fb5503ce016f0fa693f765f0157a2379422202dd 100644 (file)
@@ -136,7 +136,7 @@ static u16 saved_regs(struct jit_ctx *ctx)
        u16 ret = 0;
 
        if ((ctx->skf->len > 1) ||
-           (ctx->skf->insns[0].code == BPF_S_RET_A))
+           (ctx->skf->insns[0].code == (BPF_RET | BPF_A)))
                ret |= 1 << r_A;
 
 #ifdef CONFIG_FRAME_POINTER
@@ -164,18 +164,10 @@ static inline int mem_words_used(struct jit_ctx *ctx)
 static inline bool is_load_to_a(u16 inst)
 {
        switch (inst) {
-       case BPF_S_LD_W_LEN:
-       case BPF_S_LD_W_ABS:
-       case BPF_S_LD_H_ABS:
-       case BPF_S_LD_B_ABS:
-       case BPF_S_ANC_CPU:
-       case BPF_S_ANC_IFINDEX:
-       case BPF_S_ANC_MARK:
-       case BPF_S_ANC_PROTOCOL:
-       case BPF_S_ANC_RXHASH:
-       case BPF_S_ANC_VLAN_TAG:
-       case BPF_S_ANC_VLAN_TAG_PRESENT:
-       case BPF_S_ANC_QUEUE:
+       case BPF_LD | BPF_W | BPF_LEN:
+       case BPF_LD | BPF_W | BPF_ABS:
+       case BPF_LD | BPF_H | BPF_ABS:
+       case BPF_LD | BPF_B | BPF_ABS:
                return true;
        default:
                return false;
@@ -215,7 +207,7 @@ static void build_prologue(struct jit_ctx *ctx)
                emit(ARM_MOV_I(r_X, 0), ctx);
 
        /* do not leak kernel data to userspace */
-       if ((first_inst != BPF_S_RET_K) && !(is_load_to_a(first_inst)))
+       if ((first_inst != (BPF_RET | BPF_K)) && !(is_load_to_a(first_inst)))
                emit(ARM_MOV_I(r_A, 0), ctx);
 
        /* stack space for the BPF_MEM words */
@@ -480,36 +472,39 @@ static int build_body(struct jit_ctx *ctx)
        u32 k;
 
        for (i = 0; i < prog->len; i++) {
+               u16 code;
+
                inst = &(prog->insns[i]);
                /* K as an immediate value operand */
                k = inst->k;
+               code = bpf_anc_helper(inst);
 
                /* compute offsets only in the fake pass */
                if (ctx->target == NULL)
                        ctx->offsets[i] = ctx->idx * 4;
 
-               switch (inst->code) {
-               case BPF_S_LD_IMM:
+               switch (code) {
+               case BPF_LD | BPF_IMM:
                        emit_mov_i(r_A, k, ctx);
                        break;
-               case BPF_S_LD_W_LEN:
+               case BPF_LD | BPF_W | BPF_LEN:
                        ctx->seen |= SEEN_SKB;
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4);
                        emit(ARM_LDR_I(r_A, r_skb,
                                       offsetof(struct sk_buff, len)), ctx);
                        break;
-               case BPF_S_LD_MEM:
+               case BPF_LD | BPF_MEM:
                        /* A = scratch[k] */
                        ctx->seen |= SEEN_MEM_WORD(k);
                        emit(ARM_LDR_I(r_A, ARM_SP, SCRATCH_OFF(k)), ctx);
                        break;
-               case BPF_S_LD_W_ABS:
+               case BPF_LD | BPF_W | BPF_ABS:
                        load_order = 2;
                        goto load;
-               case BPF_S_LD_H_ABS:
+               case BPF_LD | BPF_H | BPF_ABS:
                        load_order = 1;
                        goto load;
-               case BPF_S_LD_B_ABS:
+               case BPF_LD | BPF_B | BPF_ABS:
                        load_order = 0;
 load:
                        /* the interpreter will deal with the negative K */
@@ -552,31 +547,31 @@ load_common:
                        emit_err_ret(ARM_COND_NE, ctx);
                        emit(ARM_MOV_R(r_A, ARM_R0), ctx);
                        break;
-               case BPF_S_LD_W_IND:
+               case BPF_LD | BPF_W | BPF_IND:
                        load_order = 2;
                        goto load_ind;
-               case BPF_S_LD_H_IND:
+               case BPF_LD | BPF_H | BPF_IND:
                        load_order = 1;
                        goto load_ind;
-               case BPF_S_LD_B_IND:
+               case BPF_LD | BPF_B | BPF_IND:
                        load_order = 0;
 load_ind:
                        OP_IMM3(ARM_ADD, r_off, r_X, k, ctx);
                        goto load_common;
-               case BPF_S_LDX_IMM:
+               case BPF_LDX | BPF_IMM:
                        ctx->seen |= SEEN_X;
                        emit_mov_i(r_X, k, ctx);
                        break;
-               case BPF_S_LDX_W_LEN:
+               case BPF_LDX | BPF_W | BPF_LEN:
                        ctx->seen |= SEEN_X | SEEN_SKB;
                        emit(ARM_LDR_I(r_X, r_skb,
                                       offsetof(struct sk_buff, len)), ctx);
                        break;
-               case BPF_S_LDX_MEM:
+               case BPF_LDX | BPF_MEM:
                        ctx->seen |= SEEN_X | SEEN_MEM_WORD(k);
                        emit(ARM_LDR_I(r_X, ARM_SP, SCRATCH_OFF(k)), ctx);
                        break;
-               case BPF_S_LDX_B_MSH:
+               case BPF_LDX | BPF_B | BPF_MSH:
                        /* x = ((*(frame + k)) & 0xf) << 2; */
                        ctx->seen |= SEEN_X | SEEN_DATA | SEEN_CALL;
                        /* the interpreter should deal with the negative K */
@@ -606,113 +601,113 @@ load_ind:
                        emit(ARM_AND_I(r_X, ARM_R0, 0x00f), ctx);
                        emit(ARM_LSL_I(r_X, r_X, 2), ctx);
                        break;
-               case BPF_S_ST:
+               case BPF_ST:
                        ctx->seen |= SEEN_MEM_WORD(k);
                        emit(ARM_STR_I(r_A, ARM_SP, SCRATCH_OFF(k)), ctx);
                        break;
-               case BPF_S_STX:
+               case BPF_STX:
                        update_on_xread(ctx);
                        ctx->seen |= SEEN_MEM_WORD(k);
                        emit(ARM_STR_I(r_X, ARM_SP, SCRATCH_OFF(k)), ctx);
                        break;
-               case BPF_S_ALU_ADD_K:
+               case BPF_ALU | BPF_ADD | BPF_K:
                        /* A += K */
                        OP_IMM3(ARM_ADD, r_A, r_A, k, ctx);
                        break;
-               case BPF_S_ALU_ADD_X:
+               case BPF_ALU | BPF_ADD | BPF_X:
                        update_on_xread(ctx);
                        emit(ARM_ADD_R(r_A, r_A, r_X), ctx);
                        break;
-               case BPF_S_ALU_SUB_K:
+               case BPF_ALU | BPF_SUB | BPF_K:
                        /* A -= K */
                        OP_IMM3(ARM_SUB, r_A, r_A, k, ctx);
                        break;
-               case BPF_S_ALU_SUB_X:
+               case BPF_ALU | BPF_SUB | BPF_X:
                        update_on_xread(ctx);
                        emit(ARM_SUB_R(r_A, r_A, r_X), ctx);
                        break;
-               case BPF_S_ALU_MUL_K:
+               case BPF_ALU | BPF_MUL | BPF_K:
                        /* A *= K */
                        emit_mov_i(r_scratch, k, ctx);
                        emit(ARM_MUL(r_A, r_A, r_scratch), ctx);
                        break;
-               case BPF_S_ALU_MUL_X:
+               case BPF_ALU | BPF_MUL | BPF_X:
                        update_on_xread(ctx);
                        emit(ARM_MUL(r_A, r_A, r_X), ctx);
                        break;
-               case BPF_S_ALU_DIV_K:
+               case BPF_ALU | BPF_DIV | BPF_K:
                        if (k == 1)
                                break;
                        emit_mov_i(r_scratch, k, ctx);
                        emit_udiv(r_A, r_A, r_scratch, ctx);
                        break;
-               case BPF_S_ALU_DIV_X:
+               case BPF_ALU | BPF_DIV | BPF_X:
                        update_on_xread(ctx);
                        emit(ARM_CMP_I(r_X, 0), ctx);
                        emit_err_ret(ARM_COND_EQ, ctx);
                        emit_udiv(r_A, r_A, r_X, ctx);
                        break;
-               case BPF_S_ALU_OR_K:
+               case BPF_ALU | BPF_OR | BPF_K:
                        /* A |= K */
                        OP_IMM3(ARM_ORR, r_A, r_A, k, ctx);
                        break;
-               case BPF_S_ALU_OR_X:
+               case BPF_ALU | BPF_OR | BPF_X:
                        update_on_xread(ctx);
                        emit(ARM_ORR_R(r_A, r_A, r_X), ctx);
                        break;
-               case BPF_S_ALU_XOR_K:
+               case BPF_ALU | BPF_XOR | BPF_K:
                        /* A ^= K; */
                        OP_IMM3(ARM_EOR, r_A, r_A, k, ctx);
                        break;
-               case BPF_S_ANC_ALU_XOR_X:
-               case BPF_S_ALU_XOR_X:
+               case BPF_ANC | SKF_AD_ALU_XOR_X:
+               case BPF_ALU | BPF_XOR | BPF_X:
                        /* A ^= X */
                        update_on_xread(ctx);
                        emit(ARM_EOR_R(r_A, r_A, r_X), ctx);
                        break;
-               case BPF_S_ALU_AND_K:
+               case BPF_ALU | BPF_AND | BPF_K:
                        /* A &= K */
                        OP_IMM3(ARM_AND, r_A, r_A, k, ctx);
                        break;
-               case BPF_S_ALU_AND_X:
+               case BPF_ALU | BPF_AND | BPF_X:
                        update_on_xread(ctx);
                        emit(ARM_AND_R(r_A, r_A, r_X), ctx);
                        break;
-               case BPF_S_ALU_LSH_K:
+               case BPF_ALU | BPF_LSH | BPF_K:
                        if (unlikely(k > 31))
                                return -1;
                        emit(ARM_LSL_I(r_A, r_A, k), ctx);
                        break;
-               case BPF_S_ALU_LSH_X:
+               case BPF_ALU | BPF_LSH | BPF_X:
                        update_on_xread(ctx);
                        emit(ARM_LSL_R(r_A, r_A, r_X), ctx);
                        break;
-               case BPF_S_ALU_RSH_K:
+               case BPF_ALU | BPF_RSH | BPF_K:
                        if (unlikely(k > 31))
                                return -1;
                        emit(ARM_LSR_I(r_A, r_A, k), ctx);
                        break;
-               case BPF_S_ALU_RSH_X:
+               case BPF_ALU | BPF_RSH | BPF_X:
                        update_on_xread(ctx);
                        emit(ARM_LSR_R(r_A, r_A, r_X), ctx);
                        break;
-               case BPF_S_ALU_NEG:
+               case BPF_ALU | BPF_NEG:
                        /* A = -A */
                        emit(ARM_RSB_I(r_A, r_A, 0), ctx);
                        break;
-               case BPF_S_JMP_JA:
+               case BPF_JMP | BPF_JA:
                        /* pc += K */
                        emit(ARM_B(b_imm(i + k + 1, ctx)), ctx);
                        break;
-               case BPF_S_JMP_JEQ_K:
+               case BPF_JMP | BPF_JEQ | BPF_K:
                        /* pc += (A == K) ? pc->jt : pc->jf */
                        condt  = ARM_COND_EQ;
                        goto cmp_imm;
-               case BPF_S_JMP_JGT_K:
+               case BPF_JMP | BPF_JGT | BPF_K:
                        /* pc += (A > K) ? pc->jt : pc->jf */
                        condt  = ARM_COND_HI;
                        goto cmp_imm;
-               case BPF_S_JMP_JGE_K:
+               case BPF_JMP | BPF_JGE | BPF_K:
                        /* pc += (A >= K) ? pc->jt : pc->jf */
                        condt  = ARM_COND_HS;
 cmp_imm:
@@ -731,22 +726,22 @@ cond_jump:
                                _emit(condt ^ 1, ARM_B(b_imm(i + inst->jf + 1,
                                                             ctx)), ctx);
                        break;
-               case BPF_S_JMP_JEQ_X:
+               case BPF_JMP | BPF_JEQ | BPF_X:
                        /* pc += (A == X) ? pc->jt : pc->jf */
                        condt   = ARM_COND_EQ;
                        goto cmp_x;
-               case BPF_S_JMP_JGT_X:
+               case BPF_JMP | BPF_JGT | BPF_X:
                        /* pc += (A > X) ? pc->jt : pc->jf */
                        condt   = ARM_COND_HI;
                        goto cmp_x;
-               case BPF_S_JMP_JGE_X:
+               case BPF_JMP | BPF_JGE | BPF_X:
                        /* pc += (A >= X) ? pc->jt : pc->jf */
                        condt   = ARM_COND_CS;
 cmp_x:
                        update_on_xread(ctx);
                        emit(ARM_CMP_R(r_A, r_X), ctx);
                        goto cond_jump;
-               case BPF_S_JMP_JSET_K:
+               case BPF_JMP | BPF_JSET | BPF_K:
                        /* pc += (A & K) ? pc->jt : pc->jf */
                        condt  = ARM_COND_NE;
                        /* not set iff all zeroes iff Z==1 iff EQ */
@@ -759,16 +754,16 @@ cmp_x:
                                emit(ARM_TST_I(r_A, imm12), ctx);
                        }
                        goto cond_jump;
-               case BPF_S_JMP_JSET_X:
+               case BPF_JMP | BPF_JSET | BPF_X:
                        /* pc += (A & X) ? pc->jt : pc->jf */
                        update_on_xread(ctx);
                        condt  = ARM_COND_NE;
                        emit(ARM_TST_R(r_A, r_X), ctx);
                        goto cond_jump;
-               case BPF_S_RET_A:
+               case BPF_RET | BPF_A:
                        emit(ARM_MOV_R(ARM_R0, r_A), ctx);
                        goto b_epilogue;
-               case BPF_S_RET_K:
+               case BPF_RET | BPF_K:
                        if ((k == 0) && (ctx->ret0_fp_idx < 0))
                                ctx->ret0_fp_idx = i;
                        emit_mov_i(ARM_R0, k, ctx);
@@ -776,17 +771,17 @@ b_epilogue:
                        if (i != ctx->skf->len - 1)
                                emit(ARM_B(b_imm(prog->len, ctx)), ctx);
                        break;
-               case BPF_S_MISC_TAX:
+               case BPF_MISC | BPF_TAX:
                        /* X = A */
                        ctx->seen |= SEEN_X;
                        emit(ARM_MOV_R(r_X, r_A), ctx);
                        break;
-               case BPF_S_MISC_TXA:
+               case BPF_MISC | BPF_TXA:
                        /* A = X */
                        update_on_xread(ctx);
                        emit(ARM_MOV_R(r_A, r_X), ctx);
                        break;
-               case BPF_S_ANC_PROTOCOL:
+               case BPF_ANC | SKF_AD_PROTOCOL:
                        /* A = ntohs(skb->protocol) */
                        ctx->seen |= SEEN_SKB;
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff,
@@ -795,7 +790,7 @@ b_epilogue:
                        emit(ARM_LDRH_I(r_scratch, r_skb, off), ctx);
                        emit_swap16(r_A, r_scratch, ctx);
                        break;
-               case BPF_S_ANC_CPU:
+               case BPF_ANC | SKF_AD_CPU:
                        /* r_scratch = current_thread_info() */
                        OP_IMM3(ARM_BIC, r_scratch, ARM_SP, THREAD_SIZE - 1, ctx);
                        /* A = current_thread_info()->cpu */
@@ -803,7 +798,7 @@ b_epilogue:
                        off = offsetof(struct thread_info, cpu);
                        emit(ARM_LDR_I(r_A, r_scratch, off), ctx);
                        break;
-               case BPF_S_ANC_IFINDEX:
+               case BPF_ANC | SKF_AD_IFINDEX:
                        /* A = skb->dev->ifindex */
                        ctx->seen |= SEEN_SKB;
                        off = offsetof(struct sk_buff, dev);
@@ -817,30 +812,30 @@ b_epilogue:
                        off = offsetof(struct net_device, ifindex);
                        emit(ARM_LDR_I(r_A, r_scratch, off), ctx);
                        break;
-               case BPF_S_ANC_MARK:
+               case BPF_ANC | SKF_AD_MARK:
                        ctx->seen |= SEEN_SKB;
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4);
                        off = offsetof(struct sk_buff, mark);
                        emit(ARM_LDR_I(r_A, r_skb, off), ctx);
                        break;
-               case BPF_S_ANC_RXHASH:
+               case BPF_ANC | SKF_AD_RXHASH:
                        ctx->seen |= SEEN_SKB;
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4);
                        off = offsetof(struct sk_buff, hash);
                        emit(ARM_LDR_I(r_A, r_skb, off), ctx);
                        break;
-               case BPF_S_ANC_VLAN_TAG:
-               case BPF_S_ANC_VLAN_TAG_PRESENT:
+               case BPF_ANC | SKF_AD_VLAN_TAG:
+               case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
                        ctx->seen |= SEEN_SKB;
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2);
                        off = offsetof(struct sk_buff, vlan_tci);
                        emit(ARM_LDRH_I(r_A, r_skb, off), ctx);
-                       if (inst->code == BPF_S_ANC_VLAN_TAG)
+                       if (code == (BPF_ANC | SKF_AD_VLAN_TAG))
                                OP_IMM3(ARM_AND, r_A, r_A, VLAN_VID_MASK, ctx);
                        else
                                OP_IMM3(ARM_AND, r_A, r_A, VLAN_TAG_PRESENT, ctx);
                        break;
-               case BPF_S_ANC_QUEUE:
+               case BPF_ANC | SKF_AD_QUEUE:
                        ctx->seen |= SEEN_SKB;
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff,
                                                  queue_mapping) != 2);
index 1759fad540178705f0ff77940655d47569f18b93..e66ba31ef84de15520ded3e787fd1b7b91af4425 100644 (file)
@@ -53,7 +53,6 @@ CONFIG_IP_PNP=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 # CONFIG_FW_LOADER is not set
 CONFIG_MTD=y
-CONFIG_MTD_CHAR=y
 CONFIG_MTD_BLOCK=y
 CONFIG_MTD_CFI=y
 CONFIG_MTD_CFI_INTELEXT=y
@@ -63,6 +62,7 @@ CONFIG_MTD_COMPLEX_MAPPINGS=y
 CONFIG_MTD_PHYSMAP=y
 CONFIG_MTD_M25P80=y
 CONFIG_MTD_NAND=m
+CONFIG_MTD_SPI_NOR=y
 CONFIG_BLK_DEV_RAM=y
 CONFIG_SCSI=y
 # CONFIG_SCSI_PROC_FS is not set
index 357729682c0064edf2d908093037506a9dd31669..0207c588c19f3cad7d91b4dff2b84555474034f9 100644 (file)
@@ -58,7 +58,6 @@ CONFIG_BFIN_SIR0=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 # CONFIG_FW_LOADER is not set
 CONFIG_MTD=y
-CONFIG_MTD_CHAR=m
 CONFIG_MTD_BLOCK=y
 CONFIG_MTD_JEDECPROBE=m
 CONFIG_MTD_RAM=y
@@ -66,6 +65,7 @@ CONFIG_MTD_ROM=m
 CONFIG_MTD_COMPLEX_MAPPINGS=y
 CONFIG_MTD_M25P80=y
 CONFIG_MTD_NAND=m
+CONFIG_MTD_SPI_NOR=y
 CONFIG_BLK_DEV_RAM=y
 CONFIG_SCSI=y
 # CONFIG_SCSI_PROC_FS is not set
index 2e73a5d33da88f294f12d035a2f974917e8d3845..99c131ba7d908b557d85ebb57a0d0e0b6cf409b7 100644 (file)
@@ -57,7 +57,6 @@ CONFIG_BFIN_SIR0=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 # CONFIG_FW_LOADER is not set
 CONFIG_MTD=y
-CONFIG_MTD_CHAR=m
 CONFIG_MTD_BLOCK=y
 CONFIG_MTD_JEDECPROBE=m
 CONFIG_MTD_RAM=y
@@ -65,6 +64,7 @@ CONFIG_MTD_ROM=m
 CONFIG_MTD_COMPLEX_MAPPINGS=y
 CONFIG_MTD_M25P80=y
 CONFIG_MTD_NAND=m
+CONFIG_MTD_SPI_NOR=y
 CONFIG_BLK_DEV_RAM=y
 CONFIG_SCSI=y
 # CONFIG_SCSI_PROC_FS is not set
index f0a2ddf5de468d0a96f1fbc716b3e37915c377f8..38cb17d218d46f04f1b677beb748438e37cf65ea 100644 (file)
@@ -64,7 +64,6 @@ CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 CONFIG_FW_LOADER=m
 CONFIG_MTD=y
 CONFIG_MTD_CMDLINE_PARTS=y
-CONFIG_MTD_CHAR=y
 CONFIG_MTD_BLOCK=y
 CONFIG_MTD_CFI=y
 CONFIG_MTD_CFI_INTELEXT=y
@@ -75,6 +74,7 @@ CONFIG_MTD_M25P80=y
 CONFIG_MTD_NAND=y
 CONFIG_MTD_NAND_BF5XX=y
 # CONFIG_MTD_NAND_BF5XX_HWECC is not set
+CONFIG_MTD_SPI_NOR=y
 CONFIG_BLK_DEV_RAM=y
 # CONFIG_SCSI_PROC_FS is not set
 CONFIG_BLK_DEV_SD=y
index 4ca39ab6b2bf98c12b260fef822473ae75820c2f..a7e9bfd84183d5b7c26924679f9bb33d88ac0118 100644 (file)
@@ -57,7 +57,6 @@ CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 CONFIG_FW_LOADER=m
 CONFIG_MTD=y
 CONFIG_MTD_CMDLINE_PARTS=y
-CONFIG_MTD_CHAR=y
 CONFIG_MTD_BLOCK=y
 CONFIG_MTD_CFI=y
 CONFIG_MTD_CFI_INTELEXT=y
@@ -65,6 +64,7 @@ CONFIG_MTD_CFI_STAA=y
 CONFIG_MTD_COMPLEX_MAPPINGS=y
 CONFIG_MTD_PHYSMAP=y
 CONFIG_MTD_M25P80=y
+CONFIG_MTD_SPI_NOR=y
 CONFIG_MTD_UBI=m
 CONFIG_SCSI=y
 CONFIG_BLK_DEV_SD=y
index 3853c473b443193dac482e2994c1d131328a80fe..f4a9200e1ab1526e44786e0b982168aef734bf57 100644 (file)
@@ -45,7 +45,6 @@ CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 # CONFIG_FW_LOADER is not set
 CONFIG_MTD=y
 CONFIG_MTD_CMDLINE_PARTS=y
-CONFIG_MTD_CHAR=m
 CONFIG_MTD_BLOCK=y
 CONFIG_MTD_CFI=m
 CONFIG_MTD_CFI_AMDSTD=m
@@ -53,7 +52,7 @@ CONFIG_MTD_RAM=y
 CONFIG_MTD_ROM=m
 CONFIG_MTD_COMPLEX_MAPPINGS=y
 CONFIG_MTD_M25P80=y
-# CONFIG_M25PXX_USE_FAST_READ is not set
+CONFIG_MTD_SPI_NOR=y
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_BLK_DEV_NBD=y
 CONFIG_BLK_DEV_RAM=y
index f754e490bbfd00a570995da744290cfa0db1d894..0ff97d8d047a5f685e92305d93c51692c877097d 100644 (file)
@@ -36,13 +36,12 @@ CONFIG_IRTTY_SIR=m
 # CONFIG_WIRELESS is not set
 # CONFIG_FW_LOADER is not set
 CONFIG_MTD=y
-CONFIG_MTD_CHAR=y
 CONFIG_MTD_BLOCK=y
 CONFIG_MTD_RAM=y
 CONFIG_MTD_ROM=y
 CONFIG_MTD_COMPLEX_MAPPINGS=y
 CONFIG_MTD_M25P80=y
-# CONFIG_M25PXX_USE_FAST_READ is not set
+CONFIG_MTD_SPI_NOR=y
 CONFIG_BLK_DEV_RAM=y
 CONFIG_MISC_DEVICES=y
 CONFIG_EEPROM_AT25=y
index 8d1e4c2d2c36d4f2944c8e7ea4d40050da4b530a..40e9c2bbc6e37f26ef8030316556a16c05c0d776 100644 (file)
@@ -316,6 +316,8 @@ static inline void disable_dma(unsigned int channel)
 }
 static inline void enable_dma(unsigned int channel)
 {
+       dma_ch[channel].regs->curr_x_count = 0;
+       dma_ch[channel].regs->curr_y_count = 0;
        dma_ch[channel].regs->cfg |= DMAEN;
 }
 int set_dma_callback(unsigned int channel, irq_handler_t callback, void *data);
index d0989290f54cc663dca045455fdaf9cc7f461807..6f4bac969bf72e360a6476c55a9e7f667d2d21fb 100644 (file)
@@ -17,6 +17,7 @@
 #if IS_ENABLED(CONFIG_USB_ISP1362_HCD)
 #include <linux/usb/isp1362.h>
 #endif
+#include <linux/gpio.h>
 #include <linux/irq.h>
 #include <linux/i2c.h>
 #include <asm/dma.h>
index a8b5408dd3495f48a19a7ad35ddca3b5724d6005..da4cdb16844ebcca13adff525d5e486a4f9367a6 100644 (file)
@@ -168,6 +168,7 @@ static void nvram_read_alpha2(const char *prefix, const char *name,
 static void bcm47xx_fill_sprom_r1234589(struct ssb_sprom *sprom,
                                        const char *prefix, bool fallback)
 {
+       nvram_read_u16(prefix, NULL, "devid", &sprom->dev_id, 0, fallback);
        nvram_read_u8(prefix, NULL, "ledbh0", &sprom->gpio0, 0xff, fallback);
        nvram_read_u8(prefix, NULL, "ledbh1", &sprom->gpio1, 0xff, fallback);
        nvram_read_u8(prefix, NULL, "ledbh2", &sprom->gpio2, 0xff, fallback);
index 21c9f304e96c50b7945ae4571a3fdf0363d924ce..790352f937004fde97c2430c67d0823fc382d808 100644 (file)
@@ -235,11 +235,6 @@ config PPC_EARLY_DEBUG_USBGECKO
          Select this to enable early debugging for Nintendo GameCube/Wii
          consoles via an external USB Gecko adapter.
 
-config PPC_EARLY_DEBUG_WSP
-       bool "Early debugging via WSP's internal UART"
-       depends on PPC_WSP
-       select PPC_UDBG_16550
-
 config PPC_EARLY_DEBUG_PS3GELIC
        bool "Early debugging through the PS3 Ethernet port"
        depends on PPC_PS3
index 426dce7ae7c4974db360c15bd6b6c9257d6564d1..ccc25eddbcb8b2ff8404d66cecbaec1e450f5a51 100644 (file)
@@ -333,8 +333,8 @@ $(addprefix $(obj)/, $(initrd-y)): $(obj)/ramdisk.image.gz
 $(obj)/zImage.initrd.%: vmlinux $(wrapperbits)
        $(call if_changed,wrap,$*,,,$(obj)/ramdisk.image.gz)
 
-$(obj)/zImage.%: vmlinux $(wrapperbits)
-       $(call if_changed,wrap,$*)
+$(addprefix $(obj)/, $(sort $(filter zImage.%, $(image-y)))): vmlinux $(wrapperbits)
+       $(call if_changed,wrap,$(subst $(obj)/zImage.,,$@))
 
 # dtbImage% - a dtbImage is a zImage with an embedded device tree blob
 $(obj)/dtbImage.initrd.%: vmlinux $(wrapperbits) $(obj)/%.dtb
diff --git a/arch/powerpc/configs/chroma_defconfig b/arch/powerpc/configs/chroma_defconfig
deleted file mode 100644 (file)
index 4f35fc4..0000000
+++ /dev/null
@@ -1,307 +0,0 @@
-CONFIG_PPC64=y
-CONFIG_PPC_BOOK3E_64=y
-# CONFIG_VIRT_CPU_ACCOUNTING_NATIVE is not set
-CONFIG_SMP=y
-CONFIG_NR_CPUS=256
-CONFIG_EXPERIMENTAL=y
-CONFIG_SYSVIPC=y
-CONFIG_POSIX_MQUEUE=y
-CONFIG_BSD_PROCESS_ACCT=y
-CONFIG_TASKSTATS=y
-CONFIG_TASK_DELAY_ACCT=y
-CONFIG_TASK_XACCT=y
-CONFIG_TASK_IO_ACCOUNTING=y
-CONFIG_AUDIT=y
-CONFIG_AUDITSYSCALL=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_LOG_BUF_SHIFT=19
-CONFIG_CGROUPS=y
-CONFIG_CGROUP_DEVICE=y
-CONFIG_CPUSETS=y
-CONFIG_CGROUP_CPUACCT=y
-CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEMCG=y
-CONFIG_CGROUP_MEMCG_SWAP=y
-CONFIG_NAMESPACES=y
-CONFIG_RELAY=y
-CONFIG_BLK_DEV_INITRD=y
-CONFIG_INITRAMFS_SOURCE=""
-CONFIG_RD_BZIP2=y
-CONFIG_RD_LZMA=y
-CONFIG_INITRAMFS_COMPRESSION_GZIP=y
-CONFIG_KALLSYMS_ALL=y
-CONFIG_EMBEDDED=y
-CONFIG_PERF_EVENTS=y
-CONFIG_PROFILING=y
-CONFIG_OPROFILE=y
-CONFIG_KPROBES=y
-CONFIG_MODULES=y
-CONFIG_MODULE_FORCE_LOAD=y
-CONFIG_MODULE_UNLOAD=y
-CONFIG_MODULE_FORCE_UNLOAD=y
-CONFIG_MODVERSIONS=y
-CONFIG_MODULE_SRCVERSION_ALL=y
-CONFIG_SCOM_DEBUGFS=y
-CONFIG_PPC_A2_DD2=y
-CONFIG_KVM_GUEST=y
-CONFIG_NO_HZ=y
-CONFIG_HIGH_RES_TIMERS=y
-CONFIG_HZ_100=y
-# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-CONFIG_BINFMT_MISC=y
-CONFIG_NUMA=y
-# CONFIG_MIGRATION is not set
-CONFIG_PPC_64K_PAGES=y
-CONFIG_SCHED_SMT=y
-CONFIG_CMDLINE_BOOL=y
-CONFIG_CMDLINE=""
-# CONFIG_SECCOMP is not set
-CONFIG_PCIEPORTBUS=y
-# CONFIG_PCIEASPM is not set
-CONFIG_PCI_MSI=y
-CONFIG_PACKET=y
-CONFIG_UNIX=y
-CONFIG_XFRM_USER=m
-CONFIG_XFRM_SUB_POLICY=y
-CONFIG_XFRM_STATISTICS=y
-CONFIG_NET_KEY=m
-CONFIG_NET_KEY_MIGRATE=y
-CONFIG_INET=y
-CONFIG_IP_MULTICAST=y
-CONFIG_IP_ADVANCED_ROUTER=y
-CONFIG_IP_ROUTE_MULTIPATH=y
-CONFIG_IP_ROUTE_VERBOSE=y
-CONFIG_IP_PNP=y
-CONFIG_IP_PNP_DHCP=y
-CONFIG_IP_PNP_BOOTP=y
-CONFIG_NET_IPIP=y
-CONFIG_IP_MROUTE=y
-CONFIG_IP_PIMSM_V1=y
-CONFIG_IP_PIMSM_V2=y
-CONFIG_SYN_COOKIES=y
-CONFIG_INET_AH=m
-CONFIG_INET_ESP=m
-CONFIG_INET_IPCOMP=m
-CONFIG_IPV6=y
-CONFIG_IPV6_PRIVACY=y
-CONFIG_IPV6_ROUTER_PREF=y
-CONFIG_IPV6_ROUTE_INFO=y
-CONFIG_IPV6_OPTIMISTIC_DAD=y
-CONFIG_INET6_AH=y
-CONFIG_INET6_ESP=y
-CONFIG_INET6_IPCOMP=y
-CONFIG_IPV6_MIP6=y
-CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=y
-CONFIG_IPV6_TUNNEL=y
-CONFIG_IPV6_MULTIPLE_TABLES=y
-CONFIG_IPV6_SUBTREES=y
-CONFIG_IPV6_MROUTE=y
-CONFIG_IPV6_PIMSM_V2=y
-CONFIG_NETFILTER=y
-CONFIG_NF_CONNTRACK=m
-CONFIG_NF_CONNTRACK_EVENTS=y
-CONFIG_NF_CT_PROTO_UDPLITE=m
-CONFIG_NF_CONNTRACK_FTP=m
-CONFIG_NF_CONNTRACK_IRC=m
-CONFIG_NF_CONNTRACK_TFTP=m
-CONFIG_NF_CT_NETLINK=m
-CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
-CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
-CONFIG_NETFILTER_XT_TARGET_MARK=m
-CONFIG_NETFILTER_XT_TARGET_NFLOG=m
-CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
-CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
-CONFIG_NETFILTER_XT_MATCH_COMMENT=m
-CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
-CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m
-CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
-CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
-CONFIG_NETFILTER_XT_MATCH_DCCP=m
-CONFIG_NETFILTER_XT_MATCH_DSCP=m
-CONFIG_NETFILTER_XT_MATCH_ESP=m
-CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m
-CONFIG_NETFILTER_XT_MATCH_HELPER=m
-CONFIG_NETFILTER_XT_MATCH_IPRANGE=m
-CONFIG_NETFILTER_XT_MATCH_LENGTH=m
-CONFIG_NETFILTER_XT_MATCH_LIMIT=m
-CONFIG_NETFILTER_XT_MATCH_MAC=m
-CONFIG_NETFILTER_XT_MATCH_MARK=m
-CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
-CONFIG_NETFILTER_XT_MATCH_OWNER=m
-CONFIG_NETFILTER_XT_MATCH_POLICY=m
-CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
-CONFIG_NETFILTER_XT_MATCH_QUOTA=m
-CONFIG_NETFILTER_XT_MATCH_RATEEST=m
-CONFIG_NETFILTER_XT_MATCH_REALM=m
-CONFIG_NETFILTER_XT_MATCH_RECENT=m
-CONFIG_NETFILTER_XT_MATCH_SCTP=m
-CONFIG_NETFILTER_XT_MATCH_STATE=m
-CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
-CONFIG_NETFILTER_XT_MATCH_STRING=m
-CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
-CONFIG_NETFILTER_XT_MATCH_TIME=m
-CONFIG_NETFILTER_XT_MATCH_U32=m
-CONFIG_NF_CONNTRACK_IPV4=m
-CONFIG_IP_NF_QUEUE=m
-CONFIG_IP_NF_IPTABLES=m
-CONFIG_IP_NF_MATCH_AH=m
-CONFIG_IP_NF_MATCH_ECN=m
-CONFIG_IP_NF_MATCH_TTL=m
-CONFIG_IP_NF_FILTER=m
-CONFIG_IP_NF_TARGET_REJECT=m
-CONFIG_IP_NF_TARGET_LOG=m
-CONFIG_IP_NF_TARGET_ULOG=m
-CONFIG_NF_NAT=m
-CONFIG_IP_NF_TARGET_MASQUERADE=m
-CONFIG_IP_NF_TARGET_NETMAP=m
-CONFIG_IP_NF_TARGET_REDIRECT=m
-CONFIG_NET_TCPPROBE=y
-# CONFIG_WIRELESS is not set
-CONFIG_NET_9P=y
-CONFIG_NET_9P_DEBUG=y
-CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
-CONFIG_DEVTMPFS=y
-CONFIG_MTD=y
-CONFIG_MTD_CHAR=y
-CONFIG_MTD_BLOCK=y
-CONFIG_MTD_CFI=y
-CONFIG_MTD_CFI_ADV_OPTIONS=y
-CONFIG_MTD_CFI_LE_BYTE_SWAP=y
-CONFIG_MTD_CFI_INTELEXT=y
-CONFIG_MTD_CFI_AMDSTD=y
-CONFIG_MTD_CFI_STAA=y
-CONFIG_MTD_PHYSMAP_OF=y
-CONFIG_PROC_DEVICETREE=y
-CONFIG_BLK_DEV_LOOP=y
-CONFIG_BLK_DEV_CRYPTOLOOP=y
-CONFIG_BLK_DEV_NBD=m
-CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_RAM_SIZE=65536
-CONFIG_CDROM_PKTCDVD=y
-CONFIG_MISC_DEVICES=y
-CONFIG_BLK_DEV_SD=y
-CONFIG_BLK_DEV_SR=y
-CONFIG_BLK_DEV_SR_VENDOR=y
-CONFIG_CHR_DEV_SG=y
-CONFIG_SCSI_MULTI_LUN=y
-CONFIG_SCSI_CONSTANTS=y
-CONFIG_SCSI_SPI_ATTRS=y
-CONFIG_SCSI_FC_ATTRS=y
-CONFIG_SCSI_ISCSI_ATTRS=m
-CONFIG_SCSI_SAS_ATTRS=m
-CONFIG_SCSI_SRP_ATTRS=y
-CONFIG_ATA=y
-CONFIG_SATA_AHCI=y
-CONFIG_SATA_SIL24=y
-CONFIG_SATA_MV=y
-CONFIG_SATA_SIL=y
-CONFIG_PATA_CMD64X=y
-CONFIG_PATA_MARVELL=y
-CONFIG_PATA_SIL680=y
-CONFIG_MD=y
-CONFIG_BLK_DEV_MD=y
-CONFIG_MD_LINEAR=y
-CONFIG_BLK_DEV_DM=y
-CONFIG_DM_CRYPT=y
-CONFIG_DM_SNAPSHOT=y
-CONFIG_DM_MIRROR=y
-CONFIG_DM_ZERO=y
-CONFIG_DM_UEVENT=y
-CONFIG_NETDEVICES=y
-CONFIG_TUN=y
-CONFIG_E1000E=y
-CONFIG_TIGON3=y
-# CONFIG_WLAN is not set
-# CONFIG_INPUT is not set
-# CONFIG_SERIO is not set
-# CONFIG_VT is not set
-CONFIG_DEVPTS_MULTIPLE_INSTANCES=y
-CONFIG_SERIAL_8250=y
-CONFIG_SERIAL_8250_CONSOLE=y
-CONFIG_HW_RANDOM=y
-CONFIG_RAW_DRIVER=y
-CONFIG_MAX_RAW_DEVS=1024
-# CONFIG_HWMON is not set
-# CONFIG_VGA_ARB is not set
-# CONFIG_USB_SUPPORT is not set
-CONFIG_EDAC=y
-CONFIG_EDAC_MM_EDAC=y
-CONFIG_RTC_CLASS=y
-CONFIG_RTC_DRV_DS1511=y
-CONFIG_RTC_DRV_DS1553=y
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT2_FS_POSIX_ACL=y
-CONFIG_EXT2_FS_SECURITY=y
-CONFIG_EXT2_FS_XIP=y
-CONFIG_EXT3_FS=y
-# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
-CONFIG_EXT3_FS_POSIX_ACL=y
-CONFIG_EXT3_FS_SECURITY=y
-CONFIG_EXT4_FS=y
-# CONFIG_DNOTIFY is not set
-CONFIG_FUSE_FS=y
-CONFIG_ISO9660_FS=y
-CONFIG_JOLIET=y
-CONFIG_ZISOFS=y
-CONFIG_UDF_FS=m
-CONFIG_MSDOS_FS=y
-CONFIG_VFAT_FS=y
-CONFIG_PROC_KCORE=y
-CONFIG_TMPFS=y
-CONFIG_TMPFS_POSIX_ACL=y
-CONFIG_CONFIGFS_FS=m
-CONFIG_CRAMFS=y
-CONFIG_NFS_FS=y
-CONFIG_NFS_V3=y
-CONFIG_NFS_V3_ACL=y
-CONFIG_NFS_V4=y
-CONFIG_NFS_V4_1=y
-CONFIG_ROOT_NFS=y
-CONFIG_CIFS=y
-CONFIG_CIFS_WEAK_PW_HASH=y
-CONFIG_CIFS_XATTR=y
-CONFIG_CIFS_POSIX=y
-CONFIG_NLS_CODEPAGE_437=y
-CONFIG_NLS_ASCII=y
-CONFIG_NLS_ISO8859_1=y
-CONFIG_CRC_CCITT=m
-CONFIG_CRC_T10DIF=y
-CONFIG_LIBCRC32C=m
-CONFIG_PRINTK_TIME=y
-CONFIG_MAGIC_SYSRQ=y
-CONFIG_STRIP_ASM_SYMS=y
-CONFIG_DETECT_HUNG_TASK=y
-# CONFIG_SCHED_DEBUG is not set
-CONFIG_DEBUG_INFO=y
-CONFIG_FTRACE_SYSCALLS=y
-CONFIG_PPC_EMULATED_STATS=y
-CONFIG_XMON=y
-CONFIG_XMON_DEFAULT=y
-CONFIG_IRQ_DOMAIN_DEBUG=y
-CONFIG_PPC_EARLY_DEBUG=y
-CONFIG_KEYS_DEBUG_PROC_KEYS=y
-CONFIG_CRYPTO_NULL=m
-CONFIG_CRYPTO_TEST=m
-CONFIG_CRYPTO_CCM=m
-CONFIG_CRYPTO_GCM=m
-CONFIG_CRYPTO_PCBC=m
-CONFIG_CRYPTO_MICHAEL_MIC=m
-CONFIG_CRYPTO_SHA256=m
-CONFIG_CRYPTO_SHA512=m
-CONFIG_CRYPTO_TGR192=m
-CONFIG_CRYPTO_WP512=m
-CONFIG_CRYPTO_AES=m
-CONFIG_CRYPTO_ANUBIS=m
-CONFIG_CRYPTO_BLOWFISH=m
-CONFIG_CRYPTO_CAST5=m
-CONFIG_CRYPTO_CAST6=m
-CONFIG_CRYPTO_KHAZAD=m
-CONFIG_CRYPTO_SALSA20=m
-CONFIG_CRYPTO_SERPENT=m
-CONFIG_CRYPTO_TEA=m
-CONFIG_CRYPTO_TWOFISH=m
-CONFIG_CRYPTO_LZO=m
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
-CONFIG_VIRTUALIZATION=y
index f42e9baf3a4e743fddd9cf4bbc809f49aa1299d0..7c8608b096947000f7120f2a7ee729677962a094 100644 (file)
@@ -489,7 +489,6 @@ typedef struct scc_trans {
 #define FCC_GFMR_TCI           ((uint)0x20000000)
 #define FCC_GFMR_TRX           ((uint)0x10000000)
 #define FCC_GFMR_TTX           ((uint)0x08000000)
-#define FCC_GFMR_TTX           ((uint)0x08000000)
 #define FCC_GFMR_CDP           ((uint)0x04000000)
 #define FCC_GFMR_CTSP          ((uint)0x02000000)
 #define FCC_GFMR_CDS           ((uint)0x01000000)
index b76f58c124ca04c4673f1392c2f7a06960854142..fab7743c2640d565d5689f1cff06aa3dddbe0aa9 100644 (file)
@@ -254,6 +254,7 @@ void *eeh_pe_traverse(struct eeh_pe *root,
 void *eeh_pe_dev_traverse(struct eeh_pe *root,
                eeh_traverse_func fn, void *flag);
 void eeh_pe_restore_bars(struct eeh_pe *pe);
+const char *eeh_pe_loc_get(struct eeh_pe *pe);
 struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe);
 
 void *eeh_dev_init(struct device_node *dn, void *data);
index 89d5670b2eeb400ec659793fc3e960cc6d5894a3..1e551a2d6f8257f3fc78a73152ef65a3ebe24e52 100644 (file)
@@ -33,7 +33,7 @@ struct eeh_event {
 
 int eeh_event_init(void);
 int eeh_send_failure_event(struct eeh_pe *pe);
-void eeh_remove_event(struct eeh_pe *pe);
+void eeh_remove_event(struct eeh_pe *pe, bool force);
 void eeh_handle_event(struct eeh_pe *pe);
 
 #endif /* __KERNEL__ */
index 901dac6b6cb7f6bb6299c14b82158d5fc5cbcc39..d0918e09557f95e3d09f4ed41babb7d36f4e7f70 100644 (file)
@@ -223,10 +223,6 @@ typedef struct {
        unsigned int    id;
        unsigned int    active;
        unsigned long   vdso_base;
-#ifdef CONFIG_PPC_ICSWX
-       struct spinlock *cop_lockp;     /* guard cop related stuff */
-       unsigned long acop;             /* mask of enabled coprocessor types */
-#endif /* CONFIG_PPC_ICSWX */
 #ifdef CONFIG_PPC_MM_SLICES
        u64 low_slices_psize;   /* SLB page size encodings */
        u64 high_slices_psize;  /* 4 bits per slice for now */
index cb15cbb5160044233ac697d4ab0cce79fe4c06e4..460018889ba9b228c723855557ed338d1b5a6847 100644 (file)
@@ -599,9 +599,9 @@ enum {
 };
 
 struct OpalIoPhbErrorCommon {
-       uint32_t version;
-       uint32_t ioType;
-       uint32_t len;
+       __be32 version;
+       __be32 ioType;
+       __be32 len;
 };
 
 struct OpalIoP7IOCPhbErrorData {
@@ -666,64 +666,64 @@ struct OpalIoP7IOCPhbErrorData {
 struct OpalIoPhb3ErrorData {
        struct OpalIoPhbErrorCommon common;
 
-       uint32_t brdgCtl;
+       __be32 brdgCtl;
 
        /* PHB3 UTL regs */
-       uint32_t portStatusReg;
-       uint32_t rootCmplxStatus;
-       uint32_t busAgentStatus;
+       __be32 portStatusReg;
+       __be32 rootCmplxStatus;
+       __be32 busAgentStatus;
 
        /* PHB3 cfg regs */
-       uint32_t deviceStatus;
-       uint32_t slotStatus;
-       uint32_t linkStatus;
-       uint32_t devCmdStatus;
-       uint32_t devSecStatus;
+       __be32 deviceStatus;
+       __be32 slotStatus;
+       __be32 linkStatus;
+       __be32 devCmdStatus;
+       __be32 devSecStatus;
 
        /* cfg AER regs */
-       uint32_t rootErrorStatus;
-       uint32_t uncorrErrorStatus;
-       uint32_t corrErrorStatus;
-       uint32_t tlpHdr1;
-       uint32_t tlpHdr2;
-       uint32_t tlpHdr3;
-       uint32_t tlpHdr4;
-       uint32_t sourceId;
+       __be32 rootErrorStatus;
+       __be32 uncorrErrorStatus;
+       __be32 corrErrorStatus;
+       __be32 tlpHdr1;
+       __be32 tlpHdr2;
+       __be32 tlpHdr3;
+       __be32 tlpHdr4;
+       __be32 sourceId;
 
-       uint32_t rsv3;
+       __be32 rsv3;
 
        /* Record data about the call to allocate a buffer */
-       uint64_t errorClass;
-       uint64_t correlator;
+       __be64 errorClass;
+       __be64 correlator;
 
-       uint64_t nFir;                  /* 000 */
-       uint64_t nFirMask;              /* 003 */
-       uint64_t nFirWOF;               /* 008 */
+       __be64 nFir;                    /* 000 */
+       __be64 nFirMask;                /* 003 */
+       __be64 nFirWOF;         /* 008 */
 
        /* PHB3 MMIO Error Regs */
-       uint64_t phbPlssr;              /* 120 */
-       uint64_t phbCsr;                /* 110 */
-       uint64_t lemFir;                /* C00 */
-       uint64_t lemErrorMask;          /* C18 */
-       uint64_t lemWOF;                /* C40 */
-       uint64_t phbErrorStatus;        /* C80 */
-       uint64_t phbFirstErrorStatus;   /* C88 */
-       uint64_t phbErrorLog0;          /* CC0 */
-       uint64_t phbErrorLog1;          /* CC8 */
-       uint64_t mmioErrorStatus;       /* D00 */
-       uint64_t mmioFirstErrorStatus;  /* D08 */
-       uint64_t mmioErrorLog0;         /* D40 */
-       uint64_t mmioErrorLog1;         /* D48 */
-       uint64_t dma0ErrorStatus;       /* D80 */
-       uint64_t dma0FirstErrorStatus;  /* D88 */
-       uint64_t dma0ErrorLog0;         /* DC0 */
-       uint64_t dma0ErrorLog1;         /* DC8 */
-       uint64_t dma1ErrorStatus;       /* E00 */
-       uint64_t dma1FirstErrorStatus;  /* E08 */
-       uint64_t dma1ErrorLog0;         /* E40 */
-       uint64_t dma1ErrorLog1;         /* E48 */
-       uint64_t pestA[OPAL_PHB3_NUM_PEST_REGS];
-       uint64_t pestB[OPAL_PHB3_NUM_PEST_REGS];
+       __be64 phbPlssr;                /* 120 */
+       __be64 phbCsr;          /* 110 */
+       __be64 lemFir;          /* C00 */
+       __be64 lemErrorMask;            /* C18 */
+       __be64 lemWOF;          /* C40 */
+       __be64 phbErrorStatus;  /* C80 */
+       __be64 phbFirstErrorStatus;     /* C88 */
+       __be64 phbErrorLog0;            /* CC0 */
+       __be64 phbErrorLog1;            /* CC8 */
+       __be64 mmioErrorStatus; /* D00 */
+       __be64 mmioFirstErrorStatus;    /* D08 */
+       __be64 mmioErrorLog0;           /* D40 */
+       __be64 mmioErrorLog1;           /* D48 */
+       __be64 dma0ErrorStatus; /* D80 */
+       __be64 dma0FirstErrorStatus;    /* D88 */
+       __be64 dma0ErrorLog0;           /* DC0 */
+       __be64 dma0ErrorLog1;           /* DC8 */
+       __be64 dma1ErrorStatus; /* E00 */
+       __be64 dma1FirstErrorStatus;    /* E08 */
+       __be64 dma1ErrorLog0;           /* E40 */
+       __be64 dma1ErrorLog1;           /* E48 */
+       __be64 pestA[OPAL_PHB3_NUM_PEST_REGS];
+       __be64 pestB[OPAL_PHB3_NUM_PEST_REGS];
 };
 
 enum {
@@ -851,8 +851,8 @@ int64_t opal_pci_mask_pe_error(uint64_t phb_id, uint16_t pe_number, uint8_t erro
 int64_t opal_set_slot_led_status(uint64_t phb_id, uint64_t slot_id, uint8_t led_type, uint8_t led_action);
 int64_t opal_get_epow_status(__be64 *status);
 int64_t opal_set_system_attention_led(uint8_t led_action);
-int64_t opal_pci_next_error(uint64_t phb_id, uint64_t *first_frozen_pe,
-                           uint16_t *pci_error_type, uint16_t *severity);
+int64_t opal_pci_next_error(uint64_t phb_id, __be64 *first_frozen_pe,
+                           __be16 *pci_error_type, __be16 *severity);
 int64_t opal_pci_poll(uint64_t phb_id);
 int64_t opal_return_cpu(void);
 int64_t opal_reinit_cpus(uint64_t flags);
index 3d52a1132f3d30e2b007174e9cec71e7786499c0..3ba9c6f096fcfd38c517a0a2c7882994894268bb 100644 (file)
 #define TLB1_UR                        ASM_CONST(0x0000000000000002)
 #define TLB1_SR                        ASM_CONST(0x0000000000000001)
 
-#ifdef CONFIG_PPC_EARLY_DEBUG_WSP
-#define WSP_UART_PHYS  0xffc000c000
-/* This needs to be careful chosen to hit a !0 congruence class
- * in the TLB since we bolt it in way 3, which is already occupied
- * by our linear mapping primary bolted entry in CC 0.
- */
-#define WSP_UART_VIRT  0xf000000000001000
-#endif
-
 /* A2 erativax attributes definitions */
 #define ERATIVAX_RS_IS_ALL             0x000
 #define ERATIVAX_RS_IS_TID             0x040
index 0e83e7d8c73f5d0845690928ce24f179f69e22b8..58abeda64cb7afa271078497f25e3ae5a8a0e26c 100644 (file)
@@ -16,13 +16,15 @@ struct thread_struct;
 extern struct task_struct *_switch(struct thread_struct *prev,
                                   struct thread_struct *next);
 #ifdef CONFIG_PPC_BOOK3S_64
-static inline void save_tar(struct thread_struct *prev)
+static inline void save_early_sprs(struct thread_struct *prev)
 {
        if (cpu_has_feature(CPU_FTR_ARCH_207S))
                prev->tar = mfspr(SPRN_TAR);
+       if (cpu_has_feature(CPU_FTR_DSCR))
+               prev->dscr = mfspr(SPRN_DSCR);
 }
 #else
-static inline void save_tar(struct thread_struct *prev) {}
+static inline void save_early_sprs(struct thread_struct *prev) {}
 #endif
 
 extern void enable_kernel_fp(void);
@@ -84,6 +86,8 @@ static inline void clear_task_ebb(struct task_struct *t)
 {
 #ifdef CONFIG_PPC_BOOK3S_64
     /* EBB perf events are not inherited, so clear all EBB state. */
+    t->thread.ebbrr = 0;
+    t->thread.ebbhr = 0;
     t->thread.bescr = 0;
     t->thread.mmcr2 = 0;
     t->thread.mmcr0 = 0;
diff --git a/arch/powerpc/include/asm/wsp.h b/arch/powerpc/include/asm/wsp.h
deleted file mode 100644 (file)
index c7dc830..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- *  Copyright 2011 Michael Ellerman, IBM Corp.
- *
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU General Public License
- *  as published by the Free Software Foundation; either version
- *  2 of the License, or (at your option) any later version.
- */
-#ifndef __ASM_POWERPC_WSP_H
-#define __ASM_POWERPC_WSP_H
-
-extern int wsp_get_chip_id(struct device_node *dn);
-
-#endif /* __ASM_POWERPC_WSP_H */
index 5b7657959faad198796fdc868f03f66eaf9db854..de2c0e4ee1aab1c13d0ac60d0d2300849665c48c 100644 (file)
@@ -41,5 +41,6 @@
 #define PPC_FEATURE2_EBB               0x10000000
 #define PPC_FEATURE2_ISEL              0x08000000
 #define PPC_FEATURE2_TAR               0x04000000
+#define PPC_FEATURE2_VEC_CRYPTO                0x02000000
 
 #endif /* _UAPI__ASM_POWERPC_CPUTABLE_H */
index fab19ec25597c0a8ac95732b42354a8f58a335a4..670c312d914e64508ddcf8599d639663504603f6 100644 (file)
@@ -43,7 +43,6 @@ obj-$(CONFIG_PPC_BOOK3S_64)   += cpu_setup_power.o
 obj-$(CONFIG_PPC_BOOK3S_64)    += mce.o mce_power.o
 obj64-$(CONFIG_RELOCATABLE)    += reloc_64.o
 obj-$(CONFIG_PPC_BOOK3E_64)    += exceptions-64e.o idle_book3e.o
-obj-$(CONFIG_PPC_A2)           += cpu_setup_a2.o
 obj-$(CONFIG_PPC64)            += vdso64/
 obj-$(CONFIG_ALTIVEC)          += vecemu.o
 obj-$(CONFIG_PPC_970_NAP)      += idle_power4.o
diff --git a/arch/powerpc/kernel/cpu_setup_a2.S b/arch/powerpc/kernel/cpu_setup_a2.S
deleted file mode 100644 (file)
index 61f079e..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- *  A2 specific assembly support code
- *
- *  Copyright 2009 Ben Herrenschmidt, IBM Corp.
- *
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU General Public License
- *  as published by the Free Software Foundation; either version
- *  2 of the License, or (at your option) any later version.
- */
-
-#include <asm/asm-offsets.h>
-#include <asm/ppc_asm.h>
-#include <asm/ppc-opcode.h>
-#include <asm/processor.h>
-#include <asm/reg_a2.h>
-#include <asm/reg.h>
-#include <asm/thread_info.h>
-
-/*
- * Disable thdid and class fields in ERATs to bump PID to full 14 bits capacity.
- * This also prevents external LPID accesses but that isn't a problem when not a
- * guest. Under PV, this setting will be ignored and MMUCR will return the right
- * number of PID bits we can use.
- */
-#define MMUCR1_EXTEND_PID \
-       (MMUCR1_ICTID | MMUCR1_ITTID | MMUCR1_DCTID | \
-        MMUCR1_DTTID | MMUCR1_DCCD)
-
-/*
- * Use extended PIDs if enabled.
- * Don't clear the ERATs on context sync events and enable I & D LRU.
- * Enable ERAT back invalidate when tlbwe overwrites an entry.
- */
-#define INITIAL_MMUCR1 \
-       (MMUCR1_EXTEND_PID | MMUCR1_CSINV_NEVER | MMUCR1_IRRE | \
-        MMUCR1_DRRE | MMUCR1_TLBWE_BINV)
-
-_GLOBAL(__setup_cpu_a2)
-       /* Some of these are actually thread local and some are
-        * core local but doing it always won't hurt
-        */
-
-#ifdef CONFIG_PPC_ICSWX
-       /* Make sure ACOP starts out as zero */
-       li      r3,0
-       mtspr   SPRN_ACOP,r3
-
-       /* Skip the following if we are in Guest mode */
-       mfmsr   r3
-       andis.  r0,r3,MSR_GS@h
-       bne     _icswx_skip_guest
-
-       /* Enable icswx instruction */
-       mfspr   r3,SPRN_A2_CCR2
-       ori     r3,r3,A2_CCR2_ENABLE_ICSWX
-       mtspr   SPRN_A2_CCR2,r3
-
-       /* Unmask all CTs in HACOP */
-       li      r3,-1
-       mtspr   SPRN_HACOP,r3
-_icswx_skip_guest:
-#endif /* CONFIG_PPC_ICSWX */
-
-       /* Enable doorbell */
-       mfspr   r3,SPRN_A2_CCR2
-       oris     r3,r3,A2_CCR2_ENABLE_PC@h
-       mtspr   SPRN_A2_CCR2,r3
-       isync
-
-       /* Setup CCR0 to disable power saving for now as it's busted
-        * in the current implementations. Setup CCR1 to wake on
-        * interrupts normally (we write the default value but who
-        * knows what FW may have clobbered...)
-        */
-       li      r3,0
-       mtspr   SPRN_A2_CCR0, r3
-       LOAD_REG_IMMEDIATE(r3,0x0f0f0f0f)
-       mtspr   SPRN_A2_CCR1, r3
-
-       /* Initialise MMUCR1 */
-       lis     r3,INITIAL_MMUCR1@h
-       ori     r3,r3,INITIAL_MMUCR1@l
-       mtspr   SPRN_MMUCR1,r3
-
-       /* Set MMUCR2 to enable 4K, 64K, 1M, 16M and 1G pages */
-       LOAD_REG_IMMEDIATE(r3, 0x000a7531)
-       mtspr   SPRN_MMUCR2,r3
-
-       /* Set MMUCR3 to write all thids bit to the TLB */
-       LOAD_REG_IMMEDIATE(r3, 0x0000000f)
-       mtspr   SPRN_MMUCR3,r3
-
-       /* Don't do ERAT stuff if running guest mode */
-       mfmsr   r3
-       andis.  r0,r3,MSR_GS@h
-       bne     1f
-
-       /* Now set the I-ERAT watermark to 15 */
-       lis     r4,(MMUCR0_TLBSEL_I|MMUCR0_ECL)@h
-       mtspr   SPRN_MMUCR0, r4
-       li      r4,A2_IERAT_SIZE-1
-       PPC_ERATWE(R4,R4,3)
-
-       /* Now set the D-ERAT watermark to 31 */
-       lis     r4,(MMUCR0_TLBSEL_D|MMUCR0_ECL)@h
-       mtspr   SPRN_MMUCR0, r4
-       li      r4,A2_DERAT_SIZE-1
-       PPC_ERATWE(R4,R4,3)
-
-       /* And invalidate the beast just in case. That won't get rid of
-        * a bolted entry though it will be in LRU and so will go away eventually
-        * but let's not bother for now
-        */
-       PPC_ERATILX(0,0,R0)
-1:
-       blr
-
-_GLOBAL(__restore_cpu_a2)
-       b       __setup_cpu_a2
index 1557e7c2c7e15bc45120eedde3d288792676f235..46733535cc0b482a1ea35fe8411c556fb26fcb3b 100644 (file)
@@ -56,6 +56,7 @@ _GLOBAL(__setup_cpu_power8)
        li      r0,0
        mtspr   SPRN_LPID,r0
        mfspr   r3,SPRN_LPCR
+       ori     r3, r3, LPCR_PECEDH
        bl      __init_LPCR
        bl      __init_HFSCR
        bl      __init_tlb_power8
@@ -74,6 +75,7 @@ _GLOBAL(__restore_cpu_power8)
        li      r0,0
        mtspr   SPRN_LPID,r0
        mfspr   r3,SPRN_LPCR
+       ori     r3, r3, LPCR_PECEDH
        bl      __init_LPCR
        bl      __init_HFSCR
        bl      __init_tlb_power8
index c1faade6506dd581e3baf2aebfd93c7972fbd6dc..965291b4c2fa15a9f6f50cd8ca8b1e3a3067db02 100644 (file)
@@ -109,7 +109,8 @@ extern void __restore_cpu_e6500(void);
                                 PPC_FEATURE_PSERIES_PERFMON_COMPAT)
 #define COMMON_USER2_POWER8    (PPC_FEATURE2_ARCH_2_07 | \
                                 PPC_FEATURE2_HTM_COMP | PPC_FEATURE2_DSCR | \
-                                PPC_FEATURE2_ISEL | PPC_FEATURE2_TAR)
+                                PPC_FEATURE2_ISEL | PPC_FEATURE2_TAR | \
+                                PPC_FEATURE2_VEC_CRYPTO)
 #define COMMON_USER_PA6T       (COMMON_USER_PPC64 | PPC_FEATURE_PA6T |\
                                 PPC_FEATURE_TRUE_LE | \
                                 PPC_FEATURE_HAS_ALTIVEC_COMP)
@@ -2148,44 +2149,6 @@ static struct cpu_spec __initdata cpu_specs[] = {
        }
 #endif /* CONFIG_PPC32 */
 #endif /* CONFIG_E500 */
-
-#ifdef CONFIG_PPC_A2
-       {       /* Standard A2 (>= DD2) + FPU core */
-               .pvr_mask               = 0xffff0000,
-               .pvr_value              = 0x00480000,
-               .cpu_name               = "A2 (>= DD2)",
-               .cpu_features           = CPU_FTRS_A2,
-               .cpu_user_features      = COMMON_USER_PPC64,
-               .mmu_features           = MMU_FTRS_A2,
-               .icache_bsize           = 64,
-               .dcache_bsize           = 64,
-               .num_pmcs               = 0,
-               .cpu_setup              = __setup_cpu_a2,
-               .cpu_restore            = __restore_cpu_a2,
-               .machine_check          = machine_check_generic,
-               .platform               = "ppca2",
-       },
-       {       /* This is a default entry to get going, to be replaced by
-                * a real one at some stage
-                */
-#define CPU_FTRS_BASE_BOOK3E   (CPU_FTR_USE_TB | \
-           CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_SMT | \
-           CPU_FTR_NODSISRALIGN | CPU_FTR_NOEXECUTE)
-               .pvr_mask               = 0x00000000,
-               .pvr_value              = 0x00000000,
-               .cpu_name               = "Book3E",
-               .cpu_features           = CPU_FTRS_BASE_BOOK3E,
-               .cpu_user_features      = COMMON_USER_PPC64,
-               .mmu_features           = MMU_FTR_TYPE_3E | MMU_FTR_USE_TLBILX |
-                                         MMU_FTR_USE_TLBIVAX_BCAST |
-                                         MMU_FTR_LOCK_BCAST_INVAL,
-               .icache_bsize           = 64,
-               .dcache_bsize           = 64,
-               .num_pmcs               = 0,
-               .machine_check          = machine_check_generic,
-               .platform               = "power6",
-       },
-#endif /* CONFIG_PPC_A2 */
 };
 
 static struct cpu_spec the_cpu_spec;
index 7051ea3101b96af830faf0e44eadeb70884fb6fc..86e25702aaca8894701ff585b87ee08caa017199 100644 (file)
@@ -330,8 +330,8 @@ static int eeh_phb_check_failure(struct eeh_pe *pe)
        eeh_pe_state_mark(phb_pe, EEH_PE_ISOLATED);
        eeh_serialize_unlock(flags);
 
-       pr_err("EEH: PHB#%x failure detected\n",
-               phb_pe->phb->global_number);
+       pr_err("EEH: PHB#%x failure detected, location: %s\n",
+               phb_pe->phb->global_number, eeh_pe_loc_get(phb_pe));
        dump_stack();
        eeh_send_failure_event(phb_pe);
 
@@ -358,10 +358,11 @@ out:
 int eeh_dev_check_failure(struct eeh_dev *edev)
 {
        int ret;
+       int active_flags = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE);
        unsigned long flags;
        struct device_node *dn;
        struct pci_dev *dev;
-       struct eeh_pe *pe;
+       struct eeh_pe *pe, *parent_pe, *phb_pe;
        int rc = 0;
        const char *location;
 
@@ -439,14 +440,34 @@ int eeh_dev_check_failure(struct eeh_dev *edev)
         */
        if ((ret < 0) ||
            (ret == EEH_STATE_NOT_SUPPORT) ||
-           (ret & (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) ==
-           (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) {
+           ((ret & active_flags) == active_flags)) {
                eeh_stats.false_positives++;
                pe->false_positives++;
                rc = 0;
                goto dn_unlock;
        }
 
+       /*
+        * It should be corner case that the parent PE has been
+        * put into frozen state as well. We should take care
+        * that at first.
+        */
+       parent_pe = pe->parent;
+       while (parent_pe) {
+               /* Hit the ceiling ? */
+               if (parent_pe->type & EEH_PE_PHB)
+                       break;
+
+               /* Frozen parent PE ? */
+               ret = eeh_ops->get_state(parent_pe, NULL);
+               if (ret > 0 &&
+                   (ret & active_flags) != active_flags)
+                       pe = parent_pe;
+
+               /* Next parent level */
+               parent_pe = parent_pe->parent;
+       }
+
        eeh_stats.slot_resets++;
 
        /* Avoid repeated reports of this failure, including problems
@@ -460,8 +481,11 @@ int eeh_dev_check_failure(struct eeh_dev *edev)
         * a stack trace will help the device-driver authors figure
         * out what happened.  So print that out.
         */
-       pr_err("EEH: Frozen PE#%x detected on PHB#%x\n",
-               pe->addr, pe->phb->global_number);
+       phb_pe = eeh_phb_pe_get(pe->phb);
+       pr_err("EEH: Frozen PHB#%x-PE#%x detected\n",
+              pe->phb->global_number, pe->addr);
+       pr_err("EEH: PE location: %s, PHB location: %s\n",
+              eeh_pe_loc_get(pe), eeh_pe_loc_get(phb_pe));
        dump_stack();
 
        eeh_send_failure_event(pe);
index 7100a5b96e7059caf572609dec9d7f9393f0106f..420da61d4ce001d74ba64b675f8615b65f6e812a 100644 (file)
@@ -447,8 +447,9 @@ static void *eeh_pe_detach_dev(void *data, void *userdata)
  * PE reset (for 3 times), we try to clear the frozen state
  * for 3 times as well.
  */
-static int eeh_clear_pe_frozen_state(struct eeh_pe *pe)
+static void *__eeh_clear_pe_frozen_state(void *data, void *flag)
 {
+       struct eeh_pe *pe = (struct eeh_pe *)data;
        int i, rc;
 
        for (i = 0; i < 3; i++) {
@@ -461,13 +462,24 @@ static int eeh_clear_pe_frozen_state(struct eeh_pe *pe)
        }
 
        /* The PE has been isolated, clear it */
-       if (rc)
+       if (rc) {
                pr_warn("%s: Can't clear frozen PHB#%x-PE#%x (%d)\n",
                        __func__, pe->phb->global_number, pe->addr, rc);
-       else
+               return (void *)pe;
+       }
+
+       return NULL;
+}
+
+static int eeh_clear_pe_frozen_state(struct eeh_pe *pe)
+{
+       void *rc;
+
+       rc = eeh_pe_traverse(pe, __eeh_clear_pe_frozen_state, NULL);
+       if (!rc)
                eeh_pe_state_clear(pe, EEH_PE_ISOLATED);
 
-       return rc;
+       return rc ? -EIO : 0;
 }
 
 /**
@@ -758,7 +770,7 @@ static void eeh_handle_special_event(void)
                        eeh_serialize_lock(&flags);
 
                        /* Purge all events */
-                       eeh_remove_event(NULL);
+                       eeh_remove_event(NULL, true);
 
                        list_for_each_entry(hose, &hose_list, list_node) {
                                phb_pe = eeh_phb_pe_get(hose);
@@ -777,7 +789,7 @@ static void eeh_handle_special_event(void)
                        eeh_serialize_lock(&flags);
 
                        /* Purge all events of the PHB */
-                       eeh_remove_event(pe);
+                       eeh_remove_event(pe, true);
 
                        if (rc == EEH_NEXT_ERR_DEAD_PHB)
                                eeh_pe_state_mark(pe, EEH_PE_ISOLATED);
index 72d748b56c86b2b9ae960e49b819dfedef36f61a..4eefb6e34dbb2f6edbf4990349e9c11b0b5d07f8 100644 (file)
@@ -152,24 +152,33 @@ int eeh_send_failure_event(struct eeh_pe *pe)
 /**
  * eeh_remove_event - Remove EEH event from the queue
  * @pe: Event binding to the PE
+ * @force: Event will be removed unconditionally
  *
  * On PowerNV platform, we might have subsequent coming events
  * is part of the former one. For that case, those subsequent
  * coming events are totally duplicated and unnecessary, thus
  * they should be removed.
  */
-void eeh_remove_event(struct eeh_pe *pe)
+void eeh_remove_event(struct eeh_pe *pe, bool force)
 {
        unsigned long flags;
        struct eeh_event *event, *tmp;
 
+       /*
+        * If we have NULL PE passed in, we have dead IOC
+        * or we're sure we can report all existing errors
+        * by the caller.
+        *
+        * With "force", the event with associated PE that
+        * have been isolated, the event won't be removed
+        * to avoid event lost.
+        */
        spin_lock_irqsave(&eeh_eventlist_lock, flags);
        list_for_each_entry_safe(event, tmp, &eeh_eventlist, list) {
-               /*
-                * If we don't have valid PE passed in, that means
-                * we already have event corresponding to dead IOC
-                * and all events should be purged.
-                */
+               if (!force && event->pe &&
+                   (event->pe->state & EEH_PE_ISOLATED))
+                       continue;
+
                if (!pe) {
                        list_del(&event->list);
                        kfree(event);
index 995c2a2846303d3885674d474bcbfd0cae90f9cc..fbd01eba44734651d7a9e324b9fca1f733107ff7 100644 (file)
@@ -791,6 +791,66 @@ void eeh_pe_restore_bars(struct eeh_pe *pe)
        eeh_pe_dev_traverse(pe, eeh_restore_one_device_bars, NULL);
 }
 
+/**
+ * eeh_pe_loc_get - Retrieve location code binding to the given PE
+ * @pe: EEH PE
+ *
+ * Retrieve the location code of the given PE. If the primary PE bus
+ * is root bus, we will grab location code from PHB device tree node
+ * or root port. Otherwise, the upstream bridge's device tree node
+ * of the primary PE bus will be checked for the location code.
+ */
+const char *eeh_pe_loc_get(struct eeh_pe *pe)
+{
+       struct pci_controller *hose;
+       struct pci_bus *bus = eeh_pe_bus_get(pe);
+       struct pci_dev *pdev;
+       struct device_node *dn;
+       const char *loc;
+
+       if (!bus)
+               return "N/A";
+
+       /* PHB PE or root PE ? */
+       if (pci_is_root_bus(bus)) {
+               hose = pci_bus_to_host(bus);
+               loc = of_get_property(hose->dn,
+                               "ibm,loc-code", NULL);
+               if (loc)
+                       return loc;
+               loc = of_get_property(hose->dn,
+                               "ibm,io-base-loc-code", NULL);
+               if (loc)
+                       return loc;
+
+               pdev = pci_get_slot(bus, 0x0);
+       } else {
+               pdev = bus->self;
+       }
+
+       if (!pdev) {
+               loc = "N/A";
+               goto out;
+       }
+
+       dn = pci_device_to_OF_node(pdev);
+       if (!dn) {
+               loc = "N/A";
+               goto out;
+       }
+
+       loc = of_get_property(dn, "ibm,loc-code", NULL);
+       if (!loc)
+               loc = of_get_property(dn, "ibm,slot-location-code", NULL);
+       if (!loc)
+               loc = "N/A";
+
+out:
+       if (pci_is_root_bus(bus) && pdev)
+               pci_dev_put(pdev);
+       return loc;
+}
+
 /**
  * eeh_pe_bus_get - Retrieve PCI bus according to the given PE
  * @pe: EEH PE
index 911d45366f596c764a4ad001025760de4f07c5e8..6528c5e2cc44800a201802858c23d34e92162041 100644 (file)
@@ -428,12 +428,6 @@ BEGIN_FTR_SECTION
        std     r24,THREAD_VRSAVE(r3)
 END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
 #endif /* CONFIG_ALTIVEC */
-#ifdef CONFIG_PPC64
-BEGIN_FTR_SECTION
-       mfspr   r25,SPRN_DSCR
-       std     r25,THREAD_DSCR(r3)
-END_FTR_SECTION_IFSET(CPU_FTR_DSCR)
-#endif
        and.    r0,r0,r22
        beq+    1f
        andc    r22,r22,r0
index 771b4e92e5d9c091fcedfa1dc4982fa6950c1405..bb9cac6c8051ab8a10467a099e8fa998b1f7d181 100644 (file)
@@ -1467,22 +1467,6 @@ a2_tlbinit_after_linear_map:
        .globl  a2_tlbinit_after_iprot_flush
 a2_tlbinit_after_iprot_flush:
 
-#ifdef CONFIG_PPC_EARLY_DEBUG_WSP
-       /* Now establish early debug mappings if applicable */
-       /* Restore the MAS0 we used for linear mapping load */
-       mtspr   SPRN_MAS0,r11
-
-       lis     r3,(MAS1_VALID | MAS1_IPROT)@h
-       ori     r3,r3,(BOOK3E_PAGESZ_4K << MAS1_TSIZE_SHIFT)
-       mtspr   SPRN_MAS1,r3
-       LOAD_REG_IMMEDIATE(r3, WSP_UART_VIRT | MAS2_I | MAS2_G)
-       mtspr   SPRN_MAS2,r3
-       LOAD_REG_IMMEDIATE(r3, WSP_UART_PHYS | MAS3_SR | MAS3_SW)
-       mtspr   SPRN_MAS7_MAS3,r3
-       /* re-use the MAS8 value from the linear mapping */
-       tlbwe
-#endif /* CONFIG_PPC_EARLY_DEBUG_WSP */
-
        PPC_TLBILX(0,0,R0)
        sync
        isync
index 20f11eb4dff7ee4e308565ec0910f57b5bd189e6..a7d36b19221d4710eb3e35db66b43fd5055e4c92 100644 (file)
@@ -439,9 +439,9 @@ BEGIN_FTR_SECTION
         * R9           = CR
         * Original R9 to R13 is saved on PACA_EXMC
         *
-        * Switch to mc_emergency stack and handle re-entrancy (though we
-        * currently don't test for overflow). Save MCE registers srr1,
-        * srr0, dar and dsisr and then set ME=1
+        * Switch to mc_emergency stack and handle re-entrancy (we limit
+        * the nested MCE upto level 4 to avoid stack overflow).
+        * Save MCE registers srr1, srr0, dar and dsisr and then set ME=1
         *
         * We use paca->in_mce to check whether this is the first entry or
         * nested machine check. We increment paca->in_mce to track nested
@@ -464,6 +464,9 @@ BEGIN_FTR_SECTION
 0:     subi    r1,r1,INT_FRAME_SIZE    /* alloc stack frame */
        addi    r10,r10,1               /* increment paca->in_mce */
        sth     r10,PACA_IN_MCE(r13)
+       /* Limit nested MCE to level 4 to avoid stack overflow */
+       cmpwi   r10,4
+       bgt     2f                      /* Check if we hit limit of 4 */
        std     r11,GPR1(r1)            /* Save r1 on the stack. */
        std     r11,0(r1)               /* make stack chain pointer */
        mfspr   r11,SPRN_SRR0           /* Save SRR0 */
@@ -482,10 +485,23 @@ BEGIN_FTR_SECTION
        ori     r11,r11,MSR_RI          /* turn on RI bit */
        ld      r12,PACAKBASE(r13)      /* get high part of &label */
        LOAD_HANDLER(r12, machine_check_handle_early)
-       mtspr   SPRN_SRR0,r12
+1:     mtspr   SPRN_SRR0,r12
        mtspr   SPRN_SRR1,r11
        rfid
        b       .       /* prevent speculative execution */
+2:
+       /* Stack overflow. Stay on emergency stack and panic.
+        * Keep the ME bit off while panic-ing, so that if we hit
+        * another machine check we checkstop.
+        */
+       addi    r1,r1,INT_FRAME_SIZE    /* go back to previous stack frame */
+       ld      r11,PACAKMSR(r13)
+       ld      r12,PACAKBASE(r13)
+       LOAD_HANDLER(r12, unrecover_mce)
+       li      r10,MSR_ME
+       andc    r11,r11,r10             /* Turn off MSR_ME */
+       b       1b
+       b       .       /* prevent speculative execution */
 END_FTR_SECTION_IFSET(CPU_FTR_HVMODE)
 
 machine_check_pSeries:
@@ -1389,6 +1405,7 @@ machine_check_handle_early:
        bl      save_nvgprs
        addi    r3,r1,STACK_FRAME_OVERHEAD
        bl      machine_check_early
+       std     r3,RESULT(r1)   /* Save result */
        ld      r12,_MSR(r1)
 #ifdef CONFIG_PPC_P7_NAP
        /*
@@ -1443,10 +1460,32 @@ machine_check_handle_early:
         */
        andi.   r11,r12,MSR_RI
        bne     2f
-1:     addi    r3,r1,STACK_FRAME_OVERHEAD
-       bl      unrecoverable_exception
-       b       1b
+1:     mfspr   r11,SPRN_SRR0
+       ld      r10,PACAKBASE(r13)
+       LOAD_HANDLER(r10,unrecover_mce)
+       mtspr   SPRN_SRR0,r10
+       ld      r10,PACAKMSR(r13)
+       /*
+        * We are going down. But there are chances that we might get hit by
+        * another MCE during panic path and we may run into unstable state
+        * with no way out. Hence, turn ME bit off while going down, so that
+        * when another MCE is hit during panic path, system will checkstop
+        * and hypervisor will get restarted cleanly by SP.
+        */
+       li      r3,MSR_ME
+       andc    r10,r10,r3              /* Turn off MSR_ME */
+       mtspr   SPRN_SRR1,r10
+       rfid
+       b       .
 2:
+       /*
+        * Check if we have successfully handled/recovered from error, if not
+        * then stay on emergency stack and panic.
+        */
+       ld      r3,RESULT(r1)   /* Load result */
+       cmpdi   r3,0            /* see if we handled MCE successfully */
+
+       beq     1b              /* if !handled then panic */
        /*
         * Return from MC interrupt.
         * Queue up the MCE event so that we can log it later, while
@@ -1460,6 +1499,17 @@ machine_check_handle_early:
        MACHINE_CHECK_HANDLER_WINDUP
        b       machine_check_pSeries
 
+unrecover_mce:
+       /* Invoke machine_check_exception to print MCE event and panic. */
+       addi    r3,r1,STACK_FRAME_OVERHEAD
+       bl      machine_check_exception
+       /*
+        * We will not reach here. Even if we did, there is no way out. Call
+        * unrecoverable_exception and die.
+        */
+1:     addi    r3,r1,STACK_FRAME_OVERHEAD
+       bl      unrecoverable_exception
+       b       1b
 /*
  * r13 points to the PACA, r9 contains the saved CR,
  * r12 contain the saved SRR1, SRR0 is still ready for return
index 67ee0d6c1070b3f02702dbc8442a905f3b39b8d6..7d7d8635227ac76bcd054b3fd04248ec90127c9d 100644 (file)
@@ -930,25 +930,6 @@ initial_mmu:
        tlbwe   r4,r0,TLB_DATA          /* Load the data portion of the entry */
        tlbwe   r3,r0,TLB_TAG           /* Load the tag portion of the entry */
 
-#if defined(CONFIG_SERIAL_TEXT_DEBUG) && defined(SERIAL_DEBUG_IO_BASE)
-
-       /* Load a TLB entry for the UART, so that ppc4xx_progress() can use
-        * the UARTs nice and early.  We use a 4k real==virtual mapping. */
-
-       lis     r3,SERIAL_DEBUG_IO_BASE@h
-       ori     r3,r3,SERIAL_DEBUG_IO_BASE@l
-       mr      r4,r3
-       clrrwi  r4,r4,12
-       ori     r4,r4,(TLB_WR|TLB_I|TLB_M|TLB_G)
-
-       clrrwi  r3,r3,12
-       ori     r3,r3,(TLB_VALID | TLB_PAGESZ(PAGESZ_4K))
-
-       li      r0,0                    /* TLB slot 0 */
-       tlbwe   r4,r0,TLB_DATA
-       tlbwe   r3,r0,TLB_TAG
-#endif /* CONFIG_SERIAL_DEBUG_TEXT && SERIAL_DEBUG_IO_BASE */
-
        isync
 
        /* Establish the exception vector base
index 8a1edbe26b8f34c99beafc8a1d3bff68ec99f765..be99774d3f44ac9ecc83d5e637a7bb4b4f460d80 100644 (file)
@@ -755,15 +755,15 @@ struct task_struct *__switch_to(struct task_struct *prev,
 
        WARN_ON(!irqs_disabled());
 
-       /* Back up the TAR across context switches.
+       /* Back up the TAR and DSCR across context switches.
         * Note that the TAR is not available for use in the kernel.  (To
         * provide this, the TAR should be backed up/restored on exception
         * entry/exit instead, and be in pt_regs.  FIXME, this should be in
         * pt_regs anyway (for debug).)
-        * Save the TAR here before we do treclaim/trecheckpoint as these
-        * will change the TAR.
+        * Save the TAR and DSCR here before we do treclaim/trecheckpoint as
+        * these will change them.
         */
-       save_tar(&prev->thread);
+       save_early_sprs(&prev->thread);
 
        __switch_to_tm(prev);
 
index d4d418376f994575f598ee7f9bab58d1db2449ce..e239df3768acfb3e14c55367c7ab737233f50771 100644 (file)
@@ -471,7 +471,7 @@ void __init smp_setup_cpu_maps(void)
                for (j = 0; j < nthreads && cpu < nr_cpu_ids; j++) {
                        DBG("    thread %d -> cpu %d (hard id %d)\n",
                            j, cpu, be32_to_cpu(intserv[j]));
-                       set_cpu_present(cpu, true);
+                       set_cpu_present(cpu, of_device_is_available(dn));
                        set_hard_smp_processor_id(cpu, be32_to_cpu(intserv[j]));
                        set_cpu_possible(cpu, true);
                        cpu++;
index 7753af2d261381bcc21bf3e21f8ee4f4880847e9..51a3ff78838aaf1eb6726e92cb871c128221eb2e 100644 (file)
@@ -749,7 +749,7 @@ int setup_profiling_timer(unsigned int multiplier)
 /* cpumask of CPUs with asymetric SMT dependancy */
 static const int powerpc_smt_flags(void)
 {
-       int flags = SD_SHARE_CPUPOWER | SD_SHARE_PKG_RESOURCES;
+       int flags = SD_SHARE_CPUCAPACITY | SD_SHARE_PKG_RESOURCES;
 
        if (cpu_has_feature(CPU_FTR_ASYM_SMT)) {
                printk_once(KERN_INFO "Enabling Asymmetric SMT scheduling\n");
index 7e711bdcc6da5adb399e8ac0a55097e76317fe51..9fff9cdcc5196c3a20b9129d7e407c1dbc113cb7 100644 (file)
@@ -551,7 +551,7 @@ void timer_interrupt(struct pt_regs * regs)
        may_hard_irq_enable();
 
 
-#if defined(CONFIG_PPC32) && defined(CONFIG_PMAC)
+#if defined(CONFIG_PPC32) && defined(CONFIG_PPC_PMAC)
        if (atomic_read(&ppc_n_lost_interrupts) != 0)
                do_IRQ(regs);
 #endif
index 1bd7ca298fa1b132167a47bbf310ed0ef6a0db0b..239f1cde3fff161b8f4b9cfc56beff61d9143845 100644 (file)
@@ -295,6 +295,8 @@ long machine_check_early(struct pt_regs *regs)
 {
        long handled = 0;
 
+       __get_cpu_var(irq_stat).mce_exceptions++;
+
        if (cur_cpu_spec && cur_cpu_spec->machine_check_early)
                handled = cur_cpu_spec->machine_check_early(regs);
        return handled;
index a15837519dca45474a141fc6328eee130bafd779..b7aa07279a63b1fb5afeb26061631ed74c84b0e3 100644 (file)
@@ -62,8 +62,6 @@ void __init udbg_early_init(void)
        udbg_init_cpm();
 #elif defined(CONFIG_PPC_EARLY_DEBUG_USBGECKO)
        udbg_init_usbgecko();
-#elif defined(CONFIG_PPC_EARLY_DEBUG_WSP)
-       udbg_init_wsp();
 #elif defined(CONFIG_PPC_EARLY_DEBUG_MEMCONS)
        /* In memory console */
        udbg_init_memcons();
index 75702e207b2986c4741cf243c2a49c955c30e05b..6e7c4923b5ea1bb24427bc47360576900bc44fe8 100644 (file)
@@ -296,14 +296,3 @@ void __init udbg_init_40x_realmode(void)
 }
 
 #endif /* CONFIG_PPC_EARLY_DEBUG_40x */
-
-
-#ifdef CONFIG_PPC_EARLY_DEBUG_WSP
-
-void __init udbg_init_wsp(void)
-{
-       udbg_uart_init_mmio((void *)WSP_UART_VIRT, 1);
-       udbg_uart_setup(57600, 50000000);
-}
-
-#endif /* CONFIG_PPC_EARLY_DEBUG_WSP */
index 768a9f977c001ee73e6d5aa2f0b8ee44ea3b35f5..3a5c568b1e89f43210c9cd2ee0ec9e9d70c62e24 100644 (file)
@@ -113,10 +113,8 @@ static long kvmppc_realmode_mc_power7(struct kvm_vcpu *vcpu)
         * We assume that if the condition is recovered then linux host
         * will have generated an error log event that we will pick
         * up and log later.
-        * Don't release mce event now. In case if condition is not
-        * recovered we do guest exit and go back to linux host machine
-        * check handler. Hence we need make sure that current mce event
-        * is available for linux host to consume.
+        * Don't release mce event now. We will queue up the event so that
+        * we can log the MCE event info on host console.
         */
        if (!get_mce_event(&mce_evt, MCE_EVENT_DONTRELEASE))
                goto out;
@@ -128,11 +126,12 @@ static long kvmppc_realmode_mc_power7(struct kvm_vcpu *vcpu)
 
 out:
        /*
-        * If we have handled the error, then release the mce event because
-        * we will be delivering machine check to guest.
+        * We are now going enter guest either through machine check
+        * interrupt (for unhandled errors) or will continue from
+        * current HSRR0 (for handled errors) in guest. Hence
+        * queue up the event so that we can log it from host console later.
         */
-       if (handled)
-               release_mce_event();
+       machine_check_queue_event();
 
        return handled;
 }
index 77356fd25ccc96a8b4fd17746ea08edc0da16060..868347ef09fd48bcf8bfd343becb49f6898887c5 100644 (file)
@@ -2257,15 +2257,28 @@ machine_check_realmode:
        mr      r3, r9          /* get vcpu pointer */
        bl      kvmppc_realmode_machine_check
        nop
-       cmpdi   r3, 0           /* continue exiting from guest? */
+       cmpdi   r3, 0           /* Did we handle MCE ? */
        ld      r9, HSTATE_KVM_VCPU(r13)
        li      r12, BOOK3S_INTERRUPT_MACHINE_CHECK
-       beq     mc_cont
+       /*
+        * Deliver unhandled/fatal (e.g. UE) MCE errors to guest through
+        * machine check interrupt (set HSRR0 to 0x200). And for handled
+        * errors (no-fatal), just go back to guest execution with current
+        * HSRR0 instead of exiting guest. This new approach will inject
+        * machine check to guest for fatal error causing guest to crash.
+        *
+        * The old code used to return to host for unhandled errors which
+        * was causing guest to hang with soft lockups inside guest and
+        * makes it difficult to recover guest instance.
+        */
+       ld      r10, VCPU_PC(r9)
+       ld      r11, VCPU_MSR(r9)
+       bne     2f      /* Continue guest execution. */
        /* If not, deliver a machine check.  SRR0/1 are already set */
        li      r10, BOOK3S_INTERRUPT_MACHINE_CHECK
        ld      r11, VCPU_MSR(r9)
        bl      kvmppc_msr_interrupt
-       b       fast_interrupt_c_return
+2:     b       fast_interrupt_c_return
 
 /*
  * Check the reason we woke from nap, and take appropriate action.
index c0511c27a7337d757c450361a0682c8d3ef8d68e..412dd46dd0b7ea7136af14e8559e5d9b78d2c08a 100644 (file)
@@ -1470,7 +1470,7 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
                                regs->gpr[rd] = byterev_4(val);
                        goto ldst_done;
 
-#ifdef CONFIG_PPC_CPU
+#ifdef CONFIG_PPC_FPU
                case 535:       /* lfsx */
                case 567:       /* lfsux */
                        if (!(regs->msr & MSR_FP))
index e76eba74d9da8972afeed2525c755959c304dd6f..8f87d92171228d470f9274ff4eee27e921dbfbed 100644 (file)
@@ -78,7 +78,7 @@ sk_load_byte_positive_offset:
        blr
 
 /*
- * BPF_S_LDX_B_MSH: ldxb  4*([offset]&0xf)
+ * BPF_LDX | BPF_B | BPF_MSH: ldxb  4*([offset]&0xf)
  * r_addr is the offset value
  */
        .globl sk_load_byte_msh
index 808ce1cae21ab998439854e31eacaced66c044cb..6dcdadefd8d059a7c65207af5b71be761116947c 100644 (file)
@@ -79,19 +79,11 @@ static void bpf_jit_build_prologue(struct sk_filter *fp, u32 *image,
        }
 
        switch (filter[0].code) {
-       case BPF_S_RET_K:
-       case BPF_S_LD_W_LEN:
-       case BPF_S_ANC_PROTOCOL:
-       case BPF_S_ANC_IFINDEX:
-       case BPF_S_ANC_MARK:
-       case BPF_S_ANC_RXHASH:
-       case BPF_S_ANC_VLAN_TAG:
-       case BPF_S_ANC_VLAN_TAG_PRESENT:
-       case BPF_S_ANC_CPU:
-       case BPF_S_ANC_QUEUE:
-       case BPF_S_LD_W_ABS:
-       case BPF_S_LD_H_ABS:
-       case BPF_S_LD_B_ABS:
+       case BPF_RET | BPF_K:
+       case BPF_LD | BPF_W | BPF_LEN:
+       case BPF_LD | BPF_W | BPF_ABS:
+       case BPF_LD | BPF_H | BPF_ABS:
+       case BPF_LD | BPF_B | BPF_ABS:
                /* first instruction sets A register (or is RET 'constant') */
                break;
        default:
@@ -144,6 +136,7 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
 
        for (i = 0; i < flen; i++) {
                unsigned int K = filter[i].k;
+               u16 code = bpf_anc_helper(&filter[i]);
 
                /*
                 * addrs[] maps a BPF bytecode address into a real offset from
@@ -151,35 +144,35 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                 */
                addrs[i] = ctx->idx * 4;
 
-               switch (filter[i].code) {
+               switch (code) {
                        /*** ALU ops ***/
-               case BPF_S_ALU_ADD_X: /* A += X; */
+               case BPF_ALU | BPF_ADD | BPF_X: /* A += X; */
                        ctx->seen |= SEEN_XREG;
                        PPC_ADD(r_A, r_A, r_X);
                        break;
-               case BPF_S_ALU_ADD_K: /* A += K; */
+               case BPF_ALU | BPF_ADD | BPF_K: /* A += K; */
                        if (!K)
                                break;
                        PPC_ADDI(r_A, r_A, IMM_L(K));
                        if (K >= 32768)
                                PPC_ADDIS(r_A, r_A, IMM_HA(K));
                        break;
-               case BPF_S_ALU_SUB_X: /* A -= X; */
+               case BPF_ALU | BPF_SUB | BPF_X: /* A -= X; */
                        ctx->seen |= SEEN_XREG;
                        PPC_SUB(r_A, r_A, r_X);
                        break;
-               case BPF_S_ALU_SUB_K: /* A -= K */
+               case BPF_ALU | BPF_SUB | BPF_K: /* A -= K */
                        if (!K)
                                break;
                        PPC_ADDI(r_A, r_A, IMM_L(-K));
                        if (K >= 32768)
                                PPC_ADDIS(r_A, r_A, IMM_HA(-K));
                        break;
-               case BPF_S_ALU_MUL_X: /* A *= X; */
+               case BPF_ALU | BPF_MUL | BPF_X: /* A *= X; */
                        ctx->seen |= SEEN_XREG;
                        PPC_MUL(r_A, r_A, r_X);
                        break;
-               case BPF_S_ALU_MUL_K: /* A *= K */
+               case BPF_ALU | BPF_MUL | BPF_K: /* A *= K */
                        if (K < 32768)
                                PPC_MULI(r_A, r_A, K);
                        else {
@@ -187,7 +180,7 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                                PPC_MUL(r_A, r_A, r_scratch1);
                        }
                        break;
-               case BPF_S_ALU_MOD_X: /* A %= X; */
+               case BPF_ALU | BPF_MOD | BPF_X: /* A %= X; */
                        ctx->seen |= SEEN_XREG;
                        PPC_CMPWI(r_X, 0);
                        if (ctx->pc_ret0 != -1) {
@@ -201,13 +194,13 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                        PPC_MUL(r_scratch1, r_X, r_scratch1);
                        PPC_SUB(r_A, r_A, r_scratch1);
                        break;
-               case BPF_S_ALU_MOD_K: /* A %= K; */
+               case BPF_ALU | BPF_MOD | BPF_K: /* A %= K; */
                        PPC_LI32(r_scratch2, K);
                        PPC_DIVWU(r_scratch1, r_A, r_scratch2);
                        PPC_MUL(r_scratch1, r_scratch2, r_scratch1);
                        PPC_SUB(r_A, r_A, r_scratch1);
                        break;
-               case BPF_S_ALU_DIV_X: /* A /= X; */
+               case BPF_ALU | BPF_DIV | BPF_X: /* A /= X; */
                        ctx->seen |= SEEN_XREG;
                        PPC_CMPWI(r_X, 0);
                        if (ctx->pc_ret0 != -1) {
@@ -223,17 +216,17 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                        }
                        PPC_DIVWU(r_A, r_A, r_X);
                        break;
-               case BPF_S_ALU_DIV_K: /* A /= K */
+               case BPF_ALU | BPF_DIV | BPF_K: /* A /= K */
                        if (K == 1)
                                break;
                        PPC_LI32(r_scratch1, K);
                        PPC_DIVWU(r_A, r_A, r_scratch1);
                        break;
-               case BPF_S_ALU_AND_X:
+               case BPF_ALU | BPF_AND | BPF_X:
                        ctx->seen |= SEEN_XREG;
                        PPC_AND(r_A, r_A, r_X);
                        break;
-               case BPF_S_ALU_AND_K:
+               case BPF_ALU | BPF_AND | BPF_K:
                        if (!IMM_H(K))
                                PPC_ANDI(r_A, r_A, K);
                        else {
@@ -241,51 +234,51 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                                PPC_AND(r_A, r_A, r_scratch1);
                        }
                        break;
-               case BPF_S_ALU_OR_X:
+               case BPF_ALU | BPF_OR | BPF_X:
                        ctx->seen |= SEEN_XREG;
                        PPC_OR(r_A, r_A, r_X);
                        break;
-               case BPF_S_ALU_OR_K:
+               case BPF_ALU | BPF_OR | BPF_K:
                        if (IMM_L(K))
                                PPC_ORI(r_A, r_A, IMM_L(K));
                        if (K >= 65536)
                                PPC_ORIS(r_A, r_A, IMM_H(K));
                        break;
-               case BPF_S_ANC_ALU_XOR_X:
-               case BPF_S_ALU_XOR_X: /* A ^= X */
+               case BPF_ANC | SKF_AD_ALU_XOR_X:
+               case BPF_ALU | BPF_XOR | BPF_X: /* A ^= X */
                        ctx->seen |= SEEN_XREG;
                        PPC_XOR(r_A, r_A, r_X);
                        break;
-               case BPF_S_ALU_XOR_K: /* A ^= K */
+               case BPF_ALU | BPF_XOR | BPF_K: /* A ^= K */
                        if (IMM_L(K))
                                PPC_XORI(r_A, r_A, IMM_L(K));
                        if (K >= 65536)
                                PPC_XORIS(r_A, r_A, IMM_H(K));
                        break;
-               case BPF_S_ALU_LSH_X: /* A <<= X; */
+               case BPF_ALU | BPF_LSH | BPF_X: /* A <<= X; */
                        ctx->seen |= SEEN_XREG;
                        PPC_SLW(r_A, r_A, r_X);
                        break;
-               case BPF_S_ALU_LSH_K:
+               case BPF_ALU | BPF_LSH | BPF_K:
                        if (K == 0)
                                break;
                        else
                                PPC_SLWI(r_A, r_A, K);
                        break;
-               case BPF_S_ALU_RSH_X: /* A >>= X; */
+               case BPF_ALU | BPF_RSH | BPF_X: /* A >>= X; */
                        ctx->seen |= SEEN_XREG;
                        PPC_SRW(r_A, r_A, r_X);
                        break;
-               case BPF_S_ALU_RSH_K: /* A >>= K; */
+               case BPF_ALU | BPF_RSH | BPF_K: /* A >>= K; */
                        if (K == 0)
                                break;
                        else
                                PPC_SRWI(r_A, r_A, K);
                        break;
-               case BPF_S_ALU_NEG:
+               case BPF_ALU | BPF_NEG:
                        PPC_NEG(r_A, r_A);
                        break;
-               case BPF_S_RET_K:
+               case BPF_RET | BPF_K:
                        PPC_LI32(r_ret, K);
                        if (!K) {
                                if (ctx->pc_ret0 == -1)
@@ -312,7 +305,7 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                                        PPC_BLR();
                        }
                        break;
-               case BPF_S_RET_A:
+               case BPF_RET | BPF_A:
                        PPC_MR(r_ret, r_A);
                        if (i != flen - 1) {
                                if (ctx->seen)
@@ -321,53 +314,53 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                                        PPC_BLR();
                        }
                        break;
-               case BPF_S_MISC_TAX: /* X = A */
+               case BPF_MISC | BPF_TAX: /* X = A */
                        PPC_MR(r_X, r_A);
                        break;
-               case BPF_S_MISC_TXA: /* A = X */
+               case BPF_MISC | BPF_TXA: /* A = X */
                        ctx->seen |= SEEN_XREG;
                        PPC_MR(r_A, r_X);
                        break;
 
                        /*** Constant loads/M[] access ***/
-               case BPF_S_LD_IMM: /* A = K */
+               case BPF_LD | BPF_IMM: /* A = K */
                        PPC_LI32(r_A, K);
                        break;
-               case BPF_S_LDX_IMM: /* X = K */
+               case BPF_LDX | BPF_IMM: /* X = K */
                        PPC_LI32(r_X, K);
                        break;
-               case BPF_S_LD_MEM: /* A = mem[K] */
+               case BPF_LD | BPF_MEM: /* A = mem[K] */
                        PPC_MR(r_A, r_M + (K & 0xf));
                        ctx->seen |= SEEN_MEM | (1<<(K & 0xf));
                        break;
-               case BPF_S_LDX_MEM: /* X = mem[K] */
+               case BPF_LDX | BPF_MEM: /* X = mem[K] */
                        PPC_MR(r_X, r_M + (K & 0xf));
                        ctx->seen |= SEEN_MEM | (1<<(K & 0xf));
                        break;
-               case BPF_S_ST: /* mem[K] = A */
+               case BPF_ST: /* mem[K] = A */
                        PPC_MR(r_M + (K & 0xf), r_A);
                        ctx->seen |= SEEN_MEM | (1<<(K & 0xf));
                        break;
-               case BPF_S_STX: /* mem[K] = X */
+               case BPF_STX: /* mem[K] = X */
                        PPC_MR(r_M + (K & 0xf), r_X);
                        ctx->seen |= SEEN_XREG | SEEN_MEM | (1<<(K & 0xf));
                        break;
-               case BPF_S_LD_W_LEN: /* A = skb->len; */
+               case BPF_LD | BPF_W | BPF_LEN: /*       A = skb->len; */
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4);
                        PPC_LWZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, len));
                        break;
-               case BPF_S_LDX_W_LEN: /* X = skb->len; */
+               case BPF_LDX | BPF_W | BPF_LEN: /* X = skb->len; */
                        PPC_LWZ_OFFS(r_X, r_skb, offsetof(struct sk_buff, len));
                        break;
 
                        /*** Ancillary info loads ***/
-               case BPF_S_ANC_PROTOCOL: /* A = ntohs(skb->protocol); */
+               case BPF_ANC | SKF_AD_PROTOCOL: /* A = ntohs(skb->protocol); */
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff,
                                                  protocol) != 2);
                        PPC_NTOHS_OFFS(r_A, r_skb, offsetof(struct sk_buff,
                                                            protocol));
                        break;
-               case BPF_S_ANC_IFINDEX:
+               case BPF_ANC | SKF_AD_IFINDEX:
                        PPC_LD_OFFS(r_scratch1, r_skb, offsetof(struct sk_buff,
                                                                dev));
                        PPC_CMPDI(r_scratch1, 0);
@@ -384,33 +377,33 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                        PPC_LWZ_OFFS(r_A, r_scratch1,
                                     offsetof(struct net_device, ifindex));
                        break;
-               case BPF_S_ANC_MARK:
+               case BPF_ANC | SKF_AD_MARK:
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4);
                        PPC_LWZ_OFFS(r_A, r_skb, offsetof(struct sk_buff,
                                                          mark));
                        break;
-               case BPF_S_ANC_RXHASH:
+               case BPF_ANC | SKF_AD_RXHASH:
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4);
                        PPC_LWZ_OFFS(r_A, r_skb, offsetof(struct sk_buff,
                                                          hash));
                        break;
-               case BPF_S_ANC_VLAN_TAG:
-               case BPF_S_ANC_VLAN_TAG_PRESENT:
+               case BPF_ANC | SKF_AD_VLAN_TAG:
+               case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2);
                        PPC_LHZ_OFFS(r_A, r_skb, offsetof(struct sk_buff,
                                                          vlan_tci));
-                       if (filter[i].code == BPF_S_ANC_VLAN_TAG)
+                       if (code == (BPF_ANC | SKF_AD_VLAN_TAG))
                                PPC_ANDI(r_A, r_A, VLAN_VID_MASK);
                        else
                                PPC_ANDI(r_A, r_A, VLAN_TAG_PRESENT);
                        break;
-               case BPF_S_ANC_QUEUE:
+               case BPF_ANC | SKF_AD_QUEUE:
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff,
                                                  queue_mapping) != 2);
                        PPC_LHZ_OFFS(r_A, r_skb, offsetof(struct sk_buff,
                                                          queue_mapping));
                        break;
-               case BPF_S_ANC_CPU:
+               case BPF_ANC | SKF_AD_CPU:
 #ifdef CONFIG_SMP
                        /*
                         * PACA ptr is r13:
@@ -426,13 +419,13 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                        break;
 
                        /*** Absolute loads from packet header/data ***/
-               case BPF_S_LD_W_ABS:
+               case BPF_LD | BPF_W | BPF_ABS:
                        func = CHOOSE_LOAD_FUNC(K, sk_load_word);
                        goto common_load;
-               case BPF_S_LD_H_ABS:
+               case BPF_LD | BPF_H | BPF_ABS:
                        func = CHOOSE_LOAD_FUNC(K, sk_load_half);
                        goto common_load;
-               case BPF_S_LD_B_ABS:
+               case BPF_LD | BPF_B | BPF_ABS:
                        func = CHOOSE_LOAD_FUNC(K, sk_load_byte);
                common_load:
                        /* Load from [K]. */
@@ -449,13 +442,13 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                        break;
 
                        /*** Indirect loads from packet header/data ***/
-               case BPF_S_LD_W_IND:
+               case BPF_LD | BPF_W | BPF_IND:
                        func = sk_load_word;
                        goto common_load_ind;
-               case BPF_S_LD_H_IND:
+               case BPF_LD | BPF_H | BPF_IND:
                        func = sk_load_half;
                        goto common_load_ind;
-               case BPF_S_LD_B_IND:
+               case BPF_LD | BPF_B | BPF_IND:
                        func = sk_load_byte;
                common_load_ind:
                        /*
@@ -473,31 +466,31 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                        PPC_BCC(COND_LT, exit_addr);
                        break;
 
-               case BPF_S_LDX_B_MSH:
+               case BPF_LDX | BPF_B | BPF_MSH:
                        func = CHOOSE_LOAD_FUNC(K, sk_load_byte_msh);
                        goto common_load;
                        break;
 
                        /*** Jump and branches ***/
-               case BPF_S_JMP_JA:
+               case BPF_JMP | BPF_JA:
                        if (K != 0)
                                PPC_JMP(addrs[i + 1 + K]);
                        break;
 
-               case BPF_S_JMP_JGT_K:
-               case BPF_S_JMP_JGT_X:
+               case BPF_JMP | BPF_JGT | BPF_K:
+               case BPF_JMP | BPF_JGT | BPF_X:
                        true_cond = COND_GT;
                        goto cond_branch;
-               case BPF_S_JMP_JGE_K:
-               case BPF_S_JMP_JGE_X:
+               case BPF_JMP | BPF_JGE | BPF_K:
+               case BPF_JMP | BPF_JGE | BPF_X:
                        true_cond = COND_GE;
                        goto cond_branch;
-               case BPF_S_JMP_JEQ_K:
-               case BPF_S_JMP_JEQ_X:
+               case BPF_JMP | BPF_JEQ | BPF_K:
+               case BPF_JMP | BPF_JEQ | BPF_X:
                        true_cond = COND_EQ;
                        goto cond_branch;
-               case BPF_S_JMP_JSET_K:
-               case BPF_S_JMP_JSET_X:
+               case BPF_JMP | BPF_JSET | BPF_K:
+               case BPF_JMP | BPF_JSET | BPF_X:
                        true_cond = COND_NE;
                        /* Fall through */
                cond_branch:
@@ -508,20 +501,20 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                                break;
                        }
 
-                       switch (filter[i].code) {
-                       case BPF_S_JMP_JGT_X:
-                       case BPF_S_JMP_JGE_X:
-                       case BPF_S_JMP_JEQ_X:
+                       switch (code) {
+                       case BPF_JMP | BPF_JGT | BPF_X:
+                       case BPF_JMP | BPF_JGE | BPF_X:
+                       case BPF_JMP | BPF_JEQ | BPF_X:
                                ctx->seen |= SEEN_XREG;
                                PPC_CMPLW(r_A, r_X);
                                break;
-                       case BPF_S_JMP_JSET_X:
+                       case BPF_JMP | BPF_JSET | BPF_X:
                                ctx->seen |= SEEN_XREG;
                                PPC_AND_DOT(r_scratch1, r_A, r_X);
                                break;
-                       case BPF_S_JMP_JEQ_K:
-                       case BPF_S_JMP_JGT_K:
-                       case BPF_S_JMP_JGE_K:
+                       case BPF_JMP | BPF_JEQ | BPF_K:
+                       case BPF_JMP | BPF_JGT | BPF_K:
+                       case BPF_JMP | BPF_JGE | BPF_K:
                                if (K < 32768)
                                        PPC_CMPLWI(r_A, K);
                                else {
@@ -529,7 +522,7 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                                        PPC_CMPLW(r_A, r_scratch1);
                                }
                                break;
-                       case BPF_S_JMP_JSET_K:
+                       case BPF_JMP | BPF_JSET | BPF_K:
                                if (K < 32768)
                                        /* PPC_ANDI is /only/ dot-form */
                                        PPC_ANDI(r_scratch1, r_A, K);
index bf9c6d4cd26c34770c53aedcb12fd1a2ed6f4e5c..391b3f6b54a30eb4759ce2a2b898392bb796c0ac 100644 (file)
@@ -19,7 +19,6 @@ source "arch/powerpc/platforms/embedded6xx/Kconfig"
 source "arch/powerpc/platforms/44x/Kconfig"
 source "arch/powerpc/platforms/40x/Kconfig"
 source "arch/powerpc/platforms/amigaone/Kconfig"
-source "arch/powerpc/platforms/wsp/Kconfig"
 
 config KVM_GUEST
        bool "KVM Guest support"
index 43b65ad1970a1a8635e7c517b7cfb95d211bcf84..a41bd023647aff48b21da2d8af80760417f879c4 100644 (file)
@@ -148,10 +148,6 @@ config POWER4
        depends on PPC64 && PPC_BOOK3S
        def_bool y
 
-config PPC_A2
-       bool
-       depends on PPC_BOOK3E_64
-
 config TUNE_CELL
        bool "Optimize for Cell Broadband Engine"
        depends on PPC64 && PPC_BOOK3S
@@ -280,7 +276,7 @@ config VSX
 
 config PPC_ICSWX
        bool "Support for PowerPC icswx coprocessor instruction"
-       depends on POWER4 || PPC_A2
+       depends on POWER4
        default n
        ---help---
 
index 879b4a448498807e08745b731b55ebbe1ab89e8b..469ef170d218e24fadc665623787e70a7dbd39f8 100644 (file)
@@ -22,4 +22,3 @@ obj-$(CONFIG_PPC_CELL)                += cell/
 obj-$(CONFIG_PPC_PS3)          += ps3/
 obj-$(CONFIG_EMBEDDED6xx)      += embedded6xx/
 obj-$(CONFIG_AMIGAONE)         += amigaone/
-obj-$(CONFIG_PPC_WSP)          += wsp/
index 0ba3c9598358e795e39e717287397b2405bca1a6..bcfd6f063efa7e64d4e361af002f6e858ff10620 100644 (file)
@@ -35,7 +35,6 @@
 #define SPUFS_PS_MAP_SIZE      0x20000
 #define SPUFS_MFC_MAP_SIZE     0x1000
 #define SPUFS_CNTL_MAP_SIZE    0x1000
-#define SPUFS_CNTL_MAP_SIZE    0x1000
 #define SPUFS_SIGNAL_MAP_SIZE  PAGE_SIZE
 #define SPUFS_MSS_MAP_SIZE     0x1000
 
index c252ee95bddf31b17e676f0d46a852880502f131..45a8ed0585cd17cd1aef9675d759e7342c8ca334 100644 (file)
@@ -17,6 +17,7 @@ config PPC_POWERNV
        select CPU_FREQ_GOV_USERSPACE
        select CPU_FREQ_GOV_ONDEMAND
        select CPU_FREQ_GOV_CONSERVATIVE
+       select PPC_DOORBELL
        default y
 
 config PPC_POWERNV_RTAS
index 4ad0d345bc960eeec309848c4edc0e2fa23e6970..d55891f89a2ce2bf0c14b22b47564760dd16abf0 100644 (file)
@@ -1,9 +1,9 @@
 obj-y                  += setup.o opal-takeover.o opal-wrappers.o opal.o opal-async.o
 obj-y                  += opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o
 obj-y                  += rng.o opal-elog.o opal-dump.o opal-sysparam.o opal-sensor.o
-obj-y                  += opal-msglog.o subcore.o subcore-asm.o
+obj-y                  += opal-msglog.o
 
-obj-$(CONFIG_SMP)      += smp.o
+obj-$(CONFIG_SMP)      += smp.o subcore.o subcore-asm.o
 obj-$(CONFIG_PCI)      += pci.o pci-p5ioc2.o pci-ioda.o
 obj-$(CONFIG_EEH)      += eeh-ioda.o eeh-powernv.o
 obj-$(CONFIG_PPC_SCOM) += opal-xscom.o
index 753f08e36dfaa8d4cfa7884b9f27480899abbca7..8ad0c5b891f4e3083e3b554b980f0eae54a51ab7 100644 (file)
@@ -267,7 +267,7 @@ static int ioda_eeh_get_state(struct eeh_pe *pe)
 {
        s64 ret = 0;
        u8 fstate;
-       u16 pcierr;
+       __be16 pcierr;
        u32 pe_no;
        int result;
        struct pci_controller *hose = pe->phb;
@@ -316,7 +316,7 @@ static int ioda_eeh_get_state(struct eeh_pe *pe)
                result = 0;
                result &= ~EEH_STATE_RESET_ACTIVE;
 
-               if (pcierr != OPAL_EEH_PHB_ERROR) {
+               if (be16_to_cpu(pcierr) != OPAL_EEH_PHB_ERROR) {
                        result |= EEH_STATE_MMIO_ACTIVE;
                        result |= EEH_STATE_DMA_ACTIVE;
                        result |= EEH_STATE_MMIO_ENABLED;
@@ -705,18 +705,19 @@ static int ioda_eeh_next_error(struct eeh_pe **pe)
 {
        struct pci_controller *hose;
        struct pnv_phb *phb;
-       struct eeh_pe *phb_pe;
-       u64 frozen_pe_no;
-       u16 err_type, severity;
+       struct eeh_pe *phb_pe, *parent_pe;
+       __be64 frozen_pe_no;
+       __be16 err_type, severity;
+       int active_flags = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE);
        long rc;
-       int ret = EEH_NEXT_ERR_NONE;
+       int state, ret = EEH_NEXT_ERR_NONE;
 
        /*
         * While running here, it's safe to purge the event queue.
         * And we should keep the cached OPAL notifier event sychronized
         * between the kernel and firmware.
         */
-       eeh_remove_event(NULL);
+       eeh_remove_event(NULL, false);
        opal_notifier_update_evt(OPAL_EVENT_PCI_ERROR, 0x0ul);
 
        list_for_each_entry(hose, &hose_list, list_node) {
@@ -742,8 +743,8 @@ static int ioda_eeh_next_error(struct eeh_pe **pe)
                }
 
                /* If the PHB doesn't have error, stop processing */
-               if (err_type == OPAL_EEH_NO_ERROR ||
-                   severity == OPAL_EEH_SEV_NO_ERROR) {
+               if (be16_to_cpu(err_type) == OPAL_EEH_NO_ERROR ||
+                   be16_to_cpu(severity) == OPAL_EEH_SEV_NO_ERROR) {
                        pr_devel("%s: No error found on PHB#%x\n",
                                 __func__, hose->global_number);
                        continue;
@@ -755,14 +756,14 @@ static int ioda_eeh_next_error(struct eeh_pe **pe)
                 * specific PHB.
                 */
                pr_devel("%s: Error (%d, %d, %llu) on PHB#%x\n",
-                        __func__, err_type, severity,
-                        frozen_pe_no, hose->global_number);
-               switch (err_type) {
+                        __func__, be16_to_cpu(err_type), be16_to_cpu(severity),
+                        be64_to_cpu(frozen_pe_no), hose->global_number);
+               switch (be16_to_cpu(err_type)) {
                case OPAL_EEH_IOC_ERROR:
-                       if (severity == OPAL_EEH_SEV_IOC_DEAD) {
+                       if (be16_to_cpu(severity) == OPAL_EEH_SEV_IOC_DEAD) {
                                pr_err("EEH: dead IOC detected\n");
                                ret = EEH_NEXT_ERR_DEAD_IOC;
-                       } else if (severity == OPAL_EEH_SEV_INF) {
+                       } else if (be16_to_cpu(severity) == OPAL_EEH_SEV_INF) {
                                pr_info("EEH: IOC informative error "
                                        "detected\n");
                                ioda_eeh_hub_diag(hose);
@@ -771,20 +772,26 @@ static int ioda_eeh_next_error(struct eeh_pe **pe)
 
                        break;
                case OPAL_EEH_PHB_ERROR:
-                       if (severity == OPAL_EEH_SEV_PHB_DEAD) {
+                       if (be16_to_cpu(severity) == OPAL_EEH_SEV_PHB_DEAD) {
                                *pe = phb_pe;
-                               pr_err("EEH: dead PHB#%x detected\n",
-                                       hose->global_number);
+                               pr_err("EEH: dead PHB#%x detected, "
+                                      "location: %s\n",
+                                      hose->global_number,
+                                      eeh_pe_loc_get(phb_pe));
                                ret = EEH_NEXT_ERR_DEAD_PHB;
-                       } else if (severity == OPAL_EEH_SEV_PHB_FENCED) {
+                       } else if (be16_to_cpu(severity) ==
+                                               OPAL_EEH_SEV_PHB_FENCED) {
                                *pe = phb_pe;
-                               pr_err("EEH: fenced PHB#%x detected\n",
-                                       hose->global_number);
+                               pr_err("EEH: Fenced PHB#%x detected, "
+                                      "location: %s\n",
+                                      hose->global_number,
+                                      eeh_pe_loc_get(phb_pe));
                                ret = EEH_NEXT_ERR_FENCED_PHB;
-                       } else if (severity == OPAL_EEH_SEV_INF) {
+                       } else if (be16_to_cpu(severity) == OPAL_EEH_SEV_INF) {
                                pr_info("EEH: PHB#%x informative error "
-                                       "detected\n",
-                                       hose->global_number);
+                                       "detected, location: %s\n",
+                                       hose->global_number,
+                                       eeh_pe_loc_get(phb_pe));
                                ioda_eeh_phb_diag(hose);
                                ret = EEH_NEXT_ERR_NONE;
                        }
@@ -792,34 +799,33 @@ static int ioda_eeh_next_error(struct eeh_pe **pe)
                        break;
                case OPAL_EEH_PE_ERROR:
                        /*
-                        * If we can't find the corresponding PE, the
-                        * PEEV / PEST would be messy. So we force an
-                        * fenced PHB so that it can be recovered.
-                        *
-                        * If the PE has been marked as isolated, that
-                        * should have been removed permanently or in
-                        * progress with recovery. We needn't report
-                        * it again.
+                        * If we can't find the corresponding PE, we
+                        * just try to unfreeze.
                         */
-                       if (ioda_eeh_get_pe(hose, frozen_pe_no, pe)) {
-                               *pe = phb_pe;
-                               pr_err("EEH: Escalated fenced PHB#%x "
-                                      "detected for PE#%llx\n",
-                                       hose->global_number,
-                                       frozen_pe_no);
-                               ret = EEH_NEXT_ERR_FENCED_PHB;
+                       if (ioda_eeh_get_pe(hose,
+                                           be64_to_cpu(frozen_pe_no), pe)) {
+                               /* Try best to clear it */
+                               pr_info("EEH: Clear non-existing PHB#%x-PE#%llx\n",
+                                       hose->global_number, frozen_pe_no);
+                               pr_info("EEH: PHB location: %s\n",
+                                       eeh_pe_loc_get(phb_pe));
+                               opal_pci_eeh_freeze_clear(phb->opal_id, frozen_pe_no,
+                                       OPAL_EEH_ACTION_CLEAR_FREEZE_ALL);
+                               ret = EEH_NEXT_ERR_NONE;
                        } else if ((*pe)->state & EEH_PE_ISOLATED) {
                                ret = EEH_NEXT_ERR_NONE;
                        } else {
                                pr_err("EEH: Frozen PE#%x on PHB#%x detected\n",
                                        (*pe)->addr, (*pe)->phb->global_number);
+                               pr_err("EEH: PE location: %s, PHB location: %s\n",
+                                       eeh_pe_loc_get(*pe), eeh_pe_loc_get(phb_pe));
                                ret = EEH_NEXT_ERR_FROZEN_PE;
                        }
 
                        break;
                default:
                        pr_warn("%s: Unexpected error type %d\n",
-                               __func__, err_type);
+                               __func__, be16_to_cpu(err_type));
                }
 
                /*
@@ -836,6 +842,31 @@ static int ioda_eeh_next_error(struct eeh_pe **pe)
                        ioda_eeh_phb_diag(hose);
                }
 
+               /*
+                * We probably have the frozen parent PE out there and
+                * we need have to handle frozen parent PE firstly.
+                */
+               if (ret == EEH_NEXT_ERR_FROZEN_PE) {
+                       parent_pe = (*pe)->parent;
+                       while (parent_pe) {
+                               /* Hit the ceiling ? */
+                               if (parent_pe->type & EEH_PE_PHB)
+                                       break;
+
+                               /* Frozen parent PE ? */
+                               state = ioda_eeh_get_state(parent_pe);
+                               if (state > 0 &&
+                                   (state & active_flags) != active_flags)
+                                       *pe = parent_pe;
+
+                               /* Next parent level */
+                               parent_pe = parent_pe->parent;
+                       }
+
+                       /* We possibly migrate to another PE */
+                       eeh_pe_state_mark(*pe, EEH_PE_ISOLATED);
+               }
+
                /*
                 * If we have no errors on the specific PHB or only
                 * informative error there, we continue poking it.
index 1bb25b9525046f14abfa2e38c2db188b0686ff94..44ed78af1a0dd53bb9e33f16b3ce465ffc5c6f4f 100644 (file)
@@ -37,7 +37,8 @@ static ssize_t opal_msglog_read(struct file *file, struct kobject *kobj,
 {
        struct memcons *mc = bin_attr->private;
        const char *conbuf;
-       size_t ret, first_read = 0;
+       ssize_t ret;
+       size_t first_read = 0;
        uint32_t out_pos, avail;
 
        if (!mc)
@@ -69,6 +70,9 @@ static ssize_t opal_msglog_read(struct file *file, struct kobject *kobj,
                to += first_read;
                count -= first_read;
                pos -= avail;
+
+               if (count <= 0)
+                       goto out;
        }
 
        /* Sanity check. The firmware should not do this to us. */
index d202f9bc3683f5ad0072282173ddfb510b1aec9a..9d1acf22a099dfc1bf474f6244af11d1153742f6 100644 (file)
@@ -260,10 +260,10 @@ void __init opal_sys_param_init(void)
                        attr[i].kobj_attr.attr.mode = S_IRUGO;
                        break;
                case OPAL_SYSPARAM_WRITE:
-                       attr[i].kobj_attr.attr.mode = S_IWUGO;
+                       attr[i].kobj_attr.attr.mode = S_IWUSR;
                        break;
                case OPAL_SYSPARAM_RW:
-                       attr[i].kobj_attr.attr.mode = S_IRUGO | S_IWUGO;
+                       attr[i].kobj_attr.attr.mode = S_IRUGO | S_IWUSR;
                        break;
                default:
                        break;
index eefbfcc3fd8c40a9d50b9839fb2292f723c7970d..f91a4e5d872e23adc5eacc274d8b07226ad000b0 100644 (file)
@@ -206,72 +206,91 @@ static void pnv_pci_dump_phb3_diag_data(struct pci_controller *hose,
 
        data = (struct OpalIoPhb3ErrorData*)common;
        pr_info("PHB3 PHB#%d Diag-data (Version: %d)\n",
-               hose->global_number, common->version);
+               hose->global_number, be32_to_cpu(common->version));
        if (data->brdgCtl)
                pr_info("brdgCtl:     %08x\n",
-                       data->brdgCtl);
+                       be32_to_cpu(data->brdgCtl));
        if (data->portStatusReg || data->rootCmplxStatus ||
            data->busAgentStatus)
                pr_info("UtlSts:      %08x %08x %08x\n",
-                       data->portStatusReg, data->rootCmplxStatus,
-                       data->busAgentStatus);
+                       be32_to_cpu(data->portStatusReg),
+                       be32_to_cpu(data->rootCmplxStatus),
+                       be32_to_cpu(data->busAgentStatus));
        if (data->deviceStatus || data->slotStatus   ||
            data->linkStatus   || data->devCmdStatus ||
            data->devSecStatus)
                pr_info("RootSts:     %08x %08x %08x %08x %08x\n",
-                       data->deviceStatus, data->slotStatus,
-                       data->linkStatus, data->devCmdStatus,
-                       data->devSecStatus);
+                       be32_to_cpu(data->deviceStatus),
+                       be32_to_cpu(data->slotStatus),
+                       be32_to_cpu(data->linkStatus),
+                       be32_to_cpu(data->devCmdStatus),
+                       be32_to_cpu(data->devSecStatus));
        if (data->rootErrorStatus || data->uncorrErrorStatus ||
            data->corrErrorStatus)
                pr_info("RootErrSts:  %08x %08x %08x\n",
-                       data->rootErrorStatus, data->uncorrErrorStatus,
-                       data->corrErrorStatus);
+                       be32_to_cpu(data->rootErrorStatus),
+                       be32_to_cpu(data->uncorrErrorStatus),
+                       be32_to_cpu(data->corrErrorStatus));
        if (data->tlpHdr1 || data->tlpHdr2 ||
            data->tlpHdr3 || data->tlpHdr4)
                pr_info("RootErrLog:  %08x %08x %08x %08x\n",
-                       data->tlpHdr1, data->tlpHdr2,
-                       data->tlpHdr3, data->tlpHdr4);
+                       be32_to_cpu(data->tlpHdr1),
+                       be32_to_cpu(data->tlpHdr2),
+                       be32_to_cpu(data->tlpHdr3),
+                       be32_to_cpu(data->tlpHdr4));
        if (data->sourceId || data->errorClass ||
            data->correlator)
                pr_info("RootErrLog1: %08x %016llx %016llx\n",
-                       data->sourceId, data->errorClass,
-                       data->correlator);
+                       be32_to_cpu(data->sourceId),
+                       be64_to_cpu(data->errorClass),
+                       be64_to_cpu(data->correlator));
        if (data->nFir)
                pr_info("nFir:        %016llx %016llx %016llx\n",
-                       data->nFir, data->nFirMask,
-                       data->nFirWOF);
+                       be64_to_cpu(data->nFir),
+                       be64_to_cpu(data->nFirMask),
+                       be64_to_cpu(data->nFirWOF));
        if (data->phbPlssr || data->phbCsr)
                pr_info("PhbSts:      %016llx %016llx\n",
-                       data->phbPlssr, data->phbCsr);
+                       be64_to_cpu(data->phbPlssr),
+                       be64_to_cpu(data->phbCsr));
        if (data->lemFir)
                pr_info("Lem:         %016llx %016llx %016llx\n",
-                       data->lemFir, data->lemErrorMask,
-                       data->lemWOF);
+                       be64_to_cpu(data->lemFir),
+                       be64_to_cpu(data->lemErrorMask),
+                       be64_to_cpu(data->lemWOF));
        if (data->phbErrorStatus)
                pr_info("PhbErr:      %016llx %016llx %016llx %016llx\n",
-                       data->phbErrorStatus, data->phbFirstErrorStatus,
-                       data->phbErrorLog0, data->phbErrorLog1);
+                       be64_to_cpu(data->phbErrorStatus),
+                       be64_to_cpu(data->phbFirstErrorStatus),
+                       be64_to_cpu(data->phbErrorLog0),
+                       be64_to_cpu(data->phbErrorLog1));
        if (data->mmioErrorStatus)
                pr_info("OutErr:      %016llx %016llx %016llx %016llx\n",
-                       data->mmioErrorStatus, data->mmioFirstErrorStatus,
-                       data->mmioErrorLog0, data->mmioErrorLog1);
+                       be64_to_cpu(data->mmioErrorStatus),
+                       be64_to_cpu(data->mmioFirstErrorStatus),
+                       be64_to_cpu(data->mmioErrorLog0),
+                       be64_to_cpu(data->mmioErrorLog1));
        if (data->dma0ErrorStatus)
                pr_info("InAErr:      %016llx %016llx %016llx %016llx\n",
-                       data->dma0ErrorStatus, data->dma0FirstErrorStatus,
-                       data->dma0ErrorLog0, data->dma0ErrorLog1);
+                       be64_to_cpu(data->dma0ErrorStatus),
+                       be64_to_cpu(data->dma0FirstErrorStatus),
+                       be64_to_cpu(data->dma0ErrorLog0),
+                       be64_to_cpu(data->dma0ErrorLog1));
        if (data->dma1ErrorStatus)
                pr_info("InBErr:      %016llx %016llx %016llx %016llx\n",
-                       data->dma1ErrorStatus, data->dma1FirstErrorStatus,
-                       data->dma1ErrorLog0, data->dma1ErrorLog1);
+                       be64_to_cpu(data->dma1ErrorStatus),
+                       be64_to_cpu(data->dma1FirstErrorStatus),
+                       be64_to_cpu(data->dma1ErrorLog0),
+                       be64_to_cpu(data->dma1ErrorLog1));
 
        for (i = 0; i < OPAL_PHB3_NUM_PEST_REGS; i++) {
-               if ((data->pestA[i] >> 63) == 0 &&
-                   (data->pestB[i] >> 63) == 0)
+               if ((be64_to_cpu(data->pestA[i]) >> 63) == 0 &&
+                   (be64_to_cpu(data->pestB[i]) >> 63) == 0)
                        continue;
 
                pr_info("PE[%3d] A/B: %016llx %016llx\n",
-                       i, data->pestA[i], data->pestB[i]);
+                               i, be64_to_cpu(data->pestA[i]),
+                               be64_to_cpu(data->pestB[i]));
        }
 }
 
@@ -284,7 +303,7 @@ void pnv_pci_dump_phb_diag_data(struct pci_controller *hose,
                return;
 
        common = (struct OpalIoPhbErrorCommon *)log_buff;
-       switch (common->ioType) {
+       switch (be32_to_cpu(common->ioType)) {
        case OPAL_PHB_ERROR_DATA_TYPE_P7IOC:
                pnv_pci_dump_p7ioc_diag_data(hose, common);
                break;
@@ -293,7 +312,7 @@ void pnv_pci_dump_phb_diag_data(struct pci_controller *hose,
                break;
        default:
                pr_warn("%s: Unrecognized ioType %d\n",
-                       __func__, common->ioType);
+                       __func__, be32_to_cpu(common->ioType));
        }
 }
 
index 8c16a5f9672809fbbbc3bdfb934459f9d3d2ebb8..d9b88fa7c5a349f26c040b20dcf5e72a67de8860 100644 (file)
 #include <asm/rtas.h>
 #include <asm/opal.h>
 #include <asm/kexec.h>
+#include <asm/smp.h>
 
 #include "powernv.h"
 
 static void __init pnv_setup_arch(void)
 {
+       set_arch_panic_timeout(10, ARCH_PANIC_TIMEOUT);
+
        /* Initialize SMP */
        pnv_smp_init();
 
index 0062a43a2e0d8e96698cf0cb090adce0170b4581..5fcfcf44e3a9b949e6536ebcf53038645286fde6 100644 (file)
@@ -32,6 +32,7 @@
 #include <asm/opal.h>
 #include <asm/runlatch.h>
 #include <asm/code-patching.h>
+#include <asm/dbell.h>
 
 #include "powernv.h"
 
@@ -46,6 +47,11 @@ static void pnv_smp_setup_cpu(int cpu)
 {
        if (cpu != boot_cpuid)
                xics_setup_cpu();
+
+#ifdef CONFIG_PPC_DOORBELL
+       if (cpu_has_feature(CPU_FTR_DBELL))
+               doorbell_setup_this_cpu();
+#endif
 }
 
 int pnv_smp_kick_cpu(int nr)
index 2cb8b776c84a5660e5ef225cc004fbdcc9109f96..756b482f819a425a1dcd74bdfe0ab178f6b0617d 100644 (file)
@@ -21,6 +21,7 @@ config PPC_PSERIES
        select HAVE_CONTEXT_TRACKING
        select HOTPLUG_CPU if SMP
        select ARCH_RANDOM
+       select PPC_DOORBELL
        default y
 
 config PPC_SPLPAR
diff --git a/arch/powerpc/platforms/wsp/Kconfig b/arch/powerpc/platforms/wsp/Kconfig
deleted file mode 100644 (file)
index 422a175..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-config PPC_WSP
-       bool
-       select PPC_A2
-       select GENERIC_TBSYNC
-       select PPC_ICSWX
-       select PPC_SCOM
-       select PPC_XICS
-       select PPC_ICP_NATIVE
-       select PCI
-       select PPC_IO_WORKAROUNDS if PCI
-       select PPC_INDIRECT_PIO if PCI
-       default n
-
-menu "WSP platform selection"
-       depends on PPC_BOOK3E_64
-
-config PPC_PSR2
-       bool "PowerEN System Reference Platform 2"
-       select EPAPR_BOOT
-       select PPC_WSP
-       default y
-
-config PPC_CHROMA
-       bool "PowerEN PCIe Chroma Card"
-       select EPAPR_BOOT
-       select PPC_WSP
-       select OF_DYNAMIC
-       default y
-
-endmenu
diff --git a/arch/powerpc/platforms/wsp/Makefile b/arch/powerpc/platforms/wsp/Makefile
deleted file mode 100644 (file)
index 162fc60..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-ccflags-y                      += $(NO_MINIMAL_TOC)
-
-obj-y                          += setup.o ics.o wsp.o
-obj-$(CONFIG_PPC_PSR2)         += psr2.o
-obj-$(CONFIG_PPC_CHROMA)       += chroma.o h8.o
-obj-$(CONFIG_PPC_WSP)          += opb_pic.o
-obj-$(CONFIG_PPC_WSP)          += scom_wsp.o
-obj-$(CONFIG_SMP)              += smp.o scom_smp.o
-obj-$(CONFIG_PCI)              += wsp_pci.o
-obj-$(CONFIG_PCI_MSI)          += msi.o
diff --git a/arch/powerpc/platforms/wsp/chroma.c b/arch/powerpc/platforms/wsp/chroma.c
deleted file mode 100644 (file)
index aaa46b3..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2008-2011, IBM Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/irq.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/of.h>
-#include <linux/smp.h>
-#include <linux/time.h>
-#include <linux/of_fdt.h>
-
-#include <asm/machdep.h>
-#include <asm/udbg.h>
-
-#include "ics.h"
-#include "wsp.h"
-
-void __init chroma_setup_arch(void)
-{
-       wsp_setup_arch();
-       wsp_setup_h8();
-
-}
-
-static int __init chroma_probe(void)
-{
-       unsigned long root = of_get_flat_dt_root();
-
-       if (!of_flat_dt_is_compatible(root, "ibm,wsp-chroma"))
-               return 0;
-
-       return 1;
-}
-
-define_machine(chroma_md) {
-       .name                   = "Chroma PCIe",
-       .probe                  = chroma_probe,
-       .setup_arch             = chroma_setup_arch,
-       .restart                = wsp_h8_restart,
-       .power_off              = wsp_h8_power_off,
-       .halt                   = wsp_halt,
-       .calibrate_decr         = generic_calibrate_decr,
-       .init_IRQ               = wsp_setup_irq,
-       .progress               = udbg_progress,
-       .power_save             = book3e_idle,
-};
-
-machine_arch_initcall(chroma_md, wsp_probe_devices);
diff --git a/arch/powerpc/platforms/wsp/h8.c b/arch/powerpc/platforms/wsp/h8.c
deleted file mode 100644 (file)
index a3c87f3..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright 2008-2011, IBM Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-#include <linux/kernel.h>
-#include <linux/of.h>
-#include <linux/io.h>
-#include <linux/of_address.h>
-
-#include "wsp.h"
-
-/*
- * The UART connection to the H8 is over ttyS1 which is just a 16550.
- * We assume that FW has it setup right and no one messes with it.
- */
-
-
-static u8 __iomem *h8;
-
-#define RBR 0          /* Receiver Buffer Register */
-#define THR 0          /* Transmitter Holding Register */
-#define LSR 5          /* Line Status Register */
-#define LSR_DR 0x01    /* LSR value for Data-Ready */
-#define LSR_THRE 0x20  /* LSR value for Transmitter-Holding-Register-Empty */
-static void wsp_h8_putc(int c)
-{
-       u8 lsr;
-
-       do {
-               lsr = readb(h8 + LSR);
-       } while ((lsr & LSR_THRE) != LSR_THRE);
-       writeb(c, h8 + THR);
-}
-
-static int wsp_h8_getc(void)
-{
-       u8 lsr;
-
-       do {
-               lsr = readb(h8 + LSR);
-       } while ((lsr & LSR_DR) != LSR_DR);
-
-       return readb(h8 + RBR);
-}
-
-static void wsp_h8_puts(const char *s, int sz)
-{
-       int i;
-
-       for (i = 0; i < sz; i++) {
-               wsp_h8_putc(s[i]);
-
-               /* no flow control so wait for echo */
-               wsp_h8_getc();
-       }
-       wsp_h8_putc('\r');
-       wsp_h8_putc('\n');
-}
-
-static void wsp_h8_terminal_cmd(const char *cmd, int sz)
-{
-       hard_irq_disable();
-       wsp_h8_puts(cmd, sz);
-       /* should never return, but just in case */
-       for (;;)
-               continue;
-}
-
-
-void wsp_h8_restart(char *cmd)
-{
-       static const char restart[] = "warm-reset";
-
-       (void)cmd;
-       wsp_h8_terminal_cmd(restart, sizeof(restart) - 1);
-}
-
-void wsp_h8_power_off(void)
-{
-       static const char off[] = "power-off";
-
-       wsp_h8_terminal_cmd(off, sizeof(off) - 1);
-}
-
-static void __iomem *wsp_h8_getaddr(void)
-{
-       struct device_node *aliases;
-       struct device_node *uart;
-       struct property *path;
-       void __iomem *va = NULL;
-
-       /*
-        * there is nothing in the devtree to tell us which is mapped
-        * to the H8, but se know it is the second serial port.
-        */
-
-       aliases = of_find_node_by_path("/aliases");
-       if (aliases == NULL)
-               return NULL;
-
-       path = of_find_property(aliases, "serial1", NULL);
-       if (path == NULL)
-               goto out;
-
-       uart = of_find_node_by_path(path->value);
-       if (uart == NULL)
-               goto out;
-
-       va = of_iomap(uart, 0);
-
-       /* remove it so no one messes with it */
-       of_detach_node(uart);
-       of_node_put(uart);
-
-out:
-       of_node_put(aliases);
-
-       return va;
-}
-
-void __init wsp_setup_h8(void)
-{
-       h8 = wsp_h8_getaddr();
-
-       /* Devtree change? lets hard map it anyway */
-       if (h8 == NULL) {
-               pr_warn("UART to H8 could not be found");
-               h8 = ioremap(0xffc0008000ULL, 0x100);
-       }
-}
diff --git a/arch/powerpc/platforms/wsp/ics.c b/arch/powerpc/platforms/wsp/ics.c
deleted file mode 100644 (file)
index 9cd92e6..0000000
+++ /dev/null
@@ -1,762 +0,0 @@
-/*
- * Copyright 2008-2011 IBM Corporation.
- *
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU General Public License
- *  as published by the Free Software Foundation; either version
- *  2 of the License, or (at your option) any later version.
- */
-
-#include <linux/cpu.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/kernel.h>
-#include <linux/msi.h>
-#include <linux/of.h>
-#include <linux/slab.h>
-#include <linux/smp.h>
-#include <linux/spinlock.h>
-#include <linux/types.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/xics.h>
-
-#include "wsp.h"
-#include "ics.h"
-
-
-/* WSP ICS */
-
-struct wsp_ics {
-       struct ics ics;
-       struct device_node *dn;
-       void __iomem *regs;
-       spinlock_t lock;
-       unsigned long *bitmap;
-       u32 chip_id;
-       u32 lsi_base;
-       u32 lsi_count;
-       u64 hwirq_start;
-       u64 count;
-#ifdef CONFIG_SMP
-       int *hwirq_cpu_map;
-#endif
-};
-
-#define to_wsp_ics(ics)        container_of(ics, struct wsp_ics, ics)
-
-#define INT_SRC_LAYER_BUID_REG(base)   ((base) + 0x00)
-#define IODA_TBL_ADDR_REG(base)                ((base) + 0x18)
-#define IODA_TBL_DATA_REG(base)                ((base) + 0x20)
-#define XIVE_UPDATE_REG(base)          ((base) + 0x28)
-#define ICS_INT_CAPS_REG(base)         ((base) + 0x30)
-
-#define TBL_AUTO_INCREMENT     ((1UL << 63) | (1UL << 15))
-#define TBL_SELECT_XIST                (1UL << 48)
-#define TBL_SELECT_XIVT                (1UL << 49)
-
-#define IODA_IRQ(irq)          ((irq) & (0x7FFULL))    /* HRM 5.1.3.4 */
-
-#define XIST_REQUIRED          0x8
-#define XIST_REJECTED          0x4
-#define XIST_PRESENTED         0x2
-#define XIST_PENDING           0x1
-
-#define XIVE_SERVER_SHIFT      42
-#define XIVE_SERVER_MASK       0xFFFFULL
-#define XIVE_PRIORITY_MASK     0xFFULL
-#define XIVE_PRIORITY_SHIFT    32
-#define XIVE_WRITE_ENABLE      (1ULL << 63)
-
-/*
- * The docs refer to a 6 bit field called ChipID, which consists of a
- * 3 bit NodeID and a 3 bit ChipID. On WSP the ChipID is always zero
- * so we ignore it, and every where we use "chip id" in this code we
- * mean the NodeID.
- */
-#define WSP_ICS_CHIP_SHIFT             17
-
-
-static struct wsp_ics *ics_list;
-static int num_ics;
-
-/* ICS Source controller accessors */
-
-static u64 wsp_ics_get_xive(struct wsp_ics *ics, unsigned int irq)
-{
-       unsigned long flags;
-       u64 xive;
-
-       spin_lock_irqsave(&ics->lock, flags);
-       out_be64(IODA_TBL_ADDR_REG(ics->regs), TBL_SELECT_XIVT | IODA_IRQ(irq));
-       xive = in_be64(IODA_TBL_DATA_REG(ics->regs));
-       spin_unlock_irqrestore(&ics->lock, flags);
-
-       return xive;
-}
-
-static void wsp_ics_set_xive(struct wsp_ics *ics, unsigned int irq, u64 xive)
-{
-       xive &= ~XIVE_ADDR_MASK;
-       xive |= (irq & XIVE_ADDR_MASK);
-       xive |= XIVE_WRITE_ENABLE;
-
-       out_be64(XIVE_UPDATE_REG(ics->regs), xive);
-}
-
-static u64 xive_set_server(u64 xive, unsigned int server)
-{
-       u64 mask = ~(XIVE_SERVER_MASK << XIVE_SERVER_SHIFT);
-
-       xive &= mask;
-       xive |= (server & XIVE_SERVER_MASK) << XIVE_SERVER_SHIFT;
-
-       return xive;
-}
-
-static u64 xive_set_priority(u64 xive, unsigned int priority)
-{
-       u64 mask = ~(XIVE_PRIORITY_MASK << XIVE_PRIORITY_SHIFT);
-
-       xive &= mask;
-       xive |= (priority & XIVE_PRIORITY_MASK) << XIVE_PRIORITY_SHIFT;
-
-       return xive;
-}
-
-
-#ifdef CONFIG_SMP
-/* Find logical CPUs within mask on a given chip and store result in ret */
-void cpus_on_chip(int chip_id, cpumask_t *mask, cpumask_t *ret)
-{
-       int cpu, chip;
-       struct device_node *cpu_dn, *dn;
-       const u32 *prop;
-
-       cpumask_clear(ret);
-       for_each_cpu(cpu, mask) {
-               cpu_dn = of_get_cpu_node(cpu, NULL);
-               if (!cpu_dn)
-                       continue;
-
-               prop = of_get_property(cpu_dn, "at-node", NULL);
-               if (!prop) {
-                       of_node_put(cpu_dn);
-                       continue;
-               }
-
-               dn = of_find_node_by_phandle(*prop);
-               of_node_put(cpu_dn);
-
-               chip = wsp_get_chip_id(dn);
-               if (chip == chip_id)
-                       cpumask_set_cpu(cpu, ret);
-
-               of_node_put(dn);
-       }
-}
-
-/* Store a suitable CPU to handle a hwirq in the ics->hwirq_cpu_map cache */
-static int cache_hwirq_map(struct wsp_ics *ics, unsigned int hwirq,
-                          const cpumask_t *affinity)
-{
-       cpumask_var_t avail, newmask;
-       int ret = -ENOMEM, cpu, cpu_rover = 0, target;
-       int index = hwirq - ics->hwirq_start;
-       unsigned int nodeid;
-
-       BUG_ON(index < 0 || index >= ics->count);
-
-       if (!ics->hwirq_cpu_map)
-               return -ENOMEM;
-
-       if (!distribute_irqs) {
-               ics->hwirq_cpu_map[hwirq - ics->hwirq_start] = xics_default_server;
-               return 0;
-       }
-
-       /* Allocate needed CPU masks */
-       if (!alloc_cpumask_var(&avail, GFP_KERNEL))
-               goto ret;
-       if (!alloc_cpumask_var(&newmask, GFP_KERNEL))
-               goto freeavail;
-
-       /* Find PBus attached to the source of this IRQ */
-       nodeid = (hwirq >> WSP_ICS_CHIP_SHIFT) & 0x3; /* 12:14 */
-
-       /* Find CPUs that could handle this IRQ */
-       if (affinity)
-               cpumask_and(avail, cpu_online_mask, affinity);
-       else
-               cpumask_copy(avail, cpu_online_mask);
-
-       /* Narrow selection down to logical CPUs on the same chip */
-       cpus_on_chip(nodeid, avail, newmask);
-
-       /* Ensure we haven't narrowed it down to 0 */
-       if (unlikely(cpumask_empty(newmask))) {
-               if (unlikely(cpumask_empty(avail))) {
-                       ret = -1;
-                       goto out;
-               }
-               cpumask_copy(newmask, avail);
-       }
-
-       /* Choose a CPU out of those we narrowed it down to in round robin */
-       target = hwirq % cpumask_weight(newmask);
-       for_each_cpu(cpu, newmask) {
-               if (cpu_rover++ >= target) {
-                       ics->hwirq_cpu_map[index] = get_hard_smp_processor_id(cpu);
-                       ret = 0;
-                       goto out;
-               }
-       }
-
-       /* Shouldn't happen */
-       WARN_ON(1);
-
-out:
-       free_cpumask_var(newmask);
-freeavail:
-       free_cpumask_var(avail);
-ret:
-       if (ret < 0) {
-               ics->hwirq_cpu_map[index] = cpumask_first(cpu_online_mask);
-               pr_warning("Error, falling hwirq 0x%x routing back to CPU %i\n",
-                          hwirq, ics->hwirq_cpu_map[index]);
-       }
-       return ret;
-}
-
-static void alloc_irq_map(struct wsp_ics *ics)
-{
-       int i;
-
-       ics->hwirq_cpu_map = kmalloc(sizeof(int) * ics->count, GFP_KERNEL);
-       if (!ics->hwirq_cpu_map) {
-               pr_warning("Allocate hwirq_cpu_map failed, "
-                          "IRQ balancing disabled\n");
-               return;
-       }
-
-       for (i=0; i < ics->count; i++)
-               ics->hwirq_cpu_map[i] = xics_default_server;
-}
-
-static int get_irq_server(struct wsp_ics *ics, unsigned int hwirq)
-{
-       int index = hwirq - ics->hwirq_start;
-
-       BUG_ON(index < 0 || index >= ics->count);
-
-       if (!ics->hwirq_cpu_map)
-               return xics_default_server;
-
-       return ics->hwirq_cpu_map[index];
-}
-#else /* !CONFIG_SMP */
-static int cache_hwirq_map(struct wsp_ics *ics, unsigned int hwirq,
-                          const cpumask_t *affinity)
-{
-       return 0;
-}
-
-static int get_irq_server(struct wsp_ics *ics, unsigned int hwirq)
-{
-       return xics_default_server;
-}
-
-static void alloc_irq_map(struct wsp_ics *ics) { }
-#endif
-
-static void wsp_chip_unmask_irq(struct irq_data *d)
-{
-       unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
-       struct wsp_ics *ics;
-       int server;
-       u64 xive;
-
-       if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
-               return;
-
-       ics = d->chip_data;
-       if (WARN_ON(!ics))
-               return;
-
-       server = get_irq_server(ics, hw_irq);
-
-       xive = wsp_ics_get_xive(ics, hw_irq);
-       xive = xive_set_server(xive, server);
-       xive = xive_set_priority(xive, DEFAULT_PRIORITY);
-       wsp_ics_set_xive(ics, hw_irq, xive);
-}
-
-static unsigned int wsp_chip_startup(struct irq_data *d)
-{
-       /* unmask it */
-       wsp_chip_unmask_irq(d);
-       return 0;
-}
-
-static void wsp_mask_real_irq(unsigned int hw_irq, struct wsp_ics *ics)
-{
-       u64 xive;
-
-       if (hw_irq == XICS_IPI)
-               return;
-
-       if (WARN_ON(!ics))
-               return;
-       xive = wsp_ics_get_xive(ics, hw_irq);
-       xive = xive_set_server(xive, xics_default_server);
-       xive = xive_set_priority(xive, LOWEST_PRIORITY);
-       wsp_ics_set_xive(ics, hw_irq, xive);
-}
-
-static void wsp_chip_mask_irq(struct irq_data *d)
-{
-       unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
-       struct wsp_ics *ics = d->chip_data;
-
-       if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
-               return;
-
-       wsp_mask_real_irq(hw_irq, ics);
-}
-
-static int wsp_chip_set_affinity(struct irq_data *d,
-                                const struct cpumask *cpumask, bool force)
-{
-       unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
-       struct wsp_ics *ics;
-       int ret;
-       u64 xive;
-
-       if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
-               return -1;
-
-       ics = d->chip_data;
-       if (WARN_ON(!ics))
-               return -1;
-       xive = wsp_ics_get_xive(ics, hw_irq);
-
-       /*
-        * For the moment only implement delivery to all cpus or one cpu.
-        * Get current irq_server for the given irq
-        */
-       ret = cache_hwirq_map(ics, hw_irq, cpumask);
-       if (ret == -1) {
-               char cpulist[128];
-               cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask);
-               pr_warning("%s: No online cpus in the mask %s for irq %d\n",
-                          __func__, cpulist, d->irq);
-               return -1;
-       } else if (ret == -ENOMEM) {
-               pr_warning("%s: Out of memory\n", __func__);
-               return -1;
-       }
-
-       xive = xive_set_server(xive, get_irq_server(ics, hw_irq));
-       wsp_ics_set_xive(ics, hw_irq, xive);
-
-       return IRQ_SET_MASK_OK;
-}
-
-static struct irq_chip wsp_irq_chip = {
-       .name = "WSP ICS",
-       .irq_startup            = wsp_chip_startup,
-       .irq_mask               = wsp_chip_mask_irq,
-       .irq_unmask             = wsp_chip_unmask_irq,
-       .irq_set_affinity       = wsp_chip_set_affinity
-};
-
-static int wsp_ics_host_match(struct ics *ics, struct device_node *dn)
-{
-       /* All ICSs in the system implement a global irq number space,
-        * so match against them all. */
-       return of_device_is_compatible(dn, "ibm,ppc-xics");
-}
-
-static int wsp_ics_match_hwirq(struct wsp_ics *wsp_ics, unsigned int hwirq)
-{
-       if (hwirq >= wsp_ics->hwirq_start &&
-           hwirq <  wsp_ics->hwirq_start + wsp_ics->count)
-               return 1;
-
-       return 0;
-}
-
-static int wsp_ics_map(struct ics *ics, unsigned int virq)
-{
-       struct wsp_ics *wsp_ics = to_wsp_ics(ics);
-       unsigned int hw_irq = virq_to_hw(virq);
-       unsigned long flags;
-
-       if (!wsp_ics_match_hwirq(wsp_ics, hw_irq))
-               return -ENOENT;
-
-       irq_set_chip_and_handler(virq, &wsp_irq_chip, handle_fasteoi_irq);
-
-       irq_set_chip_data(virq, wsp_ics);
-
-       spin_lock_irqsave(&wsp_ics->lock, flags);
-       bitmap_allocate_region(wsp_ics->bitmap, hw_irq - wsp_ics->hwirq_start, 0);
-       spin_unlock_irqrestore(&wsp_ics->lock, flags);
-
-       return 0;
-}
-
-static void wsp_ics_mask_unknown(struct ics *ics, unsigned long hw_irq)
-{
-       struct wsp_ics *wsp_ics = to_wsp_ics(ics);
-
-       if (!wsp_ics_match_hwirq(wsp_ics, hw_irq))
-               return;
-
-       pr_err("%s: IRQ %lu (real) is invalid, disabling it.\n", __func__, hw_irq);
-       wsp_mask_real_irq(hw_irq, wsp_ics);
-}
-
-static long wsp_ics_get_server(struct ics *ics, unsigned long hw_irq)
-{
-       struct wsp_ics *wsp_ics = to_wsp_ics(ics);
-
-       if (!wsp_ics_match_hwirq(wsp_ics, hw_irq))
-               return -ENOENT;
-
-       return get_irq_server(wsp_ics, hw_irq);
-}
-
-/* HW Number allocation API */
-
-static struct wsp_ics *wsp_ics_find_dn_ics(struct device_node *dn)
-{
-       struct device_node *iparent;
-       int i;
-
-       iparent = of_irq_find_parent(dn);
-       if (!iparent) {
-               pr_err("wsp_ics: Failed to find interrupt parent!\n");
-               return NULL;
-       }
-
-       for(i = 0; i < num_ics; i++) {
-               if(ics_list[i].dn == iparent)
-                       break;
-       }
-
-       if (i >= num_ics) {
-               pr_err("wsp_ics: Unable to find parent bitmap!\n");
-               return NULL;
-       }
-
-       return &ics_list[i];
-}
-
-int wsp_ics_alloc_irq(struct device_node *dn, int num)
-{
-       struct wsp_ics *ics;
-       int order, offset;
-
-       ics = wsp_ics_find_dn_ics(dn);
-       if (!ics)
-               return -ENODEV;
-
-       /* Fast, but overly strict if num isn't a power of two */
-       order = get_count_order(num);
-
-       spin_lock_irq(&ics->lock);
-       offset = bitmap_find_free_region(ics->bitmap, ics->count, order);
-       spin_unlock_irq(&ics->lock);
-
-       if (offset < 0)
-               return offset;
-
-       return offset + ics->hwirq_start;
-}
-
-void wsp_ics_free_irq(struct device_node *dn, unsigned int irq)
-{
-       struct wsp_ics *ics;
-
-       ics = wsp_ics_find_dn_ics(dn);
-       if (WARN_ON(!ics))
-               return;
-
-       spin_lock_irq(&ics->lock);
-       bitmap_release_region(ics->bitmap, irq, 0);
-       spin_unlock_irq(&ics->lock);
-}
-
-/* Initialisation */
-
-static int __init wsp_ics_bitmap_setup(struct wsp_ics *ics,
-                                     struct device_node *dn)
-{
-       int len, i, j, size;
-       u32 start, count;
-       const u32 *p;
-
-       size = BITS_TO_LONGS(ics->count) * sizeof(long);
-       ics->bitmap = kzalloc(size, GFP_KERNEL);
-       if (!ics->bitmap) {
-               pr_err("wsp_ics: ENOMEM allocating IRQ bitmap!\n");
-               return -ENOMEM;
-       }
-
-       spin_lock_init(&ics->lock);
-
-       p = of_get_property(dn, "available-ranges", &len);
-       if (!p || !len) {
-               /* FIXME this should be a WARN() once mambo is updated */
-               pr_err("wsp_ics: No available-ranges defined for %s\n",
-                       dn->full_name);
-               return 0;
-       }
-
-       if (len % (2 * sizeof(u32)) != 0) {
-               /* FIXME this should be a WARN() once mambo is updated */
-               pr_err("wsp_ics: Invalid available-ranges for %s\n",
-                       dn->full_name);
-               return 0;
-       }
-
-       bitmap_fill(ics->bitmap, ics->count);
-
-       for (i = 0; i < len / sizeof(u32); i += 2) {
-               start = of_read_number(p + i, 1);
-               count = of_read_number(p + i + 1, 1);
-
-               pr_devel("%s: start: %d count: %d\n", __func__, start, count);
-
-               if ((start + count) > (ics->hwirq_start + ics->count) ||
-                    start < ics->hwirq_start) {
-                       pr_err("wsp_ics: Invalid range! -> %d to %d\n",
-                                       start, start + count);
-                       break;
-               }
-
-               for (j = 0; j < count; j++)
-                       bitmap_release_region(ics->bitmap,
-                               (start + j) - ics->hwirq_start, 0);
-       }
-
-       /* Ensure LSIs are not available for allocation */
-       bitmap_allocate_region(ics->bitmap, ics->lsi_base,
-                              get_count_order(ics->lsi_count));
-
-       return 0;
-}
-
-static int __init wsp_ics_setup(struct wsp_ics *ics, struct device_node *dn)
-{
-       u32 lsi_buid, msi_buid, msi_base, msi_count;
-       void __iomem *regs;
-       const u32 *p;
-       int rc, len, i;
-       u64 caps, buid;
-
-       p = of_get_property(dn, "interrupt-ranges", &len);
-       if (!p || len < (2 * sizeof(u32))) {
-               pr_err("wsp_ics: No/bad interrupt-ranges found on %s\n",
-                       dn->full_name);
-               return -ENOENT;
-       }
-
-       if (len > (2 * sizeof(u32))) {
-               pr_err("wsp_ics: Multiple ics ranges not supported.\n");
-               return -EINVAL;
-       }
-
-       regs = of_iomap(dn, 0);
-       if (!regs) {
-               pr_err("wsp_ics: of_iomap(%s) failed\n", dn->full_name);
-               return -ENXIO;
-       }
-
-       ics->hwirq_start = of_read_number(p, 1);
-       ics->count = of_read_number(p + 1, 1);
-       ics->regs = regs;
-
-       ics->chip_id = wsp_get_chip_id(dn);
-       if (WARN_ON(ics->chip_id < 0))
-               ics->chip_id = 0;
-
-       /* Get some informations about the critter */
-       caps = in_be64(ICS_INT_CAPS_REG(ics->regs));
-       buid = in_be64(INT_SRC_LAYER_BUID_REG(ics->regs));
-       ics->lsi_count = caps >> 56;
-       msi_count = (caps >> 44) & 0x7ff;
-
-       /* Note: LSI BUID is 9 bits, but really only 3 are BUID and the
-        * rest is mixed in the interrupt number. We store the whole
-        * thing though
-        */
-       lsi_buid = (buid >> 48) & 0x1ff;
-       ics->lsi_base = (ics->chip_id << WSP_ICS_CHIP_SHIFT) | lsi_buid << 5;
-       msi_buid = (buid >> 37) & 0x7;
-       msi_base = (ics->chip_id << WSP_ICS_CHIP_SHIFT) | msi_buid << 11;
-
-       pr_info("wsp_ics: Found %s\n", dn->full_name);
-       pr_info("wsp_ics:    irq range : 0x%06llx..0x%06llx\n",
-               ics->hwirq_start, ics->hwirq_start + ics->count - 1);
-       pr_info("wsp_ics:    %4d LSIs : 0x%06x..0x%06x\n",
-               ics->lsi_count, ics->lsi_base,
-               ics->lsi_base + ics->lsi_count - 1);
-       pr_info("wsp_ics:    %4d MSIs : 0x%06x..0x%06x\n",
-               msi_count, msi_base,
-               msi_base + msi_count - 1);
-
-       /* Let's check the HW config is sane */
-       if (ics->lsi_base < ics->hwirq_start ||
-           (ics->lsi_base + ics->lsi_count) > (ics->hwirq_start + ics->count))
-               pr_warning("wsp_ics: WARNING ! LSIs out of interrupt-ranges !\n");
-       if (msi_base < ics->hwirq_start ||
-           (msi_base + msi_count) > (ics->hwirq_start + ics->count))
-               pr_warning("wsp_ics: WARNING ! MSIs out of interrupt-ranges !\n");
-
-       /* We don't check for overlap between LSI and MSI, which will happen
-        * if we use the same BUID, I'm not sure yet how legit that is.
-        */
-
-       rc = wsp_ics_bitmap_setup(ics, dn);
-       if (rc) {
-               iounmap(regs);
-               return rc;
-       }
-
-       ics->dn = of_node_get(dn);
-       alloc_irq_map(ics);
-
-       for(i = 0; i < ics->count; i++)
-               wsp_mask_real_irq(ics->hwirq_start + i, ics);
-
-       ics->ics.map = wsp_ics_map;
-       ics->ics.mask_unknown = wsp_ics_mask_unknown;
-       ics->ics.get_server = wsp_ics_get_server;
-       ics->ics.host_match = wsp_ics_host_match;
-
-       xics_register_ics(&ics->ics);
-
-       return 0;
-}
-
-static void __init wsp_ics_set_default_server(void)
-{
-       struct device_node *np;
-       u32 hwid;
-
-       /* Find the server number for the boot cpu. */
-       np = of_get_cpu_node(boot_cpuid, NULL);
-       BUG_ON(!np);
-
-       hwid = get_hard_smp_processor_id(boot_cpuid);
-
-       pr_info("wsp_ics: default server is %#x, CPU %s\n", hwid, np->full_name);
-       xics_default_server = hwid;
-
-       of_node_put(np);
-}
-
-static int __init wsp_ics_init(void)
-{
-       struct device_node *dn;
-       struct wsp_ics *ics;
-       int rc, found;
-
-       wsp_ics_set_default_server();
-
-       found = 0;
-       for_each_compatible_node(dn, NULL, "ibm,ppc-xics")
-               found++;
-
-       if (found == 0) {
-               pr_err("wsp_ics: No ICS's found!\n");
-               return -ENODEV;
-       }
-
-       ics_list = kmalloc(sizeof(*ics) * found, GFP_KERNEL);
-       if (!ics_list) {
-               pr_err("wsp_ics: No memory for structs.\n");
-               return -ENOMEM;
-       }
-
-       num_ics = 0;
-       ics = ics_list;
-       for_each_compatible_node(dn, NULL, "ibm,wsp-xics") {
-               rc = wsp_ics_setup(ics, dn);
-               if (rc == 0) {
-                       ics++;
-                       num_ics++;
-               }
-       }
-
-       if (found != num_ics) {
-               pr_err("wsp_ics: Failed setting up %d ICS's\n",
-                       found - num_ics);
-               return -1;
-       }
-
-       return 0;
-}
-
-void __init wsp_init_irq(void)
-{
-       wsp_ics_init();
-       xics_init();
-
-       /* We need to patch our irq chip's EOI to point to the right ICP */
-       wsp_irq_chip.irq_eoi = icp_ops->eoi;
-}
-
-#ifdef CONFIG_PCI_MSI
-static void wsp_ics_msi_unmask_irq(struct irq_data *d)
-{
-       wsp_chip_unmask_irq(d);
-       unmask_msi_irq(d);
-}
-
-static unsigned int wsp_ics_msi_startup(struct irq_data *d)
-{
-       wsp_ics_msi_unmask_irq(d);
-       return 0;
-}
-
-static void wsp_ics_msi_mask_irq(struct irq_data *d)
-{
-       mask_msi_irq(d);
-       wsp_chip_mask_irq(d);
-}
-
-/*
- * we do it this way because we reassinge default EOI handling in
- * irq_init() above
- */
-static void wsp_ics_eoi(struct irq_data *data)
-{
-       wsp_irq_chip.irq_eoi(data);
-}
-
-static struct irq_chip wsp_ics_msi = {
-       .name = "WSP ICS MSI",
-       .irq_startup = wsp_ics_msi_startup,
-       .irq_mask = wsp_ics_msi_mask_irq,
-       .irq_unmask = wsp_ics_msi_unmask_irq,
-       .irq_eoi = wsp_ics_eoi,
-       .irq_set_affinity = wsp_chip_set_affinity
-};
-
-void wsp_ics_set_msi_chip(unsigned int irq)
-{
-       irq_set_chip(irq, &wsp_ics_msi);
-}
-
-void wsp_ics_set_std_chip(unsigned int irq)
-{
-       irq_set_chip(irq, &wsp_irq_chip);
-}
-#endif /* CONFIG_PCI_MSI */
diff --git a/arch/powerpc/platforms/wsp/ics.h b/arch/powerpc/platforms/wsp/ics.h
deleted file mode 100644 (file)
index 07b644e..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2009 IBM Corporation.
- *
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU General Public License
- *  as published by the Free Software Foundation; either version
- *  2 of the License, or (at your option) any later version.
- */
-
-#ifndef __ICS_H
-#define __ICS_H
-
-#define XIVE_ADDR_MASK         0x7FFULL
-
-extern void wsp_init_irq(void);
-
-extern int wsp_ics_alloc_irq(struct device_node *dn, int num);
-extern void wsp_ics_free_irq(struct device_node *dn, unsigned int irq);
-
-#ifdef CONFIG_PCI_MSI
-extern void wsp_ics_set_msi_chip(unsigned int irq);
-extern void wsp_ics_set_std_chip(unsigned int irq);
-#endif /* CONFIG_PCI_MSI */
-
-#endif /* __ICS_H */
diff --git a/arch/powerpc/platforms/wsp/msi.c b/arch/powerpc/platforms/wsp/msi.c
deleted file mode 100644 (file)
index 380882f..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2011 Michael Ellerman, IBM Corp.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-#include <linux/kernel.h>
-#include <linux/pci.h>
-#include <linux/msi.h>
-#include <linux/irq.h>
-#include <linux/interrupt.h>
-
-#include "msi.h"
-#include "ics.h"
-#include "wsp_pci.h"
-
-/* Magic addresses for 32 & 64-bit MSIs with hardcoded MVE 0 */
-#define MSI_ADDR_32            0xFFFF0000ul
-#define MSI_ADDR_64            0x1000000000000000ul
-
-int wsp_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
-{
-       struct pci_controller *phb;
-       struct msi_desc *entry;
-       struct msi_msg msg;
-       unsigned int virq;
-       int hwirq;
-
-       phb = pci_bus_to_host(dev->bus);
-       if (!phb)
-               return -ENOENT;
-
-       entry = list_first_entry(&dev->msi_list, struct msi_desc, list);
-       if (entry->msi_attrib.is_64) {
-               msg.address_lo = 0;
-               msg.address_hi = MSI_ADDR_64 >> 32;
-       } else {
-               msg.address_lo = MSI_ADDR_32;
-               msg.address_hi = 0;
-       }
-
-       list_for_each_entry(entry, &dev->msi_list, list) {
-               hwirq = wsp_ics_alloc_irq(phb->dn, 1);
-               if (hwirq < 0) {
-                       dev_warn(&dev->dev, "wsp_msi: hwirq alloc failed!\n");
-                       return hwirq;
-               }
-
-               virq = irq_create_mapping(NULL, hwirq);
-               if (virq == NO_IRQ) {
-                       dev_warn(&dev->dev, "wsp_msi: virq alloc failed!\n");
-                       return -1;
-               }
-
-               dev_dbg(&dev->dev, "wsp_msi: allocated irq %#x/%#x\n",
-                       hwirq, virq);
-
-               wsp_ics_set_msi_chip(virq);
-               irq_set_msi_desc(virq, entry);
-               msg.data = hwirq & XIVE_ADDR_MASK;
-               write_msi_msg(virq, &msg);
-       }
-
-       return 0;
-}
-
-void wsp_teardown_msi_irqs(struct pci_dev *dev)
-{
-       struct pci_controller *phb;
-       struct msi_desc *entry;
-       int hwirq;
-
-       phb = pci_bus_to_host(dev->bus);
-
-       dev_dbg(&dev->dev, "wsp_msi: tearing down msi irqs\n");
-
-       list_for_each_entry(entry, &dev->msi_list, list) {
-               if (entry->irq == NO_IRQ)
-                       continue;
-
-               irq_set_msi_desc(entry->irq, NULL);
-               wsp_ics_set_std_chip(entry->irq);
-
-               hwirq = virq_to_hw(entry->irq);
-               /* In this order to avoid racing with irq_create_mapping() */
-               irq_dispose_mapping(entry->irq);
-               wsp_ics_free_irq(phb->dn, hwirq);
-       }
-}
-
-void wsp_setup_phb_msi(struct pci_controller *phb)
-{
-       /* Create a single MVE at offset 0 that matches everything */
-       out_be64(phb->cfg_data + PCIE_REG_IODA_ADDR, PCIE_REG_IODA_AD_TBL_MVT);
-       out_be64(phb->cfg_data + PCIE_REG_IODA_DATA0, 1ull << 63);
-
-       ppc_md.setup_msi_irqs = wsp_setup_msi_irqs;
-       ppc_md.teardown_msi_irqs = wsp_teardown_msi_irqs;
-}
diff --git a/arch/powerpc/platforms/wsp/msi.h b/arch/powerpc/platforms/wsp/msi.h
deleted file mode 100644 (file)
index 0ab27b7..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2011 Michael Ellerman, IBM Corp.
- *
- *  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 __WSP_MSI_H
-#define __WSP_MSI_H
-
-#ifdef CONFIG_PCI_MSI
-extern void wsp_setup_phb_msi(struct pci_controller *phb);
-#else
-static inline void wsp_setup_phb_msi(struct pci_controller *phb) { }
-#endif
-
-#endif /* __WSP_MSI_H */
diff --git a/arch/powerpc/platforms/wsp/opb_pic.c b/arch/powerpc/platforms/wsp/opb_pic.c
deleted file mode 100644 (file)
index 3f67298..0000000
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * IBM Onboard Peripheral Bus Interrupt Controller
- *
- * Copyright 2010 Jack Miller, IBM Corporation.
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of  the GNU General  Public License as published by the
- * Free Software Foundation;  either version 2 of the  License, or (at your
- * option) any later version.
- */
-
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/of.h>
-#include <linux/slab.h>
-#include <linux/time.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-
-#include <asm/reg_a2.h>
-#include <asm/irq.h>
-
-#define OPB_NR_IRQS 32
-
-#define OPB_MLSASIER   0x04    /* MLS Accumulated Status IER */
-#define OPB_MLSIR      0x50    /* MLS Interrupt Register */
-#define OPB_MLSIER     0x54    /* MLS Interrupt Enable Register */
-#define OPB_MLSIPR     0x58    /* MLS Interrupt Polarity Register */
-#define OPB_MLSIIR     0x5c    /* MLS Interrupt Inputs Register */
-
-static int opb_index = 0;
-
-struct opb_pic {
-       struct irq_domain *host;
-       void *regs;
-       int index;
-       spinlock_t lock;
-};
-
-static u32 opb_in(struct opb_pic *opb, int offset)
-{
-       return in_be32(opb->regs + offset);
-}
-
-static void opb_out(struct opb_pic *opb, int offset, u32 val)
-{
-       out_be32(opb->regs + offset, val);
-}
-
-static void opb_unmask_irq(struct irq_data *d)
-{
-       struct opb_pic *opb;
-       unsigned long flags;
-       u32 ier, bitset;
-
-       opb = d->chip_data;
-       bitset = (1 << (31 - irqd_to_hwirq(d)));
-
-       spin_lock_irqsave(&opb->lock, flags);
-
-       ier = opb_in(opb, OPB_MLSIER);
-       opb_out(opb, OPB_MLSIER, ier | bitset);
-       ier = opb_in(opb, OPB_MLSIER);
-
-       spin_unlock_irqrestore(&opb->lock, flags);
-}
-
-static void opb_mask_irq(struct irq_data *d)
-{
-       struct opb_pic *opb;
-       unsigned long flags;
-       u32 ier, mask;
-
-       opb = d->chip_data;
-       mask = ~(1 << (31 - irqd_to_hwirq(d)));
-
-       spin_lock_irqsave(&opb->lock, flags);
-
-       ier = opb_in(opb, OPB_MLSIER);
-       opb_out(opb, OPB_MLSIER, ier & mask);
-       ier = opb_in(opb, OPB_MLSIER); // Flush posted writes
-
-       spin_unlock_irqrestore(&opb->lock, flags);
-}
-
-static void opb_ack_irq(struct irq_data *d)
-{
-       struct opb_pic *opb;
-       unsigned long flags;
-       u32 bitset;
-
-       opb = d->chip_data;
-       bitset = (1 << (31 - irqd_to_hwirq(d)));
-
-       spin_lock_irqsave(&opb->lock, flags);
-
-       opb_out(opb, OPB_MLSIR, bitset);
-       opb_in(opb, OPB_MLSIR); // Flush posted writes
-
-       spin_unlock_irqrestore(&opb->lock, flags);
-}
-
-static void opb_mask_ack_irq(struct irq_data *d)
-{
-       struct opb_pic *opb;
-       unsigned long flags;
-       u32 bitset;
-       u32 ier, ir;
-
-       opb = d->chip_data;
-       bitset = (1 << (31 - irqd_to_hwirq(d)));
-
-       spin_lock_irqsave(&opb->lock, flags);
-
-       ier = opb_in(opb, OPB_MLSIER);
-       opb_out(opb, OPB_MLSIER, ier & ~bitset);
-       ier = opb_in(opb, OPB_MLSIER); // Flush posted writes
-
-       opb_out(opb, OPB_MLSIR, bitset);
-       ir = opb_in(opb, OPB_MLSIR); // Flush posted writes
-
-       spin_unlock_irqrestore(&opb->lock, flags);
-}
-
-static int opb_set_irq_type(struct irq_data *d, unsigned int flow)
-{
-       struct opb_pic *opb;
-       unsigned long flags;
-       int invert, ipr, mask, bit;
-
-       opb = d->chip_data;
-
-       /* The only information we're interested in in the type is whether it's
-        * a high or low trigger. For high triggered interrupts, the polarity
-        * set for it in the MLS Interrupt Polarity Register is 0, for low
-        * interrupts it's 1 so that the proper input in the MLS Interrupt Input
-        * Register is interrupted as asserting the interrupt. */
-
-       switch (flow) {
-               case IRQ_TYPE_NONE:
-                       opb_mask_irq(d);
-                       return 0;
-
-               case IRQ_TYPE_LEVEL_HIGH:
-                       invert = 0;
-                       break;
-
-               case IRQ_TYPE_LEVEL_LOW:
-                       invert = 1;
-                       break;
-
-               default:
-                       return -EINVAL;
-       }
-
-       bit = (1 << (31 - irqd_to_hwirq(d)));
-       mask = ~bit;
-
-       spin_lock_irqsave(&opb->lock, flags);
-
-       ipr = opb_in(opb, OPB_MLSIPR);
-       ipr = (ipr & mask) | (invert ? bit : 0);
-       opb_out(opb, OPB_MLSIPR, ipr);
-       ipr = opb_in(opb, OPB_MLSIPR);  // Flush posted writes
-
-       spin_unlock_irqrestore(&opb->lock, flags);
-
-       /* Record the type in the interrupt descriptor */
-       irqd_set_trigger_type(d, flow);
-
-       return 0;
-}
-
-static struct irq_chip opb_irq_chip = {
-       .name           = "OPB",
-       .irq_mask       = opb_mask_irq,
-       .irq_unmask     = opb_unmask_irq,
-       .irq_mask_ack   = opb_mask_ack_irq,
-       .irq_ack        = opb_ack_irq,
-       .irq_set_type   = opb_set_irq_type
-};
-
-static int opb_host_map(struct irq_domain *host, unsigned int virq,
-               irq_hw_number_t hwirq)
-{
-       struct opb_pic *opb;
-
-       opb = host->host_data;
-
-       /* Most of the important stuff is handled by the generic host code, like
-        * the lookup, so just attach some info to the virtual irq */
-
-       irq_set_chip_data(virq, opb);
-       irq_set_chip_and_handler(virq, &opb_irq_chip, handle_level_irq);
-       irq_set_irq_type(virq, IRQ_TYPE_NONE);
-
-       return 0;
-}
-
-static const struct irq_domain_ops opb_host_ops = {
-       .map = opb_host_map,
-       .xlate = irq_domain_xlate_twocell,
-};
-
-irqreturn_t opb_irq_handler(int irq, void *private)
-{
-       struct opb_pic *opb;
-       u32 ir, src, subvirq;
-
-       opb = (struct opb_pic *) private;
-
-       /* Read the OPB MLS Interrupt Register for
-        * asserted interrupts */
-       ir = opb_in(opb, OPB_MLSIR);
-       if (!ir)
-               return IRQ_NONE;
-
-       do {
-               /* Get 1 - 32 source, *NOT* bit */
-               src = 32 - ffs(ir);
-
-               /* Translate from the OPB's conception of interrupt number to
-                * Linux's virtual IRQ */
-
-               subvirq = irq_linear_revmap(opb->host, src);
-
-               generic_handle_irq(subvirq);
-       } while ((ir = opb_in(opb, OPB_MLSIR)));
-
-       return IRQ_HANDLED;
-}
-
-struct opb_pic *opb_pic_init_one(struct device_node *dn)
-{
-       struct opb_pic *opb;
-       struct resource res;
-
-       if (of_address_to_resource(dn, 0, &res)) {
-               printk(KERN_ERR "opb: Couldn't translate resource\n");
-               return  NULL;
-       }
-
-       opb = kzalloc(sizeof(struct opb_pic), GFP_KERNEL);
-       if (!opb) {
-               printk(KERN_ERR "opb: Failed to allocate opb struct!\n");
-               return NULL;
-       }
-
-       /* Get access to the OPB MMIO registers */
-       opb->regs = ioremap(res.start + 0x10000, 0x1000);
-       if (!opb->regs) {
-               printk(KERN_ERR "opb: Failed to allocate register space!\n");
-               goto free_opb;
-       }
-
-       /* Allocate an irq domain so that Linux knows that despite only
-        * having one interrupt to issue, we're the controller for multiple
-        * hardware IRQs, so later we can lookup their virtual IRQs. */
-
-       opb->host = irq_domain_add_linear(dn, OPB_NR_IRQS, &opb_host_ops, opb);
-       if (!opb->host) {
-               printk(KERN_ERR "opb: Failed to allocate IRQ host!\n");
-               goto free_regs;
-       }
-
-       opb->index = opb_index++;
-       spin_lock_init(&opb->lock);
-
-       /* Disable all interrupts by default */
-       opb_out(opb, OPB_MLSASIER, 0);
-       opb_out(opb, OPB_MLSIER, 0);
-
-       /* ACK any interrupts left by FW */
-       opb_out(opb, OPB_MLSIR, 0xFFFFFFFF);
-
-       return opb;
-
-free_regs:
-       iounmap(opb->regs);
-free_opb:
-       kfree(opb);
-       return NULL;
-}
-
-void __init opb_pic_init(void)
-{
-       struct device_node *dn;
-       struct opb_pic *opb;
-       int virq;
-       int rc;
-
-       /* Call init_one for each OPB device */
-       for_each_compatible_node(dn, NULL, "ibm,opb") {
-
-               /* Fill in an OPB struct */
-               opb = opb_pic_init_one(dn);
-               if (!opb) {
-                       printk(KERN_WARNING "opb: Failed to init node, skipped!\n");
-                       continue;
-               }
-
-               /* Map / get opb's hardware virtual irq */
-               virq = irq_of_parse_and_map(dn, 0);
-               if (virq <= 0) {
-                       printk("opb: irq_op_parse_and_map failed!\n");
-                       continue;
-               }
-
-               /* Attach opb interrupt handler to new virtual IRQ */
-               rc = request_irq(virq, opb_irq_handler, IRQF_NO_THREAD,
-                                "OPB LS Cascade", opb);
-               if (rc) {
-                       printk("opb: request_irq failed: %d\n", rc);
-                       continue;
-               }
-
-               printk("OPB%d init with %d IRQs at %p\n", opb->index,
-                               OPB_NR_IRQS, opb->regs);
-       }
-}
diff --git a/arch/powerpc/platforms/wsp/psr2.c b/arch/powerpc/platforms/wsp/psr2.c
deleted file mode 100644 (file)
index a87b414..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2008-2011, IBM Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/irq.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/of.h>
-#include <linux/smp.h>
-#include <linux/time.h>
-#include <linux/of_fdt.h>
-
-#include <asm/machdep.h>
-#include <asm/udbg.h>
-
-#include "ics.h"
-#include "wsp.h"
-
-
-static void psr2_spin(void)
-{
-       hard_irq_disable();
-       for (;;)
-               continue;
-}
-
-static void psr2_restart(char *cmd)
-{
-       psr2_spin();
-}
-
-static int __init psr2_probe(void)
-{
-       unsigned long root = of_get_flat_dt_root();
-
-       if (of_flat_dt_is_compatible(root, "ibm,wsp-chroma")) {
-               /* chroma systems also claim they are psr2s */
-               return 0;
-       }
-
-       if (!of_flat_dt_is_compatible(root, "ibm,psr2"))
-               return 0;
-
-       return 1;
-}
-
-define_machine(psr2_md) {
-       .name                   = "PSR2 A2",
-       .probe                  = psr2_probe,
-       .setup_arch             = wsp_setup_arch,
-       .restart                = psr2_restart,
-       .power_off              = psr2_spin,
-       .halt                   = psr2_spin,
-       .calibrate_decr         = generic_calibrate_decr,
-       .init_IRQ               = wsp_setup_irq,
-       .progress               = udbg_progress,
-       .power_save             = book3e_idle,
-};
-
-machine_arch_initcall(psr2_md, wsp_probe_devices);
diff --git a/arch/powerpc/platforms/wsp/scom_smp.c b/arch/powerpc/platforms/wsp/scom_smp.c
deleted file mode 100644 (file)
index 8c79ce0..0000000
+++ /dev/null
@@ -1,435 +0,0 @@
-/*
- * SCOM support for A2 platforms
- *
- * Copyright 2007-2011 Benjamin Herrenschmidt, David Gibson,
- *                    Michael Ellerman, IBM Corp.
- *
- * 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/cpumask.h>
-#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/spinlock.h>
-#include <linux/types.h>
-
-#include <asm/cputhreads.h>
-#include <asm/reg_a2.h>
-#include <asm/scom.h>
-#include <asm/udbg.h>
-#include <asm/code-patching.h>
-
-#include "wsp.h"
-
-#define SCOM_RAMC              0x2a            /* Ram Command */
-#define SCOM_RAMC_TGT1_EXT     0x80000000
-#define SCOM_RAMC_SRC1_EXT     0x40000000
-#define SCOM_RAMC_SRC2_EXT     0x20000000
-#define SCOM_RAMC_SRC3_EXT     0x10000000
-#define SCOM_RAMC_ENABLE       0x00080000
-#define SCOM_RAMC_THREADSEL    0x00060000
-#define SCOM_RAMC_EXECUTE      0x00010000
-#define SCOM_RAMC_MSR_OVERRIDE 0x00008000
-#define SCOM_RAMC_MSR_PR       0x00004000
-#define SCOM_RAMC_MSR_GS       0x00002000
-#define SCOM_RAMC_FORCE                0x00001000
-#define SCOM_RAMC_FLUSH                0x00000800
-#define SCOM_RAMC_INTERRUPT    0x00000004
-#define SCOM_RAMC_ERROR                0x00000002
-#define SCOM_RAMC_DONE         0x00000001
-#define SCOM_RAMI              0x29            /* Ram Instruction */
-#define SCOM_RAMIC             0x28            /* Ram Instruction and Command */
-#define SCOM_RAMIC_INSN                0xffffffff00000000
-#define SCOM_RAMD              0x2d            /* Ram Data */
-#define SCOM_RAMDH             0x2e            /* Ram Data High */
-#define SCOM_RAMDL             0x2f            /* Ram Data Low */
-#define SCOM_PCCR0             0x33            /* PC Configuration Register 0 */
-#define SCOM_PCCR0_ENABLE_DEBUG        0x80000000
-#define SCOM_PCCR0_ENABLE_RAM  0x40000000
-#define SCOM_THRCTL            0x30            /* Thread Control and Status */
-#define SCOM_THRCTL_T0_STOP    0x80000000
-#define SCOM_THRCTL_T1_STOP    0x40000000
-#define SCOM_THRCTL_T2_STOP    0x20000000
-#define SCOM_THRCTL_T3_STOP    0x10000000
-#define SCOM_THRCTL_T0_STEP    0x08000000
-#define SCOM_THRCTL_T1_STEP    0x04000000
-#define SCOM_THRCTL_T2_STEP    0x02000000
-#define SCOM_THRCTL_T3_STEP    0x01000000
-#define SCOM_THRCTL_T0_RUN     0x00800000
-#define SCOM_THRCTL_T1_RUN     0x00400000
-#define SCOM_THRCTL_T2_RUN     0x00200000
-#define SCOM_THRCTL_T3_RUN     0x00100000
-#define SCOM_THRCTL_T0_PM      0x00080000
-#define SCOM_THRCTL_T1_PM      0x00040000
-#define SCOM_THRCTL_T2_PM      0x00020000
-#define SCOM_THRCTL_T3_PM      0x00010000
-#define SCOM_THRCTL_T0_UDE     0x00008000
-#define SCOM_THRCTL_T1_UDE     0x00004000
-#define SCOM_THRCTL_T2_UDE     0x00002000
-#define SCOM_THRCTL_T3_UDE     0x00001000
-#define SCOM_THRCTL_ASYNC_DIS  0x00000800
-#define SCOM_THRCTL_TB_DIS     0x00000400
-#define SCOM_THRCTL_DEC_DIS    0x00000200
-#define SCOM_THRCTL_AND                0x31            /* Thread Control and Status */
-#define SCOM_THRCTL_OR         0x32            /* Thread Control and Status */
-
-
-static DEFINE_PER_CPU(scom_map_t, scom_ptrs);
-
-static scom_map_t get_scom(int cpu, struct device_node *np, int *first_thread)
-{
-       scom_map_t scom = per_cpu(scom_ptrs, cpu);
-       int tcpu;
-
-       if (scom_map_ok(scom)) {
-               *first_thread = 0;
-               return scom;
-       }
-
-       *first_thread = 1;
-
-       scom = scom_map_device(np, 0);
-
-       for (tcpu = cpu_first_thread_sibling(cpu);
-            tcpu <= cpu_last_thread_sibling(cpu); tcpu++)
-               per_cpu(scom_ptrs, tcpu) = scom;
-
-       /* Hack: for the boot core, this will actually get called on
-        * the second thread up, not the first so our test above will
-        * set first_thread incorrectly. */
-       if (cpu_first_thread_sibling(cpu) == 0)
-               *first_thread = 0;
-
-       return scom;
-}
-
-static int a2_scom_ram(scom_map_t scom, int thread, u32 insn, int extmask)
-{
-       u64 cmd, mask, val;
-       int n = 0;
-
-       cmd = ((u64)insn << 32) | (((u64)extmask & 0xf) << 28)
-               | ((u64)thread << 17) | SCOM_RAMC_ENABLE | SCOM_RAMC_EXECUTE;
-       mask = SCOM_RAMC_DONE | SCOM_RAMC_INTERRUPT | SCOM_RAMC_ERROR;
-
-       scom_write(scom, SCOM_RAMIC, cmd);
-
-       for (;;) {
-               if (scom_read(scom, SCOM_RAMC, &val) != 0) {
-                       pr_err("SCOM error on instruction 0x%08x, thread %d\n",
-                              insn, thread);
-                       return -1;
-               }
-               if (val & mask)
-                       break;
-               pr_devel("Waiting on RAMC = 0x%llx\n", val);
-               if (++n == 3) {
-                       pr_err("RAMC timeout on instruction 0x%08x, thread %d\n",
-                              insn, thread);
-                       return -1;
-               }
-       }
-
-       if (val & SCOM_RAMC_INTERRUPT) {
-               pr_err("RAMC interrupt on instruction 0x%08x, thread %d\n",
-                      insn, thread);
-               return -SCOM_RAMC_INTERRUPT;
-       }
-
-       if (val & SCOM_RAMC_ERROR) {
-               pr_err("RAMC error on instruction 0x%08x, thread %d\n",
-                      insn, thread);
-               return -SCOM_RAMC_ERROR;
-       }
-
-       return 0;
-}
-
-static int a2_scom_getgpr(scom_map_t scom, int thread, int gpr, int alt,
-                         u64 *out_gpr)
-{
-       int rc;
-
-       /* or rN, rN, rN */
-       u32 insn = 0x7c000378 | (gpr << 21) | (gpr << 16) | (gpr << 11);
-       rc = a2_scom_ram(scom, thread, insn, alt ? 0xf : 0x0);
-       if (rc)
-               return rc;
-
-       return scom_read(scom, SCOM_RAMD, out_gpr);
-}
-
-static int a2_scom_getspr(scom_map_t scom, int thread, int spr, u64 *out_spr)
-{
-       int rc, sprhi, sprlo;
-       u32 insn;
-
-       sprhi = spr >> 5;
-       sprlo = spr & 0x1f;
-       insn = 0x7c2002a6 | (sprlo << 16) | (sprhi << 11); /* mfspr r1,spr */
-
-       if (spr == 0x0ff0)
-               insn = 0x7c2000a6; /* mfmsr r1 */
-
-       rc = a2_scom_ram(scom, thread, insn, 0xf);
-       if (rc)
-               return rc;
-       return a2_scom_getgpr(scom, thread, 1, 1, out_spr);
-}
-
-static int a2_scom_setgpr(scom_map_t scom, int thread, int gpr,
-                         int alt, u64 val)
-{
-       u32 lis = 0x3c000000 | (gpr << 21);
-       u32 li = 0x38000000 | (gpr << 21);
-       u32 oris = 0x64000000 | (gpr << 21) | (gpr << 16);
-       u32 ori = 0x60000000 | (gpr << 21) | (gpr << 16);
-       u32 rldicr32 = 0x780007c6 | (gpr << 21) | (gpr << 16);
-       u32 highest = val >> 48;
-       u32 higher = (val >> 32) & 0xffff;
-       u32 high = (val >> 16) & 0xffff;
-       u32 low = val & 0xffff;
-       int lext = alt ? 0x8 : 0x0;
-       int oext = alt ? 0xf : 0x0;
-       int rc = 0;
-
-       if (highest)
-               rc |= a2_scom_ram(scom, thread, lis | highest, lext);
-
-       if (higher) {
-               if (highest)
-                       rc |= a2_scom_ram(scom, thread, oris | higher, oext);
-               else
-                       rc |= a2_scom_ram(scom, thread, li | higher, lext);
-       }
-
-       if (highest || higher)
-               rc |= a2_scom_ram(scom, thread, rldicr32, oext);
-
-       if (high) {
-               if (highest || higher)
-                       rc |= a2_scom_ram(scom, thread, oris | high, oext);
-               else
-                       rc |= a2_scom_ram(scom, thread, lis | high, lext);
-       }
-
-       if (highest || higher || high)
-               rc |= a2_scom_ram(scom, thread, ori | low, oext);
-       else
-               rc |= a2_scom_ram(scom, thread, li | low, lext);
-
-       return rc;
-}
-
-static int a2_scom_setspr(scom_map_t scom, int thread, int spr, u64 val)
-{
-       int sprhi = spr >> 5;
-       int sprlo = spr & 0x1f;
-       /* mtspr spr, r1 */
-       u32 insn = 0x7c2003a6 | (sprlo << 16) | (sprhi << 11);
-
-       if (spr == 0x0ff0)
-               insn = 0x7c200124; /* mtmsr r1 */
-
-       if (a2_scom_setgpr(scom, thread, 1, 1, val))
-               return -1;
-
-       return a2_scom_ram(scom, thread, insn, 0xf);
-}
-
-static int a2_scom_initial_tlb(scom_map_t scom, int thread)
-{
-       extern u32 a2_tlbinit_code_start[], a2_tlbinit_code_end[];
-       extern u32 a2_tlbinit_after_iprot_flush[];
-       extern u32 a2_tlbinit_after_linear_map[];
-       u32 assoc, entries, i;
-       u64 epn, tlbcfg;
-       u32 *p;
-       int rc;
-
-       /* Invalidate all entries (including iprot) */
-
-       rc = a2_scom_getspr(scom, thread, SPRN_TLB0CFG, &tlbcfg);
-       if (rc)
-               goto scom_fail;
-       entries = tlbcfg & TLBnCFG_N_ENTRY;
-       assoc = (tlbcfg & TLBnCFG_ASSOC) >> 24;
-       epn = 0;
-
-       /* Set MMUCR2 to enable 4K, 64K, 1M, 16M and 1G pages */
-       a2_scom_setspr(scom, thread, SPRN_MMUCR2, 0x000a7531);
-       /* Set MMUCR3 to write all thids bit to the TLB */
-       a2_scom_setspr(scom, thread, SPRN_MMUCR3, 0x0000000f);
-
-       /* Set MAS1 for 1G page size, and MAS2 to our initial EPN */
-       a2_scom_setspr(scom, thread, SPRN_MAS1, MAS1_TSIZE(BOOK3E_PAGESZ_1GB));
-       a2_scom_setspr(scom, thread, SPRN_MAS2, epn);
-       for (i = 0; i < entries; i++) {
-
-               a2_scom_setspr(scom, thread, SPRN_MAS0, MAS0_ESEL(i % assoc));
-
-               /* tlbwe */
-               rc = a2_scom_ram(scom, thread, 0x7c0007a4, 0);
-               if (rc)
-                       goto scom_fail;
-
-               /* Next entry is new address? */
-               if((i + 1) % assoc == 0) {
-                       epn += (1 << 30);
-                       a2_scom_setspr(scom, thread, SPRN_MAS2, epn);
-               }
-       }
-
-       /* Setup args for linear mapping */
-       rc = a2_scom_setgpr(scom, thread, 3, 0, MAS0_TLBSEL(0));
-       if (rc)
-               goto scom_fail;
-
-       /* Linear mapping */
-       for (p = a2_tlbinit_code_start; p < a2_tlbinit_after_linear_map; p++) {
-               rc = a2_scom_ram(scom, thread, *p, 0);
-               if (rc)
-                       goto scom_fail;
-       }
-
-       /*
-        * For the boot thread, between the linear mapping and the debug
-        * mappings there is a loop to flush iprot mappings. Ramming doesn't do
-        * branches, but the secondary threads don't need to be nearly as smart
-        * (i.e. we don't need to worry about invalidating the mapping we're
-        * standing on).
-        */
-
-       /* Debug mappings. Expects r11 = MAS0 from linear map (set above) */
-       for (p = a2_tlbinit_after_iprot_flush; p < a2_tlbinit_code_end; p++) {
-               rc = a2_scom_ram(scom, thread, *p, 0);
-               if (rc)
-                       goto scom_fail;
-       }
-
-scom_fail:
-       if (rc)
-               pr_err("Setting up initial TLB failed, err %d\n", rc);
-
-       if (rc == -SCOM_RAMC_INTERRUPT) {
-               /* Interrupt, dump some status */
-               int rc[10];
-               u64 iar, srr0, srr1, esr, mas0, mas1, mas2, mas7_3, mas8, ccr2;
-               rc[0] = a2_scom_getspr(scom, thread, SPRN_IAR, &iar);
-               rc[1] = a2_scom_getspr(scom, thread, SPRN_SRR0, &srr0);
-               rc[2] = a2_scom_getspr(scom, thread, SPRN_SRR1, &srr1);
-               rc[3] = a2_scom_getspr(scom, thread, SPRN_ESR, &esr);
-               rc[4] = a2_scom_getspr(scom, thread, SPRN_MAS0, &mas0);
-               rc[5] = a2_scom_getspr(scom, thread, SPRN_MAS1, &mas1);
-               rc[6] = a2_scom_getspr(scom, thread, SPRN_MAS2, &mas2);
-               rc[7] = a2_scom_getspr(scom, thread, SPRN_MAS7_MAS3, &mas7_3);
-               rc[8] = a2_scom_getspr(scom, thread, SPRN_MAS8, &mas8);
-               rc[9] = a2_scom_getspr(scom, thread, SPRN_A2_CCR2, &ccr2);
-               pr_err(" -> retreived IAR =0x%llx (err %d)\n", iar, rc[0]);
-               pr_err("    retreived SRR0=0x%llx (err %d)\n", srr0, rc[1]);
-               pr_err("    retreived SRR1=0x%llx (err %d)\n", srr1, rc[2]);
-               pr_err("    retreived ESR =0x%llx (err %d)\n", esr, rc[3]);
-               pr_err("    retreived MAS0=0x%llx (err %d)\n", mas0, rc[4]);
-               pr_err("    retreived MAS1=0x%llx (err %d)\n", mas1, rc[5]);
-               pr_err("    retreived MAS2=0x%llx (err %d)\n", mas2, rc[6]);
-               pr_err("    retreived MS73=0x%llx (err %d)\n", mas7_3, rc[7]);
-               pr_err("    retreived MAS8=0x%llx (err %d)\n", mas8, rc[8]);
-               pr_err("    retreived CCR2=0x%llx (err %d)\n", ccr2, rc[9]);
-       }
-
-       return rc;
-}
-
-int a2_scom_startup_cpu(unsigned int lcpu, int thr_idx, struct device_node *np)
-{
-       u64 init_iar, init_msr, init_ccr2;
-       unsigned long start_here;
-       int rc, core_setup;
-       scom_map_t scom;
-       u64 pccr0;
-
-       scom = get_scom(lcpu, np, &core_setup);
-       if (!scom) {
-               printk(KERN_ERR "Couldn't map SCOM for CPU%d\n", lcpu);
-               return -1;
-       }
-
-       pr_devel("Bringing up CPU%d using SCOM...\n", lcpu);
-
-       if (scom_read(scom, SCOM_PCCR0, &pccr0) != 0) {
-               printk(KERN_ERR "XSCOM failure readng PCCR0 on CPU%d\n", lcpu);
-               return -1;
-       }
-       scom_write(scom, SCOM_PCCR0, pccr0 | SCOM_PCCR0_ENABLE_DEBUG |
-                                    SCOM_PCCR0_ENABLE_RAM);
-
-       /* Stop the thead with THRCTL. If we are setting up the TLB we stop all
-        * threads. We also disable asynchronous interrupts while RAMing.
-        */
-       if (core_setup)
-               scom_write(scom, SCOM_THRCTL_OR,
-                             SCOM_THRCTL_T0_STOP |
-                             SCOM_THRCTL_T1_STOP |
-                             SCOM_THRCTL_T2_STOP |
-                             SCOM_THRCTL_T3_STOP |
-                             SCOM_THRCTL_ASYNC_DIS);
-       else
-               scom_write(scom, SCOM_THRCTL_OR, SCOM_THRCTL_T0_STOP >> thr_idx);
-
-       /* Flush its pipeline just in case */
-       scom_write(scom, SCOM_RAMC, ((u64)thr_idx << 17) |
-                     SCOM_RAMC_FLUSH | SCOM_RAMC_ENABLE);
-
-       a2_scom_getspr(scom, thr_idx, SPRN_IAR, &init_iar);
-       a2_scom_getspr(scom, thr_idx, 0x0ff0, &init_msr);
-       a2_scom_getspr(scom, thr_idx, SPRN_A2_CCR2, &init_ccr2);
-
-       /* Set MSR to MSR_CM (0x0ff0 is magic value for MSR_CM) */
-       rc = a2_scom_setspr(scom, thr_idx, 0x0ff0, MSR_CM);
-       if (rc) {
-               pr_err("Failed to set MSR ! err %d\n", rc);
-               return rc;
-       }
-
-       /* RAM in an sync/isync for the sake of it */
-       a2_scom_ram(scom, thr_idx, 0x7c0004ac, 0);
-       a2_scom_ram(scom, thr_idx, 0x4c00012c, 0);
-
-       if (core_setup) {
-               pr_devel("CPU%d is first thread in core, initializing TLB...\n",
-                        lcpu);
-               rc = a2_scom_initial_tlb(scom, thr_idx);
-               if (rc)
-                       goto fail;
-       }
-
-       start_here = ppc_function_entry(core_setup ? generic_secondary_smp_init
-                                       : generic_secondary_thread_init);
-       pr_devel("CPU%d entry point at 0x%lx...\n", lcpu, start_here);
-
-       rc |= a2_scom_setspr(scom, thr_idx, SPRN_IAR, start_here);
-       rc |= a2_scom_setgpr(scom, thr_idx, 3, 0,
-                            get_hard_smp_processor_id(lcpu));
-       /*
-        * Tell book3e_secondary_core_init not to set up the TLB, we've
-        * already done that.
-        */
-       rc |= a2_scom_setgpr(scom, thr_idx, 4, 0, 1);
-
-       rc |= a2_scom_setspr(scom, thr_idx, SPRN_TENS, 0x1 << thr_idx);
-
-       scom_write(scom, SCOM_RAMC, 0);
-       scom_write(scom, SCOM_THRCTL_AND, ~(SCOM_THRCTL_T0_STOP >> thr_idx));
-       scom_write(scom, SCOM_PCCR0, pccr0);
-fail:
-       pr_devel("  SCOM initialization %s\n", rc ? "failed" : "succeeded");
-       if (rc) {
-               pr_err("Old IAR=0x%08llx MSR=0x%08llx CCR2=0x%08llx\n",
-                      init_iar, init_msr, init_ccr2);
-       }
-
-       return rc;
-}
diff --git a/arch/powerpc/platforms/wsp/scom_wsp.c b/arch/powerpc/platforms/wsp/scom_wsp.c
deleted file mode 100644 (file)
index 6538b4d..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- *  SCOM backend for WSP
- *
- *  Copyright 2010 Benjamin Herrenschmidt, IBM Corp.
- *
- *  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/cpumask.h>
-#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/spinlock.h>
-#include <linux/types.h>
-#include <linux/of_address.h>
-
-#include <asm/cputhreads.h>
-#include <asm/reg_a2.h>
-#include <asm/scom.h>
-#include <asm/udbg.h>
-
-#include "wsp.h"
-
-
-static scom_map_t wsp_scom_map(struct device_node *dev, u64 reg, u64 count)
-{
-       struct resource r;
-       u64 xscom_addr;
-
-       if (!of_get_property(dev, "scom-controller", NULL)) {
-               pr_err("%s: device %s is not a SCOM controller\n",
-                       __func__, dev->full_name);
-               return SCOM_MAP_INVALID;
-       }
-
-       if (of_address_to_resource(dev, 0, &r)) {
-               pr_debug("Failed to find SCOM controller address\n");
-               return 0;
-       }
-
-       /* Transform the SCOM address into an XSCOM offset */
-       xscom_addr = ((reg & 0x7f000000) >> 1) | ((reg & 0xfffff) << 3);
-
-       return (scom_map_t)ioremap(r.start + xscom_addr, count << 3);
-}
-
-static void wsp_scom_unmap(scom_map_t map)
-{
-       iounmap((void *)map);
-}
-
-static int wsp_scom_read(scom_map_t map, u64 reg, u64 *value)
-{
-       u64 __iomem *addr = (u64 __iomem *)map;
-
-       *value = in_be64(addr + reg);
-
-       return 0;
-}
-
-static int wsp_scom_write(scom_map_t map, u64 reg, u64 value)
-{
-       u64 __iomem *addr = (u64 __iomem *)map;
-
-       out_be64(addr + reg, value);
-
-       return 0;
-}
-
-static const struct scom_controller wsp_scom_controller = {
-       .map    = wsp_scom_map,
-       .unmap  = wsp_scom_unmap,
-       .read   = wsp_scom_read,
-       .write  = wsp_scom_write
-};
-
-void scom_init_wsp(void)
-{
-       scom_init(&wsp_scom_controller);
-}
diff --git a/arch/powerpc/platforms/wsp/setup.c b/arch/powerpc/platforms/wsp/setup.c
deleted file mode 100644 (file)
index 11ac2f0..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2010 Michael Ellerman, IBM Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-#include <linux/kernel.h>
-#include <linux/of_platform.h>
-
-#include "wsp.h"
-
-/*
- * Find chip-id by walking up device tree looking for ibm,wsp-chip-id property.
- * Won't work for nodes that are not a descendant of a wsp node.
- */
-int wsp_get_chip_id(struct device_node *dn)
-{
-       const u32 *p;
-       int rc;
-
-       /* Start looking at the specified node, not its parent */
-       dn = of_node_get(dn);
-       while (dn && !(p = of_get_property(dn, "ibm,wsp-chip-id", NULL)))
-               dn = of_get_next_parent(dn);
-
-       if (!dn)
-               return -1;
-
-       rc = *p;
-       of_node_put(dn);
-
-       return rc;
-}
diff --git a/arch/powerpc/platforms/wsp/smp.c b/arch/powerpc/platforms/wsp/smp.c
deleted file mode 100644 (file)
index 332a18b..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- *  SMP Support for A2 platforms
- *
- *  Copyright 2007 Benjamin Herrenschmidt, IBM Corp.
- *
- *  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/cpumask.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/of.h>
-#include <linux/smp.h>
-
-#include <asm/dbell.h>
-#include <asm/machdep.h>
-#include <asm/xics.h>
-
-#include "ics.h"
-#include "wsp.h"
-
-static void smp_a2_setup_cpu(int cpu)
-{
-       doorbell_setup_this_cpu();
-
-       if (cpu != boot_cpuid)
-               xics_setup_cpu();
-}
-
-int smp_a2_kick_cpu(int nr)
-{
-       const char *enable_method;
-       struct device_node *np;
-       int thr_idx;
-
-       if (nr < 0 || nr >= NR_CPUS)
-               return -ENOENT;
-
-       np = of_get_cpu_node(nr, &thr_idx);
-       if (!np)
-               return -ENODEV;
-
-       enable_method = of_get_property(np, "enable-method", NULL);
-       pr_devel("CPU%d has enable-method: \"%s\"\n", nr, enable_method);
-
-       if (!enable_method) {
-                printk(KERN_ERR "CPU%d has no enable-method\n", nr);
-               return -ENOENT;
-       } else if (strcmp(enable_method, "ibm,a2-scom") == 0) {
-               if (a2_scom_startup_cpu(nr, thr_idx, np))
-                       return -1;
-       } else {
-               printk(KERN_ERR "CPU%d: Don't understand enable-method \"%s\"\n",
-                       nr, enable_method);
-               return -EINVAL;
-       }
-
-       /*
-        * The processor is currently spinning, waiting for the
-        * cpu_start field to become non-zero After we set cpu_start,
-        * the processor will continue on to secondary_start
-        */
-       paca[nr].cpu_start = 1;
-
-       return 0;
-}
-
-static int __init smp_a2_probe(void)
-{
-       return num_possible_cpus();
-}
-
-static struct smp_ops_t a2_smp_ops = {
-       .message_pass   = NULL, /* Use smp_muxed_ipi_message_pass */
-       .cause_ipi      = doorbell_cause_ipi,
-       .probe          = smp_a2_probe,
-       .kick_cpu       = smp_a2_kick_cpu,
-       .setup_cpu      = smp_a2_setup_cpu,
-};
-
-void __init a2_setup_smp(void)
-{
-       smp_ops = &a2_smp_ops;
-}
diff --git a/arch/powerpc/platforms/wsp/wsp.c b/arch/powerpc/platforms/wsp/wsp.c
deleted file mode 100644 (file)
index 58cd1f0..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2008-2011, IBM Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-#include <linux/kernel.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/smp.h>
-#include <linux/delay.h>
-#include <linux/time.h>
-#include <linux/of_address.h>
-
-#include <asm/scom.h>
-
-#include "wsp.h"
-#include "ics.h"
-
-#define WSP_SOC_COMPATIBLE     "ibm,wsp-soc"
-#define PBIC_COMPATIBLE                "ibm,wsp-pbic"
-#define COPRO_COMPATIBLE       "ibm,wsp-coprocessor"
-
-static int __init wsp_probe_buses(void)
-{
-       static __initdata struct of_device_id bus_ids[] = {
-               /*
-                * every node in between needs to be here or you won't
-                * find it
-                */
-               { .compatible = WSP_SOC_COMPATIBLE, },
-               { .compatible = PBIC_COMPATIBLE, },
-               { .compatible = COPRO_COMPATIBLE, },
-               {},
-       };
-       of_platform_bus_probe(NULL, bus_ids, NULL);
-
-       return 0;
-}
-
-void __init wsp_setup_arch(void)
-{
-       /* init to some ~sane value until calibrate_delay() runs */
-       loops_per_jiffy = 50000000;
-
-       scom_init_wsp();
-
-       /* Setup SMP callback */
-#ifdef CONFIG_SMP
-       a2_setup_smp();
-#endif
-#ifdef CONFIG_PCI
-       wsp_setup_pci();
-#endif
-}
-
-void __init wsp_setup_irq(void)
-{
-       wsp_init_irq();
-       opb_pic_init();
-}
-
-
-int __init wsp_probe_devices(void)
-{
-       struct device_node *np;
-
-       /* Our RTC is a ds1500. It seems to be programatically compatible
-        * with the ds1511 for which we have a driver so let's use that
-        */
-       np = of_find_compatible_node(NULL, NULL, "dallas,ds1500");
-       if (np != NULL) {
-               struct resource res;
-               if (of_address_to_resource(np, 0, &res) == 0)
-                       platform_device_register_simple("ds1511", 0, &res, 1);
-       }
-
-       wsp_probe_buses();
-
-       return 0;
-}
-
-void wsp_halt(void)
-{
-       u64 val;
-       scom_map_t m;
-       struct device_node *dn;
-       struct device_node *mine;
-       struct device_node *me;
-       int rc;
-
-       me = of_get_cpu_node(smp_processor_id(), NULL);
-       mine = scom_find_parent(me);
-
-       /* This will halt all the A2s but not power off the chip */
-       for_each_node_with_property(dn, "scom-controller") {
-               if (dn == mine)
-                       continue;
-               m = scom_map(dn, 0, 1);
-
-               /* read-modify-write it so the HW probe does not get
-                * confused */
-               rc = scom_read(m, 0, &val);
-               if (rc == 0)
-                       scom_write(m, 0, val | 1);
-               scom_unmap(m);
-       }
-       m = scom_map(mine, 0, 1);
-       rc = scom_read(m, 0, &val);
-       if (rc == 0)
-               scom_write(m, 0, val | 1);
-       /* should never return */
-       scom_unmap(m);
-}
diff --git a/arch/powerpc/platforms/wsp/wsp.h b/arch/powerpc/platforms/wsp/wsp.h
deleted file mode 100644 (file)
index a563a8a..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef __WSP_H
-#define __WSP_H
-
-#include <asm/wsp.h>
-
-/* Devtree compatible strings for major devices */
-#define PCIE_COMPATIBLE     "ibm,wsp-pciex"
-
-extern void wsp_setup_arch(void);
-extern void wsp_setup_irq(void);
-extern int wsp_probe_devices(void);
-extern void wsp_halt(void);
-
-extern void wsp_setup_pci(void);
-extern void scom_init_wsp(void);
-
-extern void a2_setup_smp(void);
-extern int a2_scom_startup_cpu(unsigned int lcpu, int thr_idx,
-                              struct device_node *np);
-extern int smp_a2_kick_cpu(int nr);
-
-extern void opb_pic_init(void);
-
-/* chroma specific managment */
-extern void wsp_h8_restart(char *cmd);
-extern void wsp_h8_power_off(void);
-extern void __init wsp_setup_h8(void);
-
-#endif /*  __WSP_H */
diff --git a/arch/powerpc/platforms/wsp/wsp_pci.c b/arch/powerpc/platforms/wsp/wsp_pci.c
deleted file mode 100644 (file)
index 9a15e5b..0000000
+++ /dev/null
@@ -1,1134 +0,0 @@
-/*
- * Copyright 2010 Ben Herrenschmidt, IBM Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-#define DEBUG
-
-#include <linux/kernel.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/bootmem.h>
-#include <linux/irq.h>
-#include <linux/interrupt.h>
-#include <linux/debugfs.h>
-
-#include <asm/sections.h>
-#include <asm/io.h>
-#include <asm/prom.h>
-#include <asm/pci-bridge.h>
-#include <asm/machdep.h>
-#include <asm/ppc-pci.h>
-#include <asm/iommu.h>
-#include <asm/io-workarounds.h>
-#include <asm/debug.h>
-
-#include "wsp.h"
-#include "wsp_pci.h"
-#include "msi.h"
-
-
-/* Max number of TVTs for one table. Only 32-bit tables can use
- * multiple TVTs and so the max currently supported is thus 8
- * since only 2G of DMA space is supported
- */
-#define MAX_TABLE_TVT_COUNT            8
-
-struct wsp_dma_table {
-       struct list_head        link;
-       struct iommu_table      table;
-       struct wsp_phb  *phb;
-       struct page             *tces[MAX_TABLE_TVT_COUNT];
-};
-
-/* We support DMA regions from 0...2G in 32bit space (no support for
- * 64-bit DMA just yet). Each device gets a separate TCE table (TVT
- * entry) with validation enabled (though not supported by SimiCS
- * just yet).
- *
- * To simplify things, we divide this 2G space into N regions based
- * on the constant below which could be turned into a tunable eventually
- *
- * We then assign dynamically those regions to devices as they show up.
- *
- * We use a bitmap as an allocator for these.
- *
- * Tables are allocated/created dynamically as devices are discovered,
- * multiple TVT entries are used if needed
- *
- * When 64-bit DMA support is added we should simply use a separate set
- * of larger regions (the HW supports 64 TVT entries). We can
- * additionally create a bypass region in 64-bit space for performances
- * though that would have a cost in term of security.
- *
- * If you set NUM_DMA32_REGIONS to 1, then a single table is shared
- * for all devices and bus/dev/fn validation is disabled
- *
- * Note that a DMA32 region cannot be smaller than 256M so the max
- * supported here for now is 8. We don't yet support sharing regions
- * between multiple devices so the max number of devices supported
- * is MAX_TABLE_TVT_COUNT.
- */
-#define NUM_DMA32_REGIONS      1
-
-struct wsp_phb {
-       struct pci_controller   *hose;
-
-       /* Lock controlling access to the list of dma tables.
-        * It does -not- protect against dma_* operations on
-        * those tables, those should be stopped before an entry
-        * is removed from the list.
-        *
-        * The lock is also used for error handling operations
-        */
-       spinlock_t              lock;
-       struct list_head        dma_tables;
-       unsigned long           dma32_map;
-       unsigned long           dma32_base;
-       unsigned int            dma32_num_regions;
-       unsigned long           dma32_region_size;
-
-       /* Debugfs stuff */
-       struct dentry           *ddir;
-
-       struct list_head        all;
-};
-static LIST_HEAD(wsp_phbs);
-
-//#define cfg_debug(fmt...)    pr_debug(fmt)
-#define cfg_debug(fmt...)
-
-
-static int wsp_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
-                                 int offset, int len, u32 *val)
-{
-       struct pci_controller *hose;
-       int suboff;
-       u64 addr;
-
-       hose = pci_bus_to_host(bus);
-       if (hose == NULL)
-               return PCIBIOS_DEVICE_NOT_FOUND;
-       if (offset >= 0x1000)
-               return  PCIBIOS_BAD_REGISTER_NUMBER;
-       addr = PCIE_REG_CA_ENABLE |
-               ((u64)bus->number) << PCIE_REG_CA_BUS_SHIFT |
-               ((u64)devfn) << PCIE_REG_CA_FUNC_SHIFT |
-               ((u64)offset & ~3) << PCIE_REG_CA_REG_SHIFT;
-       suboff = offset & 3;
-
-       /*
-        * Note: the caller has already checked that offset is
-        * suitably aligned and that len is 1, 2 or 4.
-        */
-
-       switch (len) {
-       case 1:
-               addr |= (0x8ul >> suboff) << PCIE_REG_CA_BE_SHIFT;
-               out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr);
-               *val = (in_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA)
-                       >> (suboff << 3)) & 0xff;
-               cfg_debug("read 1 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%02x\n",
-                         bus->number, devfn >> 3, devfn & 7,
-                         offset, suboff, addr, *val);
-               break;
-       case 2:
-               addr |= (0xcul >> suboff) << PCIE_REG_CA_BE_SHIFT;
-               out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr);
-               *val = (in_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA)
-                       >> (suboff << 3)) & 0xffff;
-               cfg_debug("read 2 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%04x\n",
-                         bus->number, devfn >> 3, devfn & 7,
-                         offset, suboff, addr, *val);
-               break;
-       default:
-               addr |= 0xful << PCIE_REG_CA_BE_SHIFT;
-               out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr);
-               *val = in_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA);
-               cfg_debug("read 4 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%08x\n",
-                         bus->number, devfn >> 3, devfn & 7,
-                         offset, suboff, addr, *val);
-               break;
-       }
-       return PCIBIOS_SUCCESSFUL;
-}
-
-static int wsp_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
-                                  int offset, int len, u32 val)
-{
-       struct pci_controller *hose;
-       int suboff;
-       u64 addr;
-
-       hose = pci_bus_to_host(bus);
-       if (hose == NULL)
-               return PCIBIOS_DEVICE_NOT_FOUND;
-       if (offset >= 0x1000)
-               return  PCIBIOS_BAD_REGISTER_NUMBER;
-       addr = PCIE_REG_CA_ENABLE |
-               ((u64)bus->number) << PCIE_REG_CA_BUS_SHIFT |
-               ((u64)devfn) << PCIE_REG_CA_FUNC_SHIFT |
-               ((u64)offset & ~3) << PCIE_REG_CA_REG_SHIFT;
-       suboff = offset & 3;
-
-       /*
-        * Note: the caller has already checked that offset is
-        * suitably aligned and that len is 1, 2 or 4.
-        */
-       switch (len) {
-       case 1:
-               addr |= (0x8ul >> suboff) << PCIE_REG_CA_BE_SHIFT;
-               val <<= suboff << 3;
-               out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr);
-               out_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA, val);
-               cfg_debug("write 1 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%02x\n",
-                         bus->number, devfn >> 3, devfn & 7,
-                         offset, suboff, addr, val);
-               break;
-       case 2:
-               addr |= (0xcul >> suboff) << PCIE_REG_CA_BE_SHIFT;
-               val <<= suboff << 3;
-               out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr);
-               out_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA, val);
-               cfg_debug("write 2 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%04x\n",
-                         bus->number, devfn >> 3, devfn & 7,
-                         offset, suboff, addr, val);
-               break;
-       default:
-               addr |= 0xful << PCIE_REG_CA_BE_SHIFT;
-               out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr);
-               out_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA, val);
-               cfg_debug("write 4 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%08x\n",
-                         bus->number, devfn >> 3, devfn & 7,
-                         offset, suboff, addr, val);
-               break;
-       }
-       return PCIBIOS_SUCCESSFUL;
-}
-
-static struct pci_ops wsp_pcie_pci_ops =
-{
-       .read = wsp_pcie_read_config,
-       .write = wsp_pcie_write_config,
-};
-
-#define TCE_SHIFT              12
-#define TCE_PAGE_SIZE          (1 << TCE_SHIFT)
-#define TCE_PCI_WRITE          0x2              /* write from PCI allowed */
-#define TCE_PCI_READ           0x1              /* read from PCI allowed */
-#define TCE_RPN_MASK           0x3fffffffffful  /* 42-bit RPN (4K pages) */
-#define TCE_RPN_SHIFT          12
-
-//#define dma_debug(fmt...)    pr_debug(fmt)
-#define dma_debug(fmt...)
-
-static int tce_build_wsp(struct iommu_table *tbl, long index, long npages,
-                          unsigned long uaddr, enum dma_data_direction direction,
-                          struct dma_attrs *attrs)
-{
-       struct wsp_dma_table *ptbl = container_of(tbl,
-                                                   struct wsp_dma_table,
-                                                   table);
-       u64 proto_tce;
-       u64 *tcep;
-       u64 rpn;
-
-       proto_tce = TCE_PCI_READ;
-#ifdef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS
-       proto_tce |= TCE_PCI_WRITE;
-#else
-       if (direction != DMA_TO_DEVICE)
-               proto_tce |= TCE_PCI_WRITE;
-#endif
-
-       /* XXX Make this faster by factoring out the page address for
-        * within a TCE table
-        */
-       while (npages--) {
-               /* We don't use it->base as the table can be scattered */
-               tcep = (u64 *)page_address(ptbl->tces[index >> 16]);
-               tcep += (index & 0xffff);
-
-               /* can't move this out since we might cross LMB boundary */
-               rpn = __pa(uaddr) >> TCE_SHIFT;
-               *tcep = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT;
-
-               dma_debug("[DMA] TCE %p set to 0x%016llx (dma addr: 0x%lx)\n",
-                         tcep, *tcep, (tbl->it_offset + index) << IOMMU_PAGE_SHIFT_4K);
-
-               uaddr += TCE_PAGE_SIZE;
-               index++;
-       }
-       return 0;
-}
-
-static void tce_free_wsp(struct iommu_table *tbl, long index, long npages)
-{
-       struct wsp_dma_table *ptbl = container_of(tbl,
-                                                   struct wsp_dma_table,
-                                                   table);
-#ifndef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS
-       struct pci_controller *hose = ptbl->phb->hose;
-#endif
-       u64 *tcep;
-
-       /* XXX Make this faster by factoring out the page address for
-        * within a TCE table. Also use line-kill option to kill multiple
-        * TCEs at once
-        */
-       while (npages--) {
-               /* We don't use it->base as the table can be scattered */
-               tcep = (u64 *)page_address(ptbl->tces[index >> 16]);
-               tcep += (index & 0xffff);
-               dma_debug("[DMA] TCE %p cleared\n", tcep);
-               *tcep = 0;
-#ifndef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS
-               /* Don't write there since it would pollute other MMIO accesses */
-               out_be64(hose->cfg_data + PCIE_REG_TCE_KILL,
-                        PCIE_REG_TCEKILL_SINGLE | PCIE_REG_TCEKILL_PS_4K |
-                        (__pa(tcep) & PCIE_REG_TCEKILL_ADDR_MASK));
-#endif
-               index++;
-       }
-}
-
-static struct wsp_dma_table *wsp_pci_create_dma32_table(struct wsp_phb *phb,
-                                                           unsigned int region,
-                                                           struct pci_dev *validate)
-{
-       struct pci_controller *hose = phb->hose;
-       unsigned long size = phb->dma32_region_size;
-       unsigned long addr = phb->dma32_region_size * region + phb->dma32_base;
-       struct wsp_dma_table *tbl;
-       int tvts_per_table, i, tvt, nid;
-       unsigned long flags;
-
-       nid = of_node_to_nid(phb->hose->dn);
-
-       /* Calculate how many TVTs are needed */
-       tvts_per_table = size / 0x10000000;
-       if (tvts_per_table == 0)
-               tvts_per_table = 1;
-
-       /* Calculate the base TVT index. We know all tables have the same
-        * size so we just do a simple multiply here
-        */
-       tvt = region * tvts_per_table;
-
-       pr_debug("         Region : %d\n", region);
-       pr_debug("      DMA range : 0x%08lx..0x%08lx\n", addr, addr + size - 1);
-       pr_debug(" Number of TVTs : %d\n", tvts_per_table);
-       pr_debug("       Base TVT : %d\n", tvt);
-       pr_debug("         Node   : %d\n", nid);
-
-       tbl = kzalloc_node(sizeof(struct wsp_dma_table), GFP_KERNEL, nid);
-       if (!tbl)
-               return ERR_PTR(-ENOMEM);
-       tbl->phb = phb;
-
-       /* Create as many TVTs as needed, each represents 256M at most */
-       for (i = 0; i < tvts_per_table; i++) {
-               u64 tvt_data1, tvt_data0;
-
-               /* Allocate table. We use a 4K TCE size for now always so
-                * one table is always 8 * (258M / 4K) == 512K
-                */
-               tbl->tces[i] = alloc_pages_node(nid, GFP_KERNEL, get_order(0x80000));
-               if (tbl->tces[i] == NULL)
-                       goto fail;
-               memset(page_address(tbl->tces[i]), 0, 0x80000);
-
-               pr_debug(" TCE table %d at : %p\n", i, page_address(tbl->tces[i]));
-
-               /* Table size. We currently set it to be the whole 256M region */
-               tvt_data0 = 2ull << IODA_TVT0_TCE_TABLE_SIZE_SHIFT;
-               /* IO page size set to 4K */
-               tvt_data1 = 1ull << IODA_TVT1_IO_PAGE_SIZE_SHIFT;
-               /* Shift in the address */
-               tvt_data0 |= __pa(page_address(tbl->tces[i])) << IODA_TVT0_TTA_SHIFT;
-
-               /* Validation stuff. We only validate fully bus/dev/fn for now
-                * one day maybe we can group devices but that isn't the case
-                * at the moment
-                */
-               if (validate) {
-                       tvt_data0 |= IODA_TVT0_BUSNUM_VALID_MASK;
-                       tvt_data0 |= validate->bus->number;
-                       tvt_data1 |= IODA_TVT1_DEVNUM_VALID;
-                       tvt_data1 |= ((u64)PCI_SLOT(validate->devfn))
-                               << IODA_TVT1_DEVNUM_VALUE_SHIFT;
-                       tvt_data1 |= IODA_TVT1_FUNCNUM_VALID;
-                       tvt_data1 |= ((u64)PCI_FUNC(validate->devfn))
-                               << IODA_TVT1_FUNCNUM_VALUE_SHIFT;
-               }
-
-               /* XX PE number is always 0 for now */
-
-               /* Program the values using the PHB lock */
-               spin_lock_irqsave(&phb->lock, flags);
-               out_be64(hose->cfg_data + PCIE_REG_IODA_ADDR,
-                        (tvt + i) | PCIE_REG_IODA_AD_TBL_TVT);
-               out_be64(hose->cfg_data + PCIE_REG_IODA_DATA1, tvt_data1);
-               out_be64(hose->cfg_data + PCIE_REG_IODA_DATA0, tvt_data0);
-               spin_unlock_irqrestore(&phb->lock, flags);
-       }
-
-       /* Init bits and pieces */
-       tbl->table.it_blocksize = 16;
-       tbl->table.it_page_shift = IOMMU_PAGE_SHIFT_4K;
-       tbl->table.it_offset = addr >> tbl->table.it_page_shift;
-       tbl->table.it_size = size >> tbl->table.it_page_shift;
-
-       /*
-        * It's already blank but we clear it anyway.
-        * Consider an aditiona interface that makes cleaing optional
-        */
-       iommu_init_table(&tbl->table, nid);
-
-       list_add(&tbl->link, &phb->dma_tables);
-       return tbl;
-
- fail:
-       pr_debug("  Failed to allocate a 256M TCE table !\n");
-       for (i = 0; i < tvts_per_table; i++)
-               if (tbl->tces[i])
-                       __free_pages(tbl->tces[i], get_order(0x80000));
-       kfree(tbl);
-       return ERR_PTR(-ENOMEM);
-}
-
-static void wsp_pci_dma_dev_setup(struct pci_dev *pdev)
-{
-       struct dev_archdata *archdata = &pdev->dev.archdata;
-       struct pci_controller *hose = pci_bus_to_host(pdev->bus);
-       struct wsp_phb *phb = hose->private_data;
-       struct wsp_dma_table *table = NULL;
-       unsigned long flags;
-       int i;
-
-       /* Don't assign an iommu table to a bridge */
-       if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
-               return;
-
-       pr_debug("%s: Setting up DMA...\n", pci_name(pdev));
-
-       spin_lock_irqsave(&phb->lock, flags);
-
-       /* If only one region, check if it already exist */
-       if (phb->dma32_num_regions == 1) {
-               spin_unlock_irqrestore(&phb->lock, flags);
-               if (list_empty(&phb->dma_tables))
-                       table = wsp_pci_create_dma32_table(phb, 0, NULL);
-               else
-                       table = list_first_entry(&phb->dma_tables,
-                                                struct wsp_dma_table,
-                                                link);
-       } else {
-               /* else find a free region */
-               for (i = 0; i < phb->dma32_num_regions && !table; i++) {
-                       if (__test_and_set_bit(i, &phb->dma32_map))
-                               continue;
-                       spin_unlock_irqrestore(&phb->lock, flags);
-                       table = wsp_pci_create_dma32_table(phb, i, pdev);
-               }
-       }
-
-       /* Check if we got an error */
-       if (IS_ERR(table)) {
-               pr_err("%s: Failed to create DMA table, err %ld !\n",
-                      pci_name(pdev), PTR_ERR(table));
-               return;
-       }
-
-       /* Or a valid table */
-       if (table) {
-               pr_info("%s: Setup iommu: 32-bit DMA region 0x%08lx..0x%08lx\n",
-                       pci_name(pdev),
-                       table->table.it_offset << IOMMU_PAGE_SHIFT_4K,
-                       (table->table.it_offset << IOMMU_PAGE_SHIFT_4K)
-                       + phb->dma32_region_size - 1);
-               archdata->dma_data.iommu_table_base = &table->table;
-               return;
-       }
-
-       /* Or no room */
-       spin_unlock_irqrestore(&phb->lock, flags);
-       pr_err("%s: Out of DMA space !\n", pci_name(pdev));
-}
-
-static void __init wsp_pcie_configure_hw(struct pci_controller *hose)
-{
-       u64 val;
-       int i;
-
-#define DUMP_REG(x) \
-       pr_debug("%-30s : 0x%016llx\n", #x, in_be64(hose->cfg_data + x))
-
-       /*
-        * Some WSP variants  has a bogus class code by default in the PCI-E
-        * root complex's built-in P2P bridge
-        */
-       val = in_be64(hose->cfg_data + PCIE_REG_SYS_CFG1);
-       pr_debug("PCI-E SYS_CFG1 : 0x%llx\n", val);
-       out_be64(hose->cfg_data + PCIE_REG_SYS_CFG1,
-                (val & ~PCIE_REG_SYS_CFG1_CLASS_CODE) | (PCI_CLASS_BRIDGE_PCI << 8));
-       pr_debug("PCI-E SYS_CFG1 : 0x%llx\n", in_be64(hose->cfg_data + PCIE_REG_SYS_CFG1));
-
-#ifdef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS
-       /* XXX Disable TCE caching, it doesn't work on DD1 */
-       out_be64(hose->cfg_data + 0xe50,
-                in_be64(hose->cfg_data + 0xe50) | (3ull << 62));
-       printk("PCI-E DEBUG CONTROL 5 = 0x%llx\n", in_be64(hose->cfg_data + 0xe50));
-#endif
-
-       /* Configure M32A and IO. IO is hard wired to be 1M for now */
-       out_be64(hose->cfg_data + PCIE_REG_IO_BASE_ADDR, hose->io_base_phys);
-       out_be64(hose->cfg_data + PCIE_REG_IO_BASE_MASK,
-                (~(hose->io_resource.end - hose->io_resource.start)) &
-                0x3fffffff000ul);
-       out_be64(hose->cfg_data + PCIE_REG_IO_START_ADDR, 0 | 1);
-
-       out_be64(hose->cfg_data + PCIE_REG_M32A_BASE_ADDR,
-                hose->mem_resources[0].start);
-       printk("Want to write to M32A_BASE_MASK : 0x%llx\n",
-                (~(hose->mem_resources[0].end -
-                   hose->mem_resources[0].start)) & 0x3ffffff0000ul);
-       out_be64(hose->cfg_data + PCIE_REG_M32A_BASE_MASK,
-                (~(hose->mem_resources[0].end -
-                   hose->mem_resources[0].start)) & 0x3ffffff0000ul);
-       out_be64(hose->cfg_data + PCIE_REG_M32A_START_ADDR,
-                (hose->mem_resources[0].start - hose->mem_offset[0]) | 1);
-
-       /* Clear all TVT entries
-        *
-        * XX Might get TVT count from device-tree
-        */
-       for (i = 0; i < IODA_TVT_COUNT; i++) {
-               out_be64(hose->cfg_data + PCIE_REG_IODA_ADDR,
-                        PCIE_REG_IODA_AD_TBL_TVT | i);
-               out_be64(hose->cfg_data + PCIE_REG_IODA_DATA1, 0);
-               out_be64(hose->cfg_data + PCIE_REG_IODA_DATA0, 0);
-       }
-
-       /* Kill the TCE cache */
-       out_be64(hose->cfg_data + PCIE_REG_PHB_CONFIG,
-                in_be64(hose->cfg_data + PCIE_REG_PHB_CONFIG) |
-                PCIE_REG_PHBC_64B_TCE_EN);
-
-       /* Enable 32 & 64-bit MSIs, IO space and M32A */
-       val = PCIE_REG_PHBC_32BIT_MSI_EN |
-             PCIE_REG_PHBC_IO_EN |
-             PCIE_REG_PHBC_64BIT_MSI_EN |
-             PCIE_REG_PHBC_M32A_EN;
-       if (iommu_is_off)
-               val |= PCIE_REG_PHBC_DMA_XLATE_BYPASS;
-       pr_debug("Will write config: 0x%llx\n", val);
-       out_be64(hose->cfg_data + PCIE_REG_PHB_CONFIG, val);
-
-       /* Enable error reporting */
-       out_be64(hose->cfg_data + 0xe00,
-                in_be64(hose->cfg_data + 0xe00) | 0x0008000000000000ull);
-
-       /* Mask an error that's generated when doing config space probe
-        *
-        * XXX Maybe we should only mask it around config space cycles... that or
-        * ignore it when we know we had a config space cycle recently ?
-        */
-       out_be64(hose->cfg_data + PCIE_REG_DMA_ERR_STATUS_MASK, 0x8000000000000000ull);
-       out_be64(hose->cfg_data + PCIE_REG_DMA_ERR1_STATUS_MASK, 0x8000000000000000ull);
-
-       /* Enable UTL errors, for now, all of them got to UTL irq 1
-        *
-        * We similarily mask one UTL error caused apparently during normal
-        * probing. We also mask the link up error
-        */
-       out_be64(hose->cfg_data + PCIE_UTL_SYS_BUS_AGENT_ERR_SEV, 0);
-       out_be64(hose->cfg_data + PCIE_UTL_RC_ERR_SEVERITY, 0);
-       out_be64(hose->cfg_data + PCIE_UTL_PCIE_PORT_ERROR_SEV, 0);
-       out_be64(hose->cfg_data + PCIE_UTL_SYS_BUS_AGENT_IRQ_EN, 0xffffffff00000000ull);
-       out_be64(hose->cfg_data + PCIE_UTL_PCIE_PORT_IRQ_EN, 0xff5fffff00000000ull);
-       out_be64(hose->cfg_data + PCIE_UTL_EP_ERR_IRQ_EN, 0xffffffff00000000ull);
-
-       DUMP_REG(PCIE_REG_IO_BASE_ADDR);
-       DUMP_REG(PCIE_REG_IO_BASE_MASK);
-       DUMP_REG(PCIE_REG_IO_START_ADDR);
-       DUMP_REG(PCIE_REG_M32A_BASE_ADDR);
-       DUMP_REG(PCIE_REG_M32A_BASE_MASK);
-       DUMP_REG(PCIE_REG_M32A_START_ADDR);
-       DUMP_REG(PCIE_REG_M32B_BASE_ADDR);
-       DUMP_REG(PCIE_REG_M32B_BASE_MASK);
-       DUMP_REG(PCIE_REG_M32B_START_ADDR);
-       DUMP_REG(PCIE_REG_M64_BASE_ADDR);
-       DUMP_REG(PCIE_REG_M64_BASE_MASK);
-       DUMP_REG(PCIE_REG_M64_START_ADDR);
-       DUMP_REG(PCIE_REG_PHB_CONFIG);
-}
-
-static void wsp_pci_wait_io_idle(struct wsp_phb *phb, unsigned long port)
-{
-       u64 val;
-       int i;
-
-       for (i = 0; i < 10000; i++) {
-               val = in_be64(phb->hose->cfg_data + 0xe08);
-               if ((val & 0x1900000000000000ull) == 0x0100000000000000ull)
-                       return;
-               udelay(1);
-       }
-       pr_warning("PCI IO timeout on domain %d port 0x%lx\n",
-                  phb->hose->global_number, port);
-}
-
-#define DEF_PCI_AC_RET_pio(name, ret, at, al, aa)              \
-static ret wsp_pci_##name at                                   \
-{                                                              \
-       struct iowa_bus *bus;                                   \
-       struct wsp_phb *phb;                                    \
-       unsigned long flags;                                    \
-       ret rval;                                               \
-       bus = iowa_pio_find_bus(aa);                            \
-       WARN_ON(!bus);                                          \
-       phb = bus->private;                                     \
-       spin_lock_irqsave(&phb->lock, flags);                   \
-       wsp_pci_wait_io_idle(phb, aa);                          \
-       rval = __do_##name al;                                  \
-       spin_unlock_irqrestore(&phb->lock, flags);              \
-       return rval;                                            \
-}
-
-#define DEF_PCI_AC_NORET_pio(name, at, al, aa)                 \
-static void wsp_pci_##name at                                  \
-{                                                              \
-       struct iowa_bus *bus;                                   \
-       struct wsp_phb *phb;                                    \
-       unsigned long flags;                                    \
-       bus = iowa_pio_find_bus(aa);                            \
-       WARN_ON(!bus);                                          \
-       phb = bus->private;                                     \
-       spin_lock_irqsave(&phb->lock, flags);                   \
-       wsp_pci_wait_io_idle(phb, aa);                          \
-       __do_##name al;                                         \
-       spin_unlock_irqrestore(&phb->lock, flags);              \
-}
-
-#define DEF_PCI_AC_RET_mem(name, ret, at, al, aa)
-#define DEF_PCI_AC_NORET_mem(name, at, al, aa)
-
-#define DEF_PCI_AC_RET(name, ret, at, al, space, aa)           \
-       DEF_PCI_AC_RET_##space(name, ret, at, al, aa)
-
-#define DEF_PCI_AC_NORET(name, at, al, space, aa)              \
-       DEF_PCI_AC_NORET_##space(name, at, al, aa)              \
-
-
-#include <asm/io-defs.h>
-
-#undef DEF_PCI_AC_RET
-#undef DEF_PCI_AC_NORET
-
-static struct ppc_pci_io wsp_pci_iops = {
-       .inb = wsp_pci_inb,
-       .inw = wsp_pci_inw,
-       .inl = wsp_pci_inl,
-       .outb = wsp_pci_outb,
-       .outw = wsp_pci_outw,
-       .outl = wsp_pci_outl,
-       .insb = wsp_pci_insb,
-       .insw = wsp_pci_insw,
-       .insl = wsp_pci_insl,
-       .outsb = wsp_pci_outsb,
-       .outsw = wsp_pci_outsw,
-       .outsl = wsp_pci_outsl,
-};
-
-static int __init wsp_setup_one_phb(struct device_node *np)
-{
-       struct pci_controller *hose;
-       struct wsp_phb *phb;
-
-       pr_info("PCI: Setting up PCIe host bridge 0x%s\n", np->full_name);
-
-       phb = zalloc_maybe_bootmem(sizeof(struct wsp_phb), GFP_KERNEL);
-       if (!phb)
-               return -ENOMEM;
-       hose = pcibios_alloc_controller(np);
-       if (!hose) {
-               /* Can't really free the phb */
-               return -ENOMEM;
-       }
-       hose->private_data = phb;
-       phb->hose = hose;
-
-       INIT_LIST_HEAD(&phb->dma_tables);
-       spin_lock_init(&phb->lock);
-
-       /* XXX Use bus-range property ? */
-       hose->first_busno = 0;
-       hose->last_busno = 0xff;
-
-       /* We use cfg_data as the address for the whole bridge MMIO space
-        */
-       hose->cfg_data = of_iomap(hose->dn, 0);
-
-       pr_debug("PCIe registers mapped at 0x%p\n", hose->cfg_data);
-
-       /* Get the ranges of the device-tree */
-       pci_process_bridge_OF_ranges(hose, np, 0);
-
-       /* XXX Force re-assigning of everything for now */
-       pci_add_flags(PCI_REASSIGN_ALL_BUS | PCI_REASSIGN_ALL_RSRC |
-                     PCI_ENABLE_PROC_DOMAINS);
-
-       /* Calculate how the TCE space is divided */
-       phb->dma32_base         = 0;
-       phb->dma32_num_regions  = NUM_DMA32_REGIONS;
-       if (phb->dma32_num_regions > MAX_TABLE_TVT_COUNT) {
-               pr_warning("IOMMU: Clamped to %d DMA32 regions\n",
-                          MAX_TABLE_TVT_COUNT);
-               phb->dma32_num_regions = MAX_TABLE_TVT_COUNT;
-       }
-       phb->dma32_region_size  = 0x80000000 / phb->dma32_num_regions;
-
-       BUG_ON(!is_power_of_2(phb->dma32_region_size));
-
-       /* Setup config ops */
-       hose->ops = &wsp_pcie_pci_ops;
-
-       /* Configure the HW */
-       wsp_pcie_configure_hw(hose);
-
-       /* Instanciate IO workarounds */
-       iowa_register_bus(hose, &wsp_pci_iops, NULL, phb);
-#ifdef CONFIG_PCI_MSI
-       wsp_setup_phb_msi(hose);
-#endif
-
-       /* Add to global list */
-       list_add(&phb->all, &wsp_phbs);
-
-       return 0;
-}
-
-void __init wsp_setup_pci(void)
-{
-       struct device_node *np;
-       int rc;
-
-       /* Find host bridges */
-       for_each_compatible_node(np, "pciex", PCIE_COMPATIBLE) {
-               rc = wsp_setup_one_phb(np);
-               if (rc)
-                       pr_err("Failed to setup PCIe bridge %s, rc=%d\n",
-                              np->full_name, rc);
-       }
-
-       /* Establish device-tree linkage */
-       pci_devs_phb_init();
-
-       /* Set DMA ops to use TCEs */
-       if (iommu_is_off) {
-               pr_info("PCI-E: Disabled TCEs, using direct DMA\n");
-               set_pci_dma_ops(&dma_direct_ops);
-       } else {
-               ppc_md.pci_dma_dev_setup = wsp_pci_dma_dev_setup;
-               ppc_md.tce_build = tce_build_wsp;
-               ppc_md.tce_free = tce_free_wsp;
-               set_pci_dma_ops(&dma_iommu_ops);
-       }
-}
-
-#define err_debug(fmt...)      pr_debug(fmt)
-//#define err_debug(fmt...)
-
-static int __init wsp_pci_get_err_irq_no_dt(struct device_node *np)
-{
-       const u32 *prop;
-       int hw_irq;
-
-       /* Ok, no interrupts property, let's try to find our child P2P */
-       np = of_get_next_child(np, NULL);
-       if (np == NULL)
-               return 0;
-
-       /* Grab it's interrupt map */
-       prop = of_get_property(np, "interrupt-map", NULL);
-       if (prop == NULL)
-               return 0;
-
-       /* Grab one of the interrupts in there, keep the low 4 bits */
-       hw_irq = prop[5] & 0xf;
-
-       /* 0..4 for PHB 0 and 5..9 for PHB 1 */
-       if (hw_irq < 5)
-               hw_irq = 4;
-       else
-               hw_irq = 9;
-       hw_irq |= prop[5] & ~0xf;
-
-       err_debug("PCI: Using 0x%x as error IRQ for %s\n",
-                 hw_irq, np->parent->full_name);
-       return irq_create_mapping(NULL, hw_irq);
-}
-
-static const struct {
-       u32 offset;
-       const char *name;
-} wsp_pci_regs[] = {
-#define DREG(x) { PCIE_REG_##x, #x }
-#define DUTL(x) { PCIE_UTL_##x, "UTL_" #x }
-       /* Architected registers except CONFIG_ and IODA
-         * to avoid side effects
-        */
-       DREG(DMA_CHAN_STATUS),
-       DREG(CPU_LOADSTORE_STATUS),
-       DREG(LOCK0),
-       DREG(LOCK1),
-       DREG(PHB_CONFIG),
-       DREG(IO_BASE_ADDR),
-       DREG(IO_BASE_MASK),
-       DREG(IO_START_ADDR),
-       DREG(M32A_BASE_ADDR),
-       DREG(M32A_BASE_MASK),
-       DREG(M32A_START_ADDR),
-       DREG(M32B_BASE_ADDR),
-       DREG(M32B_BASE_MASK),
-       DREG(M32B_START_ADDR),
-       DREG(M64_BASE_ADDR),
-       DREG(M64_BASE_MASK),
-       DREG(M64_START_ADDR),
-       DREG(TCE_KILL),
-       DREG(LOCK2),
-       DREG(PHB_GEN_CAP),
-       DREG(PHB_TCE_CAP),
-       DREG(PHB_IRQ_CAP),
-       DREG(PHB_EEH_CAP),
-       DREG(PAPR_ERR_INJ_CONTROL),
-       DREG(PAPR_ERR_INJ_ADDR),
-       DREG(PAPR_ERR_INJ_MASK),
-
-       /* UTL core regs */
-       DUTL(SYS_BUS_CONTROL),
-       DUTL(STATUS),
-       DUTL(SYS_BUS_AGENT_STATUS),
-       DUTL(SYS_BUS_AGENT_ERR_SEV),
-       DUTL(SYS_BUS_AGENT_IRQ_EN),
-       DUTL(SYS_BUS_BURST_SZ_CONF),
-       DUTL(REVISION_ID),
-       DUTL(OUT_POST_HDR_BUF_ALLOC),
-       DUTL(OUT_POST_DAT_BUF_ALLOC),
-       DUTL(IN_POST_HDR_BUF_ALLOC),
-       DUTL(IN_POST_DAT_BUF_ALLOC),
-       DUTL(OUT_NP_BUF_ALLOC),
-       DUTL(IN_NP_BUF_ALLOC),
-       DUTL(PCIE_TAGS_ALLOC),
-       DUTL(GBIF_READ_TAGS_ALLOC),
-
-       DUTL(PCIE_PORT_CONTROL),
-       DUTL(PCIE_PORT_STATUS),
-       DUTL(PCIE_PORT_ERROR_SEV),
-       DUTL(PCIE_PORT_IRQ_EN),
-       DUTL(RC_STATUS),
-       DUTL(RC_ERR_SEVERITY),
-       DUTL(RC_IRQ_EN),
-       DUTL(EP_STATUS),
-       DUTL(EP_ERR_SEVERITY),
-       DUTL(EP_ERR_IRQ_EN),
-       DUTL(PCI_PM_CTRL1),
-       DUTL(PCI_PM_CTRL2),
-
-       /* PCIe stack regs */
-       DREG(SYSTEM_CONFIG1),
-       DREG(SYSTEM_CONFIG2),
-       DREG(EP_SYSTEM_CONFIG),
-       DREG(EP_FLR),
-       DREG(EP_BAR_CONFIG),
-       DREG(LINK_CONFIG),
-       DREG(PM_CONFIG),
-       DREG(DLP_CONTROL),
-       DREG(DLP_STATUS),
-       DREG(ERR_REPORT_CONTROL),
-       DREG(SLOT_CONTROL1),
-       DREG(SLOT_CONTROL2),
-       DREG(UTL_CONFIG),
-       DREG(BUFFERS_CONFIG),
-       DREG(ERROR_INJECT),
-       DREG(SRIOV_CONFIG),
-       DREG(PF0_SRIOV_STATUS),
-       DREG(PF1_SRIOV_STATUS),
-       DREG(PORT_NUMBER),
-       DREG(POR_SYSTEM_CONFIG),
-
-       /* Internal logic regs */
-       DREG(PHB_VERSION),
-       DREG(RESET),
-       DREG(PHB_CONTROL),
-       DREG(PHB_TIMEOUT_CONTROL1),
-       DREG(PHB_QUIESCE_DMA),
-       DREG(PHB_DMA_READ_TAG_ACTV),
-       DREG(PHB_TCE_READ_TAG_ACTV),
-
-       /* FIR registers */
-       DREG(LEM_FIR_ACCUM),
-       DREG(LEM_FIR_AND_MASK),
-       DREG(LEM_FIR_OR_MASK),
-       DREG(LEM_ACTION0),
-       DREG(LEM_ACTION1),
-       DREG(LEM_ERROR_MASK),
-       DREG(LEM_ERROR_AND_MASK),
-       DREG(LEM_ERROR_OR_MASK),
-
-       /* Error traps registers */
-       DREG(PHB_ERR_STATUS),
-       DREG(PHB_ERR_STATUS),
-       DREG(PHB_ERR1_STATUS),
-       DREG(PHB_ERR_INJECT),
-       DREG(PHB_ERR_LEM_ENABLE),
-       DREG(PHB_ERR_IRQ_ENABLE),
-       DREG(PHB_ERR_FREEZE_ENABLE),
-       DREG(PHB_ERR_SIDE_ENABLE),
-       DREG(PHB_ERR_LOG_0),
-       DREG(PHB_ERR_LOG_1),
-       DREG(PHB_ERR_STATUS_MASK),
-       DREG(PHB_ERR1_STATUS_MASK),
-       DREG(MMIO_ERR_STATUS),
-       DREG(MMIO_ERR1_STATUS),
-       DREG(MMIO_ERR_INJECT),
-       DREG(MMIO_ERR_LEM_ENABLE),
-       DREG(MMIO_ERR_IRQ_ENABLE),
-       DREG(MMIO_ERR_FREEZE_ENABLE),
-       DREG(MMIO_ERR_SIDE_ENABLE),
-       DREG(MMIO_ERR_LOG_0),
-       DREG(MMIO_ERR_LOG_1),
-       DREG(MMIO_ERR_STATUS_MASK),
-       DREG(MMIO_ERR1_STATUS_MASK),
-       DREG(DMA_ERR_STATUS),
-       DREG(DMA_ERR1_STATUS),
-       DREG(DMA_ERR_INJECT),
-       DREG(DMA_ERR_LEM_ENABLE),
-       DREG(DMA_ERR_IRQ_ENABLE),
-       DREG(DMA_ERR_FREEZE_ENABLE),
-       DREG(DMA_ERR_SIDE_ENABLE),
-       DREG(DMA_ERR_LOG_0),
-       DREG(DMA_ERR_LOG_1),
-       DREG(DMA_ERR_STATUS_MASK),
-       DREG(DMA_ERR1_STATUS_MASK),
-
-       /* Debug and Trace registers */
-       DREG(PHB_DEBUG_CONTROL0),
-       DREG(PHB_DEBUG_STATUS0),
-       DREG(PHB_DEBUG_CONTROL1),
-       DREG(PHB_DEBUG_STATUS1),
-       DREG(PHB_DEBUG_CONTROL2),
-       DREG(PHB_DEBUG_STATUS2),
-       DREG(PHB_DEBUG_CONTROL3),
-       DREG(PHB_DEBUG_STATUS3),
-       DREG(PHB_DEBUG_CONTROL4),
-       DREG(PHB_DEBUG_STATUS4),
-       DREG(PHB_DEBUG_CONTROL5),
-       DREG(PHB_DEBUG_STATUS5),
-
-       /* Don't seem to exist ...
-       DREG(PHB_DEBUG_CONTROL6),
-       DREG(PHB_DEBUG_STATUS6),
-       */
-};
-
-static int wsp_pci_regs_show(struct seq_file *m, void *private)
-{
-       struct wsp_phb *phb = m->private;
-       struct pci_controller *hose = phb->hose;
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(wsp_pci_regs); i++) {
-               /* Skip write-only regs */
-               if (wsp_pci_regs[i].offset == 0xc08 ||
-                   wsp_pci_regs[i].offset == 0xc10 ||
-                   wsp_pci_regs[i].offset == 0xc38 ||
-                   wsp_pci_regs[i].offset == 0xc40)
-                       continue;
-               seq_printf(m, "0x%03x: 0x%016llx %s\n",
-                          wsp_pci_regs[i].offset,
-                          in_be64(hose->cfg_data + wsp_pci_regs[i].offset),
-                          wsp_pci_regs[i].name);
-       }
-       return 0;
-}
-
-static int wsp_pci_regs_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, wsp_pci_regs_show, inode->i_private);
-}
-
-static const struct file_operations wsp_pci_regs_fops = {
-       .open = wsp_pci_regs_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-};
-
-static int wsp_pci_reg_set(void *data, u64 val)
-{
-       out_be64((void __iomem *)data, val);
-       return 0;
-}
-
-static int wsp_pci_reg_get(void *data, u64 *val)
-{
-       *val = in_be64((void __iomem *)data);
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(wsp_pci_reg_fops, wsp_pci_reg_get, wsp_pci_reg_set, "0x%llx\n");
-
-static irqreturn_t wsp_pci_err_irq(int irq, void *dev_id)
-{
-       struct wsp_phb *phb = dev_id;
-       struct pci_controller *hose = phb->hose;
-       irqreturn_t handled = IRQ_NONE;
-       struct wsp_pcie_err_log_data ed;
-
-       pr_err("PCI: Error interrupt on %s (PHB %d)\n",
-              hose->dn->full_name, hose->global_number);
- again:
-       memset(&ed, 0, sizeof(ed));
-
-       /* Read and clear UTL errors */
-       ed.utl_sys_err = in_be64(hose->cfg_data + PCIE_UTL_SYS_BUS_AGENT_STATUS);
-       if (ed.utl_sys_err)
-               out_be64(hose->cfg_data + PCIE_UTL_SYS_BUS_AGENT_STATUS, ed.utl_sys_err);
-       ed.utl_port_err = in_be64(hose->cfg_data + PCIE_UTL_PCIE_PORT_STATUS);
-       if (ed.utl_port_err)
-               out_be64(hose->cfg_data + PCIE_UTL_PCIE_PORT_STATUS, ed.utl_port_err);
-       ed.utl_rc_err = in_be64(hose->cfg_data + PCIE_UTL_RC_STATUS);
-       if (ed.utl_rc_err)
-               out_be64(hose->cfg_data + PCIE_UTL_RC_STATUS, ed.utl_rc_err);
-
-       /* Read and clear main trap errors */
-       ed.phb_err = in_be64(hose->cfg_data + PCIE_REG_PHB_ERR_STATUS);
-       if (ed.phb_err) {
-               ed.phb_err1 = in_be64(hose->cfg_data + PCIE_REG_PHB_ERR1_STATUS);
-               ed.phb_log0 = in_be64(hose->cfg_data + PCIE_REG_PHB_ERR_LOG_0);
-               ed.phb_log1 = in_be64(hose->cfg_data + PCIE_REG_PHB_ERR_LOG_1);
-               out_be64(hose->cfg_data + PCIE_REG_PHB_ERR1_STATUS, 0);
-               out_be64(hose->cfg_data + PCIE_REG_PHB_ERR_STATUS, 0);
-       }
-       ed.mmio_err = in_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_STATUS);
-       if (ed.mmio_err) {
-               ed.mmio_err1 = in_be64(hose->cfg_data + PCIE_REG_MMIO_ERR1_STATUS);
-               ed.mmio_log0 = in_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_LOG_0);
-               ed.mmio_log1 = in_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_LOG_1);
-               out_be64(hose->cfg_data + PCIE_REG_MMIO_ERR1_STATUS, 0);
-               out_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_STATUS, 0);
-       }
-       ed.dma_err = in_be64(hose->cfg_data + PCIE_REG_DMA_ERR_STATUS);
-       if (ed.dma_err) {
-               ed.dma_err1 = in_be64(hose->cfg_data + PCIE_REG_DMA_ERR1_STATUS);
-               ed.dma_log0 = in_be64(hose->cfg_data + PCIE_REG_DMA_ERR_LOG_0);
-               ed.dma_log1 = in_be64(hose->cfg_data + PCIE_REG_DMA_ERR_LOG_1);
-               out_be64(hose->cfg_data + PCIE_REG_DMA_ERR1_STATUS, 0);
-               out_be64(hose->cfg_data + PCIE_REG_DMA_ERR_STATUS, 0);
-       }
-
-       /* Now print things out */
-       if (ed.phb_err) {
-               pr_err("   PHB Error Status      : 0x%016llx\n", ed.phb_err);
-               pr_err("   PHB First Error Status: 0x%016llx\n", ed.phb_err1);
-               pr_err("   PHB Error Log 0       : 0x%016llx\n", ed.phb_log0);
-               pr_err("   PHB Error Log 1       : 0x%016llx\n", ed.phb_log1);
-       }
-       if (ed.mmio_err) {
-               pr_err("  MMIO Error Status      : 0x%016llx\n", ed.mmio_err);
-               pr_err("  MMIO First Error Status: 0x%016llx\n", ed.mmio_err1);
-               pr_err("  MMIO Error Log 0       : 0x%016llx\n", ed.mmio_log0);
-               pr_err("  MMIO Error Log 1       : 0x%016llx\n", ed.mmio_log1);
-       }
-       if (ed.dma_err) {
-               pr_err("   DMA Error Status      : 0x%016llx\n", ed.dma_err);
-               pr_err("   DMA First Error Status: 0x%016llx\n", ed.dma_err1);
-               pr_err("   DMA Error Log 0       : 0x%016llx\n", ed.dma_log0);
-               pr_err("   DMA Error Log 1       : 0x%016llx\n", ed.dma_log1);
-       }
-       if (ed.utl_sys_err)
-               pr_err("   UTL Sys Error Status  : 0x%016llx\n", ed.utl_sys_err);
-       if (ed.utl_port_err)
-               pr_err("   UTL Port Error Status : 0x%016llx\n", ed.utl_port_err);
-       if (ed.utl_rc_err)
-               pr_err("   UTL RC Error Status   : 0x%016llx\n", ed.utl_rc_err);
-
-       /* Interrupts are caused by the error traps. If we had any error there
-        * we loop again in case the UTL buffered some new stuff between
-        * going there and going to the traps
-        */
-       if (ed.dma_err || ed.mmio_err || ed.phb_err) {
-               handled = IRQ_HANDLED;
-               goto again;
-       }
-       return handled;
-}
-
-static void __init wsp_setup_pci_err_reporting(struct wsp_phb *phb)
-{
-       struct pci_controller *hose = phb->hose;
-       int err_irq, i, rc;
-       char fname[16];
-
-       /* Create a debugfs file for that PHB */
-       sprintf(fname, "phb%d", phb->hose->global_number);
-       phb->ddir = debugfs_create_dir(fname, powerpc_debugfs_root);
-
-       /* Some useful debug output */
-       if (phb->ddir) {
-               struct dentry *d = debugfs_create_dir("regs", phb->ddir);
-               char tmp[64];
-
-               for (i = 0; i < ARRAY_SIZE(wsp_pci_regs); i++) {
-                       sprintf(tmp, "%03x_%s", wsp_pci_regs[i].offset,
-                               wsp_pci_regs[i].name);
-                       debugfs_create_file(tmp, 0600, d,
-                                           hose->cfg_data + wsp_pci_regs[i].offset,
-                                           &wsp_pci_reg_fops);
-               }
-               debugfs_create_file("all_regs", 0600, phb->ddir, phb, &wsp_pci_regs_fops);
-       }
-
-       /* Find the IRQ number for that PHB */
-       err_irq = irq_of_parse_and_map(hose->dn, 0);
-       if (err_irq == 0)
-               /* XXX Error IRQ lacking from device-tree */
-               err_irq = wsp_pci_get_err_irq_no_dt(hose->dn);
-       if (err_irq == 0) {
-               pr_err("PCI: Failed to fetch error interrupt for %s\n",
-                      hose->dn->full_name);
-               return;
-       }
-       /* Request it */
-       rc = request_irq(err_irq, wsp_pci_err_irq, 0, "wsp_pci error", phb);
-       if (rc) {
-               pr_err("PCI: Failed to request interrupt for %s\n",
-                      hose->dn->full_name);
-       }
-       /* Enable interrupts for all errors for now */
-       out_be64(hose->cfg_data + PCIE_REG_PHB_ERR_IRQ_ENABLE, 0xffffffffffffffffull);
-       out_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_IRQ_ENABLE, 0xffffffffffffffffull);
-       out_be64(hose->cfg_data + PCIE_REG_DMA_ERR_IRQ_ENABLE, 0xffffffffffffffffull);
-}
-
-/*
- * This is called later to hookup with the error interrupt
- */
-static int __init wsp_setup_pci_late(void)
-{
-       struct wsp_phb *phb;
-
-       list_for_each_entry(phb, &wsp_phbs, all)
-               wsp_setup_pci_err_reporting(phb);
-
-       return 0;
-}
-arch_initcall(wsp_setup_pci_late);
diff --git a/arch/powerpc/platforms/wsp/wsp_pci.h b/arch/powerpc/platforms/wsp/wsp_pci.h
deleted file mode 100644 (file)
index 52e9bd9..0000000
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright 2010 Ben Herrenschmidt, IBM Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-#ifndef __WSP_PCI_H
-#define __WSP_PCI_H
-
-/* Architected registers */
-#define PCIE_REG_DMA_CHAN_STATUS       0x110
-#define PCIE_REG_CPU_LOADSTORE_STATUS  0x120
-
-#define PCIE_REG_CONFIG_DATA           0x130
-#define PCIE_REG_LOCK0                 0x138
-#define PCIE_REG_CONFIG_ADDRESS                0x140
-#define   PCIE_REG_CA_ENABLE                   0x8000000000000000ull
-#define          PCIE_REG_CA_BUS_MASK                  0x0ff0000000000000ull
-#define   PCIE_REG_CA_BUS_SHIFT                        (20+32)
-#define   PCIE_REG_CA_DEV_MASK                 0x000f800000000000ull
-#define   PCIE_REG_CA_DEV_SHIFT                        (15+32)
-#define   PCIE_REG_CA_FUNC_MASK                        0x0000700000000000ull
-#define   PCIE_REG_CA_FUNC_SHIFT               (12+32)
-#define   PCIE_REG_CA_REG_MASK                 0x00000fff00000000ull
-#define   PCIE_REG_CA_REG_SHIFT                        ( 0+32)
-#define   PCIE_REG_CA_BE_MASK                  0x00000000f0000000ull
-#define   PCIE_REG_CA_BE_SHIFT                 (   28)
-#define PCIE_REG_LOCK1                 0x148
-
-#define PCIE_REG_PHB_CONFIG            0x160
-#define   PCIE_REG_PHBC_64B_TCE_EN             0x2000000000000000ull
-#define   PCIE_REG_PHBC_MMIO_DMA_FREEZE_EN     0x1000000000000000ull
-#define   PCIE_REG_PHBC_32BIT_MSI_EN           0x0080000000000000ull
-#define   PCIE_REG_PHBC_M64_EN                 0x0040000000000000ull
-#define   PCIE_REG_PHBC_IO_EN                  0x0008000000000000ull
-#define   PCIE_REG_PHBC_64BIT_MSI_EN           0x0002000000000000ull
-#define   PCIE_REG_PHBC_M32A_EN                        0x0000800000000000ull
-#define   PCIE_REG_PHBC_M32B_EN                        0x0000400000000000ull
-#define   PCIE_REG_PHBC_MSI_PE_VALIDATE                0x0000200000000000ull
-#define   PCIE_REG_PHBC_DMA_XLATE_BYPASS       0x0000100000000000ull
-
-#define PCIE_REG_IO_BASE_ADDR          0x170
-#define PCIE_REG_IO_BASE_MASK          0x178
-#define PCIE_REG_IO_START_ADDR         0x180
-
-#define PCIE_REG_M32A_BASE_ADDR                0x190
-#define PCIE_REG_M32A_BASE_MASK                0x198
-#define PCIE_REG_M32A_START_ADDR       0x1a0
-
-#define PCIE_REG_M32B_BASE_ADDR                0x1b0
-#define PCIE_REG_M32B_BASE_MASK                0x1b8
-#define PCIE_REG_M32B_START_ADDR       0x1c0
-
-#define PCIE_REG_M64_BASE_ADDR         0x1e0
-#define PCIE_REG_M64_BASE_MASK         0x1e8
-#define PCIE_REG_M64_START_ADDR                0x1f0
-
-#define PCIE_REG_TCE_KILL              0x210
-#define   PCIE_REG_TCEKILL_SINGLE      0x8000000000000000ull
-#define   PCIE_REG_TCEKILL_ADDR_MASK   0x000003fffffffff8ull
-#define   PCIE_REG_TCEKILL_PS_4K       0
-#define   PCIE_REG_TCEKILL_PS_64K      1
-#define   PCIE_REG_TCEKILL_PS_16M      2
-#define   PCIE_REG_TCEKILL_PS_16G      3
-
-#define PCIE_REG_IODA_ADDR             0x220
-#define   PCIE_REG_IODA_AD_AUTOINC     0x8000000000000000ull
-#define   PCIE_REG_IODA_AD_TBL_MVT     0x0005000000000000ull
-#define   PCIE_REG_IODA_AD_TBL_PELT    0x0006000000000000ull
-#define   PCIE_REG_IODA_AD_TBL_PESTA   0x0007000000000000ull
-#define   PCIE_REG_IODA_AD_TBL_PESTB   0x0008000000000000ull
-#define   PCIE_REG_IODA_AD_TBL_TVT     0x0009000000000000ull
-#define   PCIE_REG_IODA_AD_TBL_TCE     0x000a000000000000ull
-#define PCIE_REG_IODA_DATA0            0x228
-#define PCIE_REG_IODA_DATA1            0x230
-
-#define PCIE_REG_LOCK2                 0x240
-
-#define PCIE_REG_PHB_GEN_CAP           0x250
-#define PCIE_REG_PHB_TCE_CAP           0x258
-#define PCIE_REG_PHB_IRQ_CAP           0x260
-#define PCIE_REG_PHB_EEH_CAP           0x268
-
-#define PCIE_REG_PAPR_ERR_INJ_CONTROL  0x2b0
-#define PCIE_REG_PAPR_ERR_INJ_ADDR     0x2b8
-#define PCIE_REG_PAPR_ERR_INJ_MASK     0x2c0
-
-
-#define PCIE_REG_SYS_CFG1              0x600
-#define   PCIE_REG_SYS_CFG1_CLASS_CODE 0x0000000000ffffffull
-
-#define IODA_TVT0_TTA_MASK             0x000fffffffff0000ull
-#define IODA_TVT0_TTA_SHIFT            4
-#define IODA_TVT0_BUSNUM_VALID_MASK    0x000000000000e000ull
-#define IODA_TVT0_TCE_TABLE_SIZE_MASK  0x0000000000001f00ull
-#define IODA_TVT0_TCE_TABLE_SIZE_SHIFT 8
-#define IODA_TVT0_BUSNUM_VALUE_MASK    0x00000000000000ffull
-#define IODA_TVT0_BUSNUM_VALID_SHIFT   0
-#define IODA_TVT1_DEVNUM_VALID         0x2000000000000000ull
-#define IODA_TVT1_DEVNUM_VALUE_MASK    0x1f00000000000000ull
-#define IODA_TVT1_DEVNUM_VALUE_SHIFT   56
-#define IODA_TVT1_FUNCNUM_VALID                0x0008000000000000ull
-#define IODA_TVT1_FUNCNUM_VALUE_MASK   0x0007000000000000ull
-#define IODA_TVT1_FUNCNUM_VALUE_SHIFT  48
-#define IODA_TVT1_IO_PAGE_SIZE_MASK    0x00001f0000000000ull
-#define IODA_TVT1_IO_PAGE_SIZE_SHIFT   40
-#define IODA_TVT1_PE_NUMBER_MASK       0x000000000000003full
-#define IODA_TVT1_PE_NUMBER_SHIFT      0
-
-#define IODA_TVT_COUNT                 64
-
-/* UTL Core registers */
-#define PCIE_UTL_SYS_BUS_CONTROL       0x400
-#define PCIE_UTL_STATUS                        0x408
-#define PCIE_UTL_SYS_BUS_AGENT_STATUS  0x410
-#define PCIE_UTL_SYS_BUS_AGENT_ERR_SEV 0x418
-#define PCIE_UTL_SYS_BUS_AGENT_IRQ_EN  0x420
-#define PCIE_UTL_SYS_BUS_BURST_SZ_CONF 0x440
-#define PCIE_UTL_REVISION_ID           0x448
-
-#define PCIE_UTL_OUT_POST_HDR_BUF_ALLOC        0x4c0
-#define PCIE_UTL_OUT_POST_DAT_BUF_ALLOC        0x4d0
-#define PCIE_UTL_IN_POST_HDR_BUF_ALLOC 0x4e0
-#define PCIE_UTL_IN_POST_DAT_BUF_ALLOC 0x4f0
-#define PCIE_UTL_OUT_NP_BUF_ALLOC      0x500
-#define PCIE_UTL_IN_NP_BUF_ALLOC       0x510
-#define PCIE_UTL_PCIE_TAGS_ALLOC       0x520
-#define PCIE_UTL_GBIF_READ_TAGS_ALLOC  0x530
-
-#define PCIE_UTL_PCIE_PORT_CONTROL     0x540
-#define PCIE_UTL_PCIE_PORT_STATUS      0x548
-#define PCIE_UTL_PCIE_PORT_ERROR_SEV   0x550
-#define PCIE_UTL_PCIE_PORT_IRQ_EN      0x558
-#define PCIE_UTL_RC_STATUS             0x560
-#define PCIE_UTL_RC_ERR_SEVERITY       0x568
-#define PCIE_UTL_RC_IRQ_EN             0x570
-#define PCIE_UTL_EP_STATUS             0x578
-#define PCIE_UTL_EP_ERR_SEVERITY       0x580
-#define PCIE_UTL_EP_ERR_IRQ_EN         0x588
-
-#define PCIE_UTL_PCI_PM_CTRL1          0x590
-#define PCIE_UTL_PCI_PM_CTRL2          0x598
-
-/* PCIe stack registers */
-#define PCIE_REG_SYSTEM_CONFIG1                0x600
-#define PCIE_REG_SYSTEM_CONFIG2                0x608
-#define PCIE_REG_EP_SYSTEM_CONFIG      0x618
-#define PCIE_REG_EP_FLR                        0x620
-#define PCIE_REG_EP_BAR_CONFIG         0x628
-#define PCIE_REG_LINK_CONFIG           0x630
-#define PCIE_REG_PM_CONFIG             0x640
-#define PCIE_REG_DLP_CONTROL           0x650
-#define PCIE_REG_DLP_STATUS            0x658
-#define PCIE_REG_ERR_REPORT_CONTROL    0x660
-#define PCIE_REG_SLOT_CONTROL1         0x670
-#define PCIE_REG_SLOT_CONTROL2         0x678
-#define PCIE_REG_UTL_CONFIG            0x680
-#define PCIE_REG_BUFFERS_CONFIG                0x690
-#define PCIE_REG_ERROR_INJECT          0x698
-#define PCIE_REG_SRIOV_CONFIG          0x6a0
-#define PCIE_REG_PF0_SRIOV_STATUS      0x6a8
-#define PCIE_REG_PF1_SRIOV_STATUS      0x6b0
-#define PCIE_REG_PORT_NUMBER           0x700
-#define PCIE_REG_POR_SYSTEM_CONFIG     0x708
-
-/* PHB internal logic registers */
-#define PCIE_REG_PHB_VERSION           0x800
-#define PCIE_REG_RESET                 0x808
-#define PCIE_REG_PHB_CONTROL           0x810
-#define PCIE_REG_PHB_TIMEOUT_CONTROL1  0x878
-#define PCIE_REG_PHB_QUIESCE_DMA       0x888
-#define PCIE_REG_PHB_DMA_READ_TAG_ACTV 0x900
-#define PCIE_REG_PHB_TCE_READ_TAG_ACTV 0x908
-
-/* FIR registers */
-#define PCIE_REG_LEM_FIR_ACCUM         0xc00
-#define PCIE_REG_LEM_FIR_AND_MASK      0xc08
-#define PCIE_REG_LEM_FIR_OR_MASK       0xc10
-#define PCIE_REG_LEM_ACTION0           0xc18
-#define PCIE_REG_LEM_ACTION1           0xc20
-#define PCIE_REG_LEM_ERROR_MASK                0xc30
-#define PCIE_REG_LEM_ERROR_AND_MASK    0xc38
-#define PCIE_REG_LEM_ERROR_OR_MASK     0xc40
-
-/* PHB Error registers */
-#define PCIE_REG_PHB_ERR_STATUS                0xc80
-#define PCIE_REG_PHB_ERR1_STATUS       0xc88
-#define PCIE_REG_PHB_ERR_INJECT                0xc90
-#define PCIE_REG_PHB_ERR_LEM_ENABLE    0xc98
-#define PCIE_REG_PHB_ERR_IRQ_ENABLE    0xca0
-#define PCIE_REG_PHB_ERR_FREEZE_ENABLE 0xca8
-#define PCIE_REG_PHB_ERR_SIDE_ENABLE   0xcb8
-#define PCIE_REG_PHB_ERR_LOG_0         0xcc0
-#define PCIE_REG_PHB_ERR_LOG_1         0xcc8
-#define PCIE_REG_PHB_ERR_STATUS_MASK   0xcd0
-#define PCIE_REG_PHB_ERR1_STATUS_MASK  0xcd8
-
-#define PCIE_REG_MMIO_ERR_STATUS       0xd00
-#define PCIE_REG_MMIO_ERR1_STATUS      0xd08
-#define PCIE_REG_MMIO_ERR_INJECT       0xd10
-#define PCIE_REG_MMIO_ERR_LEM_ENABLE   0xd18
-#define PCIE_REG_MMIO_ERR_IRQ_ENABLE   0xd20
-#define PCIE_REG_MMIO_ERR_FREEZE_ENABLE        0xd28
-#define PCIE_REG_MMIO_ERR_SIDE_ENABLE  0xd38
-#define PCIE_REG_MMIO_ERR_LOG_0                0xd40
-#define PCIE_REG_MMIO_ERR_LOG_1                0xd48
-#define PCIE_REG_MMIO_ERR_STATUS_MASK  0xd50
-#define PCIE_REG_MMIO_ERR1_STATUS_MASK 0xd58
-
-#define PCIE_REG_DMA_ERR_STATUS                0xd80
-#define PCIE_REG_DMA_ERR1_STATUS       0xd88
-#define PCIE_REG_DMA_ERR_INJECT                0xd90
-#define PCIE_REG_DMA_ERR_LEM_ENABLE    0xd98
-#define PCIE_REG_DMA_ERR_IRQ_ENABLE    0xda0
-#define PCIE_REG_DMA_ERR_FREEZE_ENABLE 0xda8
-#define PCIE_REG_DMA_ERR_SIDE_ENABLE   0xdb8
-#define PCIE_REG_DMA_ERR_LOG_0         0xdc0
-#define PCIE_REG_DMA_ERR_LOG_1         0xdc8
-#define PCIE_REG_DMA_ERR_STATUS_MASK   0xdd0
-#define PCIE_REG_DMA_ERR1_STATUS_MASK  0xdd8
-
-/* Shortcuts for access to the above using the PHB definitions
- * with an offset
- */
-#define PCIE_REG_ERR_PHB_OFFSET                0x0
-#define PCIE_REG_ERR_MMIO_OFFSET       0x80
-#define PCIE_REG_ERR_DMA_OFFSET                0x100
-
-/* Debug and Trace registers */
-#define PCIE_REG_PHB_DEBUG_CONTROL0    0xe00
-#define PCIE_REG_PHB_DEBUG_STATUS0     0xe08
-#define PCIE_REG_PHB_DEBUG_CONTROL1    0xe10
-#define PCIE_REG_PHB_DEBUG_STATUS1     0xe18
-#define PCIE_REG_PHB_DEBUG_CONTROL2    0xe20
-#define PCIE_REG_PHB_DEBUG_STATUS2     0xe28
-#define PCIE_REG_PHB_DEBUG_CONTROL3    0xe30
-#define PCIE_REG_PHB_DEBUG_STATUS3     0xe38
-#define PCIE_REG_PHB_DEBUG_CONTROL4    0xe40
-#define PCIE_REG_PHB_DEBUG_STATUS4     0xe48
-#define PCIE_REG_PHB_DEBUG_CONTROL5    0xe50
-#define PCIE_REG_PHB_DEBUG_STATUS5     0xe58
-#define PCIE_REG_PHB_DEBUG_CONTROL6    0xe60
-#define PCIE_REG_PHB_DEBUG_STATUS6     0xe68
-
-/* Definition for PCIe errors */
-struct wsp_pcie_err_log_data {
-       __u64   phb_err;
-       __u64   phb_err1;
-       __u64   phb_log0;
-       __u64   phb_log1;
-       __u64   mmio_err;
-       __u64   mmio_err1;
-       __u64   mmio_log0;
-       __u64   mmio_log1;
-       __u64   dma_err;
-       __u64   dma_err1;
-       __u64   dma_log0;
-       __u64   dma_log1;
-       __u64   utl_sys_err;
-       __u64   utl_port_err;
-       __u64   utl_rc_err;
-       __u64   unused;
-};
-
-#endif /* __WSP_PCI_H */
index 228cf91b91c14bc4e865e89371595363aa7858cc..ffd1169ebaab8387c6a15831f5c660b1f90fa243 100644 (file)
@@ -25,7 +25,6 @@
 #include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/phy.h>
-#include <linux/phy_fixed.h>
 #include <linux/spi/spi.h>
 #include <linux/fsl_devices.h>
 #include <linux/fs_enet_pd.h>
@@ -178,37 +177,6 @@ u32 get_baudrate(void)
 EXPORT_SYMBOL(get_baudrate);
 #endif /* CONFIG_CPM2 */
 
-#ifdef CONFIG_FIXED_PHY
-static int __init of_add_fixed_phys(void)
-{
-       int ret;
-       struct device_node *np;
-       u32 *fixed_link;
-       struct fixed_phy_status status = {};
-
-       for_each_node_by_name(np, "ethernet") {
-               fixed_link  = (u32 *)of_get_property(np, "fixed-link", NULL);
-               if (!fixed_link)
-                       continue;
-
-               status.link = 1;
-               status.duplex = fixed_link[1];
-               status.speed = fixed_link[2];
-               status.pause = fixed_link[3];
-               status.asym_pause = fixed_link[4];
-
-               ret = fixed_phy_add(PHY_POLL, fixed_link[0], &status);
-               if (ret) {
-                       of_node_put(np);
-                       return ret;
-               }
-       }
-
-       return 0;
-}
-arch_initcall(of_add_fixed_phys);
-#endif /* CONFIG_FIXED_PHY */
-
 #if defined(CONFIG_FSL_SOC_BOOKE) || defined(CONFIG_PPC_86xx)
 static __be32 __iomem *rstcr;
 
index 9dee47071af8a74bc61cf433da5366d8faef1d11..de8d9483bbe89136042d0685680c6bddc815849a 100644 (file)
@@ -26,6 +26,7 @@
 #include <asm/errno.h>
 #include <asm/xics.h>
 #include <asm/kvm_ppc.h>
+#include <asm/dbell.h>
 
 struct icp_ipl {
        union {
@@ -145,7 +146,13 @@ static unsigned int icp_native_get_irq(void)
 static void icp_native_cause_ipi(int cpu, unsigned long data)
 {
        kvmppc_set_host_ipi(cpu, 1);
-       icp_native_set_qirr(cpu, IPI_PRIORITY);
+#ifdef CONFIG_PPC_DOORBELL
+       if (cpu_has_feature(CPU_FTR_DBELL) &&
+           (cpumask_test_cpu(cpu, cpu_sibling_mask(smp_processor_id()))))
+               doorbell_cause_ipi(cpu, data);
+       else
+#endif
+               icp_native_set_qirr(cpu, IPI_PRIORITY);
 }
 
 void xics_wake_cpu(int cpu)
index bce3dcfe50583440f09e7229e08305559acc12f9..c98748617896b18c697f589de4089c635e87a1cc 100644 (file)
@@ -122,7 +122,7 @@ void xmon_printf(const char *format, ...)
 
        if (n && rc == 0) {
                /* No udbg hooks, fallback to printk() - dangerous */
-               printk(xmon_outbuf);
+               printk("%s", xmon_outbuf);
        }
 }
 
index e9f8fa9337fe1fae0d5d0ba9bf03a8426961a3c0..a2cbd875543a3f206328221c521be3e3aeeab596 100644 (file)
@@ -269,27 +269,17 @@ static void bpf_jit_noleaks(struct bpf_jit *jit, struct sock_filter *filter)
                EMIT4(0xa7c80000);
        /* Clear A if the first register does not set it. */
        switch (filter[0].code) {
-       case BPF_S_LD_W_ABS:
-       case BPF_S_LD_H_ABS:
-       case BPF_S_LD_B_ABS:
-       case BPF_S_LD_W_LEN:
-       case BPF_S_LD_W_IND:
-       case BPF_S_LD_H_IND:
-       case BPF_S_LD_B_IND:
-       case BPF_S_LD_IMM:
-       case BPF_S_LD_MEM:
-       case BPF_S_MISC_TXA:
-       case BPF_S_ANC_PROTOCOL:
-       case BPF_S_ANC_PKTTYPE:
-       case BPF_S_ANC_IFINDEX:
-       case BPF_S_ANC_MARK:
-       case BPF_S_ANC_QUEUE:
-       case BPF_S_ANC_HATYPE:
-       case BPF_S_ANC_RXHASH:
-       case BPF_S_ANC_CPU:
-       case BPF_S_ANC_VLAN_TAG:
-       case BPF_S_ANC_VLAN_TAG_PRESENT:
-       case BPF_S_RET_K:
+       case BPF_LD | BPF_W | BPF_ABS:
+       case BPF_LD | BPF_H | BPF_ABS:
+       case BPF_LD | BPF_B | BPF_ABS:
+       case BPF_LD | BPF_W | BPF_LEN:
+       case BPF_LD | BPF_W | BPF_IND:
+       case BPF_LD | BPF_H | BPF_IND:
+       case BPF_LD | BPF_B | BPF_IND:
+       case BPF_LD | BPF_IMM:
+       case BPF_LD | BPF_MEM:
+       case BPF_MISC | BPF_TXA:
+       case BPF_RET | BPF_K:
                /* first instruction sets A register */
                break;
        default: /* A = 0 */
@@ -304,15 +294,18 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
        unsigned int K;
        int offset;
        unsigned int mask;
+       u16 code;
 
        K = filter->k;
-       switch (filter->code) {
-       case BPF_S_ALU_ADD_X: /* A += X */
+       code = bpf_anc_helper(filter);
+
+       switch (code) {
+       case BPF_ALU | BPF_ADD | BPF_X: /* A += X */
                jit->seen |= SEEN_XREG;
                /* ar %r5,%r12 */
                EMIT2(0x1a5c);
                break;
-       case BPF_S_ALU_ADD_K: /* A += K */
+       case BPF_ALU | BPF_ADD | BPF_K: /* A += K */
                if (!K)
                        break;
                if (K <= 16383)
@@ -325,12 +318,12 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
                        /* a %r5,<d(K)>(%r13) */
                        EMIT4_DISP(0x5a50d000, EMIT_CONST(K));
                break;
-       case BPF_S_ALU_SUB_X: /* A -= X */
+       case BPF_ALU | BPF_SUB | BPF_X: /* A -= X */
                jit->seen |= SEEN_XREG;
                /* sr %r5,%r12 */
                EMIT2(0x1b5c);
                break;
-       case BPF_S_ALU_SUB_K: /* A -= K */
+       case BPF_ALU | BPF_SUB | BPF_K: /* A -= K */
                if (!K)
                        break;
                if (K <= 16384)
@@ -343,12 +336,12 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
                        /* s %r5,<d(K)>(%r13) */
                        EMIT4_DISP(0x5b50d000, EMIT_CONST(K));
                break;
-       case BPF_S_ALU_MUL_X: /* A *= X */
+       case BPF_ALU | BPF_MUL | BPF_X: /* A *= X */
                jit->seen |= SEEN_XREG;
                /* msr %r5,%r12 */
                EMIT4(0xb252005c);
                break;
-       case BPF_S_ALU_MUL_K: /* A *= K */
+       case BPF_ALU | BPF_MUL | BPF_K: /* A *= K */
                if (K <= 16383)
                        /* mhi %r5,K */
                        EMIT4_IMM(0xa75c0000, K);
@@ -359,7 +352,7 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
                        /* ms %r5,<d(K)>(%r13) */
                        EMIT4_DISP(0x7150d000, EMIT_CONST(K));
                break;
-       case BPF_S_ALU_DIV_X: /* A /= X */
+       case BPF_ALU | BPF_DIV | BPF_X: /* A /= X */
                jit->seen |= SEEN_XREG | SEEN_RET0;
                /* ltr %r12,%r12 */
                EMIT2(0x12cc);
@@ -370,7 +363,7 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
                /* dlr %r4,%r12 */
                EMIT4(0xb997004c);
                break;
-       case BPF_S_ALU_DIV_K: /* A /= K */
+       case BPF_ALU | BPF_DIV | BPF_K: /* A /= K */
                if (K == 1)
                        break;
                /* lhi %r4,0 */
@@ -378,7 +371,7 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
                /* dl %r4,<d(K)>(%r13) */
                EMIT6_DISP(0xe340d000, 0x0097, EMIT_CONST(K));
                break;
-       case BPF_S_ALU_MOD_X: /* A %= X */
+       case BPF_ALU | BPF_MOD | BPF_X: /* A %= X */
                jit->seen |= SEEN_XREG | SEEN_RET0;
                /* ltr %r12,%r12 */
                EMIT2(0x12cc);
@@ -391,7 +384,7 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
                /* lr %r5,%r4 */
                EMIT2(0x1854);
                break;
-       case BPF_S_ALU_MOD_K: /* A %= K */
+       case BPF_ALU | BPF_MOD | BPF_K: /* A %= K */
                if (K == 1) {
                        /* lhi %r5,0 */
                        EMIT4(0xa7580000);
@@ -404,12 +397,12 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
                /* lr %r5,%r4 */
                EMIT2(0x1854);
                break;
-       case BPF_S_ALU_AND_X: /* A &= X */
+       case BPF_ALU | BPF_AND | BPF_X: /* A &= X */
                jit->seen |= SEEN_XREG;
                /* nr %r5,%r12 */
                EMIT2(0x145c);
                break;
-       case BPF_S_ALU_AND_K: /* A &= K */
+       case BPF_ALU | BPF_AND | BPF_K: /* A &= K */
                if (test_facility(21))
                        /* nilf %r5,<K> */
                        EMIT6_IMM(0xc05b0000, K);
@@ -417,12 +410,12 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
                        /* n %r5,<d(K)>(%r13) */
                        EMIT4_DISP(0x5450d000, EMIT_CONST(K));
                break;
-       case BPF_S_ALU_OR_X: /* A |= X */
+       case BPF_ALU | BPF_OR | BPF_X: /* A |= X */
                jit->seen |= SEEN_XREG;
                /* or %r5,%r12 */
                EMIT2(0x165c);
                break;
-       case BPF_S_ALU_OR_K: /* A |= K */
+       case BPF_ALU | BPF_OR | BPF_K: /* A |= K */
                if (test_facility(21))
                        /* oilf %r5,<K> */
                        EMIT6_IMM(0xc05d0000, K);
@@ -430,55 +423,55 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
                        /* o %r5,<d(K)>(%r13) */
                        EMIT4_DISP(0x5650d000, EMIT_CONST(K));
                break;
-       case BPF_S_ANC_ALU_XOR_X: /* A ^= X; */
-       case BPF_S_ALU_XOR_X:
+       case BPF_ANC | SKF_AD_ALU_XOR_X: /* A ^= X; */
+       case BPF_ALU | BPF_XOR | BPF_X:
                jit->seen |= SEEN_XREG;
                /* xr %r5,%r12 */
                EMIT2(0x175c);
                break;
-       case BPF_S_ALU_XOR_K: /* A ^= K */
+       case BPF_ALU | BPF_XOR | BPF_K: /* A ^= K */
                if (!K)
                        break;
                /* x %r5,<d(K)>(%r13) */
                EMIT4_DISP(0x5750d000, EMIT_CONST(K));
                break;
-       case BPF_S_ALU_LSH_X: /* A <<= X; */
+       case BPF_ALU | BPF_LSH | BPF_X: /* A <<= X; */
                jit->seen |= SEEN_XREG;
                /* sll %r5,0(%r12) */
                EMIT4(0x8950c000);
                break;
-       case BPF_S_ALU_LSH_K: /* A <<= K */
+       case BPF_ALU | BPF_LSH | BPF_K: /* A <<= K */
                if (K == 0)
                        break;
                /* sll %r5,K */
                EMIT4_DISP(0x89500000, K);
                break;
-       case BPF_S_ALU_RSH_X: /* A >>= X; */
+       case BPF_ALU | BPF_RSH | BPF_X: /* A >>= X; */
                jit->seen |= SEEN_XREG;
                /* srl %r5,0(%r12) */
                EMIT4(0x8850c000);
                break;
-       case BPF_S_ALU_RSH_K: /* A >>= K; */
+       case BPF_ALU | BPF_RSH | BPF_K: /* A >>= K; */
                if (K == 0)
                        break;
                /* srl %r5,K */
                EMIT4_DISP(0x88500000, K);
                break;
-       case BPF_S_ALU_NEG: /* A = -A */
+       case BPF_ALU | BPF_NEG: /* A = -A */
                /* lnr %r5,%r5 */
                EMIT2(0x1155);
                break;
-       case BPF_S_JMP_JA: /* ip += K */
+       case BPF_JMP | BPF_JA: /* ip += K */
                offset = addrs[i + K] + jit->start - jit->prg;
                EMIT4_PCREL(0xa7f40000, offset);
                break;
-       case BPF_S_JMP_JGT_K: /* ip += (A > K) ? jt : jf */
+       case BPF_JMP | BPF_JGT | BPF_K: /* ip += (A > K) ? jt : jf */
                mask = 0x200000; /* jh */
                goto kbranch;
-       case BPF_S_JMP_JGE_K: /* ip += (A >= K) ? jt : jf */
+       case BPF_JMP | BPF_JGE | BPF_K: /* ip += (A >= K) ? jt : jf */
                mask = 0xa00000; /* jhe */
                goto kbranch;
-       case BPF_S_JMP_JEQ_K: /* ip += (A == K) ? jt : jf */
+       case BPF_JMP | BPF_JEQ | BPF_K: /* ip += (A == K) ? jt : jf */
                mask = 0x800000; /* je */
 kbranch:       /* Emit compare if the branch targets are different */
                if (filter->jt != filter->jf) {
@@ -511,7 +504,7 @@ branch:             if (filter->jt == filter->jf) {
                        EMIT4_PCREL(0xa7040000 | (mask ^ 0xf00000), offset);
                }
                break;
-       case BPF_S_JMP_JSET_K: /* ip += (A & K) ? jt : jf */
+       case BPF_JMP | BPF_JSET | BPF_K: /* ip += (A & K) ? jt : jf */
                mask = 0x700000; /* jnz */
                /* Emit test if the branch targets are different */
                if (filter->jt != filter->jf) {
@@ -525,13 +518,13 @@ branch:           if (filter->jt == filter->jf) {
                                EMIT4_IMM(0xa7510000, K);
                }
                goto branch;
-       case BPF_S_JMP_JGT_X: /* ip += (A > X) ? jt : jf */
+       case BPF_JMP | BPF_JGT | BPF_X: /* ip += (A > X) ? jt : jf */
                mask = 0x200000; /* jh */
                goto xbranch;
-       case BPF_S_JMP_JGE_X: /* ip += (A >= X) ? jt : jf */
+       case BPF_JMP | BPF_JGE | BPF_X: /* ip += (A >= X) ? jt : jf */
                mask = 0xa00000; /* jhe */
                goto xbranch;
-       case BPF_S_JMP_JEQ_X: /* ip += (A == X) ? jt : jf */
+       case BPF_JMP | BPF_JEQ | BPF_X: /* ip += (A == X) ? jt : jf */
                mask = 0x800000; /* je */
 xbranch:       /* Emit compare if the branch targets are different */
                if (filter->jt != filter->jf) {
@@ -540,7 +533,7 @@ xbranch:    /* Emit compare if the branch targets are different */
                        EMIT2(0x195c);
                }
                goto branch;
-       case BPF_S_JMP_JSET_X: /* ip += (A & X) ? jt : jf */
+       case BPF_JMP | BPF_JSET | BPF_X: /* ip += (A & X) ? jt : jf */
                mask = 0x700000; /* jnz */
                /* Emit test if the branch targets are different */
                if (filter->jt != filter->jf) {
@@ -551,15 +544,15 @@ xbranch:  /* Emit compare if the branch targets are different */
                        EMIT2(0x144c);
                }
                goto branch;
-       case BPF_S_LD_W_ABS: /* A = *(u32 *) (skb->data+K) */
+       case BPF_LD | BPF_W | BPF_ABS: /* A = *(u32 *) (skb->data+K) */
                jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_WORD;
                offset = jit->off_load_word;
                goto load_abs;
-       case BPF_S_LD_H_ABS: /* A = *(u16 *) (skb->data+K) */
+       case BPF_LD | BPF_H | BPF_ABS: /* A = *(u16 *) (skb->data+K) */
                jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_HALF;
                offset = jit->off_load_half;
                goto load_abs;
-       case BPF_S_LD_B_ABS: /* A = *(u8 *) (skb->data+K) */
+       case BPF_LD | BPF_B | BPF_ABS: /* A = *(u8 *) (skb->data+K) */
                jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_BYTE;
                offset = jit->off_load_byte;
 load_abs:      if ((int) K < 0)
@@ -573,19 +566,19 @@ call_fn:  /* lg %r1,<d(function)>(%r13) */
                /* jnz <ret0> */
                EMIT4_PCREL(0xa7740000, (jit->ret0_ip - jit->prg));
                break;
-       case BPF_S_LD_W_IND: /* A = *(u32 *) (skb->data+K+X) */
+       case BPF_LD | BPF_W | BPF_IND: /* A = *(u32 *) (skb->data+K+X) */
                jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_IWORD;
                offset = jit->off_load_iword;
                goto call_fn;
-       case BPF_S_LD_H_IND: /* A = *(u16 *) (skb->data+K+X) */
+       case BPF_LD | BPF_H | BPF_IND: /* A = *(u16 *) (skb->data+K+X) */
                jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_IHALF;
                offset = jit->off_load_ihalf;
                goto call_fn;
-       case BPF_S_LD_B_IND: /* A = *(u8 *) (skb->data+K+X) */
+       case BPF_LD | BPF_B | BPF_IND: /* A = *(u8 *) (skb->data+K+X) */
                jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_IBYTE;
                offset = jit->off_load_ibyte;
                goto call_fn;
-       case BPF_S_LDX_B_MSH:
+       case BPF_LDX | BPF_B | BPF_MSH:
                /* X = (*(u8 *)(skb->data+K) & 0xf) << 2 */
                jit->seen |= SEEN_RET0;
                if ((int) K < 0) {
@@ -596,17 +589,17 @@ call_fn:  /* lg %r1,<d(function)>(%r13) */
                jit->seen |= SEEN_DATAREF | SEEN_LOAD_BMSH;
                offset = jit->off_load_bmsh;
                goto call_fn;
-       case BPF_S_LD_W_LEN: /* A = skb->len; */
+       case BPF_LD | BPF_W | BPF_LEN: /*       A = skb->len; */
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4);
                /* l %r5,<d(len)>(%r2) */
                EMIT4_DISP(0x58502000, offsetof(struct sk_buff, len));
                break;
-       case BPF_S_LDX_W_LEN: /* X = skb->len; */
+       case BPF_LDX | BPF_W | BPF_LEN: /* X = skb->len; */
                jit->seen |= SEEN_XREG;
                /* l %r12,<d(len)>(%r2) */
                EMIT4_DISP(0x58c02000, offsetof(struct sk_buff, len));
                break;
-       case BPF_S_LD_IMM: /* A = K */
+       case BPF_LD | BPF_IMM: /* A = K */
                if (K <= 16383)
                        /* lhi %r5,K */
                        EMIT4_IMM(0xa7580000, K);
@@ -617,7 +610,7 @@ call_fn:    /* lg %r1,<d(function)>(%r13) */
                        /* l %r5,<d(K)>(%r13) */
                        EMIT4_DISP(0x5850d000, EMIT_CONST(K));
                break;
-       case BPF_S_LDX_IMM: /* X = K */
+       case BPF_LDX | BPF_IMM: /* X = K */
                jit->seen |= SEEN_XREG;
                if (K <= 16383)
                        /* lhi %r12,<K> */
@@ -629,29 +622,29 @@ call_fn:  /* lg %r1,<d(function)>(%r13) */
                        /* l %r12,<d(K)>(%r13) */
                        EMIT4_DISP(0x58c0d000, EMIT_CONST(K));
                break;
-       case BPF_S_LD_MEM: /* A = mem[K] */
+       case BPF_LD | BPF_MEM: /* A = mem[K] */
                jit->seen |= SEEN_MEM;
                /* l %r5,<K>(%r15) */
                EMIT4_DISP(0x5850f000,
                           (jit->seen & SEEN_DATAREF) ? 160 + K*4 : K*4);
                break;
-       case BPF_S_LDX_MEM: /* X = mem[K] */
+       case BPF_LDX | BPF_MEM: /* X = mem[K] */
                jit->seen |= SEEN_XREG | SEEN_MEM;
                /* l %r12,<K>(%r15) */
                EMIT4_DISP(0x58c0f000,
                           (jit->seen & SEEN_DATAREF) ? 160 + K*4 : K*4);
                break;
-       case BPF_S_MISC_TAX: /* X = A */
+       case BPF_MISC | BPF_TAX: /* X = A */
                jit->seen |= SEEN_XREG;
                /* lr %r12,%r5 */
                EMIT2(0x18c5);
                break;
-       case BPF_S_MISC_TXA: /* A = X */
+       case BPF_MISC | BPF_TXA: /* A = X */
                jit->seen |= SEEN_XREG;
                /* lr %r5,%r12 */
                EMIT2(0x185c);
                break;
-       case BPF_S_RET_K:
+       case BPF_RET | BPF_K:
                if (K == 0) {
                        jit->seen |= SEEN_RET0;
                        if (last)
@@ -671,33 +664,33 @@ call_fn:  /* lg %r1,<d(function)>(%r13) */
                        EMIT4_PCREL(0xa7f40000, jit->exit_ip - jit->prg);
                }
                break;
-       case BPF_S_RET_A:
+       case BPF_RET | BPF_A:
                /* llgfr %r2,%r5 */
                EMIT4(0xb9160025);
                /* j <exit> */
                EMIT4_PCREL(0xa7f40000, jit->exit_ip - jit->prg);
                break;
-       case BPF_S_ST: /* mem[K] = A */
+       case BPF_ST: /* mem[K] = A */
                jit->seen |= SEEN_MEM;
                /* st %r5,<K>(%r15) */
                EMIT4_DISP(0x5050f000,
                           (jit->seen & SEEN_DATAREF) ? 160 + K*4 : K*4);
                break;
-       case BPF_S_STX: /* mem[K] = X : mov %ebx,off8(%rbp) */
+       case BPF_STX: /* mem[K] = X : mov %ebx,off8(%rbp) */
                jit->seen |= SEEN_XREG | SEEN_MEM;
                /* st %r12,<K>(%r15) */
                EMIT4_DISP(0x50c0f000,
                           (jit->seen & SEEN_DATAREF) ? 160 + K*4 : K*4);
                break;
-       case BPF_S_ANC_PROTOCOL: /* A = ntohs(skb->protocol); */
+       case BPF_ANC | SKF_AD_PROTOCOL: /* A = ntohs(skb->protocol); */
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2);
                /* lhi %r5,0 */
                EMIT4(0xa7580000);
                /* icm  %r5,3,<d(protocol)>(%r2) */
                EMIT4_DISP(0xbf532000, offsetof(struct sk_buff, protocol));
                break;
-       case BPF_S_ANC_IFINDEX: /* if (!skb->dev) return 0;
-                                * A = skb->dev->ifindex */
+       case BPF_ANC | SKF_AD_IFINDEX:  /* if (!skb->dev) return 0;
+                                        * A = skb->dev->ifindex */
                BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, ifindex) != 4);
                jit->seen |= SEEN_RET0;
                /* lg %r1,<d(dev)>(%r2) */
@@ -709,20 +702,20 @@ call_fn:  /* lg %r1,<d(function)>(%r13) */
                /* l %r5,<d(ifindex)>(%r1) */
                EMIT4_DISP(0x58501000, offsetof(struct net_device, ifindex));
                break;
-       case BPF_S_ANC_MARK: /* A = skb->mark */
+       case BPF_ANC | SKF_AD_MARK: /* A = skb->mark */
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4);
                /* l %r5,<d(mark)>(%r2) */
                EMIT4_DISP(0x58502000, offsetof(struct sk_buff, mark));
                break;
-       case BPF_S_ANC_QUEUE: /* A = skb->queue_mapping */
+       case BPF_ANC | SKF_AD_QUEUE: /* A = skb->queue_mapping */
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2);
                /* lhi %r5,0 */
                EMIT4(0xa7580000);
                /* icm  %r5,3,<d(queue_mapping)>(%r2) */
                EMIT4_DISP(0xbf532000, offsetof(struct sk_buff, queue_mapping));
                break;
-       case BPF_S_ANC_HATYPE:  /* if (!skb->dev) return 0;
-                                * A = skb->dev->type */
+       case BPF_ANC | SKF_AD_HATYPE:   /* if (!skb->dev) return 0;
+                                        * A = skb->dev->type */
                BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, type) != 2);
                jit->seen |= SEEN_RET0;
                /* lg %r1,<d(dev)>(%r2) */
@@ -736,20 +729,20 @@ call_fn:  /* lg %r1,<d(function)>(%r13) */
                /* icm  %r5,3,<d(type)>(%r1) */
                EMIT4_DISP(0xbf531000, offsetof(struct net_device, type));
                break;
-       case BPF_S_ANC_RXHASH: /* A = skb->hash */
+       case BPF_ANC | SKF_AD_RXHASH: /* A = skb->hash */
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4);
                /* l %r5,<d(hash)>(%r2) */
                EMIT4_DISP(0x58502000, offsetof(struct sk_buff, hash));
                break;
-       case BPF_S_ANC_VLAN_TAG:
-       case BPF_S_ANC_VLAN_TAG_PRESENT:
+       case BPF_ANC | SKF_AD_VLAN_TAG:
+       case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2);
                BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000);
                /* lhi %r5,0 */
                EMIT4(0xa7580000);
                /* icm  %r5,3,<d(vlan_tci)>(%r2) */
                EMIT4_DISP(0xbf532000, offsetof(struct sk_buff, vlan_tci));
-               if (filter->code == BPF_S_ANC_VLAN_TAG) {
+               if (code == (BPF_ANC | SKF_AD_VLAN_TAG)) {
                        /* nill %r5,0xefff */
                        EMIT4_IMM(0xa5570000, ~VLAN_TAG_PRESENT);
                } else {
@@ -759,7 +752,7 @@ call_fn:    /* lg %r1,<d(function)>(%r13) */
                        EMIT4_DISP(0x88500000, 12);
                }
                break;
-       case BPF_S_ANC_PKTTYPE:
+       case BPF_ANC | SKF_AD_PKTTYPE:
                if (pkt_type_offset < 0)
                        goto out;
                /* lhi %r5,0 */
@@ -769,7 +762,7 @@ call_fn:    /* lg %r1,<d(function)>(%r13) */
                /* srl %r5,5 */
                EMIT4_DISP(0x88500000, 5);
                break;
-       case BPF_S_ANC_CPU: /* A = smp_processor_id() */
+       case BPF_ANC | SKF_AD_CPU: /* A = smp_processor_id() */
 #ifdef CONFIG_SMP
                /* l %r5,<d(cpu_nr)> */
                EMIT4_DISP(0x58500000, offsetof(struct _lowcore, cpu_nr));
index bdbda1453aa9f168339896d6880e3e2acd44182f..04471dc64847269e815a26752ef029cd9206c154 100644 (file)
@@ -238,4 +238,16 @@ static inline __sum16 ip_compute_csum(const void *buff, int len)
        return csum_fold(csum_partial(buff, len, 0));
 }
 
+#define HAVE_ARCH_CSUM_ADD
+static inline __wsum csum_add(__wsum csum, __wsum addend)
+{
+       __asm__ __volatile__(
+               "addcc   %0, %1, %0\n"
+               "addx    %0, %%g0, %0"
+               : "=r" (csum)
+               : "r" (addend), "0" (csum));
+
+       return csum;
+}
+
 #endif /* !(__SPARC_CHECKSUM_H) */
index 019b9615e43c16b81230508109b2484d6313ee62..2ff81ae8f3afa17b0fd25a5a6786131a9e30bc1b 100644 (file)
@@ -164,4 +164,16 @@ static inline __sum16 ip_compute_csum(const void *buff, int len)
        return csum_fold(csum_partial(buff, len, 0));
 }
 
+#define HAVE_ARCH_CSUM_ADD
+static inline __wsum csum_add(__wsum csum, __wsum addend)
+{
+       __asm__ __volatile__(
+               "addcc   %0, %1, %0\n"
+               "addx    %0, %%g0, %0"
+               : "=r" (csum)
+               : "r" (addend), "0" (csum));
+
+       return csum;
+}
+
 #endif /* !(__SPARC64_CHECKSUM_H) */
index a82c6b2a9780cab8d882c2d13429f4889375229e..892a102671adafc03950f907612f23b6902f934a 100644 (file)
@@ -83,9 +83,9 @@ static void bpf_flush_icache(void *start_, void *end_)
 #define BNE            (F2(0, 2) | CONDNE)
 
 #ifdef CONFIG_SPARC64
-#define BNE_PTR                (F2(0, 1) | CONDNE | (2 << 20))
+#define BE_PTR         (F2(0, 1) | CONDE | (2 << 20))
 #else
-#define BNE_PTR                BNE
+#define BE_PTR         BE
 #endif
 
 #define SETHI(K, REG)  \
@@ -415,20 +415,11 @@ void bpf_jit_compile(struct sk_filter *fp)
                emit_reg_move(O7, r_saved_O7);
 
                switch (filter[0].code) {
-               case BPF_S_RET_K:
-               case BPF_S_LD_W_LEN:
-               case BPF_S_ANC_PROTOCOL:
-               case BPF_S_ANC_PKTTYPE:
-               case BPF_S_ANC_IFINDEX:
-               case BPF_S_ANC_MARK:
-               case BPF_S_ANC_RXHASH:
-               case BPF_S_ANC_VLAN_TAG:
-               case BPF_S_ANC_VLAN_TAG_PRESENT:
-               case BPF_S_ANC_CPU:
-               case BPF_S_ANC_QUEUE:
-               case BPF_S_LD_W_ABS:
-               case BPF_S_LD_H_ABS:
-               case BPF_S_LD_B_ABS:
+               case BPF_RET | BPF_K:
+               case BPF_LD | BPF_W | BPF_LEN:
+               case BPF_LD | BPF_W | BPF_ABS:
+               case BPF_LD | BPF_H | BPF_ABS:
+               case BPF_LD | BPF_B | BPF_ABS:
                        /* The first instruction sets the A register (or is
                         * a "RET 'constant'")
                         */
@@ -445,59 +436,60 @@ void bpf_jit_compile(struct sk_filter *fp)
                        unsigned int t_offset;
                        unsigned int f_offset;
                        u32 t_op, f_op;
+                       u16 code = bpf_anc_helper(&filter[i]);
                        int ilen;
 
-                       switch (filter[i].code) {
-                       case BPF_S_ALU_ADD_X:   /* A += X; */
+                       switch (code) {
+                       case BPF_ALU | BPF_ADD | BPF_X: /* A += X; */
                                emit_alu_X(ADD);
                                break;
-                       case BPF_S_ALU_ADD_K:   /* A += K; */
+                       case BPF_ALU | BPF_ADD | BPF_K: /* A += K; */
                                emit_alu_K(ADD, K);
                                break;
-                       case BPF_S_ALU_SUB_X:   /* A -= X; */
+                       case BPF_ALU | BPF_SUB | BPF_X: /* A -= X; */
                                emit_alu_X(SUB);
                                break;
-                       case BPF_S_ALU_SUB_K:   /* A -= K */
+                       case BPF_ALU | BPF_SUB | BPF_K: /* A -= K */
                                emit_alu_K(SUB, K);
                                break;
-                       case BPF_S_ALU_AND_X:   /* A &= X */
+                       case BPF_ALU | BPF_AND | BPF_X: /* A &= X */
                                emit_alu_X(AND);
                                break;
-                       case BPF_S_ALU_AND_K:   /* A &= K */
+                       case BPF_ALU | BPF_AND | BPF_K: /* A &= K */
                                emit_alu_K(AND, K);
                                break;
-                       case BPF_S_ALU_OR_X:    /* A |= X */
+                       case BPF_ALU | BPF_OR | BPF_X:  /* A |= X */
                                emit_alu_X(OR);
                                break;
-                       case BPF_S_ALU_OR_K:    /* A |= K */
+                       case BPF_ALU | BPF_OR | BPF_K:  /* A |= K */
                                emit_alu_K(OR, K);
                                break;
-                       case BPF_S_ANC_ALU_XOR_X: /* A ^= X; */
-                       case BPF_S_ALU_XOR_X:
+                       case BPF_ANC | SKF_AD_ALU_XOR_X: /* A ^= X; */
+                       case BPF_ALU | BPF_XOR | BPF_X:
                                emit_alu_X(XOR);
                                break;
-                       case BPF_S_ALU_XOR_K:   /* A ^= K */
+                       case BPF_ALU | BPF_XOR | BPF_K: /* A ^= K */
                                emit_alu_K(XOR, K);
                                break;
-                       case BPF_S_ALU_LSH_X:   /* A <<= X */
+                       case BPF_ALU | BPF_LSH | BPF_X: /* A <<= X */
                                emit_alu_X(SLL);
                                break;
-                       case BPF_S_ALU_LSH_K:   /* A <<= K */
+                       case BPF_ALU | BPF_LSH | BPF_K: /* A <<= K */
                                emit_alu_K(SLL, K);
                                break;
-                       case BPF_S_ALU_RSH_X:   /* A >>= X */
+                       case BPF_ALU | BPF_RSH | BPF_X: /* A >>= X */
                                emit_alu_X(SRL);
                                break;
-                       case BPF_S_ALU_RSH_K:   /* A >>= K */
+                       case BPF_ALU | BPF_RSH | BPF_K: /* A >>= K */
                                emit_alu_K(SRL, K);
                                break;
-                       case BPF_S_ALU_MUL_X:   /* A *= X; */
+                       case BPF_ALU | BPF_MUL | BPF_X: /* A *= X; */
                                emit_alu_X(MUL);
                                break;
-                       case BPF_S_ALU_MUL_K:   /* A *= K */
+                       case BPF_ALU | BPF_MUL | BPF_K: /* A *= K */
                                emit_alu_K(MUL, K);
                                break;
-                       case BPF_S_ALU_DIV_K:   /* A /= K with K != 0*/
+                       case BPF_ALU | BPF_DIV | BPF_K: /* A /= K with K != 0*/
                                if (K == 1)
                                        break;
                                emit_write_y(G0);
@@ -512,7 +504,7 @@ void bpf_jit_compile(struct sk_filter *fp)
 #endif
                                emit_alu_K(DIV, K);
                                break;
-                       case BPF_S_ALU_DIV_X:   /* A /= X; */
+                       case BPF_ALU | BPF_DIV | BPF_X: /* A /= X; */
                                emit_cmpi(r_X, 0);
                                if (pc_ret0 > 0) {
                                        t_offset = addrs[pc_ret0 - 1];
@@ -544,10 +536,10 @@ void bpf_jit_compile(struct sk_filter *fp)
 #endif
                                emit_alu_X(DIV);
                                break;
-                       case BPF_S_ALU_NEG:
+                       case BPF_ALU | BPF_NEG:
                                emit_neg();
                                break;
-                       case BPF_S_RET_K:
+                       case BPF_RET | BPF_K:
                                if (!K) {
                                        if (pc_ret0 == -1)
                                                pc_ret0 = i;
@@ -556,7 +548,7 @@ void bpf_jit_compile(struct sk_filter *fp)
                                        emit_loadimm(K, r_A);
                                }
                                /* Fallthrough */
-                       case BPF_S_RET_A:
+                       case BPF_RET | BPF_A:
                                if (seen_or_pass0) {
                                        if (i != flen - 1) {
                                                emit_jump(cleanup_addr);
@@ -573,18 +565,18 @@ void bpf_jit_compile(struct sk_filter *fp)
                                emit_jmpl(r_saved_O7, 8, G0);
                                emit_reg_move(r_A, O0); /* delay slot */
                                break;
-                       case BPF_S_MISC_TAX:
+                       case BPF_MISC | BPF_TAX:
                                seen |= SEEN_XREG;
                                emit_reg_move(r_A, r_X);
                                break;
-                       case BPF_S_MISC_TXA:
+                       case BPF_MISC | BPF_TXA:
                                seen |= SEEN_XREG;
                                emit_reg_move(r_X, r_A);
                                break;
-                       case BPF_S_ANC_CPU:
+                       case BPF_ANC | SKF_AD_CPU:
                                emit_load_cpu(r_A);
                                break;
-                       case BPF_S_ANC_PROTOCOL:
+                       case BPF_ANC | SKF_AD_PROTOCOL:
                                emit_skb_load16(protocol, r_A);
                                break;
 #if 0
@@ -592,38 +584,38 @@ void bpf_jit_compile(struct sk_filter *fp)
                                 * a bit field even though we very much
                                 * know what we are doing here.
                                 */
-                       case BPF_S_ANC_PKTTYPE:
+                       case BPF_ANC | SKF_AD_PKTTYPE:
                                __emit_skb_load8(pkt_type, r_A);
                                emit_alu_K(SRL, 5);
                                break;
 #endif
-                       case BPF_S_ANC_IFINDEX:
+                       case BPF_ANC | SKF_AD_IFINDEX:
                                emit_skb_loadptr(dev, r_A);
                                emit_cmpi(r_A, 0);
-                               emit_branch(BNE_PTR, cleanup_addr + 4);
+                               emit_branch(BE_PTR, cleanup_addr + 4);
                                emit_nop();
                                emit_load32(r_A, struct net_device, ifindex, r_A);
                                break;
-                       case BPF_S_ANC_MARK:
+                       case BPF_ANC | SKF_AD_MARK:
                                emit_skb_load32(mark, r_A);
                                break;
-                       case BPF_S_ANC_QUEUE:
+                       case BPF_ANC | SKF_AD_QUEUE:
                                emit_skb_load16(queue_mapping, r_A);
                                break;
-                       case BPF_S_ANC_HATYPE:
+                       case BPF_ANC | SKF_AD_HATYPE:
                                emit_skb_loadptr(dev, r_A);
                                emit_cmpi(r_A, 0);
-                               emit_branch(BNE_PTR, cleanup_addr + 4);
+                               emit_branch(BE_PTR, cleanup_addr + 4);
                                emit_nop();
                                emit_load16(r_A, struct net_device, type, r_A);
                                break;
-                       case BPF_S_ANC_RXHASH:
+                       case BPF_ANC | SKF_AD_RXHASH:
                                emit_skb_load32(hash, r_A);
                                break;
-                       case BPF_S_ANC_VLAN_TAG:
-                       case BPF_S_ANC_VLAN_TAG_PRESENT:
+                       case BPF_ANC | SKF_AD_VLAN_TAG:
+                       case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
                                emit_skb_load16(vlan_tci, r_A);
-                               if (filter[i].code == BPF_S_ANC_VLAN_TAG) {
+                               if (code == (BPF_ANC | SKF_AD_VLAN_TAG)) {
                                        emit_andi(r_A, VLAN_VID_MASK, r_A);
                                } else {
                                        emit_loadimm(VLAN_TAG_PRESENT, r_TMP);
@@ -631,44 +623,44 @@ void bpf_jit_compile(struct sk_filter *fp)
                                }
                                break;
 
-                       case BPF_S_LD_IMM:
+                       case BPF_LD | BPF_IMM:
                                emit_loadimm(K, r_A);
                                break;
-                       case BPF_S_LDX_IMM:
+                       case BPF_LDX | BPF_IMM:
                                emit_loadimm(K, r_X);
                                break;
-                       case BPF_S_LD_MEM:
+                       case BPF_LD | BPF_MEM:
                                emit_ldmem(K * 4, r_A);
                                break;
-                       case BPF_S_LDX_MEM:
+                       case BPF_LDX | BPF_MEM:
                                emit_ldmem(K * 4, r_X);
                                break;
-                       case BPF_S_ST:
+                       case BPF_ST:
                                emit_stmem(K * 4, r_A);
                                break;
-                       case BPF_S_STX:
+                       case BPF_STX:
                                emit_stmem(K * 4, r_X);
                                break;
 
 #define CHOOSE_LOAD_FUNC(K, func) \
        ((int)K < 0 ? ((int)K >= SKF_LL_OFF ? func##_negative_offset : func) : func##_positive_offset)
 
-                       case BPF_S_LD_W_ABS:
+                       case BPF_LD | BPF_W | BPF_ABS:
                                func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_word);
 common_load:                   seen |= SEEN_DATAREF;
                                emit_loadimm(K, r_OFF);
                                emit_call(func);
                                break;
-                       case BPF_S_LD_H_ABS:
+                       case BPF_LD | BPF_H | BPF_ABS:
                                func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_half);
                                goto common_load;
-                       case BPF_S_LD_B_ABS:
+                       case BPF_LD | BPF_B | BPF_ABS:
                                func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_byte);
                                goto common_load;
-                       case BPF_S_LDX_B_MSH:
+                       case BPF_LDX | BPF_B | BPF_MSH:
                                func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_byte_msh);
                                goto common_load;
-                       case BPF_S_LD_W_IND:
+                       case BPF_LD | BPF_W | BPF_IND:
                                func = bpf_jit_load_word;
 common_load_ind:               seen |= SEEN_DATAREF | SEEN_XREG;
                                if (K) {
@@ -683,13 +675,13 @@ common_load_ind:          seen |= SEEN_DATAREF | SEEN_XREG;
                                }
                                emit_call(func);
                                break;
-                       case BPF_S_LD_H_IND:
+                       case BPF_LD | BPF_H | BPF_IND:
                                func = bpf_jit_load_half;
                                goto common_load_ind;
-                       case BPF_S_LD_B_IND:
+                       case BPF_LD | BPF_B | BPF_IND:
                                func = bpf_jit_load_byte;
                                goto common_load_ind;
-                       case BPF_S_JMP_JA:
+                       case BPF_JMP | BPF_JA:
                                emit_jump(addrs[i + K]);
                                emit_nop();
                                break;
@@ -700,14 +692,14 @@ common_load_ind:          seen |= SEEN_DATAREF | SEEN_XREG;
                f_op = FOP;             \
                goto cond_branch
 
-                       COND_SEL(BPF_S_JMP_JGT_K, BGU, BLEU);
-                       COND_SEL(BPF_S_JMP_JGE_K, BGEU, BLU);
-                       COND_SEL(BPF_S_JMP_JEQ_K, BE, BNE);
-                       COND_SEL(BPF_S_JMP_JSET_K, BNE, BE);
-                       COND_SEL(BPF_S_JMP_JGT_X, BGU, BLEU);
-                       COND_SEL(BPF_S_JMP_JGE_X, BGEU, BLU);
-                       COND_SEL(BPF_S_JMP_JEQ_X, BE, BNE);
-                       COND_SEL(BPF_S_JMP_JSET_X, BNE, BE);
+                       COND_SEL(BPF_JMP | BPF_JGT | BPF_K, BGU, BLEU);
+                       COND_SEL(BPF_JMP | BPF_JGE | BPF_K, BGEU, BLU);
+                       COND_SEL(BPF_JMP | BPF_JEQ | BPF_K, BE, BNE);
+                       COND_SEL(BPF_JMP | BPF_JSET | BPF_K, BNE, BE);
+                       COND_SEL(BPF_JMP | BPF_JGT | BPF_X, BGU, BLEU);
+                       COND_SEL(BPF_JMP | BPF_JGE | BPF_X, BGEU, BLU);
+                       COND_SEL(BPF_JMP | BPF_JEQ | BPF_X, BE, BNE);
+                       COND_SEL(BPF_JMP | BPF_JSET | BPF_X, BNE, BE);
 
 cond_branch:                   f_offset = addrs[i + filter[i].jf];
                                t_offset = addrs[i + filter[i].jt];
@@ -719,20 +711,20 @@ cond_branch:                      f_offset = addrs[i + filter[i].jf];
                                        break;
                                }
 
-                               switch (filter[i].code) {
-                               case BPF_S_JMP_JGT_X:
-                               case BPF_S_JMP_JGE_X:
-                               case BPF_S_JMP_JEQ_X:
+                               switch (code) {
+                               case BPF_JMP | BPF_JGT | BPF_X:
+                               case BPF_JMP | BPF_JGE | BPF_X:
+                               case BPF_JMP | BPF_JEQ | BPF_X:
                                        seen |= SEEN_XREG;
                                        emit_cmp(r_A, r_X);
                                        break;
-                               case BPF_S_JMP_JSET_X:
+                               case BPF_JMP | BPF_JSET | BPF_X:
                                        seen |= SEEN_XREG;
                                        emit_btst(r_A, r_X);
                                        break;
-                               case BPF_S_JMP_JEQ_K:
-                               case BPF_S_JMP_JGT_K:
-                               case BPF_S_JMP_JGE_K:
+                               case BPF_JMP | BPF_JEQ | BPF_K:
+                               case BPF_JMP | BPF_JGT | BPF_K:
+                               case BPF_JMP | BPF_JGE | BPF_K:
                                        if (is_simm13(K)) {
                                                emit_cmpi(r_A, K);
                                        } else {
@@ -740,7 +732,7 @@ cond_branch:                        f_offset = addrs[i + filter[i].jf];
                                                emit_cmp(r_A, r_TMP);
                                        }
                                        break;
-                               case BPF_S_JMP_JSET_K:
+                               case BPF_JMP | BPF_JSET | BPF_K:
                                        if (is_simm13(K)) {
                                                emit_btsti(r_A, K);
                                        } else {
index d767ff9f59b9c1532aa14dd8c8b342cd9a82155d..48e4fd0f38e49e70c3a7fee5065af0f5931a1bec 100644 (file)
@@ -94,7 +94,7 @@ register unsigned long stack_pointer __asm__("sp");
 /* Sit on a nap instruction until interrupted. */
 extern void smp_nap(void);
 
-/* Enable interrupts racelessly and nap forever: helper for cpu_idle(). */
+/* Enable interrupts racelessly and nap forever: helper for arch_cpu_idle(). */
 extern void _cpu_idle(void);
 
 #else /* __ASSEMBLY__ */
index 74c91729a62a9290e12bb31ea96ab7ad240aebc2..112ababa9e5502c6593969d44c976fbd495cf663 100644 (file)
@@ -228,13 +228,10 @@ early_param("isolnodes", setup_isolnodes);
 #if defined(CONFIG_PCI) && !defined(__tilegx__)
 static int __init setup_pci_reserve(char* str)
 {
-       unsigned long mb;
-
-       if (str == NULL || strict_strtoul(str, 0, &mb) != 0 ||
-           mb > 3 * 1024)
+       if (str == NULL || kstrtouint(str, 0, &pci_reserve_mb) != 0 ||
+           pci_reserve_mb > 3 * 1024)
                return -EINVAL;
 
-       pci_reserve_mb = mb;
        pr_info("Reserving %dMB for PCIE root complex mappings\n",
                pci_reserve_mb);
        return 0;
@@ -691,7 +688,7 @@ static void __init setup_bootmem_allocator(void)
        /* Reserve any memory excluded by "memmap" arguments. */
        for (i = 0; i < memmap_nr; ++i) {
                struct memmap_entry *m = &memmap_map[i];
-               reserve_bootmem(m->addr, m->size, 0);
+               reserve_bootmem(m->addr, m->size, BOOTMEM_DEFAULT);
        }
 
 #ifdef CONFIG_BLK_DEV_INITRD
@@ -715,7 +712,8 @@ static void __init setup_bootmem_allocator(void)
 
 #ifdef CONFIG_KEXEC
        if (crashk_res.start != crashk_res.end)
-               reserve_bootmem(crashk_res.start, resource_size(&crashk_res), 0);
+               reserve_bootmem(crashk_res.start, resource_size(&crashk_res),
+                               BOOTMEM_DEFAULT);
 #endif
 }
 
index 2d1dbf38a9abe8ff9bfd8fcc18d8e18819889c23..d1d026f0126715d2620c0de1a258f9b806892b4a 100644 (file)
@@ -321,14 +321,13 @@ int show_unhandled_signals = 1;
 
 static int __init crashinfo(char *str)
 {
-       unsigned long val;
        const char *word;
 
        if (*str == '\0')
-               val = 2;
-       else if (*str != '=' || strict_strtoul(++str, 0, &val) != 0)
+               show_unhandled_signals = 2;
+       else if (*str != '=' || kstrtoint(++str, 0, &show_unhandled_signals) != 0)
                return 0;
-       show_unhandled_signals = val;
+
        switch (show_unhandled_signals) {
        case 0:
                word = "No";
index 6b603d556ca6ddc1e431fdbd1b434f28ca4c20b7..f3ceb6308e42f3672183f95df8ad3bdf5f05ef2d 100644 (file)
@@ -42,10 +42,9 @@ static int __init setup_unaligned_fixup(char *str)
         * will still parse the instruction, then fire a SIGBUS with
         * the correct address from inside the single_step code.
         */
-       long val;
-       if (strict_strtol(str, 0, &val) != 0)
+       if (kstrtoint(str, 0, &unaligned_fixup) != 0)
                return 0;
-       unaligned_fixup = val;
+
        pr_info("Fixups for unaligned data accesses are %s\n",
               unaligned_fixup >= 0 ?
               (unaligned_fixup ? "enabled" : "disabled") :
index b030b4e78845b596c1fc429687e360a2791e7046..c02ea2a45f679c056a9162911a0c0f0128ace0f9 100644 (file)
@@ -182,18 +182,7 @@ static void find_regs(tilegx_bundle_bits bundle, uint64_t *rd, uint64_t *ra,
        int i;
        uint64_t reg;
        uint64_t reg_map = 0, alias_reg_map = 0, map;
-       bool alias;
-
-       *ra = -1;
-       *rb = -1;
-
-       if (rd)
-               *rd = -1;
-
-       *clob1 = -1;
-       *clob2 = -1;
-       *clob3 = -1;
-       alias = false;
+       bool alias = false;
 
        /*
         * Parse fault bundle, find potential used registers and mark
@@ -569,7 +558,7 @@ void jit_bundle_gen(struct pt_regs *regs, tilegx_bundle_bits bundle,
        tilegx_bundle_bits bundle_2 = 0;
        /* If bundle_2_enable = false, bundle_2 is fnop/nop operation. */
        bool     bundle_2_enable = true;
-       uint64_t ra, rb, rd = -1, clob1, clob2, clob3;
+       uint64_t ra = -1, rb = -1, rd = -1, clob1 = -1, clob2 = -1, clob3 = -1;
        /*
         * Indicate if the unalign access
         * instruction's registers hit with
index 0fa1acfac79ad2a25ba82aed6607e36f868ade7c..bfb3127b4df9e9a53ed250e97d8f60f98eb81607 100644 (file)
@@ -273,9 +273,9 @@ static pgprot_t __init init_pgprot(ulong address)
        /*
         * Otherwise we just hand out consecutive cpus.  To avoid
         * requiring this function to hold state, we just walk forward from
-        * _sdata by PAGE_SIZE, skipping the readonly and init data, to reach
-        * the requested address, while walking cpu home around kdata_mask.
-        * This is typically no more than a dozen or so iterations.
+        * __end_rodata by PAGE_SIZE, skipping the readonly and init data, to
+        * reach the requested address, while walking cpu home around
+        * kdata_mask. This is typically no more than a dozen or so iterations.
         */
        page = (((ulong)__end_rodata) + PAGE_SIZE - 1) & PAGE_MASK;
        BUG_ON(address < page || address >= (ulong)_end);
@@ -912,7 +912,7 @@ static long __write_once initfree = 1;
 static int __init set_initfree(char *str)
 {
        long val;
-       if (strict_strtol(str, 0, &val) == 0) {
+       if (kstrtol(str, 0, &val) == 0) {
                initfree = val;
                pr_info("initfree: %s free init pages\n",
                        initfree ? "will" : "won't");
index 36e658a4291c685e12d554dfd74ac153179591dc..e4b1a9639c4ddfab7db3d91a080272b9c00fdba8 100644 (file)
@@ -111,8 +111,7 @@ endef
 KBUILD_KCONFIG := $(HOST_DIR)/um/Kconfig
 
 archheaders:
-       $(Q)$(MAKE) -C '$(srctree)' KBUILD_SRC= \
-               ARCH=$(HEADER_ARCH) O='$(objtree)' archheaders
+       $(Q)$(MAKE) KBUILD_SRC= ARCH=$(HEADER_ARCH) archheaders
 
 archprepare: include/generated/user_constants.h
 
index b660088c220d6c8996c348b2b6578314bc3878ea..fcefdda5136dde37c2a8893519912286f6551cf5 100644 (file)
@@ -121,6 +121,7 @@ config X86
        select MODULES_USE_ELF_RELA if X86_64
        select CLONE_BACKWARDS if X86_32
        select ARCH_USE_BUILTIN_BSWAP
+       select ARCH_USE_QUEUE_RWLOCK
        select OLD_SIGSUSPEND3 if X86_32 || IA32_EMULATION
        select OLD_SIGACTION if X86_32
        select COMPAT_OLD_SIGACTION if IA32_EMULATION
index 4582e8e1cd1ad43922d96b83a5cc2472d5ac72b7..7730c1c5c83aa7aaf6859170f812ad1f410c1a0b 100644 (file)
        .long (from) - . ;                                      \
        .long (to) - . + 0x7ffffff0 ;                           \
        .popsection
+
+# define _ASM_NOKPROBE(entry)                                  \
+       .pushsection "_kprobe_blacklist","aw" ;                 \
+       _ASM_ALIGN ;                                            \
+       _ASM_PTR (entry);                                       \
+       .popsection
 #else
 # define _ASM_EXTABLE(from,to)                                 \
        " .pushsection \"__ex_table\",\"a\"\n"                  \
@@ -71,6 +77,7 @@
        " .long (" #from ") - .\n"                              \
        " .long (" #to ") - . + 0x7ffffff0\n"                   \
        " .popsection\n"
+/* For C file, we already have NOKPROBE_SYMBOL macro */
 #endif
 
 #endif /* _ASM_X86_ASM_H */
index e6fd8a026c7be574e28d49d321d552d53c72b7cb..cd00e17744914c5b2e1d7d16da887a2809ebb5b5 100644 (file)
@@ -184,8 +184,15 @@ static inline unsigned add32_with_carry(unsigned a, unsigned b)
        asm("addl %2,%0\n\t"
            "adcl $0,%0"
            : "=r" (a)
-           : "0" (a), "r" (b));
+           : "0" (a), "rm" (b));
        return a;
 }
 
+#define HAVE_ARCH_CSUM_ADD
+static inline __wsum csum_add(__wsum csum, __wsum addend)
+{
+       return (__force __wsum)add32_with_carry((__force unsigned)csum,
+                                               (__force unsigned)addend);
+}
+
 #endif /* _ASM_X86_CHECKSUM_64_H */
index 9454c167629ff1dd7aac941e061f5ebda6da8868..53cdfb2857abe41f6f83b786a702cfb2fbd41ead 100644 (file)
@@ -116,4 +116,6 @@ struct kprobe_ctlblk {
 extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
 extern int kprobe_exceptions_notify(struct notifier_block *self,
                                    unsigned long val, void *data);
+extern int kprobe_int3_handler(struct pt_regs *regs);
+extern int kprobe_debug_handler(struct pt_regs *regs);
 #endif /* _ASM_X86_KPROBES_H */
diff --git a/arch/x86/include/asm/qrwlock.h b/arch/x86/include/asm/qrwlock.h
new file mode 100644 (file)
index 0000000..70f46f0
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _ASM_X86_QRWLOCK_H
+#define _ASM_X86_QRWLOCK_H
+
+#include <asm-generic/qrwlock_types.h>
+
+#if !defined(CONFIG_X86_OOSTORE) && !defined(CONFIG_X86_PPRO_FENCE)
+#define queue_write_unlock queue_write_unlock
+static inline void queue_write_unlock(struct qrwlock *lock)
+{
+        barrier();
+        ACCESS_ONCE(*(u8 *)&lock->cnts) = 0;
+}
+#endif
+
+#include <asm-generic/qrwlock.h>
+
+#endif /* _ASM_X86_QRWLOCK_H */
index 0f62f5482d91ec8fca86e884b6822fc467239dbc..54f1c8068c02340a4600049f843fd9f3067b51c5 100644 (file)
@@ -187,6 +187,7 @@ static inline void arch_spin_unlock_wait(arch_spinlock_t *lock)
                cpu_relax();
 }
 
+#ifndef CONFIG_QUEUE_RWLOCK
 /*
  * Read-write spinlocks, allowing multiple readers
  * but only one writer.
@@ -269,6 +270,9 @@ static inline void arch_write_unlock(arch_rwlock_t *rw)
        asm volatile(LOCK_PREFIX WRITE_LOCK_ADD(%1) "%0"
                     : "+m" (rw->write) : "i" (RW_LOCK_BIAS) : "memory");
 }
+#else
+#include <asm/qrwlock.h>
+#endif /* CONFIG_QUEUE_RWLOCK */
 
 #define arch_read_lock_flags(lock, flags) arch_read_lock(lock)
 #define arch_write_lock_flags(lock, flags) arch_write_lock(lock)
index 4f1bea19945bf8e4d6acf758c8c5fd42fce4bf2d..73c4c007200f066173f3f5628cefd62ccefa7d1e 100644 (file)
@@ -34,6 +34,10 @@ typedef struct arch_spinlock {
 
 #define __ARCH_SPIN_LOCK_UNLOCKED      { { 0 } }
 
+#ifdef CONFIG_QUEUE_RWLOCK
+#include <asm-generic/qrwlock_types.h>
+#else
 #include <asm/rwlock.h>
+#endif
 
 #endif /* _ASM_X86_SPINLOCK_TYPES_H */
index 8ba18842c48eac18aab3b90bfb9e0e3c292900a8..bc8352e7010a9e805c54068da84b3848dcc12048 100644 (file)
@@ -68,7 +68,7 @@ dotraplinkage void do_segment_not_present(struct pt_regs *, long);
 dotraplinkage void do_stack_segment(struct pt_regs *, long);
 #ifdef CONFIG_X86_64
 dotraplinkage void do_double_fault(struct pt_regs *, long);
-asmlinkage __kprobes struct pt_regs *sync_regs(struct pt_regs *);
+asmlinkage struct pt_regs *sync_regs(struct pt_regs *);
 #endif
 dotraplinkage void do_general_protection(struct pt_regs *, long);
 dotraplinkage void do_page_fault(struct pt_regs *, unsigned long);
@@ -103,7 +103,6 @@ static inline int get_si_code(unsigned long condition)
 
 extern int panic_on_unrecovered_nmi;
 
-void math_error(struct pt_regs *, int, int);
 void math_emulate(struct math_emu_info *);
 #ifndef CONFIG_X86_32
 asmlinkage void smp_thermal_interrupt(void);
index 93bee7b93854dc8a8aaa21cde812deed246e7c89..74f4c2ff6427292218521dc971c155d48b6405ff 100644 (file)
@@ -41,18 +41,18 @@ struct arch_uprobe {
                u8                      ixol[MAX_UINSN_BYTES];
        };
 
-       u16                             fixups;
        const struct uprobe_xol_ops     *ops;
 
        union {
-#ifdef CONFIG_X86_64
-               unsigned long                   rip_rela_target_address;
-#endif
                struct {
                        s32     offs;
                        u8      ilen;
                        u8      opc1;
-               }                               branch;
+               }                       branch;
+               struct {
+                       u8      fixups;
+                       u8      ilen;
+               }                       defparam;
        };
 };
 
index df94598ad05a845902e9897214cdceacc779a80d..703130f469ecf71978b9d67bf0fb50da9b31cbc9 100644 (file)
@@ -5,7 +5,6 @@
 #include <linux/mutex.h>
 #include <linux/list.h>
 #include <linux/stringify.h>
-#include <linux/kprobes.h>
 #include <linux/mm.h>
 #include <linux/vmalloc.h>
 #include <linux/memory.h>
@@ -551,7 +550,7 @@ void *__init_or_module text_poke_early(void *addr, const void *opcode,
  *
  * Note: Must be called under text_mutex.
  */
-void *__kprobes text_poke(void *addr, const void *opcode, size_t len)
+void *text_poke(void *addr, const void *opcode, size_t len)
 {
        unsigned long flags;
        char *vaddr;
index eab67047dec398200f16eae8668aa00f33c20809..c3fcb5de508391ca20684669b3746a2c734cad02 100644 (file)
@@ -60,7 +60,7 @@ void arch_trigger_all_cpu_backtrace(void)
        smp_mb__after_atomic();
 }
 
-static int __kprobes
+static int
 arch_trigger_all_cpu_backtrace_handler(unsigned int cmd, struct pt_regs *regs)
 {
        int cpu;
@@ -80,6 +80,7 @@ arch_trigger_all_cpu_backtrace_handler(unsigned int cmd, struct pt_regs *regs)
 
        return NMI_DONE;
 }
+NOKPROBE_SYMBOL(arch_trigger_all_cpu_backtrace_handler);
 
 static int __init register_trigger_all_cpu_backtrace(void)
 {
index 9d0a9795a0f80e56e7ff9acfd414ee7f5ed44459..81e08eff05eedbd2e839a14229464cbea456e84c 100644 (file)
@@ -2297,7 +2297,7 @@ int __ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask,
        int err;
 
        if (!config_enabled(CONFIG_SMP))
-               return -1;
+               return -EPERM;
 
        if (!cpumask_intersects(mask, cpu_online_mask))
                return -EINVAL;
@@ -2328,7 +2328,7 @@ int native_ioapic_set_affinity(struct irq_data *data,
        int ret;
 
        if (!config_enabled(CONFIG_SMP))
-               return -1;
+               return -EPERM;
 
        raw_spin_lock_irqsave(&ioapic_lock, flags);
        ret = __ioapic_set_affinity(data, mask, &dest);
@@ -3001,9 +3001,11 @@ msi_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force)
        struct irq_cfg *cfg = data->chip_data;
        struct msi_msg msg;
        unsigned int dest;
+       int ret;
 
-       if (__ioapic_set_affinity(data, mask, &dest))
-               return -1;
+       ret = __ioapic_set_affinity(data, mask, &dest);
+       if (ret)
+               return ret;
 
        __get_cached_msi_msg(data->msi_desc, &msg);
 
@@ -3100,9 +3102,11 @@ dmar_msi_set_affinity(struct irq_data *data, const struct cpumask *mask,
        struct irq_cfg *cfg = data->chip_data;
        unsigned int dest, irq = data->irq;
        struct msi_msg msg;
+       int ret;
 
-       if (__ioapic_set_affinity(data, mask, &dest))
-               return -1;
+       ret = __ioapic_set_affinity(data, mask, &dest);
+       if (ret)
+               return ret;
 
        dmar_msi_read(irq, &msg);
 
@@ -3149,9 +3153,11 @@ static int hpet_msi_set_affinity(struct irq_data *data,
        struct irq_cfg *cfg = data->chip_data;
        struct msi_msg msg;
        unsigned int dest;
+       int ret;
 
-       if (__ioapic_set_affinity(data, mask, &dest))
-               return -1;
+       ret = __ioapic_set_affinity(data, mask, &dest);
+       if (ret)
+               return ret;
 
        hpet_msi_read(data->handler_data, &msg);
 
@@ -3218,9 +3224,11 @@ ht_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force)
 {
        struct irq_cfg *cfg = data->chip_data;
        unsigned int dest;
+       int ret;
 
-       if (__ioapic_set_affinity(data, mask, &dest))
-               return -1;
+       ret = __ioapic_set_affinity(data, mask, &dest);
+       if (ret)
+               return ret;
 
        target_ht_irq(data->irq, dest, cfg->vector);
        return IRQ_SET_MASK_OK_NOCOPY;
index 2cbbf88d8f2cb1084d25dcecb776793b8f715bc7..ef1b93f18ed1c328ca538863f89623afb45cb1a9 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/delay.h>
 #include <linux/sched.h>
 #include <linux/init.h>
+#include <linux/kprobes.h>
 #include <linux/kgdb.h>
 #include <linux/smp.h>
 #include <linux/io.h>
@@ -1193,6 +1194,7 @@ int is_debug_stack(unsigned long addr)
                (addr <= __get_cpu_var(debug_stack_addr) &&
                 addr > (__get_cpu_var(debug_stack_addr) - DEBUG_STKSZ));
 }
+NOKPROBE_SYMBOL(is_debug_stack);
 
 DEFINE_PER_CPU(u32, debug_idt_ctr);
 
@@ -1201,6 +1203,7 @@ void debug_stack_set_zero(void)
        this_cpu_inc(debug_idt_ctr);
        load_current_idt();
 }
+NOKPROBE_SYMBOL(debug_stack_set_zero);
 
 void debug_stack_reset(void)
 {
@@ -1209,6 +1212,7 @@ void debug_stack_reset(void)
        if (this_cpu_dec_return(debug_idt_ctr) == 0)
                load_current_idt();
 }
+NOKPROBE_SYMBOL(debug_stack_reset);
 
 #else  /* CONFIG_X86_64 */
 
index 76f98fe5b35c4a2f3860b762b54f039cb7ad642c..a450373e8e91698dd235725e8d8f9f35b3cec7cc 100644 (file)
@@ -132,15 +132,6 @@ static void __init ms_hyperv_init_platform(void)
                lapic_timer_frequency = hv_lapic_frequency;
                printk(KERN_INFO "HyperV: LAPIC Timer Frequency: %#x\n",
                                lapic_timer_frequency);
-
-               /*
-                * On Hyper-V, when we are booting off an EFI firmware stack,
-                * we do not have many legacy devices including PIC, PIT etc.
-                */
-               if (efi_enabled(EFI_BOOT)) {
-                       printk(KERN_INFO "HyperV: Using null_legacy_pic\n");
-                       legacy_pic = &null_legacy_pic;
-               }
        }
 #endif
 
index 89f3b7c1af2060556eb0aac096f09f2ecebeddf4..2bdfbff8a4f6165afb1e9931edcfb250c7113a34 100644 (file)
@@ -303,15 +303,6 @@ int x86_setup_perfctr(struct perf_event *event)
                hwc->sample_period = x86_pmu.max_period;
                hwc->last_period = hwc->sample_period;
                local64_set(&hwc->period_left, hwc->sample_period);
-       } else {
-               /*
-                * If we have a PMU initialized but no APIC
-                * interrupts, we cannot sample hardware
-                * events (user-space has to fall back and
-                * sample via a hrtimer based software event):
-                */
-               if (!x86_pmu.apic)
-                       return -EOPNOTSUPP;
        }
 
        if (attr->type == PERF_TYPE_RAW)
@@ -1293,7 +1284,7 @@ void perf_events_lapic_init(void)
        apic_write(APIC_LVTPC, APIC_DM_NMI);
 }
 
-static int __kprobes
+static int
 perf_event_nmi_handler(unsigned int cmd, struct pt_regs *regs)
 {
        u64 start_clock;
@@ -1311,6 +1302,7 @@ perf_event_nmi_handler(unsigned int cmd, struct pt_regs *regs)
 
        return ret;
 }
+NOKPROBE_SYMBOL(perf_event_nmi_handler);
 
 struct event_constraint emptyconstraint;
 struct event_constraint unconstrained;
@@ -1366,6 +1358,15 @@ static void __init pmu_check_apic(void)
        x86_pmu.apic = 0;
        pr_info("no APIC, boot with the \"lapic\" boot parameter to force-enable it.\n");
        pr_info("no hardware sampling interrupt available.\n");
+
+       /*
+        * If we have a PMU initialized but no APIC
+        * interrupts, we cannot sample hardware
+        * events (user-space has to fall back and
+        * sample via a hrtimer based software event):
+        */
+       pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
+
 }
 
 static struct attribute_group x86_pmu_format_group = {
index 4c36bbe3173aa0f683a5a982cc857c6f3ffa3e0a..cbb1be3ed9e432aab5ff3b679e5e0ecc0bfd731a 100644 (file)
@@ -593,7 +593,7 @@ out:
        return 1;
 }
 
-static int __kprobes
+static int
 perf_ibs_nmi_handler(unsigned int cmd, struct pt_regs *regs)
 {
        int handled = 0;
@@ -606,6 +606,7 @@ perf_ibs_nmi_handler(unsigned int cmd, struct pt_regs *regs)
 
        return handled;
 }
+NOKPROBE_SYMBOL(perf_ibs_nmi_handler);
 
 static __init int perf_ibs_pmu_init(struct perf_ibs *perf_ibs, char *name)
 {
index d82d155aca8c7c6c44c9ba7150dae39e88e16fad..9dd2459a4c738d99bb4a75ebf51062bcbf6b21de 100644 (file)
@@ -384,6 +384,9 @@ static void intel_pmu_setup_sw_lbr_filter(struct perf_event *event)
        if (br_type & PERF_SAMPLE_BRANCH_NO_TX)
                mask |= X86_BR_NO_TX;
 
+       if (br_type & PERF_SAMPLE_BRANCH_COND)
+               mask |= X86_BR_JCC;
+
        /*
         * stash actual user request into reg, it may
         * be used by fixup code for some CPU
@@ -678,6 +681,7 @@ static const int nhm_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX] = {
         * NHM/WSM erratum: must include IND_JMP to capture IND_CALL
         */
        [PERF_SAMPLE_BRANCH_IND_CALL] = LBR_IND_CALL | LBR_IND_JMP,
+       [PERF_SAMPLE_BRANCH_COND]     = LBR_JCC,
 };
 
 static const int snb_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX] = {
@@ -689,6 +693,7 @@ static const int snb_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX] = {
        [PERF_SAMPLE_BRANCH_ANY_CALL]   = LBR_REL_CALL | LBR_IND_CALL
                                        | LBR_FAR,
        [PERF_SAMPLE_BRANCH_IND_CALL]   = LBR_IND_CALL,
+       [PERF_SAMPLE_BRANCH_COND]       = LBR_JCC,
 };
 
 /* core */
index d9c12d3022a70c68e3f69d4cf2d68bdc10123010..b74ebc7c4402e7eff3b21f4b87ba514e0c017056 100644 (file)
@@ -200,7 +200,7 @@ static arch_spinlock_t die_lock = __ARCH_SPIN_LOCK_UNLOCKED;
 static int die_owner = -1;
 static unsigned int die_nest_count;
 
-unsigned __kprobes long oops_begin(void)
+unsigned long oops_begin(void)
 {
        int cpu;
        unsigned long flags;
@@ -223,8 +223,9 @@ unsigned __kprobes long oops_begin(void)
        return flags;
 }
 EXPORT_SYMBOL_GPL(oops_begin);
+NOKPROBE_SYMBOL(oops_begin);
 
-void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr)
+void oops_end(unsigned long flags, struct pt_regs *regs, int signr)
 {
        if (regs && kexec_should_crash(current))
                crash_kexec(regs);
@@ -247,8 +248,9 @@ void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr)
                panic("Fatal exception");
        do_exit(signr);
 }
+NOKPROBE_SYMBOL(oops_end);
 
-int __kprobes __die(const char *str, struct pt_regs *regs, long err)
+int __die(const char *str, struct pt_regs *regs, long err)
 {
 #ifdef CONFIG_X86_32
        unsigned short ss;
@@ -291,6 +293,7 @@ int __kprobes __die(const char *str, struct pt_regs *regs, long err)
 #endif
        return 0;
 }
+NOKPROBE_SYMBOL(__die);
 
 /*
  * This is gone through when something in the kernel has done something bad
index 6cda0baeac9d7810dadbfadc9b9cb2b228c68c5e..2e1a6853e00cec950bdd6e0f11586a57c062a05d 100644 (file)
@@ -419,7 +419,7 @@ static size_t __init gen6_stolen_size(int num, int slot, int func)
        return gmch_ctrl << 25; /* 32 MB units */
 }
 
-static size_t gen8_stolen_size(int num, int slot, int func)
+static size_t __init gen8_stolen_size(int num, int slot, int func)
 {
        u16 gmch_ctrl;
 
@@ -429,48 +429,73 @@ static size_t gen8_stolen_size(int num, int slot, int func)
        return gmch_ctrl << 25; /* 32 MB units */
 }
 
+static size_t __init chv_stolen_size(int num, int slot, int func)
+{
+       u16 gmch_ctrl;
+
+       gmch_ctrl = read_pci_config_16(num, slot, func, SNB_GMCH_CTRL);
+       gmch_ctrl >>= SNB_GMCH_GMS_SHIFT;
+       gmch_ctrl &= SNB_GMCH_GMS_MASK;
+
+       /*
+        * 0x0  to 0x10: 32MB increments starting at 0MB
+        * 0x11 to 0x16: 4MB increments starting at 8MB
+        * 0x17 to 0x1d: 4MB increments start at 36MB
+        */
+       if (gmch_ctrl < 0x11)
+               return gmch_ctrl << 25;
+       else if (gmch_ctrl < 0x17)
+               return (gmch_ctrl - 0x11 + 2) << 22;
+       else
+               return (gmch_ctrl - 0x17 + 9) << 22;
+}
 
 struct intel_stolen_funcs {
        size_t (*size)(int num, int slot, int func);
        u32 (*base)(int num, int slot, int func, size_t size);
 };
 
-static const struct intel_stolen_funcs i830_stolen_funcs = {
+static const struct intel_stolen_funcs i830_stolen_funcs __initconst = {
        .base = i830_stolen_base,
        .size = i830_stolen_size,
 };
 
-static const struct intel_stolen_funcs i845_stolen_funcs = {
+static const struct intel_stolen_funcs i845_stolen_funcs __initconst = {
        .base = i845_stolen_base,
        .size = i830_stolen_size,
 };
 
-static const struct intel_stolen_funcs i85x_stolen_funcs = {
+static const struct intel_stolen_funcs i85x_stolen_funcs __initconst = {
        .base = i85x_stolen_base,
        .size = gen3_stolen_size,
 };
 
-static const struct intel_stolen_funcs i865_stolen_funcs = {
+static const struct intel_stolen_funcs i865_stolen_funcs __initconst = {
        .base = i865_stolen_base,
        .size = gen3_stolen_size,
 };
 
-static const struct intel_stolen_funcs gen3_stolen_funcs = {
+static const struct intel_stolen_funcs gen3_stolen_funcs __initconst = {
        .base = intel_stolen_base,
        .size = gen3_stolen_size,
 };
 
-static const struct intel_stolen_funcs gen6_stolen_funcs = {
+static const struct intel_stolen_funcs gen6_stolen_funcs __initconst = {
        .base = intel_stolen_base,
        .size = gen6_stolen_size,
 };
 
-static const struct intel_stolen_funcs gen8_stolen_funcs = {
+static const struct intel_stolen_funcs gen8_stolen_funcs __initconst = {
        .base = intel_stolen_base,
        .size = gen8_stolen_size,
 };
 
-static struct pci_device_id intel_stolen_ids[] __initdata = {
+static const struct intel_stolen_funcs chv_stolen_funcs __initconst = {
+       .base = intel_stolen_base,
+       .size = chv_stolen_size,
+};
+
+static const struct pci_device_id intel_stolen_ids[] __initconst = {
        INTEL_I830_IDS(&i830_stolen_funcs),
        INTEL_I845G_IDS(&i845_stolen_funcs),
        INTEL_I85X_IDS(&i85x_stolen_funcs),
@@ -496,7 +521,8 @@ static struct pci_device_id intel_stolen_ids[] __initdata = {
        INTEL_HSW_D_IDS(&gen6_stolen_funcs),
        INTEL_HSW_M_IDS(&gen6_stolen_funcs),
        INTEL_BDW_M_IDS(&gen8_stolen_funcs),
-       INTEL_BDW_D_IDS(&gen8_stolen_funcs)
+       INTEL_BDW_D_IDS(&gen8_stolen_funcs),
+       INTEL_CHV_IDS(&chv_stolen_funcs),
 };
 
 static void __init intel_graphics_stolen(int num, int slot, int func)
index 98313ffaae6ab0c651b904cbd68413bb3e341eb8..f0da82b8e63419b7d4469f077303492134b5ba5a 100644 (file)
@@ -314,10 +314,6 @@ ENTRY(ret_from_kernel_thread)
        CFI_ENDPROC
 ENDPROC(ret_from_kernel_thread)
 
-/*
- * Interrupt exit functions should be protected against kprobes
- */
-       .pushsection .kprobes.text, "ax"
 /*
  * Return to user mode is not as complex as all this looks,
  * but we want the default path for a system call return to
@@ -372,10 +368,6 @@ need_resched:
 END(resume_kernel)
 #endif
        CFI_ENDPROC
-/*
- * End of kprobes section
- */
-       .popsection
 
 /* SYSENTER_RETURN points to after the "sysenter" instruction in
    the vsyscall page.  See vsyscall-sysentry.S, which defines the symbol.  */
@@ -495,10 +487,6 @@ sysexit_audit:
        PTGS_TO_GS_EX
 ENDPROC(ia32_sysenter_target)
 
-/*
- * syscall stub including irq exit should be protected against kprobes
- */
-       .pushsection .kprobes.text, "ax"
        # system call handler stub
 ENTRY(system_call)
        RING0_INT_FRAME                 # can't unwind into user space anyway
@@ -690,10 +678,6 @@ syscall_badsys:
        jmp resume_userspace
 END(syscall_badsys)
        CFI_ENDPROC
-/*
- * End of kprobes section
- */
-       .popsection
 
 .macro FIXUP_ESPFIX_STACK
 /*
@@ -784,10 +768,6 @@ common_interrupt:
 ENDPROC(common_interrupt)
        CFI_ENDPROC
 
-/*
- *  Irq entries should be protected against kprobes
- */
-       .pushsection .kprobes.text, "ax"
 #define BUILD_INTERRUPT3(name, nr, fn) \
 ENTRY(name)                            \
        RING0_INT_FRAME;                \
@@ -964,10 +944,6 @@ ENTRY(spurious_interrupt_bug)
        jmp error_code
        CFI_ENDPROC
 END(spurious_interrupt_bug)
-/*
- * End of kprobes section
- */
-       .popsection
 
 #ifdef CONFIG_XEN
 /* Xen doesn't set %esp to be precisely what the normal sysenter
@@ -1242,11 +1218,6 @@ return_to_handler:
        jmp *%ecx
 #endif
 
-/*
- * Some functions should be protected against kprobes
- */
-       .pushsection .kprobes.text, "ax"
-
 #ifdef CONFIG_TRACING
 ENTRY(trace_page_fault)
        RING0_EC_FRAME
@@ -1460,7 +1431,3 @@ ENTRY(async_page_fault)
 END(async_page_fault)
 #endif
 
-/*
- * End of kprobes section
- */
-       .popsection
index 48a2644a082a4ccb25ede707403d1aa3fd212286..b25ca969edd27eca18210b013508d640775c6613 100644 (file)
@@ -284,8 +284,6 @@ ENDPROC(native_usergs_sysret64)
        TRACE_IRQS_OFF
        .endm
 
-/* save complete stack frame */
-       .pushsection .kprobes.text, "ax"
 ENTRY(save_paranoid)
        XCPT_FRAME 1 RDI+8
        cld
@@ -314,7 +312,6 @@ ENTRY(save_paranoid)
 1:     ret
        CFI_ENDPROC
 END(save_paranoid)
-       .popsection
 
 /*
  * A newly forked process directly context switches into this address.
@@ -772,10 +769,6 @@ END(interrupt)
        call \func
        .endm
 
-/*
- * Interrupt entry/exit should be protected against kprobes
- */
-       .pushsection .kprobes.text, "ax"
        /*
         * The interrupt stubs push (~vector+0x80) onto the stack and
         * then jump to common_interrupt.
@@ -982,11 +975,6 @@ END(__do_double_fault)
 # define __do_double_fault do_double_fault
 #endif
 
-/*
- * End of kprobes section
- */
-       .popsection
-
 /*
  * APIC interrupts.
  */
@@ -1321,11 +1309,6 @@ apicinterrupt3 HYPERVISOR_CALLBACK_VECTOR \
        hyperv_callback_vector hyperv_vector_handler
 #endif /* CONFIG_HYPERV */
 
-/*
- * Some functions should be protected against kprobes
- */
-       .pushsection .kprobes.text, "ax"
-
 idtentry debug do_debug has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK
 idtentry int3 do_int3 has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK
 idtentry stack_segment do_stack_segment has_error_code=1 paranoid=1
@@ -1742,7 +1725,3 @@ ENTRY(ignore_sysret)
        CFI_ENDPROC
 END(ignore_sysret)
 
-/*
- * End of kprobes section
- */
-       .popsection
index a67b47c31314ba7a2e30d7d9fc356ffc16f4f072..5f9cf20cdb68025ae7175860fba5abab136ef9f7 100644 (file)
@@ -32,7 +32,6 @@
 #include <linux/irqflags.h>
 #include <linux/notifier.h>
 #include <linux/kallsyms.h>
-#include <linux/kprobes.h>
 #include <linux/percpu.h>
 #include <linux/kdebug.h>
 #include <linux/kernel.h>
@@ -424,7 +423,7 @@ EXPORT_SYMBOL_GPL(hw_breakpoint_restore);
  * NOTIFY_STOP returned for all other cases
  *
  */
-static int __kprobes hw_breakpoint_handler(struct die_args *args)
+static int hw_breakpoint_handler(struct die_args *args)
 {
        int i, cpu, rc = NOTIFY_STOP;
        struct perf_event *bp;
@@ -511,7 +510,7 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args)
 /*
  * Handle debug exception notifications.
  */
-int __kprobes hw_breakpoint_exceptions_notify(
+int hw_breakpoint_exceptions_notify(
                struct notifier_block *unused, unsigned long val, void *data)
 {
        if (val != DIE_DEBUG)
index 2e977b5d61ddee4e00d599f04236d70e8714fee0..8af817105e29cbc25ffaa8b2430a57c5de782dd6 100644 (file)
@@ -299,13 +299,31 @@ static void unmask_8259A(void)
 static void init_8259A(int auto_eoi)
 {
        unsigned long flags;
+       unsigned char probe_val = ~(1 << PIC_CASCADE_IR);
+       unsigned char new_val;
 
        i8259A_auto_eoi = auto_eoi;
 
        raw_spin_lock_irqsave(&i8259A_lock, flags);
 
-       outb(0xff, PIC_MASTER_IMR);     /* mask all of 8259A-1 */
+       /*
+        * Check to see if we have a PIC.
+        * Mask all except the cascade and read
+        * back the value we just wrote. If we don't
+        * have a PIC, we will read 0xff as opposed to the
+        * value we wrote.
+        */
        outb(0xff, PIC_SLAVE_IMR);      /* mask all of 8259A-2 */
+       outb(probe_val, PIC_MASTER_IMR);
+       new_val = inb(PIC_MASTER_IMR);
+       if (new_val != probe_val) {
+               printk(KERN_INFO "Using NULL legacy PIC\n");
+               legacy_pic = &null_legacy_pic;
+               raw_spin_unlock_irqrestore(&i8259A_lock, flags);
+               return;
+       }
+
+       outb(0xff, PIC_MASTER_IMR);     /* mask all of 8259A-1 */
 
        /*
         * outb_pic - this has to work on a wide range of PC hardware.
index 11ccfb0a63e78d68908440c1690b3f544ee9c6c7..922d285810246aaf4382104d01be52cf6cf31767 100644 (file)
@@ -365,6 +365,7 @@ void fixup_irqs(void)
        struct irq_desc *desc;
        struct irq_data *data;
        struct irq_chip *chip;
+       int ret;
 
        for_each_irq_desc(irq, desc) {
                int break_affinity = 0;
@@ -403,10 +404,14 @@ void fixup_irqs(void)
                if (!irqd_can_move_in_process_context(data) && chip->irq_mask)
                        chip->irq_mask(data);
 
-               if (chip->irq_set_affinity)
-                       chip->irq_set_affinity(data, affinity, true);
-               else if (!(warned++))
-                       set_affinity = 0;
+               if (chip->irq_set_affinity) {
+                       ret = chip->irq_set_affinity(data, affinity, true);
+                       if (ret == -ENOSPC)
+                               pr_crit("IRQ %d set affinity failed because there are no available vectors.  The device assigned to this IRQ is unstable.\n", irq);
+               } else {
+                       if (!(warned++))
+                               set_affinity = 0;
+               }
 
                /*
                 * We unmask if the irq was not marked masked by the
index 61b17dc2c277346ae2c869da691ec19ab000e1ad..7596df664901eed5a7aea5003ab83da49d34a615 100644 (file)
@@ -112,7 +112,8 @@ struct kretprobe_blackpoint kretprobe_blacklist[] = {
 
 const int kretprobe_blacklist_size = ARRAY_SIZE(kretprobe_blacklist);
 
-static void __kprobes __synthesize_relative_insn(void *from, void *to, u8 op)
+static nokprobe_inline void
+__synthesize_relative_insn(void *from, void *to, u8 op)
 {
        struct __arch_relative_insn {
                u8 op;
@@ -125,21 +126,23 @@ static void __kprobes __synthesize_relative_insn(void *from, void *to, u8 op)
 }
 
 /* Insert a jump instruction at address 'from', which jumps to address 'to'.*/
-void __kprobes synthesize_reljump(void *from, void *to)
+void synthesize_reljump(void *from, void *to)
 {
        __synthesize_relative_insn(from, to, RELATIVEJUMP_OPCODE);
 }
+NOKPROBE_SYMBOL(synthesize_reljump);
 
 /* Insert a call instruction at address 'from', which calls address 'to'.*/
-void __kprobes synthesize_relcall(void *from, void *to)
+void synthesize_relcall(void *from, void *to)
 {
        __synthesize_relative_insn(from, to, RELATIVECALL_OPCODE);
 }
+NOKPROBE_SYMBOL(synthesize_relcall);
 
 /*
  * Skip the prefixes of the instruction.
  */
-static kprobe_opcode_t *__kprobes skip_prefixes(kprobe_opcode_t *insn)
+static kprobe_opcode_t *skip_prefixes(kprobe_opcode_t *insn)
 {
        insn_attr_t attr;
 
@@ -154,12 +157,13 @@ static kprobe_opcode_t *__kprobes skip_prefixes(kprobe_opcode_t *insn)
 #endif
        return insn;
 }
+NOKPROBE_SYMBOL(skip_prefixes);
 
 /*
  * Returns non-zero if opcode is boostable.
  * RIP relative instructions are adjusted at copying time in 64 bits mode
  */
-int __kprobes can_boost(kprobe_opcode_t *opcodes)
+int can_boost(kprobe_opcode_t *opcodes)
 {
        kprobe_opcode_t opcode;
        kprobe_opcode_t *orig_opcodes = opcodes;
@@ -260,7 +264,7 @@ unsigned long recover_probed_instruction(kprobe_opcode_t *buf, unsigned long add
 }
 
 /* Check if paddr is at an instruction boundary */
-static int __kprobes can_probe(unsigned long paddr)
+static int can_probe(unsigned long paddr)
 {
        unsigned long addr, __addr, offset = 0;
        struct insn insn;
@@ -299,7 +303,7 @@ static int __kprobes can_probe(unsigned long paddr)
 /*
  * Returns non-zero if opcode modifies the interrupt flag.
  */
-static int __kprobes is_IF_modifier(kprobe_opcode_t *insn)
+static int is_IF_modifier(kprobe_opcode_t *insn)
 {
        /* Skip prefixes */
        insn = skip_prefixes(insn);
@@ -322,7 +326,7 @@ static int __kprobes is_IF_modifier(kprobe_opcode_t *insn)
  * If not, return null.
  * Only applicable to 64-bit x86.
  */
-int __kprobes __copy_instruction(u8 *dest, u8 *src)
+int __copy_instruction(u8 *dest, u8 *src)
 {
        struct insn insn;
        kprobe_opcode_t buf[MAX_INSN_SIZE];
@@ -365,7 +369,7 @@ int __kprobes __copy_instruction(u8 *dest, u8 *src)
        return insn.length;
 }
 
-static int __kprobes arch_copy_kprobe(struct kprobe *p)
+static int arch_copy_kprobe(struct kprobe *p)
 {
        int ret;
 
@@ -392,7 +396,7 @@ static int __kprobes arch_copy_kprobe(struct kprobe *p)
        return 0;
 }
 
-int __kprobes arch_prepare_kprobe(struct kprobe *p)
+int arch_prepare_kprobe(struct kprobe *p)
 {
        if (alternatives_text_reserved(p->addr, p->addr))
                return -EINVAL;
@@ -407,17 +411,17 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
        return arch_copy_kprobe(p);
 }
 
-void __kprobes arch_arm_kprobe(struct kprobe *p)
+void arch_arm_kprobe(struct kprobe *p)
 {
        text_poke(p->addr, ((unsigned char []){BREAKPOINT_INSTRUCTION}), 1);
 }
 
-void __kprobes arch_disarm_kprobe(struct kprobe *p)
+void arch_disarm_kprobe(struct kprobe *p)
 {
        text_poke(p->addr, &p->opcode, 1);
 }
 
-void __kprobes arch_remove_kprobe(struct kprobe *p)
+void arch_remove_kprobe(struct kprobe *p)
 {
        if (p->ainsn.insn) {
                free_insn_slot(p->ainsn.insn, (p->ainsn.boostable == 1));
@@ -425,7 +429,8 @@ void __kprobes arch_remove_kprobe(struct kprobe *p)
        }
 }
 
-static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
+static nokprobe_inline void
+save_previous_kprobe(struct kprobe_ctlblk *kcb)
 {
        kcb->prev_kprobe.kp = kprobe_running();
        kcb->prev_kprobe.status = kcb->kprobe_status;
@@ -433,7 +438,8 @@ static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
        kcb->prev_kprobe.saved_flags = kcb->kprobe_saved_flags;
 }
 
-static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
+static nokprobe_inline void
+restore_previous_kprobe(struct kprobe_ctlblk *kcb)
 {
        __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp);
        kcb->kprobe_status = kcb->prev_kprobe.status;
@@ -441,8 +447,9 @@ static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
        kcb->kprobe_saved_flags = kcb->prev_kprobe.saved_flags;
 }
 
-static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs,
-                               struct kprobe_ctlblk *kcb)
+static nokprobe_inline void
+set_current_kprobe(struct kprobe *p, struct pt_regs *regs,
+                  struct kprobe_ctlblk *kcb)
 {
        __this_cpu_write(current_kprobe, p);
        kcb->kprobe_saved_flags = kcb->kprobe_old_flags
@@ -451,7 +458,7 @@ static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs,
                kcb->kprobe_saved_flags &= ~X86_EFLAGS_IF;
 }
 
-static void __kprobes clear_btf(void)
+static nokprobe_inline void clear_btf(void)
 {
        if (test_thread_flag(TIF_BLOCKSTEP)) {
                unsigned long debugctl = get_debugctlmsr();
@@ -461,7 +468,7 @@ static void __kprobes clear_btf(void)
        }
 }
 
-static void __kprobes restore_btf(void)
+static nokprobe_inline void restore_btf(void)
 {
        if (test_thread_flag(TIF_BLOCKSTEP)) {
                unsigned long debugctl = get_debugctlmsr();
@@ -471,8 +478,7 @@ static void __kprobes restore_btf(void)
        }
 }
 
-void __kprobes
-arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs)
+void arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs)
 {
        unsigned long *sara = stack_addr(regs);
 
@@ -481,9 +487,10 @@ arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs)
        /* Replace the return addr with trampoline addr */
        *sara = (unsigned long) &kretprobe_trampoline;
 }
+NOKPROBE_SYMBOL(arch_prepare_kretprobe);
 
-static void __kprobes
-setup_singlestep(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb, int reenter)
+static void setup_singlestep(struct kprobe *p, struct pt_regs *regs,
+                            struct kprobe_ctlblk *kcb, int reenter)
 {
        if (setup_detour_execution(p, regs, reenter))
                return;
@@ -519,22 +526,24 @@ setup_singlestep(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *k
        else
                regs->ip = (unsigned long)p->ainsn.insn;
 }
+NOKPROBE_SYMBOL(setup_singlestep);
 
 /*
  * We have reentered the kprobe_handler(), since another probe was hit while
  * within the handler. We save the original kprobes variables and just single
  * step on the instruction of the new probe without calling any user handlers.
  */
-static int __kprobes
-reenter_kprobe(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb)
+static int reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
+                         struct kprobe_ctlblk *kcb)
 {
        switch (kcb->kprobe_status) {
        case KPROBE_HIT_SSDONE:
        case KPROBE_HIT_ACTIVE:
+       case KPROBE_HIT_SS:
                kprobes_inc_nmissed_count(p);
                setup_singlestep(p, regs, kcb, 1);
                break;
-       case KPROBE_HIT_SS:
+       case KPROBE_REENTER:
                /* A probe has been hit in the codepath leading up to, or just
                 * after, single-stepping of a probed instruction. This entire
                 * codepath should strictly reside in .kprobes.text section.
@@ -553,12 +562,13 @@ reenter_kprobe(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb
 
        return 1;
 }
+NOKPROBE_SYMBOL(reenter_kprobe);
 
 /*
  * Interrupts are disabled on entry as trap3 is an interrupt gate and they
  * remain disabled throughout this function.
  */
-static int __kprobes kprobe_handler(struct pt_regs *regs)
+int kprobe_int3_handler(struct pt_regs *regs)
 {
        kprobe_opcode_t *addr;
        struct kprobe *p;
@@ -621,12 +631,13 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
        preempt_enable_no_resched();
        return 0;
 }
+NOKPROBE_SYMBOL(kprobe_int3_handler);
 
 /*
  * When a retprobed function returns, this code saves registers and
  * calls trampoline_handler() runs, which calls the kretprobe's handler.
  */
-static void __used __kprobes kretprobe_trampoline_holder(void)
+static void __used kretprobe_trampoline_holder(void)
 {
        asm volatile (
                        ".global kretprobe_trampoline\n"
@@ -657,11 +668,13 @@ static void __used __kprobes kretprobe_trampoline_holder(void)
 #endif
                        "       ret\n");
 }
+NOKPROBE_SYMBOL(kretprobe_trampoline_holder);
+NOKPROBE_SYMBOL(kretprobe_trampoline);
 
 /*
  * Called from kretprobe_trampoline
  */
-__visible __used __kprobes void *trampoline_handler(struct pt_regs *regs)
+__visible __used void *trampoline_handler(struct pt_regs *regs)
 {
        struct kretprobe_instance *ri = NULL;
        struct hlist_head *head, empty_rp;
@@ -747,6 +760,7 @@ __visible __used __kprobes void *trampoline_handler(struct pt_regs *regs)
        }
        return (void *)orig_ret_address;
 }
+NOKPROBE_SYMBOL(trampoline_handler);
 
 /*
  * Called after single-stepping.  p->addr is the address of the
@@ -775,8 +789,8 @@ __visible __used __kprobes void *trampoline_handler(struct pt_regs *regs)
  * jump instruction after the copied instruction, that jumps to the next
  * instruction after the probepoint.
  */
-static void __kprobes
-resume_execution(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb)
+static void resume_execution(struct kprobe *p, struct pt_regs *regs,
+                            struct kprobe_ctlblk *kcb)
 {
        unsigned long *tos = stack_addr(regs);
        unsigned long copy_ip = (unsigned long)p->ainsn.insn;
@@ -851,12 +865,13 @@ resume_execution(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *k
 no_change:
        restore_btf();
 }
+NOKPROBE_SYMBOL(resume_execution);
 
 /*
  * Interrupts are disabled on entry as trap1 is an interrupt gate and they
  * remain disabled throughout this function.
  */
-static int __kprobes post_kprobe_handler(struct pt_regs *regs)
+int kprobe_debug_handler(struct pt_regs *regs)
 {
        struct kprobe *cur = kprobe_running();
        struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
@@ -891,8 +906,9 @@ out:
 
        return 1;
 }
+NOKPROBE_SYMBOL(kprobe_debug_handler);
 
-int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
+int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
 {
        struct kprobe *cur = kprobe_running();
        struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
@@ -949,12 +965,13 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
 
        return 0;
 }
+NOKPROBE_SYMBOL(kprobe_fault_handler);
 
 /*
  * Wrapper routine for handling exceptions.
  */
-int __kprobes
-kprobe_exceptions_notify(struct notifier_block *self, unsigned long val, void *data)
+int kprobe_exceptions_notify(struct notifier_block *self, unsigned long val,
+                            void *data)
 {
        struct die_args *args = data;
        int ret = NOTIFY_DONE;
@@ -962,22 +979,7 @@ kprobe_exceptions_notify(struct notifier_block *self, unsigned long val, void *d
        if (args->regs && user_mode_vm(args->regs))
                return ret;
 
-       switch (val) {
-       case DIE_INT3:
-               if (kprobe_handler(args->regs))
-                       ret = NOTIFY_STOP;
-               break;
-       case DIE_DEBUG:
-               if (post_kprobe_handler(args->regs)) {
-                       /*
-                        * Reset the BS bit in dr6 (pointed by args->err) to
-                        * denote completion of processing
-                        */
-                       (*(unsigned long *)ERR_PTR(args->err)) &= ~DR_STEP;
-                       ret = NOTIFY_STOP;
-               }
-               break;
-       case DIE_GPF:
+       if (val == DIE_GPF) {
                /*
                 * To be potentially processing a kprobe fault and to
                 * trust the result from kprobe_running(), we have
@@ -986,14 +988,12 @@ kprobe_exceptions_notify(struct notifier_block *self, unsigned long val, void *d
                if (!preemptible() && kprobe_running() &&
                    kprobe_fault_handler(args->regs, args->trapnr))
                        ret = NOTIFY_STOP;
-               break;
-       default:
-               break;
        }
        return ret;
 }
+NOKPROBE_SYMBOL(kprobe_exceptions_notify);
 
-int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+int setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
 {
        struct jprobe *jp = container_of(p, struct jprobe, kp);
        unsigned long addr;
@@ -1017,8 +1017,9 @@ int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
        regs->ip = (unsigned long)(jp->entry);
        return 1;
 }
+NOKPROBE_SYMBOL(setjmp_pre_handler);
 
-void __kprobes jprobe_return(void)
+void jprobe_return(void)
 {
        struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
 
@@ -1034,8 +1035,10 @@ void __kprobes jprobe_return(void)
                        "       nop                     \n"::"b"
                        (kcb->jprobe_saved_sp):"memory");
 }
+NOKPROBE_SYMBOL(jprobe_return);
+NOKPROBE_SYMBOL(jprobe_return_end);
 
-int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
+int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
 {
        struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
        u8 *addr = (u8 *) (regs->ip - 1);
@@ -1063,13 +1066,22 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
        }
        return 0;
 }
+NOKPROBE_SYMBOL(longjmp_break_handler);
+
+bool arch_within_kprobe_blacklist(unsigned long addr)
+{
+       return  (addr >= (unsigned long)__kprobes_text_start &&
+                addr < (unsigned long)__kprobes_text_end) ||
+               (addr >= (unsigned long)__entry_text_start &&
+                addr < (unsigned long)__entry_text_end);
+}
 
 int __init arch_init_kprobes(void)
 {
        return 0;
 }
 
-int __kprobes arch_trampoline_kprobe(struct kprobe *p)
+int arch_trampoline_kprobe(struct kprobe *p)
 {
        return 0;
 }
index 23ef5c556f06145a2da51b51995a08afeae95dae..717b02a22e67638e8511c5d2ae299afa201739d8 100644 (file)
@@ -25,8 +25,9 @@
 
 #include "common.h"
 
-static int __skip_singlestep(struct kprobe *p, struct pt_regs *regs,
-                            struct kprobe_ctlblk *kcb)
+static nokprobe_inline
+int __skip_singlestep(struct kprobe *p, struct pt_regs *regs,
+                     struct kprobe_ctlblk *kcb)
 {
        /*
         * Emulate singlestep (and also recover regs->ip)
@@ -41,18 +42,19 @@ static int __skip_singlestep(struct kprobe *p, struct pt_regs *regs,
        return 1;
 }
 
-int __kprobes skip_singlestep(struct kprobe *p, struct pt_regs *regs,
-                             struct kprobe_ctlblk *kcb)
+int skip_singlestep(struct kprobe *p, struct pt_regs *regs,
+                   struct kprobe_ctlblk *kcb)
 {
        if (kprobe_ftrace(p))
                return __skip_singlestep(p, regs, kcb);
        else
                return 0;
 }
+NOKPROBE_SYMBOL(skip_singlestep);
 
 /* Ftrace callback handler for kprobes */
-void __kprobes kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
-                                    struct ftrace_ops *ops, struct pt_regs *regs)
+void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
+                          struct ftrace_ops *ops, struct pt_regs *regs)
 {
        struct kprobe *p;
        struct kprobe_ctlblk *kcb;
@@ -84,8 +86,9 @@ void __kprobes kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
 end:
        local_irq_restore(flags);
 }
+NOKPROBE_SYMBOL(kprobe_ftrace_handler);
 
-int __kprobes arch_prepare_kprobe_ftrace(struct kprobe *p)
+int arch_prepare_kprobe_ftrace(struct kprobe *p)
 {
        p->ainsn.insn = NULL;
        p->ainsn.boostable = -1;
index 898160b42e4392daddd224c55ab62fb467752dd7..f304773285ae360810e4290b67aa4c6f0e832ef0 100644 (file)
@@ -77,7 +77,7 @@ found:
 }
 
 /* Insert a move instruction which sets a pointer to eax/rdi (1st arg). */
-static void __kprobes synthesize_set_arg1(kprobe_opcode_t *addr, unsigned long val)
+static void synthesize_set_arg1(kprobe_opcode_t *addr, unsigned long val)
 {
 #ifdef CONFIG_X86_64
        *addr++ = 0x48;
@@ -138,7 +138,8 @@ asm (
 #define INT3_SIZE sizeof(kprobe_opcode_t)
 
 /* Optimized kprobe call back function: called from optinsn */
-static void __kprobes optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs)
+static void
+optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs)
 {
        struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
        unsigned long flags;
@@ -168,8 +169,9 @@ static void __kprobes optimized_callback(struct optimized_kprobe *op, struct pt_
        }
        local_irq_restore(flags);
 }
+NOKPROBE_SYMBOL(optimized_callback);
 
-static int __kprobes copy_optimized_instructions(u8 *dest, u8 *src)
+static int copy_optimized_instructions(u8 *dest, u8 *src)
 {
        int len = 0, ret;
 
@@ -189,7 +191,7 @@ static int __kprobes copy_optimized_instructions(u8 *dest, u8 *src)
 }
 
 /* Check whether insn is indirect jump */
-static int __kprobes insn_is_indirect_jump(struct insn *insn)
+static int insn_is_indirect_jump(struct insn *insn)
 {
        return ((insn->opcode.bytes[0] == 0xff &&
                (X86_MODRM_REG(insn->modrm.value) & 6) == 4) || /* Jump */
@@ -224,7 +226,7 @@ static int insn_jump_into_range(struct insn *insn, unsigned long start, int len)
 }
 
 /* Decode whole function to ensure any instructions don't jump into target */
-static int __kprobes can_optimize(unsigned long paddr)
+static int can_optimize(unsigned long paddr)
 {
        unsigned long addr, size = 0, offset = 0;
        struct insn insn;
@@ -275,7 +277,7 @@ static int __kprobes can_optimize(unsigned long paddr)
 }
 
 /* Check optimized_kprobe can actually be optimized. */
-int __kprobes arch_check_optimized_kprobe(struct optimized_kprobe *op)
+int arch_check_optimized_kprobe(struct optimized_kprobe *op)
 {
        int i;
        struct kprobe *p;
@@ -290,15 +292,15 @@ int __kprobes arch_check_optimized_kprobe(struct optimized_kprobe *op)
 }
 
 /* Check the addr is within the optimized instructions. */
-int __kprobes
-arch_within_optimized_kprobe(struct optimized_kprobe *op, unsigned long addr)
+int arch_within_optimized_kprobe(struct optimized_kprobe *op,
+                                unsigned long addr)
 {
        return ((unsigned long)op->kp.addr <= addr &&
                (unsigned long)op->kp.addr + op->optinsn.size > addr);
 }
 
 /* Free optimized instruction slot */
-static __kprobes
+static
 void __arch_remove_optimized_kprobe(struct optimized_kprobe *op, int dirty)
 {
        if (op->optinsn.insn) {
@@ -308,7 +310,7 @@ void __arch_remove_optimized_kprobe(struct optimized_kprobe *op, int dirty)
        }
 }
 
-void __kprobes arch_remove_optimized_kprobe(struct optimized_kprobe *op)
+void arch_remove_optimized_kprobe(struct optimized_kprobe *op)
 {
        __arch_remove_optimized_kprobe(op, 1);
 }
@@ -318,7 +320,7 @@ void __kprobes arch_remove_optimized_kprobe(struct optimized_kprobe *op)
  * Target instructions MUST be relocatable (checked inside)
  * This is called when new aggr(opt)probe is allocated or reused.
  */
-int __kprobes arch_prepare_optimized_kprobe(struct optimized_kprobe *op)
+int arch_prepare_optimized_kprobe(struct optimized_kprobe *op)
 {
        u8 *buf;
        int ret;
@@ -372,7 +374,7 @@ int __kprobes arch_prepare_optimized_kprobe(struct optimized_kprobe *op)
  * Replace breakpoints (int3) with relative jumps.
  * Caller must call with locking kprobe_mutex and text_mutex.
  */
-void __kprobes arch_optimize_kprobes(struct list_head *oplist)
+void arch_optimize_kprobes(struct list_head *oplist)
 {
        struct optimized_kprobe *op, *tmp;
        u8 insn_buf[RELATIVEJUMP_SIZE];
@@ -398,7 +400,7 @@ void __kprobes arch_optimize_kprobes(struct list_head *oplist)
 }
 
 /* Replace a relative jump with a breakpoint (int3).  */
-void __kprobes arch_unoptimize_kprobe(struct optimized_kprobe *op)
+void arch_unoptimize_kprobe(struct optimized_kprobe *op)
 {
        u8 insn_buf[RELATIVEJUMP_SIZE];
 
@@ -424,8 +426,7 @@ extern void arch_unoptimize_kprobes(struct list_head *oplist,
        }
 }
 
-int  __kprobes
-setup_detour_execution(struct kprobe *p, struct pt_regs *regs, int reenter)
+int setup_detour_execution(struct kprobe *p, struct pt_regs *regs, int reenter)
 {
        struct optimized_kprobe *op;
 
@@ -441,3 +442,4 @@ setup_detour_execution(struct kprobe *p, struct pt_regs *regs, int reenter)
        }
        return 0;
 }
+NOKPROBE_SYMBOL(setup_detour_execution);
index 7e97371387fdd80eaeb6c1fb587d0b48c1593511..3dd8e2c4d74a9ed4a124baf36a83de78be1be02d 100644 (file)
@@ -251,8 +251,9 @@ u32 kvm_read_and_reset_pf_reason(void)
        return reason;
 }
 EXPORT_SYMBOL_GPL(kvm_read_and_reset_pf_reason);
+NOKPROBE_SYMBOL(kvm_read_and_reset_pf_reason);
 
-dotraplinkage void __kprobes
+dotraplinkage void
 do_async_page_fault(struct pt_regs *regs, unsigned long error_code)
 {
        enum ctx_state prev_state;
@@ -276,6 +277,7 @@ do_async_page_fault(struct pt_regs *regs, unsigned long error_code)
                break;
        }
 }
+NOKPROBE_SYMBOL(do_async_page_fault);
 
 static void __init paravirt_ops_setup(void)
 {
index b4872b999a713d7fc08f7578b672d804a30dd13e..c3e985d1751ced9dbab5ac0aa7c38f9623b449f4 100644 (file)
@@ -110,7 +110,7 @@ static void nmi_max_handler(struct irq_work *w)
                a->handler, whole_msecs, decimal_msecs);
 }
 
-static int __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, bool b2b)
+static int nmi_handle(unsigned int type, struct pt_regs *regs, bool b2b)
 {
        struct nmi_desc *desc = nmi_to_desc(type);
        struct nmiaction *a;
@@ -146,6 +146,7 @@ static int __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, bool b2
        /* return total number of NMI events handled */
        return handled;
 }
+NOKPROBE_SYMBOL(nmi_handle);
 
 int __register_nmi_handler(unsigned int type, struct nmiaction *action)
 {
@@ -208,7 +209,7 @@ void unregister_nmi_handler(unsigned int type, const char *name)
 }
 EXPORT_SYMBOL_GPL(unregister_nmi_handler);
 
-static __kprobes void
+static void
 pci_serr_error(unsigned char reason, struct pt_regs *regs)
 {
        /* check to see if anyone registered against these types of errors */
@@ -238,8 +239,9 @@ pci_serr_error(unsigned char reason, struct pt_regs *regs)
        reason = (reason & NMI_REASON_CLEAR_MASK) | NMI_REASON_CLEAR_SERR;
        outb(reason, NMI_REASON_PORT);
 }
+NOKPROBE_SYMBOL(pci_serr_error);
 
-static __kprobes void
+static void
 io_check_error(unsigned char reason, struct pt_regs *regs)
 {
        unsigned long i;
@@ -269,8 +271,9 @@ io_check_error(unsigned char reason, struct pt_regs *regs)
        reason &= ~NMI_REASON_CLEAR_IOCHK;
        outb(reason, NMI_REASON_PORT);
 }
+NOKPROBE_SYMBOL(io_check_error);
 
-static __kprobes void
+static void
 unknown_nmi_error(unsigned char reason, struct pt_regs *regs)
 {
        int handled;
@@ -298,11 +301,12 @@ unknown_nmi_error(unsigned char reason, struct pt_regs *regs)
 
        pr_emerg("Dazed and confused, but trying to continue\n");
 }
+NOKPROBE_SYMBOL(unknown_nmi_error);
 
 static DEFINE_PER_CPU(bool, swallow_nmi);
 static DEFINE_PER_CPU(unsigned long, last_nmi_rip);
 
-static __kprobes void default_do_nmi(struct pt_regs *regs)
+static void default_do_nmi(struct pt_regs *regs)
 {
        unsigned char reason = 0;
        int handled;
@@ -401,6 +405,7 @@ static __kprobes void default_do_nmi(struct pt_regs *regs)
        else
                unknown_nmi_error(reason, regs);
 }
+NOKPROBE_SYMBOL(default_do_nmi);
 
 /*
  * NMIs can hit breakpoints which will cause it to lose its
@@ -520,7 +525,7 @@ static inline void nmi_nesting_postprocess(void)
 }
 #endif
 
-dotraplinkage notrace __kprobes void
+dotraplinkage notrace void
 do_nmi(struct pt_regs *regs, long error_code)
 {
        nmi_nesting_preprocess(regs);
@@ -537,6 +542,7 @@ do_nmi(struct pt_regs *regs, long error_code)
        /* On i386, may loop back to preprocess */
        nmi_nesting_postprocess();
 }
+NOKPROBE_SYMBOL(do_nmi);
 
 void stop_nmi(void)
 {
index 1b10af835c31d9e45d237f31d7061a8e58946b57..548d25f00c90ad010379b0b36884cabee82b6e02 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/efi.h>
 #include <linux/bcd.h>
 #include <linux/highmem.h>
+#include <linux/kprobes.h>
 
 #include <asm/bug.h>
 #include <asm/paravirt.h>
@@ -389,6 +390,11 @@ __visible struct pv_cpu_ops pv_cpu_ops = {
        .end_context_switch = paravirt_nop,
 };
 
+/* At this point, native_get/set_debugreg has real function entries */
+NOKPROBE_SYMBOL(native_get_debugreg);
+NOKPROBE_SYMBOL(native_set_debugreg);
+NOKPROBE_SYMBOL(native_load_idt);
+
 struct pv_apic_ops pv_apic_ops = {
 #ifdef CONFIG_X86_LOCAL_APIC
        .startup_ipi_hook = paravirt_nop,
index 898d077617a99ab7c6ef055b06f409c8222a4249..ca5b02d405c3ba0c17e8c7e060222559a6a1d9b3 100644 (file)
@@ -413,12 +413,11 @@ void set_personality_ia32(bool x32)
        set_thread_flag(TIF_ADDR32);
 
        /* Mark the associated mm as containing 32-bit tasks. */
-       if (current->mm)
-               current->mm->context.ia32_compat = 1;
-
        if (x32) {
                clear_thread_flag(TIF_IA32);
                set_thread_flag(TIF_X32);
+               if (current->mm)
+                       current->mm->context.ia32_compat = TIF_X32;
                current->personality &= ~READ_IMPLIES_EXEC;
                /* is_compat_task() uses the presence of the x32
                   syscall bit flag to determine compat status */
@@ -426,6 +425,8 @@ void set_personality_ia32(bool x32)
        } else {
                set_thread_flag(TIF_IA32);
                clear_thread_flag(TIF_X32);
+               if (current->mm)
+                       current->mm->context.ia32_compat = TIF_IA32;
                current->personality |= force_personality32;
                /* Prepare the first "return" to user space */
                current_thread_info()->status |= TS_COMPAT;
index f73b5d435bdca59ff7c12c157a36997773fc2e07..c6eb418c562779f4383a9c17b737e748f5394c7e 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/ptrace.h>
+#include <linux/uprobes.h>
 #include <linux/string.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
@@ -106,7 +107,7 @@ static inline void preempt_conditional_cli(struct pt_regs *regs)
        preempt_count_dec();
 }
 
-static int __kprobes
+static nokprobe_inline int
 do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str,
                  struct pt_regs *regs, long error_code)
 {
@@ -136,7 +137,38 @@ do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str,
        return -1;
 }
 
-static void __kprobes
+static siginfo_t *fill_trap_info(struct pt_regs *regs, int signr, int trapnr,
+                               siginfo_t *info)
+{
+       unsigned long siaddr;
+       int sicode;
+
+       switch (trapnr) {
+       default:
+               return SEND_SIG_PRIV;
+
+       case X86_TRAP_DE:
+               sicode = FPE_INTDIV;
+               siaddr = uprobe_get_trap_addr(regs);
+               break;
+       case X86_TRAP_UD:
+               sicode = ILL_ILLOPN;
+               siaddr = uprobe_get_trap_addr(regs);
+               break;
+       case X86_TRAP_AC:
+               sicode = BUS_ADRALN;
+               siaddr = 0;
+               break;
+       }
+
+       info->si_signo = signr;
+       info->si_errno = 0;
+       info->si_code = sicode;
+       info->si_addr = (void __user *)siaddr;
+       return info;
+}
+
+static void
 do_trap(int trapnr, int signr, char *str, struct pt_regs *regs,
        long error_code, siginfo_t *info)
 {
@@ -168,60 +200,43 @@ do_trap(int trapnr, int signr, char *str, struct pt_regs *regs,
        }
 #endif
 
-       if (info)
-               force_sig_info(signr, info, tsk);
-       else
-               force_sig(signr, tsk);
+       force_sig_info(signr, info ?: SEND_SIG_PRIV, tsk);
 }
+NOKPROBE_SYMBOL(do_trap);
 
-#define DO_ERROR(trapnr, signr, str, name)                             \
-dotraplinkage void do_##name(struct pt_regs *regs, long error_code)    \
-{                                                                      \
-       enum ctx_state prev_state;                                      \
-                                                                       \
-       prev_state = exception_enter();                                 \
-       if (notify_die(DIE_TRAP, str, regs, error_code,                 \
-                       trapnr, signr) == NOTIFY_STOP) {                \
-               exception_exit(prev_state);                             \
-               return;                                                 \
-       }                                                               \
-       conditional_sti(regs);                                          \
-       do_trap(trapnr, signr, str, regs, error_code, NULL);            \
-       exception_exit(prev_state);                                     \
+static void do_error_trap(struct pt_regs *regs, long error_code, char *str,
+                         unsigned long trapnr, int signr)
+{
+       enum ctx_state prev_state = exception_enter();
+       siginfo_t info;
+
+       if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) !=
+                       NOTIFY_STOP) {
+               conditional_sti(regs);
+               do_trap(trapnr, signr, str, regs, error_code,
+                       fill_trap_info(regs, signr, trapnr, &info));
+       }
+
+       exception_exit(prev_state);
 }
 
-#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr)                \
+#define DO_ERROR(trapnr, signr, str, name)                             \
 dotraplinkage void do_##name(struct pt_regs *regs, long error_code)    \
 {                                                                      \
-       siginfo_t info;                                                 \
-       enum ctx_state prev_state;                                      \
-                                                                       \
-       info.si_signo = signr;                                          \
-       info.si_errno = 0;                                              \
-       info.si_code = sicode;                                          \
-       info.si_addr = (void __user *)siaddr;                           \
-       prev_state = exception_enter();                                 \
-       if (notify_die(DIE_TRAP, str, regs, error_code,                 \
-                       trapnr, signr) == NOTIFY_STOP) {                \
-               exception_exit(prev_state);                             \
-               return;                                                 \
-       }                                                               \
-       conditional_sti(regs);                                          \
-       do_trap(trapnr, signr, str, regs, error_code, &info);           \
-       exception_exit(prev_state);                                     \
+       do_error_trap(regs, error_code, str, trapnr, signr);            \
 }
 
-DO_ERROR_INFO(X86_TRAP_DE,     SIGFPE,  "divide error",                        divide_error,                FPE_INTDIV, regs->ip )
-DO_ERROR     (X86_TRAP_OF,     SIGSEGV, "overflow",                    overflow                                          )
-DO_ERROR     (X86_TRAP_BR,     SIGSEGV, "bounds",                      bounds                                            )
-DO_ERROR_INFO(X86_TRAP_UD,     SIGILL,  "invalid opcode",              invalid_op,                  ILL_ILLOPN, regs->ip )
-DO_ERROR     (X86_TRAP_OLD_MF, SIGFPE,  "coprocessor segment overrun", coprocessor_segment_overrun                       )
-DO_ERROR     (X86_TRAP_TS,     SIGSEGV, "invalid TSS",                 invalid_TSS                                       )
-DO_ERROR     (X86_TRAP_NP,     SIGBUS,  "segment not present",         segment_not_present                               )
+DO_ERROR(X86_TRAP_DE,     SIGFPE,  "divide error",             divide_error)
+DO_ERROR(X86_TRAP_OF,     SIGSEGV, "overflow",                 overflow)
+DO_ERROR(X86_TRAP_BR,     SIGSEGV, "bounds",                   bounds)
+DO_ERROR(X86_TRAP_UD,     SIGILL,  "invalid opcode",           invalid_op)
+DO_ERROR(X86_TRAP_OLD_MF, SIGFPE,  "coprocessor segment overrun",coprocessor_segment_overrun)
+DO_ERROR(X86_TRAP_TS,     SIGSEGV, "invalid TSS",              invalid_TSS)
+DO_ERROR(X86_TRAP_NP,     SIGBUS,  "segment not present",      segment_not_present)
 #ifdef CONFIG_X86_32
-DO_ERROR     (X86_TRAP_SS,     SIGBUS,  "stack segment",               stack_segment                                     )
+DO_ERROR(X86_TRAP_SS,     SIGBUS,  "stack segment",            stack_segment)
 #endif
-DO_ERROR_INFO(X86_TRAP_AC,     SIGBUS,  "alignment check",             alignment_check,             BUS_ADRALN, 0        )
+DO_ERROR(X86_TRAP_AC,     SIGBUS,  "alignment check",          alignment_check)
 
 #ifdef CONFIG_X86_64
 /* Runs on IST stack */
@@ -263,7 +278,7 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
 }
 #endif
 
-dotraplinkage void __kprobes
+dotraplinkage void
 do_general_protection(struct pt_regs *regs, long error_code)
 {
        struct task_struct *tsk;
@@ -305,13 +320,14 @@ do_general_protection(struct pt_regs *regs, long error_code)
                pr_cont("\n");
        }
 
-       force_sig(SIGSEGV, tsk);
+       force_sig_info(SIGSEGV, SEND_SIG_PRIV, tsk);
 exit:
        exception_exit(prev_state);
 }
+NOKPROBE_SYMBOL(do_general_protection);
 
 /* May run on IST stack. */
-dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_code)
+dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code)
 {
        enum ctx_state prev_state;
 
@@ -327,13 +343,18 @@ dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_co
        if (poke_int3_handler(regs))
                return;
 
-       prev_state = exception_enter();
 #ifdef CONFIG_KGDB_LOW_LEVEL_TRAP
        if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,
                                SIGTRAP) == NOTIFY_STOP)
                goto exit;
 #endif /* CONFIG_KGDB_LOW_LEVEL_TRAP */
 
+#ifdef CONFIG_KPROBES
+       if (kprobe_int3_handler(regs))
+               return;
+#endif
+       prev_state = exception_enter();
+
        if (notify_die(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,
                        SIGTRAP) == NOTIFY_STOP)
                goto exit;
@@ -350,6 +371,7 @@ dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_co
 exit:
        exception_exit(prev_state);
 }
+NOKPROBE_SYMBOL(do_int3);
 
 #ifdef CONFIG_X86_64
 /*
@@ -357,7 +379,7 @@ exit:
  * for scheduling or signal handling. The actual stack switch is done in
  * entry.S
  */
-asmlinkage __visible __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs)
+asmlinkage __visible struct pt_regs *sync_regs(struct pt_regs *eregs)
 {
        struct pt_regs *regs = eregs;
        /* Did already sync */
@@ -376,6 +398,7 @@ asmlinkage __visible __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs)
                *regs = *eregs;
        return regs;
 }
+NOKPROBE_SYMBOL(sync_regs);
 #endif
 
 /*
@@ -402,7 +425,7 @@ asmlinkage __visible __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs)
  *
  * May run on IST stack.
  */
-dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
+dotraplinkage void do_debug(struct pt_regs *regs, long error_code)
 {
        struct task_struct *tsk = current;
        enum ctx_state prev_state;
@@ -410,8 +433,6 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
        unsigned long dr6;
        int si_code;
 
-       prev_state = exception_enter();
-
        get_debugreg(dr6, 6);
 
        /* Filter out all the reserved bits which are preset to 1 */
@@ -440,6 +461,12 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
        /* Store the virtualized DR6 value */
        tsk->thread.debugreg6 = dr6;
 
+#ifdef CONFIG_KPROBES
+       if (kprobe_debug_handler(regs))
+               goto exit;
+#endif
+       prev_state = exception_enter();
+
        if (notify_die(DIE_DEBUG, "debug", regs, (long)&dr6, error_code,
                                                        SIGTRAP) == NOTIFY_STOP)
                goto exit;
@@ -482,13 +509,14 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
 exit:
        exception_exit(prev_state);
 }
+NOKPROBE_SYMBOL(do_debug);
 
 /*
  * Note that we play around with the 'TS' bit in an attempt to get
  * the correct behaviour even in the presence of the asynchronous
  * IRQ13 behaviour
  */
-void math_error(struct pt_regs *regs, int error_code, int trapnr)
+static void math_error(struct pt_regs *regs, int error_code, int trapnr)
 {
        struct task_struct *task = current;
        siginfo_t info;
@@ -518,7 +546,7 @@ void math_error(struct pt_regs *regs, int error_code, int trapnr)
        task->thread.error_code = error_code;
        info.si_signo = SIGFPE;
        info.si_errno = 0;
-       info.si_addr = (void __user *)regs->ip;
+       info.si_addr = (void __user *)uprobe_get_trap_addr(regs);
        if (trapnr == X86_TRAP_MF) {
                unsigned short cwd, swd;
                /*
@@ -645,7 +673,7 @@ void math_state_restore(void)
         */
        if (unlikely(restore_fpu_checking(tsk))) {
                drop_init_fpu(tsk);
-               force_sig(SIGSEGV, tsk);
+               force_sig_info(SIGSEGV, SEND_SIG_PRIV, tsk);
                return;
        }
 
@@ -653,7 +681,7 @@ void math_state_restore(void)
 }
 EXPORT_SYMBOL_GPL(math_state_restore);
 
-dotraplinkage void __kprobes
+dotraplinkage void
 do_device_not_available(struct pt_regs *regs, long error_code)
 {
        enum ctx_state prev_state;
@@ -679,6 +707,7 @@ do_device_not_available(struct pt_regs *regs, long error_code)
 #endif
        exception_exit(prev_state);
 }
+NOKPROBE_SYMBOL(do_device_not_available);
 
 #ifdef CONFIG_X86_32
 dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code)
index ace22916ade3ce60859ead56db7f2c8f08ff9760..5d1cbfe4ae58eea4276bbbd9fc342188cafeeb95 100644 (file)
 
 /* Post-execution fixups. */
 
-/* No fixup needed */
-#define UPROBE_FIX_NONE                0x0
-
 /* Adjust IP back to vicinity of actual insn */
-#define UPROBE_FIX_IP          0x1
+#define UPROBE_FIX_IP          0x01
 
 /* Adjust the return address of a call insn */
-#define UPROBE_FIX_CALL        0x2
+#define UPROBE_FIX_CALL                0x02
 
 /* Instruction will modify TF, don't change it */
-#define UPROBE_FIX_SETF        0x4
+#define UPROBE_FIX_SETF                0x04
 
-#define UPROBE_FIX_RIP_AX      0x8000
-#define UPROBE_FIX_RIP_CX      0x4000
+#define UPROBE_FIX_RIP_SI      0x08
+#define UPROBE_FIX_RIP_DI      0x10
+#define UPROBE_FIX_RIP_BX      0x20
+#define UPROBE_FIX_RIP_MASK    \
+       (UPROBE_FIX_RIP_SI | UPROBE_FIX_RIP_DI | UPROBE_FIX_RIP_BX)
 
 #define        UPROBE_TRAP_NR          UINT_MAX
 
@@ -67,6 +67,7 @@
  * to keep gcc from statically optimizing it out, as variable_test_bit makes
  * some versions of gcc to think only *(unsigned long*) is used.
  */
+#if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION)
 static volatile u32 good_insns_32[256 / 32] = {
        /*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
        /*      ----------------------------------------------         */
@@ -89,33 +90,12 @@ static volatile u32 good_insns_32[256 / 32] = {
        /*      ----------------------------------------------         */
        /*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
 };
+#else
+#define good_insns_32  NULL
+#endif
 
-/* Using this for both 64-bit and 32-bit apps */
-static volatile u32 good_2byte_insns[256 / 32] = {
-       /*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
-       /*      ----------------------------------------------         */
-       W(0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1) | /* 00 */
-       W(0x10, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1) , /* 10 */
-       W(0x20, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1) | /* 20 */
-       W(0x30, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 30 */
-       W(0x40, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 40 */
-       W(0x50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 50 */
-       W(0x60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 60 */
-       W(0x70, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1) , /* 70 */
-       W(0x80, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 80 */
-       W(0x90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 90 */
-       W(0xa0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1) | /* a0 */
-       W(0xb0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1) , /* b0 */
-       W(0xc0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* c0 */
-       W(0xd0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* d0 */
-       W(0xe0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* e0 */
-       W(0xf0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)   /* f0 */
-       /*      ----------------------------------------------         */
-       /*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
-};
-
-#ifdef CONFIG_X86_64
 /* Good-instruction tables for 64-bit apps */
+#if defined(CONFIG_X86_64)
 static volatile u32 good_insns_64[256 / 32] = {
        /*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
        /*      ----------------------------------------------         */
@@ -138,7 +118,33 @@ static volatile u32 good_insns_64[256 / 32] = {
        /*      ----------------------------------------------         */
        /*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
 };
+#else
+#define good_insns_64  NULL
 #endif
+
+/* Using this for both 64-bit and 32-bit apps */
+static volatile u32 good_2byte_insns[256 / 32] = {
+       /*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
+       /*      ----------------------------------------------         */
+       W(0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1) | /* 00 */
+       W(0x10, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1) , /* 10 */
+       W(0x20, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1) | /* 20 */
+       W(0x30, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 30 */
+       W(0x40, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 40 */
+       W(0x50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 50 */
+       W(0x60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 60 */
+       W(0x70, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1) , /* 70 */
+       W(0x80, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 80 */
+       W(0x90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 90 */
+       W(0xa0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1) | /* a0 */
+       W(0xb0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1) , /* b0 */
+       W(0xc0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* c0 */
+       W(0xd0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* d0 */
+       W(0xe0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* e0 */
+       W(0xf0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)   /* f0 */
+       /*      ----------------------------------------------         */
+       /*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
+};
 #undef W
 
 /*
@@ -209,16 +215,25 @@ static bool is_prefix_bad(struct insn *insn)
        return false;
 }
 
-static int validate_insn_32bits(struct arch_uprobe *auprobe, struct insn *insn)
+static int uprobe_init_insn(struct arch_uprobe *auprobe, struct insn *insn, bool x86_64)
 {
-       insn_init(insn, auprobe->insn, false);
+       u32 volatile *good_insns;
+
+       insn_init(insn, auprobe->insn, x86_64);
+       /* has the side-effect of processing the entire instruction */
+       insn_get_length(insn);
+       if (WARN_ON_ONCE(!insn_complete(insn)))
+               return -ENOEXEC;
 
-       /* Skip good instruction prefixes; reject "bad" ones. */
-       insn_get_opcode(insn);
        if (is_prefix_bad(insn))
                return -ENOTSUPP;
 
-       if (test_bit(OPCODE1(insn), (unsigned long *)good_insns_32))
+       if (x86_64)
+               good_insns = good_insns_64;
+       else
+               good_insns = good_insns_32;
+
+       if (test_bit(OPCODE1(insn), (unsigned long *)good_insns))
                return 0;
 
        if (insn->opcode.nbytes == 2) {
@@ -230,14 +245,18 @@ static int validate_insn_32bits(struct arch_uprobe *auprobe, struct insn *insn)
 }
 
 #ifdef CONFIG_X86_64
+static inline bool is_64bit_mm(struct mm_struct *mm)
+{
+       return  !config_enabled(CONFIG_IA32_EMULATION) ||
+               !(mm->context.ia32_compat == TIF_IA32);
+}
 /*
  * If arch_uprobe->insn doesn't use rip-relative addressing, return
  * immediately.  Otherwise, rewrite the instruction so that it accesses
  * its memory operand indirectly through a scratch register.  Set
- * arch_uprobe->fixups and arch_uprobe->rip_rela_target_address
- * accordingly.  (The contents of the scratch register will be saved
- * before we single-step the modified instruction, and restored
- * afterward.)
+ * defparam->fixups accordingly. (The contents of the scratch register
+ * will be saved before we single-step the modified instruction,
+ * and restored afterward).
  *
  * We do this because a rip-relative instruction can access only a
  * relatively small area (+/- 2 GB from the instruction), and the XOL
@@ -248,164 +267,192 @@ static int validate_insn_32bits(struct arch_uprobe *auprobe, struct insn *insn)
  *
  * Some useful facts about rip-relative instructions:
  *
- *  - There's always a modrm byte.
+ *  - There's always a modrm byte with bit layout "00 reg 101".
  *  - There's never a SIB byte.
  *  - The displacement is always 4 bytes.
+ *  - REX.B=1 bit in REX prefix, which normally extends r/m field,
+ *    has no effect on rip-relative mode. It doesn't make modrm byte
+ *    with r/m=101 refer to register 1101 = R13.
  */
-static void
-handle_riprel_insn(struct arch_uprobe *auprobe, struct insn *insn)
+static void riprel_analyze(struct arch_uprobe *auprobe, struct insn *insn)
 {
        u8 *cursor;
        u8 reg;
+       u8 reg2;
 
        if (!insn_rip_relative(insn))
                return;
 
        /*
-        * insn_rip_relative() would have decoded rex_prefix, modrm.
+        * insn_rip_relative() would have decoded rex_prefix, vex_prefix, modrm.
         * Clear REX.b bit (extension of MODRM.rm field):
-        * we want to encode rax/rcx, not r8/r9.
+        * we want to encode low numbered reg, not r8+.
         */
        if (insn->rex_prefix.nbytes) {
                cursor = auprobe->insn + insn_offset_rex_prefix(insn);
-               *cursor &= 0xfe;        /* Clearing REX.B bit */
+               /* REX byte has 0100wrxb layout, clearing REX.b bit */
+               *cursor &= 0xfe;
+       }
+       /*
+        * Similar treatment for VEX3 prefix.
+        * TODO: add XOP/EVEX treatment when insn decoder supports them
+        */
+       if (insn->vex_prefix.nbytes == 3) {
+               /*
+                * vex2:     c5    rvvvvLpp   (has no b bit)
+                * vex3/xop: c4/8f rxbmmmmm wvvvvLpp
+                * evex:     62    rxbR00mm wvvvv1pp zllBVaaa
+                *   (evex will need setting of both b and x since
+                *   in non-sib encoding evex.x is 4th bit of MODRM.rm)
+                * Setting VEX3.b (setting because it has inverted meaning):
+                */
+               cursor = auprobe->insn + insn_offset_vex_prefix(insn) + 1;
+               *cursor |= 0x20;
        }
 
+       /*
+        * Convert from rip-relative addressing to register-relative addressing
+        * via a scratch register.
+        *
+        * This is tricky since there are insns with modrm byte
+        * which also use registers not encoded in modrm byte:
+        * [i]div/[i]mul: implicitly use dx:ax
+        * shift ops: implicitly use cx
+        * cmpxchg: implicitly uses ax
+        * cmpxchg8/16b: implicitly uses dx:ax and bx:cx
+        *   Encoding: 0f c7/1 modrm
+        *   The code below thinks that reg=1 (cx), chooses si as scratch.
+        * mulx: implicitly uses dx: mulx r/m,r1,r2 does r1:r2 = dx * r/m.
+        *   First appeared in Haswell (BMI2 insn). It is vex-encoded.
+        *   Example where none of bx,cx,dx can be used as scratch reg:
+        *   c4 e2 63 f6 0d disp32   mulx disp32(%rip),%ebx,%ecx
+        * [v]pcmpistri: implicitly uses cx, xmm0
+        * [v]pcmpistrm: implicitly uses xmm0
+        * [v]pcmpestri: implicitly uses ax, dx, cx, xmm0
+        * [v]pcmpestrm: implicitly uses ax, dx, xmm0
+        *   Evil SSE4.2 string comparison ops from hell.
+        * maskmovq/[v]maskmovdqu: implicitly uses (ds:rdi) as destination.
+        *   Encoding: 0f f7 modrm, 66 0f f7 modrm, vex-encoded: c5 f9 f7 modrm.
+        *   Store op1, byte-masked by op2 msb's in each byte, to (ds:rdi).
+        *   AMD says it has no 3-operand form (vex.vvvv must be 1111)
+        *   and that it can have only register operands, not mem
+        *   (its modrm byte must have mode=11).
+        *   If these restrictions will ever be lifted,
+        *   we'll need code to prevent selection of di as scratch reg!
+        *
+        * Summary: I don't know any insns with modrm byte which
+        * use SI register implicitly. DI register is used only
+        * by one insn (maskmovq) and BX register is used
+        * only by one too (cmpxchg8b).
+        * BP is stack-segment based (may be a problem?).
+        * AX, DX, CX are off-limits (many implicit users).
+        * SP is unusable (it's stack pointer - think about "pop mem";
+        * also, rsp+disp32 needs sib encoding -> insn length change).
+        */
+
+       reg = MODRM_REG(insn);  /* Fetch modrm.reg */
+       reg2 = 0xff;            /* Fetch vex.vvvv */
+       if (insn->vex_prefix.nbytes == 2)
+               reg2 = insn->vex_prefix.bytes[1];
+       else if (insn->vex_prefix.nbytes == 3)
+               reg2 = insn->vex_prefix.bytes[2];
+       /*
+        * TODO: add XOP, EXEV vvvv reading.
+        *
+        * vex.vvvv field is in bits 6-3, bits are inverted.
+        * But in 32-bit mode, high-order bit may be ignored.
+        * Therefore, let's consider only 3 low-order bits.
+        */
+       reg2 = ((reg2 >> 3) & 0x7) ^ 0x7;
+       /*
+        * Register numbering is ax,cx,dx,bx, sp,bp,si,di, r8..r15.
+        *
+        * Choose scratch reg. Order is important: must not select bx
+        * if we can use si (cmpxchg8b case!)
+        */
+       if (reg != 6 && reg2 != 6) {
+               reg2 = 6;
+               auprobe->defparam.fixups |= UPROBE_FIX_RIP_SI;
+       } else if (reg != 7 && reg2 != 7) {
+               reg2 = 7;
+               auprobe->defparam.fixups |= UPROBE_FIX_RIP_DI;
+               /* TODO (paranoia): force maskmovq to not use di */
+       } else {
+               reg2 = 3;
+               auprobe->defparam.fixups |= UPROBE_FIX_RIP_BX;
+       }
        /*
         * Point cursor at the modrm byte.  The next 4 bytes are the
         * displacement.  Beyond the displacement, for some instructions,
         * is the immediate operand.
         */
        cursor = auprobe->insn + insn_offset_modrm(insn);
-       insn_get_length(insn);
-
        /*
-        * Convert from rip-relative addressing to indirect addressing
-        * via a scratch register.  Change the r/m field from 0x5 (%rip)
-        * to 0x0 (%rax) or 0x1 (%rcx), and squeeze out the offset field.
+        * Change modrm from "00 reg 101" to "10 reg reg2". Example:
+        * 89 05 disp32  mov %eax,disp32(%rip) becomes
+        * 89 86 disp32  mov %eax,disp32(%rsi)
         */
-       reg = MODRM_REG(insn);
-       if (reg == 0) {
-               /*
-                * The register operand (if any) is either the A register
-                * (%rax, %eax, etc.) or (if the 0x4 bit is set in the
-                * REX prefix) %r8.  In any case, we know the C register
-                * is NOT the register operand, so we use %rcx (register
-                * #1) for the scratch register.
-                */
-               auprobe->fixups = UPROBE_FIX_RIP_CX;
-               /* Change modrm from 00 000 101 to 00 000 001. */
-               *cursor = 0x1;
-       } else {
-               /* Use %rax (register #0) for the scratch register. */
-               auprobe->fixups = UPROBE_FIX_RIP_AX;
-               /* Change modrm from 00 xxx 101 to 00 xxx 000 */
-               *cursor = (reg << 3);
-       }
-
-       /* Target address = address of next instruction + (signed) offset */
-       auprobe->rip_rela_target_address = (long)insn->length + insn->displacement.value;
+       *cursor = 0x80 | (reg << 3) | reg2;
+}
 
-       /* Displacement field is gone; slide immediate field (if any) over. */
-       if (insn->immediate.nbytes) {
-               cursor++;
-               memmove(cursor, cursor + insn->displacement.nbytes, insn->immediate.nbytes);
-       }
+static inline unsigned long *
+scratch_reg(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+       if (auprobe->defparam.fixups & UPROBE_FIX_RIP_SI)
+               return &regs->si;
+       if (auprobe->defparam.fixups & UPROBE_FIX_RIP_DI)
+               return &regs->di;
+       return &regs->bx;
 }
 
 /*
  * If we're emulating a rip-relative instruction, save the contents
  * of the scratch register and store the target address in that register.
  */
-static void
-pre_xol_rip_insn(struct arch_uprobe *auprobe, struct pt_regs *regs,
-                               struct arch_uprobe_task *autask)
-{
-       if (auprobe->fixups & UPROBE_FIX_RIP_AX) {
-               autask->saved_scratch_register = regs->ax;
-               regs->ax = current->utask->vaddr;
-               regs->ax += auprobe->rip_rela_target_address;
-       } else if (auprobe->fixups & UPROBE_FIX_RIP_CX) {
-               autask->saved_scratch_register = regs->cx;
-               regs->cx = current->utask->vaddr;
-               regs->cx += auprobe->rip_rela_target_address;
-       }
-}
-
-static void
-handle_riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs, long *correction)
+static void riprel_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
-       if (auprobe->fixups & (UPROBE_FIX_RIP_AX | UPROBE_FIX_RIP_CX)) {
-               struct arch_uprobe_task *autask;
-
-               autask = &current->utask->autask;
-               if (auprobe->fixups & UPROBE_FIX_RIP_AX)
-                       regs->ax = autask->saved_scratch_register;
-               else
-                       regs->cx = autask->saved_scratch_register;
+       if (auprobe->defparam.fixups & UPROBE_FIX_RIP_MASK) {
+               struct uprobe_task *utask = current->utask;
+               unsigned long *sr = scratch_reg(auprobe, regs);
 
-               /*
-                * The original instruction includes a displacement, and so
-                * is 4 bytes longer than what we've just single-stepped.
-                * Caller may need to apply other fixups to handle stuff
-                * like "jmpq *...(%rip)" and "callq *...(%rip)".
-                */
-               if (correction)
-                       *correction += 4;
+               utask->autask.saved_scratch_register = *sr;
+               *sr = utask->vaddr + auprobe->defparam.ilen;
        }
 }
 
-static int validate_insn_64bits(struct arch_uprobe *auprobe, struct insn *insn)
+static void riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
-       insn_init(insn, auprobe->insn, true);
-
-       /* Skip good instruction prefixes; reject "bad" ones. */
-       insn_get_opcode(insn);
-       if (is_prefix_bad(insn))
-               return -ENOTSUPP;
+       if (auprobe->defparam.fixups & UPROBE_FIX_RIP_MASK) {
+               struct uprobe_task *utask = current->utask;
+               unsigned long *sr = scratch_reg(auprobe, regs);
 
-       if (test_bit(OPCODE1(insn), (unsigned long *)good_insns_64))
-               return 0;
-
-       if (insn->opcode.nbytes == 2) {
-               if (test_bit(OPCODE2(insn), (unsigned long *)good_2byte_insns))
-                       return 0;
+               *sr = utask->autask.saved_scratch_register;
        }
-       return -ENOTSUPP;
 }
-
-static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm, struct insn *insn)
+#else /* 32-bit: */
+static inline bool is_64bit_mm(struct mm_struct *mm)
 {
-       if (mm->context.ia32_compat)
-               return validate_insn_32bits(auprobe, insn);
-       return validate_insn_64bits(auprobe, insn);
+       return false;
 }
-#else /* 32-bit: */
 /*
  * No RIP-relative addressing on 32-bit
  */
-static void handle_riprel_insn(struct arch_uprobe *auprobe, struct insn *insn)
+static void riprel_analyze(struct arch_uprobe *auprobe, struct insn *insn)
 {
 }
-static void pre_xol_rip_insn(struct arch_uprobe *auprobe, struct pt_regs *regs,
-                               struct arch_uprobe_task *autask)
+static void riprel_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
 }
-static void handle_riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs,
-                                       long *correction)
+static void riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
 }
-
-static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm,  struct insn *insn)
-{
-       return validate_insn_32bits(auprobe, insn);
-}
 #endif /* CONFIG_X86_64 */
 
 struct uprobe_xol_ops {
        bool    (*emulate)(struct arch_uprobe *, struct pt_regs *);
        int     (*pre_xol)(struct arch_uprobe *, struct pt_regs *);
        int     (*post_xol)(struct arch_uprobe *, struct pt_regs *);
+       void    (*abort)(struct arch_uprobe *, struct pt_regs *);
 };
 
 static inline int sizeof_long(void)
@@ -415,50 +462,67 @@ static inline int sizeof_long(void)
 
 static int default_pre_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
-       pre_xol_rip_insn(auprobe, regs, &current->utask->autask);
+       riprel_pre_xol(auprobe, regs);
        return 0;
 }
 
-/*
- * Adjust the return address pushed by a call insn executed out of line.
- */
-static int adjust_ret_addr(unsigned long sp, long correction)
+static int push_ret_address(struct pt_regs *regs, unsigned long ip)
 {
-       int rasize = sizeof_long();
-       long ra;
-
-       if (copy_from_user(&ra, (void __user *)sp, rasize))
-               return -EFAULT;
+       unsigned long new_sp = regs->sp - sizeof_long();
 
-       ra += correction;
-       if (copy_to_user((void __user *)sp, &ra, rasize))
+       if (copy_to_user((void __user *)new_sp, &ip, sizeof_long()))
                return -EFAULT;
 
+       regs->sp = new_sp;
        return 0;
 }
 
+/*
+ * We have to fix things up as follows:
+ *
+ * Typically, the new ip is relative to the copied instruction.  We need
+ * to make it relative to the original instruction (FIX_IP).  Exceptions
+ * are return instructions and absolute or indirect jump or call instructions.
+ *
+ * If the single-stepped instruction was a call, the return address that
+ * is atop the stack is the address following the copied instruction.  We
+ * need to make it the address following the original instruction (FIX_CALL).
+ *
+ * If the original instruction was a rip-relative instruction such as
+ * "movl %edx,0xnnnn(%rip)", we have instead executed an equivalent
+ * instruction using a scratch register -- e.g., "movl %edx,0xnnnn(%rsi)".
+ * We need to restore the contents of the scratch register
+ * (FIX_RIP_reg).
+ */
 static int default_post_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
        struct uprobe_task *utask = current->utask;
-       long correction = (long)(utask->vaddr - utask->xol_vaddr);
 
-       handle_riprel_post_xol(auprobe, regs, &correction);
-       if (auprobe->fixups & UPROBE_FIX_IP)
+       riprel_post_xol(auprobe, regs);
+       if (auprobe->defparam.fixups & UPROBE_FIX_IP) {
+               long correction = utask->vaddr - utask->xol_vaddr;
                regs->ip += correction;
-
-       if (auprobe->fixups & UPROBE_FIX_CALL) {
-               if (adjust_ret_addr(regs->sp, correction)) {
-                       regs->sp += sizeof_long();
+       } else if (auprobe->defparam.fixups & UPROBE_FIX_CALL) {
+               regs->sp += sizeof_long(); /* Pop incorrect return address */
+               if (push_ret_address(regs, utask->vaddr + auprobe->defparam.ilen))
                        return -ERESTART;
-               }
        }
+       /* popf; tell the caller to not touch TF */
+       if (auprobe->defparam.fixups & UPROBE_FIX_SETF)
+               utask->autask.saved_tf = true;
 
        return 0;
 }
 
+static void default_abort_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+       riprel_post_xol(auprobe, regs);
+}
+
 static struct uprobe_xol_ops default_xol_ops = {
        .pre_xol  = default_pre_xol_op,
        .post_xol = default_post_xol_op,
+       .abort    = default_abort_op,
 };
 
 static bool branch_is_call(struct arch_uprobe *auprobe)
@@ -520,7 +584,6 @@ static bool branch_emulate_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
        unsigned long offs = (long)auprobe->branch.offs;
 
        if (branch_is_call(auprobe)) {
-               unsigned long new_sp = regs->sp - sizeof_long();
                /*
                 * If it fails we execute this (mangled, see the comment in
                 * branch_clear_offset) insn out-of-line. In the likely case
@@ -530,9 +593,8 @@ static bool branch_emulate_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
                 *
                 * But there is corner case, see the comment in ->post_xol().
                 */
-               if (copy_to_user((void __user *)new_sp, &new_ip, sizeof_long()))
+               if (push_ret_address(regs, new_ip))
                        return false;
-               regs->sp = new_sp;
        } else if (!check_jmp_cond(auprobe, regs)) {
                offs = 0;
        }
@@ -583,11 +645,7 @@ static struct uprobe_xol_ops branch_xol_ops = {
 static int branch_setup_xol_ops(struct arch_uprobe *auprobe, struct insn *insn)
 {
        u8 opc1 = OPCODE1(insn);
-
-       /* has the side-effect of processing the entire instruction */
-       insn_get_length(insn);
-       if (WARN_ON_ONCE(!insn_complete(insn)))
-               return -ENOEXEC;
+       int i;
 
        switch (opc1) {
        case 0xeb:      /* jmp 8 */
@@ -612,6 +670,16 @@ static int branch_setup_xol_ops(struct arch_uprobe *auprobe, struct insn *insn)
                        return -ENOSYS;
        }
 
+       /*
+        * 16-bit overrides such as CALLW (66 e8 nn nn) are not supported.
+        * Intel and AMD behavior differ in 64-bit mode: Intel ignores 66 prefix.
+        * No one uses these insns, reject any branch insns with such prefix.
+        */
+       for (i = 0; i < insn->prefixes.nbytes; i++) {
+               if (insn->prefixes.bytes[i] == 0x66)
+                       return -ENOTSUPP;
+       }
+
        auprobe->branch.opc1 = opc1;
        auprobe->branch.ilen = insn->length;
        auprobe->branch.offs = insn->immediate.value;
@@ -630,10 +698,10 @@ static int branch_setup_xol_ops(struct arch_uprobe *auprobe, struct insn *insn)
 int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long addr)
 {
        struct insn insn;
-       bool fix_ip = true, fix_call = false;
+       u8 fix_ip_or_call = UPROBE_FIX_IP;
        int ret;
 
-       ret = validate_insn_bits(auprobe, mm, &insn);
+       ret = uprobe_init_insn(auprobe, &insn, is_64bit_mm(mm));
        if (ret)
                return ret;
 
@@ -642,44 +710,39 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
                return ret;
 
        /*
-        * Figure out which fixups arch_uprobe_post_xol() will need to perform,
-        * and annotate arch_uprobe->fixups accordingly. To start with, ->fixups
-        * is either zero or it reflects rip-related fixups.
+        * Figure out which fixups default_post_xol_op() will need to perform,
+        * and annotate defparam->fixups accordingly.
         */
        switch (OPCODE1(&insn)) {
        case 0x9d:              /* popf */
-               auprobe->fixups |= UPROBE_FIX_SETF;
+               auprobe->defparam.fixups |= UPROBE_FIX_SETF;
                break;
        case 0xc3:              /* ret or lret -- ip is correct */
        case 0xcb:
        case 0xc2:
        case 0xca:
-               fix_ip = false;
+       case 0xea:              /* jmp absolute -- ip is correct */
+               fix_ip_or_call = 0;
                break;
        case 0x9a:              /* call absolute - Fix return addr, not ip */
-               fix_call = true;
-               fix_ip = false;
-               break;
-       case 0xea:              /* jmp absolute -- ip is correct */
-               fix_ip = false;
+               fix_ip_or_call = UPROBE_FIX_CALL;
                break;
        case 0xff:
-               insn_get_modrm(&insn);
                switch (MODRM_REG(&insn)) {
                case 2: case 3:                 /* call or lcall, indirect */
-                       fix_call = true;
+                       fix_ip_or_call = UPROBE_FIX_CALL;
+                       break;
                case 4: case 5:                 /* jmp or ljmp, indirect */
-                       fix_ip = false;
+                       fix_ip_or_call = 0;
+                       break;
                }
                /* fall through */
        default:
-               handle_riprel_insn(auprobe, &insn);
+               riprel_analyze(auprobe, &insn);
        }
 
-       if (fix_ip)
-               auprobe->fixups |= UPROBE_FIX_IP;
-       if (fix_call)
-               auprobe->fixups |= UPROBE_FIX_CALL;
+       auprobe->defparam.ilen = insn.length;
+       auprobe->defparam.fixups |= fix_ip_or_call;
 
        auprobe->ops = &default_xol_ops;
        return 0;
@@ -694,6 +757,12 @@ int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
        struct uprobe_task *utask = current->utask;
 
+       if (auprobe->ops->pre_xol) {
+               int err = auprobe->ops->pre_xol(auprobe, regs);
+               if (err)
+                       return err;
+       }
+
        regs->ip = utask->xol_vaddr;
        utask->autask.saved_trap_nr = current->thread.trap_nr;
        current->thread.trap_nr = UPROBE_TRAP_NR;
@@ -703,8 +772,6 @@ int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
        if (test_tsk_thread_flag(current, TIF_BLOCKSTEP))
                set_task_blockstep(current, false);
 
-       if (auprobe->ops->pre_xol)
-               return auprobe->ops->pre_xol(auprobe, regs);
        return 0;
 }
 
@@ -732,56 +799,42 @@ bool arch_uprobe_xol_was_trapped(struct task_struct *t)
  * single-step, we single-stepped a copy of the instruction.
  *
  * This function prepares to resume execution after the single-step.
- * We have to fix things up as follows:
- *
- * Typically, the new ip is relative to the copied instruction.  We need
- * to make it relative to the original instruction (FIX_IP).  Exceptions
- * are return instructions and absolute or indirect jump or call instructions.
- *
- * If the single-stepped instruction was a call, the return address that
- * is atop the stack is the address following the copied instruction.  We
- * need to make it the address following the original instruction (FIX_CALL).
- *
- * If the original instruction was a rip-relative instruction such as
- * "movl %edx,0xnnnn(%rip)", we have instead executed an equivalent
- * instruction using a scratch register -- e.g., "movl %edx,(%rax)".
- * We need to restore the contents of the scratch register and adjust
- * the ip, keeping in mind that the instruction we executed is 4 bytes
- * shorter than the original instruction (since we squeezed out the offset
- * field).  (FIX_RIP_AX or FIX_RIP_CX)
  */
 int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
        struct uprobe_task *utask = current->utask;
+       bool send_sigtrap = utask->autask.saved_tf;
+       int err = 0;
 
        WARN_ON_ONCE(current->thread.trap_nr != UPROBE_TRAP_NR);
+       current->thread.trap_nr = utask->autask.saved_trap_nr;
 
        if (auprobe->ops->post_xol) {
-               int err = auprobe->ops->post_xol(auprobe, regs);
+               err = auprobe->ops->post_xol(auprobe, regs);
                if (err) {
-                       arch_uprobe_abort_xol(auprobe, regs);
                        /*
-                        * Restart the probed insn. ->post_xol() must ensure
-                        * this is really possible if it returns -ERESTART.
+                        * Restore ->ip for restart or post mortem analysis.
+                        * ->post_xol() must not return -ERESTART unless this
+                        * is really possible.
                         */
+                       regs->ip = utask->vaddr;
                        if (err == -ERESTART)
-                               return 0;
-                       return err;
+                               err = 0;
+                       send_sigtrap = false;
                }
        }
-
-       current->thread.trap_nr = utask->autask.saved_trap_nr;
        /*
         * arch_uprobe_pre_xol() doesn't save the state of TIF_BLOCKSTEP
         * so we can get an extra SIGTRAP if we do not clear TF. We need
         * to examine the opcode to make it right.
         */
-       if (utask->autask.saved_tf)
+       if (send_sigtrap)
                send_sig(SIGTRAP, current, 0);
-       else if (!(auprobe->fixups & UPROBE_FIX_SETF))
+
+       if (!utask->autask.saved_tf)
                regs->flags &= ~X86_EFLAGS_TF;
 
-       return 0;
+       return err;
 }
 
 /* callback routine for handling exceptions. */
@@ -815,18 +868,18 @@ int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val,
 
 /*
  * This function gets called when XOL instruction either gets trapped or
- * the thread has a fatal signal, or if arch_uprobe_post_xol() failed.
- * Reset the instruction pointer to its probed address for the potential
- * restart or for post mortem analysis.
+ * the thread has a fatal signal. Reset the instruction pointer to its
+ * probed address for the potential restart or for post mortem analysis.
  */
 void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
        struct uprobe_task *utask = current->utask;
 
-       current->thread.trap_nr = utask->autask.saved_trap_nr;
-       handle_riprel_post_xol(auprobe, regs, NULL);
-       instruction_pointer_set(regs, utask->vaddr);
+       if (auprobe->ops->abort)
+               auprobe->ops->abort(auprobe, regs);
 
+       current->thread.trap_nr = utask->autask.saved_trap_nr;
+       regs->ip = utask->vaddr;
        /* clear TF if it was set by us in arch_uprobe_pre_xol() */
        if (!utask->autask.saved_tf)
                regs->flags &= ~X86_EFLAGS_TF;
index 2930ae05d77305a3c3f76b821f843203969ffb2b..28f85c916712232e951f30a0e9819b56357a87a9 100644 (file)
@@ -4,8 +4,8 @@
  *  (inspired by Andi Kleen's thunk_64.S)
  * Subject to the GNU public license, v.2. No warranty of any kind.
  */
-
        #include <linux/linkage.h>
+       #include <asm/asm.h>
 
 #ifdef CONFIG_TRACE_IRQFLAGS
        /* put return address in eax (arg1) */
@@ -22,6 +22,7 @@
        popl %ecx
        popl %eax
        ret
+       _ASM_NOKPROBE(\name)
        .endm
 
        thunk_ra trace_hardirqs_on_thunk,trace_hardirqs_on_caller
index a63efd6bb6a5a24e9553abc8aef18e198be0953f..92d9feaff42b04fa0dd42f1c1f686cf3a43e7636 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/linkage.h>
 #include <asm/dwarf2.h>
 #include <asm/calling.h>
+#include <asm/asm.h>
 
        /* rdi: arg1 ... normal C conventions. rax is saved/restored. */
        .macro THUNK name, func, put_ret_addr_in_rdi=0
@@ -25,6 +26,7 @@
        call \func
        jmp  restore
        CFI_ENDPROC
+       _ASM_NOKPROBE(\name)
        .endm
 
 #ifdef CONFIG_TRACE_IRQFLAGS
@@ -43,3 +45,4 @@ restore:
        RESTORE_ARGS
        ret
        CFI_ENDPROC
+       _ASM_NOKPROBE(restore)
index 858b47b5221be716eba34760cfd44d511b9183e9..36642793e315fc8bb4b8682e5bf8f4dbe8f4ab7c 100644 (file)
@@ -8,7 +8,7 @@
 #include <linux/kdebug.h>              /* oops_begin/end, ...          */
 #include <linux/module.h>              /* search_exception_table       */
 #include <linux/bootmem.h>             /* max_low_pfn                  */
-#include <linux/kprobes.h>             /* __kprobes, ...               */
+#include <linux/kprobes.h>             /* NOKPROBE_SYMBOL, ...         */
 #include <linux/mmiotrace.h>           /* kmmio_handler, ...           */
 #include <linux/perf_event.h>          /* perf_sw_event                */
 #include <linux/hugetlb.h>             /* hstate_index_to_shift        */
@@ -46,7 +46,7 @@ enum x86_pf_error_code {
  * Returns 0 if mmiotrace is disabled, or if the fault is not
  * handled by mmiotrace:
  */
-static inline int __kprobes
+static nokprobe_inline int
 kmmio_fault(struct pt_regs *regs, unsigned long addr)
 {
        if (unlikely(is_kmmio_active()))
@@ -55,7 +55,7 @@ kmmio_fault(struct pt_regs *regs, unsigned long addr)
        return 0;
 }
 
-static inline int __kprobes kprobes_fault(struct pt_regs *regs)
+static nokprobe_inline int kprobes_fault(struct pt_regs *regs)
 {
        int ret = 0;
 
@@ -262,7 +262,7 @@ void vmalloc_sync_all(void)
  *
  *   Handle a fault on the vmalloc or module mapping area
  */
-static noinline __kprobes int vmalloc_fault(unsigned long address)
+static noinline int vmalloc_fault(unsigned long address)
 {
        unsigned long pgd_paddr;
        pmd_t *pmd_k;
@@ -292,6 +292,7 @@ static noinline __kprobes int vmalloc_fault(unsigned long address)
 
        return 0;
 }
+NOKPROBE_SYMBOL(vmalloc_fault);
 
 /*
  * Did it hit the DOS screen memory VA from vm86 mode?
@@ -359,7 +360,7 @@ void vmalloc_sync_all(void)
  *
  * This assumes no large pages in there.
  */
-static noinline __kprobes int vmalloc_fault(unsigned long address)
+static noinline int vmalloc_fault(unsigned long address)
 {
        pgd_t *pgd, *pgd_ref;
        pud_t *pud, *pud_ref;
@@ -426,6 +427,7 @@ static noinline __kprobes int vmalloc_fault(unsigned long address)
 
        return 0;
 }
+NOKPROBE_SYMBOL(vmalloc_fault);
 
 #ifdef CONFIG_CPU_SUP_AMD
 static const char errata93_warning[] =
@@ -928,7 +930,7 @@ static int spurious_fault_check(unsigned long error_code, pte_t *pte)
  * There are no security implications to leaving a stale TLB when
  * increasing the permissions on a page.
  */
-static noinline __kprobes int
+static noinline int
 spurious_fault(unsigned long error_code, unsigned long address)
 {
        pgd_t *pgd;
@@ -976,6 +978,7 @@ spurious_fault(unsigned long error_code, unsigned long address)
 
        return ret;
 }
+NOKPROBE_SYMBOL(spurious_fault);
 
 int show_unhandled_signals = 1;
 
@@ -1031,7 +1034,7 @@ static inline bool smap_violation(int error_code, struct pt_regs *regs)
  * {,trace_}do_page_fault() have notrace on. Having this an actual function
  * guarantees there's a function trace entry.
  */
-static void __kprobes noinline
+static noinline void
 __do_page_fault(struct pt_regs *regs, unsigned long error_code,
                unsigned long address)
 {
@@ -1254,8 +1257,9 @@ good_area:
 
        up_read(&mm->mmap_sem);
 }
+NOKPROBE_SYMBOL(__do_page_fault);
 
-dotraplinkage void __kprobes notrace
+dotraplinkage void notrace
 do_page_fault(struct pt_regs *regs, unsigned long error_code)
 {
        unsigned long address = read_cr2(); /* Get the faulting address */
@@ -1273,10 +1277,12 @@ do_page_fault(struct pt_regs *regs, unsigned long error_code)
        __do_page_fault(regs, error_code, address);
        exception_exit(prev_state);
 }
+NOKPROBE_SYMBOL(do_page_fault);
 
 #ifdef CONFIG_TRACING
-static void trace_page_fault_entries(unsigned long address, struct pt_regs *regs,
-                                    unsigned long error_code)
+static nokprobe_inline void
+trace_page_fault_entries(unsigned long address, struct pt_regs *regs,
+                        unsigned long error_code)
 {
        if (user_mode(regs))
                trace_page_fault_user(address, regs, error_code);
@@ -1284,7 +1290,7 @@ static void trace_page_fault_entries(unsigned long address, struct pt_regs *regs
                trace_page_fault_kernel(address, regs, error_code);
 }
 
-dotraplinkage void __kprobes notrace
+dotraplinkage void notrace
 trace_do_page_fault(struct pt_regs *regs, unsigned long error_code)
 {
        /*
@@ -1301,4 +1307,5 @@ trace_do_page_fault(struct pt_regs *regs, unsigned long error_code)
        __do_page_fault(regs, error_code, address);
        exception_exit(prev_state);
 }
+NOKPROBE_SYMBOL(trace_do_page_fault);
 #endif /* CONFIG_TRACING */
index 01495755701bd3d068db95df291ef71096d9cf33..6440221ced0d4925d3fee4a0c11b424a6b696f2d 100644 (file)
 
 /*
  * Calling convention :
- * rdi : skb pointer
+ * rbx : skb pointer (callee saved)
  * esi : offset of byte(s) to fetch in skb (can be scratched)
- * r : copy of skb->data
+ * r10 : copy of skb->data
  * r9d : hlen = skb->len - skb->data_len
  */
-#define SKBDATA        %r8
+#define SKBDATA        %r10
 #define SKF_MAX_NEG_OFF    $(-0x200000) /* SKF_LL_OFF from filter.h */
+#define MAX_BPF_STACK (512 /* from filter.h */ + \
+       32 /* space for rbx,r13,r14,r15 */ + \
+       8 /* space for skb_copy_bits */)
 
 sk_load_word:
        .globl  sk_load_word
@@ -68,53 +71,31 @@ sk_load_byte_positive_offset:
        movzbl  (SKBDATA,%rsi),%eax
        ret
 
-/**
- * sk_load_byte_msh - BPF_S_LDX_B_MSH helper
- *
- * Implements BPF_S_LDX_B_MSH : ldxb  4*([offset]&0xf)
- * Must preserve A accumulator (%eax)
- * Inputs : %esi is the offset value
- */
-sk_load_byte_msh:
-       .globl  sk_load_byte_msh
-       test    %esi,%esi
-       js      bpf_slow_path_byte_msh_neg
-
-sk_load_byte_msh_positive_offset:
-       .globl  sk_load_byte_msh_positive_offset
-       cmp     %esi,%r9d      /* if (offset >= hlen) goto bpf_slow_path_byte_msh */
-       jle     bpf_slow_path_byte_msh
-       movzbl  (SKBDATA,%rsi),%ebx
-       and     $15,%bl
-       shl     $2,%bl
-       ret
-
 /* rsi contains offset and can be scratched */
 #define bpf_slow_path_common(LEN)              \
-       push    %rdi;    /* save skb */         \
+       mov     %rbx, %rdi; /* arg1 == skb */   \
        push    %r9;                            \
        push    SKBDATA;                        \
 /* rsi already has offset */                   \
        mov     $LEN,%ecx;      /* len */       \
-       lea     -12(%rbp),%rdx;                 \
+       lea     - MAX_BPF_STACK + 32(%rbp),%rdx;                        \
        call    skb_copy_bits;                  \
        test    %eax,%eax;                      \
        pop     SKBDATA;                        \
-       pop     %r9;                            \
-       pop     %rdi
+       pop     %r9;
 
 
 bpf_slow_path_word:
        bpf_slow_path_common(4)
        js      bpf_error
-       mov     -12(%rbp),%eax
+       mov     - MAX_BPF_STACK + 32(%rbp),%eax
        bswap   %eax
        ret
 
 bpf_slow_path_half:
        bpf_slow_path_common(2)
        js      bpf_error
-       mov     -12(%rbp),%ax
+       mov     - MAX_BPF_STACK + 32(%rbp),%ax
        rol     $8,%ax
        movzwl  %ax,%eax
        ret
@@ -122,21 +103,11 @@ bpf_slow_path_half:
 bpf_slow_path_byte:
        bpf_slow_path_common(1)
        js      bpf_error
-       movzbl  -12(%rbp),%eax
-       ret
-
-bpf_slow_path_byte_msh:
-       xchg    %eax,%ebx /* dont lose A , X is about to be scratched */
-       bpf_slow_path_common(1)
-       js      bpf_error
-       movzbl  -12(%rbp),%eax
-       and     $15,%al
-       shl     $2,%al
-       xchg    %eax,%ebx
+       movzbl  - MAX_BPF_STACK + 32(%rbp),%eax
        ret
 
 #define sk_negative_common(SIZE)                               \
-       push    %rdi;   /* save skb */                          \
+       mov     %rbx, %rdi; /* arg1 == skb */                   \
        push    %r9;                                            \
        push    SKBDATA;                                        \
 /* rsi already has offset */                                   \
@@ -145,10 +116,8 @@ bpf_slow_path_byte_msh:
        test    %rax,%rax;                                      \
        pop     SKBDATA;                                        \
        pop     %r9;                                            \
-       pop     %rdi;                                           \
        jz      bpf_error
 
-
 bpf_slow_path_word_neg:
        cmp     SKF_MAX_NEG_OFF, %esi   /* test range */
        jl      bpf_error       /* offset lower -> error  */
@@ -179,22 +148,12 @@ sk_load_byte_negative_offset:
        movzbl  (%rax), %eax
        ret
 
-bpf_slow_path_byte_msh_neg:
-       cmp     SKF_MAX_NEG_OFF, %esi
-       jl      bpf_error
-sk_load_byte_msh_negative_offset:
-       .globl  sk_load_byte_msh_negative_offset
-       xchg    %eax,%ebx /* dont lose A , X is about to be scratched */
-       sk_negative_common(1)
-       movzbl  (%rax),%eax
-       and     $15,%al
-       shl     $2,%al
-       xchg    %eax,%ebx
-       ret
-
 bpf_error:
 # force a return 0 from jit handler
-       xor             %eax,%eax
-       mov             -8(%rbp),%rbx
+       xor     %eax,%eax
+       mov     - MAX_BPF_STACK(%rbp),%rbx
+       mov     - MAX_BPF_STACK + 8(%rbp),%r13
+       mov     - MAX_BPF_STACK + 16(%rbp),%r14
+       mov     - MAX_BPF_STACK + 24(%rbp),%r15
        leaveq
        ret
index 6d5663a599a7a362756b5bcb6d3ff3e30ee34bae..99bef86ed6dffe48490cf49a383c19ff8ad72287 100644 (file)
@@ -1,6 +1,7 @@
 /* bpf_jit_comp.c : BPF JIT compiler
  *
  * Copyright (C) 2011-2013 Eric Dumazet (eric.dumazet@gmail.com)
+ * Internal BPF Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
 #include <linux/if_vlan.h>
 #include <linux/random.h>
 
-/*
- * Conventions :
- *  EAX : BPF A accumulator
- *  EBX : BPF X accumulator
- *  RDI : pointer to skb   (first argument given to JIT function)
- *  RBP : frame pointer (even if CONFIG_FRAME_POINTER=n)
- *  ECX,EDX,ESI : scratch registers
- *  r9d : skb->len - skb->data_len (headlen)
- *  r8  : skb->data
- * -8(RBP) : saved RBX value
- * -16(RBP)..-80(RBP) : BPF_MEMWORDS values
- */
 int bpf_jit_enable __read_mostly;
 
 /*
  * assembly code in arch/x86/net/bpf_jit.S
  */
-extern u8 sk_load_word[], sk_load_half[], sk_load_byte[], sk_load_byte_msh[];
+extern u8 sk_load_word[], sk_load_half[], sk_load_byte[];
 extern u8 sk_load_word_positive_offset[], sk_load_half_positive_offset[];
-extern u8 sk_load_byte_positive_offset[], sk_load_byte_msh_positive_offset[];
+extern u8 sk_load_byte_positive_offset[];
 extern u8 sk_load_word_negative_offset[], sk_load_half_negative_offset[];
-extern u8 sk_load_byte_negative_offset[], sk_load_byte_msh_negative_offset[];
+extern u8 sk_load_byte_negative_offset[];
 
 static inline u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len)
 {
@@ -56,30 +45,44 @@ static inline u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len)
 #define EMIT2(b1, b2)          EMIT((b1) + ((b2) << 8), 2)
 #define EMIT3(b1, b2, b3)      EMIT((b1) + ((b2) << 8) + ((b3) << 16), 3)
 #define EMIT4(b1, b2, b3, b4)   EMIT((b1) + ((b2) << 8) + ((b3) << 16) + ((b4) << 24), 4)
-#define EMIT1_off32(b1, off)   do { EMIT1(b1); EMIT(off, 4);} while (0)
-
-#define CLEAR_A() EMIT2(0x31, 0xc0) /* xor %eax,%eax */
-#define CLEAR_X() EMIT2(0x31, 0xdb) /* xor %ebx,%ebx */
+#define EMIT1_off32(b1, off) \
+       do {EMIT1(b1); EMIT(off, 4); } while (0)
+#define EMIT2_off32(b1, b2, off) \
+       do {EMIT2(b1, b2); EMIT(off, 4); } while (0)
+#define EMIT3_off32(b1, b2, b3, off) \
+       do {EMIT3(b1, b2, b3); EMIT(off, 4); } while (0)
+#define EMIT4_off32(b1, b2, b3, b4, off) \
+       do {EMIT4(b1, b2, b3, b4); EMIT(off, 4); } while (0)
 
 static inline bool is_imm8(int value)
 {
        return value <= 127 && value >= -128;
 }
 
-static inline bool is_near(int offset)
+static inline bool is_simm32(s64 value)
 {
-       return offset <= 127 && offset >= -128;
+       return value == (s64) (s32) value;
 }
 
-#define EMIT_JMP(offset)                                               \
-do {                                                                   \
-       if (offset) {                                                   \
-               if (is_near(offset))                                    \
-                       EMIT2(0xeb, offset); /* jmp .+off8 */           \
-               else                                                    \
-                       EMIT1_off32(0xe9, offset); /* jmp .+off32 */    \
-       }                                                               \
-} while (0)
+/* mov dst, src */
+#define EMIT_mov(DST, SRC) \
+       do {if (DST != SRC) \
+               EMIT3(add_2mod(0x48, DST, SRC), 0x89, add_2reg(0xC0, DST, SRC)); \
+       } while (0)
+
+static int bpf_size_to_x86_bytes(int bpf_size)
+{
+       if (bpf_size == BPF_W)
+               return 4;
+       else if (bpf_size == BPF_H)
+               return 2;
+       else if (bpf_size == BPF_B)
+               return 1;
+       else if (bpf_size == BPF_DW)
+               return 4; /* imm32 */
+       else
+               return 0;
+}
 
 /* list of x86 cond jumps opcodes (. + s8)
  * Add 0x10 (and an extra 0x0f) to generate far jumps (. + s32)
@@ -90,27 +93,8 @@ do {                                                                 \
 #define X86_JNE 0x75
 #define X86_JBE 0x76
 #define X86_JA  0x77
-
-#define EMIT_COND_JMP(op, offset)                              \
-do {                                                           \
-       if (is_near(offset))                                    \
-               EMIT2(op, offset); /* jxx .+off8 */             \
-       else {                                                  \
-               EMIT2(0x0f, op + 0x10);                         \
-               EMIT(offset, 4); /* jxx .+off32 */              \
-       }                                                       \
-} while (0)
-
-#define COND_SEL(CODE, TOP, FOP)       \
-       case CODE:                      \
-               t_op = TOP;             \
-               f_op = FOP;             \
-               goto cond_branch
-
-
-#define SEEN_DATAREF 1 /* might call external helpers */
-#define SEEN_XREG    2 /* ebx is used */
-#define SEEN_MEM     4 /* use mem[] for temporary storage */
+#define X86_JGE 0x7D
+#define X86_JG  0x7F
 
 static inline void bpf_flush_icache(void *start, void *end)
 {
@@ -125,26 +109,6 @@ static inline void bpf_flush_icache(void *start, void *end)
 #define CHOOSE_LOAD_FUNC(K, func) \
        ((int)K < 0 ? ((int)K >= SKF_LL_OFF ? func##_negative_offset : func) : func##_positive_offset)
 
-/* Helper to find the offset of pkt_type in sk_buff
- * We want to make sure its still a 3bit field starting at a byte boundary.
- */
-#define PKT_TYPE_MAX 7
-static int pkt_type_offset(void)
-{
-       struct sk_buff skb_probe = {
-               .pkt_type = ~0,
-       };
-       char *ct = (char *)&skb_probe;
-       unsigned int off;
-
-       for (off = 0; off < sizeof(struct sk_buff); off++) {
-               if (ct[off] == PKT_TYPE_MAX)
-                       return off;
-       }
-       pr_err_once("Please fix pkt_type_offset(), as pkt_type couldn't be found\n");
-       return -1;
-}
-
 struct bpf_binary_header {
        unsigned int    pages;
        /* Note : for security reasons, bpf code will follow a randomly
@@ -178,583 +142,771 @@ static struct bpf_binary_header *bpf_alloc_binary(unsigned int proglen,
        return header;
 }
 
-void bpf_jit_compile(struct sk_filter *fp)
+/* pick a register outside of BPF range for JIT internal work */
+#define AUX_REG (MAX_BPF_REG + 1)
+
+/* the following table maps BPF registers to x64 registers.
+ * x64 register r12 is unused, since if used as base address register
+ * in load/store instructions, it always needs an extra byte of encoding
+ */
+static const int reg2hex[] = {
+       [BPF_REG_0] = 0,  /* rax */
+       [BPF_REG_1] = 7,  /* rdi */
+       [BPF_REG_2] = 6,  /* rsi */
+       [BPF_REG_3] = 2,  /* rdx */
+       [BPF_REG_4] = 1,  /* rcx */
+       [BPF_REG_5] = 0,  /* r8 */
+       [BPF_REG_6] = 3,  /* rbx callee saved */
+       [BPF_REG_7] = 5,  /* r13 callee saved */
+       [BPF_REG_8] = 6,  /* r14 callee saved */
+       [BPF_REG_9] = 7,  /* r15 callee saved */
+       [BPF_REG_FP] = 5, /* rbp readonly */
+       [AUX_REG] = 3,    /* r11 temp register */
+};
+
+/* is_ereg() == true if BPF register 'reg' maps to x64 r8..r15
+ * which need extra byte of encoding.
+ * rax,rcx,...,rbp have simpler encoding
+ */
+static inline bool is_ereg(u32 reg)
 {
-       u8 temp[64];
-       u8 *prog;
-       unsigned int proglen, oldproglen = 0;
-       int ilen, i;
-       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 */
-       unsigned int *addrs;
-       const struct sock_filter *filter = fp->insns;
-       int flen = fp->len;
+       if (reg == BPF_REG_5 || reg == AUX_REG ||
+           (reg >= BPF_REG_7 && reg <= BPF_REG_9))
+               return true;
+       else
+               return false;
+}
 
-       if (!bpf_jit_enable)
-               return;
+/* add modifiers if 'reg' maps to x64 registers r8..r15 */
+static inline u8 add_1mod(u8 byte, u32 reg)
+{
+       if (is_ereg(reg))
+               byte |= 1;
+       return byte;
+}
 
-       addrs = kmalloc(flen * sizeof(*addrs), GFP_KERNEL);
-       if (addrs == NULL)
-               return;
+static inline u8 add_2mod(u8 byte, u32 r1, u32 r2)
+{
+       if (is_ereg(r1))
+               byte |= 1;
+       if (is_ereg(r2))
+               byte |= 4;
+       return byte;
+}
 
-       /* Before first pass, make a rough estimation of addrs[]
-        * each bpf instruction is translated to less than 64 bytes
+/* encode 'dst_reg' register into x64 opcode 'byte' */
+static inline u8 add_1reg(u8 byte, u32 dst_reg)
+{
+       return byte + reg2hex[dst_reg];
+}
+
+/* encode 'dst_reg' and 'src_reg' registers into x64 opcode 'byte' */
+static inline u8 add_2reg(u8 byte, u32 dst_reg, u32 src_reg)
+{
+       return byte + reg2hex[dst_reg] + (reg2hex[src_reg] << 3);
+}
+
+struct jit_context {
+       unsigned int cleanup_addr; /* epilogue code offset */
+       bool seen_ld_abs;
+};
+
+static int do_jit(struct sk_filter *bpf_prog, int *addrs, u8 *image,
+                 int oldproglen, struct jit_context *ctx)
+{
+       struct sock_filter_int *insn = bpf_prog->insnsi;
+       int insn_cnt = bpf_prog->len;
+       u8 temp[64];
+       int i;
+       int proglen = 0;
+       u8 *prog = temp;
+       int stacksize = MAX_BPF_STACK +
+               32 /* space for rbx, r13, r14, r15 */ +
+               8 /* space for skb_copy_bits() buffer */;
+
+       EMIT1(0x55); /* push rbp */
+       EMIT3(0x48, 0x89, 0xE5); /* mov rbp,rsp */
+
+       /* sub rsp, stacksize */
+       EMIT3_off32(0x48, 0x81, 0xEC, stacksize);
+
+       /* all classic BPF filters use R6(rbx) save it */
+
+       /* mov qword ptr [rbp-X],rbx */
+       EMIT3_off32(0x48, 0x89, 0x9D, -stacksize);
+
+       /* sk_convert_filter() maps classic BPF register X to R7 and uses R8
+        * as temporary, so all tcpdump filters need to spill/fill R7(r13) and
+        * R8(r14). R9(r15) spill could be made conditional, but there is only
+        * one 'bpf_error' return path out of helper functions inside bpf_jit.S
+        * The overhead of extra spill is negligible for any filter other
+        * than synthetic ones. Therefore not worth adding complexity.
         */
-       for (proglen = 0, i = 0; i < flen; i++) {
-               proglen += 64;
-               addrs[i] = proglen;
+
+       /* mov qword ptr [rbp-X],r13 */
+       EMIT3_off32(0x4C, 0x89, 0xAD, -stacksize + 8);
+       /* mov qword ptr [rbp-X],r14 */
+       EMIT3_off32(0x4C, 0x89, 0xB5, -stacksize + 16);
+       /* mov qword ptr [rbp-X],r15 */
+       EMIT3_off32(0x4C, 0x89, 0xBD, -stacksize + 24);
+
+       /* clear A and X registers */
+       EMIT2(0x31, 0xc0); /* xor eax, eax */
+       EMIT3(0x4D, 0x31, 0xED); /* xor r13, r13 */
+
+       if (ctx->seen_ld_abs) {
+               /* r9d : skb->len - skb->data_len (headlen)
+                * r10 : skb->data
+                */
+               if (is_imm8(offsetof(struct sk_buff, len)))
+                       /* mov %r9d, off8(%rdi) */
+                       EMIT4(0x44, 0x8b, 0x4f,
+                             offsetof(struct sk_buff, len));
+               else
+                       /* mov %r9d, off32(%rdi) */
+                       EMIT3_off32(0x44, 0x8b, 0x8f,
+                                   offsetof(struct sk_buff, len));
+
+               if (is_imm8(offsetof(struct sk_buff, data_len)))
+                       /* sub %r9d, off8(%rdi) */
+                       EMIT4(0x44, 0x2b, 0x4f,
+                             offsetof(struct sk_buff, data_len));
+               else
+                       EMIT3_off32(0x44, 0x2b, 0x8f,
+                                   offsetof(struct sk_buff, data_len));
+
+               if (is_imm8(offsetof(struct sk_buff, data)))
+                       /* mov %r10, off8(%rdi) */
+                       EMIT4(0x4c, 0x8b, 0x57,
+                             offsetof(struct sk_buff, data));
+               else
+                       /* mov %r10, off32(%rdi) */
+                       EMIT3_off32(0x4c, 0x8b, 0x97,
+                                   offsetof(struct sk_buff, data));
        }
-       cleanup_addr = proglen; /* epilogue address */
 
-       for (pass = 0; pass < 10; pass++) {
-               u8 seen_or_pass0 = (pass == 0) ? (SEEN_XREG | SEEN_DATAREF | SEEN_MEM) : seen;
-               /* no prologue/epilogue for trivial filters (RET something) */
-               proglen = 0;
-               prog = temp;
+       for (i = 0; i < insn_cnt; i++, insn++) {
+               const s32 imm32 = insn->imm;
+               u32 dst_reg = insn->dst_reg;
+               u32 src_reg = insn->src_reg;
+               u8 b1 = 0, b2 = 0, b3 = 0;
+               s64 jmp_offset;
+               u8 jmp_cond;
+               int ilen;
+               u8 *func;
+
+               switch (insn->code) {
+                       /* ALU */
+               case BPF_ALU | BPF_ADD | BPF_X:
+               case BPF_ALU | BPF_SUB | BPF_X:
+               case BPF_ALU | BPF_AND | BPF_X:
+               case BPF_ALU | BPF_OR | BPF_X:
+               case BPF_ALU | BPF_XOR | BPF_X:
+               case BPF_ALU64 | BPF_ADD | BPF_X:
+               case BPF_ALU64 | BPF_SUB | BPF_X:
+               case BPF_ALU64 | BPF_AND | BPF_X:
+               case BPF_ALU64 | BPF_OR | BPF_X:
+               case BPF_ALU64 | BPF_XOR | BPF_X:
+                       switch (BPF_OP(insn->code)) {
+                       case BPF_ADD: b2 = 0x01; break;
+                       case BPF_SUB: b2 = 0x29; break;
+                       case BPF_AND: b2 = 0x21; break;
+                       case BPF_OR: b2 = 0x09; break;
+                       case BPF_XOR: b2 = 0x31; break;
+                       }
+                       if (BPF_CLASS(insn->code) == BPF_ALU64)
+                               EMIT1(add_2mod(0x48, dst_reg, src_reg));
+                       else if (is_ereg(dst_reg) || is_ereg(src_reg))
+                               EMIT1(add_2mod(0x40, dst_reg, src_reg));
+                       EMIT2(b2, add_2reg(0xC0, dst_reg, src_reg));
+                       break;
 
-               if (seen_or_pass0) {
-                       EMIT4(0x55, 0x48, 0x89, 0xe5); /* push %rbp; mov %rsp,%rbp */
-                       EMIT4(0x48, 0x83, 0xec, 96);    /* subq  $96,%rsp       */
-                       /* note : must save %rbx in case bpf_error is hit */
-                       if (seen_or_pass0 & (SEEN_XREG | SEEN_DATAREF))
-                               EMIT4(0x48, 0x89, 0x5d, 0xf8); /* mov %rbx, -8(%rbp) */
-                       if (seen_or_pass0 & SEEN_XREG)
-                               CLEAR_X(); /* make sure we dont leek kernel memory */
-
-                       /*
-                        * If this filter needs to access skb data,
-                        * loads r9 and r8 with :
-                        *  r9 = skb->len - skb->data_len
-                        *  r8 = skb->data
+                       /* mov dst, src */
+               case BPF_ALU64 | BPF_MOV | BPF_X:
+                       EMIT_mov(dst_reg, src_reg);
+                       break;
+
+                       /* mov32 dst, src */
+               case BPF_ALU | BPF_MOV | BPF_X:
+                       if (is_ereg(dst_reg) || is_ereg(src_reg))
+                               EMIT1(add_2mod(0x40, dst_reg, src_reg));
+                       EMIT2(0x89, add_2reg(0xC0, dst_reg, src_reg));
+                       break;
+
+                       /* neg dst */
+               case BPF_ALU | BPF_NEG:
+               case BPF_ALU64 | BPF_NEG:
+                       if (BPF_CLASS(insn->code) == BPF_ALU64)
+                               EMIT1(add_1mod(0x48, dst_reg));
+                       else if (is_ereg(dst_reg))
+                               EMIT1(add_1mod(0x40, dst_reg));
+                       EMIT2(0xF7, add_1reg(0xD8, dst_reg));
+                       break;
+
+               case BPF_ALU | BPF_ADD | BPF_K:
+               case BPF_ALU | BPF_SUB | BPF_K:
+               case BPF_ALU | BPF_AND | BPF_K:
+               case BPF_ALU | BPF_OR | BPF_K:
+               case BPF_ALU | BPF_XOR | BPF_K:
+               case BPF_ALU64 | BPF_ADD | BPF_K:
+               case BPF_ALU64 | BPF_SUB | BPF_K:
+               case BPF_ALU64 | BPF_AND | BPF_K:
+               case BPF_ALU64 | BPF_OR | BPF_K:
+               case BPF_ALU64 | BPF_XOR | BPF_K:
+                       if (BPF_CLASS(insn->code) == BPF_ALU64)
+                               EMIT1(add_1mod(0x48, dst_reg));
+                       else if (is_ereg(dst_reg))
+                               EMIT1(add_1mod(0x40, dst_reg));
+
+                       switch (BPF_OP(insn->code)) {
+                       case BPF_ADD: b3 = 0xC0; break;
+                       case BPF_SUB: b3 = 0xE8; break;
+                       case BPF_AND: b3 = 0xE0; break;
+                       case BPF_OR: b3 = 0xC8; break;
+                       case BPF_XOR: b3 = 0xF0; break;
+                       }
+
+                       if (is_imm8(imm32))
+                               EMIT3(0x83, add_1reg(b3, dst_reg), imm32);
+                       else
+                               EMIT2_off32(0x81, add_1reg(b3, dst_reg), imm32);
+                       break;
+
+               case BPF_ALU64 | BPF_MOV | BPF_K:
+                       /* optimization: if imm32 is positive,
+                        * use 'mov eax, imm32' (which zero-extends imm32)
+                        * to save 2 bytes
                         */
-                       if (seen_or_pass0 & SEEN_DATAREF) {
-                               if (offsetof(struct sk_buff, len) <= 127)
-                                       /* mov    off8(%rdi),%r9d */
-                                       EMIT4(0x44, 0x8b, 0x4f, offsetof(struct sk_buff, len));
-                               else {
-                                       /* mov    off32(%rdi),%r9d */
-                                       EMIT3(0x44, 0x8b, 0x8f);
-                                       EMIT(offsetof(struct sk_buff, len), 4);
-                               }
-                               if (is_imm8(offsetof(struct sk_buff, data_len)))
-                                       /* sub    off8(%rdi),%r9d */
-                                       EMIT4(0x44, 0x2b, 0x4f, offsetof(struct sk_buff, data_len));
-                               else {
-                                       EMIT3(0x44, 0x2b, 0x8f);
-                                       EMIT(offsetof(struct sk_buff, data_len), 4);
-                               }
+                       if (imm32 < 0) {
+                               /* 'mov rax, imm32' sign extends imm32 */
+                               b1 = add_1mod(0x48, dst_reg);
+                               b2 = 0xC7;
+                               b3 = 0xC0;
+                               EMIT3_off32(b1, b2, add_1reg(b3, dst_reg), imm32);
+                               break;
+                       }
 
-                               if (is_imm8(offsetof(struct sk_buff, data)))
-                                       /* mov off8(%rdi),%r8 */
-                                       EMIT4(0x4c, 0x8b, 0x47, offsetof(struct sk_buff, data));
-                               else {
-                                       /* mov off32(%rdi),%r8 */
-                                       EMIT3(0x4c, 0x8b, 0x87);
-                                       EMIT(offsetof(struct sk_buff, data), 4);
-                               }
+               case BPF_ALU | BPF_MOV | BPF_K:
+                       /* mov %eax, imm32 */
+                       if (is_ereg(dst_reg))
+                               EMIT1(add_1mod(0x40, dst_reg));
+                       EMIT1_off32(add_1reg(0xB8, dst_reg), imm32);
+                       break;
+
+                       /* dst %= src, dst /= src, dst %= imm32, dst /= imm32 */
+               case BPF_ALU | BPF_MOD | BPF_X:
+               case BPF_ALU | BPF_DIV | BPF_X:
+               case BPF_ALU | BPF_MOD | BPF_K:
+               case BPF_ALU | BPF_DIV | BPF_K:
+               case BPF_ALU64 | BPF_MOD | BPF_X:
+               case BPF_ALU64 | BPF_DIV | BPF_X:
+               case BPF_ALU64 | BPF_MOD | BPF_K:
+               case BPF_ALU64 | BPF_DIV | BPF_K:
+                       EMIT1(0x50); /* push rax */
+                       EMIT1(0x52); /* push rdx */
+
+                       if (BPF_SRC(insn->code) == BPF_X)
+                               /* mov r11, src_reg */
+                               EMIT_mov(AUX_REG, src_reg);
+                       else
+                               /* mov r11, imm32 */
+                               EMIT3_off32(0x49, 0xC7, 0xC3, imm32);
+
+                       /* mov rax, dst_reg */
+                       EMIT_mov(BPF_REG_0, dst_reg);
+
+                       /* xor edx, edx
+                        * equivalent to 'xor rdx, rdx', but one byte less
+                        */
+                       EMIT2(0x31, 0xd2);
+
+                       if (BPF_SRC(insn->code) == BPF_X) {
+                               /* if (src_reg == 0) return 0 */
+
+                               /* cmp r11, 0 */
+                               EMIT4(0x49, 0x83, 0xFB, 0x00);
+
+                               /* jne .+9 (skip over pop, pop, xor and jmp) */
+                               EMIT2(X86_JNE, 1 + 1 + 2 + 5);
+                               EMIT1(0x5A); /* pop rdx */
+                               EMIT1(0x58); /* pop rax */
+                               EMIT2(0x31, 0xc0); /* xor eax, eax */
+
+                               /* jmp cleanup_addr
+                                * addrs[i] - 11, because there are 11 bytes
+                                * after this insn: div, mov, pop, pop, mov
+                                */
+                               jmp_offset = ctx->cleanup_addr - (addrs[i] - 11);
+                               EMIT1_off32(0xE9, jmp_offset);
                        }
-               }
 
-               switch (filter[0].code) {
-               case BPF_S_RET_K:
-               case BPF_S_LD_W_LEN:
-               case BPF_S_ANC_PROTOCOL:
-               case BPF_S_ANC_IFINDEX:
-               case BPF_S_ANC_MARK:
-               case BPF_S_ANC_RXHASH:
-               case BPF_S_ANC_CPU:
-               case BPF_S_ANC_VLAN_TAG:
-               case BPF_S_ANC_VLAN_TAG_PRESENT:
-               case BPF_S_ANC_QUEUE:
-               case BPF_S_ANC_PKTTYPE:
-               case BPF_S_LD_W_ABS:
-               case BPF_S_LD_H_ABS:
-               case BPF_S_LD_B_ABS:
-                       /* first instruction sets A register (or is RET 'constant') */
+                       if (BPF_CLASS(insn->code) == BPF_ALU64)
+                               /* div r11 */
+                               EMIT3(0x49, 0xF7, 0xF3);
+                       else
+                               /* div r11d */
+                               EMIT3(0x41, 0xF7, 0xF3);
+
+                       if (BPF_OP(insn->code) == BPF_MOD)
+                               /* mov r11, rdx */
+                               EMIT3(0x49, 0x89, 0xD3);
+                       else
+                               /* mov r11, rax */
+                               EMIT3(0x49, 0x89, 0xC3);
+
+                       EMIT1(0x5A); /* pop rdx */
+                       EMIT1(0x58); /* pop rax */
+
+                       /* mov dst_reg, r11 */
+                       EMIT_mov(dst_reg, AUX_REG);
                        break;
-               default:
-                       /* make sure we dont leak kernel information to user */
-                       CLEAR_A(); /* A = 0 */
-               }
 
-               for (i = 0; i < flen; i++) {
-                       unsigned int K = filter[i].k;
+               case BPF_ALU | BPF_MUL | BPF_K:
+               case BPF_ALU | BPF_MUL | BPF_X:
+               case BPF_ALU64 | BPF_MUL | BPF_K:
+               case BPF_ALU64 | BPF_MUL | BPF_X:
+                       EMIT1(0x50); /* push rax */
+                       EMIT1(0x52); /* push rdx */
+
+                       /* mov r11, dst_reg */
+                       EMIT_mov(AUX_REG, dst_reg);
+
+                       if (BPF_SRC(insn->code) == BPF_X)
+                               /* mov rax, src_reg */
+                               EMIT_mov(BPF_REG_0, src_reg);
+                       else
+                               /* mov rax, imm32 */
+                               EMIT3_off32(0x48, 0xC7, 0xC0, imm32);
+
+                       if (BPF_CLASS(insn->code) == BPF_ALU64)
+                               EMIT1(add_1mod(0x48, AUX_REG));
+                       else if (is_ereg(AUX_REG))
+                               EMIT1(add_1mod(0x40, AUX_REG));
+                       /* mul(q) r11 */
+                       EMIT2(0xF7, add_1reg(0xE0, AUX_REG));
+
+                       /* mov r11, rax */
+                       EMIT_mov(AUX_REG, BPF_REG_0);
+
+                       EMIT1(0x5A); /* pop rdx */
+                       EMIT1(0x58); /* pop rax */
+
+                       /* mov dst_reg, r11 */
+                       EMIT_mov(dst_reg, AUX_REG);
+                       break;
 
-                       switch (filter[i].code) {
-                       case BPF_S_ALU_ADD_X: /* A += X; */
-                               seen |= SEEN_XREG;
-                               EMIT2(0x01, 0xd8);              /* add %ebx,%eax */
-                               break;
-                       case BPF_S_ALU_ADD_K: /* A += K; */
-                               if (!K)
-                                       break;
-                               if (is_imm8(K))
-                                       EMIT3(0x83, 0xc0, K);   /* add imm8,%eax */
-                               else
-                                       EMIT1_off32(0x05, K);   /* add imm32,%eax */
-                               break;
-                       case BPF_S_ALU_SUB_X: /* A -= X; */
-                               seen |= SEEN_XREG;
-                               EMIT2(0x29, 0xd8);              /* sub    %ebx,%eax */
-                               break;
-                       case BPF_S_ALU_SUB_K: /* A -= K */
-                               if (!K)
-                                       break;
-                               if (is_imm8(K))
-                                       EMIT3(0x83, 0xe8, K); /* sub imm8,%eax */
-                               else
-                                       EMIT1_off32(0x2d, K); /* sub imm32,%eax */
-                               break;
-                       case BPF_S_ALU_MUL_X: /* A *= X; */
-                               seen |= SEEN_XREG;
-                               EMIT3(0x0f, 0xaf, 0xc3);        /* imul %ebx,%eax */
-                               break;
-                       case BPF_S_ALU_MUL_K: /* A *= K */
-                               if (is_imm8(K))
-                                       EMIT3(0x6b, 0xc0, K); /* imul imm8,%eax,%eax */
-                               else {
-                                       EMIT2(0x69, 0xc0);              /* imul imm32,%eax */
-                                       EMIT(K, 4);
-                               }
-                               break;
-                       case BPF_S_ALU_DIV_X: /* A /= X; */
-                               seen |= SEEN_XREG;
-                               EMIT2(0x85, 0xdb);      /* test %ebx,%ebx */
-                               if (pc_ret0 > 0) {
-                                       /* addrs[pc_ret0 - 1] is start address of target
-                                        * (addrs[i] - 4) is the address following this jmp
-                                        * ("xor %edx,%edx; div %ebx" being 4 bytes long)
-                                        */
-                                       EMIT_COND_JMP(X86_JE, addrs[pc_ret0 - 1] -
-                                                               (addrs[i] - 4));
-                               } else {
-                                       EMIT_COND_JMP(X86_JNE, 2 + 5);
-                                       CLEAR_A();
-                                       EMIT1_off32(0xe9, cleanup_addr - (addrs[i] - 4)); /* jmp .+off32 */
-                               }
-                               EMIT4(0x31, 0xd2, 0xf7, 0xf3); /* xor %edx,%edx; div %ebx */
-                               break;
-                       case BPF_S_ALU_MOD_X: /* A %= X; */
-                               seen |= SEEN_XREG;
-                               EMIT2(0x85, 0xdb);      /* test %ebx,%ebx */
-                               if (pc_ret0 > 0) {
-                                       /* addrs[pc_ret0 - 1] is start address of target
-                                        * (addrs[i] - 6) is the address following this jmp
-                                        * ("xor %edx,%edx; div %ebx;mov %edx,%eax" being 6 bytes long)
-                                        */
-                                       EMIT_COND_JMP(X86_JE, addrs[pc_ret0 - 1] -
-                                                               (addrs[i] - 6));
-                               } else {
-                                       EMIT_COND_JMP(X86_JNE, 2 + 5);
-                                       CLEAR_A();
-                                       EMIT1_off32(0xe9, cleanup_addr - (addrs[i] - 6)); /* jmp .+off32 */
-                               }
-                               EMIT2(0x31, 0xd2);      /* xor %edx,%edx */
-                               EMIT2(0xf7, 0xf3);      /* div %ebx */
-                               EMIT2(0x89, 0xd0);      /* mov %edx,%eax */
-                               break;
-                       case BPF_S_ALU_MOD_K: /* A %= K; */
-                               if (K == 1) {
-                                       CLEAR_A();
-                                       break;
-                               }
-                               EMIT2(0x31, 0xd2);      /* xor %edx,%edx */
-                               EMIT1(0xb9);EMIT(K, 4); /* mov imm32,%ecx */
-                               EMIT2(0xf7, 0xf1);      /* div %ecx */
-                               EMIT2(0x89, 0xd0);      /* mov %edx,%eax */
-                               break;
-                       case BPF_S_ALU_DIV_K: /* A /= K */
-                               if (K == 1)
-                                       break;
-                               EMIT2(0x31, 0xd2);      /* xor %edx,%edx */
-                               EMIT1(0xb9);EMIT(K, 4); /* mov imm32,%ecx */
-                               EMIT2(0xf7, 0xf1);      /* div %ecx */
-                               break;
-                       case BPF_S_ALU_AND_X:
-                               seen |= SEEN_XREG;
-                               EMIT2(0x21, 0xd8);              /* and %ebx,%eax */
-                               break;
-                       case BPF_S_ALU_AND_K:
-                               if (K >= 0xFFFFFF00) {
-                                       EMIT2(0x24, K & 0xFF); /* and imm8,%al */
-                               } else if (K >= 0xFFFF0000) {
-                                       EMIT2(0x66, 0x25);      /* and imm16,%ax */
-                                       EMIT(K, 2);
-                               } else {
-                                       EMIT1_off32(0x25, K);   /* and imm32,%eax */
-                               }
-                               break;
-                       case BPF_S_ALU_OR_X:
-                               seen |= SEEN_XREG;
-                               EMIT2(0x09, 0xd8);              /* or %ebx,%eax */
-                               break;
-                       case BPF_S_ALU_OR_K:
-                               if (is_imm8(K))
-                                       EMIT3(0x83, 0xc8, K); /* or imm8,%eax */
-                               else
-                                       EMIT1_off32(0x0d, K);   /* or imm32,%eax */
-                               break;
-                       case BPF_S_ANC_ALU_XOR_X: /* A ^= X; */
-                       case BPF_S_ALU_XOR_X:
-                               seen |= SEEN_XREG;
-                               EMIT2(0x31, 0xd8);              /* xor %ebx,%eax */
-                               break;
-                       case BPF_S_ALU_XOR_K: /* A ^= K; */
-                               if (K == 0)
-                                       break;
-                               if (is_imm8(K))
-                                       EMIT3(0x83, 0xf0, K);   /* xor imm8,%eax */
-                               else
-                                       EMIT1_off32(0x35, K);   /* xor imm32,%eax */
-                               break;
-                       case BPF_S_ALU_LSH_X: /* A <<= X; */
-                               seen |= SEEN_XREG;
-                               EMIT4(0x89, 0xd9, 0xd3, 0xe0);  /* mov %ebx,%ecx; shl %cl,%eax */
-                               break;
-                       case BPF_S_ALU_LSH_K:
-                               if (K == 0)
-                                       break;
-                               else if (K == 1)
-                                       EMIT2(0xd1, 0xe0); /* shl %eax */
-                               else
-                                       EMIT3(0xc1, 0xe0, K);
-                               break;
-                       case BPF_S_ALU_RSH_X: /* A >>= X; */
-                               seen |= SEEN_XREG;
-                               EMIT4(0x89, 0xd9, 0xd3, 0xe8);  /* mov %ebx,%ecx; shr %cl,%eax */
-                               break;
-                       case BPF_S_ALU_RSH_K: /* A >>= K; */
-                               if (K == 0)
-                                       break;
-                               else if (K == 1)
-                                       EMIT2(0xd1, 0xe8); /* shr %eax */
-                               else
-                                       EMIT3(0xc1, 0xe8, K);
-                               break;
-                       case BPF_S_ALU_NEG:
-                               EMIT2(0xf7, 0xd8);              /* neg %eax */
-                               break;
-                       case BPF_S_RET_K:
-                               if (!K) {
-                                       if (pc_ret0 == -1)
-                                               pc_ret0 = i;
-                                       CLEAR_A();
-                               } else {
-                                       EMIT1_off32(0xb8, K);   /* mov $imm32,%eax */
-                               }
-                               /* fallinto */
-                       case BPF_S_RET_A:
-                               if (seen_or_pass0) {
-                                       if (i != flen - 1) {
-                                               EMIT_JMP(cleanup_addr - addrs[i]);
-                                               break;
-                                       }
-                                       if (seen_or_pass0 & SEEN_XREG)
-                                               EMIT4(0x48, 0x8b, 0x5d, 0xf8);  /* mov  -8(%rbp),%rbx */
-                                       EMIT1(0xc9);            /* leaveq */
-                               }
-                               EMIT1(0xc3);            /* ret */
-                               break;
-                       case BPF_S_MISC_TAX: /* X = A */
-                               seen |= SEEN_XREG;
-                               EMIT2(0x89, 0xc3);      /* mov    %eax,%ebx */
-                               break;
-                       case BPF_S_MISC_TXA: /* A = X */
-                               seen |= SEEN_XREG;
-                               EMIT2(0x89, 0xd8);      /* mov    %ebx,%eax */
-                               break;
-                       case BPF_S_LD_IMM: /* A = K */
-                               if (!K)
-                                       CLEAR_A();
-                               else
-                                       EMIT1_off32(0xb8, K); /* mov $imm32,%eax */
-                               break;
-                       case BPF_S_LDX_IMM: /* X = K */
-                               seen |= SEEN_XREG;
-                               if (!K)
-                                       CLEAR_X();
+                       /* shifts */
+               case BPF_ALU | BPF_LSH | BPF_K:
+               case BPF_ALU | BPF_RSH | BPF_K:
+               case BPF_ALU | BPF_ARSH | BPF_K:
+               case BPF_ALU64 | BPF_LSH | BPF_K:
+               case BPF_ALU64 | BPF_RSH | BPF_K:
+               case BPF_ALU64 | BPF_ARSH | BPF_K:
+                       if (BPF_CLASS(insn->code) == BPF_ALU64)
+                               EMIT1(add_1mod(0x48, dst_reg));
+                       else if (is_ereg(dst_reg))
+                               EMIT1(add_1mod(0x40, dst_reg));
+
+                       switch (BPF_OP(insn->code)) {
+                       case BPF_LSH: b3 = 0xE0; break;
+                       case BPF_RSH: b3 = 0xE8; break;
+                       case BPF_ARSH: b3 = 0xF8; break;
+                       }
+                       EMIT3(0xC1, add_1reg(b3, dst_reg), imm32);
+                       break;
+
+               case BPF_ALU | BPF_END | BPF_FROM_BE:
+                       switch (imm32) {
+                       case 16:
+                               /* emit 'ror %ax, 8' to swap lower 2 bytes */
+                               EMIT1(0x66);
+                               if (is_ereg(dst_reg))
+                                       EMIT1(0x41);
+                               EMIT3(0xC1, add_1reg(0xC8, dst_reg), 8);
+                               break;
+                       case 32:
+                               /* emit 'bswap eax' to swap lower 4 bytes */
+                               if (is_ereg(dst_reg))
+                                       EMIT2(0x41, 0x0F);
                                else
-                                       EMIT1_off32(0xbb, K); /* mov $imm32,%ebx */
-                               break;
-                       case BPF_S_LD_MEM: /* A = mem[K] : mov off8(%rbp),%eax */
-                               seen |= SEEN_MEM;
-                               EMIT3(0x8b, 0x45, 0xf0 - K*4);
-                               break;
-                       case BPF_S_LDX_MEM: /* X = mem[K] : mov off8(%rbp),%ebx */
-                               seen |= SEEN_XREG | SEEN_MEM;
-                               EMIT3(0x8b, 0x5d, 0xf0 - K*4);
-                               break;
-                       case BPF_S_ST: /* mem[K] = A : mov %eax,off8(%rbp) */
-                               seen |= SEEN_MEM;
-                               EMIT3(0x89, 0x45, 0xf0 - K*4);
-                               break;
-                       case BPF_S_STX: /* mem[K] = X : mov %ebx,off8(%rbp) */
-                               seen |= SEEN_XREG | SEEN_MEM;
-                               EMIT3(0x89, 0x5d, 0xf0 - K*4);
-                               break;
-                       case BPF_S_LD_W_LEN: /* A = skb->len; */
-                               BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4);
-                               if (is_imm8(offsetof(struct sk_buff, len)))
-                                       /* mov    off8(%rdi),%eax */
-                                       EMIT3(0x8b, 0x47, offsetof(struct sk_buff, len));
-                               else {
-                                       EMIT2(0x8b, 0x87);
-                                       EMIT(offsetof(struct sk_buff, len), 4);
-                               }
-                               break;
-                       case BPF_S_LDX_W_LEN: /* X = skb->len; */
-                               seen |= SEEN_XREG;
-                               if (is_imm8(offsetof(struct sk_buff, len)))
-                                       /* mov off8(%rdi),%ebx */
-                                       EMIT3(0x8b, 0x5f, offsetof(struct sk_buff, len));
-                               else {
-                                       EMIT2(0x8b, 0x9f);
-                                       EMIT(offsetof(struct sk_buff, len), 4);
-                               }
-                               break;
-                       case BPF_S_ANC_PROTOCOL: /* A = ntohs(skb->protocol); */
-                               BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2);
-                               if (is_imm8(offsetof(struct sk_buff, protocol))) {
-                                       /* movzwl off8(%rdi),%eax */
-                                       EMIT4(0x0f, 0xb7, 0x47, offsetof(struct sk_buff, protocol));
-                               } else {
-                                       EMIT3(0x0f, 0xb7, 0x87); /* movzwl off32(%rdi),%eax */
-                                       EMIT(offsetof(struct sk_buff, protocol), 4);
-                               }
-                               EMIT2(0x86, 0xc4); /* ntohs() : xchg   %al,%ah */
-                               break;
-                       case BPF_S_ANC_IFINDEX:
-                               if (is_imm8(offsetof(struct sk_buff, dev))) {
-                                       /* movq off8(%rdi),%rax */
-                                       EMIT4(0x48, 0x8b, 0x47, offsetof(struct sk_buff, dev));
-                               } else {
-                                       EMIT3(0x48, 0x8b, 0x87); /* movq off32(%rdi),%rax */
-                                       EMIT(offsetof(struct sk_buff, dev), 4);
-                               }
-                               EMIT3(0x48, 0x85, 0xc0);        /* test %rax,%rax */
-                               EMIT_COND_JMP(X86_JE, cleanup_addr - (addrs[i] - 6));
-                               BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, ifindex) != 4);
-                               EMIT2(0x8b, 0x80);      /* mov off32(%rax),%eax */
-                               EMIT(offsetof(struct net_device, ifindex), 4);
-                               break;
-                       case BPF_S_ANC_MARK:
-                               BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4);
-                               if (is_imm8(offsetof(struct sk_buff, mark))) {
-                                       /* mov off8(%rdi),%eax */
-                                       EMIT3(0x8b, 0x47, offsetof(struct sk_buff, mark));
-                               } else {
-                                       EMIT2(0x8b, 0x87);
-                                       EMIT(offsetof(struct sk_buff, mark), 4);
-                               }
-                               break;
-                       case BPF_S_ANC_RXHASH:
-                               BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4);
-                               if (is_imm8(offsetof(struct sk_buff, hash))) {
-                                       /* mov off8(%rdi),%eax */
-                                       EMIT3(0x8b, 0x47, offsetof(struct sk_buff, hash));
-                               } else {
-                                       EMIT2(0x8b, 0x87);
-                                       EMIT(offsetof(struct sk_buff, hash), 4);
-                               }
-                               break;
-                       case BPF_S_ANC_QUEUE:
-                               BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2);
-                               if (is_imm8(offsetof(struct sk_buff, queue_mapping))) {
-                                       /* movzwl off8(%rdi),%eax */
-                                       EMIT4(0x0f, 0xb7, 0x47, offsetof(struct sk_buff, queue_mapping));
-                               } else {
-                                       EMIT3(0x0f, 0xb7, 0x87); /* movzwl off32(%rdi),%eax */
-                                       EMIT(offsetof(struct sk_buff, queue_mapping), 4);
-                               }
-                               break;
-                       case BPF_S_ANC_CPU:
-#ifdef CONFIG_SMP
-                               EMIT4(0x65, 0x8b, 0x04, 0x25); /* mov %gs:off32,%eax */
-                               EMIT((u32)(unsigned long)&cpu_number, 4); /* A = smp_processor_id(); */
-#else
-                               CLEAR_A();
-#endif
+                                       EMIT1(0x0F);
+                               EMIT1(add_1reg(0xC8, dst_reg));
                                break;
-                       case BPF_S_ANC_VLAN_TAG:
-                       case BPF_S_ANC_VLAN_TAG_PRESENT:
-                               BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2);
-                               if (is_imm8(offsetof(struct sk_buff, vlan_tci))) {
-                                       /* movzwl off8(%rdi),%eax */
-                                       EMIT4(0x0f, 0xb7, 0x47, offsetof(struct sk_buff, vlan_tci));
-                               } else {
-                                       EMIT3(0x0f, 0xb7, 0x87); /* movzwl off32(%rdi),%eax */
-                                       EMIT(offsetof(struct sk_buff, vlan_tci), 4);
-                               }
-                               BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000);
-                               if (filter[i].code == BPF_S_ANC_VLAN_TAG) {
-                                       EMIT3(0x80, 0xe4, 0xef); /* and    $0xef,%ah */
-                               } else {
-                                       EMIT3(0xc1, 0xe8, 0x0c); /* shr    $0xc,%eax */
-                                       EMIT3(0x83, 0xe0, 0x01); /* and    $0x1,%eax */
-                               }
-                               break;
-                       case BPF_S_ANC_PKTTYPE:
-                       {
-                               int off = pkt_type_offset();
-
-                               if (off < 0)
-                                       goto out;
-                               if (is_imm8(off)) {
-                                       /* movzbl off8(%rdi),%eax */
-                                       EMIT4(0x0f, 0xb6, 0x47, off);
-                               } else {
-                                       /* movbl off32(%rdi),%eax */
-                                       EMIT3(0x0f, 0xb6, 0x87);
-                                       EMIT(off, 4);
-                               }
-                               EMIT3(0x83, 0xe0, PKT_TYPE_MAX); /* and    $0x7,%eax */
+                       case 64:
+                               /* emit 'bswap rax' to swap 8 bytes */
+                               EMIT3(add_1mod(0x48, dst_reg), 0x0F,
+                                     add_1reg(0xC8, dst_reg));
                                break;
                        }
-                       case BPF_S_LD_W_ABS:
-                               func = CHOOSE_LOAD_FUNC(K, sk_load_word);
-common_load:                   seen |= SEEN_DATAREF;
-                               t_offset = func - (image + addrs[i]);
-                               EMIT1_off32(0xbe, K); /* mov imm32,%esi */
-                               EMIT1_off32(0xe8, t_offset); /* call */
-                               break;
-                       case BPF_S_LD_H_ABS:
-                               func = CHOOSE_LOAD_FUNC(K, sk_load_half);
-                               goto common_load;
-                       case BPF_S_LD_B_ABS:
-                               func = CHOOSE_LOAD_FUNC(K, sk_load_byte);
-                               goto common_load;
-                       case BPF_S_LDX_B_MSH:
-                               func = CHOOSE_LOAD_FUNC(K, sk_load_byte_msh);
-                               seen |= SEEN_DATAREF | SEEN_XREG;
-                               t_offset = func - (image + addrs[i]);
-                               EMIT1_off32(0xbe, K);   /* mov imm32,%esi */
-                               EMIT1_off32(0xe8, t_offset); /* call sk_load_byte_msh */
-                               break;
-                       case BPF_S_LD_W_IND:
-                               func = sk_load_word;
-common_load_ind:               seen |= SEEN_DATAREF | SEEN_XREG;
-                               t_offset = func - (image + addrs[i]);
-                               if (K) {
-                                       if (is_imm8(K)) {
-                                               EMIT3(0x8d, 0x73, K); /* lea imm8(%rbx), %esi */
-                                       } else {
-                                               EMIT2(0x8d, 0xb3); /* lea imm32(%rbx),%esi */
-                                               EMIT(K, 4);
-                                       }
-                               } else {
-                                       EMIT2(0x89,0xde); /* mov %ebx,%esi */
-                               }
-                               EMIT1_off32(0xe8, t_offset);    /* call sk_load_xxx_ind */
-                               break;
-                       case BPF_S_LD_H_IND:
-                               func = sk_load_half;
-                               goto common_load_ind;
-                       case BPF_S_LD_B_IND:
-                               func = sk_load_byte;
-                               goto common_load_ind;
-                       case BPF_S_JMP_JA:
-                               t_offset = addrs[i + K] - addrs[i];
-                               EMIT_JMP(t_offset);
-                               break;
-                       COND_SEL(BPF_S_JMP_JGT_K, X86_JA, X86_JBE);
-                       COND_SEL(BPF_S_JMP_JGE_K, X86_JAE, X86_JB);
-                       COND_SEL(BPF_S_JMP_JEQ_K, X86_JE, X86_JNE);
-                       COND_SEL(BPF_S_JMP_JSET_K,X86_JNE, X86_JE);
-                       COND_SEL(BPF_S_JMP_JGT_X, X86_JA, X86_JBE);
-                       COND_SEL(BPF_S_JMP_JGE_X, X86_JAE, X86_JB);
-                       COND_SEL(BPF_S_JMP_JEQ_X, X86_JE, X86_JNE);
-                       COND_SEL(BPF_S_JMP_JSET_X,X86_JNE, X86_JE);
-
-cond_branch:                   f_offset = addrs[i + filter[i].jf] - addrs[i];
-                               t_offset = addrs[i + filter[i].jt] - addrs[i];
-
-                               /* same targets, can avoid doing the test :) */
-                               if (filter[i].jt == filter[i].jf) {
-                                       EMIT_JMP(t_offset);
-                                       break;
-                               }
+                       break;
+
+               case BPF_ALU | BPF_END | BPF_FROM_LE:
+                       break;
+
+                       /* ST: *(u8*)(dst_reg + off) = imm */
+               case BPF_ST | BPF_MEM | BPF_B:
+                       if (is_ereg(dst_reg))
+                               EMIT2(0x41, 0xC6);
+                       else
+                               EMIT1(0xC6);
+                       goto st;
+               case BPF_ST | BPF_MEM | BPF_H:
+                       if (is_ereg(dst_reg))
+                               EMIT3(0x66, 0x41, 0xC7);
+                       else
+                               EMIT2(0x66, 0xC7);
+                       goto st;
+               case BPF_ST | BPF_MEM | BPF_W:
+                       if (is_ereg(dst_reg))
+                               EMIT2(0x41, 0xC7);
+                       else
+                               EMIT1(0xC7);
+                       goto st;
+               case BPF_ST | BPF_MEM | BPF_DW:
+                       EMIT2(add_1mod(0x48, dst_reg), 0xC7);
+
+st:                    if (is_imm8(insn->off))
+                               EMIT2(add_1reg(0x40, dst_reg), insn->off);
+                       else
+                               EMIT1_off32(add_1reg(0x80, dst_reg), insn->off);
+
+                       EMIT(imm32, bpf_size_to_x86_bytes(BPF_SIZE(insn->code)));
+                       break;
+
+                       /* STX: *(u8*)(dst_reg + off) = src_reg */
+               case BPF_STX | BPF_MEM | BPF_B:
+                       /* emit 'mov byte ptr [rax + off], al' */
+                       if (is_ereg(dst_reg) || is_ereg(src_reg) ||
+                           /* have to add extra byte for x86 SIL, DIL regs */
+                           src_reg == BPF_REG_1 || src_reg == BPF_REG_2)
+                               EMIT2(add_2mod(0x40, dst_reg, src_reg), 0x88);
+                       else
+                               EMIT1(0x88);
+                       goto stx;
+               case BPF_STX | BPF_MEM | BPF_H:
+                       if (is_ereg(dst_reg) || is_ereg(src_reg))
+                               EMIT3(0x66, add_2mod(0x40, dst_reg, src_reg), 0x89);
+                       else
+                               EMIT2(0x66, 0x89);
+                       goto stx;
+               case BPF_STX | BPF_MEM | BPF_W:
+                       if (is_ereg(dst_reg) || is_ereg(src_reg))
+                               EMIT2(add_2mod(0x40, dst_reg, src_reg), 0x89);
+                       else
+                               EMIT1(0x89);
+                       goto stx;
+               case BPF_STX | BPF_MEM | BPF_DW:
+                       EMIT2(add_2mod(0x48, dst_reg, src_reg), 0x89);
+stx:                   if (is_imm8(insn->off))
+                               EMIT2(add_2reg(0x40, dst_reg, src_reg), insn->off);
+                       else
+                               EMIT1_off32(add_2reg(0x80, dst_reg, src_reg),
+                                           insn->off);
+                       break;
+
+                       /* LDX: dst_reg = *(u8*)(src_reg + off) */
+               case BPF_LDX | BPF_MEM | BPF_B:
+                       /* emit 'movzx rax, byte ptr [rax + off]' */
+                       EMIT3(add_2mod(0x48, src_reg, dst_reg), 0x0F, 0xB6);
+                       goto ldx;
+               case BPF_LDX | BPF_MEM | BPF_H:
+                       /* emit 'movzx rax, word ptr [rax + off]' */
+                       EMIT3(add_2mod(0x48, src_reg, dst_reg), 0x0F, 0xB7);
+                       goto ldx;
+               case BPF_LDX | BPF_MEM | BPF_W:
+                       /* emit 'mov eax, dword ptr [rax+0x14]' */
+                       if (is_ereg(dst_reg) || is_ereg(src_reg))
+                               EMIT2(add_2mod(0x40, src_reg, dst_reg), 0x8B);
+                       else
+                               EMIT1(0x8B);
+                       goto ldx;
+               case BPF_LDX | BPF_MEM | BPF_DW:
+                       /* emit 'mov rax, qword ptr [rax+0x14]' */
+                       EMIT2(add_2mod(0x48, src_reg, dst_reg), 0x8B);
+ldx:                   /* if insn->off == 0 we can save one extra byte, but
+                        * special case of x86 r13 which always needs an offset
+                        * is not worth the hassle
+                        */
+                       if (is_imm8(insn->off))
+                               EMIT2(add_2reg(0x40, src_reg, dst_reg), insn->off);
+                       else
+                               EMIT1_off32(add_2reg(0x80, src_reg, dst_reg),
+                                           insn->off);
+                       break;
+
+                       /* STX XADD: lock *(u32*)(dst_reg + off) += src_reg */
+               case BPF_STX | BPF_XADD | BPF_W:
+                       /* emit 'lock add dword ptr [rax + off], eax' */
+                       if (is_ereg(dst_reg) || is_ereg(src_reg))
+                               EMIT3(0xF0, add_2mod(0x40, dst_reg, src_reg), 0x01);
+                       else
+                               EMIT2(0xF0, 0x01);
+                       goto xadd;
+               case BPF_STX | BPF_XADD | BPF_DW:
+                       EMIT3(0xF0, add_2mod(0x48, dst_reg, src_reg), 0x01);
+xadd:                  if (is_imm8(insn->off))
+                               EMIT2(add_2reg(0x40, dst_reg, src_reg), insn->off);
+                       else
+                               EMIT1_off32(add_2reg(0x80, dst_reg, src_reg),
+                                           insn->off);
+                       break;
+
+                       /* call */
+               case BPF_JMP | BPF_CALL:
+                       func = (u8 *) __bpf_call_base + imm32;
+                       jmp_offset = func - (image + addrs[i]);
+                       if (ctx->seen_ld_abs) {
+                               EMIT2(0x41, 0x52); /* push %r10 */
+                               EMIT2(0x41, 0x51); /* push %r9 */
+                               /* need to adjust jmp offset, since
+                                * pop %r9, pop %r10 take 4 bytes after call insn
+                                */
+                               jmp_offset += 4;
+                       }
+                       if (!imm32 || !is_simm32(jmp_offset)) {
+                               pr_err("unsupported bpf func %d addr %p image %p\n",
+                                      imm32, func, image);
+                               return -EINVAL;
+                       }
+                       EMIT1_off32(0xE8, jmp_offset);
+                       if (ctx->seen_ld_abs) {
+                               EMIT2(0x41, 0x59); /* pop %r9 */
+                               EMIT2(0x41, 0x5A); /* pop %r10 */
+                       }
+                       break;
+
+                       /* cond jump */
+               case BPF_JMP | BPF_JEQ | BPF_X:
+               case BPF_JMP | BPF_JNE | BPF_X:
+               case BPF_JMP | BPF_JGT | BPF_X:
+               case BPF_JMP | BPF_JGE | BPF_X:
+               case BPF_JMP | BPF_JSGT | BPF_X:
+               case BPF_JMP | BPF_JSGE | BPF_X:
+                       /* cmp dst_reg, src_reg */
+                       EMIT3(add_2mod(0x48, dst_reg, src_reg), 0x39,
+                             add_2reg(0xC0, dst_reg, src_reg));
+                       goto emit_cond_jmp;
+
+               case BPF_JMP | BPF_JSET | BPF_X:
+                       /* test dst_reg, src_reg */
+                       EMIT3(add_2mod(0x48, dst_reg, src_reg), 0x85,
+                             add_2reg(0xC0, dst_reg, src_reg));
+                       goto emit_cond_jmp;
+
+               case BPF_JMP | BPF_JSET | BPF_K:
+                       /* test dst_reg, imm32 */
+                       EMIT1(add_1mod(0x48, dst_reg));
+                       EMIT2_off32(0xF7, add_1reg(0xC0, dst_reg), imm32);
+                       goto emit_cond_jmp;
+
+               case BPF_JMP | BPF_JEQ | BPF_K:
+               case BPF_JMP | BPF_JNE | BPF_K:
+               case BPF_JMP | BPF_JGT | BPF_K:
+               case BPF_JMP | BPF_JGE | BPF_K:
+               case BPF_JMP | BPF_JSGT | BPF_K:
+               case BPF_JMP | BPF_JSGE | BPF_K:
+                       /* cmp dst_reg, imm8/32 */
+                       EMIT1(add_1mod(0x48, dst_reg));
+
+                       if (is_imm8(imm32))
+                               EMIT3(0x83, add_1reg(0xF8, dst_reg), imm32);
+                       else
+                               EMIT2_off32(0x81, add_1reg(0xF8, dst_reg), imm32);
+
+emit_cond_jmp:         /* convert BPF opcode to x86 */
+                       switch (BPF_OP(insn->code)) {
+                       case BPF_JEQ:
+                               jmp_cond = X86_JE;
+                               break;
+                       case BPF_JSET:
+                       case BPF_JNE:
+                               jmp_cond = X86_JNE;
+                               break;
+                       case BPF_JGT:
+                               /* GT is unsigned '>', JA in x86 */
+                               jmp_cond = X86_JA;
+                               break;
+                       case BPF_JGE:
+                               /* GE is unsigned '>=', JAE in x86 */
+                               jmp_cond = X86_JAE;
+                               break;
+                       case BPF_JSGT:
+                               /* signed '>', GT in x86 */
+                               jmp_cond = X86_JG;
+                               break;
+                       case BPF_JSGE:
+                               /* signed '>=', GE in x86 */
+                               jmp_cond = X86_JGE;
+                               break;
+                       default: /* to silence gcc warning */
+                               return -EFAULT;
+                       }
+                       jmp_offset = addrs[i + insn->off] - addrs[i];
+                       if (is_imm8(jmp_offset)) {
+                               EMIT2(jmp_cond, jmp_offset);
+                       } else if (is_simm32(jmp_offset)) {
+                               EMIT2_off32(0x0F, jmp_cond + 0x10, jmp_offset);
+                       } else {
+                               pr_err("cond_jmp gen bug %llx\n", jmp_offset);
+                               return -EFAULT;
+                       }
+
+                       break;
 
-                               switch (filter[i].code) {
-                               case BPF_S_JMP_JGT_X:
-                               case BPF_S_JMP_JGE_X:
-                               case BPF_S_JMP_JEQ_X:
-                                       seen |= SEEN_XREG;
-                                       EMIT2(0x39, 0xd8); /* cmp %ebx,%eax */
-                                       break;
-                               case BPF_S_JMP_JSET_X:
-                                       seen |= SEEN_XREG;
-                                       EMIT2(0x85, 0xd8); /* test %ebx,%eax */
-                                       break;
-                               case BPF_S_JMP_JEQ_K:
-                                       if (K == 0) {
-                                               EMIT2(0x85, 0xc0); /* test   %eax,%eax */
-                                               break;
-                                       }
-                               case BPF_S_JMP_JGT_K:
-                               case BPF_S_JMP_JGE_K:
-                                       if (K <= 127)
-                                               EMIT3(0x83, 0xf8, K); /* cmp imm8,%eax */
+               case BPF_JMP | BPF_JA:
+                       jmp_offset = addrs[i + insn->off] - addrs[i];
+                       if (!jmp_offset)
+                               /* optimize out nop jumps */
+                               break;
+emit_jmp:
+                       if (is_imm8(jmp_offset)) {
+                               EMIT2(0xEB, jmp_offset);
+                       } else if (is_simm32(jmp_offset)) {
+                               EMIT1_off32(0xE9, jmp_offset);
+                       } else {
+                               pr_err("jmp gen bug %llx\n", jmp_offset);
+                               return -EFAULT;
+                       }
+                       break;
+
+               case BPF_LD | BPF_IND | BPF_W:
+                       func = sk_load_word;
+                       goto common_load;
+               case BPF_LD | BPF_ABS | BPF_W:
+                       func = CHOOSE_LOAD_FUNC(imm32, sk_load_word);
+common_load:           ctx->seen_ld_abs = true;
+                       jmp_offset = func - (image + addrs[i]);
+                       if (!func || !is_simm32(jmp_offset)) {
+                               pr_err("unsupported bpf func %d addr %p image %p\n",
+                                      imm32, func, image);
+                               return -EINVAL;
+                       }
+                       if (BPF_MODE(insn->code) == BPF_ABS) {
+                               /* mov %esi, imm32 */
+                               EMIT1_off32(0xBE, imm32);
+                       } else {
+                               /* mov %rsi, src_reg */
+                               EMIT_mov(BPF_REG_2, src_reg);
+                               if (imm32) {
+                                       if (is_imm8(imm32))
+                                               /* add %esi, imm8 */
+                                               EMIT3(0x83, 0xC6, imm32);
                                        else
-                                               EMIT1_off32(0x3d, K); /* cmp imm32,%eax */
-                                       break;
-                               case BPF_S_JMP_JSET_K:
-                                       if (K <= 0xFF)
-                                               EMIT2(0xa8, K); /* test imm8,%al */
-                                       else if (!(K & 0xFFFF00FF))
-                                               EMIT3(0xf6, 0xc4, K >> 8); /* test imm8,%ah */
-                                       else if (K <= 0xFFFF) {
-                                               EMIT2(0x66, 0xa9); /* test imm16,%ax */
-                                               EMIT(K, 2);
-                                       } else {
-                                               EMIT1_off32(0xa9, K); /* test imm32,%eax */
-                                       }
-                                       break;
+                                               /* add %esi, imm32 */
+                                               EMIT2_off32(0x81, 0xC6, imm32);
                                }
-                               if (filter[i].jt != 0) {
-                                       if (filter[i].jf && f_offset)
-                                               t_offset += is_near(f_offset) ? 2 : 5;
-                                       EMIT_COND_JMP(t_op, t_offset);
-                                       if (filter[i].jf)
-                                               EMIT_JMP(f_offset);
-                                       break;
-                               }
-                               EMIT_COND_JMP(f_op, f_offset);
-                               break;
-                       default:
-                               /* hmm, too complex filter, give up with jit compiler */
-                               goto out;
                        }
-                       ilen = prog - temp;
-                       if (image) {
-                               if (unlikely(proglen + ilen > oldproglen)) {
-                                       pr_err("bpb_jit_compile fatal error\n");
-                                       kfree(addrs);
-                                       module_free(NULL, header);
-                                       return;
-                               }
-                               memcpy(image + proglen, temp, ilen);
+                       /* skb pointer is in R6 (%rbx), it will be copied into
+                        * %rdi if skb_copy_bits() call is necessary.
+                        * sk_load_* helpers also use %r10 and %r9d.
+                        * See bpf_jit.S
+                        */
+                       EMIT1_off32(0xE8, jmp_offset); /* call */
+                       break;
+
+               case BPF_LD | BPF_IND | BPF_H:
+                       func = sk_load_half;
+                       goto common_load;
+               case BPF_LD | BPF_ABS | BPF_H:
+                       func = CHOOSE_LOAD_FUNC(imm32, sk_load_half);
+                       goto common_load;
+               case BPF_LD | BPF_IND | BPF_B:
+                       func = sk_load_byte;
+                       goto common_load;
+               case BPF_LD | BPF_ABS | BPF_B:
+                       func = CHOOSE_LOAD_FUNC(imm32, sk_load_byte);
+                       goto common_load;
+
+               case BPF_JMP | BPF_EXIT:
+                       if (i != insn_cnt - 1) {
+                               jmp_offset = ctx->cleanup_addr - addrs[i];
+                               goto emit_jmp;
                        }
-                       proglen += ilen;
-                       addrs[i] = proglen;
-                       prog = temp;
+                       /* update cleanup_addr */
+                       ctx->cleanup_addr = proglen;
+                       /* mov rbx, qword ptr [rbp-X] */
+                       EMIT3_off32(0x48, 0x8B, 0x9D, -stacksize);
+                       /* mov r13, qword ptr [rbp-X] */
+                       EMIT3_off32(0x4C, 0x8B, 0xAD, -stacksize + 8);
+                       /* mov r14, qword ptr [rbp-X] */
+                       EMIT3_off32(0x4C, 0x8B, 0xB5, -stacksize + 16);
+                       /* mov r15, qword ptr [rbp-X] */
+                       EMIT3_off32(0x4C, 0x8B, 0xBD, -stacksize + 24);
+
+                       EMIT1(0xC9); /* leave */
+                       EMIT1(0xC3); /* ret */
+                       break;
+
+               default:
+                       /* By design x64 JIT should support all BPF instructions
+                        * This error will be seen if new instruction was added
+                        * to interpreter, but not to JIT
+                        * or if there is junk in sk_filter
+                        */
+                       pr_err("bpf_jit: unknown opcode %02x\n", insn->code);
+                       return -EINVAL;
                }
-               /* last bpf instruction is always a RET :
-                * use it to give the cleanup instruction(s) addr
-                */
-               cleanup_addr = proglen - 1; /* ret */
-               if (seen_or_pass0)
-                       cleanup_addr -= 1; /* leaveq */
-               if (seen_or_pass0 & SEEN_XREG)
-                       cleanup_addr -= 4; /* mov  -8(%rbp),%rbx */
 
+               ilen = prog - temp;
+               if (image) {
+                       if (unlikely(proglen + ilen > oldproglen)) {
+                               pr_err("bpf_jit_compile fatal error\n");
+                               return -EFAULT;
+                       }
+                       memcpy(image + proglen, temp, ilen);
+               }
+               proglen += ilen;
+               addrs[i] = proglen;
+               prog = temp;
+       }
+       return proglen;
+}
+
+void bpf_jit_compile(struct sk_filter *prog)
+{
+}
+
+void bpf_int_jit_compile(struct sk_filter *prog)
+{
+       struct bpf_binary_header *header = NULL;
+       int proglen, oldproglen = 0;
+       struct jit_context ctx = {};
+       u8 *image = NULL;
+       int *addrs;
+       int pass;
+       int i;
+
+       if (!bpf_jit_enable)
+               return;
+
+       if (!prog || !prog->len)
+               return;
+
+       addrs = kmalloc(prog->len * sizeof(*addrs), GFP_KERNEL);
+       if (!addrs)
+               return;
+
+       /* Before first pass, make a rough estimation of addrs[]
+        * each bpf instruction is translated to less than 64 bytes
+        */
+       for (proglen = 0, i = 0; i < prog->len; i++) {
+               proglen += 64;
+               addrs[i] = proglen;
+       }
+       ctx.cleanup_addr = proglen;
+
+       for (pass = 0; pass < 10; pass++) {
+               proglen = do_jit(prog, addrs, image, oldproglen, &ctx);
+               if (proglen <= 0) {
+                       image = NULL;
+                       if (header)
+                               module_free(NULL, header);
+                       goto out;
+               }
                if (image) {
                        if (proglen != oldproglen)
-                               pr_err("bpb_jit_compile proglen=%u != oldproglen=%u\n", proglen, oldproglen);
+                               pr_err("bpf_jit: proglen=%d != oldproglen=%d\n",
+                                      proglen, oldproglen);
                        break;
                }
                if (proglen == oldproglen) {
@@ -766,17 +918,16 @@ cond_branch:                      f_offset = addrs[i + filter[i].jf] - addrs[i];
        }
 
        if (bpf_jit_enable > 1)
-               bpf_jit_dump(flen, proglen, pass, image);
+               bpf_jit_dump(prog->len, proglen, 0, image);
 
        if (image) {
                bpf_flush_icache(header, image + proglen);
                set_memory_ro((unsigned long)header, header->pages);
-               fp->bpf_func = (void *)image;
-               fp->jited = 1;
+               prog->bpf_func = (void *)image;
+               prog->jited = 1;
        }
 out:
        kfree(addrs);
-       return;
 }
 
 static void bpf_jit_free_deferred(struct work_struct *work)
index 9769df094035535180efca797917780b2c65f915..3c0809a0631f22acd45d19de1e3d548fe9e664a4 100644 (file)
@@ -9,18 +9,9 @@ VDSOX32-$(CONFIG_X86_X32_ABI)  := y
 VDSO32-$(CONFIG_X86_32)                := y
 VDSO32-$(CONFIG_COMPAT)                := y
 
-vdso-install-$(VDSO64-y)       += vdso.so
-vdso-install-$(VDSOX32-y)      += vdsox32.so
-vdso-install-$(VDSO32-y)       += $(vdso32-images)
-
-
 # files to link into the vdso
-vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o
-
-vobjs-$(VDSOX32-y) += $(vobjx32s-compat)
-
-# Filter out x32 objects.
-vobj64s := $(filter-out $(vobjx32s-compat),$(vobjs-y))
+vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o vdso-fakesections.o
+vobjs-nox32 := vdso-fakesections.o
 
 # files to link into kernel
 obj-y                          += vma.o
@@ -34,7 +25,7 @@ vdso_img-$(VDSO32-y)          += 32-sysenter
 
 obj-$(VDSO32-y)                        += vdso32-setup.o
 
-vobjs := $(foreach F,$(vobj64s),$(obj)/$F)
+vobjs := $(foreach F,$(vobjs-y),$(obj)/$F)
 
 $(obj)/vdso.o: $(obj)/vdso.so
 
@@ -104,7 +95,13 @@ VDSO_LDFLAGS_vdsox32.lds = -Wl,-m,elf32_x86_64 \
                           -Wl,-z,max-page-size=4096 \
                           -Wl,-z,common-page-size=4096
 
-vobjx32s-y := $(vobj64s:.o=-x32.o)
+# 64-bit objects to re-brand as x32
+vobjs64-for-x32 := $(filter-out $(vobjs-nox32),$(vobjs-y))
+
+# x32-rebranded versions
+vobjx32s-y := $(vobjs64-for-x32:.o=-x32.o)
+
+# same thing, but in the output directory
 vobjx32s := $(foreach F,$(vobjx32s-y),$(obj)/$F)
 
 # Convert 64bit object file to x32 for x32 vDSO.
@@ -176,15 +173,20 @@ VDSO_LDFLAGS = -fPIC -shared $(call cc-ldoption, -Wl$(comma)--hash-style=sysv) \
 GCOV_PROFILE := n
 
 #
-# Install the unstripped copy of vdso*.so listed in $(vdso-install-y).
+# Install the unstripped copies of vdso*.so.
 #
-quiet_cmd_vdso_install = INSTALL $@
-      cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@
-$(vdso-install-y): %.so: $(obj)/%.so.dbg FORCE
+quiet_cmd_vdso_install = INSTALL $(@:install_%=%)
+      cmd_vdso_install = cp $< $(MODLIB)/vdso/$(@:install_%=%)
+
+vdso_img_insttargets := $(vdso_img_sodbg:%.dbg=install_%)
+
+$(MODLIB)/vdso: FORCE
        @mkdir -p $(MODLIB)/vdso
+
+$(vdso_img_insttargets): install_%: $(obj)/%.dbg $(MODLIB)/vdso FORCE
        $(call cmd,vdso_install)
 
-PHONY += vdso_install $(vdso-install-y)
-vdso_install: $(vdso-install-y)
+PHONY += vdso_install $(vdso_img_insttargets)
+vdso_install: $(vdso_img_insttargets) FORCE
 
 clean-files := vdso32-syscall* vdso32-sysenter* vdso32-int80*
diff --git a/arch/x86/vdso/vdso-fakesections.c b/arch/x86/vdso/vdso-fakesections.c
new file mode 100644 (file)
index 0000000..cb8a8d7
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 Andy Lutomirski
+ * Subject to the GNU Public License, v.2
+ *
+ * Hack to keep broken Go programs working.
+ *
+ * The Go runtime had a couple of bugs: it would read the section table to try
+ * to figure out how many dynamic symbols there were (it shouldn't have looked
+ * at the section table at all) and, if there were no SHT_SYNDYM section table
+ * entry, it would use an uninitialized value for the number of symbols.  As a
+ * workaround, we supply a minimal section table.  vdso2c will adjust the
+ * in-memory image so that "vdso_fake_sections" becomes the section table.
+ *
+ * The bug was introduced by:
+ * https://code.google.com/p/go/source/detail?r=56ea40aac72b (2012-08-31)
+ * and is being addressed in the Go runtime in this issue:
+ * https://code.google.com/p/go/issues/detail?id=8197
+ */
+
+#ifndef __x86_64__
+#error This hack is specific to the 64-bit vDSO
+#endif
+
+#include <linux/elf.h>
+
+extern const __visible struct elf64_shdr vdso_fake_sections[];
+const __visible struct elf64_shdr vdso_fake_sections[] = {
+       {
+               .sh_type = SHT_DYNSYM,
+               .sh_entsize = sizeof(Elf64_Sym),
+       }
+};
index 450ac6eaf613cda997e4d8032b0a5e45a39d0732..7a6bf50f9165fb6afe1a7f00537c081a9aed0024 100644 (file)
@@ -54,7 +54,7 @@ static void fail(const char *format, ...)
 }
 
 /*
- * Evil macros to do a little-endian read.
+ * Evil macros for little-endian reads and writes
  */
 #define GLE(x, bits, ifnot)                                            \
        __builtin_choose_expr(                                          \
@@ -62,11 +62,24 @@ static void fail(const char *format, ...)
                (__typeof__(*(x)))get_unaligned_le##bits(x), ifnot)
 
 extern void bad_get_le(void);
-#define LAST_LE(x)                                                     \
+#define LAST_GLE(x)                                                    \
        __builtin_choose_expr(sizeof(*(x)) == 1, *(x), bad_get_le())
 
 #define GET_LE(x)                                                      \
-       GLE(x, 64, GLE(x, 32, GLE(x, 16, LAST_LE(x))))
+       GLE(x, 64, GLE(x, 32, GLE(x, 16, LAST_GLE(x))))
+
+#define PLE(x, val, bits, ifnot)                                       \
+       __builtin_choose_expr(                                          \
+               (sizeof(*(x)) == bits/8),                               \
+               put_unaligned_le##bits((val), (x)), ifnot)
+
+extern void bad_put_le(void);
+#define LAST_PLE(x, val)                                               \
+       __builtin_choose_expr(sizeof(*(x)) == 1, *(x) = (val), bad_put_le())
+
+#define PUT_LE(x, val)                                 \
+       PLE(x, val, 64, PLE(x, val, 32, PLE(x, val, 16, LAST_PLE(x, val))))
+
 
 #define NSYMS (sizeof(required_syms) / sizeof(required_syms[0]))
 
index 8a074637a5767e20a3da32816d41e293a4eb28b3..c6eefaf389b95e6a18bef6a58943afb4c6d597bf 100644 (file)
@@ -18,6 +18,8 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name)
        const char *secstrings;
        uint64_t syms[NSYMS] = {};
 
+       uint64_t fake_sections_value = 0, fake_sections_size = 0;
+
        Elf_Phdr *pt = (Elf_Phdr *)(addr + GET_LE(&hdr->e_phoff));
 
        /* Walk the segment table. */
@@ -84,6 +86,7 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name)
                        GET_LE(&symtab_hdr->sh_entsize) * i;
                const char *name = addr + GET_LE(&strtab_hdr->sh_offset) +
                        GET_LE(&sym->st_name);
+
                for (k = 0; k < NSYMS; k++) {
                        if (!strcmp(name, required_syms[k])) {
                                if (syms[k]) {
@@ -93,6 +96,13 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name)
                                syms[k] = GET_LE(&sym->st_value);
                        }
                }
+
+               if (!strcmp(name, "vdso_fake_sections")) {
+                       if (fake_sections_value)
+                               fail("duplicate vdso_fake_sections\n");
+                       fake_sections_value = GET_LE(&sym->st_value);
+                       fake_sections_size = GET_LE(&sym->st_size);
+               }
        }
 
        /* Validate mapping addresses. */
@@ -112,11 +122,14 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name)
        if (syms[sym_end_mapping] % 4096)
                fail("end_mapping must be a multiple of 4096\n");
 
-       /* Remove sections. */
-       hdr->e_shoff = 0;
-       hdr->e_shentsize = 0;
-       hdr->e_shnum = 0;
-       hdr->e_shstrndx = htole16(SHN_UNDEF);
+       /* Remove sections or use fakes */
+       if (fake_sections_size % sizeof(Elf_Shdr))
+               fail("vdso_fake_sections size is not a multiple of %ld\n",
+                    (long)sizeof(Elf_Shdr));
+       PUT_LE(&hdr->e_shoff, fake_sections_value);
+       PUT_LE(&hdr->e_shentsize, fake_sections_value ? sizeof(Elf_Shdr) : 0);
+       PUT_LE(&hdr->e_shnum, fake_sections_size / sizeof(Elf_Shdr));
+       PUT_LE(&hdr->e_shstrndx, SHN_UNDEF);
 
        if (!name) {
                fwrite(addr, load_size, 1, outfile);
index 9aca8c71e70b0bf8d8936462f1517a62da6c8432..f6f6b9af3e3f541e78c8079cbbc7031a09efeb4d 100644 (file)
@@ -43,6 +43,7 @@
 EXPORT_TRACEPOINT_SYMBOL_GPL(block_bio_remap);
 EXPORT_TRACEPOINT_SYMBOL_GPL(block_rq_remap);
 EXPORT_TRACEPOINT_SYMBOL_GPL(block_bio_complete);
+EXPORT_TRACEPOINT_SYMBOL_GPL(block_split);
 EXPORT_TRACEPOINT_SYMBOL_GPL(block_unplug);
 
 DEFINE_IDA(blk_queue_ida);
index 147bc1b91b4214fd0f7dd7e0ae1c840e371309eb..3f2bdc812d23b7c2b4175448793eb56c96af2b47 100644 (file)
@@ -1810,6 +1810,16 @@ acpi_status __init acpi_os_initialize(void)
        acpi_os_map_generic_address(&acpi_gbl_FADT.xpm1b_event_block);
        acpi_os_map_generic_address(&acpi_gbl_FADT.xgpe0_block);
        acpi_os_map_generic_address(&acpi_gbl_FADT.xgpe1_block);
+       if (acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER) {
+               /*
+                * Use acpi_os_map_generic_address to pre-map the reset
+                * register if it's in system memory.
+                */
+               int rv;
+
+               rv = acpi_os_map_generic_address(&acpi_gbl_FADT.reset_register);
+               pr_debug(PREFIX "%s: map reset_reg status %d\n", __func__, rv);
+       }
 
        return AE_OK;
 }
@@ -1838,6 +1848,8 @@ acpi_status acpi_os_terminate(void)
        acpi_os_unmap_generic_address(&acpi_gbl_FADT.xgpe0_block);
        acpi_os_unmap_generic_address(&acpi_gbl_FADT.xpm1b_event_block);
        acpi_os_unmap_generic_address(&acpi_gbl_FADT.xpm1a_event_block);
+       if (acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER)
+               acpi_os_unmap_generic_address(&acpi_gbl_FADT.reset_register);
 
        destroy_workqueue(kacpid_wq);
        destroy_workqueue(kacpi_notify_wq);
index c11e3795431b2e686b098cd4f14f2cbcdcd0c2f4..b3e3cc73ba796edf49d856c203e441c5ab818420 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/acpi.h>
 #include <linux/module.h>
 #include <asm/io.h>
+#include <trace/events/power.h>
 
 #include "internal.h"
 #include "sleep.h"
@@ -501,6 +502,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
 
        ACPI_FLUSH_CPU_CACHE();
 
+       trace_suspend_resume(TPS("acpi_suspend"), acpi_state, true);
        switch (acpi_state) {
        case ACPI_STATE_S1:
                barrier();
@@ -516,6 +518,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
                pr_info(PREFIX "Low-level resume complete\n");
                break;
        }
+       trace_suspend_resume(TPS("acpi_suspend"), acpi_state, false);
 
        /* This violates the spec but is required for bug compatibility. */
        acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1);
index 101fb090dcb9d59632979db199483d4d46babc57..fb9ffe9adc645601361092c7252778a1aacf2e43 100644 (file)
@@ -82,7 +82,7 @@ module_param(allow_duplicates, bool, 0644);
  * For Windows 8 systems: used to decide if video module
  * should skip registering backlight interface of its own.
  */
-static int use_native_backlight_param = -1;
+static int use_native_backlight_param = 1;
 module_param_named(use_native_backlight, use_native_backlight_param, int, 0444);
 static bool use_native_backlight_dmi = false;
 
index 204814e88e464480248a6ddef6fe292d095514ad..d4725fc0395d037e9ae3b861a241f9e9d229a580 100644 (file)
@@ -2780,7 +2780,7 @@ static struct pci_driver fore200e_pca_driver = {
 
 static int __init fore200e_module_init(void)
 {
-       int err;
+       int err = 0;
 
        printk(FORE200E "FORE Systems 200E-series ATM driver - version " FORE200E_VERSION "\n");
 
index 1bdf104e90bb7f1924acf51f78caa39817d78efb..b621f56a36be5850b1abc4d1619b665fbce7ccf3 100644 (file)
@@ -2551,12 +2551,12 @@ done:
                timeout = 5 * 1000;
                while (atomic_read(&vc->scq->used) > 0) {
                        timeout = msleep_interruptible(timeout);
-                       if (!timeout)
+                       if (!timeout) {
+                               pr_warn("%s: SCQ drain timeout: %u used\n",
+                                       card->name, atomic_read(&vc->scq->used));
                                break;
+                       }
                }
-               if (!timeout)
-                       printk("%s: SCQ drain timeout: %u used\n",
-                              card->name, atomic_read(&vc->scq->used));
 
                writel(TCMDQ_HALT | vc->index, SAR_REG_TCMDQ);
                clear_scd(card, vc->scq, vc->class);
index 343ffad593779d08095132e4207fcc2a7fbfcbbf..bf412961a9349a0d9c9f687c66c3984dac12b08f 100644 (file)
@@ -214,9 +214,6 @@ static void initcall_debug_report(struct device *dev, ktime_t calltime,
                pr_info("call %s+ returned %d after %Ld usecs\n", dev_name(dev),
                        error, (unsigned long long)nsecs >> 10);
        }
-
-       trace_device_pm_report_time(dev, info, nsecs, pm_verb(state.event),
-                                   error);
 }
 
 /**
@@ -387,7 +384,9 @@ static int dpm_run_callback(pm_callback_t cb, struct device *dev,
        calltime = initcall_debug_start(dev);
 
        pm_dev_dbg(dev, state, info);
+       trace_device_pm_callback_start(dev, info, state.event);
        error = cb(dev);
+       trace_device_pm_callback_end(dev, error);
        suspend_report_result(cb, error);
 
        initcall_debug_report(dev, calltime, error, state, info);
@@ -545,6 +544,7 @@ static void dpm_resume_noirq(pm_message_t state)
        struct device *dev;
        ktime_t starttime = ktime_get();
 
+       trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, true);
        mutex_lock(&dpm_list_mtx);
        pm_transition = state;
 
@@ -587,6 +587,7 @@ static void dpm_resume_noirq(pm_message_t state)
        dpm_show_time(starttime, state, "noirq");
        resume_device_irqs();
        cpuidle_resume();
+       trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
 }
 
 /**
@@ -664,6 +665,7 @@ static void dpm_resume_early(pm_message_t state)
        struct device *dev;
        ktime_t starttime = ktime_get();
 
+       trace_suspend_resume(TPS("dpm_resume_early"), state.event, true);
        mutex_lock(&dpm_list_mtx);
        pm_transition = state;
 
@@ -703,6 +705,7 @@ static void dpm_resume_early(pm_message_t state)
        mutex_unlock(&dpm_list_mtx);
        async_synchronize_full();
        dpm_show_time(starttime, state, "early");
+       trace_suspend_resume(TPS("dpm_resume_early"), state.event, false);
 }
 
 /**
@@ -834,6 +837,7 @@ void dpm_resume(pm_message_t state)
        struct device *dev;
        ktime_t starttime = ktime_get();
 
+       trace_suspend_resume(TPS("dpm_resume"), state.event, true);
        might_sleep();
 
        mutex_lock(&dpm_list_mtx);
@@ -875,6 +879,7 @@ void dpm_resume(pm_message_t state)
        dpm_show_time(starttime, state, NULL);
 
        cpufreq_resume();
+       trace_suspend_resume(TPS("dpm_resume"), state.event, false);
 }
 
 /**
@@ -913,7 +918,9 @@ static void device_complete(struct device *dev, pm_message_t state)
 
        if (callback) {
                pm_dev_dbg(dev, state, info);
+               trace_device_pm_callback_start(dev, info, state.event);
                callback(dev);
+               trace_device_pm_callback_end(dev, 0);
        }
 
        device_unlock(dev);
@@ -932,6 +939,7 @@ void dpm_complete(pm_message_t state)
 {
        struct list_head list;
 
+       trace_suspend_resume(TPS("dpm_complete"), state.event, true);
        might_sleep();
 
        INIT_LIST_HEAD(&list);
@@ -951,6 +959,7 @@ void dpm_complete(pm_message_t state)
        }
        list_splice(&list, &dpm_list);
        mutex_unlock(&dpm_list_mtx);
+       trace_suspend_resume(TPS("dpm_complete"), state.event, false);
 }
 
 /**
@@ -1086,6 +1095,7 @@ static int dpm_suspend_noirq(pm_message_t state)
        ktime_t starttime = ktime_get();
        int error = 0;
 
+       trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true);
        cpuidle_pause();
        suspend_device_irqs();
        mutex_lock(&dpm_list_mtx);
@@ -1126,6 +1136,7 @@ static int dpm_suspend_noirq(pm_message_t state)
        } else {
                dpm_show_time(starttime, state, "noirq");
        }
+       trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, false);
        return error;
 }
 
@@ -1222,6 +1233,7 @@ static int dpm_suspend_late(pm_message_t state)
        ktime_t starttime = ktime_get();
        int error = 0;
 
+       trace_suspend_resume(TPS("dpm_suspend_late"), state.event, true);
        mutex_lock(&dpm_list_mtx);
        pm_transition = state;
        async_error = 0;
@@ -1257,6 +1269,7 @@ static int dpm_suspend_late(pm_message_t state)
        } else {
                dpm_show_time(starttime, state, "late");
        }
+       trace_suspend_resume(TPS("dpm_suspend_late"), state.event, false);
        return error;
 }
 
@@ -1295,7 +1308,9 @@ static int legacy_suspend(struct device *dev, pm_message_t state,
 
        calltime = initcall_debug_start(dev);
 
+       trace_device_pm_callback_start(dev, info, state.event);
        error = cb(dev, state);
+       trace_device_pm_callback_end(dev, error);
        suspend_report_result(cb, error);
 
        initcall_debug_report(dev, calltime, error, state, info);
@@ -1461,6 +1476,7 @@ int dpm_suspend(pm_message_t state)
        ktime_t starttime = ktime_get();
        int error = 0;
 
+       trace_suspend_resume(TPS("dpm_suspend"), state.event, true);
        might_sleep();
 
        cpufreq_suspend();
@@ -1498,6 +1514,7 @@ int dpm_suspend(pm_message_t state)
                dpm_save_failed_step(SUSPEND_SUSPEND);
        } else
                dpm_show_time(starttime, state, NULL);
+       trace_suspend_resume(TPS("dpm_suspend"), state.event, false);
        return error;
 }
 
@@ -1549,8 +1566,11 @@ static int device_prepare(struct device *dev, pm_message_t state)
                callback = dev->driver->pm->prepare;
        }
 
-       if (callback)
+       if (callback) {
+               trace_device_pm_callback_start(dev, info, state.event);
                ret = callback(dev);
+               trace_device_pm_callback_end(dev, ret);
+       }
 
        device_unlock(dev);
 
@@ -1582,6 +1602,7 @@ int dpm_prepare(pm_message_t state)
 {
        int error = 0;
 
+       trace_suspend_resume(TPS("dpm_prepare"), state.event, true);
        might_sleep();
 
        mutex_lock(&dpm_list_mtx);
@@ -1612,6 +1633,7 @@ int dpm_prepare(pm_message_t state)
                put_device(dev);
        }
        mutex_unlock(&dpm_list_mtx);
+       trace_suspend_resume(TPS("dpm_prepare"), state.event, false);
        return error;
 }
 
index e8d11b6630eeb6ed7b815ffe2e21588965882587..dbb8350ea8dc232d713a10c9a4179e221dbcbb45 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/mutex.h>
 #include <linux/module.h>
 #include <linux/interrupt.h>
+#include <trace/events/power.h>
 
 static LIST_HEAD(syscore_ops_list);
 static DEFINE_MUTEX(syscore_ops_lock);
@@ -49,6 +50,7 @@ int syscore_suspend(void)
        struct syscore_ops *ops;
        int ret = 0;
 
+       trace_suspend_resume(TPS("syscore_suspend"), 0, true);
        pr_debug("Checking wakeup interrupts\n");
 
        /* Return error code if there are any wakeup interrupts pending. */
@@ -70,6 +72,7 @@ int syscore_suspend(void)
                                "Interrupts enabled after %pF\n", ops->suspend);
                }
 
+       trace_suspend_resume(TPS("syscore_suspend"), 0, false);
        return 0;
 
  err_out:
@@ -92,6 +95,7 @@ void syscore_resume(void)
 {
        struct syscore_ops *ops;
 
+       trace_suspend_resume(TPS("syscore_resume"), 0, true);
        WARN_ONCE(!irqs_disabled(),
                "Interrupts enabled before system core resume.\n");
 
@@ -103,6 +107,7 @@ void syscore_resume(void)
                        WARN_ONCE(!irqs_disabled(),
                                "Interrupts enabled after %pF\n", ops->resume);
                }
+       trace_suspend_resume(TPS("syscore_resume"), 0, false);
 }
 EXPORT_SYMBOL_GPL(syscore_resume);
 #endif /* CONFIG_PM_SLEEP */
index a842c71dcc211b8f036de693633345492f9a21e6..02351e2171651dc47585d4f5a25a9b5e85d303f3 100644 (file)
  * 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/nvme.h>
 #include <scsi/sg.h>
 #include <asm-generic/io-64-nonatomic-lo-hi.h>
 
-#define NVME_Q_DEPTH 1024
+#include <trace/events/block.h>
+
+#define NVME_Q_DEPTH           1024
 #define SQ_SIZE(depth)         (depth * sizeof(struct nvme_command))
 #define CQ_SIZE(depth)         (depth * sizeof(struct nvme_completion))
-#define ADMIN_TIMEOUT  (60 * HZ)
-#define IOD_TIMEOUT    (4 * NVME_IO_TIMEOUT)
+#define ADMIN_TIMEOUT          (admin_timeout * HZ)
+#define IOD_TIMEOUT            (retry_time * HZ)
+
+static unsigned char admin_timeout = 60;
+module_param(admin_timeout, byte, 0644);
+MODULE_PARM_DESC(admin_timeout, "timeout in seconds for admin commands");
 
-unsigned char io_timeout = 30;
-module_param(io_timeout, byte, 0644);
+unsigned char nvme_io_timeout = 30;
+module_param_named(io_timeout, nvme_io_timeout, byte, 0644);
 MODULE_PARM_DESC(io_timeout, "timeout in seconds for I/O");
 
+static unsigned char retry_time = 30;
+module_param(retry_time, byte, 0644);
+MODULE_PARM_DESC(retry_time, "time in seconds to retry failed I/O");
+
 static int nvme_major;
 module_param(nvme_major, int, 0);
 
@@ -67,6 +73,7 @@ static LIST_HEAD(dev_list);
 static struct task_struct *nvme_thread;
 static struct workqueue_struct *nvme_workq;
 static wait_queue_head_t nvme_kthread_wait;
+static struct notifier_block nvme_nb;
 
 static void nvme_reset_failed_dev(struct work_struct *ws);
 
@@ -199,16 +206,13 @@ static int alloc_cmdid_killable(struct nvme_queue *nvmeq, void *ctx,
 #define CMD_CTX_CANCELLED      (0x30C + CMD_CTX_BASE)
 #define CMD_CTX_COMPLETED      (0x310 + CMD_CTX_BASE)
 #define CMD_CTX_INVALID                (0x314 + CMD_CTX_BASE)
-#define CMD_CTX_FLUSH          (0x318 + CMD_CTX_BASE)
-#define CMD_CTX_ABORT          (0x31C + CMD_CTX_BASE)
+#define CMD_CTX_ABORT          (0x318 + CMD_CTX_BASE)
 
 static void special_completion(struct nvme_queue *nvmeq, void *ctx,
                                                struct nvme_completion *cqe)
 {
        if (ctx == CMD_CTX_CANCELLED)
                return;
-       if (ctx == CMD_CTX_FLUSH)
-               return;
        if (ctx == CMD_CTX_ABORT) {
                ++nvmeq->dev->abort_limit;
                return;
@@ -247,8 +251,9 @@ static void *free_cmdid(struct nvme_queue *nvmeq, int cmdid,
        void *ctx;
        struct nvme_cmd_info *info = nvme_cmd_info(nvmeq);
 
-       if (cmdid >= nvmeq->q_depth) {
-               *fn = special_completion;
+       if (cmdid >= nvmeq->q_depth || !info[cmdid].fn) {
+               if (fn)
+                       *fn = special_completion;
                return CMD_CTX_INVALID;
        }
        if (fn)
@@ -281,9 +286,17 @@ static struct nvme_queue *raw_nvmeq(struct nvme_dev *dev, int qid)
 
 static struct nvme_queue *get_nvmeq(struct nvme_dev *dev) __acquires(RCU)
 {
+       struct nvme_queue *nvmeq;
        unsigned queue_id = get_cpu_var(*dev->io_queue);
+
        rcu_read_lock();
-       return rcu_dereference(dev->queues[queue_id]);
+       nvmeq = rcu_dereference(dev->queues[queue_id]);
+       if (nvmeq)
+               return nvmeq;
+
+       rcu_read_unlock();
+       put_cpu_var(*dev->io_queue);
+       return NULL;
 }
 
 static void put_nvmeq(struct nvme_queue *nvmeq) __releases(RCU)
@@ -295,8 +308,15 @@ static void put_nvmeq(struct nvme_queue *nvmeq) __releases(RCU)
 static struct nvme_queue *lock_nvmeq(struct nvme_dev *dev, int q_idx)
                                                        __acquires(RCU)
 {
+       struct nvme_queue *nvmeq;
+
        rcu_read_lock();
-       return rcu_dereference(dev->queues[q_idx]);
+       nvmeq = rcu_dereference(dev->queues[q_idx]);
+       if (nvmeq)
+               return nvmeq;
+
+       rcu_read_unlock();
+       return NULL;
 }
 
 static void unlock_nvmeq(struct nvme_queue *nvmeq) __releases(RCU)
@@ -387,25 +407,30 @@ void nvme_free_iod(struct nvme_dev *dev, struct nvme_iod *iod)
 static void nvme_start_io_acct(struct bio *bio)
 {
        struct gendisk *disk = bio->bi_bdev->bd_disk;
-       const int rw = bio_data_dir(bio);
-       int cpu = part_stat_lock();
-       part_round_stats(cpu, &disk->part0);
-       part_stat_inc(cpu, &disk->part0, ios[rw]);
-       part_stat_add(cpu, &disk->part0, sectors[rw], bio_sectors(bio));
-       part_inc_in_flight(&disk->part0, rw);
-       part_stat_unlock();
+       if (blk_queue_io_stat(disk->queue)) {
+               const int rw = bio_data_dir(bio);
+               int cpu = part_stat_lock();
+               part_round_stats(cpu, &disk->part0);
+               part_stat_inc(cpu, &disk->part0, ios[rw]);
+               part_stat_add(cpu, &disk->part0, sectors[rw],
+                                                       bio_sectors(bio));
+               part_inc_in_flight(&disk->part0, rw);
+               part_stat_unlock();
+       }
 }
 
 static void nvme_end_io_acct(struct bio *bio, unsigned long start_time)
 {
        struct gendisk *disk = bio->bi_bdev->bd_disk;
-       const int rw = bio_data_dir(bio);
-       unsigned long duration = jiffies - start_time;
-       int cpu = part_stat_lock();
-       part_stat_add(cpu, &disk->part0, ticks[rw], duration);
-       part_round_stats(cpu, &disk->part0);
-       part_dec_in_flight(&disk->part0, rw);
-       part_stat_unlock();
+       if (blk_queue_io_stat(disk->queue)) {
+               const int rw = bio_data_dir(bio);
+               unsigned long duration = jiffies - start_time;
+               int cpu = part_stat_lock();
+               part_stat_add(cpu, &disk->part0, ticks[rw], duration);
+               part_round_stats(cpu, &disk->part0);
+               part_dec_in_flight(&disk->part0, rw);
+               part_stat_unlock();
+       }
 }
 
 static void bio_completion(struct nvme_queue *nvmeq, void *ctx,
@@ -414,6 +439,7 @@ static void bio_completion(struct nvme_queue *nvmeq, void *ctx,
        struct nvme_iod *iod = ctx;
        struct bio *bio = iod->private;
        u16 status = le16_to_cpup(&cqe->status) >> 1;
+       int error = 0;
 
        if (unlikely(status)) {
                if (!(status & NVME_SC_DNR ||
@@ -426,6 +452,7 @@ static void bio_completion(struct nvme_queue *nvmeq, void *ctx,
                        wake_up(&nvmeq->sq_full);
                        return;
                }
+               error = -EIO;
        }
        if (iod->nents) {
                dma_unmap_sg(nvmeq->q_dmadev, iod->sg, iod->nents,
@@ -433,10 +460,9 @@ static void bio_completion(struct nvme_queue *nvmeq, void *ctx,
                nvme_end_io_acct(bio, iod->start_time);
        }
        nvme_free_iod(nvmeq->dev, iod);
-       if (status)
-               bio_endio(bio, -EIO);
-       else
-               bio_endio(bio, 0);
+
+       trace_block_bio_complete(bdev_get_queue(bio->bi_bdev), bio, error);
+       bio_endio(bio, error);
 }
 
 /* length is in bytes.  gfp flags indicates whether we may sleep. */
@@ -525,6 +551,8 @@ static int nvme_split_and_submit(struct bio *bio, struct nvme_queue *nvmeq,
        if (!split)
                return -ENOMEM;
 
+       trace_block_split(bdev_get_queue(bio->bi_bdev), bio,
+                                       split->bi_iter.bi_sector);
        bio_chain(split, bio);
 
        if (!waitqueue_active(&nvmeq->sq_full))
@@ -627,16 +655,6 @@ static int nvme_submit_flush(struct nvme_queue *nvmeq, struct nvme_ns *ns,
        return 0;
 }
 
-int nvme_submit_flush_data(struct nvme_queue *nvmeq, struct nvme_ns *ns)
-{
-       int cmdid = alloc_cmdid(nvmeq, (void *)CMD_CTX_FLUSH,
-                                       special_completion, NVME_IO_TIMEOUT);
-       if (unlikely(cmdid < 0))
-               return cmdid;
-
-       return nvme_submit_flush(nvmeq, ns, cmdid);
-}
-
 static int nvme_submit_iod(struct nvme_queue *nvmeq, struct nvme_iod *iod)
 {
        struct bio *bio = iod->private;
@@ -652,7 +670,7 @@ static int nvme_submit_iod(struct nvme_queue *nvmeq, struct nvme_iod *iod)
 
        if (bio->bi_rw & REQ_DISCARD)
                return nvme_submit_discard(nvmeq, ns, bio, iod, cmdid);
-       if ((bio->bi_rw & REQ_FLUSH) && !iod->nents)
+       if (bio->bi_rw & REQ_FLUSH)
                return nvme_submit_flush(nvmeq, ns, cmdid);
 
        control = 0;
@@ -686,6 +704,26 @@ static int nvme_submit_iod(struct nvme_queue *nvmeq, struct nvme_iod *iod)
        return 0;
 }
 
+static int nvme_split_flush_data(struct nvme_queue *nvmeq, struct bio *bio)
+{
+       struct bio *split = bio_clone(bio, GFP_ATOMIC);
+       if (!split)
+               return -ENOMEM;
+
+       split->bi_iter.bi_size = 0;
+       split->bi_phys_segments = 0;
+       bio->bi_rw &= ~REQ_FLUSH;
+       bio_chain(split, bio);
+
+       if (!waitqueue_active(&nvmeq->sq_full))
+               add_wait_queue(&nvmeq->sq_full, &nvmeq->sq_cong_wait);
+       bio_list_add(&nvmeq->sq_cong, split);
+       bio_list_add(&nvmeq->sq_cong, bio);
+       wake_up_process(nvme_thread);
+
+       return 0;
+}
+
 /*
  * Called with local interrupts disabled and the q_lock held.  May not sleep.
  */
@@ -696,11 +734,8 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns,
        int psegs = bio_phys_segments(ns->queue, bio);
        int result;
 
-       if ((bio->bi_rw & REQ_FLUSH) && psegs) {
-               result = nvme_submit_flush_data(nvmeq, ns);
-               if (result)
-                       return result;
-       }
+       if ((bio->bi_rw & REQ_FLUSH) && psegs)
+               return nvme_split_flush_data(nvmeq, bio);
 
        iod = nvme_alloc_iod(psegs, bio->bi_iter.bi_size, GFP_ATOMIC);
        if (!iod)
@@ -795,7 +830,6 @@ static void nvme_make_request(struct request_queue *q, struct bio *bio)
        int result = -EBUSY;
 
        if (!nvmeq) {
-               put_nvmeq(NULL);
                bio_endio(bio, -EIO);
                return;
        }
@@ -870,10 +904,8 @@ static int nvme_submit_sync_cmd(struct nvme_dev *dev, int q_idx,
        struct nvme_queue *nvmeq;
 
        nvmeq = lock_nvmeq(dev, q_idx);
-       if (!nvmeq) {
-               unlock_nvmeq(nvmeq);
+       if (!nvmeq)
                return -ENODEV;
-       }
 
        cmdinfo.task = current;
        cmdinfo.status = -EINTR;
@@ -898,9 +930,10 @@ static int nvme_submit_sync_cmd(struct nvme_dev *dev, int q_idx,
 
        if (cmdinfo.status == -EINTR) {
                nvmeq = lock_nvmeq(dev, q_idx);
-               if (nvmeq)
+               if (nvmeq) {
                        nvme_abort_command(nvmeq, cmdid);
-               unlock_nvmeq(nvmeq);
+                       unlock_nvmeq(nvmeq);
+               }
                return -EINTR;
        }
 
@@ -1358,7 +1391,8 @@ static int nvme_wait_ready(struct nvme_dev *dev, u64 cap, bool enabled)
                        return -EINTR;
                if (time_after(jiffies, timeout)) {
                        dev_err(&dev->pci_dev->dev,
-                               "Device not ready; aborting initialisation\n");
+                               "Device not ready; aborting %s\n", enabled ?
+                                               "initialisation" : "reset");
                        return -ENODEV;
                }
        }
@@ -1481,7 +1515,11 @@ struct nvme_iod *nvme_map_user_pages(struct nvme_dev *dev, int write,
                goto put_pages;
        }
 
+       err = -ENOMEM;
        iod = nvme_alloc_iod(count, length, GFP_KERNEL);
+       if (!iod)
+               goto put_pages;
+
        sg = iod->sg;
        sg_init_table(sg, count);
        for (i = 0; i < count; i++) {
@@ -1494,7 +1532,6 @@ struct nvme_iod *nvme_map_user_pages(struct nvme_dev *dev, int write,
        sg_mark_end(&sg[i - 1]);
        iod->nents = count;
 
-       err = -ENOMEM;
        nents = dma_map_sg(&dev->pci_dev->dev, sg, count,
                                write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
        if (!nents)
@@ -1894,6 +1931,8 @@ static struct nvme_ns *nvme_alloc_ns(struct nvme_dev *dev, unsigned nsid,
        blk_queue_logical_block_size(ns->queue, 1 << ns->lba_shift);
        if (dev->max_hw_sectors)
                blk_queue_max_hw_sectors(ns->queue, dev->max_hw_sectors);
+       if (dev->vwc & NVME_CTRL_VWC_PRESENT)
+               blk_queue_flush(ns->queue, REQ_FLUSH | REQ_FUA);
 
        disk->major = nvme_major;
        disk->first_minor = 0;
@@ -2062,8 +2101,13 @@ static int set_queue_count(struct nvme_dev *dev, int count)
 
        status = nvme_set_features(dev, NVME_FEAT_NUM_QUEUES, q_count, 0,
                                                                &result);
-       if (status)
-               return status < 0 ? -EIO : -EBUSY;
+       if (status < 0)
+               return status;
+       if (status > 0) {
+               dev_err(&dev->pci_dev->dev, "Could not set queue count (%d)\n",
+                                                                       status);
+               return -EBUSY;
+       }
        return min(result & 0xffff, result >> 16) + 1;
 }
 
@@ -2072,14 +2116,25 @@ static size_t db_bar_size(struct nvme_dev *dev, unsigned nr_io_queues)
        return 4096 + ((nr_io_queues + 1) * 8 * dev->db_stride);
 }
 
+static void nvme_cpu_workfn(struct work_struct *work)
+{
+       struct nvme_dev *dev = container_of(work, struct nvme_dev, cpu_work);
+       if (dev->initialized)
+               nvme_assign_io_queues(dev);
+}
+
 static int nvme_cpu_notify(struct notifier_block *self,
                                unsigned long action, void *hcpu)
 {
-       struct nvme_dev *dev = container_of(self, struct nvme_dev, nb);
+       struct nvme_dev *dev;
+
        switch (action) {
        case CPU_ONLINE:
        case CPU_DEAD:
-               nvme_assign_io_queues(dev);
+               spin_lock(&dev_list_lock);
+               list_for_each_entry(dev, &dev_list, node)
+                       schedule_work(&dev->cpu_work);
+               spin_unlock(&dev_list_lock);
                break;
        }
        return NOTIFY_OK;
@@ -2148,11 +2203,6 @@ static int nvme_setup_io_queues(struct nvme_dev *dev)
        nvme_free_queues(dev, nr_io_queues + 1);
        nvme_assign_io_queues(dev);
 
-       dev->nb.notifier_call = &nvme_cpu_notify;
-       result = register_hotcpu_notifier(&dev->nb);
-       if (result)
-               goto free_queues;
-
        return 0;
 
  free_queues:
@@ -2184,6 +2234,7 @@ static int nvme_dev_add(struct nvme_dev *dev)
 
        res = nvme_identify(dev, 0, 1, dma_addr);
        if (res) {
+               dev_err(&pdev->dev, "Identify Controller failed (%d)\n", res);
                res = -EIO;
                goto out;
        }
@@ -2192,6 +2243,7 @@ static int nvme_dev_add(struct nvme_dev *dev)
        nn = le32_to_cpup(&ctrl->nn);
        dev->oncs = le16_to_cpup(&ctrl->oncs);
        dev->abort_limit = ctrl->acl + 1;
+       dev->vwc = ctrl->vwc;
        memcpy(dev->serial, ctrl->sn, sizeof(ctrl->sn));
        memcpy(dev->model, ctrl->mn, sizeof(ctrl->mn));
        memcpy(dev->firmware_rev, ctrl->fr, sizeof(ctrl->fr));
@@ -2450,8 +2502,6 @@ static void nvme_dev_shutdown(struct nvme_dev *dev)
        int i;
 
        dev->initialized = 0;
-       unregister_hotcpu_notifier(&dev->nb);
-
        nvme_dev_list_remove(dev);
 
        if (!dev->bar || (dev->bar && readl(&dev->bar->csts) == -1)) {
@@ -2722,6 +2772,7 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        INIT_LIST_HEAD(&dev->namespaces);
        dev->reset_workfn = nvme_reset_failed_dev;
        INIT_WORK(&dev->reset_work, nvme_reset_workfn);
+       INIT_WORK(&dev->cpu_work, nvme_cpu_workfn);
        dev->pci_dev = pdev;
        pci_set_drvdata(pdev, dev);
        result = nvme_set_instance(dev);
@@ -2801,6 +2852,7 @@ static void nvme_remove(struct pci_dev *pdev)
 
        pci_set_drvdata(pdev, NULL);
        flush_work(&dev->reset_work);
+       flush_work(&dev->cpu_work);
        misc_deregister(&dev->miscdev);
        nvme_dev_remove(dev);
        nvme_dev_shutdown(dev);
@@ -2889,11 +2941,18 @@ static int __init nvme_init(void)
        else if (result > 0)
                nvme_major = result;
 
-       result = pci_register_driver(&nvme_driver);
+       nvme_nb.notifier_call = &nvme_cpu_notify;
+       result = register_hotcpu_notifier(&nvme_nb);
        if (result)
                goto unregister_blkdev;
+
+       result = pci_register_driver(&nvme_driver);
+       if (result)
+               goto unregister_hotcpu;
        return 0;
 
+ unregister_hotcpu:
+       unregister_hotcpu_notifier(&nvme_nb);
  unregister_blkdev:
        unregister_blkdev(nvme_major, "nvme");
  kill_workq:
@@ -2904,9 +2963,11 @@ static int __init nvme_init(void)
 static void __exit nvme_exit(void)
 {
        pci_unregister_driver(&nvme_driver);
+       unregister_hotcpu_notifier(&nvme_nb);
        unregister_blkdev(nvme_major, "nvme");
        destroy_workqueue(nvme_workq);
        BUG_ON(nvme_thread && !IS_ERR(nvme_thread));
+       _nvme_check_size();
 }
 
 MODULE_AUTHOR("Matthew Wilcox <willy@linux.intel.com>");
index 2c3f5be06da1078aa28a1a42e070495d4e761367..a4cd6d691c63569f5e4757d5b32886edd7701815 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * NVM Express device driver
- * Copyright (c) 2011, Intel Corporation.
+ * Copyright (c) 2011-2014, 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,
  * 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.
  */
 
 /*
@@ -243,8 +239,6 @@ static int sg_version_num = 30534;  /* 2 digits for each component */
 #define READ_CAP_16_RESP_SIZE                          32
 
 /* NVMe Namespace and Command Defines */
-#define NVME_GET_SMART_LOG_PAGE                                0x02
-#define NVME_GET_FEAT_TEMP_THRESH                      0x04
 #define BYTES_TO_DWORDS                                        4
 #define NVME_MAX_FIRMWARE_SLOT                         7
 
@@ -686,6 +680,7 @@ static int nvme_trans_standard_inquiry_page(struct nvme_ns *ns,
        u8 resp_data_format = 0x02;
        u8 protect;
        u8 cmdque = 0x01 << 1;
+       u8 fw_offset = sizeof(dev->firmware_rev);
 
        mem = dma_alloc_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ns),
                                &dma_addr, GFP_KERNEL);
@@ -721,7 +716,11 @@ static int nvme_trans_standard_inquiry_page(struct nvme_ns *ns,
        inq_response[7] = cmdque;       /* wbus16=0 | sync=0 | vs=0 */
        strncpy(&inq_response[8], "NVMe    ", 8);
        strncpy(&inq_response[16], dev->model, 16);
-       strncpy(&inq_response[32], dev->firmware_rev, 4);
+
+       while (dev->firmware_rev[fw_offset - 1] == ' ' && fw_offset > 4)
+               fw_offset--;
+       fw_offset -= 4;
+       strncpy(&inq_response[32], dev->firmware_rev + fw_offset, 4);
 
        xfer_len = min(alloc_len, STANDARD_INQUIRY_LENGTH);
        res = nvme_trans_copy_to_user(hdr, inq_response, xfer_len);
@@ -1018,8 +1017,8 @@ static int nvme_trans_log_info_exceptions(struct nvme_ns *ns,
        c.common.opcode = nvme_admin_get_log_page;
        c.common.nsid = cpu_to_le32(0xFFFFFFFF);
        c.common.prp1 = cpu_to_le64(dma_addr);
-       c.common.cdw10[0] = cpu_to_le32(((sizeof(struct nvme_smart_log) /
-                       BYTES_TO_DWORDS) << 16) | NVME_GET_SMART_LOG_PAGE);
+       c.common.cdw10[0] = cpu_to_le32((((sizeof(struct nvme_smart_log) /
+                       BYTES_TO_DWORDS) - 1) << 16) | NVME_LOG_SMART);
        res = nvme_submit_admin_cmd(dev, &c, NULL);
        if (res != NVME_SC_SUCCESS) {
                temp_c = LOG_TEMP_UNKNOWN;
@@ -1086,8 +1085,8 @@ static int nvme_trans_log_temperature(struct nvme_ns *ns, struct sg_io_hdr *hdr,
        c.common.opcode = nvme_admin_get_log_page;
        c.common.nsid = cpu_to_le32(0xFFFFFFFF);
        c.common.prp1 = cpu_to_le64(dma_addr);
-       c.common.cdw10[0] = cpu_to_le32(((sizeof(struct nvme_smart_log) /
-                       BYTES_TO_DWORDS) << 16) | NVME_GET_SMART_LOG_PAGE);
+       c.common.cdw10[0] = cpu_to_le32((((sizeof(struct nvme_smart_log) /
+                       BYTES_TO_DWORDS) - 1) << 16) | NVME_LOG_SMART);
        res = nvme_submit_admin_cmd(dev, &c, NULL);
        if (res != NVME_SC_SUCCESS) {
                temp_c_cur = LOG_TEMP_UNKNOWN;
@@ -1477,7 +1476,7 @@ static int nvme_trans_power_state(struct nvme_ns *ns, struct sg_io_hdr *hdr,
                goto out_dma;
        }
        id_ctrl = mem;
-       lowest_pow_st = id_ctrl->npss - 1;
+       lowest_pow_st = max(POWER_STATE_0, (int)(id_ctrl->npss - 1));
 
        switch (pc) {
        case NVME_POWER_STATE_START_VALID:
@@ -1494,20 +1493,19 @@ static int nvme_trans_power_state(struct nvme_ns *ns, struct sg_io_hdr *hdr,
                break;
        case NVME_POWER_STATE_IDLE:
                /* Action unspecified if POWER CONDITION MODIFIER != [0,1,2] */
-               /* min of desired state and (lps-1) because lps is STOP */
                if (pcmod == 0x0)
-                       ps_desired = min(POWER_STATE_1, (lowest_pow_st - 1));
+                       ps_desired = POWER_STATE_1;
                else if (pcmod == 0x1)
-                       ps_desired = min(POWER_STATE_2, (lowest_pow_st - 1));
+                       ps_desired = POWER_STATE_2;
                else if (pcmod == 0x2)
-                       ps_desired = min(POWER_STATE_3, (lowest_pow_st - 1));
+                       ps_desired = POWER_STATE_3;
                break;
        case NVME_POWER_STATE_STANDBY:
                /* Action unspecified if POWER CONDITION MODIFIER != [0,1] */
                if (pcmod == 0x0)
-                       ps_desired = max(0, (lowest_pow_st - 2));
+                       ps_desired = max(POWER_STATE_0, (lowest_pow_st - 2));
                else if (pcmod == 0x1)
-                       ps_desired = max(0, (lowest_pow_st - 1));
+                       ps_desired = max(POWER_STATE_0, (lowest_pow_st - 1));
                break;
        case NVME_POWER_STATE_LU_CONTROL:
        default:
index 4c95b503b09ee3593ecfe1e2ca035788295b9f80..bbeb404b3a07068ae2d3c94a44d10fbe6101d080 100644 (file)
@@ -541,7 +541,6 @@ static int rbd_open(struct block_device *bdev, fmode_t mode)
                return -ENOENT;
 
        (void) get_device(&rbd_dev->dev);
-       set_device_ro(bdev, rbd_dev->mapping.read_only);
 
        return 0;
 }
@@ -559,10 +558,76 @@ static void rbd_release(struct gendisk *disk, fmode_t mode)
        put_device(&rbd_dev->dev);
 }
 
+static int rbd_ioctl_set_ro(struct rbd_device *rbd_dev, unsigned long arg)
+{
+       int ret = 0;
+       int val;
+       bool ro;
+       bool ro_changed = false;
+
+       /* get_user() may sleep, so call it before taking rbd_dev->lock */
+       if (get_user(val, (int __user *)(arg)))
+               return -EFAULT;
+
+       ro = val ? true : false;
+       /* Snapshot doesn't allow to write*/
+       if (rbd_dev->spec->snap_id != CEPH_NOSNAP && !ro)
+               return -EROFS;
+
+       spin_lock_irq(&rbd_dev->lock);
+       /* prevent others open this device */
+       if (rbd_dev->open_count > 1) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       if (rbd_dev->mapping.read_only != ro) {
+               rbd_dev->mapping.read_only = ro;
+               ro_changed = true;
+       }
+
+out:
+       spin_unlock_irq(&rbd_dev->lock);
+       /* set_disk_ro() may sleep, so call it after releasing rbd_dev->lock */
+       if (ret == 0 && ro_changed)
+               set_disk_ro(rbd_dev->disk, ro ? 1 : 0);
+
+       return ret;
+}
+
+static int rbd_ioctl(struct block_device *bdev, fmode_t mode,
+                       unsigned int cmd, unsigned long arg)
+{
+       struct rbd_device *rbd_dev = bdev->bd_disk->private_data;
+       int ret = 0;
+
+       switch (cmd) {
+       case BLKROSET:
+               ret = rbd_ioctl_set_ro(rbd_dev, arg);
+               break;
+       default:
+               ret = -ENOTTY;
+       }
+
+       return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static int rbd_compat_ioctl(struct block_device *bdev, fmode_t mode,
+                               unsigned int cmd, unsigned long arg)
+{
+       return rbd_ioctl(bdev, mode, cmd, arg);
+}
+#endif /* CONFIG_COMPAT */
+
 static const struct block_device_operations rbd_bd_ops = {
        .owner                  = THIS_MODULE,
        .open                   = rbd_open,
        .release                = rbd_release,
+       .ioctl                  = rbd_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl           = rbd_compat_ioctl,
+#endif
 };
 
 /*
@@ -1382,6 +1447,13 @@ static void rbd_obj_request_put(struct rbd_obj_request *obj_request)
        kref_put(&obj_request->kref, rbd_obj_request_destroy);
 }
 
+static void rbd_img_request_get(struct rbd_img_request *img_request)
+{
+       dout("%s: img %p (was %d)\n", __func__, img_request,
+            atomic_read(&img_request->kref.refcount));
+       kref_get(&img_request->kref);
+}
+
 static bool img_request_child_test(struct rbd_img_request *img_request);
 static void rbd_parent_request_destroy(struct kref *kref);
 static void rbd_img_request_destroy(struct kref *kref);
@@ -2142,6 +2214,7 @@ static void rbd_img_obj_callback(struct rbd_obj_request *obj_request)
        img_request->next_completion = which;
 out:
        spin_unlock_irq(&img_request->completion_lock);
+       rbd_img_request_put(img_request);
 
        if (!more)
                rbd_img_request_complete(img_request);
@@ -2242,6 +2315,7 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,
                        goto out_unwind;
                obj_request->osd_req = osd_req;
                obj_request->callback = rbd_img_obj_callback;
+               rbd_img_request_get(img_request);
 
                if (write_request) {
                        osd_req_op_alloc_hint_init(osd_req, which,
@@ -2872,56 +2946,55 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data)
 }
 
 /*
- * Request sync osd watch/unwatch.  The value of "start" determines
- * whether a watch request is being initiated or torn down.
+ * Initiate a watch request, synchronously.
  */
-static int __rbd_dev_header_watch_sync(struct rbd_device *rbd_dev, bool start)
+static int rbd_dev_header_watch_sync(struct rbd_device *rbd_dev)
 {
        struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
        struct rbd_obj_request *obj_request;
        int ret;
 
-       rbd_assert(start ^ !!rbd_dev->watch_event);
-       rbd_assert(start ^ !!rbd_dev->watch_request);
+       rbd_assert(!rbd_dev->watch_event);
+       rbd_assert(!rbd_dev->watch_request);
 
-       if (start) {
-               ret = ceph_osdc_create_event(osdc, rbd_watch_cb, rbd_dev,
-                                               &rbd_dev->watch_event);
-               if (ret < 0)
-                       return ret;
-               rbd_assert(rbd_dev->watch_event != NULL);
-       }
+       ret = ceph_osdc_create_event(osdc, rbd_watch_cb, rbd_dev,
+                                    &rbd_dev->watch_event);
+       if (ret < 0)
+               return ret;
+
+       rbd_assert(rbd_dev->watch_event);
 
-       ret = -ENOMEM;
        obj_request = rbd_obj_request_create(rbd_dev->header_name, 0, 0,
-                                                       OBJ_REQUEST_NODATA);
-       if (!obj_request)
+                                            OBJ_REQUEST_NODATA);
+       if (!obj_request) {
+               ret = -ENOMEM;
                goto out_cancel;
+       }
 
        obj_request->osd_req = rbd_osd_req_create(rbd_dev, true, 1,
                                                  obj_request);
-       if (!obj_request->osd_req)
-               goto out_cancel;
+       if (!obj_request->osd_req) {
+               ret = -ENOMEM;
+               goto out_put;
+       }
 
-       if (start)
-               ceph_osdc_set_request_linger(osdc, obj_request->osd_req);
-       else
-               ceph_osdc_unregister_linger_request(osdc,
-                                       rbd_dev->watch_request->osd_req);
+       ceph_osdc_set_request_linger(osdc, obj_request->osd_req);
 
        osd_req_op_watch_init(obj_request->osd_req, 0, CEPH_OSD_OP_WATCH,
-                               rbd_dev->watch_event->cookie, 0, start ? 1 : 0);
+                             rbd_dev->watch_event->cookie, 0, 1);
        rbd_osd_req_format_write(obj_request);
 
        ret = rbd_obj_request_submit(osdc, obj_request);
        if (ret)
-               goto out_cancel;
+               goto out_linger;
+
        ret = rbd_obj_request_wait(obj_request);
        if (ret)
-               goto out_cancel;
+               goto out_linger;
+
        ret = obj_request->result;
        if (ret)
-               goto out_cancel;
+               goto out_linger;
 
        /*
         * A watch request is set to linger, so the underlying osd
@@ -2931,36 +3004,84 @@ static int __rbd_dev_header_watch_sync(struct rbd_device *rbd_dev, bool start)
         * it.  We'll drop that reference (below) after we've
         * unregistered it.
         */
-       if (start) {
-               rbd_dev->watch_request = obj_request;
+       rbd_dev->watch_request = obj_request;
 
-               return 0;
+       return 0;
+
+out_linger:
+       ceph_osdc_unregister_linger_request(osdc, obj_request->osd_req);
+out_put:
+       rbd_obj_request_put(obj_request);
+out_cancel:
+       ceph_osdc_cancel_event(rbd_dev->watch_event);
+       rbd_dev->watch_event = NULL;
+
+       return ret;
+}
+
+/*
+ * Tear down a watch request, synchronously.
+ */
+static int __rbd_dev_header_unwatch_sync(struct rbd_device *rbd_dev)
+{
+       struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
+       struct rbd_obj_request *obj_request;
+       int ret;
+
+       rbd_assert(rbd_dev->watch_event);
+       rbd_assert(rbd_dev->watch_request);
+
+       obj_request = rbd_obj_request_create(rbd_dev->header_name, 0, 0,
+                                            OBJ_REQUEST_NODATA);
+       if (!obj_request) {
+               ret = -ENOMEM;
+               goto out_cancel;
+       }
+
+       obj_request->osd_req = rbd_osd_req_create(rbd_dev, true, 1,
+                                                 obj_request);
+       if (!obj_request->osd_req) {
+               ret = -ENOMEM;
+               goto out_put;
        }
 
+       osd_req_op_watch_init(obj_request->osd_req, 0, CEPH_OSD_OP_WATCH,
+                             rbd_dev->watch_event->cookie, 0, 0);
+       rbd_osd_req_format_write(obj_request);
+
+       ret = rbd_obj_request_submit(osdc, obj_request);
+       if (ret)
+               goto out_put;
+
+       ret = rbd_obj_request_wait(obj_request);
+       if (ret)
+               goto out_put;
+
+       ret = obj_request->result;
+       if (ret)
+               goto out_put;
+
        /* We have successfully torn down the watch request */
 
+       ceph_osdc_unregister_linger_request(osdc,
+                                           rbd_dev->watch_request->osd_req);
        rbd_obj_request_put(rbd_dev->watch_request);
        rbd_dev->watch_request = NULL;
+
+out_put:
+       rbd_obj_request_put(obj_request);
 out_cancel:
-       /* Cancel the event if we're tearing down, or on error */
        ceph_osdc_cancel_event(rbd_dev->watch_event);
        rbd_dev->watch_event = NULL;
-       if (obj_request)
-               rbd_obj_request_put(obj_request);
 
        return ret;
 }
 
-static int rbd_dev_header_watch_sync(struct rbd_device *rbd_dev)
-{
-       return __rbd_dev_header_watch_sync(rbd_dev, true);
-}
-
 static void rbd_dev_header_unwatch_sync(struct rbd_device *rbd_dev)
 {
        int ret;
 
-       ret = __rbd_dev_header_watch_sync(rbd_dev, false);
+       ret = __rbd_dev_header_unwatch_sync(rbd_dev);
        if (ret) {
                rbd_warn(rbd_dev, "unable to tear down watch request: %d\n",
                         ret);
@@ -3058,7 +3179,6 @@ static void rbd_request_fn(struct request_queue *q)
                __releases(q->queue_lock) __acquires(q->queue_lock)
 {
        struct rbd_device *rbd_dev = q->queuedata;
-       bool read_only = rbd_dev->mapping.read_only;
        struct request *rq;
        int result;
 
@@ -3094,7 +3214,7 @@ static void rbd_request_fn(struct request_queue *q)
 
                if (write_request) {
                        result = -EROFS;
-                       if (read_only)
+                       if (rbd_dev->mapping.read_only)
                                goto end_request;
                        rbd_assert(rbd_dev->spec->snap_id == CEPH_NOSNAP);
                }
@@ -4682,6 +4802,38 @@ out_err:
        return ret;
 }
 
+/*
+ * Return pool id (>= 0) or a negative error code.
+ */
+static int rbd_add_get_pool_id(struct rbd_client *rbdc, const char *pool_name)
+{
+       u64 newest_epoch;
+       unsigned long timeout = rbdc->client->options->mount_timeout * HZ;
+       int tries = 0;
+       int ret;
+
+again:
+       ret = ceph_pg_poolid_by_name(rbdc->client->osdc.osdmap, pool_name);
+       if (ret == -ENOENT && tries++ < 1) {
+               ret = ceph_monc_do_get_version(&rbdc->client->monc, "osdmap",
+                                              &newest_epoch);
+               if (ret < 0)
+                       return ret;
+
+               if (rbdc->client->osdc.osdmap->epoch < newest_epoch) {
+                       ceph_monc_request_next_osdmap(&rbdc->client->monc);
+                       (void) ceph_monc_wait_osdmap(&rbdc->client->monc,
+                                                    newest_epoch, timeout);
+                       goto again;
+               } else {
+                       /* the osdmap we have is new enough */
+                       return -ENOENT;
+               }
+       }
+
+       return ret;
+}
+
 /*
  * An rbd format 2 image has a unique identifier, distinct from the
  * name given to it by the user.  Internally, that identifier is
@@ -4752,7 +4904,7 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev)
 
                image_id = ceph_extract_encoded_string(&p, p + ret,
                                                NULL, GFP_NOIO);
-               ret = IS_ERR(image_id) ? PTR_ERR(image_id) : 0;
+               ret = PTR_ERR_OR_ZERO(image_id);
                if (!ret)
                        rbd_dev->image_format = 2;
        } else {
@@ -4907,6 +5059,7 @@ static int rbd_dev_device_setup(struct rbd_device *rbd_dev)
        if (ret)
                goto err_out_disk;
        set_capacity(rbd_dev->disk, rbd_dev->mapping.size / SECTOR_SIZE);
+       set_disk_ro(rbd_dev->disk, rbd_dev->mapping.read_only);
 
        ret = rbd_bus_add_dev(rbd_dev);
        if (ret)
@@ -5053,7 +5206,6 @@ static ssize_t do_rbd_add(struct bus_type *bus,
        struct rbd_options *rbd_opts = NULL;
        struct rbd_spec *spec = NULL;
        struct rbd_client *rbdc;
-       struct ceph_osd_client *osdc;
        bool read_only;
        int rc = -ENOMEM;
 
@@ -5075,8 +5227,7 @@ static ssize_t do_rbd_add(struct bus_type *bus,
        }
 
        /* pick the pool */
-       osdc = &rbdc->client->osdc;
-       rc = ceph_pg_poolid_by_name(osdc->osdmap, spec->pool_name);
+       rc = rbd_add_get_pool_id(rbdc, spec->pool_name);
        if (rc < 0)
                goto err_out_client;
        spec->pool_id = (u64)rc;
@@ -5387,6 +5538,7 @@ err_out_slab:
 
 static void __exit rbd_exit(void)
 {
+       ida_destroy(&rbd_dev_id_ida);
        rbd_sysfs_cleanup();
        if (single_major)
                unregister_blkdev(rbd_major, RBD_DRV_NAME);
index a83b57e57b6370572d53325638355a0d94ce24bf..f98380648cb3513fe47ac19213ce0b105a0d1873 100644 (file)
@@ -193,9 +193,10 @@ static int ath3k_load_firmware(struct usb_device *udev,
        sent += 20;
        count -= 20;
 
+       pipe = usb_sndbulkpipe(udev, 0x02);
+
        while (count) {
                size = min_t(uint, count, BULK_SIZE);
-               pipe = usb_sndbulkpipe(udev, 0x02);
                memcpy(send_buf, firmware->data + sent, size);
 
                err = usb_bulk_msg(udev, pipe, send_buf, size,
index 7399303d7d9978447ff722d823c1dcece408029a..dc79f88f8717f478c8d8ab6d0a7d45849f772ac1 100644 (file)
@@ -59,6 +59,8 @@ struct btmrvl_device {
 };
 
 struct btmrvl_adapter {
+       void *hw_regs_buf;
+       u8 *hw_regs;
        u32 int_count;
        struct sk_buff_head tx_queue;
        u8 psmode;
@@ -140,7 +142,7 @@ void btmrvl_interrupt(struct btmrvl_private *priv);
 bool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb);
 int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb);
 
-int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd);
+int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd);
 int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv);
 int btmrvl_enable_ps(struct btmrvl_private *priv);
 int btmrvl_prepare_command(struct btmrvl_private *priv);
index 2c4997ce248484703a1b859c5e518396fcdbfa64..e9dbddb0b8f1efb1f15ede65f50d80390ca370c3 100644 (file)
@@ -24,6 +24,7 @@
 #include <net/bluetooth/hci_core.h>
 
 #include "btmrvl_drv.h"
+#include "btmrvl_sdio.h"
 
 #define VERSION "1.0"
 
@@ -201,7 +202,7 @@ static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode,
        return 0;
 }
 
-int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd)
+int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd)
 {
        int ret;
 
@@ -337,10 +338,25 @@ static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb)
 
 static void btmrvl_init_adapter(struct btmrvl_private *priv)
 {
+       int buf_size;
+
        skb_queue_head_init(&priv->adapter->tx_queue);
 
        priv->adapter->ps_state = PS_AWAKE;
 
+       buf_size = ALIGN_SZ(SDIO_BLOCK_SIZE, BTSDIO_DMA_ALIGN);
+       priv->adapter->hw_regs_buf = kzalloc(buf_size, GFP_KERNEL);
+       if (!priv->adapter->hw_regs_buf) {
+               priv->adapter->hw_regs = NULL;
+               BT_ERR("Unable to allocate buffer for hw_regs.");
+       } else {
+               priv->adapter->hw_regs =
+                       (u8 *)ALIGN_ADDR(priv->adapter->hw_regs_buf,
+                                        BTSDIO_DMA_ALIGN);
+               BT_DBG("hw_regs_buf=%p hw_regs=%p",
+                      priv->adapter->hw_regs_buf, priv->adapter->hw_regs);
+       }
+
        init_waitqueue_head(&priv->adapter->cmd_wait_q);
 }
 
@@ -348,6 +364,7 @@ static void btmrvl_free_adapter(struct btmrvl_private *priv)
 {
        skb_queue_purge(&priv->adapter->tx_queue);
 
+       kfree(priv->adapter->hw_regs_buf);
        kfree(priv->adapter);
 
        priv->adapter = NULL;
index 1b52c9f5230d324d0476a2d7dd3f308e7fd723d8..9dedca516ff50567a278fb9a511dbcdfd7a1980c 100644 (file)
@@ -64,6 +64,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8688 = {
        .io_port_0 = 0x00,
        .io_port_1 = 0x01,
        .io_port_2 = 0x02,
+       .int_read_to_clear = false,
 };
 static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = {
        .cfg = 0x00,
@@ -80,6 +81,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = {
        .io_port_0 = 0x78,
        .io_port_1 = 0x79,
        .io_port_2 = 0x7a,
+       .int_read_to_clear = false,
 };
 
 static const struct btmrvl_sdio_card_reg btmrvl_reg_88xx = {
@@ -97,6 +99,9 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_88xx = {
        .io_port_0 = 0xd8,
        .io_port_1 = 0xd9,
        .io_port_2 = 0xda,
+       .int_read_to_clear = true,
+       .host_int_rsr = 0x01,
+       .card_misc_cfg = 0xcc,
 };
 
 static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
@@ -667,46 +672,78 @@ static int btmrvl_sdio_process_int_status(struct btmrvl_private *priv)
        return 0;
 }
 
-static void btmrvl_sdio_interrupt(struct sdio_func *func)
+static int btmrvl_sdio_read_to_clear(struct btmrvl_sdio_card *card, u8 *ireg)
 {
-       struct btmrvl_private *priv;
-       struct btmrvl_sdio_card *card;
-       ulong flags;
-       u8 ireg = 0;
+       struct btmrvl_adapter *adapter = card->priv->adapter;
        int ret;
 
-       card = sdio_get_drvdata(func);
-       if (!card || !card->priv) {
-               BT_ERR("sbi_interrupt(%p) card or priv is "
-                               "NULL, card=%p\n", func, card);
-               return;
+       ret = sdio_readsb(card->func, adapter->hw_regs, 0, SDIO_BLOCK_SIZE);
+       if (ret) {
+               BT_ERR("sdio_readsb: read int hw_regs failed: %d", ret);
+               return ret;
        }
 
-       priv = card->priv;
+       *ireg = adapter->hw_regs[card->reg->host_intstatus];
+       BT_DBG("hw_regs[%#x]=%#x", card->reg->host_intstatus, *ireg);
+
+       return 0;
+}
 
-       ireg = sdio_readb(card->func, card->reg->host_intstatus, &ret);
+static int btmrvl_sdio_write_to_clear(struct btmrvl_sdio_card *card, u8 *ireg)
+{
+       int ret;
+
+       *ireg = sdio_readb(card->func, card->reg->host_intstatus, &ret);
        if (ret) {
-               BT_ERR("sdio_readb: read int status register failed");
-               return;
+               BT_ERR("sdio_readb: read int status failed: %d", ret);
+               return ret;
        }
 
-       if (ireg != 0) {
+       if (*ireg) {
                /*
                 * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
                 * Clear the interrupt status register and re-enable the
                 * interrupt.
                 */
-               BT_DBG("ireg = 0x%x", ireg);
+               BT_DBG("int_status = 0x%x", *ireg);
 
-               sdio_writeb(card->func, ~(ireg) & (DN_LD_HOST_INT_STATUS |
-                                       UP_LD_HOST_INT_STATUS),
-                               card->reg->host_intstatus, &ret);
+               sdio_writeb(card->func, ~(*ireg) & (DN_LD_HOST_INT_STATUS |
+                                                   UP_LD_HOST_INT_STATUS),
+                           card->reg->host_intstatus, &ret);
                if (ret) {
-                       BT_ERR("sdio_writeb: clear int status register failed");
-                       return;
+                       BT_ERR("sdio_writeb: clear int status failed: %d", ret);
+                       return ret;
                }
        }
 
+       return 0;
+}
+
+static void btmrvl_sdio_interrupt(struct sdio_func *func)
+{
+       struct btmrvl_private *priv;
+       struct btmrvl_sdio_card *card;
+       ulong flags;
+       u8 ireg = 0;
+       int ret;
+
+       card = sdio_get_drvdata(func);
+       if (!card || !card->priv) {
+               BT_ERR("sbi_interrupt(%p) card or priv is "
+                               "NULL, card=%p\n", func, card);
+               return;
+       }
+
+       priv = card->priv;
+
+       if (card->reg->int_read_to_clear)
+               ret = btmrvl_sdio_read_to_clear(card, &ireg);
+       else
+               ret = btmrvl_sdio_write_to_clear(card, &ireg);
+
+       if (ret)
+               return;
+
        spin_lock_irqsave(&priv->driver_lock, flags);
        sdio_ireg |= ireg;
        spin_unlock_irqrestore(&priv->driver_lock, flags);
@@ -777,6 +814,30 @@ static int btmrvl_sdio_register_dev(struct btmrvl_sdio_card *card)
 
        BT_DBG("SDIO FUNC%d IO port: 0x%x", func->num, card->ioport);
 
+       if (card->reg->int_read_to_clear) {
+               reg = sdio_readb(func, card->reg->host_int_rsr, &ret);
+               if (ret < 0) {
+                       ret = -EIO;
+                       goto release_irq;
+               }
+               sdio_writeb(func, reg | 0x3f, card->reg->host_int_rsr, &ret);
+               if (ret < 0) {
+                       ret = -EIO;
+                       goto release_irq;
+               }
+
+               reg = sdio_readb(func, card->reg->card_misc_cfg, &ret);
+               if (ret < 0) {
+                       ret = -EIO;
+                       goto release_irq;
+               }
+               sdio_writeb(func, reg | 0x10, card->reg->card_misc_cfg, &ret);
+               if (ret < 0) {
+                       ret = -EIO;
+                       goto release_irq;
+               }
+       }
+
        sdio_set_drvdata(func, card);
 
        sdio_release_host(func);
index 43d35a609ca9a94795afb731d230fa88ca109bef..d4dd3b0fa53d16d68da0101494e43ca4664716c2 100644 (file)
@@ -78,6 +78,9 @@ struct btmrvl_sdio_card_reg {
        u8 io_port_0;
        u8 io_port_1;
        u8 io_port_2;
+       bool int_read_to_clear;
+       u8 host_int_rsr;
+       u8 card_misc_cfg;
 };
 
 struct btmrvl_sdio_card {
index a7dfbf9a3afb6be53e372f78d9ee8202bdb17d08..a1c80b0c7663d25baf2224bae0513b3bb29c6543 100644 (file)
@@ -49,6 +49,7 @@ static struct usb_driver btusb_driver;
 #define BTUSB_WRONG_SCO_MTU    0x40
 #define BTUSB_ATH3012          0x80
 #define BTUSB_INTEL            0x100
+#define BTUSB_BCM_PATCHRAM     0x200
 
 static const struct usb_device_id btusb_table[] = {
        /* Generic Bluetooth USB device */
@@ -111,7 +112,8 @@ static const struct usb_device_id btusb_table[] = {
        { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) },
 
        /* Broadcom devices with vendor specific id */
-       { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) },
+       { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01),
+         .driver_info = BTUSB_BCM_PATCHRAM },
 
        /* Belkin F8065bf - Broadcom based */
        { USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) },
@@ -1381,6 +1383,154 @@ exit_mfg_deactivate:
        return 0;
 }
 
+static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
+{
+       struct btusb_data *data = hci_get_drvdata(hdev);
+       struct usb_device *udev = data->udev;
+       char fw_name[64];
+       const struct firmware *fw;
+       const u8 *fw_ptr;
+       size_t fw_size;
+       const struct hci_command_hdr *cmd;
+       const u8 *cmd_param;
+       u16 opcode;
+       struct sk_buff *skb;
+       struct hci_rp_read_local_version *ver;
+       long ret;
+
+       snprintf(fw_name, sizeof(fw_name), "brcm/%s-%04x-%04x.hcd",
+                udev->product ? udev->product : "BCM",
+                le16_to_cpu(udev->descriptor.idVendor),
+                le16_to_cpu(udev->descriptor.idProduct));
+
+       ret = request_firmware(&fw, fw_name, &hdev->dev);
+       if (ret < 0) {
+               BT_INFO("%s: BCM: patch %s not found", hdev->name,
+                       fw_name);
+               return 0;
+       }
+
+       /* Reset */
+       skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret);
+               goto done;
+       }
+       kfree_skb(skb);
+
+       /* Read Local Version Info */
+       skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
+                            HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
+                       hdev->name, ret);
+               goto done;
+       }
+
+       if (skb->len != sizeof(*ver)) {
+               BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
+                       hdev->name);
+               kfree_skb(skb);
+               ret = -EIO;
+               goto done;
+       }
+
+       ver = (struct hci_rp_read_local_version *) skb->data;
+       BT_INFO("%s: BCM: patching hci_ver=%02x hci_rev=%04x lmp_ver=%02x "
+               "lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev,
+               ver->lmp_ver, ver->lmp_subver);
+       kfree_skb(skb);
+
+       /* Start Download */
+       skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: BCM: Download Minidrv command failed (%ld)",
+                       hdev->name, ret);
+               goto reset_fw;
+       }
+       kfree_skb(skb);
+
+       /* 50 msec delay after Download Minidrv completes */
+       msleep(50);
+
+       fw_ptr = fw->data;
+       fw_size = fw->size;
+
+       while (fw_size >= sizeof(*cmd)) {
+               cmd = (struct hci_command_hdr *) fw_ptr;
+               fw_ptr += sizeof(*cmd);
+               fw_size -= sizeof(*cmd);
+
+               if (fw_size < cmd->plen) {
+                       BT_ERR("%s: BCM: patch %s is corrupted",
+                               hdev->name, fw_name);
+                       ret = -EINVAL;
+                       goto reset_fw;
+               }
+
+               cmd_param = fw_ptr;
+               fw_ptr += cmd->plen;
+               fw_size -= cmd->plen;
+
+               opcode = le16_to_cpu(cmd->opcode);
+
+               skb = __hci_cmd_sync(hdev, opcode, cmd->plen, cmd_param,
+                                    HCI_INIT_TIMEOUT);
+               if (IS_ERR(skb)) {
+                       ret = PTR_ERR(skb);
+                       BT_ERR("%s: BCM: patch command %04x failed (%ld)",
+                               hdev->name, opcode, ret);
+                       goto reset_fw;
+               }
+               kfree_skb(skb);
+       }
+
+       /* 250 msec delay after Launch Ram completes */
+       msleep(250);
+
+reset_fw:
+       /* Reset */
+       skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret);
+               goto done;
+       }
+       kfree_skb(skb);
+
+       /* Read Local Version Info */
+       skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
+                            HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
+                       hdev->name, ret);
+               goto done;
+       }
+
+       if (skb->len != sizeof(*ver)) {
+               BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
+                       hdev->name);
+               kfree_skb(skb);
+               ret = -EIO;
+               goto done;
+       }
+
+       ver = (struct hci_rp_read_local_version *) skb->data;
+       BT_INFO("%s: BCM: firmware hci_ver=%02x hci_rev=%04x lmp_ver=%02x "
+               "lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev,
+               ver->lmp_ver, ver->lmp_subver);
+       kfree_skb(skb);
+
+done:
+       release_firmware(fw);
+
+       return ret;
+}
+
 static int btusb_probe(struct usb_interface *intf,
                                const struct usb_device_id *id)
 {
@@ -1486,6 +1636,9 @@ static int btusb_probe(struct usb_interface *intf,
        if (id->driver_info & BTUSB_BCM92035)
                hdev->setup = btusb_setup_bcm92035;
 
+       if (id->driver_info & BTUSB_BCM_PATCHRAM)
+               hdev->setup = btusb_setup_bcm_patchram;
+
        if (id->driver_info & BTUSB_INTEL)
                hdev->setup = btusb_setup_intel;
 
index 7048a583fe51a695a044ad541a0f894e3bf7c510..66db9a803373efb92c8966c9f69fdb6b8ec7aa59 100644 (file)
@@ -55,13 +55,6 @@ struct h4_struct {
        struct sk_buff_head txq;
 };
 
-/* H4 receiver States */
-#define H4_W4_PACKET_TYPE      0
-#define H4_W4_EVENT_HDR                1
-#define H4_W4_ACL_HDR          2
-#define H4_W4_SCO_HDR          3
-#define H4_W4_DATA             4
-
 /* Initialize protocol */
 static int h4_open(struct hci_uart *hu)
 {
index 2ce0e225e58c3edbceeafa290c8db065eb4e6e72..f3e71501de5409cb9947b851d2f689ab76545391 100644 (file)
 #include <linux/virtio_rng.h>
 #include <linux/module.h>
 
-static struct virtqueue *vq;
-static unsigned int data_avail;
-static DECLARE_COMPLETION(have_data);
-static bool busy;
+static DEFINE_IDA(rng_index_ida);
+
+struct virtrng_info {
+       struct virtio_device *vdev;
+       struct hwrng hwrng;
+       struct virtqueue *vq;
+       unsigned int data_avail;
+       struct completion have_data;
+       bool busy;
+       char name[25];
+       int index;
+};
 
 static void random_recv_done(struct virtqueue *vq)
 {
+       struct virtrng_info *vi = vq->vdev->priv;
+
        /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */
-       if (!virtqueue_get_buf(vq, &data_avail))
+       if (!virtqueue_get_buf(vi->vq, &vi->data_avail))
                return;
 
-       complete(&have_data);
+       complete(&vi->have_data);
 }
 
 /* The host will fill any buffer we give it with sweet, sweet randomness. */
-static void register_buffer(u8 *buf, size_t size)
+static void register_buffer(struct virtrng_info *vi, u8 *buf, size_t size)
 {
        struct scatterlist sg;
 
        sg_init_one(&sg, buf, size);
 
        /* There should always be room for one buffer. */
-       virtqueue_add_inbuf(vq, &sg, 1, buf, GFP_KERNEL);
+       virtqueue_add_inbuf(vi->vq, &sg, 1, buf, GFP_KERNEL);
 
-       virtqueue_kick(vq);
+       virtqueue_kick(vi->vq);
 }
 
 static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
 {
        int ret;
+       struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
 
-       if (!busy) {
-               busy = true;
-               init_completion(&have_data);
-               register_buffer(buf, size);
+       if (!vi->busy) {
+               vi->busy = true;
+               init_completion(&vi->have_data);
+               register_buffer(vi, buf, size);
        }
 
        if (!wait)
                return 0;
 
-       ret = wait_for_completion_killable(&have_data);
+       ret = wait_for_completion_killable(&vi->have_data);
        if (ret < 0)
                return ret;
 
-       busy = false;
+       vi->busy = false;
 
-       return data_avail;
+       return vi->data_avail;
 }
 
 static void virtio_cleanup(struct hwrng *rng)
 {
-       if (busy)
-               wait_for_completion(&have_data);
-}
+       struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
 
-
-static struct hwrng virtio_hwrng = {
-       .name           = "virtio",
-       .cleanup        = virtio_cleanup,
-       .read           = virtio_read,
-};
+       if (vi->busy)
+               wait_for_completion(&vi->have_data);
+}
 
 static int probe_common(struct virtio_device *vdev)
 {
-       int err;
+       int err, index;
+       struct virtrng_info *vi = NULL;
+
+       vi = kzalloc(sizeof(struct virtrng_info), GFP_KERNEL);
+       if (!vi)
+               return -ENOMEM;
 
-       if (vq) {
-               /* We only support one device for now */
-               return -EBUSY;
+       vi->index = index = ida_simple_get(&rng_index_ida, 0, 0, GFP_KERNEL);
+       if (index < 0) {
+               kfree(vi);
+               return index;
        }
+       sprintf(vi->name, "virtio_rng.%d", index);
+       init_completion(&vi->have_data);
+
+       vi->hwrng = (struct hwrng) {
+               .read = virtio_read,
+               .cleanup = virtio_cleanup,
+               .priv = (unsigned long)vi,
+               .name = vi->name,
+       };
+       vdev->priv = vi;
+
        /* We expect a single virtqueue. */
-       vq = virtio_find_single_vq(vdev, random_recv_done, "input");
-       if (IS_ERR(vq)) {
-               err = PTR_ERR(vq);
-               vq = NULL;
+       vi->vq = virtio_find_single_vq(vdev, random_recv_done, "input");
+       if (IS_ERR(vi->vq)) {
+               err = PTR_ERR(vi->vq);
+               vi->vq = NULL;
+               kfree(vi);
+               ida_simple_remove(&rng_index_ida, index);
                return err;
        }
 
-       err = hwrng_register(&virtio_hwrng);
+       err = hwrng_register(&vi->hwrng);
        if (err) {
                vdev->config->del_vqs(vdev);
-               vq = NULL;
+               vi->vq = NULL;
+               kfree(vi);
+               ida_simple_remove(&rng_index_ida, index);
                return err;
        }
 
@@ -115,11 +142,13 @@ static int probe_common(struct virtio_device *vdev)
 
 static void remove_common(struct virtio_device *vdev)
 {
+       struct virtrng_info *vi = vdev->priv;
        vdev->config->reset(vdev);
-       busy = false;
-       hwrng_unregister(&virtio_hwrng);
+       vi->busy = false;
+       hwrng_unregister(&vi->hwrng);
        vdev->config->del_vqs(vdev);
-       vq = NULL;
+       ida_simple_remove(&rng_index_ida, vi->index);
+       kfree(vi);
 }
 
 static int virtrng_probe(struct virtio_device *vdev)
index 6e8d65e9b1d3c196ea2d2bd76b78530dd0387920..0102dc788608ec0060cf2fe1666e058c2d2d8076 100644 (file)
@@ -284,10 +284,10 @@ static long raw_ctl_compat_ioctl(struct file *file, unsigned int cmd,
 #endif
 
 static const struct file_operations raw_fops = {
-       .read           = do_sync_read,
-       .aio_read       = generic_file_aio_read,
-       .write          = do_sync_write,
-       .aio_write      = blkdev_aio_write,
+       .read           = new_sync_read,
+       .read_iter      = generic_file_read_iter,
+       .write          = new_sync_write,
+       .write_iter     = blkdev_write_iter,
        .fsync          = blkdev_fsync,
        .open           = raw_open,
        .release        = raw_release,
index b5bac917612c4d41e38ad081919f2204ddbef0e3..762fd64dbd1f1344f0af277b1e6fbb7ed5d5e7a1 100644 (file)
@@ -3,3 +3,7 @@
 #
 
 obj-y += clk-sunxi.o clk-factors.o
+obj-y += clk-a10-hosc.o
+obj-y += clk-a20-gmac.o
+
+obj-$(CONFIG_MFD_SUN6I_PRCM) += clk-sun6i-ar100.o clk-sun6i-apb0.o clk-sun6i-apb0-gates.o
diff --git a/drivers/clk/sunxi/clk-a10-hosc.c b/drivers/clk/sunxi/clk-a10-hosc.c
new file mode 100644 (file)
index 0000000..0481d5d
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2013 Emilio López
+ *
+ * Emilio López <emilio@elopez.com.ar>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#define SUNXI_OSC24M_GATE      0
+
+static DEFINE_SPINLOCK(hosc_lock);
+
+static void __init sun4i_osc_clk_setup(struct device_node *node)
+{
+       struct clk *clk;
+       struct clk_fixed_rate *fixed;
+       struct clk_gate *gate;
+       const char *clk_name = node->name;
+       u32 rate;
+
+       if (of_property_read_u32(node, "clock-frequency", &rate))
+               return;
+
+       /* allocate fixed-rate and gate clock structs */
+       fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL);
+       if (!fixed)
+               return;
+       gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+       if (!gate)
+               goto err_free_fixed;
+
+       of_property_read_string(node, "clock-output-names", &clk_name);
+
+       /* set up gate and fixed rate properties */
+       gate->reg = of_iomap(node, 0);
+       gate->bit_idx = SUNXI_OSC24M_GATE;
+       gate->lock = &hosc_lock;
+       fixed->fixed_rate = rate;
+
+       clk = clk_register_composite(NULL, clk_name,
+                       NULL, 0,
+                       NULL, NULL,
+                       &fixed->hw, &clk_fixed_rate_ops,
+                       &gate->hw, &clk_gate_ops,
+                       CLK_IS_ROOT);
+
+       if (IS_ERR(clk))
+               goto err_free_gate;
+
+       of_clk_add_provider(node, of_clk_src_simple_get, clk);
+       clk_register_clkdev(clk, clk_name, NULL);
+
+       return;
+
+err_free_gate:
+       kfree(gate);
+err_free_fixed:
+       kfree(fixed);
+}
+CLK_OF_DECLARE(sun4i_osc, "allwinner,sun4i-a10-osc-clk", sun4i_osc_clk_setup);
diff --git a/drivers/clk/sunxi/clk-a20-gmac.c b/drivers/clk/sunxi/clk-a20-gmac.c
new file mode 100644 (file)
index 0000000..633ddc4
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2013 Emilio López
+ * Emilio López <emilio@elopez.com.ar>
+ *
+ * Copyright 2013 Chen-Yu Tsai
+ * Chen-Yu Tsai <wens@csie.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+static DEFINE_SPINLOCK(gmac_lock);
+
+/**
+ * sun7i_a20_gmac_clk_setup - Setup function for A20/A31 GMAC clock module
+ *
+ * This clock looks something like this
+ *                               ________________________
+ *  MII TX clock from PHY >-----|___________    _________|----> to GMAC core
+ *  GMAC Int. RGMII TX clk >----|___________\__/__gate---|----> to PHY
+ *  Ext. 125MHz RGMII TX clk >--|__divider__/            |
+ *                              |________________________|
+ *
+ * The external 125 MHz reference is optional, i.e. GMAC can use its
+ * internal TX clock just fine. The A31 GMAC clock module does not have
+ * the divider controls for the external reference.
+ *
+ * To keep it simple, let the GMAC use either the MII TX clock for MII mode,
+ * and its internal TX clock for GMII and RGMII modes. The GMAC driver should
+ * select the appropriate source and gate/ungate the output to the PHY.
+ *
+ * Only the GMAC should use this clock. Altering the clock so that it doesn't
+ * match the GMAC's operation parameters will result in the GMAC not being
+ * able to send traffic out. The GMAC driver should set the clock rate and
+ * enable/disable this clock to configure the required state. The clock
+ * driver then responds by auto-reparenting the clock.
+ */
+
+#define SUN7I_A20_GMAC_GPIT    2
+#define SUN7I_A20_GMAC_MASK    0x3
+#define SUN7I_A20_GMAC_PARENTS 2
+
+static void __init sun7i_a20_gmac_clk_setup(struct device_node *node)
+{
+       struct clk *clk;
+       struct clk_mux *mux;
+       struct clk_gate *gate;
+       const char *clk_name = node->name;
+       const char *parents[SUN7I_A20_GMAC_PARENTS];
+       void *reg;
+
+       if (of_property_read_string(node, "clock-output-names", &clk_name))
+               return;
+
+       /* allocate mux and gate clock structs */
+       mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
+       if (!mux)
+               return;
+
+       gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+       if (!gate)
+               goto free_mux;
+
+       /* gmac clock requires exactly 2 parents */
+       parents[0] = of_clk_get_parent_name(node, 0);
+       parents[1] = of_clk_get_parent_name(node, 1);
+       if (!parents[0] || !parents[1])
+               goto free_gate;
+
+       reg = of_iomap(node, 0);
+       if (!reg)
+               goto free_gate;
+
+       /* set up gate and fixed rate properties */
+       gate->reg = reg;
+       gate->bit_idx = SUN7I_A20_GMAC_GPIT;
+       gate->lock = &gmac_lock;
+       mux->reg = reg;
+       mux->mask = SUN7I_A20_GMAC_MASK;
+       mux->flags = CLK_MUX_INDEX_BIT;
+       mux->lock = &gmac_lock;
+
+       clk = clk_register_composite(NULL, clk_name,
+                       parents, SUN7I_A20_GMAC_PARENTS,
+                       &mux->hw, &clk_mux_ops,
+                       NULL, NULL,
+                       &gate->hw, &clk_gate_ops,
+                       0);
+
+       if (IS_ERR(clk))
+               goto iounmap_reg;
+
+       of_clk_add_provider(node, of_clk_src_simple_get, clk);
+       clk_register_clkdev(clk, clk_name, NULL);
+
+       return;
+
+iounmap_reg:
+       iounmap(reg);
+free_gate:
+       kfree(gate);
+free_mux:
+       kfree(mux);
+}
+CLK_OF_DECLARE(sun7i_a20_gmac, "allwinner,sun7i-a20-gmac-clk",
+               sun7i_a20_gmac_clk_setup);
diff --git a/drivers/clk/sunxi/clk-sun6i-apb0-gates.c b/drivers/clk/sunxi/clk-sun6i-apb0-gates.c
new file mode 100644 (file)
index 0000000..44cd27c
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
+ *
+ * Allwinner A31 APB0 clock gates driver
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#define SUN6I_APB0_GATES_MAX_SIZE      32
+
+static int sun6i_a31_apb0_gates_clk_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct clk_onecell_data *clk_data;
+       const char *clk_parent;
+       const char *clk_name;
+       struct resource *r;
+       void __iomem *reg;
+       int gate_id;
+       int ngates;
+       int i;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       reg = devm_ioremap_resource(&pdev->dev, r);
+       if (!reg)
+               return PTR_ERR(reg);
+
+       clk_parent = of_clk_get_parent_name(np, 0);
+       if (!clk_parent)
+               return -EINVAL;
+
+       ngates = of_property_count_strings(np, "clock-output-names");
+       if (ngates < 0)
+               return ngates;
+
+       if (!ngates || ngates > SUN6I_APB0_GATES_MAX_SIZE)
+               return -EINVAL;
+
+       clk_data = devm_kzalloc(&pdev->dev, sizeof(struct clk_onecell_data),
+                               GFP_KERNEL);
+       if (!clk_data)
+               return -ENOMEM;
+
+       clk_data->clks = devm_kzalloc(&pdev->dev,
+                                     SUN6I_APB0_GATES_MAX_SIZE *
+                                     sizeof(struct clk *),
+                                     GFP_KERNEL);
+       if (!clk_data->clks)
+               return -ENOMEM;
+
+       for (i = 0; i < ngates; i++) {
+               of_property_read_string_index(np, "clock-output-names",
+                                             i, &clk_name);
+
+               gate_id = i;
+               of_property_read_u32_index(np, "clock-indices", i, &gate_id);
+
+               WARN_ON(gate_id >= SUN6I_APB0_GATES_MAX_SIZE);
+               if (gate_id >= SUN6I_APB0_GATES_MAX_SIZE)
+                       continue;
+
+               clk_data->clks[gate_id] = clk_register_gate(&pdev->dev,
+                                                           clk_name,
+                                                           clk_parent, 0,
+                                                           reg, gate_id,
+                                                           0, NULL);
+               WARN_ON(IS_ERR(clk_data->clks[gate_id]));
+       }
+
+       clk_data->clk_num = ngates;
+
+       return of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+}
+
+const struct of_device_id sun6i_a31_apb0_gates_clk_dt_ids[] = {
+       { .compatible = "allwinner,sun6i-a31-apb0-gates-clk" },
+       { /* sentinel */ }
+};
+
+static struct platform_driver sun6i_a31_apb0_gates_clk_driver = {
+       .driver = {
+               .name = "sun6i-a31-apb0-gates-clk",
+               .owner = THIS_MODULE,
+               .of_match_table = sun6i_a31_apb0_gates_clk_dt_ids,
+       },
+       .probe = sun6i_a31_apb0_gates_clk_probe,
+};
+module_platform_driver(sun6i_a31_apb0_gates_clk_driver);
+
+MODULE_AUTHOR("Boris BREZILLON <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("Allwinner A31 APB0 gate clocks driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/sunxi/clk-sun6i-apb0.c b/drivers/clk/sunxi/clk-sun6i-apb0.c
new file mode 100644 (file)
index 0000000..11f17c3
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
+ *
+ * Allwinner A31 APB0 clock driver
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+/*
+ * The APB0 clk has a configurable divisor.
+ *
+ * We must use a clk_div_table and not a regular power of 2
+ * divisor here, because the first 2 values divide the clock
+ * by 2.
+ */
+static const struct clk_div_table sun6i_a31_apb0_divs[] = {
+       { .val = 0, .div = 2, },
+       { .val = 1, .div = 2, },
+       { .val = 2, .div = 4, },
+       { .val = 3, .div = 8, },
+       { /* sentinel */ },
+};
+
+static int sun6i_a31_apb0_clk_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       const char *clk_name = np->name;
+       const char *clk_parent;
+       struct resource *r;
+       void __iomem *reg;
+       struct clk *clk;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       reg = devm_ioremap_resource(&pdev->dev, r);
+       if (IS_ERR(reg))
+               return PTR_ERR(reg);
+
+       clk_parent = of_clk_get_parent_name(np, 0);
+       if (!clk_parent)
+               return -EINVAL;
+
+       of_property_read_string(np, "clock-output-names", &clk_name);
+
+       clk = clk_register_divider_table(&pdev->dev, clk_name, clk_parent,
+                                        0, reg, 0, 2, 0, sun6i_a31_apb0_divs,
+                                        NULL);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+const struct of_device_id sun6i_a31_apb0_clk_dt_ids[] = {
+       { .compatible = "allwinner,sun6i-a31-apb0-clk" },
+       { /* sentinel */ }
+};
+
+static struct platform_driver sun6i_a31_apb0_clk_driver = {
+       .driver = {
+               .name = "sun6i-a31-apb0-clk",
+               .owner = THIS_MODULE,
+               .of_match_table = sun6i_a31_apb0_clk_dt_ids,
+       },
+       .probe = sun6i_a31_apb0_clk_probe,
+};
+module_platform_driver(sun6i_a31_apb0_clk_driver);
+
+MODULE_AUTHOR("Boris BREZILLON <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("Allwinner A31 APB0 clock Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/sunxi/clk-sun6i-ar100.c b/drivers/clk/sunxi/clk-sun6i-ar100.c
new file mode 100644 (file)
index 0000000..f73cc05
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
+ *
+ * Allwinner A31 AR100 clock driver
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#define SUN6I_AR100_MAX_PARENTS                4
+#define SUN6I_AR100_SHIFT_MASK         0x3
+#define SUN6I_AR100_SHIFT_MAX          SUN6I_AR100_SHIFT_MASK
+#define SUN6I_AR100_SHIFT_SHIFT                4
+#define SUN6I_AR100_DIV_MASK           0x1f
+#define SUN6I_AR100_DIV_MAX            (SUN6I_AR100_DIV_MASK + 1)
+#define SUN6I_AR100_DIV_SHIFT          8
+#define SUN6I_AR100_MUX_MASK           0x3
+#define SUN6I_AR100_MUX_SHIFT          16
+
+struct ar100_clk {
+       struct clk_hw hw;
+       void __iomem *reg;
+};
+
+static inline struct ar100_clk *to_ar100_clk(struct clk_hw *hw)
+{
+       return container_of(hw, struct ar100_clk, hw);
+}
+
+static unsigned long ar100_recalc_rate(struct clk_hw *hw,
+                                      unsigned long parent_rate)
+{
+       struct ar100_clk *clk = to_ar100_clk(hw);
+       u32 val = readl(clk->reg);
+       int shift = (val >> SUN6I_AR100_SHIFT_SHIFT) & SUN6I_AR100_SHIFT_MASK;
+       int div = (val >> SUN6I_AR100_DIV_SHIFT) & SUN6I_AR100_DIV_MASK;
+
+       return (parent_rate >> shift) / (div + 1);
+}
+
+static long ar100_determine_rate(struct clk_hw *hw, unsigned long rate,
+                                unsigned long *best_parent_rate,
+                                struct clk **best_parent_clk)
+{
+       int nparents = __clk_get_num_parents(hw->clk);
+       long best_rate = -EINVAL;
+       int i;
+
+       *best_parent_clk = NULL;
+
+       for (i = 0; i < nparents; i++) {
+               unsigned long parent_rate;
+               unsigned long tmp_rate;
+               struct clk *parent;
+               unsigned long div;
+               int shift;
+
+               parent = clk_get_parent_by_index(hw->clk, i);
+               parent_rate = __clk_get_rate(parent);
+               div = DIV_ROUND_UP(parent_rate, rate);
+
+               /*
+                * The AR100 clk contains 2 divisors:
+                * - one power of 2 divisor
+                * - one regular divisor
+                *
+                * First check if we can safely shift (or divide by a power
+                * of 2) without losing precision on the requested rate.
+                */
+               shift = ffs(div) - 1;
+               if (shift > SUN6I_AR100_SHIFT_MAX)
+                       shift = SUN6I_AR100_SHIFT_MAX;
+
+               div >>= shift;
+
+               /*
+                * Then if the divisor is still bigger than what the HW
+                * actually supports, use a bigger shift (or power of 2
+                * divider) value and accept to lose some precision.
+                */
+               while (div > SUN6I_AR100_DIV_MAX) {
+                       shift++;
+                       div >>= 1;
+                       if (shift > SUN6I_AR100_SHIFT_MAX)
+                               break;
+               }
+
+               /*
+                * If the shift value (or power of 2 divider) is bigger
+                * than what the HW actually support, skip this parent.
+                */
+               if (shift > SUN6I_AR100_SHIFT_MAX)
+                       continue;
+
+               tmp_rate = (parent_rate >> shift) / div;
+               if (!*best_parent_clk || tmp_rate > best_rate) {
+                       *best_parent_clk = parent;
+                       *best_parent_rate = parent_rate;
+                       best_rate = tmp_rate;
+               }
+       }
+
+       return best_rate;
+}
+
+static int ar100_set_parent(struct clk_hw *hw, u8 index)
+{
+       struct ar100_clk *clk = to_ar100_clk(hw);
+       u32 val = readl(clk->reg);
+
+       if (index >= SUN6I_AR100_MAX_PARENTS)
+               return -EINVAL;
+
+       val &= ~(SUN6I_AR100_MUX_MASK << SUN6I_AR100_MUX_SHIFT);
+       val |= (index << SUN6I_AR100_MUX_SHIFT);
+       writel(val, clk->reg);
+
+       return 0;
+}
+
+static u8 ar100_get_parent(struct clk_hw *hw)
+{
+       struct ar100_clk *clk = to_ar100_clk(hw);
+       return (readl(clk->reg) >> SUN6I_AR100_MUX_SHIFT) &
+              SUN6I_AR100_MUX_MASK;
+}
+
+static int ar100_set_rate(struct clk_hw *hw, unsigned long rate,
+                         unsigned long parent_rate)
+{
+       unsigned long div = parent_rate / rate;
+       struct ar100_clk *clk = to_ar100_clk(hw);
+       u32 val = readl(clk->reg);
+       int shift;
+
+       if (parent_rate % rate)
+               return -EINVAL;
+
+       shift = ffs(div) - 1;
+       if (shift > SUN6I_AR100_SHIFT_MAX)
+               shift = SUN6I_AR100_SHIFT_MAX;
+
+       div >>= shift;
+
+       if (div > SUN6I_AR100_DIV_MAX)
+               return -EINVAL;
+
+       val &= ~((SUN6I_AR100_SHIFT_MASK << SUN6I_AR100_SHIFT_SHIFT) |
+                (SUN6I_AR100_DIV_MASK << SUN6I_AR100_DIV_SHIFT));
+       val |= (shift << SUN6I_AR100_SHIFT_SHIFT) |
+              (div << SUN6I_AR100_DIV_SHIFT);
+       writel(val, clk->reg);
+
+       return 0;
+}
+
+struct clk_ops ar100_ops = {
+       .recalc_rate = ar100_recalc_rate,
+       .determine_rate = ar100_determine_rate,
+       .set_parent = ar100_set_parent,
+       .get_parent = ar100_get_parent,
+       .set_rate = ar100_set_rate,
+};
+
+static int sun6i_a31_ar100_clk_probe(struct platform_device *pdev)
+{
+       const char *parents[SUN6I_AR100_MAX_PARENTS];
+       struct device_node *np = pdev->dev.of_node;
+       const char *clk_name = np->name;
+       struct clk_init_data init;
+       struct ar100_clk *ar100;
+       struct resource *r;
+       struct clk *clk;
+       int nparents;
+       int i;
+
+       ar100 = devm_kzalloc(&pdev->dev, sizeof(*ar100), GFP_KERNEL);
+       if (!ar100)
+               return -ENOMEM;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       ar100->reg = devm_ioremap_resource(&pdev->dev, r);
+       if (IS_ERR(ar100->reg))
+               return PTR_ERR(ar100->reg);
+
+       nparents = of_clk_get_parent_count(np);
+       if (nparents > SUN6I_AR100_MAX_PARENTS)
+               nparents = SUN6I_AR100_MAX_PARENTS;
+
+       for (i = 0; i < nparents; i++)
+               parents[i] = of_clk_get_parent_name(np, i);
+
+       of_property_read_string(np, "clock-output-names", &clk_name);
+
+       init.name = clk_name;
+       init.ops = &ar100_ops;
+       init.parent_names = parents;
+       init.num_parents = nparents;
+       init.flags = 0;
+
+       ar100->hw.init = &init;
+
+       clk = clk_register(&pdev->dev, &ar100->hw);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+const struct of_device_id sun6i_a31_ar100_clk_dt_ids[] = {
+       { .compatible = "allwinner,sun6i-a31-ar100-clk" },
+       { /* sentinel */ }
+};
+
+static struct platform_driver sun6i_a31_ar100_clk_driver = {
+       .driver = {
+               .name = "sun6i-a31-ar100-clk",
+               .owner = THIS_MODULE,
+               .of_match_table = sun6i_a31_ar100_clk_dt_ids,
+       },
+       .probe = sun6i_a31_ar100_clk_probe,
+};
+module_platform_driver(sun6i_a31_ar100_clk_driver);
+
+MODULE_AUTHOR("Boris BREZILLON <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("Allwinner A31 AR100 clock Driver");
+MODULE_LICENSE("GPL v2");
index 426483422d3d50edb20c3f013668cdacff9bd6d8..fb2ce8440f0ed6cefbc30d361193383f5282a6cd 100644 (file)
@@ -27,63 +27,6 @@ static DEFINE_SPINLOCK(clk_lock);
 /* Maximum number of parents our clocks have */
 #define SUNXI_MAX_PARENTS      5
 
-/**
- * sun4i_osc_clk_setup() - Setup function for gatable oscillator
- */
-
-#define SUNXI_OSC24M_GATE      0
-
-static void __init sun4i_osc_clk_setup(struct device_node *node)
-{
-       struct clk *clk;
-       struct clk_fixed_rate *fixed;
-       struct clk_gate *gate;
-       const char *clk_name = node->name;
-       u32 rate;
-
-       if (of_property_read_u32(node, "clock-frequency", &rate))
-               return;
-
-       /* allocate fixed-rate and gate clock structs */
-       fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL);
-       if (!fixed)
-               return;
-       gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
-       if (!gate)
-               goto err_free_fixed;
-
-       of_property_read_string(node, "clock-output-names", &clk_name);
-
-       /* set up gate and fixed rate properties */
-       gate->reg = of_iomap(node, 0);
-       gate->bit_idx = SUNXI_OSC24M_GATE;
-       gate->lock = &clk_lock;
-       fixed->fixed_rate = rate;
-
-       clk = clk_register_composite(NULL, clk_name,
-                       NULL, 0,
-                       NULL, NULL,
-                       &fixed->hw, &clk_fixed_rate_ops,
-                       &gate->hw, &clk_gate_ops,
-                       CLK_IS_ROOT);
-
-       if (IS_ERR(clk))
-               goto err_free_gate;
-
-       of_clk_add_provider(node, of_clk_src_simple_get, clk);
-       clk_register_clkdev(clk, clk_name, NULL);
-
-       return;
-
-err_free_gate:
-       kfree(gate);
-err_free_fixed:
-       kfree(fixed);
-}
-CLK_OF_DECLARE(sun4i_osc, "allwinner,sun4i-a10-osc-clk", sun4i_osc_clk_setup);
-
-
-
 /**
  * sun4i_get_pll1_factors() - calculates n, k, m, p factors for PLL1
  * PLL1 rate is calculated as follows
@@ -408,104 +351,6 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate,
        *p = calcp;
 }
 
-
-
-/**
- * sun7i_a20_gmac_clk_setup - Setup function for A20/A31 GMAC clock module
- *
- * This clock looks something like this
- *                               ________________________
- *  MII TX clock from PHY >-----|___________    _________|----> to GMAC core
- *  GMAC Int. RGMII TX clk >----|___________\__/__gate---|----> to PHY
- *  Ext. 125MHz RGMII TX clk >--|__divider__/            |
- *                              |________________________|
- *
- * The external 125 MHz reference is optional, i.e. GMAC can use its
- * internal TX clock just fine. The A31 GMAC clock module does not have
- * the divider controls for the external reference.
- *
- * To keep it simple, let the GMAC use either the MII TX clock for MII mode,
- * and its internal TX clock for GMII and RGMII modes. The GMAC driver should
- * select the appropriate source and gate/ungate the output to the PHY.
- *
- * Only the GMAC should use this clock. Altering the clock so that it doesn't
- * match the GMAC's operation parameters will result in the GMAC not being
- * able to send traffic out. The GMAC driver should set the clock rate and
- * enable/disable this clock to configure the required state. The clock
- * driver then responds by auto-reparenting the clock.
- */
-
-#define SUN7I_A20_GMAC_GPIT    2
-#define SUN7I_A20_GMAC_MASK    0x3
-#define SUN7I_A20_GMAC_PARENTS 2
-
-static void __init sun7i_a20_gmac_clk_setup(struct device_node *node)
-{
-       struct clk *clk;
-       struct clk_mux *mux;
-       struct clk_gate *gate;
-       const char *clk_name = node->name;
-       const char *parents[SUN7I_A20_GMAC_PARENTS];
-       void *reg;
-
-       if (of_property_read_string(node, "clock-output-names", &clk_name))
-               return;
-
-       /* allocate mux and gate clock structs */
-       mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
-       if (!mux)
-               return;
-
-       gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
-       if (!gate)
-               goto free_mux;
-
-       /* gmac clock requires exactly 2 parents */
-       parents[0] = of_clk_get_parent_name(node, 0);
-       parents[1] = of_clk_get_parent_name(node, 1);
-       if (!parents[0] || !parents[1])
-               goto free_gate;
-
-       reg = of_iomap(node, 0);
-       if (!reg)
-               goto free_gate;
-
-       /* set up gate and fixed rate properties */
-       gate->reg = reg;
-       gate->bit_idx = SUN7I_A20_GMAC_GPIT;
-       gate->lock = &clk_lock;
-       mux->reg = reg;
-       mux->mask = SUN7I_A20_GMAC_MASK;
-       mux->flags = CLK_MUX_INDEX_BIT;
-       mux->lock = &clk_lock;
-
-       clk = clk_register_composite(NULL, clk_name,
-                       parents, SUN7I_A20_GMAC_PARENTS,
-                       &mux->hw, &clk_mux_ops,
-                       NULL, NULL,
-                       &gate->hw, &clk_gate_ops,
-                       0);
-
-       if (IS_ERR(clk))
-               goto iounmap_reg;
-
-       of_clk_add_provider(node, of_clk_src_simple_get, clk);
-       clk_register_clkdev(clk, clk_name, NULL);
-
-       return;
-
-iounmap_reg:
-       iounmap(reg);
-free_gate:
-       kfree(gate);
-free_mux:
-       kfree(mux);
-}
-CLK_OF_DECLARE(sun7i_a20_gmac, "allwinner,sun7i-a20-gmac-clk",
-               sun7i_a20_gmac_clk_setup);
-
-
-
 /**
  * clk_sunxi_mmc_phase_control() - configures MMC clock phase control
  */
@@ -1009,6 +854,11 @@ static const struct gates_data sun5i_a13_usb_gates_data __initconst = {
        .reset_mask = 0x03,
 };
 
+static const struct gates_data sun6i_a31_usb_gates_data __initconst = {
+       .mask = { BIT(18) | BIT(17) | BIT(16) | BIT(10) | BIT(9) | BIT(8) },
+       .reset_mask = BIT(2) | BIT(1) | BIT(0),
+};
+
 static void __init sunxi_gates_clk_setup(struct device_node *node,
                                         struct gates_data *data)
 {
@@ -1304,6 +1154,7 @@ static const struct of_device_id clk_gates_match[] __initconst = {
        {.compatible = "allwinner,sun6i-a31-apb2-gates-clk", .data = &sun6i_a31_apb2_gates_data,},
        {.compatible = "allwinner,sun4i-a10-usb-clk", .data = &sun4i_a10_usb_gates_data,},
        {.compatible = "allwinner,sun5i-a13-usb-clk", .data = &sun5i_a13_usb_gates_data,},
+       {.compatible = "allwinner,sun6i-a31-usb-clk", .data = &sun6i_a31_usb_gates_data,},
        {}
 };
 
@@ -1321,33 +1172,10 @@ static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_mat
        }
 }
 
-/**
- * System clock protection
- *
- * By enabling these critical clocks, we prevent their accidental gating
- * by the framework
- */
-static void __init sunxi_clock_protect(void)
+static void __init sunxi_init_clocks(const char *clocks[], int nclocks)
 {
-       struct clk *clk;
-
-       /* memory bus clock - sun5i+ */
-       clk = clk_get(NULL, "mbus");
-       if (!IS_ERR(clk)) {
-               clk_prepare_enable(clk);
-               clk_put(clk);
-       }
-
-       /* DDR clock - sun4i+ */
-       clk = clk_get(NULL, "pll5_ddr");
-       if (!IS_ERR(clk)) {
-               clk_prepare_enable(clk);
-               clk_put(clk);
-       }
-}
+       unsigned int i;
 
-static void __init sunxi_init_clocks(struct device_node *np)
-{
        /* Register factor clocks */
        of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup);
 
@@ -1363,11 +1191,48 @@ static void __init sunxi_init_clocks(struct device_node *np)
        /* Register gate clocks */
        of_sunxi_table_clock_setup(clk_gates_match, sunxi_gates_clk_setup);
 
-       /* Enable core system clocks */
-       sunxi_clock_protect();
+       /* Protect the clocks that needs to stay on */
+       for (i = 0; i < nclocks; i++) {
+               struct clk *clk = clk_get(NULL, clocks[i]);
+
+               if (!IS_ERR(clk))
+                       clk_prepare_enable(clk);
+       }
+}
+
+static const char *sun4i_a10_critical_clocks[] __initdata = {
+       "pll5_ddr",
+};
+
+static void __init sun4i_a10_init_clocks(struct device_node *node)
+{
+       sunxi_init_clocks(sun4i_a10_critical_clocks,
+                         ARRAY_SIZE(sun4i_a10_critical_clocks));
+}
+CLK_OF_DECLARE(sun4i_a10_clk_init, "allwinner,sun4i-a10", sun4i_a10_init_clocks);
+
+static const char *sun5i_critical_clocks[] __initdata = {
+       "mbus",
+       "pll5_ddr",
+};
+
+static void __init sun5i_init_clocks(struct device_node *node)
+{
+       sunxi_init_clocks(sun5i_critical_clocks,
+                         ARRAY_SIZE(sun5i_critical_clocks));
+}
+CLK_OF_DECLARE(sun5i_a10s_clk_init, "allwinner,sun5i-a10s", sun5i_init_clocks);
+CLK_OF_DECLARE(sun5i_a13_clk_init, "allwinner,sun5i-a13", sun5i_init_clocks);
+CLK_OF_DECLARE(sun7i_a20_clk_init, "allwinner,sun7i-a20", sun5i_init_clocks);
+
+static const char *sun6i_critical_clocks[] __initdata = {
+       "cpu",
+       "ahb1_sdram",
+};
+
+static void __init sun6i_init_clocks(struct device_node *node)
+{
+       sunxi_init_clocks(sun6i_critical_clocks,
+                         ARRAY_SIZE(sun6i_critical_clocks));
 }
-CLK_OF_DECLARE(sun4i_a10_clk_init, "allwinner,sun4i-a10", sunxi_init_clocks);
-CLK_OF_DECLARE(sun5i_a10s_clk_init, "allwinner,sun5i-a10s", sunxi_init_clocks);
-CLK_OF_DECLARE(sun5i_a13_clk_init, "allwinner,sun5i-a13", sunxi_init_clocks);
-CLK_OF_DECLARE(sun6i_a31_clk_init, "allwinner,sun6i-a31", sunxi_init_clocks);
-CLK_OF_DECLARE(sun7i_a20_clk_init, "allwinner,sun7i-a20", sunxi_init_clocks);
+CLK_OF_DECLARE(sun6i_a31_clk_init, "allwinner,sun6i-a31", sun6i_init_clocks);
index 4319d4031aa3785de25bfb745f1d514c2690c9c5..ed4d0aaf891639585825344d7460bbfcbc1c81e2 100644 (file)
@@ -3,9 +3,11 @@ obj-y                                  += clk.o autoidle.o clockdomain.o
 clk-common                             = dpll.o composite.o divider.o gate.o \
                                          fixed-factor.o mux.o apll.o
 obj-$(CONFIG_SOC_AM33XX)               += $(clk-common) clk-33xx.o
+obj-$(CONFIG_ARCH_OMAP2)               += $(clk-common) interface.o clk-2xxx.o
 obj-$(CONFIG_ARCH_OMAP3)               += $(clk-common) interface.o clk-3xxx.o
 obj-$(CONFIG_ARCH_OMAP4)               += $(clk-common) clk-44xx.o
 obj-$(CONFIG_SOC_OMAP5)                        += $(clk-common) clk-54xx.o
-obj-$(CONFIG_SOC_DRA7XX)               += $(clk-common) clk-7xx.o
+obj-$(CONFIG_SOC_DRA7XX)               += $(clk-common) clk-7xx.o \
+                                          clk-dra7-atl.o
 obj-$(CONFIG_SOC_AM43XX)               += $(clk-common) clk-43xx.o
 endif
index b986f61f5a7713791d4cbd3abedf56054eab150b..5428c9c547cd97aa64bc826f5bb25d09d0806eb3 100644 (file)
@@ -221,3 +221,184 @@ cleanup:
        kfree(init);
 }
 CLK_OF_DECLARE(dra7_apll_clock, "ti,dra7-apll-clock", of_dra7_apll_setup);
+
+#define OMAP2_EN_APLL_LOCKED   0x3
+#define OMAP2_EN_APLL_STOPPED  0x0
+
+static int omap2_apll_is_enabled(struct clk_hw *hw)
+{
+       struct clk_hw_omap *clk = to_clk_hw_omap(hw);
+       struct dpll_data *ad = clk->dpll_data;
+       u32 v;
+
+       v = ti_clk_ll_ops->clk_readl(ad->control_reg);
+       v &= ad->enable_mask;
+
+       v >>= __ffs(ad->enable_mask);
+
+       return v == OMAP2_EN_APLL_LOCKED ? 1 : 0;
+}
+
+static unsigned long omap2_apll_recalc(struct clk_hw *hw,
+                                      unsigned long parent_rate)
+{
+       struct clk_hw_omap *clk = to_clk_hw_omap(hw);
+
+       if (omap2_apll_is_enabled(hw))
+               return clk->fixed_rate;
+
+       return 0;
+}
+
+static int omap2_apll_enable(struct clk_hw *hw)
+{
+       struct clk_hw_omap *clk = to_clk_hw_omap(hw);
+       struct dpll_data *ad = clk->dpll_data;
+       u32 v;
+       int i = 0;
+
+       v = ti_clk_ll_ops->clk_readl(ad->control_reg);
+       v &= ~ad->enable_mask;
+       v |= OMAP2_EN_APLL_LOCKED << __ffs(ad->enable_mask);
+       ti_clk_ll_ops->clk_writel(v, ad->control_reg);
+
+       while (1) {
+               v = ti_clk_ll_ops->clk_readl(ad->idlest_reg);
+               if (v & ad->idlest_mask)
+                       break;
+               if (i > MAX_APLL_WAIT_TRIES)
+                       break;
+               i++;
+               udelay(1);
+       }
+
+       if (i == MAX_APLL_WAIT_TRIES) {
+               pr_warn("%s failed to transition to locked\n",
+                       __clk_get_name(clk->hw.clk));
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+static void omap2_apll_disable(struct clk_hw *hw)
+{
+       struct clk_hw_omap *clk = to_clk_hw_omap(hw);
+       struct dpll_data *ad = clk->dpll_data;
+       u32 v;
+
+       v = ti_clk_ll_ops->clk_readl(ad->control_reg);
+       v &= ~ad->enable_mask;
+       v |= OMAP2_EN_APLL_STOPPED << __ffs(ad->enable_mask);
+       ti_clk_ll_ops->clk_writel(v, ad->control_reg);
+}
+
+static struct clk_ops omap2_apll_ops = {
+       .enable         = &omap2_apll_enable,
+       .disable        = &omap2_apll_disable,
+       .is_enabled     = &omap2_apll_is_enabled,
+       .recalc_rate    = &omap2_apll_recalc,
+};
+
+static void omap2_apll_set_autoidle(struct clk_hw_omap *clk, u32 val)
+{
+       struct dpll_data *ad = clk->dpll_data;
+       u32 v;
+
+       v = ti_clk_ll_ops->clk_readl(ad->autoidle_reg);
+       v &= ~ad->autoidle_mask;
+       v |= val << __ffs(ad->autoidle_mask);
+       ti_clk_ll_ops->clk_writel(v, ad->control_reg);
+}
+
+#define OMAP2_APLL_AUTOIDLE_LOW_POWER_STOP     0x3
+#define OMAP2_APLL_AUTOIDLE_DISABLE            0x0
+
+static void omap2_apll_allow_idle(struct clk_hw_omap *clk)
+{
+       omap2_apll_set_autoidle(clk, OMAP2_APLL_AUTOIDLE_LOW_POWER_STOP);
+}
+
+static void omap2_apll_deny_idle(struct clk_hw_omap *clk)
+{
+       omap2_apll_set_autoidle(clk, OMAP2_APLL_AUTOIDLE_DISABLE);
+}
+
+static struct clk_hw_omap_ops omap2_apll_hwops = {
+       .allow_idle     = &omap2_apll_allow_idle,
+       .deny_idle      = &omap2_apll_deny_idle,
+};
+
+static void __init of_omap2_apll_setup(struct device_node *node)
+{
+       struct dpll_data *ad = NULL;
+       struct clk_hw_omap *clk_hw = NULL;
+       struct clk_init_data *init = NULL;
+       struct clk *clk;
+       const char *parent_name;
+       u32 val;
+
+       ad = kzalloc(sizeof(*clk_hw), GFP_KERNEL);
+       clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL);
+       init = kzalloc(sizeof(*init), GFP_KERNEL);
+
+       if (!ad || !clk_hw || !init)
+               goto cleanup;
+
+       clk_hw->dpll_data = ad;
+       clk_hw->hw.init = init;
+       init->ops = &omap2_apll_ops;
+       init->name = node->name;
+       clk_hw->ops = &omap2_apll_hwops;
+
+       init->num_parents = of_clk_get_parent_count(node);
+       if (init->num_parents != 1) {
+               pr_err("%s must have one parent\n", node->name);
+               goto cleanup;
+       }
+
+       parent_name = of_clk_get_parent_name(node, 0);
+       init->parent_names = &parent_name;
+
+       if (of_property_read_u32(node, "ti,clock-frequency", &val)) {
+               pr_err("%s missing clock-frequency\n", node->name);
+               goto cleanup;
+       }
+       clk_hw->fixed_rate = val;
+
+       if (of_property_read_u32(node, "ti,bit-shift", &val)) {
+               pr_err("%s missing bit-shift\n", node->name);
+               goto cleanup;
+       }
+
+       clk_hw->enable_bit = val;
+       ad->enable_mask = 0x3 << val;
+       ad->autoidle_mask = 0x3 << val;
+
+       if (of_property_read_u32(node, "ti,idlest-shift", &val)) {
+               pr_err("%s missing idlest-shift\n", node->name);
+               goto cleanup;
+       }
+
+       ad->idlest_mask = 1 << val;
+
+       ad->control_reg = ti_clk_get_reg_addr(node, 0);
+       ad->autoidle_reg = ti_clk_get_reg_addr(node, 1);
+       ad->idlest_reg = ti_clk_get_reg_addr(node, 2);
+
+       if (!ad->control_reg || !ad->autoidle_reg || !ad->idlest_reg)
+               goto cleanup;
+
+       clk = clk_register(NULL, &clk_hw->hw);
+       if (!IS_ERR(clk)) {
+               of_clk_add_provider(node, of_clk_src_simple_get, clk);
+               kfree(init);
+               return;
+       }
+cleanup:
+       kfree(ad);
+       kfree(clk_hw);
+       kfree(init);
+}
+CLK_OF_DECLARE(omap2_apll_clock, "ti,omap2-apll-clock",
+              of_omap2_apll_setup);
diff --git a/drivers/clk/ti/clk-2xxx.c b/drivers/clk/ti/clk-2xxx.c
new file mode 100644 (file)
index 0000000..c808ab3
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * OMAP2 Clock init
+ *
+ * Copyright (C) 2013 Texas Instruments, Inc
+ *     Tero Kristo (t-kristo@ti.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/ti.h>
+
+static struct ti_dt_clk omap2xxx_clks[] = {
+       DT_CLK(NULL, "func_32k_ck", "func_32k_ck"),
+       DT_CLK(NULL, "secure_32k_ck", "secure_32k_ck"),
+       DT_CLK(NULL, "virt_12m_ck", "virt_12m_ck"),
+       DT_CLK(NULL, "virt_13m_ck", "virt_13m_ck"),
+       DT_CLK(NULL, "virt_19200000_ck", "virt_19200000_ck"),
+       DT_CLK(NULL, "virt_26m_ck", "virt_26m_ck"),
+       DT_CLK(NULL, "aplls_clkin_ck", "aplls_clkin_ck"),
+       DT_CLK(NULL, "aplls_clkin_x2_ck", "aplls_clkin_x2_ck"),
+       DT_CLK(NULL, "osc_ck", "osc_ck"),
+       DT_CLK(NULL, "sys_ck", "sys_ck"),
+       DT_CLK(NULL, "alt_ck", "alt_ck"),
+       DT_CLK(NULL, "mcbsp_clks", "mcbsp_clks"),
+       DT_CLK(NULL, "dpll_ck", "dpll_ck"),
+       DT_CLK(NULL, "apll96_ck", "apll96_ck"),
+       DT_CLK(NULL, "apll54_ck", "apll54_ck"),
+       DT_CLK(NULL, "func_54m_ck", "func_54m_ck"),
+       DT_CLK(NULL, "core_ck", "core_ck"),
+       DT_CLK(NULL, "func_96m_ck", "func_96m_ck"),
+       DT_CLK(NULL, "func_48m_ck", "func_48m_ck"),
+       DT_CLK(NULL, "func_12m_ck", "func_12m_ck"),
+       DT_CLK(NULL, "sys_clkout_src", "sys_clkout_src"),
+       DT_CLK(NULL, "sys_clkout", "sys_clkout"),
+       DT_CLK(NULL, "emul_ck", "emul_ck"),
+       DT_CLK(NULL, "mpu_ck", "mpu_ck"),
+       DT_CLK(NULL, "dsp_fck", "dsp_fck"),
+       DT_CLK(NULL, "gfx_3d_fck", "gfx_3d_fck"),
+       DT_CLK(NULL, "gfx_2d_fck", "gfx_2d_fck"),
+       DT_CLK(NULL, "gfx_ick", "gfx_ick"),
+       DT_CLK("omapdss_dss", "ick", "dss_ick"),
+       DT_CLK(NULL, "dss_ick", "dss_ick"),
+       DT_CLK(NULL, "dss1_fck", "dss1_fck"),
+       DT_CLK(NULL, "dss2_fck", "dss2_fck"),
+       DT_CLK(NULL, "dss_54m_fck", "dss_54m_fck"),
+       DT_CLK(NULL, "core_l3_ck", "core_l3_ck"),
+       DT_CLK(NULL, "ssi_fck", "ssi_ssr_sst_fck"),
+       DT_CLK(NULL, "usb_l4_ick", "usb_l4_ick"),
+       DT_CLK(NULL, "l4_ck", "l4_ck"),
+       DT_CLK(NULL, "ssi_l4_ick", "ssi_l4_ick"),
+       DT_CLK(NULL, "gpt1_ick", "gpt1_ick"),
+       DT_CLK(NULL, "gpt1_fck", "gpt1_fck"),
+       DT_CLK(NULL, "gpt2_ick", "gpt2_ick"),
+       DT_CLK(NULL, "gpt2_fck", "gpt2_fck"),
+       DT_CLK(NULL, "gpt3_ick", "gpt3_ick"),
+       DT_CLK(NULL, "gpt3_fck", "gpt3_fck"),
+       DT_CLK(NULL, "gpt4_ick", "gpt4_ick"),
+       DT_CLK(NULL, "gpt4_fck", "gpt4_fck"),
+       DT_CLK(NULL, "gpt5_ick", "gpt5_ick"),
+       DT_CLK(NULL, "gpt5_fck", "gpt5_fck"),
+       DT_CLK(NULL, "gpt6_ick", "gpt6_ick"),
+       DT_CLK(NULL, "gpt6_fck", "gpt6_fck"),
+       DT_CLK(NULL, "gpt7_ick", "gpt7_ick"),
+       DT_CLK(NULL, "gpt7_fck", "gpt7_fck"),
+       DT_CLK(NULL, "gpt8_ick", "gpt8_ick"),
+       DT_CLK(NULL, "gpt8_fck", "gpt8_fck"),
+       DT_CLK(NULL, "gpt9_ick", "gpt9_ick"),
+       DT_CLK(NULL, "gpt9_fck", "gpt9_fck"),
+       DT_CLK(NULL, "gpt10_ick", "gpt10_ick"),
+       DT_CLK(NULL, "gpt10_fck", "gpt10_fck"),
+       DT_CLK(NULL, "gpt11_ick", "gpt11_ick"),
+       DT_CLK(NULL, "gpt11_fck", "gpt11_fck"),
+       DT_CLK(NULL, "gpt12_ick", "gpt12_ick"),
+       DT_CLK(NULL, "gpt12_fck", "gpt12_fck"),
+       DT_CLK("omap-mcbsp.1", "ick", "mcbsp1_ick"),
+       DT_CLK(NULL, "mcbsp1_ick", "mcbsp1_ick"),
+       DT_CLK(NULL, "mcbsp1_fck", "mcbsp1_fck"),
+       DT_CLK("omap-mcbsp.2", "ick", "mcbsp2_ick"),
+       DT_CLK(NULL, "mcbsp2_ick", "mcbsp2_ick"),
+       DT_CLK(NULL, "mcbsp2_fck", "mcbsp2_fck"),
+       DT_CLK("omap2_mcspi.1", "ick", "mcspi1_ick"),
+       DT_CLK(NULL, "mcspi1_ick", "mcspi1_ick"),
+       DT_CLK(NULL, "mcspi1_fck", "mcspi1_fck"),
+       DT_CLK("omap2_mcspi.2", "ick", "mcspi2_ick"),
+       DT_CLK(NULL, "mcspi2_ick", "mcspi2_ick"),
+       DT_CLK(NULL, "mcspi2_fck", "mcspi2_fck"),
+       DT_CLK(NULL, "uart1_ick", "uart1_ick"),
+       DT_CLK(NULL, "uart1_fck", "uart1_fck"),
+       DT_CLK(NULL, "uart2_ick", "uart2_ick"),
+       DT_CLK(NULL, "uart2_fck", "uart2_fck"),
+       DT_CLK(NULL, "uart3_ick", "uart3_ick"),
+       DT_CLK(NULL, "uart3_fck", "uart3_fck"),
+       DT_CLK(NULL, "gpios_ick", "gpios_ick"),
+       DT_CLK(NULL, "gpios_fck", "gpios_fck"),
+       DT_CLK("omap_wdt", "ick", "mpu_wdt_ick"),
+       DT_CLK(NULL, "mpu_wdt_ick", "mpu_wdt_ick"),
+       DT_CLK(NULL, "mpu_wdt_fck", "mpu_wdt_fck"),
+       DT_CLK(NULL, "sync_32k_ick", "sync_32k_ick"),
+       DT_CLK(NULL, "wdt1_ick", "wdt1_ick"),
+       DT_CLK(NULL, "omapctrl_ick", "omapctrl_ick"),
+       DT_CLK("omap24xxcam", "fck", "cam_fck"),
+       DT_CLK(NULL, "cam_fck", "cam_fck"),
+       DT_CLK("omap24xxcam", "ick", "cam_ick"),
+       DT_CLK(NULL, "cam_ick", "cam_ick"),
+       DT_CLK(NULL, "mailboxes_ick", "mailboxes_ick"),
+       DT_CLK(NULL, "wdt4_ick", "wdt4_ick"),
+       DT_CLK(NULL, "wdt4_fck", "wdt4_fck"),
+       DT_CLK(NULL, "mspro_ick", "mspro_ick"),
+       DT_CLK(NULL, "mspro_fck", "mspro_fck"),
+       DT_CLK(NULL, "fac_ick", "fac_ick"),
+       DT_CLK(NULL, "fac_fck", "fac_fck"),
+       DT_CLK("omap_hdq.0", "ick", "hdq_ick"),
+       DT_CLK(NULL, "hdq_ick", "hdq_ick"),
+       DT_CLK("omap_hdq.0", "fck", "hdq_fck"),
+       DT_CLK(NULL, "hdq_fck", "hdq_fck"),
+       DT_CLK("omap_i2c.1", "ick", "i2c1_ick"),
+       DT_CLK(NULL, "i2c1_ick", "i2c1_ick"),
+       DT_CLK("omap_i2c.2", "ick", "i2c2_ick"),
+       DT_CLK(NULL, "i2c2_ick", "i2c2_ick"),
+       DT_CLK(NULL, "gpmc_fck", "gpmc_fck"),
+       DT_CLK(NULL, "sdma_fck", "sdma_fck"),
+       DT_CLK(NULL, "sdma_ick", "sdma_ick"),
+       DT_CLK(NULL, "sdrc_ick", "sdrc_ick"),
+       DT_CLK(NULL, "des_ick", "des_ick"),
+       DT_CLK("omap-sham", "ick", "sha_ick"),
+       DT_CLK(NULL, "sha_ick", "sha_ick"),
+       DT_CLK("omap_rng", "ick", "rng_ick"),
+       DT_CLK(NULL, "rng_ick", "rng_ick"),
+       DT_CLK("omap-aes", "ick", "aes_ick"),
+       DT_CLK(NULL, "aes_ick", "aes_ick"),
+       DT_CLK(NULL, "pka_ick", "pka_ick"),
+       DT_CLK(NULL, "usb_fck", "usb_fck"),
+       DT_CLK(NULL, "timer_32k_ck", "func_32k_ck"),
+       DT_CLK(NULL, "timer_sys_ck", "sys_ck"),
+       DT_CLK(NULL, "timer_ext_ck", "alt_ck"),
+       { .node_name = NULL },
+};
+
+static struct ti_dt_clk omap2420_clks[] = {
+       DT_CLK(NULL, "sys_clkout2_src", "sys_clkout2_src"),
+       DT_CLK(NULL, "sys_clkout2", "sys_clkout2"),
+       DT_CLK(NULL, "dsp_ick", "dsp_ick"),
+       DT_CLK(NULL, "iva1_ifck", "iva1_ifck"),
+       DT_CLK(NULL, "iva1_mpu_int_ifck", "iva1_mpu_int_ifck"),
+       DT_CLK(NULL, "wdt3_ick", "wdt3_ick"),
+       DT_CLK(NULL, "wdt3_fck", "wdt3_fck"),
+       DT_CLK("mmci-omap.0", "ick", "mmc_ick"),
+       DT_CLK(NULL, "mmc_ick", "mmc_ick"),
+       DT_CLK("mmci-omap.0", "fck", "mmc_fck"),
+       DT_CLK(NULL, "mmc_fck", "mmc_fck"),
+       DT_CLK(NULL, "eac_ick", "eac_ick"),
+       DT_CLK(NULL, "eac_fck", "eac_fck"),
+       DT_CLK(NULL, "i2c1_fck", "i2c1_fck"),
+       DT_CLK(NULL, "i2c2_fck", "i2c2_fck"),
+       DT_CLK(NULL, "vlynq_ick", "vlynq_ick"),
+       DT_CLK(NULL, "vlynq_fck", "vlynq_fck"),
+       DT_CLK("musb-hdrc", "fck", "osc_ck"),
+       { .node_name = NULL },
+};
+
+static struct ti_dt_clk omap2430_clks[] = {
+       DT_CLK("twl", "fck", "osc_ck"),
+       DT_CLK(NULL, "iva2_1_ick", "iva2_1_ick"),
+       DT_CLK(NULL, "mdm_ick", "mdm_ick"),
+       DT_CLK(NULL, "mdm_osc_ck", "mdm_osc_ck"),
+       DT_CLK("omap-mcbsp.3", "ick", "mcbsp3_ick"),
+       DT_CLK(NULL, "mcbsp3_ick", "mcbsp3_ick"),
+       DT_CLK(NULL, "mcbsp3_fck", "mcbsp3_fck"),
+       DT_CLK("omap-mcbsp.4", "ick", "mcbsp4_ick"),
+       DT_CLK(NULL, "mcbsp4_ick", "mcbsp4_ick"),
+       DT_CLK(NULL, "mcbsp4_fck", "mcbsp4_fck"),
+       DT_CLK("omap-mcbsp.5", "ick", "mcbsp5_ick"),
+       DT_CLK(NULL, "mcbsp5_ick", "mcbsp5_ick"),
+       DT_CLK(NULL, "mcbsp5_fck", "mcbsp5_fck"),
+       DT_CLK("omap2_mcspi.3", "ick", "mcspi3_ick"),
+       DT_CLK(NULL, "mcspi3_ick", "mcspi3_ick"),
+       DT_CLK(NULL, "mcspi3_fck", "mcspi3_fck"),
+       DT_CLK(NULL, "icr_ick", "icr_ick"),
+       DT_CLK(NULL, "i2chs1_fck", "i2chs1_fck"),
+       DT_CLK(NULL, "i2chs2_fck", "i2chs2_fck"),
+       DT_CLK("musb-omap2430", "ick", "usbhs_ick"),
+       DT_CLK(NULL, "usbhs_ick", "usbhs_ick"),
+       DT_CLK("omap_hsmmc.0", "ick", "mmchs1_ick"),
+       DT_CLK(NULL, "mmchs1_ick", "mmchs1_ick"),
+       DT_CLK(NULL, "mmchs1_fck", "mmchs1_fck"),
+       DT_CLK("omap_hsmmc.1", "ick", "mmchs2_ick"),
+       DT_CLK(NULL, "mmchs2_ick", "mmchs2_ick"),
+       DT_CLK(NULL, "mmchs2_fck", "mmchs2_fck"),
+       DT_CLK(NULL, "gpio5_ick", "gpio5_ick"),
+       DT_CLK(NULL, "gpio5_fck", "gpio5_fck"),
+       DT_CLK(NULL, "mdm_intc_ick", "mdm_intc_ick"),
+       DT_CLK("omap_hsmmc.0", "mmchsdb_fck", "mmchsdb1_fck"),
+       DT_CLK(NULL, "mmchsdb1_fck", "mmchsdb1_fck"),
+       DT_CLK("omap_hsmmc.1", "mmchsdb_fck", "mmchsdb2_fck"),
+       DT_CLK(NULL, "mmchsdb2_fck", "mmchsdb2_fck"),
+       { .node_name = NULL },
+};
+
+static const char *enable_init_clks[] = {
+       "apll96_ck",
+       "apll54_ck",
+       "sync_32k_ick",
+       "omapctrl_ick",
+       "gpmc_fck",
+       "sdrc_ick",
+};
+
+enum {
+       OMAP2_SOC_OMAP2420,
+       OMAP2_SOC_OMAP2430,
+};
+
+static int __init omap2xxx_dt_clk_init(int soc_type)
+{
+       ti_dt_clocks_register(omap2xxx_clks);
+
+       if (soc_type == OMAP2_SOC_OMAP2420)
+               ti_dt_clocks_register(omap2420_clks);
+       else
+               ti_dt_clocks_register(omap2430_clks);
+
+       omap2xxx_clkt_vps_init();
+
+       omap2_clk_disable_autoidle_all();
+
+       omap2_clk_enable_init_clocks(enable_init_clks,
+                                    ARRAY_SIZE(enable_init_clks));
+
+       pr_info("Clocking rate (Crystal/DPLL/MPU): %ld.%01ld/%ld/%ld MHz\n",
+               (clk_get_rate(clk_get_sys(NULL, "sys_ck")) / 1000000),
+               (clk_get_rate(clk_get_sys(NULL, "sys_ck")) / 100000) % 10,
+               (clk_get_rate(clk_get_sys(NULL, "dpll_ck")) / 1000000),
+               (clk_get_rate(clk_get_sys(NULL, "mpu_ck")) / 1000000));
+
+       return 0;
+}
+
+int __init omap2420_dt_clk_init(void)
+{
+       return omap2xxx_dt_clk_init(OMAP2_SOC_OMAP2420);
+}
+
+int __init omap2430_dt_clk_init(void)
+{
+       return omap2xxx_dt_clk_init(OMAP2_SOC_OMAP2430);
+}
index 527a43da3d33724ba81f15f1dd7f14f1bb99d40c..3795fce8a8303e66a004fe12bcf6d9008a7d5c63 100644 (file)
@@ -116,9 +116,25 @@ static struct ti_dt_clk am43xx_clks[] = {
 
 int __init am43xx_dt_clk_init(void)
 {
+       struct clk *clk1, *clk2;
+
        ti_dt_clocks_register(am43xx_clks);
 
        omap2_clk_disable_autoidle_all();
 
+       /*
+        * cpsw_cpts_rft_clk  has got the choice of 3 clocksources
+        * dpll_core_m4_ck, dpll_core_m5_ck and dpll_disp_m2_ck.
+        * By default dpll_core_m4_ck is selected, witn this as clock
+        * source the CPTS doesnot work properly. It gives clockcheck errors
+        * while running PTP.
+        * clockcheck: clock jumped backward or running slower than expected!
+        * By selecting dpll_core_m5_ck as the clocksource fixes this issue.
+        * In AM335x dpll_core_m5_ck is the default clocksource.
+        */
+       clk1 = clk_get_sys(NULL, "cpsw_cpts_rft_clk");
+       clk2 = clk_get_sys(NULL, "dpll_core_m5_ck");
+       clk_set_parent(clk1, clk2);
+
        return 0;
 }
index 08f3d1b915b396d393e2a9bf0aa444784f39388d..5e183993e3ec56b926fffd252325000d02566d03 100644 (file)
@@ -240,6 +240,12 @@ int __init omap5xxx_dt_clk_init(void)
        if (rc)
                pr_err("%s: failed to configure ABE DPLL!\n", __func__);
 
+       abe_dpll = clk_get_sys(NULL, "dpll_abe_m2x2_ck");
+       if (!rc)
+               rc = clk_set_rate(abe_dpll, OMAP5_DPLL_ABE_DEFFREQ * 2);
+       if (rc)
+               pr_err("%s: failed to configure ABE m2x2 DPLL!\n", __func__);
+
        usb_dpll = clk_get_sys(NULL, "dpll_usb_ck");
        rc = clk_set_rate(usb_dpll, OMAP5_DPLL_USB_DEFFREQ);
        if (rc)
index f7e40734c819f045caa9be5f5a32c299c7fa3344..e1581335937d274dfddfd502cd5c4c896b305267 100644 (file)
@@ -24,7 +24,7 @@ static struct ti_dt_clk dra7xx_clks[] = {
        DT_CLK(NULL, "atl_clkin0_ck", "atl_clkin0_ck"),
        DT_CLK(NULL, "atl_clkin1_ck", "atl_clkin1_ck"),
        DT_CLK(NULL, "atl_clkin2_ck", "atl_clkin2_ck"),
-       DT_CLK(NULL, "atlclkin3_ck", "atlclkin3_ck"),
+       DT_CLK(NULL, "atl_clkin3_ck", "atl_clkin3_ck"),
        DT_CLK(NULL, "hdmi_clkin_ck", "hdmi_clkin_ck"),
        DT_CLK(NULL, "mlb_clkin_ck", "mlb_clkin_ck"),
        DT_CLK(NULL, "mlbp_clkin_ck", "mlbp_clkin_ck"),
diff --git a/drivers/clk/ti/clk-dra7-atl.c b/drivers/clk/ti/clk-dra7-atl.c
new file mode 100644 (file)
index 0000000..4a65b41
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * DRA7 ATL (Audio Tracking Logic) clock driver
+ *
+ * Copyright (C) 2013 Texas Instruments, Inc.
+ *
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#define DRA7_ATL_INSTANCES     4
+
+#define DRA7_ATL_PPMR_REG(id)          (0x200 + (id * 0x80))
+#define DRA7_ATL_BBSR_REG(id)          (0x204 + (id * 0x80))
+#define DRA7_ATL_ATLCR_REG(id)         (0x208 + (id * 0x80))
+#define DRA7_ATL_SWEN_REG(id)          (0x210 + (id * 0x80))
+#define DRA7_ATL_BWSMUX_REG(id)                (0x214 + (id * 0x80))
+#define DRA7_ATL_AWSMUX_REG(id)                (0x218 + (id * 0x80))
+#define DRA7_ATL_PCLKMUX_REG(id)       (0x21c + (id * 0x80))
+
+#define DRA7_ATL_SWEN                  BIT(0)
+#define DRA7_ATL_DIVIDER_MASK          (0x1f)
+#define DRA7_ATL_PCLKMUX               BIT(0)
+struct dra7_atl_clock_info;
+
+struct dra7_atl_desc {
+       struct clk *clk;
+       struct clk_hw hw;
+       struct dra7_atl_clock_info *cinfo;
+       int id;
+
+       bool probed;            /* the driver for the IP has been loaded */
+       bool valid;             /* configured */
+       bool enabled;
+       u32 bws;                /* Baseband Word Select Mux */
+       u32 aws;                /* Audio Word Select Mux */
+       u32 divider;            /* Cached divider value */
+};
+
+struct dra7_atl_clock_info {
+       struct device *dev;
+       void __iomem *iobase;
+
+       struct dra7_atl_desc *cdesc;
+};
+
+#define to_atl_desc(_hw)       container_of(_hw, struct dra7_atl_desc, hw)
+
+static inline void atl_write(struct dra7_atl_clock_info *cinfo, u32 reg,
+                            u32 val)
+{
+       __raw_writel(val, cinfo->iobase + reg);
+}
+
+static inline int atl_read(struct dra7_atl_clock_info *cinfo, u32 reg)
+{
+       return __raw_readl(cinfo->iobase + reg);
+}
+
+static int atl_clk_enable(struct clk_hw *hw)
+{
+       struct dra7_atl_desc *cdesc = to_atl_desc(hw);
+
+       if (!cdesc->probed)
+               goto out;
+
+       if (unlikely(!cdesc->valid))
+               dev_warn(cdesc->cinfo->dev, "atl%d has not been configured\n",
+                        cdesc->id);
+       pm_runtime_get_sync(cdesc->cinfo->dev);
+
+       atl_write(cdesc->cinfo, DRA7_ATL_ATLCR_REG(cdesc->id),
+                 cdesc->divider - 1);
+       atl_write(cdesc->cinfo, DRA7_ATL_SWEN_REG(cdesc->id), DRA7_ATL_SWEN);
+
+out:
+       cdesc->enabled = true;
+
+       return 0;
+}
+
+static void atl_clk_disable(struct clk_hw *hw)
+{
+       struct dra7_atl_desc *cdesc = to_atl_desc(hw);
+
+       if (!cdesc->probed)
+               goto out;
+
+       atl_write(cdesc->cinfo, DRA7_ATL_SWEN_REG(cdesc->id), 0);
+       pm_runtime_put_sync(cdesc->cinfo->dev);
+
+out:
+       cdesc->enabled = false;
+}
+
+static int atl_clk_is_enabled(struct clk_hw *hw)
+{
+       struct dra7_atl_desc *cdesc = to_atl_desc(hw);
+
+       return cdesc->enabled;
+}
+
+static unsigned long atl_clk_recalc_rate(struct clk_hw *hw,
+                                        unsigned long parent_rate)
+{
+       struct dra7_atl_desc *cdesc = to_atl_desc(hw);
+
+       return parent_rate / cdesc->divider;
+}
+
+static long atl_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+                              unsigned long *parent_rate)
+{
+       unsigned divider;
+
+       divider = (*parent_rate + rate / 2) / rate;
+       if (divider > DRA7_ATL_DIVIDER_MASK + 1)
+               divider = DRA7_ATL_DIVIDER_MASK + 1;
+
+       return *parent_rate / divider;
+}
+
+static int atl_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+                           unsigned long parent_rate)
+{
+       struct dra7_atl_desc *cdesc = to_atl_desc(hw);
+       u32 divider;
+
+       divider = ((parent_rate + rate / 2) / rate) - 1;
+       if (divider > DRA7_ATL_DIVIDER_MASK)
+               divider = DRA7_ATL_DIVIDER_MASK;
+
+       cdesc->divider = divider + 1;
+
+       return 0;
+}
+
+const struct clk_ops atl_clk_ops = {
+       .enable         = atl_clk_enable,
+       .disable        = atl_clk_disable,
+       .is_enabled     = atl_clk_is_enabled,
+       .recalc_rate    = atl_clk_recalc_rate,
+       .round_rate     = atl_clk_round_rate,
+       .set_rate       = atl_clk_set_rate,
+};
+
+static void __init of_dra7_atl_clock_setup(struct device_node *node)
+{
+       struct dra7_atl_desc *clk_hw = NULL;
+       struct clk_init_data init = { 0 };
+       const char **parent_names = NULL;
+       struct clk *clk;
+
+       clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL);
+       if (!clk_hw) {
+               pr_err("%s: could not allocate dra7_atl_desc\n", __func__);
+               return;
+       }
+
+       clk_hw->hw.init = &init;
+       clk_hw->divider = 1;
+       init.name = node->name;
+       init.ops = &atl_clk_ops;
+       init.flags = CLK_IGNORE_UNUSED;
+       init.num_parents = of_clk_get_parent_count(node);
+
+       if (init.num_parents != 1) {
+               pr_err("%s: atl clock %s must have 1 parent\n", __func__,
+                      node->name);
+               goto cleanup;
+       }
+
+       parent_names = kzalloc(sizeof(char *), GFP_KERNEL);
+
+       if (!parent_names)
+               goto cleanup;
+
+       parent_names[0] = of_clk_get_parent_name(node, 0);
+
+       init.parent_names = parent_names;
+
+       clk = clk_register(NULL, &clk_hw->hw);
+
+       if (!IS_ERR(clk)) {
+               of_clk_add_provider(node, of_clk_src_simple_get, clk);
+               return;
+       }
+cleanup:
+       kfree(parent_names);
+       kfree(clk_hw);
+}
+CLK_OF_DECLARE(dra7_atl_clock, "ti,dra7-atl-clock", of_dra7_atl_clock_setup);
+
+static int of_dra7_atl_clk_probe(struct platform_device *pdev)
+{
+       struct device_node *node = pdev->dev.of_node;
+       struct dra7_atl_clock_info *cinfo;
+       int i;
+       int ret = 0;
+
+       if (!node)
+               return -ENODEV;
+
+       cinfo = devm_kzalloc(&pdev->dev, sizeof(*cinfo), GFP_KERNEL);
+       if (!cinfo)
+               return -ENOMEM;
+
+       cinfo->iobase = of_iomap(node, 0);
+       cinfo->dev = &pdev->dev;
+       pm_runtime_enable(cinfo->dev);
+
+       pm_runtime_get_sync(cinfo->dev);
+       atl_write(cinfo, DRA7_ATL_PCLKMUX_REG(0), DRA7_ATL_PCLKMUX);
+
+       for (i = 0; i < DRA7_ATL_INSTANCES; i++) {
+               struct device_node *cfg_node;
+               char prop[5];
+               struct dra7_atl_desc *cdesc;
+               struct of_phandle_args clkspec;
+               struct clk *clk;
+               int rc;
+
+               rc = of_parse_phandle_with_args(node, "ti,provided-clocks",
+                                               NULL, i, &clkspec);
+
+               if (rc) {
+                       pr_err("%s: failed to lookup atl clock %d\n", __func__,
+                              i);
+                       return -EINVAL;
+               }
+
+               clk = of_clk_get_from_provider(&clkspec);
+
+               cdesc = to_atl_desc(__clk_get_hw(clk));
+               cdesc->cinfo = cinfo;
+               cdesc->id = i;
+
+               /* Get configuration for the ATL instances */
+               snprintf(prop, sizeof(prop), "atl%u", i);
+               cfg_node = of_find_node_by_name(node, prop);
+               if (cfg_node) {
+                       ret = of_property_read_u32(cfg_node, "bws",
+                                                  &cdesc->bws);
+                       ret |= of_property_read_u32(cfg_node, "aws",
+                                                   &cdesc->aws);
+                       if (!ret) {
+                               cdesc->valid = true;
+                               atl_write(cinfo, DRA7_ATL_BWSMUX_REG(i),
+                                         cdesc->bws);
+                               atl_write(cinfo, DRA7_ATL_AWSMUX_REG(i),
+                                         cdesc->aws);
+                       }
+               }
+
+               cdesc->probed = true;
+               /*
+                * Enable the clock if it has been asked prior to loading the
+                * hw driver
+                */
+               if (cdesc->enabled)
+                       atl_clk_enable(__clk_get_hw(clk));
+       }
+       pm_runtime_put_sync(cinfo->dev);
+
+       return ret;
+}
+
+static int of_dra7_atl_clk_remove(struct platform_device *pdev)
+{
+       pm_runtime_disable(&pdev->dev);
+
+       return 0;
+}
+
+static struct of_device_id of_dra7_atl_clk_match_tbl[] = {
+       { .compatible = "ti,dra7-atl", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, of_dra7_atl_clk_match_tbl);
+
+static struct platform_driver dra7_atl_clk_driver = {
+       .driver = {
+               .name = "dra7-atl",
+               .owner = THIS_MODULE,
+               .of_match_table = of_dra7_atl_clk_match_tbl,
+       },
+       .probe = of_dra7_atl_clk_probe,
+       .remove = of_dra7_atl_clk_remove,
+};
+
+module_platform_driver(dra7_atl_clk_driver);
+
+MODULE_DESCRIPTION("Clock driver for DRA7 Audio Tracking Logic");
+MODULE_ALIAS("platform:dra7-atl-clock");
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
+MODULE_LICENSE("GPL v2");
index 7e498a44f97dea25af8db165cde8aac07ba78361..abd956d5f83811b3b53be1d63a708248c0faa6b5 100644 (file)
@@ -25,8 +25,6 @@
 #undef pr_fmt
 #define pr_fmt(fmt) "%s: " fmt, __func__
 
-#define DPLL_HAS_AUTOIDLE      0x1
-
 #if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5) || \
        defined(CONFIG_SOC_DRA7XX)
 static const struct clk_ops dpll_m4xen_ck_ops = {
@@ -37,21 +35,18 @@ static const struct clk_ops dpll_m4xen_ck_ops = {
        .set_rate       = &omap3_noncore_dpll_set_rate,
        .get_parent     = &omap2_init_dpll_parent,
 };
+#else
+static const struct clk_ops dpll_m4xen_ck_ops = {};
 #endif
 
+#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_ARCH_OMAP4) || \
+       defined(CONFIG_SOC_OMAP5) || defined(CONFIG_SOC_DRA7XX) || \
+       defined(CONFIG_SOC_AM33XX) || defined(CONFIG_SOC_AM43XX)
 static const struct clk_ops dpll_core_ck_ops = {
        .recalc_rate    = &omap3_dpll_recalc,
        .get_parent     = &omap2_init_dpll_parent,
 };
 
-#ifdef CONFIG_ARCH_OMAP3
-static const struct clk_ops omap3_dpll_core_ck_ops = {
-       .get_parent     = &omap2_init_dpll_parent,
-       .recalc_rate    = &omap3_dpll_recalc,
-       .round_rate     = &omap2_dpll_round_rate,
-};
-#endif
-
 static const struct clk_ops dpll_ck_ops = {
        .enable         = &omap3_noncore_dpll_enable,
        .disable        = &omap3_noncore_dpll_disable,
@@ -67,6 +62,33 @@ static const struct clk_ops dpll_no_gate_ck_ops = {
        .round_rate     = &omap2_dpll_round_rate,
        .set_rate       = &omap3_noncore_dpll_set_rate,
 };
+#else
+static const struct clk_ops dpll_core_ck_ops = {};
+static const struct clk_ops dpll_ck_ops = {};
+static const struct clk_ops dpll_no_gate_ck_ops = {};
+const struct clk_hw_omap_ops clkhwops_omap3_dpll = {};
+#endif
+
+#ifdef CONFIG_ARCH_OMAP2
+static const struct clk_ops omap2_dpll_core_ck_ops = {
+       .get_parent     = &omap2_init_dpll_parent,
+       .recalc_rate    = &omap2_dpllcore_recalc,
+       .round_rate     = &omap2_dpll_round_rate,
+       .set_rate       = &omap2_reprogram_dpllcore,
+};
+#else
+static const struct clk_ops omap2_dpll_core_ck_ops = {};
+#endif
+
+#ifdef CONFIG_ARCH_OMAP3
+static const struct clk_ops omap3_dpll_core_ck_ops = {
+       .get_parent     = &omap2_init_dpll_parent,
+       .recalc_rate    = &omap3_dpll_recalc,
+       .round_rate     = &omap2_dpll_round_rate,
+};
+#else
+static const struct clk_ops omap3_dpll_core_ck_ops = {};
+#endif
 
 #ifdef CONFIG_ARCH_OMAP3
 static const struct clk_ops omap3_dpll_ck_ops = {
@@ -193,14 +215,12 @@ static void ti_clk_register_dpll_x2(struct device_node *node,
  * @node: device node containing the DPLL info
  * @ops: ops for the DPLL
  * @ddt: DPLL data template to use
- * @init_flags: flags for controlling init types
  *
  * Initializes a DPLL clock from device tree data.
  */
 static void __init of_ti_dpll_setup(struct device_node *node,
                                    const struct clk_ops *ops,
-                                   const struct dpll_data *ddt,
-                                   u8 init_flags)
+                                   const struct dpll_data *ddt)
 {
        struct clk_hw_omap *clk_hw = NULL;
        struct clk_init_data *init = NULL;
@@ -241,13 +261,30 @@ static void __init of_ti_dpll_setup(struct device_node *node,
        init->parent_names = parent_names;
 
        dd->control_reg = ti_clk_get_reg_addr(node, 0);
-       dd->idlest_reg = ti_clk_get_reg_addr(node, 1);
-       dd->mult_div1_reg = ti_clk_get_reg_addr(node, 2);
 
-       if (!dd->control_reg || !dd->idlest_reg || !dd->mult_div1_reg)
+       /*
+        * Special case for OMAP2 DPLL, register order is different due to
+        * missing idlest_reg, also clkhwops is different. Detected from
+        * missing idlest_mask.
+        */
+       if (!dd->idlest_mask) {
+               dd->mult_div1_reg = ti_clk_get_reg_addr(node, 1);
+#ifdef CONFIG_ARCH_OMAP2
+               clk_hw->ops = &clkhwops_omap2xxx_dpll;
+               omap2xxx_clkt_dpllcore_init(&clk_hw->hw);
+#endif
+       } else {
+               dd->idlest_reg = ti_clk_get_reg_addr(node, 1);
+               if (!dd->idlest_reg)
+                       goto cleanup;
+
+               dd->mult_div1_reg = ti_clk_get_reg_addr(node, 2);
+       }
+
+       if (!dd->control_reg || !dd->mult_div1_reg)
                goto cleanup;
 
-       if (init_flags & DPLL_HAS_AUTOIDLE) {
+       if (dd->autoidle_mask) {
                dd->autoidle_reg = ti_clk_get_reg_addr(node, 3);
                if (!dd->autoidle_reg)
                        goto cleanup;
@@ -310,7 +347,7 @@ static void __init of_ti_omap3_dpll_setup(struct device_node *node)
                .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
        };
 
-       of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd, DPLL_HAS_AUTOIDLE);
+       of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd);
 }
 CLK_OF_DECLARE(ti_omap3_dpll_clock, "ti,omap3-dpll-clock",
               of_ti_omap3_dpll_setup);
@@ -329,7 +366,7 @@ static void __init of_ti_omap3_core_dpll_setup(struct device_node *node)
                .freqsel_mask = 0xf0,
        };
 
-       of_ti_dpll_setup(node, &omap3_dpll_core_ck_ops, &dd, DPLL_HAS_AUTOIDLE);
+       of_ti_dpll_setup(node, &omap3_dpll_core_ck_ops, &dd);
 }
 CLK_OF_DECLARE(ti_omap3_core_dpll_clock, "ti,omap3-dpll-core-clock",
               of_ti_omap3_core_dpll_setup);
@@ -349,7 +386,7 @@ static void __init of_ti_omap3_per_dpll_setup(struct device_node *node)
                .modes = (1 << DPLL_LOW_POWER_STOP) | (1 << DPLL_LOCKED),
        };
 
-       of_ti_dpll_setup(node, &omap3_dpll_per_ck_ops, &dd, DPLL_HAS_AUTOIDLE);
+       of_ti_dpll_setup(node, &omap3_dpll_per_ck_ops, &dd);
 }
 CLK_OF_DECLARE(ti_omap3_per_dpll_clock, "ti,omap3-dpll-per-clock",
               of_ti_omap3_per_dpll_setup);
@@ -371,7 +408,7 @@ static void __init of_ti_omap3_per_jtype_dpll_setup(struct device_node *node)
                .modes = (1 << DPLL_LOW_POWER_STOP) | (1 << DPLL_LOCKED),
        };
 
-       of_ti_dpll_setup(node, &omap3_dpll_per_ck_ops, &dd, DPLL_HAS_AUTOIDLE);
+       of_ti_dpll_setup(node, &omap3_dpll_per_ck_ops, &dd);
 }
 CLK_OF_DECLARE(ti_omap3_per_jtype_dpll_clock, "ti,omap3-dpll-per-j-type-clock",
               of_ti_omap3_per_jtype_dpll_setup);
@@ -391,11 +428,32 @@ static void __init of_ti_omap4_dpll_setup(struct device_node *node)
                .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
        };
 
-       of_ti_dpll_setup(node, &dpll_ck_ops, &dd, DPLL_HAS_AUTOIDLE);
+       of_ti_dpll_setup(node, &dpll_ck_ops, &dd);
 }
 CLK_OF_DECLARE(ti_omap4_dpll_clock, "ti,omap4-dpll-clock",
               of_ti_omap4_dpll_setup);
 
+static void __init of_ti_omap5_mpu_dpll_setup(struct device_node *node)
+{
+       const struct dpll_data dd = {
+               .idlest_mask = 0x1,
+               .enable_mask = 0x7,
+               .autoidle_mask = 0x7,
+               .mult_mask = 0x7ff << 8,
+               .div1_mask = 0x7f,
+               .max_multiplier = 2047,
+               .max_divider = 128,
+               .dcc_mask = BIT(22),
+               .dcc_rate = 1400000000, /* DCC beyond 1.4GHz */
+               .min_divider = 1,
+               .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
+       };
+
+       of_ti_dpll_setup(node, &dpll_ck_ops, &dd);
+}
+CLK_OF_DECLARE(of_ti_omap5_mpu_dpll_clock, "ti,omap5-mpu-dpll-clock",
+              of_ti_omap5_mpu_dpll_setup);
+
 static void __init of_ti_omap4_core_dpll_setup(struct device_node *node)
 {
        const struct dpll_data dd = {
@@ -410,7 +468,7 @@ static void __init of_ti_omap4_core_dpll_setup(struct device_node *node)
                .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
        };
 
-       of_ti_dpll_setup(node, &dpll_core_ck_ops, &dd, DPLL_HAS_AUTOIDLE);
+       of_ti_dpll_setup(node, &dpll_core_ck_ops, &dd);
 }
 CLK_OF_DECLARE(ti_omap4_core_dpll_clock, "ti,omap4-dpll-core-clock",
               of_ti_omap4_core_dpll_setup);
@@ -433,7 +491,7 @@ static void __init of_ti_omap4_m4xen_dpll_setup(struct device_node *node)
                .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
        };
 
-       of_ti_dpll_setup(node, &dpll_m4xen_ck_ops, &dd, DPLL_HAS_AUTOIDLE);
+       of_ti_dpll_setup(node, &dpll_m4xen_ck_ops, &dd);
 }
 CLK_OF_DECLARE(ti_omap4_m4xen_dpll_clock, "ti,omap4-dpll-m4xen-clock",
               of_ti_omap4_m4xen_dpll_setup);
@@ -454,7 +512,7 @@ static void __init of_ti_omap4_jtype_dpll_setup(struct device_node *node)
                .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
        };
 
-       of_ti_dpll_setup(node, &dpll_m4xen_ck_ops, &dd, DPLL_HAS_AUTOIDLE);
+       of_ti_dpll_setup(node, &dpll_m4xen_ck_ops, &dd);
 }
 CLK_OF_DECLARE(ti_omap4_jtype_dpll_clock, "ti,omap4-dpll-j-type-clock",
               of_ti_omap4_jtype_dpll_setup);
@@ -465,7 +523,6 @@ static void __init of_ti_am3_no_gate_dpll_setup(struct device_node *node)
        const struct dpll_data dd = {
                .idlest_mask = 0x1,
                .enable_mask = 0x7,
-               .autoidle_mask = 0x7,
                .mult_mask = 0x7ff << 8,
                .div1_mask = 0x7f,
                .max_multiplier = 2047,
@@ -474,7 +531,7 @@ static void __init of_ti_am3_no_gate_dpll_setup(struct device_node *node)
                .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
        };
 
-       of_ti_dpll_setup(node, &dpll_no_gate_ck_ops, &dd, 0);
+       of_ti_dpll_setup(node, &dpll_no_gate_ck_ops, &dd);
 }
 CLK_OF_DECLARE(ti_am3_no_gate_dpll_clock, "ti,am3-dpll-no-gate-clock",
               of_ti_am3_no_gate_dpll_setup);
@@ -484,7 +541,6 @@ static void __init of_ti_am3_jtype_dpll_setup(struct device_node *node)
        const struct dpll_data dd = {
                .idlest_mask = 0x1,
                .enable_mask = 0x7,
-               .autoidle_mask = 0x7,
                .mult_mask = 0x7ff << 8,
                .div1_mask = 0x7f,
                .max_multiplier = 4095,
@@ -494,7 +550,7 @@ static void __init of_ti_am3_jtype_dpll_setup(struct device_node *node)
                .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
        };
 
-       of_ti_dpll_setup(node, &dpll_ck_ops, &dd, 0);
+       of_ti_dpll_setup(node, &dpll_ck_ops, &dd);
 }
 CLK_OF_DECLARE(ti_am3_jtype_dpll_clock, "ti,am3-dpll-j-type-clock",
               of_ti_am3_jtype_dpll_setup);
@@ -504,7 +560,6 @@ static void __init of_ti_am3_no_gate_jtype_dpll_setup(struct device_node *node)
        const struct dpll_data dd = {
                .idlest_mask = 0x1,
                .enable_mask = 0x7,
-               .autoidle_mask = 0x7,
                .mult_mask = 0x7ff << 8,
                .div1_mask = 0x7f,
                .max_multiplier = 2047,
@@ -514,7 +569,7 @@ static void __init of_ti_am3_no_gate_jtype_dpll_setup(struct device_node *node)
                .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
        };
 
-       of_ti_dpll_setup(node, &dpll_no_gate_ck_ops, &dd, 0);
+       of_ti_dpll_setup(node, &dpll_no_gate_ck_ops, &dd);
 }
 CLK_OF_DECLARE(ti_am3_no_gate_jtype_dpll_clock,
               "ti,am3-dpll-no-gate-j-type-clock",
@@ -525,7 +580,6 @@ static void __init of_ti_am3_dpll_setup(struct device_node *node)
        const struct dpll_data dd = {
                .idlest_mask = 0x1,
                .enable_mask = 0x7,
-               .autoidle_mask = 0x7,
                .mult_mask = 0x7ff << 8,
                .div1_mask = 0x7f,
                .max_multiplier = 2047,
@@ -534,7 +588,7 @@ static void __init of_ti_am3_dpll_setup(struct device_node *node)
                .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
        };
 
-       of_ti_dpll_setup(node, &dpll_ck_ops, &dd, 0);
+       of_ti_dpll_setup(node, &dpll_ck_ops, &dd);
 }
 CLK_OF_DECLARE(ti_am3_dpll_clock, "ti,am3-dpll-clock", of_ti_am3_dpll_setup);
 
@@ -543,7 +597,6 @@ static void __init of_ti_am3_core_dpll_setup(struct device_node *node)
        const struct dpll_data dd = {
                .idlest_mask = 0x1,
                .enable_mask = 0x7,
-               .autoidle_mask = 0x7,
                .mult_mask = 0x7ff << 8,
                .div1_mask = 0x7f,
                .max_multiplier = 2047,
@@ -552,7 +605,22 @@ static void __init of_ti_am3_core_dpll_setup(struct device_node *node)
                .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
        };
 
-       of_ti_dpll_setup(node, &dpll_core_ck_ops, &dd, 0);
+       of_ti_dpll_setup(node, &dpll_core_ck_ops, &dd);
 }
 CLK_OF_DECLARE(ti_am3_core_dpll_clock, "ti,am3-dpll-core-clock",
               of_ti_am3_core_dpll_setup);
+
+static void __init of_ti_omap2_core_dpll_setup(struct device_node *node)
+{
+       const struct dpll_data dd = {
+               .enable_mask = 0x3,
+               .mult_mask = 0x3ff << 12,
+               .div1_mask = 0xf << 8,
+               .max_divider = 16,
+               .min_divider = 1,
+       };
+
+       of_ti_dpll_setup(node, &omap2_dpll_core_ck_ops, &dd);
+}
+CLK_OF_DECLARE(ti_omap2_core_dpll_clock, "ti,omap2-dpll-core-clock",
+              of_ti_omap2_core_dpll_setup);
index 58734817d5027956107fef67847f2585a691afdc..b326d2797feb23c0ed84fe52be5b9905102f0234 100644 (file)
@@ -185,7 +185,7 @@ of_ti_composite_no_wait_gate_clk_setup(struct device_node *node)
 CLK_OF_DECLARE(ti_composite_no_wait_gate_clk, "ti,composite-no-wait-gate-clock",
               of_ti_composite_no_wait_gate_clk_setup);
 
-#ifdef CONFIG_ARCH_OMAP3
+#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
 static void __init of_ti_composite_interface_clk_setup(struct device_node *node)
 {
        _of_ti_composite_gate_clk_setup(node, &clkhwops_iclk_wait);
index 320a2b168bb20fab7e69f812d4ca6307cb89b28c..9c3e8c4aaa40c0b8a46048bab734286941a77e6b 100644 (file)
@@ -94,6 +94,7 @@ static void __init of_ti_no_wait_interface_clk_setup(struct device_node *node)
 CLK_OF_DECLARE(ti_no_wait_interface_clk, "ti,omap3-no-wait-interface-clock",
               of_ti_no_wait_interface_clk_setup);
 
+#ifdef CONFIG_ARCH_OMAP3
 static void __init of_ti_hsotgusb_interface_clk_setup(struct device_node *node)
 {
        _of_ti_interface_clk_setup(node,
@@ -123,3 +124,13 @@ static void __init of_ti_am35xx_interface_clk_setup(struct device_node *node)
 }
 CLK_OF_DECLARE(ti_am35xx_interface_clk, "ti,am35xx-interface-clock",
               of_ti_am35xx_interface_clk_setup);
+#endif
+
+#ifdef CONFIG_SOC_OMAP2430
+static void __init of_ti_omap2430_interface_clk_setup(struct device_node *node)
+{
+       _of_ti_interface_clk_setup(node, &clkhwops_omap2430_i2chs_wait);
+}
+CLK_OF_DECLARE(ti_omap2430_interface_clk, "ti,omap2430-interface-clock",
+              of_ti_omap2430_interface_clk_setup);
+#endif
index 1fbe11f2a14603e499042974e7fc8064414978a4..e473d6555f96de1666f725c2966373d2b7e21ce1 100644 (file)
@@ -185,7 +185,7 @@ config CPU_FREQ_GOV_CONSERVATIVE
 
 config GENERIC_CPUFREQ_CPU0
        tristate "Generic CPU0 cpufreq driver"
-       depends on HAVE_CLK && REGULATOR && OF && THERMAL && CPU_THERMAL
+       depends on HAVE_CLK && OF
        select PM_OPP
        help
          This adds a generic cpufreq driver for CPU0 frequency management.
index 36d20d0fce27b03a073269afa5ecfa80e24ddea0..ebac671150098b179e091aba0744a7757d3b1b2e 100644 (file)
@@ -5,8 +5,7 @@
 # big LITTLE core layer and glue drivers
 config ARM_BIG_LITTLE_CPUFREQ
        tristate "Generic ARM big LITTLE CPUfreq driver"
-       depends on (BIG_LITTLE && ARM_CPU_TOPOLOGY) || (ARM64 && SMP)
-       depends on HAVE_CLK
+       depends on ARM && BIG_LITTLE && ARM_CPU_TOPOLOGY && HAVE_CLK
        select PM_OPP
        help
          This enables the Generic CPUfreq driver for ARM big.LITTLE platforms.
index 09b9129c7bd3b806cbacde8471ed574dd260ea25..ee1ae303a07c45176f562ce0662fe2ad1051640a 100644 (file)
@@ -104,7 +104,7 @@ static int cpu0_cpufreq_init(struct cpufreq_policy *policy)
 }
 
 static struct cpufreq_driver cpu0_cpufreq_driver = {
-       .flags = CPUFREQ_STICKY,
+       .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
        .verify = cpufreq_generic_frequency_table_verify,
        .target_index = cpu0_set_target,
        .get = cpufreq_generic_get,
index ae11dd51f81d4d94156a4d2a73abe6f340746b46..aed2b0cb83dc109203368d85b8cc76178211053c 100644 (file)
@@ -1816,20 +1816,55 @@ EXPORT_SYMBOL(cpufreq_unregister_notifier);
  *                              GOVERNORS                            *
  *********************************************************************/
 
+/* Must set freqs->new to intermediate frequency */
+static int __target_intermediate(struct cpufreq_policy *policy,
+                                struct cpufreq_freqs *freqs, int index)
+{
+       int ret;
+
+       freqs->new = cpufreq_driver->get_intermediate(policy, index);
+
+       /* We don't need to switch to intermediate freq */
+       if (!freqs->new)
+               return 0;
+
+       pr_debug("%s: cpu: %d, switching to intermediate freq: oldfreq: %u, intermediate freq: %u\n",
+                __func__, policy->cpu, freqs->old, freqs->new);
+
+       cpufreq_freq_transition_begin(policy, freqs);
+       ret = cpufreq_driver->target_intermediate(policy, index);
+       cpufreq_freq_transition_end(policy, freqs, ret);
+
+       if (ret)
+               pr_err("%s: Failed to change to intermediate frequency: %d\n",
+                      __func__, ret);
+
+       return ret;
+}
+
 static int __target_index(struct cpufreq_policy *policy,
                          struct cpufreq_frequency_table *freq_table, int index)
 {
-       struct cpufreq_freqs freqs;
+       struct cpufreq_freqs freqs = {.old = policy->cur, .flags = 0};
+       unsigned int intermediate_freq = 0;
        int retval = -EINVAL;
        bool notify;
 
        notify = !(cpufreq_driver->flags & CPUFREQ_ASYNC_NOTIFICATION);
-
        if (notify) {
-               freqs.old = policy->cur;
-               freqs.new = freq_table[index].frequency;
-               freqs.flags = 0;
+               /* Handle switching to intermediate frequency */
+               if (cpufreq_driver->get_intermediate) {
+                       retval = __target_intermediate(policy, &freqs, index);
+                       if (retval)
+                               return retval;
+
+                       intermediate_freq = freqs.new;
+                       /* Set old freq to intermediate */
+                       if (intermediate_freq)
+                               freqs.old = freqs.new;
+               }
 
+               freqs.new = freq_table[index].frequency;
                pr_debug("%s: cpu: %d, oldfreq: %u, new freq: %u\n",
                         __func__, policy->cpu, freqs.old, freqs.new);
 
@@ -1841,9 +1876,23 @@ static int __target_index(struct cpufreq_policy *policy,
                pr_err("%s: Failed to change cpu frequency: %d\n", __func__,
                       retval);
 
-       if (notify)
+       if (notify) {
                cpufreq_freq_transition_end(policy, &freqs, retval);
 
+               /*
+                * Failed after setting to intermediate freq? Driver should have
+                * reverted back to initial frequency and so should we. Check
+                * here for intermediate_freq instead of get_intermediate, in
+                * case we have't switched to intermediate freq at all.
+                */
+               if (unlikely(retval && intermediate_freq)) {
+                       freqs.old = intermediate_freq;
+                       freqs.new = policy->restore_freq;
+                       cpufreq_freq_transition_begin(policy, &freqs);
+                       cpufreq_freq_transition_end(policy, &freqs, 0);
+               }
+       }
+
        return retval;
 }
 
@@ -1875,6 +1924,9 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
        if (target_freq == policy->cur)
                return 0;
 
+       /* Save last value to restore later on errors */
+       policy->restore_freq = policy->cur;
+
        if (cpufreq_driver->target)
                retval = cpufreq_driver->target(policy, target_freq, relation);
        else if (cpufreq_driver->target_index) {
@@ -2361,7 +2413,8 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
            !(driver_data->setpolicy || driver_data->target_index ||
                    driver_data->target) ||
             (driver_data->setpolicy && (driver_data->target_index ||
-                   driver_data->target)))
+                   driver_data->target)) ||
+            (!!driver_data->get_intermediate != !!driver_data->target_intermediate))
                return -EINVAL;
 
        pr_debug("trying to register driver %s\n", driver_data->name);
index e1c6433b16e06653d159782b0a053830e570e3af..1b44496b2d2b3548bab6c1aff39a5c4f25c17428 100644 (file)
@@ -36,14 +36,29 @@ void dbs_check_cpu(struct dbs_data *dbs_data, int cpu)
        struct od_dbs_tuners *od_tuners = dbs_data->tuners;
        struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
        struct cpufreq_policy *policy;
+       unsigned int sampling_rate;
        unsigned int max_load = 0;
        unsigned int ignore_nice;
        unsigned int j;
 
-       if (dbs_data->cdata->governor == GOV_ONDEMAND)
+       if (dbs_data->cdata->governor == GOV_ONDEMAND) {
+               struct od_cpu_dbs_info_s *od_dbs_info =
+                               dbs_data->cdata->get_cpu_dbs_info_s(cpu);
+
+               /*
+                * Sometimes, the ondemand governor uses an additional
+                * multiplier to give long delays. So apply this multiplier to
+                * the 'sampling_rate', so as to keep the wake-up-from-idle
+                * detection logic a bit conservative.
+                */
+               sampling_rate = od_tuners->sampling_rate;
+               sampling_rate *= od_dbs_info->rate_mult;
+
                ignore_nice = od_tuners->ignore_nice_load;
-       else
+       } else {
+               sampling_rate = cs_tuners->sampling_rate;
                ignore_nice = cs_tuners->ignore_nice_load;
+       }
 
        policy = cdbs->cur_policy;
 
@@ -96,7 +111,46 @@ void dbs_check_cpu(struct dbs_data *dbs_data, int cpu)
                if (unlikely(!wall_time || wall_time < idle_time))
                        continue;
 
-               load = 100 * (wall_time - idle_time) / wall_time;
+               /*
+                * If the CPU had gone completely idle, and a task just woke up
+                * on this CPU now, it would be unfair to calculate 'load' the
+                * usual way for this elapsed time-window, because it will show
+                * near-zero load, irrespective of how CPU intensive that task
+                * actually is. This is undesirable for latency-sensitive bursty
+                * workloads.
+                *
+                * To avoid this, we reuse the 'load' from the previous
+                * time-window and give this task a chance to start with a
+                * reasonably high CPU frequency. (However, we shouldn't over-do
+                * this copy, lest we get stuck at a high load (high frequency)
+                * for too long, even when the current system load has actually
+                * dropped down. So we perform the copy only once, upon the
+                * first wake-up from idle.)
+                *
+                * Detecting this situation is easy: the governor's deferrable
+                * timer would not have fired during CPU-idle periods. Hence
+                * an unusually large 'wall_time' (as compared to the sampling
+                * rate) indicates this scenario.
+                *
+                * prev_load can be zero in two cases and we must recalculate it
+                * for both cases:
+                * - during long idle intervals
+                * - explicitly set to zero
+                */
+               if (unlikely(wall_time > (2 * sampling_rate) &&
+                            j_cdbs->prev_load)) {
+                       load = j_cdbs->prev_load;
+
+                       /*
+                        * Perform a destructive copy, to ensure that we copy
+                        * the previous load only once, upon the first wake-up
+                        * from idle.
+                        */
+                       j_cdbs->prev_load = 0;
+               } else {
+                       load = 100 * (wall_time - idle_time) / wall_time;
+                       j_cdbs->prev_load = load;
+               }
 
                if (load > max_load)
                        max_load = load;
@@ -318,11 +372,18 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy,
                for_each_cpu(j, policy->cpus) {
                        struct cpu_dbs_common_info *j_cdbs =
                                dbs_data->cdata->get_cpu_cdbs(j);
+                       unsigned int prev_load;
 
                        j_cdbs->cpu = j;
                        j_cdbs->cur_policy = policy;
                        j_cdbs->prev_cpu_idle = get_cpu_idle_time(j,
                                               &j_cdbs->prev_cpu_wall, io_busy);
+
+                       prev_load = (unsigned int)
+                               (j_cdbs->prev_cpu_wall - j_cdbs->prev_cpu_idle);
+                       j_cdbs->prev_load = 100 * prev_load /
+                                       (unsigned int) j_cdbs->prev_cpu_wall;
+
                        if (ignore_nice)
                                j_cdbs->prev_cpu_nice =
                                        kcpustat_cpu(j).cpustat[CPUTIME_NICE];
index bfb9ae14142c85a82341bf63afa594ed0fdaa278..cc401d147e727615c8255a951a08065ae954fb23 100644 (file)
@@ -134,6 +134,13 @@ struct cpu_dbs_common_info {
        u64 prev_cpu_idle;
        u64 prev_cpu_wall;
        u64 prev_cpu_nice;
+       /*
+        * Used to keep track of load in the previous interval. However, when
+        * explicitly set to zero, it is used as a flag to ensure that we copy
+        * the previous load to the current interval only once, upon the first
+        * wake-up from idle.
+        */
+       unsigned int prev_load;
        struct cpufreq_policy *cur_policy;
        struct delayed_work work;
        /*
index aebd4572eb6d7554d08da2ec316c898b2114eb8f..4e7f492ad5839d43ef02a092d1b8cd9fde735278 100644 (file)
@@ -691,14 +691,8 @@ MODULE_DEVICE_TABLE(x86cpu, intel_pstate_cpu_ids);
 
 static int intel_pstate_init_cpu(unsigned int cpunum)
 {
-
-       const struct x86_cpu_id *id;
        struct cpudata *cpu;
 
-       id = x86_match_cpu(intel_pstate_cpu_ids);
-       if (!id)
-               return -ENODEV;
-
        all_cpu_data[cpunum] = kzalloc(sizeof(struct cpudata), GFP_KERNEL);
        if (!all_cpu_data[cpunum])
                return -ENOMEM;
index 0af618abebafa4b44b323d1811c1f885a52e0beb..3607070797af307f959d0f413a2497acd262ba95 100644 (file)
@@ -138,7 +138,7 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
        struct cpufreq_frequency_table *table;
        struct cpu_data *data;
        unsigned int cpu = policy->cpu;
-       u64 transition_latency_hz;
+       u64 u64temp;
 
        np = of_get_cpu_node(cpu, NULL);
        if (!np)
@@ -206,9 +206,10 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
        for_each_cpu(i, per_cpu(cpu_mask, cpu))
                per_cpu(cpu_data, i) = data;
 
-       transition_latency_hz = 12ULL * NSEC_PER_SEC;
-       policy->cpuinfo.transition_latency =
-               do_div(transition_latency_hz, fsl_get_sys_freq());
+       /* Minimum transition latency is 12 platform clocks */
+       u64temp = 12ULL * NSEC_PER_SEC;
+       do_div(u64temp, fsl_get_sys_freq());
+       policy->cpuinfo.transition_latency = u64temp + 1;
 
        of_node_put(np);
 
index 6e774c6ac20bf3ab82fe0e0dc3bd204de04009f9..8084c7f7e206320eccb6a3bb4e52b3e8e41831fb 100644 (file)
@@ -45,46 +45,54 @@ static struct clk *cpu_clk;
 static struct clk *pll_x_clk;
 static struct clk *pll_p_clk;
 static struct clk *emc_clk;
+static bool pll_x_prepared;
 
-static int tegra_cpu_clk_set_rate(unsigned long rate)
+static unsigned int tegra_get_intermediate(struct cpufreq_policy *policy,
+                                          unsigned int index)
+{
+       unsigned int ifreq = clk_get_rate(pll_p_clk) / 1000;
+
+       /*
+        * Don't switch to intermediate freq if:
+        * - we are already at it, i.e. policy->cur == ifreq
+        * - index corresponds to ifreq
+        */
+       if ((freq_table[index].frequency == ifreq) || (policy->cur == ifreq))
+               return 0;
+
+       return ifreq;
+}
+
+static int tegra_target_intermediate(struct cpufreq_policy *policy,
+                                    unsigned int index)
 {
        int ret;
 
        /*
         * Take an extra reference to the main pll so it doesn't turn
-        * off when we move the cpu off of it
+        * off when we move the cpu off of it as enabling it again while we
+        * switch to it from tegra_target() would take additional time.
+        *
+        * When target-freq is equal to intermediate freq we don't need to
+        * switch to an intermediate freq and so this routine isn't called.
+        * Also, we wouldn't be using pll_x anymore and must not take extra
+        * reference to it, as it can be disabled now to save some power.
         */
        clk_prepare_enable(pll_x_clk);
 
        ret = clk_set_parent(cpu_clk, pll_p_clk);
-       if (ret) {
-               pr_err("Failed to switch cpu to clock pll_p\n");
-               goto out;
-       }
-
-       if (rate == clk_get_rate(pll_p_clk))
-               goto out;
-
-       ret = clk_set_rate(pll_x_clk, rate);
-       if (ret) {
-               pr_err("Failed to change pll_x to %lu\n", rate);
-               goto out;
-       }
-
-       ret = clk_set_parent(cpu_clk, pll_x_clk);
-       if (ret) {
-               pr_err("Failed to switch cpu to clock pll_x\n");
-               goto out;
-       }
+       if (ret)
+               clk_disable_unprepare(pll_x_clk);
+       else
+               pll_x_prepared = true;
 
-out:
-       clk_disable_unprepare(pll_x_clk);
        return ret;
 }
 
 static int tegra_target(struct cpufreq_policy *policy, unsigned int index)
 {
        unsigned long rate = freq_table[index].frequency;
+       unsigned int ifreq = clk_get_rate(pll_p_clk) / 1000;
        int ret = 0;
 
        /*
@@ -98,10 +106,30 @@ static int tegra_target(struct cpufreq_policy *policy, unsigned int index)
        else
                clk_set_rate(emc_clk, 100000000);  /* emc 50Mhz */
 
-       ret = tegra_cpu_clk_set_rate(rate * 1000);
+       /*
+        * target freq == pll_p, don't need to take extra reference to pll_x_clk
+        * as it isn't used anymore.
+        */
+       if (rate == ifreq)
+               return clk_set_parent(cpu_clk, pll_p_clk);
+
+       ret = clk_set_rate(pll_x_clk, rate * 1000);
+       /* Restore to earlier frequency on error, i.e. pll_x */
        if (ret)
-               pr_err("cpu-tegra: Failed to set cpu frequency to %lu kHz\n",
-                       rate);
+               pr_err("Failed to change pll_x to %lu\n", rate);
+
+       ret = clk_set_parent(cpu_clk, pll_x_clk);
+       /* This shouldn't fail while changing or restoring */
+       WARN_ON(ret);
+
+       /*
+        * Drop count to pll_x clock only if we switched to intermediate freq
+        * earlier while transitioning to a target frequency.
+        */
+       if (pll_x_prepared) {
+               clk_disable_unprepare(pll_x_clk);
+               pll_x_prepared = false;
+       }
 
        return ret;
 }
@@ -137,16 +165,18 @@ static int tegra_cpu_exit(struct cpufreq_policy *policy)
 }
 
 static struct cpufreq_driver tegra_cpufreq_driver = {
-       .flags          = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
-       .verify         = cpufreq_generic_frequency_table_verify,
-       .target_index   = tegra_target,
-       .get            = cpufreq_generic_get,
-       .init           = tegra_cpu_init,
-       .exit           = tegra_cpu_exit,
-       .name           = "tegra",
-       .attr           = cpufreq_generic_attr,
+       .flags                  = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
+       .verify                 = cpufreq_generic_frequency_table_verify,
+       .get_intermediate       = tegra_get_intermediate,
+       .target_intermediate    = tegra_target_intermediate,
+       .target_index           = tegra_target,
+       .get                    = cpufreq_generic_get,
+       .init                   = tegra_cpu_init,
+       .exit                   = tegra_cpu_exit,
+       .name                   = "tegra",
+       .attr                   = cpufreq_generic_attr,
 #ifdef CONFIG_PM
-       .suspend        = cpufreq_generic_suspend,
+       .suspend                = cpufreq_generic_suspend,
 #endif
 };
 
index 719f6fb5b1c35d00108c47a61918741200eae75d..74f5788d50b12986c68fdfdda10d92218314c45e 100644 (file)
@@ -73,12 +73,10 @@ static int fastsleep_loop(struct cpuidle_device *dev,
                return index;
 
        new_lpcr = old_lpcr;
-       new_lpcr &= ~(LPCR_MER | LPCR_PECE); /* lpcr[mer] must be 0 */
-
-       /* exit powersave upon external interrupt, but not decrementer
-        * interrupt.
+       /* Do not exit powersave upon decrementer as we've setup the timer
+        * offload.
         */
-       new_lpcr |= LPCR_PECE0;
+       new_lpcr &= ~LPCR_PECE1;
 
        mtspr(SPRN_LPCR, new_lpcr);
        power7_sleep();
index 136d6a283e0a3818846eab4e78e7e9245b7d6f07..9634f20e392611b32349aaa348aa3980f2344ecd 100644 (file)
@@ -187,8 +187,11 @@ static int poll_idle(struct cpuidle_device *dev,
 
        t1 = ktime_get();
        local_irq_enable();
-       while (!need_resched())
-               cpu_relax();
+       if (!current_set_polling_and_test()) {
+               while (!need_resched())
+                       cpu_relax();
+       }
+       current_clr_polling();
 
        t2 = ktime_get();
        diff = ktime_to_us(ktime_sub(t2, t1));
index f066fa23cc050f2fb8ea1ed605d24774f11956dc..02f177aeb16c8488be0335186680ed8947b39c00 100644 (file)
@@ -313,7 +313,7 @@ config CRYPTO_DEV_S5P
 
 config CRYPTO_DEV_NX
        bool "Support for IBM Power7+ in-Nest cryptographic acceleration"
-       depends on PPC64 && IBMVIO
+       depends on PPC64 && IBMVIO && !CPU_LITTLE_ENDIAN
        default n
        help
          Support for Power7+ in-Nest cryptographic acceleration.
index d9c9cb4665dbf8899d3fe5b7ce9269b3fdee5961..2ebc9071e354884a80de8c17a978ba7bb3d28077 100644 (file)
@@ -2614,7 +2614,7 @@ static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
 
                desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx,
                                                &of_flags);
-               if (!IS_ERR(desc))
+               if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER))
                        break;
        }
 
index d8a22c2a579d458c17f4fd5d7b57f5bdee13cf65..70da9eb52a42cb623c887ca91e31fa250f8fd33f 100644 (file)
@@ -1,2 +1,3 @@
 obj-y                  += drm/ vga/
 obj-$(CONFIG_TEGRA_HOST1X)     += host1x/
+obj-$(CONFIG_IMX_IPUV3_CORE)   += ipu-v3/
index d1cc2f613a78bb29e9969953402f856d77edd9e9..f5120046ff800a5433df4d52bce57ff589286ddf 100644 (file)
@@ -83,6 +83,8 @@ config DRM_KMS_CMA_HELPER
 
 source "drivers/gpu/drm/i2c/Kconfig"
 
+source "drivers/gpu/drm/bridge/Kconfig"
+
 config DRM_TDFX
        tristate "3dfx Banshee/Voodoo3+"
        depends on DRM && PCI
@@ -199,5 +201,3 @@ source "drivers/gpu/drm/msm/Kconfig"
 source "drivers/gpu/drm/tegra/Kconfig"
 
 source "drivers/gpu/drm/panel/Kconfig"
-
-source "drivers/gpu/drm/bridge/Kconfig"
index 48e38ba2278361673c4d6b2f6aa3c6a6e1f869b4..dd2ba4269740f563ac29eecadbe62b40bf55ade3 100644 (file)
@@ -14,7 +14,7 @@ drm-y       :=        drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
                drm_info.o drm_debugfs.o drm_encoder_slave.o \
                drm_trace_points.o drm_global.o drm_prime.o \
                drm_rect.o drm_vma_manager.o drm_flip_work.o \
-               drm_plane_helper.o
+               drm_modeset_lock.o
 
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
@@ -23,7 +23,8 @@ drm-$(CONFIG_DRM_PANEL) += drm_panel.o
 
 drm-usb-y   := drm_usb.o
 
-drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o
+drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \
+               drm_plane_helper.o
 drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
 drm_kms_helper-$(CONFIG_DRM_KMS_FB_HELPER) += drm_fb_helper.o
 drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o
index 32982da82694be753d1072c90136d842a8a866ec..8ab3cd1a8cdbdad1385a9c9042582e9fff05c855 100644 (file)
@@ -173,7 +173,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
        if (ret)
                goto err_kms;
 
-       ret = drm_irq_install(dev);
+       ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0));
        if (ret)
                goto err_kms;
 
@@ -402,7 +402,7 @@ static struct platform_driver armada_drm_platform_driver = {
 
 static int __init armada_drm_init(void)
 {
-       armada_drm_driver.num_ioctls = DRM_ARRAY_SIZE(armada_ioctls);
+       armada_drm_driver.num_ioctls = ARRAY_SIZE(armada_ioctls);
        return platform_driver_register(&armada_drm_platform_driver);
 }
 module_init(armada_drm_init);
index 948cb14c561ec501e10bae0f89adb043dc0b2808..fd166f532ab94f30f2bc3b0d9eff226ea9a07fd7 100644 (file)
@@ -181,10 +181,8 @@ void armada_fbdev_lastclose(struct drm_device *dev)
 {
        struct armada_private *priv = dev->dev_private;
 
-       drm_modeset_lock_all(dev);
        if (priv->fbdev)
-               drm_fb_helper_restore_fbdev_mode(priv->fbdev);
-       drm_modeset_unlock_all(dev);
+               drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev);
 }
 
 void armada_fbdev_fini(struct drm_device *dev)
index 887816f43476937fcfbceed5207025b63eccb132..bb9b642d848511217d2900faf2b4bc71a091536a 100644 (file)
@@ -433,7 +433,6 @@ armada_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
 
        if (dobj->obj.filp) {
                struct address_space *mapping;
-               gfp_t gfp;
                int count;
 
                count = dobj->obj.size / PAGE_SIZE;
@@ -441,12 +440,11 @@ armada_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
                        goto free_sgt;
 
                mapping = file_inode(dobj->obj.filp)->i_mapping;
-               gfp = mapping_gfp_mask(mapping);
 
                for_each_sg(sgt->sgl, sg, count, i) {
                        struct page *page;
 
-                       page = shmem_read_mapping_page_gfp(mapping, i, gfp);
+                       page = shmem_read_mapping_page(mapping, i);
                        if (IS_ERR(page)) {
                                num = i;
                                goto release;
index 8df4f284ee24702951caff2e14d3d783d5a0eded..171aa0622b665e5805d2fb0876b387d48c59ede7 100644 (file)
@@ -4,6 +4,6 @@
 
 ccflags-y := -Iinclude/drm
 
-ast-y := ast_drv.o ast_main.o ast_mode.o ast_fb.o ast_ttm.o ast_post.o
+ast-y := ast_drv.o ast_main.o ast_mode.o ast_fb.o ast_ttm.o ast_post.o ast_dp501.o
 
-obj-$(CONFIG_DRM_AST) := ast.o
\ No newline at end of file
+obj-$(CONFIG_DRM_AST) := ast.o
diff --git a/drivers/gpu/drm/ast/ast_dp501.c b/drivers/gpu/drm/ast/ast_dp501.c
new file mode 100644 (file)
index 0000000..5da4b62
--- /dev/null
@@ -0,0 +1,410 @@
+
+#include <linux/firmware.h>
+#include <drm/drmP.h>
+#include "ast_drv.h"
+MODULE_FIRMWARE("ast_dp501_fw.bin");
+
+int ast_load_dp501_microcode(struct drm_device *dev)
+{
+       struct ast_private *ast = dev->dev_private;
+       static char *fw_name = "ast_dp501_fw.bin";
+       int err;
+       err = request_firmware(&ast->dp501_fw, fw_name, dev->dev);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+static void send_ack(struct ast_private *ast)
+{
+       u8 sendack;
+       sendack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0xff);
+       sendack |= 0x80;
+       ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0x00, sendack);
+}
+
+static void send_nack(struct ast_private *ast)
+{
+       u8 sendack;
+       sendack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0xff);
+       sendack &= ~0x80;
+       ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0x00, sendack);
+}
+
+static bool wait_ack(struct ast_private *ast)
+{
+       u8 waitack;
+       u32 retry = 0;
+       do {
+               waitack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd2, 0xff);
+               waitack &= 0x80;
+               udelay(100);
+       } while ((!waitack) && (retry++ < 1000));
+
+       if (retry < 1000)
+               return true;
+       else
+               return false;
+}
+
+static bool wait_nack(struct ast_private *ast)
+{
+       u8 waitack;
+       u32 retry = 0;
+       do {
+               waitack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd2, 0xff);
+               waitack &= 0x80;
+               udelay(100);
+       } while ((waitack) && (retry++ < 1000));
+
+       if (retry < 1000)
+               return true;
+       else
+               return false;
+}
+
+static void set_cmd_trigger(struct ast_private *ast)
+{
+       ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, ~0x40, 0x40);
+}
+
+static void clear_cmd_trigger(struct ast_private *ast)
+{
+       ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, ~0x40, 0x00);
+}
+
+#if 0
+static bool wait_fw_ready(struct ast_private *ast)
+{
+       u8 waitready;
+       u32 retry = 0;
+       do {
+               waitready = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd2, 0xff);
+               waitready &= 0x40;
+               udelay(100);
+       } while ((!waitready) && (retry++ < 1000));
+
+       if (retry < 1000)
+               return true;
+       else
+               return false;
+}
+#endif
+
+static bool ast_write_cmd(struct drm_device *dev, u8 data)
+{
+       struct ast_private *ast = dev->dev_private;
+       int retry = 0;
+       if (wait_nack(ast)) {
+               send_nack(ast);
+               ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9a, 0x00, data);
+               send_ack(ast);
+               set_cmd_trigger(ast);
+               do {
+                       if (wait_ack(ast)) {
+                               clear_cmd_trigger(ast);
+                               send_nack(ast);
+                               return true;
+                       }
+               } while (retry++ < 100);
+       }
+       clear_cmd_trigger(ast);
+       send_nack(ast);
+       return false;
+}
+
+static bool ast_write_data(struct drm_device *dev, u8 data)
+{
+       struct ast_private *ast = dev->dev_private;
+
+       if (wait_nack(ast)) {
+               send_nack(ast);
+               ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9a, 0x00, data);
+               send_ack(ast);
+               if (wait_ack(ast)) {
+                       send_nack(ast);
+                       return true;
+               }
+       }
+       send_nack(ast);
+       return false;
+}
+
+#if 0
+static bool ast_read_data(struct drm_device *dev, u8 *data)
+{
+       struct ast_private *ast = dev->dev_private;
+       u8 tmp;
+
+       *data = 0;
+
+       if (wait_ack(ast) == false)
+               return false;
+       tmp = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd3, 0xff);
+       *data = tmp;
+       if (wait_nack(ast) == false) {
+               send_nack(ast);
+               return false;
+       }
+       send_nack(ast);
+       return true;
+}
+
+static void clear_cmd(struct ast_private *ast)
+{
+       send_nack(ast);
+       ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9a, 0x00, 0x00);
+}
+#endif
+
+void ast_set_dp501_video_output(struct drm_device *dev, u8 mode)
+{
+       ast_write_cmd(dev, 0x40);
+       ast_write_data(dev, mode);
+
+       msleep(10);
+}
+
+static u32 get_fw_base(struct ast_private *ast)
+{
+       return ast_mindwm(ast, 0x1e6e2104) & 0x7fffffff;
+}
+
+bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size)
+{
+       struct ast_private *ast = dev->dev_private;
+       u32 i, data;
+       u32 boot_address;
+
+       data = ast_mindwm(ast, 0x1e6e2100) & 0x01;
+       if (data) {
+               boot_address = get_fw_base(ast);
+               for (i = 0; i < size; i += 4)
+                       *(u32 *)(addr + i) = ast_mindwm(ast, boot_address + i);
+               return true;
+       }
+       return false;
+}
+
+bool ast_launch_m68k(struct drm_device *dev)
+{
+       struct ast_private *ast = dev->dev_private;
+       u32 i, data, len = 0;
+       u32 boot_address;
+       u8 *fw_addr = NULL;
+       u8 jreg;
+
+       data = ast_mindwm(ast, 0x1e6e2100) & 0x01;
+       if (!data) {
+
+               if (ast->dp501_fw_addr) {
+                       fw_addr = ast->dp501_fw_addr;
+                       len = 32*1024;
+               } else if (ast->dp501_fw) {
+                       fw_addr = (u8 *)ast->dp501_fw->data;
+                       len = ast->dp501_fw->size;
+               }
+               /* Get BootAddress */
+               ast_moutdwm(ast, 0x1e6e2000, 0x1688a8a8);
+               data = ast_mindwm(ast, 0x1e6e0004);
+               switch (data & 0x03) {
+               case 0:
+                       boot_address = 0x44000000;
+                       break;
+               default:
+               case 1:
+                       boot_address = 0x48000000;
+                       break;
+               case 2:
+                       boot_address = 0x50000000;
+                       break;
+               case 3:
+                       boot_address = 0x60000000;
+                       break;
+               }
+               boot_address -= 0x200000; /* -2MB */
+
+               /* copy image to buffer */
+               for (i = 0; i < len; i += 4) {
+                       data = *(u32 *)(fw_addr + i);
+                       ast_moutdwm(ast, boot_address + i, data);
+               }
+
+               /* Init SCU */
+               ast_moutdwm(ast, 0x1e6e2000, 0x1688a8a8);
+
+               /* Launch FW */
+               ast_moutdwm(ast, 0x1e6e2104, 0x80000000 + boot_address);
+               ast_moutdwm(ast, 0x1e6e2100, 1);
+
+               /* Update Scratch */
+               data = ast_mindwm(ast, 0x1e6e2040) & 0xfffff1ff;                /* D[11:9] = 100b: UEFI handling */
+               data |= 0x800;
+               ast_moutdwm(ast, 0x1e6e2040, data);
+
+               jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x99, 0xfc); /* D[1:0]: Reserved Video Buffer */
+               jreg |= 0x02;
+               ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x99, jreg);
+       }
+       return true;
+}
+
+u8 ast_get_dp501_max_clk(struct drm_device *dev)
+{
+       struct ast_private *ast = dev->dev_private;
+       u32 boot_address, offset, data;
+       u8 linkcap[4], linkrate, linklanes, maxclk = 0xff;
+
+       boot_address = get_fw_base(ast);
+
+       /* validate FW version */
+       offset = 0xf000;
+       data = ast_mindwm(ast, boot_address + offset);
+       if ((data & 0xf0) != 0x10) /* version: 1x */
+               return maxclk;
+
+       /* Read Link Capability */
+       offset  = 0xf014;
+       *(u32 *)linkcap = ast_mindwm(ast, boot_address + offset);
+       if (linkcap[2] == 0) {
+               linkrate = linkcap[0];
+               linklanes = linkcap[1];
+               data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes);
+               if (data > 0xff)
+                       data = 0xff;
+               maxclk = (u8)data;
+       }
+       return maxclk;
+}
+
+bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata)
+{
+       struct ast_private *ast = dev->dev_private;
+       u32 i, boot_address, offset, data;
+
+       boot_address = get_fw_base(ast);
+
+       /* validate FW version */
+       offset = 0xf000;
+       data = ast_mindwm(ast, boot_address + offset);
+       if ((data & 0xf0) != 0x10)
+               return false;
+
+       /* validate PnP Monitor */
+       offset = 0xf010;
+       data = ast_mindwm(ast, boot_address + offset);
+       if (!(data & 0x01))
+               return false;
+
+       /* Read EDID */
+       offset = 0xf020;
+       for (i = 0; i < 128; i += 4) {
+               data = ast_mindwm(ast, boot_address + offset + i);
+               *(u32 *)(ediddata + i) = data;
+       }
+
+       return true;
+}
+
+static bool ast_init_dvo(struct drm_device *dev)
+{
+       struct ast_private *ast = dev->dev_private;
+       u8 jreg;
+       u32 data;
+       ast_write32(ast, 0xf004, 0x1e6e0000);
+       ast_write32(ast, 0xf000, 0x1);
+       ast_write32(ast, 0x12000, 0x1688a8a8);
+
+       jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff);
+       if (!(jreg & 0x80)) {
+               /* Init SCU DVO Settings */
+               data = ast_read32(ast, 0x12008);
+               /* delay phase */
+               data &= 0xfffff8ff;
+               data |= 0x00000500;
+               ast_write32(ast, 0x12008, data);
+
+               if (ast->chip == AST2300) {
+                       data = ast_read32(ast, 0x12084);
+                       /* multi-pins for DVO single-edge */
+                       data |= 0xfffe0000;
+                       ast_write32(ast, 0x12084, data);
+
+                       data = ast_read32(ast, 0x12088);
+                       /* multi-pins for DVO single-edge */
+                       data |= 0x000fffff;
+                       ast_write32(ast, 0x12088, data);
+
+                       data = ast_read32(ast, 0x12090);
+                       /* multi-pins for DVO single-edge */
+                       data &= 0xffffffcf;
+                       data |= 0x00000020;
+                       ast_write32(ast, 0x12090, data);
+               } else { /* AST2400 */
+                       data = ast_read32(ast, 0x12088);
+                       /* multi-pins for DVO single-edge */
+                       data |= 0x30000000;
+                       ast_write32(ast, 0x12088, data);
+
+                       data = ast_read32(ast, 0x1208c);
+                       /* multi-pins for DVO single-edge */
+                       data |= 0x000000cf;
+                       ast_write32(ast, 0x1208c, data);
+
+                       data = ast_read32(ast, 0x120a4);
+                       /* multi-pins for DVO single-edge */
+                       data |= 0xffff0000;
+                       ast_write32(ast, 0x120a4, data);
+
+                       data = ast_read32(ast, 0x120a8);
+                       /* multi-pins for DVO single-edge */
+                       data |= 0x0000000f;
+                       ast_write32(ast, 0x120a8, data);
+
+                       data = ast_read32(ast, 0x12094);
+                       /* multi-pins for DVO single-edge */
+                       data |= 0x00000002;
+                       ast_write32(ast, 0x12094, data);
+               }
+       }
+
+       /* Force to DVO */
+       data = ast_read32(ast, 0x1202c);
+       data &= 0xfffbffff;
+       ast_write32(ast, 0x1202c, data);
+
+       /* Init VGA DVO Settings */
+       ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xcf, 0x80);
+       return true;
+}
+
+void ast_init_3rdtx(struct drm_device *dev)
+{
+       struct ast_private *ast = dev->dev_private;
+       u8 jreg;
+       u32 data;
+       if (ast->chip == AST2300 || ast->chip == AST2400) {
+               jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff);
+               switch (jreg & 0x0e) {
+               case 0x04:
+                       ast_init_dvo(dev);
+                       break;
+               case 0x08:
+                       ast_launch_m68k(dev);
+                       break;
+               case 0x0c:
+                       ast_init_dvo(dev);
+                       break;
+               default:
+                       if (ast->tx_chip_type == AST_TX_SIL164)
+                               ast_init_dvo(dev);
+                       else {
+                               ast_write32(ast, 0x12000, 0x1688a8a8);
+                               data = ast_read32(ast, 0x1202c);
+                               data &= 0xfffcffff;
+                               ast_write32(ast, 0, data);
+                       }
+               }
+       }
+}
index 5137f15dba19e71a220ac38efb7e1fee2ebbbd4a..44074fbcf7ff135a8c1bb3ac3d5eea9955845442 100644 (file)
@@ -94,9 +94,7 @@ static int ast_drm_thaw(struct drm_device *dev)
        ast_post_gpu(dev);
 
        drm_mode_config_reset(dev);
-       drm_modeset_lock_all(dev);
        drm_helper_resume_force_mode(dev);
-       drm_modeset_unlock_all(dev);
 
        console_lock();
        ast_fbdev_set_suspend(dev, 0);
@@ -198,7 +196,6 @@ static const struct file_operations ast_fops = {
 
 static struct drm_driver driver = {
        .driver_features = DRIVER_MODESET | DRIVER_GEM,
-       .dev_priv_size = 0,
 
        .load = ast_driver_load,
        .unload = ast_driver_unload,
index 9833a1b1acc140f36f0d248a026e43fee2b1ae33..5d6a87573c339d14eef728ae3edf690b096c6ddc 100644 (file)
@@ -61,9 +61,17 @@ enum ast_chip {
        AST2200,
        AST2150,
        AST2300,
+       AST2400,
        AST1180,
 };
 
+enum ast_tx_chip {
+       AST_TX_NONE,
+       AST_TX_SIL164,
+       AST_TX_ITE66121,
+       AST_TX_DP501,
+};
+
 #define AST_DRAM_512Mx16 0
 #define AST_DRAM_1Gx16   1
 #define AST_DRAM_512Mx32 2
@@ -102,6 +110,12 @@ struct ast_private {
         * we have. */
        struct ttm_bo_kmap_obj cache_kmap;
        int next_cursor;
+       bool support_wide_screen;
+
+       enum ast_tx_chip tx_chip_type;
+       u8 dp501_maxclk;
+       u8 *dp501_fw_addr;
+       const struct firmware *dp501_fw;        /* dp501 fw */
 };
 
 int ast_driver_load(struct drm_device *dev, unsigned long flags);
@@ -368,4 +382,14 @@ int ast_mmap(struct file *filp, struct vm_area_struct *vma);
 
 /* ast post */
 void ast_post_gpu(struct drm_device *dev);
+u32 ast_mindwm(struct ast_private *ast, u32 r);
+void ast_moutdwm(struct ast_private *ast, u32 r, u32 v);
+/* ast dp501 */
+int ast_load_dp501_microcode(struct drm_device *dev);
+void ast_set_dp501_video_output(struct drm_device *dev, u8 mode);
+bool ast_launch_m68k(struct drm_device *dev);
+bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size);
+bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata);
+u8 ast_get_dp501_max_clk(struct drm_device *dev);
+void ast_init_3rdtx(struct drm_device *dev);
 #endif
index 50535fd5a88d258492b90ca9e9e9cebfaf2de563..a2cc6be97983fe327cd4f67a4c3204cd4985691c 100644 (file)
@@ -66,12 +66,16 @@ uint8_t ast_get_index_reg_mask(struct ast_private *ast,
 static int ast_detect_chip(struct drm_device *dev)
 {
        struct ast_private *ast = dev->dev_private;
+       uint32_t data, jreg;
 
        if (dev->pdev->device == PCI_CHIP_AST1180) {
                ast->chip = AST1100;
                DRM_INFO("AST 1180 detected\n");
        } else {
-               if (dev->pdev->revision >= 0x20) {
+               if (dev->pdev->revision >= 0x30) {
+                       ast->chip = AST2400;
+                       DRM_INFO("AST 2400 detected\n");
+               } else if (dev->pdev->revision >= 0x20) {
                        ast->chip = AST2300;
                        DRM_INFO("AST 2300 detected\n");
                } else if (dev->pdev->revision >= 0x10) {
@@ -104,6 +108,59 @@ static int ast_detect_chip(struct drm_device *dev)
                        DRM_INFO("AST 2000 detected\n");
                }
        }
+
+       switch (ast->chip) {
+       case AST1180:
+               ast->support_wide_screen = true;
+               break;
+       case AST2000:
+               ast->support_wide_screen = false;
+               break;
+       default:
+               jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff);
+               if (!(jreg & 0x80))
+                       ast->support_wide_screen = true;
+               else if (jreg & 0x01)
+                       ast->support_wide_screen = true;
+               else {
+                       ast->support_wide_screen = false;
+                       ast_write32(ast, 0xf004, 0x1e6e0000);
+                       ast_write32(ast, 0xf000, 0x1);
+                       data = ast_read32(ast, 0x1207c);
+                       data &= 0x300;
+                       if (ast->chip == AST2300 && data == 0x0) /* ast1300 */
+                               ast->support_wide_screen = true;
+                       if (ast->chip == AST2400 && data == 0x100) /* ast1400 */
+                               ast->support_wide_screen = true;
+               }
+               break;
+       }
+
+       ast->tx_chip_type = AST_TX_NONE;
+       jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xff);
+       if (jreg & 0x80)
+               ast->tx_chip_type = AST_TX_SIL164;
+       if ((ast->chip == AST2300) || (ast->chip == AST2400)) {
+               jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff);
+               switch (jreg) {
+               case 0x04:
+                       ast->tx_chip_type = AST_TX_SIL164;
+                       break;
+               case 0x08:
+                       ast->dp501_fw_addr = kzalloc(32*1024, GFP_KERNEL);
+                       if (ast->dp501_fw_addr) {
+                               /* backup firmware */
+                               if (ast_backup_fw(dev, ast->dp501_fw_addr, 32*1024)) {
+                                       kfree(ast->dp501_fw_addr);
+                                       ast->dp501_fw_addr = NULL;
+                               }
+                       }
+                       /* fallthrough */
+               case 0x0c:
+                       ast->tx_chip_type = AST_TX_DP501;
+               }
+       }
+
        return 0;
 }
 
@@ -129,7 +186,7 @@ static int ast_get_dram_info(struct drm_device *dev)
        else
                ast->dram_bus_width = 32;
 
-       if (ast->chip == AST2300) {
+       if (ast->chip == AST2300 || ast->chip == AST2400) {
                switch (data & 0x03) {
                case 0:
                        ast->dram_type = AST_DRAM_512Mx16;
@@ -257,17 +314,32 @@ static u32 ast_get_vram_info(struct drm_device *dev)
 {
        struct ast_private *ast = dev->dev_private;
        u8 jreg;
-
+       u32 vram_size;
        ast_open_key(ast);
 
+       vram_size = AST_VIDMEM_DEFAULT_SIZE;
        jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xaa, 0xff);
        switch (jreg & 3) {
-       case 0: return AST_VIDMEM_SIZE_8M;
-       case 1: return AST_VIDMEM_SIZE_16M;
-       case 2: return AST_VIDMEM_SIZE_32M;
-       case 3: return AST_VIDMEM_SIZE_64M;
+       case 0: vram_size = AST_VIDMEM_SIZE_8M; break;
+       case 1: vram_size = AST_VIDMEM_SIZE_16M; break;
+       case 2: vram_size = AST_VIDMEM_SIZE_32M; break;
+       case 3: vram_size = AST_VIDMEM_SIZE_64M; break;
+       }
+
+       jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x99, 0xff);
+       switch (jreg & 0x03) {
+       case 1:
+               vram_size -= 0x100000;
+               break;
+       case 2:
+               vram_size -= 0x200000;
+               break;
+       case 3:
+               vram_size -= 0x400000;
+               break;
        }
-       return AST_VIDMEM_DEFAULT_SIZE;
+
+       return vram_size;
 }
 
 int ast_driver_load(struct drm_device *dev, unsigned long flags)
@@ -316,6 +388,7 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags)
        if (ast->chip == AST2100 ||
            ast->chip == AST2200 ||
            ast->chip == AST2300 ||
+           ast->chip == AST2400 ||
            ast->chip == AST1180) {
                dev->mode_config.max_width = 1920;
                dev->mode_config.max_height = 2048;
@@ -343,6 +416,7 @@ int ast_driver_unload(struct drm_device *dev)
 {
        struct ast_private *ast = dev->dev_private;
 
+       kfree(ast->dp501_fw_addr);
        ast_mode_fini(dev);
        ast_fbdev_fini(dev);
        drm_mode_config_cleanup(dev);
@@ -411,16 +485,13 @@ static void ast_bo_unref(struct ast_bo **bo)
 
        tbo = &((*bo)->bo);
        ttm_bo_unref(&tbo);
-       if (tbo == NULL)
-               *bo = NULL;
-
+       *bo = NULL;
 }
+
 void ast_gem_free_object(struct drm_gem_object *obj)
 {
        struct ast_bo *ast_bo = gem_to_ast_bo(obj);
 
-       if (!ast_bo)
-               return;
        ast_bo_unref(&ast_bo);
 }
 
index a4afdc8bb578b826979b16431efef7183dee8f1a..114aee941d46b417f87a7aad1c20502374873ef8 100644 (file)
@@ -115,11 +115,17 @@ static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mo
                else
                        vbios_mode->enh_table = &res_1280x1024[refresh_rate_index];
                break;
+       case 1360:
+               vbios_mode->enh_table = &res_1360x768[refresh_rate_index];
+               break;
        case 1440:
                vbios_mode->enh_table = &res_1440x900[refresh_rate_index];
                break;
        case 1600:
-               vbios_mode->enh_table = &res_1600x1200[refresh_rate_index];
+               if (crtc->mode.crtc_vdisplay == 900)
+                       vbios_mode->enh_table = &res_1600x900[refresh_rate_index];
+               else
+                       vbios_mode->enh_table = &res_1600x1200[refresh_rate_index];
                break;
        case 1680:
                vbios_mode->enh_table = &res_1680x1050[refresh_rate_index];
@@ -175,14 +181,17 @@ static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mo
                ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x8d, refresh_rate_index & 0xff);
                ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x8e, mode_id & 0xff);
 
-               ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x91, 0xa8);
-               ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x92, crtc->primary->fb->bits_per_pixel);
-               ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x93, adjusted_mode->clock / 1000);
-               ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x94, adjusted_mode->crtc_hdisplay);
-               ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x95, adjusted_mode->crtc_hdisplay >> 8);
+               ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x91, 0x00);
+               if (vbios_mode->enh_table->flags & NewModeInfo) {
+                       ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x91, 0xa8);
+                       ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x92, crtc->primary->fb->bits_per_pixel);
+                       ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x93, adjusted_mode->clock / 1000);
+                       ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x94, adjusted_mode->crtc_hdisplay);
+                       ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x95, adjusted_mode->crtc_hdisplay >> 8);
 
-               ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x96, adjusted_mode->crtc_vdisplay);
-               ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x97, adjusted_mode->crtc_vdisplay >> 8);
+                       ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x96, adjusted_mode->crtc_vdisplay);
+                       ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x97, adjusted_mode->crtc_vdisplay >> 8);
+               }
        }
 
        return true;
@@ -389,7 +398,7 @@ static void ast_set_ext_reg(struct drm_crtc *crtc, struct drm_display_mode *mode
        ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa8, 0xfd, jregA8);
 
        /* Set Threshold */
-       if (ast->chip == AST2300) {
+       if (ast->chip == AST2300 || ast->chip == AST2400) {
                ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa7, 0x78);
                ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa6, 0x60);
        } else if (ast->chip == AST2100 ||
@@ -451,9 +460,13 @@ static void ast_crtc_dpms(struct drm_crtc *crtc, int mode)
        case DRM_MODE_DPMS_STANDBY:
        case DRM_MODE_DPMS_SUSPEND:
                ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x1, 0xdf, 0);
+               if (ast->tx_chip_type == AST_TX_DP501)
+                       ast_set_dp501_video_output(crtc->dev, 1);
                ast_crtc_load_lut(crtc);
                break;
        case DRM_MODE_DPMS_OFF:
+               if (ast->tx_chip_type == AST_TX_DP501)
+                       ast_set_dp501_video_output(crtc->dev, 0);
                ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x1, 0xdf, 0x20);
                break;
        }
@@ -729,10 +742,24 @@ static int ast_encoder_init(struct drm_device *dev)
 static int ast_get_modes(struct drm_connector *connector)
 {
        struct ast_connector *ast_connector = to_ast_connector(connector);
+       struct ast_private *ast = connector->dev->dev_private;
        struct edid *edid;
        int ret;
-
-       edid = drm_get_edid(connector, &ast_connector->i2c->adapter);
+       bool flags = false;
+       if (ast->tx_chip_type == AST_TX_DP501) {
+               ast->dp501_maxclk = 0xff;
+               edid = kmalloc(128, GFP_KERNEL);
+               if (!edid)
+                       return -ENOMEM;
+
+               flags = ast_dp501_read_edid(connector->dev, (u8 *)edid);
+               if (flags)
+                       ast->dp501_maxclk = ast_get_dp501_max_clk(connector->dev);
+               else
+                       kfree(edid);
+       }
+       if (!flags)
+               edid = drm_get_edid(connector, &ast_connector->i2c->adapter);
        if (edid) {
                drm_mode_connector_update_edid_property(&ast_connector->base, edid);
                ret = drm_add_edid_modes(connector, edid);
@@ -746,7 +773,56 @@ static int ast_get_modes(struct drm_connector *connector)
 static int ast_mode_valid(struct drm_connector *connector,
                          struct drm_display_mode *mode)
 {
-       return MODE_OK;
+       struct ast_private *ast = connector->dev->dev_private;
+       int flags = MODE_NOMODE;
+       uint32_t jtemp;
+
+       if (ast->support_wide_screen) {
+               if ((mode->hdisplay == 1680) && (mode->vdisplay == 1050))
+                       return MODE_OK;
+               if ((mode->hdisplay == 1280) && (mode->vdisplay == 800))
+                       return MODE_OK;
+               if ((mode->hdisplay == 1440) && (mode->vdisplay == 900))
+                       return MODE_OK;
+               if ((mode->hdisplay == 1360) && (mode->vdisplay == 768))
+                       return MODE_OK;
+               if ((mode->hdisplay == 1600) && (mode->vdisplay == 900))
+                       return MODE_OK;
+
+               if ((ast->chip == AST2100) || (ast->chip == AST2200) || (ast->chip == AST2300) || (ast->chip == AST2400) || (ast->chip == AST1180)) {
+                       if ((mode->hdisplay == 1920) && (mode->vdisplay == 1080))
+                               return MODE_OK;
+
+                       if ((mode->hdisplay == 1920) && (mode->vdisplay == 1200)) {
+                               jtemp = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff);
+                               if (jtemp & 0x01)
+                                       return MODE_NOMODE;
+                               else
+                                       return MODE_OK;
+                       }
+               }
+       }
+       switch (mode->hdisplay) {
+       case 640:
+               if (mode->vdisplay == 480) flags = MODE_OK;
+               break;
+       case 800:
+               if (mode->vdisplay == 600) flags = MODE_OK;
+               break;
+       case 1024:
+               if (mode->vdisplay == 768) flags = MODE_OK;
+               break;
+       case 1280:
+               if (mode->vdisplay == 1024) flags = MODE_OK;
+               break;
+       case 1600:
+               if (mode->vdisplay == 1200) flags = MODE_OK;
+               break;
+       default:
+               return flags;
+       }
+
+       return flags;
 }
 
 static void ast_connector_destroy(struct drm_connector *connector)
index 635f6ffc27c2852593e20a56b4cb5c451c417a38..38d437f3a267493fd097da14aa0a05d6cda68a99 100644 (file)
@@ -78,7 +78,7 @@ ast_set_def_ext_reg(struct drm_device *dev)
        for (i = 0x81; i <= 0x8f; i++)
                ast_set_index_reg(ast, AST_IO_CRTC_PORT, i, 0x00);
 
-       if (ast->chip == AST2300) {
+       if (ast->chip == AST2300 || ast->chip == AST2400) {
                if (dev->pdev->revision >= 0x20)
                        ext_reg_info = extreginfo_ast2300;
                else
@@ -102,23 +102,32 @@ ast_set_def_ext_reg(struct drm_device *dev)
 
        /* Enable RAMDAC for A1 */
        reg = 0x04;
-       if (ast->chip == AST2300)
+       if (ast->chip == AST2300 || ast->chip == AST2400)
                reg |= 0x20;
        ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff, reg);
 }
 
-static inline u32 mindwm(struct ast_private *ast, u32 r)
+u32 ast_mindwm(struct ast_private *ast, u32 r)
 {
+       uint32_t data;
+
        ast_write32(ast, 0xf004, r & 0xffff0000);
        ast_write32(ast, 0xf000, 0x1);
 
+       do {
+               data = ast_read32(ast, 0xf004) & 0xffff0000;
+       } while (data != (r & 0xffff0000));
        return ast_read32(ast, 0x10000 + (r & 0x0000ffff));
 }
 
-static inline void moutdwm(struct ast_private *ast, u32 r, u32 v)
+void ast_moutdwm(struct ast_private *ast, u32 r, u32 v)
 {
+       uint32_t data;
        ast_write32(ast, 0xf004, r & 0xffff0000);
        ast_write32(ast, 0xf000, 0x1);
+       do {
+               data = ast_read32(ast, 0xf004) & 0xffff0000;
+       } while (data != (r & 0xffff0000));
        ast_write32(ast, 0x10000 + (r & 0x0000ffff), v);
 }
 
@@ -154,28 +163,28 @@ static u32 mmctestburst2_ast2150(struct ast_private *ast, u32 datagen)
 {
        u32 data, timeout;
 
-       moutdwm(ast, 0x1e6e0070, 0x00000000);
-       moutdwm(ast, 0x1e6e0070, 0x00000001 | (datagen << 3));
+       ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
+       ast_moutdwm(ast, 0x1e6e0070, 0x00000001 | (datagen << 3));
        timeout = 0;
        do {
-               data = mindwm(ast, 0x1e6e0070) & 0x40;
+               data = ast_mindwm(ast, 0x1e6e0070) & 0x40;
                if (++timeout > TIMEOUT_AST2150) {
-                       moutdwm(ast, 0x1e6e0070, 0x00000000);
+                       ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
                        return 0xffffffff;
                }
        } while (!data);
-       moutdwm(ast, 0x1e6e0070, 0x00000000);
-       moutdwm(ast, 0x1e6e0070, 0x00000003 | (datagen << 3));
+       ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
+       ast_moutdwm(ast, 0x1e6e0070, 0x00000003 | (datagen << 3));
        timeout = 0;
        do {
-               data = mindwm(ast, 0x1e6e0070) & 0x40;
+               data = ast_mindwm(ast, 0x1e6e0070) & 0x40;
                if (++timeout > TIMEOUT_AST2150) {
-                       moutdwm(ast, 0x1e6e0070, 0x00000000);
+                       ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
                        return 0xffffffff;
                }
        } while (!data);
-       data = (mindwm(ast, 0x1e6e0070) & 0x80) >> 7;
-       moutdwm(ast, 0x1e6e0070, 0x00000000);
+       data = (ast_mindwm(ast, 0x1e6e0070) & 0x80) >> 7;
+       ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
        return data;
 }
 
@@ -184,18 +193,18 @@ static u32 mmctestsingle2_ast2150(struct ast_private *ast, u32 datagen)
 {
        u32 data, timeout;
 
-       moutdwm(ast, 0x1e6e0070, 0x00000000);
-       moutdwm(ast, 0x1e6e0070, 0x00000005 | (datagen << 3));
+       ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
+       ast_moutdwm(ast, 0x1e6e0070, 0x00000005 | (datagen << 3));
        timeout = 0;
        do {
-               data = mindwm(ast, 0x1e6e0070) & 0x40;
+               data = ast_mindwm(ast, 0x1e6e0070) & 0x40;
                if (++timeout > TIMEOUT_AST2150) {
-                       moutdwm(ast, 0x1e6e0070, 0x00000000);
+                       ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
                        return 0xffffffff;
                }
        } while (!data);
-       data = (mindwm(ast, 0x1e6e0070) & 0x80) >> 7;
-       moutdwm(ast, 0x1e6e0070, 0x00000000);
+       data = (ast_mindwm(ast, 0x1e6e0070) & 0x80) >> 7;
+       ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
        return data;
 }
 #endif
@@ -215,7 +224,7 @@ static int cbrscan_ast2150(struct ast_private *ast, int busw)
        u32 patcnt, loop;
 
        for (patcnt = 0; patcnt < CBR_PATNUM_AST2150; patcnt++) {
-               moutdwm(ast, 0x1e6e007c, pattern_AST2150[patcnt]);
+               ast_moutdwm(ast, 0x1e6e007c, pattern_AST2150[patcnt]);
                for (loop = 0; loop < CBR_PASSNUM_AST2150; loop++) {
                        if (cbrtest_ast2150(ast))
                                break;
@@ -237,7 +246,7 @@ cbr_start:
        passcnt = 0;
 
        for (dlli = 0; dlli < 100; dlli++) {
-               moutdwm(ast, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24));
+               ast_moutdwm(ast, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24));
                data = cbrscan_ast2150(ast, busw);
                if (data != 0) {
                        if (data & 0x1) {
@@ -254,7 +263,7 @@ cbr_start:
                goto cbr_start;
 
        dlli = dll_min[0] + (((dll_max[0] - dll_min[0]) * 7) >> 4);
-       moutdwm(ast, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24));
+       ast_moutdwm(ast, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24));
 }
 
 
@@ -365,10 +374,12 @@ void ast_post_gpu(struct drm_device *dev)
        ast_open_key(ast);
        ast_set_def_ext_reg(dev);
 
-       if (ast->chip == AST2300)
+       if (ast->chip == AST2300 || ast->chip == AST2400)
                ast_init_dram_2300(dev);
        else
                ast_init_dram_reg(dev);
+
+       ast_init_3rdtx(dev);
 }
 
 /* AST 2300 DRAM settings */
@@ -403,6 +414,7 @@ struct ast2300_dram_param {
 /*
  * DQSI DLL CBR Setting
  */
+#define CBR_SIZE0            ((1  << 10) - 1)
 #define CBR_SIZE1            ((4  << 10) - 1)
 #define CBR_SIZE2            ((64 << 10) - 1)
 #define CBR_PASSNUM          5
@@ -423,88 +435,84 @@ static const u32 pattern[8] = {
        0x7C61D253
 };
 
-#if 0 /* unused in DDX, included for completeness */
 static int mmc_test_burst(struct ast_private *ast, u32 datagen)
 {
        u32 data, timeout;
 
-       moutdwm(ast, 0x1e6e0070, 0x00000000);
-       moutdwm(ast, 0x1e6e0070, 0x000000c1 | (datagen << 3));
+       ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
+       ast_moutdwm(ast, 0x1e6e0070, 0x000000c1 | (datagen << 3));
        timeout = 0;
        do {
-               data = mindwm(ast, 0x1e6e0070) & 0x3000;
+               data = ast_mindwm(ast, 0x1e6e0070) & 0x3000;
                if (data & 0x2000) {
                        return 0;
                }
                if (++timeout > TIMEOUT) {
-                       moutdwm(ast, 0x1e6e0070, 0x00000000);
+                       ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
                        return 0;
                }
        } while (!data);
-       moutdwm(ast, 0x1e6e0070, 0x00000000);
+       ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
        return 1;
 }
-#endif
 
 static int mmc_test_burst2(struct ast_private *ast, u32 datagen)
 {
        u32 data, timeout;
 
-       moutdwm(ast, 0x1e6e0070, 0x00000000);
-       moutdwm(ast, 0x1e6e0070, 0x00000041 | (datagen << 3));
+       ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
+       ast_moutdwm(ast, 0x1e6e0070, 0x00000041 | (datagen << 3));
        timeout = 0;
        do {
-               data = mindwm(ast, 0x1e6e0070) & 0x1000;
+               data = ast_mindwm(ast, 0x1e6e0070) & 0x1000;
                if (++timeout > TIMEOUT) {
-                       moutdwm(ast, 0x1e6e0070, 0x0);
+                       ast_moutdwm(ast, 0x1e6e0070, 0x0);
                        return -1;
                }
        } while (!data);
-       data = mindwm(ast, 0x1e6e0078);
+       data = ast_mindwm(ast, 0x1e6e0078);
        data = (data | (data >> 16)) & 0xffff;
-       moutdwm(ast, 0x1e6e0070, 0x0);
+       ast_moutdwm(ast, 0x1e6e0070, 0x0);
        return data;
 }
 
-#if 0 /* Unused in DDX here for completeness */
 static int mmc_test_single(struct ast_private *ast, u32 datagen)
 {
        u32 data, timeout;
 
-       moutdwm(ast, 0x1e6e0070, 0x00000000);
-       moutdwm(ast, 0x1e6e0070, 0x000000c5 | (datagen << 3));
+       ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
+       ast_moutdwm(ast, 0x1e6e0070, 0x000000c5 | (datagen << 3));
        timeout = 0;
        do {
-               data = mindwm(ast, 0x1e6e0070) & 0x3000;
+               data = ast_mindwm(ast, 0x1e6e0070) & 0x3000;
                if (data & 0x2000)
                        return 0;
                if (++timeout > TIMEOUT) {
-                       moutdwm(ast, 0x1e6e0070, 0x0);
+                       ast_moutdwm(ast, 0x1e6e0070, 0x0);
                        return 0;
                }
        } while (!data);
-       moutdwm(ast, 0x1e6e0070, 0x0);
+       ast_moutdwm(ast, 0x1e6e0070, 0x0);
        return 1;
 }
-#endif
 
 static int mmc_test_single2(struct ast_private *ast, u32 datagen)
 {
        u32 data, timeout;
 
-       moutdwm(ast, 0x1e6e0070, 0x00000000);
-       moutdwm(ast, 0x1e6e0070, 0x00000005 | (datagen << 3));
+       ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
+       ast_moutdwm(ast, 0x1e6e0070, 0x00000005 | (datagen << 3));
        timeout = 0;
        do {
-               data = mindwm(ast, 0x1e6e0070) & 0x1000;
+               data = ast_mindwm(ast, 0x1e6e0070) & 0x1000;
                if (++timeout > TIMEOUT) {
-                       moutdwm(ast, 0x1e6e0070, 0x0);
+                       ast_moutdwm(ast, 0x1e6e0070, 0x0);
                        return -1;
                }
        } while (!data);
-       data = mindwm(ast, 0x1e6e0078);
+       data = ast_mindwm(ast, 0x1e6e0078);
        data = (data | (data >> 16)) & 0xffff;
-       moutdwm(ast, 0x1e6e0070, 0x0);
+       ast_moutdwm(ast, 0x1e6e0070, 0x0);
        return data;
 }
 
@@ -533,7 +541,7 @@ static int cbr_scan(struct ast_private *ast)
 
        data2 = 3;
        for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) {
-               moutdwm(ast, 0x1e6e007c, pattern[patcnt]);
+               ast_moutdwm(ast, 0x1e6e007c, pattern[patcnt]);
                for (loop = 0; loop < CBR_PASSNUM2; loop++) {
                        if ((data = cbr_test(ast)) != 0) {
                                data2 &= data;
@@ -568,7 +576,7 @@ static u32 cbr_scan2(struct ast_private *ast)
 
        data2 = 0xffff;
        for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) {
-               moutdwm(ast, 0x1e6e007c, pattern[patcnt]);
+               ast_moutdwm(ast, 0x1e6e007c, pattern[patcnt]);
                for (loop = 0; loop < CBR_PASSNUM2; loop++) {
                        if ((data = cbr_test2(ast)) != 0) {
                                data2 &= data;
@@ -583,106 +591,35 @@ static u32 cbr_scan2(struct ast_private *ast)
        return data2;
 }
 
-#if 0 /* unused in DDX - added for completeness */
-static void finetuneDQI(struct ast_private *ast, struct ast2300_dram_param *param)
+static u32 cbr_test3(struct ast_private *ast)
 {
-       u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt;
-
-       gold_sadj[0] = (mindwm(ast, 0x1E6E0024) >> 16) & 0xffff;
-       gold_sadj[1] = gold_sadj[0] >> 8;
-       gold_sadj[0] = gold_sadj[0] & 0xff;
-       gold_sadj[0] = (gold_sadj[0] + gold_sadj[1]) >> 1;
-       gold_sadj[1] = gold_sadj[0];
-
-       for (cnt = 0; cnt < 16; cnt++) {
-               dllmin[cnt] = 0xff;
-               dllmax[cnt] = 0x0;
-       }
-       passcnt = 0;
-       for (dlli = 0; dlli < 76; dlli++) {
-               moutdwm(ast, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24));
-               /* Wait DQSI latch phase calibration */
-               moutdwm(ast, 0x1E6E0074, 0x00000010);
-               moutdwm(ast, 0x1E6E0070, 0x00000003);
-               do {
-                       data = mindwm(ast, 0x1E6E0070);
-               } while (!(data & 0x00001000));
-               moutdwm(ast, 0x1E6E0070, 0x00000000);
+       if (!mmc_test_burst(ast, 0))
+               return 0;
+       if (!mmc_test_single(ast, 0))
+               return 0;
+       return 1;
+}
 
-               moutdwm(ast, 0x1E6E0074, CBR_SIZE1);
-               data = cbr_scan2(ast);
-               if (data != 0) {
-                       mask = 0x00010001;
-                       for (cnt = 0; cnt < 16; cnt++) {
-                               if (data & mask) {
-                                       if (dllmin[cnt] > dlli) {
-                                               dllmin[cnt] = dlli;
-                                       }
-                                       if (dllmax[cnt] < dlli) {
-                                               dllmax[cnt] = dlli;
-                                       }
-                               }
-                               mask <<= 1;
-                       }
-                       passcnt++;
-               } else if (passcnt >= CBR_THRESHOLD) {
-                       break;
-               }
-       }
-       data = 0;
-       for (cnt = 0; cnt < 8; cnt++) {
-               data >>= 3;
-               if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD)) {
-                       dlli = (dllmin[cnt] + dllmax[cnt]) >> 1;
-                       if (gold_sadj[0] >= dlli) {
-                               dlli = (gold_sadj[0] - dlli) >> 1;
-                               if (dlli > 3) {
-                                       dlli = 3;
-                               }
-                       } else {
-                               dlli = (dlli - gold_sadj[0]) >> 1;
-                               if (dlli > 4) {
-                                       dlli = 4;
-                               }
-                               dlli = (8 - dlli) & 0x7;
-                       }
-                       data |= dlli << 21;
-               }
-       }
-       moutdwm(ast, 0x1E6E0080, data);
+static u32 cbr_scan3(struct ast_private *ast)
+{
+       u32 patcnt, loop;
 
-       data = 0;
-       for (cnt = 8; cnt < 16; cnt++) {
-               data >>= 3;
-               if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD)) {
-                       dlli = (dllmin[cnt] + dllmax[cnt]) >> 1;
-                       if (gold_sadj[1] >= dlli) {
-                               dlli = (gold_sadj[1] - dlli) >> 1;
-                               if (dlli > 3) {
-                                       dlli = 3;
-                               } else {
-                                       dlli = (dlli - 1) & 0x7;
-                               }
-                       } else {
-                               dlli = (dlli - gold_sadj[1]) >> 1;
-                               dlli += 1;
-                               if (dlli > 4) {
-                                       dlli = 4;
-                               }
-                               dlli = (8 - dlli) & 0x7;
-                       }
-                       data |= dlli << 21;
+       for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) {
+               ast_moutdwm(ast, 0x1e6e007c, pattern[patcnt]);
+               for (loop = 0; loop < 2; loop++) {
+                       if (cbr_test3(ast))
+                               break;
                }
+               if (loop == 2)
+                       return 0;
        }
-       moutdwm(ast, 0x1E6E0084, data);
-
-} /* finetuneDQI */
-#endif
+       return 1;
+}
 
-static void finetuneDQI_L(struct ast_private *ast, struct ast2300_dram_param *param)
+static bool finetuneDQI_L(struct ast_private *ast, struct ast2300_dram_param *param)
 {
-       u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt;
-
+       u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt, retry = 0;
+       bool status = false;
 FINETUNE_START:
        for (cnt = 0; cnt < 16; cnt++) {
                dllmin[cnt] = 0xff;
@@ -690,16 +627,8 @@ FINETUNE_START:
        }
        passcnt = 0;
        for (dlli = 0; dlli < 76; dlli++) {
-               moutdwm(ast, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24));
-               /* Wait DQSI latch phase calibration */
-               moutdwm(ast, 0x1E6E0074, 0x00000010);
-               moutdwm(ast, 0x1E6E0070, 0x00000003);
-               do {
-                       data = mindwm(ast, 0x1E6E0070);
-               } while (!(data & 0x00001000));
-               moutdwm(ast, 0x1E6E0070, 0x00000000);
-
-               moutdwm(ast, 0x1E6E0074, CBR_SIZE1);
+               ast_moutdwm(ast, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24));
+               ast_moutdwm(ast, 0x1E6E0074, CBR_SIZE1);
                data = cbr_scan2(ast);
                if (data != 0) {
                        mask = 0x00010001;
@@ -727,9 +656,13 @@ FINETUNE_START:
                        passcnt++;
                }
        }
+       if (retry++ > 10)
+               goto FINETUNE_DONE;
        if (passcnt != 16) {
                goto FINETUNE_START;
        }
+       status = true;
+FINETUNE_DONE:
        gold_sadj[0] = gold_sadj[0] >> 4;
        gold_sadj[1] = gold_sadj[0];
 
@@ -753,7 +686,7 @@ FINETUNE_START:
                        data |= dlli << 21;
                }
        }
-       moutdwm(ast, 0x1E6E0080, data);
+       ast_moutdwm(ast, 0x1E6E0080, data);
 
        data = 0;
        for (cnt = 8; cnt < 16; cnt++) {
@@ -778,162 +711,116 @@ FINETUNE_START:
                        data |= dlli << 21;
                }
        }
-       moutdwm(ast, 0x1E6E0084, data);
-
+       ast_moutdwm(ast, 0x1E6E0084, data);
+       return status;
 } /* finetuneDQI_L */
 
-static void finetuneDQI_L2(struct ast_private *ast, struct ast2300_dram_param *param)
+static void finetuneDQSI(struct ast_private *ast)
 {
-       u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt, data2;
+       u32 dlli, dqsip, dqidly;
+       u32 reg_mcr18, reg_mcr0c, passcnt[2], diff;
+       u32 g_dqidly, g_dqsip, g_margin, g_side;
+       u16 pass[32][2][2];
+       char tag[2][76];
+
+       /* Disable DQI CBR */
+       reg_mcr0c  = ast_mindwm(ast, 0x1E6E000C);
+       reg_mcr18  = ast_mindwm(ast, 0x1E6E0018);
+       reg_mcr18 &= 0x0000ffff;
+       ast_moutdwm(ast, 0x1E6E0018, reg_mcr18);
 
-       for (cnt = 0; cnt < 16; cnt++) {
-               dllmin[cnt] = 0xff;
-               dllmax[cnt] = 0x0;
-       }
-       passcnt = 0;
        for (dlli = 0; dlli < 76; dlli++) {
-               moutdwm(ast, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24));
-               /* Wait DQSI latch phase calibration */
-               moutdwm(ast, 0x1E6E0074, 0x00000010);
-               moutdwm(ast, 0x1E6E0070, 0x00000003);
-               do {
-                       data = mindwm(ast, 0x1E6E0070);
-               } while (!(data & 0x00001000));
-               moutdwm(ast, 0x1E6E0070, 0x00000000);
-
-               moutdwm(ast, 0x1E6E0074, CBR_SIZE2);
-               data = cbr_scan2(ast);
-               if (data != 0) {
-                       mask = 0x00010001;
-                       for (cnt = 0; cnt < 16; cnt++) {
-                               if (data & mask) {
-                                       if (dllmin[cnt] > dlli) {
-                                               dllmin[cnt] = dlli;
-                                       }
-                                       if (dllmax[cnt] < dlli) {
-                                               dllmax[cnt] = dlli;
-                                       }
-                               }
-                               mask <<= 1;
-                       }
-                       passcnt++;
-               } else if (passcnt >= CBR_THRESHOLD2) {
-                       break;
-               }
+               tag[0][dlli] = 0x0;
+               tag[1][dlli] = 0x0;
        }
-       gold_sadj[0] = 0x0;
-       gold_sadj[1] = 0xFF;
-       for (cnt = 0; cnt < 8; cnt++) {
-               if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
-                       if (gold_sadj[0] < dllmin[cnt]) {
-                               gold_sadj[0] = dllmin[cnt];
-                       }
-                       if (gold_sadj[1] > dllmax[cnt]) {
-                               gold_sadj[1] = dllmax[cnt];
-                       }
-               }
+       for (dqidly = 0; dqidly < 32; dqidly++) {
+               pass[dqidly][0][0] = 0xff;
+               pass[dqidly][0][1] = 0x0;
+               pass[dqidly][1][0] = 0xff;
+               pass[dqidly][1][1] = 0x0;
        }
-       gold_sadj[0] = (gold_sadj[1] + gold_sadj[0]) >> 1;
-       gold_sadj[1] = mindwm(ast, 0x1E6E0080);
-
-       data = 0;
-       for (cnt = 0; cnt < 8; cnt++) {
-               data >>= 3;
-               data2 = gold_sadj[1] & 0x7;
-               gold_sadj[1] >>= 3;
-               if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
-                       dlli = (dllmin[cnt] + dllmax[cnt]) >> 1;
-                       if (gold_sadj[0] >= dlli) {
-                               dlli = (gold_sadj[0] - dlli) >> 1;
-                               if (dlli > 0) {
-                                       dlli = 1;
-                               }
-                               if (data2 != 3) {
-                                       data2 = (data2 + dlli) & 0x7;
-                               }
-                       } else {
-                               dlli = (dlli - gold_sadj[0]) >> 1;
-                               if (dlli > 0) {
-                                       dlli = 1;
-                               }
-                               if (data2 != 4) {
-                                       data2 = (data2 - dlli) & 0x7;
+       for (dqidly = 0; dqidly < 32; dqidly++) {
+               passcnt[0] = passcnt[1] = 0;
+               for (dqsip = 0; dqsip < 2; dqsip++) {
+                       ast_moutdwm(ast, 0x1E6E000C, 0);
+                       ast_moutdwm(ast, 0x1E6E0018, reg_mcr18 | (dqidly << 16) | (dqsip << 23));
+                       ast_moutdwm(ast, 0x1E6E000C, reg_mcr0c);
+                       for (dlli = 0; dlli < 76; dlli++) {
+                               ast_moutdwm(ast, 0x1E6E0068, 0x00001300 | (dlli << 16) | (dlli << 24));
+                               ast_moutdwm(ast, 0x1E6E0070, 0);
+                               ast_moutdwm(ast, 0x1E6E0074, CBR_SIZE0);
+                               if (cbr_scan3(ast)) {
+                                       if (dlli == 0)
+                                               break;
+                                       passcnt[dqsip]++;
+                                       tag[dqsip][dlli] = 'P';
+                                       if (dlli < pass[dqidly][dqsip][0])
+                                               pass[dqidly][dqsip][0] = (u16) dlli;
+                                       if (dlli > pass[dqidly][dqsip][1])
+                                               pass[dqidly][dqsip][1] = (u16) dlli;
+                               } else if (passcnt[dqsip] >= 5)
+                                       break;
+                               else {
+                                       pass[dqidly][dqsip][0] = 0xff;
+                                       pass[dqidly][dqsip][1] = 0x0;
                                }
                        }
                }
-               data |= data2 << 21;
-       }
-       moutdwm(ast, 0x1E6E0080, data);
-
-       gold_sadj[0] = 0x0;
-       gold_sadj[1] = 0xFF;
-       for (cnt = 8; cnt < 16; cnt++) {
-               if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
-                       if (gold_sadj[0] < dllmin[cnt]) {
-                               gold_sadj[0] = dllmin[cnt];
-                       }
-                       if (gold_sadj[1] > dllmax[cnt]) {
-                               gold_sadj[1] = dllmax[cnt];
-                       }
-               }
+               if (passcnt[0] == 0 && passcnt[1] == 0)
+                       dqidly++;
        }
-       gold_sadj[0] = (gold_sadj[1] + gold_sadj[0]) >> 1;
-       gold_sadj[1] = mindwm(ast, 0x1E6E0084);
-
-       data = 0;
-       for (cnt = 8; cnt < 16; cnt++) {
-               data >>= 3;
-               data2 = gold_sadj[1] & 0x7;
-               gold_sadj[1] >>= 3;
-               if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
-                       dlli = (dllmin[cnt] + dllmax[cnt]) >> 1;
-                       if (gold_sadj[0] >= dlli) {
-                               dlli = (gold_sadj[0] - dlli) >> 1;
-                               if (dlli > 0) {
-                                       dlli = 1;
-                               }
-                               if (data2 != 3) {
-                                       data2 = (data2 + dlli) & 0x7;
-                               }
-                       } else {
-                               dlli = (dlli - gold_sadj[0]) >> 1;
-                               if (dlli > 0) {
-                                       dlli = 1;
-                               }
-                               if (data2 != 4) {
-                                       data2 = (data2 - dlli) & 0x7;
-                               }
+       /* Search margin */
+       g_dqidly = g_dqsip = g_margin = g_side = 0;
+
+       for (dqidly = 0; dqidly < 32; dqidly++) {
+               for (dqsip = 0; dqsip < 2; dqsip++) {
+                       if (pass[dqidly][dqsip][0] > pass[dqidly][dqsip][1])
+                               continue;
+                       diff = pass[dqidly][dqsip][1] - pass[dqidly][dqsip][0];
+                       if ((diff+2) < g_margin)
+                               continue;
+                       passcnt[0] = passcnt[1] = 0;
+                       for (dlli = pass[dqidly][dqsip][0]; dlli > 0  && tag[dqsip][dlli] != 0; dlli--, passcnt[0]++);
+                       for (dlli = pass[dqidly][dqsip][1]; dlli < 76 && tag[dqsip][dlli] != 0; dlli++, passcnt[1]++);
+                       if (passcnt[0] > passcnt[1])
+                               passcnt[0] = passcnt[1];
+                       passcnt[1] = 0;
+                       if (passcnt[0] > g_side)
+                               passcnt[1] = passcnt[0] - g_side;
+                       if (diff > (g_margin+1) && (passcnt[1] > 0 || passcnt[0] > 8)) {
+                               g_margin = diff;
+                               g_dqidly = dqidly;
+                               g_dqsip  = dqsip;
+                               g_side   = passcnt[0];
+                       } else if (passcnt[1] > 1 && g_side < 8) {
+                               if (diff > g_margin)
+                                       g_margin = diff;
+                               g_dqidly = dqidly;
+                               g_dqsip  = dqsip;
+                               g_side   = passcnt[0];
                        }
                }
-               data |= data2 << 21;
        }
-       moutdwm(ast, 0x1E6E0084, data);
-
-} /* finetuneDQI_L2 */
+       reg_mcr18 = reg_mcr18 | (g_dqidly << 16) | (g_dqsip << 23);
+       ast_moutdwm(ast, 0x1E6E0018, reg_mcr18);
 
-static void cbr_dll2(struct ast_private *ast, struct ast2300_dram_param *param)
+}
+static bool cbr_dll2(struct ast_private *ast, struct ast2300_dram_param *param)
 {
-       u32 dllmin[2], dllmax[2], dlli, data, data2, passcnt;
+       u32 dllmin[2], dllmax[2], dlli, data, passcnt, retry = 0;
+       bool status = false;
 
-
-       finetuneDQI_L(ast, param);
-       finetuneDQI_L2(ast, param);
+       finetuneDQSI(ast);
+       if (finetuneDQI_L(ast, param) == false)
+               return status;
 
 CBR_START2:
        dllmin[0] = dllmin[1] = 0xff;
        dllmax[0] = dllmax[1] = 0x0;
        passcnt = 0;
        for (dlli = 0; dlli < 76; dlli++) {
-               moutdwm(ast, 0x1E6E0068, 0x00001300 | (dlli << 16) | (dlli << 24));
-               /* Wait DQSI latch phase calibration */
-               moutdwm(ast, 0x1E6E0074, 0x00000010);
-               moutdwm(ast, 0x1E6E0070, 0x00000003);
-               do {
-                       data = mindwm(ast, 0x1E6E0070);
-               } while (!(data & 0x00001000));
-               moutdwm(ast, 0x1E6E0070, 0x00000000);
-
-               moutdwm(ast, 0x1E6E0074, CBR_SIZE2);
+               ast_moutdwm(ast, 0x1E6E0068, 0x00001300 | (dlli << 16) | (dlli << 24));
+               ast_moutdwm(ast, 0x1E6E0074, CBR_SIZE2);
                data = cbr_scan(ast);
                if (data != 0) {
                        if (data & 0x1) {
@@ -957,44 +844,31 @@ CBR_START2:
                        break;
                }
        }
+       if (retry++ > 10)
+               goto CBR_DONE2;
        if (dllmax[0] == 0 || (dllmax[0]-dllmin[0]) < CBR_THRESHOLD) {
                goto CBR_START2;
        }
        if (dllmax[1] == 0 || (dllmax[1]-dllmin[1]) < CBR_THRESHOLD) {
                goto CBR_START2;
        }
+       status = true;
+CBR_DONE2:
        dlli  = (dllmin[1] + dllmax[1]) >> 1;
        dlli <<= 8;
        dlli += (dllmin[0] + dllmax[0]) >> 1;
-       moutdwm(ast, 0x1E6E0068, (mindwm(ast, 0x1E6E0068) & 0xFFFF) | (dlli << 16));
-
-       data  = (mindwm(ast, 0x1E6E0080) >> 24) & 0x1F;
-       data2 = (mindwm(ast, 0x1E6E0018) & 0xff80ffff) | (data << 16);
-       moutdwm(ast, 0x1E6E0018, data2);
-       moutdwm(ast, 0x1E6E0024, 0x8001 | (data << 1) | (param->dll2_finetune_step << 8));
-
-       /* Wait DQSI latch phase calibration */
-       moutdwm(ast, 0x1E6E0074, 0x00000010);
-       moutdwm(ast, 0x1E6E0070, 0x00000003);
-       do {
-               data = mindwm(ast, 0x1E6E0070);
-       } while (!(data & 0x00001000));
-       moutdwm(ast, 0x1E6E0070, 0x00000000);
-       moutdwm(ast, 0x1E6E0070, 0x00000003);
-       do {
-               data = mindwm(ast, 0x1E6E0070);
-       } while (!(data & 0x00001000));
-       moutdwm(ast, 0x1E6E0070, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0068, ast_mindwm(ast, 0x1E720058) | (dlli << 16));
+       return status;
 } /* CBRDLL2 */
 
 static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *param)
 {
        u32 trap, trap_AC2, trap_MRS;
 
-       moutdwm(ast, 0x1E6E2000, 0x1688A8A8);
+       ast_moutdwm(ast, 0x1E6E2000, 0x1688A8A8);
 
        /* Ger trap info */
-       trap = (mindwm(ast, 0x1E6E2070) >> 25) & 0x3;
+       trap = (ast_mindwm(ast, 0x1E6E2070) >> 25) & 0x3;
        trap_AC2  = 0x00020000 + (trap << 16);
        trap_AC2 |= 0x00300000 + ((trap & 0x2) << 19);
        trap_MRS  = 0x00000010 + (trap << 4);
@@ -1008,22 +882,35 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
 
        switch (param->dram_freq) {
        case 336:
-               moutdwm(ast, 0x1E6E2020, 0x0190);
+               ast_moutdwm(ast, 0x1E6E2020, 0x0190);
                param->wodt          = 0;
                param->reg_AC1       = 0x22202725;
                param->reg_AC2       = 0xAA007613 | trap_AC2;
                param->reg_DQSIC     = 0x000000BA;
                param->reg_MRS       = 0x04001400 | trap_MRS;
                param->reg_EMRS      = 0x00000000;
-               param->reg_IOZ       = 0x00000034;
+               param->reg_IOZ       = 0x00000023;
                param->reg_DQIDLY    = 0x00000074;
                param->reg_FREQ      = 0x00004DC0;
                param->madj_max      = 96;
                param->dll2_finetune_step = 3;
+               switch (param->dram_chipid) {
+               default:
+               case AST_DRAM_512Mx16:
+               case AST_DRAM_1Gx16:
+                       param->reg_AC2   = 0xAA007613 | trap_AC2;
+                       break;
+               case AST_DRAM_2Gx16:
+                       param->reg_AC2   = 0xAA00761C | trap_AC2;
+                       break;
+               case AST_DRAM_4Gx16:
+                       param->reg_AC2   = 0xAA007636 | trap_AC2;
+                       break;
+               }
                break;
        default:
        case 396:
-               moutdwm(ast, 0x1E6E2020, 0x03F1);
+               ast_moutdwm(ast, 0x1E6E2020, 0x03F1);
                param->wodt          = 1;
                param->reg_AC1       = 0x33302825;
                param->reg_AC2       = 0xCC009617 | trap_AC2;
@@ -1033,7 +920,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
                param->reg_IOZ       = 0x00000034;
                param->reg_DRV       = 0x000000FA;
                param->reg_DQIDLY    = 0x00000089;
-               param->reg_FREQ      = 0x000050C0;
+               param->reg_FREQ      = 0x00005040;
                param->madj_max      = 96;
                param->dll2_finetune_step = 4;
 
@@ -1053,14 +940,14 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
                break;
 
        case 408:
-               moutdwm(ast, 0x1E6E2020, 0x01F0);
+               ast_moutdwm(ast, 0x1E6E2020, 0x01F0);
                param->wodt          = 1;
                param->reg_AC1       = 0x33302825;
                param->reg_AC2       = 0xCC009617 | trap_AC2;
                param->reg_DQSIC     = 0x000000E2;
                param->reg_MRS       = 0x04001600 | trap_MRS;
                param->reg_EMRS      = 0x00000000;
-               param->reg_IOZ       = 0x00000034;
+               param->reg_IOZ       = 0x00000023;
                param->reg_DRV       = 0x000000FA;
                param->reg_DQIDLY    = 0x00000089;
                param->reg_FREQ      = 0x000050C0;
@@ -1083,7 +970,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
 
                break;
        case 456:
-               moutdwm(ast, 0x1E6E2020, 0x0230);
+               ast_moutdwm(ast, 0x1E6E2020, 0x0230);
                param->wodt          = 0;
                param->reg_AC1       = 0x33302926;
                param->reg_AC2       = 0xCD44961A;
@@ -1097,7 +984,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
                param->dll2_finetune_step = 4;
                break;
        case 504:
-               moutdwm(ast, 0x1E6E2020, 0x0270);
+               ast_moutdwm(ast, 0x1E6E2020, 0x0270);
                param->wodt          = 1;
                param->reg_AC1       = 0x33302926;
                param->reg_AC2       = 0xDE44A61D;
@@ -1111,7 +998,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
                param->dll2_finetune_step = 4;
                break;
        case 528:
-               moutdwm(ast, 0x1E6E2020, 0x0290);
+               ast_moutdwm(ast, 0x1E6E2020, 0x0290);
                param->wodt          = 1;
                param->rodt          = 1;
                param->reg_AC1       = 0x33302926;
@@ -1127,7 +1014,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
                param->dll2_finetune_step = 3;
                break;
        case 576:
-               moutdwm(ast, 0x1E6E2020, 0x0140);
+               ast_moutdwm(ast, 0x1E6E2020, 0x0140);
                param->reg_MADJ      = 0x00136868;
                param->reg_SADJ      = 0x00004534;
                param->wodt          = 1;
@@ -1145,7 +1032,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
                param->dll2_finetune_step = 3;
                break;
        case 600:
-               moutdwm(ast, 0x1E6E2020, 0x02E1);
+               ast_moutdwm(ast, 0x1E6E2020, 0x02E1);
                param->reg_MADJ      = 0x00136868;
                param->reg_SADJ      = 0x00004534;
                param->wodt          = 1;
@@ -1163,7 +1050,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
                param->dll2_finetune_step = 3;
                break;
        case 624:
-               moutdwm(ast, 0x1E6E2020, 0x0160);
+               ast_moutdwm(ast, 0x1E6E2020, 0x0160);
                param->reg_MADJ      = 0x00136868;
                param->reg_SADJ      = 0x00004534;
                param->wodt          = 1;
@@ -1196,7 +1083,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
        case AST_DRAM_4Gx16:
                param->dram_config = 0x133;
                break;
-       }; /* switch size */
+       } /* switch size */
 
        switch (param->vram_size) {
        default:
@@ -1218,106 +1105,98 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
 
 static void ddr3_init(struct ast_private *ast, struct ast2300_dram_param *param)
 {
-       u32 data, data2;
+       u32 data, data2, retry = 0;
 
-       moutdwm(ast, 0x1E6E0000, 0xFC600309);
-       moutdwm(ast, 0x1E6E0018, 0x00000100);
-       moutdwm(ast, 0x1E6E0024, 0x00000000);
-       moutdwm(ast, 0x1E6E0034, 0x00000000);
+ddr3_init_start:
+       ast_moutdwm(ast, 0x1E6E0000, 0xFC600309);
+       ast_moutdwm(ast, 0x1E6E0018, 0x00000100);
+       ast_moutdwm(ast, 0x1E6E0024, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0034, 0x00000000);
        udelay(10);
-       moutdwm(ast, 0x1E6E0064, param->reg_MADJ);
-       moutdwm(ast, 0x1E6E0068, param->reg_SADJ);
+       ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ);
+       ast_moutdwm(ast, 0x1E6E0068, param->reg_SADJ);
        udelay(10);
-       moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000);
+       ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000);
        udelay(10);
 
-       moutdwm(ast, 0x1E6E0004, param->dram_config);
-       moutdwm(ast, 0x1E6E0008, 0x90040f);
-       moutdwm(ast, 0x1E6E0010, param->reg_AC1);
-       moutdwm(ast, 0x1E6E0014, param->reg_AC2);
-       moutdwm(ast, 0x1E6E0020, param->reg_DQSIC);
-       moutdwm(ast, 0x1E6E0080, 0x00000000);
-       moutdwm(ast, 0x1E6E0084, 0x00000000);
-       moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY);
-       moutdwm(ast, 0x1E6E0018, 0x4040A170);
-       moutdwm(ast, 0x1E6E0018, 0x20402370);
-       moutdwm(ast, 0x1E6E0038, 0x00000000);
-       moutdwm(ast, 0x1E6E0040, 0xFF444444);
-       moutdwm(ast, 0x1E6E0044, 0x22222222);
-       moutdwm(ast, 0x1E6E0048, 0x22222222);
-       moutdwm(ast, 0x1E6E004C, 0x00000002);
-       moutdwm(ast, 0x1E6E0050, 0x80000000);
-       moutdwm(ast, 0x1E6E0050, 0x00000000);
-       moutdwm(ast, 0x1E6E0054, 0);
-       moutdwm(ast, 0x1E6E0060, param->reg_DRV);
-       moutdwm(ast, 0x1E6E006C, param->reg_IOZ);
-       moutdwm(ast, 0x1E6E0070, 0x00000000);
-       moutdwm(ast, 0x1E6E0074, 0x00000000);
-       moutdwm(ast, 0x1E6E0078, 0x00000000);
-       moutdwm(ast, 0x1E6E007C, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0004, param->dram_config);
+       ast_moutdwm(ast, 0x1E6E0008, 0x90040f);
+       ast_moutdwm(ast, 0x1E6E0010, param->reg_AC1);
+       ast_moutdwm(ast, 0x1E6E0014, param->reg_AC2);
+       ast_moutdwm(ast, 0x1E6E0020, param->reg_DQSIC);
+       ast_moutdwm(ast, 0x1E6E0080, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0084, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY);
+       ast_moutdwm(ast, 0x1E6E0018, 0x4000A170);
+       ast_moutdwm(ast, 0x1E6E0018, 0x00002370);
+       ast_moutdwm(ast, 0x1E6E0038, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0040, 0xFF444444);
+       ast_moutdwm(ast, 0x1E6E0044, 0x22222222);
+       ast_moutdwm(ast, 0x1E6E0048, 0x22222222);
+       ast_moutdwm(ast, 0x1E6E004C, 0x00000002);
+       ast_moutdwm(ast, 0x1E6E0050, 0x80000000);
+       ast_moutdwm(ast, 0x1E6E0050, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0054, 0);
+       ast_moutdwm(ast, 0x1E6E0060, param->reg_DRV);
+       ast_moutdwm(ast, 0x1E6E006C, param->reg_IOZ);
+       ast_moutdwm(ast, 0x1E6E0070, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0074, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0078, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E007C, 0x00000000);
        /* Wait MCLK2X lock to MCLK */
        do {
-               data = mindwm(ast, 0x1E6E001C);
+               data = ast_mindwm(ast, 0x1E6E001C);
        } while (!(data & 0x08000000));
-       moutdwm(ast, 0x1E6E0034, 0x00000001);
-       moutdwm(ast, 0x1E6E000C, 0x00005C04);
-       udelay(10);
-       moutdwm(ast, 0x1E6E000C, 0x00000000);
-       moutdwm(ast, 0x1E6E0034, 0x00000000);
-       data = mindwm(ast, 0x1E6E001C);
+       data = ast_mindwm(ast, 0x1E6E001C);
        data = (data >> 8) & 0xff;
        while ((data & 0x08) || ((data & 0x7) < 2) || (data < 4)) {
-               data2 = (mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4;
+               data2 = (ast_mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4;
                if ((data2 & 0xff) > param->madj_max) {
                        break;
                }
-               moutdwm(ast, 0x1E6E0064, data2);
+               ast_moutdwm(ast, 0x1E6E0064, data2);
                if (data2 & 0x00100000) {
                        data2 = ((data2 & 0xff) >> 3) + 3;
                } else {
                        data2 = ((data2 & 0xff) >> 2) + 5;
                }
-               data = mindwm(ast, 0x1E6E0068) & 0xffff00ff;
+               data = ast_mindwm(ast, 0x1E6E0068) & 0xffff00ff;
                data2 += data & 0xff;
                data = data | (data2 << 8);
-               moutdwm(ast, 0x1E6E0068, data);
+               ast_moutdwm(ast, 0x1E6E0068, data);
                udelay(10);
-               moutdwm(ast, 0x1E6E0064, mindwm(ast, 0x1E6E0064) | 0xC0000);
+               ast_moutdwm(ast, 0x1E6E0064, ast_mindwm(ast, 0x1E6E0064) | 0xC0000);
                udelay(10);
-               data = mindwm(ast, 0x1E6E0018) & 0xfffff1ff;
-               moutdwm(ast, 0x1E6E0018, data);
+               data = ast_mindwm(ast, 0x1E6E0018) & 0xfffff1ff;
+               ast_moutdwm(ast, 0x1E6E0018, data);
                data = data | 0x200;
-               moutdwm(ast, 0x1E6E0018, data);
+               ast_moutdwm(ast, 0x1E6E0018, data);
                do {
-                       data = mindwm(ast, 0x1E6E001C);
+                       data = ast_mindwm(ast, 0x1E6E001C);
                } while (!(data & 0x08000000));
 
-               moutdwm(ast, 0x1E6E0034, 0x00000001);
-               moutdwm(ast, 0x1E6E000C, 0x00005C04);
-               udelay(10);
-               moutdwm(ast, 0x1E6E000C, 0x00000000);
-               moutdwm(ast, 0x1E6E0034, 0x00000000);
-               data = mindwm(ast, 0x1E6E001C);
+               data = ast_mindwm(ast, 0x1E6E001C);
                data = (data >> 8) & 0xff;
        }
-       data = mindwm(ast, 0x1E6E0018) | 0xC00;
-       moutdwm(ast, 0x1E6E0018, data);
+       ast_moutdwm(ast, 0x1E720058, ast_mindwm(ast, 0x1E6E0068) & 0xffff);
+       data = ast_mindwm(ast, 0x1E6E0018) | 0xC00;
+       ast_moutdwm(ast, 0x1E6E0018, data);
 
-       moutdwm(ast, 0x1E6E0034, 0x00000001);
-       moutdwm(ast, 0x1E6E000C, 0x00000040);
+       ast_moutdwm(ast, 0x1E6E0034, 0x00000001);
+       ast_moutdwm(ast, 0x1E6E000C, 0x00000040);
        udelay(50);
        /* Mode Register Setting */
-       moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100);
-       moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
-       moutdwm(ast, 0x1E6E0028, 0x00000005);
-       moutdwm(ast, 0x1E6E0028, 0x00000007);
-       moutdwm(ast, 0x1E6E0028, 0x00000003);
-       moutdwm(ast, 0x1E6E0028, 0x00000001);
-       moutdwm(ast, 0x1E6E002C, param->reg_MRS);
-       moutdwm(ast, 0x1E6E000C, 0x00005C08);
-       moutdwm(ast, 0x1E6E0028, 0x00000001);
-
-       moutdwm(ast, 0x1E6E000C, 0x7FFF5C01);
+       ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100);
+       ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
+       ast_moutdwm(ast, 0x1E6E0028, 0x00000005);
+       ast_moutdwm(ast, 0x1E6E0028, 0x00000007);
+       ast_moutdwm(ast, 0x1E6E0028, 0x00000003);
+       ast_moutdwm(ast, 0x1E6E0028, 0x00000001);
+       ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS);
+       ast_moutdwm(ast, 0x1E6E000C, 0x00005C08);
+       ast_moutdwm(ast, 0x1E6E0028, 0x00000001);
+
+       ast_moutdwm(ast, 0x1E6E000C, 0x00005C01);
        data = 0;
        if (param->wodt) {
                data = 0x300;
@@ -1325,30 +1204,23 @@ static void ddr3_init(struct ast_private *ast, struct ast2300_dram_param *param)
        if (param->rodt) {
                data = data | 0x3000 | ((param->reg_AC2 & 0x60000) >> 3);
        }
-       moutdwm(ast, 0x1E6E0034, data | 0x3);
+       ast_moutdwm(ast, 0x1E6E0034, data | 0x3);
 
-       /* Wait DQI delay lock */
-       do {
-               data = mindwm(ast, 0x1E6E0080);
-       } while (!(data & 0x40000000));
-       /* Wait DQSI delay lock */
-       do {
-               data = mindwm(ast, 0x1E6E0020);
-       } while (!(data & 0x00000800));
        /* Calibrate the DQSI delay */
-       cbr_dll2(ast, param);
+       if ((cbr_dll2(ast, param) == false) && (retry++ < 10))
+               goto ddr3_init_start;
 
-       moutdwm(ast, 0x1E6E0120, param->reg_FREQ);
+       ast_moutdwm(ast, 0x1E6E0120, param->reg_FREQ);
        /* ECC Memory Initialization */
 #ifdef ECC
-       moutdwm(ast, 0x1E6E007C, 0x00000000);
-       moutdwm(ast, 0x1E6E0070, 0x221);
+       ast_moutdwm(ast, 0x1E6E007C, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0070, 0x221);
        do {
-               data = mindwm(ast, 0x1E6E0070);
+               data = ast_mindwm(ast, 0x1E6E0070);
        } while (!(data & 0x00001000));
-       moutdwm(ast, 0x1E6E0070, 0x00000000);
-       moutdwm(ast, 0x1E6E0050, 0x80000000);
-       moutdwm(ast, 0x1E6E0050, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0070, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0050, 0x80000000);
+       ast_moutdwm(ast, 0x1E6E0050, 0x00000000);
 #endif
 
 
@@ -1358,10 +1230,10 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
 {
        u32 trap, trap_AC2, trap_MRS;
 
-       moutdwm(ast, 0x1E6E2000, 0x1688A8A8);
+       ast_moutdwm(ast, 0x1E6E2000, 0x1688A8A8);
 
        /* Ger trap info */
-       trap = (mindwm(ast, 0x1E6E2070) >> 25) & 0x3;
+       trap = (ast_mindwm(ast, 0x1E6E2070) >> 25) & 0x3;
        trap_AC2  = (trap << 20) | (trap << 16);
        trap_AC2 += 0x00110000;
        trap_MRS  = 0x00000040 | (trap << 4);
@@ -1375,7 +1247,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
 
        switch (param->dram_freq) {
        case 264:
-               moutdwm(ast, 0x1E6E2020, 0x0130);
+               ast_moutdwm(ast, 0x1E6E2020, 0x0130);
                param->wodt          = 0;
                param->reg_AC1       = 0x11101513;
                param->reg_AC2       = 0x78117011;
@@ -1390,7 +1262,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
                param->dll2_finetune_step = 3;
                break;
        case 336:
-               moutdwm(ast, 0x1E6E2020, 0x0190);
+               ast_moutdwm(ast, 0x1E6E2020, 0x0190);
                param->wodt          = 1;
                param->reg_AC1       = 0x22202613;
                param->reg_AC2       = 0xAA009016 | trap_AC2;
@@ -1403,10 +1275,25 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
                param->reg_FREQ      = 0x00004DC0;
                param->madj_max      = 96;
                param->dll2_finetune_step = 3;
+               switch (param->dram_chipid) {
+               default:
+               case AST_DRAM_512Mx16:
+                       param->reg_AC2   = 0xAA009012 | trap_AC2;
+                       break;
+               case AST_DRAM_1Gx16:
+                       param->reg_AC2   = 0xAA009016 | trap_AC2;
+                       break;
+               case AST_DRAM_2Gx16:
+                       param->reg_AC2   = 0xAA009023 | trap_AC2;
+                       break;
+               case AST_DRAM_4Gx16:
+                       param->reg_AC2   = 0xAA00903B | trap_AC2;
+                       break;
+               }
                break;
        default:
        case 396:
-               moutdwm(ast, 0x1E6E2020, 0x03F1);
+               ast_moutdwm(ast, 0x1E6E2020, 0x03F1);
                param->wodt          = 1;
                param->rodt          = 0;
                param->reg_AC1       = 0x33302714;
@@ -1417,7 +1304,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
                param->reg_DRV       = 0x000000FA;
                param->reg_IOZ       = 0x00000034;
                param->reg_DQIDLY    = 0x00000089;
-               param->reg_FREQ      = 0x000050C0;
+               param->reg_FREQ      = 0x00005040;
                param->madj_max      = 96;
                param->dll2_finetune_step = 4;
 
@@ -1440,7 +1327,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
                break;
 
        case 408:
-               moutdwm(ast, 0x1E6E2020, 0x01F0);
+               ast_moutdwm(ast, 0x1E6E2020, 0x01F0);
                param->wodt          = 1;
                param->rodt          = 0;
                param->reg_AC1       = 0x33302714;
@@ -1473,7 +1360,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
 
                break;
        case 456:
-               moutdwm(ast, 0x1E6E2020, 0x0230);
+               ast_moutdwm(ast, 0x1E6E2020, 0x0230);
                param->wodt          = 0;
                param->reg_AC1       = 0x33302815;
                param->reg_AC2       = 0xCD44B01E;
@@ -1488,7 +1375,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
                param->dll2_finetune_step = 3;
                break;
        case 504:
-               moutdwm(ast, 0x1E6E2020, 0x0261);
+               ast_moutdwm(ast, 0x1E6E2020, 0x0261);
                param->wodt          = 1;
                param->rodt          = 1;
                param->reg_AC1       = 0x33302815;
@@ -1504,7 +1391,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
                param->dll2_finetune_step = 3;
                break;
        case 528:
-               moutdwm(ast, 0x1E6E2020, 0x0120);
+               ast_moutdwm(ast, 0x1E6E2020, 0x0120);
                param->wodt          = 1;
                param->rodt          = 1;
                param->reg_AC1       = 0x33302815;
@@ -1520,7 +1407,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
                param->dll2_finetune_step = 3;
                break;
        case 552:
-               moutdwm(ast, 0x1E6E2020, 0x02A1);
+               ast_moutdwm(ast, 0x1E6E2020, 0x02A1);
                param->wodt          = 1;
                param->rodt          = 1;
                param->reg_AC1       = 0x43402915;
@@ -1536,7 +1423,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
                param->dll2_finetune_step = 3;
                break;
        case 576:
-               moutdwm(ast, 0x1E6E2020, 0x0140);
+               ast_moutdwm(ast, 0x1E6E2020, 0x0140);
                param->wodt          = 1;
                param->rodt          = 1;
                param->reg_AC1       = 0x43402915;
@@ -1567,7 +1454,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
        case AST_DRAM_4Gx16:
                param->dram_config = 0x123;
                break;
-       }; /* switch size */
+       } /* switch size */
 
        switch (param->vram_size) {
        default:
@@ -1588,110 +1475,102 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
 
 static void ddr2_init(struct ast_private *ast, struct ast2300_dram_param *param)
 {
-       u32 data, data2;
-
-       moutdwm(ast, 0x1E6E0000, 0xFC600309);
-       moutdwm(ast, 0x1E6E0018, 0x00000100);
-       moutdwm(ast, 0x1E6E0024, 0x00000000);
-       moutdwm(ast, 0x1E6E0064, param->reg_MADJ);
-       moutdwm(ast, 0x1E6E0068, param->reg_SADJ);
+       u32 data, data2, retry = 0;
+
+ddr2_init_start:
+       ast_moutdwm(ast, 0x1E6E0000, 0xFC600309);
+       ast_moutdwm(ast, 0x1E6E0018, 0x00000100);
+       ast_moutdwm(ast, 0x1E6E0024, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ);
+       ast_moutdwm(ast, 0x1E6E0068, param->reg_SADJ);
        udelay(10);
-       moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000);
+       ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000);
        udelay(10);
 
-       moutdwm(ast, 0x1E6E0004, param->dram_config);
-       moutdwm(ast, 0x1E6E0008, 0x90040f);
-       moutdwm(ast, 0x1E6E0010, param->reg_AC1);
-       moutdwm(ast, 0x1E6E0014, param->reg_AC2);
-       moutdwm(ast, 0x1E6E0020, param->reg_DQSIC);
-       moutdwm(ast, 0x1E6E0080, 0x00000000);
-       moutdwm(ast, 0x1E6E0084, 0x00000000);
-       moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY);
-       moutdwm(ast, 0x1E6E0018, 0x4040A130);
-       moutdwm(ast, 0x1E6E0018, 0x20402330);
-       moutdwm(ast, 0x1E6E0038, 0x00000000);
-       moutdwm(ast, 0x1E6E0040, 0xFF808000);
-       moutdwm(ast, 0x1E6E0044, 0x88848466);
-       moutdwm(ast, 0x1E6E0048, 0x44440008);
-       moutdwm(ast, 0x1E6E004C, 0x00000000);
-       moutdwm(ast, 0x1E6E0050, 0x80000000);
-       moutdwm(ast, 0x1E6E0050, 0x00000000);
-       moutdwm(ast, 0x1E6E0054, 0);
-       moutdwm(ast, 0x1E6E0060, param->reg_DRV);
-       moutdwm(ast, 0x1E6E006C, param->reg_IOZ);
-       moutdwm(ast, 0x1E6E0070, 0x00000000);
-       moutdwm(ast, 0x1E6E0074, 0x00000000);
-       moutdwm(ast, 0x1E6E0078, 0x00000000);
-       moutdwm(ast, 0x1E6E007C, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0004, param->dram_config);
+       ast_moutdwm(ast, 0x1E6E0008, 0x90040f);
+       ast_moutdwm(ast, 0x1E6E0010, param->reg_AC1);
+       ast_moutdwm(ast, 0x1E6E0014, param->reg_AC2);
+       ast_moutdwm(ast, 0x1E6E0020, param->reg_DQSIC);
+       ast_moutdwm(ast, 0x1E6E0080, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0084, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY);
+       ast_moutdwm(ast, 0x1E6E0018, 0x4000A130);
+       ast_moutdwm(ast, 0x1E6E0018, 0x00002330);
+       ast_moutdwm(ast, 0x1E6E0038, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0040, 0xFF808000);
+       ast_moutdwm(ast, 0x1E6E0044, 0x88848466);
+       ast_moutdwm(ast, 0x1E6E0048, 0x44440008);
+       ast_moutdwm(ast, 0x1E6E004C, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0050, 0x80000000);
+       ast_moutdwm(ast, 0x1E6E0050, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0054, 0);
+       ast_moutdwm(ast, 0x1E6E0060, param->reg_DRV);
+       ast_moutdwm(ast, 0x1E6E006C, param->reg_IOZ);
+       ast_moutdwm(ast, 0x1E6E0070, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0074, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0078, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E007C, 0x00000000);
 
        /* Wait MCLK2X lock to MCLK */
        do {
-               data = mindwm(ast, 0x1E6E001C);
+               data = ast_mindwm(ast, 0x1E6E001C);
        } while (!(data & 0x08000000));
-       moutdwm(ast, 0x1E6E0034, 0x00000001);
-       moutdwm(ast, 0x1E6E000C, 0x00005C04);
-       udelay(10);
-       moutdwm(ast, 0x1E6E000C, 0x00000000);
-       moutdwm(ast, 0x1E6E0034, 0x00000000);
-       data = mindwm(ast, 0x1E6E001C);
+       data = ast_mindwm(ast, 0x1E6E001C);
        data = (data >> 8) & 0xff;
        while ((data & 0x08) || ((data & 0x7) < 2) || (data < 4)) {
-               data2 = (mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4;
+               data2 = (ast_mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4;
                if ((data2 & 0xff) > param->madj_max) {
                        break;
                }
-               moutdwm(ast, 0x1E6E0064, data2);
+               ast_moutdwm(ast, 0x1E6E0064, data2);
                if (data2 & 0x00100000) {
                        data2 = ((data2 & 0xff) >> 3) + 3;
                } else {
                        data2 = ((data2 & 0xff) >> 2) + 5;
                }
-               data = mindwm(ast, 0x1E6E0068) & 0xffff00ff;
+               data = ast_mindwm(ast, 0x1E6E0068) & 0xffff00ff;
                data2 += data & 0xff;
                data = data | (data2 << 8);
-               moutdwm(ast, 0x1E6E0068, data);
+               ast_moutdwm(ast, 0x1E6E0068, data);
                udelay(10);
-               moutdwm(ast, 0x1E6E0064, mindwm(ast, 0x1E6E0064) | 0xC0000);
+               ast_moutdwm(ast, 0x1E6E0064, ast_mindwm(ast, 0x1E6E0064) | 0xC0000);
                udelay(10);
-               data = mindwm(ast, 0x1E6E0018) & 0xfffff1ff;
-               moutdwm(ast, 0x1E6E0018, data);
+               data = ast_mindwm(ast, 0x1E6E0018) & 0xfffff1ff;
+               ast_moutdwm(ast, 0x1E6E0018, data);
                data = data | 0x200;
-               moutdwm(ast, 0x1E6E0018, data);
+               ast_moutdwm(ast, 0x1E6E0018, data);
                do {
-                       data = mindwm(ast, 0x1E6E001C);
+                       data = ast_mindwm(ast, 0x1E6E001C);
                } while (!(data & 0x08000000));
 
-               moutdwm(ast, 0x1E6E0034, 0x00000001);
-               moutdwm(ast, 0x1E6E000C, 0x00005C04);
-               udelay(10);
-               moutdwm(ast, 0x1E6E000C, 0x00000000);
-               moutdwm(ast, 0x1E6E0034, 0x00000000);
-               data = mindwm(ast, 0x1E6E001C);
+               data = ast_mindwm(ast, 0x1E6E001C);
                data = (data >> 8) & 0xff;
        }
-       data = mindwm(ast, 0x1E6E0018) | 0xC00;
-       moutdwm(ast, 0x1E6E0018, data);
+       ast_moutdwm(ast, 0x1E720058, ast_mindwm(ast, 0x1E6E0008) & 0xffff);
+       data = ast_mindwm(ast, 0x1E6E0018) | 0xC00;
+       ast_moutdwm(ast, 0x1E6E0018, data);
 
-       moutdwm(ast, 0x1E6E0034, 0x00000001);
-       moutdwm(ast, 0x1E6E000C, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0034, 0x00000001);
+       ast_moutdwm(ast, 0x1E6E000C, 0x00000000);
        udelay(50);
        /* Mode Register Setting */
-       moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100);
-       moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
-       moutdwm(ast, 0x1E6E0028, 0x00000005);
-       moutdwm(ast, 0x1E6E0028, 0x00000007);
-       moutdwm(ast, 0x1E6E0028, 0x00000003);
-       moutdwm(ast, 0x1E6E0028, 0x00000001);
-
-       moutdwm(ast, 0x1E6E000C, 0x00005C08);
-       moutdwm(ast, 0x1E6E002C, param->reg_MRS);
-       moutdwm(ast, 0x1E6E0028, 0x00000001);
-       moutdwm(ast, 0x1E6E0030, param->reg_EMRS | 0x380);
-       moutdwm(ast, 0x1E6E0028, 0x00000003);
-       moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
-       moutdwm(ast, 0x1E6E0028, 0x00000003);
-
-       moutdwm(ast, 0x1E6E000C, 0x7FFF5C01);
+       ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100);
+       ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
+       ast_moutdwm(ast, 0x1E6E0028, 0x00000005);
+       ast_moutdwm(ast, 0x1E6E0028, 0x00000007);
+       ast_moutdwm(ast, 0x1E6E0028, 0x00000003);
+       ast_moutdwm(ast, 0x1E6E0028, 0x00000001);
+
+       ast_moutdwm(ast, 0x1E6E000C, 0x00005C08);
+       ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS);
+       ast_moutdwm(ast, 0x1E6E0028, 0x00000001);
+       ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS | 0x380);
+       ast_moutdwm(ast, 0x1E6E0028, 0x00000003);
+       ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
+       ast_moutdwm(ast, 0x1E6E0028, 0x00000003);
+
+       ast_moutdwm(ast, 0x1E6E000C, 0x7FFF5C01);
        data = 0;
        if (param->wodt) {
                data = 0x500;
@@ -1699,30 +1578,23 @@ static void ddr2_init(struct ast_private *ast, struct ast2300_dram_param *param)
        if (param->rodt) {
                data = data | 0x3000 | ((param->reg_AC2 & 0x60000) >> 3);
        }
-       moutdwm(ast, 0x1E6E0034, data | 0x3);
-       moutdwm(ast, 0x1E6E0120, param->reg_FREQ);
+       ast_moutdwm(ast, 0x1E6E0034, data | 0x3);
+       ast_moutdwm(ast, 0x1E6E0120, param->reg_FREQ);
 
-       /* Wait DQI delay lock */
-       do {
-               data = mindwm(ast, 0x1E6E0080);
-       } while (!(data & 0x40000000));
-       /* Wait DQSI delay lock */
-       do {
-               data = mindwm(ast, 0x1E6E0020);
-       } while (!(data & 0x00000800));
        /* Calibrate the DQSI delay */
-       cbr_dll2(ast, param);
+       if ((cbr_dll2(ast, param) == false) && (retry++ < 10))
+               goto ddr2_init_start;
 
        /* ECC Memory Initialization */
 #ifdef ECC
-       moutdwm(ast, 0x1E6E007C, 0x00000000);
-       moutdwm(ast, 0x1E6E0070, 0x221);
+       ast_moutdwm(ast, 0x1E6E007C, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0070, 0x221);
        do {
-               data = mindwm(ast, 0x1E6E0070);
+               data = ast_mindwm(ast, 0x1E6E0070);
        } while (!(data & 0x00001000));
-       moutdwm(ast, 0x1E6E0070, 0x00000000);
-       moutdwm(ast, 0x1E6E0050, 0x80000000);
-       moutdwm(ast, 0x1E6E0050, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0070, 0x00000000);
+       ast_moutdwm(ast, 0x1E6E0050, 0x80000000);
+       ast_moutdwm(ast, 0x1E6E0050, 0x00000000);
 #endif
 
 }
@@ -1768,8 +1640,8 @@ static void ast_init_dram_2300(struct drm_device *dev)
                        ddr2_init(ast, &param);
                }
 
-               temp = mindwm(ast, 0x1e6e2040);
-               moutdwm(ast, 0x1e6e2040, temp | 0x40);
+               temp = ast_mindwm(ast, 0x1e6e2040);
+               ast_moutdwm(ast, 0x1e6e2040, temp | 0x40);
        }
 
        /* wait ready */
index 95fa6aba26bc14bb3e8a27956cf8b39ea942b514..4c761dcea97217e9495ed16a6403250ac04a7ff3 100644 (file)
@@ -42,7 +42,7 @@
 #define HBorder                 0x00000020
 #define VBorder                 0x00000010
 #define WideScreenMode         0x00000100
-
+#define NewModeInfo            0x00000200
 
 /* DCLK Index */
 #define VCLK25_175                     0x00
 #define VCLK106_5              0x12
 #define VCLK146_25             0x13
 #define VCLK148_5              0x14
+#define VCLK71                 0x15
+#define VCLK88_75              0x16
+#define VCLK119                0x17
+#define VCLK85_5               0x18
+#define VCLK97_75              0x19
 
 static struct ast_vbios_dclk_info dclk_table[] = {
        {0x2C, 0xE7, 0x03},                                     /* 00: VCLK25_175       */
@@ -90,6 +95,10 @@ static struct ast_vbios_dclk_info dclk_table[] = {
        {0x28, 0x49, 0x80},                                     /* 12: VCLK106.5        */
        {0x37, 0x49, 0x80},                                     /* 13: VCLK146.25       */
        {0x1f, 0x45, 0x80},                                     /* 14: VCLK148.5        */
+       {0x47, 0x6c, 0x80},                                     /* 15: VCLK71       */
+       {0x25, 0x65, 0x80},                                     /* 16: VCLK88.75    */
+       {0x77, 0x58, 0x80},                                     /* 17: VCLK119      */
+       {0x32, 0x67, 0x80},                                 /* 18: VCLK85_5     */
 };
 
 static struct ast_vbios_stdtable vbios_stdtable[] = {
@@ -225,41 +234,63 @@ static struct ast_vbios_enhtable res_1600x1200[] = {
         (SyncPP | Charx8Dot), 0xFF, 1, 0x33 },
 };
 
-static struct ast_vbios_enhtable res_1920x1200[] = {
-       {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz */
-        (SyncNP | Charx8Dot), 60, 1, 0x34 },
-       {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz */
-        (SyncNP | Charx8Dot), 0xFF, 1, 0x34 },
+/* 16:9 */
+static struct ast_vbios_enhtable res_1360x768[] = {
+       {1792, 1360, 64,112, 795,  768, 3, 6, VCLK85_5,          /* 60Hz */
+        (SyncPP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x39 },
+       {1792, 1360, 64,112, 795,  768, 3, 6, VCLK85_5,          /* end */
+        (SyncPP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x39 },
+};
+
+static struct ast_vbios_enhtable res_1600x900[] = {
+       {1760, 1600, 48, 32, 926,  900, 3, 5, VCLK97_75,        /* 60Hz CVT RB */
+        (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x3A },
+       {1760, 1600, 48, 32, 926,  900, 3, 5, VCLK97_75,        /* end */
+        (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x3A }
 };
 
+static struct ast_vbios_enhtable res_1920x1080[] = {
+       {2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5,       /* 60Hz */
+        (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x38 },
+       {2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5,       /* 60Hz */
+        (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x38 },
+};
+
+
 /* 16:10 */
 static struct ast_vbios_enhtable res_1280x800[] = {
+       {1440, 1280, 48, 32,  823,  800, 3, 6, VCLK71,  /* 60Hz RB */
+        (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 35 },
        {1680, 1280, 72,128,  831,  800, 3, 6, VCLK83_5,        /* 60Hz */
-        (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 60, 1, 0x35 },
+        (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x35 },
        {1680, 1280, 72,128,  831,  800, 3, 6, VCLK83_5,        /* 60Hz */
-        (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 0xFF, 1, 0x35 },
+        (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x35 },
 
 };
 
 static struct ast_vbios_enhtable res_1440x900[] = {
+       {1600, 1440, 48, 32,  926,  900, 3, 6, VCLK88_75,       /* 60Hz RB */
+        (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x36 },
        {1904, 1440, 80,152,  934,  900, 3, 6, VCLK106_5,       /* 60Hz */
-        (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 60, 1, 0x36 },
+        (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x36 },
        {1904, 1440, 80,152,  934,  900, 3, 6, VCLK106_5,       /* 60Hz */
-        (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 0xFF, 1, 0x36 },
+        (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x36 },
 };
 
 static struct ast_vbios_enhtable res_1680x1050[] = {
+       {1840, 1680, 48, 32, 1080, 1050, 3, 6, VCLK119, /* 60Hz RB */
+        (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x37 },
        {2240, 1680,104,176, 1089, 1050, 3, 6, VCLK146_25,      /* 60Hz */
-        (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 60, 1, 0x37 },
+        (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x37 },
        {2240, 1680,104,176, 1089, 1050, 3, 6, VCLK146_25,      /* 60Hz */
-        (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 0xFF, 1, 0x37 },
+        (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x37 },
 };
 
-/* HDTV */
-static struct ast_vbios_enhtable res_1920x1080[] = {
-       {2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5,       /* 60Hz */
-        (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode), 60, 1, 0x38 },
-       {2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5,       /* 60Hz */
-        (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode), 0xFF, 1, 0x38 },
+static struct ast_vbios_enhtable res_1920x1200[] = {
+       {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz */
+        (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x34 },
+       {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz */
+        (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x34 },
 };
+
 #endif
index f488be55d650e332560e7bd83212007cac50c4a5..b9a695d92792ee224868d3f9f9ce32e0475d48c8 100644 (file)
@@ -434,17 +434,13 @@ static void bochs_bo_unref(struct bochs_bo **bo)
 
        tbo = &((*bo)->bo);
        ttm_bo_unref(&tbo);
-       if (tbo == NULL)
-               *bo = NULL;
-
+       *bo = NULL;
 }
 
 void bochs_gem_free_object(struct drm_gem_object *obj)
 {
        struct bochs_bo *bochs_bo = gem_to_bochs_bo(obj);
 
-       if (!bochs_bo)
-               return;
        bochs_bo_unref(&bochs_bo);
 }
 
index b171901a35538d7a958e8289df8631f29ae53a47..98fd17ae491690b3f63de688dae611bf617b529a 100644 (file)
@@ -225,12 +225,6 @@ out:
        return num_modes;
 }
 
-static int ptn3460_mode_valid(struct drm_connector *connector,
-               struct drm_display_mode *mode)
-{
-       return MODE_OK;
-}
-
 struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector)
 {
        struct ptn3460_bridge *ptn_bridge;
@@ -242,7 +236,6 @@ struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector)
 
 struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
        .get_modes = ptn3460_get_modes,
-       .mode_valid = ptn3460_mode_valid,
        .best_encoder = ptn3460_best_encoder,
 };
 
index 4b0170cf53fd9225f07f731766cb8fe71fb40e91..99c1983f99d228b77ce9c498236ad68b348440e2 100644 (file)
@@ -264,17 +264,13 @@ static void cirrus_bo_unref(struct cirrus_bo **bo)
 
        tbo = &((*bo)->bo);
        ttm_bo_unref(&tbo);
-       if (tbo == NULL)
-               *bo = NULL;
-
+       *bo = NULL;
 }
 
 void cirrus_gem_free_object(struct drm_gem_object *obj)
 {
        struct cirrus_bo *cirrus_bo = gem_to_cirrus_bo(obj);
 
-       if (!cirrus_bo)
-               return;
        cirrus_bo_unref(&cirrus_bo);
 }
 
index f59433b7610c560db846a9e330a3b3a1b87c1f0b..49332c5fe35b04a763923f3c44376276ec989338 100644 (file)
@@ -505,13 +505,6 @@ static int cirrus_vga_get_modes(struct drm_connector *connector)
        return count;
 }
 
-static int cirrus_vga_mode_valid(struct drm_connector *connector,
-                                struct drm_display_mode *mode)
-{
-       /* Any mode we've added is valid */
-       return MODE_OK;
-}
-
 static struct drm_encoder *cirrus_connector_best_encoder(struct drm_connector
                                                  *connector)
 {
@@ -546,7 +539,6 @@ static void cirrus_connector_destroy(struct drm_connector *connector)
 
 struct drm_connector_helper_funcs cirrus_vga_connector_helper_funcs = {
        .get_modes = cirrus_vga_get_modes,
-       .mode_valid = cirrus_vga_mode_valid,
        .best_encoder = cirrus_connector_best_encoder,
 };
 
index edec31fe3fed865aa2669e7c8e17aec912a82058..68175b54504bf1518b294402b824666c19c60b6f 100644 (file)
@@ -363,7 +363,7 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
                list->master = dev->primary->master;
        *maplist = list;
        return 0;
-       }
+}
 
 int drm_addmap(struct drm_device * dev, resource_size_t offset,
               unsigned int size, enum drm_map_type type,
@@ -656,13 +656,13 @@ int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request)
                DRM_DEBUG("zone invalid\n");
                return -EINVAL;
        }
-       spin_lock(&dev->count_lock);
+       spin_lock(&dev->buf_lock);
        if (dev->buf_use) {
-               spin_unlock(&dev->count_lock);
+               spin_unlock(&dev->buf_lock);
                return -EBUSY;
        }
        atomic_inc(&dev->buf_alloc);
-       spin_unlock(&dev->count_lock);
+       spin_unlock(&dev->buf_lock);
 
        mutex_lock(&dev->struct_mutex);
        entry = &dma->bufs[order];
@@ -805,13 +805,13 @@ int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request)
        page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
        total = PAGE_SIZE << page_order;
 
-       spin_lock(&dev->count_lock);
+       spin_lock(&dev->buf_lock);
        if (dev->buf_use) {
-               spin_unlock(&dev->count_lock);
+               spin_unlock(&dev->buf_lock);
                return -EBUSY;
        }
        atomic_inc(&dev->buf_alloc);
-       spin_unlock(&dev->count_lock);
+       spin_unlock(&dev->buf_lock);
 
        mutex_lock(&dev->struct_mutex);
        entry = &dma->bufs[order];
@@ -1015,13 +1015,13 @@ static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request
        if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
                return -EINVAL;
 
-       spin_lock(&dev->count_lock);
+       spin_lock(&dev->buf_lock);
        if (dev->buf_use) {
-               spin_unlock(&dev->count_lock);
+               spin_unlock(&dev->buf_lock);
                return -EBUSY;
        }
        atomic_inc(&dev->buf_alloc);
-       spin_unlock(&dev->count_lock);
+       spin_unlock(&dev->buf_lock);
 
        mutex_lock(&dev->struct_mutex);
        entry = &dma->bufs[order];
@@ -1175,7 +1175,7 @@ int drm_addbufs(struct drm_device *dev, void *data,
  * \param arg pointer to a drm_buf_info structure.
  * \return zero on success or a negative number on failure.
  *
- * Increments drm_device::buf_use while holding the drm_device::count_lock
+ * Increments drm_device::buf_use while holding the drm_device::buf_lock
  * lock, preventing of allocating more buffers after this call. Information
  * about each requested buffer is then copied into user space.
  */
@@ -1196,13 +1196,13 @@ int drm_infobufs(struct drm_device *dev, void *data,
        if (!dma)
                return -EINVAL;
 
-       spin_lock(&dev->count_lock);
+       spin_lock(&dev->buf_lock);
        if (atomic_read(&dev->buf_alloc)) {
-               spin_unlock(&dev->count_lock);
+               spin_unlock(&dev->buf_lock);
                return -EBUSY;
        }
        ++dev->buf_use;         /* Can't allocate more after this call */
-       spin_unlock(&dev->count_lock);
+       spin_unlock(&dev->buf_lock);
 
        for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
                if (dma->bufs[i].buf_count)
@@ -1381,13 +1381,13 @@ int drm_mapbufs(struct drm_device *dev, void *data,
        if (!dma)
                return -EINVAL;
 
-       spin_lock(&dev->count_lock);
+       spin_lock(&dev->buf_lock);
        if (atomic_read(&dev->buf_alloc)) {
-               spin_unlock(&dev->count_lock);
+               spin_unlock(&dev->buf_lock);
                return -EBUSY;
        }
        dev->buf_use++;         /* Can't allocate more after this call */
-       spin_unlock(&dev->count_lock);
+       spin_unlock(&dev->buf_lock);
 
        if (request->count >= dma->buf_count) {
                if ((dev->agp && (dma->flags & _DRM_DMA_USE_AGP))
index 534cb89b160d686d60218dad53082be9b0f331ea..a6b690626a6b0563e026e9217859cc2505aadb76 100644 (file)
@@ -131,14 +131,14 @@ drm_clflush_sg(struct sg_table *st)
 EXPORT_SYMBOL(drm_clflush_sg);
 
 void
-drm_clflush_virt_range(char *addr, unsigned long length)
+drm_clflush_virt_range(void *addr, unsigned long length)
 {
 #if defined(CONFIG_X86)
        if (cpu_has_clflush) {
-               char *end = addr + length;
+               void *end = addr + length;
                mb();
                for (; addr < end; addr += boot_cpu_data.x86_clflush_size)
-                       clflush(addr);
+                       clflushopt(addr);
                clflushopt(end - 1);
                mb();
                return;
index d8b7099abece7b9ccc44ee21550aa67423855f3f..fe94cc10cd350f8dcf06f989f7f131fb392771a9 100644 (file)
@@ -37,6 +37,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_modeset_lock.h>
 
 #include "drm_crtc_internal.h"
 
  */
 void drm_modeset_lock_all(struct drm_device *dev)
 {
-       struct drm_crtc *crtc;
+       struct drm_mode_config *config = &dev->mode_config;
+       struct drm_modeset_acquire_ctx *ctx;
+       int ret;
 
-       mutex_lock(&dev->mode_config.mutex);
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (WARN_ON(!ctx))
+               return;
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-               mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);
+       mutex_lock(&config->mutex);
+
+       drm_modeset_acquire_init(ctx, 0);
+
+retry:
+       ret = drm_modeset_lock(&config->connection_mutex, ctx);
+       if (ret)
+               goto fail;
+       ret = drm_modeset_lock_all_crtcs(dev, ctx);
+       if (ret)
+               goto fail;
+
+       WARN_ON(config->acquire_ctx);
+
+       /* now we hold the locks, so now that it is safe, stash the
+        * ctx for drm_modeset_unlock_all():
+        */
+       config->acquire_ctx = ctx;
+
+       drm_warn_on_modeset_not_all_locked(dev);
+
+       return;
+
+fail:
+       if (ret == -EDEADLK) {
+               drm_modeset_backoff(ctx);
+               goto retry;
+       }
 }
 EXPORT_SYMBOL(drm_modeset_lock_all);
 
@@ -67,10 +98,17 @@ EXPORT_SYMBOL(drm_modeset_lock_all);
  */
 void drm_modeset_unlock_all(struct drm_device *dev)
 {
-       struct drm_crtc *crtc;
+       struct drm_mode_config *config = &dev->mode_config;
+       struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx;
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-               mutex_unlock(&crtc->mutex);
+       if (WARN_ON(!ctx))
+               return;
+
+       config->acquire_ctx = NULL;
+       drm_modeset_drop_locks(ctx);
+       drm_modeset_acquire_fini(ctx);
+
+       kfree(ctx);
 
        mutex_unlock(&dev->mode_config.mutex);
 }
@@ -91,8 +129,9 @@ void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
                return;
 
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-               WARN_ON(!mutex_is_locked(&crtc->mutex));
+               WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
 
+       WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
        WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
 }
 EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
@@ -227,6 +266,7 @@ static const struct drm_prop_enum_list drm_encoder_enum_list[] =
        { DRM_MODE_ENCODER_TVDAC, "TV" },
        { DRM_MODE_ENCODER_VIRTUAL, "Virtual" },
        { DRM_MODE_ENCODER_DSI, "DSI" },
+       { DRM_MODE_ENCODER_DPMST, "DP MST" },
 };
 
 static const struct drm_prop_enum_list drm_subpixel_enum_list[] =
@@ -255,46 +295,6 @@ void drm_connector_ida_destroy(void)
                ida_destroy(&drm_connector_enum_list[i].ida);
 }
 
-/**
- * drm_get_encoder_name - return a string for encoder
- * @encoder: encoder to compute name of
- *
- * Note that the buffer used by this function is globally shared and owned by
- * the function itself.
- *
- * FIXME: This isn't really multithreading safe.
- */
-const char *drm_get_encoder_name(const struct drm_encoder *encoder)
-{
-       static char buf[32];
-
-       snprintf(buf, 32, "%s-%d",
-                drm_encoder_enum_list[encoder->encoder_type].name,
-                encoder->base.id);
-       return buf;
-}
-EXPORT_SYMBOL(drm_get_encoder_name);
-
-/**
- * drm_get_connector_name - return a string for connector
- * @connector: connector to compute name of
- *
- * Note that the buffer used by this function is globally shared and owned by
- * the function itself.
- *
- * FIXME: This isn't really multithreading safe.
- */
-const char *drm_get_connector_name(const struct drm_connector *connector)
-{
-       static char buf[32];
-
-       snprintf(buf, 32, "%s-%d",
-                drm_connector_enum_list[connector->connector_type].name,
-                connector->connector_type_id);
-       return buf;
-}
-EXPORT_SYMBOL(drm_get_connector_name);
-
 /**
  * drm_get_connector_status_name - return a string for connector status
  * @status: connector status to compute name of
@@ -409,6 +409,21 @@ void drm_mode_object_put(struct drm_device *dev,
        mutex_unlock(&dev->mode_config.idr_mutex);
 }
 
+static struct drm_mode_object *_object_find(struct drm_device *dev,
+               uint32_t id, uint32_t type)
+{
+       struct drm_mode_object *obj = NULL;
+
+       mutex_lock(&dev->mode_config.idr_mutex);
+       obj = idr_find(&dev->mode_config.crtc_idr, id);
+       if (!obj || (type != DRM_MODE_OBJECT_ANY && obj->type != type) ||
+           (obj->id != id))
+               obj = NULL;
+       mutex_unlock(&dev->mode_config.idr_mutex);
+
+       return obj;
+}
+
 /**
  * drm_mode_object_find - look up a drm object with static lifetime
  * @dev: drm device
@@ -416,7 +431,9 @@ void drm_mode_object_put(struct drm_device *dev,
  * @type: type of the mode object
  *
  * Note that framebuffers cannot be looked up with this functions - since those
- * are reference counted, they need special treatment.
+ * are reference counted, they need special treatment.  Even with
+ * DRM_MODE_OBJECT_ANY (although that will simply return NULL
+ * rather than WARN_ON()).
  */
 struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
                uint32_t id, uint32_t type)
@@ -426,13 +443,10 @@ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
        /* Framebuffers are reference counted and need their own lookup
         * function.*/
        WARN_ON(type == DRM_MODE_OBJECT_FB);
-
-       mutex_lock(&dev->mode_config.idr_mutex);
-       obj = idr_find(&dev->mode_config.crtc_idr, id);
-       if (!obj || (obj->type != type) || (obj->id != id))
+       obj = _object_find(dev, id, type);
+       /* don't leak out unref'd fb's */
+       if (obj && (obj->type == DRM_MODE_OBJECT_FB))
                obj = NULL;
-       mutex_unlock(&dev->mode_config.idr_mutex);
-
        return obj;
 }
 EXPORT_SYMBOL(drm_mode_object_find);
@@ -538,7 +552,7 @@ EXPORT_SYMBOL(drm_framebuffer_lookup);
  */
 void drm_framebuffer_unreference(struct drm_framebuffer *fb)
 {
-       DRM_DEBUG("FB ID: %d\n", fb->base.id);
+       DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount));
        kref_put(&fb->refcount, drm_framebuffer_free);
 }
 EXPORT_SYMBOL(drm_framebuffer_unreference);
@@ -551,7 +565,7 @@ EXPORT_SYMBOL(drm_framebuffer_unreference);
  */
 void drm_framebuffer_reference(struct drm_framebuffer *fb)
 {
-       DRM_DEBUG("FB ID: %d\n", fb->base.id);
+       DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount));
        kref_get(&fb->refcount);
 }
 EXPORT_SYMBOL(drm_framebuffer_reference);
@@ -563,7 +577,7 @@ static void drm_framebuffer_free_bug(struct kref *kref)
 
 static void __drm_framebuffer_unreference(struct drm_framebuffer *fb)
 {
-       DRM_DEBUG("FB ID: %d\n", fb->base.id);
+       DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount));
        kref_put(&fb->refcount, drm_framebuffer_free_bug);
 }
 
@@ -691,6 +705,8 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
 }
 EXPORT_SYMBOL(drm_framebuffer_remove);
 
+DEFINE_WW_CLASS(crtc_ww_class);
+
 /**
  * drm_crtc_init_with_planes - Initialise a new CRTC object with
  *    specified primary and cursor planes.
@@ -710,6 +726,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
                              void *cursor,
                              const struct drm_crtc_funcs *funcs)
 {
+       struct drm_mode_config *config = &dev->mode_config;
        int ret;
 
        crtc->dev = dev;
@@ -717,8 +734,9 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
        crtc->invert_dimensions = false;
 
        drm_modeset_lock_all(dev);
-       mutex_init(&crtc->mutex);
-       mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);
+       drm_modeset_lock_init(&crtc->mutex);
+       /* dropped by _unlock_all(): */
+       drm_modeset_lock(&crtc->mutex, config->acquire_ctx);
 
        ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
        if (ret)
@@ -726,8 +744,8 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
 
        crtc->base.properties = &crtc->properties;
 
-       list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
-       dev->mode_config.num_crtc++;
+       list_add_tail(&crtc->head, &config->crtc_list);
+       config->num_crtc++;
 
        crtc->primary = primary;
        if (primary)
@@ -755,6 +773,8 @@ void drm_crtc_cleanup(struct drm_crtc *crtc)
        kfree(crtc->gamma_store);
        crtc->gamma_store = NULL;
 
+       drm_modeset_lock_fini(&crtc->mutex);
+
        drm_mode_object_put(dev, &crtc->base);
        list_del(&crtc->head);
        dev->mode_config.num_crtc--;
@@ -824,7 +844,7 @@ int drm_connector_init(struct drm_device *dev,
 
        ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR);
        if (ret)
-               goto out;
+               goto out_unlock;
 
        connector->base.properties = &connector->properties;
        connector->dev = dev;
@@ -834,9 +854,17 @@ int drm_connector_init(struct drm_device *dev,
                ida_simple_get(connector_ida, 1, 0, GFP_KERNEL);
        if (connector->connector_type_id < 0) {
                ret = connector->connector_type_id;
-               drm_mode_object_put(dev, &connector->base);
-               goto out;
+               goto out_put;
+       }
+       connector->name =
+               kasprintf(GFP_KERNEL, "%s-%d",
+                         drm_connector_enum_list[connector_type].name,
+                         connector->connector_type_id);
+       if (!connector->name) {
+               ret = -ENOMEM;
+               goto out_put;
        }
+
        INIT_LIST_HEAD(&connector->probed_modes);
        INIT_LIST_HEAD(&connector->modes);
        connector->edid_blob_ptr = NULL;
@@ -853,7 +881,11 @@ int drm_connector_init(struct drm_device *dev,
        drm_object_attach_property(&connector->base,
                                      dev->mode_config.dpms_property, 0);
 
- out:
+out_put:
+       if (ret)
+               drm_mode_object_put(dev, &connector->base);
+
+out_unlock:
        drm_modeset_unlock_all(dev);
 
        return ret;
@@ -881,6 +913,8 @@ void drm_connector_cleanup(struct drm_connector *connector)
                   connector->connector_type_id);
 
        drm_mode_object_put(dev, &connector->base);
+       kfree(connector->name);
+       connector->name = NULL;
        list_del(&connector->head);
        dev->mode_config.num_connector--;
 }
@@ -982,16 +1016,27 @@ int drm_encoder_init(struct drm_device *dev,
 
        ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
        if (ret)
-               goto out;
+               goto out_unlock;
 
        encoder->dev = dev;
        encoder->encoder_type = encoder_type;
        encoder->funcs = funcs;
+       encoder->name = kasprintf(GFP_KERNEL, "%s-%d",
+                                 drm_encoder_enum_list[encoder_type].name,
+                                 encoder->base.id);
+       if (!encoder->name) {
+               ret = -ENOMEM;
+               goto out_put;
+       }
 
        list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
        dev->mode_config.num_encoder++;
 
- out:
+out_put:
+       if (ret)
+               drm_mode_object_put(dev, &encoder->base);
+
+out_unlock:
        drm_modeset_unlock_all(dev);
 
        return ret;
@@ -1009,6 +1054,8 @@ void drm_encoder_cleanup(struct drm_encoder *encoder)
        struct drm_device *dev = encoder->dev;
        drm_modeset_lock_all(dev);
        drm_mode_object_put(dev, &encoder->base);
+       kfree(encoder->name);
+       encoder->name = NULL;
        list_del(&encoder->head);
        dev->mode_config.num_encoder--;
        drm_modeset_unlock_all(dev);
@@ -1145,16 +1192,19 @@ EXPORT_SYMBOL(drm_plane_cleanup);
  */
 void drm_plane_force_disable(struct drm_plane *plane)
 {
+       struct drm_framebuffer *old_fb = plane->fb;
        int ret;
 
-       if (!plane->fb)
+       if (!old_fb)
                return;
 
        ret = plane->funcs->disable_plane(plane);
-       if (ret)
+       if (ret) {
                DRM_ERROR("failed to disable plane with busy fb\n");
+               return;
+       }
        /* disconnect the plane from the fb and crtc: */
-       __drm_framebuffer_unreference(plane->fb);
+       __drm_framebuffer_unreference(old_fb);
        plane->fb = NULL;
        plane->crtc = NULL;
 }
@@ -1378,6 +1428,12 @@ static int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *gr
        return 0;
 }
 
+void drm_mode_group_destroy(struct drm_mode_group *group)
+{
+       kfree(group->id_list);
+       group->id_list = NULL;
+}
+
 /*
  * NOTE: Driver's shouldn't ever call drm_mode_group_init_legacy_group - it is
  * the drm core's responsibility to set up mode control groups.
@@ -1614,7 +1670,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
                                            &dev->mode_config.encoder_list,
                                            head) {
                                DRM_DEBUG_KMS("[ENCODER:%d:%s]\n", encoder->base.id,
-                                               drm_get_encoder_name(encoder));
+                                               encoder->name);
                                if (put_user(encoder->base.id, encoder_id +
                                             copied)) {
                                        ret = -EFAULT;
@@ -1646,7 +1702,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
                                            head) {
                                DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
                                        connector->base.id,
-                                       drm_get_connector_name(connector));
+                                       connector->name);
                                if (put_user(connector->base.id,
                                             connector_id + copied)) {
                                        ret = -EFAULT;
@@ -1695,7 +1751,6 @@ int drm_mode_getcrtc(struct drm_device *dev,
 {
        struct drm_mode_crtc *crtc_resp = data;
        struct drm_crtc *crtc;
-       struct drm_mode_object *obj;
        int ret = 0;
 
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
@@ -1703,13 +1758,11 @@ int drm_mode_getcrtc(struct drm_device *dev,
 
        drm_modeset_lock_all(dev);
 
-       obj = drm_mode_object_find(dev, crtc_resp->crtc_id,
-                                  DRM_MODE_OBJECT_CRTC);
-       if (!obj) {
+       crtc = drm_crtc_find(dev, crtc_resp->crtc_id);
+       if (!crtc) {
                ret = -ENOENT;
                goto out;
        }
-       crtc = obj_to_crtc(obj);
 
        crtc_resp->x = crtc->x;
        crtc_resp->y = crtc->y;
@@ -1763,7 +1816,6 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
                          struct drm_file *file_priv)
 {
        struct drm_mode_get_connector *out_resp = data;
-       struct drm_mode_object *obj;
        struct drm_connector *connector;
        struct drm_display_mode *mode;
        int mode_count = 0;
@@ -1787,13 +1839,11 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
 
        mutex_lock(&dev->mode_config.mutex);
 
-       obj = drm_mode_object_find(dev, out_resp->connector_id,
-                                  DRM_MODE_OBJECT_CONNECTOR);
-       if (!obj) {
+       connector = drm_connector_find(dev, out_resp->connector_id);
+       if (!connector) {
                ret = -ENOENT;
                goto out;
        }
-       connector = obj_to_connector(obj);
 
        props_count = connector->properties.count;
 
@@ -1821,10 +1871,12 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
        out_resp->mm_height = connector->display_info.height_mm;
        out_resp->subpixel = connector->display_info.subpixel_order;
        out_resp->connection = connector->status;
+       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
        if (connector->encoder)
                out_resp->encoder_id = connector->encoder->base.id;
        else
                out_resp->encoder_id = 0;
+       drm_modeset_unlock(&dev->mode_config.connection_mutex);
 
        /*
         * This ioctl is called twice, once to determine how much space is
@@ -1908,7 +1960,6 @@ int drm_mode_getencoder(struct drm_device *dev, void *data,
                        struct drm_file *file_priv)
 {
        struct drm_mode_get_encoder *enc_resp = data;
-       struct drm_mode_object *obj;
        struct drm_encoder *encoder;
        int ret = 0;
 
@@ -1916,13 +1967,11 @@ int drm_mode_getencoder(struct drm_device *dev, void *data,
                return -EINVAL;
 
        drm_modeset_lock_all(dev);
-       obj = drm_mode_object_find(dev, enc_resp->encoder_id,
-                                  DRM_MODE_OBJECT_ENCODER);
-       if (!obj) {
+       encoder = drm_encoder_find(dev, enc_resp->encoder_id);
+       if (!encoder) {
                ret = -ENOENT;
                goto out;
        }
-       encoder = obj_to_encoder(obj);
 
        if (encoder->crtc)
                enc_resp->crtc_id = encoder->crtc->base.id;
@@ -2020,7 +2069,6 @@ int drm_mode_getplane(struct drm_device *dev, void *data,
                      struct drm_file *file_priv)
 {
        struct drm_mode_get_plane *plane_resp = data;
-       struct drm_mode_object *obj;
        struct drm_plane *plane;
        uint32_t __user *format_ptr;
        int ret = 0;
@@ -2029,13 +2077,11 @@ int drm_mode_getplane(struct drm_device *dev, void *data,
                return -EINVAL;
 
        drm_modeset_lock_all(dev);
-       obj = drm_mode_object_find(dev, plane_resp->plane_id,
-                                  DRM_MODE_OBJECT_PLANE);
-       if (!obj) {
+       plane = drm_plane_find(dev, plane_resp->plane_id);
+       if (!plane) {
                ret = -ENOENT;
                goto out;
        }
-       plane = obj_to_plane(obj);
 
        if (plane->crtc)
                plane_resp->crtc_id = plane->crtc->base.id;
@@ -2088,7 +2134,6 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
                      struct drm_file *file_priv)
 {
        struct drm_mode_set_plane *plane_req = data;
-       struct drm_mode_object *obj;
        struct drm_plane *plane;
        struct drm_crtc *crtc;
        struct drm_framebuffer *fb = NULL, *old_fb = NULL;
@@ -2103,35 +2148,42 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
         * First, find the plane, crtc, and fb objects.  If not available,
         * we don't bother to call the driver.
         */
-       obj = drm_mode_object_find(dev, plane_req->plane_id,
-                                  DRM_MODE_OBJECT_PLANE);
-       if (!obj) {
+       plane = drm_plane_find(dev, plane_req->plane_id);
+       if (!plane) {
                DRM_DEBUG_KMS("Unknown plane ID %d\n",
                              plane_req->plane_id);
                return -ENOENT;
        }
-       plane = obj_to_plane(obj);
 
        /* No fb means shut it down */
        if (!plane_req->fb_id) {
                drm_modeset_lock_all(dev);
                old_fb = plane->fb;
-               plane->funcs->disable_plane(plane);
-               plane->crtc = NULL;
-               plane->fb = NULL;
+               ret = plane->funcs->disable_plane(plane);
+               if (!ret) {
+                       plane->crtc = NULL;
+                       plane->fb = NULL;
+               } else {
+                       old_fb = NULL;
+               }
                drm_modeset_unlock_all(dev);
                goto out;
        }
 
-       obj = drm_mode_object_find(dev, plane_req->crtc_id,
-                                  DRM_MODE_OBJECT_CRTC);
-       if (!obj) {
+       crtc = drm_crtc_find(dev, plane_req->crtc_id);
+       if (!crtc) {
                DRM_DEBUG_KMS("Unknown crtc ID %d\n",
                              plane_req->crtc_id);
                ret = -ENOENT;
                goto out;
        }
-       crtc = obj_to_crtc(obj);
+
+       /* Check whether this plane is usable on this CRTC */
+       if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) {
+               DRM_DEBUG_KMS("Invalid crtc for plane\n");
+               ret = -EINVAL;
+               goto out;
+       }
 
        fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
        if (!fb) {
@@ -2187,16 +2239,18 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
        }
 
        drm_modeset_lock_all(dev);
+       old_fb = plane->fb;
        ret = plane->funcs->update_plane(plane, crtc, fb,
                                         plane_req->crtc_x, plane_req->crtc_y,
                                         plane_req->crtc_w, plane_req->crtc_h,
                                         plane_req->src_x, plane_req->src_y,
                                         plane_req->src_w, plane_req->src_h);
        if (!ret) {
-               old_fb = plane->fb;
                plane->crtc = crtc;
                plane->fb = fb;
                fb = NULL;
+       } else {
+               old_fb = NULL;
        }
        drm_modeset_unlock_all(dev);
 
@@ -2239,9 +2293,7 @@ int drm_mode_set_config_internal(struct drm_mode_set *set)
        ret = crtc->funcs->set_config(set);
        if (ret == 0) {
                crtc->primary->crtc = crtc;
-
-               /* crtc->fb must be updated by ->set_config, enforces this. */
-               WARN_ON(fb != crtc->primary->fb);
+               crtc->primary->fb = fb;
        }
 
        list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
@@ -2318,7 +2370,6 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
 {
        struct drm_mode_config *config = &dev->mode_config;
        struct drm_mode_crtc *crtc_req = data;
-       struct drm_mode_object *obj;
        struct drm_crtc *crtc;
        struct drm_connector **connector_set = NULL, *connector;
        struct drm_framebuffer *fb = NULL;
@@ -2336,14 +2387,12 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
                return -ERANGE;
 
        drm_modeset_lock_all(dev);
-       obj = drm_mode_object_find(dev, crtc_req->crtc_id,
-                                  DRM_MODE_OBJECT_CRTC);
-       if (!obj) {
+       crtc = drm_crtc_find(dev, crtc_req->crtc_id);
+       if (!crtc) {
                DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
                ret = -ENOENT;
                goto out;
        }
-       crtc = obj_to_crtc(obj);
        DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
 
        if (crtc_req->mode_valid) {
@@ -2426,18 +2475,16 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
                                goto out;
                        }
 
-                       obj = drm_mode_object_find(dev, out_id,
-                                                  DRM_MODE_OBJECT_CONNECTOR);
-                       if (!obj) {
+                       connector = drm_connector_find(dev, out_id);
+                       if (!connector) {
                                DRM_DEBUG_KMS("Connector id %d unknown\n",
                                                out_id);
                                ret = -ENOENT;
                                goto out;
                        }
-                       connector = obj_to_connector(obj);
                        DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
                                        connector->base.id,
-                                       drm_get_connector_name(connector));
+                                       connector->name);
 
                        connector_set[i] = connector;
                }
@@ -2466,7 +2513,6 @@ static int drm_mode_cursor_common(struct drm_device *dev,
                                  struct drm_mode_cursor2 *req,
                                  struct drm_file *file_priv)
 {
-       struct drm_mode_object *obj;
        struct drm_crtc *crtc;
        int ret = 0;
 
@@ -2476,14 +2522,13 @@ static int drm_mode_cursor_common(struct drm_device *dev,
        if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags))
                return -EINVAL;
 
-       obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC);
-       if (!obj) {
+       crtc = drm_crtc_find(dev, req->crtc_id);
+       if (!crtc) {
                DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id);
                return -ENOENT;
        }
-       crtc = obj_to_crtc(obj);
 
-       mutex_lock(&crtc->mutex);
+       drm_modeset_lock(&crtc->mutex, NULL);
        if (req->flags & DRM_MODE_CURSOR_BO) {
                if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {
                        ret = -ENXIO;
@@ -2507,7 +2552,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
                }
        }
 out:
-       mutex_unlock(&crtc->mutex);
+       drm_modeset_unlock(&crtc->mutex);
 
        return ret;
 
@@ -3097,6 +3142,8 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
        if (!property)
                return NULL;
 
+       property->dev = dev;
+
        if (num_values) {
                property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL);
                if (!property->values)
@@ -3117,6 +3164,9 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
        }
 
        list_add_tail(&property->head, &dev->mode_config.property_list);
+
+       WARN_ON(!drm_property_type_valid(property));
+
        return property;
 fail:
        kfree(property->values);
@@ -3217,6 +3267,22 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drm_property_create_bitmask);
 
+static struct drm_property *property_create_range(struct drm_device *dev,
+                                        int flags, const char *name,
+                                        uint64_t min, uint64_t max)
+{
+       struct drm_property *property;
+
+       property = drm_property_create(dev, flags, name, 2);
+       if (!property)
+               return NULL;
+
+       property->values[0] = min;
+       property->values[1] = max;
+
+       return property;
+}
+
 /**
  * drm_property_create - create a new ranged property type
  * @dev: drm device
@@ -3238,21 +3304,37 @@ EXPORT_SYMBOL(drm_property_create_bitmask);
 struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
                                         const char *name,
                                         uint64_t min, uint64_t max)
+{
+       return property_create_range(dev, DRM_MODE_PROP_RANGE | flags,
+                       name, min, max);
+}
+EXPORT_SYMBOL(drm_property_create_range);
+
+struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
+                                        int flags, const char *name,
+                                        int64_t min, int64_t max)
+{
+       return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags,
+                       name, I642U64(min), I642U64(max));
+}
+EXPORT_SYMBOL(drm_property_create_signed_range);
+
+struct drm_property *drm_property_create_object(struct drm_device *dev,
+                                        int flags, const char *name, uint32_t type)
 {
        struct drm_property *property;
 
-       flags |= DRM_MODE_PROP_RANGE;
+       flags |= DRM_MODE_PROP_OBJECT;
 
-       property = drm_property_create(dev, flags, name, 2);
+       property = drm_property_create(dev, flags, name, 1);
        if (!property)
                return NULL;
 
-       property->values[0] = min;
-       property->values[1] = max;
+       property->values[0] = type;
 
        return property;
 }
-EXPORT_SYMBOL(drm_property_create_range);
+EXPORT_SYMBOL(drm_property_create_object);
 
 /**
  * drm_property_add_enum - add a possible value to an enumeration property
@@ -3274,14 +3356,16 @@ int drm_property_add_enum(struct drm_property *property, int index,
 {
        struct drm_property_enum *prop_enum;
 
-       if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)))
+       if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
+                       drm_property_type_is(property, DRM_MODE_PROP_BITMASK)))
                return -EINVAL;
 
        /*
         * Bitmask enum properties have the additional constraint of values
         * from 0 to 63
         */
-       if ((property->flags & DRM_MODE_PROP_BITMASK) && (value > 63))
+       if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) &&
+                       (value > 63))
                return -EINVAL;
 
        if (!list_empty(&property->enum_blob_list)) {
@@ -3438,7 +3522,6 @@ EXPORT_SYMBOL(drm_object_property_get_value);
 int drm_mode_getproperty_ioctl(struct drm_device *dev,
                               void *data, struct drm_file *file_priv)
 {
-       struct drm_mode_object *obj;
        struct drm_mode_get_property *out_resp = data;
        struct drm_property *property;
        int enum_count = 0;
@@ -3457,17 +3540,17 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
                return -EINVAL;
 
        drm_modeset_lock_all(dev);
-       obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY);
-       if (!obj) {
+       property = drm_property_find(dev, out_resp->prop_id);
+       if (!property) {
                ret = -ENOENT;
                goto done;
        }
-       property = obj_to_property(obj);
 
-       if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
+       if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
+                       drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
                list_for_each_entry(prop_enum, &property->enum_blob_list, head)
                        enum_count++;
-       } else if (property->flags & DRM_MODE_PROP_BLOB) {
+       } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
                list_for_each_entry(prop_blob, &property->enum_blob_list, head)
                        blob_count++;
        }
@@ -3489,7 +3572,8 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
        }
        out_resp->count_values = value_count;
 
-       if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
+       if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
+                       drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
                if ((out_resp->count_enum_blobs >= enum_count) && enum_count) {
                        copied = 0;
                        enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr;
@@ -3511,7 +3595,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
                out_resp->count_enum_blobs = enum_count;
        }
 
-       if (property->flags & DRM_MODE_PROP_BLOB) {
+       if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
                if ((out_resp->count_enum_blobs >= blob_count) && blob_count) {
                        copied = 0;
                        blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr;
@@ -3590,7 +3674,6 @@ static void drm_property_destroy_blob(struct drm_device *dev,
 int drm_mode_getblob_ioctl(struct drm_device *dev,
                           void *data, struct drm_file *file_priv)
 {
-       struct drm_mode_object *obj;
        struct drm_mode_get_blob *out_resp = data;
        struct drm_property_blob *blob;
        int ret = 0;
@@ -3600,12 +3683,11 @@ int drm_mode_getblob_ioctl(struct drm_device *dev,
                return -EINVAL;
 
        drm_modeset_lock_all(dev);
-       obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB);
-       if (!obj) {
+       blob = drm_property_blob_find(dev, out_resp->blob_id);
+       if (!blob) {
                ret = -ENOENT;
                goto done;
        }
-       blob = obj_to_blob(obj);
 
        if (out_resp->length == blob->length) {
                blob_ptr = (void __user *)(unsigned long)out_resp->data;
@@ -3667,19 +3749,40 @@ static bool drm_property_change_is_valid(struct drm_property *property,
 {
        if (property->flags & DRM_MODE_PROP_IMMUTABLE)
                return false;
-       if (property->flags & DRM_MODE_PROP_RANGE) {
+
+       if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) {
                if (value < property->values[0] || value > property->values[1])
                        return false;
                return true;
-       } else if (property->flags & DRM_MODE_PROP_BITMASK) {
+       } else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) {
+               int64_t svalue = U642I64(value);
+               if (svalue < U642I64(property->values[0]) ||
+                               svalue > U642I64(property->values[1]))
+                       return false;
+               return true;
+       } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
                int i;
                uint64_t valid_mask = 0;
                for (i = 0; i < property->num_values; i++)
                        valid_mask |= (1ULL << property->values[i]);
                return !(value & ~valid_mask);
-       } else if (property->flags & DRM_MODE_PROP_BLOB) {
+       } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
                /* Only the driver knows */
                return true;
+       } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
+               struct drm_mode_object *obj;
+               /* a zero value for an object property translates to null: */
+               if (value == 0)
+                       return true;
+               /*
+                * NOTE: use _object_find() directly to bypass restriction on
+                * looking up refcnt'd objects (ie. fb's).  For a refcnt'd
+                * object this could race against object finalization, so it
+                * simply tells us that the object *was* valid.  Which is good
+                * enough.
+                */
+               obj = _object_find(property->dev, value, property->values[0]);
+               return obj != NULL;
        } else {
                int i;
                for (i = 0; i < property->num_values; i++)
@@ -3987,7 +4090,6 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,
                             void *data, struct drm_file *file_priv)
 {
        struct drm_mode_crtc_lut *crtc_lut = data;
-       struct drm_mode_object *obj;
        struct drm_crtc *crtc;
        void *r_base, *g_base, *b_base;
        int size;
@@ -3997,12 +4099,11 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,
                return -EINVAL;
 
        drm_modeset_lock_all(dev);
-       obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
-       if (!obj) {
+       crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
+       if (!crtc) {
                ret = -ENOENT;
                goto out;
        }
-       crtc = obj_to_crtc(obj);
 
        if (crtc->funcs->gamma_set == NULL) {
                ret = -ENOSYS;
@@ -4061,7 +4162,6 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev,
                             void *data, struct drm_file *file_priv)
 {
        struct drm_mode_crtc_lut *crtc_lut = data;
-       struct drm_mode_object *obj;
        struct drm_crtc *crtc;
        void *r_base, *g_base, *b_base;
        int size;
@@ -4071,12 +4171,11 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev,
                return -EINVAL;
 
        drm_modeset_lock_all(dev);
-       obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
-       if (!obj) {
+       crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
+       if (!crtc) {
                ret = -ENOENT;
                goto out;
        }
-       crtc = obj_to_crtc(obj);
 
        /* memcpy into gamma store */
        if (crtc_lut->gamma_size != crtc->gamma_size) {
@@ -4129,7 +4228,6 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
                             void *data, struct drm_file *file_priv)
 {
        struct drm_mode_crtc_page_flip *page_flip = data;
-       struct drm_mode_object *obj;
        struct drm_crtc *crtc;
        struct drm_framebuffer *fb = NULL, *old_fb = NULL;
        struct drm_pending_vblank_event *e = NULL;
@@ -4143,12 +4241,11 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
        if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip)
                return -EINVAL;
 
-       obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC);
-       if (!obj)
+       crtc = drm_crtc_find(dev, page_flip->crtc_id);
+       if (!crtc)
                return -ENOENT;
-       crtc = obj_to_crtc(obj);
 
-       mutex_lock(&crtc->mutex);
+       drm_modeset_lock(&crtc->mutex, NULL);
        if (crtc->primary->fb == NULL) {
                /* The framebuffer is currently unbound, presumably
                 * due to a hotplug event, that userspace has not
@@ -4232,7 +4329,7 @@ out:
                drm_framebuffer_unreference(fb);
        if (old_fb)
                drm_framebuffer_unreference(old_fb);
-       mutex_unlock(&crtc->mutex);
+       drm_modeset_unlock(&crtc->mutex);
 
        return ret;
 }
@@ -4597,6 +4694,7 @@ EXPORT_SYMBOL(drm_format_vert_chroma_subsampling);
 void drm_mode_config_init(struct drm_device *dev)
 {
        mutex_init(&dev->mode_config.mutex);
+       drm_modeset_lock_init(&dev->mode_config.connection_mutex);
        mutex_init(&dev->mode_config.idr_mutex);
        mutex_init(&dev->mode_config.fb_lock);
        INIT_LIST_HEAD(&dev->mode_config.fb_list);
@@ -4696,5 +4794,6 @@ void drm_mode_config_cleanup(struct drm_device *dev)
        }
 
        idr_destroy(&dev->mode_config.crtc_idr);
+       drm_modeset_lock_fini(&dev->mode_config.connection_mutex);
 }
 EXPORT_SYMBOL(drm_mode_config_cleanup);
index 872ba11c4533d3be2d120938d34110ccd6b8efa7..78b37f3febd37cb288b529a0136c14a05e4c96f8 100644 (file)
@@ -93,8 +93,10 @@ bool drm_helper_encoder_in_use(struct drm_encoder *encoder)
         * We can expect this mutex to be locked if we are not panicking.
         * Locking is currently fubar in the panic handler.
         */
-       if (!oops_in_progress)
+       if (!oops_in_progress) {
                WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+               WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
+       }
 
        list_for_each_entry(connector, &dev->mode_config.connector_list, head)
                if (connector->encoder == encoder)
@@ -153,20 +155,14 @@ drm_encoder_disable(struct drm_encoder *encoder)
 static void __drm_helper_disable_unused_functions(struct drm_device *dev)
 {
        struct drm_encoder *encoder;
-       struct drm_connector *connector;
        struct drm_crtc *crtc;
 
        drm_warn_on_modeset_not_all_locked(dev);
 
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-               if (!connector->encoder)
-                       continue;
-       }
-
        list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
                if (!drm_helper_encoder_in_use(encoder)) {
                        drm_encoder_disable(encoder);
-                       /* disconnector encoder from any connector */
+                       /* disconnect encoder from any connector */
                        encoder->crtc = NULL;
                }
        }
@@ -349,7 +345,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
                        continue;
 
                DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%d:%s]\n",
-                       encoder->base.id, drm_get_encoder_name(encoder),
+                       encoder->base.id, encoder->name,
                        mode->base.id, mode->name);
                encoder_funcs = encoder->helper_private;
                encoder_funcs->mode_set(encoder, mode, adjusted_mode);
@@ -400,8 +396,7 @@ done:
 }
 EXPORT_SYMBOL(drm_crtc_helper_set_mode);
 
-
-static int
+static void
 drm_crtc_helper_disable(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
@@ -430,7 +425,6 @@ drm_crtc_helper_disable(struct drm_crtc *crtc)
        }
 
        __drm_helper_disable_unused_functions(dev);
-       return 0;
 }
 
 /**
@@ -481,7 +475,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                                (int)set->num_connectors, set->x, set->y);
        } else {
                DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id);
-               return drm_crtc_helper_disable(set->crtc);
+               drm_crtc_helper_disable(set->crtc);
+               return 0;
        }
 
        dev = set->crtc->dev;
@@ -620,11 +615,11 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                }
                if (new_crtc) {
                        DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n",
-                               connector->base.id, drm_get_connector_name(connector),
+                               connector->base.id, connector->name,
                                new_crtc->base.id);
                } else {
                        DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n",
-                               connector->base.id, drm_get_connector_name(connector));
+                               connector->base.id, connector->name);
                }
        }
 
@@ -650,7 +645,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                        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]->name);
                                set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON);
                        }
                }
index 4b6e6f3ba0a19cf10326110897ad665048da29f1..08e33b8b13a433be43b0a39f462791388a856b95 100644 (file)
@@ -206,13 +206,17 @@ i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter)
  * i2c_dp_aux_add_bus() - register an i2c adapter using the aux ch helper
  * @adapter: i2c adapter to register
  *
- * This registers an i2c adapater that uses dp aux channel as it's underlaying
+ * This registers an i2c adapter that uses dp aux channel as it's underlaying
  * transport. The driver needs to fill out the &i2c_algo_dp_aux_data structure
  * and store it in the algo_data member of the @adapter argument. This will be
  * used by the i2c over dp aux algorithm to drive the hardware.
  *
  * RETURNS:
  * 0 on success, -ERRNO on failure.
+ *
+ * IMPORTANT:
+ * This interface is deprecated, please switch to the new dp aux helpers and
+ * drm_dp_aux_register().
  */
 int
 i2c_dp_aux_add_bus(struct i2c_adapter *adapter)
@@ -378,7 +382,10 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
         * transactions.
         */
        for (retry = 0; retry < 7; retry++) {
+
+               mutex_lock(&aux->hw_mutex);
                err = aux->transfer(aux, &msg);
+               mutex_unlock(&aux->hw_mutex);
                if (err < 0) {
                        if (err == -EBUSY)
                                continue;
@@ -592,7 +599,9 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
         * before giving up the AUX transaction.
         */
        for (retry = 0; retry < 7; retry++) {
+               mutex_lock(&aux->hw_mutex);
                err = aux->transfer(aux, msg);
+               mutex_unlock(&aux->hw_mutex);
                if (err < 0) {
                        if (err == -EBUSY)
                                continue;
@@ -725,13 +734,15 @@ static const struct i2c_algorithm drm_dp_i2c_algo = {
 };
 
 /**
- * drm_dp_aux_register_i2c_bus() - register an I2C adapter for I2C-over-AUX
+ * drm_dp_aux_register() - initialise and register aux channel
  * @aux: DisplayPort AUX channel
  *
  * Returns 0 on success or a negative error code on failure.
  */
-int drm_dp_aux_register_i2c_bus(struct drm_dp_aux *aux)
+int drm_dp_aux_register(struct drm_dp_aux *aux)
 {
+       mutex_init(&aux->hw_mutex);
+
        aux->ddc.algo = &drm_dp_i2c_algo;
        aux->ddc.algo_data = aux;
        aux->ddc.retries = 3;
@@ -746,14 +757,14 @@ int drm_dp_aux_register_i2c_bus(struct drm_dp_aux *aux)
 
        return i2c_add_adapter(&aux->ddc);
 }
-EXPORT_SYMBOL(drm_dp_aux_register_i2c_bus);
+EXPORT_SYMBOL(drm_dp_aux_register);
 
 /**
- * drm_dp_aux_unregister_i2c_bus() - unregister an I2C-over-AUX adapter
+ * drm_dp_aux_unregister() - unregister an AUX adapter
  * @aux: DisplayPort AUX channel
  */
-void drm_dp_aux_unregister_i2c_bus(struct drm_dp_aux *aux)
+void drm_dp_aux_unregister(struct drm_dp_aux *aux)
 {
        i2c_del_adapter(&aux->ddc);
 }
-EXPORT_SYMBOL(drm_dp_aux_unregister_i2c_bus);
+EXPORT_SYMBOL(drm_dp_aux_unregister);
index d4e3f9d9370fec545a0cd7c3c8cb93e6d942789b..dfa9769b26b5c57db0731361478639a9600b65b3 100644 (file)
@@ -70,6 +70,8 @@
 #define EDID_QUIRK_FORCE_REDUCED_BLANKING      (1 << 7)
 /* Force 8bpc */
 #define EDID_QUIRK_FORCE_8BPC                  (1 << 8)
+/* Force 12bpc */
+#define EDID_QUIRK_FORCE_12BPC                 (1 << 9)
 
 struct detailed_mode_closure {
        struct drm_connector *connector;
@@ -125,6 +127,9 @@ static struct edid_quirk {
        { "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 },
        { "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 },
 
+       /* Sony PVM-2541A does up to 12 bpc, but only reports max 8 bpc */
+       { "SNY", 0x2541, EDID_QUIRK_FORCE_12BPC },
+
        /* ViewSonic VA2026w */
        { "VSC", 5020, EDID_QUIRK_FORCE_REDUCED_BLANKING },
 
@@ -984,9 +989,13 @@ static const u8 edid_header[] = {
        0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
 };
 
- /*
- * Sanity check the header of the base EDID block.  Return 8 if the header
- * is perfect, down to 0 if it's totally wrong.
+/**
+ * drm_edid_header_is_valid - sanity check the header of the base EDID block
+ * @raw_edid: pointer to raw base EDID block
+ *
+ * Sanity check the header of the base EDID block.
+ *
+ * Return: 8 if the header is perfect, down to 0 if it's totally wrong.
  */
 int drm_edid_header_is_valid(const u8 *raw_edid)
 {
@@ -1005,9 +1014,16 @@ module_param_named(edid_fixup, edid_fixup, int, 0400);
 MODULE_PARM_DESC(edid_fixup,
                 "Minimum number of valid EDID header bytes (0-8, default 6)");
 
-/*
- * Sanity check the EDID block (base or extension).  Return 0 if the block
- * doesn't check out, or 1 if it's valid.
+/**
+ * drm_edid_block_valid - Sanity check the EDID block (base or extension)
+ * @raw_edid: pointer to raw EDID block
+ * @block: type of block to validate (0 for base, extension otherwise)
+ * @print_bad_edid: if true, dump bad EDID blocks to the console
+ *
+ * Validate a base or extension EDID block and optionally dump bad blocks to
+ * the console.
+ *
+ * Return: True if the block is valid, false otherwise.
  */
 bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid)
 {
@@ -1077,6 +1093,8 @@ EXPORT_SYMBOL(drm_edid_block_valid);
  * @edid: EDID data
  *
  * Sanity-check an entire EDID record (including extensions)
+ *
+ * Return: True if the EDID data is valid, false otherwise.
  */
 bool drm_edid_is_valid(struct edid *edid)
 {
@@ -1096,18 +1114,15 @@ EXPORT_SYMBOL(drm_edid_is_valid);
 
 #define DDC_SEGMENT_ADDR 0x30
 /**
- * Get EDID information via I2C.
- *
- * @adapter : i2c device adaptor
+ * drm_do_probe_ddc_edid() - get EDID information via I2C
+ * @adapter: I2C device adaptor
  * @buf: EDID data buffer to be filled
  * @block: 128 byte EDID block to start fetching from
  * @len: EDID data buffer length to fetch
  *
- * Returns:
- *
- * 0 on success or -1 on failure.
+ * Try to fetch EDID information by calling I2C driver functions.
  *
- * Try to fetch EDID information by calling i2c driver function.
+ * Return: 0 on success or -1 on failure.
  */
 static int
 drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf,
@@ -1118,7 +1133,8 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf,
        unsigned char xfers = segment ? 3 : 2;
        int ret, retries = 5;
 
-       /* The core i2c driver will automatically retry the transfer if the
+       /*
+        * The core I2C driver will automatically retry the transfer if the
         * adapter reports EAGAIN. However, we find that bit-banging transfers
         * are susceptible to errors under a heavily loaded machine and
         * generate spurious NAKs and timeouts. Retrying the transfer
@@ -1144,10 +1160,10 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf,
                        }
                };
 
-       /*
-        * Avoid sending the segment addr to not upset non-compliant ddc
-        * monitors.
-        */
+               /*
+                * Avoid sending the segment addr to not upset non-compliant
+                * DDC monitors.
+                */
                ret = i2c_transfer(adapter, &msgs[3 - xfers], xfers);
 
                if (ret == -ENXIO) {
@@ -1216,7 +1232,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
                if (i == 4 && print_bad_edid) {
                        dev_warn(connector->dev->dev,
                         "%s: Ignoring invalid EDID block %d.\n",
-                        drm_get_connector_name(connector), j);
+                        connector->name, j);
 
                        connector->bad_edid_counter++;
                }
@@ -1236,7 +1252,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
 carp:
        if (print_bad_edid) {
                dev_warn(connector->dev->dev, "%s: EDID block %d invalid.\n",
-                        drm_get_connector_name(connector), j);
+                        connector->name, j);
        }
        connector->bad_edid_counter++;
 
@@ -1246,12 +1262,10 @@ out:
 }
 
 /**
- * Probe DDC presence.
- * @adapter: i2c adapter to probe
+ * drm_probe_ddc() - probe DDC presence
+ * @adapter: I2C adapter to probe
  *
- * Returns:
- *
- * 1 on success
+ * Return: True on success, false on failure.
  */
 bool
 drm_probe_ddc(struct i2c_adapter *adapter)
@@ -1265,12 +1279,12 @@ EXPORT_SYMBOL(drm_probe_ddc);
 /**
  * drm_get_edid - get EDID data, if available
  * @connector: connector we're probing
- * @adapter: i2c adapter to use for DDC
+ * @adapter: I2C adapter to use for DDC
  *
- * Poke the given i2c channel to grab EDID data if possible.  If found,
+ * Poke the given I2C channel to grab EDID data if possible.  If found,
  * attach it to the connector.
  *
- * Return edid data or NULL if we couldn't find any.
+ * Return: Pointer to valid EDID or NULL if we couldn't find any.
  */
 struct edid *drm_get_edid(struct drm_connector *connector,
                          struct i2c_adapter *adapter)
@@ -1288,7 +1302,7 @@ EXPORT_SYMBOL(drm_get_edid);
  * drm_edid_duplicate - duplicate an EDID and the extensions
  * @edid: EDID to duplicate
  *
- * Return duplicate edid or NULL on allocation failure.
+ * Return: Pointer to duplicated EDID or NULL on allocation failure.
  */
 struct edid *drm_edid_duplicate(const struct edid *edid)
 {
@@ -1411,7 +1425,8 @@ mode_is_rb(const struct drm_display_mode *mode)
  * @rb: Mode reduced-blanking-ness
  *
  * Walk the DMT mode list looking for a match for the given parameters.
- * Return a newly allocated copy of the mode, or NULL if not found.
+ *
+ * Return: A newly allocated copy of the mode, or NULL if not found.
  */
 struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
                                           int hsize, int vsize, int fresh,
@@ -1595,14 +1610,13 @@ bad_std_timing(u8 a, u8 b)
  * @connector: connector of for the EDID block
  * @edid: EDID block to scan
  * @t: standard timing params
- * @revision: standard timing level
  *
  * Take the standard timing params (in this case width, aspect, and refresh)
  * and convert them into a real mode using CVT/GTF/DMT.
  */
 static struct drm_display_mode *
 drm_mode_std(struct drm_connector *connector, struct edid *edid,
-            struct std_timing *t, int revision)
+            struct std_timing *t)
 {
        struct drm_device *dev = connector->dev;
        struct drm_display_mode *m, *mode = NULL;
@@ -1623,7 +1637,7 @@ drm_mode_std(struct drm_connector *connector, struct edid *edid,
        vrefresh_rate = vfreq + 60;
        /* the vdisplay is calculated based on the aspect ratio */
        if (aspect_ratio == 0) {
-               if (revision < 3)
+               if (edid->revision < 3)
                        vsize = hsize;
                else
                        vsize = (hsize * 10) / 16;
@@ -2140,7 +2154,7 @@ do_established_modes(struct detailed_timing *timing, void *c)
 
 /**
  * add_established_modes - get est. modes from EDID and add them
- * @connector: connector of for the EDID block
+ * @connector: connector to add mode(s) to
  * @edid: EDID block to scan
  *
  * Each EDID block contains a bitmap of the supported "established modes" list
@@ -2191,8 +2205,7 @@ do_standard_modes(struct detailed_timing *timing, void *c)
                        struct drm_display_mode *newmode;
 
                        std = &data->data.timings[i];
-                       newmode = drm_mode_std(connector, edid, std,
-                                              edid->revision);
+                       newmode = drm_mode_std(connector, edid, std);
                        if (newmode) {
                                drm_mode_probed_add(connector, newmode);
                                closure->modes++;
@@ -2203,7 +2216,7 @@ do_standard_modes(struct detailed_timing *timing, void *c)
 
 /**
  * add_standard_modes - get std. modes from EDID and add them
- * @connector: connector of for the EDID block
+ * @connector: connector to add mode(s) to
  * @edid: EDID block to scan
  *
  * Standard modes can be calculated using the appropriate standard (DMT,
@@ -2221,8 +2234,7 @@ add_standard_modes(struct drm_connector *connector, struct edid *edid)
                struct drm_display_mode *newmode;
 
                newmode = drm_mode_std(connector, edid,
-                                      &edid->standard_timings[i],
-                                      edid->revision);
+                                      &edid->standard_timings[i]);
                if (newmode) {
                        drm_mode_probed_add(connector, newmode);
                        modes++;
@@ -2425,7 +2437,7 @@ cea_mode_alternate_clock(const struct drm_display_mode *cea_mode)
  * drm_match_cea_mode - look for a CEA mode matching given mode
  * @to_match: display mode
  *
- * Returns the CEA Video ID (VIC) of the mode or 0 if it isn't a CEA-861
+ * Return: The CEA Video ID (VIC) of the mode or 0 if it isn't a CEA-861
  * mode.
  */
 u8 drm_match_cea_mode(const struct drm_display_mode *to_match)
@@ -2452,6 +2464,22 @@ u8 drm_match_cea_mode(const struct drm_display_mode *to_match)
 }
 EXPORT_SYMBOL(drm_match_cea_mode);
 
+/**
+ * drm_get_cea_aspect_ratio - get the picture aspect ratio corresponding to
+ * the input VIC from the CEA mode list
+ * @video_code: ID given to each of the CEA modes
+ *
+ * Returns picture aspect ratio
+ */
+enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code)
+{
+       /* return picture aspect ratio for video_code - 1 to access the
+        * right array element
+       */
+       return edid_cea_modes[video_code-1].picture_aspect_ratio;
+}
+EXPORT_SYMBOL(drm_get_cea_aspect_ratio);
+
 /*
  * Calculate the alternate clock for HDMI modes (those from the HDMI vendor
  * specific block).
@@ -3023,11 +3051,9 @@ monitor_name(struct detailed_timing *t, void *data)
  * @connector: connector corresponding to the HDMI/DP sink
  * @edid: EDID to parse
  *
- * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver.
- * Some ELD fields are left to the graphics driver caller:
- * - Conn_Type
- * - HDCP
- * - Port_ID
+ * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The
+ * Conn_Type, HDCP and Port_ID ELD fields are left for the graphics driver to
+ * fill in.
  */
 void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
 {
@@ -3111,9 +3137,10 @@ EXPORT_SYMBOL(drm_edid_to_eld);
  * @sads: pointer that will be set to the extracted SADs
  *
  * Looks for CEA EDID block and extracts SADs (Short Audio Descriptors) from it.
- * Note: returned pointer needs to be kfreed
  *
- * Return number of found SADs or negative number on error.
+ * Note: The returned pointer needs to be freed using kfree().
+ *
+ * Return: The number of found SADs or negative number on error.
  */
 int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads)
 {
@@ -3170,9 +3197,11 @@ EXPORT_SYMBOL(drm_edid_to_sad);
  * @sadb: pointer to the speaker block
  *
  * Looks for CEA EDID block and extracts the Speaker Allocation Data Block from it.
- * Note: returned pointer needs to be kfreed
  *
- * Return number of found Speaker Allocation Blocks or negative number on error.
+ * Note: The returned pointer needs to be freed using kfree().
+ *
+ * Return: The number of found Speaker Allocation Blocks or negative number on
+ * error.
  */
 int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb)
 {
@@ -3204,10 +3233,9 @@ int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb)
 
                        /* Speaker Allocation Data Block */
                        if (dbl == 3) {
-                               *sadb = kmalloc(dbl, GFP_KERNEL);
+                               *sadb = kmemdup(&db[1], dbl, GFP_KERNEL);
                                if (!*sadb)
                                        return -ENOMEM;
-                               memcpy(*sadb, &db[1], dbl);
                                count = dbl;
                                break;
                        }
@@ -3219,9 +3247,12 @@ int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb)
 EXPORT_SYMBOL(drm_edid_to_speaker_allocation);
 
 /**
- * drm_av_sync_delay - HDMI/DP sink audio-video sync delay in millisecond
+ * drm_av_sync_delay - compute the HDMI/DP sink audio-video sync delay
  * @connector: connector associated with the HDMI/DP sink
  * @mode: the display mode
+ *
+ * Return: The HDMI/DP sink's audio-video sync delay in milliseconds or 0 if
+ * the sink doesn't support audio or video.
  */
 int drm_av_sync_delay(struct drm_connector *connector,
                      struct drm_display_mode *mode)
@@ -3263,6 +3294,9 @@ EXPORT_SYMBOL(drm_av_sync_delay);
  *
  * It's possible for one encoder to be associated with multiple HDMI/DP sinks.
  * The policy is now hard coded to simply use the first HDMI/DP sink's ELD.
+ *
+ * Return: The connector associated with the first HDMI/DP sink that has ELD
+ * attached to it.
  */
 struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
                                     struct drm_display_mode *mode)
@@ -3270,6 +3304,8 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
        struct drm_connector *connector;
        struct drm_device *dev = encoder->dev;
 
+       WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+
        list_for_each_entry(connector, &dev->mode_config.connector_list, head)
                if (connector->encoder == encoder && connector->eld[0])
                        return connector;
@@ -3279,11 +3315,12 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
 EXPORT_SYMBOL(drm_select_eld);
 
 /**
- * drm_detect_hdmi_monitor - detect whether monitor is hdmi.
+ * drm_detect_hdmi_monitor - detect whether monitor is HDMI
  * @edid: monitor EDID information
  *
  * Parse the CEA extension according to CEA-861-B.
- * Return true if HDMI, false if not or unknown.
+ *
+ * Return: True if the monitor is HDMI, false if not or unknown.
  */
 bool drm_detect_hdmi_monitor(struct edid *edid)
 {
@@ -3321,6 +3358,7 @@ EXPORT_SYMBOL(drm_detect_hdmi_monitor);
  * audio format, assume at least 'basic audio' support, even if 'basic
  * audio' is not defined in EDID.
  *
+ * Return: True if the monitor supports audio, false otherwise.
  */
 bool drm_detect_monitor_audio(struct edid *edid)
 {
@@ -3364,6 +3402,8 @@ EXPORT_SYMBOL(drm_detect_monitor_audio);
  * Check whether the monitor reports the RGB quantization range selection
  * as supported. The AVI infoframe can then be used to inform the monitor
  * which quantization range (full or limited) is used.
+ *
+ * Return: True if the RGB quantization range is selectable, false otherwise.
  */
 bool drm_rgb_quant_range_selectable(struct edid *edid)
 {
@@ -3389,17 +3429,120 @@ bool drm_rgb_quant_range_selectable(struct edid *edid)
 }
 EXPORT_SYMBOL(drm_rgb_quant_range_selectable);
 
+/**
+ * drm_assign_hdmi_deep_color_info - detect whether monitor supports
+ * hdmi deep color modes and update drm_display_info if so.
+ *
+ * @edid: monitor EDID information
+ * @info: Updated with maximum supported deep color bpc and color format
+ *        if deep color supported.
+ *
+ * Parse the CEA extension according to CEA-861-B.
+ * Return true if HDMI deep color supported, false if not or unknown.
+ */
+static bool drm_assign_hdmi_deep_color_info(struct edid *edid,
+                                            struct drm_display_info *info,
+                                            struct drm_connector *connector)
+{
+       u8 *edid_ext, *hdmi;
+       int i;
+       int start_offset, end_offset;
+       unsigned int dc_bpc = 0;
+
+       edid_ext = drm_find_cea_extension(edid);
+       if (!edid_ext)
+               return false;
+
+       if (cea_db_offsets(edid_ext, &start_offset, &end_offset))
+               return false;
+
+       /*
+        * Because HDMI identifier is in Vendor Specific Block,
+        * search it from all data blocks of CEA extension.
+        */
+       for_each_cea_db(edid_ext, i, start_offset, end_offset) {
+               if (cea_db_is_hdmi_vsdb(&edid_ext[i])) {
+                       /* HDMI supports at least 8 bpc */
+                       info->bpc = 8;
+
+                       hdmi = &edid_ext[i];
+                       if (cea_db_payload_len(hdmi) < 6)
+                               return false;
+
+                       if (hdmi[6] & DRM_EDID_HDMI_DC_30) {
+                               dc_bpc = 10;
+                               info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_30;
+                               DRM_DEBUG("%s: HDMI sink does deep color 30.\n",
+                                                 connector->name);
+                       }
+
+                       if (hdmi[6] & DRM_EDID_HDMI_DC_36) {
+                               dc_bpc = 12;
+                               info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_36;
+                               DRM_DEBUG("%s: HDMI sink does deep color 36.\n",
+                                                 connector->name);
+                       }
+
+                       if (hdmi[6] & DRM_EDID_HDMI_DC_48) {
+                               dc_bpc = 16;
+                               info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_48;
+                               DRM_DEBUG("%s: HDMI sink does deep color 48.\n",
+                                                 connector->name);
+                       }
+
+                       if (dc_bpc > 0) {
+                               DRM_DEBUG("%s: Assigning HDMI sink color depth as %d bpc.\n",
+                                                 connector->name, dc_bpc);
+                               info->bpc = dc_bpc;
+
+                               /*
+                                * Deep color support mandates RGB444 support for all video
+                                * modes and forbids YCRCB422 support for all video modes per
+                                * HDMI 1.3 spec.
+                                */
+                               info->color_formats = DRM_COLOR_FORMAT_RGB444;
+
+                               /* YCRCB444 is optional according to spec. */
+                               if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
+                                       info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
+                                       DRM_DEBUG("%s: HDMI sink does YCRCB444 in deep color.\n",
+                                                         connector->name);
+                               }
+
+                               /*
+                                * Spec says that if any deep color mode is supported at all,
+                                * then deep color 36 bit must be supported.
+                                */
+                               if (!(hdmi[6] & DRM_EDID_HDMI_DC_36)) {
+                                       DRM_DEBUG("%s: HDMI sink should do DC_36, but does not!\n",
+                                                         connector->name);
+                               }
+
+                               return true;
+                       }
+                       else {
+                               DRM_DEBUG("%s: No deep color support on this HDMI sink.\n",
+                                                 connector->name);
+                       }
+               }
+       }
+
+       return false;
+}
+
 /**
  * drm_add_display_info - pull display info out if present
  * @edid: EDID data
  * @info: display info (attached to connector)
+ * @connector: connector whose edid is used to build display info
  *
  * Grab any available display info and stuff it into the drm_display_info
  * structure that's part of the connector.  Useful for tracking bpp and
  * color spaces.
  */
 static void drm_add_display_info(struct edid *edid,
-                                struct drm_display_info *info)
+                                 struct drm_display_info *info,
+                                 struct drm_connector *connector)
 {
        u8 *edid_ext;
 
@@ -3429,6 +3572,9 @@ static void drm_add_display_info(struct edid *edid,
                        info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
        }
 
+       /* HDMI deep color modes supported? Assign to info, if so */
+       drm_assign_hdmi_deep_color_info(edid, info, connector);
+
        /* Only defined for 1.4 with digital displays */
        if (edid->revision < 4)
                return;
@@ -3458,6 +3604,9 @@ static void drm_add_display_info(struct edid *edid,
                break;
        }
 
+       DRM_DEBUG("%s: Assigning EDID-1.4 digital sink color depth as %d bpc.\n",
+                         connector->name, info->bpc);
+
        info->color_formats |= DRM_COLOR_FORMAT_RGB444;
        if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB444)
                info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
@@ -3468,11 +3617,11 @@ static void drm_add_display_info(struct edid *edid,
 /**
  * drm_add_edid_modes - add modes from EDID data, if available
  * @connector: connector we're probing
- * @edid: edid data
+ * @edid: EDID data
  *
  * Add the specified modes to the connector's mode list.
  *
- * Return number of modes added or 0 if we couldn't find any.
+ * Return: The number of modes added or 0 if we couldn't find any.
  */
 int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
 {
@@ -3484,7 +3633,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
        }
        if (!drm_edid_is_valid(edid)) {
                dev_warn(connector->dev->dev, "%s: EDID invalid.\n",
-                        drm_get_connector_name(connector));
+                        connector->name);
                return 0;
        }
 
@@ -3516,11 +3665,14 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
        if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
                edid_fixup_preferred(connector, quirks);
 
-       drm_add_display_info(edid, &connector->display_info);
+       drm_add_display_info(edid, &connector->display_info, connector);
 
        if (quirks & EDID_QUIRK_FORCE_8BPC)
                connector->display_info.bpc = 8;
 
+       if (quirks & EDID_QUIRK_FORCE_12BPC)
+               connector->display_info.bpc = 12;
+
        return num_modes;
 }
 EXPORT_SYMBOL(drm_add_edid_modes);
@@ -3534,7 +3686,7 @@ EXPORT_SYMBOL(drm_add_edid_modes);
  * Add the specified modes to the connector's mode list. Only when the
  * hdisplay/vdisplay is not beyond the given limit, it will be added.
  *
- * Return number of modes added or 0 if we couldn't find any.
+ * Return: The number of modes added or 0 if we couldn't find any.
  */
 int drm_add_modes_noedid(struct drm_connector *connector,
                        int hdisplay, int vdisplay)
@@ -3573,13 +3725,22 @@ int drm_add_modes_noedid(struct drm_connector *connector,
 }
 EXPORT_SYMBOL(drm_add_modes_noedid);
 
+/**
+ * drm_set_preferred_mode - Sets the preferred mode of a connector
+ * @connector: connector whose mode list should be processed
+ * @hpref: horizontal resolution of preferred mode
+ * @vpref: vertical resolution of preferred mode
+ *
+ * Marks a mode as preferred if it matches the resolution specified by @hpref
+ * and @vpref.
+ */
 void drm_set_preferred_mode(struct drm_connector *connector,
                           int hpref, int vpref)
 {
        struct drm_display_mode *mode;
 
        list_for_each_entry(mode, &connector->probed_modes, head) {
-               if (mode->hdisplay  == hpref &&
+               if (mode->hdisplay == hpref &&
                    mode->vdisplay == vpref)
                        mode->type |= DRM_MODE_TYPE_PREFERRED;
        }
@@ -3592,7 +3753,7 @@ EXPORT_SYMBOL(drm_set_preferred_mode);
  * @frame: HDMI AVI infoframe
  * @mode: DRM display mode
  *
- * Returns 0 on success or a negative error code on failure.
+ * Return: 0 on success or a negative error code on failure.
  */
 int
 drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
@@ -3613,6 +3774,12 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
        frame->video_code = drm_match_cea_mode(mode);
 
        frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE;
+
+       /* Populate picture aspect ratio from CEA mode list */
+       if (frame->video_code > 0)
+               frame->picture_aspect = drm_get_cea_aspect_ratio(
+                                               frame->video_code);
+
        frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE;
        frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN;
 
@@ -3657,7 +3824,7 @@ s3d_structure_from_display_mode(const struct drm_display_mode *mode)
  * 4k or stereoscopic 3D mode. So when giving any other mode as input this
  * function will return -EINVAL, error that can be safely ignored.
  *
- * Returns 0 on success or a negative error code on failure.
+ * Return: 0 on success or a negative error code on failure.
  */
 int
 drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
index 1b4c7a5442c5de22210925c2cea8317790340b04..0a235fe61c9b9bfd929a1f4a71461bc6ad2bd3d7 100644 (file)
@@ -31,8 +31,9 @@ module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644);
 MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob "
        "from built-in data or /lib/firmware instead. ");
 
-#define GENERIC_EDIDS 5
+#define GENERIC_EDIDS 6
 static const char *generic_edid_name[GENERIC_EDIDS] = {
+       "edid/800x600.bin",
        "edid/1024x768.bin",
        "edid/1280x1024.bin",
        "edid/1600x1200.bin",
@@ -41,6 +42,24 @@ static const char *generic_edid_name[GENERIC_EDIDS] = {
 };
 
 static const u8 generic_edid[GENERIC_EDIDS][128] = {
+       {
+       0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+       0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x05, 0x16, 0x01, 0x03, 0x6d, 0x1b, 0x14, 0x78,
+       0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
+       0x20, 0x50, 0x54, 0x01, 0x00, 0x00, 0x45, 0x40,
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xa0, 0x0f,
+       0x20, 0x00, 0x31, 0x58, 0x1c, 0x20, 0x28, 0x80,
+       0x14, 0x00, 0x15, 0xd0, 0x10, 0x00, 0x00, 0x1e,
+       0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
+       0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
+       0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
+       0x3d, 0x24, 0x26, 0x05, 0x00, 0x0a, 0x20, 0x20,
+       0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
+       0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53,
+       0x56, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xc2,
+       },
        {
        0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
        0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -242,7 +261,7 @@ out:
 
 int drm_load_edid_firmware(struct drm_connector *connector)
 {
-       const char *connector_name = drm_get_connector_name(connector);
+       const char *connector_name = connector->name;
        char *edidname = edid_firmware, *last, *colon;
        int ret;
        struct edid *edid;
index 61b5a47ad239115f00b2359f41bd99734645e443..f27c883be391f51dfe0ef41e9cada8ed76ee67dc 100644 (file)
@@ -429,13 +429,8 @@ EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini);
  */
 void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma)
 {
-       if (fbdev_cma) {
-               struct drm_device *dev = fbdev_cma->fb_helper.dev;
-
-               drm_modeset_lock_all(dev);
-               drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper);
-               drm_modeset_unlock_all(dev);
-       }
+       if (fbdev_cma)
+               drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev_cma->fb_helper);
 }
 EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode);
 
index 04d3fd3658f3378e095a728b68de17653c1f1747..d5d8cea1a67996a4a3e61ebc657e68ec7ae6d9b6 100644 (file)
@@ -45,13 +45,13 @@ static LIST_HEAD(kernel_fb_helper_list);
  * DOC: fbdev helpers
  *
  * The fb helper functions are useful to provide an fbdev on top of a drm kernel
- * mode setting driver. They can be used mostly independantely from the crtc
+ * mode setting driver. They can be used mostly independently from the crtc
  * helper functions used by many drivers to implement the kernel mode setting
  * interfaces.
  *
  * Initialization is done as a three-step process with drm_fb_helper_init(),
  * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config().
- * Drivers with fancier requirements than the default beheviour can override the
+ * Drivers with fancier requirements than the default behaviour can override the
  * second step with their own code.  Teardown is done with drm_fb_helper_fini().
  *
  * At runtime drivers should restore the fbdev console by calling
@@ -59,7 +59,7 @@ static LIST_HEAD(kernel_fb_helper_list);
  * should also notify the fb helper code from updates to the output
  * configuration by calling drm_fb_helper_hotplug_event(). For easier
  * integration with the output polling code in drm_crtc_helper.c the modeset
- * code proves a ->output_poll_changed callback.
+ * code provides a ->output_poll_changed callback.
  *
  * All other functions exported by the fb helper library can be used to
  * implement the fbdev driver interface by the driver.
@@ -120,7 +120,7 @@ static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
                mode = &fb_helper_conn->cmdline_mode;
 
                /* do something on return - turn off connector maybe */
-               if (fb_get_options(drm_get_connector_name(connector), &option))
+               if (fb_get_options(connector->name, &option))
                        continue;
 
                if (drm_mode_parse_command_line_for_connector(option,
@@ -142,12 +142,12 @@ static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
                                }
 
                                DRM_INFO("forcing %s connector %s\n",
-                                        drm_get_connector_name(connector), s);
+                                        connector->name, s);
                                connector->force = mode->force;
                        }
 
                        DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
-                                     drm_get_connector_name(connector),
+                                     connector->name,
                                      mode->xres, mode->yres,
                                      mode->refresh_specified ? mode->refresh : 60,
                                      mode->rb ? " reduced blanking" : "",
@@ -273,15 +273,7 @@ int drm_fb_helper_debug_leave(struct fb_info *info)
 }
 EXPORT_SYMBOL(drm_fb_helper_debug_leave);
 
-/**
- * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration
- * @fb_helper: fbcon to restore
- *
- * This should be called from driver's drm ->lastclose callback
- * when implementing an fbcon on top of kms using this helper. This ensures that
- * the user isn't greeted with a black screen when e.g. X dies.
- */
-bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
+static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper)
 {
        struct drm_device *dev = fb_helper->dev;
        struct drm_plane *plane;
@@ -311,7 +303,40 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
        }
        return error;
 }
-EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode);
+/**
+ * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration
+ * @fb_helper: fbcon to restore
+ *
+ * This should be called from driver's drm ->lastclose callback
+ * when implementing an fbcon on top of kms using this helper. This ensures that
+ * the user isn't greeted with a black screen when e.g. X dies.
+ *
+ * Use this variant if you need to bypass locking (panic), or already
+ * hold all modeset locks.  Otherwise use drm_fb_helper_restore_fbdev_mode_unlocked()
+ */
+static bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
+{
+       return restore_fbdev_mode(fb_helper);
+}
+
+/**
+ * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
+ * @fb_helper: fbcon to restore
+ *
+ * This should be called from driver's drm ->lastclose callback
+ * when implementing an fbcon on top of kms using this helper. This ensures that
+ * the user isn't greeted with a black screen when e.g. X dies.
+ */
+bool drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
+{
+       struct drm_device *dev = fb_helper->dev;
+       bool ret;
+       drm_modeset_lock_all(dev);
+       ret = restore_fbdev_mode(fb_helper);
+       drm_modeset_unlock_all(dev);
+       return ret;
+}
+EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
 
 /*
  * restore fbcon display for all kms driver's using this helper, used for sysrq
@@ -326,12 +351,25 @@ static bool drm_fb_helper_force_kernel_mode(void)
                return false;
 
        list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
-               if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+               struct drm_device *dev = helper->dev;
+
+               if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+                       continue;
+
+               /* NOTE: we use lockless flag below to avoid grabbing other
+                * modeset locks.  So just trylock the underlying mutex
+                * directly:
+                */
+               if (!mutex_trylock(&dev->mode_config.mutex)) {
+                       error = true;
                        continue;
+               }
 
                ret = drm_fb_helper_restore_fbdev_mode(helper);
                if (ret)
                        error = true;
+
+               mutex_unlock(&dev->mode_config.mutex);
        }
        return error;
 }
@@ -811,7 +849,6 @@ EXPORT_SYMBOL(drm_fb_helper_check_var);
 int drm_fb_helper_set_par(struct fb_info *info)
 {
        struct drm_fb_helper *fb_helper = info->par;
-       struct drm_device *dev = fb_helper->dev;
        struct fb_var_screeninfo *var = &info->var;
 
        if (var->pixclock != 0) {
@@ -819,9 +856,7 @@ int drm_fb_helper_set_par(struct fb_info *info)
                return -EINVAL;
        }
 
-       drm_modeset_lock_all(dev);
-       drm_fb_helper_restore_fbdev_mode(fb_helper);
-       drm_modeset_unlock_all(dev);
+       drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
 
        if (fb_helper->delayed_hotplug) {
                fb_helper->delayed_hotplug = false;
index e1eba0b7cd45144a8a3b6337ec0ea3ebeb688ddf..021fe5d11df51111629739da5fc12665839768ca 100644 (file)
@@ -43,8 +43,7 @@
 DEFINE_MUTEX(drm_global_mutex);
 EXPORT_SYMBOL(drm_global_mutex);
 
-static int drm_open_helper(struct inode *inode, struct file *filp,
-                          struct drm_minor *minor);
+static int drm_open_helper(struct file *filp, struct drm_minor *minor);
 
 static int drm_setup(struct drm_device * dev)
 {
@@ -95,7 +94,7 @@ int drm_open(struct inode *inode, struct file *filp)
        /* share address_space across all char-devs of a single device */
        filp->f_mapping = dev->anon_inode->i_mapping;
 
-       retcode = drm_open_helper(inode, filp, minor);
+       retcode = drm_open_helper(filp, minor);
        if (retcode)
                goto err_undo;
        if (need_setup) {
@@ -171,7 +170,6 @@ static int drm_cpu_valid(void)
 /**
  * Called whenever a process opens /dev/drm.
  *
- * \param inode device inode.
  * \param filp file pointer.
  * \param minor acquired minor-object.
  * \return zero on success or a negative number on failure.
@@ -179,8 +177,7 @@ static int drm_cpu_valid(void)
  * Creates and initializes a drm_file structure for the file private data in \p
  * filp and add it into the double linked list in \p dev.
  */
-static int drm_open_helper(struct inode *inode, struct file *filp,
-                          struct drm_minor *minor)
+static int drm_open_helper(struct file *filp, struct drm_minor *minor)
 {
        struct drm_device *dev = minor->dev;
        struct drm_file *priv;
index 9909bef5980045e3ba5eba0ec35187f4e2bbf0cf..f7d71190aad5c0c07495bd26e841b46dd959cd72 100644 (file)
@@ -474,21 +474,10 @@ struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask)
                        goto fail;
                pages[i] = p;
 
-               /* There is a hypothetical issue w/ drivers that require
-                * buffer memory in the low 4GB.. if the pages are un-
-                * pinned, and swapped out, they can end up swapped back
-                * in above 4GB.  If pages are already in memory, then
-                * shmem_read_mapping_page_gfp will ignore the gfpmask,
-                * even if the already in-memory page disobeys the mask.
-                *
-                * It is only a theoretical issue today, because none of
-                * the devices with this limitation can be populated with
-                * enough memory to trigger the issue.  But this BUG_ON()
-                * is here as a reminder in case the problem with
-                * shmem_read_mapping_page_gfp() isn't solved by the time
-                * it does become a real issue.
-                *
-                * See this thread: http://lkml.org/lkml/2011/7/11/238
+               /* Make sure shmem keeps __GFP_DMA32 allocated pages in the
+                * correct region during swapin. Note that this requires
+                * __GFP_DMA32 to be set in mapping_gfp_mask(inode->i_mapping)
+                * so shmem can relocate pages during swapin if required.
                 */
                BUG_ON((gfpmask & __GFP_DMA32) &&
                                (page_to_pfn(p) >= 0x00100000UL));
index 7473035dd28b781ed8f382d0e8b6cd3743df772e..86feedd5e6f696f521e5fa0619c2f86b64598f60 100644 (file)
@@ -47,18 +47,16 @@ int drm_name_info(struct seq_file *m, void *data)
        struct drm_minor *minor = node->minor;
        struct drm_device *dev = minor->dev;
        struct drm_master *master = minor->master;
-       const char *bus_name;
        if (!master)
                return 0;
 
-       bus_name = dev->driver->bus->get_name(dev);
        if (master->unique) {
                seq_printf(m, "%s %s %s\n",
-                          bus_name,
+                          dev->driver->name,
                           dev_name(dev->dev), master->unique);
        } else {
                seq_printf(m, "%s %s\n",
-                          bus_name, dev_name(dev->dev));
+                          dev->driver->name, dev_name(dev->dev));
        }
        return 0;
 }
index 93a42040bedb38a2bd21c4a8409a6ad83081674f..69c61f392e663995466c75550fbb5f6992bb4d77 100644 (file)
@@ -72,9 +72,6 @@ static void
 drm_unset_busid(struct drm_device *dev,
                struct drm_master *master)
 {
-       kfree(dev->devname);
-       dev->devname = NULL;
-
        kfree(master->unique);
        master->unique = NULL;
        master->unique_len = 0;
@@ -93,7 +90,8 @@ drm_unset_busid(struct drm_device *dev,
  * Copies the bus id from userspace into drm_device::unique, and verifies that
  * it matches the device this DRM is attached to (EINVAL otherwise).  Deprecated
  * in interface version 1.1 and will return EBUSY when setversion has requested
- * version 1.1 or greater.
+ * version 1.1 or greater. Also note that KMS is all version 1.1 and later and
+ * UMS was only ever supported on pci devices.
  */
 int drm_setunique(struct drm_device *dev, void *data,
                  struct drm_file *file_priv)
@@ -108,10 +106,13 @@ int drm_setunique(struct drm_device *dev, void *data,
        if (!u->unique_len || u->unique_len > 1024)
                return -EINVAL;
 
-       if (!dev->driver->bus->set_unique)
+       if (drm_core_check_feature(dev, DRIVER_MODESET))
+               return 0;
+
+       if (WARN_ON(!dev->pdev))
                return -EINVAL;
 
-       ret = dev->driver->bus->set_unique(dev, master, u);
+       ret = drm_pci_set_unique(dev, master, u);
        if (ret)
                goto err;
 
@@ -130,13 +131,25 @@ static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv)
        if (master->unique != NULL)
                drm_unset_busid(dev, master);
 
-       ret = dev->driver->bus->set_busid(dev, master);
-       if (ret)
-               goto err;
+       if (dev->driver->bus && dev->driver->bus->set_busid) {
+               ret = dev->driver->bus->set_busid(dev, master);
+               if (ret) {
+                       drm_unset_busid(dev, master);
+                       return ret;
+               }
+       } else {
+               if (WARN(dev->unique == NULL,
+                        "No drm_bus.set_busid() implementation provided by "
+                        "%ps. Use drm_dev_set_unique() to set the unique "
+                        "name explicitly.", dev->driver))
+                       return -EINVAL;
+
+               master->unique = kstrdup(dev->unique, GFP_KERNEL);
+               if (master->unique)
+                       master->unique_len = strlen(dev->unique);
+       }
+
        return 0;
-err:
-       drm_unset_busid(dev, master);
-       return ret;
 }
 
 /**
index ec5c3f4cdd011de07be08bc1b74a955fb4b3fcda..0de123afdb346164d278bf801424629e2059b242 100644 (file)
@@ -1,6 +1,5 @@
-/**
- * \file drm_irq.c
- * IRQ support
+/*
+ * drm_irq.c IRQ and vblank support
  *
  * \author Rickard E. (Rik) Faith <faith@valinux.com>
  * \author Gareth Hughes <gareth@valinux.com>
  */
 #define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000
 
-/**
- * Get interrupt from bus id.
- *
- * \param inode device inode.
- * \param file_priv DRM file private.
- * \param cmd command.
- * \param arg user argument, pointing to a drm_irq_busid structure.
- * \return zero on success or a negative number on failure.
- *
- * Finds the PCI device with the specified bus id and gets its IRQ number.
- * This IOCTL is deprecated, and will now return EINVAL for any busid not equal
- * to that of the device that this DRM instance attached to.
- */
-int drm_irq_by_busid(struct drm_device *dev, void *data,
-                    struct drm_file *file_priv)
-{
-       struct drm_irq_busid *p = data;
-
-       if (!dev->driver->bus->irq_by_busid)
-               return -EINVAL;
-
-       if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
-               return -EINVAL;
-
-       return dev->driver->bus->irq_by_busid(dev, p);
-}
-
 /*
  * Clear vblank timestamp buffer for a crtc.
  */
@@ -167,33 +139,40 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc)
 
 static void vblank_disable_fn(unsigned long arg)
 {
-       struct drm_device *dev = (struct drm_device *)arg;
+       struct drm_vblank_crtc *vblank = (void *)arg;
+       struct drm_device *dev = vblank->dev;
        unsigned long irqflags;
-       int i;
+       int crtc = vblank->crtc;
 
        if (!dev->vblank_disable_allowed)
                return;
 
-       for (i = 0; i < dev->num_crtcs; i++) {
-               spin_lock_irqsave(&dev->vbl_lock, irqflags);
-               if (atomic_read(&dev->vblank[i].refcount) == 0 &&
-                   dev->vblank[i].enabled) {
-                       DRM_DEBUG("disabling vblank on crtc %d\n", i);
-                       vblank_disable_and_save(dev, i);
-               }
-               spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+       spin_lock_irqsave(&dev->vbl_lock, irqflags);
+       if (atomic_read(&vblank->refcount) == 0 && vblank->enabled) {
+               DRM_DEBUG("disabling vblank on crtc %d\n", crtc);
+               vblank_disable_and_save(dev, crtc);
        }
+       spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
 }
 
+/**
+ * drm_vblank_cleanup - cleanup vblank support
+ * @dev: DRM device
+ *
+ * This function cleans up any resources allocated in drm_vblank_init.
+ */
 void drm_vblank_cleanup(struct drm_device *dev)
 {
+       int crtc;
+
        /* Bail if the driver didn't call drm_vblank_init() */
        if (dev->num_crtcs == 0)
                return;
 
-       del_timer_sync(&dev->vblank_disable_timer);
-
-       vblank_disable_fn((unsigned long)dev);
+       for (crtc = 0; crtc < dev->num_crtcs; crtc++) {
+               del_timer_sync(&dev->vblank[crtc].disable_timer);
+               vblank_disable_fn((unsigned long)&dev->vblank[crtc]);
+       }
 
        kfree(dev->vblank);
 
@@ -201,12 +180,20 @@ void drm_vblank_cleanup(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_vblank_cleanup);
 
+/**
+ * drm_vblank_init - initialize vblank support
+ * @dev: drm_device
+ * @num_crtcs: number of crtcs supported by @dev
+ *
+ * This function initializes vblank support for @num_crtcs display pipelines.
+ *
+ * Returns:
+ * Zero on success or a negative error code on failure.
+ */
 int drm_vblank_init(struct drm_device *dev, int num_crtcs)
 {
        int i, ret = -ENOMEM;
 
-       setup_timer(&dev->vblank_disable_timer, vblank_disable_fn,
-                   (unsigned long)dev);
        spin_lock_init(&dev->vbl_lock);
        spin_lock_init(&dev->vblank_time_lock);
 
@@ -216,8 +203,13 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
        if (!dev->vblank)
                goto err;
 
-       for (i = 0; i < num_crtcs; i++)
+       for (i = 0; i < num_crtcs; i++) {
+               dev->vblank[i].dev = dev;
+               dev->vblank[i].crtc = i;
                init_waitqueue_head(&dev->vblank[i].queue);
+               setup_timer(&dev->vblank[i].disable_timer, vblank_disable_fn,
+                           (unsigned long)&dev->vblank[i]);
+       }
 
        DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n");
 
@@ -261,42 +253,42 @@ static void drm_irq_vgaarb_nokms(void *cookie, bool state)
 }
 
 /**
- * Install IRQ handler.
- *
- * \param dev DRM device.
+ * drm_irq_install - install IRQ handler
+ * @dev: DRM device
+ * @irq: IRQ number to install the handler for
  *
  * Initializes the IRQ related data. Installs the handler, calling the driver
- * \c irq_preinstall() and \c irq_postinstall() functions
- * before and after the installation.
+ * irq_preinstall() and irq_postinstall() functions before and after the
+ * installation.
+ *
+ * This is the simplified helper interface provided for drivers with no special
+ * needs. Drivers which need to install interrupt handlers for multiple
+ * interrupts must instead set drm_device->irq_enabled to signal the DRM core
+ * that vblank interrupts are available.
+ *
+ * Returns:
+ * Zero on success or a negative error code on failure.
  */
-int drm_irq_install(struct drm_device *dev)
+int drm_irq_install(struct drm_device *dev, int irq)
 {
        int ret;
        unsigned long sh_flags = 0;
-       char *irqname;
 
        if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
                return -EINVAL;
 
-       if (drm_dev_to_irq(dev) == 0)
+       if (irq == 0)
                return -EINVAL;
 
-       mutex_lock(&dev->struct_mutex);
-
        /* Driver must have been initialized */
-       if (!dev->dev_private) {
-               mutex_unlock(&dev->struct_mutex);
+       if (!dev->dev_private)
                return -EINVAL;
-       }
 
-       if (dev->irq_enabled) {
-               mutex_unlock(&dev->struct_mutex);
+       if (dev->irq_enabled)
                return -EBUSY;
-       }
        dev->irq_enabled = true;
-       mutex_unlock(&dev->struct_mutex);
 
-       DRM_DEBUG("irq=%d\n", drm_dev_to_irq(dev));
+       DRM_DEBUG("irq=%d\n", irq);
 
        /* Before installing handler */
        if (dev->driver->irq_preinstall)
@@ -306,18 +298,11 @@ int drm_irq_install(struct drm_device *dev)
        if (drm_core_check_feature(dev, DRIVER_IRQ_SHARED))
                sh_flags = IRQF_SHARED;
 
-       if (dev->devname)
-               irqname = dev->devname;
-       else
-               irqname = dev->driver->name;
-
-       ret = request_irq(drm_dev_to_irq(dev), dev->driver->irq_handler,
-                         sh_flags, irqname, dev);
+       ret = request_irq(irq, dev->driver->irq_handler,
+                         sh_flags, dev->driver->name, dev);
 
        if (ret < 0) {
-               mutex_lock(&dev->struct_mutex);
                dev->irq_enabled = false;
-               mutex_unlock(&dev->struct_mutex);
                return ret;
        }
 
@@ -329,12 +314,12 @@ int drm_irq_install(struct drm_device *dev)
                ret = dev->driver->irq_postinstall(dev);
 
        if (ret < 0) {
-               mutex_lock(&dev->struct_mutex);
                dev->irq_enabled = false;
-               mutex_unlock(&dev->struct_mutex);
                if (!drm_core_check_feature(dev, DRIVER_MODESET))
                        vga_client_register(dev->pdev, NULL, NULL, NULL);
-               free_irq(drm_dev_to_irq(dev), dev);
+               free_irq(irq, dev);
+       } else {
+               dev->irq = irq;
        }
 
        return ret;
@@ -342,11 +327,20 @@ int drm_irq_install(struct drm_device *dev)
 EXPORT_SYMBOL(drm_irq_install);
 
 /**
- * Uninstall the IRQ handler.
+ * drm_irq_uninstall - uninstall the IRQ handler
+ * @dev: DRM device
+ *
+ * Calls the driver's irq_uninstall() function and unregisters the IRQ handler.
+ * This should only be called by drivers which used drm_irq_install() to set up
+ * their interrupt handler. Other drivers must only reset
+ * drm_device->irq_enabled to false.
  *
- * \param dev DRM device.
+ * Note that for kernel modesetting drivers it is a bug if this function fails.
+ * The sanity checks are only to catch buggy user modesetting drivers which call
+ * the same function through an ioctl.
  *
- * Calls the driver's \c irq_uninstall() function, and stops the irq.
+ * Returns:
+ * Zero on success or a negative error code on failure.
  */
 int drm_irq_uninstall(struct drm_device *dev)
 {
@@ -357,10 +351,8 @@ int drm_irq_uninstall(struct drm_device *dev)
        if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
                return -EINVAL;
 
-       mutex_lock(&dev->struct_mutex);
        irq_enabled = dev->irq_enabled;
        dev->irq_enabled = false;
-       mutex_unlock(&dev->struct_mutex);
 
        /*
         * Wake up any waiters so they don't hang.
@@ -379,7 +371,7 @@ int drm_irq_uninstall(struct drm_device *dev)
        if (!irq_enabled)
                return -EINVAL;
 
-       DRM_DEBUG("irq=%d\n", drm_dev_to_irq(dev));
+       DRM_DEBUG("irq=%d\n", dev->irq);
 
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                vga_client_register(dev->pdev, NULL, NULL, NULL);
@@ -387,13 +379,13 @@ int drm_irq_uninstall(struct drm_device *dev)
        if (dev->driver->irq_uninstall)
                dev->driver->irq_uninstall(dev);
 
-       free_irq(drm_dev_to_irq(dev), dev);
+       free_irq(dev->irq, dev);
 
        return 0;
 }
 EXPORT_SYMBOL(drm_irq_uninstall);
 
-/**
+/*
  * IRQ control ioctl.
  *
  * \param inode device inode.
@@ -408,43 +400,52 @@ int drm_control(struct drm_device *dev, void *data,
                struct drm_file *file_priv)
 {
        struct drm_control *ctl = data;
+       int ret = 0, irq;
 
        /* if we haven't irq we fallback for compatibility reasons -
         * this used to be a separate function in drm_dma.h
         */
 
+       if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
+               return 0;
+       if (drm_core_check_feature(dev, DRIVER_MODESET))
+               return 0;
+       /* UMS was only ever support on pci devices. */
+       if (WARN_ON(!dev->pdev))
+               return -EINVAL;
 
        switch (ctl->func) {
        case DRM_INST_HANDLER:
-               if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
-                       return 0;
-               if (drm_core_check_feature(dev, DRIVER_MODESET))
-                       return 0;
+               irq = dev->pdev->irq;
+
                if (dev->if_version < DRM_IF_VERSION(1, 2) &&
-                   ctl->irq != drm_dev_to_irq(dev))
+                   ctl->irq != irq)
                        return -EINVAL;
-               return drm_irq_install(dev);
+               mutex_lock(&dev->struct_mutex);
+               ret = drm_irq_install(dev, irq);
+               mutex_unlock(&dev->struct_mutex);
+
+               return ret;
        case DRM_UNINST_HANDLER:
-               if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
-                       return 0;
-               if (drm_core_check_feature(dev, DRIVER_MODESET))
-                       return 0;
-               return drm_irq_uninstall(dev);
+               mutex_lock(&dev->struct_mutex);
+               ret = drm_irq_uninstall(dev);
+               mutex_unlock(&dev->struct_mutex);
+
+               return ret;
        default:
                return -EINVAL;
        }
 }
 
 /**
- * drm_calc_timestamping_constants - Calculate vblank timestamp constants
- *
- * @crtc drm_crtc whose timestamp constants should be updated.
- * @mode display mode containing the scanout timings
+ * drm_calc_timestamping_constants - calculate vblank timestamp constants
+ * @crtc: drm_crtc whose timestamp constants should be updated.
+ * @mode: display mode containing the scanout timings
  *
  * Calculate and store various constants which are later
  * needed by vblank and swap-completion timestamping, e.g,
  * by drm_calc_vbltimestamp_from_scanoutpos(). They are
- * derived from crtc's true scanout timing, so they take
+ * derived from CRTC's true scanout timing, so they take
  * things like panel scaling or other adjustments into account.
  */
 void drm_calc_timestamping_constants(struct drm_crtc *crtc,
@@ -489,11 +490,22 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc,
 EXPORT_SYMBOL(drm_calc_timestamping_constants);
 
 /**
- * drm_calc_vbltimestamp_from_scanoutpos - helper routine for kms
- * drivers. Implements calculation of exact vblank timestamps from
- * given drm_display_mode timings and current video scanout position
- * of a crtc. This can be called from within get_vblank_timestamp()
- * implementation of a kms driver to implement the actual timestamping.
+ * drm_calc_vbltimestamp_from_scanoutpos - precise vblank timestamp helper
+ * @dev: DRM device
+ * @crtc: Which CRTC's vblank timestamp to retrieve
+ * @max_error: Desired maximum allowable error in timestamps (nanosecs)
+ *             On return contains true maximum error of timestamp
+ * @vblank_time: Pointer to struct timeval which should receive the timestamp
+ * @flags: Flags to pass to driver:
+ *         0 = Default,
+ *         DRM_CALLED_FROM_VBLIRQ = If function is called from vbl IRQ handler
+ * @refcrtc: CRTC which defines scanout timing
+ * @mode: mode which defines the scanout timings
+ *
+ * Implements calculation of exact vblank timestamps from given drm_display_mode
+ * timings and current video scanout position of a CRTC. This can be called from
+ * within get_vblank_timestamp() implementation of a kms driver to implement the
+ * actual timestamping.
  *
  * Should return timestamps conforming to the OML_sync_control OpenML
  * extension specification. The timestamp corresponds to the end of
@@ -508,21 +520,11 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
  * returns as no operation if a doublescan or interlaced video mode is
  * active. Higher level code is expected to handle this.
  *
- * @dev: DRM device.
- * @crtc: Which crtc's vblank timestamp to retrieve.
- * @max_error: Desired maximum allowable error in timestamps (nanosecs).
- *             On return contains true maximum error of timestamp.
- * @vblank_time: Pointer to struct timeval which should receive the timestamp.
- * @flags: Flags to pass to driver:
- *         0 = Default.
- *         DRM_CALLED_FROM_VBLIRQ = If function is called from vbl irq handler.
- * @refcrtc: drm_crtc* of crtc which defines scanout timing.
- * @mode: mode which defines the scanout timings
- *
- * Returns negative value on error, failure or if not supported in current
+ * Returns:
+ * Negative value on error, failure or if not supported in current
  * video mode:
  *
- * -EINVAL   - Invalid crtc.
+ * -EINVAL   - Invalid CRTC.
  * -EAGAIN   - Temporary unavailable, e.g., called before initial modeset.
  * -ENOTSUPP - Function not supported in current display mode.
  * -EIO      - Failed, e.g., due to failed scanout position query.
@@ -671,23 +673,23 @@ static struct timeval get_drm_timestamp(void)
 
 /**
  * drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent
- * vblank interval.
- *
+ *                            vblank interval
  * @dev: DRM device
- * @crtc: which crtc's vblank timestamp to retrieve
+ * @crtc: which CRTC's vblank timestamp to retrieve
  * @tvblank: Pointer to target struct timeval which should receive the timestamp
  * @flags: Flags to pass to driver:
- *         0 = Default.
- *         DRM_CALLED_FROM_VBLIRQ = If function is called from vbl irq handler.
+ *         0 = Default,
+ *         DRM_CALLED_FROM_VBLIRQ = If function is called from vbl IRQ handler
  *
  * Fetches the system timestamp corresponding to the time of the most recent
- * vblank interval on specified crtc. May call into kms-driver to
+ * vblank interval on specified CRTC. May call into kms-driver to
  * compute the timestamp with a high-precision GPU specific method.
  *
  * Returns zero if timestamp originates from uncorrected do_gettimeofday()
  * call, i.e., it isn't very precisely locked to the true vblank.
  *
- * Returns non-zero if timestamp is considered to be very precise.
+ * Returns:
+ * Non-zero if timestamp is considered to be very precise, zero otherwise.
  */
 u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc,
                              struct timeval *tvblank, unsigned flags)
@@ -722,6 +724,9 @@ EXPORT_SYMBOL(drm_get_last_vbltimestamp);
  * Fetches the "cooked" vblank count value that represents the number of
  * vblank events since the system was booted, including lost events due to
  * modesetting activity.
+ *
+ * Returns:
+ * The software vblank counter.
  */
 u32 drm_vblank_count(struct drm_device *dev, int crtc)
 {
@@ -740,8 +745,7 @@ EXPORT_SYMBOL(drm_vblank_count);
  * Fetches the "cooked" vblank count value that represents the number of
  * vblank events since the system was booted, including lost events due to
  * modesetting activity. Returns corresponding system timestamp of the time
- * of the vblank interval that corresponds to the current value vblank counter
- * value.
+ * of the vblank interval that corresponds to the current vblank counter value.
  */
 u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc,
                              struct timeval *vblanktime)
@@ -869,6 +873,42 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc)
        smp_mb__after_atomic();
 }
 
+/**
+ * drm_vblank_enable - enable the vblank interrupt on a CRTC
+ * @dev: DRM device
+ * @crtc: CRTC in question
+ */
+static int drm_vblank_enable(struct drm_device *dev, int crtc)
+{
+       int ret = 0;
+
+       assert_spin_locked(&dev->vbl_lock);
+
+       spin_lock(&dev->vblank_time_lock);
+
+       if (!dev->vblank[crtc].enabled) {
+               /*
+                * Enable vblank irqs under vblank_time_lock protection.
+                * All vblank count & timestamp updates are held off
+                * until we are done reinitializing master counter and
+                * timestamps. Filtercode in drm_handle_vblank() will
+                * prevent double-accounting of same vblank interval.
+                */
+               ret = dev->driver->enable_vblank(dev, crtc);
+               DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", crtc, ret);
+               if (ret)
+                       atomic_dec(&dev->vblank[crtc].refcount);
+               else {
+                       dev->vblank[crtc].enabled = true;
+                       drm_update_vblank_count(dev, crtc);
+               }
+       }
+
+       spin_unlock(&dev->vblank_time_lock);
+
+       return ret;
+}
+
 /**
  * drm_vblank_get - get a reference count on vblank events
  * @dev: DRM device
@@ -877,36 +917,20 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc)
  * Acquire a reference count on vblank events to avoid having them disabled
  * while in use.
  *
- * RETURNS
+ * This is the legacy version of drm_crtc_vblank_get().
+ *
+ * Returns:
  * Zero on success, nonzero on failure.
  */
 int drm_vblank_get(struct drm_device *dev, int crtc)
 {
-       unsigned long irqflags, irqflags2;
+       unsigned long irqflags;
        int ret = 0;
 
        spin_lock_irqsave(&dev->vbl_lock, irqflags);
        /* Going from 0->1 means we have to enable interrupts again */
        if (atomic_add_return(1, &dev->vblank[crtc].refcount) == 1) {
-               spin_lock_irqsave(&dev->vblank_time_lock, irqflags2);
-               if (!dev->vblank[crtc].enabled) {
-                       /* Enable vblank irqs under vblank_time_lock protection.
-                        * All vblank count & timestamp updates are held off
-                        * until we are done reinitializing master counter and
-                        * timestamps. Filtercode in drm_handle_vblank() will
-                        * prevent double-accounting of same vblank interval.
-                        */
-                       ret = dev->driver->enable_vblank(dev, crtc);
-                       DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n",
-                                 crtc, ret);
-                       if (ret)
-                               atomic_dec(&dev->vblank[crtc].refcount);
-                       else {
-                               dev->vblank[crtc].enabled = true;
-                               drm_update_vblank_count(dev, crtc);
-                       }
-               }
-               spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags2);
+               ret = drm_vblank_enable(dev, crtc);
        } else {
                if (!dev->vblank[crtc].enabled) {
                        atomic_dec(&dev->vblank[crtc].refcount);
@@ -919,6 +943,24 @@ int drm_vblank_get(struct drm_device *dev, int crtc)
 }
 EXPORT_SYMBOL(drm_vblank_get);
 
+/**
+ * drm_crtc_vblank_get - get a reference count on vblank events
+ * @crtc: which CRTC to own
+ *
+ * Acquire a reference count on vblank events to avoid having them disabled
+ * while in use.
+ *
+ * This is the native kms version of drm_vblank_off().
+ *
+ * Returns:
+ * Zero on success, nonzero on failure.
+ */
+int drm_crtc_vblank_get(struct drm_crtc *crtc)
+{
+       return drm_vblank_get(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_vblank_get);
+
 /**
  * drm_vblank_put - give up ownership of vblank events
  * @dev: DRM device
@@ -926,6 +968,8 @@ EXPORT_SYMBOL(drm_vblank_get);
  *
  * Release ownership of a given vblank counter, turning off interrupts
  * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
+ *
+ * This is the legacy version of drm_crtc_vblank_put().
  */
 void drm_vblank_put(struct drm_device *dev, int crtc)
 {
@@ -934,17 +978,39 @@ void drm_vblank_put(struct drm_device *dev, int crtc)
        /* Last user schedules interrupt disable */
        if (atomic_dec_and_test(&dev->vblank[crtc].refcount) &&
            (drm_vblank_offdelay > 0))
-               mod_timer(&dev->vblank_disable_timer,
+               mod_timer(&dev->vblank[crtc].disable_timer,
                          jiffies + ((drm_vblank_offdelay * HZ)/1000));
 }
 EXPORT_SYMBOL(drm_vblank_put);
 
+/**
+ * drm_crtc_vblank_put - give up ownership of vblank events
+ * @crtc: which counter to give up
+ *
+ * Release ownership of a given vblank counter, turning off interrupts
+ * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
+ *
+ * This is the native kms version of drm_vblank_put().
+ */
+void drm_crtc_vblank_put(struct drm_crtc *crtc)
+{
+       drm_vblank_put(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_vblank_put);
+
 /**
  * drm_vblank_off - disable vblank events on a CRTC
  * @dev: DRM device
  * @crtc: CRTC in question
  *
- * Caller must hold event lock.
+ * Drivers can use this function to shut down the vblank interrupt handling when
+ * disabling a crtc. This function ensures that the latest vblank frame count is
+ * stored so that drm_vblank_on() can restore it again.
+ *
+ * Drivers must use this function when the hardware vblank counter can get
+ * reset, e.g. when suspending.
+ *
+ * This is the legacy version of drm_crtc_vblank_off().
  */
 void drm_vblank_off(struct drm_device *dev, int crtc)
 {
@@ -977,6 +1043,66 @@ void drm_vblank_off(struct drm_device *dev, int crtc)
 }
 EXPORT_SYMBOL(drm_vblank_off);
 
+/**
+ * drm_crtc_vblank_off - disable vblank events on a CRTC
+ * @crtc: CRTC in question
+ *
+ * Drivers can use this function to shut down the vblank interrupt handling when
+ * disabling a crtc. This function ensures that the latest vblank frame count is
+ * stored so that drm_vblank_on can restore it again.
+ *
+ * Drivers must use this function when the hardware vblank counter can get
+ * reset, e.g. when suspending.
+ *
+ * This is the native kms version of drm_vblank_off().
+ */
+void drm_crtc_vblank_off(struct drm_crtc *crtc)
+{
+       drm_vblank_off(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_vblank_off);
+
+/**
+ * drm_vblank_on - enable vblank events on a CRTC
+ * @dev: DRM device
+ * @crtc: CRTC in question
+ *
+ * This functions restores the vblank interrupt state captured with
+ * drm_vblank_off() again. Note that calls to drm_vblank_on() and
+ * drm_vblank_off() can be unbalanced and so can also be unconditionaly called
+ * in driver load code to reflect the current hardware state of the crtc.
+ *
+ * This is the legacy version of drm_crtc_vblank_on().
+ */
+void drm_vblank_on(struct drm_device *dev, int crtc)
+{
+       unsigned long irqflags;
+
+       spin_lock_irqsave(&dev->vbl_lock, irqflags);
+       /* re-enable interrupts if there's are users left */
+       if (atomic_read(&dev->vblank[crtc].refcount) != 0)
+               WARN_ON(drm_vblank_enable(dev, crtc));
+       spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+}
+EXPORT_SYMBOL(drm_vblank_on);
+
+/**
+ * drm_crtc_vblank_on - enable vblank events on a CRTC
+ * @crtc: CRTC in question
+ *
+ * This functions restores the vblank interrupt state captured with
+ * drm_vblank_off() again. Note that calls to drm_vblank_on() and
+ * drm_vblank_off() can be unbalanced and so can also be unconditionaly called
+ * in driver load code to reflect the current hardware state of the crtc.
+ *
+ * This is the native kms version of drm_vblank_on().
+ */
+void drm_crtc_vblank_on(struct drm_crtc *crtc)
+{
+       drm_vblank_on(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_vblank_on);
+
 /**
  * drm_vblank_pre_modeset - account for vblanks across mode sets
  * @dev: DRM device
@@ -984,6 +1110,21 @@ EXPORT_SYMBOL(drm_vblank_off);
  *
  * Account for vblank events across mode setting events, which will likely
  * reset the hardware frame counter.
+ *
+ * This is done by grabbing a temporary vblank reference to ensure that the
+ * vblank interrupt keeps running across the modeset sequence. With this the
+ * software-side vblank frame counting will ensure that there are no jumps or
+ * discontinuities.
+ *
+ * Unfortunately this approach is racy and also doesn't work when the vblank
+ * interrupt stops running, e.g. across system suspend resume. It is therefore
+ * highly recommended that drivers use the newer drm_vblank_off() and
+ * drm_vblank_on() instead. drm_vblank_pre_modeset() only works correctly when
+ * using "cooked" software vblank frame counters and not relying on any hardware
+ * counters.
+ *
+ * Drivers must call drm_vblank_post_modeset() when re-enabling the same crtc
+ * again.
  */
 void drm_vblank_pre_modeset(struct drm_device *dev, int crtc)
 {
@@ -1005,6 +1146,14 @@ void drm_vblank_pre_modeset(struct drm_device *dev, int crtc)
 }
 EXPORT_SYMBOL(drm_vblank_pre_modeset);
 
+/**
+ * drm_vblank_post_modeset - undo drm_vblank_pre_modeset changes
+ * @dev: DRM device
+ * @crtc: CRTC in question
+ *
+ * This function again drops the temporary vblank reference acquired in
+ * drm_vblank_pre_modeset.
+ */
 void drm_vblank_post_modeset(struct drm_device *dev, int crtc)
 {
        unsigned long irqflags;
@@ -1026,7 +1175,7 @@ void drm_vblank_post_modeset(struct drm_device *dev, int crtc)
 }
 EXPORT_SYMBOL(drm_vblank_post_modeset);
 
-/**
+/*
  * drm_modeset_ctl - handle vblank event counter changes across mode switch
  * @DRM_IOCTL_ARGS: standard ioctl arguments
  *
@@ -1139,7 +1288,7 @@ err_put:
        return ret;
 }
 
-/**
+/*
  * Wait for VBLANK.
  *
  * \param inode device inode.
@@ -1150,7 +1299,7 @@ err_put:
  *
  * This function enables the vblank interrupt on the pipe requested, then
  * sleeps waiting for the requested sequence number to occur, and drops
- * the vblank interrupt refcount afterwards. (vblank irq disable follows that
+ * the vblank interrupt refcount afterwards. (vblank IRQ disable follows that
  * after a timeout with no further vblank waits scheduled).
  */
 int drm_wait_vblank(struct drm_device *dev, void *data,
@@ -1160,9 +1309,8 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
        int ret;
        unsigned int flags, seq, crtc, high_crtc;
 
-       if (drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
-               if ((!drm_dev_to_irq(dev)) || (!dev->irq_enabled))
-                       return -EINVAL;
+       if (!dev->irq_enabled)
+               return -EINVAL;
 
        if (vblwait->request.type & _DRM_VBLANK_SIGNAL)
                return -EINVAL;
@@ -1222,6 +1370,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
        DRM_WAIT_ON(ret, dev->vblank[crtc].queue, 3 * HZ,
                    (((drm_vblank_count(dev, crtc) -
                       vblwait->request.sequence) <= (1 << 23)) ||
+                    !dev->vblank[crtc].enabled ||
                     !dev->irq_enabled));
 
        if (ret != -EINTR) {
index 09821f46d768dae2ab019898e6036da32aa0a279..e633df2f68d896622b56a946dd0b649826cf05bc 100644 (file)
@@ -282,6 +282,14 @@ static int mipi_dsi_drv_remove(struct device *dev)
        return drv->remove(dsi);
 }
 
+static void mipi_dsi_drv_shutdown(struct device *dev)
+{
+       struct mipi_dsi_driver *drv = to_mipi_dsi_driver(dev->driver);
+       struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
+
+       drv->shutdown(dsi);
+}
+
 /**
  * mipi_dsi_driver_register - register a driver for DSI devices
  * @drv: DSI driver structure
@@ -293,6 +301,8 @@ int mipi_dsi_driver_register(struct mipi_dsi_driver *drv)
                drv->driver.probe = mipi_dsi_drv_probe;
        if (drv->remove)
                drv->driver.remove = mipi_dsi_drv_remove;
+       if (drv->shutdown)
+               drv->driver.shutdown = mipi_dsi_drv_shutdown;
 
        return driver_register(&drv->driver);
 }
index 8b410576fce4b42998bb8e9a0334de5ff38207db..bedf1894e17e5fa0f8f6a028a970c976725e1dba 100644 (file)
@@ -1013,6 +1013,7 @@ EXPORT_SYMBOL(drm_mode_sort);
 /**
  * drm_mode_connector_list_update - update the mode list for the connector
  * @connector: the connector to update
+ * @merge_type_bits: whether to merge or overright type bits.
  *
  * This moves the modes from the @connector probed_modes list
  * to the actual mode list. It compares the probed mode against the current
@@ -1021,7 +1022,8 @@ EXPORT_SYMBOL(drm_mode_sort);
  * This is just a helper functions doesn't validate any modes itself and also
  * doesn't prune any invalid modes. Callers need to do that themselves.
  */
-void drm_mode_connector_list_update(struct drm_connector *connector)
+void drm_mode_connector_list_update(struct drm_connector *connector,
+                                   bool merge_type_bits)
 {
        struct drm_display_mode *mode;
        struct drm_display_mode *pmode, *pt;
@@ -1039,7 +1041,10 @@ void drm_mode_connector_list_update(struct drm_connector *connector)
                                /* if equal delete the probed mode */
                                mode->status = pmode->status;
                                /* Merge type bits together */
-                               mode->type |= pmode->type;
+                               if (merge_type_bits)
+                                       mode->type |= pmode->type;
+                               else
+                                       mode->type = pmode->type;
                                list_del(&pmode->head);
                                drm_mode_destroy(connector->dev, pmode);
                                break;
diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c
new file mode 100644 (file)
index 0000000..7c2497d
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2014 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * 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 <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_modeset_lock.h>
+
+/**
+ * DOC: kms locking
+ *
+ * As KMS moves toward more fine grained locking, and atomic ioctl where
+ * userspace can indirectly control locking order, it becomes necessary
+ * to use ww_mutex and acquire-contexts to avoid deadlocks.  But because
+ * the locking is more distributed around the driver code, we want a bit
+ * of extra utility/tracking out of our acquire-ctx.  This is provided
+ * by drm_modeset_lock / drm_modeset_acquire_ctx.
+ *
+ * For basic principles of ww_mutex, see: Documentation/ww-mutex-design.txt
+ *
+ * The basic usage pattern is to:
+ *
+ *     drm_modeset_acquire_init(&ctx)
+ *   retry:
+ *     foreach (lock in random_ordered_set_of_locks) {
+ *       ret = drm_modeset_lock(lock, &ctx)
+ *       if (ret == -EDEADLK) {
+ *          drm_modeset_backoff(&ctx);
+ *          goto retry;
+ *       }
+ *     }
+ *
+ *     ... do stuff ...
+ *
+ *     drm_modeset_drop_locks(&ctx);
+ *     drm_modeset_acquire_fini(&ctx);
+ */
+
+
+/**
+ * drm_modeset_acquire_init - initialize acquire context
+ * @ctx: the acquire context
+ * @flags: for future
+ */
+void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
+               uint32_t flags)
+{
+       ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
+       INIT_LIST_HEAD(&ctx->locked);
+}
+EXPORT_SYMBOL(drm_modeset_acquire_init);
+
+/**
+ * drm_modeset_acquire_fini - cleanup acquire context
+ * @ctx: the acquire context
+ */
+void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx)
+{
+       ww_acquire_fini(&ctx->ww_ctx);
+}
+EXPORT_SYMBOL(drm_modeset_acquire_fini);
+
+/**
+ * drm_modeset_drop_locks - drop all locks
+ * @ctx: the acquire context
+ *
+ * Drop all locks currently held against this acquire context.
+ */
+void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
+{
+       WARN_ON(ctx->contended);
+       while (!list_empty(&ctx->locked)) {
+               struct drm_modeset_lock *lock;
+
+               lock = list_first_entry(&ctx->locked,
+                               struct drm_modeset_lock, head);
+
+               drm_modeset_unlock(lock);
+       }
+}
+EXPORT_SYMBOL(drm_modeset_drop_locks);
+
+static inline int modeset_lock(struct drm_modeset_lock *lock,
+               struct drm_modeset_acquire_ctx *ctx,
+               bool interruptible, bool slow)
+{
+       int ret;
+
+       WARN_ON(ctx->contended);
+
+       if (interruptible && slow) {
+               ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx);
+       } else if (interruptible) {
+               ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx);
+       } else if (slow) {
+               ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx);
+               ret = 0;
+       } else {
+               ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx);
+       }
+       if (!ret) {
+               WARN_ON(!list_empty(&lock->head));
+               list_add(&lock->head, &ctx->locked);
+       } else if (ret == -EALREADY) {
+               /* we already hold the lock.. this is fine.  For atomic
+                * we will need to be able to drm_modeset_lock() things
+                * without having to keep track of what is already locked
+                * or not.
+                */
+               ret = 0;
+       } else if (ret == -EDEADLK) {
+               ctx->contended = lock;
+       }
+
+       return ret;
+}
+
+static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx,
+               bool interruptible)
+{
+       struct drm_modeset_lock *contended = ctx->contended;
+
+       ctx->contended = NULL;
+
+       if (WARN_ON(!contended))
+               return 0;
+
+       drm_modeset_drop_locks(ctx);
+
+       return modeset_lock(contended, ctx, interruptible, true);
+}
+
+/**
+ * drm_modeset_backoff - deadlock avoidance backoff
+ * @ctx: the acquire context
+ *
+ * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK),
+ * you must call this function to drop all currently held locks and
+ * block until the contended lock becomes available.
+ */
+void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
+{
+       modeset_backoff(ctx, false);
+}
+EXPORT_SYMBOL(drm_modeset_backoff);
+
+/**
+ * drm_modeset_backoff_interruptible - deadlock avoidance backoff
+ * @ctx: the acquire context
+ *
+ * Interruptible version of drm_modeset_backoff()
+ */
+int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx)
+{
+       return modeset_backoff(ctx, true);
+}
+EXPORT_SYMBOL(drm_modeset_backoff_interruptible);
+
+/**
+ * drm_modeset_lock - take modeset lock
+ * @lock: lock to take
+ * @ctx: acquire ctx
+ *
+ * If ctx is not NULL, then its ww acquire context is used and the
+ * lock will be tracked by the context and can be released by calling
+ * drm_modeset_drop_locks().  If -EDEADLK is returned, this means a
+ * deadlock scenario has been detected and it is an error to attempt
+ * to take any more locks without first calling drm_modeset_backoff().
+ */
+int drm_modeset_lock(struct drm_modeset_lock *lock,
+               struct drm_modeset_acquire_ctx *ctx)
+{
+       if (ctx)
+               return modeset_lock(lock, ctx, false, false);
+
+       ww_mutex_lock(&lock->mutex, NULL);
+       return 0;
+}
+EXPORT_SYMBOL(drm_modeset_lock);
+
+/**
+ * drm_modeset_lock_interruptible - take modeset lock
+ * @lock: lock to take
+ * @ctx: acquire ctx
+ *
+ * Interruptible version of drm_modeset_lock()
+ */
+int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
+               struct drm_modeset_acquire_ctx *ctx)
+{
+       if (ctx)
+               return modeset_lock(lock, ctx, true, false);
+
+       return ww_mutex_lock_interruptible(&lock->mutex, NULL);
+}
+EXPORT_SYMBOL(drm_modeset_lock_interruptible);
+
+/**
+ * drm_modeset_unlock - drop modeset lock
+ * @lock: lock to release
+ */
+void drm_modeset_unlock(struct drm_modeset_lock *lock)
+{
+       list_del_init(&lock->head);
+       ww_mutex_unlock(&lock->mutex);
+}
+EXPORT_SYMBOL(drm_modeset_unlock);
+
+/* Temporary.. until we have sufficiently fine grained locking, there
+ * are a couple scenarios where it is convenient to grab all crtc locks.
+ * It is planned to remove this:
+ */
+int drm_modeset_lock_all_crtcs(struct drm_device *dev,
+               struct drm_modeset_acquire_ctx *ctx)
+{
+       struct drm_mode_config *config = &dev->mode_config;
+       struct drm_crtc *crtc;
+       int ret = 0;
+
+       list_for_each_entry(crtc, &config->crtc_list, head) {
+               ret = drm_modeset_lock(&crtc->mutex, ctx);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_modeset_lock_all_crtcs);
index 9c696a5ad74de262244be248549f99173f4c3806..020cfd9348541107774a3bcb5fd2a9ab2d9cb539 100644 (file)
@@ -1,17 +1,3 @@
-/* drm_pci.h -- PCI DMA memory management wrappers for DRM -*- linux-c -*- */
-/**
- * \file drm_pci.c
- * \brief Functions and ioctls to manage PCI memory
- *
- * \warning These interfaces aren't stable yet.
- *
- * \todo Implement the remaining ioctl's for the PCI pools.
- * \todo The wrappers here are so thin that they would be better off inlined..
- *
- * \author José Fonseca <jrfonseca@tungstengraphics.com>
- * \author Leif Delgass <ldelgass@retinalburn.net>
- */
-
 /*
  * Copyright 2003 José Fonseca.
  * Copyright 2003 Leif Delgass.
 #include <linux/export.h>
 #include <drm/drmP.h>
 
-/**********************************************************************/
-/** \name PCI memory */
-/*@{*/
-
 /**
- * \brief Allocate a PCI consistent memory block, for DMA.
+ * drm_pci_alloc - Allocate a PCI consistent memory block, for DMA.
+ * @dev: DRM device
+ * @size: size of block to allocate
+ * @align: alignment of block
+ *
+ * Return: A handle to the allocated memory block on success or NULL on
+ * failure.
  */
 drm_dma_handle_t *drm_pci_alloc(struct drm_device * dev, size_t size, size_t align)
 {
@@ -88,8 +76,8 @@ drm_dma_handle_t *drm_pci_alloc(struct drm_device * dev, size_t size, size_t ali
 
 EXPORT_SYMBOL(drm_pci_alloc);
 
-/**
- * \brief Free a PCI consistent memory block without freeing its descriptor.
+/*
+ * Free a PCI consistent memory block without freeing its descriptor.
  *
  * This function is for internal use in the Linux-specific DRM core code.
  */
@@ -111,7 +99,9 @@ void __drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah)
 }
 
 /**
- * \brief Free a PCI consistent memory block
+ * drm_pci_free - Free a PCI consistent memory block
+ * @dev: DRM device
+ * @dmah: handle to memory block
  */
 void drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah)
 {
@@ -137,21 +127,9 @@ static int drm_get_pci_domain(struct drm_device *dev)
        return pci_domain_nr(dev->pdev->bus);
 }
 
-static int drm_pci_get_irq(struct drm_device *dev)
-{
-       return dev->pdev->irq;
-}
-
-static const char *drm_pci_get_name(struct drm_device *dev)
-{
-       struct pci_driver *pdriver = dev->driver->kdriver.pci;
-       return pdriver->name;
-}
-
 static int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master)
 {
        int len, ret;
-       struct pci_driver *pdriver = dev->driver->kdriver.pci;
        master->unique_len = 40;
        master->unique_size = master->unique_len;
        master->unique = kmalloc(master->unique_size, GFP_KERNEL);
@@ -173,29 +151,16 @@ static int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master)
        } else
                master->unique_len = len;
 
-       dev->devname =
-               kmalloc(strlen(pdriver->name) +
-                       master->unique_len + 2, GFP_KERNEL);
-
-       if (dev->devname == NULL) {
-               ret = -ENOMEM;
-               goto err;
-       }
-
-       sprintf(dev->devname, "%s@%s", pdriver->name,
-               master->unique);
-
        return 0;
 err:
        return ret;
 }
 
-static int drm_pci_set_unique(struct drm_device *dev,
-                             struct drm_master *master,
-                             struct drm_unique *u)
+int drm_pci_set_unique(struct drm_device *dev,
+                      struct drm_master *master,
+                      struct drm_unique *u)
 {
        int domain, bus, slot, func, ret;
-       const char *bus_name;
 
        master->unique_len = u->unique_len;
        master->unique_size = u->unique_len + 1;
@@ -212,17 +177,6 @@ static int drm_pci_set_unique(struct drm_device *dev,
 
        master->unique[master->unique_len] = '\0';
 
-       bus_name = dev->driver->bus->get_name(dev);
-       dev->devname = kmalloc(strlen(bus_name) +
-                              strlen(master->unique) + 2, GFP_KERNEL);
-       if (!dev->devname) {
-               ret = -ENOMEM;
-               goto err;
-       }
-
-       sprintf(dev->devname, "%s@%s", bus_name,
-               master->unique);
-
        /* Return error if the busid submitted doesn't match the device's actual
         * busid.
         */
@@ -247,7 +201,6 @@ err:
        return ret;
 }
 
-
 static int drm_pci_irq_by_busid(struct drm_device *dev, struct drm_irq_busid *p)
 {
        if ((p->busnum >> 8) != drm_get_pci_domain(dev) ||
@@ -262,6 +215,36 @@ static int drm_pci_irq_by_busid(struct drm_device *dev, struct drm_irq_busid *p)
        return 0;
 }
 
+/**
+ * drm_irq_by_busid - Get interrupt from bus ID
+ * @dev: DRM device
+ * @data: IOCTL parameter pointing to a drm_irq_busid structure
+ * @file_priv: DRM file private.
+ *
+ * Finds the PCI device with the specified bus id and gets its IRQ number.
+ * This IOCTL is deprecated, and will now return EINVAL for any busid not equal
+ * to that of the device that this DRM instance attached to.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int drm_irq_by_busid(struct drm_device *dev, void *data,
+                    struct drm_file *file_priv)
+{
+       struct drm_irq_busid *p = data;
+
+       if (drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       /* UMS was only ever support on PCI devices. */
+       if (WARN_ON(!dev->pdev))
+               return -EINVAL;
+
+       if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
+               return -EINVAL;
+
+       return drm_pci_irq_by_busid(dev, p);
+}
+
 static void drm_pci_agp_init(struct drm_device *dev)
 {
        if (drm_core_check_feature(dev, DRIVER_USE_AGP)) {
@@ -287,24 +270,20 @@ void drm_pci_agp_destroy(struct drm_device *dev)
 }
 
 static struct drm_bus drm_pci_bus = {
-       .bus_type = DRIVER_BUS_PCI,
-       .get_irq = drm_pci_get_irq,
-       .get_name = drm_pci_get_name,
        .set_busid = drm_pci_set_busid,
-       .set_unique = drm_pci_set_unique,
-       .irq_by_busid = drm_pci_irq_by_busid,
 };
 
 /**
- * Register.
- *
- * \param pdev - PCI device structure
- * \param ent entry from the PCI ID table with device type flags
- * \return zero on success or a negative number on failure.
+ * drm_get_pci_dev - Register a PCI device with the DRM subsystem
+ * @pdev: PCI device
+ * @ent: entry from the PCI ID table that matches @pdev
+ * @driver: DRM device driver
  *
  * Attempt to gets inter module "drm" information. If we are first
  * then register the character device and inter module information.
  * Try and register, if we fail to register, backout previous work.
+ *
+ * Return: 0 on success or a negative error code on failure.
  */
 int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
                    struct drm_driver *driver)
@@ -357,15 +336,14 @@ err_free:
 EXPORT_SYMBOL(drm_get_pci_dev);
 
 /**
- * PCI device initialization. Called direct from modules at load time.
- *
- * \return zero on success or a negative number on failure.
+ * drm_pci_init - Register matching PCI devices with the DRM subsystem
+ * @driver: DRM device driver
+ * @pdriver: PCI device driver
  *
- * Initializes a drm_device structures,registering the
- * stubs and initializing the AGP device.
+ * Initializes a drm_device structures, registering the stubs and initializing
+ * the AGP device.
  *
- * Expands the \c DRIVER_PREINIT and \c DRIVER_POST_INIT macros before and
- * after the initialization for driver customization.
+ * Return: 0 on success or a negative error code on failure.
  */
 int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
 {
@@ -375,7 +353,6 @@ int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
 
        DRM_DEBUG("\n");
 
-       driver->kdriver.pci = pdriver;
        driver->bus = &drm_pci_bus;
 
        if (driver->driver_features & DRIVER_MODESET)
@@ -453,11 +430,31 @@ int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
 }
 
 void drm_pci_agp_destroy(struct drm_device *dev) {}
+
+int drm_irq_by_busid(struct drm_device *dev, void *data,
+                    struct drm_file *file_priv)
+{
+       return -EINVAL;
+}
+
+int drm_pci_set_unique(struct drm_device *dev,
+                      struct drm_master *master,
+                      struct drm_unique *u)
+{
+       return -EINVAL;
+}
 #endif
 
 EXPORT_SYMBOL(drm_pci_init);
 
-/*@}*/
+/**
+ * drm_pci_exit - Unregister matching PCI devices from the DRM subsystem
+ * @driver: DRM device driver
+ * @pdriver: PCI device driver
+ *
+ * Unregisters one or more devices matched by a PCI driver from the DRM
+ * subsystem.
+ */
 void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver)
 {
        struct drm_device *dev, *tmp;
index d2b1c03b3d7113f03027199107732d76acdf58ea..6d133149cc74873fd593aff0ae1aaa9362c29c31 100644 (file)
@@ -25,7 +25,9 @@
 
 #include <linux/list.h>
 #include <drm/drmP.h>
+#include <drm/drm_plane_helper.h>
 #include <drm/drm_rect.h>
+#include <drm/drm_plane_helper.h>
 
 #define SUBPIXEL_MASK 0xffff
 
@@ -36,9 +38,9 @@
  * creating the primary plane.  However drivers that still call
  * drm_plane_init() will use this minimal format list as the default.
  */
-const static uint32_t safe_modeset_formats[] = {
-       DRM_FORMAT_XRGB8888,
-       DRM_FORMAT_ARGB8888,
+static const uint32_t safe_modeset_formats[] = {
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_ARGB8888,
 };
 
 /*
@@ -54,6 +56,13 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc,
        struct drm_connector *connector;
        int count = 0;
 
+       /*
+        * Note: Once we change the plane hooks to more fine-grained locking we
+        * need to grab the connection_mutex here to be able to make these
+        * checks.
+        */
+       WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
+
        list_for_each_entry(connector, &dev->mode_config.connector_list, head)
                if (connector->encoder && connector->encoder->crtc == crtc) {
                        if (connector_list != NULL && count < num_connectors)
@@ -65,6 +74,79 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc,
        return count;
 }
 
+/**
+ * drm_plane_helper_check_update() - Check plane update for validity
+ * @plane: plane object to update
+ * @crtc: owning CRTC of owning plane
+ * @fb: framebuffer to flip onto plane
+ * @src: source coordinates in 16.16 fixed point
+ * @dest: integer destination coordinates
+ * @clip: integer clipping coordinates
+ * @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point
+ * @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point
+ * @can_position: is it legal to position the plane such that it
+ *                doesn't cover the entire crtc?  This will generally
+ *                only be false for primary planes.
+ * @can_update_disabled: can the plane be updated while the crtc
+ *                       is disabled?
+ * @visible: output parameter indicating whether plane is still visible after
+ *           clipping
+ *
+ * Checks that a desired plane update is valid.  Drivers that provide
+ * their own plane handling rather than helper-provided implementations may
+ * still wish to call this function to avoid duplication of error checking
+ * code.
+ *
+ * RETURNS:
+ * Zero if update appears valid, error code on failure
+ */
+int drm_plane_helper_check_update(struct drm_plane *plane,
+                                   struct drm_crtc *crtc,
+                                   struct drm_framebuffer *fb,
+                                   struct drm_rect *src,
+                                   struct drm_rect *dest,
+                                   const struct drm_rect *clip,
+                                   int min_scale,
+                                   int max_scale,
+                                   bool can_position,
+                                   bool can_update_disabled,
+                                   bool *visible)
+{
+       int hscale, vscale;
+
+       if (!crtc->enabled && !can_update_disabled) {
+               DRM_DEBUG_KMS("Cannot update plane of a disabled CRTC.\n");
+               return -EINVAL;
+       }
+
+       /* Check scaling */
+       hscale = drm_rect_calc_hscale(src, dest, min_scale, max_scale);
+       vscale = drm_rect_calc_vscale(src, dest, min_scale, max_scale);
+       if (hscale < 0 || vscale < 0) {
+               DRM_DEBUG_KMS("Invalid scaling of plane\n");
+               return -ERANGE;
+       }
+
+       *visible = drm_rect_clip_scaled(src, dest, clip, hscale, vscale);
+       if (!*visible)
+               /*
+                * Plane isn't visible; some drivers can handle this
+                * so we just return success here.  Drivers that can't
+                * (including those that use the primary plane helper's
+                * update function) will return an error from their
+                * update_plane handler.
+                */
+               return 0;
+
+       if (!can_position && !drm_rect_equals(dest, clip)) {
+               DRM_DEBUG_KMS("Plane must cover entire CRTC\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_plane_helper_check_update);
+
 /**
  * drm_primary_helper_update() - Helper for primary plane update
  * @plane: plane object to update
@@ -113,56 +195,42 @@ int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc,
                .x = src_x >> 16,
                .y = src_y >> 16,
        };
+       struct drm_rect src = {
+               .x1 = src_x,
+               .y1 = src_y,
+               .x2 = src_x + src_w,
+               .y2 = src_y + src_h,
+       };
        struct drm_rect dest = {
                .x1 = crtc_x,
                .y1 = crtc_y,
                .x2 = crtc_x + crtc_w,
                .y2 = crtc_y + crtc_h,
        };
-       struct drm_rect clip = {
+       const struct drm_rect clip = {
                .x2 = crtc->mode.hdisplay,
                .y2 = crtc->mode.vdisplay,
        };
        struct drm_connector **connector_list;
-       struct drm_framebuffer *tmpfb;
        int num_connectors, ret;
+       bool visible;
 
-       if (!crtc->enabled) {
-               DRM_DEBUG_KMS("Cannot update primary plane of a disabled CRTC.\n");
-               return -EINVAL;
-       }
-
-       /* Disallow subpixel positioning */
-       if ((src_x | src_y | src_w | src_h) & SUBPIXEL_MASK) {
-               DRM_DEBUG_KMS("Primary plane does not support subpixel positioning\n");
-               return -EINVAL;
-       }
-
-       /* Primary planes are locked to their owning CRTC */
-       if (plane->possible_crtcs != drm_crtc_mask(crtc)) {
-               DRM_DEBUG_KMS("Cannot change primary plane CRTC\n");
-               return -EINVAL;
-       }
-
-       /* Disallow scaling */
-       if (crtc_w != src_w || crtc_h != src_h) {
-               DRM_DEBUG_KMS("Can't scale primary plane\n");
-               return -EINVAL;
-       }
-
-       /* Make sure primary plane covers entire CRTC */
-       drm_rect_intersect(&dest, &clip);
-       if (dest.x1 != 0 || dest.y1 != 0 ||
-           dest.x2 != crtc->mode.hdisplay || dest.y2 != crtc->mode.vdisplay) {
-               DRM_DEBUG_KMS("Primary plane must cover entire CRTC\n");
-               return -EINVAL;
-       }
-
-       /* Framebuffer must be big enough to cover entire plane */
-       ret = drm_crtc_check_viewport(crtc, crtc_x, crtc_y, &crtc->mode, fb);
+       ret = drm_plane_helper_check_update(plane, crtc, fb,
+                                           &src, &dest, &clip,
+                                           DRM_PLANE_HELPER_NO_SCALING,
+                                           DRM_PLANE_HELPER_NO_SCALING,
+                                           false, false, &visible);
        if (ret)
                return ret;
 
+       if (!visible)
+               /*
+                * Primary plane isn't visible.  Note that unless a driver
+                * provides their own disable function, this will just
+                * wind up returning -EINVAL to userspace.
+                */
+               return plane->funcs->disable_plane(plane);
+
        /* Find current connectors for CRTC */
        num_connectors = get_connectors_for_crtc(crtc, NULL, 0);
        BUG_ON(num_connectors == 0);
@@ -176,21 +244,14 @@ int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc,
        set.num_connectors = num_connectors;
 
        /*
-        * set_config() adjusts crtc->primary->fb; however the DRM setplane
-        * code that called us expects to handle the framebuffer update and
-        * reference counting; save and restore the current fb before
-        * calling it.
-        *
-        * N.B., we call set_config() directly here rather than using
+        * We call set_config() directly here rather than using
         * drm_mode_set_config_internal.  We're reprogramming the same
         * connectors that were already in use, so we shouldn't need the extra
         * cross-CRTC fb refcounting to accomodate stealing connectors.
         * drm_mode_setplane() already handles the basic refcounting for the
         * framebuffers involved in this operation.
         */
-       tmpfb = plane->fb;
        ret = crtc->funcs->set_config(&set);
-       plane->fb = tmpfb;
 
        kfree(connector_list);
        return ret;
@@ -232,7 +293,6 @@ EXPORT_SYMBOL(drm_primary_helper_disable);
  */
 void drm_primary_helper_destroy(struct drm_plane *plane)
 {
-       plane->funcs->disable_plane(plane);
        drm_plane_cleanup(plane);
        kfree(plane);
 }
index 319ff538560119beda233eaff71d007d7aae3897..d5b76f148c12aa2b214b9a4185d6e302eb3de71d 100644 (file)
@@ -68,16 +68,6 @@ err_free:
        return ret;
 }
 
-static int drm_platform_get_irq(struct drm_device *dev)
-{
-       return platform_get_irq(dev->platformdev, 0);
-}
-
-static const char *drm_platform_get_name(struct drm_device *dev)
-{
-       return dev->platformdev->name;
-}
-
 static int drm_platform_set_busid(struct drm_device *dev, struct drm_master *master)
 {
        int len, ret, id;
@@ -106,46 +96,30 @@ static int drm_platform_set_busid(struct drm_device *dev, struct drm_master *mas
                goto err;
        }
 
-       dev->devname =
-               kmalloc(strlen(dev->platformdev->name) +
-                       master->unique_len + 2, GFP_KERNEL);
-
-       if (dev->devname == NULL) {
-               ret = -ENOMEM;
-               goto err;
-       }
-
-       sprintf(dev->devname, "%s@%s", dev->platformdev->name,
-               master->unique);
        return 0;
 err:
        return ret;
 }
 
 static struct drm_bus drm_platform_bus = {
-       .bus_type = DRIVER_BUS_PLATFORM,
-       .get_irq = drm_platform_get_irq,
-       .get_name = drm_platform_get_name,
        .set_busid = drm_platform_set_busid,
 };
 
 /**
- * Platform device initialization. Called direct from modules.
+ * drm_platform_init - Register a platform device with the DRM subsystem
+ * @driver: DRM device driver
+ * @platform_device: platform device to register
  *
- * \return zero on success or a negative number on failure.
- *
- * Initializes a drm_device structures,registering the
- * stubs
+ * Registers the specified DRM device driver and platform device with the DRM
+ * subsystem, initializing a drm_device structure and calling the driver's
+ * .load() function.
  *
- * Expands the \c DRIVER_PREINIT and \c DRIVER_POST_INIT macros before and
- * after the initialization for driver customization.
+ * Return: 0 on success or a negative error code on failure.
  */
-
 int drm_platform_init(struct drm_driver *driver, struct platform_device *platform_device)
 {
        DRM_DEBUG("\n");
 
-       driver->kdriver.platform_device = platform_device;
        driver->bus = &drm_platform_bus;
        return drm_get_platform_dev(platform_device, driver);
 }
index e70f54d4a5810cec31f2c09a9a5e81a0bb1fe343..d22676b89cbb337147ec319fe5af4963e5e9f1d8 100644 (file)
@@ -82,26 +82,8 @@ static void drm_mode_validate_flag(struct drm_connector *connector,
        return;
 }
 
-/**
- * drm_helper_probe_single_connector_modes - get complete set of display modes
- * @connector: connector to probe
- * @maxX: max width for modes
- * @maxY: max height for modes
- *
- * Based on the helper callbacks implemented by @connector try to detect all
- * valid modes.  Modes will first be added to the connector's probed_modes list,
- * then culled (based on validity and the @maxX, @maxY parameters) and put into
- * the normal modes list.
- *
- * Intended to be use as a generic implementation of the ->fill_modes()
- * @connector vfunc for drivers that use the crtc helpers for output mode
- * filtering and detection.
- *
- * Returns:
- * The number of modes found on @connector.
- */
-int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
-                                           uint32_t maxX, uint32_t maxY)
+static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connector *connector,
+                                                             uint32_t maxX, uint32_t maxY, bool merge_type_bits)
 {
        struct drm_device *dev = connector->dev;
        struct drm_display_mode *mode;
@@ -114,7 +96,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
        WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id,
-                       drm_get_connector_name(connector));
+                       connector->name);
        /* set all modes to the unverified state */
        list_for_each_entry(mode, &connector->modes, head)
                mode->status = MODE_UNVERIFIED;
@@ -138,7 +120,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
 
        if (connector->status == connector_status_disconnected) {
                DRM_DEBUG_KMS("[CONNECTOR:%d:%s] disconnected\n",
-                       connector->base.id, drm_get_connector_name(connector));
+                       connector->base.id, connector->name);
                drm_mode_connector_update_edid_property(connector, NULL);
                verbose_prune = false;
                goto prune;
@@ -155,7 +137,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
        if (count == 0)
                goto prune;
 
-       drm_mode_connector_list_update(connector);
+       drm_mode_connector_list_update(connector, merge_type_bits);
 
        if (maxX && maxY)
                drm_mode_validate_size(dev, &connector->modes, maxX, maxY);
@@ -169,7 +151,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
        drm_mode_validate_flag(connector, mode_flags);
 
        list_for_each_entry(mode, &connector->modes, head) {
-               if (mode->status == MODE_OK)
+               if (mode->status == MODE_OK && connector_funcs->mode_valid)
                        mode->status = connector_funcs->mode_valid(connector,
                                                                   mode);
        }
@@ -186,7 +168,7 @@ prune:
        drm_mode_sort(&connector->modes);
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s] probed modes :\n", connector->base.id,
-                       drm_get_connector_name(connector));
+                       connector->name);
        list_for_each_entry(mode, &connector->modes, head) {
                drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
                drm_mode_debug_printmodeline(mode);
@@ -194,8 +176,48 @@ prune:
 
        return count;
 }
+
+/**
+ * drm_helper_probe_single_connector_modes - get complete set of display modes
+ * @connector: connector to probe
+ * @maxX: max width for modes
+ * @maxY: max height for modes
+ *
+ * Based on the helper callbacks implemented by @connector try to detect all
+ * valid modes.  Modes will first be added to the connector's probed_modes list,
+ * then culled (based on validity and the @maxX, @maxY parameters) and put into
+ * the normal modes list.
+ *
+ * Intended to be use as a generic implementation of the ->fill_modes()
+ * @connector vfunc for drivers that use the crtc helpers for output mode
+ * filtering and detection.
+ *
+ * Returns:
+ * The number of modes found on @connector.
+ */
+int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
+                                           uint32_t maxX, uint32_t maxY)
+{
+       return drm_helper_probe_single_connector_modes_merge_bits(connector, maxX, maxY, true);
+}
 EXPORT_SYMBOL(drm_helper_probe_single_connector_modes);
 
+/**
+ * drm_helper_probe_single_connector_modes_nomerge - get complete set of display modes
+ * @connector: connector to probe
+ * @maxX: max width for modes
+ * @maxY: max height for modes
+ *
+ * This operates like drm_hehlper_probe_single_connector_modes except it
+ * replaces the mode bits instead of merging them for preferred modes.
+ */
+int drm_helper_probe_single_connector_modes_nomerge(struct drm_connector *connector,
+                                           uint32_t maxX, uint32_t maxY)
+{
+       return drm_helper_probe_single_connector_modes_merge_bits(connector, maxX, maxY, false);
+}
+EXPORT_SYMBOL(drm_helper_probe_single_connector_modes_nomerge);
+
 /**
  * drm_kms_helper_hotplug_event - fire off KMS hotplug events
  * @dev: drm_device whose connector state changed
@@ -264,7 +286,7 @@ static void output_poll_execute(struct work_struct *work)
                        DRM_DEBUG_KMS("[CONNECTOR:%d:%s] "
                                      "status updated from %s to %s\n",
                                      connector->base.id,
-                                     drm_get_connector_name(connector),
+                                     connector->name,
                                      old, new);
 
                        changed = true;
@@ -409,7 +431,7 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev)
                connector->status = connector->funcs->detect(connector, false);
                DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n",
                              connector->base.id,
-                             drm_get_connector_name(connector),
+                             connector->name,
                              drm_get_connector_status_name(old_status),
                              drm_get_connector_status_name(connector->status));
                if (old_status != connector->status)
index 4c24c3ac1efaf28225aa635834e982c9f5a6055b..14d16464000ac3c83d2c227094fa0643f691772b 100644 (file)
@@ -1,16 +1,11 @@
-/**
- * \file drm_stub.h
- * Stub support
- *
- * \author Rickard E. (Rik) Faith <faith@valinux.com>
- */
-
 /*
  * Created: Fri Jan 19 10:48:35 2001 by faith@acm.org
  *
  * Copyright 2001 VA Linux Systems, Inc., Sunnyvale, California.
  * All Rights Reserved.
  *
+ * Author Rickard E. (Rik) Faith <faith@valinux.com>
+ *
  * 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
@@ -128,7 +123,10 @@ struct drm_master *drm_master_create(struct drm_minor *minor)
        kref_init(&master->refcount);
        spin_lock_init(&master->lock.spinlock);
        init_waitqueue_head(&master->lock.lock_queue);
-       drm_ht_create(&master->magiclist, DRM_MAGIC_HASH_ORDER);
+       if (drm_ht_create(&master->magiclist, DRM_MAGIC_HASH_ORDER)) {
+               kfree(master);
+               return NULL;
+       }
        INIT_LIST_HEAD(&master->magicfree);
        master->minor = minor;
 
@@ -166,9 +164,6 @@ static void drm_master_destroy(struct kref *kref)
                master->unique_len = 0;
        }
 
-       kfree(dev->devname);
-       dev->devname = NULL;
-
        list_for_each_entry_safe(pt, next, &master->magicfree, head) {
                list_del(&pt->head);
                drm_ht_remove_item(&master->magiclist, &pt->hash_item);
@@ -294,6 +289,7 @@ static void drm_minor_free(struct drm_device *dev, unsigned int type)
 
        slot = drm_minor_get_slot(dev, type);
        if (*slot) {
+               drm_mode_group_destroy(&(*slot)->mode_group);
                kfree(*slot);
                *slot = NULL;
        }
@@ -424,11 +420,15 @@ void drm_minor_release(struct drm_minor *minor)
 }
 
 /**
- * Called via drm_exit() at module unload time or when pci device is
- * unplugged.
+ * drm_put_dev - Unregister and release a DRM device
+ * @dev: DRM device
  *
- * Cleans up all DRM device, calling drm_lastclose().
+ * Called at module unload time or when a PCI device is unplugged.
  *
+ * Use of this function is discouraged. It will eventually go away completely.
+ * Please use drm_dev_unregister() and drm_dev_unref() explicitly instead.
+ *
+ * Cleans up all DRM device, calling drm_lastclose().
  */
 void drm_put_dev(struct drm_device *dev)
 {
@@ -535,7 +535,7 @@ static void drm_fs_inode_free(struct inode *inode)
 }
 
 /**
- * drm_dev_alloc - Allocate new drm device
+ * drm_dev_alloc - Allocate new DRM device
  * @driver: DRM driver to allocate device for
  * @parent: Parent device object
  *
@@ -569,7 +569,7 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
        INIT_LIST_HEAD(&dev->maplist);
        INIT_LIST_HEAD(&dev->vblank_event_list);
 
-       spin_lock_init(&dev->count_lock);
+       spin_lock_init(&dev->buf_lock);
        spin_lock_init(&dev->event_lock);
        mutex_init(&dev->struct_mutex);
        mutex_init(&dev->ctxlist_mutex);
@@ -648,9 +648,8 @@ static void drm_dev_release(struct kref *ref)
        drm_minor_free(dev, DRM_MINOR_RENDER);
        drm_minor_free(dev, DRM_MINOR_CONTROL);
 
-       kfree(dev->devname);
-
        mutex_destroy(&dev->master_mutex);
+       kfree(dev->unique);
        kfree(dev);
 }
 
@@ -690,6 +689,7 @@ EXPORT_SYMBOL(drm_dev_unref);
 /**
  * drm_dev_register - Register DRM device
  * @dev: Device to register
+ * @flags: Flags passed to the driver's .load() function
  *
  * Register the DRM device @dev with the system, advertise device to user-space
  * and start normal device operation. @dev must be allocated via drm_dev_alloc()
@@ -778,3 +778,28 @@ void drm_dev_unregister(struct drm_device *dev)
        drm_minor_unregister(dev, DRM_MINOR_CONTROL);
 }
 EXPORT_SYMBOL(drm_dev_unregister);
+
+/**
+ * drm_dev_set_unique - Set the unique name of a DRM device
+ * @dev: device of which to set the unique name
+ * @fmt: format string for unique name
+ *
+ * Sets the unique name of a DRM device using the specified format string and
+ * a variable list of arguments. Drivers can use this at driver probe time if
+ * the unique name of the devices they drive is static.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int drm_dev_set_unique(struct drm_device *dev, const char *fmt, ...)
+{
+       va_list ap;
+
+       kfree(dev->unique);
+
+       va_start(ap, fmt);
+       dev->unique = kvasprintf(GFP_KERNEL, fmt, ap);
+       va_end(ap);
+
+       return dev->unique ? 0 : -ENOMEM;
+}
+EXPORT_SYMBOL(drm_dev_set_unique);
index c22c3097c3e857ba823cd9c339333983842c4ecf..369b26278e76601b519f19ef82fe771d297ea250 100644 (file)
@@ -380,9 +380,9 @@ int drm_sysfs_connector_add(struct drm_connector *connector)
 
        connector->kdev = device_create(drm_class, dev->primary->kdev,
                                        0, connector, "card%d-%s",
-                                       dev->primary->index, drm_get_connector_name(connector));
+                                       dev->primary->index, connector->name);
        DRM_DEBUG("adding \"%s\" to sysfs\n",
-                 drm_get_connector_name(connector));
+                 connector->name);
 
        if (IS_ERR(connector->kdev)) {
                DRM_ERROR("failed to register connector device: %ld\n", PTR_ERR(connector->kdev));
@@ -460,7 +460,7 @@ void drm_sysfs_connector_remove(struct drm_connector *connector)
        if (!connector->kdev)
                return;
        DRM_DEBUG("removing \"%s\" from sysfs\n",
-                 drm_get_connector_name(connector));
+                 connector->name);
 
        for (i = 0; i < ARRAY_SIZE(connector_attrs); i++)
                device_remove_file(connector->kdev, &connector_attrs[i]);
index c3406aad294463718ccdaf1d83fc7636a0322b17..f2fe94aab901a8edbb6cbac827bad1e27a15b242 100644 (file)
@@ -36,16 +36,6 @@ err_free:
 }
 EXPORT_SYMBOL(drm_get_usb_dev);
 
-static int drm_usb_get_irq(struct drm_device *dev)
-{
-       return 0;
-}
-
-static const char *drm_usb_get_name(struct drm_device *dev)
-{
-       return "USB";
-}
-
 static int drm_usb_set_busid(struct drm_device *dev,
                               struct drm_master *master)
 {
@@ -53,18 +43,24 @@ static int drm_usb_set_busid(struct drm_device *dev,
 }
 
 static struct drm_bus drm_usb_bus = {
-       .bus_type = DRIVER_BUS_USB,
-       .get_irq = drm_usb_get_irq,
-       .get_name = drm_usb_get_name,
        .set_busid = drm_usb_set_busid,
 };
-    
+
+/**
+ * drm_usb_init - Register matching USB devices with the DRM subsystem
+ * @driver: DRM device driver
+ * @udriver: USB device driver
+ *
+ * Registers one or more devices matched by a USB driver with the DRM
+ * subsystem.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
 int drm_usb_init(struct drm_driver *driver, struct usb_driver *udriver)
 {
        int res;
        DRM_DEBUG("\n");
 
-       driver->kdriver.usb = udriver;
        driver->bus = &drm_usb_bus;
 
        res = usb_register(udriver);
@@ -72,6 +68,14 @@ int drm_usb_init(struct drm_driver *driver, struct usb_driver *udriver)
 }
 EXPORT_SYMBOL(drm_usb_init);
 
+/**
+ * drm_usb_exit - Unregister matching USB devices from the DRM subsystem
+ * @driver: DRM device driver
+ * @udriver: USB device driver
+ *
+ * Unregisters one or more devices matched by a USB driver from the DRM
+ * subsystem.
+ */
 void drm_usb_exit(struct drm_driver *driver,
                  struct usb_driver *udriver)
 {
index 5bf5bca94f562850ad96a9dcb654766d7632999a..178d2a9672a8245020f39f0059f013483a5a5169 100644 (file)
@@ -26,14 +26,14 @@ config DRM_EXYNOS_DMABUF
 
 config DRM_EXYNOS_FIMD
        bool "Exynos DRM FIMD"
-       depends on DRM_EXYNOS && !FB_S3C && !ARCH_MULTIPLATFORM
+       depends on DRM_EXYNOS && !FB_S3C
        select FB_MODE_HELPERS
        help
          Choose this option if you want to use Exynos FIMD for DRM.
 
 config DRM_EXYNOS_DPI
        bool "EXYNOS DRM parallel output support"
-       depends on DRM_EXYNOS
+       depends on DRM_EXYNOS_FIMD
        select DRM_PANEL
        default n
        help
@@ -41,7 +41,7 @@ config DRM_EXYNOS_DPI
 
 config DRM_EXYNOS_DSI
        bool "EXYNOS DRM MIPI-DSI driver support"
-       depends on DRM_EXYNOS
+       depends on DRM_EXYNOS_FIMD
        select DRM_MIPI_DSI
        select DRM_PANEL
        default n
@@ -50,7 +50,7 @@ config DRM_EXYNOS_DSI
 
 config DRM_EXYNOS_DP
        bool "EXYNOS DRM DP driver support"
-       depends on DRM_EXYNOS && ARCH_EXYNOS
+       depends on DRM_EXYNOS_FIMD && ARCH_EXYNOS && (DRM_PTN3460=n || DRM_PTN3460=y || DRM_PTN3460=DRM_EXYNOS)
        default DRM_EXYNOS
        help
          This enables support for DP device.
diff --git a/drivers/gpu/drm/exynos/exynos_ddc.c b/drivers/gpu/drm/exynos/exynos_ddc.c
deleted file mode 100644 (file)
index 6a8c84e..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2011 Samsung Electronics Co.Ltd
- * Authors:
- *     Seung-Woo Kim <sw0312.kim@samsung.com>
- *     Inki Dae <inki.dae@samsung.com>
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of  the GNU General  Public License as published by the
- * Free Software Foundation;  either version 2 of the  License, or (at your
- * option) any later version.
- *
- */
-
-#include <drm/drmP.h>
-
-#include <linux/kernel.h>
-#include <linux/i2c.h>
-#include <linux/of.h>
-
-#include "exynos_drm_drv.h"
-#include "exynos_hdmi.h"
-
-static int s5p_ddc_probe(struct i2c_client *client,
-                       const struct i2c_device_id *dev_id)
-{
-       hdmi_attach_ddc_client(client);
-
-       dev_info(&client->adapter->dev,
-               "attached %s into i2c adapter successfully\n",
-               client->name);
-
-       return 0;
-}
-
-static int s5p_ddc_remove(struct i2c_client *client)
-{
-       dev_info(&client->adapter->dev,
-               "detached %s from i2c adapter successfully\n",
-               client->name);
-
-       return 0;
-}
-
-static struct of_device_id hdmiddc_match_types[] = {
-       {
-               .compatible = "samsung,exynos5-hdmiddc",
-       }, {
-               .compatible = "samsung,exynos4210-hdmiddc",
-       }, {
-               /* end node */
-       }
-};
-
-struct i2c_driver ddc_driver = {
-       .driver = {
-               .name = "exynos-hdmiddc",
-               .owner = THIS_MODULE,
-               .of_match_table = hdmiddc_match_types,
-       },
-       .probe          = s5p_ddc_probe,
-       .remove         = s5p_ddc_remove,
-       .command                = NULL,
-};
index aed533bbfd314e919d243aa07f5a4876fef66c2e..a8ffc8c1477b07e2ac4d90936872c0d1694818e7 100644 (file)
@@ -18,6 +18,9 @@
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/component.h>
 #include <linux/phy/phy.h>
 #include <video/of_display_timing.h>
 #include <video/of_videomode.h>
@@ -141,15 +144,15 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
                        return -EIO;
                }
 
-               exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_TEST_REQUEST,
+               exynos_dp_read_byte_from_dpcd(dp, DP_TEST_REQUEST,
                                        &test_vector);
-               if (test_vector & DPCD_TEST_EDID_READ) {
+               if (test_vector & DP_TEST_LINK_EDID_READ) {
                        exynos_dp_write_byte_to_dpcd(dp,
-                               DPCD_ADDR_TEST_EDID_CHECKSUM,
+                               DP_TEST_EDID_CHECKSUM,
                                edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]);
                        exynos_dp_write_byte_to_dpcd(dp,
-                               DPCD_ADDR_TEST_RESPONSE,
-                               DPCD_TEST_EDID_CHECKSUM_WRITE);
+                               DP_TEST_RESPONSE,
+                               DP_TEST_EDID_CHECKSUM_WRITE);
                }
        } else {
                dev_info(dp->dev, "EDID data does not include any extensions.\n");
@@ -171,15 +174,15 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
                }
 
                exynos_dp_read_byte_from_dpcd(dp,
-                       DPCD_ADDR_TEST_REQUEST,
+                       DP_TEST_REQUEST,
                        &test_vector);
-               if (test_vector & DPCD_TEST_EDID_READ) {
+               if (test_vector & DP_TEST_LINK_EDID_READ) {
                        exynos_dp_write_byte_to_dpcd(dp,
-                               DPCD_ADDR_TEST_EDID_CHECKSUM,
+                               DP_TEST_EDID_CHECKSUM,
                                edid[EDID_CHECKSUM]);
                        exynos_dp_write_byte_to_dpcd(dp,
-                               DPCD_ADDR_TEST_RESPONSE,
-                               DPCD_TEST_EDID_CHECKSUM_WRITE);
+                               DP_TEST_RESPONSE,
+                               DP_TEST_EDID_CHECKSUM_WRITE);
                }
        }
 
@@ -193,8 +196,8 @@ static int exynos_dp_handle_edid(struct exynos_dp_device *dp)
        int i;
        int retval;
 
-       /* Read DPCD DPCD_ADDR_DPCD_REV~RECEIVE_PORT1_CAP_1 */
-       retval = exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_DPCD_REV,
+       /* Read DPCD DP_DPCD_REV~RECEIVE_PORT1_CAP_1 */
+       retval = exynos_dp_read_bytes_from_dpcd(dp, DP_DPCD_REV,
                                12, buf);
        if (retval)
                return retval;
@@ -214,14 +217,14 @@ static void exynos_dp_enable_rx_to_enhanced_mode(struct exynos_dp_device *dp,
 {
        u8 data;
 
-       exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, &data);
+       exynos_dp_read_byte_from_dpcd(dp, DP_LANE_COUNT_SET, &data);
 
        if (enable)
-               exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET,
-                       DPCD_ENHANCED_FRAME_EN |
+               exynos_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET,
+                       DP_LANE_COUNT_ENHANCED_FRAME_EN |
                        DPCD_LANE_COUNT_SET(data));
        else
-               exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET,
+               exynos_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET,
                        DPCD_LANE_COUNT_SET(data));
 }
 
@@ -230,7 +233,7 @@ static int exynos_dp_is_enhanced_mode_available(struct exynos_dp_device *dp)
        u8 data;
        int retval;
 
-       exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LANE_COUNT, &data);
+       exynos_dp_read_byte_from_dpcd(dp, DP_MAX_LANE_COUNT, &data);
        retval = DPCD_ENHANCED_FRAME_CAP(data);
 
        return retval;
@@ -250,8 +253,8 @@ static void exynos_dp_training_pattern_dis(struct exynos_dp_device *dp)
        exynos_dp_set_training_pattern(dp, DP_NONE);
 
        exynos_dp_write_byte_to_dpcd(dp,
-               DPCD_ADDR_TRAINING_PATTERN_SET,
-               DPCD_TRAINING_PATTERN_DISABLED);
+               DP_TRAINING_PATTERN_SET,
+               DP_TRAINING_PATTERN_DISABLE);
 }
 
 static void exynos_dp_set_lane_lane_pre_emphasis(struct exynos_dp_device *dp,
@@ -295,7 +298,7 @@ static int exynos_dp_link_start(struct exynos_dp_device *dp)
        /* Setup RX configuration */
        buf[0] = dp->link_train.link_rate;
        buf[1] = dp->link_train.lane_count;
-       retval = exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_LINK_BW_SET,
+       retval = exynos_dp_write_bytes_to_dpcd(dp, DP_LINK_BW_SET,
                                2, buf);
        if (retval)
                return retval;
@@ -322,16 +325,16 @@ static int exynos_dp_link_start(struct exynos_dp_device *dp)
 
        /* Set RX training pattern */
        retval = exynos_dp_write_byte_to_dpcd(dp,
-                       DPCD_ADDR_TRAINING_PATTERN_SET,
-                       DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_1);
+                       DP_TRAINING_PATTERN_SET,
+                       DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_1);
        if (retval)
                return retval;
 
        for (lane = 0; lane < lane_count; lane++)
-               buf[lane] = DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 |
-                           DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0;
+               buf[lane] = DP_TRAIN_PRE_EMPHASIS_0 |
+                           DP_TRAIN_VOLTAGE_SWING_400;
 
-       retval = exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_TRAINING_LANE0_SET,
+       retval = exynos_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET,
                        lane_count, buf);
 
        return retval;
@@ -352,7 +355,7 @@ static int exynos_dp_clock_recovery_ok(u8 link_status[2], int lane_count)
 
        for (lane = 0; lane < lane_count; lane++) {
                lane_status = exynos_dp_get_lane_status(link_status, lane);
-               if ((lane_status & DPCD_LANE_CR_DONE) == 0)
+               if ((lane_status & DP_LANE_CR_DONE) == 0)
                        return -EINVAL;
        }
        return 0;
@@ -364,13 +367,13 @@ static int exynos_dp_channel_eq_ok(u8 link_status[2], u8 link_align,
        int lane;
        u8 lane_status;
 
-       if ((link_align & DPCD_INTERLANE_ALIGN_DONE) == 0)
+       if ((link_align & DP_INTERLANE_ALIGN_DONE) == 0)
                return -EINVAL;
 
        for (lane = 0; lane < lane_count; lane++) {
                lane_status = exynos_dp_get_lane_status(link_status, lane);
-               lane_status &= DPCD_CHANNEL_EQ_BITS;
-               if (lane_status != DPCD_CHANNEL_EQ_BITS)
+               lane_status &= DP_CHANNEL_EQ_BITS;
+               if (lane_status != DP_CHANNEL_EQ_BITS)
                        return -EINVAL;
        }
 
@@ -468,9 +471,9 @@ static void exynos_dp_get_adjust_training_lane(struct exynos_dp_device *dp,
                                DPCD_PRE_EMPHASIS_SET(pre_emphasis);
 
                if (voltage_swing == VOLTAGE_LEVEL_3)
-                       training_lane |= DPCD_MAX_SWING_REACHED;
+                       training_lane |= DP_TRAIN_MAX_SWING_REACHED;
                if (pre_emphasis == PRE_EMPHASIS_LEVEL_3)
-                       training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED;
+                       training_lane |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
 
                dp->link_train.training_lane[lane] = training_lane;
        }
@@ -487,12 +490,12 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
        lane_count = dp->link_train.lane_count;
 
        retval =  exynos_dp_read_bytes_from_dpcd(dp,
-                       DPCD_ADDR_LANE0_1_STATUS, 2, link_status);
+                       DP_LANE0_1_STATUS, 2, link_status);
        if (retval)
                return retval;
 
        retval =  exynos_dp_read_bytes_from_dpcd(dp,
-                       DPCD_ADDR_ADJUST_REQUEST_LANE0_1, 2, adjust_request);
+                       DP_ADJUST_REQUEST_LANE0_1, 2, adjust_request);
        if (retval)
                return retval;
 
@@ -501,9 +504,9 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
                exynos_dp_set_training_pattern(dp, TRAINING_PTN2);
 
                retval = exynos_dp_write_byte_to_dpcd(dp,
-                               DPCD_ADDR_TRAINING_PATTERN_SET,
-                               DPCD_SCRAMBLING_DISABLED |
-                               DPCD_TRAINING_PATTERN_2);
+                               DP_TRAINING_PATTERN_SET,
+                               DP_LINK_SCRAMBLING_DISABLE |
+                               DP_TRAINING_PATTERN_2);
                if (retval)
                        return retval;
 
@@ -543,7 +546,7 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
                        dp->link_train.training_lane[lane], lane);
 
        retval = exynos_dp_write_bytes_to_dpcd(dp,
-                       DPCD_ADDR_TRAINING_LANE0_SET, lane_count,
+                       DP_TRAINING_LANE0_SET, lane_count,
                        dp->link_train.training_lane);
        if (retval)
                return retval;
@@ -562,7 +565,7 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
        lane_count = dp->link_train.lane_count;
 
        retval = exynos_dp_read_bytes_from_dpcd(dp,
-                       DPCD_ADDR_LANE0_1_STATUS, 2, link_status);
+                       DP_LANE0_1_STATUS, 2, link_status);
        if (retval)
                return retval;
 
@@ -572,12 +575,12 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
        }
 
        retval = exynos_dp_read_bytes_from_dpcd(dp,
-                       DPCD_ADDR_ADJUST_REQUEST_LANE0_1, 2, adjust_request);
+                       DP_ADJUST_REQUEST_LANE0_1, 2, adjust_request);
        if (retval)
                return retval;
 
        retval = exynos_dp_read_byte_from_dpcd(dp,
-                       DPCD_ADDR_LANE_ALIGN_STATUS_UPDATED, &link_align);
+                       DP_LANE_ALIGN_STATUS_UPDATED, &link_align);
        if (retval)
                return retval;
 
@@ -619,7 +622,7 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
                exynos_dp_set_lane_link_training(dp,
                        dp->link_train.training_lane[lane], lane);
 
-       retval = exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_TRAINING_LANE0_SET,
+       retval = exynos_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET,
                        lane_count, dp->link_train.training_lane);
 
        return retval;
@@ -634,7 +637,7 @@ static void exynos_dp_get_max_rx_bandwidth(struct exynos_dp_device *dp,
         * For DP rev.1.1, Maximum link rate of Main Link lanes
         * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps
         */
-       exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LINK_RATE, &data);
+       exynos_dp_read_byte_from_dpcd(dp, DP_MAX_LINK_RATE, &data);
        *bandwidth = data;
 }
 
@@ -647,7 +650,7 @@ static void exynos_dp_get_max_rx_lane_count(struct exynos_dp_device *dp,
         * For DP rev.1.1, Maximum number of Main Link lanes
         * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes
         */
-       exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LANE_COUNT, &data);
+       exynos_dp_read_byte_from_dpcd(dp, DP_MAX_LANE_COUNT, &data);
        *lane_count = DPCD_MAX_LANE_COUNT(data);
 }
 
@@ -819,20 +822,20 @@ static void exynos_dp_enable_scramble(struct exynos_dp_device *dp, bool enable)
                exynos_dp_enable_scrambling(dp);
 
                exynos_dp_read_byte_from_dpcd(dp,
-                       DPCD_ADDR_TRAINING_PATTERN_SET,
+                       DP_TRAINING_PATTERN_SET,
                        &data);
                exynos_dp_write_byte_to_dpcd(dp,
-                       DPCD_ADDR_TRAINING_PATTERN_SET,
-                       (u8)(data & ~DPCD_SCRAMBLING_DISABLED));
+                       DP_TRAINING_PATTERN_SET,
+                       (u8)(data & ~DP_LINK_SCRAMBLING_DISABLE));
        } else {
                exynos_dp_disable_scrambling(dp);
 
                exynos_dp_read_byte_from_dpcd(dp,
-                       DPCD_ADDR_TRAINING_PATTERN_SET,
+                       DP_TRAINING_PATTERN_SET,
                        &data);
                exynos_dp_write_byte_to_dpcd(dp,
-                       DPCD_ADDR_TRAINING_PATTERN_SET,
-                       (u8)(data | DPCD_SCRAMBLING_DISABLED));
+                       DP_TRAINING_PATTERN_SET,
+                       (u8)(data | DP_LINK_SCRAMBLING_DISABLE));
        }
 }
 
@@ -949,12 +952,6 @@ static int exynos_dp_get_modes(struct drm_connector *connector)
        return 1;
 }
 
-static int exynos_dp_mode_valid(struct drm_connector *connector,
-                       struct drm_display_mode *mode)
-{
-       return MODE_OK;
-}
-
 static struct drm_encoder *exynos_dp_best_encoder(
                        struct drm_connector *connector)
 {
@@ -965,20 +962,9 @@ static struct drm_encoder *exynos_dp_best_encoder(
 
 static struct drm_connector_helper_funcs exynos_dp_connector_helper_funcs = {
        .get_modes = exynos_dp_get_modes,
-       .mode_valid = exynos_dp_mode_valid,
        .best_encoder = exynos_dp_best_encoder,
 };
 
-static int exynos_dp_initialize(struct exynos_drm_display *display,
-                               struct drm_device *drm_dev)
-{
-       struct exynos_dp_device *dp = display->ctx;
-
-       dp->drm_dev = drm_dev;
-
-       return 0;
-}
-
 static bool find_bridge(const char *compat, struct bridge_init *bridge)
 {
        bridge->client = NULL;
@@ -1101,12 +1087,11 @@ static void exynos_dp_dpms(struct exynos_drm_display *display, int mode)
                break;
        default:
                break;
-       };
+       }
        dp->dpms_mode = mode;
 }
 
 static struct exynos_drm_display_ops exynos_dp_display_ops = {
-       .initialize = exynos_dp_initialize,
        .create_connector = exynos_dp_create_connector,
        .dpms = exynos_dp_dpms,
 };
@@ -1123,10 +1108,8 @@ static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev)
 
        dp_video_config = devm_kzalloc(dev,
                                sizeof(*dp_video_config), GFP_KERNEL);
-       if (!dp_video_config) {
-               dev_err(dev, "memory allocation for video config failed\n");
+       if (!dp_video_config)
                return ERR_PTR(-ENOMEM);
-       }
 
        dp_video_config->h_sync_polarity =
                of_property_read_bool(dp_node, "hsync-active-high");
@@ -1185,10 +1168,7 @@ static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp)
        dp_phy_node = of_find_node_by_name(dp_phy_node, "dptx-phy");
        if (!dp_phy_node) {
                dp->phy = devm_phy_get(dp->dev, "dp");
-               if (IS_ERR(dp->phy))
-                       return PTR_ERR(dp->phy);
-               else
-                       return 0;
+               return PTR_ERR_OR_ZERO(dp->phy);
        }
 
        if (of_property_read_u32(dp_phy_node, "reg", &phy_base)) {
@@ -1230,19 +1210,20 @@ static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp)
        return 0;
 }
 
-static int exynos_dp_probe(struct platform_device *pdev)
+static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
 {
+       struct platform_device *pdev = to_platform_device(dev);
+       struct drm_device *drm_dev = data;
        struct resource *res;
        struct exynos_dp_device *dp;
+       unsigned int irq_flags;
 
        int ret = 0;
 
        dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
                                GFP_KERNEL);
-       if (!dp) {
-               dev_err(&pdev->dev, "no memory for device data\n");
+       if (!dp)
                return -ENOMEM;
-       }
 
        dp->dev = &pdev->dev;
        dp->dpms_mode = DRM_MODE_DPMS_OFF;
@@ -1273,7 +1254,30 @@ static int exynos_dp_probe(struct platform_device *pdev)
        if (IS_ERR(dp->reg_base))
                return PTR_ERR(dp->reg_base);
 
-       dp->irq = platform_get_irq(pdev, 0);
+       dp->hpd_gpio = of_get_named_gpio(dev->of_node, "samsung,hpd-gpio", 0);
+
+       if (gpio_is_valid(dp->hpd_gpio)) {
+               /*
+                * Set up the hotplug GPIO from the device tree as an interrupt.
+                * Simply specifying a different interrupt in the device tree
+                * doesn't work since we handle hotplug rather differently when
+                * using a GPIO.  We also need the actual GPIO specifier so
+                * that we can get the current state of the GPIO.
+                */
+               ret = devm_gpio_request_one(&pdev->dev, dp->hpd_gpio, GPIOF_IN,
+                                           "hpd_gpio");
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to get hpd gpio\n");
+                       return ret;
+               }
+               dp->irq = gpio_to_irq(dp->hpd_gpio);
+               irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+       } else {
+               dp->hpd_gpio = -ENODEV;
+               dp->irq = platform_get_irq(pdev, 0);
+               irq_flags = 0;
+       }
+
        if (dp->irq == -ENXIO) {
                dev_err(&pdev->dev, "failed to get irq\n");
                return -ENODEV;
@@ -1285,28 +1289,61 @@ static int exynos_dp_probe(struct platform_device *pdev)
 
        exynos_dp_init_dp(dp);
 
-       ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler, 0,
-                               "exynos-dp", dp);
+       ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler,
+                       irq_flags, "exynos-dp", dp);
        if (ret) {
                dev_err(&pdev->dev, "failed to request irq\n");
                return ret;
        }
        disable_irq(dp->irq);
 
+       dp->drm_dev = drm_dev;
        exynos_dp_display.ctx = dp;
 
        platform_set_drvdata(pdev, &exynos_dp_display);
-       exynos_drm_display_register(&exynos_dp_display);
 
-       return 0;
+       return exynos_drm_create_enc_conn(drm_dev, &exynos_dp_display);
 }
 
-static int exynos_dp_remove(struct platform_device *pdev)
+static void exynos_dp_unbind(struct device *dev, struct device *master,
+                               void *data)
 {
-       struct exynos_drm_display *display = platform_get_drvdata(pdev);
+       struct exynos_drm_display *display = dev_get_drvdata(dev);
+       struct exynos_dp_device *dp = display->ctx;
+       struct drm_encoder *encoder = dp->encoder;
 
        exynos_dp_dpms(display, DRM_MODE_DPMS_OFF);
-       exynos_drm_display_unregister(&exynos_dp_display);
+
+       encoder->funcs->destroy(encoder);
+       drm_connector_cleanup(&dp->connector);
+}
+
+static const struct component_ops exynos_dp_ops = {
+       .bind   = exynos_dp_bind,
+       .unbind = exynos_dp_unbind,
+};
+
+static int exynos_dp_probe(struct platform_device *pdev)
+{
+       int ret;
+
+       ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
+                                       exynos_dp_display.type);
+       if (ret)
+               return ret;
+
+       ret = component_add(&pdev->dev, &exynos_dp_ops);
+       if (ret)
+               exynos_drm_component_del(&pdev->dev,
+                                               EXYNOS_DEVICE_TYPE_CONNECTOR);
+
+       return ret;
+}
+
+static int exynos_dp_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &exynos_dp_ops);
+       exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
 
        return 0;
 }
index d6a900d4ee40d1d307a42dc62cf1d72a27da892a..02cc4f9ab9034f1324b2b52ec92fc04713198c0b 100644 (file)
@@ -14,6 +14,7 @@
 #define _EXYNOS_DP_CORE_H
 
 #include <drm/drm_crtc.h>
+#include <drm/drm_dp_helper.h>
 #include <drm/exynos_drm.h>
 
 #define DP_TIMEOUT_LOOP_COUNT 100
@@ -159,6 +160,7 @@ struct exynos_dp_device {
        struct work_struct      hotplug_work;
        struct phy              *phy;
        int                     dpms_mode;
+       int                     hpd_gpio;
 
        struct exynos_drm_panel_info panel;
 };
@@ -261,69 +263,17 @@ void exynos_dp_disable_scrambling(struct exynos_dp_device *dp);
 #define EDID_EXTENSION_FLAG                    0x7e
 #define EDID_CHECKSUM                          0x7f
 
-/* Definition for DPCD Register */
-#define DPCD_ADDR_DPCD_REV                     0x0000
-#define DPCD_ADDR_MAX_LINK_RATE                        0x0001
-#define DPCD_ADDR_MAX_LANE_COUNT               0x0002
-#define DPCD_ADDR_LINK_BW_SET                  0x0100
-#define DPCD_ADDR_LANE_COUNT_SET               0x0101
-#define DPCD_ADDR_TRAINING_PATTERN_SET         0x0102
-#define DPCD_ADDR_TRAINING_LANE0_SET           0x0103
-#define DPCD_ADDR_LANE0_1_STATUS               0x0202
-#define DPCD_ADDR_LANE_ALIGN_STATUS_UPDATED    0x0204
-#define DPCD_ADDR_ADJUST_REQUEST_LANE0_1       0x0206
-#define DPCD_ADDR_ADJUST_REQUEST_LANE2_3       0x0207
-#define DPCD_ADDR_TEST_REQUEST                 0x0218
-#define DPCD_ADDR_TEST_RESPONSE                        0x0260
-#define DPCD_ADDR_TEST_EDID_CHECKSUM           0x0261
-#define DPCD_ADDR_SINK_POWER_STATE             0x0600
-
-/* DPCD_ADDR_MAX_LANE_COUNT */
+/* DP_MAX_LANE_COUNT */
 #define DPCD_ENHANCED_FRAME_CAP(x)             (((x) >> 7) & 0x1)
 #define DPCD_MAX_LANE_COUNT(x)                 ((x) & 0x1f)
 
-/* DPCD_ADDR_LANE_COUNT_SET */
-#define DPCD_ENHANCED_FRAME_EN                 (0x1 << 7)
+/* DP_LANE_COUNT_SET */
 #define DPCD_LANE_COUNT_SET(x)                 ((x) & 0x1f)
 
-/* DPCD_ADDR_TRAINING_PATTERN_SET */
-#define DPCD_SCRAMBLING_DISABLED               (0x1 << 5)
-#define DPCD_SCRAMBLING_ENABLED                        (0x0 << 5)
-#define DPCD_TRAINING_PATTERN_2                        (0x2 << 0)
-#define DPCD_TRAINING_PATTERN_1                        (0x1 << 0)
-#define DPCD_TRAINING_PATTERN_DISABLED         (0x0 << 0)
-
-/* DPCD_ADDR_TRAINING_LANE0_SET */
-#define DPCD_MAX_PRE_EMPHASIS_REACHED          (0x1 << 5)
+/* DP_TRAINING_LANE0_SET */
 #define DPCD_PRE_EMPHASIS_SET(x)               (((x) & 0x3) << 3)
 #define DPCD_PRE_EMPHASIS_GET(x)               (((x) >> 3) & 0x3)
-#define DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0      (0x0 << 3)
-#define DPCD_MAX_SWING_REACHED                 (0x1 << 2)
 #define DPCD_VOLTAGE_SWING_SET(x)              (((x) & 0x3) << 0)
 #define DPCD_VOLTAGE_SWING_GET(x)              (((x) >> 0) & 0x3)
-#define DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0     (0x0 << 0)
-
-/* DPCD_ADDR_LANE0_1_STATUS */
-#define DPCD_LANE_SYMBOL_LOCKED                        (0x1 << 2)
-#define DPCD_LANE_CHANNEL_EQ_DONE              (0x1 << 1)
-#define DPCD_LANE_CR_DONE                      (0x1 << 0)
-#define DPCD_CHANNEL_EQ_BITS                   (DPCD_LANE_CR_DONE|     \
-                                                DPCD_LANE_CHANNEL_EQ_DONE|\
-                                                DPCD_LANE_SYMBOL_LOCKED)
-
-/* DPCD_ADDR_LANE_ALIGN__STATUS_UPDATED */
-#define DPCD_LINK_STATUS_UPDATED               (0x1 << 7)
-#define DPCD_DOWNSTREAM_PORT_STATUS_CHANGED    (0x1 << 6)
-#define DPCD_INTERLANE_ALIGN_DONE              (0x1 << 0)
-
-/* DPCD_ADDR_TEST_REQUEST */
-#define DPCD_TEST_EDID_READ                    (0x1 << 2)
-
-/* DPCD_ADDR_TEST_RESPONSE */
-#define DPCD_TEST_EDID_CHECKSUM_WRITE          (0x1 << 2)
-
-/* DPCD_ADDR_SINK_POWER_STATE */
-#define DPCD_SET_POWER_STATE_D0                        (0x1 << 0)
-#define DPCD_SET_POWER_STATE_D4                        (0x2 << 0)
 
 #endif /* _EXYNOS_DP_CORE_H */
index b70da5052ff0287233b3668574df40ed5b7e8fb1..c1f87a2a9284159fcd07709e11ff4fdd87365f33 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/device.h>
 #include <linux/io.h>
 #include <linux/delay.h>
+#include <linux/gpio.h>
 
 #include "exynos_dp_core.h"
 #include "exynos_dp_reg.h"
@@ -326,6 +327,9 @@ void exynos_dp_clear_hotplug_interrupts(struct exynos_dp_device *dp)
 {
        u32 reg;
 
+       if (gpio_is_valid(dp->hpd_gpio))
+               return;
+
        reg = HOTPLUG_CHG | HPD_LOST | PLUG;
        writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
 
@@ -337,6 +341,9 @@ void exynos_dp_init_hpd(struct exynos_dp_device *dp)
 {
        u32 reg;
 
+       if (gpio_is_valid(dp->hpd_gpio))
+               return;
+
        exynos_dp_clear_hotplug_interrupts(dp);
 
        reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
@@ -348,19 +355,27 @@ enum dp_irq_type exynos_dp_get_irq_type(struct exynos_dp_device *dp)
 {
        u32 reg;
 
-       /* Parse hotplug interrupt status register */
-       reg = readl(dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
+       if (gpio_is_valid(dp->hpd_gpio)) {
+               reg = gpio_get_value(dp->hpd_gpio);
+               if (reg)
+                       return DP_IRQ_TYPE_HP_CABLE_IN;
+               else
+                       return DP_IRQ_TYPE_HP_CABLE_OUT;
+       } else {
+               /* Parse hotplug interrupt status register */
+               reg = readl(dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
 
-       if (reg & PLUG)
-               return DP_IRQ_TYPE_HP_CABLE_IN;
+               if (reg & PLUG)
+                       return DP_IRQ_TYPE_HP_CABLE_IN;
 
-       if (reg & HPD_LOST)
-               return DP_IRQ_TYPE_HP_CABLE_OUT;
+               if (reg & HPD_LOST)
+                       return DP_IRQ_TYPE_HP_CABLE_OUT;
 
-       if (reg & HOTPLUG_CHG)
-               return DP_IRQ_TYPE_HP_CHANGE;
+               if (reg & HOTPLUG_CHG)
+                       return DP_IRQ_TYPE_HP_CHANGE;
 
-       return DP_IRQ_TYPE_UNKNOWN;
+               return DP_IRQ_TYPE_UNKNOWN;
+       }
 }
 
 void exynos_dp_reset_aux(struct exynos_dp_device *dp)
@@ -386,7 +401,7 @@ void exynos_dp_init_aux(struct exynos_dp_device *dp)
        /* Disable AUX transaction H/W retry */
        reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(0)|
                AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
-       writel(reg, dp->reg_base + EXYNOS_DP_AUX_HW_RETRY_CTL) ;
+       writel(reg, dp->reg_base + EXYNOS_DP_AUX_HW_RETRY_CTL);
 
        /* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
        reg = DEFER_CTRL_EN | DEFER_COUNT(1);
@@ -402,9 +417,14 @@ int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp)
 {
        u32 reg;
 
-       reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
-       if (reg & HPD_STATUS)
-               return 0;
+       if (gpio_is_valid(dp->hpd_gpio)) {
+               if (gpio_get_value(dp->hpd_gpio))
+                       return 0;
+       } else {
+               reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
+               if (reg & HPD_STATUS)
+                       return 0;
+       }
 
        return -EINVAL;
 }
index 0e9e06ce36b860ae8fb5a91d40faf31d917b180a..4c9f972eaa0771c0b2d9c5ff0f5480da3dc1e93d 100644 (file)
 #include "exynos_drm_fbdev.h"
 
 static LIST_HEAD(exynos_drm_subdrv_list);
-static LIST_HEAD(exynos_drm_manager_list);
-static LIST_HEAD(exynos_drm_display_list);
 
-static int exynos_drm_create_enc_conn(struct drm_device *dev,
+int exynos_drm_create_enc_conn(struct drm_device *dev,
                                        struct exynos_drm_display *display)
 {
        struct drm_encoder *encoder;
-       struct exynos_drm_manager *manager;
        int ret;
        unsigned long possible_crtcs = 0;
 
-       /* Find possible crtcs for this display */
-       list_for_each_entry(manager, &exynos_drm_manager_list, list)
-               if (manager->type == display->type)
-                       possible_crtcs |= 1 << manager->pipe;
+       ret = exynos_drm_crtc_get_pipe_from_type(dev, display->type);
+       if (ret < 0)
+               return ret;
+
+       possible_crtcs |= 1 << ret;
 
        /* create and initialize a encoder for this sub driver. */
        encoder = exynos_drm_encoder_create(dev, display, possible_crtcs);
@@ -57,127 +55,29 @@ err_destroy_encoder:
        return ret;
 }
 
-static int exynos_drm_subdrv_probe(struct drm_device *dev,
-                                       struct exynos_drm_subdrv *subdrv)
-{
-       if (subdrv->probe) {
-               int ret;
-
-               subdrv->drm_dev = dev;
-
-               /*
-                * this probe callback would be called by sub driver
-                * after setting of all resources to this sub driver,
-                * such as clock, irq and register map are done or by load()
-                * of exynos drm driver.
-                *
-                * P.S. note that this driver is considered for modularization.
-                */
-               ret = subdrv->probe(dev, subdrv->dev);
-               if (ret)
-                       return ret;
-       }
-
-       return 0;
-}
-
-static void exynos_drm_subdrv_remove(struct drm_device *dev,
-                                     struct exynos_drm_subdrv *subdrv)
-{
-       if (subdrv->remove)
-               subdrv->remove(dev, subdrv->dev);
-}
-
-int exynos_drm_initialize_managers(struct drm_device *dev)
+int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
 {
-       struct exynos_drm_manager *manager, *n;
-       int ret, pipe = 0;
-
-       list_for_each_entry(manager, &exynos_drm_manager_list, list) {
-               if (manager->ops->initialize) {
-                       ret = manager->ops->initialize(manager, dev, pipe);
-                       if (ret) {
-                               DRM_ERROR("Mgr init [%d] failed with %d\n",
-                                               manager->type, ret);
-                               goto err;
-                       }
-               }
+       if (!subdrv)
+               return -EINVAL;
 
-               manager->drm_dev = dev;
-               manager->pipe = pipe++;
+       list_add_tail(&subdrv->list, &exynos_drm_subdrv_list);
 
-               ret = exynos_drm_crtc_create(manager);
-               if (ret) {
-                       DRM_ERROR("CRTC create [%d] failed with %d\n",
-                                       manager->type, ret);
-                       goto err;
-               }
-       }
        return 0;
-
-err:
-       list_for_each_entry_safe(manager, n, &exynos_drm_manager_list, list) {
-               if (pipe-- > 0)
-                       exynos_drm_manager_unregister(manager);
-               else
-                       list_del(&manager->list);
-       }
-       return ret;
-}
-
-void exynos_drm_remove_managers(struct drm_device *dev)
-{
-       struct exynos_drm_manager *manager, *n;
-
-       list_for_each_entry_safe(manager, n, &exynos_drm_manager_list, list)
-               exynos_drm_manager_unregister(manager);
 }
+EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register);
 
-int exynos_drm_initialize_displays(struct drm_device *dev)
+int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv)
 {
-       struct exynos_drm_display *display, *n;
-       int ret, initialized = 0;
-
-       list_for_each_entry(display, &exynos_drm_display_list, list) {
-               if (display->ops->initialize) {
-                       ret = display->ops->initialize(display, dev);
-                       if (ret) {
-                               DRM_ERROR("Display init [%d] failed with %d\n",
-                                               display->type, ret);
-                               goto err;
-                       }
-               }
+       if (!subdrv)
+               return -EINVAL;
 
-               initialized++;
+       list_del(&subdrv->list);
 
-               ret = exynos_drm_create_enc_conn(dev, display);
-               if (ret) {
-                       DRM_ERROR("Encoder create [%d] failed with %d\n",
-                                       display->type, ret);
-                       goto err;
-               }
-       }
        return 0;
-
-err:
-       list_for_each_entry_safe(display, n, &exynos_drm_display_list, list) {
-               if (initialized-- > 0)
-                       exynos_drm_display_unregister(display);
-               else
-                       list_del(&display->list);
-       }
-       return ret;
-}
-
-void exynos_drm_remove_displays(struct drm_device *dev)
-{
-       struct exynos_drm_display *display, *n;
-
-       list_for_each_entry_safe(display, n, &exynos_drm_display_list, list)
-               exynos_drm_display_unregister(display);
 }
+EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister);
 
-int exynos_drm_device_register(struct drm_device *dev)
+int exynos_drm_device_subdrv_probe(struct drm_device *dev)
 {
        struct exynos_drm_subdrv *subdrv, *n;
        int err;
@@ -186,19 +86,28 @@ int exynos_drm_device_register(struct drm_device *dev)
                return -EINVAL;
 
        list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) {
-               err = exynos_drm_subdrv_probe(dev, subdrv);
-               if (err) {
-                       DRM_DEBUG("exynos drm subdrv probe failed.\n");
-                       list_del(&subdrv->list);
-                       continue;
+               if (subdrv->probe) {
+                       subdrv->drm_dev = dev;
+
+                       /*
+                        * this probe callback would be called by sub driver
+                        * after setting of all resources to this sub driver,
+                        * such as clock, irq and register map are done.
+                        */
+                       err = subdrv->probe(dev, subdrv->dev);
+                       if (err) {
+                               DRM_DEBUG("exynos drm subdrv probe failed.\n");
+                               list_del(&subdrv->list);
+                               continue;
+                       }
                }
        }
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(exynos_drm_device_register);
+EXPORT_SYMBOL_GPL(exynos_drm_device_subdrv_probe);
 
-int exynos_drm_device_unregister(struct drm_device *dev)
+int exynos_drm_device_subdrv_remove(struct drm_device *dev)
 {
        struct exynos_drm_subdrv *subdrv;
 
@@ -208,66 +117,13 @@ int exynos_drm_device_unregister(struct drm_device *dev)
        }
 
        list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
-               exynos_drm_subdrv_remove(dev, subdrv);
+               if (subdrv->remove)
+                       subdrv->remove(dev, subdrv->dev);
        }
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(exynos_drm_device_unregister);
-
-int exynos_drm_manager_register(struct exynos_drm_manager *manager)
-{
-       BUG_ON(!manager->ops);
-       list_add_tail(&manager->list, &exynos_drm_manager_list);
-       return 0;
-}
-
-int exynos_drm_manager_unregister(struct exynos_drm_manager *manager)
-{
-       if (manager->ops->remove)
-               manager->ops->remove(manager);
-
-       list_del(&manager->list);
-       return 0;
-}
-
-int exynos_drm_display_register(struct exynos_drm_display *display)
-{
-       BUG_ON(!display->ops);
-       list_add_tail(&display->list, &exynos_drm_display_list);
-       return 0;
-}
-
-int exynos_drm_display_unregister(struct exynos_drm_display *display)
-{
-       if (display->ops->remove)
-               display->ops->remove(display);
-
-       list_del(&display->list);
-       return 0;
-}
-
-int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
-{
-       if (!subdrv)
-               return -EINVAL;
-
-       list_add_tail(&subdrv->list, &exynos_drm_subdrv_list);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register);
-
-int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv)
-{
-       if (!subdrv)
-               return -EINVAL;
-
-       list_del(&subdrv->list);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister);
+EXPORT_SYMBOL_GPL(exynos_drm_device_subdrv_remove);
 
 int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file)
 {
index 1ef5ab9c9d519d175b202dbf01cba243a870e1f7..95c9435d02668213921ba9a0cf98441ec8bd5628 100644 (file)
@@ -368,6 +368,7 @@ int exynos_drm_crtc_create(struct exynos_drm_manager *manager)
                return -ENOMEM;
        }
 
+       manager->crtc = &exynos_crtc->drm_crtc;
        crtc = &exynos_crtc->drm_crtc;
 
        private->crtc[manager->pipe] = crtc;
@@ -491,3 +492,19 @@ void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb)
                        manager->ops->wait_for_vblank(manager);
        }
 }
+
+int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
+                                       unsigned int out_type)
+{
+       struct drm_crtc *crtc;
+
+       list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) {
+               struct exynos_drm_crtc *exynos_crtc;
+
+               exynos_crtc = to_exynos_crtc(crtc);
+               if (exynos_crtc->manager->type == out_type)
+                       return exynos_crtc->manager->pipe;
+       }
+
+       return -EPERM;
+}
index c27b66cc5d24cadc20e12d3427cd4dca3a10998c..9f74b10a8a01de0fcadc9990a30fa2193cdb51d6 100644 (file)
@@ -32,4 +32,8 @@ void exynos_drm_crtc_plane_commit(struct drm_crtc *crtc, int zpos);
 void exynos_drm_crtc_plane_enable(struct drm_crtc *crtc, int zpos);
 void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos);
 
+/* This function gets pipe value to crtc device matched with out_type. */
+int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
+                                       unsigned int out_type);
+
 #endif
index 2b09c7c0bfcc8b229a9458ff6b9e1a2f8648e56e..482127f633c573fe84db8faffbe7cf5210df7ac3 100644 (file)
@@ -40,20 +40,10 @@ exynos_dpi_detect(struct drm_connector *connector, bool force)
 {
        struct exynos_dpi *ctx = connector_to_dpi(connector);
 
-       /* panels supported only by boot-loader are always connected */
-       if (!ctx->panel_node)
-               return connector_status_connected;
-
-       if (!ctx->panel) {
-               ctx->panel = of_drm_find_panel(ctx->panel_node);
-               if (ctx->panel)
-                       drm_panel_attach(ctx->panel, &ctx->connector);
-       }
-
-       if (ctx->panel)
-               return connector_status_connected;
+       if (!ctx->panel->connector)
+               drm_panel_attach(ctx->panel, &ctx->connector);
 
-       return connector_status_disconnected;
+       return connector_status_connected;
 }
 
 static void exynos_dpi_connector_destroy(struct drm_connector *connector)
@@ -94,12 +84,6 @@ static int exynos_dpi_get_modes(struct drm_connector *connector)
        return 0;
 }
 
-static int exynos_dpi_mode_valid(struct drm_connector *connector,
-                                struct drm_display_mode *mode)
-{
-       return MODE_OK;
-}
-
 static struct drm_encoder *
 exynos_dpi_best_encoder(struct drm_connector *connector)
 {
@@ -110,7 +94,6 @@ exynos_dpi_best_encoder(struct drm_connector *connector)
 
 static struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = {
        .get_modes = exynos_dpi_get_modes,
-       .mode_valid = exynos_dpi_mode_valid,
        .best_encoder = exynos_dpi_best_encoder,
 };
 
@@ -123,10 +106,7 @@ static int exynos_dpi_create_connector(struct exynos_drm_display *display,
 
        ctx->encoder = encoder;
 
-       if (ctx->panel_node)
-               connector->polled = DRM_CONNECTOR_POLL_CONNECT;
-       else
-               connector->polled = DRM_CONNECTOR_POLL_HPD;
+       connector->polled = DRM_CONNECTOR_POLL_HPD;
 
        ret = drm_connector_init(encoder->dev, connector,
                                 &exynos_dpi_connector_funcs,
@@ -172,7 +152,7 @@ static void exynos_dpi_dpms(struct exynos_drm_display *display, int mode)
                break;
        default:
                break;
-       };
+       }
        ctx->dpms_mode = mode;
 }
 
@@ -294,8 +274,10 @@ static int exynos_dpi_parse_dt(struct exynos_dpi *ctx)
                        return -ENOMEM;
 
                ret = of_get_videomode(dn, vm, 0);
-               if (ret < 0)
+               if (ret < 0) {
+                       devm_kfree(dev, vm);
                        return ret;
+               }
 
                ctx->vm = vm;
 
@@ -308,32 +290,58 @@ static int exynos_dpi_parse_dt(struct exynos_dpi *ctx)
        return 0;
 }
 
-int exynos_dpi_probe(struct device *dev)
+struct exynos_drm_display *exynos_dpi_probe(struct device *dev)
 {
        struct exynos_dpi *ctx;
        int ret;
 
+       ret = exynos_drm_component_add(dev,
+                                       EXYNOS_DEVICE_TYPE_CONNECTOR,
+                                       exynos_dpi_display.type);
+       if (ret)
+               return ERR_PTR(ret);
+
        ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
        if (!ctx)
-               return -ENOMEM;
+               goto err_del_component;
 
        ctx->dev = dev;
        exynos_dpi_display.ctx = ctx;
        ctx->dpms_mode = DRM_MODE_DPMS_OFF;
 
        ret = exynos_dpi_parse_dt(ctx);
-       if (ret < 0)
-               return ret;
+       if (ret < 0) {
+               devm_kfree(dev, ctx);
+               goto err_del_component;
+       }
 
-       exynos_drm_display_register(&exynos_dpi_display);
+       if (ctx->panel_node) {
+               ctx->panel = of_drm_find_panel(ctx->panel_node);
+               if (!ctx->panel) {
+                       exynos_drm_component_del(dev,
+                                               EXYNOS_DEVICE_TYPE_CONNECTOR);
+                       return ERR_PTR(-EPROBE_DEFER);
+               }
+       }
 
-       return 0;
+       return &exynos_dpi_display;
+
+err_del_component:
+       exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
+
+       return NULL;
 }
 
 int exynos_dpi_remove(struct device *dev)
 {
+       struct drm_encoder *encoder = exynos_dpi_display.encoder;
+       struct exynos_dpi *ctx = exynos_dpi_display.ctx;
+
        exynos_dpi_dpms(&exynos_dpi_display, DRM_MODE_DPMS_OFF);
-       exynos_drm_display_unregister(&exynos_dpi_display);
+       encoder->funcs->destroy(encoder);
+       drm_connector_cleanup(&ctx->connector);
+
+       exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
 
        return 0;
 }
index 2d27ba23a6a8ef17f39f45b903254ef3679be59c..d91f277775375050c9b8e9e63c925b4854cc3cec 100644 (file)
@@ -16,6 +16,7 @@
 #include <drm/drm_crtc_helper.h>
 
 #include <linux/anon_inodes.h>
+#include <linux/component.h>
 
 #include <drm/exynos_drm.h>
 
 
 #define VBLANK_OFF_DELAY       50000
 
-/* platform device pointer for eynos drm device. */
 static struct platform_device *exynos_drm_pdev;
 
+static DEFINE_MUTEX(drm_component_lock);
+static LIST_HEAD(drm_component_list);
+
+struct component_dev {
+       struct list_head list;
+       struct device *crtc_dev;
+       struct device *conn_dev;
+       enum exynos_drm_output_type out_type;
+       unsigned int dev_type_flag;
+};
+
 static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
 {
        struct exynos_drm_private *private;
@@ -73,38 +84,21 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
 
        exynos_drm_mode_config_init(dev);
 
-       ret = exynos_drm_initialize_managers(dev);
-       if (ret)
-               goto err_mode_config_cleanup;
-
        for (nr = 0; nr < MAX_PLANE; nr++) {
                struct drm_plane *plane;
                unsigned long possible_crtcs = (1 << MAX_CRTC) - 1;
 
                plane = exynos_plane_init(dev, possible_crtcs, false);
                if (!plane)
-                       goto err_manager_cleanup;
+                       goto err_mode_config_cleanup;
        }
 
-       ret = exynos_drm_initialize_displays(dev);
-       if (ret)
-               goto err_manager_cleanup;
-
        /* init kms poll for handling hpd */
        drm_kms_helper_poll_init(dev);
 
        ret = drm_vblank_init(dev, MAX_CRTC);
        if (ret)
-               goto err_display_cleanup;
-
-       /*
-        * probe sub drivers such as display controller and hdmi driver,
-        * that were registered at probe() of platform driver
-        * to the sub driver and create encoder and connector for them.
-        */
-       ret = exynos_drm_device_register(dev);
-       if (ret)
-               goto err_vblank;
+               goto err_mode_config_cleanup;
 
        /* setup possible_clones. */
        exynos_drm_encoder_setup(dev);
@@ -113,17 +107,25 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
 
        platform_set_drvdata(dev->platformdev, dev);
 
+       /* Try to bind all sub drivers. */
+       ret = component_bind_all(dev->dev, dev);
+       if (ret)
+               goto err_cleanup_vblank;
+
+       /* Probe non kms sub drivers and virtual display driver. */
+       ret = exynos_drm_device_subdrv_probe(dev);
+       if (ret)
+               goto err_unbind_all;
+
        /* force connectors detection */
        drm_helper_hpd_irq_event(dev);
 
        return 0;
 
-err_vblank:
+err_unbind_all:
+       component_unbind_all(dev->dev, dev);
+err_cleanup_vblank:
        drm_vblank_cleanup(dev);
-err_display_cleanup:
-       exynos_drm_remove_displays(dev);
-err_manager_cleanup:
-       exynos_drm_remove_managers(dev);
 err_mode_config_cleanup:
        drm_mode_config_cleanup(dev);
        drm_release_iommu_mapping(dev);
@@ -135,17 +137,17 @@ err_free_private:
 
 static int exynos_drm_unload(struct drm_device *dev)
 {
+       exynos_drm_device_subdrv_remove(dev);
+
        exynos_drm_fbdev_fini(dev);
-       exynos_drm_device_unregister(dev);
        drm_vblank_cleanup(dev);
        drm_kms_helper_poll_fini(dev);
-       exynos_drm_remove_displays(dev);
-       exynos_drm_remove_managers(dev);
        drm_mode_config_cleanup(dev);
 
        drm_release_iommu_mapping(dev);
        kfree(dev->dev_private);
 
+       component_unbind_all(dev->dev, dev);
        dev->dev_private = NULL;
 
        return 0;
@@ -183,9 +185,9 @@ static int exynos_drm_resume(struct drm_device *dev)
                if (connector->funcs->dpms)
                        connector->funcs->dpms(connector, connector->dpms);
        }
+       drm_modeset_unlock_all(dev);
 
        drm_helper_resume_force_mode(dev);
-       drm_modeset_unlock_all(dev);
 
        return 0;
 }
@@ -323,8 +325,7 @@ static const struct file_operations exynos_drm_driver_fops = {
 };
 
 static struct drm_driver exynos_drm_driver = {
-       .driver_features        = DRIVER_HAVE_IRQ | DRIVER_MODESET |
-                                       DRIVER_GEM | DRIVER_PRIME,
+       .driver_features        = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
        .load                   = exynos_drm_load,
        .unload                 = exynos_drm_unload,
        .suspend                = exynos_drm_suspend,
@@ -355,27 +356,6 @@ static struct drm_driver exynos_drm_driver = {
        .minor  = DRIVER_MINOR,
 };
 
-static int exynos_drm_platform_probe(struct platform_device *pdev)
-{
-       int ret;
-
-       ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
-       if (ret)
-               return ret;
-
-       pm_runtime_enable(&pdev->dev);
-       pm_runtime_get_sync(&pdev->dev);
-
-       return drm_platform_init(&exynos_drm_driver, pdev);
-}
-
-static int exynos_drm_platform_remove(struct platform_device *pdev)
-{
-       drm_put_dev(platform_get_drvdata(pdev));
-
-       return 0;
-}
-
 #ifdef CONFIG_PM_SLEEP
 static int exynos_drm_sys_suspend(struct device *dev)
 {
@@ -400,196 +380,319 @@ static int exynos_drm_sys_resume(struct device *dev)
 }
 #endif
 
-#ifdef CONFIG_PM_RUNTIME
-static int exynos_drm_runtime_suspend(struct device *dev)
+static const struct dev_pm_ops exynos_drm_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(exynos_drm_sys_suspend, exynos_drm_sys_resume)
+};
+
+int exynos_drm_component_add(struct device *dev,
+                               enum exynos_drm_device_type dev_type,
+                               enum exynos_drm_output_type out_type)
 {
-       struct drm_device *drm_dev = dev_get_drvdata(dev);
-       pm_message_t message;
+       struct component_dev *cdev;
 
-       if (pm_runtime_suspended(dev))
-               return 0;
+       if (dev_type != EXYNOS_DEVICE_TYPE_CRTC &&
+                       dev_type != EXYNOS_DEVICE_TYPE_CONNECTOR) {
+               DRM_ERROR("invalid device type.\n");
+               return -EINVAL;
+       }
 
-       message.event = PM_EVENT_SUSPEND;
-       return exynos_drm_suspend(drm_dev, message);
+       mutex_lock(&drm_component_lock);
+
+       /*
+        * Make sure to check if there is a component which has two device
+        * objects, for connector and for encoder/connector.
+        * It should make sure that crtc and encoder/connector drivers are
+        * ready before exynos drm core binds them.
+        */
+       list_for_each_entry(cdev, &drm_component_list, list) {
+               if (cdev->out_type == out_type) {
+                       /*
+                        * If crtc and encoder/connector device objects are
+                        * added already just return.
+                        */
+                       if (cdev->dev_type_flag == (EXYNOS_DEVICE_TYPE_CRTC |
+                                               EXYNOS_DEVICE_TYPE_CONNECTOR)) {
+                               mutex_unlock(&drm_component_lock);
+                               return 0;
+                       }
+
+                       if (dev_type == EXYNOS_DEVICE_TYPE_CRTC) {
+                               cdev->crtc_dev = dev;
+                               cdev->dev_type_flag |= dev_type;
+                       }
+
+                       if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR) {
+                               cdev->conn_dev = dev;
+                               cdev->dev_type_flag |= dev_type;
+                       }
+
+                       mutex_unlock(&drm_component_lock);
+                       return 0;
+               }
+       }
+
+       mutex_unlock(&drm_component_lock);
+
+       cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+       if (!cdev)
+               return -ENOMEM;
+
+       if (dev_type == EXYNOS_DEVICE_TYPE_CRTC)
+               cdev->crtc_dev = dev;
+       if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR)
+               cdev->conn_dev = dev;
+
+       cdev->out_type = out_type;
+       cdev->dev_type_flag = dev_type;
+
+       mutex_lock(&drm_component_lock);
+       list_add_tail(&cdev->list, &drm_component_list);
+       mutex_unlock(&drm_component_lock);
+
+       return 0;
 }
 
-static int exynos_drm_runtime_resume(struct device *dev)
+void exynos_drm_component_del(struct device *dev,
+                               enum exynos_drm_device_type dev_type)
 {
-       struct drm_device *drm_dev = dev_get_drvdata(dev);
+       struct component_dev *cdev, *next;
 
-       if (!pm_runtime_suspended(dev))
-               return 0;
+       mutex_lock(&drm_component_lock);
 
-       return exynos_drm_resume(drm_dev);
+       list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
+               if (dev_type == EXYNOS_DEVICE_TYPE_CRTC) {
+                       if (cdev->crtc_dev == dev) {
+                               cdev->crtc_dev = NULL;
+                               cdev->dev_type_flag &= ~dev_type;
+                       }
+               }
+
+               if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR) {
+                       if (cdev->conn_dev == dev) {
+                               cdev->conn_dev = NULL;
+                               cdev->dev_type_flag &= ~dev_type;
+                       }
+               }
+
+               /*
+                * Release cdev object only in case that both of crtc and
+                * encoder/connector device objects are NULL.
+                */
+               if (!cdev->crtc_dev && !cdev->conn_dev) {
+                       list_del(&cdev->list);
+                       kfree(cdev);
+               }
+
+               break;
+       }
+
+       mutex_unlock(&drm_component_lock);
 }
-#endif
 
-static const struct dev_pm_ops exynos_drm_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(exynos_drm_sys_suspend, exynos_drm_sys_resume)
-       SET_RUNTIME_PM_OPS(exynos_drm_runtime_suspend,
-                       exynos_drm_runtime_resume, NULL)
-};
+static int compare_of(struct device *dev, void *data)
+{
+       return dev == (struct device *)data;
+}
 
-static struct platform_driver exynos_drm_platform_driver = {
-       .probe          = exynos_drm_platform_probe,
-       .remove         = exynos_drm_platform_remove,
-       .driver         = {
-               .owner  = THIS_MODULE,
-               .name   = "exynos-drm",
-               .pm     = &exynos_drm_pm_ops,
-       },
+static int exynos_drm_add_components(struct device *dev, struct master *m)
+{
+       struct component_dev *cdev;
+       unsigned int attach_cnt = 0;
+
+       mutex_lock(&drm_component_lock);
+
+       list_for_each_entry(cdev, &drm_component_list, list) {
+               int ret;
+
+               /*
+                * Add components to master only in case that crtc and
+                * encoder/connector device objects exist.
+                */
+               if (!cdev->crtc_dev || !cdev->conn_dev)
+                       continue;
+
+               attach_cnt++;
+
+               mutex_unlock(&drm_component_lock);
+
+               /*
+                * fimd and dpi modules have same device object so add
+                * only crtc device object in this case.
+                *
+                * TODO. if dpi module follows driver-model driver then
+                * below codes can be removed.
+                */
+               if (cdev->crtc_dev == cdev->conn_dev) {
+                       ret = component_master_add_child(m, compare_of,
+                                       cdev->crtc_dev);
+                       if (ret < 0)
+                               return ret;
+
+                       goto out_lock;
+               }
+
+               /*
+                * Do not chage below call order.
+                * crtc device first should be added to master because
+                * connector/encoder need pipe number of crtc when they
+                * are created.
+                */
+               ret = component_master_add_child(m, compare_of, cdev->crtc_dev);
+               ret |= component_master_add_child(m, compare_of,
+                                                       cdev->conn_dev);
+               if (ret < 0)
+                       return ret;
+
+out_lock:
+               mutex_lock(&drm_component_lock);
+       }
+
+       mutex_unlock(&drm_component_lock);
+
+       return attach_cnt ? 0 : -ENODEV;
+}
+
+static int exynos_drm_bind(struct device *dev)
+{
+       return drm_platform_init(&exynos_drm_driver, to_platform_device(dev));
+}
+
+static void exynos_drm_unbind(struct device *dev)
+{
+       drm_put_dev(dev_get_drvdata(dev));
+}
+
+static const struct component_master_ops exynos_drm_ops = {
+       .add_components = exynos_drm_add_components,
+       .bind           = exynos_drm_bind,
+       .unbind         = exynos_drm_unbind,
 };
 
-static int __init exynos_drm_init(void)
+static int exynos_drm_platform_probe(struct platform_device *pdev)
 {
        int ret;
 
+       pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+       exynos_drm_driver.num_ioctls = ARRAY_SIZE(exynos_ioctls);
+
+#ifdef CONFIG_DRM_EXYNOS_FIMD
+       ret = platform_driver_register(&fimd_driver);
+       if (ret < 0)
+               return ret;
+#endif
+
 #ifdef CONFIG_DRM_EXYNOS_DP
        ret = platform_driver_register(&dp_driver);
        if (ret < 0)
-               goto out_dp;
+               goto err_unregister_fimd_drv;
 #endif
 
 #ifdef CONFIG_DRM_EXYNOS_DSI
        ret = platform_driver_register(&dsi_driver);
        if (ret < 0)
-               goto out_dsi;
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_FIMD
-       ret = platform_driver_register(&fimd_driver);
-       if (ret < 0)
-               goto out_fimd;
+               goto err_unregister_dp_drv;
 #endif
 
 #ifdef CONFIG_DRM_EXYNOS_HDMI
-       ret = platform_driver_register(&hdmi_driver);
-       if (ret < 0)
-               goto out_hdmi;
        ret = platform_driver_register(&mixer_driver);
        if (ret < 0)
-               goto out_mixer;
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_VIDI
-       ret = platform_driver_register(&vidi_driver);
+               goto err_unregister_dsi_drv;
+       ret = platform_driver_register(&hdmi_driver);
        if (ret < 0)
-               goto out_vidi;
+               goto err_unregister_mixer_drv;
 #endif
 
 #ifdef CONFIG_DRM_EXYNOS_G2D
        ret = platform_driver_register(&g2d_driver);
        if (ret < 0)
-               goto out_g2d;
+               goto err_unregister_hdmi_drv;
 #endif
 
 #ifdef CONFIG_DRM_EXYNOS_FIMC
        ret = platform_driver_register(&fimc_driver);
        if (ret < 0)
-               goto out_fimc;
+               goto err_unregister_g2d_drv;
 #endif
 
 #ifdef CONFIG_DRM_EXYNOS_ROTATOR
        ret = platform_driver_register(&rotator_driver);
        if (ret < 0)
-               goto out_rotator;
+               goto err_unregister_fimc_drv;
 #endif
 
 #ifdef CONFIG_DRM_EXYNOS_GSC
        ret = platform_driver_register(&gsc_driver);
        if (ret < 0)
-               goto out_gsc;
+               goto err_unregister_rotator_drv;
 #endif
 
 #ifdef CONFIG_DRM_EXYNOS_IPP
        ret = platform_driver_register(&ipp_driver);
        if (ret < 0)
-               goto out_ipp;
+               goto err_unregister_gsc_drv;
 
        ret = exynos_platform_device_ipp_register();
        if (ret < 0)
-               goto out_ipp_dev;
+               goto err_unregister_ipp_drv;
 #endif
 
-       ret = platform_driver_register(&exynos_drm_platform_driver);
+       ret = component_master_add(&pdev->dev, &exynos_drm_ops);
        if (ret < 0)
-               goto out_drm;
-
-       exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1,
-                               NULL, 0);
-       if (IS_ERR(exynos_drm_pdev)) {
-               ret = PTR_ERR(exynos_drm_pdev);
-               goto out;
-       }
+               DRM_DEBUG_KMS("re-tried by last sub driver probed later.\n");
 
        return 0;
 
-out:
-       platform_driver_unregister(&exynos_drm_platform_driver);
-
-out_drm:
 #ifdef CONFIG_DRM_EXYNOS_IPP
-       exynos_platform_device_ipp_unregister();
-out_ipp_dev:
+err_unregister_ipp_drv:
        platform_driver_unregister(&ipp_driver);
-out_ipp:
+err_unregister_gsc_drv:
 #endif
 
 #ifdef CONFIG_DRM_EXYNOS_GSC
        platform_driver_unregister(&gsc_driver);
-out_gsc:
+err_unregister_rotator_drv:
 #endif
 
 #ifdef CONFIG_DRM_EXYNOS_ROTATOR
        platform_driver_unregister(&rotator_driver);
-out_rotator:
+err_unregister_fimc_drv:
 #endif
 
 #ifdef CONFIG_DRM_EXYNOS_FIMC
        platform_driver_unregister(&fimc_driver);
-out_fimc:
+err_unregister_g2d_drv:
 #endif
 
 #ifdef CONFIG_DRM_EXYNOS_G2D
        platform_driver_unregister(&g2d_driver);
-out_g2d:
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_VIDI
-       platform_driver_unregister(&vidi_driver);
-out_vidi:
+err_unregister_hdmi_drv:
 #endif
 
 #ifdef CONFIG_DRM_EXYNOS_HDMI
-       platform_driver_unregister(&mixer_driver);
-out_mixer:
        platform_driver_unregister(&hdmi_driver);
-out_hdmi:
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_FIMD
-       platform_driver_unregister(&fimd_driver);
-out_fimd:
+err_unregister_mixer_drv:
+       platform_driver_unregister(&mixer_driver);
+err_unregister_dsi_drv:
 #endif
 
 #ifdef CONFIG_DRM_EXYNOS_DSI
        platform_driver_unregister(&dsi_driver);
-out_dsi:
+err_unregister_dp_drv:
 #endif
 
 #ifdef CONFIG_DRM_EXYNOS_DP
        platform_driver_unregister(&dp_driver);
-out_dp:
+err_unregister_fimd_drv:
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_FIMD
+       platform_driver_unregister(&fimd_driver);
 #endif
        return ret;
 }
 
-static void __exit exynos_drm_exit(void)
+static int exynos_drm_platform_remove(struct platform_device *pdev)
 {
-       platform_device_unregister(exynos_drm_pdev);
-
-       platform_driver_unregister(&exynos_drm_platform_driver);
-
 #ifdef CONFIG_DRM_EXYNOS_IPP
        exynos_platform_device_ipp_unregister();
        platform_driver_unregister(&ipp_driver);
@@ -616,10 +719,6 @@ static void __exit exynos_drm_exit(void)
        platform_driver_unregister(&hdmi_driver);
 #endif
 
-#ifdef CONFIG_DRM_EXYNOS_VIDI
-       platform_driver_unregister(&vidi_driver);
-#endif
-
 #ifdef CONFIG_DRM_EXYNOS_FIMD
        platform_driver_unregister(&fimd_driver);
 #endif
@@ -631,6 +730,59 @@ static void __exit exynos_drm_exit(void)
 #ifdef CONFIG_DRM_EXYNOS_DP
        platform_driver_unregister(&dp_driver);
 #endif
+       component_master_del(&pdev->dev, &exynos_drm_ops);
+       return 0;
+}
+
+static struct platform_driver exynos_drm_platform_driver = {
+       .probe  = exynos_drm_platform_probe,
+       .remove = exynos_drm_platform_remove,
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "exynos-drm",
+               .pm     = &exynos_drm_pm_ops,
+       },
+};
+
+static int exynos_drm_init(void)
+{
+       int ret;
+
+       exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1,
+                                                               NULL, 0);
+       if (IS_ERR(exynos_drm_pdev))
+               return PTR_ERR(exynos_drm_pdev);
+
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+       ret = exynos_drm_probe_vidi();
+       if (ret < 0)
+               goto err_unregister_pd;
+#endif
+
+       ret = platform_driver_register(&exynos_drm_platform_driver);
+       if (ret)
+               goto err_remove_vidi;
+
+       return 0;
+
+err_unregister_pd:
+       platform_device_unregister(exynos_drm_pdev);
+
+err_remove_vidi:
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+       exynos_drm_remove_vidi();
+#endif
+
+       return ret;
+}
+
+static void exynos_drm_exit(void)
+{
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+       exynos_drm_remove_vidi();
+#endif
+       platform_device_unregister(exynos_drm_pdev);
+       platform_driver_unregister(&exynos_drm_platform_driver);
 }
 
 module_init(exynos_drm_init);
index ce3e6a30deaab13bffb8d0c06a1b1cdb4bd3e366..36535f398848480e58562f2432b7ab7c815b8a65 100644 (file)
@@ -42,6 +42,13 @@ struct drm_connector;
 
 extern unsigned int drm_vblank_offdelay;
 
+/* This enumerates device type. */
+enum exynos_drm_device_type {
+       EXYNOS_DEVICE_TYPE_NONE,
+       EXYNOS_DEVICE_TYPE_CRTC,
+       EXYNOS_DEVICE_TYPE_CONNECTOR,
+};
+
 /* this enumerates display type. */
 enum exynos_drm_output_type {
        EXYNOS_DISPLAY_TYPE_NONE,
@@ -122,7 +129,6 @@ struct exynos_drm_overlay {
  * Exynos DRM Display Structure.
  *     - this structure is common to analog tv, digital tv and lcd panel.
  *
- * @initialize: initializes the display with drm_dev
  * @remove: cleans up the display for removal
  * @mode_fixup: fix mode data comparing to hw specific display mode.
  * @mode_set: convert drm_display_mode to hw specific display mode and
@@ -133,8 +139,6 @@ struct exynos_drm_overlay {
  */
 struct exynos_drm_display;
 struct exynos_drm_display_ops {
-       int (*initialize)(struct exynos_drm_display *display,
-                               struct drm_device *drm_dev);
        int (*create_connector)(struct exynos_drm_display *display,
                                struct drm_encoder *encoder);
        void (*remove)(struct exynos_drm_display *display);
@@ -172,8 +176,6 @@ struct exynos_drm_display {
 /*
  * Exynos drm manager ops
  *
- * @initialize: initializes the manager with drm_dev
- * @remove: cleans up the manager for removal
  * @dpms: control device power.
  * @mode_fixup: fix mode data before applying it
  * @mode_set: set the given mode to the manager
@@ -189,9 +191,6 @@ struct exynos_drm_display {
  */
 struct exynos_drm_manager;
 struct exynos_drm_manager_ops {
-       int (*initialize)(struct exynos_drm_manager *mgr,
-                               struct drm_device *drm_dev, int pipe);
-       void (*remove)(struct exynos_drm_manager *mgr);
        void (*dpms)(struct exynos_drm_manager *mgr, int mode);
        bool (*mode_fixup)(struct exynos_drm_manager *mgr,
                                const struct drm_display_mode *mode,
@@ -215,6 +214,7 @@ struct exynos_drm_manager_ops {
  * @list: the list entry for this manager
  * @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
  * @drm_dev: pointer to the drm device
+ * @crtc: crtc object.
  * @pipe: the pipe number for this crtc/manager
  * @ops: pointer to callbacks for exynos drm specific functionality
  * @ctx: A pointer to the manager's implementation specific context
@@ -223,6 +223,7 @@ struct exynos_drm_manager {
        struct list_head list;
        enum exynos_drm_output_type type;
        struct drm_device *drm_dev;
+       struct drm_crtc *crtc;
        int pipe;
        struct exynos_drm_manager_ops *ops;
        void *ctx;
@@ -254,6 +255,7 @@ struct drm_exynos_file_private {
  *     otherwise default one.
  * @da_space_size: size of device address space.
  *     if 0 then default value is used for it.
+ * @pipe: the pipe number for this crtc/manager.
  */
 struct exynos_drm_private {
        struct drm_fb_helper *fb_helper;
@@ -271,6 +273,8 @@ struct exynos_drm_private {
 
        unsigned long da_start;
        unsigned long da_space_size;
+
+       unsigned int pipe;
 };
 
 /*
@@ -281,11 +285,11 @@ struct exynos_drm_private {
  * @drm_dev: pointer to drm_device and this pointer would be set
  *     when sub driver calls exynos_drm_subdrv_register().
  * @manager: subdrv has its own manager to control a hardware appropriately
- *     and we can access a hardware drawing on this manager.
+ *     and we can access a hardware drawing on this manager.
  * @probe: this callback would be called by exynos drm driver after
- *     subdrv is registered to it.
+ *     subdrv is registered to it.
  * @remove: this callback is used to release resources created
- *     by probe callback.
+ *     by probe callback.
  * @open: this would be called with drm device file open.
  * @close: this would be called with drm device file close.
  */
@@ -302,39 +306,14 @@ struct exynos_drm_subdrv {
                        struct drm_file *file);
 };
 
-/*
- * this function calls a probe callback registered to sub driver list and
- * create its own encoder and connector and then set drm_device object
- * to global one.
- */
-int exynos_drm_device_register(struct drm_device *dev);
-/*
- * this function calls a remove callback registered to sub driver list and
- * destroy its own encoder and connetor.
- */
-int exynos_drm_device_unregister(struct drm_device *dev);
-
-int exynos_drm_initialize_managers(struct drm_device *dev);
-void exynos_drm_remove_managers(struct drm_device *dev);
-int exynos_drm_initialize_displays(struct drm_device *dev);
-void exynos_drm_remove_displays(struct drm_device *dev);
-
-int exynos_drm_manager_register(struct exynos_drm_manager *manager);
-int exynos_drm_manager_unregister(struct exynos_drm_manager *manager);
-int exynos_drm_display_register(struct exynos_drm_display *display);
-int exynos_drm_display_unregister(struct exynos_drm_display *display);
-
-/*
- * this function would be called by sub drivers such as display controller
- * or hdmi driver to register this sub driver object to exynos drm driver
- * and when a sub driver is registered to exynos drm driver a probe callback
- * of the sub driver is called and creates its own encoder and connector.
- */
+ /* This function would be called by non kms drivers such as g2d and ipp. */
 int exynos_drm_subdrv_register(struct exynos_drm_subdrv *drm_subdrv);
 
 /* this function removes subdrv list from exynos drm driver */
 int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *drm_subdrv);
 
+int exynos_drm_device_subdrv_probe(struct drm_device *dev);
+int exynos_drm_device_subdrv_remove(struct drm_device *dev);
 int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file);
 void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
 
@@ -360,18 +339,40 @@ int exynos_platform_device_ipp_register(void);
 void exynos_platform_device_ipp_unregister(void);
 
 #ifdef CONFIG_DRM_EXYNOS_DPI
-int exynos_dpi_probe(struct device *dev);
+struct exynos_drm_display * exynos_dpi_probe(struct device *dev);
 int exynos_dpi_remove(struct device *dev);
 #else
-static inline int exynos_dpi_probe(struct device *dev) { return 0; }
+static inline struct exynos_drm_display *
+exynos_dpi_probe(struct device *dev) { return 0; }
 static inline int exynos_dpi_remove(struct device *dev) { return 0; }
 #endif
 
+/*
+ * this function registers exynos drm vidi platform device/driver.
+ */
+int exynos_drm_probe_vidi(void);
+
+/*
+ * this function unregister exynos drm vidi platform device/driver.
+ */
+void exynos_drm_remove_vidi(void);
+
+/* This function creates a encoder and a connector, and initializes them. */
+int exynos_drm_create_enc_conn(struct drm_device *dev,
+                               struct exynos_drm_display *display);
+
+int exynos_drm_component_add(struct device *dev,
+                               enum exynos_drm_device_type dev_type,
+                               enum exynos_drm_output_type out_type);
+
+void exynos_drm_component_del(struct device *dev,
+                               enum exynos_drm_device_type dev_type);
+
+extern struct platform_driver fimd_driver;
 extern struct platform_driver dp_driver;
 extern struct platform_driver dsi_driver;
-extern struct platform_driver fimd_driver;
-extern struct platform_driver hdmi_driver;
 extern struct platform_driver mixer_driver;
+extern struct platform_driver hdmi_driver;
 extern struct platform_driver exynos_drm_common_hdmi_driver;
 extern struct platform_driver vidi_driver;
 extern struct platform_driver g2d_driver;
index 4ac438187568ed4a6894436433e404c94b90a7a2..6302aa64f6c1e328bb315a8ccd70e07e3489d17a 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/irq.h>
 #include <linux/phy/phy.h>
 #include <linux/regulator/consumer.h>
+#include <linux/component.h>
 
 #include <video/mipi_display.h>
 #include <video/videomode.h>
@@ -1378,16 +1379,60 @@ end:
        return ret;
 }
 
+static int exynos_dsi_bind(struct device *dev, struct device *master,
+                               void *data)
+{
+       struct drm_device *drm_dev = data;
+       struct exynos_dsi *dsi;
+       int ret;
+
+       ret = exynos_drm_create_enc_conn(drm_dev, &exynos_dsi_display);
+       if (ret) {
+               DRM_ERROR("Encoder create [%d] failed with %d\n",
+                               exynos_dsi_display.type, ret);
+               return ret;
+       }
+
+       dsi = exynos_dsi_display.ctx;
+
+       return mipi_dsi_host_register(&dsi->dsi_host);
+}
+
+static void exynos_dsi_unbind(struct device *dev, struct device *master,
+                               void *data)
+{
+       struct exynos_dsi *dsi = exynos_dsi_display.ctx;
+       struct drm_encoder *encoder = dsi->encoder;
+
+       exynos_dsi_dpms(&exynos_dsi_display, DRM_MODE_DPMS_OFF);
+
+       mipi_dsi_host_unregister(&dsi->dsi_host);
+
+       encoder->funcs->destroy(encoder);
+       drm_connector_cleanup(&dsi->connector);
+}
+
+static const struct component_ops exynos_dsi_component_ops = {
+       .bind   = exynos_dsi_bind,
+       .unbind = exynos_dsi_unbind,
+};
+
 static int exynos_dsi_probe(struct platform_device *pdev)
 {
        struct resource *res;
        struct exynos_dsi *dsi;
        int ret;
 
+       ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
+                                       exynos_dsi_display.type);
+       if (ret)
+               return ret;
+
        dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
        if (!dsi) {
                dev_err(&pdev->dev, "failed to allocate dsi object.\n");
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto err_del_component;
        }
 
        init_completion(&dsi->completed);
@@ -1401,7 +1446,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)
 
        ret = exynos_dsi_parse_dt(dsi);
        if (ret)
-               return ret;
+               goto err_del_component;
 
        dsi->supplies[0].supply = "vddcore";
        dsi->supplies[1].supply = "vddio";
@@ -1415,32 +1460,37 @@ static int exynos_dsi_probe(struct platform_device *pdev)
        dsi->pll_clk = devm_clk_get(&pdev->dev, "pll_clk");
        if (IS_ERR(dsi->pll_clk)) {
                dev_info(&pdev->dev, "failed to get dsi pll input clock\n");
-               return -EPROBE_DEFER;
+               ret = PTR_ERR(dsi->pll_clk);
+               goto err_del_component;
        }
 
        dsi->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
        if (IS_ERR(dsi->bus_clk)) {
                dev_info(&pdev->dev, "failed to get dsi bus clock\n");
-               return -EPROBE_DEFER;
+               ret = PTR_ERR(dsi->bus_clk);
+               goto err_del_component;
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        dsi->reg_base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(dsi->reg_base)) {
                dev_err(&pdev->dev, "failed to remap io region\n");
-               return PTR_ERR(dsi->reg_base);
+               ret = PTR_ERR(dsi->reg_base);
+               goto err_del_component;
        }
 
        dsi->phy = devm_phy_get(&pdev->dev, "dsim");
        if (IS_ERR(dsi->phy)) {
                dev_info(&pdev->dev, "failed to get dsim phy\n");
-               return -EPROBE_DEFER;
+               ret = PTR_ERR(dsi->phy);
+               goto err_del_component;
        }
 
        dsi->irq = platform_get_irq(pdev, 0);
        if (dsi->irq < 0) {
                dev_err(&pdev->dev, "failed to request dsi irq resource\n");
-               return dsi->irq;
+               ret = dsi->irq;
+               goto err_del_component;
        }
 
        irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
@@ -1449,58 +1499,31 @@ static int exynos_dsi_probe(struct platform_device *pdev)
                                        dev_name(&pdev->dev), dsi);
        if (ret) {
                dev_err(&pdev->dev, "failed to request dsi irq\n");
-               return ret;
+               goto err_del_component;
        }
 
        exynos_dsi_display.ctx = dsi;
 
        platform_set_drvdata(pdev, &exynos_dsi_display);
-       exynos_drm_display_register(&exynos_dsi_display);
-
-       return mipi_dsi_host_register(&dsi->dsi_host);
-}
-
-static int exynos_dsi_remove(struct platform_device *pdev)
-{
-       struct exynos_dsi *dsi = exynos_dsi_display.ctx;
-
-       exynos_dsi_dpms(&exynos_dsi_display, DRM_MODE_DPMS_OFF);
-
-       exynos_drm_display_unregister(&exynos_dsi_display);
-       mipi_dsi_host_unregister(&dsi->dsi_host);
-
-       return 0;
-}
 
-#if CONFIG_PM_SLEEP
-static int exynos_dsi_resume(struct device *dev)
-{
-       struct exynos_dsi *dsi = exynos_dsi_display.ctx;
+       ret = component_add(&pdev->dev, &exynos_dsi_component_ops);
+       if (ret)
+               goto err_del_component;
 
-       if (dsi->state & DSIM_STATE_ENABLED) {
-               dsi->state &= ~DSIM_STATE_ENABLED;
-               exynos_dsi_enable(dsi);
-       }
+       return ret;
 
-       return 0;
+err_del_component:
+       exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
+       return ret;
 }
 
-static int exynos_dsi_suspend(struct device *dev)
+static int exynos_dsi_remove(struct platform_device *pdev)
 {
-       struct exynos_dsi *dsi = exynos_dsi_display.ctx;
-
-       if (dsi->state & DSIM_STATE_ENABLED) {
-               exynos_dsi_disable(dsi);
-               dsi->state |= DSIM_STATE_ENABLED;
-       }
+       component_del(&pdev->dev, &exynos_dsi_component_ops);
+       exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
 
        return 0;
 }
-#endif
-
-static const struct dev_pm_ops exynos_dsi_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume)
-};
 
 static struct of_device_id exynos_dsi_of_match[] = {
        { .compatible = "samsung,exynos4210-mipi-dsi" },
@@ -1513,7 +1536,6 @@ struct platform_driver dsi_driver = {
        .driver = {
                   .name = "exynos-dsi",
                   .owner = THIS_MODULE,
-                  .pm = &exynos_dsi_pm_ops,
                   .of_match_table = exynos_dsi_of_match,
        },
 };
index addbf7536da45fe7748b1a79ba00b6df69f2f080..d771b467cf0c03c70446f58bc68d3029dd5f6305 100644 (file)
@@ -121,16 +121,8 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
        offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
        offset += fbi->var.yoffset * fb->pitches[0];
 
-       dev->mode_config.fb_base = (resource_size_t)buffer->dma_addr;
        fbi->screen_base = buffer->kvaddr + offset;
-       if (is_drm_iommu_supported(dev))
-               fbi->fix.smem_start = (unsigned long)
-                       (page_to_phys(sg_page(buffer->sgt->sgl)) + offset);
-       else
-               fbi->fix.smem_start = (unsigned long)buffer->dma_addr;
-
        fbi->screen_size = size;
-       fbi->fix.smem_len = size;
 
        return 0;
 }
@@ -237,7 +229,7 @@ static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
        .fb_probe =     exynos_drm_fbdev_create,
 };
 
-bool exynos_drm_fbdev_is_anything_connected(struct drm_device *dev)
+static bool exynos_drm_fbdev_is_anything_connected(struct drm_device *dev)
 {
        struct drm_connector *connector;
        bool ret = false;
@@ -375,7 +367,5 @@ void exynos_drm_fbdev_restore_mode(struct drm_device *dev)
        if (!private || !private->fb_helper)
                return;
 
-       drm_modeset_lock_all(dev);
-       drm_fb_helper_restore_fbdev_mode(private->fb_helper);
-       drm_modeset_unlock_all(dev);
+       drm_fb_helper_restore_fbdev_mode_unlocked(private->fb_helper);
 }
index 30d76b2ff9c26a8e2f3329dff66d8e301d04b11f..831dde9034c6270436946e59bb4a6a3b1adb3008 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/clk.h>
 #include <linux/pm_runtime.h>
 #include <linux/of.h>
+#include <linux/spinlock.h>
 
 #include <drm/drmP.h>
 #include <drm/exynos_drm.h>
@@ -57,7 +58,6 @@
 #define FIMC_SHFACTOR  10
 #define FIMC_BUF_STOP  1
 #define FIMC_BUF_START 2
-#define FIMC_REG_SZ            32
 #define FIMC_WIDTH_ITU_709     1280
 #define FIMC_REFRESH_MAX       60
 #define FIMC_REFRESH_MIN       12
@@ -69,9 +69,6 @@
 #define get_fimc_context(dev)  platform_get_drvdata(to_platform_device(dev))
 #define get_ctx_from_ippdrv(ippdrv)    container_of(ippdrv,\
                                        struct fimc_context, ippdrv);
-#define fimc_read(offset)              readl(ctx->regs + (offset))
-#define fimc_write(cfg, offset)        writel(cfg, ctx->regs + (offset))
-
 enum fimc_wb {
        FIMC_WB_NONE,
        FIMC_WB_A,
@@ -161,7 +158,7 @@ struct fimc_context {
        struct exynos_drm_ippdrv        ippdrv;
        struct resource *regs_res;
        void __iomem    *regs;
-       struct mutex    lock;
+       spinlock_t      lock;
        struct clk      *clocks[FIMC_CLKS_MAX];
        u32             clk_frequency;
        struct regmap   *sysreg;
@@ -172,39 +169,53 @@ struct fimc_context {
        bool    suspended;
 };
 
+static u32 fimc_read(struct fimc_context *ctx, u32 reg)
+{
+       return readl(ctx->regs + reg);
+}
+
+static void fimc_write(struct fimc_context *ctx, u32 val, u32 reg)
+{
+       writel(val, ctx->regs + reg);
+}
+
+static void fimc_set_bits(struct fimc_context *ctx, u32 reg, u32 bits)
+{
+       void __iomem *r = ctx->regs + reg;
+
+       writel(readl(r) | bits, r);
+}
+
+static void fimc_clear_bits(struct fimc_context *ctx, u32 reg, u32 bits)
+{
+       void __iomem *r = ctx->regs + reg;
+
+       writel(readl(r) & ~bits, r);
+}
+
 static void fimc_sw_reset(struct fimc_context *ctx)
 {
        u32 cfg;
 
        /* stop dma operation */
-       cfg = fimc_read(EXYNOS_CISTATUS);
-       if (EXYNOS_CISTATUS_GET_ENVID_STATUS(cfg)) {
-               cfg = fimc_read(EXYNOS_MSCTRL);
-               cfg &= ~EXYNOS_MSCTRL_ENVID;
-               fimc_write(cfg, EXYNOS_MSCTRL);
-       }
+       cfg = fimc_read(ctx, EXYNOS_CISTATUS);
+       if (EXYNOS_CISTATUS_GET_ENVID_STATUS(cfg))
+               fimc_clear_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID);
 
-       cfg = fimc_read(EXYNOS_CISRCFMT);
-       cfg |= EXYNOS_CISRCFMT_ITU601_8BIT;
-       fimc_write(cfg, EXYNOS_CISRCFMT);
+       fimc_set_bits(ctx, EXYNOS_CISRCFMT, EXYNOS_CISRCFMT_ITU601_8BIT);
 
        /* disable image capture */
-       cfg = fimc_read(EXYNOS_CIIMGCPT);
-       cfg &= ~(EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN);
-       fimc_write(cfg, EXYNOS_CIIMGCPT);
+       fimc_clear_bits(ctx, EXYNOS_CIIMGCPT,
+               EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN);
 
        /* s/w reset */
-       cfg = fimc_read(EXYNOS_CIGCTRL);
-       cfg |= (EXYNOS_CIGCTRL_SWRST);
-       fimc_write(cfg, EXYNOS_CIGCTRL);
+       fimc_set_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_SWRST);
 
        /* s/w reset complete */
-       cfg = fimc_read(EXYNOS_CIGCTRL);
-       cfg &= ~EXYNOS_CIGCTRL_SWRST;
-       fimc_write(cfg, EXYNOS_CIGCTRL);
+       fimc_clear_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_SWRST);
 
        /* reset sequence */
-       fimc_write(0x0, EXYNOS_CIFCNTSEQ);
+       fimc_write(ctx, 0x0, EXYNOS_CIFCNTSEQ);
 }
 
 static int fimc_set_camblk_fimd0_wb(struct fimc_context *ctx)
@@ -220,7 +231,7 @@ static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb)
 
        DRM_DEBUG_KMS("wb[%d]\n", wb);
 
-       cfg = fimc_read(EXYNOS_CIGCTRL);
+       cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
        cfg &= ~(EXYNOS_CIGCTRL_TESTPATTERN_MASK |
                EXYNOS_CIGCTRL_SELCAM_ITU_MASK |
                EXYNOS_CIGCTRL_SELCAM_MIPI_MASK |
@@ -246,7 +257,7 @@ static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb)
                break;
        }
 
-       fimc_write(cfg, EXYNOS_CIGCTRL);
+       fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
 }
 
 static void fimc_set_polarity(struct fimc_context *ctx,
@@ -259,7 +270,7 @@ static void fimc_set_polarity(struct fimc_context *ctx,
        DRM_DEBUG_KMS("inv_href[%d]inv_hsync[%d]\n",
                pol->inv_href, pol->inv_hsync);
 
-       cfg = fimc_read(EXYNOS_CIGCTRL);
+       cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
        cfg &= ~(EXYNOS_CIGCTRL_INVPOLPCLK | EXYNOS_CIGCTRL_INVPOLVSYNC |
                 EXYNOS_CIGCTRL_INVPOLHREF | EXYNOS_CIGCTRL_INVPOLHSYNC);
 
@@ -272,7 +283,7 @@ static void fimc_set_polarity(struct fimc_context *ctx,
        if (pol->inv_hsync)
                cfg |= EXYNOS_CIGCTRL_INVPOLHSYNC;
 
-       fimc_write(cfg, EXYNOS_CIGCTRL);
+       fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
 }
 
 static void fimc_handle_jpeg(struct fimc_context *ctx, bool enable)
@@ -281,70 +292,54 @@ static void fimc_handle_jpeg(struct fimc_context *ctx, bool enable)
 
        DRM_DEBUG_KMS("enable[%d]\n", enable);
 
-       cfg = fimc_read(EXYNOS_CIGCTRL);
+       cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
        if (enable)
                cfg |= EXYNOS_CIGCTRL_CAM_JPEG;
        else
                cfg &= ~EXYNOS_CIGCTRL_CAM_JPEG;
 
-       fimc_write(cfg, EXYNOS_CIGCTRL);
+       fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
 }
 
-static void fimc_handle_irq(struct fimc_context *ctx, bool enable,
-               bool overflow, bool level)
+static void fimc_mask_irq(struct fimc_context *ctx, bool enable)
 {
        u32 cfg;
 
-       DRM_DEBUG_KMS("enable[%d]overflow[%d]level[%d]\n",
-                       enable, overflow, level);
+       DRM_DEBUG_KMS("enable[%d]\n", enable);
 
-       cfg = fimc_read(EXYNOS_CIGCTRL);
+       cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
        if (enable) {
-               cfg &= ~(EXYNOS_CIGCTRL_IRQ_OVFEN | EXYNOS_CIGCTRL_IRQ_LEVEL);
-               cfg |= EXYNOS_CIGCTRL_IRQ_ENABLE;
-               if (overflow)
-                       cfg |= EXYNOS_CIGCTRL_IRQ_OVFEN;
-               if (level)
-                       cfg |= EXYNOS_CIGCTRL_IRQ_LEVEL;
+               cfg &= ~EXYNOS_CIGCTRL_IRQ_OVFEN;
+               cfg |= EXYNOS_CIGCTRL_IRQ_ENABLE | EXYNOS_CIGCTRL_IRQ_LEVEL;
        } else
-               cfg &= ~(EXYNOS_CIGCTRL_IRQ_OVFEN | EXYNOS_CIGCTRL_IRQ_ENABLE);
-
-       fimc_write(cfg, EXYNOS_CIGCTRL);
+               cfg &= ~EXYNOS_CIGCTRL_IRQ_ENABLE;
+       fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
 }
 
 static void fimc_clear_irq(struct fimc_context *ctx)
 {
-       u32 cfg;
-
-       cfg = fimc_read(EXYNOS_CIGCTRL);
-       cfg |= EXYNOS_CIGCTRL_IRQ_CLR;
-       fimc_write(cfg, EXYNOS_CIGCTRL);
+       fimc_set_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_IRQ_CLR);
 }
 
 static bool fimc_check_ovf(struct fimc_context *ctx)
 {
        struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
-       u32 cfg, status, flag;
+       u32 status, flag;
 
-       status = fimc_read(EXYNOS_CISTATUS);
+       status = fimc_read(ctx, EXYNOS_CISTATUS);
        flag = EXYNOS_CISTATUS_OVFIY | EXYNOS_CISTATUS_OVFICB |
                EXYNOS_CISTATUS_OVFICR;
 
        DRM_DEBUG_KMS("flag[0x%x]\n", flag);
 
        if (status & flag) {
-               cfg = fimc_read(EXYNOS_CIWDOFST);
-               cfg |= (EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB |
+               fimc_set_bits(ctx, EXYNOS_CIWDOFST,
+                       EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB |
                        EXYNOS_CIWDOFST_CLROVFICR);
-
-               fimc_write(cfg, EXYNOS_CIWDOFST);
-
-               cfg = fimc_read(EXYNOS_CIWDOFST);
-               cfg &= ~(EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB |
+               fimc_clear_bits(ctx, EXYNOS_CIWDOFST,
+                       EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB |
                        EXYNOS_CIWDOFST_CLROVFICR);
 
-               fimc_write(cfg, EXYNOS_CIWDOFST);
-
                dev_err(ippdrv->dev, "occurred overflow at %d, status 0x%x.\n",
                        ctx->id, status);
                return true;
@@ -357,7 +352,7 @@ static bool fimc_check_frame_end(struct fimc_context *ctx)
 {
        u32 cfg;
 
-       cfg = fimc_read(EXYNOS_CISTATUS);
+       cfg = fimc_read(ctx, EXYNOS_CISTATUS);
 
        DRM_DEBUG_KMS("cfg[0x%x]\n", cfg);
 
@@ -365,7 +360,7 @@ static bool fimc_check_frame_end(struct fimc_context *ctx)
                return false;
 
        cfg &= ~(EXYNOS_CISTATUS_FRAMEEND);
-       fimc_write(cfg, EXYNOS_CISTATUS);
+       fimc_write(ctx, cfg, EXYNOS_CISTATUS);
 
        return true;
 }
@@ -375,7 +370,7 @@ static int fimc_get_buf_id(struct fimc_context *ctx)
        u32 cfg;
        int frame_cnt, buf_id;
 
-       cfg = fimc_read(EXYNOS_CISTATUS2);
+       cfg = fimc_read(ctx, EXYNOS_CISTATUS2);
        frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(cfg);
 
        if (frame_cnt == 0)
@@ -402,13 +397,13 @@ static void fimc_handle_lastend(struct fimc_context *ctx, bool enable)
 
        DRM_DEBUG_KMS("enable[%d]\n", enable);
 
-       cfg = fimc_read(EXYNOS_CIOCTRL);
+       cfg = fimc_read(ctx, EXYNOS_CIOCTRL);
        if (enable)
                cfg |= EXYNOS_CIOCTRL_LASTENDEN;
        else
                cfg &= ~EXYNOS_CIOCTRL_LASTENDEN;
 
-       fimc_write(cfg, EXYNOS_CIOCTRL);
+       fimc_write(ctx, cfg, EXYNOS_CIOCTRL);
 }
 
 
@@ -420,18 +415,18 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt)
        DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
 
        /* RGB */
-       cfg = fimc_read(EXYNOS_CISCCTRL);
+       cfg = fimc_read(ctx, EXYNOS_CISCCTRL);
        cfg &= ~EXYNOS_CISCCTRL_INRGB_FMT_RGB_MASK;
 
        switch (fmt) {
        case DRM_FORMAT_RGB565:
                cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB565;
-               fimc_write(cfg, EXYNOS_CISCCTRL);
+               fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
                return 0;
        case DRM_FORMAT_RGB888:
        case DRM_FORMAT_XRGB8888:
                cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB888;
-               fimc_write(cfg, EXYNOS_CISCCTRL);
+               fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
                return 0;
        default:
                /* bypass */
@@ -439,7 +434,7 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt)
        }
 
        /* YUV */
-       cfg = fimc_read(EXYNOS_MSCTRL);
+       cfg = fimc_read(ctx, EXYNOS_MSCTRL);
        cfg &= ~(EXYNOS_MSCTRL_ORDER2P_SHIFT_MASK |
                EXYNOS_MSCTRL_C_INT_IN_2PLANE |
                EXYNOS_MSCTRL_ORDER422_YCBYCR);
@@ -479,7 +474,7 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt)
                return -EINVAL;
        }
 
-       fimc_write(cfg, EXYNOS_MSCTRL);
+       fimc_write(ctx, cfg, EXYNOS_MSCTRL);
 
        return 0;
 }
@@ -492,7 +487,7 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt)
 
        DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
 
-       cfg = fimc_read(EXYNOS_MSCTRL);
+       cfg = fimc_read(ctx, EXYNOS_MSCTRL);
        cfg &= ~EXYNOS_MSCTRL_INFORMAT_RGB;
 
        switch (fmt) {
@@ -527,9 +522,9 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt)
                return -EINVAL;
        }
 
-       fimc_write(cfg, EXYNOS_MSCTRL);
+       fimc_write(ctx, cfg, EXYNOS_MSCTRL);
 
-       cfg = fimc_read(EXYNOS_CIDMAPARAM);
+       cfg = fimc_read(ctx, EXYNOS_CIDMAPARAM);
        cfg &= ~EXYNOS_CIDMAPARAM_R_MODE_MASK;
 
        if (fmt == DRM_FORMAT_NV12MT)
@@ -537,7 +532,7 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt)
        else
                cfg |= EXYNOS_CIDMAPARAM_R_MODE_LINEAR;
 
-       fimc_write(cfg, EXYNOS_CIDMAPARAM);
+       fimc_write(ctx, cfg, EXYNOS_CIDMAPARAM);
 
        return fimc_src_set_fmt_order(ctx, fmt);
 }
@@ -552,11 +547,11 @@ static int fimc_src_set_transf(struct device *dev,
 
        DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip);
 
-       cfg1 = fimc_read(EXYNOS_MSCTRL);
+       cfg1 = fimc_read(ctx, EXYNOS_MSCTRL);
        cfg1 &= ~(EXYNOS_MSCTRL_FLIP_X_MIRROR |
                EXYNOS_MSCTRL_FLIP_Y_MIRROR);
 
-       cfg2 = fimc_read(EXYNOS_CITRGFMT);
+       cfg2 = fimc_read(ctx, EXYNOS_CITRGFMT);
        cfg2 &= ~EXYNOS_CITRGFMT_INROT90_CLOCKWISE;
 
        switch (degree) {
@@ -595,8 +590,8 @@ static int fimc_src_set_transf(struct device *dev,
                return -EINVAL;
        }
 
-       fimc_write(cfg1, EXYNOS_MSCTRL);
-       fimc_write(cfg2, EXYNOS_CITRGFMT);
+       fimc_write(ctx, cfg1, EXYNOS_MSCTRL);
+       fimc_write(ctx, cfg2, EXYNOS_CITRGFMT);
        *swap = (cfg2 & EXYNOS_CITRGFMT_INROT90_CLOCKWISE) ? 1 : 0;
 
        return 0;
@@ -621,17 +616,17 @@ static int fimc_set_window(struct fimc_context *ctx,
         * set window offset 1, 2 size
         * check figure 43-21 in user manual
         */
-       cfg = fimc_read(EXYNOS_CIWDOFST);
+       cfg = fimc_read(ctx, EXYNOS_CIWDOFST);
        cfg &= ~(EXYNOS_CIWDOFST_WINHOROFST_MASK |
                EXYNOS_CIWDOFST_WINVEROFST_MASK);
        cfg |= (EXYNOS_CIWDOFST_WINHOROFST(h1) |
                EXYNOS_CIWDOFST_WINVEROFST(v1));
        cfg |= EXYNOS_CIWDOFST_WINOFSEN;
-       fimc_write(cfg, EXYNOS_CIWDOFST);
+       fimc_write(ctx, cfg, EXYNOS_CIWDOFST);
 
        cfg = (EXYNOS_CIWDOFST2_WINHOROFST2(h2) |
                EXYNOS_CIWDOFST2_WINVEROFST2(v2));
-       fimc_write(cfg, EXYNOS_CIWDOFST2);
+       fimc_write(ctx, cfg, EXYNOS_CIWDOFST2);
 
        return 0;
 }
@@ -651,7 +646,7 @@ static int fimc_src_set_size(struct device *dev, int swap,
        cfg = (EXYNOS_ORGISIZE_HORIZONTAL(img_sz.hsize) |
                EXYNOS_ORGISIZE_VERTICAL(img_sz.vsize));
 
-       fimc_write(cfg, EXYNOS_ORGISIZE);
+       fimc_write(ctx, cfg, EXYNOS_ORGISIZE);
 
        DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", pos->x, pos->y, pos->w, pos->h);
 
@@ -663,12 +658,12 @@ static int fimc_src_set_size(struct device *dev, int swap,
        }
 
        /* set input DMA image size */
-       cfg = fimc_read(EXYNOS_CIREAL_ISIZE);
+       cfg = fimc_read(ctx, EXYNOS_CIREAL_ISIZE);
        cfg &= ~(EXYNOS_CIREAL_ISIZE_HEIGHT_MASK |
                EXYNOS_CIREAL_ISIZE_WIDTH_MASK);
        cfg |= (EXYNOS_CIREAL_ISIZE_WIDTH(img_pos.w) |
                EXYNOS_CIREAL_ISIZE_HEIGHT(img_pos.h));
-       fimc_write(cfg, EXYNOS_CIREAL_ISIZE);
+       fimc_write(ctx, cfg, EXYNOS_CIREAL_ISIZE);
 
        /*
         * set input FIFO image size
@@ -677,18 +672,18 @@ static int fimc_src_set_size(struct device *dev, int swap,
        cfg = (EXYNOS_CISRCFMT_ITU601_8BIT |
                EXYNOS_CISRCFMT_SOURCEHSIZE(img_sz.hsize) |
                EXYNOS_CISRCFMT_SOURCEVSIZE(img_sz.vsize));
-       fimc_write(cfg, EXYNOS_CISRCFMT);
+       fimc_write(ctx, cfg, EXYNOS_CISRCFMT);
 
        /* offset Y(RGB), Cb, Cr */
        cfg = (EXYNOS_CIIYOFF_HORIZONTAL(img_pos.x) |
                EXYNOS_CIIYOFF_VERTICAL(img_pos.y));
-       fimc_write(cfg, EXYNOS_CIIYOFF);
+       fimc_write(ctx, cfg, EXYNOS_CIIYOFF);
        cfg = (EXYNOS_CIICBOFF_HORIZONTAL(img_pos.x) |
                EXYNOS_CIICBOFF_VERTICAL(img_pos.y));
-       fimc_write(cfg, EXYNOS_CIICBOFF);
+       fimc_write(ctx, cfg, EXYNOS_CIICBOFF);
        cfg = (EXYNOS_CIICROFF_HORIZONTAL(img_pos.x) |
                EXYNOS_CIICROFF_VERTICAL(img_pos.y));
-       fimc_write(cfg, EXYNOS_CIICROFF);
+       fimc_write(ctx, cfg, EXYNOS_CIICROFF);
 
        return fimc_set_window(ctx, &img_pos, &img_sz);
 }
@@ -722,25 +717,25 @@ static int fimc_src_set_addr(struct device *dev,
        switch (buf_type) {
        case IPP_BUF_ENQUEUE:
                config = &property->config[EXYNOS_DRM_OPS_SRC];
-               fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y],
+               fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_Y],
                        EXYNOS_CIIYSA(buf_id));
 
                if (config->fmt == DRM_FORMAT_YVU420) {
-                       fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR],
+                       fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR],
                                EXYNOS_CIICBSA(buf_id));
-                       fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB],
+                       fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
                                EXYNOS_CIICRSA(buf_id));
                } else {
-                       fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB],
+                       fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
                                EXYNOS_CIICBSA(buf_id));
-                       fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR],
+                       fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR],
                                EXYNOS_CIICRSA(buf_id));
                }
                break;
        case IPP_BUF_DEQUEUE:
-               fimc_write(0x0, EXYNOS_CIIYSA(buf_id));
-               fimc_write(0x0, EXYNOS_CIICBSA(buf_id));
-               fimc_write(0x0, EXYNOS_CIICRSA(buf_id));
+               fimc_write(ctx, 0x0, EXYNOS_CIIYSA(buf_id));
+               fimc_write(ctx, 0x0, EXYNOS_CIICBSA(buf_id));
+               fimc_write(ctx, 0x0, EXYNOS_CIICRSA(buf_id));
                break;
        default:
                /* bypass */
@@ -765,22 +760,22 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt)
        DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
 
        /* RGB */
-       cfg = fimc_read(EXYNOS_CISCCTRL);
+       cfg = fimc_read(ctx, EXYNOS_CISCCTRL);
        cfg &= ~EXYNOS_CISCCTRL_OUTRGB_FMT_RGB_MASK;
 
        switch (fmt) {
        case DRM_FORMAT_RGB565:
                cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB565;
-               fimc_write(cfg, EXYNOS_CISCCTRL);
+               fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
                return 0;
        case DRM_FORMAT_RGB888:
                cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888;
-               fimc_write(cfg, EXYNOS_CISCCTRL);
+               fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
                return 0;
        case DRM_FORMAT_XRGB8888:
                cfg |= (EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888 |
                        EXYNOS_CISCCTRL_EXTRGB_EXTENSION);
-               fimc_write(cfg, EXYNOS_CISCCTRL);
+               fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
                break;
        default:
                /* bypass */
@@ -788,7 +783,7 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt)
        }
 
        /* YUV */
-       cfg = fimc_read(EXYNOS_CIOCTRL);
+       cfg = fimc_read(ctx, EXYNOS_CIOCTRL);
        cfg &= ~(EXYNOS_CIOCTRL_ORDER2P_MASK |
                EXYNOS_CIOCTRL_ORDER422_MASK |
                EXYNOS_CIOCTRL_YCBCR_PLANE_MASK);
@@ -830,7 +825,7 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt)
                return -EINVAL;
        }
 
-       fimc_write(cfg, EXYNOS_CIOCTRL);
+       fimc_write(ctx, cfg, EXYNOS_CIOCTRL);
 
        return 0;
 }
@@ -843,16 +838,16 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt)
 
        DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
 
-       cfg = fimc_read(EXYNOS_CIEXTEN);
+       cfg = fimc_read(ctx, EXYNOS_CIEXTEN);
 
        if (fmt == DRM_FORMAT_AYUV) {
                cfg |= EXYNOS_CIEXTEN_YUV444_OUT;
-               fimc_write(cfg, EXYNOS_CIEXTEN);
+               fimc_write(ctx, cfg, EXYNOS_CIEXTEN);
        } else {
                cfg &= ~EXYNOS_CIEXTEN_YUV444_OUT;
-               fimc_write(cfg, EXYNOS_CIEXTEN);
+               fimc_write(ctx, cfg, EXYNOS_CIEXTEN);
 
-               cfg = fimc_read(EXYNOS_CITRGFMT);
+               cfg = fimc_read(ctx, EXYNOS_CITRGFMT);
                cfg &= ~EXYNOS_CITRGFMT_OUTFORMAT_MASK;
 
                switch (fmt) {
@@ -885,10 +880,10 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt)
                        return -EINVAL;
                }
 
-               fimc_write(cfg, EXYNOS_CITRGFMT);
+               fimc_write(ctx, cfg, EXYNOS_CITRGFMT);
        }
 
-       cfg = fimc_read(EXYNOS_CIDMAPARAM);
+       cfg = fimc_read(ctx, EXYNOS_CIDMAPARAM);
        cfg &= ~EXYNOS_CIDMAPARAM_W_MODE_MASK;
 
        if (fmt == DRM_FORMAT_NV12MT)
@@ -896,7 +891,7 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt)
        else
                cfg |= EXYNOS_CIDMAPARAM_W_MODE_LINEAR;
 
-       fimc_write(cfg, EXYNOS_CIDMAPARAM);
+       fimc_write(ctx, cfg, EXYNOS_CIDMAPARAM);
 
        return fimc_dst_set_fmt_order(ctx, fmt);
 }
@@ -911,7 +906,7 @@ static int fimc_dst_set_transf(struct device *dev,
 
        DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip);
 
-       cfg = fimc_read(EXYNOS_CITRGFMT);
+       cfg = fimc_read(ctx, EXYNOS_CITRGFMT);
        cfg &= ~EXYNOS_CITRGFMT_FLIP_MASK;
        cfg &= ~EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE;
 
@@ -951,53 +946,23 @@ static int fimc_dst_set_transf(struct device *dev,
                return -EINVAL;
        }
 
-       fimc_write(cfg, EXYNOS_CITRGFMT);
+       fimc_write(ctx, cfg, EXYNOS_CITRGFMT);
        *swap = (cfg & EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE) ? 1 : 0;
 
        return 0;
 }
 
-static int fimc_get_ratio_shift(u32 src, u32 dst, u32 *ratio, u32 *shift)
-{
-       DRM_DEBUG_KMS("src[%d]dst[%d]\n", src, dst);
-
-       if (src >= dst * 64) {
-               DRM_ERROR("failed to make ratio and shift.\n");
-               return -EINVAL;
-       } else if (src >= dst * 32) {
-               *ratio = 32;
-               *shift = 5;
-       } else if (src >= dst * 16) {
-               *ratio = 16;
-               *shift = 4;
-       } else if (src >= dst * 8) {
-               *ratio = 8;
-               *shift = 3;
-       } else if (src >= dst * 4) {
-               *ratio = 4;
-               *shift = 2;
-       } else if (src >= dst * 2) {
-               *ratio = 2;
-               *shift = 1;
-       } else {
-               *ratio = 1;
-               *shift = 0;
-       }
-
-       return 0;
-}
-
 static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc,
                struct drm_exynos_pos *src, struct drm_exynos_pos *dst)
 {
        struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
        u32 cfg, cfg_ext, shfactor;
        u32 pre_dst_width, pre_dst_height;
-       u32 pre_hratio, hfactor, pre_vratio, vfactor;
+       u32 hfactor, vfactor;
        int ret = 0;
        u32 src_w, src_h, dst_w, dst_h;
 
-       cfg_ext = fimc_read(EXYNOS_CITRGFMT);
+       cfg_ext = fimc_read(ctx, EXYNOS_CITRGFMT);
        if (cfg_ext & EXYNOS_CITRGFMT_INROT90_CLOCKWISE) {
                src_w = src->h;
                src_h = src->w;
@@ -1014,24 +979,24 @@ static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc,
                dst_h = dst->h;
        }
 
-       ret = fimc_get_ratio_shift(src_w, dst_w, &pre_hratio, &hfactor);
-       if (ret) {
+       /* fimc_ippdrv_check_property assures that dividers are not null */
+       hfactor = fls(src_w / dst_w / 2);
+       if (hfactor > FIMC_SHFACTOR / 2) {
                dev_err(ippdrv->dev, "failed to get ratio horizontal.\n");
-               return ret;
+               return -EINVAL;
        }
 
-       ret = fimc_get_ratio_shift(src_h, dst_h, &pre_vratio, &vfactor);
-       if (ret) {
+       vfactor = fls(src_h / dst_h / 2);
+       if (vfactor > FIMC_SHFACTOR / 2) {
                dev_err(ippdrv->dev, "failed to get ratio vertical.\n");
-               return ret;
+               return -EINVAL;
        }
 
-       pre_dst_width = src_w / pre_hratio;
-       pre_dst_height = src_h / pre_vratio;
+       pre_dst_width = src_w >> hfactor;
+       pre_dst_height = src_h >> vfactor;
        DRM_DEBUG_KMS("pre_dst_width[%d]pre_dst_height[%d]\n",
                pre_dst_width, pre_dst_height);
-       DRM_DEBUG_KMS("pre_hratio[%d]hfactor[%d]pre_vratio[%d]vfactor[%d]\n",
-               pre_hratio, hfactor, pre_vratio, vfactor);
+       DRM_DEBUG_KMS("hfactor[%d]vfactor[%d]\n", hfactor, vfactor);
 
        sc->hratio = (src_w << 14) / (dst_w << hfactor);
        sc->vratio = (src_h << 14) / (dst_h << vfactor);
@@ -1044,13 +1009,13 @@ static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc,
        DRM_DEBUG_KMS("shfactor[%d]\n", shfactor);
 
        cfg = (EXYNOS_CISCPRERATIO_SHFACTOR(shfactor) |
-               EXYNOS_CISCPRERATIO_PREHORRATIO(pre_hratio) |
-               EXYNOS_CISCPRERATIO_PREVERRATIO(pre_vratio));
-       fimc_write(cfg, EXYNOS_CISCPRERATIO);
+               EXYNOS_CISCPRERATIO_PREHORRATIO(1 << hfactor) |
+               EXYNOS_CISCPRERATIO_PREVERRATIO(1 << vfactor));
+       fimc_write(ctx, cfg, EXYNOS_CISCPRERATIO);
 
        cfg = (EXYNOS_CISCPREDST_PREDSTWIDTH(pre_dst_width) |
                EXYNOS_CISCPREDST_PREDSTHEIGHT(pre_dst_height));
-       fimc_write(cfg, EXYNOS_CISCPREDST);
+       fimc_write(ctx, cfg, EXYNOS_CISCPREDST);
 
        return ret;
 }
@@ -1064,7 +1029,7 @@ static void fimc_set_scaler(struct fimc_context *ctx, struct fimc_scaler *sc)
        DRM_DEBUG_KMS("hratio[%d]vratio[%d]\n",
                sc->hratio, sc->vratio);
 
-       cfg = fimc_read(EXYNOS_CISCCTRL);
+       cfg = fimc_read(ctx, EXYNOS_CISCCTRL);
        cfg &= ~(EXYNOS_CISCCTRL_SCALERBYPASS |
                EXYNOS_CISCCTRL_SCALEUP_H | EXYNOS_CISCCTRL_SCALEUP_V |
                EXYNOS_CISCCTRL_MAIN_V_RATIO_MASK |
@@ -1084,14 +1049,14 @@ static void fimc_set_scaler(struct fimc_context *ctx, struct fimc_scaler *sc)
 
        cfg |= (EXYNOS_CISCCTRL_MAINHORRATIO((sc->hratio >> 6)) |
                EXYNOS_CISCCTRL_MAINVERRATIO((sc->vratio >> 6)));
-       fimc_write(cfg, EXYNOS_CISCCTRL);
+       fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
 
-       cfg_ext = fimc_read(EXYNOS_CIEXTEN);
+       cfg_ext = fimc_read(ctx, EXYNOS_CIEXTEN);
        cfg_ext &= ~EXYNOS_CIEXTEN_MAINHORRATIO_EXT_MASK;
        cfg_ext &= ~EXYNOS_CIEXTEN_MAINVERRATIO_EXT_MASK;
        cfg_ext |= (EXYNOS_CIEXTEN_MAINHORRATIO_EXT(sc->hratio) |
                EXYNOS_CIEXTEN_MAINVERRATIO_EXT(sc->vratio));
-       fimc_write(cfg_ext, EXYNOS_CIEXTEN);
+       fimc_write(ctx, cfg_ext, EXYNOS_CIEXTEN);
 }
 
 static int fimc_dst_set_size(struct device *dev, int swap,
@@ -1109,12 +1074,12 @@ static int fimc_dst_set_size(struct device *dev, int swap,
        cfg = (EXYNOS_ORGOSIZE_HORIZONTAL(img_sz.hsize) |
                EXYNOS_ORGOSIZE_VERTICAL(img_sz.vsize));
 
-       fimc_write(cfg, EXYNOS_ORGOSIZE);
+       fimc_write(ctx, cfg, EXYNOS_ORGOSIZE);
 
        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);
+       cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
        cfg &= ~EXYNOS_CIGCTRL_CSC_MASK;
 
        if (sz->hsize >= FIMC_WIDTH_ITU_709)
@@ -1122,7 +1087,7 @@ static int fimc_dst_set_size(struct device *dev, int swap,
        else
                cfg |= EXYNOS_CIGCTRL_CSC_ITU601;
 
-       fimc_write(cfg, EXYNOS_CIGCTRL);
+       fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
 
        if (swap) {
                img_pos.w = pos->h;
@@ -1132,41 +1097,38 @@ static int fimc_dst_set_size(struct device *dev, int swap,
        }
 
        /* target image size */
-       cfg = fimc_read(EXYNOS_CITRGFMT);
+       cfg = fimc_read(ctx, EXYNOS_CITRGFMT);
        cfg &= ~(EXYNOS_CITRGFMT_TARGETH_MASK |
                EXYNOS_CITRGFMT_TARGETV_MASK);
        cfg |= (EXYNOS_CITRGFMT_TARGETHSIZE(img_pos.w) |
                EXYNOS_CITRGFMT_TARGETVSIZE(img_pos.h));
-       fimc_write(cfg, EXYNOS_CITRGFMT);
+       fimc_write(ctx, cfg, EXYNOS_CITRGFMT);
 
        /* target area */
        cfg = EXYNOS_CITAREA_TARGET_AREA(img_pos.w * img_pos.h);
-       fimc_write(cfg, EXYNOS_CITAREA);
+       fimc_write(ctx, cfg, EXYNOS_CITAREA);
 
        /* offset Y(RGB), Cb, Cr */
        cfg = (EXYNOS_CIOYOFF_HORIZONTAL(img_pos.x) |
                EXYNOS_CIOYOFF_VERTICAL(img_pos.y));
-       fimc_write(cfg, EXYNOS_CIOYOFF);
+       fimc_write(ctx, cfg, EXYNOS_CIOYOFF);
        cfg = (EXYNOS_CIOCBOFF_HORIZONTAL(img_pos.x) |
                EXYNOS_CIOCBOFF_VERTICAL(img_pos.y));
-       fimc_write(cfg, EXYNOS_CIOCBOFF);
+       fimc_write(ctx, cfg, EXYNOS_CIOCBOFF);
        cfg = (EXYNOS_CIOCROFF_HORIZONTAL(img_pos.x) |
                EXYNOS_CIOCROFF_VERTICAL(img_pos.y));
-       fimc_write(cfg, EXYNOS_CIOCROFF);
+       fimc_write(ctx, cfg, EXYNOS_CIOCROFF);
 
        return 0;
 }
 
-static int fimc_dst_get_buf_seq(struct fimc_context *ctx)
+static int fimc_dst_get_buf_count(struct fimc_context *ctx)
 {
-       u32 cfg, i, buf_num = 0;
-       u32 mask = 0x00000001;
+       u32 cfg, buf_num;
 
-       cfg = fimc_read(EXYNOS_CIFCNTSEQ);
+       cfg = fimc_read(ctx, EXYNOS_CIFCNTSEQ);
 
-       for (i = 0; i < FIMC_REG_SZ; i++)
-               if (cfg & (mask << i))
-                       buf_num++;
+       buf_num = hweight32(cfg);
 
        DRM_DEBUG_KMS("buf_num[%d]\n", buf_num);
 
@@ -1181,13 +1143,14 @@ static int fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id,
        u32 cfg;
        u32 mask = 0x00000001 << buf_id;
        int ret = 0;
+       unsigned long flags;
 
        DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type);
 
-       mutex_lock(&ctx->lock);
+       spin_lock_irqsave(&ctx->lock, flags);
 
        /* mask register set */
-       cfg = fimc_read(EXYNOS_CIFCNTSEQ);
+       cfg = fimc_read(ctx, EXYNOS_CIFCNTSEQ);
 
        switch (buf_type) {
        case IPP_BUF_ENQUEUE:
@@ -1205,20 +1168,20 @@ static int fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id,
        /* sequence id */
        cfg &= ~mask;
        cfg |= (enable << buf_id);
-       fimc_write(cfg, EXYNOS_CIFCNTSEQ);
+       fimc_write(ctx, cfg, EXYNOS_CIFCNTSEQ);
 
        /* interrupt enable */
        if (buf_type == IPP_BUF_ENQUEUE &&
-           fimc_dst_get_buf_seq(ctx) >= FIMC_BUF_START)
-               fimc_handle_irq(ctx, true, false, true);
+           fimc_dst_get_buf_count(ctx) >= FIMC_BUF_START)
+               fimc_mask_irq(ctx, true);
 
        /* interrupt disable */
        if (buf_type == IPP_BUF_DEQUEUE &&
-           fimc_dst_get_buf_seq(ctx) <= FIMC_BUF_STOP)
-               fimc_handle_irq(ctx, false, false, true);
+           fimc_dst_get_buf_count(ctx) <= FIMC_BUF_STOP)
+               fimc_mask_irq(ctx, false);
 
 err_unlock:
-       mutex_unlock(&ctx->lock);
+       spin_unlock_irqrestore(&ctx->lock, flags);
        return ret;
 }
 
@@ -1252,25 +1215,25 @@ static int fimc_dst_set_addr(struct device *dev,
        case IPP_BUF_ENQUEUE:
                config = &property->config[EXYNOS_DRM_OPS_DST];
 
-               fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y],
+               fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_Y],
                        EXYNOS_CIOYSA(buf_id));
 
                if (config->fmt == DRM_FORMAT_YVU420) {
-                       fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR],
+                       fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR],
                                EXYNOS_CIOCBSA(buf_id));
-                       fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB],
+                       fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
                                EXYNOS_CIOCRSA(buf_id));
                } else {
-                       fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB],
+                       fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
                                EXYNOS_CIOCBSA(buf_id));
-                       fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR],
+                       fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR],
                                EXYNOS_CIOCRSA(buf_id));
                }
                break;
        case IPP_BUF_DEQUEUE:
-               fimc_write(0x0, EXYNOS_CIOYSA(buf_id));
-               fimc_write(0x0, EXYNOS_CIOCBSA(buf_id));
-               fimc_write(0x0, EXYNOS_CIOCRSA(buf_id));
+               fimc_write(ctx, 0x0, EXYNOS_CIOYSA(buf_id));
+               fimc_write(ctx, 0x0, EXYNOS_CIOCBSA(buf_id));
+               fimc_write(ctx, 0x0, EXYNOS_CIOCRSA(buf_id));
                break;
        default:
                /* bypass */
@@ -1342,11 +1305,7 @@ static irqreturn_t fimc_irq_handler(int irq, void *dev_id)
 
 static int fimc_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
 {
-       struct drm_exynos_ipp_prop_list *prop_list;
-
-       prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL);
-       if (!prop_list)
-               return -ENOMEM;
+       struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list;
 
        prop_list->version = 1;
        prop_list->writeback = 1;
@@ -1371,8 +1330,6 @@ static int fimc_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
        prop_list->scale_min.hsize = FIMC_SCALE_MIN;
        prop_list->scale_min.vsize = FIMC_SCALE_MIN;
 
-       ippdrv->prop_list = prop_list;
-
        return 0;
 }
 
@@ -1395,7 +1352,7 @@ static int fimc_ippdrv_check_property(struct device *dev,
 {
        struct fimc_context *ctx = get_fimc_context(dev);
        struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
-       struct drm_exynos_ipp_prop_list *pp = ippdrv->prop_list;
+       struct drm_exynos_ipp_prop_list *pp = &ippdrv->prop_list;
        struct drm_exynos_ipp_config *config;
        struct drm_exynos_pos *pos;
        struct drm_exynos_sz *sz;
@@ -1508,15 +1465,15 @@ static void fimc_clear_addr(struct fimc_context *ctx)
        int i;
 
        for (i = 0; i < FIMC_MAX_SRC; i++) {
-               fimc_write(0, EXYNOS_CIIYSA(i));
-               fimc_write(0, EXYNOS_CIICBSA(i));
-               fimc_write(0, EXYNOS_CIICRSA(i));
+               fimc_write(ctx, 0, EXYNOS_CIIYSA(i));
+               fimc_write(ctx, 0, EXYNOS_CIICBSA(i));
+               fimc_write(ctx, 0, EXYNOS_CIICRSA(i));
        }
 
        for (i = 0; i < FIMC_MAX_DST; i++) {
-               fimc_write(0, EXYNOS_CIOYSA(i));
-               fimc_write(0, EXYNOS_CIOCBSA(i));
-               fimc_write(0, EXYNOS_CIOCRSA(i));
+               fimc_write(ctx, 0, EXYNOS_CIOYSA(i));
+               fimc_write(ctx, 0, EXYNOS_CIOCBSA(i));
+               fimc_write(ctx, 0, EXYNOS_CIOCRSA(i));
        }
 }
 
@@ -1556,7 +1513,7 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
 
        property = &c_node->property;
 
-       fimc_handle_irq(ctx, true, false, true);
+       fimc_mask_irq(ctx, true);
 
        for_each_ipp_ops(i) {
                config = &property->config[i];
@@ -1582,10 +1539,10 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
                fimc_handle_lastend(ctx, false);
 
                /* setup dma */
-               cfg0 = fimc_read(EXYNOS_MSCTRL);
+               cfg0 = fimc_read(ctx, EXYNOS_MSCTRL);
                cfg0 &= ~EXYNOS_MSCTRL_INPUT_MASK;
                cfg0 |= EXYNOS_MSCTRL_INPUT_MEMORY;
-               fimc_write(cfg0, EXYNOS_MSCTRL);
+               fimc_write(ctx, cfg0, EXYNOS_MSCTRL);
                break;
        case IPP_CMD_WB:
                fimc_set_type_ctrl(ctx, FIMC_WB_A);
@@ -1610,41 +1567,33 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
        }
 
        /* Reset status */
-       fimc_write(0x0, EXYNOS_CISTATUS);
+       fimc_write(ctx, 0x0, EXYNOS_CISTATUS);
 
-       cfg0 = fimc_read(EXYNOS_CIIMGCPT);
+       cfg0 = fimc_read(ctx, EXYNOS_CIIMGCPT);
        cfg0 &= ~EXYNOS_CIIMGCPT_IMGCPTEN_SC;
        cfg0 |= EXYNOS_CIIMGCPT_IMGCPTEN_SC;
 
        /* Scaler */
-       cfg1 = fimc_read(EXYNOS_CISCCTRL);
+       cfg1 = fimc_read(ctx, EXYNOS_CISCCTRL);
        cfg1 &= ~EXYNOS_CISCCTRL_SCAN_MASK;
        cfg1 |= (EXYNOS_CISCCTRL_PROGRESSIVE |
                EXYNOS_CISCCTRL_SCALERSTART);
 
-       fimc_write(cfg1, EXYNOS_CISCCTRL);
+       fimc_write(ctx, cfg1, EXYNOS_CISCCTRL);
 
        /* Enable image capture*/
        cfg0 |= EXYNOS_CIIMGCPT_IMGCPTEN;
-       fimc_write(cfg0, EXYNOS_CIIMGCPT);
+       fimc_write(ctx, cfg0, EXYNOS_CIIMGCPT);
 
        /* Disable frame end irq */
-       cfg0 = fimc_read(EXYNOS_CIGCTRL);
-       cfg0 &= ~EXYNOS_CIGCTRL_IRQ_END_DISABLE;
-       fimc_write(cfg0, EXYNOS_CIGCTRL);
+       fimc_clear_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_IRQ_END_DISABLE);
 
-       cfg0 = fimc_read(EXYNOS_CIOCTRL);
-       cfg0 &= ~EXYNOS_CIOCTRL_WEAVE_MASK;
-       fimc_write(cfg0, EXYNOS_CIOCTRL);
+       fimc_clear_bits(ctx, EXYNOS_CIOCTRL, EXYNOS_CIOCTRL_WEAVE_MASK);
 
        if (cmd == IPP_CMD_M2M) {
-               cfg0 = fimc_read(EXYNOS_MSCTRL);
-               cfg0 |= EXYNOS_MSCTRL_ENVID;
-               fimc_write(cfg0, EXYNOS_MSCTRL);
+               fimc_set_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID);
 
-               cfg0 = fimc_read(EXYNOS_MSCTRL);
-               cfg0 |= EXYNOS_MSCTRL_ENVID;
-               fimc_write(cfg0, EXYNOS_MSCTRL);
+               fimc_set_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID);
        }
 
        return 0;
@@ -1661,10 +1610,10 @@ static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd)
        switch (cmd) {
        case IPP_CMD_M2M:
                /* Source clear */
-               cfg = fimc_read(EXYNOS_MSCTRL);
+               cfg = fimc_read(ctx, EXYNOS_MSCTRL);
                cfg &= ~EXYNOS_MSCTRL_INPUT_MASK;
                cfg &= ~EXYNOS_MSCTRL_ENVID;
-               fimc_write(cfg, EXYNOS_MSCTRL);
+               fimc_write(ctx, cfg, EXYNOS_MSCTRL);
                break;
        case IPP_CMD_WB:
                exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb);
@@ -1675,25 +1624,20 @@ static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd)
                break;
        }
 
-       fimc_handle_irq(ctx, false, false, true);
+       fimc_mask_irq(ctx, false);
 
        /* reset sequence */
-       fimc_write(0x0, EXYNOS_CIFCNTSEQ);
+       fimc_write(ctx, 0x0, EXYNOS_CIFCNTSEQ);
 
        /* Scaler disable */
-       cfg = fimc_read(EXYNOS_CISCCTRL);
-       cfg &= ~EXYNOS_CISCCTRL_SCALERSTART;
-       fimc_write(cfg, EXYNOS_CISCCTRL);
+       fimc_clear_bits(ctx, EXYNOS_CISCCTRL, EXYNOS_CISCCTRL_SCALERSTART);
 
        /* Disable image capture */
-       cfg = fimc_read(EXYNOS_CIIMGCPT);
-       cfg &= ~(EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN);
-       fimc_write(cfg, EXYNOS_CIIMGCPT);
+       fimc_clear_bits(ctx, EXYNOS_CIIMGCPT,
+               EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN);
 
        /* Enable frame end irq */
-       cfg = fimc_read(EXYNOS_CIGCTRL);
-       cfg |= EXYNOS_CIGCTRL_IRQ_END_DISABLE;
-       fimc_write(cfg, EXYNOS_CIGCTRL);
+       fimc_set_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_IRQ_END_DISABLE);
 }
 
 static void fimc_put_clocks(struct fimc_context *ctx)
@@ -1848,7 +1792,7 @@ static int fimc_probe(struct platform_device *pdev)
 
        DRM_DEBUG_KMS("id[%d]ippdrv[0x%x]\n", ctx->id, (int)ippdrv);
 
-       mutex_init(&ctx->lock);
+       spin_lock_init(&ctx->lock);
        platform_set_drvdata(pdev, ctx);
 
        pm_runtime_set_active(dev);
@@ -1879,7 +1823,6 @@ static int fimc_remove(struct platform_device *pdev)
        struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
 
        exynos_drm_ippdrv_unregister(ippdrv);
-       mutex_destroy(&ctx->lock);
 
        fimc_put_clocks(ctx);
        pm_runtime_set_suspended(dev);
index 40fd6ccfcd6f7595671d750b57ea44066ddf4b87..bb45ab2e7384efaab5eefcf13bcde9724d41b0f8 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/component.h>
 
 #include <video/of_display_timing.h>
 #include <video/of_videomode.h>
@@ -38,6 +39,7 @@
  */
 
 #define FIMD_DEFAULT_FRAMERATE 60
+#define MIN_FB_WIDTH_FOR_16WORD_BURST 128
 
 /* position control register for hardware window 0, 2 ~ 4.*/
 #define VIDOSD_A(win)          (VIDOSD_BASE + 0x00 + (win) * 16)
@@ -122,6 +124,7 @@ struct fimd_context {
 
        struct exynos_drm_panel_info panel;
        struct fimd_driver_data *driver_data;
+       struct exynos_drm_display *display;
 };
 
 static const struct of_device_id fimd_driver_dt_match[] = {
@@ -143,13 +146,57 @@ static inline struct fimd_driver_data *drm_fimd_get_driver_data(
        return (struct fimd_driver_data *)of_id->data;
 }
 
+static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr)
+{
+       struct fimd_context *ctx = mgr->ctx;
+
+       if (ctx->suspended)
+               return;
+
+       atomic_set(&ctx->wait_vsync_event, 1);
+
+       /*
+        * wait for FIMD to signal VSYNC interrupt or return after
+        * timeout which is set to 50ms (refresh rate of 20).
+        */
+       if (!wait_event_timeout(ctx->wait_vsync_queue,
+                               !atomic_read(&ctx->wait_vsync_event),
+                               HZ/20))
+               DRM_DEBUG_KMS("vblank wait timed out.\n");
+}
+
+
+static void fimd_clear_channel(struct exynos_drm_manager *mgr)
+{
+       struct fimd_context *ctx = mgr->ctx;
+       int win, ch_enabled = 0;
+
+       DRM_DEBUG_KMS("%s\n", __FILE__);
+
+       /* Check if any channel is enabled. */
+       for (win = 0; win < WINDOWS_NR; win++) {
+               u32 val = readl(ctx->regs + SHADOWCON);
+               if (val & SHADOWCON_CHx_ENABLE(win)) {
+                       val &= ~SHADOWCON_CHx_ENABLE(win);
+                       writel(val, ctx->regs + SHADOWCON);
+                       ch_enabled = 1;
+               }
+       }
+
+       /* Wait for vsync, as disable channel takes effect at next vsync */
+       if (ch_enabled)
+               fimd_wait_for_vblank(mgr);
+}
+
 static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
-                       struct drm_device *drm_dev, int pipe)
+                       struct drm_device *drm_dev)
 {
        struct fimd_context *ctx = mgr->ctx;
+       struct exynos_drm_private *priv;
+       priv = drm_dev->dev_private;
 
-       ctx->drm_dev = drm_dev;
-       ctx->pipe = pipe;
+       mgr->drm_dev = ctx->drm_dev = drm_dev;
+       mgr->pipe = ctx->pipe = priv->pipe++;
 
        /*
         * enable drm irq mode.
@@ -169,8 +216,14 @@ static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
        drm_dev->vblank_disable_allowed = true;
 
        /* attach this sub driver to iommu mapping if supported. */
-       if (is_drm_iommu_supported(ctx->drm_dev))
+       if (is_drm_iommu_supported(ctx->drm_dev)) {
+               /*
+                * If any channel is already active, iommu will throw
+                * a PAGE FAULT when enabled. So clear any channel if enabled.
+                */
+               fimd_clear_channel(mgr);
                drm_iommu_attach_device(ctx->drm_dev, ctx->dev);
+       }
 
        return 0;
 }
@@ -324,25 +377,6 @@ static void fimd_disable_vblank(struct exynos_drm_manager *mgr)
        }
 }
 
-static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr)
-{
-       struct fimd_context *ctx = mgr->ctx;
-
-       if (ctx->suspended)
-               return;
-
-       atomic_set(&ctx->wait_vsync_event, 1);
-
-       /*
-        * wait for FIMD to signal VSYNC interrupt or return after
-        * timeout which is set to 50ms (refresh rate of 20).
-        */
-       if (!wait_event_timeout(ctx->wait_vsync_queue,
-                               !atomic_read(&ctx->wait_vsync_event),
-                               HZ/20))
-               DRM_DEBUG_KMS("vblank wait timed out.\n");
-}
-
 static void fimd_win_mode_set(struct exynos_drm_manager *mgr,
                        struct exynos_drm_overlay *overlay)
 {
@@ -446,6 +480,19 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
 
        DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp);
 
+       /*
+        * In case of exynos, setting dma-burst to 16Word causes permanent
+        * tearing for very small buffers, e.g. cursor buffer. Burst Mode
+        * switching which is based on overlay size is not recommended as
+        * overlay size varies alot towards the end of the screen and rapid
+        * movement causes unstable DMA which results into iommu crash/tear.
+        */
+
+       if (win_data->fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST) {
+               val &= ~WINCONx_BURSTLEN_MASK;
+               val |= WINCONx_BURSTLEN_4WORD;
+       }
+
        writel(val, ctx->regs + WINCON(win));
 }
 
@@ -656,19 +703,6 @@ static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
        win_data->enabled = false;
 }
 
-static void fimd_clear_win(struct fimd_context *ctx, int win)
-{
-       writel(0, ctx->regs + WINCON(win));
-       writel(0, ctx->regs + VIDOSD_A(win));
-       writel(0, ctx->regs + VIDOSD_B(win));
-       writel(0, ctx->regs + VIDOSD_C(win));
-
-       if (win == 1 || win == 2)
-               writel(0, ctx->regs + VIDOSD_D(win));
-
-       fimd_shadow_protect_win(ctx, win, false);
-}
-
 static void fimd_window_suspend(struct exynos_drm_manager *mgr)
 {
        struct fimd_context *ctx = mgr->ctx;
@@ -803,8 +837,6 @@ static void fimd_dpms(struct exynos_drm_manager *mgr, int mode)
 }
 
 static struct exynos_drm_manager_ops fimd_manager_ops = {
-       .initialize = fimd_mgr_initialize,
-       .remove = fimd_mgr_remove,
        .dpms = fimd_dpms,
        .mode_fixup = fimd_mode_fixup,
        .mode_set = fimd_mode_set,
@@ -849,20 +881,64 @@ out:
        return IRQ_HANDLED;
 }
 
+static int fimd_bind(struct device *dev, struct device *master, void *data)
+{
+       struct fimd_context *ctx = fimd_manager.ctx;
+       struct drm_device *drm_dev = data;
+
+       fimd_mgr_initialize(&fimd_manager, drm_dev);
+       exynos_drm_crtc_create(&fimd_manager);
+       if (ctx->display)
+               exynos_drm_create_enc_conn(drm_dev, ctx->display);
+
+       return 0;
+
+}
+
+static void fimd_unbind(struct device *dev, struct device *master,
+                       void *data)
+{
+       struct exynos_drm_manager *mgr = dev_get_drvdata(dev);
+       struct fimd_context *ctx = fimd_manager.ctx;
+       struct drm_crtc *crtc = mgr->crtc;
+
+       fimd_dpms(mgr, DRM_MODE_DPMS_OFF);
+
+       if (ctx->display)
+               exynos_dpi_remove(dev);
+
+       fimd_mgr_remove(mgr);
+
+       crtc->funcs->destroy(crtc);
+}
+
+static const struct component_ops fimd_component_ops = {
+       .bind   = fimd_bind,
+       .unbind = fimd_unbind,
+};
+
 static int fimd_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct fimd_context *ctx;
        struct resource *res;
-       int win;
        int ret = -EINVAL;
 
-       if (!dev->of_node)
-               return -ENODEV;
+       ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
+                                       fimd_manager.type);
+       if (ret)
+               return ret;
+
+       if (!dev->of_node) {
+               ret = -ENODEV;
+               goto err_del_component;
+       }
 
        ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
-       if (!ctx)
-               return -ENOMEM;
+       if (!ctx) {
+               ret = -ENOMEM;
+               goto err_del_component;
+       }
 
        ctx->dev = dev;
        ctx->suspended = true;
@@ -875,32 +951,37 @@ static int fimd_probe(struct platform_device *pdev)
        ctx->bus_clk = devm_clk_get(dev, "fimd");
        if (IS_ERR(ctx->bus_clk)) {
                dev_err(dev, "failed to get bus clock\n");
-               return PTR_ERR(ctx->bus_clk);
+               ret = PTR_ERR(ctx->bus_clk);
+               goto err_del_component;
        }
 
        ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
        if (IS_ERR(ctx->lcd_clk)) {
                dev_err(dev, "failed to get lcd clock\n");
-               return PTR_ERR(ctx->lcd_clk);
+               ret = PTR_ERR(ctx->lcd_clk);
+               goto err_del_component;
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
        ctx->regs = devm_ioremap_resource(dev, res);
-       if (IS_ERR(ctx->regs))
-               return PTR_ERR(ctx->regs);
+       if (IS_ERR(ctx->regs)) {
+               ret = PTR_ERR(ctx->regs);
+               goto err_del_component;
+       }
 
        res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync");
        if (!res) {
                dev_err(dev, "irq request failed.\n");
-               return -ENXIO;
+               ret = -ENXIO;
+               goto err_del_component;
        }
 
        ret = devm_request_irq(dev, res->start, fimd_irq_handler,
                                                        0, "drm_fimd", ctx);
        if (ret) {
                dev_err(dev, "irq request failed.\n");
-               return ret;
+               goto err_del_component;
        }
 
        ctx->driver_data = drm_fimd_get_driver_data(pdev);
@@ -910,30 +991,34 @@ static int fimd_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, &fimd_manager);
 
        fimd_manager.ctx = ctx;
-       exynos_drm_manager_register(&fimd_manager);
 
-       exynos_dpi_probe(ctx->dev);
+       ctx->display = exynos_dpi_probe(dev);
+       if (IS_ERR(ctx->display))
+               return PTR_ERR(ctx->display);
 
-       pm_runtime_enable(dev);
+       pm_runtime_enable(&pdev->dev);
 
-       for (win = 0; win < WINDOWS_NR; win++)
-               fimd_clear_win(ctx, win);
+       ret = component_add(&pdev->dev, &fimd_component_ops);
+       if (ret)
+               goto err_disable_pm_runtime;
 
-       return 0;
+       return ret;
+
+err_disable_pm_runtime:
+       pm_runtime_disable(&pdev->dev);
+
+err_del_component:
+       exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
+       return ret;
 }
 
 static int fimd_remove(struct platform_device *pdev)
 {
-       struct exynos_drm_manager *mgr = platform_get_drvdata(pdev);
-
-       exynos_dpi_remove(&pdev->dev);
-
-       exynos_drm_manager_unregister(&fimd_manager);
-
-       fimd_dpms(mgr, DRM_MODE_DPMS_OFF);
-
        pm_runtime_disable(&pdev->dev);
 
+       component_del(&pdev->dev, &fimd_component_ops);
+       exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
+
        return 0;
 }
 
index 42d2904d88c7e32d90cd6d2dd56f97a3c917dc84..163a054922cb89f1845e2fe23c4d46b6c442f0f2 100644 (file)
@@ -612,22 +612,20 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
        args->pitch = args->width * ((args->bpp + 7) / 8);
        args->size = args->pitch * args->height;
 
-       exynos_gem_obj = exynos_drm_gem_create(dev, EXYNOS_BO_CONTIG |
-                                               EXYNOS_BO_WC, args->size);
-       /*
-        * If physically contiguous memory allocation fails and if IOMMU is
-        * supported then try to get buffer from non physically contiguous
-        * memory area.
-        */
-       if (IS_ERR(exynos_gem_obj) && is_drm_iommu_supported(dev)) {
-               dev_warn(dev->dev, "contiguous FB allocation failed, falling back to non-contiguous\n");
+       if (is_drm_iommu_supported(dev)) {
+               exynos_gem_obj = exynos_drm_gem_create(dev,
+                       EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC,
+                       args->size);
+       } else {
                exynos_gem_obj = exynos_drm_gem_create(dev,
-                                       EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC,
-                                       args->size);
+                       EXYNOS_BO_CONTIG | EXYNOS_BO_WC,
+                       args->size);
        }
 
-       if (IS_ERR(exynos_gem_obj))
+       if (IS_ERR(exynos_gem_obj)) {
+               dev_warn(dev->dev, "FB allocation failed.\n");
                return PTR_ERR(exynos_gem_obj);
+       }
 
        ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
                        &args->handle);
index fa75059a61047e7c51fc17adad52275f59177589..9e3ff1672965d3e17044d0e59fa524193404ff02 100644 (file)
@@ -1335,11 +1335,7 @@ static irqreturn_t gsc_irq_handler(int irq, void *dev_id)
 
 static int gsc_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
 {
-       struct drm_exynos_ipp_prop_list *prop_list;
-
-       prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL);
-       if (!prop_list)
-               return -ENOMEM;
+       struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list;
 
        prop_list->version = 1;
        prop_list->writeback = 1;
@@ -1363,8 +1359,6 @@ static int gsc_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
        prop_list->scale_min.hsize = GSC_SCALE_MIN;
        prop_list->scale_min.vsize = GSC_SCALE_MIN;
 
-       ippdrv->prop_list = prop_list;
-
        return 0;
 }
 
@@ -1387,7 +1381,7 @@ static int gsc_ippdrv_check_property(struct device *dev,
 {
        struct gsc_context *ctx = get_gsc_context(dev);
        struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
-       struct drm_exynos_ipp_prop_list *pp = ippdrv->prop_list;
+       struct drm_exynos_ipp_prop_list *pp = &ippdrv->prop_list;
        struct drm_exynos_ipp_config *config;
        struct drm_exynos_pos *pos;
        struct drm_exynos_sz *sz;
index 3d78144387ac6cf091a8434172c0654ef9f2f819..a1888e128f1d306e0c10934cb6042c83f4ce10e7 100644 (file)
@@ -167,6 +167,13 @@ static int ipp_create_id(struct idr *id_idr, struct mutex *lock, void *obj,
        return 0;
 }
 
+static void ipp_remove_id(struct idr *id_idr, struct mutex *lock, u32 id)
+{
+       mutex_lock(lock);
+       idr_remove(id_idr, id);
+       mutex_unlock(lock);
+}
+
 static void *ipp_find_obj(struct idr *id_idr, struct mutex *lock, u32 id)
 {
        void *obj;
@@ -276,11 +283,6 @@ static struct exynos_drm_ippdrv *ipp_find_drv_by_handle(u32 prop_id)
 
        DRM_DEBUG_KMS("prop_id[%d]\n", prop_id);
 
-       if (list_empty(&exynos_drm_ippdrv_list)) {
-               DRM_DEBUG_KMS("ippdrv_list is empty.\n");
-               return ERR_PTR(-ENODEV);
-       }
-
        /*
         * This case is search ipp driver by prop_id handle.
         * sometimes, ipp subsystem find driver by prop_id.
@@ -289,11 +291,14 @@ static struct exynos_drm_ippdrv *ipp_find_drv_by_handle(u32 prop_id)
        list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
                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)
-                               if (c_node->property.prop_id == prop_id)
-                                       return ippdrv;
+               mutex_lock(&ippdrv->cmd_lock);
+               list_for_each_entry(c_node, &ippdrv->cmd_list, list) {
+                       if (c_node->property.prop_id == prop_id) {
+                               mutex_unlock(&ippdrv->cmd_lock);
+                               return ippdrv;
+                       }
                }
+               mutex_unlock(&ippdrv->cmd_lock);
        }
 
        return ERR_PTR(-ENODEV);
@@ -325,6 +330,7 @@ int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data,
        if (!prop_list->ipp_id) {
                list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list)
                        count++;
+
                /*
                 * Supports ippdrv list count for user application.
                 * First step user application getting ippdrv count.
@@ -346,7 +352,7 @@ int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data,
                        return PTR_ERR(ippdrv);
                }
 
-               prop_list = ippdrv->prop_list;
+               *prop_list = ippdrv->prop_list;
        }
 
        return 0;
@@ -386,9 +392,11 @@ static int ipp_find_and_set_property(struct drm_exynos_ipp_property *property)
         * when we find this command no using prop_id.
         * return property information set in this command node.
         */
+       mutex_lock(&ippdrv->cmd_lock);
        list_for_each_entry(c_node, &ippdrv->cmd_list, list) {
                if ((c_node->property.prop_id == prop_id) &&
                    (c_node->state == IPP_STATE_STOP)) {
+                       mutex_unlock(&ippdrv->cmd_lock);
                        DRM_DEBUG_KMS("found cmd[%d]ippdrv[0x%x]\n",
                                property->cmd, (int)ippdrv);
 
@@ -396,6 +404,7 @@ static int ipp_find_and_set_property(struct drm_exynos_ipp_property *property)
                        return 0;
                }
        }
+       mutex_unlock(&ippdrv->cmd_lock);
 
        DRM_ERROR("failed to search property.\n");
 
@@ -499,7 +508,7 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
        c_node->start_work = ipp_create_cmd_work();
        if (IS_ERR(c_node->start_work)) {
                DRM_ERROR("failed to create start work.\n");
-               goto err_clear;
+               goto err_remove_id;
        }
 
        c_node->stop_work = ipp_create_cmd_work();
@@ -514,7 +523,7 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
                goto err_free_stop;
        }
 
-       mutex_init(&c_node->cmd_lock);
+       mutex_init(&c_node->lock);
        mutex_init(&c_node->mem_lock);
        mutex_init(&c_node->event_lock);
 
@@ -526,7 +535,9 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
 
        INIT_LIST_HEAD(&c_node->event_list);
        list_splice_init(&priv->event_list, &c_node->event_list);
+       mutex_lock(&ippdrv->cmd_lock);
        list_add_tail(&c_node->list, &ippdrv->cmd_list);
+       mutex_unlock(&ippdrv->cmd_lock);
 
        /* make dedicated state without m2m */
        if (!ipp_is_m2m_cmd(property->cmd))
@@ -538,18 +549,24 @@ err_free_stop:
        kfree(c_node->stop_work);
 err_free_start:
        kfree(c_node->start_work);
+err_remove_id:
+       ipp_remove_id(&ctx->prop_idr, &ctx->prop_lock, property->prop_id);
 err_clear:
        kfree(c_node);
        return ret;
 }
 
-static void ipp_clean_cmd_node(struct drm_exynos_ipp_cmd_node *c_node)
+static void ipp_clean_cmd_node(struct ipp_context *ctx,
+                               struct drm_exynos_ipp_cmd_node *c_node)
 {
        /* delete list */
        list_del(&c_node->list);
 
+       ipp_remove_id(&ctx->prop_idr, &ctx->prop_lock,
+                       c_node->property.prop_id);
+
        /* destroy mutex */
-       mutex_destroy(&c_node->cmd_lock);
+       mutex_destroy(&c_node->lock);
        mutex_destroy(&c_node->mem_lock);
        mutex_destroy(&c_node->event_lock);
 
@@ -567,17 +584,10 @@ 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, };
 
-       mutex_lock(&c_node->mem_lock);
-
        for_each_ipp_ops(i) {
                /* source/destination memory list */
                head = &c_node->mem_list[i];
 
-               if (list_empty(head)) {
-                       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,count[%d]m_node[0x%x]\n",
@@ -602,8 +612,6 @@ static int ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node)
                ret = max(count[EXYNOS_DRM_OPS_SRC],
                        count[EXYNOS_DRM_OPS_DST]);
 
-       mutex_unlock(&c_node->mem_lock);
-
        return ret;
 }
 
@@ -646,16 +654,13 @@ static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv,
                return -EFAULT;
        }
 
-       mutex_lock(&c_node->mem_lock);
-
        DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id);
 
        /* get operations callback */
        ops = ippdrv->ops[m_node->ops_id];
        if (!ops) {
                DRM_ERROR("not support ops.\n");
-               ret = -EFAULT;
-               goto err_unlock;
+               return -EFAULT;
        }
 
        /* set address and enable irq */
@@ -664,12 +669,10 @@ static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv,
                        m_node->buf_id, IPP_BUF_ENQUEUE);
                if (ret) {
                        DRM_ERROR("failed to set addr.\n");
-                       goto err_unlock;
+                       return ret;
                }
        }
 
-err_unlock:
-       mutex_unlock(&c_node->mem_lock);
        return ret;
 }
 
@@ -684,11 +687,9 @@ static struct drm_exynos_ipp_mem_node
        void *addr;
        int i;
 
-       mutex_lock(&c_node->mem_lock);
-
        m_node = kzalloc(sizeof(*m_node), GFP_KERNEL);
        if (!m_node)
-               goto err_unlock;
+               return ERR_PTR(-ENOMEM);
 
        /* clear base address for error handling */
        memset(&buf_info, 0x0, sizeof(buf_info));
@@ -722,15 +723,14 @@ static struct drm_exynos_ipp_mem_node
 
        m_node->filp = file;
        m_node->buf_info = buf_info;
+       mutex_lock(&c_node->mem_lock);
        list_add_tail(&m_node->list, &c_node->mem_list[qbuf->ops_id]);
-
        mutex_unlock(&c_node->mem_lock);
+
        return m_node;
 
 err_clear:
        kfree(m_node);
-err_unlock:
-       mutex_unlock(&c_node->mem_lock);
        return ERR_PTR(-EFAULT);
 }
 
@@ -747,13 +747,6 @@ static int ipp_put_mem_node(struct drm_device *drm_dev,
                return -EFAULT;
        }
 
-       if (list_empty(&m_node->list)) {
-               DRM_ERROR("empty memory node.\n");
-               return -ENOMEM;
-       }
-
-       mutex_lock(&c_node->mem_lock);
-
        DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id);
 
        /* put gem buffer */
@@ -768,8 +761,6 @@ static int ipp_put_mem_node(struct drm_device *drm_dev,
        list_del(&m_node->list);
        kfree(m_node);
 
-       mutex_unlock(&c_node->mem_lock);
-
        return 0;
 }
 
@@ -805,7 +796,9 @@ static int ipp_get_event(struct drm_device *drm_dev,
        e->base.event = &e->event.base;
        e->base.file_priv = file;
        e->base.destroy = ipp_free_event;
+       mutex_lock(&c_node->event_lock);
        list_add_tail(&e->base.link, &c_node->event_list);
+       mutex_unlock(&c_node->event_lock);
 
        return 0;
 }
@@ -816,11 +809,7 @@ static void ipp_put_event(struct drm_exynos_ipp_cmd_node *c_node,
        struct drm_exynos_ipp_send_event *e, *te;
        int count = 0;
 
-       if (list_empty(&c_node->event_list)) {
-               DRM_DEBUG_KMS("event_list is empty.\n");
-               return;
-       }
-
+       mutex_lock(&c_node->event_lock);
        list_for_each_entry_safe(e, te, &c_node->event_list, base.link) {
                DRM_DEBUG_KMS("count[%d]e[0x%x]\n", count++, (int)e);
 
@@ -841,9 +830,13 @@ static void ipp_put_event(struct drm_exynos_ipp_cmd_node *c_node,
                        /* delete list */
                        list_del(&e->base.link);
                        kfree(e);
-                       return;
+                       goto out_unlock;
                }
        }
+
+out_unlock:
+       mutex_unlock(&c_node->event_lock);
+       return;
 }
 
 static void ipp_handle_cmd_work(struct device *dev,
@@ -887,7 +880,9 @@ static int ipp_queue_buf_with_run(struct device *dev,
                return 0;
        }
 
+       mutex_lock(&c_node->mem_lock);
        if (!ipp_check_mem_list(c_node)) {
+               mutex_unlock(&c_node->mem_lock);
                DRM_DEBUG_KMS("empty memory.\n");
                return 0;
        }
@@ -904,10 +899,12 @@ static int ipp_queue_buf_with_run(struct device *dev,
        } else {
                ret = ipp_set_mem_node(ippdrv, c_node, m_node);
                if (ret) {
+                       mutex_unlock(&c_node->mem_lock);
                        DRM_ERROR("failed to set m node.\n");
                        return ret;
                }
        }
+       mutex_unlock(&c_node->mem_lock);
 
        return 0;
 }
@@ -918,15 +915,15 @@ static void ipp_clean_queue_buf(struct drm_device *drm_dev,
 {
        struct drm_exynos_ipp_mem_node *m_node, *tm_node;
 
-       if (!list_empty(&c_node->mem_list[qbuf->ops_id])) {
-               /* delete list */
-               list_for_each_entry_safe(m_node, tm_node,
-                       &c_node->mem_list[qbuf->ops_id], list) {
-                       if (m_node->buf_id == qbuf->buf_id &&
-                           m_node->ops_id == qbuf->ops_id)
-                               ipp_put_mem_node(drm_dev, c_node, m_node);
-               }
+       /* delete list */
+       mutex_lock(&c_node->mem_lock);
+       list_for_each_entry_safe(m_node, tm_node,
+               &c_node->mem_list[qbuf->ops_id], list) {
+               if (m_node->buf_id == qbuf->buf_id &&
+                   m_node->ops_id == qbuf->ops_id)
+                       ipp_put_mem_node(drm_dev, c_node, m_node);
        }
+       mutex_unlock(&c_node->mem_lock);
 }
 
 int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data,
@@ -998,7 +995,7 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data,
                }
                break;
        case IPP_BUF_DEQUEUE:
-               mutex_lock(&c_node->cmd_lock);
+               mutex_lock(&c_node->lock);
 
                /* put event for destination buffer */
                if (qbuf->ops_id == EXYNOS_DRM_OPS_DST)
@@ -1006,7 +1003,7 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data,
 
                ipp_clean_queue_buf(drm_dev, c_node, qbuf);
 
-               mutex_unlock(&c_node->cmd_lock);
+               mutex_unlock(&c_node->lock);
                break;
        default:
                DRM_ERROR("invalid buffer control.\n");
@@ -1109,12 +1106,12 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data,
        case IPP_CTRL_PLAY:
                if (pm_runtime_suspended(ippdrv->dev))
                        pm_runtime_get_sync(ippdrv->dev);
+
                c_node->state = IPP_STATE_START;
 
                cmd_work = c_node->start_work;
                cmd_work->ctrl = cmd_ctrl->ctrl;
                ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node);
-               c_node->state = IPP_STATE_START;
                break;
        case IPP_CTRL_STOP:
                cmd_work = c_node->stop_work;
@@ -1129,10 +1126,12 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data,
 
                c_node->state = IPP_STATE_STOP;
                ippdrv->dedicated = false;
-               ipp_clean_cmd_node(c_node);
+               mutex_lock(&ippdrv->cmd_lock);
+               ipp_clean_cmd_node(ctx, c_node);
 
                if (list_empty(&ippdrv->cmd_list))
                        pm_runtime_put_sync(ippdrv->dev);
+               mutex_unlock(&ippdrv->cmd_lock);
                break;
        case IPP_CTRL_PAUSE:
                cmd_work = c_node->stop_work;
@@ -1260,9 +1259,11 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
        /* store command info in ippdrv */
        ippdrv->c_node = c_node;
 
+       mutex_lock(&c_node->mem_lock);
        if (!ipp_check_mem_list(c_node)) {
                DRM_DEBUG_KMS("empty memory.\n");
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto err_unlock;
        }
 
        /* set current property in ippdrv */
@@ -1270,7 +1271,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
        if (ret) {
                DRM_ERROR("failed to set property.\n");
                ippdrv->c_node = NULL;
-               return ret;
+               goto err_unlock;
        }
 
        /* check command */
@@ -1285,7 +1286,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
                        if (!m_node) {
                                DRM_ERROR("failed to get node.\n");
                                ret = -EFAULT;
-                               return ret;
+                               goto err_unlock;
                        }
 
                        DRM_DEBUG_KMS("m_node[0x%x]\n", (int)m_node);
@@ -1293,7 +1294,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
                        ret = ipp_set_mem_node(ippdrv, c_node, m_node);
                        if (ret) {
                                DRM_ERROR("failed to set m node.\n");
-                               return ret;
+                               goto err_unlock;
                        }
                }
                break;
@@ -1305,7 +1306,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
                        ret = ipp_set_mem_node(ippdrv, c_node, m_node);
                        if (ret) {
                                DRM_ERROR("failed to set m node.\n");
-                               return ret;
+                               goto err_unlock;
                        }
                }
                break;
@@ -1317,14 +1318,16 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
                        ret = ipp_set_mem_node(ippdrv, c_node, m_node);
                        if (ret) {
                                DRM_ERROR("failed to set m node.\n");
-                               return ret;
+                               goto err_unlock;
                        }
                }
                break;
        default:
                DRM_ERROR("invalid operations.\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto err_unlock;
        }
+       mutex_unlock(&c_node->mem_lock);
 
        DRM_DEBUG_KMS("cmd[%d]\n", property->cmd);
 
@@ -1333,11 +1336,17 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
                ret = ippdrv->start(ippdrv->dev, property->cmd);
                if (ret) {
                        DRM_ERROR("failed to start ops.\n");
+                       ippdrv->c_node = NULL;
                        return ret;
                }
        }
 
        return 0;
+
+err_unlock:
+       mutex_unlock(&c_node->mem_lock);
+       ippdrv->c_node = NULL;
+       return ret;
 }
 
 static int ipp_stop_property(struct drm_device *drm_dev,
@@ -1354,6 +1363,8 @@ static int ipp_stop_property(struct drm_device *drm_dev,
        /* put event */
        ipp_put_event(c_node, NULL);
 
+       mutex_lock(&c_node->mem_lock);
+
        /* check command */
        switch (property->cmd) {
        case IPP_CMD_M2M:
@@ -1361,11 +1372,6 @@ static int ipp_stop_property(struct drm_device *drm_dev,
                        /* source/destination memory list */
                        head = &c_node->mem_list[i];
 
-                       if (list_empty(head)) {
-                               DRM_DEBUG_KMS("mem_list is empty.\n");
-                               break;
-                       }
-
                        list_for_each_entry_safe(m_node, tm_node,
                                head, list) {
                                ret = ipp_put_mem_node(drm_dev, c_node,
@@ -1381,11 +1387,6 @@ static int ipp_stop_property(struct drm_device *drm_dev,
                /* destination memory list */
                head = &c_node->mem_list[EXYNOS_DRM_OPS_DST];
 
-               if (list_empty(head)) {
-                       DRM_DEBUG_KMS("mem_list is empty.\n");
-                       break;
-               }
-
                list_for_each_entry_safe(m_node, tm_node, head, list) {
                        ret = ipp_put_mem_node(drm_dev, c_node, m_node);
                        if (ret) {
@@ -1398,11 +1399,6 @@ static int ipp_stop_property(struct drm_device *drm_dev,
                /* source memory list */
                head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC];
 
-               if (list_empty(head)) {
-                       DRM_DEBUG_KMS("mem_list is empty.\n");
-                       break;
-               }
-
                list_for_each_entry_safe(m_node, tm_node, head, list) {
                        ret = ipp_put_mem_node(drm_dev, c_node, m_node);
                        if (ret) {
@@ -1418,6 +1414,8 @@ static int ipp_stop_property(struct drm_device *drm_dev,
        }
 
 err_clear:
+       mutex_unlock(&c_node->mem_lock);
+
        /* stop operations */
        if (ippdrv->stop)
                ippdrv->stop(ippdrv->dev, property->cmd);
@@ -1446,7 +1444,7 @@ void ipp_sched_cmd(struct work_struct *work)
                return;
        }
 
-       mutex_lock(&c_node->cmd_lock);
+       mutex_lock(&c_node->lock);
 
        property = &c_node->property;
 
@@ -1494,7 +1492,7 @@ void ipp_sched_cmd(struct work_struct *work)
        DRM_DEBUG_KMS("ctrl[%d] done.\n", cmd_work->ctrl);
 
 err_unlock:
-       mutex_unlock(&c_node->cmd_lock);
+       mutex_unlock(&c_node->lock);
 }
 
 static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
@@ -1524,14 +1522,18 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
                return -EINVAL;
        }
 
+       mutex_lock(&c_node->event_lock);
        if (list_empty(&c_node->event_list)) {
                DRM_DEBUG_KMS("event list is empty.\n");
-               return 0;
+               ret = 0;
+               goto err_event_unlock;
        }
 
+       mutex_lock(&c_node->mem_lock);
        if (!ipp_check_mem_list(c_node)) {
                DRM_DEBUG_KMS("empty memory.\n");
-               return 0;
+               ret = 0;
+               goto err_mem_unlock;
        }
 
        /* check command */
@@ -1545,7 +1547,8 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
                                struct drm_exynos_ipp_mem_node, list);
                        if (!m_node) {
                                DRM_ERROR("empty memory node.\n");
-                               return -ENOMEM;
+                               ret = -ENOMEM;
+                               goto err_mem_unlock;
                        }
 
                        tbuf_id[i] = m_node->buf_id;
@@ -1567,7 +1570,8 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
                m_node = ipp_find_mem_node(c_node, &qbuf);
                if (!m_node) {
                        DRM_ERROR("empty memory node.\n");
-                       return -ENOMEM;
+                       ret = -ENOMEM;
+                       goto err_mem_unlock;
                }
 
                tbuf_id[EXYNOS_DRM_OPS_DST] = m_node->buf_id;
@@ -1584,7 +1588,8 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
                        struct drm_exynos_ipp_mem_node, list);
                if (!m_node) {
                        DRM_ERROR("empty memory node.\n");
-                       return -ENOMEM;
+                       ret = -ENOMEM;
+                       goto err_mem_unlock;
                }
 
                tbuf_id[EXYNOS_DRM_OPS_SRC] = m_node->buf_id;
@@ -1595,8 +1600,10 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
                break;
        default:
                DRM_ERROR("invalid operations.\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto err_mem_unlock;
        }
+       mutex_unlock(&c_node->mem_lock);
 
        if (tbuf_id[EXYNOS_DRM_OPS_DST] != buf_id[EXYNOS_DRM_OPS_DST])
                DRM_ERROR("failed to match buf_id[%d %d]prop_id[%d]\n",
@@ -1611,11 +1618,6 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
        e = list_first_entry(&c_node->event_list,
                struct drm_exynos_ipp_send_event, base.link);
 
-       if (!e) {
-               DRM_ERROR("empty event.\n");
-               return -EINVAL;
-       }
-
        do_gettimeofday(&now);
        DRM_DEBUG_KMS("tv_sec[%ld]tv_usec[%ld]\n", now.tv_sec, now.tv_usec);
        e->event.tv_sec = now.tv_sec;
@@ -1630,11 +1632,18 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
        list_move_tail(&e->base.link, &e->base.file_priv->event_list);
        wake_up_interruptible(&e->base.file_priv->event_wait);
        spin_unlock_irqrestore(&drm_dev->event_lock, flags);
+       mutex_unlock(&c_node->event_lock);
 
        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;
+
+err_mem_unlock:
+       mutex_unlock(&c_node->mem_lock);
+err_event_unlock:
+       mutex_unlock(&c_node->event_lock);
+       return ret;
 }
 
 void ipp_sched_event(struct work_struct *work)
@@ -1676,8 +1685,6 @@ void ipp_sched_event(struct work_struct *work)
                goto err_completion;
        }
 
-       mutex_lock(&c_node->event_lock);
-
        ret = ipp_send_event(ippdrv, c_node, event_work->buf_id);
        if (ret) {
                DRM_ERROR("failed to send event.\n");
@@ -1687,8 +1694,6 @@ void ipp_sched_event(struct work_struct *work)
 err_completion:
        if (ipp_is_m2m_cmd(c_node->property.cmd))
                complete(&c_node->start_complete);
-
-       mutex_unlock(&c_node->event_lock);
 }
 
 static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
@@ -1699,23 +1704,21 @@ static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
 
        /* get ipp driver entry */
        list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
+               u32 ipp_id;
+
                ippdrv->drm_dev = drm_dev;
 
                ret = ipp_create_id(&ctx->ipp_idr, &ctx->ipp_lock, ippdrv,
-                       &ippdrv->ipp_id);
-               if (ret) {
+                                   &ipp_id);
+               if (ret || ipp_id == 0) {
                        DRM_ERROR("failed to create id.\n");
-                       goto err_idr;
+                       goto err;
                }
 
                DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]ipp_id[%d]\n",
-                       count++, (int)ippdrv, ippdrv->ipp_id);
+                       count++, (int)ippdrv, ipp_id);
 
-               if (ippdrv->ipp_id == 0) {
-                       DRM_ERROR("failed to get ipp_id[%d]\n",
-                               ippdrv->ipp_id);
-                       goto err_idr;
-               }
+               ippdrv->prop_list.ipp_id = ipp_id;
 
                /* store parent device for node */
                ippdrv->parent_dev = dev;
@@ -1724,39 +1727,46 @@ static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
                ippdrv->event_workq = ctx->event_workq;
                ippdrv->sched_event = ipp_sched_event;
                INIT_LIST_HEAD(&ippdrv->cmd_list);
+               mutex_init(&ippdrv->cmd_lock);
 
                if (is_drm_iommu_supported(drm_dev)) {
                        ret = drm_iommu_attach_device(drm_dev, ippdrv->dev);
                        if (ret) {
                                DRM_ERROR("failed to activate iommu\n");
-                               goto err_iommu;
+                               goto err;
                        }
                }
        }
 
        return 0;
 
-err_iommu:
+err:
        /* get ipp driver entry */
-       list_for_each_entry_reverse(ippdrv, &exynos_drm_ippdrv_list, drv_list)
+       list_for_each_entry_continue_reverse(ippdrv, &exynos_drm_ippdrv_list,
+                                               drv_list) {
                if (is_drm_iommu_supported(drm_dev))
                        drm_iommu_detach_device(drm_dev, ippdrv->dev);
 
-err_idr:
-       idr_destroy(&ctx->ipp_idr);
-       idr_destroy(&ctx->prop_idr);
+               ipp_remove_id(&ctx->ipp_idr, &ctx->ipp_lock,
+                               ippdrv->prop_list.ipp_id);
+       }
+
        return ret;
 }
 
 static void ipp_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
 {
        struct exynos_drm_ippdrv *ippdrv;
+       struct ipp_context *ctx = get_ipp_context(dev);
 
        /* get ipp driver entry */
        list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
                if (is_drm_iommu_supported(drm_dev))
                        drm_iommu_detach_device(drm_dev, ippdrv->dev);
 
+               ipp_remove_id(&ctx->ipp_idr, &ctx->ipp_lock,
+                               ippdrv->prop_list.ipp_id);
+
                ippdrv->drm_dev = NULL;
                exynos_drm_ippdrv_unregister(ippdrv);
        }
@@ -1787,20 +1797,14 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev,
        struct drm_exynos_file_private *file_priv = file->driver_priv;
        struct exynos_drm_ipp_private *priv = file_priv->ipp_priv;
        struct exynos_drm_ippdrv *ippdrv = NULL;
+       struct ipp_context *ctx = get_ipp_context(dev);
        struct drm_exynos_ipp_cmd_node *c_node, *tc_node;
        int count = 0;
 
        DRM_DEBUG_KMS("for priv[0x%x]\n", (int)priv);
 
-       if (list_empty(&exynos_drm_ippdrv_list)) {
-               DRM_DEBUG_KMS("ippdrv_list is empty.\n");
-               goto err_clear;
-       }
-
        list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
-               if (list_empty(&ippdrv->cmd_list))
-                       continue;
-
+               mutex_lock(&ippdrv->cmd_lock);
                list_for_each_entry_safe(c_node, tc_node,
                        &ippdrv->cmd_list, list) {
                        DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]\n",
@@ -1820,14 +1824,14 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev,
                                }
 
                                ippdrv->dedicated = false;
-                               ipp_clean_cmd_node(c_node);
+                               ipp_clean_cmd_node(ctx, c_node);
                                if (list_empty(&ippdrv->cmd_list))
                                        pm_runtime_put_sync(ippdrv->dev);
                        }
                }
+               mutex_unlock(&ippdrv->cmd_lock);
        }
 
-err_clear:
        kfree(priv);
        return;
 }
index ab1634befc05fd11e7766f1b537ae0f039af91bc..7aaeaae757c2cc0bdfa3ee6cad874ecf21630ddd 100644 (file)
@@ -52,7 +52,7 @@ struct drm_exynos_ipp_cmd_work {
  * @list: list head to command queue information.
  * @event_list: list head of event.
  * @mem_list: list head to source,destination memory queue information.
- * @cmd_lock: lock for synchronization of access to ioctl.
+ * @lock: lock for synchronization of access to ioctl.
  * @mem_lock: lock for synchronization of access to memory nodes.
  * @event_lock: lock for synchronization of access to scheduled event.
  * @start_complete: completion of start of command.
@@ -68,7 +68,7 @@ struct drm_exynos_ipp_cmd_node {
        struct list_head        list;
        struct list_head        event_list;
        struct list_head        mem_list[EXYNOS_DRM_OPS_MAX];
-       struct mutex    cmd_lock;
+       struct mutex    lock;
        struct mutex    mem_lock;
        struct mutex    event_lock;
        struct completion       start_complete;
@@ -83,7 +83,7 @@ struct drm_exynos_ipp_cmd_node {
 /*
  * A structure of buffer information.
  *
- * @gem_objs: Y, Cb, Cr each gem object.
+ * @handles: Y, Cb, Cr each gem object handle.
  * @base: Y, Cb, Cr each planar address.
  */
 struct drm_exynos_ipp_buf_info {
@@ -142,12 +142,12 @@ struct exynos_drm_ipp_ops {
  * @parent_dev: parent device information.
  * @dev: platform device.
  * @drm_dev: drm device.
- * @ipp_id: id of ipp driver.
  * @dedicated: dedicated ipp device.
  * @ops: source, destination operations.
  * @event_workq: event work queue.
  * @c_node: current command information.
  * @cmd_list: list head for command information.
+ * @cmd_lock: lock for synchronization of access to cmd_list.
  * @prop_list: property informations of current ipp driver.
  * @check_property: check property about format, size, buffer.
  * @reset: reset ipp block.
@@ -160,13 +160,13 @@ struct exynos_drm_ippdrv {
        struct device   *parent_dev;
        struct device   *dev;
        struct drm_device       *drm_dev;
-       u32     ipp_id;
        bool    dedicated;
        struct exynos_drm_ipp_ops       *ops[EXYNOS_DRM_OPS_MAX];
        struct workqueue_struct *event_workq;
        struct drm_exynos_ipp_cmd_node *c_node;
        struct list_head        cmd_list;
-       struct drm_exynos_ipp_prop_list *prop_list;
+       struct mutex    cmd_lock;
+       struct drm_exynos_ipp_prop_list prop_list;
 
        int (*check_property)(struct device *dev,
                struct drm_exynos_ipp_property *property);
index 7b901688defa78f5e2d6ee1853e45f71f8e4bf2b..f01fbb6dc1f06249f7c3947f14a378a7a8651f95 100644 (file)
@@ -158,8 +158,9 @@ static irqreturn_t rotator_irq_handler(int irq, void *arg)
                        rot->cur_buf_id[EXYNOS_DRM_OPS_DST];
                queue_work(ippdrv->event_workq,
                        (struct work_struct *)event_work);
-       } else
+       } else {
                DRM_ERROR("the SFR is set illegally\n");
+       }
 
        return IRQ_HANDLED;
 }
@@ -469,11 +470,7 @@ static struct exynos_drm_ipp_ops rot_dst_ops = {
 
 static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
 {
-       struct drm_exynos_ipp_prop_list *prop_list;
-
-       prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL);
-       if (!prop_list)
-               return -ENOMEM;
+       struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list;
 
        prop_list->version = 1;
        prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) |
@@ -486,8 +483,6 @@ static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
        prop_list->crop = 0;
        prop_list->scale = 0;
 
-       ippdrv->prop_list = prop_list;
-
        return 0;
 }
 
index 852f2dadaebdbbe3a385b5fe28cd65a560f0c210..2fb8705d6461f68457f476cd7d231b0c776eabdc 100644 (file)
@@ -51,6 +51,7 @@ struct vidi_context {
        struct drm_crtc                 *crtc;
        struct drm_encoder              *encoder;
        struct drm_connector            connector;
+       struct exynos_drm_subdrv        subdrv;
        struct vidi_win_data            win_data[WINDOWS_NR];
        struct edid                     *raw_edid;
        unsigned int                    clkdiv;
@@ -294,14 +295,13 @@ static void vidi_dpms(struct exynos_drm_manager *mgr, int mode)
 }
 
 static int vidi_mgr_initialize(struct exynos_drm_manager *mgr,
-                       struct drm_device *drm_dev, int pipe)
+                       struct drm_device *drm_dev)
 {
        struct vidi_context *ctx = mgr->ctx;
+       struct exynos_drm_private *priv = drm_dev->dev_private;
 
-       DRM_ERROR("vidi initialize ct=%p dev=%p pipe=%d\n", ctx, drm_dev, pipe);
-
-       ctx->drm_dev = drm_dev;
-       ctx->pipe = pipe;
+       mgr->drm_dev = ctx->drm_dev = drm_dev;
+       mgr->pipe = ctx->pipe = priv->pipe++;
 
        /*
         * enable drm irq mode.
@@ -324,7 +324,6 @@ static int vidi_mgr_initialize(struct exynos_drm_manager *mgr,
 }
 
 static struct exynos_drm_manager_ops vidi_manager_ops = {
-       .initialize = vidi_mgr_initialize,
        .dpms = vidi_dpms,
        .commit = vidi_commit,
        .enable_vblank = vidi_enable_vblank,
@@ -533,12 +532,6 @@ static int vidi_get_modes(struct drm_connector *connector)
        return drm_add_edid_modes(connector, edid);
 }
 
-static int vidi_mode_valid(struct drm_connector *connector,
-                       struct drm_display_mode *mode)
-{
-       return MODE_OK;
-}
-
 static struct drm_encoder *vidi_best_encoder(struct drm_connector *connector)
 {
        struct vidi_context *ctx = ctx_from_connector(connector);
@@ -548,7 +541,6 @@ static struct drm_encoder *vidi_best_encoder(struct drm_connector *connector)
 
 static struct drm_connector_helper_funcs vidi_connector_helper_funcs = {
        .get_modes = vidi_get_modes,
-       .mode_valid = vidi_mode_valid,
        .best_encoder = vidi_best_encoder,
 };
 
@@ -586,13 +578,38 @@ static struct exynos_drm_display vidi_display = {
        .ops = &vidi_display_ops,
 };
 
+static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
+{
+       struct exynos_drm_manager *mgr = get_vidi_mgr(dev);
+       struct vidi_context *ctx = mgr->ctx;
+       struct drm_crtc *crtc = ctx->crtc;
+       int ret;
+
+       vidi_mgr_initialize(mgr, drm_dev);
+
+       ret = exynos_drm_crtc_create(&vidi_manager);
+       if (ret) {
+               DRM_ERROR("failed to create crtc.\n");
+               return ret;
+       }
+
+       ret = exynos_drm_create_enc_conn(drm_dev, &vidi_display);
+       if (ret) {
+               crtc->funcs->destroy(crtc);
+               DRM_ERROR("failed to create encoder and connector.\n");
+               return ret;
+       }
+
+       return 0;
+}
+
 static int vidi_probe(struct platform_device *pdev)
 {
-       struct device *dev = &pdev->dev;
+       struct exynos_drm_subdrv *subdrv;
        struct vidi_context *ctx;
        int ret;
 
-       ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+       ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
        if (!ctx)
                return -ENOMEM;
 
@@ -607,28 +624,43 @@ static int vidi_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, &vidi_manager);
 
-       ret = device_create_file(dev, &dev_attr_connection);
-       if (ret < 0)
-               DRM_INFO("failed to create connection sysfs.\n");
+       subdrv = &ctx->subdrv;
+       subdrv->dev = &pdev->dev;
+       subdrv->probe = vidi_subdrv_probe;
+
+       ret = exynos_drm_subdrv_register(subdrv);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to register drm vidi device\n");
+               return ret;
+       }
 
-       exynos_drm_manager_register(&vidi_manager);
-       exynos_drm_display_register(&vidi_display);
+       ret = device_create_file(&pdev->dev, &dev_attr_connection);
+       if (ret < 0) {
+               exynos_drm_subdrv_unregister(subdrv);
+               DRM_INFO("failed to create connection sysfs.\n");
+       }
 
        return 0;
 }
 
 static int vidi_remove(struct platform_device *pdev)
 {
-       struct vidi_context *ctx = platform_get_drvdata(pdev);
-
-       exynos_drm_display_unregister(&vidi_display);
-       exynos_drm_manager_unregister(&vidi_manager);
+       struct exynos_drm_manager *mgr = platform_get_drvdata(pdev);
+       struct vidi_context *ctx = mgr->ctx;
+       struct drm_encoder *encoder = ctx->encoder;
+       struct drm_crtc *crtc = mgr->crtc;
 
        if (ctx->raw_edid != (struct edid *)fake_edid_info) {
                kfree(ctx->raw_edid);
                ctx->raw_edid = NULL;
+
+               return -EINVAL;
        }
 
+       crtc->funcs->destroy(crtc);
+       encoder->funcs->destroy(encoder);
+       drm_connector_cleanup(&ctx->connector);
+
        return 0;
 }
 
@@ -640,3 +672,31 @@ struct platform_driver vidi_driver = {
                .owner  = THIS_MODULE,
        },
 };
+
+int exynos_drm_probe_vidi(void)
+{
+       struct platform_device *pdev;
+       int ret;
+
+       pdev = platform_device_register_simple("exynos-drm-vidi", -1, NULL, 0);
+       if (IS_ERR(pdev))
+               return PTR_ERR(pdev);
+
+       ret = platform_driver_register(&vidi_driver);
+       if (ret) {
+               platform_device_unregister(pdev);
+               return ret;
+       }
+
+       return ret;
+}
+
+void exynos_drm_remove_vidi(void)
+{
+       struct vidi_context *ctx = vidi_manager.ctx;
+       struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
+       struct platform_device *pdev = to_platform_device(subdrv->dev);
+
+       platform_driver_unregister(&vidi_driver);
+       platform_device_unregister(pdev);
+}
index 9a6d652a3ef248127723ad6b011ad021411690c9..c104d0c9b385a3860a4a21b8989bb8aede5abcbd 100644 (file)
 #include <linux/regulator/consumer.h>
 #include <linux/io.h>
 #include <linux/of.h>
-#include <linux/i2c.h>
+#include <linux/of_address.h>
 #include <linux/of_gpio.h>
 #include <linux/hdmi.h>
+#include <linux/component.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
 
 #include <drm/exynos_drm.h>
 
 #include "exynos_drm_drv.h"
+#include "exynos_drm_crtc.h"
 #include "exynos_mixer.h"
 
 #include <linux/gpio.h>
@@ -48,6 +52,8 @@
 #define get_hdmi_display(dev)  platform_get_drvdata(to_platform_device(dev))
 #define ctx_from_connector(c)  container_of(c, struct hdmi_context, connector)
 
+#define HOTPLUG_DEBOUNCE_MS            1100
+
 /* AVI header and aspect ratio */
 #define HDMI_AVI_VERSION               0x02
 #define HDMI_AVI_LENGTH                0x0D
@@ -66,6 +72,8 @@ enum hdmi_type {
 
 struct hdmi_driver_data {
        unsigned int type;
+       const struct hdmiphy_config *phy_confs;
+       unsigned int phy_conf_count;
        unsigned int is_apb_phy:1;
 };
 
@@ -74,7 +82,6 @@ struct hdmi_resources {
        struct clk                      *sclk_hdmi;
        struct clk                      *sclk_pixel;
        struct clk                      *sclk_hdmiphy;
-       struct clk                      *hdmiphy;
        struct clk                      *mout_hdmi;
        struct regulator_bulk_data      *regul_bulk;
        int                             regul_count;
@@ -185,17 +192,23 @@ struct hdmi_context {
 
        void __iomem                    *regs;
        int                             irq;
+       struct delayed_work             hotplug_work;
 
        struct i2c_adapter              *ddc_adpt;
        struct i2c_client               *hdmiphy_port;
 
        /* current hdmiphy conf regs */
+       struct drm_display_mode         current_mode;
        struct hdmi_conf_regs           mode_conf;
 
        struct hdmi_resources           res;
 
        int                             hpd_gpio;
+       void __iomem                    *regs_hdmiphy;
+       const struct hdmiphy_config             *phy_confs;
+       unsigned int                    phy_conf_count;
 
+       struct regmap                   *pmureg;
        enum hdmi_type                  type;
 };
 
@@ -204,14 +217,6 @@ struct hdmiphy_config {
        u8 conf[32];
 };
 
-struct hdmi_driver_data exynos4212_hdmi_driver_data = {
-       .type   = HDMI_TYPE14,
-};
-
-struct hdmi_driver_data exynos5_hdmi_driver_data = {
-       .type   = HDMI_TYPE14,
-};
-
 /* list of phy config settings */
 static const struct hdmiphy_config hdmiphy_v13_configs[] = {
        {
@@ -319,18 +324,18 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
        {
                .pixel_clock = 71000000,
                .conf = {
-                       0x01, 0x91, 0x1e, 0x15, 0x40, 0x3c, 0xce, 0x08,
-                       0x04, 0x20, 0xb2, 0xd8, 0x45, 0xa0, 0xac, 0x80,
-                       0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+                       0x01, 0xd1, 0x3b, 0x35, 0x40, 0x0c, 0x04, 0x08,
+                       0x85, 0xa0, 0x63, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
                        0x54, 0xad, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
                },
        },
        {
                .pixel_clock = 73250000,
                .conf = {
-                       0x01, 0xd1, 0x1f, 0x15, 0x40, 0x18, 0xe9, 0x08,
-                       0x02, 0xa0, 0xb7, 0xd8, 0x45, 0xa0, 0xac, 0x80,
-                       0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+                       0x01, 0xd1, 0x3d, 0x35, 0x40, 0x18, 0x02, 0x08,
+                       0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
                        0x54, 0xa8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
                },
        },
@@ -361,15 +366,6 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
                        0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
                },
        },
-       {
-               .pixel_clock = 88750000,
-               .conf = {
-                       0x01, 0x91, 0x25, 0x17, 0x40, 0x30, 0xfe, 0x08,
-                       0x06, 0x20, 0xde, 0xd8, 0x45, 0xa0, 0xac, 0x80,
-                       0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
-                       0x54, 0x8a, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
-               },
-       },
        {
                .pixel_clock = 106500000,
                .conf = {
@@ -391,18 +387,18 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
        {
                .pixel_clock = 115500000,
                .conf = {
-                       0x01, 0xd1, 0x30, 0x1a, 0x40, 0x40, 0x10, 0x04,
-                       0x04, 0xa0, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80,
-                       0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+                       0x01, 0xd1, 0x30, 0x12, 0x40, 0x40, 0x10, 0x08,
+                       0x80, 0x80, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
                        0x54, 0xaa, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
                },
        },
        {
                .pixel_clock = 119000000,
                .conf = {
-                       0x01, 0x91, 0x32, 0x14, 0x40, 0x60, 0xd8, 0x08,
-                       0x06, 0x20, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
-                       0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+                       0x01, 0xd1, 0x32, 0x1a, 0x40, 0x30, 0xd8, 0x08,
+                       0x04, 0xa0, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+                       0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
                        0x54, 0x9d, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
                },
        },
@@ -426,6 +422,183 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
        },
 };
 
+static const struct hdmiphy_config hdmiphy_5420_configs[] = {
+       {
+               .pixel_clock = 25200000,
+               .conf = {
+                       0x01, 0x52, 0x3F, 0x55, 0x40, 0x01, 0x00, 0xC8,
+                       0x82, 0xC8, 0xBD, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+                       0x06, 0x80, 0x01, 0x84, 0x05, 0x02, 0x24, 0x66,
+                       0x54, 0xF4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+               },
+       },
+       {
+               .pixel_clock = 27000000,
+               .conf = {
+                       0x01, 0xD1, 0x22, 0x51, 0x40, 0x08, 0xFC, 0xE0,
+                       0x98, 0xE8, 0xCB, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+                       0x06, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+                       0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+               },
+       },
+       {
+               .pixel_clock = 27027000,
+               .conf = {
+                       0x01, 0xD1, 0x2D, 0x72, 0x40, 0x64, 0x12, 0xC8,
+                       0x43, 0xE8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
+                       0x26, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+                       0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+               },
+       },
+       {
+               .pixel_clock = 36000000,
+               .conf = {
+                       0x01, 0x51, 0x2D, 0x55, 0x40, 0x40, 0x00, 0xC8,
+                       0x02, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
+                       0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+                       0x54, 0xAB, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+               },
+       },
+       {
+               .pixel_clock = 40000000,
+               .conf = {
+                       0x01, 0xD1, 0x21, 0x31, 0x40, 0x3C, 0x28, 0xC8,
+                       0x87, 0xE8, 0xC8, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+                       0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+                       0x54, 0x9A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+               },
+       },
+       {
+               .pixel_clock = 65000000,
+               .conf = {
+                       0x01, 0xD1, 0x36, 0x34, 0x40, 0x0C, 0x04, 0xC8,
+                       0x82, 0xE8, 0x45, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
+                       0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+                       0x54, 0xBD, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+               },
+       },
+       {
+               .pixel_clock = 71000000,
+               .conf = {
+                       0x01, 0xD1, 0x3B, 0x35, 0x40, 0x0C, 0x04, 0xC8,
+                       0x85, 0xE8, 0x63, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
+                       0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+                       0x54, 0x57, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+               },
+       },
+       {
+               .pixel_clock = 73250000,
+               .conf = {
+                       0x01, 0xD1, 0x1F, 0x10, 0x40, 0x78, 0x8D, 0xC8,
+                       0x81, 0xE8, 0xB7, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+                       0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+                       0x54, 0xA8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+               },
+       },
+       {
+               .pixel_clock = 74176000,
+               .conf = {
+                       0x01, 0xD1, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0xC8,
+                       0x81, 0xE8, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+                       0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+                       0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+               },
+       },
+       {
+               .pixel_clock = 74250000,
+               .conf = {
+                       0x01, 0xD1, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08,
+                       0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+                       0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
+                       0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+               },
+       },
+       {
+               .pixel_clock = 83500000,
+               .conf = {
+                       0x01, 0xD1, 0x23, 0x11, 0x40, 0x0C, 0xFB, 0xC8,
+                       0x85, 0xE8, 0xD1, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+                       0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+                       0x54, 0x4A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+               },
+       },
+       {
+               .pixel_clock = 88750000,
+               .conf = {
+                       0x01, 0xD1, 0x25, 0x11, 0x40, 0x18, 0xFF, 0xC8,
+                       0x83, 0xE8, 0xDE, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+                       0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+                       0x54, 0x45, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+               },
+       },
+       {
+               .pixel_clock = 106500000,
+               .conf = {
+                       0x01, 0xD1, 0x2C, 0x12, 0x40, 0x0C, 0x09, 0xC8,
+                       0x84, 0xE8, 0x0A, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
+                       0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+                       0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+               },
+       },
+       {
+               .pixel_clock = 108000000,
+               .conf = {
+                       0x01, 0x51, 0x2D, 0x15, 0x40, 0x01, 0x00, 0xC8,
+                       0x82, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
+                       0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+                       0x54, 0xC7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
+               },
+       },
+       {
+               .pixel_clock = 115500000,
+               .conf = {
+                       0x01, 0xD1, 0x30, 0x14, 0x40, 0x0C, 0x03, 0xC8,
+                       0x88, 0xE8, 0x21, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
+                       0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+                       0x54, 0x6A, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+               },
+       },
+       {
+               .pixel_clock = 146250000,
+               .conf = {
+                       0x01, 0xD1, 0x3D, 0x15, 0x40, 0x18, 0xFD, 0xC8,
+                       0x83, 0xE8, 0x6E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
+                       0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+                       0x54, 0x54, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+               },
+       },
+       {
+               .pixel_clock = 148500000,
+               .conf = {
+                       0x01, 0xD1, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08,
+                       0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+                       0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
+                       0x54, 0x4B, 0x25, 0x03, 0x00, 0x80, 0x01, 0x80,
+               },
+       },
+};
+
+static struct hdmi_driver_data exynos5420_hdmi_driver_data = {
+       .type           = HDMI_TYPE14,
+       .phy_confs      = hdmiphy_5420_configs,
+       .phy_conf_count = ARRAY_SIZE(hdmiphy_5420_configs),
+       .is_apb_phy     = 1,
+};
+
+static struct hdmi_driver_data exynos4212_hdmi_driver_data = {
+       .type           = HDMI_TYPE14,
+       .phy_confs      = hdmiphy_v14_configs,
+       .phy_conf_count = ARRAY_SIZE(hdmiphy_v14_configs),
+       .is_apb_phy     = 0,
+};
+
+static struct hdmi_driver_data exynos5_hdmi_driver_data = {
+       .type           = HDMI_TYPE14,
+       .phy_confs      = hdmiphy_v13_configs,
+       .phy_conf_count = ARRAY_SIZE(hdmiphy_v13_configs),
+       .is_apb_phy     = 0,
+};
+
 static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
 {
        return readl(hdata->regs + reg_id);
@@ -445,6 +618,48 @@ static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
        writel(value, hdata->regs + reg_id);
 }
 
+static int hdmiphy_reg_writeb(struct hdmi_context *hdata,
+                       u32 reg_offset, u8 value)
+{
+       if (hdata->hdmiphy_port) {
+               u8 buffer[2];
+               int ret;
+
+               buffer[0] = reg_offset;
+               buffer[1] = value;
+
+               ret = i2c_master_send(hdata->hdmiphy_port, buffer, 2);
+               if (ret == 2)
+                       return 0;
+               return ret;
+       } else {
+               writeb(value, hdata->regs_hdmiphy + (reg_offset<<2));
+               return 0;
+       }
+}
+
+static int hdmiphy_reg_write_buf(struct hdmi_context *hdata,
+                       u32 reg_offset, const u8 *buf, u32 len)
+{
+       if ((reg_offset + len) > 32)
+               return -EINVAL;
+
+       if (hdata->hdmiphy_port) {
+               int ret;
+
+               ret = i2c_master_send(hdata->hdmiphy_port, buf, len);
+               if (ret == len)
+                       return 0;
+               return ret;
+       } else {
+               int i;
+               for (i = 0; i < len; i++)
+                       writeb(buf[i], hdata->regs_hdmiphy +
+                               ((reg_offset + i)<<2));
+               return 0;
+       }
+}
+
 static void hdmi_v13_regs_dump(struct hdmi_context *hdata, char *prefix)
 {
 #define DUMPREG(reg_id) \
@@ -809,6 +1024,8 @@ static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
 {
        struct hdmi_context *hdata = ctx_from_connector(connector);
 
+       hdata->hpd = gpio_get_value(hdata->hpd_gpio);
+
        return hdata->hpd ? connector_status_connected :
                        connector_status_disconnected;
 }
@@ -848,20 +1065,10 @@ static int hdmi_get_modes(struct drm_connector *connector)
 
 static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
 {
-       const struct hdmiphy_config *confs;
-       int count, i;
-
-       if (hdata->type == HDMI_TYPE13) {
-               confs = hdmiphy_v13_configs;
-               count = ARRAY_SIZE(hdmiphy_v13_configs);
-       } else if (hdata->type == HDMI_TYPE14) {
-               confs = hdmiphy_v14_configs;
-               count = ARRAY_SIZE(hdmiphy_v14_configs);
-       } else
-               return -EINVAL;
+       int i;
 
-       for (i = 0; i < count; i++)
-               if (confs[i].pixel_clock == pixel_clock)
+       for (i = 0; i < hdata->phy_conf_count; i++)
+               if (hdata->phy_confs[i].pixel_clock == pixel_clock)
                        return i;
 
        DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
@@ -928,16 +1135,6 @@ static int hdmi_create_connector(struct exynos_drm_display *display,
        return 0;
 }
 
-static int hdmi_initialize(struct exynos_drm_display *display,
-                       struct drm_device *drm_dev)
-{
-       struct hdmi_context *hdata = display->ctx;
-
-       hdata->drm_dev = drm_dev;
-
-       return 0;
-}
-
 static void hdmi_mode_fixup(struct exynos_drm_display *display,
                                struct drm_connector *connector,
                                const struct drm_display_mode *mode,
@@ -1136,20 +1333,15 @@ static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff)
                        HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
 }
 
-static void hdmi_conf_reset(struct hdmi_context *hdata)
+static void hdmi_start(struct hdmi_context *hdata, bool start)
 {
-       u32 reg;
+       u32 val = start ? HDMI_TG_EN : 0;
 
-       if (hdata->type == HDMI_TYPE13)
-               reg = HDMI_V13_CORE_RSTOUT;
-       else
-               reg = HDMI_CORE_RSTOUT;
+       if (hdata->current_mode.flags & DRM_MODE_FLAG_INTERLACE)
+               val |= HDMI_FIELD_EN;
 
-       /* resetting HDMI core */
-       hdmi_reg_writemask(hdata, reg,  0, HDMI_CORE_SW_RSTOUT);
-       usleep_range(10000, 12000);
-       hdmi_reg_writemask(hdata, reg, ~0, HDMI_CORE_SW_RSTOUT);
-       usleep_range(10000, 12000);
+       hdmi_reg_writemask(hdata, HDMI_CON_0, val, HDMI_EN);
+       hdmi_reg_writemask(hdata, HDMI_TG_CMD, val, HDMI_TG_EN | HDMI_FIELD_EN);
 }
 
 static void hdmi_conf_init(struct hdmi_context *hdata)
@@ -1163,6 +1355,8 @@ static void hdmi_conf_init(struct hdmi_context *hdata)
        /* choose HDMI mode */
        hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
                HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
+       /* Apply Video preable and Guard band in HDMI mode only */
+       hdmi_reg_writeb(hdata, HDMI_CON_2, 0);
        /* disable bluescreen */
        hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
 
@@ -1286,12 +1480,7 @@ static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
        clk_prepare_enable(hdata->res.sclk_hdmi);
 
        /* enable HDMI and timing generator */
-       hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN);
-       if (core->int_pro_mode[0])
-               hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN |
-                               HDMI_FIELD_EN);
-       else
-               hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN);
+       hdmi_start(hdata, true);
 }
 
 static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
@@ -1453,12 +1642,7 @@ static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
        clk_prepare_enable(hdata->res.sclk_hdmi);
 
        /* enable HDMI and timing generator */
-       hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN);
-       if (core->int_pro_mode[0])
-               hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN |
-                               HDMI_FIELD_EN);
-       else
-               hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN);
+       hdmi_start(hdata, true);
 }
 
 static void hdmi_mode_apply(struct hdmi_context *hdata)
@@ -1499,32 +1683,51 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata)
 
 static void hdmiphy_poweron(struct hdmi_context *hdata)
 {
-       if (hdata->type == HDMI_TYPE14)
-               hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0,
-                       HDMI_PHY_POWER_OFF_EN);
+       if (hdata->type != HDMI_TYPE14)
+               return;
+
+       DRM_DEBUG_KMS("\n");
+
+       /* For PHY Mode Setting */
+       hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
+                               HDMI_PHY_ENABLE_MODE_SET);
+       /* Phy Power On */
+       hdmiphy_reg_writeb(hdata, HDMIPHY_POWER,
+                               HDMI_PHY_POWER_ON);
+       /* For PHY Mode Setting */
+       hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
+                               HDMI_PHY_DISABLE_MODE_SET);
+       /* PHY SW Reset */
+       hdmiphy_conf_reset(hdata);
 }
 
 static void hdmiphy_poweroff(struct hdmi_context *hdata)
 {
-       if (hdata->type == HDMI_TYPE14)
-               hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0,
-                       HDMI_PHY_POWER_OFF_EN);
+       if (hdata->type != HDMI_TYPE14)
+               return;
+
+       DRM_DEBUG_KMS("\n");
+
+       /* PHY SW Reset */
+       hdmiphy_conf_reset(hdata);
+       /* For PHY Mode Setting */
+       hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
+                               HDMI_PHY_ENABLE_MODE_SET);
+
+       /* PHY Power Off */
+       hdmiphy_reg_writeb(hdata, HDMIPHY_POWER,
+                               HDMI_PHY_POWER_OFF);
+
+       /* For PHY Mode Setting */
+       hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
+                               HDMI_PHY_DISABLE_MODE_SET);
 }
 
 static void hdmiphy_conf_apply(struct hdmi_context *hdata)
 {
-       const u8 *hdmiphy_data;
-       u8 buffer[32];
-       u8 operation[2];
-       u8 read_buffer[32] = {0, };
        int ret;
        int i;
 
-       if (!hdata->hdmiphy_port) {
-               DRM_ERROR("hdmiphy is not attached\n");
-               return;
-       }
-
        /* pixel clock */
        i = hdmi_find_phy_conf(hdata, hdata->mode_conf.pixel_clock);
        if (i < 0) {
@@ -1532,39 +1735,21 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata)
                return;
        }
 
-       if (hdata->type == HDMI_TYPE13)
-               hdmiphy_data = hdmiphy_v13_configs[i].conf;
-       else
-               hdmiphy_data = hdmiphy_v14_configs[i].conf;
-
-       memcpy(buffer, hdmiphy_data, 32);
-       ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32);
-       if (ret != 32) {
-               DRM_ERROR("failed to configure HDMIPHY via I2C\n");
+       ret = hdmiphy_reg_write_buf(hdata, 0, hdata->phy_confs[i].conf, 32);
+       if (ret) {
+               DRM_ERROR("failed to configure hdmiphy\n");
                return;
        }
 
        usleep_range(10000, 12000);
 
-       /* operation mode */
-       operation[0] = 0x1f;
-       operation[1] = 0x80;
-
-       ret = i2c_master_send(hdata->hdmiphy_port, operation, 2);
-       if (ret != 2) {
+       ret = hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
+                               HDMI_PHY_DISABLE_MODE_SET);
+       if (ret) {
                DRM_ERROR("failed to enable hdmiphy\n");
                return;
        }
 
-       ret = i2c_master_recv(hdata->hdmiphy_port, read_buffer, 32);
-       if (ret < 0) {
-               DRM_ERROR("failed to read hdmiphy config\n");
-               return;
-       }
-
-       for (i = 0; i < ret; i++)
-               DRM_DEBUG_KMS("hdmiphy[0x%02x] write[0x%02x] - "
-                       "recv [0x%02x]\n", i, buffer[i], read_buffer[i]);
 }
 
 static void hdmi_conf_apply(struct hdmi_context *hdata)
@@ -1573,7 +1758,7 @@ static void hdmi_conf_apply(struct hdmi_context *hdata)
        hdmiphy_conf_apply(hdata);
 
        mutex_lock(&hdata->hdmi_mutex);
-       hdmi_conf_reset(hdata);
+       hdmi_start(hdata, false);
        hdmi_conf_init(hdata);
        mutex_unlock(&hdata->hdmi_mutex);
 
@@ -1814,6 +1999,9 @@ static void hdmi_mode_set(struct exynos_drm_display *display,
                m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ?
                "INTERLACED" : "PROGERESSIVE");
 
+       /* preserve mode information for later use. */
+       drm_mode_copy(&hdata->current_mode, mode);
+
        if (hdata->type == HDMI_TYPE13)
                hdmi_v13_mode_set(hdata, mode);
        else
@@ -1854,7 +2042,10 @@ static void hdmi_poweron(struct exynos_drm_display *display)
        if (regulator_bulk_enable(res->regul_count, res->regul_bulk))
                DRM_DEBUG_KMS("failed to enable regulator bulk\n");
 
-       clk_prepare_enable(res->hdmiphy);
+       /* set pmu hdmiphy control bit to enable hdmiphy */
+       regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
+                       PMU_HDMI_PHY_ENABLE_BIT, 1);
+
        clk_prepare_enable(res->hdmi);
        clk_prepare_enable(res->sclk_hdmi);
 
@@ -1872,16 +2063,20 @@ static void hdmi_poweroff(struct exynos_drm_display *display)
                goto out;
        mutex_unlock(&hdata->hdmi_mutex);
 
-       /*
-        * The TV power domain needs any condition of hdmiphy to turn off and
-        * its reset state seems to meet the condition.
-        */
-       hdmiphy_conf_reset(hdata);
+       /* HDMI System Disable */
+       hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN);
+
        hdmiphy_poweroff(hdata);
 
+       cancel_delayed_work(&hdata->hotplug_work);
+
        clk_disable_unprepare(res->sclk_hdmi);
        clk_disable_unprepare(res->hdmi);
-       clk_disable_unprepare(res->hdmiphy);
+
+       /* reset pmu hdmiphy control bit to disable hdmiphy */
+       regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
+                       PMU_HDMI_PHY_ENABLE_BIT, 0);
+
        regulator_bulk_disable(res->regul_count, res->regul_bulk);
 
        pm_runtime_put_sync(hdata->dev);
@@ -1913,7 +2108,6 @@ static void hdmi_dpms(struct exynos_drm_display *display, int mode)
 }
 
 static struct exynos_drm_display_ops hdmi_display_ops = {
-       .initialize     = hdmi_initialize,
        .create_connector = hdmi_create_connector,
        .mode_fixup     = hdmi_mode_fixup,
        .mode_set       = hdmi_mode_set,
@@ -1926,9 +2120,11 @@ static struct exynos_drm_display hdmi_display = {
        .ops = &hdmi_display_ops,
 };
 
-static irqreturn_t hdmi_irq_thread(int irq, void *arg)
+static void hdmi_hotplug_work_func(struct work_struct *work)
 {
-       struct hdmi_context *hdata = arg;
+       struct hdmi_context *hdata;
+
+       hdata = container_of(work, struct hdmi_context, hotplug_work.work);
 
        mutex_lock(&hdata->hdmi_mutex);
        hdata->hpd = gpio_get_value(hdata->hpd_gpio);
@@ -1936,6 +2132,14 @@ static irqreturn_t hdmi_irq_thread(int irq, void *arg)
 
        if (hdata->drm_dev)
                drm_helper_hpd_irq_event(hdata->drm_dev);
+}
+
+static irqreturn_t hdmi_irq_thread(int irq, void *arg)
+{
+       struct hdmi_context *hdata = arg;
+
+       mod_delayed_work(system_wq, &hdata->hotplug_work,
+                       msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
 
        return IRQ_HANDLED;
 }
@@ -1954,37 +2158,35 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
 
        DRM_DEBUG_KMS("HDMI resource init\n");
 
-       memset(res, 0, sizeof(*res));
-
        /* get clocks, power */
        res->hdmi = devm_clk_get(dev, "hdmi");
        if (IS_ERR(res->hdmi)) {
                DRM_ERROR("failed to get clock 'hdmi'\n");
+               ret = PTR_ERR(res->hdmi);
                goto fail;
        }
        res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
        if (IS_ERR(res->sclk_hdmi)) {
                DRM_ERROR("failed to get clock 'sclk_hdmi'\n");
+               ret = PTR_ERR(res->sclk_hdmi);
                goto fail;
        }
        res->sclk_pixel = devm_clk_get(dev, "sclk_pixel");
        if (IS_ERR(res->sclk_pixel)) {
                DRM_ERROR("failed to get clock 'sclk_pixel'\n");
+               ret = PTR_ERR(res->sclk_pixel);
                goto fail;
        }
        res->sclk_hdmiphy = devm_clk_get(dev, "sclk_hdmiphy");
        if (IS_ERR(res->sclk_hdmiphy)) {
                DRM_ERROR("failed to get clock 'sclk_hdmiphy'\n");
-               goto fail;
-       }
-       res->hdmiphy = devm_clk_get(dev, "hdmiphy");
-       if (IS_ERR(res->hdmiphy)) {
-               DRM_ERROR("failed to get clock 'hdmiphy'\n");
+               ret = PTR_ERR(res->sclk_hdmiphy);
                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");
+               ret = PTR_ERR(res->mout_hdmi);
                goto fail;
        }
 
@@ -1992,8 +2194,10 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
 
        res->regul_bulk = devm_kzalloc(dev, ARRAY_SIZE(supply) *
                sizeof(res->regul_bulk[0]), GFP_KERNEL);
-       if (!res->regul_bulk)
+       if (!res->regul_bulk) {
+               ret = -ENOMEM;
                goto fail;
+       }
        for (i = 0; i < ARRAY_SIZE(supply); ++i) {
                res->regul_bulk[i].supply = supply[i];
                res->regul_bulk[i].consumer = NULL;
@@ -2001,14 +2205,14 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
        ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk);
        if (ret) {
                DRM_ERROR("failed to get regulators\n");
-               goto fail;
+               return ret;
        }
        res->regul_count = ARRAY_SIZE(supply);
 
-       return 0;
+       return ret;
 fail:
        DRM_ERROR("HDMI resource init - failed\n");
-       return -ENODEV;
+       return ret;
 }
 
 static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata
@@ -2042,43 +2246,106 @@ static struct of_device_id hdmi_match_types[] = {
        }, {
                .compatible = "samsung,exynos4212-hdmi",
                .data = &exynos4212_hdmi_driver_data,
+       }, {
+               .compatible = "samsung,exynos5420-hdmi",
+               .data = &exynos5420_hdmi_driver_data,
        }, {
                /* end node */
        }
 };
 
+static int hdmi_bind(struct device *dev, struct device *master, void *data)
+{
+       struct drm_device *drm_dev = data;
+       struct hdmi_context *hdata;
+
+       hdata = hdmi_display.ctx;
+       hdata->drm_dev = drm_dev;
+
+       return exynos_drm_create_enc_conn(drm_dev, &hdmi_display);
+}
+
+static void hdmi_unbind(struct device *dev, struct device *master, void *data)
+{
+       struct exynos_drm_display *display = get_hdmi_display(dev);
+       struct drm_encoder *encoder = display->encoder;
+       struct hdmi_context *hdata = display->ctx;
+
+       encoder->funcs->destroy(encoder);
+       drm_connector_cleanup(&hdata->connector);
+}
+
+static const struct component_ops hdmi_component_ops = {
+       .bind   = hdmi_bind,
+       .unbind = hdmi_unbind,
+};
+
+static struct device_node *hdmi_legacy_ddc_dt_binding(struct device *dev)
+{
+       const char *compatible_str = "samsung,exynos4210-hdmiddc";
+       struct device_node *np;
+
+       np = of_find_compatible_node(NULL, NULL, compatible_str);
+       if (np)
+               return of_get_next_parent(np);
+
+       return NULL;
+}
+
+static struct device_node *hdmi_legacy_phy_dt_binding(struct device *dev)
+{
+       const char *compatible_str = "samsung,exynos4212-hdmiphy";
+
+       return of_find_compatible_node(NULL, NULL, compatible_str);
+}
+
 static int hdmi_probe(struct platform_device *pdev)
 {
+       struct device_node *ddc_node, *phy_node;
+       struct s5p_hdmi_platform_data *pdata;
+       struct hdmi_driver_data *drv_data;
+       const struct of_device_id *match;
        struct device *dev = &pdev->dev;
        struct hdmi_context *hdata;
-       struct s5p_hdmi_platform_data *pdata;
        struct resource *res;
-       const struct of_device_id *match;
-       struct device_node *ddc_node, *phy_node;
-       struct hdmi_driver_data *drv_data;
        int ret;
 
-        if (!dev->of_node)
-               return -ENODEV;
+       ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
+                                       hdmi_display.type);
+       if (ret)
+               return ret;
+
+       if (!dev->of_node) {
+               ret = -ENODEV;
+               goto err_del_component;
+       }
 
        pdata = drm_hdmi_dt_parse_pdata(dev);
-       if (!pdata)
-               return -EINVAL;
+       if (!pdata) {
+               ret = -EINVAL;
+               goto err_del_component;
+       }
 
        hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
-       if (!hdata)
-               return -ENOMEM;
+       if (!hdata) {
+               ret = -ENOMEM;
+               goto err_del_component;
+       }
 
        mutex_init(&hdata->hdmi_mutex);
 
        platform_set_drvdata(pdev, &hdmi_display);
 
        match = of_match_node(hdmi_match_types, dev->of_node);
-       if (!match)
-               return -ENODEV;
+       if (!match) {
+               ret = -ENODEV;
+               goto err_del_component;
+       }
 
        drv_data = (struct hdmi_driver_data *)match->data;
        hdata->type = drv_data->type;
+       hdata->phy_confs = drv_data->phy_confs;
+       hdata->phy_conf_count = drv_data->phy_conf_count;
 
        hdata->hpd_gpio = pdata->hpd_gpio;
        hdata->dev = dev;
@@ -2086,35 +2353,44 @@ static int hdmi_probe(struct platform_device *pdev)
        ret = hdmi_resources_init(hdata);
        if (ret) {
                DRM_ERROR("hdmi_resources_init failed\n");
-               return -EINVAL;
+               return ret;
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        hdata->regs = devm_ioremap_resource(dev, res);
-       if (IS_ERR(hdata->regs))
-               return PTR_ERR(hdata->regs);
+       if (IS_ERR(hdata->regs)) {
+               ret = PTR_ERR(hdata->regs);
+               goto err_del_component;
+       }
 
        ret = devm_gpio_request(dev, hdata->hpd_gpio, "HPD");
        if (ret) {
                DRM_ERROR("failed to request HPD gpio\n");
-               return ret;
+               goto err_del_component;
        }
 
+       ddc_node = hdmi_legacy_ddc_dt_binding(dev);
+       if (ddc_node)
+               goto out_get_ddc_adpt;
+
        /* DDC i2c driver */
        ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
        if (!ddc_node) {
                DRM_ERROR("Failed to find ddc node in device tree\n");
-               return -ENODEV;
+               ret = -ENODEV;
+               goto err_del_component;
        }
+
+out_get_ddc_adpt:
        hdata->ddc_adpt = of_find_i2c_adapter_by_node(ddc_node);
        if (!hdata->ddc_adpt) {
                DRM_ERROR("Failed to get ddc i2c adapter by node\n");
-               return -ENODEV;
+               return -EPROBE_DEFER;
        }
 
-       /* Not support APB PHY yet. */
-       if (drv_data->is_apb_phy)
-               return -EPERM;
+       phy_node = hdmi_legacy_phy_dt_binding(dev);
+       if (phy_node)
+               goto out_get_phy_port;
 
        /* hdmiphy i2c driver */
        phy_node = of_parse_phandle(dev->of_node, "phy", 0);
@@ -2123,11 +2399,22 @@ static int hdmi_probe(struct platform_device *pdev)
                ret = -ENODEV;
                goto err_ddc;
        }
-       hdata->hdmiphy_port = of_find_i2c_device_by_node(phy_node);
-       if (!hdata->hdmiphy_port) {
-               DRM_ERROR("Failed to get hdmi phy i2c client from node\n");
-               ret = -ENODEV;
-               goto err_ddc;
+
+out_get_phy_port:
+       if (drv_data->is_apb_phy) {
+               hdata->regs_hdmiphy = of_iomap(phy_node, 0);
+               if (!hdata->regs_hdmiphy) {
+                       DRM_ERROR("failed to ioremap hdmi phy\n");
+                       ret = -ENOMEM;
+                       goto err_ddc;
+               }
+       } else {
+               hdata->hdmiphy_port = of_find_i2c_device_by_node(phy_node);
+               if (!hdata->hdmiphy_port) {
+                       DRM_ERROR("Failed to get hdmi phy i2c client\n");
+                       ret = -EPROBE_DEFER;
+                       goto err_ddc;
+               }
        }
 
        hdata->irq = gpio_to_irq(hdata->hpd_gpio);
@@ -2139,6 +2426,8 @@ static int hdmi_probe(struct platform_device *pdev)
 
        hdata->hpd = gpio_get_value(hdata->hpd_gpio);
 
+       INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func);
+
        ret = devm_request_threaded_irq(dev, hdata->irq, NULL,
                        hdmi_irq_thread, IRQF_TRIGGER_RISING |
                        IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
@@ -2148,30 +2437,51 @@ static int hdmi_probe(struct platform_device *pdev)
                goto err_hdmiphy;
        }
 
-       pm_runtime_enable(dev);
+       hdata->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
+                       "samsung,syscon-phandle");
+       if (IS_ERR(hdata->pmureg)) {
+               DRM_ERROR("syscon regmap lookup failed.\n");
+               ret = -EPROBE_DEFER;
+               goto err_hdmiphy;
+       }
 
+       pm_runtime_enable(dev);
        hdmi_display.ctx = hdata;
-       exynos_drm_display_register(&hdmi_display);
 
-       return 0;
+       ret = component_add(&pdev->dev, &hdmi_component_ops);
+       if (ret)
+               goto err_disable_pm_runtime;
+
+       return ret;
+
+err_disable_pm_runtime:
+       pm_runtime_disable(dev);
 
 err_hdmiphy:
-       put_device(&hdata->hdmiphy_port->dev);
+       if (hdata->hdmiphy_port)
+               put_device(&hdata->hdmiphy_port->dev);
 err_ddc:
        put_device(&hdata->ddc_adpt->dev);
+
+err_del_component:
+       exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
+
        return ret;
 }
 
 static int hdmi_remove(struct platform_device *pdev)
 {
-       struct device *dev = &pdev->dev;
-       struct exynos_drm_display *display = get_hdmi_display(dev);
-       struct hdmi_context *hdata = display->ctx;
+       struct hdmi_context *hdata = hdmi_display.ctx;
+
+       cancel_delayed_work_sync(&hdata->hotplug_work);
 
        put_device(&hdata->hdmiphy_port->dev);
        put_device(&hdata->ddc_adpt->dev);
+
        pm_runtime_disable(&pdev->dev);
+       component_del(&pdev->dev, &hdmi_component_ops);
 
+       exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
        return 0;
 }
 
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.h b/drivers/gpu/drm/exynos/exynos_hdmi.h
deleted file mode 100644 (file)
index 0ddf395..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * Authors:
- *     Inki Dae <inki.dae@samsung.com>
- *     Seung-Woo Kim <sw0312.kim@samsung.com>
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of  the GNU General  Public License as published by the
- * Free Software Foundation;  either version 2 of the  License, or (at your
- * option) any later version.
- */
-
-#ifndef _EXYNOS_HDMI_H_
-#define _EXYNOS_HDMI_H_
-
-void hdmi_attach_ddc_client(struct i2c_client *ddc);
-void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy);
-
-extern struct i2c_driver hdmiphy_driver;
-extern struct i2c_driver ddc_driver;
-
-#endif
diff --git a/drivers/gpu/drm/exynos/exynos_hdmiphy.c b/drivers/gpu/drm/exynos/exynos_hdmiphy.c
deleted file mode 100644 (file)
index 59abb14..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2011 Samsung Electronics Co.Ltd
- * Authors:
- *     Seung-Woo Kim <sw0312.kim@samsung.com>
- *     Inki Dae <inki.dae@samsung.com>
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of  the GNU General  Public License as published by the
- * Free Software Foundation;  either version 2 of the  License, or (at your
- * option) any later version.
- *
- */
-
-#include <drm/drmP.h>
-
-#include <linux/kernel.h>
-#include <linux/i2c.h>
-#include <linux/of.h>
-
-#include "exynos_drm_drv.h"
-#include "exynos_hdmi.h"
-
-
-static int hdmiphy_probe(struct i2c_client *client,
-       const struct i2c_device_id *id)
-{
-       hdmi_attach_hdmiphy_client(client);
-
-       dev_info(&client->adapter->dev, "attached s5p_hdmiphy "
-               "into i2c adapter successfully\n");
-
-       return 0;
-}
-
-static int hdmiphy_remove(struct i2c_client *client)
-{
-       dev_info(&client->adapter->dev, "detached s5p_hdmiphy "
-               "from i2c adapter successfully\n");
-
-       return 0;
-}
-
-static struct of_device_id hdmiphy_match_types[] = {
-       {
-               .compatible = "samsung,exynos5-hdmiphy",
-       }, {
-               .compatible = "samsung,exynos4210-hdmiphy",
-       }, {
-               .compatible = "samsung,exynos4212-hdmiphy",
-       }, {
-               /* end node */
-       }
-};
-
-struct i2c_driver hdmiphy_driver = {
-       .driver = {
-               .name   = "exynos-hdmiphy",
-               .owner  = THIS_MODULE,
-               .of_match_table = hdmiphy_match_types,
-       },
-       .probe          = hdmiphy_probe,
-       .remove         = hdmiphy_remove,
-       .command                = NULL,
-};
-EXPORT_SYMBOL(hdmiphy_driver);
index ce288818d2c0f727cd56e70fd1d367ad7e050f4c..4c5aed7e54c8ed909e1a608102b959831807a466 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/clk.h>
 #include <linux/regulator/consumer.h>
 #include <linux/of.h>
+#include <linux/component.h>
 
 #include <drm/exynos_drm.h>
 
@@ -830,13 +831,15 @@ static int vp_resources_init(struct mixer_context *mixer_ctx)
 }
 
 static int mixer_initialize(struct exynos_drm_manager *mgr,
-                       struct drm_device *drm_dev, int pipe)
+                       struct drm_device *drm_dev)
 {
        int ret;
        struct mixer_context *mixer_ctx = mgr->ctx;
+       struct exynos_drm_private *priv;
+       priv = drm_dev->dev_private;
 
-       mixer_ctx->drm_dev = drm_dev;
-       mixer_ctx->pipe = pipe;
+       mgr->drm_dev = mixer_ctx->drm_dev = drm_dev;
+       mgr->pipe = mixer_ctx->pipe = priv->pipe++;
 
        /* acquire resources: regs, irqs, clocks */
        ret = mixer_resources_init(mixer_ctx);
@@ -1142,8 +1145,6 @@ int mixer_check_mode(struct drm_display_mode *mode)
 }
 
 static struct exynos_drm_manager_ops mixer_manager_ops = {
-       .initialize             = mixer_initialize,
-       .remove                 = mixer_mgr_remove,
        .dpms                   = mixer_dpms,
        .enable_vblank          = mixer_enable_vblank,
        .disable_vblank         = mixer_disable_vblank,
@@ -1200,11 +1201,13 @@ static struct of_device_id mixer_match_types[] = {
        }
 };
 
-static int mixer_probe(struct platform_device *pdev)
+static int mixer_bind(struct device *dev, struct device *manager, void *data)
 {
-       struct device *dev = &pdev->dev;
+       struct platform_device *pdev = to_platform_device(dev);
+       struct drm_device *drm_dev = data;
        struct mixer_context *ctx;
        struct mixer_drv_data *drv;
+       int ret;
 
        dev_info(dev, "probe start\n");
 
@@ -1233,19 +1236,61 @@ static int mixer_probe(struct platform_device *pdev)
        atomic_set(&ctx->wait_vsync_event, 0);
 
        mixer_manager.ctx = ctx;
+       ret = mixer_initialize(&mixer_manager, drm_dev);
+       if (ret)
+               return ret;
+
        platform_set_drvdata(pdev, &mixer_manager);
-       exynos_drm_manager_register(&mixer_manager);
+       ret = exynos_drm_crtc_create(&mixer_manager);
+       if (ret) {
+               mixer_mgr_remove(&mixer_manager);
+               return ret;
+       }
 
        pm_runtime_enable(dev);
 
        return 0;
 }
 
-static int mixer_remove(struct platform_device *pdev)
+static void mixer_unbind(struct device *dev, struct device *master, void *data)
+{
+       struct exynos_drm_manager *mgr = dev_get_drvdata(dev);
+       struct drm_crtc *crtc = mgr->crtc;
+
+       dev_info(dev, "remove successful\n");
+
+       mixer_mgr_remove(mgr);
+
+       pm_runtime_disable(dev);
+
+       crtc->funcs->destroy(crtc);
+}
+
+static const struct component_ops mixer_component_ops = {
+       .bind   = mixer_bind,
+       .unbind = mixer_unbind,
+};
+
+static int mixer_probe(struct platform_device *pdev)
 {
-       dev_info(&pdev->dev, "remove successful\n");
+       int ret;
 
-       pm_runtime_disable(&pdev->dev);
+       ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
+                                       mixer_manager.type);
+       if (ret)
+               return ret;
+
+       ret = component_add(&pdev->dev, &mixer_component_ops);
+       if (ret)
+               exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
+
+       return ret;
+}
+
+static int mixer_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &mixer_component_ops);
+       exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
 
        return 0;
 }
index ef1b3eb3ba6e19f8417c88185342053e64c4508b..3f35ac6d8a470d96e230ccc98518a513e88e8a85 100644 (file)
 #define HDMI_TG_VACT_ST4_H             HDMI_TG_BASE(0x0074)
 #define HDMI_TG_3D                     HDMI_TG_BASE(0x00F0)
 
+/* HDMI PHY Registers Offsets*/
+#define HDMIPHY_POWER          (0x74 >> 2)
+#define HDMIPHY_MODE_SET_DONE          (0x7c >> 2)
+
+/* HDMI PHY Values */
+#define HDMI_PHY_POWER_ON              0x80
+#define HDMI_PHY_POWER_OFF             0xff
+
+/* HDMI PHY Values */
+#define HDMI_PHY_DISABLE_MODE_SET      0x80
+#define HDMI_PHY_ENABLE_MODE_SET       0x00
+
+/* PMU Registers for PHY */
+#define PMU_HDMI_PHY_CONTROL           0x700
+#define PMU_HDMI_PHY_ENABLE_BIT                BIT(0)
+
 #endif /* SAMSUNG_REGS_HDMI_H */
index 489ffd2c66e5dd300ec9bb44a154015c41596df6..87885d8c06e849290be8dfb543dc0caaa88cac4d 100644 (file)
@@ -148,7 +148,7 @@ static int handle_dsi_error(struct mdfld_dsi_pkg_sender *sender, u32 mask)
                break;
        case BIT(14):
                /*wait for all fifo empty*/
-               /*wait_for_all_fifos_empty(sender)*/;
+               /*wait_for_all_fifos_empty(sender)*/
                break;
        case BIT(15):
                dev_dbg(sender->dev->dev, "No Action required\n");
index b686e56646ebd4b8abe6ba53582dd4e595484f29..6e8fe9ec02b596595e975d9472095684d70e44dd 100644 (file)
@@ -112,11 +112,9 @@ static void psb_driver_lastclose(struct drm_device *dev)
        struct drm_psb_private *dev_priv = dev->dev_private;
        struct psb_fbdev *fbdev = dev_priv->fbdev;
 
-       drm_modeset_lock_all(dev);
-       ret = drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper);
+       ret = drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->psb_fb_helper);
        if (ret)
                DRM_DEBUG("failed to restore crtc mode\n");
-       drm_modeset_unlock_all(dev);
 
        return;
 }
@@ -354,7 +352,7 @@ static int psb_driver_load(struct drm_device *dev, unsigned long flags)
        PSB_WVDC32(0xFFFFFFFF, PSB_INT_MASK_R);
        spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
 
-       drm_irq_install(dev);
+       drm_irq_install(dev, dev->pdev->irq);
 
        dev->vblank_disable_allowed = true;
        dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
@@ -479,7 +477,7 @@ static struct drm_driver driver = {
        .lastclose = psb_driver_lastclose,
        .preclose = psb_driver_preclose,
 
-       .num_ioctls = DRM_ARRAY_SIZE(psb_ioctls),
+       .num_ioctls = ARRAY_SIZE(psb_ioctls),
        .device_is_agp = psb_driver_device_is_agp,
        .irq_preinstall = psb_irq_preinstall,
        .irq_postinstall = psb_irq_postinstall,
index 48af5cac1902bbfa1360ee0a76918701baf83739..240c331405b92fac28ff40f83aebebed042d2125 100644 (file)
@@ -568,11 +568,11 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data)
 
 static uint8_t tda998x_cksum(uint8_t *buf, size_t bytes)
 {
-       uint8_t sum = 0;
+       int sum = 0;
 
        while (bytes--)
-               sum += *buf++;
-       return (255 - sum) + 1;
+               sum -= *buf++;
+       return sum;
 }
 
 #define HB(x) (x)
index aeace37415aac8ae62ab2aa53a3b927ae648fb6c..e88bac1d781f7b5a34051d0fcb138f2efc11dc0d 100644 (file)
@@ -1251,7 +1251,7 @@ const struct drm_ioctl_desc i810_ioctls[] = {
        DRM_IOCTL_DEF_DRV(I810_FLIP, i810_flip_bufs, DRM_AUTH|DRM_UNLOCKED),
 };
 
-int i810_max_ioctl = DRM_ARRAY_SIZE(i810_ioctls);
+int i810_max_ioctl = ARRAY_SIZE(i810_ioctls);
 
 /**
  * Determine if the device really is AGP or not.
index bea2d67196fb47b95f87d03ed80cd6dd53f250d4..437e1824d0bf1762db64587833269a7ebeec78e6 100644 (file)
@@ -5,6 +5,7 @@ config DRM_I915
        depends on (AGP || AGP=n)
        select INTEL_GTT
        select AGP_INTEL if AGP
+       select INTERVAL_TREE
        # we need shmfs for the swappable backing store, and in particular
        # the shmem_readpage() which depends upon tmpfs
        select SHMEM
@@ -71,7 +72,7 @@ config DRM_I915_PRELIMINARY_HW_SUPPORT
 
 config DRM_I915_UMS
        bool "Enable userspace modesetting on Intel hardware (DEPRECATED)"
-       depends on DRM_I915
+       depends on DRM_I915 && BROKEN
        default n
        help
          Choose this option if you still need userspace modesetting.
index b1445b73465be0642aa0904df983183f0ae42135..cad1683d8bb527cc3c5e3fe9b163d581ff42ec53 100644 (file)
@@ -18,6 +18,7 @@ i915-$(CONFIG_DEBUG_FS) += i915_debugfs.o
 # GEM code
 i915-y += i915_cmd_parser.o \
          i915_gem_context.o \
+         i915_gem_render_state.o \
          i915_gem_debug.o \
          i915_gem_dmabuf.o \
          i915_gem_evict.o \
@@ -26,12 +27,18 @@ i915-y += i915_cmd_parser.o \
          i915_gem.o \
          i915_gem_stolen.o \
          i915_gem_tiling.o \
+         i915_gem_userptr.o \
          i915_gpu_error.o \
          i915_irq.o \
          i915_trace_points.o \
          intel_ringbuffer.o \
          intel_uncore.o
 
+# autogenerated null render state
+i915-y += intel_renderstate_gen6.o \
+         intel_renderstate_gen7.o \
+         intel_renderstate_gen8.o
+
 # modesetting core code
 i915-y += intel_bios.o \
          intel_display.o \
@@ -55,6 +62,7 @@ i915-y += dvo_ch7017.o \
          intel_dsi_cmd.o \
          intel_dsi.o \
          intel_dsi_pll.o \
+         intel_dsi_panel_vbt.o \
          intel_dvo.o \
          intel_hdmi.o \
          intel_i2c.o \
index a0f5bdd694919a86206ab210e5eb30827d297232..80449f47596085406cb525fab2288c4489c2811e 100644 (file)
@@ -160,7 +160,7 @@ static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
        if (i2c_transfer(adapter, msgs, 2) == 2) {
                *ch = in_buf[0];
                return true;
-       };
+       }
 
        if (!ch7xxx->quiet) {
                DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
index 0f1865d7d4d8412918e99cbf92841ba85d0ec5d8..0f2587ff347c9576581ab6d730099579196f8078 100644 (file)
@@ -195,7 +195,7 @@ static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data)
        if (i2c_transfer(adapter, msgs, 3) == 3) {
                *data = (in_buf[1] << 8) | in_buf[0];
                return true;
-       };
+       }
 
        if (!priv->quiet) {
                DRM_DEBUG_KMS("Unable to read register 0x%02x from "
index 8155ded79079dfdee1b513299a499efe054c20d4..74f2af7c2d3ed41688494a41b78e28390cf7c2b5 100644 (file)
@@ -121,7 +121,7 @@ static bool ns2501_readb(struct intel_dvo_device *dvo, int addr, uint8_t * ch)
        if (i2c_transfer(adapter, msgs, 2) == 2) {
                *ch = in_buf[0];
                return true;
-       };
+       }
 
        if (!ns->quiet) {
                DRM_DEBUG_KMS
@@ -233,9 +233,8 @@ static enum drm_mode_status ns2501_mode_valid(struct intel_dvo_device *dvo,
                                              struct drm_display_mode *mode)
 {
        DRM_DEBUG_KMS
-           ("%s: is mode valid (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d)\n",
-            __FUNCTION__, mode->hdisplay, mode->htotal, mode->vdisplay,
-            mode->vtotal);
+           ("is mode valid (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d)\n",
+            mode->hdisplay, mode->htotal, mode->vdisplay, mode->vtotal);
 
        /*
         * Currently, these are all the modes I have data from.
@@ -261,9 +260,8 @@ static void ns2501_mode_set(struct intel_dvo_device *dvo,
        struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
 
        DRM_DEBUG_KMS
-           ("%s: set mode (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d).\n",
-            __FUNCTION__, mode->hdisplay, mode->htotal, mode->vdisplay,
-            mode->vtotal);
+           ("set mode (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d).\n",
+            mode->hdisplay, mode->htotal, mode->vdisplay, mode->vtotal);
 
        /*
         * Where do I find the native resolution for which scaling is not required???
@@ -277,8 +275,7 @@ static void ns2501_mode_set(struct intel_dvo_device *dvo,
                if (mode->hdisplay == 800 && mode->vdisplay == 600) {
                        /* mode 277 */
                        ns->reg_8_shadow &= ~NS2501_8_BPAS;
-                       DRM_DEBUG_KMS("%s: switching to 800x600\n",
-                                     __FUNCTION__);
+                       DRM_DEBUG_KMS("switching to 800x600\n");
 
                        /*
                         * No, I do not know where this data comes from.
@@ -341,8 +338,7 @@ static void ns2501_mode_set(struct intel_dvo_device *dvo,
 
                } else if (mode->hdisplay == 640 && mode->vdisplay == 480) {
                        /* mode 274 */
-                       DRM_DEBUG_KMS("%s: switching to 640x480\n",
-                                     __FUNCTION__);
+                       DRM_DEBUG_KMS("switching to 640x480\n");
                        /*
                         * No, I do not know where this data comes from.
                         * It is just what the video bios left in the DVO, so
@@ -406,8 +402,7 @@ static void ns2501_mode_set(struct intel_dvo_device *dvo,
 
                } else if (mode->hdisplay == 1024 && mode->vdisplay == 768) {
                        /* mode 280 */
-                       DRM_DEBUG_KMS("%s: switching to 1024x768\n",
-                                     __FUNCTION__);
+                       DRM_DEBUG_KMS("switching to 1024x768\n");
                        /*
                         * This might or might not work, actually. I'm silently
                         * assuming here that the native panel resolution is
@@ -458,8 +453,7 @@ static void ns2501_dpms(struct intel_dvo_device *dvo, bool enable)
        struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
        unsigned char ch;
 
-       DRM_DEBUG_KMS("%s: Trying set the dpms of the DVO to %i\n",
-                     __FUNCTION__, enable);
+       DRM_DEBUG_KMS("Trying set the dpms of the DVO to %i\n", enable);
 
        ch = ns->reg_8_shadow;
 
index 7b3e9e9362003461951dfa737809f8ac916db47e..fa011496707659a1e1439dad0297d2f920c34039 100644 (file)
@@ -93,7 +93,7 @@ static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
        if (i2c_transfer(adapter, msgs, 2) == 2) {
                *ch = in_buf[0];
                return true;
-       };
+       }
 
        if (!sil->quiet) {
                DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
index 12ea4b16469277d971f78a5b194079d67dcb7781..7853719a0e8132dc5af19dcdee0089f236ff2b45 100644 (file)
@@ -118,7 +118,7 @@ static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
        if (i2c_transfer(adapter, msgs, 2) == 2) {
                *ch = in_buf[0];
                return true;
-       };
+       }
 
        if (!tfp->quiet) {
                DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
index 4cf6d020d5135e3da688e5c1f586c362189b5629..9d7954366bd28ea9300ddfe321219f35f8c9726e 100644 (file)
@@ -28,7 +28,7 @@
 #include "i915_drv.h"
 
 /**
- * DOC: i915 batch buffer command parser
+ * DOC: batch buffer command parser
  *
  * Motivation:
  * Certain OpenGL features (e.g. transform feedback, performance monitoring)
  * general bitmasking mechanism.
  */
 
+#define STD_MI_OPCODE_MASK  0xFF800000
+#define STD_3D_OPCODE_MASK  0xFFFF0000
+#define STD_2D_OPCODE_MASK  0xFFC00000
+#define STD_MFX_OPCODE_MASK 0xFFFF0000
+
+#define CMD(op, opm, f, lm, fl, ...)                           \
+       {                                                       \
+               .flags = (fl) | ((f) ? CMD_DESC_FIXED : 0),     \
+               .cmd = { (op), (opm) },                         \
+               .length = { (lm) },                             \
+               __VA_ARGS__                                     \
+       }
+
+/* Convenience macros to compress the tables */
+#define SMI STD_MI_OPCODE_MASK
+#define S3D STD_3D_OPCODE_MASK
+#define S2D STD_2D_OPCODE_MASK
+#define SMFX STD_MFX_OPCODE_MASK
+#define F true
+#define S CMD_DESC_SKIP
+#define R CMD_DESC_REJECT
+#define W CMD_DESC_REGISTER
+#define B CMD_DESC_BITMASK
+#define M CMD_DESC_MASTER
+
+/*            Command                          Mask   Fixed Len   Action
+             ---------------------------------------------------------- */
+static const struct drm_i915_cmd_descriptor common_cmds[] = {
+       CMD(  MI_NOOP,                          SMI,    F,  1,      S  ),
+       CMD(  MI_USER_INTERRUPT,                SMI,    F,  1,      R  ),
+       CMD(  MI_WAIT_FOR_EVENT,                SMI,    F,  1,      M  ),
+       CMD(  MI_ARB_CHECK,                     SMI,    F,  1,      S  ),
+       CMD(  MI_REPORT_HEAD,                   SMI,    F,  1,      S  ),
+       CMD(  MI_SUSPEND_FLUSH,                 SMI,    F,  1,      S  ),
+       CMD(  MI_SEMAPHORE_MBOX,                SMI,   !F,  0xFF,   R  ),
+       CMD(  MI_STORE_DWORD_INDEX,             SMI,   !F,  0xFF,   R  ),
+       CMD(  MI_LOAD_REGISTER_IMM(1),          SMI,   !F,  0xFF,   W,
+             .reg = { .offset = 1, .mask = 0x007FFFFC }               ),
+       CMD(  MI_STORE_REGISTER_MEM(1),         SMI,   !F,  0xFF,   W | B,
+             .reg = { .offset = 1, .mask = 0x007FFFFC },
+             .bits = {{
+                       .offset = 0,
+                       .mask = MI_GLOBAL_GTT,
+                       .expected = 0,
+             }},                                                      ),
+       CMD(  MI_LOAD_REGISTER_MEM,             SMI,   !F,  0xFF,   W | B,
+             .reg = { .offset = 1, .mask = 0x007FFFFC },
+             .bits = {{
+                       .offset = 0,
+                       .mask = MI_GLOBAL_GTT,
+                       .expected = 0,
+             }},                                                      ),
+       CMD(  MI_BATCH_BUFFER_START,            SMI,   !F,  0xFF,   S  ),
+};
+
+static const struct drm_i915_cmd_descriptor render_cmds[] = {
+       CMD(  MI_FLUSH,                         SMI,    F,  1,      S  ),
+       CMD(  MI_ARB_ON_OFF,                    SMI,    F,  1,      R  ),
+       CMD(  MI_PREDICATE,                     SMI,    F,  1,      S  ),
+       CMD(  MI_TOPOLOGY_FILTER,               SMI,    F,  1,      S  ),
+       CMD(  MI_DISPLAY_FLIP,                  SMI,   !F,  0xFF,   R  ),
+       CMD(  MI_SET_CONTEXT,                   SMI,   !F,  0xFF,   R  ),
+       CMD(  MI_URB_CLEAR,                     SMI,   !F,  0xFF,   S  ),
+       CMD(  MI_STORE_DWORD_IMM,               SMI,   !F,  0x3F,   B,
+             .bits = {{
+                       .offset = 0,
+                       .mask = MI_GLOBAL_GTT,
+                       .expected = 0,
+             }},                                                      ),
+       CMD(  MI_UPDATE_GTT,                    SMI,   !F,  0xFF,   R  ),
+       CMD(  MI_CLFLUSH,                       SMI,   !F,  0x3FF,  B,
+             .bits = {{
+                       .offset = 0,
+                       .mask = MI_GLOBAL_GTT,
+                       .expected = 0,
+             }},                                                      ),
+       CMD(  MI_REPORT_PERF_COUNT,             SMI,   !F,  0x3F,   B,
+             .bits = {{
+                       .offset = 1,
+                       .mask = MI_REPORT_PERF_COUNT_GGTT,
+                       .expected = 0,
+             }},                                                      ),
+       CMD(  MI_CONDITIONAL_BATCH_BUFFER_END,  SMI,   !F,  0xFF,   B,
+             .bits = {{
+                       .offset = 0,
+                       .mask = MI_GLOBAL_GTT,
+                       .expected = 0,
+             }},                                                      ),
+       CMD(  GFX_OP_3DSTATE_VF_STATISTICS,     S3D,    F,  1,      S  ),
+       CMD(  PIPELINE_SELECT,                  S3D,    F,  1,      S  ),
+       CMD(  MEDIA_VFE_STATE,                  S3D,   !F,  0xFFFF, B,
+             .bits = {{
+                       .offset = 2,
+                       .mask = MEDIA_VFE_STATE_MMIO_ACCESS_MASK,
+                       .expected = 0,
+             }},                                                      ),
+       CMD(  GPGPU_OBJECT,                     S3D,   !F,  0xFF,   S  ),
+       CMD(  GPGPU_WALKER,                     S3D,   !F,  0xFF,   S  ),
+       CMD(  GFX_OP_3DSTATE_SO_DECL_LIST,      S3D,   !F,  0x1FF,  S  ),
+       CMD(  GFX_OP_PIPE_CONTROL(5),           S3D,   !F,  0xFF,   B,
+             .bits = {{
+                       .offset = 1,
+                       .mask = (PIPE_CONTROL_MMIO_WRITE | PIPE_CONTROL_NOTIFY),
+                       .expected = 0,
+             },
+             {
+                       .offset = 1,
+                       .mask = (PIPE_CONTROL_GLOBAL_GTT_IVB |
+                                PIPE_CONTROL_STORE_DATA_INDEX),
+                       .expected = 0,
+                       .condition_offset = 1,
+                       .condition_mask = PIPE_CONTROL_POST_SYNC_OP_MASK,
+             }},                                                      ),
+};
+
+static const struct drm_i915_cmd_descriptor hsw_render_cmds[] = {
+       CMD(  MI_SET_PREDICATE,                 SMI,    F,  1,      S  ),
+       CMD(  MI_RS_CONTROL,                    SMI,    F,  1,      S  ),
+       CMD(  MI_URB_ATOMIC_ALLOC,              SMI,    F,  1,      S  ),
+       CMD(  MI_RS_CONTEXT,                    SMI,    F,  1,      S  ),
+       CMD(  MI_LOAD_SCAN_LINES_INCL,          SMI,   !F,  0x3F,   M  ),
+       CMD(  MI_LOAD_SCAN_LINES_EXCL,          SMI,   !F,  0x3F,   R  ),
+       CMD(  MI_LOAD_REGISTER_REG,             SMI,   !F,  0xFF,   R  ),
+       CMD(  MI_RS_STORE_DATA_IMM,             SMI,   !F,  0xFF,   S  ),
+       CMD(  MI_LOAD_URB_MEM,                  SMI,   !F,  0xFF,   S  ),
+       CMD(  MI_STORE_URB_MEM,                 SMI,   !F,  0xFF,   S  ),
+       CMD(  GFX_OP_3DSTATE_DX9_CONSTANTF_VS,  S3D,   !F,  0x7FF,  S  ),
+       CMD(  GFX_OP_3DSTATE_DX9_CONSTANTF_PS,  S3D,   !F,  0x7FF,  S  ),
+
+       CMD(  GFX_OP_3DSTATE_BINDING_TABLE_EDIT_VS,  S3D,   !F,  0x1FF,  S  ),
+       CMD(  GFX_OP_3DSTATE_BINDING_TABLE_EDIT_GS,  S3D,   !F,  0x1FF,  S  ),
+       CMD(  GFX_OP_3DSTATE_BINDING_TABLE_EDIT_HS,  S3D,   !F,  0x1FF,  S  ),
+       CMD(  GFX_OP_3DSTATE_BINDING_TABLE_EDIT_DS,  S3D,   !F,  0x1FF,  S  ),
+       CMD(  GFX_OP_3DSTATE_BINDING_TABLE_EDIT_PS,  S3D,   !F,  0x1FF,  S  ),
+};
+
+static const struct drm_i915_cmd_descriptor video_cmds[] = {
+       CMD(  MI_ARB_ON_OFF,                    SMI,    F,  1,      R  ),
+       CMD(  MI_STORE_DWORD_IMM,               SMI,   !F,  0xFF,   B,
+             .bits = {{
+                       .offset = 0,
+                       .mask = MI_GLOBAL_GTT,
+                       .expected = 0,
+             }},                                                      ),
+       CMD(  MI_UPDATE_GTT,                    SMI,   !F,  0x3F,   R  ),
+       CMD(  MI_FLUSH_DW,                      SMI,   !F,  0x3F,   B,
+             .bits = {{
+                       .offset = 0,
+                       .mask = MI_FLUSH_DW_NOTIFY,
+                       .expected = 0,
+             },
+             {
+                       .offset = 1,
+                       .mask = MI_FLUSH_DW_USE_GTT,
+                       .expected = 0,
+                       .condition_offset = 0,
+                       .condition_mask = MI_FLUSH_DW_OP_MASK,
+             },
+             {
+                       .offset = 0,
+                       .mask = MI_FLUSH_DW_STORE_INDEX,
+                       .expected = 0,
+                       .condition_offset = 0,
+                       .condition_mask = MI_FLUSH_DW_OP_MASK,
+             }},                                                      ),
+       CMD(  MI_CONDITIONAL_BATCH_BUFFER_END,  SMI,   !F,  0xFF,   B,
+             .bits = {{
+                       .offset = 0,
+                       .mask = MI_GLOBAL_GTT,
+                       .expected = 0,
+             }},                                                      ),
+       /*
+        * MFX_WAIT doesn't fit the way we handle length for most commands.
+        * It has a length field but it uses a non-standard length bias.
+        * It is always 1 dword though, so just treat it as fixed length.
+        */
+       CMD(  MFX_WAIT,                         SMFX,   F,  1,      S  ),
+};
+
+static const struct drm_i915_cmd_descriptor vecs_cmds[] = {
+       CMD(  MI_ARB_ON_OFF,                    SMI,    F,  1,      R  ),
+       CMD(  MI_STORE_DWORD_IMM,               SMI,   !F,  0xFF,   B,
+             .bits = {{
+                       .offset = 0,
+                       .mask = MI_GLOBAL_GTT,
+                       .expected = 0,
+             }},                                                      ),
+       CMD(  MI_UPDATE_GTT,                    SMI,   !F,  0x3F,   R  ),
+       CMD(  MI_FLUSH_DW,                      SMI,   !F,  0x3F,   B,
+             .bits = {{
+                       .offset = 0,
+                       .mask = MI_FLUSH_DW_NOTIFY,
+                       .expected = 0,
+             },
+             {
+                       .offset = 1,
+                       .mask = MI_FLUSH_DW_USE_GTT,
+                       .expected = 0,
+                       .condition_offset = 0,
+                       .condition_mask = MI_FLUSH_DW_OP_MASK,
+             },
+             {
+                       .offset = 0,
+                       .mask = MI_FLUSH_DW_STORE_INDEX,
+                       .expected = 0,
+                       .condition_offset = 0,
+                       .condition_mask = MI_FLUSH_DW_OP_MASK,
+             }},                                                      ),
+       CMD(  MI_CONDITIONAL_BATCH_BUFFER_END,  SMI,   !F,  0xFF,   B,
+             .bits = {{
+                       .offset = 0,
+                       .mask = MI_GLOBAL_GTT,
+                       .expected = 0,
+             }},                                                      ),
+};
+
+static const struct drm_i915_cmd_descriptor blt_cmds[] = {
+       CMD(  MI_DISPLAY_FLIP,                  SMI,   !F,  0xFF,   R  ),
+       CMD(  MI_STORE_DWORD_IMM,               SMI,   !F,  0x3FF,  B,
+             .bits = {{
+                       .offset = 0,
+                       .mask = MI_GLOBAL_GTT,
+                       .expected = 0,
+             }},                                                      ),
+       CMD(  MI_UPDATE_GTT,                    SMI,   !F,  0x3F,   R  ),
+       CMD(  MI_FLUSH_DW,                      SMI,   !F,  0x3F,   B,
+             .bits = {{
+                       .offset = 0,
+                       .mask = MI_FLUSH_DW_NOTIFY,
+                       .expected = 0,
+             },
+             {
+                       .offset = 1,
+                       .mask = MI_FLUSH_DW_USE_GTT,
+                       .expected = 0,
+                       .condition_offset = 0,
+                       .condition_mask = MI_FLUSH_DW_OP_MASK,
+             },
+             {
+                       .offset = 0,
+                       .mask = MI_FLUSH_DW_STORE_INDEX,
+                       .expected = 0,
+                       .condition_offset = 0,
+                       .condition_mask = MI_FLUSH_DW_OP_MASK,
+             }},                                                      ),
+       CMD(  COLOR_BLT,                        S2D,   !F,  0x3F,   S  ),
+       CMD(  SRC_COPY_BLT,                     S2D,   !F,  0x3F,   S  ),
+};
+
+static const struct drm_i915_cmd_descriptor hsw_blt_cmds[] = {
+       CMD(  MI_LOAD_SCAN_LINES_INCL,          SMI,   !F,  0x3F,   M  ),
+       CMD(  MI_LOAD_SCAN_LINES_EXCL,          SMI,   !F,  0x3F,   R  ),
+};
+
+#undef CMD
+#undef SMI
+#undef S3D
+#undef S2D
+#undef SMFX
+#undef F
+#undef S
+#undef R
+#undef W
+#undef B
+#undef M
+
+static const struct drm_i915_cmd_table gen7_render_cmds[] = {
+       { common_cmds, ARRAY_SIZE(common_cmds) },
+       { render_cmds, ARRAY_SIZE(render_cmds) },
+};
+
+static const struct drm_i915_cmd_table hsw_render_ring_cmds[] = {
+       { common_cmds, ARRAY_SIZE(common_cmds) },
+       { render_cmds, ARRAY_SIZE(render_cmds) },
+       { hsw_render_cmds, ARRAY_SIZE(hsw_render_cmds) },
+};
+
+static const struct drm_i915_cmd_table gen7_video_cmds[] = {
+       { common_cmds, ARRAY_SIZE(common_cmds) },
+       { video_cmds, ARRAY_SIZE(video_cmds) },
+};
+
+static const struct drm_i915_cmd_table hsw_vebox_cmds[] = {
+       { common_cmds, ARRAY_SIZE(common_cmds) },
+       { vecs_cmds, ARRAY_SIZE(vecs_cmds) },
+};
+
+static const struct drm_i915_cmd_table gen7_blt_cmds[] = {
+       { common_cmds, ARRAY_SIZE(common_cmds) },
+       { blt_cmds, ARRAY_SIZE(blt_cmds) },
+};
+
+static const struct drm_i915_cmd_table hsw_blt_ring_cmds[] = {
+       { common_cmds, ARRAY_SIZE(common_cmds) },
+       { blt_cmds, ARRAY_SIZE(blt_cmds) },
+       { hsw_blt_cmds, ARRAY_SIZE(hsw_blt_cmds) },
+};
+
+/*
+ * Register whitelists, sorted by increasing register offset.
+ *
+ * Some registers that userspace accesses are 64 bits. The register
+ * access commands only allow 32-bit accesses. Hence, we have to include
+ * entries for both halves of the 64-bit registers.
+ */
+
+/* Convenience macro for adding 64-bit registers */
+#define REG64(addr) (addr), (addr + sizeof(u32))
+
+static const u32 gen7_render_regs[] = {
+       REG64(HS_INVOCATION_COUNT),
+       REG64(DS_INVOCATION_COUNT),
+       REG64(IA_VERTICES_COUNT),
+       REG64(IA_PRIMITIVES_COUNT),
+       REG64(VS_INVOCATION_COUNT),
+       REG64(GS_INVOCATION_COUNT),
+       REG64(GS_PRIMITIVES_COUNT),
+       REG64(CL_INVOCATION_COUNT),
+       REG64(CL_PRIMITIVES_COUNT),
+       REG64(PS_INVOCATION_COUNT),
+       REG64(PS_DEPTH_COUNT),
+       OACONTROL, /* Only allowed for LRI and SRM. See below. */
+       GEN7_3DPRIM_END_OFFSET,
+       GEN7_3DPRIM_START_VERTEX,
+       GEN7_3DPRIM_VERTEX_COUNT,
+       GEN7_3DPRIM_INSTANCE_COUNT,
+       GEN7_3DPRIM_START_INSTANCE,
+       GEN7_3DPRIM_BASE_VERTEX,
+       REG64(GEN7_SO_NUM_PRIMS_WRITTEN(0)),
+       REG64(GEN7_SO_NUM_PRIMS_WRITTEN(1)),
+       REG64(GEN7_SO_NUM_PRIMS_WRITTEN(2)),
+       REG64(GEN7_SO_NUM_PRIMS_WRITTEN(3)),
+       REG64(GEN7_SO_PRIM_STORAGE_NEEDED(0)),
+       REG64(GEN7_SO_PRIM_STORAGE_NEEDED(1)),
+       REG64(GEN7_SO_PRIM_STORAGE_NEEDED(2)),
+       REG64(GEN7_SO_PRIM_STORAGE_NEEDED(3)),
+       GEN7_SO_WRITE_OFFSET(0),
+       GEN7_SO_WRITE_OFFSET(1),
+       GEN7_SO_WRITE_OFFSET(2),
+       GEN7_SO_WRITE_OFFSET(3),
+};
+
+static const u32 gen7_blt_regs[] = {
+       BCS_SWCTRL,
+};
+
+static const u32 ivb_master_regs[] = {
+       FORCEWAKE_MT,
+       DERRMR,
+       GEN7_PIPE_DE_LOAD_SL(PIPE_A),
+       GEN7_PIPE_DE_LOAD_SL(PIPE_B),
+       GEN7_PIPE_DE_LOAD_SL(PIPE_C),
+};
+
+static const u32 hsw_master_regs[] = {
+       FORCEWAKE_MT,
+       DERRMR,
+};
+
+#undef REG64
+
 static u32 gen7_render_get_cmd_length_mask(u32 cmd_header)
 {
        u32 client = (cmd_header & INSTR_CLIENT_MASK) >> INSTR_CLIENT_SHIFT;
@@ -137,15 +498,18 @@ static u32 gen7_blt_get_cmd_length_mask(u32 cmd_header)
        return 0;
 }
 
-static void validate_cmds_sorted(struct intel_ring_buffer *ring)
+static bool validate_cmds_sorted(struct intel_engine_cs *ring,
+                                const struct drm_i915_cmd_table *cmd_tables,
+                                int cmd_table_count)
 {
        int i;
+       bool ret = true;
 
-       if (!ring->cmd_tables || ring->cmd_table_count == 0)
-               return;
+       if (!cmd_tables || cmd_table_count == 0)
+               return true;
 
-       for (i = 0; i < ring->cmd_table_count; i++) {
-               const struct drm_i915_cmd_table *table = &ring->cmd_tables[i];
+       for (i = 0; i < cmd_table_count; i++) {
+               const struct drm_i915_cmd_table *table = &cmd_tables[i];
                u32 previous = 0;
                int j;
 
@@ -154,35 +518,107 @@ static void validate_cmds_sorted(struct intel_ring_buffer *ring)
                                &table->table[i];
                        u32 curr = desc->cmd.value & desc->cmd.mask;
 
-                       if (curr < previous)
+                       if (curr < previous) {
                                DRM_ERROR("CMD: table not sorted ring=%d table=%d entry=%d cmd=0x%08X prev=0x%08X\n",
                                          ring->id, i, j, curr, previous);
+                               ret = false;
+                       }
 
                        previous = curr;
                }
        }
+
+       return ret;
 }
 
-static void check_sorted(int ring_id, const u32 *reg_table, int reg_count)
+static bool check_sorted(int ring_id, const u32 *reg_table, int reg_count)
 {
        int i;
        u32 previous = 0;
+       bool ret = true;
 
        for (i = 0; i < reg_count; i++) {
                u32 curr = reg_table[i];
 
-               if (curr < previous)
+               if (curr < previous) {
                        DRM_ERROR("CMD: table not sorted ring=%d entry=%d reg=0x%08X prev=0x%08X\n",
                                  ring_id, i, curr, previous);
+                       ret = false;
+               }
 
                previous = curr;
        }
+
+       return ret;
+}
+
+static bool validate_regs_sorted(struct intel_engine_cs *ring)
+{
+       return check_sorted(ring->id, ring->reg_table, ring->reg_count) &&
+               check_sorted(ring->id, ring->master_reg_table,
+                            ring->master_reg_count);
+}
+
+struct cmd_node {
+       const struct drm_i915_cmd_descriptor *desc;
+       struct hlist_node node;
+};
+
+/*
+ * Different command ranges have different numbers of bits for the opcode. For
+ * example, MI commands use bits 31:23 while 3D commands use bits 31:16. The
+ * problem is that, for example, MI commands use bits 22:16 for other fields
+ * such as GGTT vs PPGTT bits. If we include those bits in the mask then when
+ * we mask a command from a batch it could hash to the wrong bucket due to
+ * non-opcode bits being set. But if we don't include those bits, some 3D
+ * commands may hash to the same bucket due to not including opcode bits that
+ * make the command unique. For now, we will risk hashing to the same bucket.
+ *
+ * If we attempt to generate a perfect hash, we should be able to look at bits
+ * 31:29 of a command from a batch buffer and use the full mask for that
+ * client. The existing INSTR_CLIENT_MASK/SHIFT defines can be used for this.
+ */
+#define CMD_HASH_MASK STD_MI_OPCODE_MASK
+
+static int init_hash_table(struct intel_engine_cs *ring,
+                          const struct drm_i915_cmd_table *cmd_tables,
+                          int cmd_table_count)
+{
+       int i, j;
+
+       hash_init(ring->cmd_hash);
+
+       for (i = 0; i < cmd_table_count; i++) {
+               const struct drm_i915_cmd_table *table = &cmd_tables[i];
+
+               for (j = 0; j < table->count; j++) {
+                       const struct drm_i915_cmd_descriptor *desc =
+                               &table->table[j];
+                       struct cmd_node *desc_node =
+                               kmalloc(sizeof(*desc_node), GFP_KERNEL);
+
+                       if (!desc_node)
+                               return -ENOMEM;
+
+                       desc_node->desc = desc;
+                       hash_add(ring->cmd_hash, &desc_node->node,
+                                desc->cmd.value & CMD_HASH_MASK);
+               }
+       }
+
+       return 0;
 }
 
-static void validate_regs_sorted(struct intel_ring_buffer *ring)
+static void fini_hash_table(struct intel_engine_cs *ring)
 {
-       check_sorted(ring->id, ring->reg_table, ring->reg_count);
-       check_sorted(ring->id, ring->master_reg_table, ring->master_reg_count);
+       struct hlist_node *tmp;
+       struct cmd_node *desc_node;
+       int i;
+
+       hash_for_each_safe(ring->cmd_hash, i, tmp, desc_node, node) {
+               hash_del(&desc_node->node);
+               kfree(desc_node);
+       }
 }
 
 /**
@@ -190,25 +626,74 @@ static void validate_regs_sorted(struct intel_ring_buffer *ring)
  * @ring: the ringbuffer to initialize
  *
  * Optionally initializes fields related to batch buffer command parsing in the
- * struct intel_ring_buffer based on whether the platform requires software
+ * struct intel_engine_cs based on whether the platform requires software
  * command parsing.
+ *
+ * Return: non-zero if initialization fails
  */
-void i915_cmd_parser_init_ring(struct intel_ring_buffer *ring)
+int i915_cmd_parser_init_ring(struct intel_engine_cs *ring)
 {
+       const struct drm_i915_cmd_table *cmd_tables;
+       int cmd_table_count;
+       int ret;
+
        if (!IS_GEN7(ring->dev))
-               return;
+               return 0;
 
        switch (ring->id) {
        case RCS:
+               if (IS_HASWELL(ring->dev)) {
+                       cmd_tables = hsw_render_ring_cmds;
+                       cmd_table_count =
+                               ARRAY_SIZE(hsw_render_ring_cmds);
+               } else {
+                       cmd_tables = gen7_render_cmds;
+                       cmd_table_count = ARRAY_SIZE(gen7_render_cmds);
+               }
+
+               ring->reg_table = gen7_render_regs;
+               ring->reg_count = ARRAY_SIZE(gen7_render_regs);
+
+               if (IS_HASWELL(ring->dev)) {
+                       ring->master_reg_table = hsw_master_regs;
+                       ring->master_reg_count = ARRAY_SIZE(hsw_master_regs);
+               } else {
+                       ring->master_reg_table = ivb_master_regs;
+                       ring->master_reg_count = ARRAY_SIZE(ivb_master_regs);
+               }
+
                ring->get_cmd_length_mask = gen7_render_get_cmd_length_mask;
                break;
        case VCS:
+               cmd_tables = gen7_video_cmds;
+               cmd_table_count = ARRAY_SIZE(gen7_video_cmds);
                ring->get_cmd_length_mask = gen7_bsd_get_cmd_length_mask;
                break;
        case BCS:
+               if (IS_HASWELL(ring->dev)) {
+                       cmd_tables = hsw_blt_ring_cmds;
+                       cmd_table_count = ARRAY_SIZE(hsw_blt_ring_cmds);
+               } else {
+                       cmd_tables = gen7_blt_cmds;
+                       cmd_table_count = ARRAY_SIZE(gen7_blt_cmds);
+               }
+
+               ring->reg_table = gen7_blt_regs;
+               ring->reg_count = ARRAY_SIZE(gen7_blt_regs);
+
+               if (IS_HASWELL(ring->dev)) {
+                       ring->master_reg_table = hsw_master_regs;
+                       ring->master_reg_count = ARRAY_SIZE(hsw_master_regs);
+               } else {
+                       ring->master_reg_table = ivb_master_regs;
+                       ring->master_reg_count = ARRAY_SIZE(ivb_master_regs);
+               }
+
                ring->get_cmd_length_mask = gen7_blt_get_cmd_length_mask;
                break;
        case VECS:
+               cmd_tables = hsw_vebox_cmds;
+               cmd_table_count = ARRAY_SIZE(hsw_vebox_cmds);
                /* VECS can use the same length_mask function as VCS */
                ring->get_cmd_length_mask = gen7_bsd_get_cmd_length_mask;
                break;
@@ -218,18 +703,45 @@ void i915_cmd_parser_init_ring(struct intel_ring_buffer *ring)
                BUG();
        }
 
-       validate_cmds_sorted(ring);
-       validate_regs_sorted(ring);
+       BUG_ON(!validate_cmds_sorted(ring, cmd_tables, cmd_table_count));
+       BUG_ON(!validate_regs_sorted(ring));
+
+       ret = init_hash_table(ring, cmd_tables, cmd_table_count);
+       if (ret) {
+               DRM_ERROR("CMD: cmd_parser_init failed!\n");
+               fini_hash_table(ring);
+               return ret;
+       }
+
+       ring->needs_cmd_parser = true;
+
+       return 0;
+}
+
+/**
+ * i915_cmd_parser_fini_ring() - clean up cmd parser related fields
+ * @ring: the ringbuffer to clean up
+ *
+ * Releases any resources related to command parsing that may have been
+ * initialized for the specified ring.
+ */
+void i915_cmd_parser_fini_ring(struct intel_engine_cs *ring)
+{
+       if (!ring->needs_cmd_parser)
+               return;
+
+       fini_hash_table(ring);
 }
 
 static const struct drm_i915_cmd_descriptor*
-find_cmd_in_table(const struct drm_i915_cmd_table *table,
+find_cmd_in_table(struct intel_engine_cs *ring,
                  u32 cmd_header)
 {
-       int i;
+       struct cmd_node *desc_node;
 
-       for (i = 0; i < table->count; i++) {
-               const struct drm_i915_cmd_descriptor *desc = &table->table[i];
+       hash_for_each_possible(ring->cmd_hash, desc_node, node,
+                              cmd_header & CMD_HASH_MASK) {
+               const struct drm_i915_cmd_descriptor *desc = desc_node->desc;
                u32 masked_cmd = desc->cmd.mask & cmd_header;
                u32 masked_value = desc->cmd.value & desc->cmd.mask;
 
@@ -249,20 +761,16 @@ find_cmd_in_table(const struct drm_i915_cmd_table *table,
  * ring's default length encoding and returns default_desc.
  */
 static const struct drm_i915_cmd_descriptor*
-find_cmd(struct intel_ring_buffer *ring,
+find_cmd(struct intel_engine_cs *ring,
         u32 cmd_header,
         struct drm_i915_cmd_descriptor *default_desc)
 {
+       const struct drm_i915_cmd_descriptor *desc;
        u32 mask;
-       int i;
 
-       for (i = 0; i < ring->cmd_table_count; i++) {
-               const struct drm_i915_cmd_descriptor *desc;
-
-               desc = find_cmd_in_table(&ring->cmd_tables[i], cmd_header);
-               if (desc)
-                       return desc;
-       }
+       desc = find_cmd_in_table(ring, cmd_header);
+       if (desc)
+               return desc;
 
        mask = ring->get_cmd_length_mask(cmd_header);
        if (!mask)
@@ -329,15 +837,112 @@ finish:
  *
  * Return: true if the ring requires software command parsing
  */
-bool i915_needs_cmd_parser(struct intel_ring_buffer *ring)
+bool i915_needs_cmd_parser(struct intel_engine_cs *ring)
 {
-       /* No command tables indicates a platform without parsing */
-       if (!ring->cmd_tables)
+       struct drm_i915_private *dev_priv = ring->dev->dev_private;
+
+       if (!ring->needs_cmd_parser)
+               return false;
+
+       /*
+        * XXX: VLV is Gen7 and therefore has cmd_tables, but has PPGTT
+        * disabled. That will cause all of the parser's PPGTT checks to
+        * fail. For now, disable parsing when PPGTT is off.
+        */
+       if (!dev_priv->mm.aliasing_ppgtt)
                return false;
 
        return (i915.enable_cmd_parser == 1);
 }
 
+static bool check_cmd(const struct intel_engine_cs *ring,
+                     const struct drm_i915_cmd_descriptor *desc,
+                     const u32 *cmd,
+                     const bool is_master,
+                     bool *oacontrol_set)
+{
+       if (desc->flags & CMD_DESC_REJECT) {
+               DRM_DEBUG_DRIVER("CMD: Rejected command: 0x%08X\n", *cmd);
+               return false;
+       }
+
+       if ((desc->flags & CMD_DESC_MASTER) && !is_master) {
+               DRM_DEBUG_DRIVER("CMD: Rejected master-only command: 0x%08X\n",
+                                *cmd);
+               return false;
+       }
+
+       if (desc->flags & CMD_DESC_REGISTER) {
+               u32 reg_addr = cmd[desc->reg.offset] & desc->reg.mask;
+
+               /*
+                * OACONTROL requires some special handling for writes. We
+                * want to make sure that any batch which enables OA also
+                * disables it before the end of the batch. The goal is to
+                * prevent one process from snooping on the perf data from
+                * another process. To do that, we need to check the value
+                * that will be written to the register. Hence, limit
+                * OACONTROL writes to only MI_LOAD_REGISTER_IMM commands.
+                */
+               if (reg_addr == OACONTROL) {
+                       if (desc->cmd.value == MI_LOAD_REGISTER_MEM)
+                               return false;
+
+                       if (desc->cmd.value == MI_LOAD_REGISTER_IMM(1))
+                               *oacontrol_set = (cmd[2] != 0);
+               }
+
+               if (!valid_reg(ring->reg_table,
+                              ring->reg_count, reg_addr)) {
+                       if (!is_master ||
+                           !valid_reg(ring->master_reg_table,
+                                      ring->master_reg_count,
+                                      reg_addr)) {
+                               DRM_DEBUG_DRIVER("CMD: Rejected register 0x%08X in command: 0x%08X (ring=%d)\n",
+                                                reg_addr,
+                                                *cmd,
+                                                ring->id);
+                               return false;
+                       }
+               }
+       }
+
+       if (desc->flags & CMD_DESC_BITMASK) {
+               int i;
+
+               for (i = 0; i < MAX_CMD_DESC_BITMASKS; i++) {
+                       u32 dword;
+
+                       if (desc->bits[i].mask == 0)
+                               break;
+
+                       if (desc->bits[i].condition_mask != 0) {
+                               u32 offset =
+                                       desc->bits[i].condition_offset;
+                               u32 condition = cmd[offset] &
+                                       desc->bits[i].condition_mask;
+
+                               if (condition == 0)
+                                       continue;
+                       }
+
+                       dword = cmd[desc->bits[i].offset] &
+                               desc->bits[i].mask;
+
+                       if (dword != desc->bits[i].expected) {
+                               DRM_DEBUG_DRIVER("CMD: Rejected command 0x%08X for bitmask 0x%08X (exp=0x%08X act=0x%08X) (ring=%d)\n",
+                                                *cmd,
+                                                desc->bits[i].mask,
+                                                desc->bits[i].expected,
+                                                dword, ring->id);
+                               return false;
+                       }
+               }
+       }
+
+       return true;
+}
+
 #define LENGTH_BIAS 2
 
 /**
@@ -352,7 +957,7 @@ bool i915_needs_cmd_parser(struct intel_ring_buffer *ring)
  *
  * Return: non-zero if the parser finds violations or otherwise fails
  */
-int i915_parse_cmds(struct intel_ring_buffer *ring,
+int i915_parse_cmds(struct intel_engine_cs *ring,
                    struct drm_i915_gem_object *batch_obj,
                    u32 batch_start_offset,
                    bool is_master)
@@ -361,6 +966,7 @@ int i915_parse_cmds(struct intel_ring_buffer *ring,
        u32 *cmd, *batch_base, *batch_end;
        struct drm_i915_cmd_descriptor default_desc = { 0 };
        int needs_clflush = 0;
+       bool oacontrol_set = false; /* OACONTROL tracking. See check_cmd() */
 
        ret = i915_gem_obj_prepare_shmem_read(batch_obj, &needs_clflush);
        if (ret) {
@@ -402,76 +1008,27 @@ int i915_parse_cmds(struct intel_ring_buffer *ring,
                        length = ((*cmd & desc->length.mask) + LENGTH_BIAS);
 
                if ((batch_end - cmd) < length) {
-                       DRM_DEBUG_DRIVER("CMD: Command length exceeds batch length: 0x%08X length=%d batchlen=%td\n",
+                       DRM_DEBUG_DRIVER("CMD: Command length exceeds batch length: 0x%08X length=%u batchlen=%td\n",
                                         *cmd,
                                         length,
-                                        (unsigned long)(batch_end - cmd));
+                                        batch_end - cmd);
                        ret = -EINVAL;
                        break;
                }
 
-               if (desc->flags & CMD_DESC_REJECT) {
-                       DRM_DEBUG_DRIVER("CMD: Rejected command: 0x%08X\n", *cmd);
+               if (!check_cmd(ring, desc, cmd, is_master, &oacontrol_set)) {
                        ret = -EINVAL;
                        break;
                }
 
-               if ((desc->flags & CMD_DESC_MASTER) && !is_master) {
-                       DRM_DEBUG_DRIVER("CMD: Rejected master-only command: 0x%08X\n",
-                                        *cmd);
-                       ret = -EINVAL;
-                       break;
-               }
-
-               if (desc->flags & CMD_DESC_REGISTER) {
-                       u32 reg_addr = cmd[desc->reg.offset] & desc->reg.mask;
-
-                       if (!valid_reg(ring->reg_table,
-                                      ring->reg_count, reg_addr)) {
-                               if (!is_master ||
-                                   !valid_reg(ring->master_reg_table,
-                                              ring->master_reg_count,
-                                              reg_addr)) {
-                                       DRM_DEBUG_DRIVER("CMD: Rejected register 0x%08X in command: 0x%08X (ring=%d)\n",
-                                                        reg_addr,
-                                                        *cmd,
-                                                        ring->id);
-                                       ret = -EINVAL;
-                                       break;
-                               }
-                       }
-               }
-
-               if (desc->flags & CMD_DESC_BITMASK) {
-                       int i;
-
-                       for (i = 0; i < MAX_CMD_DESC_BITMASKS; i++) {
-                               u32 dword;
-
-                               if (desc->bits[i].mask == 0)
-                                       break;
-
-                               dword = cmd[desc->bits[i].offset] &
-                                       desc->bits[i].mask;
-
-                               if (dword != desc->bits[i].expected) {
-                                       DRM_DEBUG_DRIVER("CMD: Rejected command 0x%08X for bitmask 0x%08X (exp=0x%08X act=0x%08X) (ring=%d)\n",
-                                                        *cmd,
-                                                        desc->bits[i].mask,
-                                                        desc->bits[i].expected,
-                                                        dword, ring->id);
-                                       ret = -EINVAL;
-                                       break;
-                               }
-                       }
-
-                       if (ret)
-                               break;
-               }
-
                cmd += length;
        }
 
+       if (oacontrol_set) {
+               DRM_DEBUG_DRIVER("CMD: batch set OACONTROL but did not clear it\n");
+               ret = -EINVAL;
+       }
+
        if (cmd >= batch_end) {
                DRM_DEBUG_DRIVER("CMD: Got to the end of the buffer w/o a BBE cmd!\n");
                ret = -EINVAL;
@@ -483,3 +1040,22 @@ int i915_parse_cmds(struct intel_ring_buffer *ring,
 
        return ret;
 }
+
+/**
+ * i915_cmd_parser_get_version() - get the cmd parser version number
+ *
+ * The cmd parser maintains a simple increasing integer version number suitable
+ * for passing to userspace clients to determine what operations are permitted.
+ *
+ * Return: the current version number of the cmd parser
+ */
+int i915_cmd_parser_get_version(void)
+{
+       /*
+        * Command parser version history
+        *
+        * 1. Initial version. Checks batches and reports violations, but leaves
+        *    hardware parsing enabled (so does not allow new use cases).
+        */
+       return 1;
+}
index 195fe5bc0aacf56306ae01e87ed67660c0328646..601caa88c0928cb818c6af27cbb9f25c0a4bebf2 100644 (file)
@@ -79,7 +79,7 @@ drm_add_fake_info_node(struct drm_minor *minor,
 
 static int i915_capabilities(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        const struct intel_device_info *info = INTEL_INFO(dev);
 
@@ -172,7 +172,7 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
                seq_printf(m, " (%s)", obj->ring->name);
 }
 
-static void describe_ctx(struct seq_file *m, struct i915_hw_context *ctx)
+static void describe_ctx(struct seq_file *m, struct intel_context *ctx)
 {
        seq_putc(m, ctx->is_initialized ? 'I' : 'i');
        seq_putc(m, ctx->remap_slice ? 'R' : 'r');
@@ -181,7 +181,7 @@ static void describe_ctx(struct seq_file *m, struct i915_hw_context *ctx)
 
 static int i915_gem_object_list_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        uintptr_t list = (uintptr_t) node->info_ent->data;
        struct list_head *head;
        struct drm_device *dev = node->minor->dev;
@@ -239,7 +239,7 @@ static int obj_rank_by_stolen(void *priv,
 
 static int i915_gem_stolen_list_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_gem_object *obj;
@@ -371,7 +371,7 @@ static int per_file_stats(int id, void *ptr, void *data)
 
 static int i915_gem_object_info(struct seq_file *m, void* data)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 count, mappable_count, purgeable_count;
@@ -474,7 +474,7 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
 
 static int i915_gem_gtt_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        uintptr_t list = (uintptr_t) node->info_ent->data;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -509,12 +509,12 @@ static int i915_gem_gtt_info(struct seq_file *m, void *data)
 
 static int i915_gem_pageflip_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        unsigned long flags;
        struct intel_crtc *crtc;
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+       for_each_intel_crtc(dev, crtc) {
                const char pipe = pipe_name(crtc->pipe);
                const char plane = plane_name(crtc->plane);
                struct intel_unpin_work *work;
@@ -559,10 +559,10 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data)
 
 static int i915_gem_request_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        struct drm_i915_gem_request *gem_request;
        int ret, count, i;
 
@@ -594,7 +594,7 @@ static int i915_gem_request_info(struct seq_file *m, void *data)
 }
 
 static void i915_ring_seqno_info(struct seq_file *m,
-                                struct intel_ring_buffer *ring)
+                                struct intel_engine_cs *ring)
 {
        if (ring->get_seqno) {
                seq_printf(m, "Current sequence (%s): %u\n",
@@ -604,10 +604,10 @@ static void i915_ring_seqno_info(struct seq_file *m,
 
 static int i915_gem_seqno_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        int ret, i;
 
        ret = mutex_lock_interruptible(&dev->struct_mutex);
@@ -627,10 +627,10 @@ static int i915_gem_seqno_info(struct seq_file *m, void *data)
 
 static int i915_interrupt_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        int ret, i, pipe;
 
        ret = mutex_lock_interruptible(&dev->struct_mutex);
@@ -638,7 +638,47 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
                return ret;
        intel_runtime_pm_get(dev_priv);
 
-       if (INTEL_INFO(dev)->gen >= 8) {
+       if (IS_CHERRYVIEW(dev)) {
+               int i;
+               seq_printf(m, "Master Interrupt Control:\t%08x\n",
+                          I915_READ(GEN8_MASTER_IRQ));
+
+               seq_printf(m, "Display IER:\t%08x\n",
+                          I915_READ(VLV_IER));
+               seq_printf(m, "Display IIR:\t%08x\n",
+                          I915_READ(VLV_IIR));
+               seq_printf(m, "Display IIR_RW:\t%08x\n",
+                          I915_READ(VLV_IIR_RW));
+               seq_printf(m, "Display IMR:\t%08x\n",
+                          I915_READ(VLV_IMR));
+               for_each_pipe(pipe)
+                       seq_printf(m, "Pipe %c stat:\t%08x\n",
+                                  pipe_name(pipe),
+                                  I915_READ(PIPESTAT(pipe)));
+
+               seq_printf(m, "Port hotplug:\t%08x\n",
+                          I915_READ(PORT_HOTPLUG_EN));
+               seq_printf(m, "DPFLIPSTAT:\t%08x\n",
+                          I915_READ(VLV_DPFLIPSTAT));
+               seq_printf(m, "DPINVGTT:\t%08x\n",
+                          I915_READ(DPINVGTT));
+
+               for (i = 0; i < 4; i++) {
+                       seq_printf(m, "GT Interrupt IMR %d:\t%08x\n",
+                                  i, I915_READ(GEN8_GT_IMR(i)));
+                       seq_printf(m, "GT Interrupt IIR %d:\t%08x\n",
+                                  i, I915_READ(GEN8_GT_IIR(i)));
+                       seq_printf(m, "GT Interrupt IER %d:\t%08x\n",
+                                  i, I915_READ(GEN8_GT_IER(i)));
+               }
+
+               seq_printf(m, "PCU interrupt mask:\t%08x\n",
+                          I915_READ(GEN8_PCU_IMR));
+               seq_printf(m, "PCU interrupt identity:\t%08x\n",
+                          I915_READ(GEN8_PCU_IIR));
+               seq_printf(m, "PCU interrupt enable:\t%08x\n",
+                          I915_READ(GEN8_PCU_IER));
+       } else if (INTEL_INFO(dev)->gen >= 8) {
                seq_printf(m, "Master Interrupt Control:\t%08x\n",
                           I915_READ(GEN8_MASTER_IRQ));
 
@@ -768,7 +808,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
 
 static int i915_gem_fence_regs_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        int i, ret;
@@ -797,10 +837,10 @@ static int i915_gem_fence_regs_info(struct seq_file *m, void *data)
 
 static int i915_hws_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        const u32 *hws;
        int i;
 
@@ -945,7 +985,7 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_next_seqno_fops,
 
 static int i915_rstdby_delays(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u16 crstanddelay;
@@ -966,9 +1006,9 @@ static int i915_rstdby_delays(struct seq_file *m, void *unused)
        return 0;
 }
 
-static int i915_cur_delayinfo(struct seq_file *m, void *unused)
+static int i915_frequency_info(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        int ret = 0;
@@ -991,6 +1031,7 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
                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);
+               u32 rpmodectl, rpinclimit, rpdeclimit;
                u32 rpstat, cagf, reqf;
                u32 rpupei, rpcurup, rpprevup;
                u32 rpdownei, rpcurdown, rpprevdown;
@@ -1011,6 +1052,10 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
                        reqf >>= 25;
                reqf *= GT_FREQUENCY_MULTIPLIER;
 
+               rpmodectl = I915_READ(GEN6_RP_CONTROL);
+               rpinclimit = I915_READ(GEN6_RP_UP_THRESHOLD);
+               rpdeclimit = I915_READ(GEN6_RP_DOWN_THRESHOLD);
+
                rpstat = I915_READ(GEN6_RPSTAT1);
                rpupei = I915_READ(GEN6_RP_CUR_UP_EI);
                rpcurup = I915_READ(GEN6_RP_CUR_UP);
@@ -1027,14 +1072,23 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
                gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL);
                mutex_unlock(&dev->struct_mutex);
 
+               seq_printf(m, "PM IER=0x%08x IMR=0x%08x ISR=0x%08x IIR=0x%08x, MASK=0x%08x\n",
+                          I915_READ(GEN6_PMIER),
+                          I915_READ(GEN6_PMIMR),
+                          I915_READ(GEN6_PMISR),
+                          I915_READ(GEN6_PMIIR),
+                          I915_READ(GEN6_PMINTRMSK));
                seq_printf(m, "GT_PERF_STATUS: 0x%08x\n", gt_perf_status);
-               seq_printf(m, "RPSTAT1: 0x%08x\n", rpstat);
                seq_printf(m, "Render p-state ratio: %d\n",
                           (gt_perf_status & 0xff00) >> 8);
                seq_printf(m, "Render p-state VID: %d\n",
                           gt_perf_status & 0xff);
                seq_printf(m, "Render p-state limit: %d\n",
                           rp_state_limits & 0xff);
+               seq_printf(m, "RPSTAT1: 0x%08x\n", rpstat);
+               seq_printf(m, "RPMODECTL: 0x%08x\n", rpmodectl);
+               seq_printf(m, "RPINCLIMIT: 0x%08x\n", rpinclimit);
+               seq_printf(m, "RPDECLIMIT: 0x%08x\n", rpdeclimit);
                seq_printf(m, "RPNSWREQ: %dMHz\n", reqf);
                seq_printf(m, "CAGF: %dMHz\n", cagf);
                seq_printf(m, "RP CUR UP EI: %dus\n", rpupei &
@@ -1094,7 +1148,7 @@ out:
 
 static int i915_delayfreq_table(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 delayfreq;
@@ -1125,7 +1179,7 @@ static inline int MAP_TO_MV(int map)
 
 static int i915_inttoext_table(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 inttoext;
@@ -1149,7 +1203,7 @@ static int i915_inttoext_table(struct seq_file *m, void *unused)
 
 static int ironlake_drpc_info(struct seq_file *m)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 rgvmodectl, rstdbyctl;
@@ -1219,15 +1273,19 @@ static int ironlake_drpc_info(struct seq_file *m)
 static int vlv_drpc_info(struct seq_file *m)
 {
 
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 rpmodectl1, rcctl1;
        unsigned fw_rendercount = 0, fw_mediacount = 0;
 
+       intel_runtime_pm_get(dev_priv);
+
        rpmodectl1 = I915_READ(GEN6_RP_CONTROL);
        rcctl1 = I915_READ(GEN6_RC_CONTROL);
 
+       intel_runtime_pm_put(dev_priv);
+
        seq_printf(m, "Video Turbo Mode: %s\n",
                   yesno(rpmodectl1 & GEN6_RP_MEDIA_TURBO));
        seq_printf(m, "Turbo enabled: %s\n",
@@ -1247,6 +1305,11 @@ static int vlv_drpc_info(struct seq_file *m)
                        (I915_READ(VLV_GTLC_PW_STATUS) &
                                VLV_GTLC_PW_MEDIA_STATUS_MASK) ? "Up" : "Down");
 
+       seq_printf(m, "Render RC6 residency since boot: %u\n",
+                  I915_READ(VLV_GT_RENDER_RC6));
+       seq_printf(m, "Media RC6 residency since boot: %u\n",
+                  I915_READ(VLV_GT_MEDIA_RC6));
+
        spin_lock_irq(&dev_priv->uncore.lock);
        fw_rendercount = dev_priv->uncore.fw_rendercount;
        fw_mediacount = dev_priv->uncore.fw_mediacount;
@@ -1263,7 +1326,7 @@ static int vlv_drpc_info(struct seq_file *m)
 static int gen6_drpc_info(struct seq_file *m)
 {
 
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 rpmodectl1, gt_core_status, rcctl1, rc6vids = 0;
@@ -1362,7 +1425,7 @@ static int gen6_drpc_info(struct seq_file *m)
 
 static int i915_drpc_info(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
 
        if (IS_VALLEYVIEW(dev))
@@ -1375,7 +1438,7 @@ static int i915_drpc_info(struct seq_file *m, void *unused)
 
 static int i915_fbc_status(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
 
@@ -1437,7 +1500,7 @@ static int i915_fbc_status(struct seq_file *m, void *unused)
 
 static int i915_ips_status(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
 
@@ -1460,7 +1523,7 @@ static int i915_ips_status(struct seq_file *m, void *unused)
 
 static int i915_sr_status(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        bool sr_enabled = false;
@@ -1486,7 +1549,7 @@ static int i915_sr_status(struct seq_file *m, void *unused)
 
 static int i915_emon_status(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        unsigned long temp, chipset, gfx;
@@ -1514,7 +1577,7 @@ static int i915_emon_status(struct seq_file *m, void *unused)
 
 static int i915_ring_freq_table(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        int ret = 0;
@@ -1557,7 +1620,7 @@ out:
 
 static int i915_gfxec(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        int ret;
@@ -1577,7 +1640,7 @@ static int i915_gfxec(struct seq_file *m, void *unused)
 
 static int i915_opregion(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_opregion *opregion = &dev_priv->opregion;
@@ -1605,7 +1668,7 @@ out:
 
 static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct intel_fbdev *ifbdev = NULL;
        struct intel_framebuffer *fb;
@@ -1651,11 +1714,11 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
 
 static int i915_context_status(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
-       struct i915_hw_context *ctx;
+       struct intel_engine_cs *ring;
+       struct intel_context *ctx;
        int ret, i;
 
        ret = mutex_lock_interruptible(&dev->mode_config.mutex);
@@ -1675,6 +1738,9 @@ static int i915_context_status(struct seq_file *m, void *unused)
        }
 
        list_for_each_entry(ctx, &dev_priv->context_list, link) {
+               if (ctx->obj == NULL)
+                       continue;
+
                seq_puts(m, "HW context ");
                describe_ctx(m, ctx);
                for_each_ring(ring, dev_priv, i)
@@ -1692,7 +1758,7 @@ static int i915_context_status(struct seq_file *m, void *unused)
 
 static int i915_gen6_forcewake_count_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        unsigned forcewake_count = 0, fw_rendercount = 0, fw_mediacount = 0;
@@ -1740,7 +1806,7 @@ static const char *swizzle_string(unsigned swizzle)
 
 static int i915_swizzle_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        int ret;
@@ -1788,10 +1854,14 @@ static int i915_swizzle_info(struct seq_file *m, void *data)
 
 static int per_file_ctx(int id, void *ptr, void *data)
 {
-       struct i915_hw_context *ctx = ptr;
+       struct intel_context *ctx = ptr;
        struct seq_file *m = data;
        struct i915_hw_ppgtt *ppgtt = ctx_to_ppgtt(ctx);
 
+       if (i915_gem_context_is_default(ctx))
+               seq_puts(m, "  default context:\n");
+       else
+               seq_printf(m, "  context %d:\n", ctx->id);
        ppgtt->debug_dump(ppgtt, m);
 
        return 0;
@@ -1800,7 +1870,7 @@ static int per_file_ctx(int id, void *ptr, void *data)
 static void gen8_ppgtt_info(struct seq_file *m, struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
        int unused, i;
 
@@ -1816,8 +1886,7 @@ static void gen8_ppgtt_info(struct seq_file *m, struct drm_device *dev)
                        u64 pdp = I915_READ(ring->mmio_base + offset + 4);
                        pdp <<= 32;
                        pdp |= I915_READ(ring->mmio_base + offset);
-                       for (i = 0; i < 4; i++)
-                               seq_printf(m, "\tPDP%d 0x%016llx\n", i, pdp);
+                       seq_printf(m, "\tPDP%d 0x%016llx\n", i, pdp);
                }
        }
 }
@@ -1825,7 +1894,7 @@ static void gen8_ppgtt_info(struct seq_file *m, struct drm_device *dev)
 static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        struct drm_file *file;
        int i;
 
@@ -1852,12 +1921,9 @@ static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev)
 
        list_for_each_entry_reverse(file, &dev->filelist, lhead) {
                struct drm_i915_file_private *file_priv = file->driver_priv;
-               struct i915_hw_ppgtt *pvt_ppgtt;
 
-               pvt_ppgtt = ctx_to_ppgtt(file_priv->private_default_ctx);
                seq_printf(m, "proc: %s\n",
                           get_pid_task(file->pid, PIDTYPE_PID)->comm);
-               seq_puts(m, "  default context:\n");
                idr_for_each(&file_priv->context_idr, per_file_ctx, m);
        }
        seq_printf(m, "ECOCHK: 0x%08x\n", I915_READ(GAM_ECOCHK));
@@ -1865,7 +1931,7 @@ static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev)
 
 static int i915_ppgtt_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
 
@@ -1885,56 +1951,9 @@ static int i915_ppgtt_info(struct seq_file *m, void *data)
        return 0;
 }
 
-static int i915_dpio_info(struct seq_file *m, void *data)
-{
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       int ret;
-
-
-       if (!IS_VALLEYVIEW(dev)) {
-               seq_puts(m, "unsupported\n");
-               return 0;
-       }
-
-       ret = mutex_lock_interruptible(&dev_priv->dpio_lock);
-       if (ret)
-               return ret;
-
-       seq_printf(m, "DPIO_CTL: 0x%08x\n", I915_READ(DPIO_CTL));
-
-       seq_printf(m, "DPIO PLL DW3 CH0 : 0x%08x\n",
-                  vlv_dpio_read(dev_priv, PIPE_A, VLV_PLL_DW3(0)));
-       seq_printf(m, "DPIO PLL DW3 CH1: 0x%08x\n",
-                  vlv_dpio_read(dev_priv, PIPE_A, VLV_PLL_DW3(1)));
-
-       seq_printf(m, "DPIO PLL DW5 CH0: 0x%08x\n",
-                  vlv_dpio_read(dev_priv, PIPE_A, VLV_PLL_DW5(0)));
-       seq_printf(m, "DPIO PLL DW5 CH1: 0x%08x\n",
-                  vlv_dpio_read(dev_priv, PIPE_A, VLV_PLL_DW5(1)));
-
-       seq_printf(m, "DPIO PLL DW7 CH0: 0x%08x\n",
-                  vlv_dpio_read(dev_priv, PIPE_A, VLV_PLL_DW7(0)));
-       seq_printf(m, "DPIO PLL DW7 CH1: 0x%08x\n",
-                  vlv_dpio_read(dev_priv, PIPE_A, VLV_PLL_DW7(1)));
-
-       seq_printf(m, "DPIO PLL DW10 CH0: 0x%08x\n",
-                  vlv_dpio_read(dev_priv, PIPE_A, VLV_PLL_DW10(0)));
-       seq_printf(m, "DPIO PLL DW10 CH1: 0x%08x\n",
-                  vlv_dpio_read(dev_priv, PIPE_A, VLV_PLL_DW10(1)));
-
-       seq_printf(m, "DPIO_FASTCLK_DISABLE: 0x%08x\n",
-                  vlv_dpio_read(dev_priv, PIPE_A, VLV_CMN_DW0));
-
-       mutex_unlock(&dev_priv->dpio_lock);
-
-       return 0;
-}
-
 static int i915_llc(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
 
@@ -2040,11 +2059,11 @@ static int i915_energy_uJ(struct seq_file *m, void *data)
 
 static int i915_pc8_status(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       if (!IS_HASWELL(dev)) {
+       if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) {
                seq_puts(m, "not supported\n");
                return 0;
        }
@@ -2115,7 +2134,7 @@ static const char *power_domain_str(enum intel_display_power_domain domain)
 
 static int i915_power_domain_info(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct i915_power_domains *power_domains = &dev_priv->power_domains;
@@ -2170,7 +2189,7 @@ static void intel_encoder_info(struct seq_file *m,
                               struct intel_crtc *intel_crtc,
                               struct intel_encoder *intel_encoder)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_crtc *crtc = &intel_crtc->base;
        struct intel_connector *intel_connector;
@@ -2178,12 +2197,12 @@ static void intel_encoder_info(struct seq_file *m,
 
        encoder = &intel_encoder->base;
        seq_printf(m, "\tencoder %d: type: %s, connectors:\n",
-                  encoder->base.id, drm_get_encoder_name(encoder));
+                  encoder->base.id, encoder->name);
        for_each_connector_on_encoder(dev, encoder, intel_connector) {
                struct drm_connector *connector = &intel_connector->base;
                seq_printf(m, "\t\tconnector %d: type: %s, status: %s",
                           connector->base.id,
-                          drm_get_connector_name(connector),
+                          connector->name,
                           drm_get_connector_status_name(connector->status));
                if (connector->status == connector_status_connected) {
                        struct drm_display_mode *mode = &crtc->mode;
@@ -2197,7 +2216,7 @@ static void intel_encoder_info(struct seq_file *m,
 
 static void intel_crtc_info(struct seq_file *m, struct intel_crtc *intel_crtc)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_crtc *crtc = &intel_crtc->base;
        struct intel_encoder *intel_encoder;
@@ -2254,7 +2273,7 @@ static void intel_connector_info(struct seq_file *m,
        struct drm_display_mode *mode;
 
        seq_printf(m, "connector %d: type %s, status: %s\n",
-                  connector->base.id, drm_get_connector_name(connector),
+                  connector->base.id, connector->name,
                   drm_get_connector_status_name(connector->status));
        if (connector->status == connector_status_connected) {
                seq_printf(m, "\tname: %s\n", connector->display_info.name);
@@ -2286,10 +2305,8 @@ static bool cursor_active(struct drm_device *dev, int pipe)
 
        if (IS_845G(dev) || IS_I865G(dev))
                state = I915_READ(_CURACNTR) & CURSOR_ENABLE;
-       else if (INTEL_INFO(dev)->gen <= 6 || IS_VALLEYVIEW(dev))
-               state = I915_READ(CURCNTR(pipe)) & CURSOR_MODE;
        else
-               state = I915_READ(CURCNTR_IVB(pipe)) & CURSOR_MODE;
+               state = I915_READ(CURCNTR(pipe)) & CURSOR_MODE;
 
        return state;
 }
@@ -2299,10 +2316,7 @@ static bool cursor_position(struct drm_device *dev, int pipe, int *x, int *y)
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 pos;
 
-       if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev) || IS_BROADWELL(dev))
-               pos = I915_READ(CURPOS_IVB(pipe));
-       else
-               pos = I915_READ(CURPOS(pipe));
+       pos = I915_READ(CURPOS(pipe));
 
        *x = (pos >> CURSOR_X_SHIFT) & CURSOR_POS_MASK;
        if (pos & (CURSOR_POS_SIGN << CURSOR_X_SHIFT))
@@ -2317,7 +2331,7 @@ static bool cursor_position(struct drm_device *dev, int pipe, int *x, int *y)
 
 static int i915_display_info(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_info_node *node = m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *crtc;
@@ -2327,7 +2341,7 @@ static int i915_display_info(struct seq_file *m, void *unused)
        drm_modeset_lock_all(dev);
        seq_printf(m, "CRTC info\n");
        seq_printf(m, "---------\n");
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+       for_each_intel_crtc(dev, crtc) {
                bool active;
                int x, y;
 
@@ -2339,10 +2353,14 @@ static int i915_display_info(struct seq_file *m, void *unused)
 
                        active = cursor_position(dev, crtc->pipe, &x, &y);
                        seq_printf(m, "\tcursor visible? %s, position (%d, %d), addr 0x%08x, active? %s\n",
-                                  yesno(crtc->cursor_visible),
+                                  yesno(crtc->cursor_base),
                                   x, y, crtc->cursor_addr,
                                   yesno(active));
                }
+
+               seq_printf(m, "\tunderrun reporting: cpu=%s pch=%s \n",
+                          yesno(!crtc->cpu_fifo_underrun_disabled),
+                          yesno(!crtc->pch_fifo_underrun_disabled));
        }
 
        seq_printf(m, "\n");
@@ -2595,7 +2613,7 @@ static int i9xx_pipe_crc_auto_source(struct drm_device *dev, enum pipe pipe,
 
        *source = INTEL_PIPE_CRC_SOURCE_PIPE;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
        list_for_each_entry(encoder, &dev->mode_config.encoder_list,
                            base.head) {
                if (!encoder->base.crtc)
@@ -2631,7 +2649,7 @@ static int i9xx_pipe_crc_auto_source(struct drm_device *dev, enum pipe pipe,
                        break;
                }
        }
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
 
        return ret;
 }
@@ -3106,7 +3124,7 @@ static const struct file_operations i915_display_crc_ctl_fops = {
 static void wm_latency_show(struct seq_file *m, const uint16_t wm[5])
 {
        struct drm_device *dev = m->private;
-       int num_levels = IS_HASWELL(dev) || IS_BROADWELL(dev) ? 5 : 4;
+       int num_levels = ilk_wm_max_level(dev) + 1;
        int level;
 
        drm_modeset_lock_all(dev);
@@ -3189,7 +3207,7 @@ static ssize_t wm_latency_write(struct file *file, const char __user *ubuf,
        struct seq_file *m = file->private_data;
        struct drm_device *dev = m->private;
        uint16_t new[5] = { 0 };
-       int num_levels = IS_HASWELL(dev) || IS_BROADWELL(dev) ? 5 : 4;
+       int num_levels = ilk_wm_max_level(dev) + 1;
        int level;
        int ret;
        char tmp[32];
@@ -3286,9 +3304,15 @@ static int
 i915_wedged_set(void *data, u64 val)
 {
        struct drm_device *dev = data;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       intel_runtime_pm_get(dev_priv);
 
        i915_handle_error(dev, val,
                          "Manually setting wedged to %llu", val);
+
+       intel_runtime_pm_put(dev_priv);
+
        return 0;
 }
 
@@ -3774,7 +3798,7 @@ static const struct drm_info_list i915_debugfs_list[] = {
        {"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_frequency_info", i915_frequency_info, 0},
        {"i915_delayfreq_table", i915_delayfreq_table, 0},
        {"i915_inttoext_table", i915_inttoext_table, 0},
        {"i915_drpc_info", i915_drpc_info, 0},
@@ -3790,7 +3814,6 @@ static const struct drm_info_list i915_debugfs_list[] = {
        {"i915_gen6_forcewake_count", i915_gen6_forcewake_count_info, 0},
        {"i915_swizzle_info", i915_swizzle_info, 0},
        {"i915_ppgtt_info", i915_ppgtt_info, 0},
-       {"i915_dpio", i915_dpio_info, 0},
        {"i915_llc", i915_llc, 0},
        {"i915_edp_psr_status", i915_edp_psr_status, 0},
        {"i915_sink_crc_eDP1", i915_sink_crc, 0},
index eedb023af27d705b7ddd23727093b17baf5352c8..4c22a5b7f4c532688ef513c716c9f0849d9fcb46 100644 (file)
@@ -44,6 +44,7 @@
 #include <acpi/video.h>
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
+#include <linux/oom.h>
 
 #define LP_RING(d) (&((struct drm_i915_private *)(d))->ring[RCS])
 
@@ -63,7 +64,7 @@
  * has access to the ring.
  */
 #define RING_LOCK_TEST_WITH_RETURN(dev, file) do {                     \
-       if (LP_RING(dev->dev_private)->obj == NULL)                     \
+       if (LP_RING(dev->dev_private)->buffer->obj == NULL)                     \
                LOCK_TEST_WITH_RETURN(dev, file);                       \
 } while (0)
 
@@ -119,7 +120,7 @@ static void i915_write_hws_pga(struct drm_device *dev)
 static void i915_free_hws(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring = LP_RING(dev_priv);
+       struct intel_engine_cs *ring = LP_RING(dev_priv);
 
        if (dev_priv->status_page_dmah) {
                drm_pci_free(dev, dev_priv->status_page_dmah);
@@ -139,7 +140,8 @@ void i915_kernel_lost_context(struct drm_device * dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_master_private *master_priv;
-       struct intel_ring_buffer *ring = LP_RING(dev_priv);
+       struct intel_engine_cs *ring = LP_RING(dev_priv);
+       struct intel_ringbuffer *ringbuf = ring->buffer;
 
        /*
         * We should never lose context on the ring with modesetting
@@ -148,17 +150,17 @@ void i915_kernel_lost_context(struct drm_device * dev)
        if (drm_core_check_feature(dev, DRIVER_MODESET))
                return;
 
-       ring->head = I915_READ_HEAD(ring) & HEAD_ADDR;
-       ring->tail = I915_READ_TAIL(ring) & TAIL_ADDR;
-       ring->space = ring->head - (ring->tail + I915_RING_FREE_SPACE);
-       if (ring->space < 0)
-               ring->space += ring->size;
+       ringbuf->head = I915_READ_HEAD(ring) & HEAD_ADDR;
+       ringbuf->tail = I915_READ_TAIL(ring) & TAIL_ADDR;
+       ringbuf->space = ringbuf->head - (ringbuf->tail + I915_RING_FREE_SPACE);
+       if (ringbuf->space < 0)
+               ringbuf->space += ringbuf->size;
 
        if (!dev->primary->master)
                return;
 
        master_priv = dev->primary->master->driver_priv;
-       if (ring->head == ring->tail && master_priv->sarea_priv)
+       if (ringbuf->head == ringbuf->tail && master_priv->sarea_priv)
                master_priv->sarea_priv->perf_boxes |= I915_BOX_RING_EMPTY;
 }
 
@@ -201,7 +203,7 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
        }
 
        if (init->ring_size != 0) {
-               if (LP_RING(dev_priv)->obj != NULL) {
+               if (LP_RING(dev_priv)->buffer->obj != NULL) {
                        i915_dma_cleanup(dev);
                        DRM_ERROR("Client tried to initialize ringbuffer in "
                                  "GEM mode\n");
@@ -234,11 +236,11 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
 static int i915_dma_resume(struct drm_device * dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring = LP_RING(dev_priv);
+       struct intel_engine_cs *ring = LP_RING(dev_priv);
 
        DRM_DEBUG_DRIVER("%s\n", __func__);
 
-       if (ring->virtual_start == NULL) {
+       if (ring->buffer->virtual_start == NULL) {
                DRM_ERROR("can not ioremap virtual address for"
                          " ring buffer\n");
                return -ENOMEM;
@@ -360,7 +362,7 @@ static int i915_emit_cmds(struct drm_device * dev, int *buffer, int dwords)
        struct drm_i915_private *dev_priv = dev->dev_private;
        int i, ret;
 
-       if ((dwords+1) * sizeof(int) >= LP_RING(dev_priv)->size - 8)
+       if ((dwords+1) * sizeof(int) >= LP_RING(dev_priv)->buffer->size - 8)
                return -EINVAL;
 
        for (i = 0; i < dwords;) {
@@ -782,7 +784,7 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
        int ret = 0;
-       struct intel_ring_buffer *ring = LP_RING(dev_priv);
+       struct intel_engine_cs *ring = LP_RING(dev_priv);
 
        DRM_DEBUG_DRIVER("irq_nr=%d breadcrumb=%d\n", irq_nr,
                  READ_BREADCRUMB(dev_priv));
@@ -823,7 +825,7 @@ static int i915_irq_emit(struct drm_device *dev, void *data,
        if (drm_core_check_feature(dev, DRIVER_MODESET))
                return -ENODEV;
 
-       if (!dev_priv || !LP_RING(dev_priv)->virtual_start) {
+       if (!dev_priv || !LP_RING(dev_priv)->buffer->virtual_start) {
                DRM_ERROR("called with no initialization\n");
                return -EINVAL;
        }
@@ -1017,6 +1019,9 @@ static int i915_getparam(struct drm_device *dev, void *data,
        case I915_PARAM_HAS_EXEC_HANDLE_LUT:
                value = 1;
                break;
+       case I915_PARAM_CMD_PARSER_VERSION:
+               value = i915_cmd_parser_get_version();
+               break;
        default:
                DRM_DEBUG("Unknown parameter %d\n", param->param);
                return -EINVAL;
@@ -1070,7 +1075,7 @@ static int i915_set_status_page(struct drm_device *dev, void *data,
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        drm_i915_hws_addr_t *hws = data;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
 
        if (drm_core_check_feature(dev, DRIVER_MODESET))
                return -ENODEV;
@@ -1277,12 +1282,13 @@ static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_
 static bool i915_switcheroo_can_switch(struct pci_dev *pdev)
 {
        struct drm_device *dev = pci_get_drvdata(pdev);
-       bool can_switch;
 
-       spin_lock(&dev->count_lock);
-       can_switch = (dev->open_count == 0);
-       spin_unlock(&dev->count_lock);
-       return can_switch;
+       /*
+        * FIXME: open_count is protected by drm_global_mutex but that would lead to
+        * locking inversion with the driver load path. And the access here is
+        * completely racy anyway. So don't bother with locking for now.
+        */
+       return dev->open_count == 0;
 }
 
 static const struct vga_switcheroo_client_ops i915_switcheroo_ops = {
@@ -1326,7 +1332,7 @@ static int i915_load_modeset_init(struct drm_device *dev)
 
        intel_power_domains_init_hw(dev_priv);
 
-       ret = drm_irq_install(dev);
+       ret = drm_irq_install(dev, dev->pdev->irq);
        if (ret)
                goto cleanup_gem_stolen;
 
@@ -1336,7 +1342,7 @@ static int i915_load_modeset_init(struct drm_device *dev)
 
        ret = i915_gem_init(dev);
        if (ret)
-               goto cleanup_power;
+               goto cleanup_irq;
 
        INIT_WORK(&dev_priv->console_resume_work, intel_console_resume);
 
@@ -1345,10 +1351,8 @@ static int i915_load_modeset_init(struct drm_device *dev)
        /* Always safe in the mode setting case. */
        /* FIXME: do pre/post-mode set stuff in core KMS code */
        dev->vblank_disable_allowed = true;
-       if (INTEL_INFO(dev)->num_pipes == 0) {
-               intel_display_power_put(dev_priv, POWER_DOMAIN_VGA);
+       if (INTEL_INFO(dev)->num_pipes == 0)
                return 0;
-       }
 
        ret = intel_fbdev_init(dev);
        if (ret)
@@ -1383,8 +1387,7 @@ cleanup_gem:
        mutex_unlock(&dev->struct_mutex);
        WARN_ON(dev_priv->mm.aliasing_ppgtt);
        drm_mm_takedown(&dev_priv->gtt.base.mm);
-cleanup_power:
-       intel_display_power_put(dev_priv, POWER_DOMAIN_VGA);
+cleanup_irq:
        drm_irq_uninstall(dev);
 cleanup_gem_stolen:
        i915_gem_cleanup_stolen(dev);
@@ -1739,8 +1742,8 @@ out_power_well:
        intel_power_domains_remove(dev_priv);
        drm_vblank_cleanup(dev);
 out_gem_unload:
-       if (dev_priv->mm.inactive_shrinker.scan_objects)
-               unregister_shrinker(&dev_priv->mm.inactive_shrinker);
+       WARN_ON(unregister_oom_notifier(&dev_priv->mm.oom_notifier));
+       unregister_shrinker(&dev_priv->mm.shrinker);
 
        if (dev->pdev->msi_enabled)
                pci_disable_msi(dev->pdev);
@@ -1791,8 +1794,8 @@ int i915_driver_unload(struct drm_device *dev)
 
        i915_teardown_sysfs(dev);
 
-       if (dev_priv->mm.inactive_shrinker.scan_objects)
-               unregister_shrinker(&dev_priv->mm.inactive_shrinker);
+       WARN_ON(unregister_oom_notifier(&dev_priv->mm.oom_notifier));
+       unregister_shrinker(&dev_priv->mm.shrinker);
 
        io_mapping_free(dev_priv->gtt.mappable);
        arch_phys_wc_del(dev_priv->gtt.mtrr);
@@ -1864,7 +1867,7 @@ int i915_driver_unload(struct drm_device *dev)
                kmem_cache_destroy(dev_priv->slab);
 
        pci_dev_put(dev_priv->bridge_dev);
-       kfree(dev->dev_private);
+       kfree(dev_priv);
 
        return 0;
 }
@@ -1925,6 +1928,8 @@ void i915_driver_postclose(struct drm_device *dev, struct drm_file *file)
 {
        struct drm_i915_file_private *file_priv = file->driver_priv;
 
+       if (file_priv && file_priv->bsd_ring)
+               file_priv->bsd_ring = NULL;
        kfree(file_priv);
 }
 
@@ -1978,9 +1983,10 @@ const struct drm_ioctl_desc i915_ioctls[] = {
        DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_DESTROY, i915_gem_context_destroy_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW),
        DRM_IOCTL_DEF_DRV(I915_REG_READ, i915_reg_read_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW),
        DRM_IOCTL_DEF_DRV(I915_GET_RESET_STATS, i915_get_reset_stats_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(I915_GEM_USERPTR, i915_gem_userptr_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW),
 };
 
-int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls);
+int i915_max_ioctl = ARRAY_SIZE(i915_ioctls);
 
 /*
  * This is really ugly: Because old userspace abused the linux agp interface to
index 82f4d1f47d3b734c582b1d2b02b770039fb042fa..651e65e051c08a8e25189719aa38f6226f699271 100644 (file)
@@ -36,6 +36,7 @@
 
 #include <linux/console.h>
 #include <linux/module.h>
+#include <linux/pm_runtime.h>
 #include <drm/drm_crtc_helper.h>
 
 static struct drm_driver driver;
@@ -49,12 +50,30 @@ static struct drm_driver driver;
        .dpll_md_offsets = { DPLL_A_MD_OFFSET, DPLL_B_MD_OFFSET }, \
        .palette_offsets = { PALETTE_A_OFFSET, PALETTE_B_OFFSET }
 
+#define GEN_CHV_PIPEOFFSETS \
+       .pipe_offsets = { PIPE_A_OFFSET, PIPE_B_OFFSET, \
+                         CHV_PIPE_C_OFFSET }, \
+       .trans_offsets = { TRANSCODER_A_OFFSET, TRANSCODER_B_OFFSET, \
+                          CHV_TRANSCODER_C_OFFSET, }, \
+       .dpll_offsets = { DPLL_A_OFFSET, DPLL_B_OFFSET, \
+                         CHV_DPLL_C_OFFSET }, \
+       .dpll_md_offsets = { DPLL_A_MD_OFFSET, DPLL_B_MD_OFFSET, \
+                            CHV_DPLL_C_MD_OFFSET }, \
+       .palette_offsets = { PALETTE_A_OFFSET, PALETTE_B_OFFSET, \
+                            CHV_PALETTE_C_OFFSET }
+
+#define CURSOR_OFFSETS \
+       .cursor_offsets = { CURSOR_A_OFFSET, CURSOR_B_OFFSET, CHV_CURSOR_C_OFFSET }
+
+#define IVB_CURSOR_OFFSETS \
+       .cursor_offsets = { CURSOR_A_OFFSET, IVB_CURSOR_B_OFFSET, IVB_CURSOR_C_OFFSET }
 
 static const struct intel_device_info intel_i830_info = {
        .gen = 2, .is_mobile = 1, .cursor_needs_physical = 1, .num_pipes = 2,
        .has_overlay = 1, .overlay_needs_physical = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_845g_info = {
@@ -62,6 +81,7 @@ static const struct intel_device_info intel_845g_info = {
        .has_overlay = 1, .overlay_needs_physical = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_i85x_info = {
@@ -71,6 +91,7 @@ static const struct intel_device_info intel_i85x_info = {
        .has_fbc = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_i865g_info = {
@@ -78,6 +99,7 @@ static const struct intel_device_info intel_i865g_info = {
        .has_overlay = 1, .overlay_needs_physical = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_i915g_info = {
@@ -85,6 +107,7 @@ static const struct intel_device_info intel_i915g_info = {
        .has_overlay = 1, .overlay_needs_physical = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 static const struct intel_device_info intel_i915gm_info = {
        .gen = 3, .is_mobile = 1, .num_pipes = 2,
@@ -94,12 +117,14 @@ static const struct intel_device_info intel_i915gm_info = {
        .has_fbc = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 static const struct intel_device_info intel_i945g_info = {
        .gen = 3, .has_hotplug = 1, .cursor_needs_physical = 1, .num_pipes = 2,
        .has_overlay = 1, .overlay_needs_physical = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 static const struct intel_device_info intel_i945gm_info = {
        .gen = 3, .is_i945gm = 1, .is_mobile = 1, .num_pipes = 2,
@@ -109,6 +134,7 @@ static const struct intel_device_info intel_i945gm_info = {
        .has_fbc = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_i965g_info = {
@@ -117,6 +143,7 @@ static const struct intel_device_info intel_i965g_info = {
        .has_overlay = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_i965gm_info = {
@@ -126,6 +153,7 @@ static const struct intel_device_info intel_i965gm_info = {
        .supports_tv = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_g33_info = {
@@ -134,6 +162,7 @@ static const struct intel_device_info intel_g33_info = {
        .has_overlay = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_g45_info = {
@@ -141,6 +170,7 @@ static const struct intel_device_info intel_g45_info = {
        .has_pipe_cxsr = 1, .has_hotplug = 1,
        .ring_mask = RENDER_RING | BSD_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_gm45_info = {
@@ -150,6 +180,7 @@ static const struct intel_device_info intel_gm45_info = {
        .supports_tv = 1,
        .ring_mask = RENDER_RING | BSD_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_pineview_info = {
@@ -157,6 +188,7 @@ static const struct intel_device_info intel_pineview_info = {
        .need_gfx_hws = 1, .has_hotplug = 1,
        .has_overlay = 1,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_ironlake_d_info = {
@@ -164,6 +196,7 @@ static const struct intel_device_info intel_ironlake_d_info = {
        .need_gfx_hws = 1, .has_hotplug = 1,
        .ring_mask = RENDER_RING | BSD_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_ironlake_m_info = {
@@ -172,6 +205,7 @@ static const struct intel_device_info intel_ironlake_m_info = {
        .has_fbc = 1,
        .ring_mask = RENDER_RING | BSD_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_sandybridge_d_info = {
@@ -181,6 +215,7 @@ static const struct intel_device_info intel_sandybridge_d_info = {
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING,
        .has_llc = 1,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_sandybridge_m_info = {
@@ -190,6 +225,7 @@ static const struct intel_device_info intel_sandybridge_m_info = {
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING,
        .has_llc = 1,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 #define GEN7_FEATURES  \
@@ -203,6 +239,7 @@ static const struct intel_device_info intel_ivybridge_d_info = {
        GEN7_FEATURES,
        .is_ivybridge = 1,
        GEN_DEFAULT_PIPEOFFSETS,
+       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_ivybridge_m_info = {
@@ -210,6 +247,7 @@ static const struct intel_device_info intel_ivybridge_m_info = {
        .is_ivybridge = 1,
        .is_mobile = 1,
        GEN_DEFAULT_PIPEOFFSETS,
+       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_ivybridge_q_info = {
@@ -217,6 +255,7 @@ static const struct intel_device_info intel_ivybridge_q_info = {
        .is_ivybridge = 1,
        .num_pipes = 0, /* legal, last one wins */
        GEN_DEFAULT_PIPEOFFSETS,
+       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_valleyview_m_info = {
@@ -228,6 +267,7 @@ static const struct intel_device_info intel_valleyview_m_info = {
        .has_fbc = 0, /* legal, last one wins */
        .has_llc = 0, /* legal, last one wins */
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_valleyview_d_info = {
@@ -238,6 +278,7 @@ static const struct intel_device_info intel_valleyview_d_info = {
        .has_fbc = 0, /* legal, last one wins */
        .has_llc = 0, /* legal, last one wins */
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_haswell_d_info = {
@@ -247,6 +288,7 @@ static const struct intel_device_info intel_haswell_d_info = {
        .has_fpga_dbg = 1,
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_haswell_m_info = {
@@ -257,6 +299,7 @@ static const struct intel_device_info intel_haswell_m_info = {
        .has_fpga_dbg = 1,
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_broadwell_d_info = {
@@ -267,6 +310,7 @@ static const struct intel_device_info intel_broadwell_d_info = {
        .has_ddi = 1,
        .has_fbc = 1,
        GEN_DEFAULT_PIPEOFFSETS,
+       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_broadwell_m_info = {
@@ -277,6 +321,40 @@ static const struct intel_device_info intel_broadwell_m_info = {
        .has_ddi = 1,
        .has_fbc = 1,
        GEN_DEFAULT_PIPEOFFSETS,
+       IVB_CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_broadwell_gt3d_info = {
+       .gen = 8, .num_pipes = 3,
+       .need_gfx_hws = 1, .has_hotplug = 1,
+       .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
+       .has_llc = 1,
+       .has_ddi = 1,
+       .has_fbc = 1,
+       GEN_DEFAULT_PIPEOFFSETS,
+       IVB_CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_broadwell_gt3m_info = {
+       .gen = 8, .is_mobile = 1, .num_pipes = 3,
+       .need_gfx_hws = 1, .has_hotplug = 1,
+       .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
+       .has_llc = 1,
+       .has_ddi = 1,
+       .has_fbc = 1,
+       GEN_DEFAULT_PIPEOFFSETS,
+       IVB_CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_cherryview_info = {
+       .is_preliminary = 1,
+       .gen = 8, .num_pipes = 3,
+       .need_gfx_hws = 1, .has_hotplug = 1,
+       .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
+       .is_valleyview = 1,
+       .display_mmio_offset = VLV_DISPLAY_BASE,
+       GEN_CHV_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 /*
@@ -311,8 +389,11 @@ static const struct intel_device_info intel_broadwell_m_info = {
        INTEL_HSW_M_IDS(&intel_haswell_m_info), \
        INTEL_VLV_M_IDS(&intel_valleyview_m_info),      \
        INTEL_VLV_D_IDS(&intel_valleyview_d_info),      \
-       INTEL_BDW_M_IDS(&intel_broadwell_m_info),       \
-       INTEL_BDW_D_IDS(&intel_broadwell_d_info)
+       INTEL_BDW_GT12M_IDS(&intel_broadwell_m_info),   \
+       INTEL_BDW_GT12D_IDS(&intel_broadwell_d_info),   \
+       INTEL_BDW_GT3M_IDS(&intel_broadwell_gt3m_info), \
+       INTEL_BDW_GT3D_IDS(&intel_broadwell_gt3d_info), \
+       INTEL_CHV_IDS(&intel_cherryview_info)
 
 static const struct pci_device_id pciidlist[] = {              /* aka */
        INTEL_PCI_IDS,
@@ -445,18 +526,20 @@ static int i915_drm_freeze(struct drm_device *dev)
                        return error;
                }
 
-               cancel_delayed_work_sync(&dev_priv->rps.delayed_resume_work);
-
                drm_irq_uninstall(dev);
                dev_priv->enable_hotplug_processing = false;
+
+               intel_disable_gt_powersave(dev);
+
                /*
                 * Disable CRTCs directly since we want to preserve sw state
                 * for _thaw.
                 */
-               mutex_lock(&dev->mode_config.mutex);
-               list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+               drm_modeset_lock_all(dev);
+               for_each_crtc(dev, crtc) {
                        dev_priv->display.crtc_disable(crtc);
-               mutex_unlock(&dev->mode_config.mutex);
+               }
+               drm_modeset_unlock_all(dev);
 
                intel_modeset_suspend_hw(dev);
        }
@@ -519,24 +602,6 @@ void intel_console_resume(struct work_struct *work)
        console_unlock();
 }
 
-static void intel_resume_hotplug(struct drm_device *dev)
-{
-       struct drm_mode_config *mode_config = &dev->mode_config;
-       struct intel_encoder *encoder;
-
-       mutex_lock(&mode_config->mutex);
-       DRM_DEBUG_KMS("running encoder hotplug functions\n");
-
-       list_for_each_entry(encoder, &mode_config->encoder_list, base.head)
-               if (encoder->hot_plug)
-                       encoder->hot_plug(encoder);
-
-       mutex_unlock(&mode_config->mutex);
-
-       /* Just fire off a uevent and let userspace tell us what to do */
-       drm_helper_hpd_irq_event(dev);
-}
-
 static int i915_drm_thaw_early(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -551,7 +616,6 @@ static int i915_drm_thaw_early(struct drm_device *dev)
 static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       int error = 0;
 
        if (drm_core_check_feature(dev, DRIVER_MODESET) &&
            restore_gtt_mappings) {
@@ -569,12 +633,14 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
                drm_mode_config_reset(dev);
 
                mutex_lock(&dev->struct_mutex);
-
-               error = i915_gem_init_hw(dev);
+               if (i915_gem_init_hw(dev)) {
+                       DRM_ERROR("failed to re-initialize GPU, declaring wedged!\n");
+                       atomic_set_mask(I915_WEDGED, &dev_priv->gpu_error.reset_counter);
+               }
                mutex_unlock(&dev->struct_mutex);
 
                /* We need working interrupts for modeset enabling ... */
-               drm_irq_install(dev);
+               drm_irq_install(dev, dev->pdev->irq);
 
                intel_modeset_init_hw(dev);
 
@@ -591,7 +657,7 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
                intel_hpd_init(dev);
                dev_priv->enable_hotplug_processing = true;
                /* Config may have changed between suspend and resume */
-               intel_resume_hotplug(dev);
+               drm_helper_hpd_irq_event(dev);
        }
 
        intel_opregion_init(dev);
@@ -613,7 +679,7 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
        mutex_unlock(&dev_priv->modeset_restore_lock);
 
        intel_runtime_pm_put(dev_priv);
-       return error;
+       return 0;
 }
 
 static int i915_drm_thaw(struct drm_device *dev)
@@ -746,18 +812,20 @@ int i915_reset(struct drm_device *dev)
                        return ret;
                }
 
-               drm_irq_uninstall(dev);
-               drm_irq_install(dev);
+               /*
+                * FIXME: This races pretty badly against concurrent holders of
+                * ring interrupts. This is possible since we've started to drop
+                * dev->struct_mutex in select places when waiting for the gpu.
+                */
 
-               /* rps/rc6 re-init is necessary to restore state lost after the
-                * reset and the re-install of drm irq. Skip for ironlake per
+               /*
+                * rps/rc6 re-init is necessary to restore state lost after the
+                * reset and the re-install of gt irqs. Skip for ironlake per
                 * previous concerns that it doesn't respond well to some forms
-                * of re-init after reset. */
-               if (INTEL_INFO(dev)->gen > 5) {
-                       mutex_lock(&dev->struct_mutex);
-                       intel_enable_gt_powersave(dev);
-                       mutex_unlock(&dev->struct_mutex);
-               }
+                * of re-init after reset.
+                */
+               if (INTEL_INFO(dev)->gen > 5)
+                       intel_reset_gt_powersave(dev);
 
                intel_hpd_init(dev);
        } else {
@@ -891,21 +959,453 @@ static int i915_pm_poweroff(struct device *dev)
        return i915_drm_freeze(drm_dev);
 }
 
-static int i915_runtime_suspend(struct device *device)
+static int hsw_runtime_suspend(struct drm_i915_private *dev_priv)
+{
+       hsw_enable_pc8(dev_priv);
+
+       return 0;
+}
+
+static int snb_runtime_resume(struct drm_i915_private *dev_priv)
+{
+       struct drm_device *dev = dev_priv->dev;
+
+       intel_init_pch_refclk(dev);
+
+       return 0;
+}
+
+static int hsw_runtime_resume(struct drm_i915_private *dev_priv)
+{
+       hsw_disable_pc8(dev_priv);
+
+       return 0;
+}
+
+/*
+ * Save all Gunit registers that may be lost after a D3 and a subsequent
+ * S0i[R123] transition. The list of registers needing a save/restore is
+ * defined in the VLV2_S0IXRegs document. This documents marks all Gunit
+ * registers in the following way:
+ * - Driver: saved/restored by the driver
+ * - Punit : saved/restored by the Punit firmware
+ * - No, w/o marking: no need to save/restore, since the register is R/O or
+ *                    used internally by the HW in a way that doesn't depend
+ *                    keeping the content across a suspend/resume.
+ * - Debug : used for debugging
+ *
+ * We save/restore all registers marked with 'Driver', with the following
+ * exceptions:
+ * - Registers out of use, including also registers marked with 'Debug'.
+ *   These have no effect on the driver's operation, so we don't save/restore
+ *   them to reduce the overhead.
+ * - Registers that are fully setup by an initialization function called from
+ *   the resume path. For example many clock gating and RPS/RC6 registers.
+ * - Registers that provide the right functionality with their reset defaults.
+ *
+ * TODO: Except for registers that based on the above 3 criteria can be safely
+ * ignored, we save/restore all others, practically treating the HW context as
+ * a black-box for the driver. Further investigation is needed to reduce the
+ * saved/restored registers even further, by following the same 3 criteria.
+ */
+static void vlv_save_gunit_s0ix_state(struct drm_i915_private *dev_priv)
+{
+       struct vlv_s0ix_state *s = &dev_priv->vlv_s0ix_state;
+       int i;
+
+       /* GAM 0x4000-0x4770 */
+       s->wr_watermark         = I915_READ(GEN7_WR_WATERMARK);
+       s->gfx_prio_ctrl        = I915_READ(GEN7_GFX_PRIO_CTRL);
+       s->arb_mode             = I915_READ(ARB_MODE);
+       s->gfx_pend_tlb0        = I915_READ(GEN7_GFX_PEND_TLB0);
+       s->gfx_pend_tlb1        = I915_READ(GEN7_GFX_PEND_TLB1);
+
+       for (i = 0; i < ARRAY_SIZE(s->lra_limits); i++)
+               s->lra_limits[i] = I915_READ(GEN7_LRA_LIMITS_BASE + i * 4);
+
+       s->media_max_req_count  = I915_READ(GEN7_MEDIA_MAX_REQ_COUNT);
+       s->gfx_max_req_count    = I915_READ(GEN7_MEDIA_MAX_REQ_COUNT);
+
+       s->render_hwsp          = I915_READ(RENDER_HWS_PGA_GEN7);
+       s->ecochk               = I915_READ(GAM_ECOCHK);
+       s->bsd_hwsp             = I915_READ(BSD_HWS_PGA_GEN7);
+       s->blt_hwsp             = I915_READ(BLT_HWS_PGA_GEN7);
+
+       s->tlb_rd_addr          = I915_READ(GEN7_TLB_RD_ADDR);
+
+       /* MBC 0x9024-0x91D0, 0x8500 */
+       s->g3dctl               = I915_READ(VLV_G3DCTL);
+       s->gsckgctl             = I915_READ(VLV_GSCKGCTL);
+       s->mbctl                = I915_READ(GEN6_MBCTL);
+
+       /* GCP 0x9400-0x9424, 0x8100-0x810C */
+       s->ucgctl1              = I915_READ(GEN6_UCGCTL1);
+       s->ucgctl3              = I915_READ(GEN6_UCGCTL3);
+       s->rcgctl1              = I915_READ(GEN6_RCGCTL1);
+       s->rcgctl2              = I915_READ(GEN6_RCGCTL2);
+       s->rstctl               = I915_READ(GEN6_RSTCTL);
+       s->misccpctl            = I915_READ(GEN7_MISCCPCTL);
+
+       /* GPM 0xA000-0xAA84, 0x8000-0x80FC */
+       s->gfxpause             = I915_READ(GEN6_GFXPAUSE);
+       s->rpdeuhwtc            = I915_READ(GEN6_RPDEUHWTC);
+       s->rpdeuc               = I915_READ(GEN6_RPDEUC);
+       s->ecobus               = I915_READ(ECOBUS);
+       s->pwrdwnupctl          = I915_READ(VLV_PWRDWNUPCTL);
+       s->rp_down_timeout      = I915_READ(GEN6_RP_DOWN_TIMEOUT);
+       s->rp_deucsw            = I915_READ(GEN6_RPDEUCSW);
+       s->rcubmabdtmr          = I915_READ(GEN6_RCUBMABDTMR);
+       s->rcedata              = I915_READ(VLV_RCEDATA);
+       s->spare2gh             = I915_READ(VLV_SPAREG2H);
+
+       /* Display CZ domain, 0x4400C-0x4402C, 0x4F000-0x4F11F */
+       s->gt_imr               = I915_READ(GTIMR);
+       s->gt_ier               = I915_READ(GTIER);
+       s->pm_imr               = I915_READ(GEN6_PMIMR);
+       s->pm_ier               = I915_READ(GEN6_PMIER);
+
+       for (i = 0; i < ARRAY_SIZE(s->gt_scratch); i++)
+               s->gt_scratch[i] = I915_READ(GEN7_GT_SCRATCH_BASE + i * 4);
+
+       /* GT SA CZ domain, 0x100000-0x138124 */
+       s->tilectl              = I915_READ(TILECTL);
+       s->gt_fifoctl           = I915_READ(GTFIFOCTL);
+       s->gtlc_wake_ctrl       = I915_READ(VLV_GTLC_WAKE_CTRL);
+       s->gtlc_survive         = I915_READ(VLV_GTLC_SURVIVABILITY_REG);
+       s->pmwgicz              = I915_READ(VLV_PMWGICZ);
+
+       /* Gunit-Display CZ domain, 0x182028-0x1821CF */
+       s->gu_ctl0              = I915_READ(VLV_GU_CTL0);
+       s->gu_ctl1              = I915_READ(VLV_GU_CTL1);
+       s->clock_gate_dis2      = I915_READ(VLV_GUNIT_CLOCK_GATE2);
+
+       /*
+        * Not saving any of:
+        * DFT,         0x9800-0x9EC0
+        * SARB,        0xB000-0xB1FC
+        * GAC,         0x5208-0x524C, 0x14000-0x14C000
+        * PCI CFG
+        */
+}
+
+static void vlv_restore_gunit_s0ix_state(struct drm_i915_private *dev_priv)
+{
+       struct vlv_s0ix_state *s = &dev_priv->vlv_s0ix_state;
+       u32 val;
+       int i;
+
+       /* GAM 0x4000-0x4770 */
+       I915_WRITE(GEN7_WR_WATERMARK,   s->wr_watermark);
+       I915_WRITE(GEN7_GFX_PRIO_CTRL,  s->gfx_prio_ctrl);
+       I915_WRITE(ARB_MODE,            s->arb_mode | (0xffff << 16));
+       I915_WRITE(GEN7_GFX_PEND_TLB0,  s->gfx_pend_tlb0);
+       I915_WRITE(GEN7_GFX_PEND_TLB1,  s->gfx_pend_tlb1);
+
+       for (i = 0; i < ARRAY_SIZE(s->lra_limits); i++)
+               I915_WRITE(GEN7_LRA_LIMITS_BASE + i * 4, s->lra_limits[i]);
+
+       I915_WRITE(GEN7_MEDIA_MAX_REQ_COUNT, s->media_max_req_count);
+       I915_WRITE(GEN7_MEDIA_MAX_REQ_COUNT, s->gfx_max_req_count);
+
+       I915_WRITE(RENDER_HWS_PGA_GEN7, s->render_hwsp);
+       I915_WRITE(GAM_ECOCHK,          s->ecochk);
+       I915_WRITE(BSD_HWS_PGA_GEN7,    s->bsd_hwsp);
+       I915_WRITE(BLT_HWS_PGA_GEN7,    s->blt_hwsp);
+
+       I915_WRITE(GEN7_TLB_RD_ADDR,    s->tlb_rd_addr);
+
+       /* MBC 0x9024-0x91D0, 0x8500 */
+       I915_WRITE(VLV_G3DCTL,          s->g3dctl);
+       I915_WRITE(VLV_GSCKGCTL,        s->gsckgctl);
+       I915_WRITE(GEN6_MBCTL,          s->mbctl);
+
+       /* GCP 0x9400-0x9424, 0x8100-0x810C */
+       I915_WRITE(GEN6_UCGCTL1,        s->ucgctl1);
+       I915_WRITE(GEN6_UCGCTL3,        s->ucgctl3);
+       I915_WRITE(GEN6_RCGCTL1,        s->rcgctl1);
+       I915_WRITE(GEN6_RCGCTL2,        s->rcgctl2);
+       I915_WRITE(GEN6_RSTCTL,         s->rstctl);
+       I915_WRITE(GEN7_MISCCPCTL,      s->misccpctl);
+
+       /* GPM 0xA000-0xAA84, 0x8000-0x80FC */
+       I915_WRITE(GEN6_GFXPAUSE,       s->gfxpause);
+       I915_WRITE(GEN6_RPDEUHWTC,      s->rpdeuhwtc);
+       I915_WRITE(GEN6_RPDEUC,         s->rpdeuc);
+       I915_WRITE(ECOBUS,              s->ecobus);
+       I915_WRITE(VLV_PWRDWNUPCTL,     s->pwrdwnupctl);
+       I915_WRITE(GEN6_RP_DOWN_TIMEOUT,s->rp_down_timeout);
+       I915_WRITE(GEN6_RPDEUCSW,       s->rp_deucsw);
+       I915_WRITE(GEN6_RCUBMABDTMR,    s->rcubmabdtmr);
+       I915_WRITE(VLV_RCEDATA,         s->rcedata);
+       I915_WRITE(VLV_SPAREG2H,        s->spare2gh);
+
+       /* Display CZ domain, 0x4400C-0x4402C, 0x4F000-0x4F11F */
+       I915_WRITE(GTIMR,               s->gt_imr);
+       I915_WRITE(GTIER,               s->gt_ier);
+       I915_WRITE(GEN6_PMIMR,          s->pm_imr);
+       I915_WRITE(GEN6_PMIER,          s->pm_ier);
+
+       for (i = 0; i < ARRAY_SIZE(s->gt_scratch); i++)
+               I915_WRITE(GEN7_GT_SCRATCH_BASE + i * 4, s->gt_scratch[i]);
+
+       /* GT SA CZ domain, 0x100000-0x138124 */
+       I915_WRITE(TILECTL,                     s->tilectl);
+       I915_WRITE(GTFIFOCTL,                   s->gt_fifoctl);
+       /*
+        * Preserve the GT allow wake and GFX force clock bit, they are not
+        * be restored, as they are used to control the s0ix suspend/resume
+        * sequence by the caller.
+        */
+       val = I915_READ(VLV_GTLC_WAKE_CTRL);
+       val &= VLV_GTLC_ALLOWWAKEREQ;
+       val |= s->gtlc_wake_ctrl & ~VLV_GTLC_ALLOWWAKEREQ;
+       I915_WRITE(VLV_GTLC_WAKE_CTRL, val);
+
+       val = I915_READ(VLV_GTLC_SURVIVABILITY_REG);
+       val &= VLV_GFX_CLK_FORCE_ON_BIT;
+       val |= s->gtlc_survive & ~VLV_GFX_CLK_FORCE_ON_BIT;
+       I915_WRITE(VLV_GTLC_SURVIVABILITY_REG, val);
+
+       I915_WRITE(VLV_PMWGICZ,                 s->pmwgicz);
+
+       /* Gunit-Display CZ domain, 0x182028-0x1821CF */
+       I915_WRITE(VLV_GU_CTL0,                 s->gu_ctl0);
+       I915_WRITE(VLV_GU_CTL1,                 s->gu_ctl1);
+       I915_WRITE(VLV_GUNIT_CLOCK_GATE2,       s->clock_gate_dis2);
+}
+
+int vlv_force_gfx_clock(struct drm_i915_private *dev_priv, bool force_on)
+{
+       u32 val;
+       int err;
+
+       val = I915_READ(VLV_GTLC_SURVIVABILITY_REG);
+       WARN_ON(!!(val & VLV_GFX_CLK_FORCE_ON_BIT) == force_on);
+
+#define COND (I915_READ(VLV_GTLC_SURVIVABILITY_REG) & VLV_GFX_CLK_STATUS_BIT)
+       /* Wait for a previous force-off to settle */
+       if (force_on) {
+               err = wait_for(!COND, 20);
+               if (err) {
+                       DRM_ERROR("timeout waiting for GFX clock force-off (%08x)\n",
+                                 I915_READ(VLV_GTLC_SURVIVABILITY_REG));
+                       return err;
+               }
+       }
+
+       val = I915_READ(VLV_GTLC_SURVIVABILITY_REG);
+       val &= ~VLV_GFX_CLK_FORCE_ON_BIT;
+       if (force_on)
+               val |= VLV_GFX_CLK_FORCE_ON_BIT;
+       I915_WRITE(VLV_GTLC_SURVIVABILITY_REG, val);
+
+       if (!force_on)
+               return 0;
+
+       err = wait_for(COND, 20);
+       if (err)
+               DRM_ERROR("timeout waiting for GFX clock force-on (%08x)\n",
+                         I915_READ(VLV_GTLC_SURVIVABILITY_REG));
+
+       return err;
+#undef COND
+}
+
+static int vlv_allow_gt_wake(struct drm_i915_private *dev_priv, bool allow)
+{
+       u32 val;
+       int err = 0;
+
+       val = I915_READ(VLV_GTLC_WAKE_CTRL);
+       val &= ~VLV_GTLC_ALLOWWAKEREQ;
+       if (allow)
+               val |= VLV_GTLC_ALLOWWAKEREQ;
+       I915_WRITE(VLV_GTLC_WAKE_CTRL, val);
+       POSTING_READ(VLV_GTLC_WAKE_CTRL);
+
+#define COND (!!(I915_READ(VLV_GTLC_PW_STATUS) & VLV_GTLC_ALLOWWAKEACK) == \
+             allow)
+       err = wait_for(COND, 1);
+       if (err)
+               DRM_ERROR("timeout disabling GT waking\n");
+       return err;
+#undef COND
+}
+
+static int vlv_wait_for_gt_wells(struct drm_i915_private *dev_priv,
+                                bool wait_for_on)
+{
+       u32 mask;
+       u32 val;
+       int err;
+
+       mask = VLV_GTLC_PW_MEDIA_STATUS_MASK | VLV_GTLC_PW_RENDER_STATUS_MASK;
+       val = wait_for_on ? mask : 0;
+#define COND ((I915_READ(VLV_GTLC_PW_STATUS) & mask) == val)
+       if (COND)
+               return 0;
+
+       DRM_DEBUG_KMS("waiting for GT wells to go %s (%08x)\n",
+                       wait_for_on ? "on" : "off",
+                       I915_READ(VLV_GTLC_PW_STATUS));
+
+       /*
+        * RC6 transitioning can be delayed up to 2 msec (see
+        * valleyview_enable_rps), use 3 msec for safety.
+        */
+       err = wait_for(COND, 3);
+       if (err)
+               DRM_ERROR("timeout waiting for GT wells to go %s\n",
+                         wait_for_on ? "on" : "off");
+
+       return err;
+#undef COND
+}
+
+static void vlv_check_no_gt_access(struct drm_i915_private *dev_priv)
+{
+       if (!(I915_READ(VLV_GTLC_PW_STATUS) & VLV_GTLC_ALLOWWAKEERR))
+               return;
+
+       DRM_ERROR("GT register access while GT waking disabled\n");
+       I915_WRITE(VLV_GTLC_PW_STATUS, VLV_GTLC_ALLOWWAKEERR);
+}
+
+static int vlv_runtime_suspend(struct drm_i915_private *dev_priv)
+{
+       u32 mask;
+       int err;
+
+       /*
+        * Bspec defines the following GT well on flags as debug only, so
+        * don't treat them as hard failures.
+        */
+       (void)vlv_wait_for_gt_wells(dev_priv, false);
+
+       mask = VLV_GTLC_RENDER_CTX_EXISTS | VLV_GTLC_MEDIA_CTX_EXISTS;
+       WARN_ON((I915_READ(VLV_GTLC_WAKE_CTRL) & mask) != mask);
+
+       vlv_check_no_gt_access(dev_priv);
+
+       err = vlv_force_gfx_clock(dev_priv, true);
+       if (err)
+               goto err1;
+
+       err = vlv_allow_gt_wake(dev_priv, false);
+       if (err)
+               goto err2;
+       vlv_save_gunit_s0ix_state(dev_priv);
+
+       err = vlv_force_gfx_clock(dev_priv, false);
+       if (err)
+               goto err2;
+
+       return 0;
+
+err2:
+       /* For safety always re-enable waking and disable gfx clock forcing */
+       vlv_allow_gt_wake(dev_priv, true);
+err1:
+       vlv_force_gfx_clock(dev_priv, false);
+
+       return err;
+}
+
+static int vlv_runtime_resume(struct drm_i915_private *dev_priv)
+{
+       struct drm_device *dev = dev_priv->dev;
+       int err;
+       int ret;
+
+       /*
+        * If any of the steps fail just try to continue, that's the best we
+        * can do at this point. Return the first error code (which will also
+        * leave RPM permanently disabled).
+        */
+       ret = vlv_force_gfx_clock(dev_priv, true);
+
+       vlv_restore_gunit_s0ix_state(dev_priv);
+
+       err = vlv_allow_gt_wake(dev_priv, true);
+       if (!ret)
+               ret = err;
+
+       err = vlv_force_gfx_clock(dev_priv, false);
+       if (!ret)
+               ret = err;
+
+       vlv_check_no_gt_access(dev_priv);
+
+       intel_init_clock_gating(dev);
+       i915_gem_restore_fences(dev);
+
+       return ret;
+}
+
+static int intel_runtime_suspend(struct device *device)
 {
        struct pci_dev *pdev = to_pci_dev(device);
        struct drm_device *dev = pci_get_drvdata(pdev);
        struct drm_i915_private *dev_priv = dev->dev_private;
+       int ret;
+
+       if (WARN_ON_ONCE(!(dev_priv->rps.enabled && intel_enable_rc6(dev))))
+               return -ENODEV;
 
        WARN_ON(!HAS_RUNTIME_PM(dev));
        assert_force_wake_inactive(dev_priv);
 
        DRM_DEBUG_KMS("Suspending device\n");
 
-       if (HAS_PC8(dev))
-               hsw_enable_pc8(dev_priv);
+       /*
+        * We could deadlock here in case another thread holding struct_mutex
+        * calls RPM suspend concurrently, since the RPM suspend will wait
+        * first for this RPM suspend to finish. In this case the concurrent
+        * RPM resume will be followed by its RPM suspend counterpart. Still
+        * for consistency return -EAGAIN, which will reschedule this suspend.
+        */
+       if (!mutex_trylock(&dev->struct_mutex)) {
+               DRM_DEBUG_KMS("device lock contention, deffering suspend\n");
+               /*
+                * Bump the expiration timestamp, otherwise the suspend won't
+                * be rescheduled.
+                */
+               pm_runtime_mark_last_busy(device);
 
+               return -EAGAIN;
+       }
+       /*
+        * We are safe here against re-faults, since the fault handler takes
+        * an RPM reference.
+        */
        i915_gem_release_all_mmaps(dev_priv);
+       mutex_unlock(&dev->struct_mutex);
+
+       /*
+        * rps.work can't be rearmed here, since we get here only after making
+        * sure the GPU is idle and the RPS freq is set to the minimum. See
+        * intel_mark_idle().
+        */
+       cancel_work_sync(&dev_priv->rps.work);
+       intel_runtime_pm_disable_interrupts(dev);
+
+       if (IS_GEN6(dev)) {
+               ret = 0;
+       } else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
+               ret = hsw_runtime_suspend(dev_priv);
+       } else if (IS_VALLEYVIEW(dev)) {
+               ret = vlv_runtime_suspend(dev_priv);
+       } else {
+               ret = -ENODEV;
+               WARN_ON(1);
+       }
+
+       if (ret) {
+               DRM_ERROR("Runtime suspend failed, disabling it (%d)\n", ret);
+               intel_runtime_pm_restore_interrupts(dev);
+
+               return ret;
+       }
 
        del_timer_sync(&dev_priv->gpu_error.hangcheck_timer);
        dev_priv->pm.suspended = true;
@@ -923,11 +1423,12 @@ static int i915_runtime_suspend(struct device *device)
        return 0;
 }
 
-static int i915_runtime_resume(struct device *device)
+static int intel_runtime_resume(struct device *device)
 {
        struct pci_dev *pdev = to_pci_dev(device);
        struct drm_device *dev = pci_get_drvdata(pdev);
        struct drm_i915_private *dev_priv = dev->dev_private;
+       int ret;
 
        WARN_ON(!HAS_RUNTIME_PM(dev));
 
@@ -936,11 +1437,33 @@ static int i915_runtime_resume(struct device *device)
        intel_opregion_notify_adapter(dev, PCI_D0);
        dev_priv->pm.suspended = false;
 
-       if (HAS_PC8(dev))
-               hsw_disable_pc8(dev_priv);
+       if (IS_GEN6(dev)) {
+               ret = snb_runtime_resume(dev_priv);
+       } else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
+               ret = hsw_runtime_resume(dev_priv);
+       } else if (IS_VALLEYVIEW(dev)) {
+               ret = vlv_runtime_resume(dev_priv);
+       } else {
+               WARN_ON(1);
+               ret = -ENODEV;
+       }
 
-       DRM_DEBUG_KMS("Device resumed\n");
-       return 0;
+       /*
+        * No point of rolling back things in case of an error, as the best
+        * we can do is to hope that things will still work (and disable RPM).
+        */
+       i915_gem_init_swizzling(dev);
+       gen6_update_ring_freq(dev);
+
+       intel_runtime_pm_restore_interrupts(dev);
+       intel_reset_gt_powersave(dev);
+
+       if (ret)
+               DRM_ERROR("Runtime resume failed, disabling it (%d)\n", ret);
+       else
+               DRM_DEBUG_KMS("Device resumed\n");
+
+       return ret;
 }
 
 static const struct dev_pm_ops i915_pm_ops = {
@@ -954,8 +1477,8 @@ static const struct dev_pm_ops i915_pm_ops = {
        .poweroff = i915_pm_poweroff,
        .restore_early = i915_pm_resume_early,
        .restore = i915_pm_resume,
-       .runtime_suspend = i915_runtime_suspend,
-       .runtime_resume = i915_runtime_resume,
+       .runtime_suspend = intel_runtime_suspend,
+       .runtime_resume = intel_runtime_resume,
 };
 
 static const struct vm_operations_struct i915_gem_vm_ops = {
@@ -1062,6 +1585,7 @@ static int __init i915_init(void)
                driver.get_vblank_timestamp = NULL;
 #ifndef CONFIG_DRM_I915_UMS
                /* Silently fail loading to not upset userspace. */
+               DRM_DEBUG_DRIVER("KMS and UMS disabled.\n");
                return 0;
 #endif
        }
index 388c028e223ccbcad3bf1c15d8bbd1f09be3719d..49414d30e8d42d3068cbd21990f1403e3e8b1f5a 100644 (file)
 #include "i915_reg.h"
 #include "intel_bios.h"
 #include "intel_ringbuffer.h"
+#include "i915_gem_gtt.h"
 #include <linux/io-mapping.h>
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
 #include <drm/intel-gtt.h>
 #include <linux/backlight.h>
+#include <linux/hashtable.h>
 #include <linux/intel-iommu.h>
 #include <linux/kref.h>
 #include <linux/pm_qos.h>
@@ -91,7 +93,7 @@ enum port {
 };
 #define port_name(p) ((p) + 'A')
 
-#define I915_NUM_PHYS_VLV 1
+#define I915_NUM_PHYS_VLV 2
 
 enum dpio_channel {
        DPIO_CH0,
@@ -162,6 +164,12 @@ enum hpd_pin {
 #define for_each_pipe(p) for ((p) = 0; (p) < INTEL_INFO(dev)->num_pipes; (p)++)
 #define for_each_sprite(p, s) for ((s) = 0; (s) < INTEL_INFO(dev)->num_sprites[(p)]; (s)++)
 
+#define for_each_crtc(dev, crtc) \
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+
+#define for_each_intel_crtc(dev, intel_crtc) \
+       list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head)
+
 #define for_each_encoder_on_crtc(dev, __crtc, intel_encoder) \
        list_for_each_entry((intel_encoder), &(dev)->mode_config.encoder_list, base.head) \
                if ((intel_encoder)->base.crtc == (__crtc))
@@ -171,6 +179,7 @@ enum hpd_pin {
                if ((intel_connector)->base.encoder == (__encoder))
 
 struct drm_i915_private;
+struct i915_mmu_object;
 
 enum intel_dpll_id {
        DPLL_ID_PRIVATE = -1, /* non-shared dpll in use */
@@ -312,7 +321,6 @@ struct drm_i915_error_state {
        u32 gab_ctl;
        u32 gfx_mode;
        u32 extra_instdone[I915_NUM_INSTDONE_REG];
-       u32 pipestat[I915_MAX_PIPES];
        u64 fence[I915_MAX_NUM_FENCES];
        struct intel_overlay_error_state *overlay;
        struct intel_display_error_state *display;
@@ -346,7 +354,7 @@ struct drm_i915_error_state {
                u64 bbaddr;
                u64 acthd;
                u32 fault_reg;
-               u32 faddr;
+               u64 faddr;
                u32 rc_psmi; /* sleep state */
                u32 semaphore_mboxes[I915_NUM_RINGS - 1];
 
@@ -385,6 +393,7 @@ struct drm_i915_error_state {
                u32 tiling:2;
                u32 dirty:1;
                u32 purgeable:1;
+               u32 userptr:1;
                s32 ring:4;
                u32 cache_level:3;
        } **active_bo, **pinned_bo;
@@ -449,10 +458,11 @@ struct drm_i915_display_funcs {
        int (*queue_flip)(struct drm_device *dev, struct drm_crtc *crtc,
                          struct drm_framebuffer *fb,
                          struct drm_i915_gem_object *obj,
+                         struct intel_engine_cs *ring,
                          uint32_t flags);
-       int (*update_primary_plane)(struct drm_crtc *crtc,
-                                   struct drm_framebuffer *fb,
-                                   int x, int y);
+       void (*update_primary_plane)(struct drm_crtc *crtc,
+                                    struct drm_framebuffer *fb,
+                                    int x, int y);
        void (*hpd_irq_setup)(struct drm_device *dev);
        /* clock updates for mode set */
        /* cursor updates */
@@ -545,6 +555,7 @@ struct intel_device_info {
        int dpll_offsets[I915_MAX_PIPES];
        int dpll_md_offsets[I915_MAX_PIPES];
        int palette_offsets[I915_MAX_PIPES];
+       int cursor_offsets[I915_MAX_PIPES];
 };
 
 #undef DEFINE_FLAG
@@ -560,168 +571,6 @@ enum i915_cache_level {
        I915_CACHE_WT, /* hsw:gt3e WriteThrough for scanouts */
 };
 
-typedef uint32_t gen6_gtt_pte_t;
-
-/**
- * A VMA represents a GEM BO that is bound into an address space. Therefore, a
- * VMA's presence cannot be guaranteed before binding, or after unbinding the
- * object into/from the address space.
- *
- * To make things as simple as possible (ie. no refcounting), a VMA's lifetime
- * will always be <= an objects lifetime. So object refcounting should cover us.
- */
-struct i915_vma {
-       struct drm_mm_node node;
-       struct drm_i915_gem_object *obj;
-       struct i915_address_space *vm;
-
-       /** This object's place on the active/inactive lists */
-       struct list_head mm_list;
-
-       struct list_head vma_link; /* Link in the object's VMA list */
-
-       /** This vma's place in the batchbuffer or on the eviction list */
-       struct list_head exec_list;
-
-       /**
-        * Used for performing relocations during execbuffer insertion.
-        */
-       struct hlist_node exec_node;
-       unsigned long exec_handle;
-       struct drm_i915_gem_exec_object2 *exec_entry;
-
-       /**
-        * How many users have pinned this object in GTT space. The following
-        * users can each hold at most one reference: pwrite/pread, pin_ioctl
-        * (via user_pin_count), execbuffer (objects are not allowed multiple
-        * times for the same batchbuffer), and the framebuffer code. When
-        * switching/pageflipping, the framebuffer code has at most two buffers
-        * pinned per crtc.
-        *
-        * In the worst case this is 1 + 1 + 1 + 2*2 = 7. That would fit into 3
-        * bits with absolutely no headroom. So use 4 bits. */
-       unsigned int pin_count:4;
-#define DRM_I915_GEM_OBJECT_MAX_PIN_COUNT 0xf
-
-       /** Unmap an object from an address space. This usually consists of
-        * setting the valid PTE entries to a reserved scratch page. */
-       void (*unbind_vma)(struct i915_vma *vma);
-       /* Map an object into an address space with the given cache flags. */
-#define GLOBAL_BIND (1<<0)
-       void (*bind_vma)(struct i915_vma *vma,
-                        enum i915_cache_level cache_level,
-                        u32 flags);
-};
-
-struct i915_address_space {
-       struct drm_mm mm;
-       struct drm_device *dev;
-       struct list_head global_link;
-       unsigned long start;            /* Start offset always 0 for dri2 */
-       size_t total;           /* size addr space maps (ex. 2GB for ggtt) */
-
-       struct {
-               dma_addr_t addr;
-               struct page *page;
-       } scratch;
-
-       /**
-        * List of objects currently involved in rendering.
-        *
-        * Includes buffers having the contents of their GPU caches
-        * flushed, not necessarily primitives.  last_rendering_seqno
-        * represents when the rendering involved will be completed.
-        *
-        * A reference is held on the buffer while on this list.
-        */
-       struct list_head active_list;
-
-       /**
-        * LRU list of objects which are not in the ringbuffer and
-        * are ready to unbind, but are still in the GTT.
-        *
-        * last_rendering_seqno is 0 while an object is in this list.
-        *
-        * A reference is not held on the buffer while on this list,
-        * as merely being GTT-bound shouldn't prevent its being
-        * freed, and we'll pull it off the list in the free path.
-        */
-       struct list_head inactive_list;
-
-       /* FIXME: Need a more generic return type */
-       gen6_gtt_pte_t (*pte_encode)(dma_addr_t addr,
-                                    enum i915_cache_level level,
-                                    bool valid); /* Create a valid PTE */
-       void (*clear_range)(struct i915_address_space *vm,
-                           uint64_t start,
-                           uint64_t length,
-                           bool use_scratch);
-       void (*insert_entries)(struct i915_address_space *vm,
-                              struct sg_table *st,
-                              uint64_t start,
-                              enum i915_cache_level cache_level);
-       void (*cleanup)(struct i915_address_space *vm);
-};
-
-/* 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
- * portion of the GTT which can be mapped by the CPU and remain both coherent
- * and correct (in cases like swizzling). That region is referred to as GMADR in
- * the spec.
- */
-struct i915_gtt {
-       struct i915_address_space base;
-       size_t stolen_size;             /* Total size of stolen memory */
-
-       unsigned long mappable_end;     /* End offset that we can CPU map */
-       struct io_mapping *mappable;    /* Mapping to our CPU mappable region */
-       phys_addr_t mappable_base;      /* PA of our GMADR */
-
-       /** "Graphics Stolen Memory" holds the global PTEs */
-       void __iomem *gsm;
-
-       bool do_idle_maps;
-
-       int mtrr;
-
-       /* global gtt ops */
-       int (*gtt_probe)(struct drm_device *dev, size_t *gtt_total,
-                         size_t *stolen, phys_addr_t *mappable_base,
-                         unsigned long *mappable_end);
-};
-#define gtt_total_entries(gtt) ((gtt).base.total >> PAGE_SHIFT)
-
-#define GEN8_LEGACY_PDPS 4
-struct i915_hw_ppgtt {
-       struct i915_address_space base;
-       struct kref ref;
-       struct drm_mm_node node;
-       unsigned num_pd_entries;
-       unsigned num_pd_pages; /* gen8+ */
-       union {
-               struct page **pt_pages;
-               struct page **gen8_pt_pages[GEN8_LEGACY_PDPS];
-       };
-       struct page *pd_pages;
-       union {
-               uint32_t pd_offset;
-               dma_addr_t pd_dma_addr[GEN8_LEGACY_PDPS];
-       };
-       union {
-               dma_addr_t *pt_dma_addr;
-               dma_addr_t *gen8_pt_dma_addr[4];
-       };
-
-       struct i915_hw_context *ctx;
-
-       int (*enable)(struct i915_hw_ppgtt *ppgtt);
-       int (*switch_mm)(struct i915_hw_ppgtt *ppgtt,
-                        struct intel_ring_buffer *ring,
-                        bool synchronous);
-       void (*debug_dump)(struct i915_hw_ppgtt *ppgtt, struct seq_file *m);
-};
-
 struct i915_ctx_hang_stats {
        /* This context had batch pending when hang was declared */
        unsigned batch_pending;
@@ -738,13 +587,13 @@ struct i915_ctx_hang_stats {
 
 /* This must match up with the value previously used for execbuf2.rsvd1. */
 #define DEFAULT_CONTEXT_ID 0
-struct i915_hw_context {
+struct intel_context {
        struct kref ref;
        int id;
        bool is_initialized;
        uint8_t remap_slice;
        struct drm_i915_file_private *file_priv;
-       struct intel_ring_buffer *last_ring;
+       struct intel_engine_cs *last_ring;
        struct drm_i915_gem_object *obj;
        struct i915_ctx_hang_stats hang_stats;
        struct i915_address_space *vm;
@@ -782,6 +631,10 @@ struct i915_fbc {
        } no_fbc_reason;
 };
 
+struct i915_drrs {
+       struct intel_connector *connector;
+};
+
 struct i915_psr {
        bool sink_support;
        bool source_ok;
@@ -965,6 +818,67 @@ struct i915_suspend_saved_registers {
        u32 savePCH_PORT_HOTPLUG;
 };
 
+struct vlv_s0ix_state {
+       /* GAM */
+       u32 wr_watermark;
+       u32 gfx_prio_ctrl;
+       u32 arb_mode;
+       u32 gfx_pend_tlb0;
+       u32 gfx_pend_tlb1;
+       u32 lra_limits[GEN7_LRA_LIMITS_REG_NUM];
+       u32 media_max_req_count;
+       u32 gfx_max_req_count;
+       u32 render_hwsp;
+       u32 ecochk;
+       u32 bsd_hwsp;
+       u32 blt_hwsp;
+       u32 tlb_rd_addr;
+
+       /* MBC */
+       u32 g3dctl;
+       u32 gsckgctl;
+       u32 mbctl;
+
+       /* GCP */
+       u32 ucgctl1;
+       u32 ucgctl3;
+       u32 rcgctl1;
+       u32 rcgctl2;
+       u32 rstctl;
+       u32 misccpctl;
+
+       /* GPM */
+       u32 gfxpause;
+       u32 rpdeuhwtc;
+       u32 rpdeuc;
+       u32 ecobus;
+       u32 pwrdwnupctl;
+       u32 rp_down_timeout;
+       u32 rp_deucsw;
+       u32 rcubmabdtmr;
+       u32 rcedata;
+       u32 spare2gh;
+
+       /* Display 1 CZ domain */
+       u32 gt_imr;
+       u32 gt_ier;
+       u32 pm_imr;
+       u32 pm_ier;
+       u32 gt_scratch[GEN7_GT_SCRATCH_REG_NUM];
+
+       /* GT SA CZ domain */
+       u32 tilectl;
+       u32 gt_fifoctl;
+       u32 gtlc_wake_ctrl;
+       u32 gtlc_survive;
+       u32 pmwgicz;
+
+       /* Display 2 CZ domain */
+       u32 gu_ctl0;
+       u32 gu_ctl1;
+       u32 clock_gate_dis2;
+};
+
 struct intel_gen6_power_mgmt {
        /* work and pm_iir are protected by dev_priv->irq_lock */
        struct work_struct work;
@@ -1074,6 +988,7 @@ struct i915_power_domains {
         * time are on. They are kept on until after the first modeset.
         */
        bool init_power_on;
+       bool initializing;
        int power_well_count;
 
        struct mutex lock;
@@ -1132,7 +1047,8 @@ struct i915_gem_mm {
        /** PPGTT used for aliasing the PPGTT with the GTT */
        struct i915_hw_ppgtt *aliasing_ppgtt;
 
-       struct shrinker inactive_shrinker;
+       struct notifier_block oom_notifier;
+       struct shrinker shrinker;
        bool shrinker_no_lock_stealing;
 
        /** LRU list of objects with fence regs on them. */
@@ -1170,6 +1086,9 @@ struct i915_gem_mm {
         */
        bool busy;
 
+       /* the indicator for dispatch video commands on two BSD rings */
+       int bsd_ring_dispatch_index;
+
        /** Bit 6 swizzling required for X tiling */
        uint32_t bit_6_swizzle_x;
        /** Bit 6 swizzling required for Y tiling */
@@ -1245,8 +1164,12 @@ struct i915_gpu_error {
         */
        wait_queue_head_t reset_queue;
 
-       /* For gpu hang simulation. */
-       unsigned int stop_rings;
+       /* Userspace knobs for gpu hang simulation;
+        * combines both a ring mask, and extra flags
+        */
+       u32 stop_rings;
+#define I915_STOP_RING_ALLOW_BAN       (1 << 31)
+#define I915_STOP_RING_ALLOW_WARN      (1 << 30)
 
        /* For missed irq/seqno simulation. */
        unsigned int test_irq_rings;
@@ -1266,6 +1189,12 @@ struct ddi_vbt_port_info {
        uint8_t supports_dp:1;
 };
 
+enum drrs_support_type {
+       DRRS_NOT_SUPPORTED = 0,
+       STATIC_DRRS_SUPPORT = 1,
+       SEAMLESS_DRRS_SUPPORT = 2
+};
+
 struct intel_vbt_data {
        struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */
        struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */
@@ -1278,9 +1207,12 @@ struct intel_vbt_data {
        unsigned int lvds_use_ssc:1;
        unsigned int display_clock_mode:1;
        unsigned int fdi_rx_polarity_inverted:1;
+       unsigned int has_mipi:1;
        int lvds_ssc_freq;
        unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */
 
+       enum drrs_support_type drrs_type;
+
        /* eDP */
        int edp_rate;
        int edp_lanes;
@@ -1299,7 +1231,14 @@ struct intel_vbt_data {
 
        /* MIPI DSI */
        struct {
+               u16 port;
                u16 panel_id;
+               struct mipi_config *config;
+               struct mipi_pps_data *pps;
+               u8 seq_version;
+               u32 size;
+               u8 *data;
+               u8 *sequence[MIPI_SEQ_MAX];
        } dsi;
 
        int crt_ddc_pin;
@@ -1351,23 +1290,13 @@ struct ilk_wm_values {
  * goes back to false exactly before we reenable the IRQs. We use this variable
  * to check if someone is trying to enable/disable IRQs while they're supposed
  * to be disabled. This shouldn't happen and we'll print some error messages in
- * case it happens, but if it actually happens we'll also update the variables
- * inside struct regsave so when we restore the IRQs they will contain the
- * latest expected values.
+ * case it happens.
  *
  * For more, read the Documentation/power/runtime_pm.txt.
  */
 struct i915_runtime_pm {
        bool suspended;
        bool irqs_disabled;
-
-       struct {
-               uint32_t deimr;
-               uint32_t sdeimr;
-               uint32_t gtimr;
-               uint32_t gtier;
-               uint32_t gen6_pmimr;
-       } regsave;
 };
 
 enum intel_pipe_crc_source {
@@ -1400,7 +1329,7 @@ struct intel_pipe_crc {
        wait_queue_head_t wq;
 };
 
-typedef struct drm_i915_private {
+struct drm_i915_private {
        struct drm_device *dev;
        struct kmem_cache *slab;
 
@@ -1424,10 +1353,13 @@ typedef struct drm_i915_private {
         */
        uint32_t gpio_mmio_base;
 
+       /* MMIO base address for MIPI regs */
+       uint32_t mipi_mmio_base;
+
        wait_queue_head_t gmbus_wait_queue;
 
        struct pci_dev *bridge_dev;
-       struct intel_ring_buffer ring[I915_NUM_RINGS];
+       struct intel_engine_cs ring[I915_NUM_RINGS];
        uint32_t last_seqno, next_seqno;
 
        drm_dma_handle_t *status_page_dmah;
@@ -1469,6 +1401,7 @@ typedef struct drm_i915_private {
        struct timer_list hotplug_reenable_timer;
 
        struct i915_fbc fbc;
+       struct i915_drrs drrs;
        struct intel_opregion opregion;
        struct intel_vbt_data vbt;
 
@@ -1486,6 +1419,7 @@ typedef struct drm_i915_private {
        int num_fence_regs; /* 8 on pre-965, 16 otherwise */
 
        unsigned int fsb_freq, mem_freq, is_ddr3;
+       unsigned int vlv_cdclk_freq;
 
        /**
         * wq - Driver workqueue for GEM.
@@ -1509,9 +1443,12 @@ typedef struct drm_i915_private {
        struct mutex modeset_restore_lock;
 
        struct list_head vm_list; /* Global list of all address spaces */
-       struct i915_gtt gtt; /* VMA representing the global address space */
+       struct i915_gtt gtt; /* VM representing the global address space */
 
        struct i915_gem_mm mm;
+#if defined(CONFIG_MMU_NOTIFIER)
+       DECLARE_HASHTABLE(mmu_notifiers, 7);
+#endif
 
        /* Kernel Modesetting */
 
@@ -1580,6 +1517,7 @@ typedef struct drm_i915_private {
 
        u32 suspend_count;
        struct i915_suspend_saved_registers regfile;
+       struct vlv_s0ix_state vlv_s0ix_state;
 
        struct {
                /*
@@ -1605,7 +1543,12 @@ typedef struct drm_i915_private {
        struct i915_dri1_state dri1;
        /* Old ums support infrastructure, same warning applies. */
        struct i915_ums_state ums;
-} drm_i915_private_t;
+
+       /*
+        * NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch
+        * will be rejected. Instead look for a better place.
+        */
+};
 
 static inline struct drm_i915_private *to_i915(const struct drm_device *dev)
 {
@@ -1642,6 +1585,8 @@ struct drm_i915_gem_object_ops {
         */
        int (*get_pages)(struct drm_i915_gem_object *);
        void (*put_pages)(struct drm_i915_gem_object *);
+       int (*dmabuf_export)(struct drm_i915_gem_object *);
+       void (*release)(struct drm_i915_gem_object *);
 };
 
 struct drm_i915_gem_object {
@@ -1732,7 +1677,7 @@ struct drm_i915_gem_object {
        void *dma_buf_vmapping;
        int vmapping_count;
 
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
 
        /** Breadcrumb of last rendering to the buffer. */
        uint32_t last_read_seqno;
@@ -1755,8 +1700,20 @@ struct drm_i915_gem_object {
 
        /** for phy allocated objects */
        drm_dma_handle_t *phys_handle;
-};
 
+       union {
+               struct i915_gem_userptr {
+                       uintptr_t ptr;
+                       unsigned read_only :1;
+                       unsigned workers :4;
+#define I915_GEM_USERPTR_MAX_WORKERS 15
+
+                       struct mm_struct *mm;
+                       struct i915_mmu_object *mn;
+                       struct work_struct *work;
+               } userptr;
+       };
+};
 #define to_intel_bo(x) container_of(x, struct drm_i915_gem_object, base)
 
 /**
@@ -1771,7 +1728,7 @@ struct drm_i915_gem_object {
  */
 struct drm_i915_gem_request {
        /** On Which ring this request was generated */
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
 
        /** GEM sequence number associated with this request. */
        uint32_t seqno;
@@ -1783,7 +1740,7 @@ struct drm_i915_gem_request {
        u32 tail;
 
        /** Context related to this request */
-       struct i915_hw_context *ctx;
+       struct intel_context *ctx;
 
        /** Batch buffer related to this request if any */
        struct drm_i915_gem_object *batch_obj;
@@ -1810,8 +1767,8 @@ struct drm_i915_file_private {
        } mm;
        struct idr context_idr;
 
-       struct i915_hw_context *private_default_ctx;
        atomic_t rps_wait_boost;
+       struct  intel_engine_cs *bsd_ring;
 };
 
 /*
@@ -1879,11 +1836,17 @@ struct drm_i915_cmd_descriptor {
         * the expected value, the parser rejects it. Only valid if flags has
         * the CMD_DESC_BITMASK bit set. Only entries where mask is non-zero
         * are valid.
+        *
+        * If the check specifies a non-zero condition_mask then the parser
+        * only performs the check when the bits specified by condition_mask
+        * are non-zero.
         */
        struct {
                u32 offset;
                u32 mask;
                u32 expected;
+               u32 condition_offset;
+               u32 condition_mask;
        } bits[MAX_CMD_DESC_BITMASKS];
 };
 
@@ -1925,8 +1888,9 @@ struct drm_i915_cmd_table {
                                 (dev)->pdev->device == 0x0106 || \
                                 (dev)->pdev->device == 0x010A)
 #define IS_VALLEYVIEW(dev)     (INTEL_INFO(dev)->is_valleyview)
+#define IS_CHERRYVIEW(dev)     (INTEL_INFO(dev)->is_valleyview && IS_GEN8(dev))
 #define IS_HASWELL(dev)        (INTEL_INFO(dev)->is_haswell)
-#define IS_BROADWELL(dev)      (INTEL_INFO(dev)->gen == 8)
+#define IS_BROADWELL(dev)      (!INTEL_INFO(dev)->is_valleyview && IS_GEN8(dev))
 #define IS_MOBILE(dev)         (INTEL_INFO(dev)->is_mobile)
 #define IS_HSW_EARLY_SDV(dev)  (IS_HASWELL(dev) && \
                                 ((dev)->pdev->device & 0xFF00) == 0x0C00)
@@ -1962,17 +1926,21 @@ struct drm_i915_cmd_table {
 #define BSD_RING               (1<<VCS)
 #define BLT_RING               (1<<BCS)
 #define VEBOX_RING             (1<<VECS)
-#define HAS_BSD(dev)            (INTEL_INFO(dev)->ring_mask & BSD_RING)
-#define HAS_BLT(dev)            (INTEL_INFO(dev)->ring_mask & BLT_RING)
-#define HAS_VEBOX(dev)            (INTEL_INFO(dev)->ring_mask & VEBOX_RING)
-#define HAS_LLC(dev)            (INTEL_INFO(dev)->has_llc)
-#define HAS_WT(dev)            (IS_HASWELL(dev) && to_i915(dev)->ellc_size)
+#define BSD2_RING              (1<<VCS2)
+#define HAS_BSD(dev)           (INTEL_INFO(dev)->ring_mask & BSD_RING)
+#define HAS_BSD2(dev)          (INTEL_INFO(dev)->ring_mask & BSD2_RING)
+#define HAS_BLT(dev)           (INTEL_INFO(dev)->ring_mask & BLT_RING)
+#define HAS_VEBOX(dev)         (INTEL_INFO(dev)->ring_mask & VEBOX_RING)
+#define HAS_LLC(dev)           (INTEL_INFO(dev)->has_llc)
+#define HAS_WT(dev)            ((IS_HASWELL(dev) || IS_BROADWELL(dev)) && \
+                                to_i915(dev)->ellc_size)
 #define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws)
 
 #define HAS_HW_CONTEXTS(dev)   (INTEL_INFO(dev)->gen >= 6)
-#define HAS_ALIASING_PPGTT(dev)        (INTEL_INFO(dev)->gen >= 6 && !IS_VALLEYVIEW(dev))
-#define HAS_PPGTT(dev)         (INTEL_INFO(dev)->gen >= 7 && !IS_VALLEYVIEW(dev) \
-                                && !IS_BROADWELL(dev))
+#define HAS_ALIASING_PPGTT(dev)        (INTEL_INFO(dev)->gen >= 6 && \
+                                (!IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)))
+#define HAS_PPGTT(dev)         (INTEL_INFO(dev)->gen >= 7 \
+                                && !IS_GEN8(dev))
 #define USES_PPGTT(dev)                intel_enable_ppgtt(dev, false)
 #define USES_FULL_PPGTT(dev)   intel_enable_ppgtt(dev, true)
 
@@ -2010,8 +1978,8 @@ struct drm_i915_cmd_table {
 #define HAS_DDI(dev)           (INTEL_INFO(dev)->has_ddi)
 #define HAS_FPGA_DBG_UNCLAIMED(dev)    (INTEL_INFO(dev)->has_fpga_dbg)
 #define HAS_PSR(dev)           (IS_HASWELL(dev) || IS_BROADWELL(dev))
-#define HAS_PC8(dev)           (IS_HASWELL(dev)) /* XXX HSW:ULX */
-#define HAS_RUNTIME_PM(dev)    (IS_HASWELL(dev))
+#define HAS_RUNTIME_PM(dev)    (IS_GEN6(dev) || IS_HASWELL(dev) || \
+                                IS_BROADWELL(dev) || IS_VALLEYVIEW(dev))
 
 #define INTEL_PCH_DEVICE_ID_MASK               0xff00
 #define INTEL_PCH_IBX_DEVICE_ID_TYPE           0x3b00
@@ -2068,6 +2036,7 @@ struct i915_params {
        bool prefault_disable;
        bool reset;
        bool disable_display;
+       bool disable_vtd_wa;
 };
 extern struct i915_params i915 __read_mostly;
 
@@ -2096,6 +2065,7 @@ extern unsigned long i915_chipset_val(struct drm_i915_private *dev_priv);
 extern unsigned long i915_mch_val(struct drm_i915_private *dev_priv);
 extern unsigned long i915_gfx_val(struct drm_i915_private *dev_priv);
 extern void i915_update_gfx_val(struct drm_i915_private *dev_priv);
+int vlv_force_gfx_clock(struct drm_i915_private *dev_priv, bool on);
 
 extern void intel_console_resume(struct work_struct *work);
 
@@ -2170,6 +2140,9 @@ int i915_gem_set_tiling(struct drm_device *dev, void *data,
                        struct drm_file *file_priv);
 int i915_gem_get_tiling(struct drm_device *dev, void *data,
                        struct drm_file *file_priv);
+int i915_gem_init_userptr(struct drm_device *dev);
+int i915_gem_userptr_ioctl(struct drm_device *dev, void *data,
+                          struct drm_file *file);
 int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
                                struct drm_file *file_priv);
 int i915_gem_wait_ioctl(struct drm_device *dev, void *data,
@@ -2227,9 +2200,9 @@ static inline void i915_gem_object_unpin_pages(struct drm_i915_gem_object *obj)
 
 int __must_check i915_mutex_lock_interruptible(struct drm_device *dev);
 int i915_gem_object_sync(struct drm_i915_gem_object *obj,
-                        struct intel_ring_buffer *to);
+                        struct intel_engine_cs *to);
 void i915_vma_move_to_active(struct i915_vma *vma,
-                            struct intel_ring_buffer *ring);
+                            struct intel_engine_cs *ring);
 int i915_gem_dumb_create(struct drm_file *file_priv,
                         struct drm_device *dev,
                         struct drm_mode_create_dumb *args);
@@ -2249,31 +2222,14 @@ int __must_check i915_gem_set_seqno(struct drm_device *dev, u32 seqno);
 int __must_check i915_gem_object_get_fence(struct drm_i915_gem_object *obj);
 int __must_check i915_gem_object_put_fence(struct drm_i915_gem_object *obj);
 
-static inline bool
-i915_gem_object_pin_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;
-               dev_priv->fence_regs[obj->fence_reg].pin_count++;
-               return true;
-       } else
-               return false;
-}
-
-static inline void
-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--;
-       }
-}
+bool i915_gem_object_pin_fence(struct drm_i915_gem_object *obj);
+void i915_gem_object_unpin_fence(struct drm_i915_gem_object *obj);
 
 struct drm_i915_gem_request *
-i915_gem_find_active_request(struct intel_ring_buffer *ring);
+i915_gem_find_active_request(struct intel_engine_cs *ring);
 
 bool i915_gem_retire_requests(struct drm_device *dev);
+void i915_gem_retire_requests_ring(struct intel_engine_cs *ring);
 int __must_check i915_gem_check_wedge(struct i915_gpu_error *error,
                                      bool interruptible);
 static inline bool i915_reset_in_progress(struct i915_gpu_error *error)
@@ -2292,23 +2248,35 @@ static inline u32 i915_reset_count(struct i915_gpu_error *error)
        return ((atomic_read(&error->reset_counter) & ~I915_WEDGED) + 1) / 2;
 }
 
+static inline bool i915_stop_ring_allow_ban(struct drm_i915_private *dev_priv)
+{
+       return dev_priv->gpu_error.stop_rings == 0 ||
+               dev_priv->gpu_error.stop_rings & I915_STOP_RING_ALLOW_BAN;
+}
+
+static inline bool i915_stop_ring_allow_warn(struct drm_i915_private *dev_priv)
+{
+       return dev_priv->gpu_error.stop_rings == 0 ||
+               dev_priv->gpu_error.stop_rings & I915_STOP_RING_ALLOW_WARN;
+}
+
 void i915_gem_reset(struct drm_device *dev);
 bool i915_gem_clflush_object(struct drm_i915_gem_object *obj, bool force);
 int __must_check i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj);
 int __must_check i915_gem_init(struct drm_device *dev);
 int __must_check i915_gem_init_hw(struct drm_device *dev);
-int i915_gem_l3_remap(struct intel_ring_buffer *ring, int slice);
+int i915_gem_l3_remap(struct intel_engine_cs *ring, int slice);
 void i915_gem_init_swizzling(struct drm_device *dev);
 void i915_gem_cleanup_ringbuffer(struct drm_device *dev);
 int __must_check i915_gpu_idle(struct drm_device *dev);
 int __must_check i915_gem_suspend(struct drm_device *dev);
-int __i915_add_request(struct intel_ring_buffer *ring,
+int __i915_add_request(struct intel_engine_cs *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,
+int __must_check i915_wait_seqno(struct intel_engine_cs *ring,
                                 uint32_t seqno);
 int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
 int __must_check
@@ -2319,7 +2287,7 @@ i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, bool write);
 int __must_check
 i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
                                     u32 alignment,
-                                    struct intel_ring_buffer *pipelined);
+                                    struct intel_engine_cs *pipelined);
 void i915_gem_object_unpin_from_display_plane(struct drm_i915_gem_object *obj);
 int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj,
                                int align);
@@ -2416,22 +2384,22 @@ void i915_gem_context_reset(struct drm_device *dev);
 int i915_gem_context_open(struct drm_device *dev, struct drm_file *file);
 int i915_gem_context_enable(struct drm_i915_private *dev_priv);
 void i915_gem_context_close(struct drm_device *dev, struct drm_file *file);
-int i915_switch_context(struct intel_ring_buffer *ring,
-                       struct i915_hw_context *to);
-struct i915_hw_context *
+int i915_switch_context(struct intel_engine_cs *ring,
+                       struct intel_context *to);
+struct intel_context *
 i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id);
 void i915_gem_context_free(struct kref *ctx_ref);
-static inline void i915_gem_context_reference(struct i915_hw_context *ctx)
+static inline void i915_gem_context_reference(struct intel_context *ctx)
 {
        kref_get(&ctx->ref);
 }
 
-static inline void i915_gem_context_unreference(struct i915_hw_context *ctx)
+static inline void i915_gem_context_unreference(struct intel_context *ctx)
 {
        kref_put(&ctx->ref, i915_gem_context_free);
 }
 
-static inline bool i915_gem_context_is_default(const struct i915_hw_context *c)
+static inline bool i915_gem_context_is_default(const struct intel_context *c)
 {
        return c->id == DEFAULT_CONTEXT_ID;
 }
@@ -2441,6 +2409,8 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
 int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data,
                                   struct drm_file *file);
 
+/* i915_gem_render_state.c */
+int i915_gem_render_state_init(struct intel_engine_cs *ring);
 /* i915_gem_evict.c */
 int __must_check i915_gem_evict_something(struct drm_device *dev,
                                          struct i915_address_space *vm,
@@ -2453,23 +2423,12 @@ int __must_check i915_gem_evict_something(struct drm_device *dev,
 int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle);
 int i915_gem_evict_everything(struct drm_device *dev);
 
-/* i915_gem_gtt.c */
-void i915_check_and_clear_faults(struct drm_device *dev);
-void i915_gem_suspend_gtt_mappings(struct drm_device *dev);
-void i915_gem_restore_gtt_mappings(struct drm_device *dev);
-int __must_check i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj);
-void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj);
-void i915_gem_init_global_gtt(struct drm_device *dev);
-void i915_gem_setup_global_gtt(struct drm_device *dev, unsigned long start,
-                              unsigned long mappable_end, unsigned long end);
-int i915_gem_gtt_init(struct drm_device *dev);
+/* belongs in i915_gem_gtt.h */
 static inline void i915_gem_chipset_flush(struct drm_device *dev)
 {
        if (INTEL_INFO(dev)->gen < 6)
                intel_gtt_chipset_flush();
 }
-int i915_gem_init_ppgtt(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt);
-bool intel_enable_ppgtt(struct drm_device *dev, bool full);
 
 /* i915_gem_stolen.c */
 int i915_gem_init_stolen(struct drm_device *dev);
@@ -2537,9 +2496,11 @@ void i915_get_extra_instdone(struct drm_device *dev, uint32_t *instdone);
 const char *i915_cache_level_str(int type);
 
 /* i915_cmd_parser.c */
-void i915_cmd_parser_init_ring(struct intel_ring_buffer *ring);
-bool i915_needs_cmd_parser(struct intel_ring_buffer *ring);
-int i915_parse_cmds(struct intel_ring_buffer *ring,
+int i915_cmd_parser_get_version(void);
+int i915_cmd_parser_init_ring(struct intel_engine_cs *ring);
+void i915_cmd_parser_fini_ring(struct intel_engine_cs *ring);
+bool i915_needs_cmd_parser(struct intel_engine_cs *ring);
+int i915_parse_cmds(struct intel_engine_cs *ring,
                    struct drm_i915_gem_object *batch_obj,
                    u32 batch_start_offset,
                    bool is_master);
@@ -2688,20 +2649,6 @@ void vlv_flisdsi_write(struct drm_i915_private *dev_priv, u32 reg, u32 val);
 int vlv_gpu_freq(struct drm_i915_private *dev_priv, int val);
 int vlv_freq_opcode(struct drm_i915_private *dev_priv, int val);
 
-void vlv_force_wake_get(struct drm_i915_private *dev_priv, int fw_engine);
-void vlv_force_wake_put(struct drm_i915_private *dev_priv, int fw_engine);
-
-#define FORCEWAKE_VLV_RENDER_RANGE_OFFSET(reg) \
-       (((reg) >= 0x2000 && (reg) < 0x4000) ||\
-       ((reg) >= 0x5000 && (reg) < 0x8000) ||\
-       ((reg) >= 0xB000 && (reg) < 0x12000) ||\
-       ((reg) >= 0x2E000 && (reg) < 0x30000))
-
-#define FORCEWAKE_VLV_MEDIA_RANGE_OFFSET(reg)\
-       (((reg) >= 0x12000 && (reg) < 0x14000) ||\
-       ((reg) >= 0x22000 && (reg) < 0x24000) ||\
-       ((reg) >= 0x30000 && (reg) < 0x40000))
-
 #define FORCEWAKE_RENDER       (1 << 0)
 #define FORCEWAKE_MEDIA                (1 << 1)
 #define FORCEWAKE_ALL          (FORCEWAKE_RENDER | FORCEWAKE_MEDIA)
index 3326770c9ed2618d914d931a8fd5b54b2137c02b..f36126383d260166a950ad20c72b69637246c550 100644 (file)
@@ -31,6 +31,7 @@
 #include "i915_drv.h"
 #include "i915_trace.h"
 #include "intel_drv.h"
+#include <linux/oom.h>
 #include <linux/shmem_fs.h>
 #include <linux/slab.h>
 #include <linux/swap.h>
@@ -43,6 +44,8 @@ static void i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *o
 static __must_check int
 i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj,
                               bool readonly);
+static void
+i915_gem_object_retire(struct drm_i915_gem_object *obj);
 
 static void i915_gem_write_fence(struct drm_device *dev, int reg,
                                 struct drm_i915_gem_object *obj);
@@ -50,14 +53,15 @@ static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj,
                                         struct drm_i915_fence_reg *fence,
                                         bool enable);
 
-static unsigned long i915_gem_inactive_count(struct shrinker *shrinker,
+static unsigned long i915_gem_shrinker_count(struct shrinker *shrinker,
                                             struct shrink_control *sc);
-static unsigned long i915_gem_inactive_scan(struct shrinker *shrinker,
+static unsigned long i915_gem_shrinker_scan(struct shrinker *shrinker,
                                            struct shrink_control *sc);
+static int i915_gem_shrinker_oom(struct notifier_block *nb,
+                                unsigned long event,
+                                void *ptr);
 static unsigned long i915_gem_purge(struct drm_i915_private *dev_priv, long target);
 static unsigned long i915_gem_shrink_all(struct drm_i915_private *dev_priv);
-static void i915_gem_object_truncate(struct drm_i915_gem_object *obj);
-static void i915_gem_retire_requests_ring(struct intel_ring_buffer *ring);
 
 static bool cpu_cache_is_coherent(struct drm_device *dev,
                                  enum i915_cache_level level)
@@ -470,6 +474,8 @@ int i915_gem_obj_prepare_shmem_read(struct drm_i915_gem_object *obj,
                ret = i915_gem_object_wait_rendering(obj, true);
                if (ret)
                        return ret;
+
+               i915_gem_object_retire(obj);
        }
 
        ret = i915_gem_object_get_pages(obj);
@@ -885,6 +891,8 @@ i915_gem_shmem_pwrite(struct drm_device *dev,
                ret = i915_gem_object_wait_rendering(obj, false);
                if (ret)
                        return ret;
+
+               i915_gem_object_retire(obj);
        }
        /* Same trick applies to invalidate partially written cachelines read
         * before writing. */
@@ -1088,7 +1096,7 @@ i915_gem_check_wedge(struct i915_gpu_error *error,
  * equal.
  */
 static int
-i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno)
+i915_gem_check_olr(struct intel_engine_cs *ring, u32 seqno)
 {
        int ret;
 
@@ -1107,7 +1115,7 @@ static void fake_irq(unsigned long data)
 }
 
 static bool missed_irq(struct drm_i915_private *dev_priv,
-                      struct intel_ring_buffer *ring)
+                      struct intel_engine_cs *ring)
 {
        return test_bit(ring->id, &dev_priv->gpu_error.missed_irq_rings);
 }
@@ -1138,7 +1146,7 @@ static bool can_wait_boost(struct drm_i915_file_private *file_priv)
  * Returns 0 if the seqno was found within the alloted time. Else returns the
  * errno with remaining time filled in timeout argument.
  */
-static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno,
+static int __wait_seqno(struct intel_engine_cs *ring, u32 seqno,
                        unsigned reset_counter,
                        bool interruptible,
                        struct timespec *timeout,
@@ -1245,7 +1253,7 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno,
  * request and object lists appropriately for that event.
  */
 int
-i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno)
+i915_wait_seqno(struct intel_engine_cs *ring, uint32_t seqno)
 {
        struct drm_device *dev = ring->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1270,9 +1278,10 @@ i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno)
 
 static int
 i915_gem_object_wait_rendering__tail(struct drm_i915_gem_object *obj,
-                                    struct intel_ring_buffer *ring)
+                                    struct intel_engine_cs *ring)
 {
-       i915_gem_retire_requests_ring(ring);
+       if (!obj->active)
+               return 0;
 
        /* Manually manage the write flush as we may have not yet
         * retired the buffer.
@@ -1282,7 +1291,6 @@ i915_gem_object_wait_rendering__tail(struct drm_i915_gem_object *obj,
         * we know we have passed the last write.
         */
        obj->last_write_seqno = 0;
-       obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS;
 
        return 0;
 }
@@ -1295,7 +1303,7 @@ static __must_check int
 i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj,
                               bool readonly)
 {
-       struct intel_ring_buffer *ring = obj->ring;
+       struct intel_engine_cs *ring = obj->ring;
        u32 seqno;
        int ret;
 
@@ -1320,7 +1328,7 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj,
 {
        struct drm_device *dev = obj->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring = obj->ring;
+       struct intel_engine_cs *ring = obj->ring;
        unsigned reset_counter;
        u32 seqno;
        int ret;
@@ -1536,7 +1544,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 
        /* Access to snoopable pages through the GTT is incoherent. */
        if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(dev)) {
-               ret = -EINVAL;
+               ret = -EFAULT;
                goto unlock;
        }
 
@@ -1803,12 +1811,16 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
        return i915_gem_mmap_gtt(file, dev, args->handle, &args->offset);
 }
 
+static inline int
+i915_gem_object_is_purgeable(struct drm_i915_gem_object *obj)
+{
+       return obj->madv == I915_MADV_DONTNEED;
+}
+
 /* Immediately discard the backing storage */
 static void
 i915_gem_object_truncate(struct drm_i915_gem_object *obj)
 {
-       struct inode *inode;
-
        i915_gem_object_free_mmap_offset(obj);
 
        if (obj->base.filp == NULL)
@@ -1819,16 +1831,28 @@ i915_gem_object_truncate(struct drm_i915_gem_object *obj)
         * To do this we must instruct the shmfs to drop all of its
         * backing pages, *now*.
         */
-       inode = file_inode(obj->base.filp);
-       shmem_truncate_range(inode, 0, (loff_t)-1);
-
+       shmem_truncate_range(file_inode(obj->base.filp), 0, (loff_t)-1);
        obj->madv = __I915_MADV_PURGED;
 }
 
-static inline int
-i915_gem_object_is_purgeable(struct drm_i915_gem_object *obj)
+/* Try to discard unwanted pages */
+static void
+i915_gem_object_invalidate(struct drm_i915_gem_object *obj)
 {
-       return obj->madv == I915_MADV_DONTNEED;
+       struct address_space *mapping;
+
+       switch (obj->madv) {
+       case I915_MADV_DONTNEED:
+               i915_gem_object_truncate(obj);
+       case __I915_MADV_PURGED:
+               return;
+       }
+
+       if (obj->base.filp == NULL)
+               return;
+
+       mapping = file_inode(obj->base.filp)->i_mapping,
+       invalidate_mapping_pages(mapping, 0, (loff_t)-1);
 }
 
 static void
@@ -1893,8 +1917,7 @@ i915_gem_object_put_pages(struct drm_i915_gem_object *obj)
        ops->put_pages(obj);
        obj->pages = NULL;
 
-       if (i915_gem_object_is_purgeable(obj))
-               i915_gem_object_truncate(obj);
+       i915_gem_object_invalidate(obj);
 
        return 0;
 }
@@ -1903,58 +1926,58 @@ static unsigned long
 __i915_gem_shrink(struct drm_i915_private *dev_priv, long target,
                  bool purgeable_only)
 {
-       struct list_head still_bound_list;
-       struct drm_i915_gem_object *obj, *next;
+       struct list_head still_in_list;
+       struct drm_i915_gem_object *obj;
        unsigned long count = 0;
 
-       list_for_each_entry_safe(obj, next,
-                                &dev_priv->mm.unbound_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;
-                       if (count >= target)
-                               return count;
-               }
-       }
-
        /*
-        * As we may completely rewrite the bound list whilst unbinding
+        * As we may completely rewrite the (un)bound list whilst unbinding
         * (due to retiring requests) we have to strictly process only
         * one element of the list at the time, and recheck the list
         * on every iteration.
+        *
+        * In particular, we must hold a reference whilst removing the
+        * object as we may end up waiting for and/or retiring the objects.
+        * This might release the final reference (held by the active list)
+        * and result in the object being freed from under us. This is
+        * similar to the precautions the eviction code must take whilst
+        * removing objects.
+        *
+        * Also note that although these lists do not hold a reference to
+        * the object we can safely grab one here: The final object
+        * unreferencing and the bound_list are both protected by the
+        * dev->struct_mutex and so we won't ever be able to observe an
+        * object on the bound_list with a reference count equals 0.
         */
-       INIT_LIST_HEAD(&still_bound_list);
+       INIT_LIST_HEAD(&still_in_list);
+       while (count < target && !list_empty(&dev_priv->mm.unbound_list)) {
+               obj = list_first_entry(&dev_priv->mm.unbound_list,
+                                      typeof(*obj), global_list);
+               list_move_tail(&obj->global_list, &still_in_list);
+
+               if (!i915_gem_object_is_purgeable(obj) && purgeable_only)
+                       continue;
+
+               drm_gem_object_reference(&obj->base);
+
+               if (i915_gem_object_put_pages(obj) == 0)
+                       count += obj->base.size >> PAGE_SHIFT;
+
+               drm_gem_object_unreference(&obj->base);
+       }
+       list_splice(&still_in_list, &dev_priv->mm.unbound_list);
+
+       INIT_LIST_HEAD(&still_in_list);
        while (count < target && !list_empty(&dev_priv->mm.bound_list)) {
                struct i915_vma *vma, *v;
 
                obj = list_first_entry(&dev_priv->mm.bound_list,
                                       typeof(*obj), global_list);
-               list_move_tail(&obj->global_list, &still_bound_list);
+               list_move_tail(&obj->global_list, &still_in_list);
 
                if (!i915_gem_object_is_purgeable(obj) && purgeable_only)
                        continue;
 
-               /*
-                * Hold a reference whilst we unbind this object, as we may
-                * end up waiting for and retiring requests. This might
-                * release the final reference (held by the active list)
-                * and result in the object being freed from under us.
-                * in this object being freed.
-                *
-                * Note 1: Shrinking the bound list is special since only active
-                * (and hence bound objects) can contain such limbo objects, so
-                * we don't need special tricks for shrinking the unbound list.
-                * The only other place where we have to be careful with active
-                * objects suddenly disappearing due to retiring requests is the
-                * eviction code.
-                *
-                * Note 2: Even though the bound list doesn't hold a reference
-                * to the object we can safely grab one here: The final object
-                * unreferencing and the bound_list are both protected by the
-                * dev->struct_mutex and so we won't ever be able to observe an
-                * object on the bound_list with a reference count equals 0.
-                */
                drm_gem_object_reference(&obj->base);
 
                list_for_each_entry_safe(vma, v, &obj->vma_list, vma_link)
@@ -1966,7 +1989,7 @@ __i915_gem_shrink(struct drm_i915_private *dev_priv, long target,
 
                drm_gem_object_unreference(&obj->base);
        }
-       list_splice(&still_bound_list, &dev_priv->mm.bound_list);
+       list_splice(&still_in_list, &dev_priv->mm.bound_list);
 
        return count;
 }
@@ -1980,17 +2003,8 @@ i915_gem_purge(struct drm_i915_private *dev_priv, long target)
 static unsigned long
 i915_gem_shrink_all(struct drm_i915_private *dev_priv)
 {
-       struct drm_i915_gem_object *obj, *next;
-       long freed = 0;
-
        i915_gem_evict_everything(dev_priv->dev);
-
-       list_for_each_entry_safe(obj, next, &dev_priv->mm.unbound_list,
-                                global_list) {
-               if (i915_gem_object_put_pages(obj) == 0)
-                       freed += obj->base.size >> PAGE_SHIFT;
-       }
-       return freed;
+       return __i915_gem_shrink(dev_priv, LONG_MAX, false);
 }
 
 static int
@@ -2094,7 +2108,19 @@ err_pages:
                page_cache_release(sg_page_iter_page(&sg_iter));
        sg_free_table(st);
        kfree(st);
-       return PTR_ERR(page);
+
+       /* shmemfs first checks if there is enough memory to allocate the page
+        * and reports ENOSPC should there be insufficient, along with the usual
+        * ENOMEM for a genuine allocation failure.
+        *
+        * We use ENOSPC in our driver to mean that we have run out of aperture
+        * space and so want to translate the error from shmemfs back to our
+        * usual understanding of ENOMEM.
+        */
+       if (PTR_ERR(page) == -ENOSPC)
+               return -ENOMEM;
+       else
+               return PTR_ERR(page);
 }
 
 /* Ensure that the associated pages are gathered from the backing storage
@@ -2131,7 +2157,7 @@ i915_gem_object_get_pages(struct drm_i915_gem_object *obj)
 
 static void
 i915_gem_object_move_to_active(struct drm_i915_gem_object *obj,
-                              struct intel_ring_buffer *ring)
+                              struct intel_engine_cs *ring)
 {
        struct drm_device *dev = obj->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2169,7 +2195,7 @@ i915_gem_object_move_to_active(struct drm_i915_gem_object *obj,
 }
 
 void i915_vma_move_to_active(struct i915_vma *vma,
-                            struct intel_ring_buffer *ring)
+                            struct intel_engine_cs *ring)
 {
        list_move_tail(&vma->mm_list, &vma->vm->active_list);
        return i915_gem_object_move_to_active(vma->obj, ring);
@@ -2207,11 +2233,24 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj)
        WARN_ON(i915_verify_lists(dev));
 }
 
+static void
+i915_gem_object_retire(struct drm_i915_gem_object *obj)
+{
+       struct intel_engine_cs *ring = obj->ring;
+
+       if (ring == NULL)
+               return;
+
+       if (i915_seqno_passed(ring->get_seqno(ring, true),
+                             obj->last_read_seqno))
+               i915_gem_object_move_to_inactive(obj);
+}
+
 static int
 i915_gem_init_seqno(struct drm_device *dev, u32 seqno)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        int ret, i, j;
 
        /* Carefully retire all requests without writing to the rings */
@@ -2226,8 +2265,8 @@ i915_gem_init_seqno(struct drm_device *dev, u32 seqno)
        for_each_ring(ring, dev_priv, i) {
                intel_ring_init_seqno(ring, seqno);
 
-               for (j = 0; j < ARRAY_SIZE(ring->sync_seqno); j++)
-                       ring->sync_seqno[j] = 0;
+               for (j = 0; j < ARRAY_SIZE(ring->semaphore.sync_seqno); j++)
+                       ring->semaphore.sync_seqno[j] = 0;
        }
 
        return 0;
@@ -2277,7 +2316,7 @@ i915_gem_get_seqno(struct drm_device *dev, u32 *seqno)
        return 0;
 }
 
-int __i915_add_request(struct intel_ring_buffer *ring,
+int __i915_add_request(struct intel_engine_cs *ring,
                       struct drm_file *file,
                       struct drm_i915_gem_object *obj,
                       u32 *out_seqno)
@@ -2382,7 +2421,7 @@ i915_gem_request_remove_from_client(struct drm_i915_gem_request *request)
 }
 
 static bool i915_context_is_banned(struct drm_i915_private *dev_priv,
-                                  const struct i915_hw_context *ctx)
+                                  const struct intel_context *ctx)
 {
        unsigned long elapsed;
 
@@ -2395,8 +2434,9 @@ static bool i915_context_is_banned(struct drm_i915_private *dev_priv,
                if (!i915_gem_context_is_default(ctx)) {
                        DRM_DEBUG("context hanging too fast, banning!\n");
                        return true;
-               } else if (dev_priv->gpu_error.stop_rings == 0) {
-                       DRM_ERROR("gpu hanging too fast, banning!\n");
+               } else if (i915_stop_ring_allow_ban(dev_priv)) {
+                       if (i915_stop_ring_allow_warn(dev_priv))
+                               DRM_ERROR("gpu hanging too fast, banning!\n");
                        return true;
                }
        }
@@ -2405,7 +2445,7 @@ static bool i915_context_is_banned(struct drm_i915_private *dev_priv,
 }
 
 static void i915_set_reset_status(struct drm_i915_private *dev_priv,
-                                 struct i915_hw_context *ctx,
+                                 struct intel_context *ctx,
                                  const bool guilty)
 {
        struct i915_ctx_hang_stats *hs;
@@ -2436,7 +2476,7 @@ static void i915_gem_free_request(struct drm_i915_gem_request *request)
 }
 
 struct drm_i915_gem_request *
-i915_gem_find_active_request(struct intel_ring_buffer *ring)
+i915_gem_find_active_request(struct intel_engine_cs *ring)
 {
        struct drm_i915_gem_request *request;
        u32 completed_seqno;
@@ -2454,7 +2494,7 @@ i915_gem_find_active_request(struct intel_ring_buffer *ring)
 }
 
 static void i915_gem_reset_ring_status(struct drm_i915_private *dev_priv,
-                                      struct intel_ring_buffer *ring)
+                                      struct intel_engine_cs *ring)
 {
        struct drm_i915_gem_request *request;
        bool ring_hung;
@@ -2473,7 +2513,7 @@ static void i915_gem_reset_ring_status(struct drm_i915_private *dev_priv,
 }
 
 static void i915_gem_reset_ring_cleanup(struct drm_i915_private *dev_priv,
-                                       struct intel_ring_buffer *ring)
+                                       struct intel_engine_cs *ring)
 {
        while (!list_empty(&ring->active_list)) {
                struct drm_i915_gem_object *obj;
@@ -2501,6 +2541,11 @@ static void i915_gem_reset_ring_cleanup(struct drm_i915_private *dev_priv,
 
                i915_gem_free_request(request);
        }
+
+       /* These may not have been flush before the reset, do so now */
+       kfree(ring->preallocated_lazy_request);
+       ring->preallocated_lazy_request = NULL;
+       ring->outstanding_lazy_seqno = 0;
 }
 
 void i915_gem_restore_fences(struct drm_device *dev)
@@ -2527,7 +2572,7 @@ void i915_gem_restore_fences(struct drm_device *dev)
 void i915_gem_reset(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        int i;
 
        /*
@@ -2541,8 +2586,6 @@ void i915_gem_reset(struct drm_device *dev)
        for_each_ring(ring, dev_priv, i)
                i915_gem_reset_ring_cleanup(dev_priv, ring);
 
-       i915_gem_cleanup_ringbuffer(dev);
-
        i915_gem_context_reset(dev);
 
        i915_gem_restore_fences(dev);
@@ -2551,8 +2594,8 @@ void i915_gem_reset(struct drm_device *dev)
 /**
  * This function clears the request list as sequence numbers are passed.
  */
-static void
-i915_gem_retire_requests_ring(struct intel_ring_buffer *ring)
+void
+i915_gem_retire_requests_ring(struct intel_engine_cs *ring)
 {
        uint32_t seqno;
 
@@ -2597,7 +2640,7 @@ i915_gem_retire_requests_ring(struct intel_ring_buffer *ring)
                 * of tail of the request to update the last known position
                 * of the GPU head.
                 */
-               ring->last_retired_head = request->tail;
+               ring->buffer->last_retired_head = request->tail;
 
                i915_gem_free_request(request);
        }
@@ -2615,7 +2658,7 @@ bool
 i915_gem_retire_requests(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        bool idle = true;
        int i;
 
@@ -2709,7 +2752,7 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_gem_wait *args = data;
        struct drm_i915_gem_object *obj;
-       struct intel_ring_buffer *ring = NULL;
+       struct intel_engine_cs *ring = NULL;
        struct timespec timeout_stack, *timeout = NULL;
        unsigned reset_counter;
        u32 seqno = 0;
@@ -2780,9 +2823,9 @@ out:
  */
 int
 i915_gem_object_sync(struct drm_i915_gem_object *obj,
-                    struct intel_ring_buffer *to)
+                    struct intel_engine_cs *to)
 {
-       struct intel_ring_buffer *from = obj->ring;
+       struct intel_engine_cs *from = obj->ring;
        u32 seqno;
        int ret, idx;
 
@@ -2795,7 +2838,7 @@ i915_gem_object_sync(struct drm_i915_gem_object *obj,
        idx = intel_ring_sync_index(from, to);
 
        seqno = obj->last_read_seqno;
-       if (seqno <= from->sync_seqno[idx])
+       if (seqno <= from->semaphore.sync_seqno[idx])
                return 0;
 
        ret = i915_gem_check_olr(obj->ring, seqno);
@@ -2803,13 +2846,13 @@ i915_gem_object_sync(struct drm_i915_gem_object *obj,
                return ret;
 
        trace_i915_gem_ring_sync_to(from, to, seqno);
-       ret = to->sync_to(to, from, seqno);
+       ret = to->semaphore.sync_to(to, from, seqno);
        if (!ret)
                /* We use last_read_seqno because sync_to()
                 * might have just caused seqno wrap under
                 * the radar.
                 */
-               from->sync_seqno[idx] = obj->last_read_seqno;
+               from->semaphore.sync_seqno[idx] = obj->last_read_seqno;
 
        return ret;
 }
@@ -2865,12 +2908,14 @@ int i915_vma_unbind(struct i915_vma *vma)
         * cause memory corruption through use-after-free.
         */
 
-       i915_gem_object_finish_gtt(obj);
+       if (i915_is_ggtt(vma->vm)) {
+               i915_gem_object_finish_gtt(obj);
 
-       /* release the fence reg _after_ flushing */
-       ret = i915_gem_object_put_fence(obj);
-       if (ret)
-               return ret;
+               /* release the fence reg _after_ flushing */
+               ret = i915_gem_object_put_fence(obj);
+               if (ret)
+                       return ret;
+       }
 
        trace_i915_vma_unbind(vma);
 
@@ -2903,7 +2948,7 @@ int i915_vma_unbind(struct i915_vma *vma)
 int i915_gpu_idle(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        int ret, i;
 
        /* Flush everything onto the inactive list. */
@@ -3144,6 +3189,9 @@ i915_gem_object_put_fence(struct drm_i915_gem_object *obj)
 
        fence = &dev_priv->fence_regs[obj->fence_reg];
 
+       if (WARN_ON(fence->pin_count))
+               return -EBUSY;
+
        i915_gem_object_fence_lost(obj);
        i915_gem_object_update_fence(obj, fence, false);
 
@@ -3548,6 +3596,7 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
        if (ret)
                return ret;
 
+       i915_gem_object_retire(obj);
        i915_gem_object_flush_cpu_write_domain(obj, false);
 
        /* Serialise direct access to this object with the barriers for
@@ -3646,6 +3695,7 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
                 * in obj->write_domain and have been skipping the clflushes.
                 * Just set it to the CPU cache for now.
                 */
+               i915_gem_object_retire(obj);
                WARN_ON(obj->base.write_domain & ~I915_GEM_DOMAIN_CPU);
 
                old_read_domains = obj->base.read_domains;
@@ -3743,6 +3793,15 @@ unlock:
 
 static bool is_pin_display(struct drm_i915_gem_object *obj)
 {
+       struct i915_vma *vma;
+
+       if (list_empty(&obj->vma_list))
+               return false;
+
+       vma = i915_gem_obj_to_ggtt(obj);
+       if (!vma)
+               return false;
+
        /* There are 3 sources that pin objects:
         *   1. The display engine (scanouts, sprites, cursors);
         *   2. Reservations for execbuffer;
@@ -3754,7 +3813,7 @@ static bool is_pin_display(struct drm_i915_gem_object *obj)
         * subtracting the potential reference by the user, any pin_count
         * remains, it must be due to another use by the display engine.
         */
-       return i915_gem_obj_to_ggtt(obj)->pin_count - !!obj->user_pin_count;
+       return vma->pin_count - !!obj->user_pin_count;
 }
 
 /*
@@ -3765,9 +3824,10 @@ static bool is_pin_display(struct drm_i915_gem_object *obj)
 int
 i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
                                     u32 alignment,
-                                    struct intel_ring_buffer *pipelined)
+                                    struct intel_engine_cs *pipelined)
 {
        u32 old_read_domains, old_write_domain;
+       bool was_pin_display;
        int ret;
 
        if (pipelined != obj->ring) {
@@ -3779,6 +3839,7 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
        /* Mark the pin_display early so that we account for the
         * display coherency whilst setting up the cache domains.
         */
+       was_pin_display = obj->pin_display;
        obj->pin_display = true;
 
        /* The display engine is not coherent with the LLC cache on gen6.  As
@@ -3821,7 +3882,8 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
        return 0;
 
 err_unpin_display:
-       obj->pin_display = is_pin_display(obj);
+       WARN_ON(was_pin_display != is_pin_display(obj));
+       obj->pin_display = was_pin_display;
        return ret;
 }
 
@@ -3868,6 +3930,7 @@ i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, bool write)
        if (ret)
                return ret;
 
+       i915_gem_object_retire(obj);
        i915_gem_object_flush_gtt_write_domain(obj);
 
        old_write_domain = obj->base.write_domain;
@@ -3917,7 +3980,7 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file)
        struct drm_i915_file_private *file_priv = file->driver_priv;
        unsigned long recent_enough = jiffies - msecs_to_jiffies(20);
        struct drm_i915_gem_request *request;
-       struct intel_ring_buffer *ring = NULL;
+       struct intel_engine_cs *ring = NULL;
        unsigned reset_counter;
        u32 seqno = 0;
        int ret;
@@ -3976,9 +4039,13 @@ i915_gem_object_pin(struct drm_i915_gem_object *obj,
                    uint32_t alignment,
                    uint64_t flags)
 {
+       struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
        struct i915_vma *vma;
        int ret;
 
+       if (WARN_ON(vm == &dev_priv->mm.aliasing_ppgtt->base))
+               return -ENODEV;
+
        if (WARN_ON(flags & (PIN_GLOBAL | PIN_MAPPABLE) && !i915_is_ggtt(vm)))
                return -EINVAL;
 
@@ -4032,6 +4099,32 @@ i915_gem_object_ggtt_unpin(struct drm_i915_gem_object *obj)
                obj->pin_mappable = false;
 }
 
+bool
+i915_gem_object_pin_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;
+               struct i915_vma *ggtt_vma = i915_gem_obj_to_ggtt(obj);
+
+               WARN_ON(!ggtt_vma ||
+                       dev_priv->fence_regs[obj->fence_reg].pin_count >
+                       ggtt_vma->pin_count);
+               dev_priv->fence_regs[obj->fence_reg].pin_count++;
+               return true;
+       } else
+               return false;
+}
+
+void
+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--;
+       }
+}
+
 int
 i915_gem_pin_ioctl(struct drm_device *dev, void *data,
                   struct drm_file *file)
@@ -4292,6 +4385,30 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
        return obj;
 }
 
+static bool discard_backing_storage(struct drm_i915_gem_object *obj)
+{
+       /* If we are the last user of the backing storage (be it shmemfs
+        * pages or stolen etc), we know that the pages are going to be
+        * immediately released. In this case, we can then skip copying
+        * back the contents from the GPU.
+        */
+
+       if (obj->madv != I915_MADV_WILLNEED)
+               return false;
+
+       if (obj->base.filp == NULL)
+               return true;
+
+       /* At first glance, this looks racy, but then again so would be
+        * userspace racing mmap against close. However, the first external
+        * reference to the filp can only be obtained through the
+        * i915_gem_mmap_ioctl() which safeguards us against the user
+        * acquiring such a reference whilst we are in the middle of
+        * freeing the object.
+        */
+       return atomic_long_read(&obj->base.filp->f_count) == 1;
+}
+
 void i915_gem_free_object(struct drm_gem_object *gem_obj)
 {
        struct drm_i915_gem_object *obj = to_intel_bo(gem_obj);
@@ -4329,6 +4446,8 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
 
        if (WARN_ON(obj->pages_pin_count))
                obj->pages_pin_count = 0;
+       if (discard_backing_storage(obj))
+               obj->madv = I915_MADV_DONTNEED;
        i915_gem_object_put_pages(obj);
        i915_gem_object_free_mmap_offset(obj);
        i915_gem_object_release_stolen(obj);
@@ -4338,6 +4457,9 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
        if (obj->base.import_attach)
                drm_prime_gem_destroy(&obj->base, NULL);
 
+       if (obj->ops->release)
+               obj->ops->release(obj);
+
        drm_gem_object_release(&obj->base);
        i915_gem_info_remove_obj(dev_priv, obj->base.size);
 
@@ -4371,6 +4493,17 @@ void i915_gem_vma_destroy(struct i915_vma *vma)
        kfree(vma);
 }
 
+static void
+i915_gem_stop_ringbuffers(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_engine_cs *ring;
+       int i;
+
+       for_each_ring(ring, dev_priv, i)
+               intel_stop_ring_buffer(ring);
+}
+
 int
 i915_gem_suspend(struct drm_device *dev)
 {
@@ -4392,7 +4525,7 @@ i915_gem_suspend(struct drm_device *dev)
                i915_gem_evict_everything(dev);
 
        i915_kernel_lost_context(dev);
-       i915_gem_cleanup_ringbuffer(dev);
+       i915_gem_stop_ringbuffers(dev);
 
        /* Hack!  Don't let anybody do execbuf while we don't control the chip.
         * We need to replace this with a semaphore, or something.
@@ -4413,7 +4546,7 @@ err:
        return ret;
 }
 
-int i915_gem_l3_remap(struct intel_ring_buffer *ring, int slice)
+int i915_gem_l3_remap(struct intel_engine_cs *ring, int slice)
 {
        struct drm_device *dev = ring->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -4512,13 +4645,20 @@ static int i915_gem_init_rings(struct drm_device *dev)
                        goto cleanup_blt_ring;
        }
 
+       if (HAS_BSD2(dev)) {
+               ret = intel_init_bsd2_ring_buffer(dev);
+               if (ret)
+                       goto cleanup_vebox_ring;
+       }
 
        ret = i915_gem_set_seqno(dev, ((u32)~0 - 0x1000));
        if (ret)
-               goto cleanup_vebox_ring;
+               goto cleanup_bsd2_ring;
 
        return 0;
 
+cleanup_bsd2_ring:
+       intel_cleanup_ring_buffer(&dev_priv->ring[VCS2]);
 cleanup_vebox_ring:
        intel_cleanup_ring_buffer(&dev_priv->ring[VECS]);
 cleanup_blt_ring:
@@ -4576,15 +4716,11 @@ i915_gem_init_hw(struct drm_device *dev)
         * the do_switch), but before enabling PPGTT. So don't move this.
         */
        ret = i915_gem_context_enable(dev_priv);
-       if (ret) {
+       if (ret && ret != -EIO) {
                DRM_ERROR("Context enable failed %d\n", ret);
-               goto err_out;
+               i915_gem_cleanup_ringbuffer(dev);
        }
 
-       return 0;
-
-err_out:
-       i915_gem_cleanup_ringbuffer(dev);
        return ret;
 }
 
@@ -4597,11 +4733,13 @@ int i915_gem_init(struct drm_device *dev)
 
        if (IS_VALLEYVIEW(dev)) {
                /* VLVA0 (potential hack), BIOS isn't actually waking us */
-               I915_WRITE(VLV_GTLC_WAKE_CTRL, 1);
-               if (wait_for((I915_READ(VLV_GTLC_PW_STATUS) & 1) == 1, 10))
+               I915_WRITE(VLV_GTLC_WAKE_CTRL, VLV_GTLC_ALLOWWAKEREQ);
+               if (wait_for((I915_READ(VLV_GTLC_PW_STATUS) &
+                             VLV_GTLC_ALLOWWAKEACK), 10))
                        DRM_DEBUG_DRIVER("allow wake ack timed out\n");
        }
 
+       i915_gem_init_userptr(dev);
        i915_gem_init_global_gtt(dev);
 
        ret = i915_gem_context_init(dev);
@@ -4611,25 +4749,28 @@ int i915_gem_init(struct drm_device *dev)
        }
 
        ret = i915_gem_init_hw(dev);
-       mutex_unlock(&dev->struct_mutex);
-       if (ret) {
-               WARN_ON(dev_priv->mm.aliasing_ppgtt);
-               i915_gem_context_fini(dev);
-               drm_mm_takedown(&dev_priv->gtt.base.mm);
-               return ret;
+       if (ret == -EIO) {
+               /* Allow ring initialisation to fail by marking the GPU as
+                * wedged. But we only want to do this where the GPU is angry,
+                * for all other failure, such as an allocation failure, bail.
+                */
+               DRM_ERROR("Failed to initialize GPU, declaring it wedged\n");
+               atomic_set_mask(I915_WEDGED, &dev_priv->gpu_error.reset_counter);
+               ret = 0;
        }
+       mutex_unlock(&dev->struct_mutex);
 
        /* Allow hardware batchbuffers unless told otherwise, but not for KMS. */
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                dev_priv->dri1.allow_batchbuffer = 1;
-       return 0;
+       return ret;
 }
 
 void
 i915_gem_cleanup_ringbuffer(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        int i;
 
        for_each_ring(ring, dev_priv, i)
@@ -4661,16 +4802,15 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data,
        }
 
        BUG_ON(!list_empty(&dev_priv->gtt.base.active_list));
-       mutex_unlock(&dev->struct_mutex);
 
-       ret = drm_irq_install(dev);
+       ret = drm_irq_install(dev, dev->pdev->irq);
        if (ret)
                goto cleanup_ringbuffer;
+       mutex_unlock(&dev->struct_mutex);
 
        return 0;
 
 cleanup_ringbuffer:
-       mutex_lock(&dev->struct_mutex);
        i915_gem_cleanup_ringbuffer(dev);
        dev_priv->ums.mm_suspended = 1;
        mutex_unlock(&dev->struct_mutex);
@@ -4685,7 +4825,9 @@ i915_gem_leavevt_ioctl(struct drm_device *dev, void *data,
        if (drm_core_check_feature(dev, DRIVER_MODESET))
                return 0;
 
+       mutex_lock(&dev->struct_mutex);
        drm_irq_uninstall(dev);
+       mutex_unlock(&dev->struct_mutex);
 
        return i915_gem_suspend(dev);
 }
@@ -4704,7 +4846,7 @@ i915_gem_lastclose(struct drm_device *dev)
 }
 
 static void
-init_ring_lists(struct intel_ring_buffer *ring)
+init_ring_lists(struct intel_engine_cs *ring)
 {
        INIT_LIST_HEAD(&ring->active_list);
        INIT_LIST_HEAD(&ring->request_list);
@@ -4752,7 +4894,7 @@ i915_gem_load(struct drm_device *dev)
        init_waitqueue_head(&dev_priv->gpu_error.reset_queue);
 
        /* On GEN3 we really need to make sure the ARB C3 LP bit is set */
-       if (IS_GEN3(dev)) {
+       if (!drm_core_check_feature(dev, DRIVER_MODESET) && IS_GEN3(dev)) {
                I915_WRITE(MI_ARB_STATE,
                           _MASKED_BIT_ENABLE(MI_ARB_C3_LP_WRITE_ENABLE));
        }
@@ -4779,10 +4921,13 @@ i915_gem_load(struct drm_device *dev)
 
        dev_priv->mm.interruptible = true;
 
-       dev_priv->mm.inactive_shrinker.scan_objects = i915_gem_inactive_scan;
-       dev_priv->mm.inactive_shrinker.count_objects = i915_gem_inactive_count;
-       dev_priv->mm.inactive_shrinker.seeks = DEFAULT_SEEKS;
-       register_shrinker(&dev_priv->mm.inactive_shrinker);
+       dev_priv->mm.shrinker.scan_objects = i915_gem_shrinker_scan;
+       dev_priv->mm.shrinker.count_objects = i915_gem_shrinker_count;
+       dev_priv->mm.shrinker.seeks = DEFAULT_SEEKS;
+       register_shrinker(&dev_priv->mm.shrinker);
+
+       dev_priv->mm.oom_notifier.notifier_call = i915_gem_shrinker_oom;
+       register_oom_notifier(&dev_priv->mm.oom_notifier);
 }
 
 void i915_gem_release(struct drm_device *dev, struct drm_file *file)
@@ -4857,27 +5002,46 @@ static bool mutex_is_locked_by(struct mutex *mutex, struct task_struct *task)
 #endif
 }
 
+static bool i915_gem_shrinker_lock(struct drm_device *dev, bool *unlock)
+{
+       if (!mutex_trylock(&dev->struct_mutex)) {
+               if (!mutex_is_locked_by(&dev->struct_mutex, current))
+                       return false;
+
+               if (to_i915(dev)->mm.shrinker_no_lock_stealing)
+                       return false;
+
+               *unlock = false;
+       } else
+               *unlock = true;
+
+       return true;
+}
+
+static int num_vma_bound(struct drm_i915_gem_object *obj)
+{
+       struct i915_vma *vma;
+       int count = 0;
+
+       list_for_each_entry(vma, &obj->vma_list, vma_link)
+               if (drm_mm_node_allocated(&vma->node))
+                       count++;
+
+       return count;
+}
+
 static unsigned long
-i915_gem_inactive_count(struct shrinker *shrinker, struct shrink_control *sc)
+i915_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc)
 {
        struct drm_i915_private *dev_priv =
-               container_of(shrinker,
-                            struct drm_i915_private,
-                            mm.inactive_shrinker);
+               container_of(shrinker, struct drm_i915_private, mm.shrinker);
        struct drm_device *dev = dev_priv->dev;
        struct drm_i915_gem_object *obj;
-       bool unlock = true;
        unsigned long count;
+       bool unlock;
 
-       if (!mutex_trylock(&dev->struct_mutex)) {
-               if (!mutex_is_locked_by(&dev->struct_mutex, current))
-                       return 0;
-
-               if (dev_priv->mm.shrinker_no_lock_stealing)
-                       return 0;
-
-               unlock = false;
-       }
+       if (!i915_gem_shrinker_lock(dev, &unlock))
+               return 0;
 
        count = 0;
        list_for_each_entry(obj, &dev_priv->mm.unbound_list, global_list)
@@ -4885,10 +5049,8 @@ i915_gem_inactive_count(struct shrinker *shrinker, struct shrink_control *sc)
                        count += obj->base.size >> PAGE_SHIFT;
 
        list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
-               if (obj->active)
-                       continue;
-
-               if (!i915_gem_obj_is_pinned(obj) && obj->pages_pin_count == 0)
+               if (!i915_gem_obj_is_pinned(obj) &&
+                   obj->pages_pin_count == num_vma_bound(obj))
                        count += obj->base.size >> PAGE_SHIFT;
        }
 
@@ -4961,44 +5123,99 @@ unsigned long i915_gem_obj_size(struct drm_i915_gem_object *o,
 }
 
 static unsigned long
-i915_gem_inactive_scan(struct shrinker *shrinker, struct shrink_control *sc)
+i915_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
 {
        struct drm_i915_private *dev_priv =
-               container_of(shrinker,
-                            struct drm_i915_private,
-                            mm.inactive_shrinker);
+               container_of(shrinker, struct drm_i915_private, mm.shrinker);
        struct drm_device *dev = dev_priv->dev;
        unsigned long freed;
-       bool unlock = true;
+       bool unlock;
 
-       if (!mutex_trylock(&dev->struct_mutex)) {
-               if (!mutex_is_locked_by(&dev->struct_mutex, current))
-                       return SHRINK_STOP;
-
-               if (dev_priv->mm.shrinker_no_lock_stealing)
-                       return SHRINK_STOP;
-
-               unlock = false;
-       }
+       if (!i915_gem_shrinker_lock(dev, &unlock))
+               return SHRINK_STOP;
 
        freed = i915_gem_purge(dev_priv, sc->nr_to_scan);
        if (freed < sc->nr_to_scan)
                freed += __i915_gem_shrink(dev_priv,
                                           sc->nr_to_scan - freed,
                                           false);
-       if (freed < sc->nr_to_scan)
-               freed += i915_gem_shrink_all(dev_priv);
-
        if (unlock)
                mutex_unlock(&dev->struct_mutex);
 
        return freed;
 }
 
+static int
+i915_gem_shrinker_oom(struct notifier_block *nb, unsigned long event, void *ptr)
+{
+       struct drm_i915_private *dev_priv =
+               container_of(nb, struct drm_i915_private, mm.oom_notifier);
+       struct drm_device *dev = dev_priv->dev;
+       struct drm_i915_gem_object *obj;
+       unsigned long timeout = msecs_to_jiffies(5000) + 1;
+       unsigned long pinned, bound, unbound, freed;
+       bool was_interruptible;
+       bool unlock;
+
+       while (!i915_gem_shrinker_lock(dev, &unlock) && --timeout)
+               schedule_timeout_killable(1);
+       if (timeout == 0) {
+               pr_err("Unable to purge GPU memory due lock contention.\n");
+               return NOTIFY_DONE;
+       }
+
+       was_interruptible = dev_priv->mm.interruptible;
+       dev_priv->mm.interruptible = false;
+
+       freed = i915_gem_shrink_all(dev_priv);
+
+       dev_priv->mm.interruptible = was_interruptible;
+
+       /* Because we may be allocating inside our own driver, we cannot
+        * assert that there are no objects with pinned pages that are not
+        * being pointed to by hardware.
+        */
+       unbound = bound = pinned = 0;
+       list_for_each_entry(obj, &dev_priv->mm.unbound_list, global_list) {
+               if (!obj->base.filp) /* not backed by a freeable object */
+                       continue;
+
+               if (obj->pages_pin_count)
+                       pinned += obj->base.size;
+               else
+                       unbound += obj->base.size;
+       }
+       list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
+               if (!obj->base.filp)
+                       continue;
+
+               if (obj->pages_pin_count)
+                       pinned += obj->base.size;
+               else
+                       bound += obj->base.size;
+       }
+
+       if (unlock)
+               mutex_unlock(&dev->struct_mutex);
+
+       pr_info("Purging GPU memory, %lu bytes freed, %lu bytes still pinned.\n",
+               freed, pinned);
+       if (unbound || bound)
+               pr_err("%lu and %lu bytes still available in the "
+                      "bound and unbound GPU page lists.\n",
+                      bound, unbound);
+
+       *(unsigned long *)ptr += freed;
+       return NOTIFY_DONE;
+}
+
 struct i915_vma *i915_gem_obj_to_ggtt(struct drm_i915_gem_object *obj)
 {
        struct i915_vma *vma;
 
+       /* This WARN has probably outlived its usefulness (callers already
+        * WARN if they don't find the GGTT vma they expect). When removing,
+        * remember to remove the pre-check in is_pin_display() as well */
        if (WARN_ON(list_empty(&obj->vma_list)))
                return NULL;
 
index d72db15afa02fde49c56693bbf84ce227038298d..3ffe308d58937b767e6337ed6a9cf7aaa65d4b5b 100644 (file)
@@ -178,7 +178,7 @@ static int get_context_size(struct drm_device *dev)
 
 void i915_gem_context_free(struct kref *ctx_ref)
 {
-       struct i915_hw_context *ctx = container_of(ctx_ref,
+       struct intel_context *ctx = container_of(ctx_ref,
                                                   typeof(*ctx), ref);
        struct i915_hw_ppgtt *ppgtt = NULL;
 
@@ -199,7 +199,7 @@ void i915_gem_context_free(struct kref *ctx_ref)
 }
 
 static struct i915_hw_ppgtt *
-create_vm_for_ctx(struct drm_device *dev, struct i915_hw_context *ctx)
+create_vm_for_ctx(struct drm_device *dev, struct intel_context *ctx)
 {
        struct i915_hw_ppgtt *ppgtt;
        int ret;
@@ -218,12 +218,12 @@ create_vm_for_ctx(struct drm_device *dev, struct i915_hw_context *ctx)
        return ppgtt;
 }
 
-static struct i915_hw_context *
+static struct intel_context *
 __create_hw_context(struct drm_device *dev,
                  struct drm_i915_file_private *file_priv)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct i915_hw_context *ctx;
+       struct intel_context *ctx;
        int ret;
 
        ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
@@ -240,7 +240,15 @@ __create_hw_context(struct drm_device *dev,
                        goto err_out;
                }
 
-               if (INTEL_INFO(dev)->gen >= 7) {
+               /*
+                * Try to make the context utilize L3 as well as LLC.
+                *
+                * On VLV we don't have L3 controls in the PTEs so we
+                * shouldn't touch the cache level, especially as that
+                * would make the object snooped which might have a
+                * negative performance impact.
+                */
+               if (INTEL_INFO(dev)->gen >= 7 && !IS_VALLEYVIEW(dev)) {
                        ret = i915_gem_object_set_cache_level(ctx->obj,
                                                              I915_CACHE_L3_LLC);
                        /* Failure shouldn't ever happen this early */
@@ -277,14 +285,14 @@ err_out:
  * context state of the GPU for applications that don't utilize HW contexts, as
  * well as an idle case.
  */
-static struct i915_hw_context *
+static struct intel_context *
 i915_gem_create_context(struct drm_device *dev,
                        struct drm_i915_file_private *file_priv,
                        bool create_vm)
 {
        const bool is_global_default_ctx = file_priv == NULL;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct i915_hw_context *ctx;
+       struct intel_context *ctx;
        int ret = 0;
 
        BUG_ON(!mutex_is_locked(&dev->struct_mutex));
@@ -356,8 +364,8 @@ void i915_gem_context_reset(struct drm_device *dev)
        /* Prevent the hardware from restoring the last context (which hung) on
         * the next switch */
        for (i = 0; i < I915_NUM_RINGS; i++) {
-               struct intel_ring_buffer *ring = &dev_priv->ring[i];
-               struct i915_hw_context *dctx = ring->default_context;
+               struct intel_engine_cs *ring = &dev_priv->ring[i];
+               struct intel_context *dctx = ring->default_context;
 
                /* Do a fake switch to the default context */
                if (ring->last_context == dctx)
@@ -383,7 +391,7 @@ void i915_gem_context_reset(struct drm_device *dev)
 int i915_gem_context_init(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct i915_hw_context *ctx;
+       struct intel_context *ctx;
        int i;
 
        /* Init should only be called once per module load. Eventually the
@@ -418,7 +426,7 @@ int 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;
+       struct intel_context *dctx = dev_priv->ring[RCS].default_context;
        int i;
 
        if (dctx->obj) {
@@ -441,10 +449,12 @@ void i915_gem_context_fini(struct drm_device *dev)
                        i915_gem_context_unreference(dctx);
                        dev_priv->ring[RCS].last_context = NULL;
                }
+
+               i915_gem_object_ggtt_unpin(dctx->obj);
        }
 
        for (i = 0; i < I915_NUM_RINGS; i++) {
-               struct intel_ring_buffer *ring = &dev_priv->ring[i];
+               struct intel_engine_cs *ring = &dev_priv->ring[i];
 
                if (ring->last_context)
                        i915_gem_context_unreference(ring->last_context);
@@ -453,13 +463,12 @@ void i915_gem_context_fini(struct drm_device *dev)
                ring->last_context = NULL;
        }
 
-       i915_gem_object_ggtt_unpin(dctx->obj);
        i915_gem_context_unreference(dctx);
 }
 
 int i915_gem_context_enable(struct drm_i915_private *dev_priv)
 {
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        int ret, i;
 
        /* This is the only place the aliasing PPGTT gets enabled, which means
@@ -486,11 +495,7 @@ int i915_gem_context_enable(struct drm_i915_private *dev_priv)
 
 static int context_idr_cleanup(int id, void *p, void *data)
 {
-       struct i915_hw_context *ctx = p;
-
-       /* Ignore the default context because close will handle it */
-       if (i915_gem_context_is_default(ctx))
-               return 0;
+       struct intel_context *ctx = p;
 
        i915_gem_context_unreference(ctx);
        return 0;
@@ -499,17 +504,17 @@ static int context_idr_cleanup(int id, void *p, void *data)
 int i915_gem_context_open(struct drm_device *dev, struct drm_file *file)
 {
        struct drm_i915_file_private *file_priv = file->driver_priv;
+       struct intel_context *ctx;
 
        idr_init(&file_priv->context_idr);
 
        mutex_lock(&dev->struct_mutex);
-       file_priv->private_default_ctx =
-               i915_gem_create_context(dev, file_priv, USES_FULL_PPGTT(dev));
+       ctx = i915_gem_create_context(dev, file_priv, USES_FULL_PPGTT(dev));
        mutex_unlock(&dev->struct_mutex);
 
-       if (IS_ERR(file_priv->private_default_ctx)) {
+       if (IS_ERR(ctx)) {
                idr_destroy(&file_priv->context_idr);
-               return PTR_ERR(file_priv->private_default_ctx);
+               return PTR_ERR(ctx);
        }
 
        return 0;
@@ -521,16 +526,14 @@ void i915_gem_context_close(struct drm_device *dev, struct drm_file *file)
 
        idr_for_each(&file_priv->context_idr, context_idr_cleanup, NULL);
        idr_destroy(&file_priv->context_idr);
-
-       i915_gem_context_unreference(file_priv->private_default_ctx);
 }
 
-struct i915_hw_context *
+struct intel_context *
 i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id)
 {
-       struct i915_hw_context *ctx;
+       struct intel_context *ctx;
 
-       ctx = (struct i915_hw_context *)idr_find(&file_priv->context_idr, id);
+       ctx = (struct intel_context *)idr_find(&file_priv->context_idr, id);
        if (!ctx)
                return ERR_PTR(-ENOENT);
 
@@ -538,8 +541,8 @@ i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id)
 }
 
 static inline int
-mi_set_context(struct intel_ring_buffer *ring,
-              struct i915_hw_context *new_context,
+mi_set_context(struct intel_engine_cs *ring,
+              struct intel_context *new_context,
               u32 hw_flags)
 {
        int ret;
@@ -549,7 +552,7 @@ mi_set_context(struct intel_ring_buffer *ring,
         * explicitly, so we rely on the value at ring init, stored in
         * itlb_before_ctx_switch.
         */
-       if (IS_GEN6(ring->dev) && ring->itlb_before_ctx_switch) {
+       if (IS_GEN6(ring->dev)) {
                ret = ring->flush(ring, I915_GEM_GPU_DOMAINS, 0);
                if (ret)
                        return ret;
@@ -559,8 +562,8 @@ mi_set_context(struct intel_ring_buffer *ring,
        if (ret)
                return ret;
 
-       /* WaProgramMiArbOnOffAroundMiSetContext:ivb,vlv,hsw */
-       if (IS_GEN7(ring->dev))
+       /* WaProgramMiArbOnOffAroundMiSetContext:ivb,vlv,hsw,bdw,chv */
+       if (INTEL_INFO(ring->dev)->gen >= 7)
                intel_ring_emit(ring, MI_ARB_ON_OFF | MI_ARB_DISABLE);
        else
                intel_ring_emit(ring, MI_NOOP);
@@ -578,7 +581,7 @@ mi_set_context(struct intel_ring_buffer *ring,
         */
        intel_ring_emit(ring, MI_NOOP);
 
-       if (IS_GEN7(ring->dev))
+       if (INTEL_INFO(ring->dev)->gen >= 7)
                intel_ring_emit(ring, MI_ARB_ON_OFF | MI_ARB_ENABLE);
        else
                intel_ring_emit(ring, MI_NOOP);
@@ -588,11 +591,11 @@ mi_set_context(struct intel_ring_buffer *ring,
        return ret;
 }
 
-static int do_switch(struct intel_ring_buffer *ring,
-                    struct i915_hw_context *to)
+static int do_switch(struct intel_engine_cs *ring,
+                    struct intel_context *to)
 {
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
-       struct i915_hw_context *from = ring->last_context;
+       struct intel_context *from = ring->last_context;
        struct i915_hw_ppgtt *ppgtt = ctx_to_ppgtt(to);
        u32 hw_flags = 0;
        int ret, i;
@@ -693,13 +696,19 @@ static int do_switch(struct intel_ring_buffer *ring,
                i915_gem_context_unreference(from);
        }
 
-       to->is_initialized = true;
-
 done:
        i915_gem_context_reference(to);
        ring->last_context = to;
        to->last_ring = ring;
 
+       if (ring->id == RCS && !to->is_initialized && from == NULL) {
+               ret = i915_gem_render_state_init(ring);
+               if (ret)
+                       DRM_ERROR("init render state: %d\n", ret);
+       }
+
+       to->is_initialized = true;
+
        return 0;
 
 unpin_out:
@@ -718,8 +727,8 @@ unpin_out:
  * it will have a refoucnt > 1. This allows us to destroy the context abstract
  * object while letting the normal object tracking destroy the backing BO.
  */
-int i915_switch_context(struct intel_ring_buffer *ring,
-                       struct i915_hw_context *to)
+int i915_switch_context(struct intel_engine_cs *ring,
+                       struct intel_context *to)
 {
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
 
@@ -748,7 +757,7 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
 {
        struct drm_i915_gem_context_create *args = data;
        struct drm_i915_file_private *file_priv = file->driver_priv;
-       struct i915_hw_context *ctx;
+       struct intel_context *ctx;
        int ret;
 
        if (!hw_context_enabled(dev))
@@ -774,7 +783,7 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data,
 {
        struct drm_i915_gem_context_destroy *args = data;
        struct drm_i915_file_private *file_priv = file->driver_priv;
-       struct i915_hw_context *ctx;
+       struct intel_context *ctx;
        int ret;
 
        if (args->ctx_id == DEFAULT_CONTEXT_ID)
index 9bb533e0d76234cdc14ec6d9da31a310114080a5..580aa42443edc87823dc4fb8f3776ea0d39b6b52 100644 (file)
@@ -161,12 +161,8 @@ static void i915_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr)
 {
        struct drm_i915_gem_object *obj = dma_buf_to_obj(dma_buf);
        struct drm_device *dev = obj->base.dev;
-       int ret;
-
-       ret = i915_mutex_lock_interruptible(dev);
-       if (ret)
-               return;
 
+       mutex_lock(&dev->struct_mutex);
        if (--obj->vmapping_count == 0) {
                vunmap(obj->dma_buf_vmapping);
                obj->dma_buf_vmapping = NULL;
@@ -233,6 +229,14 @@ static const struct dma_buf_ops i915_dmabuf_ops =  {
 struct dma_buf *i915_gem_prime_export(struct drm_device *dev,
                                      struct drm_gem_object *gem_obj, int flags)
 {
+       struct drm_i915_gem_object *obj = to_intel_bo(gem_obj);
+
+       if (obj->ops->dmabuf_export) {
+               int ret = obj->ops->dmabuf_export(obj);
+               if (ret)
+                       return ERR_PTR(ret);
+       }
+
        return dma_buf_export(gem_obj, &i915_dmabuf_ops, gem_obj->size, flags);
 }
 
index 20fef6c502676b2c82bdd19cbe343f5f864b0ccc..3a30133f93e858a449366727c266a4093485e085 100644 (file)
@@ -265,10 +265,12 @@ static inline int use_cpu_reloc(struct drm_i915_gem_object *obj)
 
 static int
 relocate_entry_cpu(struct drm_i915_gem_object *obj,
-                  struct drm_i915_gem_relocation_entry *reloc)
+                  struct drm_i915_gem_relocation_entry *reloc,
+                  uint64_t target_offset)
 {
        struct drm_device *dev = obj->base.dev;
        uint32_t page_offset = offset_in_page(reloc->offset);
+       uint64_t delta = reloc->delta + target_offset;
        char *vaddr;
        int ret;
 
@@ -278,7 +280,7 @@ relocate_entry_cpu(struct drm_i915_gem_object *obj,
 
        vaddr = kmap_atomic(i915_gem_object_get_page(obj,
                                reloc->offset >> PAGE_SHIFT));
-       *(uint32_t *)(vaddr + page_offset) = reloc->delta;
+       *(uint32_t *)(vaddr + page_offset) = lower_32_bits(delta);
 
        if (INTEL_INFO(dev)->gen >= 8) {
                page_offset = offset_in_page(page_offset + sizeof(uint32_t));
@@ -289,7 +291,7 @@ relocate_entry_cpu(struct drm_i915_gem_object *obj,
                            (reloc->offset + sizeof(uint32_t)) >> PAGE_SHIFT));
                }
 
-               *(uint32_t *)(vaddr + page_offset) = 0;
+               *(uint32_t *)(vaddr + page_offset) = upper_32_bits(delta);
        }
 
        kunmap_atomic(vaddr);
@@ -299,10 +301,12 @@ relocate_entry_cpu(struct drm_i915_gem_object *obj,
 
 static int
 relocate_entry_gtt(struct drm_i915_gem_object *obj,
-                  struct drm_i915_gem_relocation_entry *reloc)
+                  struct drm_i915_gem_relocation_entry *reloc,
+                  uint64_t target_offset)
 {
        struct drm_device *dev = obj->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       uint64_t delta = reloc->delta + target_offset;
        uint32_t __iomem *reloc_entry;
        void __iomem *reloc_page;
        int ret;
@@ -321,7 +325,7 @@ relocate_entry_gtt(struct drm_i915_gem_object *obj,
                        reloc->offset & PAGE_MASK);
        reloc_entry = (uint32_t __iomem *)
                (reloc_page + offset_in_page(reloc->offset));
-       iowrite32(reloc->delta, reloc_entry);
+       iowrite32(lower_32_bits(delta), reloc_entry);
 
        if (INTEL_INFO(dev)->gen >= 8) {
                reloc_entry += 1;
@@ -334,7 +338,7 @@ relocate_entry_gtt(struct drm_i915_gem_object *obj,
                        reloc_entry = reloc_page;
                }
 
-               iowrite32(0, reloc_entry);
+               iowrite32(upper_32_bits(delta), reloc_entry);
        }
 
        io_mapping_unmap_atomic(reloc_page);
@@ -351,7 +355,7 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
        struct drm_gem_object *target_obj;
        struct drm_i915_gem_object *target_i915_obj;
        struct i915_vma *target_vma;
-       uint32_t target_offset;
+       uint64_t target_offset;
        int ret;
 
        /* we've already hold a reference to all valid objects */
@@ -429,11 +433,10 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
        if (obj->active && in_atomic())
                return -EFAULT;
 
-       reloc->delta += target_offset;
        if (use_cpu_reloc(obj))
-               ret = relocate_entry_cpu(obj, reloc);
+               ret = relocate_entry_cpu(obj, reloc, target_offset);
        else
-               ret = relocate_entry_gtt(obj, reloc);
+               ret = relocate_entry_gtt(obj, reloc, target_offset);
 
        if (ret)
                return ret;
@@ -541,7 +544,7 @@ need_reloc_mappable(struct i915_vma *vma)
 
 static int
 i915_gem_execbuffer_reserve_vma(struct i915_vma *vma,
-                               struct intel_ring_buffer *ring,
+                               struct intel_engine_cs *ring,
                                bool *need_reloc)
 {
        struct drm_i915_gem_object *obj = vma->obj;
@@ -628,7 +631,7 @@ eb_vma_misplaced(struct i915_vma *vma, bool has_fenced_gpu_access)
 }
 
 static int
-i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
+i915_gem_execbuffer_reserve(struct intel_engine_cs *ring,
                            struct list_head *vmas,
                            bool *need_relocs)
 {
@@ -642,6 +645,8 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
        if (list_empty(vmas))
                return 0;
 
+       i915_gem_retire_requests_ring(ring);
+
        vm = list_first_entry(vmas, struct i915_vma, exec_list)->vm;
 
        INIT_LIST_HEAD(&ordered_vmas);
@@ -727,7 +732,7 @@ static int
 i915_gem_execbuffer_relocate_slow(struct drm_device *dev,
                                  struct drm_i915_gem_execbuffer2 *args,
                                  struct drm_file *file,
-                                 struct intel_ring_buffer *ring,
+                                 struct intel_engine_cs *ring,
                                  struct eb_vmas *eb,
                                  struct drm_i915_gem_exec_object2 *exec)
 {
@@ -843,7 +848,7 @@ err:
 }
 
 static int
-i915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring,
+i915_gem_execbuffer_move_to_gpu(struct intel_engine_cs *ring,
                                struct list_head *vmas)
 {
        struct i915_vma *vma;
@@ -926,11 +931,11 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec,
        return 0;
 }
 
-static struct i915_hw_context *
+static struct intel_context *
 i915_gem_validate_context(struct drm_device *dev, struct drm_file *file,
-                         struct intel_ring_buffer *ring, const u32 ctx_id)
+                         struct intel_engine_cs *ring, const u32 ctx_id)
 {
-       struct i915_hw_context *ctx = NULL;
+       struct intel_context *ctx = NULL;
        struct i915_ctx_hang_stats *hs;
 
        if (ring->id != RCS && ctx_id != DEFAULT_CONTEXT_ID)
@@ -951,7 +956,7 @@ i915_gem_validate_context(struct drm_device *dev, struct drm_file *file,
 
 static void
 i915_gem_execbuffer_move_to_active(struct list_head *vmas,
-                                  struct intel_ring_buffer *ring)
+                                  struct intel_engine_cs *ring)
 {
        struct i915_vma *vma;
 
@@ -974,6 +979,9 @@ i915_gem_execbuffer_move_to_active(struct list_head *vmas,
                        if (i915_gem_obj_ggtt_bound(obj) &&
                            i915_gem_obj_to_ggtt(obj)->pin_count)
                                intel_mark_fb_busy(obj, ring);
+
+                       /* update for the implicit flush after a batch */
+                       obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS;
                }
 
                trace_i915_gem_object_change_domain(obj, old_read, old_write);
@@ -983,7 +991,7 @@ i915_gem_execbuffer_move_to_active(struct list_head *vmas,
 static void
 i915_gem_execbuffer_retire_commands(struct drm_device *dev,
                                    struct drm_file *file,
-                                   struct intel_ring_buffer *ring,
+                                   struct intel_engine_cs *ring,
                                    struct drm_i915_gem_object *obj)
 {
        /* Unconditionally force add_request to emit a full flush. */
@@ -995,13 +1003,15 @@ i915_gem_execbuffer_retire_commands(struct drm_device *dev,
 
 static int
 i915_reset_gen7_sol_offsets(struct drm_device *dev,
-                           struct intel_ring_buffer *ring)
+                           struct intel_engine_cs *ring)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        int ret, i;
 
-       if (!IS_GEN7(dev) || ring != &dev_priv->ring[RCS])
-               return 0;
+       if (!IS_GEN7(dev) || ring != &dev_priv->ring[RCS]) {
+               DRM_DEBUG("sol reset is gen7/rcs only\n");
+               return -EINVAL;
+       }
 
        ret = intel_ring_begin(ring, 4 * 3);
        if (ret)
@@ -1018,6 +1028,37 @@ i915_reset_gen7_sol_offsets(struct drm_device *dev,
        return 0;
 }
 
+/**
+ * Find one BSD ring to dispatch the corresponding BSD command.
+ * The Ring ID is returned.
+ */
+static int gen8_dispatch_bsd_ring(struct drm_device *dev,
+                                 struct drm_file *file)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_i915_file_private *file_priv = file->driver_priv;
+
+       /* Check whether the file_priv is using one ring */
+       if (file_priv->bsd_ring)
+               return file_priv->bsd_ring->id;
+       else {
+               /* If no, use the ping-pong mechanism to select one ring */
+               int ring_id;
+
+               mutex_lock(&dev->struct_mutex);
+               if (dev_priv->mm.bsd_ring_dispatch_index == 0) {
+                       ring_id = VCS;
+                       dev_priv->mm.bsd_ring_dispatch_index = 1;
+               } else {
+                       ring_id = VCS2;
+                       dev_priv->mm.bsd_ring_dispatch_index = 0;
+               }
+               file_priv->bsd_ring = &dev_priv->ring[ring_id];
+               mutex_unlock(&dev->struct_mutex);
+               return ring_id;
+       }
+}
+
 static struct drm_i915_gem_object *
 eb_get_batch(struct eb_vmas *eb)
 {
@@ -1047,11 +1088,11 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
        struct eb_vmas *eb;
        struct drm_i915_gem_object *batch_obj;
        struct drm_clip_rect *cliprects = NULL;
-       struct intel_ring_buffer *ring;
-       struct i915_hw_context *ctx;
+       struct intel_engine_cs *ring;
+       struct intel_context *ctx;
        struct i915_address_space *vm;
        const u32 ctx_id = i915_execbuffer2_get_context_id(*args);
-       u32 exec_start = args->batch_start_offset, exec_len;
+       u64 exec_start = args->batch_start_offset, exec_len;
        u32 mask, flags;
        int ret, mode, i;
        bool need_relocs;
@@ -1073,7 +1114,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
        if (args->flags & I915_EXEC_IS_PINNED)
                flags |= I915_DISPATCH_PINNED;
 
-       if ((args->flags & I915_EXEC_RING_MASK) > I915_NUM_RINGS) {
+       if ((args->flags & I915_EXEC_RING_MASK) > LAST_USER_RING) {
                DRM_DEBUG("execbuf with unknown ring: %d\n",
                          (int)(args->flags & I915_EXEC_RING_MASK));
                return -EINVAL;
@@ -1081,7 +1122,14 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
 
        if ((args->flags & I915_EXEC_RING_MASK) == I915_EXEC_DEFAULT)
                ring = &dev_priv->ring[RCS];
-       else
+       else if ((args->flags & I915_EXEC_RING_MASK) == I915_EXEC_BSD) {
+               if (HAS_BSD2(dev)) {
+                       int ring_id;
+                       ring_id = gen8_dispatch_bsd_ring(dev, file);
+                       ring = &dev_priv->ring[ring_id];
+               } else
+                       ring = &dev_priv->ring[VCS];
+       } else
                ring = &dev_priv->ring[(args->flags & I915_EXEC_RING_MASK) - 1];
 
        if (!intel_ring_initialized(ring)) {
@@ -1096,14 +1144,22 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
        case I915_EXEC_CONSTANTS_REL_GENERAL:
        case I915_EXEC_CONSTANTS_ABSOLUTE:
        case I915_EXEC_CONSTANTS_REL_SURFACE:
-               if (ring == &dev_priv->ring[RCS] &&
-                   mode != dev_priv->relative_constants_mode) {
-                       if (INTEL_INFO(dev)->gen < 4)
+               if (mode != 0 && ring != &dev_priv->ring[RCS]) {
+                       DRM_DEBUG("non-0 rel constants mode on non-RCS\n");
+                       return -EINVAL;
+               }
+
+               if (mode != dev_priv->relative_constants_mode) {
+                       if (INTEL_INFO(dev)->gen < 4) {
+                               DRM_DEBUG("no rel constants on pre-gen4\n");
                                return -EINVAL;
+                       }
 
                        if (INTEL_INFO(dev)->gen > 5 &&
-                           mode == I915_EXEC_CONSTANTS_REL_SURFACE)
+                           mode == I915_EXEC_CONSTANTS_REL_SURFACE) {
+                               DRM_DEBUG("rel surface constants mode invalid on gen5+\n");
                                return -EINVAL;
+                       }
 
                        /* The HW changed the meaning on this bit on gen6 */
                        if (INTEL_INFO(dev)->gen >= 6)
@@ -1151,6 +1207,16 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
                        ret = -EFAULT;
                        goto pre_mutex_err;
                }
+       } else {
+               if (args->DR4 == 0xffffffff) {
+                       DRM_DEBUG("UXA submitting garbage DR4, fixing up\n");
+                       args->DR4 = 0;
+               }
+
+               if (args->DR1 || args->DR4 || args->cliprects_ptr) {
+                       DRM_DEBUG("0 cliprects but dirt in cliprects fields\n");
+                       return -EINVAL;
+               }
        }
 
        intel_runtime_pm_get(dev_priv);
@@ -1170,7 +1236,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
                mutex_unlock(&dev->struct_mutex);
                ret = PTR_ERR(ctx);
                goto pre_mutex_err;
-       } 
+       }
 
        i915_gem_context_reference(ctx);
 
@@ -1180,6 +1246,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
 
        eb = eb_create(args);
        if (eb == NULL) {
+               i915_gem_context_unreference(ctx);
                mutex_unlock(&dev->struct_mutex);
                ret = -ENOMEM;
                goto pre_mutex_err;
@@ -1430,6 +1497,11 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
                return -EINVAL;
        }
 
+       if (args->rsvd2 != 0) {
+               DRM_DEBUG("dirty rvsd2 field\n");
+               return -EINVAL;
+       }
+
        exec2_list = kmalloc(sizeof(*exec2_list)*args->buffer_count,
                             GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY);
        if (exec2_list == NULL)
index 5deb22864c522a6513de4c49742e725eef987d9b..eec820aec0224a975325a439c350e57036bd5dc9 100644 (file)
@@ -30,7 +30,8 @@
 #include "i915_trace.h"
 #include "intel_drv.h"
 
-static void gen8_setup_private_ppat(struct drm_i915_private *dev_priv);
+static void bdw_setup_private_ppat(struct drm_i915_private *dev_priv);
+static void chv_setup_private_ppat(struct drm_i915_private *dev_priv);
 
 bool intel_enable_ppgtt(struct drm_device *dev, bool full)
 {
@@ -65,59 +66,6 @@ static int sanitize_enable_ppgtt(struct drm_device *dev, int enable_ppgtt)
        return HAS_ALIASING_PPGTT(dev) ? 1 : 0;
 }
 
-#define GEN6_PPGTT_PD_ENTRIES 512
-#define I915_PPGTT_PT_ENTRIES (PAGE_SIZE / sizeof(gen6_gtt_pte_t))
-typedef uint64_t gen8_gtt_pte_t;
-typedef gen8_gtt_pte_t gen8_ppgtt_pde_t;
-
-/* PPGTT stuff */
-#define GEN6_GTT_ADDR_ENCODE(addr)     ((addr) | (((addr) >> 28) & 0xff0))
-#define HSW_GTT_ADDR_ENCODE(addr)      ((addr) | (((addr) >> 28) & 0x7f0))
-
-#define GEN6_PDE_VALID                 (1 << 0)
-/* gen6+ has bit 11-4 for physical addr bit 39-32 */
-#define GEN6_PDE_ADDR_ENCODE(addr)     GEN6_GTT_ADDR_ENCODE(addr)
-
-#define GEN6_PTE_VALID                 (1 << 0)
-#define GEN6_PTE_UNCACHED              (1 << 1)
-#define HSW_PTE_UNCACHED               (0)
-#define GEN6_PTE_CACHE_LLC             (2 << 1)
-#define GEN7_PTE_CACHE_L3_LLC          (3 << 1)
-#define GEN6_PTE_ADDR_ENCODE(addr)     GEN6_GTT_ADDR_ENCODE(addr)
-#define HSW_PTE_ADDR_ENCODE(addr)      HSW_GTT_ADDR_ENCODE(addr)
-
-/* Cacheability Control is a 4-bit value. The low three bits are stored in *
- * bits 3:1 of the PTE, while the fourth bit is stored in bit 11 of the PTE.
- */
-#define HSW_CACHEABILITY_CONTROL(bits) ((((bits) & 0x7) << 1) | \
-                                        (((bits) & 0x8) << (11 - 3)))
-#define HSW_WB_LLC_AGE3                        HSW_CACHEABILITY_CONTROL(0x2)
-#define HSW_WB_LLC_AGE0                        HSW_CACHEABILITY_CONTROL(0x3)
-#define HSW_WB_ELLC_LLC_AGE0           HSW_CACHEABILITY_CONTROL(0xb)
-#define HSW_WB_ELLC_LLC_AGE3           HSW_CACHEABILITY_CONTROL(0x8)
-#define HSW_WT_ELLC_LLC_AGE0           HSW_CACHEABILITY_CONTROL(0x6)
-#define HSW_WT_ELLC_LLC_AGE3           HSW_CACHEABILITY_CONTROL(0x7)
-
-#define GEN8_PTES_PER_PAGE             (PAGE_SIZE / sizeof(gen8_gtt_pte_t))
-#define GEN8_PDES_PER_PAGE             (PAGE_SIZE / sizeof(gen8_ppgtt_pde_t))
-
-/* GEN8 legacy style addressis defined as a 3 level page table:
- * 31:30 | 29:21 | 20:12 |  11:0
- * PDPE  |  PDE  |  PTE  | offset
- * The difference as compared to normal x86 3 level page table is the PDPEs are
- * programmed via register.
- */
-#define GEN8_PDPE_SHIFT                        30
-#define GEN8_PDPE_MASK                 0x3
-#define GEN8_PDE_SHIFT                 21
-#define GEN8_PDE_MASK                  0x1ff
-#define GEN8_PTE_SHIFT                 12
-#define GEN8_PTE_MASK                  0x1ff
-
-#define PPAT_UNCACHED_INDEX            (_PAGE_PWT | _PAGE_PCD)
-#define PPAT_CACHED_PDE_INDEX          0 /* WB LLC */
-#define PPAT_CACHED_INDEX              _PAGE_PAT /* WB LLCeLLC */
-#define PPAT_DISPLAY_ELLC_INDEX                _PAGE_PCD /* WT eLLC */
 
 static void ppgtt_bind_vma(struct i915_vma *vma,
                           enum i915_cache_level cache_level,
@@ -131,10 +79,19 @@ static inline gen8_gtt_pte_t gen8_pte_encode(dma_addr_t addr,
 {
        gen8_gtt_pte_t pte = valid ? _PAGE_PRESENT | _PAGE_RW : 0;
        pte |= addr;
-       if (level != I915_CACHE_NONE)
-               pte |= PPAT_CACHED_INDEX;
-       else
+
+       switch (level) {
+       case I915_CACHE_NONE:
                pte |= PPAT_UNCACHED_INDEX;
+               break;
+       case I915_CACHE_WT:
+               pte |= PPAT_DISPLAY_ELLC_INDEX;
+               break;
+       default:
+               pte |= PPAT_CACHED_INDEX;
+               break;
+       }
+
        return pte;
 }
 
@@ -197,9 +154,6 @@ static gen6_gtt_pte_t ivb_pte_encode(dma_addr_t addr,
        return pte;
 }
 
-#define BYT_PTE_WRITEABLE              (1 << 1)
-#define BYT_PTE_SNOOPED_BY_CPU_CACHES  (1 << 2)
-
 static gen6_gtt_pte_t byt_pte_encode(dma_addr_t addr,
                                     enum i915_cache_level level,
                                     bool valid)
@@ -253,7 +207,7 @@ static gen6_gtt_pte_t iris_pte_encode(dma_addr_t addr,
 }
 
 /* Broadwell Page Directory Pointer Descriptors */
-static int gen8_write_pdp(struct intel_ring_buffer *ring, unsigned entry,
+static int gen8_write_pdp(struct intel_engine_cs *ring, unsigned entry,
                           uint64_t val, bool synchronous)
 {
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
@@ -283,7 +237,7 @@ static int gen8_write_pdp(struct intel_ring_buffer *ring, unsigned entry,
 }
 
 static int gen8_mm_switch(struct i915_hw_ppgtt *ppgtt,
-                         struct intel_ring_buffer *ring,
+                         struct intel_engine_cs *ring,
                          bool synchronous)
 {
        int i, ret;
@@ -332,6 +286,8 @@ static void gen8_ppgtt_clear_range(struct i915_address_space *vm,
                        num_entries--;
                }
 
+               if (!HAS_LLC(ppgtt->base.dev))
+                       drm_clflush_virt_range(pt_vaddr, PAGE_SIZE);
                kunmap_atomic(pt_vaddr);
 
                pte = 0;
@@ -368,6 +324,8 @@ static void gen8_ppgtt_insert_entries(struct i915_address_space *vm,
                        gen8_pte_encode(sg_page_iter_dma_address(&sg_iter),
                                        cache_level, true);
                if (++pte == GEN8_PTES_PER_PAGE) {
+                       if (!HAS_LLC(ppgtt->base.dev))
+                               drm_clflush_virt_range(pt_vaddr, PAGE_SIZE);
                        kunmap_atomic(pt_vaddr);
                        pt_vaddr = NULL;
                        if (++pde == GEN8_PDES_PER_PAGE) {
@@ -377,8 +335,11 @@ static void gen8_ppgtt_insert_entries(struct i915_address_space *vm,
                        pte = 0;
                }
        }
-       if (pt_vaddr)
+       if (pt_vaddr) {
+               if (!HAS_LLC(ppgtt->base.dev))
+                       drm_clflush_virt_range(pt_vaddr, PAGE_SIZE);
                kunmap_atomic(pt_vaddr);
+       }
 }
 
 static void gen8_free_page_tables(struct page **pt_pages)
@@ -641,6 +602,8 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt, uint64_t size)
                        pd_vaddr[j] = gen8_pde_encode(ppgtt->base.dev, addr,
                                                      I915_CACHE_LLC);
                }
+               if (!HAS_LLC(ppgtt->base.dev))
+                       drm_clflush_virt_range(pd_vaddr, PAGE_SIZE);
                kunmap_atomic(pd_vaddr);
        }
 
@@ -753,7 +716,7 @@ static uint32_t get_pd_offset(struct i915_hw_ppgtt *ppgtt)
 }
 
 static int hsw_mm_switch(struct i915_hw_ppgtt *ppgtt,
-                        struct intel_ring_buffer *ring,
+                        struct intel_engine_cs *ring,
                         bool synchronous)
 {
        struct drm_device *dev = ppgtt->base.dev;
@@ -797,7 +760,7 @@ static int hsw_mm_switch(struct i915_hw_ppgtt *ppgtt,
 }
 
 static int gen7_mm_switch(struct i915_hw_ppgtt *ppgtt,
-                         struct intel_ring_buffer *ring,
+                         struct intel_engine_cs *ring,
                          bool synchronous)
 {
        struct drm_device *dev = ppgtt->base.dev;
@@ -848,7 +811,7 @@ static int gen7_mm_switch(struct i915_hw_ppgtt *ppgtt,
 }
 
 static int gen6_mm_switch(struct i915_hw_ppgtt *ppgtt,
-                         struct intel_ring_buffer *ring,
+                         struct intel_engine_cs *ring,
                          bool synchronous)
 {
        struct drm_device *dev = ppgtt->base.dev;
@@ -869,7 +832,7 @@ static int gen8_ppgtt_enable(struct i915_hw_ppgtt *ppgtt)
 {
        struct drm_device *dev = ppgtt->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        int j, ret;
 
        for_each_ring(ring, dev_priv, j) {
@@ -899,7 +862,7 @@ static int gen7_ppgtt_enable(struct i915_hw_ppgtt *ppgtt)
 {
        struct drm_device *dev = ppgtt->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        uint32_t ecochk, ecobits;
        int i;
 
@@ -938,7 +901,7 @@ static int gen6_ppgtt_enable(struct i915_hw_ppgtt *ppgtt)
 {
        struct drm_device *dev = ppgtt->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        uint32_t ecochk, gab_ctl, ecobits;
        int i;
 
@@ -1067,8 +1030,6 @@ static void gen6_ppgtt_cleanup(struct i915_address_space *vm)
 
 static int gen6_ppgtt_allocate_page_directories(struct i915_hw_ppgtt *ppgtt)
 {
-#define GEN6_PD_ALIGN (PAGE_SIZE * 16)
-#define GEN6_PD_SIZE (GEN6_PPGTT_PD_ENTRIES * PAGE_SIZE)
        struct drm_device *dev = ppgtt->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        bool retried = false;
@@ -1084,8 +1045,7 @@ alloc:
                                                  &ppgtt->node, GEN6_PD_SIZE,
                                                  GEN6_PD_ALIGN, 0,
                                                  0, dev_priv->gtt.base.total,
-                                                 DRM_MM_SEARCH_DEFAULT,
-                                                 DRM_MM_CREATE_DEFAULT);
+                                                 DRM_MM_TOPDOWN);
        if (ret == -ENOSPC && !retried) {
                ret = i915_gem_evict_something(dev, &dev_priv->gtt.base,
                                               GEN6_PD_SIZE, GEN6_PD_ALIGN,
@@ -1311,7 +1271,7 @@ static void undo_idling(struct drm_i915_private *dev_priv, bool interruptible)
 void i915_check_and_clear_faults(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        int i;
 
        if (INTEL_INFO(dev)->gen < 6)
@@ -1386,7 +1346,11 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev)
 
 
        if (INTEL_INFO(dev)->gen >= 8) {
-               gen8_setup_private_ppat(dev_priv);
+               if (IS_CHERRYVIEW(dev))
+                       chv_setup_private_ppat(dev_priv);
+               else
+                       bdw_setup_private_ppat(dev_priv);
+
                return;
        }
 
@@ -1438,7 +1402,7 @@ static void gen8_ggtt_insert_entries(struct i915_address_space *vm,
                (gen8_gtt_pte_t __iomem *)dev_priv->gtt.gsm + first_entry;
        int i = 0;
        struct sg_page_iter sg_iter;
-       dma_addr_t addr;
+       dma_addr_t addr = 0;
 
        for_each_sg_page(st->sgl, &sg_iter, st->nents, 0) {
                addr = sg_dma_address(sg_iter.sg) +
@@ -1811,9 +1775,27 @@ static inline unsigned int gen8_get_total_gtt_size(u16 bdw_gmch_ctl)
        bdw_gmch_ctl &= BDW_GMCH_GGMS_MASK;
        if (bdw_gmch_ctl)
                bdw_gmch_ctl = 1 << bdw_gmch_ctl;
+
+#ifdef CONFIG_X86_32
+       /* Limit 32b platforms to a 2GB GGTT: 4 << 20 / pte size * PAGE_SIZE */
+       if (bdw_gmch_ctl > 4)
+               bdw_gmch_ctl = 4;
+#endif
+
        return bdw_gmch_ctl << 20;
 }
 
+static inline unsigned int chv_get_total_gtt_size(u16 gmch_ctrl)
+{
+       gmch_ctrl >>= SNB_GMCH_GGMS_SHIFT;
+       gmch_ctrl &= SNB_GMCH_GGMS_MASK;
+
+       if (gmch_ctrl)
+               return 1 << (20 + gmch_ctrl);
+
+       return 0;
+}
+
 static inline size_t gen6_get_stolen_size(u16 snb_gmch_ctl)
 {
        snb_gmch_ctl >>= SNB_GMCH_GMS_SHIFT;
@@ -1828,6 +1810,24 @@ static inline size_t gen8_get_stolen_size(u16 bdw_gmch_ctl)
        return bdw_gmch_ctl << 25; /* 32 MB units */
 }
 
+static size_t chv_get_stolen_size(u16 gmch_ctrl)
+{
+       gmch_ctrl >>= SNB_GMCH_GMS_SHIFT;
+       gmch_ctrl &= SNB_GMCH_GMS_MASK;
+
+       /*
+        * 0x0  to 0x10: 32MB increments starting at 0MB
+        * 0x11 to 0x16: 4MB increments starting at 8MB
+        * 0x17 to 0x1d: 4MB increments start at 36MB
+        */
+       if (gmch_ctrl < 0x11)
+               return gmch_ctrl << 25;
+       else if (gmch_ctrl < 0x17)
+               return (gmch_ctrl - 0x11 + 2) << 22;
+       else
+               return (gmch_ctrl - 0x17 + 9) << 22;
+}
+
 static int ggtt_probe_common(struct drm_device *dev,
                             size_t gtt_size)
 {
@@ -1858,19 +1858,8 @@ static int ggtt_probe_common(struct drm_device *dev,
 /* The GGTT and PPGTT need a private PPAT setup in order to handle cacheability
  * bits. When using advanced contexts each context stores its own PAT, but
  * writing this data shouldn't be harmful even in those cases. */
-static void gen8_setup_private_ppat(struct drm_i915_private *dev_priv)
-{
-#define GEN8_PPAT_UC           (0<<0)
-#define GEN8_PPAT_WC           (1<<0)
-#define GEN8_PPAT_WT           (2<<0)
-#define GEN8_PPAT_WB           (3<<0)
-#define GEN8_PPAT_ELLC_OVERRIDE        (0<<2)
-/* FIXME(BDW): Bspec is completely confused about cache control bits. */
-#define GEN8_PPAT_LLC          (1<<2)
-#define GEN8_PPAT_LLCELLC      (2<<2)
-#define GEN8_PPAT_LLCeLLC      (3<<2)
-#define GEN8_PPAT_AGE(x)       (x<<4)
-#define GEN8_PPAT(i, x) ((uint64_t) (x) << ((i) * 8))
+static void bdw_setup_private_ppat(struct drm_i915_private *dev_priv)
+{
        uint64_t pat;
 
        pat = GEN8_PPAT(0, GEN8_PPAT_WB | GEN8_PPAT_LLC)     | /* for normal objects, no eLLC */
@@ -1888,6 +1877,33 @@ static void gen8_setup_private_ppat(struct drm_i915_private *dev_priv)
        I915_WRITE(GEN8_PRIVATE_PAT + 4, pat >> 32);
 }
 
+static void chv_setup_private_ppat(struct drm_i915_private *dev_priv)
+{
+       uint64_t pat;
+
+       /*
+        * Map WB on BDW to snooped on CHV.
+        *
+        * Only the snoop bit has meaning for CHV, the rest is
+        * ignored.
+        *
+        * Note that the harware enforces snooping for all page
+        * table accesses. The snoop bit is actually ignored for
+        * PDEs.
+        */
+       pat = GEN8_PPAT(0, CHV_PPAT_SNOOP) |
+             GEN8_PPAT(1, 0) |
+             GEN8_PPAT(2, 0) |
+             GEN8_PPAT(3, 0) |
+             GEN8_PPAT(4, CHV_PPAT_SNOOP) |
+             GEN8_PPAT(5, CHV_PPAT_SNOOP) |
+             GEN8_PPAT(6, CHV_PPAT_SNOOP) |
+             GEN8_PPAT(7, CHV_PPAT_SNOOP);
+
+       I915_WRITE(GEN8_PRIVATE_PAT, pat);
+       I915_WRITE(GEN8_PRIVATE_PAT + 4, pat >> 32);
+}
+
 static int gen8_gmch_probe(struct drm_device *dev,
                           size_t *gtt_total,
                           size_t *stolen,
@@ -1908,12 +1924,20 @@ static int gen8_gmch_probe(struct drm_device *dev,
 
        pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &snb_gmch_ctl);
 
-       *stolen = gen8_get_stolen_size(snb_gmch_ctl);
+       if (IS_CHERRYVIEW(dev)) {
+               *stolen = chv_get_stolen_size(snb_gmch_ctl);
+               gtt_size = chv_get_total_gtt_size(snb_gmch_ctl);
+       } else {
+               *stolen = gen8_get_stolen_size(snb_gmch_ctl);
+               gtt_size = gen8_get_total_gtt_size(snb_gmch_ctl);
+       }
 
-       gtt_size = gen8_get_total_gtt_size(snb_gmch_ctl);
        *gtt_total = (gtt_size / sizeof(gen8_gtt_pte_t)) << PAGE_SHIFT;
 
-       gen8_setup_private_ppat(dev_priv);
+       if (IS_CHERRYVIEW(dev))
+               chv_setup_private_ppat(dev_priv);
+       else
+               bdw_setup_private_ppat(dev_priv);
 
        ret = ggtt_probe_common(dev, gtt_size);
 
@@ -2043,6 +2067,10 @@ int i915_gem_gtt_init(struct drm_device *dev)
                 gtt->base.total >> 20);
        DRM_DEBUG_DRIVER("GMADR size = %ldM\n", gtt->mappable_end >> 20);
        DRM_DEBUG_DRIVER("GTT stolen size = %zdM\n", gtt->stolen_size >> 20);
+#ifdef CONFIG_INTEL_IOMMU
+       if (intel_iommu_gfx_mapped)
+               DRM_INFO("VT-d active for gfx access\n");
+#endif
        /*
         * i915.enable_ppgtt is read-only, so do an early pass to validate the
         * user's requested state against the hardware/driver capabilities.  We
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h
new file mode 100644 (file)
index 0000000..1b96a06
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright © 2014 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.
+ *
+ * Please try to maintain the following order within this file unless it makes
+ * sense to do otherwise. From top to bottom:
+ * 1. typedefs
+ * 2. #defines, and macros
+ * 3. structure definitions
+ * 4. function prototypes
+ *
+ * Within each section, please try to order by generation in ascending order,
+ * from top to bottom (ie. gen6 on the top, gen8 on the bottom).
+ */
+
+#ifndef __I915_GEM_GTT_H__
+#define __I915_GEM_GTT_H__
+
+typedef uint32_t gen6_gtt_pte_t;
+typedef uint64_t gen8_gtt_pte_t;
+typedef gen8_gtt_pte_t gen8_ppgtt_pde_t;
+
+#define gtt_total_entries(gtt) ((gtt).base.total >> PAGE_SHIFT)
+
+#define I915_PPGTT_PT_ENTRIES          (PAGE_SIZE / sizeof(gen6_gtt_pte_t))
+/* gen6-hsw has bit 11-4 for physical addr bit 39-32 */
+#define GEN6_GTT_ADDR_ENCODE(addr)     ((addr) | (((addr) >> 28) & 0xff0))
+#define GEN6_PTE_ADDR_ENCODE(addr)     GEN6_GTT_ADDR_ENCODE(addr)
+#define GEN6_PDE_ADDR_ENCODE(addr)     GEN6_GTT_ADDR_ENCODE(addr)
+#define GEN6_PTE_CACHE_LLC             (2 << 1)
+#define GEN6_PTE_UNCACHED              (1 << 1)
+#define GEN6_PTE_VALID                 (1 << 0)
+
+#define GEN6_PPGTT_PD_ENTRIES          512
+#define GEN6_PD_SIZE                   (GEN6_PPGTT_PD_ENTRIES * PAGE_SIZE)
+#define GEN6_PD_ALIGN                  (PAGE_SIZE * 16)
+#define GEN6_PDE_VALID                 (1 << 0)
+
+#define GEN7_PTE_CACHE_L3_LLC          (3 << 1)
+
+#define BYT_PTE_SNOOPED_BY_CPU_CACHES  (1 << 2)
+#define BYT_PTE_WRITEABLE              (1 << 1)
+
+/* Cacheability Control is a 4-bit value. The low three bits are stored in bits
+ * 3:1 of the PTE, while the fourth bit is stored in bit 11 of the PTE.
+ */
+#define HSW_CACHEABILITY_CONTROL(bits) ((((bits) & 0x7) << 1) | \
+                                        (((bits) & 0x8) << (11 - 3)))
+#define HSW_WB_LLC_AGE3                        HSW_CACHEABILITY_CONTROL(0x2)
+#define HSW_WB_LLC_AGE0                        HSW_CACHEABILITY_CONTROL(0x3)
+#define HSW_WB_ELLC_LLC_AGE3           HSW_CACHEABILITY_CONTROL(0x8)
+#define HSW_WB_ELLC_LLC_AGE0           HSW_CACHEABILITY_CONTROL(0xb)
+#define HSW_WT_ELLC_LLC_AGE3           HSW_CACHEABILITY_CONTROL(0x7)
+#define HSW_WT_ELLC_LLC_AGE0           HSW_CACHEABILITY_CONTROL(0x6)
+#define HSW_PTE_UNCACHED               (0)
+#define HSW_GTT_ADDR_ENCODE(addr)      ((addr) | (((addr) >> 28) & 0x7f0))
+#define HSW_PTE_ADDR_ENCODE(addr)      HSW_GTT_ADDR_ENCODE(addr)
+
+/* GEN8 legacy style address is defined as a 3 level page table:
+ * 31:30 | 29:21 | 20:12 |  11:0
+ * PDPE  |  PDE  |  PTE  | offset
+ * The difference as compared to normal x86 3 level page table is the PDPEs are
+ * programmed via register.
+ */
+#define GEN8_PDPE_SHIFT                        30
+#define GEN8_PDPE_MASK                 0x3
+#define GEN8_PDE_SHIFT                 21
+#define GEN8_PDE_MASK                  0x1ff
+#define GEN8_PTE_SHIFT                 12
+#define GEN8_PTE_MASK                  0x1ff
+#define GEN8_LEGACY_PDPS               4
+#define GEN8_PTES_PER_PAGE             (PAGE_SIZE / sizeof(gen8_gtt_pte_t))
+#define GEN8_PDES_PER_PAGE             (PAGE_SIZE / sizeof(gen8_ppgtt_pde_t))
+
+#define PPAT_UNCACHED_INDEX            (_PAGE_PWT | _PAGE_PCD)
+#define PPAT_CACHED_PDE_INDEX          0 /* WB LLC */
+#define PPAT_CACHED_INDEX              _PAGE_PAT /* WB LLCeLLC */
+#define PPAT_DISPLAY_ELLC_INDEX                _PAGE_PCD /* WT eLLC */
+
+#define CHV_PPAT_SNOOP                 (1<<6)
+#define GEN8_PPAT_AGE(x)               (x<<4)
+#define GEN8_PPAT_LLCeLLC              (3<<2)
+#define GEN8_PPAT_LLCELLC              (2<<2)
+#define GEN8_PPAT_LLC                  (1<<2)
+#define GEN8_PPAT_WB                   (3<<0)
+#define GEN8_PPAT_WT                   (2<<0)
+#define GEN8_PPAT_WC                   (1<<0)
+#define GEN8_PPAT_UC                   (0<<0)
+#define GEN8_PPAT_ELLC_OVERRIDE                (0<<2)
+#define GEN8_PPAT(i, x)                        ((uint64_t) (x) << ((i) * 8))
+
+enum i915_cache_level;
+/**
+ * A VMA represents a GEM BO that is bound into an address space. Therefore, a
+ * VMA's presence cannot be guaranteed before binding, or after unbinding the
+ * object into/from the address space.
+ *
+ * To make things as simple as possible (ie. no refcounting), a VMA's lifetime
+ * will always be <= an objects lifetime. So object refcounting should cover us.
+ */
+struct i915_vma {
+       struct drm_mm_node node;
+       struct drm_i915_gem_object *obj;
+       struct i915_address_space *vm;
+
+       /** This object's place on the active/inactive lists */
+       struct list_head mm_list;
+
+       struct list_head vma_link; /* Link in the object's VMA list */
+
+       /** This vma's place in the batchbuffer or on the eviction list */
+       struct list_head exec_list;
+
+       /**
+        * Used for performing relocations during execbuffer insertion.
+        */
+       struct hlist_node exec_node;
+       unsigned long exec_handle;
+       struct drm_i915_gem_exec_object2 *exec_entry;
+
+       /**
+        * How many users have pinned this object in GTT space. The following
+        * users can each hold at most one reference: pwrite/pread, pin_ioctl
+        * (via user_pin_count), execbuffer (objects are not allowed multiple
+        * times for the same batchbuffer), and the framebuffer code. When
+        * switching/pageflipping, the framebuffer code has at most two buffers
+        * pinned per crtc.
+        *
+        * In the worst case this is 1 + 1 + 1 + 2*2 = 7. That would fit into 3
+        * bits with absolutely no headroom. So use 4 bits. */
+       unsigned int pin_count:4;
+#define DRM_I915_GEM_OBJECT_MAX_PIN_COUNT 0xf
+
+       /** Unmap an object from an address space. This usually consists of
+        * setting the valid PTE entries to a reserved scratch page. */
+       void (*unbind_vma)(struct i915_vma *vma);
+       /* Map an object into an address space with the given cache flags. */
+#define GLOBAL_BIND (1<<0)
+       void (*bind_vma)(struct i915_vma *vma,
+                        enum i915_cache_level cache_level,
+                        u32 flags);
+};
+
+struct i915_address_space {
+       struct drm_mm mm;
+       struct drm_device *dev;
+       struct list_head global_link;
+       unsigned long start;            /* Start offset always 0 for dri2 */
+       size_t total;           /* size addr space maps (ex. 2GB for ggtt) */
+
+       struct {
+               dma_addr_t addr;
+               struct page *page;
+       } scratch;
+
+       /**
+        * List of objects currently involved in rendering.
+        *
+        * Includes buffers having the contents of their GPU caches
+        * flushed, not necessarily primitives.  last_rendering_seqno
+        * represents when the rendering involved will be completed.
+        *
+        * A reference is held on the buffer while on this list.
+        */
+       struct list_head active_list;
+
+       /**
+        * LRU list of objects which are not in the ringbuffer and
+        * are ready to unbind, but are still in the GTT.
+        *
+        * last_rendering_seqno is 0 while an object is in this list.
+        *
+        * A reference is not held on the buffer while on this list,
+        * as merely being GTT-bound shouldn't prevent its being
+        * freed, and we'll pull it off the list in the free path.
+        */
+       struct list_head inactive_list;
+
+       /* FIXME: Need a more generic return type */
+       gen6_gtt_pte_t (*pte_encode)(dma_addr_t addr,
+                                    enum i915_cache_level level,
+                                    bool valid); /* Create a valid PTE */
+       void (*clear_range)(struct i915_address_space *vm,
+                           uint64_t start,
+                           uint64_t length,
+                           bool use_scratch);
+       void (*insert_entries)(struct i915_address_space *vm,
+                              struct sg_table *st,
+                              uint64_t start,
+                              enum i915_cache_level cache_level);
+       void (*cleanup)(struct i915_address_space *vm);
+};
+
+/* 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
+ * portion of the GTT which can be mapped by the CPU and remain both coherent
+ * and correct (in cases like swizzling). That region is referred to as GMADR in
+ * the spec.
+ */
+struct i915_gtt {
+       struct i915_address_space base;
+       size_t stolen_size;             /* Total size of stolen memory */
+
+       unsigned long mappable_end;     /* End offset that we can CPU map */
+       struct io_mapping *mappable;    /* Mapping to our CPU mappable region */
+       phys_addr_t mappable_base;      /* PA of our GMADR */
+
+       /** "Graphics Stolen Memory" holds the global PTEs */
+       void __iomem *gsm;
+
+       bool do_idle_maps;
+
+       int mtrr;
+
+       /* global gtt ops */
+       int (*gtt_probe)(struct drm_device *dev, size_t *gtt_total,
+                         size_t *stolen, phys_addr_t *mappable_base,
+                         unsigned long *mappable_end);
+};
+
+struct i915_hw_ppgtt {
+       struct i915_address_space base;
+       struct kref ref;
+       struct drm_mm_node node;
+       unsigned num_pd_entries;
+       unsigned num_pd_pages; /* gen8+ */
+       union {
+               struct page **pt_pages;
+               struct page **gen8_pt_pages[GEN8_LEGACY_PDPS];
+       };
+       struct page *pd_pages;
+       union {
+               uint32_t pd_offset;
+               dma_addr_t pd_dma_addr[GEN8_LEGACY_PDPS];
+       };
+       union {
+               dma_addr_t *pt_dma_addr;
+               dma_addr_t *gen8_pt_dma_addr[4];
+       };
+
+       struct intel_context *ctx;
+
+       int (*enable)(struct i915_hw_ppgtt *ppgtt);
+       int (*switch_mm)(struct i915_hw_ppgtt *ppgtt,
+                        struct intel_engine_cs *ring,
+                        bool synchronous);
+       void (*debug_dump)(struct i915_hw_ppgtt *ppgtt, struct seq_file *m);
+};
+
+int i915_gem_gtt_init(struct drm_device *dev);
+void i915_gem_init_global_gtt(struct drm_device *dev);
+void i915_gem_setup_global_gtt(struct drm_device *dev, unsigned long start,
+                              unsigned long mappable_end, unsigned long end);
+
+bool intel_enable_ppgtt(struct drm_device *dev, bool full);
+int i915_gem_init_ppgtt(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt);
+
+void i915_check_and_clear_faults(struct drm_device *dev);
+void i915_gem_suspend_gtt_mappings(struct drm_device *dev);
+void i915_gem_restore_gtt_mappings(struct drm_device *dev);
+
+int __must_check i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj);
+void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj);
+
+#endif
diff --git a/drivers/gpu/drm/i915/i915_gem_render_state.c b/drivers/gpu/drm/i915/i915_gem_render_state.c
new file mode 100644 (file)
index 0000000..3521f99
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright © 2014 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Mika Kuoppala <mika.kuoppala@intel.com>
+ *
+ */
+
+#include "i915_drv.h"
+#include "intel_renderstate.h"
+
+struct i915_render_state {
+       struct drm_i915_gem_object *obj;
+       unsigned long ggtt_offset;
+       void *batch;
+       u32 size;
+       u32 len;
+};
+
+static struct i915_render_state *render_state_alloc(struct drm_device *dev)
+{
+       struct i915_render_state *so;
+       struct page *page;
+       int ret;
+
+       so = kzalloc(sizeof(*so), GFP_KERNEL);
+       if (!so)
+               return ERR_PTR(-ENOMEM);
+
+       so->obj = i915_gem_alloc_object(dev, 4096);
+       if (so->obj == NULL) {
+               ret = -ENOMEM;
+               goto free;
+       }
+       so->size = 4096;
+
+       ret = i915_gem_obj_ggtt_pin(so->obj, 4096, 0);
+       if (ret)
+               goto free_gem;
+
+       BUG_ON(so->obj->pages->nents != 1);
+       page = sg_page(so->obj->pages->sgl);
+
+       so->batch = kmap(page);
+       if (!so->batch) {
+               ret = -ENOMEM;
+               goto unpin;
+       }
+
+       so->ggtt_offset = i915_gem_obj_ggtt_offset(so->obj);
+
+       return so;
+unpin:
+       i915_gem_object_ggtt_unpin(so->obj);
+free_gem:
+       drm_gem_object_unreference(&so->obj->base);
+free:
+       kfree(so);
+       return ERR_PTR(ret);
+}
+
+static void render_state_free(struct i915_render_state *so)
+{
+       kunmap(so->batch);
+       i915_gem_object_ggtt_unpin(so->obj);
+       drm_gem_object_unreference(&so->obj->base);
+       kfree(so);
+}
+
+static const struct intel_renderstate_rodata *
+render_state_get_rodata(struct drm_device *dev, const int gen)
+{
+       switch (gen) {
+       case 6:
+               return &gen6_null_state;
+       case 7:
+               return &gen7_null_state;
+       case 8:
+               return &gen8_null_state;
+       }
+
+       return NULL;
+}
+
+static int render_state_setup(const int gen,
+                             const struct intel_renderstate_rodata *rodata,
+                             struct i915_render_state *so)
+{
+       const u64 goffset = i915_gem_obj_ggtt_offset(so->obj);
+       u32 reloc_index = 0;
+       u32 * const d = so->batch;
+       unsigned int i = 0;
+       int ret;
+
+       if (!rodata || rodata->batch_items * 4 > so->size)
+               return -EINVAL;
+
+       ret = i915_gem_object_set_to_cpu_domain(so->obj, true);
+       if (ret)
+               return ret;
+
+       while (i < rodata->batch_items) {
+               u32 s = rodata->batch[i];
+
+               if (reloc_index < rodata->reloc_items &&
+                   i * 4  == rodata->reloc[reloc_index]) {
+
+                       s += goffset & 0xffffffff;
+
+                       /* We keep batch offsets max 32bit */
+                       if (gen >= 8) {
+                               if (i + 1 >= rodata->batch_items ||
+                                   rodata->batch[i + 1] != 0)
+                                       return -EINVAL;
+
+                               d[i] = s;
+                               i++;
+                               s = (goffset & 0xffffffff00000000ull) >> 32;
+                       }
+
+                       reloc_index++;
+               }
+
+               d[i] = s;
+               i++;
+       }
+
+       ret = i915_gem_object_set_to_gtt_domain(so->obj, false);
+       if (ret)
+               return ret;
+
+       if (rodata->reloc_items != reloc_index) {
+               DRM_ERROR("not all relocs resolved, %d out of %d\n",
+                         reloc_index, rodata->reloc_items);
+               return -EINVAL;
+       }
+
+       so->len = rodata->batch_items * 4;
+
+       return 0;
+}
+
+int i915_gem_render_state_init(struct intel_engine_cs *ring)
+{
+       const int gen = INTEL_INFO(ring->dev)->gen;
+       struct i915_render_state *so;
+       const struct intel_renderstate_rodata *rodata;
+       int ret;
+
+       if (WARN_ON(ring->id != RCS))
+               return -ENOENT;
+
+       rodata = render_state_get_rodata(ring->dev, gen);
+       if (rodata == NULL)
+               return 0;
+
+       so = render_state_alloc(ring->dev);
+       if (IS_ERR(so))
+               return PTR_ERR(so);
+
+       ret = render_state_setup(gen, rodata, so);
+       if (ret)
+               goto out;
+
+       ret = ring->dispatch_execbuffer(ring,
+                                       i915_gem_obj_ggtt_offset(so->obj),
+                                       so->len,
+                                       I915_DISPATCH_SECURE);
+       if (ret)
+               goto out;
+
+       i915_vma_move_to_active(i915_gem_obj_to_ggtt(so->obj), ring);
+
+       ret = __i915_add_request(ring, NULL, so->obj, NULL);
+       /* __i915_add_request moves object to inactive if it fails */
+out:
+       render_state_free(so);
+       return ret;
+}
diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c
new file mode 100644 (file)
index 0000000..21ea928
--- /dev/null
@@ -0,0 +1,711 @@
+/*
+ * Copyright © 2012-2014 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 "drmP.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "i915_trace.h"
+#include "intel_drv.h"
+#include <linux/mmu_context.h>
+#include <linux/mmu_notifier.h>
+#include <linux/mempolicy.h>
+#include <linux/swap.h>
+
+#if defined(CONFIG_MMU_NOTIFIER)
+#include <linux/interval_tree.h>
+
+struct i915_mmu_notifier {
+       spinlock_t lock;
+       struct hlist_node node;
+       struct mmu_notifier mn;
+       struct rb_root objects;
+       struct drm_device *dev;
+       struct mm_struct *mm;
+       struct work_struct work;
+       unsigned long count;
+       unsigned long serial;
+};
+
+struct i915_mmu_object {
+       struct i915_mmu_notifier *mmu;
+       struct interval_tree_node it;
+       struct drm_i915_gem_object *obj;
+};
+
+static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
+                                                      struct mm_struct *mm,
+                                                      unsigned long start,
+                                                      unsigned long end)
+{
+       struct i915_mmu_notifier *mn = container_of(_mn, struct i915_mmu_notifier, mn);
+       struct interval_tree_node *it = NULL;
+       unsigned long serial = 0;
+
+       end--; /* interval ranges are inclusive, but invalidate range is exclusive */
+       while (start < end) {
+               struct drm_i915_gem_object *obj;
+
+               obj = NULL;
+               spin_lock(&mn->lock);
+               if (serial == mn->serial)
+                       it = interval_tree_iter_next(it, start, end);
+               else
+                       it = interval_tree_iter_first(&mn->objects, start, end);
+               if (it != NULL) {
+                       obj = container_of(it, struct i915_mmu_object, it)->obj;
+                       drm_gem_object_reference(&obj->base);
+                       serial = mn->serial;
+               }
+               spin_unlock(&mn->lock);
+               if (obj == NULL)
+                       return;
+
+               mutex_lock(&mn->dev->struct_mutex);
+               /* Cancel any active worker and force us to re-evaluate gup */
+               obj->userptr.work = NULL;
+
+               if (obj->pages != NULL) {
+                       struct drm_i915_private *dev_priv = to_i915(mn->dev);
+                       struct i915_vma *vma, *tmp;
+                       bool was_interruptible;
+
+                       was_interruptible = dev_priv->mm.interruptible;
+                       dev_priv->mm.interruptible = false;
+
+                       list_for_each_entry_safe(vma, tmp, &obj->vma_list, vma_link) {
+                               int ret = i915_vma_unbind(vma);
+                               WARN_ON(ret && ret != -EIO);
+                       }
+                       WARN_ON(i915_gem_object_put_pages(obj));
+
+                       dev_priv->mm.interruptible = was_interruptible;
+               }
+
+               start = obj->userptr.ptr + obj->base.size;
+
+               drm_gem_object_unreference(&obj->base);
+               mutex_unlock(&mn->dev->struct_mutex);
+       }
+}
+
+static const struct mmu_notifier_ops i915_gem_userptr_notifier = {
+       .invalidate_range_start = i915_gem_userptr_mn_invalidate_range_start,
+};
+
+static struct i915_mmu_notifier *
+__i915_mmu_notifier_lookup(struct drm_device *dev, struct mm_struct *mm)
+{
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct i915_mmu_notifier *mmu;
+
+       /* Protected by dev->struct_mutex */
+       hash_for_each_possible(dev_priv->mmu_notifiers, mmu, node, (unsigned long)mm)
+               if (mmu->mm == mm)
+                       return mmu;
+
+       return NULL;
+}
+
+static struct i915_mmu_notifier *
+i915_mmu_notifier_get(struct drm_device *dev, struct mm_struct *mm)
+{
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct i915_mmu_notifier *mmu;
+       int ret;
+
+       lockdep_assert_held(&dev->struct_mutex);
+
+       mmu = __i915_mmu_notifier_lookup(dev, mm);
+       if (mmu)
+               return mmu;
+
+       mmu = kmalloc(sizeof(*mmu), GFP_KERNEL);
+       if (mmu == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       spin_lock_init(&mmu->lock);
+       mmu->dev = dev;
+       mmu->mn.ops = &i915_gem_userptr_notifier;
+       mmu->mm = mm;
+       mmu->objects = RB_ROOT;
+       mmu->count = 0;
+       mmu->serial = 0;
+
+       /* Protected by mmap_sem (write-lock) */
+       ret = __mmu_notifier_register(&mmu->mn, mm);
+       if (ret) {
+               kfree(mmu);
+               return ERR_PTR(ret);
+       }
+
+       /* Protected by dev->struct_mutex */
+       hash_add(dev_priv->mmu_notifiers, &mmu->node, (unsigned long)mm);
+       return mmu;
+}
+
+static void
+__i915_mmu_notifier_destroy_worker(struct work_struct *work)
+{
+       struct i915_mmu_notifier *mmu = container_of(work, typeof(*mmu), work);
+       mmu_notifier_unregister(&mmu->mn, mmu->mm);
+       kfree(mmu);
+}
+
+static void
+__i915_mmu_notifier_destroy(struct i915_mmu_notifier *mmu)
+{
+       lockdep_assert_held(&mmu->dev->struct_mutex);
+
+       /* Protected by dev->struct_mutex */
+       hash_del(&mmu->node);
+
+       /* Our lock ordering is: mmap_sem, mmu_notifier_scru, struct_mutex.
+        * We enter the function holding struct_mutex, therefore we need
+        * to drop our mutex prior to calling mmu_notifier_unregister in
+        * order to prevent lock inversion (and system-wide deadlock)
+        * between the mmap_sem and struct-mutex. Hence we defer the
+        * unregistration to a workqueue where we hold no locks.
+        */
+       INIT_WORK(&mmu->work, __i915_mmu_notifier_destroy_worker);
+       schedule_work(&mmu->work);
+}
+
+static void __i915_mmu_notifier_update_serial(struct i915_mmu_notifier *mmu)
+{
+       if (++mmu->serial == 0)
+               mmu->serial = 1;
+}
+
+static void
+i915_mmu_notifier_del(struct i915_mmu_notifier *mmu,
+                     struct i915_mmu_object *mn)
+{
+       lockdep_assert_held(&mmu->dev->struct_mutex);
+
+       spin_lock(&mmu->lock);
+       interval_tree_remove(&mn->it, &mmu->objects);
+       __i915_mmu_notifier_update_serial(mmu);
+       spin_unlock(&mmu->lock);
+
+       /* Protected against _add() by dev->struct_mutex */
+       if (--mmu->count == 0)
+               __i915_mmu_notifier_destroy(mmu);
+}
+
+static int
+i915_mmu_notifier_add(struct i915_mmu_notifier *mmu,
+                     struct i915_mmu_object *mn)
+{
+       struct interval_tree_node *it;
+       int ret;
+
+       ret = i915_mutex_lock_interruptible(mmu->dev);
+       if (ret)
+               return ret;
+
+       /* Make sure we drop the final active reference (and thereby
+        * remove the objects from the interval tree) before we do
+        * the check for overlapping objects.
+        */
+       i915_gem_retire_requests(mmu->dev);
+
+       /* Disallow overlapping userptr objects */
+       spin_lock(&mmu->lock);
+       it = interval_tree_iter_first(&mmu->objects,
+                                     mn->it.start, mn->it.last);
+       if (it) {
+               struct drm_i915_gem_object *obj;
+
+               /* We only need to check the first object in the range as it
+                * either has cancelled gup work queued and we need to
+                * return back to the user to give time for the gup-workers
+                * to flush their object references upon which the object will
+                * be removed from the interval-tree, or the the range is
+                * still in use by another client and the overlap is invalid.
+                */
+
+               obj = container_of(it, struct i915_mmu_object, it)->obj;
+               ret = obj->userptr.workers ? -EAGAIN : -EINVAL;
+       } else {
+               interval_tree_insert(&mn->it, &mmu->objects);
+               __i915_mmu_notifier_update_serial(mmu);
+               ret = 0;
+       }
+       spin_unlock(&mmu->lock);
+       mutex_unlock(&mmu->dev->struct_mutex);
+
+       return ret;
+}
+
+static void
+i915_gem_userptr_release__mmu_notifier(struct drm_i915_gem_object *obj)
+{
+       struct i915_mmu_object *mn;
+
+       mn = obj->userptr.mn;
+       if (mn == NULL)
+               return;
+
+       i915_mmu_notifier_del(mn->mmu, mn);
+       obj->userptr.mn = NULL;
+}
+
+static int
+i915_gem_userptr_init__mmu_notifier(struct drm_i915_gem_object *obj,
+                                   unsigned flags)
+{
+       struct i915_mmu_notifier *mmu;
+       struct i915_mmu_object *mn;
+       int ret;
+
+       if (flags & I915_USERPTR_UNSYNCHRONIZED)
+               return capable(CAP_SYS_ADMIN) ? 0 : -EPERM;
+
+       down_write(&obj->userptr.mm->mmap_sem);
+       ret = i915_mutex_lock_interruptible(obj->base.dev);
+       if (ret == 0) {
+               mmu = i915_mmu_notifier_get(obj->base.dev, obj->userptr.mm);
+               if (!IS_ERR(mmu))
+                       mmu->count++; /* preemptive add to act as a refcount */
+               else
+                       ret = PTR_ERR(mmu);
+               mutex_unlock(&obj->base.dev->struct_mutex);
+       }
+       up_write(&obj->userptr.mm->mmap_sem);
+       if (ret)
+               return ret;
+
+       mn = kzalloc(sizeof(*mn), GFP_KERNEL);
+       if (mn == NULL) {
+               ret = -ENOMEM;
+               goto destroy_mmu;
+       }
+
+       mn->mmu = mmu;
+       mn->it.start = obj->userptr.ptr;
+       mn->it.last = mn->it.start + obj->base.size - 1;
+       mn->obj = obj;
+
+       ret = i915_mmu_notifier_add(mmu, mn);
+       if (ret)
+               goto free_mn;
+
+       obj->userptr.mn = mn;
+       return 0;
+
+free_mn:
+       kfree(mn);
+destroy_mmu:
+       mutex_lock(&obj->base.dev->struct_mutex);
+       if (--mmu->count == 0)
+               __i915_mmu_notifier_destroy(mmu);
+       mutex_unlock(&obj->base.dev->struct_mutex);
+       return ret;
+}
+
+#else
+
+static void
+i915_gem_userptr_release__mmu_notifier(struct drm_i915_gem_object *obj)
+{
+}
+
+static int
+i915_gem_userptr_init__mmu_notifier(struct drm_i915_gem_object *obj,
+                                   unsigned flags)
+{
+       if ((flags & I915_USERPTR_UNSYNCHRONIZED) == 0)
+               return -ENODEV;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       return 0;
+}
+#endif
+
+struct get_pages_work {
+       struct work_struct work;
+       struct drm_i915_gem_object *obj;
+       struct task_struct *task;
+};
+
+
+#if IS_ENABLED(CONFIG_SWIOTLB)
+#define swiotlb_active() swiotlb_nr_tbl()
+#else
+#define swiotlb_active() 0
+#endif
+
+static int
+st_set_pages(struct sg_table **st, struct page **pvec, int num_pages)
+{
+       struct scatterlist *sg;
+       int ret, n;
+
+       *st = kmalloc(sizeof(**st), GFP_KERNEL);
+       if (*st == NULL)
+               return -ENOMEM;
+
+       if (swiotlb_active()) {
+               ret = sg_alloc_table(*st, num_pages, GFP_KERNEL);
+               if (ret)
+                       goto err;
+
+               for_each_sg((*st)->sgl, sg, num_pages, n)
+                       sg_set_page(sg, pvec[n], PAGE_SIZE, 0);
+       } else {
+               ret = sg_alloc_table_from_pages(*st, pvec, num_pages,
+                                               0, num_pages << PAGE_SHIFT,
+                                               GFP_KERNEL);
+               if (ret)
+                       goto err;
+       }
+
+       return 0;
+
+err:
+       kfree(*st);
+       *st = NULL;
+       return ret;
+}
+
+static void
+__i915_gem_userptr_get_pages_worker(struct work_struct *_work)
+{
+       struct get_pages_work *work = container_of(_work, typeof(*work), work);
+       struct drm_i915_gem_object *obj = work->obj;
+       struct drm_device *dev = obj->base.dev;
+       const int num_pages = obj->base.size >> PAGE_SHIFT;
+       struct page **pvec;
+       int pinned, ret;
+
+       ret = -ENOMEM;
+       pinned = 0;
+
+       pvec = kmalloc(num_pages*sizeof(struct page *),
+                      GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY);
+       if (pvec == NULL)
+               pvec = drm_malloc_ab(num_pages, sizeof(struct page *));
+       if (pvec != NULL) {
+               struct mm_struct *mm = obj->userptr.mm;
+
+               down_read(&mm->mmap_sem);
+               while (pinned < num_pages) {
+                       ret = get_user_pages(work->task, mm,
+                                            obj->userptr.ptr + pinned * PAGE_SIZE,
+                                            num_pages - pinned,
+                                            !obj->userptr.read_only, 0,
+                                            pvec + pinned, NULL);
+                       if (ret < 0)
+                               break;
+
+                       pinned += ret;
+               }
+               up_read(&mm->mmap_sem);
+       }
+
+       mutex_lock(&dev->struct_mutex);
+       if (obj->userptr.work != &work->work) {
+               ret = 0;
+       } else if (pinned == num_pages) {
+               ret = st_set_pages(&obj->pages, pvec, num_pages);
+               if (ret == 0) {
+                       list_add_tail(&obj->global_list, &to_i915(dev)->mm.unbound_list);
+                       pinned = 0;
+               }
+       }
+
+       obj->userptr.work = ERR_PTR(ret);
+       obj->userptr.workers--;
+       drm_gem_object_unreference(&obj->base);
+       mutex_unlock(&dev->struct_mutex);
+
+       release_pages(pvec, pinned, 0);
+       drm_free_large(pvec);
+
+       put_task_struct(work->task);
+       kfree(work);
+}
+
+static int
+i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
+{
+       const int num_pages = obj->base.size >> PAGE_SHIFT;
+       struct page **pvec;
+       int pinned, ret;
+
+       /* If userspace should engineer that these pages are replaced in
+        * the vma between us binding this page into the GTT and completion
+        * of rendering... Their loss. If they change the mapping of their
+        * pages they need to create a new bo to point to the new vma.
+        *
+        * However, that still leaves open the possibility of the vma
+        * being copied upon fork. Which falls under the same userspace
+        * synchronisation issue as a regular bo, except that this time
+        * the process may not be expecting that a particular piece of
+        * memory is tied to the GPU.
+        *
+        * Fortunately, we can hook into the mmu_notifier in order to
+        * discard the page references prior to anything nasty happening
+        * to the vma (discard or cloning) which should prevent the more
+        * egregious cases from causing harm.
+        */
+
+       pvec = NULL;
+       pinned = 0;
+       if (obj->userptr.mm == current->mm) {
+               pvec = kmalloc(num_pages*sizeof(struct page *),
+                              GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY);
+               if (pvec == NULL) {
+                       pvec = drm_malloc_ab(num_pages, sizeof(struct page *));
+                       if (pvec == NULL)
+                               return -ENOMEM;
+               }
+
+               pinned = __get_user_pages_fast(obj->userptr.ptr, num_pages,
+                                              !obj->userptr.read_only, pvec);
+       }
+       if (pinned < num_pages) {
+               if (pinned < 0) {
+                       ret = pinned;
+                       pinned = 0;
+               } else {
+                       /* Spawn a worker so that we can acquire the
+                        * user pages without holding our mutex. Access
+                        * to the user pages requires mmap_sem, and we have
+                        * a strict lock ordering of mmap_sem, struct_mutex -
+                        * we already hold struct_mutex here and so cannot
+                        * call gup without encountering a lock inversion.
+                        *
+                        * Userspace will keep on repeating the operation
+                        * (thanks to EAGAIN) until either we hit the fast
+                        * path or the worker completes. If the worker is
+                        * cancelled or superseded, the task is still run
+                        * but the results ignored. (This leads to
+                        * complications that we may have a stray object
+                        * refcount that we need to be wary of when
+                        * checking for existing objects during creation.)
+                        * If the worker encounters an error, it reports
+                        * that error back to this function through
+                        * obj->userptr.work = ERR_PTR.
+                        */
+                       ret = -EAGAIN;
+                       if (obj->userptr.work == NULL &&
+                           obj->userptr.workers < I915_GEM_USERPTR_MAX_WORKERS) {
+                               struct get_pages_work *work;
+
+                               work = kmalloc(sizeof(*work), GFP_KERNEL);
+                               if (work != NULL) {
+                                       obj->userptr.work = &work->work;
+                                       obj->userptr.workers++;
+
+                                       work->obj = obj;
+                                       drm_gem_object_reference(&obj->base);
+
+                                       work->task = current;
+                                       get_task_struct(work->task);
+
+                                       INIT_WORK(&work->work, __i915_gem_userptr_get_pages_worker);
+                                       schedule_work(&work->work);
+                               } else
+                                       ret = -ENOMEM;
+                       } else {
+                               if (IS_ERR(obj->userptr.work)) {
+                                       ret = PTR_ERR(obj->userptr.work);
+                                       obj->userptr.work = NULL;
+                               }
+                       }
+               }
+       } else {
+               ret = st_set_pages(&obj->pages, pvec, num_pages);
+               if (ret == 0) {
+                       obj->userptr.work = NULL;
+                       pinned = 0;
+               }
+       }
+
+       release_pages(pvec, pinned, 0);
+       drm_free_large(pvec);
+       return ret;
+}
+
+static void
+i915_gem_userptr_put_pages(struct drm_i915_gem_object *obj)
+{
+       struct scatterlist *sg;
+       int i;
+
+       BUG_ON(obj->userptr.work != NULL);
+
+       if (obj->madv != I915_MADV_WILLNEED)
+               obj->dirty = 0;
+
+       for_each_sg(obj->pages->sgl, sg, obj->pages->nents, i) {
+               struct page *page = sg_page(sg);
+
+               if (obj->dirty)
+                       set_page_dirty(page);
+
+               mark_page_accessed(page);
+               page_cache_release(page);
+       }
+       obj->dirty = 0;
+
+       sg_free_table(obj->pages);
+       kfree(obj->pages);
+}
+
+static void
+i915_gem_userptr_release(struct drm_i915_gem_object *obj)
+{
+       i915_gem_userptr_release__mmu_notifier(obj);
+
+       if (obj->userptr.mm) {
+               mmput(obj->userptr.mm);
+               obj->userptr.mm = NULL;
+       }
+}
+
+static int
+i915_gem_userptr_dmabuf_export(struct drm_i915_gem_object *obj)
+{
+       if (obj->userptr.mn)
+               return 0;
+
+       return i915_gem_userptr_init__mmu_notifier(obj, 0);
+}
+
+static const struct drm_i915_gem_object_ops i915_gem_userptr_ops = {
+       .dmabuf_export = i915_gem_userptr_dmabuf_export,
+       .get_pages = i915_gem_userptr_get_pages,
+       .put_pages = i915_gem_userptr_put_pages,
+       .release = i915_gem_userptr_release,
+};
+
+/**
+ * Creates a new mm object that wraps some normal memory from the process
+ * context - user memory.
+ *
+ * We impose several restrictions upon the memory being mapped
+ * into the GPU.
+ * 1. It must be page aligned (both start/end addresses, i.e ptr and size).
+ * 2. It cannot overlap any other userptr object in the same address space.
+ * 3. It must be normal system memory, not a pointer into another map of IO
+ *    space (e.g. it must not be a GTT mmapping of another object).
+ * 4. We only allow a bo as large as we could in theory map into the GTT,
+ *    that is we limit the size to the total size of the GTT.
+ * 5. The bo is marked as being snoopable. The backing pages are left
+ *    accessible directly by the CPU, but reads and writes by the GPU may
+ *    incur the cost of a snoop (unless you have an LLC architecture).
+ *
+ * Synchronisation between multiple users and the GPU is left to userspace
+ * through the normal set-domain-ioctl. The kernel will enforce that the
+ * GPU relinquishes the VMA before it is returned back to the system
+ * i.e. upon free(), munmap() or process termination. However, the userspace
+ * malloc() library may not immediately relinquish the VMA after free() and
+ * instead reuse it whilst the GPU is still reading and writing to the VMA.
+ * Caveat emptor.
+ *
+ * Also note, that the object created here is not currently a "first class"
+ * object, in that several ioctls are banned. These are the CPU access
+ * ioctls: mmap(), pwrite and pread. In practice, you are expected to use
+ * direct access via your pointer rather than use those ioctls.
+ *
+ * If you think this is a good interface to use to pass GPU memory between
+ * drivers, please use dma-buf instead. In fact, wherever possible use
+ * dma-buf instead.
+ */
+int
+i915_gem_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_i915_gem_userptr *args = data;
+       struct drm_i915_gem_object *obj;
+       int ret;
+       u32 handle;
+
+       if (args->flags & ~(I915_USERPTR_READ_ONLY |
+                           I915_USERPTR_UNSYNCHRONIZED))
+               return -EINVAL;
+
+       if (offset_in_page(args->user_ptr | args->user_size))
+               return -EINVAL;
+
+       if (args->user_size > dev_priv->gtt.base.total)
+               return -E2BIG;
+
+       if (!access_ok(args->flags & I915_USERPTR_READ_ONLY ? VERIFY_READ : VERIFY_WRITE,
+                      (char __user *)(unsigned long)args->user_ptr, args->user_size))
+               return -EFAULT;
+
+       if (args->flags & I915_USERPTR_READ_ONLY) {
+               /* On almost all of the current hw, we cannot tell the GPU that a
+                * page is readonly, so this is just a placeholder in the uAPI.
+                */
+               return -ENODEV;
+       }
+
+       /* Allocate the new object */
+       obj = i915_gem_object_alloc(dev);
+       if (obj == NULL)
+               return -ENOMEM;
+
+       drm_gem_private_object_init(dev, &obj->base, args->user_size);
+       i915_gem_object_init(obj, &i915_gem_userptr_ops);
+       obj->cache_level = I915_CACHE_LLC;
+       obj->base.write_domain = I915_GEM_DOMAIN_CPU;
+       obj->base.read_domains = I915_GEM_DOMAIN_CPU;
+
+       obj->userptr.ptr = args->user_ptr;
+       obj->userptr.read_only = !!(args->flags & I915_USERPTR_READ_ONLY);
+
+       /* And keep a pointer to the current->mm for resolving the user pages
+        * at binding. This means that we need to hook into the mmu_notifier
+        * in order to detect if the mmu is destroyed.
+        */
+       ret = -ENOMEM;
+       if ((obj->userptr.mm = get_task_mm(current)))
+               ret = i915_gem_userptr_init__mmu_notifier(obj, args->flags);
+       if (ret == 0)
+               ret = drm_gem_handle_create(file, &obj->base, &handle);
+
+       /* drop reference from allocate - handle holds it now */
+       drm_gem_object_unreference_unlocked(&obj->base);
+       if (ret)
+               return ret;
+
+       args->handle = handle;
+       return 0;
+}
+
+int
+i915_gem_init_userptr(struct drm_device *dev)
+{
+#if defined(CONFIG_MMU_NOTIFIER)
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       hash_init(dev_priv->mmu_notifiers);
+#endif
+       return 0;
+}
index 12f1d43b2d68fbf28f8f0aeeb2ec6c727825eb88..87ec60e181a716bd46d4cf2c196552962204941f 100644 (file)
@@ -42,6 +42,7 @@ static const char *ring_str(int ring)
        case VCS: return "bsd";
        case BCS: return "blt";
        case VECS: return "vebox";
+       case VCS2: return "bsd2";
        default: return "";
        }
 }
@@ -204,6 +205,7 @@ static void print_error_buffers(struct drm_i915_error_state_buf *m,
                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->userptr ? " userptr" : "");
                err_puts(m, err->ring != -1 ? " " : "");
                err_puts(m, ring_str(err->ring));
                err_puts(m, i915_cache_level_str(err->cache_level));
@@ -257,7 +259,8 @@ static void i915_ring_error_state(struct drm_i915_error_state_buf *m,
                err_printf(m, "  INSTPS: 0x%08x\n", ring->instps);
        }
        err_printf(m, "  INSTPM: 0x%08x\n", ring->instpm);
-       err_printf(m, "  FADDR: 0x%08x\n", ring->faddr);
+       err_printf(m, "  FADDR: 0x%08x %08x\n", upper_32_bits(ring->faddr),
+                  lower_32_bits(ring->faddr));
        if (INTEL_INFO(dev)->gen >= 6) {
                err_printf(m, "  RC PSMI: 0x%08x\n", ring->rc_psmi);
                err_printf(m, "  FAULT_REG: 0x%08x\n", ring->fault_reg);
@@ -452,16 +455,7 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
                        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) {
-                               err_printf(m, "[%04x] %08x %08x %08x %08x\n",
-                                          offset,
-                                          obj->pages[0][elt],
-                                          obj->pages[0][elt+1],
-                                          obj->pages[0][elt+2],
-                                          obj->pages[0][elt+3]);
-                                       offset += 16;
-                       }
+                       print_error_obj(m, obj);
                }
        }
 
@@ -648,6 +642,7 @@ static void capture_bo(struct drm_i915_error_buffer *err,
        err->tiling = obj->tiling_mode;
        err->dirty = obj->dirty;
        err->purgeable = obj->madv != I915_MADV_WILLNEED;
+       err->userptr = obj->userptr.mm != NULL;
        err->ring = obj->ring ? obj->ring->id : -1;
        err->cache_level = obj->cache_level;
 }
@@ -752,7 +747,7 @@ static void i915_gem_record_fences(struct drm_device *dev,
 }
 
 static void i915_record_ring_state(struct drm_device *dev,
-                                  struct intel_ring_buffer *ring,
+                                  struct intel_engine_cs *ring,
                                   struct drm_i915_error_ring *ering)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -764,14 +759,14 @@ static void i915_record_ring_state(struct drm_device *dev,
                        = I915_READ(RING_SYNC_0(ring->mmio_base));
                ering->semaphore_mboxes[1]
                        = I915_READ(RING_SYNC_1(ring->mmio_base));
-               ering->semaphore_seqno[0] = ring->sync_seqno[0];
-               ering->semaphore_seqno[1] = ring->sync_seqno[1];
+               ering->semaphore_seqno[0] = ring->semaphore.sync_seqno[0];
+               ering->semaphore_seqno[1] = ring->semaphore.sync_seqno[1];
        }
 
        if (HAS_VEBOX(dev)) {
                ering->semaphore_mboxes[2] =
                        I915_READ(RING_SYNC_2(ring->mmio_base));
-               ering->semaphore_seqno[2] = ring->sync_seqno[2];
+               ering->semaphore_seqno[2] = ring->semaphore.sync_seqno[2];
        }
 
        if (INTEL_INFO(dev)->gen >= 4) {
@@ -781,8 +776,10 @@ static void i915_record_ring_state(struct drm_device *dev,
                ering->instdone = I915_READ(RING_INSTDONE(ring->mmio_base));
                ering->instps = I915_READ(RING_INSTPS(ring->mmio_base));
                ering->bbaddr = I915_READ(RING_BBADDR(ring->mmio_base));
-               if (INTEL_INFO(dev)->gen >= 8)
+               if (INTEL_INFO(dev)->gen >= 8) {
+                       ering->faddr |= (u64) I915_READ(RING_DMA_FADD_UDW(ring->mmio_base)) << 32;
                        ering->bbaddr |= (u64) I915_READ(RING_BBADDR_UDW(ring->mmio_base)) << 32;
+               }
                ering->bbstate = I915_READ(RING_BBSTATE(ring->mmio_base));
        } else {
                ering->faddr = I915_READ(DMA_FADD_I8XX);
@@ -828,8 +825,8 @@ static void i915_record_ring_state(struct drm_device *dev,
                ering->hws = I915_READ(mmio);
        }
 
-       ering->cpu_ring_head = ring->head;
-       ering->cpu_ring_tail = ring->tail;
+       ering->cpu_ring_head = ring->buffer->head;
+       ering->cpu_ring_tail = ring->buffer->tail;
 
        ering->hangcheck_score = ring->hangcheck.score;
        ering->hangcheck_action = ring->hangcheck.action;
@@ -862,7 +859,7 @@ static void i915_record_ring_state(struct drm_device *dev,
 }
 
 
-static void i915_gem_record_active_context(struct intel_ring_buffer *ring,
+static void i915_gem_record_active_context(struct intel_engine_cs *ring,
                                           struct drm_i915_error_state *error,
                                           struct drm_i915_error_ring *ering)
 {
@@ -875,10 +872,7 @@ static void i915_gem_record_active_context(struct intel_ring_buffer *ring,
 
        list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
                if ((error->ccid & PAGE_MASK) == i915_gem_obj_ggtt_offset(obj)) {
-                       ering->ctx = i915_error_object_create_sized(dev_priv,
-                                                                   obj,
-                                                                   &dev_priv->gtt.base,
-                                                                   1);
+                       ering->ctx = i915_error_ggtt_object_create(dev_priv, obj);
                        break;
                }
        }
@@ -892,7 +886,7 @@ static void i915_gem_record_rings(struct drm_device *dev,
        int i, count;
 
        for (i = 0; i < I915_NUM_RINGS; i++) {
-               struct intel_ring_buffer *ring = &dev_priv->ring[i];
+               struct intel_engine_cs *ring = &dev_priv->ring[i];
 
                if (ring->dev == NULL)
                        continue;
@@ -936,7 +930,7 @@ static void i915_gem_record_rings(struct drm_device *dev,
                }
 
                error->ring[i].ringbuffer =
-                       i915_error_ggtt_object_create(dev_priv, ring->obj);
+                       i915_error_ggtt_object_create(dev_priv, ring->buffer->obj);
 
                if (ring->status_page.obj)
                        error->ring[i].hws_page =
@@ -1037,7 +1031,6 @@ static void i915_capture_reg_state(struct drm_i915_private *dev_priv,
                                   struct drm_i915_error_state *error)
 {
        struct drm_device *dev = dev_priv->dev;
-       int pipe;
 
        /* General organization
         * 1. Registers specific to a single generation
@@ -1062,9 +1055,6 @@ static void i915_capture_reg_state(struct drm_i915_private *dev_priv,
                error->gfx_mode = I915_READ(GFX_MODE);
        }
 
-       if (IS_GEN2(dev))
-               error->ier = I915_READ16(IER);
-
        /* 2: Registers which belong to multiple generations */
        if (INTEL_INFO(dev)->gen >= 7)
                error->forcewake = I915_READ(FORCEWAKE_MT);
@@ -1088,9 +1078,10 @@ static void i915_capture_reg_state(struct drm_i915_private *dev_priv,
        if (HAS_PCH_SPLIT(dev))
                error->ier = I915_READ(DEIER) | I915_READ(GTIER);
        else {
-               error->ier = I915_READ(IER);
-               for_each_pipe(pipe)
-                       error->pipestat[pipe] = I915_READ(PIPESTAT(pipe));
+               if (IS_GEN2(dev))
+                       error->ier = I915_READ16(IER);
+               else
+                       error->ier = I915_READ(IER);
        }
 
        /* 4: Everything else */
index 3c59584161c2dab0f0476b013c27a1248ec7189e..2e0613e262515f7d2ad6f8b75c4f0884bdf05333 100644 (file)
@@ -208,7 +208,7 @@ long i915_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
        if (nr < DRM_COMMAND_BASE)
                return drm_compat_ioctl(filp, cmd, arg);
 
-       if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(i915_compat_ioctls))
+       if (nr < DRM_COMMAND_BASE + ARRAY_SIZE(i915_compat_ioctls))
                fn = i915_compat_ioctls[nr - DRM_COMMAND_BASE];
 
        if (fn != NULL)
index 0b99de95593b5a6f650fe8b0aa3e487168d6af5f..6f8017a7e937f0a4e43d726ac6a66b6c030fed76 100644 (file)
@@ -80,17 +80,64 @@ static const u32 hpd_status_i915[] = { /* i915 and valleyview are the same */
        [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS
 };
 
+/* IIR can theoretically queue up two events. Be paranoid. */
+#define GEN8_IRQ_RESET_NDX(type, which) do { \
+       I915_WRITE(GEN8_##type##_IMR(which), 0xffffffff); \
+       POSTING_READ(GEN8_##type##_IMR(which)); \
+       I915_WRITE(GEN8_##type##_IER(which), 0); \
+       I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
+       POSTING_READ(GEN8_##type##_IIR(which)); \
+       I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
+       POSTING_READ(GEN8_##type##_IIR(which)); \
+} while (0)
+
+#define GEN5_IRQ_RESET(type) do { \
+       I915_WRITE(type##IMR, 0xffffffff); \
+       POSTING_READ(type##IMR); \
+       I915_WRITE(type##IER, 0); \
+       I915_WRITE(type##IIR, 0xffffffff); \
+       POSTING_READ(type##IIR); \
+       I915_WRITE(type##IIR, 0xffffffff); \
+       POSTING_READ(type##IIR); \
+} while (0)
+
+/*
+ * We should clear IMR at preinstall/uninstall, and just check at postinstall.
+ */
+#define GEN5_ASSERT_IIR_IS_ZERO(reg) do { \
+       u32 val = I915_READ(reg); \
+       if (val) { \
+               WARN(1, "Interrupt register 0x%x is not zero: 0x%08x\n", \
+                    (reg), val); \
+               I915_WRITE((reg), 0xffffffff); \
+               POSTING_READ(reg); \
+               I915_WRITE((reg), 0xffffffff); \
+               POSTING_READ(reg); \
+       } \
+} while (0)
+
+#define GEN8_IRQ_INIT_NDX(type, which, imr_val, ier_val) do { \
+       GEN5_ASSERT_IIR_IS_ZERO(GEN8_##type##_IIR(which)); \
+       I915_WRITE(GEN8_##type##_IMR(which), (imr_val)); \
+       I915_WRITE(GEN8_##type##_IER(which), (ier_val)); \
+       POSTING_READ(GEN8_##type##_IER(which)); \
+} while (0)
+
+#define GEN5_IRQ_INIT(type, imr_val, ier_val) do { \
+       GEN5_ASSERT_IIR_IS_ZERO(type##IIR); \
+       I915_WRITE(type##IMR, (imr_val)); \
+       I915_WRITE(type##IER, (ier_val)); \
+       POSTING_READ(type##IER); \
+} while (0)
+
 /* For display hotplug interrupt */
 static void
 ironlake_enable_display_irq(struct drm_i915_private *dev_priv, u32 mask)
 {
        assert_spin_locked(&dev_priv->irq_lock);
 
-       if (dev_priv->pm.irqs_disabled) {
-               WARN(1, "IRQs disabled\n");
-               dev_priv->pm.regsave.deimr &= ~mask;
+       if (WARN_ON(dev_priv->pm.irqs_disabled))
                return;
-       }
 
        if ((dev_priv->irq_mask & mask) != 0) {
                dev_priv->irq_mask &= ~mask;
@@ -104,11 +151,8 @@ ironlake_disable_display_irq(struct drm_i915_private *dev_priv, u32 mask)
 {
        assert_spin_locked(&dev_priv->irq_lock);
 
-       if (dev_priv->pm.irqs_disabled) {
-               WARN(1, "IRQs disabled\n");
-               dev_priv->pm.regsave.deimr |= mask;
+       if (WARN_ON(dev_priv->pm.irqs_disabled))
                return;
-       }
 
        if ((dev_priv->irq_mask & mask) != mask) {
                dev_priv->irq_mask |= mask;
@@ -129,13 +173,8 @@ static void ilk_update_gt_irq(struct drm_i915_private *dev_priv,
 {
        assert_spin_locked(&dev_priv->irq_lock);
 
-       if (dev_priv->pm.irqs_disabled) {
-               WARN(1, "IRQs disabled\n");
-               dev_priv->pm.regsave.gtimr &= ~interrupt_mask;
-               dev_priv->pm.regsave.gtimr |= (~enabled_irq_mask &
-                                               interrupt_mask);
+       if (WARN_ON(dev_priv->pm.irqs_disabled))
                return;
-       }
 
        dev_priv->gt_irq_mask &= ~interrupt_mask;
        dev_priv->gt_irq_mask |= (~enabled_irq_mask & interrupt_mask);
@@ -167,13 +206,8 @@ static void snb_update_pm_irq(struct drm_i915_private *dev_priv,
 
        assert_spin_locked(&dev_priv->irq_lock);
 
-       if (dev_priv->pm.irqs_disabled) {
-               WARN(1, "IRQs disabled\n");
-               dev_priv->pm.regsave.gen6_pmimr &= ~interrupt_mask;
-               dev_priv->pm.regsave.gen6_pmimr |= (~enabled_irq_mask &
-                                                    interrupt_mask);
+       if (WARN_ON(dev_priv->pm.irqs_disabled))
                return;
-       }
 
        new_val = dev_priv->pm_irq_mask;
        new_val &= ~interrupt_mask;
@@ -214,6 +248,46 @@ static bool ivb_can_enable_err_int(struct drm_device *dev)
        return true;
 }
 
+/**
+  * bdw_update_pm_irq - update GT interrupt 2
+  * @dev_priv: driver private
+  * @interrupt_mask: mask of interrupt bits to update
+  * @enabled_irq_mask: mask of interrupt bits to enable
+  *
+  * Copied from the snb function, updated with relevant register offsets
+  */
+static void bdw_update_pm_irq(struct drm_i915_private *dev_priv,
+                             uint32_t interrupt_mask,
+                             uint32_t enabled_irq_mask)
+{
+       uint32_t new_val;
+
+       assert_spin_locked(&dev_priv->irq_lock);
+
+       if (WARN_ON(dev_priv->pm.irqs_disabled))
+               return;
+
+       new_val = dev_priv->pm_irq_mask;
+       new_val &= ~interrupt_mask;
+       new_val |= (~enabled_irq_mask & interrupt_mask);
+
+       if (new_val != dev_priv->pm_irq_mask) {
+               dev_priv->pm_irq_mask = new_val;
+               I915_WRITE(GEN8_GT_IMR(2), dev_priv->pm_irq_mask);
+               POSTING_READ(GEN8_GT_IMR(2));
+       }
+}
+
+void bdw_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask)
+{
+       bdw_update_pm_irq(dev_priv, mask, mask);
+}
+
+void bdw_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask)
+{
+       bdw_update_pm_irq(dev_priv, mask, 0);
+}
+
 static bool cpt_can_enable_serr_int(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -232,16 +306,51 @@ static bool cpt_can_enable_serr_int(struct drm_device *dev)
        return true;
 }
 
-static void i9xx_clear_fifo_underrun(struct drm_device *dev, enum pipe pipe)
+void i9xx_check_fifo_underruns(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *crtc;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev_priv->irq_lock, flags);
+
+       for_each_intel_crtc(dev, crtc) {
+               u32 reg = PIPESTAT(crtc->pipe);
+               u32 pipestat;
+
+               if (crtc->cpu_fifo_underrun_disabled)
+                       continue;
+
+               pipestat = I915_READ(reg) & 0xffff0000;
+               if ((pipestat & PIPE_FIFO_UNDERRUN_STATUS) == 0)
+                       continue;
+
+               I915_WRITE(reg, pipestat | PIPE_FIFO_UNDERRUN_STATUS);
+               POSTING_READ(reg);
+
+               DRM_ERROR("pipe %c underrun\n", pipe_name(crtc->pipe));
+       }
+
+       spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
+}
+
+static void i9xx_set_fifo_underrun_reporting(struct drm_device *dev,
+                                            enum pipe pipe,
+                                            bool enable, bool old)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 reg = PIPESTAT(pipe);
-       u32 pipestat = I915_READ(reg) & 0x7fff0000;
+       u32 pipestat = I915_READ(reg) & 0xffff0000;
 
        assert_spin_locked(&dev_priv->irq_lock);
 
-       I915_WRITE(reg, pipestat | PIPE_FIFO_UNDERRUN_STATUS);
-       POSTING_READ(reg);
+       if (enable) {
+               I915_WRITE(reg, pipestat | PIPE_FIFO_UNDERRUN_STATUS);
+               POSTING_READ(reg);
+       } else {
+               if (old && pipestat & PIPE_FIFO_UNDERRUN_STATUS)
+                       DRM_ERROR("pipe %c underrun\n", pipe_name(pipe));
+       }
 }
 
 static void ironlake_set_fifo_underrun_reporting(struct drm_device *dev,
@@ -258,7 +367,8 @@ static void ironlake_set_fifo_underrun_reporting(struct drm_device *dev,
 }
 
 static void ivybridge_set_fifo_underrun_reporting(struct drm_device *dev,
-                                                 enum pipe pipe, bool enable)
+                                                 enum pipe pipe,
+                                                 bool enable, bool old)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        if (enable) {
@@ -269,15 +379,12 @@ static void ivybridge_set_fifo_underrun_reporting(struct drm_device *dev,
 
                ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB);
        } else {
-               bool was_enabled = !(I915_READ(DEIMR) & DE_ERR_INT_IVB);
-
-               /* Change the state _after_ we've read out the current one. */
                ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
 
-               if (!was_enabled &&
-                   (I915_READ(GEN7_ERR_INT) & ERR_INT_FIFO_UNDERRUN(pipe))) {
-                       DRM_DEBUG_KMS("uncleared fifo underrun on pipe %c\n",
-                                     pipe_name(pipe));
+               if (old &&
+                   I915_READ(GEN7_ERR_INT) & ERR_INT_FIFO_UNDERRUN(pipe)) {
+                       DRM_ERROR("uncleared fifo underrun on pipe %c\n",
+                                 pipe_name(pipe));
                }
        }
 }
@@ -313,14 +420,8 @@ static void ibx_display_interrupt_update(struct drm_i915_private *dev_priv,
 
        assert_spin_locked(&dev_priv->irq_lock);
 
-       if (dev_priv->pm.irqs_disabled &&
-           (interrupt_mask & SDE_HOTPLUG_MASK_CPT)) {
-               WARN(1, "IRQs disabled\n");
-               dev_priv->pm.regsave.sdeimr &= ~interrupt_mask;
-               dev_priv->pm.regsave.sdeimr |= (~enabled_irq_mask &
-                                                interrupt_mask);
+       if (WARN_ON(dev_priv->pm.irqs_disabled))
                return;
-       }
 
        I915_WRITE(SDEIMR, sdeimr);
        POSTING_READ(SDEIMR);
@@ -346,7 +447,7 @@ static void ibx_set_fifo_underrun_reporting(struct drm_device *dev,
 
 static void cpt_set_fifo_underrun_reporting(struct drm_device *dev,
                                            enum transcoder pch_transcoder,
-                                           bool enable)
+                                           bool enable, bool old)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
@@ -359,16 +460,12 @@ static void cpt_set_fifo_underrun_reporting(struct drm_device *dev,
 
                ibx_enable_display_interrupt(dev_priv, SDE_ERROR_CPT);
        } else {
-               uint32_t tmp = I915_READ(SERR_INT);
-               bool was_enabled = !(I915_READ(SDEIMR) & SDE_ERROR_CPT);
-
-               /* Change the state _after_ we've read out the current one. */
                ibx_disable_display_interrupt(dev_priv, SDE_ERROR_CPT);
 
-               if (!was_enabled &&
-                   (tmp & SERR_INT_TRANS_FIFO_UNDERRUN(pch_transcoder))) {
-                       DRM_DEBUG_KMS("uncleared pch fifo underrun on pch transcoder %c\n",
-                                     transcoder_name(pch_transcoder));
+               if (old && I915_READ(SERR_INT) &
+                   SERR_INT_TRANS_FIFO_UNDERRUN(pch_transcoder)) {
+                       DRM_ERROR("uncleared pch fifo underrun on pch transcoder %c\n",
+                                 transcoder_name(pch_transcoder));
                }
        }
 }
@@ -387,34 +484,29 @@ static void cpt_set_fifo_underrun_reporting(struct drm_device *dev,
  *
  * Returns the previous state of underrun reporting.
  */
-bool __intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
-                                            enum pipe pipe, bool enable)
+static 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);
-       bool ret;
+       bool old;
 
        assert_spin_locked(&dev_priv->irq_lock);
 
-       ret = !intel_crtc->cpu_fifo_underrun_disabled;
-
-       if (enable == ret)
-               goto done;
-
+       old = !intel_crtc->cpu_fifo_underrun_disabled;
        intel_crtc->cpu_fifo_underrun_disabled = !enable;
 
-       if (enable && (INTEL_INFO(dev)->gen < 5 || IS_VALLEYVIEW(dev)))
-               i9xx_clear_fifo_underrun(dev, pipe);
+       if (INTEL_INFO(dev)->gen < 5 || IS_VALLEYVIEW(dev))
+               i9xx_set_fifo_underrun_reporting(dev, pipe, enable, old);
        else 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, pipe, enable);
+               ivybridge_set_fifo_underrun_reporting(dev, pipe, enable, old);
        else if (IS_GEN8(dev))
                broadwell_set_fifo_underrun_reporting(dev, pipe, enable);
 
-done:
-       return ret;
+       return old;
 }
 
 bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
@@ -463,7 +555,7 @@ bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
        struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pch_transcoder];
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        unsigned long flags;
-       bool ret;
+       bool old;
 
        /*
         * NOTE: Pre-LPT has a fixed cpu pipe -> pch transcoder mapping, but LPT
@@ -476,21 +568,16 @@ bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
 
        spin_lock_irqsave(&dev_priv->irq_lock, flags);
 
-       ret = !intel_crtc->pch_fifo_underrun_disabled;
-
-       if (enable == ret)
-               goto done;
-
+       old = !intel_crtc->pch_fifo_underrun_disabled;
        intel_crtc->pch_fifo_underrun_disabled = !enable;
 
        if (HAS_PCH_IBX(dev))
                ibx_set_fifo_underrun_reporting(dev, pch_transcoder, enable);
        else
-               cpt_set_fifo_underrun_reporting(dev, pch_transcoder, enable);
+               cpt_set_fifo_underrun_reporting(dev, pch_transcoder, enable, old);
 
-done:
        spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
-       return ret;
+       return old;
 }
 
 
@@ -503,8 +590,10 @@ __i915_enable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
 
        assert_spin_locked(&dev_priv->irq_lock);
 
-       if (WARN_ON_ONCE(enable_mask & ~PIPESTAT_INT_ENABLE_MASK ||
-                        status_mask & ~PIPESTAT_INT_STATUS_MASK))
+       if (WARN_ONCE(enable_mask & ~PIPESTAT_INT_ENABLE_MASK ||
+                     status_mask & ~PIPESTAT_INT_STATUS_MASK,
+                     "pipe %c: enable_mask=0x%x, status_mask=0x%x\n",
+                     pipe_name(pipe), enable_mask, status_mask))
                return;
 
        if ((pipestat & enable_mask) == enable_mask)
@@ -527,8 +616,10 @@ __i915_disable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
 
        assert_spin_locked(&dev_priv->irq_lock);
 
-       if (WARN_ON_ONCE(enable_mask & ~PIPESTAT_INT_ENABLE_MASK ||
-                        status_mask & ~PIPESTAT_INT_STATUS_MASK))
+       if (WARN_ONCE(enable_mask & ~PIPESTAT_INT_ENABLE_MASK ||
+                     status_mask & ~PIPESTAT_INT_STATUS_MASK,
+                     "pipe %c: enable_mask=0x%x, status_mask=0x%x\n",
+                     pipe_name(pipe), enable_mask, status_mask))
                return;
 
        if ((pipestat & enable_mask) == 0)
@@ -546,11 +637,17 @@ static u32 vlv_get_pipestat_enable_mask(struct drm_device *dev, u32 status_mask)
        u32 enable_mask = status_mask << 16;
 
        /*
-        * On pipe A we don't support the PSR interrupt yet, on pipe B the
-        * same bit MBZ.
+        * On pipe A we don't support the PSR interrupt yet,
+        * on pipe B and C the same bit MBZ.
         */
        if (WARN_ON_ONCE(status_mask & PIPE_A_PSR_STATUS_VLV))
                return 0;
+       /*
+        * On pipe B and C we don't support the PSR interrupt yet, on pipe
+        * A the same bit is for perf counters which we don't use either.
+        */
+       if (WARN_ON_ONCE(status_mask & PIPE_B_PSR_STATUS_VLV))
+               return 0;
 
        enable_mask &= ~(PIPE_FIFO_UNDERRUN_STATUS |
                         SPRITE0_FLIP_DONE_INT_EN_VLV |
@@ -637,6 +734,56 @@ i915_pipe_enabled(struct drm_device *dev, int pipe)
        }
 }
 
+/*
+ * This timing diagram depicts the video signal in and
+ * around the vertical blanking period.
+ *
+ * Assumptions about the fictitious mode used in this example:
+ *  vblank_start >= 3
+ *  vsync_start = vblank_start + 1
+ *  vsync_end = vblank_start + 2
+ *  vtotal = vblank_start + 3
+ *
+ *           start of vblank:
+ *           latch double buffered registers
+ *           increment frame counter (ctg+)
+ *           generate start of vblank interrupt (gen4+)
+ *           |
+ *           |          frame start:
+ *           |          generate frame start interrupt (aka. vblank interrupt) (gmch)
+ *           |          may be shifted forward 1-3 extra lines via PIPECONF
+ *           |          |
+ *           |          |  start of vsync:
+ *           |          |  generate vsync interrupt
+ *           |          |  |
+ * ___xxxx___    ___xxxx___    ___xxxx___    ___xxxx___    ___xxxx___    ___xxxx
+ *       .   \hs/   .      \hs/          \hs/          \hs/   .      \hs/
+ * ----va---> <-----------------vb--------------------> <--------va-------------
+ *       |          |       <----vs----->                     |
+ * -vbs-----> <---vbs+1---> <---vbs+2---> <-----0-----> <-----1-----> <-----2--- (scanline counter gen2)
+ * -vbs-2---> <---vbs-1---> <---vbs-----> <---vbs+1---> <---vbs+2---> <-----0--- (scanline counter gen3+)
+ * -vbs-2---> <---vbs-2---> <---vbs-1---> <---vbs-----> <---vbs+1---> <---vbs+2- (scanline counter hsw+ hdmi)
+ *       |          |                                         |
+ *       last visible pixel                                   first visible pixel
+ *                  |                                         increment frame counter (gen3/4)
+ *                  pixel counter = vblank_start * htotal     pixel counter = 0 (gen3/4)
+ *
+ * x  = horizontal active
+ * _  = horizontal blanking
+ * hs = horizontal sync
+ * va = vertical active
+ * vb = vertical blanking
+ * vs = vertical sync
+ * vbs = vblank_start (number)
+ *
+ * Summary:
+ * - most events happen at the start of horizontal sync
+ * - frame start happens at the start of horizontal blank, 1-4 lines
+ *   (depending on PIPECONF settings) after the start of vblank
+ * - gen3/4 pixel and frame counter are synchronized with the start
+ *   of horizontal active on the first line of vertical active
+ */
+
 static u32 i8xx_get_vblank_counter(struct drm_device *dev, int pipe)
 {
        /* Gen2 doesn't have a hardware frame counter */
@@ -651,7 +798,7 @@ static u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
        struct drm_i915_private *dev_priv = dev->dev_private;
        unsigned long high_frame;
        unsigned long low_frame;
-       u32 high1, high2, low, pixel, vbl_start;
+       u32 high1, high2, low, pixel, vbl_start, hsync_start, htotal;
 
        if (!i915_pipe_enabled(dev, pipe)) {
                DRM_DEBUG_DRIVER("trying to get vblank count for disabled "
@@ -665,17 +812,28 @@ static u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
                const struct drm_display_mode *mode =
                        &intel_crtc->config.adjusted_mode;
 
-               vbl_start = mode->crtc_vblank_start * mode->crtc_htotal;
+               htotal = mode->crtc_htotal;
+               hsync_start = mode->crtc_hsync_start;
+               vbl_start = mode->crtc_vblank_start;
+               if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+                       vbl_start = DIV_ROUND_UP(vbl_start, 2);
        } else {
                enum transcoder cpu_transcoder = (enum transcoder) pipe;
-               u32 htotal;
 
                htotal = ((I915_READ(HTOTAL(cpu_transcoder)) >> 16) & 0x1fff) + 1;
+               hsync_start = (I915_READ(HSYNC(cpu_transcoder))  & 0x1fff) + 1;
                vbl_start = (I915_READ(VBLANK(cpu_transcoder)) & 0x1fff) + 1;
-
-               vbl_start *= htotal;
+               if ((I915_READ(PIPECONF(cpu_transcoder)) &
+                    PIPECONF_INTERLACE_MASK) != PIPECONF_PROGRESSIVE)
+                       vbl_start = DIV_ROUND_UP(vbl_start, 2);
        }
 
+       /* Convert to pixel count */
+       vbl_start *= htotal;
+
+       /* Start of vblank event occurs at start of hsync */
+       vbl_start -= htotal - hsync_start;
+
        high_frame = PIPEFRAME(pipe);
        low_frame = PIPEFRAMEPIXEL(pipe);
 
@@ -719,24 +877,28 @@ static u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
 /* raw reads, only for fast reads of display block, no need for forcewake etc. */
 #define __raw_i915_read32(dev_priv__, reg__) readl((dev_priv__)->regs + (reg__))
 
-static bool ilk_pipe_in_vblank_locked(struct drm_device *dev, enum pipe pipe)
+static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
 {
+       struct drm_device *dev = crtc->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       uint32_t status;
-       int reg;
+       const struct drm_display_mode *mode = &crtc->config.adjusted_mode;
+       enum pipe pipe = crtc->pipe;
+       int position, vtotal;
 
-       if (INTEL_INFO(dev)->gen >= 8) {
-               status = GEN8_PIPE_VBLANK;
-               reg = GEN8_DE_PIPE_ISR(pipe);
-       } else if (INTEL_INFO(dev)->gen >= 7) {
-               status = DE_PIPE_VBLANK_IVB(pipe);
-               reg = DEISR;
-       } else {
-               status = DE_PIPE_VBLANK(pipe);
-               reg = DEISR;
-       }
+       vtotal = mode->crtc_vtotal;
+       if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+               vtotal /= 2;
+
+       if (IS_GEN2(dev))
+               position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN2;
+       else
+               position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN3;
 
-       return __raw_i915_read32(dev_priv, reg) & status;
+       /*
+        * See update_scanline_offset() for the details on the
+        * scanline_offset adjustment.
+        */
+       return (position + crtc->scanline_offset) % vtotal;
 }
 
 static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
@@ -748,7 +910,7 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        const struct drm_display_mode *mode = &intel_crtc->config.adjusted_mode;
        int position;
-       int vbl_start, vbl_end, htotal, vtotal;
+       int vbl_start, vbl_end, hsync_start, htotal, vtotal;
        bool in_vbl = true;
        int ret = 0;
        unsigned long irqflags;
@@ -760,6 +922,7 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
        }
 
        htotal = mode->crtc_htotal;
+       hsync_start = mode->crtc_hsync_start;
        vtotal = mode->crtc_vtotal;
        vbl_start = mode->crtc_vblank_start;
        vbl_end = mode->crtc_vblank_end;
@@ -778,7 +941,7 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
         * following code must not block on uncore.lock.
         */
        spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
-       
+
        /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
 
        /* Get optional system timestamp before query. */
@@ -789,68 +952,7 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
                /* No obvious pixelcount register. Only query vertical
                 * scanout position from Display scan line register.
                 */
-               if (IS_GEN2(dev))
-                       position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN2;
-               else
-                       position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN3;
-
-               if (HAS_DDI(dev)) {
-                       /*
-                        * On HSW HDMI outputs there seems to be a 2 line
-                        * difference, whereas eDP has the normal 1 line
-                        * difference that earlier platforms have. External
-                        * DP is unknown. For now just check for the 2 line
-                        * difference case on all output types on HSW+.
-                        *
-                        * This might misinterpret the scanline counter being
-                        * one line too far along on eDP, but that's less
-                        * dangerous than the alternative since that would lead
-                        * the vblank timestamp code astray when it sees a
-                        * scanline count before vblank_start during a vblank
-                        * interrupt.
-                        */
-                       in_vbl = ilk_pipe_in_vblank_locked(dev, pipe);
-                       if ((in_vbl && (position == vbl_start - 2 ||
-                                       position == vbl_start - 1)) ||
-                           (!in_vbl && (position == vbl_end - 2 ||
-                                        position == vbl_end - 1)))
-                               position = (position + 2) % vtotal;
-               } else if (HAS_PCH_SPLIT(dev)) {
-                       /*
-                        * The scanline counter increments at the leading edge
-                        * of hsync, ie. it completely misses the active portion
-                        * of the line. Fix up the counter at both edges of vblank
-                        * to get a more accurate picture whether we're in vblank
-                        * or not.
-                        */
-                       in_vbl = ilk_pipe_in_vblank_locked(dev, pipe);
-                       if ((in_vbl && position == vbl_start - 1) ||
-                           (!in_vbl && position == vbl_end - 1))
-                               position = (position + 1) % vtotal;
-               } else {
-                       /*
-                        * ISR vblank status bits don't work the way we'd want
-                        * them to work on non-PCH platforms (for
-                        * ilk_pipe_in_vblank_locked()), and there doesn't
-                        * appear any other way to determine if we're currently
-                        * in vblank.
-                        *
-                        * Instead let's assume that we're already in vblank if
-                        * we got called from the vblank interrupt and the
-                        * scanline counter value indicates that we're on the
-                        * line just prior to vblank start. This should result
-                        * in the correct answer, unless the vblank interrupt
-                        * delivery really got delayed for almost exactly one
-                        * full frame/field.
-                        */
-                       if (flags & DRM_CALLED_FROM_VBLIRQ &&
-                           position == vbl_start - 1) {
-                               position = (position + 1) % vtotal;
-
-                               /* Signal this correction as "applied". */
-                               ret |= 0x8;
-                       }
-               }
+               position = __intel_get_crtc_scanline(intel_crtc);
        } else {
                /* Have access to pixelcount since start of frame.
                 * We can split this into vertical and horizontal
@@ -862,6 +964,29 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
                vbl_start *= htotal;
                vbl_end *= htotal;
                vtotal *= htotal;
+
+               /*
+                * In interlaced modes, the pixel counter counts all pixels,
+                * so one field will have htotal more pixels. In order to avoid
+                * the reported position from jumping backwards when the pixel
+                * counter is beyond the length of the shorter field, just
+                * clamp the position the length of the shorter field. This
+                * matches how the scanline counter based position works since
+                * the scanline counter doesn't count the two half lines.
+                */
+               if (position >= vtotal)
+                       position = vtotal - 1;
+
+               /*
+                * Start of vblank interrupt is triggered at start of hsync,
+                * just prior to the first active line of vblank. However we
+                * consider lines to start at the leading edge of horizontal
+                * active. So, should we get here before we've crossed into
+                * the horizontal active of the first line in vblank, we would
+                * not set the DRM_SCANOUTPOS_INVBL flag. In order to fix that,
+                * always add htotal-hsync_start to the current pixel position.
+                */
+               position = (position + htotal - hsync_start) % vtotal;
        }
 
        /* Get optional system timestamp after query. */
@@ -900,6 +1025,19 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
        return ret;
 }
 
+int intel_get_crtc_scanline(struct intel_crtc *crtc)
+{
+       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+       unsigned long irqflags;
+       int position;
+
+       spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
+       position = __intel_get_crtc_scanline(crtc);
+       spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
+
+       return position;
+}
+
 static int i915_get_vblank_timestamp(struct drm_device *dev, int pipe,
                              int *max_error,
                              struct timeval *vblank_time,
@@ -945,7 +1083,7 @@ static bool intel_hpd_irq_event(struct drm_device *dev,
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n",
                      connector->base.id,
-                     drm_get_connector_name(connector),
+                     connector->name,
                      drm_get_connector_status_name(old_status),
                      drm_get_connector_status_name(connector->status));
 
@@ -990,7 +1128,7 @@ static void i915_hotplug_work_func(struct work_struct *work)
                    connector->polled == DRM_CONNECTOR_POLL_HPD) {
                        DRM_INFO("HPD interrupt storm detected on connector %s: "
                                 "switching from hotplug detection to polling\n",
-                               drm_get_connector_name(connector));
+                               connector->name);
                        dev_priv->hpd_stats[intel_encoder->hpd_pin].hpd_mark = HPD_DISABLED;
                        connector->polled = DRM_CONNECTOR_POLL_CONNECT
                                | DRM_CONNECTOR_POLL_DISCONNECT;
@@ -998,7 +1136,7 @@ static void i915_hotplug_work_func(struct work_struct *work)
                }
                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);
+                                     connector->name, intel_encoder->hpd_pin);
                }
        }
         /* if there were no outputs to poll, poll was disabled,
@@ -1073,9 +1211,9 @@ static void ironlake_rps_change_irq_handler(struct drm_device *dev)
 }
 
 static void notify_ring(struct drm_device *dev,
-                       struct intel_ring_buffer *ring)
+                       struct intel_engine_cs *ring)
 {
-       if (ring->obj == NULL)
+       if (!intel_ring_initialized(ring))
                return;
 
        trace_i915_gem_request_complete(ring);
@@ -1094,8 +1232,12 @@ static void gen6_pm_rps_work(struct work_struct *work)
        spin_lock_irq(&dev_priv->irq_lock);
        pm_iir = dev_priv->rps.pm_iir;
        dev_priv->rps.pm_iir = 0;
-       /* Make sure not to corrupt PMIMR state used by ringbuffer code */
-       snb_enable_pm_irq(dev_priv, dev_priv->pm_rps_events);
+       if (IS_BROADWELL(dev_priv->dev))
+               bdw_enable_pm_irq(dev_priv, dev_priv->pm_rps_events);
+       else {
+               /* Make sure not to corrupt PMIMR state used by ringbuffer */
+               snb_enable_pm_irq(dev_priv, dev_priv->pm_rps_events);
+       }
        spin_unlock_irq(&dev_priv->irq_lock);
 
        /* Make sure we didn't queue anything we're not going to process. */
@@ -1292,6 +1434,19 @@ static void snb_gt_irq_handler(struct drm_device *dev,
                ivybridge_parity_error_irq_handler(dev, gt_iir);
 }
 
+static void gen8_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir)
+{
+       if ((pm_iir & dev_priv->pm_rps_events) == 0)
+               return;
+
+       spin_lock(&dev_priv->irq_lock);
+       dev_priv->rps.pm_iir |= pm_iir & dev_priv->pm_rps_events;
+       bdw_disable_pm_irq(dev_priv, pm_iir & dev_priv->pm_rps_events);
+       spin_unlock(&dev_priv->irq_lock);
+
+       queue_work(dev_priv->wq, &dev_priv->rps.work);
+}
+
 static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev,
                                       struct drm_i915_private *dev_priv,
                                       u32 master_ctl)
@@ -1315,18 +1470,32 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev,
                        DRM_ERROR("The master control interrupt lied (GT0)!\n");
        }
 
-       if (master_ctl & GEN8_GT_VCS1_IRQ) {
+       if (master_ctl & (GEN8_GT_VCS1_IRQ | GEN8_GT_VCS2_IRQ)) {
                tmp = I915_READ(GEN8_GT_IIR(1));
                if (tmp) {
                        ret = IRQ_HANDLED;
                        vcs = tmp >> GEN8_VCS1_IRQ_SHIFT;
                        if (vcs & GT_RENDER_USER_INTERRUPT)
                                notify_ring(dev, &dev_priv->ring[VCS]);
+                       vcs = tmp >> GEN8_VCS2_IRQ_SHIFT;
+                       if (vcs & GT_RENDER_USER_INTERRUPT)
+                               notify_ring(dev, &dev_priv->ring[VCS2]);
                        I915_WRITE(GEN8_GT_IIR(1), tmp);
                } else
                        DRM_ERROR("The master control interrupt lied (GT1)!\n");
        }
 
+       if (master_ctl & GEN8_GT_PM_IRQ) {
+               tmp = I915_READ(GEN8_GT_IIR(2));
+               if (tmp & dev_priv->pm_rps_events) {
+                       ret = IRQ_HANDLED;
+                       gen8_rps_irq_handler(dev_priv, tmp);
+                       I915_WRITE(GEN8_GT_IIR(2),
+                                  tmp & dev_priv->pm_rps_events);
+               } else
+                       DRM_ERROR("The master control interrupt lied (PM)!\n");
+       }
+
        if (master_ctl & GEN8_GT_VECS_IRQ) {
                tmp = I915_READ(GEN8_GT_IIR(3));
                if (tmp) {
@@ -1549,6 +1718,19 @@ static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir)
        }
 }
 
+static bool intel_pipe_handle_vblank(struct drm_device *dev, enum pipe pipe)
+{
+       struct intel_crtc *crtc;
+
+       if (!drm_handle_vblank(dev, pipe))
+               return false;
+
+       crtc = to_intel_crtc(intel_get_crtc_for_pipe(dev, pipe));
+       wake_up(&crtc->vbl_wait);
+
+       return true;
+}
+
 static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1578,6 +1760,9 @@ static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir)
                case PIPE_B:
                        iir_bit = I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
                        break;
+               case PIPE_C:
+                       iir_bit = I915_DISPLAY_PIPE_C_EVENT_INTERRUPT;
+                       break;
                }
                if (iir & iir_bit)
                        mask |= dev_priv->pipestat_irq_mask[pipe];
@@ -1600,7 +1785,7 @@ static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir)
 
        for_each_pipe(pipe) {
                if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS)
-                       drm_handle_vblank(dev, pipe);
+                       intel_pipe_handle_vblank(dev, pipe);
 
                if (pipe_stats[pipe] & PLANE_FLIP_DONE_INT_STATUS_VLV) {
                        intel_prepare_page_flip(dev, pipe);
@@ -1619,9 +1804,36 @@ static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir)
                gmbus_irq_handler(dev);
 }
 
+static void i9xx_hpd_irq_handler(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
+
+       if (IS_G4X(dev)) {
+               u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_G4X;
+
+               intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_g4x);
+       } else {
+               u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_I915;
+
+               intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915);
+       }
+
+       if ((IS_G4X(dev) || IS_VALLEYVIEW(dev)) &&
+           hotplug_status & DP_AUX_CHANNEL_MASK_INT_STATUS_G4X)
+               dp_aux_irq_handler(dev);
+
+       I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
+       /*
+        * Make sure hotplug status is cleared before we clear IIR, or else we
+        * may miss hotplug events.
+        */
+       POSTING_READ(PORT_HOTPLUG_STAT);
+}
+
 static irqreturn_t valleyview_irq_handler(int irq, void *arg)
 {
-       struct drm_device *dev = (struct drm_device *) arg;
+       struct drm_device *dev = arg;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 iir, gt_iir, pm_iir;
        irqreturn_t ret = IRQ_NONE;
@@ -1641,19 +1853,8 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
                valleyview_pipestat_irq_handler(dev, iir);
 
                /* Consume port.  Then clear IIR or we'll miss events */
-               if (iir & I915_DISPLAY_PORT_INTERRUPT) {
-                       u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
-                       u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_I915;
-
-                       intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915);
-
-                       if (hotplug_status & DP_AUX_CHANNEL_MASK_INT_STATUS_G4X)
-                               dp_aux_irq_handler(dev);
-
-                       I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
-                       I915_READ(PORT_HOTPLUG_STAT);
-               }
-
+               if (iir & I915_DISPLAY_PORT_INTERRUPT)
+                       i9xx_hpd_irq_handler(dev);
 
                if (pm_iir)
                        gen6_rps_irq_handler(dev_priv, pm_iir);
@@ -1667,6 +1868,40 @@ out:
        return ret;
 }
 
+static irqreturn_t cherryview_irq_handler(int irq, void *arg)
+{
+       struct drm_device *dev = arg;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 master_ctl, iir;
+       irqreturn_t ret = IRQ_NONE;
+
+       for (;;) {
+               master_ctl = I915_READ(GEN8_MASTER_IRQ) & ~GEN8_MASTER_IRQ_CONTROL;
+               iir = I915_READ(VLV_IIR);
+
+               if (master_ctl == 0 && iir == 0)
+                       break;
+
+               I915_WRITE(GEN8_MASTER_IRQ, 0);
+
+               gen8_gt_irq_handler(dev, dev_priv, master_ctl);
+
+               valleyview_pipestat_irq_handler(dev, iir);
+
+               /* Consume port.  Then clear IIR or we'll miss events */
+               i9xx_hpd_irq_handler(dev);
+
+               I915_WRITE(VLV_IIR, iir);
+
+               I915_WRITE(GEN8_MASTER_IRQ, DE_MASTER_IRQ_CONTROL);
+               POSTING_READ(GEN8_MASTER_IRQ);
+
+               ret = IRQ_HANDLED;
+       }
+
+       return ret;
+}
+
 static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1827,7 +2062,7 @@ static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir)
 
        for_each_pipe(pipe) {
                if (de_iir & DE_PIPE_VBLANK(pipe))
-                       drm_handle_vblank(dev, pipe);
+                       intel_pipe_handle_vblank(dev, pipe);
 
                if (de_iir & DE_PIPE_FIFO_UNDERRUN(pipe))
                        if (intel_set_cpu_fifo_underrun_reporting(dev, pipe, false))
@@ -1877,7 +2112,7 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir)
 
        for_each_pipe(pipe) {
                if (de_iir & (DE_PIPE_VBLANK_IVB(pipe)))
-                       drm_handle_vblank(dev, pipe);
+                       intel_pipe_handle_vblank(dev, pipe);
 
                /* plane/pipes map 1:1 on ilk+ */
                if (de_iir & DE_PLANE_FLIP_DONE_IVB(pipe)) {
@@ -1899,7 +2134,7 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir)
 
 static irqreturn_t ironlake_irq_handler(int irq, void *arg)
 {
-       struct drm_device *dev = (struct drm_device *) arg;
+       struct drm_device *dev = arg;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 de_iir, gt_iir, de_ier, sde_ier = 0;
        irqreturn_t ret = IRQ_NONE;
@@ -2020,9 +2255,9 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
 
                pipe_iir = I915_READ(GEN8_DE_PIPE_IIR(pipe));
                if (pipe_iir & GEN8_PIPE_VBLANK)
-                       drm_handle_vblank(dev, pipe);
+                       intel_pipe_handle_vblank(dev, pipe);
 
-               if (pipe_iir & GEN8_PIPE_FLIP_DONE) {
+               if (pipe_iir & GEN8_PIPE_PRIMARY_FLIP_DONE) {
                        intel_prepare_page_flip(dev, pipe);
                        intel_finish_page_flip_plane(dev, pipe);
                }
@@ -2075,7 +2310,7 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
 static void i915_error_wake_up(struct drm_i915_private *dev_priv,
                               bool reset_completed)
 {
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        int i;
 
        /*
@@ -2136,6 +2371,14 @@ static void i915_error_work_func(struct work_struct *work)
                kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE,
                                   reset_event);
 
+               /*
+                * In most cases it's guaranteed that we get here with an RPM
+                * reference held, for example because there is a pending GPU
+                * request that won't finish until the reset is done. This
+                * isn't the case at least when we get here by doing a
+                * simulated reset via debugs, so get an RPM reference.
+                */
+               intel_runtime_pm_get(dev_priv);
                /*
                 * All state reset _must_ be completed before we update the
                 * reset counter, for otherwise waiters might miss the reset
@@ -2146,6 +2389,8 @@ static void i915_error_work_func(struct work_struct *work)
 
                intel_display_handle_reset(dev);
 
+               intel_runtime_pm_put(dev_priv);
+
                if (ret == 0) {
                        /*
                         * After all the gem state is reset, increment the reset
@@ -2383,10 +2628,6 @@ static int i915_enable_vblank(struct drm_device *dev, int pipe)
        else
                i915_enable_pipestat(dev_priv, pipe,
                                     PIPE_VBLANK_INTERRUPT_STATUS);
-
-       /* maintain vblank delivery even in deep C-states */
-       if (INTEL_INFO(dev)->gen == 3)
-               I915_WRITE(INSTPM, _MASKED_BIT_DISABLE(INSTPM_AGPBUSY_DIS));
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 
        return 0;
@@ -2450,9 +2691,6 @@ static void i915_disable_vblank(struct drm_device *dev, int pipe)
        unsigned long irqflags;
 
        spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
-       if (INTEL_INFO(dev)->gen == 3)
-               I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_AGPBUSY_DIS));
-
        i915_disable_pipestat(dev_priv, pipe,
                              PIPE_VBLANK_INTERRUPT_STATUS |
                              PIPE_START_VBLANK_INTERRUPT_STATUS);
@@ -2498,29 +2736,77 @@ static void gen8_disable_vblank(struct drm_device *dev, int pipe)
 }
 
 static u32
-ring_last_seqno(struct intel_ring_buffer *ring)
+ring_last_seqno(struct intel_engine_cs *ring)
 {
        return list_entry(ring->request_list.prev,
                          struct drm_i915_gem_request, list)->seqno;
 }
 
 static bool
-ring_idle(struct intel_ring_buffer *ring, u32 seqno)
+ring_idle(struct intel_engine_cs *ring, u32 seqno)
 {
        return (list_empty(&ring->request_list) ||
                i915_seqno_passed(seqno, ring_last_seqno(ring)));
 }
 
-static struct intel_ring_buffer *
-semaphore_waits_for(struct intel_ring_buffer *ring, u32 *seqno)
+static bool
+ipehr_is_semaphore_wait(struct drm_device *dev, u32 ipehr)
+{
+       if (INTEL_INFO(dev)->gen >= 8) {
+               /*
+                * FIXME: gen8 semaphore support - currently we don't emit
+                * semaphores on bdw anyway, but this needs to be addressed when
+                * we merge that code.
+                */
+               return false;
+       } else {
+               ipehr &= ~MI_SEMAPHORE_SYNC_MASK;
+               return ipehr == (MI_SEMAPHORE_MBOX | MI_SEMAPHORE_COMPARE |
+                                MI_SEMAPHORE_REGISTER);
+       }
+}
+
+static struct intel_engine_cs *
+semaphore_wait_to_signaller_ring(struct intel_engine_cs *ring, u32 ipehr)
+{
+       struct drm_i915_private *dev_priv = ring->dev->dev_private;
+       struct intel_engine_cs *signaller;
+       int i;
+
+       if (INTEL_INFO(dev_priv->dev)->gen >= 8) {
+               /*
+                * FIXME: gen8 semaphore support - currently we don't emit
+                * semaphores on bdw anyway, but this needs to be addressed when
+                * we merge that code.
+                */
+               return NULL;
+       } else {
+               u32 sync_bits = ipehr & MI_SEMAPHORE_SYNC_MASK;
+
+               for_each_ring(signaller, dev_priv, i) {
+                       if(ring == signaller)
+                               continue;
+
+                       if (sync_bits == signaller->semaphore.mbox.wait[ring->id])
+                               return signaller;
+               }
+       }
+
+       DRM_ERROR("No signaller ring found for ring %i, ipehr 0x%08x\n",
+                 ring->id, ipehr);
+
+       return NULL;
+}
+
+static struct intel_engine_cs *
+semaphore_waits_for(struct intel_engine_cs *ring, u32 *seqno)
 {
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
        u32 cmd, ipehr, head;
        int i;
 
        ipehr = I915_READ(RING_IPEHR(ring->mmio_base));
-       if ((ipehr & ~(0x3 << 16)) !=
-           (MI_SEMAPHORE_MBOX | MI_SEMAPHORE_COMPARE | MI_SEMAPHORE_REGISTER))
+       if (!ipehr_is_semaphore_wait(ring->dev, ipehr))
                return NULL;
 
        /*
@@ -2538,10 +2824,10 @@ semaphore_waits_for(struct intel_ring_buffer *ring, u32 *seqno)
                 * our ring is smaller than what the hardware (and hence
                 * HEAD_ADDR) allows. Also handles wrap-around.
                 */
-               head &= ring->size - 1;
+               head &= ring->buffer->size - 1;
 
                /* This here seems to blow up */
-               cmd = ioread32(ring->virtual_start + head);
+               cmd = ioread32(ring->buffer->virtual_start + head);
                if (cmd == ipehr)
                        break;
 
@@ -2551,14 +2837,14 @@ semaphore_waits_for(struct intel_ring_buffer *ring, u32 *seqno)
        if (!i)
                return NULL;
 
-       *seqno = ioread32(ring->virtual_start + head + 4) + 1;
-       return &dev_priv->ring[(ring->id + (((ipehr >> 17) & 1) + 1)) % 3];
+       *seqno = ioread32(ring->buffer->virtual_start + head + 4) + 1;
+       return semaphore_wait_to_signaller_ring(ring, ipehr);
 }
 
-static int semaphore_passed(struct intel_ring_buffer *ring)
+static int semaphore_passed(struct intel_engine_cs *ring)
 {
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
-       struct intel_ring_buffer *signaller;
+       struct intel_engine_cs *signaller;
        u32 seqno, ctl;
 
        ring->hangcheck.deadlock = true;
@@ -2577,7 +2863,7 @@ static int semaphore_passed(struct intel_ring_buffer *ring)
 
 static void semaphore_clear_deadlocks(struct drm_i915_private *dev_priv)
 {
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        int i;
 
        for_each_ring(ring, dev_priv, i)
@@ -2585,7 +2871,7 @@ static void semaphore_clear_deadlocks(struct drm_i915_private *dev_priv)
 }
 
 static enum intel_ring_hangcheck_action
-ring_stuck(struct intel_ring_buffer *ring, u64 acthd)
+ring_stuck(struct intel_engine_cs *ring, u64 acthd)
 {
        struct drm_device *dev = ring->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2641,7 +2927,7 @@ static void i915_hangcheck_elapsed(unsigned long data)
 {
        struct drm_device *dev = (struct drm_device *)data;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        int i;
        int busy_count = 0, rings_hung = 0;
        bool stuck[I915_NUM_RINGS] = { 0 };
@@ -2759,57 +3045,63 @@ void i915_queue_hangcheck(struct drm_device *dev)
                  round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES));
 }
 
-static void ibx_irq_preinstall(struct drm_device *dev)
+static void ibx_irq_reset(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
        if (HAS_PCH_NOP(dev))
                return;
 
-       /* 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.
-        */
+       GEN5_IRQ_RESET(SDE);
+
+       if (HAS_PCH_CPT(dev) || HAS_PCH_LPT(dev))
+               I915_WRITE(SERR_INT, 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.
+ *
+ * This function needs to be called before interrupts are enabled.
+ */
+static void ibx_irq_pre_postinstall(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (HAS_PCH_NOP(dev))
+               return;
+
+       WARN_ON(I915_READ(SDEIER) != 0);
        I915_WRITE(SDEIER, 0xffffffff);
        POSTING_READ(SDEIER);
 }
 
-static void gen5_gt_irq_preinstall(struct drm_device *dev)
+static void gen5_gt_irq_reset(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       /* and GT */
-       I915_WRITE(GTIMR, 0xffffffff);
-       I915_WRITE(GTIER, 0x0);
-       POSTING_READ(GTIER);
-
-       if (INTEL_INFO(dev)->gen >= 6) {
-               /* and PM */
-               I915_WRITE(GEN6_PMIMR, 0xffffffff);
-               I915_WRITE(GEN6_PMIER, 0x0);
-               POSTING_READ(GEN6_PMIER);
-       }
+       GEN5_IRQ_RESET(GT);
+       if (INTEL_INFO(dev)->gen >= 6)
+               GEN5_IRQ_RESET(GEN6_PM);
 }
 
 /* drm_dma.h hooks
 */
-static void ironlake_irq_preinstall(struct drm_device *dev)
+static void ironlake_irq_reset(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       I915_WRITE(HWSTAM, 0xeffe);
+       I915_WRITE(HWSTAM, 0xffffffff);
 
-       I915_WRITE(DEIMR, 0xffffffff);
-       I915_WRITE(DEIER, 0x0);
-       POSTING_READ(DEIER);
+       GEN5_IRQ_RESET(DE);
+       if (IS_GEN7(dev))
+               I915_WRITE(GEN7_ERR_INT, 0xffffffff);
 
-       gen5_gt_irq_preinstall(dev);
+       gen5_gt_irq_reset(dev);
 
-       ibx_irq_preinstall(dev);
+       ibx_irq_reset(dev);
 }
 
 static void valleyview_irq_preinstall(struct drm_device *dev)
@@ -2827,7 +3119,7 @@ static void valleyview_irq_preinstall(struct drm_device *dev)
        I915_WRITE(GTIIR, I915_READ(GTIIR));
        I915_WRITE(GTIIR, I915_READ(GTIIR));
 
-       gen5_gt_irq_preinstall(dev);
+       gen5_gt_irq_reset(dev);
 
        I915_WRITE(DPINVGTT, 0xff);
 
@@ -2841,7 +3133,15 @@ static void valleyview_irq_preinstall(struct drm_device *dev)
        POSTING_READ(VLV_IER);
 }
 
-static void gen8_irq_preinstall(struct drm_device *dev)
+static void gen8_gt_irq_reset(struct drm_i915_private *dev_priv)
+{
+       GEN8_IRQ_RESET_NDX(GT, 0);
+       GEN8_IRQ_RESET_NDX(GT, 1);
+       GEN8_IRQ_RESET_NDX(GT, 2);
+       GEN8_IRQ_RESET_NDX(GT, 3);
+}
+
+static void gen8_irq_reset(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        int pipe;
@@ -2849,43 +3149,44 @@ static void gen8_irq_preinstall(struct drm_device *dev)
        I915_WRITE(GEN8_MASTER_IRQ, 0);
        POSTING_READ(GEN8_MASTER_IRQ);
 
-       /* IIR can theoretically queue up two events. Be paranoid */
-#define GEN8_IRQ_INIT_NDX(type, which) do { \
-               I915_WRITE(GEN8_##type##_IMR(which), 0xffffffff); \
-               POSTING_READ(GEN8_##type##_IMR(which)); \
-               I915_WRITE(GEN8_##type##_IER(which), 0); \
-               I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
-               POSTING_READ(GEN8_##type##_IIR(which)); \
-               I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
-       } while (0)
-
-#define GEN8_IRQ_INIT(type) do { \
-               I915_WRITE(GEN8_##type##_IMR, 0xffffffff); \
-               POSTING_READ(GEN8_##type##_IMR); \
-               I915_WRITE(GEN8_##type##_IER, 0); \
-               I915_WRITE(GEN8_##type##_IIR, 0xffffffff); \
-               POSTING_READ(GEN8_##type##_IIR); \
-               I915_WRITE(GEN8_##type##_IIR, 0xffffffff); \
-       } while (0)
-
-       GEN8_IRQ_INIT_NDX(GT, 0);
-       GEN8_IRQ_INIT_NDX(GT, 1);
-       GEN8_IRQ_INIT_NDX(GT, 2);
-       GEN8_IRQ_INIT_NDX(GT, 3);
+       gen8_gt_irq_reset(dev_priv);
 
-       for_each_pipe(pipe) {
-               GEN8_IRQ_INIT_NDX(DE_PIPE, pipe);
-       }
+       for_each_pipe(pipe)
+               GEN8_IRQ_RESET_NDX(DE_PIPE, pipe);
+
+       GEN5_IRQ_RESET(GEN8_DE_PORT_);
+       GEN5_IRQ_RESET(GEN8_DE_MISC_);
+       GEN5_IRQ_RESET(GEN8_PCU_);
+
+       ibx_irq_reset(dev);
+}
+
+static void cherryview_irq_preinstall(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int pipe;
+
+       I915_WRITE(GEN8_MASTER_IRQ, 0);
+       POSTING_READ(GEN8_MASTER_IRQ);
 
-       GEN8_IRQ_INIT(DE_PORT);
-       GEN8_IRQ_INIT(DE_MISC);
-       GEN8_IRQ_INIT(PCU);
-#undef GEN8_IRQ_INIT
-#undef GEN8_IRQ_INIT_NDX
+       gen8_gt_irq_reset(dev_priv);
+
+       GEN5_IRQ_RESET(GEN8_PCU_);
 
        POSTING_READ(GEN8_PCU_IIR);
 
-       ibx_irq_preinstall(dev);
+       I915_WRITE(DPINVGTT, DPINVGTT_STATUS_MASK_CHV);
+
+       I915_WRITE(PORT_HOTPLUG_EN, 0);
+       I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
+
+       for_each_pipe(pipe)
+               I915_WRITE(PIPESTAT(pipe), 0xffff);
+
+       I915_WRITE(VLV_IMR, 0xffffffff);
+       I915_WRITE(VLV_IER, 0x0);
+       I915_WRITE(VLV_IIR, 0xffffffff);
+       POSTING_READ(VLV_IIR);
 }
 
 static void ibx_hpd_irq_setup(struct drm_device *dev)
@@ -2931,15 +3232,12 @@ static void ibx_irq_postinstall(struct drm_device *dev)
        if (HAS_PCH_NOP(dev))
                return;
 
-       if (HAS_PCH_IBX(dev)) {
+       if (HAS_PCH_IBX(dev))
                mask = SDE_GMBUS | SDE_AUX_MASK | SDE_POISON;
-       } else {
+       else
                mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT;
 
-               I915_WRITE(SERR_INT, I915_READ(SERR_INT));
-       }
-
-       I915_WRITE(SDEIIR, I915_READ(SDEIIR));
+       GEN5_ASSERT_IIR_IS_ZERO(SDEIIR);
        I915_WRITE(SDEIMR, ~mask);
 }
 
@@ -2965,10 +3263,7 @@ static void gen5_gt_irq_postinstall(struct drm_device *dev)
                gt_irqs |= GT_BLT_USER_INTERRUPT | GT_BSD_USER_INTERRUPT;
        }
 
-       I915_WRITE(GTIIR, I915_READ(GTIIR));
-       I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
-       I915_WRITE(GTIER, gt_irqs);
-       POSTING_READ(GTIER);
+       GEN5_IRQ_INIT(GT, dev_priv->gt_irq_mask, gt_irqs);
 
        if (INTEL_INFO(dev)->gen >= 6) {
                pm_irqs |= dev_priv->pm_rps_events;
@@ -2977,10 +3272,7 @@ static void gen5_gt_irq_postinstall(struct drm_device *dev)
                        pm_irqs |= PM_VEBOX_USER_INTERRUPT;
 
                dev_priv->pm_irq_mask = 0xffffffff;
-               I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR));
-               I915_WRITE(GEN6_PMIMR, dev_priv->pm_irq_mask);
-               I915_WRITE(GEN6_PMIER, pm_irqs);
-               POSTING_READ(GEN6_PMIER);
+               GEN5_IRQ_INIT(GEN6_PM, dev_priv->pm_irq_mask, pm_irqs);
        }
 }
 
@@ -2997,8 +3289,6 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
                                DE_PLANEA_FLIP_DONE_IVB | DE_AUX_CHANNEL_A_IVB);
                extra_mask = (DE_PIPEC_VBLANK_IVB | DE_PIPEB_VBLANK_IVB |
                              DE_PIPEA_VBLANK_IVB | DE_ERR_INT_IVB);
-
-               I915_WRITE(GEN7_ERR_INT, I915_READ(GEN7_ERR_INT));
        } else {
                display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT |
                                DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE |
@@ -3011,11 +3301,11 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
 
        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 | extra_mask);
-       POSTING_READ(DEIER);
+       I915_WRITE(HWSTAM, 0xeffe);
+
+       ibx_irq_pre_postinstall(dev);
+
+       GEN5_IRQ_INIT(DE, dev_priv->irq_mask, display_mask | extra_mask);
 
        gen5_gt_irq_postinstall(dev);
 
@@ -3175,21 +3465,16 @@ static void gen8_gt_irq_postinstall(struct drm_i915_private *dev_priv)
                GT_RENDER_USER_INTERRUPT << GEN8_VECS_IRQ_SHIFT
                };
 
-       for (i = 0; i < ARRAY_SIZE(gt_interrupts); i++) {
-               u32 tmp = I915_READ(GEN8_GT_IIR(i));
-               if (tmp)
-                       DRM_ERROR("Interrupt (%d) should have been masked in pre-install 0x%08x\n",
-                                 i, tmp);
-               I915_WRITE(GEN8_GT_IMR(i), ~gt_interrupts[i]);
-               I915_WRITE(GEN8_GT_IER(i), gt_interrupts[i]);
-       }
-       POSTING_READ(GEN8_GT_IER(0));
+       for (i = 0; i < ARRAY_SIZE(gt_interrupts); i++)
+               GEN8_IRQ_INIT_NDX(GT, i, ~gt_interrupts[i], gt_interrupts[i]);
+
+       dev_priv->pm_irq_mask = 0xffffffff;
 }
 
 static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv)
 {
        struct drm_device *dev = dev_priv->dev;
-       uint32_t de_pipe_masked = GEN8_PIPE_FLIP_DONE |
+       uint32_t de_pipe_masked = GEN8_PIPE_PRIMARY_FLIP_DONE |
                GEN8_PIPE_CDCLK_CRC_DONE |
                GEN8_DE_PIPE_IRQ_FAULT_ERRORS;
        uint32_t de_pipe_enables = de_pipe_masked | GEN8_PIPE_VBLANK |
@@ -3199,25 +3484,19 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv)
        dev_priv->de_irq_mask[PIPE_B] = ~de_pipe_masked;
        dev_priv->de_irq_mask[PIPE_C] = ~de_pipe_masked;
 
-       for_each_pipe(pipe) {
-               u32 tmp = I915_READ(GEN8_DE_PIPE_IIR(pipe));
-               if (tmp)
-                       DRM_ERROR("Interrupt (%d) should have been masked in pre-install 0x%08x\n",
-                                 pipe, tmp);
-               I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]);
-               I915_WRITE(GEN8_DE_PIPE_IER(pipe), de_pipe_enables);
-       }
-       POSTING_READ(GEN8_DE_PIPE_ISR(0));
+       for_each_pipe(pipe)
+               GEN8_IRQ_INIT_NDX(DE_PIPE, pipe, dev_priv->de_irq_mask[pipe],
+                                 de_pipe_enables);
 
-       I915_WRITE(GEN8_DE_PORT_IMR, ~GEN8_AUX_CHANNEL_A);
-       I915_WRITE(GEN8_DE_PORT_IER, GEN8_AUX_CHANNEL_A);
-       POSTING_READ(GEN8_DE_PORT_IER);
+       GEN5_IRQ_INIT(GEN8_DE_PORT_, ~GEN8_AUX_CHANNEL_A, GEN8_AUX_CHANNEL_A);
 }
 
 static int gen8_irq_postinstall(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
+       ibx_irq_pre_postinstall(dev);
+
        gen8_gt_irq_postinstall(dev_priv);
        gen8_de_irq_postinstall(dev_priv);
 
@@ -3229,44 +3508,55 @@ static int gen8_irq_postinstall(struct drm_device *dev)
        return 0;
 }
 
-static void gen8_irq_uninstall(struct drm_device *dev)
+static int cherryview_irq_postinstall(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 enable_mask = I915_DISPLAY_PORT_INTERRUPT |
+               I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
+               I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
+               I915_DISPLAY_PIPE_C_EVENT_INTERRUPT;
+       u32 pipestat_enable = PLANE_FLIP_DONE_INT_STATUS_VLV |
+               PIPE_CRC_DONE_INTERRUPT_STATUS;
+       unsigned long irqflags;
        int pipe;
 
-       if (!dev_priv)
-               return;
+       /*
+        * Leave vblank interrupts masked initially.  enable/disable will
+        * toggle them based on usage.
+        */
+       dev_priv->irq_mask = ~enable_mask;
 
-       I915_WRITE(GEN8_MASTER_IRQ, 0);
+       for_each_pipe(pipe)
+               I915_WRITE(PIPESTAT(pipe), 0xffff);
 
-#define GEN8_IRQ_FINI_NDX(type, which) do { \
-               I915_WRITE(GEN8_##type##_IMR(which), 0xffffffff); \
-               I915_WRITE(GEN8_##type##_IER(which), 0); \
-               I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
-       } while (0)
+       spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+       i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_INTERRUPT_STATUS);
+       for_each_pipe(pipe)
+               i915_enable_pipestat(dev_priv, pipe, pipestat_enable);
+       spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 
-#define GEN8_IRQ_FINI(type) do { \
-               I915_WRITE(GEN8_##type##_IMR, 0xffffffff); \
-               I915_WRITE(GEN8_##type##_IER, 0); \
-               I915_WRITE(GEN8_##type##_IIR, 0xffffffff); \
-       } while (0)
+       I915_WRITE(VLV_IIR, 0xffffffff);
+       I915_WRITE(VLV_IMR, dev_priv->irq_mask);
+       I915_WRITE(VLV_IER, enable_mask);
 
-       GEN8_IRQ_FINI_NDX(GT, 0);
-       GEN8_IRQ_FINI_NDX(GT, 1);
-       GEN8_IRQ_FINI_NDX(GT, 2);
-       GEN8_IRQ_FINI_NDX(GT, 3);
+       gen8_gt_irq_postinstall(dev_priv);
 
-       for_each_pipe(pipe) {
-               GEN8_IRQ_FINI_NDX(DE_PIPE, pipe);
-       }
+       I915_WRITE(GEN8_MASTER_IRQ, MASTER_INTERRUPT_ENABLE);
+       POSTING_READ(GEN8_MASTER_IRQ);
 
-       GEN8_IRQ_FINI(DE_PORT);
-       GEN8_IRQ_FINI(DE_MISC);
-       GEN8_IRQ_FINI(PCU);
-#undef GEN8_IRQ_FINI
-#undef GEN8_IRQ_FINI_NDX
+       return 0;
+}
 
-       POSTING_READ(GEN8_PCU_IIR);
+static void gen8_irq_uninstall(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (!dev_priv)
+               return;
+
+       intel_hpd_irq_uninstall(dev_priv);
+
+       gen8_irq_reset(dev);
 }
 
 static void valleyview_irq_uninstall(struct drm_device *dev)
@@ -3278,6 +3568,8 @@ static void valleyview_irq_uninstall(struct drm_device *dev)
        if (!dev_priv)
                return;
 
+       I915_WRITE(VLV_MASTER_IER, 0);
+
        intel_hpd_irq_uninstall(dev_priv);
 
        for_each_pipe(pipe)
@@ -3300,35 +3592,67 @@ static void valleyview_irq_uninstall(struct drm_device *dev)
        POSTING_READ(VLV_IER);
 }
 
-static void ironlake_irq_uninstall(struct drm_device *dev)
+static void cherryview_irq_uninstall(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       int pipe;
 
        if (!dev_priv)
                return;
 
-       intel_hpd_irq_uninstall(dev_priv);
+       I915_WRITE(GEN8_MASTER_IRQ, 0);
+       POSTING_READ(GEN8_MASTER_IRQ);
 
-       I915_WRITE(HWSTAM, 0xffffffff);
+#define GEN8_IRQ_FINI_NDX(type, which)                         \
+do {                                                           \
+       I915_WRITE(GEN8_##type##_IMR(which), 0xffffffff);       \
+       I915_WRITE(GEN8_##type##_IER(which), 0);                \
+       I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff);       \
+       POSTING_READ(GEN8_##type##_IIR(which));                 \
+       I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff);       \
+} while (0)
+
+#define GEN8_IRQ_FINI(type)                            \
+do {                                                   \
+       I915_WRITE(GEN8_##type##_IMR, 0xffffffff);      \
+       I915_WRITE(GEN8_##type##_IER, 0);               \
+       I915_WRITE(GEN8_##type##_IIR, 0xffffffff);      \
+       POSTING_READ(GEN8_##type##_IIR);                \
+       I915_WRITE(GEN8_##type##_IIR, 0xffffffff);      \
+} while (0)
 
-       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));
+       GEN8_IRQ_FINI_NDX(GT, 0);
+       GEN8_IRQ_FINI_NDX(GT, 1);
+       GEN8_IRQ_FINI_NDX(GT, 2);
+       GEN8_IRQ_FINI_NDX(GT, 3);
 
-       I915_WRITE(GTIMR, 0xffffffff);
-       I915_WRITE(GTIER, 0x0);
-       I915_WRITE(GTIIR, I915_READ(GTIIR));
+       GEN8_IRQ_FINI(PCU);
 
-       if (HAS_PCH_NOP(dev))
+#undef GEN8_IRQ_FINI
+#undef GEN8_IRQ_FINI_NDX
+
+       I915_WRITE(PORT_HOTPLUG_EN, 0);
+       I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
+
+       for_each_pipe(pipe)
+               I915_WRITE(PIPESTAT(pipe), 0xffff);
+
+       I915_WRITE(VLV_IMR, 0xffffffff);
+       I915_WRITE(VLV_IER, 0x0);
+       I915_WRITE(VLV_IIR, 0xffffffff);
+       POSTING_READ(VLV_IIR);
+}
+
+static void ironlake_irq_uninstall(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (!dev_priv)
                return;
 
-       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));
+       intel_hpd_irq_uninstall(dev_priv);
+
+       ironlake_irq_reset(dev);
 }
 
 static void i8xx_irq_preinstall(struct drm_device * dev)
@@ -3386,7 +3710,7 @@ static bool i8xx_handle_vblank(struct drm_device *dev,
        struct drm_i915_private *dev_priv = dev->dev_private;
        u16 flip_pending = DISPLAY_PLANE_FLIP_PENDING(plane);
 
-       if (!drm_handle_vblank(dev, pipe))
+       if (!intel_pipe_handle_vblank(dev, pipe))
                return false;
 
        if ((iir & flip_pending) == 0)
@@ -3410,7 +3734,7 @@ static bool i8xx_handle_vblank(struct drm_device *dev,
 
 static irqreturn_t i8xx_irq_handler(int irq, void *arg)
 {
-       struct drm_device *dev = (struct drm_device *) arg;
+       struct drm_device *dev = arg;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u16 iir, new_iir;
        u32 pipe_stats[2];
@@ -3571,7 +3895,7 @@ static bool i915_handle_vblank(struct drm_device *dev,
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 flip_pending = DISPLAY_PLANE_FLIP_PENDING(plane);
 
-       if (!drm_handle_vblank(dev, pipe))
+       if (!intel_pipe_handle_vblank(dev, pipe))
                return false;
 
        if ((iir & flip_pending) == 0)
@@ -3595,7 +3919,7 @@ static bool i915_handle_vblank(struct drm_device *dev,
 
 static irqreturn_t i915_irq_handler(int irq, void *arg)
 {
-       struct drm_device *dev = (struct drm_device *) arg;
+       struct drm_device *dev = arg;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 iir, new_iir, pipe_stats[I915_MAX_PIPES];
        unsigned long irqflags;
@@ -3636,16 +3960,9 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
                        break;
 
                /* Consume port.  Then clear IIR or we'll miss events */
-               if ((I915_HAS_HOTPLUG(dev)) &&
-                   (iir & I915_DISPLAY_PORT_INTERRUPT)) {
-                       u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
-                       u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_I915;
-
-                       intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915);
-
-                       I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
-                       POSTING_READ(PORT_HOTPLUG_STAT);
-               }
+               if (I915_HAS_HOTPLUG(dev) &&
+                   iir & I915_DISPLAY_PORT_INTERRUPT)
+                       i9xx_hpd_irq_handler(dev);
 
                I915_WRITE(IIR, iir & ~flip_mask);
                new_iir = I915_READ(IIR); /* Flush posted writes */
@@ -3832,7 +4149,7 @@ static void i915_hpd_irq_setup(struct drm_device *dev)
 
 static irqreturn_t i965_irq_handler(int irq, void *arg)
 {
-       struct drm_device *dev = (struct drm_device *) arg;
+       struct drm_device *dev = arg;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 iir, new_iir;
        u32 pipe_stats[I915_MAX_PIPES];
@@ -3879,22 +4196,8 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
                ret = IRQ_HANDLED;
 
                /* Consume port.  Then clear IIR or we'll miss events */
-               if (iir & I915_DISPLAY_PORT_INTERRUPT) {
-                       u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
-                       u32 hotplug_trigger = hotplug_status & (IS_G4X(dev) ?
-                                                                 HOTPLUG_INT_STATUS_G4X :
-                                                                 HOTPLUG_INT_STATUS_I915);
-
-                       intel_hpd_irq_handler(dev, hotplug_trigger,
-                                             IS_G4X(dev) ? hpd_status_g4x : hpd_status_i915);
-
-                       if (IS_G4X(dev) &&
-                           (hotplug_status & DP_AUX_CHANNEL_MASK_INT_STATUS_G4X))
-                               dp_aux_irq_handler(dev);
-
-                       I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
-                       I915_READ(PORT_HOTPLUG_STAT);
-               }
+               if (iir & I915_DISPLAY_PORT_INTERRUPT)
+                       i9xx_hpd_irq_handler(dev);
 
                I915_WRITE(IIR, iir & ~flip_mask);
                new_iir = I915_READ(IIR); /* Flush posted writes */
@@ -3997,7 +4300,7 @@ static void intel_hpd_irq_reenable(unsigned long data)
                        if (intel_connector->encoder->hpd_pin == i) {
                                if (connector->polled != intel_connector->polled)
                                        DRM_DEBUG_DRIVER("Reenabling HPD on connector %s\n",
-                                                        drm_get_connector_name(connector));
+                                                        connector->name);
                                connector->polled = intel_connector->polled;
                                if (!connector->polled)
                                        connector->polled = DRM_CONNECTOR_POLL_HPD;
@@ -4045,7 +4348,15 @@ void intel_irq_init(struct drm_device *dev)
                dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;
        }
 
-       if (IS_VALLEYVIEW(dev)) {
+       if (IS_CHERRYVIEW(dev)) {
+               dev->driver->irq_handler = cherryview_irq_handler;
+               dev->driver->irq_preinstall = cherryview_irq_preinstall;
+               dev->driver->irq_postinstall = cherryview_irq_postinstall;
+               dev->driver->irq_uninstall = cherryview_irq_uninstall;
+               dev->driver->enable_vblank = valleyview_enable_vblank;
+               dev->driver->disable_vblank = valleyview_disable_vblank;
+               dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup;
+       } else if (IS_VALLEYVIEW(dev)) {
                dev->driver->irq_handler = valleyview_irq_handler;
                dev->driver->irq_preinstall = valleyview_irq_preinstall;
                dev->driver->irq_postinstall = valleyview_irq_postinstall;
@@ -4055,7 +4366,7 @@ void intel_irq_init(struct drm_device *dev)
                dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup;
        } else if (IS_GEN8(dev)) {
                dev->driver->irq_handler = gen8_irq_handler;
-               dev->driver->irq_preinstall = gen8_irq_preinstall;
+               dev->driver->irq_preinstall = gen8_irq_reset;
                dev->driver->irq_postinstall = gen8_irq_postinstall;
                dev->driver->irq_uninstall = gen8_irq_uninstall;
                dev->driver->enable_vblank = gen8_enable_vblank;
@@ -4063,7 +4374,7 @@ void intel_irq_init(struct drm_device *dev)
                dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup;
        } else if (HAS_PCH_SPLIT(dev)) {
                dev->driver->irq_handler = ironlake_irq_handler;
-               dev->driver->irq_preinstall = ironlake_irq_preinstall;
+               dev->driver->irq_preinstall = ironlake_irq_reset;
                dev->driver->irq_postinstall = ironlake_irq_postinstall;
                dev->driver->irq_uninstall = ironlake_irq_uninstall;
                dev->driver->enable_vblank = ironlake_enable_vblank;
@@ -4121,57 +4432,20 @@ void intel_hpd_init(struct drm_device *dev)
 }
 
 /* Disable interrupts so we can allow runtime PM. */
-void hsw_runtime_pm_disable_interrupts(struct drm_device *dev)
+void intel_runtime_pm_disable_interrupts(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       unsigned long irqflags;
-
-       spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
-
-       dev_priv->pm.regsave.deimr = I915_READ(DEIMR);
-       dev_priv->pm.regsave.sdeimr = I915_READ(SDEIMR);
-       dev_priv->pm.regsave.gtimr = I915_READ(GTIMR);
-       dev_priv->pm.regsave.gtier = I915_READ(GTIER);
-       dev_priv->pm.regsave.gen6_pmimr = I915_READ(GEN6_PMIMR);
-
-       ironlake_disable_display_irq(dev_priv, 0xffffffff);
-       ibx_disable_display_interrupt(dev_priv, 0xffffffff);
-       ilk_disable_gt_irq(dev_priv, 0xffffffff);
-       snb_disable_pm_irq(dev_priv, 0xffffffff);
 
+       dev->driver->irq_uninstall(dev);
        dev_priv->pm.irqs_disabled = true;
-
-       spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 }
 
 /* Restore interrupts so we can recover from runtime PM. */
-void hsw_runtime_pm_restore_interrupts(struct drm_device *dev)
+void intel_runtime_pm_restore_interrupts(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       unsigned long irqflags;
-       uint32_t val;
-
-       spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
-
-       val = I915_READ(DEIMR);
-       WARN(val != 0xffffffff, "DEIMR is 0x%08x\n", val);
-
-       val = I915_READ(SDEIMR);
-       WARN(val != 0xffffffff, "SDEIMR is 0x%08x\n", val);
-
-       val = I915_READ(GTIMR);
-       WARN(val != 0xffffffff, "GTIMR is 0x%08x\n", val);
-
-       val = I915_READ(GEN6_PMIMR);
-       WARN(val != 0xffffffff, "GEN6_PMIMR is 0x%08x\n", val);
 
        dev_priv->pm.irqs_disabled = false;
-
-       ironlake_enable_display_irq(dev_priv, ~dev_priv->pm.regsave.deimr);
-       ibx_enable_display_interrupt(dev_priv, ~dev_priv->pm.regsave.sdeimr);
-       ilk_enable_gt_irq(dev_priv, ~dev_priv->pm.regsave.gtimr);
-       snb_enable_pm_irq(dev_priv, ~dev_priv->pm.regsave.gen6_pmimr);
-       I915_WRITE(GTIER, dev_priv->pm.regsave.gtier);
-
-       spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+       dev->driver->irq_preinstall(dev);
+       dev->driver->irq_postinstall(dev);
 }
index d1d7980f0e010cc3d48ee86ce4699b3068ce47be..d05a2afa17dc605acc294e0e15ae88ac200b5093 100644 (file)
@@ -46,7 +46,8 @@ struct i915_params i915 __read_mostly = {
        .reset = true,
        .invert_brightness = 0,
        .disable_display = 0,
-       .enable_cmd_parser = 0,
+       .enable_cmd_parser = 1,
+       .disable_vtd_wa = 0,
 };
 
 module_param_named(modeset, i915.modeset, int, 0400);
@@ -149,6 +150,9 @@ MODULE_PARM_DESC(invert_brightness,
 module_param_named(disable_display, i915.disable_display, bool, 0600);
 MODULE_PARM_DESC(disable_display, "Disable display (default: false)");
 
+module_param_named(disable_vtd_wa, i915.disable_vtd_wa, bool, 0600);
+MODULE_PARM_DESC(disable_vtd_wa, "Disable all VT-d workarounds (default: false)");
+
 module_param_named(enable_cmd_parser, i915.enable_cmd_parser, int, 0600);
 MODULE_PARM_DESC(enable_cmd_parser,
-                "Enable command parsing (1=enabled, 0=disabled [default])");
+                "Enable command parsing (1=enabled [default], 0=disabled)");
index c77af69c2d8f5f8c97f5dc2045fd99a07960617b..e691b30b28179ab9d026daeaa4c6a5e176062327 100644 (file)
@@ -29,6 +29,8 @@
 #define _TRANSCODER(tran, a, b) ((a) + (tran)*((b)-(a)))
 
 #define _PORT(port, a, b) ((a) + (port)*((b)-(a)))
+#define _PIPE3(pipe, a, b, c) (pipe < 2 ? _PIPE(pipe, a, b) : c)
+#define _PORT3(port, a, b, c) (port < 2 ? _PORT(port, a, b) : c)
 
 #define _MASKED_BIT_ENABLE(a) (((a) << 16) | (a))
 #define _MASKED_BIT_DISABLE(a) ((a) << 16)
 
 /* Graphics reset regs */
 #define I965_GDRST 0xc0 /* PCI config register */
-#define ILK_GDSR 0x2ca4 /* MCHBAR offset */
 #define  GRDOM_FULL    (0<<2)
 #define  GRDOM_RENDER  (1<<2)
 #define  GRDOM_MEDIA   (3<<2)
 #define  GRDOM_MASK    (3<<2)
 #define  GRDOM_RESET_ENABLE (1<<0)
 
+#define ILK_GDSR 0x2ca4 /* MCHBAR offset */
+#define  ILK_GRDOM_FULL                (0<<1)
+#define  ILK_GRDOM_RENDER      (1<<1)
+#define  ILK_GRDOM_MEDIA       (3<<1)
+#define  ILK_GRDOM_MASK                (3<<1)
+#define  ILK_GRDOM_RESET_ENABLE (1<<0)
+
 #define GEN6_MBCUNIT_SNPCR     0x900c /* for LLC config */
 #define   GEN6_MBC_SNPCR_SHIFT 21
 #define   GEN6_MBC_SNPCR_MASK  (3<<21)
 #define   GEN6_MBC_SNPCR_LOW   (2<<21)
 #define   GEN6_MBC_SNPCR_MIN   (3<<21) /* only 1/16th of the cache is shared */
 
+#define VLV_G3DCTL             0x9024
+#define VLV_GSCKGCTL           0x9028
+
 #define GEN6_MBCTL             0x0907c
 #define   GEN6_MBCTL_ENABLE_BOOT_FETCH (1 << 4)
 #define   GEN6_MBCTL_CTX_FETCH_NEEDED  (1 << 3)
  * Memory interface instructions used by the kernel
  */
 #define MI_INSTR(opcode, flags) (((opcode) << 23) | (flags))
+/* Many MI commands use bit 22 of the header dword for GGTT vs PPGTT */
+#define  MI_GLOBAL_GTT    (1<<22)
 
 #define MI_NOOP                        MI_INSTR(0, 0)
 #define MI_USER_INTERRUPT      MI_INSTR(0x02, 0)
 #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)
+#define   MI_SEMAPHORE_SYNC_INVALID (3<<16)
+#define   MI_SEMAPHORE_SYNC_MASK    (3<<16)
 #define MI_SET_CONTEXT         MI_INSTR(0x18, 0)
 #define   MI_MM_SPACE_GTT              (1<<8)
 #define   MI_MM_SPACE_PHYSICAL         (0<<8)
  * - One can actually load arbitrary many arbitrary registers: Simply issue x
  *   address/value pairs. Don't overdue it, though, x <= 2^4 must hold!
  */
-#define MI_LOAD_REGISTER_IMM(x)        MI_INSTR(0x22, 2*x-1)
-#define MI_STORE_REGISTER_MEM(x) MI_INSTR(0x24, 2*x-1)
+#define MI_LOAD_REGISTER_IMM(x)        MI_INSTR(0x22, 2*(x)-1)
+#define MI_STORE_REGISTER_MEM(x) MI_INSTR(0x24, 2*(x)-1)
+#define MI_STORE_REGISTER_MEM_GEN8(x) MI_INSTR(0x24, 3*(x)-1)
 #define   MI_SRM_LRM_GLOBAL_GTT                (1<<22)
 #define MI_FLUSH_DW            MI_INSTR(0x26, 1) /* for GEN6 */
 #define   MI_FLUSH_DW_STORE_INDEX      (1<<21)
 #define   MI_INVALIDATE_TLB            (1<<18)
 #define   MI_FLUSH_DW_OP_STOREDW       (1<<14)
+#define   MI_FLUSH_DW_OP_MASK          (3<<14)
+#define   MI_FLUSH_DW_NOTIFY           (1<<8)
 #define   MI_INVALIDATE_BSD            (1<<7)
 #define   MI_FLUSH_DW_USE_GTT          (1<<2)
 #define   MI_FLUSH_DW_USE_PPGTT                (0<<2)
 #define   DISPLAY_PLANE_B           (1<<20)
 #define GFX_OP_PIPE_CONTROL(len)       ((0x3<<29)|(0x3<<27)|(0x2<<24)|(len-2))
 #define   PIPE_CONTROL_GLOBAL_GTT_IVB                  (1<<24) /* gen7+ */
+#define   PIPE_CONTROL_MMIO_WRITE                      (1<<23)
+#define   PIPE_CONTROL_STORE_DATA_INDEX                        (1<<21)
 #define   PIPE_CONTROL_CS_STALL                                (1<<20)
 #define   PIPE_CONTROL_TLB_INVALIDATE                  (1<<18)
 #define   PIPE_CONTROL_QW_WRITE                                (1<<14)
+#define   PIPE_CONTROL_POST_SYNC_OP_MASK                (3<<14)
 #define   PIPE_CONTROL_DEPTH_STALL                     (1<<13)
 #define   PIPE_CONTROL_WRITE_FLUSH                     (1<<12)
 #define   PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH       (1<<12) /* gen6+ */
 #define   PIPE_CONTROL_DEPTH_CACHE_FLUSH               (1<<0)
 #define   PIPE_CONTROL_GLOBAL_GTT (1<<2) /* in addr dword */
 
+/*
+ * Commands used only by the command parser
+ */
+#define MI_SET_PREDICATE        MI_INSTR(0x01, 0)
+#define MI_ARB_CHECK            MI_INSTR(0x05, 0)
+#define MI_RS_CONTROL           MI_INSTR(0x06, 0)
+#define MI_URB_ATOMIC_ALLOC     MI_INSTR(0x09, 0)
+#define MI_PREDICATE            MI_INSTR(0x0C, 0)
+#define MI_RS_CONTEXT           MI_INSTR(0x0F, 0)
+#define MI_TOPOLOGY_FILTER      MI_INSTR(0x0D, 0)
+#define MI_LOAD_SCAN_LINES_EXCL MI_INSTR(0x13, 0)
+#define MI_URB_CLEAR            MI_INSTR(0x19, 0)
+#define MI_UPDATE_GTT           MI_INSTR(0x23, 0)
+#define MI_CLFLUSH              MI_INSTR(0x27, 0)
+#define MI_REPORT_PERF_COUNT    MI_INSTR(0x28, 0)
+#define   MI_REPORT_PERF_COUNT_GGTT (1<<0)
+#define MI_LOAD_REGISTER_MEM    MI_INSTR(0x29, 0)
+#define MI_LOAD_REGISTER_REG    MI_INSTR(0x2A, 0)
+#define MI_RS_STORE_DATA_IMM    MI_INSTR(0x2B, 0)
+#define MI_LOAD_URB_MEM         MI_INSTR(0x2C, 0)
+#define MI_STORE_URB_MEM        MI_INSTR(0x2D, 0)
+#define MI_CONDITIONAL_BATCH_BUFFER_END MI_INSTR(0x36, 0)
+
+#define PIPELINE_SELECT                ((0x3<<29)|(0x1<<27)|(0x1<<24)|(0x4<<16))
+#define GFX_OP_3DSTATE_VF_STATISTICS   ((0x3<<29)|(0x1<<27)|(0x0<<24)|(0xB<<16))
+#define MEDIA_VFE_STATE                ((0x3<<29)|(0x2<<27)|(0x0<<24)|(0x0<<16))
+#define  MEDIA_VFE_STATE_MMIO_ACCESS_MASK (0x18)
+#define GPGPU_OBJECT                   ((0x3<<29)|(0x2<<27)|(0x1<<24)|(0x4<<16))
+#define GPGPU_WALKER                   ((0x3<<29)|(0x2<<27)|(0x1<<24)|(0x5<<16))
+#define GFX_OP_3DSTATE_DX9_CONSTANTF_VS \
+       ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x39<<16))
+#define GFX_OP_3DSTATE_DX9_CONSTANTF_PS \
+       ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x3A<<16))
+#define GFX_OP_3DSTATE_SO_DECL_LIST \
+       ((0x3<<29)|(0x3<<27)|(0x1<<24)|(0x17<<16))
+
+#define GFX_OP_3DSTATE_BINDING_TABLE_EDIT_VS \
+       ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x43<<16))
+#define GFX_OP_3DSTATE_BINDING_TABLE_EDIT_GS \
+       ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x44<<16))
+#define GFX_OP_3DSTATE_BINDING_TABLE_EDIT_HS \
+       ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x45<<16))
+#define GFX_OP_3DSTATE_BINDING_TABLE_EDIT_DS \
+       ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x46<<16))
+#define GFX_OP_3DSTATE_BINDING_TABLE_EDIT_PS \
+       ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x47<<16))
+
+#define MFX_WAIT  ((0x3<<29)|(0x1<<27)|(0x0<<16))
+
+#define COLOR_BLT     ((0x2<<29)|(0x40<<22))
+#define SRC_COPY_BLT  ((0x2<<29)|(0x43<<22))
+
+/*
+ * Registers used only by the command parser
+ */
+#define BCS_SWCTRL 0x22200
+
+#define HS_INVOCATION_COUNT 0x2300
+#define DS_INVOCATION_COUNT 0x2308
+#define IA_VERTICES_COUNT   0x2310
+#define IA_PRIMITIVES_COUNT 0x2318
+#define VS_INVOCATION_COUNT 0x2320
+#define GS_INVOCATION_COUNT 0x2328
+#define GS_PRIMITIVES_COUNT 0x2330
+#define CL_INVOCATION_COUNT 0x2338
+#define CL_PRIMITIVES_COUNT 0x2340
+#define PS_INVOCATION_COUNT 0x2348
+#define PS_DEPTH_COUNT      0x2350
+
+/* There are the 4 64-bit counter registers, one for each stream output */
+#define GEN7_SO_NUM_PRIMS_WRITTEN(n) (0x5200 + (n) * 8)
+
+#define GEN7_SO_PRIM_STORAGE_NEEDED(n)  (0x5240 + (n) * 8)
+
+#define GEN7_3DPRIM_END_OFFSET          0x2420
+#define GEN7_3DPRIM_START_VERTEX        0x2430
+#define GEN7_3DPRIM_VERTEX_COUNT        0x2434
+#define GEN7_3DPRIM_INSTANCE_COUNT      0x2438
+#define GEN7_3DPRIM_START_INSTANCE      0x243C
+#define GEN7_3DPRIM_BASE_VERTEX         0x2440
+
+#define OACONTROL 0x2360
+
+#define _GEN7_PIPEA_DE_LOAD_SL 0x70068
+#define _GEN7_PIPEB_DE_LOAD_SL 0x71068
+#define GEN7_PIPE_DE_LOAD_SL(pipe) _PIPE(pipe, \
+                                        _GEN7_PIPEA_DE_LOAD_SL, \
+                                        _GEN7_PIPEB_DE_LOAD_SL)
 
 /*
  * Reset registers
 #define   IOSF_PORT_PUNIT                      0x4
 #define   IOSF_PORT_NC                         0x11
 #define   IOSF_PORT_DPIO                       0x12
+#define   IOSF_PORT_DPIO_2                     0x1a
 #define   IOSF_PORT_GPIO_NC                    0x13
 #define   IOSF_PORT_CCK                                0x14
 #define   IOSF_PORT_CCU                                0xA9
 /* See configdb bunit SB addr map */
 #define BUNIT_REG_BISOC                                0x11
 
-#define PUNIT_OPCODE_REG_READ                  6
-#define PUNIT_OPCODE_REG_WRITE                 7
-
 #define PUNIT_REG_DSPFREQ                      0x36
 #define   DSPFREQSTAT_SHIFT                    30
 #define   DSPFREQSTAT_MASK                     (0x3 << DSPFREQSTAT_SHIFT)
@@ -469,16 +575,91 @@ enum punit_power_well {
 #define  DSI_PLL_M1_DIV_MASK                   (0x1ff << 0)
 #define CCK_DISPLAY_CLOCK_CONTROL              0x6b
 
-/*
- * DPIO - a special bus for various display related registers to hide behind
+/**
+ * DOC: DPIO
+ *
+ * VLV and CHV have slightly peculiar display PHYs for driving DP/HDMI
+ * ports. DPIO is the name given to such a display PHY. These PHYs
+ * don't follow the standard programming model using direct MMIO
+ * registers, and instead their registers must be accessed trough IOSF
+ * sideband. VLV has one such PHY for driving ports B and C, and CHV
+ * adds another PHY for driving port D. Each PHY responds to specific
+ * IOSF-SB port.
+ *
+ * Each display PHY is made up of one or two channels. Each channel
+ * houses a common lane part which contains the PLL and other common
+ * logic. CH0 common lane also contains the IOSF-SB logic for the
+ * Common Register Interface (CRI) ie. the DPIO registers. CRI clock
+ * must be running when any DPIO registers are accessed.
+ *
+ * In addition to having their own registers, the PHYs are also
+ * controlled through some dedicated signals from the display
+ * controller. These include PLL reference clock enable, PLL enable,
+ * and CRI clock selection, for example.
  *
- * DPIO is VLV only.
+ * Eeach channel also has two splines (also called data lanes), and
+ * each spline is made up of one Physical Access Coding Sub-Layer
+ * (PCS) block and two TX lanes. So each channel has two PCS blocks
+ * and four TX lanes. The TX lanes are used as DP lanes or TMDS
+ * data/clock pairs depending on the output type.
+ *
+ * Additionally the PHY also contains an AUX lane with AUX blocks
+ * for each channel. This is used for DP AUX communication, but
+ * this fact isn't really relevant for the driver since AUX is
+ * controlled from the display controller side. No DPIO registers
+ * need to be accessed during AUX communication,
+ *
+ * Generally the common lane corresponds to the pipe and
+ * the spline (PCS/TX) correponds to the port.
+ *
+ * For dual channel PHY (VLV/CHV):
+ *
+ *  pipe A == CMN/PLL/REF CH0
+ *
+ *  pipe B == CMN/PLL/REF CH1
+ *
+ *  port B == PCS/TX CH0
+ *
+ *  port C == PCS/TX CH1
+ *
+ * This is especially important when we cross the streams
+ * ie. drive port B with pipe B, or port C with pipe A.
+ *
+ * For single channel PHY (CHV):
+ *
+ *  pipe C == CMN/PLL/REF CH0
+ *
+ *  port D == PCS/TX CH0
+ *
+ * Note: digital port B is DDI0, digital port C is DDI1,
+ * digital port D is DDI2
+ */
+/*
+ * Dual channel PHY (VLV/CHV)
+ * ---------------------------------
+ * |      CH0      |      CH1      |
+ * |  CMN/PLL/REF  |  CMN/PLL/REF  |
+ * |---------------|---------------| Display PHY
+ * | PCS01 | PCS23 | PCS01 | PCS23 |
+ * |-------|-------|-------|-------|
+ * |TX0|TX1|TX2|TX3|TX0|TX1|TX2|TX3|
+ * ---------------------------------
+ * |     DDI0      |     DDI1      | DP/HDMI ports
+ * ---------------------------------
  *
- * Note: digital port B is DDI0, digital pot C is DDI1
+ * Single channel PHY (CHV)
+ * -----------------
+ * |      CH0      |
+ * |  CMN/PLL/REF  |
+ * |---------------| Display PHY
+ * | PCS01 | PCS23 |
+ * |-------|-------|
+ * |TX0|TX1|TX2|TX3|
+ * -----------------
+ * |     DDI2      | DP/HDMI port
+ * -----------------
  */
 #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 */
@@ -555,14 +736,29 @@ enum punit_power_well {
 #define   DPIO_PCS_TX_LANE1_RESET      (1<<7)
 #define VLV_PCS_DW0(ch) _PORT(ch, _VLV_PCS_DW0_CH0, _VLV_PCS_DW0_CH1)
 
+#define _VLV_PCS01_DW0_CH0             0x200
+#define _VLV_PCS23_DW0_CH0             0x400
+#define _VLV_PCS01_DW0_CH1             0x2600
+#define _VLV_PCS23_DW0_CH1             0x2800
+#define VLV_PCS01_DW0(ch) _PORT(ch, _VLV_PCS01_DW0_CH0, _VLV_PCS01_DW0_CH1)
+#define VLV_PCS23_DW0(ch) _PORT(ch, _VLV_PCS23_DW0_CH0, _VLV_PCS23_DW0_CH1)
+
 #define _VLV_PCS_DW1_CH0               0x8204
 #define _VLV_PCS_DW1_CH1               0x8404
+#define   CHV_PCS_REQ_SOFTRESET_EN     (1<<23)
 #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 VLV_PCS_DW1(ch) _PORT(ch, _VLV_PCS_DW1_CH0, _VLV_PCS_DW1_CH1)
 
+#define _VLV_PCS01_DW1_CH0             0x204
+#define _VLV_PCS23_DW1_CH0             0x404
+#define _VLV_PCS01_DW1_CH1             0x2604
+#define _VLV_PCS23_DW1_CH1             0x2804
+#define VLV_PCS01_DW1(ch) _PORT(ch, _VLV_PCS01_DW1_CH0, _VLV_PCS01_DW1_CH1)
+#define VLV_PCS23_DW1(ch) _PORT(ch, _VLV_PCS23_DW1_CH0, _VLV_PCS23_DW1_CH1)
+
 #define _VLV_PCS_DW8_CH0               0x8220
 #define _VLV_PCS_DW8_CH1               0x8420
 #define VLV_PCS_DW8(ch) _PORT(ch, _VLV_PCS_DW8_CH0, _VLV_PCS_DW8_CH1)
@@ -578,6 +774,19 @@ enum punit_power_well {
 #define _VLV_PCS_DW9_CH1               0x8424
 #define        VLV_PCS_DW9(ch) _PORT(ch, _VLV_PCS_DW9_CH0, _VLV_PCS_DW9_CH1)
 
+#define _CHV_PCS_DW10_CH0              0x8228
+#define _CHV_PCS_DW10_CH1              0x8428
+#define   DPIO_PCS_SWING_CALC_TX0_TX2  (1<<30)
+#define   DPIO_PCS_SWING_CALC_TX1_TX3  (1<<31)
+#define CHV_PCS_DW10(ch) _PORT(ch, _CHV_PCS_DW10_CH0, _CHV_PCS_DW10_CH1)
+
+#define _VLV_PCS01_DW10_CH0            0x0228
+#define _VLV_PCS23_DW10_CH0            0x0428
+#define _VLV_PCS01_DW10_CH1            0x2628
+#define _VLV_PCS23_DW10_CH1            0x2828
+#define VLV_PCS01_DW10(port) _PORT(port, _VLV_PCS01_DW10_CH0, _VLV_PCS01_DW10_CH1)
+#define VLV_PCS23_DW10(port) _PORT(port, _VLV_PCS23_DW10_CH0, _VLV_PCS23_DW10_CH1)
+
 #define _VLV_PCS_DW11_CH0              0x822c
 #define _VLV_PCS_DW11_CH1              0x842c
 #define VLV_PCS_DW11(ch) _PORT(ch, _VLV_PCS_DW11_CH0, _VLV_PCS_DW11_CH1)
@@ -596,14 +805,21 @@ enum punit_power_well {
 
 #define _VLV_TX_DW2_CH0                        0x8288
 #define _VLV_TX_DW2_CH1                        0x8488
+#define   DPIO_SWING_MARGIN_SHIFT      16
+#define   DPIO_SWING_MARGIN_MASK       (0xff << DPIO_SWING_MARGIN_SHIFT)
+#define   DPIO_UNIQ_TRANS_SCALE_SHIFT  8
 #define VLV_TX_DW2(ch) _PORT(ch, _VLV_TX_DW2_CH0, _VLV_TX_DW2_CH1)
 
 #define _VLV_TX_DW3_CH0                        0x828c
 #define _VLV_TX_DW3_CH1                        0x848c
+/* The following bit for CHV phy */
+#define   DPIO_TX_UNIQ_TRANS_SCALE_EN  (1<<27)
 #define VLV_TX_DW3(ch) _PORT(ch, _VLV_TX_DW3_CH0, _VLV_TX_DW3_CH1)
 
 #define _VLV_TX_DW4_CH0                        0x8290
 #define _VLV_TX_DW4_CH1                        0x8490
+#define   DPIO_SWING_DEEMPH9P5_SHIFT   24
+#define   DPIO_SWING_DEEMPH9P5_MASK    (0xff << DPIO_SWING_DEEMPH9P5_SHIFT)
 #define VLV_TX_DW4(ch) _PORT(ch, _VLV_TX_DW4_CH0, _VLV_TX_DW4_CH1)
 
 #define _VLV_TX3_DW4_CH0               0x690
@@ -623,6 +839,73 @@ enum punit_power_well {
 #define _VLV_TX_DW14_CH1               0x84b8
 #define VLV_TX_DW14(ch) _PORT(ch, _VLV_TX_DW14_CH0, _VLV_TX_DW14_CH1)
 
+/* CHV dpPhy registers */
+#define _CHV_PLL_DW0_CH0               0x8000
+#define _CHV_PLL_DW0_CH1               0x8180
+#define CHV_PLL_DW0(ch) _PIPE(ch, _CHV_PLL_DW0_CH0, _CHV_PLL_DW0_CH1)
+
+#define _CHV_PLL_DW1_CH0               0x8004
+#define _CHV_PLL_DW1_CH1               0x8184
+#define   DPIO_CHV_N_DIV_SHIFT         8
+#define   DPIO_CHV_M1_DIV_BY_2         (0 << 0)
+#define CHV_PLL_DW1(ch) _PIPE(ch, _CHV_PLL_DW1_CH0, _CHV_PLL_DW1_CH1)
+
+#define _CHV_PLL_DW2_CH0               0x8008
+#define _CHV_PLL_DW2_CH1               0x8188
+#define CHV_PLL_DW2(ch) _PIPE(ch, _CHV_PLL_DW2_CH0, _CHV_PLL_DW2_CH1)
+
+#define _CHV_PLL_DW3_CH0               0x800c
+#define _CHV_PLL_DW3_CH1               0x818c
+#define  DPIO_CHV_FRAC_DIV_EN          (1 << 16)
+#define  DPIO_CHV_FIRST_MOD            (0 << 8)
+#define  DPIO_CHV_SECOND_MOD           (1 << 8)
+#define  DPIO_CHV_FEEDFWD_GAIN_SHIFT   0
+#define CHV_PLL_DW3(ch) _PIPE(ch, _CHV_PLL_DW3_CH0, _CHV_PLL_DW3_CH1)
+
+#define _CHV_PLL_DW6_CH0               0x8018
+#define _CHV_PLL_DW6_CH1               0x8198
+#define   DPIO_CHV_GAIN_CTRL_SHIFT     16
+#define          DPIO_CHV_INT_COEFF_SHIFT      8
+#define   DPIO_CHV_PROP_COEFF_SHIFT    0
+#define CHV_PLL_DW6(ch) _PIPE(ch, _CHV_PLL_DW6_CH0, _CHV_PLL_DW6_CH1)
+
+#define _CHV_CMN_DW13_CH0              0x8134
+#define _CHV_CMN_DW0_CH1               0x8080
+#define   DPIO_CHV_S1_DIV_SHIFT                21
+#define   DPIO_CHV_P1_DIV_SHIFT                13 /* 3 bits */
+#define   DPIO_CHV_P2_DIV_SHIFT                8  /* 5 bits */
+#define   DPIO_CHV_K_DIV_SHIFT         4
+#define   DPIO_PLL_FREQLOCK            (1 << 1)
+#define   DPIO_PLL_LOCK                        (1 << 0)
+#define CHV_CMN_DW13(ch) _PIPE(ch, _CHV_CMN_DW13_CH0, _CHV_CMN_DW0_CH1)
+
+#define _CHV_CMN_DW14_CH0              0x8138
+#define _CHV_CMN_DW1_CH1               0x8084
+#define   DPIO_AFC_RECAL               (1 << 14)
+#define   DPIO_DCLKP_EN                        (1 << 13)
+#define CHV_CMN_DW14(ch) _PIPE(ch, _CHV_CMN_DW14_CH0, _CHV_CMN_DW1_CH1)
+
+#define CHV_CMN_DW30                   0x8178
+#define   DPIO_LRC_BYPASS              (1 << 3)
+
+#define _TXLANE(ch, lane, offset) ((ch ? 0x2400 : 0) + \
+                                       (lane) * 0x200 + (offset))
+
+#define CHV_TX_DW0(ch, lane) _TXLANE(ch, lane, 0x80)
+#define CHV_TX_DW1(ch, lane) _TXLANE(ch, lane, 0x84)
+#define CHV_TX_DW2(ch, lane) _TXLANE(ch, lane, 0x88)
+#define CHV_TX_DW3(ch, lane) _TXLANE(ch, lane, 0x8c)
+#define CHV_TX_DW4(ch, lane) _TXLANE(ch, lane, 0x90)
+#define CHV_TX_DW5(ch, lane) _TXLANE(ch, lane, 0x94)
+#define CHV_TX_DW6(ch, lane) _TXLANE(ch, lane, 0x98)
+#define CHV_TX_DW7(ch, lane) _TXLANE(ch, lane, 0x9c)
+#define CHV_TX_DW8(ch, lane) _TXLANE(ch, lane, 0xa0)
+#define CHV_TX_DW9(ch, lane) _TXLANE(ch, lane, 0xa4)
+#define CHV_TX_DW10(ch, lane) _TXLANE(ch, lane, 0xa8)
+#define CHV_TX_DW11(ch, lane) _TXLANE(ch, lane, 0xac)
+#define   DPIO_FRC_LATENCY_SHFIT       8
+#define CHV_TX_DW14(ch, lane) _TXLANE(ch, lane, 0xb8)
+#define   DPIO_UPAR_SHIFT              30
 /*
  * Fence registers
  */
@@ -663,6 +946,7 @@ enum punit_power_well {
 #define RENDER_RING_BASE       0x02000
 #define BSD_RING_BASE          0x04000
 #define GEN6_BSD_RING_BASE     0x12000
+#define GEN8_BSD2_RING_BASE    0x1c000
 #define VEBOX_RING_BASE                0x1a000
 #define BLT_RING_BASE          0x22000
 #define RING_TAIL(base)                ((base)+0x30)
@@ -688,9 +972,20 @@ enum punit_power_well {
 #define RING_MAX_IDLE(base)    ((base)+0x54)
 #define RING_HWS_PGA(base)     ((base)+0x80)
 #define RING_HWS_PGA_GEN6(base)        ((base)+0x2080)
-#define ARB_MODE               0x04030
+
+#define GEN7_WR_WATERMARK      0x4028
+#define GEN7_GFX_PRIO_CTRL     0x402C
+#define ARB_MODE               0x4030
 #define   ARB_MODE_SWIZZLE_SNB (1<<4)
 #define   ARB_MODE_SWIZZLE_IVB (1<<5)
+#define GEN7_GFX_PEND_TLB0     0x4034
+#define GEN7_GFX_PEND_TLB1     0x4038
+/* L3, CVS, ZTLB, RCC, CASC LRA min, max values */
+#define GEN7_LRA_LIMITS_BASE   0x403C
+#define GEN7_LRA_LIMITS_REG_NUM        13
+#define GEN7_MEDIA_MAX_REQ_COUNT       0x4070
+#define GEN7_GFX_MAX_REQ_COUNT         0x4074
+
 #define GAMTARBMODE            0x04a08
 #define   ARB_MODE_BWGTLB_DISABLE (1<<9)
 #define   ARB_MODE_SWIZZLE_BDW (1<<1)
@@ -725,6 +1020,9 @@ enum punit_power_well {
 #define   RING_WAIT_I8XX       (1<<0) /* gen2, PRBx_HEAD */
 #define   RING_WAIT            (1<<11) /* gen3+, PRBx_CTL */
 #define   RING_WAIT_SEMAPHORE  (1<<10) /* gen6+ */
+
+#define GEN7_TLB_RD_ADDR       0x4700
+
 #if 0
 #define PRB0_TAIL      0x02030
 #define PRB0_HEAD      0x02034
@@ -748,6 +1046,7 @@ enum punit_power_well {
 #define RING_INSTDONE(base)    ((base)+0x6c)
 #define RING_INSTPS(base)      ((base)+0x70)
 #define RING_DMA_FADD(base)    ((base)+0x78)
+#define RING_DMA_FADD_UDW(base)        ((base)+0x60) /* gen8+ */
 #define RING_INSTPM(base)      ((base)+0xc0)
 #define RING_MI_MODE(base)     ((base)+0x9c)
 #define INSTPS         0x02070 /* 965+ only */
@@ -842,21 +1141,26 @@ enum punit_power_well {
 #define GFX_MODE_GEN7  0x0229c
 #define RING_MODE_GEN7(ring)   ((ring)->mmio_base+0x29c)
 #define   GFX_RUN_LIST_ENABLE          (1<<15)
-#define   GFX_TLB_INVALIDATE_ALWAYS    (1<<13)
+#define   GFX_TLB_INVALIDATE_EXPLICIT  (1<<13)
 #define   GFX_SURFACE_FAULT_ENABLE     (1<<12)
 #define   GFX_REPLAY_MODE              (1<<11)
 #define   GFX_PSMI_GRANULARITY         (1<<10)
 #define   GFX_PPGTT_ENABLE             (1<<9)
 
 #define VLV_DISPLAY_BASE 0x180000
+#define VLV_MIPI_BASE VLV_DISPLAY_BASE
 
+#define VLV_GU_CTL0    (VLV_DISPLAY_BASE + 0x2030)
+#define VLV_GU_CTL1    (VLV_DISPLAY_BASE + 0x2034)
 #define SCPD0          0x0209c /* 915+ only */
 #define IER            0x020a0
 #define IIR            0x020a4
 #define IMR            0x020a8
 #define ISR            0x020ac
 #define VLV_GUNIT_CLOCK_GATE   (VLV_DISPLAY_BASE + 0x2060)
+#define   GINT_DIS             (1<<22)
 #define   GCFG_DIS             (1<<8)
+#define VLV_GUNIT_CLOCK_GATE2  (VLV_DISPLAY_BASE + 0x2064)
 #define VLV_IIR_RW     (VLV_DISPLAY_BASE + 0x2084)
 #define VLV_IER                (VLV_DISPLAY_BASE + 0x20a0)
 #define VLV_IIR                (VLV_DISPLAY_BASE + 0x20a4)
@@ -875,7 +1179,7 @@ enum punit_power_well {
 #define   I915_ERROR_INSTRUCTION                       (1<<0)
 #define INSTPM         0x020c0
 #define   INSTPM_SELF_EN (1<<12) /* 915GM only */
-#define   INSTPM_AGPBUSY_DIS (1<<11) /* gen3: when disabled, pending interrupts
+#define   INSTPM_AGPBUSY_INT_EN (1<<11) /* gen3: when disabled, pending interrupts
                                        will not assert AGPBUSY# and will only
                                        be delivered when out of C3. */
 #define   INSTPM_FORCE_ORDERING                                (1<<7) /* GEN6+ */
@@ -956,6 +1260,10 @@ enum punit_power_well {
 #define   MI_ARB_DISPLAY_PRIORITY_A_B          (0 << 0)        /* display A > display B */
 #define   MI_ARB_DISPLAY_PRIORITY_B_A          (1 << 0)        /* display B > display A */
 
+#define MI_STATE       0x020e4 /* gen2 only */
+#define   MI_AGPBUSY_INT_EN                    (1 << 1) /* 85x only */
+#define   MI_AGPBUSY_830_MODE                  (1 << 0) /* 85x only */
+
 #define CACHE_MODE_0   0x02120 /* 915+ only */
 #define   CM0_PIPELINED_RENDER_FLUSH_DISABLE (1<<8)
 #define   CM0_IZ_OPT_DISABLE      (1<<6)
@@ -973,6 +1281,7 @@ enum punit_power_well {
 #define   ECO_FLIP_DONE                (1<<0)
 
 #define CACHE_MODE_0_GEN7      0x7000 /* IVB+ */
+#define RC_OP_FLUSH_ENABLE (1<<0)
 #define   HIZ_RAW_STALL_OPT_DISABLE (1<<2)
 #define CACHE_MODE_1           0x7004 /* IVB+ */
 #define   PIXEL_SUBSPAN_COLLECT_OPT_DISABLE    (1<<6)
@@ -984,6 +1293,7 @@ enum punit_power_well {
 
 #define GEN6_RC_SLEEP_PSMI_CONTROL     0x2050
 #define   GEN8_RC_SEMA_IDLE_MSG_DISABLE        (1 << 12)
+#define   GEN8_FF_DOP_CLOCK_GATE_DISABLE       (1<<10)
 
 #define GEN6_BSD_SLEEP_PSMI_CONTROL    0x12050
 #define   GEN6_BSD_SLEEP_MSG_DISABLE   (1 << 0)
@@ -1024,24 +1334,43 @@ enum punit_power_well {
 
 /* These are all the "old" interrupts */
 #define ILK_BSD_USER_INTERRUPT                         (1<<5)
+
+#define I915_PM_INTERRUPT                              (1<<31)
+#define I915_ISP_INTERRUPT                             (1<<22)
+#define I915_LPE_PIPE_B_INTERRUPT                      (1<<21)
+#define I915_LPE_PIPE_A_INTERRUPT                      (1<<20)
+#define I915_MIPIB_INTERRUPT                           (1<<19)
+#define I915_MIPIA_INTERRUPT                           (1<<18)
 #define I915_PIPE_CONTROL_NOTIFY_INTERRUPT             (1<<18)
 #define I915_DISPLAY_PORT_INTERRUPT                    (1<<17)
+#define I915_DISPLAY_PIPE_C_HBLANK_INTERRUPT           (1<<16)
+#define I915_MASTER_ERROR_INTERRUPT                    (1<<15)
 #define I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT     (1<<15)
+#define I915_DISPLAY_PIPE_B_HBLANK_INTERRUPT           (1<<14)
 #define I915_GMCH_THERMAL_SENSOR_EVENT_INTERRUPT       (1<<14) /* p-state */
+#define I915_DISPLAY_PIPE_A_HBLANK_INTERRUPT           (1<<13)
 #define I915_HWB_OOM_INTERRUPT                         (1<<13)
+#define I915_LPE_PIPE_C_INTERRUPT                      (1<<12)
 #define I915_SYNC_STATUS_INTERRUPT                     (1<<12)
+#define I915_MISC_INTERRUPT                            (1<<11)
 #define I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT    (1<<11)
+#define I915_DISPLAY_PIPE_C_VBLANK_INTERRUPT           (1<<10)
 #define I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT    (1<<10)
+#define I915_DISPLAY_PIPE_C_EVENT_INTERRUPT            (1<<9)
 #define I915_OVERLAY_PLANE_FLIP_PENDING_INTERRUPT      (1<<9)
+#define I915_DISPLAY_PIPE_C_DPBM_INTERRUPT             (1<<8)
 #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_DISPLAY_PIPE_A_DPBM_INTERRUPT             (1<<3)
+#define I915_DISPLAY_PIPE_B_DPBM_INTERRUPT             (1<<2)
 #define I915_DEBUG_INTERRUPT                           (1<<2)
+#define I915_WINVALID_INTERRUPT                                (1<<1)
 #define I915_USER_INTERRUPT                            (1<<1)
 #define I915_ASLE_INTERRUPT                            (1<<0)
-#define I915_BSD_USER_INTERRUPT                                (1 << 25)
+#define I915_BSD_USER_INTERRUPT                                (1<<25)
 
 #define GEN6_BSD_RNCID                 0x12198
 
@@ -1198,6 +1527,7 @@ enum punit_power_well {
 #define   GMBUS_PORT_SSC       1
 #define   GMBUS_PORT_VGADDC    2
 #define   GMBUS_PORT_PANEL     3
+#define   GMBUS_PORT_DPD_CHV   3 /* HDMID_CHV */
 #define   GMBUS_PORT_DPC       4 /* HDMIC */
 #define   GMBUS_PORT_DPB       5 /* SDVO, HDMIB */
 #define   GMBUS_PORT_DPD       6 /* HDMID */
@@ -1239,6 +1569,7 @@ enum punit_power_well {
  */
 #define DPLL_A_OFFSET 0x6014
 #define DPLL_B_OFFSET 0x6018
+#define CHV_DPLL_C_OFFSET 0x6030
 #define DPLL(pipe) (dev_priv->info.dpll_offsets[pipe] + \
                    dev_priv->info.display_mmio_offset)
 
@@ -1273,10 +1604,23 @@ enum punit_power_well {
 #define   DPLL_LOCK_VLV                        (1<<15)
 #define   DPLL_INTEGRATED_CRI_CLK_VLV  (1<<14)
 #define   DPLL_INTEGRATED_CLOCK_VLV    (1<<13)
+#define   DPLL_SSC_REF_CLOCK_CHV       (1<<13)
 #define   DPLL_PORTC_READY_MASK                (0xf << 4)
 #define   DPLL_PORTB_READY_MASK                (0xf)
 
 #define   DPLL_FPA01_P1_POST_DIV_MASK_I830     0x001f0000
+
+/* Additional CHV pll/phy registers */
+#define DPIO_PHY_STATUS                        (VLV_DISPLAY_BASE + 0x6240)
+#define   DPLL_PORTD_READY_MASK                (0xf)
+#define DISPLAY_PHY_CONTROL (VLV_DISPLAY_BASE + 0x60100)
+#define   PHY_COM_LANE_RESET_DEASSERT(phy, val) \
+                               ((phy == DPIO_PHY0) ? (val | 1) : (val | 2))
+#define   PHY_COM_LANE_RESET_ASSERT(phy, val) \
+                               ((phy == DPIO_PHY0) ? (val & ~1) : (val & ~2))
+#define DISPLAY_PHY_STATUS (VLV_DISPLAY_BASE + 0x60104)
+#define   PHY_POWERGOOD(phy)   ((phy == DPIO_PHY0) ? (1<<31) : (1<<30))
+
 /*
  * The i830 generation, in LVDS mode, defines P1 as the bit number set within
  * this field (only one bit may be set).
@@ -1317,6 +1661,7 @@ enum punit_power_well {
 
 #define DPLL_A_MD_OFFSET 0x601c /* 965+ only */
 #define DPLL_B_MD_OFFSET 0x6020 /* 965+ only */
+#define CHV_DPLL_C_MD_OFFSET 0x603c
 #define DPLL_MD(pipe) (dev_priv->info.dpll_md_offsets[pipe] + \
                       dev_priv->info.display_mmio_offset)
 
@@ -1416,7 +1761,7 @@ enum punit_power_well {
 # define DPIOUNIT_CLOCK_GATE_DISABLE           (1 << 6) /* 915-945 */
 # define OVFUNIT_CLOCK_GATE_DISABLE            (1 << 5)
 # define OVBUNIT_CLOCK_GATE_DISABLE            (1 << 4)
-/**
+/*
  * This bit must be set on the 830 to prevent hangs when turning off the
  * overlay scaler.
  */
@@ -1436,12 +1781,12 @@ enum punit_power_well {
 # define COLOR_CALCULATOR_CLOCK_GATE_DISABLE   (1 << 7)
 # define MOTION_COMP_CLOCK_GATE_DISABLE                (1 << 6)
 # define MAG_CLOCK_GATE_DISABLE                        (1 << 5)
-/** This bit must be unset on 855,865 */
+/* This bit must be unset on 855,865 */
 # define MECI_CLOCK_GATE_DISABLE               (1 << 4)
 # define DCMP_CLOCK_GATE_DISABLE               (1 << 3)
 # define MEC_CLOCK_GATE_DISABLE                        (1 << 2)
 # define MECO_CLOCK_GATE_DISABLE               (1 << 1)
-/** This bit must be set on 855,865. */
+/* This bit must be set on 855,865. */
 # define SV_CLOCK_GATE_DISABLE                 (1 << 0)
 # define I915_MPEG_CLOCK_GATE_DISABLE          (1 << 16)
 # define I915_VLD_IP_PR_CLOCK_GATE_DISABLE     (1 << 15)
@@ -1462,14 +1807,14 @@ enum punit_power_well {
 # define I915_BY_CLOCK_GATE_DISABLE            (1 << 0)
 
 # define I965_RCZ_CLOCK_GATE_DISABLE           (1 << 30)
-/** This bit must always be set on 965G/965GM */
+/* This bit must always be set on 965G/965GM */
 # define I965_RCC_CLOCK_GATE_DISABLE           (1 << 29)
 # define I965_RCPB_CLOCK_GATE_DISABLE          (1 << 28)
 # define I965_DAP_CLOCK_GATE_DISABLE           (1 << 27)
 # define I965_ROC_CLOCK_GATE_DISABLE           (1 << 26)
 # define I965_GW_CLOCK_GATE_DISABLE            (1 << 25)
 # define I965_TD_CLOCK_GATE_DISABLE            (1 << 24)
-/** This bit must always be set on 965G */
+/* This bit must always be set on 965G */
 # define I965_ISC_CLOCK_GATE_DISABLE           (1 << 23)
 # define I965_IC_CLOCK_GATE_DISABLE            (1 << 22)
 # define I965_EU_CLOCK_GATE_DISABLE            (1 << 21)
@@ -1494,6 +1839,10 @@ enum punit_power_well {
 #define VF_UNIT_CLOCK_GATE_DISABLE             (1 << 9)
 #define GS_UNIT_CLOCK_GATE_DISABLE             (1 << 7)
 #define CL_UNIT_CLOCK_GATE_DISABLE             (1 << 6)
+
+#define VDECCLK_GATE_D         0x620C          /* g4x only */
+#define  VCP_UNIT_CLOCK_GATE_DISABLE           (1 << 4)
+
 #define RAMCLK_GATE_D          0x6210          /* CRL only */
 #define DEUC                   0x6214          /* CRL only */
 
@@ -1513,6 +1862,7 @@ enum punit_power_well {
  */
 #define PALETTE_A_OFFSET 0xa000
 #define PALETTE_B_OFFSET 0xa800
+#define CHV_PALETTE_C_OFFSET 0xc000
 #define PALETTE(pipe) (dev_priv->info.palette_offsets[pipe] + \
                       dev_priv->info.display_mmio_offset)
 
@@ -1535,7 +1885,7 @@ enum punit_power_well {
 /* Memory controller frequency in MCHBAR for Haswell (possible SNB+) */
 #define DCLK (MCHBAR_MIRROR_BASE_SNB + 0x5e04)
 
-/** 915-945 and GM965 MCH register controlling DRAM channel access */
+/* 915-945 and GM965 MCH register controlling DRAM channel access */
 #define DCC                    0x10200
 #define DCC_ADDRESSING_MODE_SINGLE_CHANNEL             (0 << 0)
 #define DCC_ADDRESSING_MODE_DUAL_CHANNEL_ASYMMETRIC    (1 << 0)
@@ -1544,15 +1894,15 @@ enum punit_power_well {
 #define DCC_CHANNEL_XOR_DISABLE                                (1 << 10)
 #define DCC_CHANNEL_XOR_BIT_17                         (1 << 9)
 
-/** Pineview MCH register contains DDR3 setting */
+/* Pineview MCH register contains DDR3 setting */
 #define CSHRDDR3CTL            0x101a8
 #define CSHRDDR3CTL_DDR3       (1 << 2)
 
-/** 965 MCH register controlling DRAM channel configuration */
+/* 965 MCH register controlling DRAM channel configuration */
 #define C0DRB3                 0x10206
 #define C1DRB3                 0x10606
 
-/** snb MCH registers for reading the DRAM channel configuration */
+/* snb MCH registers for reading the DRAM channel configuration */
 #define MAD_DIMM_C0                    (MCHBAR_MIRROR_BASE_SNB + 0x5004)
 #define MAD_DIMM_C1                    (MCHBAR_MIRROR_BASE_SNB + 0x5008)
 #define MAD_DIMM_C2                    (MCHBAR_MIRROR_BASE_SNB + 0x500C)
@@ -1574,7 +1924,7 @@ enum punit_power_well {
 #define   MAD_DIMM_A_SIZE_SHIFT                0
 #define   MAD_DIMM_A_SIZE_MASK         (0xff << MAD_DIMM_A_SIZE_SHIFT)
 
-/** snb MCH registers for priority tuning */
+/* snb MCH registers for priority tuning */
 #define MCH_SSKPD                      (MCHBAR_MIRROR_BASE_SNB + 0x5d10)
 #define   MCH_SSKPD_WM0_MASK           0x3f
 #define   MCH_SSKPD_WM0_VAL            0xc
@@ -2002,6 +2352,7 @@ enum punit_power_well {
 #define TRANSCODER_A_OFFSET 0x60000
 #define TRANSCODER_B_OFFSET 0x61000
 #define TRANSCODER_C_OFFSET 0x62000
+#define CHV_TRANSCODER_C_OFFSET 0x63000
 #define TRANSCODER_EDP_OFFSET 0x6f000
 
 #define _TRANSCODER2(pipe, reg) (dev_priv->info.trans_offsets[(pipe)] - \
@@ -2226,6 +2577,7 @@ enum punit_power_well {
 #define GEN3_SDVOC     0x61160
 #define GEN4_HDMIB     GEN3_SDVOB
 #define GEN4_HDMIC     GEN3_SDVOC
+#define CHV_HDMID      0x6116C
 #define PCH_SDVOB      0xe1140
 #define PCH_HDMIB      PCH_SDVOB
 #define PCH_HDMIC      0xe1150
@@ -2246,7 +2598,7 @@ enum punit_power_well {
 #define   SDVO_PIPE_B_SELECT                   (1 << 30)
 #define   SDVO_STALL_SELECT                    (1 << 29)
 #define   SDVO_INTERRUPT_ENABLE                        (1 << 26)
-/**
+/*
  * 915G/GM SDVO pixel multiplier.
  * Programmed value is multiplier - 1, up to 5x.
  * \sa DPLL_MD_UDI_MULTIPLIER_MASK
@@ -2286,6 +2638,10 @@ enum punit_power_well {
 #define   SDVO_PIPE_SEL_CPT(pipe)              ((pipe) << 29)
 #define   SDVO_PIPE_SEL_MASK_CPT               (3 << 29)
 
+/* CHV SDVO/HDMI bits: */
+#define   SDVO_PIPE_SEL_CHV(pipe)              ((pipe) << 24)
+#define   SDVO_PIPE_SEL_MASK_CHV               (3 << 24)
+
 
 /* DVO port control */
 #define DVOA                   0x61120
@@ -2556,65 +2912,65 @@ enum punit_power_well {
 
 /* TV port control */
 #define TV_CTL                 0x68000
-/** Enables the TV encoder */
+/* Enables the TV encoder */
 # define TV_ENC_ENABLE                 (1 << 31)
-/** Sources the TV encoder input from pipe B instead of A. */
+/* Sources the TV encoder input from pipe B instead of A. */
 # define TV_ENC_PIPEB_SELECT           (1 << 30)
-/** Outputs composite video (DAC A only) */
+/* Outputs composite video (DAC A only) */
 # define TV_ENC_OUTPUT_COMPOSITE       (0 << 28)
-/** Outputs SVideo video (DAC B/C) */
+/* Outputs SVideo video (DAC B/C) */
 # define TV_ENC_OUTPUT_SVIDEO          (1 << 28)
-/** Outputs Component video (DAC A/B/C) */
+/* Outputs Component video (DAC A/B/C) */
 # define TV_ENC_OUTPUT_COMPONENT       (2 << 28)
-/** Outputs Composite and SVideo (DAC A/B/C) */
+/* Outputs Composite and SVideo (DAC A/B/C) */
 # define TV_ENC_OUTPUT_SVIDEO_COMPOSITE        (3 << 28)
 # define TV_TRILEVEL_SYNC              (1 << 21)
-/** Enables slow sync generation (945GM only) */
+/* Enables slow sync generation (945GM only) */
 # define TV_SLOW_SYNC                  (1 << 20)
-/** Selects 4x oversampling for 480i and 576p */
+/* Selects 4x oversampling for 480i and 576p */
 # define TV_OVERSAMPLE_4X              (0 << 18)
-/** Selects 2x oversampling for 720p and 1080i */
+/* Selects 2x oversampling for 720p and 1080i */
 # define TV_OVERSAMPLE_2X              (1 << 18)
-/** Selects no oversampling for 1080p */
+/* Selects no oversampling for 1080p */
 # define TV_OVERSAMPLE_NONE            (2 << 18)
-/** Selects 8x oversampling */
+/* Selects 8x oversampling */
 # define TV_OVERSAMPLE_8X              (3 << 18)
-/** Selects progressive mode rather than interlaced */
+/* Selects progressive mode rather than interlaced */
 # define TV_PROGRESSIVE                        (1 << 17)
-/** Sets the colorburst to PAL mode.  Required for non-M PAL modes. */
+/* Sets the colorburst to PAL mode.  Required for non-M PAL modes. */
 # define TV_PAL_BURST                  (1 << 16)
-/** Field for setting delay of Y compared to C */
+/* Field for setting delay of Y compared to C */
 # define TV_YC_SKEW_MASK               (7 << 12)
-/** Enables a fix for 480p/576p standard definition modes on the 915GM only */
+/* Enables a fix for 480p/576p standard definition modes on the 915GM only */
 # define TV_ENC_SDP_FIX                        (1 << 11)
-/**
+/*
  * Enables a fix for the 915GM only.
  *
  * Not sure what it does.
  */
 # define TV_ENC_C0_FIX                 (1 << 10)
-/** Bits that must be preserved by software */
+/* Bits that must be preserved by software */
 # define TV_CTL_SAVE                   ((1 << 11) | (3 << 9) | (7 << 6) | 0xf)
 # define TV_FUSE_STATE_MASK            (3 << 4)
-/** Read-only state that reports all features enabled */
+/* Read-only state that reports all features enabled */
 # define TV_FUSE_STATE_ENABLED         (0 << 4)
-/** Read-only state that reports that Macrovision is disabled in hardware*/
+/* Read-only state that reports that Macrovision is disabled in hardware*/
 # define TV_FUSE_STATE_NO_MACROVISION  (1 << 4)
-/** Read-only state that reports that TV-out is disabled in hardware. */
+/* Read-only state that reports that TV-out is disabled in hardware. */
 # define TV_FUSE_STATE_DISABLED                (2 << 4)
-/** Normal operation */
+/* Normal operation */
 # define TV_TEST_MODE_NORMAL           (0 << 0)
-/** Encoder test pattern 1 - combo pattern */
+/* Encoder test pattern 1 - combo pattern */
 # define TV_TEST_MODE_PATTERN_1                (1 << 0)
-/** Encoder test pattern 2 - full screen vertical 75% color bars */
+/* Encoder test pattern 2 - full screen vertical 75% color bars */
 # define TV_TEST_MODE_PATTERN_2                (2 << 0)
-/** Encoder test pattern 3 - full screen horizontal 75% color bars */
+/* Encoder test pattern 3 - full screen horizontal 75% color bars */
 # define TV_TEST_MODE_PATTERN_3                (3 << 0)
-/** Encoder test pattern 4 - random noise */
+/* Encoder test pattern 4 - random noise */
 # define TV_TEST_MODE_PATTERN_4                (4 << 0)
-/** Encoder test pattern 5 - linear color ramps */
+/* Encoder test pattern 5 - linear color ramps */
 # define TV_TEST_MODE_PATTERN_5                (5 << 0)
-/**
+/*
  * This test mode forces the DACs to 50% of full output.
  *
  * This is used for load detection in combination with TVDAC_SENSE_MASK
@@ -2624,35 +2980,35 @@ enum punit_power_well {
 
 #define TV_DAC                 0x68004
 # define TV_DAC_SAVE           0x00ffff00
-/**
+/*
  * Reports that DAC state change logic has reported change (RO).
  *
  * This gets cleared when TV_DAC_STATE_EN is cleared
 */
 # define TVDAC_STATE_CHG               (1 << 31)
 # define TVDAC_SENSE_MASK              (7 << 28)
-/** Reports that DAC A voltage is above the detect threshold */
+/* Reports that DAC A voltage is above the detect threshold */
 # define TVDAC_A_SENSE                 (1 << 30)
-/** Reports that DAC B voltage is above the detect threshold */
+/* Reports that DAC B voltage is above the detect threshold */
 # define TVDAC_B_SENSE                 (1 << 29)
-/** Reports that DAC C voltage is above the detect threshold */
+/* Reports that DAC C voltage is above the detect threshold */
 # define TVDAC_C_SENSE                 (1 << 28)
-/**
+/*
  * Enables DAC state detection logic, for load-based TV detection.
  *
  * The PLL of the chosen pipe (in TV_CTL) must be running, and the encoder set
  * to off, for load detection to work.
  */
 # define TVDAC_STATE_CHG_EN            (1 << 27)
-/** Sets the DAC A sense value to high */
+/* Sets the DAC A sense value to high */
 # define TVDAC_A_SENSE_CTL             (1 << 26)
-/** Sets the DAC B sense value to high */
+/* Sets the DAC B sense value to high */
 # define TVDAC_B_SENSE_CTL             (1 << 25)
-/** Sets the DAC C sense value to high */
+/* Sets the DAC C sense value to high */
 # define TVDAC_C_SENSE_CTL             (1 << 24)
-/** Overrides the ENC_ENABLE and DAC voltage levels */
+/* Overrides the ENC_ENABLE and DAC voltage levels */
 # define DAC_CTL_OVERRIDE              (1 << 7)
-/** Sets the slew rate.  Must be preserved in software */
+/* Sets the slew rate.  Must be preserved in software */
 # define ENC_TVDAC_SLEW_FAST           (1 << 6)
 # define DAC_A_1_3_V                   (0 << 4)
 # define DAC_A_1_1_V                   (1 << 4)
@@ -2667,7 +3023,7 @@ enum punit_power_well {
 # define DAC_C_0_7_V                   (2 << 0)
 # define DAC_C_MASK                    (3 << 0)
 
-/**
+/*
  * CSC coefficients are stored in a floating point format with 9 bits of
  * mantissa and 2 or 3 bits of exponent.  The exponent is represented as 2**-n,
  * where 2-bit exponents are unsigned n, and 3-bit exponents are signed n with
@@ -2682,7 +3038,7 @@ enum punit_power_well {
 #define TV_CSC_Y2              0x68014
 # define TV_BY_MASK                    0x07ff0000
 # define TV_BY_SHIFT                   16
-/**
+/*
  * Y attenuation for component video.
  *
  * Stored in 1.9 fixed point.
@@ -2699,7 +3055,7 @@ enum punit_power_well {
 #define TV_CSC_U2              0x6801c
 # define TV_BU_MASK                    0x07ff0000
 # define TV_BU_SHIFT                   16
-/**
+/*
  * U attenuation for component video.
  *
  * Stored in 1.9 fixed point.
@@ -2716,7 +3072,7 @@ enum punit_power_well {
 #define TV_CSC_V2              0x68024
 # define TV_BV_MASK                    0x07ff0000
 # define TV_BV_SHIFT                   16
-/**
+/*
  * V attenuation for component video.
  *
  * Stored in 1.9 fixed point.
@@ -2725,74 +3081,74 @@ enum punit_power_well {
 # define TV_AV_SHIFT                   0
 
 #define TV_CLR_KNOBS           0x68028
-/** 2s-complement brightness adjustment */
+/* 2s-complement brightness adjustment */
 # define TV_BRIGHTNESS_MASK            0xff000000
 # define TV_BRIGHTNESS_SHIFT           24
-/** Contrast adjustment, as a 2.6 unsigned floating point number */
+/* Contrast adjustment, as a 2.6 unsigned floating point number */
 # define TV_CONTRAST_MASK              0x00ff0000
 # define TV_CONTRAST_SHIFT             16
-/** Saturation adjustment, as a 2.6 unsigned floating point number */
+/* Saturation adjustment, as a 2.6 unsigned floating point number */
 # define TV_SATURATION_MASK            0x0000ff00
 # define TV_SATURATION_SHIFT           8
-/** Hue adjustment, as an integer phase angle in degrees */
+/* Hue adjustment, as an integer phase angle in degrees */
 # define TV_HUE_MASK                   0x000000ff
 # define TV_HUE_SHIFT                  0
 
 #define TV_CLR_LEVEL           0x6802c
-/** Controls the DAC level for black */
+/* Controls the DAC level for black */
 # define TV_BLACK_LEVEL_MASK           0x01ff0000
 # define TV_BLACK_LEVEL_SHIFT          16
-/** Controls the DAC level for blanking */
+/* Controls the DAC level for blanking */
 # define TV_BLANK_LEVEL_MASK           0x000001ff
 # define TV_BLANK_LEVEL_SHIFT          0
 
 #define TV_H_CTL_1             0x68030
-/** Number of pixels in the hsync. */
+/* Number of pixels in the hsync. */
 # define TV_HSYNC_END_MASK             0x1fff0000
 # define TV_HSYNC_END_SHIFT            16
-/** Total number of pixels minus one in the line (display and blanking). */
+/* Total number of pixels minus one in the line (display and blanking). */
 # define TV_HTOTAL_MASK                        0x00001fff
 # define TV_HTOTAL_SHIFT               0
 
 #define TV_H_CTL_2             0x68034
-/** Enables the colorburst (needed for non-component color) */
+/* Enables the colorburst (needed for non-component color) */
 # define TV_BURST_ENA                  (1 << 31)
-/** Offset of the colorburst from the start of hsync, in pixels minus one. */
+/* Offset of the colorburst from the start of hsync, in pixels minus one. */
 # define TV_HBURST_START_SHIFT         16
 # define TV_HBURST_START_MASK          0x1fff0000
-/** Length of the colorburst */
+/* Length of the colorburst */
 # define TV_HBURST_LEN_SHIFT           0
 # define TV_HBURST_LEN_MASK            0x0001fff
 
 #define TV_H_CTL_3             0x68038
-/** End of hblank, measured in pixels minus one from start of hsync */
+/* End of hblank, measured in pixels minus one from start of hsync */
 # define TV_HBLANK_END_SHIFT           16
 # define TV_HBLANK_END_MASK            0x1fff0000
-/** Start of hblank, measured in pixels minus one from start of hsync */
+/* Start of hblank, measured in pixels minus one from start of hsync */
 # define TV_HBLANK_START_SHIFT         0
 # define TV_HBLANK_START_MASK          0x0001fff
 
 #define TV_V_CTL_1             0x6803c
-/** XXX */
+/* XXX */
 # define TV_NBR_END_SHIFT              16
 # define TV_NBR_END_MASK               0x07ff0000
-/** XXX */
+/* XXX */
 # define TV_VI_END_F1_SHIFT            8
 # define TV_VI_END_F1_MASK             0x00003f00
-/** XXX */
+/* XXX */
 # define TV_VI_END_F2_SHIFT            0
 # define TV_VI_END_F2_MASK             0x0000003f
 
 #define TV_V_CTL_2             0x68040
-/** Length of vsync, in half lines */
+/* Length of vsync, in half lines */
 # define TV_VSYNC_LEN_MASK             0x07ff0000
 # define TV_VSYNC_LEN_SHIFT            16
-/** Offset of the start of vsync in field 1, measured in one less than the
+/* Offset of the start of vsync in field 1, measured in one less than the
  * number of half lines.
  */
 # define TV_VSYNC_START_F1_MASK                0x00007f00
 # define TV_VSYNC_START_F1_SHIFT       8
-/**
+/*
  * Offset of the start of vsync in field 2, measured in one less than the
  * number of half lines.
  */
@@ -2800,17 +3156,17 @@ enum punit_power_well {
 # define TV_VSYNC_START_F2_SHIFT       0
 
 #define TV_V_CTL_3             0x68044
-/** Enables generation of the equalization signal */
+/* Enables generation of the equalization signal */
 # define TV_EQUAL_ENA                  (1 << 31)
-/** Length of vsync, in half lines */
+/* Length of vsync, in half lines */
 # define TV_VEQ_LEN_MASK               0x007f0000
 # define TV_VEQ_LEN_SHIFT              16
-/** Offset of the start of equalization in field 1, measured in one less than
+/* Offset of the start of equalization in field 1, measured in one less than
  * the number of half lines.
  */
 # define TV_VEQ_START_F1_MASK          0x0007f00
 # define TV_VEQ_START_F1_SHIFT         8
-/**
+/*
  * Offset of the start of equalization in field 2, measured in one less than
  * the number of half lines.
  */
@@ -2818,13 +3174,13 @@ enum punit_power_well {
 # define TV_VEQ_START_F2_SHIFT         0
 
 #define TV_V_CTL_4             0x68048
-/**
+/*
  * Offset to start of vertical colorburst, measured in one less than the
  * number of lines from vertical start.
  */
 # define TV_VBURST_START_F1_MASK       0x003f0000
 # define TV_VBURST_START_F1_SHIFT      16
-/**
+/*
  * Offset to the end of vertical colorburst, measured in one less than the
  * number of lines from the start of NBR.
  */
@@ -2832,13 +3188,13 @@ enum punit_power_well {
 # define TV_VBURST_END_F1_SHIFT                0
 
 #define TV_V_CTL_5             0x6804c
-/**
+/*
  * Offset to start of vertical colorburst, measured in one less than the
  * number of lines from vertical start.
  */
 # define TV_VBURST_START_F2_MASK       0x003f0000
 # define TV_VBURST_START_F2_SHIFT      16
-/**
+/*
  * Offset to the end of vertical colorburst, measured in one less than the
  * number of lines from the start of NBR.
  */
@@ -2846,13 +3202,13 @@ enum punit_power_well {
 # define TV_VBURST_END_F2_SHIFT                0
 
 #define TV_V_CTL_6             0x68050
-/**
+/*
  * Offset to start of vertical colorburst, measured in one less than the
  * number of lines from vertical start.
  */
 # define TV_VBURST_START_F3_MASK       0x003f0000
 # define TV_VBURST_START_F3_SHIFT      16
-/**
+/*
  * Offset to the end of vertical colorburst, measured in one less than the
  * number of lines from the start of NBR.
  */
@@ -2860,13 +3216,13 @@ enum punit_power_well {
 # define TV_VBURST_END_F3_SHIFT                0
 
 #define TV_V_CTL_7             0x68054
-/**
+/*
  * Offset to start of vertical colorburst, measured in one less than the
  * number of lines from vertical start.
  */
 # define TV_VBURST_START_F4_MASK       0x003f0000
 # define TV_VBURST_START_F4_SHIFT      16
-/**
+/*
  * Offset to the end of vertical colorburst, measured in one less than the
  * number of lines from the start of NBR.
  */
@@ -2874,56 +3230,56 @@ enum punit_power_well {
 # define TV_VBURST_END_F4_SHIFT                0
 
 #define TV_SC_CTL_1            0x68060
-/** Turns on the first subcarrier phase generation DDA */
+/* Turns on the first subcarrier phase generation DDA */
 # define TV_SC_DDA1_EN                 (1 << 31)
-/** Turns on the first subcarrier phase generation DDA */
+/* Turns on the first subcarrier phase generation DDA */
 # define TV_SC_DDA2_EN                 (1 << 30)
-/** Turns on the first subcarrier phase generation DDA */
+/* Turns on the first subcarrier phase generation DDA */
 # define TV_SC_DDA3_EN                 (1 << 29)
-/** Sets the subcarrier DDA to reset frequency every other field */
+/* Sets the subcarrier DDA to reset frequency every other field */
 # define TV_SC_RESET_EVERY_2           (0 << 24)
-/** Sets the subcarrier DDA to reset frequency every fourth field */
+/* Sets the subcarrier DDA to reset frequency every fourth field */
 # define TV_SC_RESET_EVERY_4           (1 << 24)
-/** Sets the subcarrier DDA to reset frequency every eighth field */
+/* Sets the subcarrier DDA to reset frequency every eighth field */
 # define TV_SC_RESET_EVERY_8           (2 << 24)
-/** Sets the subcarrier DDA to never reset the frequency */
+/* Sets the subcarrier DDA to never reset the frequency */
 # define TV_SC_RESET_NEVER             (3 << 24)
-/** Sets the peak amplitude of the colorburst.*/
+/* Sets the peak amplitude of the colorburst.*/
 # define TV_BURST_LEVEL_MASK           0x00ff0000
 # define TV_BURST_LEVEL_SHIFT          16
-/** Sets the increment of the first subcarrier phase generation DDA */
+/* Sets the increment of the first subcarrier phase generation DDA */
 # define TV_SCDDA1_INC_MASK            0x00000fff
 # define TV_SCDDA1_INC_SHIFT           0
 
 #define TV_SC_CTL_2            0x68064
-/** Sets the rollover for the second subcarrier phase generation DDA */
+/* Sets the rollover for the second subcarrier phase generation DDA */
 # define TV_SCDDA2_SIZE_MASK           0x7fff0000
 # define TV_SCDDA2_SIZE_SHIFT          16
-/** Sets the increent of the second subcarrier phase generation DDA */
+/* Sets the increent of the second subcarrier phase generation DDA */
 # define TV_SCDDA2_INC_MASK            0x00007fff
 # define TV_SCDDA2_INC_SHIFT           0
 
 #define TV_SC_CTL_3            0x68068
-/** Sets the rollover for the third subcarrier phase generation DDA */
+/* Sets the rollover for the third subcarrier phase generation DDA */
 # define TV_SCDDA3_SIZE_MASK           0x7fff0000
 # define TV_SCDDA3_SIZE_SHIFT          16
-/** Sets the increent of the third subcarrier phase generation DDA */
+/* Sets the increent of the third subcarrier phase generation DDA */
 # define TV_SCDDA3_INC_MASK            0x00007fff
 # define TV_SCDDA3_INC_SHIFT           0
 
 #define TV_WIN_POS             0x68070
-/** X coordinate of the display from the start of horizontal active */
+/* X coordinate of the display from the start of horizontal active */
 # define TV_XPOS_MASK                  0x1fff0000
 # define TV_XPOS_SHIFT                 16
-/** Y coordinate of the display from the start of vertical active (NBR) */
+/* Y coordinate of the display from the start of vertical active (NBR) */
 # define TV_YPOS_MASK                  0x00000fff
 # define TV_YPOS_SHIFT                 0
 
 #define TV_WIN_SIZE            0x68074
-/** Horizontal size of the display window, measured in pixels*/
+/* Horizontal size of the display window, measured in pixels*/
 # define TV_XSIZE_MASK                 0x1fff0000
 # define TV_XSIZE_SHIFT                        16
-/**
+/*
  * Vertical size of the display window, measured in pixels.
  *
  * Must be even for interlaced modes.
@@ -2932,28 +3288,28 @@ enum punit_power_well {
 # define TV_YSIZE_SHIFT                        0
 
 #define TV_FILTER_CTL_1                0x68080
-/**
+/*
  * Enables automatic scaling calculation.
  *
  * If set, the rest of the registers are ignored, and the calculated values can
  * be read back from the register.
  */
 # define TV_AUTO_SCALE                 (1 << 31)
-/**
+/*
  * Disables the vertical filter.
  *
  * This is required on modes more than 1024 pixels wide */
 # define TV_V_FILTER_BYPASS            (1 << 29)
-/** Enables adaptive vertical filtering */
+/* Enables adaptive vertical filtering */
 # define TV_VADAPT                     (1 << 28)
 # define TV_VADAPT_MODE_MASK           (3 << 26)
-/** Selects the least adaptive vertical filtering mode */
+/* Selects the least adaptive vertical filtering mode */
 # define TV_VADAPT_MODE_LEAST          (0 << 26)
-/** Selects the moderately adaptive vertical filtering mode */
+/* Selects the moderately adaptive vertical filtering mode */
 # define TV_VADAPT_MODE_MODERATE       (1 << 26)
-/** Selects the most adaptive vertical filtering mode */
+/* Selects the most adaptive vertical filtering mode */
 # define TV_VADAPT_MODE_MOST           (3 << 26)
-/**
+/*
  * Sets the horizontal scaling factor.
  *
  * This should be the fractional part of the horizontal scaling factor divided
@@ -2965,14 +3321,14 @@ enum punit_power_well {
 # define TV_HSCALE_FRAC_SHIFT          0
 
 #define TV_FILTER_CTL_2                0x68084
-/**
+/*
  * Sets the integer part of the 3.15 fixed-point vertical scaling factor.
  *
  * TV_VSCALE should be (src height - 1) / ((interlace * dest height) - 1)
  */
 # define TV_VSCALE_INT_MASK            0x00038000
 # define TV_VSCALE_INT_SHIFT           15
-/**
+/*
  * Sets the fractional part of the 3.15 fixed-point vertical scaling factor.
  *
  * \sa TV_VSCALE_INT_MASK
@@ -2981,7 +3337,7 @@ enum punit_power_well {
 # define TV_VSCALE_FRAC_SHIFT          0
 
 #define TV_FILTER_CTL_3                0x68088
-/**
+/*
  * Sets the integer part of the 3.15 fixed-point vertical scaling factor.
  *
  * TV_VSCALE should be (src height - 1) / (1/4 * (dest height - 1))
@@ -2990,7 +3346,7 @@ enum punit_power_well {
  */
 # define TV_VSCALE_IP_INT_MASK         0x00038000
 # define TV_VSCALE_IP_INT_SHIFT                15
-/**
+/*
  * Sets the fractional part of the 3.15 fixed-point vertical scaling factor.
  *
  * For progressive modes, TV_VSCALE_IP_INT should be set to zeroes.
@@ -3002,26 +3358,26 @@ enum punit_power_well {
 
 #define TV_CC_CONTROL          0x68090
 # define TV_CC_ENABLE                  (1 << 31)
-/**
+/*
  * Specifies which field to send the CC data in.
  *
  * CC data is usually sent in field 0.
  */
 # define TV_CC_FID_MASK                        (1 << 27)
 # define TV_CC_FID_SHIFT               27
-/** Sets the horizontal position of the CC data.  Usually 135. */
+/* Sets the horizontal position of the CC data.  Usually 135. */
 # define TV_CC_HOFF_MASK               0x03ff0000
 # define TV_CC_HOFF_SHIFT              16
-/** Sets the vertical position of the CC data.  Usually 21 */
+/* Sets the vertical position of the CC data.  Usually 21 */
 # define TV_CC_LINE_MASK               0x0000003f
 # define TV_CC_LINE_SHIFT              0
 
 #define TV_CC_DATA             0x68094
 # define TV_CC_RDY                     (1 << 31)
-/** Second word of CC data to be transmitted. */
+/* Second word of CC data to be transmitted. */
 # define TV_CC_DATA_2_MASK             0x007f0000
 # define TV_CC_DATA_2_SHIFT            16
-/** First word of CC data to be transmitted. */
+/* First word of CC data to be transmitted. */
 # define TV_CC_DATA_1_MASK             0x0000007f
 # define TV_CC_DATA_1_SHIFT            0
 
@@ -3043,6 +3399,8 @@ enum punit_power_well {
 #define   DP_PORT_EN                   (1 << 31)
 #define   DP_PIPEB_SELECT              (1 << 30)
 #define   DP_PIPE_MASK                 (1 << 30)
+#define   DP_PIPE_SELECT_CHV(pipe)     ((pipe) << 16)
+#define   DP_PIPE_MASK_CHV             (3 << 16)
 
 /* Link training mode - select a suitable mode for each stage */
 #define   DP_LINK_TRAIN_PAT_1          (0 << 28)
@@ -3090,32 +3448,32 @@ enum punit_power_well {
 #define   DP_PLL_FREQ_160MHZ           (1 << 16)
 #define   DP_PLL_FREQ_MASK             (3 << 16)
 
-/** locked once port is enabled */
+/* locked once port is enabled */
 #define   DP_PORT_REVERSAL             (1 << 15)
 
 /* eDP */
 #define   DP_PLL_ENABLE                        (1 << 14)
 
-/** sends the clock on lane 15 of the PEG for debug */
+/* sends the clock on lane 15 of the PEG for debug */
 #define   DP_CLOCK_OUTPUT_ENABLE       (1 << 13)
 
 #define   DP_SCRAMBLING_DISABLE                (1 << 12)
 #define   DP_SCRAMBLING_DISABLE_IRONLAKE       (1 << 7)
 
-/** limit RGB values to avoid confusing TVs */
+/* limit RGB values to avoid confusing TVs */
 #define   DP_COLOR_RANGE_16_235                (1 << 8)
 
-/** Turn on the audio link */
+/* Turn on the audio link */
 #define   DP_AUDIO_OUTPUT_ENABLE       (1 << 6)
 
-/** vs and hs sync polarity */
+/* vs and hs sync polarity */
 #define   DP_SYNC_VS_HIGH              (1 << 4)
 #define   DP_SYNC_HS_HIGH              (1 << 3)
 
-/** A fantasy */
+/* A fantasy */
 #define   DP_DETECTED                  (1 << 2)
 
-/** The aux channel provides a way to talk to the
+/* The aux channel provides a way to talk to the
  * signal sink for DDC etc. Max packet size supported
  * is 20 bytes in each direction, hence the 5 fixed
  * data registers
@@ -3258,6 +3616,7 @@ enum punit_power_well {
 #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_EDP_RR_MODE_SWITCH          (1 << 20)
 #define   PIPECONF_CXSR_DOWNCLOCK      (1<<16)
 #define   PIPECONF_COLOR_RANGE_SELECT  (1 << 13)
 #define   PIPECONF_BPC_MASK    (0x7 << 5)
@@ -3276,6 +3635,7 @@ enum punit_power_well {
 #define   SPRITE1_FLIP_DONE_INT_EN_VLV         (1UL<<30)
 #define   PIPE_CRC_ERROR_ENABLE                        (1UL<<29)
 #define   PIPE_CRC_DONE_ENABLE                 (1UL<<28)
+#define   PERF_COUNTER2_INTERRUPT_EN           (1UL<<27)
 #define   PIPE_GMBUS_EVENT_ENABLE              (1UL<<27)
 #define   PLANE_FLIP_DONE_INT_EN_VLV           (1UL<<26)
 #define   PIPE_HOTPLUG_INTERRUPT_ENABLE                (1UL<<26)
@@ -3287,8 +3647,10 @@ enum punit_power_well {
 #define   PIPE_ODD_FIELD_INTERRUPT_ENABLE      (1UL<<21)
 #define   PIPE_EVEN_FIELD_INTERRUPT_ENABLE     (1UL<<20)
 #define   PIPE_B_PSR_INTERRUPT_ENABLE_VLV      (1UL<<19)
+#define   PERF_COUNTER_INTERRUPT_EN            (1UL<<19)
 #define   PIPE_HOTPLUG_TV_INTERRUPT_ENABLE     (1UL<<18) /* pre-965 */
 #define   PIPE_START_VBLANK_INTERRUPT_ENABLE   (1UL<<18) /* 965 or later */
+#define   PIPE_FRAMESTART_INTERRUPT_ENABLE     (1UL<<17)
 #define   PIPE_VBLANK_INTERRUPT_ENABLE         (1UL<<17)
 #define   PIPEA_HBLANK_INT_EN_VLV              (1UL<<16)
 #define   PIPE_OVERLAY_UPDATED_ENABLE          (1UL<<16)
@@ -3296,6 +3658,7 @@ enum punit_power_well {
 #define   SPRITE0_FLIP_DONE_INT_STATUS_VLV     (1UL<<14)
 #define   PIPE_CRC_ERROR_INTERRUPT_STATUS      (1UL<<13)
 #define   PIPE_CRC_DONE_INTERRUPT_STATUS       (1UL<<12)
+#define   PERF_COUNTER2_INTERRUPT_STATUS       (1UL<<11)
 #define   PIPE_GMBUS_INTERRUPT_STATUS          (1UL<<11)
 #define   PLANE_FLIP_DONE_INT_STATUS_VLV       (1UL<<10)
 #define   PIPE_HOTPLUG_INTERRUPT_STATUS                (1UL<<10)
@@ -3304,20 +3667,25 @@ enum punit_power_well {
 #define   PIPE_DPST_EVENT_STATUS               (1UL<<7)
 #define   PIPE_LEGACY_BLC_EVENT_STATUS         (1UL<<6)
 #define   PIPE_A_PSR_STATUS_VLV                        (1UL<<6)
+#define   PIPE_LEGACY_BLC_EVENT_STATUS         (1UL<<6)
 #define   PIPE_ODD_FIELD_INTERRUPT_STATUS      (1UL<<5)
 #define   PIPE_EVEN_FIELD_INTERRUPT_STATUS     (1UL<<4)
 #define   PIPE_B_PSR_STATUS_VLV                        (1UL<<3)
+#define   PERF_COUNTER_INTERRUPT_STATUS                (1UL<<3)
 #define   PIPE_HOTPLUG_TV_INTERRUPT_STATUS     (1UL<<2) /* pre-965 */
 #define   PIPE_START_VBLANK_INTERRUPT_STATUS   (1UL<<2) /* 965 or later */
+#define   PIPE_FRAMESTART_INTERRUPT_STATUS     (1UL<<1)
 #define   PIPE_VBLANK_INTERRUPT_STATUS         (1UL<<1)
+#define   PIPE_HBLANK_INT_STATUS               (1UL<<0)
 #define   PIPE_OVERLAY_UPDATED_STATUS          (1UL<<0)
 
 #define PIPESTAT_INT_ENABLE_MASK               0x7fff0000
 #define PIPESTAT_INT_STATUS_MASK               0x0000ffff
 
-#define PIPE_A_OFFSET  0x70000
-#define PIPE_B_OFFSET  0x71000
-#define PIPE_C_OFFSET  0x72000
+#define PIPE_A_OFFSET          0x70000
+#define PIPE_B_OFFSET          0x71000
+#define PIPE_C_OFFSET          0x72000
+#define CHV_PIPE_C_OFFSET      0x74000
 /*
  * There's actually no pipe EDP. Some pipe registers have
  * simply shifted from the pipe to the transcoder, while
@@ -3355,14 +3723,25 @@ enum punit_power_well {
 #define   SPRITED_FLIP_DONE_INT_EN             (1<<26)
 #define   SPRITEC_FLIP_DONE_INT_EN             (1<<25)
 #define   PLANEB_FLIP_DONE_INT_EN              (1<<24)
+#define   PIPE_PSR_INT_EN                      (1<<22)
 #define   PIPEA_LINE_COMPARE_INT_EN            (1<<21)
 #define   PIPEA_HLINE_INT_EN                   (1<<20)
 #define   PIPEA_VBLANK_INT_EN                  (1<<19)
 #define   SPRITEB_FLIP_DONE_INT_EN             (1<<18)
 #define   SPRITEA_FLIP_DONE_INT_EN             (1<<17)
 #define   PLANEA_FLIPDONE_INT_EN               (1<<16)
-
-#define DPINVGTT                               (VLV_DISPLAY_BASE + 0x7002c) /* VLV only */
+#define   PIPEC_LINE_COMPARE_INT_EN            (1<<13)
+#define   PIPEC_HLINE_INT_EN                   (1<<12)
+#define   PIPEC_VBLANK_INT_EN                  (1<<11)
+#define   SPRITEF_FLIPDONE_INT_EN              (1<<10)
+#define   SPRITEE_FLIPDONE_INT_EN              (1<<9)
+#define   PLANEC_FLIPDONE_INT_EN               (1<<8)
+
+#define DPINVGTT                               (VLV_DISPLAY_BASE + 0x7002c) /* VLV/CHV only */
+#define   SPRITEF_INVALID_GTT_INT_EN           (1<<27)
+#define   SPRITEE_INVALID_GTT_INT_EN           (1<<26)
+#define   PLANEC_INVALID_GTT_INT_EN            (1<<25)
+#define   CURSORC_INVALID_GTT_INT_EN           (1<<24)
 #define   CURSORB_INVALID_GTT_INT_EN           (1<<23)
 #define   CURSORA_INVALID_GTT_INT_EN           (1<<22)
 #define   SPRITED_INVALID_GTT_INT_EN           (1<<21)
@@ -3372,6 +3751,11 @@ enum punit_power_well {
 #define   SPRITEA_INVALID_GTT_INT_EN           (1<<17)
 #define   PLANEA_INVALID_GTT_INT_EN            (1<<16)
 #define   DPINVGTT_EN_MASK                     0xff0000
+#define   DPINVGTT_EN_MASK_CHV                 0xfff0000
+#define   SPRITEF_INVALID_GTT_STATUS           (1<<11)
+#define   SPRITEE_INVALID_GTT_STATUS           (1<<10)
+#define   PLANEC_INVALID_GTT_STATUS            (1<<9)
+#define   CURSORC_INVALID_GTT_STATUS           (1<<8)
 #define   CURSORB_INVALID_GTT_STATUS           (1<<7)
 #define   CURSORA_INVALID_GTT_STATUS           (1<<6)
 #define   SPRITED_INVALID_GTT_STATUS           (1<<5)
@@ -3381,6 +3765,7 @@ enum punit_power_well {
 #define   SPRITEA_INVALID_GTT_STATUS           (1<<1)
 #define   PLANEA_INVALID_GTT_STATUS            (1<<0)
 #define   DPINVGTT_STATUS_MASK                 0xff
+#define   DPINVGTT_STATUS_MASK_CHV             0xfff
 
 #define DSPARB                 0x70030
 #define   DSPARB_CSTART_MASK   (0x7f << 7)
@@ -3420,14 +3805,43 @@ enum punit_power_well {
 #define DDL_CURSORA_PRECISION_32       (1<<31)
 #define DDL_CURSORA_PRECISION_16       (0<<31)
 #define DDL_CURSORA_SHIFT              24
+#define DDL_SPRITEB_PRECISION_32       (1<<23)
+#define DDL_SPRITEB_PRECISION_16       (0<<23)
+#define DDL_SPRITEB_SHIFT              16
+#define DDL_SPRITEA_PRECISION_32       (1<<15)
+#define DDL_SPRITEA_PRECISION_16       (0<<15)
+#define DDL_SPRITEA_SHIFT              8
 #define DDL_PLANEA_PRECISION_32                (1<<7)
 #define DDL_PLANEA_PRECISION_16                (0<<7)
+#define DDL_PLANEA_SHIFT               0
+
 #define VLV_DDL2                       (VLV_DISPLAY_BASE + 0x70054)
 #define DDL_CURSORB_PRECISION_32       (1<<31)
 #define DDL_CURSORB_PRECISION_16       (0<<31)
 #define DDL_CURSORB_SHIFT              24
+#define DDL_SPRITED_PRECISION_32       (1<<23)
+#define DDL_SPRITED_PRECISION_16       (0<<23)
+#define DDL_SPRITED_SHIFT              16
+#define DDL_SPRITEC_PRECISION_32       (1<<15)
+#define DDL_SPRITEC_PRECISION_16       (0<<15)
+#define DDL_SPRITEC_SHIFT              8
 #define DDL_PLANEB_PRECISION_32                (1<<7)
 #define DDL_PLANEB_PRECISION_16                (0<<7)
+#define DDL_PLANEB_SHIFT               0
+
+#define VLV_DDL3                       (VLV_DISPLAY_BASE + 0x70058)
+#define DDL_CURSORC_PRECISION_32       (1<<31)
+#define DDL_CURSORC_PRECISION_16       (0<<31)
+#define DDL_CURSORC_SHIFT              24
+#define DDL_SPRITEF_PRECISION_32       (1<<23)
+#define DDL_SPRITEF_PRECISION_16       (0<<23)
+#define DDL_SPRITEF_SHIFT              16
+#define DDL_SPRITEE_PRECISION_32       (1<<15)
+#define DDL_SPRITEE_PRECISION_16       (0<<15)
+#define DDL_SPRITEE_SHIFT              8
+#define DDL_PLANEC_PRECISION_32                (1<<7)
+#define DDL_PLANEC_PRECISION_16                (0<<7)
+#define DDL_PLANEC_SHIFT               0
 
 /* FIFO watermark sizes etc */
 #define G4X_FIFO_LINE_SIZE     64
@@ -3535,12 +3949,13 @@ enum punit_power_well {
 #define   PIPE_PIXEL_MASK         0x00ffffff
 #define   PIPE_PIXEL_SHIFT        0
 /* GM45+ just has to be different */
-#define _PIPEA_FRMCOUNT_GM45   (dev_priv->info.display_mmio_offset + 0x70040)
-#define _PIPEA_FLIPCOUNT_GM45  (dev_priv->info.display_mmio_offset + 0x70044)
-#define PIPE_FRMCOUNT_GM45(pipe) _PIPE(pipe, _PIPEA_FRMCOUNT_GM45, _PIPEB_FRMCOUNT_GM45)
+#define _PIPEA_FRMCOUNT_GM45   0x70040
+#define _PIPEA_FLIPCOUNT_GM45  0x70044
+#define PIPE_FRMCOUNT_GM45(pipe) _PIPE2(pipe, _PIPEA_FRMCOUNT_GM45)
+#define PIPE_FLIPCOUNT_GM45(pipe) _PIPE2(pipe, _PIPEA_FLIPCOUNT_GM45)
 
 /* Cursor A & B regs */
-#define _CURACNTR              (dev_priv->info.display_mmio_offset + 0x70080)
+#define _CURACNTR              0x70080
 /* Old style CUR*CNTR flags (desktop 8xx) */
 #define   CURSOR_ENABLE                0x80000000
 #define   CURSOR_GAMMA_ENABLE  0x40000000
@@ -3567,28 +3982,34 @@ enum punit_power_well {
 #define   MCURSOR_PIPE_B       (1 << 28)
 #define   MCURSOR_GAMMA_ENABLE  (1 << 26)
 #define   CURSOR_TRICKLE_FEED_DISABLE  (1 << 14)
-#define _CURABASE              (dev_priv->info.display_mmio_offset + 0x70084)
-#define _CURAPOS               (dev_priv->info.display_mmio_offset + 0x70088)
+#define _CURABASE              0x70084
+#define _CURAPOS               0x70088
 #define   CURSOR_POS_MASK       0x007FF
 #define   CURSOR_POS_SIGN       0x8000
 #define   CURSOR_X_SHIFT        0
 #define   CURSOR_Y_SHIFT        16
 #define CURSIZE                        0x700a0
-#define _CURBCNTR              (dev_priv->info.display_mmio_offset + 0x700c0)
-#define _CURBBASE              (dev_priv->info.display_mmio_offset + 0x700c4)
-#define _CURBPOS               (dev_priv->info.display_mmio_offset + 0x700c8)
+#define _CURBCNTR              0x700c0
+#define _CURBBASE              0x700c4
+#define _CURBPOS               0x700c8
 
 #define _CURBCNTR_IVB          0x71080
 #define _CURBBASE_IVB          0x71084
 #define _CURBPOS_IVB           0x71088
 
-#define CURCNTR(pipe) _PIPE(pipe, _CURACNTR, _CURBCNTR)
-#define CURBASE(pipe) _PIPE(pipe, _CURABASE, _CURBBASE)
-#define CURPOS(pipe) _PIPE(pipe, _CURAPOS, _CURBPOS)
+#define _CURSOR2(pipe, reg) (dev_priv->info.cursor_offsets[(pipe)] - \
+       dev_priv->info.cursor_offsets[PIPE_A] + (reg) + \
+       dev_priv->info.display_mmio_offset)
+
+#define CURCNTR(pipe) _CURSOR2(pipe, _CURACNTR)
+#define CURBASE(pipe) _CURSOR2(pipe, _CURABASE)
+#define CURPOS(pipe) _CURSOR2(pipe, _CURAPOS)
 
-#define CURCNTR_IVB(pipe) _PIPE(pipe, _CURACNTR, _CURBCNTR_IVB)
-#define CURBASE_IVB(pipe) _PIPE(pipe, _CURABASE, _CURBBASE_IVB)
-#define CURPOS_IVB(pipe) _PIPE(pipe, _CURAPOS, _CURBPOS_IVB)
+#define CURSOR_A_OFFSET 0x70080
+#define CURSOR_B_OFFSET 0x700c0
+#define CHV_CURSOR_C_OFFSET 0x700e0
+#define IVB_CURSOR_B_OFFSET 0x71080
+#define IVB_CURSOR_C_OFFSET 0x72080
 
 /* Display A control */
 #define _DSPACNTR                              0x70180
@@ -4093,6 +4514,7 @@ enum punit_power_well {
 #define  GEN8_DE_PIPE_A_IRQ            (1<<16)
 #define  GEN8_DE_PIPE_IRQ(pipe)                (1<<(16+pipe))
 #define  GEN8_GT_VECS_IRQ              (1<<6)
+#define  GEN8_GT_PM_IRQ                        (1<<4)
 #define  GEN8_GT_VCS2_IRQ              (1<<3)
 #define  GEN8_GT_VCS1_IRQ              (1<<2)
 #define  GEN8_GT_BCS_IRQ               (1<<1)
@@ -4120,7 +4542,7 @@ enum punit_power_well {
 #define  GEN8_PIPE_SPRITE_FAULT                (1 << 9)
 #define  GEN8_PIPE_PRIMARY_FAULT       (1 << 8)
 #define  GEN8_PIPE_SPRITE_FLIP_DONE    (1 << 5)
-#define  GEN8_PIPE_FLIP_DONE           (1 << 4)
+#define  GEN8_PIPE_PRIMARY_FLIP_DONE   (1 << 4)
 #define  GEN8_PIPE_SCAN_LINE_EVENT     (1 << 2)
 #define  GEN8_PIPE_VSYNC               (1 << 1)
 #define  GEN8_PIPE_VBLANK              (1 << 0)
@@ -4832,6 +5254,8 @@ enum punit_power_well {
 #define  PORT_TRANS_SEL_CPT(pipe)      ((pipe) << 29)
 #define  PORT_TO_PIPE(val)     (((val) & (1<<30)) >> 30)
 #define  PORT_TO_PIPE_CPT(val) (((val) & PORT_TRANS_SEL_MASK) >> 29)
+#define  SDVO_PORT_TO_PIPE_CHV(val)    (((val) & (3<<24)) >> 24)
+#define  DP_PORT_TO_PIPE_CHV(val)      (((val) & (3<<16)) >> 16)
 
 #define TRANS_DP_CTL_A         0xe0300
 #define TRANS_DP_CTL_B         0xe1300
@@ -4888,6 +5312,8 @@ enum punit_power_well {
 
 #define  EDP_LINK_TRAIN_VOL_EMP_MASK_IVB       (0x3f<<22)
 
+#define  VLV_PMWGICZ                           0x1300a4
+
 #define  FORCEWAKE                             0xA18C
 #define  FORCEWAKE_VLV                         0x1300b0
 #define  FORCEWAKE_ACK_VLV                     0x1300b4
@@ -4896,15 +5322,22 @@ enum punit_power_well {
 #define  FORCEWAKE_ACK_HSW                     0x130044
 #define  FORCEWAKE_ACK                         0x130090
 #define  VLV_GTLC_WAKE_CTRL                    0x130090
+#define   VLV_GTLC_RENDER_CTX_EXISTS           (1 << 25)
+#define   VLV_GTLC_MEDIA_CTX_EXISTS            (1 << 24)
+#define   VLV_GTLC_ALLOWWAKEREQ                        (1 << 0)
+
 #define  VLV_GTLC_PW_STATUS                    0x130094
-#define VLV_GTLC_PW_RENDER_STATUS_MASK         0x80
-#define VLV_GTLC_PW_MEDIA_STATUS_MASK          0x20
+#define   VLV_GTLC_ALLOWWAKEACK                        (1 << 0)
+#define   VLV_GTLC_ALLOWWAKEERR                        (1 << 1)
+#define   VLV_GTLC_PW_MEDIA_STATUS_MASK                (1 << 5)
+#define   VLV_GTLC_PW_RENDER_STATUS_MASK       (1 << 7)
 #define  FORCEWAKE_MT                          0xa188 /* multi-threaded */
 #define   FORCEWAKE_KERNEL                     0x1
 #define   FORCEWAKE_USER                       0x2
 #define  FORCEWAKE_MT_ACK                      0x130040
 #define  ECOBUS                                        0xa180
 #define    FORCEWAKE_MT_ENABLE                 (1<<5)
+#define  VLV_SPAREG2H                          0xA194
 
 #define  GTFIFODBG                             0x120000
 #define    GT_FIFO_SBDROPERR                   (1<<6)
@@ -4924,6 +5357,7 @@ enum punit_power_well {
 #define  HSW_EDRAM_PRESENT                     0x120010
 
 #define GEN6_UCGCTL1                           0x9400
+# define GEN6_EU_TCUNIT_CLOCK_GATE_DISABLE             (1 << 16)
 # define GEN6_BLBUNIT_CLOCK_GATE_DISABLE               (1 << 5)
 # define GEN6_CSUNIT_CLOCK_GATE_DISABLE                        (1 << 7)
 
@@ -4934,12 +5368,19 @@ enum punit_power_well {
 # define GEN6_RCPBUNIT_CLOCK_GATE_DISABLE              (1 << 12)
 # define GEN6_RCCUNIT_CLOCK_GATE_DISABLE               (1 << 11)
 
+#define GEN6_UCGCTL3                           0x9408
+
 #define GEN7_UCGCTL4                           0x940c
 #define  GEN7_L3BANK2X_CLOCK_GATE_DISABLE      (1<<25)
 
+#define GEN6_RCGCTL1                           0x9410
+#define GEN6_RCGCTL2                           0x9414
+#define GEN6_RSTCTL                            0x9420
+
 #define GEN8_UCGCTL6                           0x9430
 #define   GEN8_SDEUNIT_CLOCK_GATE_DISABLE      (1<<14)
 
+#define GEN6_GFXPAUSE                          0xA000
 #define GEN6_RPNSWREQ                          0xA008
 #define   GEN6_TURBO_DISABLE                   (1<<31)
 #define   GEN6_FREQUENCY(x)                    ((x)<<25)
@@ -4992,6 +5433,9 @@ enum punit_power_well {
 #define GEN6_RP_UP_EI                          0xA068
 #define GEN6_RP_DOWN_EI                                0xA06C
 #define GEN6_RP_IDLE_HYSTERSIS                 0xA070
+#define GEN6_RPDEUHWTC                         0xA080
+#define GEN6_RPDEUC                            0xA084
+#define GEN6_RPDEUCSW                          0xA088
 #define GEN6_RC_STATE                          0xA094
 #define GEN6_RC1_WAKE_RATE_LIMIT               0xA098
 #define GEN6_RC6_WAKE_RATE_LIMIT               0xA09C
@@ -4999,11 +5443,15 @@ enum punit_power_well {
 #define GEN6_RC_EVALUATION_INTERVAL            0xA0A8
 #define GEN6_RC_IDLE_HYSTERSIS                 0xA0AC
 #define GEN6_RC_SLEEP                          0xA0B0
+#define GEN6_RCUBMABDTMR                       0xA0B0
 #define GEN6_RC1e_THRESHOLD                    0xA0B4
 #define GEN6_RC6_THRESHOLD                     0xA0B8
 #define GEN6_RC6p_THRESHOLD                    0xA0BC
+#define VLV_RCEDATA                            0xA0BC
 #define GEN6_RC6pp_THRESHOLD                   0xA0C0
 #define GEN6_PMINTRMSK                         0xA168
+#define GEN8_PMINTR_REDIRECT_TO_NON_DISP       (1<<31)
+#define VLV_PWRDWNUPCTL                                0xA294
 
 #define GEN6_PMISR                             0x44020
 #define GEN6_PMIMR                             0x44024 /* rps_lock */
@@ -5020,6 +5468,9 @@ enum punit_power_well {
                                                 GEN6_PM_RP_DOWN_THRESHOLD | \
                                                 GEN6_PM_RP_DOWN_TIMEOUT)
 
+#define GEN7_GT_SCRATCH_BASE                   0x4F100
+#define GEN7_GT_SCRATCH_REG_NUM                        8
+
 #define VLV_GTLC_SURVIVABILITY_REG              0x130098
 #define VLV_GFX_CLK_STATUS_BIT                 (1<<3)
 #define VLV_GFX_CLK_FORCE_ON_BIT               (1<<2)
@@ -5030,6 +5481,9 @@ enum punit_power_well {
 #define   VLV_MEDIA_RC6_COUNT_EN               (1<<1)
 #define   VLV_RENDER_RC6_COUNT_EN              (1<<0)
 #define GEN6_GT_GFX_RC6                                0x138108
+#define VLV_GT_RENDER_RC6                      0x138108
+#define VLV_GT_MEDIA_RC6                       0x13810C
+
 #define GEN6_GT_GFX_RC6p                       0x13810C
 #define GEN6_GT_GFX_RC6pp                      0x138110
 
index 56785e8fb2eb5f7e209b8713f3815b4c5ecbcf22..043123c77a1f4e0f3d92143f3804eb614c94fd24 100644 (file)
@@ -328,8 +328,6 @@ int i915_save_state(struct drm_device *dev)
                }
        }
 
-       intel_disable_gt_powersave(dev);
-
        /* Cache mode state */
        if (INTEL_INFO(dev)->gen < 7)
                dev_priv->regfile.saveCACHE_MODE_0 = I915_READ(CACHE_MODE_0);
index 9c57029f6f4b4b9ba6b63ce2493eac4942f5eb59..86ce39aad0ffd25e5502bf616f1349b387922f3e 100644 (file)
@@ -186,7 +186,7 @@ i915_l3_write(struct file *filp, struct kobject *kobj,
        struct drm_minor *dminor = dev_to_drm_minor(dev);
        struct drm_device *drm_dev = dminor->dev;
        struct drm_i915_private *dev_priv = drm_dev->dev_private;
-       struct i915_hw_context *ctx;
+       struct intel_context *ctx;
        u32 *temp = NULL; /* Just here to make handling failures easy */
        int slice = (int)(uintptr_t)attr->private;
        int ret;
@@ -263,6 +263,8 @@ static ssize_t gt_cur_freq_mhz_show(struct device *kdev,
 
        flush_delayed_work(&dev_priv->rps.delayed_resume_work);
 
+       intel_runtime_pm_get(dev_priv);
+
        mutex_lock(&dev_priv->rps.hw_lock);
        if (IS_VALLEYVIEW(dev_priv->dev)) {
                u32 freq;
@@ -273,6 +275,8 @@ static ssize_t gt_cur_freq_mhz_show(struct device *kdev,
        }
        mutex_unlock(&dev_priv->rps.hw_lock);
 
+       intel_runtime_pm_put(dev_priv);
+
        return snprintf(buf, PAGE_SIZE, "%d\n", ret);
 }
 
index 23c26f1f8b372e960f9f3dd5901a818a6a314374..f5aa0067755a04aa0bb52d58f3dcabc3e3aa1f94 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <drm/drmP.h>
 #include "i915_drv.h"
+#include "intel_drv.h"
 #include "intel_ringbuffer.h"
 
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM_STRING __stringify(TRACE_SYSTEM)
 #define TRACE_INCLUDE_FILE i915_trace
 
+/* pipe updates */
+
+TRACE_EVENT(i915_pipe_update_start,
+           TP_PROTO(struct intel_crtc *crtc, u32 min, u32 max),
+           TP_ARGS(crtc, min, max),
+
+           TP_STRUCT__entry(
+                            __field(enum pipe, pipe)
+                            __field(u32, frame)
+                            __field(u32, scanline)
+                            __field(u32, min)
+                            __field(u32, max)
+                            ),
+
+           TP_fast_assign(
+                          __entry->pipe = crtc->pipe;
+                          __entry->frame = crtc->base.dev->driver->get_vblank_counter(crtc->base.dev,
+                                                                                      crtc->pipe);
+                          __entry->scanline = intel_get_crtc_scanline(crtc);
+                          __entry->min = min;
+                          __entry->max = max;
+                          ),
+
+           TP_printk("pipe %c, frame=%u, scanline=%u, min=%u, max=%u",
+                     pipe_name(__entry->pipe), __entry->frame,
+                      __entry->scanline, __entry->min, __entry->max)
+);
+
+TRACE_EVENT(i915_pipe_update_vblank_evaded,
+           TP_PROTO(struct intel_crtc *crtc, u32 min, u32 max, u32 frame),
+           TP_ARGS(crtc, min, max, frame),
+
+           TP_STRUCT__entry(
+                            __field(enum pipe, pipe)
+                            __field(u32, frame)
+                            __field(u32, scanline)
+                            __field(u32, min)
+                            __field(u32, max)
+                            ),
+
+           TP_fast_assign(
+                          __entry->pipe = crtc->pipe;
+                          __entry->frame = frame;
+                          __entry->scanline = intel_get_crtc_scanline(crtc);
+                          __entry->min = min;
+                          __entry->max = max;
+                          ),
+
+           TP_printk("pipe %c, frame=%u, scanline=%u, min=%u, max=%u",
+                     pipe_name(__entry->pipe), __entry->frame,
+                      __entry->scanline, __entry->min, __entry->max)
+);
+
+TRACE_EVENT(i915_pipe_update_end,
+           TP_PROTO(struct intel_crtc *crtc, u32 frame),
+           TP_ARGS(crtc, frame),
+
+           TP_STRUCT__entry(
+                            __field(enum pipe, pipe)
+                            __field(u32, frame)
+                            __field(u32, scanline)
+                            ),
+
+           TP_fast_assign(
+                          __entry->pipe = crtc->pipe;
+                          __entry->frame = frame;
+                          __entry->scanline = intel_get_crtc_scanline(crtc);
+                          ),
+
+           TP_printk("pipe %c, frame=%u, scanline=%u",
+                     pipe_name(__entry->pipe), __entry->frame,
+                     __entry->scanline)
+);
+
 /* object tracking */
 
 TRACE_EVENT(i915_gem_object_create,
@@ -251,8 +326,8 @@ TRACE_EVENT(i915_gem_evict_vm,
 );
 
 TRACE_EVENT(i915_gem_ring_sync_to,
-           TP_PROTO(struct intel_ring_buffer *from,
-                    struct intel_ring_buffer *to,
+           TP_PROTO(struct intel_engine_cs *from,
+                    struct intel_engine_cs *to,
                     u32 seqno),
            TP_ARGS(from, to, seqno),
 
@@ -277,7 +352,7 @@ TRACE_EVENT(i915_gem_ring_sync_to,
 );
 
 TRACE_EVENT(i915_gem_ring_dispatch,
-           TP_PROTO(struct intel_ring_buffer *ring, u32 seqno, u32 flags),
+           TP_PROTO(struct intel_engine_cs *ring, u32 seqno, u32 flags),
            TP_ARGS(ring, seqno, flags),
 
            TP_STRUCT__entry(
@@ -300,7 +375,7 @@ TRACE_EVENT(i915_gem_ring_dispatch,
 );
 
 TRACE_EVENT(i915_gem_ring_flush,
-           TP_PROTO(struct intel_ring_buffer *ring, u32 invalidate, u32 flush),
+           TP_PROTO(struct intel_engine_cs *ring, u32 invalidate, u32 flush),
            TP_ARGS(ring, invalidate, flush),
 
            TP_STRUCT__entry(
@@ -323,7 +398,7 @@ TRACE_EVENT(i915_gem_ring_flush,
 );
 
 DECLARE_EVENT_CLASS(i915_gem_request,
-           TP_PROTO(struct intel_ring_buffer *ring, u32 seqno),
+           TP_PROTO(struct intel_engine_cs *ring, u32 seqno),
            TP_ARGS(ring, seqno),
 
            TP_STRUCT__entry(
@@ -343,12 +418,12 @@ DECLARE_EVENT_CLASS(i915_gem_request,
 );
 
 DEFINE_EVENT(i915_gem_request, i915_gem_request_add,
-           TP_PROTO(struct intel_ring_buffer *ring, u32 seqno),
+           TP_PROTO(struct intel_engine_cs *ring, u32 seqno),
            TP_ARGS(ring, seqno)
 );
 
 TRACE_EVENT(i915_gem_request_complete,
-           TP_PROTO(struct intel_ring_buffer *ring),
+           TP_PROTO(struct intel_engine_cs *ring),
            TP_ARGS(ring),
 
            TP_STRUCT__entry(
@@ -368,12 +443,12 @@ TRACE_EVENT(i915_gem_request_complete,
 );
 
 DEFINE_EVENT(i915_gem_request, i915_gem_request_retire,
-           TP_PROTO(struct intel_ring_buffer *ring, u32 seqno),
+           TP_PROTO(struct intel_engine_cs *ring, u32 seqno),
            TP_ARGS(ring, seqno)
 );
 
 TRACE_EVENT(i915_gem_request_wait_begin,
-           TP_PROTO(struct intel_ring_buffer *ring, u32 seqno),
+           TP_PROTO(struct intel_engine_cs *ring, u32 seqno),
            TP_ARGS(ring, seqno),
 
            TP_STRUCT__entry(
@@ -402,12 +477,12 @@ TRACE_EVENT(i915_gem_request_wait_begin,
 );
 
 DEFINE_EVENT(i915_gem_request, i915_gem_request_wait_end,
-           TP_PROTO(struct intel_ring_buffer *ring, u32 seqno),
+           TP_PROTO(struct intel_engine_cs *ring, u32 seqno),
            TP_ARGS(ring, seqno)
 );
 
 DECLARE_EVENT_CLASS(i915_ring,
-           TP_PROTO(struct intel_ring_buffer *ring),
+           TP_PROTO(struct intel_engine_cs *ring),
            TP_ARGS(ring),
 
            TP_STRUCT__entry(
@@ -424,12 +499,12 @@ DECLARE_EVENT_CLASS(i915_ring,
 );
 
 DEFINE_EVENT(i915_ring, i915_ring_wait_begin,
-           TP_PROTO(struct intel_ring_buffer *ring),
+           TP_PROTO(struct intel_engine_cs *ring),
            TP_ARGS(ring)
 );
 
 DEFINE_EVENT(i915_ring, i915_ring_wait_end,
-           TP_PROTO(struct intel_ring_buffer *ring),
+           TP_PROTO(struct intel_engine_cs *ring),
            TP_ARGS(ring)
 );
 
index aff4a113cda3c0cd724d3b4d13ce0fb8bbc48313..1ee98f121a00fbe4be26cf60187d9b7a81983caf 100644 (file)
@@ -49,13 +49,19 @@ find_section(struct bdb_header *bdb, int section_id)
        total = bdb->bdb_size;
 
        /* walk the sections looking for section_id */
-       while (index < total) {
+       while (index + 3 < total) {
                current_id = *(base + index);
                index++;
+
                current_size = *((u16 *)(base + index));
                index += 2;
+
+               if (index + current_size > total)
+                       return NULL;
+
                if (current_id == section_id)
                        return base + index;
+
                index += current_size;
        }
 
@@ -206,7 +212,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
        const struct lvds_dvo_timing *panel_dvo_timing;
        const struct lvds_fp_timing *fp_timing;
        struct drm_display_mode *panel_fixed_mode;
-       int i, downclock;
+       int i, downclock, drrs_mode;
 
        lvds_options = find_section(bdb, BDB_LVDS_OPTIONS);
        if (!lvds_options)
@@ -218,6 +224,28 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
 
        panel_type = lvds_options->panel_type;
 
+       drrs_mode = (lvds_options->dps_panel_type_bits
+                               >> (panel_type * 2)) & MODE_MASK;
+       /*
+        * VBT has static DRRS = 0 and seamless DRRS = 2.
+        * The below piece of code is required to adjust vbt.drrs_type
+        * to match the enum drrs_support_type.
+        */
+       switch (drrs_mode) {
+       case 0:
+               dev_priv->vbt.drrs_type = STATIC_DRRS_SUPPORT;
+               DRM_DEBUG_KMS("DRRS supported mode is static\n");
+               break;
+       case 2:
+               dev_priv->vbt.drrs_type = SEAMLESS_DRRS_SUPPORT;
+               DRM_DEBUG_KMS("DRRS supported mode is seamless\n");
+               break;
+       default:
+               dev_priv->vbt.drrs_type = DRRS_NOT_SUPPORTED;
+               DRM_DEBUG_KMS("DRRS not supported (VBT input)\n");
+               break;
+       }
+
        lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA);
        if (!lvds_lfp_data)
                return;
@@ -526,6 +554,16 @@ parse_driver_features(struct drm_i915_private *dev_priv,
 
        if (driver->dual_frequency)
                dev_priv->render_reclock_avail = true;
+
+       DRM_DEBUG_KMS("DRRS State Enabled:%d\n", driver->drrs_enabled);
+       /*
+        * If DRRS is not supported, drrs_type has to be set to 0.
+        * This is because, VBT is configured in such a way that
+        * static DRRS is 0 and DRRS not supported is represented by
+        * driver->drrs_enabled=false
+        */
+       if (!driver->drrs_enabled)
+               dev_priv->vbt.drrs_type = DRRS_NOT_SUPPORTED;
 }
 
 static void
@@ -628,19 +666,221 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
        }
 }
 
+static u8 *goto_next_sequence(u8 *data, int *size)
+{
+       u16 len;
+       int tmp = *size;
+
+       if (--tmp < 0)
+               return NULL;
+
+       /* goto first element */
+       data++;
+       while (1) {
+               switch (*data) {
+               case MIPI_SEQ_ELEM_SEND_PKT:
+                       /*
+                        * skip by this element payload size
+                        * skip elem id, command flag and data type
+                        */
+                       tmp -= 5;
+                       if (tmp < 0)
+                               return NULL;
+
+                       data += 3;
+                       len = *((u16 *)data);
+
+                       tmp -= len;
+                       if (tmp < 0)
+                               return NULL;
+
+                       /* skip by len */
+                       data = data + 2 + len;
+                       break;
+               case MIPI_SEQ_ELEM_DELAY:
+                       /* skip by elem id, and delay is 4 bytes */
+                       tmp -= 5;
+                       if (tmp < 0)
+                               return NULL;
+
+                       data += 5;
+                       break;
+               case MIPI_SEQ_ELEM_GPIO:
+                       tmp -= 3;
+                       if (tmp < 0)
+                               return NULL;
+
+                       data += 3;
+                       break;
+               default:
+                       DRM_ERROR("Unknown element\n");
+                       return NULL;
+               }
+
+               /* end of sequence ? */
+               if (*data == 0)
+                       break;
+       }
+
+       /* goto next sequence or end of block byte */
+       if (--tmp < 0)
+               return NULL;
+
+       data++;
+
+       /* update amount of data left for the sequence block to be parsed */
+       *size = tmp;
+       return data;
+}
+
 static void
 parse_mipi(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
 {
-       struct bdb_mipi *mipi;
+       struct bdb_mipi_config *start;
+       struct bdb_mipi_sequence *sequence;
+       struct mipi_config *config;
+       struct mipi_pps_data *pps;
+       u8 *data, *seq_data;
+       int i, panel_id, seq_size;
+       u16 block_size;
+
+       /* parse MIPI blocks only if LFP type is MIPI */
+       if (!dev_priv->vbt.has_mipi)
+               return;
+
+       /* Initialize this to undefined indicating no generic MIPI support */
+       dev_priv->vbt.dsi.panel_id = MIPI_DSI_UNDEFINED_PANEL_ID;
+
+       /* Block #40 is already parsed and panel_fixed_mode is
+        * stored in dev_priv->lfp_lvds_vbt_mode
+        * resuse this when needed
+        */
+
+       /* Parse #52 for panel index used from panel_type already
+        * parsed
+        */
+       start = find_section(bdb, BDB_MIPI_CONFIG);
+       if (!start) {
+               DRM_DEBUG_KMS("No MIPI config BDB found");
+               return;
+       }
+
+       DRM_DEBUG_DRIVER("Found MIPI Config block, panel index = %d\n",
+                                                               panel_type);
 
-       mipi = find_section(bdb, BDB_MIPI_CONFIG);
-       if (!mipi) {
-               DRM_DEBUG_KMS("No MIPI BDB found");
+       /*
+        * get hold of the correct configuration block and pps data as per
+        * the panel_type as index
+        */
+       config = &start->config[panel_type];
+       pps = &start->pps[panel_type];
+
+       /* store as of now full data. Trim when we realise all is not needed */
+       dev_priv->vbt.dsi.config = kmemdup(config, sizeof(struct mipi_config), GFP_KERNEL);
+       if (!dev_priv->vbt.dsi.config)
+               return;
+
+       dev_priv->vbt.dsi.pps = kmemdup(pps, sizeof(struct mipi_pps_data), GFP_KERNEL);
+       if (!dev_priv->vbt.dsi.pps) {
+               kfree(dev_priv->vbt.dsi.config);
                return;
        }
 
-       /* XXX: add more info */
+       /* We have mandatory mipi config blocks. Initialize as generic panel */
        dev_priv->vbt.dsi.panel_id = MIPI_DSI_GENERIC_PANEL_ID;
+
+       /* Check if we have sequence block as well */
+       sequence = find_section(bdb, BDB_MIPI_SEQUENCE);
+       if (!sequence) {
+               DRM_DEBUG_KMS("No MIPI Sequence found, parsing complete\n");
+               return;
+       }
+
+       DRM_DEBUG_DRIVER("Found MIPI sequence block\n");
+
+       block_size = get_blocksize(sequence);
+
+       /*
+        * parse the sequence block for individual sequences
+        */
+       dev_priv->vbt.dsi.seq_version = sequence->version;
+
+       seq_data = &sequence->data[0];
+
+       /*
+        * sequence block is variable length and hence we need to parse and
+        * get the sequence data for specific panel id
+        */
+       for (i = 0; i < MAX_MIPI_CONFIGURATIONS; i++) {
+               panel_id = *seq_data;
+               seq_size = *((u16 *) (seq_data + 1));
+               if (panel_id == panel_type)
+                       break;
+
+               /* skip the sequence including seq header of 3 bytes */
+               seq_data = seq_data + 3 + seq_size;
+               if ((seq_data - &sequence->data[0]) > block_size) {
+                       DRM_ERROR("Sequence start is beyond sequence block size, corrupted sequence block\n");
+                       return;
+               }
+       }
+
+       if (i == MAX_MIPI_CONFIGURATIONS) {
+               DRM_ERROR("Sequence block detected but no valid configuration\n");
+               return;
+       }
+
+       /* check if found sequence is completely within the sequence block
+        * just being paranoid */
+       if (seq_size > block_size) {
+               DRM_ERROR("Corrupted sequence/size, bailing out\n");
+               return;
+       }
+
+       /* skip the panel id(1 byte) and seq size(2 bytes) */
+       dev_priv->vbt.dsi.data = kmemdup(seq_data + 3, seq_size, GFP_KERNEL);
+       if (!dev_priv->vbt.dsi.data)
+               return;
+
+       /*
+        * loop into the sequence data and split into multiple sequneces
+        * There are only 5 types of sequences as of now
+        */
+       data = dev_priv->vbt.dsi.data;
+       dev_priv->vbt.dsi.size = seq_size;
+
+       /* two consecutive 0x00 indicate end of all sequences */
+       while (1) {
+               int seq_id = *data;
+               if (MIPI_SEQ_MAX > seq_id && seq_id > MIPI_SEQ_UNDEFINED) {
+                       dev_priv->vbt.dsi.sequence[seq_id] = data;
+                       DRM_DEBUG_DRIVER("Found mipi sequence - %d\n", seq_id);
+               } else {
+                       DRM_ERROR("undefined sequence\n");
+                       goto err;
+               }
+
+               /* partial parsing to skip elements */
+               data = goto_next_sequence(data, &seq_size);
+
+               if (data == NULL) {
+                       DRM_ERROR("Sequence elements going beyond block itself. Sequence block parsing failed\n");
+                       goto err;
+               }
+
+               if (*data == 0)
+                       break; /* end of sequence reached */
+       }
+
+       DRM_DEBUG_DRIVER("MIPI related vbt parsing complete\n");
+       return;
+err:
+       kfree(dev_priv->vbt.dsi.data);
+       dev_priv->vbt.dsi.data = NULL;
+
+       /* error during parsing so set all pointers to null
+        * because of partial parsing */
+       memset(dev_priv->vbt.dsi.sequence, 0, MIPI_SEQ_MAX);
 }
 
 static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port,
@@ -823,6 +1063,15 @@ parse_device_mapping(struct drm_i915_private *dev_priv,
                        /* skip the device block if device type is invalid */
                        continue;
                }
+
+               if (p_child->common.dvo_port >= DVO_PORT_MIPIA
+                   && p_child->common.dvo_port <= DVO_PORT_MIPID
+                   &&p_child->common.device_type & DEVICE_TYPE_MIPI_OUTPUT) {
+                       DRM_DEBUG_KMS("Found MIPI as LFP\n");
+                       dev_priv->vbt.has_mipi = 1;
+                       dev_priv->vbt.dsi.port = p_child->common.dvo_port;
+               }
+
                child_dev_ptr = dev_priv->vbt.child_dev + count;
                count++;
                memcpy((void *)child_dev_ptr, (void *)p_child,
@@ -893,6 +1142,46 @@ static const struct dmi_system_id intel_no_opregion_vbt[] = {
        { }
 };
 
+static struct bdb_header *validate_vbt(char *base, size_t size,
+                                      struct vbt_header *vbt,
+                                      const char *source)
+{
+       size_t offset;
+       struct bdb_header *bdb;
+
+       if (vbt == NULL) {
+               DRM_DEBUG_DRIVER("VBT signature missing\n");
+               return NULL;
+       }
+
+       offset = (char *)vbt - base;
+       if (offset + sizeof(struct vbt_header) > size) {
+               DRM_DEBUG_DRIVER("VBT header incomplete\n");
+               return NULL;
+       }
+
+       if (memcmp(vbt->signature, "$VBT", 4)) {
+               DRM_DEBUG_DRIVER("VBT invalid signature\n");
+               return NULL;
+       }
+
+       offset += vbt->bdb_offset;
+       if (offset + sizeof(struct bdb_header) > size) {
+               DRM_DEBUG_DRIVER("BDB header incomplete\n");
+               return NULL;
+       }
+
+       bdb = (struct bdb_header *)(base + offset);
+       if (offset + bdb->bdb_size > size) {
+               DRM_DEBUG_DRIVER("BDB incomplete\n");
+               return NULL;
+       }
+
+       DRM_DEBUG_KMS("Using VBT from %s: %20s\n",
+                     source, vbt->signature);
+       return bdb;
+}
+
 /**
  * intel_parse_bios - find VBT and initialize settings from the BIOS
  * @dev: DRM device
@@ -916,20 +1205,13 @@ intel_parse_bios(struct drm_device *dev)
        init_vbt_defaults(dev_priv);
 
        /* XXX Should this validation be moved to intel_opregion.c? */
-       if (!dmi_check_system(intel_no_opregion_vbt) && dev_priv->opregion.vbt) {
-               struct vbt_header *vbt = dev_priv->opregion.vbt;
-               if (memcmp(vbt->signature, "$VBT", 4) == 0) {
-                       DRM_DEBUG_KMS("Using VBT from OpRegion: %20s\n",
-                                        vbt->signature);
-                       bdb = (struct bdb_header *)((char *)vbt + vbt->bdb_offset);
-               } else
-                       dev_priv->opregion.vbt = NULL;
-       }
+       if (!dmi_check_system(intel_no_opregion_vbt) && dev_priv->opregion.vbt)
+               bdb = validate_vbt((char *)dev_priv->opregion.header, OPREGION_SIZE,
+                                  (struct vbt_header *)dev_priv->opregion.vbt,
+                                  "OpRegion");
 
        if (bdb == NULL) {
-               struct vbt_header *vbt = NULL;
-               size_t size;
-               int i;
+               size_t i, size;
 
                bios = pci_map_rom(pdev, &size);
                if (!bios)
@@ -937,19 +1219,18 @@ intel_parse_bios(struct drm_device *dev)
 
                /* Scour memory looking for the VBT signature */
                for (i = 0; i + 4 < size; i++) {
-                       if (!memcmp(bios + i, "$VBT", 4)) {
-                               vbt = (struct vbt_header *)(bios + i);
+                       if (memcmp(bios + i, "$VBT", 4) == 0) {
+                               bdb = validate_vbt(bios, size,
+                                                  (struct vbt_header *)(bios + i),
+                                                  "PCI ROM");
                                break;
                        }
                }
 
-               if (!vbt) {
-                       DRM_DEBUG_DRIVER("VBT signature missing\n");
+               if (!bdb) {
                        pci_unmap_rom(pdev, bios);
                        return -1;
                }
-
-               bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset);
        }
 
        /* Grab useful general definitions */
index f27f7b282465aa76a77690ebe8239775c923f06e..b98667796337d776b1d1d39c677a5dfffe6b32b6 100644 (file)
@@ -282,6 +282,9 @@ struct bdb_general_definitions {
        union child_device_config devices[0];
 } __packed;
 
+/* Mask for DRRS / Panel Channel / SSC / BLT control bits extraction */
+#define MODE_MASK              0x3
+
 struct bdb_lvds_options {
        u8 panel_type;
        u8 rsvd1;
@@ -294,6 +297,18 @@ struct bdb_lvds_options {
        u8 lvds_edid:1;
        u8 rsvd2:1;
        u8 rsvd4;
+       /* LVDS Panel channel bits stored here */
+       u32 lvds_panel_channel_bits;
+       /* LVDS SSC (Spread Spectrum Clock) bits stored here. */
+       u16 ssc_bits;
+       u16 ssc_freq;
+       u16 ssc_ddt;
+       /* Panel color depth defined here */
+       u16 panel_color_depth;
+       /* LVDS panel type bits stored here */
+       u32 dps_panel_type_bits;
+       /* LVDS backlight control type bits stored here */
+       u32 blt_control_type_bits;
 } __packed;
 
 /* LFP pointer table contains entries to the struct below */
@@ -482,6 +497,20 @@ struct bdb_driver_features {
 
        u8 hdmi_termination;
        u8 custom_vbt_version;
+       /* Driver features data block */
+       u16 rmpm_enabled:1;
+       u16 s2ddt_enabled:1;
+       u16 dpst_enabled:1;
+       u16 bltclt_enabled:1;
+       u16 adb_enabled:1;
+       u16 drrs_enabled:1;
+       u16 grs_enabled:1;
+       u16 gpmt_enabled:1;
+       u16 tbt_enabled:1;
+       u16 psr_enabled:1;
+       u16 ips_enabled:1;
+       u16 reserved3:4;
+       u16 pc_feature_valid:1;
 } __packed;
 
 #define EDP_18BPP      0
@@ -714,6 +743,10 @@ int intel_parse_bios(struct drm_device *dev);
 #define DVO_PORT_DPC   8
 #define DVO_PORT_DPD   9
 #define DVO_PORT_DPA   10
+#define DVO_PORT_MIPIA 21
+#define DVO_PORT_MIPIB 22
+#define DVO_PORT_MIPIC 23
+#define DVO_PORT_MIPID 24
 
 /* Block 52 contains MIPI Panel info
  * 6 such enteries will there. Index into correct
@@ -870,4 +903,35 @@ struct bdb_mipi_sequence {
        u8 data[0];
 };
 
+/* MIPI Sequnece Block definitions */
+enum mipi_seq {
+       MIPI_SEQ_UNDEFINED = 0,
+       MIPI_SEQ_ASSERT_RESET,
+       MIPI_SEQ_INIT_OTP,
+       MIPI_SEQ_DISPLAY_ON,
+       MIPI_SEQ_DISPLAY_OFF,
+       MIPI_SEQ_DEASSERT_RESET,
+       MIPI_SEQ_MAX
+};
+
+enum mipi_seq_element {
+       MIPI_SEQ_ELEM_UNDEFINED = 0,
+       MIPI_SEQ_ELEM_SEND_PKT,
+       MIPI_SEQ_ELEM_DELAY,
+       MIPI_SEQ_ELEM_GPIO,
+       MIPI_SEQ_ELEM_STATUS,
+       MIPI_SEQ_ELEM_MAX
+};
+
+enum mipi_gpio_pin_index {
+       MIPI_GPIO_UNDEFINED = 0,
+       MIPI_GPIO_PANEL_ENABLE,
+       MIPI_GPIO_BL_ENABLE,
+       MIPI_GPIO_PWM_ENABLE,
+       MIPI_GPIO_RESET_N,
+       MIPI_GPIO_PWR_DOWN_R,
+       MIPI_GPIO_STDBY_RST_N,
+       MIPI_GPIO_MAX
+};
+
 #endif /* _I830_BIOS_H_ */
index aa5a3dc43342a1d484a5419d5fa8388049348636..5a045d3bd77e7c7f77c7e8fc55292d37714eab3a 100644 (file)
@@ -144,28 +144,49 @@ static void intel_crt_set_dpms(struct intel_encoder *encoder, int mode)
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crt *crt = intel_encoder_to_crt(encoder);
-       u32 temp;
+       struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
+       struct drm_display_mode *adjusted_mode = &crtc->config.adjusted_mode;
+       u32 adpa;
+
+       if (INTEL_INFO(dev)->gen >= 5)
+               adpa = ADPA_HOTPLUG_BITS;
+       else
+               adpa = 0;
 
-       temp = I915_READ(crt->adpa_reg);
-       temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
-       temp &= ~ADPA_DAC_ENABLE;
+       if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
+               adpa |= ADPA_HSYNC_ACTIVE_HIGH;
+       if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
+               adpa |= ADPA_VSYNC_ACTIVE_HIGH;
+
+       /* For CPT allow 3 pipe config, for others just use A or B */
+       if (HAS_PCH_LPT(dev))
+               ; /* Those bits don't exist here */
+       else if (HAS_PCH_CPT(dev))
+               adpa |= PORT_TRANS_SEL_CPT(crtc->pipe);
+       else if (crtc->pipe == 0)
+               adpa |= ADPA_PIPE_A_SELECT;
+       else
+               adpa |= ADPA_PIPE_B_SELECT;
+
+       if (!HAS_PCH_SPLIT(dev))
+               I915_WRITE(BCLRPAT(crtc->pipe), 0);
 
        switch (mode) {
        case DRM_MODE_DPMS_ON:
-               temp |= ADPA_DAC_ENABLE;
+               adpa |= ADPA_DAC_ENABLE;
                break;
        case DRM_MODE_DPMS_STANDBY:
-               temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE;
+               adpa |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE;
                break;
        case DRM_MODE_DPMS_SUSPEND:
-               temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE;
+               adpa |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE;
                break;
        case DRM_MODE_DPMS_OFF:
-               temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE;
+               adpa |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE;
                break;
        }
 
-       I915_WRITE(crt->adpa_reg, temp);
+       I915_WRITE(crt->adpa_reg, adpa);
 }
 
 static void intel_disable_crt(struct intel_encoder *encoder)
@@ -274,42 +295,6 @@ static bool intel_crt_compute_config(struct intel_encoder *encoder,
        return true;
 }
 
-static void intel_crt_mode_set(struct intel_encoder *encoder)
-{
-
-       struct drm_device *dev = encoder->base.dev;
-       struct intel_crt *crt = intel_encoder_to_crt(encoder);
-       struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       struct drm_display_mode *adjusted_mode = &crtc->config.adjusted_mode;
-       u32 adpa;
-
-       if (INTEL_INFO(dev)->gen >= 5)
-               adpa = ADPA_HOTPLUG_BITS;
-       else
-               adpa = 0;
-
-       if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
-               adpa |= ADPA_HSYNC_ACTIVE_HIGH;
-       if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
-               adpa |= ADPA_VSYNC_ACTIVE_HIGH;
-
-       /* For CPT allow 3 pipe config, for others just use A or B */
-       if (HAS_PCH_LPT(dev))
-               ; /* Those bits don't exist here */
-       else if (HAS_PCH_CPT(dev))
-               adpa |= PORT_TRANS_SEL_CPT(crtc->pipe);
-       else if (crtc->pipe == 0)
-               adpa |= ADPA_PIPE_A_SELECT;
-       else
-               adpa |= ADPA_PIPE_B_SELECT;
-
-       if (!HAS_PCH_SPLIT(dev))
-               I915_WRITE(BCLRPAT(crtc->pipe), 0);
-
-       I915_WRITE(crt->adpa_reg, adpa);
-}
-
 static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector)
 {
        struct drm_device *dev = connector->dev;
@@ -645,11 +630,12 @@ intel_crt_detect(struct drm_connector *connector, bool force)
        enum intel_display_power_domain power_domain;
        enum drm_connector_status status;
        struct intel_load_detect_pipe tmp;
+       struct drm_modeset_acquire_ctx ctx;
 
        intel_runtime_pm_get(dev_priv);
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force=%d\n",
-                     connector->base.id, drm_get_connector_name(connector),
+                     connector->base.id, connector->name,
                      force);
 
        power_domain = intel_display_port_power_domain(intel_encoder);
@@ -688,12 +674,12 @@ intel_crt_detect(struct drm_connector *connector, bool force)
        }
 
        /* for pre-945g platforms use load detect */
-       if (intel_get_load_detect_pipe(connector, NULL, &tmp)) {
+       if (intel_get_load_detect_pipe(connector, NULL, &tmp, &ctx)) {
                if (intel_crt_detect_ddc(connector))
                        status = connector_status_connected;
                else
                        status = intel_crt_load_detect(crt);
-               intel_release_load_detect_pipe(connector, &tmp);
+               intel_release_load_detect_pipe(connector, &tmp, &ctx);
        } else
                status = connector_status_unknown;
 
@@ -867,7 +853,6 @@ void intel_crt_init(struct drm_device *dev)
                crt->adpa_reg = ADPA;
 
        crt->base.compute_config = intel_crt_compute_config;
-       crt->base.mode_set = intel_crt_mode_set;
        crt->base.disable = intel_disable_crt;
        crt->base.enable = intel_enable_crt;
        if (I915_HAS_HOTPLUG(dev))
index 0ad4e96000632c693df6c8faa736cdd3aaac85f1..b17b9c7c769f92eca6497a938db2f40310cd0b10 100644 (file)
@@ -364,55 +364,6 @@ void hsw_fdi_link_train(struct drm_crtc *crtc)
        DRM_ERROR("FDI link training failed!\n");
 }
 
-static void intel_ddi_mode_set(struct intel_encoder *encoder)
-{
-       struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
-       int port = intel_ddi_get_encoder_port(encoder);
-       int pipe = crtc->pipe;
-       int type = encoder->type;
-       struct drm_display_mode *adjusted_mode = &crtc->config.adjusted_mode;
-
-       DRM_DEBUG_KMS("Preparing DDI mode on port %c, pipe %c\n",
-                     port_name(port), pipe_name(pipe));
-
-       crtc->eld_vld = false;
-       if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
-               struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
-               struct intel_digital_port *intel_dig_port =
-                       enc_to_dig_port(&encoder->base);
-
-               intel_dp->DP = intel_dig_port->saved_port_bits |
-                              DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW;
-               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",
-                                        pipe_name(crtc->pipe));
-
-                       /* write eld */
-                       DRM_DEBUG_DRIVER("DP audio: write eld information\n");
-                       intel_write_eld(&encoder->base, adjusted_mode);
-               }
-       } else if (type == INTEL_OUTPUT_HDMI) {
-               struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
-
-               if (intel_hdmi->has_audio) {
-                       /* Proper support for digital audio needs a new logic
-                        * and a new set of registers, so we leave it for future
-                        * patch bombing.
-                        */
-                       DRM_DEBUG_DRIVER("HDMI audio on pipe %c on DDI\n",
-                                        pipe_name(crtc->pipe));
-
-                       /* write eld */
-                       DRM_DEBUG_DRIVER("HDMI audio: write eld information\n");
-                       intel_write_eld(&encoder->base, adjusted_mode);
-               }
-
-               intel_hdmi->set_infoframes(&encoder->base, adjusted_mode);
-       }
-}
-
 static struct intel_encoder *
 intel_ddi_get_crtc_encoder(struct drm_crtc *crtc)
 {
@@ -1062,9 +1013,7 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
        }
 
        if (type == INTEL_OUTPUT_HDMI) {
-               struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
-
-               if (intel_hdmi->has_hdmi_sink)
+               if (intel_crtc->config.has_hdmi_sink)
                        temp |= TRANS_DDI_MODE_SELECT_HDMI;
                else
                        temp |= TRANS_DDI_MODE_SELECT_DVI;
@@ -1293,28 +1242,48 @@ void intel_ddi_disable_pipe_clock(struct intel_crtc *intel_crtc)
 static void intel_ddi_pre_enable(struct intel_encoder *intel_encoder)
 {
        struct drm_encoder *encoder = &intel_encoder->base;
-       struct drm_crtc *crtc = encoder->crtc;
        struct drm_i915_private *dev_priv = encoder->dev->dev_private;
-       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       struct intel_crtc *crtc = to_intel_crtc(encoder->crtc);
        enum port port = intel_ddi_get_encoder_port(intel_encoder);
        int type = intel_encoder->type;
 
+       if (crtc->config.has_audio) {
+               DRM_DEBUG_DRIVER("Audio on pipe %c on DDI\n",
+                                pipe_name(crtc->pipe));
+
+               /* write eld */
+               DRM_DEBUG_DRIVER("DDI audio: write eld information\n");
+               intel_write_eld(encoder, &crtc->config.adjusted_mode);
+       }
+
        if (type == INTEL_OUTPUT_EDP) {
                struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
                intel_edp_panel_on(intel_dp);
        }
 
-       WARN_ON(intel_crtc->ddi_pll_sel == PORT_CLK_SEL_NONE);
-       I915_WRITE(PORT_CLK_SEL(port), intel_crtc->ddi_pll_sel);
+       WARN_ON(crtc->ddi_pll_sel == PORT_CLK_SEL_NONE);
+       I915_WRITE(PORT_CLK_SEL(port), crtc->ddi_pll_sel);
 
        if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
                struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+               struct intel_digital_port *intel_dig_port =
+                       enc_to_dig_port(encoder);
+
+               intel_dp->DP = intel_dig_port->saved_port_bits |
+                              DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW;
+               intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count);
 
                intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
                intel_dp_start_link_train(intel_dp);
                intel_dp_complete_link_train(intel_dp);
                if (port != PORT_A)
                        intel_dp_stop_link_train(intel_dp);
+       } else if (type == INTEL_OUTPUT_HDMI) {
+               struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+
+               intel_hdmi->set_infoframes(encoder,
+                                          crtc->config.has_hdmi_sink,
+                                          &crtc->config.adjusted_mode);
        }
 }
 
@@ -1385,7 +1354,8 @@ static void intel_enable_ddi(struct intel_encoder *intel_encoder)
                intel_edp_psr_enable(intel_dp);
        }
 
-       if (intel_crtc->eld_vld && type != INTEL_OUTPUT_EDP) {
+       if (intel_crtc->config.has_audio) {
+               intel_display_power_get(dev_priv, POWER_DOMAIN_AUDIO);
                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);
@@ -1403,11 +1373,14 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder)
        struct drm_i915_private *dev_priv = dev->dev_private;
        uint32_t tmp;
 
-       if (intel_crtc->eld_vld && type != INTEL_OUTPUT_EDP) {
+       /* We can't touch HSW_AUD_PIN_ELD_CP_VLD uncionditionally because this
+        * register is part of the power well on Haswell. */
+       if (intel_crtc->config.has_audio) {
                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);
+               intel_display_power_put(dev_priv, POWER_DOMAIN_AUDIO);
        }
 
        if (type == INTEL_OUTPUT_EDP) {
@@ -1580,6 +1553,7 @@ void intel_ddi_get_config(struct intel_encoder *encoder,
 
        switch (temp & TRANS_DDI_MODE_SELECT_MASK) {
        case TRANS_DDI_MODE_SELECT_HDMI:
+               pipe_config->has_hdmi_sink = true;
        case TRANS_DDI_MODE_SELECT_DVI:
        case TRANS_DDI_MODE_SELECT_FDI:
                break;
@@ -1592,6 +1566,12 @@ void intel_ddi_get_config(struct intel_encoder *encoder,
                break;
        }
 
+       if (intel_display_power_enabled(dev_priv, POWER_DOMAIN_AUDIO)) {
+               temp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD);
+               if (temp & (AUDIO_OUTPUT_ENABLE_A << (intel_crtc->pipe * 4)))
+                       pipe_config->has_audio = true;
+       }
+
        if (encoder->type == INTEL_OUTPUT_EDP && dev_priv->vbt.edp_bpp &&
            pipe_config->pipe_bpp > dev_priv->vbt.edp_bpp) {
                /*
@@ -1708,7 +1688,6 @@ void intel_ddi_init(struct drm_device *dev, enum port port)
                         DRM_MODE_ENCODER_TMDS);
 
        intel_encoder->compute_config = intel_ddi_compute_config;
-       intel_encoder->mode_set = intel_ddi_mode_set;
        intel_encoder->enable = intel_enable_ddi;
        intel_encoder->pre_enable = intel_ddi_pre_enable;
        intel_encoder->disable = intel_disable_ddi;
index 5b60e25baa321367775e1d75e29a5ad153d72a51..efd3cf50cb0f6a647d670a0f2a7b886b4e1d0b8a 100644 (file)
@@ -41,6 +41,9 @@
 #include <drm/drm_crtc_helper.h>
 #include <linux/dma_remapping.h>
 
+#define DIV_ROUND_CLOSEST_ULL(ll, d)   \
+       ({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; })
+
 static void intel_increase_pllclock(struct drm_crtc *crtc);
 static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on);
 
@@ -55,6 +58,15 @@ static int intel_framebuffer_init(struct drm_device *dev,
                                  struct intel_framebuffer *ifb,
                                  struct drm_mode_fb_cmd2 *mode_cmd,
                                  struct drm_i915_gem_object *obj);
+static void intel_dp_set_m_n(struct intel_crtc *crtc);
+static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc);
+static void intel_set_pipe_timings(struct intel_crtc *intel_crtc);
+static void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc,
+                                        struct intel_link_m_n *m_n);
+static void ironlake_set_pipeconf(struct drm_crtc *crtc);
+static void haswell_set_pipeconf(struct drm_crtc *crtc);
+static void intel_set_pipe_csc(struct drm_crtc *crtc);
+static void vlv_prepare_pll(struct intel_crtc *crtc);
 
 typedef struct {
        int     min, max;
@@ -328,6 +340,22 @@ static const intel_limit_t intel_limits_vlv = {
        .p2 = { .p2_slow = 2, .p2_fast = 20 }, /* slow=min, fast=max */
 };
 
+static const intel_limit_t intel_limits_chv = {
+       /*
+        * These are the data rate limits (measured in fast clocks)
+        * since those are the strictest limits we have.  The fast
+        * clock and actual rate limits are more relaxed, so checking
+        * them would make no difference.
+        */
+       .dot = { .min = 25000 * 5, .max = 540000 * 5},
+       .vco = { .min = 4860000, .max = 6700000 },
+       .n = { .min = 1, .max = 1 },
+       .m1 = { .min = 2, .max = 2 },
+       .m2 = { .min = 24 << 22, .max = 175 << 22 },
+       .p1 = { .min = 2, .max = 4 },
+       .p2 = { .p2_slow = 1, .p2_fast = 14 },
+};
+
 static void vlv_clock(int refclk, intel_clock_t *clock)
 {
        clock->m = clock->m1 * clock->m2;
@@ -412,6 +440,8 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc, int refclk)
                        limit = &intel_limits_pineview_lvds;
                else
                        limit = &intel_limits_pineview_sdvo;
+       } else if (IS_CHERRYVIEW(dev)) {
+               limit = &intel_limits_chv;
        } else if (IS_VALLEYVIEW(dev)) {
                limit = &intel_limits_vlv;
        } else if (!IS_GEN2(dev)) {
@@ -456,6 +486,17 @@ static void i9xx_clock(int refclk, intel_clock_t *clock)
        clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p);
 }
 
+static void chv_clock(int refclk, intel_clock_t *clock)
+{
+       clock->m = clock->m1 * clock->m2;
+       clock->p = clock->p1 * clock->p2;
+       if (WARN_ON(clock->n == 0 || clock->p == 0))
+               return;
+       clock->vco = DIV_ROUND_CLOSEST_ULL((uint64_t)refclk * clock->m,
+                       clock->n << 22);
+       clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p);
+}
+
 #define INTELPllInvalid(s)   do { /* DRM_DEBUG(s); */ return false; } while (0)
 /**
  * Returns whether the given set of divisors are valid for a given refclk with
@@ -731,6 +772,58 @@ vlv_find_best_dpll(const intel_limit_t *limit, struct drm_crtc *crtc,
        return found;
 }
 
+static bool
+chv_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;
+       uint64_t m2;
+       int found = false;
+
+       memset(best_clock, 0, sizeof(*best_clock));
+
+       /*
+        * Based on hardware doc, the n always set to 1, and m1 always
+        * set to 2.  If requires to support 200Mhz refclk, we need to
+        * revisit this because n may not 1 anymore.
+        */
+       clock.n = 1, clock.m1 = 2;
+       target *= 5;    /* fast clock */
+
+       for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) {
+               for (clock.p2 = limit->p2.p2_fast;
+                               clock.p2 >= limit->p2.p2_slow;
+                               clock.p2 -= clock.p2 > 10 ? 2 : 1) {
+
+                       clock.p = clock.p1 * clock.p2;
+
+                       m2 = DIV_ROUND_CLOSEST_ULL(((uint64_t)target * clock.p *
+                                       clock.n) << 22, refclk * clock.m1);
+
+                       if (m2 > INT_MAX/clock.m1)
+                               continue;
+
+                       clock.m2 = m2;
+
+                       chv_clock(refclk, &clock);
+
+                       if (!intel_PLL_is_valid(dev, limit, &clock))
+                               continue;
+
+                       /* based on hardware requirement, prefer bigger p
+                        */
+                       if (clock.p > best_clock->p) {
+                               *best_clock = clock;
+                               found = true;
+                       }
+               }
+       }
+
+       return found;
+}
+
 bool intel_crtc_active(struct drm_crtc *crtc)
 {
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
@@ -765,7 +858,7 @@ static void g4x_wait_for_vblank(struct drm_device *dev, int pipe)
        frame = I915_READ(frame_reg);
 
        if (wait_for(I915_READ_NOTRACE(frame_reg) != frame, 50))
-               DRM_DEBUG_KMS("vblank wait timed out\n");
+               WARN(1, "vblank wait timed out\n");
 }
 
 /**
@@ -878,7 +971,7 @@ bool ibx_digital_port_connected(struct drm_i915_private *dev_priv,
        u32 bit;
 
        if (HAS_PCH_IBX(dev_priv->dev)) {
-               switch(port->port) {
+               switch (port->port) {
                case PORT_B:
                        bit = SDE_PORTB_HOTPLUG;
                        break;
@@ -892,7 +985,7 @@ bool ibx_digital_port_connected(struct drm_i915_private *dev_priv,
                        return true;
                }
        } else {
-               switch(port->port) {
+               switch (port->port) {
                case PORT_B:
                        bit = SDE_PORTB_HOTPLUG_CPT;
                        break;
@@ -1097,10 +1190,8 @@ static void assert_cursor(struct drm_i915_private *dev_priv,
 
        if (IS_845G(dev) || IS_I865G(dev))
                cur_state = I915_READ(_CURACNTR) & CURSOR_ENABLE;
-       else if (INTEL_INFO(dev)->gen <= 6 || IS_VALLEYVIEW(dev))
-               cur_state = I915_READ(CURCNTR(pipe)) & CURSOR_MODE;
        else
-               cur_state = I915_READ(CURCNTR_IVB(pipe)) & CURSOR_MODE;
+               cur_state = I915_READ(CURCNTR(pipe)) & CURSOR_MODE;
 
        WARN(cur_state != state,
             "cursor on pipe %c assertion failure (expected %s, current %s)\n",
@@ -1253,6 +1344,9 @@ static bool dp_pipe_enabled(struct drm_i915_private *dev_priv,
                u32     trans_dp_ctl = I915_READ(trans_dp_ctl_reg);
                if ((trans_dp_ctl & TRANS_DP_PORT_SEL_MASK) != port_sel)
                        return false;
+       } else if (IS_CHERRYVIEW(dev_priv->dev)) {
+               if ((val & DP_PIPE_MASK_CHV) != DP_PIPE_SELECT_CHV(pipe))
+                       return false;
        } else {
                if ((val & DP_PIPE_MASK) != (pipe << 30))
                        return false;
@@ -1269,6 +1363,9 @@ static bool hdmi_pipe_enabled(struct drm_i915_private *dev_priv,
        if (HAS_PCH_CPT(dev_priv->dev)) {
                if ((val & SDVO_PIPE_SEL_MASK_CPT) != SDVO_PIPE_SEL_CPT(pipe))
                        return false;
+       } else if (IS_CHERRYVIEW(dev_priv->dev)) {
+               if ((val & SDVO_PIPE_SEL_MASK_CHV) != SDVO_PIPE_SEL_CHV(pipe))
+                       return false;
        } else {
                if ((val & SDVO_PIPE_SEL_MASK) != SDVO_PIPE_SEL(pipe))
                        return false;
@@ -1367,7 +1464,17 @@ static void intel_init_dpio(struct drm_device *dev)
        if (!IS_VALLEYVIEW(dev))
                return;
 
-       DPIO_PHY_IOSF_PORT(DPIO_PHY0) = IOSF_PORT_DPIO;
+       /*
+        * IOSF_PORT_DPIO is used for VLV x2 PHY (DP/HDMI B and C),
+        * CHV x1 PHY (DP/HDMI D)
+        * IOSF_PORT_DPIO_2 is used for CHV x2 PHY (DP/HDMI B and C)
+        */
+       if (IS_CHERRYVIEW(dev)) {
+               DPIO_PHY_IOSF_PORT(DPIO_PHY0) = IOSF_PORT_DPIO_2;
+               DPIO_PHY_IOSF_PORT(DPIO_PHY1) = IOSF_PORT_DPIO;
+       } else {
+               DPIO_PHY_IOSF_PORT(DPIO_PHY0) = IOSF_PORT_DPIO;
+       }
 }
 
 static void intel_reset_dpio(struct drm_device *dev)
@@ -1377,25 +1484,48 @@ static void intel_reset_dpio(struct drm_device *dev)
        if (!IS_VALLEYVIEW(dev))
                return;
 
-       /*
-        * Enable the CRI clock source so we can get at the display and the
-        * reference clock for VGA hotplug / manual detection.
-        */
-       I915_WRITE(DPLL(PIPE_B), I915_READ(DPLL(PIPE_B)) |
-                  DPLL_REFA_CLK_ENABLE_VLV |
-                  DPLL_INTEGRATED_CRI_CLK_VLV);
+       if (IS_CHERRYVIEW(dev)) {
+               enum dpio_phy phy;
+               u32 val;
 
-       /*
-        * From VLV2A0_DP_eDP_DPIO_driver_vbios_notes_10.docx -
-        *  6.  De-assert cmn_reset/side_reset. Same as VLV X0.
-        *   a. GUnit 0x2110 bit[0] set to 1 (def 0)
-        *   b. The other bits such as sfr settings / modesel may all be set
-        *      to 0.
-        *
-        * This should only be done on init and resume from S3 with both
-        * PLLs disabled, or we risk losing DPIO and PLL synchronization.
-        */
-       I915_WRITE(DPIO_CTL, I915_READ(DPIO_CTL) | DPIO_CMNRST);
+               for (phy = DPIO_PHY0; phy < I915_NUM_PHYS_VLV; phy++) {
+                       /* Poll for phypwrgood signal */
+                       if (wait_for(I915_READ(DISPLAY_PHY_STATUS) &
+                                               PHY_POWERGOOD(phy), 1))
+                               DRM_ERROR("Display PHY %d is not power up\n", phy);
+
+                       /*
+                        * Deassert common lane reset for PHY.
+                        *
+                        * This should only be done on init and resume from S3
+                        * with both PLLs disabled, or we risk losing DPIO and
+                        * PLL synchronization.
+                        */
+                       val = I915_READ(DISPLAY_PHY_CONTROL);
+                       I915_WRITE(DISPLAY_PHY_CONTROL,
+                               PHY_COM_LANE_RESET_DEASSERT(phy, val));
+               }
+
+       } else {
+               /*
+                * If DPIO has already been reset, e.g. by BIOS, just skip all
+                * this.
+                */
+               if (I915_READ(DPIO_CTL) & DPIO_CMNRST)
+                       return;
+
+               /*
+                * From VLV2A0_DP_eDP_HDMI_DPIO_driver_vbios_notes_11.docx:
+                * Need to assert and de-assert PHY SB reset by gating the
+                * common lane power, then un-gating it.
+                * Simply ungating isn't enough to reset the PHY enough to get
+                * ports and lanes running.
+                */
+               __vlv_set_power_well(dev_priv, PUNIT_POWER_WELL_DPIO_CMN_BC,
+                                    false);
+               __vlv_set_power_well(dev_priv, PUNIT_POWER_WELL_DPIO_CMN_BC,
+                                    true);
+       }
 }
 
 static void vlv_enable_pll(struct intel_crtc *crtc)
@@ -1436,6 +1566,44 @@ static void vlv_enable_pll(struct intel_crtc *crtc)
        udelay(150); /* wait for warmup */
 }
 
+static void chv_enable_pll(struct intel_crtc *crtc)
+{
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int pipe = crtc->pipe;
+       enum dpio_channel port = vlv_pipe_to_channel(pipe);
+       u32 tmp;
+
+       assert_pipe_disabled(dev_priv, crtc->pipe);
+
+       BUG_ON(!IS_CHERRYVIEW(dev_priv->dev));
+
+       mutex_lock(&dev_priv->dpio_lock);
+
+       /* Enable back the 10bit clock to display controller */
+       tmp = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW14(port));
+       tmp |= DPIO_DCLKP_EN;
+       vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW14(port), tmp);
+
+       /*
+        * Need to wait > 100ns between dclkp clock enable bit and PLL enable.
+        */
+       udelay(1);
+
+       /* Enable PLL */
+       I915_WRITE(DPLL(pipe), crtc->config.dpll_hw_state.dpll);
+
+       /* Check PLL is locked */
+       if (wait_for(((I915_READ(DPLL(pipe)) & DPLL_LOCK_VLV) == DPLL_LOCK_VLV), 1))
+               DRM_ERROR("PLL %d failed to lock\n", pipe);
+
+       /* not sure when this should be written */
+       I915_WRITE(DPLL_MD(pipe), crtc->config.dpll_hw_state.dpll_md);
+       POSTING_READ(DPLL_MD(pipe));
+
+       mutex_unlock(&dev_priv->dpio_lock);
+}
+
 static void i9xx_enable_pll(struct intel_crtc *crtc)
 {
        struct drm_device *dev = crtc->base.dev;
@@ -1519,45 +1687,92 @@ static void vlv_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe)
                val = DPLL_INTEGRATED_CRI_CLK_VLV | DPLL_REFA_CLK_ENABLE_VLV;
        I915_WRITE(DPLL(pipe), val);
        POSTING_READ(DPLL(pipe));
+
+}
+
+static void chv_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe)
+{
+       enum dpio_channel port = vlv_pipe_to_channel(pipe);
+       u32 val;
+
+       /* Make sure the pipe isn't still relying on us */
+       assert_pipe_disabled(dev_priv, pipe);
+
+       /* Set PLL en = 0 */
+       val = DPLL_SSC_REF_CLOCK_CHV;
+       if (pipe != PIPE_A)
+               val |= DPLL_INTEGRATED_CRI_CLK_VLV;
+       I915_WRITE(DPLL(pipe), val);
+       POSTING_READ(DPLL(pipe));
+
+       mutex_lock(&dev_priv->dpio_lock);
+
+       /* Disable 10bit clock to display controller */
+       val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW14(port));
+       val &= ~DPIO_DCLKP_EN;
+       vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW14(port), val);
+
+       mutex_unlock(&dev_priv->dpio_lock);
 }
 
 void vlv_wait_port_ready(struct drm_i915_private *dev_priv,
                struct intel_digital_port *dport)
 {
        u32 port_mask;
+       int dpll_reg;
 
        switch (dport->port) {
        case PORT_B:
                port_mask = DPLL_PORTB_READY_MASK;
+               dpll_reg = DPLL(0);
                break;
        case PORT_C:
                port_mask = DPLL_PORTC_READY_MASK;
+               dpll_reg = DPLL(0);
+               break;
+       case PORT_D:
+               port_mask = DPLL_PORTD_READY_MASK;
+               dpll_reg = DPIO_PHY_STATUS;
                break;
        default:
                BUG();
        }
 
-       if (wait_for((I915_READ(DPLL(0)) & port_mask) == 0, 1000))
+       if (wait_for((I915_READ(dpll_reg) & port_mask) == 0, 1000))
                WARN(1, "timed out waiting for port %c ready: 0x%08x\n",
-                    port_name(dport->port), I915_READ(DPLL(0)));
+                    port_name(dport->port), I915_READ(dpll_reg));
+}
+
+static void intel_prepare_shared_dpll(struct intel_crtc *crtc)
+{
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc);
+
+       WARN_ON(!pll->refcount);
+       if (pll->active == 0) {
+               DRM_DEBUG_DRIVER("setting up %s\n", pll->name);
+               WARN_ON(pll->on);
+               assert_shared_dpll_disabled(dev_priv, pll);
+
+               pll->mode_set(dev_priv, pll);
+       }
 }
 
 /**
- * ironlake_enable_shared_dpll - enable PCH PLL
+ * intel_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_shared_dpll(struct intel_crtc *crtc)
+static void intel_enable_shared_dpll(struct intel_crtc *crtc)
 {
        struct drm_device *dev = crtc->base.dev;
        struct drm_i915_private *dev_priv = 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(INTEL_INFO(dev)->gen < 5);
        if (WARN_ON(pll == NULL))
                return;
 
@@ -1804,16 +2019,6 @@ static void intel_enable_pipe(struct intel_crtc *crtc)
 
        I915_WRITE(reg, val | PIPECONF_ENABLE);
        POSTING_READ(reg);
-
-       /*
-        * There's no guarantee the pipe will really start running now. It
-        * depends on the Gen, the output type and the relative order between
-        * pipe and plane enabling. Avoid waiting on HSW+ since it's not
-        * necessary.
-        * TODO: audit the previous gens.
-        */
-       if (INTEL_INFO(dev)->gen <= 7 && !IS_HASWELL(dev))
-               intel_wait_for_vblank(dev_priv->dev, pipe);
 }
 
 /**
@@ -1890,18 +2095,17 @@ static void intel_enable_primary_hw_plane(struct drm_i915_private *dev_priv,
        /* If the pipe isn't enabled, we can't pump pixels and may hang */
        assert_pipe_enabled(dev_priv, pipe);
 
-       WARN(intel_crtc->primary_enabled, "Primary plane already enabled\n");
+       if (intel_crtc->primary_enabled)
+               return;
 
        intel_crtc->primary_enabled = true;
 
        reg = DSPCNTR(plane);
        val = I915_READ(reg);
-       if (val & DISPLAY_PLANE_ENABLE)
-               return;
+       WARN_ON(val & DISPLAY_PLANE_ENABLE);
 
        I915_WRITE(reg, val | DISPLAY_PLANE_ENABLE);
        intel_flush_primary_plane(dev_priv, plane);
-       intel_wait_for_vblank(dev_priv->dev, pipe);
 }
 
 /**
@@ -1920,18 +2124,17 @@ static void intel_disable_primary_hw_plane(struct drm_i915_private *dev_priv,
        int reg;
        u32 val;
 
-       WARN(!intel_crtc->primary_enabled, "Primary plane already disabled\n");
+       if (!intel_crtc->primary_enabled)
+               return;
 
        intel_crtc->primary_enabled = false;
 
        reg = DSPCNTR(plane);
        val = I915_READ(reg);
-       if ((val & DISPLAY_PLANE_ENABLE) == 0)
-               return;
+       WARN_ON((val & DISPLAY_PLANE_ENABLE) == 0);
 
        I915_WRITE(reg, val & ~DISPLAY_PLANE_ENABLE);
        intel_flush_primary_plane(dev_priv, plane);
-       intel_wait_for_vblank(dev_priv->dev, pipe);
 }
 
 static bool need_vtd_wa(struct drm_device *dev)
@@ -1954,7 +2157,7 @@ static int intel_align_height(struct drm_device *dev, int height, bool tiled)
 int
 intel_pin_and_fence_fb_obj(struct drm_device *dev,
                           struct drm_i915_gem_object *obj,
-                          struct intel_ring_buffer *pipelined)
+                          struct intel_engine_cs *pipelined)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 alignment;
@@ -2134,7 +2337,7 @@ static void intel_find_plane_obj(struct intel_crtc *intel_crtc,
         * Failed to alloc the obj, check to see if we should share
         * an fb with another CRTC instead
         */
-       list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
+       for_each_crtc(dev, c) {
                i = to_intel_crtc(c);
 
                if (c == &intel_crtc->base)
@@ -2152,9 +2355,9 @@ static void intel_find_plane_obj(struct intel_crtc *intel_crtc,
        }
 }
 
-static int i9xx_update_primary_plane(struct drm_crtc *crtc,
-                                    struct drm_framebuffer *fb,
-                                    int x, int y)
+static void i9xx_update_primary_plane(struct drm_crtc *crtc,
+                                     struct drm_framebuffer *fb,
+                                     int x, int y)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2166,15 +2369,6 @@ static int i9xx_update_primary_plane(struct drm_crtc *crtc,
        u32 dspcntr;
        u32 reg;
 
-       switch (plane) {
-       case 0:
-       case 1:
-               break;
-       default:
-               DRM_ERROR("Can't update plane %c in SAREA\n", plane_name(plane));
-               return -EINVAL;
-       }
-
        intel_fb = to_intel_framebuffer(fb);
        obj = intel_fb->obj;
 
@@ -2249,13 +2443,11 @@ static int i9xx_update_primary_plane(struct drm_crtc *crtc,
        } else
                I915_WRITE(DSPADDR(plane), i915_gem_obj_ggtt_offset(obj) + linear_offset);
        POSTING_READ(reg);
-
-       return 0;
 }
 
-static int ironlake_update_primary_plane(struct drm_crtc *crtc,
-                                        struct drm_framebuffer *fb,
-                                        int x, int y)
+static void ironlake_update_primary_plane(struct drm_crtc *crtc,
+                                         struct drm_framebuffer *fb,
+                                         int x, int y)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2267,16 +2459,6 @@ static int ironlake_update_primary_plane(struct drm_crtc *crtc,
        u32 dspcntr;
        u32 reg;
 
-       switch (plane) {
-       case 0:
-       case 1:
-       case 2:
-               break;
-       default:
-               DRM_ERROR("Can't update plane %c in SAREA\n", plane_name(plane));
-               return -EINVAL;
-       }
-
        intel_fb = to_intel_framebuffer(fb);
        obj = intel_fb->obj;
 
@@ -2343,8 +2525,6 @@ static int ironlake_update_primary_plane(struct drm_crtc *crtc,
                I915_WRITE(DSPLINOFF(plane), linear_offset);
        }
        POSTING_READ(reg);
-
-       return 0;
 }
 
 /* Assume fb object is pinned & idle & fenced and just update base pointers */
@@ -2359,7 +2539,9 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
                dev_priv->display.disable_fbc(dev);
        intel_increase_pllclock(crtc);
 
-       return dev_priv->display.update_primary_plane(crtc, fb, x, y);
+       dev_priv->display.update_primary_plane(crtc, fb, x, y);
+
+       return 0;
 }
 
 void intel_display_handle_reset(struct drm_device *dev)
@@ -2381,7 +2563,7 @@ void intel_display_handle_reset(struct drm_device *dev)
         * pending_flip_queue really got woken up.
         */
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+       for_each_crtc(dev, crtc) {
                struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
                enum plane plane = intel_crtc->plane;
 
@@ -2389,10 +2571,10 @@ void intel_display_handle_reset(struct drm_device *dev)
                intel_finish_page_flip_plane(dev, plane);
        }
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+       for_each_crtc(dev, crtc) {
                struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 
-               mutex_lock(&crtc->mutex);
+               drm_modeset_lock(&crtc->mutex, NULL);
                /*
                 * FIXME: Once we have proper support for primary planes (and
                 * disabling them without disabling the entire crtc) allow again
@@ -2403,7 +2585,7 @@ void intel_display_handle_reset(struct drm_device *dev)
                                                               crtc->primary->fb,
                                                               crtc->x,
                                                               crtc->y);
-               mutex_unlock(&crtc->mutex);
+               drm_modeset_unlock(&crtc->mutex);
        }
 }
 
@@ -2518,14 +2700,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                intel_crtc->config.pipe_src_h = adjusted_mode->crtc_vdisplay;
        }
 
-       ret = dev_priv->display.update_primary_plane(crtc, fb, x, y);
-       if (ret) {
-               mutex_lock(&dev->struct_mutex);
-               intel_unpin_fb_obj(to_intel_framebuffer(fb)->obj);
-               mutex_unlock(&dev->struct_mutex);
-               DRM_ERROR("failed to update base address\n");
-               return ret;
-       }
+       dev_priv->display.update_primary_plane(crtc, fb, x, y);
 
        old_fb = crtc->primary->fb;
        crtc->primary->fb = fb;
@@ -2628,12 +2803,10 @@ static void ironlake_fdi_link_train(struct drm_crtc *crtc)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int pipe = intel_crtc->pipe;
-       int plane = intel_crtc->plane;
        u32 reg, temp, tries;
 
-       /* FDI needs bits from pipe & plane first */
+       /* FDI needs bits from pipe first */
        assert_pipe_enabled(dev_priv, pipe);
-       assert_plane_enabled(dev_priv, plane);
 
        /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit
           for train result */
@@ -3064,9 +3237,8 @@ static void ironlake_fdi_disable(struct drm_crtc *crtc)
        udelay(100);
 
        /* Ironlake workaround, disable clock pointer after downing FDI */
-       if (HAS_PCH_IBX(dev)) {
+       if (HAS_PCH_IBX(dev))
                I915_WRITE(FDI_RX_CHICKEN(pipe), FDI_RX_PHASE_SYNC_POINTER_OVR);
-       }
 
        /* still set train pattern 1 */
        reg = FDI_TX_CTL(pipe);
@@ -3104,7 +3276,7 @@ bool intel_has_pending_fb_unpin(struct drm_device *dev)
         * cannot claim and pin a new fb without at least acquring the
         * struct_mutex and so serialising with us.
         */
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+       for_each_intel_crtc(dev, crtc) {
                if (atomic_read(&crtc->unpin_work_count) == 0)
                        continue;
 
@@ -3117,7 +3289,7 @@ bool intel_has_pending_fb_unpin(struct drm_device *dev)
        return false;
 }
 
-static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc)
+void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3127,8 +3299,9 @@ static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc)
 
        WARN_ON(waitqueue_active(&dev_priv->pending_flip_queue));
 
-       wait_event(dev_priv->pending_flip_queue,
-                  !intel_crtc_has_pending_flip(crtc));
+       WARN_ON(wait_event_timeout(dev_priv->pending_flip_queue,
+                                  !intel_crtc_has_pending_flip(crtc),
+                                  60*HZ) == 0);
 
        mutex_lock(&dev->struct_mutex);
        intel_finish_fb(crtc->primary->fb);
@@ -3341,7 +3514,7 @@ static void ironlake_pch_enable(struct drm_crtc *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);
+       intel_enable_shared_dpll(intel_crtc);
 
        /* set transcoder timing, panel must allow it */
        assert_panel_unlocked(dev_priv, pipe);
@@ -3445,6 +3618,8 @@ static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc)
                DRM_DEBUG_KMS("CRTC:%d using pre-allocated %s\n",
                              crtc->base.base.id, pll->name);
 
+               WARN_ON(pll->refcount);
+
                goto found;
        }
 
@@ -3478,20 +3653,13 @@ static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc)
        return NULL;
 
 found:
+       if (pll->refcount == 0)
+               pll->hw_state = crtc->config.dpll_hw_state;
+
        crtc->config.shared_dpll = i;
        DRM_DEBUG_DRIVER("using %s for pipe %c\n", pll->name,
                         pipe_name(crtc->pipe));
 
-       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);
-
-               pll->mode_set(dev_priv, pll);
-       }
        pll->refcount++;
 
        return pll;
@@ -3562,17 +3730,17 @@ static void intel_disable_planes(struct drm_crtc *crtc)
 
 void hsw_enable_ips(struct intel_crtc *crtc)
 {
-       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = 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. */
+       /* We can only enable IPS after we enable a plane and wait for a vblank */
+       intel_wait_for_vblank(dev, crtc->pipe);
+
        assert_plane_enabled(dev_priv, crtc->plane);
-       if (IS_BROADWELL(crtc->base.dev)) {
+       if (IS_BROADWELL(dev)) {
                mutex_lock(&dev_priv->rps.hw_lock);
                WARN_ON(sandybridge_pcode_write(dev_priv, DISPLAY_IPS_CONTROL, 0xc0000000));
                mutex_unlock(&dev_priv->rps.hw_lock);
@@ -3602,10 +3770,13 @@ void hsw_disable_ips(struct intel_crtc *crtc)
                return;
 
        assert_plane_enabled(dev_priv, crtc->plane);
-       if (IS_BROADWELL(crtc->base.dev)) {
+       if (IS_BROADWELL(dev)) {
                mutex_lock(&dev_priv->rps.hw_lock);
                WARN_ON(sandybridge_pcode_write(dev_priv, DISPLAY_IPS_CONTROL, 0));
                mutex_unlock(&dev_priv->rps.hw_lock);
+               /* wait for pcode to finish disabling IPS, which may take up to 42ms */
+               if (wait_for((I915_READ(IPS_CTL) & IPS_ENABLE) == 0, 42))
+                       DRM_ERROR("Timed out waiting for IPS disable\n");
        } else {
                I915_WRITE(IPS_CTL, 0);
                POSTING_READ(IPS_CTL);
@@ -3662,84 +3833,73 @@ static void intel_crtc_load_lut(struct drm_crtc *crtc)
                hsw_enable_ips(intel_crtc);
 }
 
-static void ironlake_crtc_enable(struct drm_crtc *crtc)
+static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable)
 {
-       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 (!enable && intel_crtc->overlay) {
+               struct drm_device *dev = intel_crtc->base.dev;
+               struct drm_i915_private *dev_priv = dev->dev_private;
 
-       if (intel_crtc->active)
-               return;
+               mutex_lock(&dev->struct_mutex);
+               dev_priv->mm.interruptible = false;
+               (void) intel_overlay_switch_off(intel_crtc->overlay);
+               dev_priv->mm.interruptible = true;
+               mutex_unlock(&dev->struct_mutex);
+       }
 
-       intel_crtc->active = true;
+       /* Let userspace switch the overlay on again. In most cases userspace
+        * has to recompute where to put it anyway.
+        */
+}
 
-       intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
-       intel_set_pch_fifo_underrun_reporting(dev, pipe, true);
+/**
+ * i9xx_fixup_plane - ugly workaround for G45 to fire up the hardware
+ * cursor plane briefly if not already running after enabling the display
+ * plane.
+ * This workaround avoids occasional blank screens when self refresh is
+ * enabled.
+ */
+static void
+g4x_fixup_plane(struct drm_i915_private *dev_priv, enum pipe pipe)
+{
+       u32 cntl = I915_READ(CURCNTR(pipe));
 
-       for_each_encoder_on_crtc(dev, crtc, encoder)
-               if (encoder->pre_enable)
-                       encoder->pre_enable(encoder);
+       if ((cntl & CURSOR_MODE) == 0) {
+               u32 fw_bcl_self = I915_READ(FW_BLC_SELF);
 
-       if (intel_crtc->config.has_pch_encoder) {
-               /* Note: FDI PLL enabling _must_ be done before we enable the
-                * cpu pipes, hence this is separate from all the other fdi/pch
-                * enabling. */
-               ironlake_fdi_pll_enable(intel_crtc);
-       } else {
-               assert_fdi_tx_disabled(dev_priv, pipe);
-               assert_fdi_rx_disabled(dev_priv, pipe);
+               I915_WRITE(FW_BLC_SELF, fw_bcl_self & ~FW_BLC_SELF_EN);
+               I915_WRITE(CURCNTR(pipe), CURSOR_MODE_64_ARGB_AX);
+               intel_wait_for_vblank(dev_priv->dev, pipe);
+               I915_WRITE(CURCNTR(pipe), cntl);
+               I915_WRITE(CURBASE(pipe), I915_READ(CURBASE(pipe)));
+               I915_WRITE(FW_BLC_SELF, fw_bcl_self);
        }
+}
 
-       ironlake_pfit_enable(intel_crtc);
-
-       /*
-        * On ILK+ LUT must be loaded before the pipe is running but with
-        * clocks enabled
-        */
-       intel_crtc_load_lut(crtc);
+static void intel_crtc_enable_planes(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 pipe = intel_crtc->pipe;
+       int plane = intel_crtc->plane;
 
-       intel_update_watermarks(crtc);
-       intel_enable_pipe(intel_crtc);
        intel_enable_primary_hw_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_update_cursor(crtc, true);
+       intel_crtc_dpms_overlay(intel_crtc, true);
 
-       if (intel_crtc->config.has_pch_encoder)
-               ironlake_pch_enable(crtc);
+       hsw_enable_ips(intel_crtc);
 
        mutex_lock(&dev->struct_mutex);
        intel_update_fbc(dev);
+       intel_edp_psr_update(dev);
        mutex_unlock(&dev->struct_mutex);
-
-       for_each_encoder_on_crtc(dev, crtc, encoder)
-               encoder->enable(encoder);
-
-       if (HAS_PCH_CPT(dev))
-               cpt_verify_modeset(dev, intel_crtc->pipe);
-
-       /*
-        * There seems to be a race in PCH platform hw (at least on some
-        * outputs) where an enabled pipe still completes any pageflip right
-        * away (as if the pipe is off) instead of waiting for vblank. As soon
-        * as the first vblank happend, everything works as expected. Hence just
-        * wait for one vblank before returning to avoid strange things
-        * happening.
-        */
-       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 haswell_crtc_enable_planes(struct drm_crtc *crtc)
+static void intel_crtc_disable_planes(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3747,37 +3907,104 @@ static void haswell_crtc_enable_planes(struct drm_crtc *crtc)
        int pipe = intel_crtc->pipe;
        int plane = intel_crtc->plane;
 
-       intel_enable_primary_hw_plane(dev_priv, plane, pipe);
-       intel_enable_planes(crtc);
-       intel_crtc_update_cursor(crtc, true);
+       intel_crtc_wait_for_pending_flips(crtc);
+       drm_crtc_vblank_off(crtc);
 
-       hsw_enable_ips(intel_crtc);
+       if (dev_priv->fbc.plane == plane)
+               intel_disable_fbc(dev);
 
-       mutex_lock(&dev->struct_mutex);
-       intel_update_fbc(dev);
-       mutex_unlock(&dev->struct_mutex);
+       hsw_disable_ips(intel_crtc);
+
+       intel_crtc_dpms_overlay(intel_crtc, false);
+       intel_crtc_update_cursor(crtc, false);
+       intel_disable_planes(crtc);
+       intel_disable_primary_hw_plane(dev_priv, plane, pipe);
 }
 
-static void haswell_crtc_disable_planes(struct drm_crtc *crtc)
+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;
+       enum plane plane = intel_crtc->plane;
 
-       intel_crtc_wait_for_pending_flips(crtc);
-       drm_vblank_off(dev, pipe);
+       WARN_ON(!crtc->enabled);
 
-       /* FBC must be disabled before disabling the plane on HSW. */
-       if (dev_priv->fbc.plane == plane)
-               intel_disable_fbc(dev);
+       if (intel_crtc->active)
+               return;
 
-       hsw_disable_ips(intel_crtc);
+       if (intel_crtc->config.has_pch_encoder)
+               intel_prepare_shared_dpll(intel_crtc);
 
-       intel_crtc_update_cursor(crtc, false);
-       intel_disable_planes(crtc);
-       intel_disable_primary_hw_plane(dev_priv, plane, pipe);
+       if (intel_crtc->config.has_dp_encoder)
+               intel_dp_set_m_n(intel_crtc);
+
+       intel_set_pipe_timings(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);
+
+       /* Set up the display plane register */
+       I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE);
+       POSTING_READ(DSPCNTR(plane));
+
+       dev_priv->display.update_primary_plane(crtc, crtc->primary->fb,
+                                              crtc->x, crtc->y);
+
+       intel_crtc->active = true;
+
+       intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
+       intel_set_pch_fifo_underrun_reporting(dev, pipe, true);
+
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               if (encoder->pre_enable)
+                       encoder->pre_enable(encoder);
+
+       if (intel_crtc->config.has_pch_encoder) {
+               /* Note: FDI PLL enabling _must_ be done before we enable the
+                * cpu pipes, hence this is separate from all the other fdi/pch
+                * enabling. */
+               ironlake_fdi_pll_enable(intel_crtc);
+       } else {
+               assert_fdi_tx_disabled(dev_priv, pipe);
+               assert_fdi_rx_disabled(dev_priv, pipe);
+       }
+
+       ironlake_pfit_enable(intel_crtc);
+
+       /*
+        * On ILK+ LUT must be loaded before the pipe is running but with
+        * clocks enabled
+        */
+       intel_crtc_load_lut(crtc);
+
+       intel_update_watermarks(crtc);
+       intel_enable_pipe(intel_crtc);
+
+       if (intel_crtc->config.has_pch_encoder)
+               ironlake_pch_enable(crtc);
+
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               encoder->enable(encoder);
+
+       if (HAS_PCH_CPT(dev))
+               cpt_verify_modeset(dev, intel_crtc->pipe);
+
+       intel_crtc_enable_planes(crtc);
+
+       drm_crtc_vblank_on(crtc);
+}
+
+/* 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;
 }
 
 /*
@@ -3793,7 +4020,7 @@ static void haswell_mode_set_planes_workaround(struct intel_crtc *crtc)
 
        /* We want to get the other_active_crtc only if there's only 1 other
         * active crtc. */
-       list_for_each_entry(crtc_it, &dev->mode_config.crtc_list, base.head) {
+       for_each_intel_crtc(dev, crtc_it) {
                if (!crtc_it->active || crtc_it == crtc)
                        continue;
 
@@ -3816,12 +4043,34 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct intel_encoder *encoder;
        int pipe = intel_crtc->pipe;
+       enum plane plane = intel_crtc->plane;
 
        WARN_ON(!crtc->enabled);
 
        if (intel_crtc->active)
                return;
 
+       if (intel_crtc->config.has_dp_encoder)
+               intel_dp_set_m_n(intel_crtc);
+
+       intel_set_pipe_timings(intel_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);
+
+       intel_set_pipe_csc(crtc);
+
+       /* Set up the display plane register */
+       I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE | DISPPLANE_PIPE_CSC_ENABLE);
+       POSTING_READ(DSPCNTR(plane));
+
+       dev_priv->display.update_primary_plane(crtc, crtc->primary->fb,
+                                              crtc->x, crtc->y);
+
        intel_crtc->active = true;
 
        intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
@@ -3862,7 +4111,9 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
        /* If we change the relative order between pipe/planes enabling, we need
         * to change the workaround. */
        haswell_mode_set_planes_workaround(intel_crtc);
-       haswell_crtc_enable_planes(crtc);
+       intel_crtc_enable_planes(crtc);
+
+       drm_crtc_vblank_on(crtc);
 }
 
 static void ironlake_pfit_disable(struct intel_crtc *crtc)
@@ -3887,26 +4138,16 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct intel_encoder *encoder;
        int pipe = intel_crtc->pipe;
-       int plane = intel_crtc->plane;
        u32 reg, temp;
 
-
        if (!intel_crtc->active)
                return;
 
+       intel_crtc_disable_planes(crtc);
+
        for_each_encoder_on_crtc(dev, crtc, encoder)
                encoder->disable(encoder);
 
-       intel_crtc_wait_for_pending_flips(crtc);
-       drm_vblank_off(dev, pipe);
-
-       if (dev_priv->fbc.plane == plane)
-               intel_disable_fbc(dev);
-
-       intel_crtc_update_cursor(crtc, false);
-       intel_disable_planes(crtc);
-       intel_disable_primary_hw_plane(dev_priv, plane, pipe);
-
        if (intel_crtc->config.has_pch_encoder)
                intel_set_pch_fifo_underrun_reporting(dev, pipe, false);
 
@@ -3950,6 +4191,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
 
        mutex_lock(&dev->struct_mutex);
        intel_update_fbc(dev);
+       intel_edp_psr_update(dev);
        mutex_unlock(&dev->struct_mutex);
 }
 
@@ -3965,7 +4207,7 @@ static void haswell_crtc_disable(struct drm_crtc *crtc)
        if (!intel_crtc->active)
                return;
 
-       haswell_crtc_disable_planes(crtc);
+       intel_crtc_disable_planes(crtc);
 
        for_each_encoder_on_crtc(dev, crtc, encoder) {
                intel_opregion_notify_encoder(encoder, false);
@@ -3997,6 +4239,7 @@ static void haswell_crtc_disable(struct drm_crtc *crtc)
 
        mutex_lock(&dev->struct_mutex);
        intel_update_fbc(dev);
+       intel_edp_psr_update(dev);
        mutex_unlock(&dev->struct_mutex);
 }
 
@@ -4011,48 +4254,6 @@ static void haswell_crtc_off(struct drm_crtc *crtc)
        intel_ddi_put_crtc_pll(crtc);
 }
 
-static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable)
-{
-       if (!enable && intel_crtc->overlay) {
-               struct drm_device *dev = intel_crtc->base.dev;
-               struct drm_i915_private *dev_priv = dev->dev_private;
-
-               mutex_lock(&dev->struct_mutex);
-               dev_priv->mm.interruptible = false;
-               (void) intel_overlay_switch_off(intel_crtc->overlay);
-               dev_priv->mm.interruptible = true;
-               mutex_unlock(&dev->struct_mutex);
-       }
-
-       /* Let userspace switch the overlay on again. In most cases userspace
-        * has to recompute where to put it anyway.
-        */
-}
-
-/**
- * i9xx_fixup_plane - ugly workaround for G45 to fire up the hardware
- * cursor plane briefly if not already running after enabling the display
- * plane.
- * This workaround avoids occasional blank screens when self refresh is
- * enabled.
- */
-static void
-g4x_fixup_plane(struct drm_i915_private *dev_priv, enum pipe pipe)
-{
-       u32 cntl = I915_READ(CURCNTR(pipe));
-
-       if ((cntl & CURSOR_MODE) == 0) {
-               u32 fw_bcl_self = I915_READ(FW_BLC_SELF);
-
-               I915_WRITE(FW_BLC_SELF, fw_bcl_self & ~FW_BLC_SELF_EN);
-               I915_WRITE(CURCNTR(pipe), CURSOR_MODE_64_ARGB_AX);
-               intel_wait_for_vblank(dev_priv->dev, pipe);
-               I915_WRITE(CURCNTR(pipe), cntl);
-               I915_WRITE(CURBASE(pipe), I915_READ(CURBASE(pipe)));
-               I915_WRITE(FW_BLC_SELF, fw_bcl_self);
-       }
-}
-
 static void i9xx_pfit_enable(struct intel_crtc *crtc)
 {
        struct drm_device *dev = crtc->base.dev;
@@ -4164,7 +4365,7 @@ static void modeset_update_crtc_power_domains(struct drm_device *dev)
         * First get all needed power domains, then put all unneeded, to avoid
         * any unnecessary toggling of the power wells.
         */
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+       for_each_intel_crtc(dev, crtc) {
                enum intel_display_power_domain domain;
 
                if (!crtc->base.enabled)
@@ -4176,7 +4377,7 @@ static void modeset_update_crtc_power_domains(struct drm_device *dev)
                        intel_display_power_get(dev_priv, domain);
        }
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+       for_each_intel_crtc(dev, crtc) {
                enum intel_display_power_domain domain;
 
                for_each_power_domain(domain, crtc->enabled_power_domains)
@@ -4207,6 +4408,9 @@ static void valleyview_set_cdclk(struct drm_device *dev, int cdclk)
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 val, cmd;
 
+       WARN_ON(valleyview_cur_cdclk(dev_priv) != dev_priv->vlv_cdclk_freq);
+       dev_priv->vlv_cdclk_freq = cdclk;
+
        if (cdclk >= 320) /* jump to highest voltage for 400MHz too */
                cmd = 2;
        else if (cdclk == 266)
@@ -4261,7 +4465,7 @@ static void valleyview_set_cdclk(struct drm_device *dev, int cdclk)
        intel_i2c_reset(dev);
 }
 
-static int valleyview_cur_cdclk(struct drm_i915_private *dev_priv)
+int valleyview_cur_cdclk(struct drm_i915_private *dev_priv)
 {
        int cur_cdclk, vco;
        int divider;
@@ -4282,10 +4486,6 @@ static int valleyview_cur_cdclk(struct drm_i915_private *dev_priv)
 static int valleyview_calc_cdclk(struct drm_i915_private *dev_priv,
                                 int max_pixclk)
 {
-       int cur_cdclk;
-
-       cur_cdclk = valleyview_cur_cdclk(dev_priv);
-
        /*
         * Really only a few cases to deal with, as only 4 CDclks are supported:
         *   200MHz
@@ -4311,8 +4511,7 @@ static int intel_mode_max_pixclk(struct drm_i915_private *dev_priv)
        struct intel_crtc *intel_crtc;
        int max_pixclk = 0;
 
-       list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list,
-                           base.head) {
+       for_each_intel_crtc(dev, intel_crtc) {
                if (intel_crtc->new_enabled)
                        max_pixclk = max(max_pixclk,
                                         intel_crtc->new_config->adjusted_mode.crtc_clock);
@@ -4327,14 +4526,13 @@ static void valleyview_modeset_global_pipes(struct drm_device *dev,
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc;
        int max_pixclk = intel_mode_max_pixclk(dev_priv);
-       int cur_cdclk = valleyview_cur_cdclk(dev_priv);
 
-       if (valleyview_calc_cdclk(dev_priv, max_pixclk) == cur_cdclk)
+       if (valleyview_calc_cdclk(dev_priv, max_pixclk) ==
+           dev_priv->vlv_cdclk_freq)
                return;
 
        /* disable/enable all currently active pipes while we change cdclk */
-       list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list,
-                           base.head)
+       for_each_intel_crtc(dev, intel_crtc)
                if (intel_crtc->base.enabled)
                        *prepare_pipes |= (1 << intel_crtc->pipe);
 }
@@ -4343,10 +4541,9 @@ static void valleyview_modeset_global_resources(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        int max_pixclk = intel_mode_max_pixclk(dev_priv);
-       int cur_cdclk = valleyview_cur_cdclk(dev_priv);
        int req_cdclk = valleyview_calc_cdclk(dev_priv, max_pixclk);
 
-       if (req_cdclk != cur_cdclk)
+       if (req_cdclk != dev_priv->vlv_cdclk_freq)
                valleyview_set_cdclk(dev, req_cdclk);
        modeset_update_crtc_power_domains(dev);
 }
@@ -4360,22 +4557,55 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
        int pipe = intel_crtc->pipe;
        int plane = intel_crtc->plane;
        bool is_dsi;
+       u32 dspcntr;
 
        WARN_ON(!crtc->enabled);
 
        if (intel_crtc->active)
                return;
 
+       vlv_prepare_pll(intel_crtc);
+
+       /* Set up the display plane register */
+       dspcntr = DISPPLANE_GAMMA_ENABLE;
+
+       if (intel_crtc->config.has_dp_encoder)
+               intel_dp_set_m_n(intel_crtc);
+
+       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.
+        */
+       I915_WRITE(DSPSIZE(plane),
+                  ((intel_crtc->config.pipe_src_h - 1) << 16) |
+                  (intel_crtc->config.pipe_src_w - 1));
+       I915_WRITE(DSPPOS(plane), 0);
+
+       i9xx_set_pipeconf(intel_crtc);
+
+       I915_WRITE(DSPCNTR(plane), dspcntr);
+       POSTING_READ(DSPCNTR(plane));
+
+       dev_priv->display.update_primary_plane(crtc, crtc->primary->fb,
+                                              crtc->x, crtc->y);
+
        intel_crtc->active = true;
 
+       intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
+
        for_each_encoder_on_crtc(dev, crtc, encoder)
                if (encoder->pre_pll_enable)
                        encoder->pre_pll_enable(encoder);
 
        is_dsi = intel_pipe_has_type(crtc, INTEL_OUTPUT_DSI);
 
-       if (!is_dsi)
-               vlv_enable_pll(intel_crtc);
+       if (!is_dsi) {
+               if (IS_CHERRYVIEW(dev))
+                       chv_enable_pll(intel_crtc);
+               else
+                       vlv_enable_pll(intel_crtc);
+       }
 
        for_each_encoder_on_crtc(dev, crtc, encoder)
                if (encoder->pre_enable)
@@ -4387,15 +4617,25 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
 
        intel_update_watermarks(crtc);
        intel_enable_pipe(intel_crtc);
-       intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
-       intel_enable_primary_hw_plane(dev_priv, plane, pipe);
-       intel_enable_planes(crtc);
-       intel_crtc_update_cursor(crtc, true);
-
-       intel_update_fbc(dev);
 
        for_each_encoder_on_crtc(dev, crtc, encoder)
                encoder->enable(encoder);
+
+       intel_crtc_enable_planes(crtc);
+
+       drm_crtc_vblank_on(crtc);
+
+       /* Underruns don't raise interrupts, so check manually. */
+       i9xx_check_fifo_underruns(dev);
+}
+
+static void i9xx_set_pll_dividers(struct intel_crtc *crtc)
+{
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       I915_WRITE(FP0(crtc->pipe), crtc->config.dpll_hw_state.fp0);
+       I915_WRITE(FP1(crtc->pipe), crtc->config.dpll_hw_state.fp1);
 }
 
 static void i9xx_crtc_enable(struct drm_crtc *crtc)
@@ -4406,14 +4646,49 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
        struct intel_encoder *encoder;
        int pipe = intel_crtc->pipe;
        int plane = intel_crtc->plane;
+       u32 dspcntr;
 
        WARN_ON(!crtc->enabled);
 
        if (intel_crtc->active)
                return;
 
+       i9xx_set_pll_dividers(intel_crtc);
+
+       /* Set up the display plane register */
+       dspcntr = DISPPLANE_GAMMA_ENABLE;
+
+       if (pipe == 0)
+               dspcntr &= ~DISPPLANE_SEL_PIPE_MASK;
+       else
+               dspcntr |= DISPPLANE_SEL_PIPE_B;
+
+       if (intel_crtc->config.has_dp_encoder)
+               intel_dp_set_m_n(intel_crtc);
+
+       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.
+        */
+       I915_WRITE(DSPSIZE(plane),
+                  ((intel_crtc->config.pipe_src_h - 1) << 16) |
+                  (intel_crtc->config.pipe_src_w - 1));
+       I915_WRITE(DSPPOS(plane), 0);
+
+       i9xx_set_pipeconf(intel_crtc);
+
+       I915_WRITE(DSPCNTR(plane), dspcntr);
+       POSTING_READ(DSPCNTR(plane));
+
+       dev_priv->display.update_primary_plane(crtc, crtc->primary->fb,
+                                              crtc->x, crtc->y);
+
        intel_crtc->active = true;
 
+       if (!IS_GEN2(dev))
+               intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
+
        for_each_encoder_on_crtc(dev, crtc, encoder)
                if (encoder->pre_enable)
                        encoder->pre_enable(encoder);
@@ -4426,21 +4701,26 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
 
        intel_update_watermarks(crtc);
        intel_enable_pipe(intel_crtc);
-       intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
-       intel_enable_primary_hw_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_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_update_fbc(dev);
 
        for_each_encoder_on_crtc(dev, crtc, encoder)
                encoder->enable(encoder);
+
+       intel_crtc_enable_planes(crtc);
+
+       /*
+        * Gen2 reports pipe underruns whenever all planes are disabled.
+        * So don't enable underrun reporting before at least some planes
+        * are enabled.
+        * FIXME: Need to fix the logic to work when we turn off all planes
+        * but leave the pipe running.
+        */
+       if (IS_GEN2(dev))
+               intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
+
+       drm_crtc_vblank_on(crtc);
+
+       /* Underruns don't raise interrupts, so check manually. */
+       i9xx_check_fifo_underruns(dev);
 }
 
 static void i9xx_pfit_disable(struct intel_crtc *crtc)
@@ -4465,27 +4745,31 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct intel_encoder *encoder;
        int pipe = intel_crtc->pipe;
-       int plane = intel_crtc->plane;
 
        if (!intel_crtc->active)
                return;
 
-       for_each_encoder_on_crtc(dev, crtc, encoder)
-               encoder->disable(encoder);
+       /*
+        * Gen2 reports pipe underruns whenever all planes are disabled.
+        * So diasble underrun reporting before all the planes get disabled.
+        * FIXME: Need to fix the logic to work when we turn off all planes
+        * but leave the pipe running.
+        */
+       if (IS_GEN2(dev))
+               intel_set_cpu_fifo_underrun_reporting(dev, pipe, false);
 
-       /* 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_disable_planes(crtc);
 
-       if (dev_priv->fbc.plane == plane)
-               intel_disable_fbc(dev);
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               encoder->disable(encoder);
 
-       intel_crtc_dpms_overlay(intel_crtc, false);
-       intel_crtc_update_cursor(crtc, false);
-       intel_disable_planes(crtc);
-       intel_disable_primary_hw_plane(dev_priv, plane, pipe);
+       /*
+        * On gen2 planes are double buffered but the pipe isn't, so we must
+        * wait for planes to fully turn off before disabling the pipe.
+        */
+       if (IS_GEN2(dev))
+               intel_wait_for_vblank(dev, pipe);
 
-       intel_set_cpu_fifo_underrun_reporting(dev, pipe, false);
        intel_disable_pipe(dev_priv, pipe);
 
        i9xx_pfit_disable(intel_crtc);
@@ -4494,15 +4778,25 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
                if (encoder->post_disable)
                        encoder->post_disable(encoder);
 
-       if (IS_VALLEYVIEW(dev) && !intel_pipe_has_type(crtc, INTEL_OUTPUT_DSI))
-               vlv_disable_pll(dev_priv, pipe);
-       else if (!IS_VALLEYVIEW(dev))
-               i9xx_disable_pll(dev_priv, pipe);
+       if (!intel_pipe_has_type(crtc, INTEL_OUTPUT_DSI)) {
+               if (IS_CHERRYVIEW(dev))
+                       chv_disable_pll(dev_priv, pipe);
+               else if (IS_VALLEYVIEW(dev))
+                       vlv_disable_pll(dev_priv, pipe);
+               else
+                       i9xx_disable_pll(dev_priv, pipe);
+       }
+
+       if (!IS_GEN2(dev))
+               intel_set_cpu_fifo_underrun_reporting(dev, pipe, false);
 
        intel_crtc->active = false;
        intel_update_watermarks(crtc);
 
+       mutex_lock(&dev->struct_mutex);
        intel_update_fbc(dev);
+       intel_edp_psr_update(dev);
+       mutex_unlock(&dev->struct_mutex);
 }
 
 static void i9xx_crtc_off(struct drm_crtc *crtc)
@@ -4565,13 +4859,11 @@ static void intel_crtc_disable(struct drm_crtc *crtc)
        struct drm_device *dev = crtc->dev;
        struct drm_connector *connector;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 
        /* crtc should still be enabled when we disable it. */
        WARN_ON(!crtc->enabled);
 
        dev_priv->display.crtc_disable(crtc);
-       intel_crtc->eld_vld = false;
        intel_crtc_update_sarea(crtc, false);
        dev_priv->display.off(crtc);
 
@@ -4635,7 +4927,7 @@ static void intel_connector_check_state(struct intel_connector *connector)
 
                DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
                              connector->base.base.id,
-                             drm_get_connector_name(&connector->base));
+                             connector->base.name);
 
                WARN(connector->base.dpms == DRM_MODE_DPMS_OFF,
                     "wrong connector dpms state\n");
@@ -5039,8 +5331,6 @@ static void i9xx_update_pll_dividers(struct intel_crtc *crtc,
                                     intel_clock_t *reduced_clock)
 {
        struct drm_device *dev = crtc->base.dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       int pipe = crtc->pipe;
        u32 fp, fp2 = 0;
 
        if (IS_PINEVIEW(dev)) {
@@ -5053,17 +5343,14 @@ static void i9xx_update_pll_dividers(struct intel_crtc *crtc,
                        fp2 = i9xx_dpll_compute_fp(reduced_clock);
        }
 
-       I915_WRITE(FP0(pipe), fp);
        crtc->config.dpll_hw_state.fp0 = fp;
 
        crtc->lowfreq_avail = false;
        if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS) &&
            reduced_clock && i915.powersave) {
-               I915_WRITE(FP1(pipe), fp2);
                crtc->config.dpll_hw_state.fp1 = fp2;
                crtc->lowfreq_avail = true;
        } else {
-               I915_WRITE(FP1(pipe), fp);
                crtc->config.dpll_hw_state.fp1 = fp;
        }
 }
@@ -5140,13 +5427,35 @@ static void intel_dp_set_m_n(struct intel_crtc *crtc)
 }
 
 static void vlv_update_pll(struct intel_crtc *crtc)
+{
+       u32 dpll, dpll_md;
+
+       /*
+        * Enable DPIO clock input. We should never disable the reference
+        * clock for pipe B, since VGA hotplug / manual detection depends
+        * on it.
+        */
+       dpll = DPLL_EXT_BUFFER_ENABLE_VLV | DPLL_REFA_CLK_ENABLE_VLV |
+               DPLL_VGA_MODE_DIS | DPLL_INTEGRATED_CLOCK_VLV;
+       /* We should never disable this, set it here for state tracking */
+       if (crtc->pipe == PIPE_B)
+               dpll |= DPLL_INTEGRATED_CRI_CLK_VLV;
+       dpll |= DPLL_VCO_ENABLE;
+       crtc->config.dpll_hw_state.dpll = dpll;
+
+       dpll_md = (crtc->config.pixel_multiplier - 1)
+               << DPLL_MD_UDI_MULTIPLIER_SHIFT;
+       crtc->config.dpll_hw_state.dpll_md = dpll_md;
+}
+
+static void vlv_prepare_pll(struct intel_crtc *crtc)
 {
        struct drm_device *dev = crtc->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        int pipe = crtc->pipe;
-       u32 dpll, mdiv;
+       u32 mdiv;
        u32 bestn, bestm1, bestm2, bestp1, bestp2;
-       u32 coreclk, reg_val, dpll_md;
+       u32 coreclk, reg_val;
 
        mutex_lock(&dev_priv->dpio_lock);
 
@@ -5159,7 +5468,7 @@ static void vlv_update_pll(struct intel_crtc *crtc)
        /* See eDP HDMI DPIO driver vbios notes doc */
 
        /* PLL B needs special handling */
-       if (pipe)
+       if (pipe == PIPE_B)
                vlv_pllb_recal_opamp(dev_priv, pipe);
 
        /* Set up Tx target for periodic Rcomp update */
@@ -5203,7 +5512,7 @@ static void vlv_update_pll(struct intel_crtc *crtc)
        if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP) ||
            intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT)) {
                /* Use SSC source */
-               if (!pipe)
+               if (pipe == PIPE_A)
                        vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW5(pipe),
                                         0x0df40000);
                else
@@ -5211,7 +5520,7 @@ static void vlv_update_pll(struct intel_crtc *crtc)
                                         0x0df70000);
        } else { /* HDMI or VGA */
                /* Use bend source */
-               if (!pipe)
+               if (pipe == PIPE_A)
                        vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW5(pipe),
                                         0x0df70000);
                else
@@ -5227,26 +5536,84 @@ static void vlv_update_pll(struct intel_crtc *crtc)
        vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW7(pipe), coreclk);
 
        vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW11(pipe), 0x87871000);
+       mutex_unlock(&dev_priv->dpio_lock);
+}
+
+static void chv_update_pll(struct intel_crtc *crtc)
+{
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int pipe = crtc->pipe;
+       int dpll_reg = DPLL(crtc->pipe);
+       enum dpio_channel port = vlv_pipe_to_channel(pipe);
+       u32 loopfilter, intcoeff;
+       u32 bestn, bestm1, bestm2, bestp1, bestp2, bestm2_frac;
+       int refclk;
+
+       crtc->config.dpll_hw_state.dpll = DPLL_SSC_REF_CLOCK_CHV |
+               DPLL_REFA_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS |
+               DPLL_VCO_ENABLE;
+       if (pipe != PIPE_A)
+               crtc->config.dpll_hw_state.dpll |= DPLL_INTEGRATED_CRI_CLK_VLV;
+
+       crtc->config.dpll_hw_state.dpll_md =
+               (crtc->config.pixel_multiplier - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT;
+
+       bestn = crtc->config.dpll.n;
+       bestm2_frac = crtc->config.dpll.m2 & 0x3fffff;
+       bestm1 = crtc->config.dpll.m1;
+       bestm2 = crtc->config.dpll.m2 >> 22;
+       bestp1 = crtc->config.dpll.p1;
+       bestp2 = crtc->config.dpll.p2;
 
        /*
-        * Enable DPIO clock input. We should never disable the reference
-        * clock for pipe B, since VGA hotplug / manual detection depends
-        * on it.
+        * Enable Refclk and SSC
         */
-       dpll = DPLL_EXT_BUFFER_ENABLE_VLV | DPLL_REFA_CLK_ENABLE_VLV |
-               DPLL_VGA_MODE_DIS | DPLL_INTEGRATED_CLOCK_VLV;
-       /* We should never disable this, set it here for state tracking */
-       if (pipe == PIPE_B)
-               dpll |= DPLL_INTEGRATED_CRI_CLK_VLV;
-       dpll |= DPLL_VCO_ENABLE;
-       crtc->config.dpll_hw_state.dpll = dpll;
+       I915_WRITE(dpll_reg,
+                  crtc->config.dpll_hw_state.dpll & ~DPLL_VCO_ENABLE);
 
-       dpll_md = (crtc->config.pixel_multiplier - 1)
-               << DPLL_MD_UDI_MULTIPLIER_SHIFT;
-       crtc->config.dpll_hw_state.dpll_md = dpll_md;
+       mutex_lock(&dev_priv->dpio_lock);
+
+       /* p1 and p2 divider */
+       vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW13(port),
+                       5 << DPIO_CHV_S1_DIV_SHIFT |
+                       bestp1 << DPIO_CHV_P1_DIV_SHIFT |
+                       bestp2 << DPIO_CHV_P2_DIV_SHIFT |
+                       1 << DPIO_CHV_K_DIV_SHIFT);
+
+       /* Feedback post-divider - m2 */
+       vlv_dpio_write(dev_priv, pipe, CHV_PLL_DW0(port), bestm2);
+
+       /* Feedback refclk divider - n and m1 */
+       vlv_dpio_write(dev_priv, pipe, CHV_PLL_DW1(port),
+                       DPIO_CHV_M1_DIV_BY_2 |
+                       1 << DPIO_CHV_N_DIV_SHIFT);
+
+       /* M2 fraction division */
+       vlv_dpio_write(dev_priv, pipe, CHV_PLL_DW2(port), bestm2_frac);
+
+       /* M2 fraction division enable */
+       vlv_dpio_write(dev_priv, pipe, CHV_PLL_DW3(port),
+                      DPIO_CHV_FRAC_DIV_EN |
+                      (2 << DPIO_CHV_FEEDFWD_GAIN_SHIFT));
+
+       /* Loop filter */
+       refclk = i9xx_get_refclk(&crtc->base, 0);
+       loopfilter = 5 << DPIO_CHV_PROP_COEFF_SHIFT |
+               2 << DPIO_CHV_GAIN_CTRL_SHIFT;
+       if (refclk == 100000)
+               intcoeff = 11;
+       else if (refclk == 38400)
+               intcoeff = 10;
+       else
+               intcoeff = 9;
+       loopfilter |= intcoeff << DPIO_CHV_INT_COEFF_SHIFT;
+       vlv_dpio_write(dev_priv, pipe, CHV_PLL_DW6(port), loopfilter);
 
-       if (crtc->config.has_dp_encoder)
-               intel_dp_set_m_n(crtc);
+       /* AFC Recal */
+       vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW14(port),
+                       vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW14(port)) |
+                       DPIO_AFC_RECAL);
 
        mutex_unlock(&dev_priv->dpio_lock);
 }
@@ -5325,9 +5692,6 @@ static void i9xx_update_pll(struct intel_crtc *crtc,
                        << DPLL_MD_UDI_MULTIPLIER_SHIFT;
                crtc->config.dpll_hw_state.dpll_md = dpll_md;
        }
-
-       if (crtc->config.has_dp_encoder)
-               intel_dp_set_m_n(crtc);
 }
 
 static void i8xx_update_pll(struct intel_crtc *crtc,
@@ -5567,16 +5931,12 @@ 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);
-       int pipe = intel_crtc->pipe;
-       int plane = intel_crtc->plane;
        int refclk, num_connectors = 0;
        intel_clock_t clock, reduced_clock;
-       u32 dspcntr;
        bool ok, has_reduced_clock = false;
        bool is_lvds = false, is_dsi = false;
        struct intel_encoder *encoder;
        const intel_limit_t *limit;
-       int ret;
 
        for_each_encoder_on_crtc(dev, crtc, encoder) {
                switch (encoder->type) {
@@ -5592,7 +5952,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
        }
 
        if (is_dsi)
-               goto skip_dpll;
+               return 0;
 
        if (!intel_crtc->config.clock_set) {
                refclk = i9xx_get_refclk(crtc, num_connectors);
@@ -5637,43 +5997,17 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
                i8xx_update_pll(intel_crtc,
                                has_reduced_clock ? &reduced_clock : NULL,
                                num_connectors);
+       } else if (IS_CHERRYVIEW(dev)) {
+               chv_update_pll(intel_crtc);
        } else if (IS_VALLEYVIEW(dev)) {
                vlv_update_pll(intel_crtc);
        } else {
                i9xx_update_pll(intel_crtc,
                                has_reduced_clock ? &reduced_clock : NULL,
-                                num_connectors);
-       }
-
-skip_dpll:
-       /* Set up the display plane register */
-       dspcntr = DISPPLANE_GAMMA_ENABLE;
-
-       if (!IS_VALLEYVIEW(dev)) {
-               if (pipe == 0)
-                       dspcntr &= ~DISPPLANE_SEL_PIPE_MASK;
-               else
-                       dspcntr |= DISPPLANE_SEL_PIPE_B;
+                               num_connectors);
        }
 
-       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.
-        */
-       I915_WRITE(DSPSIZE(plane),
-                  ((intel_crtc->config.pipe_src_h - 1) << 16) |
-                  (intel_crtc->config.pipe_src_w - 1));
-       I915_WRITE(DSPPOS(plane), 0);
-
-       i9xx_set_pipeconf(intel_crtc);
-
-       I915_WRITE(DSPCNTR(plane), dspcntr);
-       POSTING_READ(DSPCNTR(plane));
-
-       ret = intel_pipe_set_base(crtc, x, y, fb);
-
-       return ret;
+       return 0;
 }
 
 static void i9xx_get_pfit_config(struct intel_crtc *crtc,
@@ -5793,6 +6127,36 @@ static void i9xx_get_plane_config(struct intel_crtc *crtc,
 
 }
 
+static void chv_crtc_clock_get(struct intel_crtc *crtc,
+                              struct intel_crtc_config *pipe_config)
+{
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int pipe = pipe_config->cpu_transcoder;
+       enum dpio_channel port = vlv_pipe_to_channel(pipe);
+       intel_clock_t clock;
+       u32 cmn_dw13, pll_dw0, pll_dw1, pll_dw2;
+       int refclk = 100000;
+
+       mutex_lock(&dev_priv->dpio_lock);
+       cmn_dw13 = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW13(port));
+       pll_dw0 = vlv_dpio_read(dev_priv, pipe, CHV_PLL_DW0(port));
+       pll_dw1 = vlv_dpio_read(dev_priv, pipe, CHV_PLL_DW1(port));
+       pll_dw2 = vlv_dpio_read(dev_priv, pipe, CHV_PLL_DW2(port));
+       mutex_unlock(&dev_priv->dpio_lock);
+
+       clock.m1 = (pll_dw1 & 0x7) == DPIO_CHV_M1_DIV_BY_2 ? 2 : 0;
+       clock.m2 = ((pll_dw0 & 0xff) << 22) | (pll_dw2 & 0x3fffff);
+       clock.n = (pll_dw1 >> DPIO_CHV_N_DIV_SHIFT) & 0xf;
+       clock.p1 = (cmn_dw13 >> DPIO_CHV_P1_DIV_SHIFT) & 0x7;
+       clock.p2 = (cmn_dw13 >> DPIO_CHV_P2_DIV_SHIFT) & 0x1f;
+
+       chv_clock(refclk, &clock);
+
+       /* clock.dot is the fast clock */
+       pipe_config->port_clock = clock.dot / 5;
+}
+
 static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
                                 struct intel_crtc_config *pipe_config)
 {
@@ -5827,6 +6191,9 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
                }
        }
 
+       if (IS_VALLEYVIEW(dev) && (tmp & PIPECONF_COLOR_RANGE_SELECT))
+               pipe_config->limited_color_range = true;
+
        if (INTEL_INFO(dev)->gen < 4)
                pipe_config->double_wide = tmp & PIPECONF_DOUBLE_WIDE;
 
@@ -5862,7 +6229,9 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
                                                     DPLL_PORTB_READY_MASK);
        }
 
-       if (IS_VALLEYVIEW(dev))
+       if (IS_CHERRYVIEW(dev))
+               chv_crtc_clock_get(crtc, pipe_config);
+       else if (IS_VALLEYVIEW(dev))
                vlv_crtc_clock_get(crtc, pipe_config);
        else
                i9xx_crtc_clock_get(crtc, pipe_config);
@@ -5983,8 +6352,7 @@ static void ironlake_init_pch_refclk(struct drm_device *dev)
                        if (intel_panel_use_ssc(dev_priv) && can_ssc) {
                                DRM_DEBUG_KMS("Using SSC on eDP\n");
                                val |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
-                       }
-                       else
+                       } else
                                val |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
                } else
                        val |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
@@ -6563,10 +6931,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
                                  struct drm_framebuffer *fb)
 {
        struct drm_device *dev = crtc->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       int pipe = intel_crtc->pipe;
-       int plane = intel_crtc->plane;
        int num_connectors = 0;
        intel_clock_t clock, reduced_clock;
        u32 dpll = 0, fp = 0, fp2 = 0;
@@ -6574,7 +6939,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
        bool is_lvds = false;
        struct intel_encoder *encoder;
        struct intel_shared_dpll *pll;
-       int ret;
 
        for_each_encoder_on_crtc(dev, crtc, encoder) {
                switch (encoder->type) {
@@ -6624,36 +6988,18 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
                pll = intel_get_shared_dpll(intel_crtc);
                if (pll == NULL) {
                        DRM_DEBUG_DRIVER("failed to find PLL for pipe %c\n",
-                                        pipe_name(pipe));
+                                        pipe_name(intel_crtc->pipe));
                        return -EINVAL;
                }
        } else
                intel_put_shared_dpll(intel_crtc);
 
-       if (intel_crtc->config.has_dp_encoder)
-               intel_dp_set_m_n(intel_crtc);
-
        if (is_lvds && has_reduced_clock && i915.powersave)
                intel_crtc->lowfreq_avail = true;
        else
                intel_crtc->lowfreq_avail = false;
 
-       intel_set_pipe_timings(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);
-
-       /* Set up the display plane register */
-       I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE);
-       POSTING_READ(DSPCNTR(plane));
-
-       ret = intel_pipe_set_base(crtc, x, y, fb);
-
-       return ret;
+       return 0;
 }
 
 static void intel_pch_transcoder_get_m_n(struct intel_crtc *crtc,
@@ -6831,6 +7177,9 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc,
                break;
        }
 
+       if (tmp & PIPECONF_COLOR_RANGE_SELECT)
+               pipe_config->limited_color_range = true;
+
        if (I915_READ(PCH_TRANSCONF(crtc->pipe)) & TRANS_ENABLE) {
                struct intel_shared_dpll *pll;
 
@@ -6880,10 +7229,8 @@ static void assert_can_disable_lcpll(struct drm_i915_private *dev_priv)
        struct drm_device *dev = dev_priv->dev;
        struct intel_ddi_plls *plls = &dev_priv->ddi_plls;
        struct intel_crtc *crtc;
-       unsigned long irqflags;
-       uint32_t val;
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head)
+       for_each_intel_crtc(dev, crtc)
                WARN(crtc->active, "CRTC for pipe %c enabled\n",
                     pipe_name(crtc->pipe));
 
@@ -6902,14 +7249,29 @@ static void assert_can_disable_lcpll(struct drm_i915_private *dev_priv)
             "Utility pin enabled\n");
        WARN(I915_READ(PCH_GTC_CTL) & PCH_GTC_ENABLE, "PCH GTC enabled\n");
 
-       spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
-       val = I915_READ(DEIMR);
-       WARN((val | DE_PCH_EVENT_IVB) != 0xffffffff,
-            "Unexpected DEIMR bits enabled: 0x%x\n", val);
-       val = I915_READ(SDEIMR);
-       WARN((val | SDE_HOTPLUG_MASK_CPT) != 0xffffffff,
-            "Unexpected SDEIMR bits enabled: 0x%x\n", val);
-       spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+       /*
+        * In theory we can still leave IRQs enabled, as long as only the HPD
+        * interrupts remain enabled. We used to check for that, but since it's
+        * gen-specific and since we only disable LCPLL after we fully disable
+        * the interrupts, the check below should be enough.
+        */
+       WARN(!dev_priv->pm.irqs_disabled, "IRQs enabled\n");
+}
+
+static void hsw_write_dcomp(struct drm_i915_private *dev_priv, uint32_t val)
+{
+       struct drm_device *dev = dev_priv->dev;
+
+       if (IS_HASWELL(dev)) {
+               mutex_lock(&dev_priv->rps.hw_lock);
+               if (sandybridge_pcode_write(dev_priv, GEN6_PCODE_WRITE_D_COMP,
+                                           val))
+                       DRM_ERROR("Failed to disable D_COMP\n");
+               mutex_unlock(&dev_priv->rps.hw_lock);
+       } else {
+               I915_WRITE(D_COMP, val);
+       }
+       POSTING_READ(D_COMP);
 }
 
 /*
@@ -6949,11 +7311,7 @@ static void hsw_disable_lcpll(struct drm_i915_private *dev_priv,
 
        val = I915_READ(D_COMP);
        val |= D_COMP_COMP_DISABLE;
-       mutex_lock(&dev_priv->rps.hw_lock);
-       if (sandybridge_pcode_write(dev_priv, GEN6_PCODE_WRITE_D_COMP, val))
-               DRM_ERROR("Failed to disable D_COMP\n");
-       mutex_unlock(&dev_priv->rps.hw_lock);
-       POSTING_READ(D_COMP);
+       hsw_write_dcomp(dev_priv, val);
        ndelay(100);
 
        if (wait_for((I915_READ(D_COMP) & D_COMP_RCOMP_IN_PROGRESS) == 0, 1))
@@ -7008,11 +7366,7 @@ static void hsw_restore_lcpll(struct drm_i915_private *dev_priv)
        val = I915_READ(D_COMP);
        val |= D_COMP_COMP_FORCE;
        val &= ~D_COMP_COMP_DISABLE;
-       mutex_lock(&dev_priv->rps.hw_lock);
-       if (sandybridge_pcode_write(dev_priv, GEN6_PCODE_WRITE_D_COMP, val))
-               DRM_ERROR("Failed to enable D_COMP\n");
-       mutex_unlock(&dev_priv->rps.hw_lock);
-       POSTING_READ(D_COMP);
+       hsw_write_dcomp(dev_priv, val);
 
        val = I915_READ(LCPLL_CTL);
        val &= ~LCPLL_PLL_DISABLE;
@@ -7066,8 +7420,6 @@ void hsw_enable_pc8(struct drm_i915_private *dev_priv)
        struct drm_device *dev = dev_priv->dev;
        uint32_t val;
 
-       WARN_ON(!HAS_PC8(dev));
-
        DRM_DEBUG_KMS("Enabling package C8+\n");
 
        if (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) {
@@ -7077,7 +7429,6 @@ void hsw_enable_pc8(struct drm_i915_private *dev_priv)
        }
 
        lpt_disable_clkout_dp(dev);
-       hsw_runtime_pm_disable_interrupts(dev);
        hsw_disable_lcpll(dev_priv, true, true);
 }
 
@@ -7086,12 +7437,9 @@ void hsw_disable_pc8(struct drm_i915_private *dev_priv)
        struct drm_device *dev = dev_priv->dev;
        uint32_t val;
 
-       WARN_ON(!HAS_PC8(dev));
-
        DRM_DEBUG_KMS("Disabling package C8+\n");
 
        hsw_restore_lcpll(dev_priv);
-       hsw_runtime_pm_restore_interrupts(dev);
        lpt_init_pch_refclk(dev);
 
        if (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) {
@@ -7101,10 +7449,11 @@ void hsw_disable_pc8(struct drm_i915_private *dev_priv)
        }
 
        intel_prepare_ddi(dev);
-       i915_gem_init_swizzling(dev);
-       mutex_lock(&dev_priv->rps.hw_lock);
-       gen6_update_ring_freq(dev);
-       mutex_unlock(&dev_priv->rps.hw_lock);
+}
+
+static void snb_modeset_global_resources(struct drm_device *dev)
+{
+       modeset_update_crtc_power_domains(dev);
 }
 
 static void haswell_modeset_global_resources(struct drm_device *dev)
@@ -7115,40 +7464,16 @@ static void haswell_modeset_global_resources(struct drm_device *dev)
 static int haswell_crtc_mode_set(struct drm_crtc *crtc,
                                 int x, int y,
                                 struct drm_framebuffer *fb)
-{
-       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 plane = intel_crtc->plane;
-       int ret;
 
        if (!intel_ddi_pll_select(intel_crtc))
                return -EINVAL;
        intel_ddi_pll_enable(intel_crtc);
 
-       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);
-
-       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);
-
-       intel_set_pipe_csc(crtc);
-
-       /* Set up the display plane register */
-       I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE | DISPPLANE_PIPE_CSC_ENABLE);
-       POSTING_READ(DSPCNTR(plane));
-
-       ret = intel_pipe_set_base(crtc, x, y, fb);
-
-       return ret;
+       return 0;
 }
 
 static bool haswell_get_pipe_config(struct intel_crtc *crtc,
@@ -7228,38 +7553,6 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc,
        return true;
 }
 
-static int intel_crtc_mode_set(struct drm_crtc *crtc,
-                              int x, int y,
-                              struct drm_framebuffer *fb)
-{
-       struct drm_device *dev = crtc->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_encoder *encoder;
-       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       struct drm_display_mode *mode = &intel_crtc->config.requested_mode;
-       int pipe = intel_crtc->pipe;
-       int ret;
-
-       drm_vblank_pre_modeset(dev, pipe);
-
-       ret = dev_priv->display.crtc_mode_set(crtc, x, y, fb);
-
-       drm_vblank_post_modeset(dev, pipe);
-
-       if (ret != 0)
-               return ret;
-
-       for_each_encoder_on_crtc(dev, crtc, encoder) {
-               DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%d:%s]\n",
-                       encoder->base.base.id,
-                       drm_get_encoder_name(&encoder->base),
-                       mode->base.id, mode->name);
-               encoder->mode_set(encoder);
-       }
-
-       return 0;
-}
-
 static struct {
        int clock;
        u32 config;
@@ -7374,8 +7667,6 @@ static void haswell_write_eld(struct drm_connector *connector,
 {
        struct drm_i915_private *dev_priv = connector->dev->dev_private;
        uint8_t *eld = connector->eld;
-       struct drm_device *dev = crtc->dev;
-       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        uint32_t eldv;
        uint32_t i;
        int len;
@@ -7387,17 +7678,14 @@ static void haswell_write_eld(struct drm_connector *connector,
        int aud_config = HSW_AUD_CFG(pipe);
        int aud_cntrl_st2 = HSW_AUD_PIN_ELD_CP_VLD;
 
-
-       DRM_DEBUG_DRIVER("HDMI: Haswell Audio initialize....\n");
-
        /* Audio output enable */
        DRM_DEBUG_DRIVER("HDMI audio: enable codec\n");
        tmp = I915_READ(aud_cntrl_st2);
        tmp |= (AUDIO_OUTPUT_ENABLE_A << (pipe * 4));
        I915_WRITE(aud_cntrl_st2, tmp);
+       POSTING_READ(aud_cntrl_st2);
 
-       /* Wait for 1 vertical blank */
-       intel_wait_for_vblank(dev, pipe);
+       assert_pipe_disabled(dev_priv, to_intel_crtc(crtc)->pipe);
 
        /* Set ELD valid state */
        tmp = I915_READ(aud_cntrl_st2);
@@ -7417,7 +7705,6 @@ static void haswell_write_eld(struct drm_connector *connector,
        DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(pipe));
 
        eldv = AUDIO_ELD_VALID_A << (pipe * 4);
-       intel_crtc->eld_vld = true;
 
        if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
                DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n");
@@ -7564,9 +7851,9 @@ void intel_write_eld(struct drm_encoder *encoder,
 
        DRM_DEBUG_DRIVER("ELD on [CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
                         connector->base.id,
-                        drm_get_connector_name(connector),
+                        connector->name,
                         connector->encoder->base.id,
-                        drm_get_encoder_name(connector->encoder));
+                        connector->encoder->name);
 
        connector->eld[6] = drm_av_sync_delay(connector, mode) / 2;
 
@@ -7579,29 +7866,33 @@ static void i845_update_cursor(struct drm_crtc *crtc, u32 base)
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       bool visible = base != 0;
-       u32 cntl;
-
-       if (intel_crtc->cursor_visible == visible)
-               return;
+       uint32_t cntl;
 
-       cntl = I915_READ(_CURACNTR);
-       if (visible) {
+       if (base != intel_crtc->cursor_base) {
                /* On these chipsets we can only modify the base whilst
                 * the cursor is disabled.
                 */
+               if (intel_crtc->cursor_cntl) {
+                       I915_WRITE(_CURACNTR, 0);
+                       POSTING_READ(_CURACNTR);
+                       intel_crtc->cursor_cntl = 0;
+               }
+
                I915_WRITE(_CURABASE, base);
+               POSTING_READ(_CURABASE);
+       }
 
-               cntl &= ~(CURSOR_FORMAT_MASK);
-               /* XXX width must be 64, stride 256 => 0x00 << 28 */
-               cntl |= CURSOR_ENABLE |
+       /* XXX width must be 64, stride 256 => 0x00 << 28 */
+       cntl = 0;
+       if (base)
+               cntl = (CURSOR_ENABLE |
                        CURSOR_GAMMA_ENABLE |
-                       CURSOR_FORMAT_ARGB;
-       } else
-               cntl &= ~(CURSOR_ENABLE | CURSOR_GAMMA_ENABLE);
-       I915_WRITE(_CURACNTR, cntl);
-
-       intel_crtc->cursor_visible = visible;
+                       CURSOR_FORMAT_ARGB);
+       if (intel_crtc->cursor_cntl != cntl) {
+               I915_WRITE(_CURACNTR, cntl);
+               POSTING_READ(_CURACNTR);
+               intel_crtc->cursor_cntl = cntl;
+       }
 }
 
 static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base)
@@ -7610,16 +7901,12 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int pipe = intel_crtc->pipe;
-       bool visible = base != 0;
-
-       if (intel_crtc->cursor_visible != visible) {
-               int16_t width = intel_crtc->cursor_width;
-               uint32_t cntl = I915_READ(CURCNTR(pipe));
-               if (base) {
-                       cntl &= ~(CURSOR_MODE | MCURSOR_PIPE_SELECT);
-                       cntl |= MCURSOR_GAMMA_ENABLE;
+       uint32_t cntl;
 
-                       switch (width) {
+       cntl = 0;
+       if (base) {
+               cntl = MCURSOR_GAMMA_ENABLE;
+               switch (intel_crtc->cursor_width) {
                        case 64:
                                cntl |= CURSOR_MODE_64_ARGB_AX;
                                break;
@@ -7632,18 +7919,16 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base)
                        default:
                                WARN_ON(1);
                                return;
-                       }
-                       cntl |= pipe << 28; /* Connect to correct pipe */
-               } else {
-                       cntl &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE);
-                       cntl |= CURSOR_MODE_DISABLE;
                }
+               cntl |= pipe << 28; /* Connect to correct pipe */
+       }
+       if (intel_crtc->cursor_cntl != cntl) {
                I915_WRITE(CURCNTR(pipe), cntl);
-
-               intel_crtc->cursor_visible = visible;
+               POSTING_READ(CURCNTR(pipe));
+               intel_crtc->cursor_cntl = cntl;
        }
+
        /* and commit changes on next vblank */
-       POSTING_READ(CURCNTR(pipe));
        I915_WRITE(CURBASE(pipe), base);
        POSTING_READ(CURBASE(pipe));
 }
@@ -7654,15 +7939,12 @@ static void ivb_update_cursor(struct drm_crtc *crtc, u32 base)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int pipe = intel_crtc->pipe;
-       bool visible = base != 0;
-
-       if (intel_crtc->cursor_visible != visible) {
-               int16_t width = intel_crtc->cursor_width;
-               uint32_t cntl = I915_READ(CURCNTR_IVB(pipe));
-               if (base) {
-                       cntl &= ~CURSOR_MODE;
-                       cntl |= MCURSOR_GAMMA_ENABLE;
-                       switch (width) {
+       uint32_t cntl;
+
+       cntl = 0;
+       if (base) {
+               cntl = MCURSOR_GAMMA_ENABLE;
+               switch (intel_crtc->cursor_width) {
                        case 64:
                                cntl |= CURSOR_MODE_64_ARGB_AX;
                                break;
@@ -7675,23 +7957,20 @@ static void ivb_update_cursor(struct drm_crtc *crtc, u32 base)
                        default:
                                WARN_ON(1);
                                return;
-                       }
-               } else {
-                       cntl &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE);
-                       cntl |= CURSOR_MODE_DISABLE;
                }
-               if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
-                       cntl |= CURSOR_PIPE_CSC_ENABLE;
-                       cntl &= ~CURSOR_TRICKLE_FEED_DISABLE;
-               }
-               I915_WRITE(CURCNTR_IVB(pipe), cntl);
+       }
+       if (IS_HASWELL(dev) || IS_BROADWELL(dev))
+               cntl |= CURSOR_PIPE_CSC_ENABLE;
 
-               intel_crtc->cursor_visible = visible;
+       if (intel_crtc->cursor_cntl != cntl) {
+               I915_WRITE(CURCNTR(pipe), cntl);
+               POSTING_READ(CURCNTR(pipe));
+               intel_crtc->cursor_cntl = cntl;
        }
+
        /* and commit changes on next vblank */
-       POSTING_READ(CURCNTR_IVB(pipe));
-       I915_WRITE(CURBASE_IVB(pipe), base);
-       POSTING_READ(CURBASE_IVB(pipe));
+       I915_WRITE(CURBASE(pipe), base);
+       POSTING_READ(CURBASE(pipe));
 }
 
 /* If no-part of the cursor is visible on the framebuffer, then the GPU may hang... */
@@ -7705,7 +7984,6 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
        int x = intel_crtc->cursor_x;
        int y = intel_crtc->cursor_y;
        u32 base = 0, pos = 0;
-       bool visible;
 
        if (on)
                base = intel_crtc->cursor_addr;
@@ -7734,20 +8012,18 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
        }
        pos |= y << CURSOR_Y_SHIFT;
 
-       visible = base != 0;
-       if (!visible && !intel_crtc->cursor_visible)
+       if (base == 0 && intel_crtc->cursor_base == 0)
                return;
 
-       if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev) || IS_BROADWELL(dev)) {
-               I915_WRITE(CURPOS_IVB(pipe), pos);
+       I915_WRITE(CURPOS(pipe), pos);
+
+       if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev) || IS_BROADWELL(dev))
                ivb_update_cursor(crtc, base);
-       } else {
-               I915_WRITE(CURPOS(pipe), pos);
-               if (IS_845G(dev) || IS_I865G(dev))
-                       i845_update_cursor(crtc, base);
-               else
-                       i9xx_update_cursor(crtc, base);
-       }
+       else if (IS_845G(dev) || IS_I865G(dev))
+               i845_update_cursor(crtc, base);
+       else
+               i9xx_update_cursor(crtc, base);
+       intel_crtc->cursor_base = base;
 }
 
 static int intel_crtc_cursor_set(struct drm_crtc *crtc,
@@ -8015,7 +8291,8 @@ mode_fits_in_fbdev(struct drm_device *dev,
 
 bool intel_get_load_detect_pipe(struct drm_connector *connector,
                                struct drm_display_mode *mode,
-                               struct intel_load_detect_pipe *old)
+                               struct intel_load_detect_pipe *old,
+                               struct drm_modeset_acquire_ctx *ctx)
 {
        struct intel_crtc *intel_crtc;
        struct intel_encoder *intel_encoder =
@@ -8025,11 +8302,19 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
        struct drm_crtc *crtc = NULL;
        struct drm_device *dev = encoder->dev;
        struct drm_framebuffer *fb;
-       int i = -1;
+       struct drm_mode_config *config = &dev->mode_config;
+       int ret, i = -1;
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
-                     connector->base.id, drm_get_connector_name(connector),
-                     encoder->base.id, drm_get_encoder_name(encoder));
+                     connector->base.id, connector->name,
+                     encoder->base.id, encoder->name);
+
+       drm_modeset_acquire_init(ctx, 0);
+
+retry:
+       ret = drm_modeset_lock(&config->connection_mutex, ctx);
+       if (ret)
+               goto fail_unlock;
 
        /*
         * Algorithm gets a little messy:
@@ -8045,7 +8330,9 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
        if (encoder->crtc) {
                crtc = encoder->crtc;
 
-               mutex_lock(&crtc->mutex);
+               ret = drm_modeset_lock(&crtc->mutex, ctx);
+               if (ret)
+                       goto fail_unlock;
 
                old->dpms_mode = connector->dpms;
                old->load_detect_temp = false;
@@ -8058,7 +8345,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
        }
 
        /* Find an unused one (if possible) */
-       list_for_each_entry(possible_crtc, &dev->mode_config.crtc_list, head) {
+       for_each_crtc(dev, possible_crtc) {
                i++;
                if (!(encoder->possible_crtcs & (1 << i)))
                        continue;
@@ -8073,10 +8360,12 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
         */
        if (!crtc) {
                DRM_DEBUG_KMS("no pipe available for load-detect\n");
-               return false;
+               goto fail_unlock;
        }
 
-       mutex_lock(&crtc->mutex);
+       ret = drm_modeset_lock(&crtc->mutex, ctx);
+       if (ret)
+               goto fail_unlock;
        intel_encoder->new_crtc = to_intel_crtc(crtc);
        to_intel_connector(connector)->new_encoder = intel_encoder;
 
@@ -8126,12 +8415,21 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
                intel_crtc->new_config = &intel_crtc->config;
        else
                intel_crtc->new_config = NULL;
-       mutex_unlock(&crtc->mutex);
+fail_unlock:
+       if (ret == -EDEADLK) {
+               drm_modeset_backoff(ctx);
+               goto retry;
+       }
+
+       drm_modeset_drop_locks(ctx);
+       drm_modeset_acquire_fini(ctx);
+
        return false;
 }
 
 void intel_release_load_detect_pipe(struct drm_connector *connector,
-                                   struct intel_load_detect_pipe *old)
+                                   struct intel_load_detect_pipe *old,
+                                   struct drm_modeset_acquire_ctx *ctx)
 {
        struct intel_encoder *intel_encoder =
                intel_attached_encoder(connector);
@@ -8140,8 +8438,8 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
-                     connector->base.id, drm_get_connector_name(connector),
-                     encoder->base.id, drm_get_encoder_name(encoder));
+                     connector->base.id, connector->name,
+                     encoder->base.id, encoder->name);
 
        if (old->load_detect_temp) {
                to_intel_connector(connector)->new_encoder = NULL;
@@ -8155,7 +8453,7 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
                        drm_framebuffer_unreference(old->release_fb);
                }
 
-               mutex_unlock(&crtc->mutex);
+               goto unlock;
                return;
        }
 
@@ -8163,7 +8461,9 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
        if (old->dpms_mode != DRM_MODE_DPMS_ON)
                connector->funcs->dpms(connector, old->dpms_mode);
 
-       mutex_unlock(&crtc->mutex);
+unlock:
+       drm_modeset_drop_locks(ctx);
+       drm_modeset_acquire_fini(ctx);
 }
 
 static int i9xx_pll_refclk(struct drm_device *dev,
@@ -8449,7 +8749,7 @@ void intel_mark_idle(struct drm_device *dev)
        if (!i915.powersave)
                goto out;
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+       for_each_crtc(dev, crtc) {
                if (!crtc->primary->fb)
                        continue;
 
@@ -8464,7 +8764,7 @@ out:
 }
 
 void intel_mark_fb_busy(struct drm_i915_gem_object *obj,
-                       struct intel_ring_buffer *ring)
+                       struct intel_engine_cs *ring)
 {
        struct drm_device *dev = obj->base.dev;
        struct drm_crtc *crtc;
@@ -8472,7 +8772,7 @@ void intel_mark_fb_busy(struct drm_i915_gem_object *obj,
        if (!i915.powersave)
                return;
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+       for_each_crtc(dev, crtc) {
                if (!crtc->primary->fb)
                        continue;
 
@@ -8560,7 +8860,7 @@ static void do_intel_finish_page_flip(struct drm_device *dev,
        if (work->event)
                drm_send_vblank_event(dev, intel_crtc->pipe, work->event);
 
-       drm_vblank_put(dev, intel_crtc->pipe);
+       drm_crtc_vblank_put(crtc);
 
        spin_unlock_irqrestore(&dev->event_lock, flags);
 
@@ -8587,6 +8887,48 @@ void intel_finish_page_flip_plane(struct drm_device *dev, int plane)
        do_intel_finish_page_flip(dev, crtc);
 }
 
+/* Is 'a' after or equal to 'b'? */
+static bool g4x_flip_count_after_eq(u32 a, u32 b)
+{
+       return !((a - b) & 0x80000000);
+}
+
+static bool page_flip_finished(struct intel_crtc *crtc)
+{
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       /*
+        * The relevant registers doen't exist on pre-ctg.
+        * As the flip done interrupt doesn't trigger for mmio
+        * flips on gmch platforms, a flip count check isn't
+        * really needed there. But since ctg has the registers,
+        * include it in the check anyway.
+        */
+       if (INTEL_INFO(dev)->gen < 5 && !IS_G4X(dev))
+               return true;
+
+       /*
+        * A DSPSURFLIVE check isn't enough in case the mmio and CS flips
+        * used the same base address. In that case the mmio flip might
+        * have completed, but the CS hasn't even executed the flip yet.
+        *
+        * A flip count check isn't enough as the CS might have updated
+        * the base address just after start of vblank, but before we
+        * managed to process the interrupt. This means we'd complete the
+        * CS flip too soon.
+        *
+        * Combining both checks should get us a good enough result. It may
+        * still happen that the CS flip has been executed, but has not
+        * yet actually completed. But in case the base address is the same
+        * anyway, we don't really care.
+        */
+       return (I915_READ(DSPSURFLIVE(crtc->plane)) & ~0xfff) ==
+               crtc->unpin_work->gtt_offset &&
+               g4x_flip_count_after_eq(I915_READ(PIPE_FLIPCOUNT_GM45(crtc->pipe)),
+                                   crtc->unpin_work->flip_count);
+}
+
 void intel_prepare_page_flip(struct drm_device *dev, int plane)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -8599,12 +8941,12 @@ void intel_prepare_page_flip(struct drm_device *dev, int plane)
         * is also accompanied by a spurious intel_prepare_page_flip().
         */
        spin_lock_irqsave(&dev->event_lock, flags);
-       if (intel_crtc->unpin_work)
+       if (intel_crtc->unpin_work && page_flip_finished(intel_crtc))
                atomic_inc_not_zero(&intel_crtc->unpin_work->pending);
        spin_unlock_irqrestore(&dev->event_lock, flags);
 }
 
-inline static void intel_mark_page_flip_active(struct intel_crtc *intel_crtc)
+static inline void intel_mark_page_flip_active(struct intel_crtc *intel_crtc)
 {
        /* Ensure that the work item is consistent when activating it ... */
        smp_wmb();
@@ -8617,21 +8959,16 @@ static int intel_gen2_queue_flip(struct drm_device *dev,
                                 struct drm_crtc *crtc,
                                 struct drm_framebuffer *fb,
                                 struct drm_i915_gem_object *obj,
+                                struct intel_engine_cs *ring,
                                 uint32_t flags)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        u32 flip_mask;
-       struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
        int ret;
 
-       ret = intel_pin_and_fence_fb_obj(dev, obj, ring);
-       if (ret)
-               goto err;
-
        ret = intel_ring_begin(ring, 6);
        if (ret)
-               goto err_unpin;
+               return ret;
 
        /* Can't queue multiple flips, so wait for the previous
         * one to finish before executing the next.
@@ -8645,38 +8982,28 @@ static int intel_gen2_queue_flip(struct drm_device *dev,
        intel_ring_emit(ring, MI_DISPLAY_FLIP |
                        MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
        intel_ring_emit(ring, fb->pitches[0]);
-       intel_ring_emit(ring, i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
+       intel_ring_emit(ring, intel_crtc->unpin_work->gtt_offset);
        intel_ring_emit(ring, 0); /* aux display base address, unused */
 
        intel_mark_page_flip_active(intel_crtc);
        __intel_ring_advance(ring);
        return 0;
-
-err_unpin:
-       intel_unpin_fb_obj(obj);
-err:
-       return ret;
 }
 
 static int intel_gen3_queue_flip(struct drm_device *dev,
                                 struct drm_crtc *crtc,
                                 struct drm_framebuffer *fb,
                                 struct drm_i915_gem_object *obj,
+                                struct intel_engine_cs *ring,
                                 uint32_t flags)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        u32 flip_mask;
-       struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
        int ret;
 
-       ret = intel_pin_and_fence_fb_obj(dev, obj, ring);
-       if (ret)
-               goto err;
-
        ret = intel_ring_begin(ring, 6);
        if (ret)
-               goto err_unpin;
+               return ret;
 
        if (intel_crtc->plane)
                flip_mask = MI_WAIT_FOR_PLANE_B_FLIP;
@@ -8687,38 +9014,29 @@ static int intel_gen3_queue_flip(struct drm_device *dev,
        intel_ring_emit(ring, MI_DISPLAY_FLIP_I915 |
                        MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
        intel_ring_emit(ring, fb->pitches[0]);
-       intel_ring_emit(ring, i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
+       intel_ring_emit(ring, intel_crtc->unpin_work->gtt_offset);
        intel_ring_emit(ring, MI_NOOP);
 
        intel_mark_page_flip_active(intel_crtc);
        __intel_ring_advance(ring);
        return 0;
-
-err_unpin:
-       intel_unpin_fb_obj(obj);
-err:
-       return ret;
 }
 
 static int intel_gen4_queue_flip(struct drm_device *dev,
                                 struct drm_crtc *crtc,
                                 struct drm_framebuffer *fb,
                                 struct drm_i915_gem_object *obj,
+                                struct intel_engine_cs *ring,
                                 uint32_t flags)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        uint32_t pf, pipesrc;
-       struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
        int ret;
 
-       ret = intel_pin_and_fence_fb_obj(dev, obj, ring);
-       if (ret)
-               goto err;
-
        ret = intel_ring_begin(ring, 4);
        if (ret)
-               goto err_unpin;
+               return ret;
 
        /* i965+ uses the linear or tiled offsets from the
         * Display Registers (which do not change across a page-flip)
@@ -8727,8 +9045,7 @@ static int intel_gen4_queue_flip(struct drm_device *dev,
        intel_ring_emit(ring, MI_DISPLAY_FLIP |
                        MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
        intel_ring_emit(ring, fb->pitches[0]);
-       intel_ring_emit(ring,
-                       (i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset) |
+       intel_ring_emit(ring, intel_crtc->unpin_work->gtt_offset |
                        obj->tiling_mode);
 
        /* XXX Enabling the panel-fitter across page-flip is so far
@@ -8742,37 +9059,28 @@ static int intel_gen4_queue_flip(struct drm_device *dev,
        intel_mark_page_flip_active(intel_crtc);
        __intel_ring_advance(ring);
        return 0;
-
-err_unpin:
-       intel_unpin_fb_obj(obj);
-err:
-       return ret;
 }
 
 static int intel_gen6_queue_flip(struct drm_device *dev,
                                 struct drm_crtc *crtc,
                                 struct drm_framebuffer *fb,
                                 struct drm_i915_gem_object *obj,
+                                struct intel_engine_cs *ring,
                                 uint32_t flags)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
        uint32_t pf, pipesrc;
        int ret;
 
-       ret = intel_pin_and_fence_fb_obj(dev, obj, ring);
-       if (ret)
-               goto err;
-
        ret = intel_ring_begin(ring, 4);
        if (ret)
-               goto err_unpin;
+               return ret;
 
        intel_ring_emit(ring, MI_DISPLAY_FLIP |
                        MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
        intel_ring_emit(ring, fb->pitches[0] | obj->tiling_mode);
-       intel_ring_emit(ring, i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
+       intel_ring_emit(ring, intel_crtc->unpin_work->gtt_offset);
 
        /* Contrary to the suggestions in the documentation,
         * "Enable Panel Fitter" does not seem to be required when page
@@ -8787,34 +9095,20 @@ static int intel_gen6_queue_flip(struct drm_device *dev,
        intel_mark_page_flip_active(intel_crtc);
        __intel_ring_advance(ring);
        return 0;
-
-err_unpin:
-       intel_unpin_fb_obj(obj);
-err:
-       return ret;
 }
 
 static int intel_gen7_queue_flip(struct drm_device *dev,
                                 struct drm_crtc *crtc,
                                 struct drm_framebuffer *fb,
                                 struct drm_i915_gem_object *obj,
+                                struct intel_engine_cs *ring,
                                 uint32_t flags)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       struct intel_ring_buffer *ring;
        uint32_t plane_bit = 0;
        int len, ret;
 
-       ring = obj->ring;
-       if (IS_VALLEYVIEW(dev) || ring == NULL || ring->id != RCS)
-               ring = &dev_priv->ring[BCS];
-
-       ret = intel_pin_and_fence_fb_obj(dev, obj, ring);
-       if (ret)
-               goto err;
-
-       switch(intel_crtc->plane) {
+       switch (intel_crtc->plane) {
        case PLANE_A:
                plane_bit = MI_DISPLAY_FLIP_IVB_PLANE_A;
                break;
@@ -8826,13 +9120,20 @@ static int intel_gen7_queue_flip(struct drm_device *dev,
                break;
        default:
                WARN_ONCE(1, "unknown plane in flip command\n");
-               ret = -ENODEV;
-               goto err_unpin;
+               return -ENODEV;
        }
 
        len = 4;
-       if (ring->id == RCS)
+       if (ring->id == RCS) {
                len += 6;
+               /*
+                * On Gen 8, SRM is now taking an extra dword to accommodate
+                * 48bits addresses, and we need a NOOP for the batch size to
+                * stay even.
+                */
+               if (IS_GEN8(dev))
+                       len += 2;
+       }
 
        /*
         * BSpec MI_DISPLAY_FLIP for IVB:
@@ -8846,11 +9147,11 @@ static int intel_gen7_queue_flip(struct drm_device *dev,
         */
        ret = intel_ring_cacheline_align(ring);
        if (ret)
-               goto err_unpin;
+               return ret;
 
        ret = intel_ring_begin(ring, len);
        if (ret)
-               goto err_unpin;
+               return ret;
 
        /* Unmask the flip-done completion message. Note that the bspec says that
         * we should do this for both the BCS and RCS, and that we must not unmask
@@ -8867,31 +9168,35 @@ static int intel_gen7_queue_flip(struct drm_device *dev,
                intel_ring_emit(ring, ~(DERRMR_PIPEA_PRI_FLIP_DONE |
                                        DERRMR_PIPEB_PRI_FLIP_DONE |
                                        DERRMR_PIPEC_PRI_FLIP_DONE));
-               intel_ring_emit(ring, MI_STORE_REGISTER_MEM(1) |
-                               MI_SRM_LRM_GLOBAL_GTT);
+               if (IS_GEN8(dev))
+                       intel_ring_emit(ring, MI_STORE_REGISTER_MEM_GEN8(1) |
+                                             MI_SRM_LRM_GLOBAL_GTT);
+               else
+                       intel_ring_emit(ring, MI_STORE_REGISTER_MEM(1) |
+                                             MI_SRM_LRM_GLOBAL_GTT);
                intel_ring_emit(ring, DERRMR);
                intel_ring_emit(ring, ring->scratch.gtt_offset + 256);
+               if (IS_GEN8(dev)) {
+                       intel_ring_emit(ring, 0);
+                       intel_ring_emit(ring, MI_NOOP);
+               }
        }
 
        intel_ring_emit(ring, MI_DISPLAY_FLIP_I915 | plane_bit);
        intel_ring_emit(ring, (fb->pitches[0] | obj->tiling_mode));
-       intel_ring_emit(ring, i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
+       intel_ring_emit(ring, intel_crtc->unpin_work->gtt_offset);
        intel_ring_emit(ring, (MI_NOOP));
 
        intel_mark_page_flip_active(intel_crtc);
        __intel_ring_advance(ring);
        return 0;
-
-err_unpin:
-       intel_unpin_fb_obj(obj);
-err:
-       return ret;
 }
 
 static int intel_default_queue_flip(struct drm_device *dev,
                                    struct drm_crtc *crtc,
                                    struct drm_framebuffer *fb,
                                    struct drm_i915_gem_object *obj,
+                                   struct intel_engine_cs *ring,
                                    uint32_t flags)
 {
        return -ENODEV;
@@ -8908,6 +9213,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
        struct drm_i915_gem_object *obj = to_intel_framebuffer(fb)->obj;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct intel_unpin_work *work;
+       struct intel_engine_cs *ring;
        unsigned long flags;
        int ret;
 
@@ -8936,7 +9242,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
        work->old_fb_obj = to_intel_framebuffer(old_fb)->obj;
        INIT_WORK(&work->work, intel_unpin_work_fn);
 
-       ret = drm_vblank_get(dev, intel_crtc->pipe);
+       ret = drm_crtc_vblank_get(crtc);
        if (ret)
                goto free_work;
 
@@ -8945,7 +9251,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
        if (intel_crtc->unpin_work) {
                spin_unlock_irqrestore(&dev->event_lock, flags);
                kfree(work);
-               drm_vblank_put(dev, intel_crtc->pipe);
+               drm_crtc_vblank_put(crtc);
 
                DRM_DEBUG_DRIVER("flip queue: crtc already busy\n");
                return -EBUSY;
@@ -8973,10 +9279,30 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
        atomic_inc(&intel_crtc->unpin_work_count);
        intel_crtc->reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter);
 
-       ret = dev_priv->display.queue_flip(dev, crtc, fb, obj, page_flip_flags);
+       if (INTEL_INFO(dev)->gen >= 5 || IS_G4X(dev))
+               work->flip_count = I915_READ(PIPE_FLIPCOUNT_GM45(intel_crtc->pipe)) + 1;
+
+       if (IS_VALLEYVIEW(dev)) {
+               ring = &dev_priv->ring[BCS];
+       } else if (INTEL_INFO(dev)->gen >= 7) {
+               ring = obj->ring;
+               if (ring == NULL || ring->id != RCS)
+                       ring = &dev_priv->ring[BCS];
+       } else {
+               ring = &dev_priv->ring[RCS];
+       }
+
+       ret = intel_pin_and_fence_fb_obj(dev, obj, ring);
        if (ret)
                goto cleanup_pending;
 
+       work->gtt_offset =
+               i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset;
+
+       ret = dev_priv->display.queue_flip(dev, crtc, fb, obj, ring, page_flip_flags);
+       if (ret)
+               goto cleanup_unpin;
+
        intel_disable_fbc(dev);
        intel_mark_fb_busy(obj, NULL);
        mutex_unlock(&dev->struct_mutex);
@@ -8985,6 +9311,8 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
 
        return 0;
 
+cleanup_unpin:
+       intel_unpin_fb_obj(obj);
 cleanup_pending:
        atomic_dec(&intel_crtc->unpin_work_count);
        crtc->primary->fb = old_fb;
@@ -8997,7 +9325,7 @@ cleanup:
        intel_crtc->unpin_work = NULL;
        spin_unlock_irqrestore(&dev->event_lock, flags);
 
-       drm_vblank_put(dev, intel_crtc->pipe);
+       drm_crtc_vblank_put(crtc);
 free_work:
        kfree(work);
 
@@ -9040,8 +9368,7 @@ static void intel_modeset_update_staged_output_state(struct drm_device *dev)
                        to_intel_crtc(encoder->base.crtc);
        }
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list,
-                           base.head) {
+       for_each_intel_crtc(dev, crtc) {
                crtc->new_enabled = crtc->base.enabled;
 
                if (crtc->new_enabled)
@@ -9072,21 +9399,20 @@ static void intel_modeset_commit_output_state(struct drm_device *dev)
                encoder->base.crtc = &encoder->new_crtc->base;
        }
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list,
-                           base.head) {
+       for_each_intel_crtc(dev, crtc) {
                crtc->base.enabled = crtc->new_enabled;
        }
 }
 
 static void
-connected_sink_compute_bpp(struct intel_connector * connector,
+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));
+               connector->base.name);
 
        /* Don't use an invalid EDID bpc value */
        if (connector->base.display_info.bpc &&
@@ -9427,8 +9753,7 @@ intel_modeset_affected_pipes(struct drm_crtc *crtc, unsigned *modeset_pipes,
        }
 
        /* Check for pipes that will be enabled/disabled ... */
-       list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list,
-                           base.head) {
+       for_each_intel_crtc(dev, intel_crtc) {
                if (intel_crtc->base.enabled == intel_crtc->new_enabled)
                        continue;
 
@@ -9501,8 +9826,7 @@ intel_modeset_update_state(struct drm_device *dev, unsigned prepare_pipes)
        intel_modeset_commit_output_state(dev);
 
        /* Double check state. */
-       list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list,
-                           base.head) {
+       for_each_intel_crtc(dev, intel_crtc) {
                WARN_ON(intel_crtc->base.enabled != intel_crtc_in_use(&intel_crtc->base));
                WARN_ON(intel_crtc->new_config &&
                        intel_crtc->new_config != &intel_crtc->config);
@@ -9631,6 +9955,12 @@ intel_pipe_config_compare(struct drm_device *dev,
        PIPE_CONF_CHECK_I(adjusted_mode.crtc_vsync_end);
 
        PIPE_CONF_CHECK_I(pixel_multiplier);
+       PIPE_CONF_CHECK_I(has_hdmi_sink);
+       if ((INTEL_INFO(dev)->gen < 8 && !IS_HASWELL(dev)) ||
+           IS_VALLEYVIEW(dev))
+               PIPE_CONF_CHECK_I(limited_color_range);
+
+       PIPE_CONF_CHECK_I(has_audio);
 
        PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags,
                              DRM_MODE_FLAG_INTERLACE);
@@ -9728,7 +10058,7 @@ check_encoder_state(struct drm_device *dev)
 
                DRM_DEBUG_KMS("[ENCODER:%d:%s]\n",
                              encoder->base.base.id,
-                             drm_get_encoder_name(&encoder->base));
+                             encoder->base.name);
 
                WARN(&encoder->new_crtc->base != encoder->base.crtc,
                     "encoder's stage crtc doesn't match current crtc\n");
@@ -9780,8 +10110,7 @@ check_crtc_state(struct drm_device *dev)
        struct intel_encoder *encoder;
        struct intel_crtc_config pipe_config;
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list,
-                           base.head) {
+       for_each_intel_crtc(dev, crtc) {
                bool enabled = false;
                bool active = false;
 
@@ -9870,8 +10199,7 @@ check_shared_dpll_state(struct drm_device *dev)
                     "pll on state mismatch (expected %i, found %i)\n",
                     pll->on, active);
 
-               list_for_each_entry(crtc, &dev->mode_config.crtc_list,
-                                   base.head) {
+               for_each_intel_crtc(dev, crtc) {
                        if (crtc->base.enabled && intel_crtc_to_shared_dpll(crtc) == pll)
                                enabled_crtcs++;
                        if (crtc->active && intel_crtc_to_shared_dpll(crtc) == pll)
@@ -9911,6 +10239,44 @@ void ironlake_check_encoder_dotclock(const struct intel_crtc_config *pipe_config
             pipe_config->adjusted_mode.crtc_clock, dotclock);
 }
 
+static void update_scanline_offset(struct intel_crtc *crtc)
+{
+       struct drm_device *dev = crtc->base.dev;
+
+       /*
+        * The scanline counter increments at the leading edge of hsync.
+        *
+        * On most platforms it starts counting from vtotal-1 on the
+        * first active line. That means the scanline counter value is
+        * always one less than what we would expect. Ie. just after
+        * start of vblank, which also occurs at start of hsync (on the
+        * last active line), the scanline counter will read vblank_start-1.
+        *
+        * On gen2 the scanline counter starts counting from 1 instead
+        * of vtotal-1, so we have to subtract one (or rather add vtotal-1
+        * to keep the value positive), instead of adding one.
+        *
+        * On HSW+ the behaviour of the scanline counter depends on the output
+        * type. For DP ports it behaves like most other platforms, but on HDMI
+        * there's an extra 1 line difference. So we need to add two instead of
+        * one to the value.
+        */
+       if (IS_GEN2(dev)) {
+               const struct drm_display_mode *mode = &crtc->config.adjusted_mode;
+               int vtotal;
+
+               vtotal = mode->crtc_vtotal;
+               if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+                       vtotal /= 2;
+
+               crtc->scanline_offset = vtotal - 1;
+       } else if (HAS_DDI(dev) &&
+                  intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI)) {
+               crtc->scanline_offset = 2;
+       } else
+               crtc->scanline_offset = 1;
+}
+
 static int __intel_set_mode(struct drm_crtc *crtc,
                            struct drm_display_mode *mode,
                            int x, int y, struct drm_framebuffer *fb)
@@ -10002,15 +10368,38 @@ static int __intel_set_mode(struct drm_crtc *crtc,
         * on the DPLL.
         */
        for_each_intel_crtc_masked(dev, modeset_pipes, intel_crtc) {
-               ret = intel_crtc_mode_set(&intel_crtc->base,
-                                         x, y, fb);
+               struct drm_framebuffer *old_fb;
+
+               mutex_lock(&dev->struct_mutex);
+               ret = intel_pin_and_fence_fb_obj(dev,
+                                                to_intel_framebuffer(fb)->obj,
+                                                NULL);
+               if (ret != 0) {
+                       DRM_ERROR("pin & fence failed\n");
+                       mutex_unlock(&dev->struct_mutex);
+                       goto done;
+               }
+               old_fb = crtc->primary->fb;
+               if (old_fb)
+                       intel_unpin_fb_obj(to_intel_framebuffer(old_fb)->obj);
+               mutex_unlock(&dev->struct_mutex);
+
+               crtc->primary->fb = fb;
+               crtc->x = x;
+               crtc->y = y;
+
+               ret = dev_priv->display.crtc_mode_set(&intel_crtc->base,
+                                                     x, y, fb);
                if (ret)
                        goto done;
        }
 
        /* Now enable the clocks, plane, pipe, and connectors that we set up. */
-       for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc)
+       for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) {
+               update_scanline_offset(intel_crtc);
+
                dev_priv->display.crtc_enable(&intel_crtc->base);
+       }
 
        /* FIXME: add subpixel order */
 done:
@@ -10086,7 +10475,7 @@ static int intel_set_config_save_state(struct drm_device *dev,
         * restored, not the drivers personal bookkeeping.
         */
        count = 0;
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+       for_each_crtc(dev, crtc) {
                config->save_crtc_enabled[count++] = crtc->enabled;
        }
 
@@ -10112,7 +10501,7 @@ static void intel_set_config_restore_state(struct drm_device *dev,
        int count;
 
        count = 0;
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+       for_each_intel_crtc(dev, crtc) {
                crtc->new_enabled = config->save_crtc_enabled[count++];
 
                if (crtc->new_enabled)
@@ -10236,7 +10625,7 @@ intel_modeset_stage_output_state(struct drm_device *dev,
 
                        DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n",
                                connector->base.base.id,
-                               drm_get_connector_name(&connector->base));
+                               connector->base.name);
                }
 
 
@@ -10271,7 +10660,7 @@ intel_modeset_stage_output_state(struct drm_device *dev,
 
                DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n",
                        connector->base.base.id,
-                       drm_get_connector_name(&connector->base),
+                       connector->base.name,
                        new_crtc->base.id);
        }
 
@@ -10302,8 +10691,7 @@ intel_modeset_stage_output_state(struct drm_device *dev,
        }
        /* Now we've also updated encoder->new_crtc for all encoders. */
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list,
-                           base.head) {
+       for_each_intel_crtc(dev, crtc) {
                crtc->new_enabled = false;
 
                list_for_each_entry(encoder,
@@ -10516,7 +10904,7 @@ static void ibx_pch_dpll_disable(struct drm_i915_private *dev_priv,
        struct intel_crtc *crtc;
 
        /* Make sure no transcoder isn't still depending on us. */
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+       for_each_intel_crtc(dev, crtc) {
                if (intel_crtc_to_shared_dpll(crtc) == pll)
                        assert_pch_transcoder_disabled(dev_priv, crtc->pipe);
        }
@@ -10573,16 +10961,6 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
 
        drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs);
 
-       if (IS_GEN2(dev)) {
-               intel_crtc->max_cursor_width = GEN2_CURSOR_WIDTH;
-               intel_crtc->max_cursor_height = GEN2_CURSOR_HEIGHT;
-       } else {
-               intel_crtc->max_cursor_width = CURSOR_WIDTH;
-               intel_crtc->max_cursor_height = CURSOR_HEIGHT;
-       }
-       dev->mode_config.cursor_width = intel_crtc->max_cursor_width;
-       dev->mode_config.cursor_height = intel_crtc->max_cursor_height;
-
        drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256);
        for (i = 0; i < 256; i++) {
                intel_crtc->lut_r[i] = i;
@@ -10601,19 +10979,27 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
                intel_crtc->plane = !pipe;
        }
 
+       intel_crtc->cursor_base = ~0;
+       intel_crtc->cursor_cntl = ~0;
+
+       init_waitqueue_head(&intel_crtc->vbl_wait);
+
        BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) ||
               dev_priv->plane_to_crtc_mapping[intel_crtc->plane] != NULL);
        dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = &intel_crtc->base;
        dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = &intel_crtc->base;
 
        drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
+
+       WARN_ON(drm_crtc_index(&intel_crtc->base) != intel_crtc->pipe);
 }
 
 enum pipe intel_get_pipe_from_connector(struct intel_connector *connector)
 {
        struct drm_encoder *encoder = connector->base.encoder;
+       struct drm_device *dev = connector->base.dev;
 
-       WARN_ON(!mutex_is_locked(&connector->base.dev->mode_config.mutex));
+       WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
 
        if (!encoder)
                return INVALID_PIPE;
@@ -10709,7 +11095,7 @@ static void intel_setup_outputs(struct drm_device *dev)
 
        intel_lvds_init(dev);
 
-       if (!IS_ULT(dev))
+       if (!IS_ULT(dev) && !IS_CHERRYVIEW(dev) && dev_priv->vbt.int_crt_support)
                intel_crt_init(dev);
 
        if (HAS_DDI(dev)) {
@@ -10773,6 +11159,15 @@ static void intel_setup_outputs(struct drm_device *dev)
                                intel_dp_init(dev, VLV_DISPLAY_BASE + DP_C, PORT_C);
                }
 
+               if (IS_CHERRYVIEW(dev)) {
+                       if (I915_READ(VLV_DISPLAY_BASE + CHV_HDMID) & SDVO_DETECTED) {
+                               intel_hdmi_init(dev, VLV_DISPLAY_BASE + CHV_HDMID,
+                                               PORT_D);
+                               if (I915_READ(VLV_DISPLAY_BASE + DP_D) & DP_DETECTED)
+                                       intel_dp_init(dev, VLV_DISPLAY_BASE + DP_D, PORT_D);
+                       }
+               }
+
                intel_dsi_init(dev);
        } else if (SUPPORTS_DIGITAL_OUTPUTS(dev)) {
                bool found = false;
@@ -11002,6 +11397,8 @@ static void intel_init_display(struct drm_device *dev)
 
        if (HAS_PCH_SPLIT(dev) || IS_G4X(dev))
                dev_priv->display.find_dpll = g4x_find_best_dpll;
+       else if (IS_CHERRYVIEW(dev))
+               dev_priv->display.find_dpll = chv_find_best_dpll;
        else if (IS_VALLEYVIEW(dev))
                dev_priv->display.find_dpll = vlv_find_best_dpll;
        else if (IS_PINEVIEW(dev))
@@ -11083,6 +11480,8 @@ static void intel_init_display(struct drm_device *dev)
                } else if (IS_GEN6(dev)) {
                        dev_priv->display.fdi_link_train = gen6_fdi_link_train;
                        dev_priv->display.write_eld = ironlake_write_eld;
+                       dev_priv->display.modeset_global_resources =
+                               snb_modeset_global_resources;
                } else if (IS_IVYBRIDGE(dev)) {
                        /* FIXME: detect B0+ stepping and use auto training */
                        dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train;
@@ -11211,9 +11610,6 @@ static struct intel_quirk intel_quirks[] = {
        /* ThinkPad T60 needs pipe A force quirk (bug #16494) */
        { 0x2782, 0x17aa, 0x201a, quirk_pipea_force },
 
-       /* 830 needs to leave pipe A & dpll A up */
-       { 0x3577, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force },
-
        /* Lenovo U160 cannot use SSC on LVDS */
        { 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable },
 
@@ -11287,9 +11683,7 @@ void intel_modeset_init_hw(struct drm_device *dev)
 
        intel_reset_dpio(dev);
 
-       mutex_lock(&dev->struct_mutex);
        intel_enable_gt_powersave(dev);
-       mutex_unlock(&dev->struct_mutex);
 }
 
 void intel_modeset_suspend_hw(struct drm_device *dev)
@@ -11333,6 +11727,15 @@ void intel_modeset_init(struct drm_device *dev)
                dev->mode_config.max_width = 8192;
                dev->mode_config.max_height = 8192;
        }
+
+       if (IS_GEN2(dev)) {
+               dev->mode_config.cursor_width = GEN2_CURSOR_WIDTH;
+               dev->mode_config.cursor_height = GEN2_CURSOR_HEIGHT;
+       } else {
+               dev->mode_config.cursor_width = MAX_CURSOR_WIDTH;
+               dev->mode_config.cursor_height = MAX_CURSOR_HEIGHT;
+       }
+
        dev->mode_config.fb_base = dev_priv->gtt.mappable_base;
 
        DRM_DEBUG_KMS("%d display pipe%s available.\n",
@@ -11362,12 +11765,11 @@ void intel_modeset_init(struct drm_device *dev)
        /* Just in case the BIOS is doing something questionable. */
        intel_disable_fbc(dev);
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock_all(dev);
        intel_modeset_setup_hw_state(dev, false);
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock_all(dev);
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list,
-                           base.head) {
+       for_each_intel_crtc(dev, crtc) {
                if (!crtc->active)
                        continue;
 
@@ -11395,6 +11797,7 @@ static void intel_enable_pipe_a(struct drm_device *dev)
        struct intel_connector *connector;
        struct drm_connector *crt = NULL;
        struct intel_load_detect_pipe load_detect_temp;
+       struct drm_modeset_acquire_ctx ctx;
 
        /* We can't just switch on the pipe A, we need to set things up with a
         * proper mode and output configuration. As a gross hack, enable pipe A
@@ -11411,8 +11814,8 @@ static void intel_enable_pipe_a(struct drm_device *dev)
        if (!crt)
                return;
 
-       if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp))
-               intel_release_load_detect_pipe(crt, &load_detect_temp);
+       if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp, &ctx))
+               intel_release_load_detect_pipe(crt, &load_detect_temp, &ctx);
 
 
 }
@@ -11447,6 +11850,12 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc)
        reg = PIPECONF(crtc->config.cpu_transcoder);
        I915_WRITE(reg, I915_READ(reg) & ~PIPECONF_FRAME_START_DELAY_MASK);
 
+       /* restore vblank interrupts to correct state */
+       if (crtc->active)
+               drm_vblank_on(dev, crtc->pipe);
+       else
+               drm_vblank_off(dev, crtc->pipe);
+
        /* We need to sanitize the plane -> pipe mapping first because this will
         * disable the crtc (and hence change the state) if it is wrong. Note
         * that gen4+ has a fixed plane -> pipe mapping.  */
@@ -11525,16 +11934,25 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc)
                        encoder->base.crtc = NULL;
                }
        }
-       if (crtc->active) {
+
+       if (crtc->active || IS_VALLEYVIEW(dev) || INTEL_INFO(dev)->gen < 5) {
                /*
                 * We start out with underrun reporting disabled to avoid races.
                 * For correct bookkeeping mark this on active crtcs.
                 *
+                * Also on gmch platforms we dont have any hardware bits to
+                * disable the underrun reporting. Which means we need to start
+                * out with underrun reporting disabled also on inactive pipes,
+                * since otherwise we'll complain about the garbage we read when
+                * e.g. coming up after runtime pm.
+                *
                 * No protection against concurrent access is required - at
                 * worst a fifo underrun happens which also sets this to false.
                 */
                crtc->cpu_fifo_underrun_disabled = true;
                crtc->pch_fifo_underrun_disabled = true;
+
+               update_scanline_offset(crtc);
        }
 }
 
@@ -11552,7 +11970,7 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder)
        if (encoder->connectors_active && !has_active_crtc) {
                DRM_DEBUG_KMS("[ENCODER:%d:%s] has active connectors but no active pipe!\n",
                              encoder->base.base.id,
-                             drm_get_encoder_name(&encoder->base));
+                             encoder->base.name);
 
                /* Connector is active, but has no active pipe. This is
                 * fallout from our resume register restoring. Disable
@@ -11560,7 +11978,7 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder)
                if (encoder->base.crtc) {
                        DRM_DEBUG_KMS("[ENCODER:%d:%s] manually disabled\n",
                                      encoder->base.base.id,
-                                     drm_get_encoder_name(&encoder->base));
+                                     encoder->base.name);
                        encoder->disable(encoder);
                }
                encoder->base.crtc = NULL;
@@ -11611,6 +12029,16 @@ void i915_redisable_vga(struct drm_device *dev)
        i915_redisable_vga_power_on(dev);
 }
 
+static bool primary_get_hw_state(struct intel_crtc *crtc)
+{
+       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+
+       if (!crtc->active)
+               return false;
+
+       return I915_READ(DSPCNTR(crtc->plane)) & DISPLAY_PLANE_ENABLE;
+}
+
 static void intel_modeset_readout_hw_state(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -11620,8 +12048,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
        struct intel_connector *connector;
        int i;
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list,
-                           base.head) {
+       for_each_intel_crtc(dev, crtc) {
                memset(&crtc->config, 0, sizeof(crtc->config));
 
                crtc->config.quirks |= PIPE_CONFIG_QUIRK_INHERITED_MODE;
@@ -11630,7 +12057,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
                                                                 &crtc->config);
 
                crtc->base.enabled = crtc->active;
-               crtc->primary_enabled = crtc->active;
+               crtc->primary_enabled = primary_get_hw_state(crtc);
 
                DRM_DEBUG_KMS("[CRTC:%d] hw state readout: %s\n",
                              crtc->base.base.id,
@@ -11646,8 +12073,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
 
                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) {
+               for_each_intel_crtc(dev, crtc) {
                        if (crtc->active && intel_crtc_to_shared_dpll(crtc) == pll)
                                pll->active++;
                }
@@ -11672,7 +12098,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
                encoder->connectors_active = false;
                DRM_DEBUG_KMS("[ENCODER:%d:%s] hw state readout: %s, pipe %c\n",
                              encoder->base.base.id,
-                             drm_get_encoder_name(&encoder->base),
+                             encoder->base.name,
                              encoder->base.crtc ? "enabled" : "disabled",
                              pipe_name(pipe));
        }
@@ -11689,7 +12115,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
                }
                DRM_DEBUG_KMS("[CONNECTOR:%d:%s] hw state readout: %s\n",
                              connector->base.base.id,
-                             drm_get_connector_name(&connector->base),
+                             connector->base.name,
                              connector->base.encoder ? "enabled" : "disabled");
        }
 }
@@ -11712,8 +12138,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev,
         * Note that this could go away if we move to using crtc_config
         * checking everywhere.
         */
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list,
-                           base.head) {
+       for_each_intel_crtc(dev, crtc) {
                if (crtc->active && i915.fastboot) {
                        intel_mode_from_pipe_config(&crtc->base.mode, &crtc->config);
                        DRM_DEBUG_KMS("[CRTC:%d] found active mode: ",
@@ -11789,7 +12214,7 @@ void intel_modeset_gem_init(struct drm_device *dev)
         * for this.
         */
        mutex_lock(&dev->struct_mutex);
-       list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
+       for_each_crtc(dev, c) {
                if (!c->primary->fb)
                        continue;
 
@@ -11835,7 +12260,7 @@ void intel_modeset_cleanup(struct drm_device *dev)
 
        intel_unregister_dsm_handler();
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+       for_each_crtc(dev, crtc) {
                /* Skip inactive CRTCs */
                if (!crtc->primary->fb)
                        continue;
@@ -11933,6 +12358,7 @@ struct intel_display_error_state {
        struct intel_pipe_error_state {
                bool power_domain_on;
                u32 source;
+               u32 stat;
        } pipe[I915_MAX_PIPES];
 
        struct intel_plane_error_state {
@@ -11990,15 +12416,9 @@ intel_display_capture_error_state(struct drm_device *dev)
                if (!error->pipe[i].power_domain_on)
                        continue;
 
-               if (INTEL_INFO(dev)->gen <= 6 || IS_VALLEYVIEW(dev)) {
-                       error->cursor[i].control = I915_READ(CURCNTR(i));
-                       error->cursor[i].position = I915_READ(CURPOS(i));
-                       error->cursor[i].base = I915_READ(CURBASE(i));
-               } else {
-                       error->cursor[i].control = I915_READ(CURCNTR_IVB(i));
-                       error->cursor[i].position = I915_READ(CURPOS_IVB(i));
-                       error->cursor[i].base = I915_READ(CURBASE_IVB(i));
-               }
+               error->cursor[i].control = I915_READ(CURCNTR(i));
+               error->cursor[i].position = I915_READ(CURPOS(i));
+               error->cursor[i].base = I915_READ(CURBASE(i));
 
                error->plane[i].control = I915_READ(DSPCNTR(i));
                error->plane[i].stride = I915_READ(DSPSTRIDE(i));
@@ -12014,6 +12434,9 @@ intel_display_capture_error_state(struct drm_device *dev)
                }
 
                error->pipe[i].source = I915_READ(PIPESRC(i));
+
+               if (!HAS_PCH_SPLIT(dev))
+                       error->pipe[i].stat = I915_READ(PIPESTAT(i));
        }
 
        error->num_transcoders = INTEL_INFO(dev)->num_pipes;
@@ -12064,6 +12487,7 @@ intel_display_print_error_state(struct drm_i915_error_state_buf *m,
                err_printf(m, "  Power: %s\n",
                           error->pipe[i].power_domain_on ? "on" : "off");
                err_printf(m, "  SRC: %08x\n", error->pipe[i].source);
+               err_printf(m, "  STAT: %08x\n", error->pipe[i].stat);
 
                err_printf(m, "Plane [%d]:\n", i);
                err_printf(m, "  CNTR: %08x\n", error->plane[i].control);
index 2a00cb828d20c7549a7b00a6745c228998cc136b..52fda950fd2a1556cb23fc000e5a7a575e58e4e1 100644 (file)
@@ -64,6 +64,24 @@ static const struct dp_link_dpll vlv_dpll[] = {
                { .p1 = 2, .p2 = 2, .n = 1, .m1 = 2, .m2 = 27 } }
 };
 
+/*
+ * CHV supports eDP 1.4 that have  more link rates.
+ * Below only provides the fixed rate but exclude variable rate.
+ */
+static const struct dp_link_dpll chv_dpll[] = {
+       /*
+        * CHV requires to program fractional division for m2.
+        * m2 is stored in fixed point format using formula below
+        * (m2_int << 22) | m2_fraction
+        */
+       { DP_LINK_BW_1_62,      /* m2_int = 32, m2_fraction = 1677722 */
+               { .p1 = 4, .p2 = 2, .n = 1, .m1 = 2, .m2 = 0x819999a } },
+       { DP_LINK_BW_2_7,       /* m2_int = 27, m2_fraction = 0 */
+               { .p1 = 4, .p2 = 1, .n = 1, .m1 = 2, .m2 = 0x6c00000 } },
+       { DP_LINK_BW_5_4,       /* m2_int = 27, m2_fraction = 0 */
+               { .p1 = 2, .p2 = 1, .n = 1, .m1 = 2, .m2 = 0x6c00000 } }
+};
+
 /**
  * is_edp - is the given port attached to an eDP panel (either CPU or PCH)
  * @intel_dp: DP struct
@@ -330,8 +348,12 @@ static bool edp_have_panel_vdd(struct intel_dp *intel_dp)
 {
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+       struct intel_encoder *intel_encoder = &intel_dig_port->base;
+       enum intel_display_power_domain power_domain;
 
-       return !dev_priv->pm.suspended &&
+       power_domain = intel_display_port_power_domain(intel_encoder);
+       return intel_display_power_enabled(dev_priv, power_domain) &&
               (I915_READ(_pp_ctrl_reg(intel_dp)) & EDP_FORCE_VDD) != 0;
 }
 
@@ -697,9 +719,9 @@ intel_dp_aux_init(struct intel_dp *intel_dp, struct intel_connector *connector)
        DRM_DEBUG_KMS("registering %s bus for %s\n", name,
                      connector->base.kdev->kobj.name);
 
-       ret = drm_dp_aux_register_i2c_bus(&intel_dp->aux);
+       ret = drm_dp_aux_register(&intel_dp->aux);
        if (ret < 0) {
-               DRM_ERROR("drm_dp_aux_register_i2c_bus() for %s failed (%d)\n",
+               DRM_ERROR("drm_dp_aux_register() for %s failed (%d)\n",
                          name, ret);
                return;
        }
@@ -709,7 +731,7 @@ intel_dp_aux_init(struct intel_dp *intel_dp, struct intel_connector *connector)
                                intel_dp->aux.ddc.dev.kobj.name);
        if (ret < 0) {
                DRM_ERROR("sysfs_create_link() for %s failed (%d)\n", name, ret);
-               drm_dp_aux_unregister_i2c_bus(&intel_dp->aux);
+               drm_dp_aux_unregister(&intel_dp->aux);
        }
 }
 
@@ -739,6 +761,9 @@ intel_dp_set_clock(struct intel_encoder *encoder,
        } else if (HAS_PCH_SPLIT(dev)) {
                divisor = pch_dpll;
                count = ARRAY_SIZE(pch_dpll);
+       } else if (IS_CHERRYVIEW(dev)) {
+               divisor = chv_dpll;
+               count = ARRAY_SIZE(chv_dpll);
        } else if (IS_VALLEYVIEW(dev)) {
                divisor = vlv_dpll;
                count = ARRAY_SIZE(vlv_dpll);
@@ -755,6 +780,20 @@ intel_dp_set_clock(struct intel_encoder *encoder,
        }
 }
 
+static void
+intel_dp_set_m2_n2(struct intel_crtc *crtc, struct intel_link_m_n *m_n)
+{
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       enum transcoder transcoder = crtc->config.cpu_transcoder;
+
+       I915_WRITE(PIPE_DATA_M2(transcoder),
+               TU_SIZE(m_n->tu) | m_n->gmch_m);
+       I915_WRITE(PIPE_DATA_N2(transcoder), m_n->gmch_n);
+       I915_WRITE(PIPE_LINK_M2(transcoder), m_n->link_m);
+       I915_WRITE(PIPE_LINK_N2(transcoder), m_n->link_n);
+}
+
 bool
 intel_dp_compute_config(struct intel_encoder *encoder,
                        struct intel_crtc_config *pipe_config)
@@ -780,6 +819,7 @@ intel_dp_compute_config(struct intel_encoder *encoder,
                pipe_config->has_pch_encoder = true;
 
        pipe_config->has_dp_encoder = true;
+       pipe_config->has_audio = intel_dp->has_audio;
 
        if (is_edp(intel_dp) && intel_connector->panel.fixed_mode) {
                intel_fixed_panel_mode(intel_connector->panel.fixed_mode,
@@ -880,6 +920,14 @@ found:
                               pipe_config->port_clock,
                               &pipe_config->dp_m_n);
 
+       if (intel_connector->panel.downclock_mode != NULL &&
+               intel_dp->drrs_state.type == SEAMLESS_DRRS_SUPPORT) {
+                       intel_link_compute_m_n(bpp, lane_count,
+                               intel_connector->panel.downclock_mode->clock,
+                               pipe_config->port_clock,
+                               &pipe_config->dp_m2_n2);
+       }
+
        intel_dp_set_clock(encoder, pipe_config, intel_dp->link_bw);
 
        return true;
@@ -915,7 +963,7 @@ static void ironlake_set_pll_cpu_edp(struct intel_dp *intel_dp)
        udelay(500);
 }
 
-static void intel_dp_mode_set(struct intel_encoder *encoder)
+static void intel_dp_prepare(struct intel_encoder *encoder)
 {
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -950,7 +998,7 @@ static void intel_dp_mode_set(struct intel_encoder *encoder)
        intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0;
        intel_dp->DP |= DP_PORT_WIDTH(intel_dp->lane_count);
 
-       if (intel_dp->has_audio) {
+       if (crtc->config.has_audio) {
                DRM_DEBUG_DRIVER("Enabling DP audio on pipe %c\n",
                                 pipe_name(crtc->pipe));
                intel_dp->DP |= DP_AUDIO_OUTPUT_ENABLE;
@@ -983,14 +1031,15 @@ static void intel_dp_mode_set(struct intel_encoder *encoder)
                if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
                        intel_dp->DP |= DP_ENHANCED_FRAMING;
 
-               if (crtc->pipe == 1)
-                       intel_dp->DP |= DP_PIPEB_SELECT;
+               if (!IS_CHERRYVIEW(dev)) {
+                       if (crtc->pipe == 1)
+                               intel_dp->DP |= DP_PIPEB_SELECT;
+               } else {
+                       intel_dp->DP |= DP_PIPE_SELECT_CHV(crtc->pipe);
+               }
        } else {
                intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT;
        }
-
-       if (port == PORT_A && !IS_VALLEYVIEW(dev))
-               ironlake_set_pll_cpu_edp(intel_dp);
 }
 
 #define IDLE_ON_MASK           (PP_ON | PP_SEQUENCE_MASK | 0                     | PP_SEQUENCE_STATE_MASK)
@@ -1082,7 +1131,10 @@ static  u32 ironlake_get_pp_control(struct intel_dp *intel_dp)
 static bool _edp_panel_vdd_on(struct intel_dp *intel_dp)
 {
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
+       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+       struct intel_encoder *intel_encoder = &intel_dig_port->base;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       enum intel_display_power_domain power_domain;
        u32 pp;
        u32 pp_stat_reg, pp_ctrl_reg;
        bool need_to_disable = !intel_dp->want_panel_vdd;
@@ -1095,7 +1147,8 @@ static bool _edp_panel_vdd_on(struct intel_dp *intel_dp)
        if (edp_have_panel_vdd(intel_dp))
                return need_to_disable;
 
-       intel_runtime_pm_get(dev_priv);
+       power_domain = intel_display_port_power_domain(intel_encoder);
+       intel_display_power_get(dev_priv, power_domain);
 
        DRM_DEBUG_KMS("Turning eDP VDD on\n");
 
@@ -1139,9 +1192,14 @@ static void edp_panel_vdd_off_sync(struct intel_dp *intel_dp)
        u32 pp;
        u32 pp_stat_reg, pp_ctrl_reg;
 
-       WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+       WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
 
        if (!intel_dp->want_panel_vdd && edp_have_panel_vdd(intel_dp)) {
+               struct intel_digital_port *intel_dig_port =
+                                               dp_to_dig_port(intel_dp);
+               struct intel_encoder *intel_encoder = &intel_dig_port->base;
+               enum intel_display_power_domain power_domain;
+
                DRM_DEBUG_KMS("Turning eDP VDD off\n");
 
                pp = ironlake_get_pp_control(intel_dp);
@@ -1160,7 +1218,8 @@ static void edp_panel_vdd_off_sync(struct intel_dp *intel_dp)
                if ((pp & POWER_TARGET_ON) == 0)
                        intel_dp->last_power_cycle = jiffies;
 
-               intel_runtime_pm_put(dev_priv);
+               power_domain = intel_display_port_power_domain(intel_encoder);
+               intel_display_power_put(dev_priv, power_domain);
        }
 }
 
@@ -1170,9 +1229,9 @@ static void edp_panel_vdd_work(struct work_struct *__work)
                                                 struct intel_dp, panel_vdd_work);
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
        edp_panel_vdd_off_sync(intel_dp);
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock(&dev->mode_config.connection_mutex);
 }
 
 static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync)
@@ -1244,8 +1303,11 @@ void intel_edp_panel_on(struct intel_dp *intel_dp)
 
 void intel_edp_panel_off(struct intel_dp *intel_dp)
 {
+       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+       struct intel_encoder *intel_encoder = &intel_dig_port->base;
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
        struct drm_i915_private *dev_priv = dev->dev_private;
+       enum intel_display_power_domain power_domain;
        u32 pp;
        u32 pp_ctrl_reg;
 
@@ -1275,7 +1337,8 @@ void intel_edp_panel_off(struct intel_dp *intel_dp)
        wait_panel_off(intel_dp);
 
        /* We got a reference when we enabled the VDD. */
-       intel_runtime_pm_put(dev_priv);
+       power_domain = intel_display_port_power_domain(intel_encoder);
+       intel_display_power_put(dev_priv, power_domain);
 }
 
 void intel_edp_backlight_on(struct intel_dp *intel_dp)
@@ -1432,6 +1495,8 @@ static bool intel_dp_get_hw_state(struct intel_encoder *encoder,
 
        if (port == PORT_A && IS_GEN7(dev) && !IS_VALLEYVIEW(dev)) {
                *pipe = PORT_TO_PIPE_CPT(tmp);
+       } else if (IS_CHERRYVIEW(dev)) {
+               *pipe = DP_PORT_TO_PIPE_CHV(tmp);
        } else if (!HAS_PCH_CPT(dev) || port == PORT_A) {
                *pipe = PORT_TO_PIPE(tmp);
        } else {
@@ -1479,8 +1544,11 @@ static void intel_dp_get_config(struct intel_encoder *encoder,
        struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
        int dotclock;
 
+       tmp = I915_READ(intel_dp->output_reg);
+       if (tmp & DP_AUDIO_OUTPUT_ENABLE)
+               pipe_config->has_audio = true;
+
        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
@@ -1816,17 +1884,59 @@ static void intel_disable_dp(struct intel_encoder *encoder)
                intel_dp_link_down(intel_dp);
 }
 
-static void intel_post_disable_dp(struct intel_encoder *encoder)
+static void g4x_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;
+
+       if (port != PORT_A)
+               return;
+
+       intel_dp_link_down(intel_dp);
+       ironlake_edp_pll_off(intel_dp);
+}
+
+static void vlv_post_disable_dp(struct intel_encoder *encoder)
+{
+       struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
+
+       intel_dp_link_down(intel_dp);
+}
+
+static void chv_post_disable_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;
+       struct intel_crtc *intel_crtc =
+               to_intel_crtc(encoder->base.crtc);
+       enum dpio_channel ch = vlv_dport_to_channel(dport);
+       enum pipe pipe = intel_crtc->pipe;
+       u32 val;
 
-       if (port == PORT_A || IS_VALLEYVIEW(dev)) {
-               intel_dp_link_down(intel_dp);
-               if (!IS_VALLEYVIEW(dev))
-                       ironlake_edp_pll_off(intel_dp);
-       }
+       intel_dp_link_down(intel_dp);
+
+       mutex_lock(&dev_priv->dpio_lock);
+
+       /* Propagate soft reset to data lane reset */
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch));
+       val |= CHV_PCS_REQ_SOFTRESET_EN;
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val);
+
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch));
+       val |= CHV_PCS_REQ_SOFTRESET_EN;
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val);
+
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch));
+       val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val);
+
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch));
+       val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val);
+
+       mutex_unlock(&dev_priv->dpio_lock);
 }
 
 static void intel_enable_dp(struct intel_encoder *encoder)
@@ -1868,8 +1978,13 @@ static void g4x_pre_enable_dp(struct intel_encoder *encoder)
        struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
        struct intel_digital_port *dport = dp_to_dig_port(intel_dp);
 
-       if (dport->port == PORT_A)
+       intel_dp_prepare(encoder);
+
+       /* Only ilk+ has port A */
+       if (dport->port == PORT_A) {
+               ironlake_set_pll_cpu_edp(intel_dp);
                ironlake_edp_pll_on(intel_dp);
+       }
 }
 
 static void vlv_pre_enable_dp(struct intel_encoder *encoder)
@@ -1921,6 +2036,8 @@ static void vlv_dp_pre_pll_enable(struct intel_encoder *encoder)
        enum dpio_channel port = vlv_dport_to_channel(dport);
        int pipe = intel_crtc->pipe;
 
+       intel_dp_prepare(encoder);
+
        /* Program Tx lane resets to default */
        mutex_lock(&dev_priv->dpio_lock);
        vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW0(port),
@@ -1939,6 +2056,69 @@ static void vlv_dp_pre_pll_enable(struct intel_encoder *encoder)
        mutex_unlock(&dev_priv->dpio_lock);
 }
 
+static void chv_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;
+       struct edp_power_seq power_seq;
+       struct intel_crtc *intel_crtc =
+               to_intel_crtc(encoder->base.crtc);
+       enum dpio_channel ch = vlv_dport_to_channel(dport);
+       int pipe = intel_crtc->pipe;
+       int data, i;
+       u32 val;
+
+       mutex_lock(&dev_priv->dpio_lock);
+
+       /* Deassert soft data lane reset*/
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch));
+       val |= CHV_PCS_REQ_SOFTRESET_EN;
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val);
+
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch));
+       val |= CHV_PCS_REQ_SOFTRESET_EN;
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val);
+
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch));
+       val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val);
+
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch));
+       val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val);
+
+       /* Program Tx lane latency optimal setting*/
+       for (i = 0; i < 4; i++) {
+               /* Set the latency optimal bit */
+               data = (i == 1) ? 0x0 : 0x6;
+               vlv_dpio_write(dev_priv, pipe, CHV_TX_DW11(ch, i),
+                               data << DPIO_FRC_LATENCY_SHFIT);
+
+               /* Set the upar bit */
+               data = (i == 1) ? 0x0 : 0x1;
+               vlv_dpio_write(dev_priv, pipe, CHV_TX_DW14(ch, i),
+                               data << DPIO_UPAR_SHIFT);
+       }
+
+       /* Data lane stagger programming */
+       /* FIXME: Fix up value only after power analysis */
+
+       mutex_unlock(&dev_priv->dpio_lock);
+
+       if (is_edp(intel_dp)) {
+               /* init power sequencer on this pipe and port */
+               intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq);
+               intel_dp_init_panel_power_sequencer_registers(dev, intel_dp,
+                                                             &power_seq);
+       }
+
+       intel_enable_dp(encoder);
+
+       vlv_wait_port_ready(dev_priv, dport);
+}
+
 /*
  * Native read with retry for link status and receiver capability reads for
  * cases where the sink may still be asleep.
@@ -2163,6 +2343,166 @@ static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp)
        return 0;
 }
 
+static uint32_t intel_chv_signal_levels(struct intel_dp *intel_dp)
+{
+       struct drm_device *dev = intel_dp_to_dev(intel_dp);
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_digital_port *dport = dp_to_dig_port(intel_dp);
+       struct intel_crtc *intel_crtc = to_intel_crtc(dport->base.base.crtc);
+       u32 deemph_reg_value, margin_reg_value, val;
+       uint8_t train_set = intel_dp->train_set[0];
+       enum dpio_channel ch = vlv_dport_to_channel(dport);
+       enum pipe pipe = intel_crtc->pipe;
+       int i;
+
+       switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) {
+       case DP_TRAIN_PRE_EMPHASIS_0:
+               switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
+               case DP_TRAIN_VOLTAGE_SWING_400:
+                       deemph_reg_value = 128;
+                       margin_reg_value = 52;
+                       break;
+               case DP_TRAIN_VOLTAGE_SWING_600:
+                       deemph_reg_value = 128;
+                       margin_reg_value = 77;
+                       break;
+               case DP_TRAIN_VOLTAGE_SWING_800:
+                       deemph_reg_value = 128;
+                       margin_reg_value = 102;
+                       break;
+               case DP_TRAIN_VOLTAGE_SWING_1200:
+                       deemph_reg_value = 128;
+                       margin_reg_value = 154;
+                       /* FIXME extra to set for 1200 */
+                       break;
+               default:
+                       return 0;
+               }
+               break;
+       case DP_TRAIN_PRE_EMPHASIS_3_5:
+               switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
+               case DP_TRAIN_VOLTAGE_SWING_400:
+                       deemph_reg_value = 85;
+                       margin_reg_value = 78;
+                       break;
+               case DP_TRAIN_VOLTAGE_SWING_600:
+                       deemph_reg_value = 85;
+                       margin_reg_value = 116;
+                       break;
+               case DP_TRAIN_VOLTAGE_SWING_800:
+                       deemph_reg_value = 85;
+                       margin_reg_value = 154;
+                       break;
+               default:
+                       return 0;
+               }
+               break;
+       case DP_TRAIN_PRE_EMPHASIS_6:
+               switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
+               case DP_TRAIN_VOLTAGE_SWING_400:
+                       deemph_reg_value = 64;
+                       margin_reg_value = 104;
+                       break;
+               case DP_TRAIN_VOLTAGE_SWING_600:
+                       deemph_reg_value = 64;
+                       margin_reg_value = 154;
+                       break;
+               default:
+                       return 0;
+               }
+               break;
+       case DP_TRAIN_PRE_EMPHASIS_9_5:
+               switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
+               case DP_TRAIN_VOLTAGE_SWING_400:
+                       deemph_reg_value = 43;
+                       margin_reg_value = 154;
+                       break;
+               default:
+                       return 0;
+               }
+               break;
+       default:
+               return 0;
+       }
+
+       mutex_lock(&dev_priv->dpio_lock);
+
+       /* Clear calc init */
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW10(ch));
+       val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3);
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val);
+
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch));
+       val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3);
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val);
+
+       /* Program swing deemph */
+       for (i = 0; i < 4; i++) {
+               val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW4(ch, i));
+               val &= ~DPIO_SWING_DEEMPH9P5_MASK;
+               val |= deemph_reg_value << DPIO_SWING_DEEMPH9P5_SHIFT;
+               vlv_dpio_write(dev_priv, pipe, CHV_TX_DW4(ch, i), val);
+       }
+
+       /* Program swing margin */
+       for (i = 0; i < 4; i++) {
+               val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i));
+               val &= ~DPIO_SWING_MARGIN_MASK;
+               val |= margin_reg_value << DPIO_SWING_MARGIN_SHIFT;
+               vlv_dpio_write(dev_priv, pipe, CHV_TX_DW2(ch, i), val);
+       }
+
+       /* Disable unique transition scale */
+       for (i = 0; i < 4; i++) {
+               val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW3(ch, i));
+               val &= ~DPIO_TX_UNIQ_TRANS_SCALE_EN;
+               vlv_dpio_write(dev_priv, pipe, CHV_TX_DW3(ch, i), val);
+       }
+
+       if (((train_set & DP_TRAIN_PRE_EMPHASIS_MASK)
+                       == DP_TRAIN_PRE_EMPHASIS_0) &&
+               ((train_set & DP_TRAIN_VOLTAGE_SWING_MASK)
+                       == DP_TRAIN_VOLTAGE_SWING_1200)) {
+
+               /*
+                * The document said it needs to set bit 27 for ch0 and bit 26
+                * for ch1. Might be a typo in the doc.
+                * For now, for this unique transition scale selection, set bit
+                * 27 for ch0 and ch1.
+                */
+               for (i = 0; i < 4; i++) {
+                       val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW3(ch, i));
+                       val |= DPIO_TX_UNIQ_TRANS_SCALE_EN;
+                       vlv_dpio_write(dev_priv, pipe, CHV_TX_DW3(ch, i), val);
+               }
+
+               for (i = 0; i < 4; i++) {
+                       val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i));
+                       val &= ~(0xff << DPIO_UNIQ_TRANS_SCALE_SHIFT);
+                       val |= (0x9a << DPIO_UNIQ_TRANS_SCALE_SHIFT);
+                       vlv_dpio_write(dev_priv, pipe, CHV_TX_DW2(ch, i), val);
+               }
+       }
+
+       /* Start swing calculation */
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW10(ch));
+       val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3;
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val);
+
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch));
+       val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3;
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val);
+
+       /* LRC Bypass */
+       val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW30);
+       val |= DPIO_LRC_BYPASS;
+       vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW30, val);
+
+       mutex_unlock(&dev_priv->dpio_lock);
+
+       return 0;
+}
+
 static void
 intel_get_adjust_train(struct intel_dp *intel_dp,
                       const uint8_t link_status[DP_LINK_STATUS_SIZE])
@@ -2377,6 +2717,9 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
        } else if (IS_HASWELL(dev)) {
                signal_levels = intel_hsw_signal_levels(train_set);
                mask = DDI_BUF_EMP_MASK;
+       } else if (IS_CHERRYVIEW(dev)) {
+               signal_levels = intel_chv_signal_levels(intel_dp);
+               mask = 0;
        } else if (IS_VALLEYVIEW(dev)) {
                signal_levels = intel_vlv_signal_levels(intel_dp);
                mask = 0;
@@ -2743,22 +3086,7 @@ intel_dp_link_down(struct intel_dp *intel_dp)
                to_intel_crtc(intel_dig_port->base.base.crtc);
        uint32_t DP = intel_dp->DP;
 
-       /*
-        * DDI code has a strict mode set sequence and we should try to respect
-        * it, otherwise we might hang the machine in many different ways. So we
-        * really should be disabling the port only on a complete crtc_disable
-        * sequence. This function is just called under two conditions on DDI
-        * code:
-        * - Link train failed while doing crtc_enable, and on this case we
-        *   really should respect the mode set sequence and wait for a
-        *   crtc_disable.
-        * - Someone turned the monitor off and intel_dp_check_link_status
-        *   called us. We don't need to disable the whole port on this case, so
-        *   when someone turns the monitor on again,
-        *   intel_ddi_prepare_link_retrain will take care of redoing the link
-        *   train.
-        */
-       if (HAS_DDI(dev))
+       if (WARN_ON(HAS_DDI(dev)))
                return;
 
        if (WARN_ON((I915_READ(intel_dp->output_reg) & DP_PORT_EN) == 0))
@@ -2775,9 +3103,6 @@ intel_dp_link_down(struct intel_dp *intel_dp)
        }
        POSTING_READ(intel_dp->output_reg);
 
-       /* We don't really know why we're doing this */
-       intel_wait_for_vblank(dev, intel_crtc->pipe);
-
        if (HAS_PCH_IBX(dev) &&
            I915_READ(intel_dp->output_reg) & DP_PIPEB_SELECT) {
                struct drm_crtc *crtc = intel_dig_port->base.base.crtc;
@@ -2948,6 +3273,7 @@ intel_dp_check_link_status(struct intel_dp *intel_dp)
        u8 sink_irq_vector;
        u8 link_status[DP_LINK_STATUS_SIZE];
 
+       /* FIXME: This access isn't protected by any locks. */
        if (!intel_encoder->connectors_active)
                return;
 
@@ -2980,7 +3306,7 @@ intel_dp_check_link_status(struct intel_dp *intel_dp)
 
        if (!drm_dp_channel_eq_ok(link_status, intel_dp->lane_count)) {
                DRM_DEBUG_KMS("%s: channel EQ not ok, retraining\n",
-                             drm_get_encoder_name(&intel_encoder->base));
+                             intel_encoder->base.name);
                intel_dp_start_link_train(intel_dp);
                intel_dp_complete_link_train(intel_dp);
                intel_dp_stop_link_train(intel_dp);
@@ -3166,7 +3492,7 @@ intel_dp_detect(struct drm_connector *connector, bool force)
        intel_display_power_get(dev_priv, power_domain);
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
-                     connector->base.id, drm_get_connector_name(connector));
+                     connector->base.id, connector->name);
 
        intel_dp->has_audio = false;
 
@@ -3374,13 +3700,13 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder)
        struct intel_dp *intel_dp = &intel_dig_port->dp;
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
 
-       drm_dp_aux_unregister_i2c_bus(&intel_dp->aux);
+       drm_dp_aux_unregister(&intel_dp->aux);
        drm_encoder_cleanup(encoder);
        if (is_edp(intel_dp)) {
                cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
-               mutex_lock(&dev->mode_config.mutex);
+               drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
                edp_panel_vdd_off_sync(intel_dp);
-               mutex_unlock(&dev->mode_config.mutex);
+               drm_modeset_unlock(&dev->mode_config.connection_mutex);
        }
        kfree(intel_dig_port);
 }
@@ -3651,6 +3977,130 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev,
                      I915_READ(pp_div_reg));
 }
 
+void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_encoder *encoder;
+       struct intel_dp *intel_dp = NULL;
+       struct intel_crtc_config *config = NULL;
+       struct intel_crtc *intel_crtc = NULL;
+       struct intel_connector *intel_connector = dev_priv->drrs.connector;
+       u32 reg, val;
+       enum edp_drrs_refresh_rate_type index = DRRS_HIGH_RR;
+
+       if (refresh_rate <= 0) {
+               DRM_DEBUG_KMS("Refresh rate should be positive non-zero.\n");
+               return;
+       }
+
+       if (intel_connector == NULL) {
+               DRM_DEBUG_KMS("DRRS supported for eDP only.\n");
+               return;
+       }
+
+       if (INTEL_INFO(dev)->gen < 8 && intel_edp_is_psr_enabled(dev)) {
+               DRM_DEBUG_KMS("DRRS is disabled as PSR is enabled\n");
+               return;
+       }
+
+       encoder = intel_attached_encoder(&intel_connector->base);
+       intel_dp = enc_to_intel_dp(&encoder->base);
+       intel_crtc = encoder->new_crtc;
+
+       if (!intel_crtc) {
+               DRM_DEBUG_KMS("DRRS: intel_crtc not initialized\n");
+               return;
+       }
+
+       config = &intel_crtc->config;
+
+       if (intel_dp->drrs_state.type < SEAMLESS_DRRS_SUPPORT) {
+               DRM_DEBUG_KMS("Only Seamless DRRS supported.\n");
+               return;
+       }
+
+       if (intel_connector->panel.downclock_mode->vrefresh == refresh_rate)
+               index = DRRS_LOW_RR;
+
+       if (index == intel_dp->drrs_state.refresh_rate_type) {
+               DRM_DEBUG_KMS(
+                       "DRRS requested for previously set RR...ignoring\n");
+               return;
+       }
+
+       if (!intel_crtc->active) {
+               DRM_DEBUG_KMS("eDP encoder disabled. CRTC not Active\n");
+               return;
+       }
+
+       if (INTEL_INFO(dev)->gen > 6 && INTEL_INFO(dev)->gen < 8) {
+               reg = PIPECONF(intel_crtc->config.cpu_transcoder);
+               val = I915_READ(reg);
+               if (index > DRRS_HIGH_RR) {
+                       val |= PIPECONF_EDP_RR_MODE_SWITCH;
+                       intel_dp_set_m2_n2(intel_crtc, &config->dp_m2_n2);
+               } else {
+                       val &= ~PIPECONF_EDP_RR_MODE_SWITCH;
+               }
+               I915_WRITE(reg, val);
+       }
+
+       /*
+        * mutex taken to ensure that there is no race between differnt
+        * drrs calls trying to update refresh rate. This scenario may occur
+        * in future when idleness detection based DRRS in kernel and
+        * possible calls from user space to set differnt RR are made.
+        */
+
+       mutex_lock(&intel_dp->drrs_state.mutex);
+
+       intel_dp->drrs_state.refresh_rate_type = index;
+
+       mutex_unlock(&intel_dp->drrs_state.mutex);
+
+       DRM_DEBUG_KMS("eDP Refresh Rate set to : %dHz\n", refresh_rate);
+}
+
+static struct drm_display_mode *
+intel_dp_drrs_init(struct intel_digital_port *intel_dig_port,
+                       struct intel_connector *intel_connector,
+                       struct drm_display_mode *fixed_mode)
+{
+       struct drm_connector *connector = &intel_connector->base;
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct drm_device *dev = intel_dig_port->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_display_mode *downclock_mode = NULL;
+
+       if (INTEL_INFO(dev)->gen <= 6) {
+               DRM_DEBUG_KMS("DRRS supported for Gen7 and above\n");
+               return NULL;
+       }
+
+       if (dev_priv->vbt.drrs_type != SEAMLESS_DRRS_SUPPORT) {
+               DRM_INFO("VBT doesn't support DRRS\n");
+               return NULL;
+       }
+
+       downclock_mode = intel_find_panel_downclock
+                                       (dev, fixed_mode, connector);
+
+       if (!downclock_mode) {
+               DRM_INFO("DRRS not supported\n");
+               return NULL;
+       }
+
+       dev_priv->drrs.connector = intel_connector;
+
+       mutex_init(&intel_dp->drrs_state.mutex);
+
+       intel_dp->drrs_state.type = dev_priv->vbt.drrs_type;
+
+       intel_dp->drrs_state.refresh_rate_type = DRRS_HIGH_RR;
+       DRM_INFO("seamless DRRS supported for eDP panel.\n");
+       return downclock_mode;
+}
+
 static bool intel_edp_init_connector(struct intel_dp *intel_dp,
                                     struct intel_connector *intel_connector,
                                     struct edp_power_seq *power_seq)
@@ -3661,10 +4111,13 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
        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 drm_display_mode *downclock_mode = NULL;
        bool has_dpcd;
        struct drm_display_mode *scan;
        struct edid *edid;
 
+       intel_dp->drrs_state.type = DRRS_NOT_SUPPORTED;
+
        if (!is_edp(intel_dp))
                return true;
 
@@ -3715,6 +4168,9 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
        list_for_each_entry(scan, &connector->probed_modes, head) {
                if ((scan->type & DRM_MODE_TYPE_PREFERRED)) {
                        fixed_mode = drm_mode_duplicate(dev, scan);
+                       downclock_mode = intel_dp_drrs_init(
+                                               intel_dig_port,
+                                               intel_connector, fixed_mode);
                        break;
                }
        }
@@ -3728,7 +4184,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
        }
        mutex_unlock(&dev->mode_config.mutex);
 
-       intel_panel_init(&intel_connector->panel, fixed_mode, NULL);
+       intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode);
        intel_panel_setup_backlight(connector);
 
        return true;
@@ -3826,12 +4282,12 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
        intel_dp->psr_setup_done = false;
 
        if (!intel_edp_init_connector(intel_dp, intel_connector, &power_seq)) {
-               drm_dp_aux_unregister_i2c_bus(&intel_dp->aux);
+               drm_dp_aux_unregister(&intel_dp->aux);
                if (is_edp(intel_dp)) {
                        cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
-                       mutex_lock(&dev->mode_config.mutex);
+                       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
                        edp_panel_vdd_off_sync(intel_dp);
-                       mutex_unlock(&dev->mode_config.mutex);
+                       drm_modeset_unlock(&dev->mode_config.connection_mutex);
                }
                drm_sysfs_connector_remove(connector);
                drm_connector_cleanup(connector);
@@ -3877,25 +4333,36 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
                         DRM_MODE_ENCODER_TMDS);
 
        intel_encoder->compute_config = intel_dp_compute_config;
-       intel_encoder->mode_set = intel_dp_mode_set;
        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)) {
+       if (IS_CHERRYVIEW(dev)) {
+               intel_encoder->pre_enable = chv_pre_enable_dp;
+               intel_encoder->enable = vlv_enable_dp;
+               intel_encoder->post_disable = chv_post_disable_dp;
+       } else if (IS_VALLEYVIEW(dev)) {
                intel_encoder->pre_pll_enable = vlv_dp_pre_pll_enable;
                intel_encoder->pre_enable = vlv_pre_enable_dp;
                intel_encoder->enable = vlv_enable_dp;
+               intel_encoder->post_disable = vlv_post_disable_dp;
        } else {
                intel_encoder->pre_enable = g4x_pre_enable_dp;
                intel_encoder->enable = g4x_enable_dp;
+               intel_encoder->post_disable = g4x_post_disable_dp;
        }
 
        intel_dig_port->port = port;
        intel_dig_port->dp.output_reg = output_reg;
 
        intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
-       intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
+       if (IS_CHERRYVIEW(dev)) {
+               if (port == PORT_D)
+                       intel_encoder->crtc_mask = 1 << 2;
+               else
+                       intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
+       } else {
+               intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
+       }
        intel_encoder->cloneable = 0;
        intel_encoder->hot_plug = intel_dp_hot_plug;
 
index 328b1a70264b4c12a07400de7284b2968813aa94..bda0ae3d80cc667a5f2706fade10ed58825d34ff 100644 (file)
@@ -81,8 +81,8 @@
 /* Maximum cursor sizes */
 #define GEN2_CURSOR_WIDTH 64
 #define GEN2_CURSOR_HEIGHT 64
-#define CURSOR_WIDTH 256
-#define CURSOR_HEIGHT 256
+#define MAX_CURSOR_WIDTH 256
+#define MAX_CURSOR_HEIGHT 256
 
 #define INTEL_I2C_BUS_DVO 1
 #define INTEL_I2C_BUS_SDVO 2
 #define INTEL_DVO_CHIP_TMDS 2
 #define INTEL_DVO_CHIP_TVOUT 4
 
-#define INTEL_DSI_COMMAND_MODE 0
-#define INTEL_DSI_VIDEO_MODE   1
+#define INTEL_DSI_VIDEO_MODE   0
+#define INTEL_DSI_COMMAND_MODE 1
 
 struct intel_framebuffer {
        struct drm_framebuffer base;
@@ -273,6 +273,13 @@ struct intel_crtc_config {
         * accordingly. */
        bool has_dp_encoder;
 
+       /* Whether we should send NULL infoframes. Required for audio. */
+       bool has_hdmi_sink;
+
+       /* Audio enabled on this pipe. Only valid if either has_hdmi_sink or
+        * has_dp_encoder is set. */
+       bool has_audio;
+
        /*
         * Enable dithering, used when the selected pipe bpp doesn't match the
         * plane bpp.
@@ -306,6 +313,9 @@ struct intel_crtc_config {
        int pipe_bpp;
        struct intel_link_m_n dp_m_n;
 
+       /* m2_n2 for eDP downclock */
+       struct intel_link_m_n dp_m2_n2;
+
        /*
         * Frequence the dpll for the port should run at. Differs from the
         * adjusted dotclock e.g. for DP or 12bpc hdmi mode. This is also
@@ -343,6 +353,9 @@ struct intel_pipe_wm {
        struct intel_wm_level wm[5];
        uint32_t linetime;
        bool fbc_wm_enabled;
+       bool pipe_enabled;
+       bool sprites_enabled;
+       bool sprites_scaled;
 };
 
 struct intel_crtc {
@@ -357,7 +370,6 @@ struct intel_crtc {
         */
        bool active;
        unsigned long enabled_power_domains;
-       bool eld_vld;
        bool primary_enabled; /* is the primary plane (partially) visible? */
        bool lowfreq_avail;
        struct intel_overlay *overlay;
@@ -374,8 +386,8 @@ struct intel_crtc {
        uint32_t cursor_addr;
        int16_t cursor_x, cursor_y;
        int16_t cursor_width, cursor_height;
-       int16_t max_cursor_width, max_cursor_height;
-       bool cursor_visible;
+       uint32_t cursor_cntl;
+       uint32_t cursor_base;
 
        struct intel_plane_config plane_config;
        struct intel_crtc_config config;
@@ -396,6 +408,10 @@ struct intel_crtc {
                /* watermarks currently being used  */
                struct intel_pipe_wm active;
        } wm;
+
+       wait_queue_head_t vbl_wait;
+
+       int scanline_offset;
 };
 
 struct intel_plane_wm_parameters {
@@ -479,11 +495,23 @@ struct intel_hdmi {
                                enum hdmi_infoframe_type type,
                                const void *frame, ssize_t len);
        void (*set_infoframes)(struct drm_encoder *encoder,
+                              bool enable,
                               struct drm_display_mode *adjusted_mode);
 };
 
 #define DP_MAX_DOWNSTREAM_PORTS                0x10
 
+/**
+ * HIGH_RR is the highest eDP panel refresh rate read from EDID
+ * LOW_RR is the lowest eDP panel refresh rate found from EDID
+ * parsing for same resolution.
+ */
+enum edp_drrs_refresh_rate_type {
+       DRRS_HIGH_RR,
+       DRRS_LOW_RR,
+       DRRS_MAX_RR, /* RR count */
+};
+
 struct intel_dp {
        uint32_t output_reg;
        uint32_t aux_ch_ctl_reg;
@@ -522,6 +550,12 @@ struct intel_dp {
                                     bool has_aux_irq,
                                     int send_bytes,
                                     uint32_t aux_clock_divider);
+       struct {
+               enum drrs_support_type type;
+               enum edp_drrs_refresh_rate_type refresh_rate_type;
+               struct mutex mutex;
+       } drrs_state;
+
 };
 
 struct intel_digital_port {
@@ -537,6 +571,7 @@ vlv_dport_to_channel(struct intel_digital_port *dport)
 {
        switch (dport->port) {
        case PORT_B:
+       case PORT_D:
                return DPIO_CH0;
        case PORT_C:
                return DPIO_CH1;
@@ -545,6 +580,20 @@ vlv_dport_to_channel(struct intel_digital_port *dport)
        }
 }
 
+static inline int
+vlv_pipe_to_channel(enum pipe pipe)
+{
+       switch (pipe) {
+       case PIPE_A:
+       case PIPE_C:
+               return DPIO_CH0;
+       case PIPE_B:
+               return DPIO_CH1;
+       default:
+               BUG();
+       }
+}
+
 static inline struct drm_crtc *
 intel_get_crtc_for_pipe(struct drm_device *dev, int pipe)
 {
@@ -569,6 +618,8 @@ struct intel_unpin_work {
 #define INTEL_FLIP_INACTIVE    0
 #define INTEL_FLIP_PENDING     1
 #define INTEL_FLIP_COMPLETE    2
+       u32 flip_count;
+       u32 gtt_offset;
        bool enable_stall_check;
 };
 
@@ -620,8 +671,6 @@ hdmi_to_dig_port(struct intel_hdmi *intel_hdmi)
 /* i915_irq.c */
 bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
                                           enum pipe pipe, bool enable);
-bool __intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
-                                            enum pipe pipe, bool enable);
 bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
                                           enum transcoder pch_transcoder,
                                           bool enable);
@@ -629,8 +678,12 @@ void ilk_enable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask);
 void ilk_disable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask);
 void snb_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask);
 void snb_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask);
-void hsw_runtime_pm_disable_interrupts(struct drm_device *dev);
-void hsw_runtime_pm_restore_interrupts(struct drm_device *dev);
+void bdw_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask);
+void bdw_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask);
+void intel_runtime_pm_disable_interrupts(struct drm_device *dev);
+void intel_runtime_pm_restore_interrupts(struct drm_device *dev);
+int intel_get_crtc_scanline(struct intel_crtc *crtc);
+void i9xx_check_fifo_underruns(struct drm_device *dev);
 
 
 /* intel_crt.c */
@@ -666,9 +719,10 @@ void intel_ddi_get_config(struct intel_encoder *encoder,
 const char *intel_output_name(int output);
 bool intel_has_pending_fb_unpin(struct drm_device *dev);
 int intel_pch_rawclk(struct drm_device *dev);
+int valleyview_cur_cdclk(struct drm_i915_private *dev_priv);
 void intel_mark_busy(struct drm_device *dev);
 void intel_mark_fb_busy(struct drm_i915_gem_object *obj,
-                       struct intel_ring_buffer *ring);
+                       struct intel_engine_cs *ring);
 void intel_mark_idle(struct drm_device *dev);
 void intel_crtc_restore_mode(struct drm_crtc *crtc);
 void intel_crtc_update_dpms(struct drm_crtc *crtc);
@@ -695,12 +749,14 @@ void vlv_wait_port_ready(struct drm_i915_private *dev_priv,
                         struct intel_digital_port *dport);
 bool intel_get_load_detect_pipe(struct drm_connector *connector,
                                struct drm_display_mode *mode,
-                               struct intel_load_detect_pipe *old);
+                               struct intel_load_detect_pipe *old,
+                               struct drm_modeset_acquire_ctx *ctx);
 void intel_release_load_detect_pipe(struct drm_connector *connector,
-                                   struct intel_load_detect_pipe *old);
+                                   struct intel_load_detect_pipe *old,
+                                   struct drm_modeset_acquire_ctx *ctx);
 int intel_pin_and_fence_fb_obj(struct drm_device *dev,
                               struct drm_i915_gem_object *obj,
-                              struct intel_ring_buffer *pipelined);
+                              struct intel_engine_cs *pipelined);
 void intel_unpin_fb_obj(struct drm_i915_gem_object *obj);
 struct drm_framebuffer *
 __intel_framebuffer_create(struct drm_device *dev,
@@ -751,6 +807,8 @@ int valleyview_get_vco(struct drm_i915_private *dev_priv);
 void intel_mode_from_pipe_config(struct drm_display_mode *mode,
                                 struct intel_crtc_config *pipe_config);
 int intel_format_to_fourcc(int format);
+void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc);
+
 
 /* intel_dp.c */
 void intel_dp_init(struct drm_device *dev, int output_reg, enum port port);
@@ -774,7 +832,7 @@ void intel_edp_panel_off(struct intel_dp *intel_dp);
 void intel_edp_psr_enable(struct intel_dp *intel_dp);
 void intel_edp_psr_disable(struct intel_dp *intel_dp);
 void intel_edp_psr_update(struct drm_device *dev);
-
+void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate);
 
 /* intel_dsi.c */
 bool intel_dsi_init(struct drm_device *dev);
@@ -876,6 +934,7 @@ extern struct drm_display_mode *intel_find_panel_downclock(
 /* intel_pm.c */
 void intel_init_clock_gating(struct drm_device *dev);
 void intel_suspend_hw(struct drm_device *dev);
+int ilk_wm_max_level(const struct drm_device *dev);
 void intel_update_watermarks(struct drm_crtc *crtc);
 void intel_update_sprite_watermarks(struct drm_plane *plane,
                                    struct drm_crtc *crtc,
@@ -902,6 +961,7 @@ void intel_init_gt_powersave(struct drm_device *dev);
 void intel_cleanup_gt_powersave(struct drm_device *dev);
 void intel_enable_gt_powersave(struct drm_device *dev);
 void intel_disable_gt_powersave(struct drm_device *dev);
+void intel_reset_gt_powersave(struct drm_device *dev);
 void ironlake_teardown_rc6(struct drm_device *dev);
 void gen6_update_ring_freq(struct drm_device *dev);
 void gen6_rps_idle(struct drm_i915_private *dev_priv);
@@ -909,11 +969,13 @@ void gen6_rps_boost(struct drm_i915_private *dev_priv);
 void intel_aux_display_runtime_get(struct drm_i915_private *dev_priv);
 void intel_aux_display_runtime_put(struct drm_i915_private *dev_priv);
 void intel_runtime_pm_get(struct drm_i915_private *dev_priv);
+void intel_runtime_pm_get_noresume(struct drm_i915_private *dev_priv);
 void intel_runtime_pm_put(struct drm_i915_private *dev_priv);
 void intel_init_runtime_pm(struct drm_i915_private *dev_priv);
 void intel_fini_runtime_pm(struct drm_i915_private *dev_priv);
 void ilk_wm_get_hw_state(struct drm_device *dev);
-
+void __vlv_set_power_well(struct drm_i915_private *dev_priv,
+                         enum punit_power_well power_well_id, bool enable);
 
 /* intel_sdvo.c */
 bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob);
index 33656647f8bcf566ea8fba54f898ec6fc71b2b5b..02f99d768d49f4f76d6eb1ec9de683911d3332c5 100644 (file)
 
 /* the sub-encoders aka panel drivers */
 static const struct intel_dsi_device intel_dsi_devices[] = {
+       {
+               .panel_id = MIPI_DSI_GENERIC_PANEL_ID,
+               .name = "vbt-generic-dsi-vid-mode-display",
+               .dev_ops = &vbt_generic_dsi_display_ops,
+       },
 };
 
 static void band_gap_reset(struct drm_i915_private *dev_priv)
@@ -59,12 +64,12 @@ static struct intel_dsi *intel_attached_dsi(struct drm_connector *connector)
 
 static inline bool is_vid_mode(struct intel_dsi *intel_dsi)
 {
-       return intel_dsi->dev.type == INTEL_DSI_VIDEO_MODE;
+       return intel_dsi->operation_mode == INTEL_DSI_VIDEO_MODE;
 }
 
 static inline bool is_cmd_mode(struct intel_dsi *intel_dsi)
 {
-       return intel_dsi->dev.type == INTEL_DSI_COMMAND_MODE;
+       return intel_dsi->operation_mode == INTEL_DSI_COMMAND_MODE;
 }
 
 static void intel_dsi_hot_plug(struct intel_encoder *encoder)
@@ -94,13 +99,6 @@ static bool intel_dsi_compute_config(struct intel_encoder *encoder,
        return true;
 }
 
-static void intel_dsi_pre_pll_enable(struct intel_encoder *encoder)
-{
-       DRM_DEBUG_KMS("\n");
-
-       vlv_enable_dsi_pll(encoder);
-}
-
 static void intel_dsi_device_ready(struct intel_encoder *encoder)
 {
        struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
@@ -110,6 +108,15 @@ static void intel_dsi_device_ready(struct intel_encoder *encoder)
 
        DRM_DEBUG_KMS("\n");
 
+       mutex_lock(&dev_priv->dpio_lock);
+       /* program rcomp for compliance, reduce from 50 ohms to 45 ohms
+        * needed everytime after power gate */
+       vlv_flisdsi_write(dev_priv, 0x04, 0x0004);
+       mutex_unlock(&dev_priv->dpio_lock);
+
+       /* bandgap reset is needed after everytime we do power gate */
+       band_gap_reset(dev_priv);
+
        val = I915_READ(MIPI_PORT_CTRL(pipe));
        I915_WRITE(MIPI_PORT_CTRL(pipe), val | LP_OUTPUT_HOLD);
        usleep_range(1000, 1500);
@@ -122,21 +129,6 @@ static void intel_dsi_device_ready(struct intel_encoder *encoder)
        I915_WRITE(MIPI_DEVICE_READY(pipe), DEVICE_READY);
        usleep_range(2000, 2500);
 }
-static void intel_dsi_pre_enable(struct intel_encoder *encoder)
-{
-       struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
-
-       DRM_DEBUG_KMS("\n");
-
-       if (intel_dsi->dev.dev_ops->panel_reset)
-               intel_dsi->dev.dev_ops->panel_reset(&intel_dsi->dev);
-
-       /* put device in ready state */
-       intel_dsi_device_ready(encoder);
-
-       if (intel_dsi->dev.dev_ops->send_otp_cmds)
-               intel_dsi->dev.dev_ops->send_otp_cmds(&intel_dsi->dev);
-}
 
 static void intel_dsi_enable(struct intel_encoder *encoder)
 {
@@ -153,18 +145,78 @@ static void intel_dsi_enable(struct intel_encoder *encoder)
                I915_WRITE(MIPI_MAX_RETURN_PKT_SIZE(pipe), 8 * 4);
        else {
                msleep(20); /* XXX */
-               dpi_send_cmd(intel_dsi, TURN_ON);
+               dpi_send_cmd(intel_dsi, TURN_ON, DPI_LP_MODE_EN);
                msleep(100);
 
+               if (intel_dsi->dev.dev_ops->enable)
+                       intel_dsi->dev.dev_ops->enable(&intel_dsi->dev);
+
                /* assert ip_tg_enable signal */
                temp = I915_READ(MIPI_PORT_CTRL(pipe)) & ~LANE_CONFIGURATION_MASK;
                temp = temp | intel_dsi->port_bits;
                I915_WRITE(MIPI_PORT_CTRL(pipe), temp | DPI_ENABLE);
                POSTING_READ(MIPI_PORT_CTRL(pipe));
        }
+}
+
+static void intel_dsi_pre_enable(struct intel_encoder *encoder)
+{
+       struct drm_device *dev = encoder->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
+       enum pipe pipe = intel_crtc->pipe;
+       u32 tmp;
+
+       DRM_DEBUG_KMS("\n");
+
+       /* Disable DPOunit clock gating, can stall pipe
+        * and we need DPLL REFA always enabled */
+       tmp = I915_READ(DPLL(pipe));
+       tmp |= DPLL_REFA_CLK_ENABLE_VLV;
+       I915_WRITE(DPLL(pipe), tmp);
+
+       tmp = I915_READ(DSPCLK_GATE_D);
+       tmp |= DPOUNIT_CLOCK_GATE_DISABLE;
+       I915_WRITE(DSPCLK_GATE_D, tmp);
+
+       /* put device in ready state */
+       intel_dsi_device_ready(encoder);
+
+       msleep(intel_dsi->panel_on_delay);
+
+       if (intel_dsi->dev.dev_ops->panel_reset)
+               intel_dsi->dev.dev_ops->panel_reset(&intel_dsi->dev);
+
+       if (intel_dsi->dev.dev_ops->send_otp_cmds)
+               intel_dsi->dev.dev_ops->send_otp_cmds(&intel_dsi->dev);
 
-       if (intel_dsi->dev.dev_ops->enable)
-               intel_dsi->dev.dev_ops->enable(&intel_dsi->dev);
+       /* Enable port in pre-enable phase itself because as per hw team
+        * recommendation, port should be enabled befor plane & pipe */
+       intel_dsi_enable(encoder);
+}
+
+static void intel_dsi_enable_nop(struct intel_encoder *encoder)
+{
+       DRM_DEBUG_KMS("\n");
+
+       /* for DSI port enable has to be done before pipe
+        * and plane enable, so port enable is done in
+        * pre_enable phase itself unlike other encoders
+        */
+}
+
+static void intel_dsi_pre_disable(struct intel_encoder *encoder)
+{
+       struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
+
+       DRM_DEBUG_KMS("\n");
+
+       if (is_vid_mode(intel_dsi)) {
+               /* Send Shutdown command to the panel in LP mode */
+               dpi_send_cmd(intel_dsi, SHUTDOWN, DPI_LP_MODE_EN);
+               msleep(10);
+       }
 }
 
 static void intel_dsi_disable(struct intel_encoder *encoder)
@@ -179,9 +231,6 @@ static void intel_dsi_disable(struct intel_encoder *encoder)
        DRM_DEBUG_KMS("\n");
 
        if (is_vid_mode(intel_dsi)) {
-               dpi_send_cmd(intel_dsi, SHUTDOWN);
-               msleep(10);
-
                /* de-assert ip_tg_enable signal */
                temp = I915_READ(MIPI_PORT_CTRL(pipe));
                I915_WRITE(MIPI_PORT_CTRL(pipe), temp & ~DPI_ENABLE);
@@ -190,6 +239,23 @@ static void intel_dsi_disable(struct intel_encoder *encoder)
                msleep(2);
        }
 
+       /* Panel commands can be sent when clock is in LP11 */
+       I915_WRITE(MIPI_DEVICE_READY(pipe), 0x0);
+
+       temp = I915_READ(MIPI_CTRL(pipe));
+       temp &= ~ESCAPE_CLOCK_DIVIDER_MASK;
+       I915_WRITE(MIPI_CTRL(pipe), temp |
+                       intel_dsi->escape_clk_div <<
+                       ESCAPE_CLOCK_DIVIDER_SHIFT);
+
+       I915_WRITE(MIPI_EOT_DISABLE(pipe), CLOCKSTOP);
+
+       temp = I915_READ(MIPI_DSI_FUNC_PRG(pipe));
+       temp &= ~VID_MODE_FORMAT_MASK;
+       I915_WRITE(MIPI_DSI_FUNC_PRG(pipe), temp);
+
+       I915_WRITE(MIPI_DEVICE_READY(pipe), 0x1);
+
        /* if disable packets are sent before sending shutdown packet then in
         * some next enable sequence send turn on packet error is observed */
        if (intel_dsi->dev.dev_ops->disable)
@@ -227,16 +293,28 @@ static void intel_dsi_clear_device_ready(struct intel_encoder *encoder)
 
        vlv_disable_dsi_pll(encoder);
 }
+
 static void intel_dsi_post_disable(struct intel_encoder *encoder)
 {
+       struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
        struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
+       u32 val;
 
        DRM_DEBUG_KMS("\n");
 
+       intel_dsi_disable(encoder);
+
        intel_dsi_clear_device_ready(encoder);
 
+       val = I915_READ(DSPCLK_GATE_D);
+       val &= ~DPOUNIT_CLOCK_GATE_DISABLE;
+       I915_WRITE(DSPCLK_GATE_D, val);
+
        if (intel_dsi->dev.dev_ops->disable_panel_power)
                intel_dsi->dev.dev_ops->disable_panel_power(&intel_dsi->dev);
+
+       msleep(intel_dsi->panel_off_delay);
+       msleep(intel_dsi->panel_pwr_cycle_delay);
 }
 
 static bool intel_dsi_get_hw_state(struct intel_encoder *encoder,
@@ -364,7 +442,7 @@ static void set_dsi_timings(struct drm_encoder *encoder,
        I915_WRITE(MIPI_VBP_COUNT(pipe), vbp);
 }
 
-static void intel_dsi_mode_set(struct intel_encoder *intel_encoder)
+static void intel_dsi_prepare(struct intel_encoder *intel_encoder)
 {
        struct drm_encoder *encoder = &intel_encoder->base;
        struct drm_device *dev = encoder->dev;
@@ -379,9 +457,6 @@ static void intel_dsi_mode_set(struct intel_encoder *intel_encoder)
 
        DRM_DEBUG_KMS("pipe %c\n", pipe_name(pipe));
 
-       /* XXX: Location of the call */
-       band_gap_reset(dev_priv);
-
        /* escape clock divider, 20MHz, shared for A and C. device ready must be
         * off when doing this! txclkesc? */
        tmp = I915_READ(MIPI_CTRL(0));
@@ -452,10 +527,20 @@ static void intel_dsi_mode_set(struct intel_encoder *intel_encoder)
        /* dphy stuff */
 
        /* in terms of low power clock */
-       I915_WRITE(MIPI_INIT_COUNT(pipe), txclkesc(ESCAPE_CLOCK_DIVIDER_1, 100));
+       I915_WRITE(MIPI_INIT_COUNT(pipe), txclkesc(intel_dsi->escape_clk_div, 100));
+
+       val = 0;
+       if (intel_dsi->eotp_pkt == 0)
+               val |= EOT_DISABLE;
+
+       if (intel_dsi->clock_stop)
+               val |= CLOCKSTOP;
 
        /* recovery disables */
-       I915_WRITE(MIPI_EOT_DISABLE(pipe), intel_dsi->eot_disable);
+       I915_WRITE(MIPI_EOT_DISABLE(pipe), val);
+
+       /* in terms of low power clock */
+       I915_WRITE(MIPI_INIT_COUNT(pipe), intel_dsi->init_count);
 
        /* in terms of txbyteclkhs. actual high to low switch +
         * MIPI_STOP_STATE_STALL * MIPI_LP_BYTECLK.
@@ -484,9 +569,23 @@ static void intel_dsi_mode_set(struct intel_encoder *intel_encoder)
                   intel_dsi->clk_hs_to_lp_count << HS_LP_PWR_SW_CNT_SHIFT);
 
        if (is_vid_mode(intel_dsi))
+               /* Some panels might have resolution which is not a multiple of
+                * 64 like 1366 x 768. Enable RANDOM resolution support for such
+                * panels by default */
                I915_WRITE(MIPI_VIDEO_MODE_FORMAT(pipe),
                                intel_dsi->video_frmt_cfg_bits |
-                               intel_dsi->video_mode_format);
+                               intel_dsi->video_mode_format |
+                               IP_TG_CONFIG |
+                               RANDOM_DPI_DISPLAY_RESOLUTION);
+}
+
+static void intel_dsi_pre_pll_enable(struct intel_encoder *encoder)
+{
+       DRM_DEBUG_KMS("\n");
+
+       intel_dsi_prepare(encoder);
+
+       vlv_enable_dsi_pll(encoder);
 }
 
 static enum drm_connector_status
@@ -566,11 +665,16 @@ bool intel_dsi_init(struct drm_device *dev)
        struct intel_connector *intel_connector;
        struct drm_connector *connector;
        struct drm_display_mode *fixed_mode = NULL;
+       struct drm_i915_private *dev_priv = dev->dev_private;
        const struct intel_dsi_device *dsi;
        unsigned int i;
 
        DRM_DEBUG_KMS("\n");
 
+       /* There is no detection method for MIPI so rely on VBT */
+       if (!dev_priv->vbt.has_mipi)
+               return false;
+
        intel_dsi = kzalloc(sizeof(*intel_dsi), GFP_KERNEL);
        if (!intel_dsi)
                return false;
@@ -585,6 +689,13 @@ bool intel_dsi_init(struct drm_device *dev)
        encoder = &intel_encoder->base;
        intel_dsi->attached_connector = intel_connector;
 
+       if (IS_VALLEYVIEW(dev)) {
+               dev_priv->mipi_mmio_base = VLV_MIPI_BASE;
+       } else {
+               DRM_ERROR("Unsupported Mipi device to reg base");
+               return false;
+       }
+
        connector = &intel_connector->base;
 
        drm_encoder_init(dev, encoder, &intel_dsi_funcs, DRM_MODE_ENCODER_DSI);
@@ -594,9 +705,8 @@ bool intel_dsi_init(struct drm_device *dev)
        intel_encoder->compute_config = intel_dsi_compute_config;
        intel_encoder->pre_pll_enable = intel_dsi_pre_pll_enable;
        intel_encoder->pre_enable = intel_dsi_pre_enable;
-       intel_encoder->enable = intel_dsi_enable;
-       intel_encoder->mode_set = intel_dsi_mode_set;
-       intel_encoder->disable = intel_dsi_disable;
+       intel_encoder->enable = intel_dsi_enable_nop;
+       intel_encoder->disable = intel_dsi_pre_disable;
        intel_encoder->post_disable = intel_dsi_post_disable;
        intel_encoder->get_hw_state = intel_dsi_get_hw_state;
        intel_encoder->get_config = intel_dsi_get_config;
index b4a27cec882f76d7cab17400d8adf438532b4c9d..31db33d3e5cc5d0616d36d69a6230ba7fe496d40 100644 (file)
@@ -31,7 +31,6 @@
 struct intel_dsi_device {
        unsigned int panel_id;
        const char *name;
-       int type;
        const struct intel_dsi_dev_ops *dev_ops;
        void *dev_priv;
 };
@@ -85,6 +84,9 @@ struct intel_dsi {
        /* virtual channel */
        int channel;
 
+       /* Video mode or command mode */
+       u16 operation_mode;
+
        /* number of DSI lanes */
        unsigned int lane_count;
 
@@ -95,8 +97,10 @@ struct intel_dsi {
        u32 video_mode_format;
 
        /* eot for MIPI_EOT_DISABLE register */
-       u32 eot_disable;
+       u8 eotp_pkt;
+       u8 clock_stop;
 
+       u8 escape_clk_div;
        u32 port_bits;
        u32 bw_timer;
        u32 dphy_reg;
@@ -110,6 +114,15 @@ struct intel_dsi {
        u16 hs_to_lp_count;
        u16 clk_lp_to_hs_count;
        u16 clk_hs_to_lp_count;
+
+       u16 init_count;
+
+       /* all delays in ms */
+       u16 backlight_off_delay;
+       u16 backlight_on_delay;
+       u16 panel_on_delay;
+       u16 panel_off_delay;
+       u16 panel_pwr_cycle_delay;
 };
 
 static inline struct intel_dsi *enc_to_intel_dsi(struct drm_encoder *encoder)
@@ -120,4 +133,6 @@ static inline struct intel_dsi *enc_to_intel_dsi(struct drm_encoder *encoder)
 extern void vlv_enable_dsi_pll(struct intel_encoder *encoder);
 extern void vlv_disable_dsi_pll(struct intel_encoder *encoder);
 
+extern struct intel_dsi_dev_ops vbt_generic_dsi_display_ops;
+
 #endif /* _INTEL_DSI_H */
index 7c40f981d2c75d236a33faf843c03a1c100bf5e7..3eeb21b9fddface4d1122a6be9389f5ca1598f0f 100644 (file)
@@ -389,7 +389,7 @@ int dsi_vc_generic_read(struct intel_dsi *intel_dsi, int channel,
  *
  * XXX: commands with data in MIPI_DPI_DATA?
  */
-int dpi_send_cmd(struct intel_dsi *intel_dsi, u32 cmd)
+int dpi_send_cmd(struct intel_dsi *intel_dsi, u32 cmd, bool hs)
 {
        struct drm_encoder *encoder = &intel_dsi->base.base;
        struct drm_device *dev = encoder->dev;
@@ -399,7 +399,7 @@ int dpi_send_cmd(struct intel_dsi *intel_dsi, u32 cmd)
        u32 mask;
 
        /* XXX: pipe, hs */
-       if (intel_dsi->hs)
+       if (hs)
                cmd &= ~DPI_LP_MODE;
        else
                cmd |= DPI_LP_MODE;
index 54c8a234a2e0fc53d7c5f3e5a01ec02351ceff9a..9a18cbfa546010320826013c956f53c8c9eb0d02 100644 (file)
@@ -33,6 +33,9 @@
 #include "intel_drv.h"
 #include "intel_dsi.h"
 
+#define DPI_LP_MODE_EN false
+#define DPI_HS_MODE_EN true
+
 void dsi_hs_mode_enable(struct intel_dsi *intel_dsi, bool enable);
 
 int dsi_vc_dcs_write(struct intel_dsi *intel_dsi, int channel,
@@ -47,7 +50,7 @@ int dsi_vc_dcs_read(struct intel_dsi *intel_dsi, int channel, u8 dcs_cmd,
 int dsi_vc_generic_read(struct intel_dsi *intel_dsi, int channel,
                        u8 *reqdata, int reqlen, u8 *buf, int buflen);
 
-int dpi_send_cmd(struct intel_dsi *intel_dsi, u32 cmd);
+int dpi_send_cmd(struct intel_dsi *intel_dsi, u32 cmd, bool hs);
 
 /* XXX: questionable write helpers */
 static inline int dsi_vc_dcs_write_0(struct intel_dsi *intel_dsi,
diff --git a/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c
new file mode 100644 (file)
index 0000000..21a0d34
--- /dev/null
@@ -0,0 +1,589 @@
+/*
+ * Copyright © 2014 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author: Shobhit Kumar <shobhit.kumar@intel.com>
+ *
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
+#include <drm/i915_drm.h>
+#include <linux/slab.h>
+#include <video/mipi_display.h>
+#include <asm/intel-mid.h>
+#include <video/mipi_display.h>
+#include "i915_drv.h"
+#include "intel_drv.h"
+#include "intel_dsi.h"
+#include "intel_dsi_cmd.h"
+
+#define MIPI_TRANSFER_MODE_SHIFT       0
+#define MIPI_VIRTUAL_CHANNEL_SHIFT     1
+#define MIPI_PORT_SHIFT                        3
+
+#define PREPARE_CNT_MAX                0x3F
+#define EXIT_ZERO_CNT_MAX      0x3F
+#define CLK_ZERO_CNT_MAX       0xFF
+#define TRAIL_CNT_MAX          0x1F
+
+#define NS_KHZ_RATIO 1000000
+
+#define GPI0_NC_0_HV_DDI0_HPD           0x4130
+#define GPIO_NC_0_HV_DDI0_PAD           0x4138
+#define GPIO_NC_1_HV_DDI0_DDC_SDA       0x4120
+#define GPIO_NC_1_HV_DDI0_DDC_SDA_PAD   0x4128
+#define GPIO_NC_2_HV_DDI0_DDC_SCL       0x4110
+#define GPIO_NC_2_HV_DDI0_DDC_SCL_PAD   0x4118
+#define GPIO_NC_3_PANEL0_VDDEN          0x4140
+#define GPIO_NC_3_PANEL0_VDDEN_PAD      0x4148
+#define GPIO_NC_4_PANEL0_BLKEN          0x4150
+#define GPIO_NC_4_PANEL0_BLKEN_PAD      0x4158
+#define GPIO_NC_5_PANEL0_BLKCTL         0x4160
+#define GPIO_NC_5_PANEL0_BLKCTL_PAD     0x4168
+#define GPIO_NC_6_PCONF0                0x4180
+#define GPIO_NC_6_PAD                   0x4188
+#define GPIO_NC_7_PCONF0                0x4190
+#define GPIO_NC_7_PAD                   0x4198
+#define GPIO_NC_8_PCONF0                0x4170
+#define GPIO_NC_8_PAD                   0x4178
+#define GPIO_NC_9_PCONF0                0x4100
+#define GPIO_NC_9_PAD                   0x4108
+#define GPIO_NC_10_PCONF0               0x40E0
+#define GPIO_NC_10_PAD                  0x40E8
+#define GPIO_NC_11_PCONF0               0x40F0
+#define GPIO_NC_11_PAD                  0x40F8
+
+struct gpio_table {
+       u16 function_reg;
+       u16 pad_reg;
+       u8 init;
+};
+
+static struct gpio_table gtable[] = {
+       { GPI0_NC_0_HV_DDI0_HPD, GPIO_NC_0_HV_DDI0_PAD, 0 },
+       { GPIO_NC_1_HV_DDI0_DDC_SDA, GPIO_NC_1_HV_DDI0_DDC_SDA_PAD, 0 },
+       { GPIO_NC_2_HV_DDI0_DDC_SCL, GPIO_NC_2_HV_DDI0_DDC_SCL_PAD, 0 },
+       { GPIO_NC_3_PANEL0_VDDEN, GPIO_NC_3_PANEL0_VDDEN_PAD, 0 },
+       { GPIO_NC_4_PANEL0_BLKEN, GPIO_NC_4_PANEL0_BLKEN_PAD, 0 },
+       { GPIO_NC_5_PANEL0_BLKCTL, GPIO_NC_5_PANEL0_BLKCTL_PAD, 0 },
+       { GPIO_NC_6_PCONF0, GPIO_NC_6_PAD, 0 },
+       { GPIO_NC_7_PCONF0, GPIO_NC_7_PAD, 0 },
+       { GPIO_NC_8_PCONF0, GPIO_NC_8_PAD, 0 },
+       { GPIO_NC_9_PCONF0, GPIO_NC_9_PAD, 0 },
+       { GPIO_NC_10_PCONF0, GPIO_NC_10_PAD, 0},
+       { GPIO_NC_11_PCONF0, GPIO_NC_11_PAD, 0}
+};
+
+static u8 *mipi_exec_send_packet(struct intel_dsi *intel_dsi, u8 *data)
+{
+       u8 type, byte, mode, vc, port;
+       u16 len;
+
+       byte = *data++;
+       mode = (byte >> MIPI_TRANSFER_MODE_SHIFT) & 0x1;
+       vc = (byte >> MIPI_VIRTUAL_CHANNEL_SHIFT) & 0x3;
+       port = (byte >> MIPI_PORT_SHIFT) & 0x3;
+
+       /* LP or HS mode */
+       intel_dsi->hs = mode;
+
+       /* get packet type and increment the pointer */
+       type = *data++;
+
+       len = *((u16 *) data);
+       data += 2;
+
+       switch (type) {
+       case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
+               dsi_vc_generic_write_0(intel_dsi, vc);
+               break;
+       case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
+               dsi_vc_generic_write_1(intel_dsi, vc, *data);
+               break;
+       case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
+               dsi_vc_generic_write_2(intel_dsi, vc, *data, *(data + 1));
+               break;
+       case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
+       case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
+       case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
+               DRM_DEBUG_DRIVER("Generic Read not yet implemented or used\n");
+               break;
+       case MIPI_DSI_GENERIC_LONG_WRITE:
+               dsi_vc_generic_write(intel_dsi, vc, data, len);
+               break;
+       case MIPI_DSI_DCS_SHORT_WRITE:
+               dsi_vc_dcs_write_0(intel_dsi, vc, *data);
+               break;
+       case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
+               dsi_vc_dcs_write_1(intel_dsi, vc, *data, *(data + 1));
+               break;
+       case MIPI_DSI_DCS_READ:
+               DRM_DEBUG_DRIVER("DCS Read not yet implemented or used\n");
+               break;
+       case MIPI_DSI_DCS_LONG_WRITE:
+               dsi_vc_dcs_write(intel_dsi, vc, data, len);
+               break;
+       };
+
+       data += len;
+
+       return data;
+}
+
+static u8 *mipi_exec_delay(struct intel_dsi *intel_dsi, u8 *data)
+{
+       u32 delay = *((u32 *) data);
+
+       usleep_range(delay, delay + 10);
+       data += 4;
+
+       return data;
+}
+
+static u8 *mipi_exec_gpio(struct intel_dsi *intel_dsi, u8 *data)
+{
+       u8 gpio, action;
+       u16 function, pad;
+       u32 val;
+       struct drm_device *dev = intel_dsi->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       gpio = *data++;
+
+       /* pull up/down */
+       action = *data++;
+
+       function = gtable[gpio].function_reg;
+       pad = gtable[gpio].pad_reg;
+
+       mutex_lock(&dev_priv->dpio_lock);
+       if (!gtable[gpio].init) {
+               /* program the function */
+               /* FIXME: remove constant below */
+               vlv_gpio_nc_write(dev_priv, function, 0x2000CC00);
+               gtable[gpio].init = 1;
+       }
+
+       val = 0x4 | action;
+
+       /* pull up/down */
+       vlv_gpio_nc_write(dev_priv, pad, val);
+       mutex_unlock(&dev_priv->dpio_lock);
+
+       return data;
+}
+
+typedef u8 * (*fn_mipi_elem_exec)(struct intel_dsi *intel_dsi, u8 *data);
+static const fn_mipi_elem_exec exec_elem[] = {
+       NULL, /* reserved */
+       mipi_exec_send_packet,
+       mipi_exec_delay,
+       mipi_exec_gpio,
+       NULL, /* status read; later */
+};
+
+/*
+ * MIPI Sequence from VBT #53 parsing logic
+ * We have already separated each seqence during bios parsing
+ * Following is generic execution function for any sequence
+ */
+
+static const char * const seq_name[] = {
+       "UNDEFINED",
+       "MIPI_SEQ_ASSERT_RESET",
+       "MIPI_SEQ_INIT_OTP",
+       "MIPI_SEQ_DISPLAY_ON",
+       "MIPI_SEQ_DISPLAY_OFF",
+       "MIPI_SEQ_DEASSERT_RESET"
+};
+
+static void generic_exec_sequence(struct intel_dsi *intel_dsi, char *sequence)
+{
+       u8 *data = sequence;
+       fn_mipi_elem_exec mipi_elem_exec;
+       int index;
+
+       if (!sequence)
+               return;
+
+       DRM_DEBUG_DRIVER("Starting MIPI sequence - %s\n", seq_name[*data]);
+
+       /* go to the first element of the sequence */
+       data++;
+
+       /* parse each byte till we reach end of sequence byte - 0x00 */
+       while (1) {
+               index = *data;
+               mipi_elem_exec = exec_elem[index];
+               if (!mipi_elem_exec) {
+                       DRM_ERROR("Unsupported MIPI element, skipping sequence execution\n");
+                       return;
+               }
+
+               /* goto element payload */
+               data++;
+
+               /* execute the element specific rotines */
+               data = mipi_elem_exec(intel_dsi, data);
+
+               /*
+                * After processing the element, data should point to
+                * next element or end of sequence
+                * check if have we reached end of sequence
+                */
+               if (*data == 0x00)
+                       break;
+       }
+}
+
+static bool generic_init(struct intel_dsi_device *dsi)
+{
+       struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev);
+       struct drm_device *dev = intel_dsi->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct mipi_config *mipi_config = dev_priv->vbt.dsi.config;
+       struct mipi_pps_data *pps = dev_priv->vbt.dsi.pps;
+       struct drm_display_mode *mode = dev_priv->vbt.lfp_lvds_vbt_mode;
+       u32 bits_per_pixel = 24;
+       u32 tlpx_ns, extra_byte_count, bitrate, tlpx_ui;
+       u32 ui_num, ui_den;
+       u32 prepare_cnt, exit_zero_cnt, clk_zero_cnt, trail_cnt;
+       u32 ths_prepare_ns, tclk_trail_ns;
+       u32 tclk_prepare_clkzero, ths_prepare_hszero;
+       u32 lp_to_hs_switch, hs_to_lp_switch;
+
+       DRM_DEBUG_KMS("\n");
+
+       intel_dsi->eotp_pkt = mipi_config->eot_pkt_disabled ? 0 : 1;
+       intel_dsi->clock_stop = mipi_config->enable_clk_stop ? 1 : 0;
+       intel_dsi->lane_count = mipi_config->lane_cnt + 1;
+       intel_dsi->pixel_format = mipi_config->videomode_color_format << 7;
+
+       if (intel_dsi->pixel_format == VID_MODE_FORMAT_RGB666)
+               bits_per_pixel = 18;
+       else if (intel_dsi->pixel_format == VID_MODE_FORMAT_RGB565)
+               bits_per_pixel = 16;
+
+       bitrate = (mode->clock * bits_per_pixel) / intel_dsi->lane_count;
+
+       intel_dsi->operation_mode = mipi_config->is_cmd_mode;
+       intel_dsi->video_mode_format = mipi_config->video_transfer_mode;
+       intel_dsi->escape_clk_div = mipi_config->byte_clk_sel;
+       intel_dsi->lp_rx_timeout = mipi_config->lp_rx_timeout;
+       intel_dsi->turn_arnd_val = mipi_config->turn_around_timeout;
+       intel_dsi->rst_timer_val = mipi_config->device_reset_timer;
+       intel_dsi->init_count = mipi_config->master_init_timer;
+       intel_dsi->bw_timer = mipi_config->dbi_bw_timer;
+       intel_dsi->video_frmt_cfg_bits = mipi_config->bta_enabled ? DISABLE_VIDEO_BTA : 0;
+
+       switch (intel_dsi->escape_clk_div) {
+       case 0:
+               tlpx_ns = 50;
+               break;
+       case 1:
+               tlpx_ns = 100;
+               break;
+
+       case 2:
+               tlpx_ns = 200;
+               break;
+       default:
+               tlpx_ns = 50;
+               break;
+       }
+
+       switch (intel_dsi->lane_count) {
+       case 1:
+       case 2:
+               extra_byte_count = 2;
+               break;
+       case 3:
+               extra_byte_count = 4;
+               break;
+       case 4:
+       default:
+               extra_byte_count = 3;
+               break;
+       }
+
+       /*
+        * ui(s) = 1/f [f in hz]
+        * ui(ns) = 10^9 / (f*10^6) [f in Mhz] -> 10^3/f(Mhz)
+        */
+
+       /* in Kbps */
+       ui_num = NS_KHZ_RATIO;
+       ui_den = bitrate;
+
+       tclk_prepare_clkzero = mipi_config->tclk_prepare_clkzero;
+       ths_prepare_hszero = mipi_config->ths_prepare_hszero;
+
+       /*
+        * B060
+        * LP byte clock = TLPX/ (8UI)
+        */
+       intel_dsi->lp_byte_clk = DIV_ROUND_UP(tlpx_ns * ui_den, 8 * ui_num);
+
+       /* count values in UI = (ns value) * (bitrate / (2 * 10^6))
+        *
+        * Since txddrclkhs_i is 2xUI, all the count values programmed in
+        * DPHY param register are divided by 2
+        *
+        * prepare count
+        */
+       ths_prepare_ns = max(mipi_config->ths_prepare, mipi_config->tclk_prepare);
+       prepare_cnt = DIV_ROUND_UP(ths_prepare_ns * ui_den, ui_num * 2);
+
+       /* exit zero count */
+       exit_zero_cnt = DIV_ROUND_UP(
+                               (ths_prepare_hszero - ths_prepare_ns) * ui_den,
+                               ui_num * 2
+                               );
+
+       /*
+        * Exit zero  is unified val ths_zero and ths_exit
+        * minimum value for ths_exit = 110ns
+        * min (exit_zero_cnt * 2) = 110/UI
+        * exit_zero_cnt = 55/UI
+        */
+        if (exit_zero_cnt < (55 * ui_den / ui_num))
+               if ((55 * ui_den) % ui_num)
+                       exit_zero_cnt += 1;
+
+       /* clk zero count */
+       clk_zero_cnt = DIV_ROUND_UP(
+                       (tclk_prepare_clkzero - ths_prepare_ns)
+                       * ui_den, 2 * ui_num);
+
+       /* trail count */
+       tclk_trail_ns = max(mipi_config->tclk_trail, mipi_config->ths_trail);
+       trail_cnt = DIV_ROUND_UP(tclk_trail_ns * ui_den, 2 * ui_num);
+
+       if (prepare_cnt > PREPARE_CNT_MAX ||
+               exit_zero_cnt > EXIT_ZERO_CNT_MAX ||
+               clk_zero_cnt > CLK_ZERO_CNT_MAX ||
+               trail_cnt > TRAIL_CNT_MAX)
+               DRM_DEBUG_DRIVER("Values crossing maximum limits, restricting to max values\n");
+
+       if (prepare_cnt > PREPARE_CNT_MAX)
+               prepare_cnt = PREPARE_CNT_MAX;
+
+       if (exit_zero_cnt > EXIT_ZERO_CNT_MAX)
+               exit_zero_cnt = EXIT_ZERO_CNT_MAX;
+
+       if (clk_zero_cnt > CLK_ZERO_CNT_MAX)
+               clk_zero_cnt = CLK_ZERO_CNT_MAX;
+
+       if (trail_cnt > TRAIL_CNT_MAX)
+               trail_cnt = TRAIL_CNT_MAX;
+
+       /* B080 */
+       intel_dsi->dphy_reg = exit_zero_cnt << 24 | trail_cnt << 16 |
+                                               clk_zero_cnt << 8 | prepare_cnt;
+
+       /*
+        * LP to HS switch count = 4TLPX + PREP_COUNT * 2 + EXIT_ZERO_COUNT * 2
+        *                                      + 10UI + Extra Byte Count
+        *
+        * HS to LP switch count = THS-TRAIL + 2TLPX + Extra Byte Count
+        * Extra Byte Count is calculated according to number of lanes.
+        * High Low Switch Count is the Max of LP to HS and
+        * HS to LP switch count
+        *
+        */
+       tlpx_ui = DIV_ROUND_UP(tlpx_ns * ui_den, ui_num);
+
+       /* B044 */
+       /* FIXME:
+        * The comment above does not match with the code */
+       lp_to_hs_switch = DIV_ROUND_UP(4 * tlpx_ui + prepare_cnt * 2 +
+                                               exit_zero_cnt * 2 + 10, 8);
+
+       hs_to_lp_switch = DIV_ROUND_UP(mipi_config->ths_trail + 2 * tlpx_ui, 8);
+
+       intel_dsi->hs_to_lp_count = max(lp_to_hs_switch, hs_to_lp_switch);
+       intel_dsi->hs_to_lp_count += extra_byte_count;
+
+       /* B088 */
+       /* LP -> HS for clock lanes
+        * LP clk sync + LP11 + LP01 + tclk_prepare + tclk_zero +
+        *                                              extra byte count
+        * 2TPLX + 1TLPX + 1 TPLX(in ns) + prepare_cnt * 2 + clk_zero_cnt *
+        *                                      2(in UI) + extra byte count
+        * In byteclks = (4TLPX + prepare_cnt * 2 + clk_zero_cnt *2 (in UI)) /
+        *                                      8 + extra byte count
+        */
+       intel_dsi->clk_lp_to_hs_count =
+               DIV_ROUND_UP(
+                       4 * tlpx_ui + prepare_cnt * 2 +
+                       clk_zero_cnt * 2,
+                       8);
+
+       intel_dsi->clk_lp_to_hs_count += extra_byte_count;
+
+       /* HS->LP for Clock Lanes
+        * Low Power clock synchronisations + 1Tx byteclk + tclk_trail +
+        *                                              Extra byte count
+        * 2TLPX + 8UI + (trail_count*2)(in UI) + Extra byte count
+        * In byteclks = (2*TLpx(in UI) + trail_count*2 +8)(in UI)/8 +
+        *                                              Extra byte count
+        */
+       intel_dsi->clk_hs_to_lp_count =
+               DIV_ROUND_UP(2 * tlpx_ui + trail_cnt * 2 + 8,
+                       8);
+       intel_dsi->clk_hs_to_lp_count += extra_byte_count;
+
+       DRM_DEBUG_KMS("Eot %s\n", intel_dsi->eotp_pkt ? "enabled" : "disabled");
+       DRM_DEBUG_KMS("Clockstop %s\n", intel_dsi->clock_stop ?
+                                               "disabled" : "enabled");
+       DRM_DEBUG_KMS("Mode %s\n", intel_dsi->operation_mode ? "command" : "video");
+       DRM_DEBUG_KMS("Pixel Format %d\n", intel_dsi->pixel_format);
+       DRM_DEBUG_KMS("TLPX %d\n", intel_dsi->escape_clk_div);
+       DRM_DEBUG_KMS("LP RX Timeout 0x%x\n", intel_dsi->lp_rx_timeout);
+       DRM_DEBUG_KMS("Turnaround Timeout 0x%x\n", intel_dsi->turn_arnd_val);
+       DRM_DEBUG_KMS("Init Count 0x%x\n", intel_dsi->init_count);
+       DRM_DEBUG_KMS("HS to LP Count 0x%x\n", intel_dsi->hs_to_lp_count);
+       DRM_DEBUG_KMS("LP Byte Clock %d\n", intel_dsi->lp_byte_clk);
+       DRM_DEBUG_KMS("DBI BW Timer 0x%x\n", intel_dsi->bw_timer);
+       DRM_DEBUG_KMS("LP to HS Clock Count 0x%x\n", intel_dsi->clk_lp_to_hs_count);
+       DRM_DEBUG_KMS("HS to LP Clock Count 0x%x\n", intel_dsi->clk_hs_to_lp_count);
+       DRM_DEBUG_KMS("BTA %s\n",
+                       intel_dsi->video_frmt_cfg_bits & DISABLE_VIDEO_BTA ?
+                       "disabled" : "enabled");
+
+       /* delays in VBT are in unit of 100us, so need to convert
+        * here in ms
+        * Delay (100us) * 100 /1000 = Delay / 10 (ms) */
+       intel_dsi->backlight_off_delay = pps->bl_disable_delay / 10;
+       intel_dsi->backlight_on_delay = pps->bl_enable_delay / 10;
+       intel_dsi->panel_on_delay = pps->panel_on_delay / 10;
+       intel_dsi->panel_off_delay = pps->panel_off_delay / 10;
+       intel_dsi->panel_pwr_cycle_delay = pps->panel_power_cycle_delay / 10;
+
+       return true;
+}
+
+static int generic_mode_valid(struct intel_dsi_device *dsi,
+                  struct drm_display_mode *mode)
+{
+       return MODE_OK;
+}
+
+static bool generic_mode_fixup(struct intel_dsi_device *dsi,
+                   const struct drm_display_mode *mode,
+                   struct drm_display_mode *adjusted_mode) {
+       return true;
+}
+
+static void generic_panel_reset(struct intel_dsi_device *dsi)
+{
+       struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev);
+       struct drm_device *dev = intel_dsi->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       char *sequence = dev_priv->vbt.dsi.sequence[MIPI_SEQ_ASSERT_RESET];
+
+       generic_exec_sequence(intel_dsi, sequence);
+}
+
+static void generic_disable_panel_power(struct intel_dsi_device *dsi)
+{
+       struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev);
+       struct drm_device *dev = intel_dsi->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       char *sequence = dev_priv->vbt.dsi.sequence[MIPI_SEQ_DEASSERT_RESET];
+
+       generic_exec_sequence(intel_dsi, sequence);
+}
+
+static void generic_send_otp_cmds(struct intel_dsi_device *dsi)
+{
+       struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev);
+       struct drm_device *dev = intel_dsi->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       char *sequence = dev_priv->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP];
+
+       generic_exec_sequence(intel_dsi, sequence);
+}
+
+static void generic_enable(struct intel_dsi_device *dsi)
+{
+       struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev);
+       struct drm_device *dev = intel_dsi->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       char *sequence = dev_priv->vbt.dsi.sequence[MIPI_SEQ_DISPLAY_ON];
+
+       generic_exec_sequence(intel_dsi, sequence);
+}
+
+static void generic_disable(struct intel_dsi_device *dsi)
+{
+       struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev);
+       struct drm_device *dev = intel_dsi->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       char *sequence = dev_priv->vbt.dsi.sequence[MIPI_SEQ_DISPLAY_OFF];
+
+       generic_exec_sequence(intel_dsi, sequence);
+}
+
+static enum drm_connector_status generic_detect(struct intel_dsi_device *dsi)
+{
+       return connector_status_connected;
+}
+
+static bool generic_get_hw_state(struct intel_dsi_device *dev)
+{
+       return true;
+}
+
+static struct drm_display_mode *generic_get_modes(struct intel_dsi_device *dsi)
+{
+       struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev);
+       struct drm_device *dev = intel_dsi->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       dev_priv->vbt.lfp_lvds_vbt_mode->type |= DRM_MODE_TYPE_PREFERRED;
+       return dev_priv->vbt.lfp_lvds_vbt_mode;
+}
+
+static void generic_destroy(struct intel_dsi_device *dsi) { }
+
+/* Callbacks. We might not need them all. */
+struct intel_dsi_dev_ops vbt_generic_dsi_display_ops = {
+       .init = generic_init,
+       .mode_valid = generic_mode_valid,
+       .mode_fixup = generic_mode_fixup,
+       .panel_reset = generic_panel_reset,
+       .disable_panel_power = generic_disable_panel_power,
+       .send_otp_cmds = generic_send_otp_cmds,
+       .enable = generic_enable,
+       .disable = generic_disable,
+       .detect = generic_detect,
+       .get_hw_state = generic_get_hw_state,
+       .get_modes = generic_get_modes,
+       .destroy = generic_destroy,
+};
index 7fe3feedfe039cda453851f1338fd56b100945cb..a3631c0a5c283695ac75147dda830618f3ad55cc 100644 (file)
@@ -285,7 +285,7 @@ static bool intel_dvo_compute_config(struct intel_encoder *encoder,
        return true;
 }
 
-static void intel_dvo_mode_set(struct intel_encoder *encoder)
+static void intel_dvo_pre_enable(struct intel_encoder *encoder)
 {
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -343,7 +343,7 @@ intel_dvo_detect(struct drm_connector *connector, bool force)
 {
        struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
-                     connector->base.id, drm_get_connector_name(connector));
+                     connector->base.id, connector->name);
        return intel_dvo->dev.dev_ops->detect(&intel_dvo->dev);
 }
 
@@ -475,7 +475,7 @@ void intel_dvo_init(struct drm_device *dev)
        intel_encoder->get_hw_state = intel_dvo_get_hw_state;
        intel_encoder->get_config = intel_dvo_get_config;
        intel_encoder->compute_config = intel_dvo_compute_config;
-       intel_encoder->mode_set = intel_dvo_mode_set;
+       intel_encoder->pre_enable = intel_dvo_pre_enable;
        intel_connector->get_hw_state = intel_dvo_connector_get_hw_state;
        intel_connector->unregister = intel_connector_unregister;
 
index f73ba5e6b7a8d685b4530e16c6735c095c2aeb5f..088fe9378a4cbec751cd927142b48827a0b23226 100644 (file)
@@ -343,15 +343,15 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
                        num_connectors_detected++;
 
                if (!enabled[i]) {
-                       DRM_DEBUG_KMS("connector %d not enabled, skipping\n",
-                                     connector->base.id);
+                       DRM_DEBUG_KMS("connector %s not enabled, skipping\n",
+                                     connector->name);
                        continue;
                }
 
                encoder = connector->encoder;
                if (!encoder || WARN_ON(!encoder->crtc)) {
-                       DRM_DEBUG_KMS("connector %d has no encoder or crtc, skipping\n",
-                                     connector->base.id);
+                       DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n",
+                                     connector->name);
                        enabled[i] = false;
                        continue;
                }
@@ -373,16 +373,16 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
                        }
                }
 
-               DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
-                             fb_conn->connector->base.id);
+               DRM_DEBUG_KMS("looking for cmdline mode on connector %s\n",
+                             connector->name);
 
                /* go for command line mode first */
                modes[i] = drm_pick_cmdline_mode(fb_conn, width, height);
 
                /* try for preferred next */
                if (!modes[i]) {
-                       DRM_DEBUG_KMS("looking for preferred mode on connector %d\n",
-                                     fb_conn->connector->base.id);
+                       DRM_DEBUG_KMS("looking for preferred mode on connector %s\n",
+                                     connector->name);
                        modes[i] = drm_has_preferred_mode(fb_conn, width,
                                                          height);
                }
@@ -390,7 +390,7 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
                /* No preferred mode marked by the EDID? Are there any modes? */
                if (!modes[i] && !list_empty(&connector->modes)) {
                        DRM_DEBUG_KMS("using first mode listed on connector %s\n",
-                                     drm_get_connector_name(connector));
+                                     connector->name);
                        modes[i] = list_first_entry(&connector->modes,
                                                    struct drm_display_mode,
                                                    head);
@@ -409,16 +409,20 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
                         * since the fb helper layer wants a pointer to
                         * something we own.
                         */
+                       DRM_DEBUG_KMS("looking for current mode on connector %s\n",
+                                     connector->name);
                        intel_mode_from_pipe_config(&encoder->crtc->hwmode,
                                                    &to_intel_crtc(encoder->crtc)->config);
                        modes[i] = &encoder->crtc->hwmode;
                }
                crtcs[i] = new_crtc;
 
-               DRM_DEBUG_KMS("connector %s on crtc %d: %s\n",
-                             drm_get_connector_name(connector),
+               DRM_DEBUG_KMS("connector %s on pipe %d [CRTC:%d]: %dx%d%s\n",
+                             connector->name,
+                             pipe_name(to_intel_crtc(encoder->crtc)->pipe),
                              encoder->crtc->base.id,
-                             modes[i]->name);
+                             modes[i]->hdisplay, modes[i]->vdisplay,
+                             modes[i]->flags & DRM_MODE_FLAG_INTERLACE ? "i" :"");
 
                fallback = false;
        }
@@ -497,7 +501,7 @@ static bool intel_fbdev_init_bios(struct drm_device *dev,
                return false;
 
        /* Find the largest fb */
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+       for_each_crtc(dev, crtc) {
                intel_crtc = to_intel_crtc(crtc);
 
                if (!intel_crtc->active || !crtc->primary->fb) {
@@ -521,7 +525,7 @@ static bool intel_fbdev_init_bios(struct drm_device *dev,
        }
 
        /* Now make sure all the pipes will fit into it */
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+       for_each_crtc(dev, crtc) {
                unsigned int cur_size;
 
                intel_crtc = to_intel_crtc(crtc);
@@ -586,7 +590,7 @@ static bool intel_fbdev_init_bios(struct drm_device *dev,
        drm_framebuffer_reference(&ifbdev->fb->base);
 
        /* Final pass to check if any active pipes don't have fbs */
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+       for_each_crtc(dev, crtc) {
                intel_crtc = to_intel_crtc(crtc);
 
                if (!intel_crtc->active)
@@ -692,11 +696,7 @@ void intel_fbdev_restore_mode(struct drm_device *dev)
        if (!dev_priv->fbdev)
                return;
 
-       drm_modeset_lock_all(dev);
-
-       ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper);
+       ret = drm_fb_helper_restore_fbdev_mode_unlocked(&dev_priv->fbdev->helper);
        if (ret)
                DRM_DEBUG("failed to restore crtc mode\n");
-
-       drm_modeset_unlock_all(dev);
 }
index 157267aa356165b7b7fcd94c3318a79f04f2bb8c..eee2bbec2958d4d569dcc67c1c293b51fc9e5c34 100644 (file)
@@ -418,6 +418,7 @@ intel_hdmi_set_hdmi_infoframe(struct drm_encoder *encoder,
 }
 
 static void g4x_set_infoframes(struct drm_encoder *encoder,
+                              bool enable,
                               struct drm_display_mode *adjusted_mode)
 {
        struct drm_i915_private *dev_priv = encoder->dev->dev_private;
@@ -440,7 +441,7 @@ static void g4x_set_infoframes(struct drm_encoder *encoder,
         * either. */
        val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
 
-       if (!intel_hdmi->has_hdmi_sink) {
+       if (!enable) {
                if (!(val & VIDEO_DIP_ENABLE))
                        return;
                val &= ~VIDEO_DIP_ENABLE;
@@ -471,6 +472,7 @@ static void g4x_set_infoframes(struct drm_encoder *encoder,
 }
 
 static void ibx_set_infoframes(struct drm_encoder *encoder,
+                              bool enable,
                               struct drm_display_mode *adjusted_mode)
 {
        struct drm_i915_private *dev_priv = encoder->dev->dev_private;
@@ -486,7 +488,7 @@ static void ibx_set_infoframes(struct drm_encoder *encoder,
        /* See the big comment in g4x_set_infoframes() */
        val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
 
-       if (!intel_hdmi->has_hdmi_sink) {
+       if (!enable) {
                if (!(val & VIDEO_DIP_ENABLE))
                        return;
                val &= ~VIDEO_DIP_ENABLE;
@@ -518,6 +520,7 @@ static void ibx_set_infoframes(struct drm_encoder *encoder,
 }
 
 static void cpt_set_infoframes(struct drm_encoder *encoder,
+                              bool enable,
                               struct drm_display_mode *adjusted_mode)
 {
        struct drm_i915_private *dev_priv = encoder->dev->dev_private;
@@ -531,7 +534,7 @@ static void cpt_set_infoframes(struct drm_encoder *encoder,
        /* See the big comment in g4x_set_infoframes() */
        val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
 
-       if (!intel_hdmi->has_hdmi_sink) {
+       if (!enable) {
                if (!(val & VIDEO_DIP_ENABLE))
                        return;
                val &= ~(VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI);
@@ -554,20 +557,23 @@ static void cpt_set_infoframes(struct drm_encoder *encoder,
 }
 
 static void vlv_set_infoframes(struct drm_encoder *encoder,
+                              bool enable,
                               struct drm_display_mode *adjusted_mode)
 {
        struct drm_i915_private *dev_priv = encoder->dev->dev_private;
+       struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
        struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
        struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
        u32 reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe);
        u32 val = I915_READ(reg);
+       u32 port = VIDEO_DIP_PORT(intel_dig_port->port);
 
        assert_hdmi_port_disabled(intel_hdmi);
 
        /* See the big comment in g4x_set_infoframes() */
        val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
 
-       if (!intel_hdmi->has_hdmi_sink) {
+       if (!enable) {
                if (!(val & VIDEO_DIP_ENABLE))
                        return;
                val &= ~VIDEO_DIP_ENABLE;
@@ -576,9 +582,19 @@ static void vlv_set_infoframes(struct drm_encoder *encoder,
                return;
        }
 
+       if (port != (val & VIDEO_DIP_PORT_MASK)) {
+               if (val & VIDEO_DIP_ENABLE) {
+                       val &= ~VIDEO_DIP_ENABLE;
+                       I915_WRITE(reg, val);
+                       POSTING_READ(reg);
+               }
+               val &= ~VIDEO_DIP_PORT_MASK;
+               val |= port;
+       }
+
        val |= VIDEO_DIP_ENABLE;
-       val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
-                VIDEO_DIP_ENABLE_GCP);
+       val &= ~(VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_ENABLE_VENDOR |
+                VIDEO_DIP_ENABLE_GAMUT | VIDEO_DIP_ENABLE_GCP);
 
        I915_WRITE(reg, val);
        POSTING_READ(reg);
@@ -589,6 +605,7 @@ static void vlv_set_infoframes(struct drm_encoder *encoder,
 }
 
 static void hsw_set_infoframes(struct drm_encoder *encoder,
+                              bool enable,
                               struct drm_display_mode *adjusted_mode)
 {
        struct drm_i915_private *dev_priv = encoder->dev->dev_private;
@@ -599,7 +616,7 @@ static void hsw_set_infoframes(struct drm_encoder *encoder,
 
        assert_hdmi_port_disabled(intel_hdmi);
 
-       if (!intel_hdmi->has_hdmi_sink) {
+       if (!enable) {
                I915_WRITE(reg, 0);
                POSTING_READ(reg);
                return;
@@ -616,7 +633,7 @@ static void hsw_set_infoframes(struct drm_encoder *encoder,
        intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode);
 }
 
-static void intel_hdmi_mode_set(struct intel_encoder *encoder)
+static void intel_hdmi_prepare(struct intel_encoder *encoder)
 {
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -638,27 +655,26 @@ static void intel_hdmi_mode_set(struct intel_encoder *encoder)
        else
                hdmi_val |= SDVO_COLOR_FORMAT_8bpc;
 
-       /* Required on CPT */
-       if (intel_hdmi->has_hdmi_sink && HAS_PCH_CPT(dev))
+       if (crtc->config.has_hdmi_sink)
                hdmi_val |= HDMI_MODE_SELECT_HDMI;
 
-       if (intel_hdmi->has_audio) {
+       if (crtc->config.has_audio) {
+               WARN_ON(!crtc->config.has_hdmi_sink);
                DRM_DEBUG_DRIVER("Enabling HDMI audio on pipe %c\n",
                                 pipe_name(crtc->pipe));
                hdmi_val |= SDVO_AUDIO_ENABLE;
-               hdmi_val |= HDMI_MODE_SELECT_HDMI;
                intel_write_eld(&encoder->base, adjusted_mode);
        }
 
        if (HAS_PCH_CPT(dev))
                hdmi_val |= SDVO_PIPE_SEL_CPT(crtc->pipe);
+       else if (IS_CHERRYVIEW(dev))
+               hdmi_val |= SDVO_PIPE_SEL_CHV(crtc->pipe);
        else
                hdmi_val |= SDVO_PIPE_SEL(crtc->pipe);
 
        I915_WRITE(intel_hdmi->hdmi_reg, hdmi_val);
        POSTING_READ(intel_hdmi->hdmi_reg);
-
-       intel_hdmi->set_infoframes(&encoder->base, adjusted_mode);
 }
 
 static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder,
@@ -681,6 +697,8 @@ static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder,
 
        if (HAS_PCH_CPT(dev))
                *pipe = PORT_TO_PIPE_CPT(tmp);
+       else if (IS_CHERRYVIEW(dev))
+               *pipe = SDVO_PORT_TO_PIPE_CHV(tmp);
        else
                *pipe = PORT_TO_PIPE(tmp);
 
@@ -707,6 +725,12 @@ static void intel_hdmi_get_config(struct intel_encoder *encoder,
        else
                flags |= DRM_MODE_FLAG_NVSYNC;
 
+       if (tmp & HDMI_MODE_SELECT_HDMI)
+               pipe_config->has_hdmi_sink = true;
+
+       if (tmp & HDMI_MODE_SELECT_HDMI)
+               pipe_config->has_audio = true;
+
        pipe_config->adjusted_mode.flags |= flags;
 
        if ((tmp & SDVO_COLOR_FORMAT_MASK) == HDMI_COLOR_FORMAT_12bpc)
@@ -729,7 +753,7 @@ static void intel_enable_hdmi(struct intel_encoder *encoder)
        u32 temp;
        u32 enable_bits = SDVO_ENABLE;
 
-       if (intel_hdmi->has_audio)
+       if (intel_crtc->config.has_audio)
                enable_bits |= SDVO_AUDIO_ENABLE;
 
        temp = I915_READ(intel_hdmi->hdmi_reg);
@@ -883,9 +907,11 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
        int portclock_limit = hdmi_portclock_limit(intel_hdmi, false);
        int desired_bpp;
 
+       pipe_config->has_hdmi_sink = intel_hdmi->has_hdmi_sink;
+
        if (intel_hdmi->color_range_auto) {
                /* See CEA-861-E - 5.1 Default Encoding Parameters */
-               if (intel_hdmi->has_hdmi_sink &&
+               if (pipe_config->has_hdmi_sink &&
                    drm_match_cea_mode(adjusted_mode) > 1)
                        intel_hdmi->color_range = HDMI_COLOR_RANGE_16_235;
                else
@@ -898,13 +924,16 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
        if (HAS_PCH_SPLIT(dev) && !HAS_DDI(dev))
                pipe_config->has_pch_encoder = true;
 
+       if (pipe_config->has_hdmi_sink && intel_hdmi->has_audio)
+               pipe_config->has_audio = true;
+
        /*
         * 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. We also need to check that the higher clock still fits
         * within limits.
         */
-       if (pipe_config->pipe_bpp > 8*3 && intel_hdmi->has_hdmi_sink &&
+       if (pipe_config->pipe_bpp > 8*3 && pipe_config->has_hdmi_sink &&
            clock_12bpc <= portclock_limit &&
            hdmi_12bpc_possible(encoder->new_crtc)) {
                DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n");
@@ -944,7 +973,7 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
        enum drm_connector_status status = connector_status_disconnected;
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
-                     connector->base.id, drm_get_connector_name(connector));
+                     connector->base.id, connector->name);
 
        power_domain = intel_display_port_power_domain(intel_encoder);
        intel_display_power_get(dev_priv, power_domain);
@@ -1104,20 +1133,34 @@ done:
        return 0;
 }
 
+static void intel_hdmi_pre_enable(struct intel_encoder *encoder)
+{
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
+       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
+       struct drm_display_mode *adjusted_mode =
+               &intel_crtc->config.adjusted_mode;
+
+       intel_hdmi_prepare(encoder);
+
+       intel_hdmi->set_infoframes(&encoder->base,
+                                  intel_crtc->config.has_hdmi_sink,
+                                  adjusted_mode);
+}
+
 static void vlv_hdmi_pre_enable(struct intel_encoder *encoder)
 {
        struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
+       struct intel_hdmi *intel_hdmi = &dport->hdmi;
        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);
+       struct drm_display_mode *adjusted_mode =
+               &intel_crtc->config.adjusted_mode;
        enum dpio_channel port = vlv_dport_to_channel(dport);
        int pipe = intel_crtc->pipe;
        u32 val;
 
-       if (!IS_VALLEYVIEW(dev))
-               return;
-
        /* Enable clock channels for this port */
        mutex_lock(&dev_priv->dpio_lock);
        val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW8(port));
@@ -1144,6 +1187,10 @@ static void vlv_hdmi_pre_enable(struct intel_encoder *encoder)
        vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW23(port), 0x00400888);
        mutex_unlock(&dev_priv->dpio_lock);
 
+       intel_hdmi->set_infoframes(&encoder->base,
+                                  intel_crtc->config.has_hdmi_sink,
+                                  adjusted_mode);
+
        intel_enable_hdmi(encoder);
 
        vlv_wait_port_ready(dev_priv, dport);
@@ -1159,8 +1206,7 @@ static void vlv_hdmi_pre_pll_enable(struct intel_encoder *encoder)
        enum dpio_channel port = vlv_dport_to_channel(dport);
        int pipe = intel_crtc->pipe;
 
-       if (!IS_VALLEYVIEW(dev))
-               return;
+       intel_hdmi_prepare(encoder);
 
        /* Program Tx lane resets to default */
        mutex_lock(&dev_priv->dpio_lock);
@@ -1199,6 +1245,152 @@ static void vlv_hdmi_post_disable(struct intel_encoder *encoder)
        mutex_unlock(&dev_priv->dpio_lock);
 }
 
+static void chv_hdmi_post_disable(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);
+       enum dpio_channel ch = vlv_dport_to_channel(dport);
+       enum pipe pipe = intel_crtc->pipe;
+       u32 val;
+
+       mutex_lock(&dev_priv->dpio_lock);
+
+       /* Propagate soft reset to data lane reset */
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch));
+       val |= CHV_PCS_REQ_SOFTRESET_EN;
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val);
+
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch));
+       val |= CHV_PCS_REQ_SOFTRESET_EN;
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val);
+
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch));
+       val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val);
+
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch));
+       val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val);
+
+       mutex_unlock(&dev_priv->dpio_lock);
+}
+
+static void chv_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);
+       enum dpio_channel ch = vlv_dport_to_channel(dport);
+       int pipe = intel_crtc->pipe;
+       int data, i;
+       u32 val;
+
+       mutex_lock(&dev_priv->dpio_lock);
+
+       /* Deassert soft data lane reset*/
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch));
+       val |= CHV_PCS_REQ_SOFTRESET_EN;
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val);
+
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch));
+       val |= CHV_PCS_REQ_SOFTRESET_EN;
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val);
+
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch));
+       val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val);
+
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch));
+       val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val);
+
+       /* Program Tx latency optimal setting */
+       for (i = 0; i < 4; i++) {
+               /* Set the latency optimal bit */
+               data = (i == 1) ? 0x0 : 0x6;
+               vlv_dpio_write(dev_priv, pipe, CHV_TX_DW11(ch, i),
+                               data << DPIO_FRC_LATENCY_SHFIT);
+
+               /* Set the upar bit */
+               data = (i == 1) ? 0x0 : 0x1;
+               vlv_dpio_write(dev_priv, pipe, CHV_TX_DW14(ch, i),
+                               data << DPIO_UPAR_SHIFT);
+       }
+
+       /* Data lane stagger programming */
+       /* FIXME: Fix up value only after power analysis */
+
+       /* Clear calc init */
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW10(ch));
+       val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3);
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val);
+
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch));
+       val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3);
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val);
+
+       /* FIXME: Program the support xxx V-dB */
+       /* Use 800mV-0dB */
+       for (i = 0; i < 4; i++) {
+               val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW4(ch, i));
+               val &= ~DPIO_SWING_DEEMPH9P5_MASK;
+               val |= 128 << DPIO_SWING_DEEMPH9P5_SHIFT;
+               vlv_dpio_write(dev_priv, pipe, CHV_TX_DW4(ch, i), val);
+       }
+
+       for (i = 0; i < 4; i++) {
+               val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i));
+               val &= ~DPIO_SWING_MARGIN_MASK;
+               val |= 102 << DPIO_SWING_MARGIN_SHIFT;
+               vlv_dpio_write(dev_priv, pipe, CHV_TX_DW2(ch, i), val);
+       }
+
+       /* Disable unique transition scale */
+       for (i = 0; i < 4; i++) {
+               val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW3(ch, i));
+               val &= ~DPIO_TX_UNIQ_TRANS_SCALE_EN;
+               vlv_dpio_write(dev_priv, pipe, CHV_TX_DW3(ch, i), val);
+       }
+
+       /* Additional steps for 1200mV-0dB */
+#if 0
+       val = vlv_dpio_read(dev_priv, pipe, VLV_TX_DW3(ch));
+       if (ch)
+               val |= DPIO_TX_UNIQ_TRANS_SCALE_CH1;
+       else
+               val |= DPIO_TX_UNIQ_TRANS_SCALE_CH0;
+       vlv_dpio_write(dev_priv, pipe, VLV_TX_DW3(ch), val);
+
+       vlv_dpio_write(dev_priv, pipe, VLV_TX_DW2(ch),
+                       vlv_dpio_read(dev_priv, pipe, VLV_TX_DW2(ch)) |
+                               (0x9a << DPIO_UNIQ_TRANS_SCALE_SHIFT));
+#endif
+       /* Start swing calculation */
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW10(ch));
+       val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3;
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val);
+
+       val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch));
+       val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3;
+       vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val);
+
+       /* LRC Bypass */
+       val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW30);
+       val |= DPIO_LRC_BYPASS;
+       vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW30, val);
+
+       mutex_unlock(&dev_priv->dpio_lock);
+
+       intel_enable_hdmi(encoder);
+
+       vlv_wait_port_ready(dev_priv, dport);
+}
+
 static void intel_hdmi_destroy(struct drm_connector *connector)
 {
        drm_connector_cleanup(connector);
@@ -1259,7 +1451,10 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
                intel_encoder->hpd_pin = HPD_PORT_C;
                break;
        case PORT_D:
-               intel_hdmi->ddc_bus = GMBUS_PORT_DPD;
+               if (IS_CHERRYVIEW(dev))
+                       intel_hdmi->ddc_bus = GMBUS_PORT_DPD_CHV;
+               else
+                       intel_hdmi->ddc_bus = GMBUS_PORT_DPD;
                intel_encoder->hpd_pin = HPD_PORT_D;
                break;
        case PORT_A:
@@ -1329,21 +1524,32 @@ void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port)
                         DRM_MODE_ENCODER_TMDS);
 
        intel_encoder->compute_config = intel_hdmi_compute_config;
-       intel_encoder->mode_set = intel_hdmi_mode_set;
        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)) {
+       if (IS_CHERRYVIEW(dev)) {
+               intel_encoder->pre_enable = chv_hdmi_pre_enable;
+               intel_encoder->enable = vlv_enable_hdmi;
+               intel_encoder->post_disable = chv_hdmi_post_disable;
+       } else if (IS_VALLEYVIEW(dev)) {
                intel_encoder->pre_pll_enable = vlv_hdmi_pre_pll_enable;
                intel_encoder->pre_enable = vlv_hdmi_pre_enable;
                intel_encoder->enable = vlv_enable_hdmi;
                intel_encoder->post_disable = vlv_hdmi_post_disable;
        } else {
+               intel_encoder->pre_enable = intel_hdmi_pre_enable;
                intel_encoder->enable = intel_enable_hdmi;
        }
 
        intel_encoder->type = INTEL_OUTPUT_HDMI;
-       intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
+       if (IS_CHERRYVIEW(dev)) {
+               if (port == PORT_D)
+                       intel_encoder->crtc_mask = 1 << 2;
+               else
+                       intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
+       } else {
+               intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
+       }
        intel_encoder->cloneable = 1 << INTEL_OUTPUT_ANALOG;
        /*
         * BSpec is unclear about HDMI+HDMI cloning on g4x, but it seems
index f1ecf916474a93e3a18dd604991a8915309d83cb..23126023aeba04e6819d9ad1fdd64f2b8d69b646 100644 (file)
@@ -111,13 +111,6 @@ static void intel_lvds_get_config(struct intel_encoder *encoder,
 
        pipe_config->adjusted_mode.flags |= flags;
 
-       /* gen2/3 store dither state in pfit control, needs to match */
-       if (INTEL_INFO(dev)->gen < 4) {
-               tmp = I915_READ(PFIT_CONTROL);
-
-               pipe_config->gmch_pfit.control |= tmp & PANEL_8TO6_DITHER_ENABLE;
-       }
-
        dotclock = pipe_config->port_clock;
 
        if (HAS_PCH_SPLIT(dev_priv->dev))
@@ -126,10 +119,6 @@ static void intel_lvds_get_config(struct intel_encoder *encoder,
        pipe_config->adjusted_mode.crtc_clock = dotclock;
 }
 
-/* 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.
- */
 static void intel_pre_enable_lvds(struct intel_encoder *encoder)
 {
        struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
@@ -331,15 +320,6 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
        return true;
 }
 
-static void intel_lvds_mode_set(struct intel_encoder *encoder)
-{
-       /*
-        * We don't do anything here, the LVDS port is fully set up in the pre
-        * enable hook - the ordering constraints for enabling the lvds port vs.
-        * enabling the display pll are too strict.
-        */
-}
-
 /**
  * Detect the LVDS connection.
  *
@@ -354,7 +334,7 @@ intel_lvds_detect(struct drm_connector *connector, bool force)
        enum drm_connector_status status;
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
-                     connector->base.id, drm_get_connector_name(connector));
+                     connector->base.id, connector->name);
 
        status = intel_panel_detect(dev);
        if (status != connector_status_unknown)
@@ -953,7 +933,6 @@ void intel_lvds_init(struct drm_device *dev)
        intel_encoder->enable = intel_enable_lvds;
        intel_encoder->pre_enable = intel_pre_enable_lvds;
        intel_encoder->compute_config = intel_lvds_compute_config;
-       intel_encoder->mode_set = intel_lvds_mode_set;
        intel_encoder->disable = intel_disable_lvds;
        intel_encoder->get_hw_state = intel_lvds_get_hw_state;
        intel_encoder->get_config = intel_lvds_get_config;
index acde2945eb8a73e075c2fa60493f4b22e89bb666..2e2c71fcc9ed502dc3a013089d53a3270999ad51 100644 (file)
@@ -410,7 +410,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
        if (bclp > 255)
                return ASLC_BACKLIGHT_FAILED;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
 
        /*
         * Update backlight on all connectors that support backlight (usually
@@ -421,7 +421,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
                intel_panel_set_backlight(intel_connector, bclp, 255);
        iowrite32(DIV_ROUND_UP(bclp * 100, 255) | ASLE_CBLV_VALID, &asle->cblv);
 
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock(&dev->mode_config.connection_mutex);
 
 
        return 0;
index 129db0c7d83579750118172220d0c8380228dd72..daa118978eec725b471d1c3dbb3933f52f85c1b5 100644 (file)
@@ -213,7 +213,7 @@ static int intel_overlay_do_wait_request(struct intel_overlay *overlay,
 {
        struct drm_device *dev = overlay->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
+       struct intel_engine_cs *ring = &dev_priv->ring[RCS];
        int ret;
 
        BUG_ON(overlay->last_flip_req);
@@ -236,7 +236,7 @@ static int intel_overlay_on(struct intel_overlay *overlay)
 {
        struct drm_device *dev = overlay->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
+       struct intel_engine_cs *ring = &dev_priv->ring[RCS];
        int ret;
 
        BUG_ON(overlay->active);
@@ -263,7 +263,7 @@ static int intel_overlay_continue(struct intel_overlay *overlay,
 {
        struct drm_device *dev = overlay->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
+       struct intel_engine_cs *ring = &dev_priv->ring[RCS];
        u32 flip_addr = overlay->flip_addr;
        u32 tmp;
        int ret;
@@ -320,7 +320,7 @@ static int intel_overlay_off(struct intel_overlay *overlay)
 {
        struct drm_device *dev = overlay->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
+       struct intel_engine_cs *ring = &dev_priv->ring[RCS];
        u32 flip_addr = overlay->flip_addr;
        int ret;
 
@@ -363,7 +363,7 @@ static int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay)
 {
        struct drm_device *dev = overlay->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
+       struct intel_engine_cs *ring = &dev_priv->ring[RCS];
        int ret;
 
        if (overlay->last_flip_req == 0)
@@ -389,7 +389,7 @@ static int intel_overlay_release_old_vid(struct intel_overlay *overlay)
 {
        struct drm_device *dev = overlay->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
+       struct intel_engine_cs *ring = &dev_priv->ring[RCS];
        int ret;
 
        /* Only wait if there is actually an old frame to release to
@@ -688,7 +688,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
        u32 swidth, swidthsw, sheight, ostride;
 
        BUG_ON(!mutex_is_locked(&dev->struct_mutex));
-       BUG_ON(!mutex_is_locked(&dev->mode_config.mutex));
+       BUG_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
        BUG_ON(!overlay);
 
        ret = intel_overlay_release_old_vid(overlay);
@@ -793,7 +793,7 @@ int intel_overlay_switch_off(struct intel_overlay *overlay)
        int ret;
 
        BUG_ON(!mutex_is_locked(&dev->struct_mutex));
-       BUG_ON(!mutex_is_locked(&dev->mode_config.mutex));
+       BUG_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
 
        ret = intel_overlay_recover_from_interrupt(overlay);
        if (ret != 0)
index cb8cfb7e09749938383c18a7eb6e2bc149823095..5e6c888b492886bcea6291fd5b4ba1c35f416af9 100644 (file)
@@ -42,6 +42,59 @@ intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode,
        drm_mode_set_crtcinfo(adjusted_mode, 0);
 }
 
+/**
+ * intel_find_panel_downclock - find the reduced downclock for LVDS in EDID
+ * @dev: drm device
+ * @fixed_mode : panel native mode
+ * @connector: LVDS/eDP connector
+ *
+ * Return downclock_avail
+ * Find the reduced downclock for LVDS/eDP in EDID.
+ */
+struct drm_display_mode *
+intel_find_panel_downclock(struct drm_device *dev,
+                       struct drm_display_mode *fixed_mode,
+                       struct drm_connector *connector)
+{
+       struct drm_display_mode *scan, *tmp_mode;
+       int temp_downclock;
+
+       temp_downclock = fixed_mode->clock;
+       tmp_mode = NULL;
+
+       list_for_each_entry(scan, &connector->probed_modes, head) {
+               /*
+                * If one mode has the same resolution with the fixed_panel
+                * mode while they have the different refresh rate, it means
+                * that the reduced downclock is found. In such
+                * case we can set the different FPx0/1 to dynamically select
+                * between low and high frequency.
+                */
+               if (scan->hdisplay == fixed_mode->hdisplay &&
+                   scan->hsync_start == fixed_mode->hsync_start &&
+                   scan->hsync_end == fixed_mode->hsync_end &&
+                   scan->htotal == fixed_mode->htotal &&
+                   scan->vdisplay == fixed_mode->vdisplay &&
+                   scan->vsync_start == fixed_mode->vsync_start &&
+                   scan->vsync_end == fixed_mode->vsync_end &&
+                   scan->vtotal == fixed_mode->vtotal) {
+                       if (scan->clock < temp_downclock) {
+                               /*
+                                * The downclock is already found. But we
+                                * expect to find the lower downclock.
+                                */
+                               temp_downclock = scan->clock;
+                               tmp_mode = scan;
+                       }
+               }
+       }
+
+       if (temp_downclock < fixed_mode->clock)
+               return drm_mode_duplicate(dev, tmp_mode);
+       else
+               return NULL;
+}
+
 /* adjusted_mode has been preset to be the panel's fixed mode */
 void
 intel_pch_panel_fitting(struct intel_crtc *intel_crtc,
@@ -308,21 +361,43 @@ void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc,
                pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) |
                                 PFIT_FILTER_FUZZY);
 
+       /* 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;
+
 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;
 }
 
+enum drm_connector_status
+intel_panel_detect(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       /* Assume that the BIOS does not lie through the OpRegion... */
+       if (!i915.panel_ignore_lid && dev_priv->opregion.lid_state) {
+               return ioread32(dev_priv->opregion.lid_state) & 0x1 ?
+                       connector_status_connected :
+                       connector_status_disconnected;
+       }
+
+       switch (i915.panel_ignore_lid) {
+       case -2:
+               return connector_status_connected;
+       case -1:
+               return connector_status_disconnected;
+       default:
+               return connector_status_unknown;
+       }
+}
+
 static u32 intel_panel_compute_brightness(struct intel_connector *connector,
                                          u32 val)
 {
@@ -795,40 +870,18 @@ void intel_panel_enable_backlight(struct intel_connector *connector)
        spin_unlock_irqrestore(&dev_priv->backlight_lock, flags);
 }
 
-enum drm_connector_status
-intel_panel_detect(struct drm_device *dev)
-{
-       struct drm_i915_private *dev_priv = dev->dev_private;
-
-       /* Assume that the BIOS does not lie through the OpRegion... */
-       if (!i915.panel_ignore_lid && dev_priv->opregion.lid_state) {
-               return ioread32(dev_priv->opregion.lid_state) & 0x1 ?
-                       connector_status_connected :
-                       connector_status_disconnected;
-       }
-
-       switch (i915.panel_ignore_lid) {
-       case -2:
-               return connector_status_connected;
-       case -1:
-               return connector_status_disconnected;
-       default:
-               return connector_status_unknown;
-       }
-}
-
 #if IS_ENABLED(CONFIG_BACKLIGHT_CLASS_DEVICE)
 static int intel_backlight_device_update_status(struct backlight_device *bd)
 {
        struct intel_connector *connector = bl_get_data(bd);
        struct drm_device *dev = connector->base.dev;
 
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
        DRM_DEBUG_KMS("updating intel_backlight, brightness=%d/%d\n",
                      bd->props.brightness, bd->props.max_brightness);
        intel_panel_set_backlight(connector, bd->props.brightness,
                                  bd->props.max_brightness);
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock(&dev->mode_config.connection_mutex);
        return 0;
 }
 
@@ -840,9 +893,9 @@ static int intel_backlight_device_get_brightness(struct backlight_device *bd)
        int ret;
 
        intel_runtime_pm_get(dev_priv);
-       mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
        ret = intel_panel_get_backlight(connector);
-       mutex_unlock(&dev->mode_config.mutex);
+       drm_modeset_unlock(&dev->mode_config.connection_mutex);
        intel_runtime_pm_put(dev_priv);
 
        return ret;
@@ -1077,7 +1130,7 @@ int intel_panel_setup_backlight(struct drm_connector *connector)
 
        if (ret) {
                DRM_DEBUG_KMS("failed to setup backlight for connector %s\n",
-                             drm_get_connector_name(connector));
+                             connector->name);
                return ret;
        }
 
@@ -1103,59 +1156,6 @@ void intel_panel_destroy_backlight(struct drm_connector *connector)
        intel_backlight_device_unregister(intel_connector);
 }
 
-/**
- * intel_find_panel_downclock - find the reduced downclock for LVDS in EDID
- * @dev: drm device
- * @fixed_mode : panel native mode
- * @connector: LVDS/eDP connector
- *
- * Return downclock_avail
- * Find the reduced downclock for LVDS/eDP in EDID.
- */
-struct drm_display_mode *
-intel_find_panel_downclock(struct drm_device *dev,
-                       struct drm_display_mode *fixed_mode,
-                       struct drm_connector *connector)
-{
-       struct drm_display_mode *scan, *tmp_mode;
-       int temp_downclock;
-
-       temp_downclock = fixed_mode->clock;
-       tmp_mode = NULL;
-
-       list_for_each_entry(scan, &connector->probed_modes, head) {
-               /*
-                * If one mode has the same resolution with the fixed_panel
-                * mode while they have the different refresh rate, it means
-                * that the reduced downclock is found. In such
-                * case we can set the different FPx0/1 to dynamically select
-                * between low and high frequency.
-                */
-               if (scan->hdisplay == fixed_mode->hdisplay &&
-                   scan->hsync_start == fixed_mode->hsync_start &&
-                   scan->hsync_end == fixed_mode->hsync_end &&
-                   scan->htotal == fixed_mode->htotal &&
-                   scan->vdisplay == fixed_mode->vdisplay &&
-                   scan->vsync_start == fixed_mode->vsync_start &&
-                   scan->vsync_end == fixed_mode->vsync_end &&
-                   scan->vtotal == fixed_mode->vtotal) {
-                       if (scan->clock < temp_downclock) {
-                               /*
-                                * The downclock is already found. But we
-                                * expect to find the lower downclock.
-                                */
-                               temp_downclock = scan->clock;
-                               tmp_mode = scan;
-                       }
-               }
-       }
-
-       if (temp_downclock < fixed_mode->clock)
-               return drm_mode_duplicate(dev, tmp_mode);
-       else
-               return NULL;
-}
-
 /* Set up chip specific backlight functions */
 void intel_panel_init_backlight_funcs(struct drm_device *dev)
 {
index d93dcf683e8c3695960ca93c92dce87a7823d3f2..d1e53abec1b5f808fbf5457f565f2343689e41f6 100644 (file)
@@ -487,7 +487,7 @@ void intel_update_fbc(struct drm_device *dev)
         *   - new fb is too large to fit in compressed buffer
         *   - going to an unsupported config (interlace, pixel multiply, etc.)
         */
-       list_for_each_entry(tmp_crtc, &dev->mode_config.crtc_list, head) {
+       for_each_crtc(dev, tmp_crtc) {
                if (intel_crtc_active(tmp_crtc) &&
                    to_intel_crtc(tmp_crtc)->primary_enabled) {
                        if (crtc) {
@@ -1010,7 +1010,7 @@ static struct drm_crtc *single_enabled_crtc(struct drm_device *dev)
 {
        struct drm_crtc *crtc, *enabled = NULL;
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+       for_each_crtc(dev, crtc) {
                if (intel_crtc_active(crtc)) {
                        if (enabled)
                                return NULL;
@@ -1831,6 +1831,40 @@ static unsigned int ilk_display_fifo_size(const struct drm_device *dev)
                return 512;
 }
 
+static unsigned int ilk_plane_wm_reg_max(const struct drm_device *dev,
+                                        int level, bool is_sprite)
+{
+       if (INTEL_INFO(dev)->gen >= 8)
+               /* BDW primary/sprite plane watermarks */
+               return level == 0 ? 255 : 2047;
+       else if (INTEL_INFO(dev)->gen >= 7)
+               /* IVB/HSW primary/sprite plane watermarks */
+               return level == 0 ? 127 : 1023;
+       else if (!is_sprite)
+               /* ILK/SNB primary plane watermarks */
+               return level == 0 ? 127 : 511;
+       else
+               /* ILK/SNB sprite plane watermarks */
+               return level == 0 ? 63 : 255;
+}
+
+static unsigned int ilk_cursor_wm_reg_max(const struct drm_device *dev,
+                                         int level)
+{
+       if (INTEL_INFO(dev)->gen >= 7)
+               return level == 0 ? 63 : 255;
+       else
+               return level == 0 ? 31 : 63;
+}
+
+static unsigned int ilk_fbc_wm_reg_max(const struct drm_device *dev)
+{
+       if (INTEL_INFO(dev)->gen >= 8)
+               return 31;
+       else
+               return 15;
+}
+
 /* Calculate the maximum primary/sprite plane watermark */
 static unsigned int ilk_plane_wm_max(const struct drm_device *dev,
                                     int level,
@@ -1839,7 +1873,6 @@ static unsigned int ilk_plane_wm_max(const struct drm_device *dev,
                                     bool is_sprite)
 {
        unsigned int fifo_size = ilk_display_fifo_size(dev);
-       unsigned int max;
 
        /* if sprites aren't enabled, sprites get nothing */
        if (is_sprite && !config->sprites_enabled)
@@ -1870,19 +1903,7 @@ static unsigned int ilk_plane_wm_max(const struct drm_device *dev,
        }
 
        /* clamp to max that the registers can hold */
-       if (INTEL_INFO(dev)->gen >= 8)
-               max = level == 0 ? 255 : 2047;
-       else if (INTEL_INFO(dev)->gen >= 7)
-               /* IVB/HSW primary/sprite plane watermarks */
-               max = level == 0 ? 127 : 1023;
-       else if (!is_sprite)
-               /* ILK/SNB primary plane watermarks */
-               max = level == 0 ? 127 : 511;
-       else
-               /* ILK/SNB sprite plane watermarks */
-               max = level == 0 ? 63 : 255;
-
-       return min(fifo_size, max);
+       return min(fifo_size, ilk_plane_wm_reg_max(dev, level, is_sprite));
 }
 
 /* Calculate the maximum cursor plane watermark */
@@ -1895,20 +1916,7 @@ static unsigned int ilk_cursor_wm_max(const struct drm_device *dev,
                return 64;
 
        /* otherwise just report max that registers can hold */
-       if (INTEL_INFO(dev)->gen >= 7)
-               return level == 0 ? 63 : 255;
-       else
-               return level == 0 ? 31 : 63;
-}
-
-/* Calculate the maximum FBC watermark */
-static unsigned int ilk_fbc_wm_max(const struct drm_device *dev)
-{
-       /* max that registers can hold */
-       if (INTEL_INFO(dev)->gen >= 8)
-               return 31;
-       else
-               return 15;
+       return ilk_cursor_wm_reg_max(dev, level);
 }
 
 static void ilk_compute_wm_maximums(const struct drm_device *dev,
@@ -1920,7 +1928,17 @@ static void ilk_compute_wm_maximums(const struct drm_device *dev,
        max->pri = ilk_plane_wm_max(dev, level, config, ddb_partitioning, false);
        max->spr = ilk_plane_wm_max(dev, level, config, ddb_partitioning, true);
        max->cur = ilk_cursor_wm_max(dev, level, config);
-       max->fbc = ilk_fbc_wm_max(dev);
+       max->fbc = ilk_fbc_wm_reg_max(dev);
+}
+
+static void ilk_compute_wm_reg_maximums(struct drm_device *dev,
+                                       int level,
+                                       struct ilk_wm_maximums *max)
+{
+       max->pri = ilk_plane_wm_reg_max(dev, level, false);
+       max->spr = ilk_plane_wm_reg_max(dev, level, true);
+       max->cur = ilk_cursor_wm_reg_max(dev, level);
+       max->fbc = ilk_fbc_wm_reg_max(dev);
 }
 
 static bool ilk_validate_wm_level(int level,
@@ -2059,7 +2077,7 @@ static void intel_fixup_cur_wm_latency(struct drm_device *dev, uint16_t wm[5])
                wm[3] *= 2;
 }
 
-static int ilk_wm_max_level(const struct drm_device *dev)
+int ilk_wm_max_level(const struct drm_device *dev)
 {
        /* how many WM levels are we expecting */
        if (IS_HASWELL(dev) || IS_BROADWELL(dev))
@@ -2155,38 +2173,52 @@ static void ilk_setup_wm_latency(struct drm_device *dev)
 }
 
 static void ilk_compute_wm_parameters(struct drm_crtc *crtc,
-                                     struct ilk_pipe_wm_parameters *p,
-                                     struct intel_wm_config *config)
+                                     struct ilk_pipe_wm_parameters *p)
 {
        struct drm_device *dev = crtc->dev;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        enum pipe pipe = intel_crtc->pipe;
        struct drm_plane *plane;
 
-       p->active = intel_crtc_active(crtc);
-       if (p->active) {
-               p->pipe_htotal = intel_crtc->config.adjusted_mode.crtc_htotal;
-               p->pixel_rate = ilk_pipe_pixel_rate(dev, crtc);
-               p->pri.bytes_per_pixel = crtc->primary->fb->bits_per_pixel / 8;
-               p->cur.bytes_per_pixel = 4;
-               p->pri.horiz_pixels = intel_crtc->config.pipe_src_w;
-               p->cur.horiz_pixels = intel_crtc->cursor_width;
-               /* TODO: for now, assume primary and cursor planes are always enabled. */
-               p->pri.enabled = true;
-               p->cur.enabled = true;
-       }
+       if (!intel_crtc_active(crtc))
+               return;
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-               config->num_pipes_active += intel_crtc_active(crtc);
+       p->active = true;
+       p->pipe_htotal = intel_crtc->config.adjusted_mode.crtc_htotal;
+       p->pixel_rate = ilk_pipe_pixel_rate(dev, crtc);
+       p->pri.bytes_per_pixel = crtc->primary->fb->bits_per_pixel / 8;
+       p->cur.bytes_per_pixel = 4;
+       p->pri.horiz_pixels = intel_crtc->config.pipe_src_w;
+       p->cur.horiz_pixels = intel_crtc->cursor_width;
+       /* TODO: for now, assume primary and cursor planes are always enabled. */
+       p->pri.enabled = true;
+       p->cur.enabled = true;
 
        drm_for_each_legacy_plane(plane, &dev->mode_config.plane_list) {
                struct intel_plane *intel_plane = to_intel_plane(plane);
 
-               if (intel_plane->pipe == pipe)
+               if (intel_plane->pipe == pipe) {
                        p->spr = intel_plane->wm;
+                       break;
+               }
+       }
+}
+
+static void ilk_compute_wm_config(struct drm_device *dev,
+                                 struct intel_wm_config *config)
+{
+       struct intel_crtc *intel_crtc;
 
-               config->sprites_enabled |= intel_plane->wm.enabled;
-               config->sprites_scaled |= intel_plane->wm.scaled;
+       /* Compute the currently _active_ config */
+       for_each_intel_crtc(dev, intel_crtc) {
+               const struct intel_pipe_wm *wm = &intel_crtc->wm.active;
+
+               if (!wm->pipe_enabled)
+                       continue;
+
+               config->sprites_enabled |= wm->sprites_enabled;
+               config->sprites_scaled |= wm->sprites_scaled;
+               config->num_pipes_active++;
        }
 }
 
@@ -2206,8 +2238,9 @@ static bool intel_compute_pipe_wm(struct drm_crtc *crtc,
        };
        struct ilk_wm_maximums max;
 
-       /* LP0 watermarks always use 1/2 DDB partitioning */
-       ilk_compute_wm_maximums(dev, 0, &config, INTEL_DDB_PART_1_2, &max);
+       pipe_wm->pipe_enabled = params->active;
+       pipe_wm->sprites_enabled = params->spr.enabled;
+       pipe_wm->sprites_scaled = params->spr.scaled;
 
        /* ILK/SNB: LP2+ watermarks only w/o sprites */
        if (INTEL_INFO(dev)->gen <= 6 && params->spr.enabled)
@@ -2217,15 +2250,37 @@ static bool intel_compute_pipe_wm(struct drm_crtc *crtc,
        if (params->spr.scaled)
                max_level = 0;
 
-       for (level = 0; level <= max_level; level++)
-               ilk_compute_wm_level(dev_priv, level, params,
-                                    &pipe_wm->wm[level]);
+       ilk_compute_wm_level(dev_priv, 0, params, &pipe_wm->wm[0]);
 
        if (IS_HASWELL(dev) || IS_BROADWELL(dev))
                pipe_wm->linetime = hsw_compute_linetime_wm(dev, crtc);
 
+       /* LP0 watermarks always use 1/2 DDB partitioning */
+       ilk_compute_wm_maximums(dev, 0, &config, INTEL_DDB_PART_1_2, &max);
+
        /* At least LP0 must be valid */
-       return ilk_validate_wm_level(0, &max, &pipe_wm->wm[0]);
+       if (!ilk_validate_wm_level(0, &max, &pipe_wm->wm[0]))
+               return false;
+
+       ilk_compute_wm_reg_maximums(dev, 1, &max);
+
+       for (level = 1; level <= max_level; level++) {
+               struct intel_wm_level wm = {};
+
+               ilk_compute_wm_level(dev_priv, level, params, &wm);
+
+               /*
+                * Disable any watermark level that exceeds the
+                * register maximums since such watermarks are
+                * always invalid.
+                */
+               if (!ilk_validate_wm_level(level, &max, &wm))
+                       break;
+
+               pipe_wm->wm[level] = wm;
+       }
+
+       return true;
 }
 
 /*
@@ -2237,20 +2292,28 @@ static void ilk_merge_wm_level(struct drm_device *dev,
 {
        const struct intel_crtc *intel_crtc;
 
-       list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) {
-               const struct intel_wm_level *wm =
-                       &intel_crtc->wm.active.wm[level];
+       ret_wm->enable = true;
+
+       for_each_intel_crtc(dev, intel_crtc) {
+               const struct intel_pipe_wm *active = &intel_crtc->wm.active;
+               const struct intel_wm_level *wm = &active->wm[level];
+
+               if (!active->pipe_enabled)
+                       continue;
 
+               /*
+                * The watermark values may have been used in the past,
+                * so we must maintain them in the registers for some
+                * time even if the level is now disabled.
+                */
                if (!wm->enable)
-                       return;
+                       ret_wm->enable = false;
 
                ret_wm->pri_val = max(ret_wm->pri_val, wm->pri_val);
                ret_wm->spr_val = max(ret_wm->spr_val, wm->spr_val);
                ret_wm->cur_val = max(ret_wm->cur_val, wm->cur_val);
                ret_wm->fbc_val = max(ret_wm->fbc_val, wm->fbc_val);
        }
-
-       ret_wm->enable = true;
 }
 
 /*
@@ -2262,6 +2325,7 @@ static void ilk_wm_merge(struct drm_device *dev,
                         struct intel_pipe_wm *merged)
 {
        int level, max_level = ilk_wm_max_level(dev);
+       int last_enabled_level = max_level;
 
        /* ILK/SNB/IVB: LP1+ watermarks only w/ single pipe */
        if ((INTEL_INFO(dev)->gen <= 6 || IS_IVYBRIDGE(dev)) &&
@@ -2277,15 +2341,19 @@ static void ilk_wm_merge(struct drm_device *dev,
 
                ilk_merge_wm_level(dev, level, wm);
 
-               if (!ilk_validate_wm_level(level, max, wm))
-                       break;
+               if (level > last_enabled_level)
+                       wm->enable = false;
+               else if (!ilk_validate_wm_level(level, max, wm))
+                       /* make sure all following levels get disabled */
+                       last_enabled_level = level - 1;
 
                /*
                 * The spec says it is preferred to disable
                 * FBC WMs instead of disabling a WM level.
                 */
                if (wm->fbc_val > max->fbc) {
-                       merged->fbc_wm_enabled = false;
+                       if (wm->enable)
+                               merged->fbc_wm_enabled = false;
                        wm->fbc_val = 0;
                }
        }
@@ -2340,14 +2408,19 @@ static void ilk_compute_wm_results(struct drm_device *dev,
                level = ilk_wm_lp_to_level(wm_lp, merged);
 
                r = &merged->wm[level];
-               if (!r->enable)
-                       break;
 
-               results->wm_lp[wm_lp - 1] = WM3_LP_EN |
+               /*
+                * Maintain the watermark values even if the level is
+                * disabled. Doing otherwise could cause underruns.
+                */
+               results->wm_lp[wm_lp - 1] =
                        (ilk_wm_lp_latency(dev, level) << WM1_LP_LATENCY_SHIFT) |
                        (r->pri_val << WM1_LP_SR_SHIFT) |
                        r->cur_val;
 
+               if (r->enable)
+                       results->wm_lp[wm_lp - 1] |= WM1_LP_SR_EN;
+
                if (INTEL_INFO(dev)->gen >= 8)
                        results->wm_lp[wm_lp - 1] |=
                                r->fbc_val << WM1_LP_FBC_SHIFT_BDW;
@@ -2355,6 +2428,10 @@ static void ilk_compute_wm_results(struct drm_device *dev,
                        results->wm_lp[wm_lp - 1] |=
                                r->fbc_val << WM1_LP_FBC_SHIFT;
 
+               /*
+                * Always set WM1S_LP_EN when spr_val != 0, even if the
+                * level is disabled. Doing otherwise could cause underruns.
+                */
                if (INTEL_INFO(dev)->gen <= 6 && r->spr_val) {
                        WARN_ON(wm_lp != 1);
                        results->wm_lp_spr[wm_lp - 1] = WM1S_LP_EN | r->spr_val;
@@ -2363,7 +2440,7 @@ static void ilk_compute_wm_results(struct drm_device *dev,
        }
 
        /* LP0 register values */
-       list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) {
+       for_each_intel_crtc(dev, intel_crtc) {
                enum pipe pipe = intel_crtc->pipe;
                const struct intel_wm_level *r =
                        &intel_crtc->wm.active.wm[0];
@@ -2598,7 +2675,7 @@ static void ilk_update_wm(struct drm_crtc *crtc)
        struct intel_pipe_wm lp_wm_1_2 = {}, lp_wm_5_6 = {}, *best_lp_wm;
        struct intel_wm_config config = {};
 
-       ilk_compute_wm_parameters(crtc, &params, &config);
+       ilk_compute_wm_parameters(crtc, &params);
 
        intel_compute_pipe_wm(crtc, &params, &pipe_wm);
 
@@ -2607,6 +2684,8 @@ static void ilk_update_wm(struct drm_crtc *crtc)
 
        intel_crtc->wm.active = pipe_wm;
 
+       ilk_compute_wm_config(dev, &config);
+
        ilk_compute_wm_maximums(dev, 1, &config, INTEL_DDB_PART_1_2, &max);
        ilk_wm_merge(dev, &config, &max, &lp_wm_1_2);
 
@@ -2673,7 +2752,9 @@ static void ilk_pipe_wm_get_hw_state(struct drm_crtc *crtc)
        if (IS_HASWELL(dev) || IS_BROADWELL(dev))
                hw->wm_linetime[pipe] = I915_READ(PIPE_WM_LINETIME(pipe));
 
-       if (intel_crtc_active(crtc)) {
+       active->pipe_enabled = intel_crtc_active(crtc);
+
+       if (active->pipe_enabled) {
                u32 tmp = hw->wm_pipe[pipe];
 
                /*
@@ -2706,7 +2787,7 @@ void ilk_wm_get_hw_state(struct drm_device *dev)
        struct ilk_wm_values *hw = &dev_priv->wm.hw;
        struct drm_crtc *crtc;
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+       for_each_crtc(dev, crtc)
                ilk_pipe_wm_get_hw_state(crtc);
 
        hw->wm_lp[0] = I915_READ(WM1_LP_ILK);
@@ -2714,8 +2795,10 @@ void ilk_wm_get_hw_state(struct drm_device *dev)
        hw->wm_lp[2] = I915_READ(WM3_LP_ILK);
 
        hw->wm_lp_spr[0] = I915_READ(WM1S_LP_ILK);
-       hw->wm_lp_spr[1] = I915_READ(WM2S_LP_IVB);
-       hw->wm_lp_spr[2] = I915_READ(WM3S_LP_IVB);
+       if (INTEL_INFO(dev)->gen >= 7) {
+               hw->wm_lp_spr[1] = I915_READ(WM2S_LP_IVB);
+               hw->wm_lp_spr[2] = I915_READ(WM3S_LP_IVB);
+       }
 
        if (IS_HASWELL(dev) || IS_BROADWELL(dev))
                hw->partitioning = (I915_READ(WM_MISC) & WM_MISC_DATA_PARTITION_5_6) ?
@@ -3071,6 +3154,9 @@ static u32 gen6_rps_pm_mask(struct drm_i915_private *dev_priv, u8 val)
        if (INTEL_INFO(dev_priv->dev)->gen <= 7 && !IS_HASWELL(dev_priv->dev))
                mask |= GEN6_PM_RP_UP_EI_EXPIRED;
 
+       if (IS_GEN8(dev_priv->dev))
+               mask |= GEN8_PMINTR_REDIRECT_TO_NON_DISP;
+
        return ~mask;
 }
 
@@ -3091,7 +3177,7 @@ void gen6_set_rps(struct drm_device *dev, u8 val)
        if (val != dev_priv->rps.cur_freq) {
                gen6_set_rps_thresholds(dev_priv, val);
 
-               if (IS_HASWELL(dev))
+               if (IS_HASWELL(dev) || IS_BROADWELL(dev))
                        I915_WRITE(GEN6_RPNSWREQ,
                                   HSW_FREQUENCY(val));
                else
@@ -3134,16 +3220,7 @@ static void vlv_set_rps_idle(struct drm_i915_private *dev_priv)
        /* Mask turbo interrupt so that they will not come in between */
        I915_WRITE(GEN6_PMINTRMSK, 0xffffffff);
 
-       /* Bring up the Gfx clock */
-       I915_WRITE(VLV_GTLC_SURVIVABILITY_REG,
-               I915_READ(VLV_GTLC_SURVIVABILITY_REG) |
-                               VLV_GFX_CLK_FORCE_ON_BIT);
-
-       if (wait_for(((VLV_GFX_CLK_STATUS_BIT &
-               I915_READ(VLV_GTLC_SURVIVABILITY_REG)) != 0), 5)) {
-                       DRM_ERROR("GFX_CLK_ON request timed out\n");
-               return;
-       }
+       vlv_force_gfx_clock(dev_priv, true);
 
        dev_priv->rps.cur_freq = dev_priv->rps.min_freq_softlimit;
 
@@ -3154,10 +3231,7 @@ static void vlv_set_rps_idle(struct drm_i915_private *dev_priv)
                                & GENFREQSTATUS) == 0, 5))
                DRM_ERROR("timed out waiting for Punit\n");
 
-       /* Release the Gfx clock */
-       I915_WRITE(VLV_GTLC_SURVIVABILITY_REG,
-               I915_READ(VLV_GTLC_SURVIVABILITY_REG) &
-                               ~VLV_GFX_CLK_FORCE_ON_BIT);
+       vlv_force_gfx_clock(dev_priv, false);
 
        I915_WRITE(GEN6_PMINTRMSK,
                   gen6_rps_pm_mask(dev_priv, dev_priv->rps.cur_freq));
@@ -3215,6 +3289,26 @@ void valleyview_set_rps(struct drm_device *dev, u8 val)
        trace_intel_gpu_freq_change(vlv_gpu_freq(dev_priv, val));
 }
 
+static void gen8_disable_rps_interrupts(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       I915_WRITE(GEN6_PMINTRMSK, ~GEN8_PMINTR_REDIRECT_TO_NON_DISP);
+       I915_WRITE(GEN8_GT_IER(2), I915_READ(GEN8_GT_IER(2)) &
+                                  ~dev_priv->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 (GEN8_GT_IMR(2)) to mask PM interrupts. The only risk is in
+        * leaving stale bits in GEN8_GT_IIR(2) and GEN8_GT_IMR(2) which
+        * gen8_enable_rps will clean up. */
+
+       spin_lock_irq(&dev_priv->irq_lock);
+       dev_priv->rps.pm_iir = 0;
+       spin_unlock_irq(&dev_priv->irq_lock);
+
+       I915_WRITE(GEN8_GT_IIR(2), dev_priv->pm_rps_events);
+}
+
 static void gen6_disable_rps_interrupts(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3241,7 +3335,10 @@ static void gen6_disable_rps(struct drm_device *dev)
        I915_WRITE(GEN6_RC_CONTROL, 0);
        I915_WRITE(GEN6_RPNSWREQ, 1 << 31);
 
-       gen6_disable_rps_interrupts(dev);
+       if (IS_BROADWELL(dev))
+               gen8_disable_rps_interrupts(dev);
+       else
+               gen6_disable_rps_interrupts(dev);
 }
 
 static void valleyview_disable_rps(struct drm_device *dev)
@@ -3255,21 +3352,44 @@ static void valleyview_disable_rps(struct drm_device *dev)
 
 static void intel_print_rc6_info(struct drm_device *dev, u32 mode)
 {
+       if (IS_VALLEYVIEW(dev)) {
+               if (mode & (GEN7_RC_CTL_TO_MODE | GEN6_RC_CTL_EI_MODE(1)))
+                       mode = GEN6_RC_CTL_RC6_ENABLE;
+               else
+                       mode = 0;
+       }
        DRM_INFO("Enabling RC6 states: RC6 %s, RC6p %s, RC6pp %s\n",
                 (mode & GEN6_RC_CTL_RC6_ENABLE) ? "on" : "off",
                 (mode & GEN6_RC_CTL_RC6p_ENABLE) ? "on" : "off",
                 (mode & GEN6_RC_CTL_RC6pp_ENABLE) ? "on" : "off");
 }
 
-int intel_enable_rc6(const struct drm_device *dev)
+static int sanitize_rc6_option(const struct drm_device *dev, int enable_rc6)
 {
        /* No RC6 before Ironlake */
        if (INTEL_INFO(dev)->gen < 5)
                return 0;
 
+       /* RC6 is only on Ironlake mobile not on desktop */
+       if (INTEL_INFO(dev)->gen == 5 && !IS_IRONLAKE_M(dev))
+               return 0;
+
        /* Respect the kernel parameter if it is set */
-       if (i915.enable_rc6 >= 0)
-               return i915.enable_rc6;
+       if (enable_rc6 >= 0) {
+               int mask;
+
+               if (INTEL_INFO(dev)->gen == 6 || IS_IVYBRIDGE(dev))
+                       mask = INTEL_RC6_ENABLE | INTEL_RC6p_ENABLE |
+                              INTEL_RC6pp_ENABLE;
+               else
+                       mask = INTEL_RC6_ENABLE;
+
+               if ((enable_rc6 & mask) != enable_rc6)
+                       DRM_INFO("Adjusting RC6 mask to %d (requested %d, valid %d)\n",
+                                enable_rc6 & mask, enable_rc6, mask);
+
+               return enable_rc6 & mask;
+       }
 
        /* Disable RC6 on Ironlake */
        if (INTEL_INFO(dev)->gen == 5)
@@ -3281,6 +3401,22 @@ int intel_enable_rc6(const struct drm_device *dev)
        return INTEL_RC6_ENABLE;
 }
 
+int intel_enable_rc6(const struct drm_device *dev)
+{
+       return i915.enable_rc6;
+}
+
+static void gen8_enable_rps_interrupts(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       spin_lock_irq(&dev_priv->irq_lock);
+       WARN_ON(dev_priv->rps.pm_iir);
+       bdw_enable_pm_irq(dev_priv, dev_priv->pm_rps_events);
+       I915_WRITE(GEN8_GT_IIR(2), dev_priv->pm_rps_events);
+       spin_unlock_irq(&dev_priv->irq_lock);
+}
+
 static void gen6_enable_rps_interrupts(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3292,10 +3428,31 @@ static void gen6_enable_rps_interrupts(struct drm_device *dev)
        spin_unlock_irq(&dev_priv->irq_lock);
 }
 
+static void parse_rp_state_cap(struct drm_i915_private *dev_priv, u32 rp_state_cap)
+{
+       /* All of these values are in units of 50MHz */
+       dev_priv->rps.cur_freq          = 0;
+       /* static values from HW: RP0 < RPe < RP1 < RPn (min_freq) */
+       dev_priv->rps.rp1_freq          = (rp_state_cap >>  8) & 0xff;
+       dev_priv->rps.rp0_freq          = (rp_state_cap >>  0) & 0xff;
+       dev_priv->rps.min_freq          = (rp_state_cap >> 16) & 0xff;
+       /* XXX: only BYT has a special efficient freq */
+       dev_priv->rps.efficient_freq    = dev_priv->rps.rp1_freq;
+       /* hw_max = RP0 until we check for overclocking */
+       dev_priv->rps.max_freq          = dev_priv->rps.rp0_freq;
+
+       /* Preserve min/max settings in case of re-init */
+       if (dev_priv->rps.max_freq_softlimit == 0)
+               dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq;
+
+       if (dev_priv->rps.min_freq_softlimit == 0)
+               dev_priv->rps.min_freq_softlimit = dev_priv->rps.min_freq;
+}
+
 static void gen8_enable_rps(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        uint32_t rc6_mask = 0, rp_state_cap;
        int unused;
 
@@ -3310,6 +3467,7 @@ static void gen8_enable_rps(struct drm_device *dev)
        I915_WRITE(GEN6_RC_CONTROL, 0);
 
        rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
+       parse_rp_state_cap(dev_priv, rp_state_cap);
 
        /* 2b: Program RC6 thresholds.*/
        I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 40 << 16);
@@ -3329,8 +3487,10 @@ static void gen8_enable_rps(struct drm_device *dev)
                                    rc6_mask);
 
        /* 4 Program defaults and thresholds for RPS*/
-       I915_WRITE(GEN6_RPNSWREQ, HSW_FREQUENCY(10)); /* Request 500 MHz */
-       I915_WRITE(GEN6_RC_VIDEO_FREQ, HSW_FREQUENCY(12)); /* Request 600 MHz */
+       I915_WRITE(GEN6_RPNSWREQ,
+                  HSW_FREQUENCY(dev_priv->rps.rp1_freq));
+       I915_WRITE(GEN6_RC_VIDEO_FREQ,
+                  HSW_FREQUENCY(dev_priv->rps.rp1_freq));
        /* NB: Docs say 1s, and 1000000 - which aren't equivalent */
        I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 100000000 / 128); /* 1 second timeout */
 
@@ -3346,11 +3506,15 @@ static void gen8_enable_rps(struct drm_device *dev)
 
        I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10);
 
+       /* WaDisablePwrmtrEvent:chv (pre-production hw) */
+       I915_WRITE(0xA80C, I915_READ(0xA80C) & 0x00ffffff);
+       I915_WRITE(0xA810, I915_READ(0xA810) & 0xffffff00);
+
        /* 5: Enable RPS */
        I915_WRITE(GEN6_RP_CONTROL,
                   GEN6_RP_MEDIA_TURBO |
                   GEN6_RP_MEDIA_HW_NORMAL_MODE |
-                  GEN6_RP_MEDIA_IS_GFX |
+                  GEN6_RP_MEDIA_IS_GFX | /* WaSetMaskForGfxBusyness:chv (pre-production hw ?) */
                   GEN6_RP_ENABLE |
                   GEN6_RP_UP_BUSY_AVG |
                   GEN6_RP_DOWN_IDLE_AVG);
@@ -3359,7 +3523,7 @@ static void gen8_enable_rps(struct drm_device *dev)
 
        gen6_set_rps(dev, (I915_READ(GEN6_GT_PERF_STATUS) & 0xff00) >> 8);
 
-       gen6_enable_rps_interrupts(dev);
+       gen8_enable_rps_interrupts(dev);
 
        gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL);
 }
@@ -3367,7 +3531,7 @@ static void gen8_enable_rps(struct drm_device *dev)
 static void gen6_enable_rps(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        u32 rp_state_cap;
        u32 gt_perf_status;
        u32 rc6vids, pcu_mbox = 0, rc6_mask = 0;
@@ -3396,23 +3560,7 @@ static void gen6_enable_rps(struct drm_device *dev)
        rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
        gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
 
-       /* All of these values are in units of 50MHz */
-       dev_priv->rps.cur_freq          = 0;
-       /* static values from HW: RP0 < RPe < RP1 < RPn (min_freq) */
-       dev_priv->rps.rp1_freq          = (rp_state_cap >>  8) & 0xff;
-       dev_priv->rps.rp0_freq          = (rp_state_cap >>  0) & 0xff;
-       dev_priv->rps.min_freq          = (rp_state_cap >> 16) & 0xff;
-       /* XXX: only BYT has a special efficient freq */
-       dev_priv->rps.efficient_freq    = dev_priv->rps.rp1_freq;
-       /* hw_max = RP0 until we check for overclocking */
-       dev_priv->rps.max_freq          = dev_priv->rps.rp0_freq;
-
-       /* Preserve min/max settings in case of re-init */
-       if (dev_priv->rps.max_freq_softlimit == 0)
-               dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq;
-
-       if (dev_priv->rps.min_freq_softlimit == 0)
-               dev_priv->rps.min_freq_softlimit = dev_priv->rps.min_freq;
+       parse_rp_state_cap(dev_priv, rp_state_cap);
 
        /* disable the counters and set deterministic thresholds */
        I915_WRITE(GEN6_RC_CONTROL, 0);
@@ -3494,7 +3642,7 @@ static void gen6_enable_rps(struct drm_device *dev)
        gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL);
 }
 
-void gen6_update_ring_freq(struct drm_device *dev)
+static void __gen6_update_ring_freq(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        int min_freq = 15;
@@ -3564,6 +3712,18 @@ void gen6_update_ring_freq(struct drm_device *dev)
        }
 }
 
+void gen6_update_ring_freq(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (INTEL_INFO(dev)->gen < 6 || IS_VALLEYVIEW(dev))
+               return;
+
+       mutex_lock(&dev_priv->rps.hw_lock);
+       __gen6_update_ring_freq(dev);
+       mutex_unlock(&dev_priv->rps.hw_lock);
+}
+
 int valleyview_rps_max_freq(struct drm_i915_private *dev_priv)
 {
        u32 val, rp0;
@@ -3658,10 +3818,49 @@ static void valleyview_cleanup_pctx(struct drm_device *dev)
        dev_priv->vlv_pctx = NULL;
 }
 
+static void valleyview_init_gt_powersave(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       valleyview_setup_pctx(dev);
+
+       mutex_lock(&dev_priv->rps.hw_lock);
+
+       dev_priv->rps.max_freq = valleyview_rps_max_freq(dev_priv);
+       dev_priv->rps.rp0_freq = dev_priv->rps.max_freq;
+       DRM_DEBUG_DRIVER("max GPU freq: %d MHz (%u)\n",
+                        vlv_gpu_freq(dev_priv, dev_priv->rps.max_freq),
+                        dev_priv->rps.max_freq);
+
+       dev_priv->rps.efficient_freq = valleyview_rps_rpe_freq(dev_priv);
+       DRM_DEBUG_DRIVER("RPe GPU freq: %d MHz (%u)\n",
+                        vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq),
+                        dev_priv->rps.efficient_freq);
+
+       dev_priv->rps.min_freq = valleyview_rps_min_freq(dev_priv);
+       DRM_DEBUG_DRIVER("min GPU freq: %d MHz (%u)\n",
+                        vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq),
+                        dev_priv->rps.min_freq);
+
+       /* Preserve min/max settings in case of re-init */
+       if (dev_priv->rps.max_freq_softlimit == 0)
+               dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq;
+
+       if (dev_priv->rps.min_freq_softlimit == 0)
+               dev_priv->rps.min_freq_softlimit = dev_priv->rps.min_freq;
+
+       mutex_unlock(&dev_priv->rps.hw_lock);
+}
+
+static void valleyview_cleanup_gt_powersave(struct drm_device *dev)
+{
+       valleyview_cleanup_pctx(dev);
+}
+
 static void valleyview_enable_rps(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        u32 gtfifodbg, val, rc6_mode = 0;
        int i;
 
@@ -3724,29 +3923,6 @@ static void valleyview_enable_rps(struct drm_device *dev)
                         vlv_gpu_freq(dev_priv, dev_priv->rps.cur_freq),
                         dev_priv->rps.cur_freq);
 
-       dev_priv->rps.max_freq = valleyview_rps_max_freq(dev_priv);
-       dev_priv->rps.rp0_freq  = dev_priv->rps.max_freq;
-       DRM_DEBUG_DRIVER("max GPU freq: %d MHz (%u)\n",
-                        vlv_gpu_freq(dev_priv, dev_priv->rps.max_freq),
-                        dev_priv->rps.max_freq);
-
-       dev_priv->rps.efficient_freq = valleyview_rps_rpe_freq(dev_priv);
-       DRM_DEBUG_DRIVER("RPe GPU freq: %d MHz (%u)\n",
-                        vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq),
-                        dev_priv->rps.efficient_freq);
-
-       dev_priv->rps.min_freq = valleyview_rps_min_freq(dev_priv);
-       DRM_DEBUG_DRIVER("min GPU freq: %d MHz (%u)\n",
-                        vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq),
-                        dev_priv->rps.min_freq);
-
-       /* Preserve min/max settings in case of re-init */
-       if (dev_priv->rps.max_freq_softlimit == 0)
-               dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq;
-
-       if (dev_priv->rps.min_freq_softlimit == 0)
-               dev_priv->rps.min_freq_softlimit = dev_priv->rps.min_freq;
-
        DRM_DEBUG_DRIVER("setting GPU freq to %d MHz (%u)\n",
                         vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq),
                         dev_priv->rps.efficient_freq);
@@ -3815,7 +3991,7 @@ static int ironlake_setup_rc6(struct drm_device *dev)
 static void ironlake_enable_rc6(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
+       struct intel_engine_cs *ring = &dev_priv->ring[RCS];
        bool was_interruptible;
        int ret;
 
@@ -3873,7 +4049,7 @@ static void ironlake_enable_rc6(struct drm_device *dev)
        I915_WRITE(PWRCTXA, i915_gem_obj_ggtt_offset(dev_priv->ips.pwrctx) | PWRCTX_EN);
        I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) & ~RCX_SW_EXIT);
 
-       intel_print_rc6_info(dev, INTEL_RC6_ENABLE);
+       intel_print_rc6_info(dev, GEN6_RC_CTL_RC6_ENABLE);
 }
 
 static unsigned long intel_pxfreq(u32 vidfreq)
@@ -4327,7 +4503,7 @@ EXPORT_SYMBOL_GPL(i915_gpu_lower);
 bool i915_gpu_busy(void)
 {
        struct drm_i915_private *dev_priv;
-       struct intel_ring_buffer *ring;
+       struct intel_engine_cs *ring;
        bool ret = false;
        int i;
 
@@ -4487,14 +4663,16 @@ static void intel_init_emon(struct drm_device *dev)
 
 void intel_init_gt_powersave(struct drm_device *dev)
 {
+       i915.enable_rc6 = sanitize_rc6_option(dev, i915.enable_rc6);
+
        if (IS_VALLEYVIEW(dev))
-               valleyview_setup_pctx(dev);
+               valleyview_init_gt_powersave(dev);
 }
 
 void intel_cleanup_gt_powersave(struct drm_device *dev)
 {
        if (IS_VALLEYVIEW(dev))
-               valleyview_cleanup_pctx(dev);
+               valleyview_cleanup_gt_powersave(dev);
 }
 
 void intel_disable_gt_powersave(struct drm_device *dev)
@@ -4507,8 +4685,10 @@ void intel_disable_gt_powersave(struct drm_device *dev)
        if (IS_IRONLAKE_M(dev)) {
                ironlake_disable_drps(dev);
                ironlake_disable_rc6(dev);
-       } else if (INTEL_INFO(dev)->gen >= 6) {
-               cancel_delayed_work_sync(&dev_priv->rps.delayed_resume_work);
+       } else if (IS_GEN6(dev) || IS_GEN7(dev) || IS_BROADWELL(dev)) {
+               if (cancel_delayed_work_sync(&dev_priv->rps.delayed_resume_work))
+                       intel_runtime_pm_put(dev_priv);
+
                cancel_work_sync(&dev_priv->rps.work);
                mutex_lock(&dev_priv->rps.hw_lock);
                if (IS_VALLEYVIEW(dev))
@@ -4533,13 +4713,15 @@ static void intel_gen6_powersave_work(struct work_struct *work)
                valleyview_enable_rps(dev);
        } else if (IS_BROADWELL(dev)) {
                gen8_enable_rps(dev);
-               gen6_update_ring_freq(dev);
+               __gen6_update_ring_freq(dev);
        } else {
                gen6_enable_rps(dev);
-               gen6_update_ring_freq(dev);
+               __gen6_update_ring_freq(dev);
        }
        dev_priv->rps.enabled = true;
        mutex_unlock(&dev_priv->rps.hw_lock);
+
+       intel_runtime_pm_put(dev_priv);
 }
 
 void intel_enable_gt_powersave(struct drm_device *dev)
@@ -4547,20 +4729,38 @@ void intel_enable_gt_powersave(struct drm_device *dev)
        struct drm_i915_private *dev_priv = dev->dev_private;
 
        if (IS_IRONLAKE_M(dev)) {
+               mutex_lock(&dev->struct_mutex);
                ironlake_enable_drps(dev);
                ironlake_enable_rc6(dev);
                intel_init_emon(dev);
-       } else if (IS_GEN6(dev) || IS_GEN7(dev)) {
+               mutex_unlock(&dev->struct_mutex);
+       } else if (IS_GEN6(dev) || IS_GEN7(dev) || IS_BROADWELL(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
                 * to make resume and init faster.
+                *
+                * We depend on the HW RC6 power context save/restore
+                * mechanism when entering D3 through runtime PM suspend. So
+                * disable RPM until RPS/RC6 is properly setup. We can only
+                * get here via the driver load/system resume/runtime resume
+                * paths, so the _noresume version is enough (and in case of
+                * runtime resume it's necessary).
                 */
-               schedule_delayed_work(&dev_priv->rps.delayed_resume_work,
-                                     round_jiffies_up_relative(HZ));
+               if (schedule_delayed_work(&dev_priv->rps.delayed_resume_work,
+                                          round_jiffies_up_relative(HZ)))
+                       intel_runtime_pm_get_noresume(dev_priv);
        }
 }
 
+void intel_reset_gt_powersave(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       dev_priv->rps.enabled = false;
+       intel_enable_gt_powersave(dev);
+}
+
 static void ibx_init_clock_gating(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -4666,6 +4866,9 @@ static void ironlake_init_clock_gating(struct drm_device *dev)
        I915_WRITE(CACHE_MODE_0,
                   _MASKED_BIT_ENABLE(CM0_PIPELINED_RENDER_FLUSH_DISABLE));
 
+       /* WaDisable_RenderCache_OperationalFlush:ilk */
+       I915_WRITE(CACHE_MODE_0, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE));
+
        g4x_disable_trickle_feed(dev);
 
        ibx_init_clock_gating(dev);
@@ -4741,6 +4944,9 @@ static void gen6_init_clock_gating(struct drm_device *dev)
                I915_WRITE(GEN6_GT_MODE,
                           _MASKED_BIT_ENABLE(GEN6_TD_FOUR_ROW_DISPATCH_DISABLE));
 
+       /* WaDisable_RenderCache_OperationalFlush:snb */
+       I915_WRITE(CACHE_MODE_0, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE));
+
        /*
         * BSpec recoomends 8x4 when MSAA is used,
         * however in practice 16x4 seems fastest.
@@ -4909,6 +5115,10 @@ static void gen8_init_clock_gating(struct drm_device *dev)
        I915_WRITE(GEN7_HALF_SLICE_CHICKEN1,
                   _MASKED_BIT_ENABLE(GEN7_SINGLE_SUBSCAN_DISPATCH_ENABLE));
 
+       /* WaDisableDopClockGating:bdw May not be needed for production */
+       I915_WRITE(GEN7_ROW_CHICKEN2,
+                  _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE));
+
        /* WaSwitchSolVfFArbitrationPriority:bdw */
        I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) | HSW_ECOCHK_ARB_PRIO_SOL);
 
@@ -4980,6 +5190,9 @@ static void haswell_init_clock_gating(struct drm_device *dev)
        I915_WRITE(GEN7_FF_THREAD_MODE,
                   I915_READ(GEN7_FF_THREAD_MODE) & ~GEN7_FF_VS_REF_CNT_FFME);
 
+       /* WaDisable_RenderCache_OperationalFlush:hsw */
+       I915_WRITE(CACHE_MODE_0_GEN7, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE));
+
        /* enable HiZ Raw Stall Optimization */
        I915_WRITE(CACHE_MODE_0_GEN7,
                   _MASKED_BIT_DISABLE(HIZ_RAW_STALL_OPT_DISABLE));
@@ -5032,6 +5245,9 @@ static void ivybridge_init_clock_gating(struct drm_device *dev)
                I915_WRITE(GEN7_HALF_SLICE_CHICKEN1,
                           _MASKED_BIT_ENABLE(GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE));
 
+       /* WaDisable_RenderCache_OperationalFlush:ivb */
+       I915_WRITE(CACHE_MODE_0_GEN7, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE));
+
        /* Apply the WaDisableRHWOOptimizationForRenderHang:ivb workaround. */
        I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1,
                   GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC);
@@ -5126,6 +5342,10 @@ static void valleyview_init_clock_gating(struct drm_device *dev)
        }
        DRM_DEBUG_DRIVER("DDR speed: %d MHz", dev_priv->mem_freq);
 
+       dev_priv->vlv_cdclk_freq = valleyview_cur_cdclk(dev_priv);
+       DRM_DEBUG_DRIVER("Current CD clock rate: %d MHz",
+                        dev_priv->vlv_cdclk_freq);
+
        I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE);
 
        /* WaDisableEarlyCull:vlv */
@@ -5143,6 +5363,9 @@ static void valleyview_init_clock_gating(struct drm_device *dev)
                   _MASKED_BIT_ENABLE(GEN7_MAX_PS_THREAD_DEP |
                                      GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE));
 
+       /* WaDisable_RenderCache_OperationalFlush:vlv */
+       I915_WRITE(CACHE_MODE_0_GEN7, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE));
+
        /* WaForceL3Serialization:vlv */
        I915_WRITE(GEN7_L3SQCREG4, I915_READ(GEN7_L3SQCREG4) &
                   ~L3SQ_URB_READ_CAM_MATCH_DISABLE);
@@ -5165,8 +5388,11 @@ static void valleyview_init_clock_gating(struct drm_device *dev)
        I915_WRITE(GEN6_UCGCTL2,
                   GEN6_RCZUNIT_CLOCK_GATE_DISABLE);
 
-       /* WaDisableL3Bank2xClockGate:vlv */
-       I915_WRITE(GEN7_UCGCTL4, GEN7_L3BANK2X_CLOCK_GATE_DISABLE);
+       /* WaDisableL3Bank2xClockGate:vlv
+        * Disabling L3 clock gating- MMIO 940c[25] = 1
+        * Set bit 25, to disable L3_BANK_2x_CLK_GATING */
+       I915_WRITE(GEN7_UCGCTL4,
+                  I915_READ(GEN7_UCGCTL4) | GEN7_L3BANK2X_CLOCK_GATE_DISABLE);
 
        I915_WRITE(MI_ARB_VLV, MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE);
 
@@ -5191,6 +5417,59 @@ static void valleyview_init_clock_gating(struct drm_device *dev)
        I915_WRITE(VLV_GUNIT_CLOCK_GATE, GCFG_DIS);
 }
 
+static void cherryview_init_clock_gating(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE);
+
+       I915_WRITE(MI_ARB_VLV, MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE);
+
+       /* WaDisablePartialInstShootdown:chv */
+       I915_WRITE(GEN8_ROW_CHICKEN,
+                  _MASKED_BIT_ENABLE(PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE));
+
+       /* WaDisableThreadStallDopClockGating:chv */
+       I915_WRITE(GEN8_ROW_CHICKEN,
+                  _MASKED_BIT_ENABLE(STALL_DOP_GATING_DISABLE));
+
+       /* WaVSRefCountFullforceMissDisable:chv */
+       /* WaDSRefCountFullforceMissDisable:chv */
+       I915_WRITE(GEN7_FF_THREAD_MODE,
+                  I915_READ(GEN7_FF_THREAD_MODE) &
+                  ~(GEN8_FF_DS_REF_CNT_FFME | GEN7_FF_VS_REF_CNT_FFME));
+
+       /* WaDisableSemaphoreAndSyncFlipWait:chv */
+       I915_WRITE(GEN6_RC_SLEEP_PSMI_CONTROL,
+                  _MASKED_BIT_ENABLE(GEN8_RC_SEMA_IDLE_MSG_DISABLE));
+
+       /* WaDisableCSUnitClockGating:chv */
+       I915_WRITE(GEN6_UCGCTL1, I915_READ(GEN6_UCGCTL1) |
+                  GEN6_CSUNIT_CLOCK_GATE_DISABLE);
+
+       /* WaDisableSDEUnitClockGating:chv */
+       I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) |
+                  GEN8_SDEUNIT_CLOCK_GATE_DISABLE);
+
+       /* WaDisableSamplerPowerBypass:chv (pre-production hw) */
+       I915_WRITE(HALF_SLICE_CHICKEN3,
+                  _MASKED_BIT_ENABLE(GEN8_SAMPLER_POWER_BYPASS_DIS));
+
+       /* WaDisableGunitClockGating:chv (pre-production hw) */
+       I915_WRITE(VLV_GUNIT_CLOCK_GATE, I915_READ(VLV_GUNIT_CLOCK_GATE) |
+                  GINT_DIS);
+
+       /* WaDisableFfDopClockGating:chv (pre-production hw) */
+       I915_WRITE(GEN6_RC_SLEEP_PSMI_CONTROL,
+                  _MASKED_BIT_ENABLE(GEN8_FF_DOP_CLOCK_GATE_DISABLE));
+
+       /* WaDisableDopClockGating:chv (pre-production hw) */
+       I915_WRITE(GEN7_ROW_CHICKEN2,
+                  _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE));
+       I915_WRITE(GEN6_UCGCTL1, I915_READ(GEN6_UCGCTL1) |
+                  GEN6_EU_TCUNIT_CLOCK_GATE_DISABLE);
+}
+
 static void g4x_init_clock_gating(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -5212,6 +5491,9 @@ static void g4x_init_clock_gating(struct drm_device *dev)
        I915_WRITE(CACHE_MODE_0,
                   _MASKED_BIT_ENABLE(CM0_PIPELINED_RENDER_FLUSH_DISABLE));
 
+       /* WaDisable_RenderCache_OperationalFlush:g4x */
+       I915_WRITE(CACHE_MODE_0, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE));
+
        g4x_disable_trickle_feed(dev);
 }
 
@@ -5226,6 +5508,9 @@ static void crestline_init_clock_gating(struct drm_device *dev)
        I915_WRITE16(DEUC, 0);
        I915_WRITE(MI_ARB_STATE,
                   _MASKED_BIT_ENABLE(MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE));
+
+       /* WaDisable_RenderCache_OperationalFlush:gen4 */
+       I915_WRITE(CACHE_MODE_0, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE));
 }
 
 static void broadwater_init_clock_gating(struct drm_device *dev)
@@ -5240,6 +5525,9 @@ static void broadwater_init_clock_gating(struct drm_device *dev)
        I915_WRITE(RENCLK_GATE_D2, 0);
        I915_WRITE(MI_ARB_STATE,
                   _MASKED_BIT_ENABLE(MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE));
+
+       /* WaDisable_RenderCache_OperationalFlush:gen4 */
+       I915_WRITE(CACHE_MODE_0, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE));
 }
 
 static void gen3_init_clock_gating(struct drm_device *dev)
@@ -5256,6 +5544,12 @@ static void gen3_init_clock_gating(struct drm_device *dev)
 
        /* IIR "flip pending" means done if this bit is set */
        I915_WRITE(ECOSKPD, _MASKED_BIT_DISABLE(ECO_FLIP_DONE));
+
+       /* interrupts should cause a wake up from C3 */
+       I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_AGPBUSY_INT_EN));
+
+       /* On GEN3 we really need to make sure the ARB C3 LP bit is set */
+       I915_WRITE(MI_ARB_STATE, _MASKED_BIT_ENABLE(MI_ARB_C3_LP_WRITE_ENABLE));
 }
 
 static void i85x_init_clock_gating(struct drm_device *dev)
@@ -5263,6 +5557,10 @@ static void i85x_init_clock_gating(struct drm_device *dev)
        struct drm_i915_private *dev_priv = dev->dev_private;
 
        I915_WRITE(RENCLK_GATE_D1, SV_CLOCK_GATE_DISABLE);
+
+       /* interrupts should cause a wake up from C3 */
+       I915_WRITE(MI_STATE, _MASKED_BIT_ENABLE(MI_AGPBUSY_INT_EN) |
+                  _MASKED_BIT_DISABLE(MI_AGPBUSY_830_MODE));
 }
 
 static void i830_init_clock_gating(struct drm_device *dev)
@@ -5314,10 +5612,25 @@ bool intel_display_power_enabled_sw(struct drm_i915_private *dev_priv,
                                    enum intel_display_power_domain domain)
 {
        struct i915_power_domains *power_domains;
+       struct i915_power_well *power_well;
+       bool is_enabled;
+       int i;
+
+       if (dev_priv->pm.suspended)
+               return false;
 
        power_domains = &dev_priv->power_domains;
+       is_enabled = true;
+       for_each_power_well_rev(i, power_well, BIT(domain), power_domains) {
+               if (power_well->always_on)
+                       continue;
 
-       return power_domains->domain_use_count[domain];
+               if (!power_well->count) {
+                       is_enabled = false;
+                       break;
+               }
+       }
+       return is_enabled;
 }
 
 bool intel_display_power_enabled(struct drm_i915_private *dev_priv,
@@ -5392,33 +5705,6 @@ static void hsw_power_well_post_enable(struct drm_i915_private *dev_priv)
        }
 }
 
-static void reset_vblank_counter(struct drm_device *dev, enum pipe pipe)
-{
-       assert_spin_locked(&dev->vbl_lock);
-
-       dev->vblank[pipe].last = 0;
-}
-
-static void hsw_power_well_post_disable(struct drm_i915_private *dev_priv)
-{
-       struct drm_device *dev = dev_priv->dev;
-       enum pipe pipe;
-       unsigned long irqflags;
-
-       /*
-        * After this, the registers on the pipes that are part of the power
-        * well will become zero, so we have to adjust our counters according to
-        * that.
-        *
-        * FIXME: Should we do this in general in drm_vblank_post_modeset?
-        */
-       spin_lock_irqsave(&dev->vbl_lock, irqflags);
-       for_each_pipe(pipe)
-               if (pipe != PIPE_A)
-                       reset_vblank_counter(dev, pipe);
-       spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
-}
-
 static void hsw_set_power_well(struct drm_i915_private *dev_priv,
                               struct i915_power_well *power_well, bool enable)
 {
@@ -5447,8 +5733,6 @@ static void hsw_set_power_well(struct drm_i915_private *dev_priv,
                        I915_WRITE(HSW_PWR_WELL_DRIVER, 0);
                        POSTING_READ(HSW_PWR_WELL_DRIVER);
                        DRM_DEBUG_KMS("Requesting to disable the power well\n");
-
-                       hsw_power_well_post_disable(dev_priv);
                }
        }
 }
@@ -5489,13 +5773,34 @@ static bool i9xx_always_on_power_well_enabled(struct drm_i915_private *dev_priv,
        return true;
 }
 
-static void vlv_set_power_well(struct drm_i915_private *dev_priv,
-                              struct i915_power_well *power_well, bool enable)
+void __vlv_set_power_well(struct drm_i915_private *dev_priv,
+                         enum punit_power_well power_well_id, bool enable)
 {
-       enum punit_power_well power_well_id = power_well->data;
+       struct drm_device *dev = dev_priv->dev;
        u32 mask;
        u32 state;
        u32 ctrl;
+       enum pipe pipe;
+
+       if (power_well_id == PUNIT_POWER_WELL_DPIO_CMN_BC) {
+               if (enable) {
+                       /*
+                        * Enable the CRI clock source so we can get at the
+                        * display and the reference clock for VGA
+                        * hotplug / manual detection.
+                        */
+                       I915_WRITE(DPLL(PIPE_B), I915_READ(DPLL(PIPE_B)) |
+                                  DPLL_REFA_CLK_ENABLE_VLV |
+                                  DPLL_INTEGRATED_CRI_CLK_VLV);
+                       udelay(1); /* >10ns for cmnreset, >0ns for sidereset */
+               } else {
+                       for_each_pipe(pipe)
+                               assert_pll_disabled(dev_priv, pipe);
+                       /* Assert common reset */
+                       I915_WRITE(DPIO_CTL, I915_READ(DPIO_CTL) &
+                                  ~DPIO_CMNRST);
+               }
+       }
 
        mask = PUNIT_PWRGT_MASK(power_well_id);
        state = enable ? PUNIT_PWRGT_PWR_ON(power_well_id) :
@@ -5523,6 +5828,28 @@ static void vlv_set_power_well(struct drm_i915_private *dev_priv,
 
 out:
        mutex_unlock(&dev_priv->rps.hw_lock);
+
+       /*
+        * From VLV2A0_DP_eDP_DPIO_driver_vbios_notes_10.docx -
+        *  6.  De-assert cmn_reset/side_reset. Same as VLV X0.
+        *   a. GUnit 0x2110 bit[0] set to 1 (def 0)
+        *   b. The other bits such as sfr settings / modesel may all
+        *      be set to 0.
+        *
+        * This should only be done on init and resume from S3 with
+        * both PLLs disabled, or we risk losing DPIO and PLL
+        * synchronization.
+        */
+       if (power_well_id == PUNIT_POWER_WELL_DPIO_CMN_BC && enable)
+               I915_WRITE(DPIO_CTL, I915_READ(DPIO_CTL) | DPIO_CMNRST);
+}
+
+static void vlv_set_power_well(struct drm_i915_private *dev_priv,
+                              struct i915_power_well *power_well, bool enable)
+{
+       enum punit_power_well power_well_id = power_well->data;
+
+       __vlv_set_power_well(dev_priv, power_well_id, enable);
 }
 
 static void vlv_power_well_sync_hw(struct drm_i915_private *dev_priv,
@@ -5591,11 +5918,13 @@ static void vlv_display_power_well_enable(struct drm_i915_private *dev_priv,
        spin_unlock_irq(&dev_priv->irq_lock);
 
        /*
-        * During driver initialization we need to defer enabling hotplug
-        * processing until fbdev is set up.
+        * During driver initialization/resume we can avoid restoring the
+        * part of the HW/SW state that will be inited anyway explicitly.
         */
-       if (dev_priv->enable_hotplug_processing)
-               intel_hpd_init(dev_priv->dev);
+       if (dev_priv->power_domains.initializing)
+               return;
+
+       intel_hpd_init(dev_priv->dev);
 
        i915_redisable_vga_power_on(dev_priv->dev);
 }
@@ -5603,23 +5932,12 @@ static void vlv_display_power_well_enable(struct drm_i915_private *dev_priv,
 static void vlv_display_power_well_disable(struct drm_i915_private *dev_priv,
                                           struct i915_power_well *power_well)
 {
-       struct drm_device *dev = dev_priv->dev;
-       enum pipe pipe;
-
        WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DISP2D);
 
        spin_lock_irq(&dev_priv->irq_lock);
-       for_each_pipe(pipe)
-               __intel_set_cpu_fifo_underrun_reporting(dev, pipe, false);
-
        valleyview_disable_display_irqs(dev_priv);
        spin_unlock_irq(&dev_priv->irq_lock);
 
-       spin_lock_irq(&dev->vbl_lock);
-       for_each_pipe(pipe)
-               reset_vblank_counter(dev, pipe);
-       spin_unlock_irq(&dev->vbl_lock);
-
        vlv_set_power_well(dev_priv, power_well, false);
 }
 
@@ -5866,12 +6184,6 @@ static struct i915_power_well vlv_power_wells[] = {
                .data = PUNIT_POWER_WELL_DISP2D,
                .ops = &vlv_display_power_well_ops,
        },
-       {
-               .name = "dpio-common",
-               .domains = VLV_DPIO_CMN_BC_POWER_DOMAINS,
-               .data = PUNIT_POWER_WELL_DPIO_CMN_BC,
-               .ops = &vlv_dpio_power_well_ops,
-       },
        {
                .name = "dpio-tx-b-01",
                .domains = VLV_DPIO_TX_B_LANES_01_POWER_DOMAINS |
@@ -5908,6 +6220,12 @@ static struct i915_power_well vlv_power_wells[] = {
                .ops = &vlv_dpio_power_well_ops,
                .data = PUNIT_POWER_WELL_DPIO_TX_C_LANES_23,
        },
+       {
+               .name = "dpio-common",
+               .domains = VLV_DPIO_CMN_BC_POWER_DOMAINS,
+               .data = PUNIT_POWER_WELL_DPIO_CMN_BC,
+               .ops = &vlv_dpio_power_well_ops,
+       },
 };
 
 #define set_power_wells(power_domains, __power_wells) ({               \
@@ -5959,9 +6277,13 @@ static void intel_power_domains_resume(struct drm_i915_private *dev_priv)
 
 void intel_power_domains_init_hw(struct drm_i915_private *dev_priv)
 {
+       struct i915_power_domains *power_domains = &dev_priv->power_domains;
+
+       power_domains->initializing = true;
        /* For now, we need the power well to be always enabled. */
        intel_display_set_init_power(dev_priv, true);
        intel_power_domains_resume(dev_priv);
+       power_domains->initializing = false;
 }
 
 void intel_aux_display_runtime_get(struct drm_i915_private *dev_priv)
@@ -5986,6 +6308,18 @@ void intel_runtime_pm_get(struct drm_i915_private *dev_priv)
        WARN(dev_priv->pm.suspended, "Device still suspended.\n");
 }
 
+void intel_runtime_pm_get_noresume(struct drm_i915_private *dev_priv)
+{
+       struct drm_device *dev = dev_priv->dev;
+       struct device *device = &dev->pdev->dev;
+
+       if (!HAS_RUNTIME_PM(dev))
+               return;
+
+       WARN(dev_priv->pm.suspended, "Getting nosync-ref while suspended.\n");
+       pm_runtime_get_noresume(device);
+}
+
 void intel_runtime_pm_put(struct drm_i915_private *dev_priv)
 {
        struct drm_device *dev = dev_priv->dev;
@@ -6008,6 +6342,15 @@ void intel_init_runtime_pm(struct drm_i915_private *dev_priv)
 
        pm_runtime_set_active(device);
 
+       /*
+        * RPM depends on RC6 to save restore the GT HW context, so make RC6 a
+        * requirement.
+        */
+       if (!intel_enable_rc6(dev)) {
+               DRM_INFO("RC6 disabled, disabling runtime PM support\n");
+               return;
+       }
+
        pm_runtime_set_autosuspend_delay(device, 10000); /* 10s */
        pm_runtime_mark_last_busy(device);
        pm_runtime_use_autosuspend(device);
@@ -6023,6 +6366,9 @@ void intel_fini_runtime_pm(struct drm_i915_private *dev_priv)
        if (!HAS_RUNTIME_PM(dev))
                return;
 
+       if (!intel_enable_rc6(dev))
+               return;
+
        /* Make sure we're not suspended first. */
        pm_runtime_get_sync(device);
        pm_runtime_disable(device);
@@ -6087,6 +6433,10 @@ void intel_init_pm(struct drm_device *dev)
                        dev_priv->display.init_clock_gating = haswell_init_clock_gating;
                else if (INTEL_INFO(dev)->gen == 8)
                        dev_priv->display.init_clock_gating = gen8_init_clock_gating;
+       } else if (IS_CHERRYVIEW(dev)) {
+               dev_priv->display.update_wm = valleyview_update_wm;
+               dev_priv->display.init_clock_gating =
+                       cherryview_init_clock_gating;
        } else if (IS_VALLEYVIEW(dev)) {
                dev_priv->display.update_wm = valleyview_update_wm;
                dev_priv->display.init_clock_gating =
diff --git a/drivers/gpu/drm/i915/intel_renderstate.h b/drivers/gpu/drm/i915/intel_renderstate.h
new file mode 100644 (file)
index 0000000..a5e783a
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright © 2014 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _INTEL_RENDERSTATE_H
+#define _INTEL_RENDERSTATE_H
+
+#include <linux/types.h>
+
+struct intel_renderstate_rodata {
+       const u32 *reloc;
+       const u32 reloc_items;
+       const u32 *batch;
+       const u32 batch_items;
+};
+
+extern const struct intel_renderstate_rodata gen6_null_state;
+extern const struct intel_renderstate_rodata gen7_null_state;
+extern const struct intel_renderstate_rodata gen8_null_state;
+
+#define RO_RENDERSTATE(_g)                                             \
+       const struct intel_renderstate_rodata gen ## _g ## _null_state = { \
+               .reloc = gen ## _g ## _null_state_relocs,               \
+               .reloc_items = sizeof(gen ## _g ## _null_state_relocs)/4, \
+               .batch = gen ## _g ## _null_state_batch,                \
+               .batch_items = sizeof(gen ## _g ## _null_state_batch)/4, \
+       }
+
+#endif /* INTEL_RENDERSTATE_H */
diff --git a/drivers/gpu/drm/i915/intel_renderstate_gen6.c b/drivers/gpu/drm/i915/intel_renderstate_gen6.c
new file mode 100644 (file)
index 0000000..740538a
--- /dev/null
@@ -0,0 +1,289 @@
+#include "intel_renderstate.h"
+
+static const u32 gen6_null_state_relocs[] = {
+       0x00000020,
+       0x00000024,
+       0x0000002c,
+       0x000001e0,
+       0x000001e4,
+};
+
+static const u32 gen6_null_state_batch[] = {
+       0x69040000,
+       0x790d0001,
+       0x00000000,
+       0x00000000,
+       0x78180000,
+       0x00000001,
+       0x61010008,
+       0x00000000,
+       0x00000001,      /* reloc */
+       0x00000001,      /* reloc */
+       0x00000000,
+       0x00000001,      /* reloc */
+       0x00000000,
+       0x00000001,
+       0x00000000,
+       0x00000001,
+       0x61020000,
+       0x00000000,
+       0x78050001,
+       0x00000018,
+       0x00000000,
+       0x780d1002,
+       0x00000000,
+       0x00000000,
+       0x00000420,
+       0x78150003,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78100004,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78160003,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78110005,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78120002,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78170003,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x79050005,
+       0xe0040000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x79100000,
+       0x00000000,
+       0x79000002,
+       0xffffffff,
+       0x00000000,
+       0x00000000,
+       0x780e0002,
+       0x00000441,
+       0x00000401,
+       0x00000401,
+       0x78021002,
+       0x00000000,
+       0x00000000,
+       0x00000400,
+       0x78130012,
+       0x00400810,
+       0x00000000,
+       0x20000000,
+       0x04000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78140007,
+       0x00000280,
+       0x08080000,
+       0x00000000,
+       0x00060000,
+       0x4e080002,
+       0x00100400,
+       0x00000000,
+       0x00000000,
+       0x78090005,
+       0x02000000,
+       0x22220000,
+       0x02f60000,
+       0x11330000,
+       0x02850004,
+       0x11220000,
+       0x78011002,
+       0x00000000,
+       0x00000000,
+       0x00000200,
+       0x78080003,
+       0x00002000,
+       0x00000448,      /* reloc */
+       0x00000448,      /* reloc */
+       0x00000000,
+       0x05000000,      /* cmds end */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000220,      /* state start */
+       0x00000240,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x0060005a,
+       0x204077be,
+       0x000000c0,
+       0x008d0040,
+       0x0060005a,
+       0x206077be,
+       0x000000c0,
+       0x008d0080,
+       0x0060005a,
+       0x208077be,
+       0x000000d0,
+       0x008d0040,
+       0x0060005a,
+       0x20a077be,
+       0x000000d0,
+       0x008d0080,
+       0x00000201,
+       0x20080061,
+       0x00000000,
+       0x00000000,
+       0x00600001,
+       0x20200022,
+       0x008d0000,
+       0x00000000,
+       0x02800031,
+       0x21c01cc9,
+       0x00000020,
+       0x0a8a0001,
+       0x00600001,
+       0x204003be,
+       0x008d01c0,
+       0x00000000,
+       0x00600001,
+       0x206003be,
+       0x008d01e0,
+       0x00000000,
+       0x00600001,
+       0x208003be,
+       0x008d0200,
+       0x00000000,
+       0x00600001,
+       0x20a003be,
+       0x008d0220,
+       0x00000000,
+       0x00600001,
+       0x20c003be,
+       0x008d0240,
+       0x00000000,
+       0x00600001,
+       0x20e003be,
+       0x008d0260,
+       0x00000000,
+       0x00600001,
+       0x210003be,
+       0x008d0280,
+       0x00000000,
+       0x00600001,
+       0x212003be,
+       0x008d02a0,
+       0x00000000,
+       0x05800031,
+       0x24001cc8,
+       0x00000040,
+       0x90019000,
+       0x0000007e,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x0000007e,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x0000007e,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x0000007e,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x0000007e,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x0000007e,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x0000007e,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x0000007e,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x30000000,
+       0x00000124,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0xf99a130c,
+       0x799a130c,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x80000031,
+       0x00000003,
+       0x00000000,      /* state end */
+};
+
+RO_RENDERSTATE(6);
diff --git a/drivers/gpu/drm/i915/intel_renderstate_gen7.c b/drivers/gpu/drm/i915/intel_renderstate_gen7.c
new file mode 100644 (file)
index 0000000..6fa7ff2
--- /dev/null
@@ -0,0 +1,253 @@
+#include "intel_renderstate.h"
+
+static const u32 gen7_null_state_relocs[] = {
+       0x0000000c,
+       0x00000010,
+       0x00000018,
+       0x000001ec,
+};
+
+static const u32 gen7_null_state_batch[] = {
+       0x69040000,
+       0x61010008,
+       0x00000000,
+       0x00000001,      /* reloc */
+       0x00000001,      /* reloc */
+       0x00000000,
+       0x00000001,      /* reloc */
+       0x00000000,
+       0x00000001,
+       0x00000000,
+       0x00000001,
+       0x790d0002,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78180000,
+       0x00000001,
+       0x79160000,
+       0x00000008,
+       0x78300000,
+       0x02010040,
+       0x78310000,
+       0x04000000,
+       0x78320000,
+       0x04000000,
+       0x78330000,
+       0x02000000,
+       0x78100004,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x781b0005,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x781c0002,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x781d0004,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78110005,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78120002,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78210000,
+       0x00000000,
+       0x78130005,
+       0x00000000,
+       0x20000000,
+       0x04000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78140001,
+       0x20000800,
+       0x00000000,
+       0x781e0001,
+       0x00000000,
+       0x00000000,
+       0x78050005,
+       0xe0040000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78040001,
+       0x00000000,
+       0x00000000,
+       0x78240000,
+       0x00000240,
+       0x78230000,
+       0x00000260,
+       0x782f0000,
+       0x00000280,
+       0x781f000c,
+       0x00400810,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78200006,
+       0x000002c0,
+       0x08080000,
+       0x00000000,
+       0x28000402,
+       0x00060000,
+       0x00000000,
+       0x00000000,
+       0x78090005,
+       0x02000000,
+       0x22220000,
+       0x02f60000,
+       0x11230000,
+       0x02f60004,
+       0x11230000,
+       0x78080003,
+       0x00006008,
+       0x00000340,      /* reloc */
+       0xffffffff,
+       0x00000000,
+       0x782a0000,
+       0x00000360,
+       0x79000002,
+       0xffffffff,
+       0x00000000,
+       0x00000000,
+       0x7b000005,
+       0x0000000f,
+       0x00000003,
+       0x00000000,
+       0x00000001,
+       0x00000000,
+       0x00000000,
+       0x05000000,      /* cmds end */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000031,      /* state start */
+       0x00000003,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0xf99a130c,
+       0x799a130c,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000492,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x0080005a,
+       0x2e2077bd,
+       0x000000c0,
+       0x008d0040,
+       0x0080005a,
+       0x2e6077bd,
+       0x000000d0,
+       0x008d0040,
+       0x02800031,
+       0x21801fa9,
+       0x008d0e20,
+       0x08840001,
+       0x00800001,
+       0x2e2003bd,
+       0x008d0180,
+       0x00000000,
+       0x00800001,
+       0x2e6003bd,
+       0x008d01c0,
+       0x00000000,
+       0x00800001,
+       0x2ea003bd,
+       0x008d0200,
+       0x00000000,
+       0x00800001,
+       0x2ee003bd,
+       0x008d0240,
+       0x00000000,
+       0x05800031,
+       0x20001fa8,
+       0x008d0e20,
+       0x90031000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000380,
+       0x000003a0,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,      /* state end */
+};
+
+RO_RENDERSTATE(7);
diff --git a/drivers/gpu/drm/i915/intel_renderstate_gen8.c b/drivers/gpu/drm/i915/intel_renderstate_gen8.c
new file mode 100644 (file)
index 0000000..5c87561
--- /dev/null
@@ -0,0 +1,479 @@
+#include "intel_renderstate.h"
+
+static const u32 gen8_null_state_relocs[] = {
+       0x00000048,
+       0x00000050,
+       0x00000060,
+       0x000003ec,
+};
+
+static const u32 gen8_null_state_batch[] = {
+       0x69040000,
+       0x61020001,
+       0x00000000,
+       0x00000000,
+       0x79120000,
+       0x00000000,
+       0x79130000,
+       0x00000000,
+       0x79140000,
+       0x00000000,
+       0x79150000,
+       0x00000000,
+       0x79160000,
+       0x00000000,
+       0x6101000e,
+       0x00000001,
+       0x00000000,
+       0x00000001,
+       0x00000001,      /* reloc */
+       0x00000000,
+       0x00000001,      /* reloc */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000001,      /* reloc */
+       0x00000000,
+       0xfffff001,
+       0x00001001,
+       0xfffff001,
+       0x00001001,
+       0x78230000,
+       0x000006e0,
+       0x78210000,
+       0x00000700,
+       0x78300000,
+       0x08010040,
+       0x78330000,
+       0x08000000,
+       0x78310000,
+       0x08000000,
+       0x78320000,
+       0x08000000,
+       0x78240000,
+       0x00000641,
+       0x780e0000,
+       0x00000601,
+       0x780d0000,
+       0x00000000,
+       0x78180000,
+       0x00000001,
+       0x78520003,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78190009,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x781b0007,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78270000,
+       0x00000000,
+       0x782c0000,
+       0x00000000,
+       0x781c0002,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78160009,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78110008,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78290000,
+       0x00000000,
+       0x782e0000,
+       0x00000000,
+       0x781a0009,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x781d0007,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78280000,
+       0x00000000,
+       0x782d0000,
+       0x00000000,
+       0x78260000,
+       0x00000000,
+       0x782b0000,
+       0x00000000,
+       0x78150009,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78100007,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x781e0003,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78120002,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x781f0002,
+       0x30400820,
+       0x00000000,
+       0x00000000,
+       0x78510009,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78500003,
+       0x00210000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78130002,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x782a0000,
+       0x00000480,
+       0x782f0000,
+       0x00000540,
+       0x78140000,
+       0x00000800,
+       0x78170009,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x7820000a,
+       0x00000580,
+       0x00000000,
+       0x08080000,
+       0x00000000,
+       0x00000000,
+       0x1f000002,
+       0x00060000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x784d0000,
+       0x40000000,
+       0x784f0000,
+       0x80000100,
+       0x780f0000,
+       0x00000740,
+       0x78050006,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78070003,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78060003,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x78040001,
+       0x00000000,
+       0x00000001,
+       0x79000002,
+       0xffffffff,
+       0x00000000,
+       0x00000000,
+       0x78080003,
+       0x00006000,
+       0x000005e0,      /* reloc */
+       0x00000000,
+       0x00000000,
+       0x78090005,
+       0x02000000,
+       0x22220000,
+       0x02f60000,
+       0x11230000,
+       0x02850004,
+       0x11230000,
+       0x784b0000,
+       0x0000000f,
+       0x78490001,
+       0x00000000,
+       0x00000000,
+       0x7b000005,
+       0x00000000,
+       0x00000003,
+       0x00000000,
+       0x00000001,
+       0x00000000,
+       0x00000000,
+       0x05000000,      /* cmds end */
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x000004c0,      /* state start */
+       0x00000500,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000092,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x0060005a,
+       0x21403ae8,
+       0x3a0000c0,
+       0x008d0040,
+       0x0060005a,
+       0x21603ae8,
+       0x3a0000c0,
+       0x008d0080,
+       0x0060005a,
+       0x21803ae8,
+       0x3a0000d0,
+       0x008d0040,
+       0x0060005a,
+       0x21a03ae8,
+       0x3a0000d0,
+       0x008d0080,
+       0x02800031,
+       0x2e0022e8,
+       0x0e000140,
+       0x08840001,
+       0x05800031,
+       0x200022e0,
+       0x0e000e00,
+       0x90031000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x06200000,
+       0x00000002,
+       0x06200000,
+       0x00000002,
+       0x06200000,
+       0x00000002,
+       0x06200000,
+       0x00000002,
+       0x06200000,
+       0x00000002,
+       0x06200000,
+       0x00000002,
+       0x06200000,
+       0x00000002,
+       0x06200000,
+       0x00000002,
+       0x06200000,
+       0x00000002,
+       0x06200000,
+       0x00000002,
+       0x06200000,
+       0x00000002,
+       0x06200000,
+       0x00000002,
+       0x06200000,
+       0x00000002,
+       0x06200000,
+       0x00000002,
+       0x06200000,
+       0x00000002,
+       0x06200000,
+       0x00000002,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0xf99a130c,
+       0x799a130c,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x3f800000,
+       0x00000000,
+       0x3f800000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,
+       0x00000000,      /* state end */
+};
+
+RO_RENDERSTATE(8);
index 79fb4cc2137c19d2cf4ad5dc23e6c5883eb58e4d..279488addf3f6bd7afb194bf29479a9228390460 100644 (file)
 #include "i915_trace.h"
 #include "intel_drv.h"
 
-static inline int ring_space(struct intel_ring_buffer *ring)
+/* Early gen2 devices have a cacheline of just 32 bytes, using 64 is overkill,
+ * but keeps the logic simple. Indeed, the whole purpose of this macro is just
+ * to give some inclination as to some of the magic values used in the various
+ * workarounds!
+ */
+#define CACHELINE_BYTES 64
+
+static inline int __ring_space(int head, int tail, int size)
 {
-       int space = (ring->head & HEAD_ADDR) - (ring->tail + I915_RING_FREE_SPACE);
+       int space = head - (tail + I915_RING_FREE_SPACE);
        if (space < 0)
-               space += ring->size;
+               space += size;
        return space;
 }
 
-void __intel_ring_advance(struct intel_ring_buffer *ring)
+static inline int ring_space(struct intel_engine_cs *ring)
+{
+       struct intel_ringbuffer *ringbuf = ring->buffer;
+       return __ring_space(ringbuf->head & HEAD_ADDR, ringbuf->tail, ringbuf->size);
+}
+
+static bool intel_ring_stopped(struct intel_engine_cs *ring)
 {
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
+       return dev_priv->gpu_error.stop_rings & intel_ring_flag(ring);
+}
 
-       ring->tail &= ring->size - 1;
-       if (dev_priv->gpu_error.stop_rings & intel_ring_flag(ring))
+void __intel_ring_advance(struct intel_engine_cs *ring)
+{
+       struct intel_ringbuffer *ringbuf = ring->buffer;
+       ringbuf->tail &= ringbuf->size - 1;
+       if (intel_ring_stopped(ring))
                return;
-       ring->write_tail(ring, ring->tail);
+       ring->write_tail(ring, ringbuf->tail);
 }
 
 static int
-gen2_render_ring_flush(struct intel_ring_buffer *ring,
+gen2_render_ring_flush(struct intel_engine_cs *ring,
                       u32      invalidate_domains,
                       u32      flush_domains)
 {
@@ -78,7 +96,7 @@ gen2_render_ring_flush(struct intel_ring_buffer *ring,
 }
 
 static int
-gen4_render_ring_flush(struct intel_ring_buffer *ring,
+gen4_render_ring_flush(struct intel_engine_cs *ring,
                       u32      invalidate_domains,
                       u32      flush_domains)
 {
@@ -173,9 +191,9 @@ gen4_render_ring_flush(struct intel_ring_buffer *ring,
  * really our business.  That leaves only stall at scoreboard.
  */
 static int
-intel_emit_post_sync_nonzero_flush(struct intel_ring_buffer *ring)
+intel_emit_post_sync_nonzero_flush(struct intel_engine_cs *ring)
 {
-       u32 scratch_addr = ring->scratch.gtt_offset + 128;
+       u32 scratch_addr = ring->scratch.gtt_offset + 2 * CACHELINE_BYTES;
        int ret;
 
 
@@ -208,11 +226,11 @@ intel_emit_post_sync_nonzero_flush(struct intel_ring_buffer *ring)
 }
 
 static int
-gen6_render_ring_flush(struct intel_ring_buffer *ring,
+gen6_render_ring_flush(struct intel_engine_cs *ring,
                          u32 invalidate_domains, u32 flush_domains)
 {
        u32 flags = 0;
-       u32 scratch_addr = ring->scratch.gtt_offset + 128;
+       u32 scratch_addr = ring->scratch.gtt_offset + 2 * CACHELINE_BYTES;
        int ret;
 
        /* Force SNB workarounds for PIPE_CONTROL flushes */
@@ -260,7 +278,7 @@ gen6_render_ring_flush(struct intel_ring_buffer *ring,
 }
 
 static int
-gen7_render_ring_cs_stall_wa(struct intel_ring_buffer *ring)
+gen7_render_ring_cs_stall_wa(struct intel_engine_cs *ring)
 {
        int ret;
 
@@ -278,7 +296,7 @@ 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)
+static int gen7_ring_fbc_flush(struct intel_engine_cs *ring, u32 value)
 {
        int ret;
 
@@ -302,11 +320,11 @@ static int gen7_ring_fbc_flush(struct intel_ring_buffer *ring, u32 value)
 }
 
 static int
-gen7_render_ring_flush(struct intel_ring_buffer *ring,
+gen7_render_ring_flush(struct intel_engine_cs *ring,
                       u32 invalidate_domains, u32 flush_domains)
 {
        u32 flags = 0;
-       u32 scratch_addr = ring->scratch.gtt_offset + 128;
+       u32 scratch_addr = ring->scratch.gtt_offset + 2 * CACHELINE_BYTES;
        int ret;
 
        /*
@@ -363,11 +381,11 @@ gen7_render_ring_flush(struct intel_ring_buffer *ring,
 }
 
 static int
-gen8_render_ring_flush(struct intel_ring_buffer *ring,
+gen8_render_ring_flush(struct intel_engine_cs *ring,
                       u32 invalidate_domains, u32 flush_domains)
 {
        u32 flags = 0;
-       u32 scratch_addr = ring->scratch.gtt_offset + 128;
+       u32 scratch_addr = ring->scratch.gtt_offset + 2 * CACHELINE_BYTES;
        int ret;
 
        flags |= PIPE_CONTROL_CS_STALL;
@@ -403,14 +421,14 @@ gen8_render_ring_flush(struct intel_ring_buffer *ring,
 
 }
 
-static void ring_write_tail(struct intel_ring_buffer *ring,
+static void ring_write_tail(struct intel_engine_cs *ring,
                            u32 value)
 {
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
        I915_WRITE_TAIL(ring, value);
 }
 
-u64 intel_ring_get_active_head(struct intel_ring_buffer *ring)
+u64 intel_ring_get_active_head(struct intel_engine_cs *ring)
 {
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
        u64 acthd;
@@ -426,7 +444,7 @@ u64 intel_ring_get_active_head(struct intel_ring_buffer *ring)
        return acthd;
 }
 
-static void ring_setup_phys_status_page(struct intel_ring_buffer *ring)
+static void ring_setup_phys_status_page(struct intel_engine_cs *ring)
 {
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
        u32 addr;
@@ -437,7 +455,7 @@ static void ring_setup_phys_status_page(struct intel_ring_buffer *ring)
        I915_WRITE(HWS_PGA, addr);
 }
 
-static bool stop_ring(struct intel_ring_buffer *ring)
+static bool stop_ring(struct intel_engine_cs *ring)
 {
        struct drm_i915_private *dev_priv = to_i915(ring->dev);
 
@@ -461,11 +479,12 @@ static bool stop_ring(struct intel_ring_buffer *ring)
        return (I915_READ_HEAD(ring) & HEAD_ADDR) == 0;
 }
 
-static int init_ring_common(struct intel_ring_buffer *ring)
+static int init_ring_common(struct intel_engine_cs *ring)
 {
        struct drm_device *dev = ring->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct drm_i915_gem_object *obj = ring->obj;
+       struct intel_ringbuffer *ringbuf = ring->buffer;
+       struct drm_i915_gem_object *obj = ringbuf->obj;
        int ret = 0;
 
        gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL);
@@ -504,7 +523,7 @@ static int init_ring_common(struct intel_ring_buffer *ring)
         * register values. */
        I915_WRITE_START(ring, i915_gem_obj_ggtt_offset(obj));
        I915_WRITE_CTL(ring,
-                       ((ring->size - PAGE_SIZE) & RING_NR_PAGES)
+                       ((ringbuf->size - PAGE_SIZE) & RING_NR_PAGES)
                        | RING_VALID);
 
        /* If the head is still not zero, the ring is dead */
@@ -512,12 +531,11 @@ static int init_ring_common(struct intel_ring_buffer *ring)
                     I915_READ_START(ring) == i915_gem_obj_ggtt_offset(obj) &&
                     (I915_READ_HEAD(ring) & HEAD_ADDR) == 0, 50)) {
                DRM_ERROR("%s initialization failed "
-                               "ctl %08x head %08x tail %08x start %08x\n",
-                               ring->name,
-                               I915_READ_CTL(ring),
-                               I915_READ_HEAD(ring),
-                               I915_READ_TAIL(ring),
-                               I915_READ_START(ring));
+                         "ctl %08x (valid? %d) head %08x tail %08x start %08x [expected %08lx]\n",
+                         ring->name,
+                         I915_READ_CTL(ring), I915_READ_CTL(ring) & RING_VALID,
+                         I915_READ_HEAD(ring), I915_READ_TAIL(ring),
+                         I915_READ_START(ring), (unsigned long)i915_gem_obj_ggtt_offset(obj));
                ret = -EIO;
                goto out;
        }
@@ -525,10 +543,10 @@ static int init_ring_common(struct intel_ring_buffer *ring)
        if (!drm_core_check_feature(ring->dev, DRIVER_MODESET))
                i915_kernel_lost_context(ring->dev);
        else {
-               ring->head = I915_READ_HEAD(ring);
-               ring->tail = I915_READ_TAIL(ring) & TAIL_ADDR;
-               ring->space = ring_space(ring);
-               ring->last_retired_head = -1;
+               ringbuf->head = I915_READ_HEAD(ring);
+               ringbuf->tail = I915_READ_TAIL(ring) & TAIL_ADDR;
+               ringbuf->space = ring_space(ring);
+               ringbuf->last_retired_head = -1;
        }
 
        memset(&ring->hangcheck, 0, sizeof(ring->hangcheck));
@@ -540,7 +558,7 @@ out:
 }
 
 static int
-init_pipe_control(struct intel_ring_buffer *ring)
+init_pipe_control(struct intel_engine_cs *ring)
 {
        int ret;
 
@@ -581,7 +599,7 @@ err:
        return ret;
 }
 
-static int init_render_ring(struct intel_ring_buffer *ring)
+static int init_render_ring(struct intel_engine_cs *ring)
 {
        struct drm_device *dev = ring->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -595,19 +613,21 @@ static int init_render_ring(struct intel_ring_buffer *ring)
         * to use MI_WAIT_FOR_EVENT within the CS. It should already be
         * programmed to '1' on all products.
         *
-        * WaDisableAsyncFlipPerfMode:snb,ivb,hsw,vlv,bdw
+        * WaDisableAsyncFlipPerfMode:snb,ivb,hsw,vlv,bdw,chv
         */
        if (INTEL_INFO(dev)->gen >= 6)
                I915_WRITE(MI_MODE, _MASKED_BIT_ENABLE(ASYNC_FLIP_PERF_DISABLE));
 
        /* Required for the hardware to program scanline values for waiting */
+       /* WaEnableFlushTlbInvalidationMode:snb */
        if (INTEL_INFO(dev)->gen == 6)
                I915_WRITE(GFX_MODE,
-                          _MASKED_BIT_ENABLE(GFX_TLB_INVALIDATE_ALWAYS));
+                          _MASKED_BIT_ENABLE(GFX_TLB_INVALIDATE_EXPLICIT));
 
+       /* WaBCSVCSTlbInvalidationMode:ivb,vlv,hsw */
        if (IS_GEN7(dev))
                I915_WRITE(GFX_MODE_GEN7,
-                          _MASKED_BIT_DISABLE(GFX_TLB_INVALIDATE_ALWAYS) |
+                          _MASKED_BIT_ENABLE(GFX_TLB_INVALIDATE_EXPLICIT) |
                           _MASKED_BIT_ENABLE(GFX_REPLAY_MODE));
 
        if (INTEL_INFO(dev)->gen >= 5) {
@@ -624,13 +644,6 @@ static int init_render_ring(struct intel_ring_buffer *ring)
                 */
                I915_WRITE(CACHE_MODE_0,
                           _MASKED_BIT_DISABLE(CM0_STC_EVICT_DISABLE_LRA_SNB));
-
-               /* This is not explicitly set for GEN6, so read the register.
-                * see intel_ring_mi_set_context() for why we care.
-                * TODO: consider explicitly setting the bit for GEN5
-                */
-               ring->itlb_before_ctx_switch =
-                       !!(I915_READ(GFX_MODE) & GFX_TLB_INVALIDATE_ALWAYS);
        }
 
        if (INTEL_INFO(dev)->gen >= 6)
@@ -642,7 +655,7 @@ static int init_render_ring(struct intel_ring_buffer *ring)
        return ret;
 }
 
-static void render_ring_cleanup(struct intel_ring_buffer *ring)
+static void render_ring_cleanup(struct intel_engine_cs *ring)
 {
        struct drm_device *dev = ring->dev;
 
@@ -658,20 +671,46 @@ static void render_ring_cleanup(struct intel_ring_buffer *ring)
        ring->scratch.obj = NULL;
 }
 
-static void
-update_mboxes(struct intel_ring_buffer *ring,
-             u32 mmio_offset)
+static int gen6_signal(struct intel_engine_cs *signaller,
+                      unsigned int num_dwords)
 {
-/* 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.
- */
+       struct drm_device *dev = signaller->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_engine_cs *useless;
+       int i, ret;
+
+       /* 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_seqno);
-       intel_ring_emit(ring, MI_NOOP);
+       if (i915_semaphore_is_enabled(dev))
+               num_dwords += ((I915_NUM_RINGS-1) * MBOX_UPDATE_DWORDS);
+       else
+               return intel_ring_begin(signaller, num_dwords);
+
+       ret = intel_ring_begin(signaller, num_dwords);
+       if (ret)
+               return ret;
+#undef MBOX_UPDATE_DWORDS
+
+       for_each_ring(useless, dev_priv, i) {
+               u32 mbox_reg = signaller->semaphore.mbox.signal[i];
+               if (mbox_reg != GEN6_NOSYNC) {
+                       intel_ring_emit(signaller, MI_LOAD_REGISTER_IMM(1));
+                       intel_ring_emit(signaller, mbox_reg);
+                       intel_ring_emit(signaller, signaller->outstanding_lazy_seqno);
+                       intel_ring_emit(signaller, MI_NOOP);
+               } else {
+                       intel_ring_emit(signaller, MI_NOOP);
+                       intel_ring_emit(signaller, MI_NOOP);
+                       intel_ring_emit(signaller, MI_NOOP);
+                       intel_ring_emit(signaller, MI_NOOP);
+               }
+       }
+
+       return 0;
 }
 
 /**
@@ -684,29 +723,14 @@ update_mboxes(struct intel_ring_buffer *ring,
  * This acts like a signal in the canonical semaphore.
  */
 static int
-gen6_add_request(struct intel_ring_buffer *ring)
+gen6_add_request(struct intel_engine_cs *ring)
 {
-       struct drm_device *dev = ring->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *useless;
-       int i, ret, num_dwords = 4;
-
-       if (i915_semaphore_is_enabled(dev))
-               num_dwords += ((I915_NUM_RINGS-1) * MBOX_UPDATE_DWORDS);
-#undef MBOX_UPDATE_DWORDS
+       int ret;
 
-       ret = intel_ring_begin(ring, num_dwords);
+       ret = ring->semaphore.signal(ring, 4);
        if (ret)
                return ret;
 
-       if (i915_semaphore_is_enabled(dev)) {
-               for_each_ring(useless, dev_priv, i) {
-                       u32 mbox_reg = ring->signal_mbox[i];
-                       if (mbox_reg != GEN6_NOSYNC)
-                               update_mboxes(ring, mbox_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_seqno);
@@ -731,14 +755,15 @@ static inline bool i915_gem_has_seqno_wrapped(struct drm_device *dev,
  * @seqno - seqno which the waiter will block on
  */
 static int
-gen6_ring_sync(struct intel_ring_buffer *waiter,
-              struct intel_ring_buffer *signaller,
+gen6_ring_sync(struct intel_engine_cs *waiter,
+              struct intel_engine_cs *signaller,
               u32 seqno)
 {
-       int ret;
        u32 dw1 = MI_SEMAPHORE_MBOX |
                  MI_SEMAPHORE_COMPARE |
                  MI_SEMAPHORE_REGISTER;
+       u32 wait_mbox = signaller->semaphore.mbox.wait[waiter->id];
+       int ret;
 
        /* Throughout all of the GEM code, seqno passed implies our current
         * seqno is >= the last seqno executed. However for hardware the
@@ -746,8 +771,7 @@ gen6_ring_sync(struct intel_ring_buffer *waiter,
         */
        seqno -= 1;
 
-       WARN_ON(signaller->semaphore_register[waiter->id] ==
-               MI_SEMAPHORE_SYNC_INVALID);
+       WARN_ON(wait_mbox == MI_SEMAPHORE_SYNC_INVALID);
 
        ret = intel_ring_begin(waiter, 4);
        if (ret)
@@ -755,9 +779,7 @@ gen6_ring_sync(struct intel_ring_buffer *waiter,
 
        /* If seqno wrap happened, omit the wait with no-ops */
        if (likely(!i915_gem_has_seqno_wrapped(waiter->dev, seqno))) {
-               intel_ring_emit(waiter,
-                               dw1 |
-                               signaller->semaphore_register[waiter->id]);
+               intel_ring_emit(waiter, dw1 | wait_mbox);
                intel_ring_emit(waiter, seqno);
                intel_ring_emit(waiter, 0);
                intel_ring_emit(waiter, MI_NOOP);
@@ -782,9 +804,9 @@ do {                                                                        \
 } while (0)
 
 static int
-pc_render_add_request(struct intel_ring_buffer *ring)
+pc_render_add_request(struct intel_engine_cs *ring)
 {
-       u32 scratch_addr = ring->scratch.gtt_offset + 128;
+       u32 scratch_addr = ring->scratch.gtt_offset + 2 * CACHELINE_BYTES;
        int ret;
 
        /* For Ironlake, MI_USER_INTERRUPT was deprecated and apparently
@@ -806,15 +828,15 @@ pc_render_add_request(struct intel_ring_buffer *ring)
        intel_ring_emit(ring, ring->outstanding_lazy_seqno);
        intel_ring_emit(ring, 0);
        PIPE_CONTROL_FLUSH(ring, scratch_addr);
-       scratch_addr += 128; /* write to separate cachelines */
+       scratch_addr += 2 * CACHELINE_BYTES; /* write to separate cachelines */
        PIPE_CONTROL_FLUSH(ring, scratch_addr);
-       scratch_addr += 128;
+       scratch_addr += 2 * CACHELINE_BYTES;
        PIPE_CONTROL_FLUSH(ring, scratch_addr);
-       scratch_addr += 128;
+       scratch_addr += 2 * CACHELINE_BYTES;
        PIPE_CONTROL_FLUSH(ring, scratch_addr);
-       scratch_addr += 128;
+       scratch_addr += 2 * CACHELINE_BYTES;
        PIPE_CONTROL_FLUSH(ring, scratch_addr);
-       scratch_addr += 128;
+       scratch_addr += 2 * CACHELINE_BYTES;
        PIPE_CONTROL_FLUSH(ring, scratch_addr);
 
        intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE |
@@ -830,7 +852,7 @@ pc_render_add_request(struct intel_ring_buffer *ring)
 }
 
 static u32
-gen6_ring_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency)
+gen6_ring_get_seqno(struct intel_engine_cs *ring, bool lazy_coherency)
 {
        /* Workaround to force correct ordering between irq and seqno writes on
         * ivb (and maybe also on snb) by reading from a CS register (like
@@ -844,31 +866,31 @@ gen6_ring_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency)
 }
 
 static u32
-ring_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency)
+ring_get_seqno(struct intel_engine_cs *ring, bool lazy_coherency)
 {
        return intel_read_status_page(ring, I915_GEM_HWS_INDEX);
 }
 
 static void
-ring_set_seqno(struct intel_ring_buffer *ring, u32 seqno)
+ring_set_seqno(struct intel_engine_cs *ring, u32 seqno)
 {
        intel_write_status_page(ring, I915_GEM_HWS_INDEX, seqno);
 }
 
 static u32
-pc_render_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency)
+pc_render_get_seqno(struct intel_engine_cs *ring, bool lazy_coherency)
 {
        return ring->scratch.cpu_page[0];
 }
 
 static void
-pc_render_set_seqno(struct intel_ring_buffer *ring, u32 seqno)
+pc_render_set_seqno(struct intel_engine_cs *ring, u32 seqno)
 {
        ring->scratch.cpu_page[0] = seqno;
 }
 
 static bool
-gen5_ring_get_irq(struct intel_ring_buffer *ring)
+gen5_ring_get_irq(struct intel_engine_cs *ring)
 {
        struct drm_device *dev = ring->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -886,7 +908,7 @@ gen5_ring_get_irq(struct intel_ring_buffer *ring)
 }
 
 static void
-gen5_ring_put_irq(struct intel_ring_buffer *ring)
+gen5_ring_put_irq(struct intel_engine_cs *ring)
 {
        struct drm_device *dev = ring->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -899,7 +921,7 @@ gen5_ring_put_irq(struct intel_ring_buffer *ring)
 }
 
 static bool
-i9xx_ring_get_irq(struct intel_ring_buffer *ring)
+i9xx_ring_get_irq(struct intel_engine_cs *ring)
 {
        struct drm_device *dev = ring->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -920,7 +942,7 @@ i9xx_ring_get_irq(struct intel_ring_buffer *ring)
 }
 
 static void
-i9xx_ring_put_irq(struct intel_ring_buffer *ring)
+i9xx_ring_put_irq(struct intel_engine_cs *ring)
 {
        struct drm_device *dev = ring->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -936,7 +958,7 @@ i9xx_ring_put_irq(struct intel_ring_buffer *ring)
 }
 
 static bool
-i8xx_ring_get_irq(struct intel_ring_buffer *ring)
+i8xx_ring_get_irq(struct intel_engine_cs *ring)
 {
        struct drm_device *dev = ring->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -957,7 +979,7 @@ i8xx_ring_get_irq(struct intel_ring_buffer *ring)
 }
 
 static void
-i8xx_ring_put_irq(struct intel_ring_buffer *ring)
+i8xx_ring_put_irq(struct intel_engine_cs *ring)
 {
        struct drm_device *dev = ring->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -972,7 +994,7 @@ i8xx_ring_put_irq(struct intel_ring_buffer *ring)
        spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
 }
 
-void intel_ring_setup_status_page(struct intel_ring_buffer *ring)
+void intel_ring_setup_status_page(struct intel_engine_cs *ring)
 {
        struct drm_device *dev = ring->dev;
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
@@ -989,6 +1011,11 @@ void intel_ring_setup_status_page(struct intel_ring_buffer *ring)
                case BCS:
                        mmio = BLT_HWS_PGA_GEN7;
                        break;
+               /*
+                * VCS2 actually doesn't exist on Gen7. Only shut up
+                * gcc switch check warning
+                */
+               case VCS2:
                case VCS:
                        mmio = BSD_HWS_PGA_GEN7;
                        break;
@@ -1030,7 +1057,7 @@ void intel_ring_setup_status_page(struct intel_ring_buffer *ring)
 }
 
 static int
-bsd_ring_flush(struct intel_ring_buffer *ring,
+bsd_ring_flush(struct intel_engine_cs *ring,
               u32     invalidate_domains,
               u32     flush_domains)
 {
@@ -1047,7 +1074,7 @@ bsd_ring_flush(struct intel_ring_buffer *ring,
 }
 
 static int
-i9xx_add_request(struct intel_ring_buffer *ring)
+i9xx_add_request(struct intel_engine_cs *ring)
 {
        int ret;
 
@@ -1065,7 +1092,7 @@ i9xx_add_request(struct intel_ring_buffer *ring)
 }
 
 static bool
-gen6_ring_get_irq(struct intel_ring_buffer *ring)
+gen6_ring_get_irq(struct intel_engine_cs *ring)
 {
        struct drm_device *dev = ring->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1090,7 +1117,7 @@ gen6_ring_get_irq(struct intel_ring_buffer *ring)
 }
 
 static void
-gen6_ring_put_irq(struct intel_ring_buffer *ring)
+gen6_ring_put_irq(struct intel_engine_cs *ring)
 {
        struct drm_device *dev = ring->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1108,7 +1135,7 @@ gen6_ring_put_irq(struct intel_ring_buffer *ring)
 }
 
 static bool
-hsw_vebox_get_irq(struct intel_ring_buffer *ring)
+hsw_vebox_get_irq(struct intel_engine_cs *ring)
 {
        struct drm_device *dev = ring->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1128,7 +1155,7 @@ hsw_vebox_get_irq(struct intel_ring_buffer *ring)
 }
 
 static void
-hsw_vebox_put_irq(struct intel_ring_buffer *ring)
+hsw_vebox_put_irq(struct intel_engine_cs *ring)
 {
        struct drm_device *dev = ring->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1146,7 +1173,7 @@ hsw_vebox_put_irq(struct intel_ring_buffer *ring)
 }
 
 static bool
-gen8_ring_get_irq(struct intel_ring_buffer *ring)
+gen8_ring_get_irq(struct intel_engine_cs *ring)
 {
        struct drm_device *dev = ring->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1172,7 +1199,7 @@ gen8_ring_get_irq(struct intel_ring_buffer *ring)
 }
 
 static void
-gen8_ring_put_irq(struct intel_ring_buffer *ring)
+gen8_ring_put_irq(struct intel_engine_cs *ring)
 {
        struct drm_device *dev = ring->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1192,8 +1219,8 @@ gen8_ring_put_irq(struct intel_ring_buffer *ring)
 }
 
 static int
-i965_dispatch_execbuffer(struct intel_ring_buffer *ring,
-                        u32 offset, u32 length,
+i965_dispatch_execbuffer(struct intel_engine_cs *ring,
+                        u64 offset, u32 length,
                         unsigned flags)
 {
        int ret;
@@ -1215,8 +1242,8 @@ i965_dispatch_execbuffer(struct intel_ring_buffer *ring,
 /* Just userspace ABI convention to limit the wa batch bo to a resonable size */
 #define I830_BATCH_LIMIT (256*1024)
 static int
-i830_dispatch_execbuffer(struct intel_ring_buffer *ring,
-                               u32 offset, u32 len,
+i830_dispatch_execbuffer(struct intel_engine_cs *ring,
+                               u64 offset, u32 len,
                                unsigned flags)
 {
        int ret;
@@ -1266,8 +1293,8 @@ i830_dispatch_execbuffer(struct intel_ring_buffer *ring,
 }
 
 static int
-i915_dispatch_execbuffer(struct intel_ring_buffer *ring,
-                        u32 offset, u32 len,
+i915_dispatch_execbuffer(struct intel_engine_cs *ring,
+                        u64 offset, u32 len,
                         unsigned flags)
 {
        int ret;
@@ -1283,7 +1310,7 @@ i915_dispatch_execbuffer(struct intel_ring_buffer *ring,
        return 0;
 }
 
-static void cleanup_status_page(struct intel_ring_buffer *ring)
+static void cleanup_status_page(struct intel_engine_cs *ring)
 {
        struct drm_i915_gem_object *obj;
 
@@ -1297,50 +1324,44 @@ static void cleanup_status_page(struct intel_ring_buffer *ring)
        ring->status_page.obj = NULL;
 }
 
-static int init_status_page(struct intel_ring_buffer *ring)
+static int init_status_page(struct intel_engine_cs *ring)
 {
-       struct drm_device *dev = ring->dev;
        struct drm_i915_gem_object *obj;
-       int ret;
 
-       obj = i915_gem_alloc_object(dev, 4096);
-       if (obj == NULL) {
-               DRM_ERROR("Failed to allocate status page\n");
-               ret = -ENOMEM;
-               goto err;
-       }
+       if ((obj = ring->status_page.obj) == NULL) {
+               int ret;
 
-       ret = i915_gem_object_set_cache_level(obj, I915_CACHE_LLC);
-       if (ret)
-               goto err_unref;
+               obj = i915_gem_alloc_object(ring->dev, 4096);
+               if (obj == NULL) {
+                       DRM_ERROR("Failed to allocate status page\n");
+                       return -ENOMEM;
+               }
 
-       ret = i915_gem_obj_ggtt_pin(obj, 4096, 0);
-       if (ret)
-               goto err_unref;
+               ret = i915_gem_object_set_cache_level(obj, I915_CACHE_LLC);
+               if (ret)
+                       goto err_unref;
+
+               ret = i915_gem_obj_ggtt_pin(obj, 4096, 0);
+               if (ret) {
+err_unref:
+                       drm_gem_object_unreference(&obj->base);
+                       return ret;
+               }
+
+               ring->status_page.obj = obj;
+       }
 
        ring->status_page.gfx_addr = i915_gem_obj_ggtt_offset(obj);
        ring->status_page.page_addr = kmap(sg_page(obj->pages->sgl));
-       if (ring->status_page.page_addr == NULL) {
-               ret = -ENOMEM;
-               goto err_unpin;
-       }
-       ring->status_page.obj = obj;
        memset(ring->status_page.page_addr, 0, PAGE_SIZE);
 
        DRM_DEBUG_DRIVER("%s hws offset: 0x%08x\n",
                        ring->name, ring->status_page.gfx_addr);
 
        return 0;
-
-err_unpin:
-       i915_gem_object_ggtt_unpin(obj);
-err_unref:
-       drm_gem_object_unreference(&obj->base);
-err:
-       return ret;
 }
 
-static int init_phys_status_page(struct intel_ring_buffer *ring)
+static int init_phys_status_page(struct intel_engine_cs *ring)
 {
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
 
@@ -1357,44 +1378,24 @@ static int init_phys_status_page(struct intel_ring_buffer *ring)
        return 0;
 }
 
-static int intel_init_ring_buffer(struct drm_device *dev,
-                                 struct intel_ring_buffer *ring)
+static int allocate_ring_buffer(struct intel_engine_cs *ring)
 {
+       struct drm_device *dev = ring->dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct intel_ringbuffer *ringbuf = ring->buffer;
        struct drm_i915_gem_object *obj;
-       struct drm_i915_private *dev_priv = dev->dev_private;
        int ret;
 
-       ring->dev = dev;
-       INIT_LIST_HEAD(&ring->active_list);
-       INIT_LIST_HEAD(&ring->request_list);
-       ring->size = 32 * PAGE_SIZE;
-       memset(ring->sync_seqno, 0, sizeof(ring->sync_seqno));
-
-       init_waitqueue_head(&ring->irq_queue);
-
-       if (I915_NEED_GFX_HWS(dev)) {
-               ret = init_status_page(ring);
-               if (ret)
-                       return ret;
-       } else {
-               BUG_ON(ring->id != RCS);
-               ret = init_phys_status_page(ring);
-               if (ret)
-                       return ret;
-       }
+       if (intel_ring_initialized(ring))
+               return 0;
 
        obj = NULL;
        if (!HAS_LLC(dev))
-               obj = i915_gem_object_create_stolen(dev, ring->size);
+               obj = i915_gem_object_create_stolen(dev, ringbuf->size);
        if (obj == NULL)
-               obj = i915_gem_alloc_object(dev, ring->size);
-       if (obj == NULL) {
-               DRM_ERROR("Failed to allocate ringbuffer\n");
-               ret = -ENOMEM;
-               goto err_hws;
-       }
-
-       ring->obj = obj;
+               obj = i915_gem_alloc_object(dev, ringbuf->size);
+       if (obj == NULL)
+               return -ENOMEM;
 
        ret = i915_gem_obj_ggtt_pin(obj, PAGE_SIZE, PIN_MAPPABLE);
        if (ret)
@@ -1404,65 +1405,102 @@ static int intel_init_ring_buffer(struct drm_device *dev,
        if (ret)
                goto err_unpin;
 
-       ring->virtual_start =
+       ringbuf->virtual_start =
                ioremap_wc(dev_priv->gtt.mappable_base + i915_gem_obj_ggtt_offset(obj),
-                          ring->size);
-       if (ring->virtual_start == NULL) {
-               DRM_ERROR("Failed to map ringbuffer.\n");
+                               ringbuf->size);
+       if (ringbuf->virtual_start == NULL) {
                ret = -EINVAL;
                goto err_unpin;
        }
 
-       ret = ring->init(ring);
-       if (ret)
-               goto err_unmap;
+       ringbuf->obj = obj;
+       return 0;
+
+err_unpin:
+       i915_gem_object_ggtt_unpin(obj);
+err_unref:
+       drm_gem_object_unreference(&obj->base);
+       return ret;
+}
+
+static int intel_init_ring_buffer(struct drm_device *dev,
+                                 struct intel_engine_cs *ring)
+{
+       struct intel_ringbuffer *ringbuf = ring->buffer;
+       int ret;
+
+       if (ringbuf == NULL) {
+               ringbuf = kzalloc(sizeof(*ringbuf), GFP_KERNEL);
+               if (!ringbuf)
+                       return -ENOMEM;
+               ring->buffer = ringbuf;
+       }
+
+       ring->dev = dev;
+       INIT_LIST_HEAD(&ring->active_list);
+       INIT_LIST_HEAD(&ring->request_list);
+       ringbuf->size = 32 * PAGE_SIZE;
+       memset(ring->semaphore.sync_seqno, 0, sizeof(ring->semaphore.sync_seqno));
+
+       init_waitqueue_head(&ring->irq_queue);
+
+       if (I915_NEED_GFX_HWS(dev)) {
+               ret = init_status_page(ring);
+               if (ret)
+                       goto error;
+       } else {
+               BUG_ON(ring->id != RCS);
+               ret = init_phys_status_page(ring);
+               if (ret)
+                       goto error;
+       }
+
+       ret = allocate_ring_buffer(ring);
+       if (ret) {
+               DRM_ERROR("Failed to allocate ringbuffer %s: %d\n", ring->name, ret);
+               goto error;
+       }
 
        /* Workaround an erratum on the i830 which causes a hang if
         * the TAIL pointer points to within the last 2 cachelines
         * of the buffer.
         */
-       ring->effective_size = ring->size;
-       if (IS_I830(ring->dev) || IS_845G(ring->dev))
-               ring->effective_size -= 128;
+       ringbuf->effective_size = ringbuf->size;
+       if (IS_I830(dev) || IS_845G(dev))
+               ringbuf->effective_size -= 2 * CACHELINE_BYTES;
 
-       i915_cmd_parser_init_ring(ring);
+       ret = i915_cmd_parser_init_ring(ring);
+       if (ret)
+               goto error;
+
+       ret = ring->init(ring);
+       if (ret)
+               goto error;
 
        return 0;
 
-err_unmap:
-       iounmap(ring->virtual_start);
-err_unpin:
-       i915_gem_object_ggtt_unpin(obj);
-err_unref:
-       drm_gem_object_unreference(&obj->base);
-       ring->obj = NULL;
-err_hws:
-       cleanup_status_page(ring);
+error:
+       kfree(ringbuf);
+       ring->buffer = NULL;
        return ret;
 }
 
-void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring)
+void intel_cleanup_ring_buffer(struct intel_engine_cs *ring)
 {
-       struct drm_i915_private *dev_priv;
-       int ret;
+       struct drm_i915_private *dev_priv = to_i915(ring->dev);
+       struct intel_ringbuffer *ringbuf = ring->buffer;
 
-       if (ring->obj == NULL)
+       if (!intel_ring_initialized(ring))
                return;
 
-       /* Disable the ring buffer. The ring must be idle at this point */
-       dev_priv = ring->dev->dev_private;
-       ret = intel_ring_idle(ring);
-       if (ret && !i915_reset_in_progress(&dev_priv->gpu_error))
-               DRM_ERROR("failed to quiesce %s whilst cleaning up: %d\n",
-                         ring->name, ret);
-
-       I915_WRITE_CTL(ring, 0);
+       intel_stop_ring_buffer(ring);
+       WARN_ON(!IS_GEN2(ring->dev) && (I915_READ_MODE(ring) & MODE_IDLE) == 0);
 
-       iounmap(ring->virtual_start);
+       iounmap(ringbuf->virtual_start);
 
-       i915_gem_object_ggtt_unpin(ring->obj);
-       drm_gem_object_unreference(&ring->obj->base);
-       ring->obj = NULL;
+       i915_gem_object_ggtt_unpin(ringbuf->obj);
+       drm_gem_object_unreference(&ringbuf->obj->base);
+       ringbuf->obj = NULL;
        ring->preallocated_lazy_request = NULL;
        ring->outstanding_lazy_seqno = 0;
 
@@ -1470,44 +1508,34 @@ void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring)
                ring->cleanup(ring);
 
        cleanup_status_page(ring);
+
+       i915_cmd_parser_fini_ring(ring);
+
+       kfree(ringbuf);
+       ring->buffer = NULL;
 }
 
-static int intel_ring_wait_request(struct intel_ring_buffer *ring, int n)
+static int intel_ring_wait_request(struct intel_engine_cs *ring, int n)
 {
+       struct intel_ringbuffer *ringbuf = ring->buffer;
        struct drm_i915_gem_request *request;
-       u32 seqno = 0, tail;
+       u32 seqno = 0;
        int ret;
 
-       if (ring->last_retired_head != -1) {
-               ring->head = ring->last_retired_head;
-               ring->last_retired_head = -1;
+       if (ringbuf->last_retired_head != -1) {
+               ringbuf->head = ringbuf->last_retired_head;
+               ringbuf->last_retired_head = -1;
 
-               ring->space = ring_space(ring);
-               if (ring->space >= n)
+               ringbuf->space = ring_space(ring);
+               if (ringbuf->space >= n)
                        return 0;
        }
 
        list_for_each_entry(request, &ring->request_list, list) {
-               int space;
-
-               if (request->tail == -1)
-                       continue;
-
-               space = request->tail - (ring->tail + I915_RING_FREE_SPACE);
-               if (space < 0)
-                       space += ring->size;
-               if (space >= n) {
+               if (__ring_space(request->tail, ringbuf->tail, ringbuf->size) >= n) {
                        seqno = request->seqno;
-                       tail = request->tail;
                        break;
                }
-
-               /* Consume this request in case we need more space than
-                * is available and so need to prevent a race between
-                * updating last_retired_head and direct reads of
-                * I915_RING_HEAD. It also provides a nice sanity check.
-                */
-               request->tail = -1;
        }
 
        if (seqno == 0)
@@ -1517,18 +1545,19 @@ static int intel_ring_wait_request(struct intel_ring_buffer *ring, int n)
        if (ret)
                return ret;
 
-       ring->head = tail;
-       ring->space = ring_space(ring);
-       if (WARN_ON(ring->space < n))
-               return -ENOSPC;
+       i915_gem_retire_requests_ring(ring);
+       ringbuf->head = ringbuf->last_retired_head;
+       ringbuf->last_retired_head = -1;
 
+       ringbuf->space = ring_space(ring);
        return 0;
 }
 
-static int ring_wait_for_space(struct intel_ring_buffer *ring, int n)
+static int ring_wait_for_space(struct intel_engine_cs *ring, int n)
 {
        struct drm_device *dev = ring->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_ringbuffer *ringbuf = ring->buffer;
        unsigned long end;
        int ret;
 
@@ -1539,7 +1568,6 @@ static int ring_wait_for_space(struct intel_ring_buffer *ring, int n)
        /* force the tail write in case we have been skipping them */
        __intel_ring_advance(ring);
 
-       trace_i915_ring_wait_begin(ring);
        /* With GEM the hangcheck timer should kick us out of the loop,
         * leaving it early runs the risk of corrupting GEM state (due
         * to running on almost untested codepaths). But on resume
@@ -1547,12 +1575,13 @@ static int ring_wait_for_space(struct intel_ring_buffer *ring, int n)
         * case by choosing an insanely large timeout. */
        end = jiffies + 60 * HZ;
 
+       trace_i915_ring_wait_begin(ring);
        do {
-               ring->head = I915_READ_HEAD(ring);
-               ring->space = ring_space(ring);
-               if (ring->space >= n) {
-                       trace_i915_ring_wait_end(ring);
-                       return 0;
+               ringbuf->head = I915_READ_HEAD(ring);
+               ringbuf->space = ring_space(ring);
+               if (ringbuf->space >= n) {
+                       ret = 0;
+                       break;
                }
 
                if (!drm_core_check_feature(dev, DRIVER_MODESET) &&
@@ -1564,38 +1593,49 @@ static int ring_wait_for_space(struct intel_ring_buffer *ring, int n)
 
                msleep(1);
 
+               if (dev_priv->mm.interruptible && signal_pending(current)) {
+                       ret = -ERESTARTSYS;
+                       break;
+               }
+
                ret = i915_gem_check_wedge(&dev_priv->gpu_error,
                                           dev_priv->mm.interruptible);
                if (ret)
-                       return ret;
-       } while (!time_after(jiffies, end));
+                       break;
+
+               if (time_after(jiffies, end)) {
+                       ret = -EBUSY;
+                       break;
+               }
+       } while (1);
        trace_i915_ring_wait_end(ring);
-       return -EBUSY;
+       return ret;
 }
 
-static int intel_wrap_ring_buffer(struct intel_ring_buffer *ring)
+static int intel_wrap_ring_buffer(struct intel_engine_cs *ring)
 {
        uint32_t __iomem *virt;
-       int rem = ring->size - ring->tail;
+       struct intel_ringbuffer *ringbuf = ring->buffer;
+       int rem = ringbuf->size - ringbuf->tail;
 
-       if (ring->space < rem) {
+       if (ringbuf->space < rem) {
                int ret = ring_wait_for_space(ring, rem);
                if (ret)
                        return ret;
        }
 
-       virt = ring->virtual_start + ring->tail;
+       virt = ringbuf->virtual_start + ringbuf->tail;
        rem /= 4;
        while (rem--)
                iowrite32(MI_NOOP, virt++);
 
-       ring->tail = 0;
-       ring->space = ring_space(ring);
+       ringbuf->tail = 0;
+       ringbuf->space = ring_space(ring);
 
        return 0;
 }
 
-int intel_ring_idle(struct intel_ring_buffer *ring)
+int intel_ring_idle(struct intel_engine_cs *ring)
 {
        u32 seqno;
        int ret;
@@ -1619,7 +1659,7 @@ int intel_ring_idle(struct intel_ring_buffer *ring)
 }
 
 static int
-intel_ring_alloc_seqno(struct intel_ring_buffer *ring)
+intel_ring_alloc_seqno(struct intel_engine_cs *ring)
 {
        if (ring->outstanding_lazy_seqno)
                return 0;
@@ -1637,18 +1677,19 @@ intel_ring_alloc_seqno(struct intel_ring_buffer *ring)
        return i915_gem_get_seqno(ring->dev, &ring->outstanding_lazy_seqno);
 }
 
-static int __intel_ring_prepare(struct intel_ring_buffer *ring,
+static int __intel_ring_prepare(struct intel_engine_cs *ring,
                                int bytes)
 {
+       struct intel_ringbuffer *ringbuf = ring->buffer;
        int ret;
 
-       if (unlikely(ring->tail + bytes > ring->effective_size)) {
+       if (unlikely(ringbuf->tail + bytes > ringbuf->effective_size)) {
                ret = intel_wrap_ring_buffer(ring);
                if (unlikely(ret))
                        return ret;
        }
 
-       if (unlikely(ring->space < bytes)) {
+       if (unlikely(ringbuf->space < bytes)) {
                ret = ring_wait_for_space(ring, bytes);
                if (unlikely(ret))
                        return ret;
@@ -1657,7 +1698,7 @@ static int __intel_ring_prepare(struct intel_ring_buffer *ring,
        return 0;
 }
 
-int intel_ring_begin(struct intel_ring_buffer *ring,
+int intel_ring_begin(struct intel_engine_cs *ring,
                     int num_dwords)
 {
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
@@ -1677,19 +1718,20 @@ int intel_ring_begin(struct intel_ring_buffer *ring,
        if (ret)
                return ret;
 
-       ring->space -= num_dwords * sizeof(uint32_t);
+       ring->buffer->space -= num_dwords * sizeof(uint32_t);
        return 0;
 }
 
 /* Align the ring tail to a cacheline boundary */
-int intel_ring_cacheline_align(struct intel_ring_buffer *ring)
+int intel_ring_cacheline_align(struct intel_engine_cs *ring)
 {
-       int num_dwords = (64 - (ring->tail & 63)) / sizeof(uint32_t);
+       int num_dwords = (ring->buffer->tail & (CACHELINE_BYTES - 1)) / sizeof(uint32_t);
        int ret;
 
        if (num_dwords == 0)
                return 0;
 
+       num_dwords = CACHELINE_BYTES / sizeof(uint32_t) - num_dwords;
        ret = intel_ring_begin(ring, num_dwords);
        if (ret)
                return ret;
@@ -1702,7 +1744,7 @@ int intel_ring_cacheline_align(struct intel_ring_buffer *ring)
        return 0;
 }
 
-void intel_ring_init_seqno(struct intel_ring_buffer *ring, u32 seqno)
+void intel_ring_init_seqno(struct intel_engine_cs *ring, u32 seqno)
 {
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
 
@@ -1719,7 +1761,7 @@ void intel_ring_init_seqno(struct intel_ring_buffer *ring, u32 seqno)
        ring->hangcheck.seqno = seqno;
 }
 
-static void gen6_bsd_ring_write_tail(struct intel_ring_buffer *ring,
+static void gen6_bsd_ring_write_tail(struct intel_engine_cs *ring,
                                     u32 value)
 {
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
@@ -1752,7 +1794,7 @@ static void gen6_bsd_ring_write_tail(struct intel_ring_buffer *ring,
                   _MASKED_BIT_DISABLE(GEN6_BSD_SLEEP_MSG_DISABLE));
 }
 
-static int gen6_bsd_ring_flush(struct intel_ring_buffer *ring,
+static int gen6_bsd_ring_flush(struct intel_engine_cs *ring,
                               u32 invalidate, u32 flush)
 {
        uint32_t cmd;
@@ -1788,8 +1830,8 @@ static int gen6_bsd_ring_flush(struct intel_ring_buffer *ring,
 }
 
 static int
-gen8_ring_dispatch_execbuffer(struct intel_ring_buffer *ring,
-                             u32 offset, u32 len,
+gen8_ring_dispatch_execbuffer(struct intel_engine_cs *ring,
+                             u64 offset, u32 len,
                              unsigned flags)
 {
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
@@ -1803,8 +1845,8 @@ gen8_ring_dispatch_execbuffer(struct intel_ring_buffer *ring,
 
        /* FIXME(BDW): Address space and security selectors. */
        intel_ring_emit(ring, MI_BATCH_BUFFER_START_GEN8 | (ppgtt<<8));
-       intel_ring_emit(ring, offset);
-       intel_ring_emit(ring, 0);
+       intel_ring_emit(ring, lower_32_bits(offset));
+       intel_ring_emit(ring, upper_32_bits(offset));
        intel_ring_emit(ring, MI_NOOP);
        intel_ring_advance(ring);
 
@@ -1812,8 +1854,8 @@ gen8_ring_dispatch_execbuffer(struct intel_ring_buffer *ring,
 }
 
 static int
-hsw_ring_dispatch_execbuffer(struct intel_ring_buffer *ring,
-                             u32 offset, u32 len,
+hsw_ring_dispatch_execbuffer(struct intel_engine_cs *ring,
+                             u64 offset, u32 len,
                              unsigned flags)
 {
        int ret;
@@ -1833,8 +1875,8 @@ hsw_ring_dispatch_execbuffer(struct intel_ring_buffer *ring,
 }
 
 static int
-gen6_ring_dispatch_execbuffer(struct intel_ring_buffer *ring,
-                             u32 offset, u32 len,
+gen6_ring_dispatch_execbuffer(struct intel_engine_cs *ring,
+                             u64 offset, u32 len,
                              unsigned flags)
 {
        int ret;
@@ -1855,7 +1897,7 @@ gen6_ring_dispatch_execbuffer(struct intel_ring_buffer *ring,
 
 /* Blitter support (SandyBridge+) */
 
-static int gen6_ring_flush(struct intel_ring_buffer *ring,
+static int gen6_ring_flush(struct intel_engine_cs *ring,
                           u32 invalidate, u32 flush)
 {
        struct drm_device *dev = ring->dev;
@@ -1898,7 +1940,7 @@ static int gen6_ring_flush(struct intel_ring_buffer *ring,
 int intel_init_render_ring_buffer(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
+       struct intel_engine_cs *ring = &dev_priv->ring[RCS];
 
        ring->name = "render ring";
        ring->id = RCS;
@@ -1920,15 +1962,24 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
                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[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;
+               ring->semaphore.sync_to = gen6_ring_sync;
+               ring->semaphore.signal = gen6_signal;
+               /*
+                * The current semaphore is only applied on pre-gen8 platform.
+                * And there is no VCS2 ring on the pre-gen8 platform. So the
+                * semaphore between RCS and VCS2 is initialized as INVALID.
+                * Gen8 will initialize the sema between VCS2 and RCS later.
+                */
+               ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_INVALID;
+               ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_RV;
+               ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_RB;
+               ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_RVE;
+               ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID;
+               ring->semaphore.mbox.signal[RCS] = GEN6_NOSYNC;
+               ring->semaphore.mbox.signal[VCS] = GEN6_VRSYNC;
+               ring->semaphore.mbox.signal[BCS] = GEN6_BRSYNC;
+               ring->semaphore.mbox.signal[VECS] = GEN6_VERSYNC;
+               ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC;
        } else if (IS_GEN5(dev)) {
                ring->add_request = pc_render_add_request;
                ring->flush = gen4_render_ring_flush;
@@ -1999,16 +2050,25 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
 int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
+       struct intel_engine_cs *ring = &dev_priv->ring[RCS];
+       struct intel_ringbuffer *ringbuf = ring->buffer;
        int ret;
 
+       if (ringbuf == NULL) {
+               ringbuf = kzalloc(sizeof(*ringbuf), GFP_KERNEL);
+               if (!ringbuf)
+                       return -ENOMEM;
+               ring->buffer = ringbuf;
+       }
+
        ring->name = "render ring";
        ring->id = RCS;
        ring->mmio_base = RENDER_RING_BASE;
 
        if (INTEL_INFO(dev)->gen >= 6) {
                /* non-kms not supported on gen6+ */
-               return -ENODEV;
+               ret = -ENODEV;
+               goto err_ringbuf;
        }
 
        /* Note: gem is not supported on gen5/ilk without kms (the corresponding
@@ -2043,31 +2103,39 @@ int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size)
        INIT_LIST_HEAD(&ring->active_list);
        INIT_LIST_HEAD(&ring->request_list);
 
-       ring->size = size;
-       ring->effective_size = ring->size;
+       ringbuf->size = size;
+       ringbuf->effective_size = ringbuf->size;
        if (IS_I830(ring->dev) || IS_845G(ring->dev))
-               ring->effective_size -= 128;
+               ringbuf->effective_size -= 2 * CACHELINE_BYTES;
 
-       ring->virtual_start = ioremap_wc(start, size);
-       if (ring->virtual_start == NULL) {
+       ringbuf->virtual_start = ioremap_wc(start, size);
+       if (ringbuf->virtual_start == NULL) {
                DRM_ERROR("can not ioremap virtual address for"
                          " ring buffer\n");
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto err_ringbuf;
        }
 
        if (!I915_NEED_GFX_HWS(dev)) {
                ret = init_phys_status_page(ring);
                if (ret)
-                       return ret;
+                       goto err_vstart;
        }
 
        return 0;
+
+err_vstart:
+       iounmap(ringbuf->virtual_start);
+err_ringbuf:
+       kfree(ringbuf);
+       ring->buffer = NULL;
+       return ret;
 }
 
 int intel_init_bsd_ring_buffer(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring = &dev_priv->ring[VCS];
+       struct intel_engine_cs *ring = &dev_priv->ring[VCS];
 
        ring->name = "bsd ring";
        ring->id = VCS;
@@ -2096,15 +2164,24 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev)
                        ring->dispatch_execbuffer =
                                gen6_ring_dispatch_execbuffer;
                }
-               ring->sync_to = gen6_ring_sync;
-               ring->semaphore_register[RCS] = MI_SEMAPHORE_SYNC_VR;
-               ring->semaphore_register[VCS] = MI_SEMAPHORE_SYNC_INVALID;
-               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;
+               ring->semaphore.sync_to = gen6_ring_sync;
+               ring->semaphore.signal = gen6_signal;
+               /*
+                * The current semaphore is only applied on pre-gen8 platform.
+                * And there is no VCS2 ring on the pre-gen8 platform. So the
+                * semaphore between VCS and VCS2 is initialized as INVALID.
+                * Gen8 will initialize the sema between VCS2 and VCS later.
+                */
+               ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_VR;
+               ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_INVALID;
+               ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_VB;
+               ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_VVE;
+               ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID;
+               ring->semaphore.mbox.signal[RCS] = GEN6_RVSYNC;
+               ring->semaphore.mbox.signal[VCS] = GEN6_NOSYNC;
+               ring->semaphore.mbox.signal[BCS] = GEN6_BVSYNC;
+               ring->semaphore.mbox.signal[VECS] = GEN6_VEVSYNC;
+               ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC;
        } else {
                ring->mmio_base = BSD_RING_BASE;
                ring->flush = bsd_ring_flush;
@@ -2127,10 +2204,63 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev)
        return intel_init_ring_buffer(dev, ring);
 }
 
+/**
+ * Initialize the second BSD ring for Broadwell GT3.
+ * It is noted that this only exists on Broadwell GT3.
+ */
+int intel_init_bsd2_ring_buffer(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_engine_cs *ring = &dev_priv->ring[VCS2];
+
+       if ((INTEL_INFO(dev)->gen != 8)) {
+               DRM_ERROR("No dual-BSD ring on non-BDW machine\n");
+               return -EINVAL;
+       }
+
+       ring->name = "bds2_ring";
+       ring->id = VCS2;
+
+       ring->write_tail = ring_write_tail;
+       ring->mmio_base = GEN8_BSD2_RING_BASE;
+       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 =
+                       GT_RENDER_USER_INTERRUPT << GEN8_VCS2_IRQ_SHIFT;
+       ring->irq_get = gen8_ring_get_irq;
+       ring->irq_put = gen8_ring_put_irq;
+       ring->dispatch_execbuffer =
+                       gen8_ring_dispatch_execbuffer;
+       ring->semaphore.sync_to = gen6_ring_sync;
+       ring->semaphore.signal = gen6_signal;
+       /*
+        * The current semaphore is only applied on the pre-gen8. And there
+        * is no bsd2 ring on the pre-gen8. So now the semaphore_register
+        * between VCS2 and other ring is initialized as invalid.
+        * Gen8 will initialize the sema between VCS2 and other ring later.
+        */
+       ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_INVALID;
+       ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_INVALID;
+       ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_INVALID;
+       ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_INVALID;
+       ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID;
+       ring->semaphore.mbox.signal[RCS] = GEN6_NOSYNC;
+       ring->semaphore.mbox.signal[VCS] = GEN6_NOSYNC;
+       ring->semaphore.mbox.signal[BCS] = GEN6_NOSYNC;
+       ring->semaphore.mbox.signal[VECS] = GEN6_NOSYNC;
+       ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC;
+
+       ring->init = init_ring_common;
+
+       return intel_init_ring_buffer(dev, ring);
+}
+
 int intel_init_blt_ring_buffer(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring = &dev_priv->ring[BCS];
+       struct intel_engine_cs *ring = &dev_priv->ring[BCS];
 
        ring->name = "blitter ring";
        ring->id = BCS;
@@ -2153,15 +2283,24 @@ int intel_init_blt_ring_buffer(struct drm_device *dev)
                ring->irq_put = gen6_ring_put_irq;
                ring->dispatch_execbuffer = gen6_ring_dispatch_execbuffer;
        }
-       ring->sync_to = gen6_ring_sync;
-       ring->semaphore_register[RCS] = MI_SEMAPHORE_SYNC_BR;
-       ring->semaphore_register[VCS] = MI_SEMAPHORE_SYNC_BV;
-       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->semaphore.sync_to = gen6_ring_sync;
+       ring->semaphore.signal = gen6_signal;
+       /*
+        * The current semaphore is only applied on pre-gen8 platform. And
+        * there is no VCS2 ring on the pre-gen8 platform. So the semaphore
+        * between BCS and VCS2 is initialized as INVALID.
+        * Gen8 will initialize the sema between BCS and VCS2 later.
+        */
+       ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_BR;
+       ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_BV;
+       ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_INVALID;
+       ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_BVE;
+       ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID;
+       ring->semaphore.mbox.signal[RCS] = GEN6_RBSYNC;
+       ring->semaphore.mbox.signal[VCS] = GEN6_VBSYNC;
+       ring->semaphore.mbox.signal[BCS] = GEN6_NOSYNC;
+       ring->semaphore.mbox.signal[VECS] = GEN6_VEBSYNC;
+       ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC;
        ring->init = init_ring_common;
 
        return intel_init_ring_buffer(dev, ring);
@@ -2170,7 +2309,7 @@ int intel_init_blt_ring_buffer(struct drm_device *dev)
 int intel_init_vebox_ring_buffer(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring = &dev_priv->ring[VECS];
+       struct intel_engine_cs *ring = &dev_priv->ring[VECS];
 
        ring->name = "video enhancement ring";
        ring->id = VECS;
@@ -2194,22 +2333,25 @@ int intel_init_vebox_ring_buffer(struct drm_device *dev)
                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->semaphore.sync_to = gen6_ring_sync;
+       ring->semaphore.signal = gen6_signal;
+       ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_VER;
+       ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_VEV;
+       ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_VEB;
+       ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_INVALID;
+       ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID;
+       ring->semaphore.mbox.signal[RCS] = GEN6_RVESYNC;
+       ring->semaphore.mbox.signal[VCS] = GEN6_VVESYNC;
+       ring->semaphore.mbox.signal[BCS] = GEN6_BVESYNC;
+       ring->semaphore.mbox.signal[VECS] = GEN6_NOSYNC;
+       ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC;
        ring->init = init_ring_common;
 
        return intel_init_ring_buffer(dev, ring);
 }
 
 int
-intel_ring_flush_all_caches(struct intel_ring_buffer *ring)
+intel_ring_flush_all_caches(struct intel_engine_cs *ring)
 {
        int ret;
 
@@ -2227,7 +2369,7 @@ intel_ring_flush_all_caches(struct intel_ring_buffer *ring)
 }
 
 int
-intel_ring_invalidate_all_caches(struct intel_ring_buffer *ring)
+intel_ring_invalidate_all_caches(struct intel_engine_cs *ring)
 {
        uint32_t flush_domains;
        int ret;
@@ -2245,3 +2387,19 @@ intel_ring_invalidate_all_caches(struct intel_ring_buffer *ring)
        ring->gpu_caches_dirty = false;
        return 0;
 }
+
+void
+intel_stop_ring_buffer(struct intel_engine_cs *ring)
+{
+       int ret;
+
+       if (!intel_ring_initialized(ring))
+               return;
+
+       ret = intel_ring_idle(ring);
+       if (ret && !i915_reset_in_progress(&to_i915(ring->dev)->gpu_error))
+               DRM_ERROR("failed to quiesce %s whilst cleaning up: %d\n",
+                         ring->name, ret);
+
+       stop_ring(ring);
+}
index 2b91c4b4d34b2efd6029bb52f46ece14eb2e515f..910c83cf7d441e26d226e4c975ad712949d32f0a 100644 (file)
@@ -1,6 +1,10 @@
 #ifndef _INTEL_RINGBUFFER_H_
 #define _INTEL_RINGBUFFER_H_
 
+#include <linux/hashtable.h>
+
+#define I915_CMD_HASH_ORDER 9
+
 /*
  * Gen2 BSpec "1. Programming Environment" / 1.4.4.6 "Ring Buffer Use"
  * Gen3 BSpec "vol1c Memory Interface Functions" / 2.3.4.5 "Ring Buffer Use"
@@ -54,76 +58,93 @@ struct intel_ring_hangcheck {
        bool deadlock;
 };
 
-struct  intel_ring_buffer {
+struct intel_ringbuffer {
+       struct drm_i915_gem_object *obj;
+       void __iomem *virtual_start;
+
+       u32 head;
+       u32 tail;
+       int space;
+       int size;
+       int effective_size;
+
+       /** We track the position of the requests in the ring buffer, and
+        * when each is retired we increment last_retired_head as the GPU
+        * must have finished processing the request and so we know we
+        * can advance the ringbuffer up to that position.
+        *
+        * last_retired_head is set to -1 after the value is consumed so
+        * we can detect new retirements.
+        */
+       u32 last_retired_head;
+};
+
+struct  intel_engine_cs {
        const char      *name;
        enum intel_ring_id {
                RCS = 0x0,
                VCS,
                BCS,
                VECS,
+               VCS2
        } id;
-#define I915_NUM_RINGS 4
+#define I915_NUM_RINGS 5
+#define LAST_USER_RING (VECS + 1)
        u32             mmio_base;
-       void            __iomem *virtual_start;
        struct          drm_device *dev;
-       struct          drm_i915_gem_object *obj;
+       struct intel_ringbuffer *buffer;
 
-       u32             head;
-       u32             tail;
-       int             space;
-       int             size;
-       int             effective_size;
        struct intel_hw_status_page status_page;
 
-       /** We track the position of the requests in the ring buffer, and
-        * when each is retired we increment last_retired_head as the GPU
-        * must have finished processing the request and so we know we
-        * can advance the ringbuffer up to that position.
-        *
-        * last_retired_head is set to -1 after the value is consumed so
-        * we can detect new retirements.
-        */
-       u32             last_retired_head;
-
        unsigned irq_refcount; /* protected by dev_priv->irq_lock */
        u32             irq_enable_mask;        /* bitmask to enable ring interrupt */
        u32             trace_irq_seqno;
-       u32             sync_seqno[I915_NUM_RINGS-1];
-       bool __must_check (*irq_get)(struct intel_ring_buffer *ring);
-       void            (*irq_put)(struct intel_ring_buffer *ring);
+       bool __must_check (*irq_get)(struct intel_engine_cs *ring);
+       void            (*irq_put)(struct intel_engine_cs *ring);
 
-       int             (*init)(struct intel_ring_buffer *ring);
+       int             (*init)(struct intel_engine_cs *ring);
 
-       void            (*write_tail)(struct intel_ring_buffer *ring,
+       void            (*write_tail)(struct intel_engine_cs *ring,
                                      u32 value);
-       int __must_check (*flush)(struct intel_ring_buffer *ring,
+       int __must_check (*flush)(struct intel_engine_cs *ring,
                                  u32   invalidate_domains,
                                  u32   flush_domains);
-       int             (*add_request)(struct intel_ring_buffer *ring);
+       int             (*add_request)(struct intel_engine_cs *ring);
        /* Some chipsets are not quite as coherent as advertised and need
         * an expensive kick to force a true read of the up-to-date seqno.
         * However, the up-to-date seqno is not always required and the last
         * seen value is good enough. Note that the seqno will always be
         * monotonic, even if not coherent.
         */
-       u32             (*get_seqno)(struct intel_ring_buffer *ring,
+       u32             (*get_seqno)(struct intel_engine_cs *ring,
                                     bool lazy_coherency);
-       void            (*set_seqno)(struct intel_ring_buffer *ring,
+       void            (*set_seqno)(struct intel_engine_cs *ring,
                                     u32 seqno);
-       int             (*dispatch_execbuffer)(struct intel_ring_buffer *ring,
-                                              u32 offset, u32 length,
+       int             (*dispatch_execbuffer)(struct intel_engine_cs *ring,
+                                              u64 offset, u32 length,
                                               unsigned flags);
 #define I915_DISPATCH_SECURE 0x1
 #define I915_DISPATCH_PINNED 0x2
-       void            (*cleanup)(struct intel_ring_buffer *ring);
-       int             (*sync_to)(struct intel_ring_buffer *ring,
-                                  struct intel_ring_buffer *to,
-                                  u32 seqno);
+       void            (*cleanup)(struct intel_engine_cs *ring);
 
-       /* our mbox written by others */
-       u32             semaphore_register[I915_NUM_RINGS];
-       /* mboxes this ring signals to */
-       u32             signal_mbox[I915_NUM_RINGS];
+       struct {
+               u32     sync_seqno[I915_NUM_RINGS-1];
+
+               struct {
+                       /* our mbox written by others */
+                       u32             wait[I915_NUM_RINGS];
+                       /* mboxes this ring signals to */
+                       u32             signal[I915_NUM_RINGS];
+               } mbox;
+
+               /* AKA wait() */
+               int     (*sync_to)(struct intel_engine_cs *ring,
+                                  struct intel_engine_cs *to,
+                                  u32 seqno);
+               int     (*signal)(struct intel_engine_cs *signaller,
+                                 /* num_dwords needed by caller */
+                                 unsigned int num_dwords);
+       } semaphore;
 
        /**
         * List of objects currently involved in rendering from the
@@ -153,12 +174,8 @@ struct  intel_ring_buffer {
 
        wait_queue_head_t irq_queue;
 
-       /**
-        * Do an explicit TLB flush before MI_SET_CONTEXT
-        */
-       bool itlb_before_ctx_switch;
-       struct i915_hw_context *default_context;
-       struct i915_hw_context *last_context;
+       struct intel_context *default_context;
+       struct intel_context *last_context;
 
        struct intel_ring_hangcheck hangcheck;
 
@@ -168,12 +185,13 @@ struct  intel_ring_buffer {
                volatile u32 *cpu_page;
        } scratch;
 
+       bool needs_cmd_parser;
+
        /*
-        * Tables of commands the command parser needs to know about
+        * Table of commands the command parser needs to know about
         * for this ring.
         */
-       const struct drm_i915_cmd_table *cmd_tables;
-       int cmd_table_count;
+       DECLARE_HASHTABLE(cmd_hash, I915_CMD_HASH_ORDER);
 
        /*
         * Table of registers allowed in commands that read/write registers.
@@ -202,20 +220,20 @@ struct  intel_ring_buffer {
 };
 
 static inline bool
-intel_ring_initialized(struct intel_ring_buffer *ring)
+intel_ring_initialized(struct intel_engine_cs *ring)
 {
-       return ring->obj != NULL;
+       return ring->buffer && ring->buffer->obj;
 }
 
 static inline unsigned
-intel_ring_flag(struct intel_ring_buffer *ring)
+intel_ring_flag(struct intel_engine_cs *ring)
 {
        return 1 << ring->id;
 }
 
 static inline u32
-intel_ring_sync_index(struct intel_ring_buffer *ring,
-                     struct intel_ring_buffer *other)
+intel_ring_sync_index(struct intel_engine_cs *ring,
+                     struct intel_engine_cs *other)
 {
        int idx;
 
@@ -233,7 +251,7 @@ intel_ring_sync_index(struct intel_ring_buffer *ring,
 }
 
 static inline u32
-intel_read_status_page(struct intel_ring_buffer *ring,
+intel_read_status_page(struct intel_engine_cs *ring,
                       int reg)
 {
        /* Ensure that the compiler doesn't optimize away the load. */
@@ -242,7 +260,7 @@ intel_read_status_page(struct intel_ring_buffer *ring,
 }
 
 static inline void
-intel_write_status_page(struct intel_ring_buffer *ring,
+intel_write_status_page(struct intel_engine_cs *ring,
                        int reg, u32 value)
 {
        ring->status_page.page_addr[reg] = value;
@@ -267,47 +285,51 @@ intel_write_status_page(struct intel_ring_buffer *ring,
 #define I915_GEM_HWS_SCRATCH_INDEX     0x30
 #define I915_GEM_HWS_SCRATCH_ADDR (I915_GEM_HWS_SCRATCH_INDEX << MI_STORE_DWORD_INDEX_SHIFT)
 
-void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring);
+void intel_stop_ring_buffer(struct intel_engine_cs *ring);
+void intel_cleanup_ring_buffer(struct intel_engine_cs *ring);
 
-int __must_check intel_ring_begin(struct intel_ring_buffer *ring, int n);
-int __must_check intel_ring_cacheline_align(struct intel_ring_buffer *ring);
-static inline void intel_ring_emit(struct intel_ring_buffer *ring,
+int __must_check intel_ring_begin(struct intel_engine_cs *ring, int n);
+int __must_check intel_ring_cacheline_align(struct intel_engine_cs *ring);
+static inline void intel_ring_emit(struct intel_engine_cs *ring,
                                   u32 data)
 {
-       iowrite32(data, ring->virtual_start + ring->tail);
-       ring->tail += 4;
+       struct intel_ringbuffer *ringbuf = ring->buffer;
+       iowrite32(data, ringbuf->virtual_start + ringbuf->tail);
+       ringbuf->tail += 4;
 }
-static inline void intel_ring_advance(struct intel_ring_buffer *ring)
+static inline void intel_ring_advance(struct intel_engine_cs *ring)
 {
-       ring->tail &= ring->size - 1;
+       struct intel_ringbuffer *ringbuf = ring->buffer;
+       ringbuf->tail &= ringbuf->size - 1;
 }
-void __intel_ring_advance(struct intel_ring_buffer *ring);
+void __intel_ring_advance(struct intel_engine_cs *ring);
 
-int __must_check intel_ring_idle(struct intel_ring_buffer *ring);
-void intel_ring_init_seqno(struct intel_ring_buffer *ring, u32 seqno);
-int intel_ring_flush_all_caches(struct intel_ring_buffer *ring);
-int intel_ring_invalidate_all_caches(struct intel_ring_buffer *ring);
+int __must_check intel_ring_idle(struct intel_engine_cs *ring);
+void intel_ring_init_seqno(struct intel_engine_cs *ring, u32 seqno);
+int intel_ring_flush_all_caches(struct intel_engine_cs *ring);
+int intel_ring_invalidate_all_caches(struct intel_engine_cs *ring);
 
 int intel_init_render_ring_buffer(struct drm_device *dev);
 int intel_init_bsd_ring_buffer(struct drm_device *dev);
+int intel_init_bsd2_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);
 
-u64 intel_ring_get_active_head(struct intel_ring_buffer *ring);
-void intel_ring_setup_status_page(struct intel_ring_buffer *ring);
+u64 intel_ring_get_active_head(struct intel_engine_cs *ring);
+void intel_ring_setup_status_page(struct intel_engine_cs *ring);
 
-static inline u32 intel_ring_get_tail(struct intel_ring_buffer *ring)
+static inline u32 intel_ring_get_tail(struct intel_engine_cs *ring)
 {
-       return ring->tail;
+       return ring->buffer->tail;
 }
 
-static inline u32 intel_ring_get_seqno(struct intel_ring_buffer *ring)
+static inline u32 intel_ring_get_seqno(struct intel_engine_cs *ring)
 {
        BUG_ON(ring->outstanding_lazy_seqno == 0);
        return ring->outstanding_lazy_seqno;
 }
 
-static inline void i915_trace_irq_get(struct intel_ring_buffer *ring, u32 seqno)
+static inline void i915_trace_irq_get(struct intel_engine_cs *ring, u32 seqno)
 {
        if (ring->trace_irq_seqno == 0 && ring->irq_get(ring))
                ring->trace_irq_seqno = seqno;
index 46be00d66df3da3e74597ba9c02f0ac745741751..6a4d5bc17697867c00f34dd9476a294d3145d015 100644 (file)
@@ -1153,20 +1153,21 @@ static bool intel_sdvo_compute_config(struct intel_encoder *encoder,
        pipe_config->pixel_multiplier =
                intel_sdvo_get_pixel_multiplier(adjusted_mode);
 
+       pipe_config->has_hdmi_sink = intel_sdvo->has_hdmi_monitor;
+
        if (intel_sdvo->color_range_auto) {
                /* See CEA-861-E - 5.1 Default Encoding Parameters */
                /* FIXME: This bit is only valid when using TMDS encoding and 8
                 * bit per color mode. */
-               if (intel_sdvo->has_hdmi_monitor &&
+               if (pipe_config->has_hdmi_sink &&
                    drm_match_cea_mode(adjusted_mode) > 1)
-                       intel_sdvo->color_range = HDMI_COLOR_RANGE_16_235;
-               else
-                       intel_sdvo->color_range = 0;
+                       pipe_config->limited_color_range = true;
+       } else {
+               if (pipe_config->has_hdmi_sink &&
+                   intel_sdvo->color_range == HDMI_COLOR_RANGE_16_235)
+                       pipe_config->limited_color_range = true;
        }
 
-       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);
@@ -1174,7 +1175,7 @@ static bool intel_sdvo_compute_config(struct intel_encoder *encoder,
        return true;
 }
 
-static void intel_sdvo_mode_set(struct intel_encoder *intel_encoder)
+static void intel_sdvo_pre_enable(struct intel_encoder *intel_encoder)
 {
        struct drm_device *dev = intel_encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1223,7 +1224,7 @@ static void intel_sdvo_mode_set(struct intel_encoder *intel_encoder)
        if (!intel_sdvo_set_target_input(intel_sdvo))
                return;
 
-       if (intel_sdvo->has_hdmi_monitor) {
+       if (crtc->config.has_hdmi_sink) {
                intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_HDMI);
                intel_sdvo_set_colorimetry(intel_sdvo,
                                           SDVO_COLORIMETRY_RGB256);
@@ -1258,8 +1259,8 @@ static void intel_sdvo_mode_set(struct intel_encoder *intel_encoder)
                /* The real mode polarity is set by the SDVO commands, using
                 * struct intel_sdvo_dtd. */
                sdvox = SDVO_VSYNC_ACTIVE_HIGH | SDVO_HSYNC_ACTIVE_HIGH;
-               if (!HAS_PCH_SPLIT(dev) && intel_sdvo->is_hdmi)
-                       sdvox |= intel_sdvo->color_range;
+               if (!HAS_PCH_SPLIT(dev) && crtc->config.limited_color_range)
+                       sdvox |= HDMI_COLOR_RANGE_16_235;
                if (INTEL_INFO(dev)->gen < 5)
                        sdvox |= SDVO_BORDER_ENABLE;
        } else {
@@ -1349,6 +1350,8 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder,
        u8 val;
        bool ret;
 
+       sdvox = I915_READ(intel_sdvo->sdvo_reg);
+
        ret = intel_sdvo_get_input_timing(intel_sdvo, &dtd);
        if (!ret) {
                /* Some sdvo encoders are not spec compliant and don't
@@ -1377,7 +1380,6 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder,
         * 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;
@@ -1406,6 +1408,15 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder,
                }
        }
 
+       if (sdvox & HDMI_COLOR_RANGE_16_235)
+               pipe_config->limited_color_range = true;
+
+       if (intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_ENCODE,
+                                &val, 1)) {
+               if (val == SDVO_ENCODE_HDMI)
+                       pipe_config->has_hdmi_sink = true;
+       }
+
        WARN(encoder_pixel_multiplier != pipe_config->pixel_multiplier,
             "SDVO pixel multiplier mismatch, port: %i, encoder: %i\n",
             pipe_config->pixel_multiplier, encoder_pixel_multiplier);
@@ -1732,7 +1743,7 @@ intel_sdvo_detect(struct drm_connector *connector, bool force)
        enum drm_connector_status ret;
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
-                     connector->base.id, drm_get_connector_name(connector));
+                     connector->base.id, connector->name);
 
        if (!intel_sdvo_get_value(intel_sdvo,
                                  SDVO_CMD_GET_ATTACHED_DISPLAYS,
@@ -1794,7 +1805,7 @@ static void intel_sdvo_get_ddc_modes(struct drm_connector *connector)
        struct edid *edid;
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
-                     connector->base.id, drm_get_connector_name(connector));
+                     connector->base.id, connector->name);
 
        /* set the bus switch and get the modes */
        edid = intel_sdvo_get_edid(connector);
@@ -1892,7 +1903,7 @@ static void intel_sdvo_get_tv_modes(struct drm_connector *connector)
        int i;
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
-                     connector->base.id, drm_get_connector_name(connector));
+                     connector->base.id, connector->name);
 
        /* Read the list of supported input resolutions for the selected TV
         * format.
@@ -1929,7 +1940,7 @@ static void intel_sdvo_get_lvds_modes(struct drm_connector *connector)
        struct drm_display_mode *newmode;
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
-                     connector->base.id, drm_get_connector_name(connector));
+                     connector->base.id, connector->name);
 
        /*
         * Fetch modes from VBT. For SDVO prefer the VBT mode since some
@@ -2999,7 +3010,7 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob)
 
        intel_encoder->compute_config = intel_sdvo_compute_config;
        intel_encoder->disable = intel_disable_sdvo;
-       intel_encoder->mode_set = intel_sdvo_mode_set;
+       intel_encoder->pre_enable = intel_sdvo_pre_enable;
        intel_encoder->enable = intel_enable_sdvo;
        intel_encoder->get_hw_state = intel_sdvo_get_hw_state;
        intel_encoder->get_config = intel_sdvo_get_config;
index 0954f132726ea0ae15593364ef976168fc2c909f..01d841ea3140f701cdd1cef414ad20cbf8de6ee2 100644 (file)
  * IOSF sideband, see VLV2_SidebandMsg_HAS.docx and
  * VLV_VLV2_PUNIT_HAS_0.8.docx
  */
+
+/* Standard MMIO read, non-posted */
+#define SB_MRD_NP      0x00
+/* Standard MMIO write, non-posted */
+#define SB_MWR_NP      0x01
+/* Private register read, double-word addressing, non-posted */
+#define SB_CRRDDA_NP   0x06
+/* Private register write, double-word addressing, non-posted */
+#define SB_CRWRDA_NP   0x07
+
 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);
+       bool is_read = (opcode == SB_MRD_NP || opcode == SB_CRRDDA_NP);
 
        cmd = (devfn << IOSF_DEVFN_SHIFT) | (opcode << IOSF_OPCODE_SHIFT) |
                (port << IOSF_PORT_SHIFT) | (be << IOSF_BYTE_ENABLES_SHIFT) |
@@ -74,7 +83,7 @@ u32 vlv_punit_read(struct drm_i915_private *dev_priv, u8 addr)
 
        mutex_lock(&dev_priv->dpio_lock);
        vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_PUNIT,
-                       PUNIT_OPCODE_REG_READ, addr, &val);
+                       SB_CRRDDA_NP, addr, &val);
        mutex_unlock(&dev_priv->dpio_lock);
 
        return val;
@@ -86,7 +95,7 @@ void vlv_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val)
 
        mutex_lock(&dev_priv->dpio_lock);
        vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_PUNIT,
-                       PUNIT_OPCODE_REG_WRITE, addr, &val);
+                       SB_CRWRDA_NP, addr, &val);
        mutex_unlock(&dev_priv->dpio_lock);
 }
 
@@ -95,7 +104,7 @@ u32 vlv_bunit_read(struct drm_i915_private *dev_priv, u32 reg)
        u32 val = 0;
 
        vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_BUNIT,
-                       PUNIT_OPCODE_REG_READ, reg, &val);
+                       SB_CRRDDA_NP, reg, &val);
 
        return val;
 }
@@ -103,7 +112,7 @@ u32 vlv_bunit_read(struct drm_i915_private *dev_priv, u32 reg)
 void vlv_bunit_write(struct drm_i915_private *dev_priv, u32 reg, u32 val)
 {
        vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_BUNIT,
-                       PUNIT_OPCODE_REG_WRITE, reg, &val);
+                       SB_CRWRDA_NP, reg, &val);
 }
 
 u32 vlv_nc_read(struct drm_i915_private *dev_priv, u8 addr)
@@ -114,7 +123,7 @@ u32 vlv_nc_read(struct drm_i915_private *dev_priv, u8 addr)
 
        mutex_lock(&dev_priv->dpio_lock);
        vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_NC,
-                       PUNIT_OPCODE_REG_READ, addr, &val);
+                       SB_CRRDDA_NP, addr, &val);
        mutex_unlock(&dev_priv->dpio_lock);
 
        return val;
@@ -124,56 +133,56 @@ u32 vlv_gpio_nc_read(struct drm_i915_private *dev_priv, u32 reg)
 {
        u32 val = 0;
        vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_GPIO_NC,
-                       PUNIT_OPCODE_REG_READ, reg, &val);
+                       SB_CRRDDA_NP, reg, &val);
        return val;
 }
 
 void vlv_gpio_nc_write(struct drm_i915_private *dev_priv, u32 reg, u32 val)
 {
        vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_GPIO_NC,
-                       PUNIT_OPCODE_REG_WRITE, reg, &val);
+                       SB_CRWRDA_NP, reg, &val);
 }
 
 u32 vlv_cck_read(struct drm_i915_private *dev_priv, u32 reg)
 {
        u32 val = 0;
        vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_CCK,
-                       PUNIT_OPCODE_REG_READ, reg, &val);
+                       SB_CRRDDA_NP, reg, &val);
        return val;
 }
 
 void vlv_cck_write(struct drm_i915_private *dev_priv, u32 reg, u32 val)
 {
        vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_CCK,
-                       PUNIT_OPCODE_REG_WRITE, reg, &val);
+                       SB_CRWRDA_NP, reg, &val);
 }
 
 u32 vlv_ccu_read(struct drm_i915_private *dev_priv, u32 reg)
 {
        u32 val = 0;
        vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_CCU,
-                       PUNIT_OPCODE_REG_READ, reg, &val);
+                       SB_CRRDDA_NP, reg, &val);
        return val;
 }
 
 void vlv_ccu_write(struct drm_i915_private *dev_priv, u32 reg, u32 val)
 {
        vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_CCU,
-                       PUNIT_OPCODE_REG_WRITE, reg, &val);
+                       SB_CRWRDA_NP, reg, &val);
 }
 
 u32 vlv_gps_core_read(struct drm_i915_private *dev_priv, u32 reg)
 {
        u32 val = 0;
        vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_GPS_CORE,
-                       PUNIT_OPCODE_REG_READ, reg, &val);
+                       SB_CRRDDA_NP, reg, &val);
        return val;
 }
 
 void vlv_gps_core_write(struct drm_i915_private *dev_priv, u32 reg, u32 val)
 {
        vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_GPS_CORE,
-                       PUNIT_OPCODE_REG_WRITE, reg, &val);
+                       SB_CRWRDA_NP, reg, &val);
 }
 
 u32 vlv_dpio_read(struct drm_i915_private *dev_priv, enum pipe pipe, int reg)
@@ -181,14 +190,22 @@ u32 vlv_dpio_read(struct drm_i915_private *dev_priv, enum pipe pipe, int reg)
        u32 val = 0;
 
        vlv_sideband_rw(dev_priv, DPIO_DEVFN, DPIO_PHY_IOSF_PORT(DPIO_PHY(pipe)),
-                       DPIO_OPCODE_REG_READ, reg, &val);
+                       SB_MRD_NP, reg, &val);
+
+       /*
+        * FIXME: There might be some registers where all 1's is a valid value,
+        * so ideally we should check the register offset instead...
+        */
+       WARN(val == 0xffffffff, "DPIO read pipe %c reg 0x%x == 0x%x\n",
+            pipe_name(pipe), reg, val);
+
        return val;
 }
 
 void vlv_dpio_write(struct drm_i915_private *dev_priv, enum pipe pipe, int reg, u32 val)
 {
        vlv_sideband_rw(dev_priv, DPIO_DEVFN, DPIO_PHY_IOSF_PORT(DPIO_PHY(pipe)),
-                       DPIO_OPCODE_REG_WRITE, reg, &val);
+                       SB_MWR_NP, reg, &val);
 }
 
 /* SBI access */
@@ -253,13 +270,13 @@ void intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value,
 u32 vlv_flisdsi_read(struct drm_i915_private *dev_priv, u32 reg)
 {
        u32 val = 0;
-       vlv_sideband_rw(dev_priv, DPIO_DEVFN, IOSF_PORT_FLISDSI,
-                                       DPIO_OPCODE_REG_READ, reg, &val);
+       vlv_sideband_rw(dev_priv, DPIO_DEVFN, IOSF_PORT_FLISDSI, SB_CRRDDA_NP,
+                       reg, &val);
        return val;
 }
 
 void vlv_flisdsi_write(struct drm_i915_private *dev_priv, u32 reg, u32 val)
 {
-       vlv_sideband_rw(dev_priv, DPIO_DEVFN, IOSF_PORT_FLISDSI,
-                                       DPIO_OPCODE_REG_WRITE, reg, &val);
+       vlv_sideband_rw(dev_priv, DPIO_DEVFN, IOSF_PORT_FLISDSI, SB_CRWRDA_NP,
+                       reg, &val);
 }
index 336ae6c602f2a6855e58c782a68debf350f49807..1b66ddcdfb331cdcb83dd9558ce494041cebc2b3 100644 (file)
 #include <drm/i915_drm.h>
 #include "i915_drv.h"
 
+static int usecs_to_scanlines(const struct drm_display_mode *mode, int usecs)
+{
+       /* paranoia */
+       if (!mode->crtc_htotal)
+               return 1;
+
+       return DIV_ROUND_UP(usecs * mode->crtc_clock, 1000 * mode->crtc_htotal);
+}
+
+static bool intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl_count)
+{
+       struct drm_device *dev = crtc->base.dev;
+       const struct drm_display_mode *mode = &crtc->config.adjusted_mode;
+       enum pipe pipe = crtc->pipe;
+       long timeout = msecs_to_jiffies_timeout(1);
+       int scanline, min, max, vblank_start;
+       DEFINE_WAIT(wait);
+
+       WARN_ON(!drm_modeset_is_locked(&crtc->base.mutex));
+
+       vblank_start = mode->crtc_vblank_start;
+       if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+               vblank_start = DIV_ROUND_UP(vblank_start, 2);
+
+       /* FIXME needs to be calibrated sensibly */
+       min = vblank_start - usecs_to_scanlines(mode, 100);
+       max = vblank_start - 1;
+
+       if (min <= 0 || max <= 0)
+               return false;
+
+       if (WARN_ON(drm_vblank_get(dev, pipe)))
+               return false;
+
+       local_irq_disable();
+
+       trace_i915_pipe_update_start(crtc, min, max);
+
+       for (;;) {
+               /*
+                * prepare_to_wait() has a memory barrier, which guarantees
+                * other CPUs can see the task state update by the time we
+                * read the scanline.
+                */
+               prepare_to_wait(&crtc->vbl_wait, &wait, TASK_UNINTERRUPTIBLE);
+
+               scanline = intel_get_crtc_scanline(crtc);
+               if (scanline < min || scanline > max)
+                       break;
+
+               if (timeout <= 0) {
+                       DRM_ERROR("Potential atomic update failure on pipe %c\n",
+                                 pipe_name(crtc->pipe));
+                       break;
+               }
+
+               local_irq_enable();
+
+               timeout = schedule_timeout(timeout);
+
+               local_irq_disable();
+       }
+
+       finish_wait(&crtc->vbl_wait, &wait);
+
+       drm_vblank_put(dev, pipe);
+
+       *start_vbl_count = dev->driver->get_vblank_counter(dev, pipe);
+
+       trace_i915_pipe_update_vblank_evaded(crtc, min, max, *start_vbl_count);
+
+       return true;
+}
+
+static void intel_pipe_update_end(struct intel_crtc *crtc, u32 start_vbl_count)
+{
+       struct drm_device *dev = crtc->base.dev;
+       enum pipe pipe = crtc->pipe;
+       u32 end_vbl_count = dev->driver->get_vblank_counter(dev, pipe);
+
+       trace_i915_pipe_update_end(crtc, end_vbl_count);
+
+       local_irq_enable();
+
+       if (start_vbl_count != end_vbl_count)
+               DRM_ERROR("Atomic update failure on pipe %c (start=%u end=%u)\n",
+                         pipe_name(pipe), start_vbl_count, end_vbl_count);
+}
+
+static void intel_update_primary_plane(struct intel_crtc *crtc)
+{
+       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+       int reg = DSPCNTR(crtc->plane);
+
+       if (crtc->primary_enabled)
+               I915_WRITE(reg, I915_READ(reg) | DISPLAY_PLANE_ENABLE);
+       else
+               I915_WRITE(reg, I915_READ(reg) & ~DISPLAY_PLANE_ENABLE);
+}
+
 static void
 vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
                 struct drm_framebuffer *fb,
@@ -48,11 +148,14 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
        struct drm_device *dev = dplane->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_plane *intel_plane = to_intel_plane(dplane);
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int pipe = intel_plane->pipe;
        int plane = intel_plane->plane;
        u32 sprctl;
        unsigned long sprsurf_offset, linear_offset;
        int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
+       u32 start_vbl_count;
+       bool atomic_update;
 
        sprctl = I915_READ(SPCNTR(pipe, plane));
 
@@ -131,6 +234,10 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
                                                        fb->pitches[0]);
        linear_offset -= sprsurf_offset;
 
+       atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
+
+       intel_update_primary_plane(intel_crtc);
+
        I915_WRITE(SPSTRIDE(pipe, plane), fb->pitches[0]);
        I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x);
 
@@ -143,7 +250,11 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
        I915_WRITE(SPCNTR(pipe, plane), sprctl);
        I915_WRITE(SPSURF(pipe, plane), i915_gem_obj_ggtt_offset(obj) +
                   sprsurf_offset);
-       POSTING_READ(SPSURF(pipe, plane));
+
+       intel_flush_primary_plane(dev_priv, intel_crtc->plane);
+
+       if (atomic_update)
+               intel_pipe_update_end(intel_crtc, start_vbl_count);
 }
 
 static void
@@ -152,14 +263,25 @@ vlv_disable_plane(struct drm_plane *dplane, struct drm_crtc *crtc)
        struct drm_device *dev = dplane->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_plane *intel_plane = to_intel_plane(dplane);
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int pipe = intel_plane->pipe;
        int plane = intel_plane->plane;
+       u32 start_vbl_count;
+       bool atomic_update;
+
+       atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
+
+       intel_update_primary_plane(intel_crtc);
 
        I915_WRITE(SPCNTR(pipe, plane), I915_READ(SPCNTR(pipe, plane)) &
                   ~SP_ENABLE);
        /* Activate double buffered register update */
        I915_WRITE(SPSURF(pipe, plane), 0);
-       POSTING_READ(SPSURF(pipe, plane));
+
+       intel_flush_primary_plane(dev_priv, intel_crtc->plane);
+
+       if (atomic_update)
+               intel_pipe_update_end(intel_crtc, start_vbl_count);
 
        intel_update_sprite_watermarks(dplane, crtc, 0, 0, false, false);
 }
@@ -226,10 +348,13 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
        struct drm_device *dev = plane->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_plane *intel_plane = to_intel_plane(plane);
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int pipe = intel_plane->pipe;
        u32 sprctl, sprscale = 0;
        unsigned long sprsurf_offset, linear_offset;
        int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
+       u32 start_vbl_count;
+       bool atomic_update;
 
        sprctl = I915_READ(SPRCTL(pipe));
 
@@ -299,6 +424,10 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
                                               pixel_size, fb->pitches[0]);
        linear_offset -= sprsurf_offset;
 
+       atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
+
+       intel_update_primary_plane(intel_crtc);
+
        I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]);
        I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x);
 
@@ -317,7 +446,11 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
        I915_WRITE(SPRCTL(pipe), sprctl);
        I915_WRITE(SPRSURF(pipe),
                   i915_gem_obj_ggtt_offset(obj) + sprsurf_offset);
-       POSTING_READ(SPRSURF(pipe));
+
+       intel_flush_primary_plane(dev_priv, intel_crtc->plane);
+
+       if (atomic_update)
+               intel_pipe_update_end(intel_crtc, start_vbl_count);
 }
 
 static void
@@ -326,7 +459,14 @@ ivb_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
        struct drm_device *dev = plane->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_plane *intel_plane = to_intel_plane(plane);
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int pipe = intel_plane->pipe;
+       u32 start_vbl_count;
+       bool atomic_update;
+
+       atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
+
+       intel_update_primary_plane(intel_crtc);
 
        I915_WRITE(SPRCTL(pipe), I915_READ(SPRCTL(pipe)) & ~SPRITE_ENABLE);
        /* Can't leave the scaler enabled... */
@@ -334,7 +474,11 @@ ivb_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
                I915_WRITE(SPRSCALE(pipe), 0);
        /* Activate double buffered register update */
        I915_WRITE(SPRSURF(pipe), 0);
-       POSTING_READ(SPRSURF(pipe));
+
+       intel_flush_primary_plane(dev_priv, intel_crtc->plane);
+
+       if (atomic_update)
+               intel_pipe_update_end(intel_crtc, start_vbl_count);
 
        /*
         * Avoid underruns when disabling the sprite.
@@ -410,10 +554,13 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
        struct drm_device *dev = plane->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_plane *intel_plane = to_intel_plane(plane);
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int pipe = intel_plane->pipe;
        unsigned long dvssurf_offset, linear_offset;
        u32 dvscntr, dvsscale;
        int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
+       u32 start_vbl_count;
+       bool atomic_update;
 
        dvscntr = I915_READ(DVSCNTR(pipe));
 
@@ -478,6 +625,10 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
                                               pixel_size, fb->pitches[0]);
        linear_offset -= dvssurf_offset;
 
+       atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
+
+       intel_update_primary_plane(intel_crtc);
+
        I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]);
        I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x);
 
@@ -491,7 +642,11 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
        I915_WRITE(DVSCNTR(pipe), dvscntr);
        I915_WRITE(DVSSURF(pipe),
                   i915_gem_obj_ggtt_offset(obj) + dvssurf_offset);
-       POSTING_READ(DVSSURF(pipe));
+
+       intel_flush_primary_plane(dev_priv, intel_crtc->plane);
+
+       if (atomic_update)
+               intel_pipe_update_end(intel_crtc, start_vbl_count);
 }
 
 static void
@@ -500,14 +655,25 @@ ilk_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
        struct drm_device *dev = plane->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_plane *intel_plane = to_intel_plane(plane);
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int pipe = intel_plane->pipe;
+       u32 start_vbl_count;
+       bool atomic_update;
+
+       atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
+
+       intel_update_primary_plane(intel_crtc);
 
        I915_WRITE(DVSCNTR(pipe), I915_READ(DVSCNTR(pipe)) & ~DVS_ENABLE);
        /* Disable the scaler */
        I915_WRITE(DVSSCALE(pipe), 0);
        /* Flush double buffered register updates */
        I915_WRITE(DVSSURF(pipe), 0);
-       POSTING_READ(DVSSURF(pipe));
+
+       intel_flush_primary_plane(dev_priv, intel_crtc->plane);
+
+       if (atomic_update)
+               intel_pipe_update_end(intel_crtc, start_vbl_count);
 
        /*
         * Avoid underruns when disabling the sprite.
@@ -519,20 +685,10 @@ ilk_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
 }
 
 static void
-intel_enable_primary(struct drm_crtc *crtc)
+intel_post_enable_primary(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 reg = DSPCNTR(intel_crtc->plane);
-
-       if (intel_crtc->primary_enabled)
-               return;
-
-       intel_crtc->primary_enabled = true;
-
-       I915_WRITE(reg, I915_READ(reg) | DISPLAY_PLANE_ENABLE);
-       intel_flush_primary_plane(dev_priv, intel_crtc->plane);
 
        /*
         * FIXME IPS should be fine as long as one plane is
@@ -540,10 +696,7 @@ intel_enable_primary(struct drm_crtc *crtc)
         * when going from primary only to sprite only and vice
         * versa.
         */
-       if (intel_crtc->config.ips_enabled) {
-               intel_wait_for_vblank(dev, intel_crtc->pipe);
-               hsw_enable_ips(intel_crtc);
-       }
+       hsw_enable_ips(intel_crtc);
 
        mutex_lock(&dev->struct_mutex);
        intel_update_fbc(dev);
@@ -551,17 +704,11 @@ intel_enable_primary(struct drm_crtc *crtc)
 }
 
 static void
-intel_disable_primary(struct drm_crtc *crtc)
+intel_pre_disable_primary(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 reg = DSPCNTR(intel_crtc->plane);
-
-       if (!intel_crtc->primary_enabled)
-               return;
-
-       intel_crtc->primary_enabled = false;
 
        mutex_lock(&dev->struct_mutex);
        if (dev_priv->fbc.plane == intel_crtc->plane)
@@ -575,9 +722,6 @@ intel_disable_primary(struct drm_crtc *crtc)
         * versa.
         */
        hsw_disable_ips(intel_crtc);
-
-       I915_WRITE(reg, I915_READ(reg) & ~DISPLAY_PLANE_ENABLE);
-       intel_flush_primary_plane(dev_priv, intel_crtc->plane);
 }
 
 static int
@@ -671,7 +815,7 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
        struct drm_i915_gem_object *obj = intel_fb->obj;
        struct drm_i915_gem_object *old_obj = intel_plane->obj;
        int ret;
-       bool disable_primary = false;
+       bool primary_enabled;
        bool visible;
        int hscale, vscale;
        int max_scale, min_scale;
@@ -842,8 +986,8 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
         * If the sprite is completely covering the primary plane,
         * we can disable the primary and save power.
         */
-       disable_primary = drm_rect_equals(&dst, &clip) && !colorkey_enabled(intel_plane);
-       WARN_ON(disable_primary && !visible && intel_crtc->active);
+       primary_enabled = !drm_rect_equals(&dst, &clip) || colorkey_enabled(intel_plane);
+       WARN_ON(!primary_enabled && !visible && intel_crtc->active);
 
        mutex_lock(&dev->struct_mutex);
 
@@ -870,12 +1014,15 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
        intel_plane->obj = obj;
 
        if (intel_crtc->active) {
-               /*
-                * Be sure to re-enable the primary before the sprite is no longer
-                * covering it fully.
-                */
-               if (!disable_primary)
-                       intel_enable_primary(crtc);
+               bool primary_was_enabled = intel_crtc->primary_enabled;
+
+               intel_crtc->primary_enabled = primary_enabled;
+
+               if (primary_was_enabled != primary_enabled)
+                       intel_crtc_wait_for_pending_flips(crtc);
+
+               if (primary_was_enabled && !primary_enabled)
+                       intel_pre_disable_primary(crtc);
 
                if (visible)
                        intel_plane->update_plane(plane, crtc, fb, obj,
@@ -884,8 +1031,8 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
                else
                        intel_plane->disable_plane(plane, crtc);
 
-               if (disable_primary)
-                       intel_disable_primary(crtc);
+               if (!primary_was_enabled && primary_enabled)
+                       intel_post_enable_primary(crtc);
        }
 
        /* Unpin old obj after new one is active to avoid ugliness */
@@ -923,8 +1070,14 @@ intel_disable_plane(struct drm_plane *plane)
        intel_crtc = to_intel_crtc(plane->crtc);
 
        if (intel_crtc->active) {
-               intel_enable_primary(plane->crtc);
+               bool primary_was_enabled = intel_crtc->primary_enabled;
+
+               intel_crtc->primary_enabled = true;
+
                intel_plane->disable_plane(plane, plane->crtc);
+
+               if (!primary_was_enabled && intel_crtc->primary_enabled)
+                       intel_post_enable_primary(plane->crtc);
        }
 
        if (intel_plane->obj) {
index bafe92e317d5d24c103bc83ea93a846cfadf19d0..67c6c9a2eb1c9f3ed3d0d6979a686b52fdabe9fe 100644 (file)
@@ -934,7 +934,86 @@ intel_tv_compute_config(struct intel_encoder *encoder,
        return true;
 }
 
-static void intel_tv_mode_set(struct intel_encoder *encoder)
+static void
+set_tv_mode_timings(struct drm_i915_private *dev_priv,
+                   const struct tv_mode *tv_mode,
+                   bool burst_ena)
+{
+       u32 hctl1, hctl2, hctl3;
+       u32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7;
+
+       hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) |
+               (tv_mode->htotal << TV_HTOTAL_SHIFT);
+
+       hctl2 = (tv_mode->hburst_start << 16) |
+               (tv_mode->hburst_len << TV_HBURST_LEN_SHIFT);
+
+       if (burst_ena)
+               hctl2 |= TV_BURST_ENA;
+
+       hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) |
+               (tv_mode->hblank_end << TV_HBLANK_END_SHIFT);
+
+       vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) |
+               (tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) |
+               (tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT);
+
+       vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) |
+               (tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) |
+               (tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT);
+
+       vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) |
+               (tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) |
+               (tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT);
+
+       if (tv_mode->veq_ena)
+               vctl3 |= TV_EQUAL_ENA;
+
+       vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) |
+               (tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT);
+
+       vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) |
+               (tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT);
+
+       vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) |
+               (tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT);
+
+       vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) |
+               (tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT);
+
+       I915_WRITE(TV_H_CTL_1, hctl1);
+       I915_WRITE(TV_H_CTL_2, hctl2);
+       I915_WRITE(TV_H_CTL_3, hctl3);
+       I915_WRITE(TV_V_CTL_1, vctl1);
+       I915_WRITE(TV_V_CTL_2, vctl2);
+       I915_WRITE(TV_V_CTL_3, vctl3);
+       I915_WRITE(TV_V_CTL_4, vctl4);
+       I915_WRITE(TV_V_CTL_5, vctl5);
+       I915_WRITE(TV_V_CTL_6, vctl6);
+       I915_WRITE(TV_V_CTL_7, vctl7);
+}
+
+static void set_color_conversion(struct drm_i915_private *dev_priv,
+                                const struct color_conversion *color_conversion)
+{
+       if (!color_conversion)
+               return;
+
+       I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) |
+                  color_conversion->gy);
+       I915_WRITE(TV_CSC_Y2, (color_conversion->by << 16) |
+                  color_conversion->ay);
+       I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) |
+                  color_conversion->gu);
+       I915_WRITE(TV_CSC_U2, (color_conversion->bu << 16) |
+                  color_conversion->au);
+       I915_WRITE(TV_CSC_V, (color_conversion->rv << 16) |
+                  color_conversion->gv);
+       I915_WRITE(TV_CSC_V2, (color_conversion->bv << 16) |
+                  color_conversion->av);
+}
+
+static void intel_tv_pre_enable(struct intel_encoder *encoder)
 {
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -942,14 +1021,13 @@ static void intel_tv_mode_set(struct intel_encoder *encoder)
        struct intel_tv *intel_tv = enc_to_tv(encoder);
        const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
        u32 tv_ctl;
-       u32 hctl1, hctl2, hctl3;
-       u32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7;
        u32 scctl1, scctl2, scctl3;
        int i, j;
        const struct video_levels *video_levels;
        const struct color_conversion *color_conversion;
        bool burst_ena;
-       int pipe = intel_crtc->pipe;
+       int xpos = 0x0, ypos = 0x0;
+       unsigned int xsize, ysize;
 
        if (!tv_mode)
                return; /* can't happen (mode_prepare prevents this) */
@@ -982,44 +1060,6 @@ static void intel_tv_mode_set(struct intel_encoder *encoder)
                burst_ena = tv_mode->burst_ena;
                break;
        }
-       hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) |
-               (tv_mode->htotal << TV_HTOTAL_SHIFT);
-
-       hctl2 = (tv_mode->hburst_start << 16) |
-               (tv_mode->hburst_len << TV_HBURST_LEN_SHIFT);
-
-       if (burst_ena)
-               hctl2 |= TV_BURST_ENA;
-
-       hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) |
-               (tv_mode->hblank_end << TV_HBLANK_END_SHIFT);
-
-       vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) |
-               (tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) |
-               (tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT);
-
-       vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) |
-               (tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) |
-               (tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT);
-
-       vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) |
-               (tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) |
-               (tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT);
-
-       if (tv_mode->veq_ena)
-               vctl3 |= TV_EQUAL_ENA;
-
-       vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) |
-               (tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT);
-
-       vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) |
-               (tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT);
-
-       vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) |
-               (tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT);
-
-       vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) |
-               (tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT);
 
        if (intel_crtc->pipe == 1)
                tv_ctl |= TV_ENC_PIPEB_SELECT;
@@ -1051,37 +1091,16 @@ static void intel_tv_mode_set(struct intel_encoder *encoder)
                tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT;
 
        /* Enable two fixes for the chips that need them. */
-       if (dev->pdev->device < 0x2772)
+       if (IS_I915GM(dev))
                tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX;
 
-       I915_WRITE(TV_H_CTL_1, hctl1);
-       I915_WRITE(TV_H_CTL_2, hctl2);
-       I915_WRITE(TV_H_CTL_3, hctl3);
-       I915_WRITE(TV_V_CTL_1, vctl1);
-       I915_WRITE(TV_V_CTL_2, vctl2);
-       I915_WRITE(TV_V_CTL_3, vctl3);
-       I915_WRITE(TV_V_CTL_4, vctl4);
-       I915_WRITE(TV_V_CTL_5, vctl5);
-       I915_WRITE(TV_V_CTL_6, vctl6);
-       I915_WRITE(TV_V_CTL_7, vctl7);
+       set_tv_mode_timings(dev_priv, tv_mode, burst_ena);
+
        I915_WRITE(TV_SC_CTL_1, scctl1);
        I915_WRITE(TV_SC_CTL_2, scctl2);
        I915_WRITE(TV_SC_CTL_3, scctl3);
 
-       if (color_conversion) {
-               I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) |
-                          color_conversion->gy);
-               I915_WRITE(TV_CSC_Y2, (color_conversion->by << 16) |
-                          color_conversion->ay);
-               I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) |
-                          color_conversion->gu);
-               I915_WRITE(TV_CSC_U2, (color_conversion->bu << 16) |
-                          color_conversion->au);
-               I915_WRITE(TV_CSC_V, (color_conversion->rv << 16) |
-                          color_conversion->gv);
-               I915_WRITE(TV_CSC_V2, (color_conversion->bv << 16) |
-                          color_conversion->av);
-       }
+       set_color_conversion(dev_priv, color_conversion);
 
        if (INTEL_INFO(dev)->gen >= 4)
                I915_WRITE(TV_CLR_KNOBS, 0x00404000);
@@ -1092,46 +1111,25 @@ static void intel_tv_mode_set(struct intel_encoder *encoder)
                I915_WRITE(TV_CLR_LEVEL,
                           ((video_levels->black << TV_BLACK_LEVEL_SHIFT) |
                            (video_levels->blank << TV_BLANK_LEVEL_SHIFT)));
-       {
-               int pipeconf_reg = PIPECONF(pipe);
-               int dspcntr_reg = DSPCNTR(intel_crtc->plane);
-               int pipeconf = I915_READ(pipeconf_reg);
-               int dspcntr = I915_READ(dspcntr_reg);
-               int xpos = 0x0, ypos = 0x0;
-               unsigned int xsize, ysize;
-               /* Pipe must be off here */
-               I915_WRITE(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE);
-               intel_flush_primary_plane(dev_priv, intel_crtc->plane);
-
-               /* Wait for vblank for the disable to take effect */
-               if (IS_GEN2(dev))
-                       intel_wait_for_vblank(dev, intel_crtc->pipe);
-
-               I915_WRITE(pipeconf_reg, pipeconf & ~PIPECONF_ENABLE);
-               /* Wait for vblank for the disable to take effect. */
-               intel_wait_for_pipe_off(dev, intel_crtc->pipe);
-
-               /* Filter ctl must be set before TV_WIN_SIZE */
-               I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE);
-               xsize = tv_mode->hblank_start - tv_mode->hblank_end;
-               if (tv_mode->progressive)
-                       ysize = tv_mode->nbr_end + 1;
-               else
-                       ysize = 2*tv_mode->nbr_end + 1;
-
-               xpos += intel_tv->margin[TV_MARGIN_LEFT];
-               ypos += intel_tv->margin[TV_MARGIN_TOP];
-               xsize -= (intel_tv->margin[TV_MARGIN_LEFT] +
-                         intel_tv->margin[TV_MARGIN_RIGHT]);
-               ysize -= (intel_tv->margin[TV_MARGIN_TOP] +
-                         intel_tv->margin[TV_MARGIN_BOTTOM]);
-               I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos);
-               I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize);
-
-               I915_WRITE(pipeconf_reg, pipeconf);
-               I915_WRITE(dspcntr_reg, dspcntr);
-               intel_flush_primary_plane(dev_priv, intel_crtc->plane);
-       }
+
+       assert_pipe_disabled(dev_priv, intel_crtc->pipe);
+
+       /* Filter ctl must be set before TV_WIN_SIZE */
+       I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE);
+       xsize = tv_mode->hblank_start - tv_mode->hblank_end;
+       if (tv_mode->progressive)
+               ysize = tv_mode->nbr_end + 1;
+       else
+               ysize = 2*tv_mode->nbr_end + 1;
+
+       xpos += intel_tv->margin[TV_MARGIN_LEFT];
+       ypos += intel_tv->margin[TV_MARGIN_TOP];
+       xsize -= (intel_tv->margin[TV_MARGIN_LEFT] +
+                 intel_tv->margin[TV_MARGIN_RIGHT]);
+       ysize -= (intel_tv->margin[TV_MARGIN_TOP] +
+                 intel_tv->margin[TV_MARGIN_BOTTOM]);
+       I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos);
+       I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize);
 
        j = 0;
        for (i = 0; i < 60; i++)
@@ -1316,17 +1314,18 @@ intel_tv_detect(struct drm_connector *connector, bool force)
        int type;
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force=%d\n",
-                     connector->base.id, drm_get_connector_name(connector),
+                     connector->base.id, connector->name,
                      force);
 
        mode = reported_modes[0];
 
        if (force) {
                struct intel_load_detect_pipe tmp;
+               struct drm_modeset_acquire_ctx ctx;
 
-               if (intel_get_load_detect_pipe(connector, &mode, &tmp)) {
+               if (intel_get_load_detect_pipe(connector, &mode, &tmp, &ctx)) {
                        type = intel_tv_detect_type(intel_tv, connector);
-                       intel_release_load_detect_pipe(connector, &tmp);
+                       intel_release_load_detect_pipe(connector, &tmp, &ctx);
                } else
                        return connector_status_unknown;
        } else
@@ -1634,7 +1633,7 @@ intel_tv_init(struct drm_device *dev)
 
        intel_encoder->compute_config = intel_tv_compute_config;
        intel_encoder->get_config = intel_tv_get_config;
-       intel_encoder->mode_set = intel_tv_mode_set;
+       intel_encoder->pre_enable = intel_tv_pre_enable;
        intel_encoder->enable = intel_enable_tv;
        intel_encoder->disable = intel_disable_tv;
        intel_encoder->get_hw_state = intel_tv_get_hw_state;
index d0c75779d3f6f91e9cc98b0f1a853344783fdb48..79cba593df0d33dd1bb1fe6732e94d62ec39fe73 100644 (file)
@@ -255,8 +255,7 @@ static void __vlv_force_wake_put(struct drm_i915_private *dev_priv,
 
 }
 
-void vlv_force_wake_get(struct drm_i915_private *dev_priv,
-                                               int fw_engine)
+static void vlv_force_wake_get(struct drm_i915_private *dev_priv, int fw_engine)
 {
        unsigned long irqflags;
 
@@ -275,8 +274,7 @@ void vlv_force_wake_get(struct drm_i915_private *dev_priv,
        spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
 }
 
-void vlv_force_wake_put(struct drm_i915_private *dev_priv,
-                                               int fw_engine)
+static void vlv_force_wake_put(struct drm_i915_private *dev_priv, int fw_engine)
 {
        unsigned long irqflags;
 
@@ -374,7 +372,7 @@ void intel_uncore_early_sanitize(struct drm_device *dev)
        if (HAS_FPGA_DBG_UNCLAIMED(dev))
                __raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
 
-       if (IS_HASWELL(dev) &&
+       if ((IS_HASWELL(dev) || IS_BROADWELL(dev)) &&
            (__raw_i915_read32(dev_priv, HSW_EDRAM_PRESENT) == 1)) {
                /* The docs do not explain exactly how the calculation can be
                 * made. It is somewhat guessable, but for now, it's always
@@ -395,26 +393,8 @@ void intel_uncore_early_sanitize(struct drm_device *dev)
 
 void intel_uncore_sanitize(struct drm_device *dev)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 reg_val;
-
        /* BIOS often leaves RC6 enabled, but disable it for hw init */
        intel_disable_gt_powersave(dev);
-
-       /* Turn off power gate, require especially for the BIOS less system */
-       if (IS_VALLEYVIEW(dev)) {
-
-               mutex_lock(&dev_priv->rps.hw_lock);
-               reg_val = vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_STATUS);
-
-               if (reg_val & (PUNIT_PWRGT_PWR_GATE(PUNIT_POWER_WELL_RENDER) |
-                              PUNIT_PWRGT_PWR_GATE(PUNIT_POWER_WELL_MEDIA) |
-                              PUNIT_PWRGT_PWR_GATE(PUNIT_POWER_WELL_DISP2D)))
-                       vlv_punit_write(dev_priv, PUNIT_REG_PWRGT_CTRL, 0x0);
-
-               mutex_unlock(&dev_priv->rps.hw_lock);
-
-       }
 }
 
 /*
@@ -488,6 +468,17 @@ void assert_force_wake_inactive(struct drm_i915_private *dev_priv)
 #define NEEDS_FORCE_WAKE(dev_priv, reg) \
         ((reg) < 0x40000 && (reg) != FORCEWAKE)
 
+#define FORCEWAKE_VLV_RENDER_RANGE_OFFSET(reg) \
+       (((reg) >= 0x2000 && (reg) < 0x4000) ||\
+       ((reg) >= 0x5000 && (reg) < 0x8000) ||\
+       ((reg) >= 0xB000 && (reg) < 0x12000) ||\
+       ((reg) >= 0x2E000 && (reg) < 0x30000))
+
+#define FORCEWAKE_VLV_MEDIA_RANGE_OFFSET(reg)\
+       (((reg) >= 0x12000 && (reg) < 0x14000) ||\
+       ((reg) >= 0x22000 && (reg) < 0x24000) ||\
+       ((reg) >= 0x30000 && (reg) < 0x40000))
+
 static void
 ilk_dummy_write(struct drm_i915_private *dev_priv)
 {
@@ -854,12 +845,15 @@ void intel_uncore_fini(struct drm_device *dev)
        intel_uncore_forcewake_reset(dev, false);
 }
 
+#define GEN_RANGE(l, h) GENMASK(h, l)
+
 static const struct register_whitelist {
        uint64_t offset;
        uint32_t size;
-       uint32_t gen_bitmask; /* support gens, 0x10 for 4, 0x30 for 4 and 5, etc. */
+       /* supported gens, 0x10 for 4, 0x30 for 4 and 5, etc. */
+       uint32_t gen_bitmask;
 } whitelist[] = {
-       { RING_TIMESTAMP(RENDER_RING_BASE), 8, 0x1F0 },
+       { RING_TIMESTAMP(RENDER_RING_BASE), 8, GEN_RANGE(4, 8) },
 };
 
 int i915_reg_read_ioctl(struct drm_device *dev,
@@ -911,7 +905,7 @@ int i915_get_reset_stats_ioctl(struct drm_device *dev,
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_reset_stats *args = data;
        struct i915_ctx_hang_stats *hs;
-       struct i915_hw_context *ctx;
+       struct intel_context *ctx;
        int ret;
 
        if (args->flags || args->pad)
@@ -955,6 +949,9 @@ static int i965_do_reset(struct drm_device *dev)
 {
        int ret;
 
+       /* FIXME: i965g/gm need a display save/restore for gpu reset. */
+       return -ENODEV;
+
        /*
         * Set the domains we want to reset (GRDOM/bits 2 and 3) as
         * well as the reset bit (GR/bit 0).  Setting the GR bit
@@ -966,7 +963,6 @@ static int i965_do_reset(struct drm_device *dev)
        if (ret)
                return ret;
 
-       /* We can't reset render&media without also resetting display ... */
        pci_write_config_byte(dev->pdev, I965_GDRST,
                              GRDOM_MEDIA | GRDOM_RESET_ENABLE);
 
@@ -979,26 +975,58 @@ static int i965_do_reset(struct drm_device *dev)
        return 0;
 }
 
+static int g4x_do_reset(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int ret;
+
+       pci_write_config_byte(dev->pdev, I965_GDRST,
+                             GRDOM_RENDER | GRDOM_RESET_ENABLE);
+       ret =  wait_for(i965_reset_complete(dev), 500);
+       if (ret)
+               return ret;
+
+       /* WaVcpClkGateDisableForMediaReset:ctg,elk */
+       I915_WRITE(VDECCLK_GATE_D, I915_READ(VDECCLK_GATE_D) | VCP_UNIT_CLOCK_GATE_DISABLE);
+       POSTING_READ(VDECCLK_GATE_D);
+
+       pci_write_config_byte(dev->pdev, I965_GDRST,
+                             GRDOM_MEDIA | GRDOM_RESET_ENABLE);
+       ret =  wait_for(i965_reset_complete(dev), 500);
+       if (ret)
+               return ret;
+
+       /* WaVcpClkGateDisableForMediaReset:ctg,elk */
+       I915_WRITE(VDECCLK_GATE_D, I915_READ(VDECCLK_GATE_D) & ~VCP_UNIT_CLOCK_GATE_DISABLE);
+       POSTING_READ(VDECCLK_GATE_D);
+
+       pci_write_config_byte(dev->pdev, I965_GDRST, 0);
+
+       return 0;
+}
+
 static int ironlake_do_reset(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 gdrst;
        int ret;
 
-       gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR);
-       gdrst &= ~GRDOM_MASK;
        I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR,
-                  gdrst | GRDOM_RENDER | GRDOM_RESET_ENABLE);
-       ret = wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500);
+                  ILK_GRDOM_RENDER | ILK_GRDOM_RESET_ENABLE);
+       ret = wait_for((I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) &
+                       ILK_GRDOM_RESET_ENABLE) == 0, 500);
        if (ret)
                return ret;
 
-       /* We can't reset render&media without also resetting display ... */
-       gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR);
-       gdrst &= ~GRDOM_MASK;
        I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR,
-                  gdrst | GRDOM_MEDIA | GRDOM_RESET_ENABLE);
-       return wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500);
+                  ILK_GRDOM_MEDIA | ILK_GRDOM_RESET_ENABLE);
+       ret = wait_for((I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) &
+                       ILK_GRDOM_RESET_ENABLE) == 0, 500);
+       if (ret)
+               return ret;
+
+       I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR, 0);
+
+       return 0;
 }
 
 static int gen6_do_reset(struct drm_device *dev)
@@ -1029,7 +1057,11 @@ int intel_gpu_reset(struct drm_device *dev)
        case 7:
        case 6: return gen6_do_reset(dev);
        case 5: return ironlake_do_reset(dev);
-       case 4: return i965_do_reset(dev);
+       case 4:
+               if (IS_G4X(dev))
+                       return g4x_do_reset(dev);
+               else
+                       return i965_do_reset(dev);
        default: return -ENODEV;
        }
 }
index 86b4bb80485200e1067c47574117fa1c46c17b8f..729bfd56b55f49d9bd71a501f6717659a2f47af1 100644 (file)
@@ -214,7 +214,7 @@ long mga_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
        if (nr < DRM_COMMAND_BASE)
                return drm_compat_ioctl(filp, cmd, arg);
 
-       if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(mga_compat_ioctls))
+       if (nr < DRM_COMMAND_BASE + ARRAY_SIZE(mga_compat_ioctls))
                fn = mga_compat_ioctls[nr - DRM_COMMAND_BASE];
 
        if (fn != NULL)
index 314685b7f41fc4acb21a2c9c0cad86ecbd1ccad2..792f924496fc8cecd9fb7e16f8e2d58973476885 100644 (file)
@@ -1020,7 +1020,7 @@ static int mga_getparam(struct drm_device *dev, void *data, struct drm_file *fil
 
        switch (param->param) {
        case MGA_PARAM_IRQ_NR:
-               value = drm_dev_to_irq(dev);
+               value = dev->pdev->irq;
                break;
        case MGA_PARAM_CARD_TYPE:
                value = dev_priv->chipset;
@@ -1099,4 +1099,4 @@ const struct drm_ioctl_desc mga_ioctls[] = {
        DRM_IOCTL_DEF_DRV(MGA_DMA_BOOTSTRAP, mga_dma_bootstrap, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
 };
 
-int mga_max_ioctl = DRM_ARRAY_SIZE(mga_ioctls);
+int mga_max_ioctl = ARRAY_SIZE(mga_ioctls);
index 26868e5c55b076352188d34962b928a87a64d1a9..f6b283b8375ec7071adb32459a1bc2f6427dc2ad 100644 (file)
@@ -322,17 +322,13 @@ static void mgag200_bo_unref(struct mgag200_bo **bo)
 
        tbo = &((*bo)->bo);
        ttm_bo_unref(&tbo);
-       if (tbo == NULL)
-               *bo = NULL;
-
+       *bo = NULL;
 }
 
 void mgag200_gem_free_object(struct drm_gem_object *obj)
 {
        struct mgag200_bo *mgag200_bo = gem_to_mga_bo(obj);
 
-       if (!mgag200_bo)
-               return;
        mgag200_bo_unref(&mgag200_bo);
 }
 
index b6984971ce0c9b928dca6251db2916cdc78c77e9..f12388967856bfad8522814f48afd4e20b1052e3 100644 (file)
@@ -3,7 +3,7 @@ config DRM_MSM
        tristate "MSM DRM"
        depends on DRM
        depends on MSM_IOMMU
-       depends on ARCH_MSM8960 || (ARM && COMPILE_TEST)
+       depends on ARCH_QCOM || (ARM && COMPILE_TEST)
        select DRM_KMS_HELPER
        select SHMEM
        select TMPFS
index 5e1e6b0cd8acff9fa0b65ed6df7a6a91901450c5..93ca49c8df44b06cfe832bfdf03eeccd5d559bb0 100644 (file)
@@ -34,6 +34,8 @@ msm-y := \
        msm_gem_submit.o \
        msm_gpu.o \
        msm_iommu.o \
+       msm_perf.o \
+       msm_rd.o \
        msm_ringbuffer.o
 
 msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o
index f20fbde5dc490595f57d171424c1c25f7ff5dd5b..942e09d898a871b9b533a7dff07db703f29d5c58 100644 (file)
@@ -207,11 +207,11 @@ static int a3xx_hw_init(struct msm_gpu *gpu)
        /* Turn on performance counters: */
        gpu_write(gpu, REG_A3XX_RBBM_PERFCTR_CTL, 0x01);
 
-       /* Set SP perfcounter 7 to count SP_FS_FULL_ALU_INSTRUCTIONS
-        * we will use this to augment our hang detection:
-        */
-       gpu_write(gpu, REG_A3XX_SP_PERFCOUNTER7_SELECT,
-                       SP_FS_FULL_ALU_INSTRUCTIONS);
+       /* Enable the perfcntrs that we use.. */
+       for (i = 0; i < gpu->num_perfcntrs; i++) {
+               const struct msm_gpu_perfcntr *perfcntr = &gpu->perfcntrs[i];
+               gpu_write(gpu, perfcntr->select_reg, perfcntr->select_val);
+       }
 
        gpu_write(gpu, REG_A3XX_RBBM_INT_0_MASK, A3XX_INT0_MASK);
 
@@ -465,6 +465,13 @@ static const struct adreno_gpu_funcs funcs = {
        },
 };
 
+static const struct msm_gpu_perfcntr perfcntrs[] = {
+       { REG_A3XX_SP_PERFCOUNTER6_SELECT, REG_A3XX_RBBM_PERFCTR_SP_6_LO,
+                       SP_ALU_ACTIVE_CYCLES, "ALUACTIVE" },
+       { REG_A3XX_SP_PERFCOUNTER7_SELECT, REG_A3XX_RBBM_PERFCTR_SP_7_LO,
+                       SP_FS_FULL_ALU_INSTRUCTIONS, "ALUFULL" },
+};
+
 struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
 {
        struct a3xx_gpu *a3xx_gpu = NULL;
@@ -504,6 +511,9 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
        DBG("fast_rate=%u, slow_rate=%u, bus_freq=%u",
                        gpu->fast_rate, gpu->slow_rate, gpu->bus_freq);
 
+       gpu->perfcntrs = perfcntrs;
+       gpu->num_perfcntrs = ARRAY_SIZE(perfcntrs);
+
        ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, config->rev);
        if (ret)
                goto fail;
index 7dedfdd120759d51c7bd5a940bb01e874b87f579..e56a6196867c6609c4940555d359cd898a4155bd 100644 (file)
@@ -247,36 +247,49 @@ void hdmi_connector_irq(struct drm_connector *connector)
        }
 }
 
+static enum drm_connector_status detect_reg(struct hdmi *hdmi)
+{
+       uint32_t hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
+       return (hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED) ?
+                       connector_status_connected : connector_status_disconnected;
+}
+
+static enum drm_connector_status detect_gpio(struct hdmi *hdmi)
+{
+       const struct hdmi_platform_config *config = hdmi->config;
+       return gpio_get_value(config->hpd_gpio) ?
+                       connector_status_connected :
+                       connector_status_disconnected;
+}
+
 static enum drm_connector_status hdmi_connector_detect(
                struct drm_connector *connector, bool force)
 {
        struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
        struct hdmi *hdmi = hdmi_connector->hdmi;
-       const struct hdmi_platform_config *config = hdmi->config;
-       uint32_t hpd_int_status;
+       enum drm_connector_status stat_gpio, stat_reg;
        int retry = 20;
 
-       hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
+       do {
+               stat_gpio = detect_gpio(hdmi);
+               stat_reg  = detect_reg(hdmi);
 
-       /* sense seems to in some cases be momentarily de-asserted, don't
-        * let that trick us into thinking the monitor is gone:
-        */
-       while (retry-- && !(hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED)) {
-               /* hdmi debounce logic seems to get stuck sometimes,
-                * read directly the gpio to get a second opinion:
-                */
-               if (gpio_get_value(config->hpd_gpio)) {
-                       DBG("gpio tells us we are connected!");
-                       hpd_int_status |= HDMI_HPD_INT_STATUS_CABLE_DETECTED;
+               if (stat_gpio == stat_reg)
                        break;
-               }
+
                mdelay(10);
-               hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
-               DBG("status=%08x", hpd_int_status);
+       } while (--retry);
+
+       /* the status we get from reading gpio seems to be more reliable,
+        * so trust that one the most if we didn't manage to get hdmi and
+        * gpio status to agree:
+        */
+       if (stat_gpio != stat_reg) {
+               DBG("HDMI_HPD_INT_STATUS tells us: %d", stat_reg);
+               DBG("hpd gpio tells us: %d", stat_gpio);
        }
 
-       return (hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED) ?
-                       connector_status_connected : connector_status_disconnected;
+       return stat_gpio;
 }
 
 static void hdmi_connector_destroy(struct drm_connector *connector)
@@ -389,7 +402,8 @@ struct drm_connector *hdmi_connector_init(struct hdmi *hdmi)
                        DRM_MODE_CONNECTOR_HDMIA);
        drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
 
-       connector->polled = DRM_CONNECTOR_POLL_HPD;
+       connector->polled = DRM_CONNECTOR_POLL_CONNECT |
+                       DRM_CONNECTOR_POLL_DISCONNECT;
 
        connector->interlace_allowed = 1;
        connector->doublescan_allowed = 0;
index ef9957dbac943bdda6a1e6fd9bca142ebb202cef..74cebb51e8c285e23475c6df40ae64a271857b9f 100644 (file)
@@ -217,8 +217,6 @@ static void mdp4_crtc_destroy(struct drm_crtc *crtc)
 {
        struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
 
-       mdp4_crtc->plane->funcs->destroy(mdp4_crtc->plane);
-
        drm_crtc_cleanup(crtc);
        drm_flip_work_cleanup(&mdp4_crtc->unref_fb_work);
        drm_flip_work_cleanup(&mdp4_crtc->unref_cursor_work);
index 6ea10bdb6e8fc60d7691ba5ff6338177023e94ac..ebe2e60f3ab1147826e49a79b9772f4115635040 100644 (file)
@@ -195,8 +195,6 @@ static void mdp5_crtc_destroy(struct drm_crtc *crtc)
 {
        struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
 
-       mdp5_crtc->plane->funcs->destroy(mdp5_crtc->plane);
-
        drm_crtc_cleanup(crtc);
        drm_flip_work_cleanup(&mdp5_crtc->unref_fb_work);
 
index ee8446c1b5f6b2622e1b9cac5ef69916375b970d..42caf7fcb0b93c5776950ddf4e2bdd0adfebd0cf 100644 (file)
@@ -280,12 +280,22 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
                goto fail;
        }
 
-       ret = get_clk(pdev, &mdp5_kms->axi_clk, "bus_clk") ||
-                       get_clk(pdev, &mdp5_kms->ahb_clk, "iface_clk") ||
-                       get_clk(pdev, &mdp5_kms->src_clk, "core_clk_src") ||
-                       get_clk(pdev, &mdp5_kms->core_clk, "core_clk") ||
-                       get_clk(pdev, &mdp5_kms->lut_clk, "lut_clk") ||
-                       get_clk(pdev, &mdp5_kms->vsync_clk, "vsync_clk");
+       ret = get_clk(pdev, &mdp5_kms->axi_clk, "bus_clk");
+       if (ret)
+               goto fail;
+       ret = get_clk(pdev, &mdp5_kms->ahb_clk, "iface_clk");
+       if (ret)
+               goto fail;
+       ret = get_clk(pdev, &mdp5_kms->src_clk, "core_clk_src");
+       if (ret)
+               goto fail;
+       ret = get_clk(pdev, &mdp5_kms->core_clk, "core_clk");
+       if (ret)
+               goto fail;
+       ret = get_clk(pdev, &mdp5_kms->lut_clk, "lut_clk");
+       if (ret)
+               goto fail;
+       ret = get_clk(pdev, &mdp5_kms->vsync_clk, "vsync_clk");
        if (ret)
                goto fail;
 
index 47f7bbb9c15ace7bec2606c7d550f30e480c20b1..f3daec4412ad3e9fd6b8f7303a2b27280df7442c 100644 (file)
@@ -85,8 +85,11 @@ static int mdp5_plane_disable(struct drm_plane *plane)
 static void mdp5_plane_destroy(struct drm_plane *plane)
 {
        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
+       struct msm_drm_private *priv = plane->dev->dev_private;
+
+       if (priv->kms)
+               mdp5_plane_disable(plane);
 
-       mdp5_plane_disable(plane);
        drm_plane_cleanup(plane);
 
        kfree(mdp5_plane);
index f9de156b9e65c992d51f32d9670c315d240797da..0d2562fb681eee5704228fe5eeea708c86f6b23d 100644 (file)
@@ -220,7 +220,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
                 * is bogus, but non-null if allocation succeeded:
                 */
                p = dma_alloc_attrs(dev->dev, size,
-                               &priv->vram.paddr, 0, &attrs);
+                               &priv->vram.paddr, GFP_KERNEL, &attrs);
                if (!p) {
                        dev_err(dev->dev, "failed to allocate VRAM\n");
                        priv->vram.paddr = 0;
@@ -288,7 +288,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
        }
 
        pm_runtime_get_sync(dev->dev);
-       ret = drm_irq_install(dev);
+       ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0));
        pm_runtime_put_sync(dev->dev);
        if (ret < 0) {
                dev_err(dev->dev, "failed to install IRQ handler\n");
@@ -299,6 +299,10 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
        priv->fbdev = msm_fbdev_init(dev);
 #endif
 
+       ret = msm_debugfs_late_init(dev);
+       if (ret)
+               goto fail;
+
        drm_kms_helper_poll_init(dev);
 
        return 0;
@@ -382,11 +386,8 @@ static void msm_preclose(struct drm_device *dev, struct drm_file *file)
 static void msm_lastclose(struct drm_device *dev)
 {
        struct msm_drm_private *priv = dev->dev_private;
-       if (priv->fbdev) {
-               drm_modeset_lock_all(dev);
-               drm_fb_helper_restore_fbdev_mode(priv->fbdev);
-               drm_modeset_unlock_all(dev);
-       }
+       if (priv->fbdev)
+               drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev);
 }
 
 static irqreturn_t msm_irq(int irq, void *arg)
@@ -531,6 +532,41 @@ static struct drm_info_list msm_debugfs_list[] = {
                { "fb", show_locked, 0, msm_fb_show },
 };
 
+static int late_init_minor(struct drm_minor *minor)
+{
+       int ret;
+
+       if (!minor)
+               return 0;
+
+       ret = msm_rd_debugfs_init(minor);
+       if (ret) {
+               dev_err(minor->dev->dev, "could not install rd debugfs\n");
+               return ret;
+       }
+
+       ret = msm_perf_debugfs_init(minor);
+       if (ret) {
+               dev_err(minor->dev->dev, "could not install perf debugfs\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+int msm_debugfs_late_init(struct drm_device *dev)
+{
+       int ret;
+       ret = late_init_minor(dev->primary);
+       if (ret)
+               return ret;
+       ret = late_init_minor(dev->render);
+       if (ret)
+               return ret;
+       ret = late_init_minor(dev->control);
+       return ret;
+}
+
 static int msm_debugfs_init(struct drm_minor *minor)
 {
        struct drm_device *dev = minor->dev;
@@ -545,13 +581,17 @@ static int msm_debugfs_init(struct drm_minor *minor)
                return ret;
        }
 
-       return ret;
+       return 0;
 }
 
 static void msm_debugfs_cleanup(struct drm_minor *minor)
 {
        drm_debugfs_remove_files(msm_debugfs_list,
                        ARRAY_SIZE(msm_debugfs_list), minor);
+       if (!minor->dev->dev_private)
+               return;
+       msm_rd_debugfs_cleanup(minor);
+       msm_perf_debugfs_cleanup(minor);
 }
 #endif
 
index 9d10ee0b5aacf9d234de220e93a49fbea786a503..8a2c5fd0893e07f824b46e2d2d7528e96eca2ace 100644 (file)
@@ -33,7 +33,7 @@
 #include <asm/sizes.h>
 
 
-#if defined(CONFIG_COMPILE_TEST) && !defined(CONFIG_ARCH_MSM)
+#if defined(CONFIG_COMPILE_TEST) && !defined(CONFIG_ARCH_QCOM)
 /* stubs we need for compile-test: */
 static inline struct device *msm_iommu_get_ctx(const char *ctx_name)
 {
@@ -55,6 +55,9 @@ static inline struct device *msm_iommu_get_ctx(const char *ctx_name)
 struct msm_kms;
 struct msm_gpu;
 struct msm_mmu;
+struct msm_rd_state;
+struct msm_perf_state;
+struct msm_gem_submit;
 
 #define NUM_DOMAINS 2    /* one for KMS, then one per gpu core (?) */
 
@@ -82,6 +85,9 @@ struct msm_drm_private {
        uint32_t next_fence, completed_fence;
        wait_queue_head_t fence_event;
 
+       struct msm_rd_state *rd;
+       struct msm_perf_state *perf;
+
        /* list of GEM objects: */
        struct list_head inactive_list;
 
@@ -204,6 +210,15 @@ void __exit hdmi_unregister(void);
 void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m);
 void msm_gem_describe_objects(struct list_head *list, struct seq_file *m);
 void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m);
+int msm_debugfs_late_init(struct drm_device *dev);
+int msm_rd_debugfs_init(struct drm_minor *minor);
+void msm_rd_debugfs_cleanup(struct drm_minor *minor);
+void msm_rd_dump_submit(struct msm_gem_submit *submit);
+int msm_perf_debugfs_init(struct drm_minor *minor);
+void msm_perf_debugfs_cleanup(struct drm_minor *minor);
+#else
+static inline int msm_debugfs_late_init(struct drm_device *dev) { return 0; }
+static inline void msm_rd_dump_submit(struct msm_gem_submit *submit) {}
 #endif
 
 void __iomem *msm_ioremap(struct platform_device *pdev, const char *name,
index 3246bb46c4f2add0636bca94b1b4c726542b1178..bfb052688f8ea01a7852bb748cc508c40d1311d3 100644 (file)
@@ -90,6 +90,7 @@ struct msm_gem_submit {
                uint32_t type;
                uint32_t size;  /* in dwords */
                uint32_t iova;
+               uint32_t idx;   /* cmdstream buffer idx in bos[] */
        } cmd[MAX_CMDS];
        struct {
                uint32_t flags;
index 1f1f4cffdaed1ec43b3a3b6a8338b0b27064f6f4..cd0554f68316e9fa085edade11e122072d417a2b 100644 (file)
@@ -402,6 +402,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
                submit->cmd[i].type = submit_cmd.type;
                submit->cmd[i].size = submit_cmd.size / 4;
                submit->cmd[i].iova = iova + submit_cmd.submit_offset;
+               submit->cmd[i].idx  = submit_cmd.submit_idx;
 
                if (submit->valid)
                        continue;
index 3e667ca1f2b9360d5904a8c5bb7f0e4b48e7f8fc..c6322197db8cf15a1f9f96ea627afae5b10dc46b 100644 (file)
@@ -319,6 +319,101 @@ static void hangcheck_handler(unsigned long data)
        queue_work(priv->wq, &gpu->retire_work);
 }
 
+/*
+ * Performance Counters:
+ */
+
+/* called under perf_lock */
+static int update_hw_cntrs(struct msm_gpu *gpu, uint32_t ncntrs, uint32_t *cntrs)
+{
+       uint32_t current_cntrs[ARRAY_SIZE(gpu->last_cntrs)];
+       int i, n = min(ncntrs, gpu->num_perfcntrs);
+
+       /* read current values: */
+       for (i = 0; i < gpu->num_perfcntrs; i++)
+               current_cntrs[i] = gpu_read(gpu, gpu->perfcntrs[i].sample_reg);
+
+       /* update cntrs: */
+       for (i = 0; i < n; i++)
+               cntrs[i] = current_cntrs[i] - gpu->last_cntrs[i];
+
+       /* save current values: */
+       for (i = 0; i < gpu->num_perfcntrs; i++)
+               gpu->last_cntrs[i] = current_cntrs[i];
+
+       return n;
+}
+
+static void update_sw_cntrs(struct msm_gpu *gpu)
+{
+       ktime_t time;
+       uint32_t elapsed;
+       unsigned long flags;
+
+       spin_lock_irqsave(&gpu->perf_lock, flags);
+       if (!gpu->perfcntr_active)
+               goto out;
+
+       time = ktime_get();
+       elapsed = ktime_to_us(ktime_sub(time, gpu->last_sample.time));
+
+       gpu->totaltime += elapsed;
+       if (gpu->last_sample.active)
+               gpu->activetime += elapsed;
+
+       gpu->last_sample.active = msm_gpu_active(gpu);
+       gpu->last_sample.time = time;
+
+out:
+       spin_unlock_irqrestore(&gpu->perf_lock, flags);
+}
+
+void msm_gpu_perfcntr_start(struct msm_gpu *gpu)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&gpu->perf_lock, flags);
+       /* we could dynamically enable/disable perfcntr registers too.. */
+       gpu->last_sample.active = msm_gpu_active(gpu);
+       gpu->last_sample.time = ktime_get();
+       gpu->activetime = gpu->totaltime = 0;
+       gpu->perfcntr_active = true;
+       update_hw_cntrs(gpu, 0, NULL);
+       spin_unlock_irqrestore(&gpu->perf_lock, flags);
+}
+
+void msm_gpu_perfcntr_stop(struct msm_gpu *gpu)
+{
+       gpu->perfcntr_active = false;
+}
+
+/* returns -errno or # of cntrs sampled */
+int msm_gpu_perfcntr_sample(struct msm_gpu *gpu, uint32_t *activetime,
+               uint32_t *totaltime, uint32_t ncntrs, uint32_t *cntrs)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&gpu->perf_lock, flags);
+
+       if (!gpu->perfcntr_active) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       *activetime = gpu->activetime;
+       *totaltime = gpu->totaltime;
+
+       gpu->activetime = gpu->totaltime = 0;
+
+       ret = update_hw_cntrs(gpu, ncntrs, cntrs);
+
+out:
+       spin_unlock_irqrestore(&gpu->perf_lock, flags);
+
+       return ret;
+}
+
 /*
  * Cmdstream submission/retirement:
  */
@@ -361,6 +456,7 @@ void msm_gpu_retire(struct msm_gpu *gpu)
 {
        struct msm_drm_private *priv = gpu->dev->dev_private;
        queue_work(priv->wq, &gpu->retire_work);
+       update_sw_cntrs(gpu);
 }
 
 /* add bo's to gpu's ring, and kick gpu: */
@@ -377,6 +473,12 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
 
        inactive_cancel(gpu);
 
+       msm_rd_dump_submit(submit);
+
+       gpu->submitted_fence = submit->fence;
+
+       update_sw_cntrs(gpu);
+
        ret = gpu->funcs->submit(gpu, submit, ctx);
        priv->lastctx = ctx;
 
@@ -429,6 +531,9 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
        struct iommu_domain *iommu;
        int i, ret;
 
+       if (WARN_ON(gpu->num_perfcntrs > ARRAY_SIZE(gpu->last_cntrs)))
+               gpu->num_perfcntrs = ARRAY_SIZE(gpu->last_cntrs);
+
        gpu->dev = drm;
        gpu->funcs = funcs;
        gpu->name = name;
@@ -444,6 +549,8 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
        setup_timer(&gpu->hangcheck_timer, hangcheck_handler,
                        (unsigned long)gpu);
 
+       spin_lock_init(&gpu->perf_lock);
+
        BUG_ON(ARRAY_SIZE(clk_names) != ARRAY_SIZE(gpu->grp_clks));
 
        /* Map registers: */
index fad27008922fa0829f25af10d3cde5198530c87d..9b579b792840fb8d1c84bc1176ee7c992ab5e731 100644 (file)
@@ -25,6 +25,7 @@
 #include "msm_ringbuffer.h"
 
 struct msm_gem_submit;
+struct msm_gpu_perfcntr;
 
 /* So far, with hardware that I've seen to date, we can have:
  *  + zero, one, or two z180 2d cores
@@ -64,6 +65,18 @@ struct msm_gpu {
        struct drm_device *dev;
        const struct msm_gpu_funcs *funcs;
 
+       /* performance counters (hw & sw): */
+       spinlock_t perf_lock;
+       bool perfcntr_active;
+       struct {
+               bool active;
+               ktime_t time;
+       } last_sample;
+       uint32_t totaltime, activetime;    /* sw counters */
+       uint32_t last_cntrs[5];            /* hw counters */
+       const struct msm_gpu_perfcntr *perfcntrs;
+       uint32_t num_perfcntrs;
+
        struct msm_ringbuffer *rb;
        uint32_t rb_iova;
 
@@ -113,6 +126,19 @@ static inline bool msm_gpu_active(struct msm_gpu *gpu)
        return gpu->submitted_fence > gpu->funcs->last_fence(gpu);
 }
 
+/* Perf-Counters:
+ * The select_reg and select_val are just there for the benefit of the child
+ * class that actually enables the perf counter..  but msm_gpu base class
+ * will handle sampling/displaying the counters.
+ */
+
+struct msm_gpu_perfcntr {
+       uint32_t select_reg;
+       uint32_t sample_reg;
+       uint32_t select_val;
+       const char *name;
+};
+
 static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data)
 {
        msm_writel(data, gpu->mmio + (reg << 2));
@@ -126,6 +152,11 @@ static inline u32 gpu_read(struct msm_gpu *gpu, u32 reg)
 int msm_gpu_pm_suspend(struct msm_gpu *gpu);
 int msm_gpu_pm_resume(struct msm_gpu *gpu);
 
+void msm_gpu_perfcntr_start(struct msm_gpu *gpu);
+void msm_gpu_perfcntr_stop(struct msm_gpu *gpu);
+int msm_gpu_perfcntr_sample(struct msm_gpu *gpu, uint32_t *activetime,
+               uint32_t *totaltime, uint32_t ncntrs, uint32_t *cntrs);
+
 void msm_gpu_retire(struct msm_gpu *gpu);
 int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
                struct msm_file_private *ctx);
diff --git a/drivers/gpu/drm/msm/msm_perf.c b/drivers/gpu/drm/msm/msm_perf.c
new file mode 100644 (file)
index 0000000..830857c
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* For profiling, userspace can:
+ *
+ *   tail -f /sys/kernel/debug/dri/<minor>/gpu
+ *
+ * This will enable performance counters/profiling to track the busy time
+ * and any gpu specific performance counters that are supported.
+ */
+
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/debugfs.h>
+
+#include "msm_drv.h"
+#include "msm_gpu.h"
+
+struct msm_perf_state {
+       struct drm_device *dev;
+
+       bool open;
+       int cnt;
+       struct mutex read_lock;
+
+       char buf[256];
+       int buftot, bufpos;
+
+       unsigned long next_jiffies;
+
+       struct dentry *ent;
+       struct drm_info_node *node;
+};
+
+#define SAMPLE_TIME (HZ/4)
+
+/* wait for next sample time: */
+static int wait_sample(struct msm_perf_state *perf)
+{
+       unsigned long start_jiffies = jiffies;
+
+       if (time_after(perf->next_jiffies, start_jiffies)) {
+               unsigned long remaining_jiffies =
+                       perf->next_jiffies - start_jiffies;
+               int ret = schedule_timeout_interruptible(remaining_jiffies);
+               if (ret > 0) {
+                       /* interrupted */
+                       return -ERESTARTSYS;
+               }
+       }
+       perf->next_jiffies += SAMPLE_TIME;
+       return 0;
+}
+
+static int refill_buf(struct msm_perf_state *perf)
+{
+       struct msm_drm_private *priv = perf->dev->dev_private;
+       struct msm_gpu *gpu = priv->gpu;
+       char *ptr = perf->buf;
+       int rem = sizeof(perf->buf);
+       int i, n;
+
+       if ((perf->cnt++ % 32) == 0) {
+               /* Header line: */
+               n = snprintf(ptr, rem, "%%BUSY");
+               ptr += n;
+               rem -= n;
+
+               for (i = 0; i < gpu->num_perfcntrs; i++) {
+                       const struct msm_gpu_perfcntr *perfcntr = &gpu->perfcntrs[i];
+                       n = snprintf(ptr, rem, "\t%s", perfcntr->name);
+                       ptr += n;
+                       rem -= n;
+               }
+       } else {
+               /* Sample line: */
+               uint32_t activetime = 0, totaltime = 0;
+               uint32_t cntrs[5];
+               uint32_t val;
+               int ret;
+
+               /* sleep until next sample time: */
+               ret = wait_sample(perf);
+               if (ret)
+                       return ret;
+
+               ret = msm_gpu_perfcntr_sample(gpu, &activetime, &totaltime,
+                               ARRAY_SIZE(cntrs), cntrs);
+               if (ret < 0)
+                       return ret;
+
+               val = totaltime ? 1000 * activetime / totaltime : 0;
+               n = snprintf(ptr, rem, "%3d.%d%%", val / 10, val % 10);
+               ptr += n;
+               rem -= n;
+
+               for (i = 0; i < ret; i++) {
+                       /* cycle counters (I think).. convert to MHz.. */
+                       val = cntrs[i] / 10000;
+                       n = snprintf(ptr, rem, "\t%5d.%02d",
+                                       val / 100, val % 100);
+                       ptr += n;
+                       rem -= n;
+               }
+       }
+
+       n = snprintf(ptr, rem, "\n");
+       ptr += n;
+       rem -= n;
+
+       perf->bufpos = 0;
+       perf->buftot = ptr - perf->buf;
+
+       return 0;
+}
+
+static ssize_t perf_read(struct file *file, char __user *buf,
+               size_t sz, loff_t *ppos)
+{
+       struct msm_perf_state *perf = file->private_data;
+       int n = 0, ret;
+
+       mutex_lock(&perf->read_lock);
+
+       if (perf->bufpos >= perf->buftot) {
+               ret = refill_buf(perf);
+               if (ret)
+                       goto out;
+       }
+
+       n = min((int)sz, perf->buftot - perf->bufpos);
+       ret = copy_to_user(buf, &perf->buf[perf->bufpos], n);
+       if (ret)
+               goto out;
+
+       perf->bufpos += n;
+       *ppos += n;
+
+out:
+       mutex_unlock(&perf->read_lock);
+       if (ret)
+               return ret;
+       return n;
+}
+
+static int perf_open(struct inode *inode, struct file *file)
+{
+       struct msm_perf_state *perf = inode->i_private;
+       struct drm_device *dev = perf->dev;
+       struct msm_drm_private *priv = dev->dev_private;
+       struct msm_gpu *gpu = priv->gpu;
+       int ret = 0;
+
+       mutex_lock(&dev->struct_mutex);
+
+       if (perf->open || !gpu) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       file->private_data = perf;
+       perf->open = true;
+       perf->cnt = 0;
+       perf->buftot = 0;
+       perf->bufpos = 0;
+       msm_gpu_perfcntr_start(gpu);
+       perf->next_jiffies = jiffies + SAMPLE_TIME;
+
+out:
+       mutex_unlock(&dev->struct_mutex);
+       return ret;
+}
+
+static int perf_release(struct inode *inode, struct file *file)
+{
+       struct msm_perf_state *perf = inode->i_private;
+       struct msm_drm_private *priv = perf->dev->dev_private;
+       msm_gpu_perfcntr_stop(priv->gpu);
+       perf->open = false;
+       return 0;
+}
+
+
+static const struct file_operations perf_debugfs_fops = {
+       .owner = THIS_MODULE,
+       .open = perf_open,
+       .read = perf_read,
+       .llseek = no_llseek,
+       .release = perf_release,
+};
+
+int msm_perf_debugfs_init(struct drm_minor *minor)
+{
+       struct msm_drm_private *priv = minor->dev->dev_private;
+       struct msm_perf_state *perf;
+
+       /* only create on first minor: */
+       if (priv->perf)
+               return 0;
+
+       perf = kzalloc(sizeof(*perf), GFP_KERNEL);
+       if (!perf)
+               return -ENOMEM;
+
+       perf->dev = minor->dev;
+
+       mutex_init(&perf->read_lock);
+       priv->perf = perf;
+
+       perf->node = kzalloc(sizeof(*perf->node), GFP_KERNEL);
+       if (!perf->node)
+               goto fail;
+
+       perf->ent = debugfs_create_file("perf", S_IFREG | S_IRUGO,
+                       minor->debugfs_root, perf, &perf_debugfs_fops);
+       if (!perf->ent) {
+               DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s/perf\n",
+                               minor->debugfs_root->d_name.name);
+               goto fail;
+       }
+
+       perf->node->minor = minor;
+       perf->node->dent  = perf->ent;
+       perf->node->info_ent = NULL;
+
+       mutex_lock(&minor->debugfs_lock);
+       list_add(&perf->node->list, &minor->debugfs_list);
+       mutex_unlock(&minor->debugfs_lock);
+
+       return 0;
+
+fail:
+       msm_perf_debugfs_cleanup(minor);
+       return -1;
+}
+
+void msm_perf_debugfs_cleanup(struct drm_minor *minor)
+{
+       struct msm_drm_private *priv = minor->dev->dev_private;
+       struct msm_perf_state *perf = priv->perf;
+
+       if (!perf)
+               return;
+
+       priv->perf = NULL;
+
+       debugfs_remove(perf->ent);
+
+       if (perf->node) {
+               mutex_lock(&minor->debugfs_lock);
+               list_del(&perf->node->list);
+               mutex_unlock(&minor->debugfs_lock);
+               kfree(perf->node);
+       }
+
+       mutex_destroy(&perf->read_lock);
+
+       kfree(perf);
+}
+
+#endif
diff --git a/drivers/gpu/drm/msm/msm_rd.c b/drivers/gpu/drm/msm/msm_rd.c
new file mode 100644 (file)
index 0000000..9a78c48
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* For debugging crashes, userspace can:
+ *
+ *   tail -f /sys/kernel/debug/dri/<minor>/rd > logfile.rd
+ *
+ * To log the cmdstream in a format that is understood by freedreno/cffdump
+ * utility.  By comparing the last successfully completed fence #, to the
+ * cmdstream for the next fence, you can narrow down which process and submit
+ * caused the gpu crash/lockup.
+ *
+ * This bypasses drm_debugfs_create_files() mainly because we need to use
+ * our own fops for a bit more control.  In particular, we don't want to
+ * do anything if userspace doesn't have the debugfs file open.
+ */
+
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/kfifo.h>
+#include <linux/debugfs.h>
+#include <linux/circ_buf.h>
+#include <linux/wait.h>
+
+#include "msm_drv.h"
+#include "msm_gpu.h"
+#include "msm_gem.h"
+
+enum rd_sect_type {
+       RD_NONE,
+       RD_TEST,       /* ascii text */
+       RD_CMD,        /* ascii text */
+       RD_GPUADDR,    /* u32 gpuaddr, u32 size */
+       RD_CONTEXT,    /* raw dump */
+       RD_CMDSTREAM,  /* raw dump */
+       RD_CMDSTREAM_ADDR, /* gpu addr of cmdstream */
+       RD_PARAM,      /* u32 param_type, u32 param_val, u32 bitlen */
+       RD_FLUSH,      /* empty, clear previous params */
+       RD_PROGRAM,    /* shader program, raw dump */
+       RD_VERT_SHADER,
+       RD_FRAG_SHADER,
+       RD_BUFFER_CONTENTS,
+       RD_GPU_ID,
+};
+
+#define BUF_SZ 512  /* should be power of 2 */
+
+/* space used: */
+#define circ_count(circ) \
+       (CIRC_CNT((circ)->head, (circ)->tail, BUF_SZ))
+#define circ_count_to_end(circ) \
+       (CIRC_CNT_TO_END((circ)->head, (circ)->tail, BUF_SZ))
+/* space available: */
+#define circ_space(circ) \
+       (CIRC_SPACE((circ)->head, (circ)->tail, BUF_SZ))
+#define circ_space_to_end(circ) \
+       (CIRC_SPACE_TO_END((circ)->head, (circ)->tail, BUF_SZ))
+
+struct msm_rd_state {
+       struct drm_device *dev;
+
+       bool open;
+
+       struct dentry *ent;
+       struct drm_info_node *node;
+
+       /* current submit to read out: */
+       struct msm_gem_submit *submit;
+
+       /* fifo access is synchronized on the producer side by
+        * struct_mutex held by submit code (otherwise we could
+        * end up w/ cmds logged in different order than they
+        * were executed).  And read_lock synchronizes the reads
+        */
+       struct mutex read_lock;
+
+       wait_queue_head_t fifo_event;
+       struct circ_buf fifo;
+
+       char buf[BUF_SZ];
+};
+
+static void rd_write(struct msm_rd_state *rd, const void *buf, int sz)
+{
+       struct circ_buf *fifo = &rd->fifo;
+       const char *ptr = buf;
+
+       while (sz > 0) {
+               char *fptr = &fifo->buf[fifo->head];
+               int n;
+
+               wait_event(rd->fifo_event, circ_space(&rd->fifo) > 0);
+
+               n = min(sz, circ_space_to_end(&rd->fifo));
+               memcpy(fptr, ptr, n);
+
+               fifo->head = (fifo->head + n) & (BUF_SZ - 1);
+               sz  -= n;
+               ptr += n;
+
+               wake_up_all(&rd->fifo_event);
+       }
+}
+
+static void rd_write_section(struct msm_rd_state *rd,
+               enum rd_sect_type type, const void *buf, int sz)
+{
+       rd_write(rd, &type, 4);
+       rd_write(rd, &sz, 4);
+       rd_write(rd, buf, sz);
+}
+
+static ssize_t rd_read(struct file *file, char __user *buf,
+               size_t sz, loff_t *ppos)
+{
+       struct msm_rd_state *rd = file->private_data;
+       struct circ_buf *fifo = &rd->fifo;
+       const char *fptr = &fifo->buf[fifo->tail];
+       int n = 0, ret = 0;
+
+       mutex_lock(&rd->read_lock);
+
+       ret = wait_event_interruptible(rd->fifo_event,
+                       circ_count(&rd->fifo) > 0);
+       if (ret)
+               goto out;
+
+       n = min_t(int, sz, circ_count_to_end(&rd->fifo));
+       ret = copy_to_user(buf, fptr, n);
+       if (ret)
+               goto out;
+
+       fifo->tail = (fifo->tail + n) & (BUF_SZ - 1);
+       *ppos += n;
+
+       wake_up_all(&rd->fifo_event);
+
+out:
+       mutex_unlock(&rd->read_lock);
+       if (ret)
+               return ret;
+       return n;
+}
+
+static int rd_open(struct inode *inode, struct file *file)
+{
+       struct msm_rd_state *rd = inode->i_private;
+       struct drm_device *dev = rd->dev;
+       struct msm_drm_private *priv = dev->dev_private;
+       struct msm_gpu *gpu = priv->gpu;
+       uint64_t val;
+       uint32_t gpu_id;
+       int ret = 0;
+
+       mutex_lock(&dev->struct_mutex);
+
+       if (rd->open || !gpu) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       file->private_data = rd;
+       rd->open = true;
+
+       /* the parsing tools need to know gpu-id to know which
+        * register database to load.
+        */
+       gpu->funcs->get_param(gpu, MSM_PARAM_GPU_ID, &val);
+       gpu_id = val;
+
+       rd_write_section(rd, RD_GPU_ID, &gpu_id, sizeof(gpu_id));
+
+out:
+       mutex_unlock(&dev->struct_mutex);
+       return ret;
+}
+
+static int rd_release(struct inode *inode, struct file *file)
+{
+       struct msm_rd_state *rd = inode->i_private;
+       rd->open = false;
+       return 0;
+}
+
+
+static const struct file_operations rd_debugfs_fops = {
+       .owner = THIS_MODULE,
+       .open = rd_open,
+       .read = rd_read,
+       .llseek = no_llseek,
+       .release = rd_release,
+};
+
+int msm_rd_debugfs_init(struct drm_minor *minor)
+{
+       struct msm_drm_private *priv = minor->dev->dev_private;
+       struct msm_rd_state *rd;
+
+       /* only create on first minor: */
+       if (priv->rd)
+               return 0;
+
+       rd = kzalloc(sizeof(*rd), GFP_KERNEL);
+       if (!rd)
+               return -ENOMEM;
+
+       rd->dev = minor->dev;
+       rd->fifo.buf = rd->buf;
+
+       mutex_init(&rd->read_lock);
+       priv->rd = rd;
+
+       init_waitqueue_head(&rd->fifo_event);
+
+       rd->node = kzalloc(sizeof(*rd->node), GFP_KERNEL);
+       if (!rd->node)
+               goto fail;
+
+       rd->ent = debugfs_create_file("rd", S_IFREG | S_IRUGO,
+                       minor->debugfs_root, rd, &rd_debugfs_fops);
+       if (!rd->ent) {
+               DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s/rd\n",
+                               minor->debugfs_root->d_name.name);
+               goto fail;
+       }
+
+       rd->node->minor = minor;
+       rd->node->dent  = rd->ent;
+       rd->node->info_ent = NULL;
+
+       mutex_lock(&minor->debugfs_lock);
+       list_add(&rd->node->list, &minor->debugfs_list);
+       mutex_unlock(&minor->debugfs_lock);
+
+       return 0;
+
+fail:
+       msm_rd_debugfs_cleanup(minor);
+       return -1;
+}
+
+void msm_rd_debugfs_cleanup(struct drm_minor *minor)
+{
+       struct msm_drm_private *priv = minor->dev->dev_private;
+       struct msm_rd_state *rd = priv->rd;
+
+       if (!rd)
+               return;
+
+       priv->rd = NULL;
+
+       debugfs_remove(rd->ent);
+
+       if (rd->node) {
+               mutex_lock(&minor->debugfs_lock);
+               list_del(&rd->node->list);
+               mutex_unlock(&minor->debugfs_lock);
+               kfree(rd->node);
+       }
+
+       mutex_destroy(&rd->read_lock);
+
+       kfree(rd);
+}
+
+/* called under struct_mutex */
+void msm_rd_dump_submit(struct msm_gem_submit *submit)
+{
+       struct drm_device *dev = submit->dev;
+       struct msm_drm_private *priv = dev->dev_private;
+       struct msm_rd_state *rd = priv->rd;
+       char msg[128];
+       int i, n;
+
+       if (!rd->open)
+               return;
+
+       /* writing into fifo is serialized by caller, and
+        * rd->read_lock is used to serialize the reads
+        */
+       WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
+       n = snprintf(msg, sizeof(msg), "%.*s/%d: fence=%u",
+                       TASK_COMM_LEN, current->comm, task_pid_nr(current),
+                       submit->fence);
+
+       rd_write_section(rd, RD_CMD, msg, ALIGN(n, 4));
+
+       /* could be nice to have an option (module-param?) to snapshot
+        * all the bo's associated with the submit.  Handy to see vtx
+        * buffers, etc.  For now just the cmdstream bo's is enough.
+        */
+
+       for (i = 0; i < submit->nr_cmds; i++) {
+               uint32_t idx  = submit->cmd[i].idx;
+               uint32_t iova = submit->cmd[i].iova;
+               uint32_t szd  = submit->cmd[i].size; /* in dwords */
+               struct msm_gem_object *obj = submit->bos[idx].obj;
+               const char *buf = msm_gem_vaddr_locked(&obj->base);
+
+               buf += iova - submit->bos[idx].iova;
+
+               rd_write_section(rd, RD_GPUADDR,
+                               (uint32_t[2]){ iova, szd * 4 }, 8);
+               rd_write_section(rd, RD_BUFFER_CONTENTS,
+                               buf, szd * 4);
+
+               switch (submit->cmd[i].type) {
+               case MSM_SUBMIT_CMD_IB_TARGET_BUF:
+                       /* ignore IB-targets, we've logged the buffer, the
+                        * parser tool will follow the IB based on the logged
+                        * buffer/gpuaddr, so nothing more to do.
+                        */
+                       break;
+               case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
+               case MSM_SUBMIT_CMD_BUF:
+                       rd_write_section(rd, RD_CMDSTREAM_ADDR,
+                                       (uint32_t[2]){ iova, szd }, 8);
+                       break;
+               }
+       }
+}
+#endif
index b7d216264775511bcb807566047d9c69db26ff31..2b6156d0e4b5abb985a65a7ad321ddc4053a08f9 100644 (file)
@@ -102,6 +102,7 @@ nouveau-y += core/subdev/fb/nvaa.o
 nouveau-y += core/subdev/fb/nvaf.o
 nouveau-y += core/subdev/fb/nvc0.o
 nouveau-y += core/subdev/fb/nve0.o
+nouveau-y += core/subdev/fb/gk20a.o
 nouveau-y += core/subdev/fb/gm107.o
 nouveau-y += core/subdev/fb/ramnv04.o
 nouveau-y += core/subdev/fb/ramnv10.o
@@ -117,25 +118,32 @@ nouveau-y += core/subdev/fb/ramnva3.o
 nouveau-y += core/subdev/fb/ramnvaa.o
 nouveau-y += core/subdev/fb/ramnvc0.o
 nouveau-y += core/subdev/fb/ramnve0.o
+nouveau-y += core/subdev/fb/ramgk20a.o
 nouveau-y += core/subdev/fb/ramgm107.o
 nouveau-y += core/subdev/fb/sddr3.o
 nouveau-y += core/subdev/fb/gddr5.o
 nouveau-y += core/subdev/gpio/base.o
 nouveau-y += core/subdev/gpio/nv10.o
 nouveau-y += core/subdev/gpio/nv50.o
+nouveau-y += core/subdev/gpio/nv92.o
 nouveau-y += core/subdev/gpio/nvd0.o
 nouveau-y += core/subdev/gpio/nve0.o
 nouveau-y += core/subdev/i2c/base.o
 nouveau-y += core/subdev/i2c/anx9805.o
 nouveau-y += core/subdev/i2c/aux.o
 nouveau-y += core/subdev/i2c/bit.o
+nouveau-y += core/subdev/i2c/pad.o
+nouveau-y += core/subdev/i2c/padnv04.o
+nouveau-y += core/subdev/i2c/padnv94.o
 nouveau-y += core/subdev/i2c/nv04.o
 nouveau-y += core/subdev/i2c/nv4e.o
 nouveau-y += core/subdev/i2c/nv50.o
 nouveau-y += core/subdev/i2c/nv94.o
 nouveau-y += core/subdev/i2c/nvd0.o
+nouveau-y += core/subdev/i2c/nve0.o
 nouveau-y += core/subdev/ibus/nvc0.o
 nouveau-y += core/subdev/ibus/nve0.o
+nouveau-y += core/subdev/ibus/gk20a.o
 nouveau-y += core/subdev/instmem/base.o
 nouveau-y += core/subdev/instmem/nv04.o
 nouveau-y += core/subdev/instmem/nv40.o
@@ -214,6 +222,9 @@ nouveau-y += core/engine/device/nvc0.o
 nouveau-y += core/engine/device/nve0.o
 nouveau-y += core/engine/device/gm100.o
 nouveau-y += core/engine/disp/base.o
+nouveau-y += core/engine/disp/conn.o
+nouveau-y += core/engine/disp/outp.o
+nouveau-y += core/engine/disp/outpdp.o
 nouveau-y += core/engine/disp/nv04.o
 nouveau-y += core/engine/disp/nv50.o
 nouveau-y += core/engine/disp/nv84.o
@@ -245,6 +256,7 @@ nouveau-y += core/engine/fifo/nv50.o
 nouveau-y += core/engine/fifo/nv84.o
 nouveau-y += core/engine/fifo/nvc0.o
 nouveau-y += core/engine/fifo/nve0.o
+nouveau-y += core/engine/fifo/gk20a.o
 nouveau-y += core/engine/fifo/nv108.o
 nouveau-y += core/engine/graph/ctxnv40.o
 nouveau-y += core/engine/graph/ctxnv50.o
@@ -255,6 +267,7 @@ 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/ctxgk20a.o
 nouveau-y += core/engine/graph/ctxnvf0.o
 nouveau-y += core/engine/graph/ctxnv108.o
 nouveau-y += core/engine/graph/ctxgm107.o
@@ -275,6 +288,7 @@ 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/gk20a.o
 nouveau-y += core/engine/graph/nvf0.o
 nouveau-y += core/engine/graph/nv108.o
 nouveau-y += core/engine/graph/gm107.o
index 3f3c76581a9e3047f3716943f22c489ffea0c946..ae81d3b5d8b7170ea80c382d214dd753b6b61e1d 100644 (file)
@@ -28,14 +28,20 @@ nouveau_event_put(struct nouveau_eventh *handler)
 {
        struct nouveau_event *event = handler->event;
        unsigned long flags;
-       if (__test_and_clear_bit(NVKM_EVENT_ENABLE, &handler->flags)) {
-               spin_lock_irqsave(&event->refs_lock, flags);
-               if (!--event->index[handler->index].refs) {
+       u32 m, t;
+
+       if (!__test_and_clear_bit(NVKM_EVENT_ENABLE, &handler->flags))
+               return;
+
+       spin_lock_irqsave(&event->refs_lock, flags);
+       for (m = handler->types; t = __ffs(m), m; m &= ~(1 << t)) {
+               if (!--event->refs[handler->index * event->types_nr + t]) {
                        if (event->disable)
-                               event->disable(event, handler->index);
+                               event->disable(event, 1 << t, handler->index);
                }
-               spin_unlock_irqrestore(&event->refs_lock, flags);
+
        }
+       spin_unlock_irqrestore(&event->refs_lock, flags);
 }
 
 void
@@ -43,14 +49,20 @@ nouveau_event_get(struct nouveau_eventh *handler)
 {
        struct nouveau_event *event = handler->event;
        unsigned long flags;
-       if (!__test_and_set_bit(NVKM_EVENT_ENABLE, &handler->flags)) {
-               spin_lock_irqsave(&event->refs_lock, flags);
-               if (!event->index[handler->index].refs++) {
+       u32 m, t;
+
+       if (__test_and_set_bit(NVKM_EVENT_ENABLE, &handler->flags))
+               return;
+
+       spin_lock_irqsave(&event->refs_lock, flags);
+       for (m = handler->types; t = __ffs(m), m; m &= ~(1 << t)) {
+               if (!event->refs[handler->index * event->types_nr + t]++) {
                        if (event->enable)
-                               event->enable(event, handler->index);
+                               event->enable(event, 1 << t, handler->index);
                }
-               spin_unlock_irqrestore(&event->refs_lock, flags);
+
        }
+       spin_unlock_irqrestore(&event->refs_lock, flags);
 }
 
 static void
@@ -65,38 +77,47 @@ nouveau_event_fini(struct nouveau_eventh *handler)
 }
 
 static int
-nouveau_event_init(struct nouveau_event *event, int index,
-                  int (*func)(void *, int), void *priv,
+nouveau_event_init(struct nouveau_event *event, u32 types, int index,
+                  int (*func)(void *, u32, int), void *priv,
                   struct nouveau_eventh *handler)
 {
        unsigned long flags;
 
+       if (types & ~((1 << event->types_nr) - 1))
+               return -EINVAL;
        if (index >= event->index_nr)
                return -EINVAL;
 
        handler->event = event;
        handler->flags = 0;
+       handler->types = types;
        handler->index = index;
        handler->func = func;
        handler->priv = priv;
 
        spin_lock_irqsave(&event->list_lock, flags);
-       list_add_tail(&handler->head, &event->index[index].list);
+       list_add_tail(&handler->head, &event->list[index]);
        spin_unlock_irqrestore(&event->list_lock, flags);
        return 0;
 }
 
 int
-nouveau_event_new(struct nouveau_event *event, int index,
-                 int (*func)(void *, int), void *priv,
+nouveau_event_new(struct nouveau_event *event, u32 types, int index,
+                 int (*func)(void *, u32, int), void *priv,
                  struct nouveau_eventh **phandler)
 {
        struct nouveau_eventh *handler;
        int ret = -ENOMEM;
 
+       if (event->check) {
+               ret = event->check(event, types, index);
+               if (ret)
+                       return ret;
+       }
+
        handler = *phandler = kmalloc(sizeof(*handler), GFP_KERNEL);
        if (handler) {
-               ret = nouveau_event_init(event, index, func, priv, handler);
+               ret = nouveau_event_init(event, types, index, func, priv, handler);
                if (ret)
                        kfree(handler);
        }
@@ -116,7 +137,7 @@ nouveau_event_ref(struct nouveau_eventh *handler, struct nouveau_eventh **ref)
 }
 
 void
-nouveau_event_trigger(struct nouveau_event *event, int index)
+nouveau_event_trigger(struct nouveau_event *event, u32 types, int index)
 {
        struct nouveau_eventh *handler;
        unsigned long flags;
@@ -125,10 +146,15 @@ nouveau_event_trigger(struct nouveau_event *event, int index)
                return;
 
        spin_lock_irqsave(&event->list_lock, flags);
-       list_for_each_entry(handler, &event->index[index].list, head) {
-               if (test_bit(NVKM_EVENT_ENABLE, &handler->flags) &&
-                   handler->func(handler->priv, index) == NVKM_EVENT_DROP)
-                       nouveau_event_put(handler);
+       list_for_each_entry(handler, &event->list[index], head) {
+               if (!test_bit(NVKM_EVENT_ENABLE, &handler->flags))
+                       continue;
+               if (!(handler->types & types))
+                       continue;
+               if (handler->func(handler->priv, handler->types & types, index)
+                               != NVKM_EVENT_DROP)
+                       continue;
+               nouveau_event_put(handler);
        }
        spin_unlock_irqrestore(&event->list_lock, flags);
 }
@@ -144,20 +170,27 @@ nouveau_event_destroy(struct nouveau_event **pevent)
 }
 
 int
-nouveau_event_create(int index_nr, struct nouveau_event **pevent)
+nouveau_event_create(int types_nr, int index_nr, struct nouveau_event **pevent)
 {
        struct nouveau_event *event;
        int i;
 
-       event = *pevent = kzalloc(sizeof(*event) + index_nr *
-                                 sizeof(event->index[0]), GFP_KERNEL);
+       event = *pevent = kzalloc(sizeof(*event) + (index_nr * types_nr) *
+                                 sizeof(event->refs[0]), GFP_KERNEL);
        if (!event)
                return -ENOMEM;
 
+       event->list = kmalloc(sizeof(*event->list) * index_nr, GFP_KERNEL);
+       if (!event->list) {
+               kfree(event);
+               return -ENOMEM;
+       }
+
        spin_lock_init(&event->list_lock);
        spin_lock_init(&event->refs_lock);
        for (i = 0; i < index_nr; i++)
-               INIT_LIST_HEAD(&event->index[i].list);
+               INIT_LIST_HEAD(&event->list[i]);
+       event->types_nr = types_nr;
        event->index_nr = index_nr;
        return 0;
 }
index 7f48e288215f0ca2427b6bb0095db86042846f5a..12453855590403142578455e50bec1d9ad059b1e 100644 (file)
@@ -156,7 +156,7 @@ nouveau_object_ctor(struct nouveau_object *parent,
        }
 
        if (ret == 0) {
-               nv_debug(object, "created\n");
+               nv_trace(object, "created\n");
                atomic_set(&object->refcount, 1);
        }
 
@@ -166,7 +166,7 @@ nouveau_object_ctor(struct nouveau_object *parent,
 static void
 nouveau_object_dtor(struct nouveau_object *object)
 {
-       nv_debug(object, "destroying\n");
+       nv_trace(object, "destroying\n");
        nv_ofuncs(object)->dtor(object);
 }
 
@@ -337,7 +337,7 @@ nouveau_object_inc(struct nouveau_object *object)
                goto fail_self;
        }
 
-       nv_debug(object, "initialised\n");
+       nv_trace(object, "initialised\n");
        return 0;
 
 fail_self:
@@ -375,7 +375,7 @@ nouveau_object_decf(struct nouveau_object *object)
        if (object->parent)
                nouveau_object_dec(object->parent, false);
 
-       nv_debug(object, "stopped\n");
+       nv_trace(object, "stopped\n");
        return 0;
 }
 
@@ -411,7 +411,7 @@ nouveau_object_decs(struct nouveau_object *object)
                }
        }
 
-       nv_debug(object, "suspended\n");
+       nv_trace(object, "suspended\n");
        return 0;
 
 fail_parent:
index d258c21c4a225d01b43cdc074a0bae0715f138bf..a520029e25d938818ae0b6193c1c516aabf63a19 100644 (file)
@@ -60,8 +60,8 @@ gm100_identify(struct nouveau_device *device)
        case 0x117:
                device->cname = "GM107";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nve0_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nve0_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nvd0_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nve0_clock_oclass;
 #if 0
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
index 0a51ff4e9e00547b4649f43b2eadf55f86d44300..40b29d0214cb162ed6de884eb08ba98c548ee1ee 100644 (file)
@@ -47,7 +47,7 @@ nv04_identify(struct nouveau_device *device)
        case 0x04:
                device->cname = "NV04";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv04_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
@@ -65,7 +65,7 @@ nv04_identify(struct nouveau_device *device)
        case 0x05:
                device->cname = "NV05";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv05_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
index e008de8b51b01927468c3f465c11ee9cd4b0c932..5f7c25ff523d181d2b20fa9480bb72809a782bcf 100644 (file)
@@ -48,8 +48,8 @@ nv10_identify(struct nouveau_device *device)
        case 0x10:
                device->cname = "NV10";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv10_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
@@ -65,8 +65,8 @@ nv10_identify(struct nouveau_device *device)
        case 0x15:
                device->cname = "NV15";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv10_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
@@ -84,8 +84,8 @@ nv10_identify(struct nouveau_device *device)
        case 0x16:
                device->cname = "NV16";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv10_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
@@ -103,8 +103,8 @@ nv10_identify(struct nouveau_device *device)
        case 0x1a:
                device->cname = "nForce";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
@@ -122,8 +122,8 @@ nv10_identify(struct nouveau_device *device)
        case 0x11:
                device->cname = "NV11";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv10_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
@@ -141,8 +141,8 @@ nv10_identify(struct nouveau_device *device)
        case 0x17:
                device->cname = "NV17";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv10_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
@@ -160,8 +160,8 @@ nv10_identify(struct nouveau_device *device)
        case 0x1f:
                device->cname = "nForce2";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv1a_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
@@ -179,8 +179,8 @@ nv10_identify(struct nouveau_device *device)
        case 0x18:
                device->cname = "NV18";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv10_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
index 7b629a3aed059649e5b64b071370b9781e2b75d5..75fed11bba0ac9e7b04e66ecb95d16b9ed314170 100644 (file)
@@ -49,8 +49,8 @@ nv20_identify(struct nouveau_device *device)
        case 0x20:
                device->cname = "NV20";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv20_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
@@ -68,8 +68,8 @@ nv20_identify(struct nouveau_device *device)
        case 0x25:
                device->cname = "NV25";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv20_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
@@ -87,8 +87,8 @@ nv20_identify(struct nouveau_device *device)
        case 0x28:
                device->cname = "NV28";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv20_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
@@ -106,8 +106,8 @@ nv20_identify(struct nouveau_device *device)
        case 0x2a:
                device->cname = "NV2A";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv20_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
index 7dfddd5a1908fc517ac2e2f47359b787a08ec615..36919d7db7ccb5250faaab837311ead535ba42ac 100644 (file)
@@ -49,8 +49,8 @@ nv30_identify(struct nouveau_device *device)
        case 0x30:
                device->cname = "NV30";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv20_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
@@ -68,8 +68,8 @@ nv30_identify(struct nouveau_device *device)
        case 0x35:
                device->cname = "NV35";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv20_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
@@ -87,8 +87,8 @@ nv30_identify(struct nouveau_device *device)
        case 0x31:
                device->cname = "NV31";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv20_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
@@ -107,8 +107,8 @@ nv30_identify(struct nouveau_device *device)
        case 0x36:
                device->cname = "NV36";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv20_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
@@ -127,8 +127,8 @@ nv30_identify(struct nouveau_device *device)
        case 0x34:
                device->cname = "NV34";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv04_clock_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv10_devinit_oclass;
                device->oclass[NVDEV_SUBDEV_MC     ] =  nv04_mc_oclass;
index 7c1ce6cf4f1f6ba931260d52cf2ea0000c61bfc5..1130a62be2c7cfaec6196529bd228fb0d583a9c6 100644 (file)
@@ -53,8 +53,8 @@ nv40_identify(struct nouveau_device *device)
        case 0x40:
                device->cname = "NV40";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv1a_devinit_oclass;
@@ -76,8 +76,8 @@ nv40_identify(struct nouveau_device *device)
        case 0x41:
                device->cname = "NV41";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv1a_devinit_oclass;
@@ -99,8 +99,8 @@ nv40_identify(struct nouveau_device *device)
        case 0x42:
                device->cname = "NV42";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv1a_devinit_oclass;
@@ -122,8 +122,8 @@ nv40_identify(struct nouveau_device *device)
        case 0x43:
                device->cname = "NV43";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv1a_devinit_oclass;
@@ -145,8 +145,8 @@ nv40_identify(struct nouveau_device *device)
        case 0x45:
                device->cname = "NV45";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv1a_devinit_oclass;
@@ -168,8 +168,8 @@ nv40_identify(struct nouveau_device *device)
        case 0x47:
                device->cname = "G70";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv1a_devinit_oclass;
@@ -191,8 +191,8 @@ nv40_identify(struct nouveau_device *device)
        case 0x49:
                device->cname = "G71";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv1a_devinit_oclass;
@@ -214,8 +214,8 @@ nv40_identify(struct nouveau_device *device)
        case 0x4b:
                device->cname = "G73";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv1a_devinit_oclass;
@@ -237,8 +237,8 @@ nv40_identify(struct nouveau_device *device)
        case 0x44:
                device->cname = "NV44";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv1a_devinit_oclass;
@@ -260,8 +260,8 @@ nv40_identify(struct nouveau_device *device)
        case 0x46:
                device->cname = "G72";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv1a_devinit_oclass;
@@ -283,8 +283,8 @@ nv40_identify(struct nouveau_device *device)
        case 0x4a:
                device->cname = "NV44A";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv1a_devinit_oclass;
@@ -306,8 +306,8 @@ nv40_identify(struct nouveau_device *device)
        case 0x4c:
                device->cname = "C61";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv1a_devinit_oclass;
@@ -329,8 +329,8 @@ nv40_identify(struct nouveau_device *device)
        case 0x4e:
                device->cname = "C51";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv4e_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv4e_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv1a_devinit_oclass;
@@ -352,8 +352,8 @@ nv40_identify(struct nouveau_device *device)
        case 0x63:
                device->cname = "C73";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv1a_devinit_oclass;
@@ -375,8 +375,8 @@ nv40_identify(struct nouveau_device *device)
        case 0x67:
                device->cname = "C67";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv1a_devinit_oclass;
@@ -398,8 +398,8 @@ nv40_identify(struct nouveau_device *device)
        case 0x68:
                device->cname = "C68";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv10_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv04_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv10_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv04_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nv40_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv40_therm_oclass;
                device->oclass[NVDEV_SUBDEV_DEVINIT] =  nv1a_devinit_oclass;
index 66499fa0f7589000a2ddfd263cddf5037c592c4f..ef0b0bde1a91d5d8502529cf69859a9efb2503a5 100644 (file)
@@ -60,8 +60,8 @@ nv50_identify(struct nouveau_device *device)
        case 0x50:
                device->cname = "G80";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv50_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv50_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv50_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] =  nv50_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv50_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -85,8 +85,8 @@ nv50_identify(struct nouveau_device *device)
        case 0x84:
                device->cname = "G84";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv50_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv50_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv50_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] =  nv84_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv84_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -113,8 +113,8 @@ nv50_identify(struct nouveau_device *device)
        case 0x86:
                device->cname = "G86";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv50_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv50_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv50_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] =  nv84_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv84_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -141,8 +141,8 @@ nv50_identify(struct nouveau_device *device)
        case 0x92:
                device->cname = "G92";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv50_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv92_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv50_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] =  nv84_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv84_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -169,8 +169,8 @@ nv50_identify(struct nouveau_device *device)
        case 0x94:
                device->cname = "G94";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv92_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] =  nv84_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv84_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -197,8 +197,8 @@ nv50_identify(struct nouveau_device *device)
        case 0x96:
                device->cname = "G96";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv92_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] =  nv84_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv84_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -225,8 +225,8 @@ nv50_identify(struct nouveau_device *device)
        case 0x98:
                device->cname = "G98";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv92_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] =  nv84_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv84_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -253,8 +253,8 @@ nv50_identify(struct nouveau_device *device)
        case 0xa0:
                device->cname = "G200";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv50_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv92_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv50_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] =  nv84_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv84_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -281,8 +281,8 @@ nv50_identify(struct nouveau_device *device)
        case 0xaa:
                device->cname = "MCP77/MCP78";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv92_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] =  nvaa_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv84_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -309,8 +309,8 @@ nv50_identify(struct nouveau_device *device)
        case 0xac:
                device->cname = "MCP79/MCP7A";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv92_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] =  nvaa_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nv84_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -337,8 +337,8 @@ nv50_identify(struct nouveau_device *device)
        case 0xa3:
                device->cname = "GT215";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv92_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nva3_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -367,8 +367,8 @@ nv50_identify(struct nouveau_device *device)
        case 0xa5:
                device->cname = "GT216";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv92_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nva3_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -396,8 +396,8 @@ nv50_identify(struct nouveau_device *device)
        case 0xa8:
                device->cname = "GT218";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv92_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nva3_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -425,8 +425,8 @@ nv50_identify(struct nouveau_device *device)
        case 0xaf:
                device->cname = "MCP89";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv92_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nva3_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
index 2075b3027052c349f7b714036c2cf358d43871ae..f199957995facf518a3f8aa9d0ad34cbba9eadf5 100644 (file)
@@ -60,8 +60,8 @@ nvc0_identify(struct nouveau_device *device)
        case 0xc0:
                device->cname = "GF100";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv92_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -92,8 +92,8 @@ nvc0_identify(struct nouveau_device *device)
        case 0xc4:
                device->cname = "GF104";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv92_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -124,8 +124,8 @@ nvc0_identify(struct nouveau_device *device)
        case 0xc3:
                device->cname = "GF106";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv92_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -155,8 +155,8 @@ nvc0_identify(struct nouveau_device *device)
        case 0xce:
                device->cname = "GF114";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv92_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -187,8 +187,8 @@ nvc0_identify(struct nouveau_device *device)
        case 0xcf:
                device->cname = "GF116";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv92_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -219,8 +219,8 @@ nvc0_identify(struct nouveau_device *device)
        case 0xc1:
                device->cname = "GF108";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv92_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -250,8 +250,8 @@ nvc0_identify(struct nouveau_device *device)
        case 0xc8:
                device->cname = "GF110";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nv50_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nv94_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nv92_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nv94_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nva3_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -282,8 +282,8 @@ nvc0_identify(struct nouveau_device *device)
        case 0xd9:
                device->cname = "GF119";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nvd0_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nvd0_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nvd0_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -313,8 +313,8 @@ nvc0_identify(struct nouveau_device *device)
        case 0xd7:
                device->cname = "GF117";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nvd0_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nvd0_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nvd0_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nvc0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
index 9784cbf8a9d20d2cf7d9706ca0fa5a571a720d74..2d1e97d4264f862fb72a671fa8154ee7cb83a55c 100644 (file)
@@ -60,8 +60,8 @@ nve0_identify(struct nouveau_device *device)
        case 0xe4:
                device->cname = "GK104";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nve0_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nve0_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nve0_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nve0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -93,8 +93,8 @@ nve0_identify(struct nouveau_device *device)
        case 0xe7:
                device->cname = "GK107";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nve0_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nve0_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nve0_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nve0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -126,8 +126,8 @@ nve0_identify(struct nouveau_device *device)
        case 0xe6:
                device->cname = "GK106";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nve0_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nve0_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nve0_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nve0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -156,11 +156,61 @@ nve0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_ENGINE_PPP    ] = &nvc0_ppp_oclass;
                device->oclass[NVDEV_ENGINE_PERFMON] = &nve0_perfmon_oclass;
                break;
+       case 0xea:
+               device->cname = "GK20A";
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nvc3_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nvc0_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_TIMER  ] = &gk20a_timer_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  gk20a_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_IBUS   ] = &gk20a_ibus_oclass;
+               device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
+               device->oclass[NVDEV_SUBDEV_VM     ] = &nvc0_vmmgr_oclass;
+               device->oclass[NVDEV_SUBDEV_BAR    ] = &nvc0_bar_oclass;
+               device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  gk20a_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nvc0_software_oclass;
+               device->oclass[NVDEV_ENGINE_GR     ] =  gk20a_graph_oclass;
+               device->oclass[NVDEV_ENGINE_COPY2  ] = &nve0_copy2_oclass;
+               device->oclass[NVDEV_ENGINE_PERFMON] = &nve0_perfmon_oclass;
+               break;
        case 0xf0:
                device->cname = "GK110";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nve0_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nve0_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nve0_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nve0_clock_oclass;
+               device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
+               device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
+               device->oclass[NVDEV_SUBDEV_DEVINIT] =  nvc0_devinit_oclass;
+               device->oclass[NVDEV_SUBDEV_MC     ] =  nvc3_mc_oclass;
+               device->oclass[NVDEV_SUBDEV_BUS    ] =  nvc0_bus_oclass;
+               device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+               device->oclass[NVDEV_SUBDEV_FB     ] =  nve0_fb_oclass;
+               device->oclass[NVDEV_SUBDEV_LTCG   ] =  gf100_ltcg_oclass;
+               device->oclass[NVDEV_SUBDEV_IBUS   ] = &nve0_ibus_oclass;
+               device->oclass[NVDEV_SUBDEV_INSTMEM] =  nv50_instmem_oclass;
+               device->oclass[NVDEV_SUBDEV_VM     ] = &nvc0_vmmgr_oclass;
+               device->oclass[NVDEV_SUBDEV_BAR    ] = &nvc0_bar_oclass;
+               device->oclass[NVDEV_SUBDEV_PWR    ] = &nvd0_pwr_oclass;
+               device->oclass[NVDEV_SUBDEV_VOLT   ] = &nv40_volt_oclass;
+               device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass;
+               device->oclass[NVDEV_ENGINE_FIFO   ] =  nve0_fifo_oclass;
+               device->oclass[NVDEV_ENGINE_SW     ] =  nvc0_software_oclass;
+               device->oclass[NVDEV_ENGINE_GR     ] =  nvf0_graph_oclass;
+               device->oclass[NVDEV_ENGINE_DISP   ] =  nvf0_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;
+               device->oclass[NVDEV_ENGINE_PERFMON] = &nvf0_perfmon_oclass;
+               break;
+       case 0xf1:
+               device->cname = "GK110B";
+               device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nve0_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nvd0_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nve0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -184,18 +234,16 @@ nve0_identify(struct nouveau_device *device)
                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;
-#endif
                device->oclass[NVDEV_ENGINE_PERFMON] = &nvf0_perfmon_oclass;
                break;
        case 0x108:
                device->cname = "GK208";
                device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
-               device->oclass[NVDEV_SUBDEV_GPIO   ] = &nve0_gpio_oclass;
-               device->oclass[NVDEV_SUBDEV_I2C    ] = &nvd0_i2c_oclass;
+               device->oclass[NVDEV_SUBDEV_GPIO   ] =  nve0_gpio_oclass;
+               device->oclass[NVDEV_SUBDEV_I2C    ] =  nve0_i2c_oclass;
                device->oclass[NVDEV_SUBDEV_CLOCK  ] = &nve0_clock_oclass;
                device->oclass[NVDEV_SUBDEV_THERM  ] = &nvd0_therm_oclass;
                device->oclass[NVDEV_SUBDEV_MXM    ] = &nv50_mxm_oclass;
@@ -219,11 +267,9 @@ nve0_identify(struct nouveau_device *device)
                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;
-#endif
                break;
        default:
                nv_fatal(device, "unknown Kepler chipset\n");
index 7a5cae42834f950c222778cbf44bd7dee5240972..c41f656abe64cfaab5225fc49a67234c618f97bb 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <engine/disp.h>
+#include "priv.h"
+#include "outp.h"
+#include "conn.h"
+
+static int
+nouveau_disp_hpd_check(struct nouveau_event *event, u32 types, int index)
+{
+       struct nouveau_disp *disp = event->priv;
+       struct nvkm_output *outp;
+       list_for_each_entry(outp, &disp->outp, head) {
+               if (outp->conn->index == index) {
+                       if (outp->conn->hpd.event)
+                               return 0;
+                       break;
+               }
+       }
+       return -ENOSYS;
+}
+
+int
+_nouveau_disp_fini(struct nouveau_object *object, bool suspend)
+{
+       struct nouveau_disp *disp = (void *)object;
+       struct nvkm_output *outp;
+       int ret;
+
+       list_for_each_entry(outp, &disp->outp, head) {
+               ret = nv_ofuncs(outp)->fini(nv_object(outp), suspend);
+               if (ret && suspend)
+                       goto fail_outp;
+       }
+
+       return nouveau_engine_fini(&disp->base, suspend);
+
+fail_outp:
+       list_for_each_entry_continue_reverse(outp, &disp->outp, head) {
+               nv_ofuncs(outp)->init(nv_object(outp));
+       }
+
+       return ret;
+}
+
+int
+_nouveau_disp_init(struct nouveau_object *object)
+{
+       struct nouveau_disp *disp = (void *)object;
+       struct nvkm_output *outp;
+       int ret;
+
+       ret = nouveau_engine_init(&disp->base);
+       if (ret)
+               return ret;
+
+       list_for_each_entry(outp, &disp->outp, head) {
+               ret = nv_ofuncs(outp)->init(nv_object(outp));
+               if (ret)
+                       goto fail_outp;
+       }
+
+       return ret;
+
+fail_outp:
+       list_for_each_entry_continue_reverse(outp, &disp->outp, head) {
+               nv_ofuncs(outp)->fini(nv_object(outp), false);
+       }
+
+       return ret;
+}
 
 void
 _nouveau_disp_dtor(struct nouveau_object *object)
 {
        struct nouveau_disp *disp = (void *)object;
+       struct nvkm_output *outp, *outt;
+
        nouveau_event_destroy(&disp->vblank);
+
+       list_for_each_entry_safe(outp, outt, &disp->outp, head) {
+               nouveau_object_ref(NULL, (struct nouveau_object **)&outp);
+       }
+
        nouveau_engine_destroy(&disp->base);
 }
 
@@ -39,8 +113,15 @@ nouveau_disp_create_(struct nouveau_object *parent,
                     const char *intname, const char *extname,
                     int length, void **pobject)
 {
+       struct nouveau_disp_impl *impl = (void *)oclass;
+       struct nouveau_bios *bios = nouveau_bios(parent);
        struct nouveau_disp *disp;
-       int ret;
+       struct nouveau_oclass **sclass;
+       struct nouveau_object *object;
+       struct dcb_output dcbE;
+       u8  hpd = 0, ver, hdr;
+       u32 data;
+       int ret, i;
 
        ret = nouveau_engine_create_(parent, engine, oclass, true,
                                     intname, extname, length, pobject);
@@ -48,5 +129,42 @@ nouveau_disp_create_(struct nouveau_object *parent,
        if (ret)
                return ret;
 
-       return nouveau_event_create(heads, &disp->vblank);
+       INIT_LIST_HEAD(&disp->outp);
+
+       /* create output objects for each display path in the vbios */
+       i = -1;
+       while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) {
+               if (dcbE.type == DCB_OUTPUT_UNUSED)
+                       continue;
+               if (dcbE.type == DCB_OUTPUT_EOL)
+                       break;
+               data = dcbE.location << 4 | dcbE.type;
+
+               oclass = nvkm_output_oclass;
+               sclass = impl->outp;
+               while (sclass && sclass[0]) {
+                       if (sclass[0]->handle == data) {
+                               oclass = sclass[0];
+                               break;
+                       }
+                       sclass++;
+               }
+
+               nouveau_object_ctor(*pobject, *pobject, oclass,
+                                   &dcbE, i, &object);
+               hpd = max(hpd, (u8)(dcbE.connector + 1));
+       }
+
+       ret = nouveau_event_create(3, hpd, &disp->hpd);
+       if (ret)
+               return ret;
+
+       disp->hpd->priv = disp;
+       disp->hpd->check = nouveau_disp_hpd_check;
+
+       ret = nouveau_event_create(1, heads, &disp->vblank);
+       if (ret)
+               return ret;
+
+       return 0;
 }
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/conn.c b/drivers/gpu/drm/nouveau/core/engine/disp/conn.c
new file mode 100644 (file)
index 0000000..4ffbc70
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/gpio.h>
+
+#include "conn.h"
+#include "outp.h"
+
+static void
+nvkm_connector_hpd_work(struct work_struct *w)
+{
+       struct nvkm_connector *conn = container_of(w, typeof(*conn), hpd.work);
+       struct nouveau_disp *disp = nouveau_disp(conn);
+       struct nouveau_gpio *gpio = nouveau_gpio(conn);
+       u32 send = NVKM_HPD_UNPLUG;
+       if (gpio->get(gpio, 0, DCB_GPIO_UNUSED, conn->hpd.event->index))
+               send = NVKM_HPD_PLUG;
+       nouveau_event_trigger(disp->hpd, send, conn->index);
+       nouveau_event_get(conn->hpd.event);
+}
+
+static int
+nvkm_connector_hpd(void *data, u32 type, int index)
+{
+       struct nvkm_connector *conn = data;
+       DBG("HPD: %d\n", type);
+       schedule_work(&conn->hpd.work);
+       return NVKM_EVENT_DROP;
+}
+
+int
+_nvkm_connector_fini(struct nouveau_object *object, bool suspend)
+{
+       struct nvkm_connector *conn = (void *)object;
+       if (conn->hpd.event)
+               nouveau_event_put(conn->hpd.event);
+       return nouveau_object_fini(&conn->base, suspend);
+}
+
+int
+_nvkm_connector_init(struct nouveau_object *object)
+{
+       struct nvkm_connector *conn = (void *)object;
+       int ret = nouveau_object_init(&conn->base);
+       if (ret == 0) {
+               if (conn->hpd.event)
+                       nouveau_event_get(conn->hpd.event);
+       }
+       return ret;
+}
+
+void
+_nvkm_connector_dtor(struct nouveau_object *object)
+{
+       struct nvkm_connector *conn = (void *)object;
+       nouveau_event_ref(NULL, &conn->hpd.event);
+       nouveau_object_destroy(&conn->base);
+}
+
+int
+nvkm_connector_create_(struct nouveau_object *parent,
+                      struct nouveau_object *engine,
+                      struct nouveau_oclass *oclass,
+                      struct nvbios_connE *info, int index,
+                      int length, void **pobject)
+{
+       static const u8 hpd[] = { 0x07, 0x08, 0x51, 0x52, 0x5e, 0x5f, 0x60 };
+       struct nouveau_gpio *gpio = nouveau_gpio(parent);
+       struct nouveau_disp *disp = (void *)engine;
+       struct nvkm_connector *conn;
+       struct nvkm_output *outp;
+       struct dcb_gpio_func func;
+       int ret;
+
+       list_for_each_entry(outp, &disp->outp, head) {
+               if (outp->conn && outp->conn->index == index) {
+                       atomic_inc(&nv_object(outp->conn)->refcount);
+                       *pobject = outp->conn;
+                       return 1;
+               }
+       }
+
+       ret = nouveau_object_create_(parent, engine, oclass, 0, length, pobject);
+       conn = *pobject;
+       if (ret)
+               return ret;
+
+       conn->info = *info;
+       conn->index = index;
+
+       DBG("type %02x loc %d hpd %02x dp %x di %x sr %x lcdid %x\n",
+           info->type, info->location, info->hpd, info->dp,
+           info->di, info->sr, info->lcdid);
+
+       if ((info->hpd = ffs(info->hpd))) {
+               if (--info->hpd >= ARRAY_SIZE(hpd)) {
+                       ERR("hpd %02x unknown\n", info->hpd);
+                       goto done;
+               }
+               info->hpd = hpd[info->hpd];
+
+               ret = gpio->find(gpio, 0, info->hpd, DCB_GPIO_UNUSED, &func);
+               if (ret) {
+                       ERR("func %02x lookup failed, %d\n", info->hpd, ret);
+                       goto done;
+               }
+
+               ret = nouveau_event_new(gpio->events, NVKM_GPIO_TOGGLED,
+                                       func.line, nvkm_connector_hpd,
+                                       conn, &conn->hpd.event);
+               if (ret) {
+                       ERR("func %02x failed, %d\n", info->hpd, ret);
+               } else {
+                       DBG("func %02x (HPD)\n", info->hpd);
+               }
+       }
+
+done:
+       INIT_WORK(&conn->hpd.work, nvkm_connector_hpd_work);
+       return 0;
+}
+
+int
+_nvkm_connector_ctor(struct nouveau_object *parent,
+                    struct nouveau_object *engine,
+                    struct nouveau_oclass *oclass, void *info, u32 index,
+                    struct nouveau_object **pobject)
+{
+       struct nvkm_connector *conn;
+       int ret;
+
+       ret = nvkm_connector_create(parent, engine, oclass, info, index, &conn);
+       *pobject = nv_object(conn);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+struct nouveau_oclass *
+nvkm_connector_oclass = &(struct nvkm_connector_impl) {
+       .base = {
+               .handle = 0,
+               .ofuncs = &(struct nouveau_ofuncs) {
+                       .ctor = _nvkm_connector_ctor,
+                       .dtor = _nvkm_connector_dtor,
+                       .init = _nvkm_connector_init,
+                       .fini = _nvkm_connector_fini,
+               },
+       },
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/conn.h b/drivers/gpu/drm/nouveau/core/engine/disp/conn.h
new file mode 100644 (file)
index 0000000..035ebea
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef __NVKM_DISP_CONN_H__
+#define __NVKM_DISP_CONN_H__
+
+#include "priv.h"
+
+struct nvkm_connector {
+       struct nouveau_object base;
+       struct list_head head;
+
+       struct nvbios_connE info;
+       int index;
+
+       struct {
+               struct nouveau_eventh *event;
+               struct work_struct work;
+       } hpd;
+};
+
+#define nvkm_connector_create(p,e,c,b,i,d)                                     \
+       nvkm_connector_create_((p), (e), (c), (b), (i), sizeof(**d), (void **)d)
+#define nvkm_connector_destroy(d) ({                                           \
+       struct nvkm_connector *disp = (d);                                     \
+       _nvkm_connector_dtor(nv_object(disp));                                 \
+})
+#define nvkm_connector_init(d) ({                                              \
+       struct nvkm_connector *disp = (d);                                     \
+       _nvkm_connector_init(nv_object(disp));                                 \
+})
+#define nvkm_connector_fini(d,s) ({                                            \
+       struct nvkm_connector *disp = (d);                                     \
+       _nvkm_connector_fini(nv_object(disp), (s));                            \
+})
+
+int nvkm_connector_create_(struct nouveau_object *, struct nouveau_object *,
+                          struct nouveau_oclass *, struct nvbios_connE *,
+                          int, int, void **);
+
+int  _nvkm_connector_ctor(struct nouveau_object *, struct nouveau_object *,
+                         struct nouveau_oclass *, void *, u32,
+                         struct nouveau_object **);
+void _nvkm_connector_dtor(struct nouveau_object *);
+int  _nvkm_connector_init(struct nouveau_object *);
+int  _nvkm_connector_fini(struct nouveau_object *, bool);
+
+struct nvkm_connector_impl {
+       struct nouveau_oclass base;
+};
+
+#ifndef MSG
+#define MSG(l,f,a...) do {                                                     \
+       struct nvkm_connector *_conn = (void *)conn;                           \
+       nv_##l(nv_object(conn)->engine, "%02x:%02x%02x: "f, _conn->index,      \
+              _conn->info.location, _conn->info.type, ##a);                   \
+} while(0)
+#define DBG(f,a...) MSG(debug, f, ##a)
+#define ERR(f,a...) MSG(error, f, ##a)
+#endif
+
+#endif
index 3ca2d25b7f5e023b917dbd4dd246cd8e3fe64031..39562d48101dcfd4b02be892ea568ad20f87bf73 100644 (file)
 
 #include <engine/disp.h>
 
-#include "dport.h"
+#include <core/class.h>
 
-#define DBG(fmt, args...) nv_debug(dp->disp, "DP:%04x:%04x: " fmt,             \
-                                  dp->outp->hasht, dp->outp->hashm, ##args)
-#define ERR(fmt, args...) nv_error(dp->disp, "DP:%04x:%04x: " fmt,             \
-                                  dp->outp->hasht, dp->outp->hashm, ##args)
+#include "dport.h"
+#include "outpdp.h"
 
 /******************************************************************************
  * link training
  *****************************************************************************/
 struct dp_state {
-       const struct nouveau_dp_func *func;
-       struct nouveau_disp *disp;
-       struct dcb_output *outp;
-       struct nvbios_dpout info;
-       u8 version;
-       struct nouveau_i2c_port *aux;
-       int head;
-       u8  dpcd[4];
+       struct nvkm_output_dp *outp;
        int link_nr;
        u32 link_bw;
        u8  stat[6];
        u8  conf[4];
+       bool pc2;
+       u8  pc2stat;
+       u8  pc2conf[2];
 };
 
 static int
 dp_set_link_config(struct dp_state *dp)
 {
-       struct nouveau_disp *disp = dp->disp;
+       struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp);
+       struct nvkm_output_dp *outp = dp->outp;
+       struct nouveau_disp *disp = nouveau_disp(outp);
        struct nouveau_bios *bios = nouveau_bios(disp);
        struct nvbios_init init = {
-               .subdev = nv_subdev(dp->disp),
+               .subdev = nv_subdev(disp),
                .bios = bios,
                .offset = 0x0000,
-               .outp = dp->outp,
-               .crtc = dp->head,
+               .outp = &outp->base.info,
+               .crtc = -1,
                .execute = 1,
        };
        u32 lnkcmp;
@@ -75,8 +71,8 @@ dp_set_link_config(struct dp_state *dp)
        DBG("%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw);
 
        /* set desired link configuration on the source */
-       if ((lnkcmp = dp->info.lnkcmp)) {
-               if (dp->version < 0x30) {
+       if ((lnkcmp = dp->outp->info.lnkcmp)) {
+               if (outp->version < 0x30) {
                        while ((dp->link_bw / 10) < nv_ro16(bios, lnkcmp))
                                lnkcmp += 4;
                        init.offset = nv_ro16(bios, lnkcmp + 2);
@@ -89,73 +85,112 @@ dp_set_link_config(struct dp_state *dp)
                nvbios_exec(&init);
        }
 
-       ret = dp->func->lnk_ctl(dp->disp, dp->outp, dp->head,
-                               dp->link_nr, dp->link_bw / 27000,
-                               dp->dpcd[DPCD_RC02] &
-                                        DPCD_RC02_ENHANCED_FRAME_CAP);
+       ret = impl->lnk_ctl(outp, dp->link_nr, dp->link_bw / 27000,
+                           outp->dpcd[DPCD_RC02] &
+                                      DPCD_RC02_ENHANCED_FRAME_CAP);
        if (ret) {
-               ERR("lnk_ctl failed with %d\n", ret);
+               if (ret < 0)
+                       ERR("lnk_ctl failed with %d\n", ret);
                return ret;
        }
 
+       impl->lnk_pwr(outp, dp->link_nr);
+
        /* set desired link configuration on the sink */
        sink[0] = dp->link_bw / 27000;
        sink[1] = dp->link_nr;
-       if (dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP)
+       if (outp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP)
                sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN;
 
-       return nv_wraux(dp->aux, DPCD_LC00, sink, 2);
+       return nv_wraux(outp->base.edid, DPCD_LC00_LINK_BW_SET, sink, 2);
 }
 
 static void
 dp_set_training_pattern(struct dp_state *dp, u8 pattern)
 {
+       struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp);
+       struct nvkm_output_dp *outp = dp->outp;
        u8 sink_tp;
 
        DBG("training pattern %d\n", pattern);
-       dp->func->pattern(dp->disp, dp->outp, dp->head, pattern);
+       impl->pattern(outp, pattern);
 
-       nv_rdaux(dp->aux, DPCD_LC02, &sink_tp, 1);
+       nv_rdaux(outp->base.edid, DPCD_LC02, &sink_tp, 1);
        sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET;
        sink_tp |= pattern;
-       nv_wraux(dp->aux, DPCD_LC02, &sink_tp, 1);
+       nv_wraux(outp->base.edid, DPCD_LC02, &sink_tp, 1);
 }
 
 static int
-dp_link_train_commit(struct dp_state *dp)
+dp_link_train_commit(struct dp_state *dp, bool pc)
 {
-       int i;
+       struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp);
+       struct nvkm_output_dp *outp = dp->outp;
+       int ret, i;
 
        for (i = 0; i < dp->link_nr; i++) {
                u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf;
+               u8 lpc2 = (dp->pc2stat >> (i * 2)) & 0x3;
                u8 lpre = (lane & 0x0c) >> 2;
                u8 lvsw = (lane & 0x03) >> 0;
+               u8 hivs = 3 - lpre;
+               u8 hipe = 3;
+               u8 hipc = 3;
+
+               if (lpc2 >= hipc)
+                       lpc2 = hipc | DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED;
+               if (lpre >= hipe) {
+                       lpre = hipe | DPCD_LC03_MAX_SWING_REACHED; /* yes. */
+                       lvsw = hivs = 3 - (lpre & 3);
+               } else
+               if (lvsw >= hivs) {
+                       lvsw = hivs | DPCD_LC03_MAX_SWING_REACHED;
+               }
 
                dp->conf[i] = (lpre << 3) | lvsw;
-               if (lvsw == 3)
-                       dp->conf[i] |= DPCD_LC03_MAX_SWING_REACHED;
-               if (lpre == 3)
-                       dp->conf[i] |= DPCD_LC03_MAX_PRE_EMPHASIS_REACHED;
+               dp->pc2conf[i >> 1] |= lpc2 << ((i & 1) * 4);
+
+               DBG("config lane %d %02x %02x\n", i, dp->conf[i], lpc2);
+               impl->drv_ctl(outp, i, lvsw & 3, lpre & 3, lpc2 & 3);
+       }
+
+       ret = nv_wraux(outp->base.edid, DPCD_LC03(0), dp->conf, 4);
+       if (ret)
+               return ret;
 
-               DBG("config lane %d %02x\n", i, dp->conf[i]);
-               dp->func->drv_ctl(dp->disp, dp->outp, dp->head, i, lvsw, lpre);
+       if (pc) {
+               ret = nv_wraux(outp->base.edid, DPCD_LC0F, dp->pc2conf, 2);
+               if (ret)
+                       return ret;
        }
 
-       return nv_wraux(dp->aux, DPCD_LC03(0), dp->conf, 4);
+       return 0;
 }
 
 static int
-dp_link_train_update(struct dp_state *dp, u32 delay)
+dp_link_train_update(struct dp_state *dp, bool pc, u32 delay)
 {
+       struct nvkm_output_dp *outp = dp->outp;
        int ret;
 
-       udelay(delay);
+       if (outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL])
+               mdelay(outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4);
+       else
+               udelay(delay);
 
-       ret = nv_rdaux(dp->aux, DPCD_LS02, dp->stat, 6);
+       ret = nv_rdaux(outp->base.edid, DPCD_LS02, dp->stat, 6);
        if (ret)
                return ret;
 
-       DBG("status %6ph\n", dp->stat);
+       if (pc) {
+               ret = nv_rdaux(outp->base.edid, DPCD_LS0C, &dp->pc2stat, 1);
+               if (ret)
+                       dp->pc2stat = 0x00;
+               DBG("status %6ph pc2 %02x\n", dp->stat, dp->pc2stat);
+       } else {
+               DBG("status %6ph\n", dp->stat);
+       }
+
        return 0;
 }
 
@@ -169,8 +204,8 @@ dp_link_train_cr(struct dp_state *dp)
        dp_set_training_pattern(dp, 1);
 
        do {
-               if (dp_link_train_commit(dp) ||
-                   dp_link_train_update(dp, 100))
+               if (dp_link_train_commit(dp, false) ||
+                   dp_link_train_update(dp, false, 100))
                        break;
 
                cr_done = true;
@@ -196,13 +231,17 @@ dp_link_train_cr(struct dp_state *dp)
 static int
 dp_link_train_eq(struct dp_state *dp)
 {
+       struct nvkm_output_dp *outp = dp->outp;
        bool eq_done = false, cr_done = true;
        int tries = 0, i;
 
-       dp_set_training_pattern(dp, 2);
+       if (outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED)
+               dp_set_training_pattern(dp, 3);
+       else
+               dp_set_training_pattern(dp, 2);
 
        do {
-               if (dp_link_train_update(dp, 400))
+               if (dp_link_train_update(dp, dp->pc2, 400))
                        break;
 
                eq_done = !!(dp->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE);
@@ -215,7 +254,7 @@ dp_link_train_eq(struct dp_state *dp)
                                eq_done = false;
                }
 
-               if (dp_link_train_commit(dp))
+               if (dp_link_train_commit(dp, dp->pc2))
                        break;
        } while (!eq_done && cr_done && ++tries <= 5);
 
@@ -225,121 +264,109 @@ dp_link_train_eq(struct dp_state *dp)
 static void
 dp_link_train_init(struct dp_state *dp, bool spread)
 {
+       struct nvkm_output_dp *outp = dp->outp;
+       struct nouveau_disp *disp = nouveau_disp(outp);
+       struct nouveau_bios *bios = nouveau_bios(disp);
        struct nvbios_init init = {
-               .subdev = nv_subdev(dp->disp),
-               .bios = nouveau_bios(dp->disp),
-               .outp = dp->outp,
-               .crtc = dp->head,
+               .subdev = nv_subdev(disp),
+               .bios = bios,
+               .outp = &outp->base.info,
+               .crtc = -1,
                .execute = 1,
        };
 
        /* set desired spread */
        if (spread)
-               init.offset = dp->info.script[2];
+               init.offset = outp->info.script[2];
        else
-               init.offset = dp->info.script[3];
+               init.offset = outp->info.script[3];
        nvbios_exec(&init);
 
        /* pre-train script */
-       init.offset = dp->info.script[0];
+       init.offset = outp->info.script[0];
        nvbios_exec(&init);
 }
 
 static void
 dp_link_train_fini(struct dp_state *dp)
 {
+       struct nvkm_output_dp *outp = dp->outp;
+       struct nouveau_disp *disp = nouveau_disp(outp);
+       struct nouveau_bios *bios = nouveau_bios(disp);
        struct nvbios_init init = {
-               .subdev = nv_subdev(dp->disp),
-               .bios = nouveau_bios(dp->disp),
-               .outp = dp->outp,
-               .crtc = dp->head,
+               .subdev = nv_subdev(disp),
+               .bios = bios,
+               .outp = &outp->base.info,
+               .crtc = -1,
                .execute = 1,
        };
 
        /* post-train script */
-       init.offset = dp->info.script[1],
+       init.offset = outp->info.script[1],
        nvbios_exec(&init);
 }
 
-int
-nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func,
-                struct dcb_output *outp, int head, u32 datarate)
+static const struct dp_rates {
+       u32 rate;
+       u8  bw;
+       u8  nr;
+} nouveau_dp_rates[] = {
+       { 2160000, 0x14, 4 },
+       { 1080000, 0x0a, 4 },
+       { 1080000, 0x14, 2 },
+       {  648000, 0x06, 4 },
+       {  540000, 0x0a, 2 },
+       {  540000, 0x14, 1 },
+       {  324000, 0x06, 2 },
+       {  270000, 0x0a, 1 },
+       {  162000, 0x06, 1 },
+       {}
+};
+
+void
+nouveau_dp_train(struct work_struct *w)
 {
-       struct nouveau_bios *bios = nouveau_bios(disp);
-       struct nouveau_i2c *i2c = nouveau_i2c(disp);
+       struct nvkm_output_dp *outp = container_of(w, typeof(*outp), lt.work);
+       struct nouveau_disp *disp = nouveau_disp(outp);
+       const struct dp_rates *cfg = nouveau_dp_rates;
        struct dp_state _dp = {
-               .disp = disp,
-               .func = func,
                .outp = outp,
-               .head = head,
        }, *dp = &_dp;
-       const u32 bw_list[] = { 540000, 270000, 162000, 0 };
-       const u32 *link_bw = bw_list;
-       u8  hdr, cnt, len;
-       u32 data;
+       u32 datarate = 0;
        int ret;
 
-       /* find the bios displayport data relevant to this output */
-       data = nvbios_dpout_match(bios, outp->hasht, outp->hashm, &dp->version,
-                                &hdr, &cnt, &len, &dp->info);
-       if (!data) {
-               ERR("bios data not found\n");
-               return -EINVAL;
-       }
-
-       /* acquire the aux channel and fetch some info about the display */
-       if (outp->location)
-               dp->aux = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev));
-       else
-               dp->aux = i2c->find(i2c, NV_I2C_TYPE_DCBI2C(outp->i2c_index));
-       if (!dp->aux) {
-               ERR("no aux channel?!\n");
-               return -ENODEV;
-       }
-
-       ret = nv_rdaux(dp->aux, 0x00000, dp->dpcd, sizeof(dp->dpcd));
-       if (ret) {
-               /* it's possible the display has been unplugged before we
-                * get here.  we still need to execute the full set of
-                * vbios scripts, and program the OR at a high enough
-                * frequency to satisfy the target mode.  failure to do
-                * so results at best in an UPDATE hanging, and at worst
-                * with PDISP running away to join the circus.
-                */
-               dp->dpcd[1] = link_bw[0] / 27000;
-               dp->dpcd[2] = 4;
-               dp->dpcd[3] = 0x00;
-               ERR("failed to read DPCD\n");
-       }
-
        /* bring capabilities within encoder limits */
-       if ((dp->dpcd[2] & 0x1f) > dp->outp->dpconf.link_nr) {
-               dp->dpcd[2] &= ~0x1f;
-               dp->dpcd[2] |= dp->outp->dpconf.link_nr;
+       if (nv_mclass(disp) < NVD0_DISP_CLASS)
+               outp->dpcd[2] &= ~DPCD_RC02_TPS3_SUPPORTED;
+       if ((outp->dpcd[2] & 0x1f) > outp->base.info.dpconf.link_nr) {
+               outp->dpcd[2] &= ~DPCD_RC02_MAX_LANE_COUNT;
+               outp->dpcd[2] |= outp->base.info.dpconf.link_nr;
+       }
+       if (outp->dpcd[1] > outp->base.info.dpconf.link_bw)
+               outp->dpcd[1] = outp->base.info.dpconf.link_bw;
+       dp->pc2 = outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED;
+
+       /* restrict link config to the lowest required rate, if requested */
+       if (datarate) {
+               datarate = (datarate / 8) * 10; /* 8B/10B coding overhead */
+               while (cfg[1].rate >= datarate)
+                       cfg++;
        }
-       if (dp->dpcd[1] > dp->outp->dpconf.link_bw)
-               dp->dpcd[1] = dp->outp->dpconf.link_bw;
+       cfg--;
 
-       /* adjust required bandwidth for 8B/10B coding overhead */
-       datarate = (datarate / 8) * 10;
+       /* disable link interrupt handling during link training */
+       nouveau_event_put(outp->irq);
 
        /* enable down-spreading and execute pre-train script from vbios */
-       dp_link_train_init(dp, dp->dpcd[3] & 0x01);
+       dp_link_train_init(dp, outp->dpcd[3] & 0x01);
 
-       /* start off at highest link rate supported by encoder and display */
-       while (*link_bw > (dp->dpcd[1] * 27000))
-               link_bw++;
-
-       while ((ret = -EIO) && link_bw[0]) {
-               /* find minimum required lane count at this link rate */
-               dp->link_nr = dp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT;
-               while ((dp->link_nr >> 1) * link_bw[0] > datarate)
-                       dp->link_nr >>= 1;
-
-               /* drop link rate to minimum with this lane count */
-               while ((link_bw[1] * dp->link_nr) > datarate)
-                       link_bw++;
-               dp->link_bw = link_bw[0];
+       while (ret = -EIO, (++cfg)->rate) {
+               /* select next configuration supported by encoder and sink */
+               while (cfg->nr > (outp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT) ||
+                      cfg->bw > (outp->dpcd[DPCD_RC01_MAX_LINK_RATE]))
+                       cfg++;
+               dp->link_bw = cfg->bw * 27000;
+               dp->link_nr = cfg->nr;
 
                /* program selected link configuration */
                ret = dp_set_link_config(dp);
@@ -356,17 +383,18 @@ nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func,
                         */
                        break;
                }
-
-               /* retry at lower rate */
-               link_bw++;
        }
 
-       /* finish link training */
+       /* finish link training and execute post-train script from vbios */
        dp_set_training_pattern(dp, 0);
        if (ret < 0)
                ERR("link training failed\n");
 
-       /* execute post-train script from vbios */
        dp_link_train_fini(dp);
-       return (ret < 0) ? false : true;
+
+       /* signal completion and enable link interrupt handling */
+       DBG("training complete\n");
+       atomic_set(&outp->lt.done, 1);
+       wake_up(&outp->lt.wait);
+       nouveau_event_get(outp->irq);
 }
index 0e1bbd18ff6c4e4dc05d961d06a7f5e697643c07..5628d2d5ec71a2aed960267ce7fd9f0555098f8c 100644 (file)
@@ -2,19 +2,18 @@
 #define __NVKM_DISP_DPORT_H__
 
 /* DPCD Receiver Capabilities */
-#define DPCD_RC00                                                       0x00000
-#define DPCD_RC00_DPCD_REV                                                 0xff
-#define DPCD_RC01                                                       0x00001
-#define DPCD_RC01_MAX_LINK_RATE                                            0xff
+#define DPCD_RC00_DPCD_REV                                              0x00000
+#define DPCD_RC01_MAX_LINK_RATE                                         0x00001
 #define DPCD_RC02                                                       0x00002
 #define DPCD_RC02_ENHANCED_FRAME_CAP                                       0x80
+#define DPCD_RC02_TPS3_SUPPORTED                                           0x40
 #define DPCD_RC02_MAX_LANE_COUNT                                           0x1f
 #define DPCD_RC03                                                       0x00003
 #define DPCD_RC03_MAX_DOWNSPREAD                                           0x01
+#define DPCD_RC0E_AUX_RD_INTERVAL                                       0x0000e
 
 /* DPCD Link Configuration */
-#define DPCD_LC00                                                       0x00100
-#define DPCD_LC00_LINK_BW_SET                                              0xff
+#define DPCD_LC00_LINK_BW_SET                                           0x00100
 #define DPCD_LC01                                                       0x00101
 #define DPCD_LC01_ENHANCED_FRAME_EN                                        0x80
 #define DPCD_LC01_LANE_COUNT_SET                                           0x1f
 #define DPCD_LC03_PRE_EMPHASIS_SET                                         0x18
 #define DPCD_LC03_MAX_SWING_REACHED                                        0x04
 #define DPCD_LC03_VOLTAGE_SWING_SET                                        0x03
+#define DPCD_LC0F                                                       0x0010f
+#define DPCD_LC0F_LANE1_MAX_POST_CURSOR2_REACHED                           0x40
+#define DPCD_LC0F_LANE1_POST_CURSOR2_SET                                   0x30
+#define DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED                           0x04
+#define DPCD_LC0F_LANE0_POST_CURSOR2_SET                                   0x03
+#define DPCD_LC10                                                       0x00110
+#define DPCD_LC10_LANE3_MAX_POST_CURSOR2_REACHED                           0x40
+#define DPCD_LC10_LANE3_POST_CURSOR2_SET                                   0x30
+#define DPCD_LC10_LANE2_MAX_POST_CURSOR2_REACHED                           0x04
+#define DPCD_LC10_LANE2_POST_CURSOR2_SET                                   0x03
 
 /* DPCD Link/Sink Status */
 #define DPCD_LS02                                                       0x00202
 #define DPCD_LS07_LANE3_VOLTAGE_SWING                                      0x30
 #define DPCD_LS07_LANE2_PRE_EMPHASIS                                       0x0c
 #define DPCD_LS07_LANE2_VOLTAGE_SWING                                      0x03
+#define DPCD_LS0C                                                       0x0020c
+#define DPCD_LS0C_LANE3_POST_CURSOR2                                       0xc0
+#define DPCD_LS0C_LANE2_POST_CURSOR2                                       0x30
+#define DPCD_LS0C_LANE1_POST_CURSOR2                                       0x0c
+#define DPCD_LS0C_LANE0_POST_CURSOR2                                       0x03
 
-struct nouveau_disp;
-struct dcb_output;
-
-struct nouveau_dp_func {
-       int (*pattern)(struct nouveau_disp *, struct dcb_output *,
-                      int head, int pattern);
-       int (*lnk_ctl)(struct nouveau_disp *, struct dcb_output *, int head,
-                      int link_nr, int link_bw, bool enh_frame);
-       int (*drv_ctl)(struct nouveau_disp *, struct dcb_output *, int head,
-                      int lane, int swing, int preem);
-};
-
-extern const struct nouveau_dp_func nv94_sor_dp_func;
-extern const struct nouveau_dp_func nvd0_sor_dp_func;
-extern const struct nouveau_dp_func nv50_pior_dp_func;
-
-int nouveau_dp_train(struct nouveau_disp *, const struct nouveau_dp_func *,
-                    struct dcb_output *, int, u32);
+void nouveau_dp_train(struct work_struct *);
 
 #endif
index cf6f59677b74291c328e5a4d826e7566504e11c2..9fc7447fec90815121e7cd060771ecb03455341e 100644 (file)
@@ -81,7 +81,6 @@ gm107_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        priv->sor.power = nv50_sor_power;
        priv->sor.hda_eld = nvd0_hda_eld;
        priv->sor.hdmi = nvd0_hdmi_ctrl;
-       priv->sor.dp = &nvd0_sor_dp_func;
        return 0;
 }
 
@@ -94,6 +93,7 @@ gm107_disp_oclass = &(struct nv50_disp_impl) {
                .init = _nouveau_disp_init,
                .fini = _nouveau_disp_fini,
        },
+       .base.outp =  nvd0_disp_outp_sclass,
        .mthd.core = &nve0_disp_mast_mthd_chan,
        .mthd.base = &nvd0_disp_sync_mthd_chan,
        .mthd.ovly = &nve0_disp_ovly_mthd_chan,
index 6c89af7928892e0a80a99ae5e8960996d81babae..a32666ed0c47828717219b56c5cf88e602642108 100644 (file)
@@ -51,6 +51,14 @@ nv04_disp_scanoutpos(struct nouveau_object *object, u32 mthd,
        args->htotal  = nv_rd32(priv, 0x680824 + (head * 0x2000)) & 0xffff;
        args->hblanke = args->htotal - 1;
 
+       /*
+        * If output is vga instead of digital then vtotal/htotal is invalid
+        * so we have to give up and trigger the timestamping fallback in the
+        * drm core.
+        */
+       if (!args->vtotal || !args->htotal)
+               return -ENOTSUPP;
+
        args->time[0] = ktime_to_ns(ktime_get());
        line = nv_rd32(priv, 0x600868 + (head * 0x2000));
        args->time[1] = ktime_to_ns(ktime_get());
@@ -78,13 +86,13 @@ nv04_disp_sclass[] = {
  ******************************************************************************/
 
 static void
-nv04_disp_vblank_enable(struct nouveau_event *event, int head)
+nv04_disp_vblank_enable(struct nouveau_event *event, int type, int head)
 {
        nv_wr32(event->priv, 0x600140 + (head * 0x2000) , 0x00000001);
 }
 
 static void
-nv04_disp_vblank_disable(struct nouveau_event *event, int head)
+nv04_disp_vblank_disable(struct nouveau_event *event, int type, int head)
 {
        nv_wr32(event->priv, 0x600140 + (head * 0x2000) , 0x00000000);
 }
@@ -98,12 +106,12 @@ nv04_disp_intr(struct nouveau_subdev *subdev)
        u32 pvideo;
 
        if (crtc0 & 0x00000001) {
-               nouveau_event_trigger(priv->base.vblank, 0);
+               nouveau_event_trigger(priv->base.vblank, 1, 0);
                nv_wr32(priv, 0x600100, 0x00000001);
        }
 
        if (crtc1 & 0x00000001) {
-               nouveau_event_trigger(priv->base.vblank, 1);
+               nouveau_event_trigger(priv->base.vblank, 1, 1);
                nv_wr32(priv, 0x602100, 0x00000001);
        }
 
index 9a0cab9c3adbc5b4a58b92bbb2939952d896bd49..1e85f36c705f34faba1c93d94af337b1e4d90abc 100644 (file)
@@ -829,13 +829,13 @@ nv50_disp_base_scanoutpos(struct nouveau_object *object, u32 mthd,
 }
 
 static void
-nv50_disp_base_vblank_enable(struct nouveau_event *event, int head)
+nv50_disp_base_vblank_enable(struct nouveau_event *event, int type, int head)
 {
        nv_mask(event->priv, 0x61002c, (4 << head), (4 << head));
 }
 
 static void
-nv50_disp_base_vblank_disable(struct nouveau_event *event, int head)
+nv50_disp_base_vblank_disable(struct nouveau_event *event, int type, int head)
 {
        nv_mask(event->priv, 0x61002c, (4 << head), 0);
 }
@@ -1114,19 +1114,20 @@ nv50_disp_intr_error(struct nv50_disp_priv *priv, int chid)
        nv_wr32(priv, 0x610080 + (chid * 0x08), 0x90000000);
 }
 
-static u16
-exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
-           struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+static struct nvkm_output *
+exec_lookup(struct nv50_disp_priv *priv, int head, int or, u32 ctrl,
+           u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
            struct nvbios_outp *info)
 {
        struct nouveau_bios *bios = nouveau_bios(priv);
-       u16 mask, type, data;
+       struct nvkm_output *outp;
+       u16 mask, type;
 
-       if (outp < 4) {
+       if (or < 4) {
                type = DCB_OUTPUT_ANALOG;
                mask = 0;
        } else
-       if (outp < 8) {
+       if (or < 8) {
                switch (ctrl & 0x00000f00) {
                case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break;
                case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break;
@@ -1136,45 +1137,48 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
                case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break;
                default:
                        nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl);
-                       return 0x0000;
+                       return NULL;
                }
-               outp -= 4;
+               o -= 4;
        } else {
-               outp = outp - 8;
+               or   = or - 8;
                type = 0x0010;
                mask = 0;
                switch (ctrl & 0x00000f00) {
-               case 0x00000000: type |= priv->pior.type[outp]; break;
+               case 0x00000000: type |= priv->pior.type[or]; break;
                default:
                        nv_error(priv, "unknown PIOR mc 0x%08x\n", ctrl);
-                       return 0x0000;
+                       return NULL;
                }
        }
 
        mask  = 0x00c0 & (mask << 6);
-       mask |= 0x0001 << outp;
+       mask |= 0x0001 << or;
        mask |= 0x0100 << head;
 
-       data = dcb_outp_match(bios, type, mask, ver, hdr, dcb);
-       if (!data)
-               return 0x0000;
-
-       /* off-chip encoders require matching the exact encoder type */
-       if (dcb->location != 0)
-               type |= dcb->extdev << 8;
+       list_for_each_entry(outp, &priv->base.outp, head) {
+               if ((outp->info.hasht & 0xff) == type &&
+                   (outp->info.hashm & mask) == mask) {
+                       *data = nvbios_outp_match(bios, outp->info.hasht,
+                                                       outp->info.hashm,
+                                                 ver, hdr, cnt, len, info);
+                       if (!*data)
+                               return NULL;
+                       return outp;
+               }
+       }
 
-       return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info);
+       return NULL;
 }
 
-static bool
+static struct nvkm_output *
 exec_script(struct nv50_disp_priv *priv, int head, int id)
 {
        struct nouveau_bios *bios = nouveau_bios(priv);
+       struct nvkm_output *outp;
        struct nvbios_outp info;
-       struct dcb_output dcb;
        u8  ver, hdr, cnt, len;
-       u16 data;
-       u32 ctrl = 0x00000000;
+       u32 data, ctrl = 0;
        u32 reg;
        int i;
 
@@ -1204,36 +1208,35 @@ exec_script(struct nv50_disp_priv *priv, int head, int id)
        }
 
        if (!(ctrl & (1 << head)))
-               return false;
+               return NULL;
        i--;
 
-       data = exec_lookup(priv, head, i, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info);
-       if (data) {
+       outp = exec_lookup(priv, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info);
+       if (outp) {
                struct nvbios_init init = {
                        .subdev = nv_subdev(priv),
                        .bios = bios,
                        .offset = info.script[id],
-                       .outp = &dcb,
+                       .outp = &outp->info,
                        .crtc = head,
                        .execute = 1,
                };
 
-               return nvbios_exec(&init) == 0;
+               nvbios_exec(&init);
        }
 
-       return false;
+       return outp;
 }
 
-static u32
-exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk,
-           struct dcb_output *outp)
+static struct nvkm_output *
+exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, u32 *conf)
 {
        struct nouveau_bios *bios = nouveau_bios(priv);
+       struct nvkm_output *outp;
        struct nvbios_outp info1;
        struct nvbios_ocfg info2;
        u8  ver, hdr, cnt, len;
-       u32 ctrl = 0x00000000;
-       u32 data, conf = ~0;
+       u32 data, ctrl = 0;
        u32 reg;
        int i;
 
@@ -1263,37 +1266,37 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk,
        }
 
        if (!(ctrl & (1 << head)))
-               return conf;
+               return NULL;
        i--;
 
-       data = exec_lookup(priv, head, i, ctrl, outp, &ver, &hdr, &cnt, &len, &info1);
+       outp = exec_lookup(priv, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info1);
        if (!data)
-               return conf;
+               return NULL;
 
-       if (outp->location == 0) {
-               switch (outp->type) {
+       if (outp->info.location == 0) {
+               switch (outp->info.type) {
                case DCB_OUTPUT_TMDS:
-                       conf = (ctrl & 0x00000f00) >> 8;
+                       *conf = (ctrl & 0x00000f00) >> 8;
                        if (pclk >= 165000)
-                               conf |= 0x0100;
+                               *conf |= 0x0100;
                        break;
                case DCB_OUTPUT_LVDS:
-                       conf = priv->sor.lvdsconf;
+                       *conf = priv->sor.lvdsconf;
                        break;
                case DCB_OUTPUT_DP:
-                       conf = (ctrl & 0x00000f00) >> 8;
+                       *conf = (ctrl & 0x00000f00) >> 8;
                        break;
                case DCB_OUTPUT_ANALOG:
                default:
-                       conf = 0x00ff;
+                       *conf = 0x00ff;
                        break;
                }
        } else {
-               conf = (ctrl & 0x00000f00) >> 8;
+               *conf = (ctrl & 0x00000f00) >> 8;
                pclk = pclk / 2;
        }
 
-       data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2);
+       data = nvbios_ocfg_match(bios, data, *conf, &ver, &hdr, &cnt, &len, &info2);
        if (data && id < 0xff) {
                data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
                if (data) {
@@ -1301,7 +1304,7 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk,
                                .subdev = nv_subdev(priv),
                                .bios = bios,
                                .offset = data,
-                               .outp = outp,
+                               .outp = &outp->info,
                                .crtc = head,
                                .execute = 1,
                        };
@@ -1310,7 +1313,7 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk,
                }
        }
 
-       return conf;
+       return outp;
 }
 
 static void
@@ -1322,7 +1325,35 @@ nv50_disp_intr_unk10_0(struct nv50_disp_priv *priv, int head)
 static void
 nv50_disp_intr_unk20_0(struct nv50_disp_priv *priv, int head)
 {
-       exec_script(priv, head, 2);
+       struct nvkm_output *outp = exec_script(priv, head, 2);
+
+       /* the binary driver does this outside of the supervisor handling
+        * (after the third supervisor from a detach).  we (currently?)
+        * allow both detach/attach to happen in the same set of
+        * supervisor interrupts, so it would make sense to execute this
+        * (full power down?) script after all the detach phases of the
+        * supervisor handling.  like with training if needed from the
+        * second supervisor, nvidia doesn't do this, so who knows if it's
+        * entirely safe, but it does appear to work..
+        *
+        * without this script being run, on some configurations i've
+        * seen, switching from DP to TMDS on a DP connector may result
+        * in a blank screen (SOR_PWR off/on can restore it)
+        */
+       if (outp && outp->info.type == DCB_OUTPUT_DP) {
+               struct nvkm_output_dp *outpdp = (void *)outp;
+               struct nvbios_init init = {
+                       .subdev = nv_subdev(priv),
+                       .bios = nouveau_bios(priv),
+                       .outp = &outp->info,
+                       .crtc = head,
+                       .offset = outpdp->info.script[4],
+                       .execute = 1,
+               };
+
+               nvbios_exec(&init);
+               atomic_set(&outpdp->lt.done, 0);
+       }
 }
 
 static void
@@ -1444,56 +1475,83 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv,
 static void
 nv50_disp_intr_unk20_2(struct nv50_disp_priv *priv, int head)
 {
-       struct dcb_output outp;
+       struct nvkm_output *outp;
        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) {
-                       u32 soff = (ffs(outp.or) - 1) * 0x08;
-                       u32 ctrl = nv_rd32(priv, 0x610794 + soff);
-                       u32 datarate;
-
-                       switch ((ctrl & 0x000f0000) >> 16) {
-                       case 6: datarate = pclk * 30 / 8; break;
-                       case 5: datarate = pclk * 24 / 8; break;
-                       case 2:
-                       default:
-                               datarate = pclk * 18 / 8;
-                               break;
-                       }
+       u32 mask, conf;
 
-                       nouveau_dp_train(&priv->base, priv->sor.dp,
-                                        &outp, head, datarate);
-               }
+       outp = exec_clkcmp(priv, head, 0xff, pclk, &conf);
+       if (!outp)
+               return;
+
+       /* we allow both encoder attach and detach operations to occur
+        * within a single supervisor (ie. modeset) sequence.  the
+        * encoder detach scripts quite often switch off power to the
+        * lanes, which requires the link to be re-trained.
+        *
+        * this is not generally an issue as the sink "must" (heh)
+        * signal an irq when it's lost sync so the driver can
+        * re-train.
+        *
+        * however, on some boards, if one does not configure at least
+        * the gpu side of the link *before* attaching, then various
+        * things can go horribly wrong (PDISP disappearing from mmio,
+        * third supervisor never happens, etc).
+        *
+        * the solution is simply to retrain here, if necessary.  last
+        * i checked, the binary driver userspace does not appear to
+        * trigger this situation (it forces an UPDATE between steps).
+        */
+       if (outp->info.type == DCB_OUTPUT_DP) {
+               u32 soff = (ffs(outp->info.or) - 1) * 0x08;
+               u32 ctrl, datarate;
 
-               exec_clkcmp(priv, head, 0, pclk, &outp);
-
-               if (!outp.location && outp.type == DCB_OUTPUT_ANALOG) {
-                       oreg = 0x614280 + (ffs(outp.or) - 1) * 0x800;
-                       oval = 0x00000000;
-                       hval = 0x00000000;
-                       mask = 0xffffffff;
-               } else
-               if (!outp.location) {
-                       if (outp.type == DCB_OUTPUT_DP)
-                               nv50_disp_intr_unk20_2_dp(priv, &outp, pclk);
-                       oreg = 0x614300 + (ffs(outp.or) - 1) * 0x800;
-                       oval = (conf & 0x0100) ? 0x00000101 : 0x00000000;
-                       hval = 0x00000000;
-                       mask = 0x00000707;
+               if (outp->info.location == 0) {
+                       ctrl = nv_rd32(priv, 0x610794 + soff);
+                       soff = 1;
                } else {
-                       oreg = 0x614380 + (ffs(outp.or) - 1) * 0x800;
-                       oval = 0x00000001;
-                       hval = 0x00000001;
-                       mask = 0x00000707;
+                       ctrl = nv_rd32(priv, 0x610b80 + soff);
+                       soff = 2;
                }
 
-               nv_mask(priv, hreg, 0x0000000f, hval);
-               nv_mask(priv, oreg, mask, oval);
+               switch ((ctrl & 0x000f0000) >> 16) {
+               case 6: datarate = pclk * 30 / 8; break;
+               case 5: datarate = pclk * 24 / 8; break;
+               case 2:
+               default:
+                       datarate = pclk * 18 / 8;
+                       break;
+               }
+
+               if (nvkm_output_dp_train(outp, datarate / soff, true))
+                       ERR("link not trained before attach\n");
        }
+
+       exec_clkcmp(priv, head, 0, pclk, &conf);
+
+       if (!outp->info.location && outp->info.type == DCB_OUTPUT_ANALOG) {
+               oreg = 0x614280 + (ffs(outp->info.or) - 1) * 0x800;
+               oval = 0x00000000;
+               hval = 0x00000000;
+               mask = 0xffffffff;
+       } else
+       if (!outp->info.location) {
+               if (outp->info.type == DCB_OUTPUT_DP)
+                       nv50_disp_intr_unk20_2_dp(priv, &outp->info, pclk);
+               oreg = 0x614300 + (ffs(outp->info.or) - 1) * 0x800;
+               oval = (conf & 0x0100) ? 0x00000101 : 0x00000000;
+               hval = 0x00000000;
+               mask = 0x00000707;
+       } else {
+               oreg = 0x614380 + (ffs(outp->info.or) - 1) * 0x800;
+               oval = 0x00000001;
+               hval = 0x00000001;
+               mask = 0x00000707;
+       }
+
+       nv_mask(priv, hreg, 0x0000000f, hval);
+       nv_mask(priv, oreg, mask, oval);
 }
 
 /* If programming a TMDS output on a SOR that can also be configured for
@@ -1521,30 +1579,16 @@ nv50_disp_intr_unk40_0_tmds(struct nv50_disp_priv *priv, struct dcb_output *outp
 static void
 nv50_disp_intr_unk40_0(struct nv50_disp_priv *priv, int head)
 {
-       struct dcb_output outp;
+       struct nvkm_output *outp;
        u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
-       if (exec_clkcmp(priv, head, 1, pclk, &outp) != ~0) {
-               if (outp.location == 0 && outp.type == DCB_OUTPUT_TMDS)
-                       nv50_disp_intr_unk40_0_tmds(priv, &outp);
-               else
-               if (outp.location == 1 && outp.type == DCB_OUTPUT_DP) {
-                       u32 soff = (ffs(outp.or) - 1) * 0x08;
-                       u32 ctrl = nv_rd32(priv, 0x610b84 + soff);
-                       u32 datarate;
-
-                       switch ((ctrl & 0x000f0000) >> 16) {
-                       case 6: datarate = pclk * 30 / 8; break;
-                       case 5: datarate = pclk * 24 / 8; break;
-                       case 2:
-                       default:
-                               datarate = pclk * 18 / 8;
-                               break;
-                       }
+       u32 conf;
 
-                       nouveau_dp_train(&priv->base, priv->pior.dp,
-                                        &outp, head, datarate);
-               }
-       }
+       outp = exec_clkcmp(priv, head, 1, pclk, &conf);
+       if (!outp)
+               return;
+
+       if (outp->info.location == 0 && outp->info.type == DCB_OUTPUT_TMDS)
+               nv50_disp_intr_unk40_0_tmds(priv, &outp->info);
 }
 
 void
@@ -1610,13 +1654,13 @@ nv50_disp_intr(struct nouveau_subdev *subdev)
        }
 
        if (intr1 & 0x00000004) {
-               nouveau_event_trigger(priv->base.vblank, 0);
+               nouveau_event_trigger(priv->base.vblank, 1, 0);
                nv_wr32(priv, 0x610024, 0x00000004);
                intr1 &= ~0x00000004;
        }
 
        if (intr1 & 0x00000008) {
-               nouveau_event_trigger(priv->base.vblank, 1);
+               nouveau_event_trigger(priv->base.vblank, 1, 1);
                nv_wr32(priv, 0x610024, 0x00000008);
                intr1 &= ~0x00000008;
        }
@@ -1656,10 +1700,15 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        priv->dac.sense = nv50_dac_sense;
        priv->sor.power = nv50_sor_power;
        priv->pior.power = nv50_pior_power;
-       priv->pior.dp = &nv50_pior_dp_func;
        return 0;
 }
 
+struct nouveau_oclass *
+nv50_disp_outp_sclass[] = {
+       &nv50_pior_dp_impl.base.base,
+       NULL
+};
+
 struct nouveau_oclass *
 nv50_disp_oclass = &(struct nv50_disp_impl) {
        .base.base.handle = NV_ENGINE(DISP, 0x50),
@@ -1669,6 +1718,7 @@ nv50_disp_oclass = &(struct nv50_disp_impl) {
                .init = _nouveau_disp_init,
                .fini = _nouveau_disp_fini,
        },
+       .base.outp =  nv50_disp_outp_sclass,
        .mthd.core = &nv50_disp_mast_mthd_chan,
        .mthd.base = &nv50_disp_sync_mthd_chan,
        .mthd.ovly = &nv50_disp_ovly_mthd_chan,
index 48d59db47f0d00eae37445ee3c22fc658895266d..1a886472b6f5b93df20d06b05a57157b8f1cd987 100644 (file)
@@ -11,6 +11,8 @@
 
 #include "dport.h"
 #include "priv.h"
+#include "outp.h"
+#include "outpdp.h"
 
 struct nv50_disp_impl {
        struct nouveau_disp_impl base;
@@ -43,13 +45,11 @@ struct nv50_disp_priv {
                int (*hda_eld)(struct nv50_disp_priv *, int sor, u8 *, u32);
                int (*hdmi)(struct nv50_disp_priv *, int head, int sor, u32);
                u32 lvdsconf;
-               const struct nouveau_dp_func *dp;
        } sor;
        struct {
                int nr;
                int (*power)(struct nv50_disp_priv *, int ext, u32 data);
                u8 type[3];
-               const struct nouveau_dp_func *dp;
        } pior;
 };
 
@@ -199,4 +199,14 @@ void nvd0_disp_intr(struct nouveau_subdev *);
 extern const struct nv50_disp_mthd_chan nve0_disp_mast_mthd_chan;
 extern const struct nv50_disp_mthd_chan nve0_disp_ovly_mthd_chan;
 
+extern struct nvkm_output_dp_impl nv50_pior_dp_impl;
+extern struct nouveau_oclass *nv50_disp_outp_sclass[];
+
+extern struct nvkm_output_dp_impl nv94_sor_dp_impl;
+int nv94_sor_dp_lnk_pwr(struct nvkm_output_dp *, int);
+extern struct nouveau_oclass *nv94_disp_outp_sclass[];
+
+extern struct nvkm_output_dp_impl nvd0_sor_dp_impl;
+extern struct nouveau_oclass *nvd0_disp_outp_sclass[];
+
 #endif
index 98c5b19bc2b06d25643bd904905cc834ef7b8661..1cc62e4346833ee02f3b92b74087f9549e6642e2 100644 (file)
@@ -264,7 +264,6 @@ nv84_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        priv->sor.power = nv50_sor_power;
        priv->sor.hdmi = nv84_hdmi_ctrl;
        priv->pior.power = nv50_pior_power;
-       priv->pior.dp = &nv50_pior_dp_func;
        return 0;
 }
 
@@ -277,6 +276,7 @@ nv84_disp_oclass = &(struct nv50_disp_impl) {
                .init = _nouveau_disp_init,
                .fini = _nouveau_disp_fini,
        },
+       .base.outp =  nv50_disp_outp_sclass,
        .mthd.core = &nv84_disp_mast_mthd_chan,
        .mthd.base = &nv84_disp_sync_mthd_chan,
        .mthd.ovly = &nv84_disp_ovly_mthd_chan,
index 6844061c7e0450afc2de82c1d4e6aa111ebab6b5..4f718a9f5aefbeb0b75d8a55de7c05f2f3d04793 100644 (file)
@@ -77,6 +77,7 @@ nv94_disp_base_omthds[] = {
        { SOR_MTHD(NV50_DISP_SOR_PWR)         , nv50_sor_mthd },
        { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR)    , nv50_sor_mthd },
        { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
+       { SOR_MTHD(NV94_DISP_SOR_DP_PWR)      , nv50_sor_mthd },
        { DAC_MTHD(NV50_DISP_DAC_PWR)         , nv50_dac_mthd },
        { DAC_MTHD(NV50_DISP_DAC_LOAD)        , nv50_dac_mthd },
        { PIOR_MTHD(NV50_DISP_PIOR_PWR)       , nv50_pior_mthd },
@@ -122,12 +123,17 @@ nv94_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        priv->dac.sense = nv50_dac_sense;
        priv->sor.power = nv50_sor_power;
        priv->sor.hdmi = nv84_hdmi_ctrl;
-       priv->sor.dp = &nv94_sor_dp_func;
        priv->pior.power = nv50_pior_power;
-       priv->pior.dp = &nv50_pior_dp_func;
        return 0;
 }
 
+struct nouveau_oclass *
+nv94_disp_outp_sclass[] = {
+       &nv50_pior_dp_impl.base.base,
+       &nv94_sor_dp_impl.base.base,
+       NULL
+};
+
 struct nouveau_oclass *
 nv94_disp_oclass = &(struct nv50_disp_impl) {
        .base.base.handle = NV_ENGINE(DISP, 0x88),
@@ -137,6 +143,7 @@ nv94_disp_oclass = &(struct nv50_disp_impl) {
                .init = _nouveau_disp_init,
                .fini = _nouveau_disp_fini,
        },
+       .base.outp =  nv94_disp_outp_sclass,
        .mthd.core = &nv94_disp_mast_mthd_chan,
        .mthd.base = &nv84_disp_sync_mthd_chan,
        .mthd.ovly = &nv84_disp_ovly_mthd_chan,
index 88c96241c02a1a2fba69c67c3acadaffb5d498dc..6237a9a36f709cf6cd4f503049373c02aec0eb38 100644 (file)
@@ -126,7 +126,6 @@ nva0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        priv->sor.power = nv50_sor_power;
        priv->sor.hdmi = nv84_hdmi_ctrl;
        priv->pior.power = nv50_pior_power;
-       priv->pior.dp = &nv50_pior_dp_func;
        return 0;
 }
 
@@ -139,6 +138,7 @@ nva0_disp_oclass = &(struct nv50_disp_impl) {
                .init = _nouveau_disp_init,
                .fini = _nouveau_disp_fini,
        },
+       .base.outp =  nv50_disp_outp_sclass,
        .mthd.core = &nv84_disp_mast_mthd_chan,
        .mthd.base = &nv84_disp_sync_mthd_chan,
        .mthd.ovly = &nva0_disp_ovly_mthd_chan,
index 46cb2ce0e82a99a8995a6e24bb33dd3c55284780..019124d4782bf5303733927ef4bc9c6861152cde 100644 (file)
@@ -50,6 +50,7 @@ nva3_disp_base_omthds[] = {
        { SOR_MTHD(NVA3_DISP_SOR_HDA_ELD)     , nv50_sor_mthd },
        { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR)    , nv50_sor_mthd },
        { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
+       { SOR_MTHD(NV94_DISP_SOR_DP_PWR)      , nv50_sor_mthd },
        { DAC_MTHD(NV50_DISP_DAC_PWR)         , nv50_dac_mthd },
        { DAC_MTHD(NV50_DISP_DAC_LOAD)        , nv50_dac_mthd },
        { PIOR_MTHD(NV50_DISP_PIOR_PWR)       , nv50_pior_mthd },
@@ -96,9 +97,7 @@ nva3_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        priv->sor.power = nv50_sor_power;
        priv->sor.hda_eld = nva3_hda_eld;
        priv->sor.hdmi = nva3_hdmi_ctrl;
-       priv->sor.dp = &nv94_sor_dp_func;
        priv->pior.power = nv50_pior_power;
-       priv->pior.dp = &nv50_pior_dp_func;
        return 0;
 }
 
@@ -111,6 +110,7 @@ nva3_disp_oclass = &(struct nv50_disp_impl) {
                .init = _nouveau_disp_init,
                .fini = _nouveau_disp_fini,
        },
+       .base.outp =  nv94_disp_outp_sclass,
        .mthd.core = &nv94_disp_mast_mthd_chan,
        .mthd.base = &nv84_disp_sync_mthd_chan,
        .mthd.ovly = &nv84_disp_ovly_mthd_chan,
index 876de9ac3793fd30af193ac7e8410818bdb56e7a..48aa38a87e3fdb9651aea5f99586cf380099335f 100644 (file)
@@ -748,13 +748,13 @@ nvd0_disp_base_scanoutpos(struct nouveau_object *object, u32 mthd,
 }
 
 static void
-nvd0_disp_base_vblank_enable(struct nouveau_event *event, int head)
+nvd0_disp_base_vblank_enable(struct nouveau_event *event, int type, int head)
 {
        nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000001);
 }
 
 static void
-nvd0_disp_base_vblank_disable(struct nouveau_event *event, int head)
+nvd0_disp_base_vblank_disable(struct nouveau_event *event, int type, int head)
 {
        nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000000);
 }
@@ -887,6 +887,7 @@ nvd0_disp_base_omthds[] = {
        { SOR_MTHD(NVA3_DISP_SOR_HDA_ELD)     , nv50_sor_mthd },
        { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR)    , nv50_sor_mthd },
        { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
+       { SOR_MTHD(NV94_DISP_SOR_DP_PWR)      , nv50_sor_mthd },
        { DAC_MTHD(NV50_DISP_DAC_PWR)         , nv50_dac_mthd },
        { DAC_MTHD(NV50_DISP_DAC_LOAD)        , nv50_dac_mthd },
        { PIOR_MTHD(NV50_DISP_PIOR_PWR)       , nv50_pior_mthd },
@@ -915,19 +916,20 @@ nvd0_disp_sclass[] = {
  * Display engine implementation
  ******************************************************************************/
 
-static u16
-exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
-           struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+static struct nvkm_output *
+exec_lookup(struct nv50_disp_priv *priv, int head, int or, u32 ctrl,
+           u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
            struct nvbios_outp *info)
 {
        struct nouveau_bios *bios = nouveau_bios(priv);
-       u16 mask, type, data;
+       struct nvkm_output *outp;
+       u16 mask, type;
 
-       if (outp < 4) {
+       if (or < 4) {
                type = DCB_OUTPUT_ANALOG;
                mask = 0;
        } else {
-               outp -= 4;
+               or -= 4;
                switch (ctrl & 0x00000f00) {
                case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break;
                case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break;
@@ -939,101 +941,106 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
                        nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl);
                        return 0x0000;
                }
-               dcb->sorconf.link = mask;
        }
 
        mask  = 0x00c0 & (mask << 6);
-       mask |= 0x0001 << outp;
+       mask |= 0x0001 << or;
        mask |= 0x0100 << head;
 
-       data = dcb_outp_match(bios, type, mask, ver, hdr, dcb);
-       if (!data)
-               return 0x0000;
+       list_for_each_entry(outp, &priv->base.outp, head) {
+               if ((outp->info.hasht & 0xff) == type &&
+                   (outp->info.hashm & mask) == mask) {
+                       *data = nvbios_outp_match(bios, outp->info.hasht,
+                                                       outp->info.hashm,
+                                                 ver, hdr, cnt, len, info);
+                       if (!*data)
+                               return NULL;
+                       return outp;
+               }
+       }
 
-       return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info);
+       return NULL;
 }
 
-static bool
+static struct nvkm_output *
 exec_script(struct nv50_disp_priv *priv, int head, int id)
 {
        struct nouveau_bios *bios = nouveau_bios(priv);
+       struct nvkm_output *outp;
        struct nvbios_outp info;
-       struct dcb_output dcb;
        u8  ver, hdr, cnt, len;
-       u32 ctrl = 0x00000000;
-       u16 data;
-       int outp;
+       u32 data, ctrl = 0;
+       int or;
 
-       for (outp = 0; !(ctrl & (1 << head)) && outp < 8; outp++) {
-               ctrl = nv_rd32(priv, 0x640180 + (outp * 0x20));
+       for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) {
+               ctrl = nv_rd32(priv, 0x640180 + (or * 0x20));
                if (ctrl & (1 << head))
                        break;
        }
 
-       if (outp == 8)
-               return false;
+       if (or == 8)
+               return NULL;
 
-       data = exec_lookup(priv, head, outp, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info);
-       if (data) {
+       outp = exec_lookup(priv, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info);
+       if (outp) {
                struct nvbios_init init = {
                        .subdev = nv_subdev(priv),
                        .bios = bios,
                        .offset = info.script[id],
-                       .outp = &dcb,
+                       .outp = &outp->info,
                        .crtc = head,
                        .execute = 1,
                };
 
-               return nvbios_exec(&init) == 0;
+               nvbios_exec(&init);
        }
 
-       return false;
+       return outp;
 }
 
-static u32
-exec_clkcmp(struct nv50_disp_priv *priv, int head, int id,
-           u32 pclk, struct dcb_output *dcb)
+static struct nvkm_output *
+exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, u32 *conf)
 {
        struct nouveau_bios *bios = nouveau_bios(priv);
+       struct nvkm_output *outp;
        struct nvbios_outp info1;
        struct nvbios_ocfg info2;
        u8  ver, hdr, cnt, len;
-       u32 ctrl = 0x00000000;
-       u32 data, conf = ~0;
-       int outp;
+       u32 data, ctrl = 0;
+       int or;
 
-       for (outp = 0; !(ctrl & (1 << head)) && outp < 8; outp++) {
-               ctrl = nv_rd32(priv, 0x660180 + (outp * 0x20));
+       for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) {
+               ctrl = nv_rd32(priv, 0x660180 + (or * 0x20));
                if (ctrl & (1 << head))
                        break;
        }
 
-       if (outp == 8)
-               return conf;
+       if (or == 8)
+               return NULL;
 
-       data = exec_lookup(priv, head, outp, ctrl, dcb, &ver, &hdr, &cnt, &len, &info1);
-       if (data == 0x0000)
-               return conf;
+       outp = exec_lookup(priv, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info1);
+       if (!outp)
+               return NULL;
 
-       switch (dcb->type) {
+       switch (outp->info.type) {
        case DCB_OUTPUT_TMDS:
-               conf = (ctrl & 0x00000f00) >> 8;
+               *conf = (ctrl & 0x00000f00) >> 8;
                if (pclk >= 165000)
-                       conf |= 0x0100;
+                       *conf |= 0x0100;
                break;
        case DCB_OUTPUT_LVDS:
-               conf = priv->sor.lvdsconf;
+               *conf = priv->sor.lvdsconf;
                break;
        case DCB_OUTPUT_DP:
-               conf = (ctrl & 0x00000f00) >> 8;
+               *conf = (ctrl & 0x00000f00) >> 8;
                break;
        case DCB_OUTPUT_ANALOG:
        default:
-               conf = 0x00ff;
+               *conf = 0x00ff;
                break;
        }
 
-       data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2);
+       data = nvbios_ocfg_match(bios, data, *conf, &ver, &hdr, &cnt, &len, &info2);
        if (data && id < 0xff) {
                data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
                if (data) {
@@ -1041,7 +1048,7 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id,
                                .subdev = nv_subdev(priv),
                                .bios = bios,
                                .offset = data,
-                               .outp = dcb,
+                               .outp = &outp->info,
                                .crtc = head,
                                .execute = 1,
                        };
@@ -1050,7 +1057,7 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id,
                }
        }
 
-       return conf;
+       return outp;
 }
 
 static void
@@ -1062,7 +1069,23 @@ nvd0_disp_intr_unk1_0(struct nv50_disp_priv *priv, int head)
 static void
 nvd0_disp_intr_unk2_0(struct nv50_disp_priv *priv, int head)
 {
-       exec_script(priv, head, 2);
+       struct nvkm_output *outp = exec_script(priv, head, 2);
+
+       /* see note in nv50_disp_intr_unk20_0() */
+       if (outp && outp->info.type == DCB_OUTPUT_DP) {
+               struct nvkm_output_dp *outpdp = (void *)outp;
+               struct nvbios_init init = {
+                       .subdev = nv_subdev(priv),
+                       .bios = nouveau_bios(priv),
+                       .outp = &outp->info,
+                       .crtc = head,
+                       .offset = outpdp->info.script[4],
+                       .execute = 1,
+               };
+
+               nvbios_exec(&init);
+               atomic_set(&outpdp->lt.done, 0);
+       }
 }
 
 static void
@@ -1124,49 +1147,52 @@ nvd0_disp_intr_unk2_2_tu(struct nv50_disp_priv *priv, int head,
 static void
 nvd0_disp_intr_unk2_2(struct nv50_disp_priv *priv, int head)
 {
-       struct dcb_output outp;
+       struct nvkm_output *outp;
        u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
-       u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp);
-       if (conf != ~0) {
-               u32 addr, data;
-
-               if (outp.type == DCB_OUTPUT_DP) {
-                       u32 sync = nv_rd32(priv, 0x660404 + (head * 0x300));
-                       switch ((sync & 0x000003c0) >> 6) {
-                       case 6: pclk = pclk * 30 / 8; break;
-                       case 5: pclk = pclk * 24 / 8; break;
-                       case 2:
-                       default:
-                               pclk = pclk * 18 / 8;
-                               break;
-                       }
-
-                       nouveau_dp_train(&priv->base, priv->sor.dp,
-                                        &outp, head, pclk);
+       u32 conf, addr, data;
+
+       outp = exec_clkcmp(priv, head, 0xff, pclk, &conf);
+       if (!outp)
+               return;
+
+       /* see note in nv50_disp_intr_unk20_2() */
+       if (outp->info.type == DCB_OUTPUT_DP) {
+               u32 sync = nv_rd32(priv, 0x660404 + (head * 0x300));
+               switch ((sync & 0x000003c0) >> 6) {
+               case 6: pclk = pclk * 30 / 8; break;
+               case 5: pclk = pclk * 24 / 8; break;
+               case 2:
+               default:
+                       pclk = pclk * 18 / 8;
+                       break;
                }
 
-               exec_clkcmp(priv, head, 0, pclk, &outp);
+               if (nvkm_output_dp_train(outp, pclk, true))
+                       ERR("link not trained before attach\n");
+       }
 
-               if (outp.type == DCB_OUTPUT_ANALOG) {
-                       addr = 0x612280 + (ffs(outp.or) - 1) * 0x800;
-                       data = 0x00000000;
-               } else {
-                       if (outp.type == DCB_OUTPUT_DP)
-                               nvd0_disp_intr_unk2_2_tu(priv, head, &outp);
-                       addr = 0x612300 + (ffs(outp.or) - 1) * 0x800;
-                       data = (conf & 0x0100) ? 0x00000101 : 0x00000000;
-               }
+       exec_clkcmp(priv, head, 0, pclk, &conf);
 
-               nv_mask(priv, addr, 0x00000707, data);
+       if (outp->info.type == DCB_OUTPUT_ANALOG) {
+               addr = 0x612280 + (ffs(outp->info.or) - 1) * 0x800;
+               data = 0x00000000;
+       } else {
+               if (outp->info.type == DCB_OUTPUT_DP)
+                       nvd0_disp_intr_unk2_2_tu(priv, head, &outp->info);
+               addr = 0x612300 + (ffs(outp->info.or) - 1) * 0x800;
+               data = (conf & 0x0100) ? 0x00000101 : 0x00000000;
        }
+
+       nv_mask(priv, addr, 0x00000707, data);
 }
 
 static void
 nvd0_disp_intr_unk4_0(struct nv50_disp_priv *priv, int head)
 {
-       struct dcb_output outp;
        u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
-       exec_clkcmp(priv, head, 1, pclk, &outp);
+       u32 conf;
+
+       exec_clkcmp(priv, head, 1, pclk, &conf);
 }
 
 void
@@ -1240,7 +1266,7 @@ nvd0_disp_intr_error(struct nv50_disp_priv *priv, int chid)
                 chid, (mthd & 0x0000ffc), data, mthd, unkn);
 
        if (chid == 0) {
-               switch (mthd) {
+               switch (mthd & 0xffc) {
                case 0x0080:
                        nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 0,
                                            impl->mthd.core);
@@ -1250,7 +1276,7 @@ nvd0_disp_intr_error(struct nv50_disp_priv *priv, int chid)
                }
        } else
        if (chid <= 4) {
-               switch (mthd) {
+               switch (mthd & 0xffc) {
                case 0x0080:
                        nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 1,
                                            impl->mthd.base);
@@ -1260,7 +1286,7 @@ nvd0_disp_intr_error(struct nv50_disp_priv *priv, int chid)
                }
        } else
        if (chid <= 8) {
-               switch (mthd) {
+               switch (mthd & 0xffc) {
                case 0x0080:
                        nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 5,
                                            impl->mthd.ovly);
@@ -1317,7 +1343,7 @@ nvd0_disp_intr(struct nouveau_subdev *subdev)
                if (mask & intr) {
                        u32 stat = nv_rd32(priv, 0x6100bc + (i * 0x800));
                        if (stat & 0x00000001)
-                               nouveau_event_trigger(priv->base.vblank, i);
+                               nouveau_event_trigger(priv->base.vblank, 1, i);
                        nv_mask(priv, 0x6100bc + (i * 0x800), 0, 0);
                        nv_rd32(priv, 0x6100c0 + (i * 0x800));
                }
@@ -1352,10 +1378,15 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        priv->sor.power = nv50_sor_power;
        priv->sor.hda_eld = nvd0_hda_eld;
        priv->sor.hdmi = nvd0_hdmi_ctrl;
-       priv->sor.dp = &nvd0_sor_dp_func;
        return 0;
 }
 
+struct nouveau_oclass *
+nvd0_disp_outp_sclass[] = {
+       &nvd0_sor_dp_impl.base.base,
+       NULL
+};
+
 struct nouveau_oclass *
 nvd0_disp_oclass = &(struct nv50_disp_impl) {
        .base.base.handle = NV_ENGINE(DISP, 0x90),
@@ -1365,6 +1396,7 @@ nvd0_disp_oclass = &(struct nv50_disp_impl) {
                .init = _nouveau_disp_init,
                .fini = _nouveau_disp_fini,
        },
+       .base.outp =  nvd0_disp_outp_sclass,
        .mthd.core = &nvd0_disp_mast_mthd_chan,
        .mthd.base = &nvd0_disp_sync_mthd_chan,
        .mthd.ovly = &nvd0_disp_ovly_mthd_chan,
index 44e0b8f34c1a8fc8d15fa019598be533e87df713..11328e3f5df1140472d396d4e346958d86a211fb 100644 (file)
@@ -246,7 +246,6 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        priv->sor.power = nv50_sor_power;
        priv->sor.hda_eld = nvd0_hda_eld;
        priv->sor.hdmi = nvd0_hdmi_ctrl;
-       priv->sor.dp = &nvd0_sor_dp_func;
        return 0;
 }
 
@@ -259,6 +258,7 @@ nve0_disp_oclass = &(struct nv50_disp_impl) {
                .init = _nouveau_disp_init,
                .fini = _nouveau_disp_fini,
        },
+       .base.outp =  nvd0_disp_outp_sclass,
        .mthd.core = &nve0_disp_mast_mthd_chan,
        .mthd.base = &nvd0_disp_sync_mthd_chan,
        .mthd.ovly = &nve0_disp_ovly_mthd_chan,
index 482585d375fa564ec2ff4e1bcbb7b87e2b15efe8..104388081d7329e9bd636c0539a820b1a79917c0 100644 (file)
@@ -81,7 +81,6 @@ nvf0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        priv->sor.power = nv50_sor_power;
        priv->sor.hda_eld = nvd0_hda_eld;
        priv->sor.hdmi = nvd0_hdmi_ctrl;
-       priv->sor.dp = &nvd0_sor_dp_func;
        return 0;
 }
 
@@ -94,6 +93,7 @@ nvf0_disp_oclass = &(struct nv50_disp_impl) {
                .init = _nouveau_disp_init,
                .fini = _nouveau_disp_fini,
        },
+       .base.outp =  nvd0_disp_outp_sclass,
        .mthd.core = &nve0_disp_mast_mthd_chan,
        .mthd.base = &nvd0_disp_sync_mthd_chan,
        .mthd.ovly = &nve0_disp_ovly_mthd_chan,
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outp.c b/drivers/gpu/drm/nouveau/core/engine/disp/outp.c
new file mode 100644 (file)
index 0000000..ad9ba7c
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2014 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/i2c.h>
+#include <subdev/bios.h>
+#include <subdev/bios/conn.h>
+
+#include "outp.h"
+
+int
+_nvkm_output_fini(struct nouveau_object *object, bool suspend)
+{
+       struct nvkm_output *outp = (void *)object;
+       nv_ofuncs(outp->conn)->fini(nv_object(outp->conn), suspend);
+       return nouveau_object_fini(&outp->base, suspend);
+}
+
+int
+_nvkm_output_init(struct nouveau_object *object)
+{
+       struct nvkm_output *outp = (void *)object;
+       int ret = nouveau_object_init(&outp->base);
+       if (ret == 0)
+               nv_ofuncs(outp->conn)->init(nv_object(outp->conn));
+       return 0;
+}
+
+void
+_nvkm_output_dtor(struct nouveau_object *object)
+{
+       struct nvkm_output *outp = (void *)object;
+       list_del(&outp->head);
+       nouveau_object_ref(NULL, (void *)&outp->conn);
+       nouveau_object_destroy(&outp->base);
+}
+
+int
+nvkm_output_create_(struct nouveau_object *parent,
+                   struct nouveau_object *engine,
+                   struct nouveau_oclass *oclass,
+                   struct dcb_output *dcbE, int index,
+                   int length, void **pobject)
+{
+       struct nouveau_bios *bios = nouveau_bios(engine);
+       struct nouveau_i2c *i2c = nouveau_i2c(parent);
+       struct nouveau_disp *disp = (void *)engine;
+       struct nvbios_connE connE;
+       struct nvkm_output *outp;
+       u8  ver, hdr;
+       u32 data;
+       int ret;
+
+       ret = nouveau_object_create_(parent, engine, oclass, 0, length, pobject);
+       outp = *pobject;
+       if (ret)
+               return ret;
+
+       outp->info = *dcbE;
+       outp->index = index;
+
+       DBG("type %02x loc %d or %d link %d con %x edid %x bus %d head %x\n",
+           dcbE->type, dcbE->location, dcbE->or, dcbE->type >= 2 ?
+           dcbE->sorconf.link : 0, dcbE->connector, dcbE->i2c_index,
+           dcbE->bus, dcbE->heads);
+
+       outp->port = i2c->find(i2c, outp->info.i2c_index);
+       outp->edid = outp->port;
+
+       data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr, &connE);
+       if (!data) {
+               DBG("vbios connector data not found\n");
+               memset(&connE, 0x00, sizeof(connE));
+               connE.type = DCB_CONNECTOR_NONE;
+       }
+
+       ret = nouveau_object_ctor(parent, engine, nvkm_connector_oclass,
+                                &connE, outp->info.connector,
+                                (struct nouveau_object **)&outp->conn);
+       if (ret < 0) {
+               ERR("error %d creating connector, disabling\n", ret);
+               return ret;
+       }
+
+       list_add_tail(&outp->head, &disp->outp);
+       return 0;
+}
+
+int
+_nvkm_output_ctor(struct nouveau_object *parent,
+                 struct nouveau_object *engine,
+                 struct nouveau_oclass *oclass, void *dcbE, u32 index,
+                 struct nouveau_object **pobject)
+{
+       struct nvkm_output *outp;
+       int ret;
+
+       ret = nvkm_output_create(parent, engine, oclass, dcbE, index, &outp);
+       *pobject = nv_object(outp);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+struct nouveau_oclass *
+nvkm_output_oclass = &(struct nvkm_output_impl) {
+       .base = {
+               .handle = 0,
+               .ofuncs = &(struct nouveau_ofuncs) {
+                       .ctor = _nvkm_output_ctor,
+                       .dtor = _nvkm_output_dtor,
+                       .init = _nvkm_output_init,
+                       .fini = _nvkm_output_fini,
+               },
+       },
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outp.h b/drivers/gpu/drm/nouveau/core/engine/disp/outp.h
new file mode 100644 (file)
index 0000000..bc76fbf
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef __NVKM_DISP_OUTP_H__
+#define __NVKM_DISP_OUTP_H__
+
+#include "priv.h"
+
+struct nvkm_output {
+       struct nouveau_object base;
+       struct list_head head;
+
+       struct dcb_output info;
+       int index;
+
+       struct nouveau_i2c_port *port;
+       struct nouveau_i2c_port *edid;
+
+       struct nvkm_connector *conn;
+};
+
+#define nvkm_output_create(p,e,c,b,i,d)                                        \
+       nvkm_output_create_((p), (e), (c), (b), (i), sizeof(**d), (void **)d)
+#define nvkm_output_destroy(d) ({                                              \
+       struct nvkm_output *_outp = (d);                                       \
+       _nvkm_output_dtor(nv_object(_outp));                                   \
+})
+#define nvkm_output_init(d) ({                                                 \
+       struct nvkm_output *_outp = (d);                                       \
+       _nvkm_output_init(nv_object(_outp));                                   \
+})
+#define nvkm_output_fini(d,s) ({                                               \
+       struct nvkm_output *_outp = (d);                                       \
+       _nvkm_output_fini(nv_object(_outp), (s));                              \
+})
+
+int nvkm_output_create_(struct nouveau_object *, struct nouveau_object *,
+                       struct nouveau_oclass *, struct dcb_output *,
+                       int, int, void **);
+
+int  _nvkm_output_ctor(struct nouveau_object *, struct nouveau_object *,
+                      struct nouveau_oclass *, void *, u32,
+                      struct nouveau_object **);
+void _nvkm_output_dtor(struct nouveau_object *);
+int  _nvkm_output_init(struct nouveau_object *);
+int  _nvkm_output_fini(struct nouveau_object *, bool);
+
+struct nvkm_output_impl {
+       struct nouveau_oclass base;
+};
+
+#ifndef MSG
+#define MSG(l,f,a...) do {                                                     \
+       struct nvkm_output *_outp = (void *)outp;                              \
+       nv_##l(nv_object(outp)->engine, "%02x:%04x:%04x: "f, _outp->index,     \
+              _outp->info.hasht, _outp->info.hashm, ##a);                     \
+} while(0)
+#define DBG(f,a...) MSG(debug, f, ##a)
+#define ERR(f,a...) MSG(error, f, ##a)
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c b/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c
new file mode 100644 (file)
index 0000000..52c299c
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2014 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/i2c.h>
+
+#include "outpdp.h"
+#include "conn.h"
+#include "dport.h"
+
+int
+nvkm_output_dp_train(struct nvkm_output *base, u32 datarate, bool wait)
+{
+       struct nvkm_output_dp *outp = (void *)base;
+       bool retrain = true;
+       u8 link[2], stat[3];
+       u32 rate;
+       int ret, i;
+
+       /* check that the link is trained at a high enough rate */
+       ret = nv_rdaux(outp->base.edid, DPCD_LC00_LINK_BW_SET, link, 2);
+       if (ret) {
+               DBG("failed to read link config, assuming no sink\n");
+               goto done;
+       }
+
+       rate = link[0] * 27000 * (link[1] & DPCD_LC01_LANE_COUNT_SET);
+       if (rate < ((datarate / 8) * 10)) {
+               DBG("link not trained at sufficient rate\n");
+               goto done;
+       }
+
+       /* check that link is still trained */
+       ret = nv_rdaux(outp->base.edid, DPCD_LS02, stat, 3);
+       if (ret) {
+               DBG("failed to read link status, assuming no sink\n");
+               goto done;
+       }
+
+       if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) {
+               for (i = 0; i < (link[1] & DPCD_LC01_LANE_COUNT_SET); i++) {
+                       u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f;
+                       if (!(lane & DPCD_LS02_LANE0_CR_DONE) ||
+                           !(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) ||
+                           !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) {
+                               DBG("lane %d not equalised\n", lane);
+                               goto done;
+                       }
+               }
+               retrain = false;
+       } else {
+               DBG("no inter-lane alignment\n");
+       }
+
+done:
+       if (retrain || !atomic_read(&outp->lt.done)) {
+               /* no sink, but still need to configure source */
+               if (outp->dpcd[DPCD_RC00_DPCD_REV] == 0x00) {
+                       outp->dpcd[DPCD_RC01_MAX_LINK_RATE] =
+                               outp->base.info.dpconf.link_bw;
+                       outp->dpcd[DPCD_RC02] =
+                               outp->base.info.dpconf.link_nr;
+               }
+               atomic_set(&outp->lt.done, 0);
+               schedule_work(&outp->lt.work);
+       } else {
+               nouveau_event_get(outp->irq);
+       }
+
+       if (wait) {
+               if (!wait_event_timeout(outp->lt.wait,
+                                       atomic_read(&outp->lt.done),
+                                       msecs_to_jiffies(2000)))
+                       ret = -ETIMEDOUT;
+       }
+
+       return ret;
+}
+
+static void
+nvkm_output_dp_enable(struct nvkm_output_dp *outp, bool present)
+{
+       struct nouveau_i2c_port *port = outp->base.edid;
+       if (present) {
+               if (!outp->present) {
+                       nouveau_i2c(port)->acquire_pad(port, 0);
+                       DBG("aux power -> always\n");
+                       outp->present = true;
+               }
+               nvkm_output_dp_train(&outp->base, 0, true);
+       } else {
+               if (outp->present) {
+                       nouveau_i2c(port)->release_pad(port);
+                       DBG("aux power -> demand\n");
+                       outp->present = false;
+               }
+               atomic_set(&outp->lt.done, 0);
+       }
+}
+
+static void
+nvkm_output_dp_detect(struct nvkm_output_dp *outp)
+{
+       struct nouveau_i2c_port *port = outp->base.edid;
+       int ret = nouveau_i2c(port)->acquire_pad(port, 0);
+       if (ret == 0) {
+               ret = nv_rdaux(outp->base.edid, DPCD_RC00_DPCD_REV,
+                              outp->dpcd, sizeof(outp->dpcd));
+               nvkm_output_dp_enable(outp, ret == 0);
+               nouveau_i2c(port)->release_pad(port);
+       }
+}
+
+static void
+nvkm_output_dp_service_work(struct work_struct *work)
+{
+       struct nvkm_output_dp *outp = container_of(work, typeof(*outp), work);
+       struct nouveau_disp *disp = nouveau_disp(outp);
+       int type = atomic_xchg(&outp->pending, 0);
+       u32 send = 0;
+
+       if (type & (NVKM_I2C_PLUG | NVKM_I2C_UNPLUG)) {
+               nvkm_output_dp_detect(outp);
+               if (type & NVKM_I2C_UNPLUG)
+                       send |= NVKM_HPD_UNPLUG;
+               if (type & NVKM_I2C_PLUG)
+                       send |= NVKM_HPD_PLUG;
+               nouveau_event_get(outp->base.conn->hpd.event);
+       }
+
+       if (type & NVKM_I2C_IRQ) {
+               nvkm_output_dp_train(&outp->base, 0, true);
+               send |= NVKM_HPD_IRQ;
+       }
+
+       nouveau_event_trigger(disp->hpd, send, outp->base.info.connector);
+}
+
+static int
+nvkm_output_dp_service(void *data, u32 type, int index)
+{
+       struct nvkm_output_dp *outp = data;
+       DBG("HPD: %d\n", type);
+       atomic_or(type, &outp->pending);
+       schedule_work(&outp->work);
+       return NVKM_EVENT_DROP;
+}
+
+int
+_nvkm_output_dp_fini(struct nouveau_object *object, bool suspend)
+{
+       struct nvkm_output_dp *outp = (void *)object;
+       nouveau_event_put(outp->irq);
+       nvkm_output_dp_enable(outp, false);
+       return nvkm_output_fini(&outp->base, suspend);
+}
+
+int
+_nvkm_output_dp_init(struct nouveau_object *object)
+{
+       struct nvkm_output_dp *outp = (void *)object;
+       nvkm_output_dp_detect(outp);
+       return nvkm_output_init(&outp->base);
+}
+
+void
+_nvkm_output_dp_dtor(struct nouveau_object *object)
+{
+       struct nvkm_output_dp *outp = (void *)object;
+       nouveau_event_ref(NULL, &outp->irq);
+       nvkm_output_destroy(&outp->base);
+}
+
+int
+nvkm_output_dp_create_(struct nouveau_object *parent,
+                      struct nouveau_object *engine,
+                      struct nouveau_oclass *oclass,
+                      struct dcb_output *info, int index,
+                      int length, void **pobject)
+{
+       struct nouveau_bios *bios = nouveau_bios(parent);
+       struct nouveau_i2c *i2c = nouveau_i2c(parent);
+       struct nvkm_output_dp *outp;
+       u8  hdr, cnt, len;
+       u32 data;
+       int ret;
+
+       ret = nvkm_output_create_(parent, engine, oclass, info, index,
+                                 length, pobject);
+       outp = *pobject;
+       if (ret)
+               return ret;
+
+       nouveau_event_ref(NULL, &outp->base.conn->hpd.event);
+
+       /* access to the aux channel is not optional... */
+       if (!outp->base.edid) {
+               ERR("aux channel not found\n");
+               return -ENODEV;
+       }
+
+       /* nor is the bios data for this output... */
+       data = nvbios_dpout_match(bios, outp->base.info.hasht,
+                                 outp->base.info.hashm, &outp->version,
+                                 &hdr, &cnt, &len, &outp->info);
+       if (!data) {
+               ERR("no bios dp data\n");
+               return -ENODEV;
+       }
+
+       DBG("bios dp %02x %02x %02x %02x\n", outp->version, hdr, cnt, len);
+
+       /* link training */
+       INIT_WORK(&outp->lt.work, nouveau_dp_train);
+       init_waitqueue_head(&outp->lt.wait);
+       atomic_set(&outp->lt.done, 0);
+
+       /* link maintenance */
+       ret = nouveau_event_new(i2c->ntfy, NVKM_I2C_IRQ, outp->base.edid->index,
+                               nvkm_output_dp_service, outp, &outp->irq);
+       if (ret) {
+               ERR("error monitoring aux irq event: %d\n", ret);
+               return ret;
+       }
+
+       INIT_WORK(&outp->work, nvkm_output_dp_service_work);
+
+       /* hotplug detect, replaces gpio-based mechanism with aux events */
+       ret = nouveau_event_new(i2c->ntfy, NVKM_I2C_PLUG | NVKM_I2C_UNPLUG,
+                               outp->base.edid->index,
+                               nvkm_output_dp_service, outp,
+                              &outp->base.conn->hpd.event);
+       if (ret) {
+               ERR("error monitoring aux hpd events: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+int
+_nvkm_output_dp_ctor(struct nouveau_object *parent,
+                    struct nouveau_object *engine,
+                    struct nouveau_oclass *oclass, void *info, u32 index,
+                    struct nouveau_object **pobject)
+{
+       struct nvkm_output_dp *outp;
+       int ret;
+
+       ret = nvkm_output_dp_create(parent, engine, oclass, info, index, &outp);
+       *pobject = nv_object(outp);
+       if (ret)
+               return ret;
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.h b/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.h
new file mode 100644 (file)
index 0000000..ff33ba1
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef __NVKM_DISP_OUTP_DP_H__
+#define __NVKM_DISP_OUTP_DP_H__
+
+#include <subdev/bios.h>
+#include <subdev/bios/dp.h>
+
+#include "outp.h"
+
+struct nvkm_output_dp {
+       struct nvkm_output base;
+
+       struct nvbios_dpout info;
+       u8 version;
+
+       struct nouveau_eventh *irq;
+       struct nouveau_eventh *hpd;
+       struct work_struct work;
+       atomic_t pending;
+       bool present;
+       u8 dpcd[16];
+
+       struct {
+               struct work_struct work;
+               wait_queue_head_t wait;
+               atomic_t done;
+       } lt;
+};
+
+#define nvkm_output_dp_create(p,e,c,b,i,d)                                     \
+       nvkm_output_dp_create_((p), (e), (c), (b), (i), sizeof(**d), (void **)d)
+#define nvkm_output_dp_destroy(d) ({                                           \
+       struct nvkm_output_dp *_outp = (d);                                    \
+       _nvkm_output_dp_dtor(nv_object(_outp));                                \
+})
+#define nvkm_output_dp_init(d) ({                                              \
+       struct nvkm_output_dp *_outp = (d);                                    \
+       _nvkm_output_dp_init(nv_object(_outp));                                \
+})
+#define nvkm_output_dp_fini(d,s) ({                                            \
+       struct nvkm_output_dp *_outp = (d);                                    \
+       _nvkm_output_dp_fini(nv_object(_outp), (s));                           \
+})
+
+int nvkm_output_dp_create_(struct nouveau_object *, struct nouveau_object *,
+                          struct nouveau_oclass *, struct dcb_output *,
+                          int, int, void **);
+
+int  _nvkm_output_dp_ctor(struct nouveau_object *, struct nouveau_object *,
+                         struct nouveau_oclass *, void *, u32,
+                         struct nouveau_object **);
+void _nvkm_output_dp_dtor(struct nouveau_object *);
+int  _nvkm_output_dp_init(struct nouveau_object *);
+int  _nvkm_output_dp_fini(struct nouveau_object *, bool);
+
+struct nvkm_output_dp_impl {
+       struct nvkm_output_impl base;
+       int (*pattern)(struct nvkm_output_dp *, int);
+       int (*lnk_pwr)(struct nvkm_output_dp *, int nr);
+       int (*lnk_ctl)(struct nvkm_output_dp *, int nr, int bw, bool ef);
+       int (*drv_ctl)(struct nvkm_output_dp *, int ln, int vs, int pe, int pc);
+};
+
+int nvkm_output_dp_train(struct nvkm_output *, u32 rate, bool wait);
+
+#endif
index 2c8ce351b52d0e2ba8aa917dc0dc3f8a4dc4e697..fe0f256f11bfbcba3cc40a82eb04f4adb94a3f5c 100644 (file)
 #include "nv50.h"
 
 /******************************************************************************
- * DisplayPort
+ * TMDS
  *****************************************************************************/
-static struct nouveau_i2c_port *
-nv50_pior_dp_find(struct nouveau_disp *disp, struct dcb_output *outp)
+
+static int
+nv50_pior_tmds_ctor(struct nouveau_object *parent,
+                   struct nouveau_object *engine,
+                   struct nouveau_oclass *oclass, void *info, u32 index,
+                   struct nouveau_object **pobject)
 {
-       struct nouveau_i2c *i2c = nouveau_i2c(disp);
-       return i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev));
+       struct nouveau_i2c *i2c = nouveau_i2c(parent);
+       struct nvkm_output *outp;
+       int ret;
+
+       ret = nvkm_output_create(parent, engine, oclass, info, index, &outp);
+       *pobject = nv_object(outp);
+       if (ret)
+               return ret;
+
+       outp->edid = i2c->find_type(i2c, NV_I2C_TYPE_EXTDDC(outp->info.extdev));
+       return 0;
 }
 
+struct nvkm_output_impl
+nv50_pior_tmds_impl = {
+       .base.handle = DCB_OUTPUT_TMDS | 0x0100,
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv50_pior_tmds_ctor,
+               .dtor = _nvkm_output_dtor,
+               .init = _nvkm_output_init,
+               .fini = _nvkm_output_fini,
+       },
+};
+
+/******************************************************************************
+ * DisplayPort
+ *****************************************************************************/
+
 static int
-nv50_pior_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp,
-                    int head, int pattern)
+nv50_pior_dp_pattern(struct nvkm_output_dp *outp, int pattern)
 {
-       struct nouveau_i2c_port *port;
-       int ret = -EINVAL;
-
-       port = nv50_pior_dp_find(disp, outp);
-       if (port) {
-               if (port->func->pattern)
-                       ret = port->func->pattern(port, pattern);
-               else
-                       ret = 0;
-       }
-
-       return ret;
+       struct nouveau_i2c_port *port = outp->base.edid;
+       if (port && port->func->pattern)
+               return port->func->pattern(port, pattern);
+       return port ? 0 : -ENODEV;
 }
 
 static int
-nv50_pior_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
-                    int head, int lane_nr, int link_bw, bool enh)
+nv50_pior_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
 {
-       struct nouveau_i2c_port *port;
-       int ret = -EINVAL;
+       return 0;
+}
 
-       port = nv50_pior_dp_find(disp, outp);
+static int
+nv50_pior_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
+{
+       struct nouveau_i2c_port *port = outp->base.edid;
        if (port && port->func->lnk_ctl)
-               ret = port->func->lnk_ctl(port, lane_nr, link_bw, enh);
+               return port->func->lnk_ctl(port, nr, bw, ef);
+       return port ? 0 : -ENODEV;
+}
 
-       return ret;
+static int
+nv50_pior_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
+{
+       struct nouveau_i2c_port *port = outp->base.edid;
+       if (port && port->func->drv_ctl)
+               return port->func->drv_ctl(port, ln, vs, pe);
+       return port ? 0 : -ENODEV;
 }
 
 static int
-nv50_pior_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
-                    int head, int lane, int vsw, int pre)
+nv50_pior_dp_ctor(struct nouveau_object *parent,
+                 struct nouveau_object *engine,
+                 struct nouveau_oclass *oclass, void *info, u32 index,
+                 struct nouveau_object **pobject)
 {
-       struct nouveau_i2c_port *port;
-       int ret = -EINVAL;
-
-       port = nv50_pior_dp_find(disp, outp);
-       if (port) {
-               if (port->func->drv_ctl)
-                       ret = port->func->drv_ctl(port, lane, vsw, pre);
-               else
-                       ret = 0;
-       }
+       struct nouveau_i2c *i2c = nouveau_i2c(parent);
+       struct nvkm_output_dp *outp;
+       int ret;
 
-       return ret;
+       ret = nvkm_output_dp_create(parent, engine, oclass, info, index, &outp);
+       *pobject = nv_object(outp);
+       if (ret)
+               return ret;
+
+       outp->base.edid = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(
+                                        outp->base.info.extdev));
+       return 0;
 }
 
-const struct nouveau_dp_func
-nv50_pior_dp_func = {
+struct nvkm_output_dp_impl
+nv50_pior_dp_impl = {
+       .base.base.handle = DCB_OUTPUT_DP | 0x0010,
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv50_pior_dp_ctor,
+               .dtor = _nvkm_output_dp_dtor,
+               .init = _nvkm_output_dp_init,
+               .fini = _nvkm_output_dp_fini,
+       },
        .pattern = nv50_pior_dp_pattern,
+       .lnk_pwr = nv50_pior_dp_lnk_pwr,
        .lnk_ctl = nv50_pior_dp_lnk_ctl,
        .drv_ctl = nv50_pior_dp_drv_ctl,
 };
@@ -102,6 +141,7 @@ nv50_pior_dp_func = {
 /******************************************************************************
  * General PIOR handling
  *****************************************************************************/
+
 int
 nv50_pior_power(struct nv50_disp_priv *priv, int or, u32 data)
 {
index cc3c7a4ca747886222964c98d51bc510e88e5d26..26e9a42569c7c07d36cd1d0207e096cd7f2c046d 100644 (file)
@@ -1,10 +1,42 @@
 #ifndef __NVKM_DISP_PRIV_H__
 #define __NVKM_DISP_PRIV_H__
 
+#include <subdev/bios.h>
+#include <subdev/bios/dcb.h>
+#include <subdev/bios/conn.h>
+
 #include <engine/disp.h>
 
 struct nouveau_disp_impl {
        struct nouveau_oclass base;
+       struct nouveau_oclass **outp;
+       struct nouveau_oclass **conn;
 };
 
+#define nouveau_disp_create(p,e,c,h,i,x,d)                                     \
+       nouveau_disp_create_((p), (e), (c), (h), (i), (x),                     \
+                            sizeof(**d), (void **)d)
+#define nouveau_disp_destroy(d) ({                                             \
+       struct nouveau_disp *disp = (d);                                       \
+       _nouveau_disp_dtor(nv_object(disp));                                   \
+})
+#define nouveau_disp_init(d) ({                                                \
+       struct nouveau_disp *disp = (d);                                       \
+       _nouveau_disp_init(nv_object(disp));                                   \
+})
+#define nouveau_disp_fini(d,s) ({                                              \
+       struct nouveau_disp *disp = (d);                                       \
+       _nouveau_disp_fini(nv_object(disp), (s));                              \
+})
+
+int  nouveau_disp_create_(struct nouveau_object *, struct nouveau_object *,
+                         struct nouveau_oclass *, int heads,
+                         const char *, const char *, int, void **);
+void _nouveau_disp_dtor(struct nouveau_object *);
+int  _nouveau_disp_init(struct nouveau_object *);
+int  _nouveau_disp_fini(struct nouveau_object *, bool);
+
+extern struct nouveau_oclass *nvkm_output_oclass;
+extern struct nouveau_oclass *nvkm_connector_oclass;
+
 #endif
index 526b75242899fb6e97e0abc3bd48cd228affe151..e1832778e8b67def8e1030fa47955acc42c3f204 100644 (file)
@@ -47,8 +47,12 @@ int
 nv50_sor_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size)
 {
        struct nv50_disp_priv *priv = (void *)object->engine;
+       const u8  type = (mthd & NV50_DISP_SOR_MTHD_TYPE) >> 12;
        const u8  head = (mthd & NV50_DISP_SOR_MTHD_HEAD) >> 3;
+       const u8  link = (mthd & NV50_DISP_SOR_MTHD_LINK) >> 2;
        const u8    or = (mthd & NV50_DISP_SOR_MTHD_OR);
+       const u16 mask = (0x0100 << head) | (0x0040 << link) | (0x0001 << or);
+       struct nvkm_output *outp = NULL, *temp;
        u32 data;
        int ret = -EINVAL;
 
@@ -56,6 +60,13 @@ nv50_sor_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size)
                return -EINVAL;
        data = *(u32 *)args;
 
+       list_for_each_entry(temp, &priv->base.outp, head) {
+               if ((temp->info.hasht & 0xff) == type &&
+                   (temp->info.hashm & mask) == mask) {
+                       outp = temp;
+                       break;
+               }
+       }
 
        switch (mthd & ~0x3f) {
        case NV50_DISP_SOR_PWR:
@@ -71,6 +82,23 @@ nv50_sor_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size)
                priv->sor.lvdsconf = data & NV50_DISP_SOR_LVDS_SCRIPT_ID;
                ret = 0;
                break;
+       case NV94_DISP_SOR_DP_PWR:
+               if (outp) {
+                       struct nvkm_output_dp *outpdp = (void *)outp;
+                       switch (data) {
+                       case NV94_DISP_SOR_DP_PWR_STATE_OFF:
+                               ((struct nvkm_output_dp_impl *)nv_oclass(outp))
+                                       ->lnk_pwr(outpdp, 0);
+                               atomic_set(&outpdp->lt.done, 0);
+                               break;
+                       case NV94_DISP_SOR_DP_PWR_STATE_ON:
+                               nvkm_output_dp_train(&outpdp->base, 0, true);
+                               break;
+                       default:
+                               return -EINVAL;
+                       }
+               }
+               break;
        default:
                BUG_ON(1);
        }
index eea3ef59693d6dd3158764690f3e82b8286ac022..05487cda84a89afd7e7e79813aaa9330a9b7cdb8 100644 (file)
 #include <subdev/bios/dcb.h>
 #include <subdev/bios/dp.h>
 #include <subdev/bios/init.h>
+#include <subdev/timer.h>
 
 #include "nv50.h"
+#include "outpdp.h"
 
 static inline u32
-nv94_sor_soff(struct dcb_output *outp)
+nv94_sor_soff(struct nvkm_output_dp *outp)
 {
-       return (ffs(outp->or) - 1) * 0x800;
+       return (ffs(outp->base.info.or) - 1) * 0x800;
 }
 
 static inline u32
-nv94_sor_loff(struct dcb_output *outp)
+nv94_sor_loff(struct nvkm_output_dp *outp)
 {
-       return nv94_sor_soff(outp) + !(outp->sorconf.link & 1) * 0x80;
+       return nv94_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80;
 }
 
 static inline u32
@@ -55,77 +57,96 @@ nv94_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane)
 }
 
 static int
-nv94_sor_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp,
-                   int head, int pattern)
+nv94_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
 {
-       struct nv50_disp_priv *priv = (void *)disp;
+       struct nv50_disp_priv *priv = (void *)nouveau_disp(outp);
        const u32 loff = nv94_sor_loff(outp);
        nv_mask(priv, 0x61c10c + loff, 0x0f000000, pattern << 24);
        return 0;
 }
 
+int
+nv94_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
+{
+       struct nv50_disp_priv *priv = (void *)nouveau_disp(outp);
+       const u32 soff = nv94_sor_soff(outp);
+       const u32 loff = nv94_sor_loff(outp);
+       u32 mask = 0, i;
+
+       for (i = 0; i < nr; i++)
+               mask |= 1 << (nv94_sor_dp_lane_map(priv, i) >> 3);
+
+       nv_mask(priv, 0x61c130 + loff, 0x0000000f, mask);
+       nv_mask(priv, 0x61c034 + soff, 0x80000000, 0x80000000);
+       nv_wait(priv, 0x61c034 + soff, 0x80000000, 0x00000000);
+       return 0;
+}
+
 static int
-nv94_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
-                   int head, int link_nr, int link_bw, bool enh_frame)
+nv94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
 {
-       struct nv50_disp_priv *priv = (void *)disp;
+       struct nv50_disp_priv *priv = (void *)nouveau_disp(outp);
        const u32 soff = nv94_sor_soff(outp);
        const u32 loff = nv94_sor_loff(outp);
        u32 dpctrl = 0x00000000;
        u32 clksor = 0x00000000;
-       u32 lane = 0;
-       int i;
 
-       dpctrl |= ((1 << link_nr) - 1) << 16;
-       if (enh_frame)
+       dpctrl |= ((1 << nr) - 1) << 16;
+       if (ef)
                dpctrl |= 0x00004000;
-       if (link_bw > 0x06)
+       if (bw > 0x06)
                clksor |= 0x00040000;
 
-       for (i = 0; i < link_nr; i++)
-               lane |= 1 << (nv94_sor_dp_lane_map(priv, i) >> 3);
-
        nv_mask(priv, 0x614300 + soff, 0x000c0000, clksor);
        nv_mask(priv, 0x61c10c + loff, 0x001f4000, dpctrl);
-       nv_mask(priv, 0x61c130 + loff, 0x0000000f, lane);
        return 0;
 }
 
 static int
-nv94_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
-                   int head, int lane, int swing, int preem)
+nv94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
 {
-       struct nouveau_bios *bios = nouveau_bios(disp);
-       struct nv50_disp_priv *priv = (void *)disp;
-       const u32 shift = nv94_sor_dp_lane_map(priv, lane);
+       struct nv50_disp_priv *priv = (void *)nouveau_disp(outp);
+       struct nouveau_bios *bios = nouveau_bios(priv);
+       const u32 shift = nv94_sor_dp_lane_map(priv, ln);
        const u32 loff = nv94_sor_loff(outp);
        u32 addr, data[3];
        u8  ver, hdr, cnt, len;
        struct nvbios_dpout info;
        struct nvbios_dpcfg ocfg;
 
-       addr = nvbios_dpout_match(bios, outp->hasht, outp->hashm,
+       addr = nvbios_dpout_match(bios, outp->base.info.hasht,
+                                       outp->base.info.hashm,
                                 &ver, &hdr, &cnt, &len, &info);
        if (!addr)
                return -ENODEV;
 
-       addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem,
+       addr = nvbios_dpcfg_match(bios, addr, 0, vs, pe,
                                 &ver, &hdr, &cnt, &len, &ocfg);
        if (!addr)
                return -EINVAL;
 
        data[0] = nv_rd32(priv, 0x61c118 + loff) & ~(0x000000ff << shift);
        data[1] = nv_rd32(priv, 0x61c120 + loff) & ~(0x000000ff << shift);
-       data[2] = nv_rd32(priv, 0x61c130 + loff) & ~(0x0000ff00);
-       nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.drv << shift));
-       nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pre << shift));
-       nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.unk << 8));
+       data[2] = nv_rd32(priv, 0x61c130 + loff);
+       if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0)
+               data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8);
+       nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.dc << shift));
+       nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pe << shift));
+       nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.tx_pu << 8));
        return 0;
 }
 
-const struct nouveau_dp_func
-nv94_sor_dp_func = {
+struct nvkm_output_dp_impl
+nv94_sor_dp_impl = {
+       .base.base.handle = DCB_OUTPUT_DP,
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = _nvkm_output_dp_ctor,
+               .dtor = _nvkm_output_dp_dtor,
+               .init = _nvkm_output_dp_init,
+               .fini = _nvkm_output_dp_fini,
+       },
        .pattern = nv94_sor_dp_pattern,
+       .lnk_pwr = nv94_sor_dp_lnk_pwr,
        .lnk_ctl = nv94_sor_dp_lnk_ctl,
        .drv_ctl = nv94_sor_dp_drv_ctl,
 };
index d2df572f16a3afda530f60a049daed4fac0bfbac..97f0e9cd3d40b71b2a793c086eb18a617da14d39 100644 (file)
 #include <subdev/bios/dcb.h>
 #include <subdev/bios/dp.h>
 #include <subdev/bios/init.h>
+#include <subdev/timer.h>
 
 #include "nv50.h"
 
 static inline u32
-nvd0_sor_soff(struct dcb_output *outp)
+nvd0_sor_soff(struct nvkm_output_dp *outp)
 {
-       return (ffs(outp->or) - 1) * 0x800;
+       return (ffs(outp->base.info.or) - 1) * 0x800;
 }
 
 static inline u32
-nvd0_sor_loff(struct dcb_output *outp)
+nvd0_sor_loff(struct nvkm_output_dp *outp)
 {
-       return nvd0_sor_soff(outp) + !(outp->sorconf.link & 1) * 0x80;
+       return nvd0_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80;
 }
 
 static inline u32
@@ -52,77 +53,80 @@ nvd0_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane)
 }
 
 static int
-nvd0_sor_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp,
-                   int head, int pattern)
+nvd0_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
 {
-       struct nv50_disp_priv *priv = (void *)disp;
+       struct nv50_disp_priv *priv = (void *)nouveau_disp(outp);
        const u32 loff = nvd0_sor_loff(outp);
        nv_mask(priv, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * pattern);
        return 0;
 }
 
 static int
-nvd0_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
-                   int head, int link_nr, int link_bw, bool enh_frame)
+nvd0_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
 {
-       struct nv50_disp_priv *priv = (void *)disp;
+       struct nv50_disp_priv *priv = (void *)nouveau_disp(outp);
        const u32 soff = nvd0_sor_soff(outp);
        const u32 loff = nvd0_sor_loff(outp);
        u32 dpctrl = 0x00000000;
        u32 clksor = 0x00000000;
-       u32 lane = 0;
-       int i;
 
-       clksor |= link_bw << 18;
-       dpctrl |= ((1 << link_nr) - 1) << 16;
-       if (enh_frame)
+       clksor |= bw << 18;
+       dpctrl |= ((1 << nr) - 1) << 16;
+       if (ef)
                dpctrl |= 0x00004000;
 
-       for (i = 0; i < link_nr; i++)
-               lane |= 1 << (nvd0_sor_dp_lane_map(priv, i) >> 3);
-
        nv_mask(priv, 0x612300 + soff, 0x007c0000, clksor);
        nv_mask(priv, 0x61c10c + loff, 0x001f4000, dpctrl);
-       nv_mask(priv, 0x61c130 + loff, 0x0000000f, lane);
        return 0;
 }
 
 static int
-nvd0_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
-                   int head, int lane, int swing, int preem)
+nvd0_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
 {
-       struct nouveau_bios *bios = nouveau_bios(disp);
-       struct nv50_disp_priv *priv = (void *)disp;
-       const u32 shift = nvd0_sor_dp_lane_map(priv, lane);
+       struct nv50_disp_priv *priv = (void *)nouveau_disp(outp);
+       struct nouveau_bios *bios = nouveau_bios(priv);
+       const u32 shift = nvd0_sor_dp_lane_map(priv, ln);
        const u32 loff = nvd0_sor_loff(outp);
-       u32 addr, data[3];
+       u32 addr, data[4];
        u8  ver, hdr, cnt, len;
        struct nvbios_dpout info;
        struct nvbios_dpcfg ocfg;
 
-       addr = nvbios_dpout_match(bios, outp->hasht, outp->hashm,
+       addr = nvbios_dpout_match(bios, outp->base.info.hasht,
+                                       outp->base.info.hashm,
                                 &ver, &hdr, &cnt, &len, &info);
        if (!addr)
                return -ENODEV;
 
-       addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem,
+       addr = nvbios_dpcfg_match(bios, addr, pc, vs, pe,
                                 &ver, &hdr, &cnt, &len, &ocfg);
        if (!addr)
                return -EINVAL;
 
        data[0] = nv_rd32(priv, 0x61c118 + loff) & ~(0x000000ff << shift);
        data[1] = nv_rd32(priv, 0x61c120 + loff) & ~(0x000000ff << shift);
-       data[2] = nv_rd32(priv, 0x61c130 + loff) & ~(0x0000ff00);
-       nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.drv << shift));
-       nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pre << shift));
-       nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.unk << 8));
-       nv_mask(priv, 0x61c13c + loff, 0x00000000, 0x00000000);
+       data[2] = nv_rd32(priv, 0x61c130 + loff);
+       if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0)
+               data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8);
+       nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.dc << shift));
+       nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pe << shift));
+       nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.tx_pu << 8));
+       data[3] = nv_rd32(priv, 0x61c13c + loff) & ~(0x000000ff << shift);
+       nv_wr32(priv, 0x61c13c + loff, data[3] | (ocfg.pc << shift));
        return 0;
 }
 
-const struct nouveau_dp_func
-nvd0_sor_dp_func = {
+struct nvkm_output_dp_impl
+nvd0_sor_dp_impl = {
+       .base.base.handle = DCB_OUTPUT_DP,
+       .base.base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = _nvkm_output_dp_ctor,
+               .dtor = _nvkm_output_dp_dtor,
+               .init = _nvkm_output_dp_init,
+               .fini = _nvkm_output_dp_fini,
+       },
        .pattern = nvd0_sor_dp_pattern,
+       .lnk_pwr = nv94_sor_dp_lnk_pwr,
        .lnk_ctl = nvd0_sor_dp_lnk_ctl,
        .drv_ctl = nvd0_sor_dp_drv_ctl,
 };
index 6f9041ced9a2b131ad0e571091b4383757b3e085..56ed3d73bf8e7bc997855a84203139ac2729228f 100644 (file)
@@ -91,7 +91,7 @@ nouveau_fifo_channel_create_(struct nouveau_object *parent,
        if (!chan->user)
                return -EFAULT;
 
-       nouveau_event_trigger(priv->cevent, 0);
+       nouveau_event_trigger(priv->cevent, 1, 0);
 
        chan->size = size;
        return 0;
@@ -194,11 +194,11 @@ nouveau_fifo_create_(struct nouveau_object *parent,
        if (!priv->channel)
                return -ENOMEM;
 
-       ret = nouveau_event_create(1, &priv->cevent);
+       ret = nouveau_event_create(1, 1, &priv->cevent);
        if (ret)
                return ret;
 
-       ret = nouveau_event_create(1, &priv->uevent);
+       ret = nouveau_event_create(1, 1, &priv->uevent);
        if (ret)
                return ret;
 
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/gk20a.c b/drivers/gpu/drm/nouveau/core/engine/fifo/gk20a.c
new file mode 100644 (file)
index 0000000..327456e
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. 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, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS 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 "nve0.h"
+
+struct nouveau_oclass *
+gk20a_fifo_oclass = &(struct nve0_fifo_impl) {
+       .base.handle = NV_ENGINE(FIFO, 0xea),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nve0_fifo_ctor,
+               .dtor = nve0_fifo_dtor,
+               .init = nve0_fifo_init,
+               .fini = nve0_fifo_fini,
+       },
+       .channels = 128,
+}.base;
index 54f26cc801c722010857f30d745dde967ba968cb..c61b16a63884d9ba98d13dbc8a76014e6330ad79 100644 (file)
@@ -539,7 +539,7 @@ nv04_fifo_intr(struct nouveau_subdev *subdev)
                        }
 
                        if (status & 0x40000000) {
-                               nouveau_event_trigger(priv->base.uevent, 0);
+                               nouveau_event_trigger(priv->base.uevent, 1, 0);
                                nv_wr32(priv, 0x002100, 0x40000000);
                                status &= ~0x40000000;
                        }
index fe0f41e65d9b9c6ab6abfa5b1d5f7b3031fe5557..6e5ac16e54605607827bb0f1ad8b5130e6c559bd 100644 (file)
@@ -389,14 +389,14 @@ nv84_fifo_cclass = {
  ******************************************************************************/
 
 static void
-nv84_fifo_uevent_enable(struct nouveau_event *event, int index)
+nv84_fifo_uevent_enable(struct nouveau_event *event, int type, int index)
 {
        struct nv84_fifo_priv *priv = event->priv;
        nv_mask(priv, 0x002140, 0x40000000, 0x40000000);
 }
 
 static void
-nv84_fifo_uevent_disable(struct nouveau_event *event, int index)
+nv84_fifo_uevent_disable(struct nouveau_event *event, int type, int index)
 {
        struct nv84_fifo_priv *priv = event->priv;
        nv_mask(priv, 0x002140, 0x40000000, 0x00000000);
index fa1e719872b769c79a959efc7b6a62321ebad552..ae4a4dc5642abf600d121ae7771664f30ad6b838 100644 (file)
@@ -730,7 +730,7 @@ nvc0_fifo_intr_engine_unit(struct nvc0_fifo_priv *priv, int engn)
        for (unkn = 0; unkn < 8; unkn++) {
                u32 ints = (intr >> (unkn * 0x04)) & inte;
                if (ints & 0x1) {
-                       nouveau_event_trigger(priv->base.uevent, 0);
+                       nouveau_event_trigger(priv->base.uevent, 1, 0);
                        ints &= ~1;
                }
                if (ints) {
@@ -827,14 +827,14 @@ nvc0_fifo_intr(struct nouveau_subdev *subdev)
 }
 
 static void
-nvc0_fifo_uevent_enable(struct nouveau_event *event, int index)
+nvc0_fifo_uevent_enable(struct nouveau_event *event, int type, int index)
 {
        struct nvc0_fifo_priv *priv = event->priv;
        nv_mask(priv, 0x002140, 0x80000000, 0x80000000);
 }
 
 static void
-nvc0_fifo_uevent_disable(struct nouveau_event *event, int index)
+nvc0_fifo_uevent_disable(struct nouveau_event *event, int type, int index)
 {
        struct nvc0_fifo_priv *priv = event->priv;
        nv_mask(priv, 0x002140, 0x80000000, 0x00000000);
index a9a1a9c9f9f292648bf294eb358f0bc8ec232015..298063edb92d785d546ed075a572ed5fc238c209 100644 (file)
@@ -859,7 +859,7 @@ nve0_fifo_intr_runlist(struct nve0_fifo_priv *priv)
 static void
 nve0_fifo_intr_engine(struct nve0_fifo_priv *priv)
 {
-       nouveau_event_trigger(priv->base.uevent, 0);
+       nouveau_event_trigger(priv->base.uevent, 1, 0);
 }
 
 static void
@@ -952,14 +952,14 @@ nve0_fifo_intr(struct nouveau_subdev *subdev)
 }
 
 static void
-nve0_fifo_uevent_enable(struct nouveau_event *event, int index)
+nve0_fifo_uevent_enable(struct nouveau_event *event, int type, int index)
 {
        struct nve0_fifo_priv *priv = event->priv;
        nv_mask(priv, 0x002140, 0x80000000, 0x80000000);
 }
 
 static void
-nve0_fifo_uevent_disable(struct nouveau_event *event, int index)
+nve0_fifo_uevent_disable(struct nouveau_event *event, int type, int index)
 {
        struct nve0_fifo_priv *priv = event->priv;
        nv_mask(priv, 0x002140, 0x80000000, 0x00000000);
index 014344ebee66c4718ec5a59179b2ca6d1d80076f..e96b32bb1bbc5406bac7d1e72b51e68be4a43da3 100644 (file)
@@ -8,6 +8,7 @@ int  nve0_fifo_ctor(struct nouveau_object *, struct nouveau_object *,
                    struct nouveau_object **);
 void nve0_fifo_dtor(struct nouveau_object *);
 int  nve0_fifo_init(struct nouveau_object *);
+int  nve0_fifo_fini(struct nouveau_object *, bool);
 
 struct nve0_fifo_impl {
        struct nouveau_oclass base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxgk20a.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxgk20a.c
new file mode 100644 (file)
index 0000000..224ee02
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. 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, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS 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 "ctxnvc0.h"
+
+static const struct nvc0_graph_pack
+gk20a_grctx_pack_mthd[] = {
+       { nve4_grctx_init_a097_0, 0xa297 },
+       { nvc0_grctx_init_902d_0, 0x902d },
+       {}
+};
+
+struct nouveau_oclass *
+gk20a_grctx_oclass = &(struct nvc0_grctx_oclass) {
+       .base.handle = NV_ENGCTX(GR, 0xea),
+       .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_pack_hub,
+       .gpc   = nve4_grctx_pack_gpc,
+       .zcull = nvc0_grctx_pack_zcull,
+       .tpc   = nve4_grctx_pack_tpc,
+       .ppc   = nve4_grctx_pack_ppc,
+       .icmd  = nve4_grctx_pack_icmd,
+       .mthd  = gk20a_grctx_pack_mthd,
+}.base;
index 48351b4d6d6bd8460ee83b2110f9e1adce7235bf..8de4a4291548897d85d84a2662f031b364bb3bc9 100644 (file)
@@ -545,10 +545,12 @@ nv108_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
        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, 0x00c20200,  0, 0);
        mmio_list(0x418810, 0x80000000, 12, 2);
        mmio_list(0x419848, 0x10000000, 12, 2);
 
index 9c815d1f99ef9f32171d6ea636291c5aabff6eee..8da8b627b9d0a10299f683781be391074ae31d50 100644 (file)
@@ -69,7 +69,9 @@ extern struct nouveau_oclass *nvd7_grctx_oclass;
 extern struct nouveau_oclass *nvd9_grctx_oclass;
 
 extern struct nouveau_oclass *nve4_grctx_oclass;
+extern struct nouveau_oclass *gk20a_grctx_oclass;
 void nve4_grctx_generate_main(struct nvc0_graph_priv *, struct nvc0_grctx *);
+void nve4_grctx_generate_mods(struct nvc0_graph_priv *, struct nvc0_grctx *);
 void nve4_grctx_generate_unkn(struct nvc0_graph_priv *);
 void nve4_grctx_generate_r418bb8(struct nvc0_graph_priv *);
 
@@ -151,6 +153,13 @@ extern const struct nvc0_graph_init nve4_grctx_init_gpm_0[];
 
 extern const struct nvc0_graph_init nve4_grctx_init_pes_0[];
 
+extern const struct nvc0_graph_pack nve4_grctx_pack_hub[];
+extern const struct nvc0_graph_pack nve4_grctx_pack_gpc[];
+extern const struct nvc0_graph_pack nve4_grctx_pack_tpc[];
+extern const struct nvc0_graph_pack nve4_grctx_pack_ppc[];
+extern const struct nvc0_graph_pack nve4_grctx_pack_icmd[];
+extern const struct nvc0_graph_init nve4_grctx_init_a097_0[];
+
 extern const struct nvc0_graph_pack nvf0_grctx_pack_mthd[];
 
 extern const struct nvc0_graph_init nvf0_grctx_init_pri_0[];
index 49a14b116a5f81fd771da20c756478b7e7dfd839..c5b24923858792fb69869c697e9c34f5431918dd 100644 (file)
@@ -272,13 +272,13 @@ nve4_grctx_init_icmd_0[] = {
        {}
 };
 
-static const struct nvc0_graph_pack
+const struct nvc0_graph_pack
 nve4_grctx_pack_icmd[] = {
        { nve4_grctx_init_icmd_0 },
        {}
 };
 
-static const struct nvc0_graph_init
+const struct nvc0_graph_init
 nve4_grctx_init_a097_0[] = {
        { 0x000800,   8, 0x40, 0x00000000 },
        { 0x000804,   8, 0x40, 0x00000000 },
@@ -697,7 +697,7 @@ nve4_grctx_init_be_0[] = {
        {}
 };
 
-static const struct nvc0_graph_pack
+const struct nvc0_graph_pack
 nve4_grctx_pack_hub[] = {
        { nvc0_grctx_init_main_0 },
        { nve4_grctx_init_fe_0 },
@@ -737,7 +737,7 @@ nve4_grctx_init_gpm_0[] = {
        {}
 };
 
-static const struct nvc0_graph_pack
+const struct nvc0_graph_pack
 nve4_grctx_pack_gpc[] = {
        { nvc0_grctx_init_gpc_unk_0 },
        { nvd9_grctx_init_prop_0 },
@@ -802,7 +802,7 @@ nve4_grctx_init_sm_0[] = {
        {}
 };
 
-static const struct nvc0_graph_pack
+const struct nvc0_graph_pack
 nve4_grctx_pack_tpc[] = {
        { nvd7_grctx_init_pe_0 },
        { nve4_grctx_init_tex_0 },
@@ -826,7 +826,7 @@ nve4_grctx_init_cbm_0[] = {
        {}
 };
 
-static const struct nvc0_graph_pack
+const struct nvc0_graph_pack
 nve4_grctx_pack_ppc[] = {
        { nve4_grctx_init_pes_0 },
        { nve4_grctx_init_cbm_0 },
@@ -838,7 +838,7 @@ nve4_grctx_pack_ppc[] = {
  * PGRAPH context implementation
  ******************************************************************************/
 
-static void
+void
 nve4_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
 {
        u32 magic[GPC_MAX][2];
index 0fab95e49f53b4f8c452b8afd3c114a5b4ad55f2..dec03f04114d8bd0594946183de4bac43dccbe63 100644 (file)
@@ -842,7 +842,7 @@ nvf0_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
                u16 magic3 = 0x0648;
                magic[gpc][0]  = 0x10000000 | (magic0 << 16) | offset;
                magic[gpc][1]  = 0x00000000 | (magic1 << 16);
-               offset += 0x0324 * (priv->tpc_nr[gpc] - 1);;
+               offset += 0x0324 * (priv->tpc_nr[gpc] - 1);
                magic[gpc][2]  = 0x10000000 | (magic2 << 16) | offset;
                magic[gpc][3]  = 0x00000000 | (magic3 << 16);
                offset += 0x0324;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/gk20a.c b/drivers/gpu/drm/nouveau/core/engine/graph/gk20a.c
new file mode 100644 (file)
index 0000000..83048a5
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. 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, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS 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 "nvc0.h"
+#include "ctxnvc0.h"
+
+static struct nouveau_oclass
+gk20a_graph_sclass[] = {
+       { 0x902d, &nouveau_object_ofuncs },
+       { 0xa040, &nouveau_object_ofuncs },
+       { 0xa297, &nouveau_object_ofuncs },
+       { 0xa0c0, &nouveau_object_ofuncs },
+       {}
+};
+
+struct nouveau_oclass *
+gk20a_graph_oclass = &(struct nvc0_graph_oclass) {
+       .base.handle = NV_ENGINE(GR, 0xea),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nvc0_graph_ctor,
+               .dtor = nvc0_graph_dtor,
+               .init = nve4_graph_init,
+               .fini = nve4_graph_fini,
+       },
+       .cclass = &gk20a_grctx_oclass,
+       .sclass = gk20a_graph_sclass,
+       .mmio = nve4_graph_pack_mmio,
+}.base;
index 2c7809e1a09b6c906c3bd58f7cd9e49287bc9fa5..1a2d56493cf6a5ce00612430d6b7aa5429c7bdf1 100644 (file)
@@ -901,7 +901,7 @@ nv50_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                nv_engine(priv)->sclass = nvaf_graph_sclass;
                break;
 
-       };
+       }
 
        /* unfortunate hw bug workaround... */
        if (nv_device(priv)->chipset != 0x50 &&
index f3c7329da0a04c8870ccba95a9b525c2ebc3ddca..bf7bdb1f291efd9e7a2f5c92db554be638f62e4a 100644 (file)
@@ -894,6 +894,10 @@ nvc0_graph_init_fw(struct nvc0_graph_priv *priv, u32 fuc_base,
                        nv_wr32(priv, fuc_base + 0x0188, i >> 6);
                nv_wr32(priv, fuc_base + 0x0184, code->data[i]);
        }
+
+       /* code must be padded to 0x40 words */
+       for (; i & 0x3f; i++)
+               nv_wr32(priv, fuc_base + 0x0184, 0);
 }
 
 static void
@@ -1259,10 +1263,14 @@ nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nvc0_graph_oclass *oclass = (void *)bclass;
        struct nouveau_device *device = nv_device(parent);
        struct nvc0_graph_priv *priv;
+       bool use_ext_fw, enable;
        int ret, i;
 
-       ret = nouveau_graph_create(parent, engine, bclass,
-                                  (oclass->fecs.ucode != NULL), &priv);
+       use_ext_fw = nouveau_boolopt(device->cfgopt, "NvGrUseFW",
+                                    oclass->fecs.ucode == NULL);
+       enable = use_ext_fw || oclass->fecs.ucode != NULL;
+
+       ret = nouveau_graph_create(parent, engine, bclass, enable, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
@@ -1272,7 +1280,7 @@ nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
 
        priv->base.units = nvc0_graph_units;
 
-       if (nouveau_boolopt(device->cfgopt, "NvGrUseFW", false)) {
+       if (use_ext_fw) {
                nv_info(priv, "using external firmware\n");
                if (nvc0_graph_ctor_fw(priv, "fuc409c", &priv->fuc409c) ||
                    nvc0_graph_ctor_fw(priv, "fuc409d", &priv->fuc409d) ||
index 90d44616c876e3d62d23dc619f16a6d24dab4b3a..75203a99d9021e7f18d1d7b721f6e10b2f5dba3a 100644 (file)
@@ -116,6 +116,7 @@ int  nvc0_graph_ctor(struct nouveau_object *, struct nouveau_object *,
                     struct nouveau_object **);
 void nvc0_graph_dtor(struct nouveau_object *);
 int  nvc0_graph_init(struct nouveau_object *);
+int  nve4_graph_fini(struct nouveau_object *, bool);
 int  nve4_graph_init(struct nouveau_object *);
 
 extern struct nouveau_oclass nvc0_graph_sclass[];
@@ -217,6 +218,7 @@ extern const struct nvc0_graph_init nve4_graph_init_main_0[];
 extern const struct nvc0_graph_init nve4_graph_init_tpccs_0[];
 extern const struct nvc0_graph_init nve4_graph_init_pe_0[];
 extern const struct nvc0_graph_init nve4_graph_init_be_0[];
+extern const struct nvc0_graph_pack nve4_graph_pack_mmio[];
 
 extern const struct nvc0_graph_init nvf0_graph_init_fe_0[];
 extern const struct nvc0_graph_init nvf0_graph_init_sked_0[];
index f7c0112171750253214a111931e7ffe3bb9c1c81..51e0c075ad342878b876f2828db31cd19a82aa9b 100644 (file)
@@ -151,7 +151,7 @@ nve4_graph_init_be_0[] = {
        {}
 };
 
-static const struct nvc0_graph_pack
+const struct nvc0_graph_pack
 nve4_graph_pack_mmio[] = {
        { nve4_graph_init_main_0 },
        { nvc0_graph_init_fe_0 },
@@ -189,7 +189,7 @@ nve4_graph_pack_mmio[] = {
  * PGRAPH engine/subdev functions
  ******************************************************************************/
 
-static int
+int
 nve4_graph_fini(struct nouveau_object *object, bool suspend)
 {
        struct nvc0_graph_priv *priv = (void *)object;
index 5ce686ee729ea5d937164d55892a9aebd2312b77..f3b4d9dbf23c4cdb230cb962c1c99743114ad500 100644 (file)
@@ -124,7 +124,7 @@ nv50_software_sclass[] = {
  ******************************************************************************/
 
 static int
-nv50_software_vblsem_release(void *data, int head)
+nv50_software_vblsem_release(void *data, u32 type, int head)
 {
        struct nv50_software_chan *chan = data;
        struct nv50_software_priv *priv = (void *)nv_object(chan)->engine;
@@ -183,7 +183,7 @@ nv50_software_context_ctor(struct nouveau_object *parent,
                return -ENOMEM;
 
        for (i = 0; i < chan->vblank.nr_event; i++) {
-               ret = nouveau_event_new(pdisp->vblank, i, pclass->vblank,
+               ret = nouveau_event_new(pdisp->vblank, 1, i, pclass->vblank,
                                        chan, &chan->vblank.event[i]);
                if (ret)
                        return ret;
index 2de370c212791a080b13401b045f3bbb0bd8ce45..bb49a7a20857d3804f4b59edb287336e4bb42ed9 100644 (file)
@@ -19,7 +19,7 @@ int  nv50_software_ctor(struct nouveau_object *, struct nouveau_object *,
 
 struct nv50_software_cclass {
        struct nouveau_oclass base;
-       int (*vblank)(void *, int);
+       int (*vblank)(void *, u32, int);
 };
 
 struct nv50_software_chan {
index f9430c1bf3e511a343373f2c20bac059dd2f7b82..135c20f383567968f8df77910a242ef220fcaac5 100644 (file)
@@ -104,7 +104,7 @@ nvc0_software_sclass[] = {
  ******************************************************************************/
 
 static int
-nvc0_software_vblsem_release(void *data, int head)
+nvc0_software_vblsem_release(void *data, u32 type, int head)
 {
        struct nv50_software_chan *chan = data;
        struct nv50_software_priv *priv = (void *)nv_object(chan)->engine;
index 9c0cd73462d953971159f94c0d643381662215bd..e0c812bc884f900f5171da05e35ad89e3072da67 100644 (file)
@@ -295,6 +295,10 @@ struct nv04_display_scanoutpos {
 #define NV84_DISP_SOR_HDMI_PWR_REKEY                                 0x0000007f
 #define NV50_DISP_SOR_LVDS_SCRIPT                                    0x00013000
 #define NV50_DISP_SOR_LVDS_SCRIPT_ID                                 0x0000ffff
+#define NV94_DISP_SOR_DP_PWR                                         0x00016000
+#define NV94_DISP_SOR_DP_PWR_STATE                                   0x00000001
+#define NV94_DISP_SOR_DP_PWR_STATE_OFF                               0x00000000
+#define NV94_DISP_SOR_DP_PWR_STATE_ON                                0x00000001
 
 #define NV50_DISP_DAC_MTHD                                           0x00020000
 #define NV50_DISP_DAC_MTHD_TYPE                                      0x0000f000
index 5d539ebff3ed3c48d5280820e5875c434d5dcea8..ba3f1a76a815899ac3da8554575c363454a05453 100644 (file)
@@ -12,32 +12,33 @@ struct nouveau_eventh {
        struct nouveau_event *event;
        struct list_head head;
        unsigned long flags;
+       u32 types;
        int index;
-       int (*func)(void *, int);
+       int (*func)(void *, u32, int);
        void *priv;
 };
 
 struct nouveau_event {
-       spinlock_t list_lock;
-       spinlock_t refs_lock;
-
        void *priv;
-       void (*enable)(struct nouveau_event *, int index);
-       void (*disable)(struct nouveau_event *, int index);
+       int (*check)(struct nouveau_event *, u32 type, int index);
+       void (*enable)(struct nouveau_event *, int type, int index);
+       void (*disable)(struct nouveau_event *, int type, int index);
 
+       int types_nr;
        int index_nr;
-       struct {
-               struct list_head list;
-               int refs;
-       } index[];
+
+       spinlock_t list_lock;
+       struct list_head *list;
+       spinlock_t refs_lock;
+       int refs[];
 };
 
-int  nouveau_event_create(int index_nr, struct nouveau_event **);
+int  nouveau_event_create(int types_nr, int index_nr, struct nouveau_event **);
 void nouveau_event_destroy(struct nouveau_event **);
-void nouveau_event_trigger(struct nouveau_event *, int index);
+void nouveau_event_trigger(struct nouveau_event *, u32 types, int index);
 
-int  nouveau_event_new(struct nouveau_event *, int index,
-                      int (*func)(void *, int), void *,
+int  nouveau_event_new(struct nouveau_event *, u32 types, int index,
+                      int (*func)(void *, u32, int), void *,
                       struct nouveau_eventh **);
 void nouveau_event_ref(struct nouveau_eventh *, struct nouveau_eventh **);
 void nouveau_event_get(struct nouveau_eventh *);
index fd0c68804de33f7da8e890b41b7007f0e64aec12..fde8428968065611acc7e68decb9998e48a56232 100644 (file)
@@ -6,8 +6,19 @@
 #include <core/device.h>
 #include <core/event.h>
 
+enum nvkm_hpd_event {
+       NVKM_HPD_PLUG = 1,
+       NVKM_HPD_UNPLUG = 2,
+       NVKM_HPD_IRQ = 4,
+       NVKM_HPD = (NVKM_HPD_PLUG | NVKM_HPD_UNPLUG | NVKM_HPD_IRQ)
+};
+
 struct nouveau_disp {
        struct nouveau_engine base;
+
+       struct list_head outp;
+       struct nouveau_event *hpd;
+
        struct nouveau_event *vblank;
 };
 
@@ -17,25 +28,6 @@ nouveau_disp(void *obj)
        return (void *)nv_device(obj)->subdev[NVDEV_ENGINE_DISP];
 }
 
-#define nouveau_disp_create(p,e,c,h,i,x,d)                                     \
-       nouveau_disp_create_((p), (e), (c), (h), (i), (x),                     \
-                            sizeof(**d), (void **)d)
-#define nouveau_disp_destroy(d) ({                                             \
-       struct nouveau_disp *disp = (d);                                       \
-       _nouveau_disp_dtor(nv_object(disp));                                   \
-})
-#define nouveau_disp_init(d)                                                   \
-       nouveau_engine_init(&(d)->base)
-#define nouveau_disp_fini(d,s)                                                 \
-       nouveau_engine_fini(&(d)->base, (s))
-
-int  nouveau_disp_create_(struct nouveau_object *, struct nouveau_object *,
-                         struct nouveau_oclass *, int heads,
-                         const char *, const char *, int, void **);
-void _nouveau_disp_dtor(struct nouveau_object *);
-#define _nouveau_disp_init _nouveau_engine_init
-#define _nouveau_disp_fini _nouveau_engine_fini
-
 extern struct nouveau_oclass *nv04_disp_oclass;
 extern struct nouveau_oclass *nv50_disp_oclass;
 extern struct nouveau_oclass *nv84_disp_oclass;
index 26b6b2bb11121e28d4de88f08d4f308ab02fed7a..b639eb2c74ffc2cd087be99336f5774a3a6b4f91 100644 (file)
@@ -109,6 +109,7 @@ extern struct nouveau_oclass *nv50_fifo_oclass;
 extern struct nouveau_oclass *nv84_fifo_oclass;
 extern struct nouveau_oclass *nvc0_fifo_oclass;
 extern struct nouveau_oclass *nve0_fifo_oclass;
+extern struct nouveau_oclass *gk20a_fifo_oclass;
 extern struct nouveau_oclass *nv108_fifo_oclass;
 
 void nv04_fifo_intr(struct nouveau_subdev *);
index 871edfdf3d5ba4b91c87fafef697d61244b4e8a3..8c1d4772da0ccbd29e0dce305635e0c7dbbf8057 100644 (file)
@@ -68,6 +68,7 @@ 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 *gk20a_graph_oclass;
 extern struct nouveau_oclass *nvf0_graph_oclass;
 extern struct nouveau_oclass *nv108_graph_oclass;
 extern struct nouveau_oclass *gm107_graph_oclass;
index a32feb3f3fb67f0e1b662d4563ac2183805ca0b2..f3930c27cb7a242b4f75e09cf77ffded63b3e739 100644 (file)
@@ -22,7 +22,25 @@ enum dcb_connector_type {
        DCB_CONNECTOR_NONE = 0xff
 };
 
-u16 dcb_conntab(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
-u16 dcb_conn(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len);
+struct nvbios_connT {
+};
+
+u32 nvbios_connTe(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
+u32 nvbios_connTp(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+                 struct nvbios_connT *info);
+
+struct nvbios_connE {
+       u8 type;
+       u8 location;
+       u8 hpd;
+       u8 dp;
+       u8 di;
+       u8 sr;
+       u8 lcdid;
+};
+
+u32 nvbios_connEe(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *hdr);
+u32 nvbios_connEp(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *hdr,
+                 struct nvbios_connE *info);
 
 #endif
index 6e54218b55fca0f97086d6f358c260cfebea8880..728206e217774f44332af1c08513ce7af7389299 100644 (file)
@@ -17,9 +17,10 @@ u16 nvbios_dpout_match(struct nouveau_bios *, u16 type, u16 mask,
                       struct nvbios_dpout *);
 
 struct nvbios_dpcfg {
-       u8 drv;
-       u8 pre;
-       u8 unk;
+       u8 pc;
+       u8 dc;
+       u8 pe;
+       u8 tx_pu;
 };
 
 u16
@@ -27,7 +28,7 @@ nvbios_dpcfg_parse(struct nouveau_bios *, u16 outp, u8 idx,
                   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
                   struct nvbios_dpcfg *);
 u16
-nvbios_dpcfg_match(struct nouveau_bios *, u16 outp, u8 un, u8 vs, u8 pe,
+nvbios_dpcfg_match(struct nouveau_bios *, u16 outp, u8 pc, u8 vs, u8 pe,
                   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
                   struct nvbios_dpcfg *);
 
index 8f4ced75444a95478887b4be5791fc99dd3499dc..c01e29c9f89ac023ce24c9229fe1cdf14e5c4c16 100644 (file)
@@ -77,6 +77,8 @@ struct nouveau_clock {
        int tstate; /* thermal adjustment (max-) */
        int dstate; /* display adjustment (min+) */
 
+       bool allow_reclock;
+
        int  (*read)(struct nouveau_clock *, enum nv_clk_src);
        int  (*calc)(struct nouveau_clock *, struct nouveau_cstate *);
        int  (*prog)(struct nouveau_clock *);
@@ -106,8 +108,8 @@ struct nouveau_clocks {
        int mdiv;
 };
 
-#define nouveau_clock_create(p,e,o,i,d)                                        \
-       nouveau_clock_create_((p), (e), (o), (i), sizeof(**d), (void **)d)
+#define nouveau_clock_create(p,e,o,i,r,d)                                      \
+       nouveau_clock_create_((p), (e), (o), (i), (r), sizeof(**d), (void **)d)
 #define nouveau_clock_destroy(p) ({                                            \
        struct nouveau_clock *clk = (p);                                       \
        _nouveau_clock_dtor(nv_object(clk));                                   \
@@ -121,7 +123,7 @@ struct nouveau_clocks {
 
 int  nouveau_clock_create_(struct nouveau_object *, struct nouveau_object *,
                           struct nouveau_oclass *,
-                          struct nouveau_clocks *, int, void **);
+                          struct nouveau_clocks *, bool, int, void **);
 void _nouveau_clock_dtor(struct nouveau_object *);
 int _nouveau_clock_init(struct nouveau_object *);
 #define _nouveau_clock_fini _nouveau_subdev_fini
index 58c7ccdebb018787aa829961ac38f36059806ee7..871e73914b24eb7b75ca6d2d952a16ed4cc72ecc 100644 (file)
@@ -105,6 +105,7 @@ extern struct nouveau_oclass *nvaa_fb_oclass;
 extern struct nouveau_oclass *nvaf_fb_oclass;
 extern struct nouveau_oclass *nvc0_fb_oclass;
 extern struct nouveau_oclass *nve0_fb_oclass;
+extern struct nouveau_oclass *gk20a_fb_oclass;
 extern struct nouveau_oclass *gm107_fb_oclass;
 
 #include <subdev/bios/ramcfg.h>
index c85b9f1579ad89a2f2d0094d75375720d4a97dac..612d82ab683d40a776dd2b93d63d037c5ba99de3 100644 (file)
@@ -8,17 +8,18 @@
 #include <subdev/bios.h>
 #include <subdev/bios/gpio.h>
 
+enum nvkm_gpio_event {
+       NVKM_GPIO_HI = 1,
+       NVKM_GPIO_LO = 2,
+       NVKM_GPIO_TOGGLED = (NVKM_GPIO_HI | NVKM_GPIO_LO),
+};
+
 struct nouveau_gpio {
        struct nouveau_subdev base;
 
        struct nouveau_event *events;
 
-       /* hardware interfaces */
        void (*reset)(struct nouveau_gpio *, u8 func);
-       int  (*drive)(struct nouveau_gpio *, int line, int dir, int out);
-       int  (*sense)(struct nouveau_gpio *, int line);
-
-       /* software interfaces */
        int  (*find)(struct nouveau_gpio *, int idx, u8 tag, u8 line,
                     struct dcb_gpio_func *);
        int  (*set)(struct nouveau_gpio *, int idx, u8 tag, u8 line, int state);
@@ -31,23 +32,10 @@ nouveau_gpio(void *obj)
        return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_GPIO];
 }
 
-#define nouveau_gpio_create(p,e,o,l,d)                                         \
-       nouveau_gpio_create_((p), (e), (o), (l), sizeof(**d), (void **)d)
-#define nouveau_gpio_destroy(p) ({                                             \
-       struct nouveau_gpio *gpio = (p);                                       \
-       _nouveau_gpio_dtor(nv_object(gpio));                                   \
-})
-#define nouveau_gpio_fini(p,s)                                                 \
-       nouveau_subdev_fini(&(p)->base, (s))
-
-int  nouveau_gpio_create_(struct nouveau_object *, struct nouveau_object *,
-                         struct nouveau_oclass *, int, int, void **);
-void _nouveau_gpio_dtor(struct nouveau_object *);
-int  nouveau_gpio_init(struct nouveau_gpio *);
-
-extern struct nouveau_oclass nv10_gpio_oclass;
-extern struct nouveau_oclass nv50_gpio_oclass;
-extern struct nouveau_oclass nvd0_gpio_oclass;
-extern struct nouveau_oclass nve0_gpio_oclass;
+extern struct nouveau_oclass *nv10_gpio_oclass;
+extern struct nouveau_oclass *nv50_gpio_oclass;
+extern struct nouveau_oclass *nv92_gpio_oclass;
+extern struct nouveau_oclass *nvd0_gpio_oclass;
+extern struct nouveau_oclass *nve0_gpio_oclass;
 
 #endif
index 7f50a858b16f68395a3fe0b84c5bd9f275e8849f..db1b39d080135f66094debd56149a8b175f15940 100644 (file)
 #define NV_I2C_TYPE_EXTDDC(e) (0x0005 | (e) << 8)
 #define NV_I2C_TYPE_EXTAUX(e) (0x0006 | (e) << 8)
 
+enum nvkm_i2c_event {
+       NVKM_I2C_PLUG = 1,
+       NVKM_I2C_UNPLUG = 2,
+       NVKM_I2C_IRQ = 4,
+       NVKM_I2C_DONE = 8,
+       NVKM_I2C_ANY = (NVKM_I2C_PLUG |
+                       NVKM_I2C_UNPLUG |
+                       NVKM_I2C_IRQ |
+                       NVKM_I2C_DONE),
+};
+
 struct nouveau_i2c_port {
        struct nouveau_object base;
        struct i2c_adapter adapter;
+       struct mutex mutex;
 
        struct list_head head;
        u8  index;
+       int aux;
 
        const struct nouveau_i2c_func *func;
 };
 
 struct nouveau_i2c_func {
-       void (*acquire)(struct nouveau_i2c_port *);
-       void (*release)(struct nouveau_i2c_port *);
-
        void (*drive_scl)(struct nouveau_i2c_port *, int);
        void (*drive_sda)(struct nouveau_i2c_port *, int);
        int  (*sense_scl)(struct nouveau_i2c_port *);
        int  (*sense_sda)(struct nouveau_i2c_port *);
 
-       int  (*aux)(struct nouveau_i2c_port *, u8, u32, u8 *, u8);
+       int  (*aux)(struct nouveau_i2c_port *, bool, u8, u32, u8 *, u8);
        int  (*pattern)(struct nouveau_i2c_port *, int pattern);
        int  (*lnk_ctl)(struct nouveau_i2c_port *, int nr, int bw, bool enh);
        int  (*drv_ctl)(struct nouveau_i2c_port *, int lane, int sw, int pe);
 };
 
-#define nouveau_i2c_port_create(p,e,o,i,a,f,d)                                 \
-       nouveau_i2c_port_create_((p), (e), (o), (i), (a), (f),                 \
-                                sizeof(**d), (void **)d)
-#define nouveau_i2c_port_destroy(p) ({                                         \
-       struct nouveau_i2c_port *port = (p);                                   \
-       _nouveau_i2c_port_dtor(nv_object(i2c));                                \
-})
-#define nouveau_i2c_port_init(p)                                               \
-       nouveau_object_init(&(p)->base)
-#define nouveau_i2c_port_fini(p,s)                                             \
-       nouveau_object_fini(&(p)->base, (s))
-
-int nouveau_i2c_port_create_(struct nouveau_object *, struct nouveau_object *,
-                            struct nouveau_oclass *, u8,
-                            const struct i2c_algorithm *,
-                            const struct nouveau_i2c_func *,
-                            int, void **);
-void _nouveau_i2c_port_dtor(struct nouveau_object *);
-#define _nouveau_i2c_port_init nouveau_object_init
-#define _nouveau_i2c_port_fini nouveau_object_fini
-
 struct nouveau_i2c_board_info {
        struct i2c_board_info dev;
        u8 udelay; /* set to 0 to use the standard delay */
@@ -67,13 +56,20 @@ struct nouveau_i2c_board_info {
 
 struct nouveau_i2c {
        struct nouveau_subdev base;
+       struct nouveau_event *ntfy;
 
        struct nouveau_i2c_port *(*find)(struct nouveau_i2c *, u8 index);
        struct nouveau_i2c_port *(*find_type)(struct nouveau_i2c *, u16 type);
+       int  (*acquire_pad)(struct nouveau_i2c_port *, unsigned long timeout);
+       void (*release_pad)(struct nouveau_i2c_port *);
+       int  (*acquire)(struct nouveau_i2c_port *, unsigned long timeout);
+       void (*release)(struct nouveau_i2c_port *);
        int (*identify)(struct nouveau_i2c *, int index,
                        const char *what, struct nouveau_i2c_board_info *,
                        bool (*match)(struct nouveau_i2c_port *,
                                      struct i2c_board_info *, void *), void *);
+
+       wait_queue_head_t wait;
        struct list_head ports;
 };
 
@@ -83,37 +79,12 @@ nouveau_i2c(void *obj)
        return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_I2C];
 }
 
-#define nouveau_i2c_create(p,e,o,s,d)                                          \
-       nouveau_i2c_create_((p), (e), (o), (s), sizeof(**d), (void **)d)
-#define nouveau_i2c_destroy(p) ({                                              \
-       struct nouveau_i2c *i2c = (p);                                         \
-       _nouveau_i2c_dtor(nv_object(i2c));                                     \
-})
-#define nouveau_i2c_init(p) ({                                                 \
-       struct nouveau_i2c *i2c = (p);                                         \
-       _nouveau_i2c_init(nv_object(i2c));                                     \
-})
-#define nouveau_i2c_fini(p,s) ({                                               \
-       struct nouveau_i2c *i2c = (p);                                         \
-       _nouveau_i2c_fini(nv_object(i2c), (s));                                \
-})
-
-int nouveau_i2c_create_(struct nouveau_object *, struct nouveau_object *,
-                       struct nouveau_oclass *, struct nouveau_oclass *,
-                       int, void **);
-void _nouveau_i2c_dtor(struct nouveau_object *);
-int  _nouveau_i2c_init(struct nouveau_object *);
-int  _nouveau_i2c_fini(struct nouveau_object *, bool);
-
-extern struct nouveau_oclass nv04_i2c_oclass;
-extern struct nouveau_oclass nv4e_i2c_oclass;
-extern struct nouveau_oclass nv50_i2c_oclass;
-extern struct nouveau_oclass nv94_i2c_oclass;
-extern struct nouveau_oclass nvd0_i2c_oclass;
-extern struct nouveau_oclass nouveau_anx9805_sclass[];
-
-extern const struct i2c_algorithm nouveau_i2c_bit_algo;
-extern const struct i2c_algorithm nouveau_i2c_aux_algo;
+extern struct nouveau_oclass *nv04_i2c_oclass;
+extern struct nouveau_oclass *nv4e_i2c_oclass;
+extern struct nouveau_oclass *nv50_i2c_oclass;
+extern struct nouveau_oclass *nv94_i2c_oclass;
+extern struct nouveau_oclass *nvd0_i2c_oclass;
+extern struct nouveau_oclass *nve0_i2c_oclass;
 
 static inline int
 nv_rdi2cr(struct nouveau_i2c_port *port, u8 addr, u8 reg)
index 88814f159d896a86709de0bbe3666fa435e2e994..31df634c0fdcaf775166b34ffc8a7ac54982ad75 100644 (file)
@@ -30,5 +30,6 @@ nouveau_ibus(void *obj)
 
 extern struct nouveau_oclass nvc0_ibus_oclass;
 extern struct nouveau_oclass nve0_ibus_oclass;
+extern struct nouveau_oclass gk20a_ibus_oclass;
 
 #endif
index bdf594116f3f2a1ab49698669ccf7a2b641c91ee..73b1ed20c8d597b5d6b4c9f67f91b24cb3814d30 100644 (file)
@@ -118,8 +118,10 @@ nouveau_bar_create_(struct nouveau_object *parent,
        if (ret)
                return ret;
 
-       bar->iomem = ioremap(nv_device_resource_start(device, 3),
-                            nv_device_resource_len(device, 3));
+       if (nv_device_resource_len(device, 3) != 0)
+               bar->iomem = ioremap(nv_device_resource_start(device, 3),
+                                    nv_device_resource_len(device, 3));
+
        return 0;
 }
 
index 3f30db62e6563d483d0a330aafe353692865c1c0..ca8139b9ab270f8a61ae836a8abfa62414a84654 100644 (file)
 
 #include "priv.h"
 
+struct nvc0_bar_priv_vm {
+       struct nouveau_gpuobj *mem;
+       struct nouveau_gpuobj *pgd;
+       struct nouveau_vm *vm;
+};
+
 struct nvc0_bar_priv {
        struct nouveau_bar base;
        spinlock_t lock;
-       struct {
-               struct nouveau_gpuobj *mem;
-               struct nouveau_gpuobj *pgd;
-               struct nouveau_vm *vm;
-       } bar[2];
+       struct nvc0_bar_priv_vm bar[2];
 };
 
 static int
@@ -79,87 +81,87 @@ nvc0_bar_unmap(struct nouveau_bar *bar, struct nouveau_vma *vma)
 }
 
 static int
-nvc0_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-             struct nouveau_oclass *oclass, void *data, u32 size,
-             struct nouveau_object **pobject)
+nvc0_bar_init_vm(struct nvc0_bar_priv *priv, struct nvc0_bar_priv_vm *bar_vm,
+                int bar_nr)
 {
-       struct nouveau_device *device = nv_device(parent);
-       struct nvc0_bar_priv *priv;
-       struct nouveau_gpuobj *mem;
+       struct nouveau_device *device = nv_device(&priv->base);
        struct nouveau_vm *vm;
+       resource_size_t bar_len;
        int ret;
 
-       ret = nouveau_bar_create(parent, engine, oclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       /* BAR3 */
        ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 0, 0,
-                               &priv->bar[0].mem);
-       mem = priv->bar[0].mem;
+                               &bar_vm->mem);
        if (ret)
                return ret;
 
        ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x8000, 0, 0,
-                               &priv->bar[0].pgd);
+                               &bar_vm->pgd);
        if (ret)
                return ret;
 
-       ret = nouveau_vm_new(device, 0, nv_device_resource_len(device, 3), 0, &vm);
+       bar_len = nv_device_resource_len(device, bar_nr);
+
+       ret = nouveau_vm_new(device, 0, bar_len, 0, &vm);
        if (ret)
                return ret;
 
        atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]);
 
-       ret = nouveau_gpuobj_new(nv_object(priv), NULL,
-                                (nv_device_resource_len(device, 3) >> 12) * 8,
-                                0x1000, NVOBJ_FLAG_ZERO_ALLOC,
-                                &vm->pgt[0].obj[0]);
-       vm->pgt[0].refcount[0] = 1;
-       if (ret)
-               return ret;
+       /*
+        * Bootstrap page table lookup.
+        */
+       if (bar_nr == 3) {
+               ret = nouveau_gpuobj_new(nv_object(priv), NULL,
+                                        (bar_len >> 12) * 8, 0x1000,
+                                        NVOBJ_FLAG_ZERO_ALLOC,
+                                       &vm->pgt[0].obj[0]);
+               vm->pgt[0].refcount[0] = 1;
+               if (ret)
+                       return ret;
+       }
 
-       ret = nouveau_vm_ref(vm, &priv->bar[0].vm, priv->bar[0].pgd);
+       ret = nouveau_vm_ref(vm, &bar_vm->vm, bar_vm->pgd);
        nouveau_vm_ref(NULL, &vm, NULL);
        if (ret)
                return ret;
 
-       nv_wo32(mem, 0x0200, lower_32_bits(priv->bar[0].pgd->addr));
-       nv_wo32(mem, 0x0204, upper_32_bits(priv->bar[0].pgd->addr));
-       nv_wo32(mem, 0x0208, lower_32_bits(nv_device_resource_len(device, 3) - 1));
-       nv_wo32(mem, 0x020c, upper_32_bits(nv_device_resource_len(device, 3) - 1));
+       nv_wo32(bar_vm->mem, 0x0200, lower_32_bits(bar_vm->pgd->addr));
+       nv_wo32(bar_vm->mem, 0x0204, upper_32_bits(bar_vm->pgd->addr));
+       nv_wo32(bar_vm->mem, 0x0208, lower_32_bits(bar_len - 1));
+       nv_wo32(bar_vm->mem, 0x020c, upper_32_bits(bar_len - 1));
 
-       /* BAR1 */
-       ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 0, 0,
-                               &priv->bar[1].mem);
-       mem = priv->bar[1].mem;
-       if (ret)
-               return ret;
+       return 0;
+}
 
-       ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x8000, 0, 0,
-                               &priv->bar[1].pgd);
-       if (ret)
-               return ret;
+static int
+nvc0_bar_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_bar_priv *priv;
+       bool has_bar3 = nv_device_resource_len(device, 3) != 0;
+       int ret;
 
-       ret = nouveau_vm_new(device, 0, nv_device_resource_len(device, 1), 0, &vm);
+       ret = nouveau_bar_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
        if (ret)
                return ret;
 
-       atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]);
+       /* BAR3 */
+       if (has_bar3) {
+               ret = nvc0_bar_init_vm(priv, &priv->bar[0], 3);
+               if (ret)
+                       return ret;
+               priv->base.alloc = nouveau_bar_alloc;
+               priv->base.kmap = nvc0_bar_kmap;
+       }
 
-       ret = nouveau_vm_ref(vm, &priv->bar[1].vm, priv->bar[1].pgd);
-       nouveau_vm_ref(NULL, &vm, NULL);
+       /* BAR1 */
+       ret = nvc0_bar_init_vm(priv, &priv->bar[1], 1);
        if (ret)
                return ret;
 
-       nv_wo32(mem, 0x0200, lower_32_bits(priv->bar[1].pgd->addr));
-       nv_wo32(mem, 0x0204, upper_32_bits(priv->bar[1].pgd->addr));
-       nv_wo32(mem, 0x0208, lower_32_bits(nv_device_resource_len(device, 1) - 1));
-       nv_wo32(mem, 0x020c, upper_32_bits(nv_device_resource_len(device, 1) - 1));
-
-       priv->base.alloc = nouveau_bar_alloc;
-       priv->base.kmap = nvc0_bar_kmap;
        priv->base.umap = nvc0_bar_umap;
        priv->base.unmap = nvc0_bar_unmap;
        priv->base.flush = nv84_bar_flush;
@@ -201,7 +203,9 @@ nvc0_bar_init(struct nouveau_object *object)
        nv_mask(priv, 0x100c80, 0x00000001, 0x00000000);
 
        nv_wr32(priv, 0x001704, 0x80000000 | priv->bar[1].mem->addr >> 12);
-       nv_wr32(priv, 0x001714, 0xc0000000 | priv->bar[0].mem->addr >> 12);
+       if (priv->bar[0].mem)
+               nv_wr32(priv, 0x001714,
+                       0xc0000000 | priv->bar[0].mem->addr >> 12);
        return 0;
 }
 
index 222e8ebb669dff496534331682e7a77c7e6bd9e0..d45704a2c2df5b6aa1b95eda83d990995955e4c5 100644 (file)
@@ -183,10 +183,11 @@ nouveau_bios_shadow_prom(struct nouveau_bios *bios)
                goto out;
 
        bios->data = kmalloc(bios->size, GFP_KERNEL);
-       if (bios->data) {
-               for (i = 0; i < bios->size; i += 4)
-                       ((u32 *)bios->data)[i/4] = nv_rd32(bios, 0x300000 + i);
-       }
+       if (!bios->data)
+               goto out;
+
+       for (i = 0; i < bios->size; i += 4)
+               ((u32 *)bios->data)[i/4] = nv_rd32(bios, 0x300000 + i);
 
        /* check the PCI record header */
        pcir = nv_ro16(bios, 0x0018);
index 5ac010efd959651d873bc6d186b2dc06dc496968..2ede3bcd96a13f573a965ad35c4eb09baa32f3bd 100644 (file)
 #include <subdev/bios/dcb.h>
 #include <subdev/bios/conn.h>
 
-u16
-dcb_conntab(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+u32
+nvbios_connTe(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
 {
-       u16 dcb = dcb_table(bios, ver, hdr, cnt, len);
+       u32 dcb = dcb_table(bios, ver, hdr, cnt, len);
        if (dcb && *ver >= 0x30 && *hdr >= 0x16) {
-               u16 data = nv_ro16(bios, dcb + 0x14);
+               u32 data = nv_ro16(bios, dcb + 0x14);
                if (data) {
                        *ver = nv_ro08(bios, data + 0);
                        *hdr = nv_ro08(bios, data + 1);
@@ -42,15 +42,59 @@ dcb_conntab(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
                        return data;
                }
        }
-       return 0x0000;
+       return 0x00000000;
 }
 
-u16
-dcb_conn(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len)
+u32
+nvbios_connTp(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+             struct nvbios_connT *info)
+{
+       u32 data = nvbios_connTe(bios, ver, hdr, cnt, len);
+       memset(info, 0x00, sizeof(*info));
+       switch (!!data * *ver) {
+       case 0x30:
+       case 0x40:
+               return data;
+       default:
+               break;
+       }
+       return 0x00000000;
+}
+
+u32
+nvbios_connEe(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len)
 {
        u8  hdr, cnt;
-       u16 data = dcb_conntab(bios, ver, &hdr, &cnt, len);
+       u32 data = nvbios_connTe(bios, ver, &hdr, &cnt, len);
        if (data && idx < cnt)
                return data + hdr + (idx * *len);
-       return 0x0000;
+       return 0x00000000;
+}
+
+u32
+nvbios_connEp(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len,
+             struct nvbios_connE *info)
+{
+       u32 data = nvbios_connEe(bios, idx, ver, len);
+       memset(info, 0x00, sizeof(*info));
+       switch (!!data * *ver) {
+       case 0x30:
+       case 0x40:
+               info->type     =  nv_ro08(bios, data + 0x00);
+               info->location =  nv_ro08(bios, data + 0x01) & 0x0f;
+               info->hpd      = (nv_ro08(bios, data + 0x01) & 0x30) >> 4;
+               info->dp       = (nv_ro08(bios, data + 0x01) & 0xc0) >> 6;
+               if (*len < 4)
+                       return data;
+               info->hpd     |= (nv_ro08(bios, data + 0x02) & 0x03) << 2;
+               info->dp      |=  nv_ro08(bios, data + 0x02) & 0x0c;
+               info->di       = (nv_ro08(bios, data + 0x02) & 0xf0) >> 4;
+               info->hpd     |= (nv_ro08(bios, data + 0x03) & 0x07) << 4;
+               info->sr       = (nv_ro08(bios, data + 0x03) & 0x08) >> 3;
+               info->lcdid    = (nv_ro08(bios, data + 0x03) & 0x70) >> 4;
+               return data;
+       default:
+               break;
+       }
+       return 0x00000000;
 }
index 7628fe7592206b5b33da955bb0115aaa7fe5ca14..f309dd657250f3031af58bcc8a8c56c2569eef13 100644 (file)
@@ -162,18 +162,20 @@ nvbios_dpcfg_parse(struct nouveau_bios *bios, u16 outp, u8 idx,
                   struct nvbios_dpcfg *info)
 {
        u16 data = nvbios_dpcfg_entry(bios, outp, idx, ver, hdr, cnt, len);
+       memset(info, 0x00, sizeof(*info));
        if (data) {
                switch (*ver) {
                case 0x21:
-                       info->drv = nv_ro08(bios, data + 0x02);
-                       info->pre = nv_ro08(bios, data + 0x03);
-                       info->unk = nv_ro08(bios, data + 0x04);
+                       info->dc    = nv_ro08(bios, data + 0x02);
+                       info->pe    = nv_ro08(bios, data + 0x03);
+                       info->tx_pu = nv_ro08(bios, data + 0x04);
                        break;
                case 0x30:
                case 0x40:
-                       info->drv = nv_ro08(bios, data + 0x01);
-                       info->pre = nv_ro08(bios, data + 0x02);
-                       info->unk = nv_ro08(bios, data + 0x03);
+                       info->pc    = nv_ro08(bios, data + 0x00);
+                       info->dc    = nv_ro08(bios, data + 0x01);
+                       info->pe    = nv_ro08(bios, data + 0x02);
+                       info->tx_pu = nv_ro08(bios, data + 0x03);
                        break;
                default:
                        data = 0x0000;
@@ -184,7 +186,7 @@ nvbios_dpcfg_parse(struct nouveau_bios *bios, u16 outp, u8 idx,
 }
 
 u16
-nvbios_dpcfg_match(struct nouveau_bios *bios, u16 outp, u8 un, u8 vs, u8 pe,
+nvbios_dpcfg_match(struct nouveau_bios *bios, u16 outp, u8 pc, u8 vs, u8 pe,
                   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
                   struct nvbios_dpcfg *info)
 {
@@ -193,16 +195,15 @@ nvbios_dpcfg_match(struct nouveau_bios *bios, u16 outp, u8 un, u8 vs, u8 pe,
 
        if (*ver >= 0x30) {
                const u8 vsoff[] = { 0, 4, 7, 9 };
-               idx = (un * 10) + vsoff[vs] + pe;
+               idx = (pc * 10) + vsoff[vs] + pe;
        } else {
-               while ((data = nvbios_dpcfg_entry(bios, outp, idx,
+               while ((data = nvbios_dpcfg_entry(bios, outp, ++idx,
                                                  ver, hdr, cnt, len))) {
                        if (nv_ro08(bios, data + 0x00) == vs &&
                            nv_ro08(bios, data + 0x01) == pe)
                                break;
-                       idx++;
                }
        }
 
-       return nvbios_dpcfg_parse(bios, outp, pe, ver, hdr, cnt, len, info);
+       return nvbios_dpcfg_parse(bios, outp, idx, ver, hdr, cnt, len, info);
 }
index acaeaf79e3f0fa797aa974831d6bbb367ebc78ff..626380f9e4c0192e877bef46b4a154b68c972b39 100644 (file)
@@ -98,15 +98,16 @@ static u8
 init_conn(struct nvbios_init *init)
 {
        struct nouveau_bios *bios = init->bios;
-       u8  ver, len;
-       u16 conn;
+       struct nvbios_connE connE;
+       u8  ver, hdr;
+       u32 conn;
 
        if (init_exec(init)) {
                if (init->outp) {
                        conn = init->outp->connector;
-                       conn = dcb_conn(bios, conn, &ver, &len);
+                       conn = nvbios_connEp(bios, conn, &ver, &hdr, &connE);
                        if (conn)
-                               return nv_ro08(bios, conn);
+                               return connE.type;
                }
 
                error("script needs connector type\n");
index dd62baead39c54c9a906939576553b59bf9355a2..22351f594d2aaa72713fbb3d56f5151ec4c34e20 100644 (file)
@@ -346,8 +346,8 @@ nouveau_clock_ustate_update(struct nouveau_clock *clk, int req)
        struct nouveau_pstate *pstate;
        int i = 0;
 
-       /* YKW repellant */
-       return -ENOSYS;
+       if (!clk->allow_reclock)
+               return -ENOSYS;
 
        if (req != -1 && req != -2) {
                list_for_each_entry(pstate, &clk->states, head) {
@@ -456,6 +456,7 @@ nouveau_clock_create_(struct nouveau_object *parent,
                      struct nouveau_object *engine,
                      struct nouveau_oclass *oclass,
                      struct nouveau_clocks *clocks,
+                     bool allow_reclock,
                      int length, void **object)
 {
        struct nouveau_device *device = nv_device(parent);
@@ -478,6 +479,8 @@ nouveau_clock_create_(struct nouveau_object *parent,
                ret = nouveau_pstate_new(clk, idx++);
        } while (ret == 0);
 
+       clk->allow_reclock = allow_reclock;
+
        mode = nouveau_stropt(device->cfgopt, "NvClkMode", &arglen);
        if (mode) {
                if (!strncasecmpz(mode, "disabled", arglen)) {
index b74db6cfc4e21ee8f811a08e54da3dfdc7f824a1..eb2d4425a49e45e87fe49983fda2fe48c69adf88 100644 (file)
@@ -82,7 +82,8 @@ nv04_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv04_clock_priv *priv;
        int ret;
 
-       ret = nouveau_clock_create(parent, engine, oclass, nv04_domain, &priv);
+       ret = nouveau_clock_create(parent, engine, oclass, nv04_domain, false,
+                                  &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
index db7346f79080a2345c7a08c943ee934b4055a569..8a9e168397912f3d040846f548e5852ed1ca6c61 100644 (file)
@@ -213,7 +213,8 @@ nv40_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv40_clock_priv *priv;
        int ret;
 
-       ret = nouveau_clock_create(parent, engine, oclass, nv40_domain, &priv);
+       ret = nouveau_clock_create(parent, engine, oclass, nv40_domain, true,
+                                  &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
index 250a6d96016bff563946563e93d2aec4a3eeba13..8c132772ba9ea526a5cd2f9339ace5e6a22fd299 100644 (file)
@@ -507,7 +507,7 @@ nv50_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        int ret;
 
        ret = nouveau_clock_create(parent, engine, oclass, pclass->domains,
-                                 &priv);
+                                  false, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
index 4f5a1373f002b9c09e55f36e8a0acd24fd18ec46..9fb58354a80bd12d3be11866bff5078c23f55494 100644 (file)
@@ -302,7 +302,8 @@ nva3_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nva3_clock_priv *priv;
        int ret;
 
-       ret = nouveau_clock_create(parent, engine, oclass, nva3_domain, &priv);
+       ret = nouveau_clock_create(parent, engine, oclass, nva3_domain, false,
+                                  &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
index 7a723b4f564d18f7506506a6f388db9de378ac9d..6a65fc9e9663a2187c4826efa4ddbb8e31400211 100644 (file)
@@ -421,7 +421,8 @@ nvaa_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nvaa_clock_priv *priv;
        int ret;
 
-       ret = nouveau_clock_create(parent, engine, oclass, nvaa_domains, &priv);
+       ret = nouveau_clock_create(parent, engine, oclass, nvaa_domains, true,
+                                  &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
index c3105720ed246c08064c53c92d73db898366bdf7..dbf8517f54da906fc4ad634950217977565092f8 100644 (file)
@@ -437,7 +437,8 @@ nvc0_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nvc0_clock_priv *priv;
        int ret;
 
-       ret = nouveau_clock_create(parent, engine, oclass, nvc0_domain, &priv);
+       ret = nouveau_clock_create(parent, engine, oclass, nvc0_domain, false,
+                                  &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
index d3c37c96f0e7eaeed97c08749bc23611642705db..4ac1aa30ea11f156adc347cc87839ae72ef7ebd1 100644 (file)
@@ -473,7 +473,8 @@ nve0_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nve0_clock_priv *priv;
        int ret;
 
-       ret = nouveau_clock_create(parent, engine, oclass, nve0_domain, &priv);
+       ret = nouveau_clock_create(parent, engine, oclass, nve0_domain, true,
+                                  &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/gk20a.c b/drivers/gpu/drm/nouveau/core/subdev/fb/gk20a.c
new file mode 100644 (file)
index 0000000..a16024a
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. 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, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS 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 "nvc0.h"
+
+struct gk20a_fb_priv {
+       struct nouveau_fb base;
+};
+
+static int
+gk20a_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+            struct nouveau_oclass *oclass, void *data, u32 size,
+            struct nouveau_object **pobject)
+{
+       struct gk20a_fb_priv *priv;
+       int ret;
+
+       ret = nouveau_fb_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+struct nouveau_oclass *
+gk20a_fb_oclass = &(struct nouveau_fb_impl) {
+       .base.handle = NV_SUBDEV(FB, 0xea),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = gk20a_fb_ctor,
+               .dtor = _nouveau_fb_dtor,
+               .init = _nouveau_fb_init,
+               .fini = _nouveau_fb_fini,
+       },
+       .memtype = nvc0_fb_memtype_valid,
+       .ram = &gk20a_ram_oclass,
+}.base;
index da74c889aed47a01b9a51eec66e3e22a5ca7fe9a..82273f832e42ef1fa5d7993e8db299c6e74be499 100644 (file)
@@ -32,6 +32,7 @@ extern struct nouveau_oclass nva3_ram_oclass;
 extern struct nouveau_oclass nvaa_ram_oclass;
 extern struct nouveau_oclass nvc0_ram_oclass;
 extern struct nouveau_oclass nve0_ram_oclass;
+extern struct nouveau_oclass gk20a_ram_oclass;
 extern struct nouveau_oclass gm107_ram_oclass;
 
 int nouveau_sddr3_calc(struct nouveau_ram *ram);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramgk20a.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramgk20a.c
new file mode 100644 (file)
index 0000000..4d77d75
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. 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, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS 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 "priv.h"
+
+#include <subdev/fb.h>
+
+struct gk20a_mem {
+       struct nouveau_mem base;
+       void *cpuaddr;
+       dma_addr_t handle;
+};
+#define to_gk20a_mem(m) container_of(m, struct gk20a_mem, base)
+
+static void
+gk20a_ram_put(struct nouveau_fb *pfb, struct nouveau_mem **pmem)
+{
+       struct device *dev = nv_device_base(nv_device(pfb));
+       struct gk20a_mem *mem = to_gk20a_mem(*pmem);
+
+       *pmem = NULL;
+       if (unlikely(mem == NULL))
+               return;
+
+       if (likely(mem->cpuaddr))
+               dma_free_coherent(dev, mem->base.size << PAGE_SHIFT,
+                                 mem->cpuaddr, mem->handle);
+
+       kfree(mem->base.pages);
+       kfree(mem);
+}
+
+static int
+gk20a_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
+            u32 memtype, struct nouveau_mem **pmem)
+{
+       struct device *dev = nv_device_base(nv_device(pfb));
+       struct gk20a_mem *mem;
+       u32 type = memtype & 0xff;
+       u32 npages, order;
+       int i;
+
+       nv_debug(pfb, "%s: size: %llx align: %x, ncmin: %x\n", __func__, size,
+                align, ncmin);
+
+       npages = size >> PAGE_SHIFT;
+       if (npages == 0)
+               npages = 1;
+
+       if (align == 0)
+               align = PAGE_SIZE;
+       align >>= PAGE_SHIFT;
+
+       /* round alignment to the next power of 2, if needed */
+       order = fls(align);
+       if ((align & (align - 1)) == 0)
+               order--;
+       align = BIT(order);
+
+       /* ensure returned address is correctly aligned */
+       npages = max(align, npages);
+
+       mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+       if (!mem)
+               return -ENOMEM;
+
+       mem->base.size = npages;
+       mem->base.memtype = type;
+
+       mem->base.pages = kzalloc(sizeof(dma_addr_t) * npages, GFP_KERNEL);
+       if (!mem->base.pages) {
+               kfree(mem);
+               return -ENOMEM;
+       }
+
+       *pmem = &mem->base;
+
+       mem->cpuaddr = dma_alloc_coherent(dev, npages << PAGE_SHIFT,
+                                         &mem->handle, GFP_KERNEL);
+       if (!mem->cpuaddr) {
+               nv_error(pfb, "%s: cannot allocate memory!\n", __func__);
+               gk20a_ram_put(pfb, pmem);
+               return -ENOMEM;
+       }
+
+       align <<= PAGE_SHIFT;
+
+       /* alignment check */
+       if (unlikely(mem->handle & (align - 1)))
+               nv_warn(pfb, "memory not aligned as requested: %pad (0x%x)\n",
+                       &mem->handle, align);
+
+       nv_debug(pfb, "alloc size: 0x%x, align: 0x%x, paddr: %pad, vaddr: %p\n",
+                npages << PAGE_SHIFT, align, &mem->handle, mem->cpuaddr);
+
+       for (i = 0; i < npages; i++)
+               mem->base.pages[i] = mem->handle + (PAGE_SIZE * i);
+
+       mem->base.offset = (u64)mem->base.pages[0];
+
+       return 0;
+}
+
+static int
+gk20a_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+             struct nouveau_oclass *oclass, void *data, u32 datasize,
+             struct nouveau_object **pobject)
+{
+       struct nouveau_ram *ram;
+       int ret;
+
+       ret = nouveau_ram_create(parent, engine, oclass, &ram);
+       *pobject = nv_object(ram);
+       if (ret)
+               return ret;
+       ram->type = NV_MEM_TYPE_STOLEN;
+       ram->size = get_num_physpages() << PAGE_SHIFT;
+
+       ram->get = gk20a_ram_get;
+       ram->put = gk20a_ram_put;
+
+       return 0;
+}
+
+struct nouveau_oclass
+gk20a_ram_oclass = {
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = gk20a_ram_ctor,
+               .dtor = _nouveau_ram_dtor,
+               .init = _nouveau_ram_init,
+               .fini = _nouveau_ram_fini,
+       },
+};
index ef91b6e893afc4c4ca488453ea9f19ced5fa5861..e5d12c24cc43e132df49142ba23256c7e99599f5 100644 (file)
@@ -211,7 +211,7 @@ nv50_ram_prog(struct nouveau_fb *pfb)
        struct nv50_ram *ram = (void *)pfb->ram;
        struct nv50_ramseq *hwsq = &ram->hwsq;
 
-       ram_exec(hwsq, nouveau_boolopt(device->cfgopt, "NvMemExec", false));
+       ram_exec(hwsq, nouveau_boolopt(device->cfgopt, "NvMemExec", true));
        return 0;
 }
 
index 6eb97f16fbda97f40b1c0a9efbed47aacf030104..8076fb195dd514569466ae95d1ee12ef929ffab9 100644 (file)
@@ -309,7 +309,7 @@ nva3_ram_prog(struct nouveau_fb *pfb)
        struct nouveau_device *device = nv_device(pfb);
        struct nva3_ram *ram = (void *)pfb->ram;
        struct nva3_ramfuc *fuc = &ram->fuc;
-       ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", false));
+       ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", true));
        return 0;
 }
 
index 8edc92224c84a2ce31b3c5115ff0f31f7c37e0e4..5a6a5027f749f4b20cfd2cfc737be3d719e191ab 100644 (file)
@@ -408,7 +408,7 @@ nvc0_ram_prog(struct nouveau_fb *pfb)
        struct nouveau_device *device = nv_device(pfb);
        struct nvc0_ram *ram = (void *)pfb->ram;
        struct nvc0_ramfuc *fuc = &ram->fuc;
-       ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", false));
+       ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", true));
        return 0;
 }
 
index 16752192cf87acbb5e77a904fb3474e7f2de46d6..84c7efbc4f38bc1e72ca9530bdfb966ecd0805b3 100644 (file)
@@ -1111,7 +1111,7 @@ nve0_ram_prog(struct nouveau_fb *pfb)
        struct nouveau_device *device = nv_device(pfb);
        struct nve0_ram *ram = (void *)pfb->ram;
        struct nve0_ramfuc *fuc = &ram->fuc;
-       ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", false));
+       ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", true));
        return (ram->base.next == &ram->base.xition);
 }
 
index f572c2804c325f01896c061988626eb91b8e2f40..45e0202f3151868ab4b8d95587747a2f280ff7a1 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <subdev/gpio.h>
 #include <subdev/bios.h>
 #include <subdev/bios/gpio.h>
 
+#include "priv.h"
+
 static int
 nouveau_gpio_drive(struct nouveau_gpio *gpio,
                   int idx, int line, int dir, int out)
 {
-       return gpio->drive ? gpio->drive(gpio, line, dir, out) : -ENODEV;
+       const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
+       return impl->drive ? impl->drive(gpio, line, dir, out) : -ENODEV;
 }
 
 static int
 nouveau_gpio_sense(struct nouveau_gpio *gpio, int idx, int line)
 {
-       return gpio->sense ? gpio->sense(gpio, line) : -ENODEV;
+       const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
+       return impl->sense ? impl->sense(gpio, line) : -ENODEV;
 }
 
 static int
@@ -102,6 +105,80 @@ nouveau_gpio_get(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line)
        return ret;
 }
 
+static void
+nouveau_gpio_intr_disable(struct nouveau_event *event, int type, int index)
+{
+       struct nouveau_gpio *gpio = nouveau_gpio(event->priv);
+       const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
+       impl->intr_mask(gpio, type, 1 << index, 0);
+}
+
+static void
+nouveau_gpio_intr_enable(struct nouveau_event *event, int type, int index)
+{
+       struct nouveau_gpio *gpio = nouveau_gpio(event->priv);
+       const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
+       impl->intr_mask(gpio, type, 1 << index, 1 << index);
+}
+
+static void
+nouveau_gpio_intr(struct nouveau_subdev *subdev)
+{
+       struct nouveau_gpio *gpio = nouveau_gpio(subdev);
+       const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
+       u32 hi, lo, e, i;
+
+       impl->intr_stat(gpio, &hi, &lo);
+
+       for (i = 0; e = 0, (hi | lo) && i < impl->lines; i++) {
+               if (hi & (1 << i))
+                       e |= NVKM_GPIO_HI;
+               if (lo & (1 << i))
+                       e |= NVKM_GPIO_LO;
+               nouveau_event_trigger(gpio->events, e, i);
+       }
+}
+
+int
+_nouveau_gpio_fini(struct nouveau_object *object, bool suspend)
+{
+       const struct nouveau_gpio_impl *impl = (void *)object->oclass;
+       struct nouveau_gpio *gpio = nouveau_gpio(object);
+       u32 mask = (1 << impl->lines) - 1;
+
+       impl->intr_mask(gpio, NVKM_GPIO_TOGGLED, mask, 0);
+       impl->intr_stat(gpio, &mask, &mask);
+
+       return nouveau_subdev_fini(&gpio->base, suspend);
+}
+
+static struct dmi_system_id gpio_reset_ids[] = {
+       {
+               .ident = "Apple Macbook 10,1",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10,1"),
+               }
+       },
+       { }
+};
+
+int
+_nouveau_gpio_init(struct nouveau_object *object)
+{
+       struct nouveau_gpio *gpio = nouveau_gpio(object);
+       int ret;
+
+       ret = nouveau_subdev_init(&gpio->base);
+       if (ret)
+               return ret;
+
+       if (gpio->reset && dmi_check_system(gpio_reset_ids))
+               gpio->reset(gpio, DCB_GPIO_UNUSED);
+
+       return ret;
+}
+
 void
 _nouveau_gpio_dtor(struct nouveau_object *object)
 {
@@ -113,9 +190,10 @@ _nouveau_gpio_dtor(struct nouveau_object *object)
 int
 nouveau_gpio_create_(struct nouveau_object *parent,
                     struct nouveau_object *engine,
-                    struct nouveau_oclass *oclass, int lines,
+                    struct nouveau_oclass *oclass,
                     int length, void **pobject)
 {
+       const struct nouveau_gpio_impl *impl = (void *)oclass;
        struct nouveau_gpio *gpio;
        int ret;
 
@@ -125,34 +203,34 @@ nouveau_gpio_create_(struct nouveau_object *parent,
        if (ret)
                return ret;
 
-       ret = nouveau_event_create(lines, &gpio->events);
-       if (ret)
-               return ret;
-
        gpio->find = nouveau_gpio_find;
        gpio->set  = nouveau_gpio_set;
        gpio->get  = nouveau_gpio_get;
+       gpio->reset = impl->reset;
+
+       ret = nouveau_event_create(2, impl->lines, &gpio->events);
+       if (ret)
+               return ret;
+
+       gpio->events->priv = gpio;
+       gpio->events->enable = nouveau_gpio_intr_enable;
+       gpio->events->disable = nouveau_gpio_intr_disable;
+       nv_subdev(gpio)->intr = nouveau_gpio_intr;
        return 0;
 }
 
-static struct dmi_system_id gpio_reset_ids[] = {
-       {
-               .ident = "Apple Macbook 10,1",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10,1"),
-               }
-       },
-       { }
-};
-
 int
-nouveau_gpio_init(struct nouveau_gpio *gpio)
+_nouveau_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                  struct nouveau_oclass *oclass, void *data, u32 size,
+                  struct nouveau_object **pobject)
 {
-       int ret = nouveau_subdev_init(&gpio->base);
-       if (ret == 0 && gpio->reset) {
-               if (dmi_check_system(gpio_reset_ids))
-                       gpio->reset(gpio, DCB_GPIO_UNUSED);
-       }
-       return ret;
+       struct nouveau_gpio *gpio;
+       int ret;
+
+       ret = nouveau_gpio_create(parent, engine, oclass, &gpio);
+       *pobject = nv_object(gpio);
+       if (ret)
+               return ret;
+
+       return 0;
 }
index 76d5d5465ddd2c541c7427508f0046e429109ea1..27ad23eaf1859247ce291e8c8c697ee8c2f857c3 100644 (file)
 
 #include "priv.h"
 
-struct nv10_gpio_priv {
-       struct nouveau_gpio base;
-};
-
 static int
 nv10_gpio_sense(struct nouveau_gpio *gpio, int line)
 {
@@ -83,95 +79,38 @@ nv10_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out)
 }
 
 static void
-nv10_gpio_intr(struct nouveau_subdev *subdev)
-{
-       struct nv10_gpio_priv *priv = (void *)subdev;
-       u32 intr = nv_rd32(priv, 0x001104);
-       u32 hi = (intr & 0x0000ffff) >> 0;
-       u32 lo = (intr & 0xffff0000) >> 16;
-       int i;
-
-       for (i = 0; (hi | lo) && i < 32; i++) {
-               if ((hi | lo) & (1 << i))
-                       nouveau_event_trigger(priv->base.events, i);
-       }
-
-       nv_wr32(priv, 0x001104, intr);
-}
-
-static void
-nv10_gpio_intr_enable(struct nouveau_event *event, int line)
-{
-       nv_wr32(event->priv, 0x001104, 0x00010001 << line);
-       nv_mask(event->priv, 0x001144, 0x00010001 << line, 0x00010001 << line);
-}
-
-static void
-nv10_gpio_intr_disable(struct nouveau_event *event, int line)
-{
-       nv_wr32(event->priv, 0x001104, 0x00010001 << line);
-       nv_mask(event->priv, 0x001144, 0x00010001 << line, 0x00000000);
-}
-
-static int
-nv10_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-              struct nouveau_oclass *oclass, void *data, u32 size,
-              struct nouveau_object **pobject)
+nv10_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo)
 {
-       struct nv10_gpio_priv *priv;
-       int ret;
-
-       ret = nouveau_gpio_create(parent, engine, oclass, 16, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       priv->base.drive = nv10_gpio_drive;
-       priv->base.sense = nv10_gpio_sense;
-       priv->base.events->priv = priv;
-       priv->base.events->enable = nv10_gpio_intr_enable;
-       priv->base.events->disable = nv10_gpio_intr_disable;
-       nv_subdev(priv)->intr = nv10_gpio_intr;
-       return 0;
+       u32 intr = nv_rd32(gpio, 0x001104);
+       u32 stat = nv_rd32(gpio, 0x001144) & intr;
+       *lo = (stat & 0xffff0000) >> 16;
+       *hi = (stat & 0x0000ffff);
+       nv_wr32(gpio, 0x001104, intr);
 }
 
 static void
-nv10_gpio_dtor(struct nouveau_object *object)
-{
-       struct nv10_gpio_priv *priv = (void *)object;
-       nouveau_gpio_destroy(&priv->base);
-}
-
-static int
-nv10_gpio_init(struct nouveau_object *object)
-{
-       struct nv10_gpio_priv *priv = (void *)object;
-       int ret;
-
-       ret = nouveau_gpio_init(&priv->base);
-       if (ret)
-               return ret;
-
-       nv_wr32(priv, 0x001144, 0x00000000);
-       nv_wr32(priv, 0x001104, 0xffffffff);
-       return 0;
-}
-
-static int
-nv10_gpio_fini(struct nouveau_object *object, bool suspend)
+nv10_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data)
 {
-       struct nv10_gpio_priv *priv = (void *)object;
-       nv_wr32(priv, 0x001144, 0x00000000);
-       return nouveau_gpio_fini(&priv->base, suspend);
+       u32 inte = nv_rd32(gpio, 0x001144);
+       if (type & NVKM_GPIO_LO)
+               inte = (inte & ~(mask << 16)) | (data << 16);
+       if (type & NVKM_GPIO_HI)
+               inte = (inte & ~mask) | data;
+       nv_wr32(gpio, 0x001144, inte);
 }
 
-struct nouveau_oclass
-nv10_gpio_oclass = {
-       .handle = NV_SUBDEV(GPIO, 0x10),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv10_gpio_ctor,
-               .dtor = nv10_gpio_dtor,
-               .init = nv10_gpio_init,
-               .fini = nv10_gpio_fini,
+struct nouveau_oclass *
+nv10_gpio_oclass = &(struct nouveau_gpio_impl) {
+       .base.handle = NV_SUBDEV(GPIO, 0x10),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = _nouveau_gpio_ctor,
+               .dtor = _nouveau_gpio_dtor,
+               .init = _nouveau_gpio_init,
+               .fini = _nouveau_gpio_fini,
        },
-};
+       .lines = 16,
+       .intr_stat = nv10_gpio_intr_stat,
+       .intr_mask = nv10_gpio_intr_mask,
+       .drive = nv10_gpio_drive,
+       .sense = nv10_gpio_sense,
+}.base;
index 2ef7747316291c1b40bb3bfdd84d87603473c132..1864fa98e6b1f6e86017360f954047e7e4799637 100644 (file)
 
 #include "priv.h"
 
-struct nv50_gpio_priv {
-       struct nouveau_gpio base;
-};
-
-static void
+void
 nv50_gpio_reset(struct nouveau_gpio *gpio, u8 match)
 {
        struct nouveau_bios *bios = nouveau_bios(gpio);
-       struct nv50_gpio_priv *priv = (void *)gpio;
        u8 ver, len;
        u16 entry;
        int ent = -1;
@@ -55,7 +50,7 @@ nv50_gpio_reset(struct nouveau_gpio *gpio, u8 match)
 
                gpio->set(gpio, 0, func, line, defs);
 
-               nv_mask(priv, reg, 0x00010001 << lsh, val << lsh);
+               nv_mask(gpio, reg, 0x00010001 << lsh, val << lsh);
        }
 }
 
@@ -72,7 +67,7 @@ nv50_gpio_location(int line, u32 *reg, u32 *shift)
        return 0;
 }
 
-static int
+int
 nv50_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out)
 {
        u32 reg, shift;
@@ -84,7 +79,7 @@ nv50_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out)
        return 0;
 }
 
-static int
+int
 nv50_gpio_sense(struct nouveau_gpio *gpio, int line)
 {
        u32 reg, shift;
@@ -95,119 +90,40 @@ nv50_gpio_sense(struct nouveau_gpio *gpio, int line)
        return !!(nv_rd32(gpio, reg) & (4 << shift));
 }
 
-void
-nv50_gpio_intr(struct nouveau_subdev *subdev)
-{
-       struct nv50_gpio_priv *priv = (void *)subdev;
-       u32 intr0, intr1 = 0;
-       u32 hi, lo;
-       int i;
-
-       intr0 = nv_rd32(priv, 0xe054) & nv_rd32(priv, 0xe050);
-       if (nv_device(priv)->chipset > 0x92)
-               intr1 = nv_rd32(priv, 0xe074) & nv_rd32(priv, 0xe070);
-
-       hi = (intr0 & 0x0000ffff) | (intr1 << 16);
-       lo = (intr0 >> 16) | (intr1 & 0xffff0000);
-
-       for (i = 0; (hi | lo) && i < 32; i++) {
-               if ((hi | lo) & (1 << i))
-                       nouveau_event_trigger(priv->base.events, i);
-       }
-
-       nv_wr32(priv, 0xe054, intr0);
-       if (nv_device(priv)->chipset > 0x92)
-               nv_wr32(priv, 0xe074, intr1);
-}
-
-void
-nv50_gpio_intr_enable(struct nouveau_event *event, int line)
-{
-       const u32 addr = line < 16 ? 0xe050 : 0xe070;
-       const u32 mask = 0x00010001 << (line & 0xf);
-       nv_wr32(event->priv, addr + 0x04, mask);
-       nv_mask(event->priv, addr + 0x00, mask, mask);
-}
-
-void
-nv50_gpio_intr_disable(struct nouveau_event *event, int line)
-{
-       const u32 addr = line < 16 ? 0xe050 : 0xe070;
-       const u32 mask = 0x00010001 << (line & 0xf);
-       nv_wr32(event->priv, addr + 0x04, mask);
-       nv_mask(event->priv, addr + 0x00, mask, 0x00000000);
-}
-
-static int
-nv50_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-              struct nouveau_oclass *oclass, void *data, u32 size,
-              struct nouveau_object **pobject)
-{
-       struct nv50_gpio_priv *priv;
-       int ret;
-
-       ret = nouveau_gpio_create(parent, engine, oclass,
-                                 nv_device(parent)->chipset > 0x92 ? 32 : 16,
-                                 &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       priv->base.reset = nv50_gpio_reset;
-       priv->base.drive = nv50_gpio_drive;
-       priv->base.sense = nv50_gpio_sense;
-       priv->base.events->priv = priv;
-       priv->base.events->enable = nv50_gpio_intr_enable;
-       priv->base.events->disable = nv50_gpio_intr_disable;
-       nv_subdev(priv)->intr = nv50_gpio_intr;
-       return 0;
-}
-
-void
-nv50_gpio_dtor(struct nouveau_object *object)
-{
-       struct nv50_gpio_priv *priv = (void *)object;
-       nouveau_gpio_destroy(&priv->base);
-}
-
-int
-nv50_gpio_init(struct nouveau_object *object)
+static void
+nv50_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo)
 {
-       struct nv50_gpio_priv *priv = (void *)object;
-       int ret;
-
-       ret = nouveau_gpio_init(&priv->base);
-       if (ret)
-               return ret;
-
-       /* disable, and ack any pending gpio interrupts */
-       nv_wr32(priv, 0xe050, 0x00000000);
-       nv_wr32(priv, 0xe054, 0xffffffff);
-       if (nv_device(priv)->chipset > 0x92) {
-               nv_wr32(priv, 0xe070, 0x00000000);
-               nv_wr32(priv, 0xe074, 0xffffffff);
-       }
-
-       return 0;
+       u32 intr = nv_rd32(gpio, 0x00e054);
+       u32 stat = nv_rd32(gpio, 0x00e050) & intr;
+       *lo = (stat & 0xffff0000) >> 16;
+       *hi = (stat & 0x0000ffff);
+       nv_wr32(gpio, 0x00e054, intr);
 }
 
-int
-nv50_gpio_fini(struct nouveau_object *object, bool suspend)
+static void
+nv50_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data)
 {
-       struct nv50_gpio_priv *priv = (void *)object;
-       nv_wr32(priv, 0xe050, 0x00000000);
-       if (nv_device(priv)->chipset > 0x92)
-               nv_wr32(priv, 0xe070, 0x00000000);
-       return nouveau_gpio_fini(&priv->base, suspend);
+       u32 inte = nv_rd32(gpio, 0x00e050);
+       if (type & NVKM_GPIO_LO)
+               inte = (inte & ~(mask << 16)) | (data << 16);
+       if (type & NVKM_GPIO_HI)
+               inte = (inte & ~mask) | data;
+       nv_wr32(gpio, 0x00e050, inte);
 }
 
-struct nouveau_oclass
-nv50_gpio_oclass = {
-       .handle = NV_SUBDEV(GPIO, 0x50),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv50_gpio_ctor,
-               .dtor = nv50_gpio_dtor,
-               .init = nv50_gpio_init,
-               .fini = nv50_gpio_fini,
+struct nouveau_oclass *
+nv50_gpio_oclass = &(struct nouveau_gpio_impl) {
+       .base.handle = NV_SUBDEV(GPIO, 0x50),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = _nouveau_gpio_ctor,
+               .dtor = _nouveau_gpio_dtor,
+               .init = _nouveau_gpio_init,
+               .fini = _nouveau_gpio_fini,
        },
-};
+       .lines = 16,
+       .intr_stat = nv50_gpio_intr_stat,
+       .intr_mask = nv50_gpio_intr_mask,
+       .drive = nv50_gpio_drive,
+       .sense = nv50_gpio_sense,
+       .reset = nv50_gpio_reset,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c
new file mode 100644 (file)
index 0000000..252083d
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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 "priv.h"
+
+void
+nv92_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo)
+{
+       u32 intr0 = nv_rd32(gpio, 0x00e054);
+       u32 intr1 = nv_rd32(gpio, 0x00e074);
+       u32 stat0 = nv_rd32(gpio, 0x00e050) & intr0;
+       u32 stat1 = nv_rd32(gpio, 0x00e070) & intr1;
+       *lo = (stat1 & 0xffff0000) | (stat0 >> 16);
+       *hi = (stat1 << 16) | (stat0 & 0x0000ffff);
+       nv_wr32(gpio, 0x00e054, intr0);
+       nv_wr32(gpio, 0x00e074, intr1);
+}
+
+void
+nv92_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data)
+{
+       u32 inte0 = nv_rd32(gpio, 0x00e050);
+       u32 inte1 = nv_rd32(gpio, 0x00e070);
+       if (type & NVKM_GPIO_LO)
+               inte0 = (inte0 & ~(mask << 16)) | (data << 16);
+       if (type & NVKM_GPIO_HI)
+               inte0 = (inte0 & ~(mask & 0xffff)) | (data & 0xffff);
+       mask >>= 16;
+       data >>= 16;
+       if (type & NVKM_GPIO_LO)
+               inte1 = (inte1 & ~(mask << 16)) | (data << 16);
+       if (type & NVKM_GPIO_HI)
+               inte1 = (inte1 & ~mask) | data;
+       nv_wr32(gpio, 0x00e050, inte0);
+       nv_wr32(gpio, 0x00e070, inte1);
+}
+
+struct nouveau_oclass *
+nv92_gpio_oclass = &(struct nouveau_gpio_impl) {
+       .base.handle = NV_SUBDEV(GPIO, 0x92),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = _nouveau_gpio_ctor,
+               .dtor = _nouveau_gpio_dtor,
+               .init = _nouveau_gpio_init,
+               .fini = _nouveau_gpio_fini,
+       },
+       .lines = 32,
+       .intr_stat = nv92_gpio_intr_stat,
+       .intr_mask = nv92_gpio_intr_mask,
+       .drive = nv50_gpio_drive,
+       .sense = nv50_gpio_sense,
+       .reset = nv50_gpio_reset,
+}.base;
index 010431e3acecef1198c7533685e1427bb6362456..a4682b0956ad3ef325451eb083393c4a741e8cee 100644 (file)
 
 #include "priv.h"
 
-struct nvd0_gpio_priv {
-       struct nouveau_gpio base;
-};
-
 void
 nvd0_gpio_reset(struct nouveau_gpio *gpio, u8 match)
 {
        struct nouveau_bios *bios = nouveau_bios(gpio);
-       struct nvd0_gpio_priv *priv = (void *)gpio;
        u8 ver, len;
        u16 entry;
        int ent = -1;
@@ -51,9 +46,9 @@ nvd0_gpio_reset(struct nouveau_gpio *gpio, u8 match)
 
                gpio->set(gpio, 0, func, line, defs);
 
-               nv_mask(priv, 0x00d610 + (line * 4), 0xff, unk0);
+               nv_mask(gpio, 0x00d610 + (line * 4), 0xff, unk0);
                if (unk1--)
-                       nv_mask(priv, 0x00d740 + (unk1 * 4), 0xff, line);
+                       nv_mask(gpio, 0x00d740 + (unk1 * 4), 0xff, line);
        }
 }
 
@@ -72,36 +67,19 @@ nvd0_gpio_sense(struct nouveau_gpio *gpio, int line)
        return !!(nv_rd32(gpio, 0x00d610 + (line * 4)) & 0x00004000);
 }
 
-static int
-nvd0_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-              struct nouveau_oclass *oclass, void *data, u32 size,
-              struct nouveau_object **pobject)
-{
-       struct nvd0_gpio_priv *priv;
-       int ret;
-
-       ret = nouveau_gpio_create(parent, engine, oclass, 32, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       priv->base.reset = nvd0_gpio_reset;
-       priv->base.drive = nvd0_gpio_drive;
-       priv->base.sense = nvd0_gpio_sense;
-       priv->base.events->priv = priv;
-       priv->base.events->enable = nv50_gpio_intr_enable;
-       priv->base.events->disable = nv50_gpio_intr_disable;
-       nv_subdev(priv)->intr = nv50_gpio_intr;
-       return 0;
-}
-
-struct nouveau_oclass
-nvd0_gpio_oclass = {
-       .handle = NV_SUBDEV(GPIO, 0xd0),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nvd0_gpio_ctor,
-               .dtor = nv50_gpio_dtor,
-               .init = nv50_gpio_init,
-               .fini = nv50_gpio_fini,
+struct nouveau_oclass *
+nvd0_gpio_oclass = &(struct nouveau_gpio_impl) {
+       .base.handle = NV_SUBDEV(GPIO, 0xd0),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = _nouveau_gpio_ctor,
+               .dtor = _nouveau_gpio_dtor,
+               .init = _nouveau_gpio_init,
+               .fini = _nouveau_gpio_fini,
        },
-};
+       .lines = 32,
+       .intr_stat = nv92_gpio_intr_stat,
+       .intr_mask = nv92_gpio_intr_mask,
+       .drive = nvd0_gpio_drive,
+       .sense = nvd0_gpio_sense,
+       .reset = nvd0_gpio_reset,
+}.base;
index 16b8c5bf5efa90574495c3fb6530d9a66f487f8b..e1145b48c76c94d1d8bf51b2cc7188559a82feab 100644 (file)
 
 #include "priv.h"
 
-struct nve0_gpio_priv {
-       struct nouveau_gpio base;
-};
-
-void
-nve0_gpio_intr(struct nouveau_subdev *subdev)
+static void
+nve0_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo)
 {
-       struct nve0_gpio_priv *priv = (void *)subdev;
-       u32 intr0 = nv_rd32(priv, 0xdc00) & nv_rd32(priv, 0xdc08);
-       u32 intr1 = nv_rd32(priv, 0xdc80) & nv_rd32(priv, 0xdc88);
-       u32 hi = (intr0 & 0x0000ffff) | (intr1 << 16);
-       u32 lo = (intr0 >> 16) | (intr1 & 0xffff0000);
-       int i;
-
-       for (i = 0; (hi | lo) && i < 32; i++) {
-               if ((hi | lo) & (1 << i))
-                       nouveau_event_trigger(priv->base.events, i);
-       }
-
-       nv_wr32(priv, 0xdc00, intr0);
-       nv_wr32(priv, 0xdc88, intr1);
+       u32 intr0 = nv_rd32(gpio, 0x00dc00);
+       u32 intr1 = nv_rd32(gpio, 0x00dc80);
+       u32 stat0 = nv_rd32(gpio, 0x00dc08) & intr0;
+       u32 stat1 = nv_rd32(gpio, 0x00dc88) & intr1;
+       *lo = (stat1 & 0xffff0000) | (stat0 >> 16);
+       *hi = (stat1 << 16) | (stat0 & 0x0000ffff);
+       nv_wr32(gpio, 0x00dc00, intr0);
+       nv_wr32(gpio, 0x00dc80, intr1);
 }
 
 void
-nve0_gpio_intr_enable(struct nouveau_event *event, int line)
+nve0_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data)
 {
-       const u32 addr = line < 16 ? 0xdc00 : 0xdc80;
-       const u32 mask = 0x00010001 << (line & 0xf);
-       nv_wr32(event->priv, addr + 0x08, mask);
-       nv_mask(event->priv, addr + 0x00, mask, mask);
-}
-
-void
-nve0_gpio_intr_disable(struct nouveau_event *event, int line)
-{
-       const u32 addr = line < 16 ? 0xdc00 : 0xdc80;
-       const u32 mask = 0x00010001 << (line & 0xf);
-       nv_wr32(event->priv, addr + 0x08, mask);
-       nv_mask(event->priv, addr + 0x00, mask, 0x00000000);
-}
-
-int
-nve0_gpio_fini(struct nouveau_object *object, bool suspend)
-{
-       struct nve0_gpio_priv *priv = (void *)object;
-       nv_wr32(priv, 0xdc08, 0x00000000);
-       nv_wr32(priv, 0xdc88, 0x00000000);
-       return nouveau_gpio_fini(&priv->base, suspend);
-}
-
-int
-nve0_gpio_init(struct nouveau_object *object)
-{
-       struct nve0_gpio_priv *priv = (void *)object;
-       int ret;
-
-       ret = nouveau_gpio_init(&priv->base);
-       if (ret)
-               return ret;
-
-       nv_wr32(priv, 0xdc00, 0xffffffff);
-       nv_wr32(priv, 0xdc80, 0xffffffff);
-       return 0;
-}
-
-void
-nve0_gpio_dtor(struct nouveau_object *object)
-{
-       struct nve0_gpio_priv *priv = (void *)object;
-       nouveau_gpio_destroy(&priv->base);
-}
-
-static int
-nve0_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-              struct nouveau_oclass *oclass, void *data, u32 size,
-              struct nouveau_object **pobject)
-{
-       struct nve0_gpio_priv *priv;
-       int ret;
-
-       ret = nouveau_gpio_create(parent, engine, oclass, 32, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       priv->base.reset = nvd0_gpio_reset;
-       priv->base.drive = nvd0_gpio_drive;
-       priv->base.sense = nvd0_gpio_sense;
-       priv->base.events->priv = priv;
-       priv->base.events->enable = nve0_gpio_intr_enable;
-       priv->base.events->disable = nve0_gpio_intr_disable;
-       nv_subdev(priv)->intr = nve0_gpio_intr;
-       return 0;
+       u32 inte0 = nv_rd32(gpio, 0x00dc08);
+       u32 inte1 = nv_rd32(gpio, 0x00dc88);
+       if (type & NVKM_GPIO_LO)
+               inte0 = (inte0 & ~(mask << 16)) | (data << 16);
+       if (type & NVKM_GPIO_HI)
+               inte0 = (inte0 & ~(mask & 0xffff)) | (data & 0xffff);
+       mask >>= 16;
+       data >>= 16;
+       if (type & NVKM_GPIO_LO)
+               inte1 = (inte1 & ~(mask << 16)) | (data << 16);
+       if (type & NVKM_GPIO_HI)
+               inte1 = (inte1 & ~mask) | data;
+       nv_wr32(gpio, 0x00dc08, inte0);
+       nv_wr32(gpio, 0x00dc88, inte1);
 }
 
-struct nouveau_oclass
-nve0_gpio_oclass = {
-       .handle = NV_SUBDEV(GPIO, 0xe0),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nve0_gpio_ctor,
-               .dtor = nv50_gpio_dtor,
-               .init = nve0_gpio_init,
-               .fini = nve0_gpio_fini,
+struct nouveau_oclass *
+nve0_gpio_oclass = &(struct nouveau_gpio_impl) {
+       .base.handle = NV_SUBDEV(GPIO, 0xe0),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = _nouveau_gpio_ctor,
+               .dtor = _nouveau_gpio_dtor,
+               .init = _nouveau_gpio_init,
+               .fini = _nouveau_gpio_fini,
        },
-};
+       .lines = 32,
+       .intr_stat = nve0_gpio_intr_stat,
+       .intr_mask = nve0_gpio_intr_mask,
+       .drive = nvd0_gpio_drive,
+       .sense = nvd0_gpio_sense,
+       .reset = nvd0_gpio_reset,
+}.base;
index 2ee1c895c7825ab46d21ca8a67ceb1762f90fd38..e1724dfc86ae2edfe9801e59acd749203f304977 100644 (file)
@@ -3,15 +3,65 @@
 
 #include <subdev/gpio.h>
 
-void nv50_gpio_dtor(struct nouveau_object *);
-int  nv50_gpio_init(struct nouveau_object *);
-int  nv50_gpio_fini(struct nouveau_object *, bool);
-void nv50_gpio_intr(struct nouveau_subdev *);
-void nv50_gpio_intr_enable(struct nouveau_event *, int line);
-void nv50_gpio_intr_disable(struct nouveau_event *, int line);
+#define nouveau_gpio_create(p,e,o,d)                                           \
+       nouveau_gpio_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nouveau_gpio_destroy(p) ({                                             \
+       struct nouveau_gpio *gpio = (p);                                       \
+       _nouveau_gpio_dtor(nv_object(gpio));                                   \
+})
+#define nouveau_gpio_init(p) ({                                                \
+       struct nouveau_gpio *gpio = (p);                                       \
+       _nouveau_gpio_init(nv_object(gpio));                                   \
+})
+#define nouveau_gpio_fini(p,s) ({                                              \
+       struct nouveau_gpio *gpio = (p);                                       \
+       _nouveau_gpio_fini(nv_object(gpio), (s));                              \
+})
+
+int  nouveau_gpio_create_(struct nouveau_object *, struct nouveau_object *,
+                         struct nouveau_oclass *, int, void **);
+int  _nouveau_gpio_ctor(struct nouveau_object *, struct nouveau_object *,
+                       struct nouveau_oclass *, void *, u32,
+                       struct nouveau_object **);
+void _nouveau_gpio_dtor(struct nouveau_object *);
+int  _nouveau_gpio_init(struct nouveau_object *);
+int  _nouveau_gpio_fini(struct nouveau_object *, bool);
+
+struct nouveau_gpio_impl {
+       struct nouveau_oclass base;
+       int lines;
+
+       /* read and ack pending interrupts, returning only data
+        * for lines that have not been masked off, while still
+        * performing the ack for anything that was pending.
+        */
+       void (*intr_stat)(struct nouveau_gpio *, u32 *, u32 *);
+
+       /* mask on/off interrupts for hi/lo transitions on a
+        * given set of gpio lines
+        */
+       void (*intr_mask)(struct nouveau_gpio *, u32, u32, u32);
+
+       /* configure gpio direction and output value */
+       int  (*drive)(struct nouveau_gpio *, int line, int dir, int out);
+
+       /* sense current state of given gpio line */
+       int  (*sense)(struct nouveau_gpio *, int line);
+
+       /*XXX*/
+       void (*reset)(struct nouveau_gpio *, u8);
+};
+
+void nv50_gpio_reset(struct nouveau_gpio *, u8);
+int  nv50_gpio_drive(struct nouveau_gpio *, int, int, int);
+int  nv50_gpio_sense(struct nouveau_gpio *, int);
+
+void nv92_gpio_intr_stat(struct nouveau_gpio *, u32 *, u32 *);
+void nv92_gpio_intr_mask(struct nouveau_gpio *, u32, u32, u32);
 
 void nvd0_gpio_reset(struct nouveau_gpio *, u8);
 int  nvd0_gpio_drive(struct nouveau_gpio *, int, int, int);
 int  nvd0_gpio_sense(struct nouveau_gpio *, int);
 
+
 #endif
index 4b195ac4da6609dc2be937415822d5bf33ad1b35..2c2731a6cf91f6fb98b5c53f10e4b14a17df4a59 100644 (file)
@@ -22,7 +22,7 @@
  * Authors: Ben Skeggs <bskeggs@redhat.com>
  */
 
-#include <subdev/i2c.h>
+#include "port.h"
 
 struct anx9805_i2c_port {
        struct nouveau_i2c_port base;
@@ -37,6 +37,8 @@ anx9805_train(struct nouveau_i2c_port *port, int link_nr, int link_bw, bool enh)
        struct nouveau_i2c_port *mast = (void *)nv_object(chan)->parent;
        u8 tmp, i;
 
+       DBG("ANX9805 train %d 0x%02x %d\n", link_nr, link_bw, enh);
+
        nv_wri2cr(mast, chan->addr, 0xa0, link_bw);
        nv_wri2cr(mast, chan->addr, 0xa1, link_nr | (enh ? 0x80 : 0x00));
        nv_wri2cr(mast, chan->addr, 0xa2, 0x01);
@@ -60,21 +62,29 @@ anx9805_train(struct nouveau_i2c_port *port, int link_nr, int link_bw, bool enh)
 }
 
 static int
-anx9805_aux(struct nouveau_i2c_port *port, u8 type, u32 addr, u8 *data, u8 size)
+anx9805_aux(struct nouveau_i2c_port *port, bool retry,
+           u8 type, u32 addr, u8 *data, u8 size)
 {
        struct anx9805_i2c_port *chan = (void *)port;
        struct nouveau_i2c_port *mast = (void *)nv_object(chan)->parent;
        int i, ret = -ETIMEDOUT;
+       u8 buf[16] = {};
        u8 tmp;
 
+       DBG("%02x %05x %d\n", type, addr, size);
+
        tmp = nv_rdi2cr(mast, chan->ctrl, 0x07) & ~0x04;
        nv_wri2cr(mast, chan->ctrl, 0x07, tmp | 0x04);
        nv_wri2cr(mast, chan->ctrl, 0x07, tmp);
        nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01);
 
        nv_wri2cr(mast, chan->addr, 0xe4, 0x80);
-       for (i = 0; !(type & 1) && i < size; i++)
-               nv_wri2cr(mast, chan->addr, 0xf0 + i, data[i]);
+       if (!(type & 1)) {
+               memcpy(buf, data, size);
+               DBG("%16ph", buf);
+               for (i = 0; i < size; i++)
+                       nv_wri2cr(mast, chan->addr, 0xf0 + i, buf[i]);
+       }
        nv_wri2cr(mast, chan->addr, 0xe5, ((size - 1) << 4) | type);
        nv_wri2cr(mast, chan->addr, 0xe6, (addr & 0x000ff) >>  0);
        nv_wri2cr(mast, chan->addr, 0xe7, (addr & 0x0ff00) >>  8);
@@ -93,8 +103,13 @@ anx9805_aux(struct nouveau_i2c_port *port, u8 type, u32 addr, u8 *data, u8 size)
                goto done;
        }
 
-       for (i = 0; (type & 1) && i < size; i++)
-               data[i] = nv_rdi2cr(mast, chan->addr, 0xf0 + i);
+       if (type & 1) {
+               for (i = 0; i < size; i++)
+                       buf[i] = nv_rdi2cr(mast, chan->addr, 0xf0 + i);
+               DBG("%16ph", buf);
+               memcpy(data, buf, size);
+       }
+
        ret = 0;
 done:
        nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01);
index 5de074ad170b42ae37722ba8975018428b59a485..02eb42be2e9e91f674dd4d20479c0cc3ea125ecf 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <subdev/i2c.h>
+#include "priv.h"
 
 int
 nv_rdaux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size)
 {
+       struct nouveau_i2c *i2c = nouveau_i2c(port);
        if (port->func->aux) {
-               if (port->func->acquire)
-                       port->func->acquire(port);
-               return port->func->aux(port, 9, addr, data, size);
+               int ret = i2c->acquire(port, 0);
+               if (ret == 0) {
+                       ret = port->func->aux(port, true, 9, addr, data, size);
+                       i2c->release(port);
+               }
+               return ret;
        }
        return -ENODEV;
 }
@@ -38,10 +42,14 @@ nv_rdaux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size)
 int
 nv_wraux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size)
 {
+       struct nouveau_i2c *i2c = nouveau_i2c(port);
        if (port->func->aux) {
-               if (port->func->acquire)
-                       port->func->acquire(port);
-               return port->func->aux(port, 8, addr, data, size);
+               int ret = i2c->acquire(port, 0);
+               if (ret == 0) {
+                       ret = port->func->aux(port, true, 8, addr, data, size);
+                       i2c->release(port);
+               }
+               return ret;
        }
        return -ENODEV;
 }
@@ -50,13 +58,16 @@ static int
 aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 {
        struct nouveau_i2c_port *port = adap->algo_data;
+       struct nouveau_i2c *i2c = nouveau_i2c(port);
        struct i2c_msg *msg = msgs;
        int ret, mcnt = num;
 
        if (!port->func->aux)
                return -ENODEV;
-       if ( port->func->acquire)
-               port->func->acquire(port);
+
+       ret = i2c->acquire(port, 0);
+       if (ret)
+               return ret;
 
        while (mcnt--) {
                u8 remaining = msg->len;
@@ -74,9 +85,11 @@ aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
                        if (mcnt || remaining > 16)
                                cmd |= 4; /* MOT */
 
-                       ret = port->func->aux(port, cmd, msg->addr, ptr, cnt);
-                       if (ret < 0)
+                       ret = port->func->aux(port, true, cmd, msg->addr, ptr, cnt);
+                       if (ret < 0) {
+                               i2c->release(port);
                                return ret;
+                       }
 
                        ptr += cnt;
                        remaining -= cnt;
@@ -85,6 +98,7 @@ aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
                msg++;
        }
 
+       i2c->release(port);
        return num;
 }
 
index 378e05b88e6f0c4562637d00e21aed7285c5b69f..09ba2cc851cfa31e0e24629219ffab13402d74ea 100644 (file)
  */
 
 #include <core/option.h>
+#include <core/event.h>
 
 #include <subdev/bios.h>
 #include <subdev/bios/dcb.h>
 #include <subdev/bios/i2c.h>
-#include <subdev/i2c.h>
 #include <subdev/vga.h>
 
+#include "priv.h"
+#include "pad.h"
+
 /******************************************************************************
  * interface to linux i2c bit-banging algorithm
  *****************************************************************************/
@@ -45,9 +48,15 @@ nouveau_i2c_pre_xfer(struct i2c_adapter *adap)
 {
        struct i2c_algo_bit_data *bit = adap->algo_data;
        struct nouveau_i2c_port *port = bit->data;
-       if (port->func->acquire)
-               port->func->acquire(port);
-       return 0;
+       return nouveau_i2c(port)->acquire(port, bit->timeout);
+}
+
+static void
+nouveau_i2c_post_xfer(struct i2c_adapter *adap)
+{
+       struct i2c_algo_bit_data *bit = adap->algo_data;
+       struct nouveau_i2c_port *port = bit->data;
+       return nouveau_i2c(port)->release(port);
 }
 
 static void
@@ -82,6 +91,15 @@ nouveau_i2c_getsda(void *data)
  * base i2c "port" class implementation
  *****************************************************************************/
 
+int
+_nouveau_i2c_port_fini(struct nouveau_object *object, bool suspend)
+{
+       struct nouveau_i2c_port *port = (void *)object;
+       struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port);
+       nv_ofuncs(pad)->fini(nv_object(pad), suspend);
+       return nouveau_object_fini(&port->base, suspend);
+}
+
 void
 _nouveau_i2c_port_dtor(struct nouveau_object *object)
 {
@@ -98,7 +116,7 @@ nouveau_i2c_port_create_(struct nouveau_object *parent,
                         const struct nouveau_i2c_func *func,
                         int size, void **pobject)
 {
-       struct nouveau_device *device = nv_device(parent);
+       struct nouveau_device *device = nv_device(engine);
        struct nouveau_i2c *i2c = (void *)engine;
        struct nouveau_i2c_port *port;
        int ret;
@@ -113,8 +131,9 @@ nouveau_i2c_port_create_(struct nouveau_object *parent,
        port->adapter.owner = THIS_MODULE;
        port->adapter.dev.parent = nv_device_base(device);
        port->index = index;
+       port->aux = -1;
        port->func = func;
-       i2c_set_adapdata(&port->adapter, i2c);
+       mutex_init(&port->mutex);
 
        if ( algo == &nouveau_i2c_bit_algo &&
            !nouveau_boolopt(device->cfgopt, "NvI2C", CSTMSEL)) {
@@ -128,6 +147,7 @@ nouveau_i2c_port_create_(struct nouveau_object *parent,
                bit->timeout = usecs_to_jiffies(2200);
                bit->data = port;
                bit->pre_xfer = nouveau_i2c_pre_xfer;
+               bit->post_xfer = nouveau_i2c_post_xfer;
                bit->setsda = nouveau_i2c_setsda;
                bit->setscl = nouveau_i2c_setscl;
                bit->getsda = nouveau_i2c_getsda;
@@ -141,7 +161,6 @@ nouveau_i2c_port_create_(struct nouveau_object *parent,
                ret = i2c_add_adapter(&port->adapter);
        }
 
-       /* drop port's i2c subdev refcount, i2c handles this itself */
        if (ret == 0)
                list_add_tail(&port->head, &i2c->ports);
        return ret;
@@ -193,6 +212,75 @@ nouveau_i2c_find_type(struct nouveau_i2c *i2c, u16 type)
        return NULL;
 }
 
+static void
+nouveau_i2c_release_pad(struct nouveau_i2c_port *port)
+{
+       struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port);
+       struct nouveau_i2c *i2c = nouveau_i2c(port);
+
+       if (atomic_dec_and_test(&nv_object(pad)->usecount)) {
+               nv_ofuncs(pad)->fini(nv_object(pad), false);
+               wake_up_all(&i2c->wait);
+       }
+}
+
+static int
+nouveau_i2c_try_acquire_pad(struct nouveau_i2c_port *port)
+{
+       struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port);
+
+       if (atomic_add_return(1, &nv_object(pad)->usecount) != 1) {
+               struct nouveau_object *owner = (void *)pad->port;
+               do {
+                       if (owner == (void *)port)
+                               return 0;
+                       owner = owner->parent;
+               } while(owner);
+               nouveau_i2c_release_pad(port);
+               return -EBUSY;
+       }
+
+       pad->next = port;
+       nv_ofuncs(pad)->init(nv_object(pad));
+       return 0;
+}
+
+static int
+nouveau_i2c_acquire_pad(struct nouveau_i2c_port *port, unsigned long timeout)
+{
+       struct nouveau_i2c *i2c = nouveau_i2c(port);
+
+       if (timeout) {
+               if (wait_event_timeout(i2c->wait,
+                                      nouveau_i2c_try_acquire_pad(port) == 0,
+                                      timeout) == 0)
+                       return -EBUSY;
+       } else {
+               wait_event(i2c->wait, nouveau_i2c_try_acquire_pad(port) == 0);
+       }
+
+       return 0;
+}
+
+static void
+nouveau_i2c_release(struct nouveau_i2c_port *port)
+__releases(pad->mutex)
+{
+       nouveau_i2c(port)->release_pad(port);
+       mutex_unlock(&port->mutex);
+}
+
+static int
+nouveau_i2c_acquire(struct nouveau_i2c_port *port, unsigned long timeout)
+__acquires(pad->mutex)
+{
+       int ret;
+       mutex_lock(&port->mutex);
+       if ((ret = nouveau_i2c(port)->acquire_pad(port, timeout)))
+               mutex_unlock(&port->mutex);
+       return ret;
+}
+
 static int
 nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what,
                     struct nouveau_i2c_board_info *info,
@@ -237,11 +325,59 @@ nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what,
        return -ENODEV;
 }
 
+static void
+nouveau_i2c_intr_disable(struct nouveau_event *event, int type, int index)
+{
+       struct nouveau_i2c *i2c = nouveau_i2c(event->priv);
+       struct nouveau_i2c_port *port = i2c->find(i2c, index);
+       const struct nouveau_i2c_impl *impl = (void *)nv_object(i2c)->oclass;
+       if (port && port->aux >= 0)
+               impl->aux_mask(i2c, type, 1 << port->aux, 0);
+}
+
+static void
+nouveau_i2c_intr_enable(struct nouveau_event *event, int type, int index)
+{
+       struct nouveau_i2c *i2c = nouveau_i2c(event->priv);
+       struct nouveau_i2c_port *port = i2c->find(i2c, index);
+       const struct nouveau_i2c_impl *impl = (void *)nv_object(i2c)->oclass;
+       if (port && port->aux >= 0)
+               impl->aux_mask(i2c, type, 1 << port->aux, 1 << port->aux);
+}
+
+static void
+nouveau_i2c_intr(struct nouveau_subdev *subdev)
+{
+       struct nouveau_i2c_impl *impl = (void *)nv_oclass(subdev);
+       struct nouveau_i2c *i2c = nouveau_i2c(subdev);
+       struct nouveau_i2c_port *port;
+       u32 hi, lo, rq, tx, e;
+
+       if (impl->aux_stat) {
+               impl->aux_stat(i2c, &hi, &lo, &rq, &tx);
+               if (hi || lo || rq || tx) {
+                       list_for_each_entry(port, &i2c->ports, head) {
+                               if (e = 0, port->aux < 0)
+                                       continue;
+
+                               if (hi & (1 << port->aux)) e |= NVKM_I2C_PLUG;
+                               if (lo & (1 << port->aux)) e |= NVKM_I2C_UNPLUG;
+                               if (rq & (1 << port->aux)) e |= NVKM_I2C_IRQ;
+                               if (tx & (1 << port->aux)) e |= NVKM_I2C_DONE;
+
+                               nouveau_event_trigger(i2c->ntfy, e, port->index);
+                       }
+               }
+       }
+}
+
 int
 _nouveau_i2c_fini(struct nouveau_object *object, bool suspend)
 {
+       struct nouveau_i2c_impl *impl = (void *)nv_oclass(object);
        struct nouveau_i2c *i2c = (void *)object;
        struct nouveau_i2c_port *port;
+       u32 mask;
        int ret;
 
        list_for_each_entry(port, &i2c->ports, head) {
@@ -250,6 +386,11 @@ _nouveau_i2c_fini(struct nouveau_object *object, bool suspend)
                        goto fail;
        }
 
+       if ((mask = (1 << impl->aux) - 1), impl->aux_stat) {
+               impl->aux_mask(i2c, NVKM_I2C_ANY, mask, 0);
+               impl->aux_stat(i2c, &mask, &mask, &mask, &mask);
+       }
+
        return nouveau_subdev_fini(&i2c->base, suspend);
 fail:
        list_for_each_entry_continue_reverse(port, &i2c->ports, head) {
@@ -290,6 +431,8 @@ _nouveau_i2c_dtor(struct nouveau_object *object)
        struct nouveau_i2c *i2c = (void *)object;
        struct nouveau_i2c_port *port, *temp;
 
+       nouveau_event_destroy(&i2c->ntfy);
+
        list_for_each_entry_safe(port, temp, &i2c->ports, head) {
                nouveau_object_ref(NULL, (struct nouveau_object **)&port);
        }
@@ -306,14 +449,14 @@ int
 nouveau_i2c_create_(struct nouveau_object *parent,
                    struct nouveau_object *engine,
                    struct nouveau_oclass *oclass,
-                   struct nouveau_oclass *sclass,
                    int length, void **pobject)
 {
+       const struct nouveau_i2c_impl *impl = (void *)oclass;
        struct nouveau_bios *bios = nouveau_bios(parent);
        struct nouveau_i2c *i2c;
        struct nouveau_object *object;
        struct dcb_i2c_entry info;
-       int ret, i, j, index = -1;
+       int ret, i, j, index = -1, pad;
        struct dcb_output outp;
        u8  ver, hdr;
        u32 data;
@@ -324,24 +467,48 @@ nouveau_i2c_create_(struct nouveau_object *parent,
        if (ret)
                return ret;
 
+       nv_subdev(i2c)->intr = nouveau_i2c_intr;
        i2c->find = nouveau_i2c_find;
        i2c->find_type = nouveau_i2c_find_type;
+       i2c->acquire_pad = nouveau_i2c_acquire_pad;
+       i2c->release_pad = nouveau_i2c_release_pad;
+       i2c->acquire = nouveau_i2c_acquire;
+       i2c->release = nouveau_i2c_release;
        i2c->identify = nouveau_i2c_identify;
+       init_waitqueue_head(&i2c->wait);
        INIT_LIST_HEAD(&i2c->ports);
 
        while (!dcb_i2c_parse(bios, ++index, &info)) {
                if (info.type == DCB_I2C_UNUSED)
                        continue;
 
-               oclass = sclass;
+               if (info.share != DCB_I2C_UNUSED) {
+                       if (info.type == DCB_I2C_NVIO_AUX)
+                               pad = info.drive;
+                       else
+                               pad = info.share;
+                       oclass = impl->pad_s;
+               } else {
+                       pad = 0x100 + info.drive;
+                       oclass = impl->pad_x;
+               }
+
+               ret = nouveau_object_ctor(NULL, *pobject, oclass,
+                                         NULL, pad, &parent);
+               if (ret < 0)
+                       continue;
+
+               oclass = impl->sclass;
                do {
                        ret = -EINVAL;
                        if (oclass->handle == info.type) {
-                               ret = nouveau_object_ctor(*pobject, *pobject,
+                               ret = nouveau_object_ctor(parent, *pobject,
                                                          oclass, &info,
                                                          index, &object);
                        }
                } while (ret && (++oclass)->handle);
+
+               nouveau_object_ref(NULL, &parent);
        }
 
        /* in addition to the busses specified in the i2c table, there
@@ -380,5 +547,28 @@ nouveau_i2c_create_(struct nouveau_object *parent,
                }
        }
 
+       ret = nouveau_event_create(4, index, &i2c->ntfy);
+       if (ret)
+               return ret;
+
+       i2c->ntfy->priv = i2c;
+       i2c->ntfy->enable = nouveau_i2c_intr_enable;
+       i2c->ntfy->disable = nouveau_i2c_intr_disable;
+       return 0;
+}
+
+int
+_nouveau_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                 struct nouveau_oclass *oclass, void *data, u32 size,
+                 struct nouveau_object **pobject)
+{
+       struct nouveau_i2c *i2c;
+       int ret;
+
+       ret = nouveau_i2c_create(parent, engine, oclass, &i2c);
+       *pobject = nv_object(i2c);
+       if (ret)
+               return ret;
+
        return 0;
 }
index a6e72d3b06b594e1e4939eebae23353024c4280c..813ffc96e864510b8eb2e2ca0b97e6357b3dd7e7 100644 (file)
@@ -22,7 +22,7 @@
  * Authors: Ben Skeggs
  */
 
-#include "subdev/i2c.h"
+#include "priv.h"
 
 #ifdef CONFIG_NOUVEAU_I2C_INTERNAL
 #define T_TIMEOUT  2200000
@@ -187,8 +187,9 @@ i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
        struct i2c_msg *msg = msgs;
        int ret = 0, mcnt = num;
 
-       if (port->func->acquire)
-               port->func->acquire(port);
+       ret = nouveau_i2c(port)->acquire(port, nsecs_to_jiffies(T_TIMEOUT));
+       if (ret)
+               return ret;
 
        while (!ret && mcnt--) {
                u8 remaining = msg->len;
@@ -210,6 +211,7 @@ i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
        }
 
        i2c_stop(port);
+       nouveau_i2c(port)->release(port);
        return (ret < 0) ? ret : num;
 }
 #else
index 860d5d2365dac4e6c76a7dcf7c6301b416bfba22..b1725bdea967e79cb3315d37b11bdecf098203a6 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <subdev/i2c.h>
 #include <subdev/vga.h>
 
+#include "priv.h"
+
 struct nv04_i2c_priv {
        struct nouveau_i2c base;
 };
@@ -115,29 +116,15 @@ nv04_i2c_sclass[] = {
        {}
 };
 
-static int
-nv04_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-             struct nouveau_oclass *oclass, void *data, u32 size,
-             struct nouveau_object **pobject)
-{
-       struct nv04_i2c_priv *priv;
-       int ret;
-
-       ret = nouveau_i2c_create(parent, engine, oclass, nv04_i2c_sclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       return 0;
-}
-
-struct nouveau_oclass
-nv04_i2c_oclass = {
-       .handle = NV_SUBDEV(I2C, 0x04),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv04_i2c_ctor,
+struct nouveau_oclass *
+nv04_i2c_oclass = &(struct nouveau_i2c_impl) {
+       .base.handle = NV_SUBDEV(I2C, 0x04),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = _nouveau_i2c_ctor,
                .dtor = _nouveau_i2c_dtor,
                .init = _nouveau_i2c_init,
                .fini = _nouveau_i2c_fini,
        },
-};
+       .sclass = nv04_i2c_sclass,
+       .pad_x = &nv04_i2c_pad_oclass,
+}.base;
index 0c2655a03bb43f98fbac43b8d0053259b78c57e5..f16c87ce5ba1fe88194e00461c0b5dd89408a7fd 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <subdev/i2c.h>
 #include <subdev/vga.h>
 
+#include "priv.h"
+
 struct nv4e_i2c_priv {
        struct nouveau_i2c base;
 };
@@ -107,29 +108,15 @@ nv4e_i2c_sclass[] = {
        {}
 };
 
-static int
-nv4e_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-             struct nouveau_oclass *oclass, void *data, u32 size,
-             struct nouveau_object **pobject)
-{
-       struct nv4e_i2c_priv *priv;
-       int ret;
-
-       ret = nouveau_i2c_create(parent, engine, oclass, nv4e_i2c_sclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       return 0;
-}
-
-struct nouveau_oclass
-nv4e_i2c_oclass = {
-       .handle = NV_SUBDEV(I2C, 0x4e),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv4e_i2c_ctor,
+struct nouveau_oclass *
+nv4e_i2c_oclass = &(struct nouveau_i2c_impl) {
+       .base.handle = NV_SUBDEV(I2C, 0x4e),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = _nouveau_i2c_ctor,
                .dtor = _nouveau_i2c_dtor,
                .init = _nouveau_i2c_init,
                .fini = _nouveau_i2c_fini,
        },
-};
+       .sclass = nv4e_i2c_sclass,
+       .pad_x = &nv04_i2c_pad_oclass,
+}.base;
index a8d67a287704df9272af233e5fc755780fca8670..7b8756d4df083b0e3e0e8ede4f3582ffd7537501 100644 (file)
@@ -121,29 +121,15 @@ nv50_i2c_sclass[] = {
        {}
 };
 
-static int
-nv50_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-             struct nouveau_oclass *oclass, void *data, u32 size,
-             struct nouveau_object **pobject)
-{
-       struct nv50_i2c_priv *priv;
-       int ret;
-
-       ret = nouveau_i2c_create(parent, engine, oclass, nv50_i2c_sclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       return 0;
-}
-
-struct nouveau_oclass
-nv50_i2c_oclass = {
-       .handle = NV_SUBDEV(I2C, 0x50),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv50_i2c_ctor,
+struct nouveau_oclass *
+nv50_i2c_oclass = &(struct nouveau_i2c_impl) {
+       .base.handle = NV_SUBDEV(I2C, 0x50),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = _nouveau_i2c_ctor,
                .dtor = _nouveau_i2c_dtor,
                .init = _nouveau_i2c_init,
                .fini = _nouveau_i2c_fini,
        },
-};
+       .sclass = nv50_i2c_sclass,
+       .pad_x = &nv04_i2c_pad_oclass,
+}.base;
index 4e5ba48ebf5a8300dc0b349a7f4cda87a24a4189..5d2a77421c7408f909018a9ef46d0d041208b223 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef __NV50_I2C_H__
 #define __NV50_I2C_H__
 
-#include <subdev/i2c.h>
+#include "priv.h"
 
 struct nv50_i2c_priv {
        struct nouveau_i2c base;
index df6d3e4b68bef1c5975f47f42cd1d6caa35a9b4d..f59c3a25546285dfe9304cbc7a86b5319d56a836 100644 (file)
 
 #include "nv50.h"
 
+void
+nv94_aux_stat(struct nouveau_i2c *i2c, u32 *hi, u32 *lo, u32 *rq, u32 *tx)
+{
+       u32 intr = nv_rd32(i2c, 0x00e06c);
+       u32 stat = nv_rd32(i2c, 0x00e068) & intr, i;
+       for (i = 0, *hi = *lo = *rq = *tx = 0; i < 8; i++) {
+               if ((stat & (1 << (i * 4)))) *hi |= 1 << i;
+               if ((stat & (2 << (i * 4)))) *lo |= 1 << i;
+               if ((stat & (4 << (i * 4)))) *rq |= 1 << i;
+               if ((stat & (8 << (i * 4)))) *tx |= 1 << i;
+       }
+       nv_wr32(i2c, 0x00e06c, intr);
+}
+
+void
+nv94_aux_mask(struct nouveau_i2c *i2c, u32 type, u32 mask, u32 data)
+{
+       u32 temp = nv_rd32(i2c, 0x00e068), i;
+       for (i = 0; i < 8; i++) {
+               if (mask & (1 << i)) {
+                       if (!(data & (1 << i))) {
+                               temp &= ~(type << (i * 4));
+                               continue;
+                       }
+                       temp |= type << (i * 4);
+               }
+       }
+       nv_wr32(i2c, 0x00e068, temp);
+}
+
 #define AUX_DBG(fmt, args...) nv_debug(aux, "AUXCH(%d): " fmt, ch, ##args)
 #define AUX_ERR(fmt, args...) nv_error(aux, "AUXCH(%d): " fmt, ch, ##args)
 
@@ -69,7 +99,8 @@ auxch_init(struct nouveau_i2c *aux, int ch)
 }
 
 int
-nv94_aux(struct nouveau_i2c_port *base, u8 type, u32 addr, u8 *data, u8 size)
+nv94_aux(struct nouveau_i2c_port *base, bool retry,
+        u8 type, u32 addr, u8 *data, u8 size)
 {
        struct nouveau_i2c *aux = nouveau_i2c(base);
        struct nv50_i2c_port *port = (void *)base;
@@ -105,9 +136,8 @@ nv94_aux(struct nouveau_i2c_port *base, u8 type, u32 addr, u8 *data, u8 size)
        ctrl |= size - 1;
        nv_wr32(aux, 0x00e4e0 + (ch * 0x50), addr);
 
-       /* retry transaction a number of times on failure... */
-       ret = -EREMOTEIO;
-       for (retries = 0; retries < 32; retries++) {
+       /* (maybe) retry transaction a number of times on failure... */
+       for (retries = 0; !ret && retries < 32; retries++) {
                /* reset, and delay a while if this is a retry */
                nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x80000000 | ctrl);
                nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00000000 | ctrl);
@@ -123,16 +153,21 @@ nv94_aux(struct nouveau_i2c_port *base, u8 type, u32 addr, u8 *data, u8 size)
                        udelay(1);
                        if (!timeout--) {
                                AUX_ERR("tx req timeout 0x%08x\n", ctrl);
+                               ret = -EIO;
                                goto out;
                        }
                } while (ctrl & 0x00010000);
+               ret = 1;
 
                /* read status, and check if transaction completed ok */
                stat = nv_mask(aux, 0x00e4e8 + (ch * 0x50), 0, 0);
-               if (!(stat & 0x000f0f00)) {
-                       ret = 0;
-                       break;
-               }
+               if ((stat & 0x000f0000) == 0x00080000 ||
+                   (stat & 0x000f0000) == 0x00020000)
+                       ret = retry ? 0 : 1;
+               if ((stat & 0x00000100))
+                       ret = -ETIMEDOUT;
+               if ((stat & 0x00000e00))
+                       ret = -EIO;
 
                AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat);
        }
@@ -147,29 +182,11 @@ nv94_aux(struct nouveau_i2c_port *base, u8 type, u32 addr, u8 *data, u8 size)
 
 out:
        auxch_fini(aux, ch);
-       return ret;
-}
-
-void
-nv94_i2c_acquire(struct nouveau_i2c_port *base)
-{
-       struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine;
-       struct nv50_i2c_port *port = (void *)base;
-       if (port->ctrl) {
-               nv_mask(priv, port->ctrl + 0x0c, 0x00000001, 0x00000000);
-               nv_mask(priv, port->ctrl + 0x00, 0x0000f003, port->data);
-       }
-}
-
-void
-nv94_i2c_release(struct nouveau_i2c_port *base)
-{
+       return ret < 0 ? ret : (stat & 0x000f0000) >> 16;
 }
 
 static const struct nouveau_i2c_func
 nv94_i2c_func = {
-       .acquire   = nv94_i2c_acquire,
-       .release   = nv94_i2c_release,
        .drive_scl = nv50_i2c_drive_scl,
        .drive_sda = nv50_i2c_drive_sda,
        .sense_scl = nv50_i2c_sense_scl,
@@ -206,8 +223,6 @@ nv94_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
 
 static const struct nouveau_i2c_func
 nv94_aux_func = {
-       .acquire   = nv94_i2c_acquire,
-       .release   = nv94_i2c_release,
        .aux       = nv94_aux,
 };
 
@@ -227,6 +242,7 @@ nv94_aux_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
+       port->base.aux = info->drive;
        port->addr = info->drive;
        if (info->share != DCB_I2C_UNUSED) {
                port->ctrl = 0x00e500 + (info->drive * 0x50);
@@ -257,29 +273,19 @@ nv94_i2c_sclass[] = {
        {}
 };
 
-static int
-nv94_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-             struct nouveau_oclass *oclass, void *data, u32 size,
-             struct nouveau_object **pobject)
-{
-       struct nv50_i2c_priv *priv;
-       int ret;
-
-       ret = nouveau_i2c_create(parent, engine, oclass, nv94_i2c_sclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       return 0;
-}
-
-struct nouveau_oclass
-nv94_i2c_oclass = {
-       .handle = NV_SUBDEV(I2C, 0x94),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nv94_i2c_ctor,
+struct nouveau_oclass *
+nv94_i2c_oclass = &(struct nouveau_i2c_impl) {
+       .base.handle = NV_SUBDEV(I2C, 0x94),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = _nouveau_i2c_ctor,
                .dtor = _nouveau_i2c_dtor,
                .init = _nouveau_i2c_init,
                .fini = _nouveau_i2c_fini,
        },
-};
+       .sclass = nv94_i2c_sclass,
+       .pad_x = &nv04_i2c_pad_oclass,
+       .pad_s = &nv94_i2c_pad_oclass,
+       .aux = 4,
+       .aux_stat = nv94_aux_stat,
+       .aux_mask = nv94_aux_mask,
+}.base;
index 29967d30f97cbcbb7a1c3e889dd7310a98897e02..364ddb1c5f034d527d0f4e8566018ca242e14c10 100644 (file)
@@ -42,8 +42,6 @@ nvd0_i2c_sense_sda(struct nouveau_i2c_port *base)
 
 static const struct nouveau_i2c_func
 nvd0_i2c_func = {
-       .acquire   = nv94_i2c_acquire,
-       .release   = nv94_i2c_release,
        .drive_scl = nv50_i2c_drive_scl,
        .drive_sda = nv50_i2c_drive_sda,
        .sense_scl = nvd0_i2c_sense_scl,
@@ -75,7 +73,7 @@ nvd0_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        return 0;
 }
 
-static struct nouveau_oclass
+struct nouveau_oclass
 nvd0_i2c_sclass[] = {
        { .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT),
          .ofuncs = &(struct nouveau_ofuncs) {
@@ -96,29 +94,19 @@ nvd0_i2c_sclass[] = {
        {}
 };
 
-static int
-nvd0_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
-             struct nouveau_oclass *oclass, void *data, u32 size,
-             struct nouveau_object **pobject)
-{
-       struct nv50_i2c_priv *priv;
-       int ret;
-
-       ret = nouveau_i2c_create(parent, engine, oclass, nvd0_i2c_sclass, &priv);
-       *pobject = nv_object(priv);
-       if (ret)
-               return ret;
-
-       return 0;
-}
-
-struct nouveau_oclass
-nvd0_i2c_oclass = {
-       .handle = NV_SUBDEV(I2C, 0xd0),
-       .ofuncs = &(struct nouveau_ofuncs) {
-               .ctor = nvd0_i2c_ctor,
+struct nouveau_oclass *
+nvd0_i2c_oclass = &(struct nouveau_i2c_impl) {
+       .base.handle = NV_SUBDEV(I2C, 0xd0),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = _nouveau_i2c_ctor,
                .dtor = _nouveau_i2c_dtor,
                .init = _nouveau_i2c_init,
                .fini = _nouveau_i2c_fini,
        },
-};
+       .sclass = nvd0_i2c_sclass,
+       .pad_x = &nv04_i2c_pad_oclass,
+       .pad_s = &nv94_i2c_pad_oclass,
+       .aux = 4,
+       .aux_stat = nv94_aux_stat,
+       .aux_mask = nv94_aux_mask,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nve0.c
new file mode 100644 (file)
index 0000000..cae77e1
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nv50.h"
+
+static void
+nve0_aux_stat(struct nouveau_i2c *i2c, u32 *hi, u32 *lo, u32 *rq, u32 *tx)
+{
+       u32 intr = nv_rd32(i2c, 0x00dc60);
+       u32 stat = nv_rd32(i2c, 0x00dc68) & intr, i;
+       for (i = 0, *hi = *lo = *rq = *tx = 0; i < 8; i++) {
+               if ((stat & (1 << (i * 4)))) *hi |= 1 << i;
+               if ((stat & (2 << (i * 4)))) *lo |= 1 << i;
+               if ((stat & (4 << (i * 4)))) *rq |= 1 << i;
+               if ((stat & (8 << (i * 4)))) *tx |= 1 << i;
+       }
+       nv_wr32(i2c, 0x00dc60, intr);
+}
+
+static void
+nve0_aux_mask(struct nouveau_i2c *i2c, u32 type, u32 mask, u32 data)
+{
+       u32 temp = nv_rd32(i2c, 0x00dc68), i;
+       for (i = 0; i < 8; i++) {
+               if (mask & (1 << i)) {
+                       if (!(data & (1 << i))) {
+                               temp &= ~(type << (i * 4));
+                               continue;
+                       }
+                       temp |= type << (i * 4);
+               }
+       }
+       nv_wr32(i2c, 0x00dc68, temp);
+}
+
+struct nouveau_oclass *
+nve0_i2c_oclass = &(struct nouveau_i2c_impl) {
+       .base.handle = NV_SUBDEV(I2C, 0xe0),
+       .base.ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = _nouveau_i2c_ctor,
+               .dtor = _nouveau_i2c_dtor,
+               .init = _nouveau_i2c_init,
+               .fini = _nouveau_i2c_fini,
+       },
+       .sclass = nvd0_i2c_sclass,
+       .pad_x = &nv04_i2c_pad_oclass,
+       .pad_s = &nv94_i2c_pad_oclass,
+       .aux = 4,
+       .aux_stat = nve0_aux_stat,
+       .aux_mask = nve0_aux_mask,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.c
new file mode 100644 (file)
index 0000000..e9e4124
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2014 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 "pad.h"
+
+int
+_nvkm_i2c_pad_fini(struct nouveau_object *object, bool suspend)
+{
+       struct nvkm_i2c_pad *pad = (void *)object;
+       DBG("-> NULL\n");
+       pad->port = NULL;
+       return nouveau_object_fini(&pad->base, suspend);
+}
+
+int
+_nvkm_i2c_pad_init(struct nouveau_object *object)
+{
+       struct nvkm_i2c_pad *pad = (void *)object;
+       DBG("-> PORT:%02x\n", pad->next->index);
+       pad->port = pad->next;
+       return nouveau_object_init(&pad->base);
+}
+
+int
+nvkm_i2c_pad_create_(struct nouveau_object *parent,
+                    struct nouveau_object *engine,
+                    struct nouveau_oclass *oclass, int index,
+                    int size, void **pobject)
+{
+       struct nouveau_i2c *i2c = (void *)engine;
+       struct nouveau_i2c_port *port;
+       struct nvkm_i2c_pad *pad;
+       int ret;
+
+       list_for_each_entry(port, &i2c->ports, head) {
+               pad = nvkm_i2c_pad(port);
+               if (pad->index == index) {
+                       atomic_inc(&nv_object(pad)->refcount);
+                       *pobject = pad;
+                       return 1;
+               }
+       }
+
+       ret = nouveau_object_create_(parent, engine, oclass, 0, size, pobject);
+       pad = *pobject;
+       if (ret)
+               return ret;
+
+       pad->index = index;
+       return 0;
+}
+
+int
+_nvkm_i2c_pad_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                  struct nouveau_oclass *oclass, void *data, u32 index,
+                  struct nouveau_object **pobject)
+{
+       struct nvkm_i2c_pad *pad;
+       int ret;
+       ret = nvkm_i2c_pad_create(parent, engine, oclass, index, &pad);
+       *pobject = nv_object(pad);
+       return ret;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.h b/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.h
new file mode 100644 (file)
index 0000000..452ac10
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef __NVKM_I2C_PAD_H__
+#define __NVKM_I2C_PAD_H__
+
+#include "priv.h"
+
+struct nvkm_i2c_pad {
+       struct nouveau_object base;
+       int index;
+       struct nouveau_i2c_port *port;
+       struct nouveau_i2c_port *next;
+};
+
+static inline struct nvkm_i2c_pad *
+nvkm_i2c_pad(struct nouveau_i2c_port *port)
+{
+       struct nouveau_object *pad = nv_object(port);
+       while (pad->parent)
+               pad = pad->parent;
+       return (void *)pad;
+}
+
+#define nvkm_i2c_pad_create(p,e,o,i,d)                                         \
+       nvkm_i2c_pad_create_((p), (e), (o), (i), sizeof(**d), (void **)d)
+#define nvkm_i2c_pad_destroy(p) ({                                             \
+       struct nvkm_i2c_pad *_p = (p);                                         \
+       _nvkm_i2c_pad_dtor(nv_object(_p));                                     \
+})
+#define nvkm_i2c_pad_init(p) ({                                                \
+       struct nvkm_i2c_pad *_p = (p);                                         \
+       _nvkm_i2c_pad_init(nv_object(_p));                                     \
+})
+#define nvkm_i2c_pad_fini(p,s) ({                                              \
+       struct nvkm_i2c_pad *_p = (p);                                         \
+       _nvkm_i2c_pad_fini(nv_object(_p), (s));                                \
+})
+
+int nvkm_i2c_pad_create_(struct nouveau_object *, struct nouveau_object *,
+                        struct nouveau_oclass *, int index, int, void **);
+
+int _nvkm_i2c_pad_ctor(struct nouveau_object *, struct nouveau_object *,
+                      struct nouveau_oclass *, void *, u32,
+                      struct nouveau_object **);
+#define _nvkm_i2c_pad_dtor nouveau_object_destroy
+int _nvkm_i2c_pad_init(struct nouveau_object *);
+int _nvkm_i2c_pad_fini(struct nouveau_object *, bool);
+
+#ifndef MSG
+#define MSG(l,f,a...) do {                                                     \
+       struct nvkm_i2c_pad *_pad = (void *)pad;                               \
+       nv_##l(nv_object(_pad)->engine, "PAD:%c:%02x: "f,                      \
+              _pad->index >= 0x100 ? 'X' : 'S',                               \
+              _pad->index >= 0x100 ? _pad->index - 0x100 : _pad->index, ##a); \
+} while(0)
+#define DBG(f,a...) MSG(debug, f, ##a)
+#define ERR(f,a...) MSG(error, f, ##a)
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv04.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv04.c
new file mode 100644 (file)
index 0000000..2c4b612
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 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 "pad.h"
+
+struct nouveau_oclass
+nv04_i2c_pad_oclass = {
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = _nvkm_i2c_pad_ctor,
+               .dtor = _nvkm_i2c_pad_dtor,
+               .init = _nvkm_i2c_pad_init,
+               .fini = _nvkm_i2c_pad_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv94.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv94.c
new file mode 100644 (file)
index 0000000..0dc6753
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014 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 "pad.h"
+
+struct nv94_i2c_pad {
+       struct nvkm_i2c_pad base;
+       int addr;
+};
+
+static int
+nv94_i2c_pad_fini(struct nouveau_object *object, bool suspend)
+{
+       struct nouveau_i2c *i2c = (void *)object->engine;
+       struct nv94_i2c_pad *pad = (void *)object;
+       nv_mask(i2c, 0x00e50c + pad->addr, 0x00000001, 0x00000001);
+       return nvkm_i2c_pad_fini(&pad->base, suspend);
+}
+
+static int
+nv94_i2c_pad_init(struct nouveau_object *object)
+{
+       struct nouveau_i2c *i2c = (void *)object->engine;
+       struct nv94_i2c_pad *pad = (void *)object;
+
+       switch (nv_oclass(pad->base.next)->handle) {
+       case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX):
+               nv_mask(i2c, 0x00e500 + pad->addr, 0x0000c003, 0x00000002);
+               break;
+       case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT):
+       default:
+               nv_mask(i2c, 0x00e500 + pad->addr, 0x0000c003, 0x0000c001);
+               break;
+       }
+
+       nv_mask(i2c, 0x00e50c + pad->addr, 0x00000001, 0x00000000);
+       return nvkm_i2c_pad_init(&pad->base);
+}
+
+static int
+nv94_i2c_pad_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                 struct nouveau_oclass *oclass, void *data, u32 index,
+                 struct nouveau_object **pobject)
+{
+       struct nv94_i2c_pad *pad;
+       int ret;
+
+       ret = nvkm_i2c_pad_create(parent, engine, oclass, index, &pad);
+       *pobject = nv_object(pad);
+       if (ret)
+               return ret;
+
+       pad->addr = index * 0x50;;
+       return 0;
+}
+
+struct nouveau_oclass
+nv94_i2c_pad_oclass = {
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv94_i2c_pad_ctor,
+               .dtor = _nvkm_i2c_pad_dtor,
+               .init = nv94_i2c_pad_init,
+               .fini = nv94_i2c_pad_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/port.h b/drivers/gpu/drm/nouveau/core/subdev/i2c/port.h
new file mode 100644 (file)
index 0000000..a8ff6e0
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __NVKM_I2C_PORT_H__
+#define __NVKM_I2C_PORT_H__
+
+#include "priv.h"
+
+#ifndef MSG
+#define MSG(l,f,a...) do {                                                     \
+       struct nouveau_i2c_port *_port = (void *)port;                         \
+       nv_##l(nv_object(_port)->engine, "PORT:%02x: "f, _port->index, ##a);   \
+} while(0)
+#define DBG(f,a...) MSG(debug, f, ##a)
+#define ERR(f,a...) MSG(error, f, ##a)
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h b/drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h
new file mode 100644 (file)
index 0000000..780090b
--- /dev/null
@@ -0,0 +1,85 @@
+#ifndef __NVKM_I2C_H__
+#define __NVKM_I2C_H__
+
+#include <subdev/i2c.h>
+
+extern struct nouveau_oclass nv04_i2c_pad_oclass;
+extern struct nouveau_oclass nv94_i2c_pad_oclass;
+
+#define nouveau_i2c_port_create(p,e,o,i,a,f,d)                                 \
+       nouveau_i2c_port_create_((p), (e), (o), (i), (a), (f),                 \
+                                sizeof(**d), (void **)d)
+#define nouveau_i2c_port_destroy(p) ({                                         \
+       struct nouveau_i2c_port *port = (p);                                   \
+       _nouveau_i2c_port_dtor(nv_object(i2c));                                \
+})
+#define nouveau_i2c_port_init(p)                                               \
+       nouveau_object_init(&(p)->base)
+#define nouveau_i2c_port_fini(p,s)                                             \
+       nouveau_object_fini(&(p)->base, (s))
+
+int nouveau_i2c_port_create_(struct nouveau_object *, struct nouveau_object *,
+                            struct nouveau_oclass *, u8,
+                            const struct i2c_algorithm *,
+                            const struct nouveau_i2c_func *,
+                            int, void **);
+void _nouveau_i2c_port_dtor(struct nouveau_object *);
+#define _nouveau_i2c_port_init nouveau_object_init
+int  _nouveau_i2c_port_fini(struct nouveau_object *, bool);
+
+#define nouveau_i2c_create(p,e,o,d)                                            \
+       nouveau_i2c_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nouveau_i2c_destroy(p) ({                                              \
+       struct nouveau_i2c *i2c = (p);                                         \
+       _nouveau_i2c_dtor(nv_object(i2c));                                     \
+})
+#define nouveau_i2c_init(p) ({                                                 \
+       struct nouveau_i2c *i2c = (p);                                         \
+       _nouveau_i2c_init(nv_object(i2c));                                     \
+})
+#define nouveau_i2c_fini(p,s) ({                                               \
+       struct nouveau_i2c *i2c = (p);                                         \
+       _nouveau_i2c_fini(nv_object(i2c), (s));                                \
+})
+
+int nouveau_i2c_create_(struct nouveau_object *, struct nouveau_object *,
+                       struct nouveau_oclass *, int, void **);
+int  _nouveau_i2c_ctor(struct nouveau_object *, struct nouveau_object *,
+                      struct nouveau_oclass *, void *, u32,
+                      struct nouveau_object **);
+void _nouveau_i2c_dtor(struct nouveau_object *);
+int  _nouveau_i2c_init(struct nouveau_object *);
+int  _nouveau_i2c_fini(struct nouveau_object *, bool);
+
+extern struct nouveau_oclass nouveau_anx9805_sclass[];
+extern struct nouveau_oclass nvd0_i2c_sclass[];
+
+extern const struct i2c_algorithm nouveau_i2c_bit_algo;
+extern const struct i2c_algorithm nouveau_i2c_aux_algo;
+
+struct nouveau_i2c_impl {
+       struct nouveau_oclass base;
+
+       /* supported i2c port classes */
+       struct nouveau_oclass *sclass;
+       struct nouveau_oclass *pad_x;
+       struct nouveau_oclass *pad_s;
+
+       /* number of native dp aux channels present */
+       int aux;
+
+       /* read and ack pending interrupts, returning only data
+        * for ports that have not been masked off, while still
+        * performing the ack for anything that was pending.
+        */
+       void (*aux_stat)(struct nouveau_i2c *, u32 *, u32 *, u32 *, u32 *);
+
+       /* mask on/off interrupt types for a given set of auxch
+        */
+       void (*aux_mask)(struct nouveau_i2c *, u32, u32, u32);
+};
+
+void nv94_aux_stat(struct nouveau_i2c *, u32 *, u32 *, u32 *, u32 *);
+void nv94_aux_mask(struct nouveau_i2c *, u32, u32, u32);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/ibus/gk20a.c b/drivers/gpu/drm/nouveau/core/subdev/ibus/gk20a.c
new file mode 100644 (file)
index 0000000..245f0eb
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. 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, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS 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 <subdev/ibus.h>
+#include <subdev/timer.h>
+
+struct gk20a_ibus_priv {
+       struct nouveau_ibus base;
+};
+
+static void
+gk20a_ibus_init_priv_ring(struct gk20a_ibus_priv *priv)
+{
+       nv_mask(priv, 0x137250, 0x3f, 0);
+
+       nv_mask(priv, 0x000200, 0x20, 0);
+       usleep_range(20, 30);
+       nv_mask(priv, 0x000200, 0x20, 0x20);
+
+       nv_wr32(priv, 0x12004c, 0x4);
+       nv_wr32(priv, 0x122204, 0x2);
+       nv_rd32(priv, 0x122204);
+}
+
+static void
+gk20a_ibus_intr(struct nouveau_subdev *subdev)
+{
+       struct gk20a_ibus_priv *priv = (void *)subdev;
+       u32 status0 = nv_rd32(priv, 0x120058);
+
+       if (status0 & 0x7) {
+               nv_debug(priv, "resetting priv ring\n");
+               gk20a_ibus_init_priv_ring(priv);
+       }
+
+       /* Acknowledge interrupt */
+       nv_mask(priv, 0x12004c, 0x2, 0x2);
+
+       if (!nv_wait(subdev, 0x12004c, 0x3f, 0x00))
+               nv_warn(priv, "timeout waiting for ringmaster ack\n");
+}
+
+static int
+gk20a_ibus_init(struct nouveau_object *object)
+{
+       struct gk20a_ibus_priv *priv = (void *)object;
+       int ret;
+
+       ret = _nouveau_ibus_init(object);
+       if (ret)
+               return ret;
+
+       gk20a_ibus_init_priv_ring(priv);
+
+       return 0;
+}
+
+static int
+gk20a_ibus_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+              struct nouveau_oclass *oclass, void *data, u32 size,
+              struct nouveau_object **pobject)
+{
+       struct gk20a_ibus_priv *priv;
+       int ret;
+
+       ret = nouveau_ibus_create(parent, engine, oclass, &priv);
+       *pobject = nv_object(priv);
+       if (ret)
+               return ret;
+
+       nv_subdev(priv)->intr = gk20a_ibus_intr;
+       return 0;
+}
+
+struct nouveau_oclass
+gk20a_ibus_oclass = {
+       .handle = NV_SUBDEV(IBUS, 0xea),
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = gk20a_ibus_ctor,
+               .dtor = _nouveau_ibus_dtor,
+               .init = gk20a_ibus_init,
+               .fini = _nouveau_ibus_fini,
+       },
+};
index e8822a934c485742f9b2e8452225ec93ddec5f07..9ca93e2718f79d50fca7436bacf639ac88588733 100644 (file)
@@ -26,6 +26,7 @@
 
 const struct nouveau_mc_intr
 nv50_mc_intr[] = {
+       { 0x04000000, NVDEV_ENGINE_DISP },  /* DISP before FIFO, so pageflip-timestamping works! */
        { 0x00000001, NVDEV_ENGINE_MPEG },
        { 0x00000100, NVDEV_ENGINE_FIFO },
        { 0x00001000, NVDEV_ENGINE_GR },
@@ -33,8 +34,8 @@ nv50_mc_intr[] = {
        { 0x00008000, NVDEV_ENGINE_BSP },       /* NV84- */
        { 0x00020000, NVDEV_ENGINE_VP },        /* NV84- */
        { 0x00100000, NVDEV_SUBDEV_TIMER },
-       { 0x00200000, NVDEV_SUBDEV_GPIO },
-       { 0x04000000, NVDEV_ENGINE_DISP },
+       { 0x00200000, NVDEV_SUBDEV_GPIO },      /* PMGR->GPIO */
+       { 0x00200000, NVDEV_SUBDEV_I2C },       /* PMGR->I2C/AUX */
        { 0x10000000, NVDEV_SUBDEV_BUS },
        { 0x80000000, NVDEV_ENGINE_SW },
        { 0x0002d101, NVDEV_SUBDEV_FB },
index f8a6f18e2d3408f55b1447e55144e24ded4036f9..3c76d9038f3828d5e6a5c991244cd065dc4430a5 100644 (file)
@@ -26,6 +26,7 @@
 
 static const struct nouveau_mc_intr
 nv98_mc_intr[] = {
+       { 0x04000000, NVDEV_ENGINE_DISP },  /* DISP first, so pageflip timestamps work */
        { 0x00000001, NVDEV_ENGINE_PPP },
        { 0x00000100, NVDEV_ENGINE_FIFO },
        { 0x00001000, NVDEV_ENGINE_GR },
@@ -35,9 +36,9 @@ nv98_mc_intr[] = {
        { 0x00040000, NVDEV_SUBDEV_PWR },       /* NVA3:NVC0 */
        { 0x00080000, NVDEV_SUBDEV_THERM },     /* NVA3:NVC0 */
        { 0x00100000, NVDEV_SUBDEV_TIMER },
-       { 0x00200000, NVDEV_SUBDEV_GPIO },
+       { 0x00200000, NVDEV_SUBDEV_GPIO },      /* PMGR->GPIO */
+       { 0x00200000, NVDEV_SUBDEV_I2C },       /* PMGR->I2C/AUX */
        { 0x00400000, NVDEV_ENGINE_COPY0 },     /* NVA3-     */
-       { 0x04000000, NVDEV_ENGINE_DISP },
        { 0x10000000, NVDEV_SUBDEV_BUS },
        { 0x80000000, NVDEV_ENGINE_SW },
        { 0x0042d101, NVDEV_SUBDEV_FB },
index 34472d3170974ca8e6207afe0a347f332130d6bd..f9c6a678b47d81b1509215fe14d7f34c7398ca20 100644 (file)
@@ -26,6 +26,7 @@
 
 const struct nouveau_mc_intr
 nvc0_mc_intr[] = {
+       { 0x04000000, NVDEV_ENGINE_DISP },  /* DISP first, so pageflip timestamps work. */
        { 0x00000001, NVDEV_ENGINE_PPP },
        { 0x00000020, NVDEV_ENGINE_COPY0 },
        { 0x00000040, NVDEV_ENGINE_COPY1 },
@@ -37,10 +38,10 @@ nvc0_mc_intr[] = {
        { 0x00040000, NVDEV_SUBDEV_THERM },
        { 0x00020000, NVDEV_ENGINE_VP },
        { 0x00100000, NVDEV_SUBDEV_TIMER },
-       { 0x00200000, NVDEV_SUBDEV_GPIO },
+       { 0x00200000, NVDEV_SUBDEV_GPIO },      /* PMGR->GPIO */
+       { 0x00200000, NVDEV_SUBDEV_I2C },       /* PMGR->I2C/AUX */
        { 0x01000000, NVDEV_SUBDEV_PWR },
        { 0x02000000, NVDEV_SUBDEV_LTCG },
-       { 0x04000000, NVDEV_ENGINE_DISP },
        { 0x08000000, NVDEV_SUBDEV_FB },
        { 0x10000000, NVDEV_SUBDEV_BUS },
        { 0x40000000, NVDEV_SUBDEV_IBUS },
index 64f8b4702bf7272c3edf7950e16bf46b07d5457a..fcaabe8456e36bac0cf2664fc5305bcb5644c45b 100644 (file)
@@ -150,7 +150,7 @@ mxm_dcb_sanitise_entry(struct nouveau_bios *bios, void *data, int idx, u16 pdcb)
         * common example is DP->eDP.
         */
        conn  = bios->data;
-       conn += dcb_conn(bios, (ctx.outp[0] & 0x0000f000) >> 12, &ver, &len);
+       conn += nvbios_connEe(bios, (ctx.outp[0] & 0x0000f000) >> 12, &ver, &len);
        type  = conn[0];
        switch (ctx.desc.conn_type) {
        case 0x01: /* LVDS */
index 7610fc5f8fa256de8038e3770ca227a3d0150c64..ca9ad9fd47be35b0fa491e301c95f0b1fda053ff 100644 (file)
@@ -60,9 +60,9 @@ static struct nouveau_i2c_board_info
 nv_board_infos[] = {
        { { I2C_BOARD_INFO("w83l785ts", 0x2d) }, 0 },
        { { I2C_BOARD_INFO("w83781d", 0x2d) }, 0  },
-       { { I2C_BOARD_INFO("adt7473", 0x2e) }, 20  },
-       { { I2C_BOARD_INFO("adt7473", 0x2d) }, 20  },
-       { { I2C_BOARD_INFO("adt7473", 0x2c) }, 20  },
+       { { I2C_BOARD_INFO("adt7473", 0x2e) }, 40  },
+       { { I2C_BOARD_INFO("adt7473", 0x2d) }, 40  },
+       { { I2C_BOARD_INFO("adt7473", 0x2c) }, 40  },
        { { I2C_BOARD_INFO("f75375", 0x2e) }, 0  },
        { { I2C_BOARD_INFO("lm99", 0x4c) }, 0  },
        { { I2C_BOARD_INFO("lm90", 0x4c) }, 0  },
index 3b2c4580098bafea567ebadd0cfdb972adeadf93..0478b2e3fb1de76ee34e40a56e0d6209ffff1d60 100644 (file)
@@ -36,7 +36,7 @@ nva3_therm_fan_sense(struct nouveau_therm *therm)
        u32 tach = nv_rd32(therm, 0x00e728) & 0x0000ffff;
        u32 ctrl = nv_rd32(therm, 0x00e720);
        if (ctrl & 0x00000001)
-               return tach * 60;
+               return tach * 60 / 2;
        return -ENODEV;
 }
 
index 434b920f6bd4d052dc1a50fc4be9d34f58d955c9..a96dda48718e6bb42f19fa639d8d16a8679ceb9b 100644 (file)
@@ -414,7 +414,7 @@ static void nv04_dac_commit(struct drm_encoder *encoder)
        helper->dpms(encoder, DRM_MODE_DPMS_ON);
 
        NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n",
-                drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
+                nouveau_encoder_connector_get(nv_encoder)->base.name,
                 nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
 }
 
index a2d669b4acf2452d86b32403936fb42bfaa7b11f..e57babb206d3c6202d2f9f6d2537fed979a13ed0 100644 (file)
@@ -477,7 +477,7 @@ static void nv04_dfp_commit(struct drm_encoder *encoder)
        helper->dpms(encoder, DRM_MODE_DPMS_ON);
 
        NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n",
-                drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
+                nouveau_encoder_connector_get(nv_encoder)->base.name,
                 nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
 }
 
index 2f1ed61f7c8c9e39d44c1528410eb4aaeb83fe17..4342fdaee707f252f12908b644bde7afab656fc8 100644 (file)
@@ -115,7 +115,7 @@ nv04_display_create(struct drm_device *dev)
                                 &dev->mode_config.connector_list, head) {
                if (!connector->encoder_ids[0]) {
                        NV_WARN(drm, "%s has no encoders, removing\n",
-                               drm_get_connector_name(connector));
+                               connector->name);
                        connector->funcs->destroy(connector);
                }
        }
index 244822df8ffc73d1e9f15075c7ee6faa54368a84..8667620b703a3262baa4168868e42bf3680fe809 100644 (file)
@@ -171,7 +171,8 @@ static void nv04_tv_commit(struct drm_encoder *encoder)
        helper->dpms(encoder, DRM_MODE_DPMS_ON);
 
        NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n",
-                drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
+                nouveau_encoder_connector_get(nv_encoder)->base.name,
+                nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
 }
 
 static void nv04_tv_destroy(struct drm_encoder *encoder)
index acef48f4a4ea2c62c75172ae38fb7f85646d39fa..195bd8e86c6a523615679273bf71bc4fbc5aa6cc 100644 (file)
@@ -612,8 +612,7 @@ static void nv17_tv_commit(struct drm_encoder *encoder)
        helper->dpms(encoder, DRM_MODE_DPMS_ON);
 
        NV_INFO(drm, "Output %s is running on CRTC %d using output %c\n",
-               drm_get_connector_name(
-                       &nouveau_encoder_connector_get(nv_encoder)->base),
+               nouveau_encoder_connector_get(nv_encoder)->base.name,
                nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
 }
 
index d07ce028af516ace0b20db1418a7f9ed14b78c31..1fa222e8f007ba5e0b8f94a8bfcf911bee34a49d 100644 (file)
@@ -44,6 +44,7 @@
 
 #include <subdev/i2c.h>
 #include <subdev/gpio.h>
+#include <engine/disp.h>
 
 MODULE_PARM_DESC(tv_disable, "Disable TV-out detection");
 static int nouveau_tv_disable = 0;
@@ -75,7 +76,8 @@ find_encoder(struct drm_connector *connector, int type)
                        continue;
                nv_encoder = nouveau_encoder(obj_to_encoder(obj));
 
-               if (type == DCB_OUTPUT_ANY || nv_encoder->dcb->type == type)
+               if (type == DCB_OUTPUT_ANY ||
+                   (nv_encoder->dcb && nv_encoder->dcb->type == type))
                        return nv_encoder;
        }
 
@@ -100,22 +102,24 @@ static void
 nouveau_connector_destroy(struct drm_connector *connector)
 {
        struct nouveau_connector *nv_connector = nouveau_connector(connector);
-       nouveau_event_ref(NULL, &nv_connector->hpd_func);
+       nouveau_event_ref(NULL, &nv_connector->hpd);
        kfree(nv_connector->edid);
        drm_sysfs_connector_remove(connector);
        drm_connector_cleanup(connector);
+       if (nv_connector->aux.transfer)
+               drm_dp_aux_unregister(&nv_connector->aux);
        kfree(connector);
 }
 
-static struct nouveau_i2c_port *
-nouveau_connector_ddc_detect(struct drm_connector *connector,
-                            struct nouveau_encoder **pnv_encoder)
+static struct nouveau_encoder *
+nouveau_connector_ddc_detect(struct drm_connector *connector)
 {
        struct drm_device *dev = connector->dev;
        struct nouveau_connector *nv_connector = nouveau_connector(connector);
        struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
-       struct nouveau_i2c_port *port = NULL;
+       struct nouveau_encoder *nv_encoder;
+       struct drm_mode_object *obj;
        int i, panel = -ENODEV;
 
        /* eDP panels need powering on by us (if the VBIOS doesn't default it
@@ -130,13 +134,9 @@ nouveau_connector_ddc_detect(struct drm_connector *connector,
                }
        }
 
-       for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
-               struct nouveau_encoder *nv_encoder;
-               struct drm_mode_object *obj;
-               int id;
-
-               id = connector->encoder_ids[i];
-               if (!id)
+       for (i = 0; nv_encoder = NULL, i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+               int id = connector->encoder_ids[i];
+               if (id == 0)
                        break;
 
                obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
@@ -144,22 +144,24 @@ nouveau_connector_ddc_detect(struct drm_connector *connector,
                        continue;
                nv_encoder = nouveau_encoder(obj_to_encoder(obj));
 
-               port = nv_encoder->i2c;
-               if (port && nv_probe_i2c(port, 0x50)) {
-                       *pnv_encoder = nv_encoder;
-                       break;
+               if (nv_encoder->dcb->type == DCB_OUTPUT_DP) {
+                       int ret = nouveau_dp_detect(nv_encoder);
+                       if (ret == 0)
+                               break;
+               } else
+               if (nv_encoder->i2c) {
+                       if (nv_probe_i2c(nv_encoder->i2c, 0x50))
+                               break;
                }
-
-               port = NULL;
        }
 
        /* eDP panel not detected, restore panel power GPIO to previous
         * state to avoid confusing the SOR for other output types.
         */
-       if (!port && panel == 0)
+       if (!nv_encoder && panel == 0)
                gpio->set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, panel);
 
-       return port;
+       return nv_encoder;
 }
 
 static struct nouveau_encoder *
@@ -258,25 +260,17 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
        if (ret < 0 && ret != -EACCES)
                return conn_status;
 
-       i2c = nouveau_connector_ddc_detect(connector, &nv_encoder);
-       if (i2c) {
+       nv_encoder = nouveau_connector_ddc_detect(connector);
+       if (nv_encoder && (i2c = nv_encoder->i2c) != NULL) {
                nv_connector->edid = drm_get_edid(connector, &i2c->adapter);
                drm_mode_connector_update_edid_property(connector,
                                                        nv_connector->edid);
                if (!nv_connector->edid) {
                        NV_ERROR(drm, "DDC responded, but no EDID for %s\n",
-                                drm_get_connector_name(connector));
+                                connector->name);
                        goto detect_analog;
                }
 
-               if (nv_encoder->dcb->type == DCB_OUTPUT_DP &&
-                   !nouveau_dp_detect(to_drm_encoder(nv_encoder))) {
-                       NV_ERROR(drm, "Detected %s, but failed init\n",
-                                drm_get_connector_name(connector));
-                       conn_status = connector_status_disconnected;
-                       goto out;
-               }
-
                /* Override encoder type for DVI-I based on whether EDID
                 * says the display is digital or analog, both use the
                 * same i2c channel so the value returned from ddc_detect
@@ -437,7 +431,7 @@ nouveau_connector_force(struct drm_connector *connector)
        nv_encoder = find_encoder(connector, type);
        if (!nv_encoder) {
                NV_ERROR(drm, "can't find encoder to force %s on!\n",
-                        drm_get_connector_name(connector));
+                        connector->name);
                connector->status = connector_status_disconnected;
                return;
        }
@@ -911,34 +905,104 @@ nouveau_connector_funcs_lvds = {
        .force = nouveau_connector_force
 };
 
+static void
+nouveau_connector_dp_dpms(struct drm_connector *connector, int mode)
+{
+       struct nouveau_encoder *nv_encoder = NULL;
+
+       if (connector->encoder)
+               nv_encoder = nouveau_encoder(connector->encoder);
+       if (nv_encoder && nv_encoder->dcb &&
+           nv_encoder->dcb->type == DCB_OUTPUT_DP) {
+               if (mode == DRM_MODE_DPMS_ON) {
+                       u8 data = DP_SET_POWER_D0;
+                       nv_wraux(nv_encoder->i2c, DP_SET_POWER, &data, 1);
+                       usleep_range(1000, 2000);
+               } else {
+                       u8 data = DP_SET_POWER_D3;
+                       nv_wraux(nv_encoder->i2c, DP_SET_POWER, &data, 1);
+               }
+       }
+
+       drm_helper_connector_dpms(connector, mode);
+}
+
+static const struct drm_connector_funcs
+nouveau_connector_funcs_dp = {
+       .dpms = nouveau_connector_dp_dpms,
+       .save = NULL,
+       .restore = NULL,
+       .detect = nouveau_connector_detect,
+       .destroy = nouveau_connector_destroy,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .set_property = nouveau_connector_set_property,
+       .force = nouveau_connector_force
+};
+
 static void
 nouveau_connector_hotplug_work(struct work_struct *work)
 {
        struct nouveau_connector *nv_connector =
-               container_of(work, struct nouveau_connector, hpd_work);
+               container_of(work, typeof(*nv_connector), work);
        struct drm_connector *connector = &nv_connector->base;
-       struct drm_device *dev = connector->dev;
-       struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
-       bool plugged = gpio->get(gpio, 0, nv_connector->hpd.func, 0xff);
+       struct nouveau_drm *drm = nouveau_drm(connector->dev);
+       const char *name = connector->name;
 
-       NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un",
-                drm_get_connector_name(connector));
+       if (nv_connector->status & NVKM_HPD_IRQ) {
+       } else {
+               bool plugged = (nv_connector->status != NVKM_HPD_UNPLUG);
 
-       if (plugged)
-               drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
-       else
-               drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+               NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name);
 
-       drm_helper_hpd_irq_event(dev);
+               if (plugged)
+                       drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
+               else
+                       drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+               drm_helper_hpd_irq_event(connector->dev);
+       }
+
+       nouveau_event_get(nv_connector->hpd);
 }
 
 static int
-nouveau_connector_hotplug(void *data, int index)
+nouveau_connector_hotplug(void *data, u32 type, int index)
 {
        struct nouveau_connector *nv_connector = data;
-       schedule_work(&nv_connector->hpd_work);
-       return NVKM_EVENT_KEEP;
+       nv_connector->status = type;
+       schedule_work(&nv_connector->work);
+       return NVKM_EVENT_DROP;
+}
+
+static ssize_t
+nouveau_connector_aux_xfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
+{
+       struct nouveau_connector *nv_connector =
+               container_of(aux, typeof(*nv_connector), aux);
+       struct nouveau_encoder *nv_encoder;
+       struct nouveau_i2c_port *port;
+       int ret;
+
+       nv_encoder = find_encoder(&nv_connector->base, DCB_OUTPUT_DP);
+       if (!nv_encoder || !(port = nv_encoder->i2c))
+               return -ENODEV;
+       if (WARN_ON(msg->size > 16))
+               return -E2BIG;
+       if (msg->size == 0)
+               return msg->size;
+
+       ret = nouveau_i2c(port)->acquire(port, 0);
+       if (ret)
+               return ret;
+
+       ret = port->func->aux(port, false, msg->request, msg->address,
+                             msg->buffer, msg->size);
+       nouveau_i2c(port)->release(port);
+       if (ret >= 0) {
+               msg->reply = ret;
+               return msg->size;
+       }
+
+       return ret;
 }
 
 static int
@@ -974,9 +1038,9 @@ nouveau_connector_create(struct drm_device *dev, int index)
 {
        const struct drm_connector_funcs *funcs = &nouveau_connector_funcs;
        struct nouveau_drm *drm = nouveau_drm(dev);
-       struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
        struct nouveau_display *disp = nouveau_display(dev);
        struct nouveau_connector *nv_connector = NULL;
+       struct nouveau_disp *pdisp = nouveau_disp(drm->device);
        struct drm_connector *connector;
        int type, ret = 0;
        bool dummy;
@@ -992,33 +1056,15 @@ nouveau_connector_create(struct drm_device *dev, int index)
                return ERR_PTR(-ENOMEM);
 
        connector = &nv_connector->base;
-       INIT_WORK(&nv_connector->hpd_work, nouveau_connector_hotplug_work);
        nv_connector->index = index;
 
        /* attempt to parse vbios connector type and hotplug gpio */
        nv_connector->dcb = olddcb_conn(dev, index);
        if (nv_connector->dcb) {
-               static const u8 hpd[16] = {
-                       0xff, 0x07, 0x08, 0xff, 0xff, 0x51, 0x52, 0xff,
-                       0xff, 0xff, 0xff, 0xff, 0xff, 0x5e, 0x5f, 0x60,
-               };
-
                u32 entry = ROM16(nv_connector->dcb[0]);
                if (olddcb_conntab(dev)[3] >= 4)
                        entry |= (u32)ROM16(nv_connector->dcb[2]) << 16;
 
-               ret = gpio->find(gpio, 0, hpd[ffs((entry & 0x07033000) >> 12)],
-                                DCB_GPIO_UNUSED, &nv_connector->hpd);
-               if (ret)
-                       nv_connector->hpd.func = DCB_GPIO_UNUSED;
-
-               if (nv_connector->hpd.func != DCB_GPIO_UNUSED) {
-                       nouveau_event_new(gpio->events, nv_connector->hpd.line,
-                                         nouveau_connector_hotplug,
-                                         nv_connector,
-                                        &nv_connector->hpd_func);
-               }
-
                nv_connector->type = nv_connector->dcb[0];
                if (drm_conntype_from_dcb(nv_connector->type) ==
                                          DRM_MODE_CONNECTOR_Unknown) {
@@ -1040,7 +1086,6 @@ nouveau_connector_create(struct drm_device *dev, int index)
                }
        } else {
                nv_connector->type = DCB_CONNECTOR_NONE;
-               nv_connector->hpd.func = DCB_GPIO_UNUSED;
        }
 
        /* no vbios data, or an unknown dcb connector type - attempt to
@@ -1080,8 +1125,8 @@ nouveau_connector_create(struct drm_device *dev, int index)
                }
        }
 
-       type = drm_conntype_from_dcb(nv_connector->type);
-       if (type == DRM_MODE_CONNECTOR_LVDS) {
+       switch ((type = drm_conntype_from_dcb(nv_connector->type))) {
+       case DRM_MODE_CONNECTOR_LVDS:
                ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &dummy);
                if (ret) {
                        NV_ERROR(drm, "Error parsing LVDS table, disabling\n");
@@ -1090,8 +1135,23 @@ nouveau_connector_create(struct drm_device *dev, int index)
                }
 
                funcs = &nouveau_connector_funcs_lvds;
-       } else {
+               break;
+       case DRM_MODE_CONNECTOR_DisplayPort:
+       case DRM_MODE_CONNECTOR_eDP:
+               nv_connector->aux.dev = dev->dev;
+               nv_connector->aux.transfer = nouveau_connector_aux_xfer;
+               ret = drm_dp_aux_register(&nv_connector->aux);
+               if (ret) {
+                       NV_ERROR(drm, "failed to register aux channel\n");
+                       kfree(nv_connector);
+                       return ERR_PTR(ret);
+               }
+
+               funcs = &nouveau_connector_funcs_dp;
+               break;
+       default:
                funcs = &nouveau_connector_funcs;
+               break;
        }
 
        /* defaults, will get overridden in detect() */
@@ -1166,10 +1226,16 @@ nouveau_connector_create(struct drm_device *dev, int index)
                break;
        }
 
-       connector->polled = DRM_CONNECTOR_POLL_CONNECT;
-       if (nv_connector->hpd.func != DCB_GPIO_UNUSED)
+       ret = nouveau_event_new(pdisp->hpd, NVKM_HPD, index,
+                               nouveau_connector_hotplug,
+                               nv_connector, &nv_connector->hpd);
+       if (ret)
+               connector->polled = DRM_CONNECTOR_POLL_CONNECT;
+       else
                connector->polled = DRM_CONNECTOR_POLL_HPD;
 
+       INIT_WORK(&nv_connector->work, nouveau_connector_hotplug_work);
+
        drm_sysfs_connector_add(connector);
        return connector;
 }
index 264a778f473b90a76c1cc3909c778dde9a736cd3..8861b6c579adecc26f2195ca7132dd32e060338e 100644 (file)
 #define __NOUVEAU_CONNECTOR_H__
 
 #include <drm/drm_edid.h>
+#include <drm/drm_dp_helper.h>
 #include "nouveau_crtc.h"
 
 #include <core/event.h>
 
 #include <subdev/bios.h>
-#include <subdev/bios/gpio.h>
 
 struct nouveau_i2c_port;
 
@@ -67,9 +67,11 @@ struct nouveau_connector {
        u8 index;
        u8 *dcb;
 
-       struct dcb_gpio_func hpd;
-       struct work_struct hpd_work;
-       struct nouveau_eventh *hpd_func;
+       struct nouveau_eventh *hpd;
+       u32 status;
+       struct work_struct work;
+
+       struct drm_dp_aux aux;
 
        int dithering_mode;
        int dithering_depth;
index d1e5890784d759b8807495edd8db9949457536d1..a0534489d23f1c52332f52e270586d814d8abe9a 100644 (file)
@@ -74,7 +74,7 @@ struct nouveau_crtc {
 
 static inline struct nouveau_crtc *nouveau_crtc(struct drm_crtc *crtc)
 {
-       return container_of(crtc, struct nouveau_crtc, base);
+       return crtc ? container_of(crtc, struct nouveau_crtc, base) : NULL;
 }
 
 static inline struct drm_crtc *to_drm_crtc(struct nouveau_crtc *crtc)
index da764a4ed9588273fe6cf02ed0d6c589aa2ebda0..26b5647188efd736df9f799168fc99b245bb70c2 100644 (file)
@@ -42,7 +42,7 @@
 #include <core/class.h>
 
 static int
-nouveau_display_vblank_handler(void *data, int head)
+nouveau_display_vblank_handler(void *data, u32 type, int head)
 {
        struct nouveau_drm *drm = data;
        drm_handle_vblank(drm->dev, head);
@@ -178,7 +178,7 @@ nouveau_display_vblank_init(struct drm_device *dev)
                return -ENOMEM;
 
        for (i = 0; i < dev->mode_config.num_crtc; i++) {
-               ret = nouveau_event_new(pdisp->vblank, i,
+               ret = nouveau_event_new(pdisp->vblank, 1, i,
                                        nouveau_display_vblank_handler,
                                        drm, &disp->vblank[i]);
                if (ret) {
@@ -393,7 +393,7 @@ nouveau_display_init(struct drm_device *dev)
        /* enable hotplug interrupts */
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                struct nouveau_connector *conn = nouveau_connector(connector);
-               if (conn->hpd_func) nouveau_event_get(conn->hpd_func);
+               if (conn->hpd) nouveau_event_get(conn->hpd);
        }
 
        return ret;
@@ -408,7 +408,7 @@ nouveau_display_fini(struct drm_device *dev)
        /* disable hotplug interrupts */
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                struct nouveau_connector *conn = nouveau_connector(connector);
-               if (conn->hpd_func) nouveau_event_put(conn->hpd_func);
+               if (conn->hpd) nouveau_event_put(conn->hpd);
        }
 
        drm_kms_helper_poll_disable(dev);
@@ -798,6 +798,7 @@ nouveau_finish_page_flip(struct nouveau_channel *chan,
        struct drm_device *dev = drm->dev;
        struct nouveau_page_flip_state *s;
        unsigned long flags;
+       int crtcid = -1;
 
        spin_lock_irqsave(&dev->event_lock, flags);
 
@@ -808,8 +809,13 @@ nouveau_finish_page_flip(struct nouveau_channel *chan,
        }
 
        s = list_first_entry(&fctx->flip, struct nouveau_page_flip_state, head);
-       if (s->event)
-               drm_send_vblank_event(dev, s->crtc, s->event);
+       if (s->event) {
+               /* Vblank timestamps/counts are only correct on >= NV-50 */
+               if (nv_device(drm->device)->card_type >= NV_50)
+                       crtcid = s->crtc;
+
+               drm_send_vblank_event(dev, crtcid, s->event);
+       }
 
        list_del(&s->head);
        if (ps)
index 36fd22500569541505870dc1e185eddc138627db..5675ffc175aed933711a5acef1ffe34e86fd9059 100644 (file)
@@ -55,11 +55,10 @@ nouveau_dp_probe_oui(struct drm_device *dev, struct nouveau_i2c_port *auxch,
 
 }
 
-bool
-nouveau_dp_detect(struct drm_encoder *encoder)
+int
+nouveau_dp_detect(struct nouveau_encoder *nv_encoder)
 {
-       struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-       struct drm_device *dev = encoder->dev;
+       struct drm_device *dev = nv_encoder->base.base.dev;
        struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_i2c_port *auxch;
        u8 *dpcd = nv_encoder->dp.dpcd;
@@ -67,11 +66,11 @@ nouveau_dp_detect(struct drm_encoder *encoder)
 
        auxch = nv_encoder->i2c;
        if (!auxch)
-               return false;
+               return -ENODEV;
 
        ret = nv_rdaux(auxch, DP_DPCD_REV, dpcd, 8);
        if (ret)
-               return false;
+               return ret;
 
        nv_encoder->dp.link_bw = 27000 * dpcd[1];
        nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK;
@@ -91,6 +90,5 @@ nouveau_dp_detect(struct drm_encoder *encoder)
                     nv_encoder->dp.link_nr, nv_encoder->dp.link_bw);
 
        nouveau_dp_probe_oui(dev, auxch, dpcd);
-
-       return true;
+       return 0;
 }
index 24660c0f713d636f07804e0125e4d9b3499fbe27..5f0e37fc28490901a96f6f6dc1228d02e6bb7b98 100644 (file)
@@ -46,6 +46,7 @@ struct nouveau_encoder {
        /* different to drm_encoder.crtc, this reflects what's
         * actually programmed on the hw, not the proposed crtc */
        struct drm_crtc *crtc;
+       u32 ctrl;
 
        struct drm_display_mode mode;
        int last_dpms;
@@ -84,9 +85,7 @@ get_slave_funcs(struct drm_encoder *enc)
 }
 
 /* nouveau_dp.c */
-bool nouveau_dp_detect(struct drm_encoder *);
-void nouveau_dp_dpms(struct drm_encoder *, int mode, u32 datarate,
-                    struct nouveau_object *);
+int nouveau_dp_detect(struct nouveau_encoder *);
 
 struct nouveau_connector *
 nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
index 90074d620e31265bdcc70d4b7285fc9be077fee8..ab5ea3b0d6665cfbaea93c51781c03f6ff3eeef2 100644 (file)
@@ -166,7 +166,7 @@ nouveau_fence_done(struct nouveau_fence *fence)
 }
 
 static int
-nouveau_fence_wait_uevent_handler(void *data, int index)
+nouveau_fence_wait_uevent_handler(void *data, u32 type, int index)
 {
        struct nouveau_fence_priv *priv = data;
        wake_up_all(&priv->waiting);
@@ -183,7 +183,7 @@ nouveau_fence_wait_uevent(struct nouveau_fence *fence, bool intr)
        struct nouveau_eventh *handler;
        int ret = 0;
 
-       ret = nouveau_event_new(pfifo->uevent, 0,
+       ret = nouveau_event_new(pfifo->uevent, 1, 0,
                                nouveau_fence_wait_uevent_handler,
                                priv, &handler);
        if (ret)
index c1a7e5a73a26b0c98886d3583400507b4fc65866..462679a8fec5783735a5cabd61058a79e212999c 100644 (file)
@@ -57,7 +57,7 @@ long nouveau_compat_ioctl(struct file *filp, unsigned int cmd,
                return drm_compat_ioctl(filp, cmd, arg);
 
 #if 0
-       if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(mga_compat_ioctls))
+       if (nr < DRM_COMMAND_BASE + ARRAY_SIZE(mga_compat_ioctls))
                fn = nouveau_compat_ioctls[nr - DRM_COMMAND_BASE];
 #endif
        if (fn != NULL)
index fb84da3cb50d50a31f905052b4b85c2cbaf54143..4f4c3fec6916fc4ea568276851f41f5adaadfbd1 100644 (file)
@@ -64,12 +64,13 @@ static bool
 nouveau_switcheroo_can_switch(struct pci_dev *pdev)
 {
        struct drm_device *dev = pci_get_drvdata(pdev);
-       bool can_switch;
 
-       spin_lock(&dev->count_lock);
-       can_switch = (dev->open_count == 0);
-       spin_unlock(&dev->count_lock);
-       return can_switch;
+       /*
+        * FIXME: open_count is protected by drm_global_mutex but that would lead to
+        * locking inversion with the driver load path. And the access here is
+        * completely racy anyway. So don't bother with locking for now.
+        */
+       return dev->open_count == 0;
 }
 
 static const struct vga_switcheroo_client_ops
index 58af547b0b930e2ea64e74b0966c7a81e11fba54..afdf607df3e6e432d122845abed5502c4a3938d5 100644 (file)
@@ -1,4 +1,4 @@
-       /*
+/*
  * Copyright 2011 Red Hat Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
@@ -26,6 +26,7 @@
 
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
 
 #include "nouveau_drm.h"
 #include "nouveau_dma.h"
@@ -957,7 +958,7 @@ nv50_crtc_prepare(struct drm_crtc *crtc)
 
        nv50_display_flip_stop(crtc);
 
-       push = evo_wait(mast, 2);
+       push = evo_wait(mast, 6);
        if (push) {
                if (nv50_vers(mast) < NV84_DISP_MAST_CLASS) {
                        evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1);
@@ -1207,6 +1208,7 @@ static void
 nv50_crtc_disable(struct drm_crtc *crtc)
 {
        struct nv50_head *head = nv50_head(crtc);
+       evo_sync(crtc->dev);
        if (head->image)
                nouveau_bo_unpin(head->image);
        nouveau_bo_ref(NULL, &head->image);
@@ -1700,10 +1702,9 @@ nv50_hdmi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode)
 }
 
 static void
-nv50_hdmi_disconnect(struct drm_encoder *encoder)
+nv50_hdmi_disconnect(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc)
 {
        struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-       struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc);
        struct nv50_disp *disp = nv50_disp(encoder->dev);
        const u32 moff = (nv_crtc->index << 3) | nv_encoder->or;
 
@@ -1722,7 +1723,7 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode)
        struct drm_device *dev = encoder->dev;
        struct nv50_disp *disp = nv50_disp(dev);
        struct drm_encoder *partner;
-       int or = nv_encoder->or;
+       u32 mthd;
 
        nv_encoder->last_dpms = mode;
 
@@ -1740,7 +1741,17 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode)
                }
        }
 
-       nv_call(disp->core, NV50_DISP_SOR_PWR + or, (mode == DRM_MODE_DPMS_ON));
+       mthd  = (ffs(nv_encoder->dcb->sorconf.link) - 1) << 2;
+       mthd |= nv_encoder->or;
+
+       if (nv_encoder->dcb->type == DCB_OUTPUT_DP) {
+               nv_call(disp->core, NV50_DISP_SOR_PWR | mthd, 1);
+               mthd |= NV94_DISP_SOR_DP_PWR;
+       } else {
+               mthd |= NV50_DISP_SOR_PWR;
+       }
+
+       nv_call(disp->core, mthd, (mode == DRM_MODE_DPMS_ON));
 }
 
 static bool
@@ -1764,33 +1775,36 @@ nv50_sor_mode_fixup(struct drm_encoder *encoder,
 }
 
 static void
-nv50_sor_disconnect(struct drm_encoder *encoder)
+nv50_sor_ctrl(struct nouveau_encoder *nv_encoder, u32 mask, u32 data)
 {
-       struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-       struct nv50_mast *mast = nv50_mast(encoder->dev);
-       const int or = nv_encoder->or;
-       u32 *push;
-
-       if (nv_encoder->crtc) {
-               nv50_crtc_prepare(nv_encoder->crtc);
-
-               push = evo_wait(mast, 4);
-               if (push) {
-                       if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
-                               evo_mthd(push, 0x0600 + (or * 0x40), 1);
-                               evo_data(push, 0x00000000);
-                       } else {
-                               evo_mthd(push, 0x0200 + (or * 0x20), 1);
-                               evo_data(push, 0x00000000);
-                       }
-                       evo_kick(push, mast);
+       struct nv50_mast *mast = nv50_mast(nv_encoder->base.base.dev);
+       u32 temp = (nv_encoder->ctrl & ~mask) | (data & mask), *push;
+       if (temp != nv_encoder->ctrl && (push = evo_wait(mast, 2))) {
+               if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
+                       evo_mthd(push, 0x0600 + (nv_encoder->or * 0x40), 1);
+                       evo_data(push, (nv_encoder->ctrl = temp));
+               } else {
+                       evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 1);
+                       evo_data(push, (nv_encoder->ctrl = temp));
                }
-
-               nv50_hdmi_disconnect(encoder);
+               evo_kick(push, mast);
        }
+}
+
+static void
+nv50_sor_disconnect(struct drm_encoder *encoder)
+{
+       struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+       struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc);
 
        nv_encoder->last_dpms = DRM_MODE_DPMS_OFF;
        nv_encoder->crtc = NULL;
+
+       if (nv_crtc) {
+               nv50_crtc_prepare(&nv_crtc->base);
+               nv50_sor_ctrl(nv_encoder, 1 << nv_crtc->index, 0);
+               nv50_hdmi_disconnect(&nv_encoder->base.base, nv_crtc);
+       }
 }
 
 static void
@@ -1810,12 +1824,14 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
        struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
        struct nouveau_connector *nv_connector;
        struct nvbios *bios = &drm->vbios;
-       u32 *push, lvds = 0;
+       u32 lvds = 0, mask, ctrl;
        u8 owner = 1 << nv_crtc->index;
        u8 proto = 0xf;
        u8 depth = 0x0;
 
        nv_connector = nouveau_encoder_connector_get(nv_encoder);
+       nv_encoder->crtc = encoder->crtc;
+
        switch (nv_encoder->dcb->type) {
        case DCB_OUTPUT_TMDS:
                if (nv_encoder->dcb->sorconf.link & 1) {
@@ -1827,7 +1843,7 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
                        proto = 0x2;
                }
 
-               nv50_hdmi_mode_set(encoder, mode);
+               nv50_hdmi_mode_set(&nv_encoder->base.base, mode);
                break;
        case DCB_OUTPUT_LVDS:
                proto = 0x0;
@@ -1883,19 +1899,11 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
                break;
        }
 
-       nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON);
+       nv50_sor_dpms(&nv_encoder->base.base, DRM_MODE_DPMS_ON);
 
-       push = evo_wait(nv50_mast(dev), 8);
-       if (push) {
-               if (nv50_vers(mast) < NVD0_DISP_CLASS) {
-                       u32 ctrl = (depth << 16) | (proto << 8) | owner;
-                       if (mode->flags & DRM_MODE_FLAG_NHSYNC)
-                               ctrl |= 0x00001000;
-                       if (mode->flags & DRM_MODE_FLAG_NVSYNC)
-                               ctrl |= 0x00002000;
-                       evo_mthd(push, 0x0600 + (nv_encoder->or * 0x040), 1);
-                       evo_data(push, ctrl);
-               } else {
+       if (nv50_vers(mast) >= NVD0_DISP_CLASS) {
+               u32 *push = evo_wait(mast, 3);
+               if (push) {
                        u32 magic = 0x31ec6000 | (nv_crtc->index << 25);
                        u32 syncs = 0x00000001;
 
@@ -1910,14 +1918,21 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
                        evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2);
                        evo_data(push, syncs | (depth << 6));
                        evo_data(push, magic);
-                       evo_mthd(push, 0x0200 + (nv_encoder->or * 0x020), 1);
-                       evo_data(push, owner | (proto << 8));
+                       evo_kick(push, mast);
                }
 
-               evo_kick(push, mast);
+               ctrl = proto << 8;
+               mask = 0x00000f00;
+       } else {
+               ctrl = (depth << 16) | (proto << 8);
+               if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+                       ctrl |= 0x00001000;
+               if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+                       ctrl |= 0x00002000;
+               mask = 0x000f3f00;
        }
 
-       nv_encoder->crtc = encoder->crtc;
+       nv50_sor_ctrl(nv_encoder, mask | owner, ctrl | owner);
 }
 
 static void
@@ -2295,7 +2310,7 @@ nv50_display_create(struct drm_device *dev)
                        continue;
 
                NV_WARN(drm, "%s has no encoders, removing\n",
-                       drm_get_connector_name(connector));
+                       connector->name);
                connector->funcs->destroy(connector);
        }
 
index e3c47a8005ff149e07b5770f38cdaf1b9aba394f..2d28dc337cfb4ed50104370478021738de688316 100644 (file)
@@ -319,13 +319,13 @@ static void page_flip_worker(struct work_struct *work)
        struct drm_display_mode *mode = &crtc->mode;
        struct drm_gem_object *bo;
 
-       mutex_lock(&crtc->mutex);
+       drm_modeset_lock(&crtc->mutex, NULL);
        omap_plane_mode_set(omap_crtc->plane, crtc, crtc->primary->fb,
                        0, 0, mode->hdisplay, mode->vdisplay,
                        crtc->x << 16, crtc->y << 16,
                        mode->hdisplay << 16, mode->vdisplay << 16,
                        vblank_cb, crtc);
-       mutex_unlock(&crtc->mutex);
+       drm_modeset_unlock(&crtc->mutex);
 
        bo = omap_framebuffer_bo(crtc->primary->fb, 0);
        drm_gem_object_unreference_unlocked(bo);
@@ -465,7 +465,7 @@ static void apply_worker(struct work_struct *work)
         * the callbacks and list modification all serialized
         * with respect to modesetting ioctls from userspace.
         */
-       mutex_lock(&crtc->mutex);
+       drm_modeset_lock(&crtc->mutex, NULL);
        dispc_runtime_get();
 
        /*
@@ -510,7 +510,7 @@ static void apply_worker(struct work_struct *work)
 
 out:
        dispc_runtime_put();
-       mutex_unlock(&crtc->mutex);
+       drm_modeset_unlock(&crtc->mutex);
 }
 
 int omap_crtc_apply(struct drm_crtc *crtc,
@@ -518,7 +518,7 @@ int omap_crtc_apply(struct drm_crtc *crtc,
 {
        struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
 
-       WARN_ON(!mutex_is_locked(&crtc->mutex));
+       WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
 
        /* no need to queue it again if it is already queued: */
        if (apply->queued)
index c8270e4b26f3f7d15c4e0bfcb1230a5fb3e4cd05..002b9721e85a348077f09285c99bbb7600ecce32 100644 (file)
@@ -588,9 +588,7 @@ static void dev_lastclose(struct drm_device *dev)
                }
        }
 
-       drm_modeset_lock_all(dev);
-       ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev);
-       drm_modeset_unlock_all(dev);
+       ret = drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev);
        if (ret)
                DBG("failed to restore crtc mode");
 }
index 8b019602ffe61564dfa979a082e9c6b0afc0a1f2..2a5cacdc344b621fd182d3e8d053bbc0e227ac60 100644 (file)
@@ -346,6 +346,7 @@ void omap_framebuffer_flush(struct drm_framebuffer *fb,
 
        VERB("flush: %d,%d %dx%d, fb=%p", x, y, w, h, fb);
 
+       /* FIXME: This is racy - no protection against modeset config changes. */
        while ((connector = omap_framebuffer_get_next_connector(fb, connector))) {
                /* only consider connectors that are part of a chain */
                if (connector->encoder && connector->encoder->crtc) {
index 1f1f8371a199c172ed2bb4331864a07cef1ef60b..db1601fdbe2946f581b0ec108057f94f647d1d10 100644 (file)
@@ -27,6 +27,7 @@
 #define MCS_ELVSS_ON           0xb1
 #define MCS_USER_SETTING       0xf0
 #define MCS_DISPCTL            0xf2
+#define MCS_POWER_CTRL         0xf4
 #define MCS_GTCON              0xf7
 #define MCS_PANEL_CONDITION    0xf8
 #define MCS_GAMMA_SET1         0xf9
@@ -182,6 +183,8 @@ static void ld9040_init(struct ld9040 *ctx)
        ld9040_dcs_write_seq_static(ctx, MCS_DISPCTL,
                0x02, 0x08, 0x08, 0x10, 0x10);
        ld9040_dcs_write_seq_static(ctx, MCS_MANPWR, 0x04);
+       ld9040_dcs_write_seq_static(ctx, MCS_POWER_CTRL,
+               0x0a, 0x87, 0x25, 0x6a, 0x44, 0x02, 0x88);
        ld9040_dcs_write_seq_static(ctx, MCS_ELVSS_ON, 0x0d, 0x00, 0x16);
        ld9040_dcs_write_seq_static(ctx, MCS_GTCON, 0x09, 0x00, 0x00);
        ld9040_brightness_set(ctx);
index 35941d2412b82882981709a3c44af8d834c71f38..06e57a26db7a5ed09561442aab1dfe840d770775 100644 (file)
@@ -847,6 +847,7 @@ static void s6e8aa0_read_mtp_id(struct s6e8aa0 *ctx)
        if (i >= ARRAY_SIZE(s6e8aa0_variants)) {
                dev_err(ctx->dev, "unsupported display version %d\n", id[1]);
                ctx->error = -EINVAL;
+               return;
        }
 
        ctx->variant = &s6e8aa0_variants[i];
index 309f29e9234a91cd633a7f07f70131c2b846af99..a25136132c318b3a18f9579d71de8799ffefad15 100644 (file)
@@ -262,6 +262,13 @@ static int panel_simple_remove(struct device *dev)
        return 0;
 }
 
+static void panel_simple_shutdown(struct device *dev)
+{
+       struct panel_simple *panel = dev_get_drvdata(dev);
+
+       panel_simple_disable(&panel->base);
+}
+
 static const struct drm_display_mode auo_b101aw03_mode = {
        .clock = 51450,
        .hdisplay = 1024,
@@ -284,6 +291,28 @@ static const struct panel_desc auo_b101aw03 = {
        },
 };
 
+static const struct drm_display_mode auo_b133xtn01_mode = {
+       .clock = 69500,
+       .hdisplay = 1366,
+       .hsync_start = 1366 + 48,
+       .hsync_end = 1366 + 48 + 32,
+       .htotal = 1366 + 48 + 32 + 20,
+       .vdisplay = 768,
+       .vsync_start = 768 + 3,
+       .vsync_end = 768 + 3 + 6,
+       .vtotal = 768 + 3 + 6 + 13,
+       .vrefresh = 60,
+};
+
+static const struct panel_desc auo_b133xtn01 = {
+       .modes = &auo_b133xtn01_mode,
+       .num_modes = 1,
+       .size = {
+               .width = 293,
+               .height = 165,
+       },
+};
+
 static const struct drm_display_mode chunghwa_claa101wa01a_mode = {
        .clock = 72070,
        .hdisplay = 1366,
@@ -328,6 +357,52 @@ static const struct panel_desc chunghwa_claa101wb01 = {
        },
 };
 
+static const struct drm_display_mode edt_et057090dhu_mode = {
+       .clock = 25175,
+       .hdisplay = 640,
+       .hsync_start = 640 + 16,
+       .hsync_end = 640 + 16 + 30,
+       .htotal = 640 + 16 + 30 + 114,
+       .vdisplay = 480,
+       .vsync_start = 480 + 10,
+       .vsync_end = 480 + 10 + 3,
+       .vtotal = 480 + 10 + 3 + 32,
+       .vrefresh = 60,
+       .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
+};
+
+static const struct panel_desc edt_et057090dhu = {
+       .modes = &edt_et057090dhu_mode,
+       .num_modes = 1,
+       .size = {
+               .width = 115,
+               .height = 86,
+       },
+};
+
+static const struct drm_display_mode edt_etm0700g0dh6_mode = {
+       .clock = 33260,
+       .hdisplay = 800,
+       .hsync_start = 800 + 40,
+       .hsync_end = 800 + 40 + 128,
+       .htotal = 800 + 40 + 128 + 88,
+       .vdisplay = 480,
+       .vsync_start = 480 + 10,
+       .vsync_end = 480 + 10 + 2,
+       .vtotal = 480 + 10 + 2 + 33,
+       .vrefresh = 60,
+       .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+};
+
+static const struct panel_desc edt_etm0700g0dh6 = {
+       .modes = &edt_etm0700g0dh6_mode,
+       .num_modes = 1,
+       .size = {
+               .width = 152,
+               .height = 91,
+       },
+};
+
 static const struct drm_display_mode lg_lp129qe_mode = {
        .clock = 285250,
        .hdisplay = 2560,
@@ -376,12 +451,24 @@ static const struct of_device_id platform_of_match[] = {
        {
                .compatible = "auo,b101aw03",
                .data = &auo_b101aw03,
+       }, {
+               .compatible = "auo,b133xtn01",
+               .data = &auo_b133xtn01,
        }, {
                .compatible = "chunghwa,claa101wa01a",
                .data = &chunghwa_claa101wa01a
        }, {
                .compatible = "chunghwa,claa101wb01",
                .data = &chunghwa_claa101wb01
+       }, {
+               .compatible = "edt,et057090dhu",
+               .data = &edt_et057090dhu,
+       }, {
+               .compatible = "edt,et070080dh6",
+               .data = &edt_etm0700g0dh6,
+       }, {
+               .compatible = "edt,etm0700g0dh6",
+               .data = &edt_etm0700g0dh6,
        }, {
                .compatible = "lg,lp129qe",
                .data = &lg_lp129qe,
@@ -412,6 +499,11 @@ static int panel_simple_platform_remove(struct platform_device *pdev)
        return panel_simple_remove(&pdev->dev);
 }
 
+static void panel_simple_platform_shutdown(struct platform_device *pdev)
+{
+       panel_simple_shutdown(&pdev->dev);
+}
+
 static struct platform_driver panel_simple_platform_driver = {
        .driver = {
                .name = "panel-simple",
@@ -420,6 +512,7 @@ static struct platform_driver panel_simple_platform_driver = {
        },
        .probe = panel_simple_platform_probe,
        .remove = panel_simple_platform_remove,
+       .shutdown = panel_simple_platform_shutdown,
 };
 
 struct panel_desc_dsi {
@@ -561,6 +654,11 @@ static int panel_simple_dsi_remove(struct mipi_dsi_device *dsi)
        return panel_simple_remove(&dsi->dev);
 }
 
+static void panel_simple_dsi_shutdown(struct mipi_dsi_device *dsi)
+{
+       panel_simple_shutdown(&dsi->dev);
+}
+
 static struct mipi_dsi_driver panel_simple_dsi_driver = {
        .driver = {
                .name = "panel-simple-dsi",
@@ -569,6 +667,7 @@ static struct mipi_dsi_driver panel_simple_dsi_driver = {
        },
        .probe = panel_simple_dsi_probe,
        .remove = panel_simple_dsi_remove,
+       .shutdown = panel_simple_dsi_shutdown,
 };
 
 static int __init panel_simple_init(void)
index 41bdd174657e6ea34c4ad8a703d3288e721d919f..5d7ea24618524bc0c23187a2b58e551eb96cdbc9 100644 (file)
@@ -574,6 +574,10 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc,
                           bo->surf.height, bo->surf.stride, bo->surf.format);
                qxl_io_create_primary(qdev, base_offset, bo);
                bo->is_primary = true;
+       }
+
+       if (bo->is_primary) {
+               DRM_DEBUG_KMS("setting surface_id to 0 for primary surface %d on crtc %d\n", bo->surface_id, qcrtc->index);
                surf_id = 0;
        } else {
                surf_id = bo->surface_id;
@@ -841,7 +845,7 @@ static const struct drm_connector_funcs qxl_connector_funcs = {
        .save = qxl_conn_save,
        .restore = qxl_conn_restore,
        .detect = qxl_conn_detect,
-       .fill_modes = drm_helper_probe_single_connector_modes,
+       .fill_modes = drm_helper_probe_single_connector_modes_nomerge,
        .set_property = qxl_conn_set_property,
        .destroy = qxl_conn_destroy,
 };
index fee8748bdca52fda145888018b3d4a89c43de866..6e936634d65c8d36593994aa0af4b59df54ebc5c 100644 (file)
@@ -214,7 +214,6 @@ static struct pci_driver qxl_pci_driver = {
 static struct drm_driver qxl_driver = {
        .driver_features = DRIVER_GEM | DRIVER_MODESET |
                           DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,
-       .dev_priv_size = 0,
        .load = qxl_driver_load,
        .unload = qxl_driver_unload,
 
index 0bb86e6d41b44cc4c1641e72da6e67e754994798..b110883f8253dc272498abb64b4c408146519930 100644 (file)
@@ -451,4 +451,4 @@ const struct drm_ioctl_desc qxl_ioctls[] = {
                          DRM_AUTH|DRM_UNLOCKED),
 };
 
-int qxl_max_ioctls = DRM_ARRAY_SIZE(qxl_ioctls);
+int qxl_max_ioctls = ARRAY_SIZE(qxl_ioctls);
index 28f84b4fce32fab576d4bd71d324bceaf93c0682..34d6a85e9023655efd5c67bd6b1e67d20b34b1bc 100644 (file)
@@ -87,7 +87,7 @@ int qxl_irq_init(struct qxl_device *qdev)
        atomic_set(&qdev->irq_received_cursor, 0);
        atomic_set(&qdev->irq_received_io_cmd, 0);
        qdev->irq_received_error = 0;
-       ret = drm_irq_install(qdev->ddev);
+       ret = drm_irq_install(qdev->ddev, qdev->ddev->pdev->irq);
        qdev->ram_header->int_mask = QXL_INTERRUPT_MASK;
        if (unlikely(ret != 0)) {
                DRM_ERROR("Failed installing irq: %d\n", ret);
index d52c27527b9a638a09307f75a8bd4b91a56c7aef..71a1baeac14edc4ceabb8ab397d0dfe2c23b335b 100644 (file)
@@ -109,13 +109,11 @@ static const struct vm_operations_struct *ttm_vm_ops;
 static int qxl_ttm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 {
        struct ttm_buffer_object *bo;
-       struct qxl_device *qdev;
        int r;
 
        bo = (struct ttm_buffer_object *)vma->vm_private_data;
        if (bo == NULL)
                return VM_FAULT_NOPAGE;
-       qdev = qxl_get_qdev(bo->bdev);
        r = ttm_vm_ops->fault(vma, vmf);
        return r;
 }
@@ -162,10 +160,6 @@ static int qxl_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags)
 static int qxl_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
                             struct ttm_mem_type_manager *man)
 {
-       struct qxl_device *qdev;
-
-       qdev = qxl_get_qdev(bdev);
-
        switch (type) {
        case TTM_PL_SYSTEM:
                /* System memory */
index b0d0fd3e437676cb306a55df7fa82cba49bcdda1..663f38c63ba6bd334d4732de3e80cdbf89ae711b 100644 (file)
@@ -203,7 +203,7 @@ long r128_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
        if (nr < DRM_COMMAND_BASE)
                return drm_compat_ioctl(filp, cmd, arg);
 
-       if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(r128_compat_ioctls))
+       if (nr < DRM_COMMAND_BASE + ARRAY_SIZE(r128_compat_ioctls))
                fn = r128_compat_ioctls[nr - DRM_COMMAND_BASE];
 
        if (fn != NULL)
index e806dacd452f7b9cc8c93d20308db93ea0ef0da6..575e986f82a76239113e2e122b42ce961a24e3e0 100644 (file)
@@ -1594,7 +1594,7 @@ static int r128_getparam(struct drm_device *dev, void *data, struct drm_file *fi
 
        switch (param->param) {
        case R128_PARAM_IRQ_NR:
-               value = drm_dev_to_irq(dev);
+               value = dev->pdev->irq;
                break;
        default:
                return -EINVAL;
@@ -1641,4 +1641,4 @@ const struct drm_ioctl_desc r128_ioctls[] = {
        DRM_IOCTL_DEF_DRV(R128_GETPARAM, r128_getparam, DRM_AUTH),
 };
 
-int r128_max_ioctl = DRM_ARRAY_SIZE(r128_ioctls);
+int r128_max_ioctl = ARRAY_SIZE(r128_ioctls);
index 09433534dc47099b5110208b9b5ee1398cc1991a..dbcbfe80aac0293892259f5602256d2f559453cf 100644 (file)
@@ -72,7 +72,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \
        radeon_cs.o radeon_bios.o radeon_benchmark.o r100.o r300.o r420.o \
        rs400.o rs600.o rs690.o rv515.o r520.o r600.o rv770.o radeon_test.o \
        r200.o radeon_legacy_tv.o r600_cs.o r600_blit_shaders.o \
-       radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o \
+       radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o dce3_1_afmt.o \
        evergreen.o evergreen_cs.o evergreen_blit_shaders.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 \
index e911898348f87d673b904ae09707faa0ea0ce849..26c12a3fe4301f25c53d5e0f431c0eaa4076d043 100644 (file)
@@ -557,6 +557,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
        u32 adjusted_clock = mode->clock;
        int encoder_mode = atombios_get_encoder_mode(encoder);
        u32 dp_clock = mode->clock;
+       u32 clock = mode->clock;
        int bpc = radeon_crtc->bpc;
        bool is_duallink = radeon_dig_monitor_is_duallink(encoder, mode->clock);
 
@@ -632,6 +633,24 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
                        radeon_crtc->pll_flags |= RADEON_PLL_USE_REF_DIV;
        }
 
+       /* adjust pll for deep color modes */
+       if (encoder_mode == ATOM_ENCODER_MODE_HDMI) {
+               switch (bpc) {
+               case 8:
+               default:
+                       break;
+               case 10:
+                       clock = (clock * 5) / 4;
+                       break;
+               case 12:
+                       clock = (clock * 3) / 2;
+                       break;
+               case 16:
+                       clock = clock * 2;
+                       break;
+               }
+       }
+
        /* DCE3+ has an AdjustDisplayPll that will adjust the pixel clock
         * accordingly based on the encoder/transmitter to work around
         * special hw requirements.
@@ -653,7 +672,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
                        switch (crev) {
                        case 1:
                        case 2:
-                               args.v1.usPixelClock = cpu_to_le16(mode->clock / 10);
+                               args.v1.usPixelClock = cpu_to_le16(clock / 10);
                                args.v1.ucTransmitterID = radeon_encoder->encoder_id;
                                args.v1.ucEncodeMode = encoder_mode;
                                if (radeon_crtc->ss_enabled && radeon_crtc->ss.percentage)
@@ -665,7 +684,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
                                adjusted_clock = le16_to_cpu(args.v1.usPixelClock) * 10;
                                break;
                        case 3:
-                               args.v3.sInput.usPixelClock = cpu_to_le16(mode->clock / 10);
+                               args.v3.sInput.usPixelClock = cpu_to_le16(clock / 10);
                                args.v3.sInput.ucTransmitterID = radeon_encoder->encoder_id;
                                args.v3.sInput.ucEncodeMode = encoder_mode;
                                args.v3.sInput.ucDispPllConfig = 0;
@@ -679,10 +698,6 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
                                        args.v3.sInput.usPixelClock = cpu_to_le16(dp_clock / 10);
                                } else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
                                        struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
-                                       if (encoder_mode == ATOM_ENCODER_MODE_HDMI)
-                                               /* deep color support */
-                                               args.v3.sInput.usPixelClock =
-                                                       cpu_to_le16((mode->clock * bpc / 8) / 10);
                                        if (dig->coherent_mode)
                                                args.v3.sInput.ucDispPllConfig |=
                                                        DISPPLL_CONFIG_COHERENT_MODE;
@@ -862,14 +877,21 @@ static void atombios_crtc_program_pll(struct drm_crtc *crtc,
                        args.v5.ucMiscInfo = 0; /* HDMI depth, etc. */
                        if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK))
                                args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_REF_DIV_SRC;
-                       switch (bpc) {
-                       case 8:
-                       default:
-                               args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_24BPP;
-                               break;
-                       case 10:
-                               args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_30BPP;
-                               break;
+                       if (encoder_mode == ATOM_ENCODER_MODE_HDMI) {
+                               switch (bpc) {
+                               case 8:
+                               default:
+                                       args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_24BPP;
+                                       break;
+                               case 10:
+                                       /* yes this is correct, the atom define is wrong */
+                                       args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_32BPP;
+                                       break;
+                               case 12:
+                                       /* yes this is correct, the atom define is wrong */
+                                       args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_30BPP;
+                                       break;
+                               }
                        }
                        args.v5.ucTransmitterID = encoder_id;
                        args.v5.ucEncoderMode = encoder_mode;
@@ -884,20 +906,22 @@ static void atombios_crtc_program_pll(struct drm_crtc *crtc,
                        args.v6.ucMiscInfo = 0; /* HDMI depth, etc. */
                        if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK))
                                args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_REF_DIV_SRC;
-                       switch (bpc) {
-                       case 8:
-                       default:
-                               args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_24BPP;
-                               break;
-                       case 10:
-                               args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_30BPP;
-                               break;
-                       case 12:
-                               args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_36BPP;
-                               break;
-                       case 16:
-                               args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_48BPP;
-                               break;
+                       if (encoder_mode == ATOM_ENCODER_MODE_HDMI) {
+                               switch (bpc) {
+                               case 8:
+                               default:
+                                       args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_24BPP;
+                                       break;
+                               case 10:
+                                       args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_30BPP_V6;
+                                       break;
+                               case 12:
+                                       args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_36BPP_V6;
+                                       break;
+                               case 16:
+                                       args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_48BPP;
+                                       break;
+                               }
                        }
                        args.v6.ucTransmitterID = encoder_id;
                        args.v6.ucEncoderMode = encoder_mode;
@@ -938,6 +962,9 @@ static bool atombios_crtc_prepare_pll(struct drm_crtc *crtc, struct drm_display_
                struct radeon_connector_atom_dig *dig_connector =
                        radeon_connector->con_priv;
                int dp_clock;
+
+               /* Assign mode clock for hdmi deep color max clock limit check */
+               radeon_connector->pixelclock_for_modeset = mode->clock;
                radeon_crtc->bpc = radeon_get_monitor_bpc(connector);
 
                switch (encoder_mode) {
@@ -1019,10 +1046,17 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
        struct radeon_encoder *radeon_encoder =
                to_radeon_encoder(radeon_crtc->encoder);
        u32 pll_clock = mode->clock;
+       u32 clock = mode->clock;
        u32 ref_div = 0, fb_div = 0, frac_fb_div = 0, post_div = 0;
        struct radeon_pll *pll;
        int encoder_mode = atombios_get_encoder_mode(radeon_crtc->encoder);
 
+       /* pass the actual clock to atombios_crtc_program_pll for DCE5,6 for HDMI */
+       if (ASIC_IS_DCE5(rdev) && !ASIC_IS_DCE8(rdev) &&
+           (encoder_mode == ATOM_ENCODER_MODE_HDMI) &&
+           (radeon_crtc->bpc > 8))
+               clock = radeon_crtc->adjusted_clock;
+
        switch (radeon_crtc->pll_id) {
        case ATOM_PPLL1:
                pll = &rdev->clock.p1pll;
@@ -1057,7 +1091,7 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
                                 radeon_crtc->crtc_id, &radeon_crtc->ss);
 
        atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id,
-                                 encoder_mode, radeon_encoder->encoder_id, mode->clock,
+                                 encoder_mode, radeon_encoder->encoder_id, clock,
                                  ref_div, fb_div, frac_fb_div, post_div,
                                  radeon_crtc->bpc, radeon_crtc->ss_enabled, &radeon_crtc->ss);
 
index 54e4f52549af47f19edf39340754a4e33a4da49c..c5b1f2da39544e6766ae1d70a3be0d296ebe6b97 100644 (file)
@@ -95,9 +95,12 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
        int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction);
        unsigned char *base;
        int recv_bytes;
+       int r = 0;
 
        memset(&args, 0, sizeof(args));
 
+       mutex_lock(&chan->mutex);
+
        base = (unsigned char *)(rdev->mode_info.atom_context->scratch + 1);
 
        radeon_atom_copy_swap(base, send, send_bytes, true);
@@ -117,19 +120,22 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
        /* timeout */
        if (args.v1.ucReplyStatus == 1) {
                DRM_DEBUG_KMS("dp_aux_ch timeout\n");
-               return -ETIMEDOUT;
+               r = -ETIMEDOUT;
+               goto done;
        }
 
        /* flags not zero */
        if (args.v1.ucReplyStatus == 2) {
                DRM_DEBUG_KMS("dp_aux_ch flags not zero\n");
-               return -EBUSY;
+               r = -EBUSY;
+               goto done;
        }
 
        /* error */
        if (args.v1.ucReplyStatus == 3) {
                DRM_DEBUG_KMS("dp_aux_ch error\n");
-               return -EIO;
+               r = -EIO;
+               goto done;
        }
 
        recv_bytes = args.v1.ucDataOutLen;
@@ -139,7 +145,11 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
        if (recv && recv_size)
                radeon_atom_copy_swap(recv, base + 16, recv_bytes, false);
 
-       return recv_bytes;
+       r = recv_bytes;
+done:
+       mutex_unlock(&chan->mutex);
+
+       return r;
 }
 
 #define BARE_ADDRESS_SIZE 3
@@ -212,11 +222,12 @@ void radeon_dp_aux_init(struct radeon_connector *radeon_connector)
        radeon_connector->ddc_bus->rec.hpd = radeon_connector->hpd.hpd;
        radeon_connector->ddc_bus->aux.dev = radeon_connector->base.kdev;
        radeon_connector->ddc_bus->aux.transfer = radeon_dp_aux_transfer;
-       ret = drm_dp_aux_register_i2c_bus(&radeon_connector->ddc_bus->aux);
+
+       ret = drm_dp_aux_register(&radeon_connector->ddc_bus->aux);
        if (!ret)
                radeon_connector->ddc_bus->has_aux = true;
 
-       WARN(ret, "drm_dp_aux_register_i2c_bus() failed with error %d\n", ret);
+       WARN(ret, "drm_dp_aux_register() failed with error %d\n", ret);
 }
 
 /***** general DP utility functions *****/
@@ -281,6 +292,19 @@ static int dp_get_max_dp_pix_clock(int link_rate,
 
 /***** radeon specific DP functions *****/
 
+static int radeon_dp_get_max_link_rate(struct drm_connector *connector,
+                                      u8 dpcd[DP_DPCD_SIZE])
+{
+       int max_link_rate;
+
+       if (radeon_connector_is_dp12_capable(connector))
+               max_link_rate = min(drm_dp_max_link_rate(dpcd), 540000);
+       else
+               max_link_rate = min(drm_dp_max_link_rate(dpcd), 270000);
+
+       return max_link_rate;
+}
+
 /* First get the min lane# when low rate is used according to pixel clock
  * (prefer low rate), second check max lane# supported by DP panel,
  * if the max lane# < low rate lane# then use max lane# instead.
@@ -290,7 +314,7 @@ static int radeon_dp_get_dp_lane_number(struct drm_connector *connector,
                                        int pix_clock)
 {
        int bpp = convert_bpc_to_bpp(radeon_get_monitor_bpc(connector));
-       int max_link_rate = drm_dp_max_link_rate(dpcd);
+       int max_link_rate = radeon_dp_get_max_link_rate(connector, dpcd);
        int max_lane_num = drm_dp_max_lane_count(dpcd);
        int lane_num;
        int max_dp_pix_clock;
@@ -328,7 +352,7 @@ static int radeon_dp_get_dp_link_clock(struct drm_connector *connector,
                        return 540000;
        }
 
-       return drm_dp_max_link_rate(dpcd);
+       return radeon_dp_get_max_link_rate(connector, dpcd);
 }
 
 static u8 radeon_dp_encoder_service(struct radeon_device *rdev,
index e6eb5097597f6088dde34638ee9b94cca5bc8950..2b2908440644e8908accbeabc855d5c5066c7d47 100644 (file)
@@ -1884,8 +1884,11 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder)
                                        args.v2.ucEncodeMode = ATOM_ENCODER_MODE_CRT;
                                else
                                        args.v2.ucEncodeMode = atombios_get_encoder_mode(encoder);
-                       } else
+                       } else if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
+                               args.v2.ucEncodeMode = ATOM_ENCODER_MODE_LVDS;
+                       } else {
                                args.v2.ucEncodeMode = atombios_get_encoder_mode(encoder);
+                       }
                        switch (radeon_encoder->encoder_id) {
                        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
                        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
index b5162c3b6111a1845834fe1b63d3e1f17642d501..9c570fb15b8c2e49a9765787d26a58a1467c8e0e 100644 (file)
@@ -43,15 +43,19 @@ static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan,
        int index = GetIndexIntoMasterTable(COMMAND, ProcessI2cChannelTransaction);
        unsigned char *base;
        u16 out = cpu_to_le16(0);
+       int r = 0;
 
        memset(&args, 0, sizeof(args));
 
+       mutex_lock(&chan->mutex);
+
        base = (unsigned char *)rdev->mode_info.atom_context->scratch;
 
        if (flags & HW_I2C_WRITE) {
                if (num > ATOM_MAX_HW_I2C_WRITE) {
                        DRM_ERROR("hw i2c: tried to write too many bytes (%d vs 3)\n", num);
-                       return -EINVAL;
+                       r = -EINVAL;
+                       goto done;
                }
                if (buf == NULL)
                        args.ucRegIndex = 0;
@@ -65,7 +69,8 @@ static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan,
        } else {
                if (num > ATOM_MAX_HW_I2C_READ) {
                        DRM_ERROR("hw i2c: tried to read too many bytes (%d vs 255)\n", num);
-                       return -EINVAL;
+                       r = -EINVAL;
+                       goto done;
                }
                args.ucRegIndex = 0;
                args.lpI2CDataOut = 0;
@@ -82,13 +87,17 @@ static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan,
        /* error */
        if (args.ucStatus != HW_ASSISTED_I2C_STATUS_SUCCESS) {
                DRM_DEBUG_KMS("hw_i2c error\n");
-               return -EIO;
+               r = -EIO;
+               goto done;
        }
 
        if (!(flags & HW_I2C_WRITE))
                radeon_atom_copy_swap(buf, base, num, false);
 
-       return 0;
+done:
+       mutex_unlock(&chan->mutex);
+
+       return r;
 }
 
 int radeon_atom_hw_i2c_xfer(struct i2c_adapter *i2c_adap,
index d2fd989680857d5a08a7d65d586bb0b4a40be6e2..dcd4518a9b087edbab07ab96f58d0d0658b5a23c 100644 (file)
@@ -80,6 +80,7 @@ extern int sumo_rlc_init(struct radeon_device *rdev);
 extern void si_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc);
 extern void si_rlc_reset(struct radeon_device *rdev);
 extern void si_init_uvd_internal_cg(struct radeon_device *rdev);
+static u32 cik_get_cu_active_bitmap(struct radeon_device *rdev, u32 se, u32 sh);
 extern int cik_sdma_resume(struct radeon_device *rdev);
 extern void cik_sdma_enable(struct radeon_device *rdev, bool enable);
 extern void cik_sdma_fini(struct radeon_device *rdev);
@@ -3257,7 +3258,7 @@ static void cik_gpu_init(struct radeon_device *rdev)
        u32 mc_shared_chmap, mc_arb_ramcfg;
        u32 hdp_host_path_cntl;
        u32 tmp;
-       int i, j;
+       int i, j, k;
 
        switch (rdev->family) {
        case CHIP_BONAIRE:
@@ -3446,6 +3447,15 @@ static void cik_gpu_init(struct radeon_device *rdev)
                     rdev->config.cik.max_sh_per_se,
                     rdev->config.cik.max_backends_per_se);
 
+       for (i = 0; i < rdev->config.cik.max_shader_engines; i++) {
+               for (j = 0; j < rdev->config.cik.max_sh_per_se; j++) {
+                       for (k = 0; k < rdev->config.cik.max_cu_per_sh; k++) {
+                               rdev->config.cik.active_cus +=
+                                       hweight32(cik_get_cu_active_bitmap(rdev, i, j));
+                       }
+               }
+       }
+
        /* set HW defaults for 3D engine */
        WREG32(CP_MEQ_THRESHOLDS, MEQ1_START(0x30) | MEQ2_START(0x60));
 
@@ -3698,7 +3708,7 @@ bool cik_semaphore_ring_emit(struct radeon_device *rdev,
        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, lower_32_bits(addr));
        radeon_ring_write(ring, (upper_32_bits(addr) & 0xffff) | sel);
 
        return true;
@@ -3818,7 +3828,7 @@ void cik_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib)
                        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, upper_32_bits(ring->next_rptr_gpu_addr));
                        radeon_ring_write(ring, next_rptr);
                }
 
@@ -5396,6 +5406,7 @@ static int cik_pcie_gart_enable(struct radeon_device *rdev)
        WREG32(MC_VM_MX_L1_TLB_CNTL,
               (0xA << 7) |
               ENABLE_L1_TLB |
+              ENABLE_L1_FRAGMENT_PROCESSING |
               SYSTEM_ACCESS_MODE_NOT_IN_SYS |
               ENABLE_ADVANCED_DRIVER_MODEL |
               SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU);
@@ -5408,7 +5419,8 @@ static int cik_pcie_gart_enable(struct radeon_device *rdev)
               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));
+              BANK_SELECT(4) |
+              L2_CACHE_BIGK_FRAGMENT_SIZE(4));
        /* 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);
@@ -5444,6 +5456,7 @@ static int cik_pcie_gart_enable(struct radeon_device *rdev)
               (u32)(rdev->dummy_page.addr >> 12));
        WREG32(VM_CONTEXT1_CNTL2, 4);
        WREG32(VM_CONTEXT1_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(1) |
+                               PAGE_TABLE_BLOCK_SIZE(radeon_vm_block_size - 9) |
                                RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT |
                                RANGE_PROTECTION_FAULT_ENABLE_DEFAULT |
                                DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT |
@@ -7450,7 +7463,7 @@ restart_ih:
                                                wake_up(&rdev->irq.vblank_queue);
                                        }
                                        if (atomic_read(&rdev->irq.pflip[0]))
-                                               radeon_crtc_handle_flip(rdev, 0);
+                                               radeon_crtc_handle_vblank(rdev, 0);
                                        rdev->irq.stat_regs.cik.disp_int &= ~LB_D1_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D1 vblank\n");
                                }
@@ -7476,7 +7489,7 @@ restart_ih:
                                                wake_up(&rdev->irq.vblank_queue);
                                        }
                                        if (atomic_read(&rdev->irq.pflip[1]))
-                                               radeon_crtc_handle_flip(rdev, 1);
+                                               radeon_crtc_handle_vblank(rdev, 1);
                                        rdev->irq.stat_regs.cik.disp_int_cont &= ~LB_D2_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D2 vblank\n");
                                }
@@ -7502,7 +7515,7 @@ restart_ih:
                                                wake_up(&rdev->irq.vblank_queue);
                                        }
                                        if (atomic_read(&rdev->irq.pflip[2]))
-                                               radeon_crtc_handle_flip(rdev, 2);
+                                               radeon_crtc_handle_vblank(rdev, 2);
                                        rdev->irq.stat_regs.cik.disp_int_cont2 &= ~LB_D3_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D3 vblank\n");
                                }
@@ -7528,7 +7541,7 @@ restart_ih:
                                                wake_up(&rdev->irq.vblank_queue);
                                        }
                                        if (atomic_read(&rdev->irq.pflip[3]))
-                                               radeon_crtc_handle_flip(rdev, 3);
+                                               radeon_crtc_handle_vblank(rdev, 3);
                                        rdev->irq.stat_regs.cik.disp_int_cont3 &= ~LB_D4_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D4 vblank\n");
                                }
@@ -7554,7 +7567,7 @@ restart_ih:
                                                wake_up(&rdev->irq.vblank_queue);
                                        }
                                        if (atomic_read(&rdev->irq.pflip[4]))
-                                               radeon_crtc_handle_flip(rdev, 4);
+                                               radeon_crtc_handle_vblank(rdev, 4);
                                        rdev->irq.stat_regs.cik.disp_int_cont4 &= ~LB_D5_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D5 vblank\n");
                                }
@@ -7580,7 +7593,7 @@ restart_ih:
                                                wake_up(&rdev->irq.vblank_queue);
                                        }
                                        if (atomic_read(&rdev->irq.pflip[5]))
-                                               radeon_crtc_handle_flip(rdev, 5);
+                                               radeon_crtc_handle_vblank(rdev, 5);
                                        rdev->irq.stat_regs.cik.disp_int_cont5 &= ~LB_D6_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D6 vblank\n");
                                }
index 72e464c79a88a777c27d2ada64667bf9e8798aa2..8e9d0f1d858ef56d4038ab1361f339a2ba593f8f 100644 (file)
@@ -141,7 +141,7 @@ void cik_sdma_ring_ib_execute(struct radeon_device *rdev,
                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, upper_32_bits(ring->next_rptr_gpu_addr));
                radeon_ring_write(ring, 1); /* number of DWs to follow */
                radeon_ring_write(ring, next_rptr);
        }
@@ -151,7 +151,7 @@ void cik_sdma_ring_ib_execute(struct radeon_device *rdev,
                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, upper_32_bits(ib->gpu_addr));
        radeon_ring_write(ring, ib->length_dw);
 
 }
@@ -203,8 +203,8 @@ void cik_sdma_fence_ring_emit(struct radeon_device *rdev,
 
        /* 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, lower_32_bits(addr));
+       radeon_ring_write(ring, upper_32_bits(addr));
        radeon_ring_write(ring, fence->seq);
        /* generate an interrupt */
        radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_TRAP, 0, 0));
@@ -233,7 +233,7 @@ bool cik_sdma_semaphore_ring_emit(struct radeon_device *rdev,
 
        radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SEMAPHORE, 0, extra_bits));
        radeon_ring_write(ring, addr & 0xfffffff8);
-       radeon_ring_write(ring, upper_32_bits(addr) & 0xffffffff);
+       radeon_ring_write(ring, upper_32_bits(addr));
 
        return true;
 }
@@ -551,10 +551,10 @@ int cik_copy_dma(struct radeon_device *rdev,
                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 & 0xffffffff);
-               radeon_ring_write(ring, upper_32_bits(dst_offset) & 0xffffffff);
+               radeon_ring_write(ring, lower_32_bits(src_offset));
+               radeon_ring_write(ring, upper_32_bits(src_offset));
+               radeon_ring_write(ring, lower_32_bits(dst_offset));
+               radeon_ring_write(ring, upper_32_bits(dst_offset));
                src_offset += cur_size_in_bytes;
                dst_offset += cur_size_in_bytes;
        }
@@ -605,7 +605,7 @@ int cik_sdma_ring_test(struct radeon_device *rdev,
        }
        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, upper_32_bits(rdev->vram_scratch.gpu_addr));
        radeon_ring_write(ring, 1); /* number of DWs to follow */
        radeon_ring_write(ring, 0xDEADBEEF);
        radeon_ring_unlock_commit(rdev, ring);
@@ -660,7 +660,7 @@ int cik_sdma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
 
        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[2] = upper_32_bits(rdev->vram_scratch.gpu_addr);
        ib.ptr[3] = 1;
        ib.ptr[4] = 0xDEADBEEF;
        ib.length_dw = 5;
@@ -742,7 +742,26 @@ void cik_sdma_vm_set_page(struct radeon_device *rdev,
 
        trace_radeon_vm_set_page(pe, addr, count, incr, flags);
 
-       if (flags & R600_PTE_SYSTEM) {
+       if (flags == R600_PTE_GART) {
+               uint64_t src = rdev->gart.table_addr + (addr >> 12) * 8;
+               while (count) {
+                       unsigned bytes = count * 8;
+                       if (bytes > 0x1FFFF8)
+                               bytes = 0x1FFFF8;
+
+                       ib->ptr[ib->length_dw++] = SDMA_PACKET(SDMA_OPCODE_COPY, SDMA_WRITE_SUB_OPCODE_LINEAR, 0);
+                       ib->ptr[ib->length_dw++] = bytes;
+                       ib->ptr[ib->length_dw++] = 0; /* src/dst endian swap */
+                       ib->ptr[ib->length_dw++] = lower_32_bits(src);
+                       ib->ptr[ib->length_dw++] = upper_32_bits(src);
+                       ib->ptr[ib->length_dw++] = lower_32_bits(pe);
+                       ib->ptr[ib->length_dw++] = upper_32_bits(pe);
+
+                       pe += bytes;
+                       src += bytes;
+                       count -= bytes / 8;
+               }
+       } else if (flags & R600_PTE_SYSTEM) {
                while (count) {
                        ndw = count * 2;
                        if (ndw > 0xFFFFE)
index dd7926394a8fdaf6821fdd10151f98639c7c082f..ae88660f34ea5750a1729f34cc304ba946a92f10 100644 (file)
 #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                PAGE_TABLE_BLOCK_SIZE(x)                        (((x) & 0xF) << 24)
 #define VM_CONTEXT1_CNTL                               0x1414
 #define VM_CONTEXT0_CNTL2                              0x1430
 #define VM_CONTEXT1_CNTL2                              0x1434
index aa908c55a513a79d7aac941c01e8ef42f999cf3f..e48a14037b76d1ae94ec458fa3900de0b080cf08 100644 (file)
@@ -1050,7 +1050,7 @@ static const struct cs_extent_def SECT_CONTEXT_defs[] =
     {SECT_CONTEXT_def_5, 0x0000a29e, 5 },
     {SECT_CONTEXT_def_6, 0x0000a2a5, 56 },
     {SECT_CONTEXT_def_7, 0x0000a2de, 290 },
-    { 0, 0, 0 }
+    { NULL, 0, 0 }
 };
 static const u32 SECT_CLEAR_def_1[] =
 {
@@ -1061,7 +1061,7 @@ static const u32 SECT_CLEAR_def_1[] =
 static const struct cs_extent_def SECT_CLEAR_defs[] =
 {
     {SECT_CLEAR_def_1, 0x0000ffc0, 3 },
-    { 0, 0, 0 }
+    { NULL, 0, 0 }
 };
 static const u32 SECT_CTRLCONST_def_1[] =
 {
@@ -1071,11 +1071,11 @@ static const u32 SECT_CTRLCONST_def_1[] =
 static const struct cs_extent_def SECT_CTRLCONST_defs[] =
 {
     {SECT_CTRLCONST_def_1, 0x0000f3fc, 2 },
-    { 0, 0, 0 }
+    { NULL, 0, 0 }
 };
 static const 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 }
+    { NULL, SECT_NONE }
 };
index c3982f9475fb57a4d7d708e925e7390db88bd58b..f55d06664e310c0251021993899400c50d45dc37 100644 (file)
@@ -936,9 +936,9 @@ static const struct cs_extent_def ci_SECT_CONTEXT_defs[] =
     {ci_SECT_CONTEXT_def_5, 0x0000a2a0, 2 },
     {ci_SECT_CONTEXT_def_6, 0x0000a2a3, 1 },
     {ci_SECT_CONTEXT_def_7, 0x0000a2a5, 233 },
-    { 0, 0, 0 }
+    { NULL, 0, 0 }
 };
 static const struct cs_section_def ci_cs_data[] = {
     { ci_SECT_CONTEXT_defs, SECT_CONTEXT },
-    { 0, SECT_NONE }
+    { NULL, SECT_NONE }
 };
index b994cb2a35a0ca7a2bee54de451293525f72ebf6..66e39cdb5cb0dc5ff1fbac239c6895b825a443ed 100644 (file)
@@ -933,9 +933,9 @@ static const struct cs_extent_def si_SECT_CONTEXT_defs[] =
     {si_SECT_CONTEXT_def_5, 0x0000a2a1, 1 },
     {si_SECT_CONTEXT_def_6, 0x0000a2a3, 1 },
     {si_SECT_CONTEXT_def_7, 0x0000a2a5, 233 },
-    { 0, 0, 0 }
+    { NULL, 0, 0 }
 };
 static const struct cs_section_def si_cs_data[] = {
     { si_SECT_CONTEXT_defs, SECT_CONTEXT },
-    { 0, SECT_NONE }
+    { NULL, SECT_NONE }
 };
diff --git a/drivers/gpu/drm/radeon/dce3_1_afmt.c b/drivers/gpu/drm/radeon/dce3_1_afmt.c
new file mode 100644 (file)
index 0000000..51800e3
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ * Copyright 2014 Rafał Miłecki
+ *
+ * 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 <linux/hdmi.h>
+#include <drm/drmP.h>
+#include "radeon.h"
+#include "radeon_asic.h"
+#include "r600d.h"
+
+static void dce3_2_afmt_write_speaker_allocation(struct drm_encoder *encoder)
+{
+       struct radeon_device *rdev = encoder->dev->dev_private;
+       struct drm_connector *connector;
+       struct radeon_connector *radeon_connector = NULL;
+       u32 tmp;
+       u8 *sadb;
+       int sad_count;
+
+       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
+               if (connector->encoder == encoder) {
+                       radeon_connector = to_radeon_connector(connector);
+                       break;
+               }
+       }
+
+       if (!radeon_connector) {
+               DRM_ERROR("Couldn't find encoder's connector\n");
+               return;
+       }
+
+       sad_count = drm_edid_to_speaker_allocation(radeon_connector->edid, &sadb);
+       if (sad_count < 0) {
+               DRM_ERROR("Couldn't read Speaker Allocation Data Block: %d\n", sad_count);
+               return;
+       }
+
+       /* program the speaker allocation */
+       tmp = RREG32(AZ_F0_CODEC_PIN0_CONTROL_CHANNEL_SPEAKER);
+       tmp &= ~(DP_CONNECTION | SPEAKER_ALLOCATION_MASK);
+       /* set HDMI mode */
+       tmp |= HDMI_CONNECTION;
+       if (sad_count)
+               tmp |= SPEAKER_ALLOCATION(sadb[0]);
+       else
+               tmp |= SPEAKER_ALLOCATION(5); /* stereo */
+       WREG32(AZ_F0_CODEC_PIN0_CONTROL_CHANNEL_SPEAKER, tmp);
+
+       kfree(sadb);
+}
+
+static void dce3_2_afmt_write_sad_regs(struct drm_encoder *encoder)
+{
+       struct radeon_device *rdev = encoder->dev->dev_private;
+       struct drm_connector *connector;
+       struct radeon_connector *radeon_connector = NULL;
+       struct cea_sad *sads;
+       int i, sad_count;
+
+       static const u16 eld_reg_to_type[][2] = {
+               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR0, HDMI_AUDIO_CODING_TYPE_PCM },
+               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR1, HDMI_AUDIO_CODING_TYPE_AC3 },
+               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR2, HDMI_AUDIO_CODING_TYPE_MPEG1 },
+               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR3, HDMI_AUDIO_CODING_TYPE_MP3 },
+               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR4, HDMI_AUDIO_CODING_TYPE_MPEG2 },
+               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR5, HDMI_AUDIO_CODING_TYPE_AAC_LC },
+               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR6, HDMI_AUDIO_CODING_TYPE_DTS },
+               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR7, HDMI_AUDIO_CODING_TYPE_ATRAC },
+               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR9, HDMI_AUDIO_CODING_TYPE_EAC3 },
+               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR10, HDMI_AUDIO_CODING_TYPE_DTS_HD },
+               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR11, HDMI_AUDIO_CODING_TYPE_MLP },
+               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR13, HDMI_AUDIO_CODING_TYPE_WMA_PRO },
+       };
+
+       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
+               if (connector->encoder == encoder) {
+                       radeon_connector = to_radeon_connector(connector);
+                       break;
+               }
+       }
+
+       if (!radeon_connector) {
+               DRM_ERROR("Couldn't find encoder's connector\n");
+               return;
+       }
+
+       sad_count = drm_edid_to_sad(radeon_connector->edid, &sads);
+       if (sad_count < 0) {
+               DRM_ERROR("Couldn't read SADs: %d\n", sad_count);
+               return;
+       }
+       BUG_ON(!sads);
+
+       for (i = 0; i < ARRAY_SIZE(eld_reg_to_type); i++) {
+               u32 value = 0;
+               u8 stereo_freqs = 0;
+               int max_channels = -1;
+               int j;
+
+               for (j = 0; j < sad_count; j++) {
+                       struct cea_sad *sad = &sads[j];
+
+                       if (sad->format == eld_reg_to_type[i][1]) {
+                               if (sad->channels > max_channels) {
+                                       value = MAX_CHANNELS(sad->channels) |
+                                               DESCRIPTOR_BYTE_2(sad->byte2) |
+                                               SUPPORTED_FREQUENCIES(sad->freq);
+                                       max_channels = sad->channels;
+                               }
+
+                               if (sad->format == HDMI_AUDIO_CODING_TYPE_PCM)
+                                       stereo_freqs |= sad->freq;
+                               else
+                                       break;
+                       }
+               }
+
+               value |= SUPPORTED_FREQUENCIES_STEREO(stereo_freqs);
+
+               WREG32(eld_reg_to_type[i][0], value);
+       }
+
+       kfree(sads);
+}
+
+/*
+ * update the info frames with the data from the current display mode
+ */
+void dce3_1_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode)
+{
+       struct drm_device *dev = encoder->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+       struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
+       u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE];
+       struct hdmi_avi_infoframe frame;
+       uint32_t offset;
+       ssize_t err;
+
+       if (!dig || !dig->afmt)
+               return;
+
+       /* Silent, r600_hdmi_enable will raise WARN for us */
+       if (!dig->afmt->enabled)
+               return;
+       offset = dig->afmt->offset;
+
+       /* disable audio prior to setting up hw */
+       dig->afmt->pin = r600_audio_get_pin(rdev);
+       r600_audio_enable(rdev, dig->afmt->pin, false);
+
+       r600_audio_set_dto(encoder, mode->clock);
+
+       WREG32(HDMI0_VBI_PACKET_CONTROL + offset,
+              HDMI0_NULL_SEND); /* send null packets when required */
+
+       WREG32(HDMI0_AUDIO_CRC_CONTROL + offset, 0x1000);
+
+       if (ASIC_IS_DCE32(rdev)) {
+               WREG32(HDMI0_AUDIO_PACKET_CONTROL + offset,
+                      HDMI0_AUDIO_DELAY_EN(1) | /* default audio delay */
+                      HDMI0_AUDIO_PACKETS_PER_LINE(3)); /* should be suffient for all audio modes and small enough for all hblanks */
+               WREG32(AFMT_AUDIO_PACKET_CONTROL + offset,
+                      AFMT_AUDIO_SAMPLE_SEND | /* send audio packets */
+                      AFMT_60958_CS_UPDATE); /* allow 60958 channel status fields to be updated */
+       } else {
+               WREG32(HDMI0_AUDIO_PACKET_CONTROL + offset,
+                      HDMI0_AUDIO_SAMPLE_SEND | /* send audio packets */
+                      HDMI0_AUDIO_DELAY_EN(1) | /* default audio delay */
+                      HDMI0_AUDIO_PACKETS_PER_LINE(3) | /* should be suffient for all audio modes and small enough for all hblanks */
+                      HDMI0_60958_CS_UPDATE); /* allow 60958 channel status fields to be updated */
+       }
+
+       if (ASIC_IS_DCE32(rdev)) {
+               dce3_2_afmt_write_speaker_allocation(encoder);
+               dce3_2_afmt_write_sad_regs(encoder);
+       }
+
+       WREG32(HDMI0_ACR_PACKET_CONTROL + offset,
+              HDMI0_ACR_SOURCE | /* select SW CTS value - XXX verify that hw CTS works on all families */
+              HDMI0_ACR_AUTO_SEND); /* allow hw to sent ACR packets when required */
+
+       WREG32(HDMI0_VBI_PACKET_CONTROL + offset,
+              HDMI0_NULL_SEND | /* send null packets when required */
+              HDMI0_GC_SEND | /* send general control packets */
+              HDMI0_GC_CONT); /* send general control packets every frame */
+
+       /* TODO: HDMI0_AUDIO_INFO_UPDATE */
+       WREG32(HDMI0_INFOFRAME_CONTROL0 + offset,
+              HDMI0_AVI_INFO_SEND | /* enable AVI info frames */
+              HDMI0_AVI_INFO_CONT | /* send AVI info frames every frame/field */
+              HDMI0_AUDIO_INFO_SEND | /* enable audio info frames (frames won't be set until audio is enabled) */
+              HDMI0_AUDIO_INFO_CONT); /* send audio info frames every frame/field */
+
+       WREG32(HDMI0_INFOFRAME_CONTROL1 + offset,
+              HDMI0_AVI_INFO_LINE(2) | /* anything other than 0 */
+              HDMI0_AUDIO_INFO_LINE(2)); /* anything other than 0 */
+
+       WREG32(HDMI0_GC + offset, 0); /* unset HDMI0_GC_AVMUTE */
+
+       err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
+       if (err < 0) {
+               DRM_ERROR("failed to setup AVI infoframe: %zd\n", err);
+               return;
+       }
+
+       err = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer));
+       if (err < 0) {
+               DRM_ERROR("failed to pack AVI infoframe: %zd\n", err);
+               return;
+       }
+
+       r600_hdmi_update_avi_infoframe(encoder, buffer, sizeof(buffer));
+       r600_hdmi_update_ACR(encoder, mode->clock);
+
+       /* it's unknown what these bits do excatly, but it's indeed quite useful for debugging */
+       WREG32(HDMI0_RAMP_CONTROL0 + offset, 0x00FFFFFF);
+       WREG32(HDMI0_RAMP_CONTROL1 + offset, 0x007FFFFF);
+       WREG32(HDMI0_RAMP_CONTROL2 + offset, 0x00000001);
+       WREG32(HDMI0_RAMP_CONTROL3 + offset, 0x00000001);
+
+       r600_hdmi_audio_workaround(encoder);
+
+       /* enable audio after to setting up hw */
+       r600_audio_enable(rdev, dig->afmt->pin, true);
+}
index 0f7a51a3694f0fff5bde09c7a4bf57dd7bd51963..e2f605224e8c701e9b0eceb138ee77da443f51ee 100644 (file)
@@ -1300,36 +1300,6 @@ void dce4_wait_for_vblank(struct radeon_device *rdev, int crtc)
        }
 }
 
-/**
- * radeon_irq_kms_pflip_irq_get - pre-pageflip callback.
- *
- * @rdev: radeon_device pointer
- * @crtc: crtc to prepare for pageflip on
- *
- * Pre-pageflip callback (evergreen+).
- * Enables the pageflip irq (vblank irq).
- */
-void evergreen_pre_page_flip(struct radeon_device *rdev, int crtc)
-{
-       /* enable the pflip int */
-       radeon_irq_kms_pflip_irq_get(rdev, crtc);
-}
-
-/**
- * evergreen_post_page_flip - pos-pageflip callback.
- *
- * @rdev: radeon_device pointer
- * @crtc: crtc to cleanup pageflip on
- *
- * Post-pageflip callback (evergreen+).
- * Disables the pageflip irq (vblank irq).
- */
-void evergreen_post_page_flip(struct radeon_device *rdev, int crtc)
-{
-       /* disable the pflip int */
-       radeon_irq_kms_pflip_irq_put(rdev, crtc);
-}
-
 /**
  * evergreen_page_flip - pageflip callback.
  *
@@ -1343,7 +1313,7 @@ void evergreen_post_page_flip(struct radeon_device *rdev, int crtc)
  * double buffered update to take place.
  * Returns the current update pending status.
  */
-u32 evergreen_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
+void evergreen_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
 {
        struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
        u32 tmp = RREG32(EVERGREEN_GRPH_UPDATE + radeon_crtc->crtc_offset);
@@ -1375,9 +1345,23 @@ u32 evergreen_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
        /* Unlock the lock, so double-buffering can take place inside vblank */
        tmp &= ~EVERGREEN_GRPH_UPDATE_LOCK;
        WREG32(EVERGREEN_GRPH_UPDATE + radeon_crtc->crtc_offset, tmp);
+}
+
+/**
+ * evergreen_page_flip_pending - check if page flip is still pending
+ *
+ * @rdev: radeon_device pointer
+ * @crtc_id: crtc to check
+ *
+ * Returns the current update pending status.
+ */
+bool evergreen_page_flip_pending(struct radeon_device *rdev, int crtc_id)
+{
+       struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
 
        /* Return current update_pending status: */
-       return RREG32(EVERGREEN_GRPH_UPDATE + radeon_crtc->crtc_offset) & EVERGREEN_GRPH_SURFACE_UPDATE_PENDING;
+       return !!(RREG32(EVERGREEN_GRPH_UPDATE + radeon_crtc->crtc_offset) &
+               EVERGREEN_GRPH_SURFACE_UPDATE_PENDING);
 }
 
 /* get temperature in millidegrees */
@@ -3353,6 +3337,18 @@ static void evergreen_gpu_init(struct radeon_device *rdev)
                        disabled_rb_mask &= ~(1 << i);
        }
 
+       for (i = 0; i < rdev->config.evergreen.num_ses; i++) {
+               u32 simd_disable_bitmap;
+
+               WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i));
+               WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i));
+               simd_disable_bitmap = (RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0xffff0000) >> 16;
+               simd_disable_bitmap |= 0xffffffff << rdev->config.evergreen.max_simds;
+               tmp <<= 16;
+               tmp |= simd_disable_bitmap;
+       }
+       rdev->config.evergreen.active_simds = hweight32(~tmp);
+
        WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES);
        WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES);
 
@@ -4810,7 +4806,7 @@ restart_ih:
                                                wake_up(&rdev->irq.vblank_queue);
                                        }
                                        if (atomic_read(&rdev->irq.pflip[0]))
-                                               radeon_crtc_handle_flip(rdev, 0);
+                                               radeon_crtc_handle_vblank(rdev, 0);
                                        rdev->irq.stat_regs.evergreen.disp_int &= ~LB_D1_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D1 vblank\n");
                                }
@@ -4836,7 +4832,7 @@ restart_ih:
                                                wake_up(&rdev->irq.vblank_queue);
                                        }
                                        if (atomic_read(&rdev->irq.pflip[1]))
-                                               radeon_crtc_handle_flip(rdev, 1);
+                                               radeon_crtc_handle_vblank(rdev, 1);
                                        rdev->irq.stat_regs.evergreen.disp_int_cont &= ~LB_D2_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D2 vblank\n");
                                }
@@ -4862,7 +4858,7 @@ restart_ih:
                                                wake_up(&rdev->irq.vblank_queue);
                                        }
                                        if (atomic_read(&rdev->irq.pflip[2]))
-                                               radeon_crtc_handle_flip(rdev, 2);
+                                               radeon_crtc_handle_vblank(rdev, 2);
                                        rdev->irq.stat_regs.evergreen.disp_int_cont2 &= ~LB_D3_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D3 vblank\n");
                                }
@@ -4888,7 +4884,7 @@ restart_ih:
                                                wake_up(&rdev->irq.vblank_queue);
                                        }
                                        if (atomic_read(&rdev->irq.pflip[3]))
-                                               radeon_crtc_handle_flip(rdev, 3);
+                                               radeon_crtc_handle_vblank(rdev, 3);
                                        rdev->irq.stat_regs.evergreen.disp_int_cont3 &= ~LB_D4_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D4 vblank\n");
                                }
@@ -4914,7 +4910,7 @@ restart_ih:
                                                wake_up(&rdev->irq.vblank_queue);
                                        }
                                        if (atomic_read(&rdev->irq.pflip[4]))
-                                               radeon_crtc_handle_flip(rdev, 4);
+                                               radeon_crtc_handle_vblank(rdev, 4);
                                        rdev->irq.stat_regs.evergreen.disp_int_cont4 &= ~LB_D5_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D5 vblank\n");
                                }
@@ -4940,7 +4936,7 @@ restart_ih:
                                                wake_up(&rdev->irq.vblank_queue);
                                        }
                                        if (atomic_read(&rdev->irq.pflip[5]))
-                                               radeon_crtc_handle_flip(rdev, 5);
+                                               radeon_crtc_handle_vblank(rdev, 5);
                                        rdev->irq.stat_regs.evergreen.disp_int_cont5 &= ~LB_D6_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D6 vblank\n");
                                }
index 05b0c95813fd79ff4c31ade3a90c0ad1880d028d..1ec0e6e83f9f3492151d097c839251029996d6f6 100644 (file)
@@ -293,10 +293,13 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode
        struct radeon_device *rdev = dev->dev_private;
        struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
        struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
+       struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
        u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE];
        struct hdmi_avi_infoframe frame;
        uint32_t offset;
        ssize_t err;
+       uint32_t val;
+       int bpc = 8;
 
        if (!dig || !dig->afmt)
                return;
@@ -306,6 +309,12 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode
                return;
        offset = dig->afmt->offset;
 
+       /* hdmi deep color mode general control packets setup, if bpc > 8 */
+       if (encoder->crtc) {
+               struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
+               bpc = radeon_crtc->bpc;
+       }
+
        /* disable audio prior to setting up hw */
        if (ASIC_IS_DCE6(rdev)) {
                dig->afmt->pin = dce6_audio_get_pin(rdev);
@@ -322,6 +331,35 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode
 
        WREG32(AFMT_AUDIO_CRC_CONTROL + offset, 0x1000);
 
+       val = RREG32(HDMI_CONTROL + offset);
+       val &= ~HDMI_DEEP_COLOR_ENABLE;
+       val &= ~HDMI_DEEP_COLOR_DEPTH_MASK;
+
+       switch (bpc) {
+               case 0:
+               case 6:
+               case 8:
+               case 16:
+               default:
+                       DRM_DEBUG("%s: Disabling hdmi deep color for %d bpc.\n",
+                                        connector->name, bpc);
+                       break;
+               case 10:
+                       val |= HDMI_DEEP_COLOR_ENABLE;
+                       val |= HDMI_DEEP_COLOR_DEPTH(HDMI_30BIT_DEEP_COLOR);
+                       DRM_DEBUG("%s: Enabling hdmi deep color 30 for 10 bpc.\n",
+                                        connector->name);
+                       break;
+               case 12:
+                       val |= HDMI_DEEP_COLOR_ENABLE;
+                       val |= HDMI_DEEP_COLOR_DEPTH(HDMI_36BIT_DEEP_COLOR);
+                       DRM_DEBUG("%s: Enabling hdmi deep color 36 for 12 bpc.\n",
+                                        connector->name);
+                       break;
+       }
+
+       WREG32(HDMI_CONTROL + offset, val);
+
        WREG32(HDMI_VBI_PACKET_CONTROL + offset,
               HDMI_NULL_SEND | /* send null packets when required */
               HDMI_GC_SEND | /* send general control packets */
@@ -348,9 +386,13 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode
 
        /* fglrx clears sth in AFMT_AUDIO_PACKET_CONTROL2 here */
 
-       WREG32(HDMI_ACR_PACKET_CONTROL + offset,
-              HDMI_ACR_SOURCE | /* select SW CTS value */
-              HDMI_ACR_AUTO_SEND); /* allow hw to sent ACR packets when required */
+       if (bpc > 8)
+               WREG32(HDMI_ACR_PACKET_CONTROL + offset,
+                      HDMI_ACR_AUTO_SEND); /* allow hw to sent ACR packets when required */
+       else
+               WREG32(HDMI_ACR_PACKET_CONTROL + offset,
+                      HDMI_ACR_SOURCE | /* select SW CTS value */
+                      HDMI_ACR_AUTO_SEND); /* allow hw to sent ACR packets when required */
 
        evergreen_hdmi_update_ACR(encoder, mode->clock);
 
index f9c7963b3ee6b38730cdf6d422c2b612f0d1c904..b066d6711b8df54c698650b159b49be26fd31fd7 100644 (file)
 #       define HDMI_ERROR_ACK                (1 << 8)
 #       define HDMI_ERROR_MASK               (1 << 9)
 #       define HDMI_DEEP_COLOR_ENABLE        (1 << 24)
-#       define HDMI_DEEP_COLOR_DEPTH         (((x) & 3) << 28)
+#       define HDMI_DEEP_COLOR_DEPTH(x)      (((x) & 3) << 28)
 #       define HDMI_24BIT_DEEP_COLOR         0
 #       define HDMI_30BIT_DEEP_COLOR         1
 #       define HDMI_36BIT_DEEP_COLOR         2
+#       define HDMI_DEEP_COLOR_DEPTH_MASK    (3 << 28)
 #define HDMI_STATUS                          0x7034
 #       define HDMI_ACTIVE_AVMUTE            (1 << 0)
 #       define HDMI_AUDIO_PACKET_ERROR       (1 << 16)
index d246e043421a0007b2427bc0513a8ccfe4a18878..5a33ca6818677bd0e5fcc5c9564d2f789a5ae454 100644 (file)
@@ -1057,6 +1057,18 @@ static void cayman_gpu_init(struct radeon_device *rdev)
                        disabled_rb_mask &= ~(1 << i);
        }
 
+       for (i = 0; i < rdev->config.cayman.max_shader_engines; i++) {
+               u32 simd_disable_bitmap;
+
+               WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i));
+               WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i));
+               simd_disable_bitmap = (RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0xffff0000) >> 16;
+               simd_disable_bitmap |= 0xffffffff << rdev->config.cayman.max_simds_per_se;
+               tmp <<= 16;
+               tmp |= simd_disable_bitmap;
+       }
+       rdev->config.cayman.active_simds = hweight32(~tmp);
+
        WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES);
        WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES);
 
@@ -1228,12 +1240,14 @@ static int cayman_pcie_gart_enable(struct radeon_device *rdev)
               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 |
+              BANK_SELECT(6) |
               L2_CACHE_BIGK_FRAGMENT_SIZE(6));
        /* setup context0 */
        WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR, rdev->mc.gtt_start >> 12);
@@ -1266,6 +1280,7 @@ static int cayman_pcie_gart_enable(struct radeon_device *rdev)
               (u32)(rdev->dummy_page.addr >> 12));
        WREG32(VM_CONTEXT1_CNTL2, 4);
        WREG32(VM_CONTEXT1_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(1) |
+                               PAGE_TABLE_BLOCK_SIZE(radeon_vm_block_size - 9) |
                                RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT |
                                RANGE_PROTECTION_FAULT_ENABLE_DEFAULT |
                                DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT |
@@ -1343,7 +1358,7 @@ void cayman_fence_ring_emit(struct radeon_device *rdev,
        /* EVENT_WRITE_EOP - flush caches, send int */
        radeon_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4));
        radeon_ring_write(ring, EVENT_TYPE(CACHE_FLUSH_AND_INV_EVENT_TS) | EVENT_INDEX(5));
-       radeon_ring_write(ring, addr & 0xffffffff);
+       radeon_ring_write(ring, lower_32_bits(addr));
        radeon_ring_write(ring, (upper_32_bits(addr) & 0xff) | DATA_SEL(1) | INT_SEL(2));
        radeon_ring_write(ring, fence->seq);
        radeon_ring_write(ring, 0);
index d996033c243ee14f4509220a707edf50f9dbe489..2e12e4d69253fde453c3fb648566c0224bfe06bc 100644 (file)
 #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                PAGE_TABLE_BLOCK_SIZE(x)                        (((x) & 0xF) << 24)
 #define VM_CONTEXT1_CNTL                               0x1414
 #define VM_CONTEXT0_CNTL2                              0x1430
 #define VM_CONTEXT1_CNTL2                              0x1434
index b6c32640df20bed4e954ec5ee2b12a4e43053251..1544efcf1c3a655d04b197302737a820b07d1790 100644 (file)
@@ -141,36 +141,6 @@ void r100_wait_for_vblank(struct radeon_device *rdev, int crtc)
        }
 }
 
-/**
- * r100_pre_page_flip - pre-pageflip callback.
- *
- * @rdev: radeon_device pointer
- * @crtc: crtc to prepare for pageflip on
- *
- * Pre-pageflip callback (r1xx-r4xx).
- * Enables the pageflip irq (vblank irq).
- */
-void r100_pre_page_flip(struct radeon_device *rdev, int crtc)
-{
-       /* enable the pflip int */
-       radeon_irq_kms_pflip_irq_get(rdev, crtc);
-}
-
-/**
- * r100_post_page_flip - pos-pageflip callback.
- *
- * @rdev: radeon_device pointer
- * @crtc: crtc to cleanup pageflip on
- *
- * Post-pageflip callback (r1xx-r4xx).
- * Disables the pageflip irq (vblank irq).
- */
-void r100_post_page_flip(struct radeon_device *rdev, int crtc)
-{
-       /* disable the pflip int */
-       radeon_irq_kms_pflip_irq_put(rdev, crtc);
-}
-
 /**
  * r100_page_flip - pageflip callback.
  *
@@ -182,9 +152,8 @@ void r100_post_page_flip(struct radeon_device *rdev, int crtc)
  * During vblank we take the crtc lock and wait for the update_pending
  * bit to go high, when it does, we release the lock, and allow the
  * double buffered update to take place.
- * Returns the current update pending status.
  */
-u32 r100_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
+void r100_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
 {
        struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
        u32 tmp = ((u32)crtc_base) | RADEON_CRTC_OFFSET__OFFSET_LOCK;
@@ -206,8 +175,24 @@ u32 r100_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
        tmp &= ~RADEON_CRTC_OFFSET__OFFSET_LOCK;
        WREG32(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset, tmp);
 
+}
+
+/**
+ * r100_page_flip_pending - check if page flip is still pending
+ *
+ * @rdev: radeon_device pointer
+ * @crtc_id: crtc to check
+ *
+ * Check if the last pagefilp is still pending (r1xx-r4xx).
+ * Returns the current update pending status.
+ */
+bool r100_page_flip_pending(struct radeon_device *rdev, int crtc_id)
+{
+       struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
+
        /* Return current update_pending status: */
-       return RREG32(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset) & RADEON_CRTC_OFFSET__GUI_TRIG_OFFSET;
+       return !!(RREG32(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset) &
+               RADEON_CRTC_OFFSET__GUI_TRIG_OFFSET);
 }
 
 /**
@@ -697,15 +682,11 @@ void r100_pci_gart_disable(struct radeon_device *rdev)
        WREG32(RADEON_AIC_HI_ADDR, 0);
 }
 
-int r100_pci_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr)
+void r100_pci_gart_set_page(struct radeon_device *rdev, unsigned i,
+                           uint64_t addr)
 {
        u32 *gtt = rdev->gart.ptr;
-
-       if (i < 0 || i > rdev->gart.num_gpu_pages) {
-               return -EINVAL;
-       }
        gtt[i] = cpu_to_le32(lower_32_bits(addr));
-       return 0;
 }
 
 void r100_pci_gart_fini(struct radeon_device *rdev)
@@ -794,7 +775,7 @@ int r100_irq_process(struct radeon_device *rdev)
                                wake_up(&rdev->irq.vblank_queue);
                        }
                        if (atomic_read(&rdev->irq.pflip[0]))
-                               radeon_crtc_handle_flip(rdev, 0);
+                               radeon_crtc_handle_vblank(rdev, 0);
                }
                if (status & RADEON_CRTC2_VBLANK_STAT) {
                        if (rdev->irq.crtc_vblank_int[1]) {
@@ -803,7 +784,7 @@ int r100_irq_process(struct radeon_device *rdev)
                                wake_up(&rdev->irq.vblank_queue);
                        }
                        if (atomic_read(&rdev->irq.pflip[1]))
-                               radeon_crtc_handle_flip(rdev, 1);
+                               radeon_crtc_handle_vblank(rdev, 1);
                }
                if (status & RADEON_FP_DETECT_STAT) {
                        queue_hotplug = true;
index 206caf9700b7fd8d13a2d7a4b9f52a1801dc79a9..3c21d77a483d3a604c6ad6392ab1370377e78423 100644 (file)
@@ -72,13 +72,11 @@ void rv370_pcie_gart_tlb_flush(struct radeon_device *rdev)
 #define R300_PTE_WRITEABLE (1 << 2)
 #define R300_PTE_READABLE  (1 << 3)
 
-int rv370_pcie_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr)
+void rv370_pcie_gart_set_page(struct radeon_device *rdev, unsigned i,
+                             uint64_t addr)
 {
        void __iomem *ptr = rdev->gart.ptr;
 
-       if (i < 0 || i > rdev->gart.num_gpu_pages) {
-               return -EINVAL;
-       }
        addr = (lower_32_bits(addr) >> 8) |
               ((upper_32_bits(addr) & 0xff) << 24) |
               R300_PTE_WRITEABLE | R300_PTE_READABLE;
@@ -86,7 +84,6 @@ int rv370_pcie_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr)
         * on powerpc without HW swappers, it'll get swapped on way
         * into VRAM - so no need for cpu_to_le32 on VRAM tables */
        writel(addr, ((void __iomem *)ptr) + (i * 4));
-       return 0;
 }
 
 int rv370_pcie_gart_init(struct radeon_device *rdev)
index bbc189fd3ddc47f57993689cac1dd08f2b5a9c69..c66952d4b00cc5d706a13650fe2d82ef71389f1d 100644 (file)
@@ -1958,6 +1958,9 @@ static void r600_gpu_init(struct radeon_device *rdev)
        if (tmp < rdev->config.r600.max_simds) {
                rdev->config.r600.max_simds = tmp;
        }
+       tmp = rdev->config.r600.max_simds -
+               r600_count_pipe_bits((cc_gc_shader_pipe_config >> 16) & R6XX_MAX_SIMDS_MASK);
+       rdev->config.r600.active_simds = tmp;
 
        disabled_rb_mask = (RREG32(CC_RB_BACKEND_DISABLE) >> 16) & R6XX_MAX_BACKENDS_MASK;
        tmp = (tiling_config & PIPE_TILING__MASK) >> PIPE_TILING__SHIFT;
@@ -2724,7 +2727,7 @@ void r600_fence_ring_emit(struct radeon_device *rdev,
                /* EVENT_WRITE_EOP - flush caches, send int */
                radeon_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4));
                radeon_ring_write(ring, EVENT_TYPE(CACHE_FLUSH_AND_INV_EVENT_TS) | EVENT_INDEX(5));
-               radeon_ring_write(ring, addr & 0xffffffff);
+               radeon_ring_write(ring, lower_32_bits(addr));
                radeon_ring_write(ring, (upper_32_bits(addr) & 0xff) | DATA_SEL(1) | INT_SEL(2));
                radeon_ring_write(ring, fence->seq);
                radeon_ring_write(ring, 0);
@@ -2763,7 +2766,7 @@ bool r600_semaphore_ring_emit(struct radeon_device *rdev,
                sel |= PACKET3_SEM_WAIT_ON_SIGNAL;
 
        radeon_ring_write(ring, PACKET3(PACKET3_MEM_SEMAPHORE, 1));
-       radeon_ring_write(ring, addr & 0xffffffff);
+       radeon_ring_write(ring, lower_32_bits(addr));
        radeon_ring_write(ring, (upper_32_bits(addr) & 0xff) | sel);
 
        return true;
@@ -2824,9 +2827,9 @@ int r600_copy_cpdma(struct radeon_device *rdev,
                if (size_in_bytes == 0)
                        tmp |= PACKET3_CP_DMA_CP_SYNC;
                radeon_ring_write(ring, PACKET3(PACKET3_CP_DMA, 4));
-               radeon_ring_write(ring, src_offset & 0xffffffff);
+               radeon_ring_write(ring, lower_32_bits(src_offset));
                radeon_ring_write(ring, tmp);
-               radeon_ring_write(ring, dst_offset & 0xffffffff);
+               radeon_ring_write(ring, lower_32_bits(dst_offset));
                radeon_ring_write(ring, upper_32_bits(dst_offset) & 0xff);
                radeon_ring_write(ring, cur_size_in_bytes);
                src_offset += cur_size_in_bytes;
@@ -3876,7 +3879,7 @@ restart_ih:
                                                wake_up(&rdev->irq.vblank_queue);
                                        }
                                        if (atomic_read(&rdev->irq.pflip[0]))
-                                               radeon_crtc_handle_flip(rdev, 0);
+                                               radeon_crtc_handle_vblank(rdev, 0);
                                        rdev->irq.stat_regs.r600.disp_int &= ~LB_D1_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D1 vblank\n");
                                }
@@ -3902,7 +3905,7 @@ restart_ih:
                                                wake_up(&rdev->irq.vblank_queue);
                                        }
                                        if (atomic_read(&rdev->irq.pflip[1]))
-                                               radeon_crtc_handle_flip(rdev, 1);
+                                               radeon_crtc_handle_vblank(rdev, 1);
                                        rdev->irq.stat_regs.r600.disp_int &= ~LB_D2_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D2 vblank\n");
                                }
index 85a2bb28aed27699e464edc54a8573c8d1d41b39..26ef8ced6f89fd5388f80d2eb408c1a064af3b0e 100644 (file)
@@ -133,7 +133,7 @@ struct radeon_hdmi_acr r600_hdmi_acr(uint32_t clock)
 /*
  * update the N and CTS parameters for a given pixel clock rate
  */
-static void r600_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t clock)
+void r600_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t clock)
 {
        struct drm_device *dev = encoder->dev;
        struct radeon_device *rdev = dev->dev_private;
@@ -142,21 +142,33 @@ static void r600_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t clock)
        struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
        uint32_t offset = dig->afmt->offset;
 
-       WREG32(HDMI0_ACR_32_0 + offset, HDMI0_ACR_CTS_32(acr.cts_32khz));
-       WREG32(HDMI0_ACR_32_1 + offset, acr.n_32khz);
-
-       WREG32(HDMI0_ACR_44_0 + offset, HDMI0_ACR_CTS_44(acr.cts_44_1khz));
-       WREG32(HDMI0_ACR_44_1 + offset, acr.n_44_1khz);
-
-       WREG32(HDMI0_ACR_48_0 + offset, HDMI0_ACR_CTS_48(acr.cts_48khz));
-       WREG32(HDMI0_ACR_48_1 + offset, acr.n_48khz);
+       WREG32_P(HDMI0_ACR_32_0 + offset,
+                HDMI0_ACR_CTS_32(acr.cts_32khz),
+                ~HDMI0_ACR_CTS_32_MASK);
+       WREG32_P(HDMI0_ACR_32_1 + offset,
+                HDMI0_ACR_N_32(acr.n_32khz),
+                ~HDMI0_ACR_N_32_MASK);
+
+       WREG32_P(HDMI0_ACR_44_0 + offset,
+                HDMI0_ACR_CTS_44(acr.cts_44_1khz),
+                ~HDMI0_ACR_CTS_44_MASK);
+       WREG32_P(HDMI0_ACR_44_1 + offset,
+                HDMI0_ACR_N_44(acr.n_44_1khz),
+                ~HDMI0_ACR_N_44_MASK);
+
+       WREG32_P(HDMI0_ACR_48_0 + offset,
+                HDMI0_ACR_CTS_48(acr.cts_48khz),
+                ~HDMI0_ACR_CTS_48_MASK);
+       WREG32_P(HDMI0_ACR_48_1 + offset,
+                HDMI0_ACR_N_48(acr.n_48khz),
+                ~HDMI0_ACR_N_48_MASK);
 }
 
 /*
  * build a HDMI Video Info Frame
  */
-static void r600_hdmi_update_avi_infoframe(struct drm_encoder *encoder,
-                                          void *buffer, size_t size)
+void r600_hdmi_update_avi_infoframe(struct drm_encoder *encoder, void *buffer,
+                                   size_t size)
 {
        struct drm_device *dev = encoder->dev;
        struct radeon_device *rdev = dev->dev_private;
@@ -231,7 +243,7 @@ int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder)
 /*
  * write the audio workaround status to the hardware
  */
-static void r600_hdmi_audio_workaround(struct drm_encoder *encoder)
+void r600_hdmi_audio_workaround(struct drm_encoder *encoder)
 {
        struct drm_device *dev = encoder->dev;
        struct radeon_device *rdev = dev->dev_private;
@@ -250,7 +262,7 @@ static void r600_hdmi_audio_workaround(struct drm_encoder *encoder)
                 value, ~HDMI0_AUDIO_TEST_EN);
 }
 
-static void r600_audio_set_dto(struct drm_encoder *encoder, u32 clock)
+void r600_audio_set_dto(struct drm_encoder *encoder, u32 clock)
 {
        struct drm_device *dev = encoder->dev;
        struct radeon_device *rdev = dev->dev_private;
@@ -320,121 +332,6 @@ static void r600_audio_set_dto(struct drm_encoder *encoder, u32 clock)
        }
 }
 
-static void dce3_2_afmt_write_speaker_allocation(struct drm_encoder *encoder)
-{
-       struct radeon_device *rdev = encoder->dev->dev_private;
-       struct drm_connector *connector;
-       struct radeon_connector *radeon_connector = NULL;
-       u32 tmp;
-       u8 *sadb;
-       int sad_count;
-
-       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
-               if (connector->encoder == encoder) {
-                       radeon_connector = to_radeon_connector(connector);
-                       break;
-               }
-       }
-
-       if (!radeon_connector) {
-               DRM_ERROR("Couldn't find encoder's connector\n");
-               return;
-       }
-
-       sad_count = drm_edid_to_speaker_allocation(radeon_connector->edid, &sadb);
-       if (sad_count < 0) {
-               DRM_ERROR("Couldn't read Speaker Allocation Data Block: %d\n", sad_count);
-               return;
-       }
-
-       /* program the speaker allocation */
-       tmp = RREG32(AZ_F0_CODEC_PIN0_CONTROL_CHANNEL_SPEAKER);
-       tmp &= ~(DP_CONNECTION | SPEAKER_ALLOCATION_MASK);
-       /* set HDMI mode */
-       tmp |= HDMI_CONNECTION;
-       if (sad_count)
-               tmp |= SPEAKER_ALLOCATION(sadb[0]);
-       else
-               tmp |= SPEAKER_ALLOCATION(5); /* stereo */
-       WREG32(AZ_F0_CODEC_PIN0_CONTROL_CHANNEL_SPEAKER, tmp);
-
-       kfree(sadb);
-}
-
-static void dce3_2_afmt_write_sad_regs(struct drm_encoder *encoder)
-{
-       struct radeon_device *rdev = encoder->dev->dev_private;
-       struct drm_connector *connector;
-       struct radeon_connector *radeon_connector = NULL;
-       struct cea_sad *sads;
-       int i, sad_count;
-
-       static const u16 eld_reg_to_type[][2] = {
-               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR0, HDMI_AUDIO_CODING_TYPE_PCM },
-               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR1, HDMI_AUDIO_CODING_TYPE_AC3 },
-               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR2, HDMI_AUDIO_CODING_TYPE_MPEG1 },
-               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR3, HDMI_AUDIO_CODING_TYPE_MP3 },
-               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR4, HDMI_AUDIO_CODING_TYPE_MPEG2 },
-               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR5, HDMI_AUDIO_CODING_TYPE_AAC_LC },
-               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR6, HDMI_AUDIO_CODING_TYPE_DTS },
-               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR7, HDMI_AUDIO_CODING_TYPE_ATRAC },
-               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR9, HDMI_AUDIO_CODING_TYPE_EAC3 },
-               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR10, HDMI_AUDIO_CODING_TYPE_DTS_HD },
-               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR11, HDMI_AUDIO_CODING_TYPE_MLP },
-               { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR13, HDMI_AUDIO_CODING_TYPE_WMA_PRO },
-       };
-
-       list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
-               if (connector->encoder == encoder) {
-                       radeon_connector = to_radeon_connector(connector);
-                       break;
-               }
-       }
-
-       if (!radeon_connector) {
-               DRM_ERROR("Couldn't find encoder's connector\n");
-               return;
-       }
-
-       sad_count = drm_edid_to_sad(radeon_connector->edid, &sads);
-       if (sad_count < 0) {
-               DRM_ERROR("Couldn't read SADs: %d\n", sad_count);
-               return;
-       }
-       BUG_ON(!sads);
-
-       for (i = 0; i < ARRAY_SIZE(eld_reg_to_type); i++) {
-               u32 value = 0;
-               u8 stereo_freqs = 0;
-               int max_channels = -1;
-               int j;
-
-               for (j = 0; j < sad_count; j++) {
-                       struct cea_sad *sad = &sads[j];
-
-                       if (sad->format == eld_reg_to_type[i][1]) {
-                               if (sad->channels > max_channels) {
-                                       value = MAX_CHANNELS(sad->channels) |
-                                               DESCRIPTOR_BYTE_2(sad->byte2) |
-                                               SUPPORTED_FREQUENCIES(sad->freq);
-                                       max_channels = sad->channels;
-                               }
-
-                               if (sad->format == HDMI_AUDIO_CODING_TYPE_PCM)
-                                       stereo_freqs |= sad->freq;
-                               else
-                                       break;
-                       }
-               }
-
-               value |= SUPPORTED_FREQUENCIES_STEREO(stereo_freqs);
-
-               WREG32(eld_reg_to_type[i][0], value);
-       }
-
-       kfree(sads);
-}
-
 /*
  * update the info frames with the data from the current display mode
  */
@@ -447,6 +344,7 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod
        u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE];
        struct hdmi_avi_infoframe frame;
        uint32_t offset;
+       uint32_t acr_ctl;
        ssize_t err;
 
        if (!dig || !dig->afmt)
@@ -463,52 +361,44 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod
 
        r600_audio_set_dto(encoder, mode->clock);
 
-       WREG32(HDMI0_VBI_PACKET_CONTROL + offset,
-              HDMI0_NULL_SEND); /* send null packets when required */
-
-       WREG32(HDMI0_AUDIO_CRC_CONTROL + offset, 0x1000);
-
-       if (ASIC_IS_DCE32(rdev)) {
-               WREG32(HDMI0_AUDIO_PACKET_CONTROL + offset,
-                      HDMI0_AUDIO_DELAY_EN(1) | /* default audio delay */
-                      HDMI0_AUDIO_PACKETS_PER_LINE(3)); /* should be suffient for all audio modes and small enough for all hblanks */
-               WREG32(AFMT_AUDIO_PACKET_CONTROL + offset,
-                      AFMT_AUDIO_SAMPLE_SEND | /* send audio packets */
-                      AFMT_60958_CS_UPDATE); /* allow 60958 channel status fields to be updated */
-       } else {
-               WREG32(HDMI0_AUDIO_PACKET_CONTROL + offset,
-                      HDMI0_AUDIO_SAMPLE_SEND | /* send audio packets */
-                      HDMI0_AUDIO_DELAY_EN(1) | /* default audio delay */
-                      HDMI0_AUDIO_PACKETS_PER_LINE(3) | /* should be suffient for all audio modes and small enough for all hblanks */
-                      HDMI0_60958_CS_UPDATE); /* allow 60958 channel status fields to be updated */
-       }
-
-       if (ASIC_IS_DCE32(rdev)) {
-               dce3_2_afmt_write_speaker_allocation(encoder);
-               dce3_2_afmt_write_sad_regs(encoder);
-       }
-
-       WREG32(HDMI0_ACR_PACKET_CONTROL + offset,
-              HDMI0_ACR_SOURCE | /* select SW CTS value - XXX verify that hw CTS works on all families */
-              HDMI0_ACR_AUTO_SEND); /* allow hw to sent ACR packets when required */
-
-       WREG32(HDMI0_VBI_PACKET_CONTROL + offset,
-              HDMI0_NULL_SEND | /* send null packets when required */
-              HDMI0_GC_SEND | /* send general control packets */
-              HDMI0_GC_CONT); /* send general control packets every frame */
-
-       /* TODO: HDMI0_AUDIO_INFO_UPDATE */
-       WREG32(HDMI0_INFOFRAME_CONTROL0 + offset,
-              HDMI0_AVI_INFO_SEND | /* enable AVI info frames */
-              HDMI0_AVI_INFO_CONT | /* send AVI info frames every frame/field */
-              HDMI0_AUDIO_INFO_SEND | /* enable audio info frames (frames won't be set until audio is enabled) */
-              HDMI0_AUDIO_INFO_CONT); /* send audio info frames every frame/field */
-
-       WREG32(HDMI0_INFOFRAME_CONTROL1 + offset,
-              HDMI0_AVI_INFO_LINE(2) | /* anything other than 0 */
-              HDMI0_AUDIO_INFO_LINE(2)); /* anything other than 0 */
-
-       WREG32(HDMI0_GC + offset, 0); /* unset HDMI0_GC_AVMUTE */
+       WREG32_P(HDMI0_AUDIO_PACKET_CONTROL + offset,
+                HDMI0_AUDIO_SAMPLE_SEND | /* send audio packets */
+                HDMI0_AUDIO_DELAY_EN(1) | /* default audio delay */
+                HDMI0_AUDIO_PACKETS_PER_LINE(3) | /* should be suffient for all audio modes and small enough for all hblanks */
+                HDMI0_60958_CS_UPDATE, /* allow 60958 channel status fields to be updated */
+                ~(HDMI0_AUDIO_SAMPLE_SEND |
+                  HDMI0_AUDIO_DELAY_EN_MASK |
+                  HDMI0_AUDIO_PACKETS_PER_LINE_MASK |
+                  HDMI0_60958_CS_UPDATE));
+
+       /* DCE 3.0 uses register that's normally for CRC_CONTROL */
+       acr_ctl = ASIC_IS_DCE3(rdev) ? DCE3_HDMI0_ACR_PACKET_CONTROL :
+                                      HDMI0_ACR_PACKET_CONTROL;
+       WREG32_P(acr_ctl + offset,
+                HDMI0_ACR_SOURCE | /* select SW CTS value - XXX verify that hw CTS works on all families */
+                HDMI0_ACR_AUTO_SEND, /* allow hw to sent ACR packets when required */
+                ~(HDMI0_ACR_SOURCE |
+                  HDMI0_ACR_AUTO_SEND));
+
+       WREG32_OR(HDMI0_VBI_PACKET_CONTROL + offset,
+                 HDMI0_NULL_SEND | /* send null packets when required */
+                 HDMI0_GC_SEND | /* send general control packets */
+                 HDMI0_GC_CONT); /* send general control packets every frame */
+
+       WREG32_OR(HDMI0_INFOFRAME_CONTROL0 + offset,
+                 HDMI0_AVI_INFO_SEND | /* enable AVI info frames */
+                 HDMI0_AVI_INFO_CONT | /* send AVI info frames every frame/field */
+                 HDMI0_AUDIO_INFO_SEND | /* enable audio info frames (frames won't be set until audio is enabled) */
+                 HDMI0_AUDIO_INFO_UPDATE); /* required for audio info values to be updated */
+
+       WREG32_P(HDMI0_INFOFRAME_CONTROL1 + offset,
+                HDMI0_AVI_INFO_LINE(2) | /* anything other than 0 */
+                HDMI0_AUDIO_INFO_LINE(2), /* anything other than 0 */
+                ~(HDMI0_AVI_INFO_LINE_MASK |
+                  HDMI0_AUDIO_INFO_LINE_MASK));
+
+       WREG32_AND(HDMI0_GC + offset,
+                  ~HDMI0_GC_AVMUTE); /* unset HDMI0_GC_AVMUTE */
 
        err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
        if (err < 0) {
@@ -523,22 +413,45 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod
        }
 
        r600_hdmi_update_avi_infoframe(encoder, buffer, sizeof(buffer));
+
+       /* fglrx duplicates INFOFRAME_CONTROL0 & INFOFRAME_CONTROL1 ops here */
+
+       WREG32_AND(HDMI0_GENERIC_PACKET_CONTROL + offset,
+                  ~(HDMI0_GENERIC0_SEND |
+                    HDMI0_GENERIC0_CONT |
+                    HDMI0_GENERIC0_UPDATE |
+                    HDMI0_GENERIC1_SEND |
+                    HDMI0_GENERIC1_CONT |
+                    HDMI0_GENERIC0_LINE_MASK |
+                    HDMI0_GENERIC1_LINE_MASK));
+
        r600_hdmi_update_ACR(encoder, mode->clock);
 
+       WREG32_P(HDMI0_60958_0 + offset,
+                HDMI0_60958_CS_CHANNEL_NUMBER_L(1),
+                ~(HDMI0_60958_CS_CHANNEL_NUMBER_L_MASK |
+                  HDMI0_60958_CS_CLOCK_ACCURACY_MASK));
+
+       WREG32_P(HDMI0_60958_1 + offset,
+                HDMI0_60958_CS_CHANNEL_NUMBER_R(2),
+                ~HDMI0_60958_CS_CHANNEL_NUMBER_R_MASK);
+
        /* it's unknown what these bits do excatly, but it's indeed quite useful for debugging */
        WREG32(HDMI0_RAMP_CONTROL0 + offset, 0x00FFFFFF);
        WREG32(HDMI0_RAMP_CONTROL1 + offset, 0x007FFFFF);
        WREG32(HDMI0_RAMP_CONTROL2 + offset, 0x00000001);
        WREG32(HDMI0_RAMP_CONTROL3 + offset, 0x00000001);
 
-       r600_hdmi_audio_workaround(encoder);
-
        /* enable audio after to setting up hw */
        r600_audio_enable(rdev, dig->afmt->pin, true);
 }
 
-/*
- * update settings with current parameters from audio engine
+/**
+ * r600_hdmi_update_audio_settings - Update audio infoframe
+ *
+ * @encoder: drm encoder
+ *
+ * Gets info about current audio stream and updates audio infoframe.
  */
 void r600_hdmi_update_audio_settings(struct drm_encoder *encoder)
 {
@@ -550,7 +463,7 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder)
        uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE];
        struct hdmi_audio_infoframe frame;
        uint32_t offset;
-       uint32_t iec;
+       uint32_t value;
        ssize_t err;
 
        if (!dig->afmt || !dig->afmt->enabled)
@@ -563,60 +476,6 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder)
        DRM_DEBUG("0x%02X IEC60958 status bits and 0x%02X category code\n",
                  (int)audio.status_bits, (int)audio.category_code);
 
-       iec = 0;
-       if (audio.status_bits & AUDIO_STATUS_PROFESSIONAL)
-               iec |= 1 << 0;
-       if (audio.status_bits & AUDIO_STATUS_NONAUDIO)
-               iec |= 1 << 1;
-       if (audio.status_bits & AUDIO_STATUS_COPYRIGHT)
-               iec |= 1 << 2;
-       if (audio.status_bits & AUDIO_STATUS_EMPHASIS)
-               iec |= 1 << 3;
-
-       iec |= HDMI0_60958_CS_CATEGORY_CODE(audio.category_code);
-
-       switch (audio.rate) {
-       case 32000:
-               iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0x3);
-               break;
-       case 44100:
-               iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0x0);
-               break;
-       case 48000:
-               iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0x2);
-               break;
-       case 88200:
-               iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0x8);
-               break;
-       case 96000:
-               iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0xa);
-               break;
-       case 176400:
-               iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0xc);
-               break;
-       case 192000:
-               iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0xe);
-               break;
-       }
-
-       WREG32(HDMI0_60958_0 + offset, iec);
-
-       iec = 0;
-       switch (audio.bits_per_sample) {
-       case 16:
-               iec |= HDMI0_60958_CS_WORD_LENGTH(0x2);
-               break;
-       case 20:
-               iec |= HDMI0_60958_CS_WORD_LENGTH(0x3);
-               break;
-       case 24:
-               iec |= HDMI0_60958_CS_WORD_LENGTH(0xb);
-               break;
-       }
-       if (audio.status_bits & AUDIO_STATUS_V)
-               iec |= 0x5 << 16;
-       WREG32_P(HDMI0_60958_1 + offset, iec, ~0x5000f);
-
        err = hdmi_audio_infoframe_init(&frame);
        if (err < 0) {
                DRM_ERROR("failed to setup audio infoframe\n");
@@ -631,8 +490,22 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder)
                return;
        }
 
+       value = RREG32(HDMI0_AUDIO_PACKET_CONTROL + offset);
+       if (value & HDMI0_AUDIO_TEST_EN)
+               WREG32(HDMI0_AUDIO_PACKET_CONTROL + offset,
+                      value & ~HDMI0_AUDIO_TEST_EN);
+
+       WREG32_OR(HDMI0_CONTROL + offset,
+                 HDMI0_ERROR_ACK);
+
+       WREG32_AND(HDMI0_INFOFRAME_CONTROL0 + offset,
+                  ~HDMI0_AUDIO_INFO_SOURCE);
+
        r600_hdmi_update_audio_infoframe(encoder, buffer, sizeof(buffer));
-       r600_hdmi_audio_workaround(encoder);
+
+       WREG32_OR(HDMI0_INFOFRAME_CONTROL0 + offset,
+                 HDMI0_AUDIO_INFO_CONT |
+                 HDMI0_AUDIO_INFO_UPDATE);
 }
 
 /*
index 37455f65107f7fc8c0d2ff8a6a14d5abe43c13af..f94e7a9afe754c602e5e40ddf600f7bef73add6e 100644 (file)
 #define HDMI0_AUDIO_PACKET_CONTROL   0x7408
 #       define HDMI0_AUDIO_SAMPLE_SEND  (1 << 0)
 #       define HDMI0_AUDIO_DELAY_EN(x)  (((x) & 3) << 4)
+#       define HDMI0_AUDIO_DELAY_EN_MASK       (3 << 4)
 #       define HDMI0_AUDIO_SEND_MAX_PACKETS  (1 << 8)
 #       define HDMI0_AUDIO_TEST_EN         (1 << 12)
 #       define HDMI0_AUDIO_PACKETS_PER_LINE(x)  (((x) & 0x1f) << 16)
+#       define HDMI0_AUDIO_PACKETS_PER_LINE_MASK       (0x1f << 16)
 #       define HDMI0_AUDIO_CHANNEL_SWAP    (1 << 24)
 #       define HDMI0_60958_CS_UPDATE       (1 << 26)
 #       define HDMI0_AZ_FORMAT_WTRIG_MASK  (1 << 28)
 #       define HDMI0_AZ_FORMAT_WTRIG_ACK   (1 << 29)
 #define HDMI0_AUDIO_CRC_CONTROL      0x740c
 #       define HDMI0_AUDIO_CRC_EN    (1 << 0)
+#define DCE3_HDMI0_ACR_PACKET_CONTROL  0x740c
 #define HDMI0_VBI_PACKET_CONTROL     0x7410
 #       define HDMI0_NULL_SEND       (1 << 0)
 #       define HDMI0_GC_SEND         (1 << 4)
 #       define HDMI0_MPEG_INFO_UPDATE  (1 << 10)
 #define HDMI0_INFOFRAME_CONTROL1     0x7418
 #       define HDMI0_AVI_INFO_LINE(x)  (((x) & 0x3f) << 0)
+#       define HDMI0_AVI_INFO_LINE_MASK                (0x3f << 0)
 #       define HDMI0_AUDIO_INFO_LINE(x)  (((x) & 0x3f) << 8)
+#       define HDMI0_AUDIO_INFO_LINE_MASK      (0x3f << 8)
 #       define HDMI0_MPEG_INFO_LINE(x)  (((x) & 0x3f) << 16)
 #define HDMI0_GENERIC_PACKET_CONTROL 0x741c
 #       define HDMI0_GENERIC0_SEND   (1 << 0)
 #       define HDMI0_GENERIC1_SEND   (1 << 4)
 #       define HDMI0_GENERIC1_CONT   (1 << 5)
 #       define HDMI0_GENERIC0_LINE(x)  (((x) & 0x3f) << 16)
+#       define HDMI0_GENERIC0_LINE_MASK                (0x3f << 16)
 #       define HDMI0_GENERIC1_LINE(x)  (((x) & 0x3f) << 24)
+#       define HDMI0_GENERIC1_LINE_MASK                (0x3f << 24)
 #define HDMI0_GC                     0x7428
 #       define HDMI0_GC_AVMUTE       (1 << 0)
 #define HDMI0_AVI_INFO0              0x7454
 #define HDMI0_GENERIC1_6             0x74a8
 #define HDMI0_ACR_32_0               0x74ac
 #       define HDMI0_ACR_CTS_32(x)   (((x) & 0xfffff) << 12)
+#       define HDMI0_ACR_CTS_32_MASK           (0xfffff << 12)
 #define HDMI0_ACR_32_1               0x74b0
 #       define HDMI0_ACR_N_32(x)   (((x) & 0xfffff) << 0)
+#       define HDMI0_ACR_N_32_MASK             (0xfffff << 0)
 #define HDMI0_ACR_44_0               0x74b4
 #       define HDMI0_ACR_CTS_44(x)   (((x) & 0xfffff) << 12)
+#       define HDMI0_ACR_CTS_44_MASK           (0xfffff << 12)
 #define HDMI0_ACR_44_1               0x74b8
 #       define HDMI0_ACR_N_44(x)   (((x) & 0xfffff) << 0)
+#       define HDMI0_ACR_N_44_MASK             (0xfffff << 0)
 #define HDMI0_ACR_48_0               0x74bc
 #       define HDMI0_ACR_CTS_48(x)   (((x) & 0xfffff) << 12)
+#       define HDMI0_ACR_CTS_48_MASK           (0xfffff << 12)
 #define HDMI0_ACR_48_1               0x74c0
 #       define HDMI0_ACR_N_48(x)   (((x) & 0xfffff) << 0)
+#       define HDMI0_ACR_N_48_MASK             (0xfffff << 0)
 #define HDMI0_ACR_STATUS_0           0x74c4
 #define HDMI0_ACR_STATUS_1           0x74c8
 #define HDMI0_AUDIO_INFO0            0x74cc
 #       define HDMI0_60958_CS_CATEGORY_CODE(x)      (((x) & 0xff) << 8)
 #       define HDMI0_60958_CS_SOURCE_NUMBER(x)      (((x) & 0xf) << 16)
 #       define HDMI0_60958_CS_CHANNEL_NUMBER_L(x)   (((x) & 0xf) << 20)
+#       define HDMI0_60958_CS_CHANNEL_NUMBER_L_MASK    (0xf << 20)
 #       define HDMI0_60958_CS_SAMPLING_FREQUENCY(x) (((x) & 0xf) << 24)
 #       define HDMI0_60958_CS_CLOCK_ACCURACY(x)     (((x) & 3) << 28)
+#       define HDMI0_60958_CS_CLOCK_ACCURACY_MASK      (3 << 28)
 #define HDMI0_60958_1                0x74d8
 #       define HDMI0_60958_CS_WORD_LENGTH(x)        (((x) & 0xf) << 0)
 #       define HDMI0_60958_CS_ORIGINAL_SAMPLING_FREQUENCY(x)   (((x) & 0xf) << 4)
 #       define HDMI0_60958_CS_VALID_L(x)   (((x) & 1) << 16)
 #       define HDMI0_60958_CS_VALID_R(x)   (((x) & 1) << 18)
 #       define HDMI0_60958_CS_CHANNEL_NUMBER_R(x)   (((x) & 0xf) << 20)
+#       define HDMI0_60958_CS_CHANNEL_NUMBER_R_MASK    (0xf << 20)
 #define HDMI0_ACR_PACKET_CONTROL     0x74dc
 #       define HDMI0_ACR_SEND        (1 << 0)
 #       define HDMI0_ACR_CONT        (1 << 1)
 #       define HDMI0_ACR_48          3
 #       define HDMI0_ACR_SOURCE      (1 << 8) /* 0 - hw; 1 - cts value */
 #       define HDMI0_ACR_AUTO_SEND   (1 << 12)
+#define DCE3_HDMI0_AUDIO_CRC_CONTROL   0x74dc
 #define HDMI0_RAMP_CONTROL0          0x74e0
 #       define HDMI0_RAMP_MAX_COUNT(x)   (((x) & 0xffffff) << 0)
 #define HDMI0_RAMP_CONTROL1          0x74e4
index 8149e7cf430330095da34f98d27f842832c2c91c..4b0bbf88d5c0f19b3d2cda822e2634c51d1268fa 100644 (file)
@@ -100,6 +100,8 @@ extern int radeon_dpm;
 extern int radeon_aspm;
 extern int radeon_runtime_pm;
 extern int radeon_hard_reset;
+extern int radeon_vm_size;
+extern int radeon_vm_block_size;
 
 /*
  * Copy from radeon_drv.h so we don't have to include both and have conflicting
@@ -676,14 +678,16 @@ void radeon_doorbell_free(struct radeon_device *rdev, u32 doorbell);
  * IRQS.
  */
 
-struct radeon_unpin_work {
-       struct work_struct work;
-       struct radeon_device *rdev;
-       int crtc_id;
-       struct radeon_fence *fence;
+struct radeon_flip_work {
+       struct work_struct              flip_work;
+       struct work_struct              unpin_work;
+       struct radeon_device            *rdev;
+       int                             crtc_id;
+       struct drm_framebuffer          *fb;
        struct drm_pending_vblank_event *event;
-       struct radeon_bo *old_rbo;
-       u64 new_crtc_base;
+       struct radeon_bo                *old_rbo;
+       struct radeon_bo                *new_rbo;
+       struct radeon_fence             *fence;
 };
 
 struct r500_irq_stat_regs {
@@ -835,13 +839,8 @@ struct radeon_mec {
 /* maximum number of VMIDs */
 #define RADEON_NUM_VM  16
 
-/* defines number of bits in page table versus page directory,
- * a page is 4KB so we have 12 bits offset, 9 bits in the page
- * table and the remaining 19 bits are in the page directory */
-#define RADEON_VM_BLOCK_SIZE   9
-
 /* number of entries in page table */
-#define RADEON_VM_PTE_COUNT (1 << RADEON_VM_BLOCK_SIZE)
+#define RADEON_VM_PTE_COUNT (1 << radeon_vm_block_size)
 
 /* PTBs (Page Table Blocks) need to be aligned to 32K */
 #define RADEON_VM_PTB_ALIGN_SIZE   32768
@@ -854,6 +853,15 @@ struct radeon_mec {
 #define R600_PTE_READABLE      (1 << 5)
 #define R600_PTE_WRITEABLE     (1 << 6)
 
+/* PTE (Page Table Entry) fragment field for different page sizes */
+#define R600_PTE_FRAG_4KB      (0 << 7)
+#define R600_PTE_FRAG_64KB     (4 << 7)
+#define R600_PTE_FRAG_256KB    (6 << 7)
+
+/* flags used for GART page table entries on R600+ */
+#define R600_PTE_GART  ( R600_PTE_VALID | R600_PTE_SYSTEM | R600_PTE_SNOOPED \
+                       | R600_PTE_READABLE | R600_PTE_WRITEABLE)
+
 struct radeon_vm_pt {
        struct radeon_bo                *bo;
        uint64_t                        addr;
@@ -986,8 +994,8 @@ struct radeon_cs_reloc {
        struct radeon_bo                *robj;
        struct ttm_validate_buffer      tv;
        uint64_t                        gpu_offset;
-       unsigned                        domain;
-       unsigned                        alt_domain;
+       unsigned                        prefered_domains;
+       unsigned                        allowed_domains;
        uint32_t                        tiling_flags;
        uint32_t                        handle;
 };
@@ -1771,7 +1779,8 @@ struct radeon_asic {
        /* gart */
        struct {
                void (*tlb_flush)(struct radeon_device *rdev);
-               int (*set_page)(struct radeon_device *rdev, int i, uint64_t addr);
+               void (*set_page)(struct radeon_device *rdev, unsigned i,
+                                uint64_t addr);
        } gart;
        struct {
                int (*init)(struct radeon_device *rdev);
@@ -1883,9 +1892,8 @@ struct radeon_asic {
        } dpm;
        /* pageflipping */
        struct {
-               void (*pre_page_flip)(struct radeon_device *rdev, int crtc);
-               u32 (*page_flip)(struct radeon_device *rdev, int crtc, u64 crtc_base);
-               void (*post_page_flip)(struct radeon_device *rdev, int crtc);
+               void (*page_flip)(struct radeon_device *rdev, int crtc, u64 crtc_base);
+               bool (*page_flip_pending)(struct radeon_device *rdev, int crtc);
        } pflip;
 };
 
@@ -1924,6 +1932,7 @@ struct r600_asic {
        unsigned                tiling_group_size;
        unsigned                tile_config;
        unsigned                backend_map;
+       unsigned                active_simds;
 };
 
 struct rv770_asic {
@@ -1949,6 +1958,7 @@ struct rv770_asic {
        unsigned                tiling_group_size;
        unsigned                tile_config;
        unsigned                backend_map;
+       unsigned                active_simds;
 };
 
 struct evergreen_asic {
@@ -1975,6 +1985,7 @@ struct evergreen_asic {
        unsigned tiling_group_size;
        unsigned tile_config;
        unsigned backend_map;
+       unsigned active_simds;
 };
 
 struct cayman_asic {
@@ -2013,6 +2024,7 @@ struct cayman_asic {
        unsigned multi_gpu_tile_size;
 
        unsigned tile_config;
+       unsigned active_simds;
 };
 
 struct si_asic {
@@ -2043,6 +2055,7 @@ struct si_asic {
 
        unsigned tile_config;
        uint32_t tile_mode_array[32];
+       uint32_t active_cus;
 };
 
 struct cik_asic {
@@ -2074,6 +2087,7 @@ struct cik_asic {
        unsigned tile_config;
        uint32_t tile_mode_array[32];
        uint32_t macrotile_mode_array[16];
+       uint32_t active_cus;
 };
 
 union radeon_asic_config {
@@ -2745,9 +2759,8 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v);
 #define radeon_pm_finish(rdev) (rdev)->asic->pm.finish((rdev))
 #define radeon_pm_init_profile(rdev) (rdev)->asic->pm.init_profile((rdev))
 #define radeon_pm_get_dynpm_state(rdev) (rdev)->asic->pm.get_dynpm_state((rdev))
-#define radeon_pre_page_flip(rdev, crtc) (rdev)->asic->pflip.pre_page_flip((rdev), (crtc))
 #define radeon_page_flip(rdev, crtc, base) (rdev)->asic->pflip.page_flip((rdev), (crtc), (base))
-#define radeon_post_page_flip(rdev, crtc) (rdev)->asic->pflip.post_page_flip((rdev), (crtc))
+#define radeon_page_flip_pending(rdev, crtc) (rdev)->asic->pflip.page_flip_pending((rdev), (crtc))
 #define radeon_wait_for_vblank(rdev, crtc) (rdev)->asic->display.wait_for_vblank((rdev), (crtc))
 #define radeon_mc_wait_for_idle(rdev) (rdev)->asic->mc_wait_for_idle((rdev))
 #define radeon_get_xclk(rdev) (rdev)->asic->get_xclk((rdev))
index 42433344cb1b24860998067fe7c96baf332c1ece..a9297b2c3524ec128e3df129b21a4cd027cf20dd 100644 (file)
@@ -117,9 +117,6 @@ static struct radeon_agpmode_quirk radeon_agpmode_quirk_list[] = {
        /* ATI Host Bridge / RV280 [M9+] Needs AGPMode 1 (phoronix forum) */
        { PCI_VENDOR_ID_ATI, 0xcbb2, PCI_VENDOR_ID_ATI, 0x5c61,
                PCI_VENDOR_ID_SONY, 0x8175, 1},
-       /* HP Host Bridge / R300 [FireGL X1] Needs AGPMode 2 (fdo #7770) */
-       { PCI_VENDOR_ID_HP, 0x122e, PCI_VENDOR_ID_ATI, 0x4e47,
-               PCI_VENDOR_ID_ATI, 0x0152, 2},
        { 0, 0, 0, 0, 0, 0, 0 },
 };
 #endif
index e5f0177bea1e88181fc1626f2ce6b58537547cd5..34b9aa9e3c0603d52589ffc89f8885c9af9a733d 100644 (file)
@@ -248,9 +248,8 @@ static struct radeon_asic r100_asic = {
                .set_clock_gating = &radeon_legacy_set_clock_gating,
        },
        .pflip = {
-               .pre_page_flip = &r100_pre_page_flip,
                .page_flip = &r100_page_flip,
-               .post_page_flip = &r100_post_page_flip,
+               .page_flip_pending = &r100_page_flip_pending,
        },
 };
 
@@ -315,9 +314,8 @@ static struct radeon_asic r200_asic = {
                .set_clock_gating = &radeon_legacy_set_clock_gating,
        },
        .pflip = {
-               .pre_page_flip = &r100_pre_page_flip,
                .page_flip = &r100_page_flip,
-               .post_page_flip = &r100_post_page_flip,
+               .page_flip_pending = &r100_page_flip_pending,
        },
 };
 
@@ -396,9 +394,8 @@ static struct radeon_asic r300_asic = {
                .set_clock_gating = &radeon_legacy_set_clock_gating,
        },
        .pflip = {
-               .pre_page_flip = &r100_pre_page_flip,
                .page_flip = &r100_page_flip,
-               .post_page_flip = &r100_post_page_flip,
+               .page_flip_pending = &r100_page_flip_pending,
        },
 };
 
@@ -463,9 +460,8 @@ static struct radeon_asic r300_asic_pcie = {
                .set_clock_gating = &radeon_legacy_set_clock_gating,
        },
        .pflip = {
-               .pre_page_flip = &r100_pre_page_flip,
                .page_flip = &r100_page_flip,
-               .post_page_flip = &r100_post_page_flip,
+               .page_flip_pending = &r100_page_flip_pending,
        },
 };
 
@@ -530,9 +526,8 @@ static struct radeon_asic r420_asic = {
                .set_clock_gating = &radeon_atom_set_clock_gating,
        },
        .pflip = {
-               .pre_page_flip = &r100_pre_page_flip,
                .page_flip = &r100_page_flip,
-               .post_page_flip = &r100_post_page_flip,
+               .page_flip_pending = &r100_page_flip_pending,
        },
 };
 
@@ -597,9 +592,8 @@ static struct radeon_asic rs400_asic = {
                .set_clock_gating = &radeon_legacy_set_clock_gating,
        },
        .pflip = {
-               .pre_page_flip = &r100_pre_page_flip,
                .page_flip = &r100_page_flip,
-               .post_page_flip = &r100_post_page_flip,
+               .page_flip_pending = &r100_page_flip_pending,
        },
 };
 
@@ -666,9 +660,8 @@ static struct radeon_asic rs600_asic = {
                .set_clock_gating = &radeon_atom_set_clock_gating,
        },
        .pflip = {
-               .pre_page_flip = &rs600_pre_page_flip,
                .page_flip = &rs600_page_flip,
-               .post_page_flip = &rs600_post_page_flip,
+               .page_flip_pending = &rs600_page_flip_pending,
        },
 };
 
@@ -735,9 +728,8 @@ static struct radeon_asic rs690_asic = {
                .set_clock_gating = &radeon_atom_set_clock_gating,
        },
        .pflip = {
-               .pre_page_flip = &rs600_pre_page_flip,
                .page_flip = &rs600_page_flip,
-               .post_page_flip = &rs600_post_page_flip,
+               .page_flip_pending = &rs600_page_flip_pending,
        },
 };
 
@@ -802,9 +794,8 @@ static struct radeon_asic rv515_asic = {
                .set_clock_gating = &radeon_atom_set_clock_gating,
        },
        .pflip = {
-               .pre_page_flip = &rs600_pre_page_flip,
                .page_flip = &rs600_page_flip,
-               .post_page_flip = &rs600_post_page_flip,
+               .page_flip_pending = &rs600_page_flip_pending,
        },
 };
 
@@ -869,9 +860,8 @@ static struct radeon_asic r520_asic = {
                .set_clock_gating = &radeon_atom_set_clock_gating,
        },
        .pflip = {
-               .pre_page_flip = &rs600_pre_page_flip,
                .page_flip = &rs600_page_flip,
-               .post_page_flip = &rs600_post_page_flip,
+               .page_flip_pending = &rs600_page_flip_pending,
        },
 };
 
@@ -968,9 +958,8 @@ static struct radeon_asic r600_asic = {
                .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,
+               .page_flip_pending = &rs600_page_flip_pending,
        },
 };
 
@@ -1059,9 +1048,8 @@ static struct radeon_asic rv6xx_asic = {
                .force_performance_level = &rv6xx_dpm_force_performance_level,
        },
        .pflip = {
-               .pre_page_flip = &rs600_pre_page_flip,
                .page_flip = &rs600_page_flip,
-               .post_page_flip = &rs600_post_page_flip,
+               .page_flip_pending = &rs600_page_flip_pending,
        },
 };
 
@@ -1150,9 +1138,8 @@ static struct radeon_asic rs780_asic = {
                .force_performance_level = &rs780_dpm_force_performance_level,
        },
        .pflip = {
-               .pre_page_flip = &rs600_pre_page_flip,
                .page_flip = &rs600_page_flip,
-               .post_page_flip = &rs600_post_page_flip,
+               .page_flip_pending = &rs600_page_flip_pending,
        },
 };
 
@@ -1201,7 +1188,7 @@ static struct radeon_asic rv770_asic = {
                .set_backlight_level = &atombios_set_backlight_level,
                .get_backlight_level = &atombios_get_backlight_level,
                .hdmi_enable = &r600_hdmi_enable,
-               .hdmi_setmode = &r600_hdmi_setmode,
+               .hdmi_setmode = &dce3_1_hdmi_setmode,
        },
        .copy = {
                .blit = &r600_copy_cpdma,
@@ -1256,9 +1243,8 @@ static struct radeon_asic rv770_asic = {
                .vblank_too_short = &rv770_dpm_vblank_too_short,
        },
        .pflip = {
-               .pre_page_flip = &rs600_pre_page_flip,
                .page_flip = &rv770_page_flip,
-               .post_page_flip = &rs600_post_page_flip,
+               .page_flip_pending = &rv770_page_flip_pending,
        },
 };
 
@@ -1375,9 +1361,8 @@ static struct radeon_asic evergreen_asic = {
                .vblank_too_short = &cypress_dpm_vblank_too_short,
        },
        .pflip = {
-               .pre_page_flip = &evergreen_pre_page_flip,
                .page_flip = &evergreen_page_flip,
-               .post_page_flip = &evergreen_post_page_flip,
+               .page_flip_pending = &evergreen_page_flip_pending,
        },
 };
 
@@ -1467,9 +1452,8 @@ static struct radeon_asic sumo_asic = {
                .force_performance_level = &sumo_dpm_force_performance_level,
        },
        .pflip = {
-               .pre_page_flip = &evergreen_pre_page_flip,
                .page_flip = &evergreen_page_flip,
-               .post_page_flip = &evergreen_post_page_flip,
+               .page_flip_pending = &evergreen_page_flip_pending,
        },
 };
 
@@ -1560,9 +1544,8 @@ static struct radeon_asic btc_asic = {
                .vblank_too_short = &btc_dpm_vblank_too_short,
        },
        .pflip = {
-               .pre_page_flip = &evergreen_pre_page_flip,
                .page_flip = &evergreen_page_flip,
-               .post_page_flip = &evergreen_post_page_flip,
+               .page_flip_pending = &evergreen_page_flip_pending,
        },
 };
 
@@ -1704,9 +1687,8 @@ static struct radeon_asic cayman_asic = {
                .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,
+               .page_flip_pending = &evergreen_page_flip_pending,
        },
 };
 
@@ -1805,9 +1787,8 @@ static struct radeon_asic trinity_asic = {
                .enable_bapm = &trinity_dpm_enable_bapm,
        },
        .pflip = {
-               .pre_page_flip = &evergreen_pre_page_flip,
                .page_flip = &evergreen_page_flip,
-               .post_page_flip = &evergreen_post_page_flip,
+               .page_flip_pending = &evergreen_page_flip_pending,
        },
 };
 
@@ -1936,9 +1917,8 @@ static struct radeon_asic si_asic = {
                .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,
+               .page_flip_pending = &evergreen_page_flip_pending,
        },
 };
 
@@ -2049,8 +2029,8 @@ static struct radeon_asic ci_asic = {
                .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
                .dma = &cik_copy_dma,
                .dma_ring_index = R600_RING_TYPE_DMA_INDEX,
-               .copy = &cik_copy_cpdma,
-               .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+               .copy = &cik_copy_dma,
+               .copy_ring_index = R600_RING_TYPE_DMA_INDEX,
        },
        .surface = {
                .set_reg = r600_set_surface_reg,
@@ -2099,9 +2079,8 @@ static struct radeon_asic ci_asic = {
                .powergate_uvd = &ci_dpm_powergate_uvd,
        },
        .pflip = {
-               .pre_page_flip = &evergreen_pre_page_flip,
                .page_flip = &evergreen_page_flip,
-               .post_page_flip = &evergreen_post_page_flip,
+               .page_flip_pending = &evergreen_page_flip_pending,
        },
 };
 
@@ -2204,9 +2183,8 @@ static struct radeon_asic kv_asic = {
                .enable_bapm = &kv_dpm_enable_bapm,
        },
        .pflip = {
-               .pre_page_flip = &evergreen_pre_page_flip,
                .page_flip = &evergreen_page_flip,
-               .post_page_flip = &evergreen_post_page_flip,
+               .page_flip_pending = &evergreen_page_flip_pending,
        },
 };
 
index 3d55a3a39e82d55e093d707e8ff452dd9ab9b5e9..01e7c0ad8f0127a1a758c5c4348059927785bfdb 100644 (file)
@@ -67,7 +67,8 @@ bool r100_gpu_is_lockup(struct radeon_device *rdev, struct radeon_ring *cp);
 int r100_asic_reset(struct radeon_device *rdev);
 u32 r100_get_vblank_counter(struct radeon_device *rdev, int crtc);
 void r100_pci_gart_tlb_flush(struct radeon_device *rdev);
-int r100_pci_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr);
+void r100_pci_gart_set_page(struct radeon_device *rdev, unsigned i,
+                           uint64_t addr);
 void r100_ring_start(struct radeon_device *rdev, struct radeon_ring *ring);
 int r100_irq_set(struct radeon_device *rdev);
 int r100_irq_process(struct radeon_device *rdev);
@@ -135,9 +136,9 @@ extern void r100_pm_prepare(struct radeon_device *rdev);
 extern void r100_pm_finish(struct radeon_device *rdev);
 extern void r100_pm_init_profile(struct radeon_device *rdev);
 extern void r100_pm_get_dynpm_state(struct radeon_device *rdev);
-extern void r100_pre_page_flip(struct radeon_device *rdev, int crtc);
-extern u32 r100_page_flip(struct radeon_device *rdev, int crtc, u64 crtc_base);
-extern void r100_post_page_flip(struct radeon_device *rdev, int crtc);
+extern void r100_page_flip(struct radeon_device *rdev, int crtc,
+                          u64 crtc_base);
+extern bool r100_page_flip_pending(struct radeon_device *rdev, int crtc);
 extern void r100_wait_for_vblank(struct radeon_device *rdev, int crtc);
 extern int r100_mc_wait_for_idle(struct radeon_device *rdev);
 
@@ -171,7 +172,8 @@ extern void r300_fence_ring_emit(struct radeon_device *rdev,
                                struct radeon_fence *fence);
 extern int r300_cs_parse(struct radeon_cs_parser *p);
 extern void rv370_pcie_gart_tlb_flush(struct radeon_device *rdev);
-extern int rv370_pcie_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr);
+extern void rv370_pcie_gart_set_page(struct radeon_device *rdev, unsigned i,
+                                    uint64_t addr);
 extern void rv370_set_pcie_lanes(struct radeon_device *rdev, int lanes);
 extern int rv370_get_pcie_lanes(struct radeon_device *rdev);
 extern void r300_set_reg_safe(struct radeon_device *rdev);
@@ -206,7 +208,8 @@ extern void rs400_fini(struct radeon_device *rdev);
 extern int rs400_suspend(struct radeon_device *rdev);
 extern int rs400_resume(struct radeon_device *rdev);
 void rs400_gart_tlb_flush(struct radeon_device *rdev);
-int rs400_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr);
+void rs400_gart_set_page(struct radeon_device *rdev, unsigned i,
+                        uint64_t addr);
 uint32_t rs400_mc_rreg(struct radeon_device *rdev, uint32_t reg);
 void rs400_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v);
 int rs400_gart_init(struct radeon_device *rdev);
@@ -229,7 +232,8 @@ int rs600_irq_process(struct radeon_device *rdev);
 void rs600_irq_disable(struct radeon_device *rdev);
 u32 rs600_get_vblank_counter(struct radeon_device *rdev, int crtc);
 void rs600_gart_tlb_flush(struct radeon_device *rdev);
-int rs600_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr);
+void rs600_gart_set_page(struct radeon_device *rdev, unsigned i,
+                        uint64_t addr);
 uint32_t rs600_mc_rreg(struct radeon_device *rdev, uint32_t reg);
 void rs600_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v);
 void rs600_bandwidth_update(struct radeon_device *rdev);
@@ -241,9 +245,9 @@ void rs600_hpd_set_polarity(struct radeon_device *rdev,
 extern void rs600_pm_misc(struct radeon_device *rdev);
 extern void rs600_pm_prepare(struct radeon_device *rdev);
 extern void rs600_pm_finish(struct radeon_device *rdev);
-extern void rs600_pre_page_flip(struct radeon_device *rdev, int crtc);
-extern u32 rs600_page_flip(struct radeon_device *rdev, int crtc, u64 crtc_base);
-extern void rs600_post_page_flip(struct radeon_device *rdev, int crtc);
+extern void rs600_page_flip(struct radeon_device *rdev, int crtc,
+                           u64 crtc_base);
+extern bool rs600_page_flip_pending(struct radeon_device *rdev, int crtc);
 void rs600_set_safe_registers(struct radeon_device *rdev);
 extern void avivo_wait_for_vblank(struct radeon_device *rdev, int crtc);
 extern int rs600_mc_wait_for_idle(struct radeon_device *rdev);
@@ -387,6 +391,11 @@ void r600_rlc_stop(struct radeon_device *rdev);
 int r600_audio_init(struct radeon_device *rdev);
 struct r600_audio_pin r600_audio_status(struct radeon_device *rdev);
 void r600_audio_fini(struct radeon_device *rdev);
+void r600_audio_set_dto(struct drm_encoder *encoder, u32 clock);
+void r600_hdmi_update_avi_infoframe(struct drm_encoder *encoder, void *buffer,
+                                   size_t size);
+void r600_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t clock);
+void r600_hdmi_audio_workaround(struct drm_encoder *encoder);
 int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder);
 void r600_hdmi_update_audio_settings(struct drm_encoder *encoder);
 void r600_hdmi_enable(struct drm_encoder *encoder, bool enable);
@@ -447,7 +456,8 @@ void rv770_fini(struct radeon_device *rdev);
 int rv770_suspend(struct radeon_device *rdev);
 int rv770_resume(struct radeon_device *rdev);
 void rv770_pm_misc(struct radeon_device *rdev);
-u32 rv770_page_flip(struct radeon_device *rdev, int crtc, u64 crtc_base);
+void rv770_page_flip(struct radeon_device *rdev, int crtc, u64 crtc_base);
+bool rv770_page_flip_pending(struct radeon_device *rdev, int crtc);
 void r700_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc);
 void r700_cp_stop(struct radeon_device *rdev);
 void r700_cp_fini(struct radeon_device *rdev);
@@ -458,6 +468,8 @@ int rv770_copy_dma(struct radeon_device *rdev,
 u32 rv770_get_xclk(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);
+/* hdmi */
+void dce3_1_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode);
 /* rv7xx pm */
 int rv770_dpm_init(struct radeon_device *rdev);
 int rv770_dpm_enable(struct radeon_device *rdev);
@@ -513,9 +525,9 @@ extern void sumo_pm_init_profile(struct radeon_device *rdev);
 extern void btc_pm_init_profile(struct radeon_device *rdev);
 int sumo_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk);
 int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk);
-extern void evergreen_pre_page_flip(struct radeon_device *rdev, int crtc);
-extern u32 evergreen_page_flip(struct radeon_device *rdev, int crtc, u64 crtc_base);
-extern void evergreen_post_page_flip(struct radeon_device *rdev, int crtc);
+extern void evergreen_page_flip(struct radeon_device *rdev, int crtc,
+                               u64 crtc_base);
+extern bool evergreen_page_flip_pending(struct radeon_device *rdev, int crtc);
 extern void dce4_wait_for_vblank(struct radeon_device *rdev, int crtc);
 void evergreen_disable_interrupt_state(struct radeon_device *rdev);
 int evergreen_mc_wait_for_idle(struct radeon_device *rdev);
index 9ab30976287d4c27e0dc94e0cadfa993e6ec77cc..6a03624fadaa8005d40d4d4b818cf7c2a3580775 100644 (file)
@@ -626,7 +626,7 @@ static bool radeon_acpi_vfct_bios(struct radeon_device *rdev)
            vhdr->DeviceID != rdev->pdev->device) {
                DRM_INFO("ACPI VFCT table is not for this card\n");
                goto out_unmap;
-       };
+       }
 
        if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) + vhdr->ImageLength > tbl_size) {
                DRM_ERROR("ACPI VFCT image truncated\n");
index ea50e0ae7bf7f39261f03f7b301beadc5775da52..933c5c39654d2d054a9271f5b780e3ec45b6f13b 100644 (file)
@@ -48,6 +48,7 @@ void radeon_connector_hotplug(struct drm_connector *connector)
        radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
 
        /* if the connector is already off, don't turn it back on */
+       /* FIXME: This access isn't protected by any locks. */
        if (connector->dpms != DRM_MODE_DPMS_ON)
                return;
 
@@ -100,6 +101,7 @@ int radeon_get_monitor_bpc(struct drm_connector *connector)
        struct radeon_connector *radeon_connector = to_radeon_connector(connector);
        struct radeon_connector_atom_dig *dig_connector;
        int bpc = 8;
+       int mode_clock, max_tmds_clock;
 
        switch (connector->connector_type) {
        case DRM_MODE_CONNECTOR_DVII:
@@ -145,6 +147,61 @@ int radeon_get_monitor_bpc(struct drm_connector *connector)
                }
                break;
        }
+
+       if (drm_detect_hdmi_monitor(radeon_connector->edid)) {
+               /* hdmi deep color only implemented on DCE4+ */
+               if ((bpc > 8) && !ASIC_IS_DCE4(rdev)) {
+                       DRM_DEBUG("%s: HDMI deep color %d bpc unsupported. Using 8 bpc.\n",
+                                         connector->name, bpc);
+                       bpc = 8;
+               }
+
+               /*
+                * Pre DCE-8 hw can't handle > 12 bpc, and more than 12 bpc doesn't make
+                * much sense without support for > 12 bpc framebuffers. RGB 4:4:4 at
+                * 12 bpc is always supported on hdmi deep color sinks, as this is
+                * required by the HDMI-1.3 spec. Clamp to a safe 12 bpc maximum.
+                */
+               if (bpc > 12) {
+                       DRM_DEBUG("%s: HDMI deep color %d bpc unsupported. Using 12 bpc.\n",
+                                         connector->name, bpc);
+                       bpc = 12;
+               }
+
+               /* Any defined maximum tmds clock limit we must not exceed? */
+               if (connector->max_tmds_clock > 0) {
+                       /* mode_clock is clock in kHz for mode to be modeset on this connector */
+                       mode_clock = radeon_connector->pixelclock_for_modeset;
+
+                       /* Maximum allowable input clock in kHz */
+                       max_tmds_clock = connector->max_tmds_clock * 1000;
+
+                       DRM_DEBUG("%s: hdmi mode dotclock %d kHz, max tmds input clock %d kHz.\n",
+                                         connector->name, mode_clock, max_tmds_clock);
+
+                       /* Check if bpc is within clock limit. Try to degrade gracefully otherwise */
+                       if ((bpc == 12) && (mode_clock * 3/2 > max_tmds_clock)) {
+                               if ((connector->display_info.edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30) &&
+                                       (mode_clock * 5/4 <= max_tmds_clock))
+                                       bpc = 10;
+                               else
+                                       bpc = 8;
+
+                               DRM_DEBUG("%s: HDMI deep color 12 bpc exceeds max tmds clock. Using %d bpc.\n",
+                                                 connector->name, bpc);
+                       }
+
+                       if ((bpc == 10) && (mode_clock * 5/4 > max_tmds_clock)) {
+                               bpc = 8;
+                               DRM_DEBUG("%s: HDMI deep color 10 bpc exceeds max tmds clock. Using %d bpc.\n",
+                                                 connector->name, bpc);
+                       }
+               }
+       }
+
+       DRM_DEBUG("%s: Display bpc=%d, returned bpc=%d\n",
+                         connector->name, connector->display_info.bpc, bpc);
+
        return bpc;
 }
 
@@ -260,13 +317,17 @@ radeon_connector_analog_encoder_conflict_solve(struct drm_connector *connector,
                                        continue;
 
                                if (priority == true) {
-                                       DRM_DEBUG_KMS("1: conflicting encoders switching off %s\n", drm_get_connector_name(conflict));
-                                       DRM_DEBUG_KMS("in favor of %s\n", drm_get_connector_name(connector));
+                                       DRM_DEBUG_KMS("1: conflicting encoders switching off %s\n",
+                                                     conflict->name);
+                                       DRM_DEBUG_KMS("in favor of %s\n",
+                                                     connector->name);
                                        conflict->status = connector_status_disconnected;
                                        radeon_connector_update_scratch_regs(conflict, connector_status_disconnected);
                                } else {
-                                       DRM_DEBUG_KMS("2: conflicting encoders switching off %s\n", drm_get_connector_name(connector));
-                                       DRM_DEBUG_KMS("in favor of %s\n", drm_get_connector_name(conflict));
+                                       DRM_DEBUG_KMS("2: conflicting encoders switching off %s\n",
+                                                     connector->name);
+                                       DRM_DEBUG_KMS("in favor of %s\n",
+                                                     conflict->name);
                                        current_status = connector_status_disconnected;
                                }
                                break;
@@ -787,7 +848,7 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
 
                if (!radeon_connector->edid) {
                        DRM_ERROR("%s: probed a monitor but no|invalid EDID\n",
-                                       drm_get_connector_name(connector));
+                                       connector->name);
                        ret = connector_status_connected;
                } else {
                        radeon_connector->use_digital = !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL);
@@ -1010,12 +1071,13 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
 
                if (!radeon_connector->edid) {
                        DRM_ERROR("%s: probed a monitor but no|invalid EDID\n",
-                                       drm_get_connector_name(connector));
+                                       connector->name);
                        /* rs690 seems to have a problem with connectors not existing and always
                         * return a block of 0's. If we see this just stop polling on this output */
                        if ((rdev->family == CHIP_RS690 || rdev->family == CHIP_RS740) && radeon_connector->base.null_edid_counter) {
                                ret = connector_status_disconnected;
-                               DRM_ERROR("%s: detected RS690 floating bus bug, stopping ddc detect\n", drm_get_connector_name(connector));
+                               DRM_ERROR("%s: detected RS690 floating bus bug, stopping ddc detect\n",
+                                         connector->name);
                                radeon_connector->ddc_bus = NULL;
                        } else {
                                ret = connector_status_connected;
@@ -1387,7 +1449,7 @@ bool radeon_connector_is_dp12_capable(struct drm_connector *connector)
        struct radeon_device *rdev = dev->dev_private;
 
        if (ASIC_IS_DCE5(rdev) &&
-           (rdev->clock.dp_extclk >= 53900) &&
+           (rdev->clock.default_dispclk >= 53900) &&
            radeon_connector_encoder_is_hbr2(connector)) {
                return true;
        }
index 41ecf8a606117caa02954e720af0fb4ad690139e..71a143461478a60f5d9bc8428a0276c38b78953b 100644 (file)
@@ -140,10 +140,10 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
                if (p->ring == R600_RING_TYPE_UVD_INDEX &&
                    (i == 0 || drm_pci_device_is_agp(p->rdev->ddev))) {
                        /* TODO: is this still needed for NI+ ? */
-                       p->relocs[i].domain =
+                       p->relocs[i].prefered_domains =
                                RADEON_GEM_DOMAIN_VRAM;
 
-                       p->relocs[i].alt_domain =
+                       p->relocs[i].allowed_domains =
                                RADEON_GEM_DOMAIN_VRAM;
 
                        /* prioritize this over any other relocation */
@@ -158,10 +158,10 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
                                return -EINVAL;
                        }
 
-                       p->relocs[i].domain = domain;
+                       p->relocs[i].prefered_domains = domain;
                        if (domain == RADEON_GEM_DOMAIN_VRAM)
                                domain |= RADEON_GEM_DOMAIN_GTT;
-                       p->relocs[i].alt_domain = domain;
+                       p->relocs[i].allowed_domains = domain;
                }
 
                p->relocs[i].tv.bo = &p->relocs[i].robj->tbo;
index 2cd144c378d6e976a35b3dddf43e32997946060c..03686fab842d3c2e0de237fa6fb027895a025a80 100644 (file)
@@ -1052,6 +1052,43 @@ static void radeon_check_arguments(struct radeon_device *rdev)
                radeon_agpmode = 0;
                break;
        }
+
+       if (!radeon_check_pot_argument(radeon_vm_size)) {
+               dev_warn(rdev->dev, "VM size (%d) must be a power of 2\n",
+                        radeon_vm_size);
+               radeon_vm_size = 4096;
+       }
+
+       if (radeon_vm_size < 4) {
+               dev_warn(rdev->dev, "VM size (%d) to small, min is 4MB\n",
+                        radeon_vm_size);
+               radeon_vm_size = 4096;
+       }
+
+       /*
+        * Max GPUVM size for Cayman, SI and CI are 40 bits.
+        */
+       if (radeon_vm_size > 1024*1024) {
+               dev_warn(rdev->dev, "VM size (%d) to large, max is 1TB\n",
+                        radeon_vm_size);
+               radeon_vm_size = 4096;
+       }
+
+       /* defines number of bits in page table versus page directory,
+        * a page is 4KB so we have 12 bits offset, minimum 9 bits in the
+        * page table and the remaining bits are in the page directory */
+       if (radeon_vm_block_size < 9) {
+               dev_warn(rdev->dev, "VM page table size (%d) to small\n",
+                        radeon_vm_block_size);
+               radeon_vm_block_size = 9;
+       }
+
+       if (radeon_vm_block_size > 24 ||
+           radeon_vm_size < (1ull << radeon_vm_block_size)) {
+               dev_warn(rdev->dev, "VM page table size (%d) to large\n",
+                        radeon_vm_block_size);
+               radeon_vm_block_size = 9;
+       }
 }
 
 /**
@@ -1126,12 +1163,13 @@ static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switchero
 static bool radeon_switcheroo_can_switch(struct pci_dev *pdev)
 {
        struct drm_device *dev = pci_get_drvdata(pdev);
-       bool can_switch;
 
-       spin_lock(&dev->count_lock);
-       can_switch = (dev->open_count == 0);
-       spin_unlock(&dev->count_lock);
-       return can_switch;
+       /*
+        * FIXME: open_count is protected by drm_global_mutex but that would lead to
+        * locking inversion with the driver load path. And the access here is
+        * completely racy anyway. So don't bother with locking for now.
+        */
+       return dev->open_count == 0;
 }
 
 static const struct vga_switcheroo_client_ops radeon_switcheroo_ops = {
@@ -1196,17 +1234,16 @@ int radeon_device_init(struct radeon_device *rdev,
        if (r)
                return r;
 
+       radeon_check_arguments(rdev);
        /* Adjust VM size here.
-        * Currently set to 4GB ((1 << 20) 4k pages).
-        * Max GPUVM size for cayman and SI is 40 bits.
+        * Max GPUVM size for cayman+ is 40 bits.
         */
-       rdev->vm_manager.max_pfn = 1 << 20;
+       rdev->vm_manager.max_pfn = radeon_vm_size << 8;
 
        /* Set asic functions */
        r = radeon_asic_init(rdev);
        if (r)
                return r;
-       radeon_check_arguments(rdev);
 
        /* all of the newer IGP chips have an internal gart
         * However some rs4xx report as AGP, so remove that here.
index 356b733caafeb84093b75ba8ad00191ebfe4c5b5..5ed617056b9c4f4400fff1b770de25e2a08af0eb 100644 (file)
@@ -249,16 +249,21 @@ static void radeon_crtc_destroy(struct drm_crtc *crtc)
        struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
 
        drm_crtc_cleanup(crtc);
+       destroy_workqueue(radeon_crtc->flip_queue);
        kfree(radeon_crtc);
 }
 
-/*
- * Handle unpin events outside the interrupt handler proper.
+/**
+ * radeon_unpin_work_func - unpin old buffer object
+ *
+ * @__work - kernel work item
+ *
+ * Unpin the old frame buffer object outside of the interrupt handler
  */
 static void radeon_unpin_work_func(struct work_struct *__work)
 {
-       struct radeon_unpin_work *work =
-               container_of(__work, struct radeon_unpin_work, work);
+       struct radeon_flip_work *work =
+               container_of(__work, struct radeon_flip_work, unpin_work);
        int r;
 
        /* unpin of the old buffer */
@@ -276,10 +281,10 @@ static void radeon_unpin_work_func(struct work_struct *__work)
        kfree(work);
 }
 
-void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id)
+void radeon_crtc_handle_vblank(struct radeon_device *rdev, int crtc_id)
 {
        struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
-       struct radeon_unpin_work *work;
+       struct radeon_flip_work *work;
        unsigned long flags;
        u32 update_pending;
        int vpos, hpos;
@@ -289,24 +294,13 @@ void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id)
                return;
 
        spin_lock_irqsave(&rdev->ddev->event_lock, flags);
-       work = radeon_crtc->unpin_work;
-       if (work == NULL ||
-           (work->fence && !radeon_fence_signaled(work->fence))) {
+       work = radeon_crtc->flip_work;
+       if (work == NULL) {
                spin_unlock_irqrestore(&rdev->ddev->event_lock, flags);
                return;
        }
-       /* New pageflip, or just completion of a previous one? */
-       if (!radeon_crtc->deferred_flip_completion) {
-               /* do the flip (mmio) */
-               update_pending = radeon_page_flip(rdev, crtc_id, work->new_crtc_base);
-       } else {
-               /* This is just a completion of a flip queued in crtc
-                * at last invocation. Make sure we go directly to
-                * completion routine.
-                */
-               update_pending = 0;
-               radeon_crtc->deferred_flip_completion = 0;
-       }
+
+       update_pending = radeon_page_flip_pending(rdev, crtc_id);
 
        /* Has the pageflip already completed in crtc, or is it certain
         * to complete in this vblank?
@@ -324,19 +318,38 @@ void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id)
                 */
                update_pending = 0;
        }
-       if (update_pending) {
-               /* crtc didn't flip in this target vblank interval,
-                * but flip is pending in crtc. It will complete it
-                * in next vblank interval, so complete the flip at
-                * next vblank irq.
-                */
-               radeon_crtc->deferred_flip_completion = 1;
+       spin_unlock_irqrestore(&rdev->ddev->event_lock, flags);
+       if (!update_pending)
+               radeon_crtc_handle_flip(rdev, crtc_id);
+}
+
+/**
+ * radeon_crtc_handle_flip - page flip completed
+ *
+ * @rdev: radeon device pointer
+ * @crtc_id: crtc number this event is for
+ *
+ * Called when we are sure that a page flip for this crtc is completed.
+ */
+void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id)
+{
+       struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
+       struct radeon_flip_work *work;
+       unsigned long flags;
+
+       /* this can happen at init */
+       if (radeon_crtc == NULL)
+               return;
+
+       spin_lock_irqsave(&rdev->ddev->event_lock, flags);
+       work = radeon_crtc->flip_work;
+       if (work == NULL) {
                spin_unlock_irqrestore(&rdev->ddev->event_lock, flags);
                return;
        }
 
-       /* Pageflip (will be) certainly completed in this vblank. Clean up. */
-       radeon_crtc->unpin_work = NULL;
+       /* Pageflip completed. Clean up. */
+       radeon_crtc->flip_work = NULL;
 
        /* wakeup userspace */
        if (work->event)
@@ -344,86 +357,71 @@ void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id)
 
        spin_unlock_irqrestore(&rdev->ddev->event_lock, flags);
 
-       drm_vblank_put(rdev->ddev, radeon_crtc->crtc_id);
        radeon_fence_unref(&work->fence);
-       radeon_post_page_flip(work->rdev, work->crtc_id);
-       schedule_work(&work->work);
+       radeon_irq_kms_pflip_irq_get(rdev, work->crtc_id);
+       queue_work(radeon_crtc->flip_queue, &work->unpin_work);
 }
 
-static int radeon_crtc_page_flip(struct drm_crtc *crtc,
-                                struct drm_framebuffer *fb,
-                                struct drm_pending_vblank_event *event,
-                                uint32_t page_flip_flags)
+/**
+ * radeon_flip_work_func - page flip framebuffer
+ *
+ * @work - kernel work item
+ *
+ * Wait for the buffer object to become idle and do the actual page flip
+ */
+static void radeon_flip_work_func(struct work_struct *__work)
 {
-       struct drm_device *dev = crtc->dev;
-       struct radeon_device *rdev = dev->dev_private;
-       struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
-       struct radeon_framebuffer *old_radeon_fb;
-       struct radeon_framebuffer *new_radeon_fb;
-       struct drm_gem_object *obj;
-       struct radeon_bo *rbo;
-       struct radeon_unpin_work *work;
-       unsigned long flags;
-       u32 tiling_flags, pitch_pixels;
-       u64 base;
-       int r;
+       struct radeon_flip_work *work =
+               container_of(__work, struct radeon_flip_work, flip_work);
+       struct radeon_device *rdev = work->rdev;
+       struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[work->crtc_id];
 
-       work = kzalloc(sizeof *work, GFP_KERNEL);
-       if (work == NULL)
-               return -ENOMEM;
+       struct drm_crtc *crtc = &radeon_crtc->base;
+       struct drm_framebuffer *fb = work->fb;
 
-       work->event = event;
-       work->rdev = rdev;
-       work->crtc_id = radeon_crtc->crtc_id;
-       old_radeon_fb = to_radeon_framebuffer(crtc->primary->fb);
-       new_radeon_fb = to_radeon_framebuffer(fb);
-       /* schedule unpin of the old buffer */
-       obj = old_radeon_fb->obj;
-       /* take a reference to the old object */
-       drm_gem_object_reference(obj);
-       rbo = gem_to_radeon_bo(obj);
-       work->old_rbo = rbo;
-       obj = new_radeon_fb->obj;
-       rbo = gem_to_radeon_bo(obj);
+       uint32_t tiling_flags, pitch_pixels;
+       uint64_t base;
 
-       spin_lock(&rbo->tbo.bdev->fence_lock);
-       if (rbo->tbo.sync_obj)
-               work->fence = radeon_fence_ref(rbo->tbo.sync_obj);
-       spin_unlock(&rbo->tbo.bdev->fence_lock);
+       unsigned long flags;
+       int r;
 
-       INIT_WORK(&work->work, radeon_unpin_work_func);
+        down_read(&rdev->exclusive_lock);
+       while (work->fence) {
+               r = radeon_fence_wait(work->fence, false);
+               if (r == -EDEADLK) {
+                       up_read(&rdev->exclusive_lock);
+                       r = radeon_gpu_reset(rdev);
+                       down_read(&rdev->exclusive_lock);
+               }
 
-       /* We borrow the event spin lock for protecting unpin_work */
-       spin_lock_irqsave(&dev->event_lock, flags);
-       if (radeon_crtc->unpin_work) {
-               DRM_DEBUG_DRIVER("flip queue: crtc already busy\n");
-               r = -EBUSY;
-               goto unlock_free;
+               if (r) {
+                       DRM_ERROR("failed to wait on page flip fence (%d)!\n",
+                                 r);
+                       goto cleanup;
+               } else
+                       radeon_fence_unref(&work->fence);
        }
-       radeon_crtc->unpin_work = work;
-       radeon_crtc->deferred_flip_completion = 0;
-       spin_unlock_irqrestore(&dev->event_lock, flags);
 
        /* pin the new buffer */
        DRM_DEBUG_DRIVER("flip-ioctl() cur_fbo = %p, cur_bbo = %p\n",
-                        work->old_rbo, rbo);
+                        work->old_rbo, work->new_rbo);
 
-       r = radeon_bo_reserve(rbo, false);
+       r = radeon_bo_reserve(work->new_rbo, false);
        if (unlikely(r != 0)) {
                DRM_ERROR("failed to reserve new rbo buffer before flip\n");
-               goto pflip_cleanup;
+               goto cleanup;
        }
        /* Only 27 bit offset for legacy CRTC */
-       r = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM,
+       r = radeon_bo_pin_restricted(work->new_rbo, RADEON_GEM_DOMAIN_VRAM,
                                     ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27, &base);
        if (unlikely(r != 0)) {
-               radeon_bo_unreserve(rbo);
+               radeon_bo_unreserve(work->new_rbo);
                r = -EINVAL;
                DRM_ERROR("failed to pin new rbo buffer before flip\n");
-               goto pflip_cleanup;
+               goto cleanup;
        }
-       radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL);
-       radeon_bo_unreserve(rbo);
+       radeon_bo_get_tiling_flags(work->new_rbo, &tiling_flags, NULL);
+       radeon_bo_unreserve(work->new_rbo);
 
        if (!ASIC_IS_AVIVO(rdev)) {
                /* crtc offset is from display base addr not FB location */
@@ -461,44 +459,91 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc,
                base &= ~7;
        }
 
-       spin_lock_irqsave(&dev->event_lock, flags);
-       work->new_crtc_base = base;
-       spin_unlock_irqrestore(&dev->event_lock, flags);
+       /* We borrow the event spin lock for protecting flip_work */
+       spin_lock_irqsave(&crtc->dev->event_lock, flags);
 
-       /* update crtc fb */
-       crtc->primary->fb = fb;
+       /* set the proper interrupt */
+       radeon_irq_kms_pflip_irq_get(rdev, radeon_crtc->crtc_id);
 
-       r = drm_vblank_get(dev, radeon_crtc->crtc_id);
-       if (r) {
-               DRM_ERROR("failed to get vblank before flip\n");
-               goto pflip_cleanup1;
-       }
+       /* do the flip (mmio) */
+       radeon_page_flip(rdev, radeon_crtc->crtc_id, base);
 
-       /* set the proper interrupt */
-       radeon_pre_page_flip(rdev, radeon_crtc->crtc_id);
+       spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+       up_read(&rdev->exclusive_lock);
 
-       return 0;
+       return;
 
-pflip_cleanup1:
-       if (unlikely(radeon_bo_reserve(rbo, false) != 0)) {
-               DRM_ERROR("failed to reserve new rbo in error path\n");
-               goto pflip_cleanup;
-       }
-       if (unlikely(radeon_bo_unpin(rbo) != 0)) {
-               DRM_ERROR("failed to unpin new rbo in error path\n");
-       }
-       radeon_bo_unreserve(rbo);
-
-pflip_cleanup:
-       spin_lock_irqsave(&dev->event_lock, flags);
-       radeon_crtc->unpin_work = NULL;
-unlock_free:
-       spin_unlock_irqrestore(&dev->event_lock, flags);
-       drm_gem_object_unreference_unlocked(old_radeon_fb->obj);
+cleanup:
+       drm_gem_object_unreference_unlocked(&work->old_rbo->gem_base);
        radeon_fence_unref(&work->fence);
        kfree(work);
+       up_read(&rdev->exclusive_lock);
+}
 
-       return r;
+static int radeon_crtc_page_flip(struct drm_crtc *crtc,
+                                struct drm_framebuffer *fb,
+                                struct drm_pending_vblank_event *event,
+                                uint32_t page_flip_flags)
+{
+       struct drm_device *dev = crtc->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+       struct radeon_framebuffer *old_radeon_fb;
+       struct radeon_framebuffer *new_radeon_fb;
+       struct drm_gem_object *obj;
+       struct radeon_flip_work *work;
+       unsigned long flags;
+
+       work = kzalloc(sizeof *work, GFP_KERNEL);
+       if (work == NULL)
+               return -ENOMEM;
+
+       INIT_WORK(&work->flip_work, radeon_flip_work_func);
+       INIT_WORK(&work->unpin_work, radeon_unpin_work_func);
+
+       work->rdev = rdev;
+       work->crtc_id = radeon_crtc->crtc_id;
+       work->fb = fb;
+       work->event = event;
+
+       /* schedule unpin of the old buffer */
+       old_radeon_fb = to_radeon_framebuffer(crtc->primary->fb);
+       obj = old_radeon_fb->obj;
+
+       /* take a reference to the old object */
+       drm_gem_object_reference(obj);
+       work->old_rbo = gem_to_radeon_bo(obj);
+
+       new_radeon_fb = to_radeon_framebuffer(fb);
+       obj = new_radeon_fb->obj;
+       work->new_rbo = gem_to_radeon_bo(obj);
+
+       spin_lock(&work->new_rbo->tbo.bdev->fence_lock);
+       if (work->new_rbo->tbo.sync_obj)
+               work->fence = radeon_fence_ref(work->new_rbo->tbo.sync_obj);
+       spin_unlock(&work->new_rbo->tbo.bdev->fence_lock);
+
+       /* We borrow the event spin lock for protecting flip_work */
+       spin_lock_irqsave(&crtc->dev->event_lock, flags);
+
+       if (radeon_crtc->flip_work) {
+               DRM_DEBUG_DRIVER("flip queue: crtc already busy\n");
+               spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+               drm_gem_object_unreference_unlocked(&work->old_rbo->gem_base);
+               radeon_fence_unref(&work->fence);
+               kfree(work);
+               return -EBUSY;
+       }
+       radeon_crtc->flip_work = work;
+
+       /* update crtc fb */
+       crtc->primary->fb = fb;
+
+       spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+
+       queue_work(radeon_crtc->flip_queue, &work->flip_work);
+
+       return 0;
 }
 
 static int
@@ -568,6 +613,7 @@ static void radeon_crtc_init(struct drm_device *dev, int index)
 
        drm_mode_crtc_set_gamma_size(&radeon_crtc->base, 256);
        radeon_crtc->crtc_id = index;
+       radeon_crtc->flip_queue = create_singlethread_workqueue("radeon-crtc");
        rdev->mode_info.crtcs[index] = radeon_crtc;
 
        if (rdev->family >= CHIP_BONAIRE) {
@@ -661,7 +707,7 @@ static void radeon_print_display_setup(struct drm_device *dev)
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                radeon_connector = to_radeon_connector(connector);
                DRM_INFO("Connector %d:\n", i);
-               DRM_INFO("  %s\n", drm_get_connector_name(connector));
+               DRM_INFO("  %s\n", connector->name);
                if (radeon_connector->hpd.hpd != RADEON_HPD_NONE)
                        DRM_INFO("  %s\n", hpd_names[radeon_connector->hpd.hpd]);
                if (radeon_connector->ddc_bus) {
index c00a2f58518502df5620e02af532f3dd725fa3cf..6e301741338689abf0328ced81d1118ec5b4dbdd 100644 (file)
  *   2.37.0 - allow GS ring setup on r6xx/r7xx
  *   2.38.0 - RADEON_GEM_OP (GET_INITIAL_DOMAIN, SET_INITIAL_DOMAIN),
  *            CIK: 1D and linear tiling modes contain valid PIPE_CONFIG
+ *   2.39.0 - Add INFO query for number of active CUs
  */
 #define KMS_DRIVER_MAJOR       2
-#define KMS_DRIVER_MINOR       38
+#define KMS_DRIVER_MINOR       39
 #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);
@@ -172,6 +173,8 @@ int radeon_dpm = -1;
 int radeon_aspm = -1;
 int radeon_runtime_pm = -1;
 int radeon_hard_reset = 0;
+int radeon_vm_size = 4096;
+int radeon_vm_block_size = 9;
 
 MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers");
 module_param_named(no_wb, radeon_no_wb, int, 0444);
@@ -239,6 +242,12 @@ module_param_named(runpm, radeon_runtime_pm, int, 0444);
 MODULE_PARM_DESC(hard_reset, "PCI config reset (1 = force enable, 0 = disable (default))");
 module_param_named(hard_reset, radeon_hard_reset, int, 0444);
 
+MODULE_PARM_DESC(vm_size, "VM address space size in megabytes (default 4GB)");
+module_param_named(vm_size, radeon_vm_size, int, 0444);
+
+MODULE_PARM_DESC(vm_block_size, "VM page table size in bits (default 9)");
+module_param_named(vm_block_size, radeon_vm_block_size, int, 0444);
+
 static struct pci_device_id pciidlist[] = {
        radeon_PCI_IDS
 };
@@ -519,7 +528,6 @@ static struct drm_driver kms_driver = {
            DRIVER_USE_AGP |
            DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM |
            DRIVER_PRIME | DRIVER_RENDER,
-       .dev_priv_size = 0,
        .load = radeon_driver_load_kms,
        .open = radeon_driver_open_kms,
        .preclose = radeon_driver_preclose_kms,
index a77b1c13ea43d6bea244e0da8666c82bb082ea60..913787085dfadf56cfd21ed5b771c072c78cd072 100644 (file)
@@ -819,15 +819,35 @@ static int radeon_debugfs_fence_info(struct seq_file *m, void *data)
        return 0;
 }
 
+/**
+ * radeon_debugfs_gpu_reset - manually trigger a gpu reset
+ *
+ * Manually trigger a gpu reset at the next fence wait.
+ */
+static int radeon_debugfs_gpu_reset(struct seq_file *m, void *data)
+{
+       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_device *dev = node->minor->dev;
+       struct radeon_device *rdev = dev->dev_private;
+
+       down_read(&rdev->exclusive_lock);
+       seq_printf(m, "%d\n", rdev->needs_reset);
+       rdev->needs_reset = true;
+       up_read(&rdev->exclusive_lock);
+
+       return 0;
+}
+
 static struct drm_info_list radeon_debugfs_fence_list[] = {
        {"radeon_fence_info", &radeon_debugfs_fence_info, 0, NULL},
+       {"radeon_gpu_reset", &radeon_debugfs_gpu_reset, 0, NULL}
 };
 #endif
 
 int radeon_debugfs_fence_init(struct radeon_device *rdev)
 {
 #if defined(CONFIG_DEBUG_FS)
-       return radeon_debugfs_add_files(rdev, radeon_debugfs_fence_list, 1);
+       return radeon_debugfs_add_files(rdev, radeon_debugfs_fence_list, 2);
 #else
        return 0;
 #endif
index 7b944142a9fdd4e74646377e78fcc1af8f039769..add622008407e77b18955802ad1eee39ae994cfa 100644 (file)
@@ -94,6 +94,8 @@ static int pre_xfer(struct i2c_adapter *i2c_adap)
        struct radeon_i2c_bus_rec *rec = &i2c->rec;
        uint32_t temp;
 
+       mutex_lock(&i2c->mutex);
+
        /* RV410 appears to have a bug where the hw i2c in reset
         * holds the i2c port in a bad state - switch hw i2c away before
         * doing DDC - do this for all r200s/r300s/r400s for safety sake
@@ -170,6 +172,8 @@ static void post_xfer(struct i2c_adapter *i2c_adap)
        temp = RREG32(rec->mask_data_reg) & ~rec->mask_data_mask;
        WREG32(rec->mask_data_reg, temp);
        temp = RREG32(rec->mask_data_reg);
+
+       mutex_unlock(&i2c->mutex);
 }
 
 static int get_clock(void *i2c_priv)
@@ -813,6 +817,8 @@ static int radeon_hw_i2c_xfer(struct i2c_adapter *i2c_adap,
        struct radeon_i2c_bus_rec *rec = &i2c->rec;
        int ret = 0;
 
+       mutex_lock(&i2c->mutex);
+
        switch (rdev->family) {
        case CHIP_R100:
        case CHIP_RV100:
@@ -879,6 +885,8 @@ static int radeon_hw_i2c_xfer(struct i2c_adapter *i2c_adap,
                break;
        }
 
+       mutex_unlock(&i2c->mutex);
+
        return ret;
 }
 
@@ -919,6 +927,7 @@ struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
        i2c->adapter.dev.parent = &dev->pdev->dev;
        i2c->dev = dev;
        i2c_set_adapdata(&i2c->adapter, i2c);
+       mutex_init(&i2c->mutex);
        if (rec->mm_i2c ||
            (rec->hw_capable &&
             radeon_hw_i2c &&
@@ -979,7 +988,7 @@ void radeon_i2c_destroy(struct radeon_i2c_chan *i2c)
                return;
        i2c_del_adapter(&i2c->adapter);
        if (i2c->has_aux)
-               drm_dp_aux_unregister_i2c_bus(&i2c->aux);
+               drm_dp_aux_unregister(&i2c->aux);
        kfree(i2c);
 }
 
index bdb0f93e73bcfddcea63d1f8e284af05da742e91..0b98ea1345792986fa064f878e40aaa13388c7de 100644 (file)
@@ -399,7 +399,7 @@ long radeon_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
        if (nr < DRM_COMMAND_BASE)
                return drm_compat_ioctl(filp, cmd, arg);
 
-       if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(radeon_compat_ioctls))
+       if (nr < DRM_COMMAND_BASE + ARRAY_SIZE(radeon_compat_ioctls))
                fn = radeon_compat_ioctls[nr - DRM_COMMAND_BASE];
 
        if (fn != NULL)
index 089c9ffb0aa95e8e47c964f190b07e0e8fe93c32..16807afab362509f8432bf4a021d3954e747c301 100644 (file)
@@ -287,7 +287,7 @@ int radeon_irq_kms_init(struct radeon_device *rdev)
        INIT_WORK(&rdev->reset_work, radeon_irq_reset_work_func);
 
        rdev->irq.installed = true;
-       r = drm_irq_install(rdev->ddev);
+       r = drm_irq_install(rdev->ddev, rdev->ddev->pdev->irq);
        if (r) {
                rdev->irq.installed = false;
                flush_work(&rdev->hotplug_work);
index eaaedba0467595aaced591c6d70666c75ac97211..35d931881b4b80bb5f6989580e993af8796e7041 100644 (file)
@@ -513,6 +513,22 @@ static int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file
                value_size = sizeof(uint64_t);
                value64 = atomic64_read(&rdev->gtt_usage);
                break;
+       case RADEON_INFO_ACTIVE_CU_COUNT:
+               if (rdev->family >= CHIP_BONAIRE)
+                       *value = rdev->config.cik.active_cus;
+               else if (rdev->family >= CHIP_TAHITI)
+                       *value = rdev->config.si.active_cus;
+               else if (rdev->family >= CHIP_CAYMAN)
+                       *value = rdev->config.cayman.active_simds;
+               else if (rdev->family >= CHIP_CEDAR)
+                       *value = rdev->config.evergreen.active_simds;
+               else if (rdev->family >= CHIP_RV770)
+                       *value = rdev->config.rv770.active_simds;
+               else if (rdev->family >= CHIP_R600)
+                       *value = rdev->config.r600.active_simds;
+               else
+                       *value = 1;
+               break;
        default:
                DRM_DEBUG_KMS("Invalid request %d\n", info->request);
                return -EINVAL;
@@ -859,4 +875,4 @@ const struct drm_ioctl_desc radeon_ioctls_kms[] = {
        DRM_IOCTL_DEF_DRV(RADEON_GEM_VA, radeon_gem_va_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
        DRM_IOCTL_DEF_DRV(RADEON_GEM_OP, radeon_gem_op_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
 };
-int radeon_max_kms_ioctl = DRM_ARRAY_SIZE(radeon_ioctls_kms);
+int radeon_max_kms_ioctl = ARRAY_SIZE(radeon_ioctls_kms);
index 6ddf31a2d34e01f685411f5ef456c74b676fecc0..ad0e4b8cc7e3e63530e14c1f1144fa76ea9df830 100644 (file)
@@ -191,6 +191,7 @@ struct radeon_i2c_chan {
        struct radeon_i2c_bus_rec rec;
        struct drm_dp_aux aux;
        bool has_aux;
+       struct mutex mutex;
 };
 
 /* mostly for macs, but really any system without connector tables */
@@ -324,8 +325,8 @@ struct radeon_crtc {
        struct drm_display_mode native_mode;
        int pll_id;
        /* page flipping */
-       struct radeon_unpin_work *unpin_work;
-       int deferred_flip_completion;
+       struct workqueue_struct *flip_queue;
+       struct radeon_flip_work *flip_work;
        /* pll sharing */
        struct radeon_atom_ss ss;
        bool ss_enabled;
@@ -505,6 +506,7 @@ struct radeon_connector {
        struct radeon_i2c_chan *router_bus;
        enum radeon_connector_audio audio;
        enum radeon_connector_dither dither;
+       int pixelclock_for_modeset;
 };
 
 struct radeon_framebuffer {
@@ -906,6 +908,7 @@ bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj)
 
 void radeon_fb_output_poll_changed(struct radeon_device *rdev);
 
+void radeon_crtc_handle_vblank(struct radeon_device *rdev, int crtc_id);
 void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id);
 
 int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled);
index 4faa4d6f9bb4f0616e0575d916069b47fa9389ed..6c717b257d6d5c8e683ad9cbf3e88e40e9bf5557 100644 (file)
@@ -446,7 +446,7 @@ int radeon_bo_list_validate(struct radeon_device *rdev,
        list_for_each_entry(lobj, head, tv.head) {
                bo = lobj->robj;
                if (!bo->pin_count) {
-                       u32 domain = lobj->domain;
+                       u32 domain = lobj->prefered_domains;
                        u32 current_domain =
                                radeon_mem_type_to_domain(bo->tbo.mem.mem_type);
 
@@ -458,7 +458,7 @@ int radeon_bo_list_validate(struct radeon_device *rdev,
                         * into account. We don't want to disallow buffer moves
                         * completely.
                         */
-                       if ((lobj->alt_domain & current_domain) != 0 &&
+                       if ((lobj->allowed_domains & current_domain) != 0 &&
                            (domain & current_domain) == 0 && /* will be moved */
                            bytes_moved > bytes_moved_threshold) {
                                /* don't move it */
@@ -476,8 +476,9 @@ int radeon_bo_list_validate(struct radeon_device *rdev,
                                       initial_bytes_moved;
 
                        if (unlikely(r)) {
-                               if (r != -ERESTARTSYS && domain != lobj->alt_domain) {
-                                       domain = lobj->alt_domain;
+                               if (r != -ERESTARTSYS &&
+                                   domain != lobj->allowed_domains) {
+                                       domain = lobj->allowed_domains;
                                        goto retry;
                                }
                                ttm_eu_backoff_reservation(ticket, head);
@@ -730,7 +731,7 @@ int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type, bool no_wait)
 {
        int r;
 
-       r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0);
+       r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, NULL);
        if (unlikely(r != 0))
                return r;
        spin_lock(&bo->tbo.bdev->fence_lock);
index 9e7b25a0629d3a249720628aa697b9ff8a0f4a6f..5a873f31a17100289469fe465e53abba75addd51 100644 (file)
@@ -65,7 +65,7 @@ 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);
+       r = ttm_bo_reserve(&bo->tbo, !no_intr, false, false, NULL);
        if (unlikely(r != 0)) {
                if (r != -ERESTARTSYS)
                        dev_err(bo->rdev->dev, "%p reserve failed\n", bo);
index 2bdae61c0ac063f8ce5c95571f2f8e6d55d220e8..12c663e86ca18e14f9af5ea2e17a2e0704c38953 100644 (file)
@@ -984,6 +984,8 @@ void radeon_dpm_enable_uvd(struct radeon_device *rdev, bool enable)
                if (enable) {
                        mutex_lock(&rdev->pm.mutex);
                        rdev->pm.dpm.uvd_active = true;
+                       /* disable this for now */
+#if 0
                        if ((rdev->pm.dpm.sd == 1) && (rdev->pm.dpm.hd == 0))
                                dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_SD;
                        else if ((rdev->pm.dpm.sd == 2) && (rdev->pm.dpm.hd == 0))
@@ -993,6 +995,7 @@ void radeon_dpm_enable_uvd(struct radeon_device *rdev, bool enable)
                        else if ((rdev->pm.dpm.sd == 0) && (rdev->pm.dpm.hd == 2))
                                dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD2;
                        else
+#endif
                                dpm_state = POWER_STATE_TYPE_INTERNAL_UVD;
                        rdev->pm.dpm.state = dpm_state;
                        mutex_unlock(&rdev->pm.mutex);
index 956ab7f14e1650607c8dd09f1b9c96daaee3169a..23bb64fd775f674cae5091013b4d63979f62ebeb 100644 (file)
@@ -3054,7 +3054,7 @@ static int radeon_cp_getparam(struct drm_device *dev, void *data, struct drm_fil
                if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)
                        value = 0;
                else
-                       value = drm_dev_to_irq(dev);
+                       value = dev->pdev->irq;
                break;
        case RADEON_PARAM_GART_BASE:
                value = dev_priv->gart_vm_start;
@@ -3258,4 +3258,4 @@ struct drm_ioctl_desc radeon_ioctls[] = {
        DRM_IOCTL_DEF_DRV(RADEON_CS, r600_cs_legacy_ioctl, DRM_AUTH)
 };
 
-int radeon_max_ioctl = DRM_ARRAY_SIZE(radeon_ioctls);
+int radeon_max_ioctl = ARRAY_SIZE(radeon_ioctls);
index 1b65ae2433cd0e4063a546cb9806f6d917ba83a9..a4ad270e82611b8078711b87df44c44ab3feef26 100644 (file)
@@ -812,7 +812,8 @@ void radeon_uvd_note_usage(struct radeon_device *rdev)
                    (rdev->pm.dpm.hd != hd)) {
                        rdev->pm.dpm.sd = sd;
                        rdev->pm.dpm.hd = hd;
-                       streams_changed = true;
+                       /* disable this for now */
+                       /*streams_changed = true;*/
                }
        }
 
index 3971d968af6c0d86d0ba6e6f08f94d26714e17ec..aa21c31a846cfa597173d308fa889d70ce1cc698 100644 (file)
@@ -66,6 +66,7 @@ int radeon_vce_init(struct radeon_device *rdev)
        case CHIP_BONAIRE:
        case CHIP_KAVERI:
        case CHIP_KABINI:
+       case CHIP_HAWAII:
        case CHIP_MULLINS:
                fw_name = FIRMWARE_BONAIRE;
                break;
index c11b71d249e38afa1d1d7d815c0b85f301c41db6..899d9126cad6da114333ee2550710c8f21227896 100644 (file)
@@ -59,7 +59,7 @@
  */
 static unsigned radeon_vm_num_pdes(struct radeon_device *rdev)
 {
-       return rdev->vm_manager.max_pfn >> RADEON_VM_BLOCK_SIZE;
+       return rdev->vm_manager.max_pfn >> radeon_vm_block_size;
 }
 
 /**
@@ -140,8 +140,8 @@ struct radeon_cs_reloc *radeon_vm_get_bos(struct radeon_device *rdev,
        /* add the vm page table to the list */
        list[0].gobj = NULL;
        list[0].robj = vm->page_directory;
-       list[0].domain = RADEON_GEM_DOMAIN_VRAM;
-       list[0].alt_domain = RADEON_GEM_DOMAIN_VRAM;
+       list[0].prefered_domains = RADEON_GEM_DOMAIN_VRAM;
+       list[0].allowed_domains = RADEON_GEM_DOMAIN_VRAM;
        list[0].tv.bo = &vm->page_directory->tbo;
        list[0].tiling_flags = 0;
        list[0].handle = 0;
@@ -153,8 +153,8 @@ struct radeon_cs_reloc *radeon_vm_get_bos(struct radeon_device *rdev,
 
                list[idx].gobj = NULL;
                list[idx].robj = vm->page_tables[i].bo;
-               list[idx].domain = RADEON_GEM_DOMAIN_VRAM;
-               list[idx].alt_domain = RADEON_GEM_DOMAIN_VRAM;
+               list[idx].prefered_domains = RADEON_GEM_DOMAIN_VRAM;
+               list[idx].allowed_domains = RADEON_GEM_DOMAIN_VRAM;
                list[idx].tv.bo = &list[idx].robj->tbo;
                list[idx].tiling_flags = 0;
                list[idx].handle = 0;
@@ -474,8 +474,10 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev,
        bo_va->valid = false;
        list_move(&bo_va->vm_list, head);
 
-       soffset = (soffset / RADEON_GPU_PAGE_SIZE) >> RADEON_VM_BLOCK_SIZE;
-       eoffset = (eoffset / RADEON_GPU_PAGE_SIZE) >> RADEON_VM_BLOCK_SIZE;
+       soffset = (soffset / RADEON_GPU_PAGE_SIZE) >> radeon_vm_block_size;
+       eoffset = (eoffset / RADEON_GPU_PAGE_SIZE) >> radeon_vm_block_size;
+
+       BUG_ON(eoffset >= radeon_vm_num_pdes(rdev));
 
        if (eoffset > vm->max_pde_used)
                vm->max_pde_used = eoffset;
@@ -583,10 +585,9 @@ static uint32_t radeon_vm_page_flags(uint32_t flags)
 int radeon_vm_update_page_directory(struct radeon_device *rdev,
                                    struct radeon_vm *vm)
 {
-       static const uint32_t incr = RADEON_VM_PTE_COUNT * 8;
-
        struct radeon_bo *pd = vm->page_directory;
        uint64_t pd_addr = radeon_bo_gpu_offset(pd);
+       uint32_t incr = RADEON_VM_PTE_COUNT * 8;
        uint64_t last_pde = ~0, last_pt = ~0;
        unsigned count = 0, pt_idx, ndw;
        struct radeon_ib ib;
@@ -659,6 +660,84 @@ int radeon_vm_update_page_directory(struct radeon_device *rdev,
        return 0;
 }
 
+/**
+ * radeon_vm_frag_ptes - add fragment information to PTEs
+ *
+ * @rdev: radeon_device pointer
+ * @ib: IB for the update
+ * @pe_start: first PTE to handle
+ * @pe_end: last PTE to handle
+ * @addr: addr those PTEs should point to
+ * @flags: hw mapping flags
+ *
+ * Global and local mutex must be locked!
+ */
+static void radeon_vm_frag_ptes(struct radeon_device *rdev,
+                               struct radeon_ib *ib,
+                               uint64_t pe_start, uint64_t pe_end,
+                               uint64_t addr, uint32_t flags)
+{
+       /**
+        * The MC L1 TLB supports variable sized pages, based on a fragment
+        * field in the PTE. When this field is set to a non-zero value, page
+        * granularity is increased from 4KB to (1 << (12 + frag)). The PTE
+        * flags are considered valid for all PTEs within the fragment range
+        * and corresponding mappings are assumed to be physically contiguous.
+        *
+        * The L1 TLB can store a single PTE for the whole fragment,
+        * significantly increasing the space available for translation
+        * caching. This leads to large improvements in throughput when the
+        * TLB is under pressure.
+        *
+        * The L2 TLB distributes small and large fragments into two
+        * asymmetric partitions. The large fragment cache is significantly
+        * larger. Thus, we try to use large fragments wherever possible.
+        * Userspace can support this by aligning virtual base address and
+        * allocation size to the fragment size.
+        */
+
+       /* NI is optimized for 256KB fragments, SI and newer for 64KB */
+       uint64_t frag_flags = rdev->family == CHIP_CAYMAN ?
+                       R600_PTE_FRAG_256KB : R600_PTE_FRAG_64KB;
+       uint64_t frag_align = rdev->family == CHIP_CAYMAN ? 0x200 : 0x80;
+
+       uint64_t frag_start = ALIGN(pe_start, frag_align);
+       uint64_t frag_end = pe_end & ~(frag_align - 1);
+
+       unsigned count;
+
+       /* system pages are non continuously */
+       if ((flags & R600_PTE_SYSTEM) || !(flags & R600_PTE_VALID) ||
+           (frag_start >= frag_end)) {
+
+               count = (pe_end - pe_start) / 8;
+               radeon_asic_vm_set_page(rdev, ib, pe_start, addr, count,
+                                       RADEON_GPU_PAGE_SIZE, flags);
+               return;
+       }
+
+       /* handle the 4K area at the beginning */
+       if (pe_start != frag_start) {
+               count = (frag_start - pe_start) / 8;
+               radeon_asic_vm_set_page(rdev, ib, pe_start, addr, count,
+                                       RADEON_GPU_PAGE_SIZE, flags);
+               addr += RADEON_GPU_PAGE_SIZE * count;
+       }
+
+       /* handle the area in the middle */
+       count = (frag_end - frag_start) / 8;
+       radeon_asic_vm_set_page(rdev, ib, frag_start, addr, count,
+                               RADEON_GPU_PAGE_SIZE, flags | frag_flags);
+
+       /* handle the 4K area at the end */
+       if (frag_end != pe_end) {
+               addr += RADEON_GPU_PAGE_SIZE * count;
+               count = (pe_end - frag_end) / 8;
+               radeon_asic_vm_set_page(rdev, ib, frag_end, addr, count,
+                                       RADEON_GPU_PAGE_SIZE, flags);
+       }
+}
+
 /**
  * radeon_vm_update_ptes - make sure that page tables are valid
  *
@@ -679,8 +758,7 @@ static void radeon_vm_update_ptes(struct radeon_device *rdev,
                                  uint64_t start, uint64_t end,
                                  uint64_t dst, uint32_t flags)
 {
-       static const uint64_t mask = RADEON_VM_PTE_COUNT - 1;
-
+       uint64_t mask = RADEON_VM_PTE_COUNT - 1;
        uint64_t last_pte = ~0, last_dst = ~0;
        unsigned count = 0;
        uint64_t addr;
@@ -690,7 +768,7 @@ static void radeon_vm_update_ptes(struct radeon_device *rdev,
 
        /* walk over the address space and update the page tables */
        for (addr = start; addr < end; ) {
-               uint64_t pt_idx = addr >> RADEON_VM_BLOCK_SIZE;
+               uint64_t pt_idx = addr >> radeon_vm_block_size;
                struct radeon_bo *pt = vm->page_tables[pt_idx].bo;
                unsigned nptes;
                uint64_t pte;
@@ -708,10 +786,9 @@ static void radeon_vm_update_ptes(struct radeon_device *rdev,
                if ((last_pte + 8 * count) != pte) {
 
                        if (count) {
-                               radeon_asic_vm_set_page(rdev, ib, last_pte,
-                                                       last_dst, count,
-                                                       RADEON_GPU_PAGE_SIZE,
-                                                       flags);
+                               radeon_vm_frag_ptes(rdev, ib, last_pte,
+                                                   last_pte + 8 * count,
+                                                   last_dst, flags);
                        }
 
                        count = nptes;
@@ -726,9 +803,9 @@ static void radeon_vm_update_ptes(struct radeon_device *rdev,
        }
 
        if (count) {
-               radeon_asic_vm_set_page(rdev, ib, last_pte,
-                                       last_dst, count,
-                                       RADEON_GPU_PAGE_SIZE, flags);
+               radeon_vm_frag_ptes(rdev, ib, last_pte,
+                                   last_pte + 8 * count,
+                                   last_dst, flags);
        }
 }
 
@@ -796,13 +873,13 @@ int radeon_vm_bo_update(struct radeon_device *rdev,
        /* padding, etc. */
        ndw = 64;
 
-       if (RADEON_VM_BLOCK_SIZE > 11)
+       if (radeon_vm_block_size > 11)
                /* reserve space for one header for every 2k dwords */
                ndw += (nptes >> 11) * 4;
        else
                /* reserve space for one header for
                    every (1 << BLOCK_SIZE) entries */
-               ndw += (nptes >> RADEON_VM_BLOCK_SIZE) * 4;
+               ndw += (nptes >> radeon_vm_block_size) * 4;
 
        /* reserve space for pte addresses */
        ndw += nptes * 2;
@@ -892,6 +969,8 @@ void radeon_vm_bo_invalidate(struct radeon_device *rdev,
  */
 int radeon_vm_init(struct radeon_device *rdev, struct radeon_vm *vm)
 {
+       const unsigned align = min(RADEON_VM_PTB_ALIGN_SIZE,
+               RADEON_VM_PTE_COUNT * 8);
        unsigned pd_size, pd_entries, pts_size;
        int r;
 
@@ -913,7 +992,7 @@ int radeon_vm_init(struct radeon_device *rdev, struct radeon_vm *vm)
                return -ENOMEM;
        }
 
-       r = radeon_bo_create(rdev, pd_size, RADEON_VM_PTB_ALIGN_SIZE, false,
+       r = radeon_bo_create(rdev, pd_size, align, false,
                             RADEON_GEM_DOMAIN_VRAM, NULL,
                             &vm->page_directory);
        if (r)
index 130d5cc50d436fd90c1d3055bd03e177ba2dce6c..a0f96decece3c31def216611c9bab18fa836c464 100644 (file)
@@ -212,21 +212,16 @@ void rs400_gart_fini(struct radeon_device *rdev)
 #define RS400_PTE_WRITEABLE (1 << 2)
 #define RS400_PTE_READABLE  (1 << 3)
 
-int rs400_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr)
+void rs400_gart_set_page(struct radeon_device *rdev, unsigned i, uint64_t addr)
 {
        uint32_t entry;
        u32 *gtt = rdev->gart.ptr;
 
-       if (i < 0 || i > rdev->gart.num_gpu_pages) {
-               return -EINVAL;
-       }
-
        entry = (lower_32_bits(addr) & PAGE_MASK) |
                ((upper_32_bits(addr) & 0xff) << 4) |
                RS400_PTE_WRITEABLE | RS400_PTE_READABLE;
        entry = cpu_to_le32(entry);
        gtt[i] = entry;
-       return 0;
 }
 
 int rs400_mc_wait_for_idle(struct radeon_device *rdev)
index 72d3616de08e054efe6199fcd529f59cef3e18d5..d1a35cb1c91d4b6bc60e18482018612a323489a3 100644 (file)
@@ -109,19 +109,7 @@ void avivo_wait_for_vblank(struct radeon_device *rdev, int crtc)
        }
 }
 
-void rs600_pre_page_flip(struct radeon_device *rdev, int crtc)
-{
-       /* enable the pflip int */
-       radeon_irq_kms_pflip_irq_get(rdev, crtc);
-}
-
-void rs600_post_page_flip(struct radeon_device *rdev, int crtc)
-{
-       /* disable the pflip int */
-       radeon_irq_kms_pflip_irq_put(rdev, crtc);
-}
-
-u32 rs600_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
+void rs600_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
 {
        struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
        u32 tmp = RREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset);
@@ -148,9 +136,15 @@ u32 rs600_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
        /* Unlock the lock, so double-buffering can take place inside vblank */
        tmp &= ~AVIVO_D1GRPH_UPDATE_LOCK;
        WREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset, tmp);
+}
+
+bool rs600_page_flip_pending(struct radeon_device *rdev, int crtc_id)
+{
+       struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
 
        /* Return current update_pending status: */
-       return RREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset) & AVIVO_D1GRPH_SURFACE_UPDATE_PENDING;
+       return !!(RREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset) &
+               AVIVO_D1GRPH_SURFACE_UPDATE_PENDING);
 }
 
 void avivo_program_fmt(struct drm_encoder *encoder)
@@ -632,24 +626,16 @@ static void rs600_gart_fini(struct radeon_device *rdev)
        radeon_gart_table_vram_free(rdev);
 }
 
-#define R600_PTE_VALID     (1 << 0)
-#define R600_PTE_SYSTEM    (1 << 1)
-#define R600_PTE_SNOOPED   (1 << 2)
-#define R600_PTE_READABLE  (1 << 5)
-#define R600_PTE_WRITEABLE (1 << 6)
-
-int rs600_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr)
+void rs600_gart_set_page(struct radeon_device *rdev, unsigned i, uint64_t addr)
 {
        void __iomem *ptr = (void *)rdev->gart.ptr;
 
-       if (i < 0 || i > rdev->gart.num_gpu_pages) {
-               return -EINVAL;
-       }
        addr = addr & 0xFFFFFFFFFFFFF000ULL;
-       addr |= R600_PTE_VALID | R600_PTE_SYSTEM | R600_PTE_SNOOPED;
-       addr |= R600_PTE_READABLE | R600_PTE_WRITEABLE;
+       if (addr == rdev->dummy_page.addr)
+               addr |= R600_PTE_SYSTEM | R600_PTE_SNOOPED;
+       else
+               addr |= R600_PTE_GART;
        writeq(addr, ptr + (i * 8));
-       return 0;
 }
 
 int rs600_irq_set(struct radeon_device *rdev)
@@ -787,7 +773,7 @@ int rs600_irq_process(struct radeon_device *rdev)
                                wake_up(&rdev->irq.vblank_queue);
                        }
                        if (atomic_read(&rdev->irq.pflip[0]))
-                               radeon_crtc_handle_flip(rdev, 0);
+                               radeon_crtc_handle_vblank(rdev, 0);
                }
                if (G_007EDC_LB_D2_VBLANK_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) {
                        if (rdev->irq.crtc_vblank_int[1]) {
@@ -796,7 +782,7 @@ int rs600_irq_process(struct radeon_device *rdev)
                                wake_up(&rdev->irq.vblank_queue);
                        }
                        if (atomic_read(&rdev->irq.pflip[1]))
-                               radeon_crtc_handle_flip(rdev, 1);
+                               radeon_crtc_handle_vblank(rdev, 1);
                }
                if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) {
                        queue_hotplug = true;
index fef310773aadc71260888372b896df0afcced749..da8703d8d4559920dd382164bee462bddacb89c0 100644 (file)
@@ -801,7 +801,7 @@ u32 rv770_get_xclk(struct radeon_device *rdev)
        return reference_clock;
 }
 
-u32 rv770_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
+void rv770_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
 {
        struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
        u32 tmp = RREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset);
@@ -835,9 +835,15 @@ u32 rv770_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
        /* Unlock the lock, so double-buffering can take place inside vblank */
        tmp &= ~AVIVO_D1GRPH_UPDATE_LOCK;
        WREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset, tmp);
+}
+
+bool rv770_page_flip_pending(struct radeon_device *rdev, int crtc_id)
+{
+       struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
 
        /* Return current update_pending status: */
-       return RREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset) & AVIVO_D1GRPH_SURFACE_UPDATE_PENDING;
+       return !!(RREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset) &
+               AVIVO_D1GRPH_SURFACE_UPDATE_PENDING);
 }
 
 /* get temperature in millidegrees */
@@ -1321,6 +1327,9 @@ static void rv770_gpu_init(struct radeon_device *rdev)
        if (tmp < rdev->config.rv770.max_simds) {
                rdev->config.rv770.max_simds = tmp;
        }
+       tmp = rdev->config.rv770.max_simds -
+               r600_count_pipe_bits((cc_gc_shader_pipe_config >> 16) & R7XX_MAX_SIMDS_MASK);
+       rdev->config.rv770.active_simds = tmp;
 
        switch (rdev->config.rv770.max_tile_pipes) {
        case 1:
index 22a63c98ba14c688ab259fa666fe3e5d111fb792..730cee2c34cffd33d44de32c4821ecdfa2fee99b 100644 (file)
@@ -71,6 +71,7 @@ MODULE_FIRMWARE("radeon/HAINAN_mc2.bin");
 MODULE_FIRMWARE("radeon/HAINAN_rlc.bin");
 MODULE_FIRMWARE("radeon/HAINAN_smc.bin");
 
+static u32 si_get_cu_active_bitmap(struct radeon_device *rdev, u32 se, u32 sh);
 static void si_pcie_gen3_enable(struct radeon_device *rdev);
 static void si_program_aspm(struct radeon_device *rdev);
 extern void sumo_rlc_fini(struct radeon_device *rdev);
@@ -2900,7 +2901,7 @@ static void si_gpu_init(struct radeon_device *rdev)
        u32 sx_debug_1;
        u32 hdp_host_path_cntl;
        u32 tmp;
-       int i, j;
+       int i, j, k;
 
        switch (rdev->family) {
        case CHIP_TAHITI:
@@ -3098,6 +3099,14 @@ static void si_gpu_init(struct radeon_device *rdev)
                     rdev->config.si.max_sh_per_se,
                     rdev->config.si.max_cu_per_sh);
 
+       for (i = 0; i < rdev->config.si.max_shader_engines; i++) {
+               for (j = 0; j < rdev->config.si.max_sh_per_se; j++) {
+                       for (k = 0; k < rdev->config.si.max_cu_per_sh; k++) {
+                               rdev->config.si.active_cus +=
+                                       hweight32(si_get_cu_active_bitmap(rdev, i, j));
+                       }
+               }
+       }
 
        /* set HW defaults for 3D engine */
        WREG32(CP_QUEUE_THRESHOLDS, (ROQ_IB1_START(0x16) |
@@ -3186,7 +3195,7 @@ void si_fence_ring_emit(struct radeon_device *rdev,
        /* EVENT_WRITE_EOP - flush caches, send int */
        radeon_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4));
        radeon_ring_write(ring, EVENT_TYPE(CACHE_FLUSH_AND_INV_TS_EVENT) | EVENT_INDEX(5));
-       radeon_ring_write(ring, addr & 0xffffffff);
+       radeon_ring_write(ring, lower_32_bits(addr));
        radeon_ring_write(ring, (upper_32_bits(addr) & 0xff) | DATA_SEL(1) | INT_SEL(2));
        radeon_ring_write(ring, fence->seq);
        radeon_ring_write(ring, 0);
@@ -3219,7 +3228,7 @@ void si_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib)
                        radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
                        radeon_ring_write(ring, (1 << 8));
                        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, upper_32_bits(ring->next_rptr_gpu_addr));
                        radeon_ring_write(ring, next_rptr);
                }
 
@@ -4044,18 +4053,21 @@ static int si_pcie_gart_enable(struct radeon_device *rdev)
        WREG32(MC_VM_MX_L1_TLB_CNTL,
               (0xA << 7) |
               ENABLE_L1_TLB |
+              ENABLE_L1_FRAGMENT_PROCESSING |
               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(0));
+              BANK_SELECT(4) |
+              L2_CACHE_BIGK_FRAGMENT_SIZE(4));
        /* 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);
@@ -4092,6 +4104,7 @@ static int si_pcie_gart_enable(struct radeon_device *rdev)
               (u32)(rdev->dummy_page.addr >> 12));
        WREG32(VM_CONTEXT1_CNTL2, 4);
        WREG32(VM_CONTEXT1_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(1) |
+                               PAGE_TABLE_BLOCK_SIZE(radeon_vm_block_size - 9) |
                                RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT |
                                RANGE_PROTECTION_FAULT_ENABLE_DEFAULT |
                                DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT |
@@ -6151,7 +6164,7 @@ restart_ih:
                                                wake_up(&rdev->irq.vblank_queue);
                                        }
                                        if (atomic_read(&rdev->irq.pflip[0]))
-                                               radeon_crtc_handle_flip(rdev, 0);
+                                               radeon_crtc_handle_vblank(rdev, 0);
                                        rdev->irq.stat_regs.evergreen.disp_int &= ~LB_D1_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D1 vblank\n");
                                }
@@ -6177,7 +6190,7 @@ restart_ih:
                                                wake_up(&rdev->irq.vblank_queue);
                                        }
                                        if (atomic_read(&rdev->irq.pflip[1]))
-                                               radeon_crtc_handle_flip(rdev, 1);
+                                               radeon_crtc_handle_vblank(rdev, 1);
                                        rdev->irq.stat_regs.evergreen.disp_int_cont &= ~LB_D2_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D2 vblank\n");
                                }
@@ -6203,7 +6216,7 @@ restart_ih:
                                                wake_up(&rdev->irq.vblank_queue);
                                        }
                                        if (atomic_read(&rdev->irq.pflip[2]))
-                                               radeon_crtc_handle_flip(rdev, 2);
+                                               radeon_crtc_handle_vblank(rdev, 2);
                                        rdev->irq.stat_regs.evergreen.disp_int_cont2 &= ~LB_D3_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D3 vblank\n");
                                }
@@ -6229,7 +6242,7 @@ restart_ih:
                                                wake_up(&rdev->irq.vblank_queue);
                                        }
                                        if (atomic_read(&rdev->irq.pflip[3]))
-                                               radeon_crtc_handle_flip(rdev, 3);
+                                               radeon_crtc_handle_vblank(rdev, 3);
                                        rdev->irq.stat_regs.evergreen.disp_int_cont3 &= ~LB_D4_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D4 vblank\n");
                                }
@@ -6255,7 +6268,7 @@ restart_ih:
                                                wake_up(&rdev->irq.vblank_queue);
                                        }
                                        if (atomic_read(&rdev->irq.pflip[4]))
-                                               radeon_crtc_handle_flip(rdev, 4);
+                                               radeon_crtc_handle_vblank(rdev, 4);
                                        rdev->irq.stat_regs.evergreen.disp_int_cont4 &= ~LB_D5_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D5 vblank\n");
                                }
@@ -6281,7 +6294,7 @@ restart_ih:
                                                wake_up(&rdev->irq.vblank_queue);
                                        }
                                        if (atomic_read(&rdev->irq.pflip[5]))
-                                               radeon_crtc_handle_flip(rdev, 5);
+                                               radeon_crtc_handle_vblank(rdev, 5);
                                        rdev->irq.stat_regs.evergreen.disp_int_cont5 &= ~LB_D6_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D6 vblank\n");
                                }
index de0ca070122f62ee0c8b7fcc6d8db0360adfb948..e24c94b6d14d133dd66acfc72593cc5d5a6eb2c7 100644 (file)
@@ -79,7 +79,25 @@ void si_dma_vm_set_page(struct radeon_device *rdev,
 
        trace_radeon_vm_set_page(pe, addr, count, incr, flags);
 
-       if (flags & R600_PTE_SYSTEM) {
+       if (flags == R600_PTE_GART) {
+               uint64_t src = rdev->gart.table_addr + (addr >> 12) * 8;
+               while (count) {
+                       unsigned bytes = count * 8;
+                       if (bytes > 0xFFFF8)
+                               bytes = 0xFFFF8;
+
+                       ib->ptr[ib->length_dw++] = DMA_PACKET(DMA_PACKET_COPY,
+                                                             1, 0, 0, bytes);
+                       ib->ptr[ib->length_dw++] = lower_32_bits(pe);
+                       ib->ptr[ib->length_dw++] = lower_32_bits(src);
+                       ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff;
+                       ib->ptr[ib->length_dw++] = upper_32_bits(src) & 0xff;
+
+                       pe += bytes;
+                       src += bytes;
+                       count -= bytes / 8;
+               }
+       } else if (flags & R600_PTE_SYSTEM) {
                while (count) {
                        ndw = count * 2;
                        if (ndw > 0xFFFFE)
@@ -202,8 +220,8 @@ int si_copy_dma(struct radeon_device *rdev,
                        cur_size_in_bytes = 0xFFFFF;
                size_in_bytes -= cur_size_in_bytes;
                radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_COPY, 1, 0, 0, cur_size_in_bytes));
-               radeon_ring_write(ring, dst_offset & 0xffffffff);
-               radeon_ring_write(ring, src_offset & 0xffffffff);
+               radeon_ring_write(ring, lower_32_bits(dst_offset));
+               radeon_ring_write(ring, lower_32_bits(src_offset));
                radeon_ring_write(ring, upper_32_bits(dst_offset) & 0xff);
                radeon_ring_write(ring, upper_32_bits(src_offset) & 0xff);
                src_offset += cur_size_in_bytes;
index 9a3567bedaaecb7ea7bb70d9f20654700231dc27..58918868f894572fe9fb729023d7b33eb698e2f4 100644 (file)
@@ -1948,6 +1948,10 @@ static void si_initialize_powertune_defaults(struct radeon_device *rdev)
                        si_pi->cac_weights = cac_weights_cape_verde_pro;
                        si_pi->dte_data = dte_data_cape_verde;
                        break;
+               case 0x682C:
+                       si_pi->cac_weights = cac_weights_cape_verde_pro;
+                       si_pi->dte_data = dte_data_sun_xt;
+                       break;
                case 0x6825:
                case 0x6827:
                        si_pi->cac_weights = cac_weights_heathrow;
@@ -1971,10 +1975,9 @@ static void si_initialize_powertune_defaults(struct radeon_device *rdev)
                        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:
+               case 0x6822:
+               case 0x682A:
                        si_pi->cac_weights = cac_weights_chelsea_pro;
                        si_pi->dte_data = dte_data_venus_pro;
                        break;
@@ -1988,6 +1991,7 @@ static void si_initialize_powertune_defaults(struct radeon_device *rdev)
                case 0x6601:
                case 0x6621:
                case 0x6603:
+               case 0x6605:
                        si_pi->cac_weights = cac_weights_mars_pro;
                        si_pi->lcac_config = lcac_mars_pro;
                        si_pi->cac_override = cac_override_oland;
@@ -1998,6 +2002,7 @@ static void si_initialize_powertune_defaults(struct radeon_device *rdev)
                case 0x6600:
                case 0x6606:
                case 0x6620:
+               case 0x6604:
                        si_pi->cac_weights = cac_weights_mars_xt;
                        si_pi->lcac_config = lcac_mars_pro;
                        si_pi->cac_override = cac_override_oland;
@@ -2006,6 +2011,8 @@ static void si_initialize_powertune_defaults(struct radeon_device *rdev)
                        update_dte_from_pl2 = true;
                        break;
                case 0x6611:
+               case 0x6613:
+               case 0x6608:
                        si_pi->cac_weights = cac_weights_oland_pro;
                        si_pi->lcac_config = lcac_mars_pro;
                        si_pi->cac_override = cac_override_oland;
index 7321283602ce0c1d8429193efe4221ba26a33761..fd414d34d885791ce8b85d303249805eb061ecbe 100644 (file)
 #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                PAGE_TABLE_BLOCK_SIZE(x)                        (((x) & 0xF) << 24)
 #define VM_CONTEXT1_CNTL                               0x1414
 #define VM_CONTEXT0_CNTL2                              0x1430
 #define VM_CONTEXT1_CNTL2                              0x1434
index d1771004cb52ecee46ae22dc7665bd54adb477b6..8bfdadd5659881d6a47b10ff888592c96417ebee 100644 (file)
@@ -45,7 +45,7 @@ void uvd_v2_2_fence_emit(struct radeon_device *rdev,
        radeon_ring_write(ring, PACKET0(UVD_CONTEXT_ID, 0));
        radeon_ring_write(ring, fence->seq);
        radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_DATA0, 0));
-       radeon_ring_write(ring, addr & 0xffffffff);
+       radeon_ring_write(ring, lower_32_bits(addr));
        radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_DATA1, 0));
        radeon_ring_write(ring, upper_32_bits(addr) & 0xff);
        radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_CMD, 0));
index d8e835ac2c5eabf65d5cdcd26d27147aaf7dfc8d..2e3d7b5b0ad7d8363c11c7f7942858a88b03f4ed 100644 (file)
@@ -1,6 +1,7 @@
 config DRM_RCAR_DU
        tristate "DRM Support for R-Car Display Unit"
        depends on DRM && ARM
+       depends on ARCH_SHMOBILE || COMPILE_TEST
        select DRM_KMS_HELPER
        select DRM_KMS_CMA_HELPER
        select DRM_GEM_CMA_HELPER
@@ -12,6 +13,7 @@ config DRM_RCAR_DU
 config DRM_RCAR_LVDS
        bool "R-Car DU LVDS Encoder Support"
        depends on DRM_RCAR_DU
+       depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST
        help
          Enable support the R-Car Display Unit embedded LVDS encoders
          (currently only on R8A7790).
index 4f3ba93cd91dac906b7c06215dd5c7efc0be4987..289048d1c7b2fd590e20eb2425266b758fbfbd88 100644 (file)
@@ -57,15 +57,8 @@ static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector)
        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,
 };
 
index 41d563adfeaa16321f5097af481886f0185a9605..ccfe64c7188fa902750a2fa9a05be5045baa7397 100644 (file)
@@ -25,15 +25,8 @@ 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,
 };
 
index d2b2df9e26f3692b51d7495b5197b66ad9d6387b..c97cdc9ab2397696456b5caa58615499e8e67b96 100644 (file)
@@ -1079,4 +1079,4 @@ const struct drm_ioctl_desc savage_ioctls[] = {
        DRM_IOCTL_DEF_DRV(SAVAGE_BCI_EVENT_WAIT, savage_bci_event_wait, DRM_AUTH),
 };
 
-int savage_max_ioctl = DRM_ARRAY_SIZE(savage_ioctls);
+int savage_max_ioctl = ARRAY_SIZE(savage_ioctls);
index 2ee44ca9d67f9e3e6873831fec3b0fdeefd77e29..a50fe0eeaa0d1caadd7dad638cbb88894358d067 100644 (file)
@@ -1,6 +1,7 @@
 config DRM_SHMOBILE
        tristate "DRM Support for SH Mobile"
-       depends on DRM && (ARM || SUPERH)
+       depends on DRM && ARM
+       depends on ARCH_SHMOBILE || COMPILE_TEST
        select BACKLIGHT_CLASS_DEVICE
        select DRM_KMS_HELPER
        select DRM_KMS_FB_HELPER
index e9e5e6d368cca5271a7260967ac63328ffc80ad6..faf176b2daf99ae628a16c862581d65b90a02943 100644 (file)
@@ -674,12 +674,6 @@ static int shmob_drm_connector_get_modes(struct drm_connector *connector)
        return 1;
 }
 
-static int shmob_drm_connector_mode_valid(struct drm_connector *connector,
-                                         struct drm_display_mode *mode)
-{
-       return MODE_OK;
-}
-
 static struct drm_encoder *
 shmob_drm_connector_best_encoder(struct drm_connector *connector)
 {
@@ -690,7 +684,6 @@ shmob_drm_connector_best_encoder(struct drm_connector *connector)
 
 static const struct drm_connector_helper_funcs connector_helper_funcs = {
        .get_modes = shmob_drm_connector_get_modes,
-       .mode_valid = shmob_drm_connector_mode_valid,
        .best_encoder = shmob_drm_connector_best_encoder,
 };
 
index c839c9c89efbf6f4eb4a37cac2fba654435bd1f9..82c84c7fd4f6e1c93d2872414045bed206899c9a 100644 (file)
@@ -185,7 +185,7 @@ static int shmob_drm_load(struct drm_device *dev, unsigned long flags)
                goto done;
        }
 
-       ret = drm_irq_install(dev);
+       ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0));
        if (ret < 0) {
                dev_err(&pdev->dev, "failed to install IRQ handler\n");
                goto done;
index 0573be0d293304269f6cd11eb8677002ddf217a6..77f288e4a0a63a1ec558e376138230cfed2be7fb 100644 (file)
@@ -359,4 +359,4 @@ const struct drm_ioctl_desc sis_ioctls[] = {
        DRM_IOCTL_DEF_DRV(SIS_FB_INIT, sis_fb_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY),
 };
 
-int sis_max_ioctl = DRM_ARRAY_SIZE(sis_ioctls);
+int sis_max_ioctl = ARRAY_SIZE(sis_ioctls);
index d43f21bb459685b3cd26436902a45a171d412947..2c66a8db9da4290a3b4e70074868a23f6d4e8c78 100644 (file)
@@ -1,7 +1,6 @@
 ccflags-$(CONFIG_DRM_TEGRA_DEBUG) += -DDEBUG
 
 tegra-drm-y := \
-       bus.o \
        drm.o \
        gem.o \
        fb.o \
diff --git a/drivers/gpu/drm/tegra/bus.c b/drivers/gpu/drm/tegra/bus.c
deleted file mode 100644 (file)
index 71cef5c..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2013 NVIDIA Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include "drm.h"
-
-static int drm_host1x_set_busid(struct drm_device *dev,
-                               struct drm_master *master)
-{
-       const char *device = dev_name(dev->dev);
-       const char *driver = dev->driver->name;
-       const char *bus = dev->dev->bus->name;
-       int length;
-
-       master->unique_len = strlen(bus) + 1 + strlen(device);
-       master->unique_size = master->unique_len;
-
-       master->unique = kmalloc(master->unique_len + 1, GFP_KERNEL);
-       if (!master->unique)
-               return -ENOMEM;
-
-       snprintf(master->unique, master->unique_len + 1, "%s:%s", bus, device);
-
-       length = strlen(driver) + 1 + master->unique_len;
-
-       dev->devname = kmalloc(length + 1, GFP_KERNEL);
-       if (!dev->devname)
-               return -ENOMEM;
-
-       snprintf(dev->devname, length + 1, "%s@%s", driver, master->unique);
-
-       return 0;
-}
-
-static struct drm_bus drm_host1x_bus = {
-       .bus_type = DRIVER_BUS_HOST1X,
-       .set_busid = drm_host1x_set_busid,
-};
-
-int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device)
-{
-       struct drm_device *drm;
-       int ret;
-
-       driver->bus = &drm_host1x_bus;
-
-       drm = drm_dev_alloc(driver, &device->dev);
-       if (!drm)
-               return -ENOMEM;
-
-       ret = drm_dev_register(drm, 0);
-       if (ret)
-               goto err_free;
-
-       DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name,
-                driver->major, driver->minor, driver->patchlevel,
-                driver->date, drm->primary->index);
-
-       return 0;
-
-err_free:
-       drm_dev_unref(drm);
-       return ret;
-}
-
-void drm_host1x_exit(struct drm_driver *driver, struct host1x_device *device)
-{
-       struct tegra_drm *tegra = dev_get_drvdata(&device->dev);
-
-       drm_put_dev(tegra->drm);
-}
index edb871d7d395cbb4af140120953b9e854d72aae7..ef40381f3909e3ac20aed29eeb1266e6a8e3d5bd 100644 (file)
@@ -17,6 +17,7 @@
 
 struct tegra_dc_soc_info {
        bool supports_interlacing;
+       bool supports_cursor;
 };
 
 struct tegra_plane {
@@ -29,6 +30,254 @@ static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane)
        return container_of(plane, struct tegra_plane, base);
 }
 
+static unsigned int tegra_dc_format(uint32_t format, uint32_t *swap)
+{
+       /* assume no swapping of fetched data */
+       if (swap)
+               *swap = BYTE_SWAP_NOSWAP;
+
+       switch (format) {
+       case DRM_FORMAT_XBGR8888:
+               return WIN_COLOR_DEPTH_R8G8B8A8;
+
+       case DRM_FORMAT_XRGB8888:
+               return WIN_COLOR_DEPTH_B8G8R8A8;
+
+       case DRM_FORMAT_RGB565:
+               return WIN_COLOR_DEPTH_B5G6R5;
+
+       case DRM_FORMAT_UYVY:
+               return WIN_COLOR_DEPTH_YCbCr422;
+
+       case DRM_FORMAT_YUYV:
+               if (swap)
+                       *swap = BYTE_SWAP_SWAP2;
+
+               return WIN_COLOR_DEPTH_YCbCr422;
+
+       case DRM_FORMAT_YUV420:
+               return WIN_COLOR_DEPTH_YCbCr420P;
+
+       case DRM_FORMAT_YUV422:
+               return WIN_COLOR_DEPTH_YCbCr422P;
+
+       default:
+               break;
+       }
+
+       WARN(1, "unsupported pixel format %u, using default\n", format);
+       return WIN_COLOR_DEPTH_B8G8R8A8;
+}
+
+static bool tegra_dc_format_is_yuv(unsigned int format, bool *planar)
+{
+       switch (format) {
+       case WIN_COLOR_DEPTH_YCbCr422:
+       case WIN_COLOR_DEPTH_YUV422:
+               if (planar)
+                       *planar = false;
+
+               return true;
+
+       case WIN_COLOR_DEPTH_YCbCr420P:
+       case WIN_COLOR_DEPTH_YUV420P:
+       case WIN_COLOR_DEPTH_YCbCr422P:
+       case WIN_COLOR_DEPTH_YUV422P:
+       case WIN_COLOR_DEPTH_YCbCr422R:
+       case WIN_COLOR_DEPTH_YUV422R:
+       case WIN_COLOR_DEPTH_YCbCr422RA:
+       case WIN_COLOR_DEPTH_YUV422RA:
+               if (planar)
+                       *planar = true;
+
+               return true;
+       }
+
+       return false;
+}
+
+static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v,
+                                 unsigned int bpp)
+{
+       fixed20_12 outf = dfixed_init(out);
+       fixed20_12 inf = dfixed_init(in);
+       u32 dda_inc;
+       int max;
+
+       if (v)
+               max = 15;
+       else {
+               switch (bpp) {
+               case 2:
+                       max = 8;
+                       break;
+
+               default:
+                       WARN_ON_ONCE(1);
+                       /* fallthrough */
+               case 4:
+                       max = 4;
+                       break;
+               }
+       }
+
+       outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1));
+       inf.full -= dfixed_const(1);
+
+       dda_inc = dfixed_div(inf, outf);
+       dda_inc = min_t(u32, dda_inc, dfixed_const(max));
+
+       return dda_inc;
+}
+
+static inline u32 compute_initial_dda(unsigned int in)
+{
+       fixed20_12 inf = dfixed_init(in);
+       return dfixed_frac(inf);
+}
+
+static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
+                                const struct tegra_dc_window *window)
+{
+       unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp;
+       unsigned long value;
+       bool yuv, planar;
+
+       /*
+        * For YUV planar modes, the number of bytes per pixel takes into
+        * account only the luma component and therefore is 1.
+        */
+       yuv = tegra_dc_format_is_yuv(window->format, &planar);
+       if (!yuv)
+               bpp = window->bits_per_pixel / 8;
+       else
+               bpp = planar ? 1 : 2;
+
+       value = WINDOW_A_SELECT << index;
+       tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
+
+       tegra_dc_writel(dc, window->format, DC_WIN_COLOR_DEPTH);
+       tegra_dc_writel(dc, window->swap, DC_WIN_BYTE_SWAP);
+
+       value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x);
+       tegra_dc_writel(dc, value, DC_WIN_POSITION);
+
+       value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w);
+       tegra_dc_writel(dc, value, DC_WIN_SIZE);
+
+       h_offset = window->src.x * bpp;
+       v_offset = window->src.y;
+       h_size = window->src.w * bpp;
+       v_size = window->src.h;
+
+       value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size);
+       tegra_dc_writel(dc, value, DC_WIN_PRESCALED_SIZE);
+
+       /*
+        * For DDA computations the number of bytes per pixel for YUV planar
+        * modes needs to take into account all Y, U and V components.
+        */
+       if (yuv && planar)
+               bpp = 2;
+
+       h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp);
+       v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp);
+
+       value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda);
+       tegra_dc_writel(dc, value, DC_WIN_DDA_INC);
+
+       h_dda = compute_initial_dda(window->src.x);
+       v_dda = compute_initial_dda(window->src.y);
+
+       tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA);
+       tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA);
+
+       tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE);
+       tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE);
+
+       tegra_dc_writel(dc, window->base[0], DC_WINBUF_START_ADDR);
+
+       if (yuv && planar) {
+               tegra_dc_writel(dc, window->base[1], DC_WINBUF_START_ADDR_U);
+               tegra_dc_writel(dc, window->base[2], DC_WINBUF_START_ADDR_V);
+               value = window->stride[1] << 16 | window->stride[0];
+               tegra_dc_writel(dc, value, DC_WIN_LINE_STRIDE);
+       } else {
+               tegra_dc_writel(dc, window->stride[0], DC_WIN_LINE_STRIDE);
+       }
+
+       if (window->bottom_up)
+               v_offset += window->src.h - 1;
+
+       tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET);
+       tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET);
+
+       if (window->tiled) {
+               value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
+                       DC_WIN_BUFFER_ADDR_MODE_TILE;
+       } else {
+               value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
+                       DC_WIN_BUFFER_ADDR_MODE_LINEAR;
+       }
+
+       tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
+
+       value = WIN_ENABLE;
+
+       if (yuv) {
+               /* setup default colorspace conversion coefficients */
+               tegra_dc_writel(dc, 0x00f0, DC_WIN_CSC_YOF);
+               tegra_dc_writel(dc, 0x012a, DC_WIN_CSC_KYRGB);
+               tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KUR);
+               tegra_dc_writel(dc, 0x0198, DC_WIN_CSC_KVR);
+               tegra_dc_writel(dc, 0x039b, DC_WIN_CSC_KUG);
+               tegra_dc_writel(dc, 0x032f, DC_WIN_CSC_KVG);
+               tegra_dc_writel(dc, 0x0204, DC_WIN_CSC_KUB);
+               tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KVB);
+
+               value |= CSC_ENABLE;
+       } else if (window->bits_per_pixel < 24) {
+               value |= COLOR_EXPAND;
+       }
+
+       if (window->bottom_up)
+               value |= V_DIRECTION;
+
+       tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
+
+       /*
+        * Disable blending and assume Window A is the bottom-most window,
+        * Window C is the top-most window and Window B is in the middle.
+        */
+       tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_NOKEY);
+       tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_1WIN);
+
+       switch (index) {
+       case 0:
+               tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_X);
+               tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y);
+               tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY);
+               break;
+
+       case 1:
+               tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X);
+               tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y);
+               tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY);
+               break;
+
+       case 2:
+               tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X);
+               tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_Y);
+               tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_3WIN_XY);
+               break;
+       }
+
+       tegra_dc_writel(dc, WIN_A_UPDATE << index, DC_CMD_STATE_CONTROL);
+       tegra_dc_writel(dc, WIN_A_ACT_REQ << index, DC_CMD_STATE_CONTROL);
+
+       return 0;
+}
+
 static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
                              struct drm_framebuffer *fb, int crtc_x,
                              int crtc_y, unsigned int crtc_w,
@@ -49,7 +298,7 @@ static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
        window.dst.y = crtc_y;
        window.dst.w = crtc_w;
        window.dst.h = crtc_h;
-       window.format = tegra_dc_format(fb->pixel_format);
+       window.format = tegra_dc_format(fb->pixel_format, &window.swap);
        window.bits_per_pixel = fb->bits_per_pixel;
        window.bottom_up = tegra_fb_is_bottom_up(fb);
        window.tiled = tegra_fb_is_tiled(fb);
@@ -117,6 +366,7 @@ static const uint32_t plane_formats[] = {
        DRM_FORMAT_XRGB8888,
        DRM_FORMAT_RGB565,
        DRM_FORMAT_UYVY,
+       DRM_FORMAT_YUYV,
        DRM_FORMAT_YUV420,
        DRM_FORMAT_YUV422,
 };
@@ -150,9 +400,9 @@ 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 int h_offset = 0, v_offset = 0;
+       unsigned int format, swap;
        unsigned long value;
 
        tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER);
@@ -162,7 +412,10 @@ 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);
+
+       format = tegra_dc_format(fb->pixel_format, &swap);
        tegra_dc_writel(dc, format, DC_WIN_COLOR_DEPTH);
+       tegra_dc_writel(dc, swap, DC_WIN_BYTE_SWAP);
 
        if (tegra_fb_is_tiled(fb)) {
                value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
@@ -177,13 +430,13 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
        /* make sure bottom-up buffers are properly displayed */
        if (tegra_fb_is_bottom_up(fb)) {
                value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
-               value |= INVERT_V;
+               value |= V_DIRECTION;
                tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
 
                v_offset += fb->height - 1;
        } else {
                value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
-               value &= ~INVERT_V;
+               value &= ~V_DIRECTION;
                tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
        }
 
@@ -209,20 +462,123 @@ void tegra_dc_enable_vblank(struct tegra_dc *dc)
        value |= VBLANK_INT;
        tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
 
-       spin_unlock_irqrestore(&dc->lock, flags);
+       spin_unlock_irqrestore(&dc->lock, flags);
+}
+
+void tegra_dc_disable_vblank(struct tegra_dc *dc)
+{
+       unsigned long value, flags;
+
+       spin_lock_irqsave(&dc->lock, flags);
+
+       value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
+       value &= ~VBLANK_INT;
+       tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
+
+       spin_unlock_irqrestore(&dc->lock, flags);
+}
+
+static int tegra_dc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file,
+                               uint32_t handle, uint32_t width,
+                               uint32_t height, int32_t hot_x, int32_t hot_y)
+{
+       unsigned long value = CURSOR_CLIP_DISPLAY;
+       struct tegra_dc *dc = to_tegra_dc(crtc);
+       struct drm_gem_object *gem;
+       struct tegra_bo *bo = NULL;
+
+       if (!dc->soc->supports_cursor)
+               return -ENXIO;
+
+       if (width != height)
+               return -EINVAL;
+
+       switch (width) {
+       case 32:
+               value |= CURSOR_SIZE_32x32;
+               break;
+
+       case 64:
+               value |= CURSOR_SIZE_64x64;
+               break;
+
+       case 128:
+               value |= CURSOR_SIZE_128x128;
+
+       case 256:
+               value |= CURSOR_SIZE_256x256;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       if (handle) {
+               gem = drm_gem_object_lookup(crtc->dev, file, handle);
+               if (!gem)
+                       return -ENOENT;
+
+               bo = to_tegra_bo(gem);
+       }
+
+       if (bo) {
+               unsigned long addr = (bo->paddr & 0xfffffc00) >> 10;
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+               unsigned long high = (bo->paddr & 0xfffffffc) >> 32;
+#endif
+
+               tegra_dc_writel(dc, value | addr, DC_DISP_CURSOR_START_ADDR);
+
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+               tegra_dc_writel(dc, high, DC_DISP_CURSOR_START_ADDR_HI);
+#endif
+
+               value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
+               value |= CURSOR_ENABLE;
+               tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
+
+               value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL);
+               value &= ~CURSOR_DST_BLEND_MASK;
+               value &= ~CURSOR_SRC_BLEND_MASK;
+               value |= CURSOR_MODE_NORMAL;
+               value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC;
+               value |= CURSOR_SRC_BLEND_K1_TIMES_SRC;
+               value |= CURSOR_ALPHA;
+               tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);
+       } else {
+               value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
+               value &= ~CURSOR_ENABLE;
+               tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
+       }
+
+       tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
+       tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+       tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
+       tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+       return 0;
 }
 
-void tegra_dc_disable_vblank(struct tegra_dc *dc)
+static int tegra_dc_cursor_move(struct drm_crtc *crtc, int x, int y)
 {
-       unsigned long value, flags;
+       struct tegra_dc *dc = to_tegra_dc(crtc);
+       unsigned long value;
 
-       spin_lock_irqsave(&dc->lock, flags);
+       if (!dc->soc->supports_cursor)
+               return -ENXIO;
 
-       value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
-       value &= ~VBLANK_INT;
-       tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
+       value = ((y & 0x3fff) << 16) | (x & 0x3fff);
+       tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
 
-       spin_unlock_irqrestore(&dc->lock, flags);
+       tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
+       tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+       /* XXX: only required on generations earlier than Tegra124? */
+       tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
+       tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+       return 0;
 }
 
 static void tegra_dc_finish_page_flip(struct tegra_dc *dc)
@@ -301,6 +657,8 @@ static void tegra_dc_destroy(struct drm_crtc *crtc)
 }
 
 static const struct drm_crtc_funcs tegra_crtc_funcs = {
+       .cursor_set2 = tegra_dc_cursor_set2,
+       .cursor_move = tegra_dc_cursor_move,
        .page_flip = tegra_dc_page_flip,
        .set_config = drm_crtc_helper_set_config,
        .destroy = tegra_dc_destroy,
@@ -334,52 +692,11 @@ static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
        return true;
 }
 
-static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v,
-                                 unsigned int bpp)
-{
-       fixed20_12 outf = dfixed_init(out);
-       fixed20_12 inf = dfixed_init(in);
-       u32 dda_inc;
-       int max;
-
-       if (v)
-               max = 15;
-       else {
-               switch (bpp) {
-               case 2:
-                       max = 8;
-                       break;
-
-               default:
-                       WARN_ON_ONCE(1);
-                       /* fallthrough */
-               case 4:
-                       max = 4;
-                       break;
-               }
-       }
-
-       outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1));
-       inf.full -= dfixed_const(1);
-
-       dda_inc = dfixed_div(inf, outf);
-       dda_inc = min_t(u32, dda_inc, dfixed_const(max));
-
-       return dda_inc;
-}
-
-static inline u32 compute_initial_dda(unsigned int in)
-{
-       fixed20_12 inf = dfixed_init(in);
-       return dfixed_frac(inf);
-}
-
 static int tegra_dc_set_timings(struct tegra_dc *dc,
                                struct drm_display_mode *mode)
 {
-       /* TODO: For HDMI compliance, h & v ref_to_sync should be set to 1 */
-       unsigned int h_ref_to_sync = 0;
-       unsigned int v_ref_to_sync = 0;
+       unsigned int h_ref_to_sync = 1;
+       unsigned int v_ref_to_sync = 1;
        unsigned long value;
 
        tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS);
@@ -406,13 +723,14 @@ static int tegra_dc_set_timings(struct tegra_dc *dc,
 }
 
 static int tegra_crtc_setup_clk(struct drm_crtc *crtc,
-                               struct drm_display_mode *mode,
-                               unsigned long *div)
+                               struct drm_display_mode *mode)
 {
-       unsigned long pclk = mode->clock * 1000, rate;
+       unsigned long pclk = mode->clock * 1000;
        struct tegra_dc *dc = to_tegra_dc(crtc);
        struct tegra_output *output = NULL;
        struct drm_encoder *encoder;
+       unsigned int div;
+       u32 value;
        long err;
 
        list_for_each_entry(encoder, &crtc->dev->mode_config.encoder_list, head)
@@ -425,221 +743,23 @@ static int tegra_crtc_setup_clk(struct drm_crtc *crtc,
                return -ENODEV;
 
        /*
-        * This assumes that the display controller will divide its parent
-        * clock by 2 to generate the pixel clock.
+        * This assumes that the parent clock is pll_d_out0 or pll_d2_out
+        * respectively, each of which divides the base pll_d by 2.
         */
-       err = tegra_output_setup_clock(output, dc->clk, pclk * 2);
+       err = tegra_output_setup_clock(output, dc->clk, pclk, &div);
        if (err < 0) {
                dev_err(dc->dev, "failed to setup clock: %ld\n", err);
                return err;
        }
 
-       rate = clk_get_rate(dc->clk);
-       *div = (rate * 2 / pclk) - 2;
-
-       DRM_DEBUG_KMS("rate: %lu, div: %lu\n", rate, *div);
-
-       return 0;
-}
-
-static bool tegra_dc_format_is_yuv(unsigned int format, bool *planar)
-{
-       switch (format) {
-       case WIN_COLOR_DEPTH_YCbCr422:
-       case WIN_COLOR_DEPTH_YUV422:
-               if (planar)
-                       *planar = false;
-
-               return true;
-
-       case WIN_COLOR_DEPTH_YCbCr420P:
-       case WIN_COLOR_DEPTH_YUV420P:
-       case WIN_COLOR_DEPTH_YCbCr422P:
-       case WIN_COLOR_DEPTH_YUV422P:
-       case WIN_COLOR_DEPTH_YCbCr422R:
-       case WIN_COLOR_DEPTH_YUV422R:
-       case WIN_COLOR_DEPTH_YCbCr422RA:
-       case WIN_COLOR_DEPTH_YUV422RA:
-               if (planar)
-                       *planar = true;
-
-               return true;
-       }
-
-       return false;
-}
-
-int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
-                         const struct tegra_dc_window *window)
-{
-       unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp;
-       unsigned long value;
-       bool yuv, planar;
-
-       /*
-        * For YUV planar modes, the number of bytes per pixel takes into
-        * account only the luma component and therefore is 1.
-        */
-       yuv = tegra_dc_format_is_yuv(window->format, &planar);
-       if (!yuv)
-               bpp = window->bits_per_pixel / 8;
-       else
-               bpp = planar ? 1 : 2;
-
-       value = WINDOW_A_SELECT << index;
-       tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
-
-       tegra_dc_writel(dc, window->format, DC_WIN_COLOR_DEPTH);
-       tegra_dc_writel(dc, 0, DC_WIN_BYTE_SWAP);
-
-       value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x);
-       tegra_dc_writel(dc, value, DC_WIN_POSITION);
-
-       value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w);
-       tegra_dc_writel(dc, value, DC_WIN_SIZE);
-
-       h_offset = window->src.x * bpp;
-       v_offset = window->src.y;
-       h_size = window->src.w * bpp;
-       v_size = window->src.h;
-
-       value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size);
-       tegra_dc_writel(dc, value, DC_WIN_PRESCALED_SIZE);
-
-       /*
-        * For DDA computations the number of bytes per pixel for YUV planar
-        * modes needs to take into account all Y, U and V components.
-        */
-       if (yuv && planar)
-               bpp = 2;
-
-       h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp);
-       v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp);
-
-       value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda);
-       tegra_dc_writel(dc, value, DC_WIN_DDA_INC);
-
-       h_dda = compute_initial_dda(window->src.x);
-       v_dda = compute_initial_dda(window->src.y);
-
-       tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA);
-       tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA);
-
-       tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE);
-       tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE);
-
-       tegra_dc_writel(dc, window->base[0], DC_WINBUF_START_ADDR);
-
-       if (yuv && planar) {
-               tegra_dc_writel(dc, window->base[1], DC_WINBUF_START_ADDR_U);
-               tegra_dc_writel(dc, window->base[2], DC_WINBUF_START_ADDR_V);
-               value = window->stride[1] << 16 | window->stride[0];
-               tegra_dc_writel(dc, value, DC_WIN_LINE_STRIDE);
-       } else {
-               tegra_dc_writel(dc, window->stride[0], DC_WIN_LINE_STRIDE);
-       }
-
-       if (window->bottom_up)
-               v_offset += window->src.h - 1;
-
-       tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET);
-       tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET);
-
-       if (window->tiled) {
-               value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
-                       DC_WIN_BUFFER_ADDR_MODE_TILE;
-       } else {
-               value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
-                       DC_WIN_BUFFER_ADDR_MODE_LINEAR;
-       }
-
-       tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
-
-       value = WIN_ENABLE;
-
-       if (yuv) {
-               /* setup default colorspace conversion coefficients */
-               tegra_dc_writel(dc, 0x00f0, DC_WIN_CSC_YOF);
-               tegra_dc_writel(dc, 0x012a, DC_WIN_CSC_KYRGB);
-               tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KUR);
-               tegra_dc_writel(dc, 0x0198, DC_WIN_CSC_KVR);
-               tegra_dc_writel(dc, 0x039b, DC_WIN_CSC_KUG);
-               tegra_dc_writel(dc, 0x032f, DC_WIN_CSC_KVG);
-               tegra_dc_writel(dc, 0x0204, DC_WIN_CSC_KUB);
-               tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KVB);
-
-               value |= CSC_ENABLE;
-       } else if (window->bits_per_pixel < 24) {
-               value |= COLOR_EXPAND;
-       }
-
-       if (window->bottom_up)
-               value |= INVERT_V;
-
-       tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
-
-       /*
-        * Disable blending and assume Window A is the bottom-most window,
-        * Window C is the top-most window and Window B is in the middle.
-        */
-       tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_NOKEY);
-       tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_1WIN);
-
-       switch (index) {
-       case 0:
-               tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_X);
-               tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y);
-               tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY);
-               break;
-
-       case 1:
-               tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X);
-               tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y);
-               tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY);
-               break;
-
-       case 2:
-               tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X);
-               tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_Y);
-               tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_3WIN_XY);
-               break;
-       }
+       DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk), div);
 
-       tegra_dc_writel(dc, WIN_A_UPDATE << index, DC_CMD_STATE_CONTROL);
-       tegra_dc_writel(dc, WIN_A_ACT_REQ << index, DC_CMD_STATE_CONTROL);
+       value = SHIFT_CLK_DIVIDER(div) | PIXEL_CLK_DIVIDER_PCD1;
+       tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
 
        return 0;
 }
 
-unsigned int tegra_dc_format(uint32_t format)
-{
-       switch (format) {
-       case DRM_FORMAT_XBGR8888:
-               return WIN_COLOR_DEPTH_R8G8B8A8;
-
-       case DRM_FORMAT_XRGB8888:
-               return WIN_COLOR_DEPTH_B8G8R8A8;
-
-       case DRM_FORMAT_RGB565:
-               return WIN_COLOR_DEPTH_B5G6R5;
-
-       case DRM_FORMAT_UYVY:
-               return WIN_COLOR_DEPTH_YCbCr422;
-
-       case DRM_FORMAT_YUV420:
-               return WIN_COLOR_DEPTH_YCbCr420P;
-
-       case DRM_FORMAT_YUV422:
-               return WIN_COLOR_DEPTH_YCbCr422P;
-
-       default:
-               break;
-       }
-
-       WARN(1, "unsupported pixel format %u, using default\n", format);
-       return WIN_COLOR_DEPTH_B8G8R8A8;
-}
-
 static int tegra_crtc_mode_set(struct drm_crtc *crtc,
                               struct drm_display_mode *mode,
                               struct drm_display_mode *adjusted,
@@ -648,12 +768,12 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
        struct tegra_bo *bo = tegra_fb_get_plane(crtc->primary->fb, 0);
        struct tegra_dc *dc = to_tegra_dc(crtc);
        struct tegra_dc_window window;
-       unsigned long div, value;
+       u32 value;
        int err;
 
        drm_vblank_pre_modeset(crtc->dev, dc->pipe);
 
-       err = tegra_crtc_setup_clk(crtc, mode, &div);
+       err = tegra_crtc_setup_clk(crtc, mode);
        if (err) {
                dev_err(dc->dev, "failed to setup clock for CRTC: %d\n", err);
                return err;
@@ -669,9 +789,6 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
                tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL);
        }
 
-       value = SHIFT_CLK_DIVIDER(div) | PIXEL_CLK_DIVIDER_PCD1;
-       tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
-
        /* setup window parameters */
        memset(&window, 0, sizeof(window));
        window.src.x = 0;
@@ -682,7 +799,8 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
        window.dst.y = 0;
        window.dst.w = mode->hdisplay;
        window.dst.h = mode->vdisplay;
-       window.format = tegra_dc_format(crtc->primary->fb->pixel_format);
+       window.format = tegra_dc_format(crtc->primary->fb->pixel_format,
+                                       &window.swap);
        window.bits_per_pixel = crtc->primary->fb->bits_per_pixel;
        window.stride[0] = crtc->primary->fb->pitches[0];
        window.base[0] = bo->paddr;
@@ -728,10 +846,6 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc)
                WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
        tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
 
-       value = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
-               PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
-       tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
-
        /* initialize timer */
        value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) |
                WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20);
@@ -991,6 +1105,8 @@ static int tegra_dc_show_regs(struct seq_file *s, void *data)
        DUMP_REG(DC_DISP_SD_BL_CONTROL);
        DUMP_REG(DC_DISP_SD_HW_K_VALUES);
        DUMP_REG(DC_DISP_SD_MAN_K_VALUES);
+       DUMP_REG(DC_DISP_CURSOR_START_ADDR_HI);
+       DUMP_REG(DC_DISP_BLEND_CURSOR_CONTROL);
        DUMP_REG(DC_WIN_WIN_OPTIONS);
        DUMP_REG(DC_WIN_BYTE_SWAP);
        DUMP_REG(DC_WIN_BUFFER_CONTROL);
@@ -1096,26 +1212,26 @@ static int tegra_dc_debugfs_exit(struct tegra_dc *dc)
 
 static int tegra_dc_init(struct host1x_client *client)
 {
-       struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+       struct drm_device *drm = dev_get_drvdata(client->parent);
        struct tegra_dc *dc = host1x_client_to_dc(client);
        int err;
 
-       drm_crtc_init(tegra->drm, &dc->base, &tegra_crtc_funcs);
+       drm_crtc_init(drm, &dc->base, &tegra_crtc_funcs);
        drm_mode_crtc_set_gamma_size(&dc->base, 256);
        drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs);
 
-       err = tegra_dc_rgb_init(tegra->drm, dc);
+       err = tegra_dc_rgb_init(drm, dc);
        if (err < 0 && err != -ENODEV) {
                dev_err(dc->dev, "failed to initialize RGB output: %d\n", err);
                return err;
        }
 
-       err = tegra_dc_add_planes(tegra->drm, dc);
+       err = tegra_dc_add_planes(drm, dc);
        if (err < 0)
                return err;
 
        if (IS_ENABLED(CONFIG_DEBUG_FS)) {
-               err = tegra_dc_debugfs_init(dc, tegra->drm->primary);
+               err = tegra_dc_debugfs_init(dc, drm->primary);
                if (err < 0)
                        dev_err(dc->dev, "debugfs setup failed: %d\n", err);
        }
@@ -1160,14 +1276,17 @@ static const struct host1x_client_ops dc_client_ops = {
 
 static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
        .supports_interlacing = false,
+       .supports_cursor = false,
 };
 
 static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
        .supports_interlacing = false,
+       .supports_cursor = false,
 };
 
 static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
        .supports_interlacing = true,
+       .supports_cursor = true,
 };
 
 static const struct of_device_id tegra_dc_of_match[] = {
index c94101494826c898e83d43fb86c9df12064a65b0..78c5feff95d2910123c209cc212eecd1ee1d72bf 100644 (file)
 #define WIN_A_ACT_REQ   (1 <<  1)
 #define WIN_B_ACT_REQ   (1 <<  2)
 #define WIN_C_ACT_REQ   (1 <<  3)
+#define CURSOR_ACT_REQ  (1 <<  7)
 #define GENERAL_UPDATE  (1 <<  8)
 #define WIN_A_UPDATE    (1 <<  9)
 #define WIN_B_UPDATE    (1 << 10)
 #define WIN_C_UPDATE    (1 << 11)
+#define CURSOR_UPDATE   (1 << 15)
 #define NC_HOST_TRIG    (1 << 24)
 
 #define DC_CMD_DISPLAY_WINDOW_HEADER           0x042
 #define DC_DISP_DISP_SIGNAL_OPTIONS1           0x401
 
 #define DC_DISP_DISP_WIN_OPTIONS               0x402
-#define HDMI_ENABLE (1 << 30)
-#define DSI_ENABLE  (1 << 29)
-#define SOR_ENABLE  (1 << 25)
+#define HDMI_ENABLE    (1 << 30)
+#define DSI_ENABLE     (1 << 29)
+#define SOR_ENABLE     (1 << 25)
+#define CURSOR_ENABLE  (1 << 16)
 
 #define DC_DISP_DISP_MEM_HIGH_PRIORITY         0x403
 #define CURSOR_THRESHOLD(x)   (((x) & 0x03) << 24)
 #define DC_DISP_CURSOR_BACKGROUND              0x43d
 
 #define DC_DISP_CURSOR_START_ADDR              0x43e
+#define CURSOR_CLIP_DISPLAY    (0 << 28)
+#define CURSOR_CLIP_WIN_A      (1 << 28)
+#define CURSOR_CLIP_WIN_B      (2 << 28)
+#define CURSOR_CLIP_WIN_C      (3 << 28)
+#define CURSOR_SIZE_32x32      (0 << 24)
+#define CURSOR_SIZE_64x64      (1 << 24)
+#define CURSOR_SIZE_128x128    (2 << 24)
+#define CURSOR_SIZE_256x256    (3 << 24)
 #define DC_DISP_CURSOR_START_ADDR_NS           0x43f
 
 #define DC_DISP_CURSOR_POSITION                        0x440
 #define  INTERLACE_START  (1 << 1)
 #define  INTERLACE_ENABLE (1 << 0)
 
+#define DC_DISP_CURSOR_START_ADDR_HI           0x4ec
+#define DC_DISP_BLEND_CURSOR_CONTROL           0x4f1
+#define CURSOR_MODE_LEGACY                     (0 << 24)
+#define CURSOR_MODE_NORMAL                     (1 << 24)
+#define CURSOR_DST_BLEND_ZERO                  (0 << 16)
+#define CURSOR_DST_BLEND_K1                    (1 << 16)
+#define CURSOR_DST_BLEND_NEG_K1_TIMES_SRC      (2 << 16)
+#define CURSOR_DST_BLEND_MASK                  (3 << 16)
+#define CURSOR_SRC_BLEND_K1                    (0 << 8)
+#define CURSOR_SRC_BLEND_K1_TIMES_SRC          (1 << 8)
+#define CURSOR_SRC_BLEND_MASK                  (3 << 8)
+#define CURSOR_ALPHA                           0xff
+
 #define DC_WIN_CSC_YOF                         0x611
 #define DC_WIN_CSC_KYRGB                       0x612
 #define DC_WIN_CSC_KUR                         0x613
 #define DC_WIN_CSC_KVB                         0x618
 
 #define DC_WIN_WIN_OPTIONS                     0x700
-#define INVERT_V     (1 <<  2)
+#define H_DIRECTION  (1 <<  0)
+#define V_DIRECTION  (1 <<  2)
 #define COLOR_EXPAND (1 <<  6)
 #define CSC_ENABLE   (1 << 18)
 #define WIN_ENABLE   (1 << 30)
index 005c19bd92dfa0e5350303d84290b27b9aae144d..3f132e356e9cd393394bdce5a3594765be08d596 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/platform_device.h>
 #include <linux/reset.h>
 #include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
 
 #include <drm/drm_dp_helper.h>
 #include <drm/drm_panel.h>
@@ -41,6 +42,7 @@ struct tegra_dpaux {
        struct regulator *vdd;
 
        struct completion complete;
+       struct work_struct work;
        struct list_head list;
 };
 
@@ -49,6 +51,11 @@ static inline struct tegra_dpaux *to_dpaux(struct drm_dp_aux *aux)
        return container_of(aux, struct tegra_dpaux, aux);
 }
 
+static inline struct tegra_dpaux *work_to_dpaux(struct work_struct *work)
+{
+       return container_of(work, struct tegra_dpaux, work);
+}
+
 static inline unsigned long tegra_dpaux_readl(struct tegra_dpaux *dpaux,
                                              unsigned long offset)
 {
@@ -231,6 +238,14 @@ static ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux,
        return ret;
 }
 
+static void tegra_dpaux_hotplug(struct work_struct *work)
+{
+       struct tegra_dpaux *dpaux = work_to_dpaux(work);
+
+       if (dpaux->output)
+               drm_helper_hpd_irq_event(dpaux->output->connector.dev);
+}
+
 static irqreturn_t tegra_dpaux_irq(int irq, void *data)
 {
        struct tegra_dpaux *dpaux = data;
@@ -241,16 +256,8 @@ static irqreturn_t tegra_dpaux_irq(int irq, void *data)
        value = tegra_dpaux_readl(dpaux, DPAUX_INTR_AUX);
        tegra_dpaux_writel(dpaux, value, DPAUX_INTR_AUX);
 
-       if (value & DPAUX_INTR_PLUG_EVENT) {
-               if (dpaux->output) {
-                       drm_helper_hpd_irq_event(dpaux->output->connector.dev);
-               }
-       }
-
-       if (value & DPAUX_INTR_UNPLUG_EVENT) {
-               if (dpaux->output)
-                       drm_helper_hpd_irq_event(dpaux->output->connector.dev);
-       }
+       if (value & (DPAUX_INTR_PLUG_EVENT | DPAUX_INTR_UNPLUG_EVENT))
+               schedule_work(&dpaux->work);
 
        if (value & DPAUX_INTR_IRQ_EVENT) {
                /* TODO: handle this */
@@ -273,6 +280,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
        if (!dpaux)
                return -ENOMEM;
 
+       INIT_WORK(&dpaux->work, tegra_dpaux_hotplug);
        init_completion(&dpaux->complete);
        INIT_LIST_HEAD(&dpaux->list);
        dpaux->dev = &pdev->dev;
@@ -332,7 +340,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
        dpaux->aux.transfer = tegra_dpaux_transfer;
        dpaux->aux.dev = &pdev->dev;
 
-       err = drm_dp_aux_register_i2c_bus(&dpaux->aux);
+       err = drm_dp_aux_register(&dpaux->aux);
        if (err < 0)
                return err;
 
@@ -355,12 +363,14 @@ static int tegra_dpaux_remove(struct platform_device *pdev)
 {
        struct tegra_dpaux *dpaux = platform_get_drvdata(pdev);
 
-       drm_dp_aux_unregister_i2c_bus(&dpaux->aux);
+       drm_dp_aux_unregister(&dpaux->aux);
 
        mutex_lock(&dpaux_lock);
        list_del(&dpaux->list);
        mutex_unlock(&dpaux_lock);
 
+       cancel_work_sync(&dpaux->work);
+
        clk_disable_unprepare(dpaux->clk_parent);
        reset_control_assert(dpaux->rst);
        clk_disable_unprepare(dpaux->clk);
@@ -404,6 +414,7 @@ int tegra_dpaux_attach(struct tegra_dpaux *dpaux, struct tegra_output *output)
        unsigned long timeout;
        int err;
 
+       output->connector.polled = DRM_CONNECTOR_POLL_HPD;
        dpaux->output = output;
 
        err = regulator_enable(dpaux->vdd);
index 6f5b6e2f552e3798c6dc30e582ee804f9dffbee4..3396f9f6a9f76b598434f3679dbf7c0f629e4817 100644 (file)
@@ -33,7 +33,6 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
        if (!tegra)
                return -ENOMEM;
 
-       dev_set_drvdata(drm->dev, tegra);
        mutex_init(&tegra->clients_lock);
        INIT_LIST_HEAD(&tegra->clients);
        drm->dev_private = tegra;
@@ -640,14 +639,40 @@ int tegra_drm_unregister_client(struct tegra_drm *tegra,
        return 0;
 }
 
-static int host1x_drm_probe(struct host1x_device *device)
+static int host1x_drm_probe(struct host1x_device *dev)
 {
-       return drm_host1x_init(&tegra_drm_driver, device);
+       struct drm_driver *driver = &tegra_drm_driver;
+       struct drm_device *drm;
+       int err;
+
+       drm = drm_dev_alloc(driver, &dev->dev);
+       if (!drm)
+               return -ENOMEM;
+
+       drm_dev_set_unique(drm, dev_name(&dev->dev));
+       dev_set_drvdata(&dev->dev, drm);
+
+       err = drm_dev_register(drm, 0);
+       if (err < 0)
+               goto unref;
+
+       DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name,
+                driver->major, driver->minor, driver->patchlevel,
+                driver->date, drm->primary->index);
+
+       return 0;
+
+unref:
+       drm_dev_unref(drm);
+       return err;
 }
 
-static int host1x_drm_remove(struct host1x_device *device)
+static int host1x_drm_remove(struct host1x_device *dev)
 {
-       drm_host1x_exit(&tegra_drm_driver, device);
+       struct drm_device *drm = dev_get_drvdata(&dev->dev);
+
+       drm_dev_unregister(drm);
+       drm_dev_unref(drm);
 
        return 0;
 }
@@ -666,6 +691,7 @@ static const struct of_device_id host1x_drm_subdevs[] = {
        { .compatible = "nvidia,tegra114-gr3d", },
        { .compatible = "nvidia,tegra124-dc", },
        { .compatible = "nvidia,tegra124-sor", },
+       { .compatible = "nvidia,tegra124-hdmi", },
        { /* sentinel */ }
 };
 
index 126332c3ecbb6b82c4f18388ae7a809ae7c6a28c..6b8fe9d86ed47d22c8bc651c368820a545e633a5 100644 (file)
@@ -80,13 +80,13 @@ host1x_to_drm_client(struct host1x_client *client)
        return container_of(client, struct tegra_drm_client, base);
 }
 
-extern int tegra_drm_register_client(struct tegra_drm *tegra,
-                                    struct tegra_drm_client *client);
-extern int tegra_drm_unregister_client(struct tegra_drm *tegra,
-                                      struct tegra_drm_client *client);
+int tegra_drm_register_client(struct tegra_drm *tegra,
+                             struct tegra_drm_client *client);
+int tegra_drm_unregister_client(struct tegra_drm *tegra,
+                               struct tegra_drm_client *client);
 
-extern int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm);
-extern int tegra_drm_exit(struct tegra_drm *tegra);
+int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm);
+int tegra_drm_exit(struct tegra_drm *tegra);
 
 struct tegra_dc_soc_info;
 struct tegra_output;
@@ -156,6 +156,7 @@ struct tegra_dc_window {
        } dst;
        unsigned int bits_per_pixel;
        unsigned int format;
+       unsigned int swap;
        unsigned int stride[2];
        unsigned long base[3];
        bool bottom_up;
@@ -163,19 +164,15 @@ struct tegra_dc_window {
 };
 
 /* from dc.c */
-extern unsigned int tegra_dc_format(uint32_t format);
-extern int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
-                                const struct tegra_dc_window *window);
-extern void tegra_dc_enable_vblank(struct tegra_dc *dc);
-extern void tegra_dc_disable_vblank(struct tegra_dc *dc);
-extern void tegra_dc_cancel_page_flip(struct drm_crtc *crtc,
-                                     struct drm_file *file);
+void tegra_dc_enable_vblank(struct tegra_dc *dc);
+void tegra_dc_disable_vblank(struct tegra_dc *dc);
+void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file);
 
 struct tegra_output_ops {
        int (*enable)(struct tegra_output *output);
        int (*disable)(struct tegra_output *output);
        int (*setup_clock)(struct tegra_output *output, struct clk *clk,
-                          unsigned long pclk);
+                          unsigned long pclk, unsigned int *div);
        int (*check_mode)(struct tegra_output *output,
                          struct drm_display_mode *mode,
                          enum drm_mode_status *status);
@@ -233,10 +230,11 @@ static inline int tegra_output_disable(struct tegra_output *output)
 }
 
 static inline int tegra_output_setup_clock(struct tegra_output *output,
-                                          struct clk *clk, unsigned long pclk)
+                                          struct clk *clk, unsigned long pclk,
+                                          unsigned int *div)
 {
        if (output && output->ops && output->ops->setup_clock)
-               return output->ops->setup_clock(output, clk, pclk);
+               return output->ops->setup_clock(output, clk, pclk, div);
 
        return output ? -ENOSYS : -EINVAL;
 }
@@ -251,27 +249,21 @@ static inline int tegra_output_check_mode(struct tegra_output *output,
        return output ? -ENOSYS : -EINVAL;
 }
 
-/* from bus.c */
-int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device);
-void drm_host1x_exit(struct drm_driver *driver, struct host1x_device *device);
-
 /* from rgb.c */
-extern int tegra_dc_rgb_probe(struct tegra_dc *dc);
-extern int tegra_dc_rgb_remove(struct tegra_dc *dc);
-extern int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc);
-extern int tegra_dc_rgb_exit(struct tegra_dc *dc);
+int tegra_dc_rgb_probe(struct tegra_dc *dc);
+int tegra_dc_rgb_remove(struct tegra_dc *dc);
+int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc);
+int tegra_dc_rgb_exit(struct tegra_dc *dc);
 
 /* from output.c */
-extern int tegra_output_probe(struct tegra_output *output);
-extern int tegra_output_remove(struct tegra_output *output);
-extern int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
-extern int tegra_output_exit(struct tegra_output *output);
+int tegra_output_probe(struct tegra_output *output);
+int tegra_output_remove(struct tegra_output *output);
+int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
+int tegra_output_exit(struct tegra_output *output);
 
 /* from dpaux.c */
-
 struct tegra_dpaux;
 struct drm_dp_link;
-struct drm_dp_aux;
 
 struct tegra_dpaux *tegra_dpaux_find_by_of_node(struct device_node *np);
 enum drm_connector_status tegra_dpaux_detect(struct tegra_dpaux *dpaux);
@@ -288,10 +280,10 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
                                    unsigned int index);
 bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer);
 bool tegra_fb_is_tiled(struct drm_framebuffer *framebuffer);
-extern int tegra_drm_fb_init(struct drm_device *drm);
-extern void tegra_drm_fb_exit(struct drm_device *drm);
+int tegra_drm_fb_init(struct drm_device *drm);
+void tegra_drm_fb_exit(struct drm_device *drm);
 #ifdef CONFIG_DRM_TEGRA_FBDEV
-extern void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev);
+void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev);
 #endif
 
 extern struct platform_driver tegra_dc_driver;
index 0e599f0417c06869e5b1f2bdd296914dd10f0d75..bd56f2affa7895b2d1e0fbd556e3e811f8e78f14 100644 (file)
@@ -14,6 +14,8 @@
 #include <linux/platform_device.h>
 #include <linux/reset.h>
 
+#include <linux/regulator/consumer.h>
+
 #include <drm/drm_mipi_dsi.h>
 #include <drm/drm_panel.h>
 
@@ -43,11 +45,15 @@ struct tegra_dsi {
        struct drm_minor *minor;
        struct dentry *debugfs;
 
+       unsigned long flags;
        enum mipi_dsi_pixel_format format;
        unsigned int lanes;
 
        struct tegra_mipi_device *mipi;
        struct mipi_dsi_host host;
+
+       struct regulator *vdd;
+       bool enabled;
 };
 
 static inline struct tegra_dsi *
@@ -244,8 +250,10 @@ static int tegra_dsi_debugfs_exit(struct tegra_dsi *dsi)
 #define PKT_LP         (1 << 30)
 #define NUM_PKT_SEQ    12
 
-/* non-burst mode with sync-end */
-static const u32 pkt_seq_vnb_syne[NUM_PKT_SEQ] = {
+/*
+ * non-burst mode with sync pulses
+ */
+static const u32 pkt_seq_video_non_burst_sync_pulses[NUM_PKT_SEQ] = {
        [ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) |
               PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
               PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) |
@@ -280,6 +288,36 @@ static const u32 pkt_seq_vnb_syne[NUM_PKT_SEQ] = {
               PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4),
 };
 
+/*
+ * non-burst mode with sync events
+ */
+static const u32 pkt_seq_video_non_burst_sync_events[NUM_PKT_SEQ] = {
+       [ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) |
+              PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
+              PKT_LP,
+       [ 1] = 0,
+       [ 2] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+              PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
+              PKT_LP,
+       [ 3] = 0,
+       [ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+              PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
+              PKT_LP,
+       [ 5] = 0,
+       [ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+              PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) |
+              PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3),
+       [ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4),
+       [ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+              PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
+              PKT_LP,
+       [ 9] = 0,
+       [10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+              PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) |
+              PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3),
+       [11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4),
+};
+
 static int tegra_dsi_set_phy_timing(struct tegra_dsi *dsi)
 {
        struct mipi_dphy_timing timing;
@@ -361,28 +399,70 @@ static int tegra_dsi_get_muldiv(enum mipi_dsi_pixel_format format,
        return 0;
 }
 
+static int tegra_dsi_get_format(enum mipi_dsi_pixel_format format,
+                               enum tegra_dsi_format *fmt)
+{
+       switch (format) {
+       case MIPI_DSI_FMT_RGB888:
+               *fmt = TEGRA_DSI_FORMAT_24P;
+               break;
+
+       case MIPI_DSI_FMT_RGB666:
+               *fmt = TEGRA_DSI_FORMAT_18NP;
+               break;
+
+       case MIPI_DSI_FMT_RGB666_PACKED:
+               *fmt = TEGRA_DSI_FORMAT_18P;
+               break;
+
+       case MIPI_DSI_FMT_RGB565:
+               *fmt = TEGRA_DSI_FORMAT_16P;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int tegra_output_dsi_enable(struct tegra_output *output)
 {
        struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
        struct drm_display_mode *mode = &dc->base.mode;
        unsigned int hact, hsw, hbp, hfp, i, mul, div;
        struct tegra_dsi *dsi = to_dsi(output);
-       /* FIXME: don't hardcode this */
-       const u32 *pkt_seq = pkt_seq_vnb_syne;
+       enum tegra_dsi_format format;
        unsigned long value;
+       const u32 *pkt_seq;
        int err;
 
+       if (dsi->enabled)
+               return 0;
+
+       if (dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
+               DRM_DEBUG_KMS("Non-burst video mode with sync pulses\n");
+               pkt_seq = pkt_seq_video_non_burst_sync_pulses;
+       } else {
+               DRM_DEBUG_KMS("Non-burst video mode with sync events\n");
+               pkt_seq = pkt_seq_video_non_burst_sync_events;
+       }
+
        err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
        if (err < 0)
                return err;
 
+       err = tegra_dsi_get_format(dsi->format, &format);
+       if (err < 0)
+               return err;
+
        err = clk_enable(dsi->clk);
        if (err < 0)
                return err;
 
        reset_control_deassert(dsi->rst);
 
-       value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(dsi->format) |
+       value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(format) |
                DSI_CONTROL_LANES(dsi->lanes - 1) |
                DSI_CONTROL_SOURCE(dc->pipe);
        tegra_dsi_writel(dsi, value, DSI_CONTROL);
@@ -454,6 +534,8 @@ static int tegra_output_dsi_enable(struct tegra_output *output)
        value |= DSI_POWER_CONTROL_ENABLE;
        tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL);
 
+       dsi->enabled = true;
+
        return 0;
 }
 
@@ -463,9 +545,12 @@ static int tegra_output_dsi_disable(struct tegra_output *output)
        struct tegra_dsi *dsi = to_dsi(output);
        unsigned long value;
 
+       if (!dsi->enabled)
+               return 0;
+
        /* disable DSI controller */
        value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL);
-       value &= DSI_POWER_CONTROL_ENABLE;
+       value &= ~DSI_POWER_CONTROL_ENABLE;
        tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL);
 
        /*
@@ -492,30 +577,44 @@ static int tegra_output_dsi_disable(struct tegra_output *output)
 
        clk_disable(dsi->clk);
 
+       dsi->enabled = false;
+
        return 0;
 }
 
 static int tegra_output_dsi_setup_clock(struct tegra_output *output,
-                                       struct clk *clk, unsigned long pclk)
+                                       struct clk *clk, unsigned long pclk,
+                                       unsigned int *divp)
 {
        struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
        struct drm_display_mode *mode = &dc->base.mode;
        unsigned int timeout, mul, div, vrefresh;
        struct tegra_dsi *dsi = to_dsi(output);
        unsigned long bclk, plld, value;
-       struct clk *base;
        int err;
 
        err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
        if (err < 0)
                return err;
 
+       DRM_DEBUG_KMS("mul: %u, div: %u, lanes: %u\n", mul, div, dsi->lanes);
        vrefresh = drm_mode_vrefresh(mode);
+       DRM_DEBUG_KMS("vrefresh: %u\n", vrefresh);
 
-       pclk = mode->htotal * mode->vtotal * vrefresh;
+       /* compute byte clock */
        bclk = (pclk * mul) / (div * dsi->lanes);
-       plld = DIV_ROUND_UP(bclk * 8, 1000000);
-       pclk = (plld * 1000000) / 2;
+
+       /*
+        * Compute bit clock and round up to the next MHz.
+        */
+       plld = DIV_ROUND_UP(bclk * 8, 1000000) * 1000000;
+
+       /*
+        * We divide the frequency by two here, but we make up for that by
+        * setting the shift clock divider (further below) to half of the
+        * correct value.
+        */
+       plld /= 2;
 
        err = clk_set_parent(clk, dsi->clk_parent);
        if (err < 0) {
@@ -523,19 +622,25 @@ static int tegra_output_dsi_setup_clock(struct tegra_output *output,
                return err;
        }
 
-       base = clk_get_parent(dsi->clk_parent);
-
-       /*
-        * This assumes that the parent clock is pll_d_out0 or pll_d2_out
-        * respectively, each of which divides the base pll_d by 2.
-        */
-       err = clk_set_rate(base, pclk * 2);
+       err = clk_set_rate(dsi->clk_parent, plld);
        if (err < 0) {
                dev_err(dsi->dev, "failed to set base clock rate to %lu Hz\n",
-                       pclk * 2);
+                       plld);
                return err;
        }
 
+       /*
+        * Derive pixel clock from bit clock using the shift clock divider.
+        * Note that this is only half of what we would expect, but we need
+        * that to make up for the fact that we divided the bit clock by a
+        * factor of two above.
+        *
+        * It's not clear exactly why this is necessary, but the display is
+        * not working properly otherwise. Perhaps the PLLs cannot generate
+        * frequencies sufficiently high.
+        */
+       *divp = ((8 * mul) / (div * dsi->lanes)) - 2;
+
        /*
         * XXX: Move the below somewhere else so that we don't need to have
         * access to the vrefresh in this function?
@@ -610,61 +715,32 @@ static int tegra_dsi_pad_calibrate(struct tegra_dsi *dsi)
 
 static int tegra_dsi_init(struct host1x_client *client)
 {
-       struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+       struct drm_device *drm = dev_get_drvdata(client->parent);
        struct tegra_dsi *dsi = host1x_client_to_dsi(client);
-       unsigned long value, i;
        int err;
 
        dsi->output.type = TEGRA_OUTPUT_DSI;
        dsi->output.dev = client->dev;
        dsi->output.ops = &dsi_ops;
 
-       err = tegra_output_init(tegra->drm, &dsi->output);
+       err = tegra_output_init(drm, &dsi->output);
        if (err < 0) {
                dev_err(client->dev, "output setup failed: %d\n", err);
                return err;
        }
 
        if (IS_ENABLED(CONFIG_DEBUG_FS)) {
-               err = tegra_dsi_debugfs_init(dsi, tegra->drm->primary);
+               err = tegra_dsi_debugfs_init(dsi, drm->primary);
                if (err < 0)
                        dev_err(dsi->dev, "debugfs setup failed: %d\n", err);
        }
 
-       /*
-        * enable high-speed mode, checksum generation, ECC generation and
-        * disable raw mode
-        */
-       value = tegra_dsi_readl(dsi, DSI_HOST_CONTROL);
-       value |= DSI_HOST_CONTROL_ECC | DSI_HOST_CONTROL_CS |
-                DSI_HOST_CONTROL_HS;
-       value &= ~DSI_HOST_CONTROL_RAW;
-       tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);
-
-       tegra_dsi_writel(dsi, 0, DSI_SOL_DELAY);
-       tegra_dsi_writel(dsi, 0, DSI_MAX_THRESHOLD);
-
-       tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_CONTROL);
-
-       for (i = 0; i < 8; i++) {
-               tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_0 + i);
-               tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_8 + i);
-       }
-
-       for (i = 0; i < 12; i++)
-               tegra_dsi_writel(dsi, 0, DSI_PKT_SEQ_0_LO + i);
-
-       tegra_dsi_writel(dsi, 0, DSI_DCS_CMDS);
-
        err = tegra_dsi_pad_calibrate(dsi);
        if (err < 0) {
                dev_err(dsi->dev, "MIPI calibration failed: %d\n", err);
                return err;
        }
 
-       tegra_dsi_writel(dsi, DSI_POWER_CONTROL_ENABLE, DSI_POWER_CONTROL);
-       usleep_range(300, 1000);
-
        return 0;
 }
 
@@ -715,66 +791,13 @@ static int tegra_dsi_setup_clocks(struct tegra_dsi *dsi)
        return 0;
 }
 
-static void tegra_dsi_initialize(struct tegra_dsi *dsi)
-{
-       unsigned int i;
-
-       tegra_dsi_writel(dsi, 0, DSI_POWER_CONTROL);
-
-       tegra_dsi_writel(dsi, 0, DSI_INT_ENABLE);
-       tegra_dsi_writel(dsi, 0, DSI_INT_STATUS);
-       tegra_dsi_writel(dsi, 0, DSI_INT_MASK);
-
-       tegra_dsi_writel(dsi, 0, DSI_HOST_CONTROL);
-       tegra_dsi_writel(dsi, 0, DSI_CONTROL);
-
-       tegra_dsi_writel(dsi, 0, DSI_SOL_DELAY);
-       tegra_dsi_writel(dsi, 0, DSI_MAX_THRESHOLD);
-
-       tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_CONTROL);
-
-       for (i = 0; i < 8; i++) {
-               tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_0 + i);
-               tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_8 + i);
-       }
-
-       for (i = 0; i < 12; i++)
-               tegra_dsi_writel(dsi, 0, DSI_PKT_SEQ_0_LO + i);
-
-       tegra_dsi_writel(dsi, 0, DSI_DCS_CMDS);
-
-       for (i = 0; i < 4; i++)
-               tegra_dsi_writel(dsi, 0, DSI_PKT_LEN_0_1 + i);
-
-       tegra_dsi_writel(dsi, 0x00000000, DSI_PHY_TIMING_0);
-       tegra_dsi_writel(dsi, 0x00000000, DSI_PHY_TIMING_1);
-       tegra_dsi_writel(dsi, 0x000000ff, DSI_PHY_TIMING_2);
-       tegra_dsi_writel(dsi, 0x00000000, DSI_BTA_TIMING);
-
-       tegra_dsi_writel(dsi, 0, DSI_TIMEOUT_0);
-       tegra_dsi_writel(dsi, 0, DSI_TIMEOUT_1);
-       tegra_dsi_writel(dsi, 0, DSI_TO_TALLY);
-
-       tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0);
-       tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_CD);
-       tegra_dsi_writel(dsi, 0, DSI_PAD_CD_STATUS);
-       tegra_dsi_writel(dsi, 0, DSI_VIDEO_MODE_CONTROL);
-       tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1);
-       tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_2);
-       tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_3);
-       tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_4);
-
-       tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_CONTROL);
-       tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_START);
-       tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_SIZE);
-}
-
 static int tegra_dsi_host_attach(struct mipi_dsi_host *host,
                                 struct mipi_dsi_device *device)
 {
        struct tegra_dsi *dsi = host_to_tegra(host);
        struct tegra_output *output = &dsi->output;
 
+       dsi->flags = device->mode_flags;
        dsi->format = device->format;
        dsi->lanes = device->lanes;
 
@@ -829,6 +852,7 @@ static int tegra_dsi_probe(struct platform_device *pdev)
         * attaches to the DSI host, the parameters will be taken from
         * the attached device.
         */
+       dsi->flags = MIPI_DSI_MODE_VIDEO;
        dsi->format = MIPI_DSI_FMT_RGB888;
        dsi->lanes = 4;
 
@@ -872,6 +896,18 @@ static int tegra_dsi_probe(struct platform_device *pdev)
                return err;
        }
 
+       dsi->vdd = devm_regulator_get(&pdev->dev, "avdd-dsi-csi");
+       if (IS_ERR(dsi->vdd)) {
+               dev_err(&pdev->dev, "cannot get VDD supply\n");
+               return PTR_ERR(dsi->vdd);
+       }
+
+       err = regulator_enable(dsi->vdd);
+       if (err < 0) {
+               dev_err(&pdev->dev, "cannot enable VDD supply\n");
+               return err;
+       }
+
        err = tegra_dsi_setup_clocks(dsi);
        if (err < 0) {
                dev_err(&pdev->dev, "cannot setup clocks\n");
@@ -883,8 +919,6 @@ static int tegra_dsi_probe(struct platform_device *pdev)
        if (IS_ERR(dsi->regs))
                return PTR_ERR(dsi->regs);
 
-       tegra_dsi_initialize(dsi);
-
        dsi->mipi = tegra_mipi_request(&pdev->dev);
        if (IS_ERR(dsi->mipi))
                return PTR_ERR(dsi->mipi);
@@ -929,9 +963,11 @@ static int tegra_dsi_remove(struct platform_device *pdev)
        mipi_dsi_host_unregister(&dsi->host);
        tegra_mipi_free(dsi->mipi);
 
+       regulator_disable(dsi->vdd);
        clk_disable_unprepare(dsi->clk_parent);
        clk_disable_unprepare(dsi->clk_lp);
        clk_disable_unprepare(dsi->clk);
+       reset_control_assert(dsi->rst);
 
        err = tegra_output_remove(&dsi->output);
        if (err < 0) {
index 1db5cc24ea914c253864be6a0f2e72697c08c23c..5ce610d08d770afa2b9a26087a8c2055ec772edc 100644 (file)
 #define DSI_INIT_SEQ_DATA_14           0x5e
 #define DSI_INIT_SEQ_DATA_15           0x5f
 
+/*
+ * pixel format as used in the DSI_CONTROL_FORMAT field
+ */
+enum tegra_dsi_format {
+       TEGRA_DSI_FORMAT_16P,
+       TEGRA_DSI_FORMAT_18NP,
+       TEGRA_DSI_FORMAT_18P,
+       TEGRA_DSI_FORMAT_24P,
+};
+
 #endif
index f7fca09d49211c5afebdc797590c001a88e72d6d..9798a708032219adde9cfd6f75c75d937c704769 100644 (file)
@@ -346,11 +346,8 @@ static void tegra_fbdev_free(struct tegra_fbdev *fbdev)
 
 void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev)
 {
-       if (fbdev) {
-               drm_modeset_lock_all(fbdev->base.dev);
-               drm_fb_helper_restore_fbdev_mode(&fbdev->base);
-               drm_modeset_unlock_all(fbdev->base.dev);
-       }
+       if (fbdev)
+               drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->base);
 }
 
 static void tegra_fb_output_poll_changed(struct drm_device *drm)
index bcf9895cef9f2c06870d1ef90c427fdb7d1303af..aa85b7b26f105e5d7528358d6c150b1cc33d8a4e 100644 (file)
@@ -169,7 +169,8 @@ err:
        return ERR_PTR(ret);
 }
 
-struct tegra_bo *tegra_bo_import(struct drm_device *drm, struct dma_buf *buf)
+static struct tegra_bo *tegra_bo_import(struct drm_device *drm,
+                                       struct dma_buf *buf)
 {
        struct dma_buf_attachment *attach;
        struct tegra_bo *bo;
index 2c7ca748edf57f5e89eacd22af50885a0771182c..7c53941f2a9ea347d6ff44eb1583efc629b611d7 100644 (file)
@@ -28,7 +28,7 @@ static inline struct gr2d *to_gr2d(struct tegra_drm_client *client)
 static int gr2d_init(struct host1x_client *client)
 {
        struct tegra_drm_client *drm = host1x_to_drm_client(client);
-       struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+       struct drm_device *dev = dev_get_drvdata(client->parent);
        unsigned long flags = HOST1X_SYNCPT_HAS_BASE;
        struct gr2d *gr2d = to_gr2d(drm);
 
@@ -42,17 +42,17 @@ static int gr2d_init(struct host1x_client *client)
                return -ENOMEM;
        }
 
-       return tegra_drm_register_client(tegra, drm);
+       return tegra_drm_register_client(dev->dev_private, drm);
 }
 
 static int gr2d_exit(struct host1x_client *client)
 {
        struct tegra_drm_client *drm = host1x_to_drm_client(client);
-       struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+       struct drm_device *dev = dev_get_drvdata(client->parent);
        struct gr2d *gr2d = to_gr2d(drm);
        int err;
 
-       err = tegra_drm_unregister_client(tegra, drm);
+       err = tegra_drm_unregister_client(dev->dev_private, drm);
        if (err < 0)
                return err;
 
index 0cbb24b1ae04feef3e3bd602936c0e469715527e..30f5ba9bd6d05c508eac1306c7b1468e6c515587 100644 (file)
@@ -37,7 +37,7 @@ static inline struct gr3d *to_gr3d(struct tegra_drm_client *client)
 static int gr3d_init(struct host1x_client *client)
 {
        struct tegra_drm_client *drm = host1x_to_drm_client(client);
-       struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+       struct drm_device *dev = dev_get_drvdata(client->parent);
        unsigned long flags = HOST1X_SYNCPT_HAS_BASE;
        struct gr3d *gr3d = to_gr3d(drm);
 
@@ -51,17 +51,17 @@ static int gr3d_init(struct host1x_client *client)
                return -ENOMEM;
        }
 
-       return tegra_drm_register_client(tegra, drm);
+       return tegra_drm_register_client(dev->dev_private, drm);
 }
 
 static int gr3d_exit(struct host1x_client *client)
 {
        struct tegra_drm_client *drm = host1x_to_drm_client(client);
-       struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+       struct drm_device *dev = dev_get_drvdata(client->parent);
        struct gr3d *gr3d = to_gr3d(drm);
        int err;
 
-       err = tegra_drm_unregister_client(tegra, drm);
+       err = tegra_drm_unregister_client(dev->dev_private, drm);
        if (err < 0)
                return err;
 
index 6928015d11a49e9fd6e499aed2ed5f02d6d1730e..ba067bb767e376ea4aaa5a7f5fe5917d1608b7c2 100644 (file)
@@ -42,8 +42,9 @@ struct tegra_hdmi {
        struct device *dev;
        bool enabled;
 
-       struct regulator *vdd;
+       struct regulator *hdmi;
        struct regulator *pll;
+       struct regulator *vdd;
 
        void __iomem *regs;
        unsigned int irq;
@@ -317,6 +318,85 @@ static const struct tmds_config tegra114_tmds_config[] = {
        },
 };
 
+static const struct tmds_config tegra124_tmds_config[] = {
+       { /* 480p/576p / 25.2MHz/27MHz modes */
+               .pclk = 27000000,
+               .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) |
+                       SOR_PLL_VCOCAP(0) | SOR_PLL_RESISTORSEL,
+               .pll1 = SOR_PLL_LOADADJ(3) | SOR_PLL_TMDS_TERMADJ(0),
+               .pe_current = PE_CURRENT0(PE_CURRENT_0_mA_T114) |
+                       PE_CURRENT1(PE_CURRENT_0_mA_T114) |
+                       PE_CURRENT2(PE_CURRENT_0_mA_T114) |
+                       PE_CURRENT3(PE_CURRENT_0_mA_T114),
+               .drive_current =
+                       DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_10_400_mA_T114) |
+                       DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_10_400_mA_T114) |
+                       DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_10_400_mA_T114) |
+                       DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_10_400_mA_T114),
+               .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) |
+                       PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) |
+                       PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) |
+                       PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA),
+       }, { /* 720p / 74.25MHz modes */
+               .pclk = 74250000,
+               .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) |
+                       SOR_PLL_VCOCAP(1) | SOR_PLL_RESISTORSEL,
+               .pll1 = SOR_PLL_PE_EN | SOR_PLL_LOADADJ(3) |
+                       SOR_PLL_TMDS_TERMADJ(0),
+               .pe_current = PE_CURRENT0(PE_CURRENT_15_mA_T114) |
+                       PE_CURRENT1(PE_CURRENT_15_mA_T114) |
+                       PE_CURRENT2(PE_CURRENT_15_mA_T114) |
+                       PE_CURRENT3(PE_CURRENT_15_mA_T114),
+               .drive_current =
+                       DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_10_400_mA_T114) |
+                       DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_10_400_mA_T114) |
+                       DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_10_400_mA_T114) |
+                       DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_10_400_mA_T114),
+               .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) |
+                       PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) |
+                       PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) |
+                       PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA),
+       }, { /* 1080p / 148.5MHz modes */
+               .pclk = 148500000,
+               .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) |
+                       SOR_PLL_VCOCAP(3) | SOR_PLL_RESISTORSEL,
+               .pll1 = SOR_PLL_PE_EN | SOR_PLL_LOADADJ(3) |
+                       SOR_PLL_TMDS_TERMADJ(0),
+               .pe_current = PE_CURRENT0(PE_CURRENT_10_mA_T114) |
+                       PE_CURRENT1(PE_CURRENT_10_mA_T114) |
+                       PE_CURRENT2(PE_CURRENT_10_mA_T114) |
+                       PE_CURRENT3(PE_CURRENT_10_mA_T114),
+               .drive_current =
+                       DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_12_400_mA_T114) |
+                       DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_12_400_mA_T114) |
+                       DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_12_400_mA_T114) |
+                       DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_12_400_mA_T114),
+               .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) |
+                       PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) |
+                       PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) |
+                       PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA),
+       }, { /* 225/297MHz modes */
+               .pclk = UINT_MAX,
+               .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) |
+                       SOR_PLL_VCOCAP(0xf) | SOR_PLL_RESISTORSEL,
+               .pll1 = SOR_PLL_LOADADJ(3) | SOR_PLL_TMDS_TERMADJ(7)
+                       | SOR_PLL_TMDS_TERM_ENABLE,
+               .pe_current = PE_CURRENT0(PE_CURRENT_0_mA_T114) |
+                       PE_CURRENT1(PE_CURRENT_0_mA_T114) |
+                       PE_CURRENT2(PE_CURRENT_0_mA_T114) |
+                       PE_CURRENT3(PE_CURRENT_0_mA_T114),
+               .drive_current =
+                       DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_25_200_mA_T114) |
+                       DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_25_200_mA_T114) |
+                       DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_25_200_mA_T114) |
+                       DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_19_200_mA_T114),
+               .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_3_000_mA) |
+                       PEAK_CURRENT_LANE1(PEAK_CURRENT_3_000_mA) |
+                       PEAK_CURRENT_LANE2(PEAK_CURRENT_3_000_mA) |
+                       PEAK_CURRENT_LANE3(PEAK_CURRENT_0_800_mA),
+       },
+};
+
 static const struct tegra_hdmi_audio_config *
 tegra_hdmi_get_audio_config(unsigned int audio_freq, unsigned int pclk)
 {
@@ -716,13 +796,9 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
                return err;
        }
 
-       /*
-        * This assumes that the display controller will divide its parent
-        * clock by 2 to generate the pixel clock.
-        */
-       err = tegra_output_setup_clock(output, hdmi->clk, pclk * 2);
+       err = regulator_enable(hdmi->vdd);
        if (err < 0) {
-               dev_err(hdmi->dev, "failed to setup clock: %d\n", err);
+               dev_err(hdmi->dev, "failed to enable VDD regulator: %d\n", err);
                return err;
        }
 
@@ -730,7 +806,7 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
        if (err < 0)
                return err;
 
-       err = clk_enable(hdmi->clk);
+       err = clk_prepare_enable(hdmi->clk);
        if (err < 0) {
                dev_err(hdmi->dev, "failed to enable clock: %d\n", err);
                return err;
@@ -740,6 +816,17 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
        usleep_range(1000, 2000);
        reset_control_deassert(hdmi->rst);
 
+       /* power up sequence */
+       value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_PLL0);
+       value &= ~SOR_PLL_PDBG;
+       tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_PLL0);
+
+       usleep_range(10, 20);
+
+       value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_PLL0);
+       value &= ~SOR_PLL_PWR;
+       tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_PLL0);
+
        tegra_dc_writel(dc, VSYNC_H_POSITION(1),
                        DC_DISP_DISP_TIMING_OPTIONS);
        tegra_dc_writel(dc, DITHER_CONTROL_DISABLE | BASE_COLOR_SIZE888,
@@ -838,9 +925,13 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
        tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_SEQ_INST(0));
        tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_SEQ_INST(8));
 
-       value = 0x1c800;
+       value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_CSTM);
        value &= ~SOR_CSTM_ROTCLK(~0);
        value |= SOR_CSTM_ROTCLK(2);
+       value |= SOR_CSTM_PLLDIV;
+       value &= ~SOR_CSTM_LVDS_ENABLE;
+       value &= ~SOR_CSTM_MODE_MASK;
+       value |= SOR_CSTM_MODE_TMDS;
        tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_CSTM);
 
        /* start SOR */
@@ -930,10 +1021,18 @@ static int tegra_output_hdmi_disable(struct tegra_output *output)
         * sure it's only executed when the output is attached to one.
         */
        if (dc) {
+               /*
+                * XXX: We can't do this here because it causes HDMI to go
+                * into an erroneous state with the result that HDMI won't
+                * properly work once disabled. See also a similar symptom
+                * for the SOR output.
+                */
+               /*
                value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
                value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
                           PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
                tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
+               */
 
                value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
                value &= ~DISP_CTRL_MODE_MASK;
@@ -947,8 +1046,9 @@ static int tegra_output_hdmi_disable(struct tegra_output *output)
                tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
        }
 
+       clk_disable_unprepare(hdmi->clk);
        reset_control_assert(hdmi->rst);
-       clk_disable(hdmi->clk);
+       regulator_disable(hdmi->vdd);
        regulator_disable(hdmi->pll);
 
        hdmi->enabled = false;
@@ -957,10 +1057,10 @@ static int tegra_output_hdmi_disable(struct tegra_output *output)
 }
 
 static int tegra_output_hdmi_setup_clock(struct tegra_output *output,
-                                        struct clk *clk, unsigned long pclk)
+                                        struct clk *clk, unsigned long pclk,
+                                        unsigned int *div)
 {
        struct tegra_hdmi *hdmi = to_hdmi(output);
-       struct clk *base;
        int err;
 
        err = clk_set_parent(clk, hdmi->clk_parent);
@@ -969,17 +1069,12 @@ static int tegra_output_hdmi_setup_clock(struct tegra_output *output,
                return err;
        }
 
-       base = clk_get_parent(hdmi->clk_parent);
-
-       /*
-        * This assumes that the parent clock is pll_d_out0 or pll_d2_out
-        * respectively, each of which divides the base pll_d by 2.
-        */
-       err = clk_set_rate(base, pclk * 2);
+       err = clk_set_rate(hdmi->clk_parent, pclk);
        if (err < 0)
-               dev_err(output->dev,
-                       "failed to set base clock rate to %lu Hz\n",
-                       pclk * 2);
+               dev_err(output->dev, "failed to set clock rate to %lu Hz\n",
+                       pclk);
+
+       *div = 0;
 
        return 0;
 }
@@ -1017,7 +1112,7 @@ static int tegra_hdmi_show_regs(struct seq_file *s, void *data)
        struct tegra_hdmi *hdmi = node->info_ent->data;
        int err;
 
-       err = clk_enable(hdmi->clk);
+       err = clk_prepare_enable(hdmi->clk);
        if (err)
                return err;
 
@@ -1186,7 +1281,7 @@ static int tegra_hdmi_show_regs(struct seq_file *s, void *data)
 
 #undef DUMP_REG
 
-       clk_disable(hdmi->clk);
+       clk_disable_unprepare(hdmi->clk);
 
        return 0;
 }
@@ -1252,33 +1347,33 @@ static int tegra_hdmi_debugfs_exit(struct tegra_hdmi *hdmi)
 
 static int tegra_hdmi_init(struct host1x_client *client)
 {
-       struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+       struct drm_device *drm = dev_get_drvdata(client->parent);
        struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
        int err;
 
-       err = regulator_enable(hdmi->vdd);
-       if (err < 0) {
-               dev_err(client->dev, "failed to enable VDD regulator: %d\n",
-                       err);
-               return err;
-       }
-
        hdmi->output.type = TEGRA_OUTPUT_HDMI;
        hdmi->output.dev = client->dev;
        hdmi->output.ops = &hdmi_ops;
 
-       err = tegra_output_init(tegra->drm, &hdmi->output);
+       err = tegra_output_init(drm, &hdmi->output);
        if (err < 0) {
                dev_err(client->dev, "output setup failed: %d\n", err);
                return err;
        }
 
        if (IS_ENABLED(CONFIG_DEBUG_FS)) {
-               err = tegra_hdmi_debugfs_init(hdmi, tegra->drm->primary);
+               err = tegra_hdmi_debugfs_init(hdmi, drm->primary);
                if (err < 0)
                        dev_err(client->dev, "debugfs setup failed: %d\n", err);
        }
 
+       err = regulator_enable(hdmi->hdmi);
+       if (err < 0) {
+               dev_err(client->dev, "failed to enable HDMI regulator: %d\n",
+                       err);
+               return err;
+       }
+
        return 0;
 }
 
@@ -1287,6 +1382,8 @@ static int tegra_hdmi_exit(struct host1x_client *client)
        struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
        int err;
 
+       regulator_disable(hdmi->hdmi);
+
        if (IS_ENABLED(CONFIG_DEBUG_FS)) {
                err = tegra_hdmi_debugfs_exit(hdmi);
                if (err < 0)
@@ -1306,8 +1403,6 @@ static int tegra_hdmi_exit(struct host1x_client *client)
                return err;
        }
 
-       regulator_disable(hdmi->vdd);
-
        return 0;
 }
 
@@ -1340,7 +1435,16 @@ static const struct tegra_hdmi_config tegra114_hdmi_config = {
        .has_sor_io_peak_current = true,
 };
 
+static const struct tegra_hdmi_config tegra124_hdmi_config = {
+       .tmds = tegra124_tmds_config,
+       .num_tmds = ARRAY_SIZE(tegra124_tmds_config),
+       .fuse_override_offset = HDMI_NV_PDISP_SOR_PAD_CTLS0,
+       .fuse_override_value = 1 << 31,
+       .has_sor_io_peak_current = true,
+};
+
 static const struct of_device_id tegra_hdmi_of_match[] = {
+       { .compatible = "nvidia,tegra124-hdmi", .data = &tegra124_hdmi_config },
        { .compatible = "nvidia,tegra114-hdmi", .data = &tegra114_hdmi_config },
        { .compatible = "nvidia,tegra30-hdmi", .data = &tegra30_hdmi_config },
        { .compatible = "nvidia,tegra20-hdmi", .data = &tegra20_hdmi_config },
@@ -1381,28 +1485,20 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
                return PTR_ERR(hdmi->rst);
        }
 
-       err = clk_prepare(hdmi->clk);
-       if (err < 0)
-               return err;
-
        hdmi->clk_parent = devm_clk_get(&pdev->dev, "parent");
        if (IS_ERR(hdmi->clk_parent))
                return PTR_ERR(hdmi->clk_parent);
 
-       err = clk_prepare(hdmi->clk_parent);
-       if (err < 0)
-               return err;
-
        err = clk_set_parent(hdmi->clk, hdmi->clk_parent);
        if (err < 0) {
                dev_err(&pdev->dev, "failed to setup clocks: %d\n", err);
                return err;
        }
 
-       hdmi->vdd = devm_regulator_get(&pdev->dev, "vdd");
-       if (IS_ERR(hdmi->vdd)) {
-               dev_err(&pdev->dev, "failed to get VDD regulator\n");
-               return PTR_ERR(hdmi->vdd);
+       hdmi->hdmi = devm_regulator_get(&pdev->dev, "hdmi");
+       if (IS_ERR(hdmi->hdmi)) {
+               dev_err(&pdev->dev, "failed to get HDMI regulator\n");
+               return PTR_ERR(hdmi->hdmi);
        }
 
        hdmi->pll = devm_regulator_get(&pdev->dev, "pll");
@@ -1411,6 +1507,12 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
                return PTR_ERR(hdmi->pll);
        }
 
+       hdmi->vdd = devm_regulator_get(&pdev->dev, "vdd");
+       if (IS_ERR(hdmi->vdd)) {
+               dev_err(&pdev->dev, "failed to get VDD regulator\n");
+               return PTR_ERR(hdmi->vdd);
+       }
+
        hdmi->output.dev = &pdev->dev;
 
        err = tegra_output_probe(&hdmi->output);
@@ -1462,8 +1564,8 @@ static int tegra_hdmi_remove(struct platform_device *pdev)
                return err;
        }
 
-       clk_unprepare(hdmi->clk_parent);
-       clk_unprepare(hdmi->clk);
+       clk_disable_unprepare(hdmi->clk_parent);
+       clk_disable_unprepare(hdmi->clk);
 
        return 0;
 }
index 0aebc485f7fa36be983a609f693bfbb06dcb0214..919a19df4e1b59257b3969887fc288c11e43e1d1 100644 (file)
 
 #define HDMI_NV_PDISP_SOR_CSTM                                 0x5a
 #define SOR_CSTM_ROTCLK(x) (((x) & 0xf) << 24)
+#define SOR_CSTM_PLLDIV (1 << 21)
+#define SOR_CSTM_LVDS_ENABLE (1 << 16)
+#define SOR_CSTM_MODE_LVDS (0 << 12)
+#define SOR_CSTM_MODE_TMDS (1 << 12)
+#define SOR_CSTM_MODE_MASK (3 << 12)
 
 #define HDMI_NV_PDISP_SOR_LVDS                                 0x5b
 #define HDMI_NV_PDISP_SOR_CRCA                                 0x5c
index 0266fb40479eae05f5176e9835d7c2a1ede75519..d6af9be48f42ff1a536bf0c24dc4616839f95f1e 100644 (file)
@@ -159,11 +159,38 @@ static int tegra_output_rgb_disable(struct tegra_output *output)
 }
 
 static int tegra_output_rgb_setup_clock(struct tegra_output *output,
-                                       struct clk *clk, unsigned long pclk)
+                                       struct clk *clk, unsigned long pclk,
+                                       unsigned int *div)
 {
        struct tegra_rgb *rgb = to_rgb(output);
+       int err;
+
+       err = clk_set_parent(clk, rgb->clk_parent);
+       if (err < 0) {
+               dev_err(output->dev, "failed to set parent: %d\n", err);
+               return err;
+       }
 
-       return clk_set_parent(clk, rgb->clk_parent);
+       /*
+        * We may not want to change the frequency of the parent clock, since
+        * it may be a parent for other peripherals. This is due to the fact
+        * that on Tegra20 there's only a single clock dedicated to display
+        * (pll_d_out0), whereas later generations have a second one that can
+        * be used to independently drive a second output (pll_d2_out0).
+        *
+        * As a way to support multiple outputs on Tegra20 as well, pll_p is
+        * typically used as the parent clock for the display controllers.
+        * But this comes at a cost: pll_p is the parent of several other
+        * peripherals, so its frequency shouldn't change out of the blue.
+        *
+        * The best we can do at this point is to use the shift clock divider
+        * and hope that the desired frequency can be matched (or at least
+        * matched sufficiently close that the panel will still work).
+        */
+
+       *div = ((clk_get_rate(clk) * 2) / pclk) - 2;
+
+       return 0;
 }
 
 static int tegra_output_rgb_check_mode(struct tegra_output *output,
index 49ef5729f435daa411c508070a36c3e50a3e7c1a..27c979b5011112182226f7a1cd4d0e1e6b54c9ed 100644 (file)
@@ -7,6 +7,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/debugfs.h>
 #include <linux/io.h>
 #include <linux/platform_device.h>
 #include <linux/reset.h>
@@ -33,7 +34,23 @@ struct tegra_sor {
 
        struct tegra_dpaux *dpaux;
 
+       struct mutex lock;
        bool enabled;
+
+       struct dentry *debugfs;
+};
+
+struct tegra_sor_config {
+       u32 bits_per_pixel;
+
+       u32 active_polarity;
+       u32 active_count;
+       u32 tu_size;
+       u32 active_frac;
+       u32 watermark;
+
+       u32 hblank_symbols;
+       u32 vblank_symbols;
 };
 
 static inline struct tegra_sor *
@@ -289,34 +306,232 @@ static int tegra_sor_power_up(struct tegra_sor *sor, unsigned long timeout)
        return -ETIMEDOUT;
 }
 
+struct tegra_sor_params {
+       /* number of link clocks per line */
+       unsigned int num_clocks;
+       /* ratio between input and output */
+       u64 ratio;
+       /* precision factor */
+       u64 precision;
+
+       unsigned int active_polarity;
+       unsigned int active_count;
+       unsigned int active_frac;
+       unsigned int tu_size;
+       unsigned int error;
+};
+
+static int tegra_sor_compute_params(struct tegra_sor *sor,
+                                   struct tegra_sor_params *params,
+                                   unsigned int tu_size)
+{
+       u64 active_sym, active_count, frac, approx;
+       u32 active_polarity, active_frac = 0;
+       const u64 f = params->precision;
+       s64 error;
+
+       active_sym = params->ratio * tu_size;
+       active_count = div_u64(active_sym, f) * f;
+       frac = active_sym - active_count;
+
+       /* fraction < 0.5 */
+       if (frac >= (f / 2)) {
+               active_polarity = 1;
+               frac = f - frac;
+       } else {
+               active_polarity = 0;
+       }
+
+       if (frac != 0) {
+               frac = div_u64(f * f,  frac); /* 1/fraction */
+               if (frac <= (15 * f)) {
+                       active_frac = div_u64(frac, f);
+
+                       /* round up */
+                       if (active_polarity)
+                               active_frac++;
+               } else {
+                       active_frac = active_polarity ? 1 : 15;
+               }
+       }
+
+       if (active_frac == 1)
+               active_polarity = 0;
+
+       if (active_polarity == 1) {
+               if (active_frac) {
+                       approx = active_count + (active_frac * (f - 1)) * f;
+                       approx = div_u64(approx, active_frac * f);
+               } else {
+                       approx = active_count + f;
+               }
+       } else {
+               if (active_frac)
+                       approx = active_count + div_u64(f, active_frac);
+               else
+                       approx = active_count;
+       }
+
+       error = div_s64(active_sym - approx, tu_size);
+       error *= params->num_clocks;
+
+       if (error <= 0 && abs64(error) < params->error) {
+               params->active_count = div_u64(active_count, f);
+               params->active_polarity = active_polarity;
+               params->active_frac = active_frac;
+               params->error = abs64(error);
+               params->tu_size = tu_size;
+
+               if (error == 0)
+                       return true;
+       }
+
+       return false;
+}
+
+static int tegra_sor_calc_config(struct tegra_sor *sor,
+                                struct drm_display_mode *mode,
+                                struct tegra_sor_config *config,
+                                struct drm_dp_link *link)
+{
+       const u64 f = 100000, link_rate = link->rate * 1000;
+       const u64 pclk = mode->clock * 1000;
+       u64 input, output, watermark, num;
+       struct tegra_sor_params params;
+       u32 num_syms_per_line;
+       unsigned int i;
+
+       if (!link_rate || !link->num_lanes || !pclk || !config->bits_per_pixel)
+               return -EINVAL;
+
+       output = link_rate * 8 * link->num_lanes;
+       input = pclk * config->bits_per_pixel;
+
+       if (input >= output)
+               return -ERANGE;
+
+       memset(&params, 0, sizeof(params));
+       params.ratio = div64_u64(input * f, output);
+       params.num_clocks = div_u64(link_rate * mode->hdisplay, pclk);
+       params.precision = f;
+       params.error = 64 * f;
+       params.tu_size = 64;
+
+       for (i = params.tu_size; i >= 32; i--)
+               if (tegra_sor_compute_params(sor, &params, i))
+                       break;
+
+       if (params.active_frac == 0) {
+               config->active_polarity = 0;
+               config->active_count = params.active_count;
+
+               if (!params.active_polarity)
+                       config->active_count--;
+
+               config->tu_size = params.tu_size;
+               config->active_frac = 1;
+       } else {
+               config->active_polarity = params.active_polarity;
+               config->active_count = params.active_count;
+               config->active_frac = params.active_frac;
+               config->tu_size = params.tu_size;
+       }
+
+       dev_dbg(sor->dev,
+               "polarity: %d active count: %d tu size: %d active frac: %d\n",
+               config->active_polarity, config->active_count,
+               config->tu_size, config->active_frac);
+
+       watermark = params.ratio * config->tu_size * (f - params.ratio);
+       watermark = div_u64(watermark, f);
+
+       watermark = div_u64(watermark + params.error, f);
+       config->watermark = watermark + (config->bits_per_pixel / 8) + 2;
+       num_syms_per_line = (mode->hdisplay * config->bits_per_pixel) *
+                           (link->num_lanes * 8);
+
+       if (config->watermark > 30) {
+               config->watermark = 30;
+               dev_err(sor->dev,
+                       "unable to compute TU size, forcing watermark to %u\n",
+                       config->watermark);
+       } else if (config->watermark > num_syms_per_line) {
+               config->watermark = num_syms_per_line;
+               dev_err(sor->dev, "watermark too high, forcing to %u\n",
+                       config->watermark);
+       }
+
+       /* compute the number of symbols per horizontal blanking interval */
+       num = ((mode->htotal - mode->hdisplay) - 7) * link_rate;
+       config->hblank_symbols = div_u64(num, pclk);
+
+       if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING)
+               config->hblank_symbols -= 3;
+
+       config->hblank_symbols -= 12 / link->num_lanes;
+
+       /* compute the number of symbols per vertical blanking interval */
+       num = (mode->hdisplay - 25) * link_rate;
+       config->vblank_symbols = div_u64(num, pclk);
+       config->vblank_symbols -= 36 / link->num_lanes + 4;
+
+       dev_dbg(sor->dev, "blank symbols: H:%u V:%u\n", config->hblank_symbols,
+               config->vblank_symbols);
+
+       return 0;
+}
+
 static int tegra_output_sor_enable(struct tegra_output *output)
 {
        struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
        struct drm_display_mode *mode = &dc->base.mode;
        unsigned int vbe, vse, hbe, hse, vbs, hbs, i;
        struct tegra_sor *sor = to_sor(output);
+       struct tegra_sor_config config;
+       struct drm_dp_link link;
+       struct drm_dp_aux *aux;
        unsigned long value;
-       int err;
+       int err = 0;
+
+       mutex_lock(&sor->lock);
 
        if (sor->enabled)
-               return 0;
+               goto unlock;
 
        err = clk_prepare_enable(sor->clk);
        if (err < 0)
-               return err;
+               goto unlock;
 
        reset_control_deassert(sor->rst);
 
+       /* FIXME: properly convert to struct drm_dp_aux */
+       aux = (struct drm_dp_aux *)sor->dpaux;
+
        if (sor->dpaux) {
                err = tegra_dpaux_enable(sor->dpaux);
                if (err < 0)
                        dev_err(sor->dev, "failed to enable DP: %d\n", err);
+
+               err = drm_dp_link_probe(aux, &link);
+               if (err < 0) {
+                       dev_err(sor->dev, "failed to probe eDP link: %d\n",
+                               err);
+                       return err;
+               }
        }
 
        err = clk_set_parent(sor->clk, sor->clk_safe);
        if (err < 0)
                dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
 
+       memset(&config, 0, sizeof(config));
+       config.bits_per_pixel = 24; /* XXX: don't hardcode? */
+
+       err = tegra_sor_calc_config(sor, mode, &config, &link);
+       if (err < 0)
+               dev_err(sor->dev, "failed to compute link configuration: %d\n",
+                       err);
+
        value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
        value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK;
        value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK;
@@ -385,7 +600,7 @@ static int tegra_output_sor_enable(struct tegra_output *output)
        err = tegra_io_rail_power_on(TEGRA_IO_RAIL_LVDS);
        if (err < 0) {
                dev_err(sor->dev, "failed to power on I/O rail: %d\n", err);
-               return err;
+               goto unlock;
        }
 
        usleep_range(5, 100);
@@ -419,15 +634,29 @@ static int tegra_output_sor_enable(struct tegra_output *output)
        if (err < 0)
                dev_err(sor->dev, "failed to set DP parent clock: %d\n", err);
 
-       /* power dplanes (XXX parameterize based on link?) */
+       /* power DP lanes */
        value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
-       value |= SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 |
-                SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2;
+
+       if (link.num_lanes <= 2)
+               value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_2);
+       else
+               value |= SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_2;
+
+       if (link.num_lanes <= 1)
+               value &= ~SOR_DP_PADCTL_PD_TXD_1;
+       else
+               value |= SOR_DP_PADCTL_PD_TXD_1;
+
+       if (link.num_lanes == 0)
+               value &= ~SOR_DP_PADCTL_PD_TXD_0;
+       else
+               value |= SOR_DP_PADCTL_PD_TXD_0;
+
        tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
 
        value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0);
        value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK;
-       value |= SOR_DP_LINKCTL_LANE_COUNT(4);
+       value |= SOR_DP_LINKCTL_LANE_COUNT(link.num_lanes);
        tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0);
 
        /* start lane sequencer */
@@ -443,10 +672,10 @@ static int tegra_output_sor_enable(struct tegra_output *output)
                usleep_range(250, 1000);
        }
 
-       /* set link bandwidth (2.7 GHz, XXX: parameterize based on link?) */
+       /* set link bandwidth */
        value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
        value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK;
-       value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G2_70;
+       value |= drm_dp_link_rate_to_bw_code(link.rate) << 2;
        tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
 
        /* set linkctl */
@@ -454,7 +683,7 @@ static int tegra_output_sor_enable(struct tegra_output *output)
        value |= SOR_DP_LINKCTL_ENABLE;
 
        value &= ~SOR_DP_LINKCTL_TU_SIZE_MASK;
-       value |= SOR_DP_LINKCTL_TU_SIZE(59); /* XXX: don't hardcode? */
+       value |= SOR_DP_LINKCTL_TU_SIZE(config.tu_size);
 
        value |= SOR_DP_LINKCTL_ENHANCED_FRAME;
        tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0);
@@ -470,28 +699,31 @@ static int tegra_output_sor_enable(struct tegra_output *output)
 
        value = tegra_sor_readl(sor, SOR_DP_CONFIG_0);
        value &= ~SOR_DP_CONFIG_WATERMARK_MASK;
-       value |= SOR_DP_CONFIG_WATERMARK(14); /* XXX: don't hardcode? */
+       value |= SOR_DP_CONFIG_WATERMARK(config.watermark);
 
        value &= ~SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK;
-       value |= SOR_DP_CONFIG_ACTIVE_SYM_COUNT(47); /* XXX: don't hardcode? */
+       value |= SOR_DP_CONFIG_ACTIVE_SYM_COUNT(config.active_count);
 
        value &= ~SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK;
-       value |= SOR_DP_CONFIG_ACTIVE_SYM_FRAC(9); /* XXX: don't hardcode? */
+       value |= SOR_DP_CONFIG_ACTIVE_SYM_FRAC(config.active_frac);
 
-       value &= ~SOR_DP_CONFIG_ACTIVE_SYM_POLARITY; /* XXX: don't hardcode? */
+       if (config.active_polarity)
+               value |= SOR_DP_CONFIG_ACTIVE_SYM_POLARITY;
+       else
+               value &= ~SOR_DP_CONFIG_ACTIVE_SYM_POLARITY;
 
        value |= SOR_DP_CONFIG_ACTIVE_SYM_ENABLE;
-       value |= SOR_DP_CONFIG_DISPARITY_NEGATIVE; /* XXX: don't hardcode? */
+       value |= SOR_DP_CONFIG_DISPARITY_NEGATIVE;
        tegra_sor_writel(sor, value, SOR_DP_CONFIG_0);
 
        value = tegra_sor_readl(sor, SOR_DP_AUDIO_HBLANK_SYMBOLS);
        value &= ~SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK;
-       value |= 137; /* XXX: don't hardcode? */
+       value |= config.hblank_symbols & 0xffff;
        tegra_sor_writel(sor, value, SOR_DP_AUDIO_HBLANK_SYMBOLS);
 
        value = tegra_sor_readl(sor, SOR_DP_AUDIO_VBLANK_SYMBOLS);
        value &= ~SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK;
-       value |= 2368; /* XXX: don't hardcode? */
+       value |= config.vblank_symbols & 0xffff;
        tegra_sor_writel(sor, value, SOR_DP_AUDIO_VBLANK_SYMBOLS);
 
        /* enable pad calibration logic */
@@ -500,30 +732,27 @@ static int tegra_output_sor_enable(struct tegra_output *output)
        tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
 
        if (sor->dpaux) {
-               /* FIXME: properly convert to struct drm_dp_aux */
-               struct drm_dp_aux *aux = (struct drm_dp_aux *)sor->dpaux;
-               struct drm_dp_link link;
                u8 rate, lanes;
 
                err = drm_dp_link_probe(aux, &link);
                if (err < 0) {
                        dev_err(sor->dev, "failed to probe eDP link: %d\n",
                                err);
-                       return err;
+                       goto unlock;
                }
 
                err = drm_dp_link_power_up(aux, &link);
                if (err < 0) {
                        dev_err(sor->dev, "failed to power up eDP link: %d\n",
                                err);
-                       return err;
+                       goto unlock;
                }
 
                err = drm_dp_link_configure(aux, &link);
                if (err < 0) {
                        dev_err(sor->dev, "failed to configure eDP link: %d\n",
                                err);
-                       return err;
+                       goto unlock;
                }
 
                rate = drm_dp_link_rate_to_bw_code(link.rate);
@@ -558,7 +787,7 @@ static int tegra_output_sor_enable(struct tegra_output *output)
                if (err < 0) {
                        dev_err(sor->dev, "DP fast link training failed: %d\n",
                                err);
-                       return err;
+                       goto unlock;
                }
 
                dev_dbg(sor->dev, "fast link training succeeded\n");
@@ -567,7 +796,7 @@ static int tegra_output_sor_enable(struct tegra_output *output)
        err = tegra_sor_power_up(sor, 250);
        if (err < 0) {
                dev_err(sor->dev, "failed to power up SOR: %d\n", err);
-               return err;
+               goto unlock;
        }
 
        /* start display controller in continuous mode */
@@ -586,12 +815,26 @@ static int tegra_output_sor_enable(struct tegra_output *output)
         * configure panel (24bpp, vsync-, hsync-, DP-A protocol, complete
         * raster, associate with display controller)
         */
-       value = SOR_STATE_ASY_PIXELDEPTH_BPP_24_444 |
-               SOR_STATE_ASY_VSYNCPOL |
+       value = SOR_STATE_ASY_VSYNCPOL |
                SOR_STATE_ASY_HSYNCPOL |
                SOR_STATE_ASY_PROTOCOL_DP_A |
                SOR_STATE_ASY_CRC_MODE_COMPLETE |
                SOR_STATE_ASY_OWNER(dc->pipe + 1);
+
+       switch (config.bits_per_pixel) {
+       case 24:
+               value |= SOR_STATE_ASY_PIXELDEPTH_BPP_24_444;
+               break;
+
+       case 18:
+               value |= SOR_STATE_ASY_PIXELDEPTH_BPP_18_444;
+               break;
+
+       default:
+               BUG();
+               break;
+       }
+
        tegra_sor_writel(sor, value, SOR_STATE_1);
 
        /*
@@ -620,11 +863,8 @@ static int tegra_output_sor_enable(struct tegra_output *output)
        value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff);
        tegra_sor_writel(sor, value, SOR_HEAD_STATE_4(0));
 
-       /* XXX interlaced mode */
-       tegra_sor_writel(sor, 0x00000001, SOR_HEAD_STATE_5(0));
-
        /* CSTM (LVDS, link A/B, upper) */
-       value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_B | SOR_CSTM_LINK_ACT_B |
+       value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_A | SOR_CSTM_LINK_ACT_B |
                SOR_CSTM_UPPER;
        tegra_sor_writel(sor, value, SOR_CSTM);
 
@@ -632,7 +872,7 @@ static int tegra_output_sor_enable(struct tegra_output *output)
        err = tegra_sor_setup_pwm(sor, 250);
        if (err < 0) {
                dev_err(sor->dev, "failed to setup PWM: %d\n", err);
-               return err;
+               goto unlock;
        }
 
        value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
@@ -644,18 +884,20 @@ static int tegra_output_sor_enable(struct tegra_output *output)
        err = tegra_sor_attach(sor);
        if (err < 0) {
                dev_err(sor->dev, "failed to attach SOR: %d\n", err);
-               return err;
+               goto unlock;
        }
 
        err = tegra_sor_wakeup(sor);
        if (err < 0) {
                dev_err(sor->dev, "failed to enable DC: %d\n", err);
-               return err;
+               goto unlock;
        }
 
        sor->enabled = true;
 
-       return 0;
+unlock:
+       mutex_unlock(&sor->lock);
+       return err;
 }
 
 static int tegra_sor_detach(struct tegra_sor *sor)
@@ -740,7 +982,7 @@ static int tegra_sor_power_down(struct tegra_sor *sor)
        tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
 
        /* stop lane sequencer */
-       value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_DOWN |
+       value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP |
                SOR_LANE_SEQ_CTL_POWER_STATE_DOWN;
        tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
 
@@ -783,15 +1025,17 @@ static int tegra_output_sor_disable(struct tegra_output *output)
        struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
        struct tegra_sor *sor = to_sor(output);
        unsigned long value;
-       int err;
+       int err = 0;
+
+       mutex_lock(&sor->lock);
 
        if (!sor->enabled)
-               return 0;
+               goto unlock;
 
        err = tegra_sor_detach(sor);
        if (err < 0) {
                dev_err(sor->dev, "failed to detach SOR: %d\n", err);
-               return err;
+               goto unlock;
        }
 
        tegra_sor_writel(sor, 0, SOR_STATE_1);
@@ -832,21 +1076,21 @@ static int tegra_output_sor_disable(struct tegra_output *output)
        err = tegra_sor_power_down(sor);
        if (err < 0) {
                dev_err(sor->dev, "failed to power down SOR: %d\n", err);
-               return err;
+               goto unlock;
        }
 
        if (sor->dpaux) {
                err = tegra_dpaux_disable(sor->dpaux);
                if (err < 0) {
                        dev_err(sor->dev, "failed to disable DP: %d\n", err);
-                       return err;
+                       goto unlock;
                }
        }
 
        err = tegra_io_rail_power_off(TEGRA_IO_RAIL_LVDS);
        if (err < 0) {
                dev_err(sor->dev, "failed to power off I/O rail: %d\n", err);
-               return err;
+               goto unlock;
        }
 
        reset_control_assert(sor->rst);
@@ -854,18 +1098,18 @@ static int tegra_output_sor_disable(struct tegra_output *output)
 
        sor->enabled = false;
 
-       return 0;
+unlock:
+       mutex_unlock(&sor->lock);
+       return err;
 }
 
 static int tegra_output_sor_setup_clock(struct tegra_output *output,
-                                       struct clk *clk, unsigned long pclk)
+                                       struct clk *clk, unsigned long pclk,
+                                       unsigned int *div)
 {
        struct tegra_sor *sor = to_sor(output);
        int err;
 
-       /* round to next MHz */
-       pclk = DIV_ROUND_UP(pclk / 2, 1000000) * 1000000;
-
        err = clk_set_parent(clk, sor->clk_parent);
        if (err < 0) {
                dev_err(sor->dev, "failed to set parent clock: %d\n", err);
@@ -874,11 +1118,12 @@ static int tegra_output_sor_setup_clock(struct tegra_output *output,
 
        err = clk_set_rate(sor->clk_parent, pclk);
        if (err < 0) {
-               dev_err(sor->dev, "failed to set base clock rate to %lu Hz\n",
-                       pclk * 2);
+               dev_err(sor->dev, "failed to set clock rate to %lu Hz\n", pclk);
                return err;
        }
 
+       *div = 0;
+
        return 0;
 }
 
@@ -914,9 +1159,124 @@ static const struct tegra_output_ops sor_ops = {
        .detect = tegra_output_sor_detect,
 };
 
+static int tegra_sor_crc_open(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+
+       return 0;
+}
+
+static int tegra_sor_crc_release(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+
+static int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout)
+{
+       u32 value;
+
+       timeout = jiffies + msecs_to_jiffies(timeout);
+
+       while (time_before(jiffies, timeout)) {
+               value = tegra_sor_readl(sor, SOR_CRC_A);
+               if (value & SOR_CRC_A_VALID)
+                       return 0;
+
+               usleep_range(100, 200);
+       }
+
+       return -ETIMEDOUT;
+}
+
+static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer,
+                                 size_t size, loff_t *ppos)
+{
+       struct tegra_sor *sor = file->private_data;
+       ssize_t num, err;
+       char buf[10];
+       u32 value;
+
+       mutex_lock(&sor->lock);
+
+       if (!sor->enabled) {
+               err = -EAGAIN;
+               goto unlock;
+       }
+
+       value = tegra_sor_readl(sor, SOR_STATE_1);
+       value &= ~SOR_STATE_ASY_CRC_MODE_MASK;
+       tegra_sor_writel(sor, value, SOR_STATE_1);
+
+       value = tegra_sor_readl(sor, SOR_CRC_CNTRL);
+       value |= SOR_CRC_CNTRL_ENABLE;
+       tegra_sor_writel(sor, value, SOR_CRC_CNTRL);
+
+       value = tegra_sor_readl(sor, SOR_TEST);
+       value &= ~SOR_TEST_CRC_POST_SERIALIZE;
+       tegra_sor_writel(sor, value, SOR_TEST);
+
+       err = tegra_sor_crc_wait(sor, 100);
+       if (err < 0)
+               goto unlock;
+
+       tegra_sor_writel(sor, SOR_CRC_A_RESET, SOR_CRC_A);
+       value = tegra_sor_readl(sor, SOR_CRC_B);
+
+       num = scnprintf(buf, sizeof(buf), "%08x\n", value);
+
+       err = simple_read_from_buffer(buffer, size, ppos, buf, num);
+
+unlock:
+       mutex_unlock(&sor->lock);
+       return err;
+}
+
+static const struct file_operations tegra_sor_crc_fops = {
+       .owner = THIS_MODULE,
+       .open = tegra_sor_crc_open,
+       .read = tegra_sor_crc_read,
+       .release = tegra_sor_crc_release,
+};
+
+static int tegra_sor_debugfs_init(struct tegra_sor *sor,
+                                 struct drm_minor *minor)
+{
+       struct dentry *entry;
+       int err = 0;
+
+       sor->debugfs = debugfs_create_dir("sor", minor->debugfs_root);
+       if (!sor->debugfs)
+               return -ENOMEM;
+
+       entry = debugfs_create_file("crc", 0644, sor->debugfs, sor,
+                                   &tegra_sor_crc_fops);
+       if (!entry) {
+               dev_err(sor->dev,
+                       "cannot create /sys/kernel/debug/dri/%s/sor/crc\n",
+                       minor->debugfs_root->d_name.name);
+               err = -ENOMEM;
+               goto remove;
+       }
+
+       return err;
+
+remove:
+       debugfs_remove(sor->debugfs);
+       sor->debugfs = NULL;
+       return err;
+}
+
+static int tegra_sor_debugfs_exit(struct tegra_sor *sor)
+{
+       debugfs_remove_recursive(sor->debugfs);
+       sor->debugfs = NULL;
+
+       return 0;
+}
+
 static int tegra_sor_init(struct host1x_client *client)
 {
-       struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+       struct drm_device *drm = dev_get_drvdata(client->parent);
        struct tegra_sor *sor = host1x_client_to_sor(client);
        int err;
 
@@ -928,12 +1288,18 @@ static int tegra_sor_init(struct host1x_client *client)
        sor->output.dev = sor->dev;
        sor->output.ops = &sor_ops;
 
-       err = tegra_output_init(tegra->drm, &sor->output);
+       err = tegra_output_init(drm, &sor->output);
        if (err < 0) {
                dev_err(sor->dev, "output setup failed: %d\n", err);
                return err;
        }
 
+       if (IS_ENABLED(CONFIG_DEBUG_FS)) {
+               err = tegra_sor_debugfs_init(sor, drm->primary);
+               if (err < 0)
+                       dev_err(sor->dev, "debugfs setup failed: %d\n", err);
+       }
+
        if (sor->dpaux) {
                err = tegra_dpaux_attach(sor->dpaux, &sor->output);
                if (err < 0) {
@@ -964,6 +1330,12 @@ static int tegra_sor_exit(struct host1x_client *client)
                }
        }
 
+       if (IS_ENABLED(CONFIG_DEBUG_FS)) {
+               err = tegra_sor_debugfs_exit(sor);
+               if (err < 0)
+                       dev_err(sor->dev, "debugfs cleanup failed: %d\n", err);
+       }
+
        err = tegra_output_exit(&sor->output);
        if (err < 0) {
                dev_err(sor->dev, "output cleanup failed: %d\n", err);
@@ -1045,6 +1417,8 @@ static int tegra_sor_probe(struct platform_device *pdev)
        sor->client.ops = &sor_client_ops;
        sor->client.dev = &pdev->dev;
 
+       mutex_init(&sor->lock);
+
        err = host1x_client_register(&sor->client);
        if (err < 0) {
                dev_err(&pdev->dev, "failed to register host1x client: %d\n",
index f4156d54cd058d7f9775cf2892c9bdcc19545622..a5f8853fedb5aaf391e794c45ab49f754cb6df9a 100644 (file)
@@ -47,6 +47,7 @@
 #define SOR_HEAD_STATE_4(x) (0x0d + (x))
 #define SOR_HEAD_STATE_5(x) (0x0f + (x))
 #define SOR_CRC_CNTRL 0x11
+#define  SOR_CRC_CNTRL_ENABLE                  (1 << 0)
 #define SOR_DP_DEBUG_MVID 0x12
 
 #define SOR_CLK_CNTRL 0x13
@@ -69,6 +70,7 @@
 #define  SOR_PWR_NORMAL_STATE_PU               (1 << 0)
 
 #define SOR_TEST 0x16
+#define  SOR_TEST_CRC_POST_SERIALIZE           (1 << 23)
 #define  SOR_TEST_ATTACHED                     (1 << 10)
 #define  SOR_TEST_HEAD_MODE_MASK               (3 << 8)
 #define  SOR_TEST_HEAD_MODE_AWAKE              (2 << 8)
 
 #define SOR_LVDS 0x1c
 #define SOR_CRC_A 0x1d
+#define  SOR_CRC_A_VALID                       (1 << 0)
+#define  SOR_CRC_A_RESET                       (1 << 0)
 #define SOR_CRC_B 0x1e
 #define SOR_BLANK 0x1f
 #define SOR_SEQ_CTL 0x20
index 171a8203892ce16b7767b2fb3fa11fc1995f0fad..b20b69488dc9b28d66e557578fa9ab1680984c3b 100644 (file)
@@ -268,7 +268,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
        }
 
        pm_runtime_get_sync(dev->dev);
-       ret = drm_irq_install(dev);
+       ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0));
        pm_runtime_put_sync(dev->dev);
        if (ret < 0) {
                dev_err(dev->dev, "failed to install IRQ handler\n");
index afdf383f630a4640b9cfea40c49790807bf64dd4..7094b92d1ec78467b1600d6ec2e3fd8f080d1344 100644 (file)
@@ -294,6 +294,7 @@ int udl_driver_load(struct drm_device *dev, unsigned long flags)
        dev->dev_private = udl;
 
        if (!udl_parse_vendor_descriptor(dev, dev->usbdev)) {
+               ret = -ENODEV;
                DRM_ERROR("firmware not recognized. Assume incompatible device\n");
                goto err;
        }
index a18479c6b6dae3dfdbb3d3e6e238f129dc79a3ad..6fc0648dd37f6644c62968ae05d3209edbf5b6fd 100644 (file)
@@ -737,4 +737,4 @@ const struct drm_ioctl_desc via_ioctls[] = {
        DRM_IOCTL_DEF_DRV(VIA_BLIT_SYNC, via_dma_blit_sync, DRM_AUTH)
 };
 
-int via_max_ioctl = DRM_ARRAY_SIZE(via_ioctls);
+int via_max_ioctl = ARRAY_SIZE(via_ioctls);
index 9278891054834992ba30c5b9ac2a4f5b09403438..d70b1e1544bf68f7d99c1c756d00080f435e0815 100644 (file)
@@ -79,7 +79,7 @@ int via_final_context(struct drm_device *dev, int context)
 
        /* Linux specific until context tracking code gets ported to BSD */
        /* Last context, perform cleanup */
-       if (list_is_singular(&dev->ctxlist) && dev->dev_private) {
+       if (list_is_singular(&dev->ctxlist)) {
                DRM_DEBUG("Last Context\n");
                drm_irq_uninstall(dev);
                via_cleanup_futex(dev_priv);
index b71bcd0bfbbf65a60dea1ea47beef41daddb7d8c..67720f70fe29bb10a36ea8ce67ab278cd8e20c33 100644 (file)
@@ -1,11 +1,14 @@
 config DRM_VMWGFX
        tristate "DRM driver for VMware Virtual GPU"
-       depends on DRM && PCI && FB
+       depends on DRM && PCI
        select FB_DEFERRED_IO
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
        select FB_CFB_IMAGEBLIT
        select DRM_TTM
+       # Only needed for the transitional use of drm_crtc_init - can be removed
+       # again once vmwgfx sets up the primary plane itself.
+       select DRM_KMS_HELPER
        help
          Choose this option if you would like to run 3D acceleration
          in a VMware virtual machine.
@@ -14,7 +17,7 @@ config DRM_VMWGFX
          The compiled module will be called "vmwgfx.ko".
 
 config DRM_VMWGFX_FBCON
-       depends on DRM_VMWGFX
+       depends on DRM_VMWGFX && FB
        bool "Enable framebuffer console under vmwgfx by default"
        help
           Choose this option if you are shipping a new vmwgfx
index 4a223bbea3b34ee5489d25810ab04e14f1d2c7e3..246a62bab378d1838e5dd60cb39fa796ff0d7497 100644 (file)
@@ -806,7 +806,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
        }
 
        if (dev_priv->capabilities & SVGA_CAP_IRQMASK) {
-               ret = drm_irq_install(dev);
+               ret = drm_irq_install(dev, dev->pdev->irq);
                if (ret != 0) {
                        DRM_ERROR("Failed installing irq: %d\n", ret);
                        goto out_no_irq;
@@ -1417,7 +1417,7 @@ static struct drm_driver driver = {
        .enable_vblank = vmw_enable_vblank,
        .disable_vblank = vmw_disable_vblank,
        .ioctls = vmw_ioctls,
-       .num_ioctls = DRM_ARRAY_SIZE(vmw_ioctls),
+       .num_ioctls = ARRAY_SIZE(vmw_ioctls),
        .master_create = vmw_master_create,
        .master_destroy = vmw_master_destroy,
        .master_set = vmw_master_set,
index a2dde5ad81385bf932dd398bd522d1a950098f85..8f3edc4710f2869344fec29c6366caa2a1d77dc2 100644 (file)
@@ -187,7 +187,7 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
         * can do this since the caller in the drm core doesn't check anything
         * which is protected by any looks.
         */
-       mutex_unlock(&crtc->mutex);
+       drm_modeset_unlock(&crtc->mutex);
        drm_modeset_lock_all(dev_priv->dev);
 
        /* A lot of the code assumes this */
@@ -252,7 +252,7 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
        ret = 0;
 out:
        drm_modeset_unlock_all(dev_priv->dev);
-       mutex_lock(&crtc->mutex);
+       drm_modeset_lock(&crtc->mutex, NULL);
 
        return ret;
 }
@@ -273,7 +273,7 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
         * can do this since the caller in the drm core doesn't check anything
         * which is protected by any looks.
         */
-       mutex_unlock(&crtc->mutex);
+       drm_modeset_unlock(&crtc->mutex);
        drm_modeset_lock_all(dev_priv->dev);
 
        vmw_cursor_update_position(dev_priv, shown,
@@ -281,7 +281,7 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
                                   du->cursor_y + du->hotspot_y);
 
        drm_modeset_unlock_all(dev_priv->dev);
-       mutex_lock(&crtc->mutex);
+       drm_modeset_lock(&crtc->mutex, NULL);
 
        return 0;
 }
@@ -2001,7 +2001,7 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector,
        if (du->pref_mode)
                list_move(&du->pref_mode->head, &connector->probed_modes);
 
-       drm_mode_connector_list_update(connector);
+       drm_mode_connector_list_update(connector, true);
 
        return 1;
 }
index ccdd2e6da5e3710a205e1b5dc3a9c667a9ed6bcf..aaf54859adb05c4c7ffaffabb51bb333e09d1528 100644 (file)
@@ -216,8 +216,8 @@ int host1x_device_exit(struct host1x_device *device)
 }
 EXPORT_SYMBOL(host1x_device_exit);
 
-static int host1x_register_client(struct host1x *host1x,
-                                 struct host1x_client *client)
+static int host1x_add_client(struct host1x *host1x,
+                            struct host1x_client *client)
 {
        struct host1x_device *device;
        struct host1x_subdev *subdev;
@@ -238,8 +238,8 @@ static int host1x_register_client(struct host1x *host1x,
        return -ENODEV;
 }
 
-static int host1x_unregister_client(struct host1x *host1x,
-                                   struct host1x_client *client)
+static int host1x_del_client(struct host1x *host1x,
+                            struct host1x_client *client)
 {
        struct host1x_device *device, *dt;
        struct host1x_subdev *subdev;
@@ -503,7 +503,7 @@ int host1x_client_register(struct host1x_client *client)
        mutex_lock(&devices_lock);
 
        list_for_each_entry(host1x, &devices, list) {
-               err = host1x_register_client(host1x, client);
+               err = host1x_add_client(host1x, client);
                if (!err) {
                        mutex_unlock(&devices_lock);
                        return 0;
@@ -529,7 +529,7 @@ int host1x_client_unregister(struct host1x_client *client)
        mutex_lock(&devices_lock);
 
        list_for_each_entry(host1x, &devices, list) {
-               err = host1x_unregister_client(host1x, client);
+               err = host1x_del_client(host1x, client);
                if (!err) {
                        mutex_unlock(&devices_lock);
                        return 0;
diff --git a/drivers/gpu/ipu-v3/Kconfig b/drivers/gpu/ipu-v3/Kconfig
new file mode 100644 (file)
index 0000000..2f228a2
--- /dev/null
@@ -0,0 +1,7 @@
+config IMX_IPUV3_CORE
+       tristate "IPUv3 core support"
+       depends on SOC_IMX5 || SOC_IMX6Q || SOC_IMX6SL || ARCH_MULTIPLATFORM
+       depends on RESET_CONTROLLER
+       help
+         Choose this if you have a i.MX5/6 system and want to use the Image
+         Processing Unit. This option only enables IPU base support.
similarity index 51%
rename from drivers/staging/imx-drm/ipu-v3/Makefile
rename to drivers/gpu/ipu-v3/Makefile
index 28ed72e98a96bf2d5e858c8304f4d0b9153938d6..1887972b4ac299b3a40c6669d2e22fb6b5d1646b 100644 (file)
@@ -1,3 +1,3 @@
-obj-$(CONFIG_DRM_IMX_IPUV3_CORE) += imx-ipu-v3.o
+obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o
 
-imx-ipu-v3-objs := ipu-common.o ipu-dc.o ipu-di.o ipu-dp.o ipu-dmfc.o
+imx-ipu-v3-objs := ipu-common.o ipu-dc.o ipu-di.o ipu-dp.o ipu-dmfc.o ipu-smfc.o
similarity index 94%
rename from drivers/staging/imx-drm/ipu-v3/ipu-common.c
rename to drivers/gpu/ipu-v3/ipu-common.c
index a1f7b2001c8a13133824f298484c3094d77aa400..04e7b2eafbdd7b127fd540873cd334693f4f4d40 100644 (file)
@@ -31,7 +31,7 @@
 
 #include <drm/drm_fourcc.h>
 
-#include "imx-ipu-v3.h"
+#include <video/imx-ipu-v3.h>
 #include "ipu-prv.h"
 
 static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset)
@@ -661,6 +661,39 @@ int ipu_module_disable(struct ipu_soc *ipu, u32 mask)
 }
 EXPORT_SYMBOL_GPL(ipu_module_disable);
 
+int ipu_csi_enable(struct ipu_soc *ipu, int csi)
+{
+       return ipu_module_enable(ipu, csi ? IPU_CONF_CSI1_EN : IPU_CONF_CSI0_EN);
+}
+EXPORT_SYMBOL_GPL(ipu_csi_enable);
+
+int ipu_csi_disable(struct ipu_soc *ipu, int csi)
+{
+       return ipu_module_disable(ipu, csi ? IPU_CONF_CSI1_EN : IPU_CONF_CSI0_EN);
+}
+EXPORT_SYMBOL_GPL(ipu_csi_disable);
+
+int ipu_smfc_enable(struct ipu_soc *ipu)
+{
+       return ipu_module_enable(ipu, IPU_CONF_SMFC_EN);
+}
+EXPORT_SYMBOL_GPL(ipu_smfc_enable);
+
+int ipu_smfc_disable(struct ipu_soc *ipu)
+{
+       return ipu_module_disable(ipu, IPU_CONF_SMFC_EN);
+}
+EXPORT_SYMBOL_GPL(ipu_smfc_disable);
+
+int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel)
+{
+       struct ipu_soc *ipu = channel->ipu;
+       unsigned int chno = channel->num;
+
+       return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer);
+
 void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num)
 {
        struct ipu_soc *ipu = channel->ipu;
@@ -896,8 +929,17 @@ static int ipu_submodules_init(struct ipu_soc *ipu,
                goto err_dp;
        }
 
+       ret = ipu_smfc_init(ipu, dev, ipu_base +
+                       devtype->cm_ofs + IPU_CM_SMFC_REG_OFS);
+       if (ret) {
+               unit = "smfc";
+               goto err_smfc;
+       }
+
        return 0;
 
+err_smfc:
+       ipu_dp_exit(ipu);
 err_dp:
        ipu_dmfc_exit(ipu);
 err_dmfc:
@@ -977,6 +1019,7 @@ EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq);
 
 static void ipu_submodules_exit(struct ipu_soc *ipu)
 {
+       ipu_smfc_exit(ipu);
        ipu_dp_exit(ipu);
        ipu_dmfc_exit(ipu);
        ipu_dc_exit(ipu);
@@ -1001,6 +1044,7 @@ static void platform_device_unregister_children(struct platform_device *pdev)
 struct ipu_platform_reg {
        struct ipu_client_platformdata pdata;
        const char *name;
+       int reg_offset;
 };
 
 static const struct ipu_platform_reg client_reg[] = {
@@ -1022,13 +1066,29 @@ static const struct ipu_platform_reg client_reg[] = {
                        .dma[1] = -EINVAL,
                },
                .name = "imx-ipuv3-crtc",
+       }, {
+               .pdata = {
+                       .csi = 0,
+                       .dma[0] = IPUV3_CHANNEL_CSI0,
+                       .dma[1] = -EINVAL,
+               },
+               .reg_offset = IPU_CM_CSI0_REG_OFS,
+               .name = "imx-ipuv3-camera",
+       }, {
+               .pdata = {
+                       .csi = 1,
+                       .dma[0] = IPUV3_CHANNEL_CSI1,
+                       .dma[1] = -EINVAL,
+               },
+               .reg_offset = IPU_CM_CSI1_REG_OFS,
+               .name = "imx-ipuv3-camera",
        },
 };
 
 static DEFINE_MUTEX(ipu_client_id_mutex);
 static int ipu_client_id;
 
-static int ipu_add_client_devices(struct ipu_soc *ipu)
+static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base)
 {
        struct device *dev = ipu->dev;
        unsigned i;
@@ -1042,9 +1102,19 @@ static int ipu_add_client_devices(struct ipu_soc *ipu)
        for (i = 0; i < ARRAY_SIZE(client_reg); i++) {
                const struct ipu_platform_reg *reg = &client_reg[i];
                struct platform_device *pdev;
-
-               pdev = platform_device_register_data(dev, reg->name,
-                       id++, &reg->pdata, sizeof(reg->pdata));
+               struct resource res;
+
+               if (reg->reg_offset) {
+                       memset(&res, 0, sizeof(res));
+                       res.flags = IORESOURCE_MEM;
+                       res.start = ipu_base + ipu->devtype->cm_ofs + reg->reg_offset;
+                       res.end = res.start + PAGE_SIZE - 1;
+                       pdev = platform_device_register_resndata(dev, reg->name,
+                               id++, &res, 1, &reg->pdata, sizeof(reg->pdata));
+               } else {
+                       pdev = platform_device_register_data(dev, reg->name,
+                               id++, &reg->pdata, sizeof(reg->pdata));
+               }
 
                if (IS_ERR(pdev))
                        goto err_register;
@@ -1241,7 +1311,7 @@ static int ipu_probe(struct platform_device *pdev)
        if (ret)
                goto failed_submodules_init;
 
-       ret = ipu_add_client_devices(ipu);
+       ret = ipu_add_client_devices(ipu, ipu_base);
        if (ret) {
                dev_err(&pdev->dev, "adding client devices failed with %d\n",
                                ret);
similarity index 99%
rename from drivers/staging/imx-drm/ipu-v3/ipu-dc.c
rename to drivers/gpu/ipu-v3/ipu-dc.c
index 784a4a13eac3d0dd0ee8076777d4220542284523..2326c752d89b2086cb51234ef9b55ccda6bec87b 100644 (file)
@@ -21,8 +21,7 @@
 #include <linux/interrupt.h>
 #include <linux/io.h>
 
-#include "../imx-drm.h"
-#include "imx-ipu-v3.h"
+#include <video/imx-ipu-v3.h>
 #include "ipu-prv.h"
 
 #define DC_MAP_CONF_PTR(n)     (0x108 + ((n) & ~0x1) * 2)
similarity index 99%
rename from drivers/staging/imx-drm/ipu-v3/ipu-di.c
rename to drivers/gpu/ipu-v3/ipu-di.c
index 849b3e120ef0898e9117d89782db295126cd37cd..c490ba4384fc5c0b2f65621d2079a384eb4eba1f 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/err.h>
 #include <linux/platform_device.h>
 
-#include "imx-ipu-v3.h"
+#include <video/imx-ipu-v3.h>
 #include "ipu-prv.h"
 
 struct ipu_di {
similarity index 99%
rename from drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c
rename to drivers/gpu/ipu-v3/ipu-dmfc.c
index 59f182b28fc1ce2e199aebf7d102407018c25fb6..042c3958e2a099224b25cafd194e076b7150f67c 100644 (file)
@@ -17,7 +17,7 @@
 #include <linux/errno.h>
 #include <linux/io.h>
 
-#include "imx-ipu-v3.h"
+#include <video/imx-ipu-v3.h>
 #include "ipu-prv.h"
 
 #define DMFC_RD_CHAN           0x0000
similarity index 99%
rename from drivers/staging/imx-drm/ipu-v3/ipu-dp.c
rename to drivers/gpu/ipu-v3/ipu-dp.c
index d90f82a87d19d4f9ffd29a7f6ff6a5429cf3c26e..98686edbcdbb05a4ec389632007ee9043afafec2 100644 (file)
@@ -19,7 +19,7 @@
 #include <linux/io.h>
 #include <linux/err.h>
 
-#include "imx-ipu-v3.h"
+#include <video/imx-ipu-v3.h>
 #include "ipu-prv.h"
 
 #define DP_SYNC 0
similarity index 96%
rename from drivers/staging/imx-drm/ipu-v3/ipu-prv.h
rename to drivers/gpu/ipu-v3/ipu-prv.h
index bfc1b336648823ab3c6be6a6fdecea7cdd430942..c93f50ec04f72f2ee7cf41f94b1c21285a72d1a9 100644 (file)
@@ -22,7 +22,7 @@ struct ipu_soc;
 #include <linux/clk.h>
 #include <linux/platform_device.h>
 
-#include "imx-ipu-v3.h"
+#include <video/imx-ipu-v3.h>
 
 #define IPUV3_CHANNEL_CSI0                      0
 #define IPUV3_CHANNEL_CSI1                      1
@@ -151,6 +151,8 @@ struct ipuv3_channel {
 struct ipu_dc_priv;
 struct ipu_dmfc_priv;
 struct ipu_di;
+struct ipu_smfc_priv;
+
 struct ipu_devtype;
 
 struct ipu_soc {
@@ -178,6 +180,7 @@ struct ipu_soc {
        struct ipu_dp_priv      *dp_priv;
        struct ipu_dmfc_priv    *dmfc_priv;
        struct ipu_di           *di_priv[2];
+       struct ipu_smfc_priv    *smfc_priv;
 };
 
 void ipu_srm_dp_sync_update(struct ipu_soc *ipu);
@@ -206,4 +209,7 @@ void ipu_dc_exit(struct ipu_soc *ipu);
 int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base);
 void ipu_cpmem_exit(struct ipu_soc *ipu);
 
+int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base);
+void ipu_smfc_exit(struct ipu_soc *ipu);
+
 #endif                         /* __IPU_PRV_H__ */
diff --git a/drivers/gpu/ipu-v3/ipu-smfc.c b/drivers/gpu/ipu-v3/ipu-smfc.c
new file mode 100644 (file)
index 0000000..e4f85ad
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#define DEBUG
+#include <linux/export.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <video/imx-ipu-v3.h>
+
+#include "ipu-prv.h"
+
+struct ipu_smfc_priv {
+       void __iomem *base;
+       spinlock_t lock;
+};
+
+/*SMFC Registers */
+#define SMFC_MAP       0x0000
+#define SMFC_WMC       0x0004
+#define SMFC_BS                0x0008
+
+int ipu_smfc_set_burstsize(struct ipu_soc *ipu, int channel, int burstsize)
+{
+       struct ipu_smfc_priv *smfc = ipu->smfc_priv;
+       unsigned long flags;
+       u32 val, shift;
+
+       spin_lock_irqsave(&smfc->lock, flags);
+
+       shift = channel * 4;
+       val = readl(smfc->base + SMFC_BS);
+       val &= ~(0xf << shift);
+       val |= burstsize << shift;
+       writel(val, smfc->base + SMFC_BS);
+
+       spin_unlock_irqrestore(&smfc->lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_smfc_set_burstsize);
+
+int ipu_smfc_map_channel(struct ipu_soc *ipu, int channel, int csi_id, int mipi_id)
+{
+       struct ipu_smfc_priv *smfc = ipu->smfc_priv;
+       unsigned long flags;
+       u32 val, shift;
+
+       spin_lock_irqsave(&smfc->lock, flags);
+
+       shift = channel * 3;
+       val = readl(smfc->base + SMFC_MAP);
+       val &= ~(0x7 << shift);
+       val |= ((csi_id << 2) | mipi_id) << shift;
+       writel(val, smfc->base + SMFC_MAP);
+
+       spin_unlock_irqrestore(&smfc->lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_smfc_map_channel);
+
+int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev,
+                 unsigned long base)
+{
+       struct ipu_smfc_priv *smfc;
+
+       smfc = devm_kzalloc(dev, sizeof(*smfc), GFP_KERNEL);
+       if (!smfc)
+               return -ENOMEM;
+
+       ipu->smfc_priv = smfc;
+       spin_lock_init(&smfc->lock);
+
+       smfc->base = devm_ioremap(dev, base, PAGE_SIZE);
+       if (!smfc->base)
+               return -ENOMEM;
+
+       pr_debug("%s: ioremap 0x%08lx -> %p\n", __func__, base, smfc->base);
+
+       return 0;
+}
+
+void ipu_smfc_exit(struct ipu_soc *ipu)
+{
+}
index ec0ae2d1686a3150c03403fa42524aca2111afa4..6866448083b2c1d6ad99686c2854db9ad94a5bb7 100644 (file)
@@ -623,7 +623,8 @@ static int vga_switcheroo_runtime_suspend(struct device *dev)
        ret = dev->bus->pm->runtime_suspend(dev);
        if (ret)
                return ret;
-
+       if (vgasr_priv.handler->switchto)
+               vgasr_priv.handler->switchto(VGA_SWITCHEROO_IGD);
        vga_switcheroo_power_switch(pdev, VGA_SWITCHEROO_OFF);
        return 0;
 }
index 24883b4d1a49d40a2bb3462ee36ed094f474b3cd..cc2bd20221989aa0284269cce533c28ab2e7295c 100644 (file)
@@ -52,7 +52,7 @@ static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range);
 static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf);
 static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
 
-static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store);
+static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IROTH, lg4ff_range_show, lg4ff_range_store);
 
 struct lg4ff_device_entry {
        __u32 product_id;
index c930ab8554eac830dd8d615e86c01926bb716be0..7f965e2314335857df32bb70c5b9472f7620e96d 100644 (file)
@@ -501,7 +501,7 @@ static ssize_t picolcd_fb_update_rate_store(struct device *dev,
        return count;
 }
 
-static DEVICE_ATTR(fb_update_rate, 0666, picolcd_fb_update_rate_show,
+static DEVICE_ATTR(fb_update_rate, 0664, picolcd_fb_update_rate_show,
                picolcd_fb_update_rate_store);
 
 /* initialize Framebuffer device */
index 71b9f9ab86e4810a3284b0df524ae46b66f6c021..bc60dec3f586fec38c995af8300506a5f7caf5b5 100644 (file)
@@ -15,7 +15,7 @@ config NOKIA_MODEM
 
 config SSI_PROTOCOL
        tristate "SSI protocol"
-       depends on HSI && PHONET && (OMAP_SSI=y || OMAP_SSI=m)
+       depends on HSI && PHONET && OMAP_SSI
        help
        If you say Y here, you will enable the SSI protocol aka McSAAB.
 
index b8693f0b27fe05295690b39533c563ff4477ab6c..29aea0b9336071b05ce2efed2fe7590997b472dc 100644 (file)
@@ -1116,8 +1116,7 @@ static int __init ssi_port_probe(struct platform_device *pd)
 
        dev_dbg(&pd->dev, "init ssi port...\n");
 
-       err = ref_module(THIS_MODULE, ssi->owner);
-       if (err) {
+       if (!try_module_get(ssi->owner)) {
                dev_err(&pd->dev, "could not increment parent module refcount (err=%d)\n",
                        err);
                return -ENODEV;
@@ -1254,6 +1253,7 @@ static int __exit ssi_port_remove(struct platform_device *pd)
 
        omap_ssi->port[omap_port->port_id] = NULL;
        platform_set_drvdata(pd, NULL);
+       module_put(ssi->owner);
        pm_runtime_disable(&pd->dev);
 
        return 0;
index 6c8b032cacba7c0e7a21d37a5dd4de9d9d42e19b..ed9350d42764e06bb38eacfdf8756ae8ac43b2ba 100644 (file)
@@ -404,7 +404,7 @@ static u32  next_vp;
  * performance critical channels (IDE, SCSI and Network) will be uniformly
  * distributed across all available CPUs.
  */
-static void init_vp_index(struct vmbus_channel *channel, uuid_le *type_guid)
+static void init_vp_index(struct vmbus_channel *channel, const uuid_le *type_guid)
 {
        u32 cur_cpu;
        int i;
index 18d1a8404cbc0e1f9ef7dcb12d07132fba66cdb3..22b750749a39c90393012cac83db6ff4bbff64ad 100644 (file)
@@ -649,9 +649,9 @@ extern struct vmbus_connection vmbus_connection;
 
 /* General vmbus interface */
 
-struct hv_device *vmbus_device_create(uuid_le *type,
-                                        uuid_le *instance,
-                                        struct vmbus_channel *channel);
+struct hv_device *vmbus_device_create(const uuid_le *type,
+                                     const uuid_le *instance,
+                                     struct vmbus_channel *channel);
 
 int vmbus_device_register(struct hv_device *child_device_obj);
 void vmbus_device_unregister(struct hv_device *device_obj);
index 8e53a3c2607e00c07e066b28f11c056aef4a6abd..4d6b26979fbd54e457dfbcea4bfc9a6a26ec846c 100644 (file)
@@ -435,7 +435,7 @@ static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env)
        return ret;
 }
 
-static uuid_le null_guid;
+static const uuid_le null_guid;
 
 static inline bool is_null_guid(const __u8 *guid)
 {
@@ -450,7 +450,7 @@ static inline bool is_null_guid(const __u8 *guid)
  */
 static const struct hv_vmbus_device_id *hv_vmbus_get_id(
                                        const struct hv_vmbus_device_id *id,
-                                       __u8 *guid)
+                                       const __u8 *guid)
 {
        for (; !is_null_guid(id->guid); id++)
                if (!memcmp(&id->guid, guid, sizeof(uuid_le)))
@@ -779,9 +779,9 @@ EXPORT_SYMBOL_GPL(vmbus_driver_unregister);
  * vmbus_device_create - Creates and registers a new child device
  * on the vmbus.
  */
-struct hv_device *vmbus_device_create(uuid_le *type,
-                                           uuid_le *instance,
-                                           struct vmbus_channel *channel)
+struct hv_device *vmbus_device_create(const uuid_le *type,
+                                     const uuid_le *instance,
+                                     struct vmbus_channel *channel)
 {
        struct hv_device *child_device_obj;
 
index 00343166feb12f890f81d81589573e26c0edd435..08531a128f53ceaa6ba18dfaea716506848e4974 100644 (file)
@@ -1124,6 +1124,16 @@ config SENSORS_SHT21
          This driver can also be built as a module.  If so, the module
          will be called sht21.
 
+config SENSORS_SHTC1
+       tristate "Sensiron humidity and temperature sensors. SHTC1 and compat."
+       depends on I2C
+       help
+         If you say yes here you get support for the Sensiron SHTC1 and SHTW1
+         humidity and temperature sensors.
+
+         This driver can also be built as a module.  If so, the module
+         will be called shtc1.
+
 config SENSORS_S3C
        tristate "Samsung built-in ADC"
        depends on S3C_ADC
index 11798ad7e801bc71ee419c1a2e5bdd190b6221c2..3dc0f02f71d265ff218d8ed44958e6cf59af6676 100644 (file)
@@ -126,6 +126,7 @@ obj-$(CONFIG_SENSORS_SCH5627)       += sch5627.o
 obj-$(CONFIG_SENSORS_SCH5636)  += sch5636.o
 obj-$(CONFIG_SENSORS_SHT15)    += sht15.o
 obj-$(CONFIG_SENSORS_SHT21)    += sht21.o
+obj-$(CONFIG_SENSORS_SHTC1)    += shtc1.o
 obj-$(CONFIG_SENSORS_SIS5595)  += sis5595.o
 obj-$(CONFIG_SENSORS_SMM665)   += smm665.o
 obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
index 6edce42c61d51188df6d5e3e652125197558da9b..2ae8a304b5effb885b687fa5debe7745cec683df 100644 (file)
@@ -45,30 +45,6 @@ MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
 
 static const unsigned short normal_i2c[] = { 0x37, 0x4e, I2C_CLIENT_END };
 
-static int atxp1_probe(struct i2c_client *client,
-                      const struct i2c_device_id *id);
-static int atxp1_remove(struct i2c_client *client);
-static struct atxp1_data *atxp1_update_device(struct device *dev);
-static int atxp1_detect(struct i2c_client *client, struct i2c_board_info *info);
-
-static const struct i2c_device_id atxp1_id[] = {
-       { "atxp1", 0 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, atxp1_id);
-
-static struct i2c_driver atxp1_driver = {
-       .class          = I2C_CLASS_HWMON,
-       .driver = {
-               .name   = "atxp1",
-       },
-       .probe          = atxp1_probe,
-       .remove         = atxp1_remove,
-       .id_table       = atxp1_id,
-       .detect         = atxp1_detect,
-       .address_list   = normal_i2c,
-};
-
 struct atxp1_data {
        struct device *hwmon_dev;
        struct mutex update_lock;
@@ -386,4 +362,22 @@ static int atxp1_remove(struct i2c_client *client)
        return 0;
 };
 
+static const struct i2c_device_id atxp1_id[] = {
+       { "atxp1", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, atxp1_id);
+
+static struct i2c_driver atxp1_driver = {
+       .class          = I2C_CLASS_HWMON,
+       .driver = {
+               .name   = "atxp1",
+       },
+       .probe          = atxp1_probe,
+       .remove         = atxp1_remove,
+       .id_table       = atxp1_id,
+       .detect         = atxp1_detect,
+       .address_list   = normal_i2c,
+};
+
 module_i2c_driver(atxp1_driver);
index 93d26e8af3e2002b8838c08b13f06787e6021dde..bfd3f3eeabcd90cd52ad4cdef5155f00195e2c26 100644 (file)
@@ -148,7 +148,8 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg)
 
        switch (reg) {
        case INA2XX_SHUNT_VOLTAGE:
-               val = DIV_ROUND_CLOSEST(data->regs[reg],
+               /* signed register */
+               val = DIV_ROUND_CLOSEST((s16)data->regs[reg],
                                        data->config->shunt_div);
                break;
        case INA2XX_BUS_VOLTAGE:
@@ -160,8 +161,8 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg)
                val = data->regs[reg] * data->config->power_lsb;
                break;
        case INA2XX_CURRENT:
-               /* LSB=1mA (selected). Is in mA */
-               val = data->regs[reg];
+               /* signed register, LSB=1mA (selected), in mA */
+               val = (s16)data->regs[reg];
                break;
        default:
                /* programmer goofed */
index bed4af358308def4085d603665641b744a17b0f5..b0129a54e1a6b13a36170b672a97f7ae86f52d21 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (c) 2002, 2003  Philip Pokorny <ppokorny@penguincomputing.com>
  * Copyright (c) 2003        Margit Schubert-While <margitsw@t-online.de>
  * Copyright (c) 2004        Justin Thiessen <jthiessen@penguincomputing.com>
- * Copyright (C) 2007--2009  Jean Delvare <jdelvare@suse.de>
+ * Copyright (C) 2007--2014  Jean Delvare <jdelvare@suse.de>
  *
  * Chip details at           <http://www.national.com/ds/LM/LM85.pdf>
  *
@@ -39,7 +39,7 @@
 static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
 
 enum chips {
-       any_chip, lm85b, lm85c,
+       lm85,
        adm1027, adt7463, adt7468,
        emc6d100, emc6d102, emc6d103, emc6d103s
 };
@@ -75,9 +75,6 @@ enum chips {
 #define LM85_COMPANY_NATIONAL          0x01
 #define LM85_COMPANY_ANALOG_DEV                0x41
 #define LM85_COMPANY_SMSC              0x5c
-#define LM85_VERSTEP_VMASK              0xf0
-#define LM85_VERSTEP_GENERIC           0x60
-#define LM85_VERSTEP_GENERIC2          0x70
 #define LM85_VERSTEP_LM85C             0x60
 #define LM85_VERSTEP_LM85B             0x62
 #define LM85_VERSTEP_LM96000_1         0x68
@@ -351,9 +348,9 @@ static const struct i2c_device_id lm85_id[] = {
        { "adm1027", adm1027 },
        { "adt7463", adt7463 },
        { "adt7468", adt7468 },
-       { "lm85", any_chip },
-       { "lm85b", lm85b },
-       { "lm85c", lm85c },
+       { "lm85", lm85 },
+       { "lm85b", lm85 },
+       { "lm85c", lm85 },
        { "emc6d100", emc6d100 },
        { "emc6d101", emc6d100 },
        { "emc6d102", emc6d102 },
@@ -1281,7 +1278,7 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
        int address = client->addr;
-       const char *type_name;
+       const char *type_name = NULL;
        int company, verstep;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
@@ -1297,16 +1294,6 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info)
                "Detecting device at 0x%02x with COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
                address, company, verstep);
 
-       /* All supported chips have the version in common */
-       if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC &&
-           (verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC2) {
-               dev_dbg(&adapter->dev,
-                       "Autodetection failed: unsupported version\n");
-               return -ENODEV;
-       }
-       type_name = "lm85";
-
-       /* Now, refine the detection */
        if (company == LM85_COMPANY_NATIONAL) {
                switch (verstep) {
                case LM85_VERSTEP_LM85C:
@@ -1323,6 +1310,7 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info)
                                        "Found Winbond WPCD377I, ignoring\n");
                                return -ENODEV;
                        }
+                       type_name = "lm85";
                        break;
                }
        } else if (company == LM85_COMPANY_ANALOG_DEV) {
@@ -1357,12 +1345,11 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info)
                        type_name = "emc6d103s";
                        break;
                }
-       } else {
-               dev_dbg(&adapter->dev,
-                       "Autodetection failed: unknown vendor\n");
-               return -ENODEV;
        }
 
+       if (!type_name)
+               return -ENODEV;
+
        strlcpy(info->type, type_name, I2C_NAME_SIZE);
 
        return 0;
index af81be1237c950739abf897e3c7f0b7f4307850d..c86a1840249627a7d0d6f5367c8aa28add1d7813 100644 (file)
@@ -47,7 +47,7 @@
 #define LTC4151_ADIN_L 0x05
 
 struct ltc4151_data {
-       struct device *hwmon_dev;
+       struct i2c_client *client;
 
        struct mutex update_lock;
        bool valid;
@@ -59,8 +59,8 @@ struct ltc4151_data {
 
 static struct ltc4151_data *ltc4151_update_device(struct device *dev)
 {
-       struct i2c_client *client = to_i2c_client(dev);
-       struct ltc4151_data *data = i2c_get_clientdata(client);
+       struct ltc4151_data *data = dev_get_drvdata(dev);
+       struct i2c_client *client = data->client;
        struct ltc4151_data *ret = data;
 
        mutex_lock(&data->update_lock);
@@ -159,7 +159,7 @@ static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4151_show_value, NULL,
  * Finally, construct an array of pointers to members of the above objects,
  * as required for sysfs_create_group()
  */
-static struct attribute *ltc4151_attributes[] = {
+static struct attribute *ltc4151_attrs[] = {
        &sensor_dev_attr_in1_input.dev_attr.attr,
        &sensor_dev_attr_in2_input.dev_attr.attr,
 
@@ -167,54 +167,30 @@ static struct attribute *ltc4151_attributes[] = {
 
        NULL,
 };
-
-static const struct attribute_group ltc4151_group = {
-       .attrs = ltc4151_attributes,
-};
+ATTRIBUTE_GROUPS(ltc4151);
 
 static int ltc4151_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
        struct i2c_adapter *adapter = client->adapter;
+       struct device *dev = &client->dev;
        struct ltc4151_data *data;
-       int ret;
+       struct device *hwmon_dev;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
-       data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
 
-       i2c_set_clientdata(client, data);
+       data->client = client;
        mutex_init(&data->update_lock);
 
-       /* Register sysfs hooks */
-       ret = sysfs_create_group(&client->dev.kobj, &ltc4151_group);
-       if (ret)
-               return ret;
-
-       data->hwmon_dev = hwmon_device_register(&client->dev);
-       if (IS_ERR(data->hwmon_dev)) {
-               ret = PTR_ERR(data->hwmon_dev);
-               goto out_hwmon_device_register;
-       }
-
-       return 0;
-
-out_hwmon_device_register:
-       sysfs_remove_group(&client->dev.kobj, &ltc4151_group);
-       return ret;
-}
-
-static int ltc4151_remove(struct i2c_client *client)
-{
-       struct ltc4151_data *data = i2c_get_clientdata(client);
-
-       hwmon_device_unregister(data->hwmon_dev);
-       sysfs_remove_group(&client->dev.kobj, &ltc4151_group);
-
-       return 0;
+       hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+                                                          data,
+                                                          ltc4151_groups);
+       return PTR_ERR_OR_ZERO(hwmon_dev);
 }
 
 static const struct i2c_device_id ltc4151_id[] = {
@@ -229,7 +205,6 @@ static struct i2c_driver ltc4151_driver = {
                .name   = "ltc4151",
        },
        .probe          = ltc4151_probe,
-       .remove         = ltc4151_remove,
        .id_table       = ltc4151_id,
 };
 
diff --git a/drivers/hwmon/shtc1.c b/drivers/hwmon/shtc1.c
new file mode 100644 (file)
index 0000000..decd7df
--- /dev/null
@@ -0,0 +1,251 @@
+/* Sensirion SHTC1 humidity and temperature sensor driver
+ *
+ * Copyright (C) 2014 Sensirion AG, Switzerland
+ * Author: Johannes Winkelmann <johannes.winkelmann@sensirion.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/platform_data/shtc1.h>
+
+/* commands (high precision mode) */
+static const unsigned char shtc1_cmd_measure_blocking_hpm[]    = { 0x7C, 0xA2 };
+static const unsigned char shtc1_cmd_measure_nonblocking_hpm[] = { 0x78, 0x66 };
+
+/* commands (low precision mode) */
+static const unsigned char shtc1_cmd_measure_blocking_lpm[]    = { 0x64, 0x58 };
+static const unsigned char shtc1_cmd_measure_nonblocking_lpm[] = { 0x60, 0x9c };
+
+/* command for reading the ID register */
+static const unsigned char shtc1_cmd_read_id_reg[]            = { 0xef, 0xc8 };
+
+/* constants for reading the ID register */
+#define SHTC1_ID         0x07
+#define SHTC1_ID_REG_MASK 0x1f
+
+/* delays for non-blocking i2c commands, both in us */
+#define SHTC1_NONBLOCKING_WAIT_TIME_HPM  14400
+#define SHTC1_NONBLOCKING_WAIT_TIME_LPM   1000
+
+#define SHTC1_CMD_LENGTH      2
+#define SHTC1_RESPONSE_LENGTH 6
+
+struct shtc1_data {
+       struct i2c_client *client;
+       struct mutex update_lock;
+       bool valid;
+       unsigned long last_updated; /* in jiffies */
+
+       const unsigned char *command;
+       unsigned int nonblocking_wait_time; /* in us */
+
+       struct shtc1_platform_data setup;
+
+       int temperature; /* 1000 * temperature in dgr C */
+       int humidity; /* 1000 * relative humidity in %RH */
+};
+
+static int shtc1_update_values(struct i2c_client *client,
+                              struct shtc1_data *data,
+                              char *buf, int bufsize)
+{
+       int ret = i2c_master_send(client, data->command, SHTC1_CMD_LENGTH);
+       if (ret != SHTC1_CMD_LENGTH) {
+               dev_err(&client->dev, "failed to send command: %d\n", ret);
+               return ret < 0 ? ret : -EIO;
+       }
+
+       /*
+        * In blocking mode (clock stretching mode) the I2C bus
+        * is blocked for other traffic, thus the call to i2c_master_recv()
+        * will wait until the data is ready. For non blocking mode, we
+        * have to wait ourselves.
+        */
+       if (!data->setup.blocking_io)
+               usleep_range(data->nonblocking_wait_time,
+                            data->nonblocking_wait_time + 1000);
+
+       ret = i2c_master_recv(client, buf, bufsize);
+       if (ret != bufsize) {
+               dev_err(&client->dev, "failed to read values: %d\n", ret);
+               return ret < 0 ? ret : -EIO;
+       }
+
+       return 0;
+}
+
+/* sysfs attributes */
+static struct shtc1_data *shtc1_update_client(struct device *dev)
+{
+       struct shtc1_data *data = dev_get_drvdata(dev);
+       struct i2c_client *client = data->client;
+       unsigned char buf[SHTC1_RESPONSE_LENGTH];
+       int val;
+       int ret = 0;
+
+       mutex_lock(&data->update_lock);
+
+       if (time_after(jiffies, data->last_updated + HZ / 10) || !data->valid) {
+               ret = shtc1_update_values(client, data, buf, sizeof(buf));
+               if (ret)
+                       goto out;
+
+               /*
+                * From datasheet:
+                * T = -45 + 175 * ST / 2^16
+                * RH = 100 * SRH / 2^16
+                *
+                * Adapted for integer fixed point (3 digit) arithmetic.
+                */
+               val = be16_to_cpup((__be16 *)buf);
+               data->temperature = ((21875 * val) >> 13) - 45000;
+               val = be16_to_cpup((__be16 *)(buf + 3));
+               data->humidity = ((12500 * val) >> 13);
+
+               data->last_updated = jiffies;
+               data->valid = true;
+       }
+
+out:
+       mutex_unlock(&data->update_lock);
+
+       return ret == 0 ? data : ERR_PTR(ret);
+}
+
+static ssize_t temp1_input_show(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct shtc1_data *data = shtc1_update_client(dev);
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       return sprintf(buf, "%d\n", data->temperature);
+}
+
+static ssize_t humidity1_input_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct shtc1_data *data = shtc1_update_client(dev);
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       return sprintf(buf, "%d\n", data->humidity);
+}
+
+static DEVICE_ATTR_RO(temp1_input);
+static DEVICE_ATTR_RO(humidity1_input);
+
+static struct attribute *shtc1_attrs[] = {
+       &dev_attr_temp1_input.attr,
+       &dev_attr_humidity1_input.attr,
+       NULL
+};
+
+ATTRIBUTE_GROUPS(shtc1);
+
+static void shtc1_select_command(struct shtc1_data *data)
+{
+       if (data->setup.high_precision) {
+               data->command = data->setup.blocking_io ?
+                               shtc1_cmd_measure_blocking_hpm :
+                               shtc1_cmd_measure_nonblocking_hpm;
+               data->nonblocking_wait_time = SHTC1_NONBLOCKING_WAIT_TIME_HPM;
+
+       } else {
+               data->command = data->setup.blocking_io ?
+                               shtc1_cmd_measure_blocking_lpm :
+                               shtc1_cmd_measure_nonblocking_lpm;
+               data->nonblocking_wait_time = SHTC1_NONBLOCKING_WAIT_TIME_LPM;
+       }
+}
+
+static int shtc1_probe(struct i2c_client *client,
+                      const struct i2c_device_id *id)
+{
+       int ret;
+       char id_reg[2];
+       struct shtc1_data *data;
+       struct device *hwmon_dev;
+       struct i2c_adapter *adap = client->adapter;
+       struct device *dev = &client->dev;
+
+       if (!i2c_check_functionality(adap, I2C_FUNC_I2C)) {
+               dev_err(dev, "plain i2c transactions not supported\n");
+               return -ENODEV;
+       }
+
+       ret = i2c_master_send(client, shtc1_cmd_read_id_reg, SHTC1_CMD_LENGTH);
+       if (ret != SHTC1_CMD_LENGTH) {
+               dev_err(dev, "could not send read_id_reg command: %d\n", ret);
+               return ret < 0 ? ret : -ENODEV;
+       }
+       ret = i2c_master_recv(client, id_reg, sizeof(id_reg));
+       if (ret != sizeof(id_reg)) {
+               dev_err(dev, "could not read ID register: %d\n", ret);
+               return -ENODEV;
+       }
+       if ((id_reg[1] & SHTC1_ID_REG_MASK) != SHTC1_ID) {
+               dev_err(dev, "ID register doesn't match\n");
+               return -ENODEV;
+       }
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->setup.blocking_io = false;
+       data->setup.high_precision = true;
+       data->client = client;
+
+       if (client->dev.platform_data)
+               data->setup = *(struct shtc1_platform_data *)dev->platform_data;
+       shtc1_select_command(data);
+       mutex_init(&data->update_lock);
+
+       hwmon_dev = devm_hwmon_device_register_with_groups(dev,
+                                                          client->name,
+                                                          data,
+                                                          shtc1_groups);
+       if (IS_ERR(hwmon_dev))
+               dev_dbg(dev, "unable to register hwmon device\n");
+
+       return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+/* device ID table */
+static const struct i2c_device_id shtc1_id[] = {
+       { "shtc1", 0 },
+       { "shtw1", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, shtc1_id);
+
+static struct i2c_driver shtc1_i2c_driver = {
+       .driver.name  = "shtc1",
+       .probe        = shtc1_probe,
+       .id_table     = shtc1_id,
+};
+
+module_i2c_driver(shtc1_i2c_driver);
+
+MODULE_AUTHOR("Johannes Winkelmann <johannes.winkelmann@sensirion.com>");
+MODULE_DESCRIPTION("Sensirion SHTC1 humidity and temperature sensor driver");
+MODULE_LICENSE("GPL");
index 611f34c7333de3180f3bbc2d30f48f927c6b3c2c..c53619086f33ad89583d4fdb16bb805620ee4788 100644 (file)
 struct vexpress_hwmon_data {
        struct device *hwmon_dev;
        struct regmap *reg;
-       const char *name;
 };
 
-static ssize_t vexpress_hwmon_name_show(struct device *dev,
-               struct device_attribute *dev_attr, char *buffer)
-{
-       struct vexpress_hwmon_data *data = dev_get_drvdata(dev);
-
-       return sprintf(buffer, "%s\n", data->name);
-}
-
 static ssize_t vexpress_hwmon_label_show(struct device *dev,
                struct device_attribute *dev_attr, char *buffer)
 {
@@ -95,16 +86,6 @@ static umode_t vexpress_hwmon_attr_is_visible(struct kobject *kobj,
        return attr->mode;
 }
 
-static DEVICE_ATTR(name, S_IRUGO, vexpress_hwmon_name_show, NULL);
-
-#define VEXPRESS_HWMON_ATTRS(_name, _label_attr, _input_attr)  \
-struct attribute *vexpress_hwmon_attrs_##_name[] = {           \
-       &dev_attr_name.attr,                                    \
-       &dev_attr_##_label_attr.attr,                           \
-       &sensor_dev_attr_##_input_attr.dev_attr.attr,           \
-       NULL                                                    \
-}
-
 struct vexpress_hwmon_type {
        const char *name;
        const struct attribute_group **attr_groups;
@@ -114,7 +95,11 @@ struct vexpress_hwmon_type {
 static DEVICE_ATTR(in1_label, S_IRUGO, vexpress_hwmon_label_show, NULL);
 static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, vexpress_hwmon_u32_show,
                NULL, 1000);
-static VEXPRESS_HWMON_ATTRS(volt, in1_label, in1_input);
+static struct attribute *vexpress_hwmon_attrs_volt[] = {
+       &dev_attr_in1_label.attr,
+       &sensor_dev_attr_in1_input.dev_attr.attr,
+       NULL
+};
 static struct attribute_group vexpress_hwmon_group_volt = {
        .is_visible = vexpress_hwmon_attr_is_visible,
        .attrs = vexpress_hwmon_attrs_volt,
@@ -131,7 +116,11 @@ static struct vexpress_hwmon_type vexpress_hwmon_volt = {
 static DEVICE_ATTR(curr1_label, S_IRUGO, vexpress_hwmon_label_show, NULL);
 static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, vexpress_hwmon_u32_show,
                NULL, 1000);
-static VEXPRESS_HWMON_ATTRS(amp, curr1_label, curr1_input);
+static struct attribute *vexpress_hwmon_attrs_amp[] = {
+       &dev_attr_curr1_label.attr,
+       &sensor_dev_attr_curr1_input.dev_attr.attr,
+       NULL
+};
 static struct attribute_group vexpress_hwmon_group_amp = {
        .is_visible = vexpress_hwmon_attr_is_visible,
        .attrs = vexpress_hwmon_attrs_amp,
@@ -147,7 +136,11 @@ static struct vexpress_hwmon_type vexpress_hwmon_amp = {
 static DEVICE_ATTR(temp1_label, S_IRUGO, vexpress_hwmon_label_show, NULL);
 static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, vexpress_hwmon_u32_show,
                NULL, 1000);
-static VEXPRESS_HWMON_ATTRS(temp, temp1_label, temp1_input);
+static struct attribute *vexpress_hwmon_attrs_temp[] = {
+       &dev_attr_temp1_label.attr,
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       NULL
+};
 static struct attribute_group vexpress_hwmon_group_temp = {
        .is_visible = vexpress_hwmon_attr_is_visible,
        .attrs = vexpress_hwmon_attrs_temp,
@@ -163,7 +156,11 @@ static struct vexpress_hwmon_type vexpress_hwmon_temp = {
 static DEVICE_ATTR(power1_label, S_IRUGO, vexpress_hwmon_label_show, NULL);
 static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, vexpress_hwmon_u32_show,
                NULL, 1);
-static VEXPRESS_HWMON_ATTRS(power, power1_label, power1_input);
+static struct attribute *vexpress_hwmon_attrs_power[] = {
+       &dev_attr_power1_label.attr,
+       &sensor_dev_attr_power1_input.dev_attr.attr,
+       NULL
+};
 static struct attribute_group vexpress_hwmon_group_power = {
        .is_visible = vexpress_hwmon_attr_is_visible,
        .attrs = vexpress_hwmon_attrs_power,
@@ -179,7 +176,11 @@ static struct vexpress_hwmon_type vexpress_hwmon_power = {
 static DEVICE_ATTR(energy1_label, S_IRUGO, vexpress_hwmon_label_show, NULL);
 static SENSOR_DEVICE_ATTR(energy1_input, S_IRUGO, vexpress_hwmon_u64_show,
                NULL, 1);
-static VEXPRESS_HWMON_ATTRS(energy, energy1_label, energy1_input);
+static struct attribute *vexpress_hwmon_attrs_energy[] = {
+       &dev_attr_energy1_label.attr,
+       &sensor_dev_attr_energy1_input.dev_attr.attr,
+       NULL
+};
 static struct attribute_group vexpress_hwmon_group_energy = {
        .is_visible = vexpress_hwmon_attr_is_visible,
        .attrs = vexpress_hwmon_attrs_energy,
@@ -218,7 +219,6 @@ MODULE_DEVICE_TABLE(of, vexpress_hwmon_of_match);
 
 static int vexpress_hwmon_probe(struct platform_device *pdev)
 {
-       int err;
        const struct of_device_id *match;
        struct vexpress_hwmon_data *data;
        const struct vexpress_hwmon_type *type;
@@ -232,45 +232,19 @@ static int vexpress_hwmon_probe(struct platform_device *pdev)
        if (!match)
                return -ENODEV;
        type = match->data;
-       data->name = type->name;
 
        data->reg = devm_regmap_init_vexpress_config(&pdev->dev);
        if (IS_ERR(data->reg))
                return PTR_ERR(data->reg);
 
-       err = sysfs_create_groups(&pdev->dev.kobj, type->attr_groups);
-       if (err)
-               goto error;
-
-       data->hwmon_dev = hwmon_device_register(&pdev->dev);
-       if (IS_ERR(data->hwmon_dev)) {
-               err = PTR_ERR(data->hwmon_dev);
-               goto error;
-       }
-
-       return 0;
-
-error:
-       sysfs_remove_group(&pdev->dev.kobj, match->data);
-       return err;
-}
-
-static int vexpress_hwmon_remove(struct platform_device *pdev)
-{
-       struct vexpress_hwmon_data *data = platform_get_drvdata(pdev);
-       const struct of_device_id *match;
-
-       hwmon_device_unregister(data->hwmon_dev);
-
-       match = of_match_device(vexpress_hwmon_of_match, &pdev->dev);
-       sysfs_remove_group(&pdev->dev.kobj, match->data);
+       data->hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev,
+                       type->name, data, type->attr_groups);
 
-       return 0;
+       return PTR_ERR_OR_ZERO(data->hwmon_dev);
 }
 
 static struct platform_driver vexpress_hwmon_driver = {
        .probe = vexpress_hwmon_probe,
-       .remove = vexpress_hwmon_remove,
        .driver = {
                .name = DRVNAME,
                .owner = THIS_MODULE,
index 96d7131ab974bbb1baffcef9be2a4521890c82e9..5e153f6d4b48f2d36abcceec8dcee0996e5bf6d1 100644 (file)
@@ -234,12 +234,16 @@ static void release_tid(struct c4iw_rdev *rdev, u32 hwtid, struct sk_buff *skb)
 
 static void set_emss(struct c4iw_ep *ep, u16 opt)
 {
-       ep->emss = ep->com.dev->rdev.lldi.mtus[GET_TCPOPT_MSS(opt)] - 40;
+       ep->emss = ep->com.dev->rdev.lldi.mtus[GET_TCPOPT_MSS(opt)] -
+                  sizeof(struct iphdr) - sizeof(struct tcphdr);
        ep->mss = ep->emss;
        if (GET_TCPOPT_TSTAMP(opt))
                ep->emss -= 12;
        if (ep->emss < 128)
                ep->emss = 128;
+       if (ep->emss & 7)
+               PDBG("Warning: misaligned mtu idx %u mss %u emss=%u\n",
+                    GET_TCPOPT_MSS(opt), ep->mss, ep->emss);
        PDBG("%s mss_idx %u mss %u emss=%u\n", __func__, GET_TCPOPT_MSS(opt),
             ep->mss, ep->emss);
 }
@@ -473,7 +477,7 @@ static void send_flowc(struct c4iw_ep *ep, struct sk_buff *skb)
        flowc->mnemval[5].mnemonic = FW_FLOWC_MNEM_RCVNXT;
        flowc->mnemval[5].val = cpu_to_be32(ep->rcv_seq);
        flowc->mnemval[6].mnemonic = FW_FLOWC_MNEM_SNDBUF;
-       flowc->mnemval[6].val = cpu_to_be32(snd_win);
+       flowc->mnemval[6].val = cpu_to_be32(ep->snd_win);
        flowc->mnemval[7].mnemonic = FW_FLOWC_MNEM_MSS;
        flowc->mnemval[7].val = cpu_to_be32(ep->emss);
        /* Pad WR to 16 byte boundary */
@@ -565,6 +569,17 @@ static void c4iw_record_pm_msg(struct c4iw_ep *ep,
                sizeof(ep->com.mapped_remote_addr));
 }
 
+static void best_mtu(const unsigned short *mtus, unsigned short mtu,
+                    unsigned int *idx, int use_ts)
+{
+       unsigned short hdr_size = sizeof(struct iphdr) +
+                                 sizeof(struct tcphdr) +
+                                 (use_ts ? 12 : 0);
+       unsigned short data_size = mtu - hdr_size;
+
+       cxgb4_best_aligned_mtu(mtus, hdr_size, data_size, 8, idx);
+}
+
 static int send_connect(struct c4iw_ep *ep)
 {
        struct cpl_act_open_req *req;
@@ -591,6 +606,7 @@ static int send_connect(struct c4iw_ep *ep)
                                   &ep->com.mapped_local_addr;
        struct sockaddr_in6 *ra6 = (struct sockaddr_in6 *)
                                   &ep->com.mapped_remote_addr;
+       int win;
 
        wrlen = (ep->com.remote_addr.ss_family == AF_INET) ?
                        roundup(sizev4, 16) :
@@ -606,8 +622,18 @@ static int send_connect(struct c4iw_ep *ep)
        }
        set_wr_txq(skb, CPL_PRIORITY_SETUP, ep->ctrlq_idx);
 
-       cxgb4_best_mtu(ep->com.dev->rdev.lldi.mtus, ep->mtu, &mtu_idx);
+       best_mtu(ep->com.dev->rdev.lldi.mtus, ep->mtu, &mtu_idx,
+                enable_tcp_timestamps);
        wscale = compute_wscale(rcv_win);
+
+       /*
+        * Specify the largest window that will fit in opt0. The
+        * remainder will be specified in the rx_data_ack.
+        */
+       win = ep->rcv_win >> 10;
+       if (win > RCV_BUFSIZ_MASK)
+               win = RCV_BUFSIZ_MASK;
+
        opt0 = (nocong ? NO_CONG(1) : 0) |
               KEEP_ALIVE(1) |
               DELACK(1) |
@@ -618,7 +644,7 @@ static int send_connect(struct c4iw_ep *ep)
               SMAC_SEL(ep->smac_idx) |
               DSCP(ep->tos) |
               ULP_MODE(ULP_MODE_TCPDDP) |
-              RCV_BUFSIZ(rcv_win>>10);
+              RCV_BUFSIZ(win);
        opt2 = RX_CHANNEL(0) |
               CCTRL_ECN(enable_ecn) |
               RSS_QUEUE_VALID | RSS_QUEUE(ep->rss_qid);
@@ -674,6 +700,13 @@ static int send_connect(struct c4iw_ep *ep)
                        req6->opt2 = cpu_to_be32(opt2);
                }
        } else {
+               u32 isn = (prandom_u32() & ~7UL) - 1;
+
+               opt2 |= T5_OPT_2_VALID;
+               opt2 |= CONG_CNTRL_VALID; /* OPT_2_ISS for T5 */
+               if (peer2peer)
+                       isn += 4;
+
                if (ep->com.remote_addr.ss_family == AF_INET) {
                        t5_req = (struct cpl_t5_act_open_req *)
                                 skb_put(skb, wrlen);
@@ -690,6 +723,9 @@ static int send_connect(struct c4iw_ep *ep)
                                                     cxgb4_select_ntuple(
                                             ep->com.dev->rdev.lldi.ports[0],
                                             ep->l2t)));
+                       t5_req->rsvd = cpu_to_be32(isn);
+                       PDBG("%s snd_isn %u\n", __func__,
+                            be32_to_cpu(t5_req->rsvd));
                        t5_req->opt2 = cpu_to_be32(opt2);
                } else {
                        t5_req6 = (struct cpl_t5_act_open_req6 *)
@@ -713,6 +749,9 @@ static int send_connect(struct c4iw_ep *ep)
                                                        cxgb4_select_ntuple(
                                                ep->com.dev->rdev.lldi.ports[0],
                                                ep->l2t));
+                       t5_req6->rsvd = cpu_to_be32(isn);
+                       PDBG("%s snd_isn %u\n", __func__,
+                            be32_to_cpu(t5_req6->rsvd));
                        t5_req6->opt2 = cpu_to_be32(opt2);
                }
        }
@@ -1186,6 +1225,14 @@ static int update_rx_credits(struct c4iw_ep *ep, u32 credits)
                return 0;
        }
 
+       /*
+        * If we couldn't specify the entire rcv window at connection setup
+        * due to the limit in the number of bits in the RCV_BUFSIZ field,
+        * then add the overage in to the credits returned.
+        */
+       if (ep->rcv_win > RCV_BUFSIZ_MASK * 1024)
+               credits += ep->rcv_win - RCV_BUFSIZ_MASK * 1024;
+
        req = (struct cpl_rx_data_ack *) skb_put(skb, wrlen);
        memset(req, 0, wrlen);
        INIT_TP_WR(req, ep->hwtid);
@@ -1659,6 +1706,7 @@ static void send_fw_act_open_req(struct c4iw_ep *ep, unsigned int atid)
        unsigned int mtu_idx;
        int wscale;
        struct sockaddr_in *sin;
+       int win;
 
        skb = get_skb(NULL, sizeof(*req), GFP_KERNEL);
        req = (struct fw_ofld_connection_wr *)__skb_put(skb, sizeof(*req));
@@ -1681,8 +1729,18 @@ static void send_fw_act_open_req(struct c4iw_ep *ep, unsigned int atid)
                        htons(F_FW_OFLD_CONNECTION_WR_CPLRXDATAACK);
        req->tcb.tx_max = (__force __be32) jiffies;
        req->tcb.rcv_adv = htons(1);
-       cxgb4_best_mtu(ep->com.dev->rdev.lldi.mtus, ep->mtu, &mtu_idx);
+       best_mtu(ep->com.dev->rdev.lldi.mtus, ep->mtu, &mtu_idx,
+                enable_tcp_timestamps);
        wscale = compute_wscale(rcv_win);
+
+       /*
+        * Specify the largest window that will fit in opt0. The
+        * remainder will be specified in the rx_data_ack.
+        */
+       win = ep->rcv_win >> 10;
+       if (win > RCV_BUFSIZ_MASK)
+               win = RCV_BUFSIZ_MASK;
+
        req->tcb.opt0 = (__force __be64) (TCAM_BYPASS(1) |
                (nocong ? NO_CONG(1) : 0) |
                KEEP_ALIVE(1) |
@@ -1694,7 +1752,7 @@ static void send_fw_act_open_req(struct c4iw_ep *ep, unsigned int atid)
                SMAC_SEL(ep->smac_idx) |
                DSCP(ep->tos) |
                ULP_MODE(ULP_MODE_TCPDDP) |
-               RCV_BUFSIZ(rcv_win >> 10));
+               RCV_BUFSIZ(win));
        req->tcb.opt2 = (__force __be32) (PACE(1) |
                TX_QUEUE(ep->com.dev->rdev.lldi.tx_modq[ep->tx_chan]) |
                RX_CHANNEL(0) |
@@ -1731,6 +1789,13 @@ static int is_neg_adv(unsigned int status)
               status == CPL_ERR_KEEPALV_NEG_ADVICE;
 }
 
+static void set_tcp_window(struct c4iw_ep *ep, struct port_info *pi)
+{
+       ep->snd_win = snd_win;
+       ep->rcv_win = rcv_win;
+       PDBG("%s snd_win %d rcv_win %d\n", __func__, ep->snd_win, ep->rcv_win);
+}
+
 #define ACT_OPEN_RETRY_COUNT 2
 
 static int import_ep(struct c4iw_ep *ep, int iptype, __u8 *peer_ip,
@@ -1779,6 +1844,7 @@ static int import_ep(struct c4iw_ep *ep, int iptype, __u8 *peer_ip,
                ep->ctrlq_idx = cxgb4_port_idx(pdev);
                ep->rss_qid = cdev->rdev.lldi.rxq_ids[
                        cxgb4_port_idx(pdev) * step];
+               set_tcp_window(ep, (struct port_info *)netdev_priv(pdev));
                dev_put(pdev);
        } else {
                pdev = get_real_dev(n->dev);
@@ -1797,6 +1863,7 @@ static int import_ep(struct c4iw_ep *ep, int iptype, __u8 *peer_ip,
                        cdev->rdev.lldi.nchan;
                ep->rss_qid = cdev->rdev.lldi.rxq_ids[
                        cxgb4_port_idx(pdev) * step];
+               set_tcp_window(ep, (struct port_info *)netdev_priv(pdev));
 
                if (clear_mpa_v1) {
                        ep->retry_with_mpa_v1 = 0;
@@ -2027,13 +2094,36 @@ static void accept_cr(struct c4iw_ep *ep, struct sk_buff *skb,
        u64 opt0;
        u32 opt2;
        int wscale;
+       struct cpl_t5_pass_accept_rpl *rpl5 = NULL;
+       int win;
 
        PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
        BUG_ON(skb_cloned(skb));
-       skb_trim(skb, sizeof(*rpl));
+
        skb_get(skb);
-       cxgb4_best_mtu(ep->com.dev->rdev.lldi.mtus, ep->mtu, &mtu_idx);
+       rpl = cplhdr(skb);
+       if (is_t5(ep->com.dev->rdev.lldi.adapter_type)) {
+               skb_trim(skb, roundup(sizeof(*rpl5), 16));
+               rpl5 = (void *)rpl;
+               INIT_TP_WR(rpl5, ep->hwtid);
+       } else {
+               skb_trim(skb, sizeof(*rpl));
+               INIT_TP_WR(rpl, ep->hwtid);
+       }
+       OPCODE_TID(rpl) = cpu_to_be32(MK_OPCODE_TID(CPL_PASS_ACCEPT_RPL,
+                                                   ep->hwtid));
+
+       best_mtu(ep->com.dev->rdev.lldi.mtus, ep->mtu, &mtu_idx,
+                enable_tcp_timestamps && req->tcpopt.tstamp);
        wscale = compute_wscale(rcv_win);
+
+       /*
+        * Specify the largest window that will fit in opt0. The
+        * remainder will be specified in the rx_data_ack.
+        */
+       win = ep->rcv_win >> 10;
+       if (win > RCV_BUFSIZ_MASK)
+               win = RCV_BUFSIZ_MASK;
        opt0 = (nocong ? NO_CONG(1) : 0) |
               KEEP_ALIVE(1) |
               DELACK(1) |
@@ -2044,7 +2134,7 @@ static void accept_cr(struct c4iw_ep *ep, struct sk_buff *skb,
               SMAC_SEL(ep->smac_idx) |
               DSCP(ep->tos >> 2) |
               ULP_MODE(ULP_MODE_TCPDDP) |
-              RCV_BUFSIZ(rcv_win>>10);
+              RCV_BUFSIZ(win);
        opt2 = RX_CHANNEL(0) |
               RSS_QUEUE_VALID | RSS_QUEUE(ep->rss_qid);
 
@@ -2064,14 +2154,18 @@ static void accept_cr(struct c4iw_ep *ep, struct sk_buff *skb,
                        opt2 |= CCTRL_ECN(1);
        }
        if (is_t5(ep->com.dev->rdev.lldi.adapter_type)) {
+               u32 isn = (prandom_u32() & ~7UL) - 1;
                opt2 |= T5_OPT_2_VALID;
                opt2 |= V_CONG_CNTRL(CONG_ALG_TAHOE);
+               opt2 |= CONG_CNTRL_VALID; /* OPT_2_ISS for T5 */
+               rpl5 = (void *)rpl;
+               memset(&rpl5->iss, 0, roundup(sizeof(*rpl5)-sizeof(*rpl), 16));
+               if (peer2peer)
+                       isn += 4;
+               rpl5->iss = cpu_to_be32(isn);
+               PDBG("%s iss %u\n", __func__, be32_to_cpu(rpl5->iss));
        }
 
-       rpl = cplhdr(skb);
-       INIT_TP_WR(rpl, ep->hwtid);
-       OPCODE_TID(rpl) = cpu_to_be32(MK_OPCODE_TID(CPL_PASS_ACCEPT_RPL,
-                                     ep->hwtid));
        rpl->opt0 = cpu_to_be64(opt0);
        rpl->opt2 = cpu_to_be32(opt2);
        set_wr_txq(skb, CPL_PRIORITY_SETUP, ep->ctrlq_idx);
@@ -2136,6 +2230,7 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb)
        int err;
        u16 peer_mss = ntohs(req->tcpopt.mss);
        int iptype;
+       unsigned short hdrs;
 
        parent_ep = lookup_stid(t, stid);
        if (!parent_ep) {
@@ -2193,8 +2288,10 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb)
                goto reject;
        }
 
-       if (peer_mss && child_ep->mtu > (peer_mss + 40))
-               child_ep->mtu = peer_mss + 40;
+       hdrs = sizeof(struct iphdr) + sizeof(struct tcphdr) +
+              ((enable_tcp_timestamps && req->tcpopt.tstamp) ? 12 : 0);
+       if (peer_mss && child_ep->mtu > (peer_mss + hdrs))
+               child_ep->mtu = peer_mss + hdrs;
 
        state_set(&child_ep->com, CONNECTING);
        child_ep->com.dev = dev;
index 7151a02b4ebb3e3f48de3c270046684065d0e0d5..c04292c950f1750ac5fa2fc35b1ea4fda6a76079 100644 (file)
@@ -134,7 +134,8 @@ static int create_cq(struct c4iw_rdev *rdev, struct t4_cq *cq,
                        V_FW_RI_RES_WR_IQANUS(0) |
                        V_FW_RI_RES_WR_IQANUD(1) |
                        F_FW_RI_RES_WR_IQANDST |
-                       V_FW_RI_RES_WR_IQANDSTINDEX(*rdev->lldi.rxq_ids));
+                       V_FW_RI_RES_WR_IQANDSTINDEX(
+                               rdev->lldi.ciq_ids[cq->vector]));
        res->u.cq.iqdroprss_to_iqesize = cpu_to_be16(
                        F_FW_RI_RES_WR_IQDROPRSS |
                        V_FW_RI_RES_WR_IQPCIECH(2) |
@@ -870,6 +871,9 @@ struct ib_cq *c4iw_create_cq(struct ib_device *ibdev, int entries,
 
        rhp = to_c4iw_dev(ibdev);
 
+       if (vector >= rhp->rdev.lldi.nciq)
+               return ERR_PTR(-EINVAL);
+
        chp = kzalloc(sizeof(*chp), GFP_KERNEL);
        if (!chp)
                return ERR_PTR(-ENOMEM);
@@ -915,6 +919,7 @@ struct ib_cq *c4iw_create_cq(struct ib_device *ibdev, int entries,
        }
        chp->cq.size = hwentries;
        chp->cq.memsize = memsize;
+       chp->cq.vector = vector;
 
        ret = create_cq(&rhp->rdev, &chp->cq,
                        ucontext ? &ucontext->uctx : &rhp->rdev.uctx);
index 6f533fbcc4b3d89fe1f4c9da2ae3fd6220720819..125bc5d1e175ba4b18085fa0323dd304689b504b 100644 (file)
@@ -810,6 +810,8 @@ struct c4iw_ep {
        u8 retry_with_mpa_v1;
        u8 tried_with_mpa_v1;
        unsigned int retry_count;
+       int snd_win;
+       int rcv_win;
 };
 
 static inline void print_addr(struct c4iw_ep_common *epc, const char *func,
index c777e22bd8d538800fb8c616c8f20f987b21d7d7..b1d305338de6a06477d41ce11eb6f9519b3007c9 100644 (file)
@@ -500,7 +500,7 @@ int c4iw_register_device(struct c4iw_dev *dev)
        dev->ibdev.node_type = RDMA_NODE_RNIC;
        memcpy(dev->ibdev.node_desc, C4IW_NODE_DESC, sizeof(C4IW_NODE_DESC));
        dev->ibdev.phys_port_cnt = dev->rdev.lldi.nports;
-       dev->ibdev.num_comp_vectors = 1;
+       dev->ibdev.num_comp_vectors =  dev->rdev.lldi.nciq;
        dev->ibdev.dma_device = &(dev->rdev.lldi.pdev->dev);
        dev->ibdev.query_device = c4iw_query_device;
        dev->ibdev.query_port = c4iw_query_port;
index 2178f31984104542c3cb0cb0439e0865e0262774..68b0a6bf4eb00e23f8b737828ff85e465cc24173 100644 (file)
@@ -542,6 +542,7 @@ struct t4_cq {
        size_t memsize;
        __be64 bits_type_ts;
        u32 cqid;
+       int vector;
        u16 size; /* including status page */
        u16 cidx;
        u16 sw_pidx;
index 6121ca08fe588bff67aab81fe7df06119287292b..91289a051af928c7ffb04bfb7ac2ee8c342be7fe 100644 (file)
@@ -848,6 +848,7 @@ enum {                     /* TCP congestion control algorithms */
 #define V_CONG_CNTRL(x) ((x) << S_CONG_CNTRL)
 #define G_CONG_CNTRL(x) (((x) >> S_CONG_CNTRL) & M_CONG_CNTRL)
 
+#define CONG_CNTRL_VALID   (1 << 18)
 #define T5_OPT_2_VALID       (1 << 31)
 
 #endif /* _T4FW_RI_API_H_ */
index c4b3940845e60570fcbc5e5d0fa5ef807551d45e..078cadd6c797afeb0e22267fb76e5362a4b97326 100644 (file)
@@ -105,5 +105,5 @@ static const struct ethtool_ops ipoib_ethtool_ops = {
 
 void ipoib_set_ethtool_ops(struct net_device *dev)
 {
-       SET_ETHTOOL_OPS(dev, &ipoib_ethtool_ops);
+       dev->ethtool_ops = &ipoib_ethtool_ops;
 }
index 2e2d903db838f75e6105a88f875de8acf3667162..8d44a4060634c084971f6755aa246ff384c9a40a 100644 (file)
 #include "iscsi_iser.h"
 
 /* Register user buffer memory and initialize passive rdma
- *  dto descriptor. Total data size is stored in
- *  iser_task->data[ISER_DIR_IN].data_len
+ *  dto descriptor. Data size is stored in
+ *  task->data[ISER_DIR_IN].data_len, Protection size
+ *  os stored in task->prot[ISER_DIR_IN].data_len
  */
-static int iser_prepare_read_cmd(struct iscsi_task *task,
-                                unsigned int edtl)
+static int iser_prepare_read_cmd(struct iscsi_task *task)
 
 {
        struct iscsi_iser_task *iser_task = task->dd_data;
@@ -73,14 +73,6 @@ static int iser_prepare_read_cmd(struct iscsi_task *task,
                        return err;
        }
 
-       if (edtl > iser_task->data[ISER_DIR_IN].data_len) {
-               iser_err("Total data length: %ld, less than EDTL: "
-                        "%d, in READ cmd BHS itt: %d, conn: 0x%p\n",
-                        iser_task->data[ISER_DIR_IN].data_len, edtl,
-                        task->itt, iser_task->ib_conn);
-               return -EINVAL;
-       }
-
        err = device->iser_reg_rdma_mem(iser_task, ISER_DIR_IN);
        if (err) {
                iser_err("Failed to set up Data-IN RDMA\n");
@@ -100,8 +92,9 @@ static int iser_prepare_read_cmd(struct iscsi_task *task,
 }
 
 /* Register user buffer memory and initialize passive rdma
- *  dto descriptor. Total data size is stored in
- *  task->data[ISER_DIR_OUT].data_len
+ *  dto descriptor. Data size is stored in
+ *  task->data[ISER_DIR_OUT].data_len, Protection size
+ *  is stored at task->prot[ISER_DIR_OUT].data_len
  */
 static int
 iser_prepare_write_cmd(struct iscsi_task *task,
@@ -135,14 +128,6 @@ iser_prepare_write_cmd(struct iscsi_task *task,
                        return err;
        }
 
-       if (edtl > iser_task->data[ISER_DIR_OUT].data_len) {
-               iser_err("Total data length: %ld, less than EDTL: %d, "
-                        "in WRITE cmd BHS itt: %d, conn: 0x%p\n",
-                        iser_task->data[ISER_DIR_OUT].data_len,
-                        edtl, task->itt, task->conn);
-               return -EINVAL;
-       }
-
        err = device->iser_reg_rdma_mem(iser_task, ISER_DIR_OUT);
        if (err != 0) {
                iser_err("Failed to register write cmd RDMA mem\n");
@@ -417,11 +402,12 @@ int iser_send_command(struct iscsi_conn *conn,
        if (scsi_prot_sg_count(sc)) {
                prot_buf->buf  = scsi_prot_sglist(sc);
                prot_buf->size = scsi_prot_sg_count(sc);
-               prot_buf->data_len = sc->prot_sdb->length;
+               prot_buf->data_len = data_buf->data_len >>
+                                    ilog2(sc->device->sector_size) * 8;
        }
 
        if (hdr->flags & ISCSI_FLAG_CMD_READ) {
-               err = iser_prepare_read_cmd(task, edtl);
+               err = iser_prepare_read_cmd(task);
                if (err)
                        goto send_command_error;
        }
index b9d647468b99e66ed44c4a1176744ef5eefdbc82..d4c7928a0f36143844f3ca0b4735f9c22a1c148a 100644 (file)
@@ -663,8 +663,9 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
 
        pi_support = np->tpg_np->tpg->tpg_attrib.t10_pi;
        if (pi_support && !device->pi_capable) {
-               pr_err("Protection information requested but not supported\n");
-               ret = -EINVAL;
+               pr_err("Protection information requested but not supported, "
+                      "rejecting connect request\n");
+               ret = rdma_reject(cma_id, NULL, 0);
                goto out_mr;
        }
 
@@ -787,14 +788,12 @@ isert_disconnect_work(struct work_struct *work)
                isert_put_conn(isert_conn);
                return;
        }
-       if (!isert_conn->logout_posted) {
-               pr_debug("Calling rdma_disconnect for !logout_posted from"
-                        " isert_disconnect_work\n");
+
+       if (isert_conn->disconnect) {
+               /* Send DREQ/DREP towards our initiator */
                rdma_disconnect(isert_conn->conn_cm_id);
-               mutex_unlock(&isert_conn->conn_mutex);
-               iscsit_cause_connection_reinstatement(isert_conn->conn, 0);
-               goto wake_up;
        }
+
        mutex_unlock(&isert_conn->conn_mutex);
 
 wake_up:
@@ -803,10 +802,11 @@ wake_up:
 }
 
 static void
-isert_disconnected_handler(struct rdma_cm_id *cma_id)
+isert_disconnected_handler(struct rdma_cm_id *cma_id, bool disconnect)
 {
        struct isert_conn *isert_conn = (struct isert_conn *)cma_id->context;
 
+       isert_conn->disconnect = disconnect;
        INIT_WORK(&isert_conn->conn_logout_work, isert_disconnect_work);
        schedule_work(&isert_conn->conn_logout_work);
 }
@@ -815,29 +815,28 @@ static int
 isert_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
 {
        int ret = 0;
+       bool disconnect = false;
 
        pr_debug("isert_cma_handler: event %d status %d conn %p id %p\n",
                 event->event, event->status, cma_id->context, cma_id);
 
        switch (event->event) {
        case RDMA_CM_EVENT_CONNECT_REQUEST:
-               pr_debug("RDMA_CM_EVENT_CONNECT_REQUEST: >>>>>>>>>>>>>>>\n");
                ret = isert_connect_request(cma_id, event);
                break;
        case RDMA_CM_EVENT_ESTABLISHED:
-               pr_debug("RDMA_CM_EVENT_ESTABLISHED >>>>>>>>>>>>>>\n");
                isert_connected_handler(cma_id);
                break;
-       case RDMA_CM_EVENT_DISCONNECTED:
-               pr_debug("RDMA_CM_EVENT_DISCONNECTED: >>>>>>>>>>>>>>\n");
-               isert_disconnected_handler(cma_id);
-               break;
-       case RDMA_CM_EVENT_DEVICE_REMOVAL:
-       case RDMA_CM_EVENT_ADDR_CHANGE:
+       case RDMA_CM_EVENT_ADDR_CHANGE:    /* FALLTHRU */
+       case RDMA_CM_EVENT_DISCONNECTED:   /* FALLTHRU */
+       case RDMA_CM_EVENT_DEVICE_REMOVAL: /* FALLTHRU */
+               disconnect = true;
+       case RDMA_CM_EVENT_TIMEWAIT_EXIT:  /* FALLTHRU */
+               isert_disconnected_handler(cma_id, disconnect);
                break;
        case RDMA_CM_EVENT_CONNECT_ERROR:
        default:
-               pr_err("Unknown RDMA CMA event: %d\n", event->event);
+               pr_err("Unhandled RDMA CMA event: %d\n", event->event);
                break;
        }
 
@@ -1054,7 +1053,9 @@ isert_put_login_tx(struct iscsi_conn *conn, struct iscsi_login *login,
        }
        if (!login->login_failed) {
                if (login->login_complete) {
-                       if (isert_conn->conn_device->use_fastreg) {
+                       if (!conn->sess->sess_ops->SessionType &&
+                           isert_conn->conn_device->use_fastreg) {
+                               /* Normal Session and fastreg is used */
                                u8 pi_support = login->np->tpg_np->tpg->tpg_attrib.t10_pi;
 
                                ret = isert_conn_create_fastreg_pool(isert_conn,
@@ -1824,11 +1825,8 @@ isert_do_control_comp(struct work_struct *work)
                break;
        case ISTATE_SEND_LOGOUTRSP:
                pr_debug("Calling iscsit_logout_post_handler >>>>>>>>>>>>>>\n");
-               /*
-                * Call atomic_dec(&isert_conn->post_send_buf_count)
-                * from isert_wait_conn()
-                */
-               isert_conn->logout_posted = true;
+
+               atomic_dec(&isert_conn->post_send_buf_count);
                iscsit_logout_post_handler(cmd, cmd->conn);
                break;
        case ISTATE_SEND_TEXTRSP:
@@ -2034,6 +2032,8 @@ isert_cq_rx_comp_err(struct isert_conn *isert_conn)
        isert_conn->state = ISER_CONN_DOWN;
        mutex_unlock(&isert_conn->conn_mutex);
 
+       iscsit_cause_connection_reinstatement(isert_conn->conn, 0);
+
        complete(&isert_conn->conn_wait_comp_err);
 }
 
@@ -2320,7 +2320,7 @@ isert_put_text_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
        int rc;
 
        isert_create_send_desc(isert_conn, isert_cmd, &isert_cmd->tx_desc);
-       rc = iscsit_build_text_rsp(cmd, conn, hdr);
+       rc = iscsit_build_text_rsp(cmd, conn, hdr, ISCSI_INFINIBAND);
        if (rc < 0)
                return rc;
 
@@ -3156,9 +3156,14 @@ accept_wait:
                return -ENODEV;
 
        spin_lock_bh(&np->np_thread_lock);
-       if (np->np_thread_state == ISCSI_NP_THREAD_RESET) {
+       if (np->np_thread_state >= ISCSI_NP_THREAD_RESET) {
                spin_unlock_bh(&np->np_thread_lock);
-               pr_debug("ISCSI_NP_THREAD_RESET for isert_accept_np\n");
+               pr_debug("np_thread_state %d for isert_accept_np\n",
+                        np->np_thread_state);
+               /**
+                * No point in stalling here when np_thread
+                * is in state RESET/SHUTDOWN/EXIT - bail
+                **/
                return -ENODEV;
        }
        spin_unlock_bh(&np->np_thread_lock);
@@ -3208,15 +3213,9 @@ static void isert_wait_conn(struct iscsi_conn *conn)
        struct isert_conn *isert_conn = conn->context;
 
        pr_debug("isert_wait_conn: Starting \n");
-       /*
-        * Decrement post_send_buf_count for special case when called
-        * from isert_do_control_comp() -> iscsit_logout_post_handler()
-        */
-       mutex_lock(&isert_conn->conn_mutex);
-       if (isert_conn->logout_posted)
-               atomic_dec(&isert_conn->post_send_buf_count);
 
-       if (isert_conn->conn_cm_id && isert_conn->state != ISER_CONN_DOWN) {
+       mutex_lock(&isert_conn->conn_mutex);
+       if (isert_conn->conn_cm_id) {
                pr_debug("Calling rdma_disconnect from isert_wait_conn\n");
                rdma_disconnect(isert_conn->conn_cm_id);
        }
@@ -3293,6 +3292,7 @@ destroy_rx_wq:
 
 static void __exit isert_exit(void)
 {
+       flush_scheduled_work();
        destroy_workqueue(isert_comp_wq);
        destroy_workqueue(isert_rx_wq);
        iscsit_unregister_transport(&iser_target_transport);
index da6612e6800004b0984880d54ef57bcaab1b0be1..04f51f7bf614735b47d782b2ffa97d417e25fcd5 100644 (file)
@@ -116,7 +116,6 @@ struct isert_device;
 
 struct isert_conn {
        enum iser_conn_state    state;
-       bool                    logout_posted;
        int                     post_recv_buf_count;
        atomic_t                post_send_buf_count;
        u32                     responder_resources;
@@ -151,6 +150,7 @@ struct isert_conn {
 #define ISERT_COMP_BATCH_COUNT 8
        int                     conn_comp_batch;
        struct llist_head       conn_comp_llist;
+       bool                    disconnect;
 };
 
 #define ISERT_MAX_CQ 64
index 9816c51eb5c240be8c19b9c26bbd25d50fbca652..7641b3096ea67627a9a77b708380f4c681ed0ab9 100644 (file)
@@ -1,11 +1,3 @@
-config ISDN_DRV_AVMB1_VERBOSE_REASON
-       bool "Verbose reason code reporting"
-       default y
-       help
-         If you say Y here, the CAPI drivers will give verbose reasons for
-         disconnecting. This will increase the size of the kernel by 7 KB. If
-         unsure, say Y.
-
 config CAPI_TRACE
        bool "CAPI trace support"
        default y
@@ -17,7 +9,7 @@ config CAPI_TRACE
          If unsure, say Y.
 
 config ISDN_CAPI_CAPI20
-       tristate "CAPI2.0 /dev/capi support"
+       tristate "CAPI2.0 /dev/capi20 support"
        help
          This option will provide the CAPI 2.0 interface to userspace
          applications via /dev/capi20. Applications should use the
@@ -42,3 +34,11 @@ config ISDN_CAPI_CAPIDRV
          the legacy isdn4linux link layer.  If you have a card which is
          supported by a CAPI driver, but still want to use old features like
          ippp interfaces or ttyI emulation, say Y/M here.
+
+config ISDN_CAPI_CAPIDRV_VERBOSE
+       bool "Verbose reason code reporting"
+       depends on ISDN_CAPI_CAPIDRV
+       help
+         If you say Y here, the capidrv interface will give verbose reasons
+         for disconnecting. This will increase the size of the kernel by 7 KB.
+         If unsure, say N.
index ac6f72b455d174fa97c317bce1475946de7a83eb..f9a87ed2392b893da0feb125723d9ab53db99e0f 100644 (file)
@@ -1271,7 +1271,7 @@ static int __init capinc_tty_init(void)
                return -ENOMEM;
        }
        drv->driver_name = "capi_nc";
-       drv->name = "capi";
+       drv->name = "capi!";
        drv->major = 0;
        drv->minor_start = 0;
        drv->type = TTY_DRIVER_TYPE_SERIAL;
@@ -1417,7 +1417,7 @@ static int __init capi_init(void)
                return PTR_ERR(capi_class);
        }
 
-       device_create(capi_class, NULL, MKDEV(capi_major, 0), NULL, "capi");
+       device_create(capi_class, NULL, MKDEV(capi_major, 0), NULL, "capi20");
 
        if (capinc_tty_init() < 0) {
                device_destroy(capi_class, MKDEV(capi_major, 0));
index cc9f1927a322ec9d8aa1a5a05cad351711d9f861..fd6d28f3fc36e9223e759a1c3aaffa8b2de84641 100644 (file)
@@ -763,6 +763,201 @@ static inline int new_bchan(capidrv_contr *card)
 }
 
 /* ------------------------------------------------------------------- */
+static char *capi_info2str(u16 reason)
+{
+#ifndef CONFIG_ISDN_CAPI_CAPIDRV_VERBOSE
+       return "..";
+#else
+       switch (reason) {
+
+/*-- informative values (corresponding message was processed) -----*/
+       case 0x0001:
+               return "NCPI not supported by current protocol, NCPI ignored";
+       case 0x0002:
+               return "Flags not supported by current protocol, flags ignored";
+       case 0x0003:
+               return "Alert already sent by another application";
+
+/*-- error information concerning CAPI_REGISTER -----*/
+       case 0x1001:
+               return "Too many applications";
+       case 0x1002:
+               return "Logical block size too small, must be at least 128 Bytes";
+       case 0x1003:
+               return "Buffer exceeds 64 kByte";
+       case 0x1004:
+               return "Message buffer size too small, must be at least 1024 Bytes";
+       case 0x1005:
+               return "Max. number of logical connections not supported";
+       case 0x1006:
+               return "Reserved";
+       case 0x1007:
+               return "The message could not be accepted because of an internal busy condition";
+       case 0x1008:
+               return "OS resource error (no memory ?)";
+       case 0x1009:
+               return "CAPI not installed";
+       case 0x100A:
+               return "Controller does not support external equipment";
+       case 0x100B:
+               return "Controller does only support external equipment";
+
+/*-- error information concerning message exchange functions -----*/
+       case 0x1101:
+               return "Illegal application number";
+       case 0x1102:
+               return "Illegal command or subcommand or message length less than 12 bytes";
+       case 0x1103:
+               return "The message could not be accepted because of a queue full condition !! The error code does not imply that CAPI cannot receive messages directed to another controller, PLCI or NCCI";
+       case 0x1104:
+               return "Queue is empty";
+       case 0x1105:
+               return "Queue overflow, a message was lost !! This indicates a configuration error. The only recovery from this error is to perform a CAPI_RELEASE";
+       case 0x1106:
+               return "Unknown notification parameter";
+       case 0x1107:
+               return "The Message could not be accepted because of an internal busy condition";
+       case 0x1108:
+               return "OS Resource error (no memory ?)";
+       case 0x1109:
+               return "CAPI not installed";
+       case 0x110A:
+               return "Controller does not support external equipment";
+       case 0x110B:
+               return "Controller does only support external equipment";
+
+/*-- error information concerning resource / coding problems -----*/
+       case 0x2001:
+               return "Message not supported in current state";
+       case 0x2002:
+               return "Illegal Controller / PLCI / NCCI";
+       case 0x2003:
+               return "Out of PLCI";
+       case 0x2004:
+               return "Out of NCCI";
+       case 0x2005:
+               return "Out of LISTEN";
+       case 0x2006:
+               return "Out of FAX resources (protocol T.30)";
+       case 0x2007:
+               return "Illegal message parameter coding";
+
+/*-- error information concerning requested services  -----*/
+       case 0x3001:
+               return "B1 protocol not supported";
+       case 0x3002:
+               return "B2 protocol not supported";
+       case 0x3003:
+               return "B3 protocol not supported";
+       case 0x3004:
+               return "B1 protocol parameter not supported";
+       case 0x3005:
+               return "B2 protocol parameter not supported";
+       case 0x3006:
+               return "B3 protocol parameter not supported";
+       case 0x3007:
+               return "B protocol combination not supported";
+       case 0x3008:
+               return "NCPI not supported";
+       case 0x3009:
+               return "CIP Value unknown";
+       case 0x300A:
+               return "Flags not supported (reserved bits)";
+       case 0x300B:
+               return "Facility not supported";
+       case 0x300C:
+               return "Data length not supported by current protocol";
+       case 0x300D:
+               return "Reset procedure not supported by current protocol";
+
+/*-- informations about the clearing of a physical connection -----*/
+       case 0x3301:
+               return "Protocol error layer 1 (broken line or B-channel removed by signalling protocol)";
+       case 0x3302:
+               return "Protocol error layer 2";
+       case 0x3303:
+               return "Protocol error layer 3";
+       case 0x3304:
+               return "Another application got that call";
+/*-- T.30 specific reasons -----*/
+       case 0x3311:
+               return "Connecting not successful (remote station is no FAX G3 machine)";
+       case 0x3312:
+               return "Connecting not successful (training error)";
+       case 0x3313:
+               return "Disconnected before transfer (remote station does not support transfer mode, e.g. resolution)";
+       case 0x3314:
+               return "Disconnected during transfer (remote abort)";
+       case 0x3315:
+               return "Disconnected during transfer (remote procedure error, e.g. unsuccessful repetition of T.30 commands)";
+       case 0x3316:
+               return "Disconnected during transfer (local tx data underrun)";
+       case 0x3317:
+               return "Disconnected during transfer (local rx data overflow)";
+       case 0x3318:
+               return "Disconnected during transfer (local abort)";
+       case 0x3319:
+               return "Illegal parameter coding (e.g. SFF coding error)";
+
+/*-- disconnect causes from the network according to ETS 300 102-1/Q.931 -----*/
+       case 0x3481: return "Unallocated (unassigned) number";
+       case 0x3482: return "No route to specified transit network";
+       case 0x3483: return "No route to destination";
+       case 0x3486: return "Channel unacceptable";
+       case 0x3487:
+               return "Call awarded and being delivered in an established channel";
+       case 0x3490: return "Normal call clearing";
+       case 0x3491: return "User busy";
+       case 0x3492: return "No user responding";
+       case 0x3493: return "No answer from user (user alerted)";
+       case 0x3495: return "Call rejected";
+       case 0x3496: return "Number changed";
+       case 0x349A: return "Non-selected user clearing";
+       case 0x349B: return "Destination out of order";
+       case 0x349C: return "Invalid number format";
+       case 0x349D: return "Facility rejected";
+       case 0x349E: return "Response to STATUS ENQUIRY";
+       case 0x349F: return "Normal, unspecified";
+       case 0x34A2: return "No circuit / channel available";
+       case 0x34A6: return "Network out of order";
+       case 0x34A9: return "Temporary failure";
+       case 0x34AA: return "Switching equipment congestion";
+       case 0x34AB: return "Access information discarded";
+       case 0x34AC: return "Requested circuit / channel not available";
+       case 0x34AF: return "Resources unavailable, unspecified";
+       case 0x34B1: return "Quality of service unavailable";
+       case 0x34B2: return "Requested facility not subscribed";
+       case 0x34B9: return "Bearer capability not authorized";
+       case 0x34BA: return "Bearer capability not presently available";
+       case 0x34BF: return "Service or option not available, unspecified";
+       case 0x34C1: return "Bearer capability not implemented";
+       case 0x34C2: return "Channel type not implemented";
+       case 0x34C5: return "Requested facility not implemented";
+       case 0x34C6: return "Only restricted digital information bearer capability is available";
+       case 0x34CF: return "Service or option not implemented, unspecified";
+       case 0x34D1: return "Invalid call reference value";
+       case 0x34D2: return "Identified channel does not exist";
+       case 0x34D3: return "A suspended call exists, but this call identity does not";
+       case 0x34D4: return "Call identity in use";
+       case 0x34D5: return "No call suspended";
+       case 0x34D6: return "Call having the requested call identity has been cleared";
+       case 0x34D8: return "Incompatible destination";
+       case 0x34DB: return "Invalid transit network selection";
+       case 0x34DF: return "Invalid message, unspecified";
+       case 0x34E0: return "Mandatory information element is missing";
+       case 0x34E1: return "Message type non-existent or not implemented";
+       case 0x34E2: return "Message not compatible with call state or message type non-existent or not implemented";
+       case 0x34E3: return "Information element non-existent or not implemented";
+       case 0x34E4: return "Invalid information element contents";
+       case 0x34E5: return "Message not compatible with call state";
+       case 0x34E6: return "Recovery on timer expiry";
+       case 0x34EF: return "Protocol error, unspecified";
+       case 0x34FF: return "Interworking, unspecified";
+
+       default: return "No additional information";
+       }
+#endif
+}
 
 static void handle_controller(_cmsg *cmsg)
 {
index d26f17033b6863af4550498df3812dca718326c1..6e797e502cfaf0fc2c2459de6e3d80885c145117 100644 (file)
 
 /* from CAPI2.0 DDK AVM Berlin GmbH */
 
-#ifndef CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
-char *capi_info2str(u16 reason)
-{
-       return "..";
-}
-#else
-char *capi_info2str(u16 reason)
-{
-       switch (reason) {
-
-/*-- informative values (corresponding message was processed) -----*/
-       case 0x0001:
-               return "NCPI not supported by current protocol, NCPI ignored";
-       case 0x0002:
-               return "Flags not supported by current protocol, flags ignored";
-       case 0x0003:
-               return "Alert already sent by another application";
-
-/*-- error information concerning CAPI_REGISTER -----*/
-       case 0x1001:
-               return "Too many applications";
-       case 0x1002:
-               return "Logical block size too small, must be at least 128 Bytes";
-       case 0x1003:
-               return "Buffer exceeds 64 kByte";
-       case 0x1004:
-               return "Message buffer size too small, must be at least 1024 Bytes";
-       case 0x1005:
-               return "Max. number of logical connections not supported";
-       case 0x1006:
-               return "Reserved";
-       case 0x1007:
-               return "The message could not be accepted because of an internal busy condition";
-       case 0x1008:
-               return "OS resource error (no memory ?)";
-       case 0x1009:
-               return "CAPI not installed";
-       case 0x100A:
-               return "Controller does not support external equipment";
-       case 0x100B:
-               return "Controller does only support external equipment";
-
-/*-- error information concerning message exchange functions -----*/
-       case 0x1101:
-               return "Illegal application number";
-       case 0x1102:
-               return "Illegal command or subcommand or message length less than 12 bytes";
-       case 0x1103:
-               return "The message could not be accepted because of a queue full condition !! The error code does not imply that CAPI cannot receive messages directed to another controller, PLCI or NCCI";
-       case 0x1104:
-               return "Queue is empty";
-       case 0x1105:
-               return "Queue overflow, a message was lost !! This indicates a configuration error. The only recovery from this error is to perform a CAPI_RELEASE";
-       case 0x1106:
-               return "Unknown notification parameter";
-       case 0x1107:
-               return "The Message could not be accepted because of an internal busy condition";
-       case 0x1108:
-               return "OS Resource error (no memory ?)";
-       case 0x1109:
-               return "CAPI not installed";
-       case 0x110A:
-               return "Controller does not support external equipment";
-       case 0x110B:
-               return "Controller does only support external equipment";
-
-/*-- error information concerning resource / coding problems -----*/
-       case 0x2001:
-               return "Message not supported in current state";
-       case 0x2002:
-               return "Illegal Controller / PLCI / NCCI";
-       case 0x2003:
-               return "Out of PLCI";
-       case 0x2004:
-               return "Out of NCCI";
-       case 0x2005:
-               return "Out of LISTEN";
-       case 0x2006:
-               return "Out of FAX resources (protocol T.30)";
-       case 0x2007:
-               return "Illegal message parameter coding";
-
-/*-- error information concerning requested services  -----*/
-       case 0x3001:
-               return "B1 protocol not supported";
-       case 0x3002:
-               return "B2 protocol not supported";
-       case 0x3003:
-               return "B3 protocol not supported";
-       case 0x3004:
-               return "B1 protocol parameter not supported";
-       case 0x3005:
-               return "B2 protocol parameter not supported";
-       case 0x3006:
-               return "B3 protocol parameter not supported";
-       case 0x3007:
-               return "B protocol combination not supported";
-       case 0x3008:
-               return "NCPI not supported";
-       case 0x3009:
-               return "CIP Value unknown";
-       case 0x300A:
-               return "Flags not supported (reserved bits)";
-       case 0x300B:
-               return "Facility not supported";
-       case 0x300C:
-               return "Data length not supported by current protocol";
-       case 0x300D:
-               return "Reset procedure not supported by current protocol";
-
-/*-- informations about the clearing of a physical connection -----*/
-       case 0x3301:
-               return "Protocol error layer 1 (broken line or B-channel removed by signalling protocol)";
-       case 0x3302:
-               return "Protocol error layer 2";
-       case 0x3303:
-               return "Protocol error layer 3";
-       case 0x3304:
-               return "Another application got that call";
-/*-- T.30 specific reasons -----*/
-       case 0x3311:
-               return "Connecting not successful (remote station is no FAX G3 machine)";
-       case 0x3312:
-               return "Connecting not successful (training error)";
-       case 0x3313:
-               return "Disconnected before transfer (remote station does not support transfer mode, e.g. resolution)";
-       case 0x3314:
-               return "Disconnected during transfer (remote abort)";
-       case 0x3315:
-               return "Disconnected during transfer (remote procedure error, e.g. unsuccessful repetition of T.30 commands)";
-       case 0x3316:
-               return "Disconnected during transfer (local tx data underrun)";
-       case 0x3317:
-               return "Disconnected during transfer (local rx data overflow)";
-       case 0x3318:
-               return "Disconnected during transfer (local abort)";
-       case 0x3319:
-               return "Illegal parameter coding (e.g. SFF coding error)";
-
-/*-- disconnect causes from the network according to ETS 300 102-1/Q.931 -----*/
-       case 0x3481: return "Unallocated (unassigned) number";
-       case 0x3482: return "No route to specified transit network";
-       case 0x3483: return "No route to destination";
-       case 0x3486: return "Channel unacceptable";
-       case 0x3487:
-               return "Call awarded and being delivered in an established channel";
-       case 0x3490: return "Normal call clearing";
-       case 0x3491: return "User busy";
-       case 0x3492: return "No user responding";
-       case 0x3493: return "No answer from user (user alerted)";
-       case 0x3495: return "Call rejected";
-       case 0x3496: return "Number changed";
-       case 0x349A: return "Non-selected user clearing";
-       case 0x349B: return "Destination out of order";
-       case 0x349C: return "Invalid number format";
-       case 0x349D: return "Facility rejected";
-       case 0x349E: return "Response to STATUS ENQUIRY";
-       case 0x349F: return "Normal, unspecified";
-       case 0x34A2: return "No circuit / channel available";
-       case 0x34A6: return "Network out of order";
-       case 0x34A9: return "Temporary failure";
-       case 0x34AA: return "Switching equipment congestion";
-       case 0x34AB: return "Access information discarded";
-       case 0x34AC: return "Requested circuit / channel not available";
-       case 0x34AF: return "Resources unavailable, unspecified";
-       case 0x34B1: return "Quality of service unavailable";
-       case 0x34B2: return "Requested facility not subscribed";
-       case 0x34B9: return "Bearer capability not authorized";
-       case 0x34BA: return "Bearer capability not presently available";
-       case 0x34BF: return "Service or option not available, unspecified";
-       case 0x34C1: return "Bearer capability not implemented";
-       case 0x34C2: return "Channel type not implemented";
-       case 0x34C5: return "Requested facility not implemented";
-       case 0x34C6: return "Only restricted digital information bearer capability is available";
-       case 0x34CF: return "Service or option not implemented, unspecified";
-       case 0x34D1: return "Invalid call reference value";
-       case 0x34D2: return "Identified channel does not exist";
-       case 0x34D3: return "A suspended call exists, but this call identity does not";
-       case 0x34D4: return "Call identity in use";
-       case 0x34D5: return "No call suspended";
-       case 0x34D6: return "Call having the requested call identity has been cleared";
-       case 0x34D8: return "Incompatible destination";
-       case 0x34DB: return "Invalid transit network selection";
-       case 0x34DF: return "Invalid message, unspecified";
-       case 0x34E0: return "Mandatory information element is missing";
-       case 0x34E1: return "Message type non-existent or not implemented";
-       case 0x34E2: return "Message not compatible with call state or message type non-existent or not implemented";
-       case 0x34E3: return "Information element non-existent or not implemented";
-       case 0x34E4: return "Invalid information element contents";
-       case 0x34E5: return "Message not compatible with call state";
-       case 0x34E6: return "Recovery on timer expiry";
-       case 0x34EF: return "Protocol error, unspecified";
-       case 0x34FF: return "Interworking, unspecified";
-
-       default: return "No additional information";
-       }
-}
-#endif
-
 typedef struct {
        int typ;
        size_t off;
@@ -1073,4 +874,3 @@ EXPORT_SYMBOL(capi_cmsg_header);
 EXPORT_SYMBOL(capi_cmd2str);
 EXPORT_SYMBOL(capi_cmsg2str);
 EXPORT_SYMBOL(capi_message2str);
-EXPORT_SYMBOL(capi_info2str);
index 414dbf6da89afd5890c73785cbcb41f3ab903e54..fc9f9d03fa13b879ba382d079cb35a1108b19d0d 100644 (file)
@@ -197,25 +197,6 @@ typedef struct _hfc4s8s_hw {
 
 
 
-/***************************/
-/* inline function defines */
-/***************************/
-#ifdef HISAX_HFC4S8S_PCIMEM    /* inline functions memory mapped */
-
-/* memory write and dummy IO read to avoid PCI byte merge problems */
-#define Write_hfc8(a, b, c) {(*((volatile u_char *)(a->membase + b)) = c); inb(a->iobase + 4);}
-/* memory write without dummy IO access for fifo data access */
-#define fWrite_hfc8(a, b, c) (*((volatile u_char *)(a->membase + b)) = c)
-#define Read_hfc8(a, b) (*((volatile u_char *)(a->membase + b)))
-#define Write_hfc16(a, b, c) (*((volatile unsigned short *)(a->membase + b)) = c)
-#define Read_hfc16(a, b) (*((volatile unsigned short *)(a->membase + b)))
-#define Write_hfc32(a, b, c) (*((volatile unsigned long *)(a->membase + b)) = c)
-#define Read_hfc32(a, b) (*((volatile unsigned long *)(a->membase + b)))
-#define wait_busy(a) {while ((Read_hfc8(a, R_STATUS) & M_BUSY));}
-#define PCI_ENA_MEMIO  0x03
-
-#else
-
 /* inline functions io mapped */
 static inline void
 SetRegAddr(hfc4s8s_hw *a, u_char b)
@@ -306,8 +287,6 @@ wait_busy(hfc4s8s_hw *a)
 
 #define PCI_ENA_REGIO  0x01
 
-#endif                         /* HISAX_HFC4S8S_PCIMEM */
-
 /******************************************************/
 /* function to read critical counter registers that   */
 /* may be updated by the chip during read             */
@@ -724,26 +703,15 @@ rx_d_frame(struct hfc4s8s_l1 *l1p, int ech)
                                return;
                        } else {
                                /* read errornous D frame */
-
-#ifndef HISAX_HFC4S8S_PCIMEM
                                SetRegAddr(l1p->hw, A_FIFO_DATA0);
-#endif
 
                                while (z1 >= 4) {
-#ifdef HISAX_HFC4S8S_PCIMEM
-                                       Read_hfc32(l1p->hw, A_FIFO_DATA0);
-#else
                                        fRead_hfc32(l1p->hw);
-#endif
                                        z1 -= 4;
                                }
 
                                while (z1--)
-#ifdef HISAX_HFC4S8S_PCIMEM
-                                       Read_hfc8(l1p->hw, A_FIFO_DATA0);
-#else
-                               fRead_hfc8(l1p->hw);
-#endif
+                                       fRead_hfc8(l1p->hw);
 
                                Write_hfc8(l1p->hw, A_INC_RES_FIFO, 1);
                                wait_busy(l1p->hw);
@@ -753,27 +721,16 @@ rx_d_frame(struct hfc4s8s_l1 *l1p, int ech)
 
                cp = skb->data;
 
-#ifndef HISAX_HFC4S8S_PCIMEM
                SetRegAddr(l1p->hw, A_FIFO_DATA0);
-#endif
 
                while (z1 >= 4) {
-#ifdef HISAX_HFC4S8S_PCIMEM
-                       *((unsigned long *) cp) =
-                               Read_hfc32(l1p->hw, A_FIFO_DATA0);
-#else
                        *((unsigned long *) cp) = fRead_hfc32(l1p->hw);
-#endif
                        cp += 4;
                        z1 -= 4;
                }
 
                while (z1--)
-#ifdef HISAX_HFC4S8S_PCIMEM
-                       *cp++ = Read_hfc8(l1p->hw, A_FIFO_DATA0);
-#else
-               *cp++ = fRead_hfc8(l1p->hw);
-#endif
+                       *cp++ = fRead_hfc8(l1p->hw);
 
                Write_hfc8(l1p->hw, A_INC_RES_FIFO, 1); /* increment f counter */
                wait_busy(l1p->hw);
@@ -859,28 +816,17 @@ rx_b_frame(struct hfc4s8s_btype *bch)
                        wait_busy(l1->hw);
                        return;
                }
-#ifndef HISAX_HFC4S8S_PCIMEM
                SetRegAddr(l1->hw, A_FIFO_DATA0);
-#endif
 
                while (z1 >= 4) {
-#ifdef HISAX_HFC4S8S_PCIMEM
-                       *((unsigned long *) bch->rx_ptr) =
-                               Read_hfc32(l1->hw, A_FIFO_DATA0);
-#else
                        *((unsigned long *) bch->rx_ptr) =
                                fRead_hfc32(l1->hw);
-#endif
                        bch->rx_ptr += 4;
                        z1 -= 4;
                }
 
                while (z1--)
-#ifdef HISAX_HFC4S8S_PCIMEM
-                       *(bch->rx_ptr++) = Read_hfc8(l1->hw, A_FIFO_DATA0);
-#else
-               *(bch->rx_ptr++) = fRead_hfc8(l1->hw);
-#endif
+                       *(bch->rx_ptr++) = fRead_hfc8(l1->hw);
 
                if (hdlc_complete) {
                        /* increment f counter */
@@ -940,29 +886,17 @@ tx_d_frame(struct hfc4s8s_l1 *l1p)
        if ((skb = skb_dequeue(&l1p->d_tx_queue))) {
                cp = skb->data;
                cnt = skb->len;
-#ifndef HISAX_HFC4S8S_PCIMEM
                SetRegAddr(l1p->hw, A_FIFO_DATA0);
-#endif
 
                while (cnt >= 4) {
-#ifdef HISAX_HFC4S8S_PCIMEM
-                       fWrite_hfc32(l1p->hw, A_FIFO_DATA0,
-                                    *(unsigned long *) cp);
-#else
                        SetRegAddr(l1p->hw, A_FIFO_DATA0);
                        fWrite_hfc32(l1p->hw, *(unsigned long *) cp);
-#endif
                        cp += 4;
                        cnt -= 4;
                }
 
-#ifdef HISAX_HFC4S8S_PCIMEM
-               while (cnt--)
-                       fWrite_hfc8(l1p->hw, A_FIFO_DATA0, *cp++);
-#else
                while (cnt--)
                        fWrite_hfc8(l1p->hw, *cp++);
-#endif
 
                l1p->tx_cnt = skb->truesize;
                Write_hfc8(l1p->hw, A_INC_RES_FIFO, 1); /* increment f counter */
@@ -1037,26 +971,15 @@ tx_b_frame(struct hfc4s8s_btype *bch)
                cp = skb->data + bch->tx_cnt;
                bch->tx_cnt += cnt;
 
-#ifndef HISAX_HFC4S8S_PCIMEM
                SetRegAddr(l1->hw, A_FIFO_DATA0);
-#endif
                while (cnt >= 4) {
-#ifdef HISAX_HFC4S8S_PCIMEM
-                       fWrite_hfc32(l1->hw, A_FIFO_DATA0,
-                                    *(unsigned long *) cp);
-#else
                        fWrite_hfc32(l1->hw, *(unsigned long *) cp);
-#endif
                        cp += 4;
                        cnt -= 4;
                }
 
                while (cnt--)
-#ifdef HISAX_HFC4S8S_PCIMEM
-                       fWrite_hfc8(l1->hw, A_FIFO_DATA0, *cp++);
-#else
-               fWrite_hfc8(l1->hw, *cp++);
-#endif
+                       fWrite_hfc8(l1->hw, *cp++);
 
                if (bch->tx_cnt >= skb->len) {
                        if (bch->mode == L1_MODE_HDLC) {
@@ -1281,10 +1204,8 @@ hfc4s8s_interrupt(int intno, void *dev_id)
        if (!hw || !(hw->mr.r_irq_ctrl & M_GLOB_IRQ_EN))
                return IRQ_NONE;
 
-#ifndef        HISAX_HFC4S8S_PCIMEM
        /* read current selected regsister */
        old_ioreg = GetRegAddr(hw);
-#endif
 
        /* Layer 1 State change */
        hw->mr.r_irq_statech |=
@@ -1292,9 +1213,7 @@ hfc4s8s_interrupt(int intno, void *dev_id)
        if (!
            (b = (Read_hfc8(hw, R_STATUS) & (M_MISC_IRQSTA | M_FR_IRQSTA)))
            && !hw->mr.r_irq_statech) {
-#ifndef        HISAX_HFC4S8S_PCIMEM
                SetRegAddr(hw, old_ioreg);
-#endif
                return IRQ_NONE;
        }
 
@@ -1322,9 +1241,7 @@ hfc4s8s_interrupt(int intno, void *dev_id)
        /* queue the request to allow other cards to interrupt */
        schedule_work(&hw->tqueue);
 
-#ifndef        HISAX_HFC4S8S_PCIMEM
        SetRegAddr(hw, old_ioreg);
-#endif
        return IRQ_HANDLED;
 }                              /* hfc4s8s_interrupt */
 
@@ -1471,13 +1388,8 @@ static void
 release_pci_ports(hfc4s8s_hw *hw)
 {
        pci_write_config_word(hw->pdev, PCI_COMMAND, 0);
-#ifdef HISAX_HFC4S8S_PCIMEM
-       if (hw->membase)
-               iounmap((void *) hw->membase);
-#else
        if (hw->iobase)
                release_region(hw->iobase, 8);
-#endif
 }
 
 /*****************************************/
@@ -1486,11 +1398,7 @@ release_pci_ports(hfc4s8s_hw *hw)
 static void
 enable_pci_ports(hfc4s8s_hw *hw)
 {
-#ifdef HISAX_HFC4S8S_PCIMEM
-       pci_write_config_word(hw->pdev, PCI_COMMAND, PCI_ENA_MEMIO);
-#else
        pci_write_config_word(hw->pdev, PCI_COMMAND, PCI_ENA_REGIO);
-#endif
 }
 
 /*************************************/
@@ -1561,15 +1469,9 @@ setup_instance(hfc4s8s_hw *hw)
                       hw->irq);
                goto out;
        }
-#ifdef HISAX_HFC4S8S_PCIMEM
-       printk(KERN_INFO
-              "HFC-4S/8S: found PCI card at membase 0x%p, irq %d\n",
-              hw->hw_membase, hw->irq);
-#else
        printk(KERN_INFO
               "HFC-4S/8S: found PCI card at iobase 0x%x, irq %d\n",
               hw->iobase, hw->irq);
-#endif
 
        hfc_hardware_enable(hw, 1, 0);
 
@@ -1614,17 +1516,12 @@ hfc4s8s_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        hw->irq = pdev->irq;
        hw->iobase = pci_resource_start(pdev, 0);
 
-#ifdef HISAX_HFC4S8S_PCIMEM
-       hw->hw_membase = (u_char *) pci_resource_start(pdev, 1);
-       hw->membase = ioremap((ulong) hw->hw_membase, 256);
-#else
        if (!request_region(hw->iobase, 8, hw->card_name)) {
                printk(KERN_INFO
                       "HFC-4S/8S: failed to request address space at 0x%04x\n",
                       hw->iobase);
                goto out;
        }
-#endif
 
        pci_set_drvdata(pdev, hw);
        err = setup_instance(hw);
index a5da511e3c9ae4f381ae1526d2ab2779039aba9a..61ac6323744602ff27a95b56042c19d5d4e527e1 100644 (file)
@@ -634,7 +634,7 @@ isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg)
 #ifdef CONFIG_IPPP_FILTER
        case PPPIOCSPASS:
        {
-               struct sock_fprog fprog;
+               struct sock_fprog_kern fprog;
                struct sock_filter *code;
                int err, len = get_filter(argp, &code);
 
@@ -653,7 +653,7 @@ isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg)
        }
        case PPPIOCSACTIVE:
        {
-               struct sock_fprog fprog;
+               struct sock_fprog_kern fprog;
                struct sock_filter *code;
                int err, len = get_filter(argp, &code);
 
index 2c0d2c2bf94648e273b7614a65e1bc09976663e8..9f454d76cc060984317e31310b60e34080e0c6c5 100644 (file)
@@ -287,11 +287,9 @@ l1oip_socket_send(struct l1oip *hc, u8 localcodec, u8 channel, u32 chanmask,
        p = frame;
 
        /* restart timer */
-       if ((int)(hc->keep_tl.expires-jiffies) < 5 * HZ) {
-               del_timer(&hc->keep_tl);
-               hc->keep_tl.expires = jiffies + L1OIP_KEEPALIVE * HZ;
-               add_timer(&hc->keep_tl);
-       } else
+       if (time_before(hc->keep_tl.expires, jiffies + 5 * HZ))
+               mod_timer(&hc->keep_tl, jiffies + L1OIP_KEEPALIVE * HZ);
+       else
                hc->keep_tl.expires = jiffies + L1OIP_KEEPALIVE * HZ;
 
        if (debug & DEBUG_L1OIP_MSG)
@@ -621,11 +619,9 @@ multiframe:
                goto multiframe;
 
        /* restart timer */
-       if ((int)(hc->timeout_tl.expires-jiffies) < 5 * HZ || !hc->timeout_on) {
+       if (time_before(hc->timeout_tl.expires, jiffies + 5 * HZ) || !hc->timeout_on) {
                hc->timeout_on = 1;
-               del_timer(&hc->timeout_tl);
-               hc->timeout_tl.expires = jiffies + L1OIP_TIMEOUT * HZ;
-               add_timer(&hc->timeout_tl);
+               mod_timer(&hc->timeout_tl, jiffies + L1OIP_TIMEOUT * HZ);
        } else /* only adjust timer */
                hc->timeout_tl.expires = jiffies + L1OIP_TIMEOUT * HZ;
 
index 089841ca180f51f166921a358334f5ffe00796fb..a1b044e7eaad3be2c0ae9ff1225ea698839b4847 100644 (file)
@@ -300,16 +300,6 @@ config LEDS_PCA963X
          LED driver chip accessed via the I2C bus. Supported
          devices include PCA9633 and PCA9634
 
-config LEDS_PCA9685
-       tristate "LED support for PCA9685 I2C chip"
-       depends on LEDS_CLASS
-       depends on I2C
-       help
-         This option enables support for LEDs connected to the PCA9685
-         LED driver chip accessed via the I2C bus.
-         The PCA9685 offers 12-bit PWM (4095 levels of brightness) on
-         16 individual channels.
-
 config LEDS_WM831X_STATUS
        tristate "LED support for status LEDs on WM831x PMICs"
        depends on LEDS_CLASS
index 8b4c956e11bad78162fe20c254e4f8f6d58566bd..79c5155199a7d409489d13633349d460123daa9f 100644 (file)
@@ -36,7 +36,6 @@ obj-$(CONFIG_LEDS_OT200)              += leds-ot200.o
 obj-$(CONFIG_LEDS_FSG)                 += leds-fsg.o
 obj-$(CONFIG_LEDS_PCA955X)             += leds-pca955x.o
 obj-$(CONFIG_LEDS_PCA963X)             += leds-pca963x.o
-obj-$(CONFIG_LEDS_PCA9685)             += leds-pca9685.o
 obj-$(CONFIG_LEDS_DA903X)              += leds-da903x.o
 obj-$(CONFIG_LEDS_DA9052)              += leds-da9052.o
 obj-$(CONFIG_LEDS_WM831X_STATUS)       += leds-wm831x-status.o
index e5c57389efd63680af4b028910f8152667844692..c36acaf566a6e4d113e6c4a2314f1eaed60d3263 100644 (file)
 #include <linux/leds.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/dell-led.h>
 
 MODULE_AUTHOR("Louis Davis/Jim Dailey");
 MODULE_DESCRIPTION("Dell LED Control Driver");
 MODULE_LICENSE("GPL");
 
 #define DELL_LED_BIOS_GUID "F6E4FE6E-909D-47cb-8BAB-C9F6F2F8D396"
+#define DELL_APP_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
 MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID);
 
 /* Error Result Codes: */
@@ -39,6 +42,149 @@ MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID);
 #define CMD_LED_OFF    17
 #define CMD_LED_BLINK  18
 
+struct app_wmi_args {
+       u16 class;
+       u16 selector;
+       u32 arg1;
+       u32 arg2;
+       u32 arg3;
+       u32 arg4;
+       u32 res1;
+       u32 res2;
+       u32 res3;
+       u32 res4;
+       char dummy[92];
+};
+
+#define GLOBAL_MIC_MUTE_ENABLE 0x364
+#define GLOBAL_MIC_MUTE_DISABLE        0x365
+
+struct dell_bios_data_token {
+       u16 tokenid;
+       u16 location;
+       u16 value;
+};
+
+struct __attribute__ ((__packed__)) dell_bios_calling_interface {
+       struct  dmi_header header;
+       u16     cmd_io_addr;
+       u8      cmd_io_code;
+       u32     supported_cmds;
+       struct  dell_bios_data_token damap[];
+};
+
+static struct dell_bios_data_token dell_mic_tokens[2];
+
+static int dell_wmi_perform_query(struct app_wmi_args *args)
+{
+       struct app_wmi_args *bios_return;
+       union acpi_object *obj;
+       struct acpi_buffer input;
+       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+       acpi_status status;
+       u32 rc = -EINVAL;
+
+       input.length = 128;
+       input.pointer = args;
+
+       status = wmi_evaluate_method(DELL_APP_GUID, 0, 1, &input, &output);
+       if (!ACPI_SUCCESS(status))
+               goto err_out0;
+
+       obj = output.pointer;
+       if (!obj)
+               goto err_out0;
+
+       if (obj->type != ACPI_TYPE_BUFFER)
+               goto err_out1;
+
+       bios_return = (struct app_wmi_args *)obj->buffer.pointer;
+       rc = bios_return->res1;
+       if (rc)
+               goto err_out1;
+
+       memcpy(args, bios_return, sizeof(struct app_wmi_args));
+       rc = 0;
+
+ err_out1:
+       kfree(obj);
+ err_out0:
+       return rc;
+}
+
+static void __init find_micmute_tokens(const struct dmi_header *dm, void *dummy)
+{
+       struct dell_bios_calling_interface *calling_interface;
+       struct dell_bios_data_token *token;
+       int token_size = sizeof(struct dell_bios_data_token);
+       int i = 0;
+
+       if (dm->type == 0xda && dm->length > 17) {
+               calling_interface = container_of(dm,
+                               struct dell_bios_calling_interface, header);
+
+               token = &calling_interface->damap[i];
+               while (token->tokenid != 0xffff) {
+                       if (token->tokenid == GLOBAL_MIC_MUTE_DISABLE)
+                               memcpy(&dell_mic_tokens[0], token, token_size);
+                       else if (token->tokenid == GLOBAL_MIC_MUTE_ENABLE)
+                               memcpy(&dell_mic_tokens[1], token, token_size);
+
+                       i++;
+                       token = &calling_interface->damap[i];
+               }
+       }
+}
+
+static int dell_micmute_led_set(int state)
+{
+       struct app_wmi_args args;
+       struct dell_bios_data_token *token;
+
+       if (!wmi_has_guid(DELL_APP_GUID))
+               return -ENODEV;
+
+       if (state == 0 || state == 1)
+               token = &dell_mic_tokens[state];
+       else
+               return -EINVAL;
+
+       memset(&args, 0, sizeof(struct app_wmi_args));
+
+       args.class = 1;
+       args.arg1 = token->location;
+       args.arg2 = token->value;
+
+       dell_wmi_perform_query(&args);
+
+       return state;
+}
+
+int dell_app_wmi_led_set(int whichled, int on)
+{
+       int state = 0;
+
+       switch (whichled) {
+       case DELL_LED_MICMUTE:
+               state = dell_micmute_led_set(on);
+               break;
+       default:
+               pr_warn("led type %x is not supported\n", whichled);
+               break;
+       }
+
+       return state;
+}
+EXPORT_SYMBOL_GPL(dell_app_wmi_led_set);
+
+static int __init dell_micmute_led_init(void)
+{
+       memset(dell_mic_tokens, 0, sizeof(struct dell_bios_data_token) * 2);
+       dmi_walk(find_micmute_tokens, NULL);
+
+       return 0;
+}
+
 struct bios_args {
        unsigned char length;
        unsigned char result_code;
@@ -181,21 +327,32 @@ static int __init dell_led_init(void)
 {
        int error = 0;
 
-       if (!wmi_has_guid(DELL_LED_BIOS_GUID))
+       if (!wmi_has_guid(DELL_LED_BIOS_GUID) && !wmi_has_guid(DELL_APP_GUID))
                return -ENODEV;
 
-       error = led_off();
-       if (error != 0)
-               return -ENODEV;
+       if (wmi_has_guid(DELL_APP_GUID))
+               error = dell_micmute_led_init();
 
-       return led_classdev_register(NULL, &dell_led);
+       if (wmi_has_guid(DELL_LED_BIOS_GUID)) {
+               error = led_off();
+               if (error != 0)
+                       return -ENODEV;
+
+               error = led_classdev_register(NULL, &dell_led);
+       }
+
+       return error;
 }
 
 static void __exit dell_led_exit(void)
 {
-       led_classdev_unregister(&dell_led);
+       int error = 0;
 
-       led_off();
+       if (wmi_has_guid(DELL_LED_BIOS_GUID)) {
+               error = led_off();
+               if (error == 0)
+                       led_classdev_unregister(&dell_led);
+       }
 }
 
 module_init(dell_led_init);
index d1e1bca90d11e74599a54b89a358ebd0162c3934..c2def5551ce1bdd8c81caabbbee5dc0dbb39975d 100644 (file)
@@ -130,10 +130,9 @@ static int pm860x_led_dt_init(struct platform_device *pdev,
        struct device_node *nproot, *np;
        int iset = 0;
 
-       nproot = of_node_get(pdev->dev.parent->of_node);
-       if (!nproot)
+       if (!pdev->dev.parent->of_node)
                return -ENODEV;
-       nproot = of_find_node_by_name(nproot, "leds");
+       nproot = of_get_child_by_name(pdev->dev.parent->of_node, "leds");
        if (!nproot) {
                dev_err(&pdev->dev, "failed to find leds node\n");
                return -ENODEV;
index 86b5bdb0c77303665f7bc705e5bd43dca39e7df4..5036d7b4f82e812f60fce7322288339162077cb0 100644 (file)
@@ -120,13 +120,10 @@ static int adp5520_led_probe(struct platform_device *pdev)
 
        led = devm_kzalloc(&pdev->dev, sizeof(*led) * pdata->num_leds,
                                GFP_KERNEL);
-       if (led == NULL) {
-               dev_err(&pdev->dev, "failed to alloc memory\n");
+       if (!led)
                return -ENOMEM;
-       }
 
        ret = adp5520_led_prepare(pdev);
-
        if (ret) {
                dev_err(&pdev->dev, "failed to write\n");
                return ret;
index fb5a3472d61444c1cd8d491ee8a5ee571f39765c..6078c15d3452df77da066ec2f3d00bc805c02fa0 100644 (file)
@@ -678,10 +678,8 @@ static int bd2802_probe(struct i2c_client *client,
        int ret, i;
 
        led = devm_kzalloc(&client->dev, sizeof(struct bd2802_led), GFP_KERNEL);
-       if (!led) {
-               dev_err(&client->dev, "failed to allocate driver data\n");
+       if (!led)
                return -ENOMEM;
-       }
 
        led->client = client;
        pdata = led->pdata = dev_get_platdata(&client->dev);
index 35dffb100388c10e7568202c9fc73cd4c8738be5..54b8b5216b8b624210d63d05cd3ed5d12b22eb81 100644 (file)
@@ -108,10 +108,8 @@ static int da903x_led_probe(struct platform_device *pdev)
        }
 
        led = devm_kzalloc(&pdev->dev, sizeof(struct da903x_led), GFP_KERNEL);
-       if (led == NULL) {
-               dev_err(&pdev->dev, "failed to alloc memory for LED%d\n", id);
+       if (!led)
                return -ENOMEM;
-       }
 
        led->cdev.name = pdata->name;
        led->cdev.default_trigger = pdata->default_trigger;
index 01486adc7f8ba969502a3ee3e13004da439d6c01..e4da1f460ac58bddc8183314336e7031bb261623 100644 (file)
@@ -126,8 +126,7 @@ static int da9052_led_probe(struct platform_device *pdev)
        led = devm_kzalloc(&pdev->dev,
                           sizeof(struct da9052_led) * pled->num_leds,
                           GFP_KERNEL);
-       if (led == NULL) {
-               dev_err(&pdev->dev, "Failed to alloc memory\n");
+       if (!led) {
                error = -ENOMEM;
                goto err;
        }
index cb5ed82994baed92157bcebc27c27c6fb1d173a1..9e1716f8098ca2b95aa9e558b912968af8d1e2ed 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * lp5523.c - LP5523 LED Driver
+ * lp5523.c - LP5523, LP55231 LED Driver
  *
  * Copyright (C) 2010 Nokia Corporation
  * Copyright (C) 2012 Texas Instruments
@@ -814,6 +814,7 @@ MODULE_DEVICE_TABLE(i2c, lp5523_id);
 #ifdef CONFIG_OF
 static const struct of_device_id of_lp5523_leds_match[] = {
        { .compatible = "national,lp5523", },
+       { .compatible = "ti,lp55231", },
        {},
 };
 
diff --git a/drivers/leds/leds-pca9685.c b/drivers/leds/leds-pca9685.c
deleted file mode 100644 (file)
index 6e1ef3a..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright 2013 Maximilian Güntner <maximilian.guentner@gmail.com>
- *
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License.  See the file COPYING in the main
- * directory of this archive for more details.
- *
- * Based on leds-pca963x.c driver by
- * Peter Meerwald <p.meerwald@bct-electronic.com>
- *
- * Driver for the NXP PCA9685 12-Bit PWM LED driver chip.
- *
- */
-
-#include <linux/ctype.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/i2c.h>
-#include <linux/leds.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/workqueue.h>
-
-#include <linux/platform_data/leds-pca9685.h>
-
-/* Register Addresses */
-#define PCA9685_MODE1 0x00
-#define PCA9685_MODE2 0x01
-#define PCA9685_LED0_ON_L 0x06
-#define PCA9685_ALL_LED_ON_L 0xFA
-
-/* MODE1 Register */
-#define PCA9685_ALLCALL 0x00
-#define PCA9685_SLEEP   0x04
-#define PCA9685_AI      0x05
-
-/* MODE2 Register */
-#define PCA9685_INVRT   0x04
-#define PCA9685_OUTDRV  0x02
-
-static const struct i2c_device_id pca9685_id[] = {
-       { "pca9685", 0 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, pca9685_id);
-
-struct pca9685_led {
-       struct i2c_client *client;
-       struct work_struct work;
-       u16 brightness;
-       struct led_classdev led_cdev;
-       int led_num; /* 0-15 */
-       char name[32];
-};
-
-static void pca9685_write_msg(struct i2c_client *client, u8 *buf, u8 len)
-{
-       struct i2c_msg msg = {
-               .addr = client->addr,
-               .flags = 0x00,
-               .len = len,
-               .buf = buf
-       };
-       i2c_transfer(client->adapter, &msg, 1);
-}
-
-static void pca9685_all_off(struct i2c_client *client)
-{
-       u8 i2c_buffer[5] = {PCA9685_ALL_LED_ON_L, 0x00, 0x00, 0x00, 0x10};
-       pca9685_write_msg(client, i2c_buffer, 5);
-}
-
-static void pca9685_led_work(struct work_struct *work)
-{
-       struct pca9685_led *pca9685;
-       u8 i2c_buffer[5];
-
-       pca9685 = container_of(work, struct pca9685_led, work);
-       i2c_buffer[0] = PCA9685_LED0_ON_L + 4 * pca9685->led_num;
-       /*
-        * 4095 is the maximum brightness, so we set the ON time to 0x1000
-        * which disables the PWM generator for that LED
-        */
-       if (pca9685->brightness == 4095)
-               *((__le16 *)(i2c_buffer+1)) = cpu_to_le16(0x1000);
-       else
-               *((__le16 *)(i2c_buffer+1)) = 0x0000;
-
-       if (pca9685->brightness == 0)
-               *((__le16 *)(i2c_buffer+3)) = cpu_to_le16(0x1000);
-       else if (pca9685->brightness == 4095)
-               *((__le16 *)(i2c_buffer+3)) = 0x0000;
-       else
-               *((__le16 *)(i2c_buffer+3)) = cpu_to_le16(pca9685->brightness);
-
-       pca9685_write_msg(pca9685->client, i2c_buffer, 5);
-}
-
-static void pca9685_led_set(struct led_classdev *led_cdev,
-               enum led_brightness value)
-{
-       struct pca9685_led *pca9685;
-       pca9685 = container_of(led_cdev, struct pca9685_led, led_cdev);
-       pca9685->brightness = value;
-
-       schedule_work(&pca9685->work);
-}
-
-static int pca9685_probe(struct i2c_client *client,
-               const struct i2c_device_id *id)
-{
-       struct pca9685_led *pca9685;
-       struct pca9685_platform_data *pdata;
-       int err;
-       u8 i;
-
-       pdata = dev_get_platdata(&client->dev);
-       if (pdata) {
-               if (pdata->leds.num_leds < 1 || pdata->leds.num_leds > 15) {
-                       dev_err(&client->dev, "board info must claim 1-16 LEDs");
-                       return -EINVAL;
-               }
-       }
-
-       pca9685 = devm_kzalloc(&client->dev, 16 * sizeof(*pca9685), GFP_KERNEL);
-       if (!pca9685)
-               return -ENOMEM;
-
-       i2c_set_clientdata(client, pca9685);
-       pca9685_all_off(client);
-
-       for (i = 0; i < 16; i++) {
-               pca9685[i].client = client;
-               pca9685[i].led_num = i;
-               pca9685[i].name[0] = '\0';
-               if (pdata && i < pdata->leds.num_leds) {
-                       if (pdata->leds.leds[i].name)
-                               strncpy(pca9685[i].name,
-                                       pdata->leds.leds[i].name,
-                                       sizeof(pca9685[i].name)-1);
-                       if (pdata->leds.leds[i].default_trigger)
-                               pca9685[i].led_cdev.default_trigger =
-                                       pdata->leds.leds[i].default_trigger;
-               }
-               if (strlen(pca9685[i].name) == 0) {
-                       /*
-                        * Write adapter and address to the name as well.
-                        * Otherwise multiple chips attached to one host would
-                        * not work.
-                        */
-                       snprintf(pca9685[i].name, sizeof(pca9685[i].name),
-                                       "pca9685:%d:x%.2x:%d",
-                                       client->adapter->nr, client->addr, i);
-               }
-               pca9685[i].led_cdev.name = pca9685[i].name;
-               pca9685[i].led_cdev.max_brightness = 0xfff;
-               pca9685[i].led_cdev.brightness_set = pca9685_led_set;
-
-               INIT_WORK(&pca9685[i].work, pca9685_led_work);
-               err = led_classdev_register(&client->dev, &pca9685[i].led_cdev);
-               if (err < 0)
-                       goto exit;
-       }
-
-       if (pdata)
-               i2c_smbus_write_byte_data(client, PCA9685_MODE2,
-                       pdata->outdrv << PCA9685_OUTDRV |
-                       pdata->inverted << PCA9685_INVRT);
-       else
-               i2c_smbus_write_byte_data(client, PCA9685_MODE2,
-                       PCA9685_TOTEM_POLE << PCA9685_OUTDRV);
-       /* Enable Auto-Increment, enable oscillator, ALLCALL/SUBADDR disabled */
-       i2c_smbus_write_byte_data(client, PCA9685_MODE1, BIT(PCA9685_AI));
-
-       return 0;
-
-exit:
-       while (i--) {
-               led_classdev_unregister(&pca9685[i].led_cdev);
-               cancel_work_sync(&pca9685[i].work);
-       }
-       return err;
-}
-
-static int pca9685_remove(struct i2c_client *client)
-{
-       struct pca9685_led *pca9685 = i2c_get_clientdata(client);
-       u8 i;
-
-       for (i = 0; i < 16; i++) {
-               led_classdev_unregister(&pca9685[i].led_cdev);
-               cancel_work_sync(&pca9685[i].work);
-       }
-       pca9685_all_off(client);
-       return 0;
-}
-
-static struct i2c_driver pca9685_driver = {
-       .driver = {
-               .name = "leds-pca9685",
-               .owner = THIS_MODULE,
-       },
-       .probe = pca9685_probe,
-       .remove = pca9685_remove,
-       .id_table = pca9685_id,
-};
-
-module_i2c_driver(pca9685_driver);
-
-MODULE_AUTHOR("Maximilian Güntner <maximilian.guentner@gmail.com>");
-MODULE_DESCRIPTION("PCA9685 LED Driver");
-MODULE_LICENSE("GPL v2");
index aa770ec1e8928cc6b8f8eeaf9d6daea314e46d65..d672bb4480f6c96530be00ebca63b074a3400762 100644 (file)
@@ -69,6 +69,10 @@ static void led_pwm_set(struct led_classdev *led_cdev,
 
        duty *= brightness;
        do_div(duty, max);
+
+       if (led_dat->active_low)
+               duty = led_dat->period - duty;
+
        led_dat->duty = duty;
 
        if (led_dat->can_sleep)
@@ -92,55 +96,78 @@ static void led_pwm_cleanup(struct led_pwm_priv *priv)
        }
 }
 
-static int led_pwm_create_of(struct platform_device *pdev,
-                            struct led_pwm_priv *priv)
+static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
+                      struct led_pwm *led, struct device_node *child)
 {
-       struct device_node *child;
+       struct led_pwm_data *led_data = &priv->leds[priv->num_leds];
        int ret;
 
-       for_each_child_of_node(pdev->dev.of_node, child) {
-               struct led_pwm_data *led_dat = &priv->leds[priv->num_leds];
+       led_data->active_low = led->active_low;
+       led_data->cdev.name = led->name;
+       led_data->cdev.default_trigger = led->default_trigger;
+       led_data->cdev.brightness_set = led_pwm_set;
+       led_data->cdev.brightness = LED_OFF;
+       led_data->cdev.max_brightness = led->max_brightness;
+       led_data->cdev.flags = LED_CORE_SUSPENDRESUME;
 
-               led_dat->cdev.name = of_get_property(child, "label",
-                                                    NULL) ? : child->name;
+       if (child)
+               led_data->pwm = devm_of_pwm_get(dev, child, NULL);
+       else
+               led_data->pwm = devm_pwm_get(dev, led->name);
+       if (IS_ERR(led_data->pwm)) {
+               ret = PTR_ERR(led_data->pwm);
+               dev_err(dev, "unable to request PWM for %s: %d\n",
+                       led->name, ret);
+               return ret;
+       }
 
-               led_dat->pwm = devm_of_pwm_get(&pdev->dev, child, NULL);
-               if (IS_ERR(led_dat->pwm)) {
-                       dev_err(&pdev->dev, "unable to request PWM for %s\n",
-                               led_dat->cdev.name);
-                       ret = PTR_ERR(led_dat->pwm);
-                       goto err;
-               }
-               /* Get the period from PWM core when n*/
-               led_dat->period = pwm_get_period(led_dat->pwm);
+       if (child)
+               led_data->period = pwm_get_period(led_data->pwm);
 
-               led_dat->cdev.default_trigger = of_get_property(child,
-                                               "linux,default-trigger", NULL);
-               of_property_read_u32(child, "max-brightness",
-                                    &led_dat->cdev.max_brightness);
+       led_data->can_sleep = pwm_can_sleep(led_data->pwm);
+       if (led_data->can_sleep)
+               INIT_WORK(&led_data->work, led_pwm_work);
 
-               led_dat->cdev.brightness_set = led_pwm_set;
-               led_dat->cdev.brightness = LED_OFF;
-               led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
+       led_data->period = pwm_get_period(led_data->pwm);
+       if (!led_data->period && (led->pwm_period_ns > 0))
+               led_data->period = led->pwm_period_ns;
 
-               led_dat->can_sleep = pwm_can_sleep(led_dat->pwm);
-               if (led_dat->can_sleep)
-                       INIT_WORK(&led_dat->work, led_pwm_work);
+       ret = led_classdev_register(dev, &led_data->cdev);
+       if (ret == 0) {
+               priv->num_leds++;
+       } else {
+               dev_err(dev, "failed to register PWM led for %s: %d\n",
+                       led->name, ret);
+       }
+
+       return ret;
+}
 
-               ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
-               if (ret < 0) {
-                       dev_err(&pdev->dev, "failed to register for %s\n",
-                               led_dat->cdev.name);
+static int led_pwm_create_of(struct device *dev, struct led_pwm_priv *priv)
+{
+       struct device_node *child;
+       struct led_pwm led;
+       int ret = 0;
+
+       memset(&led, 0, sizeof(led));
+
+       for_each_child_of_node(dev->of_node, child) {
+               led.name = of_get_property(child, "label", NULL) ? :
+                          child->name;
+
+               led.default_trigger = of_get_property(child,
+                                               "linux,default-trigger", NULL);
+               led.active_low = of_property_read_bool(child, "active-low");
+               of_property_read_u32(child, "max-brightness",
+                                    &led.max_brightness);
+
+               ret = led_pwm_add(dev, priv, &led, child);
+               if (ret) {
                        of_node_put(child);
-                       goto err;
+                       break;
                }
-               priv->num_leds++;
        }
 
-       return 0;
-err:
-       led_pwm_cleanup(priv);
-
        return ret;
 }
 
@@ -166,54 +193,23 @@ static int led_pwm_probe(struct platform_device *pdev)
 
        if (pdata) {
                for (i = 0; i < count; i++) {
-                       struct led_pwm *cur_led = &pdata->leds[i];
-                       struct led_pwm_data *led_dat = &priv->leds[i];
-
-                       led_dat->pwm = devm_pwm_get(&pdev->dev, cur_led->name);
-                       if (IS_ERR(led_dat->pwm)) {
-                               ret = PTR_ERR(led_dat->pwm);
-                               dev_err(&pdev->dev,
-                                       "unable to request PWM for %s\n",
-                                       cur_led->name);
-                               goto err;
-                       }
-
-                       led_dat->cdev.name = cur_led->name;
-                       led_dat->cdev.default_trigger = cur_led->default_trigger;
-                       led_dat->active_low = cur_led->active_low;
-                       led_dat->cdev.brightness_set = led_pwm_set;
-                       led_dat->cdev.brightness = LED_OFF;
-                       led_dat->cdev.max_brightness = cur_led->max_brightness;
-                       led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
-
-                       led_dat->can_sleep = pwm_can_sleep(led_dat->pwm);
-                       if (led_dat->can_sleep)
-                               INIT_WORK(&led_dat->work, led_pwm_work);
-
-                       led_dat->period = pwm_get_period(led_dat->pwm);
-                       if (!led_dat->period && (cur_led->pwm_period_ns > 0))
-                               led_dat->period = cur_led->pwm_period_ns;
-
-                       ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
-                       if (ret < 0)
-                               goto err;
+                       ret = led_pwm_add(&pdev->dev, priv, &pdata->leds[i],
+                                         NULL);
+                       if (ret)
+                               break;
                }
-               priv->num_leds = count;
        } else {
-               ret = led_pwm_create_of(pdev, priv);
-               if (ret)
-                       return ret;
+               ret = led_pwm_create_of(&pdev->dev, priv);
+       }
+
+       if (ret) {
+               led_pwm_cleanup(priv);
+               return ret;
        }
 
        platform_set_drvdata(pdev, priv);
 
        return 0;
-
-err:
-       priv->num_leds = i;
-       led_pwm_cleanup(priv);
-
-       return ret;
 }
 
 static int led_pwm_remove(struct platform_device *pdev)
index 28988b7b4faba371970027eb6459105bb3ea712d..785eb53a87fc1158d5b07e78002dd5b4ec356c9e 100644 (file)
@@ -76,10 +76,8 @@ static int s3c24xx_led_probe(struct platform_device *dev)
 
        led = devm_kzalloc(&dev->dev, sizeof(struct s3c24xx_gpio_led),
                           GFP_KERNEL);
-       if (led == NULL) {
-               dev_err(&dev->dev, "No memory for device\n");
+       if (!led)
                return -ENOMEM;
-       }
 
        platform_set_drvdata(dev, led);
 
index 388632d23d447130ca3a8368cd48bae9a9b410eb..0b8cc4a021a684b249dc28b1539c1f515516c1b7 100644 (file)
@@ -135,10 +135,8 @@ static int sunfire_led_generic_probe(struct platform_device *pdev,
        }
 
        p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
-       if (!p) {
-               dev_err(&pdev->dev, "Could not allocate struct sunfire_drvdata\n");
+       if (!p)
                return -ENOMEM;
-       }
 
        for (i = 0; i < NUM_LEDS_PER_BOARD; i++) {
                struct led_classdev *lp = &p->leds[i].led_cdev;
index 1c3ee9fcaf34c5a1cb30e0efc1bf8c8109f7dd5b..aec0f02b6b3ef21c7d6ba005c03b47500966b13b 100644 (file)
@@ -47,7 +47,7 @@ static DEFINE_PER_CPU(struct led_trigger_cpu, cpu_trig);
  */
 void ledtrig_cpu(enum cpu_led_event ledevt)
 {
-       struct led_trigger_cpu *trig = &__get_cpu_var(cpu_trig);
+       struct led_trigger_cpu *trig = this_cpu_ptr(&cpu_trig);
 
        /* Locate the correct CPU LED */
        switch (ledevt) {
index 85f0b7074257b02aa53cb62f464a163ed6a56469..f752d12081ffe31a4a6b62b25654d66a54756ee4 100644 (file)
 
 /*----------------------------------------------------------------*/
 
-struct dm_bio_prison {
+struct bucket {
        spinlock_t lock;
+       struct hlist_head cells;
+};
+
+struct dm_bio_prison {
        mempool_t *cell_pool;
 
        unsigned nr_buckets;
        unsigned hash_mask;
-       struct hlist_head *cells;
+       struct bucket *buckets;
 };
 
 /*----------------------------------------------------------------*/
@@ -40,6 +44,12 @@ static uint32_t calc_nr_buckets(unsigned nr_cells)
 
 static struct kmem_cache *_cell_cache;
 
+static void init_bucket(struct bucket *b)
+{
+       spin_lock_init(&b->lock);
+       INIT_HLIST_HEAD(&b->cells);
+}
+
 /*
  * @nr_cells should be the number of cells you want in use _concurrently_.
  * Don't confuse it with the number of distinct keys.
@@ -49,13 +59,12 @@ struct dm_bio_prison *dm_bio_prison_create(unsigned nr_cells)
        unsigned i;
        uint32_t nr_buckets = calc_nr_buckets(nr_cells);
        size_t len = sizeof(struct dm_bio_prison) +
-               (sizeof(struct hlist_head) * nr_buckets);
+               (sizeof(struct bucket) * nr_buckets);
        struct dm_bio_prison *prison = kmalloc(len, GFP_KERNEL);
 
        if (!prison)
                return NULL;
 
-       spin_lock_init(&prison->lock);
        prison->cell_pool = mempool_create_slab_pool(nr_cells, _cell_cache);
        if (!prison->cell_pool) {
                kfree(prison);
@@ -64,9 +73,9 @@ struct dm_bio_prison *dm_bio_prison_create(unsigned nr_cells)
 
        prison->nr_buckets = nr_buckets;
        prison->hash_mask = nr_buckets - 1;
-       prison->cells = (struct hlist_head *) (prison + 1);
+       prison->buckets = (struct bucket *) (prison + 1);
        for (i = 0; i < nr_buckets; i++)
-               INIT_HLIST_HEAD(prison->cells + i);
+               init_bucket(prison->buckets + i);
 
        return prison;
 }
@@ -107,40 +116,44 @@ static int keys_equal(struct dm_cell_key *lhs, struct dm_cell_key *rhs)
                       (lhs->block == rhs->block);
 }
 
-static struct dm_bio_prison_cell *__search_bucket(struct hlist_head *bucket,
+static struct bucket *get_bucket(struct dm_bio_prison *prison,
+                                struct dm_cell_key *key)
+{
+       return prison->buckets + hash_key(prison, key);
+}
+
+static struct dm_bio_prison_cell *__search_bucket(struct bucket *b,
                                                  struct dm_cell_key *key)
 {
        struct dm_bio_prison_cell *cell;
 
-       hlist_for_each_entry(cell, bucket, list)
+       hlist_for_each_entry(cell, &b->cells, list)
                if (keys_equal(&cell->key, key))
                        return cell;
 
        return NULL;
 }
 
-static void __setup_new_cell(struct dm_bio_prison *prison,
+static void __setup_new_cell(struct bucket *b,
                             struct dm_cell_key *key,
                             struct bio *holder,
-                            uint32_t hash,
                             struct dm_bio_prison_cell *cell)
 {
        memcpy(&cell->key, key, sizeof(cell->key));
        cell->holder = holder;
        bio_list_init(&cell->bios);
-       hlist_add_head(&cell->list, prison->cells + hash);
+       hlist_add_head(&cell->list, &b->cells);
 }
 
-static int __bio_detain(struct dm_bio_prison *prison,
+static int __bio_detain(struct bucket *b,
                        struct dm_cell_key *key,
                        struct bio *inmate,
                        struct dm_bio_prison_cell *cell_prealloc,
                        struct dm_bio_prison_cell **cell_result)
 {
-       uint32_t hash = hash_key(prison, key);
        struct dm_bio_prison_cell *cell;
 
-       cell = __search_bucket(prison->cells + hash, key);
+       cell = __search_bucket(b, key);
        if (cell) {
                if (inmate)
                        bio_list_add(&cell->bios, inmate);
@@ -148,7 +161,7 @@ static int __bio_detain(struct dm_bio_prison *prison,
                return 1;
        }
 
-       __setup_new_cell(prison, key, inmate, hash, cell_prealloc);
+       __setup_new_cell(b, key, inmate, cell_prealloc);
        *cell_result = cell_prealloc;
        return 0;
 }
@@ -161,10 +174,11 @@ static int bio_detain(struct dm_bio_prison *prison,
 {
        int r;
        unsigned long flags;
+       struct bucket *b = get_bucket(prison, key);
 
-       spin_lock_irqsave(&prison->lock, flags);
-       r = __bio_detain(prison, key, inmate, cell_prealloc, cell_result);
-       spin_unlock_irqrestore(&prison->lock, flags);
+       spin_lock_irqsave(&b->lock, flags);
+       r = __bio_detain(b, key, inmate, cell_prealloc, cell_result);
+       spin_unlock_irqrestore(&b->lock, flags);
 
        return r;
 }
@@ -208,10 +222,11 @@ void dm_cell_release(struct dm_bio_prison *prison,
                     struct bio_list *bios)
 {
        unsigned long flags;
+       struct bucket *b = get_bucket(prison, &cell->key);
 
-       spin_lock_irqsave(&prison->lock, flags);
+       spin_lock_irqsave(&b->lock, flags);
        __cell_release(cell, bios);
-       spin_unlock_irqrestore(&prison->lock, flags);
+       spin_unlock_irqrestore(&b->lock, flags);
 }
 EXPORT_SYMBOL_GPL(dm_cell_release);
 
@@ -230,28 +245,25 @@ void dm_cell_release_no_holder(struct dm_bio_prison *prison,
                               struct bio_list *inmates)
 {
        unsigned long flags;
+       struct bucket *b = get_bucket(prison, &cell->key);
 
-       spin_lock_irqsave(&prison->lock, flags);
+       spin_lock_irqsave(&b->lock, flags);
        __cell_release_no_holder(cell, inmates);
-       spin_unlock_irqrestore(&prison->lock, flags);
+       spin_unlock_irqrestore(&b->lock, flags);
 }
 EXPORT_SYMBOL_GPL(dm_cell_release_no_holder);
 
 void dm_cell_error(struct dm_bio_prison *prison,
-                  struct dm_bio_prison_cell *cell)
+                  struct dm_bio_prison_cell *cell, int error)
 {
        struct bio_list bios;
        struct bio *bio;
-       unsigned long flags;
 
        bio_list_init(&bios);
-
-       spin_lock_irqsave(&prison->lock, flags);
-       __cell_release(cell, &bios);
-       spin_unlock_irqrestore(&prison->lock, flags);
+       dm_cell_release(prison, cell, &bios);
 
        while ((bio = bio_list_pop(&bios)))
-               bio_io_error(bio);
+               bio_endio(bio, error);
 }
 EXPORT_SYMBOL_GPL(dm_cell_error);
 
index 3f833190eadf6b3a275951a71558a583811d37ad..6805a142b750ee594d5ca2bda987d49a09787b39 100644 (file)
@@ -85,7 +85,7 @@ void dm_cell_release_no_holder(struct dm_bio_prison *prison,
                               struct dm_bio_prison_cell *cell,
                               struct bio_list *inmates);
 void dm_cell_error(struct dm_bio_prison *prison,
-                  struct dm_bio_prison_cell *cell);
+                  struct dm_bio_prison_cell *cell, int error);
 
 /*----------------------------------------------------------------*/
 
index 414dad4cb49b8be12fa8cdadc92055e09e9a89a5..ad913cd4aded33206ce8b79bae7e13d8d593f2fb 100644 (file)
@@ -1391,7 +1391,8 @@ static int era_is_congested(struct dm_target_callbacks *cb, int bdi_bits)
 
 static void era_destroy(struct era *era)
 {
-       metadata_close(era->md);
+       if (era->md)
+               metadata_close(era->md);
 
        if (era->wq)
                destroy_workqueue(era->wq);
index ebfa411d1a7d4b0193596b515f7086639057b5d0..3f6fd9d33ba3dc4a77500fb285d19bdd54f0c836 100644 (file)
@@ -1242,17 +1242,8 @@ static int do_end_io(struct multipath *m, struct request *clone,
        if (!error && !clone->errors)
                return 0;       /* I/O complete */
 
-       if (noretry_error(error)) {
-               if ((clone->cmd_flags & REQ_WRITE_SAME) &&
-                   !clone->q->limits.max_write_same_sectors) {
-                       struct queue_limits *limits;
-
-                       /* device doesn't really support WRITE SAME, disable it */
-                       limits = dm_get_queue_limits(dm_table_get_md(m->ti->table));
-                       limits->max_write_same_sectors = 0;
-               }
+       if (noretry_error(error))
                return error;
-       }
 
        if (mpio->pgpath)
                fail_path(mpio->pgpath);
index 8e0caed0bf74650d85279b9331d1b1fec132058f..5bd2290cfb1e21b73790a0543888bc8b394c83ea 100644 (file)
@@ -2141,6 +2141,11 @@ static int origin_write_extent(struct dm_snapshot *merging_snap,
  * Origin: maps a linear range of a device, with hooks for snapshotting.
  */
 
+struct dm_origin {
+       struct dm_dev *dev;
+       unsigned split_boundary;
+};
+
 /*
  * Construct an origin mapping: <dev_path>
  * The context for an origin is merely a 'struct dm_dev *'
@@ -2149,41 +2154,65 @@ static int origin_write_extent(struct dm_snapshot *merging_snap,
 static int origin_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 {
        int r;
-       struct dm_dev *dev;
+       struct dm_origin *o;
 
        if (argc != 1) {
                ti->error = "origin: incorrect number of arguments";
                return -EINVAL;
        }
 
-       r = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &dev);
+       o = kmalloc(sizeof(struct dm_origin), GFP_KERNEL);
+       if (!o) {
+               ti->error = "Cannot allocate private origin structure";
+               r = -ENOMEM;
+               goto bad_alloc;
+       }
+
+       r = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &o->dev);
        if (r) {
                ti->error = "Cannot get target device";
-               return r;
+               goto bad_open;
        }
 
-       ti->private = dev;
+       ti->private = o;
        ti->num_flush_bios = 1;
 
        return 0;
+
+bad_open:
+       kfree(o);
+bad_alloc:
+       return r;
 }
 
 static void origin_dtr(struct dm_target *ti)
 {
-       struct dm_dev *dev = ti->private;
-       dm_put_device(ti, dev);
+       struct dm_origin *o = ti->private;
+       dm_put_device(ti, o->dev);
+       kfree(o);
 }
 
 static int origin_map(struct dm_target *ti, struct bio *bio)
 {
-       struct dm_dev *dev = ti->private;
-       bio->bi_bdev = dev->bdev;
+       struct dm_origin *o = ti->private;
+       unsigned available_sectors;
 
-       if (bio->bi_rw & REQ_FLUSH)
+       bio->bi_bdev = o->dev->bdev;
+
+       if (unlikely(bio->bi_rw & REQ_FLUSH))
                return DM_MAPIO_REMAPPED;
 
+       if (bio_rw(bio) != WRITE)
+               return DM_MAPIO_REMAPPED;
+
+       available_sectors = o->split_boundary -
+               ((unsigned)bio->bi_iter.bi_sector & (o->split_boundary - 1));
+
+       if (bio_sectors(bio) > available_sectors)
+               dm_accept_partial_bio(bio, available_sectors);
+
        /* Only tell snapshots if this is a write */
-       return (bio_rw(bio) == WRITE) ? do_origin(dev, bio) : DM_MAPIO_REMAPPED;
+       return do_origin(o->dev, bio);
 }
 
 /*
@@ -2192,15 +2221,15 @@ static int origin_map(struct dm_target *ti, struct bio *bio)
  */
 static void origin_resume(struct dm_target *ti)
 {
-       struct dm_dev *dev = ti->private;
+       struct dm_origin *o = ti->private;
 
-       ti->max_io_len = get_origin_minimum_chunksize(dev->bdev);
+       o->split_boundary = get_origin_minimum_chunksize(o->dev->bdev);
 }
 
 static void origin_status(struct dm_target *ti, status_type_t type,
                          unsigned status_flags, char *result, unsigned maxlen)
 {
-       struct dm_dev *dev = ti->private;
+       struct dm_origin *o = ti->private;
 
        switch (type) {
        case STATUSTYPE_INFO:
@@ -2208,7 +2237,7 @@ static void origin_status(struct dm_target *ti, status_type_t type,
                break;
 
        case STATUSTYPE_TABLE:
-               snprintf(result, maxlen, "%s", dev->name);
+               snprintf(result, maxlen, "%s", o->dev->name);
                break;
        }
 }
@@ -2216,13 +2245,13 @@ static void origin_status(struct dm_target *ti, status_type_t type,
 static int origin_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
                        struct bio_vec *biovec, int max_size)
 {
-       struct dm_dev *dev = ti->private;
-       struct request_queue *q = bdev_get_queue(dev->bdev);
+       struct dm_origin *o = ti->private;
+       struct request_queue *q = bdev_get_queue(o->dev->bdev);
 
        if (!q->merge_bvec_fn)
                return max_size;
 
-       bvm->bi_bdev = dev->bdev;
+       bvm->bi_bdev = o->dev->bdev;
 
        return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
 }
@@ -2230,9 +2259,9 @@ static int origin_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
 static int origin_iterate_devices(struct dm_target *ti,
                                  iterate_devices_callout_fn fn, void *data)
 {
-       struct dm_dev *dev = ti->private;
+       struct dm_origin *o = ti->private;
 
-       return fn(ti, dev, 0, ti->len, data);
+       return fn(ti, o->dev, 0, ti->len, data);
 }
 
 static struct target_type origin_target = {
index 50601ec7017acd3e310abedc9bb6c8ba45525344..5f59f1e3e5b11de3156eef9e5bcd68b6a46c3b82 100644 (file)
@@ -465,8 +465,8 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
 }
 EXPORT_SYMBOL(dm_get_device);
 
-int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
-                        sector_t start, sector_t len, void *data)
+static int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
+                               sector_t start, sector_t len, void *data)
 {
        struct queue_limits *limits = data;
        struct block_device *bdev = dev->bdev;
@@ -499,7 +499,6 @@ int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
                                          (unsigned int) (PAGE_SIZE >> 9));
        return 0;
 }
-EXPORT_SYMBOL_GPL(dm_set_device_limits);
 
 /*
  * Decrement a device's use count and remove it if necessary.
index 242ac2ea5f295c0bf2ad2db85fdd86812936851d..fc9c848a60c9267a44296b54656bbdda60f40fd0 100644 (file)
@@ -310,13 +310,18 @@ static void cell_defer_no_holder_no_free(struct thin_c *tc,
        wake_worker(pool);
 }
 
-static void cell_error(struct pool *pool,
-                      struct dm_bio_prison_cell *cell)
+static void cell_error_with_code(struct pool *pool,
+                                struct dm_bio_prison_cell *cell, int error_code)
 {
-       dm_cell_error(pool->prison, cell);
+       dm_cell_error(pool->prison, cell, error_code);
        dm_bio_prison_free_cell(pool->prison, cell);
 }
 
+static void cell_error(struct pool *pool, struct dm_bio_prison_cell *cell)
+{
+       cell_error_with_code(pool, cell, -EIO);
+}
+
 /*----------------------------------------------------------------*/
 
 /*
@@ -1027,7 +1032,7 @@ static void retry_on_resume(struct bio *bio)
        spin_unlock_irqrestore(&tc->lock, flags);
 }
 
-static bool should_error_unserviceable_bio(struct pool *pool)
+static int should_error_unserviceable_bio(struct pool *pool)
 {
        enum pool_mode m = get_pool_mode(pool);
 
@@ -1035,25 +1040,27 @@ static bool should_error_unserviceable_bio(struct pool *pool)
        case PM_WRITE:
                /* Shouldn't get here */
                DMERR_LIMIT("bio unserviceable, yet pool is in PM_WRITE mode");
-               return true;
+               return -EIO;
 
        case PM_OUT_OF_DATA_SPACE:
-               return pool->pf.error_if_no_space;
+               return pool->pf.error_if_no_space ? -ENOSPC : 0;
 
        case PM_READ_ONLY:
        case PM_FAIL:
-               return true;
+               return -EIO;
        default:
                /* Shouldn't get here */
                DMERR_LIMIT("bio unserviceable, yet pool has an unknown mode");
-               return true;
+               return -EIO;
        }
 }
 
 static void handle_unserviceable_bio(struct pool *pool, struct bio *bio)
 {
-       if (should_error_unserviceable_bio(pool))
-               bio_io_error(bio);
+       int error = should_error_unserviceable_bio(pool);
+
+       if (error)
+               bio_endio(bio, error);
        else
                retry_on_resume(bio);
 }
@@ -1062,18 +1069,21 @@ static void retry_bios_on_resume(struct pool *pool, struct dm_bio_prison_cell *c
 {
        struct bio *bio;
        struct bio_list bios;
+       int error;
 
-       if (should_error_unserviceable_bio(pool)) {
-               cell_error(pool, cell);
+       error = should_error_unserviceable_bio(pool);
+       if (error) {
+               cell_error_with_code(pool, cell, error);
                return;
        }
 
        bio_list_init(&bios);
        cell_release(pool, cell, &bios);
 
-       if (should_error_unserviceable_bio(pool))
+       error = should_error_unserviceable_bio(pool);
+       if (error)
                while ((bio = bio_list_pop(&bios)))
-                       bio_io_error(bio);
+                       bio_endio(bio, error);
        else
                while ((bio = bio_list_pop(&bios)))
                        retry_on_resume(bio);
@@ -1610,47 +1620,63 @@ static void do_no_space_timeout(struct work_struct *ws)
 
 /*----------------------------------------------------------------*/
 
-struct noflush_work {
+struct pool_work {
        struct work_struct worker;
-       struct thin_c *tc;
+       struct completion complete;
+};
+
+static struct pool_work *to_pool_work(struct work_struct *ws)
+{
+       return container_of(ws, struct pool_work, worker);
+}
 
-       atomic_t complete;
-       wait_queue_head_t wait;
+static void pool_work_complete(struct pool_work *pw)
+{
+       complete(&pw->complete);
+}
+
+static void pool_work_wait(struct pool_work *pw, struct pool *pool,
+                          void (*fn)(struct work_struct *))
+{
+       INIT_WORK_ONSTACK(&pw->worker, fn);
+       init_completion(&pw->complete);
+       queue_work(pool->wq, &pw->worker);
+       wait_for_completion(&pw->complete);
+}
+
+/*----------------------------------------------------------------*/
+
+struct noflush_work {
+       struct pool_work pw;
+       struct thin_c *tc;
 };
 
-static void complete_noflush_work(struct noflush_work *w)
+static struct noflush_work *to_noflush(struct work_struct *ws)
 {
-       atomic_set(&w->complete, 1);
-       wake_up(&w->wait);
+       return container_of(to_pool_work(ws), struct noflush_work, pw);
 }
 
 static void do_noflush_start(struct work_struct *ws)
 {
-       struct noflush_work *w = container_of(ws, struct noflush_work, worker);
+       struct noflush_work *w = to_noflush(ws);
        w->tc->requeue_mode = true;
        requeue_io(w->tc);
-       complete_noflush_work(w);
+       pool_work_complete(&w->pw);
 }
 
 static void do_noflush_stop(struct work_struct *ws)
 {
-       struct noflush_work *w = container_of(ws, struct noflush_work, worker);
+       struct noflush_work *w = to_noflush(ws);
        w->tc->requeue_mode = false;
-       complete_noflush_work(w);
+       pool_work_complete(&w->pw);
 }
 
 static void noflush_work(struct thin_c *tc, void (*fn)(struct work_struct *))
 {
        struct noflush_work w;
 
-       INIT_WORK_ONSTACK(&w.worker, fn);
        w.tc = tc;
-       atomic_set(&w.complete, 0);
-       init_waitqueue_head(&w.wait);
-
-       queue_work(tc->pool->wq, &w.worker);
-
-       wait_event(w.wait, atomic_read(&w.complete));
+       pool_work_wait(&w.pw, tc->pool, fn);
 }
 
 /*----------------------------------------------------------------*/
@@ -3068,7 +3094,8 @@ static void set_discard_limits(struct pool_c *pt, struct queue_limits *limits)
         */
        if (pt->adjusted_pf.discard_passdown) {
                data_limits = &bdev_get_queue(pt->data_dev->bdev)->limits;
-               limits->discard_granularity = data_limits->discard_granularity;
+               limits->discard_granularity = max(data_limits->discard_granularity,
+                                                 pool->sectors_per_block << SECTOR_SHIFT);
        } else
                limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT;
 }
index aa9e093343d435eba77bb7fb1587e2c53b6f5d9a..437d99045ef2c3ec4969f7b1a0717ec8cb672d90 100644 (file)
@@ -755,6 +755,14 @@ static void dec_pending(struct dm_io *io, int error)
        }
 }
 
+static void disable_write_same(struct mapped_device *md)
+{
+       struct queue_limits *limits = dm_get_queue_limits(md);
+
+       /* device doesn't really support WRITE SAME, disable it */
+       limits->max_write_same_sectors = 0;
+}
+
 static void clone_endio(struct bio *bio, int error)
 {
        int r = 0;
@@ -783,6 +791,10 @@ static void clone_endio(struct bio *bio, int error)
                }
        }
 
+       if (unlikely(r == -EREMOTEIO && (bio->bi_rw & REQ_WRITE_SAME) &&
+                    !bdev_get_queue(bio->bi_bdev)->limits.max_write_same_sectors))
+               disable_write_same(md);
+
        free_tio(md, tio);
        dec_pending(io, error);
 }
@@ -977,6 +989,10 @@ static void dm_done(struct request *clone, int error, bool mapped)
                        r = rq_end_io(tio->ti, clone, error, &tio->info);
        }
 
+       if (unlikely(r == -EREMOTEIO && (clone->cmd_flags & REQ_WRITE_SAME) &&
+                    !clone->q->limits.max_write_same_sectors))
+               disable_write_same(tio->md);
+
        if (r <= 0)
                /* The target wants to complete the I/O */
                dm_end_request(clone, r);
@@ -1110,6 +1126,46 @@ int dm_set_target_max_io_len(struct dm_target *ti, sector_t len)
 }
 EXPORT_SYMBOL_GPL(dm_set_target_max_io_len);
 
+/*
+ * A target may call dm_accept_partial_bio only from the map routine.  It is
+ * allowed for all bio types except REQ_FLUSH.
+ *
+ * dm_accept_partial_bio informs the dm that the target only wants to process
+ * additional n_sectors sectors of the bio and the rest of the data should be
+ * sent in a next bio.
+ *
+ * A diagram that explains the arithmetics:
+ * +--------------------+---------------+-------+
+ * |         1          |       2       |   3   |
+ * +--------------------+---------------+-------+
+ *
+ * <-------------- *tio->len_ptr --------------->
+ *                      <------- bi_size ------->
+ *                      <-- n_sectors -->
+ *
+ * Region 1 was already iterated over with bio_advance or similar function.
+ *     (it may be empty if the target doesn't use bio_advance)
+ * Region 2 is the remaining bio size that the target wants to process.
+ *     (it may be empty if region 1 is non-empty, although there is no reason
+ *      to make it empty)
+ * The target requires that region 3 is to be sent in the next bio.
+ *
+ * If the target wants to receive multiple copies of the bio (via num_*bios, etc),
+ * the partially processed part (the sum of regions 1+2) must be the same for all
+ * copies of the bio.
+ */
+void dm_accept_partial_bio(struct bio *bio, unsigned n_sectors)
+{
+       struct dm_target_io *tio = container_of(bio, struct dm_target_io, clone);
+       unsigned bi_size = bio->bi_iter.bi_size >> SECTOR_SHIFT;
+       BUG_ON(bio->bi_rw & REQ_FLUSH);
+       BUG_ON(bi_size > *tio->len_ptr);
+       BUG_ON(n_sectors > bi_size);
+       *tio->len_ptr -= bi_size - n_sectors;
+       bio->bi_iter.bi_size = n_sectors << SECTOR_SHIFT;
+}
+EXPORT_SYMBOL_GPL(dm_accept_partial_bio);
+
 static void __map_bio(struct dm_target_io *tio)
 {
        int r;
@@ -1152,10 +1208,10 @@ struct clone_info {
        struct bio *bio;
        struct dm_io *io;
        sector_t sector;
-       sector_t sector_count;
+       unsigned sector_count;
 };
 
-static void bio_setup_sector(struct bio *bio, sector_t sector, sector_t len)
+static void bio_setup_sector(struct bio *bio, sector_t sector, unsigned len)
 {
        bio->bi_iter.bi_sector = sector;
        bio->bi_iter.bi_size = to_bytes(len);
@@ -1200,11 +1256,13 @@ static struct dm_target_io *alloc_tio(struct clone_info *ci,
 
 static void __clone_and_map_simple_bio(struct clone_info *ci,
                                       struct dm_target *ti,
-                                      unsigned target_bio_nr, sector_t len)
+                                      unsigned target_bio_nr, unsigned *len)
 {
        struct dm_target_io *tio = alloc_tio(ci, ti, ci->bio->bi_max_vecs, target_bio_nr);
        struct bio *clone = &tio->clone;
 
+       tio->len_ptr = len;
+
        /*
         * Discard requests require the bio's inline iovecs be initialized.
         * ci->bio->bi_max_vecs is BIO_INLINE_VECS anyway, for both flush
@@ -1212,13 +1270,13 @@ static void __clone_and_map_simple_bio(struct clone_info *ci,
         */
         __bio_clone_fast(clone, ci->bio);
        if (len)
-               bio_setup_sector(clone, ci->sector, len);
+               bio_setup_sector(clone, ci->sector, *len);
 
        __map_bio(tio);
 }
 
 static void __send_duplicate_bios(struct clone_info *ci, struct dm_target *ti,
-                                 unsigned num_bios, sector_t len)
+                                 unsigned num_bios, unsigned *len)
 {
        unsigned target_bio_nr;
 
@@ -1233,13 +1291,13 @@ static int __send_empty_flush(struct clone_info *ci)
 
        BUG_ON(bio_has_data(ci->bio));
        while ((ti = dm_table_get_target(ci->map, target_nr++)))
-               __send_duplicate_bios(ci, ti, ti->num_flush_bios, 0);
+               __send_duplicate_bios(ci, ti, ti->num_flush_bios, NULL);
 
        return 0;
 }
 
 static void __clone_and_map_data_bio(struct clone_info *ci, struct dm_target *ti,
-                                    sector_t sector, unsigned len)
+                                    sector_t sector, unsigned *len)
 {
        struct bio *bio = ci->bio;
        struct dm_target_io *tio;
@@ -1254,7 +1312,8 @@ static void __clone_and_map_data_bio(struct clone_info *ci, struct dm_target *ti
 
        for (target_bio_nr = 0; target_bio_nr < num_target_bios; target_bio_nr++) {
                tio = alloc_tio(ci, ti, 0, target_bio_nr);
-               clone_bio(tio, bio, sector, len);
+               tio->len_ptr = len;
+               clone_bio(tio, bio, sector, *len);
                __map_bio(tio);
        }
 }
@@ -1283,7 +1342,7 @@ static int __send_changing_extent_only(struct clone_info *ci,
                                       is_split_required_fn is_split_required)
 {
        struct dm_target *ti;
-       sector_t len;
+       unsigned len;
        unsigned num_bios;
 
        do {
@@ -1302,11 +1361,11 @@ static int __send_changing_extent_only(struct clone_info *ci,
                        return -EOPNOTSUPP;
 
                if (is_split_required && !is_split_required(ti))
-                       len = min(ci->sector_count, max_io_len_target_boundary(ci->sector, ti));
+                       len = min((sector_t)ci->sector_count, max_io_len_target_boundary(ci->sector, ti));
                else
-                       len = min(ci->sector_count, max_io_len(ci->sector, ti));
+                       len = min((sector_t)ci->sector_count, max_io_len(ci->sector, ti));
 
-               __send_duplicate_bios(ci, ti, num_bios, len);
+               __send_duplicate_bios(ci, ti, num_bios, &len);
 
                ci->sector += len;
        } while (ci->sector_count -= len);
@@ -1345,7 +1404,7 @@ static int __split_and_process_non_flush(struct clone_info *ci)
 
        len = min_t(sector_t, max_io_len(ci->sector, ti), ci->sector_count);
 
-       __clone_and_map_data_bio(ci, ti, ci->sector, len);
+       __clone_and_map_data_bio(ci, ti, ci->sector, &len);
 
        ci->sector += len;
        ci->sector_count -= len;
@@ -1439,7 +1498,6 @@ static int dm_merge_bvec(struct request_queue *q,
         * just one page.
         */
        else if (queue_max_hw_sectors(q) <= PAGE_SIZE >> 9)
-
                max_size = 0;
 
 out:
index 20f1655e6d7595c328b5499476f4dde7773cccd0..8108c698b5483c9250ec532f33af815b453aae1e 100644 (file)
@@ -93,7 +93,9 @@ config VIDEO_M32R_AR_M64278
 
 config VIDEO_OMAP3
        tristate "OMAP 3 Camera support"
-       depends on OMAP_IOVMM && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3
+       depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3
+       select ARM_DMA_USE_IOMMU
+       select OMAP_IOMMU
        ---help---
          Driver for an OMAP 3 camera controller.
 
index e8847e79e31aefc236a279d0b93c2b1953131567..254975a9174ebdf73d253e3bb5e8d8abce46e2c9 100644 (file)
@@ -3,7 +3,7 @@
 ccflags-$(CONFIG_VIDEO_OMAP3_DEBUG) += -DDEBUG
 
 omap3-isp-objs += \
-       isp.o ispqueue.o ispvideo.o \
+       isp.o ispvideo.o \
        ispcsiphy.o ispccp2.o ispcsi2.o \
        ispccdc.o isppreview.o ispresizer.o \
        ispstat.o isph3a_aewb.o isph3a_af.o isphist.o
index 06a0df434249a7f9e658d5cae51960d6b4e965d3..2c7aa67205693f36287756296aaa6e8debf0bc1c 100644 (file)
@@ -69,6 +69,8 @@
 #include <linux/sched.h>
 #include <linux/vmalloc.h>
 
+#include <asm/dma-iommu.h>
+
 #include <media/v4l2-common.h>
 #include <media/v4l2-device.h>
 
@@ -1397,14 +1399,14 @@ int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
        if (isp_pipeline_is_last(me)) {
                struct isp_video *video = pipe->output;
                unsigned long flags;
-               spin_lock_irqsave(&video->queue->irqlock, flags);
+               spin_lock_irqsave(&video->irqlock, flags);
                if (video->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) {
-                       spin_unlock_irqrestore(&video->queue->irqlock, flags);
+                       spin_unlock_irqrestore(&video->irqlock, flags);
                        atomic_set(stopping, 0);
                        smp_mb();
                        return 0;
                }
-               spin_unlock_irqrestore(&video->queue->irqlock, flags);
+               spin_unlock_irqrestore(&video->irqlock, flags);
                if (!wait_event_timeout(*wait, !atomic_read(stopping),
                                        msecs_to_jiffies(1000))) {
                        atomic_set(stopping, 0);
@@ -1625,7 +1627,7 @@ struct isp_device *omap3isp_get(struct isp_device *isp)
  * Decrement the reference count on the ISP. If the last reference is released,
  * power-down all submodules, disable clocks and free temporary buffers.
  */
-void omap3isp_put(struct isp_device *isp)
+static void __omap3isp_put(struct isp_device *isp, bool save_ctx)
 {
        if (isp == NULL)
                return;
@@ -1634,7 +1636,7 @@ void omap3isp_put(struct isp_device *isp)
        BUG_ON(isp->ref_count == 0);
        if (--isp->ref_count == 0) {
                isp_disable_interrupts(isp);
-               if (isp->domain) {
+               if (save_ctx) {
                        isp_save_ctx(isp);
                        isp->has_context = 1;
                }
@@ -1648,6 +1650,11 @@ void omap3isp_put(struct isp_device *isp)
        mutex_unlock(&isp->isp_mutex);
 }
 
+void omap3isp_put(struct isp_device *isp)
+{
+       __omap3isp_put(isp, true);
+}
+
 /* --------------------------------------------------------------------------
  * Platform device driver
  */
@@ -2120,6 +2127,61 @@ error_csiphy:
        return ret;
 }
 
+static void isp_detach_iommu(struct isp_device *isp)
+{
+       arm_iommu_release_mapping(isp->mapping);
+       isp->mapping = NULL;
+       iommu_group_remove_device(isp->dev);
+}
+
+static int isp_attach_iommu(struct isp_device *isp)
+{
+       struct dma_iommu_mapping *mapping;
+       struct iommu_group *group;
+       int ret;
+
+       /* Create a device group and add the device to it. */
+       group = iommu_group_alloc();
+       if (IS_ERR(group)) {
+               dev_err(isp->dev, "failed to allocate IOMMU group\n");
+               return PTR_ERR(group);
+       }
+
+       ret = iommu_group_add_device(group, isp->dev);
+       iommu_group_put(group);
+
+       if (ret < 0) {
+               dev_err(isp->dev, "failed to add device to IPMMU group\n");
+               return ret;
+       }
+
+       /*
+        * Create the ARM mapping, used by the ARM DMA mapping core to allocate
+        * VAs. This will allocate a corresponding IOMMU domain.
+        */
+       mapping = arm_iommu_create_mapping(&platform_bus_type, SZ_1G, SZ_2G);
+       if (IS_ERR(mapping)) {
+               dev_err(isp->dev, "failed to create ARM IOMMU mapping\n");
+               ret = PTR_ERR(mapping);
+               goto error;
+       }
+
+       isp->mapping = mapping;
+
+       /* Attach the ARM VA mapping to the device. */
+       ret = arm_iommu_attach_device(isp->dev, mapping);
+       if (ret < 0) {
+               dev_err(isp->dev, "failed to attach device to VA mapping\n");
+               goto error;
+       }
+
+       return 0;
+
+error:
+       isp_detach_iommu(isp);
+       return ret;
+}
+
 /*
  * isp_remove - Remove ISP platform device
  * @pdev: Pointer to ISP platform device
@@ -2135,10 +2197,8 @@ static int isp_remove(struct platform_device *pdev)
        isp_xclk_cleanup(isp);
 
        __omap3isp_get(isp, false);
-       iommu_detach_device(isp->domain, &pdev->dev);
-       iommu_domain_free(isp->domain);
-       isp->domain = NULL;
-       omap3isp_put(isp);
+       isp_detach_iommu(isp);
+       __omap3isp_put(isp, false);
 
        return 0;
 }
@@ -2265,39 +2325,32 @@ static int isp_probe(struct platform_device *pdev)
                }
        }
 
-       isp->domain = iommu_domain_alloc(pdev->dev.bus);
-       if (!isp->domain) {
-               dev_err(isp->dev, "can't alloc iommu domain\n");
-               ret = -ENOMEM;
+       /* IOMMU */
+       ret = isp_attach_iommu(isp);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "unable to attach to IOMMU\n");
                goto error_isp;
        }
 
-       ret = iommu_attach_device(isp->domain, &pdev->dev);
-       if (ret) {
-               dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret);
-               ret = -EPROBE_DEFER;
-               goto free_domain;
-       }
-
        /* Interrupt */
        isp->irq_num = platform_get_irq(pdev, 0);
        if (isp->irq_num <= 0) {
                dev_err(isp->dev, "No IRQ resource\n");
                ret = -ENODEV;
-               goto detach_dev;
+               goto error_iommu;
        }
 
        if (devm_request_irq(isp->dev, isp->irq_num, isp_isr, IRQF_SHARED,
                             "OMAP3 ISP", isp)) {
                dev_err(isp->dev, "Unable to request IRQ\n");
                ret = -EINVAL;
-               goto detach_dev;
+               goto error_iommu;
        }
 
        /* Entities */
        ret = isp_initialize_modules(isp);
        if (ret < 0)
-               goto detach_dev;
+               goto error_iommu;
 
        ret = isp_register_entities(isp);
        if (ret < 0)
@@ -2310,14 +2363,11 @@ static int isp_probe(struct platform_device *pdev)
 
 error_modules:
        isp_cleanup_modules(isp);
-detach_dev:
-       iommu_detach_device(isp->domain, &pdev->dev);
-free_domain:
-       iommu_domain_free(isp->domain);
-       isp->domain = NULL;
+error_iommu:
+       isp_detach_iommu(isp);
 error_isp:
        isp_xclk_cleanup(isp);
-       omap3isp_put(isp);
+       __omap3isp_put(isp, false);
 error:
        mutex_destroy(&isp->isp_mutex);
 
index 6d5e69711907e655175ffe943fa5e2602582b556..2c314eea125275942db5076c34ac4613d4d02537 100644 (file)
@@ -45,8 +45,6 @@
 #include "ispcsi2.h"
 #include "ispccp2.h"
 
-#define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8)
-
 #define ISP_TOK_TERM           0xFFFFFFFF      /*
                                                 * terminating token for ISP
                                                 * modules reg list
@@ -152,6 +150,7 @@ struct isp_xclk {
  *             regions.
  * @mmio_base_phys: Array with physical L4 bus addresses for ISP register
  *                  regions.
+ * @mapping: IOMMU mapping
  * @stat_lock: Spinlock for handling statistics
  * @isp_mutex: Mutex for serializing requests to ISP.
  * @stop_failure: Indicates that an entity failed to stop.
@@ -171,7 +170,6 @@ struct isp_xclk {
  * @isp_res: Pointer to current settings for ISP Resizer.
  * @isp_prev: Pointer to current settings for ISP Preview.
  * @isp_ccdc: Pointer to current settings for ISP CCDC.
- * @iommu: Pointer to requested IOMMU instance for ISP.
  * @platform_cb: ISP driver callback function pointers for platform code
  *
  * This structure is used to store the OMAP ISP Information.
@@ -189,6 +187,8 @@ struct isp_device {
        void __iomem *mmio_base[OMAP3_ISP_IOMEM_LAST];
        unsigned long mmio_base_phys[OMAP3_ISP_IOMEM_LAST];
 
+       struct dma_iommu_mapping *mapping;
+
        /* ISP Obj */
        spinlock_t stat_lock;   /* common lock for statistic drivers */
        struct mutex isp_mutex; /* For handling ref_count field */
@@ -219,8 +219,6 @@ struct isp_device {
 
        unsigned int sbl_resources;
        unsigned int subclk_resources;
-
-       struct iommu_domain *domain;
 };
 
 #define v4l2_dev_to_isp_device(dev) \
index 4d920c800ff5fc1e863199b332acadb57294210b..9f727d20f06d98298dcf36a4e0a9e8390ad2d9da 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
 #include <linux/mm.h>
-#include <linux/omap-iommu.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <media/v4l2-event.h>
@@ -206,7 +205,8 @@ static int ccdc_lsc_validate_config(struct isp_ccdc_device *ccdc,
  * ccdc_lsc_program_table - Program Lens Shading Compensation table address.
  * @ccdc: Pointer to ISP CCDC device.
  */
-static void ccdc_lsc_program_table(struct isp_ccdc_device *ccdc, u32 addr)
+static void ccdc_lsc_program_table(struct isp_ccdc_device *ccdc,
+                                  dma_addr_t addr)
 {
        isp_reg_writel(to_isp_device(ccdc), addr,
                       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_BASE);
@@ -333,7 +333,7 @@ static int __ccdc_lsc_configure(struct isp_ccdc_device *ccdc,
                return -EBUSY;
 
        ccdc_lsc_setup_regs(ccdc, &req->config);
-       ccdc_lsc_program_table(ccdc, req->table);
+       ccdc_lsc_program_table(ccdc, req->table.dma);
        return 0;
 }
 
@@ -368,11 +368,12 @@ static void ccdc_lsc_free_request(struct isp_ccdc_device *ccdc,
        if (req == NULL)
                return;
 
-       if (req->iovm)
-               dma_unmap_sg(isp->dev, req->iovm->sgt->sgl,
-                            req->iovm->sgt->nents, DMA_TO_DEVICE);
-       if (req->table)
-               omap_iommu_vfree(isp->domain, isp->dev, req->table);
+       if (req->table.addr) {
+               sg_free_table(&req->table.sgt);
+               dma_free_coherent(isp->dev, req->config.size, req->table.addr,
+                                 req->table.dma);
+       }
+
        kfree(req);
 }
 
@@ -416,7 +417,6 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
        struct isp_device *isp = to_isp_device(ccdc);
        struct ispccdc_lsc_config_req *req;
        unsigned long flags;
-       void *table;
        u16 update;
        int ret;
 
@@ -444,38 +444,31 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
 
                req->enable = 1;
 
-               req->table = omap_iommu_vmalloc(isp->domain, isp->dev, 0,
-                                       req->config.size, IOMMU_FLAG);
-               if (IS_ERR_VALUE(req->table)) {
-                       req->table = 0;
-                       ret = -ENOMEM;
-                       goto done;
-               }
-
-               req->iovm = omap_find_iovm_area(isp->dev, req->table);
-               if (req->iovm == NULL) {
+               req->table.addr = dma_alloc_coherent(isp->dev, req->config.size,
+                                                    &req->table.dma,
+                                                    GFP_KERNEL);
+               if (req->table.addr == NULL) {
                        ret = -ENOMEM;
                        goto done;
                }
 
-               if (!dma_map_sg(isp->dev, req->iovm->sgt->sgl,
-                               req->iovm->sgt->nents, DMA_TO_DEVICE)) {
-                       ret = -ENOMEM;
-                       req->iovm = NULL;
+               ret = dma_get_sgtable(isp->dev, &req->table.sgt,
+                                     req->table.addr, req->table.dma,
+                                     req->config.size);
+               if (ret < 0)
                        goto done;
-               }
 
-               dma_sync_sg_for_cpu(isp->dev, req->iovm->sgt->sgl,
-                                   req->iovm->sgt->nents, DMA_TO_DEVICE);
+               dma_sync_sg_for_cpu(isp->dev, req->table.sgt.sgl,
+                                   req->table.sgt.nents, DMA_TO_DEVICE);
 
-               table = omap_da_to_va(isp->dev, req->table);
-               if (copy_from_user(table, config->lsc, req->config.size)) {
+               if (copy_from_user(req->table.addr, config->lsc,
+                                  req->config.size)) {
                        ret = -EFAULT;
                        goto done;
                }
 
-               dma_sync_sg_for_device(isp->dev, req->iovm->sgt->sgl,
-                                      req->iovm->sgt->nents, DMA_TO_DEVICE);
+               dma_sync_sg_for_device(isp->dev, req->table.sgt.sgl,
+                                      req->table.sgt.nents, DMA_TO_DEVICE);
        }
 
        spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
@@ -584,7 +577,7 @@ static void ccdc_configure_fpc(struct isp_ccdc_device *ccdc)
        if (!ccdc->fpc_en)
                return;
 
-       isp_reg_writel(isp, ccdc->fpc.fpcaddr, OMAP3_ISP_IOMEM_CCDC,
+       isp_reg_writel(isp, ccdc->fpc.dma, OMAP3_ISP_IOMEM_CCDC,
                       ISPCCDC_FPC_ADDR);
        /* The FPNUM field must be set before enabling FPC. */
        isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT),
@@ -724,8 +717,9 @@ static int ccdc_config(struct isp_ccdc_device *ccdc,
        ccdc->shadow_update = 0;
 
        if (OMAP3ISP_CCDC_FPC & ccdc_struct->update) {
-               u32 table_old = 0;
-               u32 table_new;
+               struct omap3isp_ccdc_fpc fpc;
+               struct ispccdc_fpc fpc_old = { .addr = NULL, };
+               struct ispccdc_fpc fpc_new;
                u32 size;
 
                if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED)
@@ -734,35 +728,39 @@ static int ccdc_config(struct isp_ccdc_device *ccdc,
                ccdc->fpc_en = !!(OMAP3ISP_CCDC_FPC & ccdc_struct->flag);
 
                if (ccdc->fpc_en) {
-                       if (copy_from_user(&ccdc->fpc, ccdc_struct->fpc,
-                                          sizeof(ccdc->fpc)))
+                       if (copy_from_user(&fpc, ccdc_struct->fpc, sizeof(fpc)))
                                return -EFAULT;
 
+                       size = fpc.fpnum * 4;
+
                        /*
-                        * table_new must be 64-bytes aligned, but it's
-                        * already done by omap_iommu_vmalloc().
+                        * The table address must be 64-bytes aligned, which is
+                        * guaranteed by dma_alloc_coherent().
                         */
-                       size = ccdc->fpc.fpnum * 4;
-                       table_new = omap_iommu_vmalloc(isp->domain, isp->dev,
-                                                       0, size, IOMMU_FLAG);
-                       if (IS_ERR_VALUE(table_new))
+                       fpc_new.fpnum = fpc.fpnum;
+                       fpc_new.addr = dma_alloc_coherent(isp->dev, size,
+                                                         &fpc_new.dma,
+                                                         GFP_KERNEL);
+                       if (fpc_new.addr == NULL)
                                return -ENOMEM;
 
-                       if (copy_from_user(omap_da_to_va(isp->dev, table_new),
-                                          (__force void __user *)
-                                          ccdc->fpc.fpcaddr, size)) {
-                               omap_iommu_vfree(isp->domain, isp->dev,
-                                                               table_new);
+                       if (copy_from_user(fpc_new.addr,
+                                          (__force void __user *)fpc.fpcaddr,
+                                          size)) {
+                               dma_free_coherent(isp->dev, size, fpc_new.addr,
+                                                 fpc_new.dma);
                                return -EFAULT;
                        }
 
-                       table_old = ccdc->fpc.fpcaddr;
-                       ccdc->fpc.fpcaddr = table_new;
+                       fpc_old = ccdc->fpc;
+                       ccdc->fpc = fpc_new;
                }
 
                ccdc_configure_fpc(ccdc);
-               if (table_old != 0)
-                       omap_iommu_vfree(isp->domain, isp->dev, table_old);
+
+               if (fpc_old.addr != NULL)
+                       dma_free_coherent(isp->dev, fpc_old.fpnum * 4,
+                                         fpc_old.addr, fpc_old.dma);
        }
 
        return ccdc_lsc_config(ccdc, ccdc_struct);
@@ -1523,7 +1521,7 @@ static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc)
 
        buffer = omap3isp_video_buffer_next(&ccdc->video_out);
        if (buffer != NULL) {
-               ccdc_set_outaddr(ccdc, buffer->isp_addr);
+               ccdc_set_outaddr(ccdc, buffer->dma);
                restart = 1;
        }
 
@@ -1662,7 +1660,7 @@ static int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer)
        if (!(ccdc->output & CCDC_OUTPUT_MEMORY))
                return -ENODEV;
 
-       ccdc_set_outaddr(ccdc, buffer->isp_addr);
+       ccdc_set_outaddr(ccdc, buffer->dma);
 
        /* We now have a buffer queued on the output, restart the pipeline
         * on the next CCDC interrupt if running in continuous mode (or when
@@ -2580,8 +2578,9 @@ void omap3isp_ccdc_cleanup(struct isp_device *isp)
        cancel_work_sync(&ccdc->lsc.table_work);
        ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue);
 
-       if (ccdc->fpc.fpcaddr != 0)
-               omap_iommu_vfree(isp->domain, isp->dev, ccdc->fpc.fpcaddr);
+       if (ccdc->fpc.addr != NULL)
+               dma_free_coherent(isp->dev, ccdc->fpc.fpnum * 4, ccdc->fpc.addr,
+                                 ccdc->fpc.dma);
 
        mutex_destroy(&ccdc->ioctl_lock);
 }
index 9d24e4107864fc4905cbc13f8b2787634a27a24c..f65061602c71e585695503d30c4d9f340af1a27a 100644 (file)
@@ -46,6 +46,12 @@ enum ccdc_input_entity {
 
 #define        OMAP3ISP_CCDC_NEVENTS   16
 
+struct ispccdc_fpc {
+       void *addr;
+       dma_addr_t dma;
+       unsigned int fpnum;
+};
+
 enum ispccdc_lsc_state {
        LSC_STATE_STOPPED = 0,
        LSC_STATE_STOPPING = 1,
@@ -57,8 +63,12 @@ struct ispccdc_lsc_config_req {
        struct list_head list;
        struct omap3isp_ccdc_lsc_config config;
        unsigned char enable;
-       u32 table;
-       struct iovm_struct *iovm;
+
+       struct {
+               void *addr;
+               dma_addr_t dma;
+               struct sg_table sgt;
+       } table;
 };
 
 /*
@@ -136,7 +146,7 @@ struct isp_ccdc_device {
                     fpc_en:1;
        struct omap3isp_ccdc_blcomp blcomp;
        struct omap3isp_ccdc_bclamp clamp;
-       struct omap3isp_ccdc_fpc fpc;
+       struct ispccdc_fpc fpc;
        struct ispccdc_lsc lsc;
        unsigned int update;
        unsigned int shadow_update;
index b30b67d22a58cfb83990b589926be86c95d8c2bc..f3801db9095ca301bf53e564e9d9f4eb33a13327 100644 (file)
@@ -549,7 +549,7 @@ static void ccp2_isr_buffer(struct isp_ccp2_device *ccp2)
 
        buffer = omap3isp_video_buffer_next(&ccp2->video_in);
        if (buffer != NULL)
-               ccp2_set_inaddr(ccp2, buffer->isp_addr);
+               ccp2_set_inaddr(ccp2, buffer->dma);
 
        pipe->state |= ISP_PIPELINE_IDLE_INPUT;
 
@@ -940,7 +940,7 @@ static int ccp2_video_queue(struct isp_video *video, struct isp_buffer *buffer)
 {
        struct isp_ccp2_device *ccp2 = &video->isp->isp_ccp2;
 
-       ccp2_set_inaddr(ccp2, buffer->isp_addr);
+       ccp2_set_inaddr(ccp2, buffer->dma);
        return 0;
 }
 
index 620560828a48cc9d395bfba0ddbb2c7f017ed84b..5a2e47e58b846543f651d38acf5fba05012e2c3e 100644 (file)
@@ -695,7 +695,7 @@ static void csi2_isr_buffer(struct isp_csi2_device *csi2)
        if (buffer == NULL)
                return;
 
-       csi2_set_outaddr(csi2, buffer->isp_addr);
+       csi2_set_outaddr(csi2, buffer->dma);
        csi2_ctx_enable(isp, csi2, 0, 1);
 }
 
@@ -812,7 +812,7 @@ static int csi2_queue(struct isp_video *video, struct isp_buffer *buffer)
        struct isp_device *isp = video->isp;
        struct isp_csi2_device *csi2 = &isp->isp_csi2a;
 
-       csi2_set_outaddr(csi2, buffer->isp_addr);
+       csi2_set_outaddr(csi2, buffer->dma);
 
        /*
         * If streaming was enabled before there was a buffer queued
index 75fd82b152ba362f97bafef11a4e8bbffd444fb0..d6811ce263eb113102cceb7611a42e4c5f21f11d 100644 (file)
@@ -47,7 +47,7 @@ static void h3a_aewb_setup_regs(struct ispstat *aewb, void *priv)
        if (aewb->state == ISPSTAT_DISABLED)
                return;
 
-       isp_reg_writel(aewb->isp, aewb->active_buf->iommu_addr,
+       isp_reg_writel(aewb->isp, aewb->active_buf->dma_addr,
                       OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST);
 
        if (!aewb->update)
index a0bf5af32438559958b39c1d62360e35d491ad48..6fc960cd30f57accab300b749aa7049dcf5efd28 100644 (file)
@@ -51,7 +51,7 @@ static void h3a_af_setup_regs(struct ispstat *af, void *priv)
        if (af->state == ISPSTAT_DISABLED)
                return;
 
-       isp_reg_writel(af->isp, af->active_buf->iommu_addr, OMAP3_ISP_IOMEM_H3A,
+       isp_reg_writel(af->isp, af->active_buf->dma_addr, OMAP3_ISP_IOMEM_H3A,
                       ISPH3A_AFBUFST);
 
        if (!af->update)
index 395b2b068c7553eb27a544dab4953040563a7119..720809b07e75f4779f20da64aa1b0af498bad012 100644 (file)
@@ -1499,14 +1499,14 @@ static void preview_isr_buffer(struct isp_prev_device *prev)
        if (prev->input == PREVIEW_INPUT_MEMORY) {
                buffer = omap3isp_video_buffer_next(&prev->video_in);
                if (buffer != NULL)
-                       preview_set_inaddr(prev, buffer->isp_addr);
+                       preview_set_inaddr(prev, buffer->dma);
                pipe->state |= ISP_PIPELINE_IDLE_INPUT;
        }
 
        if (prev->output & PREVIEW_OUTPUT_MEMORY) {
                buffer = omap3isp_video_buffer_next(&prev->video_out);
                if (buffer != NULL) {
-                       preview_set_outaddr(prev, buffer->isp_addr);
+                       preview_set_outaddr(prev, buffer->dma);
                        restart = 1;
                }
                pipe->state |= ISP_PIPELINE_IDLE_OUTPUT;
@@ -1577,10 +1577,10 @@ static int preview_video_queue(struct isp_video *video,
        struct isp_prev_device *prev = &video->isp->isp_prev;
 
        if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
-               preview_set_inaddr(prev, buffer->isp_addr);
+               preview_set_inaddr(prev, buffer->dma);
 
        if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               preview_set_outaddr(prev, buffer->isp_addr);
+               preview_set_outaddr(prev, buffer->dma);
 
        return 0;
 }
diff --git a/drivers/media/platform/omap3isp/ispqueue.c b/drivers/media/platform/omap3isp/ispqueue.c
deleted file mode 100644 (file)
index a5e6585..0000000
+++ /dev/null
@@ -1,1161 +0,0 @@
-/*
- * ispqueue.c
- *
- * TI OMAP3 ISP - Video buffers queue handling
- *
- * Copyright (C) 2010 Nokia Corporation
- *
- * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- *          Sakari Ailus <sakari.ailus@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <asm/cacheflush.h>
-#include <linux/dma-mapping.h>
-#include <linux/mm.h>
-#include <linux/pagemap.h>
-#include <linux/poll.h>
-#include <linux/scatterlist.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-
-#include "ispqueue.h"
-
-/* -----------------------------------------------------------------------------
- * Video buffers management
- */
-
-/*
- * isp_video_buffer_cache_sync - Keep the buffers coherent between CPU and ISP
- *
- * The typical operation required here is Cache Invalidation across
- * the (user space) buffer address range. And this _must_ be done
- * at QBUF stage (and *only* at QBUF).
- *
- * We try to use optimal cache invalidation function:
- * - dmac_map_area:
- *    - used when the number of pages are _low_.
- *    - it becomes quite slow as the number of pages increase.
- *       - for 648x492 viewfinder (150 pages) it takes 1.3 ms.
- *       - for 5 Mpix buffer (2491 pages) it takes between 25-50 ms.
- *
- * - flush_cache_all:
- *    - used when the number of pages are _high_.
- *    - time taken in the range of 500-900 us.
- *    - has a higher penalty but, as whole dcache + icache is invalidated
- */
-/*
- * FIXME: dmac_inv_range crashes randomly on the user space buffer
- *        address. Fall back to flush_cache_all for now.
- */
-#define ISP_CACHE_FLUSH_PAGES_MAX       0
-
-static void isp_video_buffer_cache_sync(struct isp_video_buffer *buf)
-{
-       if (buf->skip_cache)
-               return;
-
-       if (buf->vbuf.m.userptr == 0 || buf->npages == 0 ||
-           buf->npages > ISP_CACHE_FLUSH_PAGES_MAX)
-               flush_cache_all();
-       else {
-               dmac_map_area((void *)buf->vbuf.m.userptr, buf->vbuf.length,
-                             DMA_FROM_DEVICE);
-               outer_inv_range(buf->vbuf.m.userptr,
-                               buf->vbuf.m.userptr + buf->vbuf.length);
-       }
-}
-
-/*
- * isp_video_buffer_lock_vma - Prevent VMAs from being unmapped
- *
- * Lock the VMAs underlying the given buffer into memory. This avoids the
- * userspace buffer mapping from being swapped out, making VIPT cache handling
- * easier.
- *
- * Note that the pages will not be freed as the buffers have been locked to
- * memory using by a call to get_user_pages(), but the userspace mapping could
- * still disappear if the VMAs are not locked. This is caused by the memory
- * management code trying to be as lock-less as possible, which results in the
- * userspace mapping manager not finding out that the pages are locked under
- * some conditions.
- */
-static int isp_video_buffer_lock_vma(struct isp_video_buffer *buf, int lock)
-{
-       struct vm_area_struct *vma;
-       unsigned long start;
-       unsigned long end;
-       int ret = 0;
-
-       if (buf->vbuf.memory == V4L2_MEMORY_MMAP)
-               return 0;
-
-       /* We can be called from workqueue context if the current task dies to
-        * unlock the VMAs. In that case there's no current memory management
-        * context so unlocking can't be performed, but the VMAs have been or
-        * are getting destroyed anyway so it doesn't really matter.
-        */
-       if (!current || !current->mm)
-               return lock ? -EINVAL : 0;
-
-       start = buf->vbuf.m.userptr;
-       end = buf->vbuf.m.userptr + buf->vbuf.length - 1;
-
-       down_write(&current->mm->mmap_sem);
-       spin_lock(&current->mm->page_table_lock);
-
-       do {
-               vma = find_vma(current->mm, start);
-               if (vma == NULL) {
-                       ret = -EFAULT;
-                       goto out;
-               }
-
-               if (lock)
-                       vma->vm_flags |= VM_LOCKED;
-               else
-                       vma->vm_flags &= ~VM_LOCKED;
-
-               start = vma->vm_end + 1;
-       } while (vma->vm_end < end);
-
-       if (lock)
-               buf->vm_flags |= VM_LOCKED;
-       else
-               buf->vm_flags &= ~VM_LOCKED;
-
-out:
-       spin_unlock(&current->mm->page_table_lock);
-       up_write(&current->mm->mmap_sem);
-       return ret;
-}
-
-/*
- * isp_video_buffer_sglist_kernel - Build a scatter list for a vmalloc'ed buffer
- *
- * Iterate over the vmalloc'ed area and create a scatter list entry for every
- * page.
- */
-static int isp_video_buffer_sglist_kernel(struct isp_video_buffer *buf)
-{
-       struct scatterlist *sglist;
-       unsigned int npages;
-       unsigned int i;
-       void *addr;
-
-       addr = buf->vaddr;
-       npages = PAGE_ALIGN(buf->vbuf.length) >> PAGE_SHIFT;
-
-       sglist = vmalloc(npages * sizeof(*sglist));
-       if (sglist == NULL)
-               return -ENOMEM;
-
-       sg_init_table(sglist, npages);
-
-       for (i = 0; i < npages; ++i, addr += PAGE_SIZE) {
-               struct page *page = vmalloc_to_page(addr);
-
-               if (page == NULL || PageHighMem(page)) {
-                       vfree(sglist);
-                       return -EINVAL;
-               }
-
-               sg_set_page(&sglist[i], page, PAGE_SIZE, 0);
-       }
-
-       buf->sglen = npages;
-       buf->sglist = sglist;
-
-       return 0;
-}
-
-/*
- * isp_video_buffer_sglist_user - Build a scatter list for a userspace buffer
- *
- * Walk the buffer pages list and create a 1:1 mapping to a scatter list.
- */
-static int isp_video_buffer_sglist_user(struct isp_video_buffer *buf)
-{
-       struct scatterlist *sglist;
-       unsigned int offset = buf->offset;
-       unsigned int i;
-
-       sglist = vmalloc(buf->npages * sizeof(*sglist));
-       if (sglist == NULL)
-               return -ENOMEM;
-
-       sg_init_table(sglist, buf->npages);
-
-       for (i = 0; i < buf->npages; ++i) {
-               if (PageHighMem(buf->pages[i])) {
-                       vfree(sglist);
-                       return -EINVAL;
-               }
-
-               sg_set_page(&sglist[i], buf->pages[i], PAGE_SIZE - offset,
-                           offset);
-               offset = 0;
-       }
-
-       buf->sglen = buf->npages;
-       buf->sglist = sglist;
-
-       return 0;
-}
-
-/*
- * isp_video_buffer_sglist_pfnmap - Build a scatter list for a VM_PFNMAP buffer
- *
- * Create a scatter list of physically contiguous pages starting at the buffer
- * memory physical address.
- */
-static int isp_video_buffer_sglist_pfnmap(struct isp_video_buffer *buf)
-{
-       struct scatterlist *sglist;
-       unsigned int offset = buf->offset;
-       unsigned long pfn = buf->paddr >> PAGE_SHIFT;
-       unsigned int i;
-
-       sglist = vmalloc(buf->npages * sizeof(*sglist));
-       if (sglist == NULL)
-               return -ENOMEM;
-
-       sg_init_table(sglist, buf->npages);
-
-       for (i = 0; i < buf->npages; ++i, ++pfn) {
-               sg_set_page(&sglist[i], pfn_to_page(pfn), PAGE_SIZE - offset,
-                           offset);
-               /* PFNMAP buffers will not get DMA-mapped, set the DMA address
-                * manually.
-                */
-               sg_dma_address(&sglist[i]) = (pfn << PAGE_SHIFT) + offset;
-               offset = 0;
-       }
-
-       buf->sglen = buf->npages;
-       buf->sglist = sglist;
-
-       return 0;
-}
-
-/*
- * isp_video_buffer_cleanup - Release pages for a userspace VMA.
- *
- * Release pages locked by a call isp_video_buffer_prepare_user and free the
- * pages table.
- */
-static void isp_video_buffer_cleanup(struct isp_video_buffer *buf)
-{
-       enum dma_data_direction direction;
-       unsigned int i;
-
-       if (buf->queue->ops->buffer_cleanup)
-               buf->queue->ops->buffer_cleanup(buf);
-
-       if (!(buf->vm_flags & VM_PFNMAP)) {
-               direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE
-                         ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
-               dma_unmap_sg(buf->queue->dev, buf->sglist, buf->sglen,
-                            direction);
-       }
-
-       vfree(buf->sglist);
-       buf->sglist = NULL;
-       buf->sglen = 0;
-
-       if (buf->pages != NULL) {
-               isp_video_buffer_lock_vma(buf, 0);
-
-               for (i = 0; i < buf->npages; ++i)
-                       page_cache_release(buf->pages[i]);
-
-               vfree(buf->pages);
-               buf->pages = NULL;
-       }
-
-       buf->npages = 0;
-       buf->skip_cache = false;
-}
-
-/*
- * isp_video_buffer_prepare_user - Pin userspace VMA pages to memory.
- *
- * This function creates a list of pages for a userspace VMA. The number of
- * pages is first computed based on the buffer size, and pages are then
- * retrieved by a call to get_user_pages.
- *
- * Pages are pinned to memory by get_user_pages, making them available for DMA
- * transfers. However, due to memory management optimization, it seems the
- * get_user_pages doesn't guarantee that the pinned pages will not be written
- * to swap and removed from the userspace mapping(s). When this happens, a page
- * fault can be generated when accessing those unmapped pages.
- *
- * If the fault is triggered by a page table walk caused by VIPT cache
- * management operations, the page fault handler might oops if the MM semaphore
- * is held, as it can't handle kernel page faults in that case. To fix that, a
- * fixup entry needs to be added to the cache management code, or the userspace
- * VMA must be locked to avoid removing pages from the userspace mapping in the
- * first place.
- *
- * If the number of pages retrieved is smaller than the number required by the
- * buffer size, the function returns -EFAULT.
- */
-static int isp_video_buffer_prepare_user(struct isp_video_buffer *buf)
-{
-       unsigned long data;
-       unsigned int first;
-       unsigned int last;
-       int ret;
-
-       data = buf->vbuf.m.userptr;
-       first = (data & PAGE_MASK) >> PAGE_SHIFT;
-       last = ((data + buf->vbuf.length - 1) & PAGE_MASK) >> PAGE_SHIFT;
-
-       buf->offset = data & ~PAGE_MASK;
-       buf->npages = last - first + 1;
-       buf->pages = vmalloc(buf->npages * sizeof(buf->pages[0]));
-       if (buf->pages == NULL)
-               return -ENOMEM;
-
-       down_read(&current->mm->mmap_sem);
-       ret = get_user_pages(current, current->mm, data & PAGE_MASK,
-                            buf->npages,
-                            buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,
-                            buf->pages, NULL);
-       up_read(&current->mm->mmap_sem);
-
-       if (ret != buf->npages) {
-               buf->npages = ret < 0 ? 0 : ret;
-               isp_video_buffer_cleanup(buf);
-               return -EFAULT;
-       }
-
-       ret = isp_video_buffer_lock_vma(buf, 1);
-       if (ret < 0)
-               isp_video_buffer_cleanup(buf);
-
-       return ret;
-}
-
-/*
- * isp_video_buffer_prepare_pfnmap - Validate a VM_PFNMAP userspace buffer
- *
- * Userspace VM_PFNMAP buffers are supported only if they are contiguous in
- * memory and if they span a single VMA.
- *
- * Return 0 if the buffer is valid, or -EFAULT otherwise.
- */
-static int isp_video_buffer_prepare_pfnmap(struct isp_video_buffer *buf)
-{
-       struct vm_area_struct *vma;
-       unsigned long prev_pfn;
-       unsigned long this_pfn;
-       unsigned long start;
-       unsigned long end;
-       dma_addr_t pa = 0;
-       int ret = -EFAULT;
-
-       start = buf->vbuf.m.userptr;
-       end = buf->vbuf.m.userptr + buf->vbuf.length - 1;
-
-       buf->offset = start & ~PAGE_MASK;
-       buf->npages = (end >> PAGE_SHIFT) - (start >> PAGE_SHIFT) + 1;
-       buf->pages = NULL;
-
-       down_read(&current->mm->mmap_sem);
-       vma = find_vma(current->mm, start);
-       if (vma == NULL || vma->vm_end < end)
-               goto done;
-
-       for (prev_pfn = 0; start <= end; start += PAGE_SIZE) {
-               ret = follow_pfn(vma, start, &this_pfn);
-               if (ret)
-                       goto done;
-
-               if (prev_pfn == 0)
-                       pa = this_pfn << PAGE_SHIFT;
-               else if (this_pfn != prev_pfn + 1) {
-                       ret = -EFAULT;
-                       goto done;
-               }
-
-               prev_pfn = this_pfn;
-       }
-
-       buf->paddr = pa + buf->offset;
-       ret = 0;
-
-done:
-       up_read(&current->mm->mmap_sem);
-       return ret;
-}
-
-/*
- * isp_video_buffer_prepare_vm_flags - Get VMA flags for a userspace address
- *
- * This function locates the VMAs for the buffer's userspace address and checks
- * that their flags match. The only flag that we need to care for at the moment
- * is VM_PFNMAP.
- *
- * The buffer vm_flags field is set to the first VMA flags.
- *
- * Return -EFAULT if no VMA can be found for part of the buffer, or if the VMAs
- * have incompatible flags.
- */
-static int isp_video_buffer_prepare_vm_flags(struct isp_video_buffer *buf)
-{
-       struct vm_area_struct *vma;
-       pgprot_t uninitialized_var(vm_page_prot);
-       unsigned long start;
-       unsigned long end;
-       int ret = -EFAULT;
-
-       start = buf->vbuf.m.userptr;
-       end = buf->vbuf.m.userptr + buf->vbuf.length - 1;
-
-       down_read(&current->mm->mmap_sem);
-
-       do {
-               vma = find_vma(current->mm, start);
-               if (vma == NULL)
-                       goto done;
-
-               if (start == buf->vbuf.m.userptr) {
-                       buf->vm_flags = vma->vm_flags;
-                       vm_page_prot = vma->vm_page_prot;
-               }
-
-               if ((buf->vm_flags ^ vma->vm_flags) & VM_PFNMAP)
-                       goto done;
-
-               if (vm_page_prot != vma->vm_page_prot)
-                       goto done;
-
-               start = vma->vm_end + 1;
-       } while (vma->vm_end < end);
-
-       /* Skip cache management to enhance performances for non-cached or
-        * write-combining buffers.
-        */
-       if (vm_page_prot == pgprot_noncached(vm_page_prot) ||
-           vm_page_prot == pgprot_writecombine(vm_page_prot))
-               buf->skip_cache = true;
-
-       ret = 0;
-
-done:
-       up_read(&current->mm->mmap_sem);
-       return ret;
-}
-
-/*
- * isp_video_buffer_prepare - Make a buffer ready for operation
- *
- * Preparing a buffer involves:
- *
- * - validating VMAs (userspace buffers only)
- * - locking pages and VMAs into memory (userspace buffers only)
- * - building page and scatter-gather lists
- * - mapping buffers for DMA operation
- * - performing driver-specific preparation
- *
- * The function must be called in userspace context with a valid mm context
- * (this excludes cleanup paths such as sys_close when the userspace process
- * segfaults).
- */
-static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
-{
-       enum dma_data_direction direction;
-       int ret;
-
-       switch (buf->vbuf.memory) {
-       case V4L2_MEMORY_MMAP:
-               ret = isp_video_buffer_sglist_kernel(buf);
-               break;
-
-       case V4L2_MEMORY_USERPTR:
-               ret = isp_video_buffer_prepare_vm_flags(buf);
-               if (ret < 0)
-                       return ret;
-
-               if (buf->vm_flags & VM_PFNMAP) {
-                       ret = isp_video_buffer_prepare_pfnmap(buf);
-                       if (ret < 0)
-                               return ret;
-
-                       ret = isp_video_buffer_sglist_pfnmap(buf);
-               } else {
-                       ret = isp_video_buffer_prepare_user(buf);
-                       if (ret < 0)
-                               return ret;
-
-                       ret = isp_video_buffer_sglist_user(buf);
-               }
-               break;
-
-       default:
-               return -EINVAL;
-       }
-
-       if (ret < 0)
-               goto done;
-
-       if (!(buf->vm_flags & VM_PFNMAP)) {
-               direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE
-                         ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
-               ret = dma_map_sg(buf->queue->dev, buf->sglist, buf->sglen,
-                                direction);
-               if (ret != buf->sglen) {
-                       ret = -EFAULT;
-                       goto done;
-               }
-       }
-
-       if (buf->queue->ops->buffer_prepare)
-               ret = buf->queue->ops->buffer_prepare(buf);
-
-done:
-       if (ret < 0) {
-               isp_video_buffer_cleanup(buf);
-               return ret;
-       }
-
-       return ret;
-}
-
-/*
- * isp_video_queue_query - Query the status of a given buffer
- *
- * Locking: must be called with the queue lock held.
- */
-static void isp_video_buffer_query(struct isp_video_buffer *buf,
-                                  struct v4l2_buffer *vbuf)
-{
-       memcpy(vbuf, &buf->vbuf, sizeof(*vbuf));
-
-       if (buf->vma_use_count)
-               vbuf->flags |= V4L2_BUF_FLAG_MAPPED;
-
-       switch (buf->state) {
-       case ISP_BUF_STATE_ERROR:
-               vbuf->flags |= V4L2_BUF_FLAG_ERROR;
-               /* Fallthrough */
-       case ISP_BUF_STATE_DONE:
-               vbuf->flags |= V4L2_BUF_FLAG_DONE;
-               break;
-       case ISP_BUF_STATE_QUEUED:
-       case ISP_BUF_STATE_ACTIVE:
-               vbuf->flags |= V4L2_BUF_FLAG_QUEUED;
-               break;
-       case ISP_BUF_STATE_IDLE:
-       default:
-               break;
-       }
-}
-
-/*
- * isp_video_buffer_wait - Wait for a buffer to be ready
- *
- * In non-blocking mode, return immediately with 0 if the buffer is ready or
- * -EAGAIN if the buffer is in the QUEUED or ACTIVE state.
- *
- * In blocking mode, wait (interruptibly but with no timeout) on the buffer wait
- * queue using the same condition.
- */
-static int isp_video_buffer_wait(struct isp_video_buffer *buf, int nonblocking)
-{
-       if (nonblocking) {
-               return (buf->state != ISP_BUF_STATE_QUEUED &&
-                       buf->state != ISP_BUF_STATE_ACTIVE)
-                       ? 0 : -EAGAIN;
-       }
-
-       return wait_event_interruptible(buf->wait,
-               buf->state != ISP_BUF_STATE_QUEUED &&
-               buf->state != ISP_BUF_STATE_ACTIVE);
-}
-
-/* -----------------------------------------------------------------------------
- * Queue management
- */
-
-/*
- * isp_video_queue_free - Free video buffers memory
- *
- * Buffers can only be freed if the queue isn't streaming and if no buffer is
- * mapped to userspace. Return -EBUSY if those conditions aren't satisfied.
- *
- * This function must be called with the queue lock held.
- */
-static int isp_video_queue_free(struct isp_video_queue *queue)
-{
-       unsigned int i;
-
-       if (queue->streaming)
-               return -EBUSY;
-
-       for (i = 0; i < queue->count; ++i) {
-               if (queue->buffers[i]->vma_use_count != 0)
-                       return -EBUSY;
-       }
-
-       for (i = 0; i < queue->count; ++i) {
-               struct isp_video_buffer *buf = queue->buffers[i];
-
-               isp_video_buffer_cleanup(buf);
-
-               vfree(buf->vaddr);
-               buf->vaddr = NULL;
-
-               kfree(buf);
-               queue->buffers[i] = NULL;
-       }
-
-       INIT_LIST_HEAD(&queue->queue);
-       queue->count = 0;
-       return 0;
-}
-
-/*
- * isp_video_queue_alloc - Allocate video buffers memory
- *
- * This function must be called with the queue lock held.
- */
-static int isp_video_queue_alloc(struct isp_video_queue *queue,
-                                unsigned int nbuffers,
-                                unsigned int size, enum v4l2_memory memory)
-{
-       struct isp_video_buffer *buf;
-       unsigned int i;
-       void *mem;
-       int ret;
-
-       /* Start by freeing the buffers. */
-       ret = isp_video_queue_free(queue);
-       if (ret < 0)
-               return ret;
-
-       /* Bail out if no buffers should be allocated. */
-       if (nbuffers == 0)
-               return 0;
-
-       /* Initialize the allocated buffers. */
-       for (i = 0; i < nbuffers; ++i) {
-               buf = kzalloc(queue->bufsize, GFP_KERNEL);
-               if (buf == NULL)
-                       break;
-
-               if (memory == V4L2_MEMORY_MMAP) {
-                       /* Allocate video buffers memory for mmap mode. Align
-                        * the size to the page size.
-                        */
-                       mem = vmalloc_32_user(PAGE_ALIGN(size));
-                       if (mem == NULL) {
-                               kfree(buf);
-                               break;
-                       }
-
-                       buf->vbuf.m.offset = i * PAGE_ALIGN(size);
-                       buf->vaddr = mem;
-               }
-
-               buf->vbuf.index = i;
-               buf->vbuf.length = size;
-               buf->vbuf.type = queue->type;
-               buf->vbuf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
-               buf->vbuf.field = V4L2_FIELD_NONE;
-               buf->vbuf.memory = memory;
-
-               buf->queue = queue;
-               init_waitqueue_head(&buf->wait);
-
-               queue->buffers[i] = buf;
-       }
-
-       if (i == 0)
-               return -ENOMEM;
-
-       queue->count = i;
-       return nbuffers;
-}
-
-/**
- * omap3isp_video_queue_cleanup - Clean up the video buffers queue
- * @queue: Video buffers queue
- *
- * Free all allocated resources and clean up the video buffers queue. The queue
- * must not be busy (no ongoing video stream) and buffers must have been
- * unmapped.
- *
- * Return 0 on success or -EBUSY if the queue is busy or buffers haven't been
- * unmapped.
- */
-int omap3isp_video_queue_cleanup(struct isp_video_queue *queue)
-{
-       return isp_video_queue_free(queue);
-}
-
-/**
- * omap3isp_video_queue_init - Initialize the video buffers queue
- * @queue: Video buffers queue
- * @type: V4L2 buffer type (capture or output)
- * @ops: Driver-specific queue operations
- * @dev: Device used for DMA operations
- * @bufsize: Size of the driver-specific buffer structure
- *
- * Initialize the video buffers queue with the supplied parameters.
- *
- * The queue type must be one of V4L2_BUF_TYPE_VIDEO_CAPTURE or
- * V4L2_BUF_TYPE_VIDEO_OUTPUT. Other buffer types are not supported yet.
- *
- * Buffer objects will be allocated using the given buffer size to allow room
- * for driver-specific fields. Driver-specific buffer structures must start
- * with a struct isp_video_buffer field. Drivers with no driver-specific buffer
- * structure must pass the size of the isp_video_buffer structure in the bufsize
- * parameter.
- *
- * Return 0 on success.
- */
-int omap3isp_video_queue_init(struct isp_video_queue *queue,
-                             enum v4l2_buf_type type,
-                             const struct isp_video_queue_operations *ops,
-                             struct device *dev, unsigned int bufsize)
-{
-       INIT_LIST_HEAD(&queue->queue);
-       mutex_init(&queue->lock);
-       spin_lock_init(&queue->irqlock);
-
-       queue->type = type;
-       queue->ops = ops;
-       queue->dev = dev;
-       queue->bufsize = bufsize;
-
-       return 0;
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 operations
- */
-
-/**
- * omap3isp_video_queue_reqbufs - Allocate video buffers memory
- *
- * This function is intended to be used as a VIDIOC_REQBUFS ioctl handler. It
- * allocated video buffer objects and, for MMAP buffers, buffer memory.
- *
- * If the number of buffers is 0, all buffers are freed and the function returns
- * without performing any allocation.
- *
- * If the number of buffers is not 0, currently allocated buffers (if any) are
- * freed and the requested number of buffers are allocated. Depending on
- * driver-specific requirements and on memory availability, a number of buffer
- * smaller or bigger than requested can be allocated. This isn't considered as
- * an error.
- *
- * Return 0 on success or one of the following error codes:
- *
- * -EINVAL if the buffer type or index are invalid
- * -EBUSY if the queue is busy (streaming or buffers mapped)
- * -ENOMEM if the buffers can't be allocated due to an out-of-memory condition
- */
-int omap3isp_video_queue_reqbufs(struct isp_video_queue *queue,
-                                struct v4l2_requestbuffers *rb)
-{
-       unsigned int nbuffers = rb->count;
-       unsigned int size;
-       int ret;
-
-       if (rb->type != queue->type)
-               return -EINVAL;
-
-       queue->ops->queue_prepare(queue, &nbuffers, &size);
-       if (size == 0)
-               return -EINVAL;
-
-       nbuffers = min_t(unsigned int, nbuffers, ISP_VIDEO_MAX_BUFFERS);
-
-       mutex_lock(&queue->lock);
-
-       ret = isp_video_queue_alloc(queue, nbuffers, size, rb->memory);
-       if (ret < 0)
-               goto done;
-
-       rb->count = ret;
-       ret = 0;
-
-done:
-       mutex_unlock(&queue->lock);
-       return ret;
-}
-
-/**
- * omap3isp_video_queue_querybuf - Query the status of a buffer in a queue
- *
- * This function is intended to be used as a VIDIOC_QUERYBUF ioctl handler. It
- * returns the status of a given video buffer.
- *
- * Return 0 on success or -EINVAL if the buffer type or index are invalid.
- */
-int omap3isp_video_queue_querybuf(struct isp_video_queue *queue,
-                                 struct v4l2_buffer *vbuf)
-{
-       struct isp_video_buffer *buf;
-       int ret = 0;
-
-       if (vbuf->type != queue->type)
-               return -EINVAL;
-
-       mutex_lock(&queue->lock);
-
-       if (vbuf->index >= queue->count) {
-               ret = -EINVAL;
-               goto done;
-       }
-
-       buf = queue->buffers[vbuf->index];
-       isp_video_buffer_query(buf, vbuf);
-
-done:
-       mutex_unlock(&queue->lock);
-       return ret;
-}
-
-/**
- * omap3isp_video_queue_qbuf - Queue a buffer
- *
- * This function is intended to be used as a VIDIOC_QBUF ioctl handler.
- *
- * The v4l2_buffer structure passed from userspace is first sanity tested. If
- * sane, the buffer is then processed and added to the main queue and, if the
- * queue is streaming, to the IRQ queue.
- *
- * Before being enqueued, USERPTR buffers are checked for address changes. If
- * the buffer has a different userspace address, the old memory area is unlocked
- * and the new memory area is locked.
- */
-int omap3isp_video_queue_qbuf(struct isp_video_queue *queue,
-                             struct v4l2_buffer *vbuf)
-{
-       struct isp_video_buffer *buf;
-       unsigned long flags;
-       int ret = -EINVAL;
-
-       if (vbuf->type != queue->type)
-               goto done;
-
-       mutex_lock(&queue->lock);
-
-       if (vbuf->index >= queue->count)
-               goto done;
-
-       buf = queue->buffers[vbuf->index];
-
-       if (vbuf->memory != buf->vbuf.memory)
-               goto done;
-
-       if (buf->state != ISP_BUF_STATE_IDLE)
-               goto done;
-
-       if (vbuf->memory == V4L2_MEMORY_USERPTR &&
-           vbuf->length < buf->vbuf.length)
-               goto done;
-
-       if (vbuf->memory == V4L2_MEMORY_USERPTR &&
-           vbuf->m.userptr != buf->vbuf.m.userptr) {
-               isp_video_buffer_cleanup(buf);
-               buf->vbuf.m.userptr = vbuf->m.userptr;
-               buf->prepared = 0;
-       }
-
-       if (!buf->prepared) {
-               ret = isp_video_buffer_prepare(buf);
-               if (ret < 0)
-                       goto done;
-               buf->prepared = 1;
-       }
-
-       isp_video_buffer_cache_sync(buf);
-
-       buf->state = ISP_BUF_STATE_QUEUED;
-       list_add_tail(&buf->stream, &queue->queue);
-
-       if (queue->streaming) {
-               spin_lock_irqsave(&queue->irqlock, flags);
-               queue->ops->buffer_queue(buf);
-               spin_unlock_irqrestore(&queue->irqlock, flags);
-       }
-
-       ret = 0;
-
-done:
-       mutex_unlock(&queue->lock);
-       return ret;
-}
-
-/**
- * omap3isp_video_queue_dqbuf - Dequeue a buffer
- *
- * This function is intended to be used as a VIDIOC_DQBUF ioctl handler.
- *
- * Wait until a buffer is ready to be dequeued, remove it from the queue and
- * copy its information to the v4l2_buffer structure.
- *
- * If the nonblocking argument is not zero and no buffer is ready, return
- * -EAGAIN immediately instead of waiting.
- *
- * If no buffer has been enqueued, or if the requested buffer type doesn't match
- * the queue type, return -EINVAL.
- */
-int omap3isp_video_queue_dqbuf(struct isp_video_queue *queue,
-                              struct v4l2_buffer *vbuf, int nonblocking)
-{
-       struct isp_video_buffer *buf;
-       int ret;
-
-       if (vbuf->type != queue->type)
-               return -EINVAL;
-
-       mutex_lock(&queue->lock);
-
-       if (list_empty(&queue->queue)) {
-               ret = -EINVAL;
-               goto done;
-       }
-
-       buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream);
-       ret = isp_video_buffer_wait(buf, nonblocking);
-       if (ret < 0)
-               goto done;
-
-       list_del(&buf->stream);
-
-       isp_video_buffer_query(buf, vbuf);
-       buf->state = ISP_BUF_STATE_IDLE;
-       vbuf->flags &= ~V4L2_BUF_FLAG_QUEUED;
-
-done:
-       mutex_unlock(&queue->lock);
-       return ret;
-}
-
-/**
- * omap3isp_video_queue_streamon - Start streaming
- *
- * This function is intended to be used as a VIDIOC_STREAMON ioctl handler. It
- * starts streaming on the queue and calls the buffer_queue operation for all
- * queued buffers.
- *
- * Return 0 on success.
- */
-int omap3isp_video_queue_streamon(struct isp_video_queue *queue)
-{
-       struct isp_video_buffer *buf;
-       unsigned long flags;
-
-       mutex_lock(&queue->lock);
-
-       if (queue->streaming)
-               goto done;
-
-       queue->streaming = 1;
-
-       spin_lock_irqsave(&queue->irqlock, flags);
-       list_for_each_entry(buf, &queue->queue, stream)
-               queue->ops->buffer_queue(buf);
-       spin_unlock_irqrestore(&queue->irqlock, flags);
-
-done:
-       mutex_unlock(&queue->lock);
-       return 0;
-}
-
-/**
- * omap3isp_video_queue_streamoff - Stop streaming
- *
- * This function is intended to be used as a VIDIOC_STREAMOFF ioctl handler. It
- * stops streaming on the queue and wakes up all the buffers.
- *
- * Drivers must stop the hardware and synchronize with interrupt handlers and/or
- * delayed works before calling this function to make sure no buffer will be
- * touched by the driver and/or hardware.
- */
-void omap3isp_video_queue_streamoff(struct isp_video_queue *queue)
-{
-       struct isp_video_buffer *buf;
-       unsigned long flags;
-       unsigned int i;
-
-       mutex_lock(&queue->lock);
-
-       if (!queue->streaming)
-               goto done;
-
-       queue->streaming = 0;
-
-       spin_lock_irqsave(&queue->irqlock, flags);
-       for (i = 0; i < queue->count; ++i) {
-               buf = queue->buffers[i];
-
-               if (buf->state == ISP_BUF_STATE_ACTIVE)
-                       wake_up(&buf->wait);
-
-               buf->state = ISP_BUF_STATE_IDLE;
-       }
-       spin_unlock_irqrestore(&queue->irqlock, flags);
-
-       INIT_LIST_HEAD(&queue->queue);
-
-done:
-       mutex_unlock(&queue->lock);
-}
-
-/**
- * omap3isp_video_queue_discard_done - Discard all buffers marked as DONE
- *
- * This function is intended to be used with suspend/resume operations. It
- * discards all 'done' buffers as they would be too old to be requested after
- * resume.
- *
- * Drivers must stop the hardware and synchronize with interrupt handlers and/or
- * delayed works before calling this function to make sure no buffer will be
- * touched by the driver and/or hardware.
- */
-void omap3isp_video_queue_discard_done(struct isp_video_queue *queue)
-{
-       struct isp_video_buffer *buf;
-       unsigned int i;
-
-       mutex_lock(&queue->lock);
-
-       if (!queue->streaming)
-               goto done;
-
-       for (i = 0; i < queue->count; ++i) {
-               buf = queue->buffers[i];
-
-               if (buf->state == ISP_BUF_STATE_DONE)
-                       buf->state = ISP_BUF_STATE_ERROR;
-       }
-
-done:
-       mutex_unlock(&queue->lock);
-}
-
-static void isp_video_queue_vm_open(struct vm_area_struct *vma)
-{
-       struct isp_video_buffer *buf = vma->vm_private_data;
-
-       buf->vma_use_count++;
-}
-
-static void isp_video_queue_vm_close(struct vm_area_struct *vma)
-{
-       struct isp_video_buffer *buf = vma->vm_private_data;
-
-       buf->vma_use_count--;
-}
-
-static const struct vm_operations_struct isp_video_queue_vm_ops = {
-       .open = isp_video_queue_vm_open,
-       .close = isp_video_queue_vm_close,
-};
-
-/**
- * omap3isp_video_queue_mmap - Map buffers to userspace
- *
- * This function is intended to be used as an mmap() file operation handler. It
- * maps a buffer to userspace based on the VMA offset.
- *
- * Only buffers of memory type MMAP are supported.
- */
-int omap3isp_video_queue_mmap(struct isp_video_queue *queue,
-                        struct vm_area_struct *vma)
-{
-       struct isp_video_buffer *uninitialized_var(buf);
-       unsigned long size;
-       unsigned int i;
-       int ret = 0;
-
-       mutex_lock(&queue->lock);
-
-       for (i = 0; i < queue->count; ++i) {
-               buf = queue->buffers[i];
-               if ((buf->vbuf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
-                       break;
-       }
-
-       if (i == queue->count) {
-               ret = -EINVAL;
-               goto done;
-       }
-
-       size = vma->vm_end - vma->vm_start;
-
-       if (buf->vbuf.memory != V4L2_MEMORY_MMAP ||
-           size != PAGE_ALIGN(buf->vbuf.length)) {
-               ret = -EINVAL;
-               goto done;
-       }
-
-       ret = remap_vmalloc_range(vma, buf->vaddr, 0);
-       if (ret < 0)
-               goto done;
-
-       vma->vm_ops = &isp_video_queue_vm_ops;
-       vma->vm_private_data = buf;
-       isp_video_queue_vm_open(vma);
-
-done:
-       mutex_unlock(&queue->lock);
-       return ret;
-}
-
-/**
- * omap3isp_video_queue_poll - Poll video queue state
- *
- * This function is intended to be used as a poll() file operation handler. It
- * polls the state of the video buffer at the front of the queue and returns an
- * events mask.
- *
- * If no buffer is present at the front of the queue, POLLERR is returned.
- */
-unsigned int omap3isp_video_queue_poll(struct isp_video_queue *queue,
-                                      struct file *file, poll_table *wait)
-{
-       struct isp_video_buffer *buf;
-       unsigned int mask = 0;
-
-       mutex_lock(&queue->lock);
-       if (list_empty(&queue->queue)) {
-               mask |= POLLERR;
-               goto done;
-       }
-       buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream);
-
-       poll_wait(file, &buf->wait, wait);
-       if (buf->state == ISP_BUF_STATE_DONE ||
-           buf->state == ISP_BUF_STATE_ERROR) {
-               if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
-                       mask |= POLLIN | POLLRDNORM;
-               else
-                       mask |= POLLOUT | POLLWRNORM;
-       }
-
-done:
-       mutex_unlock(&queue->lock);
-       return mask;
-}
diff --git a/drivers/media/platform/omap3isp/ispqueue.h b/drivers/media/platform/omap3isp/ispqueue.h
deleted file mode 100644 (file)
index 3e048ad..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * ispqueue.h
- *
- * TI OMAP3 ISP - Video buffers queue handling
- *
- * Copyright (C) 2010 Nokia Corporation
- *
- * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- *          Sakari Ailus <sakari.ailus@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#ifndef OMAP3_ISP_QUEUE_H
-#define OMAP3_ISP_QUEUE_H
-
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/mm_types.h>
-#include <linux/mutex.h>
-#include <linux/videodev2.h>
-#include <linux/wait.h>
-
-struct isp_video_queue;
-struct page;
-struct scatterlist;
-
-#define ISP_VIDEO_MAX_BUFFERS          16
-
-/**
- * enum isp_video_buffer_state - ISP video buffer state
- * @ISP_BUF_STATE_IDLE:        The buffer is under userspace control (dequeued
- *     or not queued yet).
- * @ISP_BUF_STATE_QUEUED: The buffer has been queued but isn't used by the
- *     device yet.
- * @ISP_BUF_STATE_ACTIVE: The buffer is in use for an active video transfer.
- * @ISP_BUF_STATE_ERROR: The device is done with the buffer and an error
- *     occurred. For capture device the buffer likely contains corrupted data or
- *     no data at all.
- * @ISP_BUF_STATE_DONE: The device is done with the buffer and no error occurred.
- *     For capture devices the buffer contains valid data.
- */
-enum isp_video_buffer_state {
-       ISP_BUF_STATE_IDLE,
-       ISP_BUF_STATE_QUEUED,
-       ISP_BUF_STATE_ACTIVE,
-       ISP_BUF_STATE_ERROR,
-       ISP_BUF_STATE_DONE,
-};
-
-/**
- * struct isp_video_buffer - ISP video buffer
- * @vma_use_count: Number of times the buffer is mmap'ed to userspace
- * @stream: List head for insertion into main queue
- * @queue: ISP buffers queue this buffer belongs to
- * @prepared: Whether the buffer has been prepared
- * @skip_cache: Whether to skip cache management operations for this buffer
- * @vaddr: Memory virtual address (for kernel buffers)
- * @vm_flags: Buffer VMA flags (for userspace buffers)
- * @offset: Offset inside the first page (for userspace buffers)
- * @npages: Number of pages (for userspace buffers)
- * @pages: Pages table (for userspace non-VM_PFNMAP buffers)
- * @paddr: Memory physical address (for userspace VM_PFNMAP buffers)
- * @sglen: Number of elements in the scatter list (for non-VM_PFNMAP buffers)
- * @sglist: Scatter list (for non-VM_PFNMAP buffers)
- * @vbuf: V4L2 buffer
- * @irqlist: List head for insertion into IRQ queue
- * @state: Current buffer state
- * @wait: Wait queue to signal buffer completion
- */
-struct isp_video_buffer {
-       unsigned long vma_use_count;
-       struct list_head stream;
-       struct isp_video_queue *queue;
-       unsigned int prepared:1;
-       bool skip_cache;
-
-       /* For kernel buffers. */
-       void *vaddr;
-
-       /* For userspace buffers. */
-       vm_flags_t vm_flags;
-       unsigned long offset;
-       unsigned int npages;
-       struct page **pages;
-       dma_addr_t paddr;
-
-       /* For all buffers except VM_PFNMAP. */
-       unsigned int sglen;
-       struct scatterlist *sglist;
-
-       /* Touched by the interrupt handler. */
-       struct v4l2_buffer vbuf;
-       struct list_head irqlist;
-       enum isp_video_buffer_state state;
-       wait_queue_head_t wait;
-};
-
-#define to_isp_video_buffer(vb)        container_of(vb, struct isp_video_buffer, vb)
-
-/**
- * struct isp_video_queue_operations - Driver-specific operations
- * @queue_prepare: Called before allocating buffers. Drivers should clamp the
- *     number of buffers according to their requirements, and must return the
- *     buffer size in bytes.
- * @buffer_prepare: Called the first time a buffer is queued, or after changing
- *     the userspace memory address for a USERPTR buffer, with the queue lock
- *     held. Drivers should perform device-specific buffer preparation (such as
- *     mapping the buffer memory in an IOMMU). This operation is optional.
- * @buffer_queue: Called when a buffer is being added to the queue with the
- *     queue irqlock spinlock held.
- * @buffer_cleanup: Called before freeing buffers, or before changing the
- *     userspace memory address for a USERPTR buffer, with the queue lock held.
- *     Drivers must perform cleanup operations required to undo the
- *     buffer_prepare call. This operation is optional.
- */
-struct isp_video_queue_operations {
-       void (*queue_prepare)(struct isp_video_queue *queue,
-                             unsigned int *nbuffers, unsigned int *size);
-       int  (*buffer_prepare)(struct isp_video_buffer *buf);
-       void (*buffer_queue)(struct isp_video_buffer *buf);
-       void (*buffer_cleanup)(struct isp_video_buffer *buf);
-};
-
-/**
- * struct isp_video_queue - ISP video buffers queue
- * @type: Type of video buffers handled by this queue
- * @ops: Queue operations
- * @dev: Device used for DMA operations
- * @bufsize: Size of a driver-specific buffer object
- * @count: Number of currently allocated buffers
- * @buffers: ISP video buffers
- * @lock: Mutex to protect access to the buffers, main queue and state
- * @irqlock: Spinlock to protect access to the IRQ queue
- * @streaming: Queue state, indicates whether the queue is streaming
- * @queue: List of all queued buffers
- */
-struct isp_video_queue {
-       enum v4l2_buf_type type;
-       const struct isp_video_queue_operations *ops;
-       struct device *dev;
-       unsigned int bufsize;
-
-       unsigned int count;
-       struct isp_video_buffer *buffers[ISP_VIDEO_MAX_BUFFERS];
-       struct mutex lock;
-       spinlock_t irqlock;
-
-       unsigned int streaming:1;
-
-       struct list_head queue;
-};
-
-int omap3isp_video_queue_cleanup(struct isp_video_queue *queue);
-int omap3isp_video_queue_init(struct isp_video_queue *queue,
-                             enum v4l2_buf_type type,
-                             const struct isp_video_queue_operations *ops,
-                             struct device *dev, unsigned int bufsize);
-
-int omap3isp_video_queue_reqbufs(struct isp_video_queue *queue,
-                                struct v4l2_requestbuffers *rb);
-int omap3isp_video_queue_querybuf(struct isp_video_queue *queue,
-                                 struct v4l2_buffer *vbuf);
-int omap3isp_video_queue_qbuf(struct isp_video_queue *queue,
-                             struct v4l2_buffer *vbuf);
-int omap3isp_video_queue_dqbuf(struct isp_video_queue *queue,
-                              struct v4l2_buffer *vbuf, int nonblocking);
-int omap3isp_video_queue_streamon(struct isp_video_queue *queue);
-void omap3isp_video_queue_streamoff(struct isp_video_queue *queue);
-void omap3isp_video_queue_discard_done(struct isp_video_queue *queue);
-int omap3isp_video_queue_mmap(struct isp_video_queue *queue,
-                             struct vm_area_struct *vma);
-unsigned int omap3isp_video_queue_poll(struct isp_video_queue *queue,
-                                      struct file *file, poll_table *wait);
-
-#endif /* OMAP3_ISP_QUEUE_H */
index 86369df81d7481fbcdece03d1653baa3fe8b6d43..6f077c2377db6a7b785f1f8a4b688c2996c0d2b1 100644 (file)
@@ -1040,7 +1040,7 @@ static void resizer_isr_buffer(struct isp_res_device *res)
         */
        buffer = omap3isp_video_buffer_next(&res->video_out);
        if (buffer != NULL) {
-               resizer_set_outaddr(res, buffer->isp_addr);
+               resizer_set_outaddr(res, buffer->dma);
                restart = 1;
        }
 
@@ -1049,7 +1049,7 @@ static void resizer_isr_buffer(struct isp_res_device *res)
        if (res->input == RESIZER_INPUT_MEMORY) {
                buffer = omap3isp_video_buffer_next(&res->video_in);
                if (buffer != NULL)
-                       resizer_set_inaddr(res, buffer->isp_addr);
+                       resizer_set_inaddr(res, buffer->dma);
                pipe->state |= ISP_PIPELINE_IDLE_INPUT;
        }
 
@@ -1101,7 +1101,7 @@ static int resizer_video_queue(struct isp_video *video,
        struct isp_res_device *res = &video->isp->isp_res;
 
        if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
-               resizer_set_inaddr(res, buffer->isp_addr);
+               resizer_set_inaddr(res, buffer->dma);
 
        /*
         * We now have a buffer queued on the output. Despite what the
@@ -1116,7 +1116,7 @@ static int resizer_video_queue(struct isp_video *video,
         * continuous mode or when starting the stream.
         */
        if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               resizer_set_outaddr(res, buffer->isp_addr);
+               resizer_set_outaddr(res, buffer->dma);
 
        return 0;
 }
index 5707f85c4cc4cba883e6107e892ba864fe8bc4f5..e6cbc1eaf4cab03fa5c29ab1ffba1de8d7bb6cec 100644 (file)
  */
 
 #include <linux/dma-mapping.h>
-#include <linux/omap-iommu.h>
 #include <linux/slab.h>
 #include <linux/uaccess.h>
 
 #include "isp.h"
 
-#define IS_COHERENT_BUF(stat)  ((stat)->dma_ch >= 0)
+#define ISP_STAT_USES_DMAENGINE(stat)  ((stat)->dma_ch >= 0)
 
 /*
  * MAGIC_SIZE must always be the greatest common divisor of
@@ -77,21 +76,10 @@ static void __isp_stat_buf_sync_magic(struct ispstat *stat,
                                        dma_addr_t, unsigned long, size_t,
                                        enum dma_data_direction))
 {
-       struct device *dev = stat->isp->dev;
-       struct page *pg;
-       dma_addr_t dma_addr;
-       u32 offset;
-
-       /* Initial magic words */
-       pg = vmalloc_to_page(buf->virt_addr);
-       dma_addr = pfn_to_dma(dev, page_to_pfn(pg));
-       dma_sync(dev, dma_addr, 0, MAGIC_SIZE, dir);
-
-       /* Final magic words */
-       pg = vmalloc_to_page(buf->virt_addr + buf_size);
-       dma_addr = pfn_to_dma(dev, page_to_pfn(pg));
-       offset = ((u32)buf->virt_addr + buf_size) & ~PAGE_MASK;
-       dma_sync(dev, dma_addr, offset, MAGIC_SIZE, dir);
+       /* Sync the initial and final magic words. */
+       dma_sync(stat->isp->dev, buf->dma_addr, 0, MAGIC_SIZE, dir);
+       dma_sync(stat->isp->dev, buf->dma_addr + (buf_size & PAGE_MASK),
+                buf_size & ~PAGE_MASK, MAGIC_SIZE, dir);
 }
 
 static void isp_stat_buf_sync_magic_for_device(struct ispstat *stat,
@@ -99,7 +87,7 @@ static void isp_stat_buf_sync_magic_for_device(struct ispstat *stat,
                                               u32 buf_size,
                                               enum dma_data_direction dir)
 {
-       if (IS_COHERENT_BUF(stat))
+       if (ISP_STAT_USES_DMAENGINE(stat))
                return;
 
        __isp_stat_buf_sync_magic(stat, buf, buf_size, dir,
@@ -111,7 +99,7 @@ static void isp_stat_buf_sync_magic_for_cpu(struct ispstat *stat,
                                            u32 buf_size,
                                            enum dma_data_direction dir)
 {
-       if (IS_COHERENT_BUF(stat))
+       if (ISP_STAT_USES_DMAENGINE(stat))
                return;
 
        __isp_stat_buf_sync_magic(stat, buf, buf_size, dir,
@@ -180,21 +168,21 @@ static void isp_stat_buf_insert_magic(struct ispstat *stat,
 static void isp_stat_buf_sync_for_device(struct ispstat *stat,
                                         struct ispstat_buffer *buf)
 {
-       if (IS_COHERENT_BUF(stat))
+       if (ISP_STAT_USES_DMAENGINE(stat))
                return;
 
-       dma_sync_sg_for_device(stat->isp->dev, buf->iovm->sgt->sgl,
-                              buf->iovm->sgt->nents, DMA_FROM_DEVICE);
+       dma_sync_sg_for_device(stat->isp->dev, buf->sgt.sgl,
+                              buf->sgt.nents, DMA_FROM_DEVICE);
 }
 
 static void isp_stat_buf_sync_for_cpu(struct ispstat *stat,
                                      struct ispstat_buffer *buf)
 {
-       if (IS_COHERENT_BUF(stat))
+       if (ISP_STAT_USES_DMAENGINE(stat))
                return;
 
-       dma_sync_sg_for_cpu(stat->isp->dev, buf->iovm->sgt->sgl,
-                           buf->iovm->sgt->nents, DMA_FROM_DEVICE);
+       dma_sync_sg_for_cpu(stat->isp->dev, buf->sgt.sgl,
+                           buf->sgt.nents, DMA_FROM_DEVICE);
 }
 
 static void isp_stat_buf_clear(struct ispstat *stat)
@@ -354,29 +342,21 @@ static struct ispstat_buffer *isp_stat_buf_get(struct ispstat *stat,
 
 static void isp_stat_bufs_free(struct ispstat *stat)
 {
-       struct isp_device *isp = stat->isp;
-       int i;
+       struct device *dev = ISP_STAT_USES_DMAENGINE(stat)
+                          ? NULL : stat->isp->dev;
+       unsigned int i;
 
        for (i = 0; i < STAT_MAX_BUFS; i++) {
                struct ispstat_buffer *buf = &stat->buf[i];
 
-               if (!IS_COHERENT_BUF(stat)) {
-                       if (IS_ERR_OR_NULL((void *)buf->iommu_addr))
-                               continue;
-                       if (buf->iovm)
-                               dma_unmap_sg(isp->dev, buf->iovm->sgt->sgl,
-                                            buf->iovm->sgt->nents,
-                                            DMA_FROM_DEVICE);
-                       omap_iommu_vfree(isp->domain, isp->dev,
-                                                       buf->iommu_addr);
-               } else {
-                       if (!buf->virt_addr)
-                               continue;
-                       dma_free_coherent(stat->isp->dev, stat->buf_alloc_size,
-                                         buf->virt_addr, buf->dma_addr);
-               }
-               buf->iommu_addr = 0;
-               buf->iovm = NULL;
+               if (!buf->virt_addr)
+                       continue;
+
+               sg_free_table(&buf->sgt);
+
+               dma_free_coherent(dev, stat->buf_alloc_size, buf->virt_addr,
+                                 buf->dma_addr);
+
                buf->dma_addr = 0;
                buf->virt_addr = NULL;
                buf->empty = 1;
@@ -389,83 +369,51 @@ static void isp_stat_bufs_free(struct ispstat *stat)
        stat->active_buf = NULL;
 }
 
-static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size)
-{
-       struct isp_device *isp = stat->isp;
-       int i;
-
-       stat->buf_alloc_size = size;
-
-       for (i = 0; i < STAT_MAX_BUFS; i++) {
-               struct ispstat_buffer *buf = &stat->buf[i];
-               struct iovm_struct *iovm;
-
-               WARN_ON(buf->dma_addr);
-               buf->iommu_addr = omap_iommu_vmalloc(isp->domain, isp->dev, 0,
-                                                       size, IOMMU_FLAG);
-               if (IS_ERR((void *)buf->iommu_addr)) {
-                       dev_err(stat->isp->dev,
-                                "%s: Can't acquire memory for "
-                                "buffer %d\n", stat->subdev.name, i);
-                       isp_stat_bufs_free(stat);
-                       return -ENOMEM;
-               }
-
-               iovm = omap_find_iovm_area(isp->dev, buf->iommu_addr);
-               if (!iovm ||
-                   !dma_map_sg(isp->dev, iovm->sgt->sgl, iovm->sgt->nents,
-                               DMA_FROM_DEVICE)) {
-                       isp_stat_bufs_free(stat);
-                       return -ENOMEM;
-               }
-               buf->iovm = iovm;
-
-               buf->virt_addr = omap_da_to_va(stat->isp->dev,
-                                         (u32)buf->iommu_addr);
-               buf->empty = 1;
-               dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated."
-                       "iommu_addr=0x%08lx virt_addr=0x%08lx",
-                       stat->subdev.name, i, buf->iommu_addr,
-                       (unsigned long)buf->virt_addr);
-       }
-
-       return 0;
-}
-
-static int isp_stat_bufs_alloc_dma(struct ispstat *stat, unsigned int size)
+static int isp_stat_bufs_alloc_one(struct device *dev,
+                                  struct ispstat_buffer *buf,
+                                  unsigned int size)
 {
-       int i;
-
-       stat->buf_alloc_size = size;
-
-       for (i = 0; i < STAT_MAX_BUFS; i++) {
-               struct ispstat_buffer *buf = &stat->buf[i];
-
-               WARN_ON(buf->iommu_addr);
-               buf->virt_addr = dma_alloc_coherent(stat->isp->dev, size,
-                                       &buf->dma_addr, GFP_KERNEL | GFP_DMA);
+       int ret;
 
-               if (!buf->virt_addr || !buf->dma_addr) {
-                       dev_info(stat->isp->dev,
-                                "%s: Can't acquire memory for "
-                                "DMA buffer %d\n", stat->subdev.name, i);
-                       isp_stat_bufs_free(stat);
-                       return -ENOMEM;
-               }
-               buf->empty = 1;
+       buf->virt_addr = dma_alloc_coherent(dev, size, &buf->dma_addr,
+                                           GFP_KERNEL | GFP_DMA);
+       if (!buf->virt_addr)
+               return -ENOMEM;
 
-               dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated."
-                       "dma_addr=0x%08lx virt_addr=0x%08lx\n",
-                       stat->subdev.name, i, (unsigned long)buf->dma_addr,
-                       (unsigned long)buf->virt_addr);
+       ret = dma_get_sgtable(dev, &buf->sgt, buf->virt_addr, buf->dma_addr,
+                             size);
+       if (ret < 0) {
+               dma_free_coherent(dev, size, buf->virt_addr, buf->dma_addr);
+               buf->virt_addr = NULL;
+               buf->dma_addr = 0;
+               return ret;
        }
 
        return 0;
 }
 
+/*
+ * The device passed to the DMA API depends on whether the statistics block uses
+ * ISP DMA, external DMA or PIO to transfer data.
+ *
+ * The first case (for the AEWB and AF engines) passes the ISP device, resulting
+ * in the DMA buffers being mapped through the ISP IOMMU.
+ *
+ * The second case (for the histogram engine) should pass the DMA engine device.
+ * As that device isn't accessible through the OMAP DMA engine API the driver
+ * passes NULL instead, resulting in the buffers being mapped directly as
+ * physical pages.
+ *
+ * The third case (for the histogram engine) doesn't require any mapping. The
+ * buffers could be allocated with kmalloc/vmalloc, but we still use
+ * dma_alloc_coherent() for consistency purpose.
+ */
 static int isp_stat_bufs_alloc(struct ispstat *stat, u32 size)
 {
+       struct device *dev = ISP_STAT_USES_DMAENGINE(stat)
+                          ? NULL : stat->isp->dev;
        unsigned long flags;
+       unsigned int i;
 
        spin_lock_irqsave(&stat->isp->stat_lock, flags);
 
@@ -489,10 +437,31 @@ static int isp_stat_bufs_alloc(struct ispstat *stat, u32 size)
 
        isp_stat_bufs_free(stat);
 
-       if (IS_COHERENT_BUF(stat))
-               return isp_stat_bufs_alloc_dma(stat, size);
-       else
-               return isp_stat_bufs_alloc_iommu(stat, size);
+       stat->buf_alloc_size = size;
+
+       for (i = 0; i < STAT_MAX_BUFS; i++) {
+               struct ispstat_buffer *buf = &stat->buf[i];
+               int ret;
+
+               ret = isp_stat_bufs_alloc_one(dev, buf, size);
+               if (ret < 0) {
+                       dev_err(stat->isp->dev,
+                               "%s: Failed to allocate DMA buffer %u\n",
+                               stat->subdev.name, i);
+                       isp_stat_bufs_free(stat);
+                       return ret;
+               }
+
+               buf->empty = 1;
+
+               dev_dbg(stat->isp->dev,
+                       "%s: buffer[%u] allocated. dma=0x%08lx virt=0x%08lx",
+                       stat->subdev.name, i,
+                       (unsigned long)buf->dma_addr,
+                       (unsigned long)buf->virt_addr);
+       }
+
+       return 0;
 }
 
 static void isp_stat_queue_event(struct ispstat *stat, int err)
index 9a047c929b9f95d47a758267cc341c92bd4f5c8c..58d6ac7cb6648ce18f2e8284cc85fe9d96199242 100644 (file)
@@ -46,8 +46,7 @@
 struct ispstat;
 
 struct ispstat_buffer {
-       unsigned long iommu_addr;
-       struct iovm_struct *iovm;
+       struct sg_table sgt;
        void *virt_addr;
        dma_addr_t dma_addr;
        struct timespec ts;
index 85b4036ba5e43296ce647c4f857e8484b430846c..e36bac26476c0fd7ac382f5e85effc07a7a0b202 100644 (file)
@@ -27,7 +27,6 @@
 #include <linux/clk.h>
 #include <linux/mm.h>
 #include <linux/module.h>
-#include <linux/omap-iommu.h>
 #include <linux/pagemap.h>
 #include <linux/scatterlist.h>
 #include <linux/sched.h>
@@ -35,6 +34,7 @@
 #include <linux/vmalloc.h>
 #include <media/v4l2-dev.h>
 #include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
 
 #include "ispvideo.h"
 #include "isp.h"
@@ -325,91 +325,37 @@ isp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh)
        return ret;
 }
 
-/* -----------------------------------------------------------------------------
- * IOMMU management
- */
-
-#define IOMMU_FLAG     (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8)
-
-/*
- * ispmmu_vmap - Wrapper for Virtual memory mapping of a scatter gather list
- * @isp: Device pointer specific to the OMAP3 ISP.
- * @sglist: Pointer to source Scatter gather list to allocate.
- * @sglen: Number of elements of the scatter-gatter list.
- *
- * Returns a resulting mapped device address by the ISP MMU, or -ENOMEM if
- * we ran out of memory.
- */
-static dma_addr_t
-ispmmu_vmap(struct isp_device *isp, const struct scatterlist *sglist, int sglen)
-{
-       struct sg_table *sgt;
-       u32 da;
-
-       sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
-       if (sgt == NULL)
-               return -ENOMEM;
-
-       sgt->sgl = (struct scatterlist *)sglist;
-       sgt->nents = sglen;
-       sgt->orig_nents = sglen;
-
-       da = omap_iommu_vmap(isp->domain, isp->dev, 0, sgt, IOMMU_FLAG);
-       if (IS_ERR_VALUE(da))
-               kfree(sgt);
-
-       return da;
-}
-
-/*
- * ispmmu_vunmap - Unmap a device address from the ISP MMU
- * @isp: Device pointer specific to the OMAP3 ISP.
- * @da: Device address generated from a ispmmu_vmap call.
- */
-static void ispmmu_vunmap(struct isp_device *isp, dma_addr_t da)
-{
-       struct sg_table *sgt;
-
-       sgt = omap_iommu_vunmap(isp->domain, isp->dev, (u32)da);
-       kfree(sgt);
-}
-
 /* -----------------------------------------------------------------------------
  * Video queue operations
  */
 
-static void isp_video_queue_prepare(struct isp_video_queue *queue,
-                                   unsigned int *nbuffers, unsigned int *size)
+static int isp_video_queue_setup(struct vb2_queue *queue,
+                                const struct v4l2_format *fmt,
+                                unsigned int *count, unsigned int *num_planes,
+                                unsigned int sizes[], void *alloc_ctxs[])
 {
-       struct isp_video_fh *vfh =
-               container_of(queue, struct isp_video_fh, queue);
+       struct isp_video_fh *vfh = vb2_get_drv_priv(queue);
        struct isp_video *video = vfh->video;
 
-       *size = vfh->format.fmt.pix.sizeimage;
-       if (*size == 0)
-               return;
+       *num_planes = 1;
 
-       *nbuffers = min(*nbuffers, video->capture_mem / PAGE_ALIGN(*size));
-}
+       sizes[0] = vfh->format.fmt.pix.sizeimage;
+       if (sizes[0] == 0)
+               return -EINVAL;
 
-static void isp_video_buffer_cleanup(struct isp_video_buffer *buf)
-{
-       struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue);
-       struct isp_buffer *buffer = to_isp_buffer(buf);
-       struct isp_video *video = vfh->video;
+       alloc_ctxs[0] = video->alloc_ctx;
 
-       if (buffer->isp_addr) {
-               ispmmu_vunmap(video->isp, buffer->isp_addr);
-               buffer->isp_addr = 0;
-       }
+       *count = min(*count, video->capture_mem / PAGE_ALIGN(sizes[0]));
+
+       return 0;
 }
 
-static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
+static int isp_video_buffer_prepare(struct vb2_buffer *buf)
 {
-       struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue);
+       struct isp_video_fh *vfh = vb2_get_drv_priv(buf->vb2_queue);
        struct isp_buffer *buffer = to_isp_buffer(buf);
        struct isp_video *video = vfh->video;
-       unsigned long addr;
+       dma_addr_t addr;
 
        /* Refuse to prepare the buffer is the video node has registered an
         * error. We don't need to take any lock here as the operation is
@@ -420,19 +366,16 @@ static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
        if (unlikely(video->error))
                return -EIO;
 
-       addr = ispmmu_vmap(video->isp, buf->sglist, buf->sglen);
-       if (IS_ERR_VALUE(addr))
-               return -EIO;
-
+       addr = vb2_dma_contig_plane_dma_addr(buf, 0);
        if (!IS_ALIGNED(addr, 32)) {
-               dev_dbg(video->isp->dev, "Buffer address must be "
-                       "aligned to 32 bytes boundary.\n");
-               ispmmu_vunmap(video->isp, buffer->isp_addr);
+               dev_dbg(video->isp->dev,
+                       "Buffer address must be aligned to 32 bytes boundary.\n");
                return -EINVAL;
        }
 
-       buf->vbuf.bytesused = vfh->format.fmt.pix.sizeimage;
-       buffer->isp_addr = addr;
+       vb2_set_plane_payload(&buffer->vb, 0, vfh->format.fmt.pix.sizeimage);
+       buffer->dma = addr;
+
        return 0;
 }
 
@@ -445,9 +388,9 @@ static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
  * If the pipeline is busy, it will be restarted in the output module interrupt
  * handler.
  */
-static void isp_video_buffer_queue(struct isp_video_buffer *buf)
+static void isp_video_buffer_queue(struct vb2_buffer *buf)
 {
-       struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue);
+       struct isp_video_fh *vfh = vb2_get_drv_priv(buf->vb2_queue);
        struct isp_buffer *buffer = to_isp_buffer(buf);
        struct isp_video *video = vfh->video;
        struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity);
@@ -456,14 +399,18 @@ static void isp_video_buffer_queue(struct isp_video_buffer *buf)
        unsigned int empty;
        unsigned int start;
 
+       spin_lock_irqsave(&video->irqlock, flags);
+
        if (unlikely(video->error)) {
-               buf->state = ISP_BUF_STATE_ERROR;
-               wake_up(&buf->wait);
+               vb2_buffer_done(&buffer->vb, VB2_BUF_STATE_ERROR);
+               spin_unlock_irqrestore(&video->irqlock, flags);
                return;
        }
 
        empty = list_empty(&video->dmaqueue);
-       list_add_tail(&buffer->buffer.irqlist, &video->dmaqueue);
+       list_add_tail(&buffer->irqlist, &video->dmaqueue);
+
+       spin_unlock_irqrestore(&video->irqlock, flags);
 
        if (empty) {
                if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -487,23 +434,22 @@ static void isp_video_buffer_queue(struct isp_video_buffer *buf)
        }
 }
 
-static const struct isp_video_queue_operations isp_video_queue_ops = {
-       .queue_prepare = &isp_video_queue_prepare,
-       .buffer_prepare = &isp_video_buffer_prepare,
-       .buffer_queue = &isp_video_buffer_queue,
-       .buffer_cleanup = &isp_video_buffer_cleanup,
+static const struct vb2_ops isp_video_queue_ops = {
+       .queue_setup = isp_video_queue_setup,
+       .buf_prepare = isp_video_buffer_prepare,
+       .buf_queue = isp_video_buffer_queue,
 };
 
 /*
  * omap3isp_video_buffer_next - Complete the current buffer and return the next
  * @video: ISP video object
  *
- * Remove the current video buffer from the DMA queue and fill its timestamp,
- * field count and state fields before waking up its completion handler.
+ * Remove the current video buffer from the DMA queue and fill its timestamp and
+ * field count before handing it back to videobuf2.
  *
- * For capture video nodes the buffer state is set to ISP_BUF_STATE_DONE if no
- * error has been flagged in the pipeline, or to ISP_BUF_STATE_ERROR otherwise.
- * For video output nodes the buffer state is always set to ISP_BUF_STATE_DONE.
+ * For capture video nodes the buffer state is set to VB2_BUF_STATE_DONE if no
+ * error has been flagged in the pipeline, or to VB2_BUF_STATE_ERROR otherwise.
+ * For video output nodes the buffer state is always set to VB2_BUF_STATE_DONE.
  *
  * The DMA queue is expected to contain at least one buffer.
  *
@@ -513,26 +459,25 @@ static const struct isp_video_queue_operations isp_video_queue_ops = {
 struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)
 {
        struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity);
-       struct isp_video_queue *queue = video->queue;
        enum isp_pipeline_state state;
-       struct isp_video_buffer *buf;
+       struct isp_buffer *buf;
        unsigned long flags;
        struct timespec ts;
 
-       spin_lock_irqsave(&queue->irqlock, flags);
+       spin_lock_irqsave(&video->irqlock, flags);
        if (WARN_ON(list_empty(&video->dmaqueue))) {
-               spin_unlock_irqrestore(&queue->irqlock, flags);
+               spin_unlock_irqrestore(&video->irqlock, flags);
                return NULL;
        }
 
-       buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer,
+       buf = list_first_entry(&video->dmaqueue, struct isp_buffer,
                               irqlist);
        list_del(&buf->irqlist);
-       spin_unlock_irqrestore(&queue->irqlock, flags);
+       spin_unlock_irqrestore(&video->irqlock, flags);
 
        ktime_get_ts(&ts);
-       buf->vbuf.timestamp.tv_sec = ts.tv_sec;
-       buf->vbuf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+       buf->vb.v4l2_buf.timestamp.tv_sec = ts.tv_sec;
+       buf->vb.v4l2_buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
 
        /* Do frame number propagation only if this is the output video node.
         * Frame number either comes from the CSI receivers or it gets
@@ -541,22 +486,27 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)
         * first, so the input number might lag behind by 1 in some cases.
         */
        if (video == pipe->output && !pipe->do_propagation)
-               buf->vbuf.sequence = atomic_inc_return(&pipe->frame_number);
+               buf->vb.v4l2_buf.sequence =
+                       atomic_inc_return(&pipe->frame_number);
        else
-               buf->vbuf.sequence = atomic_read(&pipe->frame_number);
+               buf->vb.v4l2_buf.sequence = atomic_read(&pipe->frame_number);
 
        /* Report pipeline errors to userspace on the capture device side. */
-       if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) {
-               buf->state = ISP_BUF_STATE_ERROR;
+       if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) {
+               state = VB2_BUF_STATE_ERROR;
                pipe->error = false;
        } else {
-               buf->state = ISP_BUF_STATE_DONE;
+               state = VB2_BUF_STATE_DONE;
        }
 
-       wake_up(&buf->wait);
+       vb2_buffer_done(&buf->vb, state);
+
+       spin_lock_irqsave(&video->irqlock, flags);
 
        if (list_empty(&video->dmaqueue)) {
-               if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               spin_unlock_irqrestore(&video->irqlock, flags);
+
+               if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
                        state = ISP_PIPELINE_QUEUE_OUTPUT
                              | ISP_PIPELINE_STREAM;
                else
@@ -571,16 +521,19 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)
                return NULL;
        }
 
-       if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) {
-               spin_lock_irqsave(&pipe->lock, flags);
+       if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) {
+               spin_lock(&pipe->lock);
                pipe->state &= ~ISP_PIPELINE_STREAM;
-               spin_unlock_irqrestore(&pipe->lock, flags);
+               spin_unlock(&pipe->lock);
        }
 
-       buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer,
+       buf = list_first_entry(&video->dmaqueue, struct isp_buffer,
                               irqlist);
-       buf->state = ISP_BUF_STATE_ACTIVE;
-       return to_isp_buffer(buf);
+       buf->vb.state = VB2_BUF_STATE_ACTIVE;
+
+       spin_unlock_irqrestore(&video->irqlock, flags);
+
+       return buf;
 }
 
 /*
@@ -592,25 +545,22 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)
  */
 void omap3isp_video_cancel_stream(struct isp_video *video)
 {
-       struct isp_video_queue *queue = video->queue;
        unsigned long flags;
 
-       spin_lock_irqsave(&queue->irqlock, flags);
+       spin_lock_irqsave(&video->irqlock, flags);
 
        while (!list_empty(&video->dmaqueue)) {
-               struct isp_video_buffer *buf;
+               struct isp_buffer *buf;
 
                buf = list_first_entry(&video->dmaqueue,
-                                      struct isp_video_buffer, irqlist);
+                                      struct isp_buffer, irqlist);
                list_del(&buf->irqlist);
-
-               buf->state = ISP_BUF_STATE_ERROR;
-               wake_up(&buf->wait);
+               vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
        }
 
        video->error = true;
 
-       spin_unlock_irqrestore(&queue->irqlock, flags);
+       spin_unlock_irqrestore(&video->irqlock, flags);
 }
 
 /*
@@ -627,12 +577,15 @@ void omap3isp_video_resume(struct isp_video *video, int continuous)
 {
        struct isp_buffer *buf = NULL;
 
-       if (continuous && video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               omap3isp_video_queue_discard_done(video->queue);
+       if (continuous && video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+               mutex_lock(&video->queue_lock);
+               vb2_discard_done(video->queue);
+               mutex_unlock(&video->queue_lock);
+       }
 
        if (!list_empty(&video->dmaqueue)) {
                buf = list_first_entry(&video->dmaqueue,
-                                      struct isp_buffer, buffer.irqlist);
+                                      struct isp_buffer, irqlist);
                video->ops->queue(video, buf);
                video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED;
        } else {
@@ -840,33 +793,56 @@ static int
 isp_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
 {
        struct isp_video_fh *vfh = to_isp_video_fh(fh);
+       struct isp_video *video = video_drvdata(file);
+       int ret;
 
-       return omap3isp_video_queue_reqbufs(&vfh->queue, rb);
+       mutex_lock(&video->queue_lock);
+       ret = vb2_reqbufs(&vfh->queue, rb);
+       mutex_unlock(&video->queue_lock);
+
+       return ret;
 }
 
 static int
 isp_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
 {
        struct isp_video_fh *vfh = to_isp_video_fh(fh);
+       struct isp_video *video = video_drvdata(file);
+       int ret;
+
+       mutex_lock(&video->queue_lock);
+       ret = vb2_querybuf(&vfh->queue, b);
+       mutex_unlock(&video->queue_lock);
 
-       return omap3isp_video_queue_querybuf(&vfh->queue, b);
+       return ret;
 }
 
 static int
 isp_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
 {
        struct isp_video_fh *vfh = to_isp_video_fh(fh);
+       struct isp_video *video = video_drvdata(file);
+       int ret;
 
-       return omap3isp_video_queue_qbuf(&vfh->queue, b);
+       mutex_lock(&video->queue_lock);
+       ret = vb2_qbuf(&vfh->queue, b);
+       mutex_unlock(&video->queue_lock);
+
+       return ret;
 }
 
 static int
 isp_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
 {
        struct isp_video_fh *vfh = to_isp_video_fh(fh);
+       struct isp_video *video = video_drvdata(file);
+       int ret;
+
+       mutex_lock(&video->queue_lock);
+       ret = vb2_dqbuf(&vfh->queue, b, file->f_flags & O_NONBLOCK);
+       mutex_unlock(&video->queue_lock);
 
-       return omap3isp_video_queue_dqbuf(&vfh->queue, b,
-                                         file->f_flags & O_NONBLOCK);
+       return ret;
 }
 
 static int isp_video_check_external_subdevs(struct isp_video *video,
@@ -1006,11 +982,6 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
 
        mutex_lock(&video->stream_lock);
 
-       if (video->streaming) {
-               mutex_unlock(&video->stream_lock);
-               return -EBUSY;
-       }
-
        /* Start streaming on the pipeline. No link touching an entity in the
         * pipeline can be activated or deactivated once streaming is started.
         */
@@ -1069,7 +1040,9 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
        INIT_LIST_HEAD(&video->dmaqueue);
        atomic_set(&pipe->frame_number, -1);
 
-       ret = omap3isp_video_queue_streamon(&vfh->queue);
+       mutex_lock(&video->queue_lock);
+       ret = vb2_streamon(&vfh->queue, type);
+       mutex_unlock(&video->queue_lock);
        if (ret < 0)
                goto err_check_format;
 
@@ -1082,19 +1055,19 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
                                              ISP_PIPELINE_STREAM_CONTINUOUS);
                if (ret < 0)
                        goto err_set_stream;
-               spin_lock_irqsave(&video->queue->irqlock, flags);
+               spin_lock_irqsave(&video->irqlock, flags);
                if (list_empty(&video->dmaqueue))
                        video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN;
-               spin_unlock_irqrestore(&video->queue->irqlock, flags);
+               spin_unlock_irqrestore(&video->irqlock, flags);
        }
 
-       video->streaming = 1;
-
        mutex_unlock(&video->stream_lock);
        return 0;
 
 err_set_stream:
-       omap3isp_video_queue_streamoff(&vfh->queue);
+       mutex_lock(&video->queue_lock);
+       vb2_streamoff(&vfh->queue, type);
+       mutex_unlock(&video->queue_lock);
 err_check_format:
        media_entity_pipeline_stop(&video->video.entity);
 err_pipeline_start:
@@ -1130,9 +1103,9 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
        mutex_lock(&video->stream_lock);
 
        /* Make sure we're not streaming yet. */
-       mutex_lock(&vfh->queue.lock);
-       streaming = vfh->queue.streaming;
-       mutex_unlock(&vfh->queue.lock);
+       mutex_lock(&video->queue_lock);
+       streaming = vb2_is_streaming(&vfh->queue);
+       mutex_unlock(&video->queue_lock);
 
        if (!streaming)
                goto done;
@@ -1151,9 +1124,12 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
 
        /* Stop the stream. */
        omap3isp_pipeline_set_stream(pipe, ISP_PIPELINE_STREAM_STOPPED);
-       omap3isp_video_queue_streamoff(&vfh->queue);
+       omap3isp_video_cancel_stream(video);
+
+       mutex_lock(&video->queue_lock);
+       vb2_streamoff(&vfh->queue, type);
+       mutex_unlock(&video->queue_lock);
        video->queue = NULL;
-       video->streaming = 0;
        video->error = false;
 
        if (video->isp->pdata->set_constraints)
@@ -1223,6 +1199,7 @@ static int isp_video_open(struct file *file)
 {
        struct isp_video *video = video_drvdata(file);
        struct isp_video_fh *handle;
+       struct vb2_queue *queue;
        int ret = 0;
 
        handle = kzalloc(sizeof(*handle), GFP_KERNEL);
@@ -1244,9 +1221,20 @@ static int isp_video_open(struct file *file)
                goto done;
        }
 
-       omap3isp_video_queue_init(&handle->queue, video->type,
-                                 &isp_video_queue_ops, video->isp->dev,
-                                 sizeof(struct isp_buffer));
+       queue = &handle->queue;
+       queue->type = video->type;
+       queue->io_modes = VB2_MMAP | VB2_USERPTR;
+       queue->drv_priv = handle;
+       queue->ops = &isp_video_queue_ops;
+       queue->mem_ops = &vb2_dma_contig_memops;
+       queue->buf_struct_size = sizeof(struct isp_buffer);
+       queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+
+       ret = vb2_queue_init(&handle->queue);
+       if (ret < 0) {
+               omap3isp_put(video->isp);
+               goto done;
+       }
 
        memset(&handle->format, 0, sizeof(handle->format));
        handle->format.type = video->type;
@@ -1273,9 +1261,9 @@ static int isp_video_release(struct file *file)
        /* Disable streaming and free the buffers queue resources. */
        isp_video_streamoff(file, vfh, video->type);
 
-       mutex_lock(&handle->queue.lock);
-       omap3isp_video_queue_cleanup(&handle->queue);
-       mutex_unlock(&handle->queue.lock);
+       mutex_lock(&video->queue_lock);
+       vb2_queue_release(&handle->queue);
+       mutex_unlock(&video->queue_lock);
 
        omap3isp_pipeline_pm_use(&video->video.entity, 0);
 
@@ -1292,16 +1280,27 @@ static int isp_video_release(struct file *file)
 static unsigned int isp_video_poll(struct file *file, poll_table *wait)
 {
        struct isp_video_fh *vfh = to_isp_video_fh(file->private_data);
-       struct isp_video_queue *queue = &vfh->queue;
+       struct isp_video *video = video_drvdata(file);
+       int ret;
 
-       return omap3isp_video_queue_poll(queue, file, wait);
+       mutex_lock(&video->queue_lock);
+       ret = vb2_poll(&vfh->queue, file, wait);
+       mutex_unlock(&video->queue_lock);
+
+       return ret;
 }
 
 static int isp_video_mmap(struct file *file, struct vm_area_struct *vma)
 {
        struct isp_video_fh *vfh = to_isp_video_fh(file->private_data);
+       struct isp_video *video = video_drvdata(file);
+       int ret;
+
+       mutex_lock(&video->queue_lock);
+       ret = vb2_mmap(&vfh->queue, vma);
+       mutex_unlock(&video->queue_lock);
 
-       return omap3isp_video_queue_mmap(&vfh->queue, vma);
+       return ret;
 }
 
 static struct v4l2_file_operations isp_video_fops = {
@@ -1342,15 +1341,23 @@ int omap3isp_video_init(struct isp_video *video, const char *name)
                return -EINVAL;
        }
 
+       video->alloc_ctx = vb2_dma_contig_init_ctx(video->isp->dev);
+       if (IS_ERR(video->alloc_ctx))
+               return PTR_ERR(video->alloc_ctx);
+
        ret = media_entity_init(&video->video.entity, 1, &video->pad, 0);
-       if (ret < 0)
+       if (ret < 0) {
+               vb2_dma_contig_cleanup_ctx(video->alloc_ctx);
                return ret;
+       }
 
        mutex_init(&video->mutex);
        atomic_set(&video->active, 0);
 
        spin_lock_init(&video->pipe.lock);
        mutex_init(&video->stream_lock);
+       mutex_init(&video->queue_lock);
+       spin_lock_init(&video->irqlock);
 
        /* Initialize the video device. */
        if (video->ops == NULL)
@@ -1371,7 +1378,9 @@ int omap3isp_video_init(struct isp_video *video, const char *name)
 
 void omap3isp_video_cleanup(struct isp_video *video)
 {
+       vb2_dma_contig_cleanup_ctx(video->alloc_ctx);
        media_entity_cleanup(&video->video.entity);
+       mutex_destroy(&video->queue_lock);
        mutex_destroy(&video->stream_lock);
        mutex_destroy(&video->mutex);
 }
index 4e194076cc60d611727a6441231f79006dbfadfc..7d2e82122ecda431d417ae10ba8193b9fb9154ec 100644 (file)
@@ -30,8 +30,7 @@
 #include <media/media-entity.h>
 #include <media/v4l2-dev.h>
 #include <media/v4l2-fh.h>
-
-#include "ispqueue.h"
+#include <media/videobuf2-core.h>
 
 #define ISP_VIDEO_DRIVER_NAME          "ispvideo"
 #define ISP_VIDEO_DRIVER_VERSION       "0.0.2"
@@ -124,17 +123,19 @@ static inline int isp_pipeline_ready(struct isp_pipeline *pipe)
                               ISP_PIPELINE_IDLE_OUTPUT);
 }
 
-/*
- * struct isp_buffer - ISP buffer
- * @buffer: ISP video buffer
- * @isp_addr: MMU mapped address (a.k.a. device address) of the buffer.
+/**
+ * struct isp_buffer - ISP video buffer
+ * @vb: videobuf2 buffer
+ * @irqlist: List head for insertion into IRQ queue
+ * @dma: DMA address
  */
 struct isp_buffer {
-       struct isp_video_buffer buffer;
-       dma_addr_t isp_addr;
+       struct vb2_buffer vb;
+       struct list_head irqlist;
+       dma_addr_t dma;
 };
 
-#define to_isp_buffer(buf)     container_of(buf, struct isp_buffer, buffer)
+#define to_isp_buffer(buf)     container_of(buf, struct isp_buffer, vb)
 
 enum isp_video_dmaqueue_flags {
        /* Set if DMA queue becomes empty when ISP_PIPELINE_STREAM_CONTINUOUS */
@@ -172,16 +173,16 @@ struct isp_video {
        unsigned int bpl_value;         /* bytes per line value */
        unsigned int bpl_padding;       /* padding at end of line */
 
-       /* Entity video node streaming */
-       unsigned int streaming:1;
-
        /* Pipeline state */
        struct isp_pipeline pipe;
        struct mutex stream_lock;       /* pipeline and stream states */
        bool error;
 
        /* Video buffers queue */
-       struct isp_video_queue *queue;
+       void *alloc_ctx;
+       struct vb2_queue *queue;
+       struct mutex queue_lock;        /* protects the queue */
+       spinlock_t irqlock;             /* protects dmaqueue */
        struct list_head dmaqueue;
        enum isp_video_dmaqueue_flags dmaqueue_flags;
 
@@ -193,7 +194,7 @@ struct isp_video {
 struct isp_video_fh {
        struct v4l2_fh vfh;
        struct isp_video *video;
-       struct isp_video_queue queue;
+       struct vb2_queue queue;
        struct v4l2_format format;
        struct v4l2_fract timeperframe;
 };
index 349e659d75fb861359165665638fad93a2293631..7c4489c4236502042fc1fcb3a0ff545f2f7639a7 100644 (file)
@@ -1199,6 +1199,30 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
 }
 EXPORT_SYMBOL_GPL(vb2_buffer_done);
 
+/**
+ * vb2_discard_done() - discard all buffers marked as DONE
+ * @q:         videobuf2 queue
+ *
+ * This function is intended to be used with suspend/resume operations. It
+ * discards all 'done' buffers as they would be too old to be requested after
+ * resume.
+ *
+ * Drivers must stop the hardware and synchronize with interrupt handlers and/or
+ * delayed works before calling this function to make sure no buffer will be
+ * touched by the driver and/or hardware.
+ */
+void vb2_discard_done(struct vb2_queue *q)
+{
+       struct vb2_buffer *vb;
+       unsigned long flags;
+
+       spin_lock_irqsave(&q->done_lock, flags);
+       list_for_each_entry(vb, &q->done_list, done_entry)
+               vb->state = VB2_BUF_STATE_ERROR;
+       spin_unlock_irqrestore(&q->done_lock, flags);
+}
+EXPORT_SYMBOL_GPL(vb2_discard_done);
+
 /**
  * __fill_vb2_buffer() - fill a vb2_buffer with information provided in a
  * v4l2_buffer by the userspace. The caller has already verified that struct
index 96162b62f3c0897df3f923ee0d4befcebfcb9048..3bc969a5916b8249c193a44e71eb89c4c077be85 100644 (file)
 #include <linux/i2c/twl.h>
 #include <linux/platform_device.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 
 #include <asm/mach-types.h>
 
 static u8 twl4030_start_script_address = 0x2b;
 
-#define PWR_P1_SW_EVENTS       0x10
-#define PWR_DEVOFF             (1 << 0)
+/* Register bits for P1, P2 and P3_SW_EVENTS */
+#define PWR_STOPON_PRWON       BIT(6)
+#define PWR_STOPON_SYSEN       BIT(5)
+#define PWR_ENABLE_WARMRESET   BIT(4)
+#define PWR_LVL_WAKEUP         BIT(3)
+#define PWR_DEVACT             BIT(2)
+#define PWR_DEVSLP             BIT(1)
+#define PWR_DEVOFF             BIT(0)
+
 #define SEQ_OFFSYNC            (1 << 0)
 
 #define PHY_TO_OFF_PM_MASTER(p)                (p - 0x36)
@@ -52,10 +60,6 @@ static u8 twl4030_start_script_address = 0x2b;
 #define R_CFG_P2_TRANSITION    PHY_TO_OFF_PM_MASTER(0x37)
 #define R_CFG_P3_TRANSITION    PHY_TO_OFF_PM_MASTER(0x38)
 
-#define LVL_WAKEUP     0x08
-
-#define ENABLE_WARMRESET (1<<4)
-
 #define END_OF_SCRIPT          0x3f
 
 #define R_SEQ_ADD_A2S          PHY_TO_OFF_PM_MASTER(0x55)
@@ -125,6 +129,53 @@ static u8 res_config_addrs[] = {
        [RES_MAIN_REF]  = 0x94,
 };
 
+/*
+ * Usable values for .remap_sleep and .remap_off
+ * Based on table "5.3.3 Resource Operating modes"
+ */
+enum {
+       TWL_REMAP_OFF = 0,
+       TWL_REMAP_SLEEP = 8,
+       TWL_REMAP_ACTIVE = 9,
+};
+
+/*
+ * Macros to configure the PM register states for various resources.
+ * Note that we can make MSG_SINGULAR etc private to this driver once
+ * omap3 has been made DT only.
+ */
+#define TWL_DFLT_DELAY         2       /* typically 2 32 KiHz cycles */
+#define TWL_DEV_GRP_P123       (DEV_GRP_P1 | DEV_GRP_P2 | DEV_GRP_P3)
+#define TWL_RESOURCE_SET(res, state)                                   \
+       { MSG_SINGULAR(DEV_GRP_NULL, (res), (state)), TWL_DFLT_DELAY }
+#define TWL_RESOURCE_ON(res)   TWL_RESOURCE_SET(res, RES_STATE_ACTIVE)
+#define TWL_RESOURCE_OFF(res)  TWL_RESOURCE_SET(res, RES_STATE_OFF)
+#define TWL_RESOURCE_RESET(res)        TWL_RESOURCE_SET(res, RES_STATE_WRST)
+/*
+ * It seems that type1 and type2 is just the resource init order
+ * number for the type1 and type2 group.
+ */
+#define TWL_RESOURCE_SET_ACTIVE(res, state)                            \
+       { MSG_SINGULAR(DEV_GRP_NULL, (res), RES_STATE_ACTIVE), (state) }
+#define TWL_RESOURCE_GROUP_RESET(group, type1, type2)                  \
+       { MSG_BROADCAST(DEV_GRP_NULL, (group), (type1), (type2),        \
+               RES_STATE_WRST), TWL_DFLT_DELAY }
+#define TWL_RESOURCE_GROUP_SLEEP(group, type, type2)                   \
+       { MSG_BROADCAST(DEV_GRP_NULL, (group), (type), (type2),         \
+               RES_STATE_SLEEP), TWL_DFLT_DELAY }
+#define TWL_RESOURCE_GROUP_ACTIVE(group, type, type2)                  \
+       { MSG_BROADCAST(DEV_GRP_NULL, (group), (type), (type2),         \
+               RES_STATE_ACTIVE), TWL_DFLT_DELAY }
+#define TWL_REMAP_SLEEP(res, devgrp, typ, typ2)                                \
+       { .resource = (res), .devgroup = (devgrp),                      \
+         .type = (typ), .type2 = (typ2),                               \
+         .remap_off = TWL_REMAP_OFF,                                   \
+         .remap_sleep = TWL_REMAP_SLEEP, }
+#define TWL_REMAP_OFF(res, devgrp, typ, typ2)                          \
+       { .resource = (res), .devgroup = (devgrp),                      \
+         .type = (typ), .type2 = (typ2),                               \
+         .remap_off = TWL_REMAP_OFF, .remap_sleep = TWL_REMAP_OFF, }
+
 static int twl4030_write_script_byte(u8 address, u8 byte)
 {
        int err;
@@ -196,7 +247,7 @@ static int twl4030_config_wakeup3_sequence(u8 address)
        err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &data, R_P3_SW_EVENTS);
        if (err)
                goto out;
-       data |= LVL_WAKEUP;
+       data |= PWR_LVL_WAKEUP;
        err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data, R_P3_SW_EVENTS);
 out:
        if (err)
@@ -219,7 +270,7 @@ static int twl4030_config_wakeup12_sequence(u8 address)
        if (err)
                goto out;
 
-       data |= LVL_WAKEUP;
+       data |= PWR_LVL_WAKEUP;
        err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data, R_P1_SW_EVENTS);
        if (err)
                goto out;
@@ -228,7 +279,7 @@ static int twl4030_config_wakeup12_sequence(u8 address)
        if (err)
                goto out;
 
-       data |= LVL_WAKEUP;
+       data |= PWR_LVL_WAKEUP;
        err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data, R_P2_SW_EVENTS);
        if (err)
                goto out;
@@ -281,7 +332,7 @@ static int twl4030_config_warmreset_sequence(u8 address)
        if (err)
                goto out;
 
-       rd_data |= ENABLE_WARMRESET;
+       rd_data |= PWR_ENABLE_WARMRESET;
        err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, rd_data, R_P1_SW_EVENTS);
        if (err)
                goto out;
@@ -290,7 +341,7 @@ static int twl4030_config_warmreset_sequence(u8 address)
        if (err)
                goto out;
 
-       rd_data |= ENABLE_WARMRESET;
+       rd_data |= PWR_ENABLE_WARMRESET;
        err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, rd_data, R_P2_SW_EVENTS);
        if (err)
                goto out;
@@ -299,7 +350,7 @@ static int twl4030_config_warmreset_sequence(u8 address)
        if (err)
                goto out;
 
-       rd_data |= ENABLE_WARMRESET;
+       rd_data |= PWR_ENABLE_WARMRESET;
        err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, rd_data, R_P3_SW_EVENTS);
 out:
        if (err)
@@ -421,6 +472,12 @@ static int load_twl4030_script(struct twl4030_script *tscript,
                        goto out;
        }
        if (tscript->flags & TWL4030_WAKEUP12_SCRIPT) {
+               /* Reset any existing sleep script to avoid hangs on reboot */
+               err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, END_OF_SCRIPT,
+                                      R_SEQ_ADD_A2S);
+               if (err)
+                       goto out;
+
                err = twl4030_config_wakeup12_sequence(address);
                if (err)
                        goto out;
@@ -493,7 +550,8 @@ int twl4030_remove_script(u8 flags)
        return err;
 }
 
-static int twl4030_power_configure_scripts(struct twl4030_power_data *pdata)
+static int
+twl4030_power_configure_scripts(const struct twl4030_power_data *pdata)
 {
        int err;
        int i;
@@ -509,12 +567,34 @@ static int twl4030_power_configure_scripts(struct twl4030_power_data *pdata)
        return 0;
 }
 
-static int twl4030_power_configure_resources(struct twl4030_power_data *pdata)
+static void twl4030_patch_rconfig(struct twl4030_resconfig *common,
+                                 struct twl4030_resconfig *board)
+{
+       while (common->resource) {
+               struct twl4030_resconfig *b = board;
+
+               while (b->resource) {
+                       if (b->resource == common->resource) {
+                               *common = *b;
+                               break;
+                       }
+                       b++;
+               }
+               common++;
+       }
+}
+
+static int
+twl4030_power_configure_resources(const struct twl4030_power_data *pdata)
 {
        struct twl4030_resconfig *resconfig = pdata->resource_config;
+       struct twl4030_resconfig *boardconf = pdata->board_config;
        int err;
 
        if (resconfig) {
+               if (boardconf)
+                       twl4030_patch_rconfig(resconfig, boardconf);
+
                while (resconfig->resource) {
                        err = twl4030_configure_resource(resconfig);
                        if (err)
@@ -541,7 +621,7 @@ void twl4030_power_off(void)
                pr_err("TWL4030 Unable to power off\n");
 }
 
-static bool twl4030_power_use_poweroff(struct twl4030_power_data *pdata,
+static bool twl4030_power_use_poweroff(const struct twl4030_power_data *pdata,
                                        struct device_node *node)
 {
        if (pdata && pdata->use_poweroff)
@@ -553,10 +633,170 @@ static bool twl4030_power_use_poweroff(struct twl4030_power_data *pdata,
        return false;
 }
 
+#ifdef CONFIG_OF
+
+/* Generic warm reset configuration for omap3 */
+
+static struct twl4030_ins omap3_wrst_seq[] = {
+       TWL_RESOURCE_OFF(RES_NRES_PWRON),
+       TWL_RESOURCE_OFF(RES_RESET),
+       TWL_RESOURCE_RESET(RES_MAIN_REF),
+       TWL_RESOURCE_GROUP_RESET(RES_GRP_ALL, RES_TYPE_R0, RES_TYPE2_R2),
+       TWL_RESOURCE_RESET(RES_VUSB_3V1),
+       TWL_RESOURCE_GROUP_RESET(RES_GRP_ALL, RES_TYPE_R0, RES_TYPE2_R1),
+       TWL_RESOURCE_GROUP_RESET(RES_GRP_RC, RES_TYPE_ALL, RES_TYPE2_R0),
+       TWL_RESOURCE_ON(RES_RESET),
+       TWL_RESOURCE_ON(RES_NRES_PWRON),
+};
+
+static struct twl4030_script omap3_wrst_script = {
+       .script = omap3_wrst_seq,
+       .size   = ARRAY_SIZE(omap3_wrst_seq),
+       .flags  = TWL4030_WRST_SCRIPT,
+};
+
+static struct twl4030_script *omap3_reset_scripts[] = {
+       &omap3_wrst_script,
+};
+
+static struct twl4030_resconfig omap3_rconfig[] = {
+       TWL_REMAP_SLEEP(RES_HFCLKOUT, DEV_GRP_P3, -1, -1),
+       TWL_REMAP_SLEEP(RES_VDD1, DEV_GRP_P1, -1, -1),
+       TWL_REMAP_SLEEP(RES_VDD2, DEV_GRP_P1, -1, -1),
+       { 0, 0 },
+};
+
+static struct twl4030_power_data omap3_reset = {
+       .scripts                = omap3_reset_scripts,
+       .num                    = ARRAY_SIZE(omap3_reset_scripts),
+       .resource_config        = omap3_rconfig,
+};
+
+/* Recommended generic default idle configuration for off-idle */
+
+/* Broadcast message to put res to sleep */
+static struct twl4030_ins omap3_idle_sleep_on_seq[] = {
+       TWL_RESOURCE_GROUP_SLEEP(RES_GRP_ALL, RES_TYPE_ALL, 0),
+};
+
+static struct twl4030_script omap3_idle_sleep_on_script = {
+       .script = omap3_idle_sleep_on_seq,
+       .size   = ARRAY_SIZE(omap3_idle_sleep_on_seq),
+       .flags  = TWL4030_SLEEP_SCRIPT,
+};
+
+/* Broadcast message to put res to active */
+static struct twl4030_ins omap3_idle_wakeup_p12_seq[] = {
+       TWL_RESOURCE_GROUP_ACTIVE(RES_GRP_ALL, RES_TYPE_ALL, 0),
+};
+
+static struct twl4030_script omap3_idle_wakeup_p12_script = {
+       .script = omap3_idle_wakeup_p12_seq,
+       .size   = ARRAY_SIZE(omap3_idle_wakeup_p12_seq),
+       .flags  = TWL4030_WAKEUP12_SCRIPT,
+};
+
+/* Broadcast message to put res to active */
+static struct twl4030_ins omap3_idle_wakeup_p3_seq[] = {
+       TWL_RESOURCE_SET_ACTIVE(RES_CLKEN, 0x37),
+       TWL_RESOURCE_GROUP_ACTIVE(RES_GRP_ALL, RES_TYPE_ALL, 0),
+};
+
+static struct twl4030_script omap3_idle_wakeup_p3_script = {
+       .script = omap3_idle_wakeup_p3_seq,
+       .size   = ARRAY_SIZE(omap3_idle_wakeup_p3_seq),
+       .flags  = TWL4030_WAKEUP3_SCRIPT,
+};
+
+static struct twl4030_script *omap3_idle_scripts[] = {
+       &omap3_idle_wakeup_p12_script,
+       &omap3_idle_wakeup_p3_script,
+       &omap3_wrst_script,
+       &omap3_idle_sleep_on_script,
+};
+
+/*
+ * Recommended configuration based on "Recommended Sleep
+ * Sequences for the Zoom Platform":
+ * http://omappedia.com/wiki/File:Recommended_Sleep_Sequences_Zoom.pdf
+ * Note that the type1 and type2 seem to be just the init order number
+ * for type1 and type2 groups as specified in the document mentioned
+ * above.
+ */
+static struct twl4030_resconfig omap3_idle_rconfig[] = {
+       TWL_REMAP_SLEEP(RES_VAUX1, DEV_GRP_NULL, 0, 0),
+       TWL_REMAP_SLEEP(RES_VAUX2, DEV_GRP_NULL, 0, 0),
+       TWL_REMAP_SLEEP(RES_VAUX3, DEV_GRP_NULL, 0, 0),
+       TWL_REMAP_SLEEP(RES_VAUX4, DEV_GRP_NULL, 0, 0),
+       TWL_REMAP_SLEEP(RES_VMMC1, DEV_GRP_NULL, 0, 0),
+       TWL_REMAP_SLEEP(RES_VMMC2, DEV_GRP_NULL, 0, 0),
+       TWL_REMAP_OFF(RES_VPLL1, DEV_GRP_P1, 3, 1),
+       TWL_REMAP_SLEEP(RES_VPLL2, DEV_GRP_P1, 0, 0),
+       TWL_REMAP_SLEEP(RES_VSIM, DEV_GRP_NULL, 0, 0),
+       TWL_REMAP_SLEEP(RES_VDAC, DEV_GRP_NULL, 0, 0),
+       TWL_REMAP_SLEEP(RES_VINTANA1, TWL_DEV_GRP_P123, 1, 2),
+       TWL_REMAP_SLEEP(RES_VINTANA2, TWL_DEV_GRP_P123, 0, 2),
+       TWL_REMAP_SLEEP(RES_VINTDIG, TWL_DEV_GRP_P123, 1, 2),
+       TWL_REMAP_SLEEP(RES_VIO, TWL_DEV_GRP_P123, 2, 2),
+       TWL_REMAP_OFF(RES_VDD1, DEV_GRP_P1, 4, 1),
+       TWL_REMAP_OFF(RES_VDD2, DEV_GRP_P1, 3, 1),
+       TWL_REMAP_SLEEP(RES_VUSB_1V5, DEV_GRP_NULL, 0, 0),
+       TWL_REMAP_SLEEP(RES_VUSB_1V8, DEV_GRP_NULL, 0, 0),
+       TWL_REMAP_SLEEP(RES_VUSB_3V1, TWL_DEV_GRP_P123, 0, 0),
+       /* Resource #20 USB charge pump skipped */
+       TWL_REMAP_SLEEP(RES_REGEN, TWL_DEV_GRP_P123, 2, 1),
+       TWL_REMAP_SLEEP(RES_NRES_PWRON, TWL_DEV_GRP_P123, 0, 1),
+       TWL_REMAP_SLEEP(RES_CLKEN, TWL_DEV_GRP_P123, 3, 2),
+       TWL_REMAP_SLEEP(RES_SYSEN, TWL_DEV_GRP_P123, 6, 1),
+       TWL_REMAP_SLEEP(RES_HFCLKOUT, DEV_GRP_P3, 0, 2),
+       TWL_REMAP_SLEEP(RES_32KCLKOUT, TWL_DEV_GRP_P123, 0, 0),
+       TWL_REMAP_SLEEP(RES_RESET, TWL_DEV_GRP_P123, 6, 0),
+       TWL_REMAP_SLEEP(RES_MAIN_REF, TWL_DEV_GRP_P123, 0, 0),
+       { /* Terminator */ },
+};
+
+static struct twl4030_power_data omap3_idle = {
+       .scripts                = omap3_idle_scripts,
+       .num                    = ARRAY_SIZE(omap3_idle_scripts),
+       .resource_config        = omap3_idle_rconfig,
+};
+
+/* Disable 32 KiHz oscillator during idle */
+static struct twl4030_resconfig osc_off_rconfig[] = {
+       TWL_REMAP_OFF(RES_CLKEN, DEV_GRP_P1 | DEV_GRP_P3, 3, 2),
+       { /* Terminator */ },
+};
+
+static struct twl4030_power_data osc_off_idle = {
+       .scripts                = omap3_idle_scripts,
+       .num                    = ARRAY_SIZE(omap3_idle_scripts),
+       .resource_config        = omap3_idle_rconfig,
+       .board_config           = osc_off_rconfig,
+};
+
+static struct of_device_id twl4030_power_of_match[] = {
+       {
+               .compatible = "ti,twl4030-power-reset",
+               .data = &omap3_reset,
+       },
+       {
+               .compatible = "ti,twl4030-power-idle",
+               .data = &omap3_idle,
+       },
+       {
+               .compatible = "ti,twl4030-power-idle-osc-off",
+               .data = &osc_off_idle,
+       },
+       { },
+};
+MODULE_DEVICE_TABLE(of, twl4030_power_of_match);
+#endif /* CONFIG_OF */
+
 static int twl4030_power_probe(struct platform_device *pdev)
 {
-       struct twl4030_power_data *pdata = dev_get_platdata(&pdev->dev);
+       const struct twl4030_power_data *pdata = dev_get_platdata(&pdev->dev);
        struct device_node *node = pdev->dev.of_node;
+       const struct of_device_id *match;
        int err = 0;
        int err2 = 0;
        u8 val;
@@ -577,8 +817,12 @@ static int twl4030_power_probe(struct platform_device *pdev)
                return err;
        }
 
+       match = of_match_device(of_match_ptr(twl4030_power_of_match),
+                               &pdev->dev);
+       if (match && match->data)
+               pdata = match->data;
+
        if (pdata) {
-               /* TODO: convert to device tree */
                err = twl4030_power_configure_scripts(pdata);
                if (err) {
                        pr_err("TWL4030 failed to load scripts\n");
@@ -628,14 +872,6 @@ static int twl4030_power_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_OF
-static const struct of_device_id twl4030_power_of_match[] = {
-       {.compatible = "ti,twl4030-power", },
-       { },
-};
-MODULE_DEVICE_TABLE(of, twl4030_power_of_match);
-#endif
-
 static struct platform_driver twl4030_power_driver = {
        .driver = {
                .name   = "twl4030_power",
index 7fee22432e94e9d5d943b4ce913886f2f8783ffc..a5652548230a9457812a8badc69d7001a9df1f9c 100644 (file)
@@ -216,8 +216,7 @@ config MMC_SDHCI_SIRF
 config MMC_SDHCI_PXAV3
        tristate "Marvell MMP2 SD Host Controller support (PXAV3)"
        depends on CLKDEV_LOOKUP
-       select MMC_SDHCI
-       select MMC_SDHCI_PLTFM
+       depends on MMC_SDHCI_PLTFM
        default CPU_MMP2
        help
          This selects the Marvell(R) PXAV3 SD Host Controller.
@@ -229,8 +228,7 @@ config MMC_SDHCI_PXAV3
 config MMC_SDHCI_PXAV2
        tristate "Marvell PXA9XX SD Host Controller support (PXAV2)"
        depends on CLKDEV_LOOKUP
-       select MMC_SDHCI
-       select MMC_SDHCI_PLTFM
+       depends on MMC_SDHCI_PLTFM
        default CPU_PXA910
        help
          This selects the Marvell(R) PXAV2 SD Host Controller.
@@ -264,7 +262,7 @@ config MMC_SDHCI_S3C_DMA
 config MMC_SDHCI_BCM_KONA
        tristate "SDHCI support on Broadcom KONA platform"
        depends on ARCH_BCM_MOBILE
-       select MMC_SDHCI_PLTFM
+       depends on MMC_SDHCI_PLTFM
        help
          This selects the Broadcom Kona Secure Digital Host Controller
          Interface(SDHCI) support.
@@ -295,7 +293,7 @@ config MMC_MOXART
 config MMC_OMAP
        tristate "TI OMAP Multimedia Card Interface support"
        depends on ARCH_OMAP
-       select TPS65010 if MACH_OMAP_H2
+       depends on TPS65010 || !MACH_OMAP_H2
        help
          This selects the TI OMAP Multimedia card Interface.
          If you have an OMAP board with a Multimedia Card slot,
index aece7cafbb9701c6dee629a622d156c74499dbc2..bb585d9409014e8bead8879acf5f7145e9e5790c 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/atmel-mci.h>
 #include <linux/atmel_pdc.h>
 
+#include <asm/cacheflush.h>
 #include <asm/io.h>
 #include <asm/unaligned.h>
 
index 0a87e56913411a9abab46c99b13053abee3d96af..cc8d4a6099cdc602071af7616e1451896e7f6959 100644 (file)
@@ -448,7 +448,6 @@ mmc_spi_command_send(struct mmc_spi_host *host,
 {
        struct scratch          *data = host->data;
        u8                      *cp = data->status;
-       u32                     arg = cmd->arg;
        int                     status;
        struct spi_transfer     *t;
 
@@ -465,14 +464,12 @@ mmc_spi_command_send(struct mmc_spi_host *host,
         * We init the whole buffer to all-ones, which is what we need
         * to write while we're reading (later) response data.
         */
-       memset(cp++, 0xff, sizeof(data->status));
+       memset(cp, 0xff, sizeof(data->status));
 
-       *cp++ = 0x40 | cmd->opcode;
-       *cp++ = (u8)(arg >> 24);
-       *cp++ = (u8)(arg >> 16);
-       *cp++ = (u8)(arg >> 8);
-       *cp++ = (u8)arg;
-       *cp++ = (crc7(0, &data->status[1], 5) << 1) | 0x01;
+       cp[1] = 0x40 | cmd->opcode;
+       put_unaligned_be32(cmd->arg, cp+2);
+       cp[6] = crc7_be(0, cp+1, 5) | 0x01;
+       cp += 7;
 
        /* Then, read up to 13 bytes (while writing all-ones):
         *  - N(CR) (== 1..8) bytes of all-ones
@@ -711,10 +708,7 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t,
         * so we have to cope with this situation and check the response
         * bit-by-bit. Arggh!!!
         */
-       pattern  = scratch->status[0] << 24;
-       pattern |= scratch->status[1] << 16;
-       pattern |= scratch->status[2] << 8;
-       pattern |= scratch->status[3];
+       pattern = get_unaligned_be32(scratch->status);
 
        /* First 3 bit of pattern are undefined */
        pattern |= 0xE0000000;
index 9377284f8544be955d01e3839250c848928380c6..6b4c5ad3b3939c6103871eb790d304d1a1ad4f4d 100644 (file)
@@ -79,11 +79,11 @@ static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data)
                unsigned long t = jiffies + HZ;
                unsigned int hw_state,  count = 0;
                do {
+                       hw_state = mvsd_read(MVSD_HW_STATE);
                        if (time_after(jiffies, t)) {
                                dev_warn(host->dev, "FIFO_EMPTY bit missing\n");
                                break;
                        }
-                       hw_state = mvsd_read(MVSD_HW_STATE);
                        count++;
                } while (!(hw_state & (1 << 13)));
                dev_dbg(host->dev, "*** wait for FIFO_EMPTY bit "
index acb0e9eb55f13cd90ee1c3454f5f36affdf8331b..40573a58486a165e659853b327b7c8e8f886863d 100644 (file)
@@ -468,6 +468,10 @@ MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match);
 
 static struct sdhci_ops sdhci_msm_ops = {
        .platform_execute_tuning = sdhci_msm_execute_tuning,
+       .reset = sdhci_reset,
+       .set_clock = sdhci_set_clock,
+       .set_bus_width = sdhci_set_bus_width,
+       .set_uhs_signaling = sdhci_set_uhs_signaling,
 };
 
 static int sdhci_msm_probe(struct platform_device *pdev)
index eb2bbbef19c63df01d95f0cfffd415cb77ef5b74..f0a39eb049af819cb5a15e35f4d9e6c62a66bd8e 100644 (file)
@@ -357,7 +357,7 @@ static void *usdhi6_sg_map(struct usdhi6_host *host)
 
        WARN(host->pg.page, "%p not properly unmapped!\n", host->pg.page);
        if (WARN(sg_dma_len(sg) % data->blksz,
-                "SG size %zd isn't a multiple of block size %zd\n",
+                "SG size %u isn't a multiple of block size %u\n",
                 sg_dma_len(sg), data->blksz))
                return NULL;
 
@@ -459,7 +459,7 @@ static void usdhi6_sg_advance(struct usdhi6_host *host)
        done = (host->page_idx << PAGE_SHIFT) + host->offset;
        total = host->sg->offset + sg_dma_len(host->sg);
 
-       dev_dbg(mmc_dev(host->mmc), "%s(): %zu of %zu @ %u\n", __func__,
+       dev_dbg(mmc_dev(host->mmc), "%s(): %zu of %zu @ %zu\n", __func__,
                done, total, host->offset);
 
        if (done < total && host->offset) {
@@ -489,7 +489,7 @@ static void usdhi6_sg_advance(struct usdhi6_host *host)
                host->sg = next;
 
                if (WARN(next && sg_dma_len(next) % data->blksz,
-                        "SG size %zd isn't a multiple of block size %zd\n",
+                        "SG size %u isn't a multiple of block size %u\n",
                         sg_dma_len(next), data->blksz))
                        data->error = -EINVAL;
 
@@ -896,7 +896,7 @@ static void usdhi6_request_done(struct usdhi6_host *host)
        struct mmc_data *data = mrq->data;
 
        if (WARN(host->pg.page || host->head_pg.page,
-                "Page %p or %p not unmapped: wait %u, CMD%d(%c) @ +0x%x %ux%u in SG%u!\n",
+                "Page %p or %p not unmapped: wait %u, CMD%d(%c) @ +0x%zx %ux%u in SG%u!\n",
                 host->pg.page, host->head_pg.page, host->wait, mrq->cmd->opcode,
                 data ? (data->flags & MMC_DATA_READ ? 'R' : 'W') : '-',
                 data ? host->offset : 0, data ? data->blocks : 0,
@@ -1666,7 +1666,7 @@ static void usdhi6_timeout_work(struct work_struct *work)
        case USDHI6_WAIT_FOR_READ:
        case USDHI6_WAIT_FOR_WRITE:
                dev_dbg(mmc_dev(host->mmc),
-                       "%c: page #%u @ +0x%x %ux%u in SG%u. Current SG %u bytes @ %u\n",
+                       "%c: page #%u @ +0x%zx %ux%u in SG%u. Current SG %u bytes @ %u\n",
                        data->flags & MMC_DATA_READ ? 'R' : 'W', host->page_idx,
                        host->offset, data->blocks, data->blksz, data->sg_len,
                        sg_dma_len(host->sg), host->sg->offset);
index dd5e1018d37b39e9301c9c43e01195f4f363a66c..91a169c44b390b29dc320b987b98acd804f5417e 100644 (file)
@@ -1608,8 +1608,8 @@ static ssize_t dps1_insert_key(struct device *dev,
 #define FLOOR_SYSFS(id) { \
        __ATTR(f##id##_dps0_is_keylocked, S_IRUGO, dps0_is_key_locked, NULL), \
        __ATTR(f##id##_dps1_is_keylocked, S_IRUGO, dps1_is_key_locked, NULL), \
-       __ATTR(f##id##_dps0_protection_key, S_IWUGO, NULL, dps0_insert_key), \
-       __ATTR(f##id##_dps1_protection_key, S_IWUGO, NULL, dps1_insert_key), \
+       __ATTR(f##id##_dps0_protection_key, S_IWUSR|S_IWGRP, NULL, dps0_insert_key), \
+       __ATTR(f##id##_dps1_protection_key, S_IWUSR|S_IWGRP, NULL, dps1_insert_key), \
 }
 
 static struct device_attribute doc_sys_attrs[DOC_MAX_NBFLOORS][4] = {
index 42e8a770e631c6eb4f98658cd9cef88d08ca1350..4f0d83648e5a5ad9f5f0260e21b01067f81099be 100644 (file)
@@ -575,12 +575,12 @@ static int alloc_device(struct nandsim *ns)
                cfile = filp_open(cache_file, O_CREAT | O_RDWR | O_LARGEFILE, 0600);
                if (IS_ERR(cfile))
                        return PTR_ERR(cfile);
-               if (!cfile->f_op->read && !cfile->f_op->aio_read) {
+               if (!(cfile->f_mode & FMODE_CAN_READ)) {
                        NS_ERR("alloc_device: cache file not readable\n");
                        err = -EINVAL;
                        goto err_close;
                }
-               if (!cfile->f_op->write && !cfile->f_op->aio_write) {
+               if (!(cfile->f_mode & FMODE_CAN_WRITE)) {
                        NS_ERR("alloc_device: cache file not writeable\n");
                        err = -EINVAL;
                        goto err_close;
index b667a51ed21517a3ee6cf2be6ab4c7e306a713a2..0dfeaf5da3f2c914e2fd43e40d4bf4f3d8aff079 100644 (file)
@@ -157,7 +157,7 @@ static inline struct aggregator *__get_first_agg(struct port *port)
 
        rcu_read_lock();
        first_slave = bond_first_slave_rcu(bond);
-       agg = first_slave ? &(SLAVE_AD_INFO(first_slave).aggregator) : NULL;
+       agg = first_slave ? &(SLAVE_AD_INFO(first_slave)->aggregator) : NULL;
        rcu_read_unlock();
 
        return agg;
@@ -192,7 +192,7 @@ static inline void __enable_port(struct port *port)
 {
        struct slave *slave = port->slave;
 
-       if ((slave->link == BOND_LINK_UP) && IS_UP(slave->dev))
+       if ((slave->link == BOND_LINK_UP) && bond_slave_is_up(slave))
                bond_set_slave_active_flags(slave, BOND_SLAVE_NOTIFY_LATER);
 }
 
@@ -241,7 +241,7 @@ static inline int __check_agg_selection_timer(struct port *port)
  */
 static inline void __get_state_machine_lock(struct port *port)
 {
-       spin_lock_bh(&(SLAVE_AD_INFO(port->slave).state_machine_lock));
+       spin_lock_bh(&(SLAVE_AD_INFO(port->slave)->state_machine_lock));
 }
 
 /**
@@ -250,7 +250,7 @@ static inline void __get_state_machine_lock(struct port *port)
  */
 static inline void __release_state_machine_lock(struct port *port)
 {
-       spin_unlock_bh(&(SLAVE_AD_INFO(port->slave).state_machine_lock));
+       spin_unlock_bh(&(SLAVE_AD_INFO(port->slave)->state_machine_lock));
 }
 
 /**
@@ -350,7 +350,7 @@ static u8 __get_duplex(struct port *port)
 static inline void __initialize_port_locks(struct slave *slave)
 {
        /* make sure it isn't called twice */
-       spin_lock_init(&(SLAVE_AD_INFO(slave).state_machine_lock));
+       spin_lock_init(&(SLAVE_AD_INFO(slave)->state_machine_lock));
 }
 
 /* Conversions */
@@ -688,8 +688,8 @@ static struct aggregator *__get_active_agg(struct aggregator *aggregator)
        struct slave *slave;
 
        bond_for_each_slave_rcu(bond, slave, iter)
-               if (SLAVE_AD_INFO(slave).aggregator.is_active)
-                       return &(SLAVE_AD_INFO(slave).aggregator);
+               if (SLAVE_AD_INFO(slave)->aggregator.is_active)
+                       return &(SLAVE_AD_INFO(slave)->aggregator);
 
        return NULL;
 }
@@ -1293,7 +1293,7 @@ static void ad_port_selection_logic(struct port *port)
        }
        /* search on all aggregators for a suitable aggregator for this port */
        bond_for_each_slave(bond, slave, iter) {
-               aggregator = &(SLAVE_AD_INFO(slave).aggregator);
+               aggregator = &(SLAVE_AD_INFO(slave)->aggregator);
 
                /* keep a free aggregator for later use(if needed) */
                if (!aggregator->lag_ports) {
@@ -1504,7 +1504,7 @@ static void ad_agg_selection_logic(struct aggregator *agg)
        best = (active && agg_device_up(active)) ? active : NULL;
 
        bond_for_each_slave_rcu(bond, slave, iter) {
-               agg = &(SLAVE_AD_INFO(slave).aggregator);
+               agg = &(SLAVE_AD_INFO(slave)->aggregator);
 
                agg->is_active = 0;
 
@@ -1549,7 +1549,7 @@ static void ad_agg_selection_logic(struct aggregator *agg)
                         best->slave ? best->slave->dev->name : "NULL");
 
                bond_for_each_slave_rcu(bond, slave, iter) {
-                       agg = &(SLAVE_AD_INFO(slave).aggregator);
+                       agg = &(SLAVE_AD_INFO(slave)->aggregator);
 
                        pr_debug("Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n",
                                 agg->aggregator_identifier, agg->num_of_ports,
@@ -1840,16 +1840,16 @@ void bond_3ad_bind_slave(struct slave *slave)
        struct aggregator *aggregator;
 
        /* check that the slave has not been initialized yet. */
-       if (SLAVE_AD_INFO(slave).port.slave != slave) {
+       if (SLAVE_AD_INFO(slave)->port.slave != slave) {
 
                /* port initialization */
-               port = &(SLAVE_AD_INFO(slave).port);
+               port = &(SLAVE_AD_INFO(slave)->port);
 
                ad_initialize_port(port, bond->params.lacp_fast);
 
                __initialize_port_locks(slave);
                port->slave = slave;
-               port->actor_port_number = SLAVE_AD_INFO(slave).id;
+               port->actor_port_number = SLAVE_AD_INFO(slave)->id;
                /* key is determined according to the link speed, duplex and user key(which
                 * is yet not supported)
                 */
@@ -1874,7 +1874,7 @@ void bond_3ad_bind_slave(struct slave *slave)
                __disable_port(port);
 
                /* aggregator initialization */
-               aggregator = &(SLAVE_AD_INFO(slave).aggregator);
+               aggregator = &(SLAVE_AD_INFO(slave)->aggregator);
 
                ad_initialize_agg(aggregator);
 
@@ -1903,8 +1903,8 @@ void bond_3ad_unbind_slave(struct slave *slave)
        struct slave *slave_iter;
        struct list_head *iter;
 
-       aggregator = &(SLAVE_AD_INFO(slave).aggregator);
-       port = &(SLAVE_AD_INFO(slave).port);
+       aggregator = &(SLAVE_AD_INFO(slave)->aggregator);
+       port = &(SLAVE_AD_INFO(slave)->port);
 
        /* if slave is null, the whole port is not initialized */
        if (!port->slave) {
@@ -1932,7 +1932,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
                    (aggregator->lag_ports->next_port_in_aggregator)) {
                        /* find new aggregator for the related port(s) */
                        bond_for_each_slave(bond, slave_iter, iter) {
-                               new_aggregator = &(SLAVE_AD_INFO(slave_iter).aggregator);
+                               new_aggregator = &(SLAVE_AD_INFO(slave_iter)->aggregator);
                                /* if the new aggregator is empty, or it is
                                 * connected to our port only
                                 */
@@ -2010,7 +2010,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
 
        /* find the aggregator that this port is connected to */
        bond_for_each_slave(bond, slave_iter, iter) {
-               temp_aggregator = &(SLAVE_AD_INFO(slave_iter).aggregator);
+               temp_aggregator = &(SLAVE_AD_INFO(slave_iter)->aggregator);
                prev_port = NULL;
                /* search the port in the aggregator's related ports */
                for (temp_port = temp_aggregator->lag_ports; temp_port;
@@ -2076,7 +2076,7 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
        if (BOND_AD_INFO(bond).agg_select_timer &&
            !(--BOND_AD_INFO(bond).agg_select_timer)) {
                slave = bond_first_slave_rcu(bond);
-               port = slave ? &(SLAVE_AD_INFO(slave).port) : NULL;
+               port = slave ? &(SLAVE_AD_INFO(slave)->port) : NULL;
 
                /* select the active aggregator for the bond */
                if (port) {
@@ -2094,7 +2094,7 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
 
        /* for each port run the state machines */
        bond_for_each_slave_rcu(bond, slave, iter) {
-               port = &(SLAVE_AD_INFO(slave).port);
+               port = &(SLAVE_AD_INFO(slave)->port);
                if (!port->slave) {
                        pr_warn_ratelimited("%s: Warning: Found an uninitialized port\n",
                                            bond->dev->name);
@@ -2155,7 +2155,7 @@ static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave,
 
        if (length >= sizeof(struct lacpdu)) {
 
-               port = &(SLAVE_AD_INFO(slave).port);
+               port = &(SLAVE_AD_INFO(slave)->port);
 
                if (!port->slave) {
                        pr_warn_ratelimited("%s: Warning: port of slave %s is uninitialized\n",
@@ -2212,7 +2212,7 @@ void bond_3ad_adapter_speed_changed(struct slave *slave)
 {
        struct port *port;
 
-       port = &(SLAVE_AD_INFO(slave).port);
+       port = &(SLAVE_AD_INFO(slave)->port);
 
        /* if slave is null, the whole port is not initialized */
        if (!port->slave) {
@@ -2245,7 +2245,7 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave)
 {
        struct port *port;
 
-       port = &(SLAVE_AD_INFO(slave).port);
+       port = &(SLAVE_AD_INFO(slave)->port);
 
        /* if slave is null, the whole port is not initialized */
        if (!port->slave) {
@@ -2279,7 +2279,7 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
 {
        struct port *port;
 
-       port = &(SLAVE_AD_INFO(slave).port);
+       port = &(SLAVE_AD_INFO(slave)->port);
 
        /* if slave is null, the whole port is not initialized */
        if (!port->slave) {
@@ -2347,7 +2347,7 @@ int bond_3ad_set_carrier(struct bonding *bond)
                ret = 0;
                goto out;
        }
-       active = __get_active_agg(&(SLAVE_AD_INFO(first_slave).aggregator));
+       active = __get_active_agg(&(SLAVE_AD_INFO(first_slave)->aggregator));
        if (active) {
                /* are enough slaves available to consider link up? */
                if (active->num_of_ports < bond->params.min_links) {
@@ -2384,7 +2384,7 @@ int __bond_3ad_get_active_agg_info(struct bonding *bond,
        struct port *port;
 
        bond_for_each_slave_rcu(bond, slave, iter) {
-               port = &(SLAVE_AD_INFO(slave).port);
+               port = &(SLAVE_AD_INFO(slave)->port);
                if (port->aggregator && port->aggregator->is_active) {
                        aggregator = port->aggregator;
                        break;
@@ -2440,22 +2440,22 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
                goto err_free;
        }
 
-       slave_agg_no = bond_xmit_hash(bond, skb, slaves_in_agg);
+       slave_agg_no = bond_xmit_hash(bond, skb) % slaves_in_agg;
        first_ok_slave = NULL;
 
        bond_for_each_slave_rcu(bond, slave, iter) {
-               agg = SLAVE_AD_INFO(slave).port.aggregator;
+               agg = SLAVE_AD_INFO(slave)->port.aggregator;
                if (!agg || agg->aggregator_identifier != agg_id)
                        continue;
 
                if (slave_agg_no >= 0) {
-                       if (!first_ok_slave && SLAVE_IS_OK(slave))
+                       if (!first_ok_slave && bond_slave_can_tx(slave))
                                first_ok_slave = slave;
                        slave_agg_no--;
                        continue;
                }
 
-               if (SLAVE_IS_OK(slave)) {
+               if (bond_slave_can_tx(slave)) {
                        bond_dev_queue_xmit(bond, skb, slave->dev);
                        goto out;
                }
@@ -2522,7 +2522,7 @@ void bond_3ad_update_lacp_rate(struct bonding *bond)
 
        lacp_fast = bond->params.lacp_fast;
        bond_for_each_slave(bond, slave, iter) {
-               port = &(SLAVE_AD_INFO(slave).port);
+               port = &(SLAVE_AD_INFO(slave)->port);
                __get_state_machine_lock(port);
                if (lacp_fast)
                        port->actor_oper_port_state |= AD_STATE_LACP_TIMEOUT;
index 93580a47cc548851a36d1ff4cb45e88636565a3e..76c0dade233f904324631dba34be0f370d193179 100644 (file)
@@ -229,7 +229,7 @@ static struct slave *tlb_get_least_loaded_slave(struct bonding *bond)
 
        /* Find the slave with the largest gap */
        bond_for_each_slave_rcu(bond, slave, iter) {
-               if (SLAVE_IS_OK(slave)) {
+               if (bond_slave_can_tx(slave)) {
                        long long gap = compute_gap(slave);
 
                        if (max_gap < gap) {
@@ -384,7 +384,7 @@ static struct slave *rlb_next_rx_slave(struct bonding *bond)
        bool found = false;
 
        bond_for_each_slave(bond, slave, iter) {
-               if (!SLAVE_IS_OK(slave))
+               if (!bond_slave_can_tx(slave))
                        continue;
                if (!found) {
                        if (!before || before->speed < slave->speed)
@@ -417,7 +417,7 @@ static struct slave *__rlb_next_rx_slave(struct bonding *bond)
        bool found = false;
 
        bond_for_each_slave_rcu(bond, slave, iter) {
-               if (!SLAVE_IS_OK(slave))
+               if (!bond_slave_can_tx(slave))
                        continue;
                if (!found) {
                        if (!before || before->speed < slave->speed)
@@ -755,7 +755,7 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond)
        /* Don't modify or load balance ARPs that do not originate locally
         * (e.g.,arrive via a bridge).
         */
-       if (!bond_slave_has_mac_rcu(bond, arp->mac_src))
+       if (!bond_slave_has_mac_rx(bond, arp->mac_src))
                return NULL;
 
        if (arp->op_code == htons(ARPOP_REPLY)) {
@@ -1039,11 +1039,14 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[],
        struct bonding *bond = bond_get_bond_by_slave(slave);
        struct net_device *upper;
        struct list_head *iter;
+       struct bond_vlan_tag tags[BOND_MAX_VLAN_ENCAP];
 
        /* send untagged */
        alb_send_lp_vid(slave, mac_addr, 0, 0);
 
-       /* loop through vlans and send one packet for each */
+       /* loop through all devices and see if we need to send a packet
+        * for that device.
+        */
        rcu_read_lock();
        netdev_for_each_all_upper_dev_rcu(bond->dev, upper, iter) {
                if (is_vlan_dev(upper) && vlan_get_encap_level(upper) == 0) {
@@ -1059,6 +1062,16 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[],
                                                vlan_dev_vlan_id(upper));
                        }
                }
+
+               /* If this is a macvlan device, then only send updates
+                * when strict_match is turned off.
+                */
+               if (netif_is_macvlan(upper) && !strict_match) {
+                       memset(tags, 0, sizeof(tags));
+                       bond_verify_device_path(bond->dev, upper, tags);
+                       alb_send_lp_vid(slave, upper->dev_addr,
+                                       tags[0].vlan_proto, tags[0].vlan_id);
+               }
        }
        rcu_read_unlock();
 }
@@ -1068,7 +1081,7 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[])
        struct net_device *dev = slave->dev;
        struct sockaddr s_addr;
 
-       if (slave->bond->params.mode == BOND_MODE_TLB) {
+       if (BOND_MODE(slave->bond) == BOND_MODE_TLB) {
                memcpy(dev->dev_addr, addr, dev->addr_len);
                return 0;
        }
@@ -1111,13 +1124,13 @@ static void alb_swap_mac_addr(struct slave *slave1, struct slave *slave2)
 static void alb_fasten_mac_swap(struct bonding *bond, struct slave *slave1,
                                struct slave *slave2)
 {
-       int slaves_state_differ = (SLAVE_IS_OK(slave1) != SLAVE_IS_OK(slave2));
+       int slaves_state_differ = (bond_slave_can_tx(slave1) != bond_slave_can_tx(slave2));
        struct slave *disabled_slave = NULL;
 
        ASSERT_RTNL();
 
        /* fasten the change in the switch */
-       if (SLAVE_IS_OK(slave1)) {
+       if (bond_slave_can_tx(slave1)) {
                alb_send_learning_packets(slave1, slave1->dev->dev_addr, false);
                if (bond->alb_info.rlb_enabled) {
                        /* inform the clients that the mac address
@@ -1129,7 +1142,7 @@ static void alb_fasten_mac_swap(struct bonding *bond, struct slave *slave1,
                disabled_slave = slave1;
        }
 
-       if (SLAVE_IS_OK(slave2)) {
+       if (bond_slave_can_tx(slave2)) {
                alb_send_learning_packets(slave2, slave2->dev->dev_addr, false);
                if (bond->alb_info.rlb_enabled) {
                        /* inform the clients that the mac address
@@ -1358,6 +1371,77 @@ void bond_alb_deinitialize(struct bonding *bond)
                rlb_deinitialize(bond);
 }
 
+static int bond_do_alb_xmit(struct sk_buff *skb, struct bonding *bond,
+               struct slave *tx_slave)
+{
+       struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
+       struct ethhdr *eth_data = eth_hdr(skb);
+
+       if (!tx_slave) {
+               /* unbalanced or unassigned, send through primary */
+               tx_slave = rcu_dereference(bond->curr_active_slave);
+               if (bond->params.tlb_dynamic_lb)
+                       bond_info->unbalanced_load += skb->len;
+       }
+
+       if (tx_slave && bond_slave_can_tx(tx_slave)) {
+               if (tx_slave != rcu_dereference(bond->curr_active_slave)) {
+                       ether_addr_copy(eth_data->h_source,
+                                       tx_slave->dev->dev_addr);
+               }
+
+               bond_dev_queue_xmit(bond, skb, tx_slave->dev);
+               goto out;
+       }
+
+       if (tx_slave && bond->params.tlb_dynamic_lb) {
+               _lock_tx_hashtbl(bond);
+               __tlb_clear_slave(bond, tx_slave, 0);
+               _unlock_tx_hashtbl(bond);
+       }
+
+       /* no suitable interface, frame not sent */
+       dev_kfree_skb_any(skb);
+out:
+       return NETDEV_TX_OK;
+}
+
+int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
+{
+       struct bonding *bond = netdev_priv(bond_dev);
+       struct ethhdr *eth_data;
+       struct slave *tx_slave = NULL;
+       u32 hash_index;
+
+       skb_reset_mac_header(skb);
+       eth_data = eth_hdr(skb);
+
+       /* Do not TX balance any multicast or broadcast */
+       if (!is_multicast_ether_addr(eth_data->h_dest)) {
+               switch (skb->protocol) {
+               case htons(ETH_P_IP):
+               case htons(ETH_P_IPX):
+                   /* In case of IPX, it will falback to L2 hash */
+               case htons(ETH_P_IPV6):
+                       hash_index = bond_xmit_hash(bond, skb);
+                       if (bond->params.tlb_dynamic_lb) {
+                               tx_slave = tlb_choose_channel(bond,
+                                                             hash_index & 0xFF,
+                                                             skb->len);
+                       } else {
+                               struct list_head *iter;
+                               int idx = hash_index % bond->slave_cnt;
+
+                               bond_for_each_slave_rcu(bond, tx_slave, iter)
+                                       if (--idx < 0)
+                                               break;
+                       }
+                       break;
+               }
+       }
+       return bond_do_alb_xmit(skb, bond, tx_slave);
+}
+
 int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
 {
        struct bonding *bond = netdev_priv(bond_dev);
@@ -1366,7 +1450,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
        struct slave *tx_slave = NULL;
        static const __be32 ip_bcast = htonl(0xffffffff);
        int hash_size = 0;
-       int do_tx_balance = 1;
+       bool do_tx_balance = true;
        u32 hash_index = 0;
        const u8 *hash_start = NULL;
        struct ipv6hdr *ip6hdr;
@@ -1381,7 +1465,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                if (ether_addr_equal_64bits(eth_data->h_dest, mac_bcast) ||
                    (iph->daddr == ip_bcast) ||
                    (iph->protocol == IPPROTO_IGMP)) {
-                       do_tx_balance = 0;
+                       do_tx_balance = false;
                        break;
                }
                hash_start = (char *)&(iph->daddr);
@@ -1393,7 +1477,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                 * that here just in case.
                 */
                if (ether_addr_equal_64bits(eth_data->h_dest, mac_bcast)) {
-                       do_tx_balance = 0;
+                       do_tx_balance = false;
                        break;
                }
 
@@ -1401,7 +1485,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                 * broadcasts in IPv4.
                 */
                if (ether_addr_equal_64bits(eth_data->h_dest, mac_v6_allmcast)) {
-                       do_tx_balance = 0;
+                       do_tx_balance = false;
                        break;
                }
 
@@ -1411,7 +1495,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                 */
                ip6hdr = ipv6_hdr(skb);
                if (ipv6_addr_any(&ip6hdr->saddr)) {
-                       do_tx_balance = 0;
+                       do_tx_balance = false;
                        break;
                }
 
@@ -1421,7 +1505,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
        case ETH_P_IPX:
                if (ipx_hdr(skb)->ipx_checksum != IPX_NO_CHECKSUM) {
                        /* something is wrong with this packet */
-                       do_tx_balance = 0;
+                       do_tx_balance = false;
                        break;
                }
 
@@ -1430,7 +1514,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                         * this family since it has an "ARP" like
                         * mechanism
                         */
-                       do_tx_balance = 0;
+                       do_tx_balance = false;
                        break;
                }
 
@@ -1438,12 +1522,12 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                hash_size = ETH_ALEN;
                break;
        case ETH_P_ARP:
-               do_tx_balance = 0;
+               do_tx_balance = false;
                if (bond_info->rlb_enabled)
                        tx_slave = rlb_arp_xmit(skb, bond);
                break;
        default:
-               do_tx_balance = 0;
+               do_tx_balance = false;
                break;
        }
 
@@ -1452,32 +1536,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                tx_slave = tlb_choose_channel(bond, hash_index, skb->len);
        }
 
-       if (!tx_slave) {
-               /* unbalanced or unassigned, send through primary */
-               tx_slave = rcu_dereference(bond->curr_active_slave);
-               bond_info->unbalanced_load += skb->len;
-       }
-
-       if (tx_slave && SLAVE_IS_OK(tx_slave)) {
-               if (tx_slave != rcu_dereference(bond->curr_active_slave)) {
-                       ether_addr_copy(eth_data->h_source,
-                                       tx_slave->dev->dev_addr);
-               }
-
-               bond_dev_queue_xmit(bond, skb, tx_slave->dev);
-               goto out;
-       }
-
-       if (tx_slave) {
-               _lock_tx_hashtbl(bond);
-               __tlb_clear_slave(bond, tx_slave, 0);
-               _unlock_tx_hashtbl(bond);
-       }
-
-       /* no suitable interface, frame not sent */
-       dev_kfree_skb_any(skb);
-out:
-       return NETDEV_TX_OK;
+       return bond_do_alb_xmit(skb, bond, tx_slave);
 }
 
 void bond_alb_monitor(struct work_struct *work)
@@ -1514,8 +1573,10 @@ void bond_alb_monitor(struct work_struct *work)
                        /* If updating current_active, use all currently
                         * user mac addreses (!strict_match).  Otherwise, only
                         * use mac of the slave device.
+                        * In RLB mode, we always use strict matches.
                         */
-                       strict_match = (slave != bond->curr_active_slave);
+                       strict_match = (slave != bond->curr_active_slave ||
+                                       bond_info->rlb_enabled);
                        alb_send_learning_packets(slave, slave->dev->dev_addr,
                                                  strict_match);
                }
@@ -1719,7 +1780,7 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
        /* in TLB mode, the slave might flip down/up with the old dev_addr,
         * and thus filter bond->dev_addr's packets, so force bond's mac
         */
-       if (bond->params.mode == BOND_MODE_TLB) {
+       if (BOND_MODE(bond) == BOND_MODE_TLB) {
                struct sockaddr sa;
                u8 tmp_addr[ETH_ALEN];
 
index e09dd4bfafffcf585b8f853f7661e2e416c58602..5fc76c01636cb6eb0e9e96d14fc0c79566741900 100644 (file)
@@ -175,6 +175,7 @@ void bond_alb_deinit_slave(struct bonding *bond, struct slave *slave);
 void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char link);
 void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave);
 int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev);
+int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev);
 void bond_alb_monitor(struct work_struct *);
 int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr);
 void bond_alb_clear_vlan(struct bonding *bond, unsigned short vlan_id);
index 2d3f7fa541ffe755fc1bf5f9e51aeaa464b4e032..658e761c4568dff39ef18db8c548f8ed349c8ee0 100644 (file)
@@ -23,7 +23,7 @@ static int bond_debug_rlb_hash_show(struct seq_file *m, void *v)
        struct rlb_client_info *client_info;
        u32 hash_index;
 
-       if (bond->params.mode != BOND_MODE_ALB)
+       if (BOND_MODE(bond) != BOND_MODE_ALB)
                return 0;
 
        seq_printf(m, "SourceIP        DestinationIP   "
index d3a67896d43541f0b8ebc0b4e193db945087bc56..04f35f960cb87a30a89a1308d248cf459b56e2ce 100644 (file)
@@ -343,7 +343,7 @@ static int bond_set_carrier(struct bonding *bond)
        if (!bond_has_slaves(bond))
                goto down;
 
-       if (bond->params.mode == BOND_MODE_8023AD)
+       if (BOND_MODE(bond) == BOND_MODE_8023AD)
                return bond_3ad_set_carrier(bond);
 
        bond_for_each_slave(bond, slave, iter) {
@@ -497,7 +497,7 @@ static int bond_set_promiscuity(struct bonding *bond, int inc)
        struct list_head *iter;
        int err = 0;
 
-       if (USES_PRIMARY(bond->params.mode)) {
+       if (bond_uses_primary(bond)) {
                /* write lock already acquired */
                if (bond->curr_active_slave) {
                        err = dev_set_promiscuity(bond->curr_active_slave->dev,
@@ -523,7 +523,7 @@ static int bond_set_allmulti(struct bonding *bond, int inc)
        struct list_head *iter;
        int err = 0;
 
-       if (USES_PRIMARY(bond->params.mode)) {
+       if (bond_uses_primary(bond)) {
                /* write lock already acquired */
                if (bond->curr_active_slave) {
                        err = dev_set_allmulti(bond->curr_active_slave->dev,
@@ -574,7 +574,7 @@ static void bond_hw_addr_flush(struct net_device *bond_dev,
        dev_uc_unsync(slave_dev, bond_dev);
        dev_mc_unsync(slave_dev, bond_dev);
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                /* del lacpdu mc addr from mc list */
                u8 lacpdu_multicast[ETH_ALEN] = MULTICAST_LACPDU_ADDR;
 
@@ -585,8 +585,8 @@ static void bond_hw_addr_flush(struct net_device *bond_dev,
 /*--------------------------- Active slave change ---------------------------*/
 
 /* 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
+ * old active slaves (if any).  Modes that are not using primary keep all
+ * slaves up date at all times; only the modes that use primary need to call
  * this function to swap these settings during a failover.
  */
 static void bond_hw_addr_swap(struct bonding *bond, struct slave *new_active,
@@ -747,7 +747,7 @@ static struct slave *bond_find_best_slave(struct bonding *bond)
        bond_for_each_slave(bond, slave, iter) {
                if (slave->link == BOND_LINK_UP)
                        return slave;
-               if (slave->link == BOND_LINK_BACK && IS_UP(slave->dev) &&
+               if (slave->link == BOND_LINK_BACK && bond_slave_is_up(slave) &&
                    slave->delay < mintime) {
                        mintime = slave->delay;
                        bestslave = slave;
@@ -801,7 +801,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
                new_active->last_link_up = jiffies;
 
                if (new_active->link == BOND_LINK_BACK) {
-                       if (USES_PRIMARY(bond->params.mode)) {
+                       if (bond_uses_primary(bond)) {
                                pr_info("%s: making interface %s the new active one %d ms earlier\n",
                                        bond->dev->name, new_active->dev->name,
                                        (bond->params.updelay - new_active->delay) * bond->params.miimon);
@@ -810,20 +810,20 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
                        new_active->delay = 0;
                        new_active->link = BOND_LINK_UP;
 
-                       if (bond->params.mode == BOND_MODE_8023AD)
+                       if (BOND_MODE(bond) == BOND_MODE_8023AD)
                                bond_3ad_handle_link_change(new_active, BOND_LINK_UP);
 
                        if (bond_is_lb(bond))
                                bond_alb_handle_link_change(bond, new_active, BOND_LINK_UP);
                } else {
-                       if (USES_PRIMARY(bond->params.mode)) {
+                       if (bond_uses_primary(bond)) {
                                pr_info("%s: making interface %s the new active one\n",
                                        bond->dev->name, new_active->dev->name);
                        }
                }
        }
 
-       if (USES_PRIMARY(bond->params.mode))
+       if (bond_uses_primary(bond))
                bond_hw_addr_swap(bond, new_active, old_active);
 
        if (bond_is_lb(bond)) {
@@ -838,7 +838,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
                rcu_assign_pointer(bond->curr_active_slave, new_active);
        }
 
-       if (bond->params.mode == BOND_MODE_ACTIVEBACKUP) {
+       if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) {
                if (old_active)
                        bond_set_slave_inactive_flags(old_active,
                                                      BOND_SLAVE_NOTIFY_NOW);
@@ -876,8 +876,8 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
         * resend only if bond is brought up with the affected
         * bonding modes and the retransmission is enabled */
        if (netif_running(bond->dev) && (bond->params.resend_igmp > 0) &&
-           ((USES_PRIMARY(bond->params.mode) && new_active) ||
-            bond->params.mode == BOND_MODE_ROUNDROBIN)) {
+           ((bond_uses_primary(bond) && new_active) ||
+            BOND_MODE(bond) == BOND_MODE_ROUNDROBIN)) {
                bond->igmp_retrans = bond->params.resend_igmp;
                queue_delayed_work(bond->wq, &bond->mcast_work, 1);
        }
@@ -958,7 +958,7 @@ static void bond_netpoll_cleanup(struct net_device *bond_dev)
        struct slave *slave;
 
        bond_for_each_slave(bond, slave, iter)
-               if (IS_UP(slave->dev))
+               if (bond_slave_is_up(slave))
                        slave_disable_netpoll(slave);
 }
 
@@ -1038,6 +1038,7 @@ static void bond_compute_features(struct bonding *bond)
 
        if (!bond_has_slaves(bond))
                goto done;
+       vlan_features &= NETIF_F_ALL_FOR_ALL;
 
        bond_for_each_slave(bond, slave, iter) {
                vlan_features = netdev_increment_features(vlan_features,
@@ -1084,7 +1085,7 @@ static bool bond_should_deliver_exact_match(struct sk_buff *skb,
                                            struct bonding *bond)
 {
        if (bond_is_slave_inactive(slave)) {
-               if (bond->params.mode == BOND_MODE_ALB &&
+               if (BOND_MODE(bond) == BOND_MODE_ALB &&
                    skb->pkt_type != PACKET_BROADCAST &&
                    skb->pkt_type != PACKET_MULTICAST)
                        return false;
@@ -1126,7 +1127,7 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb)
 
        skb->dev = bond->dev;
 
-       if (bond->params.mode == BOND_MODE_ALB &&
+       if (BOND_MODE(bond) == BOND_MODE_ALB &&
            bond->dev->priv_flags & IFF_BRIDGE_PORT &&
            skb->pkt_type == PACKET_HOST) {
 
@@ -1163,6 +1164,35 @@ static void bond_upper_dev_unlink(struct net_device *bond_dev,
        rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE, GFP_KERNEL);
 }
 
+static struct slave *bond_alloc_slave(struct bonding *bond)
+{
+       struct slave *slave = NULL;
+
+       slave = kzalloc(sizeof(struct slave), GFP_KERNEL);
+       if (!slave)
+               return NULL;
+
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
+               SLAVE_AD_INFO(slave) = kzalloc(sizeof(struct ad_slave_info),
+                                              GFP_KERNEL);
+               if (!SLAVE_AD_INFO(slave)) {
+                       kfree(slave);
+                       return NULL;
+               }
+       }
+       return slave;
+}
+
+static void bond_free_slave(struct slave *slave)
+{
+       struct bonding *bond = bond_get_bond_by_slave(slave);
+
+       if (BOND_MODE(bond) == BOND_MODE_8023AD)
+               kfree(SLAVE_AD_INFO(slave));
+
+       kfree(slave);
+}
+
 /* enslave device <slave> to bond device <master> */
 int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
 {
@@ -1269,7 +1299,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                if (!bond_has_slaves(bond)) {
                        pr_warn("%s: Warning: The first slave device specified does not support setting the MAC address\n",
                                bond_dev->name);
-                       if (bond->params.mode == BOND_MODE_ACTIVEBACKUP) {
+                       if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) {
                                bond->params.fail_over_mac = BOND_FOM_ACTIVE;
                                pr_warn("%s: Setting fail_over_mac to active for active-backup mode\n",
                                        bond_dev->name);
@@ -1290,11 +1320,14 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
            bond->dev->addr_assign_type == NET_ADDR_RANDOM)
                bond_set_dev_addr(bond->dev, slave_dev);
 
-       new_slave = kzalloc(sizeof(struct slave), GFP_KERNEL);
+       new_slave = bond_alloc_slave(bond);
        if (!new_slave) {
                res = -ENOMEM;
                goto err_undo_flags;
        }
+
+       new_slave->bond = bond;
+       new_slave->dev = slave_dev;
        /*
         * Set the new_slave's queue_id to be zero.  Queue ID mapping
         * is set via sysfs or module option if desired.
@@ -1317,7 +1350,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
        ether_addr_copy(new_slave->perm_hwaddr, slave_dev->dev_addr);
 
        if (!bond->params.fail_over_mac ||
-           bond->params.mode != BOND_MODE_ACTIVEBACKUP) {
+           BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) {
                /*
                 * Set slave to master's mac address.  The application already
                 * set the master's mac address to that of the first slave
@@ -1338,8 +1371,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                goto err_restore_mac;
        }
 
-       new_slave->bond = bond;
-       new_slave->dev = slave_dev;
        slave_dev->priv_flags |= IFF_BONDING;
 
        if (bond_is_lb(bond)) {
@@ -1351,10 +1382,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                        goto err_close;
        }
 
-       /* If the mode USES_PRIMARY, then the following is handled by
+       /* If the mode uses primary, then the following is handled by
         * bond_change_active_slave().
         */
-       if (!USES_PRIMARY(bond->params.mode)) {
+       if (!bond_uses_primary(bond)) {
                /* set promiscuity level to new slave */
                if (bond_dev->flags & IFF_PROMISC) {
                        res = dev_set_promiscuity(slave_dev, 1);
@@ -1377,7 +1408,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                netif_addr_unlock_bh(bond_dev);
        }
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                /* add lacpdu mc addr to mc list */
                u8 lacpdu_multicast[ETH_ALEN] = MULTICAST_LACPDU_ADDR;
 
@@ -1450,7 +1481,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                 new_slave->link == BOND_LINK_DOWN ? "DOWN" :
                 (new_slave->link == BOND_LINK_UP ? "UP" : "BACK"));
 
-       if (USES_PRIMARY(bond->params.mode) && bond->params.primary[0]) {
+       if (bond_uses_primary(bond) && bond->params.primary[0]) {
                /* if there is a primary slave, remember it */
                if (strcmp(bond->params.primary, new_slave->dev->name) == 0) {
                        bond->primary_slave = new_slave;
@@ -1458,7 +1489,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                }
        }
 
-       switch (bond->params.mode) {
+       switch (BOND_MODE(bond)) {
        case BOND_MODE_ACTIVEBACKUP:
                bond_set_slave_inactive_flags(new_slave,
                                              BOND_SLAVE_NOTIFY_NOW);
@@ -1471,14 +1502,14 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                bond_set_slave_inactive_flags(new_slave, BOND_SLAVE_NOTIFY_NOW);
                /* if this is the first slave */
                if (!prev_slave) {
-                       SLAVE_AD_INFO(new_slave).id = 1;
+                       SLAVE_AD_INFO(new_slave)->id = 1;
                        /* Initialize AD with the number of times that the AD timer is called in 1 second
                         * can be called only after the mac address of the bond is set
                         */
                        bond_3ad_initialize(bond, 1000/AD_TIMER_INTERVAL);
                } else {
-                       SLAVE_AD_INFO(new_slave).id =
-                               SLAVE_AD_INFO(prev_slave).id + 1;
+                       SLAVE_AD_INFO(new_slave)->id =
+                               SLAVE_AD_INFO(prev_slave)->id + 1;
                }
 
                bond_3ad_bind_slave(new_slave);
@@ -1539,7 +1570,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
        bond_compute_features(bond);
        bond_set_carrier(bond);
 
-       if (USES_PRIMARY(bond->params.mode)) {
+       if (bond_uses_primary(bond)) {
                block_netpoll_tx();
                write_lock_bh(&bond->curr_slave_lock);
                bond_select_active_slave(bond);
@@ -1563,7 +1594,7 @@ err_unregister:
        netdev_rx_handler_unregister(slave_dev);
 
 err_detach:
-       if (!USES_PRIMARY(bond->params.mode))
+       if (!bond_uses_primary(bond))
                bond_hw_addr_flush(bond_dev, slave_dev);
 
        vlan_vids_del_by_dev(slave_dev, bond_dev);
@@ -1585,7 +1616,7 @@ err_close:
 
 err_restore_mac:
        if (!bond->params.fail_over_mac ||
-           bond->params.mode != BOND_MODE_ACTIVEBACKUP) {
+           BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) {
                /* XXX TODO - fom follow mode needs to change master's
                 * MAC if this slave's MAC is in use by the bond, or at
                 * least print a warning.
@@ -1599,7 +1630,7 @@ err_restore_mtu:
        dev_set_mtu(slave_dev, new_slave->original_mtu);
 
 err_free:
-       kfree(new_slave);
+       bond_free_slave(new_slave);
 
 err_undo_flags:
        /* Enslave of first slave has failed and we need to fix master's mac */
@@ -1661,7 +1692,7 @@ static int __bond_release_one(struct net_device *bond_dev,
        write_lock_bh(&bond->lock);
 
        /* Inform AD package of unbinding of slave. */
-       if (bond->params.mode == BOND_MODE_8023AD)
+       if (BOND_MODE(bond) == BOND_MODE_8023AD)
                bond_3ad_unbind_slave(slave);
 
        write_unlock_bh(&bond->lock);
@@ -1676,7 +1707,7 @@ static int __bond_release_one(struct net_device *bond_dev,
        bond->current_arp_slave = NULL;
 
        if (!all && (!bond->params.fail_over_mac ||
-                    bond->params.mode != BOND_MODE_ACTIVEBACKUP)) {
+                    BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP)) {
                if (ether_addr_equal_64bits(bond_dev->dev_addr, slave->perm_hwaddr) &&
                    bond_has_slaves(bond))
                        pr_warn("%s: Warning: the permanent HWaddr of %s - %pM - is still in use by %s - set the HWaddr of %s to a different address to avoid conflicts\n",
@@ -1748,10 +1779,10 @@ static int __bond_release_one(struct net_device *bond_dev,
        /* must do this from outside any spinlocks */
        vlan_vids_del_by_dev(slave_dev, bond_dev);
 
-       /* If the mode USES_PRIMARY, then this cases was handled above by
+       /* If the mode uses primary, then this cases was handled above by
         * bond_change_active_slave(..., NULL)
         */
-       if (!USES_PRIMARY(bond->params.mode)) {
+       if (!bond_uses_primary(bond)) {
                /* unset promiscuity level from slave
                 * NOTE: The NETDEV_CHANGEADDR call above may change the value
                 * of the IFF_PROMISC flag in the bond_dev, but we need the
@@ -1775,7 +1806,7 @@ static int __bond_release_one(struct net_device *bond_dev,
        dev_close(slave_dev);
 
        if (bond->params.fail_over_mac != BOND_FOM_ACTIVE ||
-           bond->params.mode != BOND_MODE_ACTIVEBACKUP) {
+           BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) {
                /* restore original ("permanent") mac address */
                ether_addr_copy(addr.sa_data, slave->perm_hwaddr);
                addr.sa_family = slave_dev->type;
@@ -1786,7 +1817,7 @@ static int __bond_release_one(struct net_device *bond_dev,
 
        slave_dev->priv_flags &= ~IFF_BONDING;
 
-       kfree(slave);
+       bond_free_slave(slave);
 
        return 0;  /* deletion OK */
 }
@@ -1821,7 +1852,7 @@ static int bond_info_query(struct net_device *bond_dev, struct ifbond *info)
 {
        struct bonding *bond = netdev_priv(bond_dev);
 
-       info->bond_mode = bond->params.mode;
+       info->bond_mode = BOND_MODE(bond);
        info->miimon = bond->params.miimon;
 
        info->num_slaves = bond->slave_cnt;
@@ -1877,7 +1908,7 @@ static int bond_miimon_inspect(struct bonding *bond)
                        if (slave->delay) {
                                pr_info("%s: link status down for %sinterface %s, disabling it in %d ms\n",
                                        bond->dev->name,
-                                       (bond->params.mode ==
+                                       (BOND_MODE(bond) ==
                                         BOND_MODE_ACTIVEBACKUP) ?
                                        (bond_is_active_slave(slave) ?
                                         "active " : "backup ") : "",
@@ -1968,10 +1999,10 @@ static void bond_miimon_commit(struct bonding *bond)
                        slave->link = BOND_LINK_UP;
                        slave->last_link_up = jiffies;
 
-                       if (bond->params.mode == BOND_MODE_8023AD) {
+                       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                                /* prevent it from being the active one */
                                bond_set_backup_slave(slave);
-                       } else if (bond->params.mode != BOND_MODE_ACTIVEBACKUP) {
+                       } else if (BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) {
                                /* make it immediately active */
                                bond_set_active_slave(slave);
                        } else if (slave != bond->primary_slave) {
@@ -1985,7 +2016,7 @@ static void bond_miimon_commit(struct bonding *bond)
                                slave->duplex ? "full" : "half");
 
                        /* notify ad that the link status has changed */
-                       if (bond->params.mode == BOND_MODE_8023AD)
+                       if (BOND_MODE(bond) == BOND_MODE_8023AD)
                                bond_3ad_handle_link_change(slave, BOND_LINK_UP);
 
                        if (bond_is_lb(bond))
@@ -2004,15 +2035,15 @@ static void bond_miimon_commit(struct bonding *bond)
 
                        slave->link = BOND_LINK_DOWN;
 
-                       if (bond->params.mode == BOND_MODE_ACTIVEBACKUP ||
-                           bond->params.mode == BOND_MODE_8023AD)
+                       if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP ||
+                           BOND_MODE(bond) == BOND_MODE_8023AD)
                                bond_set_slave_inactive_flags(slave,
                                                              BOND_SLAVE_NOTIFY_NOW);
 
                        pr_info("%s: link status definitely down for interface %s, disabling it\n",
                                bond->dev->name, slave->dev->name);
 
-                       if (bond->params.mode == BOND_MODE_8023AD)
+                       if (BOND_MODE(bond) == BOND_MODE_8023AD)
                                bond_3ad_handle_link_change(slave,
                                                            BOND_LINK_DOWN);
 
@@ -2175,9 +2206,9 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op,
  * When the path is validated, collect any vlan information in the
  * path.
  */
-static bool bond_verify_device_path(struct net_device *start_dev,
-                                   struct net_device *end_dev,
-                                   struct bond_vlan_tag *tags)
+bool bond_verify_device_path(struct net_device *start_dev,
+                            struct net_device *end_dev,
+                            struct bond_vlan_tag *tags)
 {
        struct net_device *upper;
        struct list_head  *iter;
@@ -2287,8 +2318,8 @@ int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
        int alen, is_arp = skb->protocol == __cpu_to_be16(ETH_P_ARP);
 
        if (!slave_do_arp_validate(bond, slave)) {
-               if ((slave_do_arp_validate_only(bond, slave) && is_arp) ||
-                   !slave_do_arp_validate_only(bond, slave))
+               if ((slave_do_arp_validate_only(bond) && is_arp) ||
+                   !slave_do_arp_validate_only(bond))
                        slave->last_rx = jiffies;
                return RX_HANDLER_ANOTHER;
        } else if (!is_arp) {
@@ -2456,7 +2487,7 @@ static void bond_loadbalance_arp_mon(struct work_struct *work)
                 * do - all replies will be rx'ed on same link causing slaves
                 * to be unstable during low/no traffic periods
                 */
-               if (IS_UP(slave->dev))
+               if (bond_slave_is_up(slave))
                        bond_arp_send_all(bond, slave);
        }
 
@@ -2678,10 +2709,10 @@ static bool bond_ab_arp_probe(struct bonding *bond)
        bond_set_slave_inactive_flags(curr_arp_slave, BOND_SLAVE_NOTIFY_LATER);
 
        bond_for_each_slave_rcu(bond, slave, iter) {
-               if (!found && !before && IS_UP(slave->dev))
+               if (!found && !before && bond_slave_is_up(slave))
                        before = slave;
 
-               if (found && !new_slave && IS_UP(slave->dev))
+               if (found && !new_slave && bond_slave_is_up(slave))
                        new_slave = slave;
                /* if the link state is up at this point, we
                 * mark it down - this can happen if we have
@@ -2690,7 +2721,7 @@ static bool bond_ab_arp_probe(struct bonding *bond)
                 * one the current slave so it is still marked
                 * up when it is actually down
                 */
-               if (!IS_UP(slave->dev) && slave->link == BOND_LINK_UP) {
+               if (!bond_slave_is_up(slave) && slave->link == BOND_LINK_UP) {
                        slave->link = BOND_LINK_DOWN;
                        if (slave->link_failure_count < UINT_MAX)
                                slave->link_failure_count++;
@@ -2853,7 +2884,7 @@ static int bond_slave_netdev_event(unsigned long event,
 
                bond_update_speed_duplex(slave);
 
-               if (bond->params.mode == BOND_MODE_8023AD) {
+               if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                        if (old_speed != slave->speed)
                                bond_3ad_adapter_speed_changed(slave);
                        if (old_duplex != slave->duplex)
@@ -2881,7 +2912,7 @@ static int bond_slave_netdev_event(unsigned long event,
                break;
        case NETDEV_CHANGENAME:
                /* we don't care if we don't have primary set */
-               if (!USES_PRIMARY(bond->params.mode) ||
+               if (!bond_uses_primary(bond) ||
                    !bond->params.primary[0])
                        break;
 
@@ -3011,20 +3042,18 @@ static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb,
  * bond_xmit_hash - generate a hash value based on the xmit policy
  * @bond: bonding device
  * @skb: buffer to use for headers
- * @count: modulo value
  *
  * This function will extract the necessary headers from the skb buffer and use
  * them to generate a hash based on the xmit_policy set in the bonding device
- * which will be reduced modulo count before returning.
  */
-int bond_xmit_hash(struct bonding *bond, struct sk_buff *skb, int count)
+u32 bond_xmit_hash(struct bonding *bond, struct sk_buff *skb)
 {
        struct flow_keys flow;
        u32 hash;
 
        if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER2 ||
            !bond_flow_dissect(bond, skb, &flow))
-               return bond_eth_hash(skb) % count;
+               return bond_eth_hash(skb);
 
        if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER23 ||
            bond->params.xmit_policy == BOND_XMIT_POLICY_ENCAP23)
@@ -3035,7 +3064,7 @@ int bond_xmit_hash(struct bonding *bond, struct sk_buff *skb, int count)
        hash ^= (hash >> 16);
        hash ^= (hash >> 8);
 
-       return hash % count;
+       return hash;
 }
 
 /*-------------------------- Device entry points ----------------------------*/
@@ -3046,7 +3075,7 @@ static void bond_work_init_all(struct bonding *bond)
                          bond_resend_igmp_join_requests_delayed);
        INIT_DELAYED_WORK(&bond->alb_work, bond_alb_monitor);
        INIT_DELAYED_WORK(&bond->mii_work, bond_mii_monitor);
-       if (bond->params.mode == BOND_MODE_ACTIVEBACKUP)
+       if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP)
                INIT_DELAYED_WORK(&bond->arp_work, bond_activebackup_arp_mon);
        else
                INIT_DELAYED_WORK(&bond->arp_work, bond_loadbalance_arp_mon);
@@ -3073,7 +3102,7 @@ static int bond_open(struct net_device *bond_dev)
        if (bond_has_slaves(bond)) {
                read_lock(&bond->curr_slave_lock);
                bond_for_each_slave(bond, slave, iter) {
-                       if (USES_PRIMARY(bond->params.mode)
+                       if (bond_uses_primary(bond)
                                && (slave != bond->curr_active_slave)) {
                                bond_set_slave_inactive_flags(slave,
                                                              BOND_SLAVE_NOTIFY_NOW);
@@ -3092,9 +3121,10 @@ static int bond_open(struct net_device *bond_dev)
                /* bond_alb_initialize must be called before the timer
                 * is started.
                 */
-               if (bond_alb_initialize(bond, (bond->params.mode == BOND_MODE_ALB)))
+               if (bond_alb_initialize(bond, (BOND_MODE(bond) == BOND_MODE_ALB)))
                        return -ENOMEM;
-               queue_delayed_work(bond->wq, &bond->alb_work, 0);
+               if (bond->params.tlb_dynamic_lb)
+                       queue_delayed_work(bond->wq, &bond->alb_work, 0);
        }
 
        if (bond->params.miimon)  /* link check interval, in milliseconds. */
@@ -3105,7 +3135,7 @@ static int bond_open(struct net_device *bond_dev)
                bond->recv_probe = bond_arp_rcv;
        }
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                queue_delayed_work(bond->wq, &bond->ad_work, 0);
                /* register to receive LACPDUs */
                bond->recv_probe = bond_3ad_lacpdu_recv;
@@ -3310,7 +3340,7 @@ static void bond_set_rx_mode(struct net_device *bond_dev)
 
 
        rcu_read_lock();
-       if (USES_PRIMARY(bond->params.mode)) {
+       if (bond_uses_primary(bond)) {
                slave = rcu_dereference(bond->curr_active_slave);
                if (slave) {
                        dev_uc_sync(slave->dev, bond_dev);
@@ -3464,7 +3494,7 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
        struct list_head *iter;
        int res = 0;
 
-       if (bond->params.mode == BOND_MODE_ALB)
+       if (BOND_MODE(bond) == BOND_MODE_ALB)
                return bond_alb_set_mac_address(bond_dev, addr);
 
 
@@ -3475,7 +3505,7 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
         * Returning an error causes ifenslave to fail.
         */
        if (bond->params.fail_over_mac &&
-           bond->params.mode == BOND_MODE_ACTIVEBACKUP)
+           BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP)
                return 0;
 
        if (!is_valid_ether_addr(sa->sa_data))
@@ -3555,7 +3585,7 @@ static void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int sl
        /* Here we start from the slave with slave_id */
        bond_for_each_slave_rcu(bond, slave, iter) {
                if (--i < 0) {
-                       if (slave_can_tx(slave)) {
+                       if (bond_slave_can_tx(slave)) {
                                bond_dev_queue_xmit(bond, skb, slave->dev);
                                return;
                        }
@@ -3567,7 +3597,7 @@ static void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int sl
        bond_for_each_slave_rcu(bond, slave, iter) {
                if (--i < 0)
                        break;
-               if (slave_can_tx(slave)) {
+               if (bond_slave_can_tx(slave)) {
                        bond_dev_queue_xmit(bond, skb, slave->dev);
                        return;
                }
@@ -3624,7 +3654,7 @@ static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *bond_dev
         */
        if (iph->protocol == IPPROTO_IGMP && skb->protocol == htons(ETH_P_IP)) {
                slave = rcu_dereference(bond->curr_active_slave);
-               if (slave && slave_can_tx(slave))
+               if (slave && bond_slave_can_tx(slave))
                        bond_dev_queue_xmit(bond, skb, slave->dev);
                else
                        bond_xmit_slave_id(bond, skb, 0);
@@ -3662,7 +3692,7 @@ static int bond_xmit_xor(struct sk_buff *skb, struct net_device *bond_dev)
 {
        struct bonding *bond = netdev_priv(bond_dev);
 
-       bond_xmit_slave_id(bond, skb, bond_xmit_hash(bond, skb, bond->slave_cnt));
+       bond_xmit_slave_id(bond, skb, bond_xmit_hash(bond, skb) % bond->slave_cnt);
 
        return NETDEV_TX_OK;
 }
@@ -3677,7 +3707,7 @@ static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev)
        bond_for_each_slave_rcu(bond, slave, iter) {
                if (bond_is_last_slave(bond, slave))
                        break;
-               if (IS_UP(slave->dev) && slave->link == BOND_LINK_UP) {
+               if (bond_slave_is_up(slave) && slave->link == BOND_LINK_UP) {
                        struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
 
                        if (!skb2) {
@@ -3689,7 +3719,7 @@ static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev)
                        bond_dev_queue_xmit(bond, skb2, slave->dev);
                }
        }
-       if (slave && IS_UP(slave->dev) && slave->link == BOND_LINK_UP)
+       if (slave && bond_slave_is_up(slave) && slave->link == BOND_LINK_UP)
                bond_dev_queue_xmit(bond, skb, slave->dev);
        else
                dev_kfree_skb_any(skb);
@@ -3714,7 +3744,7 @@ static inline int bond_slave_override(struct bonding *bond,
        /* Find out if any slaves have the same mapping as this skb. */
        bond_for_each_slave_rcu(bond, slave, iter) {
                if (slave->queue_id == skb->queue_mapping) {
-                       if (slave_can_tx(slave)) {
+                       if (bond_slave_can_tx(slave)) {
                                bond_dev_queue_xmit(bond, skb, slave->dev);
                                return 0;
                        }
@@ -3755,12 +3785,11 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev
 {
        struct bonding *bond = netdev_priv(dev);
 
-       if (TX_QUEUE_OVERRIDE(bond->params.mode)) {
-               if (!bond_slave_override(bond, skb))
-                       return NETDEV_TX_OK;
-       }
+       if (bond_should_override_tx_queue(bond) &&
+           !bond_slave_override(bond, skb))
+               return NETDEV_TX_OK;
 
-       switch (bond->params.mode) {
+       switch (BOND_MODE(bond)) {
        case BOND_MODE_ROUNDROBIN:
                return bond_xmit_roundrobin(skb, dev);
        case BOND_MODE_ACTIVEBACKUP:
@@ -3772,12 +3801,13 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev
        case BOND_MODE_8023AD:
                return bond_3ad_xmit_xor(skb, dev);
        case BOND_MODE_ALB:
-       case BOND_MODE_TLB:
                return bond_alb_xmit(skb, dev);
+       case BOND_MODE_TLB:
+               return bond_tlb_xmit(skb, dev);
        default:
                /* Should never happen, mode already checked */
                pr_err("%s: Error: Unknown bonding mode %d\n",
-                      dev->name, bond->params.mode);
+                      dev->name, BOND_MODE(bond));
                WARN_ON_ONCE(1);
                dev_kfree_skb_any(skb);
                return NETDEV_TX_OK;
@@ -3817,14 +3847,14 @@ static int bond_ethtool_get_settings(struct net_device *bond_dev,
        ecmd->duplex = DUPLEX_UNKNOWN;
        ecmd->port = PORT_OTHER;
 
-       /* Since SLAVE_IS_OK returns false for all inactive or down slaves, we
+       /* Since bond_slave_can_tx returns false for all inactive or down slaves, we
         * do not need to check mode.  Though link speed might not represent
         * the true receive or transmit bandwidth (not all modes are symmetric)
         * this is an accurate maximum.
         */
        read_lock(&bond->lock);
        bond_for_each_slave(bond, slave, iter) {
-               if (SLAVE_IS_OK(slave)) {
+               if (bond_slave_can_tx(slave)) {
                        if (slave->speed != SPEED_UNKNOWN)
                                speed += slave->speed;
                        if (ecmd->duplex == DUPLEX_UNKNOWN &&
@@ -3915,7 +3945,7 @@ void bond_setup(struct net_device *bond_dev)
        /* Initialize the device options */
        bond_dev->tx_queue_len = 0;
        bond_dev->flags |= IFF_MASTER|IFF_MULTICAST;
-       bond_dev->priv_flags |= IFF_BONDING;
+       bond_dev->priv_flags |= IFF_BONDING | IFF_UNICAST_FLT;
        bond_dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
 
        /* At first, we block adding VLANs. That's the only way to
@@ -3994,7 +4024,8 @@ static int bond_check_params(struct bond_params *params)
 
        if (xmit_hash_policy) {
                if ((bond_mode != BOND_MODE_XOR) &&
-                   (bond_mode != BOND_MODE_8023AD)) {
+                   (bond_mode != BOND_MODE_8023AD) &&
+                   (bond_mode != BOND_MODE_TLB)) {
                        pr_info("xmit_hash_policy param is irrelevant in mode %s\n",
                                bond_mode_name(bond_mode));
                } else {
@@ -4079,7 +4110,7 @@ static int bond_check_params(struct bond_params *params)
        }
 
        /* reset values for 802.3ad/TLB/ALB */
-       if (BOND_NO_USES_ARP(bond_mode)) {
+       if (!bond_mode_uses_arp(bond_mode)) {
                if (!miimon) {
                        pr_warn("Warning: miimon must be specified, otherwise bonding will not detect link failure, speed and duplex which are essential for 802.3ad operation\n");
                        pr_warn("Forcing miimon to 100msec\n");
@@ -4161,7 +4192,7 @@ static int bond_check_params(struct bond_params *params)
                   catch mistakes */
                __be32 ip;
                if (!in4_pton(arp_ip_target[i], -1, (u8 *)&ip, -1, NULL) ||
-                   IS_IP_TARGET_UNUSABLE_ADDRESS(ip)) {
+                   !bond_is_ip_target_ok(ip)) {
                        pr_warn("Warning: bad arp_ip_target module parameter (%s), ARP monitoring will not be performed\n",
                                arp_ip_target[i]);
                        arp_interval = 0;
@@ -4234,7 +4265,7 @@ static int bond_check_params(struct bond_params *params)
                pr_debug("Warning: either miimon or arp_interval and arp_ip_target module parameters must be specified, otherwise bonding will not detect link failures! see bonding.txt for details\n");
        }
 
-       if (primary && !USES_PRIMARY(bond_mode)) {
+       if (primary && !bond_mode_uses_primary(bond_mode)) {
                /* currently, using a primary only makes sense
                 * in active backup, TLB or ALB modes
                 */
@@ -4300,6 +4331,7 @@ static int bond_check_params(struct bond_params *params)
        params->min_links = min_links;
        params->lp_interval = lp_interval;
        params->packets_per_slave = packets_per_slave;
+       params->tlb_dynamic_lb = 1; /* Default value */
        if (packets_per_slave > 0) {
                params->reciprocal_packets_per_slave =
                        reciprocal_value(packets_per_slave);
index f847e165d252fb2a4528fe396b2c4f0553e81ac3..5ab3c1847e6760e2f3ef7d2ec35085c2d4bf655b 100644 (file)
@@ -56,10 +56,10 @@ static int bond_fill_slave_info(struct sk_buff *skb,
        if (nla_put_u16(skb, IFLA_BOND_SLAVE_QUEUE_ID, slave->queue_id))
                goto nla_put_failure;
 
-       if (slave->bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(slave->bond) == BOND_MODE_8023AD) {
                const struct aggregator *agg;
 
-               agg = SLAVE_AD_INFO(slave).port.aggregator;
+               agg = SLAVE_AD_INFO(slave)->port.aggregator;
                if (agg)
                        if (nla_put_u16(skb, IFLA_BOND_SLAVE_AD_AGGREGATOR_ID,
                                        agg->aggregator_identifier))
@@ -407,7 +407,7 @@ static int bond_fill_info(struct sk_buff *skb,
        unsigned int packets_per_slave;
        int i, targets_added;
 
-       if (nla_put_u8(skb, IFLA_BOND_MODE, bond->params.mode))
+       if (nla_put_u8(skb, IFLA_BOND_MODE, BOND_MODE(bond)))
                goto nla_put_failure;
 
        if (slave_dev &&
@@ -505,7 +505,7 @@ static int bond_fill_info(struct sk_buff *skb,
                       bond->params.ad_select))
                goto nla_put_failure;
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                struct ad_info info;
 
                if (!bond_3ad_get_active_agg_info(bond, &info)) {
index 832070298446458483cdf1132cc46b63ba323a7d..540e0167bf24992037165d82c5af307d85f15f02 100644 (file)
@@ -70,6 +70,8 @@ static int bond_option_mode_set(struct bonding *bond,
                                const struct bond_opt_value *newval);
 static int bond_option_slaves_set(struct bonding *bond,
                                  const struct bond_opt_value *newval);
+static int bond_option_tlb_dynamic_lb_set(struct bonding *bond,
+                                 const struct bond_opt_value *newval);
 
 
 static const struct bond_opt_value bond_mode_tbl[] = {
@@ -180,6 +182,12 @@ static const struct bond_opt_value bond_lp_interval_tbl[] = {
        { NULL,      -1,      0},
 };
 
+static const struct bond_opt_value bond_tlb_dynamic_lb_tbl[] = {
+       { "off", 0,  0},
+       { "on",  1,  BOND_VALFLAG_DEFAULT},
+       { NULL,  -1, 0}
+};
+
 static const struct bond_option bond_opts[] = {
        [BOND_OPT_MODE] = {
                .id = BOND_OPT_MODE,
@@ -200,7 +208,7 @@ static const struct bond_option bond_opts[] = {
        [BOND_OPT_XMIT_HASH] = {
                .id = BOND_OPT_XMIT_HASH,
                .name = "xmit_hash_policy",
-               .desc = "balance-xor and 802.3ad hashing method",
+               .desc = "balance-xor, 802.3ad, and tlb hashing method",
                .values = bond_xmit_hashtype_tbl,
                .set = bond_option_xmit_hash_policy_set
        },
@@ -365,9 +373,33 @@ static const struct bond_option bond_opts[] = {
                .flags = BOND_OPTFLAG_RAWVAL,
                .set = bond_option_slaves_set
        },
+       [BOND_OPT_TLB_DYNAMIC_LB] = {
+               .id = BOND_OPT_TLB_DYNAMIC_LB,
+               .name = "tlb_dynamic_lb",
+               .desc = "Enable dynamic flow shuffling",
+               .unsuppmodes = BOND_MODE_ALL_EX(BIT(BOND_MODE_TLB)),
+               .values = bond_tlb_dynamic_lb_tbl,
+               .flags = BOND_OPTFLAG_IFDOWN,
+               .set = bond_option_tlb_dynamic_lb_set,
+       },
        { }
 };
 
+/* Searches for an option by name */
+const struct bond_option *bond_opt_get_by_name(const char *name)
+{
+       const struct bond_option *opt;
+       int option;
+
+       for (option = 0; option < BOND_OPT_LAST; option++) {
+               opt = bond_opt_get(option);
+               if (opt && !strcmp(opt->name, name))
+                       return opt;
+       }
+
+       return NULL;
+}
+
 /* Searches for a value in opt's values[] table */
 const struct bond_opt_value *bond_opt_get_val(unsigned int option, u64 val)
 {
@@ -641,7 +673,7 @@ const struct bond_option *bond_opt_get(unsigned int option)
 
 int bond_option_mode_set(struct bonding *bond, const struct bond_opt_value *newval)
 {
-       if (BOND_NO_USES_ARP(newval->value) && bond->params.arp_interval) {
+       if (!bond_mode_uses_arp(newval->value) && bond->params.arp_interval) {
                pr_info("%s: %s mode is incompatible with arp monitoring, start mii monitoring\n",
                        bond->dev->name, newval->string);
                /* disable arp monitoring */
@@ -662,7 +694,7 @@ int bond_option_mode_set(struct bonding *bond, const struct bond_opt_value *newv
 static struct net_device *__bond_option_active_slave_get(struct bonding *bond,
                                                         struct slave *slave)
 {
-       return USES_PRIMARY(bond->params.mode) && slave ? slave->dev : NULL;
+       return bond_uses_primary(bond) && slave ? slave->dev : NULL;
 }
 
 struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond)
@@ -727,7 +759,7 @@ static int bond_option_active_slave_set(struct bonding *bond,
                                bond->dev->name, new_active->dev->name);
                } else {
                        if (old_active && (new_active->link == BOND_LINK_UP) &&
-                           IS_UP(new_active->dev)) {
+                           bond_slave_is_up(new_active)) {
                                pr_info("%s: Setting %s as active slave\n",
                                        bond->dev->name, new_active->dev->name);
                                bond_change_active_slave(bond, new_active);
@@ -746,6 +778,10 @@ static int bond_option_active_slave_set(struct bonding *bond,
        return ret;
 }
 
+/* There are two tricky bits here.  First, if MII monitoring is activated, then
+ * we must disable ARP monitoring.  Second, if the timer isn't running, we must
+ * start it.
+ */
 static int bond_option_miimon_set(struct bonding *bond,
                                  const struct bond_opt_value *newval)
 {
@@ -784,6 +820,10 @@ static int bond_option_miimon_set(struct bonding *bond,
        return 0;
 }
 
+/* Set up and down delays. These must be multiples of the
+ * MII monitoring value, and are stored internally as the multiplier.
+ * Thus, we must translate to MS for the real world.
+ */
 static int bond_option_updelay_set(struct bonding *bond,
                                   const struct bond_opt_value *newval)
 {
@@ -842,6 +882,10 @@ static int bond_option_use_carrier_set(struct bonding *bond,
        return 0;
 }
 
+/* There are two tricky bits here.  First, if ARP monitoring is activated, then
+ * we must disable MII monitoring.  Second, if the ARP timer isn't running,
+ * we must start it.
+ */
 static int bond_option_arp_interval_set(struct bonding *bond,
                                        const struct bond_opt_value *newval)
 {
@@ -899,7 +943,7 @@ static int _bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
        __be32 *targets = bond->params.arp_targets;
        int ind;
 
-       if (IS_IP_TARGET_UNUSABLE_ADDRESS(target)) {
+       if (!bond_is_ip_target_ok(target)) {
                pr_err("%s: invalid ARP target %pI4 specified for addition\n",
                       bond->dev->name, &target);
                return -EINVAL;
@@ -944,7 +988,7 @@ static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
        unsigned long *targets_rx;
        int ind, i;
 
-       if (IS_IP_TARGET_UNUSABLE_ADDRESS(target)) {
+       if (!bond_is_ip_target_ok(target)) {
                pr_err("%s: invalid ARP target %pI4 specified for removal\n",
                       bond->dev->name, &target);
                return -EINVAL;
@@ -1338,3 +1382,13 @@ err_no_cmd:
        ret = -EPERM;
        goto out;
 }
+
+static int bond_option_tlb_dynamic_lb_set(struct bonding *bond,
+                                         const struct bond_opt_value *newval)
+{
+       pr_info("%s: Setting dynamic-lb to %s (%llu)\n",
+               bond->dev->name, newval->string, newval->value);
+       bond->params.tlb_dynamic_lb = newval->value;
+
+       return 0;
+}
index 12be9e1bfb0c0d048229a1698c2b384847fbd794..17ded5b291761ca9e85e2fa6c82514a4613815b5 100644 (file)
@@ -62,6 +62,7 @@ enum {
        BOND_OPT_RESEND_IGMP,
        BOND_OPT_LP_INTERVAL,
        BOND_OPT_SLAVES,
+       BOND_OPT_TLB_DYNAMIC_LB,
        BOND_OPT_LAST
 };
 
@@ -104,6 +105,7 @@ int bond_opt_tryset_rtnl(struct bonding *bond, unsigned int option, char *buf);
 const struct bond_opt_value *bond_opt_parse(const struct bond_option *opt,
                                            struct bond_opt_value *val);
 const struct bond_option *bond_opt_get(unsigned int option);
+const struct bond_option *bond_opt_get_by_name(const char *name);
 const struct bond_opt_value *bond_opt_get_val(unsigned int option, u64 val);
 
 /* This helper is used to initialize a bond_opt_value structure for parameter
index 013fdd0f45e94340917ee2aeecc8529d386862e2..b215b479bb3a6917ffd6d37f30f989996d176f5b 100644 (file)
@@ -72,9 +72,9 @@ static void bond_info_show_master(struct seq_file *seq)
        curr = rcu_dereference(bond->curr_active_slave);
 
        seq_printf(seq, "Bonding Mode: %s",
-                  bond_mode_name(bond->params.mode));
+                  bond_mode_name(BOND_MODE(bond)));
 
-       if (bond->params.mode == BOND_MODE_ACTIVEBACKUP &&
+       if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP &&
            bond->params.fail_over_mac) {
                optval = bond_opt_get_val(BOND_OPT_FAIL_OVER_MAC,
                                          bond->params.fail_over_mac);
@@ -83,15 +83,15 @@ static void bond_info_show_master(struct seq_file *seq)
 
        seq_printf(seq, "\n");
 
-       if (bond->params.mode == BOND_MODE_XOR ||
-               bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_XOR ||
+               BOND_MODE(bond) == BOND_MODE_8023AD) {
                optval = bond_opt_get_val(BOND_OPT_XMIT_HASH,
                                          bond->params.xmit_policy);
                seq_printf(seq, "Transmit Hash Policy: %s (%d)\n",
                           optval->string, bond->params.xmit_policy);
        }
 
-       if (USES_PRIMARY(bond->params.mode)) {
+       if (bond_uses_primary(bond)) {
                seq_printf(seq, "Primary Slave: %s",
                           (bond->primary_slave) ?
                           bond->primary_slave->dev->name : "None");
@@ -134,7 +134,7 @@ static void bond_info_show_master(struct seq_file *seq)
                seq_printf(seq, "\n");
        }
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                struct ad_info ad_info;
 
                seq_puts(seq, "\n802.3ad info\n");
@@ -188,9 +188,9 @@ static void bond_info_show_slave(struct seq_file *seq,
 
        seq_printf(seq, "Permanent HW addr: %pM\n", slave->perm_hwaddr);
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                const struct aggregator *agg
-                       = SLAVE_AD_INFO(slave).port.aggregator;
+                       = SLAVE_AD_INFO(slave)->port.aggregator;
 
                if (agg)
                        seq_printf(seq, "Aggregator ID: %d\n",
index 5f6babcfc26e6aa56881ef2dc24b1ed51e3d7539..daed52f68ce1614ec94772ad1628f09417e04bfe 100644 (file)
@@ -45,8 +45,7 @@
 #define to_dev(obj)    container_of(obj, struct device, kobj)
 #define to_bond(cd)    ((struct bonding *)(netdev_priv(to_net_dev(cd))))
 
-/*
- * "show" function for the bond_masters attribute.
+/* "show" function for the bond_masters attribute.
  * The class parameter is ignored.
  */
 static ssize_t bonding_show_bonds(struct class *cls,
@@ -88,14 +87,12 @@ static struct net_device *bond_get_by_name(struct bond_net *bn, const char *ifna
        return NULL;
 }
 
-/*
- * "store" function for the bond_masters attribute.  This is what
+/* "store" function for the bond_masters attribute.  This is what
  * creates and deletes entire bonds.
  *
  * The class parameter is ignored.
  *
  */
-
 static ssize_t bonding_store_bonds(struct class *cls,
                                   struct class_attribute *attr,
                                   const char *buffer, size_t count)
@@ -158,9 +155,26 @@ static const struct class_attribute class_attr_bonding_masters = {
        .store = bonding_store_bonds,
 };
 
-/*
- * Show the slaves in the current bond.
- */
+/* Generic "store" method for bonding sysfs option setting */
+static ssize_t bonding_sysfs_store_option(struct device *d,
+                                         struct device_attribute *attr,
+                                         const char *buffer, size_t count)
+{
+       struct bonding *bond = to_bond(d);
+       const struct bond_option *opt;
+       int ret;
+
+       opt = bond_opt_get_by_name(attr->attr.name);
+       if (WARN_ON(!opt))
+               return -ENOENT;
+       ret = bond_opt_tryset_rtnl(bond, opt->id, (char *)buffer);
+       if (!ret)
+               ret = count;
+
+       return ret;
+}
+
+/* Show the slaves in the current bond. */
 static ssize_t bonding_show_slaves(struct device *d,
                                   struct device_attribute *attr, char *buf)
 {
@@ -190,62 +204,24 @@ static ssize_t bonding_show_slaves(struct device *d,
 
        return res;
 }
-
-/*
- * 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.
- */
-static ssize_t bonding_store_slaves(struct device *d,
-                                   struct device_attribute *attr,
-                                   const char *buffer, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_SLAVES, (char *)buffer);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(slaves, S_IRUGO | S_IWUSR, bonding_show_slaves,
-                  bonding_store_slaves);
+                  bonding_sysfs_store_option);
 
-/*
- * Show and set the bonding mode.  The bond interface must be down to
- * change the mode.
- */
+/* Show the bonding mode. */
 static ssize_t bonding_show_mode(struct device *d,
                                 struct device_attribute *attr, char *buf)
 {
        struct bonding *bond = to_bond(d);
        const struct bond_opt_value *val;
 
-       val = bond_opt_get_val(BOND_OPT_MODE, bond->params.mode);
+       val = bond_opt_get_val(BOND_OPT_MODE, BOND_MODE(bond));
 
-       return sprintf(buf, "%s %d\n", val->string, bond->params.mode);
-}
-
-static ssize_t bonding_store_mode(struct device *d,
-                                 struct device_attribute *attr,
-                                 const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_MODE, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
+       return sprintf(buf, "%s %d\n", val->string, BOND_MODE(bond));
 }
 static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
-                  bonding_show_mode, bonding_store_mode);
+                  bonding_show_mode, bonding_sysfs_store_option);
 
-/*
- * Show and set the bonding transmit hash method.
- */
+/* Show the bonding transmit hash method. */
 static ssize_t bonding_show_xmit_hash(struct device *d,
                                      struct device_attribute *attr,
                                      char *buf)
@@ -257,26 +233,10 @@ static ssize_t bonding_show_xmit_hash(struct device *d,
 
        return sprintf(buf, "%s %d\n", val->string, bond->params.xmit_policy);
 }
-
-static ssize_t bonding_store_xmit_hash(struct device *d,
-                                      struct device_attribute *attr,
-                                      const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_XMIT_HASH, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(xmit_hash_policy, S_IRUGO | S_IWUSR,
-                  bonding_show_xmit_hash, bonding_store_xmit_hash);
+                  bonding_show_xmit_hash, bonding_sysfs_store_option);
 
-/*
- * Show and set arp_validate.
- */
+/* Show arp_validate. */
 static ssize_t bonding_show_arp_validate(struct device *d,
                                         struct device_attribute *attr,
                                         char *buf)
@@ -289,26 +249,10 @@ static ssize_t bonding_show_arp_validate(struct device *d,
 
        return sprintf(buf, "%s %d\n", val->string, bond->params.arp_validate);
 }
-
-static ssize_t bonding_store_arp_validate(struct device *d,
-                                         struct device_attribute *attr,
-                                         const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ARP_VALIDATE, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
-
 static DEVICE_ATTR(arp_validate, S_IRUGO | S_IWUSR, bonding_show_arp_validate,
-                  bonding_store_arp_validate);
-/*
- * Show and set arp_all_targets.
- */
+                  bonding_sysfs_store_option);
+
+/* Show arp_all_targets. */
 static ssize_t bonding_show_arp_all_targets(struct device *d,
                                         struct device_attribute *attr,
                                         char *buf)
@@ -321,28 +265,10 @@ static ssize_t bonding_show_arp_all_targets(struct device *d,
        return sprintf(buf, "%s %d\n",
                       val->string, bond->params.arp_all_targets);
 }
-
-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 ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ARP_ALL_TARGETS, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
-
 static DEVICE_ATTR(arp_all_targets, S_IRUGO | S_IWUSR,
-                  bonding_show_arp_all_targets, bonding_store_arp_all_targets);
+                  bonding_show_arp_all_targets, bonding_sysfs_store_option);
 
-/*
- * Show and store fail_over_mac.  User only allowed to change the
- * value when there are no slaves.
- */
+/* Show fail_over_mac. */
 static ssize_t bonding_show_fail_over_mac(struct device *d,
                                          struct device_attribute *attr,
                                          char *buf)
@@ -355,30 +281,10 @@ static ssize_t bonding_show_fail_over_mac(struct device *d,
 
        return sprintf(buf, "%s %d\n", val->string, bond->params.fail_over_mac);
 }
-
-static ssize_t bonding_store_fail_over_mac(struct device *d,
-                                          struct device_attribute *attr,
-                                          const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_FAIL_OVER_MAC, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
-
 static DEVICE_ATTR(fail_over_mac, S_IRUGO | S_IWUSR,
-                  bonding_show_fail_over_mac, bonding_store_fail_over_mac);
+                  bonding_show_fail_over_mac, bonding_sysfs_store_option);
 
-/*
- * Show and set the arp timer interval.  There are two tricky bits
- * here.  First, if ARP monitoring is activated, then we must disable
- * MII monitoring.  Second, if the ARP timer isn't running, we must
- * start it.
- */
+/* Show the arp timer interval. */
 static ssize_t bonding_show_arp_interval(struct device *d,
                                         struct device_attribute *attr,
                                         char *buf)
@@ -387,26 +293,10 @@ static ssize_t bonding_show_arp_interval(struct device *d,
 
        return sprintf(buf, "%d\n", bond->params.arp_interval);
 }
-
-static ssize_t bonding_store_arp_interval(struct device *d,
-                                         struct device_attribute *attr,
-                                         const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ARP_INTERVAL, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(arp_interval, S_IRUGO | S_IWUSR,
-                  bonding_show_arp_interval, bonding_store_arp_interval);
+                  bonding_show_arp_interval, bonding_sysfs_store_option);
 
-/*
- * Show and set the arp targets.
- */
+/* Show the arp targets. */
 static ssize_t bonding_show_arp_targets(struct device *d,
                                        struct device_attribute *attr,
                                        char *buf)
@@ -424,27 +314,10 @@ static ssize_t bonding_show_arp_targets(struct device *d,
 
        return res;
 }
+static DEVICE_ATTR(arp_ip_target, S_IRUGO | S_IWUSR,
+                  bonding_show_arp_targets, bonding_sysfs_store_option);
 
-static ssize_t bonding_store_arp_targets(struct device *d,
-                                        struct device_attribute *attr,
-                                        const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ARP_TARGETS, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
-static DEVICE_ATTR(arp_ip_target, S_IRUGO | S_IWUSR , bonding_show_arp_targets, bonding_store_arp_targets);
-
-/*
- * Show and set the up and down delays.  These must be multiples of the
- * MII monitoring value, and are stored internally as the multiplier.
- * Thus, we must translate to MS for the real world.
- */
+/* Show the up and down delays. */
 static ssize_t bonding_show_downdelay(struct device *d,
                                      struct device_attribute *attr,
                                      char *buf)
@@ -453,22 +326,8 @@ static ssize_t bonding_show_downdelay(struct device *d,
 
        return sprintf(buf, "%d\n", bond->params.downdelay * bond->params.miimon);
 }
-
-static ssize_t bonding_store_downdelay(struct device *d,
-                                      struct device_attribute *attr,
-                                      const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_DOWNDELAY, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(downdelay, S_IRUGO | S_IWUSR,
-                  bonding_show_downdelay, bonding_store_downdelay);
+                  bonding_show_downdelay, bonding_sysfs_store_option);
 
 static ssize_t bonding_show_updelay(struct device *d,
                                    struct device_attribute *attr,
@@ -479,27 +338,10 @@ static ssize_t bonding_show_updelay(struct device *d,
        return sprintf(buf, "%d\n", bond->params.updelay * bond->params.miimon);
 
 }
-
-static ssize_t bonding_store_updelay(struct device *d,
-                                    struct device_attribute *attr,
-                                    const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_UPDELAY, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(updelay, S_IRUGO | S_IWUSR,
-                  bonding_show_updelay, bonding_store_updelay);
+                  bonding_show_updelay, bonding_sysfs_store_option);
 
-/*
- * Show and set the LACP interval.  Interface must be down, and the mode
- * must be set to 802.3ad mode.
- */
+/* Show the LACP interval. */
 static ssize_t bonding_show_lacp(struct device *d,
                                 struct device_attribute *attr,
                                 char *buf)
@@ -511,22 +353,8 @@ static ssize_t bonding_show_lacp(struct device *d,
 
        return sprintf(buf, "%s %d\n", val->string, bond->params.lacp_fast);
 }
-
-static ssize_t bonding_store_lacp(struct device *d,
-                                 struct device_attribute *attr,
-                                 const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_LACP_RATE, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(lacp_rate, S_IRUGO | S_IWUSR,
-                  bonding_show_lacp, bonding_store_lacp);
+                  bonding_show_lacp, bonding_sysfs_store_option);
 
 static ssize_t bonding_show_min_links(struct device *d,
                                      struct device_attribute *attr,
@@ -536,22 +364,8 @@ static ssize_t bonding_show_min_links(struct device *d,
 
        return sprintf(buf, "%u\n", bond->params.min_links);
 }
-
-static ssize_t bonding_store_min_links(struct device *d,
-                                      struct device_attribute *attr,
-                                      const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_MINLINKS, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(min_links, S_IRUGO | S_IWUSR,
-                  bonding_show_min_links, bonding_store_min_links);
+                  bonding_show_min_links, bonding_sysfs_store_option);
 
 static ssize_t bonding_show_ad_select(struct device *d,
                                      struct device_attribute *attr,
@@ -564,27 +378,10 @@ static ssize_t bonding_show_ad_select(struct device *d,
 
        return sprintf(buf, "%s %d\n", val->string, bond->params.ad_select);
 }
-
-
-static ssize_t bonding_store_ad_select(struct device *d,
-                                      struct device_attribute *attr,
-                                      const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_AD_SELECT, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(ad_select, S_IRUGO | S_IWUSR,
-                  bonding_show_ad_select, bonding_store_ad_select);
+                  bonding_show_ad_select, bonding_sysfs_store_option);
 
-/*
- * Show and set the number of peer notifications to send after a failover event.
- */
+/* Show and set the number of peer notifications to send after a failover event. */
 static ssize_t bonding_show_num_peer_notif(struct device *d,
                                           struct device_attribute *attr,
                                           char *buf)
@@ -611,12 +408,7 @@ static DEVICE_ATTR(num_grat_arp, S_IRUGO | S_IWUSR,
 static DEVICE_ATTR(num_unsol_na, S_IRUGO | S_IWUSR,
                   bonding_show_num_peer_notif, bonding_store_num_peer_notif);
 
-/*
- * Show and set the MII monitor interval.  There are two tricky bits
- * here.  First, if MII monitoring is activated, then we must disable
- * ARP monitoring.  Second, if the timer isn't running, we must
- * start it.
- */
+/* Show the MII monitor interval. */
 static ssize_t bonding_show_miimon(struct device *d,
                                   struct device_attribute *attr,
                                   char *buf)
@@ -625,30 +417,10 @@ static ssize_t bonding_show_miimon(struct device *d,
 
        return sprintf(buf, "%d\n", bond->params.miimon);
 }
-
-static ssize_t bonding_store_miimon(struct device *d,
-                                   struct device_attribute *attr,
-                                   const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_MIIMON, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(miimon, S_IRUGO | S_IWUSR,
-                  bonding_show_miimon, bonding_store_miimon);
+                  bonding_show_miimon, bonding_sysfs_store_option);
 
-/*
- * Show and set the primary slave.  The store function is much
- * simpler than bonding_store_slaves function because it only needs to
- * handle one interface name.
- * The bond must be a mode that supports a primary for this be
- * set.
- */
+/* Show the primary slave. */
 static ssize_t bonding_show_primary(struct device *d,
                                    struct device_attribute *attr,
                                    char *buf)
@@ -661,26 +433,10 @@ static ssize_t bonding_show_primary(struct device *d,
 
        return count;
 }
-
-static ssize_t bonding_store_primary(struct device *d,
-                                    struct device_attribute *attr,
-                                    const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_PRIMARY, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(primary, S_IRUGO | S_IWUSR,
-                  bonding_show_primary, bonding_store_primary);
+                  bonding_show_primary, bonding_sysfs_store_option);
 
-/*
- * Show and set the primary_reselect flag.
- */
+/* Show the primary_reselect flag. */
 static ssize_t bonding_show_primary_reselect(struct device *d,
                                             struct device_attribute *attr,
                                             char *buf)
@@ -694,28 +450,10 @@ static ssize_t bonding_show_primary_reselect(struct device *d,
        return sprintf(buf, "%s %d\n",
                       val->string, bond->params.primary_reselect);
 }
-
-static ssize_t bonding_store_primary_reselect(struct device *d,
-                                             struct device_attribute *attr,
-                                             const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_PRIMARY_RESELECT,
-                                  (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(primary_reselect, S_IRUGO | S_IWUSR,
-                  bonding_show_primary_reselect,
-                  bonding_store_primary_reselect);
+                  bonding_show_primary_reselect, bonding_sysfs_store_option);
 
-/*
- * Show and set the use_carrier flag.
- */
+/* Show the use_carrier flag. */
 static ssize_t bonding_show_carrier(struct device *d,
                                    struct device_attribute *attr,
                                    char *buf)
@@ -724,27 +462,11 @@ static ssize_t bonding_show_carrier(struct device *d,
 
        return sprintf(buf, "%d\n", bond->params.use_carrier);
 }
-
-static ssize_t bonding_store_carrier(struct device *d,
-                                    struct device_attribute *attr,
-                                    const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_USE_CARRIER, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(use_carrier, S_IRUGO | S_IWUSR,
-                  bonding_show_carrier, bonding_store_carrier);
+                  bonding_show_carrier, bonding_sysfs_store_option);
 
 
-/*
- * Show and set currently active_slave.
- */
+/* Show currently active_slave. */
 static ssize_t bonding_show_active_slave(struct device *d,
                                         struct device_attribute *attr,
                                         char *buf)
@@ -761,27 +483,10 @@ static ssize_t bonding_show_active_slave(struct device *d,
 
        return count;
 }
-
-static ssize_t bonding_store_active_slave(struct device *d,
-                                         struct device_attribute *attr,
-                                         const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ACTIVE_SLAVE, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(active_slave, S_IRUGO | S_IWUSR,
-                  bonding_show_active_slave, bonding_store_active_slave);
-
+                  bonding_show_active_slave, bonding_sysfs_store_option);
 
-/*
- * Show link status of the bond interface.
- */
+/* Show link status of the bond interface. */
 static ssize_t bonding_show_mii_status(struct device *d,
                                       struct device_attribute *attr,
                                       char *buf)
@@ -792,9 +497,7 @@ static ssize_t bonding_show_mii_status(struct device *d,
 }
 static DEVICE_ATTR(mii_status, S_IRUGO, bonding_show_mii_status, NULL);
 
-/*
- * Show current 802.3ad aggregator ID.
- */
+/* Show current 802.3ad aggregator ID. */
 static ssize_t bonding_show_ad_aggregator(struct device *d,
                                          struct device_attribute *attr,
                                          char *buf)
@@ -802,7 +505,7 @@ static ssize_t bonding_show_ad_aggregator(struct device *d,
        int count = 0;
        struct bonding *bond = to_bond(d);
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                struct ad_info ad_info;
                count = sprintf(buf, "%d\n",
                                bond_3ad_get_active_agg_info(bond, &ad_info)
@@ -814,9 +517,7 @@ static ssize_t bonding_show_ad_aggregator(struct device *d,
 static DEVICE_ATTR(ad_aggregator, S_IRUGO, bonding_show_ad_aggregator, NULL);
 
 
-/*
- * Show number of active 802.3ad ports.
- */
+/* Show number of active 802.3ad ports. */
 static ssize_t bonding_show_ad_num_ports(struct device *d,
                                         struct device_attribute *attr,
                                         char *buf)
@@ -824,7 +525,7 @@ static ssize_t bonding_show_ad_num_ports(struct device *d,
        int count = 0;
        struct bonding *bond = to_bond(d);
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                struct ad_info ad_info;
                count = sprintf(buf, "%d\n",
                                bond_3ad_get_active_agg_info(bond, &ad_info)
@@ -836,9 +537,7 @@ static ssize_t bonding_show_ad_num_ports(struct device *d,
 static DEVICE_ATTR(ad_num_ports, S_IRUGO, bonding_show_ad_num_ports, NULL);
 
 
-/*
- * Show current 802.3ad actor key.
- */
+/* Show current 802.3ad actor key. */
 static ssize_t bonding_show_ad_actor_key(struct device *d,
                                         struct device_attribute *attr,
                                         char *buf)
@@ -846,7 +545,7 @@ static ssize_t bonding_show_ad_actor_key(struct device *d,
        int count = 0;
        struct bonding *bond = to_bond(d);
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                struct ad_info ad_info;
                count = sprintf(buf, "%d\n",
                                bond_3ad_get_active_agg_info(bond, &ad_info)
@@ -858,9 +557,7 @@ static ssize_t bonding_show_ad_actor_key(struct device *d,
 static DEVICE_ATTR(ad_actor_key, S_IRUGO, bonding_show_ad_actor_key, NULL);
 
 
-/*
- * Show current 802.3ad partner key.
- */
+/* Show current 802.3ad partner key. */
 static ssize_t bonding_show_ad_partner_key(struct device *d,
                                           struct device_attribute *attr,
                                           char *buf)
@@ -868,7 +565,7 @@ static ssize_t bonding_show_ad_partner_key(struct device *d,
        int count = 0;
        struct bonding *bond = to_bond(d);
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                struct ad_info ad_info;
                count = sprintf(buf, "%d\n",
                                bond_3ad_get_active_agg_info(bond, &ad_info)
@@ -880,9 +577,7 @@ static ssize_t bonding_show_ad_partner_key(struct device *d,
 static DEVICE_ATTR(ad_partner_key, S_IRUGO, bonding_show_ad_partner_key, NULL);
 
 
-/*
- * Show current 802.3ad partner mac.
- */
+/* Show current 802.3ad partner mac. */
 static ssize_t bonding_show_ad_partner_mac(struct device *d,
                                           struct device_attribute *attr,
                                           char *buf)
@@ -890,7 +585,7 @@ static ssize_t bonding_show_ad_partner_mac(struct device *d,
        int count = 0;
        struct bonding *bond = to_bond(d);
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                struct ad_info ad_info;
                if (!bond_3ad_get_active_agg_info(bond, &ad_info))
                        count = sprintf(buf, "%pM\n", ad_info.partner_system);
@@ -900,9 +595,7 @@ static ssize_t bonding_show_ad_partner_mac(struct device *d,
 }
 static DEVICE_ATTR(ad_partner_mac, S_IRUGO, bonding_show_ad_partner_mac, NULL);
 
-/*
- * Show the queue_ids of the slaves in the current bond.
- */
+/* Show the queue_ids of the slaves in the current bond. */
 static ssize_t bonding_show_queue_id(struct device *d,
                                     struct device_attribute *attr,
                                     char *buf)
@@ -933,31 +626,11 @@ static ssize_t bonding_show_queue_id(struct device *d,
 
        return res;
 }
-
-/*
- * Set the queue_ids of the  slaves in the current bond.  The bond
- * interface must be enslaved for this to work.
- */
-static ssize_t bonding_store_queue_id(struct device *d,
-                                     struct device_attribute *attr,
-                                     const char *buffer, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_QUEUE_ID, (char *)buffer);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(queue_id, S_IRUGO | S_IWUSR, bonding_show_queue_id,
-                  bonding_store_queue_id);
+                  bonding_sysfs_store_option);
 
 
-/*
- * Show and set the all_slaves_active flag.
- */
+/* Show the all_slaves_active flag. */
 static ssize_t bonding_show_slaves_active(struct device *d,
                                          struct device_attribute *attr,
                                          char *buf)
@@ -966,27 +639,10 @@ static ssize_t bonding_show_slaves_active(struct device *d,
 
        return sprintf(buf, "%d\n", bond->params.all_slaves_active);
 }
-
-static ssize_t bonding_store_slaves_active(struct device *d,
-                                          struct device_attribute *attr,
-                                          const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ALL_SLAVES_ACTIVE,
-                                  (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(all_slaves_active, S_IRUGO | S_IWUSR,
-                  bonding_show_slaves_active, bonding_store_slaves_active);
+                  bonding_show_slaves_active, bonding_sysfs_store_option);
 
-/*
- * Show and set the number of IGMP membership reports to send on link failure
- */
+/* Show the number of IGMP membership reports to send on link failure */
 static ssize_t bonding_show_resend_igmp(struct device *d,
                                        struct device_attribute *attr,
                                        char *buf)
@@ -995,23 +651,8 @@ static ssize_t bonding_show_resend_igmp(struct device *d,
 
        return sprintf(buf, "%d\n", bond->params.resend_igmp);
 }
-
-static ssize_t bonding_store_resend_igmp(struct device *d,
-                                        struct device_attribute *attr,
-                                        const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_RESEND_IGMP, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
-
 static DEVICE_ATTR(resend_igmp, S_IRUGO | S_IWUSR,
-                  bonding_show_resend_igmp, bonding_store_resend_igmp);
+                  bonding_show_resend_igmp, bonding_sysfs_store_option);
 
 
 static ssize_t bonding_show_lp_interval(struct device *d,
@@ -1019,25 +660,21 @@ static ssize_t bonding_show_lp_interval(struct device *d,
                                        char *buf)
 {
        struct bonding *bond = to_bond(d);
+
        return sprintf(buf, "%d\n", bond->params.lp_interval);
 }
+static DEVICE_ATTR(lp_interval, S_IRUGO | S_IWUSR,
+                  bonding_show_lp_interval, bonding_sysfs_store_option);
 
-static ssize_t bonding_store_lp_interval(struct device *d,
-                                        struct device_attribute *attr,
-                                        const char *buf, size_t count)
+static ssize_t bonding_show_tlb_dynamic_lb(struct device *d,
+                                          struct device_attribute *attr,
+                                          char *buf)
 {
        struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_LP_INTERVAL, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
+       return sprintf(buf, "%d\n", bond->params.tlb_dynamic_lb);
 }
-
-static DEVICE_ATTR(lp_interval, S_IRUGO | S_IWUSR,
-                  bonding_show_lp_interval, bonding_store_lp_interval);
+static DEVICE_ATTR(tlb_dynamic_lb, S_IRUGO | S_IWUSR,
+                  bonding_show_tlb_dynamic_lb, bonding_sysfs_store_option);
 
 static ssize_t bonding_show_packets_per_slave(struct device *d,
                                              struct device_attribute *attr,
@@ -1045,27 +682,11 @@ static ssize_t bonding_show_packets_per_slave(struct device *d,
 {
        struct bonding *bond = to_bond(d);
        unsigned int packets_per_slave = bond->params.packets_per_slave;
-       return sprintf(buf, "%u\n", packets_per_slave);
-}
-
-static ssize_t bonding_store_packets_per_slave(struct device *d,
-                                              struct device_attribute *attr,
-                                              const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
 
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_PACKETS_PER_SLAVE,
-                                  (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
+       return sprintf(buf, "%u\n", packets_per_slave);
 }
-
 static DEVICE_ATTR(packets_per_slave, S_IRUGO | S_IWUSR,
-                  bonding_show_packets_per_slave,
-                  bonding_store_packets_per_slave);
+                  bonding_show_packets_per_slave, bonding_sysfs_store_option);
 
 static struct attribute *per_bond_attrs[] = {
        &dev_attr_slaves.attr,
@@ -1099,6 +720,7 @@ static struct attribute *per_bond_attrs[] = {
        &dev_attr_min_links.attr,
        &dev_attr_lp_interval.attr,
        &dev_attr_packets_per_slave.attr,
+       &dev_attr_tlb_dynamic_lb.attr,
        NULL,
 };
 
@@ -1107,8 +729,7 @@ static struct attribute_group bonding_group = {
        .attrs = per_bond_attrs,
 };
 
-/*
- * Initialize sysfs.  This sets up the bonding_masters file in
+/* Initialize sysfs.  This sets up the bonding_masters file in
  * /sys/class/net.
  */
 int bond_create_sysfs(struct bond_net *bn)
@@ -1120,8 +741,7 @@ int bond_create_sysfs(struct bond_net *bn)
 
        ret = netdev_class_create_file_ns(&bn->class_attr_bonding_masters,
                                          bn->net);
-       /*
-        * Permit multiple loads of the module by ignoring failures to
+       /* Permit multiple loads of the module by ignoring failures to
         * create the bonding_masters sysfs file.  Bonding devices
         * created by second or subsequent loads of the module will
         * not be listed in, or controllable by, bonding_masters, but
@@ -1144,16 +764,13 @@ int bond_create_sysfs(struct bond_net *bn)
 
 }
 
-/*
- * Remove /sys/class/net/bonding_masters.
- */
+/* Remove /sys/class/net/bonding_masters. */
 void bond_destroy_sysfs(struct bond_net *bn)
 {
        netdev_class_remove_file_ns(&bn->class_attr_bonding_masters, bn->net);
 }
 
-/*
- * Initialize sysfs for each bond.  This sets up and registers
+/* Initialize sysfs for each bond.  This sets up and registers
  * the 'bondctl' directory for each individual bond under /sys/class/net.
  */
 void bond_prepare_sysfs_group(struct bonding *bond)
index 2e4eec5450c80b726a7bbf1a47ae169cdd18db4f..198677f58ce0af4134b2491e90e6774fa2ae17b4 100644 (file)
@@ -69,8 +69,8 @@ static ssize_t ad_aggregator_id_show(struct slave *slave, char *buf)
 {
        const struct aggregator *agg;
 
-       if (slave->bond->params.mode == BOND_MODE_8023AD) {
-               agg = SLAVE_AD_INFO(slave).port.aggregator;
+       if (BOND_MODE(slave->bond) == BOND_MODE_8023AD) {
+               agg = SLAVE_AD_INFO(slave)->port.aggregator;
                if (agg)
                        return sprintf(buf, "%d\n",
                                       agg->aggregator_identifier);
index 00bea320e3b50c1eaa75a712f7afbbe65ac146fb..0b4d9cde0b05e33a34e5deaec3b28126f1cba8e1 100644 (file)
 
 #define BOND_DEFAULT_MIIMON    100
 
-#define IS_UP(dev)                                        \
-             ((((dev)->flags & IFF_UP) == IFF_UP)      && \
-              netif_running(dev)                       && \
-              netif_carrier_ok(dev))
-
-/*
- * Checks whether slave is ready for transmit.
- */
-#define SLAVE_IS_OK(slave)                             \
-                   (((slave)->dev->flags & IFF_UP)  && \
-                    netif_running((slave)->dev)     && \
-                    ((slave)->link == BOND_LINK_UP) && \
-                    bond_is_active_slave(slave))
-
-
-#define USES_PRIMARY(mode)                             \
-               (((mode) == BOND_MODE_ACTIVEBACKUP) ||  \
-                ((mode) == BOND_MODE_TLB)          ||  \
-                ((mode) == BOND_MODE_ALB))
-
-#define BOND_NO_USES_ARP(mode)                         \
-               (((mode) == BOND_MODE_8023AD)   ||      \
-                ((mode) == BOND_MODE_TLB)      ||      \
-                ((mode) == BOND_MODE_ALB))
-
-#define TX_QUEUE_OVERRIDE(mode)                                \
-                       (((mode) == BOND_MODE_ACTIVEBACKUP) ||  \
-                        ((mode) == BOND_MODE_ROUNDROBIN))
-
-#define BOND_MODE_IS_LB(mode)                  \
-               (((mode) == BOND_MODE_TLB) ||   \
-                ((mode) == BOND_MODE_ALB))
-
-#define IS_IP_TARGET_UNUSABLE_ADDRESS(a)       \
-       ((htonl(INADDR_BROADCAST) == a) ||      \
-        ipv4_is_zeronet(a))
 /*
  * Less bad way to call ioctl from within the kernel; this needs to be
  * done some other way to get the call out of interrupt context.
@@ -90,6 +54,8 @@
        set_fs(fs);                     \
        res; })
 
+#define BOND_MODE(bond) ((bond)->params.mode)
+
 /* slave list primitives */
 #define bond_slave_list(bond) (&(bond)->dev->adj_list.lower)
 
@@ -175,6 +141,7 @@ struct bond_params {
        int resend_igmp;
        int lp_interval;
        int packets_per_slave;
+       int tlb_dynamic_lb;
        struct reciprocal_value reciprocal_packets_per_slave;
 };
 
@@ -183,8 +150,6 @@ struct bond_parm_tbl {
        int mode;
 };
 
-#define BOND_MAX_MODENAME_LEN 20
-
 struct slave {
        struct net_device *dev; /* first - useful for panic debug */
        struct bonding *bond; /* our master */
@@ -205,7 +170,7 @@ struct slave {
        u32    speed;
        u16    queue_id;
        u8     perm_hwaddr[ETH_ALEN];
-       struct ad_slave_info ad_info; /* HUGE - better to dynamically alloc */
+       struct ad_slave_info *ad_info;
        struct tlb_slave_info tlb_info;
 #ifdef CONFIG_NET_POLL_CONTROLLER
        struct netpoll *np;
@@ -285,14 +250,41 @@ static inline struct slave *bond_get_slave_by_dev(struct bonding *bond,
 
 static inline struct bonding *bond_get_bond_by_slave(struct slave *slave)
 {
-       if (!slave || !slave->bond)
-               return NULL;
        return slave->bond;
 }
 
+static inline bool bond_should_override_tx_queue(struct bonding *bond)
+{
+       return BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP ||
+              BOND_MODE(bond) == BOND_MODE_ROUNDROBIN;
+}
+
 static inline bool bond_is_lb(const struct bonding *bond)
 {
-       return BOND_MODE_IS_LB(bond->params.mode);
+       return BOND_MODE(bond) == BOND_MODE_TLB ||
+              BOND_MODE(bond) == BOND_MODE_ALB;
+}
+
+static inline bool bond_mode_uses_arp(int mode)
+{
+       return mode != BOND_MODE_8023AD && mode != BOND_MODE_TLB &&
+              mode != BOND_MODE_ALB;
+}
+
+static inline bool bond_mode_uses_primary(int mode)
+{
+       return mode == BOND_MODE_ACTIVEBACKUP || mode == BOND_MODE_TLB ||
+              mode == BOND_MODE_ALB;
+}
+
+static inline bool bond_uses_primary(struct bonding *bond)
+{
+       return bond_mode_uses_primary(BOND_MODE(bond));
+}
+
+static inline bool bond_slave_is_up(struct slave *slave)
+{
+       return netif_running(slave->dev) && netif_carrier_ok(slave->dev);
 }
 
 static inline void bond_set_active_slave(struct slave *slave)
@@ -365,6 +357,12 @@ static inline bool bond_is_active_slave(struct slave *slave)
        return !bond_slave_state(slave);
 }
 
+static inline bool bond_slave_can_tx(struct slave *slave)
+{
+       return bond_slave_is_up(slave) && slave->link == BOND_LINK_UP &&
+              bond_is_active_slave(slave);
+}
+
 #define BOND_PRI_RESELECT_ALWAYS       0
 #define BOND_PRI_RESELECT_BETTER       1
 #define BOND_PRI_RESELECT_FAILURE      2
@@ -396,12 +394,16 @@ static inline int slave_do_arp_validate(struct bonding *bond,
        return bond->params.arp_validate & (1 << bond_slave_state(slave));
 }
 
-static inline int slave_do_arp_validate_only(struct bonding *bond,
-                                            struct slave *slave)
+static inline int slave_do_arp_validate_only(struct bonding *bond)
 {
        return bond->params.arp_validate & BOND_ARP_FILTER;
 }
 
+static inline int bond_is_ip_target_ok(__be32 addr)
+{
+       return !ipv4_is_lbcast(addr) && !ipv4_is_zeronet(addr);
+}
+
 /* Get the oldest arp which we've received on this slave for bond's
  * arp_targets.
  */
@@ -479,16 +481,14 @@ static inline __be32 bond_confirm_addr(struct net_device *dev, __be32 dst, __be3
        return addr;
 }
 
-static inline bool slave_can_tx(struct slave *slave)
-{
-       if (IS_UP(slave->dev) && slave->link == BOND_LINK_UP &&
-           bond_is_active_slave(slave))
-               return true;
-       else
-               return false;
-}
-
-struct bond_net;
+struct bond_net {
+       struct net              *net;   /* Associated network namespace */
+       struct list_head        dev_list;
+#ifdef CONFIG_PROC_FS
+       struct proc_dir_entry   *proc_dir;
+#endif
+       struct class_attribute  class_attr_bonding_masters;
+};
 
 int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond, struct slave *slave);
 void bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev);
@@ -500,7 +500,7 @@ int bond_sysfs_slave_add(struct slave *slave);
 void bond_sysfs_slave_del(struct slave *slave);
 int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev);
 int bond_release(struct net_device *bond_dev, struct net_device *slave_dev);
-int bond_xmit_hash(struct bonding *bond, struct sk_buff *skb, int count);
+u32 bond_xmit_hash(struct bonding *bond, struct sk_buff *skb);
 void bond_select_active_slave(struct bonding *bond);
 void bond_change_active_slave(struct bonding *bond, struct slave *new_active);
 void bond_create_debugfs(void);
@@ -516,15 +516,9 @@ void bond_netlink_fini(void);
 struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond);
 struct net_device *bond_option_active_slave_get(struct bonding *bond);
 const char *bond_slave_link_status(s8 link);
-
-struct bond_net {
-       struct net *            net;    /* Associated network namespace */
-       struct list_head        dev_list;
-#ifdef CONFIG_PROC_FS
-       struct proc_dir_entry * proc_dir;
-#endif
-       struct class_attribute  class_attr_bonding_masters;
-};
+bool bond_verify_device_path(struct net_device *start_dev,
+                            struct net_device *end_dev,
+                            struct bond_vlan_tag *tags);
 
 #ifdef CONFIG_PROC_FS
 void bond_create_proc_entry(struct bonding *bond);
@@ -576,6 +570,27 @@ static inline struct slave *bond_slave_has_mac_rcu(struct bonding *bond,
        return NULL;
 }
 
+/* Caller must hold rcu_read_lock() for read */
+static inline bool bond_slave_has_mac_rx(struct bonding *bond, const u8 *mac)
+{
+       struct list_head *iter;
+       struct slave *tmp;
+       struct netdev_hw_addr *ha;
+
+       bond_for_each_slave_rcu(bond, tmp, iter)
+               if (ether_addr_equal_64bits(mac, tmp->dev->dev_addr))
+                       return true;
+
+       if (netdev_uc_empty(bond->dev))
+               return false;
+
+       netdev_for_each_uc_addr(ha, bond->dev)
+               if (ether_addr_equal_64bits(mac, ha->addr))
+                       return true;
+
+       return false;
+}
+
 /* 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
  */
index 9e7d95dae2c7038478d6efadddba81e2778f47a9..41688229c570eb92e9e79b2bee2bd5b9d6173e39 100644 (file)
@@ -65,7 +65,7 @@ config CAN_LEDS
 
 config CAN_AT91
        tristate "Atmel AT91 onchip CAN controller"
-       depends on ARM
+       depends on ARCH_AT91 || COMPILE_TEST
        ---help---
          This is a driver for the SoC CAN controller in Atmel's AT91SAM9263
          and AT91SAM9X5 processors.
@@ -77,12 +77,6 @@ config CAN_TI_HECC
          Driver for TI HECC (High End CAN Controller) module found on many
          TI devices. The device specifications are available from www.ti.com
 
-config CAN_MCP251X
-       tristate "Microchip MCP251x SPI CAN controllers"
-       depends on SPI && HAS_DMA
-       ---help---
-         Driver for the Microchip MCP251x SPI CAN controllers.
-
 config CAN_BFIN
        depends on BF534 || BF536 || BF537 || BF538 || BF539 || BF54x
        tristate "Analog Devices Blackfin on-chip CAN"
@@ -110,7 +104,7 @@ config CAN_FLEXCAN
 
 config PCH_CAN
        tristate "Intel EG20T PCH CAN controller"
-       depends on PCI
+       depends on PCI && (X86_32 || COMPILE_TEST)
        ---help---
          This driver is for PCH CAN of Topcliff (Intel EG20T PCH) which
          is an IOH for x86 embedded processor (Intel Atom E6xx series).
@@ -125,6 +119,24 @@ config CAN_GRCAN
          endian syntheses of the cores would need some modifications on
          the hardware level to work.
 
+config CAN_RCAR
+       tristate "Renesas R-Car CAN controller"
+       depends on ARM
+       ---help---
+         Say Y here if you want to use CAN controller found on Renesas R-Car
+         SoCs.
+
+         To compile this driver as a module, choose M here: the module will
+         be called rcar_can.
+
+config CAN_XILINXCAN
+       tristate "Xilinx CAN"
+       depends on ARCH_ZYNQ || MICROBLAZE || COMPILE_TEST
+       depends on COMMON_CLK && HAS_IOMEM
+       ---help---
+         Xilinx CAN driver. This driver supports both soft AXI CAN IP and
+         Zynq CANPS IP.
+
 source "drivers/net/can/mscan/Kconfig"
 
 source "drivers/net/can/sja1000/Kconfig"
@@ -133,6 +145,8 @@ source "drivers/net/can/c_can/Kconfig"
 
 source "drivers/net/can/cc770/Kconfig"
 
+source "drivers/net/can/spi/Kconfig"
+
 source "drivers/net/can/usb/Kconfig"
 
 source "drivers/net/can/softing/Kconfig"
index c7440392adbbaaabd5ca5b8ba872ba18c23f41d2..1697f22353a943315bdb5e162d3cdce4f7d535f7 100644 (file)
@@ -10,6 +10,7 @@ can-dev-y                     := dev.o
 
 can-dev-$(CONFIG_CAN_LEDS)     += led.o
 
+obj-y                          += spi/
 obj-y                          += usb/
 obj-y                          += softing/
 
@@ -19,11 +20,12 @@ obj-$(CONFIG_CAN_C_CAN)             += c_can/
 obj-$(CONFIG_CAN_CC770)                += cc770/
 obj-$(CONFIG_CAN_AT91)         += at91_can.o
 obj-$(CONFIG_CAN_TI_HECC)      += ti_hecc.o
-obj-$(CONFIG_CAN_MCP251X)      += mcp251x.o
 obj-$(CONFIG_CAN_BFIN)         += bfin_can.o
 obj-$(CONFIG_CAN_JANZ_ICAN3)   += janz-ican3.o
 obj-$(CONFIG_CAN_FLEXCAN)      += flexcan.o
 obj-$(CONFIG_PCH_CAN)          += pch_can.o
 obj-$(CONFIG_CAN_GRCAN)                += grcan.o
+obj-$(CONFIG_CAN_RCAR)         += rcar_can.o
+obj-$(CONFIG_CAN_XILINXCAN)    += xilinx_can.o
 
 ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
index 95e04e2002daec774d394d0f0912ca7e952f280f..8e78bb48f5a4399f0fef7e991d64984c638c077a 100644 (file)
@@ -252,8 +252,7 @@ static void c_can_obj_update(struct net_device *dev, int iface, u32 cmd, u32 obj
        struct c_can_priv *priv = netdev_priv(dev);
        int cnt, reg = C_CAN_IFACE(COMREQ_REG, iface);
 
-       priv->write_reg(priv, reg + 1, cmd);
-       priv->write_reg(priv, reg, obj);
+       priv->write_reg32(priv, reg, (cmd << 16) | obj);
 
        for (cnt = MIN_TIMEOUT_VALUE; cnt; cnt--) {
                if (!(priv->read_reg(priv, reg) & IF_COMR_BUSY))
@@ -328,8 +327,7 @@ static void c_can_setup_tx_object(struct net_device *dev, int iface,
                change_bit(idx, &priv->tx_dir);
        }
 
-       priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), arb);
-       priv->write_reg(priv, C_CAN_IFACE(ARB2_REG, iface), arb >> 16);
+       priv->write_reg32(priv, C_CAN_IFACE(ARB1_REG, iface), arb);
 
        priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), ctrl);
 
@@ -391,8 +389,7 @@ static int c_can_read_msg_object(struct net_device *dev, int iface, u32 ctrl)
 
        frame->can_dlc = get_can_dlc(ctrl & 0x0F);
 
-       arb = priv->read_reg(priv, C_CAN_IFACE(ARB1_REG, iface));
-       arb |= priv->read_reg(priv, C_CAN_IFACE(ARB2_REG, iface)) << 16;
+       arb = priv->read_reg32(priv, C_CAN_IFACE(ARB1_REG, iface));
 
        if (arb & IF_ARB_MSGXTD)
                frame->can_id = (arb & CAN_EFF_MASK) | CAN_EFF_FLAG;
@@ -424,12 +421,10 @@ static void c_can_setup_receive_object(struct net_device *dev, int iface,
        struct c_can_priv *priv = netdev_priv(dev);
 
        mask |= BIT(29);
-       priv->write_reg(priv, C_CAN_IFACE(MASK1_REG, iface), mask);
-       priv->write_reg(priv, C_CAN_IFACE(MASK2_REG, iface), mask >> 16);
+       priv->write_reg32(priv, C_CAN_IFACE(MASK1_REG, iface), mask);
 
        id |= IF_ARB_MSGVAL;
-       priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), id);
-       priv->write_reg(priv, C_CAN_IFACE(ARB2_REG, iface), id >> 16);
+       priv->write_reg32(priv, C_CAN_IFACE(ARB1_REG, iface), id);
 
        priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), mcont);
        c_can_object_put(dev, iface, obj, IF_COMM_RCV_SETUP);
index c56f1b1c11cacde9e41c5cd663fd69ce802f2025..99ad1aa576b045197f82780d64936f3b7fe5651a 100644 (file)
@@ -78,6 +78,7 @@ enum reg {
        C_CAN_INTPND2_REG,
        C_CAN_MSGVAL1_REG,
        C_CAN_MSGVAL2_REG,
+       C_CAN_FUNCTION_REG,
 };
 
 static const u16 reg_map_c_can[] = {
@@ -129,6 +130,7 @@ static const u16 reg_map_d_can[] = {
        [C_CAN_BRPEXT_REG]      = 0x0E,
        [C_CAN_INT_REG]         = 0x10,
        [C_CAN_TEST_REG]        = 0x14,
+       [C_CAN_FUNCTION_REG]    = 0x18,
        [C_CAN_TXRQST1_REG]     = 0x88,
        [C_CAN_TXRQST2_REG]     = 0x8A,
        [C_CAN_NEWDAT1_REG]     = 0x9C,
@@ -176,8 +178,10 @@ struct c_can_priv {
        atomic_t tx_active;
        unsigned long tx_dir;
        int last_status;
-       u16 (*read_reg) (struct c_can_priv *priv, enum reg index);
-       void (*write_reg) (struct c_can_priv *priv, enum reg index, u16 val);
+       u16 (*read_reg) (const struct c_can_priv *priv, enum reg index);
+       void (*write_reg) (const struct c_can_priv *priv, enum reg index, u16 val);
+       u32 (*read_reg32) (const struct c_can_priv *priv, enum reg index);
+       void (*write_reg32) (const struct c_can_priv *priv, enum reg index, u32 val);
        void __iomem *base;
        const u16 *regs;
        void *priv;             /* for board-specific data */
index fe5f6303b58400fb69913229c20da6a2f2a5d303..5d11e0e4225bf3c84442b9ec8ddea4a005b4717f 100644 (file)
 
 #include "c_can.h"
 
+#define PCI_DEVICE_ID_PCH_CAN  0x8818
+#define PCH_PCI_SOFT_RESET     0x01fc
+
 enum c_can_pci_reg_align {
        C_CAN_REG_ALIGN_16,
        C_CAN_REG_ALIGN_32,
+       C_CAN_REG_32,
 };
 
 struct c_can_pci_data {
@@ -31,6 +35,10 @@ struct c_can_pci_data {
        enum c_can_pci_reg_align reg_align;
        /* Set the frequency */
        unsigned int freq;
+       /* PCI bar number */
+       int bar;
+       /* Callback for reset */
+       void (*init)(const struct c_can_priv *priv, bool enable);
 };
 
 /*
@@ -39,30 +47,70 @@ struct c_can_pci_data {
  * registers can be aligned to a 16-bit boundary or 32-bit boundary etc.
  * Handle the same by providing a common read/write interface.
  */
-static u16 c_can_pci_read_reg_aligned_to_16bit(struct c_can_priv *priv,
+static u16 c_can_pci_read_reg_aligned_to_16bit(const struct c_can_priv *priv,
                                                enum reg index)
 {
        return readw(priv->base + priv->regs[index]);
 }
 
-static void c_can_pci_write_reg_aligned_to_16bit(struct c_can_priv *priv,
+static void c_can_pci_write_reg_aligned_to_16bit(const struct c_can_priv *priv,
                                                enum reg index, u16 val)
 {
        writew(val, priv->base + priv->regs[index]);
 }
 
-static u16 c_can_pci_read_reg_aligned_to_32bit(struct c_can_priv *priv,
+static u16 c_can_pci_read_reg_aligned_to_32bit(const struct c_can_priv *priv,
                                                enum reg index)
 {
        return readw(priv->base + 2 * priv->regs[index]);
 }
 
-static void c_can_pci_write_reg_aligned_to_32bit(struct c_can_priv *priv,
+static void c_can_pci_write_reg_aligned_to_32bit(const struct c_can_priv *priv,
                                                enum reg index, u16 val)
 {
        writew(val, priv->base + 2 * priv->regs[index]);
 }
 
+static u16 c_can_pci_read_reg_32bit(const struct c_can_priv *priv,
+                                   enum reg index)
+{
+       return (u16)ioread32(priv->base + 2 * priv->regs[index]);
+}
+
+static void c_can_pci_write_reg_32bit(const struct c_can_priv *priv,
+                                     enum reg index, u16 val)
+{
+       iowrite32((u32)val, priv->base + 2 * priv->regs[index]);
+}
+
+static u32 c_can_pci_read_reg32(const struct c_can_priv *priv, enum reg index)
+{
+       u32 val;
+
+       val = priv->read_reg(priv, index);
+       val |= ((u32) priv->read_reg(priv, index + 1)) << 16;
+
+       return val;
+}
+
+static void c_can_pci_write_reg32(const struct c_can_priv *priv, enum reg index,
+               u32 val)
+{
+       priv->write_reg(priv, index + 1, val >> 16);
+       priv->write_reg(priv, index, val);
+}
+
+static void c_can_pci_reset_pch(const struct c_can_priv *priv, bool enable)
+{
+       if (enable) {
+               u32 __iomem *addr = priv->base + PCH_PCI_SOFT_RESET;
+
+               /* write to sw reset register */
+               iowrite32(1, addr);
+               iowrite32(0, addr);
+       }
+}
+
 static int c_can_pci_probe(struct pci_dev *pdev,
                           const struct pci_device_id *ent)
 {
@@ -90,7 +138,8 @@ static int c_can_pci_probe(struct pci_dev *pdev,
                pci_set_master(pdev);
        }
 
-       addr = pci_iomap(pdev, 0, pci_resource_len(pdev, 0));
+       addr = pci_iomap(pdev, c_can_pci_data->bar,
+                        pci_resource_len(pdev, c_can_pci_data->bar));
        if (!addr) {
                dev_err(&pdev->dev,
                        "device has no PCI memory resources, "
@@ -147,10 +196,18 @@ static int c_can_pci_probe(struct pci_dev *pdev,
                priv->read_reg = c_can_pci_read_reg_aligned_to_16bit;
                priv->write_reg = c_can_pci_write_reg_aligned_to_16bit;
                break;
+       case C_CAN_REG_32:
+               priv->read_reg = c_can_pci_read_reg_32bit;
+               priv->write_reg = c_can_pci_write_reg_32bit;
+               break;
        default:
                ret = -EINVAL;
                goto out_free_c_can;
        }
+       priv->read_reg32 = c_can_pci_read_reg32;
+       priv->write_reg32 = c_can_pci_write_reg32;
+
+       priv->raminit = c_can_pci_data->init;
 
        ret = register_c_can_dev(dev);
        if (ret) {
@@ -198,6 +255,15 @@ static struct c_can_pci_data c_can_sta2x11= {
        .type = BOSCH_C_CAN,
        .reg_align = C_CAN_REG_ALIGN_32,
        .freq = 52000000, /* 52 Mhz */
+       .bar = 0,
+};
+
+static struct c_can_pci_data c_can_pch = {
+       .type = BOSCH_C_CAN,
+       .reg_align = C_CAN_REG_32,
+       .freq = 50000000, /* 50 MHz */
+       .init = c_can_pci_reset_pch,
+       .bar = 1,
 };
 
 #define C_CAN_ID(_vend, _dev, _driverdata) {           \
@@ -207,6 +273,8 @@ static struct c_can_pci_data c_can_sta2x11= {
 static DEFINE_PCI_DEVICE_TABLE(c_can_pci_tbl) = {
        C_CAN_ID(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_CAN,
                 c_can_sta2x11),
+       C_CAN_ID(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH_CAN,
+                c_can_pch),
        {},
 };
 static struct pci_driver c_can_pci_driver = {
index 1df0b322d1e461ee69c4e74a499f425cda23d7bf..824108cd9fd594a91c25b0b4a1d43d3341ad9a31 100644 (file)
@@ -40,6 +40,7 @@
 #define CAN_RAMINIT_START_MASK(i)      (0x001 << (i))
 #define CAN_RAMINIT_DONE_MASK(i)       (0x100 << (i))
 #define CAN_RAMINIT_ALL_MASK(i)                (0x101 << (i))
+#define DCAN_RAM_INIT_BIT              (1 << 3)
 static DEFINE_SPINLOCK(raminit_lock);
 /*
  * 16-bit c_can registers can be arranged differently in the memory
@@ -47,31 +48,31 @@ static DEFINE_SPINLOCK(raminit_lock);
  * registers can be aligned to a 16-bit boundary or 32-bit boundary etc.
  * Handle the same by providing a common read/write interface.
  */
-static u16 c_can_plat_read_reg_aligned_to_16bit(struct c_can_priv *priv,
+static u16 c_can_plat_read_reg_aligned_to_16bit(const struct c_can_priv *priv,
                                                enum reg index)
 {
        return readw(priv->base + priv->regs[index]);
 }
 
-static void c_can_plat_write_reg_aligned_to_16bit(struct c_can_priv *priv,
+static void c_can_plat_write_reg_aligned_to_16bit(const struct c_can_priv *priv,
                                                enum reg index, u16 val)
 {
        writew(val, priv->base + priv->regs[index]);
 }
 
-static u16 c_can_plat_read_reg_aligned_to_32bit(struct c_can_priv *priv,
+static u16 c_can_plat_read_reg_aligned_to_32bit(const struct c_can_priv *priv,
                                                enum reg index)
 {
        return readw(priv->base + 2 * priv->regs[index]);
 }
 
-static void c_can_plat_write_reg_aligned_to_32bit(struct c_can_priv *priv,
+static void c_can_plat_write_reg_aligned_to_32bit(const struct c_can_priv *priv,
                                                enum reg index, u16 val)
 {
        writew(val, priv->base + 2 * priv->regs[index]);
 }
 
-static void c_can_hw_raminit_wait(const struct c_can_priv *priv, u32 mask,
+static void c_can_hw_raminit_wait_ti(const struct c_can_priv *priv, u32 mask,
                                  u32 val)
 {
        /* We look only at the bits of our instance. */
@@ -80,7 +81,7 @@ static void c_can_hw_raminit_wait(const struct c_can_priv *priv, u32 mask,
                udelay(1);
 }
 
-static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable)
+static void c_can_hw_raminit_ti(const struct c_can_priv *priv, bool enable)
 {
        u32 mask = CAN_RAMINIT_ALL_MASK(priv->instance);
        u32 ctrl;
@@ -96,18 +97,68 @@ static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable)
        ctrl |= CAN_RAMINIT_DONE_MASK(priv->instance);
        writel(ctrl, priv->raminit_ctrlreg);
        ctrl &= ~CAN_RAMINIT_DONE_MASK(priv->instance);
-       c_can_hw_raminit_wait(priv, ctrl, mask);
+       c_can_hw_raminit_wait_ti(priv, ctrl, mask);
 
        if (enable) {
                /* Set start bit and wait for the done bit. */
                ctrl |= CAN_RAMINIT_START_MASK(priv->instance);
                writel(ctrl, priv->raminit_ctrlreg);
                ctrl |= CAN_RAMINIT_DONE_MASK(priv->instance);
-               c_can_hw_raminit_wait(priv, ctrl, mask);
+               c_can_hw_raminit_wait_ti(priv, ctrl, mask);
        }
        spin_unlock(&raminit_lock);
 }
 
+static u32 c_can_plat_read_reg32(const struct c_can_priv *priv, enum reg index)
+{
+       u32 val;
+
+       val = priv->read_reg(priv, index);
+       val |= ((u32) priv->read_reg(priv, index + 1)) << 16;
+
+       return val;
+}
+
+static void c_can_plat_write_reg32(const struct c_can_priv *priv, enum reg index,
+               u32 val)
+{
+       priv->write_reg(priv, index + 1, val >> 16);
+       priv->write_reg(priv, index, val);
+}
+
+static u32 d_can_plat_read_reg32(const struct c_can_priv *priv, enum reg index)
+{
+       return readl(priv->base + priv->regs[index]);
+}
+
+static void d_can_plat_write_reg32(const struct c_can_priv *priv, enum reg index,
+               u32 val)
+{
+       writel(val, priv->base + priv->regs[index]);
+}
+
+static void c_can_hw_raminit_wait(const struct c_can_priv *priv, u32 mask)
+{
+       while (priv->read_reg32(priv, C_CAN_FUNCTION_REG) & mask)
+               udelay(1);
+}
+
+static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable)
+{
+       u32 ctrl;
+
+       ctrl = priv->read_reg32(priv, C_CAN_FUNCTION_REG);
+       ctrl &= ~DCAN_RAM_INIT_BIT;
+       priv->write_reg32(priv, C_CAN_FUNCTION_REG, ctrl);
+       c_can_hw_raminit_wait(priv, ctrl);
+
+       if (enable) {
+               ctrl |= DCAN_RAM_INIT_BIT;
+               priv->write_reg32(priv, C_CAN_FUNCTION_REG, ctrl);
+               c_can_hw_raminit_wait(priv, ctrl);
+       }
+}
+
 static struct platform_device_id c_can_id_table[] = {
        [BOSCH_C_CAN_PLATFORM] = {
                .name = KBUILD_MODNAME,
@@ -201,11 +252,15 @@ static int c_can_plat_probe(struct platform_device *pdev)
                case IORESOURCE_MEM_32BIT:
                        priv->read_reg = c_can_plat_read_reg_aligned_to_32bit;
                        priv->write_reg = c_can_plat_write_reg_aligned_to_32bit;
+                       priv->read_reg32 = c_can_plat_read_reg32;
+                       priv->write_reg32 = c_can_plat_write_reg32;
                        break;
                case IORESOURCE_MEM_16BIT:
                default:
                        priv->read_reg = c_can_plat_read_reg_aligned_to_16bit;
                        priv->write_reg = c_can_plat_write_reg_aligned_to_16bit;
+                       priv->read_reg32 = c_can_plat_read_reg32;
+                       priv->write_reg32 = c_can_plat_write_reg32;
                        break;
                }
                break;
@@ -214,6 +269,8 @@ static int c_can_plat_probe(struct platform_device *pdev)
                priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES;
                priv->read_reg = c_can_plat_read_reg_aligned_to_16bit;
                priv->write_reg = c_can_plat_write_reg_aligned_to_16bit;
+               priv->read_reg32 = d_can_plat_read_reg32;
+               priv->write_reg32 = d_can_plat_write_reg32;
 
                if (pdev->dev.of_node)
                        priv->instance = of_alias_get_id(pdev->dev.of_node, "d_can");
@@ -221,11 +278,20 @@ static int c_can_plat_probe(struct platform_device *pdev)
                        priv->instance = pdev->id;
 
                res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+               /* Not all D_CAN modules have a separate register for the D_CAN
+                * RAM initialization. Use default RAM init bit in D_CAN module
+                * if not specified in DT.
+                */
+               if (!res) {
+                       priv->raminit = c_can_hw_raminit;
+                       break;
+               }
+
                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;
+                       priv->raminit = c_can_hw_raminit_ti;
                break;
        default:
                ret = -EINVAL;
index f19be5269e7be55ae92ccdb12f2274768aeb5ddf..81c711719490511718122ebc5ebf77bc6c883e6c 100644 (file)
@@ -1,5 +1,5 @@
 config CAN_MSCAN
-       depends on PPC || M68K
+       depends on PPC
        tristate "Support for Freescale MSCAN based chips"
        ---help---
          The Motorola Scalable Controller Area Network (MSCAN) definition
diff --git a/drivers/net/can/rcar_can.c b/drivers/net/can/rcar_can.c
new file mode 100644 (file)
index 0000000..5268d21
--- /dev/null
@@ -0,0 +1,876 @@
+/* Renesas R-Car CAN device driver
+ *
+ * Copyright (C) 2013 Cogent Embedded, Inc. <source@cogentembedded.com>
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ *
+ * 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/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/can/led.h>
+#include <linux/can/dev.h>
+#include <linux/clk.h>
+#include <linux/can/platform/rcar_can.h>
+
+#define RCAR_CAN_DRV_NAME      "rcar_can"
+
+/* Mailbox configuration:
+ * mailbox 60 - 63 - Rx FIFO mailboxes
+ * mailbox 56 - 59 - Tx FIFO mailboxes
+ * non-FIFO mailboxes are not used
+ */
+#define RCAR_CAN_N_MBX         64 /* Number of mailboxes in non-FIFO mode */
+#define RCAR_CAN_RX_FIFO_MBX   60 /* Mailbox - window to Rx FIFO */
+#define RCAR_CAN_TX_FIFO_MBX   56 /* Mailbox - window to Tx FIFO */
+#define RCAR_CAN_FIFO_DEPTH    4
+
+/* Mailbox registers structure */
+struct rcar_can_mbox_regs {
+       u32 id;         /* IDE and RTR bits, SID and EID */
+       u8 stub;        /* Not used */
+       u8 dlc;         /* Data Length Code - bits [0..3] */
+       u8 data[8];     /* Data Bytes */
+       u8 tsh;         /* Time Stamp Higher Byte */
+       u8 tsl;         /* Time Stamp Lower Byte */
+};
+
+struct rcar_can_regs {
+       struct rcar_can_mbox_regs mb[RCAR_CAN_N_MBX]; /* Mailbox registers */
+       u32 mkr_2_9[8]; /* Mask Registers 2-9 */
+       u32 fidcr[2];   /* FIFO Received ID Compare Register */
+       u32 mkivlr1;    /* Mask Invalid Register 1 */
+       u32 mier1;      /* Mailbox Interrupt Enable Register 1 */
+       u32 mkr_0_1[2]; /* Mask Registers 0-1 */
+       u32 mkivlr0;    /* Mask Invalid Register 0*/
+       u32 mier0;      /* Mailbox Interrupt Enable Register 0 */
+       u8 pad_440[0x3c0];
+       u8 mctl[64];    /* Message Control Registers */
+       u16 ctlr;       /* Control Register */
+       u16 str;        /* Status register */
+       u8 bcr[3];      /* Bit Configuration Register */
+       u8 clkr;        /* Clock Select Register */
+       u8 rfcr;        /* Receive FIFO Control Register */
+       u8 rfpcr;       /* Receive FIFO Pointer Control Register */
+       u8 tfcr;        /* Transmit FIFO Control Register */
+       u8 tfpcr;       /* Transmit FIFO Pointer Control Register */
+       u8 eier;        /* Error Interrupt Enable Register */
+       u8 eifr;        /* Error Interrupt Factor Judge Register */
+       u8 recr;        /* Receive Error Count Register */
+       u8 tecr;        /* Transmit Error Count Register */
+       u8 ecsr;        /* Error Code Store Register */
+       u8 cssr;        /* Channel Search Support Register */
+       u8 mssr;        /* Mailbox Search Status Register */
+       u8 msmr;        /* Mailbox Search Mode Register */
+       u16 tsr;        /* Time Stamp Register */
+       u8 afsr;        /* Acceptance Filter Support Register */
+       u8 pad_857;
+       u8 tcr;         /* Test Control Register */
+       u8 pad_859[7];
+       u8 ier;         /* Interrupt Enable Register */
+       u8 isr;         /* Interrupt Status Register */
+       u8 pad_862;
+       u8 mbsmr;       /* Mailbox Search Mask Register */
+};
+
+struct rcar_can_priv {
+       struct can_priv can;    /* Must be the first member! */
+       struct net_device *ndev;
+       struct napi_struct napi;
+       struct rcar_can_regs __iomem *regs;
+       struct clk *clk;
+       u8 tx_dlc[RCAR_CAN_FIFO_DEPTH];
+       u32 tx_head;
+       u32 tx_tail;
+       u8 clock_select;
+       u8 ier;
+};
+
+static const struct can_bittiming_const rcar_can_bittiming_const = {
+       .name = RCAR_CAN_DRV_NAME,
+       .tseg1_min = 4,
+       .tseg1_max = 16,
+       .tseg2_min = 2,
+       .tseg2_max = 8,
+       .sjw_max = 4,
+       .brp_min = 1,
+       .brp_max = 1024,
+       .brp_inc = 1,
+};
+
+/* Control Register bits */
+#define RCAR_CAN_CTLR_BOM      (3 << 11) /* Bus-Off Recovery Mode Bits */
+#define RCAR_CAN_CTLR_BOM_ENT  (1 << 11) /* Entry to halt mode */
+                                       /* at bus-off entry */
+#define RCAR_CAN_CTLR_SLPM     (1 << 10)
+#define RCAR_CAN_CTLR_CANM     (3 << 8) /* Operating Mode Select Bit */
+#define RCAR_CAN_CTLR_CANM_HALT        (1 << 9)
+#define RCAR_CAN_CTLR_CANM_RESET (1 << 8)
+#define RCAR_CAN_CTLR_CANM_FORCE_RESET (3 << 8)
+#define RCAR_CAN_CTLR_MLM      (1 << 3) /* Message Lost Mode Select */
+#define RCAR_CAN_CTLR_IDFM     (3 << 1) /* ID Format Mode Select Bits */
+#define RCAR_CAN_CTLR_IDFM_MIXED (1 << 2) /* Mixed ID mode */
+#define RCAR_CAN_CTLR_MBM      (1 << 0) /* Mailbox Mode select */
+
+/* Status Register bits */
+#define RCAR_CAN_STR_RSTST     (1 << 8) /* Reset Status Bit */
+
+/* FIFO Received ID Compare Registers 0 and 1 bits */
+#define RCAR_CAN_FIDCR_IDE     (1 << 31) /* ID Extension Bit */
+#define RCAR_CAN_FIDCR_RTR     (1 << 30) /* Remote Transmission Request Bit */
+
+/* Receive FIFO Control Register bits */
+#define RCAR_CAN_RFCR_RFEST    (1 << 7) /* Receive FIFO Empty Status Flag */
+#define RCAR_CAN_RFCR_RFE      (1 << 0) /* Receive FIFO Enable */
+
+/* Transmit FIFO Control Register bits */
+#define RCAR_CAN_TFCR_TFUST    (7 << 1) /* Transmit FIFO Unsent Message */
+                                       /* Number Status Bits */
+#define RCAR_CAN_TFCR_TFUST_SHIFT 1    /* Offset of Transmit FIFO Unsent */
+                                       /* Message Number Status Bits */
+#define RCAR_CAN_TFCR_TFE      (1 << 0) /* Transmit FIFO Enable */
+
+#define RCAR_CAN_N_RX_MKREGS1  2       /* Number of mask registers */
+                                       /* for Rx mailboxes 0-31 */
+#define RCAR_CAN_N_RX_MKREGS2  8
+
+/* Bit Configuration Register settings */
+#define RCAR_CAN_BCR_TSEG1(x)  (((x) & 0x0f) << 20)
+#define RCAR_CAN_BCR_BPR(x)    (((x) & 0x3ff) << 8)
+#define RCAR_CAN_BCR_SJW(x)    (((x) & 0x3) << 4)
+#define RCAR_CAN_BCR_TSEG2(x)  ((x) & 0x07)
+
+/* Mailbox and Mask Registers bits */
+#define RCAR_CAN_IDE           (1 << 31)
+#define RCAR_CAN_RTR           (1 << 30)
+#define RCAR_CAN_SID_SHIFT     18
+
+/* Mailbox Interrupt Enable Register 1 bits */
+#define RCAR_CAN_MIER1_RXFIE   (1 << 28) /* Receive  FIFO Interrupt Enable */
+#define RCAR_CAN_MIER1_TXFIE   (1 << 24) /* Transmit FIFO Interrupt Enable */
+
+/* Interrupt Enable Register bits */
+#define RCAR_CAN_IER_ERSIE     (1 << 5) /* Error (ERS) Interrupt Enable Bit */
+#define RCAR_CAN_IER_RXFIE     (1 << 4) /* Reception FIFO Interrupt */
+                                       /* Enable Bit */
+#define RCAR_CAN_IER_TXFIE     (1 << 3) /* Transmission FIFO Interrupt */
+                                       /* Enable Bit */
+/* Interrupt Status Register bits */
+#define RCAR_CAN_ISR_ERSF      (1 << 5) /* Error (ERS) Interrupt Status Bit */
+#define RCAR_CAN_ISR_RXFF      (1 << 4) /* Reception FIFO Interrupt */
+                                       /* Status Bit */
+#define RCAR_CAN_ISR_TXFF      (1 << 3) /* Transmission FIFO Interrupt */
+                                       /* Status Bit */
+
+/* Error Interrupt Enable Register bits */
+#define RCAR_CAN_EIER_BLIE     (1 << 7) /* Bus Lock Interrupt Enable */
+#define RCAR_CAN_EIER_OLIE     (1 << 6) /* Overload Frame Transmit */
+                                       /* Interrupt Enable */
+#define RCAR_CAN_EIER_ORIE     (1 << 5) /* Receive Overrun  Interrupt Enable */
+#define RCAR_CAN_EIER_BORIE    (1 << 4) /* Bus-Off Recovery Interrupt Enable */
+#define RCAR_CAN_EIER_BOEIE    (1 << 3) /* Bus-Off Entry Interrupt Enable */
+#define RCAR_CAN_EIER_EPIE     (1 << 2) /* Error Passive Interrupt Enable */
+#define RCAR_CAN_EIER_EWIE     (1 << 1) /* Error Warning Interrupt Enable */
+#define RCAR_CAN_EIER_BEIE     (1 << 0) /* Bus Error Interrupt Enable */
+
+/* Error Interrupt Factor Judge Register bits */
+#define RCAR_CAN_EIFR_BLIF     (1 << 7) /* Bus Lock Detect Flag */
+#define RCAR_CAN_EIFR_OLIF     (1 << 6) /* Overload Frame Transmission */
+                                        /* Detect Flag */
+#define RCAR_CAN_EIFR_ORIF     (1 << 5) /* Receive Overrun Detect Flag */
+#define RCAR_CAN_EIFR_BORIF    (1 << 4) /* Bus-Off Recovery Detect Flag */
+#define RCAR_CAN_EIFR_BOEIF    (1 << 3) /* Bus-Off Entry Detect Flag */
+#define RCAR_CAN_EIFR_EPIF     (1 << 2) /* Error Passive Detect Flag */
+#define RCAR_CAN_EIFR_EWIF     (1 << 1) /* Error Warning Detect Flag */
+#define RCAR_CAN_EIFR_BEIF     (1 << 0) /* Bus Error Detect Flag */
+
+/* Error Code Store Register bits */
+#define RCAR_CAN_ECSR_EDPM     (1 << 7) /* Error Display Mode Select Bit */
+#define RCAR_CAN_ECSR_ADEF     (1 << 6) /* ACK Delimiter Error Flag */
+#define RCAR_CAN_ECSR_BE0F     (1 << 5) /* Bit Error (dominant) Flag */
+#define RCAR_CAN_ECSR_BE1F     (1 << 4) /* Bit Error (recessive) Flag */
+#define RCAR_CAN_ECSR_CEF      (1 << 3) /* CRC Error Flag */
+#define RCAR_CAN_ECSR_AEF      (1 << 2) /* ACK Error Flag */
+#define RCAR_CAN_ECSR_FEF      (1 << 1) /* Form Error Flag */
+#define RCAR_CAN_ECSR_SEF      (1 << 0) /* Stuff Error Flag */
+
+#define RCAR_CAN_NAPI_WEIGHT   4
+#define MAX_STR_READS          0x100
+
+static void tx_failure_cleanup(struct net_device *ndev)
+{
+       int i;
+
+       for (i = 0; i < RCAR_CAN_FIFO_DEPTH; i++)
+               can_free_echo_skb(ndev, i);
+}
+
+static void rcar_can_error(struct net_device *ndev)
+{
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+       struct net_device_stats *stats = &ndev->stats;
+       struct can_frame *cf;
+       struct sk_buff *skb;
+       u8 eifr, txerr = 0, rxerr = 0;
+
+       /* Propagate the error condition to the CAN stack */
+       skb = alloc_can_err_skb(ndev, &cf);
+
+       eifr = readb(&priv->regs->eifr);
+       if (eifr & (RCAR_CAN_EIFR_EWIF | RCAR_CAN_EIFR_EPIF)) {
+               txerr = readb(&priv->regs->tecr);
+               rxerr = readb(&priv->regs->recr);
+               if (skb) {
+                       cf->can_id |= CAN_ERR_CRTL;
+                       cf->data[6] = txerr;
+                       cf->data[7] = rxerr;
+               }
+       }
+       if (eifr & RCAR_CAN_EIFR_BEIF) {
+               int rx_errors = 0, tx_errors = 0;
+               u8 ecsr;
+
+               netdev_dbg(priv->ndev, "Bus error interrupt:\n");
+               if (skb) {
+                       cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
+                       cf->data[2] = CAN_ERR_PROT_UNSPEC;
+               }
+               ecsr = readb(&priv->regs->ecsr);
+               if (ecsr & RCAR_CAN_ECSR_ADEF) {
+                       netdev_dbg(priv->ndev, "ACK Delimiter Error\n");
+                       tx_errors++;
+                       writeb(~RCAR_CAN_ECSR_ADEF, &priv->regs->ecsr);
+                       if (skb)
+                               cf->data[3] |= CAN_ERR_PROT_LOC_ACK_DEL;
+               }
+               if (ecsr & RCAR_CAN_ECSR_BE0F) {
+                       netdev_dbg(priv->ndev, "Bit Error (dominant)\n");
+                       tx_errors++;
+                       writeb(~RCAR_CAN_ECSR_BE0F, &priv->regs->ecsr);
+                       if (skb)
+                               cf->data[2] |= CAN_ERR_PROT_BIT0;
+               }
+               if (ecsr & RCAR_CAN_ECSR_BE1F) {
+                       netdev_dbg(priv->ndev, "Bit Error (recessive)\n");
+                       tx_errors++;
+                       writeb(~RCAR_CAN_ECSR_BE1F, &priv->regs->ecsr);
+                       if (skb)
+                               cf->data[2] |= CAN_ERR_PROT_BIT1;
+               }
+               if (ecsr & RCAR_CAN_ECSR_CEF) {
+                       netdev_dbg(priv->ndev, "CRC Error\n");
+                       rx_errors++;
+                       writeb(~RCAR_CAN_ECSR_CEF, &priv->regs->ecsr);
+                       if (skb)
+                               cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
+               }
+               if (ecsr & RCAR_CAN_ECSR_AEF) {
+                       netdev_dbg(priv->ndev, "ACK Error\n");
+                       tx_errors++;
+                       writeb(~RCAR_CAN_ECSR_AEF, &priv->regs->ecsr);
+                       if (skb) {
+                               cf->can_id |= CAN_ERR_ACK;
+                               cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
+                       }
+               }
+               if (ecsr & RCAR_CAN_ECSR_FEF) {
+                       netdev_dbg(priv->ndev, "Form Error\n");
+                       rx_errors++;
+                       writeb(~RCAR_CAN_ECSR_FEF, &priv->regs->ecsr);
+                       if (skb)
+                               cf->data[2] |= CAN_ERR_PROT_FORM;
+               }
+               if (ecsr & RCAR_CAN_ECSR_SEF) {
+                       netdev_dbg(priv->ndev, "Stuff Error\n");
+                       rx_errors++;
+                       writeb(~RCAR_CAN_ECSR_SEF, &priv->regs->ecsr);
+                       if (skb)
+                               cf->data[2] |= CAN_ERR_PROT_STUFF;
+               }
+
+               priv->can.can_stats.bus_error++;
+               ndev->stats.rx_errors += rx_errors;
+               ndev->stats.tx_errors += tx_errors;
+               writeb(~RCAR_CAN_EIFR_BEIF, &priv->regs->eifr);
+       }
+       if (eifr & RCAR_CAN_EIFR_EWIF) {
+               netdev_dbg(priv->ndev, "Error warning interrupt\n");
+               priv->can.state = CAN_STATE_ERROR_WARNING;
+               priv->can.can_stats.error_warning++;
+               /* Clear interrupt condition */
+               writeb(~RCAR_CAN_EIFR_EWIF, &priv->regs->eifr);
+               if (skb)
+                       cf->data[1] = txerr > rxerr ? CAN_ERR_CRTL_TX_WARNING :
+                                             CAN_ERR_CRTL_RX_WARNING;
+       }
+       if (eifr & RCAR_CAN_EIFR_EPIF) {
+               netdev_dbg(priv->ndev, "Error passive interrupt\n");
+               priv->can.state = CAN_STATE_ERROR_PASSIVE;
+               priv->can.can_stats.error_passive++;
+               /* Clear interrupt condition */
+               writeb(~RCAR_CAN_EIFR_EPIF, &priv->regs->eifr);
+               if (skb)
+                       cf->data[1] = txerr > rxerr ? CAN_ERR_CRTL_TX_PASSIVE :
+                                             CAN_ERR_CRTL_RX_PASSIVE;
+       }
+       if (eifr & RCAR_CAN_EIFR_BOEIF) {
+               netdev_dbg(priv->ndev, "Bus-off entry interrupt\n");
+               tx_failure_cleanup(ndev);
+               priv->ier = RCAR_CAN_IER_ERSIE;
+               writeb(priv->ier, &priv->regs->ier);
+               priv->can.state = CAN_STATE_BUS_OFF;
+               /* Clear interrupt condition */
+               writeb(~RCAR_CAN_EIFR_BOEIF, &priv->regs->eifr);
+               can_bus_off(ndev);
+               if (skb)
+                       cf->can_id |= CAN_ERR_BUSOFF;
+       }
+       if (eifr & RCAR_CAN_EIFR_ORIF) {
+               netdev_dbg(priv->ndev, "Receive overrun error interrupt\n");
+               ndev->stats.rx_over_errors++;
+               ndev->stats.rx_errors++;
+               writeb(~RCAR_CAN_EIFR_ORIF, &priv->regs->eifr);
+               if (skb) {
+                       cf->can_id |= CAN_ERR_CRTL;
+                       cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+               }
+       }
+       if (eifr & RCAR_CAN_EIFR_OLIF) {
+               netdev_dbg(priv->ndev,
+                          "Overload Frame Transmission error interrupt\n");
+               ndev->stats.rx_over_errors++;
+               ndev->stats.rx_errors++;
+               writeb(~RCAR_CAN_EIFR_OLIF, &priv->regs->eifr);
+               if (skb) {
+                       cf->can_id |= CAN_ERR_PROT;
+                       cf->data[2] |= CAN_ERR_PROT_OVERLOAD;
+               }
+       }
+
+       if (skb) {
+               stats->rx_packets++;
+               stats->rx_bytes += cf->can_dlc;
+               netif_rx(skb);
+       }
+}
+
+static void rcar_can_tx_done(struct net_device *ndev)
+{
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+       struct net_device_stats *stats = &ndev->stats;
+       u8 isr;
+
+       while (1) {
+               u8 unsent = readb(&priv->regs->tfcr);
+
+               unsent = (unsent & RCAR_CAN_TFCR_TFUST) >>
+                         RCAR_CAN_TFCR_TFUST_SHIFT;
+               if (priv->tx_head - priv->tx_tail <= unsent)
+                       break;
+               stats->tx_packets++;
+               stats->tx_bytes += priv->tx_dlc[priv->tx_tail %
+                                               RCAR_CAN_FIFO_DEPTH];
+               priv->tx_dlc[priv->tx_tail % RCAR_CAN_FIFO_DEPTH] = 0;
+               can_get_echo_skb(ndev, priv->tx_tail % RCAR_CAN_FIFO_DEPTH);
+               priv->tx_tail++;
+               netif_wake_queue(ndev);
+       }
+       /* Clear interrupt */
+       isr = readb(&priv->regs->isr);
+       writeb(isr & ~RCAR_CAN_ISR_TXFF, &priv->regs->isr);
+       can_led_event(ndev, CAN_LED_EVENT_TX);
+}
+
+static irqreturn_t rcar_can_interrupt(int irq, void *dev_id)
+{
+       struct net_device *ndev = dev_id;
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+       u8 isr;
+
+       isr = readb(&priv->regs->isr);
+       if (!(isr & priv->ier))
+               return IRQ_NONE;
+
+       if (isr & RCAR_CAN_ISR_ERSF)
+               rcar_can_error(ndev);
+
+       if (isr & RCAR_CAN_ISR_TXFF)
+               rcar_can_tx_done(ndev);
+
+       if (isr & RCAR_CAN_ISR_RXFF) {
+               if (napi_schedule_prep(&priv->napi)) {
+                       /* Disable Rx FIFO interrupts */
+                       priv->ier &= ~RCAR_CAN_IER_RXFIE;
+                       writeb(priv->ier, &priv->regs->ier);
+                       __napi_schedule(&priv->napi);
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+static void rcar_can_set_bittiming(struct net_device *dev)
+{
+       struct rcar_can_priv *priv = netdev_priv(dev);
+       struct can_bittiming *bt = &priv->can.bittiming;
+       u32 bcr;
+
+       bcr = RCAR_CAN_BCR_TSEG1(bt->phase_seg1 + bt->prop_seg - 1) |
+             RCAR_CAN_BCR_BPR(bt->brp - 1) | RCAR_CAN_BCR_SJW(bt->sjw - 1) |
+             RCAR_CAN_BCR_TSEG2(bt->phase_seg2 - 1);
+       /* Don't overwrite CLKR with 32-bit BCR access; CLKR has 8-bit access.
+        * All the registers are big-endian but they get byte-swapped on 32-bit
+        * read/write (but not on 8-bit, contrary to the manuals)...
+        */
+       writel((bcr << 8) | priv->clock_select, &priv->regs->bcr);
+}
+
+static void rcar_can_start(struct net_device *ndev)
+{
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+       u16 ctlr;
+       int i;
+
+       /* Set controller to known mode:
+        * - FIFO mailbox mode
+        * - accept all messages
+        * - overrun mode
+        * CAN is in sleep mode after MCU hardware or software reset.
+        */
+       ctlr = readw(&priv->regs->ctlr);
+       ctlr &= ~RCAR_CAN_CTLR_SLPM;
+       writew(ctlr, &priv->regs->ctlr);
+       /* Go to reset mode */
+       ctlr |= RCAR_CAN_CTLR_CANM_FORCE_RESET;
+       writew(ctlr, &priv->regs->ctlr);
+       for (i = 0; i < MAX_STR_READS; i++) {
+               if (readw(&priv->regs->str) & RCAR_CAN_STR_RSTST)
+                       break;
+       }
+       rcar_can_set_bittiming(ndev);
+       ctlr |= RCAR_CAN_CTLR_IDFM_MIXED; /* Select mixed ID mode */
+       ctlr |= RCAR_CAN_CTLR_BOM_ENT;  /* Entry to halt mode automatically */
+                                       /* at bus-off */
+       ctlr |= RCAR_CAN_CTLR_MBM;      /* Select FIFO mailbox mode */
+       ctlr |= RCAR_CAN_CTLR_MLM;      /* Overrun mode */
+       writew(ctlr, &priv->regs->ctlr);
+
+       /* Accept all SID and EID */
+       writel(0, &priv->regs->mkr_2_9[6]);
+       writel(0, &priv->regs->mkr_2_9[7]);
+       /* In FIFO mailbox mode, write "0" to bits 24 to 31 */
+       writel(0, &priv->regs->mkivlr1);
+       /* Accept all frames */
+       writel(0, &priv->regs->fidcr[0]);
+       writel(RCAR_CAN_FIDCR_IDE | RCAR_CAN_FIDCR_RTR, &priv->regs->fidcr[1]);
+       /* Enable and configure FIFO mailbox interrupts */
+       writel(RCAR_CAN_MIER1_RXFIE | RCAR_CAN_MIER1_TXFIE, &priv->regs->mier1);
+
+       priv->ier = RCAR_CAN_IER_ERSIE | RCAR_CAN_IER_RXFIE |
+                   RCAR_CAN_IER_TXFIE;
+       writeb(priv->ier, &priv->regs->ier);
+
+       /* Accumulate error codes */
+       writeb(RCAR_CAN_ECSR_EDPM, &priv->regs->ecsr);
+       /* Enable error interrupts */
+       writeb(RCAR_CAN_EIER_EWIE | RCAR_CAN_EIER_EPIE | RCAR_CAN_EIER_BOEIE |
+              (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING ?
+              RCAR_CAN_EIER_BEIE : 0) | RCAR_CAN_EIER_ORIE |
+              RCAR_CAN_EIER_OLIE, &priv->regs->eier);
+       priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+       /* Go to operation mode */
+       writew(ctlr & ~RCAR_CAN_CTLR_CANM, &priv->regs->ctlr);
+       for (i = 0; i < MAX_STR_READS; i++) {
+               if (!(readw(&priv->regs->str) & RCAR_CAN_STR_RSTST))
+                       break;
+       }
+       /* Enable Rx and Tx FIFO */
+       writeb(RCAR_CAN_RFCR_RFE, &priv->regs->rfcr);
+       writeb(RCAR_CAN_TFCR_TFE, &priv->regs->tfcr);
+}
+
+static int rcar_can_open(struct net_device *ndev)
+{
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+       int err;
+
+       err = clk_prepare_enable(priv->clk);
+       if (err) {
+               netdev_err(ndev, "clk_prepare_enable() failed, error %d\n",
+                          err);
+               goto out;
+       }
+       err = open_candev(ndev);
+       if (err) {
+               netdev_err(ndev, "open_candev() failed, error %d\n", err);
+               goto out_clock;
+       }
+       napi_enable(&priv->napi);
+       err = request_irq(ndev->irq, rcar_can_interrupt, 0, ndev->name, ndev);
+       if (err) {
+               netdev_err(ndev, "error requesting interrupt %x\n", ndev->irq);
+               goto out_close;
+       }
+       can_led_event(ndev, CAN_LED_EVENT_OPEN);
+       rcar_can_start(ndev);
+       netif_start_queue(ndev);
+       return 0;
+out_close:
+       napi_disable(&priv->napi);
+       close_candev(ndev);
+out_clock:
+       clk_disable_unprepare(priv->clk);
+out:
+       return err;
+}
+
+static void rcar_can_stop(struct net_device *ndev)
+{
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+       u16 ctlr;
+       int i;
+
+       /* Go to (force) reset mode */
+       ctlr = readw(&priv->regs->ctlr);
+       ctlr |= RCAR_CAN_CTLR_CANM_FORCE_RESET;
+       writew(ctlr, &priv->regs->ctlr);
+       for (i = 0; i < MAX_STR_READS; i++) {
+               if (readw(&priv->regs->str) & RCAR_CAN_STR_RSTST)
+                       break;
+       }
+       writel(0, &priv->regs->mier0);
+       writel(0, &priv->regs->mier1);
+       writeb(0, &priv->regs->ier);
+       writeb(0, &priv->regs->eier);
+       /* Go to sleep mode */
+       ctlr |= RCAR_CAN_CTLR_SLPM;
+       writew(ctlr, &priv->regs->ctlr);
+       priv->can.state = CAN_STATE_STOPPED;
+}
+
+static int rcar_can_close(struct net_device *ndev)
+{
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+
+       netif_stop_queue(ndev);
+       rcar_can_stop(ndev);
+       free_irq(ndev->irq, ndev);
+       napi_disable(&priv->napi);
+       clk_disable_unprepare(priv->clk);
+       close_candev(ndev);
+       can_led_event(ndev, CAN_LED_EVENT_STOP);
+       return 0;
+}
+
+static netdev_tx_t rcar_can_start_xmit(struct sk_buff *skb,
+                                      struct net_device *ndev)
+{
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+       struct can_frame *cf = (struct can_frame *)skb->data;
+       u32 data, i;
+
+       if (can_dropped_invalid_skb(ndev, skb))
+               return NETDEV_TX_OK;
+
+       if (cf->can_id & CAN_EFF_FLAG)  /* Extended frame format */
+               data = (cf->can_id & CAN_EFF_MASK) | RCAR_CAN_IDE;
+       else                            /* Standard frame format */
+               data = (cf->can_id & CAN_SFF_MASK) << RCAR_CAN_SID_SHIFT;
+
+       if (cf->can_id & CAN_RTR_FLAG) { /* Remote transmission request */
+               data |= RCAR_CAN_RTR;
+       } else {
+               for (i = 0; i < cf->can_dlc; i++)
+                       writeb(cf->data[i],
+                              &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].data[i]);
+       }
+
+       writel(data, &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].id);
+
+       writeb(cf->can_dlc, &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].dlc);
+
+       priv->tx_dlc[priv->tx_head % RCAR_CAN_FIFO_DEPTH] = cf->can_dlc;
+       can_put_echo_skb(skb, ndev, priv->tx_head % RCAR_CAN_FIFO_DEPTH);
+       priv->tx_head++;
+       /* Start Tx: write 0xff to the TFPCR register to increment
+        * the CPU-side pointer for the transmit FIFO to the next
+        * mailbox location
+        */
+       writeb(0xff, &priv->regs->tfpcr);
+       /* Stop the queue if we've filled all FIFO entries */
+       if (priv->tx_head - priv->tx_tail >= RCAR_CAN_FIFO_DEPTH)
+               netif_stop_queue(ndev);
+
+       return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops rcar_can_netdev_ops = {
+       .ndo_open = rcar_can_open,
+       .ndo_stop = rcar_can_close,
+       .ndo_start_xmit = rcar_can_start_xmit,
+};
+
+static void rcar_can_rx_pkt(struct rcar_can_priv *priv)
+{
+       struct net_device_stats *stats = &priv->ndev->stats;
+       struct can_frame *cf;
+       struct sk_buff *skb;
+       u32 data;
+       u8 dlc;
+
+       skb = alloc_can_skb(priv->ndev, &cf);
+       if (!skb) {
+               stats->rx_dropped++;
+               return;
+       }
+
+       data = readl(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].id);
+       if (data & RCAR_CAN_IDE)
+               cf->can_id = (data & CAN_EFF_MASK) | CAN_EFF_FLAG;
+       else
+               cf->can_id = (data >> RCAR_CAN_SID_SHIFT) & CAN_SFF_MASK;
+
+       dlc = readb(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].dlc);
+       cf->can_dlc = get_can_dlc(dlc);
+       if (data & RCAR_CAN_RTR) {
+               cf->can_id |= CAN_RTR_FLAG;
+       } else {
+               for (dlc = 0; dlc < cf->can_dlc; dlc++)
+                       cf->data[dlc] =
+                       readb(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].data[dlc]);
+       }
+
+       can_led_event(priv->ndev, CAN_LED_EVENT_RX);
+
+       stats->rx_bytes += cf->can_dlc;
+       stats->rx_packets++;
+       netif_receive_skb(skb);
+}
+
+static int rcar_can_rx_poll(struct napi_struct *napi, int quota)
+{
+       struct rcar_can_priv *priv = container_of(napi,
+                                                 struct rcar_can_priv, napi);
+       int num_pkts;
+
+       for (num_pkts = 0; num_pkts < quota; num_pkts++) {
+               u8 rfcr, isr;
+
+               isr = readb(&priv->regs->isr);
+               /* Clear interrupt bit */
+               if (isr & RCAR_CAN_ISR_RXFF)
+                       writeb(isr & ~RCAR_CAN_ISR_RXFF, &priv->regs->isr);
+               rfcr = readb(&priv->regs->rfcr);
+               if (rfcr & RCAR_CAN_RFCR_RFEST)
+                       break;
+               rcar_can_rx_pkt(priv);
+               /* Write 0xff to the RFPCR register to increment
+                * the CPU-side pointer for the receive FIFO
+                * to the next mailbox location
+                */
+               writeb(0xff, &priv->regs->rfpcr);
+       }
+       /* All packets processed */
+       if (num_pkts < quota) {
+               napi_complete(napi);
+               priv->ier |= RCAR_CAN_IER_RXFIE;
+               writeb(priv->ier, &priv->regs->ier);
+       }
+       return num_pkts;
+}
+
+static int rcar_can_do_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+       switch (mode) {
+       case CAN_MODE_START:
+               rcar_can_start(ndev);
+               netif_wake_queue(ndev);
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int rcar_can_get_berr_counter(const struct net_device *dev,
+                                    struct can_berr_counter *bec)
+{
+       struct rcar_can_priv *priv = netdev_priv(dev);
+       int err;
+
+       err = clk_prepare_enable(priv->clk);
+       if (err)
+               return err;
+       bec->txerr = readb(&priv->regs->tecr);
+       bec->rxerr = readb(&priv->regs->recr);
+       clk_disable_unprepare(priv->clk);
+       return 0;
+}
+
+static int rcar_can_probe(struct platform_device *pdev)
+{
+       struct rcar_can_platform_data *pdata;
+       struct rcar_can_priv *priv;
+       struct net_device *ndev;
+       struct resource *mem;
+       void __iomem *addr;
+       int err = -ENODEV;
+       int irq;
+
+       pdata = dev_get_platdata(&pdev->dev);
+       if (!pdata) {
+               dev_err(&pdev->dev, "No platform data provided!\n");
+               goto fail;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (!irq) {
+               dev_err(&pdev->dev, "No IRQ resource\n");
+               goto fail;
+       }
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       addr = devm_ioremap_resource(&pdev->dev, mem);
+       if (IS_ERR(addr)) {
+               err = PTR_ERR(addr);
+               goto fail;
+       }
+
+       ndev = alloc_candev(sizeof(struct rcar_can_priv), RCAR_CAN_FIFO_DEPTH);
+       if (!ndev) {
+               dev_err(&pdev->dev, "alloc_candev() failed\n");
+               err = -ENOMEM;
+               goto fail;
+       }
+
+       priv = netdev_priv(ndev);
+
+       priv->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(priv->clk)) {
+               err = PTR_ERR(priv->clk);
+               dev_err(&pdev->dev, "cannot get clock: %d\n", err);
+               goto fail_clk;
+       }
+
+       ndev->netdev_ops = &rcar_can_netdev_ops;
+       ndev->irq = irq;
+       ndev->flags |= IFF_ECHO;
+       priv->ndev = ndev;
+       priv->regs = addr;
+       priv->clock_select = pdata->clock_select;
+       priv->can.clock.freq = clk_get_rate(priv->clk);
+       priv->can.bittiming_const = &rcar_can_bittiming_const;
+       priv->can.do_set_mode = rcar_can_do_set_mode;
+       priv->can.do_get_berr_counter = rcar_can_get_berr_counter;
+       priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING;
+       platform_set_drvdata(pdev, ndev);
+       SET_NETDEV_DEV(ndev, &pdev->dev);
+
+       netif_napi_add(ndev, &priv->napi, rcar_can_rx_poll,
+                      RCAR_CAN_NAPI_WEIGHT);
+       err = register_candev(ndev);
+       if (err) {
+               dev_err(&pdev->dev, "register_candev() failed, error %d\n",
+                       err);
+               goto fail_candev;
+       }
+
+       devm_can_led_init(ndev);
+
+       dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%u)\n",
+                priv->regs, ndev->irq);
+
+       return 0;
+fail_candev:
+       netif_napi_del(&priv->napi);
+fail_clk:
+       free_candev(ndev);
+fail:
+       return err;
+}
+
+static int rcar_can_remove(struct platform_device *pdev)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+
+       unregister_candev(ndev);
+       netif_napi_del(&priv->napi);
+       free_candev(ndev);
+       return 0;
+}
+
+static int __maybe_unused rcar_can_suspend(struct device *dev)
+{
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+       u16 ctlr;
+
+       if (netif_running(ndev)) {
+               netif_stop_queue(ndev);
+               netif_device_detach(ndev);
+       }
+       ctlr = readw(&priv->regs->ctlr);
+       ctlr |= RCAR_CAN_CTLR_CANM_HALT;
+       writew(ctlr, &priv->regs->ctlr);
+       ctlr |= RCAR_CAN_CTLR_SLPM;
+       writew(ctlr, &priv->regs->ctlr);
+       priv->can.state = CAN_STATE_SLEEPING;
+
+       clk_disable(priv->clk);
+       return 0;
+}
+
+static int __maybe_unused rcar_can_resume(struct device *dev)
+{
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+       u16 ctlr;
+       int err;
+
+       err = clk_enable(priv->clk);
+       if (err) {
+               netdev_err(ndev, "clk_enable() failed, error %d\n", err);
+               return err;
+       }
+
+       ctlr = readw(&priv->regs->ctlr);
+       ctlr &= ~RCAR_CAN_CTLR_SLPM;
+       writew(ctlr, &priv->regs->ctlr);
+       ctlr &= ~RCAR_CAN_CTLR_CANM;
+       writew(ctlr, &priv->regs->ctlr);
+       priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+       if (netif_running(ndev)) {
+               netif_device_attach(ndev);
+               netif_start_queue(ndev);
+       }
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(rcar_can_pm_ops, rcar_can_suspend, rcar_can_resume);
+
+static struct platform_driver rcar_can_driver = {
+       .driver = {
+               .name = RCAR_CAN_DRV_NAME,
+               .owner = THIS_MODULE,
+               .pm = &rcar_can_pm_ops,
+       },
+       .probe = rcar_can_probe,
+       .remove = rcar_can_remove,
+};
+
+module_platform_driver(rcar_can_driver);
+
+MODULE_AUTHOR("Cogent Embedded, Inc.");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("CAN driver for Renesas R-Car SoC");
+MODULE_ALIAS("platform:" RCAR_CAN_DRV_NAME);
index 7d8c8f3672dd993119a28f478e5cc43514aa131c..bacd236ce3064d357271ff67e3ccacbfedddd953 100644 (file)
@@ -556,15 +556,6 @@ failed:
 /*
  * netdev sysfs
  */
-static ssize_t show_channel(struct device *dev, struct device_attribute *attr,
-               char *buf)
-{
-       struct net_device *ndev = to_net_dev(dev);
-       struct softing_priv *priv = netdev2softing(ndev);
-
-       return sprintf(buf, "%i\n", priv->index);
-}
-
 static ssize_t show_chip(struct device *dev, struct device_attribute *attr,
                char *buf)
 {
@@ -609,12 +600,10 @@ static ssize_t store_output(struct device *dev, struct device_attribute *attr,
        return count;
 }
 
-static const DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL);
 static const DEVICE_ATTR(chip, S_IRUGO, show_chip, NULL);
 static const DEVICE_ATTR(output, S_IRUGO | S_IWUSR, show_output, store_output);
 
 static const struct attribute *const netdev_sysfs_attrs[] = {
-       &dev_attr_channel.attr,
        &dev_attr_chip.attr,
        &dev_attr_output.attr,
        NULL,
@@ -679,17 +668,20 @@ static int softing_netdev_register(struct net_device *netdev)
 {
        int ret;
 
-       netdev->sysfs_groups[0] = &netdev_sysfs_group;
        ret = register_candev(netdev);
        if (ret) {
                dev_alert(&netdev->dev, "register failed\n");
                return ret;
        }
+       if (sysfs_create_group(&netdev->dev.kobj, &netdev_sysfs_group) < 0)
+               netdev_alert(netdev, "sysfs group failed\n");
+
        return 0;
 }
 
 static void softing_netdev_cleanup(struct net_device *netdev)
 {
+       sysfs_remove_group(&netdev->dev.kobj, &netdev_sysfs_group);
        unregister_candev(netdev);
        free_candev(netdev);
 }
@@ -721,8 +713,6 @@ DEV_ATTR_RO(firmware_version, id.fw_version);
 DEV_ATTR_RO_STR(hardware, pdat->name);
 DEV_ATTR_RO(hardware_version, id.hw_version);
 DEV_ATTR_RO(license, id.license);
-DEV_ATTR_RO(frequency, id.freq);
-DEV_ATTR_RO(txpending, tx.pending);
 
 static struct attribute *softing_pdev_attrs[] = {
        &dev_attr_serial.attr,
@@ -731,8 +721,6 @@ static struct attribute *softing_pdev_attrs[] = {
        &dev_attr_hardware.attr,
        &dev_attr_hardware_version.attr,
        &dev_attr_license.attr,
-       &dev_attr_frequency.attr,
-       &dev_attr_txpending.attr,
        NULL,
 };
 
diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig
new file mode 100644 (file)
index 0000000..148cae5
--- /dev/null
@@ -0,0 +1,10 @@
+menu "CAN SPI interfaces"
+       depends on SPI
+
+config CAN_MCP251X
+       tristate "Microchip MCP251x SPI CAN controllers"
+       depends on HAS_DMA
+       ---help---
+         Driver for the Microchip MCP251x SPI CAN controllers.
+
+endmenu
diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile
new file mode 100644 (file)
index 0000000..90bcacf
--- /dev/null
@@ -0,0 +1,8 @@
+#
+#  Makefile for the Linux Controller Area Network SPI drivers.
+#
+
+
+obj-$(CONFIG_CAN_MCP251X)      += mcp251x.o
+
+ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
similarity index 96%
rename from drivers/net/can/mcp251x.c
rename to drivers/net/can/spi/mcp251x.c
index 28c11f81524524fe521eb0c24177fffa76323885..5df239e68812635e1d209f835e9a62dbe2fdf555 100644 (file)
 
 #define TX_ECHO_SKB_MAX        1
 
+#define MCP251X_OST_DELAY_MS   (5)
+
 #define DEVICE_NAME "mcp251x"
 
 static int mcp251x_enable_dma; /* Enable SPI DMA. Default: 0 (Off) */
@@ -624,50 +626,45 @@ static int mcp251x_setup(struct net_device *net, struct mcp251x_priv *priv,
 static int mcp251x_hw_reset(struct spi_device *spi)
 {
        struct mcp251x_priv *priv = spi_get_drvdata(spi);
+       u8 reg;
        int ret;
-       unsigned long timeout;
+
+       /* Wait for oscillator startup timer after power up */
+       mdelay(MCP251X_OST_DELAY_MS);
 
        priv->spi_tx_buf[0] = INSTRUCTION_RESET;
-       ret = spi_write(spi, priv->spi_tx_buf, 1);
-       if (ret) {
-               dev_err(&spi->dev, "reset failed: ret = %d\n", ret);
-               return -EIO;
-       }
+       ret = mcp251x_spi_trans(spi, 1);
+       if (ret)
+               return ret;
+
+       /* Wait for oscillator startup timer after reset */
+       mdelay(MCP251X_OST_DELAY_MS);
+       
+       reg = mcp251x_read_reg(spi, CANSTAT);
+       if ((reg & CANCTRL_REQOP_MASK) != CANCTRL_REQOP_CONF)
+               return -ENODEV;
 
-       /* Wait for reset to finish */
-       timeout = jiffies + HZ;
-       mdelay(10);
-       while ((mcp251x_read_reg(spi, CANSTAT) & CANCTRL_REQOP_MASK)
-              != CANCTRL_REQOP_CONF) {
-               schedule();
-               if (time_after(jiffies, timeout)) {
-                       dev_err(&spi->dev, "MCP251x didn't"
-                               " enter in conf mode after reset\n");
-                       return -EBUSY;
-               }
-       }
        return 0;
 }
 
 static int mcp251x_hw_probe(struct spi_device *spi)
 {
-       int st1, st2;
+       u8 ctrl;
+       int ret;
 
-       mcp251x_hw_reset(spi);
+       ret = mcp251x_hw_reset(spi);
+       if (ret)
+               return ret;
 
-       /*
-        * Please note that these are "magic values" based on after
-        * reset defaults taken from data sheet which allows us to see
-        * if we really have a chip on the bus (we avoid common all
-        * zeroes or all ones situations)
-        */
-       st1 = mcp251x_read_reg(spi, CANSTAT) & 0xEE;
-       st2 = mcp251x_read_reg(spi, CANCTRL) & 0x17;
+       ctrl = mcp251x_read_reg(spi, CANCTRL);
+
+       dev_dbg(&spi->dev, "CANCTRL 0x%02x\n", ctrl);
 
-       dev_dbg(&spi->dev, "CANSTAT 0x%02x CANCTRL 0x%02x\n", st1, st2);
+       /* Check for power up default value */
+       if ((ctrl & 0x17) != 0x07)
+               return -ENODEV;
 
-       /* Check for power up default values */
-       return (st1 == 0x80 && st2 == 0x07) ? 1 : 0;
+       return 0;
 }
 
 static int mcp251x_power_enable(struct regulator *reg, int enable)
@@ -776,7 +773,6 @@ static void mcp251x_restart_work_handler(struct work_struct *ws)
 
        mutex_lock(&priv->mcp_lock);
        if (priv->after_suspend) {
-               mdelay(10);
                mcp251x_hw_reset(spi);
                mcp251x_setup(net, priv, spi);
                if (priv->after_suspend & AFTER_SUSPEND_RESTART) {
@@ -955,7 +951,7 @@ static int mcp251x_open(struct net_device *net)
        priv->tx_len = 0;
 
        ret = request_threaded_irq(spi->irq, NULL, mcp251x_can_ist,
-                                  flags, DEVICE_NAME, priv);
+                                  flags | IRQF_ONESHOT, DEVICE_NAME, priv);
        if (ret) {
                dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq);
                mcp251x_power_enable(priv->transceiver, 0);
@@ -1032,8 +1028,8 @@ static int mcp251x_can_probe(struct spi_device *spi)
        struct mcp251x_platform_data *pdata = dev_get_platdata(&spi->dev);
        struct net_device *net;
        struct mcp251x_priv *priv;
-       int freq, ret = -ENODEV;
        struct clk *clk;
+       int freq, ret;
 
        clk = devm_clk_get(&spi->dev, NULL);
        if (IS_ERR(clk)) {
@@ -1076,6 +1072,18 @@ static int mcp251x_can_probe(struct spi_device *spi)
        priv->net = net;
        priv->clk = clk;
 
+       spi_set_drvdata(spi, priv);
+
+       /* Configure the SPI bus */
+       spi->bits_per_word = 8;
+       if (mcp251x_is_2510(spi))
+               spi->max_speed_hz = spi->max_speed_hz ? : 5 * 1000 * 1000;
+       else
+               spi->max_speed_hz = spi->max_speed_hz ? : 10 * 1000 * 1000;
+       ret = spi_setup(spi);
+       if (ret)
+               goto out_clk;
+
        priv->power = devm_regulator_get(&spi->dev, "vdd");
        priv->transceiver = devm_regulator_get(&spi->dev, "xceiver");
        if ((PTR_ERR(priv->power) == -EPROBE_DEFER) ||
@@ -1088,8 +1096,6 @@ static int mcp251x_can_probe(struct spi_device *spi)
        if (ret)
                goto out_clk;
 
-       spi_set_drvdata(spi, priv);
-
        priv->spi = spi;
        mutex_init(&priv->mcp_lock);
 
@@ -1134,20 +1140,11 @@ static int mcp251x_can_probe(struct spi_device *spi)
 
        SET_NETDEV_DEV(net, &spi->dev);
 
-       /* Configure the SPI bus */
-       spi->mode = spi->mode ? : SPI_MODE_0;
-       if (mcp251x_is_2510(spi))
-               spi->max_speed_hz = spi->max_speed_hz ? : 5 * 1000 * 1000;
-       else
-               spi->max_speed_hz = spi->max_speed_hz ? : 10 * 1000 * 1000;
-       spi->bits_per_word = 8;
-       spi_setup(spi);
-
        /* Here is OK to not lock the MCP, no one knows about it yet */
-       if (!mcp251x_hw_probe(spi)) {
-               ret = -ENODEV;
+       ret = mcp251x_hw_probe(spi);
+       if (ret)
                goto error_probe;
-       }
+
        mcp251x_hw_sleep(spi);
 
        ret = register_candev(net);
@@ -1156,7 +1153,7 @@ static int mcp251x_can_probe(struct spi_device *spi)
 
        devm_can_led_init(net);
 
-       return ret;
+       return 0;
 
 error_probe:
        if (mcp251x_enable_dma)
index fc96a3d83ebecde338cdc5fd1d7b12f5ee9fbc25..a77db919363c08baa2c76dbb35c60701ea2c68ff 100644 (file)
@@ -13,13 +13,21 @@ config CAN_ESD_USB2
           This driver supports the CAN-USB/2 interface
           from esd electronic system design gmbh (http://www.esd.eu).
 
+config CAN_GS_USB
+       tristate "Geschwister Schneider UG interfaces"
+       ---help---
+         This driver supports the Geschwister Schneider USB/CAN devices.
+         If unsure choose N,
+         choose Y for built in support,
+         M to compile as module (module will be named: gs_usb).
+
 config CAN_KVASER_USB
        tristate "Kvaser CAN/USB interface"
        ---help---
          This driver adds support for Kvaser CAN/USB devices like Kvaser
          Leaf Light.
 
-         The driver gives support for the following devices:
+         The driver provides support for the following devices:
            - Kvaser Leaf Light
            - Kvaser Leaf Professional HS
            - Kvaser Leaf SemiPro HS
@@ -36,6 +44,8 @@ config CAN_KVASER_USB
            - Kvaser Leaf Light "China"
            - Kvaser BlackBird SemiPro
            - Kvaser USBcan R
+           - Kvaser Leaf Light v2
+           - Kvaser Mini PCI Express HS
 
          If unsure, say N.
 
index becef460a91aeb28851e91752b67ddec84ef931c..7b9a393b1ac82a1caf4684a704d7121ad16dc3c9 100644 (file)
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
 obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
+obj-$(CONFIG_CAN_GS_USB) += gs_usb.o
 obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
 obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
 obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o
diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c
new file mode 100644 (file)
index 0000000..04b0f84
--- /dev/null
@@ -0,0 +1,971 @@
+/* CAN driver for Geschwister Schneider USB/CAN devices.
+ *
+ * Copyright (C) 2013 Geschwister Schneider Technologie-,
+ * Entwicklungs- und Vertriebs UG (Haftungsbeschränkt).
+ *
+ * Many thanks to all socketcan devs!
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/signal.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/usb.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+/* Device specific constants */
+#define USB_GSUSB_1_VENDOR_ID      0x1d50
+#define USB_GSUSB_1_PRODUCT_ID     0x606f
+
+#define GSUSB_ENDPOINT_IN          1
+#define GSUSB_ENDPOINT_OUT         2
+
+/* Device specific constants */
+enum gs_usb_breq {
+       GS_USB_BREQ_HOST_FORMAT = 0,
+       GS_USB_BREQ_BITTIMING,
+       GS_USB_BREQ_MODE,
+       GS_USB_BREQ_BERR,
+       GS_USB_BREQ_BT_CONST,
+       GS_USB_BREQ_DEVICE_CONFIG
+};
+
+enum gs_can_mode {
+       /* reset a channel. turns it off */
+       GS_CAN_MODE_RESET = 0,
+       /* starts a channel */
+       GS_CAN_MODE_START
+};
+
+enum gs_can_state {
+       GS_CAN_STATE_ERROR_ACTIVE = 0,
+       GS_CAN_STATE_ERROR_WARNING,
+       GS_CAN_STATE_ERROR_PASSIVE,
+       GS_CAN_STATE_BUS_OFF,
+       GS_CAN_STATE_STOPPED,
+       GS_CAN_STATE_SLEEPING
+};
+
+/* data types passed between host and device */
+struct gs_host_config {
+       u32 byte_order;
+} __packed;
+/* All data exchanged between host and device is exchanged in host byte order,
+ * thanks to the struct gs_host_config byte_order member, which is sent first
+ * to indicate the desired byte order.
+ */
+
+struct gs_device_config {
+       u8 reserved1;
+       u8 reserved2;
+       u8 reserved3;
+       u8 icount;
+       u32 sw_version;
+       u32 hw_version;
+} __packed;
+
+#define GS_CAN_MODE_NORMAL               0
+#define GS_CAN_MODE_LISTEN_ONLY          (1<<0)
+#define GS_CAN_MODE_LOOP_BACK            (1<<1)
+#define GS_CAN_MODE_TRIPLE_SAMPLE        (1<<2)
+#define GS_CAN_MODE_ONE_SHOT             (1<<3)
+
+struct gs_device_mode {
+       u32 mode;
+       u32 flags;
+} __packed;
+
+struct gs_device_state {
+       u32 state;
+       u32 rxerr;
+       u32 txerr;
+} __packed;
+
+struct gs_device_bittiming {
+       u32 prop_seg;
+       u32 phase_seg1;
+       u32 phase_seg2;
+       u32 sjw;
+       u32 brp;
+} __packed;
+
+#define GS_CAN_FEATURE_LISTEN_ONLY      (1<<0)
+#define GS_CAN_FEATURE_LOOP_BACK        (1<<1)
+#define GS_CAN_FEATURE_TRIPLE_SAMPLE    (1<<2)
+#define GS_CAN_FEATURE_ONE_SHOT         (1<<3)
+
+struct gs_device_bt_const {
+       u32 feature;
+       u32 fclk_can;
+       u32 tseg1_min;
+       u32 tseg1_max;
+       u32 tseg2_min;
+       u32 tseg2_max;
+       u32 sjw_max;
+       u32 brp_min;
+       u32 brp_max;
+       u32 brp_inc;
+} __packed;
+
+#define GS_CAN_FLAG_OVERFLOW 1
+
+struct gs_host_frame {
+       u32 echo_id;
+       u32 can_id;
+
+       u8 can_dlc;
+       u8 channel;
+       u8 flags;
+       u8 reserved;
+
+       u8 data[8];
+} __packed;
+/* The GS USB devices make use of the same flags and masks as in
+ * linux/can.h and linux/can/error.h, and no additional mapping is necessary.
+ */
+
+/* Only send a max of GS_MAX_TX_URBS frames per channel at a time. */
+#define GS_MAX_TX_URBS 10
+/* Only launch a max of GS_MAX_RX_URBS usb requests at a time. */
+#define GS_MAX_RX_URBS 30
+/* Maximum number of interfaces the driver supports per device.
+ * Current hardware only supports 2 interfaces. The future may vary.
+ */
+#define GS_MAX_INTF 2
+
+struct gs_tx_context {
+       struct gs_can *dev;
+       unsigned int echo_id;
+};
+
+struct gs_can {
+       struct can_priv can; /* must be the first member */
+
+       struct gs_usb *parent;
+
+       struct net_device *netdev;
+       struct usb_device *udev;
+       struct usb_interface *iface;
+
+       struct can_bittiming_const bt_const;
+       unsigned int channel;   /* channel number */
+
+       /* This lock prevents a race condition between xmit and recieve. */
+       spinlock_t tx_ctx_lock;
+       struct gs_tx_context tx_context[GS_MAX_TX_URBS];
+
+       struct usb_anchor tx_submitted;
+       atomic_t active_tx_urbs;
+};
+
+/* usb interface struct */
+struct gs_usb {
+       struct gs_can *canch[GS_MAX_INTF];
+       struct usb_anchor rx_submitted;
+       atomic_t active_channels;
+       struct usb_device *udev;
+};
+
+/* 'allocate' a tx context.
+ * returns a valid tx context or NULL if there is no space.
+ */
+static struct gs_tx_context *gs_alloc_tx_context(struct gs_can *dev)
+{
+       int i = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->tx_ctx_lock, flags);
+
+       for (; i < GS_MAX_TX_URBS; i++) {
+               if (dev->tx_context[i].echo_id == GS_MAX_TX_URBS) {
+                       dev->tx_context[i].echo_id = i;
+                       spin_unlock_irqrestore(&dev->tx_ctx_lock, flags);
+                       return &dev->tx_context[i];
+               }
+       }
+
+       spin_unlock_irqrestore(&dev->tx_ctx_lock, flags);
+       return NULL;
+}
+
+/* releases a tx context
+ */
+static void gs_free_tx_context(struct gs_tx_context *txc)
+{
+       txc->echo_id = GS_MAX_TX_URBS;
+}
+
+/* Get a tx context by id.
+ */
+static struct gs_tx_context *gs_get_tx_context(struct gs_can *dev, unsigned int id)
+{
+       unsigned long flags;
+
+       if (id < GS_MAX_TX_URBS) {
+               spin_lock_irqsave(&dev->tx_ctx_lock, flags);
+               if (dev->tx_context[id].echo_id == id) {
+                       spin_unlock_irqrestore(&dev->tx_ctx_lock, flags);
+                       return &dev->tx_context[id];
+               }
+               spin_unlock_irqrestore(&dev->tx_ctx_lock, flags);
+       }
+       return NULL;
+}
+
+static int gs_cmd_reset(struct gs_usb *gsusb, struct gs_can *gsdev)
+{
+       struct gs_device_mode *dm;
+       struct usb_interface *intf = gsdev->iface;
+       int rc;
+
+       dm = kzalloc(sizeof(*dm), GFP_KERNEL);
+       if (!dm)
+               return -ENOMEM;
+
+       dm->mode = GS_CAN_MODE_RESET;
+
+       rc = usb_control_msg(interface_to_usbdev(intf),
+                            usb_sndctrlpipe(interface_to_usbdev(intf), 0),
+                            GS_USB_BREQ_MODE,
+                            USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+                            gsdev->channel,
+                            0,
+                            dm,
+                            sizeof(*dm),
+                            1000);
+
+       return rc;
+}
+
+static void gs_update_state(struct gs_can *dev, struct can_frame *cf)
+{
+       struct can_device_stats *can_stats = &dev->can.can_stats;
+
+       if (cf->can_id & CAN_ERR_RESTARTED) {
+               dev->can.state = CAN_STATE_ERROR_ACTIVE;
+               can_stats->restarts++;
+       } else if (cf->can_id & CAN_ERR_BUSOFF) {
+               dev->can.state = CAN_STATE_BUS_OFF;
+               can_stats->bus_off++;
+       } else if (cf->can_id & CAN_ERR_CRTL) {
+               if ((cf->data[1] & CAN_ERR_CRTL_TX_WARNING) ||
+                   (cf->data[1] & CAN_ERR_CRTL_RX_WARNING)) {
+                       dev->can.state = CAN_STATE_ERROR_WARNING;
+                       can_stats->error_warning++;
+               } else if ((cf->data[1] & CAN_ERR_CRTL_TX_PASSIVE) ||
+                          (cf->data[1] & CAN_ERR_CRTL_RX_PASSIVE)) {
+                       dev->can.state = CAN_STATE_ERROR_PASSIVE;
+                       can_stats->error_passive++;
+               } else {
+                       dev->can.state = CAN_STATE_ERROR_ACTIVE;
+               }
+       }
+}
+
+static void gs_usb_recieve_bulk_callback(struct urb *urb)
+{
+       struct gs_usb *usbcan = urb->context;
+       struct gs_can *dev;
+       struct net_device *netdev;
+       int rc;
+       struct net_device_stats *stats;
+       struct gs_host_frame *hf = urb->transfer_buffer;
+       struct gs_tx_context *txc;
+       struct can_frame *cf;
+       struct sk_buff *skb;
+
+       BUG_ON(!usbcan);
+
+       switch (urb->status) {
+       case 0: /* success */
+               break;
+       case -ENOENT:
+       case -ESHUTDOWN:
+               return;
+       default:
+               /* do not resubmit aborted urbs. eg: when device goes down */
+               return;
+       }
+
+       /* device reports out of range channel id */
+       if (hf->channel >= GS_MAX_INTF)
+               goto resubmit_urb;
+
+       dev = usbcan->canch[hf->channel];
+
+       netdev = dev->netdev;
+       stats = &netdev->stats;
+
+       if (!netif_device_present(netdev))
+               return;
+
+       if (hf->echo_id == -1) { /* normal rx */
+               skb = alloc_can_skb(dev->netdev, &cf);
+               if (!skb)
+                       return;
+
+               cf->can_id = hf->can_id;
+
+               cf->can_dlc = get_can_dlc(hf->can_dlc);
+               memcpy(cf->data, hf->data, 8);
+
+               /* ERROR frames tell us information about the controller */
+               if (hf->can_id & CAN_ERR_FLAG)
+                       gs_update_state(dev, cf);
+
+               netdev->stats.rx_packets++;
+               netdev->stats.rx_bytes += hf->can_dlc;
+
+               netif_rx(skb);
+       } else { /* echo_id == hf->echo_id */
+               if (hf->echo_id >= GS_MAX_TX_URBS) {
+                       netdev_err(netdev,
+                                  "Unexpected out of range echo id %d\n",
+                                  hf->echo_id);
+                       goto resubmit_urb;
+               }
+
+               netdev->stats.tx_packets++;
+               netdev->stats.tx_bytes += hf->can_dlc;
+
+               txc = gs_get_tx_context(dev, hf->echo_id);
+
+               /* bad devices send bad echo_ids. */
+               if (!txc) {
+                       netdev_err(netdev,
+                                  "Unexpected unused echo id %d\n",
+                                  hf->echo_id);
+                       goto resubmit_urb;
+               }
+
+               can_get_echo_skb(netdev, hf->echo_id);
+
+               gs_free_tx_context(txc);
+
+               netif_wake_queue(netdev);
+       }
+
+       if (hf->flags & GS_CAN_FLAG_OVERFLOW) {
+               skb = alloc_can_err_skb(netdev, &cf);
+               if (!skb)
+                       goto resubmit_urb;
+
+               cf->can_id |= CAN_ERR_CRTL;
+               cf->can_dlc = CAN_ERR_DLC;
+               cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+               stats->rx_over_errors++;
+               stats->rx_errors++;
+               netif_rx(skb);
+       }
+
+ resubmit_urb:
+       usb_fill_bulk_urb(urb,
+                         usbcan->udev,
+                         usb_rcvbulkpipe(usbcan->udev, GSUSB_ENDPOINT_IN),
+                         hf,
+                         sizeof(struct gs_host_frame),
+                         gs_usb_recieve_bulk_callback,
+                         usbcan
+                         );
+
+       rc = usb_submit_urb(urb, GFP_ATOMIC);
+
+       /* USB failure take down all interfaces */
+       if (rc == -ENODEV) {
+               for (rc = 0; rc < GS_MAX_INTF; rc++) {
+                       if (usbcan->canch[rc])
+                               netif_device_detach(usbcan->canch[rc]->netdev);
+               }
+       }
+}
+
+static int gs_usb_set_bittiming(struct net_device *netdev)
+{
+       struct gs_can *dev = netdev_priv(netdev);
+       struct can_bittiming *bt = &dev->can.bittiming;
+       struct usb_interface *intf = dev->iface;
+       int rc;
+       struct gs_device_bittiming *dbt;
+
+       dbt = kmalloc(sizeof(*dbt), GFP_KERNEL);
+       if (!dbt)
+               return -ENOMEM;
+
+       dbt->prop_seg = bt->prop_seg;
+       dbt->phase_seg1 = bt->phase_seg1;
+       dbt->phase_seg2 = bt->phase_seg2;
+       dbt->sjw = bt->sjw;
+       dbt->brp = bt->brp;
+
+       /* request bit timings */
+       rc = usb_control_msg(interface_to_usbdev(intf),
+                            usb_sndctrlpipe(interface_to_usbdev(intf), 0),
+                            GS_USB_BREQ_BITTIMING,
+                            USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+                            dev->channel,
+                            0,
+                            dbt,
+                            sizeof(*dbt),
+                            1000);
+
+       kfree(dbt);
+
+       if (rc < 0)
+               dev_err(netdev->dev.parent, "Couldn't set bittimings (err=%d)",
+                       rc);
+
+       return rc;
+}
+
+static void gs_usb_xmit_callback(struct urb *urb)
+{
+       struct gs_tx_context *txc = urb->context;
+       struct gs_can *dev = txc->dev;
+       struct net_device *netdev = dev->netdev;
+
+       if (urb->status)
+               netdev_info(netdev, "usb xmit fail %d\n", txc->echo_id);
+
+       usb_free_coherent(urb->dev,
+                         urb->transfer_buffer_length,
+                         urb->transfer_buffer,
+                         urb->transfer_dma);
+
+       atomic_dec(&dev->active_tx_urbs);
+
+       if (!netif_device_present(netdev))
+               return;
+
+       if (netif_queue_stopped(netdev))
+               netif_wake_queue(netdev);
+}
+
+static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+       struct gs_can *dev = netdev_priv(netdev);
+       struct net_device_stats *stats = &dev->netdev->stats;
+       struct urb *urb;
+       struct gs_host_frame *hf;
+       struct can_frame *cf;
+       int rc;
+       unsigned int idx;
+       struct gs_tx_context *txc;
+
+       if (can_dropped_invalid_skb(netdev, skb))
+               return NETDEV_TX_OK;
+
+       /* find an empty context to keep track of transmission */
+       txc = gs_alloc_tx_context(dev);
+       if (!txc)
+               return NETDEV_TX_BUSY;
+
+       /* create a URB, and a buffer for it */
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (!urb) {
+               netdev_err(netdev, "No memory left for URB\n");
+               goto nomem_urb;
+       }
+
+       hf = usb_alloc_coherent(dev->udev, sizeof(*hf), GFP_ATOMIC,
+                               &urb->transfer_dma);
+       if (!hf) {
+               netdev_err(netdev, "No memory left for USB buffer\n");
+               goto nomem_hf;
+       }
+
+       idx = txc->echo_id;
+
+       if (idx >= GS_MAX_TX_URBS) {
+               netdev_err(netdev, "Invalid tx context %d\n", idx);
+               goto badidx;
+       }
+
+       hf->echo_id = idx;
+       hf->channel = dev->channel;
+
+       cf = (struct can_frame *)skb->data;
+
+       hf->can_id = cf->can_id;
+       hf->can_dlc = cf->can_dlc;
+       memcpy(hf->data, cf->data, cf->can_dlc);
+
+       usb_fill_bulk_urb(urb, dev->udev,
+                         usb_sndbulkpipe(dev->udev, GSUSB_ENDPOINT_OUT),
+                         hf,
+                         sizeof(*hf),
+                         gs_usb_xmit_callback,
+                         txc);
+
+       urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+       usb_anchor_urb(urb, &dev->tx_submitted);
+
+       can_put_echo_skb(skb, netdev, idx);
+
+       atomic_inc(&dev->active_tx_urbs);
+
+       rc = usb_submit_urb(urb, GFP_ATOMIC);
+       if (unlikely(rc)) {                     /* usb send failed */
+               atomic_dec(&dev->active_tx_urbs);
+
+               can_free_echo_skb(netdev, idx);
+               gs_free_tx_context(txc);
+
+               usb_unanchor_urb(urb);
+               usb_free_coherent(dev->udev,
+                                 sizeof(*hf),
+                                 hf,
+                                 urb->transfer_dma);
+
+
+               if (rc == -ENODEV) {
+                       netif_device_detach(netdev);
+               } else {
+                       netdev_err(netdev, "usb_submit failed (err=%d)\n", rc);
+                       stats->tx_dropped++;
+               }
+       } else {
+               /* Slow down tx path */
+               if (atomic_read(&dev->active_tx_urbs) >= GS_MAX_TX_URBS)
+                       netif_stop_queue(netdev);
+       }
+
+       /* let usb core take care of this urb */
+       usb_free_urb(urb);
+
+       return NETDEV_TX_OK;
+
+ badidx:
+       usb_free_coherent(dev->udev,
+                         sizeof(*hf),
+                         hf,
+                         urb->transfer_dma);
+ nomem_hf:
+       usb_free_urb(urb);
+
+ nomem_urb:
+       gs_free_tx_context(txc);
+       dev_kfree_skb(skb);
+       stats->tx_dropped++;
+       return NETDEV_TX_OK;
+}
+
+static int gs_can_open(struct net_device *netdev)
+{
+       struct gs_can *dev = netdev_priv(netdev);
+       struct gs_usb *parent = dev->parent;
+       int rc, i;
+       struct gs_device_mode *dm;
+       u32 ctrlmode;
+
+       rc = open_candev(netdev);
+       if (rc)
+               return rc;
+
+       if (atomic_add_return(1, &parent->active_channels) == 1) {
+               for (i = 0; i < GS_MAX_RX_URBS; i++) {
+                       struct urb *urb;
+                       u8 *buf;
+
+                       /* alloc rx urb */
+                       urb = usb_alloc_urb(0, GFP_KERNEL);
+                       if (!urb) {
+                               netdev_err(netdev,
+                                          "No memory left for URB\n");
+                               return -ENOMEM;
+                       }
+
+                       /* alloc rx buffer */
+                       buf = usb_alloc_coherent(dev->udev,
+                                                sizeof(struct gs_host_frame),
+                                                GFP_KERNEL,
+                                                &urb->transfer_dma);
+                       if (!buf) {
+                               netdev_err(netdev,
+                                          "No memory left for USB buffer\n");
+                               usb_free_urb(urb);
+                               return -ENOMEM;
+                       }
+
+                       /* fill, anchor, and submit rx urb */
+                       usb_fill_bulk_urb(urb,
+                                         dev->udev,
+                                         usb_rcvbulkpipe(dev->udev,
+                                                         GSUSB_ENDPOINT_IN),
+                                         buf,
+                                         sizeof(struct gs_host_frame),
+                                         gs_usb_recieve_bulk_callback,
+                                         parent);
+                       urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+                       usb_anchor_urb(urb, &parent->rx_submitted);
+
+                       rc = usb_submit_urb(urb, GFP_KERNEL);
+                       if (rc) {
+                               if (rc == -ENODEV)
+                                       netif_device_detach(dev->netdev);
+
+                               netdev_err(netdev,
+                                          "usb_submit failed (err=%d)\n",
+                                          rc);
+
+                               usb_unanchor_urb(urb);
+                               break;
+                       }
+
+                       /* Drop reference,
+                        * USB core will take care of freeing it
+                        */
+                       usb_free_urb(urb);
+               }
+       }
+
+       dm = kmalloc(sizeof(*dm), GFP_KERNEL);
+       if (!dm)
+               return -ENOMEM;
+
+       /* flags */
+       ctrlmode = dev->can.ctrlmode;
+       dm->flags = 0;
+
+       if (ctrlmode & CAN_CTRLMODE_LOOPBACK)
+               dm->flags |= GS_CAN_MODE_LOOP_BACK;
+       else if (ctrlmode & CAN_CTRLMODE_LISTENONLY)
+               dm->flags |= GS_CAN_MODE_LISTEN_ONLY;
+
+       /* Controller is not allowed to retry TX
+        * this mode is unavailable on atmels uc3c hardware
+        */
+       if (ctrlmode & CAN_CTRLMODE_ONE_SHOT)
+               dm->flags |= GS_CAN_MODE_ONE_SHOT;
+
+       if (ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+               dm->flags |= GS_CAN_MODE_TRIPLE_SAMPLE;
+
+       /* finally start device */
+       dm->mode = GS_CAN_MODE_START;
+       rc = usb_control_msg(interface_to_usbdev(dev->iface),
+                            usb_sndctrlpipe(interface_to_usbdev(dev->iface), 0),
+                            GS_USB_BREQ_MODE,
+                            USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+                            dev->channel,
+                            0,
+                            dm,
+                            sizeof(*dm),
+                            1000);
+
+       if (rc < 0) {
+               netdev_err(netdev, "Couldn't start device (err=%d)\n", rc);
+               kfree(dm);
+               return rc;
+       }
+
+       kfree(dm);
+
+       dev->can.state = CAN_STATE_ERROR_ACTIVE;
+
+       if (!(dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY))
+               netif_start_queue(netdev);
+
+       return 0;
+}
+
+static int gs_can_close(struct net_device *netdev)
+{
+       int rc;
+       struct gs_can *dev = netdev_priv(netdev);
+       struct gs_usb *parent = dev->parent;
+
+       netif_stop_queue(netdev);
+
+       /* Stop polling */
+       if (atomic_dec_and_test(&parent->active_channels))
+               usb_kill_anchored_urbs(&parent->rx_submitted);
+
+       /* Stop sending URBs */
+       usb_kill_anchored_urbs(&dev->tx_submitted);
+       atomic_set(&dev->active_tx_urbs, 0);
+
+       /* reset the device */
+       rc = gs_cmd_reset(parent, dev);
+       if (rc < 0)
+               netdev_warn(netdev, "Couldn't shutdown device (err=%d)", rc);
+
+       /* reset tx contexts */
+       for (rc = 0; rc < GS_MAX_TX_URBS; rc++) {
+               dev->tx_context[rc].dev = dev;
+               dev->tx_context[rc].echo_id = GS_MAX_TX_URBS;
+       }
+
+       /* close the netdev */
+       close_candev(netdev);
+
+       return 0;
+}
+
+static const struct net_device_ops gs_usb_netdev_ops = {
+       .ndo_open = gs_can_open,
+       .ndo_stop = gs_can_close,
+       .ndo_start_xmit = gs_can_start_xmit,
+};
+
+static struct gs_can *gs_make_candev(unsigned int channel, struct usb_interface *intf)
+{
+       struct gs_can *dev;
+       struct net_device *netdev;
+       int rc;
+       struct gs_device_bt_const *bt_const;
+
+       bt_const = kmalloc(sizeof(*bt_const), GFP_KERNEL);
+       if (!bt_const)
+               return ERR_PTR(-ENOMEM);
+
+       /* fetch bit timing constants */
+       rc = usb_control_msg(interface_to_usbdev(intf),
+                            usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
+                            GS_USB_BREQ_BT_CONST,
+                            USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+                            channel,
+                            0,
+                            bt_const,
+                            sizeof(*bt_const),
+                            1000);
+
+       if (rc < 0) {
+               dev_err(&intf->dev,
+                       "Couldn't get bit timing const for channel (err=%d)\n",
+                       rc);
+               kfree(bt_const);
+               return ERR_PTR(rc);
+       }
+
+       /* create netdev */
+       netdev = alloc_candev(sizeof(struct gs_can), GS_MAX_TX_URBS);
+       if (!netdev) {
+               dev_err(&intf->dev, "Couldn't allocate candev\n");
+               kfree(bt_const);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       dev = netdev_priv(netdev);
+
+       netdev->netdev_ops = &gs_usb_netdev_ops;
+
+       netdev->flags |= IFF_ECHO; /* we support full roundtrip echo */
+
+       /* dev settup */
+       strcpy(dev->bt_const.name, "gs_usb");
+       dev->bt_const.tseg1_min = bt_const->tseg1_min;
+       dev->bt_const.tseg1_max = bt_const->tseg1_max;
+       dev->bt_const.tseg2_min = bt_const->tseg2_min;
+       dev->bt_const.tseg2_max = bt_const->tseg2_max;
+       dev->bt_const.sjw_max = bt_const->sjw_max;
+       dev->bt_const.brp_min = bt_const->brp_min;
+       dev->bt_const.brp_max = bt_const->brp_max;
+       dev->bt_const.brp_inc = bt_const->brp_inc;
+
+       dev->udev = interface_to_usbdev(intf);
+       dev->iface = intf;
+       dev->netdev = netdev;
+       dev->channel = channel;
+
+       init_usb_anchor(&dev->tx_submitted);
+       atomic_set(&dev->active_tx_urbs, 0);
+       spin_lock_init(&dev->tx_ctx_lock);
+       for (rc = 0; rc < GS_MAX_TX_URBS; rc++) {
+               dev->tx_context[rc].dev = dev;
+               dev->tx_context[rc].echo_id = GS_MAX_TX_URBS;
+       }
+
+       /* can settup */
+       dev->can.state = CAN_STATE_STOPPED;
+       dev->can.clock.freq = bt_const->fclk_can;
+       dev->can.bittiming_const = &dev->bt_const;
+       dev->can.do_set_bittiming = gs_usb_set_bittiming;
+
+       dev->can.ctrlmode_supported = 0;
+
+       if (bt_const->feature & GS_CAN_FEATURE_LISTEN_ONLY)
+               dev->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
+
+       if (bt_const->feature & GS_CAN_FEATURE_LOOP_BACK)
+               dev->can.ctrlmode_supported |= CAN_CTRLMODE_LOOPBACK;
+
+       if (bt_const->feature & GS_CAN_FEATURE_TRIPLE_SAMPLE)
+               dev->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES;
+
+       if (bt_const->feature & GS_CAN_FEATURE_ONE_SHOT)
+               dev->can.ctrlmode_supported |= CAN_CTRLMODE_ONE_SHOT;
+
+       kfree(bt_const);
+
+       SET_NETDEV_DEV(netdev, &intf->dev);
+
+       rc = register_candev(dev->netdev);
+       if (rc) {
+               free_candev(dev->netdev);
+               dev_err(&intf->dev, "Couldn't register candev (err=%d)\n", rc);
+               return ERR_PTR(rc);
+       }
+
+       return dev;
+}
+
+static void gs_destroy_candev(struct gs_can *dev)
+{
+       unregister_candev(dev->netdev);
+       free_candev(dev->netdev);
+       usb_kill_anchored_urbs(&dev->tx_submitted);
+       kfree(dev);
+}
+
+static int gs_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+       struct gs_usb *dev;
+       int rc = -ENOMEM;
+       unsigned int icount, i;
+       struct gs_host_config *hconf;
+       struct gs_device_config *dconf;
+
+       hconf = kmalloc(sizeof(*hconf), GFP_KERNEL);
+       if (!hconf)
+               return -ENOMEM;
+
+       hconf->byte_order = 0x0000beef;
+
+       /* send host config */
+       rc = usb_control_msg(interface_to_usbdev(intf),
+                            usb_sndctrlpipe(interface_to_usbdev(intf), 0),
+                            GS_USB_BREQ_HOST_FORMAT,
+                            USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+                            1,
+                            intf->altsetting[0].desc.bInterfaceNumber,
+                            hconf,
+                            sizeof(*hconf),
+                            1000);
+
+       kfree(hconf);
+
+       if (rc < 0) {
+               dev_err(&intf->dev, "Couldn't send data format (err=%d)\n",
+                       rc);
+               return rc;
+       }
+
+       dconf = kmalloc(sizeof(*dconf), GFP_KERNEL);
+       if (!dconf)
+               return -ENOMEM;
+
+       /* read device config */
+       rc = usb_control_msg(interface_to_usbdev(intf),
+                            usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
+                            GS_USB_BREQ_DEVICE_CONFIG,
+                            USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+                            1,
+                            intf->altsetting[0].desc.bInterfaceNumber,
+                            dconf,
+                            sizeof(*dconf),
+                            1000);
+       if (rc < 0) {
+               dev_err(&intf->dev, "Couldn't get device config: (err=%d)\n",
+                       rc);
+
+               kfree(dconf);
+
+               return rc;
+       }
+
+       icount = dconf->icount+1;
+
+       kfree(dconf);
+
+       dev_info(&intf->dev, "Configuring for %d interfaces\n", icount);
+
+       if (icount > GS_MAX_INTF) {
+               dev_err(&intf->dev,
+                       "Driver cannot handle more that %d CAN interfaces\n",
+                       GS_MAX_INTF);
+               return -EINVAL;
+       }
+
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       init_usb_anchor(&dev->rx_submitted);
+
+       atomic_set(&dev->active_channels, 0);
+
+       usb_set_intfdata(intf, dev);
+       dev->udev = interface_to_usbdev(intf);
+
+       for (i = 0; i < icount; i++) {
+               dev->canch[i] = gs_make_candev(i, intf);
+               if (IS_ERR_OR_NULL(dev->canch[i])) {
+                       /* on failure destroy previously created candevs */
+                       icount = i;
+                       for (i = 0; i < icount; i++) {
+                               gs_destroy_candev(dev->canch[i]);
+                               dev->canch[i] = NULL;
+                       }
+                       kfree(dev);
+                       return rc;
+               }
+               dev->canch[i]->parent = dev;
+       }
+
+       return 0;
+}
+
+static void gs_usb_disconnect(struct usb_interface *intf)
+{
+       unsigned i;
+       struct gs_usb *dev = usb_get_intfdata(intf);
+       usb_set_intfdata(intf, NULL);
+
+       if (!dev) {
+               dev_err(&intf->dev, "Disconnect (nodata)\n");
+               return;
+       }
+
+       for (i = 0; i < GS_MAX_INTF; i++) {
+               struct gs_can *can = dev->canch[i];
+
+               if (!can)
+                       continue;
+
+               gs_destroy_candev(can);
+       }
+
+       usb_kill_anchored_urbs(&dev->rx_submitted);
+}
+
+static const struct usb_device_id gs_usb_table[] = {
+       {USB_DEVICE(USB_GSUSB_1_VENDOR_ID, USB_GSUSB_1_PRODUCT_ID)},
+       {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, gs_usb_table);
+
+static struct usb_driver gs_usb_driver = {
+       .name       = "gs_usb",
+       .probe      = gs_usb_probe,
+       .disconnect = gs_usb_disconnect,
+       .id_table   = gs_usb_table,
+};
+
+module_usb_driver(gs_usb_driver);
+
+MODULE_AUTHOR("Maximilian Schneider <mws@schneidersoft.net>");
+MODULE_DESCRIPTION(
+"Socket CAN device driver for Geschwister Schneider Technologie-, "
+"Entwicklungs- und Vertriebs UG. USB2.0 to CAN interfaces.");
+MODULE_LICENSE("GPL v2");
index 4ca46edc061d761169a85fc58937dee449a4776f..541fb7a05625aaf889089704ec155956629e77c8 100644 (file)
@@ -53,6 +53,8 @@
 #define USB_OEM_MERCURY_PRODUCT_ID     34
 #define USB_OEM_LEAF_PRODUCT_ID                35
 #define USB_CAN_R_PRODUCT_ID           39
+#define USB_LEAF_LITE_V2_PRODUCT_ID    288
+#define USB_MINI_PCIE_HS_PRODUCT_ID    289
 
 /* USB devices features */
 #define KVASER_HAS_SILENT_MODE         BIT(0)
@@ -356,6 +358,8 @@ static const struct usb_device_id kvaser_usb_table[] = {
                .driver_info = KVASER_HAS_TXRX_ERRORS },
        { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID),
                .driver_info = KVASER_HAS_TXRX_ERRORS },
+       { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_V2_PRODUCT_ID) },
+       { USB_DEVICE(KVASER_VENDOR_ID, USB_MINI_PCIE_HS_PRODUCT_ID) },
        { }
 };
 MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
@@ -379,38 +383,43 @@ static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id,
        void *buf;
        int actual_len;
        int err;
-       int pos = 0;
+       int pos;
+       unsigned long to = jiffies + msecs_to_jiffies(USB_RECV_TIMEOUT);
 
        buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL);
        if (!buf)
                return -ENOMEM;
 
-       err = usb_bulk_msg(dev->udev,
-                          usb_rcvbulkpipe(dev->udev,
-                                          dev->bulk_in->bEndpointAddress),
-                          buf, RX_BUFFER_SIZE, &actual_len,
-                          USB_RECV_TIMEOUT);
-       if (err < 0)
-               goto end;
+       do {
+               err = usb_bulk_msg(dev->udev,
+                                  usb_rcvbulkpipe(dev->udev,
+                                       dev->bulk_in->bEndpointAddress),
+                                  buf, RX_BUFFER_SIZE, &actual_len,
+                                  USB_RECV_TIMEOUT);
+               if (err < 0)
+                       goto end;
 
-       while (pos <= actual_len - MSG_HEADER_LEN) {
-               tmp = buf + pos;
+               pos = 0;
+               while (pos <= actual_len - MSG_HEADER_LEN) {
+                       tmp = buf + pos;
 
-               if (!tmp->len)
-                       break;
+                       if (!tmp->len)
+                               break;
 
-               if (pos + tmp->len > actual_len) {
-                       dev_err(dev->udev->dev.parent, "Format error\n");
-                       break;
-               }
+                       if (pos + tmp->len > actual_len) {
+                               dev_err(dev->udev->dev.parent,
+                                       "Format error\n");
+                               break;
+                       }
 
-               if (tmp->id == id) {
-                       memcpy(msg, tmp, tmp->len);
-                       goto end;
-               }
+                       if (tmp->id == id) {
+                               memcpy(msg, tmp, tmp->len);
+                               goto end;
+                       }
 
-               pos += tmp->len;
-       }
+                       pos += tmp->len;
+               }
+       } while (time_before(jiffies, to));
 
        err = -EINVAL;
 
diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c
new file mode 100644 (file)
index 0000000..5e8b560
--- /dev/null
@@ -0,0 +1,1208 @@
+/* Xilinx CAN device driver
+ *
+ * Copyright (C) 2012 - 2014 Xilinx, Inc.
+ * Copyright (C) 2009 PetaLogix. All rights reserved.
+ *
+ * Description:
+ * This driver is developed for Axi CAN IP and for Zynq CANPS Controller.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <linux/can/led.h>
+
+#define DRIVER_NAME    "xilinx_can"
+
+/* CAN registers set */
+enum xcan_reg {
+       XCAN_SRR_OFFSET         = 0x00, /* Software reset */
+       XCAN_MSR_OFFSET         = 0x04, /* Mode select */
+       XCAN_BRPR_OFFSET        = 0x08, /* Baud rate prescaler */
+       XCAN_BTR_OFFSET         = 0x0C, /* Bit timing */
+       XCAN_ECR_OFFSET         = 0x10, /* Error counter */
+       XCAN_ESR_OFFSET         = 0x14, /* Error status */
+       XCAN_SR_OFFSET          = 0x18, /* Status */
+       XCAN_ISR_OFFSET         = 0x1C, /* Interrupt status */
+       XCAN_IER_OFFSET         = 0x20, /* Interrupt enable */
+       XCAN_ICR_OFFSET         = 0x24, /* Interrupt clear */
+       XCAN_TXFIFO_ID_OFFSET   = 0x30,/* TX FIFO ID */
+       XCAN_TXFIFO_DLC_OFFSET  = 0x34, /* TX FIFO DLC */
+       XCAN_TXFIFO_DW1_OFFSET  = 0x38, /* TX FIFO Data Word 1 */
+       XCAN_TXFIFO_DW2_OFFSET  = 0x3C, /* TX FIFO Data Word 2 */
+       XCAN_RXFIFO_ID_OFFSET   = 0x50, /* RX FIFO ID */
+       XCAN_RXFIFO_DLC_OFFSET  = 0x54, /* RX FIFO DLC */
+       XCAN_RXFIFO_DW1_OFFSET  = 0x58, /* RX FIFO Data Word 1 */
+       XCAN_RXFIFO_DW2_OFFSET  = 0x5C, /* RX FIFO Data Word 2 */
+};
+
+/* CAN register bit masks - XCAN_<REG>_<BIT>_MASK */
+#define XCAN_SRR_CEN_MASK              0x00000002 /* CAN enable */
+#define XCAN_SRR_RESET_MASK            0x00000001 /* Soft Reset the CAN core */
+#define XCAN_MSR_LBACK_MASK            0x00000002 /* Loop back mode select */
+#define XCAN_MSR_SLEEP_MASK            0x00000001 /* Sleep mode select */
+#define XCAN_BRPR_BRP_MASK             0x000000FF /* Baud rate prescaler */
+#define XCAN_BTR_SJW_MASK              0x00000180 /* Synchronous jump width */
+#define XCAN_BTR_TS2_MASK              0x00000070 /* Time segment 2 */
+#define XCAN_BTR_TS1_MASK              0x0000000F /* Time segment 1 */
+#define XCAN_ECR_REC_MASK              0x0000FF00 /* Receive error counter */
+#define XCAN_ECR_TEC_MASK              0x000000FF /* Transmit error counter */
+#define XCAN_ESR_ACKER_MASK            0x00000010 /* ACK error */
+#define XCAN_ESR_BERR_MASK             0x00000008 /* Bit error */
+#define XCAN_ESR_STER_MASK             0x00000004 /* Stuff error */
+#define XCAN_ESR_FMER_MASK             0x00000002 /* Form error */
+#define XCAN_ESR_CRCER_MASK            0x00000001 /* CRC error */
+#define XCAN_SR_TXFLL_MASK             0x00000400 /* TX FIFO is full */
+#define XCAN_SR_ESTAT_MASK             0x00000180 /* Error status */
+#define XCAN_SR_ERRWRN_MASK            0x00000040 /* Error warning */
+#define XCAN_SR_NORMAL_MASK            0x00000008 /* Normal mode */
+#define XCAN_SR_LBACK_MASK             0x00000002 /* Loop back mode */
+#define XCAN_SR_CONFIG_MASK            0x00000001 /* Configuration mode */
+#define XCAN_IXR_TXFEMP_MASK           0x00004000 /* TX FIFO Empty */
+#define XCAN_IXR_WKUP_MASK             0x00000800 /* Wake up interrupt */
+#define XCAN_IXR_SLP_MASK              0x00000400 /* Sleep interrupt */
+#define XCAN_IXR_BSOFF_MASK            0x00000200 /* Bus off interrupt */
+#define XCAN_IXR_ERROR_MASK            0x00000100 /* Error interrupt */
+#define XCAN_IXR_RXNEMP_MASK           0x00000080 /* RX FIFO NotEmpty intr */
+#define XCAN_IXR_RXOFLW_MASK           0x00000040 /* RX FIFO Overflow intr */
+#define XCAN_IXR_RXOK_MASK             0x00000010 /* Message received intr */
+#define XCAN_IXR_TXFLL_MASK            0x00000004 /* Tx FIFO Full intr */
+#define XCAN_IXR_TXOK_MASK             0x00000002 /* TX successful intr */
+#define XCAN_IXR_ARBLST_MASK           0x00000001 /* Arbitration lost intr */
+#define XCAN_IDR_ID1_MASK              0xFFE00000 /* Standard msg identifier */
+#define XCAN_IDR_SRR_MASK              0x00100000 /* Substitute remote TXreq */
+#define XCAN_IDR_IDE_MASK              0x00080000 /* Identifier extension */
+#define XCAN_IDR_ID2_MASK              0x0007FFFE /* Extended message ident */
+#define XCAN_IDR_RTR_MASK              0x00000001 /* Remote TX request */
+#define XCAN_DLCR_DLC_MASK             0xF0000000 /* Data length code */
+
+#define XCAN_INTR_ALL          (XCAN_IXR_TXOK_MASK | XCAN_IXR_BSOFF_MASK |\
+                                XCAN_IXR_WKUP_MASK | XCAN_IXR_SLP_MASK | \
+                                XCAN_IXR_RXNEMP_MASK | XCAN_IXR_ERROR_MASK | \
+                                XCAN_IXR_ARBLST_MASK | XCAN_IXR_RXOK_MASK)
+
+/* CAN register bit shift - XCAN_<REG>_<BIT>_SHIFT */
+#define XCAN_BTR_SJW_SHIFT             7  /* Synchronous jump width */
+#define XCAN_BTR_TS2_SHIFT             4  /* Time segment 2 */
+#define XCAN_IDR_ID1_SHIFT             21 /* Standard Messg Identifier */
+#define XCAN_IDR_ID2_SHIFT             1  /* Extended Message Identifier */
+#define XCAN_DLCR_DLC_SHIFT            28 /* Data length code */
+#define XCAN_ESR_REC_SHIFT             8  /* Rx Error Count */
+
+/* CAN frame length constants */
+#define XCAN_FRAME_MAX_DATA_LEN                8
+#define XCAN_TIMEOUT                   (1 * HZ)
+
+/**
+ * struct xcan_priv - This definition define CAN driver instance
+ * @can:                       CAN private data structure.
+ * @tx_head:                   Tx CAN packets ready to send on the queue
+ * @tx_tail:                   Tx CAN packets successfully sended on the queue
+ * @tx_max:                    Maximum number packets the driver can send
+ * @napi:                      NAPI structure
+ * @read_reg:                  For reading data from CAN registers
+ * @write_reg:                 For writing data to CAN registers
+ * @dev:                       Network device data structure
+ * @reg_base:                  Ioremapped address to registers
+ * @irq_flags:                 For request_irq()
+ * @bus_clk:                   Pointer to struct clk
+ * @can_clk:                   Pointer to struct clk
+ */
+struct xcan_priv {
+       struct can_priv can;
+       unsigned int tx_head;
+       unsigned int tx_tail;
+       unsigned int tx_max;
+       struct napi_struct napi;
+       u32 (*read_reg)(const struct xcan_priv *priv, enum xcan_reg reg);
+       void (*write_reg)(const struct xcan_priv *priv, enum xcan_reg reg,
+                       u32 val);
+       struct net_device *dev;
+       void __iomem *reg_base;
+       unsigned long irq_flags;
+       struct clk *bus_clk;
+       struct clk *can_clk;
+};
+
+/* CAN Bittiming constants as per Xilinx CAN specs */
+static const struct can_bittiming_const xcan_bittiming_const = {
+       .name = DRIVER_NAME,
+       .tseg1_min = 1,
+       .tseg1_max = 16,
+       .tseg2_min = 1,
+       .tseg2_max = 8,
+       .sjw_max = 4,
+       .brp_min = 1,
+       .brp_max = 256,
+       .brp_inc = 1,
+};
+
+/**
+ * xcan_write_reg_le - Write a value to the device register little endian
+ * @priv:      Driver private data structure
+ * @reg:       Register offset
+ * @val:       Value to write at the Register offset
+ *
+ * Write data to the paricular CAN register
+ */
+static void xcan_write_reg_le(const struct xcan_priv *priv, enum xcan_reg reg,
+                       u32 val)
+{
+       iowrite32(val, priv->reg_base + reg);
+}
+
+/**
+ * xcan_read_reg_le - Read a value from the device register little endian
+ * @priv:      Driver private data structure
+ * @reg:       Register offset
+ *
+ * Read data from the particular CAN register
+ * Return: value read from the CAN register
+ */
+static u32 xcan_read_reg_le(const struct xcan_priv *priv, enum xcan_reg reg)
+{
+       return ioread32(priv->reg_base + reg);
+}
+
+/**
+ * xcan_write_reg_be - Write a value to the device register big endian
+ * @priv:      Driver private data structure
+ * @reg:       Register offset
+ * @val:       Value to write at the Register offset
+ *
+ * Write data to the paricular CAN register
+ */
+static void xcan_write_reg_be(const struct xcan_priv *priv, enum xcan_reg reg,
+                       u32 val)
+{
+       iowrite32be(val, priv->reg_base + reg);
+}
+
+/**
+ * xcan_read_reg_be - Read a value from the device register big endian
+ * @priv:      Driver private data structure
+ * @reg:       Register offset
+ *
+ * Read data from the particular CAN register
+ * Return: value read from the CAN register
+ */
+static u32 xcan_read_reg_be(const struct xcan_priv *priv, enum xcan_reg reg)
+{
+       return ioread32be(priv->reg_base + reg);
+}
+
+/**
+ * set_reset_mode - Resets the CAN device mode
+ * @ndev:      Pointer to net_device structure
+ *
+ * This is the driver reset mode routine.The driver
+ * enters into configuration mode.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int set_reset_mode(struct net_device *ndev)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       unsigned long timeout;
+
+       priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
+
+       timeout = jiffies + XCAN_TIMEOUT;
+       while (!(priv->read_reg(priv, XCAN_SR_OFFSET) & XCAN_SR_CONFIG_MASK)) {
+               if (time_after(jiffies, timeout)) {
+                       netdev_warn(ndev, "timed out for config mode\n");
+                       return -ETIMEDOUT;
+               }
+               usleep_range(500, 10000);
+       }
+
+       return 0;
+}
+
+/**
+ * xcan_set_bittiming - CAN set bit timing routine
+ * @ndev:      Pointer to net_device structure
+ *
+ * This is the driver set bittiming  routine.
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_set_bittiming(struct net_device *ndev)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       struct can_bittiming *bt = &priv->can.bittiming;
+       u32 btr0, btr1;
+       u32 is_config_mode;
+
+       /* Check whether Xilinx CAN is in configuration mode.
+        * It cannot set bit timing if Xilinx CAN is not in configuration mode.
+        */
+       is_config_mode = priv->read_reg(priv, XCAN_SR_OFFSET) &
+                               XCAN_SR_CONFIG_MASK;
+       if (!is_config_mode) {
+               netdev_alert(ndev,
+                    "BUG! Cannot set bittiming - CAN is not in config mode\n");
+               return -EPERM;
+       }
+
+       /* Setting Baud Rate prescalar value in BRPR Register */
+       btr0 = (bt->brp - 1);
+
+       /* Setting Time Segment 1 in BTR Register */
+       btr1 = (bt->prop_seg + bt->phase_seg1 - 1);
+
+       /* Setting Time Segment 2 in BTR Register */
+       btr1 |= (bt->phase_seg2 - 1) << XCAN_BTR_TS2_SHIFT;
+
+       /* Setting Synchronous jump width in BTR Register */
+       btr1 |= (bt->sjw - 1) << XCAN_BTR_SJW_SHIFT;
+
+       priv->write_reg(priv, XCAN_BRPR_OFFSET, btr0);
+       priv->write_reg(priv, XCAN_BTR_OFFSET, btr1);
+
+       netdev_dbg(ndev, "BRPR=0x%08x, BTR=0x%08x\n",
+                       priv->read_reg(priv, XCAN_BRPR_OFFSET),
+                       priv->read_reg(priv, XCAN_BTR_OFFSET));
+
+       return 0;
+}
+
+/**
+ * xcan_chip_start - This the drivers start routine
+ * @ndev:      Pointer to net_device structure
+ *
+ * This is the drivers start routine.
+ * Based on the State of the CAN device it puts
+ * the CAN device into a proper mode.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_chip_start(struct net_device *ndev)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       u32 err, reg_msr, reg_sr_mask;
+       unsigned long timeout;
+
+       /* Check if it is in reset mode */
+       err = set_reset_mode(ndev);
+       if (err < 0)
+               return err;
+
+       err = xcan_set_bittiming(ndev);
+       if (err < 0)
+               return err;
+
+       /* Enable interrupts */
+       priv->write_reg(priv, XCAN_IER_OFFSET, XCAN_INTR_ALL);
+
+       /* Check whether it is loopback mode or normal mode  */
+       if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
+               reg_msr = XCAN_MSR_LBACK_MASK;
+               reg_sr_mask = XCAN_SR_LBACK_MASK;
+       } else {
+               reg_msr = 0x0;
+               reg_sr_mask = XCAN_SR_NORMAL_MASK;
+       }
+
+       priv->write_reg(priv, XCAN_MSR_OFFSET, reg_msr);
+       priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK);
+
+       timeout = jiffies + XCAN_TIMEOUT;
+       while (!(priv->read_reg(priv, XCAN_SR_OFFSET) & reg_sr_mask)) {
+               if (time_after(jiffies, timeout)) {
+                       netdev_warn(ndev,
+                               "timed out for correct mode\n");
+                       return -ETIMEDOUT;
+               }
+       }
+       netdev_dbg(ndev, "status:#x%08x\n",
+                       priv->read_reg(priv, XCAN_SR_OFFSET));
+
+       priv->can.state = CAN_STATE_ERROR_ACTIVE;
+       return 0;
+}
+
+/**
+ * xcan_do_set_mode - This sets the mode of the driver
+ * @ndev:      Pointer to net_device structure
+ * @mode:      Tells the mode of the driver
+ *
+ * This check the drivers state and calls the
+ * the corresponding modes to set.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_do_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+       int ret;
+
+       switch (mode) {
+       case CAN_MODE_START:
+               ret = xcan_chip_start(ndev);
+               if (ret < 0) {
+                       netdev_err(ndev, "xcan_chip_start failed!\n");
+                       return ret;
+               }
+               netif_wake_queue(ndev);
+               break;
+       default:
+               ret = -EOPNOTSUPP;
+               break;
+       }
+
+       return ret;
+}
+
+/**
+ * xcan_start_xmit - Starts the transmission
+ * @skb:       sk_buff pointer that contains data to be Txed
+ * @ndev:      Pointer to net_device structure
+ *
+ * This function is invoked from upper layers to initiate transmission. This
+ * function uses the next available free txbuff and populates their fields to
+ * start the transmission.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       struct net_device_stats *stats = &ndev->stats;
+       struct can_frame *cf = (struct can_frame *)skb->data;
+       u32 id, dlc, data[2] = {0, 0};
+
+       if (can_dropped_invalid_skb(ndev, skb))
+               return NETDEV_TX_OK;
+
+       /* Check if the TX buffer is full */
+       if (unlikely(priv->read_reg(priv, XCAN_SR_OFFSET) &
+                       XCAN_SR_TXFLL_MASK)) {
+               netif_stop_queue(ndev);
+               netdev_err(ndev, "BUG!, TX FIFO full when queue awake!\n");
+               return NETDEV_TX_BUSY;
+       }
+
+       /* Watch carefully on the bit sequence */
+       if (cf->can_id & CAN_EFF_FLAG) {
+               /* Extended CAN ID format */
+               id = ((cf->can_id & CAN_EFF_MASK) << XCAN_IDR_ID2_SHIFT) &
+                       XCAN_IDR_ID2_MASK;
+               id |= (((cf->can_id & CAN_EFF_MASK) >>
+                       (CAN_EFF_ID_BITS-CAN_SFF_ID_BITS)) <<
+                       XCAN_IDR_ID1_SHIFT) & XCAN_IDR_ID1_MASK;
+
+               /* The substibute remote TX request bit should be "1"
+                * for extended frames as in the Xilinx CAN datasheet
+                */
+               id |= XCAN_IDR_IDE_MASK | XCAN_IDR_SRR_MASK;
+
+               if (cf->can_id & CAN_RTR_FLAG)
+                       /* Extended frames remote TX request */
+                       id |= XCAN_IDR_RTR_MASK;
+       } else {
+               /* Standard CAN ID format */
+               id = ((cf->can_id & CAN_SFF_MASK) << XCAN_IDR_ID1_SHIFT) &
+                       XCAN_IDR_ID1_MASK;
+
+               if (cf->can_id & CAN_RTR_FLAG)
+                       /* Standard frames remote TX request */
+                       id |= XCAN_IDR_SRR_MASK;
+       }
+
+       dlc = cf->can_dlc << XCAN_DLCR_DLC_SHIFT;
+
+       if (cf->can_dlc > 0)
+               data[0] = be32_to_cpup((__be32 *)(cf->data + 0));
+       if (cf->can_dlc > 4)
+               data[1] = be32_to_cpup((__be32 *)(cf->data + 4));
+
+       can_put_echo_skb(skb, ndev, priv->tx_head % priv->tx_max);
+       priv->tx_head++;
+
+       /* Write the Frame to Xilinx CAN TX FIFO */
+       priv->write_reg(priv, XCAN_TXFIFO_ID_OFFSET, id);
+       /* If the CAN frame is RTR frame this write triggers tranmission */
+       priv->write_reg(priv, XCAN_TXFIFO_DLC_OFFSET, dlc);
+       if (!(cf->can_id & CAN_RTR_FLAG)) {
+               priv->write_reg(priv, XCAN_TXFIFO_DW1_OFFSET, data[0]);
+               /* If the CAN frame is Standard/Extended frame this
+                * write triggers tranmission
+                */
+               priv->write_reg(priv, XCAN_TXFIFO_DW2_OFFSET, data[1]);
+               stats->tx_bytes += cf->can_dlc;
+       }
+
+       /* Check if the TX buffer is full */
+       if ((priv->tx_head - priv->tx_tail) == priv->tx_max)
+               netif_stop_queue(ndev);
+
+       return NETDEV_TX_OK;
+}
+
+/**
+ * xcan_rx -  Is called from CAN isr to complete the received
+ *             frame  processing
+ * @ndev:      Pointer to net_device structure
+ *
+ * This function is invoked from the CAN isr(poll) to process the Rx frames. It
+ * does minimal processing and invokes "netif_receive_skb" to complete further
+ * processing.
+ * Return: 1 on success and 0 on failure.
+ */
+static int xcan_rx(struct net_device *ndev)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       struct net_device_stats *stats = &ndev->stats;
+       struct can_frame *cf;
+       struct sk_buff *skb;
+       u32 id_xcan, dlc, data[2] = {0, 0};
+
+       skb = alloc_can_skb(ndev, &cf);
+       if (unlikely(!skb)) {
+               stats->rx_dropped++;
+               return 0;
+       }
+
+       /* Read a frame from Xilinx zynq CANPS */
+       id_xcan = priv->read_reg(priv, XCAN_RXFIFO_ID_OFFSET);
+       dlc = priv->read_reg(priv, XCAN_RXFIFO_DLC_OFFSET) >>
+                               XCAN_DLCR_DLC_SHIFT;
+
+       /* Change Xilinx CAN data length format to socketCAN data format */
+       cf->can_dlc = get_can_dlc(dlc);
+
+       /* Change Xilinx CAN ID format to socketCAN ID format */
+       if (id_xcan & XCAN_IDR_IDE_MASK) {
+               /* The received frame is an Extended format frame */
+               cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >> 3;
+               cf->can_id |= (id_xcan & XCAN_IDR_ID2_MASK) >>
+                               XCAN_IDR_ID2_SHIFT;
+               cf->can_id |= CAN_EFF_FLAG;
+               if (id_xcan & XCAN_IDR_RTR_MASK)
+                       cf->can_id |= CAN_RTR_FLAG;
+       } else {
+               /* The received frame is a standard format frame */
+               cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >>
+                               XCAN_IDR_ID1_SHIFT;
+               if (id_xcan & XCAN_IDR_SRR_MASK)
+                       cf->can_id |= CAN_RTR_FLAG;
+       }
+
+       if (!(id_xcan & XCAN_IDR_SRR_MASK)) {
+               data[0] = priv->read_reg(priv, XCAN_RXFIFO_DW1_OFFSET);
+               data[1] = priv->read_reg(priv, XCAN_RXFIFO_DW2_OFFSET);
+
+               /* Change Xilinx CAN data format to socketCAN data format */
+               if (cf->can_dlc > 0)
+                       *(__be32 *)(cf->data) = cpu_to_be32(data[0]);
+               if (cf->can_dlc > 4)
+                       *(__be32 *)(cf->data + 4) = cpu_to_be32(data[1]);
+       }
+
+       stats->rx_bytes += cf->can_dlc;
+       stats->rx_packets++;
+       netif_receive_skb(skb);
+
+       return 1;
+}
+
+/**
+ * xcan_err_interrupt - error frame Isr
+ * @ndev:      net_device pointer
+ * @isr:       interrupt status register value
+ *
+ * This is the CAN error interrupt and it will
+ * check the the type of error and forward the error
+ * frame to upper layers.
+ */
+static void xcan_err_interrupt(struct net_device *ndev, u32 isr)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       struct net_device_stats *stats = &ndev->stats;
+       struct can_frame *cf;
+       struct sk_buff *skb;
+       u32 err_status, status, txerr = 0, rxerr = 0;
+
+       skb = alloc_can_err_skb(ndev, &cf);
+
+       err_status = priv->read_reg(priv, XCAN_ESR_OFFSET);
+       priv->write_reg(priv, XCAN_ESR_OFFSET, err_status);
+       txerr = priv->read_reg(priv, XCAN_ECR_OFFSET) & XCAN_ECR_TEC_MASK;
+       rxerr = ((priv->read_reg(priv, XCAN_ECR_OFFSET) &
+                       XCAN_ECR_REC_MASK) >> XCAN_ESR_REC_SHIFT);
+       status = priv->read_reg(priv, XCAN_SR_OFFSET);
+
+       if (isr & XCAN_IXR_BSOFF_MASK) {
+               priv->can.state = CAN_STATE_BUS_OFF;
+               priv->can.can_stats.bus_off++;
+               /* Leave device in Config Mode in bus-off state */
+               priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
+               can_bus_off(ndev);
+               if (skb)
+                       cf->can_id |= CAN_ERR_BUSOFF;
+       } else if ((status & XCAN_SR_ESTAT_MASK) == XCAN_SR_ESTAT_MASK) {
+               priv->can.state = CAN_STATE_ERROR_PASSIVE;
+               priv->can.can_stats.error_passive++;
+               if (skb) {
+                       cf->can_id |= CAN_ERR_CRTL;
+                       cf->data[1] = (rxerr > 127) ?
+                                       CAN_ERR_CRTL_RX_PASSIVE :
+                                       CAN_ERR_CRTL_TX_PASSIVE;
+                       cf->data[6] = txerr;
+                       cf->data[7] = rxerr;
+               }
+       } else if (status & XCAN_SR_ERRWRN_MASK) {
+               priv->can.state = CAN_STATE_ERROR_WARNING;
+               priv->can.can_stats.error_warning++;
+               if (skb) {
+                       cf->can_id |= CAN_ERR_CRTL;
+                       cf->data[1] |= (txerr > rxerr) ?
+                                       CAN_ERR_CRTL_TX_WARNING :
+                                       CAN_ERR_CRTL_RX_WARNING;
+                       cf->data[6] = txerr;
+                       cf->data[7] = rxerr;
+               }
+       }
+
+       /* Check for Arbitration lost interrupt */
+       if (isr & XCAN_IXR_ARBLST_MASK) {
+               priv->can.can_stats.arbitration_lost++;
+               if (skb) {
+                       cf->can_id |= CAN_ERR_LOSTARB;
+                       cf->data[0] = CAN_ERR_LOSTARB_UNSPEC;
+               }
+       }
+
+       /* Check for RX FIFO Overflow interrupt */
+       if (isr & XCAN_IXR_RXOFLW_MASK) {
+               stats->rx_over_errors++;
+               stats->rx_errors++;
+               priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
+               if (skb) {
+                       cf->can_id |= CAN_ERR_CRTL;
+                       cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+               }
+       }
+
+       /* Check for error interrupt */
+       if (isr & XCAN_IXR_ERROR_MASK) {
+               if (skb) {
+                       cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+                       cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+               }
+
+               /* Check for Ack error interrupt */
+               if (err_status & XCAN_ESR_ACKER_MASK) {
+                       stats->tx_errors++;
+                       if (skb) {
+                               cf->can_id |= CAN_ERR_ACK;
+                               cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
+                       }
+               }
+
+               /* Check for Bit error interrupt */
+               if (err_status & XCAN_ESR_BERR_MASK) {
+                       stats->tx_errors++;
+                       if (skb) {
+                               cf->can_id |= CAN_ERR_PROT;
+                               cf->data[2] = CAN_ERR_PROT_BIT;
+                       }
+               }
+
+               /* Check for Stuff error interrupt */
+               if (err_status & XCAN_ESR_STER_MASK) {
+                       stats->rx_errors++;
+                       if (skb) {
+                               cf->can_id |= CAN_ERR_PROT;
+                               cf->data[2] = CAN_ERR_PROT_STUFF;
+                       }
+               }
+
+               /* Check for Form error interrupt */
+               if (err_status & XCAN_ESR_FMER_MASK) {
+                       stats->rx_errors++;
+                       if (skb) {
+                               cf->can_id |= CAN_ERR_PROT;
+                               cf->data[2] = CAN_ERR_PROT_FORM;
+                       }
+               }
+
+               /* Check for CRC error interrupt */
+               if (err_status & XCAN_ESR_CRCER_MASK) {
+                       stats->rx_errors++;
+                       if (skb) {
+                               cf->can_id |= CAN_ERR_PROT;
+                               cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ |
+                                               CAN_ERR_PROT_LOC_CRC_DEL;
+                       }
+               }
+                       priv->can.can_stats.bus_error++;
+       }
+
+       if (skb) {
+               stats->rx_packets++;
+               stats->rx_bytes += cf->can_dlc;
+               netif_rx(skb);
+       }
+
+       netdev_dbg(ndev, "%s: error status register:0x%x\n",
+                       __func__, priv->read_reg(priv, XCAN_ESR_OFFSET));
+}
+
+/**
+ * xcan_state_interrupt - It will check the state of the CAN device
+ * @ndev:      net_device pointer
+ * @isr:       interrupt status register value
+ *
+ * This will checks the state of the CAN device
+ * and puts the device into appropriate state.
+ */
+static void xcan_state_interrupt(struct net_device *ndev, u32 isr)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+
+       /* Check for Sleep interrupt if set put CAN device in sleep state */
+       if (isr & XCAN_IXR_SLP_MASK)
+               priv->can.state = CAN_STATE_SLEEPING;
+
+       /* Check for Wake up interrupt if set put CAN device in Active state */
+       if (isr & XCAN_IXR_WKUP_MASK)
+               priv->can.state = CAN_STATE_ERROR_ACTIVE;
+}
+
+/**
+ * xcan_rx_poll - Poll routine for rx packets (NAPI)
+ * @napi:      napi structure pointer
+ * @quota:     Max number of rx packets to be processed.
+ *
+ * This is the poll routine for rx part.
+ * It will process the packets maximux quota value.
+ *
+ * Return: number of packets received
+ */
+static int xcan_rx_poll(struct napi_struct *napi, int quota)
+{
+       struct net_device *ndev = napi->dev;
+       struct xcan_priv *priv = netdev_priv(ndev);
+       u32 isr, ier;
+       int work_done = 0;
+
+       isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
+       while ((isr & XCAN_IXR_RXNEMP_MASK) && (work_done < quota)) {
+               if (isr & XCAN_IXR_RXOK_MASK) {
+                       priv->write_reg(priv, XCAN_ICR_OFFSET,
+                               XCAN_IXR_RXOK_MASK);
+                       work_done += xcan_rx(ndev);
+               } else {
+                       priv->write_reg(priv, XCAN_ICR_OFFSET,
+                               XCAN_IXR_RXNEMP_MASK);
+                       break;
+               }
+               priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_RXNEMP_MASK);
+               isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
+       }
+
+       if (work_done)
+               can_led_event(ndev, CAN_LED_EVENT_RX);
+
+       if (work_done < quota) {
+               napi_complete(napi);
+               ier = priv->read_reg(priv, XCAN_IER_OFFSET);
+               ier |= (XCAN_IXR_RXOK_MASK | XCAN_IXR_RXNEMP_MASK);
+               priv->write_reg(priv, XCAN_IER_OFFSET, ier);
+       }
+       return work_done;
+}
+
+/**
+ * xcan_tx_interrupt - Tx Done Isr
+ * @ndev:      net_device pointer
+ * @isr:       Interrupt status register value
+ */
+static void xcan_tx_interrupt(struct net_device *ndev, u32 isr)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       struct net_device_stats *stats = &ndev->stats;
+
+       while ((priv->tx_head - priv->tx_tail > 0) &&
+                       (isr & XCAN_IXR_TXOK_MASK)) {
+               priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_TXOK_MASK);
+               can_get_echo_skb(ndev, priv->tx_tail %
+                                       priv->tx_max);
+               priv->tx_tail++;
+               stats->tx_packets++;
+               isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
+       }
+       can_led_event(ndev, CAN_LED_EVENT_TX);
+       netif_wake_queue(ndev);
+}
+
+/**
+ * xcan_interrupt - CAN Isr
+ * @irq:       irq number
+ * @dev_id:    device id poniter
+ *
+ * This is the xilinx CAN Isr. It checks for the type of interrupt
+ * and invokes the corresponding ISR.
+ *
+ * Return:
+ * IRQ_NONE - If CAN device is in sleep mode, IRQ_HANDLED otherwise
+ */
+static irqreturn_t xcan_interrupt(int irq, void *dev_id)
+{
+       struct net_device *ndev = (struct net_device *)dev_id;
+       struct xcan_priv *priv = netdev_priv(ndev);
+       u32 isr, ier;
+
+       /* Get the interrupt status from Xilinx CAN */
+       isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
+       if (!isr)
+               return IRQ_NONE;
+
+       /* Check for the type of interrupt and Processing it */
+       if (isr & (XCAN_IXR_SLP_MASK | XCAN_IXR_WKUP_MASK)) {
+               priv->write_reg(priv, XCAN_ICR_OFFSET, (XCAN_IXR_SLP_MASK |
+                               XCAN_IXR_WKUP_MASK));
+               xcan_state_interrupt(ndev, isr);
+       }
+
+       /* Check for Tx interrupt and Processing it */
+       if (isr & XCAN_IXR_TXOK_MASK)
+               xcan_tx_interrupt(ndev, isr);
+
+       /* Check for the type of error interrupt and Processing it */
+       if (isr & (XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK |
+                       XCAN_IXR_BSOFF_MASK | XCAN_IXR_ARBLST_MASK)) {
+               priv->write_reg(priv, XCAN_ICR_OFFSET, (XCAN_IXR_ERROR_MASK |
+                               XCAN_IXR_RXOFLW_MASK | XCAN_IXR_BSOFF_MASK |
+                               XCAN_IXR_ARBLST_MASK));
+               xcan_err_interrupt(ndev, isr);
+       }
+
+       /* Check for the type of receive interrupt and Processing it */
+       if (isr & (XCAN_IXR_RXNEMP_MASK | XCAN_IXR_RXOK_MASK)) {
+               ier = priv->read_reg(priv, XCAN_IER_OFFSET);
+               ier &= ~(XCAN_IXR_RXNEMP_MASK | XCAN_IXR_RXOK_MASK);
+               priv->write_reg(priv, XCAN_IER_OFFSET, ier);
+               napi_schedule(&priv->napi);
+       }
+       return IRQ_HANDLED;
+}
+
+/**
+ * xcan_chip_stop - Driver stop routine
+ * @ndev:      Pointer to net_device structure
+ *
+ * This is the drivers stop routine. It will disable the
+ * interrupts and put the device into configuration mode.
+ */
+static void xcan_chip_stop(struct net_device *ndev)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       u32 ier;
+
+       /* Disable interrupts and leave the can in configuration mode */
+       ier = priv->read_reg(priv, XCAN_IER_OFFSET);
+       ier &= ~XCAN_INTR_ALL;
+       priv->write_reg(priv, XCAN_IER_OFFSET, ier);
+       priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
+       priv->can.state = CAN_STATE_STOPPED;
+}
+
+/**
+ * xcan_open - Driver open routine
+ * @ndev:      Pointer to net_device structure
+ *
+ * This is the driver open routine.
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_open(struct net_device *ndev)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       int ret;
+
+       ret = request_irq(ndev->irq, xcan_interrupt, priv->irq_flags,
+                       ndev->name, ndev);
+       if (ret < 0) {
+               netdev_err(ndev, "irq allocation for CAN failed\n");
+               goto err;
+       }
+
+       ret = clk_prepare_enable(priv->can_clk);
+       if (ret) {
+               netdev_err(ndev, "unable to enable device clock\n");
+               goto err_irq;
+       }
+
+       ret = clk_prepare_enable(priv->bus_clk);
+       if (ret) {
+               netdev_err(ndev, "unable to enable bus clock\n");
+               goto err_can_clk;
+       }
+
+       /* Set chip into reset mode */
+       ret = set_reset_mode(ndev);
+       if (ret < 0) {
+               netdev_err(ndev, "mode resetting failed!\n");
+               goto err_bus_clk;
+       }
+
+       /* Common open */
+       ret = open_candev(ndev);
+       if (ret)
+               goto err_bus_clk;
+
+       ret = xcan_chip_start(ndev);
+       if (ret < 0) {
+               netdev_err(ndev, "xcan_chip_start failed!\n");
+               goto err_candev;
+       }
+
+       can_led_event(ndev, CAN_LED_EVENT_OPEN);
+       napi_enable(&priv->napi);
+       netif_start_queue(ndev);
+
+       return 0;
+
+err_candev:
+       close_candev(ndev);
+err_bus_clk:
+       clk_disable_unprepare(priv->bus_clk);
+err_can_clk:
+       clk_disable_unprepare(priv->can_clk);
+err_irq:
+       free_irq(ndev->irq, ndev);
+err:
+       return ret;
+}
+
+/**
+ * xcan_close - Driver close routine
+ * @ndev:      Pointer to net_device structure
+ *
+ * Return: 0 always
+ */
+static int xcan_close(struct net_device *ndev)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+
+       netif_stop_queue(ndev);
+       napi_disable(&priv->napi);
+       xcan_chip_stop(ndev);
+       clk_disable_unprepare(priv->bus_clk);
+       clk_disable_unprepare(priv->can_clk);
+       free_irq(ndev->irq, ndev);
+       close_candev(ndev);
+
+       can_led_event(ndev, CAN_LED_EVENT_STOP);
+
+       return 0;
+}
+
+/**
+ * xcan_get_berr_counter - error counter routine
+ * @ndev:      Pointer to net_device structure
+ * @bec:       Pointer to can_berr_counter structure
+ *
+ * This is the driver error counter routine.
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_get_berr_counter(const struct net_device *ndev,
+                                       struct can_berr_counter *bec)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       int ret;
+
+       ret = clk_prepare_enable(priv->can_clk);
+       if (ret)
+               goto err;
+
+       ret = clk_prepare_enable(priv->bus_clk);
+       if (ret)
+               goto err_clk;
+
+       bec->txerr = priv->read_reg(priv, XCAN_ECR_OFFSET) & XCAN_ECR_TEC_MASK;
+       bec->rxerr = ((priv->read_reg(priv, XCAN_ECR_OFFSET) &
+                       XCAN_ECR_REC_MASK) >> XCAN_ESR_REC_SHIFT);
+
+       clk_disable_unprepare(priv->bus_clk);
+       clk_disable_unprepare(priv->can_clk);
+
+       return 0;
+
+err_clk:
+       clk_disable_unprepare(priv->can_clk);
+err:
+       return ret;
+}
+
+
+static const struct net_device_ops xcan_netdev_ops = {
+       .ndo_open       = xcan_open,
+       .ndo_stop       = xcan_close,
+       .ndo_start_xmit = xcan_start_xmit,
+};
+
+/**
+ * xcan_suspend - Suspend method for the driver
+ * @dev:       Address of the platform_device structure
+ *
+ * Put the driver into low power mode.
+ * Return: 0 always
+ */
+static int __maybe_unused xcan_suspend(struct device *dev)
+{
+       struct platform_device *pdev = dev_get_drvdata(dev);
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct xcan_priv *priv = netdev_priv(ndev);
+
+       if (netif_running(ndev)) {
+               netif_stop_queue(ndev);
+               netif_device_detach(ndev);
+       }
+
+       priv->write_reg(priv, XCAN_MSR_OFFSET, XCAN_MSR_SLEEP_MASK);
+       priv->can.state = CAN_STATE_SLEEPING;
+
+       clk_disable(priv->bus_clk);
+       clk_disable(priv->can_clk);
+
+       return 0;
+}
+
+/**
+ * xcan_resume - Resume from suspend
+ * @dev:       Address of the platformdevice structure
+ *
+ * Resume operation after suspend.
+ * Return: 0 on success and failure value on error
+ */
+static int __maybe_unused xcan_resume(struct device *dev)
+{
+       struct platform_device *pdev = dev_get_drvdata(dev);
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct xcan_priv *priv = netdev_priv(ndev);
+       int ret;
+
+       ret = clk_enable(priv->bus_clk);
+       if (ret) {
+               dev_err(dev, "Cannot enable clock.\n");
+               return ret;
+       }
+       ret = clk_enable(priv->can_clk);
+       if (ret) {
+               dev_err(dev, "Cannot enable clock.\n");
+               clk_disable_unprepare(priv->bus_clk);
+               return ret;
+       }
+
+       priv->write_reg(priv, XCAN_MSR_OFFSET, 0);
+       priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK);
+       priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+       if (netif_running(ndev)) {
+               netif_device_attach(ndev);
+               netif_start_queue(ndev);
+       }
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(xcan_dev_pm_ops, xcan_suspend, xcan_resume);
+
+/**
+ * xcan_probe - Platform registration call
+ * @pdev:      Handle to the platform device structure
+ *
+ * This function does all the memory allocation and registration for the CAN
+ * device.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_probe(struct platform_device *pdev)
+{
+       struct resource *res; /* IO mem resources */
+       struct net_device *ndev;
+       struct xcan_priv *priv;
+       void __iomem *addr;
+       int ret, rx_max, tx_max;
+
+       /* Get the virtual base address for the device */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       addr = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(addr)) {
+               ret = PTR_ERR(addr);
+               goto err;
+       }
+
+       ret = of_property_read_u32(pdev->dev.of_node, "tx-fifo-depth", &tx_max);
+       if (ret < 0)
+               goto err;
+
+       ret = of_property_read_u32(pdev->dev.of_node, "rx-fifo-depth", &rx_max);
+       if (ret < 0)
+               goto err;
+
+       /* Create a CAN device instance */
+       ndev = alloc_candev(sizeof(struct xcan_priv), tx_max);
+       if (!ndev)
+               return -ENOMEM;
+
+       priv = netdev_priv(ndev);
+       priv->dev = ndev;
+       priv->can.bittiming_const = &xcan_bittiming_const;
+       priv->can.do_set_mode = xcan_do_set_mode;
+       priv->can.do_get_berr_counter = xcan_get_berr_counter;
+       priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
+                                       CAN_CTRLMODE_BERR_REPORTING;
+       priv->reg_base = addr;
+       priv->tx_max = tx_max;
+
+       /* Get IRQ for the device */
+       ndev->irq = platform_get_irq(pdev, 0);
+       ndev->flags |= IFF_ECHO;        /* We support local echo */
+
+       platform_set_drvdata(pdev, ndev);
+       SET_NETDEV_DEV(ndev, &pdev->dev);
+       ndev->netdev_ops = &xcan_netdev_ops;
+
+       /* Getting the CAN can_clk info */
+       priv->can_clk = devm_clk_get(&pdev->dev, "can_clk");
+       if (IS_ERR(priv->can_clk)) {
+               dev_err(&pdev->dev, "Device clock not found.\n");
+               ret = PTR_ERR(priv->can_clk);
+               goto err_free;
+       }
+       /* Check for type of CAN device */
+       if (of_device_is_compatible(pdev->dev.of_node,
+                                   "xlnx,zynq-can-1.0")) {
+               priv->bus_clk = devm_clk_get(&pdev->dev, "pclk");
+               if (IS_ERR(priv->bus_clk)) {
+                       dev_err(&pdev->dev, "bus clock not found\n");
+                       ret = PTR_ERR(priv->bus_clk);
+                       goto err_free;
+               }
+       } else {
+               priv->bus_clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
+               if (IS_ERR(priv->bus_clk)) {
+                       dev_err(&pdev->dev, "bus clock not found\n");
+                       ret = PTR_ERR(priv->bus_clk);
+                       goto err_free;
+               }
+       }
+
+       ret = clk_prepare_enable(priv->can_clk);
+       if (ret) {
+               dev_err(&pdev->dev, "unable to enable device clock\n");
+               goto err_free;
+       }
+
+       ret = clk_prepare_enable(priv->bus_clk);
+       if (ret) {
+               dev_err(&pdev->dev, "unable to enable bus clock\n");
+               goto err_unprepare_disable_dev;
+       }
+
+       priv->write_reg = xcan_write_reg_le;
+       priv->read_reg = xcan_read_reg_le;
+
+       if (priv->read_reg(priv, XCAN_SR_OFFSET) != XCAN_SR_CONFIG_MASK) {
+               priv->write_reg = xcan_write_reg_be;
+               priv->read_reg = xcan_read_reg_be;
+       }
+
+       priv->can.clock.freq = clk_get_rate(priv->can_clk);
+
+       netif_napi_add(ndev, &priv->napi, xcan_rx_poll, rx_max);
+
+       ret = register_candev(ndev);
+       if (ret) {
+               dev_err(&pdev->dev, "fail to register failed (err=%d)\n", ret);
+               goto err_unprepare_disable_busclk;
+       }
+
+       devm_can_led_init(ndev);
+       clk_disable_unprepare(priv->bus_clk);
+       clk_disable_unprepare(priv->can_clk);
+       netdev_dbg(ndev, "reg_base=0x%p irq=%d clock=%d, tx fifo depth:%d\n",
+                       priv->reg_base, ndev->irq, priv->can.clock.freq,
+                       priv->tx_max);
+
+       return 0;
+
+err_unprepare_disable_busclk:
+       clk_disable_unprepare(priv->bus_clk);
+err_unprepare_disable_dev:
+       clk_disable_unprepare(priv->can_clk);
+err_free:
+       free_candev(ndev);
+err:
+       return ret;
+}
+
+/**
+ * xcan_remove - Unregister the device after releasing the resources
+ * @pdev:      Handle to the platform device structure
+ *
+ * This function frees all the resources allocated to the device.
+ * Return: 0 always
+ */
+static int xcan_remove(struct platform_device *pdev)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct xcan_priv *priv = netdev_priv(ndev);
+
+       if (set_reset_mode(ndev) < 0)
+               netdev_err(ndev, "mode resetting failed!\n");
+
+       unregister_candev(ndev);
+       netif_napi_del(&priv->napi);
+       free_candev(ndev);
+
+       return 0;
+}
+
+/* Match table for OF platform binding */
+static struct of_device_id xcan_of_match[] = {
+       { .compatible = "xlnx,zynq-can-1.0", },
+       { .compatible = "xlnx,axi-can-1.00.a", },
+       { /* end of list */ },
+};
+MODULE_DEVICE_TABLE(of, xcan_of_match);
+
+static struct platform_driver xcan_driver = {
+       .probe = xcan_probe,
+       .remove = xcan_remove,
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = DRIVER_NAME,
+               .pm = &xcan_dev_pm_ops,
+               .of_match_table = xcan_of_match,
+       },
+};
+
+module_platform_driver(xcan_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Xilinx Inc");
+MODULE_DESCRIPTION("Xilinx CAN interface");
index 41ee5b6ae91751239d81e4613f336aff02fbff84..69c42513dd724bb06ef40eaf4b5e4bbea800936d 100644 (file)
@@ -289,7 +289,7 @@ static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p)
 
 static int mv88e6123_61_65_setup(struct dsa_switch *ds)
 {
-       struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        int i;
        int ret;
 
index dadfafba64e9ac0aad3de55d84e5bdea3ceb00a0..953bc6a49e594471342270e7281a8e263f0086de 100644 (file)
@@ -155,7 +155,7 @@ static int mv88e6131_setup_global(struct dsa_switch *ds)
 
 static int mv88e6131_setup_port(struct dsa_switch *ds, int p)
 {
-       struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        int addr = REG_PORT(p);
        u16 val;
 
@@ -274,7 +274,7 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p)
 
 static int mv88e6131_setup(struct dsa_switch *ds)
 {
-       struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        int i;
        int ret;
 
index 17314ed9456d32c961aea31db10a650a2ffb1392..9ce2146346b6cda7175229d0f493f004730ab1b1 100644 (file)
@@ -74,7 +74,7 @@ int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg)
 
 int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg)
 {
-       struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        int ret;
 
        mutex_lock(&ps->smi_mutex);
@@ -118,7 +118,7 @@ int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
 
 int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val)
 {
-       struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        int ret;
 
        mutex_lock(&ps->smi_mutex);
@@ -256,7 +256,7 @@ static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
 
 static int mv88e6xxx_ppu_access_get(struct dsa_switch *ds)
 {
-       struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        int ret;
 
        mutex_lock(&ps->ppu_mutex);
@@ -283,7 +283,7 @@ static int mv88e6xxx_ppu_access_get(struct dsa_switch *ds)
 
 static void mv88e6xxx_ppu_access_put(struct dsa_switch *ds)
 {
-       struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
 
        /* Schedule a timer to re-enable the PHY polling unit. */
        mod_timer(&ps->ppu_timer, jiffies + msecs_to_jiffies(10));
@@ -292,7 +292,7 @@ static void mv88e6xxx_ppu_access_put(struct dsa_switch *ds)
 
 void mv88e6xxx_ppu_state_init(struct dsa_switch *ds)
 {
-       struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
 
        mutex_init(&ps->ppu_mutex);
        INIT_WORK(&ps->ppu_work, mv88e6xxx_ppu_reenable_work);
@@ -463,7 +463,7 @@ void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
                                 int nr_stats, struct mv88e6xxx_hw_stat *stats,
                                 int port, uint64_t *data)
 {
-       struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        int ret;
        int i;
 
index 35df0b9e6848b0f1bd964f7b93ed2f18988c6b08..a968654b631d28a860dcd83b8c9b728189c196b2 100644 (file)
@@ -534,7 +534,7 @@ static int el3_common_init(struct net_device *dev)
        /* The EL3-specific entries in the device structure. */
        dev->netdev_ops = &netdev_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
-       SET_ETHTOOL_OPS(dev, &ethtool_ops);
+       dev->ethtool_ops = &ethtool_ops;
 
        err = register_netdev(dev);
        if (err) {
index 063557e037f21b8b43fa3d1dfe64a19c870d4789..f18647c2355990ba0e55b6a2690c0cdbb819da43 100644 (file)
@@ -218,7 +218,7 @@ static int tc589_probe(struct pcmcia_device *link)
        dev->netdev_ops = &el3_netdev_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
 
-       SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+       dev->ethtool_ops = &netdev_ethtool_ops;
 
        return tc589_config(link);
 }
index 465cc7108d8a5e5bbb2c34df44f7bd0329d02d6b..e13b04624dedcecb7285943787946f2dfadd610c 100644 (file)
@@ -2435,7 +2435,7 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        netif_napi_add(dev, &tp->napi, typhoon_poll, 16);
        dev->watchdog_timeo     = TX_TIMEOUT;
 
-       SET_ETHTOOL_OPS(dev, &typhoon_ethtool_ops);
+       dev->ethtool_ops = &typhoon_ethtool_ops;
 
        /* We can handle scatter gather, up to 16 entries, and
         * we can do IP checksumming (only version 4, doh...)
index 455d4c399b52168ce6f8691160991e500216ff54..1d162ccb473341af256279b29d466bc56429472b 100644 (file)
@@ -157,7 +157,7 @@ static void ax_reset_8390(struct net_device *dev)
 
        /* This check _should_not_ be necessary, omit eventually. */
        while ((ei_inb(addr + EN0_ISR) & ENISR_RESET) == 0) {
-               if (jiffies - reset_start_time > 2 * HZ / 100) {
+               if (time_after(jiffies, reset_start_time + 2 * HZ / 100)) {
                        netdev_warn(dev, "%s: did not complete.\n", __func__);
                        break;
                }
@@ -293,7 +293,7 @@ static void ax_block_output(struct net_device *dev, int count,
        dma_start = jiffies;
 
        while ((ei_inb(nic_base + EN0_ISR) & ENISR_RDC) == 0) {
-               if (jiffies - dma_start > 2 * HZ / 100) {               /* 20ms */
+               if (time_after(jiffies, dma_start + 2 * HZ / 100)) { /* 20ms */
                        netdev_warn(dev, "timeout waiting for Tx RDC.\n");
                        ax_reset_8390(dev);
                        ax_NS8390_init(dev, 1);
index 051349458462142081f10ca6b44f0ce02ba95036..edb718661850f350711e73ada6368d2e080fe486 100644 (file)
@@ -68,6 +68,7 @@ source "drivers/net/ethernet/neterion/Kconfig"
 source "drivers/net/ethernet/faraday/Kconfig"
 source "drivers/net/ethernet/freescale/Kconfig"
 source "drivers/net/ethernet/fujitsu/Kconfig"
+source "drivers/net/ethernet/hisilicon/Kconfig"
 source "drivers/net/ethernet/hp/Kconfig"
 source "drivers/net/ethernet/ibm/Kconfig"
 source "drivers/net/ethernet/intel/Kconfig"
index 35190e36c4568e6803279f878a6aa866685ca1be..58de3339ab3c7a443d591a37425bb032c5b93ab5 100644 (file)
@@ -31,6 +31,7 @@ obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
 obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
 obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
 obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
+obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
 obj-$(CONFIG_NET_VENDOR_HP) += hp/
 obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
 obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
index 171d73c1d3c22de3209ca6c48b7978c35b2a38b0..40dbbf740331c49b13ac0d64ef977be6ce361993 100644 (file)
@@ -784,7 +784,7 @@ static int starfire_init_one(struct pci_dev *pdev,
 
        dev->netdev_ops = &netdev_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
-       SET_ETHTOOL_OPS(dev, &ethtool_ops);
+       dev->ethtool_ops = &ethtool_ops;
 
        netif_napi_add(dev, &np->napi, netdev_poll, max_interrupt_work);
 
index 1517e9df5ba16c66f6f63bc10f8a0e1b460a95a3..9a6991be9749f75b4107d979cd31518cda837ec0 100644 (file)
@@ -476,7 +476,7 @@ static int acenic_probe_one(struct pci_dev *pdev,
        dev->watchdog_timeo = 5*HZ;
 
        dev->netdev_ops = &ace_netdev_ops;
-       SET_ETHTOOL_OPS(dev, &ace_ethtool_ops);
+       dev->ethtool_ops = &ace_ethtool_ops;
 
        /* we only display this string ONCE */
        if (!boards_found)
index 99cc56f451cf4886fbf0a5bca044dfa5cf033a2c..580553d42d34fd773139cda6076f00d7bc40cce1 100644 (file)
@@ -353,7 +353,6 @@ static int sgdma_async_read(struct altera_tse_private *priv)
 
        struct sgdma_descrip __iomem *cdesc = &descbase[0];
        struct sgdma_descrip __iomem *ndesc = &descbase[1];
-
        struct tse_buffer *rxbuffer = NULL;
 
        if (!sgdma_rxbusy(priv)) {
index 54c25eff795272661e2caabfd554ffcc7dd24e00..be72e1e6452522ba9b81e58a3268580c992cfa5f 100644 (file)
@@ -271,5 +271,5 @@ static const struct ethtool_ops tse_ethtool_ops = {
 
 void altera_tse_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &tse_ethtool_ops);
+       netdev->ethtool_ops = &tse_ethtool_ops;
 }
index 562df46e0a82e24968b8b89bc35a7b357982e6ec..bbaf36d9f5e1cfbb7649ec13d5eb00595543a5b4 100644 (file)
@@ -7,7 +7,7 @@ config NET_VENDOR_AMD
        default y
        depends on DIO || MACH_DECSTATION || MVME147 || ATARI || SUN3 || \
                   SUN3X || SBUS || PCI || ZORRO || (ISA && ISA_DMA_API) || \
-                  (ARM && ARCH_EBSA110) || ISA || EISA || PCMCIA
+                  (ARM && ARCH_EBSA110) || ISA || EISA || PCMCIA || ARM64
        ---help---
          If you have a network (Ethernet) chipset belonging to this class,
          say Y.
@@ -177,4 +177,16 @@ config SUNLANCE
          To compile this driver as a module, choose M here: the module
          will be called sunlance.
 
+config AMD_XGBE
+       tristate "AMD 10GbE Ethernet driver"
+       depends on OF_NET
+       select PHYLIB
+       select AMD_XGBE_PHY
+       ---help---
+         This driver supports the AMD 10GbE Ethernet device found on an
+         AMD SoC.
+
+         To compile this driver as a module, choose M here: the module
+         will be called amd-xgbe.
+
 endif # NET_VENDOR_AMD
index cdd4301a973dc388a6d7f1d2a7ed5325f87d6e6b..a38a2dce3eb329ecceabe2b0d560160d009e527c 100644 (file)
@@ -17,3 +17,4 @@ obj-$(CONFIG_NI65) += ni65.o
 obj-$(CONFIG_PCNET32) += pcnet32.o
 obj-$(CONFIG_SUN3LANCE) += sun3lance.o
 obj-$(CONFIG_SUNLANCE) += sunlance.o
+obj-$(CONFIG_AMD_XGBE) += xgbe/
index 26efaaa5e73fd292de512fc428e9af84126fcbb9..068dc7cad5fa3c511c34b034c006add6284bb097 100644 (file)
@@ -1900,7 +1900,7 @@ static int amd8111e_probe_one(struct pci_dev *pdev,
 
        /* Initialize driver entry points */
        dev->netdev_ops = &amd8111e_netdev_ops;
-       SET_ETHTOOL_OPS(dev, &ops);
+       dev->ethtool_ops = &ops;
        dev->irq =pdev->irq;
        dev->watchdog_timeo = AMD8111E_TX_TIMEOUT;
        netif_napi_add(dev, &lp->napi, amd8111e_rx_poll, 32);
index b08101b31b8bc547ffbfb486f87abc7a18f92686..968b7bfac8fcaa9af767c57c474a83b3229b09a0 100644 (file)
@@ -718,7 +718,6 @@ static int ariadne_init_one(struct zorro_dev *z,
        unsigned long mem_start = board + ARIADNE_RAM;
        struct resource *r1, *r2;
        struct net_device *dev;
-       struct ariadne_private *priv;
        u32 serial;
        int err;
 
@@ -738,8 +737,6 @@ static int ariadne_init_one(struct zorro_dev *z,
                return -ENOMEM;
        }
 
-       priv = netdev_priv(dev);
-
        r1->name = dev->name;
        r2->name = dev->name;
 
index a2bd91e3d302acce0d727cc3ef1eda27e56040cb..a78e4c13695980e295ead2c8d85fd6f6a351f5c8 100644 (file)
@@ -1229,7 +1229,7 @@ static int au1000_probe(struct platform_device *pdev)
        dev->base_addr = base->start;
        dev->irq = irq;
        dev->netdev_ops = &au1000_netdev_ops;
-       SET_ETHTOOL_OPS(dev, &au1000_ethtool_ops);
+       dev->ethtool_ops = &au1000_ethtool_ops;
        dev->watchdog_timeo = ETH_TX_TIMEOUT;
 
        /*
index 47ce57c2c8939fd458decbb74042d8179b31c558..6c9de117ffc68f2941d20f465161460f806f2d04 100644 (file)
@@ -27,9 +27,9 @@
 
 #include "hplance.h"
 
-/* We have 16834 bytes of RAM for the init block and buffers. This places
+/* We have 16392 bytes of RAM for the init block and buffers. This places
  * an upper limit on the number of buffers we can use. NetBSD uses 8 Rx
- * buffers and 2 Tx buffers.
+ * buffers and 2 Tx buffers, it takes (8 + 2) * 1544 bytes.
  */
 #define LANCE_LOG_TX_BUFFERS 1
 #define LANCE_LOG_RX_BUFFERS 3
index 0e8399dec0543736d8dc3ef732cb18817eca7892..0660ac5846bbef3e07d99c25a3c2d63f6eff9e12 100644 (file)
@@ -26,9 +26,9 @@
 #include <asm/pgtable.h>
 #include <asm/mvme147hw.h>
 
-/* We have 16834 bytes of RAM for the init block and buffers. This places
+/* We have 32K of RAM for the init block and buffers. This places
  * an upper limit on the number of buffers we can use. NetBSD uses 8 Rx
- * buffers and 2 Tx buffers.
+ * buffers and 2 Tx buffers, it takes (8 + 2) * 1544 bytes.
  */
 #define LANCE_LOG_TX_BUFFERS 1
 #define LANCE_LOG_RX_BUFFERS 3
@@ -111,7 +111,7 @@ struct net_device * __init mvme147lance_probe(int unit)
               dev->dev_addr);
 
        lp = netdev_priv(dev);
-       lp->ram = __get_dma_pages(GFP_ATOMIC, 3);       /* 16K */
+       lp->ram = __get_dma_pages(GFP_ATOMIC, 3);       /* 32K */
        if (!lp->ram) {
                printk("%s: No memory for LANCE buffers\n", dev->name);
                free_netdev(dev);
index 08569fe2b182c2bef930ef582320b9ccb9cfc952..abf3b1581c82a2eacbf7074b3c9db9ca12fbef19 100644 (file)
@@ -457,7 +457,7 @@ static int nmclan_probe(struct pcmcia_device *link)
     lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
 
     dev->netdev_ops = &mace_netdev_ops;
-    SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+    dev->ethtool_ops = &netdev_ethtool_ops;
     dev->watchdog_timeo = TX_TIMEOUT;
 
     return nmclan_config(link);
diff --git a/drivers/net/ethernet/amd/xgbe/Makefile b/drivers/net/ethernet/amd/xgbe/Makefile
new file mode 100644 (file)
index 0000000..26cf9af
--- /dev/null
@@ -0,0 +1,6 @@
+obj-$(CONFIG_AMD_XGBE) += amd-xgbe.o
+
+amd-xgbe-objs := xgbe-main.o xgbe-drv.o xgbe-dev.o \
+                xgbe-desc.o xgbe-ethtool.o xgbe-mdio.o
+
+amd-xgbe-$(CONFIG_DEBUG_FS) += xgbe-debugfs.o
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h
new file mode 100644 (file)
index 0000000..bf462ee
--- /dev/null
@@ -0,0 +1,1007 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute 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 file 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/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and 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.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Advanced Micro Devices, Inc. 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 <COPYRIGHT HOLDER> 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.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and 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.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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 __XGBE_COMMON_H__
+#define __XGBE_COMMON_H__
+
+/* DMA register offsets */
+#define DMA_MR                         0x3000
+#define DMA_SBMR                       0x3004
+#define DMA_ISR                                0x3008
+#define DMA_AXIARCR                    0x3010
+#define DMA_AXIAWCR                    0x3018
+#define DMA_DSR0                       0x3020
+#define DMA_DSR1                       0x3024
+#define DMA_DSR2                       0x3028
+#define DMA_DSR3                       0x302c
+#define DMA_DSR4                       0x3030
+
+/* DMA register entry bit positions and sizes */
+#define DMA_AXIARCR_DRC_INDEX          0
+#define DMA_AXIARCR_DRC_WIDTH          4
+#define DMA_AXIARCR_DRD_INDEX          4
+#define DMA_AXIARCR_DRD_WIDTH          2
+#define DMA_AXIARCR_TEC_INDEX          8
+#define DMA_AXIARCR_TEC_WIDTH          4
+#define DMA_AXIARCR_TED_INDEX          12
+#define DMA_AXIARCR_TED_WIDTH          2
+#define DMA_AXIARCR_THC_INDEX          16
+#define DMA_AXIARCR_THC_WIDTH          4
+#define DMA_AXIARCR_THD_INDEX          20
+#define DMA_AXIARCR_THD_WIDTH          2
+#define DMA_AXIAWCR_DWC_INDEX          0
+#define DMA_AXIAWCR_DWC_WIDTH          4
+#define DMA_AXIAWCR_DWD_INDEX          4
+#define DMA_AXIAWCR_DWD_WIDTH          2
+#define DMA_AXIAWCR_RPC_INDEX          8
+#define DMA_AXIAWCR_RPC_WIDTH          4
+#define DMA_AXIAWCR_RPD_INDEX          12
+#define DMA_AXIAWCR_RPD_WIDTH          2
+#define DMA_AXIAWCR_RHC_INDEX          16
+#define DMA_AXIAWCR_RHC_WIDTH          4
+#define DMA_AXIAWCR_RHD_INDEX          20
+#define DMA_AXIAWCR_RHD_WIDTH          2
+#define DMA_AXIAWCR_TDC_INDEX          24
+#define DMA_AXIAWCR_TDC_WIDTH          4
+#define DMA_AXIAWCR_TDD_INDEX          28
+#define DMA_AXIAWCR_TDD_WIDTH          2
+#define DMA_DSR0_RPS_INDEX             8
+#define DMA_DSR0_RPS_WIDTH             4
+#define DMA_DSR0_TPS_INDEX             12
+#define DMA_DSR0_TPS_WIDTH             4
+#define DMA_ISR_MACIS_INDEX            17
+#define DMA_ISR_MACIS_WIDTH            1
+#define DMA_ISR_MTLIS_INDEX            16
+#define DMA_ISR_MTLIS_WIDTH            1
+#define DMA_MR_SWR_INDEX               0
+#define DMA_MR_SWR_WIDTH               1
+#define DMA_SBMR_EAME_INDEX            11
+#define DMA_SBMR_EAME_WIDTH            1
+#define DMA_SBMR_UNDEF_INDEX           0
+#define DMA_SBMR_UNDEF_WIDTH           1
+
+/* DMA channel register offsets
+ *   Multiple channels can be active.  The first channel has registers
+ *   that begin at 0x3100.  Each subsequent channel has registers that
+ *   are accessed using an offset of 0x80 from the previous channel.
+ */
+#define DMA_CH_BASE                    0x3100
+#define DMA_CH_INC                     0x80
+
+#define DMA_CH_CR                      0x00
+#define DMA_CH_TCR                     0x04
+#define DMA_CH_RCR                     0x08
+#define DMA_CH_TDLR_HI                 0x10
+#define DMA_CH_TDLR_LO                 0x14
+#define DMA_CH_RDLR_HI                 0x18
+#define DMA_CH_RDLR_LO                 0x1c
+#define DMA_CH_TDTR_LO                 0x24
+#define DMA_CH_RDTR_LO                 0x2c
+#define DMA_CH_TDRLR                   0x30
+#define DMA_CH_RDRLR                   0x34
+#define DMA_CH_IER                     0x38
+#define DMA_CH_RIWT                    0x3c
+#define DMA_CH_CATDR_LO                        0x44
+#define DMA_CH_CARDR_LO                        0x4c
+#define DMA_CH_CATBR_HI                        0x50
+#define DMA_CH_CATBR_LO                        0x54
+#define DMA_CH_CARBR_HI                        0x58
+#define DMA_CH_CARBR_LO                        0x5c
+#define DMA_CH_SR                      0x60
+
+/* DMA channel register entry bit positions and sizes */
+#define DMA_CH_CR_PBLX8_INDEX          16
+#define DMA_CH_CR_PBLX8_WIDTH          1
+#define DMA_CH_IER_AIE_INDEX           15
+#define DMA_CH_IER_AIE_WIDTH           1
+#define DMA_CH_IER_FBEE_INDEX          12
+#define DMA_CH_IER_FBEE_WIDTH          1
+#define DMA_CH_IER_NIE_INDEX           16
+#define DMA_CH_IER_NIE_WIDTH           1
+#define DMA_CH_IER_RBUE_INDEX          7
+#define DMA_CH_IER_RBUE_WIDTH          1
+#define DMA_CH_IER_RIE_INDEX           6
+#define DMA_CH_IER_RIE_WIDTH           1
+#define DMA_CH_IER_RSE_INDEX           8
+#define DMA_CH_IER_RSE_WIDTH           1
+#define DMA_CH_IER_TBUE_INDEX          2
+#define DMA_CH_IER_TBUE_WIDTH          1
+#define DMA_CH_IER_TIE_INDEX           0
+#define DMA_CH_IER_TIE_WIDTH           1
+#define DMA_CH_IER_TXSE_INDEX          1
+#define DMA_CH_IER_TXSE_WIDTH          1
+#define DMA_CH_RCR_PBL_INDEX           16
+#define DMA_CH_RCR_PBL_WIDTH           6
+#define DMA_CH_RCR_RBSZ_INDEX          1
+#define DMA_CH_RCR_RBSZ_WIDTH          14
+#define DMA_CH_RCR_SR_INDEX            0
+#define DMA_CH_RCR_SR_WIDTH            1
+#define DMA_CH_RIWT_RWT_INDEX          0
+#define DMA_CH_RIWT_RWT_WIDTH          8
+#define DMA_CH_SR_FBE_INDEX            12
+#define DMA_CH_SR_FBE_WIDTH            1
+#define DMA_CH_SR_RBU_INDEX            7
+#define DMA_CH_SR_RBU_WIDTH            1
+#define DMA_CH_SR_RI_INDEX             6
+#define DMA_CH_SR_RI_WIDTH             1
+#define DMA_CH_SR_RPS_INDEX            8
+#define DMA_CH_SR_RPS_WIDTH            1
+#define DMA_CH_SR_TBU_INDEX            2
+#define DMA_CH_SR_TBU_WIDTH            1
+#define DMA_CH_SR_TI_INDEX             0
+#define DMA_CH_SR_TI_WIDTH             1
+#define DMA_CH_SR_TPS_INDEX            1
+#define DMA_CH_SR_TPS_WIDTH            1
+#define DMA_CH_TCR_OSP_INDEX           4
+#define DMA_CH_TCR_OSP_WIDTH           1
+#define DMA_CH_TCR_PBL_INDEX           16
+#define DMA_CH_TCR_PBL_WIDTH           6
+#define DMA_CH_TCR_ST_INDEX            0
+#define DMA_CH_TCR_ST_WIDTH            1
+#define DMA_CH_TCR_TSE_INDEX           12
+#define DMA_CH_TCR_TSE_WIDTH           1
+
+/* DMA channel register values */
+#define DMA_OSP_DISABLE                        0x00
+#define DMA_OSP_ENABLE                 0x01
+#define DMA_PBL_1                      1
+#define DMA_PBL_2                      2
+#define DMA_PBL_4                      4
+#define DMA_PBL_8                      8
+#define DMA_PBL_16                     16
+#define DMA_PBL_32                     32
+#define DMA_PBL_64                     64      /* 8 x 8 */
+#define DMA_PBL_128                    128     /* 8 x 16 */
+#define DMA_PBL_256                    256     /* 8 x 32 */
+#define DMA_PBL_X8_DISABLE             0x00
+#define DMA_PBL_X8_ENABLE              0x01
+
+
+/* MAC register offsets */
+#define MAC_TCR                                0x0000
+#define MAC_RCR                                0x0004
+#define MAC_PFR                                0x0008
+#define MAC_WTR                                0x000c
+#define MAC_HTR0                       0x0010
+#define MAC_HTR1                       0x0014
+#define MAC_HTR2                       0x0018
+#define MAC_HTR3                       0x001c
+#define MAC_HTR4                       0x0020
+#define MAC_HTR5                       0x0024
+#define MAC_HTR6                       0x0028
+#define MAC_HTR7                       0x002c
+#define MAC_VLANTR                     0x0050
+#define MAC_VLANHTR                    0x0058
+#define MAC_VLANIR                     0x0060
+#define MAC_IVLANIR                    0x0064
+#define MAC_RETMR                      0x006c
+#define MAC_Q0TFCR                     0x0070
+#define MAC_RFCR                       0x0090
+#define MAC_RQC0R                      0x00a0
+#define MAC_RQC1R                      0x00a4
+#define MAC_RQC2R                      0x00a8
+#define MAC_RQC3R                      0x00ac
+#define MAC_ISR                                0x00b0
+#define MAC_IER                                0x00b4
+#define MAC_RTSR                       0x00b8
+#define MAC_PMTCSR                     0x00c0
+#define MAC_RWKPFR                     0x00c4
+#define MAC_LPICSR                     0x00d0
+#define MAC_LPITCR                     0x00d4
+#define MAC_VR                         0x0110
+#define MAC_DR                         0x0114
+#define MAC_HWF0R                      0x011c
+#define MAC_HWF1R                      0x0120
+#define MAC_HWF2R                      0x0124
+#define MAC_GPIOCR                     0x0278
+#define MAC_GPIOSR                     0x027c
+#define MAC_MACA0HR                    0x0300
+#define MAC_MACA0LR                    0x0304
+#define MAC_MACA1HR                    0x0308
+#define MAC_MACA1LR                    0x030c
+
+#define MAC_QTFCR_INC                  4
+#define MAC_MACA_INC                   4
+
+/* MAC register entry bit positions and sizes */
+#define MAC_HWF0R_ADDMACADRSEL_INDEX   18
+#define MAC_HWF0R_ADDMACADRSEL_WIDTH   5
+#define MAC_HWF0R_ARPOFFSEL_INDEX      9
+#define MAC_HWF0R_ARPOFFSEL_WIDTH      1
+#define MAC_HWF0R_EEESEL_INDEX         13
+#define MAC_HWF0R_EEESEL_WIDTH         1
+#define MAC_HWF0R_GMIISEL_INDEX                1
+#define MAC_HWF0R_GMIISEL_WIDTH                1
+#define MAC_HWF0R_MGKSEL_INDEX         7
+#define MAC_HWF0R_MGKSEL_WIDTH         1
+#define MAC_HWF0R_MMCSEL_INDEX         8
+#define MAC_HWF0R_MMCSEL_WIDTH         1
+#define MAC_HWF0R_RWKSEL_INDEX         6
+#define MAC_HWF0R_RWKSEL_WIDTH         1
+#define MAC_HWF0R_RXCOESEL_INDEX       16
+#define MAC_HWF0R_RXCOESEL_WIDTH       1
+#define MAC_HWF0R_SAVLANINS_INDEX      27
+#define MAC_HWF0R_SAVLANINS_WIDTH      1
+#define MAC_HWF0R_SMASEL_INDEX         5
+#define MAC_HWF0R_SMASEL_WIDTH         1
+#define MAC_HWF0R_TSSEL_INDEX          12
+#define MAC_HWF0R_TSSEL_WIDTH          1
+#define MAC_HWF0R_TSSTSSEL_INDEX       25
+#define MAC_HWF0R_TSSTSSEL_WIDTH       2
+#define MAC_HWF0R_TXCOESEL_INDEX       14
+#define MAC_HWF0R_TXCOESEL_WIDTH       1
+#define MAC_HWF0R_VLHASH_INDEX         4
+#define MAC_HWF0R_VLHASH_WIDTH         1
+#define MAC_HWF1R_ADVTHWORD_INDEX      13
+#define MAC_HWF1R_ADVTHWORD_WIDTH      1
+#define MAC_HWF1R_DBGMEMA_INDEX                19
+#define MAC_HWF1R_DBGMEMA_WIDTH                1
+#define MAC_HWF1R_DCBEN_INDEX          16
+#define MAC_HWF1R_DCBEN_WIDTH          1
+#define MAC_HWF1R_HASHTBLSZ_INDEX      24
+#define MAC_HWF1R_HASHTBLSZ_WIDTH      3
+#define MAC_HWF1R_L3L4FNUM_INDEX       27
+#define MAC_HWF1R_L3L4FNUM_WIDTH       4
+#define MAC_HWF1R_RSSEN_INDEX          20
+#define MAC_HWF1R_RSSEN_WIDTH          1
+#define MAC_HWF1R_RXFIFOSIZE_INDEX     0
+#define MAC_HWF1R_RXFIFOSIZE_WIDTH     5
+#define MAC_HWF1R_SPHEN_INDEX          17
+#define MAC_HWF1R_SPHEN_WIDTH          1
+#define MAC_HWF1R_TSOEN_INDEX          18
+#define MAC_HWF1R_TSOEN_WIDTH          1
+#define MAC_HWF1R_TXFIFOSIZE_INDEX     6
+#define MAC_HWF1R_TXFIFOSIZE_WIDTH     5
+#define MAC_HWF2R_AUXSNAPNUM_INDEX     28
+#define MAC_HWF2R_AUXSNAPNUM_WIDTH     3
+#define MAC_HWF2R_PPSOUTNUM_INDEX      24
+#define MAC_HWF2R_PPSOUTNUM_WIDTH      3
+#define MAC_HWF2R_RXCHCNT_INDEX                12
+#define MAC_HWF2R_RXCHCNT_WIDTH                4
+#define MAC_HWF2R_RXQCNT_INDEX         0
+#define MAC_HWF2R_RXQCNT_WIDTH         4
+#define MAC_HWF2R_TXCHCNT_INDEX                18
+#define MAC_HWF2R_TXCHCNT_WIDTH                4
+#define MAC_HWF2R_TXQCNT_INDEX         6
+#define MAC_HWF2R_TXQCNT_WIDTH         4
+#define MAC_ISR_MMCRXIS_INDEX          9
+#define MAC_ISR_MMCRXIS_WIDTH          1
+#define MAC_ISR_MMCTXIS_INDEX          10
+#define MAC_ISR_MMCTXIS_WIDTH          1
+#define MAC_ISR_PMTIS_INDEX            4
+#define MAC_ISR_PMTIS_WIDTH            1
+#define MAC_MACA1HR_AE_INDEX           31
+#define MAC_MACA1HR_AE_WIDTH           1
+#define MAC_PFR_HMC_INDEX              2
+#define MAC_PFR_HMC_WIDTH              1
+#define MAC_PFR_HUC_INDEX              1
+#define MAC_PFR_HUC_WIDTH              1
+#define MAC_PFR_PM_INDEX               4
+#define MAC_PFR_PM_WIDTH               1
+#define MAC_PFR_PR_INDEX               0
+#define MAC_PFR_PR_WIDTH               1
+#define MAC_PMTCSR_MGKPKTEN_INDEX      1
+#define MAC_PMTCSR_MGKPKTEN_WIDTH      1
+#define MAC_PMTCSR_PWRDWN_INDEX                0
+#define MAC_PMTCSR_PWRDWN_WIDTH                1
+#define MAC_PMTCSR_RWKFILTRST_INDEX    31
+#define MAC_PMTCSR_RWKFILTRST_WIDTH    1
+#define MAC_PMTCSR_RWKPKTEN_INDEX      2
+#define MAC_PMTCSR_RWKPKTEN_WIDTH      1
+#define MAC_Q0TFCR_PT_INDEX            16
+#define MAC_Q0TFCR_PT_WIDTH            16
+#define MAC_Q0TFCR_TFE_INDEX           1
+#define MAC_Q0TFCR_TFE_WIDTH           1
+#define MAC_RCR_ACS_INDEX              1
+#define MAC_RCR_ACS_WIDTH              1
+#define MAC_RCR_CST_INDEX              2
+#define MAC_RCR_CST_WIDTH              1
+#define MAC_RCR_DCRCC_INDEX            3
+#define MAC_RCR_DCRCC_WIDTH            1
+#define MAC_RCR_IPC_INDEX              9
+#define MAC_RCR_IPC_WIDTH              1
+#define MAC_RCR_JE_INDEX               8
+#define MAC_RCR_JE_WIDTH               1
+#define MAC_RCR_LM_INDEX               10
+#define MAC_RCR_LM_WIDTH               1
+#define MAC_RCR_RE_INDEX               0
+#define MAC_RCR_RE_WIDTH               1
+#define MAC_RFCR_RFE_INDEX             0
+#define MAC_RFCR_RFE_WIDTH             1
+#define MAC_RQC0R_RXQ0EN_INDEX         0
+#define MAC_RQC0R_RXQ0EN_WIDTH         2
+#define MAC_TCR_SS_INDEX               29
+#define MAC_TCR_SS_WIDTH               2
+#define MAC_TCR_TE_INDEX               0
+#define MAC_TCR_TE_WIDTH               1
+#define MAC_VLANTR_DOVLTC_INDEX                20
+#define MAC_VLANTR_DOVLTC_WIDTH                1
+#define MAC_VLANTR_ERSVLM_INDEX                19
+#define MAC_VLANTR_ERSVLM_WIDTH                1
+#define MAC_VLANTR_ESVL_INDEX          18
+#define MAC_VLANTR_ESVL_WIDTH          1
+#define MAC_VLANTR_EVLS_INDEX          21
+#define MAC_VLANTR_EVLS_WIDTH          2
+#define MAC_VLANTR_EVLRXS_INDEX                24
+#define MAC_VLANTR_EVLRXS_WIDTH                1
+#define MAC_VR_DEVID_INDEX             8
+#define MAC_VR_DEVID_WIDTH             8
+#define MAC_VR_SNPSVER_INDEX           0
+#define MAC_VR_SNPSVER_WIDTH           8
+#define MAC_VR_USERVER_INDEX           16
+#define MAC_VR_USERVER_WIDTH           8
+
+/* MMC register offsets */
+#define MMC_CR                         0x0800
+#define MMC_RISR                       0x0804
+#define MMC_TISR                       0x0808
+#define MMC_RIER                       0x080c
+#define MMC_TIER                       0x0810
+#define MMC_TXOCTETCOUNT_GB_LO         0x0814
+#define MMC_TXOCTETCOUNT_GB_HI         0x0818
+#define MMC_TXFRAMECOUNT_GB_LO         0x081c
+#define MMC_TXFRAMECOUNT_GB_HI         0x0820
+#define MMC_TXBROADCASTFRAMES_G_LO     0x0824
+#define MMC_TXBROADCASTFRAMES_G_HI     0x0828
+#define MMC_TXMULTICASTFRAMES_G_LO     0x082c
+#define MMC_TXMULTICASTFRAMES_G_HI     0x0830
+#define MMC_TX64OCTETS_GB_LO           0x0834
+#define MMC_TX64OCTETS_GB_HI           0x0838
+#define MMC_TX65TO127OCTETS_GB_LO      0x083c
+#define MMC_TX65TO127OCTETS_GB_HI      0x0840
+#define MMC_TX128TO255OCTETS_GB_LO     0x0844
+#define MMC_TX128TO255OCTETS_GB_HI     0x0848
+#define MMC_TX256TO511OCTETS_GB_LO     0x084c
+#define MMC_TX256TO511OCTETS_GB_HI     0x0850
+#define MMC_TX512TO1023OCTETS_GB_LO    0x0854
+#define MMC_TX512TO1023OCTETS_GB_HI    0x0858
+#define MMC_TX1024TOMAXOCTETS_GB_LO    0x085c
+#define MMC_TX1024TOMAXOCTETS_GB_HI    0x0860
+#define MMC_TXUNICASTFRAMES_GB_LO      0x0864
+#define MMC_TXUNICASTFRAMES_GB_HI      0x0868
+#define MMC_TXMULTICASTFRAMES_GB_LO    0x086c
+#define MMC_TXMULTICASTFRAMES_GB_HI    0x0870
+#define MMC_TXBROADCASTFRAMES_GB_LO    0x0874
+#define MMC_TXBROADCASTFRAMES_GB_HI    0x0878
+#define MMC_TXUNDERFLOWERROR_LO                0x087c
+#define MMC_TXUNDERFLOWERROR_HI                0x0880
+#define MMC_TXOCTETCOUNT_G_LO          0x0884
+#define MMC_TXOCTETCOUNT_G_HI          0x0888
+#define MMC_TXFRAMECOUNT_G_LO          0x088c
+#define MMC_TXFRAMECOUNT_G_HI          0x0890
+#define MMC_TXPAUSEFRAMES_LO           0x0894
+#define MMC_TXPAUSEFRAMES_HI           0x0898
+#define MMC_TXVLANFRAMES_G_LO          0x089c
+#define MMC_TXVLANFRAMES_G_HI          0x08a0
+#define MMC_RXFRAMECOUNT_GB_LO         0x0900
+#define MMC_RXFRAMECOUNT_GB_HI         0x0904
+#define MMC_RXOCTETCOUNT_GB_LO         0x0908
+#define MMC_RXOCTETCOUNT_GB_HI         0x090c
+#define MMC_RXOCTETCOUNT_G_LO          0x0910
+#define MMC_RXOCTETCOUNT_G_HI          0x0914
+#define MMC_RXBROADCASTFRAMES_G_LO     0x0918
+#define MMC_RXBROADCASTFRAMES_G_HI     0x091c
+#define MMC_RXMULTICASTFRAMES_G_LO     0x0920
+#define MMC_RXMULTICASTFRAMES_G_HI     0x0924
+#define MMC_RXCRCERROR_LO              0x0928
+#define MMC_RXCRCERROR_HI              0x092c
+#define MMC_RXRUNTERROR                        0x0930
+#define MMC_RXJABBERERROR              0x0934
+#define MMC_RXUNDERSIZE_G              0x0938
+#define MMC_RXOVERSIZE_G               0x093c
+#define MMC_RX64OCTETS_GB_LO           0x0940
+#define MMC_RX64OCTETS_GB_HI           0x0944
+#define MMC_RX65TO127OCTETS_GB_LO      0x0948
+#define MMC_RX65TO127OCTETS_GB_HI      0x094c
+#define MMC_RX128TO255OCTETS_GB_LO     0x0950
+#define MMC_RX128TO255OCTETS_GB_HI     0x0954
+#define MMC_RX256TO511OCTETS_GB_LO     0x0958
+#define MMC_RX256TO511OCTETS_GB_HI     0x095c
+#define MMC_RX512TO1023OCTETS_GB_LO    0x0960
+#define MMC_RX512TO1023OCTETS_GB_HI    0x0964
+#define MMC_RX1024TOMAXOCTETS_GB_LO    0x0968
+#define MMC_RX1024TOMAXOCTETS_GB_HI    0x096c
+#define MMC_RXUNICASTFRAMES_G_LO       0x0970
+#define MMC_RXUNICASTFRAMES_G_HI       0x0974
+#define MMC_RXLENGTHERROR_LO           0x0978
+#define MMC_RXLENGTHERROR_HI           0x097c
+#define MMC_RXOUTOFRANGETYPE_LO                0x0980
+#define MMC_RXOUTOFRANGETYPE_HI                0x0984
+#define MMC_RXPAUSEFRAMES_LO           0x0988
+#define MMC_RXPAUSEFRAMES_HI           0x098c
+#define MMC_RXFIFOOVERFLOW_LO          0x0990
+#define MMC_RXFIFOOVERFLOW_HI          0x0994
+#define MMC_RXVLANFRAMES_GB_LO         0x0998
+#define MMC_RXVLANFRAMES_GB_HI         0x099c
+#define MMC_RXWATCHDOGERROR            0x09a0
+
+/* MMC register entry bit positions and sizes */
+#define MMC_CR_CR_INDEX                                0
+#define MMC_CR_CR_WIDTH                                1
+#define MMC_CR_CSR_INDEX                       1
+#define MMC_CR_CSR_WIDTH                       1
+#define MMC_CR_ROR_INDEX                       2
+#define MMC_CR_ROR_WIDTH                       1
+#define MMC_CR_MCF_INDEX                       3
+#define MMC_CR_MCF_WIDTH                       1
+#define MMC_CR_MCT_INDEX                       4
+#define MMC_CR_MCT_WIDTH                       2
+#define MMC_RIER_ALL_INTERRUPTS_INDEX          0
+#define MMC_RIER_ALL_INTERRUPTS_WIDTH          23
+#define MMC_RISR_RXFRAMECOUNT_GB_INDEX         0
+#define MMC_RISR_RXFRAMECOUNT_GB_WIDTH         1
+#define MMC_RISR_RXOCTETCOUNT_GB_INDEX         1
+#define MMC_RISR_RXOCTETCOUNT_GB_WIDTH         1
+#define MMC_RISR_RXOCTETCOUNT_G_INDEX          2
+#define MMC_RISR_RXOCTETCOUNT_G_WIDTH          1
+#define MMC_RISR_RXBROADCASTFRAMES_G_INDEX     3
+#define MMC_RISR_RXBROADCASTFRAMES_G_WIDTH     1
+#define MMC_RISR_RXMULTICASTFRAMES_G_INDEX     4
+#define MMC_RISR_RXMULTICASTFRAMES_G_WIDTH     1
+#define MMC_RISR_RXCRCERROR_INDEX              5
+#define MMC_RISR_RXCRCERROR_WIDTH              1
+#define MMC_RISR_RXRUNTERROR_INDEX             6
+#define MMC_RISR_RXRUNTERROR_WIDTH             1
+#define MMC_RISR_RXJABBERERROR_INDEX           7
+#define MMC_RISR_RXJABBERERROR_WIDTH           1
+#define MMC_RISR_RXUNDERSIZE_G_INDEX           8
+#define MMC_RISR_RXUNDERSIZE_G_WIDTH           1
+#define MMC_RISR_RXOVERSIZE_G_INDEX            9
+#define MMC_RISR_RXOVERSIZE_G_WIDTH            1
+#define MMC_RISR_RX64OCTETS_GB_INDEX           10
+#define MMC_RISR_RX64OCTETS_GB_WIDTH           1
+#define MMC_RISR_RX65TO127OCTETS_GB_INDEX      11
+#define MMC_RISR_RX65TO127OCTETS_GB_WIDTH      1
+#define MMC_RISR_RX128TO255OCTETS_GB_INDEX     12
+#define MMC_RISR_RX128TO255OCTETS_GB_WIDTH     1
+#define MMC_RISR_RX256TO511OCTETS_GB_INDEX     13
+#define MMC_RISR_RX256TO511OCTETS_GB_WIDTH     1
+#define MMC_RISR_RX512TO1023OCTETS_GB_INDEX    14
+#define MMC_RISR_RX512TO1023OCTETS_GB_WIDTH    1
+#define MMC_RISR_RX1024TOMAXOCTETS_GB_INDEX    15
+#define MMC_RISR_RX1024TOMAXOCTETS_GB_WIDTH    1
+#define MMC_RISR_RXUNICASTFRAMES_G_INDEX       16
+#define MMC_RISR_RXUNICASTFRAMES_G_WIDTH       1
+#define MMC_RISR_RXLENGTHERROR_INDEX           17
+#define MMC_RISR_RXLENGTHERROR_WIDTH           1
+#define MMC_RISR_RXOUTOFRANGETYPE_INDEX                18
+#define MMC_RISR_RXOUTOFRANGETYPE_WIDTH                1
+#define MMC_RISR_RXPAUSEFRAMES_INDEX           19
+#define MMC_RISR_RXPAUSEFRAMES_WIDTH           1
+#define MMC_RISR_RXFIFOOVERFLOW_INDEX          20
+#define MMC_RISR_RXFIFOOVERFLOW_WIDTH          1
+#define MMC_RISR_RXVLANFRAMES_GB_INDEX         21
+#define MMC_RISR_RXVLANFRAMES_GB_WIDTH         1
+#define MMC_RISR_RXWATCHDOGERROR_INDEX         22
+#define MMC_RISR_RXWATCHDOGERROR_WIDTH         1
+#define MMC_TIER_ALL_INTERRUPTS_INDEX          0
+#define MMC_TIER_ALL_INTERRUPTS_WIDTH          18
+#define MMC_TISR_TXOCTETCOUNT_GB_INDEX         0
+#define MMC_TISR_TXOCTETCOUNT_GB_WIDTH         1
+#define MMC_TISR_TXFRAMECOUNT_GB_INDEX         1
+#define MMC_TISR_TXFRAMECOUNT_GB_WIDTH         1
+#define MMC_TISR_TXBROADCASTFRAMES_G_INDEX     2
+#define MMC_TISR_TXBROADCASTFRAMES_G_WIDTH     1
+#define MMC_TISR_TXMULTICASTFRAMES_G_INDEX     3
+#define MMC_TISR_TXMULTICASTFRAMES_G_WIDTH     1
+#define MMC_TISR_TX64OCTETS_GB_INDEX           4
+#define MMC_TISR_TX64OCTETS_GB_WIDTH           1
+#define MMC_TISR_TX65TO127OCTETS_GB_INDEX      5
+#define MMC_TISR_TX65TO127OCTETS_GB_WIDTH      1
+#define MMC_TISR_TX128TO255OCTETS_GB_INDEX     6
+#define MMC_TISR_TX128TO255OCTETS_GB_WIDTH     1
+#define MMC_TISR_TX256TO511OCTETS_GB_INDEX     7
+#define MMC_TISR_TX256TO511OCTETS_GB_WIDTH     1
+#define MMC_TISR_TX512TO1023OCTETS_GB_INDEX    8
+#define MMC_TISR_TX512TO1023OCTETS_GB_WIDTH    1
+#define MMC_TISR_TX1024TOMAXOCTETS_GB_INDEX    9
+#define MMC_TISR_TX1024TOMAXOCTETS_GB_WIDTH    1
+#define MMC_TISR_TXUNICASTFRAMES_GB_INDEX      10
+#define MMC_TISR_TXUNICASTFRAMES_GB_WIDTH      1
+#define MMC_TISR_TXMULTICASTFRAMES_GB_INDEX    11
+#define MMC_TISR_TXMULTICASTFRAMES_GB_WIDTH    1
+#define MMC_TISR_TXBROADCASTFRAMES_GB_INDEX    12
+#define MMC_TISR_TXBROADCASTFRAMES_GB_WIDTH    1
+#define MMC_TISR_TXUNDERFLOWERROR_INDEX                13
+#define MMC_TISR_TXUNDERFLOWERROR_WIDTH                1
+#define MMC_TISR_TXOCTETCOUNT_G_INDEX          14
+#define MMC_TISR_TXOCTETCOUNT_G_WIDTH          1
+#define MMC_TISR_TXFRAMECOUNT_G_INDEX          15
+#define MMC_TISR_TXFRAMECOUNT_G_WIDTH          1
+#define MMC_TISR_TXPAUSEFRAMES_INDEX           16
+#define MMC_TISR_TXPAUSEFRAMES_WIDTH           1
+#define MMC_TISR_TXVLANFRAMES_G_INDEX          17
+#define MMC_TISR_TXVLANFRAMES_G_WIDTH          1
+
+/* MTL register offsets */
+#define MTL_OMR                                0x1000
+#define MTL_FDCR                       0x1008
+#define MTL_FDSR                       0x100c
+#define MTL_FDDR                       0x1010
+#define MTL_ISR                                0x1020
+#define MTL_RQDCM0R                    0x1030
+#define MTL_TCPM0R                     0x1040
+#define MTL_TCPM1R                     0x1044
+
+#define MTL_RQDCM_INC                  4
+#define MTL_RQDCM_Q_PER_REG            4
+
+/* MTL register entry bit positions and sizes */
+#define MTL_OMR_ETSALG_INDEX           5
+#define MTL_OMR_ETSALG_WIDTH           2
+#define MTL_OMR_RAA_INDEX              2
+#define MTL_OMR_RAA_WIDTH              1
+
+/* MTL queue register offsets
+ *   Multiple queues can be active.  The first queue has registers
+ *   that begin at 0x1100.  Each subsequent queue has registers that
+ *   are accessed using an offset of 0x80 from the previous queue.
+ */
+#define MTL_Q_BASE                     0x1100
+#define MTL_Q_INC                      0x80
+
+#define MTL_Q_TQOMR                    0x00
+#define MTL_Q_TQUR                     0x04
+#define MTL_Q_TQDR                     0x08
+#define MTL_Q_TCECR                    0x10
+#define MTL_Q_TCESR                    0x14
+#define MTL_Q_TCQWR                    0x18
+#define MTL_Q_RQOMR                    0x40
+#define MTL_Q_RQMPOCR                  0x44
+#define MTL_Q_RQDR                     0x4c
+#define MTL_Q_IER                      0x70
+#define MTL_Q_ISR                      0x74
+
+/* MTL queue register entry bit positions and sizes */
+#define MTL_Q_TCQWR_QW_INDEX           0
+#define MTL_Q_TCQWR_QW_WIDTH           21
+#define MTL_Q_RQOMR_EHFC_INDEX         7
+#define MTL_Q_RQOMR_EHFC_WIDTH         1
+#define MTL_Q_RQOMR_RFA_INDEX          8
+#define MTL_Q_RQOMR_RFA_WIDTH          3
+#define MTL_Q_RQOMR_RFD_INDEX          13
+#define MTL_Q_RQOMR_RFD_WIDTH          3
+#define MTL_Q_RQOMR_RQS_INDEX          16
+#define MTL_Q_RQOMR_RQS_WIDTH          9
+#define MTL_Q_RQOMR_RSF_INDEX          5
+#define MTL_Q_RQOMR_RSF_WIDTH          1
+#define MTL_Q_RQOMR_RTC_INDEX          0
+#define MTL_Q_RQOMR_RTC_WIDTH          2
+#define MTL_Q_TQOMR_FTQ_INDEX          0
+#define MTL_Q_TQOMR_FTQ_WIDTH          1
+#define MTL_Q_TQOMR_TQS_INDEX          16
+#define MTL_Q_TQOMR_TQS_WIDTH          10
+#define MTL_Q_TQOMR_TSF_INDEX          1
+#define MTL_Q_TQOMR_TSF_WIDTH          1
+#define MTL_Q_TQOMR_TTC_INDEX          4
+#define MTL_Q_TQOMR_TTC_WIDTH          3
+#define MTL_Q_TQOMR_TXQEN_INDEX                2
+#define MTL_Q_TQOMR_TXQEN_WIDTH                2
+
+/* MTL queue register value */
+#define MTL_RSF_DISABLE                        0x00
+#define MTL_RSF_ENABLE                 0x01
+#define MTL_TSF_DISABLE                        0x00
+#define MTL_TSF_ENABLE                 0x01
+
+#define MTL_RX_THRESHOLD_64            0x00
+#define MTL_RX_THRESHOLD_96            0x02
+#define MTL_RX_THRESHOLD_128           0x03
+#define MTL_TX_THRESHOLD_32            0x01
+#define MTL_TX_THRESHOLD_64            0x00
+#define MTL_TX_THRESHOLD_96            0x02
+#define MTL_TX_THRESHOLD_128           0x03
+#define MTL_TX_THRESHOLD_192           0x04
+#define MTL_TX_THRESHOLD_256           0x05
+#define MTL_TX_THRESHOLD_384           0x06
+#define MTL_TX_THRESHOLD_512           0x07
+
+#define MTL_ETSALG_WRR                 0x00
+#define MTL_ETSALG_WFQ                 0x01
+#define MTL_ETSALG_DWRR                        0x02
+#define MTL_RAA_SP                     0x00
+#define MTL_RAA_WSP                    0x01
+
+#define MTL_Q_DISABLED                 0x00
+#define MTL_Q_ENABLED                  0x02
+
+
+/* MTL traffic class register offsets
+ *   Multiple traffic classes can be active.  The first class has registers
+ *   that begin at 0x1100.  Each subsequent queue has registers that
+ *   are accessed using an offset of 0x80 from the previous queue.
+ */
+#define MTL_TC_BASE                    MTL_Q_BASE
+#define MTL_TC_INC                     MTL_Q_INC
+
+#define MTL_TC_ETSCR                   0x10
+
+/* MTL traffic class register entry bit positions and sizes */
+#define MTL_TC_ETSCR_TSA_INDEX         0
+#define MTL_TC_ETSCR_TSA_WIDTH         2
+
+/* MTL traffic class register value */
+#define MTL_TSA_SP                     0x00
+#define MTL_TSA_ETS                    0x02
+
+
+/* PCS MMD select register offset
+ *  The MMD select register is used for accessing PCS registers
+ *  when the underlying APB3 interface is using indirect addressing.
+ *  Indirect addressing requires accessing registers in two phases,
+ *  an address phase and a data phase.  The address phases requires
+ *  writing an address selection value to the MMD select regiesters.
+ */
+#define PCS_MMD_SELECT                 0xff
+
+
+/* Descriptor/Packet entry bit positions and sizes */
+#define RX_PACKET_ERRORS_CRC_INDEX             2
+#define RX_PACKET_ERRORS_CRC_WIDTH             1
+#define RX_PACKET_ERRORS_FRAME_INDEX           3
+#define RX_PACKET_ERRORS_FRAME_WIDTH           1
+#define RX_PACKET_ERRORS_LENGTH_INDEX          0
+#define RX_PACKET_ERRORS_LENGTH_WIDTH          1
+#define RX_PACKET_ERRORS_OVERRUN_INDEX         1
+#define RX_PACKET_ERRORS_OVERRUN_WIDTH         1
+
+#define RX_PACKET_ATTRIBUTES_CSUM_DONE_INDEX   0
+#define RX_PACKET_ATTRIBUTES_CSUM_DONE_WIDTH   1
+#define RX_PACKET_ATTRIBUTES_VLAN_CTAG_INDEX   1
+#define RX_PACKET_ATTRIBUTES_VLAN_CTAG_WIDTH   1
+#define RX_PACKET_ATTRIBUTES_INCOMPLETE_INDEX  2
+#define RX_PACKET_ATTRIBUTES_INCOMPLETE_WIDTH  1
+
+#define RX_NORMAL_DESC0_OVT_INDEX              0
+#define RX_NORMAL_DESC0_OVT_WIDTH              16
+#define RX_NORMAL_DESC3_ES_INDEX               15
+#define RX_NORMAL_DESC3_ES_WIDTH               1
+#define RX_NORMAL_DESC3_ETLT_INDEX             16
+#define RX_NORMAL_DESC3_ETLT_WIDTH             4
+#define RX_NORMAL_DESC3_INTE_INDEX             30
+#define RX_NORMAL_DESC3_INTE_WIDTH             1
+#define RX_NORMAL_DESC3_LD_INDEX               28
+#define RX_NORMAL_DESC3_LD_WIDTH               1
+#define RX_NORMAL_DESC3_OWN_INDEX              31
+#define RX_NORMAL_DESC3_OWN_WIDTH              1
+#define RX_NORMAL_DESC3_PL_INDEX               0
+#define RX_NORMAL_DESC3_PL_WIDTH               14
+
+#define TX_PACKET_ATTRIBUTES_CSUM_ENABLE_INDEX 0
+#define TX_PACKET_ATTRIBUTES_CSUM_ENABLE_WIDTH 1
+#define TX_PACKET_ATTRIBUTES_TSO_ENABLE_INDEX  1
+#define TX_PACKET_ATTRIBUTES_TSO_ENABLE_WIDTH  1
+#define TX_PACKET_ATTRIBUTES_VLAN_CTAG_INDEX   2
+#define TX_PACKET_ATTRIBUTES_VLAN_CTAG_WIDTH   1
+
+#define TX_CONTEXT_DESC2_MSS_INDEX             0
+#define TX_CONTEXT_DESC2_MSS_WIDTH             15
+#define TX_CONTEXT_DESC3_CTXT_INDEX            30
+#define TX_CONTEXT_DESC3_CTXT_WIDTH            1
+#define TX_CONTEXT_DESC3_TCMSSV_INDEX          26
+#define TX_CONTEXT_DESC3_TCMSSV_WIDTH          1
+#define TX_CONTEXT_DESC3_VLTV_INDEX            16
+#define TX_CONTEXT_DESC3_VLTV_WIDTH            1
+#define TX_CONTEXT_DESC3_VT_INDEX              0
+#define TX_CONTEXT_DESC3_VT_WIDTH              16
+
+#define TX_NORMAL_DESC2_HL_B1L_INDEX           0
+#define TX_NORMAL_DESC2_HL_B1L_WIDTH           14
+#define TX_NORMAL_DESC2_IC_INDEX               31
+#define TX_NORMAL_DESC2_IC_WIDTH               1
+#define TX_NORMAL_DESC2_VTIR_INDEX             14
+#define TX_NORMAL_DESC2_VTIR_WIDTH             2
+#define TX_NORMAL_DESC3_CIC_INDEX              16
+#define TX_NORMAL_DESC3_CIC_WIDTH              2
+#define TX_NORMAL_DESC3_CPC_INDEX              26
+#define TX_NORMAL_DESC3_CPC_WIDTH              2
+#define TX_NORMAL_DESC3_CTXT_INDEX             30
+#define TX_NORMAL_DESC3_CTXT_WIDTH             1
+#define TX_NORMAL_DESC3_FD_INDEX               29
+#define TX_NORMAL_DESC3_FD_WIDTH               1
+#define TX_NORMAL_DESC3_FL_INDEX               0
+#define TX_NORMAL_DESC3_FL_WIDTH               15
+#define TX_NORMAL_DESC3_LD_INDEX               28
+#define TX_NORMAL_DESC3_LD_WIDTH               1
+#define TX_NORMAL_DESC3_OWN_INDEX              31
+#define TX_NORMAL_DESC3_OWN_WIDTH              1
+#define TX_NORMAL_DESC3_TCPHDRLEN_INDEX                19
+#define TX_NORMAL_DESC3_TCPHDRLEN_WIDTH                4
+#define TX_NORMAL_DESC3_TCPPL_INDEX            0
+#define TX_NORMAL_DESC3_TCPPL_WIDTH            18
+#define TX_NORMAL_DESC3_TSE_INDEX              18
+#define TX_NORMAL_DESC3_TSE_WIDTH              1
+
+#define TX_NORMAL_DESC2_VLAN_INSERT            0x2
+
+/* MDIO undefined or vendor specific registers */
+#ifndef MDIO_AN_COMP_STAT
+#define MDIO_AN_COMP_STAT              0x0030
+#endif
+
+
+/* Bit setting and getting macros
+ *  The get macro will extract the current bit field value from within
+ *  the variable
+ *
+ *  The set macro will clear the current bit field value within the
+ *  variable and then set the bit field of the variable to the
+ *  specified value
+ */
+#define GET_BITS(_var, _index, _width)                                 \
+       (((_var) >> (_index)) & ((0x1 << (_width)) - 1))
+
+#define SET_BITS(_var, _index, _width, _val)                           \
+do {                                                                   \
+       (_var) &= ~(((0x1 << (_width)) - 1) << (_index));               \
+       (_var) |= (((_val) & ((0x1 << (_width)) - 1)) << (_index));     \
+} while (0)
+
+#define GET_BITS_LE(_var, _index, _width)                              \
+       ((le32_to_cpu((_var)) >> (_index)) & ((0x1 << (_width)) - 1))
+
+#define SET_BITS_LE(_var, _index, _width, _val)                                \
+do {                                                                   \
+       (_var) &= cpu_to_le32(~(((0x1 << (_width)) - 1) << (_index)));  \
+       (_var) |= cpu_to_le32((((_val) &                                \
+                             ((0x1 << (_width)) - 1)) << (_index)));   \
+} while (0)
+
+
+/* Bit setting and getting macros based on register fields
+ *  The get macro uses the bit field definitions formed using the input
+ *  names to extract the current bit field value from within the
+ *  variable
+ *
+ *  The set macro uses the bit field definitions formed using the input
+ *  names to set the bit field of the variable to the specified value
+ */
+#define XGMAC_GET_BITS(_var, _prefix, _field)                          \
+       GET_BITS((_var),                                                \
+                _prefix##_##_field##_INDEX,                            \
+                _prefix##_##_field##_WIDTH)
+
+#define XGMAC_SET_BITS(_var, _prefix, _field, _val)                    \
+       SET_BITS((_var),                                                \
+                _prefix##_##_field##_INDEX,                            \
+                _prefix##_##_field##_WIDTH, (_val))
+
+#define XGMAC_GET_BITS_LE(_var, _prefix, _field)                       \
+       GET_BITS_LE((_var),                                             \
+                _prefix##_##_field##_INDEX,                            \
+                _prefix##_##_field##_WIDTH)
+
+#define XGMAC_SET_BITS_LE(_var, _prefix, _field, _val)                 \
+       SET_BITS_LE((_var),                                             \
+                _prefix##_##_field##_INDEX,                            \
+                _prefix##_##_field##_WIDTH, (_val))
+
+
+/* Macros for reading or writing registers
+ *  The ioread macros will get bit fields or full values using the
+ *  register definitions formed using the input names
+ *
+ *  The iowrite macros will set bit fields or full values using the
+ *  register definitions formed using the input names
+ */
+#define XGMAC_IOREAD(_pdata, _reg)                                     \
+       ioread32((_pdata)->xgmac_regs + _reg)
+
+#define XGMAC_IOREAD_BITS(_pdata, _reg, _field)                                \
+       GET_BITS(XGMAC_IOREAD((_pdata), _reg),                          \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH)
+
+#define XGMAC_IOWRITE(_pdata, _reg, _val)                              \
+       iowrite32((_val), (_pdata)->xgmac_regs + _reg)
+
+#define XGMAC_IOWRITE_BITS(_pdata, _reg, _field, _val)                 \
+do {                                                                   \
+       u32 reg_val = XGMAC_IOREAD((_pdata), _reg);                     \
+       SET_BITS(reg_val,                                               \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH, (_val));                      \
+       XGMAC_IOWRITE((_pdata), _reg, reg_val);                         \
+} while (0)
+
+
+/* Macros for reading or writing MTL queue or traffic class registers
+ *  Similar to the standard read and write macros except that the
+ *  base register value is calculated by the queue or traffic class number
+ */
+#define XGMAC_MTL_IOREAD(_pdata, _n, _reg)                             \
+       ioread32((_pdata)->xgmac_regs +                                 \
+                MTL_Q_BASE + ((_n) * MTL_Q_INC) + _reg)
+
+#define XGMAC_MTL_IOREAD_BITS(_pdata, _n, _reg, _field)                        \
+       GET_BITS(XGMAC_MTL_IOREAD((_pdata), (_n), _reg),                \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH)
+
+#define XGMAC_MTL_IOWRITE(_pdata, _n, _reg, _val)                      \
+       iowrite32((_val), (_pdata)->xgmac_regs +                        \
+                 MTL_Q_BASE + ((_n) * MTL_Q_INC) + _reg)
+
+#define XGMAC_MTL_IOWRITE_BITS(_pdata, _n, _reg, _field, _val)         \
+do {                                                                   \
+       u32 reg_val = XGMAC_MTL_IOREAD((_pdata), (_n), _reg);           \
+       SET_BITS(reg_val,                                               \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH, (_val));                      \
+       XGMAC_MTL_IOWRITE((_pdata), (_n), _reg, reg_val);               \
+} while (0)
+
+
+/* Macros for reading or writing DMA channel registers
+ *  Similar to the standard read and write macros except that the
+ *  base register value is obtained from the ring
+ */
+#define XGMAC_DMA_IOREAD(_channel, _reg)                               \
+       ioread32((_channel)->dma_regs + _reg)
+
+#define XGMAC_DMA_IOREAD_BITS(_channel, _reg, _field)                  \
+       GET_BITS(XGMAC_DMA_IOREAD((_channel), _reg),                    \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH)
+
+#define XGMAC_DMA_IOWRITE(_channel, _reg, _val)                                \
+       iowrite32((_val), (_channel)->dma_regs + _reg)
+
+#define XGMAC_DMA_IOWRITE_BITS(_channel, _reg, _field, _val)           \
+do {                                                                   \
+       u32 reg_val = XGMAC_DMA_IOREAD((_channel), _reg);               \
+       SET_BITS(reg_val,                                               \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH, (_val));                      \
+       XGMAC_DMA_IOWRITE((_channel), _reg, reg_val);                   \
+} while (0)
+
+
+/* Macros for building, reading or writing register values or bits
+ * within the register values of XPCS registers.
+ */
+#define XPCS_IOWRITE(_pdata, _off, _val)                               \
+       iowrite32(_val, (_pdata)->xpcs_regs + (_off))
+
+#define XPCS_IOREAD(_pdata, _off)                                      \
+       ioread32((_pdata)->xpcs_regs + (_off))
+
+
+/* Macros for building, reading or writing register values or bits
+ * using MDIO.  Different from above because of the use of standardized
+ * Linux include values.  No shifting is performed with the bit
+ * operations, everything works on mask values.
+ */
+#define XMDIO_READ(_pdata, _mmd, _reg)                                 \
+       ((_pdata)->hw_if.read_mmd_regs((_pdata), 0,                     \
+               MII_ADDR_C45 | (_mmd << 16) | ((_reg) & 0xffff)))
+
+#define XMDIO_READ_BITS(_pdata, _mmd, _reg, _mask)                     \
+       (XMDIO_READ((_pdata), _mmd, _reg) & _mask)
+
+#define XMDIO_WRITE(_pdata, _mmd, _reg, _val)                          \
+       ((_pdata)->hw_if.write_mmd_regs((_pdata), 0,                    \
+               MII_ADDR_C45 | (_mmd << 16) | ((_reg) & 0xffff), (_val)))
+
+#define XMDIO_WRITE_BITS(_pdata, _mmd, _reg, _mask, _val)              \
+do {                                                                   \
+       u32 mmd_val = XMDIO_READ((_pdata), _mmd, _reg);                 \
+       mmd_val &= ~_mask;                                              \
+       mmd_val |= (_val);                                              \
+       XMDIO_WRITE((_pdata), _mmd, _reg, mmd_val);                     \
+} while (0)
+
+#endif
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c
new file mode 100644 (file)
index 0000000..6bb76d5
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute 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 file 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/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and 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.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Advanced Micro Devices, Inc. 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 <COPYRIGHT HOLDER> 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.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and 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.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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/debugfs.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "xgbe.h"
+#include "xgbe-common.h"
+
+
+static ssize_t xgbe_common_read(char __user *buffer, size_t count,
+                               loff_t *ppos, unsigned int value)
+{
+       char *buf;
+       ssize_t len;
+
+       if (*ppos != 0)
+               return 0;
+
+       buf = kasprintf(GFP_KERNEL, "0x%08x\n", value);
+       if (!buf)
+               return -ENOMEM;
+
+       if (count < strlen(buf)) {
+               kfree(buf);
+               return -ENOSPC;
+       }
+
+       len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
+       kfree(buf);
+
+       return len;
+}
+
+static ssize_t xgbe_common_write(const char __user *buffer, size_t count,
+                                loff_t *ppos, unsigned int *value)
+{
+       char workarea[32];
+       ssize_t len;
+       unsigned int scan_value;
+
+       if (*ppos != 0)
+               return 0;
+
+       if (count >= sizeof(workarea))
+               return -ENOSPC;
+
+       len = simple_write_to_buffer(workarea, sizeof(workarea) - 1, ppos,
+                                    buffer, count);
+       if (len < 0)
+               return len;
+
+       workarea[len] = '\0';
+       if (sscanf(workarea, "%x", &scan_value) == 1)
+               *value = scan_value;
+       else
+               return -EIO;
+
+       return len;
+}
+
+static ssize_t xgmac_reg_addr_read(struct file *filp, char __user *buffer,
+                                  size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+
+       return xgbe_common_read(buffer, count, ppos, pdata->debugfs_xgmac_reg);
+}
+
+static ssize_t xgmac_reg_addr_write(struct file *filp,
+                                   const char __user *buffer,
+                                   size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+
+       return xgbe_common_write(buffer, count, ppos,
+                                &pdata->debugfs_xgmac_reg);
+}
+
+static ssize_t xgmac_reg_value_read(struct file *filp, char __user *buffer,
+                                   size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+       unsigned int value;
+
+       value = XGMAC_IOREAD(pdata, pdata->debugfs_xgmac_reg);
+
+       return xgbe_common_read(buffer, count, ppos, value);
+}
+
+static ssize_t xgmac_reg_value_write(struct file *filp,
+                                    const char __user *buffer,
+                                    size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+       unsigned int value;
+       ssize_t len;
+
+       len = xgbe_common_write(buffer, count, ppos, &value);
+       if (len < 0)
+               return len;
+
+       XGMAC_IOWRITE(pdata, pdata->debugfs_xgmac_reg, value);
+
+       return len;
+}
+
+static const struct file_operations xgmac_reg_addr_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read =  xgmac_reg_addr_read,
+       .write = xgmac_reg_addr_write,
+};
+
+static const struct file_operations xgmac_reg_value_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read =  xgmac_reg_value_read,
+       .write = xgmac_reg_value_write,
+};
+
+static ssize_t xpcs_mmd_read(struct file *filp, char __user *buffer,
+                            size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+
+       return xgbe_common_read(buffer, count, ppos, pdata->debugfs_xpcs_mmd);
+}
+
+static ssize_t xpcs_mmd_write(struct file *filp, const char __user *buffer,
+                             size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+
+       return xgbe_common_write(buffer, count, ppos,
+                                &pdata->debugfs_xpcs_mmd);
+}
+
+static ssize_t xpcs_reg_addr_read(struct file *filp, char __user *buffer,
+                                 size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+
+       return xgbe_common_read(buffer, count, ppos, pdata->debugfs_xpcs_reg);
+}
+
+static ssize_t xpcs_reg_addr_write(struct file *filp, const char __user *buffer,
+                                  size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+
+       return xgbe_common_write(buffer, count, ppos,
+                                &pdata->debugfs_xpcs_reg);
+}
+
+static ssize_t xpcs_reg_value_read(struct file *filp, char __user *buffer,
+                                  size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+       unsigned int value;
+
+       value = pdata->hw_if.read_mmd_regs(pdata, pdata->debugfs_xpcs_mmd,
+                                          pdata->debugfs_xpcs_reg);
+
+       return xgbe_common_read(buffer, count, ppos, value);
+}
+
+static ssize_t xpcs_reg_value_write(struct file *filp,
+                                   const char __user *buffer,
+                                   size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+       unsigned int value;
+       ssize_t len;
+
+       len = xgbe_common_write(buffer, count, ppos, &value);
+       if (len < 0)
+               return len;
+
+       pdata->hw_if.write_mmd_regs(pdata, pdata->debugfs_xpcs_mmd,
+                                   pdata->debugfs_xpcs_reg, value);
+
+       return len;
+}
+
+static const struct file_operations xpcs_mmd_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read =  xpcs_mmd_read,
+       .write = xpcs_mmd_write,
+};
+
+static const struct file_operations xpcs_reg_addr_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read =  xpcs_reg_addr_read,
+       .write = xpcs_reg_addr_write,
+};
+
+static const struct file_operations xpcs_reg_value_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read =  xpcs_reg_value_read,
+       .write = xpcs_reg_value_write,
+};
+
+void xgbe_debugfs_init(struct xgbe_prv_data *pdata)
+{
+       struct dentry *pfile;
+       char *buf;
+
+       /* Set defaults */
+       pdata->debugfs_xgmac_reg = 0;
+       pdata->debugfs_xpcs_mmd = 1;
+       pdata->debugfs_xpcs_reg = 0;
+
+       buf = kasprintf(GFP_KERNEL, "amd-xgbe-%s", pdata->netdev->name);
+       pdata->xgbe_debugfs = debugfs_create_dir(buf, NULL);
+       if (pdata->xgbe_debugfs == NULL) {
+               netdev_err(pdata->netdev, "debugfs_create_dir failed\n");
+               return;
+       }
+
+       pfile = debugfs_create_file("xgmac_register", 0600,
+                                   pdata->xgbe_debugfs, pdata,
+                                   &xgmac_reg_addr_fops);
+       if (!pfile)
+               netdev_err(pdata->netdev, "debugfs_create_file failed\n");
+
+       pfile = debugfs_create_file("xgmac_register_value", 0600,
+                                   pdata->xgbe_debugfs, pdata,
+                                   &xgmac_reg_value_fops);
+       if (!pfile)
+               netdev_err(pdata->netdev, "debugfs_create_file failed\n");
+
+       pfile = debugfs_create_file("xpcs_mmd", 0600,
+                                   pdata->xgbe_debugfs, pdata,
+                                   &xpcs_mmd_fops);
+       if (!pfile)
+               netdev_err(pdata->netdev, "debugfs_create_file failed\n");
+
+       pfile = debugfs_create_file("xpcs_register", 0600,
+                                   pdata->xgbe_debugfs, pdata,
+                                   &xpcs_reg_addr_fops);
+       if (!pfile)
+               netdev_err(pdata->netdev, "debugfs_create_file failed\n");
+
+       pfile = debugfs_create_file("xpcs_register_value", 0600,
+                                   pdata->xgbe_debugfs, pdata,
+                                   &xpcs_reg_value_fops);
+       if (!pfile)
+               netdev_err(pdata->netdev, "debugfs_create_file failed\n");
+
+       kfree(buf);
+}
+
+void xgbe_debugfs_exit(struct xgbe_prv_data *pdata)
+{
+       debugfs_remove_recursive(pdata->xgbe_debugfs);
+       pdata->xgbe_debugfs = NULL;
+}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-desc.c b/drivers/net/ethernet/amd/xgbe/xgbe-desc.c
new file mode 100644 (file)
index 0000000..6f1c859
--- /dev/null
@@ -0,0 +1,556 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute 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 file 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/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and 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.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Advanced Micro Devices, Inc. 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 <COPYRIGHT HOLDER> 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.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and 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.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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 "xgbe.h"
+#include "xgbe-common.h"
+
+
+static void xgbe_unmap_skb(struct xgbe_prv_data *, struct xgbe_ring_data *);
+
+static void xgbe_free_ring(struct xgbe_prv_data *pdata,
+                          struct xgbe_ring *ring)
+{
+       struct xgbe_ring_data *rdata;
+       unsigned int i;
+
+       if (!ring)
+               return;
+
+       if (ring->rdata) {
+               for (i = 0; i < ring->rdesc_count; i++) {
+                       rdata = GET_DESC_DATA(ring, i);
+                       xgbe_unmap_skb(pdata, rdata);
+               }
+
+               kfree(ring->rdata);
+               ring->rdata = NULL;
+       }
+
+       if (ring->rdesc) {
+               dma_free_coherent(pdata->dev,
+                                 (sizeof(struct xgbe_ring_desc) *
+                                  ring->rdesc_count),
+                                 ring->rdesc, ring->rdesc_dma);
+               ring->rdesc = NULL;
+       }
+}
+
+static void xgbe_free_ring_resources(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       DBGPR("-->xgbe_free_ring_resources\n");
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               xgbe_free_ring(pdata, channel->tx_ring);
+               xgbe_free_ring(pdata, channel->rx_ring);
+       }
+
+       DBGPR("<--xgbe_free_ring_resources\n");
+}
+
+static int xgbe_init_ring(struct xgbe_prv_data *pdata,
+                         struct xgbe_ring *ring, unsigned int rdesc_count)
+{
+       DBGPR("-->xgbe_init_ring\n");
+
+       if (!ring)
+               return 0;
+
+       /* Descriptors */
+       ring->rdesc_count = rdesc_count;
+       ring->rdesc = dma_alloc_coherent(pdata->dev,
+                                        (sizeof(struct xgbe_ring_desc) *
+                                         rdesc_count), &ring->rdesc_dma,
+                                        GFP_KERNEL);
+       if (!ring->rdesc)
+               return -ENOMEM;
+
+       /* Descriptor information */
+       ring->rdata = kcalloc(rdesc_count, sizeof(struct xgbe_ring_data),
+                             GFP_KERNEL);
+       if (!ring->rdata)
+               return -ENOMEM;
+
+       DBGPR("    rdesc=0x%p, rdesc_dma=0x%llx, rdata=0x%p\n",
+             ring->rdesc, ring->rdesc_dma, ring->rdata);
+
+       DBGPR("<--xgbe_init_ring\n");
+
+       return 0;
+}
+
+static int xgbe_alloc_ring_resources(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+       int ret;
+
+       DBGPR("-->xgbe_alloc_ring_resources\n");
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               DBGPR("  %s - tx_ring:\n", channel->name);
+               ret = xgbe_init_ring(pdata, channel->tx_ring,
+                                    pdata->tx_desc_count);
+               if (ret) {
+                       netdev_alert(pdata->netdev,
+                                    "error initializing Tx ring\n");
+                       goto err_ring;
+               }
+
+               DBGPR("  %s - rx_ring:\n", channel->name);
+               ret = xgbe_init_ring(pdata, channel->rx_ring,
+                                    pdata->rx_desc_count);
+               if (ret) {
+                       netdev_alert(pdata->netdev,
+                                    "error initializing Tx ring\n");
+                       goto err_ring;
+               }
+       }
+
+       DBGPR("<--xgbe_alloc_ring_resources\n");
+
+       return 0;
+
+err_ring:
+       xgbe_free_ring_resources(pdata);
+
+       return ret;
+}
+
+static void xgbe_wrapper_tx_descriptor_init(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_channel *channel;
+       struct xgbe_ring *ring;
+       struct xgbe_ring_data *rdata;
+       struct xgbe_ring_desc *rdesc;
+       dma_addr_t rdesc_dma;
+       unsigned int i, j;
+
+       DBGPR("-->xgbe_wrapper_tx_descriptor_init\n");
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               ring = channel->tx_ring;
+               if (!ring)
+                       break;
+
+               rdesc = ring->rdesc;
+               rdesc_dma = ring->rdesc_dma;
+
+               for (j = 0; j < ring->rdesc_count; j++) {
+                       rdata = GET_DESC_DATA(ring, j);
+
+                       rdata->rdesc = rdesc;
+                       rdata->rdesc_dma = rdesc_dma;
+
+                       rdesc++;
+                       rdesc_dma += sizeof(struct xgbe_ring_desc);
+               }
+
+               ring->cur = 0;
+               ring->dirty = 0;
+               ring->tx.queue_stopped = 0;
+
+               hw_if->tx_desc_init(channel);
+       }
+
+       DBGPR("<--xgbe_wrapper_tx_descriptor_init\n");
+}
+
+static void xgbe_wrapper_rx_descriptor_init(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_channel *channel;
+       struct xgbe_ring *ring;
+       struct xgbe_ring_desc *rdesc;
+       struct xgbe_ring_data *rdata;
+       dma_addr_t rdesc_dma, skb_dma;
+       struct sk_buff *skb = NULL;
+       unsigned int i, j;
+
+       DBGPR("-->xgbe_wrapper_rx_descriptor_init\n");
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               ring = channel->rx_ring;
+               if (!ring)
+                       break;
+
+               rdesc = ring->rdesc;
+               rdesc_dma = ring->rdesc_dma;
+
+               for (j = 0; j < ring->rdesc_count; j++) {
+                       rdata = GET_DESC_DATA(ring, j);
+
+                       rdata->rdesc = rdesc;
+                       rdata->rdesc_dma = rdesc_dma;
+
+                       /* Allocate skb & assign to each rdesc */
+                       skb = dev_alloc_skb(pdata->rx_buf_size);
+                       if (skb == NULL)
+                               break;
+                       skb_dma = dma_map_single(pdata->dev, skb->data,
+                                                pdata->rx_buf_size,
+                                                DMA_FROM_DEVICE);
+                       if (dma_mapping_error(pdata->dev, skb_dma)) {
+                               netdev_alert(pdata->netdev,
+                                            "failed to do the dma map\n");
+                               dev_kfree_skb_any(skb);
+                               break;
+                       }
+                       rdata->skb = skb;
+                       rdata->skb_dma = skb_dma;
+                       rdata->skb_dma_len = pdata->rx_buf_size;
+
+                       rdesc++;
+                       rdesc_dma += sizeof(struct xgbe_ring_desc);
+               }
+
+               ring->cur = 0;
+               ring->dirty = 0;
+               ring->rx.realloc_index = 0;
+               ring->rx.realloc_threshold = 0;
+
+               hw_if->rx_desc_init(channel);
+       }
+
+       DBGPR("<--xgbe_wrapper_rx_descriptor_init\n");
+}
+
+static void xgbe_unmap_skb(struct xgbe_prv_data *pdata,
+                          struct xgbe_ring_data *rdata)
+{
+       if (rdata->skb_dma) {
+               if (rdata->mapped_as_page) {
+                       dma_unmap_page(pdata->dev, rdata->skb_dma,
+                                      rdata->skb_dma_len, DMA_TO_DEVICE);
+               } else {
+                       dma_unmap_single(pdata->dev, rdata->skb_dma,
+                                        rdata->skb_dma_len, DMA_TO_DEVICE);
+               }
+               rdata->skb_dma = 0;
+               rdata->skb_dma_len = 0;
+       }
+
+       if (rdata->skb) {
+               dev_kfree_skb_any(rdata->skb);
+               rdata->skb = NULL;
+       }
+
+       rdata->tso_header = 0;
+       rdata->len = 0;
+       rdata->interrupt = 0;
+       rdata->mapped_as_page = 0;
+}
+
+static int xgbe_map_tx_skb(struct xgbe_channel *channel, struct sk_buff *skb)
+{
+       struct xgbe_prv_data *pdata = channel->pdata;
+       struct xgbe_ring *ring = channel->tx_ring;
+       struct xgbe_ring_data *rdata;
+       struct xgbe_packet_data *packet;
+       struct skb_frag_struct *frag;
+       dma_addr_t skb_dma;
+       unsigned int start_index, cur_index;
+       unsigned int offset, tso, vlan, datalen, len;
+       unsigned int i;
+
+       DBGPR("-->xgbe_map_tx_skb: cur = %d\n", ring->cur);
+
+       offset = 0;
+       start_index = ring->cur;
+       cur_index = ring->cur;
+
+       packet = &ring->packet_data;
+       packet->rdesc_count = 0;
+       packet->length = 0;
+
+       tso = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                            TSO_ENABLE);
+       vlan = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                             VLAN_CTAG);
+
+       /* Save space for a context descriptor if needed */
+       if ((tso && (packet->mss != ring->tx.cur_mss)) ||
+           (vlan && (packet->vlan_ctag != ring->tx.cur_vlan_ctag)))
+               cur_index++;
+       rdata = GET_DESC_DATA(ring, cur_index);
+
+       if (tso) {
+               DBGPR("  TSO packet\n");
+
+               /* Map the TSO header */
+               skb_dma = dma_map_single(pdata->dev, skb->data,
+                                        packet->header_len, DMA_TO_DEVICE);
+               if (dma_mapping_error(pdata->dev, skb_dma)) {
+                       netdev_alert(pdata->netdev, "dma_map_single failed\n");
+                       goto err_out;
+               }
+               rdata->skb_dma = skb_dma;
+               rdata->skb_dma_len = packet->header_len;
+               rdata->tso_header = 1;
+
+               offset = packet->header_len;
+
+               packet->length += packet->header_len;
+
+               cur_index++;
+               rdata = GET_DESC_DATA(ring, cur_index);
+       }
+
+       /* Map the (remainder of the) packet */
+       for (datalen = skb_headlen(skb) - offset; datalen; ) {
+               len = min_t(unsigned int, datalen, TX_MAX_BUF_SIZE);
+
+               skb_dma = dma_map_single(pdata->dev, skb->data + offset, len,
+                                        DMA_TO_DEVICE);
+               if (dma_mapping_error(pdata->dev, skb_dma)) {
+                       netdev_alert(pdata->netdev, "dma_map_single failed\n");
+                       goto err_out;
+               }
+               rdata->skb_dma = skb_dma;
+               rdata->skb_dma_len = len;
+               DBGPR("  skb data: index=%u, dma=0x%llx, len=%u\n",
+                     cur_index, skb_dma, len);
+
+               datalen -= len;
+               offset += len;
+
+               packet->length += len;
+
+               cur_index++;
+               rdata = GET_DESC_DATA(ring, cur_index);
+       }
+
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+               DBGPR("  mapping frag %u\n", i);
+
+               frag = &skb_shinfo(skb)->frags[i];
+               offset = 0;
+
+               for (datalen = skb_frag_size(frag); datalen; ) {
+                       len = min_t(unsigned int, datalen, TX_MAX_BUF_SIZE);
+
+                       skb_dma = skb_frag_dma_map(pdata->dev, frag, offset,
+                                                  len, DMA_TO_DEVICE);
+                       if (dma_mapping_error(pdata->dev, skb_dma)) {
+                               netdev_alert(pdata->netdev,
+                                            "skb_frag_dma_map failed\n");
+                               goto err_out;
+                       }
+                       rdata->skb_dma = skb_dma;
+                       rdata->skb_dma_len = len;
+                       rdata->mapped_as_page = 1;
+                       DBGPR("  skb data: index=%u, dma=0x%llx, len=%u\n",
+                             cur_index, skb_dma, len);
+
+                       datalen -= len;
+                       offset += len;
+
+                       packet->length += len;
+
+                       cur_index++;
+                       rdata = GET_DESC_DATA(ring, cur_index);
+               }
+       }
+
+       /* Save the skb address in the last entry */
+       rdata->skb = skb;
+
+       /* Save the number of descriptor entries used */
+       packet->rdesc_count = cur_index - start_index;
+
+       DBGPR("<--xgbe_map_tx_skb: count=%u\n", packet->rdesc_count);
+
+       return packet->rdesc_count;
+
+err_out:
+       while (start_index < cur_index) {
+               rdata = GET_DESC_DATA(ring, start_index++);
+               xgbe_unmap_skb(pdata, rdata);
+       }
+
+       DBGPR("<--xgbe_map_tx_skb: count=0\n");
+
+       return 0;
+}
+
+static void xgbe_realloc_skb(struct xgbe_channel *channel)
+{
+       struct xgbe_prv_data *pdata = channel->pdata;
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_ring *ring = channel->rx_ring;
+       struct xgbe_ring_data *rdata;
+       struct sk_buff *skb = NULL;
+       dma_addr_t skb_dma;
+       int i;
+
+       DBGPR("-->xgbe_realloc_skb: rx_ring->rx.realloc_index = %u\n",
+             ring->rx.realloc_index);
+
+       for (i = 0; i < ring->dirty; i++) {
+               rdata = GET_DESC_DATA(ring, ring->rx.realloc_index);
+
+               /* Reset rdata values */
+               xgbe_unmap_skb(pdata, rdata);
+
+               /* Allocate skb & assign to each rdesc */
+               skb = dev_alloc_skb(pdata->rx_buf_size);
+               if (skb == NULL) {
+                       netdev_alert(pdata->netdev,
+                                    "failed to allocate skb\n");
+                       break;
+               }
+               skb_dma = dma_map_single(pdata->dev, skb->data,
+                                        pdata->rx_buf_size, DMA_FROM_DEVICE);
+               if (dma_mapping_error(pdata->dev, skb_dma)) {
+                       netdev_alert(pdata->netdev,
+                                    "failed to do the dma map\n");
+                       dev_kfree_skb_any(skb);
+                       break;
+               }
+               rdata->skb = skb;
+               rdata->skb_dma = skb_dma;
+               rdata->skb_dma_len = pdata->rx_buf_size;
+
+               hw_if->rx_desc_reset(rdata);
+
+               ring->rx.realloc_index++;
+       }
+       ring->dirty = 0;
+
+       DBGPR("<--xgbe_realloc_skb\n");
+}
+
+void xgbe_init_function_ptrs_desc(struct xgbe_desc_if *desc_if)
+{
+       DBGPR("-->xgbe_init_function_ptrs_desc\n");
+
+       desc_if->alloc_ring_resources = xgbe_alloc_ring_resources;
+       desc_if->free_ring_resources = xgbe_free_ring_resources;
+       desc_if->map_tx_skb = xgbe_map_tx_skb;
+       desc_if->realloc_skb = xgbe_realloc_skb;
+       desc_if->unmap_skb = xgbe_unmap_skb;
+       desc_if->wrapper_tx_desc_init = xgbe_wrapper_tx_descriptor_init;
+       desc_if->wrapper_rx_desc_init = xgbe_wrapper_rx_descriptor_init;
+
+       DBGPR("<--xgbe_init_function_ptrs_desc\n");
+}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
new file mode 100644 (file)
index 0000000..002293b
--- /dev/null
@@ -0,0 +1,2182 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute 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 file 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/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and 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.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Advanced Micro Devices, Inc. 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 <COPYRIGHT HOLDER> 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.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and 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.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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/phy.h>
+#include <linux/clk.h>
+
+#include "xgbe.h"
+#include "xgbe-common.h"
+
+
+static unsigned int xgbe_usec_to_riwt(struct xgbe_prv_data *pdata,
+                                     unsigned int usec)
+{
+       unsigned long rate;
+       unsigned int ret;
+
+       DBGPR("-->xgbe_usec_to_riwt\n");
+
+       rate = clk_get_rate(pdata->sysclock);
+
+       /*
+        * Convert the input usec value to the watchdog timer value. Each
+        * watchdog timer value is equivalent to 256 clock cycles.
+        * Calculate the required value as:
+        *   ( usec * ( system_clock_mhz / 10^6 ) / 256
+        */
+       ret = (usec * (rate / 1000000)) / 256;
+
+       DBGPR("<--xgbe_usec_to_riwt\n");
+
+       return ret;
+}
+
+static unsigned int xgbe_riwt_to_usec(struct xgbe_prv_data *pdata,
+                                     unsigned int riwt)
+{
+       unsigned long rate;
+       unsigned int ret;
+
+       DBGPR("-->xgbe_riwt_to_usec\n");
+
+       rate = clk_get_rate(pdata->sysclock);
+
+       /*
+        * Convert the input watchdog timer value to the usec value. Each
+        * watchdog timer value is equivalent to 256 clock cycles.
+        * Calculate the required value as:
+        *   ( riwt * 256 ) / ( system_clock_mhz / 10^6 )
+        */
+       ret = (riwt * 256) / (rate / 1000000);
+
+       DBGPR("<--xgbe_riwt_to_usec\n");
+
+       return ret;
+}
+
+static int xgbe_config_pblx8(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++)
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_CR, PBLX8,
+                                      pdata->pblx8);
+
+       return 0;
+}
+
+static int xgbe_get_tx_pbl_val(struct xgbe_prv_data *pdata)
+{
+       return XGMAC_DMA_IOREAD_BITS(pdata->channel, DMA_CH_TCR, PBL);
+}
+
+static int xgbe_config_tx_pbl_val(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->tx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_TCR, PBL,
+                                      pdata->tx_pbl);
+       }
+
+       return 0;
+}
+
+static int xgbe_get_rx_pbl_val(struct xgbe_prv_data *pdata)
+{
+       return XGMAC_DMA_IOREAD_BITS(pdata->channel, DMA_CH_RCR, PBL);
+}
+
+static int xgbe_config_rx_pbl_val(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->rx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_RCR, PBL,
+                                      pdata->rx_pbl);
+       }
+
+       return 0;
+}
+
+static int xgbe_config_osp_mode(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->tx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_TCR, OSP,
+                                      pdata->tx_osp_mode);
+       }
+
+       return 0;
+}
+
+static int xgbe_config_rsf_mode(struct xgbe_prv_data *pdata, unsigned int val)
+{
+       unsigned int i;
+
+       for (i = 0; i < pdata->hw_feat.rx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, RSF, val);
+
+       return 0;
+}
+
+static int xgbe_config_tsf_mode(struct xgbe_prv_data *pdata, unsigned int val)
+{
+       unsigned int i;
+
+       for (i = 0; i < pdata->hw_feat.tx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, TSF, val);
+
+       return 0;
+}
+
+static int xgbe_config_rx_threshold(struct xgbe_prv_data *pdata,
+                                   unsigned int val)
+{
+       unsigned int i;
+
+       for (i = 0; i < pdata->hw_feat.rx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, RTC, val);
+
+       return 0;
+}
+
+static int xgbe_config_tx_threshold(struct xgbe_prv_data *pdata,
+                                   unsigned int val)
+{
+       unsigned int i;
+
+       for (i = 0; i < pdata->hw_feat.tx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, TTC, val);
+
+       return 0;
+}
+
+static int xgbe_config_rx_coalesce(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->rx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_RIWT, RWT,
+                                      pdata->rx_riwt);
+       }
+
+       return 0;
+}
+
+static int xgbe_config_tx_coalesce(struct xgbe_prv_data *pdata)
+{
+       return 0;
+}
+
+static void xgbe_config_rx_buffer_size(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->rx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_RCR, RBSZ,
+                                      pdata->rx_buf_size);
+       }
+}
+
+static void xgbe_config_tso_mode(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->tx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_TCR, TSE, 1);
+       }
+}
+
+static int xgbe_disable_tx_flow_control(struct xgbe_prv_data *pdata)
+{
+       unsigned int max_q_count, q_count;
+       unsigned int reg, reg_val;
+       unsigned int i;
+
+       /* Clear MTL flow control */
+       for (i = 0; i < pdata->hw_feat.rx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, EHFC, 0);
+
+       /* Clear MAC flow control */
+       max_q_count = XGMAC_MAX_FLOW_CONTROL_QUEUES;
+       q_count = min_t(unsigned int, pdata->hw_feat.rx_q_cnt, max_q_count);
+       reg = MAC_Q0TFCR;
+       for (i = 0; i < q_count; i++) {
+               reg_val = XGMAC_IOREAD(pdata, reg);
+               XGMAC_SET_BITS(reg_val, MAC_Q0TFCR, TFE, 0);
+               XGMAC_IOWRITE(pdata, reg, reg_val);
+
+               reg += MAC_QTFCR_INC;
+       }
+
+       return 0;
+}
+
+static int xgbe_enable_tx_flow_control(struct xgbe_prv_data *pdata)
+{
+       unsigned int max_q_count, q_count;
+       unsigned int reg, reg_val;
+       unsigned int i;
+
+       /* Set MTL flow control */
+       for (i = 0; i < pdata->hw_feat.rx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, EHFC, 1);
+
+       /* Set MAC flow control */
+       max_q_count = XGMAC_MAX_FLOW_CONTROL_QUEUES;
+       q_count = min_t(unsigned int, pdata->hw_feat.rx_q_cnt, max_q_count);
+       reg = MAC_Q0TFCR;
+       for (i = 0; i < q_count; i++) {
+               reg_val = XGMAC_IOREAD(pdata, reg);
+
+               /* Enable transmit flow control */
+               XGMAC_SET_BITS(reg_val, MAC_Q0TFCR, TFE, 1);
+               /* Set pause time */
+               XGMAC_SET_BITS(reg_val, MAC_Q0TFCR, PT, 0xffff);
+
+               XGMAC_IOWRITE(pdata, reg, reg_val);
+
+               reg += MAC_QTFCR_INC;
+       }
+
+       return 0;
+}
+
+static int xgbe_disable_rx_flow_control(struct xgbe_prv_data *pdata)
+{
+       XGMAC_IOWRITE_BITS(pdata, MAC_RFCR, RFE, 0);
+
+       return 0;
+}
+
+static int xgbe_enable_rx_flow_control(struct xgbe_prv_data *pdata)
+{
+       XGMAC_IOWRITE_BITS(pdata, MAC_RFCR, RFE, 1);
+
+       return 0;
+}
+
+static int xgbe_config_tx_flow_control(struct xgbe_prv_data *pdata)
+{
+       if (pdata->tx_pause)
+               xgbe_enable_tx_flow_control(pdata);
+       else
+               xgbe_disable_tx_flow_control(pdata);
+
+       return 0;
+}
+
+static int xgbe_config_rx_flow_control(struct xgbe_prv_data *pdata)
+{
+       if (pdata->rx_pause)
+               xgbe_enable_rx_flow_control(pdata);
+       else
+               xgbe_disable_rx_flow_control(pdata);
+
+       return 0;
+}
+
+static void xgbe_config_flow_control(struct xgbe_prv_data *pdata)
+{
+       xgbe_config_tx_flow_control(pdata);
+       xgbe_config_rx_flow_control(pdata);
+}
+
+static void xgbe_enable_dma_interrupts(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int dma_ch_isr, dma_ch_ier;
+       unsigned int i;
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               /* Clear all the interrupts which are set */
+               dma_ch_isr = XGMAC_DMA_IOREAD(channel, DMA_CH_SR);
+               XGMAC_DMA_IOWRITE(channel, DMA_CH_SR, dma_ch_isr);
+
+               /* Clear all interrupt enable bits */
+               dma_ch_ier = 0;
+
+               /* Enable following interrupts
+                *   NIE  - Normal Interrupt Summary Enable
+                *   AIE  - Abnormal Interrupt Summary Enable
+                *   FBEE - Fatal Bus Error Enable
+                */
+               XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, NIE, 1);
+               XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, AIE, 1);
+               XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, FBEE, 1);
+
+               if (channel->tx_ring) {
+                       /* Enable the following Tx interrupts
+                        *   TIE  - Transmit Interrupt Enable (unless polling)
+                        */
+                       XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TIE, 1);
+               }
+               if (channel->rx_ring) {
+                       /* Enable following Rx interrupts
+                        *   RBUE - Receive Buffer Unavailable Enable
+                        *   RIE  - Receive Interrupt Enable
+                        */
+                       XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RBUE, 1);
+                       XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RIE, 1);
+               }
+
+               XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, dma_ch_ier);
+       }
+}
+
+static void xgbe_enable_mtl_interrupts(struct xgbe_prv_data *pdata)
+{
+       unsigned int mtl_q_isr;
+       unsigned int q_count, i;
+
+       q_count = max(pdata->hw_feat.tx_q_cnt, pdata->hw_feat.rx_q_cnt);
+       for (i = 0; i < q_count; i++) {
+               /* Clear all the interrupts which are set */
+               mtl_q_isr = XGMAC_MTL_IOREAD(pdata, i, MTL_Q_ISR);
+               XGMAC_MTL_IOWRITE(pdata, i, MTL_Q_ISR, mtl_q_isr);
+
+               /* No MTL interrupts to be enabled */
+               XGMAC_MTL_IOWRITE(pdata, i, MTL_Q_ISR, 0);
+       }
+}
+
+static void xgbe_enable_mac_interrupts(struct xgbe_prv_data *pdata)
+{
+       /* No MAC interrupts to be enabled */
+       XGMAC_IOWRITE(pdata, MAC_IER, 0);
+
+       /* Enable all counter interrupts */
+       XGMAC_IOWRITE_BITS(pdata, MMC_RIER, ALL_INTERRUPTS, 0xff);
+       XGMAC_IOWRITE_BITS(pdata, MMC_TIER, ALL_INTERRUPTS, 0xff);
+}
+
+static int xgbe_set_gmii_speed(struct xgbe_prv_data *pdata)
+{
+       XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0x3);
+
+       return 0;
+}
+
+static int xgbe_set_gmii_2500_speed(struct xgbe_prv_data *pdata)
+{
+       XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0x2);
+
+       return 0;
+}
+
+static int xgbe_set_xgmii_speed(struct xgbe_prv_data *pdata)
+{
+       XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0);
+
+       return 0;
+}
+
+static int xgbe_set_promiscuous_mode(struct xgbe_prv_data *pdata,
+                                    unsigned int enable)
+{
+       unsigned int val = enable ? 1 : 0;
+
+       if (XGMAC_IOREAD_BITS(pdata, MAC_PFR, PR) == val)
+               return 0;
+
+       DBGPR("  %s promiscuous mode\n", enable ? "entering" : "leaving");
+       XGMAC_IOWRITE_BITS(pdata, MAC_PFR, PR, val);
+
+       return 0;
+}
+
+static int xgbe_set_all_multicast_mode(struct xgbe_prv_data *pdata,
+                                      unsigned int enable)
+{
+       unsigned int val = enable ? 1 : 0;
+
+       if (XGMAC_IOREAD_BITS(pdata, MAC_PFR, PM) == val)
+               return 0;
+
+       DBGPR("  %s allmulti mode\n", enable ? "entering" : "leaving");
+       XGMAC_IOWRITE_BITS(pdata, MAC_PFR, PM, val);
+
+       return 0;
+}
+
+static int xgbe_set_addn_mac_addrs(struct xgbe_prv_data *pdata,
+                                  unsigned int am_mode)
+{
+       struct netdev_hw_addr *ha;
+       unsigned int mac_reg;
+       unsigned int mac_addr_hi, mac_addr_lo;
+       u8 *mac_addr;
+       unsigned int i;
+
+       XGMAC_IOWRITE_BITS(pdata, MAC_PFR, HUC, 0);
+       XGMAC_IOWRITE_BITS(pdata, MAC_PFR, HMC, 0);
+
+       i = 0;
+       mac_reg = MAC_MACA1HR;
+
+       netdev_for_each_uc_addr(ha, pdata->netdev) {
+               mac_addr_lo = 0;
+               mac_addr_hi = 0;
+               mac_addr = (u8 *)&mac_addr_lo;
+               mac_addr[0] = ha->addr[0];
+               mac_addr[1] = ha->addr[1];
+               mac_addr[2] = ha->addr[2];
+               mac_addr[3] = ha->addr[3];
+               mac_addr = (u8 *)&mac_addr_hi;
+               mac_addr[0] = ha->addr[4];
+               mac_addr[1] = ha->addr[5];
+
+               DBGPR("  adding unicast address %pM at 0x%04x\n",
+                     ha->addr, mac_reg);
+
+               XGMAC_SET_BITS(mac_addr_hi, MAC_MACA1HR, AE, 1);
+
+               XGMAC_IOWRITE(pdata, mac_reg, mac_addr_hi);
+               mac_reg += MAC_MACA_INC;
+               XGMAC_IOWRITE(pdata, mac_reg, mac_addr_lo);
+               mac_reg += MAC_MACA_INC;
+
+               i++;
+       }
+
+       if (!am_mode) {
+               netdev_for_each_mc_addr(ha, pdata->netdev) {
+                       mac_addr_lo = 0;
+                       mac_addr_hi = 0;
+                       mac_addr = (u8 *)&mac_addr_lo;
+                       mac_addr[0] = ha->addr[0];
+                       mac_addr[1] = ha->addr[1];
+                       mac_addr[2] = ha->addr[2];
+                       mac_addr[3] = ha->addr[3];
+                       mac_addr = (u8 *)&mac_addr_hi;
+                       mac_addr[0] = ha->addr[4];
+                       mac_addr[1] = ha->addr[5];
+
+                       DBGPR("  adding multicast address %pM at 0x%04x\n",
+                             ha->addr, mac_reg);
+
+                       XGMAC_SET_BITS(mac_addr_hi, MAC_MACA1HR, AE, 1);
+
+                       XGMAC_IOWRITE(pdata, mac_reg, mac_addr_hi);
+                       mac_reg += MAC_MACA_INC;
+                       XGMAC_IOWRITE(pdata, mac_reg, mac_addr_lo);
+                       mac_reg += MAC_MACA_INC;
+
+                       i++;
+               }
+       }
+
+       /* Clear remaining additional MAC address entries */
+       for (; i < pdata->hw_feat.addn_mac; i++) {
+               XGMAC_IOWRITE(pdata, mac_reg, 0);
+               mac_reg += MAC_MACA_INC;
+               XGMAC_IOWRITE(pdata, mac_reg, 0);
+               mac_reg += MAC_MACA_INC;
+       }
+
+       return 0;
+}
+
+static int xgbe_set_mac_address(struct xgbe_prv_data *pdata, u8 *addr)
+{
+       unsigned int mac_addr_hi, mac_addr_lo;
+
+       mac_addr_hi = (addr[5] <<  8) | (addr[4] <<  0);
+       mac_addr_lo = (addr[3] << 24) | (addr[2] << 16) |
+                     (addr[1] <<  8) | (addr[0] <<  0);
+
+       XGMAC_IOWRITE(pdata, MAC_MACA0HR, mac_addr_hi);
+       XGMAC_IOWRITE(pdata, MAC_MACA0LR, mac_addr_lo);
+
+       return 0;
+}
+
+static int xgbe_read_mmd_regs(struct xgbe_prv_data *pdata, int prtad,
+                             int mmd_reg)
+{
+       unsigned int mmd_address;
+       int mmd_data;
+
+       if (mmd_reg & MII_ADDR_C45)
+               mmd_address = mmd_reg & ~MII_ADDR_C45;
+       else
+               mmd_address = (pdata->mdio_mmd << 16) | (mmd_reg & 0xffff);
+
+       /* The PCS registers are accessed using mmio. The underlying APB3
+        * management interface uses indirect addressing to access the MMD
+        * register sets. This requires accessing of the PCS register in two
+        * phases, an address phase and a data phase.
+        *
+        * The mmio interface is based on 32-bit offsets and values. All
+        * register offsets must therefore be adjusted by left shifting the
+        * offset 2 bits and reading 32 bits of data.
+        */
+       mutex_lock(&pdata->xpcs_mutex);
+       XPCS_IOWRITE(pdata, PCS_MMD_SELECT << 2, mmd_address >> 8);
+       mmd_data = XPCS_IOREAD(pdata, (mmd_address & 0xff) << 2);
+       mutex_unlock(&pdata->xpcs_mutex);
+
+       return mmd_data;
+}
+
+static void xgbe_write_mmd_regs(struct xgbe_prv_data *pdata, int prtad,
+                               int mmd_reg, int mmd_data)
+{
+       unsigned int mmd_address;
+
+       if (mmd_reg & MII_ADDR_C45)
+               mmd_address = mmd_reg & ~MII_ADDR_C45;
+       else
+               mmd_address = (pdata->mdio_mmd << 16) | (mmd_reg & 0xffff);
+
+       /* The PCS registers are accessed using mmio. The underlying APB3
+        * management interface uses indirect addressing to access the MMD
+        * register sets. This requires accessing of the PCS register in two
+        * phases, an address phase and a data phase.
+        *
+        * The mmio interface is based on 32-bit offsets and values. All
+        * register offsets must therefore be adjusted by left shifting the
+        * offset 2 bits and reading 32 bits of data.
+        */
+       mutex_lock(&pdata->xpcs_mutex);
+       XPCS_IOWRITE(pdata, PCS_MMD_SELECT << 2, mmd_address >> 8);
+       XPCS_IOWRITE(pdata, (mmd_address & 0xff) << 2, mmd_data);
+       mutex_unlock(&pdata->xpcs_mutex);
+}
+
+static int xgbe_tx_complete(struct xgbe_ring_desc *rdesc)
+{
+       return !XGMAC_GET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, OWN);
+}
+
+static int xgbe_disable_rx_csum(struct xgbe_prv_data *pdata)
+{
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, IPC, 0);
+
+       return 0;
+}
+
+static int xgbe_enable_rx_csum(struct xgbe_prv_data *pdata)
+{
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, IPC, 1);
+
+       return 0;
+}
+
+static int xgbe_enable_rx_vlan_stripping(struct xgbe_prv_data *pdata)
+{
+       /* Put the VLAN tag in the Rx descriptor */
+       XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, EVLRXS, 1);
+
+       /* Don't check the VLAN type */
+       XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, DOVLTC, 1);
+
+       /* Check only C-TAG (0x8100) packets */
+       XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, ERSVLM, 0);
+
+       /* Don't consider an S-TAG (0x88A8) packet as a VLAN packet */
+       XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, ESVL, 0);
+
+       /* Enable VLAN tag stripping */
+       XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, EVLS, 0x3);
+
+       return 0;
+}
+
+static int xgbe_disable_rx_vlan_stripping(struct xgbe_prv_data *pdata)
+{
+       XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, EVLS, 0);
+
+       return 0;
+}
+
+static void xgbe_tx_desc_reset(struct xgbe_ring_data *rdata)
+{
+       struct xgbe_ring_desc *rdesc = rdata->rdesc;
+
+       /* Reset the Tx descriptor
+        *   Set buffer 1 (lo) address to zero
+        *   Set buffer 1 (hi) address to zero
+        *   Reset all other control bits (IC, TTSE, B2L & B1L)
+        *   Reset all other control bits (OWN, CTXT, FD, LD, CPC, CIC, etc)
+        */
+       rdesc->desc0 = 0;
+       rdesc->desc1 = 0;
+       rdesc->desc2 = 0;
+       rdesc->desc3 = 0;
+}
+
+static void xgbe_tx_desc_init(struct xgbe_channel *channel)
+{
+       struct xgbe_ring *ring = channel->tx_ring;
+       struct xgbe_ring_data *rdata;
+       struct xgbe_ring_desc *rdesc;
+       int i;
+       int start_index = ring->cur;
+
+       DBGPR("-->tx_desc_init\n");
+
+       /* Initialze all descriptors */
+       for (i = 0; i < ring->rdesc_count; i++) {
+               rdata = GET_DESC_DATA(ring, i);
+               rdesc = rdata->rdesc;
+
+               /* Initialize Tx descriptor
+                *   Set buffer 1 (lo) address to zero
+                *   Set buffer 1 (hi) address to zero
+                *   Reset all other control bits (IC, TTSE, B2L & B1L)
+                *   Reset all other control bits (OWN, CTXT, FD, LD, CPC, CIC,
+                *     etc)
+                */
+               rdesc->desc0 = 0;
+               rdesc->desc1 = 0;
+               rdesc->desc2 = 0;
+               rdesc->desc3 = 0;
+       }
+
+       /* Make sure everything is written to the descriptor(s) before
+        * telling the device about them
+        */
+       wmb();
+
+       /* Update the total number of Tx descriptors */
+       XGMAC_DMA_IOWRITE(channel, DMA_CH_TDRLR, ring->rdesc_count - 1);
+
+       /* Update the starting address of descriptor ring */
+       rdata = GET_DESC_DATA(ring, start_index);
+       XGMAC_DMA_IOWRITE(channel, DMA_CH_TDLR_HI,
+                         upper_32_bits(rdata->rdesc_dma));
+       XGMAC_DMA_IOWRITE(channel, DMA_CH_TDLR_LO,
+                         lower_32_bits(rdata->rdesc_dma));
+
+       DBGPR("<--tx_desc_init\n");
+}
+
+static void xgbe_rx_desc_reset(struct xgbe_ring_data *rdata)
+{
+       struct xgbe_ring_desc *rdesc = rdata->rdesc;
+
+       /* Reset the Rx descriptor
+        *   Set buffer 1 (lo) address to dma address (lo)
+        *   Set buffer 1 (hi) address to dma address (hi)
+        *   Set buffer 2 (lo) address to zero
+        *   Set buffer 2 (hi) address to zero and set control bits
+        *     OWN and INTE
+        */
+       rdesc->desc0 = cpu_to_le32(lower_32_bits(rdata->skb_dma));
+       rdesc->desc1 = cpu_to_le32(upper_32_bits(rdata->skb_dma));
+       rdesc->desc2 = 0;
+
+       rdesc->desc3 = 0;
+       if (rdata->interrupt)
+               XGMAC_SET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, INTE, 1);
+
+       /* Since the Rx DMA engine is likely running, make sure everything
+        * is written to the descriptor(s) before setting the OWN bit
+        * for the descriptor
+        */
+       wmb();
+
+       XGMAC_SET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, OWN, 1);
+
+       /* Make sure ownership is written to the descriptor */
+       wmb();
+}
+
+static void xgbe_rx_desc_init(struct xgbe_channel *channel)
+{
+       struct xgbe_prv_data *pdata = channel->pdata;
+       struct xgbe_ring *ring = channel->rx_ring;
+       struct xgbe_ring_data *rdata;
+       struct xgbe_ring_desc *rdesc;
+       unsigned int start_index = ring->cur;
+       unsigned int rx_coalesce, rx_frames;
+       unsigned int i;
+
+       DBGPR("-->rx_desc_init\n");
+
+       rx_coalesce = (pdata->rx_riwt || pdata->rx_frames) ? 1 : 0;
+       rx_frames = pdata->rx_frames;
+
+       /* Initialize all descriptors */
+       for (i = 0; i < ring->rdesc_count; i++) {
+               rdata = GET_DESC_DATA(ring, i);
+               rdesc = rdata->rdesc;
+
+               /* Initialize Rx descriptor
+                *   Set buffer 1 (lo) address to dma address (lo)
+                *   Set buffer 1 (hi) address to dma address (hi)
+                *   Set buffer 2 (lo) address to zero
+                *   Set buffer 2 (hi) address to zero and set control
+                *     bits OWN and INTE appropriateley
+                */
+               rdesc->desc0 = cpu_to_le32(lower_32_bits(rdata->skb_dma));
+               rdesc->desc1 = cpu_to_le32(upper_32_bits(rdata->skb_dma));
+               rdesc->desc2 = 0;
+               rdesc->desc3 = 0;
+               XGMAC_SET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, OWN, 1);
+               XGMAC_SET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, INTE, 1);
+               rdata->interrupt = 1;
+               if (rx_coalesce && (!rx_frames || ((i + 1) % rx_frames))) {
+                       /* Clear interrupt on completion bit */
+                       XGMAC_SET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, INTE,
+                                         0);
+                       rdata->interrupt = 0;
+               }
+       }
+
+       /* Make sure everything is written to the descriptors before
+        * telling the device about them
+        */
+       wmb();
+
+       /* Update the total number of Rx descriptors */
+       XGMAC_DMA_IOWRITE(channel, DMA_CH_RDRLR, ring->rdesc_count - 1);
+
+       /* Update the starting address of descriptor ring */
+       rdata = GET_DESC_DATA(ring, start_index);
+       XGMAC_DMA_IOWRITE(channel, DMA_CH_RDLR_HI,
+                         upper_32_bits(rdata->rdesc_dma));
+       XGMAC_DMA_IOWRITE(channel, DMA_CH_RDLR_LO,
+                         lower_32_bits(rdata->rdesc_dma));
+
+       /* Update the Rx Descriptor Tail Pointer */
+       rdata = GET_DESC_DATA(ring, start_index + ring->rdesc_count - 1);
+       XGMAC_DMA_IOWRITE(channel, DMA_CH_RDTR_LO,
+                         lower_32_bits(rdata->rdesc_dma));
+
+       DBGPR("<--rx_desc_init\n");
+}
+
+static void xgbe_pre_xmit(struct xgbe_channel *channel)
+{
+       struct xgbe_prv_data *pdata = channel->pdata;
+       struct xgbe_ring *ring = channel->tx_ring;
+       struct xgbe_ring_data *rdata;
+       struct xgbe_ring_desc *rdesc;
+       struct xgbe_packet_data *packet = &ring->packet_data;
+       unsigned int csum, tso, vlan;
+       unsigned int tso_context, vlan_context;
+       unsigned int tx_coalesce, tx_frames;
+       int start_index = ring->cur;
+       int i;
+
+       DBGPR("-->xgbe_pre_xmit\n");
+
+       csum = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                             CSUM_ENABLE);
+       tso = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                            TSO_ENABLE);
+       vlan = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                             VLAN_CTAG);
+
+       if (tso && (packet->mss != ring->tx.cur_mss))
+               tso_context = 1;
+       else
+               tso_context = 0;
+
+       if (vlan && (packet->vlan_ctag != ring->tx.cur_vlan_ctag))
+               vlan_context = 1;
+       else
+               vlan_context = 0;
+
+       tx_coalesce = (pdata->tx_usecs || pdata->tx_frames) ? 1 : 0;
+       tx_frames = pdata->tx_frames;
+       if (tx_coalesce && !channel->tx_timer_active)
+               ring->coalesce_count = 0;
+
+       rdata = GET_DESC_DATA(ring, ring->cur);
+       rdesc = rdata->rdesc;
+
+       /* Create a context descriptor if this is a TSO packet */
+       if (tso_context || vlan_context) {
+               if (tso_context) {
+                       DBGPR("  TSO context descriptor, mss=%u\n",
+                             packet->mss);
+
+                       /* Set the MSS size */
+                       XGMAC_SET_BITS_LE(rdesc->desc2, TX_CONTEXT_DESC2,
+                                         MSS, packet->mss);
+
+                       /* Mark it as a CONTEXT descriptor */
+                       XGMAC_SET_BITS_LE(rdesc->desc3, TX_CONTEXT_DESC3,
+                                         CTXT, 1);
+
+                       /* Indicate this descriptor contains the MSS */
+                       XGMAC_SET_BITS_LE(rdesc->desc3, TX_CONTEXT_DESC3,
+                                         TCMSSV, 1);
+
+                       ring->tx.cur_mss = packet->mss;
+               }
+
+               if (vlan_context) {
+                       DBGPR("  VLAN context descriptor, ctag=%u\n",
+                             packet->vlan_ctag);
+
+                       /* Mark it as a CONTEXT descriptor */
+                       XGMAC_SET_BITS_LE(rdesc->desc3, TX_CONTEXT_DESC3,
+                                         CTXT, 1);
+
+                       /* Set the VLAN tag */
+                       XGMAC_SET_BITS_LE(rdesc->desc3, TX_CONTEXT_DESC3,
+                                         VT, packet->vlan_ctag);
+
+                       /* Indicate this descriptor contains the VLAN tag */
+                       XGMAC_SET_BITS_LE(rdesc->desc3, TX_CONTEXT_DESC3,
+                                         VLTV, 1);
+
+                       ring->tx.cur_vlan_ctag = packet->vlan_ctag;
+               }
+
+               ring->cur++;
+               rdata = GET_DESC_DATA(ring, ring->cur);
+               rdesc = rdata->rdesc;
+       }
+
+       /* Update buffer address (for TSO this is the header) */
+       rdesc->desc0 =  cpu_to_le32(lower_32_bits(rdata->skb_dma));
+       rdesc->desc1 =  cpu_to_le32(upper_32_bits(rdata->skb_dma));
+
+       /* Update the buffer length */
+       XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, HL_B1L,
+                         rdata->skb_dma_len);
+
+       /* VLAN tag insertion check */
+       if (vlan)
+               XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, VTIR,
+                                 TX_NORMAL_DESC2_VLAN_INSERT);
+
+       /* Set IC bit based on Tx coalescing settings */
+       XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, IC, 1);
+       if (tx_coalesce && (!tx_frames ||
+                           (++ring->coalesce_count % tx_frames)))
+               /* Clear IC bit */
+               XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, IC, 0);
+
+       /* Mark it as First Descriptor */
+       XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, FD, 1);
+
+       /* Mark it as a NORMAL descriptor */
+       XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, CTXT, 0);
+
+       /* Set OWN bit if not the first descriptor */
+       if (ring->cur != start_index)
+               XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, OWN, 1);
+
+       if (tso) {
+               /* Enable TSO */
+               XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, TSE, 1);
+               XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, TCPPL,
+                                 packet->tcp_payload_len);
+               XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, TCPHDRLEN,
+                                 packet->tcp_header_len / 4);
+       } else {
+               /* Enable CRC and Pad Insertion */
+               XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, CPC, 0);
+
+               /* Enable HW CSUM */
+               if (csum)
+                       XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3,
+                                         CIC, 0x3);
+
+               /* Set the total length to be transmitted */
+               XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, FL,
+                                 packet->length);
+       }
+
+       for (i = ring->cur - start_index + 1; i < packet->rdesc_count; i++) {
+               ring->cur++;
+               rdata = GET_DESC_DATA(ring, ring->cur);
+               rdesc = rdata->rdesc;
+
+               /* Update buffer address */
+               rdesc->desc0 = cpu_to_le32(lower_32_bits(rdata->skb_dma));
+               rdesc->desc1 = cpu_to_le32(upper_32_bits(rdata->skb_dma));
+
+               /* Update the buffer length */
+               XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, HL_B1L,
+                                 rdata->skb_dma_len);
+
+               /* Set IC bit based on Tx coalescing settings */
+               XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, IC, 1);
+               if (tx_coalesce && (!tx_frames ||
+                                   (++ring->coalesce_count % tx_frames)))
+                       /* Clear IC bit */
+                       XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, IC, 0);
+
+               /* Set OWN bit */
+               XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, OWN, 1);
+
+               /* Mark it as NORMAL descriptor */
+               XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, CTXT, 0);
+
+               /* Enable HW CSUM */
+               if (csum)
+                       XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3,
+                                         CIC, 0x3);
+       }
+
+       /* Set LAST bit for the last descriptor */
+       XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, LD, 1);
+
+       /* In case the Tx DMA engine is running, make sure everything
+        * is written to the descriptor(s) before setting the OWN bit
+        * for the first descriptor
+        */
+       wmb();
+
+       /* Set OWN bit for the first descriptor */
+       rdata = GET_DESC_DATA(ring, start_index);
+       rdesc = rdata->rdesc;
+       XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, OWN, 1);
+
+#ifdef XGMAC_ENABLE_TX_DESC_DUMP
+       xgbe_dump_tx_desc(ring, start_index, packet->rdesc_count, 1);
+#endif
+
+       /* Make sure ownership is written to the descriptor */
+       wmb();
+
+       /* Issue a poll command to Tx DMA by writing address
+        * of next immediate free descriptor */
+       ring->cur++;
+       rdata = GET_DESC_DATA(ring, ring->cur);
+       XGMAC_DMA_IOWRITE(channel, DMA_CH_TDTR_LO,
+                         lower_32_bits(rdata->rdesc_dma));
+
+       /* Start the Tx coalescing timer */
+       if (tx_coalesce && !channel->tx_timer_active) {
+               channel->tx_timer_active = 1;
+               hrtimer_start(&channel->tx_timer,
+                             ktime_set(0, pdata->tx_usecs * NSEC_PER_USEC),
+                             HRTIMER_MODE_REL);
+       }
+
+       DBGPR("  %s: descriptors %u to %u written\n",
+             channel->name, start_index & (ring->rdesc_count - 1),
+             (ring->cur - 1) & (ring->rdesc_count - 1));
+
+       DBGPR("<--xgbe_pre_xmit\n");
+}
+
+static int xgbe_dev_read(struct xgbe_channel *channel)
+{
+       struct xgbe_ring *ring = channel->rx_ring;
+       struct xgbe_ring_data *rdata;
+       struct xgbe_ring_desc *rdesc;
+       struct xgbe_packet_data *packet = &ring->packet_data;
+       unsigned int err, etlt;
+
+       DBGPR("-->xgbe_dev_read: cur = %d\n", ring->cur);
+
+       rdata = GET_DESC_DATA(ring, ring->cur);
+       rdesc = rdata->rdesc;
+
+       /* Check for data availability */
+       if (XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, OWN))
+               return 1;
+
+#ifdef XGMAC_ENABLE_RX_DESC_DUMP
+       xgbe_dump_rx_desc(ring, rdesc, ring->cur);
+#endif
+
+       /* Get the packet length */
+       rdata->len = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, PL);
+
+       if (!XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, LD)) {
+               /* Not all the data has been transferred for this packet */
+               XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
+                              INCOMPLETE, 1);
+               return 0;
+       }
+
+       /* This is the last of the data for this packet */
+       XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
+                      INCOMPLETE, 0);
+
+       /* Set checksum done indicator as appropriate */
+       if (channel->pdata->netdev->features & NETIF_F_RXCSUM)
+               XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
+                              CSUM_DONE, 1);
+
+       /* Check for errors (only valid in last descriptor) */
+       err = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, ES);
+       etlt = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, ETLT);
+       DBGPR("  err=%u, etlt=%#x\n", err, etlt);
+
+       if (!err || (err && !etlt)) {
+               if (etlt == 0x09) {
+                       XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
+                                      VLAN_CTAG, 1);
+                       packet->vlan_ctag = XGMAC_GET_BITS_LE(rdesc->desc0,
+                                                             RX_NORMAL_DESC0,
+                                                             OVT);
+                       DBGPR("  vlan-ctag=0x%04x\n", packet->vlan_ctag);
+               }
+       } else {
+               if ((etlt == 0x05) || (etlt == 0x06))
+                       XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
+                                      CSUM_DONE, 0);
+               else
+                       XGMAC_SET_BITS(packet->errors, RX_PACKET_ERRORS,
+                                      FRAME, 1);
+       }
+
+       DBGPR("<--xgbe_dev_read: %s - descriptor=%u (cur=%d)\n", channel->name,
+             ring->cur & (ring->rdesc_count - 1), ring->cur);
+
+       return 0;
+}
+
+static int xgbe_is_context_desc(struct xgbe_ring_desc *rdesc)
+{
+       /* Rx and Tx share CTXT bit, so check TDES3.CTXT bit */
+       return XGMAC_GET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, CTXT);
+}
+
+static int xgbe_is_last_desc(struct xgbe_ring_desc *rdesc)
+{
+       /* Rx and Tx share LD bit, so check TDES3.LD bit */
+       return XGMAC_GET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, LD);
+}
+
+static void xgbe_save_interrupt_status(struct xgbe_channel *channel,
+                                      enum xgbe_int_state int_state)
+{
+       unsigned int dma_ch_ier;
+
+       if (int_state == XGMAC_INT_STATE_SAVE) {
+               channel->saved_ier = XGMAC_DMA_IOREAD(channel, DMA_CH_IER);
+               channel->saved_ier &= DMA_INTERRUPT_MASK;
+       } else {
+               dma_ch_ier = XGMAC_DMA_IOREAD(channel, DMA_CH_IER);
+               dma_ch_ier |= channel->saved_ier;
+               XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, dma_ch_ier);
+       }
+}
+
+static int xgbe_enable_int(struct xgbe_channel *channel,
+                          enum xgbe_int int_id)
+{
+       switch (int_id) {
+       case XGMAC_INT_DMA_ISR_DC0IS:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, TIE, 1);
+               break;
+       case XGMAC_INT_DMA_CH_SR_TI:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, TIE, 1);
+               break;
+       case XGMAC_INT_DMA_CH_SR_TPS:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, TXSE, 1);
+               break;
+       case XGMAC_INT_DMA_CH_SR_TBU:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, TBUE, 1);
+               break;
+       case XGMAC_INT_DMA_CH_SR_RI:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, RIE, 1);
+               break;
+       case XGMAC_INT_DMA_CH_SR_RBU:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, RBUE, 1);
+               break;
+       case XGMAC_INT_DMA_CH_SR_RPS:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, RSE, 1);
+               break;
+       case XGMAC_INT_DMA_CH_SR_FBE:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, FBEE, 1);
+               break;
+       case XGMAC_INT_DMA_ALL:
+               xgbe_save_interrupt_status(channel, XGMAC_INT_STATE_RESTORE);
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
+static int xgbe_disable_int(struct xgbe_channel *channel,
+                           enum xgbe_int int_id)
+{
+       unsigned int dma_ch_ier;
+
+       switch (int_id) {
+       case XGMAC_INT_DMA_ISR_DC0IS:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, TIE, 0);
+               break;
+       case XGMAC_INT_DMA_CH_SR_TI:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, TIE, 0);
+               break;
+       case XGMAC_INT_DMA_CH_SR_TPS:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, TXSE, 0);
+               break;
+       case XGMAC_INT_DMA_CH_SR_TBU:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, TBUE, 0);
+               break;
+       case XGMAC_INT_DMA_CH_SR_RI:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, RIE, 0);
+               break;
+       case XGMAC_INT_DMA_CH_SR_RBU:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, RBUE, 0);
+               break;
+       case XGMAC_INT_DMA_CH_SR_RPS:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, RSE, 0);
+               break;
+       case XGMAC_INT_DMA_CH_SR_FBE:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, FBEE, 0);
+               break;
+       case XGMAC_INT_DMA_ALL:
+               xgbe_save_interrupt_status(channel, XGMAC_INT_STATE_SAVE);
+
+               dma_ch_ier = XGMAC_DMA_IOREAD(channel, DMA_CH_IER);
+               dma_ch_ier &= ~DMA_INTERRUPT_MASK;
+               XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, dma_ch_ier);
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
+static int xgbe_exit(struct xgbe_prv_data *pdata)
+{
+       unsigned int count = 2000;
+
+       DBGPR("-->xgbe_exit\n");
+
+       /* Issue a software reset */
+       XGMAC_IOWRITE_BITS(pdata, DMA_MR, SWR, 1);
+       usleep_range(10, 15);
+
+       /* Poll Until Poll Condition */
+       while (count-- && XGMAC_IOREAD_BITS(pdata, DMA_MR, SWR))
+               usleep_range(500, 600);
+
+       if (!count)
+               return -EBUSY;
+
+       DBGPR("<--xgbe_exit\n");
+
+       return 0;
+}
+
+static int xgbe_flush_tx_queues(struct xgbe_prv_data *pdata)
+{
+       unsigned int i, count;
+
+       for (i = 0; i < pdata->hw_feat.tx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, FTQ, 1);
+
+       /* Poll Until Poll Condition */
+       for (i = 0; i < pdata->hw_feat.tx_q_cnt; i++) {
+               count = 2000;
+               while (count-- && XGMAC_MTL_IOREAD_BITS(pdata, i,
+                                                       MTL_Q_TQOMR, FTQ))
+                       usleep_range(500, 600);
+
+               if (!count)
+                       return -EBUSY;
+       }
+
+       return 0;
+}
+
+static void xgbe_config_dma_bus(struct xgbe_prv_data *pdata)
+{
+       /* Set enhanced addressing mode */
+       XGMAC_IOWRITE_BITS(pdata, DMA_SBMR, EAME, 1);
+
+       /* Set the System Bus mode */
+       XGMAC_IOWRITE_BITS(pdata, DMA_SBMR, UNDEF, 1);
+}
+
+static void xgbe_config_dma_cache(struct xgbe_prv_data *pdata)
+{
+       unsigned int arcache, awcache;
+
+       arcache = 0;
+       XGMAC_SET_BITS(arcache, DMA_AXIARCR, DRC, DMA_ARCACHE_SETTING);
+       XGMAC_SET_BITS(arcache, DMA_AXIARCR, DRD, DMA_ARDOMAIN_SETTING);
+       XGMAC_SET_BITS(arcache, DMA_AXIARCR, TEC, DMA_ARCACHE_SETTING);
+       XGMAC_SET_BITS(arcache, DMA_AXIARCR, TED, DMA_ARDOMAIN_SETTING);
+       XGMAC_SET_BITS(arcache, DMA_AXIARCR, THC, DMA_ARCACHE_SETTING);
+       XGMAC_SET_BITS(arcache, DMA_AXIARCR, THD, DMA_ARDOMAIN_SETTING);
+       XGMAC_IOWRITE(pdata, DMA_AXIARCR, arcache);
+
+       awcache = 0;
+       XGMAC_SET_BITS(awcache, DMA_AXIAWCR, DWC, DMA_AWCACHE_SETTING);
+       XGMAC_SET_BITS(awcache, DMA_AXIAWCR, DWD, DMA_AWDOMAIN_SETTING);
+       XGMAC_SET_BITS(awcache, DMA_AXIAWCR, RPC, DMA_AWCACHE_SETTING);
+       XGMAC_SET_BITS(awcache, DMA_AXIAWCR, RPD, DMA_AWDOMAIN_SETTING);
+       XGMAC_SET_BITS(awcache, DMA_AXIAWCR, RHC, DMA_AWCACHE_SETTING);
+       XGMAC_SET_BITS(awcache, DMA_AXIAWCR, RHD, DMA_AWDOMAIN_SETTING);
+       XGMAC_SET_BITS(awcache, DMA_AXIAWCR, TDC, DMA_AWCACHE_SETTING);
+       XGMAC_SET_BITS(awcache, DMA_AXIAWCR, TDD, DMA_AWDOMAIN_SETTING);
+       XGMAC_IOWRITE(pdata, DMA_AXIAWCR, awcache);
+}
+
+static void xgbe_config_mtl_mode(struct xgbe_prv_data *pdata)
+{
+       unsigned int i;
+
+       /* Set Tx to weighted round robin scheduling algorithm (when
+        * traffic class is using ETS algorithm)
+        */
+       XGMAC_IOWRITE_BITS(pdata, MTL_OMR, ETSALG, MTL_ETSALG_WRR);
+
+       /* Set Tx traffic classes to strict priority algorithm */
+       for (i = 0; i < XGBE_TC_CNT; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_TC_ETSCR, TSA, MTL_TSA_SP);
+
+       /* Set Rx to strict priority algorithm */
+       XGMAC_IOWRITE_BITS(pdata, MTL_OMR, RAA, MTL_RAA_SP);
+}
+
+static unsigned int xgbe_calculate_per_queue_fifo(unsigned long fifo_size,
+                                                 unsigned char queue_count)
+{
+       unsigned int q_fifo_size = 0;
+       enum xgbe_mtl_fifo_size p_fifo = XGMAC_MTL_FIFO_SIZE_256;
+
+       /* Calculate Tx/Rx fifo share per queue */
+       switch (fifo_size) {
+       case 0:
+               q_fifo_size = FIFO_SIZE_B(128);
+               break;
+       case 1:
+               q_fifo_size = FIFO_SIZE_B(256);
+               break;
+       case 2:
+               q_fifo_size = FIFO_SIZE_B(512);
+               break;
+       case 3:
+               q_fifo_size = FIFO_SIZE_KB(1);
+               break;
+       case 4:
+               q_fifo_size = FIFO_SIZE_KB(2);
+               break;
+       case 5:
+               q_fifo_size = FIFO_SIZE_KB(4);
+               break;
+       case 6:
+               q_fifo_size = FIFO_SIZE_KB(8);
+               break;
+       case 7:
+               q_fifo_size = FIFO_SIZE_KB(16);
+               break;
+       case 8:
+               q_fifo_size = FIFO_SIZE_KB(32);
+               break;
+       case 9:
+               q_fifo_size = FIFO_SIZE_KB(64);
+               break;
+       case 10:
+               q_fifo_size = FIFO_SIZE_KB(128);
+               break;
+       case 11:
+               q_fifo_size = FIFO_SIZE_KB(256);
+               break;
+       }
+       q_fifo_size = q_fifo_size / queue_count;
+
+       /* Set the queue fifo size programmable value */
+       if (q_fifo_size >= FIFO_SIZE_KB(256))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_256K;
+       else if (q_fifo_size >= FIFO_SIZE_KB(128))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_128K;
+       else if (q_fifo_size >= FIFO_SIZE_KB(64))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_64K;
+       else if (q_fifo_size >= FIFO_SIZE_KB(32))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_32K;
+       else if (q_fifo_size >= FIFO_SIZE_KB(16))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_16K;
+       else if (q_fifo_size >= FIFO_SIZE_KB(8))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_8K;
+       else if (q_fifo_size >= FIFO_SIZE_KB(4))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_4K;
+       else if (q_fifo_size >= FIFO_SIZE_KB(2))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_2K;
+       else if (q_fifo_size >= FIFO_SIZE_KB(1))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_1K;
+       else if (q_fifo_size >= FIFO_SIZE_B(512))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_512;
+       else if (q_fifo_size >= FIFO_SIZE_B(256))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_256;
+
+       return p_fifo;
+}
+
+static void xgbe_config_tx_fifo_size(struct xgbe_prv_data *pdata)
+{
+       enum xgbe_mtl_fifo_size fifo_size;
+       unsigned int i;
+
+       fifo_size = xgbe_calculate_per_queue_fifo(pdata->hw_feat.tx_fifo_size,
+                                                 pdata->hw_feat.tx_q_cnt);
+
+       for (i = 0; i < pdata->hw_feat.tx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, TQS, fifo_size);
+
+       netdev_notice(pdata->netdev, "%d Tx queues, %d byte fifo per queue\n",
+                     pdata->hw_feat.tx_q_cnt, ((fifo_size + 1) * 256));
+}
+
+static void xgbe_config_rx_fifo_size(struct xgbe_prv_data *pdata)
+{
+       enum xgbe_mtl_fifo_size fifo_size;
+       unsigned int i;
+
+       fifo_size = xgbe_calculate_per_queue_fifo(pdata->hw_feat.rx_fifo_size,
+                                                 pdata->hw_feat.rx_q_cnt);
+
+       for (i = 0; i < pdata->hw_feat.rx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, RQS, fifo_size);
+
+       netdev_notice(pdata->netdev, "%d Rx queues, %d byte fifo per queue\n",
+                     pdata->hw_feat.rx_q_cnt, ((fifo_size + 1) * 256));
+}
+
+static void xgbe_config_rx_queue_mapping(struct xgbe_prv_data *pdata)
+{
+       unsigned int i, reg, reg_val;
+       unsigned int q_count = pdata->hw_feat.rx_q_cnt;
+
+       /* Select dynamic mapping of MTL Rx queue to DMA Rx channel */
+       reg = MTL_RQDCM0R;
+       reg_val = 0;
+       for (i = 0; i < q_count;) {
+               reg_val |= (0x80 << ((i++ % MTL_RQDCM_Q_PER_REG) << 3));
+
+               if ((i % MTL_RQDCM_Q_PER_REG) && (i != q_count))
+                       continue;
+
+               XGMAC_IOWRITE(pdata, reg, reg_val);
+
+               reg += MTL_RQDCM_INC;
+               reg_val = 0;
+       }
+}
+
+static void xgbe_config_flow_control_threshold(struct xgbe_prv_data *pdata)
+{
+       unsigned int i;
+
+       for (i = 0; i < pdata->hw_feat.rx_q_cnt; i++) {
+               /* Activate flow control when less than 4k left in fifo */
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, RFA, 2);
+
+               /* De-activate flow control when more than 6k left in fifo */
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, RFD, 4);
+       }
+}
+
+static void xgbe_config_mac_address(struct xgbe_prv_data *pdata)
+{
+       xgbe_set_mac_address(pdata, pdata->netdev->dev_addr);
+}
+
+static void xgbe_config_jumbo_enable(struct xgbe_prv_data *pdata)
+{
+       unsigned int val;
+
+       val = (pdata->netdev->mtu > XGMAC_STD_PACKET_MTU) ? 1 : 0;
+
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, JE, val);
+}
+
+static void xgbe_config_checksum_offload(struct xgbe_prv_data *pdata)
+{
+       if (pdata->netdev->features & NETIF_F_RXCSUM)
+               xgbe_enable_rx_csum(pdata);
+       else
+               xgbe_disable_rx_csum(pdata);
+}
+
+static void xgbe_config_vlan_support(struct xgbe_prv_data *pdata)
+{
+       if (pdata->netdev->features & NETIF_F_HW_VLAN_CTAG_RX)
+               xgbe_enable_rx_vlan_stripping(pdata);
+       else
+               xgbe_disable_rx_vlan_stripping(pdata);
+}
+
+static void xgbe_tx_mmc_int(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_mmc_stats *stats = &pdata->mmc_stats;
+       unsigned int mmc_isr = XGMAC_IOREAD(pdata, MMC_TISR);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXOCTETCOUNT_GB))
+               stats->txoctetcount_gb +=
+                       XGMAC_IOREAD(pdata, MMC_TXOCTETCOUNT_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXFRAMECOUNT_GB))
+               stats->txframecount_gb +=
+                       XGMAC_IOREAD(pdata, MMC_TXFRAMECOUNT_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXBROADCASTFRAMES_G))
+               stats->txbroadcastframes_g +=
+                       XGMAC_IOREAD(pdata, MMC_TXBROADCASTFRAMES_G_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXMULTICASTFRAMES_G))
+               stats->txmulticastframes_g +=
+                       XGMAC_IOREAD(pdata, MMC_TXMULTICASTFRAMES_G_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TX64OCTETS_GB))
+               stats->tx64octets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_TX64OCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TX65TO127OCTETS_GB))
+               stats->tx65to127octets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_TX65TO127OCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TX128TO255OCTETS_GB))
+               stats->tx128to255octets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_TX128TO255OCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TX256TO511OCTETS_GB))
+               stats->tx256to511octets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_TX256TO511OCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TX512TO1023OCTETS_GB))
+               stats->tx512to1023octets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_TX512TO1023OCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TX1024TOMAXOCTETS_GB))
+               stats->tx1024tomaxoctets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_TX1024TOMAXOCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXUNICASTFRAMES_GB))
+               stats->txunicastframes_gb +=
+                       XGMAC_IOREAD(pdata, MMC_TXUNICASTFRAMES_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXMULTICASTFRAMES_GB))
+               stats->txmulticastframes_gb +=
+                       XGMAC_IOREAD(pdata, MMC_TXMULTICASTFRAMES_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXBROADCASTFRAMES_GB))
+               stats->txbroadcastframes_g +=
+                       XGMAC_IOREAD(pdata, MMC_TXBROADCASTFRAMES_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXUNDERFLOWERROR))
+               stats->txunderflowerror +=
+                       XGMAC_IOREAD(pdata, MMC_TXUNDERFLOWERROR_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXOCTETCOUNT_G))
+               stats->txoctetcount_g +=
+                       XGMAC_IOREAD(pdata, MMC_TXOCTETCOUNT_G_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXFRAMECOUNT_G))
+               stats->txframecount_g +=
+                       XGMAC_IOREAD(pdata, MMC_TXFRAMECOUNT_G_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXPAUSEFRAMES))
+               stats->txpauseframes +=
+                       XGMAC_IOREAD(pdata, MMC_TXPAUSEFRAMES_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXVLANFRAMES_G))
+               stats->txvlanframes_g +=
+                       XGMAC_IOREAD(pdata, MMC_TXVLANFRAMES_G_LO);
+}
+
+static void xgbe_rx_mmc_int(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_mmc_stats *stats = &pdata->mmc_stats;
+       unsigned int mmc_isr = XGMAC_IOREAD(pdata, MMC_RISR);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXFRAMECOUNT_GB))
+               stats->rxframecount_gb +=
+                       XGMAC_IOREAD(pdata, MMC_RXFRAMECOUNT_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXOCTETCOUNT_GB))
+               stats->rxoctetcount_gb +=
+                       XGMAC_IOREAD(pdata, MMC_RXOCTETCOUNT_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXOCTETCOUNT_G))
+               stats->rxoctetcount_g +=
+                       XGMAC_IOREAD(pdata, MMC_RXOCTETCOUNT_G_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXBROADCASTFRAMES_G))
+               stats->rxbroadcastframes_g +=
+                       XGMAC_IOREAD(pdata, MMC_RXBROADCASTFRAMES_G_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXMULTICASTFRAMES_G))
+               stats->rxmulticastframes_g +=
+                       XGMAC_IOREAD(pdata, MMC_RXMULTICASTFRAMES_G_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXCRCERROR))
+               stats->rxcrcerror +=
+                       XGMAC_IOREAD(pdata, MMC_RXCRCERROR_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXRUNTERROR))
+               stats->rxrunterror +=
+                       XGMAC_IOREAD(pdata, MMC_RXRUNTERROR);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXJABBERERROR))
+               stats->rxjabbererror +=
+                       XGMAC_IOREAD(pdata, MMC_RXJABBERERROR);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXUNDERSIZE_G))
+               stats->rxundersize_g +=
+                       XGMAC_IOREAD(pdata, MMC_RXUNDERSIZE_G);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXOVERSIZE_G))
+               stats->rxoversize_g +=
+                       XGMAC_IOREAD(pdata, MMC_RXOVERSIZE_G);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RX64OCTETS_GB))
+               stats->rx64octets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_RX64OCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RX65TO127OCTETS_GB))
+               stats->rx65to127octets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_RX65TO127OCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RX128TO255OCTETS_GB))
+               stats->rx128to255octets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_RX128TO255OCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RX256TO511OCTETS_GB))
+               stats->rx256to511octets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_RX256TO511OCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RX512TO1023OCTETS_GB))
+               stats->rx512to1023octets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_RX512TO1023OCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RX1024TOMAXOCTETS_GB))
+               stats->rx1024tomaxoctets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_RX1024TOMAXOCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXUNICASTFRAMES_G))
+               stats->rxunicastframes_g +=
+                       XGMAC_IOREAD(pdata, MMC_RXUNICASTFRAMES_G_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXLENGTHERROR))
+               stats->rxlengtherror +=
+                       XGMAC_IOREAD(pdata, MMC_RXLENGTHERROR_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXOUTOFRANGETYPE))
+               stats->rxoutofrangetype +=
+                       XGMAC_IOREAD(pdata, MMC_RXOUTOFRANGETYPE_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXPAUSEFRAMES))
+               stats->rxpauseframes +=
+                       XGMAC_IOREAD(pdata, MMC_RXPAUSEFRAMES_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXFIFOOVERFLOW))
+               stats->rxfifooverflow +=
+                       XGMAC_IOREAD(pdata, MMC_RXFIFOOVERFLOW_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXVLANFRAMES_GB))
+               stats->rxvlanframes_gb +=
+                       XGMAC_IOREAD(pdata, MMC_RXVLANFRAMES_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXWATCHDOGERROR))
+               stats->rxwatchdogerror +=
+                       XGMAC_IOREAD(pdata, MMC_RXWATCHDOGERROR);
+}
+
+static void xgbe_read_mmc_stats(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_mmc_stats *stats = &pdata->mmc_stats;
+
+       /* Freeze counters */
+       XGMAC_IOWRITE_BITS(pdata, MMC_CR, MCF, 1);
+
+       stats->txoctetcount_gb +=
+               XGMAC_IOREAD(pdata, MMC_TXOCTETCOUNT_GB_LO);
+
+       stats->txframecount_gb +=
+               XGMAC_IOREAD(pdata, MMC_TXFRAMECOUNT_GB_LO);
+
+       stats->txbroadcastframes_g +=
+               XGMAC_IOREAD(pdata, MMC_TXBROADCASTFRAMES_G_LO);
+
+       stats->txmulticastframes_g +=
+               XGMAC_IOREAD(pdata, MMC_TXMULTICASTFRAMES_G_LO);
+
+       stats->tx64octets_gb +=
+               XGMAC_IOREAD(pdata, MMC_TX64OCTETS_GB_LO);
+
+       stats->tx65to127octets_gb +=
+               XGMAC_IOREAD(pdata, MMC_TX65TO127OCTETS_GB_LO);
+
+       stats->tx128to255octets_gb +=
+               XGMAC_IOREAD(pdata, MMC_TX128TO255OCTETS_GB_LO);
+
+       stats->tx256to511octets_gb +=
+               XGMAC_IOREAD(pdata, MMC_TX256TO511OCTETS_GB_LO);
+
+       stats->tx512to1023octets_gb +=
+               XGMAC_IOREAD(pdata, MMC_TX512TO1023OCTETS_GB_LO);
+
+       stats->tx1024tomaxoctets_gb +=
+               XGMAC_IOREAD(pdata, MMC_TX1024TOMAXOCTETS_GB_LO);
+
+       stats->txunicastframes_gb +=
+               XGMAC_IOREAD(pdata, MMC_TXUNICASTFRAMES_GB_LO);
+
+       stats->txmulticastframes_gb +=
+               XGMAC_IOREAD(pdata, MMC_TXMULTICASTFRAMES_GB_LO);
+
+       stats->txbroadcastframes_g +=
+               XGMAC_IOREAD(pdata, MMC_TXBROADCASTFRAMES_GB_LO);
+
+       stats->txunderflowerror +=
+               XGMAC_IOREAD(pdata, MMC_TXUNDERFLOWERROR_LO);
+
+       stats->txoctetcount_g +=
+               XGMAC_IOREAD(pdata, MMC_TXOCTETCOUNT_G_LO);
+
+       stats->txframecount_g +=
+               XGMAC_IOREAD(pdata, MMC_TXFRAMECOUNT_G_LO);
+
+       stats->txpauseframes +=
+               XGMAC_IOREAD(pdata, MMC_TXPAUSEFRAMES_LO);
+
+       stats->txvlanframes_g +=
+               XGMAC_IOREAD(pdata, MMC_TXVLANFRAMES_G_LO);
+
+       stats->rxframecount_gb +=
+               XGMAC_IOREAD(pdata, MMC_RXFRAMECOUNT_GB_LO);
+
+       stats->rxoctetcount_gb +=
+               XGMAC_IOREAD(pdata, MMC_RXOCTETCOUNT_GB_LO);
+
+       stats->rxoctetcount_g +=
+               XGMAC_IOREAD(pdata, MMC_RXOCTETCOUNT_G_LO);
+
+       stats->rxbroadcastframes_g +=
+               XGMAC_IOREAD(pdata, MMC_RXBROADCASTFRAMES_G_LO);
+
+       stats->rxmulticastframes_g +=
+               XGMAC_IOREAD(pdata, MMC_RXMULTICASTFRAMES_G_LO);
+
+       stats->rxcrcerror +=
+               XGMAC_IOREAD(pdata, MMC_RXCRCERROR_LO);
+
+       stats->rxrunterror +=
+               XGMAC_IOREAD(pdata, MMC_RXRUNTERROR);
+
+       stats->rxjabbererror +=
+               XGMAC_IOREAD(pdata, MMC_RXJABBERERROR);
+
+       stats->rxundersize_g +=
+               XGMAC_IOREAD(pdata, MMC_RXUNDERSIZE_G);
+
+       stats->rxoversize_g +=
+               XGMAC_IOREAD(pdata, MMC_RXOVERSIZE_G);
+
+       stats->rx64octets_gb +=
+               XGMAC_IOREAD(pdata, MMC_RX64OCTETS_GB_LO);
+
+       stats->rx65to127octets_gb +=
+               XGMAC_IOREAD(pdata, MMC_RX65TO127OCTETS_GB_LO);
+
+       stats->rx128to255octets_gb +=
+               XGMAC_IOREAD(pdata, MMC_RX128TO255OCTETS_GB_LO);
+
+       stats->rx256to511octets_gb +=
+               XGMAC_IOREAD(pdata, MMC_RX256TO511OCTETS_GB_LO);
+
+       stats->rx512to1023octets_gb +=
+               XGMAC_IOREAD(pdata, MMC_RX512TO1023OCTETS_GB_LO);
+
+       stats->rx1024tomaxoctets_gb +=
+               XGMAC_IOREAD(pdata, MMC_RX1024TOMAXOCTETS_GB_LO);
+
+       stats->rxunicastframes_g +=
+               XGMAC_IOREAD(pdata, MMC_RXUNICASTFRAMES_G_LO);
+
+       stats->rxlengtherror +=
+               XGMAC_IOREAD(pdata, MMC_RXLENGTHERROR_LO);
+
+       stats->rxoutofrangetype +=
+               XGMAC_IOREAD(pdata, MMC_RXOUTOFRANGETYPE_LO);
+
+       stats->rxpauseframes +=
+               XGMAC_IOREAD(pdata, MMC_RXPAUSEFRAMES_LO);
+
+       stats->rxfifooverflow +=
+               XGMAC_IOREAD(pdata, MMC_RXFIFOOVERFLOW_LO);
+
+       stats->rxvlanframes_gb +=
+               XGMAC_IOREAD(pdata, MMC_RXVLANFRAMES_GB_LO);
+
+       stats->rxwatchdogerror +=
+               XGMAC_IOREAD(pdata, MMC_RXWATCHDOGERROR);
+
+       /* Un-freeze counters */
+       XGMAC_IOWRITE_BITS(pdata, MMC_CR, MCF, 0);
+}
+
+static void xgbe_config_mmc(struct xgbe_prv_data *pdata)
+{
+       /* Set counters to reset on read */
+       XGMAC_IOWRITE_BITS(pdata, MMC_CR, ROR, 1);
+
+       /* Reset the counters */
+       XGMAC_IOWRITE_BITS(pdata, MMC_CR, CR, 1);
+}
+
+static void xgbe_enable_tx(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       /* Enable each Tx DMA channel */
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->tx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_TCR, ST, 1);
+       }
+
+       /* Enable each Tx queue */
+       for (i = 0; i < pdata->hw_feat.tx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, TXQEN,
+                                      MTL_Q_ENABLED);
+
+       /* Enable MAC Tx */
+       XGMAC_IOWRITE_BITS(pdata, MAC_TCR, TE, 1);
+}
+
+static void xgbe_disable_tx(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       /* Disable MAC Tx */
+       XGMAC_IOWRITE_BITS(pdata, MAC_TCR, TE, 0);
+
+       /* Disable each Tx queue */
+       for (i = 0; i < pdata->hw_feat.tx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, TXQEN, 0);
+
+       /* Disable each Tx DMA channel */
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->tx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_TCR, ST, 0);
+       }
+}
+
+static void xgbe_enable_rx(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int reg_val, i;
+
+       /* Enable each Rx DMA channel */
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->rx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_RCR, SR, 1);
+       }
+
+       /* Enable each Rx queue */
+       reg_val = 0;
+       for (i = 0; i < pdata->hw_feat.rx_q_cnt; i++)
+               reg_val |= (0x02 << (i << 1));
+       XGMAC_IOWRITE(pdata, MAC_RQC0R, reg_val);
+
+       /* Enable MAC Rx */
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, DCRCC, 1);
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, CST, 1);
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, ACS, 1);
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, RE, 1);
+}
+
+static void xgbe_disable_rx(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       /* Disable MAC Rx */
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, DCRCC, 0);
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, CST, 0);
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, ACS, 0);
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, RE, 0);
+
+       /* Disable each Rx queue */
+       XGMAC_IOWRITE(pdata, MAC_RQC0R, 0);
+
+       /* Disable each Rx DMA channel */
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->rx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_RCR, SR, 0);
+       }
+}
+
+static void xgbe_powerup_tx(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       /* Enable each Tx DMA channel */
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->tx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_TCR, ST, 1);
+       }
+
+       /* Enable MAC Tx */
+       XGMAC_IOWRITE_BITS(pdata, MAC_TCR, TE, 1);
+}
+
+static void xgbe_powerdown_tx(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       /* Disable MAC Tx */
+       XGMAC_IOWRITE_BITS(pdata, MAC_TCR, TE, 0);
+
+       /* Disable each Tx DMA channel */
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->tx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_TCR, ST, 0);
+       }
+}
+
+static void xgbe_powerup_rx(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       /* Enable each Rx DMA channel */
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->rx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_RCR, SR, 1);
+       }
+}
+
+static void xgbe_powerdown_rx(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       /* Disable each Rx DMA channel */
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->rx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_RCR, SR, 0);
+       }
+}
+
+static int xgbe_init(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_desc_if *desc_if = &pdata->desc_if;
+       int ret;
+
+       DBGPR("-->xgbe_init\n");
+
+       /* Flush Tx queues */
+       ret = xgbe_flush_tx_queues(pdata);
+       if (ret)
+               return ret;
+
+       /*
+        * Initialize DMA related features
+        */
+       xgbe_config_dma_bus(pdata);
+       xgbe_config_dma_cache(pdata);
+       xgbe_config_osp_mode(pdata);
+       xgbe_config_pblx8(pdata);
+       xgbe_config_tx_pbl_val(pdata);
+       xgbe_config_rx_pbl_val(pdata);
+       xgbe_config_rx_coalesce(pdata);
+       xgbe_config_tx_coalesce(pdata);
+       xgbe_config_rx_buffer_size(pdata);
+       xgbe_config_tso_mode(pdata);
+       desc_if->wrapper_tx_desc_init(pdata);
+       desc_if->wrapper_rx_desc_init(pdata);
+       xgbe_enable_dma_interrupts(pdata);
+
+       /*
+        * Initialize MTL related features
+        */
+       xgbe_config_mtl_mode(pdata);
+       xgbe_config_rx_queue_mapping(pdata);
+       /*TODO: Program the priorities mapped to the Selected Traffic Classes
+               in MTL_TC_Prty_Map0-3 registers */
+       xgbe_config_tsf_mode(pdata, pdata->tx_sf_mode);
+       xgbe_config_rsf_mode(pdata, pdata->rx_sf_mode);
+       xgbe_config_tx_threshold(pdata, pdata->tx_threshold);
+       xgbe_config_rx_threshold(pdata, pdata->rx_threshold);
+       xgbe_config_tx_fifo_size(pdata);
+       xgbe_config_rx_fifo_size(pdata);
+       xgbe_config_flow_control_threshold(pdata);
+       /*TODO: Queue to Traffic Class Mapping (Q2TCMAP) */
+       /*TODO: Error Packet and undersized good Packet forwarding enable
+               (FEP and FUP)
+        */
+       xgbe_enable_mtl_interrupts(pdata);
+
+       /* Transmit Class Weight */
+       XGMAC_IOWRITE_BITS(pdata, MTL_Q_TCQWR, QW, 0x10);
+
+       /*
+        * Initialize MAC related features
+        */
+       xgbe_config_mac_address(pdata);
+       xgbe_config_jumbo_enable(pdata);
+       xgbe_config_flow_control(pdata);
+       xgbe_config_checksum_offload(pdata);
+       xgbe_config_vlan_support(pdata);
+       xgbe_config_mmc(pdata);
+       xgbe_enable_mac_interrupts(pdata);
+
+       DBGPR("<--xgbe_init\n");
+
+       return 0;
+}
+
+void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *hw_if)
+{
+       DBGPR("-->xgbe_init_function_ptrs\n");
+
+       hw_if->tx_complete = xgbe_tx_complete;
+
+       hw_if->set_promiscuous_mode = xgbe_set_promiscuous_mode;
+       hw_if->set_all_multicast_mode = xgbe_set_all_multicast_mode;
+       hw_if->set_addn_mac_addrs = xgbe_set_addn_mac_addrs;
+       hw_if->set_mac_address = xgbe_set_mac_address;
+
+       hw_if->enable_rx_csum = xgbe_enable_rx_csum;
+       hw_if->disable_rx_csum = xgbe_disable_rx_csum;
+
+       hw_if->enable_rx_vlan_stripping = xgbe_enable_rx_vlan_stripping;
+       hw_if->disable_rx_vlan_stripping = xgbe_disable_rx_vlan_stripping;
+
+       hw_if->read_mmd_regs = xgbe_read_mmd_regs;
+       hw_if->write_mmd_regs = xgbe_write_mmd_regs;
+
+       hw_if->set_gmii_speed = xgbe_set_gmii_speed;
+       hw_if->set_gmii_2500_speed = xgbe_set_gmii_2500_speed;
+       hw_if->set_xgmii_speed = xgbe_set_xgmii_speed;
+
+       hw_if->enable_tx = xgbe_enable_tx;
+       hw_if->disable_tx = xgbe_disable_tx;
+       hw_if->enable_rx = xgbe_enable_rx;
+       hw_if->disable_rx = xgbe_disable_rx;
+
+       hw_if->powerup_tx = xgbe_powerup_tx;
+       hw_if->powerdown_tx = xgbe_powerdown_tx;
+       hw_if->powerup_rx = xgbe_powerup_rx;
+       hw_if->powerdown_rx = xgbe_powerdown_rx;
+
+       hw_if->pre_xmit = xgbe_pre_xmit;
+       hw_if->dev_read = xgbe_dev_read;
+       hw_if->enable_int = xgbe_enable_int;
+       hw_if->disable_int = xgbe_disable_int;
+       hw_if->init = xgbe_init;
+       hw_if->exit = xgbe_exit;
+
+       /* Descriptor related Sequences have to be initialized here */
+       hw_if->tx_desc_init = xgbe_tx_desc_init;
+       hw_if->rx_desc_init = xgbe_rx_desc_init;
+       hw_if->tx_desc_reset = xgbe_tx_desc_reset;
+       hw_if->rx_desc_reset = xgbe_rx_desc_reset;
+       hw_if->is_last_desc = xgbe_is_last_desc;
+       hw_if->is_context_desc = xgbe_is_context_desc;
+
+       /* For FLOW ctrl */
+       hw_if->config_tx_flow_control = xgbe_config_tx_flow_control;
+       hw_if->config_rx_flow_control = xgbe_config_rx_flow_control;
+
+       /* For RX coalescing */
+       hw_if->config_rx_coalesce = xgbe_config_rx_coalesce;
+       hw_if->config_tx_coalesce = xgbe_config_tx_coalesce;
+       hw_if->usec_to_riwt = xgbe_usec_to_riwt;
+       hw_if->riwt_to_usec = xgbe_riwt_to_usec;
+
+       /* For RX and TX threshold config */
+       hw_if->config_rx_threshold = xgbe_config_rx_threshold;
+       hw_if->config_tx_threshold = xgbe_config_tx_threshold;
+
+       /* For RX and TX Store and Forward Mode config */
+       hw_if->config_rsf_mode = xgbe_config_rsf_mode;
+       hw_if->config_tsf_mode = xgbe_config_tsf_mode;
+
+       /* For TX DMA Operating on Second Frame config */
+       hw_if->config_osp_mode = xgbe_config_osp_mode;
+
+       /* For RX and TX PBL config */
+       hw_if->config_rx_pbl_val = xgbe_config_rx_pbl_val;
+       hw_if->get_rx_pbl_val = xgbe_get_rx_pbl_val;
+       hw_if->config_tx_pbl_val = xgbe_config_tx_pbl_val;
+       hw_if->get_tx_pbl_val = xgbe_get_tx_pbl_val;
+       hw_if->config_pblx8 = xgbe_config_pblx8;
+
+       /* For MMC statistics support */
+       hw_if->tx_mmc_int = xgbe_tx_mmc_int;
+       hw_if->rx_mmc_int = xgbe_rx_mmc_int;
+       hw_if->read_mmc_stats = xgbe_read_mmc_stats;
+
+       DBGPR("<--xgbe_init_function_ptrs\n");
+}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
new file mode 100644 (file)
index 0000000..cfe3d93
--- /dev/null
@@ -0,0 +1,1351 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute 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 file 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/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and 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.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Advanced Micro Devices, Inc. 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 <COPYRIGHT HOLDER> 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.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and 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.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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/spinlock.h>
+#include <linux/tcp.h>
+#include <linux/if_vlan.h>
+#include <linux/phy.h>
+#include <net/busy_poll.h>
+#include <linux/clk.h>
+#include <linux/if_ether.h>
+
+#include "xgbe.h"
+#include "xgbe-common.h"
+
+
+static int xgbe_poll(struct napi_struct *, int);
+static void xgbe_set_rx_mode(struct net_device *);
+
+static inline unsigned int xgbe_tx_avail_desc(struct xgbe_ring *ring)
+{
+       return (ring->rdesc_count - (ring->cur - ring->dirty));
+}
+
+static int xgbe_calc_rx_buf_size(struct net_device *netdev, unsigned int mtu)
+{
+       unsigned int rx_buf_size;
+
+       if (mtu > XGMAC_JUMBO_PACKET_MTU) {
+               netdev_alert(netdev, "MTU exceeds maximum supported value\n");
+               return -EINVAL;
+       }
+
+       rx_buf_size = mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+       if (rx_buf_size < RX_MIN_BUF_SIZE)
+               rx_buf_size = RX_MIN_BUF_SIZE;
+       rx_buf_size = (rx_buf_size + RX_BUF_ALIGN - 1) & ~(RX_BUF_ALIGN - 1);
+
+       return rx_buf_size;
+}
+
+static void xgbe_enable_rx_tx_ints(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (channel->tx_ring)
+                       hw_if->enable_int(channel,
+                                         XGMAC_INT_DMA_CH_SR_TI);
+               if (channel->rx_ring)
+                       hw_if->enable_int(channel,
+                                         XGMAC_INT_DMA_CH_SR_RI);
+       }
+}
+
+static void xgbe_disable_rx_tx_ints(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (channel->tx_ring)
+                       hw_if->disable_int(channel,
+                                          XGMAC_INT_DMA_CH_SR_TI);
+               if (channel->rx_ring)
+                       hw_if->disable_int(channel,
+                                          XGMAC_INT_DMA_CH_SR_RI);
+       }
+}
+
+static irqreturn_t xgbe_isr(int irq, void *data)
+{
+       struct xgbe_prv_data *pdata = data;
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_channel *channel;
+       unsigned int dma_isr, dma_ch_isr;
+       unsigned int mac_isr;
+       unsigned int i;
+
+       /* The DMA interrupt status register also reports MAC and MTL
+        * interrupts. So for polling mode, we just need to check for
+        * this register to be non-zero
+        */
+       dma_isr = XGMAC_IOREAD(pdata, DMA_ISR);
+       if (!dma_isr)
+               goto isr_done;
+
+       DBGPR("-->xgbe_isr\n");
+
+       DBGPR("  DMA_ISR = %08x\n", dma_isr);
+       DBGPR("  DMA_DS0 = %08x\n", XGMAC_IOREAD(pdata, DMA_DSR0));
+       DBGPR("  DMA_DS1 = %08x\n", XGMAC_IOREAD(pdata, DMA_DSR1));
+
+       for (i = 0; i < pdata->channel_count; i++) {
+               if (!(dma_isr & (1 << i)))
+                       continue;
+
+               channel = pdata->channel + i;
+
+               dma_ch_isr = XGMAC_DMA_IOREAD(channel, DMA_CH_SR);
+               DBGPR("  DMA_CH%u_ISR = %08x\n", i, dma_ch_isr);
+
+               if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, TI) ||
+                   XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, RI)) {
+                       if (napi_schedule_prep(&pdata->napi)) {
+                               /* Disable Tx and Rx interrupts */
+                               xgbe_disable_rx_tx_ints(pdata);
+
+                               /* Turn on polling */
+                               __napi_schedule(&pdata->napi);
+                       }
+               }
+
+               /* Restart the device on a Fatal Bus Error */
+               if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, FBE))
+                       schedule_work(&pdata->restart_work);
+
+               /* Clear all interrupt signals */
+               XGMAC_DMA_IOWRITE(channel, DMA_CH_SR, dma_ch_isr);
+       }
+
+       if (XGMAC_GET_BITS(dma_isr, DMA_ISR, MACIS)) {
+               mac_isr = XGMAC_IOREAD(pdata, MAC_ISR);
+
+               if (XGMAC_GET_BITS(mac_isr, MAC_ISR, MMCTXIS))
+                       hw_if->tx_mmc_int(pdata);
+
+               if (XGMAC_GET_BITS(mac_isr, MAC_ISR, MMCRXIS))
+                       hw_if->rx_mmc_int(pdata);
+       }
+
+       DBGPR("  DMA_ISR = %08x\n", XGMAC_IOREAD(pdata, DMA_ISR));
+
+       DBGPR("<--xgbe_isr\n");
+
+isr_done:
+       return IRQ_HANDLED;
+}
+
+static enum hrtimer_restart xgbe_tx_timer(struct hrtimer *timer)
+{
+       struct xgbe_channel *channel = container_of(timer,
+                                                   struct xgbe_channel,
+                                                   tx_timer);
+       struct xgbe_ring *ring = channel->tx_ring;
+       struct xgbe_prv_data *pdata = channel->pdata;
+       unsigned long flags;
+
+       DBGPR("-->xgbe_tx_timer\n");
+
+       spin_lock_irqsave(&ring->lock, flags);
+
+       if (napi_schedule_prep(&pdata->napi)) {
+               /* Disable Tx and Rx interrupts */
+               xgbe_disable_rx_tx_ints(pdata);
+
+               /* Turn on polling */
+               __napi_schedule(&pdata->napi);
+       }
+
+       channel->tx_timer_active = 0;
+
+       spin_unlock_irqrestore(&ring->lock, flags);
+
+       DBGPR("<--xgbe_tx_timer\n");
+
+       return HRTIMER_NORESTART;
+}
+
+static void xgbe_init_tx_timers(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       DBGPR("-->xgbe_init_tx_timers\n");
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->tx_ring)
+                       break;
+
+               DBGPR("  %s adding tx timer\n", channel->name);
+               hrtimer_init(&channel->tx_timer, CLOCK_MONOTONIC,
+                            HRTIMER_MODE_REL);
+               channel->tx_timer.function = xgbe_tx_timer;
+       }
+
+       DBGPR("<--xgbe_init_tx_timers\n");
+}
+
+static void xgbe_stop_tx_timers(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       DBGPR("-->xgbe_stop_tx_timers\n");
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->tx_ring)
+                       break;
+
+               DBGPR("  %s deleting tx timer\n", channel->name);
+               channel->tx_timer_active = 0;
+               hrtimer_cancel(&channel->tx_timer);
+       }
+
+       DBGPR("<--xgbe_stop_tx_timers\n");
+}
+
+void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata)
+{
+       unsigned int mac_hfr0, mac_hfr1, mac_hfr2;
+       struct xgbe_hw_features *hw_feat = &pdata->hw_feat;
+
+       DBGPR("-->xgbe_get_all_hw_features\n");
+
+       mac_hfr0 = XGMAC_IOREAD(pdata, MAC_HWF0R);
+       mac_hfr1 = XGMAC_IOREAD(pdata, MAC_HWF1R);
+       mac_hfr2 = XGMAC_IOREAD(pdata, MAC_HWF2R);
+
+       memset(hw_feat, 0, sizeof(*hw_feat));
+
+       /* Hardware feature register 0 */
+       hw_feat->gmii        = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, GMIISEL);
+       hw_feat->vlhash      = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, VLHASH);
+       hw_feat->sma         = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, SMASEL);
+       hw_feat->rwk         = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, RWKSEL);
+       hw_feat->mgk         = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, MGKSEL);
+       hw_feat->mmc         = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, MMCSEL);
+       hw_feat->aoe         = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, ARPOFFSEL);
+       hw_feat->ts          = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, TSSEL);
+       hw_feat->eee         = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, EEESEL);
+       hw_feat->tx_coe      = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, TXCOESEL);
+       hw_feat->rx_coe      = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, RXCOESEL);
+       hw_feat->addn_mac    = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R,
+                                             ADDMACADRSEL);
+       hw_feat->ts_src      = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, TSSTSSEL);
+       hw_feat->sa_vlan_ins = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, SAVLANINS);
+
+       /* Hardware feature register 1 */
+       hw_feat->rx_fifo_size  = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R,
+                                               RXFIFOSIZE);
+       hw_feat->tx_fifo_size  = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R,
+                                               TXFIFOSIZE);
+       hw_feat->dcb           = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, DCBEN);
+       hw_feat->sph           = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, SPHEN);
+       hw_feat->tso           = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, TSOEN);
+       hw_feat->dma_debug     = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, DBGMEMA);
+       hw_feat->hash_table_size = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R,
+                                                 HASHTBLSZ);
+       hw_feat->l3l4_filter_num = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R,
+                                                 L3L4FNUM);
+
+       /* Hardware feature register 2 */
+       hw_feat->rx_q_cnt     = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, RXQCNT);
+       hw_feat->tx_q_cnt     = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, TXQCNT);
+       hw_feat->rx_ch_cnt    = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, RXCHCNT);
+       hw_feat->tx_ch_cnt    = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, TXCHCNT);
+       hw_feat->pps_out_num  = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, PPSOUTNUM);
+       hw_feat->aux_snap_num = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, AUXSNAPNUM);
+
+       /* The Queue and Channel counts are zero based so increment them
+        * to get the actual number
+        */
+       hw_feat->rx_q_cnt++;
+       hw_feat->tx_q_cnt++;
+       hw_feat->rx_ch_cnt++;
+       hw_feat->tx_ch_cnt++;
+
+       DBGPR("<--xgbe_get_all_hw_features\n");
+}
+
+static void xgbe_napi_enable(struct xgbe_prv_data *pdata, unsigned int add)
+{
+       if (add)
+               netif_napi_add(pdata->netdev, &pdata->napi, xgbe_poll,
+                              NAPI_POLL_WEIGHT);
+       napi_enable(&pdata->napi);
+}
+
+static void xgbe_napi_disable(struct xgbe_prv_data *pdata)
+{
+       napi_disable(&pdata->napi);
+}
+
+void xgbe_init_tx_coalesce(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+
+       DBGPR("-->xgbe_init_tx_coalesce\n");
+
+       pdata->tx_usecs = XGMAC_INIT_DMA_TX_USECS;
+       pdata->tx_frames = XGMAC_INIT_DMA_TX_FRAMES;
+
+       hw_if->config_tx_coalesce(pdata);
+
+       DBGPR("<--xgbe_init_tx_coalesce\n");
+}
+
+void xgbe_init_rx_coalesce(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+
+       DBGPR("-->xgbe_init_rx_coalesce\n");
+
+       pdata->rx_riwt = hw_if->usec_to_riwt(pdata, XGMAC_INIT_DMA_RX_USECS);
+       pdata->rx_frames = XGMAC_INIT_DMA_RX_FRAMES;
+
+       hw_if->config_rx_coalesce(pdata);
+
+       DBGPR("<--xgbe_init_rx_coalesce\n");
+}
+
+static void xgbe_free_tx_skbuff(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_desc_if *desc_if = &pdata->desc_if;
+       struct xgbe_channel *channel;
+       struct xgbe_ring *ring;
+       struct xgbe_ring_data *rdata;
+       unsigned int i, j;
+
+       DBGPR("-->xgbe_free_tx_skbuff\n");
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               ring = channel->tx_ring;
+               if (!ring)
+                       break;
+
+               for (j = 0; j < ring->rdesc_count; j++) {
+                       rdata = GET_DESC_DATA(ring, j);
+                       desc_if->unmap_skb(pdata, rdata);
+               }
+       }
+
+       DBGPR("<--xgbe_free_tx_skbuff\n");
+}
+
+static void xgbe_free_rx_skbuff(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_desc_if *desc_if = &pdata->desc_if;
+       struct xgbe_channel *channel;
+       struct xgbe_ring *ring;
+       struct xgbe_ring_data *rdata;
+       unsigned int i, j;
+
+       DBGPR("-->xgbe_free_rx_skbuff\n");
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               ring = channel->rx_ring;
+               if (!ring)
+                       break;
+
+               for (j = 0; j < ring->rdesc_count; j++) {
+                       rdata = GET_DESC_DATA(ring, j);
+                       desc_if->unmap_skb(pdata, rdata);
+               }
+       }
+
+       DBGPR("<--xgbe_free_rx_skbuff\n");
+}
+
+int xgbe_powerdown(struct net_device *netdev, unsigned int caller)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       unsigned long flags;
+
+       DBGPR("-->xgbe_powerdown\n");
+
+       if (!netif_running(netdev) ||
+           (caller == XGMAC_IOCTL_CONTEXT && pdata->power_down)) {
+               netdev_alert(netdev, "Device is already powered down\n");
+               DBGPR("<--xgbe_powerdown\n");
+               return -EINVAL;
+       }
+
+       phy_stop(pdata->phydev);
+
+       spin_lock_irqsave(&pdata->lock, flags);
+
+       if (caller == XGMAC_DRIVER_CONTEXT)
+               netif_device_detach(netdev);
+
+       netif_tx_stop_all_queues(netdev);
+       xgbe_napi_disable(pdata);
+
+       /* Powerdown Tx/Rx */
+       hw_if->powerdown_tx(pdata);
+       hw_if->powerdown_rx(pdata);
+
+       pdata->power_down = 1;
+
+       spin_unlock_irqrestore(&pdata->lock, flags);
+
+       DBGPR("<--xgbe_powerdown\n");
+
+       return 0;
+}
+
+int xgbe_powerup(struct net_device *netdev, unsigned int caller)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       unsigned long flags;
+
+       DBGPR("-->xgbe_powerup\n");
+
+       if (!netif_running(netdev) ||
+           (caller == XGMAC_IOCTL_CONTEXT && !pdata->power_down)) {
+               netdev_alert(netdev, "Device is already powered up\n");
+               DBGPR("<--xgbe_powerup\n");
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&pdata->lock, flags);
+
+       pdata->power_down = 0;
+
+       phy_start(pdata->phydev);
+
+       /* Enable Tx/Rx */
+       hw_if->powerup_tx(pdata);
+       hw_if->powerup_rx(pdata);
+
+       if (caller == XGMAC_DRIVER_CONTEXT)
+               netif_device_attach(netdev);
+
+       xgbe_napi_enable(pdata, 0);
+       netif_tx_start_all_queues(netdev);
+
+       spin_unlock_irqrestore(&pdata->lock, flags);
+
+       DBGPR("<--xgbe_powerup\n");
+
+       return 0;
+}
+
+static int xgbe_start(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct net_device *netdev = pdata->netdev;
+
+       DBGPR("-->xgbe_start\n");
+
+       xgbe_set_rx_mode(netdev);
+
+       hw_if->init(pdata);
+
+       phy_start(pdata->phydev);
+
+       hw_if->enable_tx(pdata);
+       hw_if->enable_rx(pdata);
+
+       xgbe_init_tx_timers(pdata);
+
+       xgbe_napi_enable(pdata, 1);
+       netif_tx_start_all_queues(netdev);
+
+       DBGPR("<--xgbe_start\n");
+
+       return 0;
+}
+
+static void xgbe_stop(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct net_device *netdev = pdata->netdev;
+
+       DBGPR("-->xgbe_stop\n");
+
+       phy_stop(pdata->phydev);
+
+       netif_tx_stop_all_queues(netdev);
+       xgbe_napi_disable(pdata);
+
+       xgbe_stop_tx_timers(pdata);
+
+       hw_if->disable_tx(pdata);
+       hw_if->disable_rx(pdata);
+
+       DBGPR("<--xgbe_stop\n");
+}
+
+static void xgbe_restart_dev(struct xgbe_prv_data *pdata, unsigned int reset)
+{
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+
+       DBGPR("-->xgbe_restart_dev\n");
+
+       /* If not running, "restart" will happen on open */
+       if (!netif_running(pdata->netdev))
+               return;
+
+       xgbe_stop(pdata);
+       synchronize_irq(pdata->irq_number);
+
+       xgbe_free_tx_skbuff(pdata);
+       xgbe_free_rx_skbuff(pdata);
+
+       /* Issue software reset to device if requested */
+       if (reset)
+               hw_if->exit(pdata);
+
+       xgbe_start(pdata);
+
+       DBGPR("<--xgbe_restart_dev\n");
+}
+
+static void xgbe_restart(struct work_struct *work)
+{
+       struct xgbe_prv_data *pdata = container_of(work,
+                                                  struct xgbe_prv_data,
+                                                  restart_work);
+
+       rtnl_lock();
+
+       xgbe_restart_dev(pdata, 1);
+
+       rtnl_unlock();
+}
+
+static void xgbe_prep_vlan(struct sk_buff *skb, struct xgbe_packet_data *packet)
+{
+       if (vlan_tx_tag_present(skb))
+               packet->vlan_ctag = vlan_tx_tag_get(skb);
+}
+
+static int xgbe_prep_tso(struct sk_buff *skb, struct xgbe_packet_data *packet)
+{
+       int ret;
+
+       if (!XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                           TSO_ENABLE))
+               return 0;
+
+       ret = skb_cow_head(skb, 0);
+       if (ret)
+               return ret;
+
+       packet->header_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+       packet->tcp_header_len = tcp_hdrlen(skb);
+       packet->tcp_payload_len = skb->len - packet->header_len;
+       packet->mss = skb_shinfo(skb)->gso_size;
+       DBGPR("  packet->header_len=%u\n", packet->header_len);
+       DBGPR("  packet->tcp_header_len=%u, packet->tcp_payload_len=%u\n",
+             packet->tcp_header_len, packet->tcp_payload_len);
+       DBGPR("  packet->mss=%u\n", packet->mss);
+
+       return 0;
+}
+
+static int xgbe_is_tso(struct sk_buff *skb)
+{
+       if (skb->ip_summed != CHECKSUM_PARTIAL)
+               return 0;
+
+       if (!skb_is_gso(skb))
+               return 0;
+
+       DBGPR("  TSO packet to be processed\n");
+
+       return 1;
+}
+
+static void xgbe_packet_info(struct xgbe_ring *ring, struct sk_buff *skb,
+                            struct xgbe_packet_data *packet)
+{
+       struct skb_frag_struct *frag;
+       unsigned int context_desc;
+       unsigned int len;
+       unsigned int i;
+
+       context_desc = 0;
+       packet->rdesc_count = 0;
+
+       if (xgbe_is_tso(skb)) {
+               /* TSO requires an extra desriptor if mss is different */
+               if (skb_shinfo(skb)->gso_size != ring->tx.cur_mss) {
+                       context_desc = 1;
+                       packet->rdesc_count++;
+               }
+
+               /* TSO requires an extra desriptor for TSO header */
+               packet->rdesc_count++;
+
+               XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                              TSO_ENABLE, 1);
+               XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                              CSUM_ENABLE, 1);
+       } else if (skb->ip_summed == CHECKSUM_PARTIAL)
+               XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                              CSUM_ENABLE, 1);
+
+       if (vlan_tx_tag_present(skb)) {
+               /* VLAN requires an extra descriptor if tag is different */
+               if (vlan_tx_tag_get(skb) != ring->tx.cur_vlan_ctag)
+                       /* We can share with the TSO context descriptor */
+                       if (!context_desc) {
+                               context_desc = 1;
+                               packet->rdesc_count++;
+                       }
+
+               XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                              VLAN_CTAG, 1);
+       }
+
+       for (len = skb_headlen(skb); len;) {
+               packet->rdesc_count++;
+               len -= min_t(unsigned int, len, TX_MAX_BUF_SIZE);
+       }
+
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+               frag = &skb_shinfo(skb)->frags[i];
+               for (len = skb_frag_size(frag); len; ) {
+                       packet->rdesc_count++;
+                       len -= min_t(unsigned int, len, TX_MAX_BUF_SIZE);
+               }
+       }
+}
+
+static int xgbe_open(struct net_device *netdev)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_desc_if *desc_if = &pdata->desc_if;
+       int ret;
+
+       DBGPR("-->xgbe_open\n");
+
+       /* Enable the clock */
+       ret = clk_prepare_enable(pdata->sysclock);
+       if (ret) {
+               netdev_alert(netdev, "clk_prepare_enable failed\n");
+               return ret;
+       }
+
+       /* Calculate the Rx buffer size before allocating rings */
+       ret = xgbe_calc_rx_buf_size(netdev, netdev->mtu);
+       if (ret < 0)
+               goto err_clk;
+       pdata->rx_buf_size = ret;
+
+       /* Allocate the ring descriptors and buffers */
+       ret = desc_if->alloc_ring_resources(pdata);
+       if (ret)
+               goto err_clk;
+
+       /* Initialize the device restart work struct */
+       INIT_WORK(&pdata->restart_work, xgbe_restart);
+
+       /* Request interrupts */
+       ret = devm_request_irq(pdata->dev, netdev->irq, xgbe_isr, 0,
+                              netdev->name, pdata);
+       if (ret) {
+               netdev_alert(netdev, "error requesting irq %d\n",
+                            pdata->irq_number);
+               goto err_irq;
+       }
+       pdata->irq_number = netdev->irq;
+
+       ret = xgbe_start(pdata);
+       if (ret)
+               goto err_start;
+
+       DBGPR("<--xgbe_open\n");
+
+       return 0;
+
+err_start:
+       hw_if->exit(pdata);
+
+       devm_free_irq(pdata->dev, pdata->irq_number, pdata);
+       pdata->irq_number = 0;
+
+err_irq:
+       desc_if->free_ring_resources(pdata);
+
+err_clk:
+       clk_disable_unprepare(pdata->sysclock);
+
+       return ret;
+}
+
+static int xgbe_close(struct net_device *netdev)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_desc_if *desc_if = &pdata->desc_if;
+
+       DBGPR("-->xgbe_close\n");
+
+       /* Stop the device */
+       xgbe_stop(pdata);
+
+       /* Issue software reset to device */
+       hw_if->exit(pdata);
+
+       /* Free all the ring data */
+       desc_if->free_ring_resources(pdata);
+
+       /* Release the interrupt */
+       if (pdata->irq_number != 0) {
+               devm_free_irq(pdata->dev, pdata->irq_number, pdata);
+               pdata->irq_number = 0;
+       }
+
+       /* Disable the clock */
+       clk_disable_unprepare(pdata->sysclock);
+
+       DBGPR("<--xgbe_close\n");
+
+       return 0;
+}
+
+static int xgbe_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_desc_if *desc_if = &pdata->desc_if;
+       struct xgbe_channel *channel;
+       struct xgbe_ring *ring;
+       struct xgbe_packet_data *packet;
+       unsigned long flags;
+       int ret;
+
+       DBGPR("-->xgbe_xmit: skb->len = %d\n", skb->len);
+
+       channel = pdata->channel + skb->queue_mapping;
+       ring = channel->tx_ring;
+       packet = &ring->packet_data;
+
+       ret = NETDEV_TX_OK;
+
+       spin_lock_irqsave(&ring->lock, flags);
+
+       if (skb->len == 0) {
+               netdev_err(netdev, "empty skb received from stack\n");
+               dev_kfree_skb_any(skb);
+               goto tx_netdev_return;
+       }
+
+       /* Calculate preliminary packet info */
+       memset(packet, 0, sizeof(*packet));
+       xgbe_packet_info(ring, skb, packet);
+
+       /* Check that there are enough descriptors available */
+       if (packet->rdesc_count > xgbe_tx_avail_desc(ring)) {
+               DBGPR("  Tx queue stopped, not enough descriptors available\n");
+               netif_stop_subqueue(netdev, channel->queue_index);
+               ring->tx.queue_stopped = 1;
+               ret = NETDEV_TX_BUSY;
+               goto tx_netdev_return;
+       }
+
+       ret = xgbe_prep_tso(skb, packet);
+       if (ret) {
+               netdev_err(netdev, "error processing TSO packet\n");
+               dev_kfree_skb_any(skb);
+               goto tx_netdev_return;
+       }
+       xgbe_prep_vlan(skb, packet);
+
+       if (!desc_if->map_tx_skb(channel, skb)) {
+               dev_kfree_skb_any(skb);
+               goto tx_netdev_return;
+       }
+
+       /* Configure required descriptor fields for transmission */
+       hw_if->pre_xmit(channel);
+
+#ifdef XGMAC_ENABLE_TX_PKT_DUMP
+       xgbe_print_pkt(netdev, skb, true);
+#endif
+
+tx_netdev_return:
+       spin_unlock_irqrestore(&ring->lock, flags);
+
+       DBGPR("<--xgbe_xmit\n");
+
+       return ret;
+}
+
+static void xgbe_set_rx_mode(struct net_device *netdev)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       unsigned int pr_mode, am_mode;
+
+       DBGPR("-->xgbe_set_rx_mode\n");
+
+       pr_mode = ((netdev->flags & IFF_PROMISC) != 0);
+       am_mode = ((netdev->flags & IFF_ALLMULTI) != 0);
+
+       if (netdev_uc_count(netdev) > pdata->hw_feat.addn_mac)
+               pr_mode = 1;
+       if (netdev_mc_count(netdev) > pdata->hw_feat.addn_mac)
+               am_mode = 1;
+       if ((netdev_uc_count(netdev) + netdev_mc_count(netdev)) >
+            pdata->hw_feat.addn_mac)
+               pr_mode = 1;
+
+       hw_if->set_promiscuous_mode(pdata, pr_mode);
+       hw_if->set_all_multicast_mode(pdata, am_mode);
+       if (!pr_mode)
+               hw_if->set_addn_mac_addrs(pdata, am_mode);
+
+       DBGPR("<--xgbe_set_rx_mode\n");
+}
+
+static int xgbe_set_mac_address(struct net_device *netdev, void *addr)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct sockaddr *saddr = addr;
+
+       DBGPR("-->xgbe_set_mac_address\n");
+
+       if (!is_valid_ether_addr(saddr->sa_data))
+               return -EADDRNOTAVAIL;
+
+       memcpy(netdev->dev_addr, saddr->sa_data, netdev->addr_len);
+
+       hw_if->set_mac_address(pdata, netdev->dev_addr);
+
+       DBGPR("<--xgbe_set_mac_address\n");
+
+       return 0;
+}
+
+static int xgbe_change_mtu(struct net_device *netdev, int mtu)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       int ret;
+
+       DBGPR("-->xgbe_change_mtu\n");
+
+       ret = xgbe_calc_rx_buf_size(netdev, mtu);
+       if (ret < 0)
+               return ret;
+
+       pdata->rx_buf_size = ret;
+       netdev->mtu = mtu;
+
+       xgbe_restart_dev(pdata, 0);
+
+       DBGPR("<--xgbe_change_mtu\n");
+
+       return 0;
+}
+
+static struct rtnl_link_stats64 *xgbe_get_stats64(struct net_device *netdev,
+                                                 struct rtnl_link_stats64 *s)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_mmc_stats *pstats = &pdata->mmc_stats;
+
+       DBGPR("-->%s\n", __func__);
+
+       pdata->hw_if.read_mmc_stats(pdata);
+
+       s->rx_packets = pstats->rxframecount_gb;
+       s->rx_bytes = pstats->rxoctetcount_gb;
+       s->rx_errors = pstats->rxframecount_gb -
+                      pstats->rxbroadcastframes_g -
+                      pstats->rxmulticastframes_g -
+                      pstats->rxunicastframes_g;
+       s->multicast = pstats->rxmulticastframes_g;
+       s->rx_length_errors = pstats->rxlengtherror;
+       s->rx_crc_errors = pstats->rxcrcerror;
+       s->rx_fifo_errors = pstats->rxfifooverflow;
+
+       s->tx_packets = pstats->txframecount_gb;
+       s->tx_bytes = pstats->txoctetcount_gb;
+       s->tx_errors = pstats->txframecount_gb - pstats->txframecount_g;
+       s->tx_dropped = netdev->stats.tx_dropped;
+
+       DBGPR("<--%s\n", __func__);
+
+       return s;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void xgbe_poll_controller(struct net_device *netdev)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+
+       DBGPR("-->xgbe_poll_controller\n");
+
+       disable_irq(pdata->irq_number);
+
+       xgbe_isr(pdata->irq_number, pdata);
+
+       enable_irq(pdata->irq_number);
+
+       DBGPR("<--xgbe_poll_controller\n");
+}
+#endif /* End CONFIG_NET_POLL_CONTROLLER */
+
+static int xgbe_set_features(struct net_device *netdev,
+                            netdev_features_t features)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       unsigned int rxcsum_enabled, rxvlan_enabled;
+
+       rxcsum_enabled = !!(pdata->netdev_features & NETIF_F_RXCSUM);
+       rxvlan_enabled = !!(pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_RX);
+
+       if ((features & NETIF_F_RXCSUM) && !rxcsum_enabled) {
+               hw_if->enable_rx_csum(pdata);
+               netdev_alert(netdev, "state change - rxcsum enabled\n");
+       } else if (!(features & NETIF_F_RXCSUM) && rxcsum_enabled) {
+               hw_if->disable_rx_csum(pdata);
+               netdev_alert(netdev, "state change - rxcsum disabled\n");
+       }
+
+       if ((features & NETIF_F_HW_VLAN_CTAG_RX) && !rxvlan_enabled) {
+               hw_if->enable_rx_vlan_stripping(pdata);
+               netdev_alert(netdev, "state change - rxvlan enabled\n");
+       } else if (!(features & NETIF_F_HW_VLAN_CTAG_RX) && rxvlan_enabled) {
+               hw_if->disable_rx_vlan_stripping(pdata);
+               netdev_alert(netdev, "state change - rxvlan disabled\n");
+       }
+
+       pdata->netdev_features = features;
+
+       DBGPR("<--xgbe_set_features\n");
+
+       return 0;
+}
+
+static const struct net_device_ops xgbe_netdev_ops = {
+       .ndo_open               = xgbe_open,
+       .ndo_stop               = xgbe_close,
+       .ndo_start_xmit         = xgbe_xmit,
+       .ndo_set_rx_mode        = xgbe_set_rx_mode,
+       .ndo_set_mac_address    = xgbe_set_mac_address,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_change_mtu         = xgbe_change_mtu,
+       .ndo_get_stats64        = xgbe_get_stats64,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = xgbe_poll_controller,
+#endif
+       .ndo_set_features       = xgbe_set_features,
+};
+
+struct net_device_ops *xgbe_get_netdev_ops(void)
+{
+       return (struct net_device_ops *)&xgbe_netdev_ops;
+}
+
+static int xgbe_tx_poll(struct xgbe_channel *channel)
+{
+       struct xgbe_prv_data *pdata = channel->pdata;
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_desc_if *desc_if = &pdata->desc_if;
+       struct xgbe_ring *ring = channel->tx_ring;
+       struct xgbe_ring_data *rdata;
+       struct xgbe_ring_desc *rdesc;
+       struct net_device *netdev = pdata->netdev;
+       unsigned long flags;
+       int processed = 0;
+
+       DBGPR("-->xgbe_tx_poll\n");
+
+       /* Nothing to do if there isn't a Tx ring for this channel */
+       if (!ring)
+               return 0;
+
+       spin_lock_irqsave(&ring->lock, flags);
+
+       while ((processed < TX_DESC_MAX_PROC) && (ring->dirty < ring->cur)) {
+               rdata = GET_DESC_DATA(ring, ring->dirty);
+               rdesc = rdata->rdesc;
+
+               if (!hw_if->tx_complete(rdesc))
+                       break;
+
+#ifdef XGMAC_ENABLE_TX_DESC_DUMP
+               xgbe_dump_tx_desc(ring, ring->dirty, 1, 0);
+#endif
+
+               /* Free the SKB and reset the descriptor for re-use */
+               desc_if->unmap_skb(pdata, rdata);
+               hw_if->tx_desc_reset(rdata);
+
+               processed++;
+               ring->dirty++;
+       }
+
+       if ((ring->tx.queue_stopped == 1) &&
+           (xgbe_tx_avail_desc(ring) > TX_DESC_MIN_FREE)) {
+               ring->tx.queue_stopped = 0;
+               netif_wake_subqueue(netdev, channel->queue_index);
+       }
+
+       DBGPR("<--xgbe_tx_poll: processed=%d\n", processed);
+
+       spin_unlock_irqrestore(&ring->lock, flags);
+
+       return processed;
+}
+
+static int xgbe_rx_poll(struct xgbe_channel *channel, int budget)
+{
+       struct xgbe_prv_data *pdata = channel->pdata;
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_desc_if *desc_if = &pdata->desc_if;
+       struct xgbe_ring *ring = channel->rx_ring;
+       struct xgbe_ring_data *rdata;
+       struct xgbe_packet_data *packet;
+       struct net_device *netdev = pdata->netdev;
+       struct sk_buff *skb;
+       unsigned int incomplete, error;
+       unsigned int cur_len, put_len, max_len;
+       int received = 0;
+
+       DBGPR("-->xgbe_rx_poll: budget=%d\n", budget);
+
+       /* Nothing to do if there isn't a Rx ring for this channel */
+       if (!ring)
+               return 0;
+
+       packet = &ring->packet_data;
+       while (received < budget) {
+               DBGPR("  cur = %d\n", ring->cur);
+
+               /* Clear the packet data information */
+               memset(packet, 0, sizeof(*packet));
+               skb = NULL;
+               error = 0;
+               cur_len = 0;
+
+read_again:
+               rdata = GET_DESC_DATA(ring, ring->cur);
+
+               if (hw_if->dev_read(channel))
+                       break;
+
+               received++;
+               ring->cur++;
+               ring->dirty++;
+
+               dma_unmap_single(pdata->dev, rdata->skb_dma,
+                                rdata->skb_dma_len, DMA_FROM_DEVICE);
+               rdata->skb_dma = 0;
+
+               incomplete = XGMAC_GET_BITS(packet->attributes,
+                                           RX_PACKET_ATTRIBUTES,
+                                           INCOMPLETE);
+
+               /* Earlier error, just drain the remaining data */
+               if (incomplete && error)
+                       goto read_again;
+
+               if (error || packet->errors) {
+                       if (packet->errors)
+                               DBGPR("Error in received packet\n");
+                       dev_kfree_skb(skb);
+                       continue;
+               }
+
+               put_len = rdata->len - cur_len;
+               if (skb) {
+                       if (pskb_expand_head(skb, 0, put_len, GFP_ATOMIC)) {
+                               DBGPR("pskb_expand_head error\n");
+                               if (incomplete) {
+                                       error = 1;
+                                       goto read_again;
+                               }
+
+                               dev_kfree_skb(skb);
+                               continue;
+                       }
+                       memcpy(skb_tail_pointer(skb), rdata->skb->data,
+                              put_len);
+               } else {
+                       skb = rdata->skb;
+                       rdata->skb = NULL;
+               }
+               skb_put(skb, put_len);
+               cur_len += put_len;
+
+               if (incomplete)
+                       goto read_again;
+
+               /* Be sure we don't exceed the configured MTU */
+               max_len = netdev->mtu + ETH_HLEN;
+               if (!(netdev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
+                   (skb->protocol == htons(ETH_P_8021Q)))
+                       max_len += VLAN_HLEN;
+
+               if (skb->len > max_len) {
+                       DBGPR("packet length exceeds configured MTU\n");
+                       dev_kfree_skb(skb);
+                       continue;
+               }
+
+#ifdef XGMAC_ENABLE_RX_PKT_DUMP
+               xgbe_print_pkt(netdev, skb, false);
+#endif
+
+               skb_checksum_none_assert(skb);
+               if (XGMAC_GET_BITS(packet->attributes,
+                                  RX_PACKET_ATTRIBUTES, CSUM_DONE))
+                       skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+               if (XGMAC_GET_BITS(packet->attributes,
+                                  RX_PACKET_ATTRIBUTES, VLAN_CTAG))
+                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+                                              packet->vlan_ctag);
+
+               skb->dev = netdev;
+               skb->protocol = eth_type_trans(skb, netdev);
+               skb_record_rx_queue(skb, channel->queue_index);
+               skb_mark_napi_id(skb, &pdata->napi);
+
+               netdev->last_rx = jiffies;
+               napi_gro_receive(&pdata->napi, skb);
+       }
+
+       if (received) {
+               desc_if->realloc_skb(channel);
+
+               /* Update the Rx Tail Pointer Register with address of
+                * the last cleaned entry */
+               rdata = GET_DESC_DATA(ring, ring->rx.realloc_index - 1);
+               XGMAC_DMA_IOWRITE(channel, DMA_CH_RDTR_LO,
+                                 lower_32_bits(rdata->rdesc_dma));
+       }
+
+       DBGPR("<--xgbe_rx_poll: received = %d\n", received);
+
+       return received;
+}
+
+static int xgbe_poll(struct napi_struct *napi, int budget)
+{
+       struct xgbe_prv_data *pdata = container_of(napi, struct xgbe_prv_data,
+                                                  napi);
+       struct xgbe_channel *channel;
+       int processed;
+       unsigned int i;
+
+       DBGPR("-->xgbe_poll: budget=%d\n", budget);
+
+       /* Cleanup Tx ring first */
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++)
+               xgbe_tx_poll(channel);
+
+       /* Process Rx ring next */
+       processed = 0;
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++)
+               processed += xgbe_rx_poll(channel, budget - processed);
+
+       /* If we processed everything, we are done */
+       if (processed < budget) {
+               /* Turn off polling */
+               napi_complete(napi);
+
+               /* Enable Tx and Rx interrupts */
+               xgbe_enable_rx_tx_ints(pdata);
+       }
+
+       DBGPR("<--xgbe_poll: received = %d\n", processed);
+
+       return processed;
+}
+
+void xgbe_dump_tx_desc(struct xgbe_ring *ring, unsigned int idx,
+                      unsigned int count, unsigned int flag)
+{
+       struct xgbe_ring_data *rdata;
+       struct xgbe_ring_desc *rdesc;
+
+       while (count--) {
+               rdata = GET_DESC_DATA(ring, idx);
+               rdesc = rdata->rdesc;
+               DBGPR("TX_NORMAL_DESC[%d %s] = %08x:%08x:%08x:%08x\n", idx,
+                     (flag == 1) ? "QUEUED FOR TX" : "TX BY DEVICE",
+                     le32_to_cpu(rdesc->desc0), le32_to_cpu(rdesc->desc1),
+                     le32_to_cpu(rdesc->desc2), le32_to_cpu(rdesc->desc3));
+               idx++;
+       }
+}
+
+void xgbe_dump_rx_desc(struct xgbe_ring *ring, struct xgbe_ring_desc *desc,
+                      unsigned int idx)
+{
+       DBGPR("RX_NORMAL_DESC[%d RX BY DEVICE] = %08x:%08x:%08x:%08x\n", idx,
+             le32_to_cpu(desc->desc0), le32_to_cpu(desc->desc1),
+             le32_to_cpu(desc->desc2), le32_to_cpu(desc->desc3));
+}
+
+void xgbe_print_pkt(struct net_device *netdev, struct sk_buff *skb, bool tx_rx)
+{
+       struct ethhdr *eth = (struct ethhdr *)skb->data;
+       unsigned char *buf = skb->data;
+       unsigned char buffer[128];
+       unsigned int i, j;
+
+       netdev_alert(netdev, "\n************** SKB dump ****************\n");
+
+       netdev_alert(netdev, "%s packet of %d bytes\n",
+                    (tx_rx ? "TX" : "RX"), skb->len);
+
+       netdev_alert(netdev, "Dst MAC addr: %pM\n", eth->h_dest);
+       netdev_alert(netdev, "Src MAC addr: %pM\n", eth->h_source);
+       netdev_alert(netdev, "Protocol: 0x%04hx\n", ntohs(eth->h_proto));
+
+       for (i = 0, j = 0; i < skb->len;) {
+               j += snprintf(buffer + j, sizeof(buffer) - j, "%02hhx",
+                             buf[i++]);
+
+               if ((i % 32) == 0) {
+                       netdev_alert(netdev, "  0x%04x: %s\n", i - 32, buffer);
+                       j = 0;
+               } else if ((i % 16) == 0) {
+                       buffer[j++] = ' ';
+                       buffer[j++] = ' ';
+               } else if ((i % 4) == 0) {
+                       buffer[j++] = ' ';
+               }
+       }
+       if (i % 32)
+               netdev_alert(netdev, "  0x%04x: %s\n", i - (i % 32), buffer);
+
+       netdev_alert(netdev, "\n************** SKB dump ****************\n");
+}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
new file mode 100644 (file)
index 0000000..8909f2b
--- /dev/null
@@ -0,0 +1,510 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute 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 file 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/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and 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.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Advanced Micro Devices, Inc. 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 <COPYRIGHT HOLDER> 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.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and 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.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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/spinlock.h>
+#include <linux/phy.h>
+
+#include "xgbe.h"
+#include "xgbe-common.h"
+
+
+struct xgbe_stats {
+       char stat_string[ETH_GSTRING_LEN];
+       int stat_size;
+       int stat_offset;
+};
+
+#define XGMAC_MMC_STAT(_string, _var)                          \
+       { _string,                                              \
+         FIELD_SIZEOF(struct xgbe_mmc_stats, _var),            \
+         offsetof(struct xgbe_prv_data, mmc_stats._var),       \
+       }
+
+static const struct xgbe_stats xgbe_gstring_stats[] = {
+       XGMAC_MMC_STAT("tx_bytes", txoctetcount_gb),
+       XGMAC_MMC_STAT("tx_packets", txframecount_gb),
+       XGMAC_MMC_STAT("tx_unicast_packets", txunicastframes_gb),
+       XGMAC_MMC_STAT("tx_broadcast_packets", txbroadcastframes_gb),
+       XGMAC_MMC_STAT("tx_multicast_packets", txmulticastframes_gb),
+       XGMAC_MMC_STAT("tx_vlan_packets", txvlanframes_g),
+       XGMAC_MMC_STAT("tx_64_byte_packets", tx64octets_gb),
+       XGMAC_MMC_STAT("tx_65_to_127_byte_packets", tx65to127octets_gb),
+       XGMAC_MMC_STAT("tx_128_to_255_byte_packets", tx128to255octets_gb),
+       XGMAC_MMC_STAT("tx_256_to_511_byte_packets", tx256to511octets_gb),
+       XGMAC_MMC_STAT("tx_512_to_1023_byte_packets", tx512to1023octets_gb),
+       XGMAC_MMC_STAT("tx_1024_to_max_byte_packets", tx1024tomaxoctets_gb),
+       XGMAC_MMC_STAT("tx_underflow_errors", txunderflowerror),
+       XGMAC_MMC_STAT("tx_pause_frames", txpauseframes),
+
+       XGMAC_MMC_STAT("rx_bytes", rxoctetcount_gb),
+       XGMAC_MMC_STAT("rx_packets", rxframecount_gb),
+       XGMAC_MMC_STAT("rx_unicast_packets", rxunicastframes_g),
+       XGMAC_MMC_STAT("rx_broadcast_packets", rxbroadcastframes_g),
+       XGMAC_MMC_STAT("rx_multicast_packets", rxmulticastframes_g),
+       XGMAC_MMC_STAT("rx_vlan_packets", rxvlanframes_gb),
+       XGMAC_MMC_STAT("rx_64_byte_packets", rx64octets_gb),
+       XGMAC_MMC_STAT("rx_65_to_127_byte_packets", rx65to127octets_gb),
+       XGMAC_MMC_STAT("rx_128_to_255_byte_packets", rx128to255octets_gb),
+       XGMAC_MMC_STAT("rx_256_to_511_byte_packets", rx256to511octets_gb),
+       XGMAC_MMC_STAT("rx_512_to_1023_byte_packets", rx512to1023octets_gb),
+       XGMAC_MMC_STAT("rx_1024_to_max_byte_packets", rx1024tomaxoctets_gb),
+       XGMAC_MMC_STAT("rx_undersize_packets", rxundersize_g),
+       XGMAC_MMC_STAT("rx_oversize_packets", rxoversize_g),
+       XGMAC_MMC_STAT("rx_crc_errors", rxcrcerror),
+       XGMAC_MMC_STAT("rx_crc_errors_small_packets", rxrunterror),
+       XGMAC_MMC_STAT("rx_crc_errors_giant_packets", rxjabbererror),
+       XGMAC_MMC_STAT("rx_length_errors", rxlengtherror),
+       XGMAC_MMC_STAT("rx_out_of_range_errors", rxoutofrangetype),
+       XGMAC_MMC_STAT("rx_fifo_overflow_errors", rxfifooverflow),
+       XGMAC_MMC_STAT("rx_watchdog_errors", rxwatchdogerror),
+       XGMAC_MMC_STAT("rx_pause_frames", rxpauseframes),
+};
+#define XGBE_STATS_COUNT       ARRAY_SIZE(xgbe_gstring_stats)
+
+static void xgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
+{
+       int i;
+
+       DBGPR("-->%s\n", __func__);
+
+       switch (stringset) {
+       case ETH_SS_STATS:
+               for (i = 0; i < XGBE_STATS_COUNT; i++) {
+                       memcpy(data, xgbe_gstring_stats[i].stat_string,
+                              ETH_GSTRING_LEN);
+                       data += ETH_GSTRING_LEN;
+               }
+               break;
+       }
+
+       DBGPR("<--%s\n", __func__);
+}
+
+static void xgbe_get_ethtool_stats(struct net_device *netdev,
+                                  struct ethtool_stats *stats, u64 *data)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       u8 *stat;
+       int i;
+
+       DBGPR("-->%s\n", __func__);
+
+       pdata->hw_if.read_mmc_stats(pdata);
+       for (i = 0; i < XGBE_STATS_COUNT; i++) {
+               stat = (u8 *)pdata + xgbe_gstring_stats[i].stat_offset;
+               *data++ = *(u64 *)stat;
+       }
+
+       DBGPR("<--%s\n", __func__);
+}
+
+static int xgbe_get_sset_count(struct net_device *netdev, int stringset)
+{
+       int ret;
+
+       DBGPR("-->%s\n", __func__);
+
+       switch (stringset) {
+       case ETH_SS_STATS:
+               ret = XGBE_STATS_COUNT;
+               break;
+
+       default:
+               ret = -EOPNOTSUPP;
+       }
+
+       DBGPR("<--%s\n", __func__);
+
+       return ret;
+}
+
+static void xgbe_get_pauseparam(struct net_device *netdev,
+                               struct ethtool_pauseparam *pause)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+
+       DBGPR("-->xgbe_get_pauseparam\n");
+
+       pause->autoneg = pdata->pause_autoneg;
+       pause->tx_pause = pdata->tx_pause;
+       pause->rx_pause = pdata->rx_pause;
+
+       DBGPR("<--xgbe_get_pauseparam\n");
+}
+
+static int xgbe_set_pauseparam(struct net_device *netdev,
+                              struct ethtool_pauseparam *pause)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct phy_device *phydev = pdata->phydev;
+       int ret = 0;
+
+       DBGPR("-->xgbe_set_pauseparam\n");
+
+       DBGPR("  autoneg = %d, tx_pause = %d, rx_pause = %d\n",
+             pause->autoneg, pause->tx_pause, pause->rx_pause);
+
+       pdata->pause_autoneg = pause->autoneg;
+       if (pause->autoneg) {
+               phydev->advertising |= ADVERTISED_Pause;
+               phydev->advertising |= ADVERTISED_Asym_Pause;
+
+       } else {
+               phydev->advertising &= ~ADVERTISED_Pause;
+               phydev->advertising &= ~ADVERTISED_Asym_Pause;
+
+               pdata->tx_pause = pause->tx_pause;
+               pdata->rx_pause = pause->rx_pause;
+       }
+
+       if (netif_running(netdev))
+               ret = phy_start_aneg(phydev);
+
+       DBGPR("<--xgbe_set_pauseparam\n");
+
+       return ret;
+}
+
+static int xgbe_get_settings(struct net_device *netdev,
+                            struct ethtool_cmd *cmd)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       int ret;
+
+       DBGPR("-->xgbe_get_settings\n");
+
+       if (!pdata->phydev)
+               return -ENODEV;
+
+       spin_lock_irq(&pdata->lock);
+
+       ret = phy_ethtool_gset(pdata->phydev, cmd);
+       cmd->transceiver = XCVR_EXTERNAL;
+
+       spin_unlock_irq(&pdata->lock);
+
+       DBGPR("<--xgbe_get_settings\n");
+
+       return ret;
+}
+
+static int xgbe_set_settings(struct net_device *netdev,
+                            struct ethtool_cmd *cmd)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct phy_device *phydev = pdata->phydev;
+       u32 speed;
+       int ret;
+
+       DBGPR("-->xgbe_set_settings\n");
+
+       if (!pdata->phydev)
+               return -ENODEV;
+
+       spin_lock_irq(&pdata->lock);
+
+       speed = ethtool_cmd_speed(cmd);
+
+       ret = -EINVAL;
+       if (cmd->phy_address != phydev->addr)
+               goto unlock;
+
+       if ((cmd->autoneg != AUTONEG_ENABLE) &&
+           (cmd->autoneg != AUTONEG_DISABLE))
+               goto unlock;
+
+       if ((cmd->autoneg == AUTONEG_DISABLE) &&
+           (((speed != SPEED_10000) && (speed != SPEED_1000)) ||
+            (cmd->duplex != DUPLEX_FULL)))
+               goto unlock;
+
+       if (cmd->autoneg == AUTONEG_ENABLE) {
+               /* Clear settings needed to force speeds */
+               phydev->supported &= ~SUPPORTED_1000baseT_Full;
+               phydev->supported &= ~SUPPORTED_10000baseT_Full;
+       } else {
+               /* Add settings needed to force speed */
+               phydev->supported |= SUPPORTED_1000baseT_Full;
+               phydev->supported |= SUPPORTED_10000baseT_Full;
+       }
+
+       cmd->advertising &= phydev->supported;
+       if ((cmd->autoneg == AUTONEG_ENABLE) && !cmd->advertising)
+               goto unlock;
+
+       ret = 0;
+       phydev->autoneg = cmd->autoneg;
+       phydev->speed = speed;
+       phydev->duplex = cmd->duplex;
+       phydev->advertising = cmd->advertising;
+
+       if (cmd->autoneg == AUTONEG_ENABLE)
+               phydev->advertising |= ADVERTISED_Autoneg;
+       else
+               phydev->advertising &= ~ADVERTISED_Autoneg;
+
+       if (netif_running(netdev))
+               ret = phy_start_aneg(phydev);
+
+unlock:
+       spin_unlock_irq(&pdata->lock);
+
+       DBGPR("<--xgbe_set_settings\n");
+
+       return ret;
+}
+
+static void xgbe_get_drvinfo(struct net_device *netdev,
+                            struct ethtool_drvinfo *drvinfo)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+
+       strlcpy(drvinfo->driver, XGBE_DRV_NAME, sizeof(drvinfo->driver));
+       strlcpy(drvinfo->version, XGBE_DRV_VERSION, sizeof(drvinfo->version));
+       strlcpy(drvinfo->bus_info, dev_name(pdata->dev),
+               sizeof(drvinfo->bus_info));
+       snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "%d.%d.%d",
+                XGMAC_IOREAD_BITS(pdata, MAC_VR, USERVER),
+                XGMAC_IOREAD_BITS(pdata, MAC_VR, DEVID),
+                XGMAC_IOREAD_BITS(pdata, MAC_VR, SNPSVER));
+       drvinfo->n_stats = XGBE_STATS_COUNT;
+}
+
+static int xgbe_get_coalesce(struct net_device *netdev,
+                            struct ethtool_coalesce *ec)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       unsigned int riwt;
+
+       DBGPR("-->xgbe_get_coalesce\n");
+
+       memset(ec, 0, sizeof(struct ethtool_coalesce));
+
+       riwt = pdata->rx_riwt;
+       ec->rx_coalesce_usecs = hw_if->riwt_to_usec(pdata, riwt);
+       ec->rx_max_coalesced_frames = pdata->rx_frames;
+
+       ec->tx_coalesce_usecs = pdata->tx_usecs;
+       ec->tx_max_coalesced_frames = pdata->tx_frames;
+
+       DBGPR("<--xgbe_get_coalesce\n");
+
+       return 0;
+}
+
+static int xgbe_set_coalesce(struct net_device *netdev,
+                            struct ethtool_coalesce *ec)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       unsigned int rx_frames, rx_riwt, rx_usecs;
+       unsigned int tx_frames, tx_usecs;
+
+       DBGPR("-->xgbe_set_coalesce\n");
+
+       /* Check for not supported parameters  */
+       if ((ec->rx_coalesce_usecs_irq) ||
+           (ec->rx_max_coalesced_frames_irq) ||
+           (ec->tx_coalesce_usecs_irq) ||
+           (ec->tx_max_coalesced_frames_irq) ||
+           (ec->stats_block_coalesce_usecs) ||
+           (ec->use_adaptive_rx_coalesce) ||
+           (ec->use_adaptive_tx_coalesce) ||
+           (ec->pkt_rate_low) ||
+           (ec->rx_coalesce_usecs_low) ||
+           (ec->rx_max_coalesced_frames_low) ||
+           (ec->tx_coalesce_usecs_low) ||
+           (ec->tx_max_coalesced_frames_low) ||
+           (ec->pkt_rate_high) ||
+           (ec->rx_coalesce_usecs_high) ||
+           (ec->rx_max_coalesced_frames_high) ||
+           (ec->tx_coalesce_usecs_high) ||
+           (ec->tx_max_coalesced_frames_high) ||
+           (ec->rate_sample_interval))
+               return -EOPNOTSUPP;
+
+       /* Can only change rx-frames when interface is down (see
+        * rx_descriptor_init in xgbe-dev.c)
+        */
+       rx_frames = pdata->rx_frames;
+       if (rx_frames != ec->rx_max_coalesced_frames && netif_running(netdev)) {
+               netdev_alert(netdev,
+                            "interface must be down to change rx-frames\n");
+               return -EINVAL;
+       }
+
+       rx_riwt = hw_if->usec_to_riwt(pdata, ec->rx_coalesce_usecs);
+       rx_frames = ec->rx_max_coalesced_frames;
+
+       /* Use smallest possible value if conversion resulted in zero */
+       if (ec->rx_coalesce_usecs && !rx_riwt)
+               rx_riwt = 1;
+
+       /* Check the bounds of values for Rx */
+       if (rx_riwt > XGMAC_MAX_DMA_RIWT) {
+               rx_usecs = hw_if->riwt_to_usec(pdata, XGMAC_MAX_DMA_RIWT);
+               netdev_alert(netdev, "rx-usec is limited to %d usecs\n",
+                            rx_usecs);
+               return -EINVAL;
+       }
+       if (rx_frames > pdata->channel->rx_ring->rdesc_count) {
+               netdev_alert(netdev, "rx-frames is limited to %d frames\n",
+                            pdata->channel->rx_ring->rdesc_count);
+               return -EINVAL;
+       }
+
+       tx_usecs = ec->tx_coalesce_usecs;
+       tx_frames = ec->tx_max_coalesced_frames;
+
+       /* Check the bounds of values for Tx */
+       if (tx_frames > pdata->channel->tx_ring->rdesc_count) {
+               netdev_alert(netdev, "tx-frames is limited to %d frames\n",
+                            pdata->channel->tx_ring->rdesc_count);
+               return -EINVAL;
+       }
+
+       pdata->rx_riwt = rx_riwt;
+       pdata->rx_frames = rx_frames;
+       hw_if->config_rx_coalesce(pdata);
+
+       pdata->tx_usecs = tx_usecs;
+       pdata->tx_frames = tx_frames;
+       hw_if->config_tx_coalesce(pdata);
+
+       DBGPR("<--xgbe_set_coalesce\n");
+
+       return 0;
+}
+
+static const struct ethtool_ops xgbe_ethtool_ops = {
+       .get_settings = xgbe_get_settings,
+       .set_settings = xgbe_set_settings,
+       .get_drvinfo = xgbe_get_drvinfo,
+       .get_link = ethtool_op_get_link,
+       .get_coalesce = xgbe_get_coalesce,
+       .set_coalesce = xgbe_set_coalesce,
+       .get_pauseparam = xgbe_get_pauseparam,
+       .set_pauseparam = xgbe_set_pauseparam,
+       .get_strings = xgbe_get_strings,
+       .get_ethtool_stats = xgbe_get_ethtool_stats,
+       .get_sset_count = xgbe_get_sset_count,
+};
+
+struct ethtool_ops *xgbe_get_ethtool_ops(void)
+{
+       return (struct ethtool_ops *)&xgbe_ethtool_ops;
+}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c
new file mode 100644 (file)
index 0000000..c83584a
--- /dev/null
@@ -0,0 +1,512 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute 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 file 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/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and 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.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Advanced Micro Devices, Inc. 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 <COPYRIGHT HOLDER> 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.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and 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.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/clk.h>
+
+#include "xgbe.h"
+#include "xgbe-common.h"
+
+
+MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(XGBE_DRV_VERSION);
+MODULE_DESCRIPTION(XGBE_DRV_DESC);
+
+static struct xgbe_channel *xgbe_alloc_rings(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel_mem, *channel;
+       struct xgbe_ring *tx_ring, *rx_ring;
+       unsigned int count, i;
+
+       DBGPR("-->xgbe_alloc_rings\n");
+
+       count = max_t(unsigned int, pdata->tx_ring_count, pdata->rx_ring_count);
+
+       channel_mem = devm_kcalloc(pdata->dev, count,
+                                  sizeof(struct xgbe_channel), GFP_KERNEL);
+       if (!channel_mem)
+               return NULL;
+
+       tx_ring = devm_kcalloc(pdata->dev, pdata->tx_ring_count,
+                              sizeof(struct xgbe_ring), GFP_KERNEL);
+       if (!tx_ring)
+               return NULL;
+
+       rx_ring = devm_kcalloc(pdata->dev, pdata->rx_ring_count,
+                              sizeof(struct xgbe_ring), GFP_KERNEL);
+       if (!rx_ring)
+               return NULL;
+
+       for (i = 0, channel = channel_mem; i < count; i++, channel++) {
+               snprintf(channel->name, sizeof(channel->name), "channel-%d", i);
+               channel->pdata = pdata;
+               channel->queue_index = i;
+               channel->dma_regs = pdata->xgmac_regs + DMA_CH_BASE +
+                                   (DMA_CH_INC * i);
+
+               if (i < pdata->tx_ring_count) {
+                       spin_lock_init(&tx_ring->lock);
+                       channel->tx_ring = tx_ring++;
+               }
+
+               if (i < pdata->rx_ring_count) {
+                       spin_lock_init(&tx_ring->lock);
+                       channel->rx_ring = rx_ring++;
+               }
+
+               DBGPR("  %s - queue_index=%u, dma_regs=%p, tx=%p, rx=%p\n",
+                     channel->name, channel->queue_index, channel->dma_regs,
+                     channel->tx_ring, channel->rx_ring);
+       }
+
+       pdata->channel_count = count;
+
+       DBGPR("<--xgbe_alloc_rings\n");
+
+       return channel_mem;
+}
+
+static void xgbe_default_config(struct xgbe_prv_data *pdata)
+{
+       DBGPR("-->xgbe_default_config\n");
+
+       pdata->pblx8 = DMA_PBL_X8_ENABLE;
+       pdata->tx_sf_mode = MTL_TSF_ENABLE;
+       pdata->tx_threshold = MTL_TX_THRESHOLD_64;
+       pdata->tx_pbl = DMA_PBL_16;
+       pdata->tx_osp_mode = DMA_OSP_ENABLE;
+       pdata->rx_sf_mode = MTL_RSF_DISABLE;
+       pdata->rx_threshold = MTL_RX_THRESHOLD_64;
+       pdata->rx_pbl = DMA_PBL_16;
+       pdata->pause_autoneg = 1;
+       pdata->tx_pause = 1;
+       pdata->rx_pause = 1;
+       pdata->power_down = 0;
+       pdata->default_autoneg = AUTONEG_ENABLE;
+       pdata->default_speed = SPEED_10000;
+
+       DBGPR("<--xgbe_default_config\n");
+}
+
+static void xgbe_init_all_fptrs(struct xgbe_prv_data *pdata)
+{
+       xgbe_init_function_ptrs_dev(&pdata->hw_if);
+       xgbe_init_function_ptrs_desc(&pdata->desc_if);
+}
+
+static int xgbe_probe(struct platform_device *pdev)
+{
+       struct xgbe_prv_data *pdata;
+       struct xgbe_hw_if *hw_if;
+       struct xgbe_desc_if *desc_if;
+       struct net_device *netdev;
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       const u8 *mac_addr;
+       int ret;
+
+       DBGPR("--> xgbe_probe\n");
+
+       netdev = alloc_etherdev_mq(sizeof(struct xgbe_prv_data),
+                                  XGBE_MAX_DMA_CHANNELS);
+       if (!netdev) {
+               dev_err(dev, "alloc_etherdev failed\n");
+               ret = -ENOMEM;
+               goto err_alloc;
+       }
+       SET_NETDEV_DEV(netdev, dev);
+       pdata = netdev_priv(netdev);
+       pdata->netdev = netdev;
+       pdata->pdev = pdev;
+       pdata->dev = dev;
+       platform_set_drvdata(pdev, netdev);
+
+       spin_lock_init(&pdata->lock);
+       mutex_init(&pdata->xpcs_mutex);
+
+       /* Set and validate the number of descriptors for a ring */
+       BUILD_BUG_ON_NOT_POWER_OF_2(TX_DESC_CNT);
+       pdata->tx_desc_count = TX_DESC_CNT;
+       if (pdata->tx_desc_count & (pdata->tx_desc_count - 1)) {
+               dev_err(dev, "tx descriptor count (%d) is not valid\n",
+                       pdata->tx_desc_count);
+               ret = -EINVAL;
+               goto err_io;
+       }
+       BUILD_BUG_ON_NOT_POWER_OF_2(RX_DESC_CNT);
+       pdata->rx_desc_count = RX_DESC_CNT;
+       if (pdata->rx_desc_count & (pdata->rx_desc_count - 1)) {
+               dev_err(dev, "rx descriptor count (%d) is not valid\n",
+                       pdata->rx_desc_count);
+               ret = -EINVAL;
+               goto err_io;
+       }
+
+       /* Obtain the system clock setting */
+       pdata->sysclock = devm_clk_get(dev, NULL);
+       if (IS_ERR(pdata->sysclock)) {
+               dev_err(dev, "devm_clk_get failed\n");
+               ret = PTR_ERR(pdata->sysclock);
+               goto err_io;
+       }
+
+       /* Obtain the mmio areas for the device */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       pdata->xgmac_regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(pdata->xgmac_regs)) {
+               dev_err(dev, "xgmac ioremap failed\n");
+               ret = PTR_ERR(pdata->xgmac_regs);
+               goto err_io;
+       }
+       DBGPR("  xgmac_regs = %p\n", pdata->xgmac_regs);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       pdata->xpcs_regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(pdata->xpcs_regs)) {
+               dev_err(dev, "xpcs ioremap failed\n");
+               ret = PTR_ERR(pdata->xpcs_regs);
+               goto err_io;
+       }
+       DBGPR("  xpcs_regs  = %p\n", pdata->xpcs_regs);
+
+       /* Set the DMA mask */
+       if (!dev->dma_mask)
+               dev->dma_mask = &dev->coherent_dma_mask;
+       *(dev->dma_mask) = DMA_BIT_MASK(40);
+       dev->coherent_dma_mask = DMA_BIT_MASK(40);
+
+       ret = platform_get_irq(pdev, 0);
+       if (ret < 0) {
+               dev_err(dev, "platform_get_irq failed\n");
+               goto err_io;
+       }
+       netdev->irq = ret;
+       netdev->base_addr = (unsigned long)pdata->xgmac_regs;
+
+       /* Set all the function pointers */
+       xgbe_init_all_fptrs(pdata);
+       hw_if = &pdata->hw_if;
+       desc_if = &pdata->desc_if;
+
+       /* Issue software reset to device */
+       hw_if->exit(pdata);
+
+       /* Populate the hardware features */
+       xgbe_get_all_hw_features(pdata);
+
+       /* Retrieve the MAC address */
+       mac_addr = of_get_mac_address(dev->of_node);
+       if (!mac_addr) {
+               dev_err(dev, "invalid mac address for this device\n");
+               ret = -EINVAL;
+               goto err_io;
+       }
+       memcpy(netdev->dev_addr, mac_addr, netdev->addr_len);
+
+       /* Retrieve the PHY mode - it must be "xgmii" */
+       pdata->phy_mode = of_get_phy_mode(dev->of_node);
+       if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII) {
+               dev_err(dev, "invalid phy-mode specified for this device\n");
+               ret = -EINVAL;
+               goto err_io;
+       }
+
+       /* Set default configuration data */
+       xgbe_default_config(pdata);
+
+       /* Calculate the number of Tx and Rx rings to be created */
+       pdata->tx_ring_count = min_t(unsigned int, num_online_cpus(),
+                                    pdata->hw_feat.tx_ch_cnt);
+       if (netif_set_real_num_tx_queues(netdev, pdata->tx_ring_count)) {
+               dev_err(dev, "error setting real tx queue count\n");
+               goto err_io;
+       }
+
+       pdata->rx_ring_count = min_t(unsigned int,
+                                    netif_get_num_default_rss_queues(),
+                                    pdata->hw_feat.rx_ch_cnt);
+       ret = netif_set_real_num_rx_queues(netdev, pdata->rx_ring_count);
+       if (ret) {
+               dev_err(dev, "error setting real rx queue count\n");
+               goto err_io;
+       }
+
+       /* Allocate the rings for the DMA channels */
+       pdata->channel = xgbe_alloc_rings(pdata);
+       if (!pdata->channel) {
+               dev_err(dev, "ring allocation failed\n");
+               ret = -ENOMEM;
+               goto err_io;
+       }
+
+       /* Prepare to regsiter with MDIO */
+       pdata->mii_bus_id = kasprintf(GFP_KERNEL, "%s", pdev->name);
+       if (!pdata->mii_bus_id) {
+               dev_err(dev, "failed to allocate mii bus id\n");
+               ret = -ENOMEM;
+               goto err_io;
+       }
+       ret = xgbe_mdio_register(pdata);
+       if (ret)
+               goto err_bus_id;
+
+       /* Set network and ethtool operations */
+       netdev->netdev_ops = xgbe_get_netdev_ops();
+       netdev->ethtool_ops = xgbe_get_ethtool_ops();
+
+       /* Set device features */
+       netdev->hw_features = NETIF_F_SG |
+                             NETIF_F_IP_CSUM |
+                             NETIF_F_IPV6_CSUM |
+                             NETIF_F_RXCSUM |
+                             NETIF_F_TSO |
+                             NETIF_F_TSO6 |
+                             NETIF_F_GRO |
+                             NETIF_F_HW_VLAN_CTAG_RX |
+                             NETIF_F_HW_VLAN_CTAG_TX;
+
+       netdev->vlan_features |= NETIF_F_SG |
+                                NETIF_F_IP_CSUM |
+                                NETIF_F_IPV6_CSUM |
+                                NETIF_F_TSO |
+                                NETIF_F_TSO6;
+
+       netdev->features |= netdev->hw_features;
+       pdata->netdev_features = netdev->features;
+
+       xgbe_init_rx_coalesce(pdata);
+       xgbe_init_tx_coalesce(pdata);
+
+       netif_carrier_off(netdev);
+       ret = register_netdev(netdev);
+       if (ret) {
+               dev_err(dev, "net device registration failed\n");
+               goto err_reg_netdev;
+       }
+
+       xgbe_debugfs_init(pdata);
+
+       netdev_notice(netdev, "net device enabled\n");
+
+       DBGPR("<-- xgbe_probe\n");
+
+       return 0;
+
+err_reg_netdev:
+       xgbe_mdio_unregister(pdata);
+
+err_bus_id:
+       kfree(pdata->mii_bus_id);
+
+err_io:
+       free_netdev(netdev);
+
+err_alloc:
+       dev_notice(dev, "net device not enabled\n");
+
+       return ret;
+}
+
+static int xgbe_remove(struct platform_device *pdev)
+{
+       struct net_device *netdev = platform_get_drvdata(pdev);
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+
+       DBGPR("-->xgbe_remove\n");
+
+       xgbe_debugfs_exit(pdata);
+
+       unregister_netdev(netdev);
+
+       xgbe_mdio_unregister(pdata);
+
+       kfree(pdata->mii_bus_id);
+
+       free_netdev(netdev);
+
+       DBGPR("<--xgbe_remove\n");
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int xgbe_suspend(struct device *dev)
+{
+       struct net_device *netdev = dev_get_drvdata(dev);
+       int ret;
+
+       DBGPR("-->xgbe_suspend\n");
+
+       if (!netif_running(netdev)) {
+               DBGPR("<--xgbe_dev_suspend\n");
+               return -EINVAL;
+       }
+
+       ret = xgbe_powerdown(netdev, XGMAC_DRIVER_CONTEXT);
+
+       DBGPR("<--xgbe_suspend\n");
+
+       return ret;
+}
+
+static int xgbe_resume(struct device *dev)
+{
+       struct net_device *netdev = dev_get_drvdata(dev);
+       int ret;
+
+       DBGPR("-->xgbe_resume\n");
+
+       if (!netif_running(netdev)) {
+               DBGPR("<--xgbe_dev_resume\n");
+               return -EINVAL;
+       }
+
+       ret = xgbe_powerup(netdev, XGMAC_DRIVER_CONTEXT);
+
+       DBGPR("<--xgbe_resume\n");
+
+       return ret;
+}
+#endif /* CONFIG_PM */
+
+static const struct of_device_id xgbe_of_match[] = {
+       { .compatible = "amd,xgbe-seattle-v1a", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, xgbe_of_match);
+static SIMPLE_DEV_PM_OPS(xgbe_pm_ops, xgbe_suspend, xgbe_resume);
+
+static struct platform_driver xgbe_driver = {
+       .driver = {
+               .name = "amd-xgbe",
+               .of_match_table = xgbe_of_match,
+               .pm = &xgbe_pm_ops,
+       },
+       .probe = xgbe_probe,
+       .remove = xgbe_remove,
+};
+
+module_platform_driver(xgbe_driver);
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
new file mode 100644 (file)
index 0000000..ea7a5d6
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute 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 file 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/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and 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.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Advanced Micro Devices, Inc. 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 <COPYRIGHT HOLDER> 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.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and 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.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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/module.h>
+#include <linux/kmod.h>
+#include <linux/spinlock.h>
+#include <linux/mdio.h>
+#include <linux/phy.h>
+#include <linux/of.h>
+
+#include "xgbe.h"
+#include "xgbe-common.h"
+
+
+static int xgbe_mdio_read(struct mii_bus *mii, int prtad, int mmd_reg)
+{
+       struct xgbe_prv_data *pdata = mii->priv;
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       int mmd_data;
+
+       DBGPR_MDIO("-->xgbe_mdio_read: prtad=%#x mmd_reg=%#x\n",
+                  prtad, mmd_reg);
+
+       mmd_data = hw_if->read_mmd_regs(pdata, prtad, mmd_reg);
+
+       DBGPR_MDIO("<--xgbe_mdio_read: mmd_data=%#x\n", mmd_data);
+
+       return mmd_data;
+}
+
+static int xgbe_mdio_write(struct mii_bus *mii, int prtad, int mmd_reg,
+                          u16 mmd_val)
+{
+       struct xgbe_prv_data *pdata = mii->priv;
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       int mmd_data = mmd_val;
+
+       DBGPR_MDIO("-->xgbe_mdio_write: prtad=%#x mmd_reg=%#x mmd_data=%#x\n",
+                  prtad, mmd_reg, mmd_data);
+
+       hw_if->write_mmd_regs(pdata, prtad, mmd_reg, mmd_data);
+
+       DBGPR_MDIO("<--xgbe_mdio_write\n");
+
+       return 0;
+}
+
+static void xgbe_adjust_link(struct net_device *netdev)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct phy_device *phydev = pdata->phydev;
+       unsigned long flags;
+       int new_state = 0;
+
+       if (phydev == NULL)
+               return;
+
+       DBGPR_MDIO("-->xgbe_adjust_link: address=%d, newlink=%d, curlink=%d\n",
+                  phydev->addr, phydev->link, pdata->phy_link);
+
+       spin_lock_irqsave(&pdata->lock, flags);
+
+       if (phydev->link) {
+               /* Flow control support */
+               if (pdata->pause_autoneg) {
+                       if (phydev->pause || phydev->asym_pause) {
+                               pdata->tx_pause = 1;
+                               pdata->rx_pause = 1;
+                       } else {
+                               pdata->tx_pause = 0;
+                               pdata->rx_pause = 0;
+                       }
+               }
+
+               if (pdata->tx_pause != pdata->phy_tx_pause) {
+                       hw_if->config_tx_flow_control(pdata);
+                       pdata->phy_tx_pause = pdata->tx_pause;
+               }
+
+               if (pdata->rx_pause != pdata->phy_rx_pause) {
+                       hw_if->config_rx_flow_control(pdata);
+                       pdata->phy_rx_pause = pdata->rx_pause;
+               }
+
+               /* Speed support */
+               if (phydev->speed != pdata->phy_speed) {
+                       new_state = 1;
+
+                       switch (phydev->speed) {
+                       case SPEED_10000:
+                               hw_if->set_xgmii_speed(pdata);
+                               break;
+
+                       case SPEED_2500:
+                               hw_if->set_gmii_2500_speed(pdata);
+                               break;
+
+                       case SPEED_1000:
+                               hw_if->set_gmii_speed(pdata);
+                               break;
+                       }
+                       pdata->phy_speed = phydev->speed;
+               }
+
+               if (phydev->link != pdata->phy_link) {
+                       new_state = 1;
+                       pdata->phy_link = 1;
+               }
+       } else if (pdata->phy_link) {
+               new_state = 1;
+               pdata->phy_link = 0;
+               pdata->phy_speed = SPEED_UNKNOWN;
+       }
+
+       if (new_state)
+               phy_print_status(phydev);
+
+       spin_unlock_irqrestore(&pdata->lock, flags);
+
+       DBGPR_MDIO("<--xgbe_adjust_link\n");
+}
+
+void xgbe_dump_phy_registers(struct xgbe_prv_data *pdata)
+{
+       struct device *dev = pdata->dev;
+       struct phy_device *phydev = pdata->mii->phy_map[XGBE_PRTAD];
+       int i;
+
+       dev_alert(dev, "\n************* PHY Reg dump **********************\n");
+
+       dev_alert(dev, "PCS Control Reg (%#04x) = %#04x\n", MDIO_CTRL1,
+                 XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1));
+       dev_alert(dev, "PCS Status Reg (%#04x) = %#04x\n", MDIO_STAT1,
+                 XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1));
+       dev_alert(dev, "Phy Id (PHYS ID 1 %#04x)= %#04x\n", MDIO_DEVID1,
+                 XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVID1));
+       dev_alert(dev, "Phy Id (PHYS ID 2 %#04x)= %#04x\n", MDIO_DEVID2,
+                 XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVID2));
+       dev_alert(dev, "Devices in Package (%#04x)= %#04x\n", MDIO_DEVS1,
+                 XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVS1));
+       dev_alert(dev, "Devices in Package (%#04x)= %#04x\n", MDIO_DEVS2,
+                 XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVS2));
+
+       dev_alert(dev, "Auto-Neg Control Reg (%#04x) = %#04x\n", MDIO_CTRL1,
+                 XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_CTRL1));
+       dev_alert(dev, "Auto-Neg Status Reg (%#04x) = %#04x\n", MDIO_STAT1,
+                 XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_STAT1));
+       dev_alert(dev, "Auto-Neg Ad Reg 1 (%#04x) = %#04x\n",
+                 MDIO_AN_ADVERTISE,
+                 XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE));
+       dev_alert(dev, "Auto-Neg Ad Reg 2 (%#04x) = %#04x\n",
+                 MDIO_AN_ADVERTISE + 1,
+                 XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1));
+       dev_alert(dev, "Auto-Neg Ad Reg 3 (%#04x) = %#04x\n",
+                 MDIO_AN_ADVERTISE + 2,
+                 XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2));
+       dev_alert(dev, "Auto-Neg Completion Reg (%#04x) = %#04x\n",
+                 MDIO_AN_COMP_STAT,
+                 XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_COMP_STAT));
+
+       dev_alert(dev, "MMD Device Mask = %#x\n",
+                 phydev->c45_ids.devices_in_package);
+       for (i = 0; i < ARRAY_SIZE(phydev->c45_ids.device_ids); i++)
+               dev_alert(dev, "  MMD %d: ID = %#08x\n", i,
+                         phydev->c45_ids.device_ids[i]);
+
+       dev_alert(dev, "\n*************************************************\n");
+}
+
+int xgbe_mdio_register(struct xgbe_prv_data *pdata)
+{
+       struct net_device *netdev = pdata->netdev;
+       struct device_node *phy_node;
+       struct mii_bus *mii;
+       struct phy_device *phydev;
+       int ret = 0;
+
+       DBGPR("-->xgbe_mdio_register\n");
+
+       /* Retrieve the phy-handle */
+       phy_node = of_parse_phandle(pdata->dev->of_node, "phy-handle", 0);
+       if (!phy_node) {
+               dev_err(pdata->dev, "unable to parse phy-handle\n");
+               return -EINVAL;
+       }
+
+       /* Register with the MDIO bus */
+       mii = mdiobus_alloc();
+       if (mii == NULL) {
+               dev_err(pdata->dev, "mdiobus_alloc failed\n");
+               ret = -ENOMEM;
+               goto err_node_get;
+       }
+
+       /* Register on the MDIO bus (don't probe any PHYs) */
+       mii->name = XGBE_PHY_NAME;
+       mii->read = xgbe_mdio_read;
+       mii->write = xgbe_mdio_write;
+       snprintf(mii->id, sizeof(mii->id), "%s", pdata->mii_bus_id);
+       mii->priv = pdata;
+       mii->phy_mask = ~0;
+       mii->parent = pdata->dev;
+       ret = mdiobus_register(mii);
+       if (ret) {
+               dev_err(pdata->dev, "mdiobus_register failed\n");
+               goto err_mdiobus_alloc;
+       }
+       DBGPR("  mdiobus_register succeeded for %s\n", pdata->mii_bus_id);
+
+       /* Probe the PCS using Clause 45 */
+       phydev = get_phy_device(mii, XGBE_PRTAD, true);
+       if (IS_ERR(phydev) || !phydev ||
+           !phydev->c45_ids.device_ids[MDIO_MMD_PCS]) {
+               dev_err(pdata->dev, "get_phy_device failed\n");
+               ret = phydev ? PTR_ERR(phydev) : -ENOLINK;
+               goto err_mdiobus_register;
+       }
+       request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT,
+                      MDIO_ID_ARGS(phydev->c45_ids.device_ids[MDIO_MMD_PCS]));
+
+       of_node_get(phy_node);
+       phydev->dev.of_node = phy_node;
+       ret = phy_device_register(phydev);
+       if (ret) {
+               dev_err(pdata->dev, "phy_device_register failed\n");
+               of_node_put(phy_node);
+               goto err_phy_device;
+       }
+
+       /* Add a reference to the PHY driver so it can't be unloaded */
+       pdata->phy_module = phydev->dev.driver ?
+                           phydev->dev.driver->owner : NULL;
+       if (!try_module_get(pdata->phy_module)) {
+               dev_err(pdata->dev, "try_module_get failed\n");
+               ret = -EIO;
+               goto err_phy_device;
+       }
+
+       pdata->mii = mii;
+       pdata->mdio_mmd = MDIO_MMD_PCS;
+
+       pdata->phy_link = -1;
+       pdata->phy_speed = SPEED_UNKNOWN;
+       pdata->phy_tx_pause = pdata->tx_pause;
+       pdata->phy_rx_pause = pdata->rx_pause;
+
+       ret = phy_connect_direct(netdev, phydev, &xgbe_adjust_link,
+                                pdata->phy_mode);
+       if (ret) {
+               netdev_err(netdev, "phy_connect_direct failed\n");
+               goto err_phy_device;
+       }
+
+       if (!phydev->drv || (phydev->drv->phy_id == 0)) {
+               netdev_err(netdev, "phy_id not valid\n");
+               ret = -ENODEV;
+               goto err_phy_connect;
+       }
+       DBGPR("  phy_connect_direct succeeded for PHY %s, link=%d\n",
+             dev_name(&phydev->dev), phydev->link);
+
+       phydev->autoneg = pdata->default_autoneg;
+       if (phydev->autoneg == AUTONEG_DISABLE) {
+               /* Add settings needed to force speed */
+               phydev->supported |= SUPPORTED_1000baseT_Full;
+               phydev->supported |= SUPPORTED_10000baseT_Full;
+
+               phydev->speed = pdata->default_speed;
+               phydev->duplex = DUPLEX_FULL;
+
+               phydev->advertising &= ~ADVERTISED_Autoneg;
+       }
+
+       pdata->phydev = phydev;
+
+       of_node_put(phy_node);
+
+       DBGPHY_REGS(pdata);
+
+       DBGPR("<--xgbe_mdio_register\n");
+
+       return 0;
+
+err_phy_connect:
+       phy_disconnect(phydev);
+
+err_phy_device:
+       phy_device_free(phydev);
+
+err_mdiobus_register:
+       mdiobus_unregister(mii);
+
+err_mdiobus_alloc:
+       mdiobus_free(mii);
+
+err_node_get:
+       of_node_put(phy_node);
+
+       return ret;
+}
+
+void xgbe_mdio_unregister(struct xgbe_prv_data *pdata)
+{
+       DBGPR("-->xgbe_mdio_unregister\n");
+
+       phy_disconnect(pdata->phydev);
+       pdata->phydev = NULL;
+
+       module_put(pdata->phy_module);
+       pdata->phy_module = NULL;
+
+       mdiobus_unregister(pdata->mii);
+       pdata->mii->priv = NULL;
+
+       mdiobus_free(pdata->mii);
+       pdata->mii = NULL;
+
+       DBGPR("<--xgbe_mdio_unregister\n");
+}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h
new file mode 100644 (file)
index 0000000..ab06271
--- /dev/null
@@ -0,0 +1,676 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute 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 file 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/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and 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.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Advanced Micro Devices, Inc. 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 <COPYRIGHT HOLDER> 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.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and 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.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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 __XGBE_H__
+#define __XGBE_H__
+
+#include <linux/dma-mapping.h>
+#include <linux/netdevice.h>
+#include <linux/workqueue.h>
+#include <linux/phy.h>
+
+
+#define XGBE_DRV_NAME          "amd-xgbe"
+#define XGBE_DRV_VERSION       "1.0.0-a"
+#define XGBE_DRV_DESC          "AMD 10 Gigabit Ethernet Driver"
+
+/* Descriptor related defines */
+#define TX_DESC_CNT            512
+#define TX_DESC_MIN_FREE       (TX_DESC_CNT >> 3)
+#define TX_DESC_MAX_PROC       (TX_DESC_CNT >> 1)
+#define RX_DESC_CNT            512
+
+#define TX_MAX_BUF_SIZE                (0x3fff & ~(64 - 1))
+
+#define RX_MIN_BUF_SIZE                (ETH_FRAME_LEN + ETH_FCS_LEN + VLAN_HLEN)
+#define RX_BUF_ALIGN           64
+
+#define XGBE_MAX_DMA_CHANNELS  16
+#define DMA_ARDOMAIN_SETTING   0x2
+#define DMA_ARCACHE_SETTING    0xb
+#define DMA_AWDOMAIN_SETTING   0x2
+#define DMA_AWCACHE_SETTING    0x7
+#define DMA_INTERRUPT_MASK     0x31c7
+
+#define XGMAC_MIN_PACKET       60
+#define XGMAC_STD_PACKET_MTU   1500
+#define XGMAC_MAX_STD_PACKET   1518
+#define XGMAC_JUMBO_PACKET_MTU 9000
+#define XGMAC_MAX_JUMBO_PACKET 9018
+
+#define MAX_MULTICAST_LIST     14
+#define TX_FLAGS_IP_PKT                0x00000001
+#define TX_FLAGS_TCP_PKT       0x00000002
+
+/* MDIO bus phy name */
+#define XGBE_PHY_NAME          "amd_xgbe_phy"
+#define XGBE_PRTAD             0
+
+/* Driver PMT macros */
+#define XGMAC_DRIVER_CONTEXT   1
+#define XGMAC_IOCTL_CONTEXT    2
+
+#define FIFO_SIZE_B(x)         (x)
+#define FIFO_SIZE_KB(x)                (x * 1024)
+
+#define XGBE_TC_CNT            2
+
+/* Helper macro for descriptor handling
+ *  Always use GET_DESC_DATA to access the descriptor data
+ *  since the index is free-running and needs to be and-ed
+ *  with the descriptor count value of the ring to index to
+ *  the proper descriptor data.
+ */
+#define GET_DESC_DATA(_ring, _idx)                             \
+       ((_ring)->rdata +                                       \
+        ((_idx) & ((_ring)->rdesc_count - 1)))
+
+
+/* Default coalescing parameters */
+#define XGMAC_INIT_DMA_TX_USECS                100
+#define XGMAC_INIT_DMA_TX_FRAMES       16
+
+#define XGMAC_MAX_DMA_RIWT             0xff
+#define XGMAC_INIT_DMA_RX_USECS                100
+#define XGMAC_INIT_DMA_RX_FRAMES       16
+
+/* Flow control queue count */
+#define XGMAC_MAX_FLOW_CONTROL_QUEUES  8
+
+
+struct xgbe_prv_data;
+
+struct xgbe_packet_data {
+       unsigned int attributes;
+
+       unsigned int errors;
+
+       unsigned int rdesc_count;
+       unsigned int length;
+
+       unsigned int header_len;
+       unsigned int tcp_header_len;
+       unsigned int tcp_payload_len;
+       unsigned short mss;
+
+       unsigned short vlan_ctag;
+};
+
+/* Common Rx and Tx descriptor mapping */
+struct xgbe_ring_desc {
+       unsigned int desc0;
+       unsigned int desc1;
+       unsigned int desc2;
+       unsigned int desc3;
+};
+
+/* Structure used to hold information related to the descriptor
+ * and the packet associated with the descriptor (always use
+ * use the GET_DESC_DATA macro to access this data from the ring)
+ */
+struct xgbe_ring_data {
+       struct xgbe_ring_desc *rdesc;   /* Virtual address of descriptor */
+       dma_addr_t rdesc_dma;           /* DMA address of descriptor */
+
+       struct sk_buff *skb;            /* Virtual address of SKB */
+       dma_addr_t skb_dma;             /* DMA address of SKB data */
+       unsigned int skb_dma_len;       /* Length of SKB DMA area */
+       unsigned int tso_header;        /* TSO header indicator */
+
+       unsigned short len;             /* Length of received Rx packet */
+
+       unsigned int interrupt;         /* Interrupt indicator */
+
+       unsigned int mapped_as_page;
+};
+
+struct xgbe_ring {
+       /* Ring lock - used just for TX rings at the moment */
+       spinlock_t lock;
+
+       /* Per packet related information */
+       struct xgbe_packet_data packet_data;
+
+       /* Virtual/DMA addresses and count of allocated descriptor memory */
+       struct xgbe_ring_desc *rdesc;
+       dma_addr_t rdesc_dma;
+       unsigned int rdesc_count;
+
+       /* Array of descriptor data corresponding the descriptor memory
+        * (always use the GET_DESC_DATA macro to access this data)
+        */
+       struct xgbe_ring_data *rdata;
+
+       /* Ring index values
+        *  cur   - Tx: index of descriptor to be used for current transfer
+        *          Rx: index of descriptor to check for packet availability
+        *  dirty - Tx: index of descriptor to check for transfer complete
+        *          Rx: count of descriptors in which a packet has been received
+        *              (used with skb_realloc_index to refresh the ring)
+        */
+       unsigned int cur;
+       unsigned int dirty;
+
+       /* Coalesce frame count used for interrupt bit setting */
+       unsigned int coalesce_count;
+
+       union {
+               struct {
+                       unsigned int queue_stopped;
+                       unsigned short cur_mss;
+                       unsigned short cur_vlan_ctag;
+               } tx;
+
+               struct {
+                       unsigned int realloc_index;
+                       unsigned int realloc_threshold;
+               } rx;
+       };
+} ____cacheline_aligned;
+
+/* Structure used to describe the descriptor rings associated with
+ * a DMA channel.
+ */
+struct xgbe_channel {
+       char name[16];
+
+       /* Address of private data area for device */
+       struct xgbe_prv_data *pdata;
+
+       /* Queue index and base address of queue's DMA registers */
+       unsigned int queue_index;
+       void __iomem *dma_regs;
+
+       unsigned int saved_ier;
+
+       unsigned int tx_timer_active;
+       struct hrtimer tx_timer;
+
+       struct xgbe_ring *tx_ring;
+       struct xgbe_ring *rx_ring;
+} ____cacheline_aligned;
+
+enum xgbe_int {
+       XGMAC_INT_DMA_ISR_DC0IS,
+       XGMAC_INT_DMA_CH_SR_TI,
+       XGMAC_INT_DMA_CH_SR_TPS,
+       XGMAC_INT_DMA_CH_SR_TBU,
+       XGMAC_INT_DMA_CH_SR_RI,
+       XGMAC_INT_DMA_CH_SR_RBU,
+       XGMAC_INT_DMA_CH_SR_RPS,
+       XGMAC_INT_DMA_CH_SR_FBE,
+       XGMAC_INT_DMA_ALL,
+};
+
+enum xgbe_int_state {
+       XGMAC_INT_STATE_SAVE,
+       XGMAC_INT_STATE_RESTORE,
+};
+
+enum xgbe_mtl_fifo_size {
+       XGMAC_MTL_FIFO_SIZE_256  = 0x00,
+       XGMAC_MTL_FIFO_SIZE_512  = 0x01,
+       XGMAC_MTL_FIFO_SIZE_1K   = 0x03,
+       XGMAC_MTL_FIFO_SIZE_2K   = 0x07,
+       XGMAC_MTL_FIFO_SIZE_4K   = 0x0f,
+       XGMAC_MTL_FIFO_SIZE_8K   = 0x1f,
+       XGMAC_MTL_FIFO_SIZE_16K  = 0x3f,
+       XGMAC_MTL_FIFO_SIZE_32K  = 0x7f,
+       XGMAC_MTL_FIFO_SIZE_64K  = 0xff,
+       XGMAC_MTL_FIFO_SIZE_128K = 0x1ff,
+       XGMAC_MTL_FIFO_SIZE_256K = 0x3ff,
+};
+
+struct xgbe_mmc_stats {
+       /* Tx Stats */
+       u64 txoctetcount_gb;
+       u64 txframecount_gb;
+       u64 txbroadcastframes_g;
+       u64 txmulticastframes_g;
+       u64 tx64octets_gb;
+       u64 tx65to127octets_gb;
+       u64 tx128to255octets_gb;
+       u64 tx256to511octets_gb;
+       u64 tx512to1023octets_gb;
+       u64 tx1024tomaxoctets_gb;
+       u64 txunicastframes_gb;
+       u64 txmulticastframes_gb;
+       u64 txbroadcastframes_gb;
+       u64 txunderflowerror;
+       u64 txoctetcount_g;
+       u64 txframecount_g;
+       u64 txpauseframes;
+       u64 txvlanframes_g;
+
+       /* Rx Stats */
+       u64 rxframecount_gb;
+       u64 rxoctetcount_gb;
+       u64 rxoctetcount_g;
+       u64 rxbroadcastframes_g;
+       u64 rxmulticastframes_g;
+       u64 rxcrcerror;
+       u64 rxrunterror;
+       u64 rxjabbererror;
+       u64 rxundersize_g;
+       u64 rxoversize_g;
+       u64 rx64octets_gb;
+       u64 rx65to127octets_gb;
+       u64 rx128to255octets_gb;
+       u64 rx256to511octets_gb;
+       u64 rx512to1023octets_gb;
+       u64 rx1024tomaxoctets_gb;
+       u64 rxunicastframes_g;
+       u64 rxlengtherror;
+       u64 rxoutofrangetype;
+       u64 rxpauseframes;
+       u64 rxfifooverflow;
+       u64 rxvlanframes_gb;
+       u64 rxwatchdogerror;
+};
+
+struct xgbe_hw_if {
+       int (*tx_complete)(struct xgbe_ring_desc *);
+
+       int (*set_promiscuous_mode)(struct xgbe_prv_data *, unsigned int);
+       int (*set_all_multicast_mode)(struct xgbe_prv_data *, unsigned int);
+       int (*set_addn_mac_addrs)(struct xgbe_prv_data *, unsigned int);
+       int (*set_mac_address)(struct xgbe_prv_data *, u8 *addr);
+
+       int (*enable_rx_csum)(struct xgbe_prv_data *);
+       int (*disable_rx_csum)(struct xgbe_prv_data *);
+
+       int (*enable_rx_vlan_stripping)(struct xgbe_prv_data *);
+       int (*disable_rx_vlan_stripping)(struct xgbe_prv_data *);
+
+       int (*read_mmd_regs)(struct xgbe_prv_data *, int, int);
+       void (*write_mmd_regs)(struct xgbe_prv_data *, int, int, int);
+       int (*set_gmii_speed)(struct xgbe_prv_data *);
+       int (*set_gmii_2500_speed)(struct xgbe_prv_data *);
+       int (*set_xgmii_speed)(struct xgbe_prv_data *);
+
+       void (*enable_tx)(struct xgbe_prv_data *);
+       void (*disable_tx)(struct xgbe_prv_data *);
+       void (*enable_rx)(struct xgbe_prv_data *);
+       void (*disable_rx)(struct xgbe_prv_data *);
+
+       void (*powerup_tx)(struct xgbe_prv_data *);
+       void (*powerdown_tx)(struct xgbe_prv_data *);
+       void (*powerup_rx)(struct xgbe_prv_data *);
+       void (*powerdown_rx)(struct xgbe_prv_data *);
+
+       int (*init)(struct xgbe_prv_data *);
+       int (*exit)(struct xgbe_prv_data *);
+
+       int (*enable_int)(struct xgbe_channel *, enum xgbe_int);
+       int (*disable_int)(struct xgbe_channel *, enum xgbe_int);
+       void (*pre_xmit)(struct xgbe_channel *);
+       int (*dev_read)(struct xgbe_channel *);
+       void (*tx_desc_init)(struct xgbe_channel *);
+       void (*rx_desc_init)(struct xgbe_channel *);
+       void (*rx_desc_reset)(struct xgbe_ring_data *);
+       void (*tx_desc_reset)(struct xgbe_ring_data *);
+       int (*is_last_desc)(struct xgbe_ring_desc *);
+       int (*is_context_desc)(struct xgbe_ring_desc *);
+
+       /* For FLOW ctrl */
+       int (*config_tx_flow_control)(struct xgbe_prv_data *);
+       int (*config_rx_flow_control)(struct xgbe_prv_data *);
+
+       /* For RX coalescing */
+       int (*config_rx_coalesce)(struct xgbe_prv_data *);
+       int (*config_tx_coalesce)(struct xgbe_prv_data *);
+       unsigned int (*usec_to_riwt)(struct xgbe_prv_data *, unsigned int);
+       unsigned int (*riwt_to_usec)(struct xgbe_prv_data *, unsigned int);
+
+       /* For RX and TX threshold config */
+       int (*config_rx_threshold)(struct xgbe_prv_data *, unsigned int);
+       int (*config_tx_threshold)(struct xgbe_prv_data *, unsigned int);
+
+       /* For RX and TX Store and Forward Mode config */
+       int (*config_rsf_mode)(struct xgbe_prv_data *, unsigned int);
+       int (*config_tsf_mode)(struct xgbe_prv_data *, unsigned int);
+
+       /* For TX DMA Operate on Second Frame config */
+       int (*config_osp_mode)(struct xgbe_prv_data *);
+
+       /* For RX and TX PBL config */
+       int (*config_rx_pbl_val)(struct xgbe_prv_data *);
+       int (*get_rx_pbl_val)(struct xgbe_prv_data *);
+       int (*config_tx_pbl_val)(struct xgbe_prv_data *);
+       int (*get_tx_pbl_val)(struct xgbe_prv_data *);
+       int (*config_pblx8)(struct xgbe_prv_data *);
+
+       /* For MMC statistics */
+       void (*rx_mmc_int)(struct xgbe_prv_data *);
+       void (*tx_mmc_int)(struct xgbe_prv_data *);
+       void (*read_mmc_stats)(struct xgbe_prv_data *);
+};
+
+struct xgbe_desc_if {
+       int (*alloc_ring_resources)(struct xgbe_prv_data *);
+       void (*free_ring_resources)(struct xgbe_prv_data *);
+       int (*map_tx_skb)(struct xgbe_channel *, struct sk_buff *);
+       void (*realloc_skb)(struct xgbe_channel *);
+       void (*unmap_skb)(struct xgbe_prv_data *, struct xgbe_ring_data *);
+       void (*wrapper_tx_desc_init)(struct xgbe_prv_data *);
+       void (*wrapper_rx_desc_init)(struct xgbe_prv_data *);
+};
+
+/* This structure contains flags that indicate what hardware features
+ * or configurations are present in the device.
+ */
+struct xgbe_hw_features {
+       /* HW Feature Register0 */
+       unsigned int gmii;              /* 1000 Mbps support */
+       unsigned int vlhash;            /* VLAN Hash Filter */
+       unsigned int sma;               /* SMA(MDIO) Interface */
+       unsigned int rwk;               /* PMT remote wake-up packet */
+       unsigned int mgk;               /* PMT magic packet */
+       unsigned int mmc;               /* RMON module */
+       unsigned int aoe;               /* ARP Offload */
+       unsigned int ts;                /* IEEE 1588-2008 Adavanced Timestamp */
+       unsigned int eee;               /* Energy Efficient Ethernet */
+       unsigned int tx_coe;            /* Tx Checksum Offload */
+       unsigned int rx_coe;            /* Rx Checksum Offload */
+       unsigned int addn_mac;          /* Additional MAC Addresses */
+       unsigned int ts_src;            /* Timestamp Source */
+       unsigned int sa_vlan_ins;       /* Source Address or VLAN Insertion */
+
+       /* HW Feature Register1 */
+       unsigned int rx_fifo_size;      /* MTL Receive FIFO Size */
+       unsigned int tx_fifo_size;      /* MTL Transmit FIFO Size */
+       unsigned int adv_ts_hi;         /* Advance Timestamping High Word */
+       unsigned int dcb;               /* DCB Feature */
+       unsigned int sph;               /* Split Header Feature */
+       unsigned int tso;               /* TCP Segmentation Offload */
+       unsigned int dma_debug;         /* DMA Debug Registers */
+       unsigned int rss;               /* Receive Side Scaling */
+       unsigned int hash_table_size;   /* Hash Table Size */
+       unsigned int l3l4_filter_num;   /* Number of L3-L4 Filters */
+
+       /* HW Feature Register2 */
+       unsigned int rx_q_cnt;          /* Number of MTL Receive Queues */
+       unsigned int tx_q_cnt;          /* Number of MTL Transmit Queues */
+       unsigned int rx_ch_cnt;         /* Number of DMA Receive Channels */
+       unsigned int tx_ch_cnt;         /* Number of DMA Transmit Channels */
+       unsigned int pps_out_num;       /* Number of PPS outputs */
+       unsigned int aux_snap_num;      /* Number of Aux snapshot inputs */
+};
+
+struct xgbe_prv_data {
+       struct net_device *netdev;
+       struct platform_device *pdev;
+       struct device *dev;
+
+       /* XGMAC/XPCS related mmio registers */
+       void __iomem *xgmac_regs;       /* XGMAC CSRs */
+       void __iomem *xpcs_regs;        /* XPCS MMD registers */
+
+       /* Overall device lock */
+       spinlock_t lock;
+
+       /* XPCS indirect addressing mutex */
+       struct mutex xpcs_mutex;
+
+       int irq_number;
+
+       struct xgbe_hw_if hw_if;
+       struct xgbe_desc_if desc_if;
+
+       /* Rings for Tx/Rx on a DMA channel */
+       struct xgbe_channel *channel;
+       unsigned int channel_count;
+       unsigned int tx_ring_count;
+       unsigned int tx_desc_count;
+       unsigned int rx_ring_count;
+       unsigned int rx_desc_count;
+
+       /* Tx/Rx common settings */
+       unsigned int pblx8;
+
+       /* Tx settings */
+       unsigned int tx_sf_mode;
+       unsigned int tx_threshold;
+       unsigned int tx_pbl;
+       unsigned int tx_osp_mode;
+
+       /* Rx settings */
+       unsigned int rx_sf_mode;
+       unsigned int rx_threshold;
+       unsigned int rx_pbl;
+
+       /* Tx coalescing settings */
+       unsigned int tx_usecs;
+       unsigned int tx_frames;
+
+       /* Rx coalescing settings */
+       unsigned int rx_riwt;
+       unsigned int rx_frames;
+
+       /* Current MTU */
+       unsigned int rx_buf_size;
+
+       /* Flow control settings */
+       unsigned int pause_autoneg;
+       unsigned int tx_pause;
+       unsigned int rx_pause;
+
+       /* MDIO settings */
+       struct module *phy_module;
+       char *mii_bus_id;
+       struct mii_bus *mii;
+       int mdio_mmd;
+       struct phy_device *phydev;
+       int default_autoneg;
+       int default_speed;
+
+       /* Current PHY settings */
+       phy_interface_t phy_mode;
+       int phy_link;
+       int phy_speed;
+       unsigned int phy_tx_pause;
+       unsigned int phy_rx_pause;
+
+       /* Netdev related settings */
+       netdev_features_t netdev_features;
+       struct napi_struct napi;
+       struct xgbe_mmc_stats mmc_stats;
+
+       /* System clock value used for Rx watchdog */
+       struct clk *sysclock;
+
+       /* Hardware features of the device */
+       struct xgbe_hw_features hw_feat;
+
+       /* Device restart work structure */
+       struct work_struct restart_work;
+
+       /* Keeps track of power mode */
+       unsigned int power_down;
+
+#ifdef CONFIG_DEBUG_FS
+       struct dentry *xgbe_debugfs;
+
+       unsigned int debugfs_xgmac_reg;
+
+       unsigned int debugfs_xpcs_mmd;
+       unsigned int debugfs_xpcs_reg;
+#endif
+};
+
+/* Function prototypes*/
+
+void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *);
+void xgbe_init_function_ptrs_desc(struct xgbe_desc_if *);
+struct net_device_ops *xgbe_get_netdev_ops(void);
+struct ethtool_ops *xgbe_get_ethtool_ops(void);
+
+int xgbe_mdio_register(struct xgbe_prv_data *);
+void xgbe_mdio_unregister(struct xgbe_prv_data *);
+void xgbe_dump_phy_registers(struct xgbe_prv_data *);
+void xgbe_dump_tx_desc(struct xgbe_ring *, unsigned int, unsigned int,
+                      unsigned int);
+void xgbe_dump_rx_desc(struct xgbe_ring *, struct xgbe_ring_desc *,
+                      unsigned int);
+void xgbe_print_pkt(struct net_device *, struct sk_buff *, bool);
+void xgbe_get_all_hw_features(struct xgbe_prv_data *);
+int xgbe_powerup(struct net_device *, unsigned int);
+int xgbe_powerdown(struct net_device *, unsigned int);
+void xgbe_init_rx_coalesce(struct xgbe_prv_data *);
+void xgbe_init_tx_coalesce(struct xgbe_prv_data *);
+
+#ifdef CONFIG_DEBUG_FS
+void xgbe_debugfs_init(struct xgbe_prv_data *);
+void xgbe_debugfs_exit(struct xgbe_prv_data *);
+#else
+static inline void xgbe_debugfs_init(struct xgbe_prv_data *pdata) {}
+static inline void xgbe_debugfs_exit(struct xgbe_prv_data *pdata) {}
+#endif /* CONFIG_DEBUG_FS */
+
+/* NOTE: Uncomment for TX and RX DESCRIPTOR DUMP in KERNEL LOG */
+#if 0
+#define XGMAC_ENABLE_TX_DESC_DUMP
+#define XGMAC_ENABLE_RX_DESC_DUMP
+#endif
+
+/* NOTE: Uncomment for TX and RX PACKET DUMP in KERNEL LOG */
+#if 0
+#define XGMAC_ENABLE_TX_PKT_DUMP
+#define XGMAC_ENABLE_RX_PKT_DUMP
+#endif
+
+/* NOTE: Uncomment for function trace log messages in KERNEL LOG */
+#if 0
+#define YDEBUG
+#define YDEBUG_MDIO
+#endif
+
+/* For debug prints */
+#ifdef YDEBUG
+#define DBGPR(x...) pr_alert(x)
+#define DBGPHY_REGS(x...) xgbe_dump_phy_registers(x)
+#else
+#define DBGPR(x...) do { } while (0)
+#define DBGPHY_REGS(x...) do { } while (0)
+#endif
+
+#ifdef YDEBUG_MDIO
+#define DBGPR_MDIO(x...) pr_alert(x)
+#else
+#define DBGPR_MDIO(x...) do { } while (0)
+#endif
+
+#endif
index d647a7d115acb2e7d40f72ee5010cd1e6ea79982..18e2faccebb0dcb98bc19bdc333561776aec6b95 100644 (file)
@@ -13,6 +13,7 @@
  *             Vineet Gupta
  */
 
+#include <linux/crc32.h>
 #include <linux/etherdevice.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
@@ -362,6 +363,15 @@ static irqreturn_t arc_emac_intr(int irq, void *dev_instance)
        return IRQ_HANDLED;
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void arc_emac_poll_controller(struct net_device *dev)
+{
+       disable_irq(dev->irq);
+       arc_emac_intr(dev->irq, dev);
+       enable_irq(dev->irq);
+}
+#endif
+
 /**
  * arc_emac_open - Open the network device.
  * @ndev:      Pointer to the network device.
@@ -450,6 +460,41 @@ static int arc_emac_open(struct net_device *ndev)
        return 0;
 }
 
+/**
+ * arc_emac_set_rx_mode - Change the receive filtering mode.
+ * @ndev:      Pointer to the network device.
+ *
+ * This function enables/disables promiscuous or all-multicast mode
+ * and updates the multicast filtering list of the network device.
+ */
+static void arc_emac_set_rx_mode(struct net_device *ndev)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+
+       if (ndev->flags & IFF_PROMISC) {
+               arc_reg_or(priv, R_CTRL, PROM_MASK);
+       } else {
+               arc_reg_clr(priv, R_CTRL, PROM_MASK);
+
+               if (ndev->flags & IFF_ALLMULTI) {
+                       arc_reg_set(priv, R_LAFL, ~0);
+                       arc_reg_set(priv, R_LAFH, ~0);
+               } else {
+                       struct netdev_hw_addr *ha;
+                       unsigned int filter[2] = { 0, 0 };
+                       int bit;
+
+                       netdev_for_each_mc_addr(ha, ndev) {
+                               bit = ether_crc_le(ETH_ALEN, ha->addr) >> 26;
+                               filter[bit >> 5] |= 1 << (bit & 31);
+                       }
+
+                       arc_reg_set(priv, R_LAFL, filter[0]);
+                       arc_reg_set(priv, R_LAFH, filter[1]);
+               }
+       }
+}
+
 /**
  * arc_emac_stop - Close the network device.
  * @ndev:      Pointer to the network device.
@@ -620,6 +665,10 @@ static const struct net_device_ops arc_emac_netdev_ops = {
        .ndo_start_xmit         = arc_emac_tx,
        .ndo_set_mac_address    = arc_emac_set_address,
        .ndo_get_stats          = arc_emac_stats,
+       .ndo_set_rx_mode        = arc_emac_set_rx_mode,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = arc_emac_poll_controller,
+#endif
 };
 
 static int arc_emac_probe(struct platform_device *pdev)
index 17bb9ce96260df20eba44a9f215778a62c28373e..49faa97a30c364185f7e988a7e64be5529a62382 100644 (file)
@@ -1302,7 +1302,7 @@ static int alx_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        }
 
        netdev->netdev_ops = &alx_netdev_ops;
-       SET_ETHTOOL_OPS(netdev, &alx_ethtool_ops);
+       netdev->ethtool_ops = &alx_ethtool_ops;
        netdev->irq = pdev->irq;
        netdev->watchdog_timeo = ALX_WATCHDOG_TIME;
 
index 859ea844ba0ff7c292994a59446951680a2e7b79..48694c239d5cee024055731d2aa586e57fe529e5 100644 (file)
@@ -56,8 +56,8 @@ static int atl1c_get_settings(struct net_device *netdev,
                else
                        ecmd->duplex = DUPLEX_HALF;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
 
        ecmd->autoneg = AUTONEG_ENABLE;
@@ -305,5 +305,5 @@ static const struct ethtool_ops atl1c_ethtool_ops = {
 
 void atl1c_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &atl1c_ethtool_ops);
+       netdev->ethtool_ops = &atl1c_ethtool_ops;
 }
index 82b23861bf5598698f3a21232e37eaf693f83c6e..1be072f4afc2a261b5504302db5dba50b81ad268 100644 (file)
@@ -57,8 +57,8 @@ static int atl1e_get_settings(struct net_device *netdev,
                else
                        ecmd->duplex = DUPLEX_HALF;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
 
        ecmd->autoneg = AUTONEG_ENABLE;
@@ -388,5 +388,5 @@ static const struct ethtool_ops atl1e_ethtool_ops = {
 
 void atl1e_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &atl1e_ethtool_ops);
+       netdev->ethtool_ops = &atl1e_ethtool_ops;
 }
index dfd0e91fa726852818b48b2853e9d60533d01dd7..b460db7919a28866c5f37db08c9a30b71a857840 100644 (file)
@@ -3258,8 +3258,8 @@ static int atl1_get_settings(struct net_device *netdev,
                else
                        ecmd->duplex = DUPLEX_HALF;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
        if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
            hw->media_type == MEDIA_TYPE_1000M_FULL)
index 78befb522a528268fae32c68649ead0a01263366..6746bd7171460100a4d224e55bdcbe62a4efcecf 100644 (file)
@@ -1396,7 +1396,7 @@ static int atl2_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        atl2_setup_pcicmd(pdev);
 
        netdev->netdev_ops = &atl2_netdev_ops;
-       SET_ETHTOOL_OPS(netdev, &atl2_ethtool_ops);
+       netdev->ethtool_ops = &atl2_ethtool_ops;
        netdev->watchdog_timeo = 5 * HZ;
        strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);
 
@@ -1769,8 +1769,8 @@ static int atl2_get_settings(struct net_device *netdev,
                else
                        ecmd->duplex = DUPLEX_HALF;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
 
        ecmd->autoneg = AUTONEG_ENABLE;
index 85dbddd03722b20a861f53cba7fe00b6cb66f3db..3e488094b0731811459c66dcb0517d00cb7dfbbe 100644 (file)
@@ -150,4 +150,15 @@ config BGMAC
          In case of using this driver on BCM4706 it's also requires to enable
          BCMA_DRIVER_GMAC_CMN to make it work.
 
+config SYSTEMPORT
+       tristate "Broadcom SYSTEMPORT internal MAC support"
+       depends on OF
+       select MII
+       select PHYLIB
+       select FIXED_PHY if SYSTEMPORT=y
+       help
+         This driver supports the built-in Ethernet MACs found in the
+         Broadcom BCM7xxx Set Top Box family chipset using an internal
+         Ethernet switch.
+
 endif # NET_VENDOR_BROADCOM
index fd639a0d4c7d64b2b7db5eb084087502e3c6d63a..e2a958a657e0bb8f816d205e0792d3fdfbfc70a4 100644 (file)
@@ -11,3 +11,4 @@ obj-$(CONFIG_BNX2X) += bnx2x/
 obj-$(CONFIG_SB1250_MAC) += sb1250-mac.o
 obj-$(CONFIG_TIGON3) += tg3.o
 obj-$(CONFIG_BGMAC) += bgmac.o
+obj-$(CONFIG_SYSTEMPORT) += bcmsysport.o
index 05ba6258901794ab51842ddc8d630f4d1286798d..ca5a20a48b14cdb48ccd75aaa1c34715a8dcfee3 100644 (file)
@@ -2380,7 +2380,7 @@ static int b44_init_one(struct ssb_device *sdev,
        netif_napi_add(dev, &bp->napi, b44_poll, 64);
        dev->watchdog_timeo = B44_TX_TIMEOUT;
        dev->irq = sdev->irq;
-       SET_ETHTOOL_OPS(dev, &b44_ethtool_ops);
+       dev->ethtool_ops = &b44_ethtool_ops;
 
        err = ssb_bus_powerup(sdev->bus, 0);
        if (err) {
index a7d11f5565d69342ad296471a9a5d44f8d7c51d5..3e8d1a88ed3d7b597298100798e5286449ffafe8 100644 (file)
@@ -1315,8 +1315,7 @@ static const struct bcm_enet_stats bcm_enet_gstrings_stats[] = {
 
 };
 
-#define BCM_ENET_STATS_LEN     \
-       (sizeof(bcm_enet_gstrings_stats) / sizeof(struct bcm_enet_stats))
+#define BCM_ENET_STATS_LEN     ARRAY_SIZE(bcm_enet_gstrings_stats)
 
 static const u32 unused_mib_regs[] = {
        ETH_MIB_TX_ALL_OCTETS,
@@ -1898,7 +1897,7 @@ static int bcm_enet_probe(struct platform_device *pdev)
        dev->netdev_ops = &bcm_enet_ops;
        netif_napi_add(dev, &priv->napi, bcm_enet_poll, 16);
 
-       SET_ETHTOOL_OPS(dev, &bcm_enet_ethtool_ops);
+       dev->ethtool_ops = &bcm_enet_ethtool_ops;
        SET_NETDEV_DEV(dev, &pdev->dev);
 
        ret = register_netdev(dev);
@@ -2784,7 +2783,7 @@ static int bcm_enetsw_probe(struct platform_device *pdev)
        /* 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);
+       dev->ethtool_ops = &bcm_enetsw_ethtool_ops;
        SET_NETDEV_DEV(dev, &pdev->dev);
 
        spin_lock_init(&priv->enetsw_mdio_lock);
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c
new file mode 100644 (file)
index 0000000..141160e
--- /dev/null
@@ -0,0 +1,1654 @@
+/*
+ * Broadcom BCM7xxx System Port Ethernet MAC driver
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
+#include <linux/phy.h>
+#include <linux/phy_fixed.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+
+#include "bcmsysport.h"
+
+/* I/O accessors register helpers */
+#define BCM_SYSPORT_IO_MACRO(name, offset) \
+static inline u32 name##_readl(struct bcm_sysport_priv *priv, u32 off) \
+{                                                                      \
+       u32 reg = __raw_readl(priv->base + offset + off);               \
+       return reg;                                                     \
+}                                                                      \
+static inline void name##_writel(struct bcm_sysport_priv *priv,                \
+                                 u32 val, u32 off)                     \
+{                                                                      \
+       __raw_writel(val, priv->base + offset + off);                   \
+}                                                                      \
+
+BCM_SYSPORT_IO_MACRO(intrl2_0, SYS_PORT_INTRL2_0_OFFSET);
+BCM_SYSPORT_IO_MACRO(intrl2_1, SYS_PORT_INTRL2_1_OFFSET);
+BCM_SYSPORT_IO_MACRO(umac, SYS_PORT_UMAC_OFFSET);
+BCM_SYSPORT_IO_MACRO(tdma, SYS_PORT_TDMA_OFFSET);
+BCM_SYSPORT_IO_MACRO(rdma, SYS_PORT_RDMA_OFFSET);
+BCM_SYSPORT_IO_MACRO(rxchk, SYS_PORT_RXCHK_OFFSET);
+BCM_SYSPORT_IO_MACRO(txchk, SYS_PORT_TXCHK_OFFSET);
+BCM_SYSPORT_IO_MACRO(rbuf, SYS_PORT_RBUF_OFFSET);
+BCM_SYSPORT_IO_MACRO(tbuf, SYS_PORT_TBUF_OFFSET);
+BCM_SYSPORT_IO_MACRO(topctrl, SYS_PORT_TOPCTRL_OFFSET);
+
+/* L2-interrupt masking/unmasking helpers, does automatic saving of the applied
+ * mask in a software copy to avoid CPU_MASK_STATUS reads in hot-paths.
+  */
+#define BCM_SYSPORT_INTR_L2(which)     \
+static inline void intrl2_##which##_mask_clear(struct bcm_sysport_priv *priv, \
+                                               u32 mask)               \
+{                                                                      \
+       intrl2_##which##_writel(priv, mask, INTRL2_CPU_MASK_CLEAR);     \
+       priv->irq##which##_mask &= ~(mask);                             \
+}                                                                      \
+static inline void intrl2_##which##_mask_set(struct bcm_sysport_priv *priv, \
+                                               u32 mask)               \
+{                                                                      \
+       intrl2_## which##_writel(priv, mask, INTRL2_CPU_MASK_SET);      \
+       priv->irq##which##_mask |= (mask);                              \
+}                                                                      \
+
+BCM_SYSPORT_INTR_L2(0)
+BCM_SYSPORT_INTR_L2(1)
+
+/* Register accesses to GISB/RBUS registers are expensive (few hundred
+ * nanoseconds), so keep the check for 64-bits explicit here to save
+ * one register write per-packet on 32-bits platforms.
+ */
+static inline void dma_desc_set_addr(struct bcm_sysport_priv *priv,
+                                    void __iomem *d,
+                                    dma_addr_t addr)
+{
+#ifdef CONFIG_PHYS_ADDR_T_64BIT
+       __raw_writel(upper_32_bits(addr) & DESC_ADDR_HI_MASK,
+                       d + DESC_ADDR_HI_STATUS_LEN);
+#endif
+       __raw_writel(lower_32_bits(addr), d + DESC_ADDR_LO);
+}
+
+static inline void tdma_port_write_desc_addr(struct bcm_sysport_priv *priv,
+                                               struct dma_desc *desc,
+                                               unsigned int port)
+{
+       /* Ports are latched, so write upper address first */
+       tdma_writel(priv, desc->addr_status_len, TDMA_WRITE_PORT_HI(port));
+       tdma_writel(priv, desc->addr_lo, TDMA_WRITE_PORT_LO(port));
+}
+
+/* Ethtool operations */
+static int bcm_sysport_set_settings(struct net_device *dev,
+                                   struct ethtool_cmd *cmd)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+
+       if (!netif_running(dev))
+               return -EINVAL;
+
+       return phy_ethtool_sset(priv->phydev, cmd);
+}
+
+static int bcm_sysport_get_settings(struct net_device *dev,
+                                       struct ethtool_cmd *cmd)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+
+       if (!netif_running(dev))
+               return -EINVAL;
+
+       return phy_ethtool_gset(priv->phydev, cmd);
+}
+
+static int bcm_sysport_set_rx_csum(struct net_device *dev,
+                                       netdev_features_t wanted)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+       u32 reg;
+
+       priv->rx_csum_en = !!(wanted & NETIF_F_RXCSUM);
+       reg = rxchk_readl(priv, RXCHK_CONTROL);
+       if (priv->rx_csum_en)
+               reg |= RXCHK_EN;
+       else
+               reg &= ~RXCHK_EN;
+
+       /* If UniMAC forwards CRC, we need to skip over it to get
+        * a valid CHK bit to be set in the per-packet status word
+        */
+       if (priv->rx_csum_en && priv->crc_fwd)
+               reg |= RXCHK_SKIP_FCS;
+       else
+               reg &= ~RXCHK_SKIP_FCS;
+
+       rxchk_writel(priv, reg, RXCHK_CONTROL);
+
+       return 0;
+}
+
+static int bcm_sysport_set_tx_csum(struct net_device *dev,
+                                       netdev_features_t wanted)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+       u32 reg;
+
+       /* Hardware transmit checksum requires us to enable the Transmit status
+        * block prepended to the packet contents
+        */
+       priv->tsb_en = !!(wanted & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM));
+       reg = tdma_readl(priv, TDMA_CONTROL);
+       if (priv->tsb_en)
+               reg |= TSB_EN;
+       else
+               reg &= ~TSB_EN;
+       tdma_writel(priv, reg, TDMA_CONTROL);
+
+       return 0;
+}
+
+static int bcm_sysport_set_features(struct net_device *dev,
+                                       netdev_features_t features)
+{
+       netdev_features_t changed = features ^ dev->features;
+       netdev_features_t wanted = dev->wanted_features;
+       int ret = 0;
+
+       if (changed & NETIF_F_RXCSUM)
+               ret = bcm_sysport_set_rx_csum(dev, wanted);
+       if (changed & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))
+               ret = bcm_sysport_set_tx_csum(dev, wanted);
+
+       return ret;
+}
+
+/* Hardware counters must be kept in sync because the order/offset
+ * is important here (order in structure declaration = order in hardware)
+ */
+static const struct bcm_sysport_stats bcm_sysport_gstrings_stats[] = {
+       /* general stats */
+       STAT_NETDEV(rx_packets),
+       STAT_NETDEV(tx_packets),
+       STAT_NETDEV(rx_bytes),
+       STAT_NETDEV(tx_bytes),
+       STAT_NETDEV(rx_errors),
+       STAT_NETDEV(tx_errors),
+       STAT_NETDEV(rx_dropped),
+       STAT_NETDEV(tx_dropped),
+       STAT_NETDEV(multicast),
+       /* UniMAC RSV counters */
+       STAT_MIB_RX("rx_64_octets", mib.rx.pkt_cnt.cnt_64),
+       STAT_MIB_RX("rx_65_127_oct", mib.rx.pkt_cnt.cnt_127),
+       STAT_MIB_RX("rx_128_255_oct", mib.rx.pkt_cnt.cnt_255),
+       STAT_MIB_RX("rx_256_511_oct", mib.rx.pkt_cnt.cnt_511),
+       STAT_MIB_RX("rx_512_1023_oct", mib.rx.pkt_cnt.cnt_1023),
+       STAT_MIB_RX("rx_1024_1518_oct", mib.rx.pkt_cnt.cnt_1518),
+       STAT_MIB_RX("rx_vlan_1519_1522_oct", mib.rx.pkt_cnt.cnt_mgv),
+       STAT_MIB_RX("rx_1522_2047_oct", mib.rx.pkt_cnt.cnt_2047),
+       STAT_MIB_RX("rx_2048_4095_oct", mib.rx.pkt_cnt.cnt_4095),
+       STAT_MIB_RX("rx_4096_9216_oct", mib.rx.pkt_cnt.cnt_9216),
+       STAT_MIB_RX("rx_pkts", mib.rx.pkt),
+       STAT_MIB_RX("rx_bytes", mib.rx.bytes),
+       STAT_MIB_RX("rx_multicast", mib.rx.mca),
+       STAT_MIB_RX("rx_broadcast", mib.rx.bca),
+       STAT_MIB_RX("rx_fcs", mib.rx.fcs),
+       STAT_MIB_RX("rx_control", mib.rx.cf),
+       STAT_MIB_RX("rx_pause", mib.rx.pf),
+       STAT_MIB_RX("rx_unknown", mib.rx.uo),
+       STAT_MIB_RX("rx_align", mib.rx.aln),
+       STAT_MIB_RX("rx_outrange", mib.rx.flr),
+       STAT_MIB_RX("rx_code", mib.rx.cde),
+       STAT_MIB_RX("rx_carrier", mib.rx.fcr),
+       STAT_MIB_RX("rx_oversize", mib.rx.ovr),
+       STAT_MIB_RX("rx_jabber", mib.rx.jbr),
+       STAT_MIB_RX("rx_mtu_err", mib.rx.mtue),
+       STAT_MIB_RX("rx_good_pkts", mib.rx.pok),
+       STAT_MIB_RX("rx_unicast", mib.rx.uc),
+       STAT_MIB_RX("rx_ppp", mib.rx.ppp),
+       STAT_MIB_RX("rx_crc", mib.rx.rcrc),
+       /* UniMAC TSV counters */
+       STAT_MIB_TX("tx_64_octets", mib.tx.pkt_cnt.cnt_64),
+       STAT_MIB_TX("tx_65_127_oct", mib.tx.pkt_cnt.cnt_127),
+       STAT_MIB_TX("tx_128_255_oct", mib.tx.pkt_cnt.cnt_255),
+       STAT_MIB_TX("tx_256_511_oct", mib.tx.pkt_cnt.cnt_511),
+       STAT_MIB_TX("tx_512_1023_oct", mib.tx.pkt_cnt.cnt_1023),
+       STAT_MIB_TX("tx_1024_1518_oct", mib.tx.pkt_cnt.cnt_1518),
+       STAT_MIB_TX("tx_vlan_1519_1522_oct", mib.tx.pkt_cnt.cnt_mgv),
+       STAT_MIB_TX("tx_1522_2047_oct", mib.tx.pkt_cnt.cnt_2047),
+       STAT_MIB_TX("tx_2048_4095_oct", mib.tx.pkt_cnt.cnt_4095),
+       STAT_MIB_TX("tx_4096_9216_oct", mib.tx.pkt_cnt.cnt_9216),
+       STAT_MIB_TX("tx_pkts", mib.tx.pkts),
+       STAT_MIB_TX("tx_multicast", mib.tx.mca),
+       STAT_MIB_TX("tx_broadcast", mib.tx.bca),
+       STAT_MIB_TX("tx_pause", mib.tx.pf),
+       STAT_MIB_TX("tx_control", mib.tx.cf),
+       STAT_MIB_TX("tx_fcs_err", mib.tx.fcs),
+       STAT_MIB_TX("tx_oversize", mib.tx.ovr),
+       STAT_MIB_TX("tx_defer", mib.tx.drf),
+       STAT_MIB_TX("tx_excess_defer", mib.tx.edf),
+       STAT_MIB_TX("tx_single_col", mib.tx.scl),
+       STAT_MIB_TX("tx_multi_col", mib.tx.mcl),
+       STAT_MIB_TX("tx_late_col", mib.tx.lcl),
+       STAT_MIB_TX("tx_excess_col", mib.tx.ecl),
+       STAT_MIB_TX("tx_frags", mib.tx.frg),
+       STAT_MIB_TX("tx_total_col", mib.tx.ncl),
+       STAT_MIB_TX("tx_jabber", mib.tx.jbr),
+       STAT_MIB_TX("tx_bytes", mib.tx.bytes),
+       STAT_MIB_TX("tx_good_pkts", mib.tx.pok),
+       STAT_MIB_TX("tx_unicast", mib.tx.uc),
+       /* UniMAC RUNT counters */
+       STAT_RUNT("rx_runt_pkts", mib.rx_runt_cnt),
+       STAT_RUNT("rx_runt_valid_fcs", mib.rx_runt_fcs),
+       STAT_RUNT("rx_runt_inval_fcs_align", mib.rx_runt_fcs_align),
+       STAT_RUNT("rx_runt_bytes", mib.rx_runt_bytes),
+       /* RXCHK misc statistics */
+       STAT_RXCHK("rxchk_bad_csum", mib.rxchk_bad_csum, RXCHK_BAD_CSUM_CNTR),
+       STAT_RXCHK("rxchk_other_pkt_disc", mib.rxchk_other_pkt_disc,
+                       RXCHK_OTHER_DISC_CNTR),
+       /* RBUF misc statistics */
+       STAT_RBUF("rbuf_ovflow_cnt", mib.rbuf_ovflow_cnt, RBUF_OVFL_DISC_CNTR),
+       STAT_RBUF("rbuf_err_cnt", mib.rbuf_err_cnt, RBUF_ERR_PKT_CNTR),
+};
+
+#define BCM_SYSPORT_STATS_LEN  ARRAY_SIZE(bcm_sysport_gstrings_stats)
+
+static void bcm_sysport_get_drvinfo(struct net_device *dev,
+                                       struct ethtool_drvinfo *info)
+{
+       strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
+       strlcpy(info->version, "0.1", sizeof(info->version));
+       strlcpy(info->bus_info, "platform", sizeof(info->bus_info));
+       info->n_stats = BCM_SYSPORT_STATS_LEN;
+}
+
+static u32 bcm_sysport_get_msglvl(struct net_device *dev)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+
+       return priv->msg_enable;
+}
+
+static void bcm_sysport_set_msglvl(struct net_device *dev, u32 enable)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+
+       priv->msg_enable = enable;
+}
+
+static int bcm_sysport_get_sset_count(struct net_device *dev, int string_set)
+{
+       switch (string_set) {
+       case ETH_SS_STATS:
+               return BCM_SYSPORT_STATS_LEN;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static void bcm_sysport_get_strings(struct net_device *dev,
+                                       u32 stringset, u8 *data)
+{
+       int i;
+
+       switch (stringset) {
+       case ETH_SS_STATS:
+               for (i = 0; i < BCM_SYSPORT_STATS_LEN; i++) {
+                       memcpy(data + i * ETH_GSTRING_LEN,
+                               bcm_sysport_gstrings_stats[i].stat_string,
+                               ETH_GSTRING_LEN);
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+static void bcm_sysport_update_mib_counters(struct bcm_sysport_priv *priv)
+{
+       int i, j = 0;
+
+       for (i = 0; i < BCM_SYSPORT_STATS_LEN; i++) {
+               const struct bcm_sysport_stats *s;
+               u8 offset = 0;
+               u32 val = 0;
+               char *p;
+
+               s = &bcm_sysport_gstrings_stats[i];
+               switch (s->type) {
+               case BCM_SYSPORT_STAT_NETDEV:
+                       continue;
+               case BCM_SYSPORT_STAT_MIB_RX:
+               case BCM_SYSPORT_STAT_MIB_TX:
+               case BCM_SYSPORT_STAT_RUNT:
+                       if (s->type != BCM_SYSPORT_STAT_MIB_RX)
+                               offset = UMAC_MIB_STAT_OFFSET;
+                       val = umac_readl(priv, UMAC_MIB_START + j + offset);
+                       break;
+               case BCM_SYSPORT_STAT_RXCHK:
+                       val = rxchk_readl(priv, s->reg_offset);
+                       if (val == ~0)
+                               rxchk_writel(priv, 0, s->reg_offset);
+                       break;
+               case BCM_SYSPORT_STAT_RBUF:
+                       val = rbuf_readl(priv, s->reg_offset);
+                       if (val == ~0)
+                               rbuf_writel(priv, 0, s->reg_offset);
+                       break;
+               }
+
+               j += s->stat_sizeof;
+               p = (char *)priv + s->stat_offset;
+               *(u32 *)p = val;
+       }
+
+       netif_dbg(priv, hw, priv->netdev, "updated MIB counters\n");
+}
+
+static void bcm_sysport_get_stats(struct net_device *dev,
+                                       struct ethtool_stats *stats, u64 *data)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+       int i;
+
+       if (netif_running(dev))
+               bcm_sysport_update_mib_counters(priv);
+
+       for (i =  0; i < BCM_SYSPORT_STATS_LEN; i++) {
+               const struct bcm_sysport_stats *s;
+               char *p;
+
+               s = &bcm_sysport_gstrings_stats[i];
+               if (s->type == BCM_SYSPORT_STAT_NETDEV)
+                       p = (char *)&dev->stats;
+               else
+                       p = (char *)priv;
+               p += s->stat_offset;
+               data[i] = *(u32 *)p;
+       }
+}
+
+static void bcm_sysport_free_cb(struct bcm_sysport_cb *cb)
+{
+       dev_kfree_skb_any(cb->skb);
+       cb->skb = NULL;
+       dma_unmap_addr_set(cb, dma_addr, 0);
+}
+
+static int bcm_sysport_rx_refill(struct bcm_sysport_priv *priv,
+                                struct bcm_sysport_cb *cb)
+{
+       struct device *kdev = &priv->pdev->dev;
+       struct net_device *ndev = priv->netdev;
+       dma_addr_t mapping;
+       int ret;
+
+       cb->skb = netdev_alloc_skb(priv->netdev, RX_BUF_LENGTH);
+       if (!cb->skb) {
+               netif_err(priv, rx_err, ndev, "SKB alloc failed\n");
+               return -ENOMEM;
+       }
+
+       mapping = dma_map_single(kdev, cb->skb->data,
+                               RX_BUF_LENGTH, DMA_FROM_DEVICE);
+       ret = dma_mapping_error(kdev, mapping);
+       if (ret) {
+               bcm_sysport_free_cb(cb);
+               netif_err(priv, rx_err, ndev, "DMA mapping failure\n");
+               return ret;
+       }
+
+       dma_unmap_addr_set(cb, dma_addr, mapping);
+       dma_desc_set_addr(priv, priv->rx_bd_assign_ptr, mapping);
+
+       priv->rx_bd_assign_index++;
+       priv->rx_bd_assign_index &= (priv->num_rx_bds - 1);
+       priv->rx_bd_assign_ptr = priv->rx_bds +
+               (priv->rx_bd_assign_index * DESC_SIZE);
+
+       netif_dbg(priv, rx_status, ndev, "RX refill\n");
+
+       return 0;
+}
+
+static int bcm_sysport_alloc_rx_bufs(struct bcm_sysport_priv *priv)
+{
+       struct bcm_sysport_cb *cb;
+       int ret = 0;
+       unsigned int i;
+
+       for (i = 0; i < priv->num_rx_bds; i++) {
+               cb = &priv->rx_cbs[priv->rx_bd_assign_index];
+               if (cb->skb)
+                       continue;
+
+               ret = bcm_sysport_rx_refill(priv, cb);
+               if (ret)
+                       break;
+       }
+
+       return ret;
+}
+
+/* Poll the hardware for up to budget packets to process */
+static unsigned int bcm_sysport_desc_rx(struct bcm_sysport_priv *priv,
+                                       unsigned int budget)
+{
+       struct device *kdev = &priv->pdev->dev;
+       struct net_device *ndev = priv->netdev;
+       unsigned int processed = 0, to_process;
+       struct bcm_sysport_cb *cb;
+       struct sk_buff *skb;
+       unsigned int p_index;
+       u16 len, status;
+       struct bcm_rsb *rsb;
+
+       /* Determine how much we should process since last call */
+       p_index = rdma_readl(priv, RDMA_PROD_INDEX);
+       p_index &= RDMA_PROD_INDEX_MASK;
+
+       if (p_index < priv->rx_c_index)
+               to_process = (RDMA_CONS_INDEX_MASK + 1) -
+                       priv->rx_c_index + p_index;
+       else
+               to_process = p_index - priv->rx_c_index;
+
+       netif_dbg(priv, rx_status, ndev,
+                       "p_index=%d rx_c_index=%d to_process=%d\n",
+                       p_index, priv->rx_c_index, to_process);
+
+       while ((processed < to_process) &&
+               (processed < budget)) {
+
+               cb = &priv->rx_cbs[priv->rx_read_ptr];
+               skb = cb->skb;
+               dma_unmap_single(kdev, dma_unmap_addr(cb, dma_addr),
+                               RX_BUF_LENGTH, DMA_FROM_DEVICE);
+
+               /* Extract the Receive Status Block prepended */
+               rsb = (struct bcm_rsb *)skb->data;
+               len = (rsb->rx_status_len >> DESC_LEN_SHIFT) & DESC_LEN_MASK;
+               status = (rsb->rx_status_len >> DESC_STATUS_SHIFT) &
+                       DESC_STATUS_MASK;
+
+               processed++;
+               priv->rx_read_ptr++;
+               if (priv->rx_read_ptr == priv->num_rx_bds)
+                       priv->rx_read_ptr = 0;
+
+               netif_dbg(priv, rx_status, ndev,
+                               "p=%d, c=%d, rd_ptr=%d, len=%d, flag=0x%04x\n",
+                               p_index, priv->rx_c_index, priv->rx_read_ptr,
+                               len, status);
+
+               if (unlikely(!skb)) {
+                       netif_err(priv, rx_err, ndev, "out of memory!\n");
+                       ndev->stats.rx_dropped++;
+                       ndev->stats.rx_errors++;
+                       goto refill;
+               }
+
+               if (unlikely(!(status & DESC_EOP) || !(status & DESC_SOP))) {
+                       netif_err(priv, rx_status, ndev, "fragmented packet!\n");
+                       ndev->stats.rx_dropped++;
+                       ndev->stats.rx_errors++;
+                       bcm_sysport_free_cb(cb);
+                       goto refill;
+               }
+
+               if (unlikely(status & (RX_STATUS_ERR | RX_STATUS_OVFLOW))) {
+                       netif_err(priv, rx_err, ndev, "error packet\n");
+                       if (status & RX_STATUS_OVFLOW)
+                               ndev->stats.rx_over_errors++;
+                       ndev->stats.rx_dropped++;
+                       ndev->stats.rx_errors++;
+                       bcm_sysport_free_cb(cb);
+                       goto refill;
+               }
+
+               skb_put(skb, len);
+
+               /* Hardware validated our checksum */
+               if (likely(status & DESC_L4_CSUM))
+                       skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+               /* Hardware pre-pends packets with 2bytes before Ethernet
+                * header plus we have the Receive Status Block, strip off all
+                * of this from the SKB.
+                */
+               skb_pull(skb, sizeof(*rsb) + 2);
+               len -= (sizeof(*rsb) + 2);
+
+               /* UniMAC may forward CRC */
+               if (priv->crc_fwd) {
+                       skb_trim(skb, len - ETH_FCS_LEN);
+                       len -= ETH_FCS_LEN;
+               }
+
+               skb->protocol = eth_type_trans(skb, ndev);
+               ndev->stats.rx_packets++;
+               ndev->stats.rx_bytes += len;
+
+               napi_gro_receive(&priv->napi, skb);
+refill:
+               bcm_sysport_rx_refill(priv, cb);
+       }
+
+       return processed;
+}
+
+static void bcm_sysport_tx_reclaim_one(struct bcm_sysport_priv *priv,
+                                       struct bcm_sysport_cb *cb,
+                                       unsigned int *bytes_compl,
+                                       unsigned int *pkts_compl)
+{
+       struct device *kdev = &priv->pdev->dev;
+       struct net_device *ndev = priv->netdev;
+
+       if (cb->skb) {
+               ndev->stats.tx_bytes += cb->skb->len;
+               *bytes_compl += cb->skb->len;
+               dma_unmap_single(kdev, dma_unmap_addr(cb, dma_addr),
+                               dma_unmap_len(cb, dma_len),
+                               DMA_TO_DEVICE);
+               ndev->stats.tx_packets++;
+               (*pkts_compl)++;
+               bcm_sysport_free_cb(cb);
+       /* SKB fragment */
+       } else if (dma_unmap_addr(cb, dma_addr)) {
+               ndev->stats.tx_bytes += dma_unmap_len(cb, dma_len);
+               dma_unmap_page(kdev, dma_unmap_addr(cb, dma_addr),
+                               dma_unmap_len(cb, dma_len), DMA_TO_DEVICE);
+               dma_unmap_addr_set(cb, dma_addr, 0);
+       }
+}
+
+/* Reclaim queued SKBs for transmission completion, lockless version */
+static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv,
+                                            struct bcm_sysport_tx_ring *ring)
+{
+       struct net_device *ndev = priv->netdev;
+       unsigned int c_index, last_c_index, last_tx_cn, num_tx_cbs;
+       unsigned int pkts_compl = 0, bytes_compl = 0;
+       struct bcm_sysport_cb *cb;
+       struct netdev_queue *txq;
+       u32 hw_ind;
+
+       txq = netdev_get_tx_queue(ndev, ring->index);
+
+       /* Compute how many descriptors have been processed since last call */
+       hw_ind = tdma_readl(priv, TDMA_DESC_RING_PROD_CONS_INDEX(ring->index));
+       c_index = (hw_ind >> RING_CONS_INDEX_SHIFT) & RING_CONS_INDEX_MASK;
+       ring->p_index = (hw_ind & RING_PROD_INDEX_MASK);
+
+       last_c_index = ring->c_index;
+       num_tx_cbs = ring->size;
+
+       c_index &= (num_tx_cbs - 1);
+
+       if (c_index >= last_c_index)
+               last_tx_cn = c_index - last_c_index;
+       else
+               last_tx_cn = num_tx_cbs - last_c_index + c_index;
+
+       netif_dbg(priv, tx_done, ndev,
+                       "ring=%d c_index=%d last_tx_cn=%d last_c_index=%d\n",
+                       ring->index, c_index, last_tx_cn, last_c_index);
+
+       while (last_tx_cn-- > 0) {
+               cb = ring->cbs + last_c_index;
+               bcm_sysport_tx_reclaim_one(priv, cb, &bytes_compl, &pkts_compl);
+
+               ring->desc_count++;
+               last_c_index++;
+               last_c_index &= (num_tx_cbs - 1);
+       }
+
+       ring->c_index = c_index;
+
+       if (netif_tx_queue_stopped(txq) && pkts_compl)
+               netif_tx_wake_queue(txq);
+
+       netif_dbg(priv, tx_done, ndev,
+                       "ring=%d c_index=%d pkts_compl=%d, bytes_compl=%d\n",
+                       ring->index, ring->c_index, pkts_compl, bytes_compl);
+
+       return pkts_compl;
+}
+
+/* Locked version of the per-ring TX reclaim routine */
+static unsigned int bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv,
+                                          struct bcm_sysport_tx_ring *ring)
+{
+       unsigned int released;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ring->lock, flags);
+       released = __bcm_sysport_tx_reclaim(priv, ring);
+       spin_unlock_irqrestore(&ring->lock, flags);
+
+       return released;
+}
+
+static int bcm_sysport_tx_poll(struct napi_struct *napi, int budget)
+{
+       struct bcm_sysport_tx_ring *ring =
+               container_of(napi, struct bcm_sysport_tx_ring, napi);
+       unsigned int work_done = 0;
+
+       work_done = bcm_sysport_tx_reclaim(ring->priv, ring);
+
+       if (work_done < budget) {
+               napi_complete(napi);
+               /* re-enable TX interrupt */
+               intrl2_1_mask_clear(ring->priv, BIT(ring->index));
+       }
+
+       return work_done;
+}
+
+static void bcm_sysport_tx_reclaim_all(struct bcm_sysport_priv *priv)
+{
+       unsigned int q;
+
+       for (q = 0; q < priv->netdev->num_tx_queues; q++)
+               bcm_sysport_tx_reclaim(priv, &priv->tx_rings[q]);
+}
+
+static int bcm_sysport_poll(struct napi_struct *napi, int budget)
+{
+       struct bcm_sysport_priv *priv =
+               container_of(napi, struct bcm_sysport_priv, napi);
+       unsigned int work_done = 0;
+
+       work_done = bcm_sysport_desc_rx(priv, budget);
+
+       priv->rx_c_index += work_done;
+       priv->rx_c_index &= RDMA_CONS_INDEX_MASK;
+       rdma_writel(priv, priv->rx_c_index, RDMA_CONS_INDEX);
+
+       if (work_done < budget) {
+               napi_complete(napi);
+               /* re-enable RX interrupts */
+               intrl2_0_mask_clear(priv, INTRL2_0_RDMA_MBDONE);
+       }
+
+       return work_done;
+}
+
+
+/* RX and misc interrupt routine */
+static irqreturn_t bcm_sysport_rx_isr(int irq, void *dev_id)
+{
+       struct net_device *dev = dev_id;
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+
+       priv->irq0_stat = intrl2_0_readl(priv, INTRL2_CPU_STATUS) &
+                         ~intrl2_0_readl(priv, INTRL2_CPU_MASK_STATUS);
+       intrl2_0_writel(priv, priv->irq0_stat, INTRL2_CPU_CLEAR);
+
+       if (unlikely(priv->irq0_stat == 0)) {
+               netdev_warn(priv->netdev, "spurious RX interrupt\n");
+               return IRQ_NONE;
+       }
+
+       if (priv->irq0_stat & INTRL2_0_RDMA_MBDONE) {
+               if (likely(napi_schedule_prep(&priv->napi))) {
+                       /* disable RX interrupts */
+                       intrl2_0_mask_set(priv, INTRL2_0_RDMA_MBDONE);
+                       __napi_schedule(&priv->napi);
+               }
+       }
+
+       /* TX ring is full, perform a full reclaim since we do not know
+        * which one would trigger this interrupt
+        */
+       if (priv->irq0_stat & INTRL2_0_TX_RING_FULL)
+               bcm_sysport_tx_reclaim_all(priv);
+
+       return IRQ_HANDLED;
+}
+
+/* TX interrupt service routine */
+static irqreturn_t bcm_sysport_tx_isr(int irq, void *dev_id)
+{
+       struct net_device *dev = dev_id;
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+       struct bcm_sysport_tx_ring *txr;
+       unsigned int ring;
+
+       priv->irq1_stat = intrl2_1_readl(priv, INTRL2_CPU_STATUS) &
+                               ~intrl2_1_readl(priv, INTRL2_CPU_MASK_STATUS);
+       intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR);
+
+       if (unlikely(priv->irq1_stat == 0)) {
+               netdev_warn(priv->netdev, "spurious TX interrupt\n");
+               return IRQ_NONE;
+       }
+
+       for (ring = 0; ring < dev->num_tx_queues; ring++) {
+               if (!(priv->irq1_stat & BIT(ring)))
+                       continue;
+
+               txr = &priv->tx_rings[ring];
+
+               if (likely(napi_schedule_prep(&txr->napi))) {
+                       intrl2_1_mask_set(priv, BIT(ring));
+                       __napi_schedule(&txr->napi);
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int bcm_sysport_insert_tsb(struct sk_buff *skb, struct net_device *dev)
+{
+       struct sk_buff *nskb;
+       struct bcm_tsb *tsb;
+       u32 csum_info;
+       u8 ip_proto;
+       u16 csum_start;
+       u16 ip_ver;
+
+       /* Re-allocate SKB if needed */
+       if (unlikely(skb_headroom(skb) < sizeof(*tsb))) {
+               nskb = skb_realloc_headroom(skb, sizeof(*tsb));
+               dev_kfree_skb(skb);
+               if (!nskb) {
+                       dev->stats.tx_errors++;
+                       dev->stats.tx_dropped++;
+                       return -ENOMEM;
+               }
+               skb = nskb;
+       }
+
+       tsb = (struct bcm_tsb *)skb_push(skb, sizeof(*tsb));
+       /* Zero-out TSB by default */
+       memset(tsb, 0, sizeof(*tsb));
+
+       if (skb->ip_summed == CHECKSUM_PARTIAL) {
+               ip_ver = htons(skb->protocol);
+               switch (ip_ver) {
+               case ETH_P_IP:
+                       ip_proto = ip_hdr(skb)->protocol;
+                       break;
+               case ETH_P_IPV6:
+                       ip_proto = ipv6_hdr(skb)->nexthdr;
+                       break;
+               default:
+                       return 0;
+               }
+
+               /* Get the checksum offset and the L4 (transport) offset */
+               csum_start = skb_checksum_start_offset(skb) - sizeof(*tsb);
+               csum_info = (csum_start + skb->csum_offset) & L4_CSUM_PTR_MASK;
+               csum_info |= (csum_start << L4_PTR_SHIFT);
+
+               if (ip_proto == IPPROTO_TCP || ip_proto == IPPROTO_UDP) {
+                       csum_info |= L4_LENGTH_VALID;
+                       if (ip_proto == IPPROTO_UDP && ip_ver == ETH_P_IP)
+                               csum_info |= L4_UDP;
+               } else
+                       csum_info = 0;
+
+               tsb->l4_ptr_dest_map = csum_info;
+       }
+
+       return 0;
+}
+
+static netdev_tx_t bcm_sysport_xmit(struct sk_buff *skb,
+                                   struct net_device *dev)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+       struct device *kdev = &priv->pdev->dev;
+       struct bcm_sysport_tx_ring *ring;
+       struct bcm_sysport_cb *cb;
+       struct netdev_queue *txq;
+       struct dma_desc *desc;
+       unsigned int skb_len;
+       unsigned long flags;
+       dma_addr_t mapping;
+       u32 len_status;
+       u16 queue;
+       int ret;
+
+       queue = skb_get_queue_mapping(skb);
+       txq = netdev_get_tx_queue(dev, queue);
+       ring = &priv->tx_rings[queue];
+
+       /* lock against tx reclaim in BH context and TX ring full interrupt */
+       spin_lock_irqsave(&ring->lock, flags);
+       if (unlikely(ring->desc_count == 0)) {
+               netif_tx_stop_queue(txq);
+               netdev_err(dev, "queue %d awake and ring full!\n", queue);
+               ret = NETDEV_TX_BUSY;
+               goto out;
+       }
+
+       /* Insert TSB and checksum infos */
+       if (priv->tsb_en) {
+               ret = bcm_sysport_insert_tsb(skb, dev);
+               if (ret) {
+                       ret = NETDEV_TX_OK;
+                       goto out;
+               }
+       }
+
+       /* The Ethernet switch we are interfaced with needs packets to be at
+        * least 64 bytes (including FCS) otherwise they will be discarded when
+        * they enter the switch port logic. When Broadcom tags are enabled, we
+        * need to make sure that packets are at least 68 bytes
+        * (including FCS and tag) because the length verification is done after
+        * the Broadcom tag is stripped off the ingress packet.
+        */
+       if (skb_padto(skb, ETH_ZLEN + ENET_BRCM_TAG_LEN)) {
+               ret = NETDEV_TX_OK;
+               goto out;
+       }
+
+       skb_len = skb->len < ETH_ZLEN + ENET_BRCM_TAG_LEN ?
+                       ETH_ZLEN + ENET_BRCM_TAG_LEN : skb->len;
+
+       mapping = dma_map_single(kdev, skb->data, skb_len, DMA_TO_DEVICE);
+       if (dma_mapping_error(kdev, mapping)) {
+               netif_err(priv, tx_err, dev, "DMA map failed at %p (len=%d)\n",
+                               skb->data, skb_len);
+               ret = NETDEV_TX_OK;
+               goto out;
+       }
+
+       /* Remember the SKB for future freeing */
+       cb = &ring->cbs[ring->curr_desc];
+       cb->skb = skb;
+       dma_unmap_addr_set(cb, dma_addr, mapping);
+       dma_unmap_len_set(cb, dma_len, skb_len);
+
+       /* Fetch a descriptor entry from our pool */
+       desc = ring->desc_cpu;
+
+       desc->addr_lo = lower_32_bits(mapping);
+       len_status = upper_32_bits(mapping) & DESC_ADDR_HI_MASK;
+       len_status |= (skb_len << DESC_LEN_SHIFT);
+       len_status |= (DESC_SOP | DESC_EOP | TX_STATUS_APP_CRC) <<
+                       DESC_STATUS_SHIFT;
+       if (skb->ip_summed == CHECKSUM_PARTIAL)
+               len_status |= (DESC_L4_CSUM << DESC_STATUS_SHIFT);
+
+       ring->curr_desc++;
+       if (ring->curr_desc == ring->size)
+               ring->curr_desc = 0;
+       ring->desc_count--;
+
+       /* Ensure write completion of the descriptor status/length
+        * in DRAM before the System Port WRITE_PORT register latches
+        * the value
+        */
+       wmb();
+       desc->addr_status_len = len_status;
+       wmb();
+
+       /* Write this descriptor address to the RING write port */
+       tdma_port_write_desc_addr(priv, desc, ring->index);
+
+       /* Check ring space and update SW control flow */
+       if (ring->desc_count == 0)
+               netif_tx_stop_queue(txq);
+
+       netif_dbg(priv, tx_queued, dev, "ring=%d desc_count=%d, curr_desc=%d\n",
+                       ring->index, ring->desc_count, ring->curr_desc);
+
+       ret = NETDEV_TX_OK;
+out:
+       spin_unlock_irqrestore(&ring->lock, flags);
+       return ret;
+}
+
+static void bcm_sysport_tx_timeout(struct net_device *dev)
+{
+       netdev_warn(dev, "transmit timeout!\n");
+
+       dev->trans_start = jiffies;
+       dev->stats.tx_errors++;
+
+       netif_tx_wake_all_queues(dev);
+}
+
+/* phylib adjust link callback */
+static void bcm_sysport_adj_link(struct net_device *dev)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+       struct phy_device *phydev = priv->phydev;
+       unsigned int changed = 0;
+       u32 cmd_bits = 0, reg;
+
+       if (priv->old_link != phydev->link) {
+               changed = 1;
+               priv->old_link = phydev->link;
+       }
+
+       if (priv->old_duplex != phydev->duplex) {
+               changed = 1;
+               priv->old_duplex = phydev->duplex;
+       }
+
+       switch (phydev->speed) {
+       case SPEED_2500:
+               cmd_bits = CMD_SPEED_2500;
+               break;
+       case SPEED_1000:
+               cmd_bits = CMD_SPEED_1000;
+               break;
+       case SPEED_100:
+               cmd_bits = CMD_SPEED_100;
+               break;
+       case SPEED_10:
+               cmd_bits = CMD_SPEED_10;
+               break;
+       default:
+               break;
+       }
+       cmd_bits <<= CMD_SPEED_SHIFT;
+
+       if (phydev->duplex == DUPLEX_HALF)
+               cmd_bits |= CMD_HD_EN;
+
+       if (priv->old_pause != phydev->pause) {
+               changed = 1;
+               priv->old_pause = phydev->pause;
+       }
+
+       if (!phydev->pause)
+               cmd_bits |= CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE;
+
+       if (changed) {
+               reg = umac_readl(priv, UMAC_CMD);
+               reg &= ~((CMD_SPEED_MASK << CMD_SPEED_SHIFT) |
+                       CMD_HD_EN | CMD_RX_PAUSE_IGNORE |
+                       CMD_TX_PAUSE_IGNORE);
+               reg |= cmd_bits;
+               umac_writel(priv, reg, UMAC_CMD);
+
+               phy_print_status(priv->phydev);
+       }
+}
+
+static int bcm_sysport_init_tx_ring(struct bcm_sysport_priv *priv,
+                                   unsigned int index)
+{
+       struct bcm_sysport_tx_ring *ring = &priv->tx_rings[index];
+       struct device *kdev = &priv->pdev->dev;
+       size_t size;
+       void *p;
+       u32 reg;
+
+       /* Simple descriptors partitioning for now */
+       size = 256;
+
+       /* We just need one DMA descriptor which is DMA-able, since writing to
+        * the port will allocate a new descriptor in its internal linked-list
+        */
+       p = dma_zalloc_coherent(kdev, 1, &ring->desc_dma, GFP_KERNEL);
+       if (!p) {
+               netif_err(priv, hw, priv->netdev, "DMA alloc failed\n");
+               return -ENOMEM;
+       }
+
+       ring->cbs = kzalloc(sizeof(struct bcm_sysport_cb) * size, GFP_KERNEL);
+       if (!ring->cbs) {
+               netif_err(priv, hw, priv->netdev, "CB allocation failed\n");
+               return -ENOMEM;
+       }
+
+       /* Initialize SW view of the ring */
+       spin_lock_init(&ring->lock);
+       ring->priv = priv;
+       netif_napi_add(priv->netdev, &ring->napi, bcm_sysport_tx_poll, 64);
+       ring->index = index;
+       ring->size = size;
+       ring->alloc_size = ring->size;
+       ring->desc_cpu = p;
+       ring->desc_count = ring->size;
+       ring->curr_desc = 0;
+
+       /* Initialize HW ring */
+       tdma_writel(priv, RING_EN, TDMA_DESC_RING_HEAD_TAIL_PTR(index));
+       tdma_writel(priv, 0, TDMA_DESC_RING_COUNT(index));
+       tdma_writel(priv, 1, TDMA_DESC_RING_INTR_CONTROL(index));
+       tdma_writel(priv, 0, TDMA_DESC_RING_PROD_CONS_INDEX(index));
+       tdma_writel(priv, RING_IGNORE_STATUS, TDMA_DESC_RING_MAPPING(index));
+       tdma_writel(priv, 0, TDMA_DESC_RING_PCP_DEI_VID(index));
+
+       /* Program the number of descriptors as MAX_THRESHOLD and half of
+        * its size for the hysteresis trigger
+        */
+       tdma_writel(priv, ring->size |
+                       1 << RING_HYST_THRESH_SHIFT,
+                       TDMA_DESC_RING_MAX_HYST(index));
+
+       /* Enable the ring queue in the arbiter */
+       reg = tdma_readl(priv, TDMA_TIER1_ARB_0_QUEUE_EN);
+       reg |= (1 << index);
+       tdma_writel(priv, reg, TDMA_TIER1_ARB_0_QUEUE_EN);
+
+       napi_enable(&ring->napi);
+
+       netif_dbg(priv, hw, priv->netdev,
+                       "TDMA cfg, size=%d, desc_cpu=%p\n",
+                       ring->size, ring->desc_cpu);
+
+       return 0;
+}
+
+static void bcm_sysport_fini_tx_ring(struct bcm_sysport_priv *priv,
+                                       unsigned int index)
+{
+       struct bcm_sysport_tx_ring *ring = &priv->tx_rings[index];
+       struct device *kdev = &priv->pdev->dev;
+       u32 reg;
+
+       /* Caller should stop the TDMA engine */
+       reg = tdma_readl(priv, TDMA_STATUS);
+       if (!(reg & TDMA_DISABLED))
+               netdev_warn(priv->netdev, "TDMA not stopped!\n");
+
+       napi_disable(&ring->napi);
+       netif_napi_del(&ring->napi);
+
+       bcm_sysport_tx_reclaim(priv, ring);
+
+       kfree(ring->cbs);
+       ring->cbs = NULL;
+
+       if (ring->desc_dma) {
+               dma_free_coherent(kdev, 1, ring->desc_cpu, ring->desc_dma);
+               ring->desc_dma = 0;
+       }
+       ring->size = 0;
+       ring->alloc_size = 0;
+
+       netif_dbg(priv, hw, priv->netdev, "TDMA fini done\n");
+}
+
+/* RDMA helper */
+static inline int rdma_enable_set(struct bcm_sysport_priv *priv,
+                                       unsigned int enable)
+{
+       unsigned int timeout = 1000;
+       u32 reg;
+
+       reg = rdma_readl(priv, RDMA_CONTROL);
+       if (enable)
+               reg |= RDMA_EN;
+       else
+               reg &= ~RDMA_EN;
+       rdma_writel(priv, reg, RDMA_CONTROL);
+
+       /* Poll for RMDA disabling completion */
+       do {
+               reg = rdma_readl(priv, RDMA_STATUS);
+               if (!!(reg & RDMA_DISABLED) == !enable)
+                       return 0;
+               usleep_range(1000, 2000);
+       } while (timeout-- > 0);
+
+       netdev_err(priv->netdev, "timeout waiting for RDMA to finish\n");
+
+       return -ETIMEDOUT;
+}
+
+/* TDMA helper */
+static inline int tdma_enable_set(struct bcm_sysport_priv *priv,
+                                       unsigned int enable)
+{
+       unsigned int timeout = 1000;
+       u32 reg;
+
+       reg = tdma_readl(priv, TDMA_CONTROL);
+       if (enable)
+               reg |= TDMA_EN;
+       else
+               reg &= ~TDMA_EN;
+       tdma_writel(priv, reg, TDMA_CONTROL);
+
+       /* Poll for TMDA disabling completion */
+       do {
+               reg = tdma_readl(priv, TDMA_STATUS);
+               if (!!(reg & TDMA_DISABLED) == !enable)
+                       return 0;
+
+               usleep_range(1000, 2000);
+       } while (timeout-- > 0);
+
+       netdev_err(priv->netdev, "timeout waiting for TDMA to finish\n");
+
+       return -ETIMEDOUT;
+}
+
+static int bcm_sysport_init_rx_ring(struct bcm_sysport_priv *priv)
+{
+       u32 reg;
+       int ret;
+
+       /* Initialize SW view of the RX ring */
+       priv->num_rx_bds = NUM_RX_DESC;
+       priv->rx_bds = priv->base + SYS_PORT_RDMA_OFFSET;
+       priv->rx_bd_assign_ptr = priv->rx_bds;
+       priv->rx_bd_assign_index = 0;
+       priv->rx_c_index = 0;
+       priv->rx_read_ptr = 0;
+       priv->rx_cbs = kzalloc(priv->num_rx_bds *
+                               sizeof(struct bcm_sysport_cb), GFP_KERNEL);
+       if (!priv->rx_cbs) {
+               netif_err(priv, hw, priv->netdev, "CB allocation failed\n");
+               return -ENOMEM;
+       }
+
+       ret = bcm_sysport_alloc_rx_bufs(priv);
+       if (ret) {
+               netif_err(priv, hw, priv->netdev, "SKB allocation failed\n");
+               return ret;
+       }
+
+       /* Initialize HW, ensure RDMA is disabled */
+       reg = rdma_readl(priv, RDMA_STATUS);
+       if (!(reg & RDMA_DISABLED))
+               rdma_enable_set(priv, 0);
+
+       rdma_writel(priv, 0, RDMA_WRITE_PTR_LO);
+       rdma_writel(priv, 0, RDMA_WRITE_PTR_HI);
+       rdma_writel(priv, 0, RDMA_PROD_INDEX);
+       rdma_writel(priv, 0, RDMA_CONS_INDEX);
+       rdma_writel(priv, priv->num_rx_bds << RDMA_RING_SIZE_SHIFT |
+                         RX_BUF_LENGTH, RDMA_RING_BUF_SIZE);
+       /* Operate the queue in ring mode */
+       rdma_writel(priv, 0, RDMA_START_ADDR_HI);
+       rdma_writel(priv, 0, RDMA_START_ADDR_LO);
+       rdma_writel(priv, 0, RDMA_END_ADDR_HI);
+       rdma_writel(priv, NUM_HW_RX_DESC_WORDS - 1, RDMA_END_ADDR_LO);
+
+       rdma_writel(priv, 1, RDMA_MBDONE_INTR);
+
+       netif_dbg(priv, hw, priv->netdev,
+                       "RDMA cfg, num_rx_bds=%d, rx_bds=%p\n",
+                       priv->num_rx_bds, priv->rx_bds);
+
+       return 0;
+}
+
+static void bcm_sysport_fini_rx_ring(struct bcm_sysport_priv *priv)
+{
+       struct bcm_sysport_cb *cb;
+       unsigned int i;
+       u32 reg;
+
+       /* Caller should ensure RDMA is disabled */
+       reg = rdma_readl(priv, RDMA_STATUS);
+       if (!(reg & RDMA_DISABLED))
+               netdev_warn(priv->netdev, "RDMA not stopped!\n");
+
+       for (i = 0; i < priv->num_rx_bds; i++) {
+               cb = &priv->rx_cbs[i];
+               if (dma_unmap_addr(cb, dma_addr))
+                       dma_unmap_single(&priv->pdev->dev,
+                                       dma_unmap_addr(cb, dma_addr),
+                                       RX_BUF_LENGTH, DMA_FROM_DEVICE);
+               bcm_sysport_free_cb(cb);
+       }
+
+       kfree(priv->rx_cbs);
+       priv->rx_cbs = NULL;
+
+       netif_dbg(priv, hw, priv->netdev, "RDMA fini done\n");
+}
+
+static void bcm_sysport_set_rx_mode(struct net_device *dev)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+       u32 reg;
+
+       reg = umac_readl(priv, UMAC_CMD);
+       if (dev->flags & IFF_PROMISC)
+               reg |= CMD_PROMISC;
+       else
+               reg &= ~CMD_PROMISC;
+       umac_writel(priv, reg, UMAC_CMD);
+
+       /* No support for ALLMULTI */
+       if (dev->flags & IFF_ALLMULTI)
+               return;
+}
+
+static inline void umac_enable_set(struct bcm_sysport_priv *priv,
+                                       unsigned int enable)
+{
+       u32 reg;
+
+       reg = umac_readl(priv, UMAC_CMD);
+       if (enable)
+               reg |= CMD_RX_EN | CMD_TX_EN;
+       else
+               reg &= ~(CMD_RX_EN | CMD_TX_EN);
+       umac_writel(priv, reg, UMAC_CMD);
+
+       /* UniMAC stops on a packet boundary, wait for a full-sized packet
+        * to be processed (1 msec).
+        */
+       if (enable == 0)
+               usleep_range(1000, 2000);
+}
+
+static inline int umac_reset(struct bcm_sysport_priv *priv)
+{
+       unsigned int timeout = 0;
+       u32 reg;
+       int ret = 0;
+
+       umac_writel(priv, 0, UMAC_CMD);
+       while (timeout++ < 1000) {
+               reg = umac_readl(priv, UMAC_CMD);
+               if (!(reg & CMD_SW_RESET))
+                       break;
+
+               udelay(1);
+       }
+
+       if (timeout == 1000) {
+               dev_err(&priv->pdev->dev,
+                       "timeout waiting for MAC to come out of reset\n");
+               ret = -ETIMEDOUT;
+       }
+
+       return ret;
+}
+
+static void umac_set_hw_addr(struct bcm_sysport_priv *priv,
+                               unsigned char *addr)
+{
+       umac_writel(priv, (addr[0] << 24) | (addr[1] << 16) |
+                       (addr[2] << 8) | addr[3], UMAC_MAC0);
+       umac_writel(priv, (addr[4] << 8) | addr[5], UMAC_MAC1);
+}
+
+static void topctrl_flush(struct bcm_sysport_priv *priv)
+{
+       topctrl_writel(priv, RX_FLUSH, RX_FLUSH_CNTL);
+       topctrl_writel(priv, TX_FLUSH, TX_FLUSH_CNTL);
+       mdelay(1);
+       topctrl_writel(priv, 0, RX_FLUSH_CNTL);
+       topctrl_writel(priv, 0, TX_FLUSH_CNTL);
+}
+
+static int bcm_sysport_open(struct net_device *dev)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+       unsigned int i;
+       u32 reg;
+       int ret;
+
+       /* Reset UniMAC */
+       ret = umac_reset(priv);
+       if (ret) {
+               netdev_err(dev, "UniMAC reset failed\n");
+               return ret;
+       }
+
+       /* Flush TX and RX FIFOs at TOPCTRL level */
+       topctrl_flush(priv);
+
+       /* Disable the UniMAC RX/TX */
+       umac_enable_set(priv, 0);
+
+       /* Enable RBUF 2bytes alignment and Receive Status Block */
+       reg = rbuf_readl(priv, RBUF_CONTROL);
+       reg |= RBUF_4B_ALGN | RBUF_RSB_EN;
+       rbuf_writel(priv, reg, RBUF_CONTROL);
+
+       /* Set maximum frame length */
+       umac_writel(priv, UMAC_MAX_MTU_SIZE, UMAC_MAX_FRAME_LEN);
+
+       /* Set MAC address */
+       umac_set_hw_addr(priv, dev->dev_addr);
+
+       /* Read CRC forward */
+       priv->crc_fwd = !!(umac_readl(priv, UMAC_CMD) & CMD_CRC_FWD);
+
+       priv->phydev = of_phy_connect(dev, priv->phy_dn, bcm_sysport_adj_link,
+                                       0, priv->phy_interface);
+       if (!priv->phydev) {
+               netdev_err(dev, "could not attach to PHY\n");
+               return -ENODEV;
+       }
+
+       /* Reset house keeping link status */
+       priv->old_duplex = -1;
+       priv->old_link = -1;
+       priv->old_pause = -1;
+
+       /* mask all interrupts and request them */
+       intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET);
+       intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR);
+       intrl2_0_writel(priv, 0, INTRL2_CPU_MASK_CLEAR);
+       intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET);
+       intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR);
+       intrl2_1_writel(priv, 0, INTRL2_CPU_MASK_CLEAR);
+
+       ret = request_irq(priv->irq0, bcm_sysport_rx_isr, 0, dev->name, dev);
+       if (ret) {
+               netdev_err(dev, "failed to request RX interrupt\n");
+               goto out_phy_disconnect;
+       }
+
+       ret = request_irq(priv->irq1, bcm_sysport_tx_isr, 0, dev->name, dev);
+       if (ret) {
+               netdev_err(dev, "failed to request TX interrupt\n");
+               goto out_free_irq0;
+       }
+
+       /* Initialize both hardware and software ring */
+       for (i = 0; i < dev->num_tx_queues; i++) {
+               ret = bcm_sysport_init_tx_ring(priv, i);
+               if (ret) {
+                       netdev_err(dev, "failed to initialize TX ring %d\n",
+                                       i);
+                       goto out_free_tx_ring;
+               }
+       }
+
+       /* Initialize linked-list */
+       tdma_writel(priv, TDMA_LL_RAM_INIT_BUSY, TDMA_STATUS);
+
+       /* Initialize RX ring */
+       ret = bcm_sysport_init_rx_ring(priv);
+       if (ret) {
+               netdev_err(dev, "failed to initialize RX ring\n");
+               goto out_free_rx_ring;
+       }
+
+       /* Turn on RDMA */
+       ret = rdma_enable_set(priv, 1);
+       if (ret)
+               goto out_free_rx_ring;
+
+       /* Enable RX interrupt and TX ring full interrupt */
+       intrl2_0_mask_clear(priv, INTRL2_0_RDMA_MBDONE | INTRL2_0_TX_RING_FULL);
+
+       /* Turn on TDMA */
+       ret = tdma_enable_set(priv, 1);
+       if (ret)
+               goto out_clear_rx_int;
+
+       /* Enable NAPI */
+       napi_enable(&priv->napi);
+
+       /* Turn on UniMAC TX/RX */
+       umac_enable_set(priv, 1);
+
+       phy_start(priv->phydev);
+
+       /* Enable TX interrupts for the 32 TXQs */
+       intrl2_1_mask_clear(priv, 0xffffffff);
+
+       /* Last call before we start the real business */
+       netif_tx_start_all_queues(dev);
+
+       return 0;
+
+out_clear_rx_int:
+       intrl2_0_mask_set(priv, INTRL2_0_RDMA_MBDONE | INTRL2_0_TX_RING_FULL);
+out_free_rx_ring:
+       bcm_sysport_fini_rx_ring(priv);
+out_free_tx_ring:
+       for (i = 0; i < dev->num_tx_queues; i++)
+               bcm_sysport_fini_tx_ring(priv, i);
+       free_irq(priv->irq1, dev);
+out_free_irq0:
+       free_irq(priv->irq0, dev);
+out_phy_disconnect:
+       phy_disconnect(priv->phydev);
+       return ret;
+}
+
+static int bcm_sysport_stop(struct net_device *dev)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+       unsigned int i;
+       u32 reg;
+       int ret;
+
+       /* stop all software from updating hardware */
+       netif_tx_stop_all_queues(dev);
+       napi_disable(&priv->napi);
+       phy_stop(priv->phydev);
+
+       /* mask all interrupts */
+       intrl2_0_mask_set(priv, 0xffffffff);
+       intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR);
+       intrl2_1_mask_set(priv, 0xffffffff);
+       intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR);
+
+       /* Disable UniMAC RX */
+       reg = umac_readl(priv, UMAC_CMD);
+       reg &= ~CMD_RX_EN;
+       umac_writel(priv, reg, UMAC_CMD);
+
+       ret = tdma_enable_set(priv, 0);
+       if (ret) {
+               netdev_err(dev, "timeout disabling RDMA\n");
+               return ret;
+       }
+
+       /* Wait for a maximum packet size to be drained */
+       usleep_range(2000, 3000);
+
+       ret = rdma_enable_set(priv, 0);
+       if (ret) {
+               netdev_err(dev, "timeout disabling TDMA\n");
+               return ret;
+       }
+
+       /* Disable UniMAC TX */
+       reg = umac_readl(priv, UMAC_CMD);
+       reg &= ~CMD_TX_EN;
+       umac_writel(priv, reg, UMAC_CMD);
+
+       /* Free RX/TX rings SW structures */
+       for (i = 0; i < dev->num_tx_queues; i++)
+               bcm_sysport_fini_tx_ring(priv, i);
+       bcm_sysport_fini_rx_ring(priv);
+
+       free_irq(priv->irq0, dev);
+       free_irq(priv->irq1, dev);
+
+       /* Disconnect from PHY */
+       phy_disconnect(priv->phydev);
+
+       return 0;
+}
+
+static struct ethtool_ops bcm_sysport_ethtool_ops = {
+       .get_settings           = bcm_sysport_get_settings,
+       .set_settings           = bcm_sysport_set_settings,
+       .get_drvinfo            = bcm_sysport_get_drvinfo,
+       .get_msglevel           = bcm_sysport_get_msglvl,
+       .set_msglevel           = bcm_sysport_set_msglvl,
+       .get_link               = ethtool_op_get_link,
+       .get_strings            = bcm_sysport_get_strings,
+       .get_ethtool_stats      = bcm_sysport_get_stats,
+       .get_sset_count         = bcm_sysport_get_sset_count,
+};
+
+static const struct net_device_ops bcm_sysport_netdev_ops = {
+       .ndo_start_xmit         = bcm_sysport_xmit,
+       .ndo_tx_timeout         = bcm_sysport_tx_timeout,
+       .ndo_open               = bcm_sysport_open,
+       .ndo_stop               = bcm_sysport_stop,
+       .ndo_set_features       = bcm_sysport_set_features,
+       .ndo_set_rx_mode        = bcm_sysport_set_rx_mode,
+};
+
+#define REV_FMT        "v%2x.%02x"
+
+static int bcm_sysport_probe(struct platform_device *pdev)
+{
+       struct bcm_sysport_priv *priv;
+       struct device_node *dn;
+       struct net_device *dev;
+       const void *macaddr;
+       struct resource *r;
+       u32 txq, rxq;
+       int ret;
+
+       dn = pdev->dev.of_node;
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       /* Read the Transmit/Receive Queue properties */
+       if (of_property_read_u32(dn, "systemport,num-txq", &txq))
+               txq = TDMA_NUM_RINGS;
+       if (of_property_read_u32(dn, "systemport,num-rxq", &rxq))
+               rxq = 1;
+
+       dev = alloc_etherdev_mqs(sizeof(*priv), txq, rxq);
+       if (!dev)
+               return -ENOMEM;
+
+       /* Initialize private members */
+       priv = netdev_priv(dev);
+
+       priv->irq0 = platform_get_irq(pdev, 0);
+       priv->irq1 = platform_get_irq(pdev, 1);
+       if (priv->irq0 <= 0 || priv->irq1 <= 0) {
+               dev_err(&pdev->dev, "invalid interrupts\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       priv->base = devm_ioremap_resource(&pdev->dev, r);
+       if (IS_ERR(priv->base)) {
+               ret = PTR_ERR(priv->base);
+               goto err;
+       }
+
+       priv->netdev = dev;
+       priv->pdev = pdev;
+
+       priv->phy_interface = of_get_phy_mode(dn);
+       /* Default to GMII interface mode */
+       if (priv->phy_interface < 0)
+               priv->phy_interface = PHY_INTERFACE_MODE_GMII;
+
+       /* In the case of a fixed PHY, the DT node associated
+        * to the PHY is the Ethernet MAC DT node.
+        */
+       if (of_phy_is_fixed_link(dn)) {
+               ret = of_phy_register_fixed_link(dn);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to register fixed PHY\n");
+                       goto err;
+               }
+
+               priv->phy_dn = dn;
+       }
+
+       /* Initialize netdevice members */
+       macaddr = of_get_mac_address(dn);
+       if (!macaddr || !is_valid_ether_addr(macaddr)) {
+               dev_warn(&pdev->dev, "using random Ethernet MAC\n");
+               random_ether_addr(dev->dev_addr);
+       } else {
+               ether_addr_copy(dev->dev_addr, macaddr);
+       }
+
+       SET_NETDEV_DEV(dev, &pdev->dev);
+       dev_set_drvdata(&pdev->dev, dev);
+       dev->ethtool_ops = &bcm_sysport_ethtool_ops;
+       dev->netdev_ops = &bcm_sysport_netdev_ops;
+       netif_napi_add(dev, &priv->napi, bcm_sysport_poll, 64);
+
+       /* HW supported features, none enabled by default */
+       dev->hw_features |= NETIF_F_RXCSUM | NETIF_F_HIGHDMA |
+                               NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
+
+       /* Set the needed headroom once and for all */
+       BUILD_BUG_ON(sizeof(struct bcm_tsb) != 8);
+       dev->needed_headroom += sizeof(struct bcm_tsb);
+
+       /* We are interfaced to a switch which handles the multicast
+        * filtering for us, so we do not support programming any
+        * multicast hash table in this Ethernet MAC.
+        */
+       dev->flags &= ~IFF_MULTICAST;
+
+       /* libphy will adjust the link state accordingly */
+       netif_carrier_off(dev);
+
+       ret = register_netdev(dev);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register net_device\n");
+               goto err;
+       }
+
+       priv->rev = topctrl_readl(priv, REV_CNTL) & REV_MASK;
+       dev_info(&pdev->dev,
+               "Broadcom SYSTEMPORT" REV_FMT
+               " at 0x%p (irqs: %d, %d, TXQs: %d, RXQs: %d)\n",
+               (priv->rev >> 8) & 0xff, priv->rev & 0xff,
+               priv->base, priv->irq0, priv->irq1, txq, rxq);
+
+       return 0;
+err:
+       free_netdev(dev);
+       return ret;
+}
+
+static int bcm_sysport_remove(struct platform_device *pdev)
+{
+       struct net_device *dev = dev_get_drvdata(&pdev->dev);
+
+       /* Not much to do, ndo_close has been called
+        * and we use managed allocations
+        */
+       unregister_netdev(dev);
+       free_netdev(dev);
+       dev_set_drvdata(&pdev->dev, NULL);
+
+       return 0;
+}
+
+static const struct of_device_id bcm_sysport_of_match[] = {
+       { .compatible = "brcm,systemport-v1.00" },
+       { .compatible = "brcm,systemport" },
+       { /* sentinel */ }
+};
+
+static struct platform_driver bcm_sysport_driver = {
+       .probe  = bcm_sysport_probe,
+       .remove = bcm_sysport_remove,
+       .driver =  {
+               .name = "brcm-systemport",
+               .owner = THIS_MODULE,
+               .of_match_table = bcm_sysport_of_match,
+       },
+};
+module_platform_driver(bcm_sysport_driver);
+
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_DESCRIPTION("Broadcom System Port Ethernet MAC driver");
+MODULE_ALIAS("platform:brcm-systemport");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.h b/drivers/net/ethernet/broadcom/bcmsysport.h
new file mode 100644 (file)
index 0000000..281c082
--- /dev/null
@@ -0,0 +1,678 @@
+/*
+ * Broadcom BCM7xxx System Port Ethernet MAC driver
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __BCM_SYSPORT_H
+#define __BCM_SYSPORT_H
+
+#include <linux/if_vlan.h>
+
+/* Receive/transmit descriptor format */
+#define DESC_ADDR_HI_STATUS_LEN        0x00
+#define  DESC_ADDR_HI_SHIFT    0
+#define  DESC_ADDR_HI_MASK     0xff
+#define  DESC_STATUS_SHIFT     8
+#define  DESC_STATUS_MASK      0x3ff
+#define  DESC_LEN_SHIFT                18
+#define  DESC_LEN_MASK         0x7fff
+#define DESC_ADDR_LO           0x04
+
+/* HW supports 40-bit addressing hence the */
+#define DESC_SIZE              (WORDS_PER_DESC * sizeof(u32))
+
+/* Default RX buffer allocation size */
+#define RX_BUF_LENGTH          2048
+
+/* Body(1500) + EH_SIZE(14) + VLANTAG(4) + BRCMTAG(4) + FCS(4) = 1526.
+ * 1536 is multiple of 256 bytes
+ */
+#define ENET_BRCM_TAG_LEN      4
+#define ENET_PAD               10
+#define UMAC_MAX_MTU_SIZE      (ETH_DATA_LEN + ETH_HLEN + VLAN_HLEN + \
+                                ENET_BRCM_TAG_LEN + ETH_FCS_LEN + ENET_PAD)
+
+/* Transmit status block */
+struct bcm_tsb {
+       u32 pcp_dei_vid;
+#define PCP_DEI_MASK           0xf
+#define VID_SHIFT              4
+#define VID_MASK               0xfff
+       u32 l4_ptr_dest_map;
+#define L4_CSUM_PTR_MASK       0x1ff
+#define L4_PTR_SHIFT           9
+#define L4_PTR_MASK            0x1ff
+#define L4_UDP                 (1 << 18)
+#define L4_LENGTH_VALID                (1 << 19)
+#define DEST_MAP_SHIFT         20
+#define DEST_MAP_MASK          0x1ff
+};
+
+/* Receive status block uses the same
+ * definitions as the DMA descriptor
+ */
+struct bcm_rsb {
+       u32 rx_status_len;
+       u32 brcm_egress_tag;
+};
+
+/* Common Receive/Transmit status bits */
+#define DESC_L4_CSUM           (1 << 7)
+#define DESC_SOP               (1 << 8)
+#define DESC_EOP               (1 << 9)
+
+/* Receive Status bits */
+#define RX_STATUS_UCAST                        0
+#define RX_STATUS_BCAST                        0x04
+#define RX_STATUS_MCAST                        0x08
+#define RX_STATUS_L2_MCAST             0x0c
+#define RX_STATUS_ERR                  (1 << 4)
+#define RX_STATUS_OVFLOW               (1 << 5)
+#define RX_STATUS_PARSE_FAIL           (1 << 6)
+
+/* Transmit Status bits */
+#define TX_STATUS_VLAN_NO_ACT          0x00
+#define TX_STATUS_VLAN_PCP_TSB         0x01
+#define TX_STATUS_VLAN_QUEUE           0x02
+#define TX_STATUS_VLAN_VID_TSB         0x03
+#define TX_STATUS_OWR_CRC              (1 << 2)
+#define TX_STATUS_APP_CRC              (1 << 3)
+#define TX_STATUS_BRCM_TAG_NO_ACT      0
+#define TX_STATUS_BRCM_TAG_ZERO                0x10
+#define TX_STATUS_BRCM_TAG_ONE_QUEUE   0x20
+#define TX_STATUS_BRCM_TAG_ONE_TSB     0x30
+#define TX_STATUS_SKIP_BYTES           (1 << 6)
+
+/* Specific register definitions */
+#define SYS_PORT_TOPCTRL_OFFSET                0
+#define REV_CNTL                       0x00
+#define  REV_MASK                      0xffff
+
+#define RX_FLUSH_CNTL                  0x04
+#define  RX_FLUSH                      (1 << 0)
+
+#define TX_FLUSH_CNTL                  0x08
+#define  TX_FLUSH                      (1 << 0)
+
+#define MISC_CNTL                      0x0c
+#define  SYS_CLK_SEL                   (1 << 0)
+#define  TDMA_EOP_SEL                  (1 << 1)
+
+/* Level-2 Interrupt controller offsets and defines */
+#define SYS_PORT_INTRL2_0_OFFSET       0x200
+#define SYS_PORT_INTRL2_1_OFFSET       0x240
+#define INTRL2_CPU_STATUS              0x00
+#define INTRL2_CPU_SET                 0x04
+#define INTRL2_CPU_CLEAR               0x08
+#define INTRL2_CPU_MASK_STATUS         0x0c
+#define INTRL2_CPU_MASK_SET            0x10
+#define INTRL2_CPU_MASK_CLEAR          0x14
+
+/* Level-2 instance 0 interrupt bits */
+#define INTRL2_0_GISB_ERR              (1 << 0)
+#define INTRL2_0_RBUF_OVFLOW           (1 << 1)
+#define INTRL2_0_TBUF_UNDFLOW          (1 << 2)
+#define INTRL2_0_MPD                   (1 << 3)
+#define INTRL2_0_BRCM_MATCH_TAG                (1 << 4)
+#define INTRL2_0_RDMA_MBDONE           (1 << 5)
+#define INTRL2_0_OVER_MAX_THRESH       (1 << 6)
+#define INTRL2_0_BELOW_HYST_THRESH     (1 << 7)
+#define INTRL2_0_FREE_LIST_EMPTY       (1 << 8)
+#define INTRL2_0_TX_RING_FULL          (1 << 9)
+#define INTRL2_0_DESC_ALLOC_ERR                (1 << 10)
+#define INTRL2_0_UNEXP_PKTSIZE_ACK     (1 << 11)
+
+/* RXCHK offset and defines */
+#define SYS_PORT_RXCHK_OFFSET          0x300
+
+#define RXCHK_CONTROL                  0x00
+#define  RXCHK_EN                      (1 << 0)
+#define  RXCHK_SKIP_FCS                        (1 << 1)
+#define  RXCHK_BAD_CSUM_DIS            (1 << 2)
+#define  RXCHK_BRCM_TAG_EN             (1 << 3)
+#define  RXCHK_BRCM_TAG_MATCH_SHIFT    4
+#define  RXCHK_BRCM_TAG_MATCH_MASK     0xff
+#define  RXCHK_PARSE_TNL               (1 << 12)
+#define  RXCHK_VIOL_EN                 (1 << 13)
+#define  RXCHK_VIOL_DIS                        (1 << 14)
+#define  RXCHK_INCOM_PKT               (1 << 15)
+#define  RXCHK_V6_DUPEXT_EN            (1 << 16)
+#define  RXCHK_V6_DUPEXT_DIS           (1 << 17)
+#define  RXCHK_ETHERTYPE_DIS           (1 << 18)
+#define  RXCHK_L2_HDR_DIS              (1 << 19)
+#define  RXCHK_L3_HDR_DIS              (1 << 20)
+#define  RXCHK_MAC_RX_ERR_DIS          (1 << 21)
+#define  RXCHK_PARSE_AUTH              (1 << 22)
+
+#define RXCHK_BRCM_TAG0                        0x04
+#define RXCHK_BRCM_TAG(i)              ((i) * RXCHK_BRCM_TAG0)
+#define RXCHK_BRCM_TAG0_MASK           0x24
+#define RXCHK_BRCM_TAG_MASK(i)         ((i) * RXCHK_BRCM_TAG0_MASK)
+#define RXCHK_BRCM_TAG_MATCH_STATUS    0x44
+#define RXCHK_ETHERTYPE                        0x48
+#define RXCHK_BAD_CSUM_CNTR            0x4C
+#define RXCHK_OTHER_DISC_CNTR          0x50
+
+/* TXCHCK offsets and defines */
+#define SYS_PORT_TXCHK_OFFSET          0x380
+#define TXCHK_PKT_RDY_THRESH           0x00
+
+/* Receive buffer offset and defines */
+#define SYS_PORT_RBUF_OFFSET           0x400
+
+#define RBUF_CONTROL                   0x00
+#define  RBUF_RSB_EN                   (1 << 0)
+#define  RBUF_4B_ALGN                  (1 << 1)
+#define  RBUF_BRCM_TAG_STRIP           (1 << 2)
+#define  RBUF_BAD_PKT_DISC             (1 << 3)
+#define  RBUF_RESUME_THRESH_SHIFT      4
+#define  RBUF_RESUME_THRESH_MASK       0xff
+#define  RBUF_OK_TO_SEND_SHIFT         12
+#define  RBUF_OK_TO_SEND_MASK          0xff
+#define  RBUF_CRC_REPLACE              (1 << 20)
+#define  RBUF_OK_TO_SEND_MODE          (1 << 21)
+#define  RBUF_RSB_SWAP                 (1 << 22)
+#define  RBUF_ACPI_EN                  (1 << 23)
+
+#define RBUF_PKT_RDY_THRESH            0x04
+
+#define RBUF_STATUS                    0x08
+#define  RBUF_WOL_MODE                 (1 << 0)
+#define  RBUF_MPD                      (1 << 1)
+#define  RBUF_ACPI                     (1 << 2)
+
+#define RBUF_OVFL_DISC_CNTR            0x0c
+#define RBUF_ERR_PKT_CNTR              0x10
+
+/* Transmit buffer offset and defines */
+#define SYS_PORT_TBUF_OFFSET           0x600
+
+#define TBUF_CONTROL                   0x00
+#define  TBUF_BP_EN                    (1 << 0)
+#define  TBUF_MAX_PKT_THRESH_SHIFT     1
+#define  TBUF_MAX_PKT_THRESH_MASK      0x1f
+#define  TBUF_FULL_THRESH_SHIFT                8
+#define  TBUF_FULL_THRESH_MASK         0x1f
+
+/* UniMAC offset and defines */
+#define SYS_PORT_UMAC_OFFSET           0x800
+
+#define UMAC_CMD                       0x008
+#define  CMD_TX_EN                     (1 << 0)
+#define  CMD_RX_EN                     (1 << 1)
+#define  CMD_SPEED_SHIFT               2
+#define  CMD_SPEED_10                  0
+#define  CMD_SPEED_100                 1
+#define  CMD_SPEED_1000                        2
+#define  CMD_SPEED_2500                        3
+#define  CMD_SPEED_MASK                        3
+#define  CMD_PROMISC                   (1 << 4)
+#define  CMD_PAD_EN                    (1 << 5)
+#define  CMD_CRC_FWD                   (1 << 6)
+#define  CMD_PAUSE_FWD                 (1 << 7)
+#define  CMD_RX_PAUSE_IGNORE           (1 << 8)
+#define  CMD_TX_ADDR_INS               (1 << 9)
+#define  CMD_HD_EN                     (1 << 10)
+#define  CMD_SW_RESET                  (1 << 13)
+#define  CMD_LCL_LOOP_EN               (1 << 15)
+#define  CMD_AUTO_CONFIG               (1 << 22)
+#define  CMD_CNTL_FRM_EN               (1 << 23)
+#define  CMD_NO_LEN_CHK                        (1 << 24)
+#define  CMD_RMT_LOOP_EN               (1 << 25)
+#define  CMD_PRBL_EN                   (1 << 27)
+#define  CMD_TX_PAUSE_IGNORE           (1 << 28)
+#define  CMD_TX_RX_EN                  (1 << 29)
+#define  CMD_RUNT_FILTER_DIS           (1 << 30)
+
+#define UMAC_MAC0                      0x00c
+#define UMAC_MAC1                      0x010
+#define UMAC_MAX_FRAME_LEN             0x014
+
+#define UMAC_TX_FLUSH                  0x334
+
+#define UMAC_MIB_START                 0x400
+
+/* There is a 0xC gap between the end of RX and beginning of TX stats and then
+ * between the end of TX stats and the beginning of the RX RUNT
+ */
+#define UMAC_MIB_STAT_OFFSET           0xc
+
+#define UMAC_MIB_CTRL                  0x580
+#define  MIB_RX_CNT_RST                        (1 << 0)
+#define  MIB_RUNT_CNT_RST              (1 << 1)
+#define  MIB_TX_CNT_RST                        (1 << 2)
+#define UMAC_MDF_CTRL                  0x650
+#define UMAC_MDF_ADDR                  0x654
+
+/* Receive DMA offset and defines */
+#define SYS_PORT_RDMA_OFFSET           0x2000
+
+#define RDMA_CONTROL                   0x1000
+#define  RDMA_EN                       (1 << 0)
+#define  RDMA_RING_CFG                 (1 << 1)
+#define  RDMA_DISC_EN                  (1 << 2)
+#define  RDMA_BUF_DATA_OFFSET_SHIFT    4
+#define  RDMA_BUF_DATA_OFFSET_MASK     0x3ff
+
+#define RDMA_STATUS                    0x1004
+#define  RDMA_DISABLED                 (1 << 0)
+#define  RDMA_DESC_RAM_INIT_BUSY       (1 << 1)
+#define  RDMA_BP_STATUS                        (1 << 2)
+
+#define RDMA_SCB_BURST_SIZE            0x1008
+
+#define RDMA_RING_BUF_SIZE             0x100c
+#define  RDMA_RING_SIZE_SHIFT          16
+
+#define RDMA_WRITE_PTR_HI              0x1010
+#define RDMA_WRITE_PTR_LO              0x1014
+#define RDMA_PROD_INDEX                        0x1018
+#define  RDMA_PROD_INDEX_MASK          0xffff
+
+#define RDMA_CONS_INDEX                        0x101c
+#define  RDMA_CONS_INDEX_MASK          0xffff
+
+#define RDMA_START_ADDR_HI             0x1020
+#define RDMA_START_ADDR_LO             0x1024
+#define RDMA_END_ADDR_HI               0x1028
+#define RDMA_END_ADDR_LO               0x102c
+
+#define RDMA_MBDONE_INTR               0x1030
+#define  RDMA_INTR_THRESH_MASK         0xff
+#define  RDMA_TIMEOUT_SHIFT            16
+#define  RDMA_TIMEOUT_MASK             0xffff
+
+#define RDMA_XON_XOFF_THRESH           0x1034
+#define  RDMA_XON_XOFF_THRESH_MASK     0xffff
+#define  RDMA_XOFF_THRESH_SHIFT                16
+
+#define RDMA_READ_PTR_HI               0x1038
+#define RDMA_READ_PTR_LO               0x103c
+
+#define RDMA_OVERRIDE                  0x1040
+#define  RDMA_LE_MODE                  (1 << 0)
+#define  RDMA_REG_MODE                 (1 << 1)
+
+#define RDMA_TEST                      0x1044
+#define  RDMA_TP_OUT_SEL               (1 << 0)
+#define  RDMA_MEM_SEL                  (1 << 1)
+
+#define RDMA_DEBUG                     0x1048
+
+/* Transmit DMA offset and defines */
+#define TDMA_NUM_RINGS                 32      /* rings = queues */
+#define TDMA_PORT_SIZE                 DESC_SIZE /* two 32-bits words */
+
+#define SYS_PORT_TDMA_OFFSET           0x4000
+#define TDMA_WRITE_PORT_OFFSET         0x0000
+#define TDMA_WRITE_PORT_HI(i)          (TDMA_WRITE_PORT_OFFSET + \
+                                       (i) * TDMA_PORT_SIZE)
+#define TDMA_WRITE_PORT_LO(i)          (TDMA_WRITE_PORT_OFFSET + \
+                                       sizeof(u32) + (i) * TDMA_PORT_SIZE)
+
+#define TDMA_READ_PORT_OFFSET          (TDMA_WRITE_PORT_OFFSET + \
+                                       (TDMA_NUM_RINGS * TDMA_PORT_SIZE))
+#define TDMA_READ_PORT_HI(i)           (TDMA_READ_PORT_OFFSET + \
+                                       (i) * TDMA_PORT_SIZE)
+#define TDMA_READ_PORT_LO(i)           (TDMA_READ_PORT_OFFSET + \
+                                       sizeof(u32) + (i) * TDMA_PORT_SIZE)
+
+#define TDMA_READ_PORT_CMD_OFFSET      (TDMA_READ_PORT_OFFSET + \
+                                       (TDMA_NUM_RINGS * TDMA_PORT_SIZE))
+#define TDMA_READ_PORT_CMD(i)          (TDMA_READ_PORT_CMD_OFFSET + \
+                                       (i) * sizeof(u32))
+
+#define TDMA_DESC_RING_00_BASE         (TDMA_READ_PORT_CMD_OFFSET + \
+                                       (TDMA_NUM_RINGS * sizeof(u32)))
+
+/* Register offsets and defines relatives to a specific ring number */
+#define RING_HEAD_TAIL_PTR             0x00
+#define  RING_HEAD_MASK                        0x7ff
+#define  RING_TAIL_SHIFT               11
+#define  RING_TAIL_MASK                        0x7ff
+#define  RING_FLUSH                    (1 << 24)
+#define  RING_EN                       (1 << 25)
+
+#define RING_COUNT                     0x04
+#define  RING_COUNT_MASK               0x7ff
+#define  RING_BUFF_DONE_SHIFT          11
+#define  RING_BUFF_DONE_MASK           0x7ff
+
+#define RING_MAX_HYST                  0x08
+#define  RING_MAX_THRESH_MASK          0x7ff
+#define  RING_HYST_THRESH_SHIFT                11
+#define  RING_HYST_THRESH_MASK         0x7ff
+
+#define RING_INTR_CONTROL              0x0c
+#define  RING_INTR_THRESH_MASK         0x7ff
+#define  RING_EMPTY_INTR_EN            (1 << 15)
+#define  RING_TIMEOUT_SHIFT            16
+#define  RING_TIMEOUT_MASK             0xffff
+
+#define RING_PROD_CONS_INDEX           0x10
+#define  RING_PROD_INDEX_MASK          0xffff
+#define  RING_CONS_INDEX_SHIFT         16
+#define  RING_CONS_INDEX_MASK          0xffff
+
+#define RING_MAPPING                   0x14
+#define  RING_QID_MASK                 0x3
+#define  RING_PORT_ID_SHIFT            3
+#define  RING_PORT_ID_MASK             0x7
+#define  RING_IGNORE_STATUS            (1 << 6)
+#define  RING_FAILOVER_EN              (1 << 7)
+#define  RING_CREDIT_SHIFT             8
+#define  RING_CREDIT_MASK              0xffff
+
+#define RING_PCP_DEI_VID               0x18
+#define  RING_VID_MASK                 0x7ff
+#define  RING_DEI                      (1 << 12)
+#define  RING_PCP_SHIFT                        13
+#define  RING_PCP_MASK                 0x7
+#define  RING_PKT_SIZE_ADJ_SHIFT       16
+#define  RING_PKT_SIZE_ADJ_MASK                0xf
+
+#define TDMA_DESC_RING_SIZE            28
+
+/* Defininition for a given TX ring base address */
+#define TDMA_DESC_RING_BASE(i)         (TDMA_DESC_RING_00_BASE + \
+                                       ((i) * TDMA_DESC_RING_SIZE))
+
+/* Ring indexed register addreses */
+#define TDMA_DESC_RING_HEAD_TAIL_PTR(i)        (TDMA_DESC_RING_BASE(i) + \
+                                       RING_HEAD_TAIL_PTR)
+#define TDMA_DESC_RING_COUNT(i)                (TDMA_DESC_RING_BASE(i) + \
+                                       RING_COUNT)
+#define TDMA_DESC_RING_MAX_HYST(i)     (TDMA_DESC_RING_BASE(i) + \
+                                       RING_MAX_HYST)
+#define TDMA_DESC_RING_INTR_CONTROL(i) (TDMA_DESC_RING_BASE(i) + \
+                                       RING_INTR_CONTROL)
+#define TDMA_DESC_RING_PROD_CONS_INDEX(i) \
+                                       (TDMA_DESC_RING_BASE(i) + \
+                                       RING_PROD_CONS_INDEX)
+#define TDMA_DESC_RING_MAPPING(i)      (TDMA_DESC_RING_BASE(i) + \
+                                       RING_MAPPING)
+#define TDMA_DESC_RING_PCP_DEI_VID(i)  (TDMA_DESC_RING_BASE(i) + \
+                                       RING_PCP_DEI_VID)
+
+#define TDMA_CONTROL                   0x600
+#define  TDMA_EN                       (1 << 0)
+#define  TSB_EN                                (1 << 1)
+#define  TSB_SWAP                      (1 << 2)
+#define  ACB_ALGO                      (1 << 3)
+#define  BUF_DATA_OFFSET_SHIFT         4
+#define  BUF_DATA_OFFSET_MASK          0x3ff
+#define  VLAN_EN                       (1 << 14)
+#define  SW_BRCM_TAG                   (1 << 15)
+#define  WNC_KPT_SIZE_UPDATE           (1 << 16)
+#define  SYNC_PKT_SIZE                 (1 << 17)
+#define  ACH_TXDONE_DELAY_SHIFT                18
+#define  ACH_TXDONE_DELAY_MASK         0xff
+
+#define TDMA_STATUS                    0x604
+#define  TDMA_DISABLED                 (1 << 0)
+#define  TDMA_LL_RAM_INIT_BUSY         (1 << 1)
+
+#define TDMA_SCB_BURST_SIZE            0x608
+#define TDMA_OVER_MAX_THRESH_STATUS    0x60c
+#define TDMA_OVER_HYST_THRESH_STATUS   0x610
+#define TDMA_TPID                      0x614
+
+#define TDMA_FREE_LIST_HEAD_TAIL_PTR   0x618
+#define  TDMA_FREE_HEAD_MASK           0x7ff
+#define  TDMA_FREE_TAIL_SHIFT          11
+#define  TDMA_FREE_TAIL_MASK           0x7ff
+
+#define TDMA_FREE_LIST_COUNT           0x61c
+#define  TDMA_FREE_LIST_COUNT_MASK     0x7ff
+
+#define TDMA_TIER2_ARB_CTRL            0x620
+#define  TDMA_ARB_MODE_RR              0
+#define  TDMA_ARB_MODE_WEIGHT_RR       0x1
+#define  TDMA_ARB_MODE_STRICT          0x2
+#define  TDMA_ARB_MODE_DEFICIT_RR      0x3
+#define  TDMA_CREDIT_SHIFT             4
+#define  TDMA_CREDIT_MASK              0xffff
+
+#define TDMA_TIER1_ARB_0_CTRL          0x624
+#define  TDMA_ARB_EN                   (1 << 0)
+
+#define TDMA_TIER1_ARB_0_QUEUE_EN      0x628
+#define TDMA_TIER1_ARB_1_CTRL          0x62c
+#define TDMA_TIER1_ARB_1_QUEUE_EN      0x630
+#define TDMA_TIER1_ARB_2_CTRL          0x634
+#define TDMA_TIER1_ARB_2_QUEUE_EN      0x638
+#define TDMA_TIER1_ARB_3_CTRL          0x63c
+#define TDMA_TIER1_ARB_3_QUEUE_EN      0x640
+
+#define TDMA_SCB_ENDIAN_OVERRIDE       0x644
+#define  TDMA_LE_MODE                  (1 << 0)
+#define  TDMA_REG_MODE                 (1 << 1)
+
+#define TDMA_TEST                      0x648
+#define  TDMA_TP_OUT_SEL               (1 << 0)
+#define  TDMA_MEM_TM                   (1 << 1)
+
+#define TDMA_DEBUG                     0x64c
+
+/* Transmit/Receive descriptor */
+struct dma_desc {
+       u32     addr_status_len;
+       u32     addr_lo;
+};
+
+/* Number of Receive hardware descriptor words */
+#define NUM_HW_RX_DESC_WORDS           1024
+/* Real number of usable descriptors */
+#define NUM_RX_DESC                    (NUM_HW_RX_DESC_WORDS / WORDS_PER_DESC)
+
+/* Internal linked-list RAM has up to 1536 entries */
+#define NUM_TX_DESC                    1536
+
+#define WORDS_PER_DESC                 (sizeof(struct dma_desc) / sizeof(u32))
+
+/* Rx/Tx common counter group.*/
+struct bcm_sysport_pkt_counters {
+       u32     cnt_64;         /* RO Received/Transmited 64 bytes packet */
+       u32     cnt_127;        /* RO Rx/Tx 127 bytes packet */
+       u32     cnt_255;        /* RO Rx/Tx 65-255 bytes packet */
+       u32     cnt_511;        /* RO Rx/Tx 256-511 bytes packet */
+       u32     cnt_1023;       /* RO Rx/Tx 512-1023 bytes packet */
+       u32     cnt_1518;       /* RO Rx/Tx 1024-1518 bytes packet */
+       u32     cnt_mgv;        /* RO Rx/Tx 1519-1522 good VLAN packet */
+       u32     cnt_2047;       /* RO Rx/Tx 1522-2047 bytes packet*/
+       u32     cnt_4095;       /* RO Rx/Tx 2048-4095 bytes packet*/
+       u32     cnt_9216;       /* RO Rx/Tx 4096-9216 bytes packet*/
+};
+
+/* RSV, Receive Status Vector */
+struct bcm_sysport_rx_counters {
+       struct  bcm_sysport_pkt_counters pkt_cnt;
+       u32     pkt;            /* RO (0x428) Received pkt count*/
+       u32     bytes;          /* RO Received byte count */
+       u32     mca;            /* RO # of Received multicast pkt */
+       u32     bca;            /* RO # of Receive broadcast pkt */
+       u32     fcs;            /* RO # of Received FCS error  */
+       u32     cf;             /* RO # of Received control frame pkt*/
+       u32     pf;             /* RO # of Received pause frame pkt */
+       u32     uo;             /* RO # of unknown op code pkt */
+       u32     aln;            /* RO # of alignment error count */
+       u32     flr;            /* RO # of frame length out of range count */
+       u32     cde;            /* RO # of code error pkt */
+       u32     fcr;            /* RO # of carrier sense error pkt */
+       u32     ovr;            /* RO # of oversize pkt*/
+       u32     jbr;            /* RO # of jabber count */
+       u32     mtue;           /* RO # of MTU error pkt*/
+       u32     pok;            /* RO # of Received good pkt */
+       u32     uc;             /* RO # of unicast pkt */
+       u32     ppp;            /* RO # of PPP pkt */
+       u32     rcrc;           /* RO (0x470),# of CRC match pkt */
+};
+
+/* TSV, Transmit Status Vector */
+struct bcm_sysport_tx_counters {
+       struct bcm_sysport_pkt_counters pkt_cnt;
+       u32     pkts;           /* RO (0x4a8) Transmited pkt */
+       u32     mca;            /* RO # of xmited multicast pkt */
+       u32     bca;            /* RO # of xmited broadcast pkt */
+       u32     pf;             /* RO # of xmited pause frame count */
+       u32     cf;             /* RO # of xmited control frame count */
+       u32     fcs;            /* RO # of xmited FCS error count */
+       u32     ovr;            /* RO # of xmited oversize pkt */
+       u32     drf;            /* RO # of xmited deferral pkt */
+       u32     edf;            /* RO # of xmited Excessive deferral pkt*/
+       u32     scl;            /* RO # of xmited single collision pkt */
+       u32     mcl;            /* RO # of xmited multiple collision pkt*/
+       u32     lcl;            /* RO # of xmited late collision pkt */
+       u32     ecl;            /* RO # of xmited excessive collision pkt*/
+       u32     frg;            /* RO # of xmited fragments pkt*/
+       u32     ncl;            /* RO # of xmited total collision count */
+       u32     jbr;            /* RO # of xmited jabber count*/
+       u32     bytes;          /* RO # of xmited byte count */
+       u32     pok;            /* RO # of xmited good pkt */
+       u32     uc;             /* RO (0x0x4f0)# of xmited unitcast pkt */
+};
+
+struct bcm_sysport_mib {
+       struct bcm_sysport_rx_counters rx;
+       struct bcm_sysport_tx_counters tx;
+       u32 rx_runt_cnt;
+       u32 rx_runt_fcs;
+       u32 rx_runt_fcs_align;
+       u32 rx_runt_bytes;
+       u32 rxchk_bad_csum;
+       u32 rxchk_other_pkt_disc;
+       u32 rbuf_ovflow_cnt;
+       u32 rbuf_err_cnt;
+};
+
+/* HW maintains a large list of counters */
+enum bcm_sysport_stat_type {
+       BCM_SYSPORT_STAT_NETDEV = -1,
+       BCM_SYSPORT_STAT_MIB_RX,
+       BCM_SYSPORT_STAT_MIB_TX,
+       BCM_SYSPORT_STAT_RUNT,
+       BCM_SYSPORT_STAT_RXCHK,
+       BCM_SYSPORT_STAT_RBUF,
+};
+
+/* Macros to help define ethtool statistics */
+#define STAT_NETDEV(m) { \
+       .stat_string = __stringify(m), \
+       .stat_sizeof = sizeof(((struct net_device_stats *)0)->m), \
+       .stat_offset = offsetof(struct net_device_stats, m), \
+       .type = BCM_SYSPORT_STAT_NETDEV, \
+}
+
+#define STAT_MIB(str, m, _type) { \
+       .stat_string = str, \
+       .stat_sizeof = sizeof(((struct bcm_sysport_priv *)0)->m), \
+       .stat_offset = offsetof(struct bcm_sysport_priv, m), \
+       .type = _type, \
+}
+
+#define STAT_MIB_RX(str, m) STAT_MIB(str, m, BCM_SYSPORT_STAT_MIB_RX)
+#define STAT_MIB_TX(str, m) STAT_MIB(str, m, BCM_SYSPORT_STAT_MIB_TX)
+#define STAT_RUNT(str, m) STAT_MIB(str, m, BCM_SYSPORT_STAT_RUNT)
+
+#define STAT_RXCHK(str, m, ofs) { \
+       .stat_string = str, \
+       .stat_sizeof = sizeof(((struct bcm_sysport_priv *)0)->m), \
+       .stat_offset = offsetof(struct bcm_sysport_priv, m), \
+       .type = BCM_SYSPORT_STAT_RXCHK, \
+       .reg_offset = ofs, \
+}
+
+#define STAT_RBUF(str, m, ofs) { \
+       .stat_string = str, \
+       .stat_sizeof = sizeof(((struct bcm_sysport_priv *)0)->m), \
+       .stat_offset = offsetof(struct bcm_sysport_priv, m), \
+       .type = BCM_SYSPORT_STAT_RBUF, \
+       .reg_offset = ofs, \
+}
+
+struct bcm_sysport_stats {
+       char stat_string[ETH_GSTRING_LEN];
+       int stat_sizeof;
+       int stat_offset;
+       enum bcm_sysport_stat_type type;
+       /* reg offset from UMAC base for misc counters */
+       u16 reg_offset;
+};
+
+/* Software house keeping helper structure */
+struct bcm_sysport_cb {
+       struct sk_buff  *skb;           /* SKB for RX packets */
+       void __iomem    *bd_addr;       /* Buffer descriptor PHYS addr */
+
+       DEFINE_DMA_UNMAP_ADDR(dma_addr);
+       DEFINE_DMA_UNMAP_LEN(dma_len);
+};
+
+/* Software view of the TX ring */
+struct bcm_sysport_tx_ring {
+       spinlock_t      lock;           /* Ring lock for tx reclaim/xmit */
+       struct napi_struct napi;        /* NAPI per tx queue */
+       dma_addr_t      desc_dma;       /* DMA cookie */
+       unsigned int    index;          /* Ring index */
+       unsigned int    size;           /* Ring current size */
+       unsigned int    alloc_size;     /* Ring one-time allocated size */
+       unsigned int    desc_count;     /* Number of descriptors */
+       unsigned int    curr_desc;      /* Current descriptor */
+       unsigned int    c_index;        /* Last consumer index */
+       unsigned int    p_index;        /* Current producer index */
+       struct bcm_sysport_cb *cbs;     /* Transmit control blocks */
+       struct dma_desc *desc_cpu;      /* CPU view of the descriptor */
+       struct bcm_sysport_priv *priv;  /* private context backpointer */
+};
+
+/* Driver private structure */
+struct bcm_sysport_priv {
+       void __iomem            *base;
+       u32                     irq0_stat;
+       u32                     irq0_mask;
+       u32                     irq1_stat;
+       u32                     irq1_mask;
+       struct napi_struct      napi ____cacheline_aligned;
+       struct net_device       *netdev;
+       struct platform_device  *pdev;
+       int                     irq0;
+       int                     irq1;
+
+       /* Transmit rings */
+       struct bcm_sysport_tx_ring tx_rings[TDMA_NUM_RINGS];
+
+       /* Receive queue */
+       void __iomem            *rx_bds;
+       void __iomem            *rx_bd_assign_ptr;
+       unsigned int            rx_bd_assign_index;
+       struct bcm_sysport_cb   *rx_cbs;
+       unsigned int            num_rx_bds;
+       unsigned int            rx_read_ptr;
+       unsigned int            rx_c_index;
+
+       /* PHY device */
+       struct device_node      *phy_dn;
+       struct phy_device       *phydev;
+       phy_interface_t         phy_interface;
+       int                     old_pause;
+       int                     old_link;
+       int                     old_duplex;
+
+       /* Misc fields */
+       unsigned int            rx_csum_en:1;
+       unsigned int            tsb_en:1;
+       unsigned int            crc_fwd:1;
+       u16                     rev;
+
+       /* MIB related fields */
+       struct bcm_sysport_mib  mib;
+
+       /* Ethtool */
+       u32                     msg_enable;
+};
+#endif /* __BCM_SYSPORT_H */
index 0297a79a38e16312c7fe24bfd7d1992b980a8f85..05c6af6c418fa45690d085885b0cca330b58c210 100644 (file)
@@ -1436,7 +1436,7 @@ static int bgmac_probe(struct bcma_device *core)
                return -ENOMEM;
        net_dev->netdev_ops = &bgmac_netdev_ops;
        net_dev->irq = core->irq;
-       SET_ETHTOOL_OPS(net_dev, &bgmac_ethtool_ops);
+       net_dev->ethtool_ops = &bgmac_ethtool_ops;
        bgmac = netdev_priv(net_dev);
        bgmac->net_dev = net_dev;
        bgmac->core = core;
index 0ab83708b6a11a66f1b3d98873d59071d578fcdb..67d2b00473718eec10b9f1fbdbf4f6a981aa02d0 100644 (file)
@@ -6916,8 +6916,8 @@ bnx2_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
                }
        }
        else {
-               ethtool_cmd_speed_set(cmd, -1);
-               cmd->duplex = -1;
+               ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
+               cmd->duplex = DUPLEX_UNKNOWN;
        }
        spin_unlock_bh(&bp->phy_lock);
 
index 4d8f8aba0ea5d93be5e288a31da7fc12903af71f..4cab09d3f80729a2bd843cf6b0b6490f0a8e95ad 100644 (file)
@@ -6,7 +6,7 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Eliezer Tamir
  * Based on code from Michael Chan's bnx2 driver
  */
index dd57c7c5a3da8e011a83e257832a05326147b64e..47c5814114e1764145f18c9c2088e8395214602e 100644 (file)
@@ -6,7 +6,7 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Eliezer Tamir
  * Based on code from Michael Chan's bnx2 driver
  * UDP CSUM errata workaround by Arik Gendelman
@@ -906,6 +906,18 @@ static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget)
                bd_prod = RX_BD(bd_prod);
                bd_cons = RX_BD(bd_cons);
 
+               /* A rmb() is required to ensure that the CQE is not read
+                * before it is written by the adapter DMA.  PCI ordering
+                * rules will make sure the other fields are written before
+                * the marker at the end of struct eth_fast_path_rx_cqe
+                * but without rmb() a weakly ordered processor can process
+                * stale data.  Without the barrier TPA state-machine might
+                * enter inconsistent state and kernel stack might be
+                * provided with incorrect packet description - these lead
+                * to various kernel crashed.
+                */
+               rmb();
+
                cqe_fp_flags = cqe_fp->type_error_flags;
                cqe_fp_type = cqe_fp_flags & ETH_FAST_PATH_RX_CQE_TYPE;
 
index 3448cc033ca55c33d4f8b002b2828d88b6185cd5..571427c7226b11f4c22669ce8d8f14c81950a10d 100644 (file)
@@ -6,7 +6,7 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Eliezer Tamir
  * Based on code from Michael Chan's bnx2 driver
  * UDP CSUM errata workaround by Arik Gendelman
index 97ea5421dd96f41bc3daf7a899ef9f1ddaab0931..51a952c51cb1a5fda4fbb3807fc9d73da8710087 100644 (file)
@@ -12,7 +12,7 @@
  * license other than the GPL, without Broadcom's express prior written
  * consent.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Dmitry Kravkov
  *
  */
index 804b8f64463e80a1fcb45f51bda976b4d8544062..c6939ecb02c572fd41fa5d2c814ed5ad932c26e7 100644 (file)
@@ -12,7 +12,7 @@
  * license other than the GPL, without Broadcom's express prior written
  * consent.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Dmitry Kravkov
  *
  */
index b6de05e3149b5604d818d5496cbbc23ab7bf64e8..bd0600cf72660f3fbc5ac9b2ad37b481fa0969a0 100644 (file)
@@ -6,7 +6,7 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Eliezer Tamir
  * Based on code from Michael Chan's bnx2 driver
  * UDP CSUM errata workaround by Arik Gendelman
@@ -3316,7 +3316,7 @@ static u32 bnx2x_get_rxfh_indir_size(struct net_device *dev)
        return T_ETH_INDIRECTION_TABLE_SIZE;
 }
 
-static int bnx2x_get_rxfh_indir(struct net_device *dev, u32 *indir)
+static int bnx2x_get_rxfh(struct net_device *dev, u32 *indir, u8 *key)
 {
        struct bnx2x *bp = netdev_priv(dev);
        u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE] = {0};
@@ -3340,14 +3340,15 @@ static int bnx2x_get_rxfh_indir(struct net_device *dev, u32 *indir)
        return 0;
 }
 
-static int bnx2x_set_rxfh_indir(struct net_device *dev, const u32 *indir)
+static int bnx2x_set_rxfh(struct net_device *dev, const u32 *indir,
+                         const u8 *key)
 {
        struct bnx2x *bp = netdev_priv(dev);
        size_t i;
 
        for (i = 0; i < T_ETH_INDIRECTION_TABLE_SIZE; i++) {
                /*
-                * The same as in bnx2x_get_rxfh_indir: we can't use a memcpy()
+                * The same as in bnx2x_get_rxfh: we can't use a memcpy()
                 * as an internal storage of an indirection table is a u8 array
                 * while indir->ring_index points to an array of u32.
                 *
@@ -3471,8 +3472,8 @@ static const struct ethtool_ops bnx2x_ethtool_ops = {
        .get_rxnfc              = bnx2x_get_rxnfc,
        .set_rxnfc              = bnx2x_set_rxnfc,
        .get_rxfh_indir_size    = bnx2x_get_rxfh_indir_size,
-       .get_rxfh_indir         = bnx2x_get_rxfh_indir,
-       .set_rxfh_indir         = bnx2x_set_rxfh_indir,
+       .get_rxfh               = bnx2x_get_rxfh,
+       .set_rxfh               = bnx2x_set_rxfh,
        .get_channels           = bnx2x_get_channels,
        .set_channels           = bnx2x_set_channels,
        .get_module_info        = bnx2x_get_module_info,
@@ -3498,16 +3499,14 @@ static const struct ethtool_ops bnx2x_vf_ethtool_ops = {
        .get_rxnfc              = bnx2x_get_rxnfc,
        .set_rxnfc              = bnx2x_set_rxnfc,
        .get_rxfh_indir_size    = bnx2x_get_rxfh_indir_size,
-       .get_rxfh_indir         = bnx2x_get_rxfh_indir,
-       .set_rxfh_indir         = bnx2x_set_rxfh_indir,
+       .get_rxfh               = bnx2x_get_rxfh,
+       .set_rxfh               = bnx2x_set_rxfh,
        .get_channels           = bnx2x_get_channels,
        .set_channels           = bnx2x_set_channels,
 };
 
 void bnx2x_set_ethtool_ops(struct bnx2x *bp, struct net_device *netdev)
 {
-       if (IS_PF(bp))
-               SET_ETHTOOL_OPS(netdev, &bnx2x_ethtool_ops);
-       else /* vf */
-               SET_ETHTOOL_OPS(netdev, &bnx2x_vf_ethtool_ops);
+       netdev->ethtool_ops = (IS_PF(bp)) ?
+               &bnx2x_ethtool_ops : &bnx2x_vf_ethtool_ops;
 }
index f572ae164fce4d49317ca752e2cd0eaac1895ae3..8aafd9b5d6a2b107f67a8cce344b4bb479ccc85c 100644 (file)
@@ -6,8 +6,8 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
- * Written by: Vladislav Zolotarov <vladz@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
+ * Written by: Vladislav Zolotarov
  * Based on the original idea of John Wright <john.wright@hp.com>.
  */
 
index c2dfea7968f452defdca6fd4b1ea68758381038e..bd90e50bd8e662d4731b61f82ba9fe06071429a1 100644 (file)
@@ -7,9 +7,9 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Eliezer Tamir
- * Modified by: Vladislav Zolotarov <vladz@broadcom.com>
+ * Modified by: Vladislav Zolotarov
  */
 
 #ifndef BNX2X_INIT_H
index 8ab0dd90096085b33f4ee831f6487c3766b164af..5669ed2e87d0039ab02c3bc70636359e49c811e6 100644 (file)
@@ -8,8 +8,8 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
- * Written by: Vladislav Zolotarov <vladz@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
+ * Written by: Vladislav Zolotarov
  */
 
 #ifndef BNX2X_INIT_OPS_H
index 9b6b3d7304b6ac451f33633853b7ba1eb5c2b787..53fb4fa61b405aa540239492fef2d5a4f94fa9bd 100644 (file)
@@ -2218,7 +2218,6 @@ int bnx2x_update_pfc(struct link_params *params,
         */
        u32 val;
        struct bnx2x *bp = params->bp;
-       int bnx2x_status = 0;
        u8 bmac_loopback = (params->loopback_mode == LOOPBACK_BMAC);
 
        if (params->feature_config_flags & FEATURE_CONFIG_PFC_ENABLED)
@@ -2232,7 +2231,7 @@ int bnx2x_update_pfc(struct link_params *params,
        bnx2x_update_pfc_nig(params, vars, pfc_params);
 
        if (!vars->link_up)
-               return bnx2x_status;
+               return 0;
 
        DP(NETIF_MSG_LINK, "About to update PFC in BMAC\n");
 
@@ -2246,7 +2245,7 @@ int bnx2x_update_pfc(struct link_params *params,
                    == 0) {
                        DP(NETIF_MSG_LINK, "About to update PFC in EMAC\n");
                        bnx2x_emac_enable(params, vars, 0);
-                       return bnx2x_status;
+                       return 0;
                }
                if (CHIP_IS_E2(bp))
                        bnx2x_update_pfc_bmac2(params, vars, bmac_loopback);
@@ -2260,7 +2259,7 @@ int bnx2x_update_pfc(struct link_params *params,
                        val = 1;
                REG_WR(bp, NIG_REG_BMAC0_PAUSE_OUT_EN + params->port*4, val);
        }
-       return bnx2x_status;
+       return 0;
 }
 
 static int bnx2x_bmac1_enable(struct link_params *params,
@@ -3703,7 +3702,8 @@ static void bnx2x_warpcore_restart_AN_KR(struct bnx2x_phy *phy,
 static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy,
                                        struct link_params *params,
                                        struct link_vars *vars) {
-       u16 lane, i, cl72_ctrl, an_adv = 0;
+       u16 lane, i, cl72_ctrl, an_adv = 0, val;
+       u32 wc_lane_config;
        struct bnx2x *bp = params->bp;
        static struct bnx2x_reg_set reg_set[] = {
                {MDIO_WC_DEVAD, MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2, 0x7},
@@ -3822,15 +3822,27 @@ static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy,
                /* Enable Auto-Detect to support 1G over CL37 as well */
                bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
                                 MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X1, 0x10);
-
+               wc_lane_config = REG_RD(bp, params->shmem_base +
+                                       offsetof(struct shmem_region, dev_info.
+                                       shared_hw_config.wc_lane_config));
+               bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+                               MDIO_WC_REG_RX0_PCI_CTRL + (lane << 4), &val);
                /* Force cl48 sync_status LOW to avoid getting stuck in CL73
                 * parallel-detect loop when CL73 and CL37 are enabled.
                 */
-               CL22_WR_OVER_CL45(bp, phy, MDIO_REG_BANK_AER_BLOCK,
-                                 MDIO_AER_BLOCK_AER_REG, 0);
+               val |= 1 << 11;
+
+               /* Restore Polarity settings in case it was run over by
+                * previous link owner
+                */
+               if (wc_lane_config &
+                   (SHARED_HW_CFG_RX_LANE0_POL_FLIP_ENABLED << lane))
+                       val |= 3 << 2;
+               else
+                       val &= ~(3 << 2);
                bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
-                                MDIO_WC_REG_RXB_ANA_RX_CONTROL_PCI, 0x0800);
-               bnx2x_set_aer_mmd(params, phy);
+                                MDIO_WC_REG_RX0_PCI_CTRL + (lane << 4),
+                                val);
 
                bnx2x_disable_kr2(params, vars, phy);
        }
@@ -6473,7 +6485,6 @@ int bnx2x_test_link(struct link_params *params, struct link_vars *vars,
 static int bnx2x_link_initialize(struct link_params *params,
                                 struct link_vars *vars)
 {
-       int rc = 0;
        u8 phy_index, non_ext_phy;
        struct bnx2x *bp = params->bp;
        /* In case of external phy existence, the line speed would be the
@@ -6546,7 +6557,7 @@ static int bnx2x_link_initialize(struct link_params *params,
                        NIG_STATUS_XGXS0_LINK_STATUS |
                        NIG_STATUS_SERDES0_LINK_STATUS |
                        NIG_MASK_MI_INT));
-       return rc;
+       return 0;
 }
 
 static void bnx2x_int_link_reset(struct bnx2x_phy *phy,
@@ -12461,6 +12472,7 @@ static int bnx2x_avoid_link_flap(struct link_params *params,
        u32 dont_clear_stat, lfa_sts;
        struct bnx2x *bp = params->bp;
 
+       bnx2x_set_mdio_emac_per_phy(bp, params);
        /* Sync the link parameters */
        bnx2x_link_status_update(params, vars);
 
index 3a8e51ed5beca4a35e29dff59bfd1ac9e5064083..2887034523e065a362dded7cf025742de29539b3 100644 (file)
@@ -6,7 +6,7 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Eliezer Tamir
  * Based on code from Michael Chan's bnx2 driver
  * UDP CSUM errata workaround by Arik Gendelman
@@ -10053,6 +10053,24 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp,
 #define BCM_5710_UNDI_FW_MF_VERS       (0x05)
 #define BNX2X_PREV_UNDI_MF_PORT(p) (BAR_TSTRORM_INTMEM + 0x150c + ((p) << 4))
 #define BNX2X_PREV_UNDI_MF_FUNC(f) (BAR_TSTRORM_INTMEM + 0x184c + ((f) << 4))
+
+static bool bnx2x_prev_is_after_undi(struct bnx2x *bp)
+{
+       /* UNDI marks its presence in DORQ -
+        * it initializes CID offset for normal bell to 0x7
+        */
+       if (!(REG_RD(bp, MISC_REG_RESET_REG_1) &
+           MISC_REGISTERS_RESET_REG_1_RST_DORQ))
+               return false;
+
+       if (REG_RD(bp, DORQ_REG_NORM_CID_OFST) == 0x7) {
+               BNX2X_DEV_INFO("UNDI previously loaded\n");
+               return true;
+       }
+
+       return false;
+}
+
 static bool bnx2x_prev_unload_undi_fw_supports_mf(struct bnx2x *bp)
 {
        u8 major, minor, version;
@@ -10302,6 +10320,10 @@ static int bnx2x_prev_unload_uncommon(struct bnx2x *bp)
 
        BNX2X_DEV_INFO("Path is unmarked\n");
 
+       /* Cannot proceed with FLR if UNDI is loaded, since FW does not match */
+       if (bnx2x_prev_is_after_undi(bp))
+               goto out;
+
        /* If function has FLR capabilities, and existing FW version matches
         * the one required, then FLR will be sufficient to clean any residue
         * left by previous driver
@@ -10322,6 +10344,7 @@ static int bnx2x_prev_unload_uncommon(struct bnx2x *bp)
 
        BNX2X_DEV_INFO("Could not FLR\n");
 
+out:
        /* Close the MCP request, return failure*/
        rc = bnx2x_prev_mcp_done(bp);
        if (!rc)
@@ -10360,19 +10383,13 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp)
                /* close LLH filters towards the BRB */
                bnx2x_set_rx_filter(&bp->link_params, 0);
 
-               /* Check if the UNDI driver was previously loaded
-                * UNDI driver initializes CID offset for normal bell to 0x7
-                */
-               if (reset_reg & MISC_REGISTERS_RESET_REG_1_RST_DORQ) {
-                       tmp_reg = REG_RD(bp, DORQ_REG_NORM_CID_OFST);
-                       if (tmp_reg == 0x7) {
-                               BNX2X_DEV_INFO("UNDI previously loaded\n");
-                               prev_undi = true;
-                               /* clear the UNDI indication */
-                               REG_WR(bp, DORQ_REG_NORM_CID_OFST, 0);
-                               /* clear possible idle check errors */
-                               REG_RD(bp, NIG_REG_NIG_INT_STS_CLR_0);
-                       }
+               /* Check if the UNDI driver was previously loaded */
+               if (bnx2x_prev_is_after_undi(bp)) {
+                       prev_undi = true;
+                       /* clear the UNDI indication */
+                       REG_WR(bp, DORQ_REG_NORM_CID_OFST, 0);
+                       /* clear possible idle check errors */
+                       REG_RD(bp, NIG_REG_NIG_INT_STS_CLR_0);
                }
                if (!CHIP_IS_E1x(bp))
                        /* block FW from writing to host */
@@ -13283,8 +13300,8 @@ static int bnx2x_eeh_nic_unload(struct bnx2x *bp)
        netdev_reset_tc(bp->dev);
 
        del_timer_sync(&bp->timer);
-       cancel_delayed_work(&bp->sp_task);
-       cancel_delayed_work(&bp->period_task);
+       cancel_delayed_work_sync(&bp->sp_task);
+       cancel_delayed_work_sync(&bp->period_task);
 
        spin_lock_bh(&bp->stats_lock);
        bp->stats_state = STATS_STATE_DISABLED;
index d725317c42773822ee5f945960df8a66e0f3df66..b1936044767aca73bc494609095ae2cc4fef55f0 100644 (file)
@@ -12,7 +12,7 @@
  * license other than the GPL, without Broadcom's express prior written
  * consent.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Vladislav Zolotarov
  *
  */
index 80f6c790ed88097ed17b3c3f259179e86451eff2..718ecd2946616195cc92d53bd2f6498d525f14fe 100644 (file)
@@ -12,7 +12,7 @@
  * license other than the GPL, without Broadcom's express prior written
  * consent.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Vladislav Zolotarov
  *
  */
index faf01488d26eb9d793a245ba8d31f611739c4695..eda8583f6fc0506c2c4d64fa1f7737645b36e277 100644 (file)
@@ -12,9 +12,9 @@
  * license other than the GPL, without Broadcom's express prior written
  * consent.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
- * Written by: Shmulik Ravid <shmulikr@broadcom.com>
- *            Ariel Elior <ariele@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
+ * Written by: Shmulik Ravid
+ *            Ariel Elior <ariel.elior@qlogic.com>
  *
  */
 #include "bnx2x.h"
@@ -1071,8 +1071,10 @@ 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 VF doorbell threshold */
-       REG_WR(bp, DORQ_REG_VF_USAGE_CT_LIMIT, 4);
+       /* set the VF doorbell threshold. This threshold represents the amount
+        * of doorbells allowed in the main DORQ fifo for a specific VF.
+        */
+       REG_WR(bp, DORQ_REG_VF_USAGE_CT_LIMIT, 64);
 }
 
 void bnx2x_iov_init_dmae(struct bnx2x *bp)
@@ -2576,7 +2578,8 @@ int bnx2x_get_vf_config(struct net_device *dev, int vfidx,
 
        ivi->vf = vfidx;
        ivi->qos = 0;
-       ivi->tx_rate = 10000; /* always 10G. TBA take from link struct */
+       ivi->max_tx_rate = 10000; /* always 10G. TBA take from link struct */
+       ivi->min_tx_rate = 0;
        ivi->spoofchk = 1; /*always enabled */
        if (vf->state == VF_ENABLED) {
                /* mac and vlan are in vlan_mac objects */
index 6929adba52f97029cf1b0e4fa44b0838bef7046d..96c575e147a5b14da2b67e9ea25a044a4a882d07 100644 (file)
@@ -12,9 +12,9 @@
  * license other than the GPL, without Broadcom's express prior written
  * consent.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
- * Written by: Shmulik Ravid <shmulikr@broadcom.com>
- *            Ariel Elior <ariele@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
+ * Written by: Shmulik Ravid
+ *            Ariel Elior <ariel.elior@qlogic.com>
  */
 #ifndef BNX2X_SRIOV_H
 #define BNX2X_SRIOV_H
@@ -571,7 +571,7 @@ static inline void __iomem *bnx2x_vf_doorbells(struct bnx2x *bp)
        return NULL;
 }
 
-static inline void bnx2x_vf_pci_dealloc(struct bnx2 *bp) {return 0; }
+static inline void bnx2x_vf_pci_dealloc(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; }
index 3b75070411aab83136ac2c245ba9c65682aefebe..ca47665f94bf76fd6c9b2c1eb920b5aba7743124 100644 (file)
@@ -6,7 +6,7 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Eliezer Tamir
  * Based on code from Michael Chan's bnx2 driver
  * UDP CSUM errata workaround by Arik Gendelman
index f35845006cdd8a74c57d99d069939548188256bb..2beceaefdeea7aa5ac53f6a3028cd0fbcacbc51a 100644 (file)
@@ -6,7 +6,7 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Eliezer Tamir
  * Based on code from Michael Chan's bnx2 driver
  * UDP CSUM errata workaround by Arik Gendelman
index 784c7155b98a1977f2b809efe4ee5a45b23dc44b..d712d0ddd719bd4dd3a37b25736dfdccf156084c 100644 (file)
@@ -12,9 +12,9 @@
  * license other than the GPL, without Broadcom's express prior written
  * consent.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
- * Written by: Shmulik Ravid <shmulikr@broadcom.com>
- *            Ariel Elior <ariele@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
+ * Written by: Shmulik Ravid
+ *            Ariel Elior <ariel.elior@qlogic.com>
  */
 
 #include "bnx2x.h"
index c922b81170e5bc20c4ff69d34d16d40f853c9d8a..e21e706762c9964ad917e69b7149700a458f74fc 100644 (file)
@@ -12,8 +12,8 @@
  * license other than the GPL, without Broadcom's express prior written
  * consent.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
- * Written by: Ariel Elior <ariele@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
+ * Written by: Ariel Elior <ariel.elior@qlogic.com>
  */
 #ifndef VF_PF_IF_H
 #define VF_PF_IF_H
index 4dd48d2fa804324c40c3989c6f29fdc939418f46..8244e2b14bb44ccef6b14f13dae25ea717f03a74 100644 (file)
@@ -608,6 +608,10 @@ static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type)
                pr_err("%s: Bad type %d\n", __func__, ulp_type);
                return -EINVAL;
        }
+
+       if (ulp_type == CNIC_ULP_ISCSI)
+               cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL);
+
        mutex_lock(&cnic_lock);
        if (rcu_dereference(cp->ulp_ops[ulp_type])) {
                RCU_INIT_POINTER(cp->ulp_ops[ulp_type], NULL);
@@ -620,9 +624,7 @@ static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type)
        }
        mutex_unlock(&cnic_lock);
 
-       if (ulp_type == CNIC_ULP_ISCSI)
-               cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL);
-       else if (ulp_type == CNIC_ULP_FCOE)
+       if (ulp_type == CNIC_ULP_FCOE)
                dev->fcoe_cap = NULL;
 
        synchronize_rcu();
@@ -1039,21 +1041,17 @@ static int cnic_alloc_uio_rings(struct cnic_dev *dev, int pages)
        struct cnic_local *cp = dev->cnic_priv;
        struct cnic_uio_dev *udev;
 
-       read_lock(&cnic_dev_lock);
        list_for_each_entry(udev, &cnic_udev_list, list) {
                if (udev->pdev == dev->pcidev) {
                        udev->dev = dev;
                        if (__cnic_alloc_uio_rings(udev, pages)) {
                                udev->dev = NULL;
-                               read_unlock(&cnic_dev_lock);
                                return -ENOMEM;
                        }
                        cp->udev = udev;
-                       read_unlock(&cnic_dev_lock);
                        return 0;
                }
        }
-       read_unlock(&cnic_dev_lock);
 
        udev = kzalloc(sizeof(struct cnic_uio_dev), GFP_ATOMIC);
        if (!udev)
@@ -1067,9 +1065,7 @@ static int cnic_alloc_uio_rings(struct cnic_dev *dev, int pages)
        if (__cnic_alloc_uio_rings(udev, pages))
                goto err_udev;
 
-       write_lock(&cnic_dev_lock);
        list_add(&udev->list, &cnic_udev_list);
-       write_unlock(&cnic_dev_lock);
 
        pci_dev_get(udev->pdev);
 
@@ -5624,20 +5620,27 @@ static void cnic_rcv_netevent(struct cnic_local *cp, unsigned long event,
 {
        int if_type;
 
-       rcu_read_lock();
        for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) {
                struct cnic_ulp_ops *ulp_ops;
                void *ctx;
 
-               ulp_ops = rcu_dereference(cp->ulp_ops[if_type]);
-               if (!ulp_ops || !ulp_ops->indicate_netevent)
+               mutex_lock(&cnic_lock);
+               ulp_ops = rcu_dereference_protected(cp->ulp_ops[if_type],
+                                               lockdep_is_held(&cnic_lock));
+               if (!ulp_ops || !ulp_ops->indicate_netevent) {
+                       mutex_unlock(&cnic_lock);
                        continue;
+               }
 
                ctx = cp->ulp_handle[if_type];
 
+               set_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]);
+               mutex_unlock(&cnic_lock);
+
                ulp_ops->indicate_netevent(ctx, event, vlan_id);
+
+               clear_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]);
        }
-       rcu_read_unlock();
 }
 
 /* netdev event handler */
index 0966bd04375f1aa0384d4196b368bdf9345dc1f9..5ba1cfbd60da3555878fa8fd467c3a9a36c03642 100644 (file)
@@ -2481,7 +2481,7 @@ static int bcmgenet_probe(struct platform_device *pdev)
        dev_set_drvdata(&pdev->dev, dev);
        ether_addr_copy(dev->dev_addr, macaddr);
        dev->watchdog_timeo = 2 * HZ;
-       SET_ETHTOOL_OPS(dev, &bcmgenet_ethtool_ops);
+       dev->ethtool_ops = &bcmgenet_ethtool_ops;
        dev->netdev_ops = &bcmgenet_netdev_ops;
        netif_napi_add(dev, &priv->napi, bcmgenet_poll, 64);
 
index 4608673beaff9f7682d669e4cb7a540fed0c0f04..add8d8596084054ca1e059a360be4a1d24501122 100644 (file)
@@ -298,6 +298,7 @@ int bcmgenet_mii_config(struct net_device *dev)
 static int bcmgenet_mii_probe(struct net_device *dev)
 {
        struct bcmgenet_priv *priv = netdev_priv(dev);
+       struct device_node *dn = priv->pdev->dev.of_node;
        struct phy_device *phydev;
        unsigned int phy_flags;
        int ret;
@@ -307,15 +308,19 @@ static int bcmgenet_mii_probe(struct net_device *dev)
                return 0;
        }
 
-       if (priv->phy_dn)
-               phydev = of_phy_connect(dev, priv->phy_dn,
-                                       bcmgenet_mii_setup, 0,
-                                       priv->phy_interface);
-       else
-               phydev = of_phy_connect_fixed_link(dev,
-                                       bcmgenet_mii_setup,
-                                       priv->phy_interface);
+       /* In the case of a fixed PHY, the DT node associated
+        * to the PHY is the Ethernet MAC DT node.
+        */
+       if (of_phy_is_fixed_link(dn)) {
+               ret = of_phy_register_fixed_link(dn);
+               if (ret)
+                       return ret;
+
+               priv->phy_dn = dn;
+       }
 
+       phydev = of_phy_connect(dev, priv->phy_dn, bcmgenet_mii_setup, 0,
+                               priv->phy_interface);
        if (!phydev) {
                pr_err("could not attach to PHY\n");
                return -ENODEV;
index e5d95c5ce1ad8df29075dcc243650a0e15aa896d..df2792d8383d83568886f5047b16abfa8894ce7e 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (C) 2001, 2002, 2003, 2004 David S. Miller (davem@redhat.com)
  * Copyright (C) 2001, 2002, 2003 Jeff Garzik (jgarzik@pobox.com)
  * Copyright (C) 2004 Sun Microsystems Inc.
- * Copyright (C) 2005-2013 Broadcom Corporation.
+ * Copyright (C) 2005-2014 Broadcom Corporation.
  *
  * Firmware is:
  *     Derived from proprietary unpublished source code,
@@ -94,10 +94,10 @@ static inline void _tg3_flag_clear(enum TG3_FLAGS flag, unsigned long *bits)
 
 #define DRV_MODULE_NAME                "tg3"
 #define TG3_MAJ_NUM                    3
-#define TG3_MIN_NUM                    136
+#define TG3_MIN_NUM                    137
 #define DRV_MODULE_VERSION     \
        __stringify(TG3_MAJ_NUM) "." __stringify(TG3_MIN_NUM)
-#define DRV_MODULE_RELDATE     "Jan 03, 2014"
+#define DRV_MODULE_RELDATE     "May 11, 2014"
 
 #define RESET_KIND_SHUTDOWN    0
 #define RESET_KIND_INIT                1
@@ -3224,7 +3224,7 @@ static int tg3_nvram_read_using_eeprom(struct tg3 *tp,
        return 0;
 }
 
-#define NVRAM_CMD_TIMEOUT 10000
+#define NVRAM_CMD_TIMEOUT 100
 
 static int tg3_nvram_exec_cmd(struct tg3 *tp, u32 nvram_cmd)
 {
@@ -7871,9 +7871,7 @@ tg3_tso_bug_end:
        return NETDEV_TX_OK;
 }
 
-/* hard_start_xmit for devices that have the 4G bug and/or 40-bit bug and
- * support TG3_FLAG_HW_TSO_1 or firmware TSO only.
- */
+/* hard_start_xmit for all devices */
 static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct tg3 *tp = netdev_priv(dev);
@@ -7884,6 +7882,10 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
        struct tg3_napi *tnapi;
        struct netdev_queue *txq;
        unsigned int last;
+       struct iphdr *iph = NULL;
+       struct tcphdr *tcph = NULL;
+       __sum16 tcp_csum = 0, ip_csum = 0;
+       __be16 ip_tot_len = 0;
 
        txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));
        tnapi = &tp->napi[skb_get_queue_mapping(skb)];
@@ -7915,7 +7917,6 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
        mss = skb_shinfo(skb)->gso_size;
        if (mss) {
-               struct iphdr *iph;
                u32 tcp_opt_len, hdr_len;
 
                if (skb_cow_head(skb, 0))
@@ -7927,27 +7928,31 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
                hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb) - ETH_HLEN;
 
                if (!skb_is_gso_v6(skb)) {
+                       if (unlikely((ETH_HLEN + hdr_len) > 80) &&
+                           tg3_flag(tp, TSO_BUG))
+                               return tg3_tso_bug(tp, skb);
+
+                       ip_csum = iph->check;
+                       ip_tot_len = iph->tot_len;
                        iph->check = 0;
                        iph->tot_len = htons(mss + hdr_len);
                }
 
-               if (unlikely((ETH_HLEN + hdr_len) > 80) &&
-                   tg3_flag(tp, TSO_BUG))
-                       return tg3_tso_bug(tp, skb);
-
                base_flags |= (TXD_FLAG_CPU_PRE_DMA |
                               TXD_FLAG_CPU_POST_DMA);
 
+               tcph = tcp_hdr(skb);
+               tcp_csum = tcph->check;
+
                if (tg3_flag(tp, HW_TSO_1) ||
                    tg3_flag(tp, HW_TSO_2) ||
                    tg3_flag(tp, HW_TSO_3)) {
-                       tcp_hdr(skb)->check = 0;
+                       tcph->check = 0;
                        base_flags &= ~TXD_FLAG_TCPUDP_CSUM;
-               } else
-                       tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
-                                                                iph->daddr, 0,
-                                                                IPPROTO_TCP,
-                                                                0);
+               } else {
+                       tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
+                                                        0, IPPROTO_TCP, 0);
+               }
 
                if (tg3_flag(tp, HW_TSO_3)) {
                        mss |= (hdr_len & 0xc) << 12;
@@ -8047,6 +8052,18 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
        if (would_hit_hwbug) {
                tg3_tx_skb_unmap(tnapi, tnapi->tx_prod, i);
 
+               if (mss) {
+                       /* If it's a TSO packet, do GSO instead of
+                        * allocating and copying to a large linear SKB
+                        */
+                       if (ip_tot_len) {
+                               iph->check = ip_csum;
+                               iph->tot_len = ip_tot_len;
+                       }
+                       tcph->check = tcp_csum;
+                       return tg3_tso_bug(tp, skb);
+               }
+
                /* If the workaround fails due to memory/mapping
                 * failure, silently drop this packet.
                 */
@@ -11876,9 +11893,9 @@ static int tg3_get_eeprom_len(struct net_device *dev)
 static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data)
 {
        struct tg3 *tp = netdev_priv(dev);
-       int ret;
+       int ret, cpmu_restore = 0;
        u8  *pd;
-       u32 i, offset, len, b_offset, b_count;
+       u32 i, offset, len, b_offset, b_count, cpmu_val = 0;
        __be32 val;
 
        if (tg3_flag(tp, NO_NVRAM))
@@ -11890,6 +11907,19 @@ static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
 
        eeprom->magic = TG3_EEPROM_MAGIC;
 
+       /* Override clock, link aware and link idle modes */
+       if (tg3_flag(tp, CPMU_PRESENT)) {
+               cpmu_val = tr32(TG3_CPMU_CTRL);
+               if (cpmu_val & (CPMU_CTRL_LINK_AWARE_MODE |
+                               CPMU_CTRL_LINK_IDLE_MODE)) {
+                       tw32(TG3_CPMU_CTRL, cpmu_val &
+                                           ~(CPMU_CTRL_LINK_AWARE_MODE |
+                                            CPMU_CTRL_LINK_IDLE_MODE));
+                       cpmu_restore = 1;
+               }
+       }
+       tg3_override_clk(tp);
+
        if (offset & 3) {
                /* adjustments to start on required 4 byte boundary */
                b_offset = offset & 3;
@@ -11900,7 +11930,7 @@ static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
                }
                ret = tg3_nvram_read_be32(tp, offset-b_offset, &val);
                if (ret)
-                       return ret;
+                       goto eeprom_done;
                memcpy(data, ((char *)&val) + b_offset, b_count);
                len -= b_count;
                offset += b_count;
@@ -11912,10 +11942,20 @@ static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
        for (i = 0; i < (len - (len & 3)); i += 4) {
                ret = tg3_nvram_read_be32(tp, offset + i, &val);
                if (ret) {
+                       if (i)
+                               i -= 4;
                        eeprom->len += i;
-                       return ret;
+                       goto eeprom_done;
                }
                memcpy(pd + i, &val, 4);
+               if (need_resched()) {
+                       if (signal_pending(current)) {
+                               eeprom->len += i;
+                               ret = -EINTR;
+                               goto eeprom_done;
+                       }
+                       cond_resched();
+               }
        }
        eeprom->len += i;
 
@@ -11926,11 +11966,19 @@ static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
                b_offset = offset + len - b_count;
                ret = tg3_nvram_read_be32(tp, b_offset, &val);
                if (ret)
-                       return ret;
+                       goto eeprom_done;
                memcpy(pd, &val, b_count);
                eeprom->len += b_count;
        }
-       return 0;
+       ret = 0;
+
+eeprom_done:
+       /* Restore clock, link aware and link idle modes */
+       tg3_restore_clk(tp);
+       if (cpmu_restore)
+               tw32(TG3_CPMU_CTRL, cpmu_val);
+
+       return ret;
 }
 
 static int tg3_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data)
@@ -12484,7 +12532,7 @@ static u32 tg3_get_rxfh_indir_size(struct net_device *dev)
        return size;
 }
 
-static int tg3_get_rxfh_indir(struct net_device *dev, u32 *indir)
+static int tg3_get_rxfh(struct net_device *dev, u32 *indir, u8 *key)
 {
        struct tg3 *tp = netdev_priv(dev);
        int i;
@@ -12495,7 +12543,7 @@ static int tg3_get_rxfh_indir(struct net_device *dev, u32 *indir)
        return 0;
 }
 
-static int tg3_set_rxfh_indir(struct net_device *dev, const u32 *indir)
+static int tg3_set_rxfh(struct net_device *dev, const u32 *indir, const u8 *key)
 {
        struct tg3 *tp = netdev_priv(dev);
        size_t i;
@@ -14027,8 +14075,8 @@ static const struct ethtool_ops tg3_ethtool_ops = {
        .get_sset_count         = tg3_get_sset_count,
        .get_rxnfc              = tg3_get_rxnfc,
        .get_rxfh_indir_size    = tg3_get_rxfh_indir_size,
-       .get_rxfh_indir         = tg3_get_rxfh_indir,
-       .set_rxfh_indir         = tg3_set_rxfh_indir,
+       .get_rxfh               = tg3_get_rxfh,
+       .set_rxfh               = tg3_set_rxfh,
        .get_channels           = tg3_get_channels,
        .set_channels           = tg3_set_channels,
        .get_ts_info            = tg3_get_ts_info,
index 04321e5a356e45a0f7fc642f3817035d983dbd90..461accaf0aa40242c3756880dd6659371cdfe5f0 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (C) 2001, 2002, 2003, 2004 David S. Miller (davem@redhat.com)
  * Copyright (C) 2001 Jeff Garzik (jgarzik@pobox.com)
  * Copyright (C) 2004 Sun Microsystems Inc.
- * Copyright (C) 2007-2013 Broadcom Corporation.
+ * Copyright (C) 2007-2014 Broadcom Corporation.
  */
 
 #ifndef _T3_H
index f9e150825bb58bf0ef9e3568a1084cb8ebc61d37..882cad71ad620004aff54832e6dd9758ec025a2c 100644 (file)
@@ -266,8 +266,8 @@ bnad_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
                ethtool_cmd_speed_set(cmd, SPEED_10000);
                cmd->duplex = DUPLEX_FULL;
        } else {
-               ethtool_cmd_speed_set(cmd, -1);
-               cmd->duplex = -1;
+               ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
+               cmd->duplex = DUPLEX_UNKNOWN;
        }
        cmd->transceiver = XCVR_EXTERNAL;
        cmd->maxtxpkt = 0;
@@ -1137,5 +1137,5 @@ static const struct ethtool_ops bnad_ethtool_ops = {
 void
 bnad_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &bnad_ethtool_ops);
+       netdev->ethtool_ops = &bnad_ethtool_ops;
 }
index 521dfea44b837d57bc7a7297ac973cd4d8098d3e..25d6b2a10e4e6f7fb6b09d25706e11e2783bbea4 100644 (file)
@@ -1737,7 +1737,7 @@ static int xgmac_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, ndev);
        ether_setup(ndev);
        ndev->netdev_ops = &xgmac_netdev_ops;
-       SET_ETHTOOL_OPS(ndev, &xgmac_ethtool_ops);
+       ndev->ethtool_ops = &xgmac_ethtool_ops;
        spin_lock_init(&priv->stats_lock);
        INIT_WORK(&priv->tx_timeout_work, xgmac_tx_timeout_work);
 
index 05613a85ce617aeb48e33ed333cb5282075189f2..186566bfdbc8c579ada4136f0d71a9242e9b7629 100644 (file)
@@ -580,8 +580,8 @@ static int get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
                ethtool_cmd_speed_set(cmd, p->link_config.speed);
                cmd->duplex = p->link_config.duplex;
        } else {
-               ethtool_cmd_speed_set(cmd, -1);
-               cmd->duplex = -1;
+               ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
+               cmd->duplex = DUPLEX_UNKNOWN;
        }
 
        cmd->port = (cmd->supported & SUPPORTED_TP) ? PORT_TP : PORT_FIBRE;
@@ -1100,7 +1100,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
                netif_napi_add(netdev, &adapter->napi, t1_poll, 64);
 
-               SET_ETHTOOL_OPS(netdev, &t1_ethtool_ops);
+               netdev->ethtool_ops = &t1_ethtool_ops;
        }
 
        if (t1_init_sw_modules(adapter, bi) < 0) {
index 07bbb711b7e5a716aba3e8d2e3e958e2ce8fa506..5d9cce053cc9a90df1608d049212f639bf559e10 100644 (file)
@@ -1809,8 +1809,8 @@ static int get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
                ethtool_cmd_speed_set(cmd, p->link_config.speed);
                cmd->duplex = p->link_config.duplex;
        } else {
-               ethtool_cmd_speed_set(cmd, -1);
-               cmd->duplex = -1;
+               ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
+               cmd->duplex = DUPLEX_UNKNOWN;
        }
 
        cmd->port = (cmd->supported & SUPPORTED_TP) ? PORT_TP : PORT_FIBRE;
@@ -3291,7 +3291,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                        netdev->features |= NETIF_F_HIGHDMA;
 
                netdev->netdev_ops = &cxgb_netdev_ops;
-               SET_ETHTOOL_OPS(netdev, &cxgb_ethtool_ops);
+               netdev->ethtool_ops = &cxgb_ethtool_ops;
        }
 
        pci_set_drvdata(pdev, adapter);
index c0a9dd55f4e55215bb0e42902c12c97b241d0d3f..b0cbb2b7fd484f95ec36574feef7981753a552e9 100644 (file)
@@ -185,7 +185,7 @@ static struct net_device *get_iff_from_mac(struct adapter *adapter,
                if (ether_addr_equal(dev->dev_addr, mac)) {
                        rcu_read_lock();
                        if (vlan && vlan != VLAN_VID_MASK) {
-                               dev = __vlan_find_dev_deep(dev, htons(ETH_P_8021Q), vlan);
+                               dev = __vlan_find_dev_deep_rcu(dev, htons(ETH_P_8021Q), vlan);
                        } else if (netif_is_bond_slave(dev)) {
                                struct net_device *upper_dev;
 
index 32db37709263bc14e596056bc7a9df80cc65bb62..f503dce4ab173ca951a89db27482ac61b1f1d808 100644 (file)
@@ -357,11 +357,17 @@ enum {
        MAX_OFLD_QSETS = 16,          /* # of offload Tx/Rx queue sets */
        MAX_CTRL_QUEUES = NCHAN,      /* # of control Tx queues */
        MAX_RDMA_QUEUES = NCHAN,      /* # of streaming RDMA Rx queues */
+       MAX_RDMA_CIQS = NCHAN,        /* # of  RDMA concentrator IQs */
+       MAX_ISCSI_QUEUES = NCHAN,     /* # of streaming iSCSI Rx queues */
 };
 
 enum {
-       MAX_EGRQ = 128,         /* max # of egress queues, including FLs */
-       MAX_INGQ = 64           /* max # of interrupt-capable ingress queues */
+       INGQ_EXTRAS = 2,        /* firmware event queue and */
+                               /*   forwarded interrupts */
+       MAX_EGRQ = MAX_ETH_QSETS*2 + MAX_OFLD_QSETS*2
+                  + MAX_CTRL_QUEUES + MAX_RDMA_QUEUES + MAX_ISCSI_QUEUES,
+       MAX_INGQ = MAX_ETH_QSETS + MAX_OFLD_QSETS + MAX_RDMA_QUEUES
+                  + MAX_RDMA_CIQS + MAX_ISCSI_QUEUES + INGQ_EXTRAS,
 };
 
 struct adapter;
@@ -538,6 +544,7 @@ struct sge {
        struct sge_eth_rxq ethrxq[MAX_ETH_QSETS];
        struct sge_ofld_rxq ofldrxq[MAX_OFLD_QSETS];
        struct sge_ofld_rxq rdmarxq[MAX_RDMA_QUEUES];
+       struct sge_ofld_rxq rdmaciq[MAX_RDMA_CIQS];
        struct sge_rspq fw_evtq ____cacheline_aligned_in_smp;
 
        struct sge_rspq intrq ____cacheline_aligned_in_smp;
@@ -548,8 +555,10 @@ struct sge {
        u16 ethtxq_rover;           /* Tx queue to clean up next */
        u16 ofldqsets;              /* # of active offload queue sets */
        u16 rdmaqs;                 /* # of available RDMA Rx queues */
+       u16 rdmaciqs;               /* # of available RDMA concentrator IQs */
        u16 ofld_rxq[MAX_OFLD_QSETS];
        u16 rdma_rxq[NCHAN];
+       u16 rdma_ciq[NCHAN];
        u16 timer_val[SGE_NTIMERS];
        u8 counter_val[SGE_NCOUNTERS];
        u32 fl_pg_order;            /* large page allocation size */
@@ -577,6 +586,7 @@ struct sge {
 #define for_each_ethrxq(sge, i) for (i = 0; i < (sge)->ethqsets; i++)
 #define for_each_ofldrxq(sge, i) for (i = 0; i < (sge)->ofldqsets; i++)
 #define for_each_rdmarxq(sge, i) for (i = 0; i < (sge)->rdmaqs; i++)
+#define for_each_rdmaciq(sge, i) for (i = 0; i < (sge)->rdmaciqs; i++)
 
 struct l2t_data;
 
index 24e16e3301e086d6cff3f28181d3c97a153bf343..2f8d6b9103838d2a602718f388b45ce6fc8ba259 100644 (file)
@@ -818,12 +818,17 @@ static void name_msix_vecs(struct adapter *adap)
        for_each_rdmarxq(&adap->sge, i)
                snprintf(adap->msix_info[msi_idx++].desc, n, "%s-rdma%d",
                         adap->port[0]->name, i);
+
+       for_each_rdmaciq(&adap->sge, i)
+               snprintf(adap->msix_info[msi_idx++].desc, n, "%s-rdma-ciq%d",
+                        adap->port[0]->name, i);
 }
 
 static int request_msix_queue_irqs(struct adapter *adap)
 {
        struct sge *s = &adap->sge;
-       int err, ethqidx, ofldqidx = 0, rdmaqidx = 0, msi_index = 2;
+       int err, ethqidx, ofldqidx = 0, rdmaqidx = 0, rdmaciqqidx = 0;
+       int msi_index = 2;
 
        err = request_irq(adap->msix_info[1].vec, t4_sge_intr_msix, 0,
                          adap->msix_info[1].desc, &s->fw_evtq);
@@ -857,9 +862,21 @@ static int request_msix_queue_irqs(struct adapter *adap)
                        goto unwind;
                msi_index++;
        }
+       for_each_rdmaciq(s, rdmaciqqidx) {
+               err = request_irq(adap->msix_info[msi_index].vec,
+                                 t4_sge_intr_msix, 0,
+                                 adap->msix_info[msi_index].desc,
+                                 &s->rdmaciq[rdmaciqqidx].rspq);
+               if (err)
+                       goto unwind;
+               msi_index++;
+       }
        return 0;
 
 unwind:
+       while (--rdmaciqqidx >= 0)
+               free_irq(adap->msix_info[--msi_index].vec,
+                        &s->rdmaciq[rdmaciqqidx].rspq);
        while (--rdmaqidx >= 0)
                free_irq(adap->msix_info[--msi_index].vec,
                         &s->rdmarxq[rdmaqidx].rspq);
@@ -885,6 +902,8 @@ static void free_msix_queue_irqs(struct adapter *adap)
                free_irq(adap->msix_info[msi_index++].vec, &s->ofldrxq[i].rspq);
        for_each_rdmarxq(s, i)
                free_irq(adap->msix_info[msi_index++].vec, &s->rdmarxq[i].rspq);
+       for_each_rdmaciq(s, i)
+               free_irq(adap->msix_info[msi_index++].vec, &s->rdmaciq[i].rspq);
 }
 
 /**
@@ -1047,7 +1066,8 @@ freeout:  t4_free_sge_resources(adap);
                if (msi_idx > 0)
                        msi_idx++;
                err = t4_sge_alloc_rxq(adap, &q->rspq, false, dev, msi_idx,
-                                      &q->fl, uldrx_handler);
+                                      q->fl.size ? &q->fl : NULL,
+                                      uldrx_handler);
                if (err)
                        goto freeout;
                memset(&q->stats, 0, sizeof(q->stats));
@@ -1064,13 +1084,28 @@ freeout:        t4_free_sge_resources(adap);
                if (msi_idx > 0)
                        msi_idx++;
                err = t4_sge_alloc_rxq(adap, &q->rspq, false, adap->port[i],
-                                      msi_idx, &q->fl, uldrx_handler);
+                                      msi_idx, q->fl.size ? &q->fl : NULL,
+                                      uldrx_handler);
                if (err)
                        goto freeout;
                memset(&q->stats, 0, sizeof(q->stats));
                s->rdma_rxq[i] = q->rspq.abs_id;
        }
 
+       for_each_rdmaciq(s, i) {
+               struct sge_ofld_rxq *q = &s->rdmaciq[i];
+
+               if (msi_idx > 0)
+                       msi_idx++;
+               err = t4_sge_alloc_rxq(adap, &q->rspq, false, adap->port[i],
+                                      msi_idx, q->fl.size ? &q->fl : NULL,
+                                      uldrx_handler);
+               if (err)
+                       goto freeout;
+               memset(&q->stats, 0, sizeof(q->stats));
+               s->rdma_ciq[i] = q->rspq.abs_id;
+       }
+
        for_each_port(adap, i) {
                /*
                 * Note that ->rdmarxq[i].rspq.cntxt_id below is 0 if we don't
@@ -2252,12 +2287,19 @@ static int get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
        else if (p->port_type == FW_PORT_TYPE_FIBER_XFI ||
                 p->port_type == FW_PORT_TYPE_FIBER_XAUI)
                cmd->port = PORT_FIBRE;
-       else if (p->port_type == FW_PORT_TYPE_SFP) {
-               if (p->mod_type == FW_PORT_MOD_TYPE_TWINAX_PASSIVE ||
-                   p->mod_type == FW_PORT_MOD_TYPE_TWINAX_ACTIVE)
+       else if (p->port_type == FW_PORT_TYPE_SFP ||
+                p->port_type == FW_PORT_TYPE_QSFP_10G ||
+                p->port_type == FW_PORT_TYPE_QSFP) {
+               if (p->mod_type == FW_PORT_MOD_TYPE_LR ||
+                   p->mod_type == FW_PORT_MOD_TYPE_SR ||
+                   p->mod_type == FW_PORT_MOD_TYPE_ER ||
+                   p->mod_type == FW_PORT_MOD_TYPE_LRM)
+                       cmd->port = PORT_FIBRE;
+               else if (p->mod_type == FW_PORT_MOD_TYPE_TWINAX_PASSIVE ||
+                        p->mod_type == FW_PORT_MOD_TYPE_TWINAX_ACTIVE)
                        cmd->port = PORT_DA;
                else
-                       cmd->port = PORT_FIBRE;
+                       cmd->port = PORT_OTHER;
        } else
                cmd->port = PORT_OTHER;
 
@@ -2461,8 +2503,7 @@ static unsigned int qtimer_val(const struct adapter *adap,
 }
 
 /**
- *     set_rxq_intr_params - set a queue's interrupt holdoff parameters
- *     @adap: the adapter
+ *     set_rspq_intr_params - set a queue's interrupt holdoff parameters
  *     @q: the Rx queue
  *     @us: the hold-off time in us, or 0 to disable timer
  *     @cnt: the hold-off packet count, or 0 to disable counter
@@ -2470,9 +2511,11 @@ static unsigned int qtimer_val(const struct adapter *adap,
  *     Sets an Rx queue's interrupt hold-off time and packet count.  At least
  *     one of the two needs to be enabled for the queue to generate interrupts.
  */
-static int set_rxq_intr_params(struct adapter *adap, struct sge_rspq *q,
-                              unsigned int us, unsigned int cnt)
+static int set_rspq_intr_params(struct sge_rspq *q,
+                               unsigned int us, unsigned int cnt)
 {
+       struct adapter *adap = q->adap;
+
        if ((us | cnt) == 0)
                cnt = 1;
 
@@ -2499,24 +2542,34 @@ static int set_rxq_intr_params(struct adapter *adap, struct sge_rspq *q,
        return 0;
 }
 
-static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
+/**
+ * set_rx_intr_params - set a net devices's RX interrupt holdoff paramete!
+ * @dev: the network device
+ * @us: the hold-off time in us, or 0 to disable timer
+ * @cnt: the hold-off packet count, or 0 to disable counter
+ *
+ * Set the RX interrupt hold-off parameters for a network device.
+ */
+static int set_rx_intr_params(struct net_device *dev,
+                             unsigned int us, unsigned int cnt)
 {
-       const struct port_info *pi = netdev_priv(dev);
+       int i, err;
+       struct port_info *pi = netdev_priv(dev);
        struct adapter *adap = pi->adapter;
-       struct sge_rspq *q;
-       int i;
-       int r = 0;
-
-       for (i = pi->first_qset; i < pi->first_qset + pi->nqsets; i++) {
-               q = &adap->sge.ethrxq[i].rspq;
-               r = set_rxq_intr_params(adap, q, c->rx_coalesce_usecs,
-                       c->rx_max_coalesced_frames);
-               if (r) {
-                       dev_err(&dev->dev, "failed to set coalesce %d\n", r);
-                       break;
-               }
+       struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset];
+
+       for (i = 0; i < pi->nqsets; i++, q++) {
+               err = set_rspq_intr_params(&q->rspq, us, cnt);
+               if (err)
+                       return err;
        }
-       return r;
+       return 0;
+}
+
+static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
+{
+       return set_rx_intr_params(dev, c->rx_coalesce_usecs,
+                                 c->rx_max_coalesced_frames);
 }
 
 static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
@@ -2732,7 +2785,7 @@ static u32 get_rss_table_size(struct net_device *dev)
        return pi->rss_size;
 }
 
-static int get_rss_table(struct net_device *dev, u32 *p)
+static int get_rss_table(struct net_device *dev, u32 *p, u8 *key)
 {
        const struct port_info *pi = netdev_priv(dev);
        unsigned int n = pi->rss_size;
@@ -2742,7 +2795,7 @@ static int get_rss_table(struct net_device *dev, u32 *p)
        return 0;
 }
 
-static int set_rss_table(struct net_device *dev, const u32 *p)
+static int set_rss_table(struct net_device *dev, const u32 *p, const u8 *key)
 {
        unsigned int i;
        struct port_info *pi = netdev_priv(dev);
@@ -2844,8 +2897,8 @@ static const struct ethtool_ops cxgb_ethtool_ops = {
        .set_wol           = set_wol,
        .get_rxnfc         = get_rxnfc,
        .get_rxfh_indir_size = get_rss_table_size,
-       .get_rxfh_indir    = get_rss_table,
-       .set_rxfh_indir    = set_rss_table,
+       .get_rxfh          = get_rss_table,
+       .set_rxfh          = set_rss_table,
        .flash_device      = set_flash,
 };
 
@@ -3385,6 +3438,77 @@ unsigned int cxgb4_best_mtu(const unsigned short *mtus, unsigned short mtu,
 }
 EXPORT_SYMBOL(cxgb4_best_mtu);
 
+/**
+ *     cxgb4_best_aligned_mtu - find best MTU, [hopefully] data size aligned
+ *     @mtus: the HW MTU table
+ *     @header_size: Header Size
+ *     @data_size_max: maximum Data Segment Size
+ *     @data_size_align: desired Data Segment Size Alignment (2^N)
+ *     @mtu_idxp: HW MTU Table Index return value pointer (possibly NULL)
+ *
+ *     Similar to cxgb4_best_mtu() but instead of searching the Hardware
+ *     MTU Table based solely on a Maximum MTU parameter, we break that
+ *     parameter up into a Header Size and Maximum Data Segment Size, and
+ *     provide a desired Data Segment Size Alignment.  If we find an MTU in
+ *     the Hardware MTU Table which will result in a Data Segment Size with
+ *     the requested alignment _and_ that MTU isn't "too far" from the
+ *     closest MTU, then we'll return that rather than the closest MTU.
+ */
+unsigned int cxgb4_best_aligned_mtu(const unsigned short *mtus,
+                                   unsigned short header_size,
+                                   unsigned short data_size_max,
+                                   unsigned short data_size_align,
+                                   unsigned int *mtu_idxp)
+{
+       unsigned short max_mtu = header_size + data_size_max;
+       unsigned short data_size_align_mask = data_size_align - 1;
+       int mtu_idx, aligned_mtu_idx;
+
+       /* Scan the MTU Table till we find an MTU which is larger than our
+        * Maximum MTU or we reach the end of the table.  Along the way,
+        * record the last MTU found, if any, which will result in a Data
+        * Segment Length matching the requested alignment.
+        */
+       for (mtu_idx = 0, aligned_mtu_idx = -1; mtu_idx < NMTUS; mtu_idx++) {
+               unsigned short data_size = mtus[mtu_idx] - header_size;
+
+               /* If this MTU minus the Header Size would result in a
+                * Data Segment Size of the desired alignment, remember it.
+                */
+               if ((data_size & data_size_align_mask) == 0)
+                       aligned_mtu_idx = mtu_idx;
+
+               /* If we're not at the end of the Hardware MTU Table and the
+                * next element is larger than our Maximum MTU, drop out of
+                * the loop.
+                */
+               if (mtu_idx+1 < NMTUS && mtus[mtu_idx+1] > max_mtu)
+                       break;
+       }
+
+       /* If we fell out of the loop because we ran to the end of the table,
+        * then we just have to use the last [largest] entry.
+        */
+       if (mtu_idx == NMTUS)
+               mtu_idx--;
+
+       /* If we found an MTU which resulted in the requested Data Segment
+        * Length alignment and that's "not far" from the largest MTU which is
+        * less than or equal to the maximum MTU, then use that.
+        */
+       if (aligned_mtu_idx >= 0 &&
+           mtu_idx - aligned_mtu_idx <= 1)
+               mtu_idx = aligned_mtu_idx;
+
+       /* If the caller has passed in an MTU Index pointer, pass the
+        * MTU Index back.  Return the MTU value.
+        */
+       if (mtu_idxp)
+               *mtu_idxp = mtu_idx;
+       return mtus[mtu_idx];
+}
+EXPORT_SYMBOL(cxgb4_best_aligned_mtu);
+
 /**
  *     cxgb4_port_chan - get the HW channel of a port
  *     @dev: the net device for the port
@@ -3782,7 +3906,9 @@ static void uld_attach(struct adapter *adap, unsigned int uld)
        lli.mtus = adap->params.mtus;
        if (uld == CXGB4_ULD_RDMA) {
                lli.rxq_ids = adap->sge.rdma_rxq;
+               lli.ciq_ids = adap->sge.rdma_ciq;
                lli.nrxq = adap->sge.rdmaqs;
+               lli.nciq = adap->sge.rdmaciqs;
        } else if (uld == CXGB4_ULD_ISCSI) {
                lli.rxq_ids = adap->sge.ofld_rxq;
                lli.nrxq = adap->sge.ofldqsets;
@@ -4061,7 +4187,7 @@ static int update_root_dev_clip(struct net_device *dev)
 
        /* Parse all bond and vlan devices layered on top of the physical dev */
        for (i = 0; i < VLAN_N_VID; i++) {
-               root_dev = __vlan_find_dev_deep(dev, htons(ETH_P_8021Q), i);
+               root_dev = __vlan_find_dev_deep_rcu(dev, htons(ETH_P_8021Q), i);
                if (!root_dev)
                        continue;
 
@@ -5528,13 +5654,41 @@ static int adap_init0(struct adapter *adap)
 #undef FW_PARAM_PFVF
 #undef FW_PARAM_DEV
 
-       /*
-        * These are finalized by FW initialization, load their values now.
+       /* The MTU/MSS Table is initialized by now, so load their values.  If
+        * we're initializing the adapter, then we'll make any modifications
+        * we want to the MTU/MSS Table and also initialize the congestion
+        * parameters.
         */
        t4_read_mtu_tbl(adap, adap->params.mtus, NULL);
-       t4_load_mtus(adap, adap->params.mtus, adap->params.a_wnd,
-                    adap->params.b_wnd);
+       if (state != DEV_STATE_INIT) {
+               int i;
+
+               /* The default MTU Table contains values 1492 and 1500.
+                * However, for TCP, it's better to have two values which are
+                * a multiple of 8 +/- 4 bytes apart near this popular MTU.
+                * This allows us to have a TCP Data Payload which is a
+                * multiple of 8 regardless of what combination of TCP Options
+                * are in use (always a multiple of 4 bytes) which is
+                * important for performance reasons.  For instance, if no
+                * options are in use, then we have a 20-byte IP header and a
+                * 20-byte TCP header.  In this case, a 1500-byte MSS would
+                * result in a TCP Data Payload of 1500 - 40 == 1460 bytes
+                * which is not a multiple of 8.  So using an MSS of 1488 in
+                * this case results in a TCP Data Payload of 1448 bytes which
+                * is a multiple of 8.  On the other hand, if 12-byte TCP Time
+                * Stamps have been negotiated, then an MTU of 1500 bytes
+                * results in a TCP Data Payload of 1448 bytes which, as
+                * above, is a multiple of 8 bytes ...
+                */
+               for (i = 0; i < NMTUS; i++)
+                       if (adap->params.mtus[i] == 1492) {
+                               adap->params.mtus[i] = 1488;
+                               break;
+                       }
 
+               t4_load_mtus(adap, adap->params.mtus, adap->params.a_wnd,
+                            adap->params.b_wnd);
+       }
        t4_init_tp_params(adap);
        adap->flags |= FW_OK;
        return 0;
@@ -5669,12 +5823,12 @@ static inline bool is_x_10g_port(const struct link_config *lc)
               (lc->supported & FW_PORT_CAP_SPEED_40G) != 0;
 }
 
-static inline void init_rspq(struct sge_rspq *q, u8 timer_idx, u8 pkt_cnt_idx,
+static inline void init_rspq(struct adapter *adap, struct sge_rspq *q,
+                            unsigned int us, unsigned int cnt,
                             unsigned int size, unsigned int iqe_size)
 {
-       q->intr_params = QINTR_TIMER_IDX(timer_idx) |
-                        (pkt_cnt_idx < SGE_NCOUNTERS ? QINTR_CNT_EN : 0);
-       q->pktcnt_idx = pkt_cnt_idx < SGE_NCOUNTERS ? pkt_cnt_idx : 0;
+       q->adap = adap;
+       set_rspq_intr_params(q, us, cnt);
        q->iqe_len = iqe_size;
        q->size = size;
 }
@@ -5688,6 +5842,7 @@ static void cfg_queues(struct adapter *adap)
 {
        struct sge *s = &adap->sge;
        int i, q10g = 0, n10g = 0, qidx = 0;
+       int ciq_size;
 
        for_each_port(adap, i)
                n10g += is_x_10g_port(&adap2pinfo(adap, i)->link_cfg);
@@ -5726,12 +5881,13 @@ static void cfg_queues(struct adapter *adap)
                        s->ofldqsets = adap->params.nports;
                /* For RDMA one Rx queue per channel suffices */
                s->rdmaqs = adap->params.nports;
+               s->rdmaciqs = adap->params.nports;
        }
 
        for (i = 0; i < ARRAY_SIZE(s->ethrxq); i++) {
                struct sge_eth_rxq *r = &s->ethrxq[i];
 
-               init_rspq(&r->rspq, 0, 0, 1024, 64);
+               init_rspq(adap, &r->rspq, 5, 10, 1024, 64);
                r->fl.size = 72;
        }
 
@@ -5747,7 +5903,7 @@ static void cfg_queues(struct adapter *adap)
        for (i = 0; i < ARRAY_SIZE(s->ofldrxq); i++) {
                struct sge_ofld_rxq *r = &s->ofldrxq[i];
 
-               init_rspq(&r->rspq, 0, 0, 1024, 64);
+               init_rspq(adap, &r->rspq, 5, 1, 1024, 64);
                r->rspq.uld = CXGB4_ULD_ISCSI;
                r->fl.size = 72;
        }
@@ -5755,13 +5911,26 @@ static void cfg_queues(struct adapter *adap)
        for (i = 0; i < ARRAY_SIZE(s->rdmarxq); i++) {
                struct sge_ofld_rxq *r = &s->rdmarxq[i];
 
-               init_rspq(&r->rspq, 0, 0, 511, 64);
+               init_rspq(adap, &r->rspq, 5, 1, 511, 64);
                r->rspq.uld = CXGB4_ULD_RDMA;
                r->fl.size = 72;
        }
 
-       init_rspq(&s->fw_evtq, 6, 0, 512, 64);
-       init_rspq(&s->intrq, 6, 0, 2 * MAX_INGQ, 64);
+       ciq_size = 64 + adap->vres.cq.size + adap->tids.nftids;
+       if (ciq_size > SGE_MAX_IQ_SIZE) {
+               CH_WARN(adap, "CIQ size too small for available IQs\n");
+               ciq_size = SGE_MAX_IQ_SIZE;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(s->rdmaciq); i++) {
+               struct sge_ofld_rxq *r = &s->rdmaciq[i];
+
+               init_rspq(adap, &r->rspq, 5, 1, ciq_size, 64);
+               r->rspq.uld = CXGB4_ULD_RDMA;
+       }
+
+       init_rspq(adap, &s->fw_evtq, 0, 1, 1024, 64);
+       init_rspq(adap, &s->intrq, 0, 1, 2 * MAX_INGQ, 64);
 }
 
 /*
@@ -5808,9 +5977,9 @@ static int enable_msix(struct adapter *adap)
 
        want = s->max_ethqsets + EXTRA_VECS;
        if (is_offload(adap)) {
-               want += s->rdmaqs + s->ofldqsets;
+               want += s->rdmaqs + s->rdmaciqs + s->ofldqsets;
                /* need nchan for each possible ULD */
-               ofld_need = 2 * nchan;
+               ofld_need = 3 * nchan;
        }
        need = adap->params.nports + EXTRA_VECS + ofld_need;
 
@@ -6076,7 +6245,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                netdev->priv_flags |= IFF_UNICAST_FLT;
 
                netdev->netdev_ops = &cxgb4_netdev_ops;
-               SET_ETHTOOL_OPS(netdev, &cxgb_ethtool_ops);
+               netdev->ethtool_ops = &cxgb_ethtool_ops;
        }
 
        pci_set_drvdata(pdev, adapter);
index e274a047528fca6ed11c9773db2d0a188e16da14..55e9daf7f9d47f95ab19a83b363637b4c549990f 100644 (file)
@@ -232,8 +232,10 @@ struct cxgb4_lld_info {
        const struct cxgb4_virt_res *vr;     /* assorted HW resources */
        const unsigned short *mtus;          /* MTU table */
        const unsigned short *rxq_ids;       /* the ULD's Rx queue ids */
+       const unsigned short *ciq_ids;       /* the ULD's concentrator IQ ids */
        unsigned short nrxq;                 /* # of Rx queues */
        unsigned short ntxq;                 /* # of Tx queues */
+       unsigned short nciq;                 /* # of concentrator IQ */
        unsigned char nchan:4;               /* # of channels */
        unsigned char nports:4;              /* # of ports */
        unsigned char wr_cred;               /* WR 16-byte credits */
@@ -274,6 +276,11 @@ unsigned int cxgb4_port_viid(const struct net_device *dev);
 unsigned int cxgb4_port_idx(const struct net_device *dev);
 unsigned int cxgb4_best_mtu(const unsigned short *mtus, unsigned short mtu,
                            unsigned int *idx);
+unsigned int cxgb4_best_aligned_mtu(const unsigned short *mtus,
+                                   unsigned short header_size,
+                                   unsigned short data_size_max,
+                                   unsigned short data_size_align,
+                                   unsigned int *mtu_idxp);
 void cxgb4_get_tcp_stats(struct pci_dev *pdev, struct tp_tcp_stats *v4,
                         struct tp_tcp_stats *v6);
 void cxgb4_iscsi_init(struct net_device *dev, unsigned int tag_mask,
index e249528c8e60bbce6bdc53c9e0c65a2f8c95d836..dd4355d248e4c4134313b637154ced4efdf3c96f 100644 (file)
@@ -1697,7 +1697,8 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp,
                return handle_trace_pkt(q->adap, si);
 
        pkt = (const struct cpl_rx_pkt *)rsp;
-       csum_ok = pkt->csum_calc && !pkt->err_vec;
+       csum_ok = pkt->csum_calc && !pkt->err_vec &&
+                 (q->netdev->features & NETIF_F_RXCSUM);
        if ((pkt->l2info & htonl(RXF_TCP)) &&
            (q->netdev->features & NETIF_F_GRO) && csum_ok && !pkt->ip_frag) {
                do_gro(rxq, si, pkt);
@@ -1720,8 +1721,7 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp,
 
        rxq->stats.pkts++;
 
-       if (csum_ok && (q->netdev->features & NETIF_F_RXCSUM) &&
-           (pkt->l2info & htonl(RXF_UDP | RXF_TCP))) {
+       if (csum_ok && (pkt->l2info & htonl(RXF_UDP | RXF_TCP))) {
                if (!pkt->ip_frag) {
                        skb->ip_summed = CHECKSUM_UNNECESSARY;
                        rxq->stats.rx_cso++;
@@ -2215,7 +2215,6 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq,
        iq->cntxt_id = ntohs(c.iqid);
        iq->abs_id = ntohs(c.physiqid);
        iq->size--;                           /* subtract status entry */
-       iq->adap = adap;
        iq->netdev = dev;
        iq->handler = hnd;
 
@@ -2515,6 +2514,10 @@ void t4_free_sge_resources(struct adapter *adap)
                if (oq->rspq.desc)
                        free_rspq_fl(adap, &oq->rspq, &oq->fl);
        }
+       for (i = 0, oq = adap->sge.rdmaciq; i < adap->sge.rdmaciqs; i++, oq++) {
+               if (oq->rspq.desc)
+                       free_rspq_fl(adap, &oq->rspq, &oq->fl);
+       }
 
        /* clean up offload Tx queues */
        for (i = 0; i < ARRAY_SIZE(adap->sge.ofldtxq); i++) {
index 1d1623be9f1e9814a19ee2b1859597c2cd8ebef5..71b799b5b0f499d050237c82788903b87dbb63bb 100644 (file)
@@ -68,6 +68,7 @@ enum {
        SGE_MAX_WR_LEN = 512,     /* max WR size in bytes */
        SGE_NTIMERS = 6,          /* # of interrupt holdoff timer values */
        SGE_NCOUNTERS = 4,        /* # of interrupt packet counter values */
+       SGE_MAX_IQ_SIZE = 65520,
 
        SGE_TIMER_RSTRT_CNTR = 6, /* restart RX packet threshold counter */
        SGE_TIMER_UPD_CIDX = 7,   /* update cidx only */
index f2738c7107898f2cf9112c74e33edc2f5ab16053..973eb11aa98a06dbcc66f343c96063954ae9b90a 100644 (file)
@@ -227,6 +227,7 @@ struct cpl_pass_open_req {
 #define DELACK(x)     ((x) << 5)
 #define ULP_MODE(x)   ((x) << 8)
 #define RCV_BUFSIZ(x) ((x) << 12)
+#define RCV_BUFSIZ_MASK 0x3FFU
 #define DSCP(x)       ((x) << 22)
 #define SMAC_SEL(x)   ((u64)(x) << 28)
 #define L2T_IDX(x)    ((u64)(x) << 36)
@@ -278,6 +279,15 @@ struct cpl_pass_accept_rpl {
        __be64 opt0;
 };
 
+struct cpl_t5_pass_accept_rpl {
+       WR_HDR;
+       union opcode_tid ot;
+       __be32 opt2;
+       __be64 opt0;
+       __be32 iss;
+       __be32 rsvd;
+};
+
 struct cpl_act_open_req {
        WR_HDR;
        union opcode_tid ot;
index 52859288de7b4d5b8c550c9a1e53fe5845225da0..ff1cdd1788b5f62efdf03ffd2f501b65054383a9 100644 (file)
@@ -2664,7 +2664,7 @@ static int cxgb4vf_pci_probe(struct pci_dev *pdev,
                netdev->priv_flags |= IFF_UNICAST_FLT;
 
                netdev->netdev_ops = &cxgb4vf_netdev_ops;
-               SET_ETHTOOL_OPS(netdev, &cxgb4vf_ethtool_ops);
+               netdev->ethtool_ops = &cxgb4vf_ethtool_ops;
 
                /*
                 * Initialize the hardware/software state for the port.
index 9d88c1d50b49ba23c2539de80b3309e08943d060..bdfa80ca5e317cee72c925a4112bdeaaf2551799 100644 (file)
@@ -1510,7 +1510,8 @@ int t4vf_ethrx_handler(struct sge_rspq *rspq, const __be64 *rsp,
 {
        struct sk_buff *skb;
        const struct cpl_rx_pkt *pkt = (void *)rsp;
-       bool csum_ok = pkt->csum_calc && !pkt->err_vec;
+       bool csum_ok = pkt->csum_calc && !pkt->err_vec &&
+                      (rspq->netdev->features & NETIF_F_RXCSUM);
        struct sge_eth_rxq *rxq = container_of(rspq, struct sge_eth_rxq, rspq);
 
        /*
@@ -1538,8 +1539,8 @@ int t4vf_ethrx_handler(struct sge_rspq *rspq, const __be64 *rsp,
        skb_record_rx_queue(skb, rspq->idx);
        rxq->stats.pkts++;
 
-       if (csum_ok && (rspq->netdev->features & NETIF_F_RXCSUM) &&
-           !pkt->err_vec && (be32_to_cpu(pkt->l2info) & (RXF_UDP|RXF_TCP))) {
+       if (csum_ok && !pkt->err_vec &&
+           (be32_to_cpu(pkt->l2info) & (RXF_UDP|RXF_TCP))) {
                if (!pkt->ip_frag)
                        skb->ip_summed = CHECKSUM_UNNECESSARY;
                else {
index e35c8e0202adda08dec6d53586bdd9f7da89a2e9..14f465f239d65d8c791618c3961b03cd6ef79fed 100644 (file)
@@ -43,6 +43,8 @@
 #define ENIC_CQ_MAX            (ENIC_WQ_MAX + ENIC_RQ_MAX)
 #define ENIC_INTR_MAX          (ENIC_CQ_MAX + 2)
 
+#define ENIC_AIC_LARGE_PKT_DIFF        3
+
 struct enic_msix_entry {
        int requested;
        char devname[IFNAMSIZ];
@@ -50,6 +52,33 @@ struct enic_msix_entry {
        void *devid;
 };
 
+/* Store only the lower range.  Higher range is given by fw. */
+struct enic_intr_mod_range {
+       u32 small_pkt_range_start;
+       u32 large_pkt_range_start;
+};
+
+struct enic_intr_mod_table {
+       u32 rx_rate;
+       u32 range_percent;
+};
+
+#define ENIC_MAX_LINK_SPEEDS           3
+#define ENIC_LINK_SPEED_10G            10000
+#define ENIC_LINK_SPEED_4G             4000
+#define ENIC_LINK_40G_INDEX            2
+#define ENIC_LINK_10G_INDEX            1
+#define ENIC_LINK_4G_INDEX             0
+#define ENIC_RX_COALESCE_RANGE_END     125
+#define ENIC_AIC_TS_BREAK              100
+
+struct enic_rx_coal {
+       u32 small_pkt_range_start;
+       u32 large_pkt_range_start;
+       u32 range_end;
+       u32 use_adaptive_rx_coalesce;
+};
+
 /* priv_flags */
 #define ENIC_SRIOV_ENABLED             (1 << 0)
 
@@ -85,13 +114,12 @@ struct enic {
        u32 msg_enable;
        spinlock_t devcmd_lock;
        u8 mac_addr[ETH_ALEN];
-       u8 mc_addr[ENIC_MULTICAST_PERFECT_FILTERS][ETH_ALEN];
-       u8 uc_addr[ENIC_UNICAST_PERFECT_FILTERS][ETH_ALEN];
        unsigned int flags;
        unsigned int priv_flags;
        unsigned int mc_count;
        unsigned int uc_count;
        u32 port_mtu;
+       struct enic_rx_coal rx_coalesce_setting;
        u32 rx_coalesce_usecs;
        u32 tx_coalesce_usecs;
 #ifdef CONFIG_PCI_IOV
index 4b6e5695b263995be898f7fbd6f44108063755d5..3e27df522847def5fd3e2c2634f8edbc98eccf49 100644 (file)
@@ -88,7 +88,7 @@ int enic_dev_packet_filter(struct enic *enic, int directed, int multicast,
        return err;
 }
 
-int enic_dev_add_addr(struct enic *enic, u8 *addr)
+int enic_dev_add_addr(struct enic *enic, const u8 *addr)
 {
        int err;
 
@@ -99,7 +99,7 @@ int enic_dev_add_addr(struct enic *enic, u8 *addr)
        return err;
 }
 
-int enic_dev_del_addr(struct enic *enic, u8 *addr)
+int enic_dev_del_addr(struct enic *enic, const u8 *addr)
 {
        int err;
 
index 129b14a4efb088ef4c83212090a4c5a4125bee1a..36ea1ab25f6aa374cfc8ef9c6db4b69112592cb0 100644 (file)
@@ -45,8 +45,8 @@ int enic_dev_add_station_addr(struct enic *enic);
 int enic_dev_del_station_addr(struct enic *enic);
 int enic_dev_packet_filter(struct enic *enic, int directed, int multicast,
        int broadcast, int promisc, int allmulti);
-int enic_dev_add_addr(struct enic *enic, u8 *addr);
-int enic_dev_del_addr(struct enic *enic, u8 *addr);
+int enic_dev_add_addr(struct enic *enic, const u8 *addr);
+int enic_dev_del_addr(struct enic *enic, const u8 *addr);
 int enic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid);
 int enic_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid);
 int enic_dev_notify_unset(struct enic *enic);
index 47e3562f48667232ad16be0e57cda9c618fac430..2e50b5489d204158e89b93dfdb7be09cfba2d8c9 100644 (file)
@@ -79,6 +79,17 @@ static const struct enic_stat enic_rx_stats[] = {
 static const unsigned int enic_n_tx_stats = ARRAY_SIZE(enic_tx_stats);
 static const unsigned int enic_n_rx_stats = ARRAY_SIZE(enic_rx_stats);
 
+void enic_intr_coal_set_rx(struct enic *enic, u32 timer)
+{
+       int i;
+       int intr;
+
+       for (i = 0; i < enic->rq_count; i++) {
+               intr = enic_msix_rq_intr(enic, i);
+               vnic_intr_coalescing_timer_set(&enic->intr[intr], timer);
+       }
+}
+
 static int enic_get_settings(struct net_device *netdev,
        struct ethtool_cmd *ecmd)
 {
@@ -93,8 +104,8 @@ static int enic_get_settings(struct net_device *netdev,
                ethtool_cmd_speed_set(ecmd, vnic_dev_port_speed(enic->vdev));
                ecmd->duplex = DUPLEX_FULL;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
 
        ecmd->autoneg = AUTONEG_DISABLE;
@@ -178,9 +189,14 @@ static int enic_get_coalesce(struct net_device *netdev,
        struct ethtool_coalesce *ecmd)
 {
        struct enic *enic = netdev_priv(netdev);
+       struct enic_rx_coal *rxcoal = &enic->rx_coalesce_setting;
 
        ecmd->tx_coalesce_usecs = enic->tx_coalesce_usecs;
        ecmd->rx_coalesce_usecs = enic->rx_coalesce_usecs;
+       if (rxcoal->use_adaptive_rx_coalesce)
+               ecmd->use_adaptive_rx_coalesce = 1;
+       ecmd->rx_coalesce_usecs_low = rxcoal->small_pkt_range_start;
+       ecmd->rx_coalesce_usecs_high = rxcoal->range_end;
 
        return 0;
 }
@@ -191,17 +207,31 @@ static int enic_set_coalesce(struct net_device *netdev,
        struct enic *enic = netdev_priv(netdev);
        u32 tx_coalesce_usecs;
        u32 rx_coalesce_usecs;
+       u32 rx_coalesce_usecs_low;
+       u32 rx_coalesce_usecs_high;
+       u32 coalesce_usecs_max;
        unsigned int i, intr;
+       struct enic_rx_coal *rxcoal = &enic->rx_coalesce_setting;
 
+       coalesce_usecs_max = vnic_dev_get_intr_coal_timer_max(enic->vdev);
        tx_coalesce_usecs = min_t(u32, ecmd->tx_coalesce_usecs,
-               vnic_dev_get_intr_coal_timer_max(enic->vdev));
+                                 coalesce_usecs_max);
        rx_coalesce_usecs = min_t(u32, ecmd->rx_coalesce_usecs,
-               vnic_dev_get_intr_coal_timer_max(enic->vdev));
+                                 coalesce_usecs_max);
+
+       rx_coalesce_usecs_low = min_t(u32, ecmd->rx_coalesce_usecs_low,
+                                     coalesce_usecs_max);
+       rx_coalesce_usecs_high = min_t(u32, ecmd->rx_coalesce_usecs_high,
+                                      coalesce_usecs_max);
 
        switch (vnic_dev_get_intr_mode(enic->vdev)) {
        case VNIC_DEV_INTR_MODE_INTX:
                if (tx_coalesce_usecs != rx_coalesce_usecs)
                        return -EINVAL;
+               if (ecmd->use_adaptive_rx_coalesce      ||
+                   ecmd->rx_coalesce_usecs_low         ||
+                   ecmd->rx_coalesce_usecs_high)
+                       return -EOPNOTSUPP;
 
                intr = enic_legacy_io_intr();
                vnic_intr_coalescing_timer_set(&enic->intr[intr],
@@ -210,6 +240,10 @@ static int enic_set_coalesce(struct net_device *netdev,
        case VNIC_DEV_INTR_MODE_MSI:
                if (tx_coalesce_usecs != rx_coalesce_usecs)
                        return -EINVAL;
+               if (ecmd->use_adaptive_rx_coalesce      ||
+                   ecmd->rx_coalesce_usecs_low         ||
+                   ecmd->rx_coalesce_usecs_high)
+                       return -EOPNOTSUPP;
 
                vnic_intr_coalescing_timer_set(&enic->intr[0],
                        tx_coalesce_usecs);
@@ -221,12 +255,27 @@ static int enic_set_coalesce(struct net_device *netdev,
                                tx_coalesce_usecs);
                }
 
-               for (i = 0; i < enic->rq_count; i++) {
-                       intr = enic_msix_rq_intr(enic, i);
-                       vnic_intr_coalescing_timer_set(&enic->intr[intr],
-                               rx_coalesce_usecs);
+               if (rxcoal->use_adaptive_rx_coalesce) {
+                       if (!ecmd->use_adaptive_rx_coalesce) {
+                               rxcoal->use_adaptive_rx_coalesce = 0;
+                               enic_intr_coal_set_rx(enic, rx_coalesce_usecs);
+                       }
+               } else {
+                       if (ecmd->use_adaptive_rx_coalesce)
+                               rxcoal->use_adaptive_rx_coalesce = 1;
+                       else
+                               enic_intr_coal_set_rx(enic, rx_coalesce_usecs);
                }
 
+               if (ecmd->rx_coalesce_usecs_high) {
+                       if (rx_coalesce_usecs_high <
+                           (rx_coalesce_usecs_low + ENIC_AIC_LARGE_PKT_DIFF))
+                               return -EINVAL;
+                       rxcoal->range_end = rx_coalesce_usecs_high;
+                       rxcoal->small_pkt_range_start = rx_coalesce_usecs_low;
+                       rxcoal->large_pkt_range_start = rx_coalesce_usecs_low +
+                                                       ENIC_AIC_LARGE_PKT_DIFF;
+               }
                break;
        default:
                break;
@@ -253,5 +302,5 @@ static const struct ethtool_ops enic_ethtool_ops = {
 
 void enic_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &enic_ethtool_ops);
+       netdev->ethtool_ops = &enic_ethtool_ops;
 }
index 2945718ce8068e4355628852ed200d539c9c2273..f32f828b7f3dc31490179210f86c72c5968d84f7 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/prefetch.h>
 #include <net/ip6_checksum.h>
+#include <linux/ktime.h>
 
 #include "cq_enet_desc.h"
 #include "vnic_dev.h"
@@ -72,6 +73,35 @@ MODULE_LICENSE("GPL");
 MODULE_VERSION(DRV_VERSION);
 MODULE_DEVICE_TABLE(pci, enic_id_table);
 
+#define ENIC_LARGE_PKT_THRESHOLD               1000
+#define ENIC_MAX_COALESCE_TIMERS               10
+/*  Interrupt moderation table, which will be used to decide the
+ *  coalescing timer values
+ *  {rx_rate in Mbps, mapping percentage of the range}
+ */
+struct enic_intr_mod_table mod_table[ENIC_MAX_COALESCE_TIMERS + 1] = {
+       {4000,  0},
+       {4400, 10},
+       {5060, 20},
+       {5230, 30},
+       {5540, 40},
+       {5820, 50},
+       {6120, 60},
+       {6435, 70},
+       {6745, 80},
+       {7000, 90},
+       {0xFFFFFFFF, 100}
+};
+
+/* This table helps the driver to pick different ranges for rx coalescing
+ * timer depending on the link speed.
+ */
+struct enic_intr_mod_range mod_range[ENIC_MAX_LINK_SPEEDS] = {
+       {0,  0}, /* 0  - 4  Gbps */
+       {0,  3}, /* 4  - 10 Gbps */
+       {3,  6}, /* 10 - 40 Gbps */
+};
+
 int enic_is_dynamic(struct enic *enic)
 {
        return enic->pdev->device == PCI_DEVICE_ID_CISCO_VIC_ENET_DYN;
@@ -586,8 +616,71 @@ static struct rtnl_link_stats64 *enic_get_stats(struct net_device *netdev,
        return net_stats;
 }
 
+static int enic_mc_sync(struct net_device *netdev, const u8 *mc_addr)
+{
+       struct enic *enic = netdev_priv(netdev);
+
+       if (enic->mc_count == ENIC_MULTICAST_PERFECT_FILTERS) {
+               unsigned int mc_count = netdev_mc_count(netdev);
+
+               netdev_warn(netdev, "Registering only %d out of %d multicast addresses\n",
+                           ENIC_MULTICAST_PERFECT_FILTERS, mc_count);
+
+               return -ENOSPC;
+       }
+
+       enic_dev_add_addr(enic, mc_addr);
+       enic->mc_count++;
+
+       return 0;
+}
+
+static int enic_mc_unsync(struct net_device *netdev, const u8 *mc_addr)
+{
+       struct enic *enic = netdev_priv(netdev);
+
+       enic_dev_del_addr(enic, mc_addr);
+       enic->mc_count--;
+
+       return 0;
+}
+
+static int enic_uc_sync(struct net_device *netdev, const u8 *uc_addr)
+{
+       struct enic *enic = netdev_priv(netdev);
+
+       if (enic->uc_count == ENIC_UNICAST_PERFECT_FILTERS) {
+               unsigned int uc_count = netdev_uc_count(netdev);
+
+               netdev_warn(netdev, "Registering only %d out of %d unicast addresses\n",
+                           ENIC_UNICAST_PERFECT_FILTERS, uc_count);
+
+               return -ENOSPC;
+       }
+
+       enic_dev_add_addr(enic, uc_addr);
+       enic->uc_count++;
+
+       return 0;
+}
+
+static int enic_uc_unsync(struct net_device *netdev, const u8 *uc_addr)
+{
+       struct enic *enic = netdev_priv(netdev);
+
+       enic_dev_del_addr(enic, uc_addr);
+       enic->uc_count--;
+
+       return 0;
+}
+
 void enic_reset_addr_lists(struct enic *enic)
 {
+       struct net_device *netdev = enic->netdev;
+
+       __dev_uc_unsync(netdev, NULL);
+       __dev_mc_unsync(netdev, NULL);
+
        enic->mc_count = 0;
        enic->uc_count = 0;
        enic->flags = 0;
@@ -654,112 +747,6 @@ static int enic_set_mac_address(struct net_device *netdev, void *p)
        return enic_dev_add_station_addr(enic);
 }
 
-static void enic_update_multicast_addr_list(struct enic *enic)
-{
-       struct net_device *netdev = enic->netdev;
-       struct netdev_hw_addr *ha;
-       unsigned int mc_count = netdev_mc_count(netdev);
-       u8 mc_addr[ENIC_MULTICAST_PERFECT_FILTERS][ETH_ALEN];
-       unsigned int i, j;
-
-       if (mc_count > ENIC_MULTICAST_PERFECT_FILTERS) {
-               netdev_warn(netdev, "Registering only %d out of %d "
-                       "multicast addresses\n",
-                       ENIC_MULTICAST_PERFECT_FILTERS, mc_count);
-               mc_count = ENIC_MULTICAST_PERFECT_FILTERS;
-       }
-
-       /* Is there an easier way?  Trying to minimize to
-        * calls to add/del multicast addrs.  We keep the
-        * addrs from the last call in enic->mc_addr and
-        * look for changes to add/del.
-        */
-
-       i = 0;
-       netdev_for_each_mc_addr(ha, netdev) {
-               if (i == mc_count)
-                       break;
-               memcpy(mc_addr[i++], ha->addr, ETH_ALEN);
-       }
-
-       for (i = 0; i < enic->mc_count; i++) {
-               for (j = 0; j < mc_count; j++)
-                       if (ether_addr_equal(enic->mc_addr[i], mc_addr[j]))
-                               break;
-               if (j == mc_count)
-                       enic_dev_del_addr(enic, enic->mc_addr[i]);
-       }
-
-       for (i = 0; i < mc_count; i++) {
-               for (j = 0; j < enic->mc_count; j++)
-                       if (ether_addr_equal(mc_addr[i], enic->mc_addr[j]))
-                               break;
-               if (j == enic->mc_count)
-                       enic_dev_add_addr(enic, mc_addr[i]);
-       }
-
-       /* Save the list to compare against next time
-        */
-
-       for (i = 0; i < mc_count; i++)
-               memcpy(enic->mc_addr[i], mc_addr[i], ETH_ALEN);
-
-       enic->mc_count = mc_count;
-}
-
-static void enic_update_unicast_addr_list(struct enic *enic)
-{
-       struct net_device *netdev = enic->netdev;
-       struct netdev_hw_addr *ha;
-       unsigned int uc_count = netdev_uc_count(netdev);
-       u8 uc_addr[ENIC_UNICAST_PERFECT_FILTERS][ETH_ALEN];
-       unsigned int i, j;
-
-       if (uc_count > ENIC_UNICAST_PERFECT_FILTERS) {
-               netdev_warn(netdev, "Registering only %d out of %d "
-                       "unicast addresses\n",
-                       ENIC_UNICAST_PERFECT_FILTERS, uc_count);
-               uc_count = ENIC_UNICAST_PERFECT_FILTERS;
-       }
-
-       /* Is there an easier way?  Trying to minimize to
-        * calls to add/del unicast addrs.  We keep the
-        * addrs from the last call in enic->uc_addr and
-        * look for changes to add/del.
-        */
-
-       i = 0;
-       netdev_for_each_uc_addr(ha, netdev) {
-               if (i == uc_count)
-                       break;
-               memcpy(uc_addr[i++], ha->addr, ETH_ALEN);
-       }
-
-       for (i = 0; i < enic->uc_count; i++) {
-               for (j = 0; j < uc_count; j++)
-                       if (ether_addr_equal(enic->uc_addr[i], uc_addr[j]))
-                               break;
-               if (j == uc_count)
-                       enic_dev_del_addr(enic, enic->uc_addr[i]);
-       }
-
-       for (i = 0; i < uc_count; i++) {
-               for (j = 0; j < enic->uc_count; j++)
-                       if (ether_addr_equal(uc_addr[i], enic->uc_addr[j]))
-                               break;
-               if (j == enic->uc_count)
-                       enic_dev_add_addr(enic, uc_addr[i]);
-       }
-
-       /* Save the list to compare against next time
-        */
-
-       for (i = 0; i < uc_count; i++)
-               memcpy(enic->uc_addr[i], uc_addr[i], ETH_ALEN);
-
-       enic->uc_count = uc_count;
-}
-
 /* netif_tx_lock held, BHs disabled */
 static void enic_set_rx_mode(struct net_device *netdev)
 {
@@ -782,9 +769,9 @@ static void enic_set_rx_mode(struct net_device *netdev)
        }
 
        if (!promisc) {
-               enic_update_unicast_addr_list(enic);
+               __dev_uc_sync(netdev, enic_uc_sync, enic_uc_unsync);
                if (!allmulti)
-                       enic_update_multicast_addr_list(enic);
+                       __dev_mc_sync(netdev, enic_mc_sync, enic_mc_unsync);
        }
 }
 
@@ -979,6 +966,15 @@ static int enic_rq_alloc_buf(struct vnic_rq *rq)
        return 0;
 }
 
+static void enic_intr_update_pkt_size(struct vnic_rx_bytes_counter *pkt_size,
+                                     u32 pkt_len)
+{
+       if (ENIC_LARGE_PKT_THRESHOLD <= pkt_len)
+               pkt_size->large_pkt_bytes_cnt += pkt_len;
+       else
+               pkt_size->small_pkt_bytes_cnt += pkt_len;
+}
+
 static void enic_rq_indicate_buf(struct vnic_rq *rq,
        struct cq_desc *cq_desc, struct vnic_rq_buf *buf,
        int skipped, void *opaque)
@@ -986,6 +982,7 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,
        struct enic *enic = vnic_dev_priv(rq->vdev);
        struct net_device *netdev = enic->netdev;
        struct sk_buff *skb;
+       struct vnic_cq *cq = &enic->cq[enic_cq_rq(enic, rq->index)];
 
        u8 type, color, eop, sop, ingress_port, vlan_stripped;
        u8 fcoe, fcoe_sof, fcoe_fc_crc_ok, fcoe_enc_error, fcoe_eof;
@@ -1056,6 +1053,9 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,
                        napi_gro_receive(&enic->napi[q_number], skb);
                else
                        netif_receive_skb(skb);
+               if (enic->rx_coalesce_setting.use_adaptive_rx_coalesce)
+                       enic_intr_update_pkt_size(&cq->pkt_size_counter,
+                                                 bytes_written);
        } else {
 
                /* Buffer overflow
@@ -1134,6 +1134,64 @@ static int enic_poll(struct napi_struct *napi, int budget)
        return rq_work_done;
 }
 
+static void enic_set_int_moderation(struct enic *enic, struct vnic_rq *rq)
+{
+       unsigned int intr = enic_msix_rq_intr(enic, rq->index);
+       struct vnic_cq *cq = &enic->cq[enic_cq_rq(enic, rq->index)];
+       u32 timer = cq->tobe_rx_coal_timeval;
+
+       if (cq->tobe_rx_coal_timeval != cq->cur_rx_coal_timeval) {
+               vnic_intr_coalescing_timer_set(&enic->intr[intr], timer);
+               cq->cur_rx_coal_timeval = cq->tobe_rx_coal_timeval;
+       }
+}
+
+static void enic_calc_int_moderation(struct enic *enic, struct vnic_rq *rq)
+{
+       struct enic_rx_coal *rx_coal = &enic->rx_coalesce_setting;
+       struct vnic_cq *cq = &enic->cq[enic_cq_rq(enic, rq->index)];
+       struct vnic_rx_bytes_counter *pkt_size_counter = &cq->pkt_size_counter;
+       int index;
+       u32 timer;
+       u32 range_start;
+       u32 traffic;
+       u64 delta;
+       ktime_t now = ktime_get();
+
+       delta = ktime_us_delta(now, cq->prev_ts);
+       if (delta < ENIC_AIC_TS_BREAK)
+               return;
+       cq->prev_ts = now;
+
+       traffic = pkt_size_counter->large_pkt_bytes_cnt +
+                 pkt_size_counter->small_pkt_bytes_cnt;
+       /* The table takes Mbps
+        * traffic *= 8    => bits
+        * traffic *= (10^6 / delta)    => bps
+        * traffic /= 10^6     => Mbps
+        *
+        * Combining, traffic *= (8 / delta)
+        */
+
+       traffic <<= 3;
+       traffic = delta > UINT_MAX ? 0 : traffic / (u32)delta;
+
+       for (index = 0; index < ENIC_MAX_COALESCE_TIMERS; index++)
+               if (traffic < mod_table[index].rx_rate)
+                       break;
+       range_start = (pkt_size_counter->small_pkt_bytes_cnt >
+                      pkt_size_counter->large_pkt_bytes_cnt << 1) ?
+                     rx_coal->small_pkt_range_start :
+                     rx_coal->large_pkt_range_start;
+       timer = range_start + ((rx_coal->range_end - range_start) *
+                              mod_table[index].range_percent / 100);
+       /* Damping */
+       cq->tobe_rx_coal_timeval = (timer + cq->tobe_rx_coal_timeval) >> 1;
+
+       pkt_size_counter->large_pkt_bytes_cnt = 0;
+       pkt_size_counter->small_pkt_bytes_cnt = 0;
+}
+
 static int enic_poll_msix(struct napi_struct *napi, int budget)
 {
        struct net_device *netdev = napi->dev;
@@ -1171,6 +1229,13 @@ static int enic_poll_msix(struct napi_struct *napi, int budget)
 
        if (err)
                work_done = work_to_do;
+       if (enic->rx_coalesce_setting.use_adaptive_rx_coalesce)
+               /* Call the function which refreshes
+                * the intr coalescing timer value based on
+                * the traffic.  This is supported only in
+                * the case of MSI-x mode
+                */
+               enic_calc_int_moderation(enic, &enic->rq[rq]);
 
        if (work_done < work_to_do) {
 
@@ -1179,6 +1244,8 @@ static int enic_poll_msix(struct napi_struct *napi, int budget)
                 */
 
                napi_complete(napi);
+               if (enic->rx_coalesce_setting.use_adaptive_rx_coalesce)
+                       enic_set_int_moderation(enic, &enic->rq[rq]);
                vnic_intr_unmask(&enic->intr[intr]);
        }
 
@@ -1314,6 +1381,42 @@ static void enic_synchronize_irqs(struct enic *enic)
        }
 }
 
+static void enic_set_rx_coal_setting(struct enic *enic)
+{
+       unsigned int speed;
+       int index = -1;
+       struct enic_rx_coal *rx_coal = &enic->rx_coalesce_setting;
+
+       /* If intr mode is not MSIX, do not do adaptive coalescing */
+       if (VNIC_DEV_INTR_MODE_MSIX != vnic_dev_get_intr_mode(enic->vdev)) {
+               netdev_info(enic->netdev, "INTR mode is not MSIX, Not initializing adaptive coalescing");
+               return;
+       }
+
+       /* 1. Read the link speed from fw
+        * 2. Pick the default range for the speed
+        * 3. Update it in enic->rx_coalesce_setting
+        */
+       speed = vnic_dev_port_speed(enic->vdev);
+       if (ENIC_LINK_SPEED_10G < speed)
+               index = ENIC_LINK_40G_INDEX;
+       else if (ENIC_LINK_SPEED_4G < speed)
+               index = ENIC_LINK_10G_INDEX;
+       else
+               index = ENIC_LINK_4G_INDEX;
+
+       rx_coal->small_pkt_range_start = mod_range[index].small_pkt_range_start;
+       rx_coal->large_pkt_range_start = mod_range[index].large_pkt_range_start;
+       rx_coal->range_end = ENIC_RX_COALESCE_RANGE_END;
+
+       /* Start with the value provided by UCSM */
+       for (index = 0; index < enic->rq_count; index++)
+               enic->cq[index].cur_rx_coal_timeval =
+                               enic->config.intr_timer_usec;
+
+       rx_coal->use_adaptive_rx_coalesce = 1;
+}
+
 static int enic_dev_notify_set(struct enic *enic)
 {
        int err;
@@ -2231,6 +2334,7 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        enic->notify_timer.function = enic_notify_timer;
        enic->notify_timer.data = (unsigned long)enic;
 
+       enic_set_rx_coal_setting(enic);
        INIT_WORK(&enic->reset, enic_reset);
        INIT_WORK(&enic->change_mtu_work, enic_change_mtu_work);
 
@@ -2250,6 +2354,9 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        }
 
        enic->tx_coalesce_usecs = enic->config.intr_timer_usec;
+       /* rx coalesce time already got initialized. This gets used
+        * if adaptive coal is turned off
+        */
        enic->rx_coalesce_usecs = enic->tx_coalesce_usecs;
 
        if (enic_is_dynamic(enic) || enic_is_sriov_vf(enic))
index 579315cbe803c91130c978b5e21538240aff3018..4e6aa65857f70a0b7236decf382c3018c137517c 100644 (file)
@@ -50,6 +50,11 @@ struct vnic_cq_ctrl {
        u32 pad10;
 };
 
+struct vnic_rx_bytes_counter {
+       unsigned int small_pkt_bytes_cnt;
+       unsigned int large_pkt_bytes_cnt;
+};
+
 struct vnic_cq {
        unsigned int index;
        struct vnic_dev *vdev;
@@ -58,6 +63,10 @@ struct vnic_cq {
        unsigned int to_clean;
        unsigned int last_color;
        unsigned int interrupt_offset;
+       struct vnic_rx_bytes_counter pkt_size_counter;
+       unsigned int cur_rx_coal_timeval;
+       unsigned int tobe_rx_coal_timeval;
+       ktime_t prev_ts;
 };
 
 static inline unsigned int vnic_cq_service(struct vnic_cq *cq,
index 69dd92598b7e672ebc56a45dc1d16efc69c9929f..e86a45cb9e682980b25e5f6ebfe1772afb3a377b 100644 (file)
@@ -657,7 +657,7 @@ int vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast,
        return err;
 }
 
-int vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr)
+int vnic_dev_add_addr(struct vnic_dev *vdev, const u8 *addr)
 {
        u64 a0 = 0, a1 = 0;
        int wait = 1000;
@@ -674,7 +674,7 @@ int vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr)
        return err;
 }
 
-int vnic_dev_del_addr(struct vnic_dev *vdev, u8 *addr)
+int vnic_dev_del_addr(struct vnic_dev *vdev, const u8 *addr)
 {
        u64 a0 = 0, a1 = 0;
        int wait = 1000;
index e670029862a1bc900ca3868d12cf5436a55346d1..1f3b301f822517d89ea881014b56152d3110c918 100644 (file)
@@ -95,8 +95,8 @@ int vnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats);
 int vnic_dev_hang_notify(struct vnic_dev *vdev);
 int vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast,
        int broadcast, int promisc, int allmulti);
-int vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr);
-int vnic_dev_del_addr(struct vnic_dev *vdev, u8 *addr);
+int vnic_dev_add_addr(struct vnic_dev *vdev, const u8 *addr);
+int vnic_dev_del_addr(struct vnic_dev *vdev, const u8 *addr);
 int vnic_dev_get_mac_addr(struct vnic_dev *vdev, u8 *mac_addr);
 int vnic_dev_notify_set(struct vnic_dev *vdev, u16 intr);
 int vnic_dev_notify_unset(struct vnic_dev *vdev);
index 8c4b93be333bc704a736fa8333977f7b76800aad..13723c96d1a272896dffac2f95a1c0b18204674e 100644 (file)
@@ -109,6 +109,7 @@ typedef struct board_info {
        u8              imr_all;
 
        unsigned int    flags;
+       unsigned int    in_timeout:1;
        unsigned int    in_suspend:1;
        unsigned int    wake_supported:1;
 
@@ -187,13 +188,13 @@ dm9000_reset(board_info_t *db)
         * The essential point is that we have to do a double reset, and the
         * instruction is to set LBK into MAC internal loopback mode.
         */
-       iow(db, DM9000_NCR, 0x03);
+       iow(db, DM9000_NCR, NCR_RST | NCR_MAC_LBK);
        udelay(100); /* Application note says at least 20 us */
        if (ior(db, DM9000_NCR) & 1)
                dev_err(db->dev, "dm9000 did not respond to first reset\n");
 
        iow(db, DM9000_NCR, 0);
-       iow(db, DM9000_NCR, 0x03);
+       iow(db, DM9000_NCR, NCR_RST | NCR_MAC_LBK);
        udelay(100);
        if (ior(db, DM9000_NCR) & 1)
                dev_err(db->dev, "dm9000 did not respond to second reset\n");
@@ -273,7 +274,7 @@ static void dm9000_dumpblk_32bit(void __iomem *reg, int count)
  */
 static void dm9000_msleep(board_info_t *db, unsigned int ms)
 {
-       if (db->in_suspend)
+       if (db->in_suspend || db->in_timeout)
                mdelay(ms);
        else
                msleep(ms);
@@ -334,7 +335,8 @@ dm9000_phy_write(struct net_device *dev,
        unsigned long reg_save;
 
        dm9000_dbg(db, 5, "phy_write[%02x] = %04x\n", reg, value);
-       mutex_lock(&db->addr_lock);
+       if (!db->in_timeout)
+               mutex_lock(&db->addr_lock);
 
        spin_lock_irqsave(&db->lock, flags);
 
@@ -365,7 +367,8 @@ dm9000_phy_write(struct net_device *dev,
        writeb(reg_save, db->io_addr);
 
        spin_unlock_irqrestore(&db->lock, flags);
-       mutex_unlock(&db->addr_lock);
+       if (!db->in_timeout)
+               mutex_unlock(&db->addr_lock);
 }
 
 /* dm9000_set_io
@@ -882,6 +885,18 @@ dm9000_hash_table(struct net_device *dev)
        spin_unlock_irqrestore(&db->lock, flags);
 }
 
+static void
+dm9000_mask_interrupts(board_info_t *db)
+{
+       iow(db, DM9000_IMR, IMR_PAR);
+}
+
+static void
+dm9000_unmask_interrupts(board_info_t *db)
+{
+       iow(db, DM9000_IMR, db->imr_all);
+}
+
 /*
  * Initialize dm9000 board
  */
@@ -894,6 +909,9 @@ dm9000_init_dm9000(struct net_device *dev)
 
        dm9000_dbg(db, 1, "entering %s\n", __func__);
 
+       dm9000_reset(db);
+       dm9000_mask_interrupts(db);
+
        /* I/O mode */
        db->io_mode = ior(db, DM9000_ISR) >> 6; /* ISR bit7:6 keeps I/O mode */
 
@@ -941,9 +959,6 @@ dm9000_init_dm9000(struct net_device *dev)
 
        db->imr_all = imr;
 
-       /* Enable TX/RX interrupt mask */
-       iow(db, DM9000_IMR, imr);
-
        /* Init Driver variable */
        db->tx_pkt_cnt = 0;
        db->queue_pkt_len = 0;
@@ -959,17 +974,19 @@ static void dm9000_timeout(struct net_device *dev)
 
        /* Save previous register address */
        spin_lock_irqsave(&db->lock, flags);
+       db->in_timeout = 1;
        reg_save = readb(db->io_addr);
 
        netif_stop_queue(dev);
-       dm9000_reset(db);
        dm9000_init_dm9000(dev);
+       dm9000_unmask_interrupts(db);
        /* We can accept TX packets again */
        dev->trans_start = jiffies; /* prevent tx timeout */
        netif_wake_queue(dev);
 
        /* Restore previous register address */
        writeb(reg_save, db->io_addr);
+       db->in_timeout = 0;
        spin_unlock_irqrestore(&db->lock, flags);
 }
 
@@ -1093,7 +1110,6 @@ dm9000_rx(struct net_device *dev)
                if (rxbyte & DM9000_PKT_ERR) {
                        dev_warn(db->dev, "status check fail: %d\n", rxbyte);
                        iow(db, DM9000_RCR, 0x00);      /* Stop Device */
-                       iow(db, DM9000_ISR, IMR_PAR);   /* Stop INT request */
                        return;
                }
 
@@ -1193,9 +1209,7 @@ static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
        /* Save previous register address */
        reg_save = readb(db->io_addr);
 
-       /* Disable all interrupts */
-       iow(db, DM9000_IMR, IMR_PAR);
-
+       dm9000_mask_interrupts(db);
        /* Got DM9000 interrupt status */
        int_status = ior(db, DM9000_ISR);       /* Got ISR */
        iow(db, DM9000_ISR, int_status);        /* Clear ISR status */
@@ -1218,9 +1232,7 @@ static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
                }
        }
 
-       /* Re-enable interrupt mask */
-       iow(db, DM9000_IMR, db->imr_all);
-
+       dm9000_unmask_interrupts(db);
        /* Restore previous register address */
        writeb(reg_save, db->io_addr);
 
@@ -1291,6 +1303,9 @@ dm9000_open(struct net_device *dev)
        /* If there is no IRQ type specified, default to something that
         * may work, and tell the user that this is a problem */
 
+       if (irqflags == IRQF_TRIGGER_NONE)
+               irqflags = irq_get_trigger_type(dev->irq);
+
        if (irqflags == IRQF_TRIGGER_NONE)
                dev_warn(db->dev, "WARNING: no IRQ resource flags set.\n");
 
@@ -1301,11 +1316,14 @@ dm9000_open(struct net_device *dev)
        mdelay(1); /* delay needs by DM9000B */
 
        /* Initialize DM9000 board */
-       dm9000_reset(db);
        dm9000_init_dm9000(dev);
 
        if (request_irq(dev->irq, dm9000_interrupt, irqflags, dev->name, dev))
                return -EAGAIN;
+       /* Now that we have an interrupt handler hooked up we can unmask
+        * our interrupts
+        */
+       dm9000_unmask_interrupts(db);
 
        /* Init driver variable */
        db->dbug_cnt = 0;
@@ -1313,7 +1331,8 @@ dm9000_open(struct net_device *dev)
        mii_check_media(&db->mii, netif_msg_link(db), 1);
        netif_start_queue(dev);
 
-       dm9000_schedule_poll(db);
+       /* Poll initial link status */
+       schedule_delayed_work(&db->phy_poll, 1);
 
        return 0;
 }
@@ -1326,7 +1345,7 @@ dm9000_shutdown(struct net_device *dev)
        /* RESET device */
        dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET); /* PHY RESET */
        iow(db, DM9000_GPR, 0x01);      /* Power-Down PHY */
-       iow(db, DM9000_IMR, IMR_PAR);   /* Disable all interrupt */
+       dm9000_mask_interrupts(db);
        iow(db, DM9000_RCR, 0x00);      /* Disable RX */
 }
 
@@ -1547,12 +1566,7 @@ dm9000_probe(struct platform_device *pdev)
        db->flags |= DM9000_PLATF_SIMPLE_PHY;
 #endif
 
-       /* Fixing bug on dm9000_probe, takeover dm9000_reset(db),
-        * Need 'NCR_MAC_LBK' bit to indeed stable our DM9000 fifo
-        * while probe stage.
-        */
-
-       iow(db, DM9000_NCR, NCR_MAC_LBK | NCR_RST);
+       dm9000_reset(db);
 
        /* try multiple times, DM9000 sometimes gets the read wrong */
        for (i = 0; i < 8; i++) {
@@ -1695,8 +1709,8 @@ dm9000_drv_resume(struct device *dev)
                        /* reset if we were not in wake mode to ensure if
                         * the device was powered off it is in a known state */
                        if (!db->wake_state) {
-                               dm9000_reset(db);
                                dm9000_init_dm9000(ndev);
+                               dm9000_unmask_interrupts(db);
                        }
 
                        netif_device_attach(ndev);
index 1642de78aac84c86b51cbae27c16d9748d70fb19..861660841ce281c23a5790e62942a779998b5209 100644 (file)
@@ -1703,7 +1703,7 @@ static int tulip_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 #ifdef CONFIG_TULIP_NAPI
        netif_napi_add(dev, &tp->napi, tulip_poll, 16);
 #endif
-       SET_ETHTOOL_OPS(dev, &ops);
+       dev->ethtool_ops = &ops;
 
        if (register_netdev(dev))
                goto err_out_free_ring;
index aa801a6af7b9904752c7f3acebc2d01940527128..80afec335a117fcee3645a8d8198b5f9c7df08d6 100644 (file)
@@ -962,8 +962,8 @@ ULi_ethtool_gset(struct uli526x_board_info *db, struct ethtool_cmd *ecmd)
        }
        if(db->link_failed)
        {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
 
        if (db->media_mode & ULI526X_AUTO)
index 4fb756d219f700bfb82a37d62e9cb078fca22024..1274b6fdac8aace34559bb1fd1cae3ccca314a2e 100644 (file)
@@ -227,7 +227,7 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent)
        }
        dev->netdev_ops = &netdev_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
-       SET_ETHTOOL_OPS(dev, &ethtool_ops);
+       dev->ethtool_ops = &ethtool_ops;
 #if 0
        dev->features = NETIF_F_IP_CSUM;
 #endif
@@ -1185,8 +1185,8 @@ static int rio_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
                ethtool_cmd_speed_set(cmd, np->speed);
                cmd->duplex = np->full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
        } else {
-               ethtool_cmd_speed_set(cmd, -1);
-               cmd->duplex = -1;
+               ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
+               cmd->duplex = DUPLEX_UNKNOWN;
        }
        if ( np->an_enable)
                cmd->autoneg = AUTONEG_ENABLE;
index d9e5ca0d48c125c88e55b319975fcab5e0a4ad99..433c1e18544250bd76dddb3e1e243d8b90ce3b73 100644 (file)
@@ -577,7 +577,7 @@ static int sundance_probe1(struct pci_dev *pdev,
 
        /* The chip-specific entries in the device structure. */
        dev->netdev_ops = &netdev_ops;
-       SET_ETHTOOL_OPS(dev, &ethtool_ops);
+       dev->ethtool_ops = &ethtool_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
 
        pci_set_drvdata(pdev, dev);
index 4884205e56eef8b938e43b0d87307dc02b50d339..056b44b934773cb0084d5126dd7946d08bb24c46 100644 (file)
@@ -134,17 +134,17 @@ struct ec_bhf_priv {
 
        struct pci_dev *dev;
 
-       void * __iomem io;
-       void * __iomem dma_io;
+       void __iomem *io;
+       void __iomem *dma_io;
 
        struct hrtimer hrtimer;
 
        int tx_dma_chan;
        int rx_dma_chan;
-       void * __iomem ec_io;
-       void * __iomem fifo_io;
-       void * __iomem mii_io;
-       void * __iomem mac_io;
+       void __iomem *ec_io;
+       void __iomem *fifo_io;
+       void __iomem *mii_io;
+       void __iomem *mac_io;
 
        struct bhf_dma rx_buf;
        struct rx_desc *rx_descs;
@@ -297,7 +297,7 @@ static int ec_bhf_setup_offsets(struct ec_bhf_priv *priv)
 {
        struct device *dev = PRIV_TO_DEV(priv);
        unsigned block_count, i;
-       void * __iomem ec_info;
+       void __iomem *ec_info;
 
        dev_dbg(dev, "Info block:\n");
        dev_dbg(dev, "Type of function: %x\n", (unsigned)ioread16(priv->io));
@@ -569,8 +569,8 @@ static int ec_bhf_probe(struct pci_dev *dev, const struct pci_device_id *id)
 {
        struct net_device *net_dev;
        struct ec_bhf_priv *priv;
-       void * __iomem dma_io;
-       void * __iomem io;
+       void __iomem *dma_io;
+       void __iomem *io;
        int err = 0;
 
        err = pci_enable_device(dev);
@@ -615,7 +615,7 @@ static int ec_bhf_probe(struct pci_dev *dev, const struct pci_device_id *id)
        }
 
        net_dev = alloc_etherdev(sizeof(struct ec_bhf_priv));
-       if (net_dev == 0) {
+       if (net_dev == NULL) {
                err = -ENOMEM;
                goto err_unmap_dma_io;
        }
index 97db5a7179df1c6c733bed5e25e5be3d44d39fa1..2e7c5553955e739ba717568325a2c1be67a15c47 100644 (file)
@@ -120,6 +120,9 @@ static inline char *nic_name(struct pci_dev *pdev)
 #define MAX_VFS                        30 /* Max VFs supported by BE3 FW */
 #define FW_VER_LEN             32
 
+#define        RSS_INDIR_TABLE_LEN     128
+#define RSS_HASH_KEY_LEN       40
+
 struct be_dma_mem {
        void *va;
        dma_addr_t dma;
@@ -371,6 +374,7 @@ enum vf_state {
 #define BE_FLAGS_LINK_STATUS_INIT              1
 #define BE_FLAGS_WORKER_SCHEDULED              (1 << 3)
 #define BE_FLAGS_VLAN_PROMISC                  (1 << 4)
+#define BE_FLAGS_MCAST_PROMISC                 (1 << 5)
 #define BE_FLAGS_NAPI_ENABLED                  (1 << 9)
 #define BE_FLAGS_QNQ_ASYNC_EVT_RCVD            (1 << 11)
 #define BE_FLAGS_VXLAN_OFFLOADS                        (1 << 12)
@@ -409,6 +413,13 @@ struct be_resources {
        u32 if_cap_flags;
 };
 
+struct rss_info {
+       u64 rss_flags;
+       u8 rsstable[RSS_INDIR_TABLE_LEN];
+       u8 rss_queue[RSS_INDIR_TABLE_LEN];
+       u8 rss_hkey[RSS_HASH_KEY_LEN];
+};
+
 struct be_adapter {
        struct pci_dev *pdev;
        struct net_device *netdev;
@@ -445,7 +456,7 @@ struct be_adapter {
        struct be_drv_stats drv_stats;
        struct be_aic_obj aic_obj[MAX_EVT_QS];
        u16 vlans_added;
-       u8 vlan_tag[VLAN_N_VID];
+       unsigned long vids[BITS_TO_LONGS(VLAN_N_VID)];
        u8 vlan_prio_bmap;      /* Available Priority BitMap */
        u16 recommended_prio;   /* Recommended Priority */
        struct be_dma_mem rx_filter; /* Cmd DMA mem for rx-filter */
@@ -507,7 +518,7 @@ struct be_adapter {
        u32 msg_enable;
        int be_get_temp_freq;
        u8 pf_number;
-       u64 rss_flags;
+       struct rss_info rss_info;
 };
 
 #define be_physfn(adapter)             (!adapter->virtfn)
index d1ec15af0d2482f46c425bb7d2f6c99976c16329..f4ea3490f44657f3e90974065fea9a0eee649cd0 100644 (file)
@@ -52,8 +52,7 @@ static struct be_cmd_priv_map cmd_priv_map[] = {
        }
 };
 
-static bool be_cmd_allowed(struct be_adapter *adapter, u8 opcode,
-                          u8 subsystem)
+static bool be_cmd_allowed(struct be_adapter *adapter, u8 opcode, u8 subsystem)
 {
        int i;
        int num_entries = sizeof(cmd_priv_map)/sizeof(struct be_cmd_priv_map);
@@ -120,21 +119,28 @@ static struct be_cmd_resp_hdr *be_decode_resp_hdr(u32 tag0, u32 tag1)
        return (void *)addr;
 }
 
-static int be_mcc_compl_process(struct be_adapter *adapter,
-                               struct be_mcc_compl *compl)
+static bool be_skip_err_log(u8 opcode, u16 base_status, u16 addl_status)
 {
-       u16 compl_status, extd_status;
-       struct be_cmd_resp_hdr *resp_hdr;
-       u8 opcode = 0, subsystem = 0;
-
-       /* Just swap the status to host endian; mcc tag is opaquely copied
-        * from mcc_wrb */
-       be_dws_le_to_cpu(compl, 4);
-
-       compl_status = (compl->status >> CQE_STATUS_COMPL_SHIFT) &
-                               CQE_STATUS_COMPL_MASK;
+       if (base_status == MCC_STATUS_NOT_SUPPORTED ||
+           base_status == MCC_STATUS_ILLEGAL_REQUEST ||
+           addl_status == MCC_ADDL_STATUS_TOO_MANY_INTERFACES ||
+           (opcode == OPCODE_COMMON_WRITE_FLASHROM &&
+           (base_status == MCC_STATUS_ILLEGAL_FIELD ||
+            addl_status == MCC_ADDL_STATUS_FLASH_IMAGE_CRC_MISMATCH)))
+               return true;
+       else
+               return false;
+}
 
-       resp_hdr = be_decode_resp_hdr(compl->tag0, compl->tag1);
+/* Place holder for all the async MCC cmds wherein the caller is not in a busy
+ * loop (has not issued be_mcc_notify_wait())
+ */
+static void be_async_cmd_process(struct be_adapter *adapter,
+                                struct be_mcc_compl *compl,
+                                struct be_cmd_resp_hdr *resp_hdr)
+{
+       enum mcc_base_status base_status = base_status(compl->status);
+       u8 opcode = 0, subsystem = 0;
 
        if (resp_hdr) {
                opcode = resp_hdr->opcode;
@@ -144,61 +150,86 @@ static int be_mcc_compl_process(struct be_adapter *adapter,
        if (opcode == OPCODE_LOWLEVEL_LOOPBACK_TEST &&
            subsystem == CMD_SUBSYSTEM_LOWLEVEL) {
                complete(&adapter->et_cmd_compl);
-               return 0;
+               return;
        }
 
-       if (((opcode == OPCODE_COMMON_WRITE_FLASHROM) ||
-            (opcode == OPCODE_COMMON_WRITE_OBJECT)) &&
-           (subsystem == CMD_SUBSYSTEM_COMMON)) {
-               adapter->flash_status = compl_status;
+       if ((opcode == OPCODE_COMMON_WRITE_FLASHROM ||
+            opcode == OPCODE_COMMON_WRITE_OBJECT) &&
+           subsystem == CMD_SUBSYSTEM_COMMON) {
+               adapter->flash_status = compl->status;
                complete(&adapter->et_cmd_compl);
+               return;
        }
 
-       if (compl_status == MCC_STATUS_SUCCESS) {
-               if (((opcode == OPCODE_ETH_GET_STATISTICS) ||
-                    (opcode == OPCODE_ETH_GET_PPORT_STATS)) &&
-                   (subsystem == CMD_SUBSYSTEM_ETH)) {
-                       be_parse_stats(adapter);
-                       adapter->stats_cmd_sent = false;
-               }
-               if (opcode == OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES &&
-                   subsystem == CMD_SUBSYSTEM_COMMON) {
+       if ((opcode == OPCODE_ETH_GET_STATISTICS ||
+            opcode == OPCODE_ETH_GET_PPORT_STATS) &&
+           subsystem == CMD_SUBSYSTEM_ETH &&
+           base_status == MCC_STATUS_SUCCESS) {
+               be_parse_stats(adapter);
+               adapter->stats_cmd_sent = false;
+               return;
+       }
+
+       if (opcode == OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES &&
+           subsystem == CMD_SUBSYSTEM_COMMON) {
+               if (base_status == MCC_STATUS_SUCCESS) {
                        struct be_cmd_resp_get_cntl_addnl_attribs *resp =
-                               (void *)resp_hdr;
+                                                       (void *)resp_hdr;
                        adapter->drv_stats.be_on_die_temperature =
-                               resp->on_die_temperature;
-               }
-       } else {
-               if (opcode == OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES)
+                                               resp->on_die_temperature;
+               } else {
                        adapter->be_get_temp_freq = 0;
+               }
+               return;
+       }
+}
+
+static int be_mcc_compl_process(struct be_adapter *adapter,
+                               struct be_mcc_compl *compl)
+{
+       enum mcc_base_status base_status;
+       enum mcc_addl_status addl_status;
+       struct be_cmd_resp_hdr *resp_hdr;
+       u8 opcode = 0, subsystem = 0;
+
+       /* Just swap the status to host endian; mcc tag is opaquely copied
+        * from mcc_wrb */
+       be_dws_le_to_cpu(compl, 4);
+
+       base_status = base_status(compl->status);
+       addl_status = addl_status(compl->status);
+
+       resp_hdr = be_decode_resp_hdr(compl->tag0, compl->tag1);
+       if (resp_hdr) {
+               opcode = resp_hdr->opcode;
+               subsystem = resp_hdr->subsystem;
+       }
+
+       be_async_cmd_process(adapter, compl, resp_hdr);
 
-               if (compl_status == MCC_STATUS_NOT_SUPPORTED ||
-                       compl_status == MCC_STATUS_ILLEGAL_REQUEST)
-                       goto done;
+       if (base_status != MCC_STATUS_SUCCESS &&
+           !be_skip_err_log(opcode, base_status, addl_status)) {
 
-               if (compl_status == MCC_STATUS_UNAUTHORIZED_REQUEST) {
+               if (base_status == MCC_STATUS_UNAUTHORIZED_REQUEST) {
                        dev_warn(&adapter->pdev->dev,
                                 "VF is not privileged to issue opcode %d-%d\n",
                                 opcode, subsystem);
                } else {
-                       extd_status = (compl->status >> CQE_STATUS_EXTD_SHIFT) &
-                                       CQE_STATUS_EXTD_MASK;
                        dev_err(&adapter->pdev->dev,
                                "opcode %d-%d failed:status %d-%d\n",
-                               opcode, subsystem, compl_status, extd_status);
-
-                       if (extd_status == MCC_ADDL_STS_INSUFFICIENT_RESOURCES)
-                               return extd_status;
+                               opcode, subsystem, base_status, addl_status);
                }
        }
-done:
-       return compl_status;
+       return compl->status;
 }
 
 /* Link state evt is a string of bytes; no need for endian swapping */
 static void be_async_link_state_process(struct be_adapter *adapter,
-               struct be_async_event_link_state *evt)
+                                       struct be_mcc_compl *compl)
 {
+       struct be_async_event_link_state *evt =
+                       (struct be_async_event_link_state *)compl;
+
        /* When link status changes, link speed must be re-queried from FW */
        adapter->phy.link_speed = -1;
 
@@ -221,8 +252,11 @@ static void be_async_link_state_process(struct be_adapter *adapter,
 
 /* Grp5 CoS Priority evt */
 static void be_async_grp5_cos_priority_process(struct be_adapter *adapter,
-               struct be_async_event_grp5_cos_priority *evt)
+                                              struct be_mcc_compl *compl)
 {
+       struct be_async_event_grp5_cos_priority *evt =
+                       (struct be_async_event_grp5_cos_priority *)compl;
+
        if (evt->valid) {
                adapter->vlan_prio_bmap = evt->available_priority_bmap;
                adapter->recommended_prio &= ~VLAN_PRIO_MASK;
@@ -233,8 +267,11 @@ static void be_async_grp5_cos_priority_process(struct be_adapter *adapter,
 
 /* Grp5 QOS Speed evt: qos_link_speed is in units of 10 Mbps */
 static void be_async_grp5_qos_speed_process(struct be_adapter *adapter,
-               struct be_async_event_grp5_qos_link_speed *evt)
+                                           struct be_mcc_compl *compl)
 {
+       struct be_async_event_grp5_qos_link_speed *evt =
+                       (struct be_async_event_grp5_qos_link_speed *)compl;
+
        if (adapter->phy.link_speed >= 0 &&
            evt->physical_port == adapter->port_num)
                adapter->phy.link_speed = le16_to_cpu(evt->qos_link_speed) * 10;
@@ -242,8 +279,11 @@ static void be_async_grp5_qos_speed_process(struct be_adapter *adapter,
 
 /*Grp5 PVID evt*/
 static void be_async_grp5_pvid_state_process(struct be_adapter *adapter,
-               struct be_async_event_grp5_pvid_state *evt)
+                                            struct be_mcc_compl *compl)
 {
+       struct be_async_event_grp5_pvid_state *evt =
+                       (struct be_async_event_grp5_pvid_state *)compl;
+
        if (evt->enabled) {
                adapter->pvid = le16_to_cpu(evt->tag) & VLAN_VID_MASK;
                dev_info(&adapter->pdev->dev, "LPVID: %d\n", adapter->pvid);
@@ -253,26 +293,21 @@ static void be_async_grp5_pvid_state_process(struct be_adapter *adapter,
 }
 
 static void be_async_grp5_evt_process(struct be_adapter *adapter,
-               u32 trailer, struct be_mcc_compl *evt)
+                                     struct be_mcc_compl *compl)
 {
-       u8 event_type = 0;
-
-       event_type = (trailer >> ASYNC_TRAILER_EVENT_TYPE_SHIFT) &
-               ASYNC_TRAILER_EVENT_TYPE_MASK;
+       u8 event_type = (compl->flags >> ASYNC_EVENT_TYPE_SHIFT) &
+                               ASYNC_EVENT_TYPE_MASK;
 
        switch (event_type) {
        case ASYNC_EVENT_COS_PRIORITY:
-               be_async_grp5_cos_priority_process(adapter,
-               (struct be_async_event_grp5_cos_priority *)evt);
-       break;
+               be_async_grp5_cos_priority_process(adapter, compl);
+               break;
        case ASYNC_EVENT_QOS_SPEED:
-               be_async_grp5_qos_speed_process(adapter,
-               (struct be_async_event_grp5_qos_link_speed *)evt);
-       break;
+               be_async_grp5_qos_speed_process(adapter, compl);
+               break;
        case ASYNC_EVENT_PVID_STATE:
-               be_async_grp5_pvid_state_process(adapter,
-               (struct be_async_event_grp5_pvid_state *)evt);
-       break;
+               be_async_grp5_pvid_state_process(adapter, compl);
+               break;
        default:
                dev_warn(&adapter->pdev->dev, "Unknown grp5 event 0x%x!\n",
                         event_type);
@@ -281,13 +316,13 @@ static void be_async_grp5_evt_process(struct be_adapter *adapter,
 }
 
 static void be_async_dbg_evt_process(struct be_adapter *adapter,
-               u32 trailer, struct be_mcc_compl *cmp)
+                                    struct be_mcc_compl *cmp)
 {
        u8 event_type = 0;
        struct be_async_event_qnq *evt = (struct be_async_event_qnq *) cmp;
 
-       event_type = (trailer >> ASYNC_TRAILER_EVENT_TYPE_SHIFT) &
-               ASYNC_TRAILER_EVENT_TYPE_MASK;
+       event_type = (cmp->flags >> ASYNC_EVENT_TYPE_SHIFT) &
+                       ASYNC_EVENT_TYPE_MASK;
 
        switch (event_type) {
        case ASYNC_DEBUG_EVENT_TYPE_QNQ:
@@ -302,25 +337,33 @@ static void be_async_dbg_evt_process(struct be_adapter *adapter,
        }
 }
 
-static inline bool is_link_state_evt(u32 trailer)
+static inline bool is_link_state_evt(u32 flags)
 {
-       return ((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) &
-               ASYNC_TRAILER_EVENT_CODE_MASK) ==
-                               ASYNC_EVENT_CODE_LINK_STATE;
+       return ((flags >> ASYNC_EVENT_CODE_SHIFT) & ASYNC_EVENT_CODE_MASK) ==
+                       ASYNC_EVENT_CODE_LINK_STATE;
 }
 
-static inline bool is_grp5_evt(u32 trailer)
+static inline bool is_grp5_evt(u32 flags)
 {
-       return (((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) &
-               ASYNC_TRAILER_EVENT_CODE_MASK) ==
-                               ASYNC_EVENT_CODE_GRP_5);
+       return ((flags >> ASYNC_EVENT_CODE_SHIFT) & ASYNC_EVENT_CODE_MASK) ==
+                       ASYNC_EVENT_CODE_GRP_5;
 }
 
-static inline bool is_dbg_evt(u32 trailer)
+static inline bool is_dbg_evt(u32 flags)
 {
-       return (((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) &
-               ASYNC_TRAILER_EVENT_CODE_MASK) ==
-                               ASYNC_EVENT_CODE_QNQ);
+       return ((flags >> ASYNC_EVENT_CODE_SHIFT) & ASYNC_EVENT_CODE_MASK) ==
+                       ASYNC_EVENT_CODE_QNQ;
+}
+
+static void be_mcc_event_process(struct be_adapter *adapter,
+                                struct be_mcc_compl *compl)
+{
+       if (is_link_state_evt(compl->flags))
+               be_async_link_state_process(adapter, compl);
+       else if (is_grp5_evt(compl->flags))
+               be_async_grp5_evt_process(adapter, compl);
+       else if (is_dbg_evt(compl->flags))
+               be_async_dbg_evt_process(adapter, compl);
 }
 
 static struct be_mcc_compl *be_mcc_compl_get(struct be_adapter *adapter)
@@ -362,21 +405,13 @@ int be_process_mcc(struct be_adapter *adapter)
        struct be_mcc_obj *mcc_obj = &adapter->mcc_obj;
 
        spin_lock(&adapter->mcc_cq_lock);
+
        while ((compl = be_mcc_compl_get(adapter))) {
                if (compl->flags & CQE_FLAGS_ASYNC_MASK) {
-                       /* Interpret flags as an async trailer */
-                       if (is_link_state_evt(compl->flags))
-                               be_async_link_state_process(adapter,
-                               (struct be_async_event_link_state *) compl);
-                       else if (is_grp5_evt(compl->flags))
-                               be_async_grp5_evt_process(adapter,
-                               compl->flags, compl);
-                       else if (is_dbg_evt(compl->flags))
-                               be_async_dbg_evt_process(adapter,
-                               compl->flags, compl);
+                       be_mcc_event_process(adapter, compl);
                } else if (compl->flags & CQE_FLAGS_COMPLETED_MASK) {
-                               status = be_mcc_compl_process(adapter, compl);
-                               atomic_dec(&mcc_obj->q.used);
+                       status = be_mcc_compl_process(adapter, compl);
+                       atomic_dec(&mcc_obj->q.used);
                }
                be_mcc_compl_use(compl);
                num++;
@@ -436,7 +471,9 @@ static int be_mcc_notify_wait(struct be_adapter *adapter)
        if (status == -EIO)
                goto out;
 
-       status = resp->status;
+       status = (resp->base_status |
+                 ((resp->addl_status & CQE_ADDL_STATUS_MASK) <<
+                  CQE_ADDL_STATUS_SHIFT));
 out:
        return status;
 }
@@ -560,10 +597,8 @@ static bool lancer_provisioning_error(struct be_adapter *adapter)
        u32 sliport_status = 0, sliport_err1 = 0, sliport_err2 = 0;
        sliport_status = ioread32(adapter->db + SLIPORT_STATUS_OFFSET);
        if (sliport_status & SLIPORT_STATUS_ERR_MASK) {
-               sliport_err1 = ioread32(adapter->db +
-                                       SLIPORT_ERROR1_OFFSET);
-               sliport_err2 = ioread32(adapter->db +
-                                       SLIPORT_ERROR2_OFFSET);
+               sliport_err1 = ioread32(adapter->db + SLIPORT_ERROR1_OFFSET);
+               sliport_err2 = ioread32(adapter->db + SLIPORT_ERROR2_OFFSET);
 
                if (sliport_err1 == SLIPORT_ERROR_NO_RESOURCE1 &&
                    sliport_err2 == SLIPORT_ERROR_NO_RESOURCE2)
@@ -630,8 +665,7 @@ int be_fw_wait_ready(struct be_adapter *adapter)
                if (stage == POST_STAGE_ARMFW_RDY)
                        return 0;
 
-               dev_info(dev, "Waiting for POST, %ds elapsed\n",
-                        timeout);
+               dev_info(dev, "Waiting for POST, %ds elapsed\n", timeout);
                if (msleep_interruptible(2000)) {
                        dev_err(dev, "Waiting for POST aborted\n");
                        return -EINTR;
@@ -649,8 +683,7 @@ static inline struct be_sge *nonembedded_sgl(struct be_mcc_wrb *wrb)
        return &wrb->payload.sgl[0];
 }
 
-static inline void fill_wrb_tags(struct be_mcc_wrb *wrb,
-                                unsigned long addr)
+static inline void fill_wrb_tags(struct be_mcc_wrb *wrb, unsigned long addr)
 {
        wrb->tag0 = addr & 0xFFFFFFFF;
        wrb->tag1 = upper_32_bits(addr);
@@ -659,8 +692,9 @@ static inline void fill_wrb_tags(struct be_mcc_wrb *wrb,
 /* Don't touch the hdr after it's prepared */
 /* mem will be NULL for embedded commands */
 static void be_wrb_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr,
-                               u8 subsystem, u8 opcode, int cmd_len,
-                               struct be_mcc_wrb *wrb, struct be_dma_mem *mem)
+                                  u8 subsystem, u8 opcode, int cmd_len,
+                                  struct be_mcc_wrb *wrb,
+                                  struct be_dma_mem *mem)
 {
        struct be_sge *sge;
 
@@ -683,7 +717,7 @@ static void be_wrb_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr,
 }
 
 static void be_cmd_page_addrs_prepare(struct phys_addr *pages, u32 max_pages,
-                       struct be_dma_mem *mem)
+                                     struct be_dma_mem *mem)
 {
        int i, buf_pages = min(PAGES_4K_SPANNED(mem->va, mem->size), max_pages);
        u64 dma = (u64)mem->dma;
@@ -868,7 +902,8 @@ int be_cmd_eq_create(struct be_adapter *adapter, struct be_eq_obj *eqo)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_EQ_CREATE, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_EQ_CREATE, sizeof(*req), wrb,
+                              NULL);
 
        /* Support for EQ_CREATEv2 available only SH-R onwards */
        if (!(BEx_chip(adapter) || lancer_chip(adapter)))
@@ -917,7 +952,8 @@ int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_NTWK_MAC_QUERY, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_NTWK_MAC_QUERY, sizeof(*req), wrb,
+                              NULL);
        req->type = MAC_ADDRESS_TYPE_NETWORK;
        if (permanent) {
                req->permanent = 1;
@@ -940,7 +976,7 @@ err:
 
 /* Uses synchronous MCCQ */
 int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr,
-               u32 if_id, u32 *pmac_id, u32 domain)
+                   u32 if_id, u32 *pmac_id, u32 domain)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_pmac_add *req;
@@ -956,7 +992,8 @@ int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_NTWK_PMAC_ADD, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_NTWK_PMAC_ADD, sizeof(*req), wrb,
+                              NULL);
 
        req->hdr.domain = domain;
        req->if_id = cpu_to_le32(if_id);
@@ -1012,7 +1049,7 @@ err:
 
 /* Uses Mbox */
 int be_cmd_cq_create(struct be_adapter *adapter, struct be_queue_info *cq,
-               struct be_queue_info *eq, bool no_delay, int coalesce_wm)
+                    struct be_queue_info *eq, bool no_delay, int coalesce_wm)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_cq_create *req;
@@ -1028,17 +1065,18 @@ int be_cmd_cq_create(struct be_adapter *adapter, struct be_queue_info *cq,
        ctxt = &req->context;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_CQ_CREATE, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_CQ_CREATE, sizeof(*req), wrb,
+                              NULL);
 
        req->num_pages =  cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size));
 
        if (BEx_chip(adapter)) {
                AMAP_SET_BITS(struct amap_cq_context_be, coalescwm, ctxt,
-                                                               coalesce_wm);
+                             coalesce_wm);
                AMAP_SET_BITS(struct amap_cq_context_be, nodelay,
-                                                               ctxt, no_delay);
+                             ctxt, no_delay);
                AMAP_SET_BITS(struct amap_cq_context_be, count, ctxt,
-                                               __ilog2_u32(cq->len/256));
+                             __ilog2_u32(cq->len / 256));
                AMAP_SET_BITS(struct amap_cq_context_be, valid, ctxt, 1);
                AMAP_SET_BITS(struct amap_cq_context_be, eventable, ctxt, 1);
                AMAP_SET_BITS(struct amap_cq_context_be, eqid, ctxt, eq->id);
@@ -1053,14 +1091,12 @@ int be_cmd_cq_create(struct be_adapter *adapter, struct be_queue_info *cq,
                        AMAP_SET_BITS(struct amap_cq_context_v2, coalescwm,
                                      ctxt, coalesce_wm);
                AMAP_SET_BITS(struct amap_cq_context_v2, nodelay, ctxt,
-                                                               no_delay);
+                             no_delay);
                AMAP_SET_BITS(struct amap_cq_context_v2, count, ctxt,
-                                               __ilog2_u32(cq->len/256));
+                             __ilog2_u32(cq->len / 256));
                AMAP_SET_BITS(struct amap_cq_context_v2, valid, ctxt, 1);
-               AMAP_SET_BITS(struct amap_cq_context_v2, eventable,
-                                                               ctxt, 1);
-               AMAP_SET_BITS(struct amap_cq_context_v2, eqid,
-                                                               ctxt, eq->id);
+               AMAP_SET_BITS(struct amap_cq_context_v2, eventable, ctxt, 1);
+               AMAP_SET_BITS(struct amap_cq_context_v2, eqid, ctxt, eq->id);
        }
 
        be_dws_cpu_to_le(ctxt, sizeof(req->context));
@@ -1088,8 +1124,8 @@ static u32 be_encoded_q_len(int q_len)
 }
 
 static int be_cmd_mccq_ext_create(struct be_adapter *adapter,
-                               struct be_queue_info *mccq,
-                               struct be_queue_info *cq)
+                                 struct be_queue_info *mccq,
+                                 struct be_queue_info *cq)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_mcc_ext_create *req;
@@ -1105,13 +1141,14 @@ static int be_cmd_mccq_ext_create(struct be_adapter *adapter,
        ctxt = &req->context;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                       OPCODE_COMMON_MCC_CREATE_EXT, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_MCC_CREATE_EXT, sizeof(*req), wrb,
+                              NULL);
 
        req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size));
        if (BEx_chip(adapter)) {
                AMAP_SET_BITS(struct amap_mcc_context_be, valid, ctxt, 1);
                AMAP_SET_BITS(struct amap_mcc_context_be, ring_size, ctxt,
-                                               be_encoded_q_len(mccq->len));
+                             be_encoded_q_len(mccq->len));
                AMAP_SET_BITS(struct amap_mcc_context_be, cq_id, ctxt, cq->id);
        } else {
                req->hdr.version = 1;
@@ -1145,8 +1182,8 @@ static int be_cmd_mccq_ext_create(struct be_adapter *adapter,
 }
 
 static int be_cmd_mccq_org_create(struct be_adapter *adapter,
-                               struct be_queue_info *mccq,
-                               struct be_queue_info *cq)
+                                 struct be_queue_info *mccq,
+                                 struct be_queue_info *cq)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_mcc_create *req;
@@ -1162,13 +1199,14 @@ static int be_cmd_mccq_org_create(struct be_adapter *adapter,
        ctxt = &req->context;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                       OPCODE_COMMON_MCC_CREATE, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_MCC_CREATE, sizeof(*req), wrb,
+                              NULL);
 
        req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size));
 
        AMAP_SET_BITS(struct amap_mcc_context_be, valid, ctxt, 1);
        AMAP_SET_BITS(struct amap_mcc_context_be, ring_size, ctxt,
-                       be_encoded_q_len(mccq->len));
+                     be_encoded_q_len(mccq->len));
        AMAP_SET_BITS(struct amap_mcc_context_be, cq_id, ctxt, cq->id);
 
        be_dws_cpu_to_le(ctxt, sizeof(req->context));
@@ -1187,8 +1225,7 @@ static int be_cmd_mccq_org_create(struct be_adapter *adapter,
 }
 
 int be_cmd_mccq_create(struct be_adapter *adapter,
-                       struct be_queue_info *mccq,
-                       struct be_queue_info *cq)
+                      struct be_queue_info *mccq, struct be_queue_info *cq)
 {
        int status;
 
@@ -1213,7 +1250,7 @@ int be_cmd_txq_create(struct be_adapter *adapter, struct be_tx_obj *txo)
 
        req = embedded_payload(&wrb);
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
-                               OPCODE_ETH_TX_CREATE, sizeof(*req), &wrb, NULL);
+                              OPCODE_ETH_TX_CREATE, sizeof(*req), &wrb, NULL);
 
        if (lancer_chip(adapter)) {
                req->hdr.version = 1;
@@ -1250,8 +1287,8 @@ int be_cmd_txq_create(struct be_adapter *adapter, struct be_tx_obj *txo)
 
 /* Uses MCC */
 int be_cmd_rxq_create(struct be_adapter *adapter,
-               struct be_queue_info *rxq, u16 cq_id, u16 frag_size,
-               u32 if_id, u32 rss, u8 *rss_id)
+                     struct be_queue_info *rxq, u16 cq_id, u16 frag_size,
+                     u32 if_id, u32 rss, u8 *rss_id)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_eth_rx_create *req;
@@ -1268,7 +1305,7 @@ int be_cmd_rxq_create(struct be_adapter *adapter,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
-                               OPCODE_ETH_RX_CREATE, sizeof(*req), wrb, NULL);
+                              OPCODE_ETH_RX_CREATE, sizeof(*req), wrb, NULL);
 
        req->cq_id = cpu_to_le16(cq_id);
        req->frag_size = fls(frag_size) - 1;
@@ -1295,7 +1332,7 @@ err:
  * Uses Mbox
  */
 int be_cmd_q_destroy(struct be_adapter *adapter, struct be_queue_info *q,
-               int queue_type)
+                    int queue_type)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_q_destroy *req;
@@ -1334,7 +1371,7 @@ int be_cmd_q_destroy(struct be_adapter *adapter, struct be_queue_info *q,
        }
 
        be_wrb_cmd_hdr_prepare(&req->hdr, subsys, opcode, sizeof(*req), wrb,
-                               NULL);
+                              NULL);
        req->id = cpu_to_le16(q->id);
 
        status = be_mbox_notify_wait(adapter);
@@ -1361,7 +1398,7 @@ int be_cmd_rxq_destroy(struct be_adapter *adapter, struct be_queue_info *q)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
-                       OPCODE_ETH_RX_DESTROY, sizeof(*req), wrb, NULL);
+                              OPCODE_ETH_RX_DESTROY, sizeof(*req), wrb, NULL);
        req->id = cpu_to_le16(q->id);
 
        status = be_mcc_notify_wait(adapter);
@@ -1384,7 +1421,8 @@ int be_cmd_if_create(struct be_adapter *adapter, u32 cap_flags, u32 en_flags,
 
        req = embedded_payload(&wrb);
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_NTWK_INTERFACE_CREATE, sizeof(*req), &wrb, NULL);
+                              OPCODE_COMMON_NTWK_INTERFACE_CREATE,
+                              sizeof(*req), &wrb, NULL);
        req->hdr.domain = domain;
        req->capability_flags = cpu_to_le32(cap_flags);
        req->enable_flags = cpu_to_le32(en_flags);
@@ -1422,7 +1460,8 @@ int be_cmd_if_destroy(struct be_adapter *adapter, int interface_id, u32 domain)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_NTWK_INTERFACE_DESTROY, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_NTWK_INTERFACE_DESTROY,
+                              sizeof(*req), wrb, NULL);
        req->hdr.domain = domain;
        req->interface_id = cpu_to_le32(interface_id);
 
@@ -1452,7 +1491,8 @@ int be_cmd_get_stats(struct be_adapter *adapter, struct be_dma_mem *nonemb_cmd)
        hdr = nonemb_cmd->va;
 
        be_wrb_cmd_hdr_prepare(hdr, CMD_SUBSYSTEM_ETH,
-               OPCODE_ETH_GET_STATISTICS, nonemb_cmd->size, wrb, nonemb_cmd);
+                              OPCODE_ETH_GET_STATISTICS, nonemb_cmd->size, wrb,
+                              nonemb_cmd);
 
        /* version 1 of the cmd is not supported only by BE2 */
        if (BE2_chip(adapter))
@@ -1472,7 +1512,7 @@ err:
 
 /* Lancer Stats */
 int lancer_cmd_get_pport_stats(struct be_adapter *adapter,
-                               struct be_dma_mem *nonemb_cmd)
+                              struct be_dma_mem *nonemb_cmd)
 {
 
        struct be_mcc_wrb *wrb;
@@ -1493,8 +1533,8 @@ int lancer_cmd_get_pport_stats(struct be_adapter *adapter,
        req = nonemb_cmd->va;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
-                       OPCODE_ETH_GET_PPORT_STATS, nonemb_cmd->size, wrb,
-                       nonemb_cmd);
+                              OPCODE_ETH_GET_PPORT_STATS, nonemb_cmd->size,
+                              wrb, nonemb_cmd);
 
        req->cmd_params.params.pport_num = cpu_to_le16(adapter->hba_port_num);
        req->cmd_params.params.reset_stats = 0;
@@ -1553,7 +1593,8 @@ int be_cmd_link_status_query(struct be_adapter *adapter, u16 *link_speed,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_NTWK_LINK_STATUS_QUERY, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_NTWK_LINK_STATUS_QUERY,
+                              sizeof(*req), wrb, NULL);
 
        /* version 1 of the cmd is not supported only by BE2 */
        if (!BE2_chip(adapter))
@@ -1598,8 +1639,8 @@ int be_cmd_get_die_temperature(struct be_adapter *adapter)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES, sizeof(*req),
-               wrb, NULL);
+                              OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES,
+                              sizeof(*req), wrb, NULL);
 
        be_mcc_notify(adapter);
 
@@ -1625,7 +1666,8 @@ int be_cmd_get_reg_len(struct be_adapter *adapter, u32 *log_size)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_MANAGE_FAT, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_MANAGE_FAT, sizeof(*req), wrb,
+                              NULL);
        req->fat_operation = cpu_to_le32(QUERY_FAT);
        status = be_mcc_notify_wait(adapter);
        if (!status) {
@@ -1655,8 +1697,8 @@ void be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
 
        get_fat_cmd.size = sizeof(struct be_cmd_req_get_fat) + 60*1024;
        get_fat_cmd.va = pci_alloc_consistent(adapter->pdev,
-                       get_fat_cmd.size,
-                       &get_fat_cmd.dma);
+                                             get_fat_cmd.size,
+                                             &get_fat_cmd.dma);
        if (!get_fat_cmd.va) {
                status = -ENOMEM;
                dev_err(&adapter->pdev->dev,
@@ -1679,8 +1721,8 @@ void be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
 
                payload_len = sizeof(struct be_cmd_req_get_fat) + buf_size;
                be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                               OPCODE_COMMON_MANAGE_FAT, payload_len, wrb,
-                               &get_fat_cmd);
+                                      OPCODE_COMMON_MANAGE_FAT, payload_len,
+                                      wrb, &get_fat_cmd);
 
                req->fat_operation = cpu_to_le32(RETRIEVE_FAT);
                req->read_log_offset = cpu_to_le32(log_offset);
@@ -1691,8 +1733,8 @@ void be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
                if (!status) {
                        struct be_cmd_resp_get_fat *resp = get_fat_cmd.va;
                        memcpy(buf + offset,
-                               resp->data_buffer,
-                               le32_to_cpu(resp->read_log_length));
+                              resp->data_buffer,
+                              le32_to_cpu(resp->read_log_length));
                } else {
                        dev_err(&adapter->pdev->dev, "FAT Table Retrieve error\n");
                        goto err;
@@ -1702,14 +1744,13 @@ void be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
        }
 err:
        pci_free_consistent(adapter->pdev, get_fat_cmd.size,
-                       get_fat_cmd.va,
-                       get_fat_cmd.dma);
+                           get_fat_cmd.va, get_fat_cmd.dma);
        spin_unlock_bh(&adapter->mcc_lock);
 }
 
 /* Uses synchronous mcc */
 int be_cmd_get_fw_ver(struct be_adapter *adapter, char *fw_ver,
-                       char *fw_on_flash)
+                     char *fw_on_flash)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_get_fw_version *req;
@@ -1726,7 +1767,8 @@ int be_cmd_get_fw_ver(struct be_adapter *adapter, char *fw_ver,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_GET_FW_VERSION, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_GET_FW_VERSION, sizeof(*req), wrb,
+                              NULL);
        status = be_mcc_notify_wait(adapter);
        if (!status) {
                struct be_cmd_resp_get_fw_version *resp = embedded_payload(wrb);
@@ -1759,7 +1801,8 @@ int be_cmd_modify_eqd(struct be_adapter *adapter, struct be_set_eqd *set_eqd,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_MODIFY_EQ_DELAY, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_MODIFY_EQ_DELAY, sizeof(*req), wrb,
+                              NULL);
 
        req->num_eq = cpu_to_le32(num);
        for (i = 0; i < num; i++) {
@@ -1777,7 +1820,7 @@ err:
 
 /* Uses sycnhronous mcc */
 int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array,
-                      u32 num, bool promiscuous)
+                      u32 num)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_vlan_config *req;
@@ -1793,19 +1836,16 @@ int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_NTWK_VLAN_CONFIG, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_NTWK_VLAN_CONFIG, sizeof(*req),
+                              wrb, NULL);
 
        req->interface_id = if_id;
-       req->promiscuous = promiscuous;
        req->untagged = BE_IF_FLAGS_UNTAGGED & be_if_cap_flags(adapter) ? 1 : 0;
        req->num_vlan = num;
-       if (!promiscuous) {
-               memcpy(req->normal_vlan, vtag_array,
-                       req->num_vlan * sizeof(vtag_array[0]));
-       }
+       memcpy(req->normal_vlan, vtag_array,
+              req->num_vlan * sizeof(vtag_array[0]));
 
        status = be_mcc_notify_wait(adapter);
-
 err:
        spin_unlock_bh(&adapter->mcc_lock);
        return status;
@@ -1827,18 +1867,19 @@ int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value)
        }
        memset(req, 0, sizeof(*req));
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                               OPCODE_COMMON_NTWK_RX_FILTER, sizeof(*req),
-                               wrb, mem);
+                              OPCODE_COMMON_NTWK_RX_FILTER, sizeof(*req),
+                              wrb, mem);
 
        req->if_id = cpu_to_le32(adapter->if_handle);
        if (flags & IFF_PROMISC) {
                req->if_flags_mask = cpu_to_le32(BE_IF_FLAGS_PROMISCUOUS |
-                                       BE_IF_FLAGS_VLAN_PROMISCUOUS |
-                                       BE_IF_FLAGS_MCAST_PROMISCUOUS);
+                                                BE_IF_FLAGS_VLAN_PROMISCUOUS |
+                                                BE_IF_FLAGS_MCAST_PROMISCUOUS);
                if (value == ON)
-                       req->if_flags = cpu_to_le32(BE_IF_FLAGS_PROMISCUOUS |
-                                               BE_IF_FLAGS_VLAN_PROMISCUOUS |
-                                               BE_IF_FLAGS_MCAST_PROMISCUOUS);
+                       req->if_flags =
+                               cpu_to_le32(BE_IF_FLAGS_PROMISCUOUS |
+                                           BE_IF_FLAGS_VLAN_PROMISCUOUS |
+                                           BE_IF_FLAGS_MCAST_PROMISCUOUS);
        } else if (flags & IFF_ALLMULTI) {
                req->if_flags_mask = req->if_flags =
                                cpu_to_le32(BE_IF_FLAGS_MCAST_PROMISCUOUS);
@@ -1867,7 +1908,7 @@ int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value)
        }
 
        if ((req->if_flags_mask & cpu_to_le32(be_if_cap_flags(adapter))) !=
-            req->if_flags_mask) {
+           req->if_flags_mask) {
                dev_warn(&adapter->pdev->dev,
                         "Cannot set rx filter flags 0x%x\n",
                         req->if_flags_mask);
@@ -1905,7 +1946,8 @@ int be_cmd_set_flow_control(struct be_adapter *adapter, u32 tx_fc, u32 rx_fc)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_SET_FLOW_CONTROL, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_SET_FLOW_CONTROL, sizeof(*req),
+                              wrb, NULL);
 
        req->tx_flow_control = cpu_to_le16((u16)tx_fc);
        req->rx_flow_control = cpu_to_le16((u16)rx_fc);
@@ -1938,7 +1980,8 @@ int be_cmd_get_flow_control(struct be_adapter *adapter, u32 *tx_fc, u32 *rx_fc)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_GET_FLOW_CONTROL, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_GET_FLOW_CONTROL, sizeof(*req),
+                              wrb, NULL);
 
        status = be_mcc_notify_wait(adapter);
        if (!status) {
@@ -1968,7 +2011,8 @@ int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_QUERY_FIRMWARE_CONFIG,
+                              sizeof(*req), wrb, NULL);
 
        status = be_mbox_notify_wait(adapter);
        if (!status) {
@@ -2011,7 +2055,8 @@ int be_cmd_reset_function(struct be_adapter *adapter)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(req, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_FUNCTION_RESET, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_FUNCTION_RESET, sizeof(*req), wrb,
+                              NULL);
 
        status = be_mbox_notify_wait(adapter);
 
@@ -2020,47 +2065,47 @@ int be_cmd_reset_function(struct be_adapter *adapter)
 }
 
 int be_cmd_rss_config(struct be_adapter *adapter, u8 *rsstable,
-                       u32 rss_hash_opts, u16 table_size)
+                     u32 rss_hash_opts, u16 table_size, const u8 *rss_hkey)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_rss_config *req;
-       u32 myhash[10] = {0x15d43fa5, 0x2534685a, 0x5f87693a, 0x5668494e,
-                       0x33cf6a53, 0x383334c6, 0x76ac4257, 0x59b242b2,
-                       0x3ea83c02, 0x4a110304};
        int status;
 
        if (!(be_if_cap_flags(adapter) & BE_IF_FLAGS_RSS))
                return 0;
 
-       if (mutex_lock_interruptible(&adapter->mbox_lock))
-               return -1;
+       spin_lock_bh(&adapter->mcc_lock);
 
-       wrb = wrb_from_mbox(adapter);
+       wrb = wrb_from_mccq(adapter);
+       if (!wrb) {
+               status = -EBUSY;
+               goto err;
+       }
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
-               OPCODE_ETH_RSS_CONFIG, sizeof(*req), wrb, NULL);
+                              OPCODE_ETH_RSS_CONFIG, sizeof(*req), wrb, NULL);
 
        req->if_id = cpu_to_le32(adapter->if_handle);
        req->enable_rss = cpu_to_le16(rss_hash_opts);
        req->cpu_table_size_log2 = cpu_to_le16(fls(table_size) - 1);
 
-       if (lancer_chip(adapter) || skyhawk_chip(adapter))
+       if (!BEx_chip(adapter))
                req->hdr.version = 1;
 
        memcpy(req->cpu_table, rsstable, table_size);
-       memcpy(req->hash, myhash, sizeof(myhash));
+       memcpy(req->hash, rss_hkey, RSS_HASH_KEY_LEN);
        be_dws_cpu_to_le(req->hash, sizeof(req->hash));
 
-       status = be_mbox_notify_wait(adapter);
-
-       mutex_unlock(&adapter->mbox_lock);
+       status = be_mcc_notify_wait(adapter);
+err:
+       spin_unlock_bh(&adapter->mcc_lock);
        return status;
 }
 
 /* Uses sync mcc */
 int be_cmd_set_beacon_state(struct be_adapter *adapter, u8 port_num,
-                       u8 bcn, u8 sts, u8 state)
+                           u8 bcn, u8 sts, u8 state)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_enable_disable_beacon *req;
@@ -2076,7 +2121,8 @@ int be_cmd_set_beacon_state(struct be_adapter *adapter, u8 port_num,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_ENABLE_DISABLE_BEACON, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_ENABLE_DISABLE_BEACON,
+                              sizeof(*req), wrb, NULL);
 
        req->port_num = port_num;
        req->beacon_state = state;
@@ -2107,7 +2153,8 @@ int be_cmd_get_beacon_state(struct be_adapter *adapter, u8 port_num, u32 *state)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_GET_BEACON_STATE, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_GET_BEACON_STATE, sizeof(*req),
+                              wrb, NULL);
 
        req->port_num = port_num;
 
@@ -2146,20 +2193,20 @@ int lancer_cmd_write_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                               OPCODE_COMMON_WRITE_OBJECT,
-                               sizeof(struct lancer_cmd_req_write_object), wrb,
-                               NULL);
+                              OPCODE_COMMON_WRITE_OBJECT,
+                              sizeof(struct lancer_cmd_req_write_object), wrb,
+                              NULL);
 
        ctxt = &req->context;
        AMAP_SET_BITS(struct amap_lancer_write_obj_context,
-                       write_length, ctxt, data_size);
+                     write_length, ctxt, data_size);
 
        if (data_size == 0)
                AMAP_SET_BITS(struct amap_lancer_write_obj_context,
-                               eof, ctxt, 1);
+                             eof, ctxt, 1);
        else
                AMAP_SET_BITS(struct amap_lancer_write_obj_context,
-                               eof, ctxt, 0);
+                             eof, ctxt, 0);
 
        be_dws_cpu_to_le(ctxt, sizeof(req->context));
        req->write_offset = cpu_to_le32(data_offset);
@@ -2167,8 +2214,8 @@ int lancer_cmd_write_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
        req->descriptor_count = cpu_to_le32(1);
        req->buf_len = cpu_to_le32(data_size);
        req->addr_low = cpu_to_le32((cmd->dma +
-                               sizeof(struct lancer_cmd_req_write_object))
-                               & 0xFFFFFFFF);
+                                    sizeof(struct lancer_cmd_req_write_object))
+                                   & 0xFFFFFFFF);
        req->addr_high = cpu_to_le32(upper_32_bits(cmd->dma +
                                sizeof(struct lancer_cmd_req_write_object)));
 
@@ -2197,8 +2244,8 @@ err_unlock:
 }
 
 int lancer_cmd_read_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
-               u32 data_size, u32 data_offset, const char *obj_name,
-               u32 *data_read, u32 *eof, u8 *addn_status)
+                          u32 data_size, u32 data_offset, const char *obj_name,
+                          u32 *data_read, u32 *eof, u8 *addn_status)
 {
        struct be_mcc_wrb *wrb;
        struct lancer_cmd_req_read_object *req;
@@ -2216,9 +2263,9 @@ int lancer_cmd_read_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                       OPCODE_COMMON_READ_OBJECT,
-                       sizeof(struct lancer_cmd_req_read_object), wrb,
-                       NULL);
+                              OPCODE_COMMON_READ_OBJECT,
+                              sizeof(struct lancer_cmd_req_read_object), wrb,
+                              NULL);
 
        req->desired_read_len = cpu_to_le32(data_size);
        req->read_offset = cpu_to_le32(data_offset);
@@ -2244,7 +2291,7 @@ err_unlock:
 }
 
 int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd,
-                       u32 flash_type, u32 flash_opcode, u32 buf_size)
+                         u32 flash_type, u32 flash_opcode, u32 buf_size)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_write_flashrom *req;
@@ -2261,7 +2308,8 @@ int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd,
        req = cmd->va;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_WRITE_FLASHROM, cmd->size, wrb, cmd);
+                              OPCODE_COMMON_WRITE_FLASHROM, cmd->size, wrb,
+                              cmd);
 
        req->params.op_type = cpu_to_le32(flash_type);
        req->params.op_code = cpu_to_le32(flash_opcode);
@@ -2284,7 +2332,7 @@ err_unlock:
 }
 
 int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc,
-                        int offset)
+                         u16 optype, int offset)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_read_flash_crc *req;
@@ -2303,7 +2351,7 @@ int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc,
                               OPCODE_COMMON_READ_FLASHROM, sizeof(*req),
                               wrb, NULL);
 
-       req->params.op_type = cpu_to_le32(OPTYPE_REDBOOT);
+       req->params.op_type = cpu_to_le32(optype);
        req->params.op_code = cpu_to_le32(FLASHROM_OPER_REPORT);
        req->params.offset = cpu_to_le32(offset);
        req->params.data_buf_size = cpu_to_le32(0x4);
@@ -2318,7 +2366,7 @@ err:
 }
 
 int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac,
-                               struct be_dma_mem *nonemb_cmd)
+                           struct be_dma_mem *nonemb_cmd)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_acpi_wol_magic_config *req;
@@ -2334,8 +2382,8 @@ int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac,
        req = nonemb_cmd->va;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
-               OPCODE_ETH_ACPI_WOL_MAGIC_CONFIG, sizeof(*req), wrb,
-               nonemb_cmd);
+                              OPCODE_ETH_ACPI_WOL_MAGIC_CONFIG, sizeof(*req),
+                              wrb, nonemb_cmd);
        memcpy(req->magic_mac, mac, ETH_ALEN);
 
        status = be_mcc_notify_wait(adapter);
@@ -2363,8 +2411,8 @@ int be_cmd_set_loopback(struct be_adapter *adapter, u8 port_num,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL,
-                       OPCODE_LOWLEVEL_SET_LOOPBACK_MODE, sizeof(*req), wrb,
-                       NULL);
+                              OPCODE_LOWLEVEL_SET_LOOPBACK_MODE, sizeof(*req),
+                              wrb, NULL);
 
        req->src_port = port_num;
        req->dest_port = port_num;
@@ -2378,7 +2426,8 @@ err:
 }
 
 int be_cmd_loopback_test(struct be_adapter *adapter, u32 port_num,
-               u32 loopback_type, u32 pkt_size, u32 num_pkts, u64 pattern)
+                        u32 loopback_type, u32 pkt_size, u32 num_pkts,
+                        u64 pattern)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_loopback_test *req;
@@ -2396,7 +2445,8 @@ int be_cmd_loopback_test(struct be_adapter *adapter, u32 port_num,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL,
-                       OPCODE_LOWLEVEL_LOOPBACK_TEST, sizeof(*req), wrb, NULL);
+                              OPCODE_LOWLEVEL_LOOPBACK_TEST, sizeof(*req), wrb,
+                              NULL);
 
        req->hdr.timeout = cpu_to_le32(15);
        req->pattern = cpu_to_le64(pattern);
@@ -2421,7 +2471,7 @@ err:
 }
 
 int be_cmd_ddr_dma_test(struct be_adapter *adapter, u64 pattern,
-                               u32 byte_cnt, struct be_dma_mem *cmd)
+                       u32 byte_cnt, struct be_dma_mem *cmd)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_ddrdma_test *req;
@@ -2437,7 +2487,8 @@ int be_cmd_ddr_dma_test(struct be_adapter *adapter, u64 pattern,
        }
        req = cmd->va;
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL,
-                       OPCODE_LOWLEVEL_HOST_DDR_DMA, cmd->size, wrb, cmd);
+                              OPCODE_LOWLEVEL_HOST_DDR_DMA, cmd->size, wrb,
+                              cmd);
 
        req->pattern = cpu_to_le64(pattern);
        req->byte_count = cpu_to_le32(byte_cnt);
@@ -2465,7 +2516,7 @@ err:
 }
 
 int be_cmd_get_seeprom_data(struct be_adapter *adapter,
-                               struct be_dma_mem *nonemb_cmd)
+                           struct be_dma_mem *nonemb_cmd)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_seeprom_read *req;
@@ -2481,8 +2532,8 @@ int be_cmd_get_seeprom_data(struct be_adapter *adapter,
        req = nonemb_cmd->va;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                       OPCODE_COMMON_SEEPROM_READ, sizeof(*req), wrb,
-                       nonemb_cmd);
+                              OPCODE_COMMON_SEEPROM_READ, sizeof(*req), wrb,
+                              nonemb_cmd);
 
        status = be_mcc_notify_wait(adapter);
 
@@ -2510,8 +2561,7 @@ int be_cmd_get_phy_info(struct be_adapter *adapter)
                goto err;
        }
        cmd.size = sizeof(struct be_cmd_req_get_phy_info);
-       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size,
-                                       &cmd.dma);
+       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma);
        if (!cmd.va) {
                dev_err(&adapter->pdev->dev, "Memory alloc failure\n");
                status = -ENOMEM;
@@ -2521,8 +2571,8 @@ int be_cmd_get_phy_info(struct be_adapter *adapter)
        req = cmd.va;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                       OPCODE_COMMON_GET_PHY_DETAILS, sizeof(*req),
-                       wrb, &cmd);
+                              OPCODE_COMMON_GET_PHY_DETAILS, sizeof(*req),
+                              wrb, &cmd);
 
        status = be_mcc_notify_wait(adapter);
        if (!status) {
@@ -2544,8 +2594,7 @@ int be_cmd_get_phy_info(struct be_adapter *adapter)
                                BE_SUPPORTED_SPEED_1GBPS;
                }
        }
-       pci_free_consistent(adapter->pdev, cmd.size,
-                               cmd.va, cmd.dma);
+       pci_free_consistent(adapter->pdev, cmd.size, cmd.va, cmd.dma);
 err:
        spin_unlock_bh(&adapter->mcc_lock);
        return status;
@@ -2568,7 +2617,7 @@ int be_cmd_set_qos(struct be_adapter *adapter, u32 bps, u32 domain)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                       OPCODE_COMMON_SET_QOS, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_SET_QOS, sizeof(*req), wrb, NULL);
 
        req->hdr.domain = domain;
        req->valid_bits = cpu_to_le32(BE_QOS_BITS_NIC);
@@ -2597,10 +2646,9 @@ int be_cmd_get_cntl_attributes(struct be_adapter *adapter)
        memset(&attribs_cmd, 0, sizeof(struct be_dma_mem));
        attribs_cmd.size = sizeof(struct be_cmd_resp_cntl_attribs);
        attribs_cmd.va = pci_alloc_consistent(adapter->pdev, attribs_cmd.size,
-                                               &attribs_cmd.dma);
+                                             &attribs_cmd.dma);
        if (!attribs_cmd.va) {
-               dev_err(&adapter->pdev->dev,
-                               "Memory allocation failure\n");
+               dev_err(&adapter->pdev->dev, "Memory allocation failure\n");
                status = -ENOMEM;
                goto err;
        }
@@ -2613,8 +2661,8 @@ int be_cmd_get_cntl_attributes(struct be_adapter *adapter)
        req = attribs_cmd.va;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                        OPCODE_COMMON_GET_CNTL_ATTRIBUTES, payload_len, wrb,
-                       &attribs_cmd);
+                              OPCODE_COMMON_GET_CNTL_ATTRIBUTES, payload_len,
+                              wrb, &attribs_cmd);
 
        status = be_mbox_notify_wait(adapter);
        if (!status) {
@@ -2649,7 +2697,8 @@ int be_cmd_req_native_mode(struct be_adapter *adapter)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_SET_DRIVER_FUNCTION_CAP, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_SET_DRIVER_FUNCTION_CAP,
+                              sizeof(*req), wrb, NULL);
 
        req->valid_cap_flags = cpu_to_le32(CAPABILITY_SW_TIMESTAMPS |
                                CAPABILITY_BE3_NATIVE_ERX_API);
@@ -2762,12 +2811,12 @@ int be_cmd_get_mac_from_list(struct be_adapter *adapter, u8 *mac,
        memset(&get_mac_list_cmd, 0, sizeof(struct be_dma_mem));
        get_mac_list_cmd.size = sizeof(struct be_cmd_resp_get_mac_list);
        get_mac_list_cmd.va = pci_alloc_consistent(adapter->pdev,
-                       get_mac_list_cmd.size,
-                       &get_mac_list_cmd.dma);
+                                                  get_mac_list_cmd.size,
+                                                  &get_mac_list_cmd.dma);
 
        if (!get_mac_list_cmd.va) {
                dev_err(&adapter->pdev->dev,
-                               "Memory allocation failure during GET_MAC_LIST\n");
+                       "Memory allocation failure during GET_MAC_LIST\n");
                return -ENOMEM;
        }
 
@@ -2831,18 +2880,18 @@ int be_cmd_get_mac_from_list(struct be_adapter *adapter, u8 *mac,
                /* If no active mac_id found, return first mac addr */
                *pmac_id_valid = false;
                memcpy(mac, resp->macaddr_list[0].mac_addr_id.macaddr,
-                                                               ETH_ALEN);
+                      ETH_ALEN);
        }
 
 out:
        spin_unlock_bh(&adapter->mcc_lock);
        pci_free_consistent(adapter->pdev, get_mac_list_cmd.size,
-                       get_mac_list_cmd.va, get_mac_list_cmd.dma);
+                           get_mac_list_cmd.va, get_mac_list_cmd.dma);
        return status;
 }
 
-int be_cmd_get_active_mac(struct be_adapter *adapter, u32 curr_pmac_id, u8 *mac,
-                         u32 if_handle, bool active, u32 domain)
+int be_cmd_get_active_mac(struct be_adapter *adapter, u32 curr_pmac_id,
+                         u8 *mac, u32 if_handle, bool active, u32 domain)
 {
 
        if (!active)
@@ -2892,7 +2941,7 @@ int be_cmd_set_mac_list(struct be_adapter *adapter, u8 *mac_array,
        memset(&cmd, 0, sizeof(struct be_dma_mem));
        cmd.size = sizeof(struct be_cmd_req_set_mac_list);
        cmd.va = dma_alloc_coherent(&adapter->pdev->dev, cmd.size,
-                       &cmd.dma, GFP_KERNEL);
+                                   &cmd.dma, GFP_KERNEL);
        if (!cmd.va)
                return -ENOMEM;
 
@@ -2906,8 +2955,8 @@ int be_cmd_set_mac_list(struct be_adapter *adapter, u8 *mac_array,
 
        req = cmd.va;
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                               OPCODE_COMMON_SET_MAC_LIST, sizeof(*req),
-                               wrb, &cmd);
+                              OPCODE_COMMON_SET_MAC_LIST, sizeof(*req),
+                              wrb, &cmd);
 
        req->hdr.domain = domain;
        req->mac_count = mac_count;
@@ -2917,8 +2966,7 @@ int be_cmd_set_mac_list(struct be_adapter *adapter, u8 *mac_array,
        status = be_mcc_notify_wait(adapter);
 
 err:
-       dma_free_coherent(&adapter->pdev->dev, cmd.size,
-                               cmd.va, cmd.dma);
+       dma_free_coherent(&adapter->pdev->dev, cmd.size, cmd.va, cmd.dma);
        spin_unlock_bh(&adapter->mcc_lock);
        return status;
 }
@@ -2963,7 +3011,8 @@ int be_cmd_set_hsw_config(struct be_adapter *adapter, u16 pvid,
        ctxt = &req->context;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                       OPCODE_COMMON_SET_HSW_CONFIG, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_SET_HSW_CONFIG, sizeof(*req), wrb,
+                              NULL);
 
        req->hdr.domain = domain;
        AMAP_SET_BITS(struct amap_set_hsw_context, interface_id, ctxt, intf_id);
@@ -3009,7 +3058,8 @@ int be_cmd_get_hsw_config(struct be_adapter *adapter, u16 *pvid,
        ctxt = &req->context;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                       OPCODE_COMMON_GET_HSW_CONFIG, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_GET_HSW_CONFIG, sizeof(*req), wrb,
+                              NULL);
 
        req->hdr.domain = domain;
        AMAP_SET_BITS(struct amap_get_hsw_req_context, interface_id,
@@ -3027,10 +3077,9 @@ int be_cmd_get_hsw_config(struct be_adapter *adapter, u16 *pvid,
        if (!status) {
                struct be_cmd_resp_get_hsw_config *resp =
                                                embedded_payload(wrb);
-               be_dws_le_to_cpu(&resp->context,
-                                               sizeof(resp->context));
+               be_dws_le_to_cpu(&resp->context, sizeof(resp->context));
                vid = AMAP_GET_BITS(struct amap_get_hsw_resp_context,
-                                                       pvid, &resp->context);
+                                   pvid, &resp->context);
                if (pvid)
                        *pvid = le16_to_cpu(vid);
                if (mode)
@@ -3062,11 +3111,9 @@ int be_cmd_get_acpi_wol_cap(struct be_adapter *adapter)
 
        memset(&cmd, 0, sizeof(struct be_dma_mem));
        cmd.size = sizeof(struct be_cmd_resp_acpi_wol_magic_config_v1);
-       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size,
-                                              &cmd.dma);
+       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma);
        if (!cmd.va) {
-               dev_err(&adapter->pdev->dev,
-                               "Memory allocation failure\n");
+               dev_err(&adapter->pdev->dev, "Memory allocation failure\n");
                status = -ENOMEM;
                goto err;
        }
@@ -3349,8 +3396,7 @@ int be_cmd_get_func_config(struct be_adapter *adapter, struct be_resources *res)
 
        memset(&cmd, 0, sizeof(struct be_dma_mem));
        cmd.size = sizeof(struct be_cmd_resp_get_func_config);
-       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size,
-                                     &cmd.dma);
+       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma);
        if (!cmd.va) {
                dev_err(&adapter->pdev->dev, "Memory alloc failure\n");
                status = -ENOMEM;
@@ -3396,7 +3442,7 @@ err:
 
 /* Uses mbox */
 static int be_cmd_get_profile_config_mbox(struct be_adapter *adapter,
-                                       u8 domain, struct be_dma_mem *cmd)
+                                         u8 domain, struct be_dma_mem *cmd)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_get_profile_config *req;
@@ -3424,7 +3470,7 @@ static int be_cmd_get_profile_config_mbox(struct be_adapter *adapter,
 
 /* Uses sync mcc */
 static int be_cmd_get_profile_config_mccq(struct be_adapter *adapter,
-                                       u8 domain, struct be_dma_mem *cmd)
+                                         u8 domain, struct be_dma_mem *cmd)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_get_profile_config *req;
@@ -3484,8 +3530,8 @@ int be_cmd_get_profile_config(struct be_adapter *adapter,
        resp = cmd.va;
        desc_count = le32_to_cpu(resp->desc_count);
 
-       pcie =  be_get_pcie_desc(adapter->pdev->devfn, resp->func_param,
-                                desc_count);
+       pcie = be_get_pcie_desc(adapter->pdev->devfn, resp->func_param,
+                               desc_count);
        if (pcie)
                res->max_vfs = le16_to_cpu(pcie->num_vfs);
 
@@ -3548,33 +3594,47 @@ void be_reset_nic_desc(struct be_nic_res_desc *nic)
        nic->cq_count = 0xFFFF;
        nic->toe_conn_count = 0xFFFF;
        nic->eq_count = 0xFFFF;
+       nic->iface_count = 0xFFFF;
        nic->link_param = 0xFF;
+       nic->channel_id_param = cpu_to_le16(0xF000);
        nic->acpi_params = 0xFF;
        nic->wol_param = 0x0F;
-       nic->bw_min = 0xFFFFFFFF;
+       nic->tunnel_iface_count = 0xFFFF;
+       nic->direct_tenant_iface_count = 0xFFFF;
        nic->bw_max = 0xFFFFFFFF;
 }
 
-int be_cmd_config_qos(struct be_adapter *adapter, u32 bps, u8 domain)
+int be_cmd_config_qos(struct be_adapter *adapter, u32 max_rate, u16 link_speed,
+                     u8 domain)
 {
-       if (lancer_chip(adapter)) {
-               struct be_nic_res_desc nic_desc;
+       struct be_nic_res_desc nic_desc;
+       u32 bw_percent;
+       u16 version = 0;
+
+       if (BE3_chip(adapter))
+               return be_cmd_set_qos(adapter, max_rate / 10, domain);
 
-               be_reset_nic_desc(&nic_desc);
+       be_reset_nic_desc(&nic_desc);
+       nic_desc.pf_num = adapter->pf_number;
+       nic_desc.vf_num = domain;
+       if (lancer_chip(adapter)) {
                nic_desc.hdr.desc_type = NIC_RESOURCE_DESC_TYPE_V0;
                nic_desc.hdr.desc_len = RESOURCE_DESC_SIZE_V0;
                nic_desc.flags = (1 << QUN_SHIFT) | (1 << IMM_SHIFT) |
                                        (1 << NOSV_SHIFT);
-               nic_desc.pf_num = adapter->pf_number;
-               nic_desc.vf_num = domain;
-               nic_desc.bw_max = cpu_to_le32(bps);
-
-               return be_cmd_set_profile_config(adapter, &nic_desc,
-                                                RESOURCE_DESC_SIZE_V0,
-                                                0, domain);
+               nic_desc.bw_max = cpu_to_le32(max_rate / 10);
        } else {
-               return be_cmd_set_qos(adapter, bps, domain);
+               version = 1;
+               nic_desc.hdr.desc_type = NIC_RESOURCE_DESC_TYPE_V1;
+               nic_desc.hdr.desc_len = RESOURCE_DESC_SIZE_V1;
+               nic_desc.flags = (1 << IMM_SHIFT) | (1 << NOSV_SHIFT);
+               bw_percent = max_rate ? (max_rate * 100) / link_speed : 100;
+               nic_desc.bw_max = cpu_to_le32(bw_percent);
        }
+
+       return be_cmd_set_profile_config(adapter, &nic_desc,
+                                        nic_desc.hdr.desc_len,
+                                        version, domain);
 }
 
 int be_cmd_manage_iface(struct be_adapter *adapter, u32 iface, u8 op)
@@ -3859,7 +3919,7 @@ err:
 }
 
 int be_roce_mcc_cmd(void *netdev_handle, void *wrb_payload,
-                       int wrb_payload_size, u16 *cmd_status, u16 *ext_status)
+                   int wrb_payload_size, u16 *cmd_status, u16 *ext_status)
 {
        struct be_adapter *adapter = netdev_priv(netdev_handle);
        struct be_mcc_wrb *wrb;
index b60e4d53c1c9a9f29be9d1c2c07099dab28b378a..3e0a6b243806d3bed3d7388899a76bcbd360ec1b 100644 (file)
@@ -50,7 +50,7 @@ struct be_mcc_wrb {
 #define CQE_FLAGS_CONSUMED_MASK        (1 << 27)
 
 /* Completion Status */
-enum {
+enum mcc_base_status {
        MCC_STATUS_SUCCESS = 0,
        MCC_STATUS_FAILED = 1,
        MCC_STATUS_ILLEGAL_REQUEST = 2,
@@ -60,12 +60,25 @@ enum {
        MCC_STATUS_NOT_SUPPORTED = 66
 };
 
-#define MCC_ADDL_STS_INSUFFICIENT_RESOURCES    0x16
+/* Additional status */
+enum mcc_addl_status {
+       MCC_ADDL_STATUS_INSUFFICIENT_RESOURCES = 0x16,
+       MCC_ADDL_STATUS_FLASH_IMAGE_CRC_MISMATCH = 0x4d,
+       MCC_ADDL_STATUS_TOO_MANY_INTERFACES = 0x4a
+};
+
+#define CQE_BASE_STATUS_MASK           0xFFFF
+#define CQE_BASE_STATUS_SHIFT          0       /* bits 0 - 15 */
+#define CQE_ADDL_STATUS_MASK           0xFF
+#define CQE_ADDL_STATUS_SHIFT          16      /* bits 16 - 31 */
 
-#define CQE_STATUS_COMPL_MASK          0xFFFF
-#define CQE_STATUS_COMPL_SHIFT         0       /* bits 0 - 15 */
-#define CQE_STATUS_EXTD_MASK           0xFFFF
-#define CQE_STATUS_EXTD_SHIFT          16      /* bits 16 - 31 */
+#define base_status(status)            \
+               ((enum mcc_base_status) \
+                       (status > 0 ? (status & CQE_BASE_STATUS_MASK) : 0))
+#define addl_status(status)            \
+               ((enum mcc_addl_status) \
+                       (status > 0 ? (status >> CQE_ADDL_STATUS_SHIFT) & \
+                                       CQE_ADDL_STATUS_MASK : 0))
 
 struct be_mcc_compl {
        u32 status;             /* dword 0 */
@@ -74,13 +87,13 @@ struct be_mcc_compl {
        u32 flags;              /* dword 3 */
 };
 
-/* When the async bit of mcc_compl is set, the last 4 bytes of
- * mcc_compl is interpreted as follows:
+/* When the async bit of mcc_compl flags is set, flags
+ * is interpreted as follows:
  */
-#define ASYNC_TRAILER_EVENT_CODE_SHIFT 8       /* bits 8 - 15 */
-#define ASYNC_TRAILER_EVENT_CODE_MASK  0xFF
-#define ASYNC_TRAILER_EVENT_TYPE_SHIFT 16
-#define ASYNC_TRAILER_EVENT_TYPE_MASK  0xFF
+#define ASYNC_EVENT_CODE_SHIFT         8       /* bits 8 - 15 */
+#define ASYNC_EVENT_CODE_MASK          0xFF
+#define ASYNC_EVENT_TYPE_SHIFT         16
+#define ASYNC_EVENT_TYPE_MASK          0xFF
 #define ASYNC_EVENT_CODE_LINK_STATE    0x1
 #define ASYNC_EVENT_CODE_GRP_5         0x5
 #define ASYNC_EVENT_QOS_SPEED          0x1
@@ -89,10 +102,6 @@ struct be_mcc_compl {
 #define ASYNC_EVENT_CODE_QNQ           0x6
 #define ASYNC_DEBUG_EVENT_TYPE_QNQ     1
 
-struct be_async_event_trailer {
-       u32 code;
-};
-
 enum {
        LINK_DOWN       = 0x0,
        LINK_UP         = 0x1
@@ -100,7 +109,7 @@ enum {
 #define LINK_STATUS_MASK                       0x1
 #define LOGICAL_LINK_STATUS_MASK               0x2
 
-/* When the event code of an async trailer is link-state, the mcc_compl
+/* When the event code of compl->flags is link-state, the mcc_compl
  * must be interpreted as follows
  */
 struct be_async_event_link_state {
@@ -110,10 +119,10 @@ struct be_async_event_link_state {
        u8 port_speed;
        u8 port_fault;
        u8 rsvd0[7];
-       struct be_async_event_trailer trailer;
+       u32 flags;
 } __packed;
 
-/* When the event code of an async trailer is GRP-5 and event_type is QOS_SPEED
+/* When the event code of compl->flags is GRP-5 and event_type is QOS_SPEED
  * the mcc_compl must be interpreted as follows
  */
 struct be_async_event_grp5_qos_link_speed {
@@ -121,10 +130,10 @@ struct be_async_event_grp5_qos_link_speed {
        u8 rsvd[5];
        u16 qos_link_speed;
        u32 event_tag;
-       struct be_async_event_trailer trailer;
+       u32 flags;
 } __packed;
 
-/* When the event code of an async trailer is GRP5 and event type is
+/* When the event code of compl->flags is GRP5 and event type is
  * CoS-Priority, the mcc_compl must be interpreted as follows
  */
 struct be_async_event_grp5_cos_priority {
@@ -134,10 +143,10 @@ struct be_async_event_grp5_cos_priority {
        u8 valid;
        u8 rsvd0;
        u8 event_tag;
-       struct be_async_event_trailer trailer;
+       u32 flags;
 } __packed;
 
-/* When the event code of an async trailer is GRP5 and event type is
+/* When the event code of compl->flags is GRP5 and event type is
  * PVID state, the mcc_compl must be interpreted as follows
  */
 struct be_async_event_grp5_pvid_state {
@@ -146,7 +155,7 @@ struct be_async_event_grp5_pvid_state {
        u16 tag;
        u32 event_tag;
        u32 rsvd1;
-       struct be_async_event_trailer trailer;
+       u32 flags;
 } __packed;
 
 /* async event indicating outer VLAN tag in QnQ */
@@ -156,7 +165,7 @@ struct be_async_event_qnq {
        u16 vlan_tag;
        u32 event_tag;
        u8 rsvd1[4];
-       struct be_async_event_trailer trailer;
+       u32 flags;
 } __packed;
 
 struct be_mcc_mailbox {
@@ -258,8 +267,8 @@ struct be_cmd_resp_hdr {
        u8 opcode;              /* dword 0 */
        u8 subsystem;           /* dword 0 */
        u8 rsvd[2];             /* dword 0 */
-       u8 status;              /* dword 1 */
-       u8 add_status;          /* dword 1 */
+       u8 base_status;         /* dword 1 */
+       u8 addl_status;         /* dword 1 */
        u8 rsvd1[2];            /* dword 1 */
        u32 response_length;    /* dword 2 */
        u32 actual_resp_len;    /* dword 3 */
@@ -1186,7 +1195,8 @@ struct be_cmd_read_flash_crc {
        struct flashrom_params params;
        u8 crc[4];
        u8 rsvd[4];
-};
+} __packed;
+
 /**************** Lancer Firmware Flash ************/
 struct amap_lancer_write_obj_context {
        u8 write_length[24];
@@ -1891,16 +1901,20 @@ struct be_nic_res_desc {
        u16 cq_count;
        u16 toe_conn_count;
        u16 eq_count;
-       u32 rsvd5;
+       u16 vlan_id;
+       u16 iface_count;
        u32 cap_flags;
        u8 link_param;
-       u8 rsvd6[3];
+       u8 rsvd6;
+       u16 channel_id_param;
        u32 bw_min;
        u32 bw_max;
        u8 acpi_params;
        u8 wol_param;
        u16 rsvd7;
-       u32 rsvd8[7];
+       u16 tunnel_iface_count;
+       u16 direct_tenant_iface_count;
+       u32 rsvd8[6];
 } __packed;
 
 /************ Multi-Channel type ***********/
@@ -2060,7 +2074,7 @@ int be_cmd_get_fw_ver(struct be_adapter *adapter, char *fw_ver,
                      char *fw_on_flash);
 int be_cmd_modify_eqd(struct be_adapter *adapter, struct be_set_eqd *, int num);
 int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array,
-                      u32 num, bool promiscuous);
+                      u32 num);
 int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 status);
 int be_cmd_set_flow_control(struct be_adapter *adapter, u32 tx_fc, u32 rx_fc);
 int be_cmd_get_flow_control(struct be_adapter *adapter, u32 *tx_fc, u32 *rx_fc);
@@ -2068,7 +2082,7 @@ int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num,
                        u32 *function_mode, u32 *function_caps, u16 *asic_rev);
 int be_cmd_reset_function(struct be_adapter *adapter);
 int be_cmd_rss_config(struct be_adapter *adapter, u8 *rsstable,
-                     u32 rss_hash_opts, u16 table_size);
+                     u32 rss_hash_opts, u16 table_size, const u8 *rss_hkey);
 int be_process_mcc(struct be_adapter *adapter);
 int be_cmd_set_beacon_state(struct be_adapter *adapter, u8 port_num, u8 beacon,
                            u8 status, u8 state);
@@ -2084,7 +2098,7 @@ int lancer_cmd_read_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
                           u32 data_size, u32 data_offset, const char *obj_name,
                           u32 *data_read, u32 *eof, u8 *addn_status);
 int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc,
-                        int offset);
+                         u16 optype, int offset);
 int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac,
                            struct be_dma_mem *nonemb_cmd);
 int be_cmd_fw_init(struct be_adapter *adapter);
@@ -2101,7 +2115,8 @@ int be_cmd_get_seeprom_data(struct be_adapter *adapter,
 int be_cmd_set_loopback(struct be_adapter *adapter, u8 port_num,
                        u8 loopback_type, u8 enable);
 int be_cmd_get_phy_info(struct be_adapter *adapter);
-int be_cmd_config_qos(struct be_adapter *adapter, u32 bps, u8 domain);
+int be_cmd_config_qos(struct be_adapter *adapter, u32 max_rate,
+                     u16 link_speed, u8 domain);
 void be_detect_error(struct be_adapter *adapter);
 int be_cmd_get_die_temperature(struct be_adapter *adapter);
 int be_cmd_get_cntl_attributes(struct be_adapter *adapter);
index 15ba96cba65df1ba051cc3b2e49b72640d92c217..e2da4d20dd3de7441f8137f1be8a9ecdf5cec615 100644 (file)
@@ -132,6 +132,7 @@ static const struct be_ethtool_stat et_rx_stats[] = {
        {DRVSTAT_RX_INFO(rx_bytes)},/* If moving this member see above note */
        {DRVSTAT_RX_INFO(rx_pkts)}, /* If moving this member see above note */
        {DRVSTAT_RX_INFO(rx_compl)},
+       {DRVSTAT_RX_INFO(rx_compl_err)},
        {DRVSTAT_RX_INFO(rx_mcast_pkts)},
        /* Number of page allocation failures while posting receive buffers
         * to HW.
@@ -181,7 +182,7 @@ static const char et_self_tests[][ETH_GSTRING_LEN] = {
 #define BE_NO_LOOPBACK 0xff
 
 static void be_get_drvinfo(struct net_device *netdev,
-                               struct ethtool_drvinfo *drvinfo)
+                          struct ethtool_drvinfo *drvinfo)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
 
@@ -201,8 +202,7 @@ static void be_get_drvinfo(struct net_device *netdev,
        drvinfo->eedump_len = 0;
 }
 
-static u32
-lancer_cmd_get_file_len(struct be_adapter *adapter, u8 *file_name)
+static u32 lancer_cmd_get_file_len(struct be_adapter *adapter, u8 *file_name)
 {
        u32 data_read = 0, eof;
        u8 addn_status;
@@ -212,14 +212,14 @@ lancer_cmd_get_file_len(struct be_adapter *adapter, u8 *file_name)
        memset(&data_len_cmd, 0, sizeof(data_len_cmd));
        /* data_offset and data_size should be 0 to get reg len */
        status = lancer_cmd_read_object(adapter, &data_len_cmd, 0, 0,
-                               file_name, &data_read, &eof, &addn_status);
+                                       file_name, &data_read, &eof,
+                                       &addn_status);
 
        return data_read;
 }
 
-static int
-lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
-               u32 buf_len, void *buf)
+static int lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
+                               u32 buf_len, void *buf)
 {
        struct be_dma_mem read_cmd;
        u32 read_len = 0, total_read_len = 0, chunk_size;
@@ -229,11 +229,11 @@ lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
 
        read_cmd.size = LANCER_READ_FILE_CHUNK;
        read_cmd.va = pci_alloc_consistent(adapter->pdev, read_cmd.size,
-                       &read_cmd.dma);
+                                          &read_cmd.dma);
 
        if (!read_cmd.va) {
                dev_err(&adapter->pdev->dev,
-                               "Memory allocation failure while reading dump\n");
+                       "Memory allocation failure while reading dump\n");
                return -ENOMEM;
        }
 
@@ -242,8 +242,8 @@ lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
                                LANCER_READ_FILE_CHUNK);
                chunk_size = ALIGN(chunk_size, 4);
                status = lancer_cmd_read_object(adapter, &read_cmd, chunk_size,
-                               total_read_len, file_name, &read_len,
-                               &eof, &addn_status);
+                                               total_read_len, file_name,
+                                               &read_len, &eof, &addn_status);
                if (!status) {
                        memcpy(buf + total_read_len, read_cmd.va, read_len);
                        total_read_len += read_len;
@@ -254,13 +254,12 @@ lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
                }
        }
        pci_free_consistent(adapter->pdev, read_cmd.size, read_cmd.va,
-                       read_cmd.dma);
+                           read_cmd.dma);
 
        return status;
 }
 
-static int
-be_get_reg_len(struct net_device *netdev)
+static int be_get_reg_len(struct net_device *netdev)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        u32 log_size = 0;
@@ -271,7 +270,7 @@ be_get_reg_len(struct net_device *netdev)
        if (be_physfn(adapter)) {
                if (lancer_chip(adapter))
                        log_size = lancer_cmd_get_file_len(adapter,
-                                       LANCER_FW_DUMP_FILE);
+                                                          LANCER_FW_DUMP_FILE);
                else
                        be_cmd_get_reg_len(adapter, &log_size);
        }
@@ -287,7 +286,7 @@ be_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *buf)
                memset(buf, 0, regs->len);
                if (lancer_chip(adapter))
                        lancer_cmd_read_file(adapter, LANCER_FW_DUMP_FILE,
-                                       regs->len, buf);
+                                            regs->len, buf);
                else
                        be_cmd_get_regs(adapter, regs->len, buf);
        }
@@ -337,9 +336,8 @@ static int be_set_coalesce(struct net_device *netdev,
        return 0;
 }
 
-static void
-be_get_ethtool_stats(struct net_device *netdev,
-               struct ethtool_stats *stats, uint64_t *data)
+static void be_get_ethtool_stats(struct net_device *netdev,
+                                struct ethtool_stats *stats, uint64_t *data)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        struct be_rx_obj *rxo;
@@ -390,9 +388,8 @@ be_get_ethtool_stats(struct net_device *netdev,
        }
 }
 
-static void
-be_get_stat_strings(struct net_device *netdev, uint32_t stringset,
-               uint8_t *data)
+static void be_get_stat_strings(struct net_device *netdev, uint32_t stringset,
+                               uint8_t *data)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        int i, j;
@@ -642,16 +639,15 @@ be_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *ecmd)
        adapter->rx_fc = ecmd->rx_pause;
 
        status = be_cmd_set_flow_control(adapter,
-                                       adapter->tx_fc, adapter->rx_fc);
+                                        adapter->tx_fc, adapter->rx_fc);
        if (status)
                dev_warn(&adapter->pdev->dev, "Pause param set failed.\n");
 
        return status;
 }
 
-static int
-be_set_phys_id(struct net_device *netdev,
-              enum ethtool_phys_id_state state)
+static int be_set_phys_id(struct net_device *netdev,
+                         enum ethtool_phys_id_state state)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
 
@@ -708,8 +704,7 @@ static int be_set_dump(struct net_device *netdev, struct ethtool_dump *dump)
        return status;
 }
 
-static void
-be_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
+static void be_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
 
@@ -723,8 +718,7 @@ be_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
        memset(&wol->sopass, 0, sizeof(wol->sopass));
 }
 
-static int
-be_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
+static int be_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
 
@@ -744,8 +738,7 @@ be_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
        return 0;
 }
 
-static int
-be_test_ddr_dma(struct be_adapter *adapter)
+static int be_test_ddr_dma(struct be_adapter *adapter)
 {
        int ret, i;
        struct be_dma_mem ddrdma_cmd;
@@ -761,7 +754,7 @@ be_test_ddr_dma(struct be_adapter *adapter)
 
        for (i = 0; i < 2; i++) {
                ret = be_cmd_ddr_dma_test(adapter, pattern[i],
-                                       4096, &ddrdma_cmd);
+                                         4096, &ddrdma_cmd);
                if (ret != 0)
                        goto err;
        }
@@ -773,20 +766,17 @@ err:
 }
 
 static u64 be_loopback_test(struct be_adapter *adapter, u8 loopback_type,
-                               u64 *status)
+                           u64 *status)
 {
-       be_cmd_set_loopback(adapter, adapter->hba_port_num,
-                               loopback_type, 1);
+       be_cmd_set_loopback(adapter, adapter->hba_port_num, loopback_type, 1);
        *status = be_cmd_loopback_test(adapter, adapter->hba_port_num,
-                               loopback_type, 1500,
-                               2, 0xabc);
-       be_cmd_set_loopback(adapter, adapter->hba_port_num,
-                               BE_NO_LOOPBACK, 1);
+                                      loopback_type, 1500, 2, 0xabc);
+       be_cmd_set_loopback(adapter, adapter->hba_port_num, BE_NO_LOOPBACK, 1);
        return *status;
 }
 
-static void
-be_self_test(struct net_device *netdev, struct ethtool_test *test, u64 *data)
+static void be_self_test(struct net_device *netdev, struct ethtool_test *test,
+                        u64 *data)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        int status;
@@ -801,12 +791,10 @@ be_self_test(struct net_device *netdev, struct ethtool_test *test, u64 *data)
        memset(data, 0, sizeof(u64) * ETHTOOL_TESTS_NUM);
 
        if (test->flags & ETH_TEST_FL_OFFLINE) {
-               if (be_loopback_test(adapter, BE_MAC_LOOPBACK,
-                                    &data[0]) != 0)
+               if (be_loopback_test(adapter, BE_MAC_LOOPBACK, &data[0]) != 0)
                        test->flags |= ETH_TEST_FL_FAILED;
 
-               if (be_loopback_test(adapter, BE_PHY_LOOPBACK,
-                                    &data[1]) != 0)
+               if (be_loopback_test(adapter, BE_PHY_LOOPBACK, &data[1]) != 0)
                        test->flags |= ETH_TEST_FL_FAILED;
 
                if (test->flags & ETH_TEST_FL_EXTERNAL_LB) {
@@ -832,16 +820,14 @@ be_self_test(struct net_device *netdev, struct ethtool_test *test, u64 *data)
        }
 }
 
-static int
-be_do_flash(struct net_device *netdev, struct ethtool_flash *efl)
+static int be_do_flash(struct net_device *netdev, struct ethtool_flash *efl)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
 
        return be_load_fw(adapter, efl->data);
 }
 
-static int
-be_get_eeprom_len(struct net_device *netdev)
+static int be_get_eeprom_len(struct net_device *netdev)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
 
@@ -851,18 +837,17 @@ be_get_eeprom_len(struct net_device *netdev)
        if (lancer_chip(adapter)) {
                if (be_physfn(adapter))
                        return lancer_cmd_get_file_len(adapter,
-                                       LANCER_VPD_PF_FILE);
+                                                      LANCER_VPD_PF_FILE);
                else
                        return lancer_cmd_get_file_len(adapter,
-                                       LANCER_VPD_VF_FILE);
+                                                      LANCER_VPD_VF_FILE);
        } else {
                return BE_READ_SEEPROM_LEN;
        }
 }
 
-static int
-be_read_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom,
-                       uint8_t *data)
+static int be_read_eeprom(struct net_device *netdev,
+                         struct ethtool_eeprom *eeprom, uint8_t *data)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        struct be_dma_mem eeprom_cmd;
@@ -875,10 +860,10 @@ be_read_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom,
        if (lancer_chip(adapter)) {
                if (be_physfn(adapter))
                        return lancer_cmd_read_file(adapter, LANCER_VPD_PF_FILE,
-                                       eeprom->len, data);
+                                                   eeprom->len, data);
                else
                        return lancer_cmd_read_file(adapter, LANCER_VPD_VF_FILE,
-                                       eeprom->len, data);
+                                                   eeprom->len, data);
        }
 
        eeprom->magic = BE_VENDOR_ID | (adapter->pdev->device<<16);
@@ -933,27 +918,27 @@ static u64 be_get_rss_hash_opts(struct be_adapter *adapter, u64 flow_type)
 
        switch (flow_type) {
        case TCP_V4_FLOW:
-               if (adapter->rss_flags & RSS_ENABLE_IPV4)
+               if (adapter->rss_info.rss_flags & RSS_ENABLE_IPV4)
                        data |= RXH_IP_DST | RXH_IP_SRC;
-               if (adapter->rss_flags & RSS_ENABLE_TCP_IPV4)
+               if (adapter->rss_info.rss_flags & RSS_ENABLE_TCP_IPV4)
                        data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
                break;
        case UDP_V4_FLOW:
-               if (adapter->rss_flags & RSS_ENABLE_IPV4)
+               if (adapter->rss_info.rss_flags & RSS_ENABLE_IPV4)
                        data |= RXH_IP_DST | RXH_IP_SRC;
-               if (adapter->rss_flags & RSS_ENABLE_UDP_IPV4)
+               if (adapter->rss_info.rss_flags & RSS_ENABLE_UDP_IPV4)
                        data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
                break;
        case TCP_V6_FLOW:
-               if (adapter->rss_flags & RSS_ENABLE_IPV6)
+               if (adapter->rss_info.rss_flags & RSS_ENABLE_IPV6)
                        data |= RXH_IP_DST | RXH_IP_SRC;
-               if (adapter->rss_flags & RSS_ENABLE_TCP_IPV6)
+               if (adapter->rss_info.rss_flags & RSS_ENABLE_TCP_IPV6)
                        data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
                break;
        case UDP_V6_FLOW:
-               if (adapter->rss_flags & RSS_ENABLE_IPV6)
+               if (adapter->rss_info.rss_flags & RSS_ENABLE_IPV6)
                        data |= RXH_IP_DST | RXH_IP_SRC;
-               if (adapter->rss_flags & RSS_ENABLE_UDP_IPV6)
+               if (adapter->rss_info.rss_flags & RSS_ENABLE_UDP_IPV6)
                        data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
                break;
        }
@@ -962,7 +947,7 @@ static u64 be_get_rss_hash_opts(struct be_adapter *adapter, u64 flow_type)
 }
 
 static int be_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
-                     u32 *rule_locs)
+                       u32 *rule_locs)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
 
@@ -992,7 +977,7 @@ static int be_set_rss_hash_opts(struct be_adapter *adapter,
        struct be_rx_obj *rxo;
        int status = 0, i, j;
        u8 rsstable[128];
-       u32 rss_flags = adapter->rss_flags;
+       u32 rss_flags = adapter->rss_info.rss_flags;
 
        if (cmd->data != L3_RSS_FLAGS &&
            cmd->data != (L3_RSS_FLAGS | L4_RSS_FLAGS))
@@ -1039,7 +1024,7 @@ static int be_set_rss_hash_opts(struct be_adapter *adapter,
                return -EINVAL;
        }
 
-       if (rss_flags == adapter->rss_flags)
+       if (rss_flags == adapter->rss_info.rss_flags)
                return status;
 
        if (be_multi_rxq(adapter)) {
@@ -1051,9 +1036,11 @@ static int be_set_rss_hash_opts(struct be_adapter *adapter,
                        }
                }
        }
-       status = be_cmd_rss_config(adapter, rsstable, rss_flags, 128);
+
+       status = be_cmd_rss_config(adapter, adapter->rss_info.rsstable,
+                                  rss_flags, 128, adapter->rss_info.rss_hkey);
        if (!status)
-               adapter->rss_flags = rss_flags;
+               adapter->rss_info.rss_flags = rss_flags;
 
        return status;
 }
@@ -1103,6 +1090,69 @@ static int be_set_channels(struct net_device  *netdev,
        return be_update_queues(adapter);
 }
 
+static u32 be_get_rxfh_indir_size(struct net_device *netdev)
+{
+       return RSS_INDIR_TABLE_LEN;
+}
+
+static u32 be_get_rxfh_key_size(struct net_device *netdev)
+{
+       return RSS_HASH_KEY_LEN;
+}
+
+static int be_get_rxfh(struct net_device *netdev, u32 *indir, u8 *hkey)
+{
+       struct be_adapter *adapter = netdev_priv(netdev);
+       int i;
+       struct rss_info *rss = &adapter->rss_info;
+
+       if (indir) {
+               for (i = 0; i < RSS_INDIR_TABLE_LEN; i++)
+                       indir[i] = rss->rss_queue[i];
+       }
+
+       if (hkey)
+               memcpy(hkey, rss->rss_hkey, RSS_HASH_KEY_LEN);
+
+       return 0;
+}
+
+static int be_set_rxfh(struct net_device *netdev, const u32 *indir,
+                      const u8 *hkey)
+{
+       int rc = 0, i, j;
+       struct be_adapter *adapter = netdev_priv(netdev);
+       u8 rsstable[RSS_INDIR_TABLE_LEN];
+
+       if (indir) {
+               struct be_rx_obj *rxo;
+               for (i = 0; i < RSS_INDIR_TABLE_LEN; i++) {
+                       j = indir[i];
+                       rxo = &adapter->rx_obj[j];
+                       rsstable[i] = rxo->rss_id;
+                       adapter->rss_info.rss_queue[i] = j;
+               }
+       } else {
+               memcpy(rsstable, adapter->rss_info.rsstable,
+                      RSS_INDIR_TABLE_LEN);
+       }
+
+       if (!hkey)
+               hkey =  adapter->rss_info.rss_hkey;
+
+       rc = be_cmd_rss_config(adapter, rsstable,
+                       adapter->rss_info.rss_flags,
+                       RSS_INDIR_TABLE_LEN, hkey);
+       if (rc) {
+               adapter->rss_info.rss_flags = RSS_ENABLE_NONE;
+               return -EIO;
+       }
+       memcpy(adapter->rss_info.rss_hkey, hkey, RSS_HASH_KEY_LEN);
+       memcpy(adapter->rss_info.rsstable, rsstable,
+              RSS_INDIR_TABLE_LEN);
+       return 0;
+}
+
 const struct ethtool_ops be_ethtool_ops = {
        .get_settings = be_get_settings,
        .get_drvinfo = be_get_drvinfo,
@@ -1129,6 +1179,10 @@ const struct ethtool_ops be_ethtool_ops = {
        .self_test = be_self_test,
        .get_rxnfc = be_get_rxnfc,
        .set_rxnfc = be_set_rxnfc,
+       .get_rxfh_indir_size = be_get_rxfh_indir_size,
+       .get_rxfh_key_size = be_get_rxfh_key_size,
+       .get_rxfh = be_get_rxfh,
+       .set_rxfh = be_set_rxfh,
        .get_channels = be_get_channels,
        .set_channels = be_set_channels
 };
index 3bd198550edbb95a64602e28535e8eb56758a220..8840c64aaeca7daca310d0a5a38dfdbed790fb12 100644 (file)
 #define OPTYPE_FCOE_FW_ACTIVE          10
 #define OPTYPE_FCOE_FW_BACKUP          11
 #define OPTYPE_NCSI_FW                 13
+#define OPTYPE_REDBOOT_DIR             18
+#define OPTYPE_REDBOOT_CONFIG          19
+#define OPTYPE_SH_PHY_FW               21
+#define OPTYPE_FLASHISM_JUMPVECTOR     22
+#define OPTYPE_UFI_DIR                 23
 #define OPTYPE_PHY_FW                  99
 #define TN_8022                                13
 
-#define ILLEGAL_IOCTL_REQ              2
 #define FLASHROM_OPER_PHY_FLASH                9
 #define FLASHROM_OPER_PHY_SAVE         10
 #define FLASHROM_OPER_FLASH            1
 #define IMAGE_FIRMWARE_BACKUP_FCoE     178
 #define IMAGE_FIRMWARE_BACKUP_COMP_FCoE 179
 #define IMAGE_FIRMWARE_PHY             192
+#define IMAGE_REDBOOT_DIR              208
+#define IMAGE_REDBOOT_CONFIG           209
+#define IMAGE_UFI_DIR                  210
 #define IMAGE_BOOT_CODE                        224
 
 /************* Rx Packet Type Encoding **************/
@@ -534,7 +541,8 @@ struct flash_section_entry {
        u32 image_size;
        u32 cksum;
        u32 entry_point;
-       u32 rsvd0;
+       u16 optype;
+       u16 rsvd0;
        u32 rsvd1;
        u8 ver_data[32];
 } __packed;
index dc19bc5dec7732c02220e009ad08ffc393aa1c2c..6822b3d76d85960f3c9b48b3cd4d8c4bad44d87b 100644 (file)
@@ -134,7 +134,7 @@ static void be_queue_free(struct be_adapter *adapter, struct be_queue_info *q)
 }
 
 static int be_queue_alloc(struct be_adapter *adapter, struct be_queue_info *q,
-               u16 len, u16 entry_size)
+                         u16 len, u16 entry_size)
 {
        struct be_dma_mem *mem = &q->dma_mem;
 
@@ -154,7 +154,7 @@ static void be_reg_intr_set(struct be_adapter *adapter, bool enable)
        u32 reg, enabled;
 
        pci_read_config_dword(adapter->pdev, PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET,
-                               &reg);
+                             &reg);
        enabled = reg & MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
 
        if (!enabled && enable)
@@ -165,7 +165,7 @@ static void be_reg_intr_set(struct be_adapter *adapter, bool enable)
                return;
 
        pci_write_config_dword(adapter->pdev,
-                       PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET, reg);
+                              PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET, reg);
 }
 
 static void be_intr_set(struct be_adapter *adapter, bool enable)
@@ -206,12 +206,11 @@ static void be_txq_notify(struct be_adapter *adapter, struct be_tx_obj *txo,
 }
 
 static void be_eq_notify(struct be_adapter *adapter, u16 qid,
-               bool arm, bool clear_int, u16 num_popped)
+                        bool arm, bool clear_int, u16 num_popped)
 {
        u32 val = 0;
        val |= qid & DB_EQ_RING_ID_MASK;
-       val |= ((qid & DB_EQ_RING_ID_EXT_MASK) <<
-                       DB_EQ_RING_ID_EXT_MASK_SHIFT);
+       val |= ((qid & DB_EQ_RING_ID_EXT_MASK) << DB_EQ_RING_ID_EXT_MASK_SHIFT);
 
        if (adapter->eeh_error)
                return;
@@ -477,7 +476,7 @@ static void populate_be_v2_stats(struct be_adapter *adapter)
        drvs->rx_drops_no_tpre_descr = rxf_stats->rx_drops_no_tpre_descr;
        drvs->rx_drops_too_many_frags = rxf_stats->rx_drops_too_many_frags;
        adapter->drv_stats.eth_red_drops = pmem_sts->eth_red_drops;
-       if (be_roce_supported(adapter))  {
+       if (be_roce_supported(adapter)) {
                drvs->rx_roce_bytes_lsd = port_stats->roce_bytes_received_lsd;
                drvs->rx_roce_bytes_msd = port_stats->roce_bytes_received_msd;
                drvs->rx_roce_frames = port_stats->roce_frames_received;
@@ -491,8 +490,7 @@ static void populate_lancer_stats(struct be_adapter *adapter)
 {
 
        struct be_drv_stats *drvs = &adapter->drv_stats;
-       struct lancer_pport_stats *pport_stats =
-                                       pport_stats_from_cmd(adapter);
+       struct lancer_pport_stats *pport_stats = pport_stats_from_cmd(adapter);
 
        be_dws_le_to_cpu(pport_stats, sizeof(*pport_stats));
        drvs->rx_pause_frames = pport_stats->rx_pause_frames_lo;
@@ -539,8 +537,7 @@ static void accumulate_16bit_val(u32 *acc, u16 val)
 }
 
 static void populate_erx_stats(struct be_adapter *adapter,
-                       struct be_rx_obj *rxo,
-                       u32 erx_stat)
+                              struct be_rx_obj *rxo, u32 erx_stat)
 {
        if (!BEx_chip(adapter))
                rx_stats(rxo)->rx_drops_no_frags = erx_stat;
@@ -579,7 +576,7 @@ void be_parse_stats(struct be_adapter *adapter)
 }
 
 static struct rtnl_link_stats64 *be_get_stats64(struct net_device *netdev,
-                                       struct rtnl_link_stats64 *stats)
+                                               struct rtnl_link_stats64 *stats)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        struct be_drv_stats *drvs = &adapter->drv_stats;
@@ -660,7 +657,8 @@ void be_link_status_update(struct be_adapter *adapter, u8 link_status)
 }
 
 static void be_tx_stats_update(struct be_tx_obj *txo,
-                       u32 wrb_cnt, u32 copied, u32 gso_segs, bool stopped)
+                              u32 wrb_cnt, u32 copied, u32 gso_segs,
+                              bool stopped)
 {
        struct be_tx_stats *stats = tx_stats(txo);
 
@@ -676,7 +674,7 @@ static void be_tx_stats_update(struct be_tx_obj *txo,
 
 /* Determine number of WRB entries needed to xmit data in an skb */
 static u32 wrb_cnt_for_skb(struct be_adapter *adapter, struct sk_buff *skb,
-                                                               bool *dummy)
+                          bool *dummy)
 {
        int cnt = (skb->len > skb->data_len);
 
@@ -704,7 +702,7 @@ static inline void wrb_fill(struct be_eth_wrb *wrb, u64 addr, int len)
 }
 
 static inline u16 be_get_tx_vlan_tag(struct be_adapter *adapter,
-                                       struct sk_buff *skb)
+                                    struct sk_buff *skb)
 {
        u8 vlan_prio;
        u16 vlan_tag;
@@ -733,7 +731,8 @@ static u16 skb_ip_proto(struct sk_buff *skb)
 }
 
 static void wrb_fill_hdr(struct be_adapter *adapter, struct be_eth_hdr_wrb *hdr,
-               struct sk_buff *skb, u32 wrb_cnt, u32 len, bool skip_hw_vlan)
+                        struct sk_buff *skb, u32 wrb_cnt, u32 len,
+                        bool skip_hw_vlan)
 {
        u16 vlan_tag, proto;
 
@@ -774,7 +773,7 @@ static void wrb_fill_hdr(struct be_adapter *adapter, struct be_eth_hdr_wrb *hdr,
 }
 
 static void unmap_tx_frag(struct device *dev, struct be_eth_wrb *wrb,
-               bool unmap_single)
+                         bool unmap_single)
 {
        dma_addr_t dma;
 
@@ -791,8 +790,8 @@ static void unmap_tx_frag(struct device *dev, struct be_eth_wrb *wrb,
 }
 
 static int make_tx_wrbs(struct be_adapter *adapter, struct be_queue_info *txq,
-               struct sk_buff *skb, u32 wrb_cnt, bool dummy_wrb,
-               bool skip_hw_vlan)
+                       struct sk_buff *skb, u32 wrb_cnt, bool dummy_wrb,
+                       bool skip_hw_vlan)
 {
        dma_addr_t busaddr;
        int i, copied = 0;
@@ -821,8 +820,7 @@ static int make_tx_wrbs(struct be_adapter *adapter, struct be_queue_info *txq,
        }
 
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
-               const struct skb_frag_struct *frag =
-                       &skb_shinfo(skb)->frags[i];
+               const struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i];
                busaddr = skb_frag_dma_map(dev, frag, 0,
                                           skb_frag_size(frag), DMA_TO_DEVICE);
                if (dma_mapping_error(dev, busaddr))
@@ -927,8 +925,7 @@ 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);
 }
@@ -959,7 +956,7 @@ static struct sk_buff *be_lancer_xmit_workarounds(struct be_adapter *adapter,
         */
        if (be_pvid_tagging_enabled(adapter) &&
            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.
@@ -1077,16 +1074,15 @@ static int be_change_mtu(struct net_device *netdev, int new_mtu)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        if (new_mtu < BE_MIN_MTU ||
-                       new_mtu > (BE_MAX_JUMBO_FRAME_SIZE -
-                                       (ETH_HLEN + ETH_FCS_LEN))) {
+           new_mtu > (BE_MAX_JUMBO_FRAME_SIZE - (ETH_HLEN + ETH_FCS_LEN))) {
                dev_info(&adapter->pdev->dev,
-                       "MTU must be between %d and %d bytes\n",
-                       BE_MIN_MTU,
-                       (BE_MAX_JUMBO_FRAME_SIZE - (ETH_HLEN + ETH_FCS_LEN)));
+                        "MTU must be between %d and %d bytes\n",
+                        BE_MIN_MTU,
+                        (BE_MAX_JUMBO_FRAME_SIZE - (ETH_HLEN + ETH_FCS_LEN)));
                return -EINVAL;
        }
        dev_info(&adapter->pdev->dev, "MTU changed from %d to %d bytes\n",
-                       netdev->mtu, new_mtu);
+                netdev->mtu, new_mtu);
        netdev->mtu = new_mtu;
        return 0;
 }
@@ -1098,7 +1094,7 @@ static int be_change_mtu(struct net_device *netdev, int new_mtu)
 static int be_vid_config(struct be_adapter *adapter)
 {
        u16 vids[BE_NUM_VLANS_SUPPORTED];
-       u16 num = 0, i;
+       u16 num = 0, i = 0;
        int status = 0;
 
        /* No need to further configure vids if in promiscuous mode */
@@ -1109,16 +1105,14 @@ static int be_vid_config(struct be_adapter *adapter)
                goto set_vlan_promisc;
 
        /* Construct VLAN Table to give to HW */
-       for (i = 0; i < VLAN_N_VID; i++)
-               if (adapter->vlan_tag[i])
-                       vids[num++] = cpu_to_le16(i);
-
-       status = be_cmd_vlan_config(adapter, adapter->if_handle,
-                                   vids, num, 0);
+       for_each_set_bit(i, adapter->vids, VLAN_N_VID)
+               vids[num++] = cpu_to_le16(i);
 
+       status = be_cmd_vlan_config(adapter, adapter->if_handle, vids, num);
        if (status) {
                /* Set to VLAN promisc mode as setting VLAN filter failed */
-               if (status == MCC_ADDL_STS_INSUFFICIENT_RESOURCES)
+               if (addl_status(status) ==
+                               MCC_ADDL_STATUS_INSUFFICIENT_RESOURCES)
                        goto set_vlan_promisc;
                dev_err(&adapter->pdev->dev,
                        "Setting HW VLAN filtering failed.\n");
@@ -1160,16 +1154,16 @@ static int be_vlan_add_vid(struct net_device *netdev, __be16 proto, u16 vid)
        if (lancer_chip(adapter) && vid == 0)
                return status;
 
-       if (adapter->vlan_tag[vid])
+       if (test_bit(vid, adapter->vids))
                return status;
 
-       adapter->vlan_tag[vid] = 1;
+       set_bit(vid, adapter->vids);
        adapter->vlans_added++;
 
        status = be_vid_config(adapter);
        if (status) {
                adapter->vlans_added--;
-               adapter->vlan_tag[vid] = 0;
+               clear_bit(vid, adapter->vids);
        }
 
        return status;
@@ -1184,12 +1178,12 @@ static int be_vlan_rem_vid(struct net_device *netdev, __be16 proto, u16 vid)
        if (lancer_chip(adapter) && vid == 0)
                goto ret;
 
-       adapter->vlan_tag[vid] = 0;
+       clear_bit(vid, adapter->vids);
        status = be_vid_config(adapter);
        if (!status)
                adapter->vlans_added--;
        else
-               adapter->vlan_tag[vid] = 1;
+               set_bit(vid, adapter->vids);
 ret:
        return status;
 }
@@ -1197,7 +1191,7 @@ ret:
 static void be_clear_promisc(struct be_adapter *adapter)
 {
        adapter->promiscuous = false;
-       adapter->flags &= ~BE_FLAGS_VLAN_PROMISC;
+       adapter->flags &= ~(BE_FLAGS_VLAN_PROMISC | BE_FLAGS_MCAST_PROMISC);
 
        be_cmd_rx_filter(adapter, IFF_PROMISC, OFF);
 }
@@ -1222,10 +1216,8 @@ static void be_set_rx_mode(struct net_device *netdev)
 
        /* Enable multicast promisc if num configured exceeds what we support */
        if (netdev->flags & IFF_ALLMULTI ||
-           netdev_mc_count(netdev) > be_max_mc(adapter)) {
-               be_cmd_rx_filter(adapter, IFF_ALLMULTI, ON);
-               goto done;
-       }
+           netdev_mc_count(netdev) > be_max_mc(adapter))
+               goto set_mcast_promisc;
 
        if (netdev_uc_count(netdev) != adapter->uc_macs) {
                struct netdev_hw_addr *ha;
@@ -1251,13 +1243,22 @@ static void be_set_rx_mode(struct net_device *netdev)
        }
 
        status = be_cmd_rx_filter(adapter, IFF_MULTICAST, ON);
-
-       /* Set to MCAST promisc mode if setting MULTICAST address fails */
-       if (status) {
-               dev_info(&adapter->pdev->dev, "Exhausted multicast HW filters.\n");
-               dev_info(&adapter->pdev->dev, "Disabling HW multicast filtering.\n");
-               be_cmd_rx_filter(adapter, IFF_ALLMULTI, ON);
+       if (!status) {
+               if (adapter->flags & BE_FLAGS_MCAST_PROMISC)
+                       adapter->flags &= ~BE_FLAGS_MCAST_PROMISC;
+               goto done;
        }
+
+set_mcast_promisc:
+       if (adapter->flags & BE_FLAGS_MCAST_PROMISC)
+               return;
+
+       /* Set to MCAST promisc mode if setting MULTICAST address fails
+        * or if num configured exceeds what we support
+        */
+       status = be_cmd_rx_filter(adapter, IFF_ALLMULTI, ON);
+       if (!status)
+               adapter->flags |= BE_FLAGS_MCAST_PROMISC;
 done:
        return;
 }
@@ -1287,7 +1288,7 @@ static int be_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
 
        if (status)
                dev_err(&adapter->pdev->dev, "MAC %pM set on VF %d Failed\n",
-                               mac, vf);
+                       mac, vf);
        else
                memcpy(vf_cfg->mac_addr, mac, ETH_ALEN);
 
@@ -1295,7 +1296,7 @@ static int be_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
 }
 
 static int be_get_vf_config(struct net_device *netdev, int vf,
-                       struct ifla_vf_info *vi)
+                           struct ifla_vf_info *vi)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf];
@@ -1307,7 +1308,8 @@ static int be_get_vf_config(struct net_device *netdev, int vf,
                return -EINVAL;
 
        vi->vf = vf;
-       vi->tx_rate = vf_cfg->tx_rate;
+       vi->max_tx_rate = vf_cfg->tx_rate;
+       vi->min_tx_rate = 0;
        vi->vlan = vf_cfg->vlan_tag & VLAN_VID_MASK;
        vi->qos = vf_cfg->vlan_tag >> VLAN_PRIO_SHIFT;
        memcpy(&vi->mac, vf_cfg->mac_addr, ETH_ALEN);
@@ -1316,8 +1318,7 @@ static int be_get_vf_config(struct net_device *netdev, int vf,
        return 0;
 }
 
-static int be_set_vf_vlan(struct net_device *netdev,
-                       int vf, u16 vlan, u8 qos)
+static int be_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf];
@@ -1348,11 +1349,14 @@ static int be_set_vf_vlan(struct net_device *netdev,
        return status;
 }
 
-static int be_set_vf_tx_rate(struct net_device *netdev,
-                       int vf, int rate)
+static int be_set_vf_tx_rate(struct net_device *netdev, int vf,
+                            int min_tx_rate, int max_tx_rate)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
-       int status = 0;
+       struct device *dev = &adapter->pdev->dev;
+       int percent_rate, status = 0;
+       u16 link_speed = 0;
+       u8 link_status;
 
        if (!sriov_enabled(adapter))
                return -EPERM;
@@ -1360,18 +1364,50 @@ static int be_set_vf_tx_rate(struct net_device *netdev,
        if (vf >= adapter->num_vfs)
                return -EINVAL;
 
-       if (rate < 100 || rate > 10000) {
-               dev_err(&adapter->pdev->dev,
-                       "tx rate must be between 100 and 10000 Mbps\n");
+       if (min_tx_rate)
                return -EINVAL;
+
+       if (!max_tx_rate)
+               goto config_qos;
+
+       status = be_cmd_link_status_query(adapter, &link_speed,
+                                         &link_status, 0);
+       if (status)
+               goto err;
+
+       if (!link_status) {
+               dev_err(dev, "TX-rate setting not allowed when link is down\n");
+               status = -EPERM;
+               goto err;
+       }
+
+       if (max_tx_rate < 100 || max_tx_rate > link_speed) {
+               dev_err(dev, "TX-rate must be between 100 and %d Mbps\n",
+                       link_speed);
+               status = -EINVAL;
+               goto err;
+       }
+
+       /* On Skyhawk the QOS setting must be done only as a % value */
+       percent_rate = link_speed / 100;
+       if (skyhawk_chip(adapter) && (max_tx_rate % percent_rate)) {
+               dev_err(dev, "TX-rate must be a multiple of %d Mbps\n",
+                       percent_rate);
+               status = -EINVAL;
+               goto err;
        }
 
-       status = be_cmd_config_qos(adapter, rate / 10, vf + 1);
+config_qos:
+       status = be_cmd_config_qos(adapter, max_tx_rate, link_speed, vf + 1);
        if (status)
-               dev_err(&adapter->pdev->dev,
-                               "tx rate %d on VF %d failed\n", rate, vf);
-       else
-               adapter->vf_cfg[vf].tx_rate = rate;
+               goto err;
+
+       adapter->vf_cfg[vf].tx_rate = max_tx_rate;
+       return 0;
+
+err:
+       dev_err(dev, "TX-rate setting of %dMbps on VF%d failed\n",
+               max_tx_rate, vf);
        return status;
 }
 static int be_set_vf_link_state(struct net_device *netdev, int vf,
@@ -1469,7 +1505,7 @@ modify_eqd:
 }
 
 static void be_rx_stats_update(struct be_rx_obj *rxo,
-               struct be_rx_compl_info *rxcp)
+                              struct be_rx_compl_info *rxcp)
 {
        struct be_rx_stats *stats = rx_stats(rxo);
 
@@ -1566,7 +1602,8 @@ static void skb_fill_rx_data(struct be_rx_obj *rxo, struct sk_buff *skb,
                skb_frag_set_page(skb, 0, page_info->page);
                skb_shinfo(skb)->frags[0].page_offset =
                                        page_info->page_offset + hdr_len;
-               skb_frag_size_set(&skb_shinfo(skb)->frags[0], curr_frag_len - hdr_len);
+               skb_frag_size_set(&skb_shinfo(skb)->frags[0],
+                                 curr_frag_len - hdr_len);
                skb->data_len = curr_frag_len - hdr_len;
                skb->truesize += rx_frag_size;
                skb->tail += hdr_len;
@@ -1725,8 +1762,8 @@ static void be_parse_rx_compl_v1(struct be_eth_rx_compl *compl,
        if (rxcp->vlanf) {
                rxcp->qnq = AMAP_GET_BITS(struct amap_eth_rx_compl_v1, qnq,
                                          compl);
-               rxcp->vlan_tag = AMAP_GET_BITS(struct amap_eth_rx_compl_v1, vlan_tag,
-                                              compl);
+               rxcp->vlan_tag = AMAP_GET_BITS(struct amap_eth_rx_compl_v1,
+                                              vlan_tag, compl);
        }
        rxcp->port = AMAP_GET_BITS(struct amap_eth_rx_compl_v1, port, compl);
        rxcp->tunneled =
@@ -1757,8 +1794,8 @@ static void be_parse_rx_compl_v0(struct be_eth_rx_compl *compl,
        if (rxcp->vlanf) {
                rxcp->qnq = AMAP_GET_BITS(struct amap_eth_rx_compl_v0, qnq,
                                          compl);
-               rxcp->vlan_tag = AMAP_GET_BITS(struct amap_eth_rx_compl_v0, vlan_tag,
-                                              compl);
+               rxcp->vlan_tag = AMAP_GET_BITS(struct amap_eth_rx_compl_v0,
+                                              vlan_tag, compl);
        }
        rxcp->port = AMAP_GET_BITS(struct amap_eth_rx_compl_v0, port, compl);
        rxcp->ip_frag = AMAP_GET_BITS(struct amap_eth_rx_compl_v0,
@@ -1799,7 +1836,7 @@ static struct be_rx_compl_info *be_rx_compl_get(struct be_rx_obj *rxo)
                        rxcp->vlan_tag = swab16(rxcp->vlan_tag);
 
                if (adapter->pvid == (rxcp->vlan_tag & VLAN_VID_MASK) &&
-                   !adapter->vlan_tag[rxcp->vlan_tag])
+                   !test_bit(rxcp->vlan_tag, adapter->vids))
                        rxcp->vlanf = 0;
        }
 
@@ -1915,7 +1952,7 @@ static struct be_eth_tx_compl *be_tx_compl_get(struct be_queue_info *tx_cq)
 }
 
 static u16 be_tx_compl_process(struct be_adapter *adapter,
-               struct be_tx_obj *txo, u16 last_index)
+                              struct be_tx_obj *txo, u16 last_index)
 {
        struct be_queue_info *txq = &txo->q;
        struct be_eth_wrb *wrb;
@@ -2122,7 +2159,7 @@ static int be_evt_queues_create(struct be_adapter *adapter)
 
                eq = &eqo->q;
                rc = be_queue_alloc(adapter, eq, EVNT_Q_LEN,
-                                       sizeof(struct be_eq_entry));
+                                   sizeof(struct be_eq_entry));
                if (rc)
                        return rc;
 
@@ -2155,7 +2192,7 @@ static int be_mcc_queues_create(struct be_adapter *adapter)
 
        cq = &adapter->mcc_obj.cq;
        if (be_queue_alloc(adapter, cq, MCC_CQ_LEN,
-                       sizeof(struct be_mcc_compl)))
+                          sizeof(struct be_mcc_compl)))
                goto err;
 
        /* Use the default EQ for MCC completions */
@@ -2275,7 +2312,7 @@ static int be_rx_cqs_create(struct be_adapter *adapter)
                rxo->adapter = adapter;
                cq = &rxo->cq;
                rc = be_queue_alloc(adapter, cq, RX_CQ_LEN,
-                               sizeof(struct be_eth_rx_compl));
+                                   sizeof(struct be_eth_rx_compl));
                if (rc)
                        return rc;
 
@@ -2339,7 +2376,7 @@ static inline bool do_gro(struct be_rx_compl_info *rxcp)
 }
 
 static int be_process_rx(struct be_rx_obj *rxo, struct napi_struct *napi,
-                       int budget, int polling)
+                        int budget, int polling)
 {
        struct be_adapter *adapter = rxo->adapter;
        struct be_queue_info *rx_cq = &rxo->cq;
@@ -2365,7 +2402,7 @@ static int be_process_rx(struct be_rx_obj *rxo, struct napi_struct *napi,
                 * promiscuous mode on some skews
                 */
                if (unlikely(rxcp->port != adapter->port_num &&
-                               !lancer_chip(adapter))) {
+                            !lancer_chip(adapter))) {
                        be_rx_compl_discard(rxo, rxcp);
                        goto loop_continue;
                }
@@ -2405,8 +2442,9 @@ static bool be_process_tx(struct be_adapter *adapter, struct be_tx_obj *txo,
                if (!txcp)
                        break;
                num_wrbs += be_tx_compl_process(adapter, txo,
-                               AMAP_GET_BITS(struct amap_eth_tx_compl,
-                                       wrb_index, txcp));
+                                               AMAP_GET_BITS(struct
+                                                             amap_eth_tx_compl,
+                                                             wrb_index, txcp));
        }
 
        if (work_done) {
@@ -2416,7 +2454,7 @@ static bool be_process_tx(struct be_adapter *adapter, struct be_tx_obj *txo,
                /* As Tx wrbs have been freed up, wake up netdev queue
                 * if it was stopped due to lack of tx wrbs.  */
                if (__netif_subqueue_stopped(adapter->netdev, idx) &&
-                       atomic_read(&txo->q.used) < txo->q.len / 2) {
+                   atomic_read(&txo->q.used) < txo->q.len / 2) {
                        netif_wake_subqueue(adapter->netdev, idx);
                }
 
@@ -2510,9 +2548,9 @@ void be_detect_error(struct be_adapter *adapter)
                sliport_status = ioread32(adapter->db + SLIPORT_STATUS_OFFSET);
                if (sliport_status & SLIPORT_STATUS_ERR_MASK) {
                        sliport_err1 = ioread32(adapter->db +
-                                       SLIPORT_ERROR1_OFFSET);
+                                               SLIPORT_ERROR1_OFFSET);
                        sliport_err2 = ioread32(adapter->db +
-                                       SLIPORT_ERROR2_OFFSET);
+                                               SLIPORT_ERROR2_OFFSET);
                        adapter->hw_error = true;
                        /* Do not log error messages if its a FW reset */
                        if (sliport_err1 == SLIPORT_ERROR_FW_RESET1 &&
@@ -2531,13 +2569,13 @@ void be_detect_error(struct be_adapter *adapter)
                }
        } else {
                pci_read_config_dword(adapter->pdev,
-                               PCICFG_UE_STATUS_LOW, &ue_lo);
+                                     PCICFG_UE_STATUS_LOW, &ue_lo);
                pci_read_config_dword(adapter->pdev,
-                               PCICFG_UE_STATUS_HIGH, &ue_hi);
+                                     PCICFG_UE_STATUS_HIGH, &ue_hi);
                pci_read_config_dword(adapter->pdev,
-                               PCICFG_UE_STATUS_LOW_MASK, &ue_lo_mask);
+                                     PCICFG_UE_STATUS_LOW_MASK, &ue_lo_mask);
                pci_read_config_dword(adapter->pdev,
-                               PCICFG_UE_STATUS_HI_MASK, &ue_hi_mask);
+                                     PCICFG_UE_STATUS_HI_MASK, &ue_hi_mask);
 
                ue_lo = (ue_lo & ~ue_lo_mask);
                ue_hi = (ue_hi & ~ue_hi_mask);
@@ -2624,7 +2662,7 @@ fail:
 }
 
 static inline int be_msix_vec_get(struct be_adapter *adapter,
-                               struct be_eq_obj *eqo)
+                                 struct be_eq_obj *eqo)
 {
        return adapter->msix_entries[eqo->msix_idx].vector;
 }
@@ -2648,7 +2686,7 @@ err_msix:
        for (i--, eqo = &adapter->eq_obj[i]; i >= 0; i--, eqo--)
                free_irq(be_msix_vec_get(adapter, eqo), eqo);
        dev_warn(&adapter->pdev->dev, "MSIX Request IRQ failed - err %d\n",
-               status);
+                status);
        be_msix_disable(adapter);
        return status;
 }
@@ -2774,7 +2812,8 @@ static int be_rx_qs_create(struct be_adapter *adapter)
 {
        struct be_rx_obj *rxo;
        int rc, i, j;
-       u8 rsstable[128];
+       u8 rss_hkey[RSS_HASH_KEY_LEN];
+       struct rss_info *rss = &adapter->rss_info;
 
        for_all_rx_queues(adapter, rxo, i) {
                rc = be_queue_alloc(adapter, &rxo->q, RX_Q_LEN,
@@ -2799,31 +2838,36 @@ static int be_rx_qs_create(struct be_adapter *adapter)
        }
 
        if (be_multi_rxq(adapter)) {
-               for (j = 0; j < 128; j += adapter->num_rx_qs - 1) {
+               for (j = 0; j < RSS_INDIR_TABLE_LEN;
+                       j += adapter->num_rx_qs - 1) {
                        for_all_rss_queues(adapter, rxo, i) {
-                               if ((j + i) >= 128)
+                               if ((j + i) >= RSS_INDIR_TABLE_LEN)
                                        break;
-                               rsstable[j + i] = rxo->rss_id;
+                               rss->rsstable[j + i] = rxo->rss_id;
+                               rss->rss_queue[j + i] = i;
                        }
                }
-               adapter->rss_flags = RSS_ENABLE_TCP_IPV4 | RSS_ENABLE_IPV4 |
-                                       RSS_ENABLE_TCP_IPV6 | RSS_ENABLE_IPV6;
+               rss->rss_flags = RSS_ENABLE_TCP_IPV4 | RSS_ENABLE_IPV4 |
+                       RSS_ENABLE_TCP_IPV6 | RSS_ENABLE_IPV6;
 
                if (!BEx_chip(adapter))
-                       adapter->rss_flags |= RSS_ENABLE_UDP_IPV4 |
-                                               RSS_ENABLE_UDP_IPV6;
+                       rss->rss_flags |= RSS_ENABLE_UDP_IPV4 |
+                               RSS_ENABLE_UDP_IPV6;
        } else {
                /* Disable RSS, if only default RX Q is created */
-               adapter->rss_flags = RSS_ENABLE_NONE;
+               rss->rss_flags = RSS_ENABLE_NONE;
        }
 
-       rc = be_cmd_rss_config(adapter, rsstable, adapter->rss_flags,
-                              128);
+       get_random_bytes(rss_hkey, RSS_HASH_KEY_LEN);
+       rc = be_cmd_rss_config(adapter, rss->rsstable, rss->rss_flags,
+                              128, rss_hkey);
        if (rc) {
-               adapter->rss_flags = RSS_ENABLE_NONE;
+               rss->rss_flags = RSS_ENABLE_NONE;
                return rc;
        }
 
+       memcpy(rss->rss_hkey, rss_hkey, RSS_HASH_KEY_LEN);
+
        /* First time posting */
        for_all_rx_queues(adapter, rxo, i)
                be_post_rx_frags(rxo, GFP_KERNEL);
@@ -2896,7 +2940,8 @@ static int be_setup_wol(struct be_adapter *adapter, bool enable)
 
        if (enable) {
                status = pci_write_config_dword(adapter->pdev,
-                       PCICFG_PM_CONTROL_OFFSET, PCICFG_PM_CONTROL_MASK);
+                                               PCICFG_PM_CONTROL_OFFSET,
+                                               PCICFG_PM_CONTROL_MASK);
                if (status) {
                        dev_err(&adapter->pdev->dev,
                                "Could not enable Wake-on-lan\n");
@@ -2905,7 +2950,8 @@ static int be_setup_wol(struct be_adapter *adapter, bool enable)
                        return status;
                }
                status = be_cmd_enable_magic_wol(adapter,
-                               adapter->netdev->dev_addr, &cmd);
+                                                adapter->netdev->dev_addr,
+                                                &cmd);
                pci_enable_wake(adapter->pdev, PCI_D3hot, 1);
                pci_enable_wake(adapter->pdev, PCI_D3cold, 1);
        } else {
@@ -2944,7 +2990,8 @@ static int be_vf_eth_addr_config(struct be_adapter *adapter)
 
                if (status)
                        dev_err(&adapter->pdev->dev,
-                       "Mac address assignment failed for VF %d\n", vf);
+                               "Mac address assignment failed for VF %d\n",
+                               vf);
                else
                        memcpy(vf_cfg->mac_addr, mac, ETH_ALEN);
 
@@ -3086,9 +3133,11 @@ static int be_vfs_if_create(struct be_adapter *adapter)
 
                /* If a FW profile exists, then cap_flags are updated */
                en_flags = cap_flags & (BE_IF_FLAGS_UNTAGGED |
-                          BE_IF_FLAGS_BROADCAST | BE_IF_FLAGS_MULTICAST);
-               status = be_cmd_if_create(adapter, cap_flags, en_flags,
-                                         &vf_cfg->if_handle, vf + 1);
+                                       BE_IF_FLAGS_BROADCAST |
+                                       BE_IF_FLAGS_MULTICAST);
+               status =
+                   be_cmd_if_create(adapter, cap_flags, en_flags,
+                                    &vf_cfg->if_handle, vf + 1);
                if (status)
                        goto err;
        }
@@ -3119,7 +3168,6 @@ static int be_vf_setup(struct be_adapter *adapter)
        struct be_vf_cfg *vf_cfg;
        int status, old_vfs, vf;
        u32 privileges;
-       u16 lnk_speed;
 
        old_vfs = pci_num_vf(adapter->pdev);
        if (old_vfs) {
@@ -3175,16 +3223,9 @@ static int be_vf_setup(struct be_adapter *adapter)
                                         vf);
                }
 
-               /* BE3 FW, by default, caps VF TX-rate to 100mbps.
-                * Allow full available bandwidth
-                */
-               if (BE3_chip(adapter) && !old_vfs)
-                       be_cmd_config_qos(adapter, 1000, vf + 1);
-
-               status = be_cmd_link_status_query(adapter, &lnk_speed,
-                                                 NULL, vf + 1);
-               if (!status)
-                       vf_cfg->tx_rate = lnk_speed;
+               /* Allow full available bandwidth */
+               if (!old_vfs)
+                       be_cmd_config_qos(adapter, 0, 0, vf + 1);
 
                if (!old_vfs) {
                        be_cmd_enable_vf(adapter, vf + 1);
@@ -3590,35 +3631,7 @@ static void be_netpoll(struct net_device *netdev)
 }
 #endif
 
-#define FW_FILE_HDR_SIGN       "ServerEngines Corp. "
-static char flash_cookie[2][16] =      {"*** SE FLAS", "H DIRECTORY *** "};
-
-static bool be_flash_redboot(struct be_adapter *adapter,
-                       const u8 *p, u32 img_start, int image_size,
-                       int hdr_size)
-{
-       u32 crc_offset;
-       u8 flashed_crc[4];
-       int status;
-
-       crc_offset = hdr_size + img_start + image_size - 4;
-
-       p += crc_offset;
-
-       status = be_cmd_get_flash_crc(adapter, flashed_crc,
-                       (image_size - 4));
-       if (status) {
-               dev_err(&adapter->pdev->dev,
-               "could not get crc from flash, not flashing redboot\n");
-               return false;
-       }
-
-       /*update redboot only if crc does not match*/
-       if (!memcmp(flashed_crc, p, 4))
-               return false;
-       else
-               return true;
-}
+static char flash_cookie[2][16] = {"*** SE FLAS", "H DIRECTORY *** "};
 
 static bool phy_flashing_required(struct be_adapter *adapter)
 {
@@ -3649,8 +3662,8 @@ static bool is_comp_in_ufi(struct be_adapter *adapter,
 }
 
 static struct flash_section_info *get_fsec_info(struct be_adapter *adapter,
-                                        int header_size,
-                                        const struct firmware *fw)
+                                               int header_size,
+                                               const struct firmware *fw)
 {
        struct flash_section_info *fsec = NULL;
        const u8 *p = fw->data;
@@ -3665,12 +3678,35 @@ static struct flash_section_info *get_fsec_info(struct be_adapter *adapter,
        return NULL;
 }
 
+static int be_check_flash_crc(struct be_adapter *adapter, const u8 *p,
+                             u32 img_offset, u32 img_size, int hdr_size,
+                             u16 img_optype, bool *crc_match)
+{
+       u32 crc_offset;
+       int status;
+       u8 crc[4];
+
+       status = be_cmd_get_flash_crc(adapter, crc, img_optype, img_size - 4);
+       if (status)
+               return status;
+
+       crc_offset = hdr_size + img_offset + img_size - 4;
+
+       /* Skip flashing, if crc of flashed region matches */
+       if (!memcmp(crc, p + crc_offset, 4))
+               *crc_match = true;
+       else
+               *crc_match = false;
+
+       return status;
+}
+
 static int be_flash(struct be_adapter *adapter, const u8 *img,
-               struct be_dma_mem *flash_cmd, int optype, int img_size)
+                   struct be_dma_mem *flash_cmd, int optype, int img_size)
 {
-       u32 total_bytes = 0, flash_op, num_bytes = 0;
-       int status = 0;
        struct be_cmd_write_flashrom *req = flash_cmd->va;
+       u32 total_bytes, flash_op, num_bytes;
+       int status;
 
        total_bytes = img_size;
        while (total_bytes) {
@@ -3693,32 +3729,28 @@ static int be_flash(struct be_adapter *adapter, const u8 *img,
                memcpy(req->data_buf, img, num_bytes);
                img += num_bytes;
                status = be_cmd_write_flashrom(adapter, flash_cmd, optype,
-                                               flash_op, num_bytes);
-               if (status) {
-                       if (status == ILLEGAL_IOCTL_REQ &&
-                           optype == OPTYPE_PHY_FW)
-                               break;
-                       dev_err(&adapter->pdev->dev,
-                               "cmd to write to flash rom failed.\n");
+                                              flash_op, num_bytes);
+               if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST &&
+                   optype == OPTYPE_PHY_FW)
+                       break;
+               else if (status)
                        return status;
-               }
        }
        return 0;
 }
 
 /* For BE2, BE3 and BE3-R */
 static int be_flash_BEx(struct be_adapter *adapter,
-                        const struct firmware *fw,
-                        struct be_dma_mem *flash_cmd,
-                        int num_of_images)
-
+                       const struct firmware *fw,
+                       struct be_dma_mem *flash_cmd, int num_of_images)
 {
-       int status = 0, i, filehdr_size = 0;
        int img_hdrs_size = (num_of_images * sizeof(struct image_hdr));
-       const u8 *p = fw->data;
-       const struct flash_comp *pflashcomp;
-       int num_comp, redboot;
+       struct device *dev = &adapter->pdev->dev;
        struct flash_section_info *fsec = NULL;
+       int status, i, filehdr_size, num_comp;
+       const struct flash_comp *pflashcomp;
+       bool crc_match;
+       const u8 *p;
 
        struct flash_comp gen3_flash_types[] = {
                { FLASH_iSCSI_PRIMARY_IMAGE_START_g3, OPTYPE_ISCSI_ACTIVE,
@@ -3775,8 +3807,7 @@ static int be_flash_BEx(struct be_adapter *adapter,
        /* Get flash section info*/
        fsec = get_fsec_info(adapter, filehdr_size + img_hdrs_size, fw);
        if (!fsec) {
-               dev_err(&adapter->pdev->dev,
-                       "Invalid Cookie. UFI corrupted ?\n");
+               dev_err(dev, "Invalid Cookie. FW image may be corrupted\n");
                return -1;
        }
        for (i = 0; i < num_comp; i++) {
@@ -3792,23 +3823,32 @@ static int be_flash_BEx(struct be_adapter *adapter,
                                continue;
 
                if (pflashcomp[i].optype == OPTYPE_REDBOOT) {
-                       redboot = be_flash_redboot(adapter, fw->data,
-                               pflashcomp[i].offset, pflashcomp[i].size,
-                               filehdr_size + img_hdrs_size);
-                       if (!redboot)
+                       status = be_check_flash_crc(adapter, fw->data,
+                                                   pflashcomp[i].offset,
+                                                   pflashcomp[i].size,
+                                                   filehdr_size +
+                                                   img_hdrs_size,
+                                                   OPTYPE_REDBOOT, &crc_match);
+                       if (status) {
+                               dev_err(dev,
+                                       "Could not get CRC for 0x%x region\n",
+                                       pflashcomp[i].optype);
+                               continue;
+                       }
+
+                       if (crc_match)
                                continue;
                }
 
-               p = fw->data;
-               p += filehdr_size + pflashcomp[i].offset + img_hdrs_size;
+               p = fw->data + filehdr_size + pflashcomp[i].offset +
+                       img_hdrs_size;
                if (p + pflashcomp[i].size > fw->data + fw->size)
                        return -1;
 
                status = be_flash(adapter, p, flash_cmd, pflashcomp[i].optype,
-                                       pflashcomp[i].size);
+                                 pflashcomp[i].size);
                if (status) {
-                       dev_err(&adapter->pdev->dev,
-                               "Flashing section type %d failed.\n",
+                       dev_err(dev, "Flashing section type 0x%x failed\n",
                                pflashcomp[i].img_type);
                        return status;
                }
@@ -3816,80 +3856,142 @@ static int be_flash_BEx(struct be_adapter *adapter,
        return 0;
 }
 
+static u16 be_get_img_optype(struct flash_section_entry fsec_entry)
+{
+       u32 img_type = le32_to_cpu(fsec_entry.type);
+       u16 img_optype = le16_to_cpu(fsec_entry.optype);
+
+       if (img_optype != 0xFFFF)
+               return img_optype;
+
+       switch (img_type) {
+       case IMAGE_FIRMWARE_iSCSI:
+               img_optype = OPTYPE_ISCSI_ACTIVE;
+               break;
+       case IMAGE_BOOT_CODE:
+               img_optype = OPTYPE_REDBOOT;
+               break;
+       case IMAGE_OPTION_ROM_ISCSI:
+               img_optype = OPTYPE_BIOS;
+               break;
+       case IMAGE_OPTION_ROM_PXE:
+               img_optype = OPTYPE_PXE_BIOS;
+               break;
+       case IMAGE_OPTION_ROM_FCoE:
+               img_optype = OPTYPE_FCOE_BIOS;
+               break;
+       case IMAGE_FIRMWARE_BACKUP_iSCSI:
+               img_optype = OPTYPE_ISCSI_BACKUP;
+               break;
+       case IMAGE_NCSI:
+               img_optype = OPTYPE_NCSI_FW;
+               break;
+       case IMAGE_FLASHISM_JUMPVECTOR:
+               img_optype = OPTYPE_FLASHISM_JUMPVECTOR;
+               break;
+       case IMAGE_FIRMWARE_PHY:
+               img_optype = OPTYPE_SH_PHY_FW;
+               break;
+       case IMAGE_REDBOOT_DIR:
+               img_optype = OPTYPE_REDBOOT_DIR;
+               break;
+       case IMAGE_REDBOOT_CONFIG:
+               img_optype = OPTYPE_REDBOOT_CONFIG;
+               break;
+       case IMAGE_UFI_DIR:
+               img_optype = OPTYPE_UFI_DIR;
+               break;
+       default:
+               break;
+       }
+
+       return img_optype;
+}
+
 static int be_flash_skyhawk(struct be_adapter *adapter,
-               const struct firmware *fw,
-               struct be_dma_mem *flash_cmd, int num_of_images)
+                           const struct firmware *fw,
+                           struct be_dma_mem *flash_cmd, int num_of_images)
 {
-       int status = 0, i, filehdr_size = 0;
-       int img_offset, img_size, img_optype, redboot;
        int img_hdrs_size = num_of_images * sizeof(struct image_hdr);
-       const u8 *p = fw->data;
+       struct device *dev = &adapter->pdev->dev;
        struct flash_section_info *fsec = NULL;
+       u32 img_offset, img_size, img_type;
+       int status, i, filehdr_size;
+       bool crc_match, old_fw_img;
+       u16 img_optype;
+       const u8 *p;
 
        filehdr_size = sizeof(struct flash_file_hdr_g3);
        fsec = get_fsec_info(adapter, filehdr_size + img_hdrs_size, fw);
        if (!fsec) {
-               dev_err(&adapter->pdev->dev,
-                       "Invalid Cookie. UFI corrupted ?\n");
+               dev_err(dev, "Invalid Cookie. FW image may be corrupted\n");
                return -1;
        }
 
        for (i = 0; i < le32_to_cpu(fsec->fsec_hdr.num_images); i++) {
                img_offset = le32_to_cpu(fsec->fsec_entry[i].offset);
                img_size   = le32_to_cpu(fsec->fsec_entry[i].pad_size);
+               img_type   = le32_to_cpu(fsec->fsec_entry[i].type);
+               img_optype = be_get_img_optype(fsec->fsec_entry[i]);
+               old_fw_img = fsec->fsec_entry[i].optype == 0xFFFF;
 
-               switch (le32_to_cpu(fsec->fsec_entry[i].type)) {
-               case IMAGE_FIRMWARE_iSCSI:
-                       img_optype = OPTYPE_ISCSI_ACTIVE;
-                       break;
-               case IMAGE_BOOT_CODE:
-                       img_optype = OPTYPE_REDBOOT;
-                       break;
-               case IMAGE_OPTION_ROM_ISCSI:
-                       img_optype = OPTYPE_BIOS;
-                       break;
-               case IMAGE_OPTION_ROM_PXE:
-                       img_optype = OPTYPE_PXE_BIOS;
-                       break;
-               case IMAGE_OPTION_ROM_FCoE:
-                       img_optype = OPTYPE_FCOE_BIOS;
-                       break;
-               case IMAGE_FIRMWARE_BACKUP_iSCSI:
-                       img_optype = OPTYPE_ISCSI_BACKUP;
-                       break;
-               case IMAGE_NCSI:
-                       img_optype = OPTYPE_NCSI_FW;
-                       break;
-               default:
+               if (img_optype == 0xFFFF)
                        continue;
+               /* Don't bother verifying CRC if an old FW image is being
+                * flashed
+                */
+               if (old_fw_img)
+                       goto flash;
+
+               status = be_check_flash_crc(adapter, fw->data, img_offset,
+                                           img_size, filehdr_size +
+                                           img_hdrs_size, img_optype,
+                                           &crc_match);
+               /* The current FW image on the card does not recognize the new
+                * FLASH op_type. The FW download is partially complete.
+                * Reboot the server now to enable FW image to recognize the
+                * new FLASH op_type. To complete the remaining process,
+                * download the same FW again after the reboot.
+                */
+               if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST ||
+                   base_status(status) == MCC_STATUS_ILLEGAL_FIELD) {
+                       dev_err(dev, "Flash incomplete. Reset the server\n");
+                       dev_err(dev, "Download FW image again after reset\n");
+                       return -EAGAIN;
+               } else if (status) {
+                       dev_err(dev, "Could not get CRC for 0x%x region\n",
+                               img_optype);
+                       return -EFAULT;
                }
 
-               if (img_optype == OPTYPE_REDBOOT) {
-                       redboot = be_flash_redboot(adapter, fw->data,
-                                       img_offset, img_size,
-                                       filehdr_size + img_hdrs_size);
-                       if (!redboot)
-                               continue;
-               }
+               if (crc_match)
+                       continue;
 
-               p = fw->data;
-               p += filehdr_size + img_offset + img_hdrs_size;
+flash:
+               p = fw->data + filehdr_size + img_offset + img_hdrs_size;
                if (p + img_size > fw->data + fw->size)
                        return -1;
 
                status = be_flash(adapter, p, flash_cmd, img_optype, img_size);
-               if (status) {
-                       dev_err(&adapter->pdev->dev,
-                               "Flashing section type %d failed.\n",
-                               fsec->fsec_entry[i].type);
-                       return status;
+               /* For old FW images ignore ILLEGAL_FIELD error or errors on
+                * UFI_DIR region
+                */
+               if (old_fw_img &&
+                   (base_status(status) == MCC_STATUS_ILLEGAL_FIELD ||
+                    (img_optype == OPTYPE_UFI_DIR &&
+                     base_status(status) == MCC_STATUS_FAILED))) {
+                       continue;
+               } else if (status) {
+                       dev_err(dev, "Flashing section type 0x%x failed\n",
+                               img_type);
+                       return -EFAULT;
                }
        }
        return 0;
 }
 
 static int lancer_fw_download(struct be_adapter *adapter,
-                               const struct firmware *fw)
+                             const struct firmware *fw)
 {
 #define LANCER_FW_DOWNLOAD_CHUNK      (32 * 1024)
 #define LANCER_FW_DOWNLOAD_LOCATION   "/prg"
@@ -3955,7 +4057,7 @@ static int lancer_fw_download(struct be_adapter *adapter,
        }
 
        dma_free_coherent(&adapter->pdev->dev, flash_cmd.size, flash_cmd.va,
-                               flash_cmd.dma);
+                         flash_cmd.dma);
        if (status) {
                dev_err(&adapter->pdev->dev,
                        "Firmware load error. "
@@ -3976,9 +4078,8 @@ static int lancer_fw_download(struct be_adapter *adapter,
                        goto lancer_fw_exit;
                }
        } else if (change_status != LANCER_NO_RESET_NEEDED) {
-                       dev_err(&adapter->pdev->dev,
-                               "System reboot required for new FW"
-                               " to be active\n");
+               dev_err(&adapter->pdev->dev,
+                       "System reboot required for new FW to be active\n");
        }
 
        dev_info(&adapter->pdev->dev, "Firmware flashed successfully\n");
@@ -4042,7 +4143,7 @@ static int be_fw_download(struct be_adapter *adapter, const struct firmware* fw)
                        switch (ufi_type) {
                        case UFI_TYPE4:
                                status = be_flash_skyhawk(adapter, fw,
-                                                       &flash_cmd, num_imgs);
+                                                         &flash_cmd, num_imgs);
                                break;
                        case UFI_TYPE3R:
                                status = be_flash_BEx(adapter, fw, &flash_cmd,
@@ -4112,8 +4213,7 @@ fw_exit:
        return status;
 }
 
-static int be_ndo_bridge_setlink(struct net_device *dev,
-                                   struct nlmsghdr *nlh)
+static int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh)
 {
        struct be_adapter *adapter = netdev_priv(dev);
        struct nlattr *attr, *br_spec;
@@ -4155,8 +4255,7 @@ err:
 }
 
 static int be_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
-                                   struct net_device *dev,
-                                   u32 filter_mask)
+                                struct net_device *dev, u32 filter_mask)
 {
        struct be_adapter *adapter = netdev_priv(dev);
        int status = 0;
@@ -4254,7 +4353,7 @@ static const struct net_device_ops be_netdev_ops = {
        .ndo_vlan_rx_kill_vid   = be_vlan_rem_vid,
        .ndo_set_vf_mac         = be_set_vf_mac,
        .ndo_set_vf_vlan        = be_set_vf_vlan,
-       .ndo_set_vf_tx_rate     = be_set_vf_tx_rate,
+       .ndo_set_vf_rate        = be_set_vf_tx_rate,
        .ndo_get_vf_config      = be_get_vf_config,
        .ndo_set_vf_link_state  = be_set_vf_link_state,
 #ifdef CONFIG_NET_POLL_CONTROLLER
@@ -4301,7 +4400,7 @@ static void be_netdev_init(struct net_device *netdev)
 
        netdev->netdev_ops = &be_netdev_ops;
 
-       SET_ETHTOOL_OPS(netdev, &be_ethtool_ops);
+       netdev->ethtool_ops = &be_ethtool_ops;
 }
 
 static void be_unmap_pci_bars(struct be_adapter *adapter)
@@ -4870,7 +4969,7 @@ static void be_shutdown(struct pci_dev *pdev)
 }
 
 static pci_ers_result_t be_eeh_err_detected(struct pci_dev *pdev,
-                               pci_channel_state_t state)
+                                           pci_channel_state_t state)
 {
        struct be_adapter *adapter = pci_get_drvdata(pdev);
        struct net_device *netdev =  adapter->netdev;
index 8b70ca7e342b6f16a8d6624293ee1b940bcff3d5..f3658bdb64cc4265de972cfc2bbd25157e95a680 100644 (file)
@@ -769,11 +769,6 @@ static int ethoc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
        return phy_mii_ioctl(phy, ifr, cmd);
 }
 
-static int ethoc_config(struct net_device *dev, struct ifmap *map)
-{
-       return -ENOSYS;
-}
-
 static void ethoc_do_set_mac_address(struct net_device *dev)
 {
        struct ethoc *priv = netdev_priv(dev);
@@ -995,7 +990,6 @@ static const struct net_device_ops ethoc_netdev_ops = {
        .ndo_open = ethoc_open,
        .ndo_stop = ethoc_stop,
        .ndo_do_ioctl = ethoc_ioctl,
-       .ndo_set_config = ethoc_config,
        .ndo_set_mac_address = ethoc_set_mac_address,
        .ndo_set_rx_mode = ethoc_set_multicast_list,
        .ndo_change_mtu = ethoc_change_mtu,
index 68069eabc4f855c0636d12b69854e44919ffb863..c77fa4a6984458648a97960deeef2d0a54b6637c 100644 (file)
@@ -1210,7 +1210,7 @@ static int ftgmac100_probe(struct platform_device *pdev)
 
        SET_NETDEV_DEV(netdev, &pdev->dev);
 
-       SET_ETHTOOL_OPS(netdev, &ftgmac100_ethtool_ops);
+       netdev->ethtool_ops = &ftgmac100_ethtool_ops;
        netdev->netdev_ops = &ftgmac100_netdev_ops;
        netdev->features = NETIF_F_IP_CSUM | NETIF_F_GRO;
 
index 8be5b40c0a121331f1d1bbc0712499eb4bec8160..4ff1adc6bfcab9132b1dbf673626f868430bbcc3 100644 (file)
@@ -1085,7 +1085,7 @@ static int ftmac100_probe(struct platform_device *pdev)
        }
 
        SET_NETDEV_DEV(netdev, &pdev->dev);
-       SET_ETHTOOL_OPS(netdev, &ftmac100_ethtool_ops);
+       netdev->ethtool_ops = &ftmac100_ethtool_ops;
        netdev->netdev_ops = &ftmac100_netdev_ops;
 
        platform_set_drvdata(pdev, netdev);
index 6048dc8604ee6139235f45f2736d9cd5b385dfa0..270308315d437e8ace4dc8da93af7789b7a499f9 100644 (file)
@@ -67,6 +67,7 @@ config FSL_XGMAC_MDIO
        tristate "Freescale XGMAC MDIO"
        depends on FSL_SOC
        select PHYLIB
+       select OF_MDIO
        ---help---
          This driver supports the MDIO bus on the Fman 10G Ethernet MACs.
 
index 3b8d6d19ff0595885570713d7f866c732695a0db..671d080105a7e08c5e20456a5ad38b29f6704e19 100644 (file)
@@ -221,7 +221,7 @@ struct bufdesc_ex {
 #define BD_ENET_TX_RCMASK       ((ushort)0x003c)
 #define BD_ENET_TX_UN           ((ushort)0x0002)
 #define BD_ENET_TX_CSL          ((ushort)0x0001)
-#define BD_ENET_TX_STATS        ((ushort)0x03ff)        /* All status bits */
+#define BD_ENET_TX_STATS        ((ushort)0x0fff)        /* All status bits */
 
 /*enhanced buffer descriptor control/status used by Ethernet transmit*/
 #define BD_ENET_TX_INT          0x40000000
@@ -246,8 +246,8 @@ struct bufdesc_ex {
 #define RX_RING_SIZE           (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES)
 #define FEC_ENET_TX_FRSIZE     2048
 #define FEC_ENET_TX_FRPPG      (PAGE_SIZE / FEC_ENET_TX_FRSIZE)
-#define TX_RING_SIZE           16      /* Must be power of two */
-#define TX_RING_MOD_MASK       15      /*   for this to work */
+#define TX_RING_SIZE           512     /* Must be power of two */
+#define TX_RING_MOD_MASK       511     /*   for this to work */
 
 #define BD_ENET_RX_INT          0x00800000
 #define BD_ENET_RX_PTP          ((ushort)0x0400)
@@ -296,8 +296,15 @@ struct fec_enet_private {
        /* The ring entries to be free()ed */
        struct bufdesc  *dirty_tx;
 
+       unsigned short bufdesc_size;
        unsigned short tx_ring_size;
        unsigned short rx_ring_size;
+       unsigned short tx_stop_threshold;
+       unsigned short tx_wake_threshold;
+
+       /* Software TSO */
+       char *tso_hdrs;
+       dma_addr_t tso_hdrs_dma;
 
        struct  platform_device *pdev;
 
index 8d69e439f0c518d4b3e46c9ae21d85e4013b7e06..38d9d276ab8b8c006fe13f1aa76eac2e55a2d775 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/in.h>
 #include <linux/ip.h>
 #include <net/ip.h>
+#include <net/tso.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
 #include <linux/icmp.h>
@@ -54,6 +55,7 @@
 #include <linux/of_net.h>
 #include <linux/regulator/consumer.h>
 #include <linux/if_vlan.h>
+#include <linux/pinctrl/consumer.h>
 
 #include <asm/cacheflush.h>
 
@@ -172,10 +174,6 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
 #endif
 #endif /* CONFIG_M5272 */
 
-#if (((RX_RING_SIZE + TX_RING_SIZE) * 32) > PAGE_SIZE)
-#error "FEC: descriptor ring size constants too large"
-#endif
-
 /* Interrupt events/masks. */
 #define FEC_ENET_HBERR ((uint)0x80000000)      /* Heartbeat error */
 #define FEC_ENET_BABR  ((uint)0x40000000)      /* Babbling receiver */
@@ -231,6 +229,15 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
 #define FEC_PAUSE_FLAG_AUTONEG 0x1
 #define FEC_PAUSE_FLAG_ENABLE  0x2
 
+#define TSO_HEADER_SIZE                128
+/* Max number of allowed TCP segments for software TSO */
+#define FEC_MAX_TSO_SEGS       100
+#define FEC_MAX_SKB_DESCS      (FEC_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS)
+
+#define IS_TSO_HEADER(txq, addr) \
+       ((addr >= txq->tso_hdrs_dma) && \
+       (addr < txq->tso_hdrs_dma + txq->tx_ring_size * TSO_HEADER_SIZE))
+
 static int mii_cnt;
 
 static inline
@@ -286,6 +293,22 @@ struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, struct fec_enet_priva
                return (new_bd < base) ? (new_bd + ring_size) : new_bd;
 }
 
+static int fec_enet_get_bd_index(struct bufdesc *base, struct bufdesc *bdp,
+                               struct fec_enet_private *fep)
+{
+       return ((const char *)bdp - (const char *)base) / fep->bufdesc_size;
+}
+
+static int fec_enet_get_free_txdesc_num(struct fec_enet_private *fep)
+{
+       int entries;
+
+       entries = ((const char *)fep->dirty_tx -
+                       (const char *)fep->cur_tx) / fep->bufdesc_size - 1;
+
+       return entries > 0 ? entries : entries + fep->tx_ring_size;
+}
+
 static void *swap_buffer(void *bufaddr, int len)
 {
        int i;
@@ -307,33 +330,133 @@ fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev)
        if (unlikely(skb_cow_head(skb, 0)))
                return -1;
 
+       ip_hdr(skb)->check = 0;
        *(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0;
 
        return 0;
 }
 
-static netdev_tx_t
-fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+static void
+fec_enet_submit_work(struct bufdesc *bdp, struct fec_enet_private *fep)
+{
+       const struct platform_device_id *id_entry =
+                               platform_get_device_id(fep->pdev);
+       struct bufdesc *bdp_pre;
+
+       bdp_pre = fec_enet_get_prevdesc(bdp, fep);
+       if ((id_entry->driver_data & FEC_QUIRK_ERR006358) &&
+           !(bdp_pre->cbd_sc & BD_ENET_TX_READY)) {
+               fep->delay_work.trig_tx = true;
+               schedule_delayed_work(&(fep->delay_work.delay_work),
+                                       msecs_to_jiffies(1));
+       }
+}
+
+static int
+fec_enet_txq_submit_frag_skb(struct sk_buff *skb, struct net_device *ndev)
 {
        struct fec_enet_private *fep = netdev_priv(ndev);
        const struct platform_device_id *id_entry =
                                platform_get_device_id(fep->pdev);
-       struct bufdesc *bdp, *bdp_pre;
-       void *bufaddr;
-       unsigned short  status;
+       struct bufdesc *bdp = fep->cur_tx;
+       struct bufdesc_ex *ebdp;
+       int nr_frags = skb_shinfo(skb)->nr_frags;
+       int frag, frag_len;
+       unsigned short status;
+       unsigned int estatus = 0;
+       skb_frag_t *this_frag;
        unsigned int index;
+       void *bufaddr;
+       int i;
 
-       /* Fill in a Tx ring entry */
+       for (frag = 0; frag < nr_frags; frag++) {
+               this_frag = &skb_shinfo(skb)->frags[frag];
+               bdp = fec_enet_get_nextdesc(bdp, fep);
+               ebdp = (struct bufdesc_ex *)bdp;
+
+               status = bdp->cbd_sc;
+               status &= ~BD_ENET_TX_STATS;
+               status |= (BD_ENET_TX_TC | BD_ENET_TX_READY);
+               frag_len = skb_shinfo(skb)->frags[frag].size;
+
+               /* Handle the last BD specially */
+               if (frag == nr_frags - 1) {
+                       status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST);
+                       if (fep->bufdesc_ex) {
+                               estatus |= BD_ENET_TX_INT;
+                               if (unlikely(skb_shinfo(skb)->tx_flags &
+                                       SKBTX_HW_TSTAMP && fep->hwts_tx_en))
+                                       estatus |= BD_ENET_TX_TS;
+                       }
+               }
+
+               if (fep->bufdesc_ex) {
+                       if (skb->ip_summed == CHECKSUM_PARTIAL)
+                               estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
+                       ebdp->cbd_bdu = 0;
+                       ebdp->cbd_esc = estatus;
+               }
+
+               bufaddr = page_address(this_frag->page.p) + this_frag->page_offset;
+
+               index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
+               if (((unsigned long) bufaddr) & FEC_ALIGNMENT ||
+                       id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
+                       memcpy(fep->tx_bounce[index], bufaddr, frag_len);
+                       bufaddr = fep->tx_bounce[index];
+
+                       if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
+                               swap_buffer(bufaddr, frag_len);
+               }
+
+               bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, bufaddr,
+                                               frag_len, DMA_TO_DEVICE);
+               if (dma_mapping_error(&fep->pdev->dev, bdp->cbd_bufaddr)) {
+                       dev_kfree_skb_any(skb);
+                       if (net_ratelimit())
+                               netdev_err(ndev, "Tx DMA memory map failed\n");
+                       goto dma_mapping_error;
+               }
+
+               bdp->cbd_datlen = frag_len;
+               bdp->cbd_sc = status;
+       }
+
+       fep->cur_tx = bdp;
+
+       return 0;
+
+dma_mapping_error:
        bdp = fep->cur_tx;
+       for (i = 0; i < frag; i++) {
+               bdp = fec_enet_get_nextdesc(bdp, fep);
+               dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
+                               bdp->cbd_datlen, DMA_TO_DEVICE);
+       }
+       return NETDEV_TX_OK;
+}
 
-       status = bdp->cbd_sc;
+static int fec_enet_txq_submit_skb(struct sk_buff *skb, struct net_device *ndev)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       const struct platform_device_id *id_entry =
+                               platform_get_device_id(fep->pdev);
+       int nr_frags = skb_shinfo(skb)->nr_frags;
+       struct bufdesc *bdp, *last_bdp;
+       void *bufaddr;
+       unsigned short status;
+       unsigned short buflen;
+       unsigned int estatus = 0;
+       unsigned int index;
+       int entries_free;
+       int ret;
 
-       if (status & BD_ENET_TX_READY) {
-               /* Ooops.  All transmit buffers are full.  Bail out.
-                * This should not happen, since ndev->tbusy should be set.
-                */
-               netdev_err(ndev, "tx queue full!\n");
-               return NETDEV_TX_BUSY;
+       entries_free = fec_enet_get_free_txdesc_num(fep);
+       if (entries_free < MAX_SKB_FRAGS + 1) {
+               dev_kfree_skb_any(skb);
+               if (net_ratelimit())
+                       netdev_err(ndev, "NOT enough BD for SG!\n");
+               return NETDEV_TX_OK;
        }
 
        /* Protocol checksum off-load for TCP and UDP. */
@@ -342,102 +465,300 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
                return NETDEV_TX_OK;
        }
 
-       /* Clear all of the status flags */
+       /* Fill in a Tx ring entry */
+       bdp = fep->cur_tx;
+       status = bdp->cbd_sc;
        status &= ~BD_ENET_TX_STATS;
 
        /* Set buffer length and buffer pointer */
        bufaddr = skb->data;
-       bdp->cbd_datlen = skb->len;
-
-       /*
-        * On some FEC implementations data must be aligned on
-        * 4-byte boundaries. Use bounce buffers to copy data
-        * and get it aligned. Ugh.
-        */
-       if (fep->bufdesc_ex)
-               index = (struct bufdesc_ex *)bdp -
-                       (struct bufdesc_ex *)fep->tx_bd_base;
-       else
-               index = bdp - fep->tx_bd_base;
+       buflen = skb_headlen(skb);
 
-       if (((unsigned long) bufaddr) & FEC_ALIGNMENT) {
-               memcpy(fep->tx_bounce[index], skb->data, skb->len);
+       index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
+       if (((unsigned long) bufaddr) & FEC_ALIGNMENT ||
+               id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
+               memcpy(fep->tx_bounce[index], skb->data, buflen);
                bufaddr = fep->tx_bounce[index];
-       }
 
-       /*
-        * Some design made an incorrect assumption on endian mode of
-        * the system that it's running on. As the result, driver has to
-        * swap every frame going to and coming from the controller.
-        */
-       if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
-               swap_buffer(bufaddr, skb->len);
-
-       /* Save skb pointer */
-       fep->tx_skbuff[index] = skb;
+               if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
+                       swap_buffer(bufaddr, buflen);
+       }
 
        /* Push the data cache so the CPM does not get stale memory
         * data.
         */
        bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, bufaddr,
-                       skb->len, DMA_TO_DEVICE);
+                                       buflen, DMA_TO_DEVICE);
        if (dma_mapping_error(&fep->pdev->dev, bdp->cbd_bufaddr)) {
-               bdp->cbd_bufaddr = 0;
-               fep->tx_skbuff[index] = NULL;
                dev_kfree_skb_any(skb);
                if (net_ratelimit())
                        netdev_err(ndev, "Tx DMA memory map failed\n");
                return NETDEV_TX_OK;
        }
 
+       if (nr_frags) {
+               ret = fec_enet_txq_submit_frag_skb(skb, ndev);
+               if (ret)
+                       return ret;
+       } else {
+               status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST);
+               if (fep->bufdesc_ex) {
+                       estatus = BD_ENET_TX_INT;
+                       if (unlikely(skb_shinfo(skb)->tx_flags &
+                               SKBTX_HW_TSTAMP && fep->hwts_tx_en))
+                               estatus |= BD_ENET_TX_TS;
+               }
+       }
+
        if (fep->bufdesc_ex) {
 
                struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
-               ebdp->cbd_bdu = 0;
+
                if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
-                       fep->hwts_tx_en)) {
-                       ebdp->cbd_esc = (BD_ENET_TX_TS | BD_ENET_TX_INT);
+                       fep->hwts_tx_en))
                        skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
-               } else {
-                       ebdp->cbd_esc = BD_ENET_TX_INT;
 
-                       /* Enable protocol checksum flags
-                        * We do not bother with the IP Checksum bits as they
-                        * are done by the kernel
-                        */
-                       if (skb->ip_summed == CHECKSUM_PARTIAL)
-                               ebdp->cbd_esc |= BD_ENET_TX_PINS;
-               }
+               if (skb->ip_summed == CHECKSUM_PARTIAL)
+                       estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
+
+               ebdp->cbd_bdu = 0;
+               ebdp->cbd_esc = estatus;
        }
 
+       last_bdp = fep->cur_tx;
+       index = fec_enet_get_bd_index(fep->tx_bd_base, last_bdp, fep);
+       /* Save skb pointer */
+       fep->tx_skbuff[index] = skb;
+
+       bdp->cbd_datlen = buflen;
+
        /* Send it on its way.  Tell FEC it's ready, interrupt when done,
         * it's the last BD of the frame, and to put the CRC on the end.
         */
-       status |= (BD_ENET_TX_READY | BD_ENET_TX_INTR
-                       | BD_ENET_TX_LAST | BD_ENET_TX_TC);
+       status |= (BD_ENET_TX_READY | BD_ENET_TX_TC);
        bdp->cbd_sc = status;
 
-       bdp_pre = fec_enet_get_prevdesc(bdp, fep);
-       if ((id_entry->driver_data & FEC_QUIRK_ERR006358) &&
-           !(bdp_pre->cbd_sc & BD_ENET_TX_READY)) {
-               fep->delay_work.trig_tx = true;
-               schedule_delayed_work(&(fep->delay_work.delay_work),
-                                       msecs_to_jiffies(1));
-       }
+       fec_enet_submit_work(bdp, fep);
 
        /* If this was the last BD in the ring, start at the beginning again. */
-       bdp = fec_enet_get_nextdesc(bdp, fep);
+       bdp = fec_enet_get_nextdesc(last_bdp, fep);
 
        skb_tx_timestamp(skb);
 
        fep->cur_tx = bdp;
 
-       if (fep->cur_tx == fep->dirty_tx)
-               netif_stop_queue(ndev);
+       /* Trigger transmission start */
+       writel(0, fep->hwp + FEC_X_DES_ACTIVE);
+
+       return 0;
+}
+
+static int
+fec_enet_txq_put_data_tso(struct sk_buff *skb, struct net_device *ndev,
+                       struct bufdesc *bdp, int index, char *data,
+                       int size, bool last_tcp, bool is_last)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       const struct platform_device_id *id_entry =
+                               platform_get_device_id(fep->pdev);
+       struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
+       unsigned short status;
+       unsigned int estatus = 0;
+
+       status = bdp->cbd_sc;
+       status &= ~BD_ENET_TX_STATS;
+
+       status |= (BD_ENET_TX_TC | BD_ENET_TX_READY);
+       bdp->cbd_datlen = size;
+
+       if (((unsigned long) data) & FEC_ALIGNMENT ||
+               id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
+               memcpy(fep->tx_bounce[index], data, size);
+               data = fep->tx_bounce[index];
+
+               if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
+                       swap_buffer(data, size);
+       }
+
+       bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, data,
+                                       size, DMA_TO_DEVICE);
+       if (dma_mapping_error(&fep->pdev->dev, bdp->cbd_bufaddr)) {
+               dev_kfree_skb_any(skb);
+               if (net_ratelimit())
+                       netdev_err(ndev, "Tx DMA memory map failed\n");
+               return NETDEV_TX_BUSY;
+       }
+
+       if (fep->bufdesc_ex) {
+               if (skb->ip_summed == CHECKSUM_PARTIAL)
+                       estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
+               ebdp->cbd_bdu = 0;
+               ebdp->cbd_esc = estatus;
+       }
+
+       /* Handle the last BD specially */
+       if (last_tcp)
+               status |= (BD_ENET_TX_LAST | BD_ENET_TX_TC);
+       if (is_last) {
+               status |= BD_ENET_TX_INTR;
+               if (fep->bufdesc_ex)
+                       ebdp->cbd_esc |= BD_ENET_TX_INT;
+       }
+
+       bdp->cbd_sc = status;
+
+       return 0;
+}
+
+static int
+fec_enet_txq_put_hdr_tso(struct sk_buff *skb, struct net_device *ndev,
+                       struct bufdesc *bdp, int index)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       const struct platform_device_id *id_entry =
+                               platform_get_device_id(fep->pdev);
+       int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+       struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
+       void *bufaddr;
+       unsigned long dmabuf;
+       unsigned short status;
+       unsigned int estatus = 0;
+
+       status = bdp->cbd_sc;
+       status &= ~BD_ENET_TX_STATS;
+       status |= (BD_ENET_TX_TC | BD_ENET_TX_READY);
+
+       bufaddr = fep->tso_hdrs + index * TSO_HEADER_SIZE;
+       dmabuf = fep->tso_hdrs_dma + index * TSO_HEADER_SIZE;
+       if (((unsigned long) bufaddr) & FEC_ALIGNMENT ||
+               id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
+               memcpy(fep->tx_bounce[index], skb->data, hdr_len);
+               bufaddr = fep->tx_bounce[index];
+
+               if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
+                       swap_buffer(bufaddr, hdr_len);
+
+               dmabuf = dma_map_single(&fep->pdev->dev, bufaddr,
+                                       hdr_len, DMA_TO_DEVICE);
+               if (dma_mapping_error(&fep->pdev->dev, dmabuf)) {
+                       dev_kfree_skb_any(skb);
+                       if (net_ratelimit())
+                               netdev_err(ndev, "Tx DMA memory map failed\n");
+                       return NETDEV_TX_BUSY;
+               }
+       }
+
+       bdp->cbd_bufaddr = dmabuf;
+       bdp->cbd_datlen = hdr_len;
+
+       if (fep->bufdesc_ex) {
+               if (skb->ip_summed == CHECKSUM_PARTIAL)
+                       estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
+               ebdp->cbd_bdu = 0;
+               ebdp->cbd_esc = estatus;
+       }
+
+       bdp->cbd_sc = status;
+
+       return 0;
+}
+
+static int fec_enet_txq_submit_tso(struct sk_buff *skb, struct net_device *ndev)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+       int total_len, data_left;
+       struct bufdesc *bdp = fep->cur_tx;
+       struct tso_t tso;
+       unsigned int index = 0;
+       int ret;
+
+       if (tso_count_descs(skb) >= fec_enet_get_free_txdesc_num(fep)) {
+               dev_kfree_skb_any(skb);
+               if (net_ratelimit())
+                       netdev_err(ndev, "NOT enough BD for TSO!\n");
+               return NETDEV_TX_OK;
+       }
+
+       /* Protocol checksum off-load for TCP and UDP. */
+       if (fec_enet_clear_csum(skb, ndev)) {
+               dev_kfree_skb_any(skb);
+               return NETDEV_TX_OK;
+       }
+
+       /* Initialize the TSO handler, and prepare the first payload */
+       tso_start(skb, &tso);
+
+       total_len = skb->len - hdr_len;
+       while (total_len > 0) {
+               char *hdr;
+
+               index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
+               data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len);
+               total_len -= data_left;
+
+               /* prepare packet headers: MAC + IP + TCP */
+               hdr = fep->tso_hdrs + index * TSO_HEADER_SIZE;
+               tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0);
+               ret = fec_enet_txq_put_hdr_tso(skb, ndev, bdp, index);
+               if (ret)
+                       goto err_release;
+
+               while (data_left > 0) {
+                       int size;
+
+                       size = min_t(int, tso.size, data_left);
+                       bdp = fec_enet_get_nextdesc(bdp, fep);
+                       index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
+                       ret = fec_enet_txq_put_data_tso(skb, ndev, bdp, index, tso.data,
+                                                       size, size == data_left,
+                                                       total_len == 0);
+                       if (ret)
+                               goto err_release;
+
+                       data_left -= size;
+                       tso_build_data(skb, &tso, size);
+               }
+
+               bdp = fec_enet_get_nextdesc(bdp, fep);
+       }
+
+       /* Save skb pointer */
+       fep->tx_skbuff[index] = skb;
+
+       fec_enet_submit_work(bdp, fep);
+
+       skb_tx_timestamp(skb);
+       fep->cur_tx = bdp;
 
        /* Trigger transmission start */
        writel(0, fep->hwp + FEC_X_DES_ACTIVE);
 
+       return 0;
+
+err_release:
+       /* TODO: Release all used data descriptors for TSO */
+       return ret;
+}
+
+static netdev_tx_t
+fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       int entries_free;
+       int ret;
+
+       if (skb_is_gso(skb))
+               ret = fec_enet_txq_submit_tso(skb, ndev);
+       else
+               ret = fec_enet_txq_submit_skb(skb, ndev);
+       if (ret)
+               return ret;
+
+       entries_free = fec_enet_get_free_txdesc_num(fep);
+       if (entries_free <= fep->tx_stop_threshold)
+               netif_stop_queue(ndev);
+
        return NETDEV_TX_OK;
 }
 
@@ -756,6 +1077,7 @@ fec_enet_tx(struct net_device *ndev)
        unsigned short status;
        struct  sk_buff *skb;
        int     index = 0;
+       int     entries_free;
 
        fep = netdev_priv(ndev);
        bdp = fep->dirty_tx;
@@ -769,16 +1091,17 @@ fec_enet_tx(struct net_device *ndev)
                if (bdp == fep->cur_tx)
                        break;
 
-               if (fep->bufdesc_ex)
-                       index = (struct bufdesc_ex *)bdp -
-                               (struct bufdesc_ex *)fep->tx_bd_base;
-               else
-                       index = bdp - fep->tx_bd_base;
+               index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
 
                skb = fep->tx_skbuff[index];
-               dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, skb->len,
-                               DMA_TO_DEVICE);
+               if (!IS_TSO_HEADER(fep, bdp->cbd_bufaddr))
+                       dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
+                                       bdp->cbd_datlen, DMA_TO_DEVICE);
                bdp->cbd_bufaddr = 0;
+               if (!skb) {
+                       bdp = fec_enet_get_nextdesc(bdp, fep);
+                       continue;
+               }
 
                /* Check for errors. */
                if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC |
@@ -797,7 +1120,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;
+                       ndev->stats.tx_bytes += skb->len;
                }
 
                if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) &&
@@ -834,15 +1157,15 @@ fec_enet_tx(struct net_device *ndev)
 
                /* Since we have freed up a buffer, the ring is no longer full
                 */
-               if (fep->dirty_tx != fep->cur_tx) {
-                       if (netif_queue_stopped(ndev))
+               if (netif_queue_stopped(ndev)) {
+                       entries_free = fec_enet_get_free_txdesc_num(fep);
+                       if (entries_free >= fep->tx_wake_threshold)
                                netif_wake_queue(ndev);
                }
        }
        return;
 }
 
-
 /* During a receive, the cur_rx points to the current incoming buffer.
  * When we update through the ring, if the next incoming buffer has
  * not been given to the system, we just set the empty indicator,
@@ -920,11 +1243,7 @@ fec_enet_rx(struct net_device *ndev, int budget)
                pkt_len = bdp->cbd_datlen;
                ndev->stats.rx_bytes += pkt_len;
 
-               if (fep->bufdesc_ex)
-                       index = (struct bufdesc_ex *)bdp -
-                               (struct bufdesc_ex *)fep->rx_bd_base;
-               else
-                       index = bdp - fep->rx_bd_base;
+               index = fec_enet_get_bd_index(fep->rx_bd_base, bdp, fep);
                data = fep->rx_skbuff[index]->data;
                dma_sync_single_for_cpu(&fep->pdev->dev, bdp->cbd_bufaddr,
                                        FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
@@ -1255,6 +1574,49 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
        return 0;
 }
 
+static int fec_enet_clk_enable(struct net_device *ndev, bool enable)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       int ret;
+
+       if (enable) {
+               ret = clk_prepare_enable(fep->clk_ahb);
+               if (ret)
+                       return ret;
+               ret = clk_prepare_enable(fep->clk_ipg);
+               if (ret)
+                       goto failed_clk_ipg;
+               if (fep->clk_enet_out) {
+                       ret = clk_prepare_enable(fep->clk_enet_out);
+                       if (ret)
+                               goto failed_clk_enet_out;
+               }
+               if (fep->clk_ptp) {
+                       ret = clk_prepare_enable(fep->clk_ptp);
+                       if (ret)
+                               goto failed_clk_ptp;
+               }
+       } else {
+               clk_disable_unprepare(fep->clk_ahb);
+               clk_disable_unprepare(fep->clk_ipg);
+               if (fep->clk_enet_out)
+                       clk_disable_unprepare(fep->clk_enet_out);
+               if (fep->clk_ptp)
+                       clk_disable_unprepare(fep->clk_ptp);
+       }
+
+       return 0;
+failed_clk_ptp:
+       if (fep->clk_enet_out)
+               clk_disable_unprepare(fep->clk_enet_out);
+failed_clk_enet_out:
+               clk_disable_unprepare(fep->clk_ipg);
+failed_clk_ipg:
+               clk_disable_unprepare(fep->clk_ahb);
+
+       return ret;
+}
+
 static int fec_enet_mii_probe(struct net_device *ndev)
 {
        struct fec_enet_private *fep = netdev_priv(ndev);
@@ -1364,7 +1726,7 @@ static int fec_enet_mii_init(struct platform_device *pdev)
         * Reference Manual has an error on this, and gets fixed on i.MX6Q
         * document.
         */
-       fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ahb), 5000000);
+       fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000);
        if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
                fep->phy_speed--;
        fep->phy_speed <<= 1;
@@ -1773,6 +2135,11 @@ fec_enet_open(struct net_device *ndev)
        struct fec_enet_private *fep = netdev_priv(ndev);
        int ret;
 
+       pinctrl_pm_select_default_state(&fep->pdev->dev);
+       ret = fec_enet_clk_enable(ndev, true);
+       if (ret)
+               return ret;
+
        /* I should reset the ring buffers here, but I don't yet know
         * a simple way to do that.
         */
@@ -1811,6 +2178,8 @@ fec_enet_close(struct net_device *ndev)
                phy_disconnect(fep->phy_dev);
        }
 
+       fec_enet_clk_enable(ndev, false);
+       pinctrl_pm_select_sleep_state(&fep->pdev->dev);
        fec_enet_free_buffers(ndev);
 
        return 0;
@@ -1988,13 +2357,35 @@ static int fec_enet_init(struct net_device *ndev)
        const struct platform_device_id *id_entry =
                                platform_get_device_id(fep->pdev);
        struct bufdesc *cbd_base;
+       int bd_size;
+
+       /* init the tx & rx ring size */
+       fep->tx_ring_size = TX_RING_SIZE;
+       fep->rx_ring_size = RX_RING_SIZE;
+
+       fep->tx_stop_threshold = FEC_MAX_SKB_DESCS;
+       fep->tx_wake_threshold = (fep->tx_ring_size - fep->tx_stop_threshold) / 2;
+
+       if (fep->bufdesc_ex)
+               fep->bufdesc_size = sizeof(struct bufdesc_ex);
+       else
+               fep->bufdesc_size = sizeof(struct bufdesc);
+       bd_size = (fep->tx_ring_size + fep->rx_ring_size) *
+                       fep->bufdesc_size;
 
        /* Allocate memory for buffer descriptors. */
-       cbd_base = dma_alloc_coherent(NULL, PAGE_SIZE, &fep->bd_dma,
+       cbd_base = dma_alloc_coherent(NULL, bd_size, &fep->bd_dma,
                                      GFP_KERNEL);
        if (!cbd_base)
                return -ENOMEM;
 
+       fep->tso_hdrs = dma_alloc_coherent(NULL, fep->tx_ring_size * TSO_HEADER_SIZE,
+                                               &fep->tso_hdrs_dma, GFP_KERNEL);
+       if (!fep->tso_hdrs) {
+               dma_free_coherent(NULL, bd_size, cbd_base, fep->bd_dma);
+               return -ENOMEM;
+       }
+
        memset(cbd_base, 0, PAGE_SIZE);
 
        fep->netdev = ndev;
@@ -2004,10 +2395,6 @@ static int fec_enet_init(struct net_device *ndev)
        /* make sure MAC we just acquired is programmed into the hw */
        fec_set_mac_address(ndev, NULL);
 
-       /* init the tx & rx ring size */
-       fep->tx_ring_size = TX_RING_SIZE;
-       fep->rx_ring_size = RX_RING_SIZE;
-
        /* Set receive and transmit descriptor base. */
        fep->rx_bd_base = cbd_base;
        if (fep->bufdesc_ex)
@@ -2024,21 +2411,21 @@ 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, NAPI_POLL_WEIGHT);
 
-       if (id_entry->driver_data & FEC_QUIRK_HAS_VLAN) {
+       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) {
+               ndev->gso_max_segs = FEC_MAX_TSO_SEGS;
+
                /* enable hw accelerator */
                ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM
-                               | NETIF_F_RXCSUM);
-               ndev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM
-                               | NETIF_F_RXCSUM);
+                               | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO);
                fep->csum_flags |= FLAG_RX_CSUM_ENABLED;
        }
 
+       ndev->hw_features = ndev->features;
+
        fec_restart(ndev, 0);
 
        return 0;
@@ -2114,6 +2501,9 @@ fec_probe(struct platform_device *pdev)
                fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG;
 #endif
 
+       /* Select default pin state */
+       pinctrl_pm_select_default_state(&pdev->dev);
+
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        fep->hwp = devm_ioremap_resource(&pdev->dev, r);
        if (IS_ERR(fep->hwp)) {
@@ -2164,26 +2554,10 @@ fec_probe(struct platform_device *pdev)
                fep->bufdesc_ex = 0;
        }
 
-       ret = clk_prepare_enable(fep->clk_ahb);
+       ret = fec_enet_clk_enable(ndev, true);
        if (ret)
                goto failed_clk;
 
-       ret = clk_prepare_enable(fep->clk_ipg);
-       if (ret)
-               goto failed_clk_ipg;
-
-       if (fep->clk_enet_out) {
-               ret = clk_prepare_enable(fep->clk_enet_out);
-               if (ret)
-                       goto failed_clk_enet_out;
-       }
-
-       if (fep->clk_ptp) {
-               ret = clk_prepare_enable(fep->clk_ptp);
-               if (ret)
-                       goto failed_clk_ptp;
-       }
-
        fep->reg_phy = devm_regulator_get(&pdev->dev, "phy");
        if (!IS_ERR(fep->reg_phy)) {
                ret = regulator_enable(fep->reg_phy);
@@ -2225,6 +2599,8 @@ fec_probe(struct platform_device *pdev)
 
        /* Carrier starts down, phylib will bring it up */
        netif_carrier_off(ndev);
+       fec_enet_clk_enable(ndev, false);
+       pinctrl_pm_select_sleep_state(&pdev->dev);
 
        ret = register_netdev(ndev);
        if (ret)
@@ -2244,15 +2620,7 @@ failed_init:
        if (fep->reg_phy)
                regulator_disable(fep->reg_phy);
 failed_regulator:
-       if (fep->clk_ptp)
-               clk_disable_unprepare(fep->clk_ptp);
-failed_clk_ptp:
-       if (fep->clk_enet_out)
-               clk_disable_unprepare(fep->clk_enet_out);
-failed_clk_enet_out:
-       clk_disable_unprepare(fep->clk_ipg);
-failed_clk_ipg:
-       clk_disable_unprepare(fep->clk_ahb);
+       fec_enet_clk_enable(ndev, false);
 failed_clk:
 failed_ioremap:
        free_netdev(ndev);
@@ -2272,14 +2640,9 @@ fec_drv_remove(struct platform_device *pdev)
        del_timer_sync(&fep->time_keep);
        if (fep->reg_phy)
                regulator_disable(fep->reg_phy);
-       if (fep->clk_ptp)
-               clk_disable_unprepare(fep->clk_ptp);
        if (fep->ptp_clock)
                ptp_clock_unregister(fep->ptp_clock);
-       if (fep->clk_enet_out)
-               clk_disable_unprepare(fep->clk_enet_out);
-       clk_disable_unprepare(fep->clk_ipg);
-       clk_disable_unprepare(fep->clk_ahb);
+       fec_enet_clk_enable(ndev, false);
        free_netdev(ndev);
 
        return 0;
@@ -2296,12 +2659,8 @@ fec_suspend(struct device *dev)
                fec_stop(ndev);
                netif_device_detach(ndev);
        }
-       if (fep->clk_ptp)
-               clk_disable_unprepare(fep->clk_ptp);
-       if (fep->clk_enet_out)
-               clk_disable_unprepare(fep->clk_enet_out);
-       clk_disable_unprepare(fep->clk_ipg);
-       clk_disable_unprepare(fep->clk_ahb);
+       fec_enet_clk_enable(ndev, false);
+       pinctrl_pm_select_sleep_state(&fep->pdev->dev);
 
        if (fep->reg_phy)
                regulator_disable(fep->reg_phy);
@@ -2322,25 +2681,10 @@ fec_resume(struct device *dev)
                        return ret;
        }
 
-       ret = clk_prepare_enable(fep->clk_ahb);
+       pinctrl_pm_select_default_state(&fep->pdev->dev);
+       ret = fec_enet_clk_enable(ndev, true);
        if (ret)
-               goto failed_clk_ahb;
-
-       ret = clk_prepare_enable(fep->clk_ipg);
-       if (ret)
-               goto failed_clk_ipg;
-
-       if (fep->clk_enet_out) {
-               ret = clk_prepare_enable(fep->clk_enet_out);
-               if (ret)
-                       goto failed_clk_enet_out;
-       }
-
-       if (fep->clk_ptp) {
-               ret = clk_prepare_enable(fep->clk_ptp);
-               if (ret)
-                       goto failed_clk_ptp;
-       }
+               goto failed_clk;
 
        if (netif_running(ndev)) {
                fec_restart(ndev, fep->full_duplex);
@@ -2349,14 +2693,7 @@ fec_resume(struct device *dev)
 
        return 0;
 
-failed_clk_ptp:
-       if (fep->clk_enet_out)
-               clk_disable_unprepare(fep->clk_enet_out);
-failed_clk_enet_out:
-       clk_disable_unprepare(fep->clk_ipg);
-failed_clk_ipg:
-       clk_disable_unprepare(fep->clk_ahb);
-failed_clk_ahb:
+failed_clk:
        if (fep->reg_phy)
                regulator_disable(fep->reg_phy);
        return ret;
index dc80db41d6b3397388b0210283c4c7fd3ce07680..cfaf17b70f3fc5d6ab7a11a81266d67267646f33 100644 (file)
@@ -791,10 +791,6 @@ static int fs_init_phy(struct net_device *dev)
 
        phydev = of_phy_connect(dev, fep->fpi->phy_node, &fs_adjust_link, 0,
                                iface);
-       if (!phydev) {
-               phydev = of_phy_connect_fixed_link(dev, &fs_adjust_link,
-                                                  iface);
-       }
        if (!phydev) {
                dev_err(&dev->dev, "Could not attach to PHY\n");
                return -ENODEV;
@@ -1029,9 +1025,16 @@ static int fs_enet_probe(struct platform_device *ofdev)
        fpi->use_napi = 1;
        fpi->napi_weight = 17;
        fpi->phy_node = of_parse_phandle(ofdev->dev.of_node, "phy-handle", 0);
-       if ((!fpi->phy_node) && (!of_get_property(ofdev->dev.of_node, "fixed-link",
-                                                 NULL)))
-               goto out_free_fpi;
+       if (!fpi->phy_node && of_phy_is_fixed_link(ofdev->dev.of_node)) {
+               err = of_phy_register_fixed_link(ofdev->dev.of_node);
+               if (err)
+                       goto out_free_fpi;
+
+               /* In the case of a fixed PHY, the DT node associated
+                * to the PHY is the Ethernet MAC DT node.
+                */
+               fpi->phy_node = ofdev->dev.of_node;
+       }
 
        if (of_device_is_compatible(ofdev->dev.of_node, "fsl,mpc5125-fec")) {
                phy_connection_type = of_get_property(ofdev->dev.of_node,
index ee6ddbd4f252789c0adc76007053b34c5468e665..a6cf40e62f3a9c531f2997c34590fbe89d0ddc80 100644 (file)
@@ -889,6 +889,17 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
 
        priv->phy_node = of_parse_phandle(np, "phy-handle", 0);
 
+       /* In the case of a fixed PHY, the DT node associated
+        * to the PHY is the Ethernet MAC DT node.
+        */
+       if (of_phy_is_fixed_link(np)) {
+               err = of_phy_register_fixed_link(np);
+               if (err)
+                       goto err_grp_init;
+
+               priv->phy_node = np;
+       }
+
        /* Find the TBI PHY.  If it's not there, we don't support SGMII */
        priv->tbi_node = of_parse_phandle(np, "tbi-handle", 0);
 
@@ -1231,7 +1242,7 @@ static void gfar_hw_init(struct gfar_private *priv)
                gfar_write_isrg(priv);
 }
 
-static void __init gfar_init_addr_hash_table(struct gfar_private *priv)
+static void gfar_init_addr_hash_table(struct gfar_private *priv)
 {
        struct gfar __iomem *regs = priv->gfargrp[0].regs;
 
@@ -1373,6 +1384,9 @@ static int gfar_probe(struct platform_device *ofdev)
 
        gfar_hw_init(priv);
 
+       /* Carrier starts down, phylib will bring it up */
+       netif_carrier_off(dev);
+
        err = register_netdev(dev);
 
        if (err) {
@@ -1380,9 +1394,6 @@ static int gfar_probe(struct platform_device *ofdev)
                goto register_fail;
        }
 
-       /* Carrier starts down, phylib will bring it up */
-       netif_carrier_off(dev);
-
        device_init_wakeup(&dev->dev,
                           priv->device_flags &
                           FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
@@ -1660,9 +1671,6 @@ static int init_phy(struct net_device *dev)
 
        priv->phydev = of_phy_connect(dev, priv->phy_node, &adjust_link, 0,
                                      interface);
-       if (!priv->phydev)
-               priv->phydev = of_phy_connect_fixed_link(dev, &adjust_link,
-                                                        interface);
        if (!priv->phydev) {
                dev_err(&dev->dev, "could not attach to PHY\n");
                return -ENODEV;
index c8299c31b21f9f5c52dc380f9b867597c1b8bcdf..fab39e2954410106f9c26304f73c29169c1d35a1 100644 (file)
@@ -1728,9 +1728,6 @@ static int init_phy(struct net_device *dev)
 
        phydev = of_phy_connect(dev, ug_info->phy_node, &adjust_link, 0,
                                priv->phy_interface);
-       if (!phydev)
-               phydev = of_phy_connect_fixed_link(dev, &adjust_link,
-                                                  priv->phy_interface);
        if (!phydev) {
                dev_err(&dev->dev, "Could not attach to PHY\n");
                return -ENODEV;
@@ -3790,6 +3787,17 @@ static int ucc_geth_probe(struct platform_device* ofdev)
        ug_info->uf_info.irq = irq_of_parse_and_map(np, 0);
 
        ug_info->phy_node = of_parse_phandle(np, "phy-handle", 0);
+       if (!ug_info->phy_node) {
+               /* In the case of a fixed PHY, the DT node associated
+                * to the PHY is the Ethernet MAC DT node.
+                */
+               if (of_phy_is_fixed_link(np)) {
+                       err = of_phy_register_fixed_link(np);
+                       if (err)
+                               return err;
+               }
+               ug_info->phy_node = np;
+       }
 
        /* Find the TBI PHY node.  If it's not there, we don't support SGMII */
        ug_info->tbi_node = of_parse_phandle(np, "tbi-handle", 0);
index 413329eff2ffc05f80197d05d16a9c4332821b6b..cc83350d56ba1c05aa7eb619b607635f76de6400 100644 (file)
@@ -417,5 +417,5 @@ static const struct ethtool_ops uec_ethtool_ops = {
 
 void uec_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &uec_ethtool_ops);
+       netdev->ethtool_ops = &uec_ethtool_ops;
 }
index d449fcb901999cce522a0301a26692ff6df34b0f..0c9d55c862aeb40bb8251bc10825549ac01aa6b3 100644 (file)
@@ -162,7 +162,9 @@ static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
 
        /* Return all Fs if nothing was there */
        if (in_be32(&regs->mdio_stat) & MDIO_STAT_RD_ER) {
-               dev_err(&bus->dev, "MDIO read error\n");
+               dev_err(&bus->dev,
+                       "Error while reading PHY%d reg at %d.%d\n",
+                       phy_id, dev_addr, regnum);
                return 0xffff;
        }
 
index 7becab1aa3e43b41c8f4b942cce18d9c584152fd..cfe7a74317307f8ef1ef39acd2c78c97e903d423 100644 (file)
@@ -256,7 +256,7 @@ static int fmvj18x_probe(struct pcmcia_device *link)
     dev->netdev_ops = &fjn_netdev_ops;
     dev->watchdog_timeo = TX_TIMEOUT;
 
-    SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+    dev->ethtool_ops = &netdev_ethtool_ops;
 
     return fmvj18x_config(link);
 } /* fmvj18x_attach */
diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
new file mode 100644 (file)
index 0000000..e942173
--- /dev/null
@@ -0,0 +1,27 @@
+#
+# HISILICON device configuration
+#
+
+config NET_VENDOR_HISILICON
+       bool "Hisilicon devices"
+       default y
+       depends on ARM
+       ---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 Hisilicon devices. If you say Y, you will be asked
+         for your specific card in the following questions.
+
+if NET_VENDOR_HISILICON
+
+config HIX5HD2_GMAC
+       tristate "Hisilicon HIX5HD2 Family Network Device Support"
+       select PHYLIB
+       help
+         This selects the hix5hd2 mac family network device.
+
+endif # NET_VENDOR_HISILICON
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
new file mode 100644 (file)
index 0000000..9175e84
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Makefile for the HISILICON network device drivers.
+#
+
+obj-$(CONFIG_HIX5HD2_GMAC) += hix5hd2_gmac.o
diff --git a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c
new file mode 100644 (file)
index 0000000..0ffdcd3
--- /dev/null
@@ -0,0 +1,1066 @@
+/* Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * 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/interrupt.h>
+#include <linux/etherdevice.h>
+#include <linux/platform_device.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
+#include <linux/clk.h>
+#include <linux/circ_buf.h>
+
+#define STATION_ADDR_LOW               0x0000
+#define STATION_ADDR_HIGH              0x0004
+#define MAC_DUPLEX_HALF_CTRL           0x0008
+#define MAX_FRM_SIZE                   0x003c
+#define PORT_MODE                      0x0040
+#define PORT_EN                                0x0044
+#define BITS_TX_EN                     BIT(2)
+#define BITS_RX_EN                     BIT(1)
+#define REC_FILT_CONTROL               0x0064
+#define BIT_CRC_ERR_PASS               BIT(5)
+#define BIT_PAUSE_FRM_PASS             BIT(4)
+#define BIT_VLAN_DROP_EN               BIT(3)
+#define BIT_BC_DROP_EN                 BIT(2)
+#define BIT_MC_MATCH_EN                        BIT(1)
+#define BIT_UC_MATCH_EN                        BIT(0)
+#define PORT_MC_ADDR_LOW               0x0068
+#define PORT_MC_ADDR_HIGH              0x006C
+#define CF_CRC_STRIP                   0x01b0
+#define MODE_CHANGE_EN                 0x01b4
+#define BIT_MODE_CHANGE_EN             BIT(0)
+#define COL_SLOT_TIME                  0x01c0
+#define RECV_CONTROL                   0x01e0
+#define BIT_STRIP_PAD_EN               BIT(3)
+#define BIT_RUNT_PKT_EN                        BIT(4)
+#define CONTROL_WORD                   0x0214
+#define MDIO_SINGLE_CMD                        0x03c0
+#define MDIO_SINGLE_DATA               0x03c4
+#define MDIO_CTRL                      0x03cc
+#define MDIO_RDATA_STATUS              0x03d0
+
+#define MDIO_START                     BIT(20)
+#define MDIO_R_VALID                   BIT(0)
+#define MDIO_READ                      (BIT(17) | MDIO_START)
+#define MDIO_WRITE                     (BIT(16) | MDIO_START)
+
+#define RX_FQ_START_ADDR               0x0500
+#define RX_FQ_DEPTH                    0x0504
+#define RX_FQ_WR_ADDR                  0x0508
+#define RX_FQ_RD_ADDR                  0x050c
+#define RX_FQ_VLDDESC_CNT              0x0510
+#define RX_FQ_ALEMPTY_TH               0x0514
+#define RX_FQ_REG_EN                   0x0518
+#define BITS_RX_FQ_START_ADDR_EN       BIT(2)
+#define BITS_RX_FQ_DEPTH_EN            BIT(1)
+#define BITS_RX_FQ_RD_ADDR_EN          BIT(0)
+#define RX_FQ_ALFULL_TH                        0x051c
+#define RX_BQ_START_ADDR               0x0520
+#define RX_BQ_DEPTH                    0x0524
+#define RX_BQ_WR_ADDR                  0x0528
+#define RX_BQ_RD_ADDR                  0x052c
+#define RX_BQ_FREE_DESC_CNT            0x0530
+#define RX_BQ_ALEMPTY_TH               0x0534
+#define RX_BQ_REG_EN                   0x0538
+#define BITS_RX_BQ_START_ADDR_EN       BIT(2)
+#define BITS_RX_BQ_DEPTH_EN            BIT(1)
+#define BITS_RX_BQ_WR_ADDR_EN          BIT(0)
+#define RX_BQ_ALFULL_TH                        0x053c
+#define TX_BQ_START_ADDR               0x0580
+#define TX_BQ_DEPTH                    0x0584
+#define TX_BQ_WR_ADDR                  0x0588
+#define TX_BQ_RD_ADDR                  0x058c
+#define TX_BQ_VLDDESC_CNT              0x0590
+#define TX_BQ_ALEMPTY_TH               0x0594
+#define TX_BQ_REG_EN                   0x0598
+#define BITS_TX_BQ_START_ADDR_EN       BIT(2)
+#define BITS_TX_BQ_DEPTH_EN            BIT(1)
+#define BITS_TX_BQ_RD_ADDR_EN          BIT(0)
+#define TX_BQ_ALFULL_TH                        0x059c
+#define TX_RQ_START_ADDR               0x05a0
+#define TX_RQ_DEPTH                    0x05a4
+#define TX_RQ_WR_ADDR                  0x05a8
+#define TX_RQ_RD_ADDR                  0x05ac
+#define TX_RQ_FREE_DESC_CNT            0x05b0
+#define TX_RQ_ALEMPTY_TH               0x05b4
+#define TX_RQ_REG_EN                   0x05b8
+#define BITS_TX_RQ_START_ADDR_EN       BIT(2)
+#define BITS_TX_RQ_DEPTH_EN            BIT(1)
+#define BITS_TX_RQ_WR_ADDR_EN          BIT(0)
+#define TX_RQ_ALFULL_TH                        0x05bc
+#define RAW_PMU_INT                    0x05c0
+#define ENA_PMU_INT                    0x05c4
+#define STATUS_PMU_INT                 0x05c8
+#define MAC_FIFO_ERR_IN                        BIT(30)
+#define TX_RQ_IN_TIMEOUT_INT           BIT(29)
+#define RX_BQ_IN_TIMEOUT_INT           BIT(28)
+#define TXOUTCFF_FULL_INT              BIT(27)
+#define TXOUTCFF_EMPTY_INT             BIT(26)
+#define TXCFF_FULL_INT                 BIT(25)
+#define TXCFF_EMPTY_INT                        BIT(24)
+#define RXOUTCFF_FULL_INT              BIT(23)
+#define RXOUTCFF_EMPTY_INT             BIT(22)
+#define RXCFF_FULL_INT                 BIT(21)
+#define RXCFF_EMPTY_INT                        BIT(20)
+#define TX_RQ_IN_INT                   BIT(19)
+#define TX_BQ_OUT_INT                  BIT(18)
+#define RX_BQ_IN_INT                   BIT(17)
+#define RX_FQ_OUT_INT                  BIT(16)
+#define TX_RQ_EMPTY_INT                        BIT(15)
+#define TX_RQ_FULL_INT                 BIT(14)
+#define TX_RQ_ALEMPTY_INT              BIT(13)
+#define TX_RQ_ALFULL_INT               BIT(12)
+#define TX_BQ_EMPTY_INT                        BIT(11)
+#define TX_BQ_FULL_INT                 BIT(10)
+#define TX_BQ_ALEMPTY_INT              BIT(9)
+#define TX_BQ_ALFULL_INT               BIT(8)
+#define RX_BQ_EMPTY_INT                        BIT(7)
+#define RX_BQ_FULL_INT                 BIT(6)
+#define RX_BQ_ALEMPTY_INT              BIT(5)
+#define RX_BQ_ALFULL_INT               BIT(4)
+#define RX_FQ_EMPTY_INT                        BIT(3)
+#define RX_FQ_FULL_INT                 BIT(2)
+#define RX_FQ_ALEMPTY_INT              BIT(1)
+#define RX_FQ_ALFULL_INT               BIT(0)
+
+#define DEF_INT_MASK                   (RX_BQ_IN_INT | RX_BQ_IN_TIMEOUT_INT | \
+                                       TX_RQ_IN_INT | TX_RQ_IN_TIMEOUT_INT)
+
+#define DESC_WR_RD_ENA                 0x05cc
+#define IN_QUEUE_TH                    0x05d8
+#define OUT_QUEUE_TH                   0x05dc
+#define QUEUE_TX_BQ_SHIFT              16
+#define RX_BQ_IN_TIMEOUT_TH            0x05e0
+#define TX_RQ_IN_TIMEOUT_TH            0x05e4
+#define STOP_CMD                       0x05e8
+#define BITS_TX_STOP                   BIT(1)
+#define BITS_RX_STOP                   BIT(0)
+#define FLUSH_CMD                      0x05eC
+#define BITS_TX_FLUSH_CMD              BIT(5)
+#define BITS_RX_FLUSH_CMD              BIT(4)
+#define BITS_TX_FLUSH_FLAG_DOWN                BIT(3)
+#define BITS_TX_FLUSH_FLAG_UP          BIT(2)
+#define BITS_RX_FLUSH_FLAG_DOWN                BIT(1)
+#define BITS_RX_FLUSH_FLAG_UP          BIT(0)
+#define RX_CFF_NUM_REG                 0x05f0
+#define PMU_FSM_REG                    0x05f8
+#define RX_FIFO_PKT_IN_NUM             0x05fc
+#define RX_FIFO_PKT_OUT_NUM            0x0600
+
+#define RGMII_SPEED_1000               0x2c
+#define RGMII_SPEED_100                        0x2f
+#define RGMII_SPEED_10                 0x2d
+#define MII_SPEED_100                  0x0f
+#define MII_SPEED_10                   0x0d
+#define GMAC_SPEED_1000                        0x05
+#define GMAC_SPEED_100                 0x01
+#define GMAC_SPEED_10                  0x00
+#define GMAC_FULL_DUPLEX               BIT(4)
+
+#define RX_BQ_INT_THRESHOLD            0x01
+#define TX_RQ_INT_THRESHOLD            0x01
+#define RX_BQ_IN_TIMEOUT               0x10000
+#define TX_RQ_IN_TIMEOUT               0x50000
+
+#define MAC_MAX_FRAME_SIZE             1600
+#define DESC_SIZE                      32
+#define RX_DESC_NUM                    1024
+#define TX_DESC_NUM                    1024
+
+#define DESC_VLD_FREE                  0
+#define DESC_VLD_BUSY                  0x80000000
+#define DESC_FL_MID                    0
+#define DESC_FL_LAST                   0x20000000
+#define DESC_FL_FIRST                  0x40000000
+#define DESC_FL_FULL                   0x60000000
+#define DESC_DATA_LEN_OFF              16
+#define DESC_BUFF_LEN_OFF              0
+#define DESC_DATA_MASK                 0x7ff
+
+/* DMA descriptor ring helpers */
+#define dma_ring_incr(n, s)            (((n) + 1) & ((s) - 1))
+#define dma_cnt(n)                     ((n) >> 5)
+#define dma_byte(n)                    ((n) << 5)
+
+struct hix5hd2_desc {
+       __le32 buff_addr;
+       __le32 cmd;
+} __aligned(32);
+
+struct hix5hd2_desc_sw {
+       struct hix5hd2_desc *desc;
+       dma_addr_t      phys_addr;
+       unsigned int    count;
+       unsigned int    size;
+};
+
+#define QUEUE_NUMS     4
+struct hix5hd2_priv {
+       struct hix5hd2_desc_sw pool[QUEUE_NUMS];
+#define rx_fq          pool[0]
+#define rx_bq          pool[1]
+#define tx_bq          pool[2]
+#define tx_rq          pool[3]
+
+       void __iomem *base;
+       void __iomem *ctrl_base;
+
+       struct sk_buff *tx_skb[TX_DESC_NUM];
+       struct sk_buff *rx_skb[RX_DESC_NUM];
+
+       struct device *dev;
+       struct net_device *netdev;
+
+       struct phy_device *phy;
+       struct device_node *phy_node;
+       phy_interface_t phy_mode;
+
+       unsigned int speed;
+       unsigned int duplex;
+
+       struct clk *clk;
+       struct mii_bus *bus;
+       struct napi_struct napi;
+       struct work_struct tx_timeout_task;
+};
+
+static void hix5hd2_config_port(struct net_device *dev, u32 speed, u32 duplex)
+{
+       struct hix5hd2_priv *priv = netdev_priv(dev);
+       u32 val;
+
+       priv->speed = speed;
+       priv->duplex = duplex;
+
+       switch (priv->phy_mode) {
+       case PHY_INTERFACE_MODE_RGMII:
+               if (speed == SPEED_1000)
+                       val = RGMII_SPEED_1000;
+               else if (speed == SPEED_100)
+                       val = RGMII_SPEED_100;
+               else
+                       val = RGMII_SPEED_10;
+               break;
+       case PHY_INTERFACE_MODE_MII:
+               if (speed == SPEED_100)
+                       val = MII_SPEED_100;
+               else
+                       val = MII_SPEED_10;
+               break;
+       default:
+               netdev_warn(dev, "not supported mode\n");
+               val = MII_SPEED_10;
+               break;
+       }
+
+       if (duplex)
+               val |= GMAC_FULL_DUPLEX;
+       writel_relaxed(val, priv->ctrl_base);
+
+       writel_relaxed(BIT_MODE_CHANGE_EN, priv->base + MODE_CHANGE_EN);
+       if (speed == SPEED_1000)
+               val = GMAC_SPEED_1000;
+       else if (speed == SPEED_100)
+               val = GMAC_SPEED_100;
+       else
+               val = GMAC_SPEED_10;
+       writel_relaxed(val, priv->base + PORT_MODE);
+       writel_relaxed(0, priv->base + MODE_CHANGE_EN);
+       writel_relaxed(duplex, priv->base + MAC_DUPLEX_HALF_CTRL);
+}
+
+static void hix5hd2_set_desc_depth(struct hix5hd2_priv *priv, int rx, int tx)
+{
+       writel_relaxed(BITS_RX_FQ_DEPTH_EN, priv->base + RX_FQ_REG_EN);
+       writel_relaxed(rx << 3, priv->base + RX_FQ_DEPTH);
+       writel_relaxed(0, priv->base + RX_FQ_REG_EN);
+
+       writel_relaxed(BITS_RX_BQ_DEPTH_EN, priv->base + RX_BQ_REG_EN);
+       writel_relaxed(rx << 3, priv->base + RX_BQ_DEPTH);
+       writel_relaxed(0, priv->base + RX_BQ_REG_EN);
+
+       writel_relaxed(BITS_TX_BQ_DEPTH_EN, priv->base + TX_BQ_REG_EN);
+       writel_relaxed(tx << 3, priv->base + TX_BQ_DEPTH);
+       writel_relaxed(0, priv->base + TX_BQ_REG_EN);
+
+       writel_relaxed(BITS_TX_RQ_DEPTH_EN, priv->base + TX_RQ_REG_EN);
+       writel_relaxed(tx << 3, priv->base + TX_RQ_DEPTH);
+       writel_relaxed(0, priv->base + TX_RQ_REG_EN);
+}
+
+static void hix5hd2_set_rx_fq(struct hix5hd2_priv *priv, dma_addr_t phy_addr)
+{
+       writel_relaxed(BITS_RX_FQ_START_ADDR_EN, priv->base + RX_FQ_REG_EN);
+       writel_relaxed(phy_addr, priv->base + RX_FQ_START_ADDR);
+       writel_relaxed(0, priv->base + RX_FQ_REG_EN);
+}
+
+static void hix5hd2_set_rx_bq(struct hix5hd2_priv *priv, dma_addr_t phy_addr)
+{
+       writel_relaxed(BITS_RX_BQ_START_ADDR_EN, priv->base + RX_BQ_REG_EN);
+       writel_relaxed(phy_addr, priv->base + RX_BQ_START_ADDR);
+       writel_relaxed(0, priv->base + RX_BQ_REG_EN);
+}
+
+static void hix5hd2_set_tx_bq(struct hix5hd2_priv *priv, dma_addr_t phy_addr)
+{
+       writel_relaxed(BITS_TX_BQ_START_ADDR_EN, priv->base + TX_BQ_REG_EN);
+       writel_relaxed(phy_addr, priv->base + TX_BQ_START_ADDR);
+       writel_relaxed(0, priv->base + TX_BQ_REG_EN);
+}
+
+static void hix5hd2_set_tx_rq(struct hix5hd2_priv *priv, dma_addr_t phy_addr)
+{
+       writel_relaxed(BITS_TX_RQ_START_ADDR_EN, priv->base + TX_RQ_REG_EN);
+       writel_relaxed(phy_addr, priv->base + TX_RQ_START_ADDR);
+       writel_relaxed(0, priv->base + TX_RQ_REG_EN);
+}
+
+static void hix5hd2_set_desc_addr(struct hix5hd2_priv *priv)
+{
+       hix5hd2_set_rx_fq(priv, priv->rx_fq.phys_addr);
+       hix5hd2_set_rx_bq(priv, priv->rx_bq.phys_addr);
+       hix5hd2_set_tx_rq(priv, priv->tx_rq.phys_addr);
+       hix5hd2_set_tx_bq(priv, priv->tx_bq.phys_addr);
+}
+
+static void hix5hd2_hw_init(struct hix5hd2_priv *priv)
+{
+       u32 val;
+
+       /* disable and clear all interrupts */
+       writel_relaxed(0, priv->base + ENA_PMU_INT);
+       writel_relaxed(~0, priv->base + RAW_PMU_INT);
+
+       writel_relaxed(BIT_CRC_ERR_PASS, priv->base + REC_FILT_CONTROL);
+       writel_relaxed(MAC_MAX_FRAME_SIZE, priv->base + CONTROL_WORD);
+       writel_relaxed(0, priv->base + COL_SLOT_TIME);
+
+       val = RX_BQ_INT_THRESHOLD | TX_RQ_INT_THRESHOLD << QUEUE_TX_BQ_SHIFT;
+       writel_relaxed(val, priv->base + IN_QUEUE_TH);
+
+       writel_relaxed(RX_BQ_IN_TIMEOUT, priv->base + RX_BQ_IN_TIMEOUT_TH);
+       writel_relaxed(TX_RQ_IN_TIMEOUT, priv->base + TX_RQ_IN_TIMEOUT_TH);
+
+       hix5hd2_set_desc_depth(priv, RX_DESC_NUM, TX_DESC_NUM);
+       hix5hd2_set_desc_addr(priv);
+}
+
+static void hix5hd2_irq_enable(struct hix5hd2_priv *priv)
+{
+       writel_relaxed(DEF_INT_MASK, priv->base + ENA_PMU_INT);
+}
+
+static void hix5hd2_irq_disable(struct hix5hd2_priv *priv)
+{
+       writel_relaxed(0, priv->base + ENA_PMU_INT);
+}
+
+static void hix5hd2_port_enable(struct hix5hd2_priv *priv)
+{
+       writel_relaxed(0xf, priv->base + DESC_WR_RD_ENA);
+       writel_relaxed(BITS_RX_EN | BITS_TX_EN, priv->base + PORT_EN);
+}
+
+static void hix5hd2_port_disable(struct hix5hd2_priv *priv)
+{
+       writel_relaxed(~(BITS_RX_EN | BITS_TX_EN), priv->base + PORT_EN);
+       writel_relaxed(0, priv->base + DESC_WR_RD_ENA);
+}
+
+static void hix5hd2_hw_set_mac_addr(struct net_device *dev)
+{
+       struct hix5hd2_priv *priv = netdev_priv(dev);
+       unsigned char *mac = dev->dev_addr;
+       u32 val;
+
+       val = mac[1] | (mac[0] << 8);
+       writel_relaxed(val, priv->base + STATION_ADDR_HIGH);
+
+       val = mac[5] | (mac[4] << 8) | (mac[3] << 16) | (mac[2] << 24);
+       writel_relaxed(val, priv->base + STATION_ADDR_LOW);
+}
+
+static int hix5hd2_net_set_mac_address(struct net_device *dev, void *p)
+{
+       int ret;
+
+       ret = eth_mac_addr(dev, p);
+       if (!ret)
+               hix5hd2_hw_set_mac_addr(dev);
+
+       return ret;
+}
+
+static void hix5hd2_adjust_link(struct net_device *dev)
+{
+       struct hix5hd2_priv *priv = netdev_priv(dev);
+       struct phy_device *phy = priv->phy;
+
+       if ((priv->speed != phy->speed) || (priv->duplex != phy->duplex)) {
+               hix5hd2_config_port(dev, phy->speed, phy->duplex);
+               phy_print_status(phy);
+       }
+}
+
+static void hix5hd2_rx_refill(struct hix5hd2_priv *priv)
+{
+       struct hix5hd2_desc *desc;
+       struct sk_buff *skb;
+       u32 start, end, num, pos, i;
+       u32 len = MAC_MAX_FRAME_SIZE;
+       dma_addr_t addr;
+
+       /* software write pointer */
+       start = dma_cnt(readl_relaxed(priv->base + RX_FQ_WR_ADDR));
+       /* logic read pointer */
+       end = dma_cnt(readl_relaxed(priv->base + RX_FQ_RD_ADDR));
+       num = CIRC_SPACE(start, end, RX_DESC_NUM);
+
+       for (i = 0, pos = start; i < num; i++) {
+               if (priv->rx_skb[pos]) {
+                       break;
+               } else {
+                       skb = netdev_alloc_skb_ip_align(priv->netdev, len);
+                       if (unlikely(skb == NULL))
+                               break;
+               }
+
+               addr = dma_map_single(priv->dev, skb->data, len, DMA_FROM_DEVICE);
+               if (dma_mapping_error(priv->dev, addr)) {
+                       dev_kfree_skb_any(skb);
+                       break;
+               }
+
+               desc = priv->rx_fq.desc + pos;
+               desc->buff_addr = cpu_to_le32(addr);
+               priv->rx_skb[pos] = skb;
+               desc->cmd = cpu_to_le32(DESC_VLD_FREE |
+                                       (len - 1) << DESC_BUFF_LEN_OFF);
+               pos = dma_ring_incr(pos, RX_DESC_NUM);
+       }
+
+       /* ensure desc updated */
+       wmb();
+
+       if (pos != start)
+               writel_relaxed(dma_byte(pos), priv->base + RX_FQ_WR_ADDR);
+}
+
+static int hix5hd2_rx(struct net_device *dev, int limit)
+{
+       struct hix5hd2_priv *priv = netdev_priv(dev);
+       struct sk_buff *skb;
+       struct hix5hd2_desc *desc;
+       dma_addr_t addr;
+       u32 start, end, num, pos, i, len;
+
+       /* software read pointer */
+       start = dma_cnt(readl_relaxed(priv->base + RX_BQ_RD_ADDR));
+       /* logic write pointer */
+       end = dma_cnt(readl_relaxed(priv->base + RX_BQ_WR_ADDR));
+       num = CIRC_CNT(end, start, RX_DESC_NUM);
+       if (num > limit)
+               num = limit;
+
+       /* ensure get updated desc */
+       rmb();
+       for (i = 0, pos = start; i < num; i++) {
+               skb = priv->rx_skb[pos];
+               if (unlikely(!skb)) {
+                       netdev_err(dev, "inconsistent rx_skb\n");
+                       break;
+               }
+               priv->rx_skb[pos] = NULL;
+
+               desc = priv->rx_bq.desc + pos;
+               len = (le32_to_cpu(desc->cmd) >> DESC_DATA_LEN_OFF) &
+                      DESC_DATA_MASK;
+               addr = le32_to_cpu(desc->buff_addr);
+               dma_unmap_single(priv->dev, addr, MAC_MAX_FRAME_SIZE,
+                                DMA_FROM_DEVICE);
+
+               skb_put(skb, len);
+               if (skb->len > MAC_MAX_FRAME_SIZE) {
+                       netdev_err(dev, "rcv len err, len = %d\n", skb->len);
+                       dev->stats.rx_errors++;
+                       dev->stats.rx_length_errors++;
+                       dev_kfree_skb_any(skb);
+                       goto next;
+               }
+
+               skb->protocol = eth_type_trans(skb, dev);
+               napi_gro_receive(&priv->napi, skb);
+               dev->stats.rx_packets++;
+               dev->stats.rx_bytes += skb->len;
+               dev->last_rx = jiffies;
+next:
+               pos = dma_ring_incr(pos, RX_DESC_NUM);
+       }
+
+       if (pos != start)
+               writel_relaxed(dma_byte(pos), priv->base + RX_BQ_RD_ADDR);
+
+       hix5hd2_rx_refill(priv);
+
+       return num;
+}
+
+static void hix5hd2_xmit_reclaim(struct net_device *dev)
+{
+       struct sk_buff *skb;
+       struct hix5hd2_desc *desc;
+       struct hix5hd2_priv *priv = netdev_priv(dev);
+       unsigned int bytes_compl = 0, pkts_compl = 0;
+       u32 start, end, num, pos, i;
+       dma_addr_t addr;
+
+       netif_tx_lock(dev);
+
+       /* software read */
+       start = dma_cnt(readl_relaxed(priv->base + TX_RQ_RD_ADDR));
+       /* logic write */
+       end = dma_cnt(readl_relaxed(priv->base + TX_RQ_WR_ADDR));
+       num = CIRC_CNT(end, start, TX_DESC_NUM);
+
+       for (i = 0, pos = start; i < num; i++) {
+               skb = priv->tx_skb[pos];
+               if (unlikely(!skb)) {
+                       netdev_err(dev, "inconsistent tx_skb\n");
+                       break;
+               }
+
+               pkts_compl++;
+               bytes_compl += skb->len;
+               desc = priv->tx_rq.desc + pos;
+               addr = le32_to_cpu(desc->buff_addr);
+               dma_unmap_single(priv->dev, addr, skb->len, DMA_TO_DEVICE);
+               priv->tx_skb[pos] = NULL;
+               dev_consume_skb_any(skb);
+               pos = dma_ring_incr(pos, TX_DESC_NUM);
+       }
+
+       if (pos != start)
+               writel_relaxed(dma_byte(pos), priv->base + TX_RQ_RD_ADDR);
+
+       netif_tx_unlock(dev);
+
+       if (pkts_compl || bytes_compl)
+               netdev_completed_queue(dev, pkts_compl, bytes_compl);
+
+       if (unlikely(netif_queue_stopped(priv->netdev)) && pkts_compl)
+               netif_wake_queue(priv->netdev);
+}
+
+static int hix5hd2_poll(struct napi_struct *napi, int budget)
+{
+       struct hix5hd2_priv *priv = container_of(napi,
+                               struct hix5hd2_priv, napi);
+       struct net_device *dev = priv->netdev;
+       int work_done = 0, task = budget;
+       int ints, num;
+
+       do {
+               hix5hd2_xmit_reclaim(dev);
+               num = hix5hd2_rx(dev, task);
+               work_done += num;
+               task -= num;
+               if ((work_done >= budget) || (num == 0))
+                       break;
+
+               ints = readl_relaxed(priv->base + RAW_PMU_INT);
+               writel_relaxed(ints, priv->base + RAW_PMU_INT);
+       } while (ints & DEF_INT_MASK);
+
+       if (work_done < budget) {
+               napi_complete(napi);
+               hix5hd2_irq_enable(priv);
+       }
+
+       return work_done;
+}
+
+static irqreturn_t hix5hd2_interrupt(int irq, void *dev_id)
+{
+       struct net_device *dev = (struct net_device *)dev_id;
+       struct hix5hd2_priv *priv = netdev_priv(dev);
+       int ints = readl_relaxed(priv->base + RAW_PMU_INT);
+
+       writel_relaxed(ints, priv->base + RAW_PMU_INT);
+       if (likely(ints & DEF_INT_MASK)) {
+               hix5hd2_irq_disable(priv);
+               napi_schedule(&priv->napi);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int hix5hd2_net_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct hix5hd2_priv *priv = netdev_priv(dev);
+       struct hix5hd2_desc *desc;
+       dma_addr_t addr;
+       u32 pos;
+
+       /* software write pointer */
+       pos = dma_cnt(readl_relaxed(priv->base + TX_BQ_WR_ADDR));
+       if (unlikely(priv->tx_skb[pos])) {
+               dev->stats.tx_dropped++;
+               dev->stats.tx_fifo_errors++;
+               netif_stop_queue(dev);
+               return NETDEV_TX_BUSY;
+       }
+
+       addr = dma_map_single(priv->dev, skb->data, skb->len, DMA_TO_DEVICE);
+       if (dma_mapping_error(priv->dev, addr)) {
+               dev_kfree_skb_any(skb);
+               return NETDEV_TX_OK;
+       }
+
+       desc = priv->tx_bq.desc + pos;
+       desc->buff_addr = cpu_to_le32(addr);
+       priv->tx_skb[pos] = skb;
+       desc->cmd = cpu_to_le32(DESC_VLD_BUSY | DESC_FL_FULL |
+                               (skb->len & DESC_DATA_MASK) << DESC_DATA_LEN_OFF |
+                               (skb->len & DESC_DATA_MASK) << DESC_BUFF_LEN_OFF);
+
+       /* ensure desc updated */
+       wmb();
+
+       pos = dma_ring_incr(pos, TX_DESC_NUM);
+       writel_relaxed(dma_byte(pos), priv->base + TX_BQ_WR_ADDR);
+
+       dev->trans_start = jiffies;
+       dev->stats.tx_packets++;
+       dev->stats.tx_bytes += skb->len;
+       netdev_sent_queue(dev, skb->len);
+
+       return NETDEV_TX_OK;
+}
+
+static void hix5hd2_free_dma_desc_rings(struct hix5hd2_priv *priv)
+{
+       struct hix5hd2_desc *desc;
+       dma_addr_t addr;
+       int i;
+
+       for (i = 0; i < RX_DESC_NUM; i++) {
+               struct sk_buff *skb = priv->rx_skb[i];
+               if (skb == NULL)
+                       continue;
+
+               desc = priv->rx_fq.desc + i;
+               addr = le32_to_cpu(desc->buff_addr);
+               dma_unmap_single(priv->dev, addr,
+                                MAC_MAX_FRAME_SIZE, DMA_FROM_DEVICE);
+               dev_kfree_skb_any(skb);
+               priv->rx_skb[i] = NULL;
+       }
+
+       for (i = 0; i < TX_DESC_NUM; i++) {
+               struct sk_buff *skb = priv->tx_skb[i];
+               if (skb == NULL)
+                       continue;
+
+               desc = priv->tx_rq.desc + i;
+               addr = le32_to_cpu(desc->buff_addr);
+               dma_unmap_single(priv->dev, addr, skb->len, DMA_TO_DEVICE);
+               dev_kfree_skb_any(skb);
+               priv->tx_skb[i] = NULL;
+       }
+}
+
+static int hix5hd2_net_open(struct net_device *dev)
+{
+       struct hix5hd2_priv *priv = netdev_priv(dev);
+       int ret;
+
+       ret = clk_prepare_enable(priv->clk);
+       if (ret < 0) {
+               netdev_err(dev, "failed to enable clk %d\n", ret);
+               return ret;
+       }
+
+       priv->phy = of_phy_connect(dev, priv->phy_node,
+                                  &hix5hd2_adjust_link, 0, priv->phy_mode);
+       if (!priv->phy)
+               return -ENODEV;
+
+       phy_start(priv->phy);
+       hix5hd2_hw_init(priv);
+       hix5hd2_rx_refill(priv);
+
+       netdev_reset_queue(dev);
+       netif_start_queue(dev);
+       napi_enable(&priv->napi);
+
+       hix5hd2_port_enable(priv);
+       hix5hd2_irq_enable(priv);
+
+       return 0;
+}
+
+static int hix5hd2_net_close(struct net_device *dev)
+{
+       struct hix5hd2_priv *priv = netdev_priv(dev);
+
+       hix5hd2_port_disable(priv);
+       hix5hd2_irq_disable(priv);
+       napi_disable(&priv->napi);
+       netif_stop_queue(dev);
+       hix5hd2_free_dma_desc_rings(priv);
+
+       if (priv->phy) {
+               phy_stop(priv->phy);
+               phy_disconnect(priv->phy);
+       }
+
+       clk_disable_unprepare(priv->clk);
+
+       return 0;
+}
+
+static void hix5hd2_tx_timeout_task(struct work_struct *work)
+{
+       struct hix5hd2_priv *priv;
+
+       priv = container_of(work, struct hix5hd2_priv, tx_timeout_task);
+       hix5hd2_net_close(priv->netdev);
+       hix5hd2_net_open(priv->netdev);
+}
+
+static void hix5hd2_net_timeout(struct net_device *dev)
+{
+       struct hix5hd2_priv *priv = netdev_priv(dev);
+
+       schedule_work(&priv->tx_timeout_task);
+}
+
+static const struct net_device_ops hix5hd2_netdev_ops = {
+       .ndo_open               = hix5hd2_net_open,
+       .ndo_stop               = hix5hd2_net_close,
+       .ndo_start_xmit         = hix5hd2_net_xmit,
+       .ndo_tx_timeout         = hix5hd2_net_timeout,
+       .ndo_set_mac_address    = hix5hd2_net_set_mac_address,
+};
+
+static int hix5hd2_get_settings(struct net_device *net_dev,
+                               struct ethtool_cmd *cmd)
+{
+       struct hix5hd2_priv *priv = netdev_priv(net_dev);
+
+       if (!priv->phy)
+               return -ENODEV;
+
+       return phy_ethtool_gset(priv->phy, cmd);
+}
+
+static int hix5hd2_set_settings(struct net_device *net_dev,
+                               struct ethtool_cmd *cmd)
+{
+       struct hix5hd2_priv *priv = netdev_priv(net_dev);
+
+       if (!priv->phy)
+               return -ENODEV;
+
+       return phy_ethtool_sset(priv->phy, cmd);
+}
+
+static struct ethtool_ops hix5hd2_ethtools_ops = {
+       .get_link               = ethtool_op_get_link,
+       .get_settings           = hix5hd2_get_settings,
+       .set_settings           = hix5hd2_set_settings,
+};
+
+static int hix5hd2_mdio_wait_ready(struct mii_bus *bus)
+{
+       struct hix5hd2_priv *priv = bus->priv;
+       void __iomem *base = priv->base;
+       int i, timeout = 10000;
+
+       for (i = 0; readl_relaxed(base + MDIO_SINGLE_CMD) & MDIO_START; i++) {
+               if (i == timeout)
+                       return -ETIMEDOUT;
+               usleep_range(10, 20);
+       }
+
+       return 0;
+}
+
+static int hix5hd2_mdio_read(struct mii_bus *bus, int phy, int reg)
+{
+       struct hix5hd2_priv *priv = bus->priv;
+       void __iomem *base = priv->base;
+       int val, ret;
+
+       ret = hix5hd2_mdio_wait_ready(bus);
+       if (ret < 0)
+               goto out;
+
+       writel_relaxed(MDIO_READ | phy << 8 | reg, base + MDIO_SINGLE_CMD);
+       ret = hix5hd2_mdio_wait_ready(bus);
+       if (ret < 0)
+               goto out;
+
+       val = readl_relaxed(base + MDIO_RDATA_STATUS);
+       if (val & MDIO_R_VALID) {
+               dev_err(bus->parent, "SMI bus read not valid\n");
+               ret = -ENODEV;
+               goto out;
+       }
+
+       val = readl_relaxed(priv->base + MDIO_SINGLE_DATA);
+       ret = (val >> 16) & 0xFFFF;
+out:
+       return ret;
+}
+
+static int hix5hd2_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
+{
+       struct hix5hd2_priv *priv = bus->priv;
+       void __iomem *base = priv->base;
+       int ret;
+
+       ret = hix5hd2_mdio_wait_ready(bus);
+       if (ret < 0)
+               goto out;
+
+       writel_relaxed(val, base + MDIO_SINGLE_DATA);
+       writel_relaxed(MDIO_WRITE | phy << 8 | reg, base + MDIO_SINGLE_CMD);
+       ret = hix5hd2_mdio_wait_ready(bus);
+out:
+       return ret;
+}
+
+static void hix5hd2_destroy_hw_desc_queue(struct hix5hd2_priv *priv)
+{
+       int i;
+
+       for (i = 0; i < QUEUE_NUMS; i++) {
+               if (priv->pool[i].desc) {
+                       dma_free_coherent(priv->dev, priv->pool[i].size,
+                                         priv->pool[i].desc,
+                                         priv->pool[i].phys_addr);
+                       priv->pool[i].desc = NULL;
+               }
+       }
+}
+
+static int hix5hd2_init_hw_desc_queue(struct hix5hd2_priv *priv)
+{
+       struct device *dev = priv->dev;
+       struct hix5hd2_desc *virt_addr;
+       dma_addr_t phys_addr;
+       int size, i;
+
+       priv->rx_fq.count = RX_DESC_NUM;
+       priv->rx_bq.count = RX_DESC_NUM;
+       priv->tx_bq.count = TX_DESC_NUM;
+       priv->tx_rq.count = TX_DESC_NUM;
+
+       for (i = 0; i < QUEUE_NUMS; i++) {
+               size = priv->pool[i].count * sizeof(struct hix5hd2_desc);
+               virt_addr = dma_alloc_coherent(dev, size, &phys_addr,
+                                              GFP_KERNEL);
+               if (virt_addr == NULL)
+                       goto error_free_pool;
+
+               memset(virt_addr, 0, size);
+               priv->pool[i].size = size;
+               priv->pool[i].desc = virt_addr;
+               priv->pool[i].phys_addr = phys_addr;
+       }
+       return 0;
+
+error_free_pool:
+       hix5hd2_destroy_hw_desc_queue(priv);
+
+       return -ENOMEM;
+}
+
+static int hix5hd2_dev_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node;
+       struct net_device *ndev;
+       struct hix5hd2_priv *priv;
+       struct resource *res;
+       struct mii_bus *bus;
+       const char *mac_addr;
+       int ret;
+
+       ndev = alloc_etherdev(sizeof(struct hix5hd2_priv));
+       if (!ndev)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, ndev);
+
+       priv = netdev_priv(ndev);
+       priv->dev = dev;
+       priv->netdev = ndev;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       priv->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(priv->base)) {
+               ret = PTR_ERR(priv->base);
+               goto out_free_netdev;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       priv->ctrl_base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(priv->ctrl_base)) {
+               ret = PTR_ERR(priv->ctrl_base);
+               goto out_free_netdev;
+       }
+
+       priv->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(priv->clk)) {
+               netdev_err(ndev, "failed to get clk\n");
+               ret = -ENODEV;
+               goto out_free_netdev;
+       }
+
+       ret = clk_prepare_enable(priv->clk);
+       if (ret < 0) {
+               netdev_err(ndev, "failed to enable clk %d\n", ret);
+               goto out_free_netdev;
+       }
+
+       bus = mdiobus_alloc();
+       if (bus == NULL) {
+               ret = -ENOMEM;
+               goto out_free_netdev;
+       }
+
+       bus->priv = priv;
+       bus->name = "hix5hd2_mii_bus";
+       bus->read = hix5hd2_mdio_read;
+       bus->write = hix5hd2_mdio_write;
+       bus->parent = &pdev->dev;
+       snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
+       priv->bus = bus;
+
+       ret = of_mdiobus_register(bus, node);
+       if (ret)
+               goto err_free_mdio;
+
+       priv->phy_mode = of_get_phy_mode(node);
+       if (priv->phy_mode < 0) {
+               netdev_err(ndev, "not find phy-mode\n");
+               ret = -EINVAL;
+               goto err_mdiobus;
+       }
+
+       priv->phy_node = of_parse_phandle(node, "phy-handle", 0);
+       if (!priv->phy_node) {
+               netdev_err(ndev, "not find phy-handle\n");
+               ret = -EINVAL;
+               goto err_mdiobus;
+       }
+
+       ndev->irq = platform_get_irq(pdev, 0);
+       if (ndev->irq <= 0) {
+               netdev_err(ndev, "No irq resource\n");
+               ret = -EINVAL;
+               goto out_phy_node;
+       }
+
+       ret = devm_request_irq(dev, ndev->irq, hix5hd2_interrupt,
+                              0, pdev->name, ndev);
+       if (ret) {
+               netdev_err(ndev, "devm_request_irq failed\n");
+               goto out_phy_node;
+       }
+
+       mac_addr = of_get_mac_address(node);
+       if (mac_addr)
+               ether_addr_copy(ndev->dev_addr, mac_addr);
+       if (!is_valid_ether_addr(ndev->dev_addr)) {
+               eth_hw_addr_random(ndev);
+               netdev_warn(ndev, "using random MAC address %pM\n",
+                           ndev->dev_addr);
+       }
+
+       INIT_WORK(&priv->tx_timeout_task, hix5hd2_tx_timeout_task);
+       ndev->watchdog_timeo = 6 * HZ;
+       ndev->priv_flags |= IFF_UNICAST_FLT;
+       ndev->netdev_ops = &hix5hd2_netdev_ops;
+       ndev->ethtool_ops = &hix5hd2_ethtools_ops;
+       SET_NETDEV_DEV(ndev, dev);
+
+       ret = hix5hd2_init_hw_desc_queue(priv);
+       if (ret)
+               goto out_phy_node;
+
+       netif_napi_add(ndev, &priv->napi, hix5hd2_poll, NAPI_POLL_WEIGHT);
+       ret = register_netdev(priv->netdev);
+       if (ret) {
+               netdev_err(ndev, "register_netdev failed!");
+               goto out_destroy_queue;
+       }
+
+       clk_disable_unprepare(priv->clk);
+
+       return ret;
+
+out_destroy_queue:
+       netif_napi_del(&priv->napi);
+       hix5hd2_destroy_hw_desc_queue(priv);
+out_phy_node:
+       of_node_put(priv->phy_node);
+err_mdiobus:
+       mdiobus_unregister(bus);
+err_free_mdio:
+       mdiobus_free(bus);
+out_free_netdev:
+       free_netdev(ndev);
+
+       return ret;
+}
+
+static int hix5hd2_dev_remove(struct platform_device *pdev)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct hix5hd2_priv *priv = netdev_priv(ndev);
+
+       netif_napi_del(&priv->napi);
+       unregister_netdev(ndev);
+       mdiobus_unregister(priv->bus);
+       mdiobus_free(priv->bus);
+
+       hix5hd2_destroy_hw_desc_queue(priv);
+       of_node_put(priv->phy_node);
+       cancel_work_sync(&priv->tx_timeout_task);
+       free_netdev(ndev);
+
+       return 0;
+}
+
+static const struct of_device_id hix5hd2_of_match[] = {
+       {.compatible = "hisilicon,hix5hd2-gmac",},
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, hix5hd2_of_match);
+
+static struct platform_driver hix5hd2_dev_driver = {
+       .driver = {
+               .name = "hix5hd2-gmac",
+               .of_match_table = hix5hd2_of_match,
+       },
+       .probe = hix5hd2_dev_probe,
+       .remove = hix5hd2_dev_remove,
+};
+
+module_platform_driver(hix5hd2_dev_driver);
+
+MODULE_DESCRIPTION("HISILICON HIX5HD2 Ethernet driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hix5hd2-gmac");
index 95837b99a464865a7ca47273cb49ce9b3200136b..85a3866459cf4a3bebc6bf6ee3fbe3ff254d48a4 100644 (file)
@@ -63,8 +63,8 @@ static int ehea_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
                cmd->duplex = port->full_duplex == 1 ?
                                                     DUPLEX_FULL : DUPLEX_HALF;
        } else {
-               speed = ~0;
-               cmd->duplex = -1;
+               speed = SPEED_UNKNOWN;
+               cmd->duplex = DUPLEX_UNKNOWN;
        }
        ethtool_cmd_speed_set(cmd, speed);
 
@@ -278,5 +278,5 @@ static const struct ethtool_ops ehea_ethtool_ops = {
 
 void ehea_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &ehea_ethtool_ops);
+       netdev->ethtool_ops = &ehea_ethtool_ops;
 }
index 538903bf13bce736161a96af324a2b9ebc9aecd9..a0b418e007a0d09303a32838d8acf1a06ca9d061 100644 (file)
@@ -28,6 +28,7 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <linux/device.h>
 #include <linux/in.h>
 #include <linux/ip.h>
 #include <linux/tcp.h>
@@ -3273,7 +3274,7 @@ static int ehea_probe_adapter(struct platform_device *dev)
                return -EINVAL;
        }
 
-       adapter = kzalloc(sizeof(*adapter), GFP_KERNEL);
+       adapter = devm_kzalloc(&dev->dev, sizeof(*adapter), GFP_KERNEL);
        if (!adapter) {
                ret = -ENOMEM;
                dev_err(&dev->dev, "no mem for ehea_adapter\n");
@@ -3359,7 +3360,6 @@ out_kill_eq:
 
 out_free_ad:
        list_del(&adapter->list);
-       kfree(adapter);
 
 out:
        ehea_update_firmware_handles();
@@ -3386,7 +3386,6 @@ static int ehea_remove(struct platform_device *dev)
        ehea_destroy_eq(adapter->neq);
        ehea_remove_adapter_mr(adapter);
        list_del(&adapter->list);
-       kfree(adapter);
 
        ehea_update_firmware_handles();
 
index 9b03033bb5576f52d7c6f687f9e1a99c8e7a21a5..a0820f72b25c88bf9141354231813864c8159fec 100644 (file)
@@ -103,12 +103,14 @@ out_nomem:
 
 static void hw_queue_dtor(struct hw_queue *queue)
 {
-       int pages_per_kpage = PAGE_SIZE / queue->pagesize;
+       int pages_per_kpage;
        int i, nr_pages;
 
        if (!queue || !queue->queue_pages)
                return;
 
+       pages_per_kpage = PAGE_SIZE / queue->pagesize;
+
        nr_pages = queue->queue_length / queue->pagesize;
 
        for (i = 0; i < nr_pages; i += pages_per_kpage)
index ae342fdb42c8e79853507007ad2ea6c8d3681fad..87bd953cc2eeaef7f6af65902841645e98f7eda5 100644 (file)
@@ -2879,7 +2879,7 @@ static int emac_probe(struct platform_device *ofdev)
                dev->commac.ops = &emac_commac_sg_ops;
        } else
                ndev->netdev_ops = &emac_netdev_ops;
-       SET_ETHTOOL_OPS(ndev, &emac_ethtool_ops);
+       ndev->ethtool_ops = &emac_ethtool_ops;
 
        netif_carrier_off(ndev);
 
index 25045ae071711f03cee9ccabf9898cf2abebbc21..5727779a7df27477bee22c8bad9ac8253b083aea 100644 (file)
@@ -2245,7 +2245,7 @@ static int ipg_probe(struct pci_dev *pdev, const struct pci_device_id *id)
         */
        dev->netdev_ops = &ipg_netdev_ops;
        SET_NETDEV_DEV(dev, &pdev->dev);
-       SET_ETHTOOL_OPS(dev, &ipg_ethtool_ops);
+       dev->ethtool_ops = &ipg_ethtool_ops;
 
        rc = pci_request_regions(pdev, DRV_NAME);
        if (rc)
index b56461ce674c7832152cdab4c0b4c3b27789d9a4..9d979d7debef0fd1cb3ec80ca3dd8d908b6cd33b 100644 (file)
@@ -2854,7 +2854,7 @@ static int e100_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        netdev->hw_features |= NETIF_F_RXALL;
 
        netdev->netdev_ops = &e100_netdev_ops;
-       SET_ETHTOOL_OPS(netdev, &e100_ethtool_ops);
+       netdev->ethtool_ops = &e100_ethtool_ops;
        netdev->watchdog_timeo = E100_WATCHDOG_PERIOD;
        strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);
 
index 73a8aeefb92a46d13a4c73be6cade2c5d694e00f..d50f78afb56d8715d8b90d936216d22bacaaa70e 100644 (file)
@@ -168,8 +168,8 @@ static int e1000_get_settings(struct net_device *netdev,
                else
                        ecmd->duplex = DUPLEX_HALF;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
 
        ecmd->autoneg = ((hw->media_type == e1000_media_type_fiber) ||
@@ -1460,7 +1460,8 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter)
                         * enough time to complete the receives, if it's
                         * exceeded, break and error off
                         */
-               } while (good_cnt < 64 && jiffies < (time + 20));
+               } while (good_cnt < 64 && time_after(time + 20, jiffies));
+
                if (good_cnt != 64) {
                        ret_val = 13; /* ret_val is the same as mis-compare */
                        break;
@@ -1905,5 +1906,5 @@ static const struct ethtool_ops e1000_ethtool_ops = {
 
 void e1000_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &e1000_ethtool_ops);
+       netdev->ethtool_ops = &e1000_ethtool_ops;
 }
index c1d3fdb296a05e4f1cd86205bce12669e41ceb6b..e9b07ccc0ebaebc0b1b0993625485cae93b0b447 100644 (file)
@@ -4877,10 +4877,10 @@ void e1000_tbi_adjust_stats(struct e1000_hw *hw, struct e1000_hw_stats *stats,
         * since the test for a multicast frame will test positive on
         * a broadcast frame.
         */
-       if ((mac_addr[0] == (u8) 0xff) && (mac_addr[1] == (u8) 0xff))
+       if (is_broadcast_ether_addr(mac_addr))
                /* Broadcast packet */
                stats->bprc++;
-       else if (*mac_addr & 0x01)
+       else if (is_multicast_ether_addr(mac_addr))
                /* Multicast packet */
                stats->mprc++;
 
index 27058dfe418bee10f0c85ecf971fc419c346582f..660971f304b242f03e49d4cfb910fde35df4651a 100644 (file)
@@ -3105,11 +3105,6 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
         */
        tx_ring = adapter->tx_ring;
 
-       if (unlikely(skb->len <= 0)) {
-               dev_kfree_skb_any(skb);
-               return NETDEV_TX_OK;
-       }
-
        /* On PCI/PCI-X HW, if packet size is less than ETH_ZLEN,
         * packets may get corrupted during padding by HW.
         * To WA this issue, pad all small packets manually.
index a5f6b11d6992e63aa9af8da9f52540697455b2d1..08f22f348800ddd37aaf2d2d87eb4ce0e9c7f3b3 100644 (file)
@@ -1365,6 +1365,7 @@ static const struct e1000_mac_operations es2_mac_ops = {
        .setup_led              = e1000e_setup_led_generic,
        .config_collision_dist  = e1000e_config_collision_dist_generic,
        .rar_set                = e1000e_rar_set_generic,
+       .rar_get_count          = e1000e_rar_get_count_generic,
 };
 
 static const struct e1000_phy_operations es2_phy_ops = {
index e0aa7f1efb08ceb50d9049128dcbdb66f442b17d..218481e509f99f4e32deba736ae4117365c650e3 100644 (file)
@@ -1896,6 +1896,7 @@ static const struct e1000_mac_operations e82571_mac_ops = {
        .config_collision_dist  = e1000e_config_collision_dist_generic,
        .read_mac_addr          = e1000_read_mac_addr_82571,
        .rar_set                = e1000e_rar_set_generic,
+       .rar_get_count          = e1000e_rar_get_count_generic,
 };
 
 static const struct e1000_phy_operations e82_phy_ops_igp = {
index 1471c5464a89e72d87aa571d4a1b15d791a3f015..7785240a0da1a1b409cfbc3d4b18ff63b5f2966a 100644 (file)
@@ -265,10 +265,10 @@ struct e1000_adapter {
        u32 tx_hwtstamp_timeouts;
 
        /* Rx */
-       bool (*clean_rx) (struct e1000_ring *ring, int *work_done,
-                         int work_to_do) ____cacheline_aligned_in_smp;
-       void (*alloc_rx_buf) (struct e1000_ring *ring, int cleaned_count,
-                             gfp_t gfp);
+       bool (*clean_rx)(struct e1000_ring *ring, int *work_done,
+                        int work_to_do) ____cacheline_aligned_in_smp;
+       void (*alloc_rx_buf)(struct e1000_ring *ring, int cleaned_count,
+                            gfp_t gfp);
        struct e1000_ring *rx_ring;
 
        u32 rx_int_delay;
@@ -391,6 +391,8 @@ s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca);
  * 25MHz       46-bit  2^46 / 10^9 / 3600 = 19.55 hours
  */
 #define E1000_SYSTIM_OVERFLOW_PERIOD   (HZ * 60 * 60 * 4)
+#define E1000_MAX_82574_SYSTIM_REREADS 50
+#define E1000_82574_SYSTIM_EPSILON     (1ULL << 35ULL)
 
 /* hardware capability, feature, and workaround flags */
 #define FLAG_HAS_AMT                      (1 << 0)
@@ -573,35 +575,8 @@ static inline u32 __er32(struct e1000_hw *hw, unsigned long reg)
 
 #define er32(reg)      __er32(hw, E1000_##reg)
 
-/**
- * __ew32_prepare - prepare to write to MAC CSR register on certain parts
- * @hw: pointer to the HW structure
- *
- * When updating the MAC CSR registers, the Manageability Engine (ME) could
- * be accessing the registers at the same time.  Normally, this is handled in
- * h/w by an arbiter but on some parts there is a bug that acknowledges Host
- * accesses later than it should which could result in the register to have
- * an incorrect value.  Workaround this by checking the FWSM register which
- * has bit 24 set while ME is accessing MAC CSR registers, wait if it is set
- * and try again a number of times.
- **/
-static inline s32 __ew32_prepare(struct e1000_hw *hw)
-{
-       s32 i = E1000_ICH_FWSM_PCIM2PCI_COUNT;
-
-       while ((er32(FWSM) & E1000_ICH_FWSM_PCIM2PCI) && --i)
-               udelay(50);
-
-       return i;
-}
-
-static inline void __ew32(struct e1000_hw *hw, unsigned long reg, u32 val)
-{
-       if (hw->adapter->flags2 & FLAG2_PCIM2PCI_ARBITER_WA)
-               __ew32_prepare(hw);
-
-       writel(val, hw->hw_addr + reg);
-}
+s32 __ew32_prepare(struct e1000_hw *hw);
+void __ew32(struct e1000_hw *hw, unsigned long reg, u32 val);
 
 #define ew32(reg, val) __ew32(hw, E1000_##reg, (val))
 
index cad250bc1b99fc81d51fb8956eee74c9acc3bc7e..815e26c6d34b85a54c554de5863e200302d6eacd 100644 (file)
@@ -159,8 +159,8 @@ static int e1000_get_settings(struct net_device *netdev,
                ecmd->transceiver = XCVR_EXTERNAL;
        }
 
-       speed = -1;
-       ecmd->duplex = -1;
+       speed = SPEED_UNKNOWN;
+       ecmd->duplex = DUPLEX_UNKNOWN;
 
        if (netif_running(netdev)) {
                if (netif_carrier_ok(netdev)) {
@@ -169,6 +169,7 @@ static int e1000_get_settings(struct net_device *netdev,
                }
        } else if (!pm_runtime_suspended(netdev->dev.parent)) {
                u32 status = er32(STATUS);
+
                if (status & E1000_STATUS_LU) {
                        if (status & E1000_STATUS_SPEED_1000)
                                speed = SPEED_1000;
@@ -783,25 +784,26 @@ static bool reg_pattern_test(struct e1000_adapter *adapter, u64 *data,
                              reg + (offset << 2), val,
                              (test[pat] & write & mask));
                        *data = reg;
-                       return 1;
+                       return true;
                }
        }
-       return 0;
+       return false;
 }
 
 static bool reg_set_and_check(struct e1000_adapter *adapter, u64 *data,
                              int reg, u32 mask, u32 write)
 {
        u32 val;
+
        __ew32(&adapter->hw, reg, write & mask);
        val = __er32(&adapter->hw, reg);
        if ((write & mask) != (val & mask)) {
                e_err("set/check test failed (reg 0x%05X): got 0x%08X expected 0x%08X\n",
                      reg, (val & mask), (write & mask));
                *data = reg;
-               return 1;
+               return true;
        }
-       return 0;
+       return false;
 }
 
 #define REG_PATTERN_TEST_ARRAY(reg, offset, mask, write)                       \
@@ -1717,6 +1719,7 @@ static int e1000_link_test(struct e1000_adapter *adapter, u64 *data)
        *data = 0;
        if (hw->phy.media_type == e1000_media_type_internal_serdes) {
                int i = 0;
+
                hw->mac.serdes_has_link = false;
 
                /* On some blade server designs, link establishment
@@ -2315,5 +2318,5 @@ static const struct ethtool_ops e1000_ethtool_ops = {
 
 void e1000e_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &e1000_ethtool_ops);
+       netdev->ethtool_ops = &e1000_ethtool_ops;
 }
index 6b3de5f39a97862e2f5742b14a24530ba6ecc33f..72f5475c4b9093d075e608070c4415a4e0ad5afd 100644 (file)
@@ -469,8 +469,9 @@ struct e1000_mac_operations {
        s32  (*setup_led)(struct e1000_hw *);
        void (*write_vfta)(struct e1000_hw *, u32, u32);
        void (*config_collision_dist)(struct e1000_hw *);
-       void (*rar_set)(struct e1000_hw *, u8 *, u32);
+       int  (*rar_set)(struct e1000_hw *, u8 *, u32);
        s32  (*read_mac_addr)(struct e1000_hw *);
+       u32  (*rar_get_count)(struct e1000_hw *);
 };
 
 /* When to use various PHY register access functions:
index f0bbd4246d71d857eba4c439cac33e0c8a930864..8894ab8ed6bd82de2c0f720939d31a906096a12c 100644 (file)
@@ -139,8 +139,9 @@ static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link);
 static s32 e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw);
 static bool e1000_check_mng_mode_ich8lan(struct e1000_hw *hw);
 static bool e1000_check_mng_mode_pchlan(struct e1000_hw *hw);
-static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index);
-static void e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index);
+static int e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index);
+static int e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index);
+static u32 e1000_rar_get_count_pch_lpt(struct e1000_hw *hw);
 static s32 e1000_k1_workaround_lv(struct e1000_hw *hw);
 static void e1000_gate_hw_phy_config_ich8lan(struct e1000_hw *hw, bool gate);
 static s32 e1000_disable_ulp_lpt_lp(struct e1000_hw *hw, bool force);
@@ -704,6 +705,7 @@ static s32 e1000_init_mac_params_ich8lan(struct e1000_hw *hw)
                mac->ops.rar_set = e1000_rar_set_pch_lpt;
                mac->ops.setup_physical_interface =
                    e1000_setup_copper_link_pch_lpt;
+               mac->ops.rar_get_count = e1000_rar_get_count_pch_lpt;
        }
 
        /* Enable PCS Lock-loss workaround for ICH8 */
@@ -1334,6 +1336,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
        if (((hw->mac.type == e1000_pch2lan) ||
             (hw->mac.type == e1000_pch_lpt)) && link) {
                u32 reg;
+
                reg = er32(STATUS);
                if (!(reg & (E1000_STATUS_FD | E1000_STATUS_SPEED_MASK))) {
                        u16 emi_addr;
@@ -1634,9 +1637,9 @@ static bool e1000_check_mng_mode_ich8lan(struct e1000_hw *hw)
        u32 fwsm;
 
        fwsm = er32(FWSM);
-       return ((fwsm & E1000_ICH_FWSM_FW_VALID) &&
+       return (fwsm & E1000_ICH_FWSM_FW_VALID) &&
                ((fwsm & E1000_FWSM_MODE_MASK) ==
-                (E1000_ICH_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT)));
+                (E1000_ICH_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT));
 }
 
 /**
@@ -1667,7 +1670,7 @@ static bool e1000_check_mng_mode_pchlan(struct e1000_hw *hw)
  *  contain the MAC address but RAR[1-6] are reserved for manageability (ME).
  *  Use SHRA[0-3] in place of those reserved for ME.
  **/
-static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index)
+static int e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index)
 {
        u32 rar_low, rar_high;
 
@@ -1689,7 +1692,7 @@ static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index)
                e1e_flush();
                ew32(RAH(index), rar_high);
                e1e_flush();
-               return;
+               return 0;
        }
 
        /* RAR[1-6] are owned by manageability.  Skip those and program the
@@ -1712,7 +1715,7 @@ static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index)
                /* verify the register updates */
                if ((er32(SHRAL(index - 1)) == rar_low) &&
                    (er32(SHRAH(index - 1)) == rar_high))
-                       return;
+                       return 0;
 
                e_dbg("SHRA[%d] might be locked by ME - FWSM=0x%8.8x\n",
                      (index - 1), er32(FWSM));
@@ -1720,6 +1723,43 @@ static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index)
 
 out:
        e_dbg("Failed to write receive address at index %d\n", index);
+       return -E1000_ERR_CONFIG;
+}
+
+/**
+ *  e1000_rar_get_count_pch_lpt - Get the number of available SHRA
+ *  @hw: pointer to the HW structure
+ *
+ *  Get the number of available receive registers that the Host can
+ *  program. SHRA[0-10] are the shared receive address registers
+ *  that are shared between the Host and manageability engine (ME).
+ *  ME can reserve any number of addresses and the host needs to be
+ *  able to tell how many available registers it has access to.
+ **/
+static u32 e1000_rar_get_count_pch_lpt(struct e1000_hw *hw)
+{
+       u32 wlock_mac;
+       u32 num_entries;
+
+       wlock_mac = er32(FWSM) & E1000_FWSM_WLOCK_MAC_MASK;
+       wlock_mac >>= E1000_FWSM_WLOCK_MAC_SHIFT;
+
+       switch (wlock_mac) {
+       case 0:
+               /* All SHRA[0..10] and RAR[0] available */
+               num_entries = hw->mac.rar_entry_count;
+               break;
+       case 1:
+               /* Only RAR[0] available */
+               num_entries = 1;
+               break;
+       default:
+               /* SHRA[0..(wlock_mac - 1)] available + RAR[0] */
+               num_entries = wlock_mac + 1;
+               break;
+       }
+
+       return num_entries;
 }
 
 /**
@@ -1733,7 +1773,7 @@ out:
  *  contain the MAC address. SHRA[0-10] are the shared receive address
  *  registers that are shared between the Host and manageability engine (ME).
  **/
-static void e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index)
+static int e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index)
 {
        u32 rar_low, rar_high;
        u32 wlock_mac;
@@ -1755,7 +1795,7 @@ static void e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index)
                e1e_flush();
                ew32(RAH(index), rar_high);
                e1e_flush();
-               return;
+               return 0;
        }
 
        /* The manageability engine (ME) can lock certain SHRAR registers that
@@ -1787,12 +1827,13 @@ static void e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index)
                        /* verify the register updates */
                        if ((er32(SHRAL_PCH_LPT(index - 1)) == rar_low) &&
                            (er32(SHRAH_PCH_LPT(index - 1)) == rar_high))
-                               return;
+                               return 0;
                }
        }
 
 out:
        e_dbg("Failed to write receive address at index %d\n", index);
+       return -E1000_ERR_CONFIG;
 }
 
 /**
@@ -4976,6 +5017,7 @@ static const struct e1000_mac_operations ich8_mac_ops = {
        /* id_led_init dependent on mac type */
        .config_collision_dist  = e1000e_config_collision_dist_generic,
        .rar_set                = e1000e_rar_set_generic,
+       .rar_get_count          = e1000e_rar_get_count_generic,
 };
 
 static const struct e1000_phy_operations ich8_phy_ops = {
index baa0a466d1d05ca533999b9ffcac3846a476ab5f..8c386f3a15ebb6649e347f5316555827b151819b 100644 (file)
@@ -211,6 +211,11 @@ s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw)
        return 0;
 }
 
+u32 e1000e_rar_get_count_generic(struct e1000_hw *hw)
+{
+       return hw->mac.rar_entry_count;
+}
+
 /**
  *  e1000e_rar_set_generic - Set receive address register
  *  @hw: pointer to the HW structure
@@ -220,7 +225,7 @@ s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw)
  *  Sets the receive address array register at index to the address passed
  *  in by addr.
  **/
-void e1000e_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index)
+int e1000e_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index)
 {
        u32 rar_low, rar_high;
 
@@ -244,6 +249,8 @@ void e1000e_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index)
        e1e_flush();
        ew32(RAH(index), rar_high);
        e1e_flush();
+
+       return 0;
 }
 
 /**
index 4e81c2825b7a1ca60b9a870ce61737e29d4e7972..0513d90cdeeaa980b3fb226eaac6feebe481c4f3 100644 (file)
@@ -61,7 +61,8 @@ void e1000e_update_adaptive(struct e1000_hw *hw);
 void e1000_write_vfta_generic(struct e1000_hw *hw, u32 offset, u32 value);
 
 void e1000_set_lan_id_multi_port_pcie(struct e1000_hw *hw);
-void e1000e_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index);
+u32 e1000e_rar_get_count_generic(struct e1000_hw *hw);
+int e1000e_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index);
 void e1000e_config_collision_dist_generic(struct e1000_hw *hw);
 
 #endif
index 3e69386add04ab43bc083d92411734320fc9845f..201cc93f36256d5776e882399d3afdf594b81192 100644 (file)
@@ -123,6 +123,36 @@ static const struct e1000_reg_info e1000_reg_info_tbl[] = {
        {0, NULL}
 };
 
+/**
+ * __ew32_prepare - prepare to write to MAC CSR register on certain parts
+ * @hw: pointer to the HW structure
+ *
+ * When updating the MAC CSR registers, the Manageability Engine (ME) could
+ * be accessing the registers at the same time.  Normally, this is handled in
+ * h/w by an arbiter but on some parts there is a bug that acknowledges Host
+ * accesses later than it should which could result in the register to have
+ * an incorrect value.  Workaround this by checking the FWSM register which
+ * has bit 24 set while ME is accessing MAC CSR registers, wait if it is set
+ * and try again a number of times.
+ **/
+s32 __ew32_prepare(struct e1000_hw *hw)
+{
+       s32 i = E1000_ICH_FWSM_PCIM2PCI_COUNT;
+
+       while ((er32(FWSM) & E1000_ICH_FWSM_PCIM2PCI) && --i)
+               udelay(50);
+
+       return i;
+}
+
+void __ew32(struct e1000_hw *hw, unsigned long reg, u32 val)
+{
+       if (hw->adapter->flags2 & FLAG2_PCIM2PCI_ARBITER_WA)
+               __ew32_prepare(hw);
+
+       writel(val, hw->hw_addr + reg);
+}
+
 /**
  * e1000_regdump - register printout routine
  * @hw: pointer to the HW structure
@@ -599,6 +629,7 @@ static void e1000e_update_rdt_wa(struct e1000_ring *rx_ring, unsigned int i)
 
        if (unlikely(!ret_val && (i != readl(rx_ring->tail)))) {
                u32 rctl = er32(RCTL);
+
                ew32(RCTL, rctl & ~E1000_RCTL_EN);
                e_err("ME firmware caused invalid RDT - resetting\n");
                schedule_work(&adapter->reset_task);
@@ -615,6 +646,7 @@ static void e1000e_update_tdt_wa(struct e1000_ring *tx_ring, unsigned int i)
 
        if (unlikely(!ret_val && (i != readl(tx_ring->tail)))) {
                u32 tctl = er32(TCTL);
+
                ew32(TCTL, tctl & ~E1000_TCTL_EN);
                e_err("ME firmware caused invalid TDT - resetting\n");
                schedule_work(&adapter->reset_task);
@@ -1198,6 +1230,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 */
                for (; !cleaned; count++) {
                        tx_desc = E1000_TX_DESC(*tx_ring, i);
@@ -1753,6 +1786,7 @@ static irqreturn_t e1000_intr_msi(int __always_unused irq, void *data)
                    adapter->flags & FLAG_RX_NEEDS_RESTART) {
                        /* disable receives */
                        u32 rctl = er32(RCTL);
+
                        ew32(RCTL, rctl & ~E1000_RCTL_EN);
                        adapter->flags |= FLAG_RESTART_NOW;
                }
@@ -1960,6 +1994,7 @@ static void e1000_configure_msix(struct e1000_adapter *adapter)
        /* Workaround issue with spurious interrupts on 82574 in MSI-X mode */
        if (hw->mac.type == e1000_82574) {
                u32 rfctl = er32(RFCTL);
+
                rfctl |= E1000_RFCTL_ACK_DIS;
                ew32(RFCTL, rfctl);
        }
@@ -2204,6 +2239,7 @@ static void e1000_irq_disable(struct e1000_adapter *adapter)
 
        if (adapter->msix_entries) {
                int i;
+
                for (i = 0; i < adapter->num_vectors; i++)
                        synchronize_irq(adapter->msix_entries[i].vector);
        } else {
@@ -2921,6 +2957,7 @@ static void e1000_configure_tx(struct e1000_adapter *adapter)
 
        if (adapter->flags2 & FLAG2_DMA_BURST) {
                u32 txdctl = er32(TXDCTL(0));
+
                txdctl &= ~(E1000_TXDCTL_PTHRESH | E1000_TXDCTL_HTHRESH |
                            E1000_TXDCTL_WTHRESH);
                /* set up some performance related parameters to encourage the
@@ -3239,6 +3276,7 @@ static void e1000_configure_rx(struct e1000_adapter *adapter)
 
                if (adapter->flags & FLAG_IS_ICH) {
                        u32 rxdctl = er32(RXDCTL(0));
+
                        ew32(RXDCTL(0), rxdctl | 0x3);
                }
 
@@ -3303,9 +3341,11 @@ static int e1000e_write_uc_addr_list(struct net_device *netdev)
 {
        struct e1000_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
-       unsigned int rar_entries = hw->mac.rar_entry_count;
+       unsigned int rar_entries;
        int count = 0;
 
+       rar_entries = hw->mac.ops.rar_get_count(hw);
+
        /* save a rar entry for our hardware address */
        rar_entries--;
 
@@ -3324,9 +3364,13 @@ static int e1000e_write_uc_addr_list(struct net_device *netdev)
                 * combining
                 */
                netdev_for_each_uc_addr(ha, netdev) {
+                       int rval;
+
                        if (!rar_entries)
                                break;
-                       hw->mac.ops.rar_set(hw, ha->addr, rar_entries--);
+                       rval = hw->mac.ops.rar_set(hw, ha->addr, rar_entries--);
+                       if (rval < 0)
+                               return -ENOMEM;
                        count++;
                }
        }
@@ -4085,12 +4129,37 @@ static cycle_t e1000e_cyclecounter_read(const struct cyclecounter *cc)
        struct e1000_adapter *adapter = container_of(cc, struct e1000_adapter,
                                                     cc);
        struct e1000_hw *hw = &adapter->hw;
-       cycle_t systim;
+       cycle_t systim, systim_next;
 
        /* latch SYSTIMH on read of SYSTIML */
        systim = (cycle_t)er32(SYSTIML);
        systim |= (cycle_t)er32(SYSTIMH) << 32;
 
+       if ((hw->mac.type == e1000_82574) || (hw->mac.type == e1000_82583)) {
+               u64 incvalue, time_delta, rem, temp;
+               int i;
+
+               /* errata for 82574/82583 possible bad bits read from SYSTIMH/L
+                * check to see that the time is incrementing at a reasonable
+                * rate and is a multiple of incvalue
+                */
+               incvalue = er32(TIMINCA) & E1000_TIMINCA_INCVALUE_MASK;
+               for (i = 0; i < E1000_MAX_82574_SYSTIM_REREADS; i++) {
+                       /* latch SYSTIMH on read of SYSTIML */
+                       systim_next = (cycle_t)er32(SYSTIML);
+                       systim_next |= (cycle_t)er32(SYSTIMH) << 32;
+
+                       time_delta = systim_next - systim;
+                       temp = time_delta;
+                       rem = do_div(temp, incvalue);
+
+                       systim = systim_next;
+
+                       if ((time_delta < E1000_82574_SYSTIM_EPSILON) &&
+                           (rem == 0))
+                               break;
+               }
+       }
        return systim;
 }
 
@@ -4491,7 +4560,7 @@ static void e1000e_update_phy_task(struct work_struct *work)
        e1000_get_phy_info(hw);
 
        /* Enable EEE on 82579 after link up */
-       if (hw->phy.type == e1000_phy_82579)
+       if (hw->phy.type >= e1000_phy_82579)
                e1000_set_eee_pchlan(hw);
 }
 
@@ -4695,6 +4764,7 @@ static void e1000e_update_stats(struct e1000_adapter *adapter)
        /* Correctable ECC Errors */
        if (hw->mac.type == e1000_pch_lpt) {
                u32 pbeccsts = er32(PBECCSTS);
+
                adapter->corr_errors +=
                    pbeccsts & E1000_PBECCSTS_CORR_ERR_CNT_MASK;
                adapter->uncorr_errors +=
@@ -4808,6 +4878,7 @@ static void e1000e_enable_receives(struct e1000_adapter *adapter)
            (adapter->flags & FLAG_RESTART_NOW)) {
                struct e1000_hw *hw = &adapter->hw;
                u32 rctl = er32(RCTL);
+
                ew32(RCTL, rctl | E1000_RCTL_EN);
                adapter->flags &= ~FLAG_RESTART_NOW;
        }
@@ -4930,6 +5001,7 @@ static void e1000_watchdog_task(struct work_struct *work)
                        if ((adapter->flags & FLAG_TARC_SPEED_MODE_BIT) &&
                            !txb2b) {
                                u32 tarc0;
+
                                tarc0 = er32(TARC(0));
                                tarc0 &= ~SPEED_MODE_BIT;
                                ew32(TARC(0), tarc0);
@@ -5170,7 +5242,7 @@ static bool e1000_tx_csum(struct e1000_ring *tx_ring, struct sk_buff *skb)
        __be16 protocol;
 
        if (skb->ip_summed != CHECKSUM_PARTIAL)
-               return 0;
+               return false;
 
        if (skb->protocol == cpu_to_be16(ETH_P_8021Q))
                protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
@@ -5215,7 +5287,7 @@ static bool e1000_tx_csum(struct e1000_ring *tx_ring, struct sk_buff *skb)
                i = 0;
        tx_ring->next_to_use = i;
 
-       return 1;
+       return true;
 }
 
 static int e1000_tx_map(struct e1000_ring *tx_ring, struct sk_buff *skb,
@@ -6209,6 +6281,7 @@ static int __e1000_resume(struct pci_dev *pdev)
                e1e_wphy(&adapter->hw, BM_WUS, ~0);
        } else {
                u32 wus = er32(WUS);
+
                if (wus) {
                        e_info("MAC Wakeup cause - %s\n",
                               wus & E1000_WUS_EX ? "Unicast Packet" :
@@ -7027,7 +7100,7 @@ static const struct pci_error_handlers e1000_err_handler = {
        .resume = e1000_io_resume,
 };
 
-static DEFINE_PCI_DEVICE_TABLE(e1000_pci_tbl) = {
+static const struct pci_device_id e1000_pci_tbl[] = {
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_COPPER), board_82571 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_FIBER), board_82571 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_COPPER), board_82571 },
@@ -7144,6 +7217,7 @@ static struct pci_driver e1000_driver = {
 static int __init e1000_init_module(void)
 {
        int ret;
+
        pr_info("Intel(R) PRO/1000 Network Driver - %s\n",
                e1000e_driver_version);
        pr_info("Copyright(c) 1999 - 2014 Intel Corporation.\n");
index a9a976f04bffe957e22b0852bd8918c6d6f63bd0..b1f212b7baf7e71f0b2a3a160463898019778a00 100644 (file)
@@ -398,6 +398,7 @@ s32 e1000e_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
                /* Loop to allow for up to whole page write of eeprom */
                while (widx < words) {
                        u16 word_out = data[widx];
+
                        word_out = (word_out >> 8) | (word_out << 8);
                        e1000_shift_out_eec_bits(hw, word_out, 16);
                        widx++;
index d0ac0f3249c886415d308c4a0cd376feda3d44db..aa1923f7ebdd2e56dd0ebd436feb69164426ad7c 100644 (file)
@@ -436,6 +436,7 @@ void e1000e_check_options(struct e1000_adapter *adapter)
 
                if (num_IntMode > bd) {
                        unsigned int int_mode = IntMode[bd];
+
                        e1000_validate_option(&int_mode, &opt, adapter);
                        adapter->int_mode = int_mode;
                } else {
@@ -457,6 +458,7 @@ void e1000e_check_options(struct e1000_adapter *adapter)
 
                if (num_SmartPowerDownEnable > bd) {
                        unsigned int spd = SmartPowerDownEnable[bd];
+
                        e1000_validate_option(&spd, &opt, adapter);
                        if ((adapter->flags & FLAG_HAS_SMART_POWER_DOWN) && spd)
                                adapter->flags |= FLAG_SMART_POWER_DOWN;
@@ -473,6 +475,7 @@ void e1000e_check_options(struct e1000_adapter *adapter)
 
                if (num_CrcStripping > bd) {
                        unsigned int crc_stripping = CrcStripping[bd];
+
                        e1000_validate_option(&crc_stripping, &opt, adapter);
                        if (crc_stripping == OPTION_ENABLED) {
                                adapter->flags2 |= FLAG2_CRC_STRIPPING;
@@ -495,6 +498,7 @@ void e1000e_check_options(struct e1000_adapter *adapter)
 
                if (num_KumeranLockLoss > bd) {
                        unsigned int kmrn_lock_loss = KumeranLockLoss[bd];
+
                        e1000_validate_option(&kmrn_lock_loss, &opt, adapter);
                        enabled = kmrn_lock_loss;
                }
index 00b3fc98bf309bf3d371a984f0da7245caf07013..b2005e13fb01583a10f58aa37339feb2336191bd 100644 (file)
@@ -2896,6 +2896,7 @@ static s32 __e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data,
                    (hw->phy.addr == 2) &&
                    !(MAX_PHY_REG_ADDRESS & reg) && (data & (1 << 11))) {
                        u16 data2 = 0x7EFF;
+
                        ret_val = e1000_access_phy_debug_regs_hv(hw,
                                                                 (1 << 6) | 0x3,
                                                                 &data2, false);
index beb7b4393a6c26fc46c917a798a6fd7bfaee28ea..65985846345deea3cc16d6f3cfd26add64dbfa7f 100644 (file)
@@ -72,6 +72,7 @@
 #define I40E_MIN_NUM_DESCRIPTORS      64
 #define I40E_MIN_MSIX                 2
 #define I40E_DEFAULT_NUM_VMDQ_VSI     8 /* max 256 VSIs */
+#define I40E_MIN_VSI_ALLOC            51 /* LAN, ATR, FCOE, 32 VF, 16 VMDQ */
 #define I40E_DEFAULT_QUEUES_PER_VMDQ  2 /* max 16 qps */
 #define I40E_DEFAULT_QUEUES_PER_VF    4
 #define I40E_DEFAULT_QUEUES_PER_TC    1 /* should be a power of 2 */
 #define STRINGIFY(foo)  #foo
 #define XSTRINGIFY(bar) STRINGIFY(bar)
 
-#ifndef ARCH_HAS_PREFETCH
-#define prefetch(X)
-#endif
-
 #define I40E_RX_DESC(R, i)                     \
        ((ring_is_16byte_desc_enabled(R))       \
                ? (union i40e_32byte_rx_desc *) \
@@ -157,11 +154,23 @@ struct i40e_lump_tracking {
 #define I40E_FDIR_BUFFER_FULL_MARGIN   10
 #define I40E_FDIR_BUFFER_HEAD_ROOM     200
 
+enum i40e_fd_stat_idx {
+       I40E_FD_STAT_ATR,
+       I40E_FD_STAT_SB,
+       I40E_FD_STAT_PF_COUNT
+};
+#define I40E_FD_STAT_PF_IDX(pf_id) ((pf_id) * I40E_FD_STAT_PF_COUNT)
+#define I40E_FD_ATR_STAT_IDX(pf_id) \
+                       (I40E_FD_STAT_PF_IDX(pf_id) + I40E_FD_STAT_ATR)
+#define I40E_FD_SB_STAT_IDX(pf_id)  \
+                       (I40E_FD_STAT_PF_IDX(pf_id) + I40E_FD_STAT_SB)
+
 struct i40e_fdir_filter {
        struct hlist_node fdir_node;
        /* filter ipnut set */
        u8 flow_type;
        u8 ip4_proto;
+       /* TX packet view of src and dst */
        __be32 dst_ip[4];
        __be32 src_ip[4];
        __be16 src_port;
@@ -205,7 +214,6 @@ struct i40e_pf {
        unsigned long state;
        unsigned long link_check_timeout;
        struct msix_entry *msix_entries;
-       u16 num_msix_entries;
        bool fc_autoneg_status;
 
        u16 eeprom_version;
@@ -220,11 +228,14 @@ struct i40e_pf {
        u16 rss_size;              /* num queues in the RSS array */
        u16 rss_size_max;          /* HW defined max RSS queues */
        u16 fdir_pf_filter_count;  /* num of guaranteed filters for this PF */
+       u16 num_alloc_vsi;         /* num VSIs this driver supports */
        u8 atr_sample_rate;
        bool wol_en;
 
        struct hlist_head fdir_filter_list;
        u16 fdir_pf_active_filters;
+       u16 fd_sb_cnt_idx;
+       u16 fd_atr_cnt_idx;
 
 #ifdef CONFIG_I40E_VXLAN
        __be16  vxlan_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS];
@@ -266,6 +277,7 @@ struct i40e_pf {
 #ifdef CONFIG_I40E_VXLAN
 #define I40E_FLAG_VXLAN_FILTER_SYNC            (u64)(1 << 27)
 #endif
+#define I40E_FLAG_DCB_CAPABLE                  (u64)(1 << 29)
 
        /* tracks features that get auto disabled by errors */
        u64 auto_disable_flags;
@@ -300,7 +312,6 @@ struct i40e_pf {
        u16 pf_seid;
        u16 main_vsi_seid;
        u16 mac_seid;
-       struct i40e_aqc_get_switch_config_data *sw_config;
        struct kobject *switch_kobj;
 #ifdef CONFIG_DEBUG_FS
        struct dentry *i40e_dbg_pf;
@@ -329,9 +340,7 @@ struct i40e_pf {
        struct ptp_clock *ptp_clock;
        struct ptp_clock_info ptp_caps;
        struct sk_buff *ptp_tx_skb;
-       struct work_struct ptp_tx_work;
        struct hwtstamp_config tstamp_config;
-       unsigned long ptp_tx_start;
        unsigned long last_rx_ptp_check;
        spinlock_t tmreg_lock; /* Used to protect the device time registers. */
        u64 ptp_base_adj;
@@ -420,6 +429,7 @@ struct i40e_vsi {
        struct i40e_q_vector **q_vectors;
        int num_q_vectors;
        int base_vector;
+       bool irqs_ready;
 
        u16 seid;            /* HW index of this VSI (absolute index) */
        u16 id;              /* VSI number */
@@ -540,6 +550,15 @@ static inline bool i40e_rx_is_programming_status(u64 qw)
                (qw >> I40E_RX_PROG_STATUS_DESC_LENGTH_SHIFT);
 }
 
+/**
+ * i40e_get_fd_cnt_all - get the total FD filter space available
+ * @pf: pointer to the pf struct
+ **/
+static inline int i40e_get_fd_cnt_all(struct i40e_pf *pf)
+{
+       return pf->hw.fdir_shared_filter_count + pf->fdir_pf_filter_count;
+}
+
 /* needed by i40e_ethtool.c */
 int i40e_up(struct i40e_vsi *vsi);
 void i40e_down(struct i40e_vsi *vsi);
index ed3902bf249b3e4ceb047ed14762d6ea50b1a4c0..7a027499fc57fc968d9ca33df5c65bb9e6825123 100644 (file)
 
 static void i40e_resume_aq(struct i40e_hw *hw);
 
+/**
+ * i40e_is_nvm_update_op - return true if this is an NVM update operation
+ * @desc: API request descriptor
+ **/
+static inline bool i40e_is_nvm_update_op(struct i40e_aq_desc *desc)
+{
+       return (desc->opcode == i40e_aqc_opc_nvm_erase) ||
+              (desc->opcode == i40e_aqc_opc_nvm_update);
+}
+
 /**
  *  i40e_adminq_init_regs - Initialize AdminQ registers
  *  @hw: pointer to the hardware structure
@@ -281,8 +291,11 @@ static void i40e_free_asq_bufs(struct i40e_hw *hw)
  *
  *  Configure base address and length registers for the transmit queue
  **/
-static void i40e_config_asq_regs(struct i40e_hw *hw)
+static i40e_status i40e_config_asq_regs(struct i40e_hw *hw)
 {
+       i40e_status ret_code = 0;
+       u32 reg = 0;
+
        if (hw->mac.type == I40E_MAC_VF) {
                /* configure the transmit queue */
                wr32(hw, I40E_VF_ATQBAH1,
@@ -291,6 +304,7 @@ static void i40e_config_asq_regs(struct i40e_hw *hw)
                    lower_32_bits(hw->aq.asq.desc_buf.pa));
                wr32(hw, I40E_VF_ATQLEN1, (hw->aq.num_asq_entries |
                                          I40E_VF_ATQLEN1_ATQENABLE_MASK));
+               reg = rd32(hw, I40E_VF_ATQBAL1);
        } else {
                /* configure the transmit queue */
                wr32(hw, I40E_PF_ATQBAH,
@@ -299,7 +313,14 @@ static void i40e_config_asq_regs(struct i40e_hw *hw)
                    lower_32_bits(hw->aq.asq.desc_buf.pa));
                wr32(hw, I40E_PF_ATQLEN, (hw->aq.num_asq_entries |
                                          I40E_PF_ATQLEN_ATQENABLE_MASK));
+               reg = rd32(hw, I40E_PF_ATQBAL);
        }
+
+       /* Check one register to verify that config was applied */
+       if (reg != lower_32_bits(hw->aq.asq.desc_buf.pa))
+               ret_code = I40E_ERR_ADMIN_QUEUE_ERROR;
+
+       return ret_code;
 }
 
 /**
@@ -308,8 +329,11 @@ static void i40e_config_asq_regs(struct i40e_hw *hw)
  *
  * Configure base address and length registers for the receive (event queue)
  **/
-static void i40e_config_arq_regs(struct i40e_hw *hw)
+static i40e_status i40e_config_arq_regs(struct i40e_hw *hw)
 {
+       i40e_status ret_code = 0;
+       u32 reg = 0;
+
        if (hw->mac.type == I40E_MAC_VF) {
                /* configure the receive queue */
                wr32(hw, I40E_VF_ARQBAH1,
@@ -318,6 +342,7 @@ static void i40e_config_arq_regs(struct i40e_hw *hw)
                    lower_32_bits(hw->aq.arq.desc_buf.pa));
                wr32(hw, I40E_VF_ARQLEN1, (hw->aq.num_arq_entries |
                                          I40E_VF_ARQLEN1_ARQENABLE_MASK));
+               reg = rd32(hw, I40E_VF_ARQBAL1);
        } else {
                /* configure the receive queue */
                wr32(hw, I40E_PF_ARQBAH,
@@ -326,10 +351,17 @@ static void i40e_config_arq_regs(struct i40e_hw *hw)
                    lower_32_bits(hw->aq.arq.desc_buf.pa));
                wr32(hw, I40E_PF_ARQLEN, (hw->aq.num_arq_entries |
                                          I40E_PF_ARQLEN_ARQENABLE_MASK));
+               reg = rd32(hw, I40E_PF_ARQBAL);
        }
 
        /* Update tail in the HW to post pre-allocated buffers */
        wr32(hw, hw->aq.arq.tail, hw->aq.num_arq_entries - 1);
+
+       /* Check one register to verify that config was applied */
+       if (reg != lower_32_bits(hw->aq.arq.desc_buf.pa))
+               ret_code = I40E_ERR_ADMIN_QUEUE_ERROR;
+
+       return ret_code;
 }
 
 /**
@@ -377,7 +409,9 @@ static i40e_status i40e_init_asq(struct i40e_hw *hw)
                goto init_adminq_free_rings;
 
        /* initialize base registers */
-       i40e_config_asq_regs(hw);
+       ret_code = i40e_config_asq_regs(hw);
+       if (ret_code)
+               goto init_adminq_free_rings;
 
        /* success! */
        goto init_adminq_exit;
@@ -434,7 +468,9 @@ static i40e_status i40e_init_arq(struct i40e_hw *hw)
                goto init_adminq_free_rings;
 
        /* initialize base registers */
-       i40e_config_arq_regs(hw);
+       ret_code = i40e_config_arq_regs(hw);
+       if (ret_code)
+               goto init_adminq_free_rings;
 
        /* success! */
        goto init_adminq_exit;
@@ -577,14 +613,14 @@ i40e_status i40e_init_adminq(struct i40e_hw *hw)
        i40e_read_nvm_word(hw, I40E_SR_NVM_EETRACK_HI, &eetrack_hi);
        hw->nvm.eetrack = (eetrack_hi << 16) | eetrack_lo;
 
-       if (hw->aq.api_maj_ver != I40E_FW_API_VERSION_MAJOR ||
-           hw->aq.api_min_ver > I40E_FW_API_VERSION_MINOR) {
+       if (hw->aq.api_maj_ver > I40E_FW_API_VERSION_MAJOR) {
                ret_code = I40E_ERR_FIRMWARE_API_VERSION;
                goto init_adminq_free_arq;
        }
 
        /* pre-emptive resource lock release */
        i40e_aq_release_resource(hw, I40E_NVM_RESOURCE_ID, 0, NULL);
+       hw->aq.nvm_busy = false;
 
        ret_code = i40e_aq_set_hmc_resource_profile(hw,
                                                    I40E_HMC_PROFILE_DEFAULT,
@@ -708,6 +744,12 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
                goto asq_send_command_exit;
        }
 
+       if (i40e_is_nvm_update_op(desc) && hw->aq.nvm_busy) {
+               i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, "AQTX: NVM busy.\n");
+               status = I40E_ERR_NVM;
+               goto asq_send_command_exit;
+       }
+
        details = I40E_ADMINQ_DETAILS(hw->aq.asq, hw->aq.asq.next_to_use);
        if (cmd_details) {
                *details = *cmd_details;
@@ -835,6 +877,9 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
                hw->aq.asq_last_status = (enum i40e_admin_queue_err)retval;
        }
 
+       if (i40e_is_nvm_update_op(desc))
+               hw->aq.nvm_busy = true;
+
        /* update the error if time out occurred */
        if ((!cmd_completed) &&
            (!details->async && !details->postpone)) {
@@ -929,6 +974,9 @@ i40e_status i40e_clean_arq_element(struct i40e_hw *hw,
                               e->msg_size);
        }
 
+       if (i40e_is_nvm_update_op(&e->desc))
+               hw->aq.nvm_busy = false;
+
        /* Restore the original datalen and buffer address in the desc,
         * FW updates datalen to indicate the event message
         * size
index 993f7685a9111694726c1a9bee44f3653651df72..b1552fbc48a0e6876acf838ab7eadf1e72fe492e 100644 (file)
@@ -90,6 +90,7 @@ struct i40e_adminq_info {
        u16 fw_min_ver;                 /* firmware minor version */
        u16 api_maj_ver;                /* api major version */
        u16 api_min_ver;                /* api minor version */
+       bool nvm_busy;
 
        struct mutex asq_mutex; /* Send queue lock */
        struct mutex arq_mutex; /* Receive queue lock */
index 7b6374a8f8da5cbb5054c431dce910d75de205c5..15f289f2917f70bb313501164f9c5fd8ef4cae05 100644 (file)
@@ -34,7 +34,7 @@
  */
 
 #define I40E_FW_API_VERSION_MAJOR  0x0001
-#define I40E_FW_API_VERSION_MINOR  0x0001
+#define I40E_FW_API_VERSION_MINOR  0x0002
 
 struct i40e_aq_desc {
        __le16 flags;
@@ -123,6 +123,7 @@ enum i40e_admin_queue_opc {
        i40e_aqc_opc_get_version      = 0x0001,
        i40e_aqc_opc_driver_version   = 0x0002,
        i40e_aqc_opc_queue_shutdown   = 0x0003,
+       i40e_aqc_opc_set_pf_context   = 0x0004,
 
        /* resource ownership */
        i40e_aqc_opc_request_resource = 0x0008,
@@ -182,9 +183,6 @@ enum i40e_admin_queue_opc {
        i40e_aqc_opc_add_mirror_rule    = 0x0260,
        i40e_aqc_opc_delete_mirror_rule = 0x0261,
 
-       i40e_aqc_opc_set_storm_control_config = 0x0280,
-       i40e_aqc_opc_get_storm_control_config = 0x0281,
-
        /* DCB commands */
        i40e_aqc_opc_dcb_ignore_pfc = 0x0301,
        i40e_aqc_opc_dcb_updated    = 0x0302,
@@ -207,6 +205,7 @@ enum i40e_admin_queue_opc {
        i40e_aqc_opc_query_switching_comp_bw_config        = 0x041A,
        i40e_aqc_opc_suspend_port_tx                       = 0x041B,
        i40e_aqc_opc_resume_port_tx                        = 0x041C,
+       i40e_aqc_opc_configure_partition_bw                = 0x041D,
 
        /* hmc */
        i40e_aqc_opc_query_hmc_resource_profile = 0x0500,
@@ -224,13 +223,15 @@ enum i40e_admin_queue_opc {
        i40e_aqc_opc_get_partner_advt    = 0x0616,
        i40e_aqc_opc_set_lb_modes        = 0x0618,
        i40e_aqc_opc_get_phy_wol_caps    = 0x0621,
-       i40e_aqc_opc_set_phy_reset       = 0x0622,
+       i40e_aqc_opc_set_phy_debug       = 0x0622,
        i40e_aqc_opc_upload_ext_phy_fm   = 0x0625,
 
        /* NVM commands */
-       i40e_aqc_opc_nvm_read   = 0x0701,
-       i40e_aqc_opc_nvm_erase  = 0x0702,
-       i40e_aqc_opc_nvm_update = 0x0703,
+       i40e_aqc_opc_nvm_read         = 0x0701,
+       i40e_aqc_opc_nvm_erase        = 0x0702,
+       i40e_aqc_opc_nvm_update       = 0x0703,
+       i40e_aqc_opc_nvm_config_read  = 0x0704,
+       i40e_aqc_opc_nvm_config_write = 0x0705,
 
        /* virtualization commands */
        i40e_aqc_opc_send_msg_to_pf   = 0x0801,
@@ -272,8 +273,6 @@ enum i40e_admin_queue_opc {
        i40e_aqc_opc_debug_set_mode         = 0xFF01,
        i40e_aqc_opc_debug_read_reg         = 0xFF03,
        i40e_aqc_opc_debug_write_reg        = 0xFF04,
-       i40e_aqc_opc_debug_read_reg_sg      = 0xFF05,
-       i40e_aqc_opc_debug_write_reg_sg     = 0xFF06,
        i40e_aqc_opc_debug_modify_reg       = 0xFF07,
        i40e_aqc_opc_debug_dump_internals   = 0xFF08,
        i40e_aqc_opc_debug_modify_internals = 0xFF09,
@@ -341,6 +340,14 @@ struct i40e_aqc_queue_shutdown {
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_queue_shutdown);
 
+/* Set PF context (0x0004, direct) */
+struct i40e_aqc_set_pf_context {
+       u8      pf_id;
+       u8      reserved[15];
+};
+
+I40E_CHECK_CMD_LENGTH(i40e_aqc_set_pf_context);
+
 /* Request resource ownership (direct 0x0008)
  * Release resource ownership (direct 0x0009)
  */
@@ -1289,27 +1296,6 @@ struct i40e_aqc_add_delete_mirror_rule_completion {
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_add_delete_mirror_rule_completion);
 
-/* Set Storm Control Configuration (direct 0x0280)
- * Get Storm Control Configuration (direct 0x0281)
- *    the command and response use the same descriptor structure
- */
-struct i40e_aqc_set_get_storm_control_config {
-       __le32 broadcast_threshold;
-       __le32 multicast_threshold;
-       __le32 control_flags;
-#define I40E_AQC_STORM_CONTROL_MDIPW            0x01
-#define I40E_AQC_STORM_CONTROL_MDICW            0x02
-#define I40E_AQC_STORM_CONTROL_BDIPW            0x04
-#define I40E_AQC_STORM_CONTROL_BDICW            0x08
-#define I40E_AQC_STORM_CONTROL_BIDU             0x10
-#define I40E_AQC_STORM_CONTROL_INTERVAL_SHIFT   8
-#define I40E_AQC_STORM_CONTROL_INTERVAL_MASK    (0x3FF << \
-                                       I40E_AQC_STORM_CONTROL_INTERVAL_SHIFT)
-       u8     reserved[4];
-};
-
-I40E_CHECK_CMD_LENGTH(i40e_aqc_set_get_storm_control_config);
-
 /* DCB 0x03xx*/
 
 /* PFC Ignore (direct 0x0301)
@@ -1427,11 +1413,12 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_configure_switching_comp_bw_limit);
 struct i40e_aqc_configure_switching_comp_ets_data {
        u8     reserved[4];
        u8     tc_valid_bits;
-       u8     reserved1;
+       u8     seepage;
+#define I40E_AQ_ETS_SEEPAGE_EN_MASK     0x1
        u8     tc_strict_priority_flags;
-       u8     reserved2[17];
+       u8     reserved1[17];
        u8     tc_bw_share_credits[8];
-       u8     reserved3[96];
+       u8     reserved2[96];
 };
 
 /* Configure Switching Component Bandwidth Limits per Tc (indirect 0x0416) */
@@ -1499,6 +1486,15 @@ struct i40e_aqc_query_switching_comp_bw_config_resp {
  * (direct 0x041B and 0x041C) uses the generic SEID struct
  */
 
+/* Configure partition BW
+ * (indirect 0x041D)
+ */
+struct i40e_aqc_configure_partition_bw_data {
+       __le16 pf_valid_bits;
+       u8     min_bw[16];      /* guaranteed bandwidth */
+       u8     max_bw[16];      /* bandwidth limit */
+};
+
 /* Get and set the active HMC resource profile and status.
  * (direct 0x0500) and (direct 0x0501)
  */
@@ -1539,6 +1535,8 @@ enum i40e_aq_phy_type {
        I40E_PHY_TYPE_XLPPI                     = 0x9,
        I40E_PHY_TYPE_40GBASE_CR4_CU            = 0xA,
        I40E_PHY_TYPE_10GBASE_CR1_CU            = 0xB,
+       I40E_PHY_TYPE_10GBASE_AOC               = 0xC,
+       I40E_PHY_TYPE_40GBASE_AOC               = 0xD,
        I40E_PHY_TYPE_100BASE_TX                = 0x11,
        I40E_PHY_TYPE_1000BASE_T                = 0x12,
        I40E_PHY_TYPE_10GBASE_T                 = 0x13,
@@ -1549,7 +1547,10 @@ enum i40e_aq_phy_type {
        I40E_PHY_TYPE_40GBASE_CR4               = 0x18,
        I40E_PHY_TYPE_40GBASE_SR4               = 0x19,
        I40E_PHY_TYPE_40GBASE_LR4               = 0x1A,
-       I40E_PHY_TYPE_20GBASE_KR2               = 0x1B,
+       I40E_PHY_TYPE_1000BASE_SX               = 0x1B,
+       I40E_PHY_TYPE_1000BASE_LX               = 0x1C,
+       I40E_PHY_TYPE_1000BASE_T_OPTICAL        = 0x1D,
+       I40E_PHY_TYPE_20GBASE_KR2               = 0x1E,
        I40E_PHY_TYPE_MAX
 };
 
@@ -1583,11 +1584,8 @@ struct i40e_aq_get_phy_abilities_resp {
 #define I40E_AQ_PHY_FLAG_PAUSE_TX         0x01
 #define I40E_AQ_PHY_FLAG_PAUSE_RX         0x02
 #define I40E_AQ_PHY_FLAG_LOW_POWER        0x04
-#define I40E_AQ_PHY_FLAG_AN_SHIFT         3
-#define I40E_AQ_PHY_FLAG_AN_MASK          (0x3 << I40E_AQ_PHY_FLAG_AN_SHIFT)
-#define I40E_AQ_PHY_FLAG_AN_OFF           0x00 /* link forced on */
-#define I40E_AQ_PHY_FLAG_AN_OFF_LINK_DOWN 0x01
-#define I40E_AQ_PHY_FLAG_AN_ON            0x02
+#define I40E_AQ_PHY_LINK_ENABLED                 0x08
+#define I40E_AQ_PHY_AN_ENABLED                   0x10
 #define I40E_AQ_PHY_FLAG_MODULE_QUAL      0x20
        __le16 eee_capability;
 #define I40E_AQ_EEE_100BASE_TX       0x0002
@@ -1696,6 +1694,7 @@ struct i40e_aqc_get_link_status {
 #define I40E_AQ_LINK_TX_ACTIVE       0x00
 #define I40E_AQ_LINK_TX_DRAINED      0x01
 #define I40E_AQ_LINK_TX_FLUSHED      0x03
+#define I40E_AQ_LINK_FORCED_40G      0x10
        u8     loopback;         /* use defines from i40e_aqc_set_lb_mode */
        __le16 max_frame_size;
        u8     config;
@@ -1747,14 +1746,21 @@ struct i40e_aqc_set_lb_mode {
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_set_lb_mode);
 
-/* Set PHY Reset command (0x0622) */
-struct i40e_aqc_set_phy_reset {
-       u8     reset_flags;
-#define I40E_AQ_PHY_RESET_REQUEST  0x02
+/* Set PHY Debug command (0x0622) */
+struct i40e_aqc_set_phy_debug {
+       u8     command_flags;
+#define I40E_AQ_PHY_DEBUG_RESET_INTERNAL       0x02
+#define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_SHIFT 2
+#define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_MASK  (0x03 << \
+                                       I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_SHIFT)
+#define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_NONE  0x00
+#define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_HARD  0x01
+#define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_SOFT  0x02
+#define I40E_AQ_PHY_DEBUG_DISABLE_LINK_FW      0x10
        u8     reserved[15];
 };
 
-I40E_CHECK_CMD_LENGTH(i40e_aqc_set_phy_reset);
+I40E_CHECK_CMD_LENGTH(i40e_aqc_set_phy_debug);
 
 enum i40e_aq_phy_reg_type {
        I40E_AQC_PHY_REG_INTERNAL         = 0x1,
@@ -1779,6 +1785,47 @@ struct i40e_aqc_nvm_update {
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_nvm_update);
 
+/* NVM Config Read (indirect 0x0704) */
+struct i40e_aqc_nvm_config_read {
+       __le16 cmd_flags;
+#define ANVM_SINGLE_OR_MULTIPLE_FEATURES_MASK  1
+#define ANVM_READ_SINGLE_FEATURE               0
+#define ANVM_READ_MULTIPLE_FEATURES            1
+       __le16 element_count;
+       __le16 element_id;              /* Feature/field ID */
+       u8     reserved[2];
+       __le32 address_high;
+       __le32 address_low;
+};
+
+I40E_CHECK_CMD_LENGTH(i40e_aqc_nvm_config_read);
+
+/* NVM Config Write (indirect 0x0705) */
+struct i40e_aqc_nvm_config_write {
+       __le16 cmd_flags;
+       __le16 element_count;
+       u8     reserved[4];
+       __le32 address_high;
+       __le32 address_low;
+};
+
+I40E_CHECK_CMD_LENGTH(i40e_aqc_nvm_config_write);
+
+struct i40e_aqc_nvm_config_data_feature {
+       __le16 feature_id;
+       __le16 instance_id;
+       __le16 feature_options;
+       __le16 feature_selection;
+};
+
+struct i40e_aqc_nvm_config_data_immediate_field {
+#define ANVM_FEATURE_OR_IMMEDIATE_MASK 0x2
+       __le16 field_id;
+       __le16 instance_id;
+       __le16 field_options;
+       __le16 field_value;
+};
+
 /* Send to PF command (indirect 0x0801) id is only used by PF
  * Send to VF command (indirect 0x0802) id is only used by PF
  * Send to Peer PF command (indirect 0x0803)
index 922cdcc45c54b1d36de2a7c0b7faf0ed8cdf1e0d..6e65f19dd6e58aadf1fc7df3df41ce8a617be959 100644 (file)
@@ -43,12 +43,10 @@ static i40e_status i40e_set_mac_type(struct i40e_hw *hw)
        if (hw->vendor_id == PCI_VENDOR_ID_INTEL) {
                switch (hw->device_id) {
                case I40E_DEV_ID_SFP_XL710:
-               case I40E_DEV_ID_SFP_X710:
                case I40E_DEV_ID_QEMU:
                case I40E_DEV_ID_KX_A:
                case I40E_DEV_ID_KX_B:
                case I40E_DEV_ID_KX_C:
-               case I40E_DEV_ID_KX_D:
                case I40E_DEV_ID_QSFP_A:
                case I40E_DEV_ID_QSFP_B:
                case I40E_DEV_ID_QSFP_C:
@@ -133,7 +131,11 @@ void i40e_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void *desc,
  **/
 bool i40e_check_asq_alive(struct i40e_hw *hw)
 {
-       return !!(rd32(hw, hw->aq.asq.len) & I40E_PF_ATQLEN_ATQENABLE_MASK);
+       if (hw->aq.asq.len)
+               return !!(rd32(hw, hw->aq.asq.len) &
+                         I40E_PF_ATQLEN_ATQENABLE_MASK);
+       else
+               return false;
 }
 
 /**
@@ -652,6 +654,36 @@ i40e_status i40e_get_mac_addr(struct i40e_hw *hw, u8 *mac_addr)
        return status;
 }
 
+/**
+ * i40e_pre_tx_queue_cfg - pre tx queue configure
+ * @hw: pointer to the HW structure
+ * @queue: target pf queue index
+ * @enable: state change request
+ *
+ * Handles hw requirement to indicate intention to enable
+ * or disable target queue.
+ **/
+void i40e_pre_tx_queue_cfg(struct i40e_hw *hw, u32 queue, bool enable)
+{
+       u32 abs_queue_idx = hw->func_caps.base_queue + queue;
+       u32 reg_block = 0;
+       u32 reg_val;
+
+       if (abs_queue_idx >= 128)
+               reg_block = abs_queue_idx / 128;
+
+       reg_val = rd32(hw, I40E_GLLAN_TXPRE_QDIS(reg_block));
+       reg_val &= ~I40E_GLLAN_TXPRE_QDIS_QINDX_MASK;
+       reg_val |= (abs_queue_idx << I40E_GLLAN_TXPRE_QDIS_QINDX_SHIFT);
+
+       if (enable)
+               reg_val |= I40E_GLLAN_TXPRE_QDIS_CLEAR_QDIS_MASK;
+       else
+               reg_val |= I40E_GLLAN_TXPRE_QDIS_SET_QDIS_MASK;
+
+       wr32(hw, I40E_GLLAN_TXPRE_QDIS(reg_block), reg_val);
+}
+
 /**
  * i40e_get_media_type - Gets media type
  * @hw: pointer to the hardware structure
@@ -699,7 +731,7 @@ static enum i40e_media_type i40e_get_media_type(struct i40e_hw *hw)
 }
 
 #define I40E_PF_RESET_WAIT_COUNT_A0    200
-#define I40E_PF_RESET_WAIT_COUNT       10
+#define I40E_PF_RESET_WAIT_COUNT       100
 /**
  * i40e_pf_reset - Reset the PF
  * @hw: pointer to the hardware structure
@@ -789,6 +821,9 @@ void i40e_clear_pxe_mode(struct i40e_hw *hw)
 {
        u32 reg;
 
+       if (i40e_check_asq_alive(hw))
+               i40e_aq_clear_pxe_mode(hw, NULL);
+
        /* Clear single descriptor fetch/write-back mode */
        reg = rd32(hw, I40E_GLLAN_RCTL_0);
 
@@ -906,6 +941,33 @@ void i40e_led_set(struct i40e_hw *hw, u32 mode, bool blink)
 
 /* Admin command wrappers */
 
+/**
+ * i40e_aq_clear_pxe_mode
+ * @hw: pointer to the hw struct
+ * @cmd_details: pointer to command details structure or NULL
+ *
+ * Tell the firmware that the driver is taking over from PXE
+ **/
+i40e_status i40e_aq_clear_pxe_mode(struct i40e_hw *hw,
+                               struct i40e_asq_cmd_details *cmd_details)
+{
+       i40e_status status;
+       struct i40e_aq_desc desc;
+       struct i40e_aqc_clear_pxe *cmd =
+               (struct i40e_aqc_clear_pxe *)&desc.params.raw;
+
+       i40e_fill_default_direct_cmd_desc(&desc,
+                                         i40e_aqc_opc_clear_pxe_mode);
+
+       cmd->rx_cnt = 0x2;
+
+       status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
+
+       wr32(hw, I40E_GLLAN_RCTL_0, 0x1);
+
+       return status;
+}
+
 /**
  * i40e_aq_set_link_restart_an
  * @hw: pointer to the hw struct
@@ -975,6 +1037,13 @@ i40e_status i40e_aq_get_link_info(struct i40e_hw *hw,
        hw_link_info->an_info = resp->an_info;
        hw_link_info->ext_info = resp->ext_info;
        hw_link_info->loopback = resp->loopback;
+       hw_link_info->max_frame_size = le16_to_cpu(resp->max_frame_size);
+       hw_link_info->pacing = resp->config & I40E_AQ_CONFIG_PACING_MASK;
+
+       if (resp->config & I40E_AQ_CONFIG_CRC_ENA)
+               hw_link_info->crc_enable = true;
+       else
+               hw_link_info->crc_enable = false;
 
        if (resp->command_flags & cpu_to_le16(I40E_AQ_LSE_ENABLE))
                hw_link_info->lse_enable = true;
@@ -1021,8 +1090,6 @@ i40e_status i40e_aq_add_vsi(struct i40e_hw *hw,
        cmd->vsi_flags = cpu_to_le16(vsi_ctx->flags);
 
        desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
-       if (sizeof(vsi_ctx->info) > I40E_AQ_LARGE_BUF)
-               desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB);
 
        status = i40e_asq_send_command(hw, &desc, &vsi_ctx->info,
                                    sizeof(vsi_ctx->info), cmd_details);
@@ -1163,8 +1230,6 @@ i40e_status i40e_aq_get_vsi_params(struct i40e_hw *hw,
        cmd->uplink_seid = cpu_to_le16(vsi_ctx->seid);
 
        desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_BUF);
-       if (sizeof(vsi_ctx->info) > I40E_AQ_LARGE_BUF)
-               desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB);
 
        status = i40e_asq_send_command(hw, &desc, &vsi_ctx->info,
                                    sizeof(vsi_ctx->info), NULL);
@@ -1203,8 +1268,6 @@ i40e_status i40e_aq_update_vsi_params(struct i40e_hw *hw,
        cmd->uplink_seid = cpu_to_le16(vsi_ctx->seid);
 
        desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
-       if (sizeof(vsi_ctx->info) > I40E_AQ_LARGE_BUF)
-               desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB);
 
        status = i40e_asq_send_command(hw, &desc, &vsi_ctx->info,
                                    sizeof(vsi_ctx->info), cmd_details);
@@ -1300,6 +1363,7 @@ i40e_status i40e_aq_send_driver_version(struct i40e_hw *hw,
        struct i40e_aqc_driver_version *cmd =
                (struct i40e_aqc_driver_version *)&desc.params.raw;
        i40e_status status;
+       u16 len;
 
        if (dv == NULL)
                return I40E_ERR_PARAM;
@@ -1311,7 +1375,14 @@ i40e_status i40e_aq_send_driver_version(struct i40e_hw *hw,
        cmd->driver_minor_ver = dv->minor_version;
        cmd->driver_build_ver = dv->build_version;
        cmd->driver_subbuild_ver = dv->subbuild_version;
-       status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
+
+       len = 0;
+       while (len < sizeof(dv->driver_string) &&
+              (dv->driver_string[len] < 0x80) &&
+              dv->driver_string[len])
+               len++;
+       status = i40e_asq_send_command(hw, &desc, dv->driver_string,
+                                      len, cmd_details);
 
        return status;
 }
@@ -1900,6 +1971,12 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff,
                }
        }
 
+       /* Software override ensuring FCoE is disabled if npar or mfp
+        * mode because it is not supported in these modes.
+        */
+       if (p->npar_enable || p->mfp_mode_1)
+               p->fcoe = false;
+
        /* additional HW specific goodies that might
         * someday be HW version specific
         */
@@ -2094,8 +2171,8 @@ i40e_status i40e_aq_start_lldp(struct i40e_hw *hw,
  * @cmd_details: pointer to command details structure or NULL
  **/
 i40e_status i40e_aq_add_udp_tunnel(struct i40e_hw *hw,
-                               u16 udp_port, u8 header_len,
-                               u8 protocol_index, u8 *filter_index,
+                               u16 udp_port, u8 protocol_index,
+                               u8 *filter_index,
                                struct i40e_asq_cmd_details *cmd_details)
 {
        struct i40e_aq_desc desc;
@@ -2252,6 +2329,35 @@ static i40e_status i40e_aq_tx_sched_cmd(struct i40e_hw *hw, u16 seid,
        return status;
 }
 
+/**
+ * i40e_aq_config_vsi_bw_limit - Configure VSI BW Limit
+ * @hw: pointer to the hw struct
+ * @seid: VSI seid
+ * @credit: BW limit credits (0 = disabled)
+ * @max_credit: Max BW limit credits
+ * @cmd_details: pointer to command details structure or NULL
+ **/
+i40e_status i40e_aq_config_vsi_bw_limit(struct i40e_hw *hw,
+                               u16 seid, u16 credit, u8 max_credit,
+                               struct i40e_asq_cmd_details *cmd_details)
+{
+       struct i40e_aq_desc desc;
+       struct i40e_aqc_configure_vsi_bw_limit *cmd =
+               (struct i40e_aqc_configure_vsi_bw_limit *)&desc.params.raw;
+       i40e_status status;
+
+       i40e_fill_default_direct_cmd_desc(&desc,
+                                         i40e_aqc_opc_configure_vsi_bw_limit);
+
+       cmd->vsi_seid = cpu_to_le16(seid);
+       cmd->credit = cpu_to_le16(credit);
+       cmd->max_credit = max_credit;
+
+       status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
+
+       return status;
+}
+
 /**
  * i40e_aq_config_vsi_tc_bw - Config VSI BW Allocation per TC
  * @hw: pointer to the hw struct
@@ -2405,7 +2511,7 @@ static i40e_status i40e_validate_filter_settings(struct i40e_hw *hw,
 {
        u32 fcoe_cntx_size, fcoe_filt_size;
        u32 pe_cntx_size, pe_filt_size;
-       u32 fcoe_fmax, pe_fmax;
+       u32 fcoe_fmax;
        u32 val;
 
        /* Validate FCoE settings passed */
@@ -2480,13 +2586,6 @@ static i40e_status i40e_validate_filter_settings(struct i40e_hw *hw,
        if (fcoe_filt_size + fcoe_cntx_size >  fcoe_fmax)
                return I40E_ERR_INVALID_SIZE;
 
-       /* PEHSIZE + PEDSIZE should not be greater than PMPEXFMAX */
-       val = rd32(hw, I40E_GLHMC_PEXFMAX);
-       pe_fmax = (val & I40E_GLHMC_PEXFMAX_PMPEXFMAX_MASK)
-                  >> I40E_GLHMC_PEXFMAX_PMPEXFMAX_SHIFT;
-       if (pe_filt_size + pe_cntx_size >  pe_fmax)
-               return I40E_ERR_INVALID_SIZE;
-
        return 0;
 }
 
index 6e8103abfd0d699401f29287ea63cfba799a2fe1..00bc0cdb3a0304b87ec9935b148a81c683927f51 100644 (file)
@@ -232,7 +232,7 @@ static void i40e_dcbnl_del_app(struct i40e_pf *pf,
                              struct i40e_ieee_app_priority_table *app)
 {
        int v, err;
-       for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+       for (v = 0; v < pf->num_alloc_vsi; v++) {
                if (pf->vsi[v] && pf->vsi[v]->netdev) {
                        err = i40e_dcbnl_vsi_del_app(pf->vsi[v], app);
                        if (err)
@@ -302,8 +302,8 @@ void i40e_dcbnl_setup(struct i40e_vsi *vsi)
        struct net_device *dev = vsi->netdev;
        struct i40e_pf *pf = i40e_netdev_to_pf(dev);
 
-       /* DCB not enabled */
-       if (!(pf->flags & I40E_FLAG_DCB_ENABLED))
+       /* Not DCB capable */
+       if (!(pf->flags & I40E_FLAG_DCB_CAPABLE))
                return;
 
        /* Do not setup DCB NL ops for MFP mode */
index 3c37386fd138fdeb3941170e5789687d90385fd5..cffdfc21290fdced44a5b1265eb2fb04a23608cf 100644 (file)
@@ -45,7 +45,7 @@ static struct i40e_vsi *i40e_dbg_find_vsi(struct i40e_pf *pf, int seid)
        if (seid < 0)
                dev_info(&pf->pdev->dev, "%d: bad seid\n", seid);
        else
-               for (i = 0; i < pf->hw.func_caps.num_vsis; i++)
+               for (i = 0; i < pf->num_alloc_vsi; i++)
                        if (pf->vsi[i] && (pf->vsi[i]->seid == seid))
                                return pf->vsi[i];
 
@@ -843,7 +843,7 @@ static void i40e_dbg_dump_vsi_no_seid(struct i40e_pf *pf)
 {
        int i;
 
-       for (i = 0; i < pf->hw.func_caps.num_vsis; i++)
+       for (i = 0; i < pf->num_alloc_vsi; i++)
                if (pf->vsi[i])
                        dev_info(&pf->pdev->dev, "dump vsi[%d]: %d\n",
                                 i, pf->vsi[i]->seid);
@@ -862,12 +862,11 @@ static void i40e_dbg_dump_eth_stats(struct i40e_pf *pf,
                 "    rx_bytes = \t%lld \trx_unicast = \t\t%lld \trx_multicast = \t%lld\n",
                estats->rx_bytes, estats->rx_unicast, estats->rx_multicast);
        dev_info(&pf->pdev->dev,
-                "    rx_broadcast = \t%lld \trx_discards = \t\t%lld \trx_errors = \t%lld\n",
-                estats->rx_broadcast, estats->rx_discards, estats->rx_errors);
+                "    rx_broadcast = \t%lld \trx_discards = \t\t%lld\n",
+                estats->rx_broadcast, estats->rx_discards);
        dev_info(&pf->pdev->dev,
-                "    rx_missed = \t%lld \trx_unknown_protocol = \t%lld \ttx_bytes = \t%lld\n",
-                estats->rx_missed, estats->rx_unknown_protocol,
-                estats->tx_bytes);
+                "    rx_unknown_protocol = \t%lld \ttx_bytes = \t%lld\n",
+                estats->rx_unknown_protocol, estats->tx_bytes);
        dev_info(&pf->pdev->dev,
                 "    tx_unicast = \t%lld \ttx_multicast = \t\t%lld \ttx_broadcast = \t%lld\n",
                 estats->tx_unicast, estats->tx_multicast, estats->tx_broadcast);
@@ -1527,7 +1526,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                        cnt = sscanf(&cmd_buf[15], "%i", &vsi_seid);
                        if (cnt == 0) {
                                int i;
-                               for (i = 0; i < pf->hw.func_caps.num_vsis; i++)
+                               for (i = 0; i < pf->num_alloc_vsi; i++)
                                        i40e_vsi_reset_stats(pf->vsi[i]);
                                dev_info(&pf->pdev->dev, "vsi clear stats called for all vsi's\n");
                        } else if (cnt == 1) {
@@ -1744,10 +1743,6 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                i40e_dbg_cmd_fd_ctrl(pf, I40E_FLAG_FD_ATR_ENABLED, false);
        } else if (strncmp(cmd_buf, "fd-atr on", 9) == 0) {
                i40e_dbg_cmd_fd_ctrl(pf, I40E_FLAG_FD_ATR_ENABLED, true);
-       } else if (strncmp(cmd_buf, "fd-sb off", 9) == 0) {
-               i40e_dbg_cmd_fd_ctrl(pf, I40E_FLAG_FD_SB_ENABLED, false);
-       } else if (strncmp(cmd_buf, "fd-sb on", 8) == 0) {
-               i40e_dbg_cmd_fd_ctrl(pf, I40E_FLAG_FD_SB_ENABLED, true);
        } else if (strncmp(cmd_buf, "lldp", 4) == 0) {
                if (strncmp(&cmd_buf[5], "stop", 4) == 0) {
                        int ret;
@@ -1967,8 +1962,6 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                dev_info(&pf->pdev->dev, "  rem fd_filter <dest q_index> <flex_off> <pctype> <dest_vsi> <dest_ctl> <fd_status> <cnt_index> <fd_id> <packet_len> <packet>\n");
                dev_info(&pf->pdev->dev, "  fd-atr off\n");
                dev_info(&pf->pdev->dev, "  fd-atr on\n");
-               dev_info(&pf->pdev->dev, "  fd-sb off\n");
-               dev_info(&pf->pdev->dev, "  fd-sb on\n");
                dev_info(&pf->pdev->dev, "  lldp start\n");
                dev_info(&pf->pdev->dev, "  lldp stop\n");
                dev_info(&pf->pdev->dev, "  lldp get local\n");
index b2380daef8c142941f0bda2b7d11620f7de75ad5..56438bd579e61a24d2f2c2cefefc89eecf2a926a 100644 (file)
@@ -67,17 +67,25 @@ static i40e_status i40e_diag_reg_pattern_test(struct i40e_hw *hw,
 
 struct i40e_diag_reg_test_info i40e_reg_list[] = {
        /* offset               mask         elements   stride */
-       {I40E_QTX_CTL(0),       0x0000FFBF,   4, I40E_QTX_CTL(1) - I40E_QTX_CTL(0)},
-       {I40E_PFINT_ITR0(0),    0x00000FFF,   3, I40E_PFINT_ITR0(1) - I40E_PFINT_ITR0(0)},
-       {I40E_PFINT_ITRN(0, 0), 0x00000FFF,   8, I40E_PFINT_ITRN(0, 1) - I40E_PFINT_ITRN(0, 0)},
-       {I40E_PFINT_ITRN(1, 0), 0x00000FFF,   8, I40E_PFINT_ITRN(1, 1) - I40E_PFINT_ITRN(1, 0)},
-       {I40E_PFINT_ITRN(2, 0), 0x00000FFF,   8, I40E_PFINT_ITRN(2, 1) - I40E_PFINT_ITRN(2, 0)},
-       {I40E_PFINT_STAT_CTL0,  0x0000000C,   1, 0},
-       {I40E_PFINT_LNKLST0,    0x00001FFF,   1, 0},
-       {I40E_PFINT_LNKLSTN(0), 0x000007FF,  64, I40E_PFINT_LNKLSTN(1) - I40E_PFINT_LNKLSTN(0)},
-       {I40E_QINT_TQCTL(0),    0x000000FF,  64, I40E_QINT_TQCTL(1) - I40E_QINT_TQCTL(0)},
-       {I40E_QINT_RQCTL(0),    0x000000FF,  64, I40E_QINT_RQCTL(1) - I40E_QINT_RQCTL(0)},
-       {I40E_PFINT_ICR0_ENA,   0xF7F20000,   1, 0},
+       {I40E_QTX_CTL(0),       0x0000FFBF, 1,
+               I40E_QTX_CTL(1) - I40E_QTX_CTL(0)},
+       {I40E_PFINT_ITR0(0),    0x00000FFF, 3,
+               I40E_PFINT_ITR0(1) - I40E_PFINT_ITR0(0)},
+       {I40E_PFINT_ITRN(0, 0), 0x00000FFF, 1,
+               I40E_PFINT_ITRN(0, 1) - I40E_PFINT_ITRN(0, 0)},
+       {I40E_PFINT_ITRN(1, 0), 0x00000FFF, 1,
+               I40E_PFINT_ITRN(1, 1) - I40E_PFINT_ITRN(1, 0)},
+       {I40E_PFINT_ITRN(2, 0), 0x00000FFF, 1,
+               I40E_PFINT_ITRN(2, 1) - I40E_PFINT_ITRN(2, 0)},
+       {I40E_PFINT_STAT_CTL0,  0x0000000C, 1, 0},
+       {I40E_PFINT_LNKLST0,    0x00001FFF, 1, 0},
+       {I40E_PFINT_LNKLSTN(0), 0x000007FF, 1,
+               I40E_PFINT_LNKLSTN(1) - I40E_PFINT_LNKLSTN(0)},
+       {I40E_QINT_TQCTL(0),    0x000000FF, 1,
+               I40E_QINT_TQCTL(1) - I40E_QINT_TQCTL(0)},
+       {I40E_QINT_RQCTL(0),    0x000000FF, 1,
+               I40E_QINT_RQCTL(1) - I40E_QINT_RQCTL(0)},
+       {I40E_PFINT_ICR0_ENA,   0xF7F20000, 1, 0},
        { 0 }
 };
 
@@ -93,9 +101,25 @@ i40e_status i40e_diag_reg_test(struct i40e_hw *hw)
        u32 reg, mask;
        u32 i, j;
 
-       for (i = 0; (i40e_reg_list[i].offset != 0) && !ret_code; i++) {
+       for (i = 0; i40e_reg_list[i].offset != 0 &&
+                                            !ret_code; i++) {
+
+               /* set actual reg range for dynamically allocated resources */
+               if (i40e_reg_list[i].offset == I40E_QTX_CTL(0) &&
+                   hw->func_caps.num_tx_qp != 0)
+                       i40e_reg_list[i].elements = hw->func_caps.num_tx_qp;
+               if ((i40e_reg_list[i].offset == I40E_PFINT_ITRN(0, 0) ||
+                    i40e_reg_list[i].offset == I40E_PFINT_ITRN(1, 0) ||
+                    i40e_reg_list[i].offset == I40E_PFINT_ITRN(2, 0) ||
+                    i40e_reg_list[i].offset == I40E_QINT_TQCTL(0) ||
+                    i40e_reg_list[i].offset == I40E_QINT_RQCTL(0)) &&
+                   hw->func_caps.num_msix_vectors != 0)
+                       i40e_reg_list[i].elements =
+                               hw->func_caps.num_msix_vectors - 1;
+
+               /* test register access */
                mask = i40e_reg_list[i].mask;
-               for (j = 0; (j < i40e_reg_list[i].elements) && !ret_code; j++) {
+               for (j = 0; j < i40e_reg_list[i].elements && !ret_code; j++) {
                        reg = i40e_reg_list[i].offset +
                              (j * i40e_reg_list[i].stride);
                        ret_code = i40e_diag_reg_pattern_test(hw, reg, mask);
index 03d99cbc5c251bcbb0120667ff1d53e304da00a9..4a488ffcd6b0b1221e31755116acf7830f49d20c 100644 (file)
@@ -46,6 +46,8 @@ struct i40e_stats {
                I40E_STAT(struct i40e_pf, _name, _stat)
 #define I40E_VSI_STAT(_name, _stat) \
                I40E_STAT(struct i40e_vsi, _name, _stat)
+#define I40E_VEB_STAT(_name, _stat) \
+               I40E_STAT(struct i40e_veb, _name, _stat)
 
 static const struct i40e_stats i40e_gstrings_net_stats[] = {
        I40E_NETDEV_STAT(rx_packets),
@@ -56,12 +58,36 @@ static const struct i40e_stats i40e_gstrings_net_stats[] = {
        I40E_NETDEV_STAT(tx_errors),
        I40E_NETDEV_STAT(rx_dropped),
        I40E_NETDEV_STAT(tx_dropped),
-       I40E_NETDEV_STAT(multicast),
        I40E_NETDEV_STAT(collisions),
        I40E_NETDEV_STAT(rx_length_errors),
        I40E_NETDEV_STAT(rx_crc_errors),
 };
 
+static const struct i40e_stats i40e_gstrings_veb_stats[] = {
+       I40E_VEB_STAT("rx_bytes", stats.rx_bytes),
+       I40E_VEB_STAT("tx_bytes", stats.tx_bytes),
+       I40E_VEB_STAT("rx_unicast", stats.rx_unicast),
+       I40E_VEB_STAT("tx_unicast", stats.tx_unicast),
+       I40E_VEB_STAT("rx_multicast", stats.rx_multicast),
+       I40E_VEB_STAT("tx_multicast", stats.tx_multicast),
+       I40E_VEB_STAT("rx_broadcast", stats.rx_broadcast),
+       I40E_VEB_STAT("tx_broadcast", stats.tx_broadcast),
+       I40E_VEB_STAT("rx_discards", stats.rx_discards),
+       I40E_VEB_STAT("tx_discards", stats.tx_discards),
+       I40E_VEB_STAT("tx_errors", stats.tx_errors),
+       I40E_VEB_STAT("rx_unknown_protocol", stats.rx_unknown_protocol),
+};
+
+static const struct i40e_stats i40e_gstrings_misc_stats[] = {
+       I40E_VSI_STAT("rx_unicast", eth_stats.rx_unicast),
+       I40E_VSI_STAT("tx_unicast", eth_stats.tx_unicast),
+       I40E_VSI_STAT("rx_multicast", eth_stats.rx_multicast),
+       I40E_VSI_STAT("tx_multicast", eth_stats.tx_multicast),
+       I40E_VSI_STAT("rx_broadcast", eth_stats.rx_broadcast),
+       I40E_VSI_STAT("tx_broadcast", eth_stats.tx_broadcast),
+       I40E_VSI_STAT("rx_unknown_protocol", eth_stats.rx_unknown_protocol),
+};
+
 static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi,
                                 struct ethtool_rxnfc *cmd);
 
@@ -78,7 +104,12 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi,
 static struct i40e_stats i40e_gstrings_stats[] = {
        I40E_PF_STAT("rx_bytes", stats.eth.rx_bytes),
        I40E_PF_STAT("tx_bytes", stats.eth.tx_bytes),
-       I40E_PF_STAT("rx_errors", stats.eth.rx_errors),
+       I40E_PF_STAT("rx_unicast", stats.eth.rx_unicast),
+       I40E_PF_STAT("tx_unicast", stats.eth.tx_unicast),
+       I40E_PF_STAT("rx_multicast", stats.eth.rx_multicast),
+       I40E_PF_STAT("tx_multicast", stats.eth.tx_multicast),
+       I40E_PF_STAT("rx_broadcast", stats.eth.rx_broadcast),
+       I40E_PF_STAT("tx_broadcast", stats.eth.tx_broadcast),
        I40E_PF_STAT("tx_errors", stats.eth.tx_errors),
        I40E_PF_STAT("rx_dropped", stats.eth.rx_discards),
        I40E_PF_STAT("tx_dropped", stats.eth.tx_discards),
@@ -88,6 +119,7 @@ static struct i40e_stats i40e_gstrings_stats[] = {
        I40E_PF_STAT("mac_local_faults", stats.mac_local_faults),
        I40E_PF_STAT("mac_remote_faults", stats.mac_remote_faults),
        I40E_PF_STAT("tx_timeout", tx_timeout_count),
+       I40E_PF_STAT("rx_csum_bad", hw_csum_rx_error),
        I40E_PF_STAT("rx_length_errors", stats.rx_length_errors),
        I40E_PF_STAT("link_xon_rx", stats.link_xon_rx),
        I40E_PF_STAT("link_xoff_rx", stats.link_xoff_rx),
@@ -112,8 +144,10 @@ static struct i40e_stats i40e_gstrings_stats[] = {
        I40E_PF_STAT("rx_oversize", stats.rx_oversize),
        I40E_PF_STAT("rx_jabber", stats.rx_jabber),
        I40E_PF_STAT("VF_admin_queue_requests", vf_aq_requests),
-       I40E_PF_STAT("tx_hwtstamp_timeouts", tx_hwtstamp_timeouts),
        I40E_PF_STAT("rx_hwtstamp_cleared", rx_hwtstamp_cleared),
+       I40E_PF_STAT("fdir_atr_match", stats.fd_atr_match),
+       I40E_PF_STAT("fdir_sb_match", stats.fd_sb_match),
+
        /* LPI stats */
        I40E_PF_STAT("tx_lpi_status", stats.tx_lpi_status),
        I40E_PF_STAT("rx_lpi_status", stats.rx_lpi_status),
@@ -122,11 +156,14 @@ static struct i40e_stats i40e_gstrings_stats[] = {
 };
 
 #define I40E_QUEUE_STATS_LEN(n) \
-  ((((struct i40e_netdev_priv *)netdev_priv((n)))->vsi->num_queue_pairs + \
-    ((struct i40e_netdev_priv *)netdev_priv((n)))->vsi->num_queue_pairs) * 2)
+       (((struct i40e_netdev_priv *)netdev_priv((n)))->vsi->num_queue_pairs \
+           * 2 /* Tx and Rx together */                                     \
+           * (sizeof(struct i40e_queue_stats) / sizeof(u64)))
 #define I40E_GLOBAL_STATS_LEN  ARRAY_SIZE(i40e_gstrings_stats)
 #define I40E_NETDEV_STATS_LEN   ARRAY_SIZE(i40e_gstrings_net_stats)
+#define I40E_MISC_STATS_LEN    ARRAY_SIZE(i40e_gstrings_misc_stats)
 #define I40E_VSI_STATS_LEN(n)   (I40E_NETDEV_STATS_LEN + \
+                                I40E_MISC_STATS_LEN + \
                                 I40E_QUEUE_STATS_LEN((n)))
 #define I40E_PFC_STATS_LEN ( \
                (FIELD_SIZEOF(struct i40e_pf, stats.priority_xoff_rx) + \
@@ -135,6 +172,7 @@ static struct i40e_stats i40e_gstrings_stats[] = {
                 FIELD_SIZEOF(struct i40e_pf, stats.priority_xon_tx) + \
                 FIELD_SIZEOF(struct i40e_pf, stats.priority_xon_2_xoff)) \
                 / sizeof(u64))
+#define I40E_VEB_STATS_LEN     ARRAY_SIZE(i40e_gstrings_veb_stats)
 #define I40E_PF_STATS_LEN(n)   (I40E_GLOBAL_STATS_LEN + \
                                 I40E_PFC_STATS_LEN + \
                                 I40E_VSI_STATS_LEN((n)))
@@ -620,10 +658,15 @@ static int i40e_get_sset_count(struct net_device *netdev, int sset)
        case ETH_SS_TEST:
                return I40E_TEST_LEN;
        case ETH_SS_STATS:
-               if (vsi == pf->vsi[pf->lan_vsi])
-                       return I40E_PF_STATS_LEN(netdev);
-               else
+               if (vsi == pf->vsi[pf->lan_vsi]) {
+                       int len = I40E_PF_STATS_LEN(netdev);
+
+                       if (pf->lan_veb != I40E_NO_VEB)
+                               len += I40E_VEB_STATS_LEN;
+                       return len;
+               } else {
                        return I40E_VSI_STATS_LEN(netdev);
+               }
        default:
                return -EOPNOTSUPP;
        }
@@ -633,6 +676,7 @@ static void i40e_get_ethtool_stats(struct net_device *netdev,
                                   struct ethtool_stats *stats, u64 *data)
 {
        struct i40e_netdev_priv *np = netdev_priv(netdev);
+       struct i40e_ring *tx_ring, *rx_ring;
        struct i40e_vsi *vsi = np->vsi;
        struct i40e_pf *pf = vsi->back;
        int i = 0;
@@ -648,10 +692,14 @@ static void i40e_get_ethtool_stats(struct net_device *netdev,
                data[i++] = (i40e_gstrings_net_stats[j].sizeof_stat ==
                        sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
        }
+       for (j = 0; j < I40E_MISC_STATS_LEN; j++) {
+               p = (char *)vsi + i40e_gstrings_misc_stats[j].stat_offset;
+               data[i++] = (i40e_gstrings_misc_stats[j].sizeof_stat ==
+                           sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+       }
        rcu_read_lock();
-       for (j = 0; j < vsi->num_queue_pairs; j++, i += 4) {
-               struct i40e_ring *tx_ring = ACCESS_ONCE(vsi->tx_rings[j]);
-               struct i40e_ring *rx_ring;
+       for (j = 0; j < vsi->num_queue_pairs; j++) {
+               tx_ring = ACCESS_ONCE(vsi->tx_rings[j]);
 
                if (!tx_ring)
                        continue;
@@ -662,33 +710,45 @@ static void i40e_get_ethtool_stats(struct net_device *netdev,
                        data[i] = tx_ring->stats.packets;
                        data[i + 1] = tx_ring->stats.bytes;
                } while (u64_stats_fetch_retry_irq(&tx_ring->syncp, start));
+               i += 2;
 
                /* Rx ring is the 2nd half of the queue pair */
                rx_ring = &tx_ring[1];
                do {
                        start = u64_stats_fetch_begin_irq(&rx_ring->syncp);
-                       data[i + 2] = rx_ring->stats.packets;
-                       data[i + 3] = rx_ring->stats.bytes;
+                       data[i] = rx_ring->stats.packets;
+                       data[i + 1] = rx_ring->stats.bytes;
                } while (u64_stats_fetch_retry_irq(&rx_ring->syncp, start));
+               i += 2;
        }
        rcu_read_unlock();
-       if (vsi == pf->vsi[pf->lan_vsi]) {
-               for (j = 0; j < I40E_GLOBAL_STATS_LEN; j++) {
-                       p = (char *)pf + i40e_gstrings_stats[j].stat_offset;
-                       data[i++] = (i40e_gstrings_stats[j].sizeof_stat ==
-                                  sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
-               }
-               for (j = 0; j < I40E_MAX_USER_PRIORITY; j++) {
-                       data[i++] = pf->stats.priority_xon_tx[j];
-                       data[i++] = pf->stats.priority_xoff_tx[j];
-               }
-               for (j = 0; j < I40E_MAX_USER_PRIORITY; j++) {
-                       data[i++] = pf->stats.priority_xon_rx[j];
-                       data[i++] = pf->stats.priority_xoff_rx[j];
+       if (vsi != pf->vsi[pf->lan_vsi])
+               return;
+
+       if (pf->lan_veb != I40E_NO_VEB) {
+               struct i40e_veb *veb = pf->veb[pf->lan_veb];
+               for (j = 0; j < I40E_VEB_STATS_LEN; j++) {
+                       p = (char *)veb;
+                       p += i40e_gstrings_veb_stats[j].stat_offset;
+                       data[i++] = (i40e_gstrings_veb_stats[j].sizeof_stat ==
+                                    sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
                }
-               for (j = 0; j < I40E_MAX_USER_PRIORITY; j++)
-                       data[i++] = pf->stats.priority_xon_2_xoff[j];
        }
+       for (j = 0; j < I40E_GLOBAL_STATS_LEN; j++) {
+               p = (char *)pf + i40e_gstrings_stats[j].stat_offset;
+               data[i++] = (i40e_gstrings_stats[j].sizeof_stat ==
+                            sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+       }
+       for (j = 0; j < I40E_MAX_USER_PRIORITY; j++) {
+               data[i++] = pf->stats.priority_xon_tx[j];
+               data[i++] = pf->stats.priority_xoff_tx[j];
+       }
+       for (j = 0; j < I40E_MAX_USER_PRIORITY; j++) {
+               data[i++] = pf->stats.priority_xon_rx[j];
+               data[i++] = pf->stats.priority_xoff_rx[j];
+       }
+       for (j = 0; j < I40E_MAX_USER_PRIORITY; j++)
+               data[i++] = pf->stats.priority_xon_2_xoff[j];
 }
 
 static void i40e_get_strings(struct net_device *netdev, u32 stringset,
@@ -713,6 +773,11 @@ static void i40e_get_strings(struct net_device *netdev, u32 stringset,
                                 i40e_gstrings_net_stats[i].stat_string);
                        p += ETH_GSTRING_LEN;
                }
+               for (i = 0; i < I40E_MISC_STATS_LEN; i++) {
+                       snprintf(p, ETH_GSTRING_LEN, "%s",
+                                i40e_gstrings_misc_stats[i].stat_string);
+                       p += ETH_GSTRING_LEN;
+               }
                for (i = 0; i < vsi->num_queue_pairs; i++) {
                        snprintf(p, ETH_GSTRING_LEN, "tx-%u.tx_packets", i);
                        p += ETH_GSTRING_LEN;
@@ -723,34 +788,42 @@ static void i40e_get_strings(struct net_device *netdev, u32 stringset,
                        snprintf(p, ETH_GSTRING_LEN, "rx-%u.rx_bytes", i);
                        p += ETH_GSTRING_LEN;
                }
-               if (vsi == pf->vsi[pf->lan_vsi]) {
-                       for (i = 0; i < I40E_GLOBAL_STATS_LEN; i++) {
-                               snprintf(p, ETH_GSTRING_LEN, "port.%s",
-                                        i40e_gstrings_stats[i].stat_string);
-                               p += ETH_GSTRING_LEN;
-                       }
-                       for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
-                               snprintf(p, ETH_GSTRING_LEN,
-                                        "port.tx_priority_%u_xon", i);
-                               p += ETH_GSTRING_LEN;
-                               snprintf(p, ETH_GSTRING_LEN,
-                                        "port.tx_priority_%u_xoff", i);
-                               p += ETH_GSTRING_LEN;
-                       }
-                       for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
-                               snprintf(p, ETH_GSTRING_LEN,
-                                        "port.rx_priority_%u_xon", i);
-                               p += ETH_GSTRING_LEN;
-                               snprintf(p, ETH_GSTRING_LEN,
-                                        "port.rx_priority_%u_xoff", i);
-                               p += ETH_GSTRING_LEN;
-                       }
-                       for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
-                               snprintf(p, ETH_GSTRING_LEN,
-                                        "port.rx_priority_%u_xon_2_xoff", i);
+               if (vsi != pf->vsi[pf->lan_vsi])
+                       return;
+
+               if (pf->lan_veb != I40E_NO_VEB) {
+                       for (i = 0; i < I40E_VEB_STATS_LEN; i++) {
+                               snprintf(p, ETH_GSTRING_LEN, "veb.%s",
+                                       i40e_gstrings_veb_stats[i].stat_string);
                                p += ETH_GSTRING_LEN;
                        }
                }
+               for (i = 0; i < I40E_GLOBAL_STATS_LEN; i++) {
+                       snprintf(p, ETH_GSTRING_LEN, "port.%s",
+                                i40e_gstrings_stats[i].stat_string);
+                       p += ETH_GSTRING_LEN;
+               }
+               for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
+                       snprintf(p, ETH_GSTRING_LEN,
+                                "port.tx_priority_%u_xon", i);
+                       p += ETH_GSTRING_LEN;
+                       snprintf(p, ETH_GSTRING_LEN,
+                                "port.tx_priority_%u_xoff", i);
+                       p += ETH_GSTRING_LEN;
+               }
+               for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
+                       snprintf(p, ETH_GSTRING_LEN,
+                                "port.rx_priority_%u_xon", i);
+                       p += ETH_GSTRING_LEN;
+                       snprintf(p, ETH_GSTRING_LEN,
+                                "port.rx_priority_%u_xoff", i);
+                       p += ETH_GSTRING_LEN;
+               }
+               for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
+                       snprintf(p, ETH_GSTRING_LEN,
+                                "port.rx_priority_%u_xon_2_xoff", i);
+                       p += ETH_GSTRING_LEN;
+               }
                /* BUG_ON(p - data != I40E_STATS_LEN * ETH_GSTRING_LEN); */
                break;
        }
@@ -1007,14 +1080,13 @@ static int i40e_get_coalesce(struct net_device *netdev,
        ec->rx_max_coalesced_frames_irq = vsi->work_limit;
 
        if (ITR_IS_DYNAMIC(vsi->rx_itr_setting))
-               ec->rx_coalesce_usecs = 1;
-       else
-               ec->rx_coalesce_usecs = vsi->rx_itr_setting;
+               ec->use_adaptive_rx_coalesce = 1;
 
        if (ITR_IS_DYNAMIC(vsi->tx_itr_setting))
-               ec->tx_coalesce_usecs = 1;
-       else
-               ec->tx_coalesce_usecs = vsi->tx_itr_setting;
+               ec->use_adaptive_tx_coalesce = 1;
+
+       ec->rx_coalesce_usecs = vsi->rx_itr_setting & ~I40E_ITR_DYNAMIC;
+       ec->tx_coalesce_usecs = vsi->tx_itr_setting & ~I40E_ITR_DYNAMIC;
 
        return 0;
 }
@@ -1033,37 +1105,27 @@ static int i40e_set_coalesce(struct net_device *netdev,
        if (ec->tx_max_coalesced_frames_irq || ec->rx_max_coalesced_frames_irq)
                vsi->work_limit = ec->tx_max_coalesced_frames_irq;
 
-       switch (ec->rx_coalesce_usecs) {
-       case 0:
-               vsi->rx_itr_setting = 0;
-               break;
-       case 1:
-               vsi->rx_itr_setting = (I40E_ITR_DYNAMIC |
-                                      ITR_REG_TO_USEC(I40E_ITR_RX_DEF));
-               break;
-       default:
-               if ((ec->rx_coalesce_usecs < (I40E_MIN_ITR << 1)) ||
-                   (ec->rx_coalesce_usecs > (I40E_MAX_ITR << 1)))
-                       return -EINVAL;
+       if ((ec->rx_coalesce_usecs >= (I40E_MIN_ITR << 1)) &&
+           (ec->rx_coalesce_usecs <= (I40E_MAX_ITR << 1)))
                vsi->rx_itr_setting = ec->rx_coalesce_usecs;
-               break;
-       }
+       else
+               return -EINVAL;
 
-       switch (ec->tx_coalesce_usecs) {
-       case 0:
-               vsi->tx_itr_setting = 0;
-               break;
-       case 1:
-               vsi->tx_itr_setting = (I40E_ITR_DYNAMIC |
-                                      ITR_REG_TO_USEC(I40E_ITR_TX_DEF));
-               break;
-       default:
-               if ((ec->tx_coalesce_usecs < (I40E_MIN_ITR << 1)) ||
-                   (ec->tx_coalesce_usecs > (I40E_MAX_ITR << 1)))
-                       return -EINVAL;
+       if ((ec->tx_coalesce_usecs >= (I40E_MIN_ITR << 1)) &&
+           (ec->tx_coalesce_usecs <= (I40E_MAX_ITR << 1)))
                vsi->tx_itr_setting = ec->tx_coalesce_usecs;
-               break;
-       }
+       else
+               return -EINVAL;
+
+       if (ec->use_adaptive_rx_coalesce)
+               vsi->rx_itr_setting |= I40E_ITR_DYNAMIC;
+       else
+               vsi->rx_itr_setting &= ~I40E_ITR_DYNAMIC;
+
+       if (ec->use_adaptive_tx_coalesce)
+               vsi->tx_itr_setting |= I40E_ITR_DYNAMIC;
+       else
+               vsi->tx_itr_setting &= ~I40E_ITR_DYNAMIC;
 
        vector = vsi->base_vector;
        for (i = 0; i < vsi->num_q_vectors; i++, vector++) {
@@ -1140,8 +1202,7 @@ static int i40e_get_ethtool_fdir_all(struct i40e_pf *pf,
        int cnt = 0;
 
        /* report total rule count */
-       cmd->data = pf->hw.fdir_shared_filter_count +
-                   pf->fdir_pf_filter_count;
+       cmd->data = i40e_get_fd_cnt_all(pf);
 
        hlist_for_each_entry_safe(rule, node2,
                                  &pf->fdir_filter_list, fdir_node) {
@@ -1175,10 +1236,6 @@ static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf,
        struct i40e_fdir_filter *rule = NULL;
        struct hlist_node *node2;
 
-       /* report total rule count */
-       cmd->data = pf->hw.fdir_shared_filter_count +
-                   pf->fdir_pf_filter_count;
-
        hlist_for_each_entry_safe(rule, node2,
                                  &pf->fdir_filter_list, fdir_node) {
                if (fsp->location <= rule->fd_id)
@@ -1189,11 +1246,24 @@ static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf,
                return -EINVAL;
 
        fsp->flow_type = rule->flow_type;
-       fsp->h_u.tcp_ip4_spec.psrc = rule->src_port;
-       fsp->h_u.tcp_ip4_spec.pdst = rule->dst_port;
-       fsp->h_u.tcp_ip4_spec.ip4src = rule->src_ip[0];
-       fsp->h_u.tcp_ip4_spec.ip4dst = rule->dst_ip[0];
-       fsp->ring_cookie = rule->q_index;
+       if (fsp->flow_type == IP_USER_FLOW) {
+               fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
+               fsp->h_u.usr_ip4_spec.proto = 0;
+               fsp->m_u.usr_ip4_spec.proto = 0;
+       }
+
+       /* Reverse the src and dest notion, since the HW views them from
+        * Tx perspective where as the user expects it from Rx filter view.
+        */
+       fsp->h_u.tcp_ip4_spec.psrc = rule->dst_port;
+       fsp->h_u.tcp_ip4_spec.pdst = rule->src_port;
+       fsp->h_u.tcp_ip4_spec.ip4src = rule->dst_ip[0];
+       fsp->h_u.tcp_ip4_spec.ip4dst = rule->src_ip[0];
+
+       if (rule->dest_ctl == I40E_FILTER_PROGRAM_DESC_DEST_DROP_PACKET)
+               fsp->ring_cookie = RX_CLS_FLOW_DISC;
+       else
+               fsp->ring_cookie = rule->q_index;
 
        return 0;
 }
@@ -1223,6 +1293,8 @@ static int i40e_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
                break;
        case ETHTOOL_GRXCLSRLCNT:
                cmd->rule_cnt = pf->fdir_pf_active_filters;
+               /* report total rule count */
+               cmd->data = i40e_get_fd_cnt_all(pf);
                ret = 0;
                break;
        case ETHTOOL_GRXCLSRULE:
@@ -1291,16 +1363,12 @@ static int i40e_set_rss_hash_opt(struct i40e_pf *pf, struct ethtool_rxnfc *nfc)
        case UDP_V4_FLOW:
                switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
                case 0:
-                       hena &=
-                       ~(((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) |
-                       ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) |
-                       ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4));
+                       hena &= ~(((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) |
+                                 ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4));
                        break;
                case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
-                       hena |=
-                       (((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP)  |
-                       ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) |
-                       ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4));
+                       hena |= (((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) |
+                                 ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4));
                        break;
                default:
                        return -EINVAL;
@@ -1309,16 +1377,12 @@ static int i40e_set_rss_hash_opt(struct i40e_pf *pf, struct ethtool_rxnfc *nfc)
        case UDP_V6_FLOW:
                switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
                case 0:
-                       hena &=
-                       ~(((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) |
-                       ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP) |
-                       ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6));
+                       hena &= ~(((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) |
+                                 ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6));
                        break;
                case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
-                       hena |=
-                       (((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP)  |
-                       ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP) |
-                       ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6));
+                       hena |= (((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) |
+                                ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6));
                        break;
                default:
                        return -EINVAL;
@@ -1503,7 +1567,8 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi,
                return -EINVAL;
        }
 
-       if (fsp->ring_cookie >= vsi->num_queue_pairs)
+       if ((fsp->ring_cookie != RX_CLS_FLOW_DISC) &&
+           (fsp->ring_cookie >= vsi->num_queue_pairs))
                return -EINVAL;
 
        input = kzalloc(sizeof(*input), GFP_KERNEL);
@@ -1524,13 +1589,17 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi,
        input->pctype = 0;
        input->dest_vsi = vsi->id;
        input->fd_status = I40E_FILTER_PROGRAM_DESC_FD_STATUS_FD_ID;
-       input->cnt_index = 0;
+       input->cnt_index  = pf->fd_sb_cnt_idx;
        input->flow_type = fsp->flow_type;
        input->ip4_proto = fsp->h_u.usr_ip4_spec.proto;
-       input->src_port = fsp->h_u.tcp_ip4_spec.psrc;
-       input->dst_port = fsp->h_u.tcp_ip4_spec.pdst;
-       input->src_ip[0] = fsp->h_u.tcp_ip4_spec.ip4src;
-       input->dst_ip[0] = fsp->h_u.tcp_ip4_spec.ip4dst;
+
+       /* Reverse the src and dest notion, since the HW expects them to be from
+        * Tx perspective where as the input from user is from Rx filter view.
+        */
+       input->dst_port = fsp->h_u.tcp_ip4_spec.psrc;
+       input->src_port = fsp->h_u.tcp_ip4_spec.pdst;
+       input->dst_ip[0] = fsp->h_u.tcp_ip4_spec.ip4src;
+       input->src_ip[0] = fsp->h_u.tcp_ip4_spec.ip4dst;
 
        ret = i40e_add_del_fdir(vsi, input, true);
        if (ret)
@@ -1692,5 +1761,5 @@ static const struct ethtool_ops i40e_ethtool_ops = {
 
 void i40e_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &i40e_ethtool_ops);
+       netdev->ethtool_ops = &i40e_ethtool_ops;
 }
index bf2d4cc5b56927e87a1be2cd5772198533d27afa..9b987ccc9e828738caef1c4811c5db1ca33b388d 100644 (file)
@@ -201,7 +201,7 @@ exit:
  **/
 i40e_status i40e_remove_pd_bp(struct i40e_hw *hw,
                                        struct i40e_hmc_info *hmc_info,
-                                       u32 idx, bool is_pf)
+                                       u32 idx)
 {
        i40e_status ret_code = 0;
        struct i40e_hmc_pd_entry *pd_entry;
@@ -237,10 +237,7 @@ i40e_status i40e_remove_pd_bp(struct i40e_hw *hw,
        pd_addr = (u64 *)pd_table->pd_page_addr.va;
        pd_addr += rel_pd_idx;
        memset(pd_addr, 0, sizeof(u64));
-       if (is_pf)
-               I40E_INVALIDATE_PF_HMC_PD(hw, sd_idx, idx);
-       else
-               I40E_INVALIDATE_VF_HMC_PD(hw, sd_idx, idx, hmc_info->hmc_fn_id);
+       I40E_INVALIDATE_PF_HMC_PD(hw, sd_idx, idx);
 
        /* free memory here */
        ret_code = i40e_free_dma_mem(hw, &(pd_entry->bp.addr));
index 0cd4701234f8c0fc41f64e926640fca1a5ad9757..b45d8fedc5e755e712e753c4f6b47a977545c45a 100644 (file)
@@ -163,11 +163,6 @@ struct i40e_hmc_info {
            (((sd_idx) << I40E_PFHMC_PDINV_PMSDIDX_SHIFT) |             \
             ((pd_idx) << I40E_PFHMC_PDINV_PMPDIDX_SHIFT)))
 
-#define I40E_INVALIDATE_VF_HMC_PD(hw, sd_idx, pd_idx, hmc_fn_id)          \
-       wr32((hw), I40E_GLHMC_VFPDINV((hmc_fn_id) - I40E_FIRST_VF_FPM_ID), \
-            (((sd_idx) << I40E_PFHMC_PDINV_PMSDIDX_SHIFT) |               \
-             ((pd_idx) << I40E_PFHMC_PDINV_PMPDIDX_SHIFT)))
-
 /**
  * I40E_FIND_SD_INDEX_LIMIT - finds segment descriptor index limit
  * @hmc_info: pointer to the HMC configuration information structure
@@ -226,7 +221,7 @@ i40e_status i40e_add_pd_table_entry(struct i40e_hw *hw,
                                              u32 pd_index);
 i40e_status i40e_remove_pd_bp(struct i40e_hw *hw,
                                        struct i40e_hmc_info *hmc_info,
-                                       u32 idx, bool is_pf);
+                                       u32 idx);
 i40e_status i40e_prep_remove_sd_bp(struct i40e_hmc_info *hmc_info,
                                             u32 idx);
 i40e_status i40e_remove_sd_bp_new(struct i40e_hw *hw,
index d5d98fe2691dd6048f7edba7f6b4847076e5316d..870ab1ee072cdf4759e848a8eca6cdbf6dc0a793 100644 (file)
@@ -397,7 +397,7 @@ static i40e_status i40e_create_lan_hmc_object(struct i40e_hw *hw,
                                /* remove the backing pages from pd_idx1 to i */
                                while (i && (i > pd_idx1)) {
                                        i40e_remove_pd_bp(hw, info->hmc_info,
-                                                         (i - 1), true);
+                                                         (i - 1));
                                        i--;
                                }
                        }
@@ -433,11 +433,7 @@ exit_sd_error:
                                      ((j - 1) * I40E_HMC_MAX_BP_COUNT));
                        pd_lmt1 = min(pd_lmt, (j * I40E_HMC_MAX_BP_COUNT));
                        for (i = pd_idx1; i < pd_lmt1; i++) {
-                               i40e_remove_pd_bp(
-                                       hw,
-                                       info->hmc_info,
-                                       i,
-                                       true);
+                               i40e_remove_pd_bp(hw, info->hmc_info, i);
                        }
                        i40e_remove_pd_page(hw, info->hmc_info, (j - 1));
                        break;
@@ -616,8 +612,7 @@ static i40e_status i40e_delete_lan_hmc_object(struct i40e_hw *hw,
                pd_table =
                        &info->hmc_info->sd_table.sd_entry[sd_idx].u.pd_table;
                if (pd_table->pd_entry[rel_pd_idx].valid) {
-                       ret_code = i40e_remove_pd_bp(hw, info->hmc_info,
-                                                    j, true);
+                       ret_code = i40e_remove_pd_bp(hw, info->hmc_info, j);
                        if (ret_code)
                                goto exit;
                }
@@ -747,6 +742,7 @@ static struct i40e_context_ele i40e_hmc_rxq_ce_info[] = {
        { I40E_HMC_STORE(i40e_hmc_obj_rxq, tphdata_ena),  1,    195 },
        { I40E_HMC_STORE(i40e_hmc_obj_rxq, tphhead_ena),  1,    196 },
        { I40E_HMC_STORE(i40e_hmc_obj_rxq, lrxqthresh),   3,    198 },
+       { I40E_HMC_STORE(i40e_hmc_obj_rxq, prefena),      1,    201 },
        { 0 }
 };
 
index 341de925a2983181a1cd7d7f18d59996fd128158..eb65fe23c4a70077e31e2546208aaeaecf93224d 100644 (file)
@@ -56,6 +56,7 @@ struct i40e_hmc_obj_rxq {
        u8  tphdata_ena;
        u8  tphhead_ena;
        u8  lrxqthresh;
+       u8  prefena;    /* NOTE: normally must be set to 1 at init */
 };
 
 /* Tx queue context data */
index 2e72449f1265e9c82c1f3273f7a40a9cce3f4504..275ca9a1719ed812e7ee15ac8bfda447ee31e217 100644 (file)
@@ -38,8 +38,8 @@ static const char i40e_driver_string[] =
 #define DRV_KERN "-k"
 
 #define DRV_VERSION_MAJOR 0
-#define DRV_VERSION_MINOR 3
-#define DRV_VERSION_BUILD 36
+#define DRV_VERSION_MINOR 4
+#define DRV_VERSION_BUILD 10
 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \
             __stringify(DRV_VERSION_MINOR) "." \
             __stringify(DRV_VERSION_BUILD)    DRV_KERN
@@ -67,12 +67,10 @@ static int i40e_veb_get_bw_info(struct i40e_veb *veb);
  */
 static DEFINE_PCI_DEVICE_TABLE(i40e_pci_tbl) = {
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_XL710), 0},
-       {PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_X710), 0},
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_QEMU), 0},
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_A), 0},
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_B), 0},
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_C), 0},
-       {PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_D), 0},
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_A), 0},
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_B), 0},
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_C), 0},
@@ -356,6 +354,7 @@ static struct rtnl_link_stats64 *i40e_get_netdev_stats_struct(
                                             struct rtnl_link_stats64 *stats)
 {
        struct i40e_netdev_priv *np = netdev_priv(netdev);
+       struct i40e_ring *tx_ring, *rx_ring;
        struct i40e_vsi *vsi = np->vsi;
        struct rtnl_link_stats64 *vsi_stats = i40e_get_vsi_stats_struct(vsi);
        int i;
@@ -368,7 +367,6 @@ static struct rtnl_link_stats64 *i40e_get_netdev_stats_struct(
 
        rcu_read_lock();
        for (i = 0; i < vsi->num_queue_pairs; i++) {
-               struct i40e_ring *tx_ring, *rx_ring;
                u64 bytes, packets;
                unsigned int start;
 
@@ -397,7 +395,7 @@ static struct rtnl_link_stats64 *i40e_get_netdev_stats_struct(
        }
        rcu_read_unlock();
 
-       /* following stats updated by ixgbe_watchdog_task() */
+       /* following stats updated by i40e_watchdog_subtask() */
        stats->multicast        = vsi_stats->multicast;
        stats->tx_errors        = vsi_stats->tx_errors;
        stats->tx_dropped       = vsi_stats->tx_dropped;
@@ -530,6 +528,12 @@ void i40e_update_eth_stats(struct i40e_vsi *vsi)
        i40e_stat_update32(hw, I40E_GLV_RDPC(stat_idx),
                           vsi->stat_offsets_loaded,
                           &oes->rx_discards, &es->rx_discards);
+       i40e_stat_update32(hw, I40E_GLV_RUPP(stat_idx),
+                          vsi->stat_offsets_loaded,
+                          &oes->rx_unknown_protocol, &es->rx_unknown_protocol);
+       i40e_stat_update32(hw, I40E_GLV_TEPC(stat_idx),
+                          vsi->stat_offsets_loaded,
+                          &oes->tx_errors, &es->tx_errors);
 
        i40e_stat_update48(hw, I40E_GLV_GORCH(stat_idx),
                           I40E_GLV_GORCL(stat_idx),
@@ -648,10 +652,10 @@ static void i40e_update_link_xoff_rx(struct i40e_pf *pf)
                return;
 
        /* Clear the __I40E_HANG_CHECK_ARMED bit for all Tx rings */
-       for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+       for (v = 0; v < pf->num_alloc_vsi; v++) {
                struct i40e_vsi *vsi = pf->vsi[v];
 
-               if (!vsi)
+               if (!vsi || !vsi->tx_rings[0])
                        continue;
 
                for (i = 0; i < vsi->num_queue_pairs; i++) {
@@ -702,10 +706,10 @@ static void i40e_update_prio_xoff_rx(struct i40e_pf *pf)
        }
 
        /* Clear the __I40E_HANG_CHECK_ARMED bit for Tx rings */
-       for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+       for (v = 0; v < pf->num_alloc_vsi; v++) {
                struct i40e_vsi *vsi = pf->vsi[v];
 
-               if (!vsi)
+               if (!vsi || !vsi->tx_rings[0])
                        continue;
 
                for (i = 0; i < vsi->num_queue_pairs; i++) {
@@ -720,19 +724,18 @@ static void i40e_update_prio_xoff_rx(struct i40e_pf *pf)
 }
 
 /**
- * i40e_update_stats - Update the board statistics counters.
+ * i40e_update_vsi_stats - Update the vsi statistics counters.
  * @vsi: the VSI to be updated
  *
  * There are a few instances where we store the same stat in a
  * couple of different structs.  This is partly because we have
  * the netdev stats that need to be filled out, which is slightly
  * different from the "eth_stats" defined by the chip and used in
- * VF communications.  We sort it all out here in a central place.
+ * VF communications.  We sort it out here.
  **/
-void i40e_update_stats(struct i40e_vsi *vsi)
+static void i40e_update_vsi_stats(struct i40e_vsi *vsi)
 {
        struct i40e_pf *pf = vsi->back;
-       struct i40e_hw *hw = &pf->hw;
        struct rtnl_link_stats64 *ons;
        struct rtnl_link_stats64 *ns;   /* netdev stats */
        struct i40e_eth_stats *oes;
@@ -741,8 +744,6 @@ void i40e_update_stats(struct i40e_vsi *vsi)
        u32 rx_page, rx_buf;
        u64 rx_p, rx_b;
        u64 tx_p, tx_b;
-       u32 val;
-       int i;
        u16 q;
 
        if (test_bit(__I40E_DOWN, &vsi->state) ||
@@ -804,195 +805,255 @@ void i40e_update_stats(struct i40e_vsi *vsi)
        ns->tx_packets = tx_p;
        ns->tx_bytes = tx_b;
 
-       i40e_update_eth_stats(vsi);
        /* update netdev stats from eth stats */
-       ons->rx_errors = oes->rx_errors;
-       ns->rx_errors = es->rx_errors;
+       i40e_update_eth_stats(vsi);
        ons->tx_errors = oes->tx_errors;
        ns->tx_errors = es->tx_errors;
        ons->multicast = oes->rx_multicast;
        ns->multicast = es->rx_multicast;
+       ons->rx_dropped = oes->rx_discards;
+       ns->rx_dropped = es->rx_discards;
        ons->tx_dropped = oes->tx_discards;
        ns->tx_dropped = es->tx_discards;
 
-       /* Get the port data only if this is the main PF VSI */
+       /* pull in a couple PF stats if this is the main vsi */
        if (vsi == pf->vsi[pf->lan_vsi]) {
-               struct i40e_hw_port_stats *nsd = &pf->stats;
-               struct i40e_hw_port_stats *osd = &pf->stats_offsets;
+               ns->rx_crc_errors = pf->stats.crc_errors;
+               ns->rx_errors = pf->stats.crc_errors + pf->stats.illegal_bytes;
+               ns->rx_length_errors = pf->stats.rx_length_errors;
+       }
+}
 
-               i40e_stat_update48(hw, I40E_GLPRT_GORCH(hw->port),
-                                  I40E_GLPRT_GORCL(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->eth.rx_bytes, &nsd->eth.rx_bytes);
-               i40e_stat_update48(hw, I40E_GLPRT_GOTCH(hw->port),
-                                  I40E_GLPRT_GOTCL(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->eth.tx_bytes, &nsd->eth.tx_bytes);
-               i40e_stat_update32(hw, I40E_GLPRT_RDPC(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->eth.rx_discards,
-                                  &nsd->eth.rx_discards);
-               i40e_stat_update32(hw, I40E_GLPRT_TDPC(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->eth.tx_discards,
-                                  &nsd->eth.tx_discards);
-               i40e_stat_update48(hw, I40E_GLPRT_MPRCH(hw->port),
-                                  I40E_GLPRT_MPRCL(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->eth.rx_multicast,
-                                  &nsd->eth.rx_multicast);
+/**
+ * i40e_update_pf_stats - Update the pf statistics counters.
+ * @pf: the PF to be updated
+ **/
+static void i40e_update_pf_stats(struct i40e_pf *pf)
+{
+       struct i40e_hw_port_stats *osd = &pf->stats_offsets;
+       struct i40e_hw_port_stats *nsd = &pf->stats;
+       struct i40e_hw *hw = &pf->hw;
+       u32 val;
+       int i;
 
-               i40e_stat_update32(hw, I40E_GLPRT_TDOLD(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->tx_dropped_link_down,
-                                  &nsd->tx_dropped_link_down);
+       i40e_stat_update48(hw, I40E_GLPRT_GORCH(hw->port),
+                          I40E_GLPRT_GORCL(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->eth.rx_bytes, &nsd->eth.rx_bytes);
+       i40e_stat_update48(hw, I40E_GLPRT_GOTCH(hw->port),
+                          I40E_GLPRT_GOTCL(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->eth.tx_bytes, &nsd->eth.tx_bytes);
+       i40e_stat_update32(hw, I40E_GLPRT_RDPC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->eth.rx_discards,
+                          &nsd->eth.rx_discards);
+       i40e_stat_update32(hw, I40E_GLPRT_TDPC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->eth.tx_discards,
+                          &nsd->eth.tx_discards);
 
-               i40e_stat_update32(hw, I40E_GLPRT_CRCERRS(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->crc_errors, &nsd->crc_errors);
-               ns->rx_crc_errors = nsd->crc_errors;
+       i40e_stat_update48(hw, I40E_GLPRT_UPRCH(hw->port),
+                          I40E_GLPRT_UPRCL(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->eth.rx_unicast,
+                          &nsd->eth.rx_unicast);
+       i40e_stat_update48(hw, I40E_GLPRT_MPRCH(hw->port),
+                          I40E_GLPRT_MPRCL(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->eth.rx_multicast,
+                          &nsd->eth.rx_multicast);
+       i40e_stat_update48(hw, I40E_GLPRT_BPRCH(hw->port),
+                          I40E_GLPRT_BPRCL(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->eth.rx_broadcast,
+                          &nsd->eth.rx_broadcast);
+       i40e_stat_update48(hw, I40E_GLPRT_UPTCH(hw->port),
+                          I40E_GLPRT_UPTCL(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->eth.tx_unicast,
+                          &nsd->eth.tx_unicast);
+       i40e_stat_update48(hw, I40E_GLPRT_MPTCH(hw->port),
+                          I40E_GLPRT_MPTCL(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->eth.tx_multicast,
+                          &nsd->eth.tx_multicast);
+       i40e_stat_update48(hw, I40E_GLPRT_BPTCH(hw->port),
+                          I40E_GLPRT_BPTCL(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->eth.tx_broadcast,
+                          &nsd->eth.tx_broadcast);
 
-               i40e_stat_update32(hw, I40E_GLPRT_ILLERRC(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->illegal_bytes, &nsd->illegal_bytes);
-               ns->rx_errors = nsd->crc_errors
-                               + nsd->illegal_bytes;
+       i40e_stat_update32(hw, I40E_GLPRT_TDOLD(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->tx_dropped_link_down,
+                          &nsd->tx_dropped_link_down);
 
-               i40e_stat_update32(hw, I40E_GLPRT_MLFC(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->mac_local_faults,
-                                  &nsd->mac_local_faults);
-               i40e_stat_update32(hw, I40E_GLPRT_MRFC(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->mac_remote_faults,
-                                  &nsd->mac_remote_faults);
+       i40e_stat_update32(hw, I40E_GLPRT_CRCERRS(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->crc_errors, &nsd->crc_errors);
 
-               i40e_stat_update32(hw, I40E_GLPRT_RLEC(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->rx_length_errors,
-                                  &nsd->rx_length_errors);
-               ns->rx_length_errors = nsd->rx_length_errors;
+       i40e_stat_update32(hw, I40E_GLPRT_ILLERRC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->illegal_bytes, &nsd->illegal_bytes);
 
-               i40e_stat_update32(hw, I40E_GLPRT_LXONRXC(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->link_xon_rx, &nsd->link_xon_rx);
-               i40e_stat_update32(hw, I40E_GLPRT_LXONTXC(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->link_xon_tx, &nsd->link_xon_tx);
-               i40e_update_prio_xoff_rx(pf);  /* handles I40E_GLPRT_LXOFFRXC */
-               i40e_stat_update32(hw, I40E_GLPRT_LXOFFTXC(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->link_xoff_tx, &nsd->link_xoff_tx);
-
-               for (i = 0; i < 8; i++) {
-                       i40e_stat_update32(hw, I40E_GLPRT_PXONRXC(hw->port, i),
-                                          pf->stat_offsets_loaded,
-                                          &osd->priority_xon_rx[i],
-                                          &nsd->priority_xon_rx[i]);
-                       i40e_stat_update32(hw, I40E_GLPRT_PXONTXC(hw->port, i),
-                                          pf->stat_offsets_loaded,
-                                          &osd->priority_xon_tx[i],
-                                          &nsd->priority_xon_tx[i]);
-                       i40e_stat_update32(hw, I40E_GLPRT_PXOFFTXC(hw->port, i),
-                                          pf->stat_offsets_loaded,
-                                          &osd->priority_xoff_tx[i],
-                                          &nsd->priority_xoff_tx[i]);
-                       i40e_stat_update32(hw,
-                                          I40E_GLPRT_RXON2OFFCNT(hw->port, i),
-                                          pf->stat_offsets_loaded,
-                                          &osd->priority_xon_2_xoff[i],
-                                          &nsd->priority_xon_2_xoff[i]);
-               }
+       i40e_stat_update32(hw, I40E_GLPRT_MLFC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->mac_local_faults,
+                          &nsd->mac_local_faults);
+       i40e_stat_update32(hw, I40E_GLPRT_MRFC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->mac_remote_faults,
+                          &nsd->mac_remote_faults);
 
-               i40e_stat_update48(hw, I40E_GLPRT_PRC64H(hw->port),
-                                  I40E_GLPRT_PRC64L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->rx_size_64, &nsd->rx_size_64);
-               i40e_stat_update48(hw, I40E_GLPRT_PRC127H(hw->port),
-                                  I40E_GLPRT_PRC127L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->rx_size_127, &nsd->rx_size_127);
-               i40e_stat_update48(hw, I40E_GLPRT_PRC255H(hw->port),
-                                  I40E_GLPRT_PRC255L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->rx_size_255, &nsd->rx_size_255);
-               i40e_stat_update48(hw, I40E_GLPRT_PRC511H(hw->port),
-                                  I40E_GLPRT_PRC511L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->rx_size_511, &nsd->rx_size_511);
-               i40e_stat_update48(hw, I40E_GLPRT_PRC1023H(hw->port),
-                                  I40E_GLPRT_PRC1023L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->rx_size_1023, &nsd->rx_size_1023);
-               i40e_stat_update48(hw, I40E_GLPRT_PRC1522H(hw->port),
-                                  I40E_GLPRT_PRC1522L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->rx_size_1522, &nsd->rx_size_1522);
-               i40e_stat_update48(hw, I40E_GLPRT_PRC9522H(hw->port),
-                                  I40E_GLPRT_PRC9522L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->rx_size_big, &nsd->rx_size_big);
+       i40e_stat_update32(hw, I40E_GLPRT_RLEC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_length_errors,
+                          &nsd->rx_length_errors);
 
-               i40e_stat_update48(hw, I40E_GLPRT_PTC64H(hw->port),
-                                  I40E_GLPRT_PTC64L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->tx_size_64, &nsd->tx_size_64);
-               i40e_stat_update48(hw, I40E_GLPRT_PTC127H(hw->port),
-                                  I40E_GLPRT_PTC127L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->tx_size_127, &nsd->tx_size_127);
-               i40e_stat_update48(hw, I40E_GLPRT_PTC255H(hw->port),
-                                  I40E_GLPRT_PTC255L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->tx_size_255, &nsd->tx_size_255);
-               i40e_stat_update48(hw, I40E_GLPRT_PTC511H(hw->port),
-                                  I40E_GLPRT_PTC511L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->tx_size_511, &nsd->tx_size_511);
-               i40e_stat_update48(hw, I40E_GLPRT_PTC1023H(hw->port),
-                                  I40E_GLPRT_PTC1023L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->tx_size_1023, &nsd->tx_size_1023);
-               i40e_stat_update48(hw, I40E_GLPRT_PTC1522H(hw->port),
-                                  I40E_GLPRT_PTC1522L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->tx_size_1522, &nsd->tx_size_1522);
-               i40e_stat_update48(hw, I40E_GLPRT_PTC9522H(hw->port),
-                                  I40E_GLPRT_PTC9522L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->tx_size_big, &nsd->tx_size_big);
+       i40e_stat_update32(hw, I40E_GLPRT_LXONRXC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->link_xon_rx, &nsd->link_xon_rx);
+       i40e_stat_update32(hw, I40E_GLPRT_LXONTXC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->link_xon_tx, &nsd->link_xon_tx);
+       i40e_update_prio_xoff_rx(pf);  /* handles I40E_GLPRT_LXOFFRXC */
+       i40e_stat_update32(hw, I40E_GLPRT_LXOFFTXC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->link_xoff_tx, &nsd->link_xoff_tx);
 
-               i40e_stat_update32(hw, I40E_GLPRT_RUC(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->rx_undersize, &nsd->rx_undersize);
-               i40e_stat_update32(hw, I40E_GLPRT_RFC(hw->port),
+       for (i = 0; i < 8; i++) {
+               i40e_stat_update32(hw, I40E_GLPRT_PXONRXC(hw->port, i),
                                   pf->stat_offsets_loaded,
-                                  &osd->rx_fragments, &nsd->rx_fragments);
-               i40e_stat_update32(hw, I40E_GLPRT_ROC(hw->port),
+                                  &osd->priority_xon_rx[i],
+                                  &nsd->priority_xon_rx[i]);
+               i40e_stat_update32(hw, I40E_GLPRT_PXONTXC(hw->port, i),
                                   pf->stat_offsets_loaded,
-                                  &osd->rx_oversize, &nsd->rx_oversize);
-               i40e_stat_update32(hw, I40E_GLPRT_RJC(hw->port),
+                                  &osd->priority_xon_tx[i],
+                                  &nsd->priority_xon_tx[i]);
+               i40e_stat_update32(hw, I40E_GLPRT_PXOFFTXC(hw->port, i),
                                   pf->stat_offsets_loaded,
-                                  &osd->rx_jabber, &nsd->rx_jabber);
-
-               val = rd32(hw, I40E_PRTPM_EEE_STAT);
-               nsd->tx_lpi_status =
-                              (val & I40E_PRTPM_EEE_STAT_TX_LPI_STATUS_MASK) >>
-                               I40E_PRTPM_EEE_STAT_TX_LPI_STATUS_SHIFT;
-               nsd->rx_lpi_status =
-                              (val & I40E_PRTPM_EEE_STAT_RX_LPI_STATUS_MASK) >>
-                               I40E_PRTPM_EEE_STAT_RX_LPI_STATUS_SHIFT;
-               i40e_stat_update32(hw, I40E_PRTPM_TLPIC,
+                                  &osd->priority_xoff_tx[i],
+                                  &nsd->priority_xoff_tx[i]);
+               i40e_stat_update32(hw,
+                                  I40E_GLPRT_RXON2OFFCNT(hw->port, i),
                                   pf->stat_offsets_loaded,
-                                  &osd->tx_lpi_count, &nsd->tx_lpi_count);
-               i40e_stat_update32(hw, I40E_PRTPM_RLPIC,
-                                  pf->stat_offsets_loaded,
-                                  &osd->rx_lpi_count, &nsd->rx_lpi_count);
+                                  &osd->priority_xon_2_xoff[i],
+                                  &nsd->priority_xon_2_xoff[i]);
        }
 
+       i40e_stat_update48(hw, I40E_GLPRT_PRC64H(hw->port),
+                          I40E_GLPRT_PRC64L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_size_64, &nsd->rx_size_64);
+       i40e_stat_update48(hw, I40E_GLPRT_PRC127H(hw->port),
+                          I40E_GLPRT_PRC127L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_size_127, &nsd->rx_size_127);
+       i40e_stat_update48(hw, I40E_GLPRT_PRC255H(hw->port),
+                          I40E_GLPRT_PRC255L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_size_255, &nsd->rx_size_255);
+       i40e_stat_update48(hw, I40E_GLPRT_PRC511H(hw->port),
+                          I40E_GLPRT_PRC511L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_size_511, &nsd->rx_size_511);
+       i40e_stat_update48(hw, I40E_GLPRT_PRC1023H(hw->port),
+                          I40E_GLPRT_PRC1023L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_size_1023, &nsd->rx_size_1023);
+       i40e_stat_update48(hw, I40E_GLPRT_PRC1522H(hw->port),
+                          I40E_GLPRT_PRC1522L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_size_1522, &nsd->rx_size_1522);
+       i40e_stat_update48(hw, I40E_GLPRT_PRC9522H(hw->port),
+                          I40E_GLPRT_PRC9522L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_size_big, &nsd->rx_size_big);
+
+       i40e_stat_update48(hw, I40E_GLPRT_PTC64H(hw->port),
+                          I40E_GLPRT_PTC64L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->tx_size_64, &nsd->tx_size_64);
+       i40e_stat_update48(hw, I40E_GLPRT_PTC127H(hw->port),
+                          I40E_GLPRT_PTC127L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->tx_size_127, &nsd->tx_size_127);
+       i40e_stat_update48(hw, I40E_GLPRT_PTC255H(hw->port),
+                          I40E_GLPRT_PTC255L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->tx_size_255, &nsd->tx_size_255);
+       i40e_stat_update48(hw, I40E_GLPRT_PTC511H(hw->port),
+                          I40E_GLPRT_PTC511L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->tx_size_511, &nsd->tx_size_511);
+       i40e_stat_update48(hw, I40E_GLPRT_PTC1023H(hw->port),
+                          I40E_GLPRT_PTC1023L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->tx_size_1023, &nsd->tx_size_1023);
+       i40e_stat_update48(hw, I40E_GLPRT_PTC1522H(hw->port),
+                          I40E_GLPRT_PTC1522L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->tx_size_1522, &nsd->tx_size_1522);
+       i40e_stat_update48(hw, I40E_GLPRT_PTC9522H(hw->port),
+                          I40E_GLPRT_PTC9522L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->tx_size_big, &nsd->tx_size_big);
+
+       i40e_stat_update32(hw, I40E_GLPRT_RUC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_undersize, &nsd->rx_undersize);
+       i40e_stat_update32(hw, I40E_GLPRT_RFC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_fragments, &nsd->rx_fragments);
+       i40e_stat_update32(hw, I40E_GLPRT_ROC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_oversize, &nsd->rx_oversize);
+       i40e_stat_update32(hw, I40E_GLPRT_RJC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_jabber, &nsd->rx_jabber);
+
+       /* FDIR stats */
+       i40e_stat_update32(hw, I40E_GLQF_PCNT(pf->fd_atr_cnt_idx),
+                          pf->stat_offsets_loaded,
+                          &osd->fd_atr_match, &nsd->fd_atr_match);
+       i40e_stat_update32(hw, I40E_GLQF_PCNT(pf->fd_sb_cnt_idx),
+                          pf->stat_offsets_loaded,
+                          &osd->fd_sb_match, &nsd->fd_sb_match);
+
+       val = rd32(hw, I40E_PRTPM_EEE_STAT);
+       nsd->tx_lpi_status =
+                      (val & I40E_PRTPM_EEE_STAT_TX_LPI_STATUS_MASK) >>
+                       I40E_PRTPM_EEE_STAT_TX_LPI_STATUS_SHIFT;
+       nsd->rx_lpi_status =
+                      (val & I40E_PRTPM_EEE_STAT_RX_LPI_STATUS_MASK) >>
+                       I40E_PRTPM_EEE_STAT_RX_LPI_STATUS_SHIFT;
+       i40e_stat_update32(hw, I40E_PRTPM_TLPIC,
+                          pf->stat_offsets_loaded,
+                          &osd->tx_lpi_count, &nsd->tx_lpi_count);
+       i40e_stat_update32(hw, I40E_PRTPM_RLPIC,
+                          pf->stat_offsets_loaded,
+                          &osd->rx_lpi_count, &nsd->rx_lpi_count);
+
        pf->stat_offsets_loaded = true;
 }
 
+/**
+ * i40e_update_stats - Update the various statistics counters.
+ * @vsi: the VSI to be updated
+ *
+ * Update the various stats for this VSI and its related entities.
+ **/
+void i40e_update_stats(struct i40e_vsi *vsi)
+{
+       struct i40e_pf *pf = vsi->back;
+
+       if (vsi == pf->vsi[pf->lan_vsi])
+               i40e_update_pf_stats(pf);
+
+       i40e_update_vsi_stats(vsi);
+}
+
 /**
  * i40e_find_filter - Search VSI filter list for specific mac/vlan filter
  * @vsi: the VSI to be searched
@@ -1100,6 +1161,30 @@ struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, u8 *macaddr,
                                        struct i40e_mac_filter, list);
 }
 
+/**
+ * i40e_rm_default_mac_filter - Remove the default MAC filter set by NVM
+ * @vsi: the PF Main VSI - inappropriate for any other VSI
+ * @macaddr: the MAC address
+ **/
+static void i40e_rm_default_mac_filter(struct i40e_vsi *vsi, u8 *macaddr)
+{
+       struct i40e_aqc_remove_macvlan_element_data element;
+       struct i40e_pf *pf = vsi->back;
+       i40e_status aq_ret;
+
+       /* Only appropriate for the PF main VSI */
+       if (vsi->type != I40E_VSI_MAIN)
+               return;
+
+       ether_addr_copy(element.mac_addr, macaddr);
+       element.vlan_tag = 0;
+       element.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH |
+                       I40E_AQC_MACVLAN_DEL_IGNORE_VLAN;
+       aq_ret = i40e_aq_remove_macvlan(&pf->hw, vsi->seid, &element, 1, NULL);
+       if (aq_ret)
+               dev_err(&pf->pdev->dev, "Could not remove default MAC-VLAN\n");
+}
+
 /**
  * i40e_add_filter - Add a mac/vlan filter to the VSI
  * @vsi: the VSI to be searched
@@ -1125,7 +1210,7 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi,
                if (!f)
                        goto add_filter_out;
 
-               memcpy(f->macaddr, macaddr, ETH_ALEN);
+               ether_addr_copy(f->macaddr, macaddr);
                f->vlan = vlan;
                f->changed = true;
 
@@ -1249,7 +1334,7 @@ static int i40e_set_mac(struct net_device *netdev, void *p)
                        return -EADDRNOTAVAIL;
                }
 
-               memcpy(vsi->back->hw.mac.addr, addr->sa_data, netdev->addr_len);
+               ether_addr_copy(vsi->back->hw.mac.addr, addr->sa_data);
        }
 
        /* In order to be sure to not drop any packets, add the new address
@@ -1263,7 +1348,7 @@ static int i40e_set_mac(struct net_device *netdev, void *p)
        i40e_del_filter(vsi, netdev->dev_addr, I40E_VLAN_ANY, false, false);
        i40e_sync_vsi_filters(vsi);
 
-       memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+       ether_addr_copy(netdev->dev_addr, addr->sa_data);
 
        return 0;
 }
@@ -1313,7 +1398,7 @@ static void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi,
        vsi->tc_config.numtc = numtc;
        vsi->tc_config.enabled_tc = enabled_tc ? enabled_tc : 1;
        /* Number of queues per enabled TC */
-       num_tc_qps = rounddown_pow_of_two(vsi->alloc_queue_pairs/numtc);
+       num_tc_qps = vsi->alloc_queue_pairs/numtc;
        num_tc_qps = min_t(int, num_tc_qps, I40E_MAX_QUEUES_PER_TC);
 
        /* Setup queue offset/count for all TCs for given VSI */
@@ -1520,8 +1605,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
                        cmd_flags = 0;
 
                        /* add to delete list */
-                       memcpy(del_list[num_del].mac_addr,
-                              f->macaddr, ETH_ALEN);
+                       ether_addr_copy(del_list[num_del].mac_addr, f->macaddr);
                        del_list[num_del].vlan_tag =
                                cpu_to_le16((u16)(f->vlan ==
                                            I40E_VLAN_ANY ? 0 : f->vlan));
@@ -1542,7 +1626,9 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
                                num_del = 0;
                                memset(del_list, 0, sizeof(*del_list));
 
-                               if (aq_ret)
+                               if (aq_ret &&
+                                   pf->hw.aq.asq_last_status !=
+                                                             I40E_AQ_RC_ENOENT)
                                        dev_info(&pf->pdev->dev,
                                                 "ignoring delete macvlan error, err %d, aq_err %d while flushing a full buffer\n",
                                                 aq_ret,
@@ -1554,7 +1640,8 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
                                                     del_list, num_del, NULL);
                        num_del = 0;
 
-                       if (aq_ret)
+                       if (aq_ret &&
+                           pf->hw.aq.asq_last_status != I40E_AQ_RC_ENOENT)
                                dev_info(&pf->pdev->dev,
                                         "ignoring delete macvlan error, err %d, aq_err %d\n",
                                         aq_ret, pf->hw.aq.asq_last_status);
@@ -1583,8 +1670,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
                        cmd_flags = 0;
 
                        /* add to add array */
-                       memcpy(add_list[num_add].mac_addr,
-                              f->macaddr, ETH_ALEN);
+                       ether_addr_copy(add_list[num_add].mac_addr, f->macaddr);
                        add_list[num_add].vlan_tag =
                                cpu_to_le16(
                                 (u16)(f->vlan == I40E_VLAN_ANY ? 0 : f->vlan));
@@ -1681,7 +1767,7 @@ static void i40e_sync_filters_subtask(struct i40e_pf *pf)
                return;
        pf->flags &= ~I40E_FLAG_FILTER_SYNC;
 
-       for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+       for (v = 0; v < pf->num_alloc_vsi; v++) {
                if (pf->vsi[v] &&
                    (pf->vsi[v]->flags & I40E_VSI_FLAG_FILTER_CHANGED))
                        i40e_sync_vsi_filters(pf->vsi[v]);
@@ -1698,7 +1784,7 @@ static void i40e_sync_filters_subtask(struct i40e_pf *pf)
 static int i40e_change_mtu(struct net_device *netdev, int new_mtu)
 {
        struct i40e_netdev_priv *np = netdev_priv(netdev);
-       int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN;
+       int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
        struct i40e_vsi *vsi = np->vsi;
 
        /* MTU < 68 is an error and causes problems on some kernels */
@@ -2312,6 +2398,8 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring)
        rx_ctx.crcstrip = 1;
        rx_ctx.l2tsel = 1;
        rx_ctx.showiv = 1;
+       /* set the prefena field to 1 because the manual says to */
+       rx_ctx.prefena = 1;
 
        /* clear the context in the HMC */
        err = i40e_clear_lan_rx_queue_context(hw, pf_q);
@@ -2413,6 +2501,7 @@ static int i40e_vsi_configure_rx(struct i40e_vsi *vsi)
  **/
 static void i40e_vsi_config_dcb_rings(struct i40e_vsi *vsi)
 {
+       struct i40e_ring *tx_ring, *rx_ring;
        u16 qoffset, qcount;
        int i, n;
 
@@ -2426,8 +2515,8 @@ static void i40e_vsi_config_dcb_rings(struct i40e_vsi *vsi)
                qoffset = vsi->tc_config.tc_info[n].qoffset;
                qcount = vsi->tc_config.tc_info[n].qcount;
                for (i = qoffset; i < (qoffset + qcount); i++) {
-                       struct i40e_ring *rx_ring = vsi->rx_rings[i];
-                       struct i40e_ring *tx_ring = vsi->tx_rings[i];
+                       rx_ring = vsi->rx_rings[i];
+                       tx_ring = vsi->tx_rings[i];
                        rx_ring->dcb_tc = n;
                        tx_ring->dcb_tc = n;
                }
@@ -2565,7 +2654,6 @@ static void i40e_enable_misc_int_causes(struct i40e_hw *hw)
              I40E_PFINT_ICR0_ENA_PCI_EXCEPTION_MASK |
              I40E_PFINT_ICR0_ENA_GPIO_MASK          |
              I40E_PFINT_ICR0_ENA_TIMESYNC_MASK      |
-             I40E_PFINT_ICR0_ENA_STORM_DETECT_MASK  |
              I40E_PFINT_ICR0_ENA_HMC_ERR_MASK       |
              I40E_PFINT_ICR0_ENA_VFLR_MASK          |
              I40E_PFINT_ICR0_ENA_ADMINQ_MASK;
@@ -2733,6 +2821,7 @@ static int i40e_vsi_request_irq_msix(struct i40e_vsi *vsi, char *basename)
                                      &q_vector->affinity_mask);
        }
 
+       vsi->irqs_ready = true;
        return 0;
 
 free_queue_irqs:
@@ -3152,6 +3241,12 @@ static int i40e_vsi_control_tx(struct i40e_vsi *vsi, bool enable)
 
        pf_q = vsi->base_queue;
        for (i = 0; i < vsi->num_queue_pairs; i++, pf_q++) {
+
+               /* warn the TX unit of coming changes */
+               i40e_pre_tx_queue_cfg(&pf->hw, pf_q, enable);
+               if (!enable)
+                       udelay(10);
+
                for (j = 0; j < 50; j++) {
                        tx_reg = rd32(hw, I40E_QTX_ENA(pf_q));
                        if (((tx_reg >> I40E_QTX_ENA_QENA_REQ_SHIFT) & 1) ==
@@ -3160,9 +3255,7 @@ static int i40e_vsi_control_tx(struct i40e_vsi *vsi, bool enable)
                        usleep_range(1000, 2000);
                }
                /* Skip if the queue is already in the requested state */
-               if (enable && (tx_reg & I40E_QTX_ENA_QENA_STAT_MASK))
-                       continue;
-               if (!enable && !(tx_reg & I40E_QTX_ENA_QENA_STAT_MASK))
+               if (enable == !!(tx_reg & I40E_QTX_ENA_QENA_STAT_MASK))
                        continue;
 
                /* turn on/off the queue */
@@ -3178,13 +3271,8 @@ static int i40e_vsi_control_tx(struct i40e_vsi *vsi, bool enable)
                /* wait for the change to finish */
                for (j = 0; j < 10; j++) {
                        tx_reg = rd32(hw, I40E_QTX_ENA(pf_q));
-                       if (enable) {
-                               if ((tx_reg & I40E_QTX_ENA_QENA_STAT_MASK))
-                                       break;
-                       } else {
-                               if (!(tx_reg & I40E_QTX_ENA_QENA_STAT_MASK))
-                                       break;
-                       }
+                       if (enable == !!(tx_reg & I40E_QTX_ENA_QENA_STAT_MASK))
+                               break;
 
                        udelay(10);
                }
@@ -3223,15 +3311,9 @@ static int i40e_vsi_control_rx(struct i40e_vsi *vsi, bool enable)
                        usleep_range(1000, 2000);
                }
 
-               if (enable) {
-                       /* is STAT set ? */
-                       if ((rx_reg & I40E_QRX_ENA_QENA_STAT_MASK))
-                               continue;
-               } else {
-                       /* is !STAT set ? */
-                       if (!(rx_reg & I40E_QRX_ENA_QENA_STAT_MASK))
-                               continue;
-               }
+               /* Skip if the queue is already in the requested state */
+               if (enable == !!(rx_reg & I40E_QRX_ENA_QENA_STAT_MASK))
+                       continue;
 
                /* turn on/off the queue */
                if (enable)
@@ -3244,13 +3326,8 @@ static int i40e_vsi_control_rx(struct i40e_vsi *vsi, bool enable)
                for (j = 0; j < 10; j++) {
                        rx_reg = rd32(hw, I40E_QRX_ENA(pf_q));
 
-                       if (enable) {
-                               if ((rx_reg & I40E_QRX_ENA_QENA_STAT_MASK))
-                                       break;
-                       } else {
-                               if (!(rx_reg & I40E_QRX_ENA_QENA_STAT_MASK))
-                                       break;
-                       }
+                       if (enable == !!(rx_reg & I40E_QRX_ENA_QENA_STAT_MASK))
+                               break;
 
                        udelay(10);
                }
@@ -3304,6 +3381,10 @@ static void i40e_vsi_free_irq(struct i40e_vsi *vsi)
                if (!vsi->q_vectors)
                        return;
 
+               if (!vsi->irqs_ready)
+                       return;
+
+               vsi->irqs_ready = false;
                for (i = 0; i < vsi->num_q_vectors; i++) {
                        u16 vector = i + base;
 
@@ -3476,7 +3557,7 @@ static void i40e_clear_interrupt_scheme(struct i40e_pf *pf)
        int i;
 
        i40e_put_lump(pf->irq_pile, 0, I40E_PILE_VALID_BIT-1);
-       for (i = 0; i < pf->hw.func_caps.num_vsis; i++)
+       for (i = 0; i < pf->num_alloc_vsi; i++)
                if (pf->vsi[i])
                        i40e_vsi_free_q_vectors(pf->vsi[i]);
        i40e_reset_interrupt_capability(pf);
@@ -3512,6 +3593,19 @@ static void i40e_napi_disable_all(struct i40e_vsi *vsi)
                napi_disable(&vsi->q_vectors[q_idx]->napi);
 }
 
+/**
+ * i40e_vsi_close - Shut down a VSI
+ * @vsi: the vsi to be quelled
+ **/
+static void i40e_vsi_close(struct i40e_vsi *vsi)
+{
+       if (!test_and_set_bit(__I40E_DOWN, &vsi->state))
+               i40e_down(vsi);
+       i40e_vsi_free_irq(vsi);
+       i40e_vsi_free_tx_resources(vsi);
+       i40e_vsi_free_rx_resources(vsi);
+}
+
 /**
  * i40e_quiesce_vsi - Pause a given VSI
  * @vsi: the VSI being paused
@@ -3525,8 +3619,7 @@ static void i40e_quiesce_vsi(struct i40e_vsi *vsi)
        if (vsi->netdev && netif_running(vsi->netdev)) {
                vsi->netdev->netdev_ops->ndo_stop(vsi->netdev);
        } else {
-               set_bit(__I40E_DOWN, &vsi->state);
-               i40e_down(vsi);
+               i40e_vsi_close(vsi);
        }
 }
 
@@ -3543,7 +3636,7 @@ static void i40e_unquiesce_vsi(struct i40e_vsi *vsi)
        if (vsi->netdev && netif_running(vsi->netdev))
                vsi->netdev->netdev_ops->ndo_open(vsi->netdev);
        else
-               i40e_up(vsi);   /* this clears the DOWN bit */
+               i40e_vsi_open(vsi);   /* this clears the DOWN bit */
 }
 
 /**
@@ -3554,7 +3647,7 @@ static void i40e_pf_quiesce_all_vsi(struct i40e_pf *pf)
 {
        int v;
 
-       for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+       for (v = 0; v < pf->num_alloc_vsi; v++) {
                if (pf->vsi[v])
                        i40e_quiesce_vsi(pf->vsi[v]);
        }
@@ -3568,7 +3661,7 @@ static void i40e_pf_unquiesce_all_vsi(struct i40e_pf *pf)
 {
        int v;
 
-       for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+       for (v = 0; v < pf->num_alloc_vsi; v++) {
                if (pf->vsi[v])
                        i40e_unquiesce_vsi(pf->vsi[v]);
        }
@@ -4009,7 +4102,7 @@ static void i40e_dcb_reconfigure(struct i40e_pf *pf)
        }
 
        /* Update each VSI */
-       for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+       for (v = 0; v < pf->num_alloc_vsi; v++) {
                if (!pf->vsi[v])
                        continue;
 
@@ -4028,6 +4121,8 @@ static void i40e_dcb_reconfigure(struct i40e_pf *pf)
                                 pf->vsi[v]->seid);
                        /* Will try to configure as many components */
                } else {
+                       /* Re-configure VSI vectors based on updated TC map */
+                       i40e_vsi_map_rings_to_vectors(pf->vsi[v]);
                        if (pf->vsi[v]->netdev)
                                i40e_dcbnl_set_all(pf->vsi[v]);
                }
@@ -4065,14 +4160,69 @@ static int i40e_init_pf_dcb(struct i40e_pf *pf)
                        /* When status is not DISABLED then DCBX in FW */
                        pf->dcbx_cap = DCB_CAP_DCBX_LLD_MANAGED |
                                       DCB_CAP_DCBX_VER_IEEE;
-                       pf->flags |= I40E_FLAG_DCB_ENABLED;
+
+                       pf->flags |= I40E_FLAG_DCB_CAPABLE;
+                       /* Enable DCB tagging only when more than one TC */
+                       if (i40e_dcb_get_num_tc(&hw->local_dcbx_config) > 1)
+                               pf->flags |= I40E_FLAG_DCB_ENABLED;
                }
+       } else {
+               dev_info(&pf->pdev->dev, "AQ Querying DCB configuration failed: %d\n",
+                        pf->hw.aq.asq_last_status);
        }
 
 out:
        return err;
 }
 #endif /* CONFIG_I40E_DCB */
+#define SPEED_SIZE 14
+#define FC_SIZE 8
+/**
+ * i40e_print_link_message - print link up or down
+ * @vsi: the VSI for which link needs a message
+ */
+static void i40e_print_link_message(struct i40e_vsi *vsi, bool isup)
+{
+       char speed[SPEED_SIZE] = "Unknown";
+       char fc[FC_SIZE] = "RX/TX";
+
+       if (!isup) {
+               netdev_info(vsi->netdev, "NIC Link is Down\n");
+               return;
+       }
+
+       switch (vsi->back->hw.phy.link_info.link_speed) {
+       case I40E_LINK_SPEED_40GB:
+               strncpy(speed, "40 Gbps", SPEED_SIZE);
+               break;
+       case I40E_LINK_SPEED_10GB:
+               strncpy(speed, "10 Gbps", SPEED_SIZE);
+               break;
+       case I40E_LINK_SPEED_1GB:
+               strncpy(speed, "1000 Mbps", SPEED_SIZE);
+               break;
+       default:
+               break;
+       }
+
+       switch (vsi->back->hw.fc.current_mode) {
+       case I40E_FC_FULL:
+               strncpy(fc, "RX/TX", FC_SIZE);
+               break;
+       case I40E_FC_TX_PAUSE:
+               strncpy(fc, "TX", FC_SIZE);
+               break;
+       case I40E_FC_RX_PAUSE:
+               strncpy(fc, "RX", FC_SIZE);
+               break;
+       default:
+               strncpy(fc, "None", FC_SIZE);
+               break;
+       }
+
+       netdev_info(vsi->netdev, "NIC Link is Up %s Full Duplex, Flow Control: %s\n",
+                   speed, fc);
+}
 
 /**
  * i40e_up_complete - Finish the last steps of bringing up a connection
@@ -4099,11 +4249,11 @@ static int i40e_up_complete(struct i40e_vsi *vsi)
 
        if ((pf->hw.phy.link_info.link_info & I40E_AQ_LINK_UP) &&
            (vsi->netdev)) {
-               netdev_info(vsi->netdev, "NIC Link is Up\n");
+               i40e_print_link_message(vsi, true);
                netif_tx_start_all_queues(vsi->netdev);
                netif_carrier_on(vsi->netdev);
        } else if (vsi->netdev) {
-               netdev_info(vsi->netdev, "NIC Link is Down\n");
+               i40e_print_link_message(vsi, false);
        }
 
        /* replay FDIR SB filters */
@@ -4309,24 +4459,32 @@ int i40e_vsi_open(struct i40e_vsi *vsi)
        if (err)
                goto err_setup_rx;
 
-       if (!vsi->netdev) {
-               err = EINVAL;
-               goto err_setup_rx;
-       }
-       snprintf(int_name, sizeof(int_name) - 1, "%s-%s",
-                dev_driver_string(&pf->pdev->dev), vsi->netdev->name);
-       err = i40e_vsi_request_irq(vsi, int_name);
-       if (err)
-               goto err_setup_rx;
+       if (vsi->netdev) {
+               snprintf(int_name, sizeof(int_name) - 1, "%s-%s",
+                        dev_driver_string(&pf->pdev->dev), vsi->netdev->name);
+               err = i40e_vsi_request_irq(vsi, int_name);
+               if (err)
+                       goto err_setup_rx;
 
-       /* Notify the stack of the actual queue counts. */
-       err = netif_set_real_num_tx_queues(vsi->netdev, vsi->num_queue_pairs);
-       if (err)
-               goto err_set_queues;
+               /* Notify the stack of the actual queue counts. */
+               err = netif_set_real_num_tx_queues(vsi->netdev,
+                                                  vsi->num_queue_pairs);
+               if (err)
+                       goto err_set_queues;
 
-       err = netif_set_real_num_rx_queues(vsi->netdev, vsi->num_queue_pairs);
-       if (err)
-               goto err_set_queues;
+               err = netif_set_real_num_rx_queues(vsi->netdev,
+                                                  vsi->num_queue_pairs);
+               if (err)
+                       goto err_set_queues;
+
+       } else if (vsi->type == I40E_VSI_FDIR) {
+               snprintf(int_name, sizeof(int_name) - 1, "%s-fdir",
+                        dev_driver_string(&pf->pdev->dev));
+               err = i40e_vsi_request_irq(vsi, int_name);
+       } else {
+               err = -EINVAL;
+               goto err_setup_rx;
+       }
 
        err = i40e_up_complete(vsi);
        if (err)
@@ -4383,14 +4541,7 @@ static int i40e_close(struct net_device *netdev)
        struct i40e_netdev_priv *np = netdev_priv(netdev);
        struct i40e_vsi *vsi = np->vsi;
 
-       if (test_and_set_bit(__I40E_DOWN, &vsi->state))
-               return 0;
-
-       i40e_down(vsi);
-       i40e_vsi_free_irq(vsi);
-
-       i40e_vsi_free_tx_resources(vsi);
-       i40e_vsi_free_rx_resources(vsi);
+       i40e_vsi_close(vsi);
 
        return 0;
 }
@@ -4410,6 +4561,9 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags)
 
        WARN_ON(in_interrupt());
 
+       if (i40e_check_asq_alive(&pf->hw))
+               i40e_vc_notify_reset(pf);
+
        /* do the biggest reset indicated */
        if (reset_flags & (1 << __I40E_GLOBAL_RESET_REQUESTED)) {
 
@@ -4475,7 +4629,7 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags)
                /* Find the VSI(s) that requested a re-init */
                dev_info(&pf->pdev->dev,
                         "VSI reinit requested\n");
-               for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+               for (v = 0; v < pf->num_alloc_vsi; v++) {
                        struct i40e_vsi *vsi = pf->vsi[v];
                        if (vsi != NULL &&
                            test_bit(__I40E_REINIT_REQUESTED, &vsi->state)) {
@@ -4565,6 +4719,10 @@ static int i40e_handle_lldp_event(struct i40e_pf *pf,
        int ret = 0;
        u8 type;
 
+       /* Not DCB capable or capability disabled */
+       if (!(pf->flags & I40E_FLAG_DCB_CAPABLE))
+               return ret;
+
        /* Ignore if event is not for Nearest Bridge */
        type = ((mib->type >> I40E_AQ_LLDP_BRIDGE_TYPE_SHIFT)
                & I40E_AQ_LLDP_BRIDGE_TYPE_MASK);
@@ -4606,6 +4764,12 @@ static int i40e_handle_lldp_event(struct i40e_pf *pf,
        if (!need_reconfig)
                goto exit;
 
+       /* Enable DCB tagging only when more than one TC */
+       if (i40e_dcb_get_num_tc(dcbx_cfg) > 1)
+               pf->flags |= I40E_FLAG_DCB_ENABLED;
+       else
+               pf->flags &= ~I40E_FLAG_DCB_ENABLED;
+
        /* Reconfiguration needed quiesce all VSIs */
        i40e_pf_quiesce_all_vsi(pf);
 
@@ -4709,8 +4873,7 @@ void i40e_fdir_check_and_reenable(struct i40e_pf *pf)
            (pf->flags & I40E_FLAG_FD_SB_ENABLED))
                return;
        fcnt_prog = i40e_get_current_fd_count(pf);
-       fcnt_avail = pf->hw.fdir_shared_filter_count +
-                                              pf->fdir_pf_filter_count;
+       fcnt_avail = i40e_get_fd_cnt_all(pf);
        if (fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM)) {
                if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) &&
                    (pf->auto_disable_flags & I40E_FLAG_FD_SB_ENABLED)) {
@@ -4803,7 +4966,7 @@ static void i40e_veb_link_event(struct i40e_veb *veb, bool link_up)
                        i40e_veb_link_event(pf->veb[i], link_up);
 
        /* ... now the local VSIs */
-       for (i = 0; i < pf->hw.func_caps.num_vsis; i++)
+       for (i = 0; i < pf->num_alloc_vsi; i++)
                if (pf->vsi[i] && (pf->vsi[i]->uplink_seid == veb->seid))
                        i40e_vsi_link_event(pf->vsi[i], link_up);
 }
@@ -4821,10 +4984,8 @@ static void i40e_link_event(struct i40e_pf *pf)
 
        if (new_link == old_link)
                return;
-
        if (!test_bit(__I40E_DOWN, &pf->vsi[pf->lan_vsi]->state))
-               netdev_info(pf->vsi[pf->lan_vsi]->netdev,
-                           "NIC Link is %s\n", (new_link ? "Up" : "Down"));
+               i40e_print_link_message(pf->vsi[pf->lan_vsi], new_link);
 
        /* Notify the base of the switch tree connected to
         * the link.  Floating VEBs are not notified.
@@ -4862,7 +5023,7 @@ static void i40e_check_hang_subtask(struct i40e_pf *pf)
         *     for each q_vector
         *         force an interrupt
         */
-       for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+       for (v = 0; v < pf->num_alloc_vsi; v++) {
                struct i40e_vsi *vsi = pf->vsi[v];
                int armed = 0;
 
@@ -4912,7 +5073,7 @@ static void i40e_watchdog_subtask(struct i40e_pf *pf)
        /* Update the stats for active netdevs so the network stack
         * can look at updated numbers whenever it cares to
         */
-       for (i = 0; i < pf->hw.func_caps.num_vsis; i++)
+       for (i = 0; i < pf->num_alloc_vsi; i++)
                if (pf->vsi[i] && pf->vsi[i]->netdev)
                        i40e_update_stats(pf->vsi[i]);
 
@@ -5018,11 +5179,47 @@ static void i40e_clean_adminq_subtask(struct i40e_pf *pf)
        u16 pending, i = 0;
        i40e_status ret;
        u16 opcode;
+       u32 oldval;
        u32 val;
 
        if (!test_bit(__I40E_ADMINQ_EVENT_PENDING, &pf->state))
                return;
 
+       /* check for error indications */
+       val = rd32(&pf->hw, pf->hw.aq.arq.len);
+       oldval = val;
+       if (val & I40E_PF_ARQLEN_ARQVFE_MASK) {
+               dev_info(&pf->pdev->dev, "ARQ VF Error detected\n");
+               val &= ~I40E_PF_ARQLEN_ARQVFE_MASK;
+       }
+       if (val & I40E_PF_ARQLEN_ARQOVFL_MASK) {
+               dev_info(&pf->pdev->dev, "ARQ Overflow Error detected\n");
+               val &= ~I40E_PF_ARQLEN_ARQOVFL_MASK;
+       }
+       if (val & I40E_PF_ARQLEN_ARQCRIT_MASK) {
+               dev_info(&pf->pdev->dev, "ARQ Critical Error detected\n");
+               val &= ~I40E_PF_ARQLEN_ARQCRIT_MASK;
+       }
+       if (oldval != val)
+               wr32(&pf->hw, pf->hw.aq.arq.len, val);
+
+       val = rd32(&pf->hw, pf->hw.aq.asq.len);
+       oldval = val;
+       if (val & I40E_PF_ATQLEN_ATQVFE_MASK) {
+               dev_info(&pf->pdev->dev, "ASQ VF Error detected\n");
+               val &= ~I40E_PF_ATQLEN_ATQVFE_MASK;
+       }
+       if (val & I40E_PF_ATQLEN_ATQOVFL_MASK) {
+               dev_info(&pf->pdev->dev, "ASQ Overflow Error detected\n");
+               val &= ~I40E_PF_ATQLEN_ATQOVFL_MASK;
+       }
+       if (val & I40E_PF_ATQLEN_ATQCRIT_MASK) {
+               dev_info(&pf->pdev->dev, "ASQ Critical Error detected\n");
+               val &= ~I40E_PF_ATQLEN_ATQCRIT_MASK;
+       }
+       if (oldval != val)
+               wr32(&pf->hw, pf->hw.aq.asq.len, val);
+
        event.msg_size = I40E_MAX_AQ_BUF_SIZE;
        event.msg_buf = kzalloc(event.msg_size, GFP_KERNEL);
        if (!event.msg_buf)
@@ -5128,7 +5325,7 @@ static int i40e_reconstitute_veb(struct i40e_veb *veb)
        int ret;
 
        /* build VSI that owns this VEB, temporarily attached to base VEB */
-       for (v = 0; v < pf->hw.func_caps.num_vsis && !ctl_vsi; v++) {
+       for (v = 0; v < pf->num_alloc_vsi && !ctl_vsi; v++) {
                if (pf->vsi[v] &&
                    pf->vsi[v]->veb_idx == veb->idx &&
                    pf->vsi[v]->flags & I40E_VSI_FLAG_VEB_OWNER) {
@@ -5158,7 +5355,7 @@ static int i40e_reconstitute_veb(struct i40e_veb *veb)
                goto end_reconstitute;
 
        /* create the remaining VSIs attached to this VEB */
-       for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+       for (v = 0; v < pf->num_alloc_vsi; v++) {
                if (!pf->vsi[v] || pf->vsi[v] == ctl_vsi)
                        continue;
 
@@ -5226,9 +5423,6 @@ static int i40e_get_capabilities(struct i40e_pf *pf)
                }
        } while (err);
 
-       /* increment MSI-X count because current FW skips one */
-       pf->hw.func_caps.num_msix_vectors++;
-
        if (((pf->hw.aq.fw_maj_ver == 2) && (pf->hw.aq.fw_min_ver < 22)) ||
            (pf->hw.aq.fw_maj_ver < 2)) {
                pf->hw.func_caps.num_msix_vectors++;
@@ -5267,15 +5461,14 @@ static int i40e_vsi_clear(struct i40e_vsi *vsi);
 static void i40e_fdir_sb_setup(struct i40e_pf *pf)
 {
        struct i40e_vsi *vsi;
-       bool new_vsi = false;
-       int err, i;
+       int i;
 
        if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED))
                return;
 
        /* find existing VSI and see if it needs configuring */
        vsi = NULL;
-       for (i = 0; i < pf->hw.func_caps.num_vsis; i++) {
+       for (i = 0; i < pf->num_alloc_vsi; i++) {
                if (pf->vsi[i] && pf->vsi[i]->type == I40E_VSI_FDIR) {
                        vsi = pf->vsi[i];
                        break;
@@ -5288,47 +5481,12 @@ static void i40e_fdir_sb_setup(struct i40e_pf *pf)
                                     pf->vsi[pf->lan_vsi]->seid, 0);
                if (!vsi) {
                        dev_info(&pf->pdev->dev, "Couldn't create FDir VSI\n");
-                       goto err_vsi;
+                       pf->flags &= ~I40E_FLAG_FD_SB_ENABLED;
+                       return;
                }
-               new_vsi = true;
-       }
-       i40e_vsi_setup_irqhandler(vsi, i40e_fdir_clean_ring);
-
-       err = i40e_vsi_setup_tx_resources(vsi);
-       if (err)
-               goto err_setup_tx;
-       err = i40e_vsi_setup_rx_resources(vsi);
-       if (err)
-               goto err_setup_rx;
-
-       if (new_vsi) {
-               char int_name[IFNAMSIZ + 9];
-               err = i40e_vsi_configure(vsi);
-               if (err)
-                       goto err_setup_rx;
-               snprintf(int_name, sizeof(int_name) - 1, "%s-fdir",
-                        dev_driver_string(&pf->pdev->dev));
-               err = i40e_vsi_request_irq(vsi, int_name);
-               if (err)
-                       goto err_setup_rx;
-               err = i40e_up_complete(vsi);
-               if (err)
-                       goto err_up_complete;
-               clear_bit(__I40E_NEEDS_RESTART, &vsi->state);
        }
 
-       return;
-
-err_up_complete:
-       i40e_down(vsi);
-       i40e_vsi_free_irq(vsi);
-err_setup_rx:
-       i40e_vsi_free_rx_resources(vsi);
-err_setup_tx:
-       i40e_vsi_free_tx_resources(vsi);
-err_vsi:
-       pf->flags &= ~I40E_FLAG_FD_SB_ENABLED;
-       i40e_vsi_clear(vsi);
+       i40e_vsi_setup_irqhandler(vsi, i40e_fdir_clean_ring);
 }
 
 /**
@@ -5340,7 +5498,7 @@ static void i40e_fdir_teardown(struct i40e_pf *pf)
        int i;
 
        i40e_fdir_filter_exit(pf);
-       for (i = 0; i < pf->hw.func_caps.num_vsis; i++) {
+       for (i = 0; i < pf->num_alloc_vsi; i++) {
                if (pf->vsi[i] && pf->vsi[i]->type == I40E_VSI_FDIR) {
                        i40e_vsi_release(pf->vsi[i]);
                        break;
@@ -5357,7 +5515,7 @@ static void i40e_fdir_teardown(struct i40e_pf *pf)
 static int i40e_prep_for_reset(struct i40e_pf *pf)
 {
        struct i40e_hw *hw = &pf->hw;
-       i40e_status ret;
+       i40e_status ret = 0;
        u32 v;
 
        clear_bit(__I40E_RESET_INTR_RECEIVED, &pf->state);
@@ -5366,13 +5524,10 @@ static int i40e_prep_for_reset(struct i40e_pf *pf)
 
        dev_dbg(&pf->pdev->dev, "Tearing down internal switch for reset\n");
 
-       if (i40e_check_asq_alive(hw))
-               i40e_vc_notify_reset(pf);
-
        /* quiesce the VSIs and their queues that are not already DOWN */
        i40e_pf_quiesce_all_vsi(pf);
 
-       for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+       for (v = 0; v < pf->num_alloc_vsi; v++) {
                if (pf->vsi[v])
                        pf->vsi[v]->seid = 0;
        }
@@ -5380,14 +5535,33 @@ static int i40e_prep_for_reset(struct i40e_pf *pf)
        i40e_shutdown_adminq(&pf->hw);
 
        /* call shutdown HMC */
-       ret = i40e_shutdown_lan_hmc(hw);
-       if (ret) {
-               dev_info(&pf->pdev->dev, "shutdown_lan_hmc failed: %d\n", ret);
-               clear_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state);
+       if (hw->hmc.hmc_obj) {
+               ret = i40e_shutdown_lan_hmc(hw);
+               if (ret) {
+                       dev_warn(&pf->pdev->dev,
+                                "shutdown_lan_hmc failed: %d\n", ret);
+                       clear_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state);
+               }
        }
        return ret;
 }
 
+/**
+ * i40e_send_version - update firmware with driver version
+ * @pf: PF struct
+ */
+static void i40e_send_version(struct i40e_pf *pf)
+{
+       struct i40e_driver_version dv;
+
+       dv.major_version = DRV_VERSION_MAJOR;
+       dv.minor_version = DRV_VERSION_MINOR;
+       dv.build_version = DRV_VERSION_BUILD;
+       dv.subbuild_version = 0;
+       strncpy(dv.driver_string, DRV_VERSION, sizeof(dv.driver_string));
+       i40e_aq_send_driver_version(&pf->hw, &dv, NULL);
+}
+
 /**
  * i40e_reset_and_rebuild - reset and rebuild using a saved config
  * @pf: board private structure
@@ -5395,7 +5569,6 @@ static int i40e_prep_for_reset(struct i40e_pf *pf)
  **/
 static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit)
 {
-       struct i40e_driver_version dv;
        struct i40e_hw *hw = &pf->hw;
        i40e_status ret;
        u32 v;
@@ -5405,8 +5578,10 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit)
         * because the reset will make them disappear.
         */
        ret = i40e_pf_reset(hw);
-       if (ret)
+       if (ret) {
                dev_info(&pf->pdev->dev, "PF reset failed, %d\n", ret);
+               goto end_core_reset;
+       }
        pf->pfr_count++;
 
        if (test_bit(__I40E_DOWN, &pf->state))
@@ -5426,6 +5601,7 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit)
                i40e_verify_eeprom(pf);
        }
 
+       i40e_clear_pxe_mode(hw);
        ret = i40e_get_capabilities(pf);
        if (ret) {
                dev_info(&pf->pdev->dev, "i40e_get_capabilities failed, %d\n",
@@ -5526,13 +5702,7 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit)
        }
 
        /* tell the firmware that we're starting */
-       dv.major_version = DRV_VERSION_MAJOR;
-       dv.minor_version = DRV_VERSION_MINOR;
-       dv.build_version = DRV_VERSION_BUILD;
-       dv.subbuild_version = 0;
-       i40e_aq_send_driver_version(&pf->hw, &dv, NULL);
-
-       dev_info(&pf->pdev->dev, "reset complete\n");
+       i40e_send_version(pf);
 
 end_core_reset:
        clear_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state);
@@ -5642,7 +5812,6 @@ static void i40e_handle_mdd_event(struct i40e_pf *pf)
  **/
 static void i40e_sync_vxlan_filters_subtask(struct i40e_pf *pf)
 {
-       const int vxlan_hdr_qwords = 4;
        struct i40e_hw *hw = &pf->hw;
        i40e_status ret;
        u8 filter_index;
@@ -5660,7 +5829,6 @@ static void i40e_sync_vxlan_filters_subtask(struct i40e_pf *pf)
                        port = pf->vxlan_ports[i];
                        ret = port ?
                              i40e_aq_add_udp_tunnel(hw, ntohs(port),
-                                                    vxlan_hdr_qwords,
                                                     I40E_AQC_TUNNEL_TYPE_VXLAN,
                                                     &filter_index, NULL)
                              : i40e_aq_del_udp_tunnel(hw, i, NULL);
@@ -5839,15 +6007,15 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type)
         * find next empty vsi slot, looping back around if necessary
         */
        i = pf->next_vsi;
-       while (i < pf->hw.func_caps.num_vsis && pf->vsi[i])
+       while (i < pf->num_alloc_vsi && pf->vsi[i])
                i++;
-       if (i >= pf->hw.func_caps.num_vsis) {
+       if (i >= pf->num_alloc_vsi) {
                i = 0;
                while (i < pf->next_vsi && pf->vsi[i])
                        i++;
        }
 
-       if (i < pf->hw.func_caps.num_vsis && !pf->vsi[i]) {
+       if (i < pf->num_alloc_vsi && !pf->vsi[i]) {
                vsi_idx = i;             /* Found one! */
        } else {
                ret = -ENODEV;
@@ -5870,6 +6038,7 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type)
        vsi->netdev_registered = false;
        vsi->work_limit = I40E_DEFAULT_IRQ_WORK;
        INIT_LIST_HEAD(&vsi->mac_filter_list);
+       vsi->irqs_ready = false;
 
        ret = i40e_set_num_rings_in_vsi(vsi);
        if (ret)
@@ -5987,14 +6156,12 @@ static void i40e_vsi_clear_rings(struct i40e_vsi *vsi)
  **/
 static int i40e_alloc_rings(struct i40e_vsi *vsi)
 {
+       struct i40e_ring *tx_ring, *rx_ring;
        struct i40e_pf *pf = vsi->back;
        int i;
 
        /* Set basic values in the rings to be used later during open() */
        for (i = 0; i < vsi->alloc_queue_pairs; i++) {
-               struct i40e_ring *tx_ring;
-               struct i40e_ring *rx_ring;
-
                /* allocate space for both Tx and Rx in one shot */
                tx_ring = kzalloc(sizeof(struct i40e_ring) * 2, GFP_KERNEL);
                if (!tx_ring)
@@ -6052,8 +6219,6 @@ static int i40e_reserve_msix_vectors(struct i40e_pf *pf, int vectors)
                vectors = 0;
        }
 
-       pf->num_msix_entries = vectors;
-
        return vectors;
 }
 
@@ -6107,6 +6272,16 @@ static int i40e_init_msix(struct i40e_pf *pf)
        for (i = 0; i < v_budget; i++)
                pf->msix_entries[i].entry = i;
        vec = i40e_reserve_msix_vectors(pf, v_budget);
+
+       if (vec != v_budget) {
+               /* If we have limited resources, we will start with no vectors
+                * for the special features and then allocate vectors to some
+                * of these features based on the policy and at the end disable
+                * the features that did not get any vectors.
+                */
+               pf->num_vmdq_msix = 0;
+       }
+
        if (vec < I40E_MIN_MSIX) {
                pf->flags &= ~I40E_FLAG_MSIX_ENABLED;
                kfree(pf->msix_entries);
@@ -6115,27 +6290,25 @@ static int i40e_init_msix(struct i40e_pf *pf)
 
        } else if (vec == I40E_MIN_MSIX) {
                /* Adjust for minimal MSIX use */
-               dev_info(&pf->pdev->dev, "Features disabled, not enough MSI-X vectors\n");
-               pf->flags &= ~I40E_FLAG_VMDQ_ENABLED;
                pf->num_vmdq_vsis = 0;
                pf->num_vmdq_qps = 0;
-               pf->num_vmdq_msix = 0;
                pf->num_lan_qps = 1;
                pf->num_lan_msix = 1;
 
        } else if (vec != v_budget) {
+               /* reserve the misc vector */
+               vec--;
+
                /* Scale vector usage down */
                pf->num_vmdq_msix = 1;    /* force VMDqs to only one vector */
-               vec--;                    /* reserve the misc vector */
+               pf->num_vmdq_vsis = 1;
 
                /* partition out the remaining vectors */
                switch (vec) {
                case 2:
-                       pf->num_vmdq_vsis = 1;
                        pf->num_lan_msix = 1;
                        break;
                case 3:
-                       pf->num_vmdq_vsis = 1;
                        pf->num_lan_msix = 2;
                        break;
                default:
@@ -6147,6 +6320,11 @@ static int i40e_init_msix(struct i40e_pf *pf)
                }
        }
 
+       if ((pf->flags & I40E_FLAG_VMDQ_ENABLED) &&
+           (pf->num_vmdq_msix == 0)) {
+               dev_info(&pf->pdev->dev, "VMDq disabled, not enough MSI-X vectors\n");
+               pf->flags &= ~I40E_FLAG_VMDQ_ENABLED;
+       }
        return err;
 }
 
@@ -6171,7 +6349,7 @@ static int i40e_vsi_alloc_q_vector(struct i40e_vsi *vsi, int v_idx)
        cpumask_set_cpu(v_idx, &q_vector->affinity_mask);
        if (vsi->netdev)
                netif_napi_add(vsi->netdev, &q_vector->napi,
-                              i40e_napi_poll, vsi->work_limit);
+                              i40e_napi_poll, NAPI_POLL_WEIGHT);
 
        q_vector->rx.latency_range = I40E_LOW_LATENCY;
        q_vector->tx.latency_range = I40E_LOW_LATENCY;
@@ -6231,7 +6409,7 @@ static void i40e_init_interrupt_scheme(struct i40e_pf *pf)
                if (err) {
                        pf->flags &= ~(I40E_FLAG_MSIX_ENABLED   |
                                       I40E_FLAG_RSS_ENABLED    |
-                                      I40E_FLAG_DCB_ENABLED    |
+                                      I40E_FLAG_DCB_CAPABLE    |
                                       I40E_FLAG_SRIOV_ENABLED  |
                                       I40E_FLAG_FD_SB_ENABLED  |
                                       I40E_FLAG_FD_ATR_ENABLED |
@@ -6364,7 +6542,6 @@ int i40e_reconfig_rss_queues(struct i40e_pf *pf, int queue_count)
                return 0;
 
        queue_count = min_t(int, queue_count, pf->rss_size_max);
-       queue_count = rounddown_pow_of_two(queue_count);
 
        if (queue_count != pf->rss_size) {
                i40e_prep_for_reset(pf);
@@ -6407,6 +6584,10 @@ static int i40e_sw_init(struct i40e_pf *pf)
                    I40E_FLAG_MSIX_ENABLED    |
                    I40E_FLAG_RX_1BUF_ENABLED;
 
+       /* Set default ITR */
+       pf->rx_itr_default = I40E_ITR_DYNAMIC | I40E_ITR_RX_DEF;
+       pf->tx_itr_default = I40E_ITR_DYNAMIC | I40E_ITR_TX_DEF;
+
        /* Depending on PF configurations, it is possible that the RSS
         * maximum might end up larger than the available queues
         */
@@ -6416,7 +6597,6 @@ static int i40e_sw_init(struct i40e_pf *pf)
        if (pf->hw.func_caps.rss) {
                pf->flags |= I40E_FLAG_RSS_ENABLED;
                pf->rss_size = min_t(int, pf->rss_size_max, num_online_cpus());
-               pf->rss_size = rounddown_pow_of_two(pf->rss_size);
        } else {
                pf->rss_size = 1;
        }
@@ -6432,8 +6612,12 @@ static int i40e_sw_init(struct i40e_pf *pf)
            (pf->hw.func_caps.fd_filters_best_effort > 0)) {
                pf->flags |= I40E_FLAG_FD_ATR_ENABLED;
                pf->atr_sample_rate = I40E_DEFAULT_ATR_SAMPLE_RATE;
+               /* Setup a counter for fd_atr per pf */
+               pf->fd_atr_cnt_idx = I40E_FD_ATR_STAT_IDX(pf->hw.pf_id);
                if (!(pf->flags & I40E_FLAG_MFP_ENABLED)) {
                        pf->flags |= I40E_FLAG_FD_SB_ENABLED;
+                       /* Setup a counter for fd_sb per pf */
+                       pf->fd_sb_cnt_idx = I40E_FD_SB_STAT_IDX(pf->hw.pf_id);
                } else {
                        dev_info(&pf->pdev->dev,
                                 "Flow Director Sideband mode Disabled in MFP mode\n");
@@ -6649,6 +6833,96 @@ static void i40e_del_vxlan_port(struct net_device *netdev,
 }
 
 #endif
+#ifdef HAVE_FDB_OPS
+#ifdef USE_CONST_DEV_UC_CHAR
+static int i40e_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
+                           struct net_device *dev,
+                           const unsigned char *addr,
+                           u16 flags)
+#else
+static int i40e_ndo_fdb_add(struct ndmsg *ndm,
+                           struct net_device *dev,
+                           unsigned char *addr,
+                           u16 flags)
+#endif
+{
+       struct i40e_netdev_priv *np = netdev_priv(dev);
+       struct i40e_pf *pf = np->vsi->back;
+       int err = 0;
+
+       if (!(pf->flags & I40E_FLAG_SRIOV_ENABLED))
+               return -EOPNOTSUPP;
+
+       /* Hardware does not support aging addresses so if a
+        * ndm_state is given only allow permanent addresses
+        */
+       if (ndm->ndm_state && !(ndm->ndm_state & NUD_PERMANENT)) {
+               netdev_info(dev, "FDB only supports static addresses\n");
+               return -EINVAL;
+       }
+
+       if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr))
+               err = dev_uc_add_excl(dev, addr);
+       else if (is_multicast_ether_addr(addr))
+               err = dev_mc_add_excl(dev, addr);
+       else
+               err = -EINVAL;
+
+       /* Only return duplicate errors if NLM_F_EXCL is set */
+       if (err == -EEXIST && !(flags & NLM_F_EXCL))
+               err = 0;
+
+       return err;
+}
+
+#ifndef USE_DEFAULT_FDB_DEL_DUMP
+#ifdef USE_CONST_DEV_UC_CHAR
+static int i40e_ndo_fdb_del(struct ndmsg *ndm,
+                           struct net_device *dev,
+                           const unsigned char *addr)
+#else
+static int i40e_ndo_fdb_del(struct ndmsg *ndm,
+                           struct net_device *dev,
+                           unsigned char *addr)
+#endif
+{
+       struct i40e_netdev_priv *np = netdev_priv(dev);
+       struct i40e_pf *pf = np->vsi->back;
+       int err = -EOPNOTSUPP;
+
+       if (ndm->ndm_state & NUD_PERMANENT) {
+               netdev_info(dev, "FDB only supports static addresses\n");
+               return -EINVAL;
+       }
+
+       if (pf->flags & I40E_FLAG_SRIOV_ENABLED) {
+               if (is_unicast_ether_addr(addr))
+                       err = dev_uc_del(dev, addr);
+               else if (is_multicast_ether_addr(addr))
+                       err = dev_mc_del(dev, addr);
+               else
+                       err = -EINVAL;
+       }
+
+       return err;
+}
+
+static int i40e_ndo_fdb_dump(struct sk_buff *skb,
+                            struct netlink_callback *cb,
+                            struct net_device *dev,
+                            int idx)
+{
+       struct i40e_netdev_priv *np = netdev_priv(dev);
+       struct i40e_pf *pf = np->vsi->back;
+
+       if (pf->flags & I40E_FLAG_SRIOV_ENABLED)
+               idx = ndo_dflt_fdb_dump(skb, cb, dev, idx);
+
+       return idx;
+}
+
+#endif /* USE_DEFAULT_FDB_DEL_DUMP */
+#endif /* HAVE_FDB_OPS */
 static const struct net_device_ops i40e_netdev_ops = {
        .ndo_open               = i40e_open,
        .ndo_stop               = i40e_close,
@@ -6669,13 +6943,21 @@ static const struct net_device_ops i40e_netdev_ops = {
        .ndo_set_features       = i40e_set_features,
        .ndo_set_vf_mac         = i40e_ndo_set_vf_mac,
        .ndo_set_vf_vlan        = i40e_ndo_set_vf_port_vlan,
-       .ndo_set_vf_tx_rate     = i40e_ndo_set_vf_bw,
+       .ndo_set_vf_rate        = i40e_ndo_set_vf_bw,
        .ndo_get_vf_config      = i40e_ndo_get_vf_config,
        .ndo_set_vf_link_state  = i40e_ndo_set_vf_link_state,
+       .ndo_set_vf_spoofchk    = i40e_ndo_set_vf_spoofck,
 #ifdef CONFIG_I40E_VXLAN
        .ndo_add_vxlan_port     = i40e_add_vxlan_port,
        .ndo_del_vxlan_port     = i40e_del_vxlan_port,
 #endif
+#ifdef HAVE_FDB_OPS
+       .ndo_fdb_add            = i40e_ndo_fdb_add,
+#ifndef USE_DEFAULT_FDB_DEL_DUMP
+       .ndo_fdb_del            = i40e_ndo_fdb_del,
+       .ndo_fdb_dump           = i40e_ndo_fdb_dump,
+#endif
+#endif
 };
 
 /**
@@ -6720,16 +7002,26 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
                           NETIF_F_TSO_ECN             |
                           NETIF_F_TSO6                |
                           NETIF_F_RXCSUM              |
-                          NETIF_F_NTUPLE              |
                           NETIF_F_RXHASH              |
                           0;
 
+       if (!(pf->flags & I40E_FLAG_MFP_ENABLED))
+               netdev->features |= NETIF_F_NTUPLE;
+
        /* copy netdev features into list of user selectable features */
        netdev->hw_features |= netdev->features;
 
        if (vsi->type == I40E_VSI_MAIN) {
                SET_NETDEV_DEV(netdev, &pf->pdev->dev);
-               memcpy(mac_addr, hw->mac.perm_addr, ETH_ALEN);
+               ether_addr_copy(mac_addr, hw->mac.perm_addr);
+               /* The following two steps are necessary to prevent reception
+                * of tagged packets - by default the NVM loads a MAC-VLAN
+                * filter that will accept any tagged packet.  This is to
+                * prevent that during normal operations until a specific
+                * VLAN tag filter has been set.
+                */
+               i40e_rm_default_mac_filter(vsi, mac_addr);
+               i40e_add_filter(vsi, mac_addr, I40E_VLAN_ANY, false, true);
        } else {
                /* relate the VSI_VMDQ name to the VSI_MAIN name */
                snprintf(netdev->name, IFNAMSIZ, "%sv%%d",
@@ -6739,8 +7031,8 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
        }
        i40e_add_filter(vsi, brdcast, I40E_VLAN_ANY, false, false);
 
-       memcpy(netdev->dev_addr, mac_addr, ETH_ALEN);
-       memcpy(netdev->perm_addr, mac_addr, ETH_ALEN);
+       ether_addr_copy(netdev->dev_addr, mac_addr);
+       ether_addr_copy(netdev->perm_addr, mac_addr);
        /* vlan gets same features (except vlan offload)
         * after any tweaks for specific VSI types
         */
@@ -6772,7 +7064,6 @@ static void i40e_vsi_delete(struct i40e_vsi *vsi)
                return;
 
        i40e_aq_delete_element(&vsi->back->hw, vsi->seid, NULL);
-       return;
 }
 
 /**
@@ -6898,6 +7189,13 @@ static int i40e_add_vsi(struct i40e_vsi *vsi)
 
                ctxt.info.valid_sections |= cpu_to_le16(I40E_AQ_VSI_PROP_VLAN_VALID);
                ctxt.info.port_vlan_flags |= I40E_AQ_VSI_PVLAN_MODE_ALL;
+               if (pf->vf[vsi->vf_id].spoofchk) {
+                       ctxt.info.valid_sections |=
+                               cpu_to_le16(I40E_AQ_VSI_PROP_SECURITY_VALID);
+                       ctxt.info.sec_flags |=
+                               (I40E_AQ_VSI_SEC_FLAG_ENABLE_VLAN_CHK |
+                                I40E_AQ_VSI_SEC_FLAG_ENABLE_MAC_CHK);
+               }
                /* Setup the VSI tx/rx queue map for TC0 only for now */
                i40e_vsi_setup_queue_map(vsi, &ctxt, enabled_tc, true);
                break;
@@ -6982,11 +7280,7 @@ int i40e_vsi_release(struct i40e_vsi *vsi)
                                unregister_netdev(vsi->netdev);
                        }
                } else {
-                       if (!test_and_set_bit(__I40E_DOWN, &vsi->state))
-                               i40e_down(vsi);
-                       i40e_vsi_free_irq(vsi);
-                       i40e_vsi_free_tx_resources(vsi);
-                       i40e_vsi_free_rx_resources(vsi);
+                       i40e_vsi_close(vsi);
                }
                i40e_vsi_disable_irq(vsi);
        }
@@ -7013,7 +7307,7 @@ int i40e_vsi_release(struct i40e_vsi *vsi)
         * the orphan VEBs yet.  We'll wait for an explicit remove request
         * from up the network stack.
         */
-       for (n = 0, i = 0; i < pf->hw.func_caps.num_vsis; i++) {
+       for (n = 0, i = 0; i < pf->num_alloc_vsi; i++) {
                if (pf->vsi[i] &&
                    pf->vsi[i]->uplink_seid == uplink_seid &&
                    (pf->vsi[i]->flags & I40E_VSI_FLAG_VEB_OWNER) == 0) {
@@ -7192,7 +7486,7 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type,
 
        if (!veb && uplink_seid != pf->mac_seid) {
 
-               for (i = 0; i < pf->hw.func_caps.num_vsis; i++) {
+               for (i = 0; i < pf->num_alloc_vsi; i++) {
                        if (pf->vsi[i] && pf->vsi[i]->seid == uplink_seid) {
                                vsi = pf->vsi[i];
                                break;
@@ -7435,7 +7729,7 @@ static void i40e_switch_branch_release(struct i40e_veb *branch)
         * NOTE: Removing the last VSI on a VEB has the SIDE EFFECT of removing
         *       the VEB itself, so don't use (*branch) after this loop.
         */
-       for (i = 0; i < pf->hw.func_caps.num_vsis; i++) {
+       for (i = 0; i < pf->num_alloc_vsi; i++) {
                if (!pf->vsi[i])
                        continue;
                if (pf->vsi[i]->uplink_seid == branch_seid &&
@@ -7487,7 +7781,7 @@ void i40e_veb_release(struct i40e_veb *veb)
        pf = veb->pf;
 
        /* find the remaining VSI and check for extras */
-       for (i = 0; i < pf->hw.func_caps.num_vsis; i++) {
+       for (i = 0; i < pf->num_alloc_vsi; i++) {
                if (pf->vsi[i] && pf->vsi[i]->uplink_seid == veb->seid) {
                        n++;
                        vsi = pf->vsi[i];
@@ -7516,8 +7810,6 @@ void i40e_veb_release(struct i40e_veb *veb)
 
        i40e_aq_delete_element(&pf->hw, veb->seid, NULL);
        i40e_veb_clear(veb);
-
-       return;
 }
 
 /**
@@ -7601,10 +7893,10 @@ struct i40e_veb *i40e_veb_setup(struct i40e_pf *pf, u16 flags,
        }
 
        /* make sure there is such a vsi and uplink */
-       for (vsi_idx = 0; vsi_idx < pf->hw.func_caps.num_vsis; vsi_idx++)
+       for (vsi_idx = 0; vsi_idx < pf->num_alloc_vsi; vsi_idx++)
                if (pf->vsi[vsi_idx] && pf->vsi[vsi_idx]->seid == vsi_seid)
                        break;
-       if (vsi_idx >= pf->hw.func_caps.num_vsis && vsi_seid != 0) {
+       if (vsi_idx >= pf->num_alloc_vsi && vsi_seid != 0) {
                dev_info(&pf->pdev->dev, "vsi seid %d not found\n",
                         vsi_seid);
                return NULL;
@@ -7639,6 +7931,8 @@ struct i40e_veb *i40e_veb_setup(struct i40e_pf *pf, u16 flags,
        ret = i40e_add_veb(veb, pf->vsi[vsi_idx]);
        if (ret)
                goto err_veb;
+       if (vsi_idx == pf->lan_vsi)
+               pf->lan_veb = veb->idx;
 
        return veb;
 
@@ -7774,15 +8068,6 @@ int i40e_fetch_switch_configuration(struct i40e_pf *pf, bool printconfig)
                                 "header: %d reported %d total\n",
                                 num_reported, num_total);
 
-               if (num_reported) {
-                       int sz = sizeof(*sw_config) * num_reported;
-
-                       kfree(pf->sw_config);
-                       pf->sw_config = kzalloc(sz, GFP_KERNEL);
-                       if (pf->sw_config)
-                               memcpy(pf->sw_config, sw_config, sz);
-               }
-
                for (i = 0; i < num_reported; i++) {
                        struct i40e_aqc_switch_config_element_resp *ele =
                                &sw_config->element[i];
@@ -7949,9 +8234,7 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf)
        queues_left = pf->hw.func_caps.num_tx_qp;
 
        if ((queues_left == 1) ||
-           !(pf->flags & I40E_FLAG_MSIX_ENABLED) ||
-           !(pf->flags & (I40E_FLAG_RSS_ENABLED | I40E_FLAG_FD_SB_ENABLED |
-                          I40E_FLAG_DCB_ENABLED))) {
+           !(pf->flags & I40E_FLAG_MSIX_ENABLED)) {
                /* one qp for PF, no queues for anything else */
                queues_left = 0;
                pf->rss_size = pf->num_lan_qps = 1;
@@ -7960,14 +8243,27 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf)
                pf->flags &= ~(I40E_FLAG_RSS_ENABLED    |
                               I40E_FLAG_FD_SB_ENABLED  |
                               I40E_FLAG_FD_ATR_ENABLED |
-                              I40E_FLAG_DCB_ENABLED    |
+                              I40E_FLAG_DCB_CAPABLE    |
                               I40E_FLAG_SRIOV_ENABLED  |
                               I40E_FLAG_VMDQ_ENABLED);
+       } else if (!(pf->flags & (I40E_FLAG_RSS_ENABLED |
+                                 I40E_FLAG_FD_SB_ENABLED |
+                                 I40E_FLAG_FD_ATR_ENABLED |
+                                 I40E_FLAG_DCB_CAPABLE))) {
+               /* one qp for PF */
+               pf->rss_size = pf->num_lan_qps = 1;
+               queues_left -= pf->num_lan_qps;
+
+               pf->flags &= ~(I40E_FLAG_RSS_ENABLED    |
+                              I40E_FLAG_FD_SB_ENABLED  |
+                              I40E_FLAG_FD_ATR_ENABLED |
+                              I40E_FLAG_DCB_ENABLED    |
+                              I40E_FLAG_VMDQ_ENABLED);
        } else {
                /* Not enough queues for all TCs */
-               if ((pf->flags & I40E_FLAG_DCB_ENABLED) &&
+               if ((pf->flags & I40E_FLAG_DCB_CAPABLE) &&
                    (queues_left < I40E_MAX_TRAFFIC_CLASS)) {
-                       pf->flags &= ~I40E_FLAG_DCB_ENABLED;
+                       pf->flags &= ~I40E_FLAG_DCB_CAPABLE;
                        dev_info(&pf->pdev->dev, "not enough queues for DCB. DCB is disabled.\n");
                }
                pf->num_lan_qps = pf->rss_size_max;
@@ -7998,7 +8294,6 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf)
        }
 
        pf->queues_left = queues_left;
-       return;
 }
 
 /**
@@ -8055,12 +8350,13 @@ static void i40e_print_features(struct i40e_pf *pf)
 
        if (pf->flags & I40E_FLAG_RSS_ENABLED)
                buf += sprintf(buf, "RSS ");
-       buf += sprintf(buf, "FDir ");
        if (pf->flags & I40E_FLAG_FD_ATR_ENABLED)
-               buf += sprintf(buf, "ATR ");
-       if (pf->flags & I40E_FLAG_FD_SB_ENABLED)
+               buf += sprintf(buf, "FD_ATR ");
+       if (pf->flags & I40E_FLAG_FD_SB_ENABLED) {
+               buf += sprintf(buf, "FD_SB ");
                buf += sprintf(buf, "NTUPLE ");
-       if (pf->flags & I40E_FLAG_DCB_ENABLED)
+       }
+       if (pf->flags & I40E_FLAG_DCB_CAPABLE)
                buf += sprintf(buf, "DCB ");
        if (pf->flags & I40E_FLAG_PTP)
                buf += sprintf(buf, "PTP ");
@@ -8083,13 +8379,13 @@ static void i40e_print_features(struct i40e_pf *pf)
  **/
 static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
-       struct i40e_driver_version dv;
        struct i40e_pf *pf;
        struct i40e_hw *hw;
        static u16 pfs_found;
        u16 link_status;
        int err = 0;
        u32 len;
+       u32 i;
 
        err = pci_enable_device_mem(pdev);
        if (err)
@@ -8201,6 +8497,10 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        i40e_verify_eeprom(pf);
 
+       /* Rev 0 hardware was never productized */
+       if (hw->revision_id < 1)
+               dev_warn(&pdev->dev, "This device is a pre-production adapter/LOM. Please be aware there may be issues with your hardware. If you are experiencing problems please contact your Intel or hardware representative who provided you with this hardware.\n");
+
        i40e_clear_pxe_mode(hw);
        err = i40e_get_capabilities(pf);
        if (err)
@@ -8234,7 +8534,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                goto err_mac_addr;
        }
        dev_info(&pdev->dev, "MAC address: %pM\n", hw->mac.addr);
-       memcpy(hw->mac.perm_addr, hw->mac.addr, ETH_ALEN);
+       ether_addr_copy(hw->mac.perm_addr, hw->mac.addr);
 
        pci_set_drvdata(pdev, pf);
        pci_save_state(pdev);
@@ -8242,8 +8542,8 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        err = i40e_init_pf_dcb(pf);
        if (err) {
                dev_info(&pdev->dev, "init_pf_dcb failed: %d\n", err);
-               pf->flags &= ~I40E_FLAG_DCB_ENABLED;
-               goto err_init_dcb;
+               pf->flags &= ~I40E_FLAG_DCB_CAPABLE;
+               /* Continue without DCB enabled */
        }
 #endif /* CONFIG_I40E_DCB */
 
@@ -8264,10 +8564,18 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        i40e_determine_queue_usage(pf);
        i40e_init_interrupt_scheme(pf);
 
-       /* Set up the *vsi struct based on the number of VSIs in the HW,
-        * and set up our local tracking of the MAIN PF vsi.
+       /* The number of VSIs reported by the FW is the minimum guaranteed
+        * to us; HW supports far more and we share the remaining pool with
+        * the other PFs. We allocate space for more than the guarantee with
+        * the understanding that we might not get them all later.
         */
-       len = sizeof(struct i40e_vsi *) * pf->hw.func_caps.num_vsis;
+       if (pf->hw.func_caps.num_vsis < I40E_MIN_VSI_ALLOC)
+               pf->num_alloc_vsi = I40E_MIN_VSI_ALLOC;
+       else
+               pf->num_alloc_vsi = pf->hw.func_caps.num_vsis;
+
+       /* Set up the *vsi struct and our local tracking of the MAIN PF vsi. */
+       len = sizeof(struct i40e_vsi *) * pf->num_alloc_vsi;
        pf->vsi = kzalloc(len, GFP_KERNEL);
        if (!pf->vsi) {
                err = -ENOMEM;
@@ -8279,6 +8587,13 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                dev_info(&pdev->dev, "setup_pf_switch failed: %d\n", err);
                goto err_vsis;
        }
+       /* if FDIR VSI was set up, start it now */
+       for (i = 0; i < pf->num_alloc_vsi; i++) {
+               if (pf->vsi[i] && pf->vsi[i]->type == I40E_VSI_FDIR) {
+                       i40e_vsi_open(pf->vsi[i]);
+                       break;
+               }
+       }
 
        /* The main driver is (mostly) up and happy. We need to set this state
         * before setting up the misc vector or we get a race and the vector
@@ -8300,6 +8615,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                }
        }
 
+#ifdef CONFIG_PCI_IOV
        /* prep for VF support */
        if ((pf->flags & I40E_FLAG_SRIOV_ENABLED) &&
            (pf->flags & I40E_FLAG_MSIX_ENABLED) &&
@@ -8322,17 +8638,14 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                                         err);
                }
        }
+#endif /* CONFIG_PCI_IOV */
 
        pfs_found++;
 
        i40e_dbg_pf_init(pf);
 
        /* tell the firmware that we're starting */
-       dv.major_version = DRV_VERSION_MAJOR;
-       dv.minor_version = DRV_VERSION_MINOR;
-       dv.build_version = DRV_VERSION_BUILD;
-       dv.subbuild_version = 0;
-       i40e_aq_send_driver_version(&pf->hw, &dv, NULL);
+       i40e_send_version(pf);
 
        /* since everything's happy, start the service_task timer */
        mod_timer(&pf->service_timer,
@@ -8373,9 +8686,6 @@ err_vsis:
 err_switch_setup:
        i40e_reset_interrupt_capability(pf);
        del_timer_sync(&pf->service_timer);
-#ifdef CONFIG_I40E_DCB
-err_init_dcb:
-#endif /* CONFIG_I40E_DCB */
 err_mac_addr:
 err_configure_lan_hmc:
        (void)i40e_shutdown_lan_hmc(hw);
@@ -8456,10 +8766,13 @@ static void i40e_remove(struct pci_dev *pdev)
        }
 
        /* shutdown and destroy the HMC */
-       ret_code = i40e_shutdown_lan_hmc(&pf->hw);
-       if (ret_code)
-               dev_warn(&pdev->dev,
-                        "Failed to destroy the HMC resources: %d\n", ret_code);
+       if (pf->hw.hmc.hmc_obj) {
+               ret_code = i40e_shutdown_lan_hmc(&pf->hw);
+               if (ret_code)
+                       dev_warn(&pdev->dev,
+                                "Failed to destroy the HMC resources: %d\n",
+                                ret_code);
+       }
 
        /* shutdown the adminq */
        ret_code = i40e_shutdown_adminq(&pf->hw);
@@ -8470,7 +8783,7 @@ static void i40e_remove(struct pci_dev *pdev)
 
        /* Clear all dynamic memory lists of rings, q_vectors, and VSIs */
        i40e_clear_interrupt_scheme(pf);
-       for (i = 0; i < pf->hw.func_caps.num_vsis; i++) {
+       for (i = 0; i < pf->num_alloc_vsi; i++) {
                if (pf->vsi[i]) {
                        i40e_vsi_clear_rings(pf->vsi[i]);
                        i40e_vsi_clear(pf->vsi[i]);
@@ -8485,7 +8798,6 @@ static void i40e_remove(struct pci_dev *pdev)
 
        kfree(pf->qp_pile);
        kfree(pf->irq_pile);
-       kfree(pf->sw_config);
        kfree(pf->vsi);
 
        /* force a PF reset to clean anything leftover */
index 9cd57e617959b1622ec57f8c8f6e2146c9c800a6..a430699c41d5e2be548db369bca02efdcaad40bf 100644 (file)
@@ -70,10 +70,12 @@ i40e_status i40e_aq_get_firmware_version(struct i40e_hw *hw,
                                u16 *fw_major_version, u16 *fw_minor_version,
                                u16 *api_major_version, u16 *api_minor_version,
                                struct i40e_asq_cmd_details *cmd_details);
-i40e_status i40e_aq_set_phy_reset(struct i40e_hw *hw,
+i40e_status i40e_aq_set_phy_debug(struct i40e_hw *hw, u8 cmd_flags,
                                struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_set_default_vsi(struct i40e_hw *hw, u16 vsi_id,
                                struct i40e_asq_cmd_details *cmd_details);
+i40e_status i40e_aq_clear_pxe_mode(struct i40e_hw *hw,
+                               struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_set_link_restart_an(struct i40e_hw *hw,
                                struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_get_link_info(struct i40e_hw *hw,
@@ -157,8 +159,8 @@ i40e_status i40e_aq_stop_lldp(struct i40e_hw *hw, bool shutdown_agent,
 i40e_status i40e_aq_start_lldp(struct i40e_hw *hw,
                                struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_add_udp_tunnel(struct i40e_hw *hw,
-                               u16 udp_port, u8 header_len,
-                               u8 protocol_index, u8 *filter_index,
+                               u16 udp_port, u8 protocol_index,
+                               u8 *filter_index,
                                struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_del_udp_tunnel(struct i40e_hw *hw, u8 index,
                                struct i40e_asq_cmd_details *cmd_details);
@@ -167,6 +169,9 @@ i40e_status i40e_aq_delete_element(struct i40e_hw *hw, u16 seid,
 i40e_status i40e_aq_mac_address_write(struct i40e_hw *hw,
                                    u16 flags, u8 *mac_addr,
                                    struct i40e_asq_cmd_details *cmd_details);
+i40e_status i40e_aq_config_vsi_bw_limit(struct i40e_hw *hw,
+                               u16 seid, u16 credit, u8 max_credit,
+                               struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_dcb_updated(struct i40e_hw *hw,
                                struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_set_hmc_resource_profile(struct i40e_hw *hw,
@@ -216,6 +221,7 @@ bool i40e_get_link_status(struct i40e_hw *hw);
 i40e_status i40e_get_mac_addr(struct i40e_hw *hw,
                                                u8 *mac_addr);
 i40e_status i40e_validate_mac_addr(u8 *mac_addr);
+void i40e_pre_tx_queue_cfg(struct i40e_hw *hw, u32 queue, bool enable);
 /* prototype for functions used for NVM access */
 i40e_status i40e_init_nvm(struct i40e_hw *hw);
 i40e_status i40e_acquire_nvm(struct i40e_hw *hw,
index e61e63720800fcbcc9c13b269f531b65e3902d7e..101f439acda6adfd7e57084865f3bd1b16d06362 100644 (file)
@@ -48,7 +48,6 @@
                                        I40E_PRTTSYN_CTL1_TSYNTYPE_SHIFT)
 #define I40E_PRTTSYN_CTL1_TSYNTYPE_V2  (0x2 << \
                                        I40E_PRTTSYN_CTL1_TSYNTYPE_SHIFT)
-#define I40E_PTP_TX_TIMEOUT  (HZ * 15)
 
 /**
  * i40e_ptp_read - Read the PHC time from the device
@@ -216,40 +215,6 @@ static int i40e_ptp_settime(struct ptp_clock_info *ptp,
        return 0;
 }
 
-/**
- * i40e_ptp_tx_work
- * @work: pointer to work struct
- *
- * This work function polls the PRTTSYN_STAT_0.TXTIME bit to determine when a
- * Tx timestamp event has occurred, in order to pass the Tx timestamp value up
- * the stack in the skb.
- */
-static void i40e_ptp_tx_work(struct work_struct *work)
-{
-       struct i40e_pf *pf = container_of(work, struct i40e_pf,
-                                         ptp_tx_work);
-       struct i40e_hw *hw = &pf->hw;
-       u32 prttsyn_stat_0;
-
-       if (!pf->ptp_tx_skb)
-               return;
-
-       if (time_is_before_jiffies(pf->ptp_tx_start +
-                                  I40E_PTP_TX_TIMEOUT)) {
-               dev_kfree_skb_any(pf->ptp_tx_skb);
-               pf->ptp_tx_skb = NULL;
-               pf->tx_hwtstamp_timeouts++;
-               dev_warn(&pf->pdev->dev, "clearing Tx timestamp hang\n");
-               return;
-       }
-
-       prttsyn_stat_0 = rd32(hw, I40E_PRTTSYN_STAT_0);
-       if (prttsyn_stat_0 & I40E_PRTTSYN_STAT_0_TXTIME_MASK)
-               i40e_ptp_tx_hwtstamp(pf);
-       else
-               schedule_work(&pf->ptp_tx_work);
-}
-
 /**
  * i40e_ptp_enable - Enable/disable ancillary features of the PHC subsystem
  * @ptp: The PTP clock structure
@@ -608,7 +573,6 @@ void i40e_ptp_init(struct i40e_pf *pf)
                u32 regval;
 
                spin_lock_init(&pf->tmreg_lock);
-               INIT_WORK(&pf->ptp_tx_work, i40e_ptp_tx_work);
 
                dev_info(&pf->pdev->dev, "%s: added PHC on %s\n", __func__,
                         netdev->name);
@@ -647,7 +611,6 @@ void i40e_ptp_stop(struct i40e_pf *pf)
        pf->ptp_tx = false;
        pf->ptp_rx = false;
 
-       cancel_work_sync(&pf->ptp_tx_work);
        if (pf->ptp_tx_skb) {
                dev_kfree_skb_any(pf->ptp_tx_skb);
                pf->ptp_tx_skb = NULL;
index 1d40f425acf1833657902dce6dd7206b5493e7eb..947de98500f33a4162c441967f6a6bacdd0ab148 100644 (file)
 #define I40E_PFINT_ICR0_GPIO_MASK (0x1 << I40E_PFINT_ICR0_GPIO_SHIFT)
 #define I40E_PFINT_ICR0_TIMESYNC_SHIFT 23
 #define I40E_PFINT_ICR0_TIMESYNC_MASK (0x1 << I40E_PFINT_ICR0_TIMESYNC_SHIFT)
-#define I40E_PFINT_ICR0_STORM_DETECT_SHIFT 24
-#define I40E_PFINT_ICR0_STORM_DETECT_MASK (0x1 << I40E_PFINT_ICR0_STORM_DETECT_SHIFT)
 #define I40E_PFINT_ICR0_LINK_STAT_CHANGE_SHIFT 25
 #define I40E_PFINT_ICR0_LINK_STAT_CHANGE_MASK (0x1 << I40E_PFINT_ICR0_LINK_STAT_CHANGE_SHIFT)
 #define I40E_PFINT_ICR0_HMC_ERR_SHIFT 26
 #define I40E_PFINT_ICR0_ENA_GPIO_MASK (0x1 << I40E_PFINT_ICR0_ENA_GPIO_SHIFT)
 #define I40E_PFINT_ICR0_ENA_TIMESYNC_SHIFT 23
 #define I40E_PFINT_ICR0_ENA_TIMESYNC_MASK (0x1 << I40E_PFINT_ICR0_ENA_TIMESYNC_SHIFT)
-#define I40E_PFINT_ICR0_ENA_STORM_DETECT_SHIFT 24
-#define I40E_PFINT_ICR0_ENA_STORM_DETECT_MASK (0x1 << I40E_PFINT_ICR0_ENA_STORM_DETECT_SHIFT)
 #define I40E_PFINT_ICR0_ENA_LINK_STAT_CHANGE_SHIFT 25
 #define I40E_PFINT_ICR0_ENA_LINK_STAT_CHANGE_MASK (0x1 << I40E_PFINT_ICR0_ENA_LINK_STAT_CHANGE_SHIFT)
 #define I40E_PFINT_ICR0_ENA_HMC_ERR_SHIFT 26
 #define I40E_GLLAN_TSOMSK_M 0x000442DC
 #define I40E_GLLAN_TSOMSK_M_TCPMSKM_SHIFT 0
 #define I40E_GLLAN_TSOMSK_M_TCPMSKM_MASK (0xFFF << I40E_GLLAN_TSOMSK_M_TCPMSKM_SHIFT)
+#define I40E_GLLAN_TXPRE_QDIS(_i) (0x000E6500 + ((_i) * 4)) /* i=0..11 */
+#define I40E_GLLAN_TXPRE_QDIS_QINDX_SHIFT 0
+#define I40E_GLLAN_TXPRE_QDIS_QINDX_MASK (0x7FF << I40E_GLLAN_TXPRE_QDIS_QINDX_SHIFT)
+#define I40E_GLLAN_TXPRE_QDIS_SET_QDIS_SHIFT 30
+#define I40E_GLLAN_TXPRE_QDIS_SET_QDIS_MASK (0x1 << I40E_GLLAN_TXPRE_QDIS_SET_QDIS_SHIFT)
+#define I40E_GLLAN_TXPRE_QDIS_CLEAR_QDIS_SHIFT 31
+#define I40E_GLLAN_TXPRE_QDIS_CLEAR_QDIS_MASK (0x1 << I40E_GLLAN_TXPRE_QDIS_CLEAR_QDIS_SHIFT)
+
 #define I40E_PFLAN_QALLOC 0x001C0400
 #define I40E_PFLAN_QALLOC_FIRSTQ_SHIFT 0
 #define I40E_PFLAN_QALLOC_FIRSTQ_MASK (0x7FF << I40E_PFLAN_QALLOC_FIRSTQ_SHIFT)
index 9478ddc66caf8d71cfecd6c322e635d247e3fd95..e49f31dbd5d87cfdd4268c8837053746ef27c3dc 100644 (file)
@@ -24,6 +24,7 @@
  *
  ******************************************************************************/
 
+#include <linux/prefetch.h>
 #include "i40e.h"
 #include "i40e_prototype.h"
 
@@ -61,7 +62,7 @@ int i40e_program_fdir_filter(struct i40e_fdir_filter *fdir_data, u8 *raw_packet,
 
        /* find existing FDIR VSI */
        vsi = NULL;
-       for (i = 0; i < pf->hw.func_caps.num_vsis; i++)
+       for (i = 0; i < pf->num_alloc_vsi; i++)
                if (pf->vsi[i] && pf->vsi[i]->type == I40E_VSI_FDIR)
                        vsi = pf->vsi[i];
        if (!vsi)
@@ -120,7 +121,7 @@ int i40e_program_fdir_filter(struct i40e_fdir_filter *fdir_data, u8 *raw_packet,
                dcc |= I40E_TXD_FLTR_QW1_CNT_ENA_MASK;
                dcc |= ((u32)fdir_data->cnt_index <<
                        I40E_TXD_FLTR_QW1_CNTINDEX_SHIFT) &
-                      I40E_TXD_FLTR_QW1_CNTINDEX_MASK;
+                       I40E_TXD_FLTR_QW1_CNTINDEX_MASK;
        }
 
        fdir_desc->dtype_cmd_cntindex = cpu_to_le32(dcc);
@@ -183,7 +184,6 @@ static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi,
        struct iphdr *ip;
        bool err = false;
        int ret;
-       int i;
        static char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0,
                0x45, 0, 0, 0x1c, 0, 0, 0x40, 0, 0x40, 0x11, 0, 0, 0, 0, 0, 0,
                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
@@ -199,21 +199,17 @@ static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi,
        ip->saddr = fd_data->src_ip[0];
        udp->source = fd_data->src_port;
 
-       for (i = I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP;
-            i <= I40E_FILTER_PCTYPE_NONF_IPV4_UDP; i++) {
-               fd_data->pctype = i;
-               ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add);
-
-               if (ret) {
-                       dev_info(&pf->pdev->dev,
-                                "Filter command send failed for PCTYPE %d (ret = %d)\n",
-                                fd_data->pctype, ret);
-                       err = true;
-               } else {
-                       dev_info(&pf->pdev->dev,
-                                "Filter OK for PCTYPE %d (ret = %d)\n",
-                                fd_data->pctype, ret);
-               }
+       fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
+       ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add);
+       if (ret) {
+               dev_info(&pf->pdev->dev,
+                        "Filter command send failed for PCTYPE %d (ret = %d)\n",
+                        fd_data->pctype, ret);
+               err = true;
+       } else {
+               dev_info(&pf->pdev->dev,
+                        "Filter OK for PCTYPE %d (ret = %d)\n",
+                        fd_data->pctype, ret);
        }
 
        return err ? -EOPNOTSUPP : 0;
@@ -262,7 +258,7 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi,
                }
        }
 
-       fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN;
+       fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP;
        ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add);
 
        if (ret) {
@@ -455,22 +451,20 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring,
 
                /* filter programming failed most likely due to table full */
                fcnt_prog = i40e_get_current_fd_count(pf);
-               fcnt_avail = pf->hw.fdir_shared_filter_count +
-                                                      pf->fdir_pf_filter_count;
-
+               fcnt_avail = i40e_get_fd_cnt_all(pf);
                /* If ATR is running fcnt_prog can quickly change,
                 * if we are very close to full, it makes sense to disable
                 * FD ATR/SB and then re-enable it when there is room.
                 */
                if (fcnt_prog >= (fcnt_avail - I40E_FDIR_BUFFER_FULL_MARGIN)) {
                        /* Turn off ATR first */
-                       if (pf->flags | I40E_FLAG_FD_ATR_ENABLED) {
+                       if (pf->flags & I40E_FLAG_FD_ATR_ENABLED) {
                                pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED;
                                dev_warn(&pdev->dev, "FD filter space full, ATR for further flows will be turned off\n");
                                pf->auto_disable_flags |=
                                                       I40E_FLAG_FD_ATR_ENABLED;
                                pf->flags |= I40E_FLAG_FDIR_REQUIRES_REINIT;
-                       } else if (pf->flags | I40E_FLAG_FD_SB_ENABLED) {
+                       } else if (pf->flags & I40E_FLAG_FD_SB_ENABLED) {
                                pf->flags &= ~I40E_FLAG_FD_SB_ENABLED;
                                dev_warn(&pdev->dev, "FD filter space full, new ntuple rules will not be added\n");
                                pf->auto_disable_flags |=
@@ -1199,10 +1193,12 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
                                    u32 rx_error,
                                    u16 rx_ptype)
 {
+       struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(rx_ptype);
+       bool ipv4 = false, ipv6 = false;
        bool ipv4_tunnel, ipv6_tunnel;
        __wsum rx_udp_csum;
-       __sum16 csum;
        struct iphdr *iph;
+       __sum16 csum;
 
        ipv4_tunnel = (rx_ptype > I40E_RX_PTYPE_GRENAT4_MAC_PAY3) &&
                      (rx_ptype < I40E_RX_PTYPE_GRENAT4_MACVLAN_IPV6_ICMP_PAY4);
@@ -1213,29 +1209,57 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
        skb->ip_summed = CHECKSUM_NONE;
 
        /* Rx csum enabled and ip headers found? */
-       if (!(vsi->netdev->features & NETIF_F_RXCSUM &&
-             rx_status & (1 << I40E_RX_DESC_STATUS_L3L4P_SHIFT)))
+       if (!(vsi->netdev->features & NETIF_F_RXCSUM))
+               return;
+
+       /* did the hardware decode the packet and checksum? */
+       if (!(rx_status & (1 << I40E_RX_DESC_STATUS_L3L4P_SHIFT)))
+               return;
+
+       /* both known and outer_ip must be set for the below code to work */
+       if (!(decoded.known && decoded.outer_ip))
                return;
 
+       if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP &&
+           decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV4)
+               ipv4 = true;
+       else if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP &&
+                decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV6)
+               ipv6 = true;
+
+       if (ipv4 &&
+           (rx_error & ((1 << I40E_RX_DESC_ERROR_IPE_SHIFT) |
+                        (1 << I40E_RX_DESC_ERROR_EIPE_SHIFT))))
+               goto checksum_fail;
+
        /* likely incorrect csum if alternate IP extension headers found */
-       if (rx_status & (1 << I40E_RX_DESC_STATUS_IPV6EXADD_SHIFT))
+       if (ipv6 &&
+           decoded.inner_prot == I40E_RX_PTYPE_INNER_PROT_TCP &&
+           rx_error & (1 << I40E_RX_DESC_ERROR_L4E_SHIFT) &&
+           rx_status & (1 << I40E_RX_DESC_STATUS_IPV6EXADD_SHIFT))
+               /* don't increment checksum err here, non-fatal err */
                return;
 
-       /* IP or L4 or outmost IP checksum error */
-       if (rx_error & ((1 << I40E_RX_DESC_ERROR_IPE_SHIFT) |
-                       (1 << I40E_RX_DESC_ERROR_L4E_SHIFT) |
-                       (1 << I40E_RX_DESC_ERROR_EIPE_SHIFT))) {
-               vsi->back->hw_csum_rx_error++;
+       /* there was some L4 error, count error and punt packet to the stack */
+       if (rx_error & (1 << I40E_RX_DESC_ERROR_L4E_SHIFT))
+               goto checksum_fail;
+
+       /* handle packets that were not able to be checksummed due
+        * to arrival speed, in this case the stack can compute
+        * the csum.
+        */
+       if (rx_error & (1 << I40E_RX_DESC_ERROR_PPRS_SHIFT))
                return;
-       }
 
+       /* If VXLAN traffic has an outer UDPv4 checksum we need to check
+        * it in the driver, hardware does not do it for us.
+        * Since L3L4P bit was set we assume a valid IHL value (>=5)
+        * so the total length of IPv4 header is IHL*4 bytes
+        * The UDP_0 bit *may* bet set if the *inner* header is UDP
+        */
        if (ipv4_tunnel &&
+           (decoded.inner_prot != I40E_RX_PTYPE_INNER_PROT_UDP) &&
            !(rx_status & (1 << I40E_RX_DESC_STATUS_UDP_0_SHIFT))) {
-               /* If VXLAN traffic has an outer UDPv4 checksum we need to check
-                * it in the driver, hardware does not do it for us.
-                * Since L3L4P bit was set we assume a valid IHL value (>=5)
-                * so the total length of IPv4 header is IHL*4 bytes
-                */
                skb->transport_header = skb->mac_header +
                                        sizeof(struct ethhdr) +
                                        (ip_hdr(skb)->ihl * 4);
@@ -1252,13 +1276,16 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
                                (skb->len - skb_transport_offset(skb)),
                                IPPROTO_UDP, rx_udp_csum);
 
-               if (udp_hdr(skb)->check != csum) {
-                       vsi->back->hw_csum_rx_error++;
-                       return;
-               }
+               if (udp_hdr(skb)->check != csum)
+                       goto checksum_fail;
        }
 
        skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+       return;
+
+checksum_fail:
+       vsi->back->hw_csum_rx_error++;
 }
 
 /**
@@ -1435,6 +1462,9 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
                /* ERR_MASK will only have valid bits if EOP set */
                if (unlikely(rx_error & (1 << I40E_RX_DESC_ERROR_RXE_SHIFT))) {
                        dev_kfree_skb_any(skb);
+                       /* TODO: shouldn't we increment a counter indicating the
+                        * drop?
+                        */
                        goto next_desc;
                }
 
@@ -1665,6 +1695,11 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
        dtype_cmd |= I40E_FILTER_PROGRAM_DESC_FD_STATUS_FD_ID <<
                     I40E_TXD_FLTR_QW1_FD_STATUS_SHIFT;
 
+       dtype_cmd |= I40E_TXD_FLTR_QW1_CNT_ENA_MASK;
+       dtype_cmd |=
+               ((u32)pf->fd_atr_cnt_idx << I40E_TXD_FLTR_QW1_CNTINDEX_SHIFT) &
+               I40E_TXD_FLTR_QW1_CNTINDEX_MASK;
+
        fdir_desc->qindex_flex_ptype_vsi = cpu_to_le32(flex_ptype);
        fdir_desc->dtype_cmd_cntindex = cpu_to_le32(dtype_cmd);
 }
@@ -1825,9 +1860,6 @@ static int i40e_tsyn(struct i40e_ring *tx_ring, struct sk_buff *skb,
        *cd_type_cmd_tso_mss |= (u64)I40E_TX_CTX_DESC_TSYN <<
                                I40E_TXD_CTX_QW1_CMD_SHIFT;
 
-       pf->ptp_tx_start = jiffies;
-       schedule_work(&pf->ptp_tx_work);
-
        return 1;
 }
 
@@ -2179,9 +2211,7 @@ static int i40e_maybe_stop_tx(struct i40e_ring *tx_ring, int size)
 static int i40e_xmit_descriptor_count(struct sk_buff *skb,
                                      struct i40e_ring *tx_ring)
 {
-#if PAGE_SIZE > I40E_MAX_DATA_PER_TXD
        unsigned int f;
-#endif
        int count = 0;
 
        /* need: 1 descriptor per page * PAGE_SIZE/I40E_MAX_DATA_PER_TXD,
@@ -2190,12 +2220,9 @@ static int i40e_xmit_descriptor_count(struct sk_buff *skb,
         *       + 1 desc for context descriptor,
         * otherwise try next time
         */
-#if PAGE_SIZE > I40E_MAX_DATA_PER_TXD
        for (f = 0; f < skb_shinfo(skb)->nr_frags; f++)
                count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size);
-#else
-       count += skb_shinfo(skb)->nr_frags;
-#endif
+
        count += TXD_USE_COUNT(skb_headlen(skb));
        if (i40e_maybe_stop_tx(tx_ring, count + 4 + 1)) {
                tx_ring->tx_stats.tx_busy++;
index d5349698e513b511a8ac9e5e6c798d888cdce409..0277894fe1c46db030045d819484ccc6f2cb6f72 100644 (file)
@@ -27,7 +27,7 @@
 #ifndef _I40E_TXRX_H_
 #define _I40E_TXRX_H_
 
-/* Interrupt Throttling and Rate Limiting (storm control) Goodies */
+/* Interrupt Throttling and Rate Limiting Goodies */
 
 #define I40E_MAX_ITR               0x0FF0  /* reg uses 2 usec resolution */
 #define I40E_MIN_ITR               0x0004  /* reg uses 2 usec resolution */
@@ -69,16 +69,11 @@ enum i40e_dyn_idx_t {
 
 /* Supported RSS offloads */
 #define I40E_DEFAULT_RSS_HENA ( \
-       ((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) | \
-       ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_SCTP) | \
-       ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER) | \
        ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4) | \
-       ((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | \
-       ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP) | \
@@ -122,11 +117,11 @@ enum i40e_dyn_idx_t {
 #define i40e_rx_desc i40e_32byte_rx_desc
 
 #define I40E_MIN_TX_LEN                17
-#define I40E_MAX_DATA_PER_TXD  16383   /* aka 16kB - 1 */
+#define I40E_MAX_DATA_PER_TXD  8192
 
 /* Tx Descriptors needed, worst case */
 #define TXD_USE_COUNT(S) DIV_ROUND_UP((S), I40E_MAX_DATA_PER_TXD)
-#define DESC_NEEDED ((MAX_SKB_FRAGS * TXD_USE_COUNT(PAGE_SIZE)) + 4)
+#define DESC_NEEDED (MAX_SKB_FRAGS + 4)
 
 #define I40E_TX_FLAGS_CSUM             (u32)(1)
 #define I40E_TX_FLAGS_HW_VLAN          (u32)(1 << 1)
@@ -184,7 +179,6 @@ enum i40e_ring_state_t {
        __I40E_TX_DETECT_HANG,
        __I40E_HANG_CHECK_ARMED,
        __I40E_RX_PS_ENABLED,
-       __I40E_RX_LRO_ENABLED,
        __I40E_RX_16BYTE_DESC_ENABLED,
 };
 
@@ -200,12 +194,6 @@ enum i40e_ring_state_t {
        set_bit(__I40E_TX_DETECT_HANG, &(ring)->state)
 #define clear_check_for_tx_hang(ring) \
        clear_bit(__I40E_TX_DETECT_HANG, &(ring)->state)
-#define ring_is_lro_enabled(ring) \
-       test_bit(__I40E_RX_LRO_ENABLED, &(ring)->state)
-#define set_ring_lro_enabled(ring) \
-       set_bit(__I40E_RX_LRO_ENABLED, &(ring)->state)
-#define clear_ring_lro_enabled(ring) \
-       clear_bit(__I40E_RX_LRO_ENABLED, &(ring)->state)
 #define ring_is_16byte_desc_enabled(ring) \
        test_bit(__I40E_RX_16BYTE_DESC_ENABLED, &(ring)->state)
 #define set_ring_16byte_desc_enabled(ring) \
index 71a968fe557f33e12f6feb81d416f4f5a55946fb..9d39ff23c5fbfbbed52ec0ba5437e585fc3a0c68 100644 (file)
 
 /* Device IDs */
 #define I40E_DEV_ID_SFP_XL710          0x1572
-#define I40E_DEV_ID_SFP_X710           0x1573
 #define I40E_DEV_ID_QEMU               0x1574
 #define I40E_DEV_ID_KX_A               0x157F
 #define I40E_DEV_ID_KX_B               0x1580
 #define I40E_DEV_ID_KX_C               0x1581
-#define I40E_DEV_ID_KX_D               0x1582
 #define I40E_DEV_ID_QSFP_A             0x1583
 #define I40E_DEV_ID_QSFP_B             0x1584
 #define I40E_DEV_ID_QSFP_C             0x1585
@@ -60,8 +58,8 @@
 /* Max default timeout in ms, */
 #define I40E_MAX_NVM_TIMEOUT           18000
 
-/* Switch from mc to the 2usec global time (this is the GTIME resolution) */
-#define I40E_MS_TO_GTIME(time)         (((time) * 1000) / 2)
+/* Switch from ms to the 1usec global time (this is the GTIME resolution) */
+#define I40E_MS_TO_GTIME(time)         ((time) * 1000)
 
 /* forward declaration */
 struct i40e_hw;
@@ -167,6 +165,9 @@ struct i40e_link_status {
        u8 loopback;
        /* is Link Status Event notification to SW enabled */
        bool lse_enable;
+       u16 max_frame_size;
+       bool crc_enable;
+       u8 pacing;
 };
 
 struct i40e_phy_info {
@@ -409,6 +410,7 @@ struct i40e_driver_version {
        u8 minor_version;
        u8 build_version;
        u8 subbuild_version;
+       u8 driver_string[32];
 };
 
 /* RX Descriptors */
@@ -488,9 +490,6 @@ union i40e_32byte_rx_desc {
        } wb;  /* writeback */
 };
 
-#define I40E_RXD_QW1_STATUS_SHIFT      0
-#define I40E_RXD_QW1_STATUS_MASK       (0x7FFFUL << I40E_RXD_QW1_STATUS_SHIFT)
-
 enum i40e_rx_desc_status_bits {
        /* Note: These are predefined bit offsets */
        I40E_RX_DESC_STATUS_DD_SHIFT            = 0,
@@ -507,9 +506,14 @@ enum i40e_rx_desc_status_bits {
        I40E_RX_DESC_STATUS_LPBK_SHIFT          = 14,
        I40E_RX_DESC_STATUS_IPV6EXADD_SHIFT     = 15,
        I40E_RX_DESC_STATUS_RESERVED_SHIFT      = 16, /* 2 BITS */
-       I40E_RX_DESC_STATUS_UDP_0_SHIFT         = 18
+       I40E_RX_DESC_STATUS_UDP_0_SHIFT         = 18,
+       I40E_RX_DESC_STATUS_LAST /* this entry must be last!!! */
 };
 
+#define I40E_RXD_QW1_STATUS_SHIFT      0
+#define I40E_RXD_QW1_STATUS_MASK       (((1 << I40E_RX_DESC_STATUS_LAST) - 1) \
+                                        << I40E_RXD_QW1_STATUS_SHIFT)
+
 #define I40E_RXD_QW1_STATUS_TSYNINDX_SHIFT   I40E_RX_DESC_STATUS_TSYNINDX_SHIFT
 #define I40E_RXD_QW1_STATUS_TSYNINDX_MASK      (0x3UL << \
                                             I40E_RXD_QW1_STATUS_TSYNINDX_SHIFT)
@@ -537,7 +541,8 @@ enum i40e_rx_desc_error_bits {
        I40E_RX_DESC_ERROR_IPE_SHIFT            = 3,
        I40E_RX_DESC_ERROR_L4E_SHIFT            = 4,
        I40E_RX_DESC_ERROR_EIPE_SHIFT           = 5,
-       I40E_RX_DESC_ERROR_OVERSIZE_SHIFT       = 6
+       I40E_RX_DESC_ERROR_OVERSIZE_SHIFT       = 6,
+       I40E_RX_DESC_ERROR_PPRS_SHIFT           = 7
 };
 
 enum i40e_rx_desc_error_l3l4e_fcoe_masks {
@@ -658,7 +663,6 @@ enum i40e_rx_desc_ext_status_bits {
        I40E_RX_DESC_EXT_STATUS_L2TAG3P_SHIFT   = 1,
        I40E_RX_DESC_EXT_STATUS_FLEXBL_SHIFT    = 2, /* 2 BITS */
        I40E_RX_DESC_EXT_STATUS_FLEXBH_SHIFT    = 4, /* 2 BITS */
-       I40E_RX_DESC_EXT_STATUS_FTYPE_SHIFT     = 6, /* 3 BITS */
        I40E_RX_DESC_EXT_STATUS_FDLONGB_SHIFT   = 9,
        I40E_RX_DESC_EXT_STATUS_FCOELONGB_SHIFT = 10,
        I40E_RX_DESC_EXT_STATUS_PELONGB_SHIFT   = 11,
@@ -862,18 +866,14 @@ struct i40e_filter_program_desc {
 
 /* Packet Classifier Types for filters */
 enum i40e_filter_pctype {
-       /* Note: Values 0-28 are reserved for future use */
-       I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP        = 29,
-       I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP      = 30,
+       /* Note: Values 0-30 are reserved for future use */
        I40E_FILTER_PCTYPE_NONF_IPV4_UDP                = 31,
-       I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN            = 32,
+       /* Note: Value 32 is reserved for future use */
        I40E_FILTER_PCTYPE_NONF_IPV4_TCP                = 33,
        I40E_FILTER_PCTYPE_NONF_IPV4_SCTP               = 34,
        I40E_FILTER_PCTYPE_NONF_IPV4_OTHER              = 35,
        I40E_FILTER_PCTYPE_FRAG_IPV4                    = 36,
-       /* Note: Values 37-38 are reserved for future use */
-       I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP        = 39,
-       I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP      = 40,
+       /* Note: Values 37-40 are reserved for future use */
        I40E_FILTER_PCTYPE_NONF_IPV6_UDP                = 41,
        I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN            = 42,
        I40E_FILTER_PCTYPE_NONF_IPV6_TCP                = 43,
@@ -955,6 +955,16 @@ struct i40e_vsi_context {
        struct i40e_aqc_vsi_properties_data info;
 };
 
+struct i40e_veb_context {
+       u16 seid;
+       u16 uplink_seid;
+       u16 veb_number;
+       u16 vebs_allocated;
+       u16 vebs_unallocated;
+       u16 flags;
+       struct i40e_aqc_get_veb_parameters_completion info;
+};
+
 /* Statistics collected by each port, VSI, VEB, and S-channel */
 struct i40e_eth_stats {
        u64 rx_bytes;                   /* gorc */
@@ -962,8 +972,6 @@ struct i40e_eth_stats {
        u64 rx_multicast;               /* mprc */
        u64 rx_broadcast;               /* bprc */
        u64 rx_discards;                /* rdpc */
-       u64 rx_errors;                  /* repc */
-       u64 rx_missed;                  /* rmpc */
        u64 rx_unknown_protocol;        /* rupp */
        u64 tx_bytes;                   /* gotc */
        u64 tx_unicast;                 /* uptc */
@@ -1015,9 +1023,12 @@ struct i40e_hw_port_stats {
        u64 tx_size_big;                /* ptc9522 */
        u64 mac_short_packet_dropped;   /* mspdc */
        u64 checksum_error;             /* xec */
+       /* flow director stats */
+       u64 fd_atr_match;
+       u64 fd_sb_match;
        /* EEE LPI */
-       bool tx_lpi_status;
-       bool rx_lpi_status;
+       u32 tx_lpi_status;
+       u32 rx_lpi_status;
        u64 tx_lpi_count;               /* etlpic */
        u64 rx_lpi_count;               /* erlpic */
 };
index 22a1b69cd6464ff072248b59e6acf421da0215f0..70951d2edcad9f04a9cc4bac25701dc61a4c4484 100644 (file)
@@ -341,10 +341,6 @@ struct i40e_virtchnl_pf_event {
        int severity;
 };
 
-/* The following are TBD, not necessary for LAN functionality.
- * I40E_VIRTCHNL_OP_FCOE
- */
-
 /* VF reset states - these are written into the RSTAT register:
  * I40E_VFGEN_RSTAT1 on the PF
  * I40E_VFGEN_RSTAT on the VF
index 02c11a7f7d29e80c533b48894e5ee0b5619a9c91..f5b9d20625736b5dc451ac32dc25cc38d7a0d99c 100644 (file)
 
 /***********************misc routines*****************************/
 
+/**
+ * i40e_vc_disable_vf
+ * @pf: pointer to the pf info
+ * @vf: pointer to the vf info
+ *
+ * Disable the VF through a SW reset
+ **/
+static inline void i40e_vc_disable_vf(struct i40e_pf *pf, struct i40e_vf *vf)
+{
+       struct i40e_hw *hw = &pf->hw;
+       u32 reg;
+
+       reg = rd32(hw, I40E_VPGEN_VFRTRIG(vf->vf_id));
+       reg |= I40E_VPGEN_VFRTRIG_VFSWR_MASK;
+       wr32(hw, I40E_VPGEN_VFRTRIG(vf->vf_id), reg);
+       i40e_flush(hw);
+}
+
 /**
  * i40e_vc_isvalid_vsi_id
  * @vf: pointer to the vf info
@@ -230,9 +248,8 @@ static int i40e_config_vsi_tx_queue(struct i40e_vf *vf, u16 vsi_idx,
        tx_ctx.qlen = info->ring_len;
        tx_ctx.rdylist = le16_to_cpu(pf->vsi[vsi_idx]->info.qs_handle[0]);
        tx_ctx.rdylist_act = 0;
-       tx_ctx.head_wb_ena = 1;
-       tx_ctx.head_wb_addr = info->dma_ring_addr +
-                             (info->ring_len * sizeof(struct i40e_tx_desc));
+       tx_ctx.head_wb_ena = info->headwb_enabled;
+       tx_ctx.head_wb_addr = info->dma_headwb_addr;
 
        /* clear the context in the HMC */
        ret = i40e_clear_lan_tx_queue_context(hw, pf_queue_id);
@@ -336,6 +353,7 @@ static int i40e_config_vsi_rx_queue(struct i40e_vf *vf, u16 vsi_idx,
        rx_ctx.tphhead_ena = 1;
        rx_ctx.lrxqthresh = 2;
        rx_ctx.crcstrip = 1;
+       rx_ctx.prefena = 1;
 
        /* clear the context in the HMC */
        ret = i40e_clear_lan_rx_queue_context(hw, pf_queue_id);
@@ -416,6 +434,15 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type)
        if (ret)
                dev_err(&pf->pdev->dev, "Unable to program ucast filters\n");
 
+       /* Set VF bandwidth if specified */
+       if (vf->tx_rate) {
+               ret = i40e_aq_config_vsi_bw_limit(&pf->hw, vsi->seid,
+                                                 vf->tx_rate / 50, 0, NULL);
+               if (ret)
+                       dev_err(&pf->pdev->dev, "Unable to set tx rate, VF %d, error code %d.\n",
+                               vf->vf_id, ret);
+       }
+
 error_alloc_vsi_res:
        return ret;
 }
@@ -815,6 +842,10 @@ void i40e_free_vfs(struct i40e_pf *pf)
        kfree(pf->vf);
        pf->vf = NULL;
 
+       /* This check is for when the driver is unloaded while VFs are
+        * assigned. Setting the number of VFs to 0 through sysfs is caught
+        * before this function ever gets called.
+        */
        if (!i40e_vfs_are_assigned(pf)) {
                pci_disable_sriov(pf->pdev);
                /* Acknowledge VFLR for all VFS. Without this, VFs will fail to
@@ -867,6 +898,7 @@ int i40e_alloc_vfs(struct i40e_pf *pf, u16 num_alloc_vfs)
                ret = -ENOMEM;
                goto err_alloc;
        }
+       pf->vf = vfs;
 
        /* apply default profile */
        for (i = 0; i < num_alloc_vfs; i++) {
@@ -876,13 +908,13 @@ int i40e_alloc_vfs(struct i40e_pf *pf, u16 num_alloc_vfs)
 
                /* assign default capabilities */
                set_bit(I40E_VIRTCHNL_VF_CAP_L2, &vfs[i].vf_caps);
+               vfs[i].spoofchk = true;
                /* vf resources get allocated during reset */
                i40e_reset_vf(&vfs[i], false);
 
                /* enable vf vplan_qtable mappings */
                i40e_enable_vf_mappings(&vfs[i]);
        }
-       pf->vf = vfs;
        pf->num_alloc_vfs = num_alloc_vfs;
 
        i40e_enable_pf_switch_lb(pf);
@@ -951,7 +983,12 @@ int i40e_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
        if (num_vfs)
                return i40e_pci_sriov_enable(pdev, num_vfs);
 
-       i40e_free_vfs(pf);
+       if (!i40e_vfs_are_assigned(pf)) {
+               i40e_free_vfs(pf);
+       } else {
+               dev_warn(&pdev->dev, "Unable to free VFs because some are assigned to VMs.\n");
+               return -EINVAL;
+       }
        return 0;
 }
 
@@ -2022,16 +2059,14 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
        }
 
        /* delete the temporary mac address */
-       i40e_del_filter(vsi, vf->default_lan_addr.addr, 0, true, false);
+       i40e_del_filter(vsi, vf->default_lan_addr.addr, vf->port_vlan_id,
+                       true, false);
 
-       /* add the new mac address */
-       f = i40e_add_filter(vsi, mac, 0, true, false);
-       if (!f) {
-               dev_err(&pf->pdev->dev,
-                       "Unable to add VF ucast filter\n");
-               ret = -ENOMEM;
-               goto error_param;
-       }
+       /* Delete all the filters for this VSI - we're going to kill it
+        * anyway.
+        */
+       list_for_each_entry(f, &vsi->mac_filter_list, list)
+               i40e_del_filter(vsi, f->macaddr, f->vlan, true, false);
 
        dev_info(&pf->pdev->dev, "Setting MAC %pM on VF %d\n", mac, vf_id);
        /* program mac filter */
@@ -2040,7 +2075,7 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
                ret = -EIO;
                goto error_param;
        }
-       memcpy(vf->default_lan_addr.addr, mac, ETH_ALEN);
+       ether_addr_copy(vf->default_lan_addr.addr, mac);
        vf->pf_set_mac = true;
        dev_info(&pf->pdev->dev, "Reload the VF driver to make this change effective.\n");
        ret = 0;
@@ -2088,18 +2123,28 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev,
                goto error_pvid;
        }
 
-       if (vsi->info.pvid == 0 && i40e_is_vsi_in_vlan(vsi))
+       if (vsi->info.pvid == 0 && i40e_is_vsi_in_vlan(vsi)) {
                dev_err(&pf->pdev->dev,
                        "VF %d has already configured VLAN filters and the administrator is requesting a port VLAN override.\nPlease unload and reload the VF driver for this change to take effect.\n",
                        vf_id);
+               /* Administrator Error - knock the VF offline until he does
+                * the right thing by reconfiguring his network correctly
+                * and then reloading the VF driver.
+                */
+               i40e_vc_disable_vf(pf, vf);
+       }
 
        /* Check for condition where there was already a port VLAN ID
         * filter set and now it is being deleted by setting it to zero.
+        * Additionally check for the condition where there was a port
+        * VLAN but now there is a new and different port VLAN being set.
         * Before deleting all the old VLAN filters we must add new ones
         * with -1 (I40E_VLAN_ANY) or otherwise we're left with all our
         * MAC addresses deleted.
         */
-       if (!(vlan_id || qos) && vsi->info.pvid)
+       if ((!(vlan_id || qos) ||
+           (vlan_id | qos) != le16_to_cpu(vsi->info.pvid)) &&
+           vsi->info.pvid)
                ret = i40e_vsi_add_vlan(vsi, I40E_VLAN_ANY);
 
        if (vsi->info.pvid) {
@@ -2150,6 +2195,8 @@ error_pvid:
        return ret;
 }
 
+#define I40E_BW_CREDIT_DIVISOR 50     /* 50Mbps per BW credit */
+#define I40E_MAX_BW_INACTIVE_ACCUM 4  /* device can accumulate 4 credits max */
 /**
  * i40e_ndo_set_vf_bw
  * @netdev: network interface device structure
@@ -2158,9 +2205,76 @@ error_pvid:
  *
  * configure vf tx rate
  **/
-int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int tx_rate)
+int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate,
+                      int max_tx_rate)
 {
-       return -EOPNOTSUPP;
+       struct i40e_netdev_priv *np = netdev_priv(netdev);
+       struct i40e_pf *pf = np->vsi->back;
+       struct i40e_vsi *vsi;
+       struct i40e_vf *vf;
+       int speed = 0;
+       int ret = 0;
+
+       /* validate the request */
+       if (vf_id >= pf->num_alloc_vfs) {
+               dev_err(&pf->pdev->dev, "Invalid VF Identifier %d.\n", vf_id);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       if (min_tx_rate) {
+               dev_err(&pf->pdev->dev, "Invalid min tx rate (%d) (greater than 0) specified for vf %d.\n",
+                       min_tx_rate, vf_id);
+               return -EINVAL;
+       }
+
+       vf = &(pf->vf[vf_id]);
+       vsi = pf->vsi[vf->lan_vsi_index];
+       if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) {
+               dev_err(&pf->pdev->dev, "Uninitialized VF %d.\n", vf_id);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       switch (pf->hw.phy.link_info.link_speed) {
+       case I40E_LINK_SPEED_40GB:
+               speed = 40000;
+               break;
+       case I40E_LINK_SPEED_10GB:
+               speed = 10000;
+               break;
+       case I40E_LINK_SPEED_1GB:
+               speed = 1000;
+               break;
+       default:
+               break;
+       }
+
+       if (max_tx_rate > speed) {
+               dev_err(&pf->pdev->dev, "Invalid max tx rate %d specified for vf %d.",
+                       max_tx_rate, vf->vf_id);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       if ((max_tx_rate < 50) && (max_tx_rate > 0)) {
+               dev_warn(&pf->pdev->dev, "Setting max Tx rate to minimum usable value of 50Mbps.\n");
+               max_tx_rate = 50;
+       }
+
+       /* Tx rate credits are in values of 50Mbps, 0 is disabled*/
+       ret = i40e_aq_config_vsi_bw_limit(&pf->hw, vsi->seid,
+                                         max_tx_rate / I40E_BW_CREDIT_DIVISOR,
+                                         I40E_MAX_BW_INACTIVE_ACCUM, NULL);
+       if (ret) {
+               dev_err(&pf->pdev->dev, "Unable to set max tx rate, error code %d.\n",
+                       ret);
+               ret = -EIO;
+               goto error;
+       }
+       vf->tx_rate = max_tx_rate;
+error:
+       return ret;
 }
 
 /**
@@ -2200,10 +2314,18 @@ int i40e_ndo_get_vf_config(struct net_device *netdev,
 
        memcpy(&ivi->mac, vf->default_lan_addr.addr, ETH_ALEN);
 
-       ivi->tx_rate = 0;
+       ivi->max_tx_rate = vf->tx_rate;
+       ivi->min_tx_rate = 0;
        ivi->vlan = le16_to_cpu(vsi->info.pvid) & I40E_VLAN_MASK;
        ivi->qos = (le16_to_cpu(vsi->info.pvid) & I40E_PRIORITY_MASK) >>
                   I40E_VLAN_PRIORITY_SHIFT;
+       if (vf->link_forced == false)
+               ivi->linkstate = IFLA_VF_LINK_STATE_AUTO;
+       else if (vf->link_up == true)
+               ivi->linkstate = IFLA_VF_LINK_STATE_ENABLE;
+       else
+               ivi->linkstate = IFLA_VF_LINK_STATE_DISABLE;
+       ivi->spoofchk = vf->spoofchk;
        ret = 0;
 
 error_param:
@@ -2270,3 +2392,50 @@ int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link)
 error_out:
        return ret;
 }
+
+/**
+ * i40e_ndo_set_vf_spoofchk
+ * @netdev: network interface device structure
+ * @vf_id: vf identifier
+ * @enable: flag to enable or disable feature
+ *
+ * Enable or disable VF spoof checking
+ **/
+int i40e_ndo_set_vf_spoofck(struct net_device *netdev, int vf_id, bool enable)
+{
+       struct i40e_netdev_priv *np = netdev_priv(netdev);
+       struct i40e_vsi *vsi = np->vsi;
+       struct i40e_pf *pf = vsi->back;
+       struct i40e_vsi_context ctxt;
+       struct i40e_hw *hw = &pf->hw;
+       struct i40e_vf *vf;
+       int ret = 0;
+
+       /* validate the request */
+       if (vf_id >= pf->num_alloc_vfs) {
+               dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       vf = &(pf->vf[vf_id]);
+
+       if (enable == vf->spoofchk)
+               goto out;
+
+       vf->spoofchk = enable;
+       memset(&ctxt, 0, sizeof(ctxt));
+       ctxt.seid = pf->vsi[vf->lan_vsi_index]->seid;
+       ctxt.pf_num = pf->hw.pf_id;
+       ctxt.info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_SECURITY_VALID);
+       if (enable)
+               ctxt.info.sec_flags |= I40E_AQ_VSI_SEC_FLAG_ENABLE_MAC_CHK;
+       ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL);
+       if (ret) {
+               dev_err(&pf->pdev->dev, "Error %d updating VSI parameters\n",
+                       ret);
+               ret = -EIO;
+       }
+out:
+       return ret;
+}
index 389c47f396d5261d708228f48d8e1bf8ed90e4c5..63e7e0d81ad22754622695dabc1a0475885477ee 100644 (file)
@@ -98,8 +98,10 @@ struct i40e_vf {
 
        unsigned long vf_caps;  /* vf's adv. capabilities */
        unsigned long vf_states;        /* vf's runtime states */
+       unsigned int tx_rate;   /* Tx bandwidth limit in Mbps */
        bool link_forced;
        bool link_up;           /* only valid if vf link is forced */
+       bool spoofchk;
 };
 
 void i40e_free_vfs(struct i40e_pf *pf);
@@ -115,10 +117,12 @@ void i40e_vc_notify_vf_reset(struct i40e_vf *vf);
 int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac);
 int i40e_ndo_set_vf_port_vlan(struct net_device *netdev,
                              int vf_id, u16 vlan_id, u8 qos);
-int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int tx_rate);
+int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate,
+                      int max_tx_rate);
 int i40e_ndo_get_vf_config(struct net_device *netdev,
                           int vf_id, struct ifla_vf_info *ivi);
 int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link);
+int i40e_ndo_set_vf_spoofck(struct net_device *netdev, int vf_id, bool enable);
 
 void i40e_vc_notify_link_state(struct i40e_pf *pf);
 void i40e_vc_notify_reset(struct i40e_pf *pf);
index e09be37a07a8384f41731cfd9e7a744c646e7216..3a423836a565294aa82dadbeb93e4d45619ccd86 100644 (file)
@@ -1,7 +1,7 @@
 ################################################################################
 #
 # Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
-# Copyright(c) 2013 Intel Corporation.
+# Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
 # 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/>.
+#
 # The full GNU General Public License is included in this distribution in
 # the file called "COPYING".
 #
index 5470ce95936ed483abc547f1ef12b1ef30a34721..eb67cce3e8f9a1bed6a0eabfaaeb7067acd703ef 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
 #include "i40e_adminq.h"
 #include "i40e_prototype.h"
 
+/**
+ * i40e_is_nvm_update_op - return true if this is an NVM update operation
+ * @desc: API request descriptor
+ **/
+static inline bool i40e_is_nvm_update_op(struct i40e_aq_desc *desc)
+{
+       return (desc->opcode == i40e_aqc_opc_nvm_erase) ||
+              (desc->opcode == i40e_aqc_opc_nvm_update);
+}
+
 /**
  *  i40e_adminq_init_regs - Initialize AdminQ registers
  *  @hw: pointer to the hardware structure
@@ -276,8 +289,11 @@ static void i40e_free_asq_bufs(struct i40e_hw *hw)
  *
  *  Configure base address and length registers for the transmit queue
  **/
-static void i40e_config_asq_regs(struct i40e_hw *hw)
+static i40e_status i40e_config_asq_regs(struct i40e_hw *hw)
 {
+       i40e_status ret_code = 0;
+       u32 reg = 0;
+
        if (hw->mac.type == I40E_MAC_VF) {
                /* configure the transmit queue */
                wr32(hw, I40E_VF_ATQBAH1,
@@ -286,6 +302,7 @@ static void i40e_config_asq_regs(struct i40e_hw *hw)
                    lower_32_bits(hw->aq.asq.desc_buf.pa));
                wr32(hw, I40E_VF_ATQLEN1, (hw->aq.num_asq_entries |
                                          I40E_VF_ATQLEN1_ATQENABLE_MASK));
+               reg = rd32(hw, I40E_VF_ATQBAL1);
        } else {
                /* configure the transmit queue */
                wr32(hw, I40E_PF_ATQBAH,
@@ -294,7 +311,14 @@ static void i40e_config_asq_regs(struct i40e_hw *hw)
                    lower_32_bits(hw->aq.asq.desc_buf.pa));
                wr32(hw, I40E_PF_ATQLEN, (hw->aq.num_asq_entries |
                                          I40E_PF_ATQLEN_ATQENABLE_MASK));
+               reg = rd32(hw, I40E_PF_ATQBAL);
        }
+
+       /* Check one register to verify that config was applied */
+       if (reg != lower_32_bits(hw->aq.asq.desc_buf.pa))
+               ret_code = I40E_ERR_ADMIN_QUEUE_ERROR;
+
+       return ret_code;
 }
 
 /**
@@ -303,8 +327,11 @@ static void i40e_config_asq_regs(struct i40e_hw *hw)
  *
  * Configure base address and length registers for the receive (event queue)
  **/
-static void i40e_config_arq_regs(struct i40e_hw *hw)
+static i40e_status i40e_config_arq_regs(struct i40e_hw *hw)
 {
+       i40e_status ret_code = 0;
+       u32 reg = 0;
+
        if (hw->mac.type == I40E_MAC_VF) {
                /* configure the receive queue */
                wr32(hw, I40E_VF_ARQBAH1,
@@ -313,6 +340,7 @@ static void i40e_config_arq_regs(struct i40e_hw *hw)
                    lower_32_bits(hw->aq.arq.desc_buf.pa));
                wr32(hw, I40E_VF_ARQLEN1, (hw->aq.num_arq_entries |
                                          I40E_VF_ARQLEN1_ARQENABLE_MASK));
+               reg = rd32(hw, I40E_VF_ARQBAL1);
        } else {
                /* configure the receive queue */
                wr32(hw, I40E_PF_ARQBAH,
@@ -321,10 +349,17 @@ static void i40e_config_arq_regs(struct i40e_hw *hw)
                    lower_32_bits(hw->aq.arq.desc_buf.pa));
                wr32(hw, I40E_PF_ARQLEN, (hw->aq.num_arq_entries |
                                          I40E_PF_ARQLEN_ARQENABLE_MASK));
+               reg = rd32(hw, I40E_PF_ARQBAL);
        }
 
        /* Update tail in the HW to post pre-allocated buffers */
        wr32(hw, hw->aq.arq.tail, hw->aq.num_arq_entries - 1);
+
+       /* Check one register to verify that config was applied */
+       if (reg != lower_32_bits(hw->aq.arq.desc_buf.pa))
+               ret_code = I40E_ERR_ADMIN_QUEUE_ERROR;
+
+       return ret_code;
 }
 
 /**
@@ -372,7 +407,9 @@ static i40e_status i40e_init_asq(struct i40e_hw *hw)
                goto init_adminq_free_rings;
 
        /* initialize base registers */
-       i40e_config_asq_regs(hw);
+       ret_code = i40e_config_asq_regs(hw);
+       if (ret_code)
+               goto init_adminq_free_rings;
 
        /* success! */
        goto init_adminq_exit;
@@ -429,7 +466,9 @@ static i40e_status i40e_init_arq(struct i40e_hw *hw)
                goto init_adminq_free_rings;
 
        /* initialize base registers */
-       i40e_config_arq_regs(hw);
+       ret_code = i40e_config_arq_regs(hw);
+       if (ret_code)
+               goto init_adminq_free_rings;
 
        /* success! */
        goto init_adminq_exit;
@@ -659,6 +698,12 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw,
                goto asq_send_command_exit;
        }
 
+       if (i40e_is_nvm_update_op(desc) && hw->aq.nvm_busy) {
+               i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, "AQTX: NVM busy.\n");
+               status = I40E_ERR_NVM;
+               goto asq_send_command_exit;
+       }
+
        details = I40E_ADMINQ_DETAILS(hw->aq.asq, hw->aq.asq.next_to_use);
        if (cmd_details) {
                *details = *cmd_details;
@@ -786,6 +831,9 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw,
                hw->aq.asq_last_status = (enum i40e_admin_queue_err)retval;
        }
 
+       if (i40e_is_nvm_update_op(desc))
+               hw->aq.nvm_busy = true;
+
        /* update the error if time out occurred */
        if ((!cmd_completed) &&
            (!details->async && !details->postpone)) {
@@ -880,6 +928,9 @@ i40e_status i40evf_clean_arq_element(struct i40e_hw *hw,
                               e->msg_size);
        }
 
+       if (i40e_is_nvm_update_op(&e->desc))
+               hw->aq.nvm_busy = false;
+
        /* Restore the original datalen and buffer address in the desc,
         * FW updates datalen to indicate the event message
         * size
index 8f72c31d95cc85adafac60190967de3f607b4bd6..e3472c62e1554194740a9233aadedaa5f13077e3 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -87,6 +90,7 @@ struct i40e_adminq_info {
        u16 fw_min_ver;                 /* firmware minor version */
        u16 api_maj_ver;                /* api major version */
        u16 api_min_ver;                /* api minor version */
+       bool nvm_busy;
 
        struct mutex asq_mutex; /* Send queue lock */
        struct mutex arq_mutex; /* Receive queue lock */
index 97662b6bd98a3e5badd0660bfcc9c932500ae124..e656ea7a79207296eea4da9975194239bae4a4bc 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -31,7 +34,7 @@
  */
 
 #define I40E_FW_API_VERSION_MAJOR  0x0001
-#define I40E_FW_API_VERSION_MINOR  0x0001
+#define I40E_FW_API_VERSION_MINOR  0x0002
 #define I40E_FW_API_VERSION_A0_MINOR  0x0000
 
 struct i40e_aq_desc {
@@ -121,6 +124,7 @@ enum i40e_admin_queue_opc {
        i40e_aqc_opc_get_version      = 0x0001,
        i40e_aqc_opc_driver_version   = 0x0002,
        i40e_aqc_opc_queue_shutdown   = 0x0003,
+       i40e_aqc_opc_set_pf_context   = 0x0004,
 
        /* resource ownership */
        i40e_aqc_opc_request_resource = 0x0008,
@@ -180,9 +184,6 @@ enum i40e_admin_queue_opc {
        i40e_aqc_opc_add_mirror_rule    = 0x0260,
        i40e_aqc_opc_delete_mirror_rule = 0x0261,
 
-       i40e_aqc_opc_set_storm_control_config = 0x0280,
-       i40e_aqc_opc_get_storm_control_config = 0x0281,
-
        /* DCB commands */
        i40e_aqc_opc_dcb_ignore_pfc = 0x0301,
        i40e_aqc_opc_dcb_updated    = 0x0302,
@@ -205,6 +206,7 @@ enum i40e_admin_queue_opc {
        i40e_aqc_opc_query_switching_comp_bw_config        = 0x041A,
        i40e_aqc_opc_suspend_port_tx                       = 0x041B,
        i40e_aqc_opc_resume_port_tx                        = 0x041C,
+       i40e_aqc_opc_configure_partition_bw                = 0x041D,
 
        /* hmc */
        i40e_aqc_opc_query_hmc_resource_profile = 0x0500,
@@ -222,13 +224,15 @@ enum i40e_admin_queue_opc {
        i40e_aqc_opc_get_partner_advt    = 0x0616,
        i40e_aqc_opc_set_lb_modes        = 0x0618,
        i40e_aqc_opc_get_phy_wol_caps    = 0x0621,
-       i40e_aqc_opc_set_phy_reset       = 0x0622,
+       i40e_aqc_opc_set_phy_debug       = 0x0622,
        i40e_aqc_opc_upload_ext_phy_fm   = 0x0625,
 
        /* NVM commands */
-       i40e_aqc_opc_nvm_read   = 0x0701,
-       i40e_aqc_opc_nvm_erase  = 0x0702,
-       i40e_aqc_opc_nvm_update = 0x0703,
+       i40e_aqc_opc_nvm_read         = 0x0701,
+       i40e_aqc_opc_nvm_erase        = 0x0702,
+       i40e_aqc_opc_nvm_update       = 0x0703,
+       i40e_aqc_opc_nvm_config_read  = 0x0704,
+       i40e_aqc_opc_nvm_config_write = 0x0705,
 
        /* virtualization commands */
        i40e_aqc_opc_send_msg_to_pf   = 0x0801,
@@ -270,8 +274,6 @@ enum i40e_admin_queue_opc {
        i40e_aqc_opc_debug_set_mode         = 0xFF01,
        i40e_aqc_opc_debug_read_reg         = 0xFF03,
        i40e_aqc_opc_debug_write_reg        = 0xFF04,
-       i40e_aqc_opc_debug_read_reg_sg      = 0xFF05,
-       i40e_aqc_opc_debug_write_reg_sg     = 0xFF06,
        i40e_aqc_opc_debug_modify_reg       = 0xFF07,
        i40e_aqc_opc_debug_dump_internals   = 0xFF08,
        i40e_aqc_opc_debug_modify_internals = 0xFF09,
@@ -339,6 +341,14 @@ struct i40e_aqc_queue_shutdown {
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_queue_shutdown);
 
+/* Set PF context (0x0004, direct) */
+struct i40e_aqc_set_pf_context {
+       u8      pf_id;
+       u8      reserved[15];
+};
+
+I40E_CHECK_CMD_LENGTH(i40e_aqc_set_pf_context);
+
 /* Request resource ownership (direct 0x0008)
  * Release resource ownership (direct 0x0009)
  */
@@ -678,7 +688,6 @@ struct i40e_aqc_add_get_update_vsi {
 #define I40E_AQ_VSI_TYPE_PF             0x2
 #define I40E_AQ_VSI_TYPE_EMP_MNG        0x3
 #define I40E_AQ_VSI_FLAG_CASCADED_PV    0x4
-#define I40E_AQ_VSI_FLAG_CLOUD_VSI      0x8
        __le32 addr_high;
        __le32 addr_low;
 };
@@ -1040,7 +1049,9 @@ struct i40e_aqc_set_vsi_promiscuous_modes {
 #define I40E_AQC_SET_VSI_PROMISC_VLAN        0x10
        __le16 seid;
 #define I40E_AQC_VSI_PROM_CMD_SEID_MASK      0x3FF
-       u8     reserved[10];
+       __le16 vlan_tag;
+#define I40E_AQC_SET_VSI_VLAN_VALID          0x8000
+       u8     reserved[8];
 };
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_set_vsi_promiscuous_modes);
@@ -1289,27 +1300,6 @@ struct i40e_aqc_add_delete_mirror_rule_completion {
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_add_delete_mirror_rule_completion);
 
-/* Set Storm Control Configuration (direct 0x0280)
- * Get Storm Control Configuration (direct 0x0281)
- *    the command and response use the same descriptor structure
- */
-struct i40e_aqc_set_get_storm_control_config {
-       __le32 broadcast_threshold;
-       __le32 multicast_threshold;
-       __le32 control_flags;
-#define I40E_AQC_STORM_CONTROL_MDIPW            0x01
-#define I40E_AQC_STORM_CONTROL_MDICW            0x02
-#define I40E_AQC_STORM_CONTROL_BDIPW            0x04
-#define I40E_AQC_STORM_CONTROL_BDICW            0x08
-#define I40E_AQC_STORM_CONTROL_BIDU             0x10
-#define I40E_AQC_STORM_CONTROL_INTERVAL_SHIFT   8
-#define I40E_AQC_STORM_CONTROL_INTERVAL_MASK    (0x3FF << \
-                                       I40E_AQC_STORM_CONTROL_INTERVAL_SHIFT)
-       u8     reserved[4];
-};
-
-I40E_CHECK_CMD_LENGTH(i40e_aqc_set_get_storm_control_config);
-
 /* DCB 0x03xx*/
 
 /* PFC Ignore (direct 0x0301)
@@ -1427,11 +1417,12 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_configure_switching_comp_bw_limit);
 struct i40e_aqc_configure_switching_comp_ets_data {
        u8     reserved[4];
        u8     tc_valid_bits;
-       u8     reserved1;
+       u8     seepage;
+#define I40E_AQ_ETS_SEEPAGE_EN_MASK     0x1
        u8     tc_strict_priority_flags;
-       u8     reserved2[17];
+       u8     reserved1[17];
        u8     tc_bw_share_credits[8];
-       u8     reserved3[96];
+       u8     reserved2[96];
 };
 
 /* Configure Switching Component Bandwidth Limits per Tc (indirect 0x0416) */
@@ -1499,6 +1490,15 @@ struct i40e_aqc_query_switching_comp_bw_config_resp {
  * (direct 0x041B and 0x041C) uses the generic SEID struct
  */
 
+/* Configure partition BW
+ * (indirect 0x041D)
+ */
+struct i40e_aqc_configure_partition_bw_data {
+       __le16 pf_valid_bits;
+       u8     min_bw[16];      /* guaranteed bandwidth */
+       u8     max_bw[16];      /* bandwidth limit */
+};
+
 /* Get and set the active HMC resource profile and status.
  * (direct 0x0500) and (direct 0x0501)
  */
@@ -1539,6 +1539,8 @@ enum i40e_aq_phy_type {
        I40E_PHY_TYPE_XLPPI                     = 0x9,
        I40E_PHY_TYPE_40GBASE_CR4_CU            = 0xA,
        I40E_PHY_TYPE_10GBASE_CR1_CU            = 0xB,
+       I40E_PHY_TYPE_10GBASE_AOC               = 0xC,
+       I40E_PHY_TYPE_40GBASE_AOC               = 0xD,
        I40E_PHY_TYPE_100BASE_TX                = 0x11,
        I40E_PHY_TYPE_1000BASE_T                = 0x12,
        I40E_PHY_TYPE_10GBASE_T                 = 0x13,
@@ -1549,7 +1551,10 @@ enum i40e_aq_phy_type {
        I40E_PHY_TYPE_40GBASE_CR4               = 0x18,
        I40E_PHY_TYPE_40GBASE_SR4               = 0x19,
        I40E_PHY_TYPE_40GBASE_LR4               = 0x1A,
-       I40E_PHY_TYPE_20GBASE_KR2               = 0x1B,
+       I40E_PHY_TYPE_1000BASE_SX               = 0x1B,
+       I40E_PHY_TYPE_1000BASE_LX               = 0x1C,
+       I40E_PHY_TYPE_1000BASE_T_OPTICAL        = 0x1D,
+       I40E_PHY_TYPE_20GBASE_KR2               = 0x1E,
        I40E_PHY_TYPE_MAX
 };
 
@@ -1583,11 +1588,8 @@ struct i40e_aq_get_phy_abilities_resp {
 #define I40E_AQ_PHY_FLAG_PAUSE_TX         0x01
 #define I40E_AQ_PHY_FLAG_PAUSE_RX         0x02
 #define I40E_AQ_PHY_FLAG_LOW_POWER        0x04
-#define I40E_AQ_PHY_FLAG_AN_SHIFT         3
-#define I40E_AQ_PHY_FLAG_AN_MASK          (0x3 << I40E_AQ_PHY_FLAG_AN_SHIFT)
-#define I40E_AQ_PHY_FLAG_AN_OFF           0x00 /* link forced on */
-#define I40E_AQ_PHY_FLAG_AN_OFF_LINK_DOWN 0x01
-#define I40E_AQ_PHY_FLAG_AN_ON            0x02
+#define I40E_AQ_PHY_LINK_ENABLED                 0x08
+#define I40E_AQ_PHY_AN_ENABLED                   0x10
 #define I40E_AQ_PHY_FLAG_MODULE_QUAL      0x20
        __le16 eee_capability;
 #define I40E_AQ_EEE_100BASE_TX       0x0002
@@ -1696,6 +1698,7 @@ struct i40e_aqc_get_link_status {
 #define I40E_AQ_LINK_TX_ACTIVE       0x00
 #define I40E_AQ_LINK_TX_DRAINED      0x01
 #define I40E_AQ_LINK_TX_FLUSHED      0x03
+#define I40E_AQ_LINK_FORCED_40G      0x10
        u8     loopback;         /* use defines from i40e_aqc_set_lb_mode */
        __le16 max_frame_size;
        u8     config;
@@ -1747,14 +1750,21 @@ struct i40e_aqc_set_lb_mode {
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_set_lb_mode);
 
-/* Set PHY Reset command (0x0622) */
-struct i40e_aqc_set_phy_reset {
-       u8     reset_flags;
-#define I40E_AQ_PHY_RESET_REQUEST  0x02
+/* Set PHY Debug command (0x0622) */
+struct i40e_aqc_set_phy_debug {
+       u8     command_flags;
+#define I40E_AQ_PHY_DEBUG_RESET_INTERNAL       0x02
+#define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_SHIFT 2
+#define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_MASK  (0x03 << \
+                                       I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_SHIFT)
+#define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_NONE  0x00
+#define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_HARD  0x01
+#define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_SOFT  0x02
+#define I40E_AQ_PHY_DEBUG_DISABLE_LINK_FW      0x10
        u8     reserved[15];
 };
 
-I40E_CHECK_CMD_LENGTH(i40e_aqc_set_phy_reset);
+I40E_CHECK_CMD_LENGTH(i40e_aqc_set_phy_debug);
 
 enum i40e_aq_phy_reg_type {
        I40E_AQC_PHY_REG_INTERNAL         = 0x1,
@@ -1779,6 +1789,47 @@ struct i40e_aqc_nvm_update {
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_nvm_update);
 
+/* NVM Config Read (indirect 0x0704) */
+struct i40e_aqc_nvm_config_read {
+       __le16 cmd_flags;
+#define ANVM_SINGLE_OR_MULTIPLE_FEATURES_MASK  1
+#define ANVM_READ_SINGLE_FEATURE               0
+#define ANVM_READ_MULTIPLE_FEATURES            1
+       __le16 element_count;
+       __le16 element_id;              /* Feature/field ID */
+       u8     reserved[2];
+       __le32 address_high;
+       __le32 address_low;
+};
+
+I40E_CHECK_CMD_LENGTH(i40e_aqc_nvm_config_read);
+
+/* NVM Config Write (indirect 0x0705) */
+struct i40e_aqc_nvm_config_write {
+       __le16 cmd_flags;
+       __le16 element_count;
+       u8     reserved[4];
+       __le32 address_high;
+       __le32 address_low;
+};
+
+I40E_CHECK_CMD_LENGTH(i40e_aqc_nvm_config_write);
+
+struct i40e_aqc_nvm_config_data_feature {
+       __le16 feature_id;
+       __le16 instance_id;
+       __le16 feature_options;
+       __le16 feature_selection;
+};
+
+struct i40e_aqc_nvm_config_data_immediate_field {
+#define ANVM_FEATURE_OR_IMMEDIATE_MASK 0x2
+       __le16 field_id;
+       __le16 instance_id;
+       __le16 field_options;
+       __le16 field_value;
+};
+
 /* Send to PF command (indirect 0x0801) id is only used by PF
  * Send to VF command (indirect 0x0802) id is only used by PF
  * Send to Peer PF command (indirect 0x0803)
@@ -1948,19 +1999,12 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_start);
 /* Add Udp Tunnel command and completion (direct 0x0B00) */
 struct i40e_aqc_add_udp_tunnel {
        __le16 udp_port;
-       u8     header_len; /* in DWords, 1 to 15 */
+       u8     reserved0[3];
        u8     protocol_type;
-#define I40E_AQC_TUNNEL_TYPE_TEREDO    0x0
-#define I40E_AQC_TUNNEL_TYPE_VXLAN     0x2
-#define I40E_AQC_TUNNEL_TYPE_NGE       0x3
-       u8     variable_udp_length;
-#define I40E_AQC_TUNNEL_FIXED_UDP_LENGTH       0x0
-#define I40E_AQC_TUNNEL_VARIABLE_UDP_LENGTH    0x1
-       u8              udp_key_index;
-#define I40E_AQC_TUNNEL_KEY_INDEX_VXLAN                        0x0
-#define I40E_AQC_TUNNEL_KEY_INDEX_NGE                  0x1
-#define I40E_AQC_TUNNEL_KEY_INDEX_PROPRIETARY_UDP      0x2
-       u8              reserved[10];
+#define I40E_AQC_TUNNEL_TYPE_VXLAN     0x00
+#define I40E_AQC_TUNNEL_TYPE_NGE       0x01
+#define I40E_AQC_TUNNEL_TYPE_TEREDO    0x10
+       u8     reserved1[10];
 };
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_add_udp_tunnel);
index d8654fb9e525a82881c88cf882d91c716577ec0f..8e6a6dd9212bb0812f42378ad208ccd451e43ee4 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
index ae084378faabff7669794cd0f47a9072f1698243..a43155afdbe24784d750f5fa8878e57a100913c8 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -40,12 +43,10 @@ i40e_status i40e_set_mac_type(struct i40e_hw *hw)
        if (hw->vendor_id == PCI_VENDOR_ID_INTEL) {
                switch (hw->device_id) {
                case I40E_DEV_ID_SFP_XL710:
-               case I40E_DEV_ID_SFP_X710:
                case I40E_DEV_ID_QEMU:
                case I40E_DEV_ID_KX_A:
                case I40E_DEV_ID_KX_B:
                case I40E_DEV_ID_KX_C:
-               case I40E_DEV_ID_KX_D:
                case I40E_DEV_ID_QSFP_A:
                case I40E_DEV_ID_QSFP_B:
                case I40E_DEV_ID_QSFP_C:
@@ -130,7 +131,11 @@ void i40evf_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void *desc,
  **/
 bool i40evf_check_asq_alive(struct i40e_hw *hw)
 {
-       return !!(rd32(hw, hw->aq.asq.len) & I40E_PF_ATQLEN_ATQENABLE_MASK);
+       if (hw->aq.asq.len)
+               return !!(rd32(hw, hw->aq.asq.len) &
+                         I40E_PF_ATQLEN_ATQENABLE_MASK);
+       else
+               return false;
 }
 
 /**
index cb97b3eed440ff764c1a9dbf705e45bc9e308b2f..a2ad9a4e399da70707a9ed984588a8c2cde04686 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -160,11 +163,6 @@ struct i40e_hmc_info {
            (((sd_idx) << I40E_PFHMC_PDINV_PMSDIDX_SHIFT) |             \
             ((pd_idx) << I40E_PFHMC_PDINV_PMPDIDX_SHIFT)))
 
-#define I40E_INVALIDATE_VF_HMC_PD(hw, sd_idx, pd_idx, hmc_fn_id)          \
-       wr32((hw), I40E_GLHMC_VFPDINV((hmc_fn_id) - I40E_FIRST_VF_FPM_ID), \
-            (((sd_idx) << I40E_PFHMC_PDINV_PMSDIDX_SHIFT) |               \
-             ((pd_idx) << I40E_PFHMC_PDINV_PMPDIDX_SHIFT)))
-
 /**
  * I40E_FIND_SD_INDEX_LIMIT - finds segment descriptor index limit
  * @hmc_info: pointer to the HMC configuration information structure
@@ -223,7 +221,7 @@ i40e_status i40e_add_pd_table_entry(struct i40e_hw *hw,
                                              u32 pd_index);
 i40e_status i40e_remove_pd_bp(struct i40e_hw *hw,
                                        struct i40e_hmc_info *hmc_info,
-                                       u32 idx, bool is_pf);
+                                       u32 idx);
 i40e_status i40e_prep_remove_sd_bp(struct i40e_hmc_info *hmc_info,
                                             u32 idx);
 i40e_status i40e_remove_sd_bp_new(struct i40e_hw *hw,
index 17e42ca26d0ba045fc51a0efd1ec0b1af0fffd71..d6f762241537804be777b97ad5f29616f990058f 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -53,6 +56,7 @@ struct i40e_hmc_obj_rxq {
        u8  tphdata_ena;
        u8  tphhead_ena;
        u8  lrxqthresh;
+       u8  prefena;    /* NOTE: normally must be set to 1 at init */
 };
 
 /* Tx queue context data */
index 622f373b745d59092355131aa20f5f5c57d486c8..21a91b14bf819365bfea82276324b14da6ef403c 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
index 97ab8c2b76f8f0e6cf1c6c485eaeafee0e42a5a1..849edcc2e398f7bfdaaea5eaf078ba714977ca5f 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
index 30af953cf106a4afc6aa9e0939d24adce8d68418..3698396558186f67736fae33294b9993ccf8abad 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
 #define I40E_PFINT_ICR0_GPIO_MASK (0x1 << I40E_PFINT_ICR0_GPIO_SHIFT)
 #define I40E_PFINT_ICR0_TIMESYNC_SHIFT 23
 #define I40E_PFINT_ICR0_TIMESYNC_MASK (0x1 << I40E_PFINT_ICR0_TIMESYNC_SHIFT)
-#define I40E_PFINT_ICR0_STORM_DETECT_SHIFT 24
-#define I40E_PFINT_ICR0_STORM_DETECT_MASK (0x1 << I40E_PFINT_ICR0_STORM_DETECT_SHIFT)
 #define I40E_PFINT_ICR0_LINK_STAT_CHANGE_SHIFT 25
 #define I40E_PFINT_ICR0_LINK_STAT_CHANGE_MASK (0x1 << I40E_PFINT_ICR0_LINK_STAT_CHANGE_SHIFT)
 #define I40E_PFINT_ICR0_HMC_ERR_SHIFT 26
 #define I40E_PFINT_ICR0_ENA_GPIO_MASK (0x1 << I40E_PFINT_ICR0_ENA_GPIO_SHIFT)
 #define I40E_PFINT_ICR0_ENA_TIMESYNC_SHIFT 23
 #define I40E_PFINT_ICR0_ENA_TIMESYNC_MASK (0x1 << I40E_PFINT_ICR0_ENA_TIMESYNC_SHIFT)
-#define I40E_PFINT_ICR0_ENA_STORM_DETECT_SHIFT 24
-#define I40E_PFINT_ICR0_ENA_STORM_DETECT_MASK (0x1 << I40E_PFINT_ICR0_ENA_STORM_DETECT_SHIFT)
 #define I40E_PFINT_ICR0_ENA_LINK_STAT_CHANGE_SHIFT 25
 #define I40E_PFINT_ICR0_ENA_LINK_STAT_CHANGE_MASK (0x1 << I40E_PFINT_ICR0_ENA_LINK_STAT_CHANGE_SHIFT)
 #define I40E_PFINT_ICR0_ENA_HMC_ERR_SHIFT 26
 #define I40E_GLLAN_TSOMSK_M 0x000442DC
 #define I40E_GLLAN_TSOMSK_M_TCPMSKM_SHIFT 0
 #define I40E_GLLAN_TSOMSK_M_TCPMSKM_MASK (0xFFF << I40E_GLLAN_TSOMSK_M_TCPMSKM_SHIFT)
+#define I40E_GLLAN_TXPRE_QDIS(_i) (0x000E6500 + ((_i) * 4)) /* i=0..11 */
+#define I40E_GLLAN_TXPRE_QDIS_QINDX_SHIFT 0
+#define I40E_GLLAN_TXPRE_QDIS_QINDX_MASK (0x7FF << I40E_GLLAN_TXPRE_QDIS_QINDX_SHIFT)
+#define I40E_GLLAN_TXPRE_QDIS_SET_QDIS_SHIFT 30
+#define I40E_GLLAN_TXPRE_QDIS_SET_QDIS_MASK (0x1 << I40E_GLLAN_TXPRE_QDIS_SET_QDIS_SHIFT)
+#define I40E_GLLAN_TXPRE_QDIS_CLEAR_QDIS_SHIFT 31
+#define I40E_GLLAN_TXPRE_QDIS_CLEAR_QDIS_MASK (0x1 << I40E_GLLAN_TXPRE_QDIS_CLEAR_QDIS_SHIFT)
+
 #define I40E_PFLAN_QALLOC 0x001C0400
 #define I40E_PFLAN_QALLOC_FIRSTQ_SHIFT 0
 #define I40E_PFLAN_QALLOC_FIRSTQ_MASK (0x7FF << I40E_PFLAN_QALLOC_FIRSTQ_SHIFT)
index 7c08cc2e339b89f05d79d37dc5e46ba9921cb17b..7fa7a41915c1acce92ce1b8d46cc22d7043f96c2 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
index b9f50f40abe18b78cf0c3ff580ded29666491d92..48ebb6cd69f28608b73fb0e22ff5b8d1a35f0389 100644 (file)
@@ -12,6 +12,9 @@
  * 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/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -725,10 +728,12 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
                                    u32 rx_error,
                                    u16 rx_ptype)
 {
+       struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(rx_ptype);
+       bool ipv4 = false, ipv6 = false;
        bool ipv4_tunnel, ipv6_tunnel;
        __wsum rx_udp_csum;
-       __sum16 csum;
        struct iphdr *iph;
+       __sum16 csum;
 
        ipv4_tunnel = (rx_ptype > I40E_RX_PTYPE_GRENAT4_MAC_PAY3) &&
                      (rx_ptype < I40E_RX_PTYPE_GRENAT4_MACVLAN_IPV6_ICMP_PAY4);
@@ -739,29 +744,57 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
        skb->ip_summed = CHECKSUM_NONE;
 
        /* Rx csum enabled and ip headers found? */
-       if (!(vsi->netdev->features & NETIF_F_RXCSUM &&
-             rx_status & (1 << I40E_RX_DESC_STATUS_L3L4P_SHIFT)))
+       if (!(vsi->netdev->features & NETIF_F_RXCSUM))
                return;
 
+       /* did the hardware decode the packet and checksum? */
+       if (!(rx_status & (1 << I40E_RX_DESC_STATUS_L3L4P_SHIFT)))
+               return;
+
+       /* both known and outer_ip must be set for the below code to work */
+       if (!(decoded.known && decoded.outer_ip))
+               return;
+
+       if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP &&
+           decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV4)
+               ipv4 = true;
+       else if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP &&
+                decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV6)
+               ipv6 = true;
+
+       if (ipv4 &&
+           (rx_error & ((1 << I40E_RX_DESC_ERROR_IPE_SHIFT) |
+                        (1 << I40E_RX_DESC_ERROR_EIPE_SHIFT))))
+               goto checksum_fail;
+
        /* likely incorrect csum if alternate IP extension headers found */
-       if (rx_status & (1 << I40E_RX_DESC_STATUS_IPV6EXADD_SHIFT))
+       if (ipv6 &&
+           decoded.inner_prot == I40E_RX_PTYPE_INNER_PROT_TCP &&
+           rx_error & (1 << I40E_RX_DESC_ERROR_L4E_SHIFT) &&
+           rx_status & (1 << I40E_RX_DESC_STATUS_IPV6EXADD_SHIFT))
+               /* don't increment checksum err here, non-fatal err */
                return;
 
-       /* IP or L4 or outmost IP checksum error */
-       if (rx_error & ((1 << I40E_RX_DESC_ERROR_IPE_SHIFT) |
-                       (1 << I40E_RX_DESC_ERROR_L4E_SHIFT) |
-                       (1 << I40E_RX_DESC_ERROR_EIPE_SHIFT))) {
-               vsi->back->hw_csum_rx_error++;
+       /* there was some L4 error, count error and punt packet to the stack */
+       if (rx_error & (1 << I40E_RX_DESC_ERROR_L4E_SHIFT))
+               goto checksum_fail;
+
+       /* handle packets that were not able to be checksummed due
+        * to arrival speed, in this case the stack can compute
+        * the csum.
+        */
+       if (rx_error & (1 << I40E_RX_DESC_ERROR_PPRS_SHIFT))
                return;
-       }
 
+       /* If VXLAN traffic has an outer UDPv4 checksum we need to check
+        * it in the driver, hardware does not do it for us.
+        * Since L3L4P bit was set we assume a valid IHL value (>=5)
+        * so the total length of IPv4 header is IHL*4 bytes
+        * The UDP_0 bit *may* bet set if the *inner* header is UDP
+        */
        if (ipv4_tunnel &&
+           (decoded.inner_prot != I40E_RX_PTYPE_INNER_PROT_UDP) &&
            !(rx_status & (1 << I40E_RX_DESC_STATUS_UDP_0_SHIFT))) {
-               /* If VXLAN traffic has an outer UDPv4 checksum we need to check
-                * it in the driver, hardware does not do it for us.
-                * Since L3L4P bit was set we assume a valid IHL value (>=5)
-                * so the total length of IPv4 header is IHL*4 bytes
-                */
                skb->transport_header = skb->mac_header +
                                        sizeof(struct ethhdr) +
                                        (ip_hdr(skb)->ihl * 4);
@@ -778,13 +811,16 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
                                (skb->len - skb_transport_offset(skb)),
                                IPPROTO_UDP, rx_udp_csum);
 
-               if (udp_hdr(skb)->check != csum) {
-                       vsi->back->hw_csum_rx_error++;
-                       return;
-               }
+               if (udp_hdr(skb)->check != csum)
+                       goto checksum_fail;
        }
 
        skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+       return;
+
+checksum_fail:
+       vsi->back->hw_csum_rx_error++;
 }
 
 /**
@@ -953,6 +989,9 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
                /* ERR_MASK will only have valid bits if EOP set */
                if (unlikely(rx_error & (1 << I40E_RX_DESC_ERROR_RXE_SHIFT))) {
                        dev_kfree_skb_any(skb);
+                       /* TODO: shouldn't we increment a counter indicating the
+                        * drop?
+                        */
                        goto next_desc;
                }
 
@@ -1508,9 +1547,7 @@ static int i40e_maybe_stop_tx(struct i40e_ring *tx_ring, int size)
 static int i40e_xmit_descriptor_count(struct sk_buff *skb,
                                      struct i40e_ring *tx_ring)
 {
-#if PAGE_SIZE > I40E_MAX_DATA_PER_TXD
        unsigned int f;
-#endif
        int count = 0;
 
        /* need: 1 descriptor per page * PAGE_SIZE/I40E_MAX_DATA_PER_TXD,
@@ -1519,12 +1556,9 @@ static int i40e_xmit_descriptor_count(struct sk_buff *skb,
         *       + 1 desc for context descriptor,
         * otherwise try next time
         */
-#if PAGE_SIZE > I40E_MAX_DATA_PER_TXD
        for (f = 0; f < skb_shinfo(skb)->nr_frags; f++)
                count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size);
-#else
-       count += skb_shinfo(skb)->nr_frags;
-#endif
+
        count += TXD_USE_COUNT(skb_headlen(skb));
        if (i40e_maybe_stop_tx(tx_ring, count + 4 + 1)) {
                tx_ring->tx_stats.tx_busy++;
index 10bf49e18d7f6c0ed8d1af47d5d30544c5a8951e..30d248bc5d199d50ba14cc79356d689c2265aebc 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -24,7 +27,7 @@
 #ifndef _I40E_TXRX_H_
 #define _I40E_TXRX_H_
 
-/* Interrupt Throttling and Rate Limiting (storm control) Goodies */
+/* Interrupt Throttling and Rate Limiting Goodies */
 
 #define I40E_MAX_ITR               0x0FF0  /* reg uses 2 usec resolution */
 #define I40E_MIN_ITR               0x0004  /* reg uses 2 usec resolution */
@@ -66,16 +69,11 @@ enum i40e_dyn_idx_t {
 
 /* Supported RSS offloads */
 #define I40E_DEFAULT_RSS_HENA ( \
-       ((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) | \
-       ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_SCTP) | \
-       ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER) | \
        ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4) | \
-       ((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | \
-       ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP) | \
@@ -119,11 +117,11 @@ enum i40e_dyn_idx_t {
 #define i40e_rx_desc i40e_32byte_rx_desc
 
 #define I40E_MIN_TX_LEN                17
-#define I40E_MAX_DATA_PER_TXD  16383   /* aka 16kB - 1 */
+#define I40E_MAX_DATA_PER_TXD  8192
 
 /* Tx Descriptors needed, worst case */
 #define TXD_USE_COUNT(S) DIV_ROUND_UP((S), I40E_MAX_DATA_PER_TXD)
-#define DESC_NEEDED ((MAX_SKB_FRAGS * TXD_USE_COUNT(PAGE_SIZE)) + 4)
+#define DESC_NEEDED (MAX_SKB_FRAGS + 4)
 
 #define I40E_TX_FLAGS_CSUM             (u32)(1)
 #define I40E_TX_FLAGS_HW_VLAN          (u32)(1 << 1)
@@ -180,7 +178,6 @@ enum i40e_ring_state_t {
        __I40E_TX_DETECT_HANG,
        __I40E_HANG_CHECK_ARMED,
        __I40E_RX_PS_ENABLED,
-       __I40E_RX_LRO_ENABLED,
        __I40E_RX_16BYTE_DESC_ENABLED,
 };
 
@@ -196,12 +193,6 @@ enum i40e_ring_state_t {
        set_bit(__I40E_TX_DETECT_HANG, &(ring)->state)
 #define clear_check_for_tx_hang(ring) \
        clear_bit(__I40E_TX_DETECT_HANG, &(ring)->state)
-#define ring_is_lro_enabled(ring) \
-       test_bit(__I40E_RX_LRO_ENABLED, &(ring)->state)
-#define set_ring_lro_enabled(ring) \
-       set_bit(__I40E_RX_LRO_ENABLED, &(ring)->state)
-#define clear_ring_lro_enabled(ring) \
-       clear_bit(__I40E_RX_LRO_ENABLED, &(ring)->state)
 #define ring_is_16byte_desc_enabled(ring) \
        test_bit(__I40E_RX_16BYTE_DESC_ENABLED, &(ring)->state)
 #define set_ring_16byte_desc_enabled(ring) \
index 4673b3381eddaa1672edca1f60b85e89d33ab247..d3cf5a69de54d6e7e7bc648dd478873b88510b5c 100644 (file)
@@ -12,6 +12,9 @@
  * 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/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
 #include "i40e_lan_hmc.h"
 
 /* Device IDs */
-#define I40E_DEV_ID_SFP_XL710  0x1572
-#define I40E_DEV_ID_SFP_X710           0x1573
+#define I40E_DEV_ID_SFP_XL710          0x1572
 #define I40E_DEV_ID_QEMU               0x1574
 #define I40E_DEV_ID_KX_A               0x157F
 #define I40E_DEV_ID_KX_B               0x1580
 #define I40E_DEV_ID_KX_C               0x1581
-#define I40E_DEV_ID_KX_D               0x1582
 #define I40E_DEV_ID_QSFP_A             0x1583
 #define I40E_DEV_ID_QSFP_B             0x1584
 #define I40E_DEV_ID_QSFP_C             0x1585
@@ -57,8 +58,8 @@
 /* Max default timeout in ms, */
 #define I40E_MAX_NVM_TIMEOUT           18000
 
-/* Switch from mc to the 2usec global time (this is the GTIME resolution) */
-#define I40E_MS_TO_GTIME(time)         (((time) * 1000) / 2)
+/* Switch from ms to the 1usec global time (this is the GTIME resolution) */
+#define I40E_MS_TO_GTIME(time)         ((time) * 1000)
 
 /* forward declaration */
 struct i40e_hw;
@@ -101,15 +102,6 @@ enum i40e_debug_mask {
        I40E_DEBUG_ALL                  = 0xFFFFFFFF
 };
 
-/* PCI Bus Info */
-#define I40E_PCI_LINK_WIDTH_1          0x10
-#define I40E_PCI_LINK_WIDTH_2          0x20
-#define I40E_PCI_LINK_WIDTH_4          0x40
-#define I40E_PCI_LINK_WIDTH_8          0x80
-#define I40E_PCI_LINK_SPEED_2500       0x1
-#define I40E_PCI_LINK_SPEED_5000       0x2
-#define I40E_PCI_LINK_SPEED_8000       0x3
-
 /* These are structs for managing the hardware information and the operations.
  * The structures of function pointers are filled out at init time when we
  * know for sure exactly which hardware we're working with.  This gives us the
@@ -173,6 +165,9 @@ struct i40e_link_status {
        u8 loopback;
        /* is Link Status Event notification to SW enabled */
        bool lse_enable;
+       u16 max_frame_size;
+       bool crc_enable;
+       u8 pacing;
 };
 
 struct i40e_phy_info {
@@ -415,6 +410,7 @@ struct i40e_driver_version {
        u8 minor_version;
        u8 build_version;
        u8 subbuild_version;
+       u8 driver_string[32];
 };
 
 /* RX Descriptors */
@@ -494,9 +490,6 @@ union i40e_32byte_rx_desc {
        } wb;  /* writeback */
 };
 
-#define I40E_RXD_QW1_STATUS_SHIFT      0
-#define I40E_RXD_QW1_STATUS_MASK       (0x7FFFUL << I40E_RXD_QW1_STATUS_SHIFT)
-
 enum i40e_rx_desc_status_bits {
        /* Note: These are predefined bit offsets */
        I40E_RX_DESC_STATUS_DD_SHIFT            = 0,
@@ -513,9 +506,14 @@ enum i40e_rx_desc_status_bits {
        I40E_RX_DESC_STATUS_LPBK_SHIFT          = 14,
        I40E_RX_DESC_STATUS_IPV6EXADD_SHIFT     = 15,
        I40E_RX_DESC_STATUS_RESERVED_SHIFT      = 16, /* 2 BITS */
-       I40E_RX_DESC_STATUS_UDP_0_SHIFT         = 18
+       I40E_RX_DESC_STATUS_UDP_0_SHIFT         = 18,
+       I40E_RX_DESC_STATUS_LAST /* this entry must be last!!! */
 };
 
+#define I40E_RXD_QW1_STATUS_SHIFT      0
+#define I40E_RXD_QW1_STATUS_MASK       (((1 << I40E_RX_DESC_STATUS_LAST) - 1) \
+                                        << I40E_RXD_QW1_STATUS_SHIFT)
+
 #define I40E_RXD_QW1_STATUS_TSYNINDX_SHIFT   I40E_RX_DESC_STATUS_TSYNINDX_SHIFT
 #define I40E_RXD_QW1_STATUS_TSYNINDX_MASK      (0x3UL << \
                                             I40E_RXD_QW1_STATUS_TSYNINDX_SHIFT)
@@ -543,7 +541,8 @@ enum i40e_rx_desc_error_bits {
        I40E_RX_DESC_ERROR_IPE_SHIFT            = 3,
        I40E_RX_DESC_ERROR_L4E_SHIFT            = 4,
        I40E_RX_DESC_ERROR_EIPE_SHIFT           = 5,
-       I40E_RX_DESC_ERROR_OVERSIZE_SHIFT       = 6
+       I40E_RX_DESC_ERROR_OVERSIZE_SHIFT       = 6,
+       I40E_RX_DESC_ERROR_PPRS_SHIFT           = 7
 };
 
 enum i40e_rx_desc_error_l3l4e_fcoe_masks {
@@ -664,7 +663,6 @@ enum i40e_rx_desc_ext_status_bits {
        I40E_RX_DESC_EXT_STATUS_L2TAG3P_SHIFT   = 1,
        I40E_RX_DESC_EXT_STATUS_FLEXBL_SHIFT    = 2, /* 2 BITS */
        I40E_RX_DESC_EXT_STATUS_FLEXBH_SHIFT    = 4, /* 2 BITS */
-       I40E_RX_DESC_EXT_STATUS_FTYPE_SHIFT     = 6, /* 3 BITS */
        I40E_RX_DESC_EXT_STATUS_FDLONGB_SHIFT   = 9,
        I40E_RX_DESC_EXT_STATUS_FCOELONGB_SHIFT = 10,
        I40E_RX_DESC_EXT_STATUS_PELONGB_SHIFT   = 11,
@@ -868,18 +866,14 @@ struct i40e_filter_program_desc {
 
 /* Packet Classifier Types for filters */
 enum i40e_filter_pctype {
-       /* Note: Values 0-28 are reserved for future use */
-       I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP        = 29,
-       I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP      = 30,
+       /* Note: Values 0-30 are reserved for future use */
        I40E_FILTER_PCTYPE_NONF_IPV4_UDP                = 31,
-       I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN            = 32,
+       /* Note: Value 32 is reserved for future use */
        I40E_FILTER_PCTYPE_NONF_IPV4_TCP                = 33,
        I40E_FILTER_PCTYPE_NONF_IPV4_SCTP               = 34,
        I40E_FILTER_PCTYPE_NONF_IPV4_OTHER              = 35,
        I40E_FILTER_PCTYPE_FRAG_IPV4                    = 36,
-       /* Note: Values 37-38 are reserved for future use */
-       I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP        = 39,
-       I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP      = 40,
+       /* Note: Values 37-40 are reserved for future use */
        I40E_FILTER_PCTYPE_NONF_IPV6_UDP                = 41,
        I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN            = 42,
        I40E_FILTER_PCTYPE_NONF_IPV6_TCP                = 43,
@@ -961,6 +955,16 @@ struct i40e_vsi_context {
        struct i40e_aqc_vsi_properties_data info;
 };
 
+struct i40e_veb_context {
+       u16 seid;
+       u16 uplink_seid;
+       u16 veb_number;
+       u16 vebs_allocated;
+       u16 vebs_unallocated;
+       u16 flags;
+       struct i40e_aqc_get_veb_parameters_completion info;
+};
+
 /* Statistics collected by each port, VSI, VEB, and S-channel */
 struct i40e_eth_stats {
        u64 rx_bytes;                   /* gorc */
@@ -968,8 +972,6 @@ struct i40e_eth_stats {
        u64 rx_multicast;               /* mprc */
        u64 rx_broadcast;               /* bprc */
        u64 rx_discards;                /* rdpc */
-       u64 rx_errors;                  /* repc */
-       u64 rx_missed;                  /* rmpc */
        u64 rx_unknown_protocol;        /* rupp */
        u64 tx_bytes;                   /* gotc */
        u64 tx_unicast;                 /* uptc */
@@ -1021,9 +1023,12 @@ struct i40e_hw_port_stats {
        u64 tx_size_big;                /* ptc9522 */
        u64 mac_short_packet_dropped;   /* mspdc */
        u64 checksum_error;             /* xec */
+       /* flow director stats */
+       u64 fd_atr_match;
+       u64 fd_sb_match;
        /* EEE LPI */
-       bool tx_lpi_status;
-       bool rx_lpi_status;
+       u32 tx_lpi_status;
+       u32 rx_lpi_status;
        u64 tx_lpi_count;               /* etlpic */
        u64 rx_lpi_count;               /* erlpic */
 };
index ccf45d04b7ef88e74e99d5ad844ad10df8d9b4f8..cd18d5689006f19eaf2cf7fa1abade5b7adc1a0b 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -338,10 +341,6 @@ struct i40e_virtchnl_pf_event {
        int severity;
 };
 
-/* The following are TBD, not necessary for LAN functionality.
- * I40E_VIRTCHNL_OP_FCOE
- */
-
 /* VF reset states - these are written into the RSTAT register:
  * I40E_VFGEN_RSTAT1 on the PF
  * I40E_VFGEN_RSTAT on the VF
index 807807d6238738c0e111739e9dc96d1f2200d77a..30ef519d4b911a5c59b4cbf06316b7be0547acd2 100644 (file)
@@ -12,6 +12,9 @@
  * 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/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -77,7 +80,7 @@ struct i40e_vsi {
 #define I40EVF_MIN_TXD       64
 #define I40EVF_MAX_RXD       4096
 #define I40EVF_MIN_RXD       64
-#define I40EVF_REQ_DESCRIPTOR_MULTIPLE  8
+#define I40EVF_REQ_DESCRIPTOR_MULTIPLE  32
 
 /* Supported Rx Buffer Sizes */
 #define I40EVF_RXBUFFER_64    64     /* Used for packet split */
@@ -193,10 +196,12 @@ struct i40evf_adapter {
        struct i40e_ring *tx_rings[I40E_MAX_VSI_QP];
        u32 tx_timeout_count;
        struct list_head mac_filter_list;
+       u32 tx_desc_count;
 
        /* RX */
        struct i40e_ring *rx_rings[I40E_MAX_VSI_QP];
        u64 hw_csum_rx_error;
+       u32 rx_desc_count;
        int num_msix_vectors;
        struct msix_entry *msix_entries;
 
index 8b0db1ce179c5447ce83240098e6c076d6782ed6..60407a9df0c1ef5103b4e0f198f556b229a9417a 100644 (file)
@@ -12,6 +12,9 @@
  * 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/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -44,8 +47,6 @@ static const struct i40evf_stats i40evf_gstrings_stats[] = {
        I40EVF_STAT("rx_multicast", current_stats.rx_multicast),
        I40EVF_STAT("rx_broadcast", current_stats.rx_broadcast),
        I40EVF_STAT("rx_discards", current_stats.rx_discards),
-       I40EVF_STAT("rx_errors", current_stats.rx_errors),
-       I40EVF_STAT("rx_missed", current_stats.rx_missed),
        I40EVF_STAT("rx_unknown_protocol", current_stats.rx_unknown_protocol),
        I40EVF_STAT("tx_bytes", current_stats.tx_bytes),
        I40EVF_STAT("tx_unicast", current_stats.tx_unicast),
@@ -56,10 +57,12 @@ static const struct i40evf_stats i40evf_gstrings_stats[] = {
 };
 
 #define I40EVF_GLOBAL_STATS_LEN ARRAY_SIZE(i40evf_gstrings_stats)
-#define I40EVF_QUEUE_STATS_LEN \
+#define I40EVF_QUEUE_STATS_LEN(_dev) \
        (((struct i40evf_adapter *) \
-               netdev_priv(netdev))->vsi_res->num_queue_pairs * 4)
-#define I40EVF_STATS_LEN (I40EVF_GLOBAL_STATS_LEN + I40EVF_QUEUE_STATS_LEN)
+               netdev_priv(_dev))->vsi_res->num_queue_pairs \
+                 * 2 * (sizeof(struct i40e_queue_stats) / sizeof(u64)))
+#define I40EVF_STATS_LEN(_dev) \
+       (I40EVF_GLOBAL_STATS_LEN + I40EVF_QUEUE_STATS_LEN(_dev))
 
 /**
  * i40evf_get_settings - Get Link Speed and Duplex settings
@@ -75,7 +78,7 @@ static int i40evf_get_settings(struct net_device *netdev,
        /* In the future the VF will be able to query the PF for
         * some information - for now use a dummy value
         */
-       ecmd->supported = SUPPORTED_10000baseT_Full;
+       ecmd->supported = 0;
        ecmd->autoneg = AUTONEG_DISABLE;
        ecmd->transceiver = XCVR_DUMMY1;
        ecmd->port = PORT_NONE;
@@ -94,9 +97,9 @@ static int i40evf_get_settings(struct net_device *netdev,
 static int i40evf_get_sset_count(struct net_device *netdev, int sset)
 {
        if (sset == ETH_SS_STATS)
-               return I40EVF_STATS_LEN;
+               return I40EVF_STATS_LEN(netdev);
        else
-               return -ENOTSUPP;
+               return -EINVAL;
 }
 
 /**
@@ -219,13 +222,11 @@ static void i40evf_get_ringparam(struct net_device *netdev,
                                  struct ethtool_ringparam *ring)
 {
        struct i40evf_adapter *adapter = netdev_priv(netdev);
-       struct i40e_ring *tx_ring = adapter->tx_rings[0];
-       struct i40e_ring *rx_ring = adapter->rx_rings[0];
 
        ring->rx_max_pending = I40EVF_MAX_RXD;
        ring->tx_max_pending = I40EVF_MAX_TXD;
-       ring->rx_pending = rx_ring->count;
-       ring->tx_pending = tx_ring->count;
+       ring->rx_pending = adapter->rx_desc_count;
+       ring->tx_pending = adapter->tx_desc_count;
 }
 
 /**
@@ -241,7 +242,6 @@ static int i40evf_set_ringparam(struct net_device *netdev,
 {
        struct i40evf_adapter *adapter = netdev_priv(netdev);
        u32 new_rx_count, new_tx_count;
-       int i;
 
        if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
                return -EINVAL;
@@ -257,17 +257,16 @@ static int i40evf_set_ringparam(struct net_device *netdev,
        new_rx_count = ALIGN(new_rx_count, I40EVF_REQ_DESCRIPTOR_MULTIPLE);
 
        /* if nothing to do return success */
-       if ((new_tx_count == adapter->tx_rings[0]->count) &&
-           (new_rx_count == adapter->rx_rings[0]->count))
+       if ((new_tx_count == adapter->tx_desc_count) &&
+           (new_rx_count == adapter->rx_desc_count))
                return 0;
 
-       for (i = 0; i < adapter->vsi_res->num_queue_pairs; i++) {
-               adapter->tx_rings[0]->count = new_tx_count;
-               adapter->rx_rings[0]->count = new_rx_count;
-       }
+       adapter->tx_desc_count = new_tx_count;
+       adapter->rx_desc_count = new_rx_count;
 
        if (netif_running(netdev))
                i40evf_reinit_locked(adapter);
+
        return 0;
 }
 
@@ -290,14 +289,13 @@ static int i40evf_get_coalesce(struct net_device *netdev,
        ec->rx_max_coalesced_frames = vsi->work_limit;
 
        if (ITR_IS_DYNAMIC(vsi->rx_itr_setting))
-               ec->rx_coalesce_usecs = 1;
-       else
-               ec->rx_coalesce_usecs = vsi->rx_itr_setting;
+               ec->use_adaptive_rx_coalesce = 1;
 
        if (ITR_IS_DYNAMIC(vsi->tx_itr_setting))
-               ec->tx_coalesce_usecs = 1;
-       else
-               ec->tx_coalesce_usecs = vsi->tx_itr_setting;
+               ec->use_adaptive_tx_coalesce = 1;
+
+       ec->rx_coalesce_usecs = vsi->rx_itr_setting & ~I40E_ITR_DYNAMIC;
+       ec->tx_coalesce_usecs = vsi->tx_itr_setting & ~I40E_ITR_DYNAMIC;
 
        return 0;
 }
@@ -318,54 +316,361 @@ static int i40evf_set_coalesce(struct net_device *netdev,
        struct i40e_q_vector *q_vector;
        int i;
 
-       if (ec->tx_max_coalesced_frames || ec->rx_max_coalesced_frames)
-               vsi->work_limit = ec->tx_max_coalesced_frames;
+       if (ec->tx_max_coalesced_frames_irq || ec->rx_max_coalesced_frames_irq)
+               vsi->work_limit = ec->tx_max_coalesced_frames_irq;
+
+       if ((ec->rx_coalesce_usecs >= (I40E_MIN_ITR << 1)) &&
+           (ec->rx_coalesce_usecs <= (I40E_MAX_ITR << 1)))
+               vsi->rx_itr_setting = ec->rx_coalesce_usecs;
+
+       else
+               return -EINVAL;
+
+       if ((ec->tx_coalesce_usecs >= (I40E_MIN_ITR << 1)) &&
+           (ec->tx_coalesce_usecs <= (I40E_MAX_ITR << 1)))
+               vsi->tx_itr_setting = ec->tx_coalesce_usecs;
+       else if (ec->use_adaptive_tx_coalesce)
+               vsi->tx_itr_setting = (I40E_ITR_DYNAMIC |
+                                      ITR_REG_TO_USEC(I40E_ITR_RX_DEF));
+       else
+               return -EINVAL;
+
+       if (ec->use_adaptive_rx_coalesce)
+               vsi->rx_itr_setting |= I40E_ITR_DYNAMIC;
+       else
+               vsi->rx_itr_setting &= ~I40E_ITR_DYNAMIC;
+
+       if (ec->use_adaptive_tx_coalesce)
+               vsi->tx_itr_setting |= I40E_ITR_DYNAMIC;
+       else
+               vsi->tx_itr_setting &= ~I40E_ITR_DYNAMIC;
 
-       switch (ec->rx_coalesce_usecs) {
-       case 0:
-               vsi->rx_itr_setting = 0;
+       for (i = 0; i < adapter->num_msix_vectors - NONQ_VECS; i++) {
+               q_vector = adapter->q_vector[i];
+               q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting);
+               wr32(hw, I40E_VFINT_ITRN1(0, i), q_vector->rx.itr);
+               q_vector->tx.itr = ITR_TO_REG(vsi->tx_itr_setting);
+               wr32(hw, I40E_VFINT_ITRN1(1, i), q_vector->tx.itr);
+               i40e_flush(hw);
+       }
+
+       return 0;
+}
+
+/**
+ * i40e_get_rss_hash_opts - Get RSS hash Input Set for each flow type
+ * @adapter: board private structure
+ * @cmd: ethtool rxnfc command
+ *
+ * Returns Success if the flow is supported, else Invalid Input.
+ **/
+static int i40evf_get_rss_hash_opts(struct i40evf_adapter *adapter,
+                                   struct ethtool_rxnfc *cmd)
+{
+       struct i40e_hw *hw = &adapter->hw;
+       u64 hena = (u64)rd32(hw, I40E_VFQF_HENA(0)) |
+                  ((u64)rd32(hw, I40E_VFQF_HENA(1)) << 32);
+
+       /* We always hash on IP src and dest addresses */
+       cmd->data = RXH_IP_SRC | RXH_IP_DST;
+
+       switch (cmd->flow_type) {
+       case TCP_V4_FLOW:
+               if (hena & ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP))
+                       cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
                break;
-       case 1:
-               vsi->rx_itr_setting = (I40E_ITR_DYNAMIC
-                                      | ITR_REG_TO_USEC(I40E_ITR_RX_DEF));
+       case UDP_V4_FLOW:
+               if (hena & ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP))
+                       cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
                break;
-       default:
-               if ((ec->rx_coalesce_usecs < (I40E_MIN_ITR << 1)) ||
-                   (ec->rx_coalesce_usecs > (I40E_MAX_ITR << 1)))
-                       return -EINVAL;
-               vsi->rx_itr_setting = ec->rx_coalesce_usecs;
+
+       case SCTP_V4_FLOW:
+       case AH_ESP_V4_FLOW:
+       case AH_V4_FLOW:
+       case ESP_V4_FLOW:
+       case IPV4_FLOW:
+               break;
+
+       case TCP_V6_FLOW:
+               if (hena & ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP))
+                       cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
                break;
+       case UDP_V6_FLOW:
+               if (hena & ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP))
+                       cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+               break;
+
+       case SCTP_V6_FLOW:
+       case AH_ESP_V6_FLOW:
+       case AH_V6_FLOW:
+       case ESP_V6_FLOW:
+       case IPV6_FLOW:
+               break;
+       default:
+               cmd->data = 0;
+               return -EINVAL;
        }
 
-       switch (ec->tx_coalesce_usecs) {
-       case 0:
-               vsi->tx_itr_setting = 0;
+       return 0;
+}
+
+/**
+ * i40evf_get_rxnfc - command to get RX flow classification rules
+ * @netdev: network interface device structure
+ * @cmd: ethtool rxnfc command
+ *
+ * Returns Success if the command is supported.
+ **/
+static int i40evf_get_rxnfc(struct net_device *netdev,
+                           struct ethtool_rxnfc *cmd,
+                           u32 *rule_locs)
+{
+       struct i40evf_adapter *adapter = netdev_priv(netdev);
+       int ret = -EOPNOTSUPP;
+
+       switch (cmd->cmd) {
+       case ETHTOOL_GRXRINGS:
+               cmd->data = adapter->vsi_res->num_queue_pairs;
+               ret = 0;
                break;
-       case 1:
-               vsi->tx_itr_setting = (I40E_ITR_DYNAMIC
-                                      | ITR_REG_TO_USEC(I40E_ITR_TX_DEF));
+       case ETHTOOL_GRXFH:
+               ret = i40evf_get_rss_hash_opts(adapter, cmd);
                break;
        default:
-               if ((ec->tx_coalesce_usecs < (I40E_MIN_ITR << 1)) ||
-                   (ec->tx_coalesce_usecs > (I40E_MAX_ITR << 1)))
+               break;
+       }
+
+       return ret;
+}
+
+/**
+ * i40evf_set_rss_hash_opt - Enable/Disable flow types for RSS hash
+ * @adapter: board private structure
+ * @cmd: ethtool rxnfc command
+ *
+ * Returns Success if the flow input set is supported.
+ **/
+static int i40evf_set_rss_hash_opt(struct i40evf_adapter *adapter,
+                                  struct ethtool_rxnfc *nfc)
+{
+       struct i40e_hw *hw = &adapter->hw;
+
+       u64 hena = (u64)rd32(hw, I40E_VFQF_HENA(0)) |
+                  ((u64)rd32(hw, I40E_VFQF_HENA(1)) << 32);
+
+       /* RSS does not support anything other than hashing
+        * to queues on src and dst IPs and ports
+        */
+       if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST |
+                         RXH_L4_B_0_1 | RXH_L4_B_2_3))
+               return -EINVAL;
+
+       /* We need at least the IP SRC and DEST fields for hashing */
+       if (!(nfc->data & RXH_IP_SRC) ||
+           !(nfc->data & RXH_IP_DST))
+               return -EINVAL;
+
+       switch (nfc->flow_type) {
+       case TCP_V4_FLOW:
+               switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
+               case 0:
+                       hena &= ~((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP);
+                       break;
+               case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
+                       hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP);
+                       break;
+               default:
                        return -EINVAL;
-               vsi->tx_itr_setting = ec->tx_coalesce_usecs;
+               }
+               break;
+       case TCP_V6_FLOW:
+               switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
+               case 0:
+                       hena &= ~((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP);
+                       break;
+               case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
+                       hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       case UDP_V4_FLOW:
+               switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
+               case 0:
+                       hena &= ~(((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) |
+                                 ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4));
+                       break;
+               case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
+                       hena |= (((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) |
+                                ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4));
+                       break;
+               default:
+                       return -EINVAL;
+               }
                break;
+       case UDP_V6_FLOW:
+               switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
+               case 0:
+                       hena &= ~(((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) |
+                                 ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6));
+                       break;
+               case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
+                       hena |= (((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) |
+                                ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6));
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       case AH_ESP_V4_FLOW:
+       case AH_V4_FLOW:
+       case ESP_V4_FLOW:
+       case SCTP_V4_FLOW:
+               if ((nfc->data & RXH_L4_B_0_1) ||
+                   (nfc->data & RXH_L4_B_2_3))
+                       return -EINVAL;
+               hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER);
+               break;
+       case AH_ESP_V6_FLOW:
+       case AH_V6_FLOW:
+       case ESP_V6_FLOW:
+       case SCTP_V6_FLOW:
+               if ((nfc->data & RXH_L4_B_0_1) ||
+                   (nfc->data & RXH_L4_B_2_3))
+                       return -EINVAL;
+               hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_OTHER);
+               break;
+       case IPV4_FLOW:
+               hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER) |
+                       ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4);
+               break;
+       case IPV6_FLOW:
+               hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_OTHER) |
+                       ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6);
+               break;
+       default:
+               return -EINVAL;
        }
 
-       for (i = 0; i < adapter->num_msix_vectors - NONQ_VECS; i++) {
-               q_vector = adapter->q_vector[i];
-               q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting);
-               wr32(hw, I40E_VFINT_ITRN1(0, i), q_vector->rx.itr);
-               q_vector->tx.itr = ITR_TO_REG(vsi->tx_itr_setting);
-               wr32(hw, I40E_VFINT_ITRN1(1, i), q_vector->tx.itr);
-               i40e_flush(hw);
+       wr32(hw, I40E_VFQF_HENA(0), (u32)hena);
+       wr32(hw, I40E_VFQF_HENA(1), (u32)(hena >> 32));
+       i40e_flush(hw);
+
+       return 0;
+}
+
+/**
+ * i40evf_set_rxnfc - command to set RX flow classification rules
+ * @netdev: network interface device structure
+ * @cmd: ethtool rxnfc command
+ *
+ * Returns Success if the command is supported.
+ **/
+static int i40evf_set_rxnfc(struct net_device *netdev,
+                           struct ethtool_rxnfc *cmd)
+{
+       struct i40evf_adapter *adapter = netdev_priv(netdev);
+       int ret = -EOPNOTSUPP;
+
+       switch (cmd->cmd) {
+       case ETHTOOL_SRXFH:
+               ret = i40evf_set_rss_hash_opt(adapter, cmd);
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+/**
+ * i40evf_get_channels: get the number of channels supported by the device
+ * @netdev: network interface device structure
+ * @ch: channel information structure
+ *
+ * For the purposes of our device, we only use combined channels, i.e. a tx/rx
+ * queue pair. Report one extra channel to match our "other" MSI-X vector.
+ **/
+static void i40evf_get_channels(struct net_device *netdev,
+                               struct ethtool_channels *ch)
+{
+       struct i40evf_adapter *adapter = netdev_priv(netdev);
+
+       /* Report maximum channels */
+       ch->max_combined = adapter->vsi_res->num_queue_pairs;
+
+       ch->max_other = NONQ_VECS;
+       ch->other_count = NONQ_VECS;
+
+       ch->combined_count = adapter->vsi_res->num_queue_pairs;
+}
+
+/**
+ * i40evf_get_rxfh_indir_size - get the rx flow hash indirection table size
+ * @netdev: network interface device structure
+ *
+ * Returns the table size.
+ **/
+static u32 i40evf_get_rxfh_indir_size(struct net_device *netdev)
+{
+       return (I40E_VFQF_HLUT_MAX_INDEX + 1) * 4;
+}
+
+/**
+ * i40evf_get_rxfh - get the rx flow hash indirection table
+ * @netdev: network interface device structure
+ * @indir: indirection table
+ * @key: hash key (will be %NULL until get_rxfh_key_size is implemented)
+ *
+ * Reads the indirection table directly from the hardware. Always returns 0.
+ **/
+static int i40evf_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key)
+{
+       struct i40evf_adapter *adapter = netdev_priv(netdev);
+       struct i40e_hw *hw = &adapter->hw;
+       u32 hlut_val;
+       int i, j;
+
+       for (i = 0, j = 0; i < I40E_VFQF_HLUT_MAX_INDEX; i++) {
+               hlut_val = rd32(hw, I40E_VFQF_HLUT(i));
+               indir[j++] = hlut_val & 0xff;
+               indir[j++] = (hlut_val >> 8) & 0xff;
+               indir[j++] = (hlut_val >> 16) & 0xff;
+               indir[j++] = (hlut_val >> 24) & 0xff;
+       }
+       return 0;
+}
+
+/**
+ * i40evf_set_rxfh - set the rx flow hash indirection table
+ * @netdev: network interface device structure
+ * @indir: indirection table
+ * @key: hash key (will be %NULL until get_rxfh_key_size is implemented)
+ *
+ * Returns -EINVAL if the table specifies an inavlid queue id, otherwise
+ * returns 0 after programming the table.
+ **/
+static int i40evf_set_rxfh(struct net_device *netdev, const u32 *indir,
+                          const u8 *key)
+{
+       struct i40evf_adapter *adapter = netdev_priv(netdev);
+       struct i40e_hw *hw = &adapter->hw;
+       u32 hlut_val;
+       int i, j;
+
+       for (i = 0, j = 0; i < I40E_VFQF_HLUT_MAX_INDEX + 1; i++) {
+               hlut_val = indir[j++];
+               hlut_val |= indir[j++] << 8;
+               hlut_val |= indir[j++] << 16;
+               hlut_val |= indir[j++] << 24;
+               wr32(hw, I40E_VFQF_HLUT(i), hlut_val);
        }
 
        return 0;
 }
 
-static struct ethtool_ops i40evf_ethtool_ops = {
+static const struct ethtool_ops i40evf_ethtool_ops = {
        .get_settings           = i40evf_get_settings,
        .get_drvinfo            = i40evf_get_drvinfo,
        .get_link               = ethtool_op_get_link,
@@ -378,6 +683,12 @@ static struct ethtool_ops i40evf_ethtool_ops = {
        .set_msglevel           = i40evf_set_msglevel,
        .get_coalesce           = i40evf_get_coalesce,
        .set_coalesce           = i40evf_set_coalesce,
+       .get_rxnfc              = i40evf_get_rxnfc,
+       .set_rxnfc              = i40evf_set_rxnfc,
+       .get_rxfh_indir_size    = i40evf_get_rxfh_indir_size,
+       .get_rxfh               = i40evf_get_rxfh,
+       .set_rxfh               = i40evf_set_rxfh,
+       .get_channels           = i40evf_get_channels,
 };
 
 /**
@@ -389,5 +700,5 @@ static struct ethtool_ops i40evf_ethtool_ops = {
  **/
 void i40evf_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &i40evf_ethtool_ops);
+       netdev->ethtool_ops = &i40evf_ethtool_ops;
 }
index 2797548fde0dd918051a8f033472749e86d124c9..7fc5f3b5d6bf610b936ae229dd31cf93435c2ee4 100644 (file)
@@ -12,6 +12,9 @@
  * 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/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
 #include "i40e_prototype.h"
 static int i40evf_setup_all_tx_resources(struct i40evf_adapter *adapter);
 static int i40evf_setup_all_rx_resources(struct i40evf_adapter *adapter);
+static void i40evf_free_all_tx_resources(struct i40evf_adapter *adapter);
+static void i40evf_free_all_rx_resources(struct i40evf_adapter *adapter);
 static int i40evf_close(struct net_device *netdev);
 
 char i40evf_driver_name[] = "i40evf";
 static const char i40evf_driver_string[] =
        "Intel(R) XL710 X710 Virtual Function Network Driver";
 
-#define DRV_VERSION "0.9.16"
+#define DRV_VERSION "0.9.34"
 const char i40evf_driver_version[] = DRV_VERSION;
 static const char i40evf_copyright[] =
        "Copyright (c) 2013 - 2014 Intel Corporation.";
@@ -167,7 +172,6 @@ static void i40evf_tx_timeout(struct net_device *netdev)
        struct i40evf_adapter *adapter = netdev_priv(netdev);
 
        adapter->tx_timeout_count++;
-       dev_info(&adapter->pdev->dev, "TX timeout detected.\n");
        if (!(adapter->flags & I40EVF_FLAG_RESET_PENDING)) {
                adapter->flags |= I40EVF_FLAG_RESET_NEEDED;
                schedule_work(&adapter->reset_task);
@@ -657,12 +661,9 @@ i40evf_vlan_filter *i40evf_add_vlan(struct i40evf_adapter *adapter, u16 vlan)
        f = i40evf_find_vlan(adapter, vlan);
        if (NULL == f) {
                f = kzalloc(sizeof(*f), GFP_ATOMIC);
-               if (NULL == f) {
-                       dev_info(&adapter->pdev->dev,
-                                "%s: no memory for new VLAN filter\n",
-                                __func__);
+               if (NULL == f)
                        return NULL;
-               }
+
                f->vlan = vlan;
 
                INIT_LIST_HEAD(&f->list);
@@ -688,7 +689,6 @@ static void i40evf_del_vlan(struct i40evf_adapter *adapter, u16 vlan)
                f->remove = true;
                adapter->aq_required |= I40EVF_FLAG_AQ_DEL_VLAN_FILTER;
        }
-       return;
 }
 
 /**
@@ -767,14 +767,12 @@ i40evf_mac_filter *i40evf_add_filter(struct i40evf_adapter *adapter,
        if (NULL == f) {
                f = kzalloc(sizeof(*f), GFP_ATOMIC);
                if (NULL == f) {
-                       dev_info(&adapter->pdev->dev,
-                                "%s: no memory for new filter\n", __func__);
                        clear_bit(__I40EVF_IN_CRITICAL_TASK,
                                  &adapter->crit_section);
                        return NULL;
                }
 
-               memcpy(f->macaddr, macaddr, ETH_ALEN);
+               ether_addr_copy(f->macaddr, macaddr);
 
                list_add(&f->list, &adapter->mac_filter_list);
                f->add = true;
@@ -807,9 +805,8 @@ static int i40evf_set_mac(struct net_device *netdev, void *p)
 
        f = i40evf_add_filter(adapter, addr->sa_data);
        if (f) {
-               memcpy(hw->mac.addr, addr->sa_data, netdev->addr_len);
-               memcpy(netdev->dev_addr, adapter->hw.mac.addr,
-                      netdev->addr_len);
+               ether_addr_copy(hw->mac.addr, addr->sa_data);
+               ether_addr_copy(netdev->dev_addr, adapter->hw.mac.addr);
        }
 
        return (f == NULL) ? -ENOMEM : 0;
@@ -841,7 +838,7 @@ static void i40evf_set_rx_mode(struct net_device *netdev)
        list_for_each_entry_safe(f, ftmp, &adapter->mac_filter_list, list) {
                bool found = false;
 
-               if (f->macaddr[0] & 0x01) {
+               if (is_multicast_ether_addr(f->macaddr)) {
                        netdev_for_each_mc_addr(mca, netdev) {
                                if (ether_addr_equal(mca->addr, f->macaddr)) {
                                        found = true;
@@ -970,6 +967,9 @@ void i40evf_down(struct i40evf_adapter *adapter)
        struct net_device *netdev = adapter->netdev;
        struct i40evf_mac_filter *f;
 
+       if (adapter->state == __I40EVF_DOWN)
+               return;
+
        /* remove all MAC filters */
        list_for_each_entry(f, &adapter->mac_filter_list, list) {
                f->remove = true;
@@ -1027,30 +1027,21 @@ i40evf_acquire_msix_vectors(struct i40evf_adapter *adapter, int vectors)
         * Right now, we simply care about how many we'll get; we'll
         * set them up later while requesting irq's.
         */
-       while (vectors >= vector_threshold) {
-               err = pci_enable_msix(adapter->pdev, adapter->msix_entries,
-                                     vectors);
-               if (!err) /* Success in acquiring all requested vectors. */
-                       break;
-               else if (err < 0)
-                       vectors = 0; /* Nasty failure, quit now */
-               else /* err == number of vectors we should try again with */
-                       vectors = err;
-       }
-
-       if (vectors < vector_threshold) {
-               dev_err(&adapter->pdev->dev, "Unable to allocate MSI-X interrupts.\n");
+       err = pci_enable_msix_range(adapter->pdev, adapter->msix_entries,
+                                   vector_threshold, vectors);
+       if (err < 0) {
+               dev_err(&adapter->pdev->dev, "Unable to allocate MSI-X interrupts\n");
                kfree(adapter->msix_entries);
                adapter->msix_entries = NULL;
-               err = -EIO;
-       } else {
-               /* Adjust for only the vectors we'll use, which is minimum
-                * of max_msix_q_vectors + NONQ_VECS, or the number of
-                * vectors we were allocated.
-                */
-               adapter->num_msix_vectors = vectors;
+               return err;
        }
-       return err;
+
+       /* Adjust for only the vectors we'll use, which is minimum
+        * of max_msix_q_vectors + NONQ_VECS, or the number of
+        * vectors we were allocated.
+        */
+       adapter->num_msix_vectors = err;
+       return 0;
 }
 
 /**
@@ -1096,14 +1087,14 @@ static int i40evf_alloc_queues(struct i40evf_adapter *adapter)
                tx_ring->queue_index = i;
                tx_ring->netdev = adapter->netdev;
                tx_ring->dev = &adapter->pdev->dev;
-               tx_ring->count = I40EVF_DEFAULT_TXD;
+               tx_ring->count = adapter->tx_desc_count;
                adapter->tx_rings[i] = tx_ring;
 
                rx_ring = &tx_ring[1];
                rx_ring->queue_index = i;
                rx_ring->netdev = adapter->netdev;
                rx_ring->dev = &adapter->pdev->dev;
-               rx_ring->count = I40EVF_DEFAULT_RXD;
+               rx_ring->count = adapter->rx_desc_count;
                adapter->rx_rings[i] = rx_ring;
        }
 
@@ -1141,9 +1132,6 @@ static int i40evf_set_interrupt_capability(struct i40evf_adapter *adapter)
        v_budget = min_t(int, pairs, (int)(num_online_cpus() * 2)) + NONQ_VECS;
        v_budget = min_t(int, v_budget, (int)adapter->vf_res->max_vectors);
 
-       /* A failure in MSI-X entry allocation isn't fatal, but it does
-        * mean we disable MSI-X capabilities of the adapter.
-        */
        adapter->msix_entries = kcalloc(v_budget,
                                        sizeof(struct msix_entry), GFP_KERNEL);
        if (!adapter->msix_entries) {
@@ -1183,7 +1171,7 @@ static int i40evf_alloc_q_vectors(struct i40evf_adapter *adapter)
                q_vector->vsi = &adapter->vsi;
                q_vector->v_idx = q_idx;
                netif_napi_add(adapter->netdev, &q_vector->napi,
-                                      i40evf_napi_poll, 64);
+                                      i40evf_napi_poll, NAPI_POLL_WEIGHT);
                adapter->q_vector[q_idx] = q_vector;
        }
 
@@ -1236,8 +1224,6 @@ void i40evf_reset_interrupt_capability(struct i40evf_adapter *adapter)
        pci_disable_msix(adapter->pdev);
        kfree(adapter->msix_entries);
        adapter->msix_entries = NULL;
-
-       return;
 }
 
 /**
@@ -1309,7 +1295,6 @@ static void i40evf_watchdog_task(struct work_struct *work)
                goto restart_watchdog;
 
        if (adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED) {
-               dev_info(&adapter->pdev->dev, "Checking for redemption\n");
                if ((rd32(hw, I40E_VFGEN_RSTAT) & 0x3) == I40E_VFR_VFACTIVE) {
                        /* A chance for redemption! */
                        dev_err(&adapter->pdev->dev, "Hardware came out of reset. Attempting reinit.\n");
@@ -1340,8 +1325,7 @@ static void i40evf_watchdog_task(struct work_struct *work)
            (rd32(hw, I40E_VFGEN_RSTAT) & 0x3) != I40E_VFR_VFACTIVE) {
                adapter->state = __I40EVF_RESETTING;
                adapter->flags |= I40EVF_FLAG_RESET_PENDING;
-               dev_err(&adapter->pdev->dev, "Hardware reset detected.\n");
-               dev_info(&adapter->pdev->dev, "Scheduling reset task\n");
+               dev_err(&adapter->pdev->dev, "Hardware reset detected\n");
                schedule_work(&adapter->reset_task);
                adapter->aq_pending = 0;
                adapter->aq_required = 0;
@@ -1413,7 +1397,7 @@ restart_watchdog:
 }
 
 /**
- * i40evf_configure_rss - increment to next available tx queue
+ * next_queue - increment to next available tx queue
  * @adapter: board private structure
  * @j: queue counter
  *
@@ -1504,15 +1488,12 @@ static void i40evf_reset_task(struct work_struct *work)
        for (i = 0; i < I40EVF_RESET_WAIT_COUNT; i++) {
                rstat_val = rd32(hw, I40E_VFGEN_RSTAT) &
                            I40E_VFGEN_RSTAT_VFR_STATE_MASK;
-               if (rstat_val != I40E_VFR_VFACTIVE) {
-                       dev_info(&adapter->pdev->dev, "Reset now occurring\n");
+               if (rstat_val != I40E_VFR_VFACTIVE)
                        break;
-               } else {
+               else
                        msleep(I40EVF_RESET_WAIT_MS);
-               }
        }
        if (i == I40EVF_RESET_WAIT_COUNT) {
-               dev_err(&adapter->pdev->dev, "Reset was not detected\n");
                adapter->flags &= ~I40EVF_FLAG_RESET_PENDING;
                goto continue_reset; /* act like the reset happened */
        }
@@ -1521,22 +1502,24 @@ static void i40evf_reset_task(struct work_struct *work)
        for (i = 0; i < I40EVF_RESET_WAIT_COUNT; i++) {
                rstat_val = rd32(hw, I40E_VFGEN_RSTAT) &
                            I40E_VFGEN_RSTAT_VFR_STATE_MASK;
-               if (rstat_val == I40E_VFR_VFACTIVE) {
-                       dev_info(&adapter->pdev->dev, "Reset is complete. Reinitializing.\n");
+               if (rstat_val == I40E_VFR_VFACTIVE)
                        break;
-               } else {
+               else
                        msleep(I40EVF_RESET_WAIT_MS);
-               }
        }
        if (i == I40EVF_RESET_WAIT_COUNT) {
                /* reset never finished */
-               dev_err(&adapter->pdev->dev, "Reset never finished (%x). PF driver is dead, and so am I.\n",
+               dev_err(&adapter->pdev->dev, "Reset never finished (%x)\n",
                        rstat_val);
                adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED;
 
-               if (netif_running(adapter->netdev))
-                       i40evf_close(adapter->netdev);
-
+               if (netif_running(adapter->netdev)) {
+                       set_bit(__I40E_DOWN, &adapter->vsi.state);
+                       i40evf_down(adapter);
+                       i40evf_free_traffic_irqs(adapter);
+                       i40evf_free_all_tx_resources(adapter);
+                       i40evf_free_all_rx_resources(adapter);
+               }
                i40evf_free_misc_irq(adapter);
                i40evf_reset_interrupt_capability(adapter);
                i40evf_free_queues(adapter);
@@ -1591,7 +1574,7 @@ continue_reset:
        }
        return;
 reset_err:
-       dev_err(&adapter->pdev->dev, "failed to allocate resources during reinit.\n");
+       dev_err(&adapter->pdev->dev, "failed to allocate resources during reinit\n");
        i40evf_close(adapter->netdev);
 }
 
@@ -1607,6 +1590,7 @@ static void i40evf_adminq_task(struct work_struct *work)
        struct i40e_arq_event_info event;
        struct i40e_virtchnl_msg *v_msg;
        i40e_status ret;
+       u32 val, oldval;
        u16 pending;
 
        if (adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED)
@@ -1614,11 +1598,9 @@ static void i40evf_adminq_task(struct work_struct *work)
 
        event.msg_size = I40EVF_MAX_AQ_BUF_SIZE;
        event.msg_buf = kzalloc(event.msg_size, GFP_KERNEL);
-       if (!event.msg_buf) {
-               dev_info(&adapter->pdev->dev, "%s: no memory for ARQ clean\n",
-                                __func__);
+       if (!event.msg_buf)
                return;
-       }
+
        v_msg = (struct i40e_virtchnl_msg *)&event.desc;
        do {
                ret = i40evf_clean_arq_element(hw, &event, &pending);
@@ -1636,6 +1618,41 @@ static void i40evf_adminq_task(struct work_struct *work)
                }
        } while (pending);
 
+       /* check for error indications */
+       val = rd32(hw, hw->aq.arq.len);
+       oldval = val;
+       if (val & I40E_VF_ARQLEN_ARQVFE_MASK) {
+               dev_info(&adapter->pdev->dev, "ARQ VF Error detected\n");
+               val &= ~I40E_VF_ARQLEN_ARQVFE_MASK;
+       }
+       if (val & I40E_VF_ARQLEN_ARQOVFL_MASK) {
+               dev_info(&adapter->pdev->dev, "ARQ Overflow Error detected\n");
+               val &= ~I40E_VF_ARQLEN_ARQOVFL_MASK;
+       }
+       if (val & I40E_VF_ARQLEN_ARQCRIT_MASK) {
+               dev_info(&adapter->pdev->dev, "ARQ Critical Error detected\n");
+               val &= ~I40E_VF_ARQLEN_ARQCRIT_MASK;
+       }
+       if (oldval != val)
+               wr32(hw, hw->aq.arq.len, val);
+
+       val = rd32(hw, hw->aq.asq.len);
+       oldval = val;
+       if (val & I40E_VF_ATQLEN_ATQVFE_MASK) {
+               dev_info(&adapter->pdev->dev, "ASQ VF Error detected\n");
+               val &= ~I40E_VF_ATQLEN_ATQVFE_MASK;
+       }
+       if (val & I40E_VF_ATQLEN_ATQOVFL_MASK) {
+               dev_info(&adapter->pdev->dev, "ASQ Overflow Error detected\n");
+               val &= ~I40E_VF_ATQLEN_ATQOVFL_MASK;
+       }
+       if (val & I40E_VF_ATQLEN_ATQCRIT_MASK) {
+               dev_info(&adapter->pdev->dev, "ASQ Critical Error detected\n");
+               val &= ~I40E_VF_ATQLEN_ATQCRIT_MASK;
+       }
+       if (oldval != val)
+               wr32(hw, hw->aq.asq.len, val);
+
        /* re-enable Admin queue interrupt cause */
        i40evf_misc_irq_enable(adapter);
 
@@ -1673,6 +1690,7 @@ static int i40evf_setup_all_tx_resources(struct i40evf_adapter *adapter)
        int i, err = 0;
 
        for (i = 0; i < adapter->vsi_res->num_queue_pairs; i++) {
+               adapter->tx_rings[i]->count = adapter->tx_desc_count;
                err = i40evf_setup_tx_descriptors(adapter->tx_rings[i]);
                if (!err)
                        continue;
@@ -1700,6 +1718,7 @@ static int i40evf_setup_all_rx_resources(struct i40evf_adapter *adapter)
        int i, err = 0;
 
        for (i = 0; i < adapter->vsi_res->num_queue_pairs; i++) {
+               adapter->rx_rings[i]->count = adapter->rx_desc_count;
                err = i40evf_setup_rx_descriptors(adapter->rx_rings[i]);
                if (!err)
                        continue;
@@ -1804,12 +1823,11 @@ static int i40evf_close(struct net_device *netdev)
        if (adapter->state <= __I40EVF_DOWN)
                return 0;
 
-       /* signal that we are down to the interrupt handler */
-       adapter->state = __I40EVF_DOWN;
 
        set_bit(__I40E_DOWN, &adapter->vsi.state);
 
        i40evf_down(adapter);
+       adapter->state = __I40EVF_DOWN;
        i40evf_free_traffic_irqs(adapter);
 
        i40evf_free_all_tx_resources(adapter);
@@ -1848,8 +1866,6 @@ void i40evf_reinit_locked(struct i40evf_adapter *adapter)
 
        WARN_ON(in_interrupt());
 
-       adapter->state = __I40EVF_RESETTING;
-
        i40evf_down(adapter);
 
        /* allocate transmit descriptors */
@@ -1872,7 +1888,7 @@ void i40evf_reinit_locked(struct i40evf_adapter *adapter)
        return;
 
 err_reinit:
-       dev_err(&adapter->pdev->dev, "failed to allocate resources during reinit.\n");
+       dev_err(&adapter->pdev->dev, "failed to allocate resources during reinit\n");
        i40evf_close(netdev);
 }
 
@@ -1967,7 +1983,7 @@ static void i40evf_init_task(struct work_struct *work)
                }
                err = i40evf_check_reset_complete(hw);
                if (err) {
-                       dev_err(&pdev->dev, "Device is still in reset (%d)\n",
+                       dev_info(&pdev->dev, "Device is still in reset (%d), retrying\n",
                                err);
                        goto err;
                }
@@ -1993,14 +2009,14 @@ static void i40evf_init_task(struct work_struct *work)
                break;
        case __I40EVF_INIT_VERSION_CHECK:
                if (!i40evf_asq_done(hw)) {
-                       dev_err(&pdev->dev, "Admin queue command never completed.\n");
+                       dev_err(&pdev->dev, "Admin queue command never completed\n");
                        goto err;
                }
 
                /* aq msg sent, awaiting reply */
                err = i40evf_verify_api_ver(adapter);
                if (err) {
-                       dev_err(&pdev->dev, "Unable to verify API version (%d)\n",
+                       dev_info(&pdev->dev, "Unable to verify API version (%d), retrying\n",
                                err);
                        goto err;
                }
@@ -2074,12 +2090,12 @@ static void i40evf_init_task(struct work_struct *work)
        netdev->hw_features &= ~NETIF_F_RXCSUM;
 
        if (!is_valid_ether_addr(adapter->hw.mac.addr)) {
-               dev_info(&pdev->dev, "Invalid MAC address %pMAC, using random\n",
+               dev_info(&pdev->dev, "Invalid MAC address %pM, using random\n",
                         adapter->hw.mac.addr);
                random_ether_addr(adapter->hw.mac.addr);
        }
-       memcpy(netdev->dev_addr, adapter->hw.mac.addr, netdev->addr_len);
-       memcpy(netdev->perm_addr, adapter->hw.mac.addr, netdev->addr_len);
+       ether_addr_copy(netdev->dev_addr, adapter->hw.mac.addr);
+       ether_addr_copy(netdev->perm_addr, adapter->hw.mac.addr);
 
        INIT_LIST_HEAD(&adapter->mac_filter_list);
        INIT_LIST_HEAD(&adapter->vlan_filter_list);
@@ -2087,7 +2103,7 @@ static void i40evf_init_task(struct work_struct *work)
        if (NULL == f)
                goto err_sw_init;
 
-       memcpy(f->macaddr, adapter->hw.mac.addr, ETH_ALEN);
+       ether_addr_copy(f->macaddr, adapter->hw.mac.addr);
        f->add = true;
        adapter->aq_required |= I40EVF_FLAG_AQ_ADD_MAC_FILTER;
 
@@ -2098,6 +2114,8 @@ static void i40evf_init_task(struct work_struct *work)
        adapter->watchdog_timer.data = (unsigned long)adapter;
        mod_timer(&adapter->watchdog_timer, jiffies + 1);
 
+       adapter->tx_desc_count = I40EVF_DEFAULT_TXD;
+       adapter->rx_desc_count = I40EVF_DEFAULT_RXD;
        err = i40evf_init_interrupt_scheme(adapter);
        if (err)
                goto err_sw_init;
@@ -2114,8 +2132,10 @@ static void i40evf_init_task(struct work_struct *work)
        adapter->vsi.back = adapter;
        adapter->vsi.base_vector = 1;
        adapter->vsi.work_limit = I40E_DEFAULT_IRQ_WORK;
-       adapter->vsi.rx_itr_setting = I40E_ITR_DYNAMIC;
-       adapter->vsi.tx_itr_setting = I40E_ITR_DYNAMIC;
+       adapter->vsi.rx_itr_setting = (I40E_ITR_DYNAMIC |
+                                      ITR_REG_TO_USEC(I40E_ITR_RX_DEF));
+       adapter->vsi.tx_itr_setting = (I40E_ITR_DYNAMIC |
+                                      ITR_REG_TO_USEC(I40E_ITR_TX_DEF));
        adapter->vsi.netdev = adapter->netdev;
 
        if (!adapter->netdev_registered) {
@@ -2128,7 +2148,7 @@ static void i40evf_init_task(struct work_struct *work)
 
        netif_tx_stop_all_queues(netdev);
 
-       dev_info(&pdev->dev, "MAC address: %pMAC\n", adapter->hw.mac.addr);
+       dev_info(&pdev->dev, "MAC address: %pM\n", adapter->hw.mac.addr);
        if (netdev->features & NETIF_F_GRO)
                dev_info(&pdev->dev, "GRO is enabled\n");
 
@@ -2152,12 +2172,11 @@ err_alloc:
 err:
        /* Things went into the weeds, so try again later */
        if (++adapter->aq_wait_count > I40EVF_AQ_MAX_ERR) {
-               dev_err(&pdev->dev, "Failed to communicate with PF; giving up.\n");
+               dev_err(&pdev->dev, "Failed to communicate with PF; giving up\n");
                adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED;
                return; /* do not reschedule */
        }
        schedule_delayed_work(&adapter->init_task, HZ * 3);
-       return;
 }
 
 /**
index e294f012647d801417af4ca1f68d0629cbaf08cc..2dc0bac7671789fa32cec15d143c217c9b99fef8 100644 (file)
@@ -12,6 +12,9 @@
  * 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/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -216,11 +219,9 @@ void i40evf_configure_queues(struct i40evf_adapter *adapter)
        len = sizeof(struct i40e_virtchnl_vsi_queue_config_info) +
                       (sizeof(struct i40e_virtchnl_queue_pair_info) * pairs);
        vqci = kzalloc(len, GFP_ATOMIC);
-       if (!vqci) {
-               dev_err(&adapter->pdev->dev, "%s: unable to allocate memory\n",
-                       __func__);
+       if (!vqci)
                return;
-       }
+
        vqci->vsi_id = adapter->vsi_res->vsi_id;
        vqci->num_queue_pairs = pairs;
        vqpi = vqci->qpair;
@@ -232,6 +233,9 @@ void i40evf_configure_queues(struct i40evf_adapter *adapter)
                vqpi->txq.queue_id = i;
                vqpi->txq.ring_len = adapter->tx_rings[i]->count;
                vqpi->txq.dma_ring_addr = adapter->tx_rings[i]->dma;
+               vqpi->txq.headwb_enabled = 1;
+               vqpi->txq.dma_headwb_addr = vqpi->txq.dma_ring_addr +
+                   (vqpi->txq.ring_len * sizeof(struct i40e_tx_desc));
 
                vqpi->rxq.vsi_id = vqci->vsi_id;
                vqpi->rxq.queue_id = i;
@@ -329,11 +333,8 @@ void i40evf_map_queues(struct i40evf_adapter *adapter)
              (adapter->num_msix_vectors *
                sizeof(struct i40e_virtchnl_vector_map));
        vimi = kzalloc(len, GFP_ATOMIC);
-       if (!vimi) {
-               dev_err(&adapter->pdev->dev, "%s: unable to allocate memory\n",
-                       __func__);
+       if (!vimi)
                return;
-       }
 
        vimi->num_vectors = adapter->num_msix_vectors;
        /* Queue vectors first */
@@ -390,7 +391,7 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter)
        len = sizeof(struct i40e_virtchnl_ether_addr_list) +
              (count * sizeof(struct i40e_virtchnl_ether_addr));
        if (len > I40EVF_MAX_AQ_BUF_SIZE) {
-               dev_warn(&adapter->pdev->dev, "%s: Too many MAC address changes in one request.\n",
+               dev_warn(&adapter->pdev->dev, "%s: Too many MAC address changes in one request\n",
                        __func__);
                count = (I40EVF_MAX_AQ_BUF_SIZE -
                         sizeof(struct i40e_virtchnl_ether_addr_list)) /
@@ -399,16 +400,14 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter)
        }
 
        veal = kzalloc(len, GFP_ATOMIC);
-       if (!veal) {
-               dev_err(&adapter->pdev->dev, "%s: unable to allocate memory\n",
-                       __func__);
+       if (!veal)
                return;
-       }
+
        veal->vsi_id = adapter->vsi_res->vsi_id;
        veal->num_elements = count;
        list_for_each_entry(f, &adapter->mac_filter_list, list) {
                if (f->add) {
-                       memcpy(veal->list[i].addr, f->macaddr, ETH_ALEN);
+                       ether_addr_copy(veal->list[i].addr, f->macaddr);
                        i++;
                        f->add = false;
                }
@@ -454,7 +453,7 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter)
        len = sizeof(struct i40e_virtchnl_ether_addr_list) +
              (count * sizeof(struct i40e_virtchnl_ether_addr));
        if (len > I40EVF_MAX_AQ_BUF_SIZE) {
-               dev_warn(&adapter->pdev->dev, "%s: Too many MAC address changes in one request.\n",
+               dev_warn(&adapter->pdev->dev, "%s: Too many MAC address changes in one request\n",
                        __func__);
                count = (I40EVF_MAX_AQ_BUF_SIZE -
                         sizeof(struct i40e_virtchnl_ether_addr_list)) /
@@ -462,16 +461,14 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter)
                len = I40EVF_MAX_AQ_BUF_SIZE;
        }
        veal = kzalloc(len, GFP_ATOMIC);
-       if (!veal) {
-               dev_err(&adapter->pdev->dev, "%s: unable to allocate memory\n",
-                       __func__);
+       if (!veal)
                return;
-       }
+
        veal->vsi_id = adapter->vsi_res->vsi_id;
        veal->num_elements = count;
        list_for_each_entry_safe(f, ftmp, &adapter->mac_filter_list, list) {
                if (f->remove) {
-                       memcpy(veal->list[i].addr, f->macaddr, ETH_ALEN);
+                       ether_addr_copy(veal->list[i].addr, f->macaddr);
                        i++;
                        list_del(&f->list);
                        kfree(f);
@@ -518,7 +515,7 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter)
        len = sizeof(struct i40e_virtchnl_vlan_filter_list) +
              (count * sizeof(u16));
        if (len > I40EVF_MAX_AQ_BUF_SIZE) {
-               dev_warn(&adapter->pdev->dev, "%s: Too many VLAN changes in one request.\n",
+               dev_warn(&adapter->pdev->dev, "%s: Too many VLAN changes in one request\n",
                        __func__);
                count = (I40EVF_MAX_AQ_BUF_SIZE -
                         sizeof(struct i40e_virtchnl_vlan_filter_list)) /
@@ -526,11 +523,9 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter)
                len = I40EVF_MAX_AQ_BUF_SIZE;
        }
        vvfl = kzalloc(len, GFP_ATOMIC);
-       if (!vvfl) {
-               dev_err(&adapter->pdev->dev, "%s: unable to allocate memory\n",
-                       __func__);
+       if (!vvfl)
                return;
-       }
+
        vvfl->vsi_id = adapter->vsi_res->vsi_id;
        vvfl->num_elements = count;
        list_for_each_entry(f, &adapter->vlan_filter_list, list) {
@@ -580,7 +575,7 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter)
        len = sizeof(struct i40e_virtchnl_vlan_filter_list) +
              (count * sizeof(u16));
        if (len > I40EVF_MAX_AQ_BUF_SIZE) {
-               dev_warn(&adapter->pdev->dev, "%s: Too many VLAN changes in one request.\n",
+               dev_warn(&adapter->pdev->dev, "%s: Too many VLAN changes in one request\n",
                        __func__);
                count = (I40EVF_MAX_AQ_BUF_SIZE -
                         sizeof(struct i40e_virtchnl_vlan_filter_list)) /
@@ -588,11 +583,9 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter)
                len = I40EVF_MAX_AQ_BUF_SIZE;
        }
        vvfl = kzalloc(len, GFP_ATOMIC);
-       if (!vvfl) {
-               dev_err(&adapter->pdev->dev, "%s: unable to allocate memory\n",
-                       __func__);
+       if (!vvfl)
                return;
-       }
+
        vvfl->vsi_id = adapter->vsi_res->vsi_id;
        vvfl->num_elements = count;
        list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, list) {
@@ -721,7 +714,7 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
                return;
        }
        if (v_opcode != adapter->current_op) {
-               dev_err(&adapter->pdev->dev, "%s: Pending op is %d, received %d.\n",
+               dev_err(&adapter->pdev->dev, "%s: Pending op is %d, received %d\n",
                        __func__, adapter->current_op, v_opcode);
                /* We're probably completely screwed at this point, but clear
                 * the current op and try to carry on....
@@ -730,7 +723,7 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
                return;
        }
        if (v_retval) {
-               dev_err(&adapter->pdev->dev, "%s: PF returned error %d to our request %d!\n",
+               dev_err(&adapter->pdev->dev, "%s: PF returned error %d to our request %d\n",
                        __func__, v_retval, v_opcode);
        }
        switch (v_opcode) {
@@ -745,9 +738,8 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
                                                 stats->tx_broadcast;
                adapter->net_stats.rx_bytes = stats->rx_bytes;
                adapter->net_stats.tx_bytes = stats->tx_bytes;
-               adapter->net_stats.rx_errors = stats->rx_errors;
                adapter->net_stats.tx_errors = stats->tx_errors;
-               adapter->net_stats.rx_dropped = stats->rx_missed;
+               adapter->net_stats.rx_dropped = stats->rx_discards;
                adapter->net_stats.tx_dropped = stats->tx_discards;
                adapter->current_stats = *stats;
                }
@@ -781,7 +773,7 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
                adapter->aq_pending &= ~(I40EVF_FLAG_AQ_MAP_VECTORS);
                break;
        default:
-               dev_warn(&adapter->pdev->dev, "%s: Received unexpected message %d from PF.\n",
+               dev_warn(&adapter->pdev->dev, "%s: Received unexpected message %d from PF\n",
                        __func__, v_opcode);
                break;
        } /* switch v_opcode */
index fa36fe12e77502658cfe864780849d6f00e93c2e..a2db388cc31e6018c892ddb2932e95ba869a09ec 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 /* e1000_82575
  * e1000_82576
@@ -73,9 +70,8 @@ static s32  igb_validate_nvm_checksum_82580(struct e1000_hw *hw);
 static s32  igb_update_nvm_checksum_82580(struct e1000_hw *hw);
 static s32 igb_validate_nvm_checksum_i350(struct e1000_hw *hw);
 static s32 igb_update_nvm_checksum_i350(struct e1000_hw *hw);
-static const u16 e1000_82580_rxpbs_table[] =
-       { 36, 72, 144, 1, 2, 4, 8, 16,
-         35, 70, 140 };
+static const u16 e1000_82580_rxpbs_table[] = {
+       36, 72, 144, 1, 2, 4, 8, 16, 35, 70, 140 };
 
 /**
  *  igb_sgmii_uses_mdio_82575 - Determine if I2C pins are for external MDIO
@@ -159,7 +155,7 @@ static s32 igb_check_for_link_media_swap(struct e1000_hw *hw)
                ret_val = igb_check_for_link_82575(hw);
        }
 
-       return E1000_SUCCESS;
+       return 0;
 }
 
 /**
@@ -526,7 +522,7 @@ out:
 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;
+       struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575;
        s32 ret_val;
        u32 ctrl_ext = 0;
        u32 link_mode = 0;
@@ -1008,7 +1004,6 @@ out:
 static s32 igb_set_d0_lplu_state_82580(struct e1000_hw *hw, bool active)
 {
        struct e1000_phy_info *phy = &hw->phy;
-       s32 ret_val = 0;
        u16 data;
 
        data = rd32(E1000_82580_PHY_POWER_MGMT);
@@ -1032,7 +1027,7 @@ static s32 igb_set_d0_lplu_state_82580(struct e1000_hw *hw, bool active)
                        data &= ~E1000_82580_PM_SPD; }
 
        wr32(E1000_82580_PHY_POWER_MGMT, data);
-       return ret_val;
+       return 0;
 }
 
 /**
@@ -1052,7 +1047,6 @@ static s32 igb_set_d0_lplu_state_82580(struct e1000_hw *hw, bool active)
 static s32 igb_set_d3_lplu_state_82580(struct e1000_hw *hw, bool active)
 {
        struct e1000_phy_info *phy = &hw->phy;
-       s32 ret_val = 0;
        u16 data;
 
        data = rd32(E1000_82580_PHY_POWER_MGMT);
@@ -1077,7 +1071,7 @@ static s32 igb_set_d3_lplu_state_82580(struct e1000_hw *hw, bool active)
        }
 
        wr32(E1000_82580_PHY_POWER_MGMT, data);
-       return ret_val;
+       return 0;
 }
 
 /**
@@ -1180,8 +1174,8 @@ static void igb_release_swfw_sync_82575(struct e1000_hw *hw, u16 mask)
 {
        u32 swfw_sync;
 
-       while (igb_get_hw_semaphore(hw) != 0);
-       /* Empty */
+       while (igb_get_hw_semaphore(hw) != 0)
+               ; /* Empty */
 
        swfw_sync = rd32(E1000_SW_FW_SYNC);
        swfw_sync &= ~mask;
@@ -1203,7 +1197,6 @@ static void igb_release_swfw_sync_82575(struct e1000_hw *hw, u16 mask)
 static s32 igb_get_cfg_done_82575(struct e1000_hw *hw)
 {
        s32 timeout = PHY_CFG_TIMEOUT;
-       s32 ret_val = 0;
        u32 mask = E1000_NVM_CFG_DONE_PORT_0;
 
        if (hw->bus.func == 1)
@@ -1216,7 +1209,7 @@ static s32 igb_get_cfg_done_82575(struct e1000_hw *hw)
        while (timeout) {
                if (rd32(E1000_EEMNGCTL) & mask)
                        break;
-               msleep(1);
+               usleep_range(1000, 2000);
                timeout--;
        }
        if (!timeout)
@@ -1227,7 +1220,7 @@ static s32 igb_get_cfg_done_82575(struct e1000_hw *hw)
            (hw->phy.type == e1000_phy_igp_3))
                igb_phy_init_script_igp3(hw);
 
-       return ret_val;
+       return 0;
 }
 
 /**
@@ -1269,7 +1262,7 @@ static s32 igb_check_for_link_82575(struct e1000_hw *hw)
 
        if (hw->phy.media_type != e1000_media_type_copper) {
                ret_val = igb_get_pcs_speed_and_duplex_82575(hw, &speed,
-                                                            &duplex);
+                                                            &duplex);
                /* Use this flag to determine if link needs to be checked or
                 * not.  If  we have link clear the flag so that we do not
                 * continue to check for link.
@@ -1316,7 +1309,7 @@ void igb_power_up_serdes_link_82575(struct e1000_hw *hw)
 
        /* flush the write to verify completion */
        wrfl();
-       msleep(1);
+       usleep_range(1000, 2000);
 }
 
 /**
@@ -1411,7 +1404,7 @@ void igb_shutdown_serdes_link_82575(struct e1000_hw *hw)
 
                /* flush the write to verify completion */
                wrfl();
-               msleep(1);
+               usleep_range(1000, 2000);
        }
 }
 
@@ -1436,9 +1429,8 @@ static s32 igb_reset_hw_82575(struct e1000_hw *hw)
 
        /* set the completion timeout for interface */
        ret_val = igb_set_pcie_completion_timeout(hw);
-       if (ret_val) {
+       if (ret_val)
                hw_dbg("PCI-E Set completion timeout has failed.\n");
-       }
 
        hw_dbg("Masking off all interrupts\n");
        wr32(E1000_IMC, 0xffffffff);
@@ -1447,7 +1439,7 @@ static s32 igb_reset_hw_82575(struct e1000_hw *hw)
        wr32(E1000_TCTL, E1000_TCTL_PSP);
        wrfl();
 
-       msleep(10);
+       usleep_range(10000, 20000);
 
        ctrl = rd32(E1000_CTRL);
 
@@ -1622,7 +1614,7 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw)
 {
        u32 ctrl_ext, ctrl_reg, reg, anadv_reg;
        bool pcs_autoneg;
-       s32 ret_val = E1000_SUCCESS;
+       s32 ret_val = 0;
        u16 data;
 
        if ((hw->phy.media_type != e1000_media_type_internal_serdes) &&
@@ -1676,7 +1668,7 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw)
                    hw->mac.type == e1000_82576) {
                        ret_val = hw->nvm.ops.read(hw, NVM_COMPAT, 1, &data);
                        if (ret_val) {
-                               printk(KERN_DEBUG "NVM Read Error\n\n");
+                               hw_dbg(KERN_DEBUG "NVM Read Error\n\n");
                                return ret_val;
                        }
 
@@ -1689,7 +1681,7 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw)
                 * link either autoneg or be forced to 1000/Full
                 */
                ctrl_reg |= E1000_CTRL_SPD_1000 | E1000_CTRL_FRCSPD |
-                           E1000_CTRL_FD | E1000_CTRL_FRCDPX;
+                               E1000_CTRL_FD | E1000_CTRL_FRCDPX;
 
                /* set speed of 1000/Full if speed/duplex is forced */
                reg |= E1000_PCS_LCTL_FSV_1000 | E1000_PCS_LCTL_FDV_FULL;
@@ -1925,7 +1917,7 @@ void igb_rx_fifo_flush_82575(struct e1000_hw *hw)
        }
        /* Poll all queues to verify they have shut down */
        for (ms_wait = 0; ms_wait < 10; ms_wait++) {
-               msleep(1);
+               usleep_range(1000, 2000);
                rx_enabled = 0;
                for (i = 0; i < 4; i++)
                        rx_enabled |= rd32(E1000_RXDCTL(i));
@@ -1953,7 +1945,7 @@ void igb_rx_fifo_flush_82575(struct e1000_hw *hw)
        wr32(E1000_RCTL, temp_rctl);
        wr32(E1000_RCTL, temp_rctl | E1000_RCTL_EN);
        wrfl();
-       msleep(2);
+       usleep_range(2000, 3000);
 
        /* Enable RX queues that were previously enabled and restore our
         * previous state
@@ -2005,14 +1997,14 @@ static s32 igb_set_pcie_completion_timeout(struct e1000_hw *hw)
         * 16ms to 55ms
         */
        ret_val = igb_read_pcie_cap_reg(hw, PCIE_DEVICE_CONTROL2,
-                                       &pcie_devctl2);
+                                       &pcie_devctl2);
        if (ret_val)
                goto out;
 
        pcie_devctl2 |= PCIE_DEVICE_CONTROL2_16ms;
 
        ret_val = igb_write_pcie_cap_reg(hw, PCIE_DEVICE_CONTROL2,
-                                        &pcie_devctl2);
+                                        &pcie_devctl2);
 out:
        /* disable completion timeout resend */
        gcr &= ~E1000_GCR_CMPL_TMOUT_RESEND;
@@ -2241,7 +2233,7 @@ static s32 igb_reset_hw_82580(struct e1000_hw *hw)
        wr32(E1000_TCTL, E1000_TCTL_PSP);
        wrfl();
 
-       msleep(10);
+       usleep_range(10000, 11000);
 
        /* Determine whether or not a global dev reset is requested */
        if (global_device_reset &&
@@ -2259,7 +2251,7 @@ static s32 igb_reset_hw_82580(struct e1000_hw *hw)
 
        /* Add delay to insure DEV_RST has time to complete */
        if (global_device_reset)
-               msleep(5);
+               usleep_range(5000, 6000);
 
        ret_val = igb_get_auto_rd_done(hw);
        if (ret_val) {
@@ -2436,8 +2428,7 @@ static s32 igb_update_nvm_checksum_82580(struct e1000_hw *hw)
 
        ret_val = hw->nvm.ops.read(hw, NVM_COMPATIBILITY_REG_3, 1, &nvm_data);
        if (ret_val) {
-               hw_dbg("NVM Read Error while updating checksum"
-                       " compatibility bit.\n");
+               hw_dbg("NVM Read Error while updating checksum compatibility bit.\n");
                goto out;
        }
 
@@ -2447,8 +2438,7 @@ static s32 igb_update_nvm_checksum_82580(struct e1000_hw *hw)
                ret_val = hw->nvm.ops.write(hw, NVM_COMPATIBILITY_REG_3, 1,
                                        &nvm_data);
                if (ret_val) {
-                       hw_dbg("NVM Write Error while updating checksum"
-                               " compatibility bit.\n");
+                       hw_dbg("NVM Write Error while updating checksum compatibility bit.\n");
                        goto out;
                }
        }
@@ -2525,7 +2515,7 @@ out:
 static s32 __igb_access_emi_reg(struct e1000_hw *hw, u16 address,
                                  u16 *data, bool read)
 {
-       s32 ret_val = E1000_SUCCESS;
+       s32 ret_val = 0;
 
        ret_val = hw->phy.ops.write_reg(hw, E1000_EMIADD, address);
        if (ret_val)
@@ -2559,7 +2549,6 @@ s32 igb_read_emi_reg(struct e1000_hw *hw, u16 addr, u16 *data)
  **/
 s32 igb_set_eee_i350(struct e1000_hw *hw)
 {
-       s32 ret_val = 0;
        u32 ipcnfg, eeer;
 
        if ((hw->mac.type < e1000_i350) ||
@@ -2593,7 +2582,7 @@ s32 igb_set_eee_i350(struct e1000_hw *hw)
        rd32(E1000_EEER);
 out:
 
-       return ret_val;
+       return 0;
 }
 
 /**
@@ -2720,7 +2709,6 @@ static const u8 e1000_emc_therm_limit[4] = {
  **/
 static s32 igb_get_thermal_sensor_data_generic(struct e1000_hw *hw)
 {
-       s32 status = E1000_SUCCESS;
        u16 ets_offset;
        u16 ets_cfg;
        u16 ets_sensor;
@@ -2738,7 +2726,7 @@ static s32 igb_get_thermal_sensor_data_generic(struct e1000_hw *hw)
        /* Return the internal sensor only if ETS is unsupported */
        hw->nvm.ops.read(hw, NVM_ETS_CFG, 1, &ets_offset);
        if ((ets_offset == 0x0000) || (ets_offset == 0xFFFF))
-               return status;
+               return 0;
 
        hw->nvm.ops.read(hw, ets_offset, 1, &ets_cfg);
        if (((ets_cfg & NVM_ETS_TYPE_MASK) >> NVM_ETS_TYPE_SHIFT)
@@ -2762,7 +2750,7 @@ static s32 igb_get_thermal_sensor_data_generic(struct e1000_hw *hw)
                                        E1000_I2C_THERMAL_SENSOR_ADDR,
                                        &data->sensor[i].temp);
        }
-       return status;
+       return 0;
 }
 
 /**
@@ -2774,7 +2762,6 @@ static s32 igb_get_thermal_sensor_data_generic(struct e1000_hw *hw)
  **/
 static s32 igb_init_thermal_sensor_thresh_generic(struct e1000_hw *hw)
 {
-       s32 status = E1000_SUCCESS;
        u16 ets_offset;
        u16 ets_cfg;
        u16 ets_sensor;
@@ -2800,7 +2787,7 @@ static s32 igb_init_thermal_sensor_thresh_generic(struct e1000_hw *hw)
        /* Return the internal sensor only if ETS is unsupported */
        hw->nvm.ops.read(hw, NVM_ETS_CFG, 1, &ets_offset);
        if ((ets_offset == 0x0000) || (ets_offset == 0xFFFF))
-               return status;
+               return 0;
 
        hw->nvm.ops.read(hw, ets_offset, 1, &ets_cfg);
        if (((ets_cfg & NVM_ETS_TYPE_MASK) >> NVM_ETS_TYPE_SHIFT)
@@ -2831,7 +2818,7 @@ static s32 igb_init_thermal_sensor_thresh_generic(struct e1000_hw *hw)
                                                        low_thresh_delta;
                }
        }
-       return status;
+       return 0;
 }
 
 #endif
index 09d78be72416563beeda5e3cb72a7338b2aa7060..b407c55738fadf0a333ee7947fedd2c0c31638a1 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #ifndef _E1000_82575_H_
 #define _E1000_82575_H_
@@ -37,9 +34,9 @@ s32 igb_write_i2c_byte(struct e1000_hw *hw, u8 byte_offset, u8 dev_addr,
                       u8 data);
 
 #define ID_LED_DEFAULT_82575_SERDES ((ID_LED_DEF1_DEF2 << 12) | \
-                                     (ID_LED_DEF1_DEF2 <<  8) | \
-                                     (ID_LED_DEF1_DEF2 <<  4) | \
-                                     (ID_LED_OFF1_ON2))
+                                    (ID_LED_DEF1_DEF2 <<  8) | \
+                                    (ID_LED_DEF1_DEF2 <<  4) | \
+                                    (ID_LED_OFF1_ON2))
 
 #define E1000_RAR_ENTRIES_82575        16
 #define E1000_RAR_ENTRIES_82576        24
@@ -67,16 +64,16 @@ s32 igb_write_i2c_byte(struct e1000_hw *hw, u8 byte_offset, u8 dev_addr,
 #define E1000_MRQC_RSS_FIELD_IPV6_UDP_EX    0x01000000
 
 #define E1000_EICR_TX_QUEUE ( \
-    E1000_EICR_TX_QUEUE0 |    \
-    E1000_EICR_TX_QUEUE1 |    \
-    E1000_EICR_TX_QUEUE2 |    \
-    E1000_EICR_TX_QUEUE3)
+       E1000_EICR_TX_QUEUE0 |    \
+       E1000_EICR_TX_QUEUE1 |    \
+       E1000_EICR_TX_QUEUE2 |    \
+       E1000_EICR_TX_QUEUE3)
 
 #define E1000_EICR_RX_QUEUE ( \
-    E1000_EICR_RX_QUEUE0 |    \
-    E1000_EICR_RX_QUEUE1 |    \
-    E1000_EICR_RX_QUEUE2 |    \
-    E1000_EICR_RX_QUEUE3)
+       E1000_EICR_RX_QUEUE0 |    \
+       E1000_EICR_RX_QUEUE1 |    \
+       E1000_EICR_RX_QUEUE2 |    \
+       E1000_EICR_RX_QUEUE3)
 
 /* Immediate Interrupt Rx (A.K.A. Low Latency Interrupt) */
 #define E1000_IMIREXT_SIZE_BP     0x00001000  /* Packet size bypass */
@@ -92,8 +89,7 @@ union e1000_adv_rx_desc {
                struct {
                        struct {
                                __le16 pkt_info;   /* RSS type, Packet type */
-                               __le16 hdr_info;   /* Split Header,
-                                                   * header buffer length */
+                               __le16 hdr_info;   /* Split Head, buf len */
                        } lo_dword;
                        union {
                                __le32 rss;          /* RSS Hash */
index b05bf925ac721982d8ded6d3aa647d64236890f4..2a8bb35c2df2bb2824015768af176abe5dfeebae 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #ifndef _E1000_DEFINES_H_
 #define _E1000_DEFINES_H_
 
 /* Same mask, but for extended and packet split descriptors */
 #define E1000_RXDEXT_ERR_FRAME_ERR_MASK ( \
-    E1000_RXDEXT_STATERR_CE  |            \
-    E1000_RXDEXT_STATERR_SE  |            \
-    E1000_RXDEXT_STATERR_SEQ |            \
-    E1000_RXDEXT_STATERR_CXE |            \
-    E1000_RXDEXT_STATERR_RXE)
+       E1000_RXDEXT_STATERR_CE  |            \
+       E1000_RXDEXT_STATERR_SE  |            \
+       E1000_RXDEXT_STATERR_SEQ |            \
+       E1000_RXDEXT_STATERR_CXE |            \
+       E1000_RXDEXT_STATERR_RXE)
 
 #define E1000_MRQC_RSS_FIELD_IPV4_TCP          0x00010000
 #define E1000_MRQC_RSS_FIELD_IPV4              0x00020000
 #define E1000_TCTL_RTLC   0x01000000    /* Re-transmit on late collision */
 
 /* DMA Coalescing register fields */
-#define E1000_DMACR_DMACWT_MASK         0x00003FFF /* DMA Coalescing
-                                                       * Watchdog Timer */
-#define E1000_DMACR_DMACTHR_MASK        0x00FF0000 /* DMA Coalescing Receive
-                                                       * Threshold */
+#define E1000_DMACR_DMACWT_MASK         0x00003FFF /* DMA Coal Watchdog Timer */
+#define E1000_DMACR_DMACTHR_MASK        0x00FF0000 /* DMA Coal Rx Threshold */
 #define E1000_DMACR_DMACTHR_SHIFT       16
-#define E1000_DMACR_DMAC_LX_MASK        0x30000000 /* Lx when no PCIe
-                                                       * transactions */
+#define E1000_DMACR_DMAC_LX_MASK        0x30000000 /* Lx when no PCIe trans */
 #define E1000_DMACR_DMAC_LX_SHIFT       28
 #define E1000_DMACR_DMAC_EN             0x80000000 /* Enable DMA Coalescing */
 /* DMA Coalescing BMC-to-OS Watchdog Enable */
 #define E1000_DMACR_DC_BMC2OSW_EN      0x00008000
 
-#define E1000_DMCTXTH_DMCTTHR_MASK      0x00000FFF /* DMA Coalescing Transmit
-                                                       * Threshold */
+#define E1000_DMCTXTH_DMCTTHR_MASK      0x00000FFF /* DMA Coal Tx Threshold */
 
 #define E1000_DMCTLX_TTLX_MASK          0x00000FFF /* Time to LX request */
 
-#define E1000_DMCRTRH_UTRESH_MASK       0x0007FFFF /* Receive Traffic Rate
-                                                       * Threshold */
-#define E1000_DMCRTRH_LRPRCW            0x80000000 /* Rcv packet rate in
-                                                       * current window */
+#define E1000_DMCRTRH_UTRESH_MASK       0x0007FFFF /* Rx Traffic Rate Thresh */
+#define E1000_DMCRTRH_LRPRCW            0x80000000 /* Rx pkt rate curr window */
 
-#define E1000_DMCCNT_CCOUNT_MASK        0x01FFFFFF /* DMA Coal Rcv Traffic
-                                                       * Current Cnt */
+#define E1000_DMCCNT_CCOUNT_MASK        0x01FFFFFF /* DMA Coal Rx Current Cnt */
 
-#define E1000_FCRTC_RTH_COAL_MASK       0x0003FFF0 /* Flow ctrl Rcv Threshold
-                                                       * High val */
+#define E1000_FCRTC_RTH_COAL_MASK       0x0003FFF0 /* FC Rx Thresh High val */
 #define E1000_FCRTC_RTH_COAL_SHIFT      4
 #define E1000_PCIEMISC_LX_DECISION      0x00000080 /* Lx power decision */
 
 /* Timestamp in Rx buffer */
 #define E1000_RXPBS_CFG_TS_EN           0x80000000
 
+#define I210_RXPBSIZE_DEFAULT          0x000000A2 /* RXPBSIZE default */
+#define I210_TXPBSIZE_DEFAULT          0x04000014 /* TXPBSIZE default */
+
 /* SerDes Control */
 #define E1000_SCTL_DISABLE_SERDES_LOOPBACK 0x0400
 
  *   o LSC    = Link Status Change
  */
 #define IMS_ENABLE_MASK ( \
-    E1000_IMS_RXT0   |    \
-    E1000_IMS_TXDW   |    \
-    E1000_IMS_RXDMT0 |    \
-    E1000_IMS_RXSEQ  |    \
-    E1000_IMS_LSC    |    \
-    E1000_IMS_DOUTSYNC)
+       E1000_IMS_RXT0   |    \
+       E1000_IMS_TXDW   |    \
+       E1000_IMS_RXDMT0 |    \
+       E1000_IMS_RXSEQ  |    \
+       E1000_IMS_LSC    |    \
+       E1000_IMS_DOUTSYNC)
 
 /* Interrupt Mask Set */
 #define E1000_IMS_TXDW      E1000_ICR_TXDW      /* Transmit desc written back */
 #define E1000_RAH_POOL_1 0x00040000
 
 /* Error Codes */
-#define E1000_SUCCESS      0
 #define E1000_ERR_NVM      1
 #define E1000_ERR_PHY      2
 #define E1000_ERR_CONFIG   3
 #define E1000_VFTA_ENTRY_BIT_SHIFT_MASK      0x1F
 
 /* DMA Coalescing register fields */
-#define E1000_PCIEMISC_LX_DECISION      0x00000080 /* Lx power decision based
-                                                      on DMA coal */
+#define E1000_PCIEMISC_LX_DECISION      0x00000080 /* Lx power on DMA coal */
 
 /* Tx Rate-Scheduler Config fields */
 #define E1000_RTTBCNRC_RS_ENA          0x80000000
index 10741d170f2ddad46b2fad14d362998bdc19d639..89925e4058498ea1c1ffda3195576d8abcda611e 100644 (file)
@@ -1,28 +1,24 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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,
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #ifndef _E1000_HW_H_
 #define _E1000_HW_H_
@@ -320,15 +316,15 @@ struct e1000_host_mng_command_info {
 #include "e1000_mbx.h"
 
 struct e1000_mac_operations {
-       s32  (*check_for_link)(struct e1000_hw *);
-       s32  (*reset_hw)(struct e1000_hw *);
-       s32  (*init_hw)(struct e1000_hw *);
+       s32 (*check_for_link)(struct e1000_hw *);
+       s32 (*reset_hw)(struct e1000_hw *);
+       s32 (*init_hw)(struct e1000_hw *);
        bool (*check_mng_mode)(struct e1000_hw *);
-       s32  (*setup_physical_interface)(struct e1000_hw *);
+       s32 (*setup_physical_interface)(struct e1000_hw *);
        void (*rar_set)(struct e1000_hw *, u8 *, u32);
-       s32  (*read_mac_addr)(struct e1000_hw *);
-       s32  (*get_speed_and_duplex)(struct e1000_hw *, u16 *, u16 *);
-       s32  (*acquire_swfw_sync)(struct e1000_hw *, u16);
+       s32 (*read_mac_addr)(struct e1000_hw *);
+       s32 (*get_speed_and_duplex)(struct e1000_hw *, u16 *, u16 *);
+       s32 (*acquire_swfw_sync)(struct e1000_hw *, u16);
        void (*release_swfw_sync)(struct e1000_hw *, u16);
 #ifdef CONFIG_IGB_HWMON
        s32 (*get_thermal_sensor_data)(struct e1000_hw *);
@@ -338,31 +334,31 @@ struct e1000_mac_operations {
 };
 
 struct e1000_phy_operations {
-       s32  (*acquire)(struct e1000_hw *);
-       s32  (*check_polarity)(struct e1000_hw *);
-       s32  (*check_reset_block)(struct e1000_hw *);
-       s32  (*force_speed_duplex)(struct e1000_hw *);
-       s32  (*get_cfg_done)(struct e1000_hw *hw);
-       s32  (*get_cable_length)(struct e1000_hw *);
-       s32  (*get_phy_info)(struct e1000_hw *);
-       s32  (*read_reg)(struct e1000_hw *, u32, u16 *);
+       s32 (*acquire)(struct e1000_hw *);
+       s32 (*check_polarity)(struct e1000_hw *);
+       s32 (*check_reset_block)(struct e1000_hw *);
+       s32 (*force_speed_duplex)(struct e1000_hw *);
+       s32 (*get_cfg_done)(struct e1000_hw *hw);
+       s32 (*get_cable_length)(struct e1000_hw *);
+       s32 (*get_phy_info)(struct e1000_hw *);
+       s32 (*read_reg)(struct e1000_hw *, u32, u16 *);
        void (*release)(struct e1000_hw *);
-       s32  (*reset)(struct e1000_hw *);
-       s32  (*set_d0_lplu_state)(struct e1000_hw *, bool);
-       s32  (*set_d3_lplu_state)(struct e1000_hw *, bool);
-       s32  (*write_reg)(struct e1000_hw *, u32, u16);
+       s32 (*reset)(struct e1000_hw *);
+       s32 (*set_d0_lplu_state)(struct e1000_hw *, bool);
+       s32 (*set_d3_lplu_state)(struct e1000_hw *, bool);
+       s32 (*write_reg)(struct e1000_hw *, u32, u16);
        s32 (*read_i2c_byte)(struct e1000_hw *, u8, u8, u8 *);
        s32 (*write_i2c_byte)(struct e1000_hw *, u8, u8, u8);
 };
 
 struct e1000_nvm_operations {
-       s32  (*acquire)(struct e1000_hw *);
-       s32  (*read)(struct e1000_hw *, u16, u16, u16 *);
+       s32 (*acquire)(struct e1000_hw *);
+       s32 (*read)(struct e1000_hw *, u16, u16, u16 *);
        void (*release)(struct e1000_hw *);
-       s32  (*write)(struct e1000_hw *, u16, u16, u16 *);
-       s32  (*update)(struct e1000_hw *);
-       s32  (*validate)(struct e1000_hw *);
-       s32  (*valid_led_default)(struct e1000_hw *, u16 *);
+       s32 (*write)(struct e1000_hw *, u16, u16, u16 *);
+       s32 (*update)(struct e1000_hw *);
+       s32 (*validate)(struct e1000_hw *);
+       s32 (*valid_led_default)(struct e1000_hw *, u16 *);
 };
 
 #define E1000_MAX_SENSORS              3
index f67f8a170b90c81bcf4738154f1f7cead7171992..337161f440dd67aa473f616621933332955a842c 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 /* e1000_i210
  * e1000_i211
@@ -100,7 +97,7 @@ static s32 igb_get_hw_semaphore_i210(struct e1000_hw *hw)
                return -E1000_ERR_NVM;
        }
 
-       return E1000_SUCCESS;
+       return 0;
 }
 
 /**
@@ -142,7 +139,7 @@ s32 igb_acquire_swfw_sync_i210(struct e1000_hw *hw, u16 mask)
        u32 swfw_sync;
        u32 swmask = mask;
        u32 fwmask = mask << 16;
-       s32 ret_val = E1000_SUCCESS;
+       s32 ret_val = 0;
        s32 i = 0, timeout = 200; /* FIXME: find real value to use here */
 
        while (i < timeout) {
@@ -187,7 +184,7 @@ void igb_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask)
 {
        u32 swfw_sync;
 
-       while (igb_get_hw_semaphore_i210(hw) != E1000_SUCCESS)
+       while (igb_get_hw_semaphore_i210(hw))
                ; /* Empty */
 
        swfw_sync = rd32(E1000_SW_FW_SYNC);
@@ -210,7 +207,7 @@ void igb_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask)
 static s32 igb_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, u16 words,
                                  u16 *data)
 {
-       s32 status = E1000_SUCCESS;
+       s32 status = 0;
        u16 i, count;
 
        /* We cannot hold synchronization semaphores for too long,
@@ -220,7 +217,7 @@ static s32 igb_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, u16 words,
        for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) {
                count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ?
                        E1000_EERD_EEWR_MAX_COUNT : (words - i);
-               if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
+               if (!(hw->nvm.ops.acquire(hw))) {
                        status = igb_read_nvm_eerd(hw, offset, count,
                                                     data + i);
                        hw->nvm.ops.release(hw);
@@ -228,7 +225,7 @@ static s32 igb_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, u16 words,
                        status = E1000_ERR_SWFW_SYNC;
                }
 
-               if (status != E1000_SUCCESS)
+               if (status)
                        break;
        }
 
@@ -253,7 +250,7 @@ static s32 igb_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words,
        struct e1000_nvm_info *nvm = &hw->nvm;
        u32 i, k, eewr = 0;
        u32 attempts = 100000;
-       s32 ret_val = E1000_SUCCESS;
+       s32 ret_val = 0;
 
        /* A check for invalid values:  offset too large, too many words,
         * too many words for the offset, and not enough words.
@@ -275,13 +272,13 @@ static s32 igb_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words,
                for (k = 0; k < attempts; k++) {
                        if (E1000_NVM_RW_REG_DONE &
                            rd32(E1000_SRWR)) {
-                               ret_val = E1000_SUCCESS;
+                               ret_val = 0;
                                break;
                        }
                        udelay(5);
        }
 
-               if (ret_val != E1000_SUCCESS) {
+               if (ret_val) {
                        hw_dbg("Shadow RAM write EEWR timed out\n");
                        break;
                }
@@ -310,7 +307,7 @@ out:
 static s32 igb_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words,
                                   u16 *data)
 {
-       s32 status = E1000_SUCCESS;
+       s32 status = 0;
        u16 i, count;
 
        /* We cannot hold synchronization semaphores for too long,
@@ -320,7 +317,7 @@ static s32 igb_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words,
        for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) {
                count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ?
                        E1000_EERD_EEWR_MAX_COUNT : (words - i);
-               if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
+               if (!(hw->nvm.ops.acquire(hw))) {
                        status = igb_write_nvm_srwr(hw, offset, count,
                                                      data + i);
                        hw->nvm.ops.release(hw);
@@ -328,7 +325,7 @@ static s32 igb_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words,
                        status = E1000_ERR_SWFW_SYNC;
                }
 
-               if (status != E1000_SUCCESS)
+               if (status)
                        break;
        }
 
@@ -367,12 +364,12 @@ static s32 igb_read_invm_word_i210(struct e1000_hw *hw, u8 address, u16 *data)
                                *data = INVM_DWORD_TO_WORD_DATA(invm_dword);
                                hw_dbg("Read INVM Word 0x%02x = %x\n",
                                          address, *data);
-                               status = E1000_SUCCESS;
+                               status = 0;
                                break;
                        }
                }
        }
-       if (status != E1000_SUCCESS)
+       if (status)
                hw_dbg("Requested word 0x%02x not found in OTP\n", address);
        return status;
 }
@@ -388,7 +385,7 @@ static s32 igb_read_invm_word_i210(struct e1000_hw *hw, u8 address, u16 *data)
 static s32 igb_read_invm_i210(struct e1000_hw *hw, u16 offset,
                                u16 words __always_unused, u16 *data)
 {
-       s32 ret_val = E1000_SUCCESS;
+       s32 ret_val = 0;
 
        /* Only the MAC addr is required to be present in the iNVM */
        switch (offset) {
@@ -398,43 +395,44 @@ static s32 igb_read_invm_i210(struct e1000_hw *hw, u16 offset,
                                                     &data[1]);
                ret_val |= igb_read_invm_word_i210(hw, (u8)offset+2,
                                                     &data[2]);
-               if (ret_val != E1000_SUCCESS)
+               if (ret_val)
                        hw_dbg("MAC Addr not found in iNVM\n");
                break;
        case NVM_INIT_CTRL_2:
                ret_val = igb_read_invm_word_i210(hw, (u8)offset, data);
-               if (ret_val != E1000_SUCCESS) {
+               if (ret_val) {
                        *data = NVM_INIT_CTRL_2_DEFAULT_I211;
-                       ret_val = E1000_SUCCESS;
+                       ret_val = 0;
                }
                break;
        case NVM_INIT_CTRL_4:
                ret_val = igb_read_invm_word_i210(hw, (u8)offset, data);
-               if (ret_val != E1000_SUCCESS) {
+               if (ret_val) {
                        *data = NVM_INIT_CTRL_4_DEFAULT_I211;
-                       ret_val = E1000_SUCCESS;
+                       ret_val = 0;
                }
                break;
        case NVM_LED_1_CFG:
                ret_val = igb_read_invm_word_i210(hw, (u8)offset, data);
-               if (ret_val != E1000_SUCCESS) {
+               if (ret_val) {
                        *data = NVM_LED_1_CFG_DEFAULT_I211;
-                       ret_val = E1000_SUCCESS;
+                       ret_val = 0;
                }
                break;
        case NVM_LED_0_2_CFG:
                ret_val = igb_read_invm_word_i210(hw, (u8)offset, data);
-               if (ret_val != E1000_SUCCESS) {
+               if (ret_val) {
                        *data = NVM_LED_0_2_CFG_DEFAULT_I211;
-                       ret_val = E1000_SUCCESS;
+                       ret_val = 0;
                }
                break;
        case NVM_ID_LED_SETTINGS:
                ret_val = igb_read_invm_word_i210(hw, (u8)offset, data);
-               if (ret_val != E1000_SUCCESS) {
+               if (ret_val) {
                        *data = ID_LED_RESERVED_FFFF;
-                       ret_val = E1000_SUCCESS;
+                       ret_val = 0;
                }
+               break;
        case NVM_SUB_DEV_ID:
                *data = hw->subsystem_device_id;
                break;
@@ -488,14 +486,14 @@ s32 igb_read_invm_version(struct e1000_hw *hw,
                /* Check if we have first version location used */
                if ((i == 1) && ((*record & E1000_INVM_VER_FIELD_ONE) == 0)) {
                        version = 0;
-                       status = E1000_SUCCESS;
+                       status = 0;
                        break;
                }
                /* Check if we have second version location used */
                else if ((i == 1) &&
                         ((*record & E1000_INVM_VER_FIELD_TWO) == 0)) {
                        version = (*record & E1000_INVM_VER_FIELD_ONE) >> 3;
-                       status = E1000_SUCCESS;
+                       status = 0;
                        break;
                }
                /* Check if we have odd version location
@@ -506,7 +504,7 @@ s32 igb_read_invm_version(struct e1000_hw *hw,
                         (i != 1))) {
                        version = (*next_record & E1000_INVM_VER_FIELD_TWO)
                                  >> 13;
-                       status = E1000_SUCCESS;
+                       status = 0;
                        break;
                }
                /* Check if we have even version location
@@ -515,12 +513,12 @@ s32 igb_read_invm_version(struct e1000_hw *hw,
                else if (((*record & E1000_INVM_VER_FIELD_TWO) == 0) &&
                         ((*record & 0x3) == 0)) {
                        version = (*record & E1000_INVM_VER_FIELD_ONE) >> 3;
-                       status = E1000_SUCCESS;
+                       status = 0;
                        break;
                }
        }
 
-       if (status == E1000_SUCCESS) {
+       if (!status) {
                invm_ver->invm_major = (version & E1000_INVM_MAJOR_MASK)
                                        >> E1000_INVM_MAJOR_SHIFT;
                invm_ver->invm_minor = version & E1000_INVM_MINOR_MASK;
@@ -533,7 +531,7 @@ s32 igb_read_invm_version(struct e1000_hw *hw,
                /* Check if we have image type in first location used */
                if ((i == 1) && ((*record & E1000_INVM_IMGTYPE_FIELD) == 0)) {
                        invm_ver->invm_img_type = 0;
-                       status = E1000_SUCCESS;
+                       status = 0;
                        break;
                }
                /* Check if we have image type in first location used */
@@ -542,7 +540,7 @@ s32 igb_read_invm_version(struct e1000_hw *hw,
                         ((((*record & 0x3) != 0) && (i != 1)))) {
                        invm_ver->invm_img_type =
                                (*next_record & E1000_INVM_IMGTYPE_FIELD) >> 23;
-                       status = E1000_SUCCESS;
+                       status = 0;
                        break;
                }
        }
@@ -558,10 +556,10 @@ s32 igb_read_invm_version(struct e1000_hw *hw,
  **/
 static s32 igb_validate_nvm_checksum_i210(struct e1000_hw *hw)
 {
-       s32 status = E1000_SUCCESS;
+       s32 status = 0;
        s32 (*read_op_ptr)(struct e1000_hw *, u16, u16, u16 *);
 
-       if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
+       if (!(hw->nvm.ops.acquire(hw))) {
 
                /* Replace the read function with semaphore grabbing with
                 * the one that skips this for a while.
@@ -593,7 +591,7 @@ static s32 igb_validate_nvm_checksum_i210(struct e1000_hw *hw)
  **/
 static s32 igb_update_nvm_checksum_i210(struct e1000_hw *hw)
 {
-       s32 ret_val = E1000_SUCCESS;
+       s32 ret_val = 0;
        u16 checksum = 0;
        u16 i, nvm_data;
 
@@ -602,12 +600,12 @@ static s32 igb_update_nvm_checksum_i210(struct e1000_hw *hw)
         * EEPROM read fails
         */
        ret_val = igb_read_nvm_eerd(hw, 0, 1, &nvm_data);
-       if (ret_val != E1000_SUCCESS) {
+       if (ret_val) {
                hw_dbg("EEPROM read failed\n");
                goto out;
        }
 
-       if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
+       if (!(hw->nvm.ops.acquire(hw))) {
                /* Do not use hw->nvm.ops.write, hw->nvm.ops.read
                 * because we do not want to take the synchronization
                 * semaphores twice here.
@@ -625,7 +623,7 @@ static s32 igb_update_nvm_checksum_i210(struct e1000_hw *hw)
                checksum = (u16) NVM_SUM - checksum;
                ret_val = igb_write_nvm_srwr(hw, NVM_CHECKSUM_REG, 1,
                                                &checksum);
-               if (ret_val != E1000_SUCCESS) {
+               if (ret_val) {
                        hw->nvm.ops.release(hw);
                        hw_dbg("NVM Write Error while updating checksum.\n");
                        goto out;
@@ -654,7 +652,7 @@ static s32 igb_pool_flash_update_done_i210(struct e1000_hw *hw)
        for (i = 0; i < E1000_FLUDONE_ATTEMPTS; i++) {
                reg = rd32(E1000_EECD);
                if (reg & E1000_EECD_FLUDONE_I210) {
-                       ret_val = E1000_SUCCESS;
+                       ret_val = 0;
                        break;
                }
                udelay(5);
@@ -687,7 +685,7 @@ bool igb_get_flash_presence_i210(struct e1000_hw *hw)
  **/
 static s32 igb_update_flash_i210(struct e1000_hw *hw)
 {
-       s32 ret_val = E1000_SUCCESS;
+       s32 ret_val = 0;
        u32 flup;
 
        ret_val = igb_pool_flash_update_done_i210(hw);
@@ -700,7 +698,7 @@ static s32 igb_update_flash_i210(struct e1000_hw *hw)
        wr32(E1000_EECD, flup);
 
        ret_val = igb_pool_flash_update_done_i210(hw);
-       if (ret_val == E1000_SUCCESS)
+       if (ret_val)
                hw_dbg("Flash update complete\n");
        else
                hw_dbg("Flash update time out\n");
@@ -753,7 +751,7 @@ out:
 static s32 __igb_access_xmdio_reg(struct e1000_hw *hw, u16 address,
                                  u8 dev_addr, u16 *data, bool read)
 {
-       s32 ret_val = E1000_SUCCESS;
+       s32 ret_val = 0;
 
        ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAC, dev_addr);
        if (ret_val)
index 907fe99a9813130e45a3dddf0d5d48c6dfdc492d..9f34976687baedc7eb4d4844678cb2592c10e9d1 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #ifndef _E1000_I210_H_
 #define _E1000_I210_H_
index 1e0c404db81a263861c2b0e683ec5b702b305df8..2a88595f956cf4e3089d20a986f0adb9db48681e 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #include <linux/if_ether.h>
 #include <linux/delay.h>
@@ -442,7 +439,7 @@ static u32 igb_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr)
  *  The caller must have a packed mc_addr_list of multicast addresses.
  **/
 void igb_update_mc_addr_list(struct e1000_hw *hw,
-                             u8 *mc_addr_list, u32 mc_addr_count)
+                            u8 *mc_addr_list, u32 mc_addr_count)
 {
        u32 hash_value, hash_bit, hash_reg;
        int i;
@@ -866,8 +863,7 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
                        goto out;
 
                if (!(mii_status_reg & MII_SR_AUTONEG_COMPLETE)) {
-                       hw_dbg("Copper PHY and Auto Neg "
-                                "has not completed.\n");
+                       hw_dbg("Copper PHY and Auto Neg has not completed.\n");
                        goto out;
                }
 
@@ -1265,7 +1261,7 @@ s32 igb_get_auto_rd_done(struct e1000_hw *hw)
        while (i < AUTO_READ_DONE_TIMEOUT) {
                if (rd32(E1000_EECD) & E1000_EECD_AUTO_RD)
                        break;
-               msleep(1);
+               usleep_range(1000, 2000);
                i++;
        }
 
@@ -1298,7 +1294,7 @@ static s32 igb_valid_led_default(struct e1000_hw *hw, u16 *data)
        }
 
        if (*data == ID_LED_RESERVED_0000 || *data == ID_LED_RESERVED_FFFF) {
-               switch(hw->phy.media_type) {
+               switch (hw->phy.media_type) {
                case e1000_media_type_internal_serdes:
                        *data = ID_LED_DEFAULT_82575_SERDES;
                        break;
index 99299ba8ee3a2def53ab2e6fcc8c5c039aca0265..ea24961b0d705e557a6b9bd57772d984ab0927ae 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #ifndef _E1000_MAC_H_
 #define _E1000_MAC_H_
index d5b121771c313716543b16f5a8464866afd4f8e2..162cc49345d09babbd7fab30ec0917215e1a9b64 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #include "e1000_mbx.h"
 
index f52f5515e5a8a8aedcc1568f90fdf1b986b2947e..d20af6b2f581698098a972d557a0a93fe19d1d48 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #ifndef _E1000_MBX_H_
 #define _E1000_MBX_H_
index 9abf82919c65535d7b3fd7f7d7200f80fffd1f0a..e8280d0d7f022942c81d399945259cf4b0a5d1f9 100644 (file)
@@ -1,28 +1,24 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #include <linux/if_ether.h>
 #include <linux/delay.h>
@@ -480,6 +476,7 @@ s32 igb_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
                /* Loop to allow for up to whole page write of eeprom */
                while (widx < words) {
                        u16 word_out = data[widx];
+
                        word_out = (word_out >> 8) | (word_out << 8);
                        igb_shift_out_eec_bits(hw, word_out, 16);
                        widx++;
@@ -801,5 +798,4 @@ etrack_id:
                fw_vers->etrack_id = (eeprom_verh << NVM_ETRACK_SHIFT)
                        | eeprom_verl;
        }
-       return;
 }
index 5b101170b17e4bbc9af310c8aacd5e0b891344a0..febc9cdb739125174e143b0159feb0b529cb5ac6 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #ifndef _E1000_NVM_H_
 #define _E1000_NVM_H_
@@ -32,7 +29,7 @@ void igb_release_nvm(struct e1000_hw *hw);
 s32  igb_read_mac_addr(struct e1000_hw *hw);
 s32  igb_read_part_num(struct e1000_hw *hw, u32 *part_num);
 s32  igb_read_part_string(struct e1000_hw *hw, u8 *part_num,
-                          u32 part_num_size);
+                         u32 part_num_size);
 s32  igb_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data);
 s32  igb_read_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data);
 s32  igb_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data);
index 4009bbab7407d21945e7c1af20df5deedba564a3..c1bb64d8366fa5e7741905ea9fc22d4b241054ce 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #include <linux/if_ether.h>
 #include <linux/delay.h>
@@ -924,8 +921,7 @@ static s32 igb_copper_link_autoneg(struct e1000_hw *hw)
        if (phy->autoneg_wait_to_complete) {
                ret_val = igb_wait_autoneg(hw);
                if (ret_val) {
-                       hw_dbg("Error while waiting for "
-                              "autoneg to complete\n");
+                       hw_dbg("Error while waiting for autoneg to complete\n");
                        goto out;
                }
        }
@@ -2208,16 +2204,10 @@ s32 igb_phy_init_script_igp3(struct e1000_hw *hw)
 void igb_power_up_phy_copper(struct e1000_hw *hw)
 {
        u16 mii_reg = 0;
-       u16 power_reg = 0;
 
        /* The PHY will retain its settings across a power down/up cycle */
        hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg);
        mii_reg &= ~MII_CR_POWER_DOWN;
-       if (hw->phy.type == e1000_phy_i210) {
-               hw->phy.ops.read_reg(hw, GS40G_COPPER_SPEC, &power_reg);
-               power_reg &= ~GS40G_CS_POWER_DOWN;
-               hw->phy.ops.write_reg(hw, GS40G_COPPER_SPEC, power_reg);
-       }
        hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg);
 }
 
@@ -2231,20 +2221,12 @@ void igb_power_up_phy_copper(struct e1000_hw *hw)
 void igb_power_down_phy_copper(struct e1000_hw *hw)
 {
        u16 mii_reg = 0;
-       u16 power_reg = 0;
 
        /* The PHY will retain its settings across a power down/up cycle */
        hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg);
        mii_reg |= MII_CR_POWER_DOWN;
-
-       /* i210 Phy requires an additional bit for power up/down */
-       if (hw->phy.type == e1000_phy_i210) {
-               hw->phy.ops.read_reg(hw, GS40G_COPPER_SPEC, &power_reg);
-               power_reg |= GS40G_CS_POWER_DOWN;
-               hw->phy.ops.write_reg(hw, GS40G_COPPER_SPEC, power_reg);
-       }
        hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg);
-       msleep(1);
+       usleep_range(1000, 2000);
 }
 
 /**
index 4c2c36c46a7398d217c1418b3966b4cde5813812..7af4ffab0285653c4c400992edcd163782f919ac 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #ifndef _E1000_PHY_H_
 #define _E1000_PHY_H_
@@ -154,7 +151,6 @@ s32  igb_check_polarity_m88(struct e1000_hw *hw);
 #define GS40G_MAC_LB                   0x4140
 #define GS40G_MAC_SPEED_1G             0X0006
 #define GS40G_COPPER_SPEC              0x0010
-#define GS40G_CS_POWER_DOWN            0x0002
 #define GS40G_LINE_LB                  0x4000
 
 /* SFP modules ID memory locations */
index bdb246e848e13bb5e569f279336dbb5a2c5bfe86..1cc4b1a7e597d32823ff2cc221aa54ce370a5a15 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #ifndef _E1000_REGS_H_
 #define _E1000_REGS_H_
                                    : (0x0E038 + ((_n) * 0x40)))
 #define E1000_TDWBAH(_n)  ((_n) < 4 ? (0x0383C + ((_n) * 0x100)) \
                                    : (0x0E03C + ((_n) * 0x40)))
+
+#define E1000_RXPBS    0x02404  /* Rx Packet Buffer Size - RW */
+#define E1000_TXPBS    0x03404  /* Tx Packet Buffer Size - RW */
+
 #define E1000_TDFH     0x03410  /* TX Data FIFO Head - RW */
 #define E1000_TDFT     0x03418  /* TX Data FIFO Tail - RW */
 #define E1000_TDFHS    0x03420  /* TX Data FIFO Head Saved - RW */
 #define E1000_RA2      0x054E0  /* 2nd half of Rx address array - RW Array */
 #define E1000_PSRTYPE(_i)       (0x05480 + ((_i) * 4))
 #define E1000_RAL(_i)  (((_i) <= 15) ? (0x05400 + ((_i) * 8)) : \
-                                       (0x054E0 + ((_i - 16) * 8)))
+                                       (0x054E0 + ((_i - 16) * 8)))
 #define E1000_RAH(_i)  (((_i) <= 15) ? (0x05404 + ((_i) * 8)) : \
-                                       (0x054E4 + ((_i - 16) * 8)))
+                                       (0x054E4 + ((_i - 16) * 8)))
 #define E1000_IP4AT_REG(_i)     (0x05840 + ((_i) * 8))
 #define E1000_IP6AT_REG(_i)     (0x05880 + ((_i) * 4))
 #define E1000_WUPM_REG(_i)      (0x05A00 + ((_i) * 4))
 #define E1000_VMBMEM(_n)       (0x00800 + (64 * (_n)))
 #define E1000_VMOLR(_n)        (0x05AD0 + (4 * (_n)))
 #define E1000_DVMOLR(_n)       (0x0C038 + (64 * (_n)))
-#define E1000_VLVF(_n)         (0x05D00 + (4 * (_n))) /* VLAN Virtual Machine
-                                                       * Filter - RW */
+#define E1000_VLVF(_n)         (0x05D00 + (4 * (_n))) /* VLAN VM Filter */
 #define E1000_VMVIR(_n)        (0x03700 + (4 * (_n)))
 
 struct e1000_hw;
index 27130065d92a70679292ef316aaadf848772944c..06102d1f7c0362208118ec99dcaba0e6db89eeb3 100644 (file)
@@ -1,29 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
-
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 /* Linux PRO/1000 Ethernet Driver main header file */
 
@@ -198,6 +194,7 @@ struct igb_tx_buffer {
        unsigned int bytecount;
        u16 gso_segs;
        __be16 protocol;
+
        DEFINE_DMA_UNMAP_ADDR(dma);
        DEFINE_DMA_UNMAP_LEN(len);
        u32 tx_flags;
index e5570acbeea84509855a98383ab128876e7447c9..c737d1f4083829ae0eed6e7ecff1d29b5c75239e 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 /* ethtool support for igb */
 
@@ -144,6 +141,7 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
        struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575;
        struct e1000_sfp_flags *eth_flags = &dev_spec->eth_flags;
        u32 status;
+       u32 speed;
 
        status = rd32(E1000_STATUS);
        if (hw->phy.media_type == e1000_media_type_copper) {
@@ -218,13 +216,13 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
        if (status & E1000_STATUS_LU) {
                if ((status & E1000_STATUS_2P5_SKU) &&
                    !(status & E1000_STATUS_2P5_SKU_OVER)) {
-                       ecmd->speed = SPEED_2500;
+                       speed = SPEED_2500;
                } else if (status & E1000_STATUS_SPEED_1000) {
-                       ecmd->speed = SPEED_1000;
+                       speed = SPEED_1000;
                } else if (status & E1000_STATUS_SPEED_100) {
-                       ecmd->speed = SPEED_100;
+                       speed = SPEED_100;
                } else {
-                       ecmd->speed = SPEED_10;
+                       speed = SPEED_10;
                }
                if ((status & E1000_STATUS_FD) ||
                    hw->phy.media_type != e1000_media_type_copper)
@@ -232,9 +230,10 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
                else
                        ecmd->duplex = DUPLEX_HALF;
        } else {
-               ecmd->speed = -1;
-               ecmd->duplex = -1;
+               speed = SPEED_UNKNOWN;
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
+       ethtool_cmd_speed_set(ecmd, speed);
        if ((hw->phy.media_type == e1000_media_type_fiber) ||
            hw->mac.autoneg)
                ecmd->autoneg = AUTONEG_ENABLE;
@@ -286,7 +285,7 @@ static int igb_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
        }
 
        while (test_and_set_bit(__IGB_RESETTING, &adapter->state))
-               msleep(1);
+               usleep_range(1000, 2000);
 
        if (ecmd->autoneg == AUTONEG_ENABLE) {
                hw->mac.autoneg = 1;
@@ -399,7 +398,7 @@ static int igb_set_pauseparam(struct net_device *netdev,
        adapter->fc_autoneg = pause->autoneg;
 
        while (test_and_set_bit(__IGB_RESETTING, &adapter->state))
-               msleep(1);
+               usleep_range(1000, 2000);
 
        if (adapter->fc_autoneg == AUTONEG_ENABLE) {
                hw->fc.requested_mode = e1000_fc_default;
@@ -886,7 +885,7 @@ static int igb_set_ringparam(struct net_device *netdev,
        }
 
        while (test_and_set_bit(__IGB_RESETTING, &adapter->state))
-               msleep(1);
+               usleep_range(1000, 2000);
 
        if (!netif_running(adapter->netdev)) {
                for (i = 0; i < adapter->num_tx_queues; i++)
@@ -1060,8 +1059,8 @@ static struct igb_reg_test reg_test_i350[] = {
        { E1000_TDT(0),    0x100, 4,  PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
        { E1000_TDT(4),    0x40,  4,  PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
        { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0xFFFFFFFF, 0x00000000 },
-       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0x003FFFFB },
-       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0xFFFFFFFF },
+       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0x003FFFFB },
+       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0xFFFFFFFF },
        { E1000_TCTL,      0x100, 1,  SET_READ_TEST, 0xFFFFFFFF, 0x00000000 },
        { E1000_RA,        0, 16, TABLE64_TEST_LO,
                                                0xFFFFFFFF, 0xFFFFFFFF },
@@ -1103,8 +1102,8 @@ static struct igb_reg_test reg_test_82580[] = {
        { E1000_TDT(0),    0x100, 4,  PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
        { E1000_TDT(4),    0x40,  4,  PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
        { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0xFFFFFFFF, 0x00000000 },
-       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0x003FFFFB },
-       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0xFFFFFFFF },
+       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0x003FFFFB },
+       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0xFFFFFFFF },
        { E1000_TCTL,      0x100, 1,  SET_READ_TEST, 0xFFFFFFFF, 0x00000000 },
        { E1000_RA,        0, 16, TABLE64_TEST_LO,
                                                0xFFFFFFFF, 0xFFFFFFFF },
@@ -1132,8 +1131,10 @@ static struct igb_reg_test reg_test_82576[] = {
        { E1000_RDBAH(4),  0x40, 12, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
        { E1000_RDLEN(4),  0x40, 12, PATTERN_TEST, 0x000FFFF0, 0x000FFFFF },
        /* Enable all RX queues before testing. */
-       { E1000_RXDCTL(0), 0x100, 4,  WRITE_NO_TEST, 0, E1000_RXDCTL_QUEUE_ENABLE },
-       { E1000_RXDCTL(4), 0x40, 12,  WRITE_NO_TEST, 0, E1000_RXDCTL_QUEUE_ENABLE },
+       { E1000_RXDCTL(0), 0x100, 4, WRITE_NO_TEST, 0,
+         E1000_RXDCTL_QUEUE_ENABLE },
+       { E1000_RXDCTL(4), 0x40, 12, WRITE_NO_TEST, 0,
+         E1000_RXDCTL_QUEUE_ENABLE },
        /* RDH is read-only for 82576, only test RDT. */
        { E1000_RDT(0),    0x100, 4,  PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
        { E1000_RDT(4),    0x40, 12,  PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
@@ -1149,14 +1150,14 @@ static struct igb_reg_test reg_test_82576[] = {
        { E1000_TDBAH(4),  0x40, 12,  PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
        { E1000_TDLEN(4),  0x40, 12,  PATTERN_TEST, 0x000FFFF0, 0x000FFFFF },
        { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0xFFFFFFFF, 0x00000000 },
-       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0x003FFFFB },
-       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0xFFFFFFFF },
+       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0x003FFFFB },
+       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0xFFFFFFFF },
        { E1000_TCTL,      0x100, 1,  SET_READ_TEST, 0xFFFFFFFF, 0x00000000 },
        { E1000_RA,        0, 16, TABLE64_TEST_LO, 0xFFFFFFFF, 0xFFFFFFFF },
        { E1000_RA,        0, 16, TABLE64_TEST_HI, 0x83FFFFFF, 0xFFFFFFFF },
        { E1000_RA2,       0, 8, TABLE64_TEST_LO, 0xFFFFFFFF, 0xFFFFFFFF },
        { E1000_RA2,       0, 8, TABLE64_TEST_HI, 0x83FFFFFF, 0xFFFFFFFF },
-       { E1000_MTA,       0, 128,TABLE32_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
+       { E1000_MTA,       0, 128, TABLE32_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
        { 0, 0, 0, 0 }
 };
 
@@ -1170,7 +1171,8 @@ static struct igb_reg_test reg_test_82575[] = {
        { E1000_RDBAH(0),  0x100, 4, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
        { E1000_RDLEN(0),  0x100, 4, PATTERN_TEST, 0x000FFF80, 0x000FFFFF },
        /* Enable all four RX queues before testing. */
-       { E1000_RXDCTL(0), 0x100, 4, WRITE_NO_TEST, 0, E1000_RXDCTL_QUEUE_ENABLE },
+       { E1000_RXDCTL(0), 0x100, 4, WRITE_NO_TEST, 0,
+         E1000_RXDCTL_QUEUE_ENABLE },
        /* RDH is read-only for 82575, only test RDT. */
        { E1000_RDT(0),    0x100, 4, PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
        { E1000_RXDCTL(0), 0x100, 4, WRITE_NO_TEST, 0, 0 },
@@ -1196,8 +1198,8 @@ static bool reg_pattern_test(struct igb_adapter *adapter, u64 *data,
 {
        struct e1000_hw *hw = &adapter->hw;
        u32 pat, val;
-       static const u32 _test[] =
-               {0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF};
+       static const u32 _test[] = {
+               0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF};
        for (pat = 0; pat < ARRAY_SIZE(_test); pat++) {
                wr32(reg, (_test[pat] & write));
                val = rd32(reg) & mask;
@@ -1206,11 +1208,11 @@ static bool reg_pattern_test(struct igb_adapter *adapter, u64 *data,
                                "pattern test reg %04X failed: got 0x%08X expected 0x%08X\n",
                                reg, val, (_test[pat] & write & mask));
                        *data = reg;
-                       return 1;
+                       return true;
                }
        }
 
-       return 0;
+       return false;
 }
 
 static bool reg_set_and_check(struct igb_adapter *adapter, u64 *data,
@@ -1218,17 +1220,18 @@ static bool reg_set_and_check(struct igb_adapter *adapter, u64 *data,
 {
        struct e1000_hw *hw = &adapter->hw;
        u32 val;
+
        wr32(reg, write & mask);
        val = rd32(reg);
        if ((write & mask) != (val & mask)) {
                dev_err(&adapter->pdev->dev,
-                       "set/check reg %04X test failed: got 0x%08X expected 0x%08X\n", reg,
-                       (val & mask), (write & mask));
+                       "set/check reg %04X test failed: got 0x%08X expected 0x%08X\n",
+                       reg, (val & mask), (write & mask));
                *data = reg;
-               return 1;
+               return true;
        }
 
-       return 0;
+       return false;
 }
 
 #define REG_PATTERN_TEST(reg, mask, write) \
@@ -1387,14 +1390,14 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
        /* Hook up test interrupt handler just for this test */
        if (adapter->flags & IGB_FLAG_HAS_MSIX) {
                if (request_irq(adapter->msix_entries[0].vector,
-                               igb_test_intr, 0, netdev->name, adapter)) {
+                               igb_test_intr, 0, netdev->name, adapter)) {
                        *data = 1;
                        return -1;
                }
        } else if (adapter->flags & IGB_FLAG_HAS_MSI) {
                shared_int = false;
                if (request_irq(irq,
-                               igb_test_intr, 0, netdev->name, adapter)) {
+                               igb_test_intr, 0, netdev->name, adapter)) {
                        *data = 1;
                        return -1;
                }
@@ -1412,7 +1415,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
        /* Disable all the interrupts */
        wr32(E1000_IMC, ~0);
        wrfl();
-       msleep(10);
+       usleep_range(10000, 11000);
 
        /* Define all writable bits for ICS */
        switch (hw->mac.type) {
@@ -1459,7 +1462,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
                        wr32(E1000_IMC, mask);
                        wr32(E1000_ICS, mask);
                        wrfl();
-                       msleep(10);
+                       usleep_range(10000, 11000);
 
                        if (adapter->test_icr & mask) {
                                *data = 3;
@@ -1481,7 +1484,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
                wr32(E1000_IMS, mask);
                wr32(E1000_ICS, mask);
                wrfl();
-               msleep(10);
+               usleep_range(10000, 11000);
 
                if (!(adapter->test_icr & mask)) {
                        *data = 4;
@@ -1503,7 +1506,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
                        wr32(E1000_IMC, ~mask);
                        wr32(E1000_ICS, ~mask);
                        wrfl();
-                       msleep(10);
+                       usleep_range(10000, 11000);
 
                        if (adapter->test_icr & mask) {
                                *data = 5;
@@ -1515,7 +1518,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
        /* Disable all the interrupts */
        wr32(E1000_IMC, ~0);
        wrfl();
-       msleep(10);
+       usleep_range(10000, 11000);
 
        /* Unhook test interrupt handler */
        if (adapter->flags & IGB_FLAG_HAS_MSIX)
@@ -1664,8 +1667,8 @@ static int igb_setup_loopback_test(struct igb_adapter *adapter)
                (hw->device_id == E1000_DEV_ID_DH89XXCC_SERDES) ||
                (hw->device_id == E1000_DEV_ID_DH89XXCC_BACKPLANE) ||
                (hw->device_id == E1000_DEV_ID_DH89XXCC_SFP) ||
-               (hw->device_id == E1000_DEV_ID_I354_SGMII)) {
-
+               (hw->device_id == E1000_DEV_ID_I354_SGMII) ||
+               (hw->device_id == E1000_DEV_ID_I354_BACKPLANE_2_5GBPS)) {
                        /* Enable DH89xxCC MPHY for near end loopback */
                        reg = rd32(E1000_MPHY_ADDR_CTL);
                        reg = (reg & E1000_MPHY_ADDR_CTL_OFFSET_MASK) |
@@ -1949,6 +1952,7 @@ static int igb_link_test(struct igb_adapter *adapter, u64 *data)
        *data = 0;
        if (hw->phy.media_type == e1000_media_type_internal_serdes) {
                int i = 0;
+
                hw->mac.serdes_has_link = false;
 
                /* On some blade server designs, link establishment
@@ -2413,9 +2417,11 @@ static int igb_get_rss_hash_opts(struct igb_adapter *adapter,
        switch (cmd->flow_type) {
        case TCP_V4_FLOW:
                cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+               /* Fall through */
        case UDP_V4_FLOW:
                if (adapter->flags & IGB_FLAG_RSS_FIELD_IPV4_UDP)
                        cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+               /* Fall through */
        case SCTP_V4_FLOW:
        case AH_ESP_V4_FLOW:
        case AH_V4_FLOW:
@@ -2425,9 +2431,11 @@ static int igb_get_rss_hash_opts(struct igb_adapter *adapter,
                break;
        case TCP_V6_FLOW:
                cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+               /* Fall through */
        case UDP_V6_FLOW:
                if (adapter->flags & IGB_FLAG_RSS_FIELD_IPV6_UDP)
                        cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+               /* Fall through */
        case SCTP_V6_FLOW:
        case AH_ESP_V6_FLOW:
        case AH_V6_FLOW:
@@ -2730,7 +2738,7 @@ static int igb_get_module_info(struct net_device *netdev,
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
-       u32 status = E1000_SUCCESS;
+       u32 status = 0;
        u16 sff8472_rev, addr_mode;
        bool page_swap = false;
 
@@ -2740,12 +2748,12 @@ static int igb_get_module_info(struct net_device *netdev,
 
        /* Check whether we support SFF-8472 or not */
        status = igb_read_phy_reg_i2c(hw, IGB_SFF_8472_COMP, &sff8472_rev);
-       if (status != E1000_SUCCESS)
+       if (status)
                return -EIO;
 
        /* addressing mode is not supported */
        status = igb_read_phy_reg_i2c(hw, IGB_SFF_8472_SWAP, &addr_mode);
-       if (status != E1000_SUCCESS)
+       if (status)
                return -EIO;
 
        /* addressing mode is not supported */
@@ -2772,7 +2780,7 @@ static int igb_get_module_eeprom(struct net_device *netdev,
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
-       u32 status = E1000_SUCCESS;
+       u32 status = 0;
        u16 *dataword;
        u16 first_word, last_word;
        int i = 0;
@@ -2791,7 +2799,7 @@ static int igb_get_module_eeprom(struct net_device *netdev,
        /* Read EEPROM block, SFF-8079/SFF-8472, word at a time */
        for (i = 0; i < last_word - first_word + 1; i++) {
                status = igb_read_phy_reg_i2c(hw, first_word + i, &dataword[i]);
-               if (status != E1000_SUCCESS) {
+               if (status) {
                        /* Error occurred while reading module */
                        kfree(dataword);
                        return -EIO;
@@ -2824,7 +2832,7 @@ static u32 igb_get_rxfh_indir_size(struct net_device *netdev)
        return IGB_RETA_SIZE;
 }
 
-static int igb_get_rxfh_indir(struct net_device *netdev, u32 *indir)
+static int igb_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key)
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
        int i;
@@ -2870,7 +2878,8 @@ void igb_write_rss_indir_tbl(struct igb_adapter *adapter)
        }
 }
 
-static int igb_set_rxfh_indir(struct net_device *netdev, const u32 *indir)
+static int igb_set_rxfh(struct net_device *netdev, const u32 *indir,
+                       const u8 *key)
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
@@ -3019,8 +3028,8 @@ static const struct ethtool_ops igb_ethtool_ops = {
        .get_module_info        = igb_get_module_info,
        .get_module_eeprom      = igb_get_module_eeprom,
        .get_rxfh_indir_size    = igb_get_rxfh_indir_size,
-       .get_rxfh_indir         = igb_get_rxfh_indir,
-       .set_rxfh_indir         = igb_set_rxfh_indir,
+       .get_rxfh               = igb_get_rxfh,
+       .set_rxfh               = igb_set_rxfh,
        .get_channels           = igb_get_channels,
        .set_channels           = igb_set_channels,
        .begin                  = igb_ethtool_begin,
@@ -3029,5 +3038,5 @@ static const struct ethtool_ops igb_ethtool_ops = {
 
 void igb_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &igb_ethtool_ops);
+       netdev->ethtool_ops = &igb_ethtool_ops;
 }
index 8333f67acf96b3a6e83746c1cedd1f0eff7636ec..44b6a68f1af727136271132014b1efa412ee7e32 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #include "igb.h"
 #include "e1000_82575.h"
index 16430a8440fa3c5bcc299427f3052f6f2a7bdfcc..f145adbb55ac011905636ac17492c55c519e2789 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
@@ -75,7 +72,7 @@ static const struct e1000_info *igb_info_tbl[] = {
        [board_82575] = &e1000_82575_info,
 };
 
-static DEFINE_PCI_DEVICE_TABLE(igb_pci_tbl) = {
+static const struct pci_device_id igb_pci_tbl[] = {
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_BACKPLANE_1GBPS) },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_SGMII) },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_BACKPLANE_2_5GBPS) },
@@ -117,7 +114,6 @@ static DEFINE_PCI_DEVICE_TABLE(igb_pci_tbl) = {
 
 MODULE_DEVICE_TABLE(pci, igb_pci_tbl);
 
-void igb_reset(struct igb_adapter *);
 static int igb_setup_all_tx_resources(struct igb_adapter *);
 static int igb_setup_all_rx_resources(struct igb_adapter *);
 static void igb_free_all_tx_resources(struct igb_adapter *);
@@ -141,7 +137,7 @@ static void igb_watchdog(unsigned long);
 static void igb_watchdog_task(struct work_struct *);
 static netdev_tx_t igb_xmit_frame(struct sk_buff *skb, struct net_device *);
 static struct rtnl_link_stats64 *igb_get_stats64(struct net_device *dev,
-                                                struct rtnl_link_stats64 *stats);
+                                         struct rtnl_link_stats64 *stats);
 static int igb_change_mtu(struct net_device *, int);
 static int igb_set_mac(struct net_device *, void *);
 static void igb_set_uta(struct igb_adapter *adapter);
@@ -159,7 +155,8 @@ static bool igb_clean_rx_irq(struct igb_q_vector *, int);
 static int igb_ioctl(struct net_device *, struct ifreq *, int cmd);
 static void igb_tx_timeout(struct net_device *);
 static void igb_reset_task(struct work_struct *);
-static void igb_vlan_mode(struct net_device *netdev, netdev_features_t features);
+static void igb_vlan_mode(struct net_device *netdev,
+                         netdev_features_t features);
 static int igb_vlan_rx_add_vid(struct net_device *, __be16, u16);
 static int igb_vlan_rx_kill_vid(struct net_device *, __be16, u16);
 static void igb_restore_vlan(struct igb_adapter *);
@@ -172,7 +169,7 @@ static void igb_restore_vf_multicasts(struct igb_adapter *adapter);
 static int igb_ndo_set_vf_mac(struct net_device *netdev, int vf, u8 *mac);
 static int igb_ndo_set_vf_vlan(struct net_device *netdev,
                               int vf, u16 vlan, u8 qos);
-static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate);
+static int igb_ndo_set_vf_bw(struct net_device *, int, int, int);
 static int igb_ndo_set_vf_spoofchk(struct net_device *netdev, int vf,
                                   bool setting);
 static int igb_ndo_get_vf_config(struct net_device *netdev, int vf,
@@ -215,10 +212,9 @@ static struct notifier_block dca_notifier = {
 static void igb_netpoll(struct net_device *);
 #endif
 #ifdef CONFIG_PCI_IOV
-static unsigned int max_vfs = 0;
+static unsigned int max_vfs;
 module_param(max_vfs, uint, 0);
-MODULE_PARM_DESC(max_vfs, "Maximum number of virtual functions to allocate "
-                 "per physical function");
+MODULE_PARM_DESC(max_vfs, "Maximum number of virtual functions to allocate per physical function");
 #endif /* CONFIG_PCI_IOV */
 
 static pci_ers_result_t igb_io_error_detected(struct pci_dev *,
@@ -384,8 +380,7 @@ static void igb_dump(struct igb_adapter *adapter)
        /* Print netdevice Info */
        if (netdev) {
                dev_info(&adapter->pdev->dev, "Net device Info\n");
-               pr_info("Device Name     state            trans_start      "
-                       "last_rx\n");
+               pr_info("Device Name     state            trans_start      last_rx\n");
                pr_info("%-15s %016lX %016lX %016lX\n", netdev->name,
                        netdev->state, netdev->trans_start, netdev->last_rx);
        }
@@ -438,9 +433,7 @@ static void igb_dump(struct igb_adapter *adapter)
                pr_info("------------------------------------\n");
                pr_info("TX QUEUE INDEX = %d\n", tx_ring->queue_index);
                pr_info("------------------------------------\n");
-               pr_info("T [desc]     [address 63:0  ] [PlPOCIStDDM Ln] "
-                       "[bi->dma       ] leng  ntw timestamp        "
-                       "bi->skb\n");
+               pr_info("T [desc]     [address 63:0  ] [PlPOCIStDDM Ln] [bi->dma       ] leng  ntw timestamp        bi->skb\n");
 
                for (i = 0; tx_ring->desc && (i < tx_ring->count); i++) {
                        const char *next_desc;
@@ -458,9 +451,8 @@ static void igb_dump(struct igb_adapter *adapter)
                        else
                                next_desc = "";
 
-                       pr_info("T [0x%03X]    %016llX %016llX %016llX"
-                               " %04X  %p %016llX %p%s\n", i,
-                               le64_to_cpu(u0->a),
+                       pr_info("T [0x%03X]    %016llX %016llX %016llX %04X  %p %016llX %p%s\n",
+                               i, le64_to_cpu(u0->a),
                                le64_to_cpu(u0->b),
                                (u64)dma_unmap_addr(buffer_info, dma),
                                dma_unmap_len(buffer_info, len),
@@ -519,10 +511,8 @@ rx_ring_summary:
                pr_info("------------------------------------\n");
                pr_info("RX QUEUE INDEX = %d\n", rx_ring->queue_index);
                pr_info("------------------------------------\n");
-               pr_info("R  [desc]      [ PktBuf     A0] [  HeadBuf   DD] "
-                       "[bi->dma       ] [bi->skb] <-- Adv Rx Read format\n");
-               pr_info("RWB[desc]      [PcsmIpSHl PtRs] [vl er S cks ln] -----"
-                       "----------- [bi->skb] <-- Adv Rx Write-Back format\n");
+               pr_info("R  [desc]      [ PktBuf     A0] [  HeadBuf   DD] [bi->dma       ] [bi->skb] <-- Adv Rx Read format\n");
+               pr_info("RWB[desc]      [PcsmIpSHl PtRs] [vl er S cks ln] ---------------- [bi->skb] <-- Adv Rx Write-Back format\n");
 
                for (i = 0; i < rx_ring->count; i++) {
                        const char *next_desc;
@@ -584,7 +574,7 @@ static int igb_get_i2c_data(void *data)
        struct e1000_hw *hw = &adapter->hw;
        s32 i2cctl = rd32(E1000_I2CPARAMS);
 
-       return ((i2cctl & E1000_I2C_DATA_IN) != 0);
+       return !!(i2cctl & E1000_I2C_DATA_IN);
 }
 
 /**
@@ -648,7 +638,7 @@ static int igb_get_i2c_clk(void *data)
        struct e1000_hw *hw = &adapter->hw;
        s32 i2cctl = rd32(E1000_I2CPARAMS);
 
-       return ((i2cctl & E1000_I2C_CLK_IN) != 0);
+       return !!(i2cctl & E1000_I2C_CLK_IN);
 }
 
 static const struct i2c_algo_bit_data igb_i2c_algo = {
@@ -681,9 +671,9 @@ struct net_device *igb_get_hw_dev(struct e1000_hw *hw)
 static int __init igb_init_module(void)
 {
        int ret;
+
        pr_info("%s - version %s\n",
               igb_driver_string, igb_driver_version);
-
        pr_info("%s\n", igb_copyright);
 
 #ifdef CONFIG_IGB_DCA
@@ -736,12 +726,14 @@ static void igb_cache_ring_register(struct igb_adapter *adapter)
                                adapter->rx_ring[i]->reg_idx = rbase_offset +
                                                               Q_IDX_82576(i);
                }
+               /* Fall through */
        case e1000_82575:
        case e1000_82580:
        case e1000_i350:
        case e1000_i354:
        case e1000_i210:
        case e1000_i211:
+               /* Fall through */
        default:
                for (; i < adapter->num_rx_queues; i++)
                        adapter->rx_ring[i]->reg_idx = rbase_offset + i;
@@ -1292,8 +1284,7 @@ static int igb_alloc_q_vector(struct igb_adapter *adapter,
                if (adapter->hw.mac.type >= e1000_82576)
                        set_bit(IGB_RING_FLAG_RX_SCTP_CSUM, &ring->flags);
 
-               /*
-                * On i350, i354, i210, and i211, loopback VLAN packets
+               /* On i350, i354, i210, and i211, loopback VLAN packets
                 * have the tag byte-swapped.
                 */
                if (adapter->hw.mac.type >= e1000_i350)
@@ -1345,6 +1336,7 @@ static int igb_alloc_q_vectors(struct igb_adapter *adapter)
        for (; v_idx < q_vectors; v_idx++) {
                int rqpv = DIV_ROUND_UP(rxr_remaining, q_vectors - v_idx);
                int tqpv = DIV_ROUND_UP(txr_remaining, q_vectors - v_idx);
+
                err = igb_alloc_q_vector(adapter, q_vectors, v_idx,
                                         tqpv, txr_idx, rqpv, rxr_idx);
 
@@ -1484,6 +1476,7 @@ static void igb_irq_disable(struct igb_adapter *adapter)
         */
        if (adapter->flags & IGB_FLAG_HAS_MSIX) {
                u32 regval = rd32(E1000_EIAM);
+
                wr32(E1000_EIAM, regval & ~adapter->eims_enable_mask);
                wr32(E1000_EIMC, adapter->eims_enable_mask);
                regval = rd32(E1000_EIAC);
@@ -1495,6 +1488,7 @@ static void igb_irq_disable(struct igb_adapter *adapter)
        wrfl();
        if (adapter->flags & IGB_FLAG_HAS_MSIX) {
                int i;
+
                for (i = 0; i < adapter->num_q_vectors; i++)
                        synchronize_irq(adapter->msix_entries[i].vector);
        } else {
@@ -1513,6 +1507,7 @@ static void igb_irq_enable(struct igb_adapter *adapter)
        if (adapter->flags & IGB_FLAG_HAS_MSIX) {
                u32 ims = E1000_IMS_LSC | E1000_IMS_DOUTSYNC | E1000_IMS_DRSTA;
                u32 regval = rd32(E1000_EIAC);
+
                wr32(E1000_EIAC, regval | adapter->eims_enable_mask);
                regval = rd32(E1000_EIAM);
                wr32(E1000_EIAM, regval | adapter->eims_enable_mask);
@@ -1745,6 +1740,7 @@ int igb_up(struct igb_adapter *adapter)
        /* notify VFs that reset has been completed */
        if (adapter->vfs_allocated_count) {
                u32 reg_data = rd32(E1000_CTRL_EXT);
+
                reg_data |= E1000_CTRL_EXT_PFRSTD;
                wr32(E1000_CTRL_EXT, reg_data);
        }
@@ -1787,7 +1783,7 @@ void igb_down(struct igb_adapter *adapter)
        wr32(E1000_TCTL, tctl);
        /* flush both disables and wait for them to finish */
        wrfl();
-       msleep(10);
+       usleep_range(10000, 11000);
 
        igb_irq_disable(adapter);
 
@@ -1827,7 +1823,7 @@ void igb_reinit_locked(struct igb_adapter *adapter)
 {
        WARN_ON(in_interrupt());
        while (test_and_set_bit(__IGB_RESETTING, &adapter->state))
-               msleep(1);
+               usleep_range(1000, 2000);
        igb_down(adapter);
        igb_up(adapter);
        clear_bit(__IGB_RESETTING, &adapter->state);
@@ -1960,6 +1956,7 @@ void igb_reset(struct igb_adapter *adapter)
        /* disable receive for all VFs and wait one second */
        if (adapter->vfs_allocated_count) {
                int i;
+
                for (i = 0 ; i < adapter->vfs_allocated_count; i++)
                        adapter->vf_data[i].flags &= IGB_VF_FLAG_PF_SET_MAC;
 
@@ -2087,7 +2084,7 @@ static const struct net_device_ops igb_netdev_ops = {
        .ndo_vlan_rx_kill_vid   = igb_vlan_rx_kill_vid,
        .ndo_set_vf_mac         = igb_ndo_set_vf_mac,
        .ndo_set_vf_vlan        = igb_ndo_set_vf_vlan,
-       .ndo_set_vf_tx_rate     = igb_ndo_set_vf_bw,
+       .ndo_set_vf_rate        = igb_ndo_set_vf_bw,
        .ndo_set_vf_spoofchk    = igb_ndo_set_vf_spoofchk,
        .ndo_get_vf_config      = igb_ndo_get_vf_config,
 #ifdef CONFIG_NET_POLL_CONTROLLER
@@ -2142,7 +2139,6 @@ void igb_set_fw_version(struct igb_adapter *adapter)
                }
                break;
        }
-       return;
 }
 
 /**
@@ -2203,11 +2199,11 @@ static void igb_init_mas(struct igb_adapter *adapter)
  **/
 static s32 igb_init_i2c(struct igb_adapter *adapter)
 {
-       s32 status = E1000_SUCCESS;
+       s32 status = 0;
 
        /* I2C interface supported on i350 devices */
        if (adapter->hw.mac.type != e1000_i350)
-               return E1000_SUCCESS;
+               return 0;
 
        /* Initialize the i2c bus which is controlled by the registers.
         * This bus will use the i2c_algo_bit structue that implements
@@ -2437,6 +2433,12 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        /* get firmware version for ethtool -i */
        igb_set_fw_version(adapter);
 
+       /* configure RXPBSIZE and TXPBSIZE */
+       if (hw->mac.type == e1000_i210) {
+               wr32(E1000_RXPBS, I210_RXPBSIZE_DEFAULT);
+               wr32(E1000_TXPBS, I210_TXPBSIZE_DEFAULT);
+       }
+
        setup_timer(&adapter->watchdog_timer, igb_watchdog,
                    (unsigned long) adapter);
        setup_timer(&adapter->phy_info_timer, igb_update_phy_info,
@@ -2529,7 +2531,8 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        }
 
        /* let the f/w know that the h/w is now under the control of the
-        * driver. */
+        * driver.
+        */
        igb_get_hw_control(adapter);
 
        strcpy(netdev->name, "eth%d");
@@ -3077,6 +3080,7 @@ static int __igb_open(struct net_device *netdev, bool resuming)
        /* notify VFs that reset has been completed */
        if (adapter->vfs_allocated_count) {
                u32 reg_data = rd32(E1000_CTRL_EXT);
+
                reg_data |= E1000_CTRL_EXT_PFRSTD;
                wr32(E1000_CTRL_EXT, reg_data);
        }
@@ -3248,7 +3252,7 @@ void igb_setup_tctl(struct igb_adapter *adapter)
  *  Configure a transmit ring after a reset.
  **/
 void igb_configure_tx_ring(struct igb_adapter *adapter,
-                           struct igb_ring *ring)
+                          struct igb_ring *ring)
 {
        struct e1000_hw *hw = &adapter->hw;
        u32 txdctl = 0;
@@ -3389,7 +3393,8 @@ static void igb_setup_mrqc(struct igb_adapter *adapter)
 
        if (adapter->rss_indir_tbl_init != num_rx_queues) {
                for (j = 0; j < IGB_RETA_SIZE; j++)
-                       adapter->rss_indir_tbl[j] = (j * num_rx_queues) / IGB_RETA_SIZE;
+                       adapter->rss_indir_tbl[j] =
+                       (j * num_rx_queues) / IGB_RETA_SIZE;
                adapter->rss_indir_tbl_init = num_rx_queues;
        }
        igb_write_rss_indir_tbl(adapter);
@@ -3430,6 +3435,7 @@ static void igb_setup_mrqc(struct igb_adapter *adapter)
                if (hw->mac.type > e1000_82575) {
                        /* Set the default pool for the PF's first queue */
                        u32 vtctl = rd32(E1000_VT_CTL);
+
                        vtctl &= ~(E1000_VT_CTL_DEFAULT_POOL_MASK |
                                   E1000_VT_CTL_DISABLE_DEF_POOL);
                        vtctl |= adapter->vfs_allocated_count <<
@@ -3511,7 +3517,7 @@ void igb_setup_rctl(struct igb_adapter *adapter)
 }
 
 static inline int igb_set_vf_rlpml(struct igb_adapter *adapter, int size,
-                                   int vfn)
+                                  int vfn)
 {
        struct e1000_hw *hw = &adapter->hw;
        u32 vmolr;
@@ -4058,7 +4064,8 @@ static void igb_check_wvbr(struct igb_adapter *adapter)
        switch (hw->mac.type) {
        case e1000_82576:
        case e1000_i350:
-               if (!(wvbr = rd32(E1000_WVBR)))
+               wvbr = rd32(E1000_WVBR);
+               if (!wvbr)
                        return;
                break;
        default:
@@ -4077,7 +4084,7 @@ static void igb_spoof_check(struct igb_adapter *adapter)
        if (!adapter->wvbr)
                return;
 
-       for(j = 0; j < adapter->vfs_allocated_count; j++) {
+       for (j = 0; j < adapter->vfs_allocated_count; j++) {
                if (adapter->wvbr & (1 << j) ||
                    adapter->wvbr & (1 << (j + IGB_STAGGERED_QUEUE_OFFSET))) {
                        dev_warn(&adapter->pdev->dev,
@@ -4209,14 +4216,15 @@ static void igb_watchdog_task(struct work_struct *work)
 
                if (!netif_carrier_ok(netdev)) {
                        u32 ctrl;
+
                        hw->mac.ops.get_speed_and_duplex(hw,
                                                         &adapter->link_speed,
                                                         &adapter->link_duplex);
 
                        ctrl = rd32(E1000_CTRL);
                        /* Links status message must follow this format */
-                       printk(KERN_INFO "igb: %s NIC Link is Up %d Mbps %s "
-                              "Duplex, Flow Control: %s\n",
+                       netdev_info(netdev,
+                              "igb: %s NIC Link is Up %d Mbps %s Duplex, Flow Control: %s\n",
                               netdev->name,
                               adapter->link_speed,
                               adapter->link_duplex == FULL_DUPLEX ?
@@ -4242,11 +4250,8 @@ static void igb_watchdog_task(struct work_struct *work)
 
                        /* check for thermal sensor event */
                        if (igb_thermal_sensor_event(hw,
-                           E1000_THSTAT_LINK_THROTTLE)) {
-                               netdev_info(netdev, "The network adapter link "
-                                           "speed was downshifted because it "
-                                           "overheated\n");
-                       }
+                           E1000_THSTAT_LINK_THROTTLE))
+                               netdev_info(netdev, "The network adapter link speed was downshifted because it overheated\n");
 
                        /* adjust timeout factor according to speed/duplex */
                        adapter->tx_timeout_factor = 1;
@@ -4277,12 +4282,11 @@ static void igb_watchdog_task(struct work_struct *work)
                        /* check for thermal sensor event */
                        if (igb_thermal_sensor_event(hw,
                            E1000_THSTAT_PWR_DOWN)) {
-                               netdev_err(netdev, "The network adapter was "
-                                          "stopped because it overheated\n");
+                               netdev_err(netdev, "The network adapter was stopped because it overheated\n");
                        }
 
                        /* Links status message must follow this format */
-                       printk(KERN_INFO "igb: %s NIC Link is Down\n",
+                       netdev_info(netdev, "igb: %s NIC Link is Down\n",
                               netdev->name);
                        netif_carrier_off(netdev);
 
@@ -4344,6 +4348,7 @@ static void igb_watchdog_task(struct work_struct *work)
        /* Cause software interrupt to ensure Rx ring is cleaned */
        if (adapter->flags & IGB_FLAG_HAS_MSIX) {
                u32 eics = 0;
+
                for (i = 0; i < adapter->num_q_vectors; i++)
                        eics |= adapter->q_vector[i]->eims_value;
                wr32(E1000_EICS, eics);
@@ -4483,13 +4488,12 @@ static void igb_update_itr(struct igb_q_vector *q_vector,
        case low_latency:  /* 50 usec aka 20000 ints/s */
                if (bytes > 10000) {
                        /* this if handles the TSO accounting */
-                       if (bytes/packets > 8000) {
+                       if (bytes/packets > 8000)
                                itrval = bulk_latency;
-                       } else if ((packets < 10) || ((bytes/packets) > 1200)) {
+                       else if ((packets < 10) || ((bytes/packets) > 1200))
                                itrval = bulk_latency;
-                       } else if ((packets > 35)) {
+                       else if ((packets > 35))
                                itrval = lowest_latency;
-                       }
                } else if (bytes/packets > 2000) {
                        itrval = bulk_latency;
                } else if (packets <= 2 && bytes < 512) {
@@ -4675,6 +4679,7 @@ static void igb_tx_csum(struct igb_ring *tx_ring, struct igb_tx_buffer *first)
                        return;
        } else {
                u8 l4_hdr = 0;
+
                switch (first->protocol) {
                case htons(ETH_P_IP):
                        vlan_macip_lens |= skb_network_header_len(skb);
@@ -4962,6 +4967,7 @@ netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb,
         */
        if (NETDEV_FRAG_PAGE_MAX_SIZE > IGB_MAX_DATA_PER_TXD) {
                unsigned short f;
+
                for (f = 0; f < skb_shinfo(skb)->nr_frags; f++)
                        count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size);
        } else {
@@ -5140,7 +5146,7 @@ static int igb_change_mtu(struct net_device *netdev, int new_mtu)
                max_frame = ETH_FRAME_LEN + ETH_FCS_LEN;
 
        while (test_and_set_bit(__IGB_RESETTING, &adapter->state))
-               msleep(1);
+               usleep_range(1000, 2000);
 
        /* igb_down has a dependency on max_frame_size */
        adapter->max_frame_size = max_frame;
@@ -5621,6 +5627,7 @@ static int igb_set_vf_promisc(struct igb_adapter *adapter, u32 *msgbuf, u32 vf)
                        vmolr |= E1000_VMOLR_MPME;
                } else if (vf_data->num_vf_mc_hashes) {
                        int j;
+
                        vmolr |= E1000_VMOLR_ROMPE;
                        for (j = 0; j < vf_data->num_vf_mc_hashes; j++)
                                igb_mta_set(hw, vf_data->vf_mc_hashes[j]);
@@ -5672,6 +5679,7 @@ static void igb_restore_vf_multicasts(struct igb_adapter *adapter)
 
        for (i = 0; i < adapter->vfs_allocated_count; i++) {
                u32 vmolr = rd32(E1000_VMOLR(i));
+
                vmolr &= ~(E1000_VMOLR_ROMPE | E1000_VMOLR_MPME);
 
                vf_data = &adapter->vf_data[i];
@@ -5770,6 +5778,7 @@ static s32 igb_vlvf_set(struct igb_adapter *adapter, u32 vid, bool add, u32 vf)
 
                        if (!adapter->vf_data[vf].vlans_enabled) {
                                u32 size;
+
                                reg = rd32(E1000_VMOLR(vf));
                                size = reg & E1000_VMOLR_RLPML_MASK;
                                size += 4;
@@ -5798,6 +5807,7 @@ static s32 igb_vlvf_set(struct igb_adapter *adapter, u32 vid, bool add, u32 vf)
                        adapter->vf_data[vf].vlans_enabled--;
                        if (!adapter->vf_data[vf].vlans_enabled) {
                                u32 size;
+
                                reg = rd32(E1000_VMOLR(vf));
                                size = reg & E1000_VMOLR_RLPML_MASK;
                                size -= 4;
@@ -5902,8 +5912,8 @@ static int igb_set_vf_vlan(struct igb_adapter *adapter, u32 *msgbuf, u32 vf)
         */
        if (!add && (adapter->netdev->flags & IFF_PROMISC)) {
                u32 vlvf, bits;
-
                int regndx = igb_find_vlvf_entry(adapter, vid);
+
                if (regndx < 0)
                        goto out;
                /* See if any other pools are set for this VLAN filter
@@ -6494,7 +6504,7 @@ static void igb_reuse_rx_page(struct igb_ring *rx_ring,
        rx_ring->next_to_alloc = (nta < rx_ring->count) ? nta : 0;
 
        /* transfer page from old buffer to new buffer */
-       memcpy(new_buff, old_buff, sizeof(struct igb_rx_buffer));
+       *new_buff = *old_buff;
 
        /* sync the buffer for use by the device */
        dma_sync_single_range_for_device(rx_ring->dev, old_buff->dma,
@@ -6963,6 +6973,7 @@ static void igb_process_skb_fields(struct igb_ring *rx_ring,
        if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
            igb_test_staterr(rx_desc, E1000_RXD_STAT_VP)) {
                u16 vid;
+
                if (igb_test_staterr(rx_desc, E1000_RXDEXT_STATERR_LB) &&
                    test_bit(IGB_RING_FLAG_RX_LB_VLAN_BSWAP, &rx_ring->flags))
                        vid = be16_to_cpu(rx_desc->wb.upper.vlan);
@@ -7051,7 +7062,7 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget)
        if (cleaned_count)
                igb_alloc_rx_buffers(rx_ring, cleaned_count);
 
-       return (total_packets < budget);
+       return total_packets < budget;
 }
 
 static bool igb_alloc_mapped_page(struct igb_ring *rx_ring,
@@ -7172,7 +7183,7 @@ static int igb_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
                break;
        case SIOCGMIIREG:
                if (igb_read_phy_reg(&adapter->hw, data->reg_num & 0x1F,
-                                    &data->val_out))
+                                    &data->val_out))
                        return -EIO;
                break;
        case SIOCSMIIREG:
@@ -7873,7 +7884,8 @@ static void igb_check_vf_rate_limit(struct igb_adapter *adapter)
        }
 }
 
-static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate)
+static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf,
+                            int min_tx_rate, int max_tx_rate)
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
@@ -7882,15 +7894,19 @@ static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate)
        if (hw->mac.type != e1000_82576)
                return -EOPNOTSUPP;
 
+       if (min_tx_rate)
+               return -EINVAL;
+
        actual_link_speed = igb_link_mbps(adapter->link_speed);
        if ((vf >= adapter->vfs_allocated_count) ||
            (!(rd32(E1000_STATUS) & E1000_STATUS_LU)) ||
-           (tx_rate < 0) || (tx_rate > actual_link_speed))
+           (max_tx_rate < 0) ||
+           (max_tx_rate > actual_link_speed))
                return -EINVAL;
 
        adapter->vf_rate_link_speed = actual_link_speed;
-       adapter->vf_data[vf].tx_rate = (u16)tx_rate;
-       igb_set_vf_rate_limit(hw, vf, tx_rate, actual_link_speed);
+       adapter->vf_data[vf].tx_rate = (u16)max_tx_rate;
+       igb_set_vf_rate_limit(hw, vf, max_tx_rate, actual_link_speed);
 
        return 0;
 }
@@ -7919,7 +7935,7 @@ static int igb_ndo_set_vf_spoofchk(struct net_device *netdev, int vf,
        wr32(reg_offset, reg_val);
 
        adapter->vf_data[vf].spoofchk_enabled = setting;
-       return E1000_SUCCESS;
+       return 0;
 }
 
 static int igb_ndo_get_vf_config(struct net_device *netdev,
@@ -7930,7 +7946,8 @@ static int igb_ndo_get_vf_config(struct net_device *netdev,
                return -EINVAL;
        ivi->vf = vf;
        memcpy(&ivi->mac, adapter->vf_data[vf].vf_mac_addresses, ETH_ALEN);
-       ivi->tx_rate = adapter->vf_data[vf].tx_rate;
+       ivi->max_tx_rate = adapter->vf_data[vf].tx_rate;
+       ivi->min_tx_rate = 0;
        ivi->vlan = adapter->vf_data[vf].pf_vlan;
        ivi->qos = adapter->vf_data[vf].pf_qos;
        ivi->spoofchk = adapter->vf_data[vf].spoofchk_enabled;
@@ -7955,11 +7972,13 @@ static void igb_vmm_control(struct igb_adapter *adapter)
                reg = rd32(E1000_DTXCTL);
                reg |= E1000_DTXCTL_VLAN_ADDED;
                wr32(E1000_DTXCTL, reg);
+               /* Fall through */
        case e1000_82580:
                /* enable replication vlan tag stripping */
                reg = rd32(E1000_RPLOLR);
                reg |= E1000_RPLOLR_STRVLAN;
                wr32(E1000_RPLOLR, reg);
+               /* Fall through */
        case e1000_i350:
                /* none of the above registers are supported by i350 */
                break;
@@ -8049,6 +8068,7 @@ static void igb_init_dmac(struct igb_adapter *adapter, u32 pba)
                } /* endif adapter->dmac is not disabled */
        } else if (hw->mac.type == e1000_82580) {
                u32 reg = rd32(E1000_PCIEMISC);
+
                wr32(E1000_PCIEMISC, reg & ~E1000_PCIEMISC_LX_DECISION);
                wr32(E1000_DMACR, 0);
        }
@@ -8077,8 +8097,7 @@ s32 igb_read_i2c_byte(struct e1000_hw *hw, u8 byte_offset,
 
        swfw_mask = E1000_SWFW_PHY0_SM;
 
-       if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask)
-           != E1000_SUCCESS)
+       if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask))
                return E1000_ERR_SWFW_SYNC;
 
        status = i2c_smbus_read_byte_data(this_client, byte_offset);
@@ -8088,7 +8107,7 @@ s32 igb_read_i2c_byte(struct e1000_hw *hw, u8 byte_offset,
                return E1000_ERR_I2C;
        else {
                *data = status;
-               return E1000_SUCCESS;
+               return 0;
        }
 }
 
@@ -8113,7 +8132,7 @@ s32 igb_write_i2c_byte(struct e1000_hw *hw, u8 byte_offset,
        if (!this_client)
                return E1000_ERR_I2C;
 
-       if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask) != E1000_SUCCESS)
+       if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask))
                return E1000_ERR_SWFW_SYNC;
        status = i2c_smbus_write_byte_data(this_client, byte_offset, data);
        hw->mac.ops.release_swfw_sync(hw, swfw_mask);
@@ -8121,7 +8140,7 @@ s32 igb_write_i2c_byte(struct e1000_hw *hw, u8 byte_offset,
        if (status)
                return E1000_ERR_I2C;
        else
-               return E1000_SUCCESS;
+               return 0;
 
 }
 
index ab25e49365f79e26cfcad7a7ad12b8c5192824c9..794c139f0cc04f86bf8319ea7e42840677db2c6c 100644 (file)
@@ -360,8 +360,8 @@ static int igb_ptp_settime_i210(struct ptp_clock_info *ptp,
        return 0;
 }
 
-static int igb_ptp_enable(struct ptp_clock_info *ptp,
-                         struct ptp_clock_request *rq, int on)
+static int igb_ptp_feature_enable(struct ptp_clock_info *ptp,
+                                 struct ptp_clock_request *rq, int on)
 {
        return -EOPNOTSUPP;
 }
@@ -559,10 +559,11 @@ int igb_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr)
        return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ?
                -EFAULT : 0;
 }
+
 /**
- * igb_ptp_set_ts_config - control hardware time stamping
- * @netdev:
- * @ifreq:
+ * igb_ptp_set_timestamp_mode - setup hardware for timestamping
+ * @adapter: networking device structure
+ * @config: hwtstamp configuration
  *
  * Outgoing time stamping can be enabled and disabled. Play nice and
  * disable it when requested, although it shouldn't case any overhead
@@ -575,12 +576,11 @@ int igb_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr)
  * type has to be specified. Matching the kind of event packet is
  * not supported, with the exception of "all V2 events regardless of
  * level 2 or 4".
- **/
-int igb_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr)
+ */
+static int igb_ptp_set_timestamp_mode(struct igb_adapter *adapter,
+                                     struct hwtstamp_config *config)
 {
-       struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
-       struct hwtstamp_config *config = &adapter->tstamp_config;
        u32 tsync_tx_ctl = E1000_TSYNCTXCTL_ENABLED;
        u32 tsync_rx_ctl = E1000_TSYNCRXCTL_ENABLED;
        u32 tsync_rx_cfg = 0;
@@ -588,9 +588,6 @@ int igb_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr)
        bool is_l2 = false;
        u32 regval;
 
-       if (copy_from_user(config, ifr->ifr_data, sizeof(*config)))
-               return -EFAULT;
-
        /* reserved for future extensions */
        if (config->flags)
                return -EINVAL;
@@ -725,7 +722,33 @@ int igb_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr)
        regval = rd32(E1000_RXSTMPL);
        regval = rd32(E1000_RXSTMPH);
 
-       return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ?
+       return 0;
+}
+
+/**
+ * igb_ptp_set_ts_config - set hardware time stamping config
+ * @netdev:
+ * @ifreq:
+ *
+ **/
+int igb_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr)
+{
+       struct igb_adapter *adapter = netdev_priv(netdev);
+       struct hwtstamp_config config;
+       int err;
+
+       if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
+               return -EFAULT;
+
+       err = igb_ptp_set_timestamp_mode(adapter, &config);
+       if (err)
+               return err;
+
+       /* save these settings for future reference */
+       memcpy(&adapter->tstamp_config, &config,
+              sizeof(adapter->tstamp_config));
+
+       return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
                -EFAULT : 0;
 }
 
@@ -745,7 +768,7 @@ void igb_ptp_init(struct igb_adapter *adapter)
                adapter->ptp_caps.adjtime = igb_ptp_adjtime_82576;
                adapter->ptp_caps.gettime = igb_ptp_gettime_82576;
                adapter->ptp_caps.settime = igb_ptp_settime_82576;
-               adapter->ptp_caps.enable = igb_ptp_enable;
+               adapter->ptp_caps.enable = igb_ptp_feature_enable;
                adapter->cc.read = igb_ptp_read_82576;
                adapter->cc.mask = CLOCKSOURCE_MASK(64);
                adapter->cc.mult = 1;
@@ -765,7 +788,7 @@ void igb_ptp_init(struct igb_adapter *adapter)
                adapter->ptp_caps.adjtime = igb_ptp_adjtime_82576;
                adapter->ptp_caps.gettime = igb_ptp_gettime_82576;
                adapter->ptp_caps.settime = igb_ptp_settime_82576;
-               adapter->ptp_caps.enable = igb_ptp_enable;
+               adapter->ptp_caps.enable = igb_ptp_feature_enable;
                adapter->cc.read = igb_ptp_read_82580;
                adapter->cc.mask = CLOCKSOURCE_MASK(IGB_NBITS_82580);
                adapter->cc.mult = 1;
@@ -784,7 +807,7 @@ void igb_ptp_init(struct igb_adapter *adapter)
                adapter->ptp_caps.adjtime = igb_ptp_adjtime_i210;
                adapter->ptp_caps.gettime = igb_ptp_gettime_i210;
                adapter->ptp_caps.settime = igb_ptp_settime_i210;
-               adapter->ptp_caps.enable = igb_ptp_enable;
+               adapter->ptp_caps.enable = igb_ptp_feature_enable;
                /* Enable the timer functions by clearing bit 31. */
                wr32(E1000_TSAUXC, 0x0);
                break;
@@ -820,6 +843,9 @@ void igb_ptp_init(struct igb_adapter *adapter)
                wr32(E1000_IMS, E1000_IMS_TS);
        }
 
+       adapter->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
+       adapter->tstamp_config.tx_type = HWTSTAMP_TX_OFF;
+
        adapter->ptp_clock = ptp_clock_register(&adapter->ptp_caps,
                                                &adapter->pdev->dev);
        if (IS_ERR(adapter->ptp_clock)) {
@@ -884,7 +910,7 @@ void igb_ptp_reset(struct igb_adapter *adapter)
                return;
 
        /* reset the tstamp_config */
-       memset(&adapter->tstamp_config, 0, sizeof(adapter->tstamp_config));
+       igb_ptp_set_timestamp_mode(adapter, &adapter->tstamp_config);
 
        switch (adapter->hw.mac.type) {
        case e1000_82576:
index 90eef07943f4d50bbd027646e170abca5abfb1a3..2178f87e9f610f3a95222275978d2b68a9d7832d 100644 (file)
@@ -101,8 +101,8 @@ static int igbvf_get_settings(struct net_device *netdev,
                else
                        ecmd->duplex = DUPLEX_HALF;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
 
        ecmd->autoneg = AUTONEG_DISABLE;
@@ -119,7 +119,6 @@ static int igbvf_set_settings(struct net_device *netdev,
 static void igbvf_get_pauseparam(struct net_device *netdev,
                                  struct ethtool_pauseparam *pause)
 {
-       return;
 }
 
 static int igbvf_set_pauseparam(struct net_device *netdev,
@@ -476,5 +475,5 @@ static const struct ethtool_ops igbvf_ethtool_ops = {
 
 void igbvf_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &igbvf_ethtool_ops);
+       netdev->ethtool_ops = &igbvf_ethtool_ops;
 }
index dbb7dd2f8e360e4d6c1013182e9d667a19d7fa56..b311e9e710d2636ddc3a17ad4d1cc10ba917cfb1 100644 (file)
@@ -107,8 +107,8 @@ ixgb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
                ethtool_cmd_speed_set(ecmd, SPEED_10000);
                ecmd->duplex = DUPLEX_FULL;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
 
        ecmd->autoneg = AUTONEG_DISABLE;
@@ -656,5 +656,5 @@ static const struct ethtool_ops ixgb_ethtool_ops = {
 
 void ixgb_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &ixgb_ethtool_ops);
+       netdev->ethtool_ops = &ixgb_ethtool_ops;
 }
index c6c4ca7d68e61a7676cd214e85f7837ee1309d39..ac9f2148cdc5ca6d20e81a43170a9550ff6ffce6 100644 (file)
@@ -155,7 +155,6 @@ struct vf_data_storage {
 struct vf_macvlans {
        struct list_head l;
        int vf;
-       int rar_entry;
        bool free;
        bool is_macvlan;
        u8 vf_macvlan[ETH_ALEN];
@@ -363,7 +362,7 @@ struct ixgbe_ring_container {
        for (pos = (head).ring; pos != NULL; pos = pos->next)
 
 #define MAX_RX_PACKET_BUFFERS ((adapter->flags & IXGBE_FLAG_DCB_ENABLED) \
-                              ? 8 : 1)
+                             ? 8 : 1)
 #define MAX_TX_PACKET_BUFFERS MAX_RX_PACKET_BUFFERS
 
 /* MAX_Q_VECTORS of these are allocated,
@@ -613,6 +612,15 @@ static inline void ixgbe_write_tail(struct ixgbe_ring *ring, u32 value)
 #define MAX_MSIX_VECTORS_82598 18
 #define MAX_Q_VECTORS_82598 16
 
+struct ixgbe_mac_addr {
+       u8 addr[ETH_ALEN];
+       u16 queue;
+       u16 state; /* bitmask */
+};
+#define IXGBE_MAC_STATE_DEFAULT                0x1
+#define IXGBE_MAC_STATE_MODIFIED       0x2
+#define IXGBE_MAC_STATE_IN_USE         0x4
+
 #define MAX_Q_VECTORS MAX_Q_VECTORS_82599
 #define MAX_MSIX_COUNT MAX_MSIX_VECTORS_82599
 
@@ -785,6 +793,7 @@ struct ixgbe_adapter {
 
        u32 timer_event_accumulator;
        u32 vferr_refcount;
+       struct ixgbe_mac_addr *mac_table;
        struct kobject *info_kobj;
 #ifdef CONFIG_IXGBE_HWMON
        struct hwmon_buff *ixgbe_hwmon_buff;
@@ -863,6 +872,13 @@ void ixgbe_update_stats(struct ixgbe_adapter *adapter);
 int ixgbe_init_interrupt_scheme(struct ixgbe_adapter *adapter);
 int ixgbe_wol_supported(struct ixgbe_adapter *adapter, u16 device_id,
                               u16 subdevice_id);
+#ifdef CONFIG_PCI_IOV
+void ixgbe_full_sync_mac_table(struct ixgbe_adapter *adapter);
+#endif
+int ixgbe_add_mac_filter(struct ixgbe_adapter *adapter,
+                        u8 *addr, u16 queue);
+int ixgbe_del_mac_filter(struct ixgbe_adapter *adapter,
+                        u8 *addr, u16 queue);
 void ixgbe_clear_interrupt_scheme(struct ixgbe_adapter *adapter);
 netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *, struct ixgbe_adapter *,
                                  struct ixgbe_ring *);
@@ -941,6 +957,7 @@ static inline struct netdev_queue *txring_txq(const struct ixgbe_ring *ring)
 }
 
 void ixgbe_ptp_init(struct ixgbe_adapter *adapter);
+void ixgbe_ptp_suspend(struct ixgbe_adapter *adapter);
 void ixgbe_ptp_stop(struct ixgbe_adapter *adapter);
 void ixgbe_ptp_overflow_check(struct ixgbe_adapter *adapter);
 void ixgbe_ptp_rx_hang(struct ixgbe_adapter *adapter);
index 4c78ea8946c1b48838db6d7e9ce890089ff6ab59..15609331ec170ddc6bb9e62c4227107fc6b50cad 100644 (file)
 #define IXGBE_82598_RX_PB_SIZE  512
 
 static s32 ixgbe_setup_copper_link_82598(struct ixgbe_hw *hw,
-                                         ixgbe_link_speed speed,
-                                         bool autoneg_wait_to_complete);
+                                        ixgbe_link_speed speed,
+                                        bool autoneg_wait_to_complete);
 static s32 ixgbe_read_i2c_eeprom_82598(struct ixgbe_hw *hw, u8 byte_offset,
-                                       u8 *eeprom_data);
+                                      u8 *eeprom_data);
 
 /**
  *  ixgbe_set_pcie_completion_timeout - set pci-e completion timeout
@@ -140,7 +140,7 @@ static s32 ixgbe_init_phy_ops_82598(struct ixgbe_hw *hw)
                phy->ops.setup_link = &ixgbe_setup_phy_link_tnx;
                phy->ops.check_link = &ixgbe_check_phy_link_tnx;
                phy->ops.get_firmware_version =
-                            &ixgbe_get_phy_firmware_version_tnx;
+                            &ixgbe_get_phy_firmware_version_tnx;
                break;
        case ixgbe_phy_nl:
                phy->ops.reset = &ixgbe_reset_phy_nl;
@@ -156,8 +156,8 @@ static s32 ixgbe_init_phy_ops_82598(struct ixgbe_hw *hw)
 
                /* Check to see if SFP+ module is supported */
                ret_val = ixgbe_get_sfp_init_sequence_offsets(hw,
-                                                           &list_offset,
-                                                           &data_offset);
+                                                           &list_offset,
+                                                           &data_offset);
                if (ret_val != 0) {
                        ret_val = IXGBE_ERR_SFP_NOT_SUPPORTED;
                        goto out;
@@ -219,8 +219,8 @@ static s32 ixgbe_start_hw_82598(struct ixgbe_hw *hw)
  *  Determines the link capabilities by reading the AUTOC register.
  **/
 static s32 ixgbe_get_link_capabilities_82598(struct ixgbe_hw *hw,
-                                             ixgbe_link_speed *speed,
-                                             bool *autoneg)
+                                            ixgbe_link_speed *speed,
+                                            bool *autoneg)
 {
        s32 status = 0;
        u32 autoc = 0;
@@ -337,19 +337,25 @@ static s32 ixgbe_fc_enable_82598(struct ixgbe_hw *hw)
        int i;
        bool link_up;
 
-       /*
-        * Validate the water mark configuration for packet buffer 0.  Zero
-        * water marks indicate that the packet buffer was not configured
-        * and the watermarks for packet buffer 0 should always be configured.
-        */
-       if (!hw->fc.low_water ||
-           !hw->fc.high_water[0] ||
-           !hw->fc.pause_time) {
-               hw_dbg(hw, "Invalid water mark configuration\n");
+       /* Validate the water mark configuration */
+       if (!hw->fc.pause_time) {
                ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS;
                goto out;
        }
 
+       /* Low water mark of zero causes XOFF floods */
+       for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
+               if ((hw->fc.current_mode & ixgbe_fc_tx_pause) &&
+                   hw->fc.high_water[i]) {
+                       if (!hw->fc.low_water[i] ||
+                           hw->fc.low_water[i] >= hw->fc.high_water[i]) {
+                               hw_dbg(hw, "Invalid water mark configuration\n");
+                               ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS;
+                               goto out;
+                       }
+               }
+       }
+
        /*
         * On 82598 having Rx FC on causes resets while doing 1G
         * so if it's on turn it off once we know link_speed. For
@@ -432,12 +438,11 @@ static s32 ixgbe_fc_enable_82598(struct ixgbe_hw *hw)
        IXGBE_WRITE_REG(hw, IXGBE_FCTRL, fctrl_reg);
        IXGBE_WRITE_REG(hw, IXGBE_RMCS, rmcs_reg);
 
-       fcrtl = (hw->fc.low_water << 10) | IXGBE_FCRTL_XONE;
-
        /* Set up and enable Rx high/low water mark thresholds, enable XON. */
        for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
                if ((hw->fc.current_mode & ixgbe_fc_tx_pause) &&
                    hw->fc.high_water[i]) {
+                       fcrtl = (hw->fc.low_water[i] << 10) | IXGBE_FCRTL_XONE;
                        fcrth = (hw->fc.high_water[i] << 10) | IXGBE_FCRTH_FCEN;
                        IXGBE_WRITE_REG(hw, IXGBE_FCRTL(i), fcrtl);
                        IXGBE_WRITE_REG(hw, IXGBE_FCRTH(i), fcrth);
@@ -468,7 +473,7 @@ out:
  *  Restarts the link.  Performs autonegotiation if needed.
  **/
 static s32 ixgbe_start_mac_link_82598(struct ixgbe_hw *hw,
-                                      bool autoneg_wait_to_complete)
+                                     bool autoneg_wait_to_complete)
 {
        u32 autoc_reg;
        u32 links_reg;
@@ -550,8 +555,8 @@ static s32 ixgbe_validate_link_ready(struct ixgbe_hw *hw)
  *  Reads the links register to determine if link is up and the current speed
  **/
 static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw,
-                                      ixgbe_link_speed *speed, bool *link_up,
-                                      bool link_up_wait_to_complete)
+                                     ixgbe_link_speed *speed, bool *link_up,
+                                     bool link_up_wait_to_complete)
 {
        u32 links_reg;
        u32 i;
@@ -567,7 +572,7 @@ static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw,
                hw->phy.ops.read_reg(hw, 0xC79F, MDIO_MMD_PMAPMD, &link_reg);
                hw->phy.ops.read_reg(hw, 0xC79F, MDIO_MMD_PMAPMD, &link_reg);
                hw->phy.ops.read_reg(hw, 0xC00C, MDIO_MMD_PMAPMD,
-                                    &adapt_comp_reg);
+                                    &adapt_comp_reg);
                if (link_up_wait_to_complete) {
                        for (i = 0; i < IXGBE_LINK_UP_TIME; i++) {
                                if ((link_reg & 1) &&
@@ -579,11 +584,11 @@ static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw,
                                }
                                msleep(100);
                                hw->phy.ops.read_reg(hw, 0xC79F,
-                                                    MDIO_MMD_PMAPMD,
-                                                    &link_reg);
+                                                    MDIO_MMD_PMAPMD,
+                                                    &link_reg);
                                hw->phy.ops.read_reg(hw, 0xC00C,
-                                                    MDIO_MMD_PMAPMD,
-                                                    &adapt_comp_reg);
+                                                    MDIO_MMD_PMAPMD,
+                                                    &adapt_comp_reg);
                        }
                } else {
                        if ((link_reg & 1) && ((adapt_comp_reg & 1) == 0))
@@ -656,7 +661,7 @@ static s32 ixgbe_setup_mac_link_82598(struct ixgbe_hw *hw,
 
        /* Set KX4/KX support according to speed requested */
        else if (link_mode == IXGBE_AUTOC_LMS_KX4_AN ||
-                link_mode == IXGBE_AUTOC_LMS_KX4_AN_1G_AN) {
+                link_mode == IXGBE_AUTOC_LMS_KX4_AN_1G_AN) {
                autoc &= ~IXGBE_AUTOC_KX4_KX_SUPP_MASK;
                if (speed & IXGBE_LINK_SPEED_10GB_FULL)
                        autoc |= IXGBE_AUTOC_KX4_SUPP;
@@ -689,14 +694,14 @@ static s32 ixgbe_setup_mac_link_82598(struct ixgbe_hw *hw,
  *  Sets the link speed in the AUTOC register in the MAC and restarts link.
  **/
 static s32 ixgbe_setup_copper_link_82598(struct ixgbe_hw *hw,
-                                               ixgbe_link_speed speed,
-                                               bool autoneg_wait_to_complete)
+                                              ixgbe_link_speed speed,
+                                              bool autoneg_wait_to_complete)
 {
        s32 status;
 
        /* Setup the PHY according to input speed */
        status = hw->phy.ops.setup_link_speed(hw, speed,
-                                             autoneg_wait_to_complete);
+                                             autoneg_wait_to_complete);
        /* Set up MAC */
        ixgbe_start_mac_link_82598(hw, autoneg_wait_to_complete);
 
@@ -735,28 +740,28 @@ static s32 ixgbe_reset_hw_82598(struct ixgbe_hw *hw)
        if (analog_val & IXGBE_ATLAS_PDN_TX_REG_EN) {
                /* Enable Tx Atlas so packets can be transmitted again */
                hw->mac.ops.read_analog_reg8(hw, IXGBE_ATLAS_PDN_LPBK,
-                                            &analog_val);
+                                            &analog_val);
                analog_val &= ~IXGBE_ATLAS_PDN_TX_REG_EN;
                hw->mac.ops.write_analog_reg8(hw, IXGBE_ATLAS_PDN_LPBK,
-                                             analog_val);
+                                             analog_val);
 
                hw->mac.ops.read_analog_reg8(hw, IXGBE_ATLAS_PDN_10G,
-                                            &analog_val);
+                                            &analog_val);
                analog_val &= ~IXGBE_ATLAS_PDN_TX_10G_QL_ALL;
                hw->mac.ops.write_analog_reg8(hw, IXGBE_ATLAS_PDN_10G,
-                                             analog_val);
+                                             analog_val);
 
                hw->mac.ops.read_analog_reg8(hw, IXGBE_ATLAS_PDN_1G,
-                                            &analog_val);
+                                            &analog_val);
                analog_val &= ~IXGBE_ATLAS_PDN_TX_1G_QL_ALL;
                hw->mac.ops.write_analog_reg8(hw, IXGBE_ATLAS_PDN_1G,
-                                             analog_val);
+                                             analog_val);
 
                hw->mac.ops.read_analog_reg8(hw, IXGBE_ATLAS_PDN_AN,
-                                            &analog_val);
+                                            &analog_val);
                analog_val &= ~IXGBE_ATLAS_PDN_TX_AN_QL_ALL;
                hw->mac.ops.write_analog_reg8(hw, IXGBE_ATLAS_PDN_AN,
-                                             analog_val);
+                                             analog_val);
        }
 
        /* Reset PHY */
@@ -955,7 +960,7 @@ static s32 ixgbe_clear_vfta_82598(struct ixgbe_hw *hw)
        for (vlanbyte = 0; vlanbyte < 4; vlanbyte++)
                for (offset = 0; offset < hw->mac.vft_size; offset++)
                        IXGBE_WRITE_REG(hw, IXGBE_VFTAVIND(vlanbyte, offset),
-                                       0);
+                                       0);
 
        return 0;
 }
@@ -973,7 +978,7 @@ static s32 ixgbe_read_analog_reg8_82598(struct ixgbe_hw *hw, u32 reg, u8 *val)
        u32  atlas_ctl;
 
        IXGBE_WRITE_REG(hw, IXGBE_ATLASCTL,
-                       IXGBE_ATLASCTL_WRITE_CMD | (reg << 8));
+                       IXGBE_ATLASCTL_WRITE_CMD | (reg << 8));
        IXGBE_WRITE_FLUSH(hw);
        udelay(10);
        atlas_ctl = IXGBE_READ_REG(hw, IXGBE_ATLASCTL);
@@ -1273,8 +1278,6 @@ static void ixgbe_set_rxpba_82598(struct ixgbe_hw *hw, int num_pb,
        /* Setup Tx packet buffer sizes */
        for (i = 0; i < IXGBE_MAX_PACKET_BUFFERS; i++)
                IXGBE_WRITE_REG(hw, IXGBE_TXPBSIZE(i), IXGBE_TXPBSIZE_40KB);
-
-       return;
 }
 
 static struct ixgbe_mac_operations mac_ops_82598 = {
index f32b3dd1ba8e18911fd58876ed20e494d92ae389..bc7c924240a52490d50531060783e0f13bc97f20 100644 (file)
@@ -48,17 +48,17 @@ static s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
                                                 ixgbe_link_speed speed,
                                                 bool autoneg_wait_to_complete);
 static s32 ixgbe_setup_mac_link_smartspeed(struct ixgbe_hw *hw,
-                                           ixgbe_link_speed speed,
-                                           bool autoneg_wait_to_complete);
+                                          ixgbe_link_speed speed,
+                                          bool autoneg_wait_to_complete);
 static void ixgbe_stop_mac_link_on_d3_82599(struct ixgbe_hw *hw);
 static s32 ixgbe_start_mac_link_82599(struct ixgbe_hw *hw,
                                      bool autoneg_wait_to_complete);
 static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw,
-                               ixgbe_link_speed speed,
-                               bool autoneg_wait_to_complete);
+                              ixgbe_link_speed speed,
+                              bool autoneg_wait_to_complete);
 static s32 ixgbe_setup_copper_link_82599(struct ixgbe_hw *hw,
-                                         ixgbe_link_speed speed,
-                                         bool autoneg_wait_to_complete);
+                                        ixgbe_link_speed speed,
+                                        bool autoneg_wait_to_complete);
 static s32 ixgbe_verify_fw_version_82599(struct ixgbe_hw *hw);
 static s32 ixgbe_read_i2c_byte_82599(struct ixgbe_hw *hw, u8 byte_offset,
                                     u8 dev_addr, u8 *data);
@@ -96,9 +96,9 @@ static void ixgbe_init_mac_link_ops_82599(struct ixgbe_hw *hw)
        if ((mac->ops.get_media_type(hw) == ixgbe_media_type_fiber) &&
            !ixgbe_mng_enabled(hw)) {
                mac->ops.disable_tx_laser =
-                                      &ixgbe_disable_tx_laser_multispeed_fiber;
+                                      &ixgbe_disable_tx_laser_multispeed_fiber;
                mac->ops.enable_tx_laser =
-                                       &ixgbe_enable_tx_laser_multispeed_fiber;
+                                       &ixgbe_enable_tx_laser_multispeed_fiber;
                mac->ops.flap_tx_laser = &ixgbe_flap_tx_laser_multispeed_fiber;
        } else {
                mac->ops.disable_tx_laser = NULL;
@@ -132,13 +132,13 @@ static s32 ixgbe_setup_sfp_modules_82599(struct ixgbe_hw *hw)
                hw->phy.ops.reset = NULL;
 
                ret_val = ixgbe_get_sfp_init_sequence_offsets(hw, &list_offset,
-                                                             &data_offset);
+                                                             &data_offset);
                if (ret_val != 0)
                        goto setup_sfp_out;
 
                /* PHY config will finish before releasing the semaphore */
                ret_val = hw->mac.ops.acquire_swfw_sync(hw,
-                                                       IXGBE_GSSR_MAC_CSR_SM);
+                                                       IXGBE_GSSR_MAC_CSR_SM);
                if (ret_val != 0) {
                        ret_val = IXGBE_ERR_SWFW_SYNC;
                        goto setup_sfp_out;
@@ -334,7 +334,7 @@ static s32 ixgbe_init_phy_ops_82599(struct ixgbe_hw *hw)
                phy->ops.check_link = &ixgbe_check_phy_link_tnx;
                phy->ops.setup_link = &ixgbe_setup_phy_link_tnx;
                phy->ops.get_firmware_version =
-                            &ixgbe_get_phy_firmware_version_tnx;
+                            &ixgbe_get_phy_firmware_version_tnx;
                break;
        default:
                break;
@@ -352,7 +352,7 @@ static s32 ixgbe_init_phy_ops_82599(struct ixgbe_hw *hw)
  *  Determines the link capabilities by reading the AUTOC register.
  **/
 static s32 ixgbe_get_link_capabilities_82599(struct ixgbe_hw *hw,
-                                             ixgbe_link_speed *speed,
+                                            ixgbe_link_speed *speed,
                                             bool *autoneg)
 {
        s32 status = 0;
@@ -543,7 +543,7 @@ static void ixgbe_stop_mac_link_on_d3_82599(struct ixgbe_hw *hw)
  *  Restarts the link.  Performs autonegotiation if needed.
  **/
 static s32 ixgbe_start_mac_link_82599(struct ixgbe_hw *hw,
-                               bool autoneg_wait_to_complete)
+                              bool autoneg_wait_to_complete)
 {
        u32 autoc_reg;
        u32 links_reg;
@@ -672,8 +672,8 @@ static void ixgbe_flap_tx_laser_multispeed_fiber(struct ixgbe_hw *hw)
  *  Set the link speed in the AUTOC register and restarts link.
  **/
 static s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
-                                          ixgbe_link_speed speed,
-                                          bool autoneg_wait_to_complete)
+                                         ixgbe_link_speed speed,
+                                         bool autoneg_wait_to_complete)
 {
        s32 status = 0;
        ixgbe_link_speed link_speed = IXGBE_LINK_SPEED_UNKNOWN;
@@ -820,8 +820,8 @@ static s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
         */
        if (speedcnt > 1)
                status = ixgbe_setup_mac_link_multispeed_fiber(hw,
-                                                              highest_link_speed,
-                                                              autoneg_wait_to_complete);
+                                                              highest_link_speed,
+                                                              autoneg_wait_to_complete);
 
 out:
        /* Set autoneg_advertised value based on input link speed */
@@ -1009,8 +1009,8 @@ static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw,
                if (speed & IXGBE_LINK_SPEED_1GB_FULL)
                        autoc |= IXGBE_AUTOC_KX_SUPP;
        } else if ((pma_pmd_1g == IXGBE_AUTOC_1G_SFI) &&
-                  (link_mode == IXGBE_AUTOC_LMS_1G_LINK_NO_AN ||
-                   link_mode == IXGBE_AUTOC_LMS_1G_AN)) {
+                  (link_mode == IXGBE_AUTOC_LMS_1G_LINK_NO_AN ||
+                   link_mode == IXGBE_AUTOC_LMS_1G_AN)) {
                /* Switch from 1G SFI to 10G SFI if requested */
                if ((speed == IXGBE_LINK_SPEED_10GB_FULL) &&
                    (pma_pmd_10g_serial == IXGBE_AUTOC2_10G_SFI)) {
@@ -1018,7 +1018,7 @@ static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw,
                        autoc |= IXGBE_AUTOC_LMS_10G_SERIAL;
                }
        } else if ((pma_pmd_10g_serial == IXGBE_AUTOC2_10G_SFI) &&
-                  (link_mode == IXGBE_AUTOC_LMS_10G_SERIAL)) {
+                  (link_mode == IXGBE_AUTOC_LMS_10G_SERIAL)) {
                /* Switch from 10G SFI to 1G SFI if requested */
                if ((speed == IXGBE_LINK_SPEED_1GB_FULL) &&
                    (pma_pmd_1g == IXGBE_AUTOC_1G_SFI)) {
@@ -1051,7 +1051,7 @@ static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw,
                                }
                                if (!(links_reg & IXGBE_LINKS_KX_AN_COMP)) {
                                        status =
-                                               IXGBE_ERR_AUTONEG_NOT_COMPLETE;
+                                               IXGBE_ERR_AUTONEG_NOT_COMPLETE;
                                        hw_dbg(hw, "Autoneg did not complete.\n");
                                }
                        }
@@ -1074,14 +1074,14 @@ out:
  *  Restarts link on PHY and MAC based on settings passed in.
  **/
 static s32 ixgbe_setup_copper_link_82599(struct ixgbe_hw *hw,
-                                         ixgbe_link_speed speed,
-                                         bool autoneg_wait_to_complete)
+                                        ixgbe_link_speed speed,
+                                        bool autoneg_wait_to_complete)
 {
        s32 status;
 
        /* Setup the PHY according to input speed */
        status = hw->phy.ops.setup_link_speed(hw, speed,
-                                             autoneg_wait_to_complete);
+                                             autoneg_wait_to_complete);
        /* Set up MAC */
        ixgbe_start_mac_link_82599(hw, autoneg_wait_to_complete);
 
@@ -1224,7 +1224,7 @@ mac_reset_top:
                    (hw->mac.orig_autoc2 & IXGBE_AUTOC2_UPPER_MASK)) {
                        autoc2 &= ~IXGBE_AUTOC2_UPPER_MASK;
                        autoc2 |= (hw->mac.orig_autoc2 &
-                                  IXGBE_AUTOC2_UPPER_MASK);
+                                  IXGBE_AUTOC2_UPPER_MASK);
                        IXGBE_WRITE_REG(hw, IXGBE_AUTOC2, autoc2);
                }
        }
@@ -1246,7 +1246,7 @@ mac_reset_top:
        /* Add the SAN MAC address to the RAR only if it's a valid address */
        if (is_valid_ether_addr(hw->mac.san_addr)) {
                hw->mac.ops.set_rar(hw, hw->mac.num_rar_entries - 1,
-                                   hw->mac.san_addr, 0, IXGBE_RAH_AV);
+                                   hw->mac.san_addr, 0, IXGBE_RAH_AV);
 
                /* Save the SAN MAC RAR index */
                hw->mac.san_mac_rar_index = hw->mac.num_rar_entries - 1;
@@ -1257,7 +1257,7 @@ mac_reset_top:
 
        /* Store the alternative WWNN/WWPN prefix */
        hw->mac.ops.get_wwn_prefix(hw, &hw->mac.wwnn_prefix,
-                                      &hw->mac.wwpn_prefix);
+                                      &hw->mac.wwpn_prefix);
 
 reset_hw_out:
        return status;
@@ -1271,6 +1271,7 @@ s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw)
 {
        int i;
        u32 fdirctrl = IXGBE_READ_REG(hw, IXGBE_FDIRCTRL);
+
        fdirctrl &= ~IXGBE_FDIRCTRL_INIT_DONE;
 
        /*
@@ -1284,8 +1285,7 @@ s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw)
                udelay(10);
        }
        if (i >= IXGBE_FDIRCMD_CMD_POLL) {
-               hw_dbg(hw, "Flow Director previous command isn't complete, "
-                      "aborting table re-initialization.\n");
+               hw_dbg(hw, "Flow Director previous command isn't complete, aborting table re-initialization.\n");
                return IXGBE_ERR_FDIR_REINIT_FAILED;
        }
 
@@ -1299,12 +1299,12 @@ s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw)
         * - write 0 to bit 8 of FDIRCMD register
         */
        IXGBE_WRITE_REG(hw, IXGBE_FDIRCMD,
-                       (IXGBE_READ_REG(hw, IXGBE_FDIRCMD) |
-                        IXGBE_FDIRCMD_CLEARHT));
+                       (IXGBE_READ_REG(hw, IXGBE_FDIRCMD) |
+                        IXGBE_FDIRCMD_CLEARHT));
        IXGBE_WRITE_FLUSH(hw);
        IXGBE_WRITE_REG(hw, IXGBE_FDIRCMD,
-                       (IXGBE_READ_REG(hw, IXGBE_FDIRCMD) &
-                        ~IXGBE_FDIRCMD_CLEARHT));
+                       (IXGBE_READ_REG(hw, IXGBE_FDIRCMD) &
+                        ~IXGBE_FDIRCMD_CLEARHT));
        IXGBE_WRITE_FLUSH(hw);
        /*
         * Clear FDIR Hash register to clear any leftover hashes
@@ -1319,7 +1319,7 @@ s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw)
        /* Poll init-done after we write FDIRCTRL register */
        for (i = 0; i < IXGBE_FDIR_INIT_DONE_POLL; i++) {
                if (IXGBE_READ_REG(hw, IXGBE_FDIRCTRL) &
-                                  IXGBE_FDIRCTRL_INIT_DONE)
+                                  IXGBE_FDIRCTRL_INIT_DONE)
                        break;
                usleep_range(1000, 2000);
        }
@@ -1368,7 +1368,7 @@ static void ixgbe_fdir_enable_82599(struct ixgbe_hw *hw, u32 fdirctrl)
        IXGBE_WRITE_FLUSH(hw);
        for (i = 0; i < IXGBE_FDIR_INIT_DONE_POLL; i++) {
                if (IXGBE_READ_REG(hw, IXGBE_FDIRCTRL) &
-                                  IXGBE_FDIRCTRL_INIT_DONE)
+                                  IXGBE_FDIRCTRL_INIT_DONE)
                        break;
                usleep_range(1000, 2000);
        }
@@ -1453,7 +1453,7 @@ do { \
                bucket_hash ^= hi_hash_dword >> n; \
        else if (IXGBE_ATR_SIGNATURE_HASH_KEY & (0x01 << (n + 16))) \
                sig_hash ^= hi_hash_dword << (16 - n); \
-} while (0);
+} while (0)
 
 /**
  *  ixgbe_atr_compute_sig_hash_82599 - Compute the signature hash
@@ -1529,9 +1529,9 @@ static u32 ixgbe_atr_compute_sig_hash_82599(union ixgbe_atr_hash_dword input,
  *  @queue: queue index to direct traffic to
  **/
 s32 ixgbe_fdir_add_signature_filter_82599(struct ixgbe_hw *hw,
-                                          union ixgbe_atr_hash_dword input,
-                                          union ixgbe_atr_hash_dword common,
-                                          u8 queue)
+                                         union ixgbe_atr_hash_dword input,
+                                         union ixgbe_atr_hash_dword common,
+                                         u8 queue)
 {
        u64  fdirhashcmd;
        u32  fdircmd;
@@ -1555,7 +1555,7 @@ s32 ixgbe_fdir_add_signature_filter_82599(struct ixgbe_hw *hw,
 
        /* configure FDIRCMD register */
        fdircmd = IXGBE_FDIRCMD_CMD_ADD_FLOW | IXGBE_FDIRCMD_FILTER_UPDATE |
-                 IXGBE_FDIRCMD_LAST | IXGBE_FDIRCMD_QUEUE_EN;
+                 IXGBE_FDIRCMD_LAST | IXGBE_FDIRCMD_QUEUE_EN;
        fdircmd |= input.formatted.flow_type << IXGBE_FDIRCMD_FLOW_TYPE_SHIFT;
        fdircmd |= (u32)queue << IXGBE_FDIRCMD_RX_QUEUE_SHIFT;
 
@@ -1579,7 +1579,7 @@ do { \
                bucket_hash ^= lo_hash_dword >> n; \
        if (IXGBE_ATR_BUCKET_HASH_KEY & (0x01 << (n + 16))) \
                bucket_hash ^= hi_hash_dword >> n; \
-} while (0);
+} while (0)
 
 /**
  *  ixgbe_atr_compute_perfect_hash_82599 - Compute the perfect filter hash
@@ -1651,6 +1651,7 @@ void ixgbe_atr_compute_perfect_hash_82599(union ixgbe_atr_input *input,
 static u32 ixgbe_get_fdirtcpm_82599(union ixgbe_atr_input *input_mask)
 {
        u32 mask = ntohs(input_mask->formatted.dst_port);
+
        mask <<= IXGBE_FDIRTCPM_DPORTM_SHIFT;
        mask |= ntohs(input_mask->formatted.src_port);
        mask = ((mask & 0x55555555) << 1) | ((mask & 0xAAAAAAAA) >> 1);
@@ -1885,7 +1886,7 @@ static s32 ixgbe_read_analog_reg8_82599(struct ixgbe_hw *hw, u32 reg, u8 *val)
        u32  core_ctl;
 
        IXGBE_WRITE_REG(hw, IXGBE_CORECTL, IXGBE_CORECTL_WRITE_CMD |
-                       (reg << 8));
+                       (reg << 8));
        IXGBE_WRITE_FLUSH(hw);
        udelay(10);
        core_ctl = IXGBE_READ_REG(hw, IXGBE_CORECTL);
index 981b8a7b100df5360954d056ff2c5c4d25246d73..4e5385a2a4658c8c5f0dc9ea4ee279d08693351d 100644 (file)
@@ -41,7 +41,7 @@ static void ixgbe_release_eeprom_semaphore(struct ixgbe_hw *hw);
 static s32 ixgbe_ready_eeprom(struct ixgbe_hw *hw);
 static void ixgbe_standby_eeprom(struct ixgbe_hw *hw);
 static void ixgbe_shift_out_eeprom_bits(struct ixgbe_hw *hw, u16 data,
-                                        u16 count);
+                                       u16 count);
 static u16 ixgbe_shift_in_eeprom_bits(struct ixgbe_hw *hw, u16 count);
 static void ixgbe_raise_eeprom_clk(struct ixgbe_hw *hw, u32 *eec);
 static void ixgbe_lower_eeprom_clk(struct ixgbe_hw *hw, u32 *eec);
@@ -271,6 +271,7 @@ out:
  **/
 s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw)
 {
+       s32 ret_val;
        u32 ctrl_ext;
 
        /* Set the media type */
@@ -292,12 +293,15 @@ s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw)
        IXGBE_WRITE_FLUSH(hw);
 
        /* Setup flow control */
-       ixgbe_setup_fc(hw);
+       ret_val = ixgbe_setup_fc(hw);
+       if (!ret_val)
+               goto out;
 
        /* Clear adapter stopped flag */
        hw->adapter_stopped = false;
 
-       return 0;
+out:
+       return ret_val;
 }
 
 /**
@@ -481,7 +485,7 @@ s32 ixgbe_clear_hw_cntrs_generic(struct ixgbe_hw *hw)
  *  Reads the part number string from the EEPROM.
  **/
 s32 ixgbe_read_pba_string_generic(struct ixgbe_hw *hw, u8 *pba_num,
-                                  u32 pba_num_size)
+                                 u32 pba_num_size)
 {
        s32 ret_val;
        u16 data;
@@ -814,9 +818,8 @@ s32 ixgbe_init_eeprom_params_generic(struct ixgbe_hw *hw)
                        eeprom->address_bits = 16;
                else
                        eeprom->address_bits = 8;
-               hw_dbg(hw, "Eeprom params: type = %d, size = %d, address bits: "
-                         "%d\n", eeprom->type, eeprom->word_size,
-                         eeprom->address_bits);
+               hw_dbg(hw, "Eeprom params: type = %d, size = %d, address bits: %d\n",
+                      eeprom->type, eeprom->word_size, eeprom->address_bits);
        }
 
        return 0;
@@ -1388,8 +1391,7 @@ static s32 ixgbe_get_eeprom_semaphore(struct ixgbe_hw *hw)
        }
 
        if (i == timeout) {
-               hw_dbg(hw, "Driver can't access the Eeprom - SMBI Semaphore "
-                      "not granted.\n");
+               hw_dbg(hw, "Driver can't access the Eeprom - SMBI Semaphore not granted.\n");
                /*
                 * this release is particularly important because our attempts
                 * above to get the semaphore may have succeeded, and if there
@@ -1434,14 +1436,12 @@ static s32 ixgbe_get_eeprom_semaphore(struct ixgbe_hw *hw)
                 * was not granted because we don't have access to the EEPROM
                 */
                if (i >= timeout) {
-                       hw_dbg(hw, "SWESMBI Software EEPROM semaphore "
-                              "not granted.\n");
+                       hw_dbg(hw, "SWESMBI Software EEPROM semaphore not granted.\n");
                        ixgbe_release_eeprom_semaphore(hw);
                        status = IXGBE_ERR_EEPROM;
                }
        } else {
-               hw_dbg(hw, "Software semaphore SMBI between device drivers "
-                      "not granted.\n");
+               hw_dbg(hw, "Software semaphore SMBI between device drivers not granted.\n");
        }
 
        return status;
@@ -1483,7 +1483,7 @@ static s32 ixgbe_ready_eeprom(struct ixgbe_hw *hw)
         */
        for (i = 0; i < IXGBE_EEPROM_MAX_RETRY_SPI; i += 5) {
                ixgbe_shift_out_eeprom_bits(hw, IXGBE_EEPROM_RDSR_OPCODE_SPI,
-                                           IXGBE_EEPROM_OPCODE_BITS);
+                                           IXGBE_EEPROM_OPCODE_BITS);
                spi_stat_reg = (u8)ixgbe_shift_in_eeprom_bits(hw, 8);
                if (!(spi_stat_reg & IXGBE_EEPROM_STATUS_RDY_SPI))
                        break;
@@ -1532,7 +1532,7 @@ static void ixgbe_standby_eeprom(struct ixgbe_hw *hw)
  *  @count: number of bits to shift out
  **/
 static void ixgbe_shift_out_eeprom_bits(struct ixgbe_hw *hw, u16 data,
-                                        u16 count)
+                                       u16 count)
 {
        u32 eec;
        u32 mask;
@@ -1736,7 +1736,7 @@ u16 ixgbe_calc_eeprom_checksum_generic(struct ixgbe_hw *hw)
  *  caller does not need checksum_val, the value can be NULL.
  **/
 s32 ixgbe_validate_eeprom_checksum_generic(struct ixgbe_hw *hw,
-                                           u16 *checksum_val)
+                                          u16 *checksum_val)
 {
        s32 status;
        u16 checksum;
@@ -1809,7 +1809,7 @@ s32 ixgbe_update_eeprom_checksum_generic(struct ixgbe_hw *hw)
  *  Puts an ethernet address into a receive address register.
  **/
 s32 ixgbe_set_rar_generic(struct ixgbe_hw *hw, u32 index, u8 *addr, u32 vmdq,
-                          u32 enable_addr)
+                         u32 enable_addr)
 {
        u32 rar_low, rar_high;
        u32 rar_entries = hw->mac.num_rar_entries;
@@ -2053,7 +2053,7 @@ s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw,
 
        if (hw->addr_ctrl.mta_in_use > 0)
                IXGBE_WRITE_REG(hw, IXGBE_MCSTCTRL,
-                               IXGBE_MCSTCTRL_MFE | hw->mac.mc_filter_type);
+                               IXGBE_MCSTCTRL_MFE | hw->mac.mc_filter_type);
 
        hw_dbg(hw, "ixgbe_update_mc_addr_list_generic Complete\n");
        return 0;
@@ -2071,7 +2071,7 @@ s32 ixgbe_enable_mc_generic(struct ixgbe_hw *hw)
 
        if (a->mta_in_use > 0)
                IXGBE_WRITE_REG(hw, IXGBE_MCSTCTRL, IXGBE_MCSTCTRL_MFE |
-                               hw->mac.mc_filter_type);
+                               hw->mac.mc_filter_type);
 
        return 0;
 }
@@ -2106,19 +2106,25 @@ s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw)
        u32 fcrtl, fcrth;
        int i;
 
-       /*
-        * Validate the water mark configuration for packet buffer 0.  Zero
-        * water marks indicate that the packet buffer was not configured
-        * and the watermarks for packet buffer 0 should always be configured.
-        */
-       if (!hw->fc.low_water ||
-           !hw->fc.high_water[0] ||
-           !hw->fc.pause_time) {
-               hw_dbg(hw, "Invalid water mark configuration\n");
+       /* Validate the water mark configuration. */
+       if (!hw->fc.pause_time) {
                ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS;
                goto out;
        }
 
+       /* Low water mark of zero causes XOFF floods */
+       for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
+               if ((hw->fc.current_mode & ixgbe_fc_tx_pause) &&
+                   hw->fc.high_water[i]) {
+                       if (!hw->fc.low_water[i] ||
+                           hw->fc.low_water[i] >= hw->fc.high_water[i]) {
+                               hw_dbg(hw, "Invalid water mark configuration\n");
+                               ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS;
+                               goto out;
+                       }
+               }
+       }
+
        /* Negotiate the fc mode to use */
        ixgbe_fc_autoneg(hw);
 
@@ -2181,12 +2187,11 @@ s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw)
        IXGBE_WRITE_REG(hw, IXGBE_MFLCN, mflcn_reg);
        IXGBE_WRITE_REG(hw, IXGBE_FCCFG, fccfg_reg);
 
-       fcrtl = (hw->fc.low_water << 10) | IXGBE_FCRTL_XONE;
-
        /* Set up and enable Rx high/low water mark thresholds, enable XON. */
        for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
                if ((hw->fc.current_mode & ixgbe_fc_tx_pause) &&
                    hw->fc.high_water[i]) {
+                       fcrtl = (hw->fc.low_water[i] << 10) | IXGBE_FCRTL_XONE;
                        IXGBE_WRITE_REG(hw, IXGBE_FCRTL_82599(i), fcrtl);
                        fcrth = (hw->fc.high_water[i] << 10) | IXGBE_FCRTH_FCEN;
                } else {
@@ -2654,8 +2659,7 @@ s32 ixgbe_disable_rx_buff_generic(struct ixgbe_hw *hw)
 
        /* For informational purposes only */
        if (i >= IXGBE_MAX_SECRX_POLL)
-               hw_dbg(hw, "Rx unit being enabled before security "
-                      "path fully disabled.  Continuing with init.\n");
+               hw_dbg(hw, "Rx unit being enabled before security path fully disabled. Continuing with init.\n");
 
        return 0;
 
@@ -2782,7 +2786,7 @@ out:
  *  get and set mac_addr routines.
  **/
 static s32 ixgbe_get_san_mac_addr_offset(struct ixgbe_hw *hw,
-                                        u16 *san_mac_offset)
+                                       u16 *san_mac_offset)
 {
        s32 ret_val;
 
@@ -2828,7 +2832,7 @@ s32 ixgbe_get_san_mac_addr_generic(struct ixgbe_hw *hw, u8 *san_mac_addr)
        hw->mac.ops.set_lan_id(hw);
        /* apply the port offset to the address offset */
        (hw->bus.func) ? (san_mac_offset += IXGBE_SAN_MAC_ADDR_PORT1_OFFSET) :
-                        (san_mac_offset += IXGBE_SAN_MAC_ADDR_PORT0_OFFSET);
+                        (san_mac_offset += IXGBE_SAN_MAC_ADDR_PORT0_OFFSET);
        for (i = 0; i < 3; i++) {
                ret_val = hw->eeprom.ops.read(hw, san_mac_offset,
                                              &san_mac_data);
@@ -3068,7 +3072,7 @@ static s32 ixgbe_find_vlvf_slot(struct ixgbe_hw *hw, u32 vlan)
  *  Turn on/off specified VLAN in the VLAN filter table.
  **/
 s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind,
-                           bool vlan_on)
+                          bool vlan_on)
 {
        s32 regindex;
        u32 bitindex;
@@ -3190,9 +3194,9 @@ s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind,
                                 * Ignore it. */
                                vfta_changed = false;
                        }
-               }
-               else
+               } else {
                        IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index), 0);
+               }
        }
 
        if (vfta_changed)
@@ -3292,7 +3296,7 @@ s32 ixgbe_check_mac_link_generic(struct ixgbe_hw *hw, ixgbe_link_speed *speed,
  *  block to check the support for the alternative WWNN/WWPN prefix support.
  **/
 s32 ixgbe_get_wwn_prefix_generic(struct ixgbe_hw *hw, u16 *wwnn_prefix,
-                                        u16 *wwpn_prefix)
+                                       u16 *wwpn_prefix)
 {
        u16 offset, caps;
        u16 alt_san_mac_blk_offset;
index f12c40fb5537a18604ff030f4adc4287946dbff4..2ae5d4b8fc93e318bba749c6042affb8eb1616a7 100644 (file)
@@ -39,7 +39,7 @@ s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw);
 s32 ixgbe_start_hw_gen2(struct ixgbe_hw *hw);
 s32 ixgbe_clear_hw_cntrs_generic(struct ixgbe_hw *hw);
 s32 ixgbe_read_pba_string_generic(struct ixgbe_hw *hw, u8 *pba_num,
-                                  u32 pba_num_size);
+                                 u32 pba_num_size);
 s32 ixgbe_get_mac_addr_generic(struct ixgbe_hw *hw, u8 *mac_addr);
 enum ixgbe_bus_width ixgbe_convert_bus_width(u16 link_status);
 enum ixgbe_bus_speed ixgbe_convert_bus_speed(u16 link_status);
@@ -61,16 +61,16 @@ s32 ixgbe_write_eewr_generic(struct ixgbe_hw *hw, u16 offset, u16 data);
 s32 ixgbe_write_eewr_buffer_generic(struct ixgbe_hw *hw, u16 offset,
                                    u16 words, u16 *data);
 s32 ixgbe_read_eeprom_bit_bang_generic(struct ixgbe_hw *hw, u16 offset,
-                                       u16 *data);
+                                      u16 *data);
 s32 ixgbe_read_eeprom_buffer_bit_bang_generic(struct ixgbe_hw *hw, u16 offset,
                                              u16 words, u16 *data);
 u16 ixgbe_calc_eeprom_checksum_generic(struct ixgbe_hw *hw);
 s32 ixgbe_validate_eeprom_checksum_generic(struct ixgbe_hw *hw,
-                                           u16 *checksum_val);
+                                          u16 *checksum_val);
 s32 ixgbe_update_eeprom_checksum_generic(struct ixgbe_hw *hw);
 
 s32 ixgbe_set_rar_generic(struct ixgbe_hw *hw, u32 index, u8 *addr, u32 vmdq,
-                          u32 enable_addr);
+                         u32 enable_addr);
 s32 ixgbe_clear_rar_generic(struct ixgbe_hw *hw, u32 index);
 s32 ixgbe_init_rx_addrs_generic(struct ixgbe_hw *hw);
 s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw,
@@ -92,13 +92,13 @@ s32 ixgbe_set_vmdq_san_mac_generic(struct ixgbe_hw *hw, u32 vmdq);
 s32 ixgbe_clear_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq);
 s32 ixgbe_init_uta_tables_generic(struct ixgbe_hw *hw);
 s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan,
-                           u32 vind, bool vlan_on);
+                          u32 vind, bool vlan_on);
 s32 ixgbe_clear_vfta_generic(struct ixgbe_hw *hw);
 s32 ixgbe_check_mac_link_generic(struct ixgbe_hw *hw,
-                                 ixgbe_link_speed *speed,
-                                 bool *link_up, bool link_up_wait_to_complete);
+                                ixgbe_link_speed *speed,
+                                bool *link_up, bool link_up_wait_to_complete);
 s32 ixgbe_get_wwn_prefix_generic(struct ixgbe_hw *hw, u16 *wwnn_prefix,
-                                 u16 *wwpn_prefix);
+                                u16 *wwpn_prefix);
 
 s32 prot_autoc_read_generic(struct ixgbe_hw *hw, bool *, u32 *reg_val);
 s32 prot_autoc_write_generic(struct ixgbe_hw *hw, u32 reg_val, bool locked);
@@ -141,8 +141,6 @@ static inline bool ixgbe_removed(void __iomem *addr)
        return unlikely(!addr);
 }
 
-void ixgbe_check_remove(struct ixgbe_hw *hw, u32 reg);
-
 static inline void ixgbe_write_reg(struct ixgbe_hw *hw, u32 reg, u32 value)
 {
        u8 __iomem *reg_addr = ACCESS_ONCE(hw->hw_addr);
@@ -172,18 +170,7 @@ static inline void ixgbe_write_reg64(struct ixgbe_hw *hw, u32 reg, u64 value)
 }
 #define IXGBE_WRITE_REG64(a, reg, value) ixgbe_write_reg64((a), (reg), (value))
 
-static inline u32 ixgbe_read_reg(struct ixgbe_hw *hw, u32 reg)
-{
-       u8 __iomem *reg_addr = ACCESS_ONCE(hw->hw_addr);
-       u32 value;
-
-       if (ixgbe_removed(reg_addr))
-               return IXGBE_FAILED_READ_REG;
-       value = readl(reg_addr + reg);
-       if (unlikely(value == IXGBE_FAILED_READ_REG))
-               ixgbe_check_remove(hw, reg);
-       return value;
-}
+u32 ixgbe_read_reg(struct ixgbe_hw *hw, u32 reg);
 #define IXGBE_READ_REG(a, reg) ixgbe_read_reg((a), (reg))
 
 #define IXGBE_WRITE_REG_ARRAY(a, reg, offset, value) \
index e055e000131bdcf0c32280270361c957d0a4d8a0..a689ee0d4bedc2d837d0765b62ab2dd0ffe1d120 100644 (file)
@@ -267,7 +267,7 @@ void ixgbe_dcb_unpack_map(struct ixgbe_dcb_config *cfg, int direction, u8 *map)
  * Configure dcb settings and enable dcb mode.
  */
 s32 ixgbe_dcb_hw_config(struct ixgbe_hw *hw,
-                        struct ixgbe_dcb_config *dcb_config)
+                       struct ixgbe_dcb_config *dcb_config)
 {
        s32 ret = 0;
        u8 pfc_en;
@@ -389,7 +389,6 @@ static void ixgbe_dcb_read_rtrup2tc_82599(struct ixgbe_hw *hw, u8 *map)
        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)
index 7a77f37a7cbcbd6b7b5b87dd78e9e50b677e778c..d3ba63f9ad3712fcf82d1bafc3408070112d1aa0 100644 (file)
@@ -208,7 +208,6 @@ s32 ixgbe_dcb_config_pfc_82598(struct ixgbe_hw *hw, u8 pfc_en)
 
        IXGBE_WRITE_REG(hw, IXGBE_FCTRL, reg);
 
-       fcrtl = (hw->fc.low_water << 10) | IXGBE_FCRTL_XONE;
        /* Configure PFC Tx thresholds per TC */
        for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
                if (!(pfc_en & (1 << i))) {
@@ -217,6 +216,7 @@ s32 ixgbe_dcb_config_pfc_82598(struct ixgbe_hw *hw, u8 pfc_en)
                        continue;
                }
 
+               fcrtl = (hw->fc.low_water[i] << 10) | IXGBE_FCRTL_XONE;
                reg = (hw->fc.high_water[i] << 10) | IXGBE_FCRTH_FCEN;
                IXGBE_WRITE_REG(hw, IXGBE_FCRTL(i), fcrtl);
                IXGBE_WRITE_REG(hw, IXGBE_FCRTH(i), reg);
index bdb99b3b0f30f4ee2253e81bc72e84fb6a0d1e30..3b932fe64ab66c916f86f4184f45d626cc687cb1 100644 (file)
@@ -242,7 +242,6 @@ s32 ixgbe_dcb_config_pfc_82599(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc)
                        max_tc = prio_tc[i];
        }
 
-       fcrtl = (hw->fc.low_water << 10) | IXGBE_FCRTL_XONE;
 
        /* Configure PFC Tx thresholds per TC */
        for (i = 0; i <= max_tc; i++) {
@@ -257,6 +256,7 @@ s32 ixgbe_dcb_config_pfc_82599(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc)
 
                if (enabled) {
                        reg = (hw->fc.high_water[i] << 10) | IXGBE_FCRTH_FCEN;
+                       fcrtl = (hw->fc.low_water[i] << 10) | IXGBE_FCRTL_XONE;
                        IXGBE_WRITE_REG(hw, IXGBE_FCRTL_82599(i), fcrtl);
                } else {
                        reg = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(i)) - 32;
index d5a1e3db0774a5580b8191126e682c33191f7fa6..90c370230e2001250752725ee7d10c0ed71de341 100644 (file)
 
 /* DCB register definitions */
 #define IXGBE_RTTDCS_TDPAC      0x00000001 /* 0 Round Robin,
-                                            * 1 WSP - Weighted Strict Priority
-                                            */
+                                           * 1 WSP - Weighted Strict Priority
+                                           */
 #define IXGBE_RTTDCS_VMPAC      0x00000002 /* 0 Round Robin,
-                                            * 1 WRR - Weighted Round Robin
-                                            */
+                                           * 1 WRR - Weighted Round Robin
+                                           */
 #define IXGBE_RTTDCS_TDRM       0x00000010 /* Transmit Recycle Mode */
 #define IXGBE_RTTDCS_ARBDIS     0x00000040 /* DCB arbiter disable */
 #define IXGBE_RTTDCS_BDPM       0x00400000 /* Bypass Data Pipe - must clear! */
 #define IXGBE_RTTDCS_BPBFSM     0x00800000 /* Bypass PB Free Space - must
-                                             * clear!
-                                             */
+                                            * clear!
+                                            */
 #define IXGBE_RTTDCS_SPEED_CHG  0x80000000 /* Link speed change */
 
 /* Receive UP2TC mapping */
 #define IXGBE_RTRPT4C_LSP       0x80000000 /* LSP enable bit */
 
 #define IXGBE_RDRXCTL_MPBEN     0x00000010 /* DMA config for multiple packet
-                                            * buffers enable
-                                            */
+                                           * buffers enable
+                                           */
 #define IXGBE_RDRXCTL_MCEN      0x00000040 /* DMA config for multiple cores
-                                            * (RSS) enable
-                                            */
+                                           * (RSS) enable
+                                           */
 
 /* RTRPCS Bit Masks */
 #define IXGBE_RTRPCS_RRM        0x00000002 /* Receive Recycle Mode enable */
@@ -81,8 +81,8 @@
 
 /* RTTPCS Bit Masks */
 #define IXGBE_RTTPCS_TPPAC      0x00000020 /* 0 Round Robin,
-                                            * 1 SP - Strict Priority
-                                            */
+                                           * 1 SP - Strict Priority
+                                           */
 #define IXGBE_RTTPCS_ARBDIS     0x00000040 /* Arbiter disable */
 #define IXGBE_RTTPCS_TPRM       0x00000100 /* Transmit Recycle Mode enable */
 #define IXGBE_RTTPCS_ARBD_SHIFT 22
index edd89a1ef27f67f0f40d0cefba9ebed3e39f3185..5172b6b12c097679b9f9b532869b500ea6cb4124 100644 (file)
@@ -192,8 +192,8 @@ static void ixgbe_dcbnl_get_perm_hw_addr(struct net_device *netdev,
 }
 
 static void ixgbe_dcbnl_set_pg_tc_cfg_tx(struct net_device *netdev, int tc,
-                                         u8 prio, u8 bwg_id, u8 bw_pct,
-                                         u8 up_map)
+                                        u8 prio, u8 bwg_id, u8 bw_pct,
+                                        u8 up_map)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
@@ -210,7 +210,7 @@ static void ixgbe_dcbnl_set_pg_tc_cfg_tx(struct net_device *netdev, int tc,
 }
 
 static void ixgbe_dcbnl_set_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id,
-                                          u8 bw_pct)
+                                         u8 bw_pct)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
@@ -218,8 +218,8 @@ static void ixgbe_dcbnl_set_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id,
 }
 
 static void ixgbe_dcbnl_set_pg_tc_cfg_rx(struct net_device *netdev, int tc,
-                                         u8 prio, u8 bwg_id, u8 bw_pct,
-                                         u8 up_map)
+                                        u8 prio, u8 bwg_id, u8 bw_pct,
+                                        u8 up_map)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
@@ -236,7 +236,7 @@ static void ixgbe_dcbnl_set_pg_tc_cfg_rx(struct net_device *netdev, int tc,
 }
 
 static void ixgbe_dcbnl_set_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id,
-                                          u8 bw_pct)
+                                         u8 bw_pct)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
@@ -244,8 +244,8 @@ static void ixgbe_dcbnl_set_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id,
 }
 
 static void ixgbe_dcbnl_get_pg_tc_cfg_tx(struct net_device *netdev, int tc,
-                                         u8 *prio, u8 *bwg_id, u8 *bw_pct,
-                                         u8 *up_map)
+                                        u8 *prio, u8 *bwg_id, u8 *bw_pct,
+                                        u8 *up_map)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
@@ -256,7 +256,7 @@ static void ixgbe_dcbnl_get_pg_tc_cfg_tx(struct net_device *netdev, int tc,
 }
 
 static void ixgbe_dcbnl_get_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id,
-                                          u8 *bw_pct)
+                                         u8 *bw_pct)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
@@ -264,8 +264,8 @@ static void ixgbe_dcbnl_get_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id,
 }
 
 static void ixgbe_dcbnl_get_pg_tc_cfg_rx(struct net_device *netdev, int tc,
-                                         u8 *prio, u8 *bwg_id, u8 *bw_pct,
-                                         u8 *up_map)
+                                        u8 *prio, u8 *bwg_id, u8 *bw_pct,
+                                        u8 *up_map)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
@@ -276,7 +276,7 @@ static void ixgbe_dcbnl_get_pg_tc_cfg_rx(struct net_device *netdev, int tc,
 }
 
 static void ixgbe_dcbnl_get_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id,
-                                          u8 *bw_pct)
+                                         u8 *bw_pct)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
@@ -284,7 +284,7 @@ static void ixgbe_dcbnl_get_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id,
 }
 
 static void ixgbe_dcbnl_set_pfc_cfg(struct net_device *netdev, int priority,
-                                    u8 setting)
+                                   u8 setting)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
@@ -295,7 +295,7 @@ static void ixgbe_dcbnl_set_pfc_cfg(struct net_device *netdev, int priority,
 }
 
 static void ixgbe_dcbnl_get_pfc_cfg(struct net_device *netdev, int priority,
-                                    u8 *setting)
+                                   u8 *setting)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
index 472b0f450bf90d4dc23fe5995d28d0d80c2d940e..5e2c1e35e517f915d2e6f43a5e7c1368d2f81bfe 100644 (file)
@@ -253,8 +253,7 @@ void ixgbe_dbg_adapter_init(struct ixgbe_adapter *adapter)
  **/
 void ixgbe_dbg_adapter_exit(struct ixgbe_adapter *adapter)
 {
-       if (adapter->ixgbe_dbg_adapter)
-               debugfs_remove_recursive(adapter->ixgbe_dbg_adapter);
+       debugfs_remove_recursive(adapter->ixgbe_dbg_adapter);
        adapter->ixgbe_dbg_adapter = NULL;
 }
 
index 6c55c14d082aa6285f43941e5d96ae86acdbb21b..a452730a327864111d72d13679fc47c2be870b7c 100644 (file)
@@ -141,8 +141,8 @@ static const struct ixgbe_stats ixgbe_gstrings_stats[] = {
                         sizeof(((struct ixgbe_adapter *)0)->stats.pxofftxc)) \
                        / sizeof(u64))
 #define IXGBE_STATS_LEN (IXGBE_GLOBAL_STATS_LEN + \
-                         IXGBE_PB_STATS_LEN + \
-                         IXGBE_QUEUE_STATS_LEN)
+                        IXGBE_PB_STATS_LEN + \
+                        IXGBE_QUEUE_STATS_LEN)
 
 static const char ixgbe_gstrings_test[][ETH_GSTRING_LEN] = {
        "Register test  (offline)", "Eeprom test    (offline)",
@@ -152,7 +152,7 @@ static const char ixgbe_gstrings_test[][ETH_GSTRING_LEN] = {
 #define IXGBE_TEST_LEN sizeof(ixgbe_gstrings_test) / ETH_GSTRING_LEN
 
 static int ixgbe_get_settings(struct net_device *netdev,
-                              struct ethtool_cmd *ecmd)
+                             struct ethtool_cmd *ecmd)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
@@ -161,13 +161,6 @@ static int ixgbe_get_settings(struct net_device *netdev,
        bool autoneg = false;
        bool link_up;
 
-       /* SFP type is needed for get_link_capabilities */
-       if (hw->phy.media_type & (ixgbe_media_type_fiber |
-                                 ixgbe_media_type_fiber_qsfp)) {
-               if (hw->phy.sfp_type == ixgbe_sfp_type_not_present)
-                               hw->phy.ops.identify_sfp(hw);
-       }
-
        hw->mac.ops.get_link_capabilities(hw, &supported_link, &autoneg);
 
        /* set the supported link speeds */
@@ -303,15 +296,15 @@ static int ixgbe_get_settings(struct net_device *netdev,
                }
                ecmd->duplex = DUPLEX_FULL;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
 
        return 0;
 }
 
 static int ixgbe_set_settings(struct net_device *netdev,
-                              struct ethtool_cmd *ecmd)
+                             struct ethtool_cmd *ecmd)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
@@ -368,7 +361,7 @@ static int ixgbe_set_settings(struct net_device *netdev,
 }
 
 static void ixgbe_get_pauseparam(struct net_device *netdev,
-                                 struct ethtool_pauseparam *pause)
+                                struct ethtool_pauseparam *pause)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
@@ -390,7 +383,7 @@ static void ixgbe_get_pauseparam(struct net_device *netdev,
 }
 
 static int ixgbe_set_pauseparam(struct net_device *netdev,
-                                struct ethtool_pauseparam *pause)
+                               struct ethtool_pauseparam *pause)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
@@ -450,7 +443,7 @@ static int ixgbe_get_regs_len(struct net_device *netdev)
 #define IXGBE_GET_STAT(_A_, _R_) _A_->stats._R_
 
 static void ixgbe_get_regs(struct net_device *netdev,
-                           struct ethtool_regs *regs, void *p)
+                          struct ethtool_regs *regs, void *p)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
@@ -812,7 +805,7 @@ static int ixgbe_get_eeprom_len(struct net_device *netdev)
 }
 
 static int ixgbe_get_eeprom(struct net_device *netdev,
-                            struct ethtool_eeprom *eeprom, u8 *bytes)
+                           struct ethtool_eeprom *eeprom, u8 *bytes)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
@@ -918,7 +911,7 @@ err:
 }
 
 static void ixgbe_get_drvinfo(struct net_device *netdev,
-                              struct ethtool_drvinfo *drvinfo)
+                             struct ethtool_drvinfo *drvinfo)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        u32 nvm_track_id;
@@ -940,7 +933,7 @@ static void ixgbe_get_drvinfo(struct net_device *netdev,
 }
 
 static void ixgbe_get_ringparam(struct net_device *netdev,
-                                struct ethtool_ringparam *ring)
+                               struct ethtool_ringparam *ring)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_ring *tx_ring = adapter->tx_ring[0];
@@ -953,7 +946,7 @@ static void ixgbe_get_ringparam(struct net_device *netdev,
 }
 
 static int ixgbe_set_ringparam(struct net_device *netdev,
-                               struct ethtool_ringparam *ring)
+                              struct ethtool_ringparam *ring)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_ring *temp_ring;
@@ -1082,7 +1075,7 @@ static int ixgbe_get_sset_count(struct net_device *netdev, int sset)
 }
 
 static void ixgbe_get_ethtool_stats(struct net_device *netdev,
-                                    struct ethtool_stats *stats, u64 *data)
+                                   struct ethtool_stats *stats, u64 *data)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct rtnl_link_stats64 temp;
@@ -1110,7 +1103,7 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev,
                }
 
                data[i] = (ixgbe_gstrings_stats[i].sizeof_stat ==
-                          sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+                          sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
        }
        for (j = 0; j < netdev->num_tx_queues; j++) {
                ring = adapter->tx_ring[j];
@@ -1180,7 +1173,7 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev,
 }
 
 static void ixgbe_get_strings(struct net_device *netdev, u32 stringset,
-                              u8 *data)
+                             u8 *data)
 {
        char *p = (char *)data;
        int i;
@@ -1357,8 +1350,7 @@ static bool reg_pattern_test(struct ixgbe_adapter *adapter, u64 *data, int reg,
                ixgbe_write_reg(&adapter->hw, reg, test_pattern[pat] & write);
                val = ixgbe_read_reg(&adapter->hw, reg);
                if (val != (test_pattern[pat] & write & mask)) {
-                       e_err(drv, "pattern test reg %04X failed: got "
-                             "0x%08X expected 0x%08X\n",
+                       e_err(drv, "pattern test reg %04X failed: got 0x%08X expected 0x%08X\n",
                              reg, val, (test_pattern[pat] & write & mask));
                        *data = reg;
                        ixgbe_write_reg(&adapter->hw, reg, before);
@@ -1382,8 +1374,8 @@ static bool reg_set_and_check(struct ixgbe_adapter *adapter, u64 *data, int reg,
        ixgbe_write_reg(&adapter->hw, reg, write & mask);
        val = ixgbe_read_reg(&adapter->hw, reg);
        if ((write & mask) != (val & mask)) {
-               e_err(drv, "set/check reg %04X test failed: got 0x%08X "
-                     "expected 0x%08X\n", reg, (val & mask), (write & mask));
+               e_err(drv, "set/check reg %04X test failed: got 0x%08X expected 0x%08X\n",
+                     reg, (val & mask), (write & mask));
                *data = reg;
                ixgbe_write_reg(&adapter->hw, reg, before);
                return true;
@@ -1430,8 +1422,8 @@ static int ixgbe_reg_test(struct ixgbe_adapter *adapter, u64 *data)
        ixgbe_write_reg(&adapter->hw, IXGBE_STATUS, toggle);
        after = ixgbe_read_reg(&adapter->hw, IXGBE_STATUS) & toggle;
        if (value != after) {
-               e_err(drv, "failed STATUS register test got: 0x%08X "
-                     "expected: 0x%08X\n", after, value);
+               e_err(drv, "failed STATUS register test got: 0x%08X expected: 0x%08X\n",
+                     after, value);
                *data = 1;
                return 1;
        }
@@ -1533,10 +1525,10 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data)
                        return -1;
                }
        } else if (!request_irq(irq, ixgbe_test_intr, IRQF_PROBE_SHARED,
-                               netdev->name, netdev)) {
+                               netdev->name, netdev)) {
                shared_int = false;
        } else if (request_irq(irq, ixgbe_test_intr, IRQF_SHARED,
-                              netdev->name, netdev)) {
+                              netdev->name, netdev)) {
                *data = 1;
                return -1;
        }
@@ -1563,9 +1555,9 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data)
                         */
                        adapter->test_icr = 0;
                        IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC,
-                                       ~mask & 0x00007FFF);
+                                       ~mask & 0x00007FFF);
                        IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS,
-                                       ~mask & 0x00007FFF);
+                                       ~mask & 0x00007FFF);
                        IXGBE_WRITE_FLUSH(&adapter->hw);
                        usleep_range(10000, 20000);
 
@@ -1587,7 +1579,7 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data)
                IXGBE_WRITE_FLUSH(&adapter->hw);
                usleep_range(10000, 20000);
 
-               if (!(adapter->test_icr &mask)) {
+               if (!(adapter->test_icr & mask)) {
                        *data = 4;
                        break;
                }
@@ -1602,9 +1594,9 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data)
                         */
                        adapter->test_icr = 0;
                        IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC,
-                                       ~mask & 0x00007FFF);
+                                       ~mask & 0x00007FFF);
                        IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS,
-                                       ~mask & 0x00007FFF);
+                                       ~mask & 0x00007FFF);
                        IXGBE_WRITE_FLUSH(&adapter->hw);
                        usleep_range(10000, 20000);
 
@@ -1964,7 +1956,7 @@ out:
 }
 
 static void ixgbe_diag_test(struct net_device *netdev,
-                            struct ethtool_test *eth_test, u64 *data)
+                           struct ethtool_test *eth_test, u64 *data)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        bool if_running = netif_running(netdev);
@@ -1987,10 +1979,7 @@ static void ixgbe_diag_test(struct net_device *netdev,
                        int i;
                        for (i = 0; i < adapter->num_vfs; i++) {
                                if (adapter->vfinfo[i].clear_to_send) {
-                                       netdev_warn(netdev, "%s",
-                                                   "offline diagnostic is not "
-                                                   "supported when VFs are "
-                                                   "present\n");
+                                       netdev_warn(netdev, "offline diagnostic is not supported when VFs are present\n");
                                        data[0] = 1;
                                        data[1] = 1;
                                        data[2] = 1;
@@ -2037,8 +2026,7 @@ static void ixgbe_diag_test(struct net_device *netdev,
                 * loopback diagnostic. */
                if (adapter->flags & (IXGBE_FLAG_SRIOV_ENABLED |
                                      IXGBE_FLAG_VMDQ_ENABLED)) {
-                       e_info(hw, "Skip MAC loopback diagnostic in VT "
-                              "mode\n");
+                       e_info(hw, "Skip MAC loopback diagnostic in VT mode\n");
                        data[3] = 0;
                        goto skip_loopback;
                }
@@ -2078,7 +2066,7 @@ skip_ol_tests:
 }
 
 static int ixgbe_wol_exclusion(struct ixgbe_adapter *adapter,
-                               struct ethtool_wolinfo *wol)
+                              struct ethtool_wolinfo *wol)
 {
        struct ixgbe_hw *hw = &adapter->hw;
        int retval = 0;
@@ -2094,12 +2082,12 @@ static int ixgbe_wol_exclusion(struct ixgbe_adapter *adapter,
 }
 
 static void ixgbe_get_wol(struct net_device *netdev,
-                          struct ethtool_wolinfo *wol)
+                         struct ethtool_wolinfo *wol)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
        wol->supported = WAKE_UCAST | WAKE_MCAST |
-                        WAKE_BCAST | WAKE_MAGIC;
+                        WAKE_BCAST | WAKE_MAGIC;
        wol->wolopts = 0;
 
        if (ixgbe_wol_exclusion(adapter, wol) ||
@@ -2181,7 +2169,7 @@ static int ixgbe_set_phys_id(struct net_device *netdev,
 }
 
 static int ixgbe_get_coalesce(struct net_device *netdev,
-                              struct ethtool_coalesce *ec)
+                             struct ethtool_coalesce *ec)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
@@ -2222,8 +2210,7 @@ static bool ixgbe_update_rsc(struct ixgbe_adapter *adapter)
            adapter->rx_itr_setting > IXGBE_MIN_RSC_ITR) {
                if (!(adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED)) {
                        adapter->flags2 |= IXGBE_FLAG2_RSC_ENABLED;
-                       e_info(probe, "rx-usecs value high enough "
-                                     "to re-enable RSC\n");
+                       e_info(probe, "rx-usecs value high enough to re-enable RSC\n");
                        return true;
                }
        /* if interrupt rate is too high then disable RSC */
@@ -2236,7 +2223,7 @@ static bool ixgbe_update_rsc(struct ixgbe_adapter *adapter)
 }
 
 static int ixgbe_set_coalesce(struct net_device *netdev,
-                              struct ethtool_coalesce *ec)
+                             struct ethtool_coalesce *ec)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_q_vector *q_vector;
@@ -2421,9 +2408,11 @@ static int ixgbe_get_rss_hash_opts(struct ixgbe_adapter *adapter,
        switch (cmd->flow_type) {
        case TCP_V4_FLOW:
                cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+               /* fallthrough */
        case UDP_V4_FLOW:
                if (adapter->flags2 & IXGBE_FLAG2_RSS_FIELD_IPV4_UDP)
                        cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+               /* fallthrough */
        case SCTP_V4_FLOW:
        case AH_ESP_V4_FLOW:
        case AH_V4_FLOW:
@@ -2433,9 +2422,11 @@ static int ixgbe_get_rss_hash_opts(struct ixgbe_adapter *adapter,
                break;
        case TCP_V6_FLOW:
                cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+               /* fallthrough */
        case UDP_V6_FLOW:
                if (adapter->flags2 & IXGBE_FLAG2_RSS_FIELD_IPV6_UDP)
                        cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+               /* fallthrough */
        case SCTP_V6_FLOW:
        case AH_ESP_V6_FLOW:
        case AH_V6_FLOW:
@@ -2787,8 +2778,7 @@ static int ixgbe_set_rss_hash_opt(struct ixgbe_adapter *adapter,
 
                if ((flags2 & UDP_RSS_FLAGS) &&
                    !(adapter->flags2 & UDP_RSS_FLAGS))
-                       e_warn(drv, "enabling UDP RSS: fragmented packets"
-                              " may arrive out of order to the stack above\n");
+                       e_warn(drv, "enabling UDP RSS: fragmented packets may arrive out of order to the stack above\n");
 
                adapter->flags2 = flags2;
 
@@ -3099,5 +3089,5 @@ static const struct ethtool_ops ixgbe_ethtool_ops = {
 
 void ixgbe_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &ixgbe_ethtool_ops);
+       netdev->ethtool_ops = &ixgbe_ethtool_ops;
 }
index b16cc786750dec8bb7bf29749db331a31f028ec9..0772b7730fce92de4e2ff54d44f6528397c2b3a9 100644 (file)
@@ -81,9 +81,7 @@ struct ixgbe_fcoe {
        void *extra_ddp_buffer;
        dma_addr_t extra_ddp_buffer_dma;
        unsigned long mode;
-#ifdef CONFIG_IXGBE_DCB
        u8 up;
-#endif
 };
 
 #endif /* _IXGBE_FCOE_H */
index 2067d392cc3d33850254e9a2d719a2db6491b6f5..2d9451e3968624db0b527dd00e9a106adfae16be 100644 (file)
@@ -1113,8 +1113,8 @@ static void ixgbe_set_interrupt_capability(struct ixgbe_adapter *adapter)
        err = pci_enable_msi(adapter->pdev);
        if (err) {
                netif_printk(adapter, hw, KERN_DEBUG, adapter->netdev,
-                            "Unable to allocate MSI interrupt, "
-                            "falling back to legacy.  Error: %d\n", err);
+                            "Unable to allocate MSI interrupt, falling back to legacy.  Error: %d\n",
+                            err);
                return;
        }
        adapter->flags |= IXGBE_FLAG_MSI_ENABLED;
index c047c3ef8d71e8d03c7860f6beb03023a9bf5d3b..f5aa3311ea286bbd45d56933ad015e33f48d69fb 100644 (file)
@@ -301,7 +301,7 @@ static void ixgbe_remove_adapter(struct ixgbe_hw *hw)
                ixgbe_service_event_schedule(adapter);
 }
 
-void ixgbe_check_remove(struct ixgbe_hw *hw, u32 reg)
+static void ixgbe_check_remove(struct ixgbe_hw *hw, u32 reg)
 {
        u32 value;
 
@@ -320,6 +320,32 @@ void ixgbe_check_remove(struct ixgbe_hw *hw, u32 reg)
                ixgbe_remove_adapter(hw);
 }
 
+/**
+ * ixgbe_read_reg - Read from device register
+ * @hw: hw specific details
+ * @reg: offset of register to read
+ *
+ * Returns : value read or IXGBE_FAILED_READ_REG if removed
+ *
+ * This function is used to read device registers. It checks for device
+ * removal by confirming any read that returns all ones by checking the
+ * status register value for all ones. This function avoids reading from
+ * the hardware if a removal was previously detected in which case it
+ * returns IXGBE_FAILED_READ_REG (all ones).
+ */
+u32 ixgbe_read_reg(struct ixgbe_hw *hw, u32 reg)
+{
+       u8 __iomem *reg_addr = ACCESS_ONCE(hw->hw_addr);
+       u32 value;
+
+       if (ixgbe_removed(reg_addr))
+               return IXGBE_FAILED_READ_REG;
+       value = readl(reg_addr + reg);
+       if (unlikely(value == IXGBE_FAILED_READ_REG))
+               ixgbe_check_remove(hw, reg);
+       return value;
+}
+
 static bool ixgbe_check_cfg_remove(struct ixgbe_hw *hw, struct pci_dev *pdev)
 {
        u16 value;
@@ -3742,35 +3768,6 @@ static int ixgbe_vlan_rx_kill_vid(struct net_device *netdev,
        return 0;
 }
 
-/**
- * ixgbe_vlan_filter_disable - helper to disable hw vlan filtering
- * @adapter: driver data
- */
-static void ixgbe_vlan_filter_disable(struct ixgbe_adapter *adapter)
-{
-       struct ixgbe_hw *hw = &adapter->hw;
-       u32 vlnctrl;
-
-       vlnctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL);
-       vlnctrl &= ~(IXGBE_VLNCTRL_VFE | IXGBE_VLNCTRL_CFIEN);
-       IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, vlnctrl);
-}
-
-/**
- * ixgbe_vlan_filter_enable - helper to enable hw vlan filtering
- * @adapter: driver data
- */
-static void ixgbe_vlan_filter_enable(struct ixgbe_adapter *adapter)
-{
-       struct ixgbe_hw *hw = &adapter->hw;
-       u32 vlnctrl;
-
-       vlnctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL);
-       vlnctrl |= IXGBE_VLNCTRL_VFE;
-       vlnctrl &= ~IXGBE_VLNCTRL_CFIEN;
-       IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, vlnctrl);
-}
-
 /**
  * ixgbe_vlan_strip_disable - helper to disable hw vlan stripping
  * @adapter: driver data
@@ -3849,6 +3846,158 @@ static void ixgbe_restore_vlan(struct ixgbe_adapter *adapter)
                ixgbe_vlan_rx_add_vid(adapter->netdev, htons(ETH_P_8021Q), vid);
 }
 
+/**
+ * ixgbe_write_mc_addr_list - write multicast addresses to MTA
+ * @netdev: network interface device structure
+ *
+ * Writes multicast address list to the MTA hash table.
+ * Returns: -ENOMEM on failure
+ *                0 on no addresses written
+ *                X on writing X addresses to MTA
+ **/
+static int ixgbe_write_mc_addr_list(struct net_device *netdev)
+{
+       struct ixgbe_adapter *adapter = netdev_priv(netdev);
+       struct ixgbe_hw *hw = &adapter->hw;
+
+       if (!netif_running(netdev))
+               return 0;
+
+       if (hw->mac.ops.update_mc_addr_list)
+               hw->mac.ops.update_mc_addr_list(hw, netdev);
+       else
+               return -ENOMEM;
+
+#ifdef CONFIG_PCI_IOV
+       ixgbe_restore_vf_multicasts(adapter);
+#endif
+
+       return netdev_mc_count(netdev);
+}
+
+#ifdef CONFIG_PCI_IOV
+void ixgbe_full_sync_mac_table(struct ixgbe_adapter *adapter)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+       int i;
+       for (i = 0; i < hw->mac.num_rar_entries; i++) {
+               if (adapter->mac_table[i].state & IXGBE_MAC_STATE_IN_USE)
+                       hw->mac.ops.set_rar(hw, i, adapter->mac_table[i].addr,
+                                           adapter->mac_table[i].queue,
+                                           IXGBE_RAH_AV);
+               else
+                       hw->mac.ops.clear_rar(hw, i);
+
+               adapter->mac_table[i].state &= ~(IXGBE_MAC_STATE_MODIFIED);
+       }
+}
+#endif
+
+static void ixgbe_sync_mac_table(struct ixgbe_adapter *adapter)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+       int i;
+       for (i = 0; i < hw->mac.num_rar_entries; i++) {
+               if (adapter->mac_table[i].state & IXGBE_MAC_STATE_MODIFIED) {
+                       if (adapter->mac_table[i].state &
+                           IXGBE_MAC_STATE_IN_USE)
+                               hw->mac.ops.set_rar(hw, i,
+                                               adapter->mac_table[i].addr,
+                                               adapter->mac_table[i].queue,
+                                               IXGBE_RAH_AV);
+                       else
+                               hw->mac.ops.clear_rar(hw, i);
+
+                       adapter->mac_table[i].state &=
+                                               ~(IXGBE_MAC_STATE_MODIFIED);
+               }
+       }
+}
+
+static void ixgbe_flush_sw_mac_table(struct ixgbe_adapter *adapter)
+{
+       int i;
+       struct ixgbe_hw *hw = &adapter->hw;
+
+       for (i = 0; i < hw->mac.num_rar_entries; i++) {
+               adapter->mac_table[i].state |= IXGBE_MAC_STATE_MODIFIED;
+               adapter->mac_table[i].state &= ~IXGBE_MAC_STATE_IN_USE;
+               memset(adapter->mac_table[i].addr, 0, ETH_ALEN);
+               adapter->mac_table[i].queue = 0;
+       }
+       ixgbe_sync_mac_table(adapter);
+}
+
+static int ixgbe_available_rars(struct ixgbe_adapter *adapter)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+       int i, count = 0;
+
+       for (i = 0; i < hw->mac.num_rar_entries; i++) {
+               if (adapter->mac_table[i].state == 0)
+                       count++;
+       }
+       return count;
+}
+
+/* this function destroys the first RAR entry */
+static void ixgbe_mac_set_default_filter(struct ixgbe_adapter *adapter,
+                                        u8 *addr)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+
+       memcpy(&adapter->mac_table[0].addr, addr, ETH_ALEN);
+       adapter->mac_table[0].queue = VMDQ_P(0);
+       adapter->mac_table[0].state = (IXGBE_MAC_STATE_DEFAULT |
+                                      IXGBE_MAC_STATE_IN_USE);
+       hw->mac.ops.set_rar(hw, 0, adapter->mac_table[0].addr,
+                           adapter->mac_table[0].queue,
+                           IXGBE_RAH_AV);
+}
+
+int ixgbe_add_mac_filter(struct ixgbe_adapter *adapter, u8 *addr, u16 queue)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+       int i;
+
+       if (is_zero_ether_addr(addr))
+               return -EINVAL;
+
+       for (i = 0; i < hw->mac.num_rar_entries; i++) {
+               if (adapter->mac_table[i].state & IXGBE_MAC_STATE_IN_USE)
+                       continue;
+               adapter->mac_table[i].state |= (IXGBE_MAC_STATE_MODIFIED |
+                                               IXGBE_MAC_STATE_IN_USE);
+               ether_addr_copy(adapter->mac_table[i].addr, addr);
+               adapter->mac_table[i].queue = queue;
+               ixgbe_sync_mac_table(adapter);
+               return i;
+       }
+       return -ENOMEM;
+}
+
+int ixgbe_del_mac_filter(struct ixgbe_adapter *adapter, u8 *addr, u16 queue)
+{
+       /* search table for addr, if found, set to 0 and sync */
+       int i;
+       struct ixgbe_hw *hw = &adapter->hw;
+
+       if (is_zero_ether_addr(addr))
+               return -EINVAL;
+
+       for (i = 0; i < hw->mac.num_rar_entries; i++) {
+               if (ether_addr_equal(addr, adapter->mac_table[i].addr) &&
+                   adapter->mac_table[i].queue == queue) {
+                       adapter->mac_table[i].state |= IXGBE_MAC_STATE_MODIFIED;
+                       adapter->mac_table[i].state &= ~IXGBE_MAC_STATE_IN_USE;
+                       memset(adapter->mac_table[i].addr, 0, ETH_ALEN);
+                       adapter->mac_table[i].queue = 0;
+                       ixgbe_sync_mac_table(adapter);
+                       return 0;
+               }
+       }
+       return -ENOMEM;
+}
 /**
  * ixgbe_write_uc_addr_list - write unicast addresses to RAR table
  * @netdev: network interface device structure
@@ -3858,39 +4007,23 @@ static void ixgbe_restore_vlan(struct ixgbe_adapter *adapter)
  *                0 on no addresses written
  *                X on writing X addresses to the RAR table
  **/
-static int ixgbe_write_uc_addr_list(struct net_device *netdev)
+static int ixgbe_write_uc_addr_list(struct net_device *netdev, int vfn)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
-       struct ixgbe_hw *hw = &adapter->hw;
-       unsigned int rar_entries = hw->mac.num_rar_entries - 1;
        int count = 0;
 
-       /* In SR-IOV/VMDQ modes significantly less RAR entries are available */
-       if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED)
-               rar_entries = IXGBE_MAX_PF_MACVLANS - 1;
-
        /* return ENOMEM indicating insufficient memory for addresses */
-       if (netdev_uc_count(netdev) > rar_entries)
+       if (netdev_uc_count(netdev) > ixgbe_available_rars(adapter))
                return -ENOMEM;
 
        if (!netdev_uc_empty(netdev)) {
                struct netdev_hw_addr *ha;
-               /* return error if we do not support writing to RAR table */
-               if (!hw->mac.ops.set_rar)
-                       return -ENOMEM;
-
                netdev_for_each_uc_addr(ha, netdev) {
-                       if (!rar_entries)
-                               break;
-                       hw->mac.ops.set_rar(hw, rar_entries--, ha->addr,
-                                           VMDQ_P(0), IXGBE_RAH_AV);
+                       ixgbe_del_mac_filter(adapter, ha->addr, vfn);
+                       ixgbe_add_mac_filter(adapter, ha->addr, vfn);
                        count++;
                }
        }
-       /* write the addresses in reverse order to avoid write combining */
-       for (; rar_entries > 0 ; rar_entries--)
-               hw->mac.ops.clear_rar(hw, rar_entries);
-
        return count;
 }
 
@@ -3908,11 +4041,12 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
        u32 fctrl, vmolr = IXGBE_VMOLR_BAM | IXGBE_VMOLR_AUPE;
+       u32 vlnctrl;
        int count;
 
        /* Check for Promiscuous and All Multicast modes */
-
        fctrl = IXGBE_READ_REG(hw, IXGBE_FCTRL);
+       vlnctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL);
 
        /* set all bits that we expect to always be set */
        fctrl &= ~IXGBE_FCTRL_SBP; /* disable store-bad-packets */
@@ -3922,26 +4056,24 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
 
        /* clear the bits we are changing the status of */
        fctrl &= ~(IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE);
-
+       vlnctrl &= ~(IXGBE_VLNCTRL_VFE | IXGBE_VLNCTRL_CFIEN);
        if (netdev->flags & IFF_PROMISC) {
                hw->addr_ctrl.user_set_promisc = true;
                fctrl |= (IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE);
-               vmolr |= (IXGBE_VMOLR_ROPE | IXGBE_VMOLR_MPE);
+               vmolr |= IXGBE_VMOLR_MPE;
                /* Only disable hardware filter vlans in promiscuous mode
                 * if SR-IOV and VMDQ are disabled - otherwise ensure
                 * that hardware VLAN filters remain enabled.
                 */
                if (!(adapter->flags & (IXGBE_FLAG_VMDQ_ENABLED |
                                        IXGBE_FLAG_SRIOV_ENABLED)))
-                       ixgbe_vlan_filter_disable(adapter);
-               else
-                       ixgbe_vlan_filter_enable(adapter);
+                       vlnctrl |= (IXGBE_VLNCTRL_VFE | IXGBE_VLNCTRL_CFIEN);
        } else {
                if (netdev->flags & IFF_ALLMULTI) {
                        fctrl |= IXGBE_FCTRL_MPE;
                        vmolr |= IXGBE_VMOLR_MPE;
                }
-               ixgbe_vlan_filter_enable(adapter);
+               vlnctrl |= IXGBE_VLNCTRL_VFE;
                hw->addr_ctrl.user_set_promisc = false;
        }
 
@@ -3950,7 +4082,7 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
         * sufficient space to store all the addresses then enable
         * unicast promiscuous mode
         */
-       count = ixgbe_write_uc_addr_list(netdev);
+       count = ixgbe_write_uc_addr_list(netdev, VMDQ_P(0));
        if (count < 0) {
                fctrl |= IXGBE_FCTRL_UPE;
                vmolr |= IXGBE_VMOLR_ROPE;
@@ -3960,11 +4092,13 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
         * then we should just turn on promiscuous mode so
         * that we can at least receive multicast traffic
         */
-       hw->mac.ops.update_mc_addr_list(hw, netdev);
-       vmolr |= IXGBE_VMOLR_ROMPE;
-
-       if (adapter->num_vfs)
-               ixgbe_restore_vf_multicasts(adapter);
+       count = ixgbe_write_mc_addr_list(netdev);
+       if (count < 0) {
+               fctrl |= IXGBE_FCTRL_MPE;
+               vmolr |= IXGBE_VMOLR_MPE;
+       } else if (count) {
+               vmolr |= IXGBE_VMOLR_ROMPE;
+       }
 
        if (hw->mac.type != ixgbe_mac_82598EB) {
                vmolr |= IXGBE_READ_REG(hw, IXGBE_VMOLR(VMDQ_P(0))) &
@@ -3985,6 +4119,7 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
                /* NOTE:  VLAN filtering is disabled by setting PROMISC */
        }
 
+       IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, vlnctrl);
        IXGBE_WRITE_REG(hw, IXGBE_FCTRL, fctrl);
 
        if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX)
@@ -4101,8 +4236,8 @@ static int ixgbe_hpbthresh(struct ixgbe_adapter *adapter, int pb)
            (tc < IXGBE_FCOE_JUMBO_FRAME_SIZE) &&
            (pb == ixgbe_fcoe_get_tc(adapter)))
                tc = IXGBE_FCOE_JUMBO_FRAME_SIZE;
-
 #endif
+
        /* Calculate delay value for device */
        switch (hw->mac.type) {
        case ixgbe_mac_X540:
@@ -4143,7 +4278,7 @@ static int ixgbe_hpbthresh(struct ixgbe_adapter *adapter, int pb)
  * @adapter: board private structure to calculate for
  * @pb: packet buffer to calculate
  */
-static int ixgbe_lpbthresh(struct ixgbe_adapter *adapter)
+static int ixgbe_lpbthresh(struct ixgbe_adapter *adapter, int pb)
 {
        struct ixgbe_hw *hw = &adapter->hw;
        struct net_device *dev = adapter->netdev;
@@ -4153,6 +4288,14 @@ static int ixgbe_lpbthresh(struct ixgbe_adapter *adapter)
        /* Calculate max LAN frame size */
        tc = dev->mtu + ETH_HLEN + ETH_FCS_LEN;
 
+#ifdef IXGBE_FCOE
+       /* FCoE traffic class uses FCOE jumbo frames */
+       if ((dev->features & NETIF_F_FCOE_MTU) &&
+           (tc < IXGBE_FCOE_JUMBO_FRAME_SIZE) &&
+           (pb == netdev_get_prio_tc_map(dev, adapter->fcoe.up)))
+               tc = IXGBE_FCOE_JUMBO_FRAME_SIZE;
+#endif
+
        /* Calculate delay value for device */
        switch (hw->mac.type) {
        case ixgbe_mac_X540:
@@ -4179,15 +4322,17 @@ static void ixgbe_pbthresh_setup(struct ixgbe_adapter *adapter)
        if (!num_tc)
                num_tc = 1;
 
-       hw->fc.low_water = ixgbe_lpbthresh(adapter);
-
        for (i = 0; i < num_tc; i++) {
                hw->fc.high_water[i] = ixgbe_hpbthresh(adapter, i);
+               hw->fc.low_water[i] = ixgbe_lpbthresh(adapter, i);
 
                /* Low water marks must not be larger than high water marks */
-               if (hw->fc.low_water > hw->fc.high_water[i])
-                       hw->fc.low_water = 0;
+               if (hw->fc.low_water[i] > hw->fc.high_water[i])
+                       hw->fc.low_water[i] = 0;
        }
+
+       for (; i < MAX_TRAFFIC_CLASS; i++)
+               hw->fc.high_water[i] = 0;
 }
 
 static void ixgbe_configure_pb(struct ixgbe_adapter *adapter)
@@ -4249,20 +4394,10 @@ static void ixgbe_macvlan_set_rx_mode(struct net_device *dev, unsigned int pool,
                vmolr |= IXGBE_VMOLR_ROMPE;
                hw->mac.ops.update_mc_addr_list(hw, dev);
        }
-       ixgbe_write_uc_addr_list(adapter->netdev);
+       ixgbe_write_uc_addr_list(adapter->netdev, pool);
        IXGBE_WRITE_REG(hw, IXGBE_VMOLR(pool), vmolr);
 }
 
-static void ixgbe_add_mac_filter(struct ixgbe_adapter *adapter,
-                                u8 *addr, u16 pool)
-{
-       struct ixgbe_hw *hw = &adapter->hw;
-       unsigned int entry;
-
-       entry = hw->mac.num_rar_entries - pool;
-       hw->mac.ops.set_rar(hw, entry, addr, VMDQ_P(pool), IXGBE_RAH_AV);
-}
-
 static void ixgbe_fwd_psrtype(struct ixgbe_fwd_adapter *vadapter)
 {
        struct ixgbe_adapter *adapter = vadapter->real_adapter;
@@ -4521,6 +4656,8 @@ static inline bool ixgbe_is_sfp(struct ixgbe_hw *hw)
        case ixgbe_phy_qsfp_active_unknown:
        case ixgbe_phy_qsfp_intel:
        case ixgbe_phy_qsfp_unknown:
+       /* ixgbe_phy_none is set when no SFP module is present */
+       case ixgbe_phy_none:
                return true;
        case ixgbe_phy_nl:
                if (hw->mac.type == ixgbe_mac_82598EB)
@@ -4742,7 +4879,9 @@ void ixgbe_up(struct ixgbe_adapter *adapter)
 void ixgbe_reset(struct ixgbe_adapter *adapter)
 {
        struct ixgbe_hw *hw = &adapter->hw;
+       struct net_device *netdev = adapter->netdev;
        int err;
+       u8 old_addr[ETH_ALEN];
 
        if (ixgbe_removed(hw->hw_addr))
                return;
@@ -4778,9 +4917,10 @@ void ixgbe_reset(struct ixgbe_adapter *adapter)
        }
 
        clear_bit(__IXGBE_IN_SFP_INIT, &adapter->state);
-
-       /* reprogram the RAR[0] in case user changed it. */
-       hw->mac.ops.set_rar(hw, 0, hw->mac.addr, VMDQ_P(0), IXGBE_RAH_AV);
+       /* do not flush user set addresses */
+       memcpy(old_addr, &adapter->mac_table[0].addr, netdev->addr_len);
+       ixgbe_flush_sw_mac_table(adapter);
+       ixgbe_mac_set_default_filter(adapter, old_addr);
 
        /* update SAN MAC vmdq pool selection */
        if (hw->mac.san_mac_rar_index)
@@ -5026,6 +5166,10 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter)
 #endif /* CONFIG_IXGBE_DCB */
 #endif /* IXGBE_FCOE */
 
+       adapter->mac_table = kzalloc(sizeof(struct ixgbe_mac_addr) *
+                                    hw->mac.num_rar_entries,
+                                    GFP_ATOMIC);
+
        /* Set MAC specific capability flags and exceptions */
        switch (hw->mac.type) {
        case ixgbe_mac_82598EB:
@@ -5517,6 +5661,17 @@ err_setup_tx:
        return err;
 }
 
+static void ixgbe_close_suspend(struct ixgbe_adapter *adapter)
+{
+       ixgbe_ptp_suspend(adapter);
+
+       ixgbe_down(adapter);
+       ixgbe_free_irq(adapter);
+
+       ixgbe_free_all_tx_resources(adapter);
+       ixgbe_free_all_rx_resources(adapter);
+}
+
 /**
  * ixgbe_close - Disables a network interface
  * @netdev: network interface device structure
@@ -5534,14 +5689,10 @@ static int ixgbe_close(struct net_device *netdev)
 
        ixgbe_ptp_stop(adapter);
 
-       ixgbe_down(adapter);
-       ixgbe_free_irq(adapter);
+       ixgbe_close_suspend(adapter);
 
        ixgbe_fdir_filter_exit(adapter);
 
-       ixgbe_free_all_tx_resources(adapter);
-       ixgbe_free_all_rx_resources(adapter);
-
        ixgbe_release_hw_control(adapter);
 
        return 0;
@@ -5608,12 +5759,8 @@ static int __ixgbe_shutdown(struct pci_dev *pdev, bool *enable_wake)
        netif_device_detach(netdev);
 
        rtnl_lock();
-       if (netif_running(netdev)) {
-               ixgbe_down(adapter);
-               ixgbe_free_irq(adapter);
-               ixgbe_free_all_tx_resources(adapter);
-               ixgbe_free_all_rx_resources(adapter);
-       }
+       if (netif_running(netdev))
+               ixgbe_close_suspend(adapter);
        rtnl_unlock();
 
        ixgbe_clear_interrupt_scheme(adapter);
@@ -5945,7 +6092,7 @@ static void ixgbe_fdir_reinit_subtask(struct ixgbe_adapter *adapter)
        if (ixgbe_reinit_fdir_tables_82599(hw) == 0) {
                for (i = 0; i < adapter->num_tx_queues; i++)
                        set_bit(__IXGBE_TX_FDIR_INIT_DONE,
-                               &(adapter->tx_ring[i]->state));
+                               &(adapter->tx_ring[i]->state));
                /* re-enable flow director interrupts */
                IXGBE_WRITE_REG(hw, IXGBE_EIMS, IXGBE_EIMS_FLOW_DIR);
        } else {
@@ -7172,16 +7319,17 @@ static int ixgbe_set_mac(struct net_device *netdev, void *p)
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
        struct sockaddr *addr = p;
+       int ret;
 
        if (!is_valid_ether_addr(addr->sa_data))
                return -EADDRNOTAVAIL;
 
+       ixgbe_del_mac_filter(adapter, hw->mac.addr, VMDQ_P(0));
        memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
        memcpy(hw->mac.addr, addr->sa_data, netdev->addr_len);
 
-       hw->mac.ops.set_rar(hw, 0, hw->mac.addr, VMDQ_P(0), IXGBE_RAH_AV);
-
-       return 0;
+       ret = ixgbe_add_mac_filter(adapter, hw->mac.addr, VMDQ_P(0));
+       return ret > 0 ? 0 : ret;
 }
 
 static int
@@ -7783,7 +7931,7 @@ static const struct net_device_ops ixgbe_netdev_ops = {
        .ndo_do_ioctl           = ixgbe_ioctl,
        .ndo_set_vf_mac         = ixgbe_ndo_set_vf_mac,
        .ndo_set_vf_vlan        = ixgbe_ndo_set_vf_vlan,
-       .ndo_set_vf_tx_rate     = ixgbe_ndo_set_vf_bw,
+       .ndo_set_vf_rate        = ixgbe_ndo_set_vf_bw,
        .ndo_set_vf_spoofchk    = ixgbe_ndo_set_vf_spoofchk,
        .ndo_get_vf_config      = ixgbe_ndo_get_vf_config,
        .ndo_get_stats64        = ixgbe_get_stats64,
@@ -8187,6 +8335,8 @@ skip_sriov:
                goto err_sw_init;
        }
 
+       ixgbe_mac_set_default_filter(adapter, hw->mac.perm_addr);
+
        setup_timer(&adapter->service_timer, &ixgbe_service_timer,
                    (unsigned long) adapter);
 
@@ -8242,7 +8392,7 @@ skip_sriov:
        if (ixgbe_is_sfp(hw) && hw->phy.sfp_type != ixgbe_sfp_type_not_present)
                e_dev_info("MAC: %d, PHY: %d, SFP+: %d, PBA No: %s\n",
                           hw->mac.type, hw->phy.type, hw->phy.sfp_type,
-                          part_str);
+                          part_str);
        else
                e_dev_info("MAC: %d, PHY: %d, PBA No: %s\n",
                           hw->mac.type, hw->phy.type, part_str);
@@ -8304,8 +8454,8 @@ skip_sriov:
 
        ixgbe_dbg_adapter_init(adapter);
 
-       /* Need link setup for MNG FW, else wait for IXGBE_UP */
-       if (ixgbe_mng_enabled(hw) && hw->mac.ops.setup_link)
+       /* setup link for SFP devices with MNG FW, else wait for IXGBE_UP */
+       if (ixgbe_mng_enabled(hw) && ixgbe_is_sfp(hw) && hw->mac.ops.setup_link)
                hw->mac.ops.setup_link(hw,
                        IXGBE_LINK_SPEED_10GB_FULL | IXGBE_LINK_SPEED_1GB_FULL,
                        true);
@@ -8319,6 +8469,7 @@ err_sw_init:
        ixgbe_disable_sriov(adapter);
        adapter->flags2 &= ~IXGBE_FLAG2_SEARCH_FOR_SFP;
        iounmap(adapter->io_addr);
+       kfree(adapter->mac_table);
 err_ioremap:
        free_netdev(netdev);
 err_alloc_etherdev:
@@ -8392,6 +8543,7 @@ static void ixgbe_remove(struct pci_dev *pdev)
 
        e_dev_info("complete\n");
 
+       kfree(adapter->mac_table);
        free_netdev(netdev);
 
        pci_disable_pcie_error_reporting(pdev);
index f5c6af2b891bd3dc797e7c4f5640b006eac78810..1918e0abf734a0d0e85f9f9df5567cc24e8cfc21 100644 (file)
@@ -223,7 +223,7 @@ out:
  *  received an ack to that message within delay * timeout period
  **/
 static s32 ixgbe_write_posted_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size,
-                           u16 mbx_id)
+                          u16 mbx_id)
 {
        struct ixgbe_mbx_info *mbx = &hw->mbx;
        s32 ret_val = IXGBE_ERR_MBX;
@@ -269,7 +269,7 @@ static s32 ixgbe_check_for_msg_pf(struct ixgbe_hw *hw, u16 vf_number)
        u32 vf_bit = vf_number % 16;
 
        if (!ixgbe_check_for_bit_pf(hw, IXGBE_MBVFICR_VFREQ_VF1 << vf_bit,
-                                   index)) {
+                                   index)) {
                ret_val = 0;
                hw->mbx.stats.reqs++;
        }
@@ -291,7 +291,7 @@ static s32 ixgbe_check_for_ack_pf(struct ixgbe_hw *hw, u16 vf_number)
        u32 vf_bit = vf_number % 16;
 
        if (!ixgbe_check_for_bit_pf(hw, IXGBE_MBVFICR_VFACK_VF1 << vf_bit,
-                                   index)) {
+                                   index)) {
                ret_val = 0;
                hw->mbx.stats.acks++;
        }
@@ -366,7 +366,7 @@ static s32 ixgbe_obtain_mbx_lock_pf(struct ixgbe_hw *hw, u16 vf_number)
  *  returns SUCCESS if it successfully copied message into the buffer
  **/
 static s32 ixgbe_write_mbx_pf(struct ixgbe_hw *hw, u32 *msg, u16 size,
-                              u16 vf_number)
+                             u16 vf_number)
 {
        s32 ret_val;
        u16 i;
@@ -407,7 +407,7 @@ out_no_write:
  *  a message due to a VF request so no polling for message is needed.
  **/
 static s32 ixgbe_read_mbx_pf(struct ixgbe_hw *hw, u32 *msg, u16 size,
-                             u16 vf_number)
+                            u16 vf_number)
 {
        s32 ret_val;
        u16 i;
index a9b9ad69ed0ec315d22d38c92eb1885c2900c9c3..a5cb755de3a994a34f68e0c9142414a8da52cc68 100644 (file)
  * Message ACK's are the value or'd with 0xF0000000
  */
 #define IXGBE_VT_MSGTYPE_ACK      0x80000000  /* Messages below or'd with
-                                               * this are the ACK */
+                                              * this are the ACK */
 #define IXGBE_VT_MSGTYPE_NACK     0x40000000  /* Messages below or'd with
-                                               * this are the NACK */
+                                              * this are the NACK */
 #define IXGBE_VT_MSGTYPE_CTS      0x20000000  /* Indicates that VF is still
-                                                 clear to send requests */
+                                                clear to send requests */
 #define IXGBE_VT_MSGINFO_SHIFT    16
 /* bits 23:16 are used for exra info for certain messages */
 #define IXGBE_VT_MSGINFO_MASK     (0xFF << IXGBE_VT_MSGINFO_SHIFT)
index a76af8e28a04be16386c444cc947581638f3befa..ff68b7a9deff15af054ec66238557fa47d3c1480 100644 (file)
@@ -67,7 +67,7 @@ s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw)
                        if (mdio45_probe(&hw->phy.mdio, phy_addr) == 0) {
                                ixgbe_get_phy_id(hw);
                                hw->phy.type =
-                                       ixgbe_get_phy_type_from_id(hw->phy.id);
+                                       ixgbe_get_phy_type_from_id(hw->phy.id);
 
                                if (hw->phy.type == ixgbe_phy_unknown) {
                                        hw->phy.ops.read_reg(hw,
@@ -136,12 +136,12 @@ static s32 ixgbe_get_phy_id(struct ixgbe_hw *hw)
        u16 phy_id_low = 0;
 
        status = hw->phy.ops.read_reg(hw, MDIO_DEVID1, MDIO_MMD_PMAPMD,
-                                     &phy_id_high);
+                                     &phy_id_high);
 
        if (status == 0) {
                hw->phy.id = (u32)(phy_id_high << 16);
                status = hw->phy.ops.read_reg(hw, MDIO_DEVID2, MDIO_MMD_PMAPMD,
-                                             &phy_id_low);
+                                             &phy_id_low);
                hw->phy.id |= (u32)(phy_id_low & IXGBE_PHY_REVISION_MASK);
                hw->phy.revision = (u32)(phy_id_low & ~IXGBE_PHY_REVISION_MASK);
        }
@@ -318,7 +318,7 @@ s32 ixgbe_read_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type,
  *  @phy_data: Pointer to read data from PHY register
  **/
 s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
-                               u32 device_type, u16 *phy_data)
+                              u32 device_type, u16 *phy_data)
 {
        s32 status;
        u16 gssr;
@@ -421,7 +421,7 @@ s32 ixgbe_write_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr,
  *  @phy_data: Data to write to the PHY register
  **/
 s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
-                                u32 device_type, u16 phy_data)
+                               u32 device_type, u16 phy_data)
 {
        s32 status;
        u16 gssr;
@@ -548,8 +548,8 @@ s32 ixgbe_setup_phy_link_generic(struct ixgbe_hw *hw)
  *  @speed: new link speed
  **/
 s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw,
-                                       ixgbe_link_speed speed,
-                                       bool autoneg_wait_to_complete)
+                                      ixgbe_link_speed speed,
+                                      bool autoneg_wait_to_complete)
 {
 
        /*
@@ -582,8 +582,8 @@ s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw,
  * Determines the link capabilities by reading the AUTOC register.
  */
 s32 ixgbe_get_copper_link_capabilities_generic(struct ixgbe_hw *hw,
-                                               ixgbe_link_speed *speed,
-                                               bool *autoneg)
+                                              ixgbe_link_speed *speed,
+                                              bool *autoneg)
 {
        s32 status = IXGBE_ERR_LINK_SETUP;
        u16 speed_ability;
@@ -592,7 +592,7 @@ s32 ixgbe_get_copper_link_capabilities_generic(struct ixgbe_hw *hw,
        *autoneg = true;
 
        status = hw->phy.ops.read_reg(hw, MDIO_SPEED, MDIO_MMD_PMAPMD,
-                                     &speed_ability);
+                                     &speed_ability);
 
        if (status == 0) {
                if (speed_ability & MDIO_SPEED_10G)
@@ -806,11 +806,11 @@ s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw)
 
        /* reset the PHY and poll for completion */
        hw->phy.ops.write_reg(hw, MDIO_CTRL1, MDIO_MMD_PHYXS,
-                             (phy_data | MDIO_CTRL1_RESET));
+                             (phy_data | MDIO_CTRL1_RESET));
 
        for (i = 0; i < 100; i++) {
                hw->phy.ops.read_reg(hw, MDIO_CTRL1, MDIO_MMD_PHYXS,
-                                    &phy_data);
+                                    &phy_data);
                if ((phy_data & MDIO_CTRL1_RESET) == 0)
                        break;
                usleep_range(10000, 20000);
@@ -824,7 +824,7 @@ s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw)
 
        /* Get init offsets */
        ret_val = ixgbe_get_sfp_init_sequence_offsets(hw, &list_offset,
-                                                     &data_offset);
+                                                     &data_offset);
        if (ret_val != 0)
                goto out;
 
@@ -838,7 +838,7 @@ s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw)
                if (ret_val)
                        goto err_eeprom;
                control = (eword & IXGBE_CONTROL_MASK_NL) >>
-                          IXGBE_CONTROL_SHIFT_NL;
+                          IXGBE_CONTROL_SHIFT_NL;
                edata = eword & IXGBE_DATA_MASK_NL;
                switch (control) {
                case IXGBE_DELAY_NL:
@@ -859,7 +859,7 @@ s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw)
                                if (ret_val)
                                        goto err_eeprom;
                                hw->phy.ops.write_reg(hw, phy_offset,
-                                                     MDIO_MMD_PMAPMD, eword);
+                                                     MDIO_MMD_PMAPMD, eword);
                                hw_dbg(hw, "Wrote %4.4x to %4.4x\n", eword,
                                       phy_offset);
                                data_offset++;
@@ -1010,10 +1010,10 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw)
                        if (cable_tech & IXGBE_SFF_DA_PASSIVE_CABLE) {
                                if (hw->bus.lan_id == 0)
                                        hw->phy.sfp_type =
-                                                    ixgbe_sfp_type_da_cu_core0;
+                                                    ixgbe_sfp_type_da_cu_core0;
                                else
                                        hw->phy.sfp_type =
-                                                    ixgbe_sfp_type_da_cu_core1;
+                                                    ixgbe_sfp_type_da_cu_core1;
                        } else if (cable_tech & IXGBE_SFF_DA_ACTIVE_CABLE) {
                                hw->phy.ops.read_i2c_eeprom(
                                                hw, IXGBE_SFF_CABLE_SPEC_COMP,
@@ -1035,10 +1035,10 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw)
                                    IXGBE_SFF_10GBASELR_CAPABLE)) {
                                if (hw->bus.lan_id == 0)
                                        hw->phy.sfp_type =
-                                                     ixgbe_sfp_type_srlr_core0;
+                                                     ixgbe_sfp_type_srlr_core0;
                                else
                                        hw->phy.sfp_type =
-                                                     ixgbe_sfp_type_srlr_core1;
+                                                     ixgbe_sfp_type_srlr_core1;
                        } else if (comp_codes_1g & IXGBE_SFF_1GBASET_CAPABLE) {
                                if (hw->bus.lan_id == 0)
                                        hw->phy.sfp_type =
@@ -1087,15 +1087,15 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw)
                                goto err_read_i2c_eeprom;
 
                        status = hw->phy.ops.read_i2c_eeprom(hw,
-                                                   IXGBE_SFF_VENDOR_OUI_BYTE1,
-                                                   &oui_bytes[1]);
+                                                   IXGBE_SFF_VENDOR_OUI_BYTE1,
+                                                   &oui_bytes[1]);
 
                        if (status != 0)
                                goto err_read_i2c_eeprom;
 
                        status = hw->phy.ops.read_i2c_eeprom(hw,
-                                                   IXGBE_SFF_VENDOR_OUI_BYTE2,
-                                                   &oui_bytes[2]);
+                                                   IXGBE_SFF_VENDOR_OUI_BYTE2,
+                                                   &oui_bytes[2]);
 
                        if (status != 0)
                                goto err_read_i2c_eeprom;
@@ -1403,8 +1403,8 @@ err_read_i2c_eeprom:
  *  so it returns the offsets to the phy init sequence block.
  **/
 s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw,
-                                        u16 *list_offset,
-                                        u16 *data_offset)
+                                       u16 *list_offset,
+                                       u16 *data_offset)
 {
        u16 sfp_id;
        u16 sfp_type = hw->phy.sfp_type;
@@ -1493,11 +1493,11 @@ err_phy:
  *  Performs byte read operation to SFP module's EEPROM over I2C interface.
  **/
 s32 ixgbe_read_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset,
-                                  u8 *eeprom_data)
+                                 u8 *eeprom_data)
 {
        return hw->phy.ops.read_i2c_byte(hw, byte_offset,
-                                        IXGBE_I2C_EEPROM_DEV_ADDR,
-                                        eeprom_data);
+                                        IXGBE_I2C_EEPROM_DEV_ADDR,
+                                        eeprom_data);
 }
 
 /**
@@ -1525,11 +1525,11 @@ s32 ixgbe_read_i2c_sff8472_generic(struct ixgbe_hw *hw, u8 byte_offset,
  *  Performs byte write operation to SFP module's EEPROM over I2C interface.
  **/
 s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset,
-                                   u8 eeprom_data)
+                                  u8 eeprom_data)
 {
        return hw->phy.ops.write_i2c_byte(hw, byte_offset,
-                                         IXGBE_I2C_EEPROM_DEV_ADDR,
-                                         eeprom_data);
+                                         IXGBE_I2C_EEPROM_DEV_ADDR,
+                                         eeprom_data);
 }
 
 /**
@@ -1542,7 +1542,7 @@ s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset,
  *  a specified device address.
  **/
 s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
-                                u8 dev_addr, u8 *data)
+                               u8 dev_addr, u8 *data)
 {
        s32 status = 0;
        u32 max_retry = 10;
@@ -1631,7 +1631,7 @@ read_byte_out:
  *  a specified device address.
  **/
 s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
-                                 u8 dev_addr, u8 data)
+                                u8 dev_addr, u8 data)
 {
        s32 status = 0;
        u32 max_retry = 1;
@@ -2046,7 +2046,7 @@ s32 ixgbe_tn_check_overtemp(struct ixgbe_hw *hw)
 
        /* Check that the LASI temp alarm status was triggered */
        hw->phy.ops.read_reg(hw, IXGBE_TN_LASI_STATUS_REG,
-                            MDIO_MMD_PMAPMD, &phy_data);
+                            MDIO_MMD_PMAPMD, &phy_data);
 
        if (!(phy_data & IXGBE_TN_LASI_STATUS_TEMP_ALARM))
                goto out;
index 0bb047f751c2a4ac6049f4500f65f035651ba830..54071ed17e3b3d589d8250f4536f622e796049dc 100644 (file)
@@ -114,47 +114,47 @@ s32 ixgbe_init_phy_ops_generic(struct ixgbe_hw *hw);
 s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw);
 s32 ixgbe_reset_phy_generic(struct ixgbe_hw *hw);
 s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
-                               u32 device_type, u16 *phy_data);
+                              u32 device_type, u16 *phy_data);
 s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
-                                u32 device_type, u16 phy_data);
+                               u32 device_type, u16 phy_data);
 s32 ixgbe_read_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr,
                           u32 device_type, u16 *phy_data);
 s32 ixgbe_write_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr,
                            u32 device_type, u16 phy_data);
 s32 ixgbe_setup_phy_link_generic(struct ixgbe_hw *hw);
 s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw,
-                                       ixgbe_link_speed speed,
-                                       bool autoneg_wait_to_complete);
+                                      ixgbe_link_speed speed,
+                                      bool autoneg_wait_to_complete);
 s32 ixgbe_get_copper_link_capabilities_generic(struct ixgbe_hw *hw,
-                                               ixgbe_link_speed *speed,
-                                               bool *autoneg);
+                                              ixgbe_link_speed *speed,
+                                              bool *autoneg);
 bool ixgbe_check_reset_blocked(struct ixgbe_hw *hw);
 
 /* PHY specific */
 s32 ixgbe_check_phy_link_tnx(struct ixgbe_hw *hw,
-                             ixgbe_link_speed *speed,
-                             bool *link_up);
+                            ixgbe_link_speed *speed,
+                            bool *link_up);
 s32 ixgbe_setup_phy_link_tnx(struct ixgbe_hw *hw);
 s32 ixgbe_get_phy_firmware_version_tnx(struct ixgbe_hw *hw,
-                                       u16 *firmware_version);
+                                      u16 *firmware_version);
 s32 ixgbe_get_phy_firmware_version_generic(struct ixgbe_hw *hw,
-                                           u16 *firmware_version);
+                                          u16 *firmware_version);
 
 s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw);
 s32 ixgbe_identify_module_generic(struct ixgbe_hw *hw);
 s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw);
 s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw,
-                                        u16 *list_offset,
-                                        u16 *data_offset);
+                                       u16 *list_offset,
+                                       u16 *data_offset);
 s32 ixgbe_tn_check_overtemp(struct ixgbe_hw *hw);
 s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
-                                u8 dev_addr, u8 *data);
+                               u8 dev_addr, u8 *data);
 s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
-                                 u8 dev_addr, u8 data);
+                                u8 dev_addr, u8 data);
 s32 ixgbe_read_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset,
-                                  u8 *eeprom_data);
+                                 u8 *eeprom_data);
 s32 ixgbe_read_i2c_sff8472_generic(struct ixgbe_hw *hw, u8 byte_offset,
                                   u8 *sff8472_data);
 s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset,
-                                   u8 eeprom_data);
+                                  u8 eeprom_data);
 #endif /* _IXGBE_PHY_H_ */
index 8902ae68345770ce5f28ba2ab91ee73bab3c7ff3..68f87ecb8a762da7e950a61ae2c9058bb61f5e8e 100644 (file)
@@ -26,7 +26,6 @@
 
 *******************************************************************************/
 #include "ixgbe.h"
-#include <linux/export.h>
 #include <linux/ptp_classify.h>
 
 /*
@@ -334,7 +333,7 @@ static int ixgbe_ptp_settime(struct ptp_clock_info *ptp,
 }
 
 /**
- * ixgbe_ptp_enable
+ * ixgbe_ptp_feature_enable
  * @ptp: the ptp clock structure
  * @rq: the requested feature to change
  * @on: whether to enable or disable the feature
@@ -342,8 +341,8 @@ static int ixgbe_ptp_settime(struct ptp_clock_info *ptp,
  * enable (or disable) ancillary features of the phc subsystem.
  * our driver only supports the PPS feature on the X540
  */
-static int ixgbe_ptp_enable(struct ptp_clock_info *ptp,
-                           struct ptp_clock_request *rq, int on)
+static int ixgbe_ptp_feature_enable(struct ptp_clock_info *ptp,
+                                   struct ptp_clock_request *rq, int on)
 {
        struct ixgbe_adapter *adapter =
                container_of(ptp, struct ixgbe_adapter, ptp_caps);
@@ -570,9 +569,9 @@ int ixgbe_ptp_get_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
 }
 
 /**
- * ixgbe_ptp_set_ts_config - control hardware time stamping
- * @adapter: pointer to adapter struct
- * @ifreq: ioctl data
+ * ixgbe_ptp_set_timestamp_mode - setup the hardware for the requested mode
+ * @adapter: the private ixgbe adapter structure
+ * @config: the hwtstamp configuration requested
  *
  * Outgoing time stamping can be enabled and disabled. Play nice and
  * disable it when requested, although it shouldn't cause any overhead
@@ -590,25 +589,25 @@ int ixgbe_ptp_get_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
  * packets, regardless of the type specified in the register, only use V2
  * Event mode. This more accurately tells the user what the hardware is going
  * to do anyways.
+ *
+ * Note: this may modify the hwtstamp configuration towards a more general
+ * mode, if required to support the specifically requested mode.
  */
-int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
+static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter,
+                                struct hwtstamp_config *config)
 {
        struct ixgbe_hw *hw = &adapter->hw;
-       struct hwtstamp_config config;
        u32 tsync_tx_ctl = IXGBE_TSYNCTXCTL_ENABLED;
        u32 tsync_rx_ctl = IXGBE_TSYNCRXCTL_ENABLED;
        u32 tsync_rx_mtrl = PTP_EV_PORT << 16;
        bool is_l2 = false;
        u32 regval;
 
-       if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
-               return -EFAULT;
-
        /* reserved for future extensions */
-       if (config.flags)
+       if (config->flags)
                return -EINVAL;
 
-       switch (config.tx_type) {
+       switch (config->tx_type) {
        case HWTSTAMP_TX_OFF:
                tsync_tx_ctl = 0;
        case HWTSTAMP_TX_ON:
@@ -617,7 +616,7 @@ int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
                return -ERANGE;
        }
 
-       switch (config.rx_filter) {
+       switch (config->rx_filter) {
        case HWTSTAMP_FILTER_NONE:
                tsync_rx_ctl = 0;
                tsync_rx_mtrl = 0;
@@ -641,7 +640,7 @@ int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
        case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
                tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_EVENT_V2;
                is_l2 = true;
-               config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+               config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
                break;
        case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
        case HWTSTAMP_FILTER_ALL:
@@ -652,7 +651,7 @@ int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
                 * Delay_Req messages and hardware does not support
                 * timestamping all packets => return error
                 */
-               config.rx_filter = HWTSTAMP_FILTER_NONE;
+               config->rx_filter = HWTSTAMP_FILTER_NONE;
                return -ERANGE;
        }
 
@@ -671,7 +670,6 @@ int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
        else
                IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_1588), 0);
 
-
        /* enable/disable TX */
        regval = IXGBE_READ_REG(hw, IXGBE_TSYNCTXCTL);
        regval &= ~IXGBE_TSYNCTXCTL_ENABLED;
@@ -693,6 +691,29 @@ int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
        regval = IXGBE_READ_REG(hw, IXGBE_TXSTMPH);
        regval = IXGBE_READ_REG(hw, IXGBE_RXSTMPH);
 
+       return 0;
+}
+
+/**
+ * ixgbe_ptp_set_ts_config - user entry point for timestamp mode
+ * @adapter: pointer to adapter struct
+ * @ifreq: ioctl data
+ *
+ * Set hardware to requested mode. If unsupported, return an error with no
+ * changes. Otherwise, store the mode for future reference.
+ */
+int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
+{
+       struct hwtstamp_config config;
+       int err;
+
+       if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
+               return -EFAULT;
+
+       err = ixgbe_ptp_set_timestamp_mode(adapter, &config);
+       if (err)
+               return err;
+
        /* save these settings for future reference */
        memcpy(&adapter->tstamp_config, &config,
               sizeof(adapter->tstamp_config));
@@ -790,9 +811,13 @@ void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter)
  * ixgbe_ptp_reset
  * @adapter: the ixgbe private board structure
  *
- * When the MAC resets, all timesync features are reset. This function should be
- * called to re-enable the PTP clock structure. It will re-init the timecounter
- * structure based on the kernel time as well as setup the cycle counter data.
+ * When the MAC resets, all the hardware bits for timesync are reset. This
+ * function is used to re-enable the device for PTP based on current settings.
+ * We do lose the current clock time, so just reset the cyclecounter to the
+ * system real clock time.
+ *
+ * This function will maintain hwtstamp_config settings, and resets the SDP
+ * output if it was enabled.
  */
 void ixgbe_ptp_reset(struct ixgbe_adapter *adapter)
 {
@@ -804,8 +829,8 @@ void ixgbe_ptp_reset(struct ixgbe_adapter *adapter)
        IXGBE_WRITE_REG(hw, IXGBE_SYSTIMH, 0x00000000);
        IXGBE_WRITE_FLUSH(hw);
 
-       /* Reset the saved tstamp_config */
-       memset(&adapter->tstamp_config, 0, sizeof(adapter->tstamp_config));
+       /* reset the hardware timestamping mode */
+       ixgbe_ptp_set_timestamp_mode(adapter, &adapter->tstamp_config);
 
        ixgbe_ptp_start_cyclecounter(adapter);
 
@@ -825,16 +850,23 @@ void ixgbe_ptp_reset(struct ixgbe_adapter *adapter)
 }
 
 /**
- * ixgbe_ptp_init
+ * ixgbe_ptp_create_clock
  * @adapter: the ixgbe private adapter structure
  *
- * This function performs the required steps for enabling ptp
- * support. If ptp support has already been loaded it simply calls the
- * cyclecounter init routine and exits.
+ * This function performs setup of the user entry point function table and
+ * initializes the PTP clock device, which is used to access the clock-like
+ * features of the PTP core. It will be called by ixgbe_ptp_init, only if
+ * there isn't already a clock device (such as after a suspend/resume cycle,
+ * where the clock device wasn't destroyed).
  */
-void ixgbe_ptp_init(struct ixgbe_adapter *adapter)
+static int ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter)
 {
        struct net_device *netdev = adapter->netdev;
+       long err;
+
+       /* do nothing if we already have a clock device */
+       if (!IS_ERR_OR_NULL(adapter->ptp_clock))
+               return 0;
 
        switch (adapter->hw.mac.type) {
        case ixgbe_mac_X540:
@@ -851,7 +883,7 @@ void ixgbe_ptp_init(struct ixgbe_adapter *adapter)
                adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime;
                adapter->ptp_caps.gettime = ixgbe_ptp_gettime;
                adapter->ptp_caps.settime = ixgbe_ptp_settime;
-               adapter->ptp_caps.enable = ixgbe_ptp_enable;
+               adapter->ptp_caps.enable = ixgbe_ptp_feature_enable;
                break;
        case ixgbe_mac_82599EB:
                snprintf(adapter->ptp_caps.name,
@@ -867,24 +899,57 @@ void ixgbe_ptp_init(struct ixgbe_adapter *adapter)
                adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime;
                adapter->ptp_caps.gettime = ixgbe_ptp_gettime;
                adapter->ptp_caps.settime = ixgbe_ptp_settime;
-               adapter->ptp_caps.enable = ixgbe_ptp_enable;
+               adapter->ptp_caps.enable = ixgbe_ptp_feature_enable;
                break;
        default:
                adapter->ptp_clock = NULL;
-               return;
+               return -EOPNOTSUPP;
        }
 
-       spin_lock_init(&adapter->tmreg_lock);
-       INIT_WORK(&adapter->ptp_tx_work, ixgbe_ptp_tx_hwtstamp_work);
-
        adapter->ptp_clock = ptp_clock_register(&adapter->ptp_caps,
                                                &adapter->pdev->dev);
        if (IS_ERR(adapter->ptp_clock)) {
+               err = PTR_ERR(adapter->ptp_clock);
                adapter->ptp_clock = NULL;
                e_dev_err("ptp_clock_register failed\n");
+               return err;
        } else
                e_dev_info("registered PHC device on %s\n", netdev->name);
 
+       /* set default timestamp mode to disabled here. We do this in
+        * create_clock instead of init, because we don't want to override the
+        * previous settings during a resume cycle.
+        */
+       adapter->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
+       adapter->tstamp_config.tx_type = HWTSTAMP_TX_OFF;
+
+       return 0;
+}
+
+/**
+ * ixgbe_ptp_init
+ * @adapter: the ixgbe private adapter structure
+ *
+ * This function performs the required steps for enabling PTP
+ * support. If PTP support has already been loaded it simply calls the
+ * cyclecounter init routine and exits.
+ */
+void ixgbe_ptp_init(struct ixgbe_adapter *adapter)
+{
+       /* initialize the spin lock first since we can't control when a user
+        * will call the entry functions once we have initialized the clock
+        * device
+        */
+       spin_lock_init(&adapter->tmreg_lock);
+
+       /* obtain a PTP device, or re-use an existing device */
+       if (ixgbe_ptp_create_clock(adapter))
+               return;
+
+       /* we have a clock so we can initialize work now */
+       INIT_WORK(&adapter->ptp_tx_work, ixgbe_ptp_tx_hwtstamp_work);
+
+       /* reset the PTP related hardware bits */
        ixgbe_ptp_reset(adapter);
 
        /* enter the IXGBE_PTP_RUNNING state */
@@ -894,28 +959,45 @@ void ixgbe_ptp_init(struct ixgbe_adapter *adapter)
 }
 
 /**
- * ixgbe_ptp_stop - disable ptp device and stop the overflow check
- * @adapter: pointer to adapter struct
+ * ixgbe_ptp_suspend - stop PTP work items
+ * @ adapter: pointer to adapter struct
  *
- * this function stops the ptp support, and cancels the delayed work.
+ * this function suspends PTP activity, and prevents more PTP work from being
+ * generated, but does not destroy the PTP clock device.
  */
-void ixgbe_ptp_stop(struct ixgbe_adapter *adapter)
+void ixgbe_ptp_suspend(struct ixgbe_adapter *adapter)
 {
        /* Leave the IXGBE_PTP_RUNNING state. */
        if (!test_and_clear_bit(__IXGBE_PTP_RUNNING, &adapter->state))
                return;
 
-       /* stop the PPS signal */
-       adapter->flags2 &= ~IXGBE_FLAG2_PTP_PPS_ENABLED;
-       ixgbe_ptp_setup_sdp(adapter);
+       /* since this might be called in suspend, we don't clear the state,
+        * but simply reset the auxiliary PPS signal control register
+        */
+       IXGBE_WRITE_REG(&adapter->hw, IXGBE_TSAUXC, 0x0);
 
+       /* ensure that we cancel any pending PTP Tx work item in progress */
        cancel_work_sync(&adapter->ptp_tx_work);
        if (adapter->ptp_tx_skb) {
                dev_kfree_skb_any(adapter->ptp_tx_skb);
                adapter->ptp_tx_skb = NULL;
                clear_bit_unlock(__IXGBE_PTP_TX_IN_PROGRESS, &adapter->state);
        }
+}
+
+/**
+ * ixgbe_ptp_stop - close the PTP device
+ * @adapter: pointer to adapter struct
+ *
+ * completely destroy the PTP device, should only be called when the device is
+ * being fully closed.
+ */
+void ixgbe_ptp_stop(struct ixgbe_adapter *adapter)
+{
+       /* first, suspend PTP activity */
+       ixgbe_ptp_suspend(adapter);
 
+       /* disable the PTP clock device */
        if (adapter->ptp_clock) {
                ptp_clock_unregister(adapter->ptp_clock);
                adapter->ptp_clock = NULL;
index e6c68d396c992fffb329a1cca4daadb47169faab..16b3a1cd9db6c0c32bef5223b479cf9f4b4a4d41 100644 (file)
@@ -72,8 +72,6 @@ static int __ixgbe_enable_sriov(struct ixgbe_adapter *adapter)
                for (i = 0; i < num_vf_macvlans; i++) {
                        mv_list->vf = -1;
                        mv_list->free = true;
-                       mv_list->rar_entry = hw->mac.num_rar_entries -
-                               (i + adapter->num_vfs + 1);
                        list_add(&mv_list->l, &adapter->vf_mvs.l);
                        mv_list++;
                }
@@ -327,6 +325,7 @@ static int ixgbe_set_vf_multicasts(struct ixgbe_adapter *adapter,
        u32 vector_bit;
        u32 vector_reg;
        u32 mta_reg;
+       u32 vmolr = IXGBE_READ_REG(hw, IXGBE_VMOLR(vf));
 
        /* only so many hash values supported */
        entries = min(entries, IXGBE_MAX_VF_MC_ENTRIES);
@@ -353,25 +352,13 @@ static int ixgbe_set_vf_multicasts(struct ixgbe_adapter *adapter,
                mta_reg |= (1 << vector_bit);
                IXGBE_WRITE_REG(hw, IXGBE_MTA(vector_reg), mta_reg);
        }
+       vmolr |= IXGBE_VMOLR_ROMPE;
+       IXGBE_WRITE_REG(hw, IXGBE_VMOLR(vf), vmolr);
 
        return 0;
 }
 
-static void ixgbe_restore_vf_macvlans(struct ixgbe_adapter *adapter)
-{
-       struct ixgbe_hw *hw = &adapter->hw;
-       struct list_head *pos;
-       struct vf_macvlans *entry;
-
-       list_for_each(pos, &adapter->vf_mvs.l) {
-               entry = list_entry(pos, struct vf_macvlans, l);
-               if (!entry->free)
-                       hw->mac.ops.set_rar(hw, entry->rar_entry,
-                                           entry->vf_macvlan,
-                                           entry->vf, IXGBE_RAH_AV);
-       }
-}
-
+#ifdef CONFIG_PCI_IOV
 void ixgbe_restore_vf_multicasts(struct ixgbe_adapter *adapter)
 {
        struct ixgbe_hw *hw = &adapter->hw;
@@ -382,6 +369,7 @@ void ixgbe_restore_vf_multicasts(struct ixgbe_adapter *adapter)
        u32 mta_reg;
 
        for (i = 0; i < adapter->num_vfs; i++) {
+               u32 vmolr = IXGBE_READ_REG(hw, IXGBE_VMOLR(i));
                vfinfo = &adapter->vfinfo[i];
                for (j = 0; j < vfinfo->num_vf_mc_hashes; j++) {
                        hw->addr_ctrl.mta_in_use++;
@@ -391,11 +379,18 @@ void ixgbe_restore_vf_multicasts(struct ixgbe_adapter *adapter)
                        mta_reg |= (1 << vector_bit);
                        IXGBE_WRITE_REG(hw, IXGBE_MTA(vector_reg), mta_reg);
                }
+
+               if (vfinfo->num_vf_mc_hashes)
+                       vmolr |= IXGBE_VMOLR_ROMPE;
+               else
+                       vmolr &= ~IXGBE_VMOLR_ROMPE;
+               IXGBE_WRITE_REG(hw, IXGBE_VMOLR(i), vmolr);
        }
 
        /* Restore any VF macvlans */
-       ixgbe_restore_vf_macvlans(adapter);
+       ixgbe_full_sync_mac_table(adapter);
 }
+#endif
 
 static int ixgbe_set_vf_vlan(struct ixgbe_adapter *adapter, int add, int vid,
                             u32 vf)
@@ -495,8 +490,7 @@ static s32 ixgbe_set_vf_lpe(struct ixgbe_adapter *adapter, u32 *msgbuf, u32 vf)
 static void ixgbe_set_vmolr(struct ixgbe_hw *hw, u32 vf, bool aupe)
 {
        u32 vmolr = IXGBE_READ_REG(hw, IXGBE_VMOLR(vf));
-       vmolr |= (IXGBE_VMOLR_ROMPE |
-                 IXGBE_VMOLR_BAM);
+       vmolr |= IXGBE_VMOLR_BAM;
        if (aupe)
                vmolr |= IXGBE_VMOLR_AUPE;
        else
@@ -514,7 +508,6 @@ static inline void ixgbe_vf_reset_event(struct ixgbe_adapter *adapter, u32 vf)
 {
        struct ixgbe_hw *hw = &adapter->hw;
        struct vf_data_storage *vfinfo = &adapter->vfinfo[vf];
-       int rar_entry = hw->mac.num_rar_entries - (vf + 1);
        u8 num_tcs = netdev_get_num_tc(adapter->netdev);
 
        /* add PF assigned VLAN or VLAN 0 */
@@ -544,7 +537,7 @@ static inline void ixgbe_vf_reset_event(struct ixgbe_adapter *adapter, u32 vf)
        /* Flush and reset the mta with the new values */
        ixgbe_set_rx_mode(adapter->netdev);
 
-       hw->mac.ops.clear_rar(hw, rar_entry);
+       ixgbe_del_mac_filter(adapter, adapter->vfinfo[vf].vf_mac_addresses, vf);
 
        /* reset VF api back to unknown */
        adapter->vfinfo[vf].vf_api = ixgbe_mbox_api_10;
@@ -553,11 +546,9 @@ static inline void ixgbe_vf_reset_event(struct ixgbe_adapter *adapter, u32 vf)
 static int ixgbe_set_vf_mac(struct ixgbe_adapter *adapter,
                            int vf, unsigned char *mac_addr)
 {
-       struct ixgbe_hw *hw = &adapter->hw;
-       int rar_entry = hw->mac.num_rar_entries - (vf + 1);
-
+       ixgbe_del_mac_filter(adapter, adapter->vfinfo[vf].vf_mac_addresses, vf);
        memcpy(adapter->vfinfo[vf].vf_mac_addresses, mac_addr, ETH_ALEN);
-       hw->mac.ops.set_rar(hw, rar_entry, mac_addr, vf, IXGBE_RAH_AV);
+       ixgbe_add_mac_filter(adapter, adapter->vfinfo[vf].vf_mac_addresses, vf);
 
        return 0;
 }
@@ -565,7 +556,6 @@ static int ixgbe_set_vf_mac(struct ixgbe_adapter *adapter,
 static int ixgbe_set_vf_macvlan(struct ixgbe_adapter *adapter,
                                int vf, int index, unsigned char *mac_addr)
 {
-       struct ixgbe_hw *hw = &adapter->hw;
        struct list_head *pos;
        struct vf_macvlans *entry;
 
@@ -576,7 +566,8 @@ static int ixgbe_set_vf_macvlan(struct ixgbe_adapter *adapter,
                                entry->vf = -1;
                                entry->free = true;
                                entry->is_macvlan = false;
-                               hw->mac.ops.clear_rar(hw, entry->rar_entry);
+                               ixgbe_del_mac_filter(adapter,
+                                                    entry->vf_macvlan, vf);
                        }
                }
        }
@@ -612,7 +603,7 @@ static int ixgbe_set_vf_macvlan(struct ixgbe_adapter *adapter,
        entry->vf = vf;
        memcpy(entry->vf_macvlan, mac_addr, ETH_ALEN);
 
-       hw->mac.ops.set_rar(hw, entry->rar_entry, mac_addr, vf, IXGBE_RAH_AV);
+       ixgbe_add_mac_filter(adapter, mac_addr, vf);
 
        return 0;
 }
@@ -1138,9 +1129,9 @@ int ixgbe_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos)
                        adapter->vfinfo[vf].vlan_count--;
                adapter->vfinfo[vf].pf_vlan = 0;
                adapter->vfinfo[vf].pf_qos = 0;
-       }
+       }
 out:
-       return err;
+       return err;
 }
 
 static int ixgbe_link_mbps(struct ixgbe_adapter *adapter)
@@ -1231,7 +1222,8 @@ void ixgbe_check_vf_rate_limit(struct ixgbe_adapter *adapter)
        }
 }
 
-int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate)
+int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int min_tx_rate,
+                       int max_tx_rate)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        int link_speed;
@@ -1249,13 +1241,16 @@ int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate)
        if (link_speed != 10000)
                return -EINVAL;
 
+       if (min_tx_rate)
+               return -EINVAL;
+
        /* rate limit cannot be less than 10Mbs or greater than link speed */
-       if (tx_rate && ((tx_rate <= 10) || (tx_rate > link_speed)))
+       if (max_tx_rate && ((max_tx_rate <= 10) || (max_tx_rate > link_speed)))
                return -EINVAL;
 
        /* store values */
        adapter->vf_rate_link_speed = link_speed;
-       adapter->vfinfo[vf].tx_rate = tx_rate;
+       adapter->vfinfo[vf].tx_rate = max_tx_rate;
 
        /* update hardware configuration */
        ixgbe_set_vf_rate_limit(adapter, vf);
@@ -1297,7 +1292,8 @@ int ixgbe_ndo_get_vf_config(struct net_device *netdev,
                return -EINVAL;
        ivi->vf = vf;
        memcpy(&ivi->mac, adapter->vfinfo[vf].vf_mac_addresses, ETH_ALEN);
-       ivi->tx_rate = adapter->vfinfo[vf].tx_rate;
+       ivi->max_tx_rate = adapter->vfinfo[vf].tx_rate;
+       ivi->min_tx_rate = 0;
        ivi->vlan = adapter->vfinfo[vf].pf_vlan;
        ivi->qos = adapter->vfinfo[vf].pf_qos;
        ivi->spoofchk = adapter->vfinfo[vf].spoofchk_enabled;
index 139eaddfb2ed5c8c14a3891137a313b4fcaa3a6a..32c26d586c01e1c5d561922774df4b250d3fdd73 100644 (file)
@@ -34,7 +34,9 @@
  */
 #define IXGBE_MAX_VFS_DRV_LIMIT  (IXGBE_MAX_VF_FUNCTIONS - 1)
 
+#ifdef CONFIG_PCI_IOV
 void ixgbe_restore_vf_multicasts(struct ixgbe_adapter *adapter);
+#endif
 void ixgbe_msg_task(struct ixgbe_adapter *adapter);
 int ixgbe_vf_configuration(struct pci_dev *pdev, unsigned int event_mask);
 void ixgbe_disable_tx_rx(struct ixgbe_adapter *adapter);
@@ -42,7 +44,8 @@ void ixgbe_ping_all_vfs(struct ixgbe_adapter *adapter);
 int ixgbe_ndo_set_vf_mac(struct net_device *netdev, int queue, u8 *mac);
 int ixgbe_ndo_set_vf_vlan(struct net_device *netdev, int queue, u16 vlan,
                           u8 qos);
-int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate);
+int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int min_tx_rate,
+                       int max_tx_rate);
 int ixgbe_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting);
 int ixgbe_ndo_get_vf_config(struct net_device *netdev,
                            int vf, struct ifla_vf_info *ivi);
index 8a6ff2423f076974d1c3c408b97c497d00bdc277..9a89f98b35f0290391c1a4da654906ae2364002c 100644 (file)
@@ -160,7 +160,7 @@ struct ixgbe_thermal_sensor_data {
 #define IXGBE_MAX_EITR     0x00000FF8
 #define IXGBE_MIN_EITR     8
 #define IXGBE_EITR(_i)  (((_i) <= 23) ? (0x00820 + ((_i) * 4)) : \
-                         (0x012300 + (((_i) - 24) * 4)))
+                        (0x012300 + (((_i) - 24) * 4)))
 #define IXGBE_EITR_ITR_INT_MASK 0x00000FF8
 #define IXGBE_EITR_LLI_MOD      0x00008000
 #define IXGBE_EITR_CNT_WDIS     0x80000000
@@ -213,7 +213,7 @@ struct ixgbe_thermal_sensor_data {
  * 64-127: 0x0D014 + (n-64)*0x40
  */
 #define IXGBE_SRRCTL(_i) (((_i) <= 15) ? (0x02100 + ((_i) * 4)) : \
-                          (((_i) < 64) ? (0x01014 + ((_i) * 0x40)) : \
+                         (((_i) < 64) ? (0x01014 + ((_i) * 0x40)) : \
                          (0x0D014 + (((_i) - 64) * 0x40))))
 /*
  * Rx DCA Control Register:
@@ -222,11 +222,11 @@ struct ixgbe_thermal_sensor_data {
  * 64-127: 0x0D00C + (n-64)*0x40
  */
 #define IXGBE_DCA_RXCTRL(_i)    (((_i) <= 15) ? (0x02200 + ((_i) * 4)) : \
-                                 (((_i) < 64) ? (0x0100C + ((_i) * 0x40)) : \
+                                (((_i) < 64) ? (0x0100C + ((_i) * 0x40)) : \
                                 (0x0D00C + (((_i) - 64) * 0x40))))
 #define IXGBE_RDRXCTL           0x02F00
 #define IXGBE_RXPBSIZE(_i)      (0x03C00 + ((_i) * 4))
-                                             /* 8 of these 0x03C00 - 0x03C1C */
+                                            /* 8 of these 0x03C00 - 0x03C1C */
 #define IXGBE_RXCTRL    0x03000
 #define IXGBE_DROPEN    0x03D04
 #define IXGBE_RXPBSIZE_SHIFT 10
@@ -239,14 +239,14 @@ struct ixgbe_thermal_sensor_data {
 /* Multicast Table Array - 128 entries */
 #define IXGBE_MTA(_i)   (0x05200 + ((_i) * 4))
 #define IXGBE_RAL(_i)   (((_i) <= 15) ? (0x05400 + ((_i) * 8)) : \
-                         (0x0A200 + ((_i) * 8)))
+                        (0x0A200 + ((_i) * 8)))
 #define IXGBE_RAH(_i)   (((_i) <= 15) ? (0x05404 + ((_i) * 8)) : \
-                         (0x0A204 + ((_i) * 8)))
+                        (0x0A204 + ((_i) * 8)))
 #define IXGBE_MPSAR_LO(_i) (0x0A600 + ((_i) * 8))
 #define IXGBE_MPSAR_HI(_i) (0x0A604 + ((_i) * 8))
 /* Packet split receive type */
 #define IXGBE_PSRTYPE(_i)    (((_i) <= 15) ? (0x05480 + ((_i) * 4)) : \
-                              (0x0EA00 + ((_i) * 4)))
+                             (0x0EA00 + ((_i) * 4)))
 /* array of 4096 1-bit vlan filters */
 #define IXGBE_VFTA(_i)  (0x0A000 + ((_i) * 4))
 /*array of 4096 4-bit vlan vmdq indices */
@@ -696,7 +696,7 @@ struct ixgbe_thermal_sensor_data {
 
 #define IXGBE_RQSMR(_i) (0x02300 + ((_i) * 4))
 #define IXGBE_TQSMR(_i) (((_i) <= 7) ? (0x07300 + ((_i) * 4)) : \
-                         (0x08600 + ((_i) * 4)))
+                        (0x08600 + ((_i) * 4)))
 #define IXGBE_TQSM(_i)  (0x08600 + ((_i) * 4))
 
 #define IXGBE_QPRC(_i) (0x01030 + ((_i) * 0x40)) /* 16 of these */
@@ -820,7 +820,7 @@ struct ixgbe_thermal_sensor_data {
 #define IXGBE_GCR_EXT_VT_MODE_32        0x00000002
 #define IXGBE_GCR_EXT_VT_MODE_64        0x00000003
 #define IXGBE_GCR_EXT_SRIOV             (IXGBE_GCR_EXT_MSIX_EN | \
-                                         IXGBE_GCR_EXT_VT_MODE_64)
+                                        IXGBE_GCR_EXT_VT_MODE_64)
 
 /* Time Sync Registers */
 #define IXGBE_TSYNCRXCTL 0x05188 /* Rx Time Sync Control register - RW */
@@ -1396,10 +1396,10 @@ enum {
 #define IXGBE_EIMC_OTHER        IXGBE_EICR_OTHER     /* INT Cause Active */
 
 #define IXGBE_EIMS_ENABLE_MASK ( \
-                                IXGBE_EIMS_RTX_QUEUE       | \
-                                IXGBE_EIMS_LSC             | \
-                                IXGBE_EIMS_TCP_TIMER       | \
-                                IXGBE_EIMS_OTHER)
+                               IXGBE_EIMS_RTX_QUEUE       | \
+                               IXGBE_EIMS_LSC             | \
+                               IXGBE_EIMS_TCP_TIMER       | \
+                               IXGBE_EIMS_OTHER)
 
 /* Immediate Interrupt Rx (A.K.A. Low Latency Interrupt) */
 #define IXGBE_IMIR_PORT_IM_EN     0x00010000  /* TCP port enable */
@@ -2161,18 +2161,18 @@ enum {
 
 /* Masks to determine if packets should be dropped due to frame errors */
 #define IXGBE_RXD_ERR_FRAME_ERR_MASK ( \
-                                      IXGBE_RXD_ERR_CE | \
-                                      IXGBE_RXD_ERR_LE | \
-                                      IXGBE_RXD_ERR_PE | \
-                                      IXGBE_RXD_ERR_OSE | \
-                                      IXGBE_RXD_ERR_USE)
+                                     IXGBE_RXD_ERR_CE | \
+                                     IXGBE_RXD_ERR_LE | \
+                                     IXGBE_RXD_ERR_PE | \
+                                     IXGBE_RXD_ERR_OSE | \
+                                     IXGBE_RXD_ERR_USE)
 
 #define IXGBE_RXDADV_ERR_FRAME_ERR_MASK ( \
-                                      IXGBE_RXDADV_ERR_CE | \
-                                      IXGBE_RXDADV_ERR_LE | \
-                                      IXGBE_RXDADV_ERR_PE | \
-                                      IXGBE_RXDADV_ERR_OSE | \
-                                      IXGBE_RXDADV_ERR_USE)
+                                     IXGBE_RXDADV_ERR_CE | \
+                                     IXGBE_RXDADV_ERR_LE | \
+                                     IXGBE_RXDADV_ERR_PE | \
+                                     IXGBE_RXDADV_ERR_OSE | \
+                                     IXGBE_RXDADV_ERR_USE)
 
 /* Multicast bit mask */
 #define IXGBE_MCSTCTRL_MFE      0x4
@@ -2393,9 +2393,9 @@ struct ixgbe_adv_tx_context_desc {
 #define IXGBE_ADVTXD_CC         0x00000080 /* Check Context */
 #define IXGBE_ADVTXD_POPTS_SHIFT      8  /* Adv desc POPTS shift */
 #define IXGBE_ADVTXD_POPTS_IXSM (IXGBE_TXD_POPTS_IXSM << \
-                                 IXGBE_ADVTXD_POPTS_SHIFT)
+                                IXGBE_ADVTXD_POPTS_SHIFT)
 #define IXGBE_ADVTXD_POPTS_TXSM (IXGBE_TXD_POPTS_TXSM << \
-                                 IXGBE_ADVTXD_POPTS_SHIFT)
+                                IXGBE_ADVTXD_POPTS_SHIFT)
 #define IXGBE_ADVTXD_POPTS_ISCO_1ST  0x00000000 /* 1st TSO of iSCSI PDU */
 #define IXGBE_ADVTXD_POPTS_ISCO_MDL  0x00000800 /* Middle TSO of iSCSI PDU */
 #define IXGBE_ADVTXD_POPTS_ISCO_LAST 0x00001000 /* Last TSO of iSCSI PDU */
@@ -2435,10 +2435,10 @@ typedef u32 ixgbe_link_speed;
 #define IXGBE_LINK_SPEED_1GB_FULL  0x0020
 #define IXGBE_LINK_SPEED_10GB_FULL 0x0080
 #define IXGBE_LINK_SPEED_82598_AUTONEG (IXGBE_LINK_SPEED_1GB_FULL | \
-                                        IXGBE_LINK_SPEED_10GB_FULL)
+                                       IXGBE_LINK_SPEED_10GB_FULL)
 #define IXGBE_LINK_SPEED_82599_AUTONEG (IXGBE_LINK_SPEED_100_FULL | \
-                                        IXGBE_LINK_SPEED_1GB_FULL | \
-                                        IXGBE_LINK_SPEED_10GB_FULL)
+                                       IXGBE_LINK_SPEED_1GB_FULL | \
+                                       IXGBE_LINK_SPEED_10GB_FULL)
 
 
 /* Physical layer type */
@@ -2746,7 +2746,7 @@ struct ixgbe_bus_info {
 /* Flow control parameters */
 struct ixgbe_fc_info {
        u32 high_water[MAX_TRAFFIC_CLASS]; /* Flow Control High-water */
-       u32 low_water; /* Flow Control Low-water */
+       u32 low_water[MAX_TRAFFIC_CLASS]; /* Flow Control Low-water */
        u16 pause_time; /* Flow Control Pause timer */
        bool send_xon; /* Flow control send XON */
        bool strict_ieee; /* Strict IEEE mode */
@@ -2840,7 +2840,7 @@ struct ixgbe_hw;
 
 /* iterator type for walking multicast address lists */
 typedef u8* (*ixgbe_mc_addr_itr) (struct ixgbe_hw *hw, u8 **mc_addr_ptr,
-                                  u32 *vmdq);
+                                 u32 *vmdq);
 
 /* Function pointer table */
 struct ixgbe_eeprom_operations {
@@ -2887,7 +2887,7 @@ struct ixgbe_mac_operations {
        s32 (*setup_link)(struct ixgbe_hw *, ixgbe_link_speed, bool);
        s32 (*check_link)(struct ixgbe_hw *, ixgbe_link_speed *, bool *, bool);
        s32 (*get_link_capabilities)(struct ixgbe_hw *, ixgbe_link_speed *,
-                                    bool *);
+                                    bool *);
 
        /* Packet Buffer Manipulation */
        void (*set_rxpba)(struct ixgbe_hw *, int, u32, int);
index 188a5974b85c41f7b8279b6128f72f749e845f76..40dd798e1290efb6b3d67928d86320b12cb767fe 100644 (file)
@@ -81,7 +81,7 @@ static s32 ixgbe_setup_mac_link_X540(struct ixgbe_hw *hw,
                                     bool autoneg_wait_to_complete)
 {
        return hw->phy.ops.setup_link_speed(hw, speed,
-                                           autoneg_wait_to_complete);
+                                           autoneg_wait_to_complete);
 }
 
 /**
@@ -155,7 +155,7 @@ mac_reset_top:
        /* Add the SAN MAC address to the RAR only if it's a valid address */
        if (is_valid_ether_addr(hw->mac.san_addr)) {
                hw->mac.ops.set_rar(hw, hw->mac.num_rar_entries - 1,
-                                   hw->mac.san_addr, 0, IXGBE_RAH_AV);
+                                   hw->mac.san_addr, 0, IXGBE_RAH_AV);
 
                /* Save the SAN MAC RAR index */
                hw->mac.san_mac_rar_index = hw->mac.num_rar_entries - 1;
@@ -166,7 +166,7 @@ mac_reset_top:
 
        /* Store the alternative WWNN/WWPN prefix */
        hw->mac.ops.get_wwn_prefix(hw, &hw->mac.wwnn_prefix,
-                                  &hw->mac.wwpn_prefix);
+                                  &hw->mac.wwpn_prefix);
 
 reset_hw_out:
        return status;
@@ -237,9 +237,9 @@ static s32 ixgbe_init_eeprom_params_X540(struct ixgbe_hw *hw)
 
                eec = IXGBE_READ_REG(hw, IXGBE_EEC);
                eeprom_size = (u16)((eec & IXGBE_EEC_SIZE) >>
-                                   IXGBE_EEC_SIZE_SHIFT);
+                                   IXGBE_EEC_SIZE_SHIFT);
                eeprom->word_size = 1 << (eeprom_size +
-                                         IXGBE_EEPROM_WORD_SIZE_SHIFT);
+                                         IXGBE_EEPROM_WORD_SIZE_SHIFT);
 
                hw_dbg(hw, "Eeprom params: type = %d, size = %d\n",
                       eeprom->type, eeprom->word_size);
@@ -712,8 +712,7 @@ static s32 ixgbe_get_swfw_sync_semaphore(struct ixgbe_hw *hw)
                        udelay(50);
                }
        } else {
-               hw_dbg(hw, "Software semaphore SMBI between device drivers "
-                          "not granted.\n");
+               hw_dbg(hw, "Software semaphore SMBI between device drivers not granted.\n");
        }
 
        return status;
@@ -813,7 +812,7 @@ static struct ixgbe_mac_operations mac_ops_X540 = {
        .clear_hw_cntrs         = &ixgbe_clear_hw_cntrs_generic,
        .get_media_type         = &ixgbe_get_media_type_X540,
        .get_supported_physical_layer =
-                                  &ixgbe_get_supported_physical_layer_X540,
+                                 &ixgbe_get_supported_physical_layer_X540,
        .enable_rx_dma          = &ixgbe_enable_rx_dma_generic,
        .get_mac_addr           = &ixgbe_get_mac_addr_generic,
        .get_san_mac_addr       = &ixgbe_get_san_mac_addr_generic,
index 1baecb60f0657e20a4500380ece36a72f908c3d9..d420f124633f046d5b61f6c357656c2a3b13b9f0 100644 (file)
@@ -135,8 +135,8 @@ static int ixgbevf_get_settings(struct net_device *netdev,
                ethtool_cmd_speed_set(ecmd, speed);
                ecmd->duplex = DUPLEX_FULL;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
 
        return 0;
@@ -813,5 +813,5 @@ static const struct ethtool_ops ixgbevf_ethtool_ops = {
 
 void ixgbevf_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &ixgbevf_ethtool_ops);
+       netdev->ethtool_ops = &ixgbevf_ethtool_ops;
 }
index de2793b06305528ee1f62da85be27a069df10306..75467f83772ca698795c495623cbae9ce1c2c7ef 100644 (file)
@@ -85,7 +85,7 @@ static DEFINE_PCI_DEVICE_TABLE(ixgbevf_pci_tbl) = {
 MODULE_DEVICE_TABLE(pci, ixgbevf_pci_tbl);
 
 MODULE_AUTHOR("Intel Corporation, <linux.nics@intel.com>");
-MODULE_DESCRIPTION("Intel(R) 82599 Virtual Function Driver");
+MODULE_DESCRIPTION("Intel(R) 10 Gigabit Virtual Function Network Driver");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(DRV_VERSION);
 
index b7b8d74c22d9c6f7e7f9aaa7c9211722ec5929fd..b151a949f352a20ec8e74b4f3a7b6bb194ce841c 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/in.h>
 #include <linux/ip.h>
+#include <net/tso.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
 #include <linux/etherdevice.h>
@@ -179,10 +180,18 @@ static char mv643xx_eth_driver_version[] = "1.4";
  * Misc definitions.
  */
 #define DEFAULT_RX_QUEUE_SIZE  128
-#define DEFAULT_TX_QUEUE_SIZE  256
+#define DEFAULT_TX_QUEUE_SIZE  512
 #define SKB_DMA_REALIGN                ((PAGE_SIZE - NET_SKB_PAD) % SMP_CACHE_BYTES)
 
+#define TSO_HEADER_SIZE                128
 
+/* Max number of allowed TCP segments for software TSO */
+#define MV643XX_MAX_TSO_SEGS 100
+#define MV643XX_MAX_SKB_DESCS (MV643XX_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS)
+
+#define IS_TSO_HEADER(txq, addr) \
+       ((addr >= txq->tso_hdrs_dma) && \
+        (addr < txq->tso_hdrs_dma + txq->tx_ring_size * TSO_HEADER_SIZE))
 /*
  * RX/TX descriptors.
  */
@@ -250,6 +259,7 @@ struct tx_desc {
 #define GEN_TCP_UDP_CHECKSUM           0x00020000
 #define UDP_FRAME                      0x00010000
 #define MAC_HDR_EXTRA_4_BYTES          0x00008000
+#define GEN_TCP_UDP_CHK_FULL           0x00000400
 #define MAC_HDR_EXTRA_8_BYTES          0x00000200
 
 #define TX_IHL_SHIFT                   11
@@ -345,6 +355,12 @@ struct tx_queue {
        int tx_curr_desc;
        int tx_used_desc;
 
+       int tx_stop_threshold;
+       int tx_wake_threshold;
+
+       char *tso_hdrs;
+       dma_addr_t tso_hdrs_dma;
+
        struct tx_desc *tx_desc_area;
        dma_addr_t tx_desc_dma;
        int tx_desc_area_size;
@@ -491,7 +507,7 @@ static void txq_maybe_wake(struct tx_queue *txq)
 
        if (netif_tx_queue_stopped(nq)) {
                __netif_tx_lock(nq, smp_processor_id());
-               if (txq->tx_ring_size - txq->tx_desc_count >= MAX_SKB_FRAGS + 1)
+               if (txq->tx_desc_count <= txq->tx_wake_threshold)
                        netif_tx_wake_queue(nq);
                __netif_tx_unlock(nq);
        }
@@ -661,6 +677,198 @@ static inline unsigned int has_tiny_unaligned_frags(struct sk_buff *skb)
        return 0;
 }
 
+static inline __be16 sum16_as_be(__sum16 sum)
+{
+       return (__force __be16)sum;
+}
+
+static int skb_tx_csum(struct mv643xx_eth_private *mp, struct sk_buff *skb,
+                      u16 *l4i_chk, u32 *command, int length)
+{
+       int ret;
+       u32 cmd = 0;
+
+       if (skb->ip_summed == CHECKSUM_PARTIAL) {
+               int hdr_len;
+               int tag_bytes;
+
+               BUG_ON(skb->protocol != htons(ETH_P_IP) &&
+                      skb->protocol != htons(ETH_P_8021Q));
+
+               hdr_len = (void *)ip_hdr(skb) - (void *)skb->data;
+               tag_bytes = hdr_len - ETH_HLEN;
+
+               if (length - hdr_len > mp->shared->tx_csum_limit ||
+                   unlikely(tag_bytes & ~12)) {
+                       ret = skb_checksum_help(skb);
+                       if (!ret)
+                               goto no_csum;
+                       return ret;
+               }
+
+               if (tag_bytes & 4)
+                       cmd |= MAC_HDR_EXTRA_4_BYTES;
+               if (tag_bytes & 8)
+                       cmd |= MAC_HDR_EXTRA_8_BYTES;
+
+               cmd |= GEN_TCP_UDP_CHECKSUM | GEN_TCP_UDP_CHK_FULL |
+                          GEN_IP_V4_CHECKSUM   |
+                          ip_hdr(skb)->ihl << TX_IHL_SHIFT;
+
+               /* TODO: Revisit this. With the usage of GEN_TCP_UDP_CHK_FULL
+                * it seems we don't need to pass the initial checksum. */
+               switch (ip_hdr(skb)->protocol) {
+               case IPPROTO_UDP:
+                       cmd |= UDP_FRAME;
+                       *l4i_chk = 0;
+                       break;
+               case IPPROTO_TCP:
+                       *l4i_chk = 0;
+                       break;
+               default:
+                       WARN(1, "protocol not supported");
+               }
+       } else {
+no_csum:
+               /* Errata BTS #50, IHL must be 5 if no HW checksum */
+               cmd |= 5 << TX_IHL_SHIFT;
+       }
+       *command = cmd;
+       return 0;
+}
+
+static inline int
+txq_put_data_tso(struct net_device *dev, struct tx_queue *txq,
+                struct sk_buff *skb, char *data, int length,
+                bool last_tcp, bool is_last)
+{
+       int tx_index;
+       u32 cmd_sts;
+       struct tx_desc *desc;
+
+       tx_index = txq->tx_curr_desc++;
+       if (txq->tx_curr_desc == txq->tx_ring_size)
+               txq->tx_curr_desc = 0;
+       desc = &txq->tx_desc_area[tx_index];
+
+       desc->l4i_chk = 0;
+       desc->byte_cnt = length;
+       desc->buf_ptr = dma_map_single(dev->dev.parent, data,
+                                      length, DMA_TO_DEVICE);
+       if (unlikely(dma_mapping_error(dev->dev.parent, desc->buf_ptr))) {
+               WARN(1, "dma_map_single failed!\n");
+               return -ENOMEM;
+       }
+
+       cmd_sts = BUFFER_OWNED_BY_DMA;
+       if (last_tcp) {
+               /* last descriptor in the TCP packet */
+               cmd_sts |= ZERO_PADDING | TX_LAST_DESC;
+               /* last descriptor in SKB */
+               if (is_last)
+                       cmd_sts |= TX_ENABLE_INTERRUPT;
+       }
+       desc->cmd_sts = cmd_sts;
+       return 0;
+}
+
+static inline void
+txq_put_hdr_tso(struct sk_buff *skb, struct tx_queue *txq, int length)
+{
+       struct mv643xx_eth_private *mp = txq_to_mp(txq);
+       int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+       int tx_index;
+       struct tx_desc *desc;
+       int ret;
+       u32 cmd_csum = 0;
+       u16 l4i_chk = 0;
+
+       tx_index = txq->tx_curr_desc;
+       desc = &txq->tx_desc_area[tx_index];
+
+       ret = skb_tx_csum(mp, skb, &l4i_chk, &cmd_csum, length);
+       if (ret)
+               WARN(1, "failed to prepare checksum!");
+
+       /* Should we set this? Can't use the value from skb_tx_csum()
+        * as it's not the correct initial L4 checksum to use. */
+       desc->l4i_chk = 0;
+
+       desc->byte_cnt = hdr_len;
+       desc->buf_ptr = txq->tso_hdrs_dma +
+                       txq->tx_curr_desc * TSO_HEADER_SIZE;
+       desc->cmd_sts = cmd_csum | BUFFER_OWNED_BY_DMA  | TX_FIRST_DESC |
+                                  GEN_CRC;
+
+       txq->tx_curr_desc++;
+       if (txq->tx_curr_desc == txq->tx_ring_size)
+               txq->tx_curr_desc = 0;
+}
+
+static int txq_submit_tso(struct tx_queue *txq, struct sk_buff *skb,
+                         struct net_device *dev)
+{
+       struct mv643xx_eth_private *mp = txq_to_mp(txq);
+       int total_len, data_left, ret;
+       int desc_count = 0;
+       struct tso_t tso;
+       int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+
+       /* Count needed descriptors */
+       if ((txq->tx_desc_count + tso_count_descs(skb)) >= txq->tx_ring_size) {
+               netdev_dbg(dev, "not enough descriptors for TSO!\n");
+               return -EBUSY;
+       }
+
+       /* Initialize the TSO handler, and prepare the first payload */
+       tso_start(skb, &tso);
+
+       total_len = skb->len - hdr_len;
+       while (total_len > 0) {
+               char *hdr;
+
+               data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len);
+               total_len -= data_left;
+               desc_count++;
+
+               /* prepare packet headers: MAC + IP + TCP */
+               hdr = txq->tso_hdrs + txq->tx_curr_desc * TSO_HEADER_SIZE;
+               tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0);
+               txq_put_hdr_tso(skb, txq, data_left);
+
+               while (data_left > 0) {
+                       int size;
+                       desc_count++;
+
+                       size = min_t(int, tso.size, data_left);
+                       ret = txq_put_data_tso(dev, txq, skb, tso.data, size,
+                                              size == data_left,
+                                              total_len == 0);
+                       if (ret)
+                               goto err_release;
+                       data_left -= size;
+                       tso_build_data(skb, &tso, size);
+               }
+       }
+
+       __skb_queue_tail(&txq->tx_skb, skb);
+       skb_tx_timestamp(skb);
+
+       /* clear TX_END status */
+       mp->work_tx_end &= ~(1 << txq->index);
+
+       /* ensure all descriptors are written before poking hardware */
+       wmb();
+       txq_enable(txq);
+       txq->tx_desc_count += desc_count;
+       return 0;
+err_release:
+       /* TODO: Release all used data descriptors; header descriptors must not
+        * be DMA-unmapped.
+        */
+       return ret;
+}
+
 static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb)
 {
        struct mv643xx_eth_private *mp = txq_to_mp(txq);
@@ -671,8 +879,10 @@ static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb)
                skb_frag_t *this_frag;
                int tx_index;
                struct tx_desc *desc;
+               void *addr;
 
                this_frag = &skb_shinfo(skb)->frags[frag];
+               addr = page_address(this_frag->page.p) + this_frag->page_offset;
                tx_index = txq->tx_curr_desc++;
                if (txq->tx_curr_desc == txq->tx_ring_size)
                        txq->tx_curr_desc = 0;
@@ -692,19 +902,13 @@ static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb)
 
                desc->l4i_chk = 0;
                desc->byte_cnt = skb_frag_size(this_frag);
-               desc->buf_ptr = skb_frag_dma_map(mp->dev->dev.parent,
-                                                this_frag, 0,
-                                                skb_frag_size(this_frag),
-                                                DMA_TO_DEVICE);
+               desc->buf_ptr = dma_map_single(mp->dev->dev.parent, addr,
+                                              desc->byte_cnt, DMA_TO_DEVICE);
        }
 }
 
-static inline __be16 sum16_as_be(__sum16 sum)
-{
-       return (__force __be16)sum;
-}
-
-static int txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb)
+static int txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb,
+                         struct net_device *dev)
 {
        struct mv643xx_eth_private *mp = txq_to_mp(txq);
        int nr_frags = skb_shinfo(skb)->nr_frags;
@@ -712,54 +916,22 @@ static int txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb)
        struct tx_desc *desc;
        u32 cmd_sts;
        u16 l4i_chk;
-       int length;
+       int length, ret;
 
-       cmd_sts = TX_FIRST_DESC | GEN_CRC | BUFFER_OWNED_BY_DMA;
+       cmd_sts = 0;
        l4i_chk = 0;
 
-       if (skb->ip_summed == CHECKSUM_PARTIAL) {
-               int hdr_len;
-               int tag_bytes;
-
-               BUG_ON(skb->protocol != htons(ETH_P_IP) &&
-                      skb->protocol != htons(ETH_P_8021Q));
-
-               hdr_len = (void *)ip_hdr(skb) - (void *)skb->data;
-               tag_bytes = hdr_len - ETH_HLEN;
-               if (skb->len - hdr_len > mp->shared->tx_csum_limit ||
-                   unlikely(tag_bytes & ~12)) {
-                       if (skb_checksum_help(skb) == 0)
-                               goto no_csum;
-                       dev_kfree_skb_any(skb);
-                       return 1;
-               }
-
-               if (tag_bytes & 4)
-                       cmd_sts |= MAC_HDR_EXTRA_4_BYTES;
-               if (tag_bytes & 8)
-                       cmd_sts |= MAC_HDR_EXTRA_8_BYTES;
-
-               cmd_sts |= GEN_TCP_UDP_CHECKSUM |
-                          GEN_IP_V4_CHECKSUM   |
-                          ip_hdr(skb)->ihl << TX_IHL_SHIFT;
-
-               switch (ip_hdr(skb)->protocol) {
-               case IPPROTO_UDP:
-                       cmd_sts |= UDP_FRAME;
-                       l4i_chk = ntohs(sum16_as_be(udp_hdr(skb)->check));
-                       break;
-               case IPPROTO_TCP:
-                       l4i_chk = ntohs(sum16_as_be(tcp_hdr(skb)->check));
-                       break;
-               default:
-                       BUG();
-               }
-       } else {
-no_csum:
-               /* Errata BTS #50, IHL must be 5 if no HW checksum */
-               cmd_sts |= 5 << TX_IHL_SHIFT;
+       if (txq->tx_ring_size - txq->tx_desc_count < MAX_SKB_FRAGS + 1) {
+               if (net_ratelimit())
+                       netdev_err(dev, "tx queue full?!\n");
+               return -EBUSY;
        }
 
+       ret = skb_tx_csum(mp, skb, &l4i_chk, &cmd_sts, skb->len);
+       if (ret)
+               return ret;
+       cmd_sts |= TX_FIRST_DESC | GEN_CRC | BUFFER_OWNED_BY_DMA;
+
        tx_index = txq->tx_curr_desc++;
        if (txq->tx_curr_desc == txq->tx_ring_size)
                txq->tx_curr_desc = 0;
@@ -801,7 +973,7 @@ no_csum:
 static netdev_tx_t mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct mv643xx_eth_private *mp = netdev_priv(dev);
-       int length, queue;
+       int length, queue, ret;
        struct tx_queue *txq;
        struct netdev_queue *nq;
 
@@ -810,30 +982,26 @@ static netdev_tx_t mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev)
        nq = netdev_get_tx_queue(dev, queue);
 
        if (has_tiny_unaligned_frags(skb) && __skb_linearize(skb)) {
-               txq->tx_dropped++;
                netdev_printk(KERN_DEBUG, dev,
                              "failed to linearize skb with tiny unaligned fragment\n");
                return NETDEV_TX_BUSY;
        }
 
-       if (txq->tx_ring_size - txq->tx_desc_count < MAX_SKB_FRAGS + 1) {
-               if (net_ratelimit())
-                       netdev_err(dev, "tx queue full?!\n");
-               dev_kfree_skb_any(skb);
-               return NETDEV_TX_OK;
-       }
-
        length = skb->len;
 
-       if (!txq_submit_skb(txq, skb)) {
-               int entries_left;
-
+       if (skb_is_gso(skb))
+               ret = txq_submit_tso(txq, skb, dev);
+       else
+               ret = txq_submit_skb(txq, skb, dev);
+       if (!ret) {
                txq->tx_bytes += length;
                txq->tx_packets++;
 
-               entries_left = txq->tx_ring_size - txq->tx_desc_count;
-               if (entries_left < MAX_SKB_FRAGS + 1)
+               if (txq->tx_desc_count >= txq->tx_stop_threshold)
                        netif_tx_stop_queue(nq);
+       } else {
+               txq->tx_dropped++;
+               dev_kfree_skb_any(skb);
        }
 
        return NETDEV_TX_OK;
@@ -907,14 +1075,9 @@ static int txq_reclaim(struct tx_queue *txq, int budget, int force)
                        mp->dev->stats.tx_errors++;
                }
 
-               if (cmd_sts & TX_FIRST_DESC) {
+               if (!IS_TSO_HEADER(txq, desc->buf_ptr))
                        dma_unmap_single(mp->dev->dev.parent, desc->buf_ptr,
                                         desc->byte_cnt, DMA_TO_DEVICE);
-               } else {
-                       dma_unmap_page(mp->dev->dev.parent, desc->buf_ptr,
-                                      desc->byte_cnt, DMA_TO_DEVICE);
-               }
-
                dev_kfree_skb(skb);
        }
 
@@ -1010,8 +1173,9 @@ static void txq_set_fixed_prio_mode(struct tx_queue *txq)
 
 
 /* mii management interface *************************************************/
-static void mv643xx_adjust_pscr(struct mv643xx_eth_private *mp)
+static void mv643xx_eth_adjust_link(struct net_device *dev)
 {
+       struct mv643xx_eth_private *mp = netdev_priv(dev);
        u32 pscr = rdlp(mp, PORT_SERIAL_CONTROL);
        u32 autoneg_disable = FORCE_LINK_PASS |
                     DISABLE_AUTO_NEG_SPEED_GMII |
@@ -1387,7 +1551,7 @@ mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 
        ret = phy_ethtool_sset(mp->phy, cmd);
        if (!ret)
-               mv643xx_adjust_pscr(mp);
+               mv643xx_eth_adjust_link(dev);
        return ret;
 }
 
@@ -1456,7 +1620,11 @@ mv643xx_eth_set_ringparam(struct net_device *dev, struct ethtool_ringparam *er)
                return -EINVAL;
 
        mp->rx_ring_size = er->rx_pending < 4096 ? er->rx_pending : 4096;
-       mp->tx_ring_size = er->tx_pending < 4096 ? er->tx_pending : 4096;
+       mp->tx_ring_size = clamp_t(unsigned int, er->tx_pending,
+                                  MV643XX_MAX_SKB_DESCS * 2, 4096);
+       if (mp->tx_ring_size != er->tx_pending)
+               netdev_warn(dev, "TX queue size set to %u (requested %u)\n",
+                           mp->tx_ring_size, er->tx_pending);
 
        if (netif_running(dev)) {
                mv643xx_eth_stop(dev);
@@ -1832,6 +2000,13 @@ static int txq_init(struct mv643xx_eth_private *mp, int index)
 
        txq->tx_ring_size = mp->tx_ring_size;
 
+       /* A queue must always have room for at least one skb.
+        * Therefore, stop the queue when the free entries reaches
+        * the maximum number of descriptors per skb.
+        */
+       txq->tx_stop_threshold = txq->tx_ring_size - MV643XX_MAX_SKB_DESCS;
+       txq->tx_wake_threshold = txq->tx_stop_threshold / 2;
+
        txq->tx_desc_count = 0;
        txq->tx_curr_desc = 0;
        txq->tx_used_desc = 0;
@@ -1871,6 +2046,15 @@ static int txq_init(struct mv643xx_eth_private *mp, int index)
                                        nexti * sizeof(struct tx_desc);
        }
 
+       /* Allocate DMA buffers for TSO MAC/IP/TCP headers */
+       txq->tso_hdrs = dma_alloc_coherent(mp->dev->dev.parent,
+                                          txq->tx_ring_size * TSO_HEADER_SIZE,
+                                          &txq->tso_hdrs_dma, GFP_KERNEL);
+       if (txq->tso_hdrs == NULL) {
+               dma_free_coherent(mp->dev->dev.parent, txq->tx_desc_area_size,
+                                 txq->tx_desc_area, txq->tx_desc_dma);
+               return -ENOMEM;
+       }
        skb_queue_head_init(&txq->tx_skb);
 
        return 0;
@@ -1891,6 +2075,10 @@ static void txq_deinit(struct tx_queue *txq)
        else
                dma_free_coherent(mp->dev->dev.parent, txq->tx_desc_area_size,
                                  txq->tx_desc_area, txq->tx_desc_dma);
+       if (txq->tso_hdrs)
+               dma_free_coherent(mp->dev->dev.parent,
+                                 txq->tx_ring_size * TSO_HEADER_SIZE,
+                                 txq->tso_hdrs, txq->tso_hdrs_dma);
 }
 
 
@@ -2303,7 +2491,7 @@ static int mv643xx_eth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 
        ret = phy_mii_ioctl(mp->phy, ifr, cmd);
        if (!ret)
-               mv643xx_adjust_pscr(mp);
+               mv643xx_eth_adjust_link(dev);
        return ret;
 }
 
@@ -2678,6 +2866,7 @@ static void set_params(struct mv643xx_eth_private *mp,
                       struct mv643xx_eth_platform_data *pd)
 {
        struct net_device *dev = mp->dev;
+       unsigned int tx_ring_size;
 
        if (is_valid_ether_addr(pd->mac_addr))
                memcpy(dev->dev_addr, pd->mac_addr, ETH_ALEN);
@@ -2692,22 +2881,22 @@ static void set_params(struct mv643xx_eth_private *mp,
 
        mp->rxq_count = pd->rx_queue_count ? : 1;
 
-       mp->tx_ring_size = DEFAULT_TX_QUEUE_SIZE;
+       tx_ring_size = DEFAULT_TX_QUEUE_SIZE;
        if (pd->tx_queue_size)
-               mp->tx_ring_size = pd->tx_queue_size;
+               tx_ring_size = pd->tx_queue_size;
+
+       mp->tx_ring_size = clamp_t(unsigned int, tx_ring_size,
+                                  MV643XX_MAX_SKB_DESCS * 2, 4096);
+       if (mp->tx_ring_size != tx_ring_size)
+               netdev_warn(dev, "TX queue size set to %u (requested %u)\n",
+                           mp->tx_ring_size, tx_ring_size);
+
        mp->tx_desc_sram_addr = pd->tx_sram_addr;
        mp->tx_desc_sram_size = pd->tx_sram_size;
 
        mp->txq_count = pd->tx_queue_count ? : 1;
 }
 
-static void mv643xx_eth_adjust_link(struct net_device *dev)
-{
-       struct mv643xx_eth_private *mp = netdev_priv(dev);
-
-       mv643xx_adjust_pscr(mp);
-}
-
 static struct phy_device *phy_scan(struct mv643xx_eth_private *mp,
                                   int phy_addr)
 {
@@ -2889,7 +3078,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
        if (err)
                goto out;
 
-       SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops);
+       dev->ethtool_ops = &mv643xx_eth_ethtool_ops;
 
        init_pscr(mp, pd->speed, pd->duplex);
 
@@ -2921,11 +3110,14 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
        dev->watchdog_timeo = 2 * HZ;
        dev->base_addr = 0;
 
-       dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
-       dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
-       dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM;
+       dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
+       dev->vlan_features = dev->features;
+
+       dev->features |= NETIF_F_RXCSUM;
+       dev->hw_features = dev->features;
 
        dev->priv_flags |= IFF_UNICAST_FLT;
+       dev->gso_max_segs = MV643XX_MAX_TSO_SEGS;
 
        SET_NETDEV_DEV(dev, &pdev->dev);
 
index 9d5ced263a5eb3d1397e95b675a2f83e71432547..fc2fb25343f417964070c058baea2e8162dab595 100644 (file)
@@ -195,11 +195,10 @@ static int orion_mdio_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       bus = mdiobus_alloc_size(sizeof(struct orion_mdio_dev));
-       if (!bus) {
-               dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
+       bus = devm_mdiobus_alloc_size(&pdev->dev,
+                                     sizeof(struct orion_mdio_dev));
+       if (!bus)
                return -ENOMEM;
-       }
 
        bus->name = "orion_mdio_bus";
        bus->read = orion_mdio_read;
@@ -208,11 +207,10 @@ static int orion_mdio_probe(struct platform_device *pdev)
                 dev_name(&pdev->dev));
        bus->parent = &pdev->dev;
 
-       bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
-       if (!bus->irq) {
-               mdiobus_free(bus);
+       bus->irq = devm_kmalloc_array(&pdev->dev, PHY_MAX_ADDR, sizeof(int),
+                                     GFP_KERNEL);
+       if (!bus->irq)
                return -ENOMEM;
-       }
 
        for (i = 0; i < PHY_MAX_ADDR; i++)
                bus->irq[i] = PHY_POLL;
@@ -264,8 +262,6 @@ static int orion_mdio_probe(struct platform_device *pdev)
 out_mdio:
        if (!IS_ERR(dev->clk))
                clk_disable_unprepare(dev->clk);
-       kfree(bus->irq);
-       mdiobus_free(bus);
        return ret;
 }
 
@@ -276,8 +272,6 @@ static int orion_mdio_remove(struct platform_device *pdev)
 
        writel(0, dev->regs + MVMDIO_ERR_INT_MASK);
        mdiobus_unregister(bus);
-       kfree(bus->irq);
-       mdiobus_free(bus);
        if (!IS_ERR(dev->clk))
                clk_disable_unprepare(dev->clk);
 
index 14786c8bf99efcddbbbdff7bc0f9ee9e20933864..45beca17fa50a3d1e4f920ad44097a3df3d73d07 100644 (file)
@@ -23,6 +23,7 @@
 #include <net/ip.h>
 #include <net/ipv6.h>
 #include <linux/io.h>
+#include <net/tso.h>
 #include <linux/of.h>
 #include <linux/of_irq.h>
 #include <linux/of_mdio.h>
 #define MVNETA_RX_COAL_PKTS            32
 #define MVNETA_RX_COAL_USEC            100
 
-/* Napi polling weight */
-#define MVNETA_RX_POLL_WEIGHT          64
-
 /* The two bytes Marvell header. Either contains a special value used
  * by Marvell switches when a specific hardware mode is enabled (not
  * supported by this driver) or is filled automatically by zeroes on
 
 #define MVNETA_TX_MTU_MAX              0x3ffff
 
+/* TSO header size */
+#define TSO_HEADER_SIZE 128
+
 /* Max number of Rx descriptors */
 #define MVNETA_MAX_RXD 128
 
 /* Max number of Tx descriptors */
 #define MVNETA_MAX_TXD 532
 
+/* Max number of allowed TCP segments for software TSO */
+#define MVNETA_MAX_TSO_SEGS 100
+
+#define MVNETA_MAX_SKB_DESCS (MVNETA_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS)
+
 /* descriptor aligned size */
 #define MVNETA_DESC_ALIGNED_SIZE       32
 
              ETH_HLEN + ETH_FCS_LEN,                        \
              MVNETA_CPU_D_CACHE_LINE_SIZE)
 
+#define IS_TSO_HEADER(txq, addr) \
+       ((addr >= txq->tso_hdrs_phys) && \
+        (addr < txq->tso_hdrs_phys + txq->size * TSO_HEADER_SIZE))
+
 #define MVNETA_RX_BUF_SIZE(pkt_size)   ((pkt_size) + NET_SKB_PAD)
 
 struct mvneta_pcpu_stats {
@@ -279,9 +289,6 @@ struct mvneta_port {
        u32 cause_rx_tx;
        struct napi_struct napi;
 
-       /* Napi weight */
-       int weight;
-
        /* Core clock */
        struct clk *clk;
        u8 mcast_count[256];
@@ -390,6 +397,8 @@ struct mvneta_tx_queue {
         * descriptor ring
         */
        int count;
+       int tx_stop_threshold;
+       int tx_wake_threshold;
 
        /* Array of transmitted skb */
        struct sk_buff **tx_skb;
@@ -413,6 +422,12 @@ struct mvneta_tx_queue {
 
        /* Index of the next TX DMA descriptor to process */
        int next_desc_to_proc;
+
+       /* DMA buffers for TSO headers */
+       char *tso_hdrs;
+
+       /* DMA address of TSO headers */
+       dma_addr_t tso_hdrs_phys;
 };
 
 struct mvneta_rx_queue {
@@ -441,7 +456,10 @@ struct mvneta_rx_queue {
        int next_desc_to_proc;
 };
 
-static int rxq_number = 8;
+/* The hardware supports eight (8) rx queues, but we are only allowing
+ * the first one to be used. Therefore, let's just allocate one queue.
+ */
+static int rxq_number = 1;
 static int txq_number = 8;
 
 static int rxq_def;
@@ -1277,11 +1295,12 @@ static void mvneta_txq_bufs_free(struct mvneta_port *pp,
 
                mvneta_txq_inc_get(txq);
 
+               if (!IS_TSO_HEADER(txq, tx_desc->buf_phys_addr))
+                       dma_unmap_single(pp->dev->dev.parent,
+                                        tx_desc->buf_phys_addr,
+                                        tx_desc->data_size, DMA_TO_DEVICE);
                if (!skb)
                        continue;
-
-               dma_unmap_single(pp->dev->dev.parent, tx_desc->buf_phys_addr,
-                                tx_desc->data_size, DMA_TO_DEVICE);
                dev_kfree_skb_any(skb);
        }
 }
@@ -1302,7 +1321,7 @@ static void mvneta_txq_done(struct mvneta_port *pp,
        txq->count -= tx_done;
 
        if (netif_tx_queue_stopped(nq)) {
-               if (txq->size - txq->count >= MAX_SKB_FRAGS + 1)
+               if (txq->count <= txq->tx_wake_threshold)
                        netif_tx_wake_queue(nq);
        }
 }
@@ -1519,14 +1538,134 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo,
        return rx_done;
 }
 
+static inline void
+mvneta_tso_put_hdr(struct sk_buff *skb,
+                  struct mvneta_port *pp, struct mvneta_tx_queue *txq)
+{
+       struct mvneta_tx_desc *tx_desc;
+       int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+
+       txq->tx_skb[txq->txq_put_index] = NULL;
+       tx_desc = mvneta_txq_next_desc_get(txq);
+       tx_desc->data_size = hdr_len;
+       tx_desc->command = mvneta_skb_tx_csum(pp, skb);
+       tx_desc->command |= MVNETA_TXD_F_DESC;
+       tx_desc->buf_phys_addr = txq->tso_hdrs_phys +
+                                txq->txq_put_index * TSO_HEADER_SIZE;
+       mvneta_txq_inc_put(txq);
+}
+
+static inline int
+mvneta_tso_put_data(struct net_device *dev, struct mvneta_tx_queue *txq,
+                   struct sk_buff *skb, char *data, int size,
+                   bool last_tcp, bool is_last)
+{
+       struct mvneta_tx_desc *tx_desc;
+
+       tx_desc = mvneta_txq_next_desc_get(txq);
+       tx_desc->data_size = size;
+       tx_desc->buf_phys_addr = dma_map_single(dev->dev.parent, data,
+                                               size, DMA_TO_DEVICE);
+       if (unlikely(dma_mapping_error(dev->dev.parent,
+                    tx_desc->buf_phys_addr))) {
+               mvneta_txq_desc_put(txq);
+               return -ENOMEM;
+       }
+
+       tx_desc->command = 0;
+       txq->tx_skb[txq->txq_put_index] = NULL;
+
+       if (last_tcp) {
+               /* last descriptor in the TCP packet */
+               tx_desc->command = MVNETA_TXD_L_DESC;
+
+               /* last descriptor in SKB */
+               if (is_last)
+                       txq->tx_skb[txq->txq_put_index] = skb;
+       }
+       mvneta_txq_inc_put(txq);
+       return 0;
+}
+
+static int mvneta_tx_tso(struct sk_buff *skb, struct net_device *dev,
+                        struct mvneta_tx_queue *txq)
+{
+       int total_len, data_left;
+       int desc_count = 0;
+       struct mvneta_port *pp = netdev_priv(dev);
+       struct tso_t tso;
+       int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+       int i;
+
+       /* Count needed descriptors */
+       if ((txq->count + tso_count_descs(skb)) >= txq->size)
+               return 0;
+
+       if (skb_headlen(skb) < (skb_transport_offset(skb) + tcp_hdrlen(skb))) {
+               pr_info("*** Is this even  possible???!?!?\n");
+               return 0;
+       }
+
+       /* Initialize the TSO handler, and prepare the first payload */
+       tso_start(skb, &tso);
+
+       total_len = skb->len - hdr_len;
+       while (total_len > 0) {
+               char *hdr;
+
+               data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len);
+               total_len -= data_left;
+               desc_count++;
+
+               /* prepare packet headers: MAC + IP + TCP */
+               hdr = txq->tso_hdrs + txq->txq_put_index * TSO_HEADER_SIZE;
+               tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0);
+
+               mvneta_tso_put_hdr(skb, pp, txq);
+
+               while (data_left > 0) {
+                       int size;
+                       desc_count++;
+
+                       size = min_t(int, tso.size, data_left);
+
+                       if (mvneta_tso_put_data(dev, txq, skb,
+                                                tso.data, size,
+                                                size == data_left,
+                                                total_len == 0))
+                               goto err_release;
+                       data_left -= size;
+
+                       tso_build_data(skb, &tso, size);
+               }
+       }
+
+       return desc_count;
+
+err_release:
+       /* Release all used data descriptors; header descriptors must not
+        * be DMA-unmapped.
+        */
+       for (i = desc_count - 1; i >= 0; i--) {
+               struct mvneta_tx_desc *tx_desc = txq->descs + i;
+               if (!IS_TSO_HEADER(txq, tx_desc->buf_phys_addr))
+                       dma_unmap_single(pp->dev->dev.parent,
+                                        tx_desc->buf_phys_addr,
+                                        tx_desc->data_size,
+                                        DMA_TO_DEVICE);
+               mvneta_txq_desc_put(txq);
+       }
+       return 0;
+}
+
 /* Handle tx fragmentation processing */
 static int mvneta_tx_frag_process(struct mvneta_port *pp, struct sk_buff *skb,
                                  struct mvneta_tx_queue *txq)
 {
        struct mvneta_tx_desc *tx_desc;
-       int i;
+       int i, nr_frags = skb_shinfo(skb)->nr_frags;
 
-       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+       for (i = 0; i < nr_frags; i++) {
                skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
                void *addr = page_address(frag->page.p) + frag->page_offset;
 
@@ -1543,20 +1682,16 @@ static int mvneta_tx_frag_process(struct mvneta_port *pp, struct sk_buff *skb,
                        goto error;
                }
 
-               if (i == (skb_shinfo(skb)->nr_frags - 1)) {
+               if (i == nr_frags - 1) {
                        /* Last descriptor */
                        tx_desc->command = MVNETA_TXD_L_DESC | MVNETA_TXD_Z_PAD;
-
                        txq->tx_skb[txq->txq_put_index] = skb;
-
-                       mvneta_txq_inc_put(txq);
                } else {
                        /* Descriptor in the middle: Not First, Not Last */
                        tx_desc->command = 0;
-
                        txq->tx_skb[txq->txq_put_index] = NULL;
-                       mvneta_txq_inc_put(txq);
                }
+               mvneta_txq_inc_put(txq);
        }
 
        return 0;
@@ -1584,15 +1719,18 @@ static int mvneta_tx(struct sk_buff *skb, struct net_device *dev)
        u16 txq_id = skb_get_queue_mapping(skb);
        struct mvneta_tx_queue *txq = &pp->txqs[txq_id];
        struct mvneta_tx_desc *tx_desc;
-       struct netdev_queue *nq;
        int frags = 0;
        u32 tx_cmd;
 
        if (!netif_running(dev))
                goto out;
 
+       if (skb_is_gso(skb)) {
+               frags = mvneta_tx_tso(skb, dev, txq);
+               goto out;
+       }
+
        frags = skb_shinfo(skb)->nr_frags + 1;
-       nq    = netdev_get_tx_queue(dev, txq_id);
 
        /* Get a descriptor for the first part of the packet */
        tx_desc = mvneta_txq_next_desc_get(txq);
@@ -1635,15 +1773,16 @@ static int mvneta_tx(struct sk_buff *skb, struct net_device *dev)
                }
        }
 
-       txq->count += frags;
-       mvneta_txq_pend_desc_add(pp, txq, frags);
-
-       if (txq->size - txq->count < MAX_SKB_FRAGS + 1)
-               netif_tx_stop_queue(nq);
-
 out:
        if (frags > 0) {
                struct mvneta_pcpu_stats *stats = this_cpu_ptr(pp->stats);
+               struct netdev_queue *nq = netdev_get_tx_queue(dev, txq_id);
+
+               txq->count += frags;
+               mvneta_txq_pend_desc_add(pp, txq, frags);
+
+               if (txq->count >= txq->tx_stop_threshold)
+                       netif_tx_stop_queue(nq);
 
                u64_stats_update_begin(&stats->syncp);
                stats->tx_packets++;
@@ -2003,7 +2142,7 @@ static void mvneta_tx_reset(struct mvneta_port *pp)
 {
        int queue;
 
-       /* free the skb's in the hal tx ring */
+       /* free the skb's in the tx ring */
        for (queue = 0; queue < txq_number; queue++)
                mvneta_txq_done_force(pp, &pp->txqs[queue]);
 
@@ -2081,6 +2220,14 @@ static int mvneta_txq_init(struct mvneta_port *pp,
 {
        txq->size = pp->tx_ring_size;
 
+       /* A queue must always have room for at least one skb.
+        * Therefore, stop the queue when the free entries reaches
+        * the maximum number of descriptors per skb.
+        */
+       txq->tx_stop_threshold = txq->size - MVNETA_MAX_SKB_DESCS;
+       txq->tx_wake_threshold = txq->tx_stop_threshold / 2;
+
+
        /* Allocate memory for TX descriptors */
        txq->descs = dma_alloc_coherent(pp->dev->dev.parent,
                                        txq->size * MVNETA_DESC_ALIGNED_SIZE,
@@ -2109,6 +2256,18 @@ static int mvneta_txq_init(struct mvneta_port *pp,
                                  txq->descs, txq->descs_phys);
                return -ENOMEM;
        }
+
+       /* Allocate DMA buffers for TSO MAC/IP/TCP headers */
+       txq->tso_hdrs = dma_alloc_coherent(pp->dev->dev.parent,
+                                          txq->size * TSO_HEADER_SIZE,
+                                          &txq->tso_hdrs_phys, GFP_KERNEL);
+       if (txq->tso_hdrs == NULL) {
+               kfree(txq->tx_skb);
+               dma_free_coherent(pp->dev->dev.parent,
+                                 txq->size * MVNETA_DESC_ALIGNED_SIZE,
+                                 txq->descs, txq->descs_phys);
+               return -ENOMEM;
+       }
        mvneta_tx_done_pkts_coal_set(pp, txq, txq->done_pkts_coal);
 
        return 0;
@@ -2120,6 +2279,10 @@ static void mvneta_txq_deinit(struct mvneta_port *pp,
 {
        kfree(txq->tx_skb);
 
+       if (txq->tso_hdrs)
+               dma_free_coherent(pp->dev->dev.parent,
+                                 txq->size * TSO_HEADER_SIZE,
+                                 txq->tso_hdrs, txq->tso_hdrs_phys);
        if (txq->descs)
                dma_free_coherent(pp->dev->dev.parent,
                                  txq->size * MVNETA_DESC_ALIGNED_SIZE,
@@ -2279,24 +2442,28 @@ static int mvneta_change_mtu(struct net_device *dev, int mtu)
                return 0;
 
        /* The interface is running, so we have to force a
-        * reallocation of the RXQs
+        * reallocation of the queues
         */
        mvneta_stop_dev(pp);
 
        mvneta_cleanup_txqs(pp);
        mvneta_cleanup_rxqs(pp);
 
-       pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu);
+       pp->pkt_size = MVNETA_RX_PKT_SIZE(dev->mtu);
        pp->frag_size = SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(pp->pkt_size)) +
                        SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
 
        ret = mvneta_setup_rxqs(pp);
        if (ret) {
-               netdev_err(pp->dev, "unable to setup rxqs after MTU change\n");
+               netdev_err(dev, "unable to setup rxqs after MTU change\n");
                return ret;
        }
 
-       mvneta_setup_txqs(pp);
+       ret = mvneta_setup_txqs(pp);
+       if (ret) {
+               netdev_err(dev, "unable to setup txqs after MTU change\n");
+               return ret;
+       }
 
        mvneta_start_dev(pp);
        mvneta_port_up(pp);
@@ -2323,22 +2490,19 @@ static void mvneta_get_mac_addr(struct mvneta_port *pp, unsigned char *addr)
 static int mvneta_set_mac_addr(struct net_device *dev, void *addr)
 {
        struct mvneta_port *pp = netdev_priv(dev);
-       u8 *mac = addr + 2;
-       int i;
-
-       if (netif_running(dev))
-               return -EBUSY;
+       struct sockaddr *sockaddr = addr;
+       int ret;
 
+       ret = eth_prepare_mac_addr_change(dev, addr);
+       if (ret < 0)
+               return ret;
        /* Remove previous address table entry */
        mvneta_mac_addr_set(pp, dev->dev_addr, -1);
 
        /* Set new addr in hw */
-       mvneta_mac_addr_set(pp, mac, rxq_def);
-
-       /* Set addr in the device */
-       for (i = 0; i < ETH_ALEN; i++)
-               dev->dev_addr[i] = mac[i];
+       mvneta_mac_addr_set(pp, sockaddr->sa_data, rxq_def);
 
+       eth_commit_mac_addr_change(dev, addr);
        return 0;
 }
 
@@ -2433,8 +2597,6 @@ static int mvneta_open(struct net_device *dev)
        struct mvneta_port *pp = netdev_priv(dev);
        int ret;
 
-       mvneta_mac_addr_set(pp, dev->dev_addr, rxq_def);
-
        pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu);
        pp->frag_size = SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(pp->pkt_size)) +
                        SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
@@ -2600,8 +2762,12 @@ static int mvneta_ethtool_set_ringparam(struct net_device *dev,
                return -EINVAL;
        pp->rx_ring_size = ring->rx_pending < MVNETA_MAX_RXD ?
                ring->rx_pending : MVNETA_MAX_RXD;
-       pp->tx_ring_size = ring->tx_pending < MVNETA_MAX_TXD ?
-               ring->tx_pending : MVNETA_MAX_TXD;
+
+       pp->tx_ring_size = clamp_t(u16, ring->tx_pending,
+                                  MVNETA_MAX_SKB_DESCS * 2, MVNETA_MAX_TXD);
+       if (pp->tx_ring_size != ring->tx_pending)
+               netdev_warn(dev, "TX queue size set to %u (requested %u)\n",
+                           pp->tx_ring_size, ring->tx_pending);
 
        if (netif_running(dev)) {
                mvneta_stop(dev);
@@ -2638,7 +2804,7 @@ const struct ethtool_ops mvneta_eth_tool_ops = {
 };
 
 /* Initialize hw */
-static int mvneta_init(struct mvneta_port *pp, int phy_addr)
+static int mvneta_init(struct device *dev, struct mvneta_port *pp)
 {
        int queue;
 
@@ -2648,8 +2814,8 @@ static int mvneta_init(struct mvneta_port *pp, int phy_addr)
        /* Set port default values */
        mvneta_defaults_set(pp);
 
-       pp->txqs = kzalloc(txq_number * sizeof(struct mvneta_tx_queue),
-                          GFP_KERNEL);
+       pp->txqs = devm_kcalloc(dev, txq_number, sizeof(struct mvneta_tx_queue),
+                               GFP_KERNEL);
        if (!pp->txqs)
                return -ENOMEM;
 
@@ -2661,12 +2827,10 @@ static int mvneta_init(struct mvneta_port *pp, int phy_addr)
                txq->done_pkts_coal = MVNETA_TXDONE_COAL_PKTS;
        }
 
-       pp->rxqs = kzalloc(rxq_number * sizeof(struct mvneta_rx_queue),
-                          GFP_KERNEL);
-       if (!pp->rxqs) {
-               kfree(pp->txqs);
+       pp->rxqs = devm_kcalloc(dev, rxq_number, sizeof(struct mvneta_rx_queue),
+                               GFP_KERNEL);
+       if (!pp->rxqs)
                return -ENOMEM;
-       }
 
        /* Create Rx descriptor rings */
        for (queue = 0; queue < rxq_number; queue++) {
@@ -2680,12 +2844,6 @@ static int mvneta_init(struct mvneta_port *pp, int phy_addr)
        return 0;
 }
 
-static void mvneta_deinit(struct mvneta_port *pp)
-{
-       kfree(pp->txqs);
-       kfree(pp->rxqs);
-}
-
 /* platform glue : initialize decoding windows */
 static void mvneta_conf_mbus_windows(struct mvneta_port *pp,
                                     const struct mbus_dram_target_info *dram)
@@ -2768,7 +2926,6 @@ static int mvneta_probe(struct platform_device *pdev)
        struct resource *res;
        struct device_node *dn = pdev->dev.of_node;
        struct device_node *phy_node;
-       u32 phy_addr;
        struct mvneta_port *pp;
        struct net_device *dev;
        const char *dt_mac_addr;
@@ -2797,9 +2954,22 @@ static int mvneta_probe(struct platform_device *pdev)
 
        phy_node = of_parse_phandle(dn, "phy", 0);
        if (!phy_node) {
-               dev_err(&pdev->dev, "no associated PHY\n");
-               err = -ENODEV;
-               goto err_free_irq;
+               if (!of_phy_is_fixed_link(dn)) {
+                       dev_err(&pdev->dev, "no PHY specified\n");
+                       err = -ENODEV;
+                       goto err_free_irq;
+               }
+
+               err = of_phy_register_fixed_link(dn);
+               if (err < 0) {
+                       dev_err(&pdev->dev, "cannot register fixed PHY\n");
+                       goto err_free_irq;
+               }
+
+               /* In the case of a fixed PHY, the DT node associated
+                * to the PHY is the Ethernet MAC DT node.
+                */
+               phy_node = dn;
        }
 
        phy_mode = of_get_phy_mode(dn);
@@ -2813,11 +2983,9 @@ static int mvneta_probe(struct platform_device *pdev)
        dev->watchdog_timeo = 5 * HZ;
        dev->netdev_ops = &mvneta_netdev_ops;
 
-       SET_ETHTOOL_OPS(dev, &mvneta_eth_tool_ops);
+       dev->ethtool_ops = &mvneta_eth_tool_ops;
 
        pp = netdev_priv(dev);
-
-       pp->weight = MVNETA_RX_POLL_WEIGHT;
        pp->phy_node = phy_node;
        pp->phy_interface = phy_mode;
 
@@ -2864,33 +3032,32 @@ static int mvneta_probe(struct platform_device *pdev)
        pp->dev = dev;
        SET_NETDEV_DEV(dev, &pdev->dev);
 
-       err = mvneta_init(pp, phy_addr);
-       if (err < 0) {
-               dev_err(&pdev->dev, "can't init eth hal\n");
+       err = mvneta_init(&pdev->dev, pp);
+       if (err < 0)
                goto err_free_stats;
-       }
 
        err = mvneta_port_power_up(pp, phy_mode);
        if (err < 0) {
                dev_err(&pdev->dev, "can't power up port\n");
-               goto err_deinit;
+               goto err_free_stats;
        }
 
        dram_target_info = mv_mbus_dram_info();
        if (dram_target_info)
                mvneta_conf_mbus_windows(pp, dram_target_info);
 
-       netif_napi_add(dev, &pp->napi, mvneta_poll, pp->weight);
+       netif_napi_add(dev, &pp->napi, mvneta_poll, NAPI_POLL_WEIGHT);
 
-       dev->features = NETIF_F_SG | NETIF_F_IP_CSUM;
-       dev->hw_features |= NETIF_F_SG | NETIF_F_IP_CSUM;
-       dev->vlan_features |= NETIF_F_SG | NETIF_F_IP_CSUM;
+       dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
+       dev->hw_features |= dev->features;
+       dev->vlan_features |= dev->features;
        dev->priv_flags |= IFF_UNICAST_FLT;
+       dev->gso_max_segs = MVNETA_MAX_TSO_SEGS;
 
        err = register_netdev(dev);
        if (err < 0) {
                dev_err(&pdev->dev, "failed to register\n");
-               goto err_deinit;
+               goto err_free_stats;
        }
 
        netdev_info(dev, "Using %s mac address %pM\n", mac_from,
@@ -2900,8 +3067,6 @@ static int mvneta_probe(struct platform_device *pdev)
 
        return 0;
 
-err_deinit:
-       mvneta_deinit(pp);
 err_free_stats:
        free_percpu(pp->stats);
 err_clk:
@@ -2920,7 +3085,6 @@ static int mvneta_remove(struct platform_device *pdev)
        struct mvneta_port *pp = netdev_priv(dev);
 
        unregister_netdev(dev);
-       mvneta_deinit(pp);
        clk_disable_unprepare(pp->clk);
        free_percpu(pp->stats);
        irq_dispose_mapping(dev->irq);
index b358c2f6f4bdc3817f98ab53c36bf8f8c7753ffd..8f5aa7c62b18f41f8eaa1b31e209687059921a47 100644 (file)
@@ -1488,7 +1488,7 @@ static int pxa168_eth_probe(struct platform_device *pdev)
        dev->netdev_ops = &pxa168_eth_netdev_ops;
        dev->watchdog_timeo = 2 * HZ;
        dev->base_addr = 0;
-       SET_ETHTOOL_OPS(dev, &pxa168_ethtool_ops);
+       dev->ethtool_ops = &pxa168_ethtool_ops;
 
        INIT_WORK(&pep->tx_timeout_task, pxa168_eth_tx_timeout_task);
 
index b81106451a0a4d2d46d831d9e629d3a24212aada..69693384b58ccfefd2bd9918112b8b08431f0368 100644 (file)
@@ -4760,7 +4760,7 @@ static struct net_device *sky2_init_netdev(struct sky2_hw *hw, unsigned port,
 
        SET_NETDEV_DEV(dev, &hw->pdev->dev);
        dev->irq = hw->pdev->irq;
-       SET_ETHTOOL_OPS(dev, &sky2_ethtool_ops);
+       dev->ethtool_ops = &sky2_ethtool_ops;
        dev->watchdog_timeo = TX_WATCHDOG;
        dev->netdev_ops = &sky2_netdev_ops[port];
 
index 29b616990e528b234c94d59cf88d7c74c5fd41de..5d940a26055c64ab71250f1d998b9b38418fb2ec 100644 (file)
@@ -212,8 +212,7 @@ static int mlx4_comm_cmd_poll(struct mlx4_dev *dev, u8 cmd, u16 param,
 
        /* First, verify that the master reports correct status */
        if (comm_pending(dev)) {
-               mlx4_warn(dev, "Communication channel is not idle."
-                         "my toggle is %d (cmd:0x%x)\n",
+               mlx4_warn(dev, "Communication channel is not idle - my toggle is %d (cmd:0x%x)\n",
                          priv->cmd.comm_toggle, cmd);
                return -EAGAIN;
        }
@@ -422,9 +421,8 @@ static int mlx4_slave_cmd(struct mlx4_dev *dev, u64 in_param, u64 *out_param,
                                        *out_param =
                                                be64_to_cpu(vhcr->out_param);
                                else {
-                                       mlx4_err(dev, "response expected while"
-                                                "output mailbox is NULL for "
-                                                "command 0x%x\n", op);
+                                       mlx4_err(dev, "response expected while output mailbox is NULL for command 0x%x\n",
+                                                op);
                                        vhcr->status = CMD_STAT_BAD_PARAM;
                                }
                        }
@@ -439,16 +437,15 @@ static int mlx4_slave_cmd(struct mlx4_dev *dev, u64 in_param, u64 *out_param,
                                        *out_param =
                                                be64_to_cpu(vhcr->out_param);
                                else {
-                                       mlx4_err(dev, "response expected while"
-                                                "output mailbox is NULL for "
-                                                "command 0x%x\n", op);
+                                       mlx4_err(dev, "response expected while output mailbox is NULL for command 0x%x\n",
+                                                op);
                                        vhcr->status = CMD_STAT_BAD_PARAM;
                                }
                        }
                        ret = mlx4_status_to_errno(vhcr->status);
                } else
-                       mlx4_err(dev, "failed execution of VHCR_POST command"
-                                "opcode 0x%x\n", op);
+                       mlx4_err(dev, "failed execution of VHCR_POST command opcode 0x%x\n",
+                                op);
        }
 
        mutex_unlock(&priv->cmd.slave_cmd_mutex);
@@ -476,6 +473,13 @@ static int mlx4_cmd_poll(struct mlx4_dev *dev, u64 in_param, u64 *out_param,
                goto out;
        }
 
+       if (out_is_imm && !out_param) {
+               mlx4_err(dev, "response expected while output mailbox is NULL for command 0x%x\n",
+                        op);
+               err = -EINVAL;
+               goto out;
+       }
+
        err = mlx4_cmd_post(dev, in_param, out_param ? *out_param : 0,
                            in_modifier, op_modifier, op, CMD_POLL_TOKEN, 0);
        if (err)
@@ -554,6 +558,13 @@ static int mlx4_cmd_wait(struct mlx4_dev *dev, u64 in_param, u64 *out_param,
        cmd->free_head = context->next;
        spin_unlock(&cmd->context_lock);
 
+       if (out_is_imm && !out_param) {
+               mlx4_err(dev, "response expected while output mailbox is NULL for command 0x%x\n",
+                        op);
+               err = -EINVAL;
+               goto out;
+       }
+
        init_completion(&context->done);
 
        mlx4_cmd_post(dev, in_param, out_param ? *out_param : 0,
@@ -625,9 +636,8 @@ static int mlx4_ACCESS_MEM(struct mlx4_dev *dev, u64 master_addr,
 
        if ((slave_addr & 0xfff) | (master_addr & 0xfff) |
            (slave & ~0x7f) | (size & 0xff)) {
-               mlx4_err(dev, "Bad access mem params - slave_addr:0x%llx "
-                             "master_addr:0x%llx slave_id:%d size:%d\n",
-                             slave_addr, master_addr, slave, size);
+               mlx4_err(dev, "Bad access mem params - slave_addr:0x%llx master_addr:0x%llx slave_id:%d size:%d\n",
+                        slave_addr, master_addr, slave, size);
                return -EINVAL;
        }
 
@@ -1422,8 +1432,8 @@ static int mlx4_master_process_vhcr(struct mlx4_dev *dev, int slave,
                                      ALIGN(sizeof(struct mlx4_vhcr_cmd),
                                            MLX4_ACCESS_MEM_ALIGN), 1);
                if (ret) {
-                       mlx4_err(dev, "%s:Failed reading vhcr"
-                                "ret: 0x%x\n", __func__, ret);
+                       mlx4_err(dev, "%s: Failed reading vhcr ret: 0x%x\n",
+                                __func__, ret);
                        kfree(vhcr);
                        return ret;
                }
@@ -1474,9 +1484,8 @@ static int mlx4_master_process_vhcr(struct mlx4_dev *dev, int slave,
 
        /* Apply permission and bound checks if applicable */
        if (cmd->verify && cmd->verify(dev, slave, vhcr, inbox)) {
-               mlx4_warn(dev, "Command:0x%x from slave: %d failed protection "
-                         "checks for resource_id:%d\n", vhcr->op, slave,
-                         vhcr->in_modifier);
+               mlx4_warn(dev, "Command:0x%x from slave: %d failed protection checks for resource_id:%d\n",
+                         vhcr->op, slave, vhcr->in_modifier);
                vhcr_cmd->status = CMD_STAT_BAD_OP;
                goto out_status;
        }
@@ -1515,8 +1524,7 @@ static int mlx4_master_process_vhcr(struct mlx4_dev *dev, int slave,
        }
 
        if (err) {
-               mlx4_warn(dev, "vhcr command:0x%x slave:%d failed with"
-                         " error:%d, status %d\n",
+               mlx4_warn(dev, "vhcr command:0x%x slave:%d failed with error:%d, status %d\n",
                          vhcr->op, slave, vhcr->errno, err);
                vhcr_cmd->status = mlx4_errno_to_status(err);
                goto out_status;
@@ -1550,8 +1558,8 @@ out_status:
                                 __func__);
                else if (vhcr->e_bit &&
                         mlx4_GEN_EQE(dev, slave, &priv->mfunc.master.cmd_eqe))
-                               mlx4_warn(dev, "Failed to generate command completion "
-                                         "eqe for slave %d\n", slave);
+                               mlx4_warn(dev, "Failed to generate command completion eqe for slave %d\n",
+                                         slave);
        }
 
 out:
@@ -1590,8 +1598,9 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv,
 
        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);
+       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)
@@ -1604,7 +1613,7 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv,
                                                   &admin_vlan_ix);
                        if (err) {
                                kfree(work);
-                               mlx4_warn((&priv->dev),
+                               mlx4_warn(&priv->dev,
                                          "No vlan resources slave %d, port %d\n",
                                          slave, port);
                                return err;
@@ -1613,7 +1622,7 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv,
                        admin_vlan_ix = NO_INDX;
                }
                work->flags |= MLX4_VF_IMMED_VLAN_FLAG_VLAN;
-               mlx4_dbg((&(priv->dev)),
+               mlx4_dbg(&priv->dev,
                         "alloc vlan %d idx  %d slave %d port %d\n",
                         (int)(vp_admin->default_vlan),
                         admin_vlan_ix, slave, port);
@@ -1676,12 +1685,12 @@ static int mlx4_master_activate_admin_state(struct mlx4_priv *priv, int slave)
                                                   vp_admin->default_vlan, &(vp_oper->vlan_idx));
                        if (err) {
                                vp_oper->vlan_idx = NO_INDX;
-                               mlx4_warn((&priv->dev),
+                               mlx4_warn(&priv->dev,
                                          "No vlan resorces slave %d, port %d\n",
                                          slave, port);
                                return err;
                        }
-                       mlx4_dbg((&(priv->dev)), "alloc vlan %d idx  %d slave %d port %d\n",
+                       mlx4_dbg(&priv->dev, "alloc vlan %d idx  %d slave %d port %d\n",
                                 (int)(vp_oper->state.default_vlan),
                                 vp_oper->vlan_idx, slave, port);
                }
@@ -1692,12 +1701,12 @@ static int mlx4_master_activate_admin_state(struct mlx4_priv *priv, int slave)
                        if (0 > vp_oper->mac_idx) {
                                err = vp_oper->mac_idx;
                                vp_oper->mac_idx = NO_INDX;
-                               mlx4_warn((&priv->dev),
+                               mlx4_warn(&priv->dev,
                                          "No mac resorces slave %d, port %d\n",
                                          slave, port);
                                return err;
                        }
-                       mlx4_dbg((&(priv->dev)), "alloc mac %llx idx  %d slave %d port %d\n",
+                       mlx4_dbg(&priv->dev, "alloc mac %llx idx  %d slave %d port %d\n",
                                 vp_oper->state.mac, vp_oper->mac_idx, slave, port);
                }
        }
@@ -1748,8 +1757,8 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd,
        slave_state[slave].comm_toggle ^= 1;
        reply = (u32) slave_state[slave].comm_toggle << 31;
        if (toggle != slave_state[slave].comm_toggle) {
-               mlx4_warn(dev, "Incorrect toggle %d from slave %d. *** MASTER"
-                         "STATE COMPROMISIED ***\n", toggle, slave);
+               mlx4_warn(dev, "Incorrect toggle %d from slave %d. *** MASTER STATE COMPROMISED ***\n",
+                         toggle, slave);
                goto reset_slave;
        }
        if (cmd == MLX4_COMM_CMD_RESET) {
@@ -1776,8 +1785,8 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd,
        /*command from slave in the middle of FLR*/
        if (cmd != MLX4_COMM_CMD_RESET &&
            MLX4_COMM_CMD_FLR == slave_state[slave].last_cmd) {
-               mlx4_warn(dev, "slave:%d is Trying to run cmd(0x%x) "
-                         "in the middle of FLR\n", slave, cmd);
+               mlx4_warn(dev, "slave:%d is Trying to run cmd(0x%x) in the middle of FLR\n",
+                         slave, cmd);
                return;
        }
 
@@ -1815,8 +1824,8 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd,
 
                mutex_lock(&priv->cmd.slave_cmd_mutex);
                if (mlx4_master_process_vhcr(dev, slave, NULL)) {
-                       mlx4_err(dev, "Failed processing vhcr for slave:%d,"
-                                " resetting slave.\n", slave);
+                       mlx4_err(dev, "Failed processing vhcr for slave:%d, resetting slave\n",
+                                slave);
                        mutex_unlock(&priv->cmd.slave_cmd_mutex);
                        goto reset_slave;
                }
@@ -1833,8 +1842,7 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd,
                is_going_down = 1;
        spin_unlock_irqrestore(&priv->mfunc.master.slave_state_lock, flags);
        if (is_going_down) {
-               mlx4_warn(dev, "Slave is going down aborting command(%d)"
-                         " executing from slave:%d\n",
+               mlx4_warn(dev, "Slave is going down aborting command(%d) executing from slave:%d\n",
                          cmd, slave);
                return;
        }
@@ -1897,10 +1905,9 @@ void mlx4_master_comm_channel(struct work_struct *work)
                        if (toggle != slt) {
                                if (master->slave_state[slave].comm_toggle
                                    != slt) {
-                                       printk(KERN_INFO "slave %d out of sync."
-                                              " read toggle %d, state toggle %d. "
-                                              "Resynching.\n", slave, slt,
-                                              master->slave_state[slave].comm_toggle);
+                                       pr_info("slave %d out of sync. read toggle %d, state toggle %d. Resynching.\n",
+                                               slave, slt,
+                                               master->slave_state[slave].comm_toggle);
                                        master->slave_state[slave].comm_toggle =
                                                slt;
                                }
@@ -1913,8 +1920,7 @@ void mlx4_master_comm_channel(struct work_struct *work)
        }
 
        if (reported && reported != served)
-               mlx4_warn(dev, "Got command event with bitmask from %d slaves"
-                         " but %d were served\n",
+               mlx4_warn(dev, "Got command event with bitmask from %d slaves but %d were served\n",
                          reported, served);
 
        if (mlx4_ARM_COMM_CHANNEL(dev))
@@ -1970,7 +1976,7 @@ int mlx4_multi_func_init(struct mlx4_dev *dev)
                ioremap(pci_resource_start(dev->pdev, 2) +
                        MLX4_SLAVE_COMM_BASE, MLX4_COMM_PAGESIZE);
        if (!priv->mfunc.comm) {
-               mlx4_err(dev, "Couldn't map communication vector.\n");
+               mlx4_err(dev, "Couldn't map communication vector\n");
                goto err_vhcr;
        }
 
@@ -2097,7 +2103,7 @@ int mlx4_cmd_init(struct mlx4_dev *dev)
                priv->cmd.hcr = ioremap(pci_resource_start(dev->pdev, 0) +
                                        MLX4_HCR_BASE, MLX4_HCR_SIZE);
                if (!priv->cmd.hcr) {
-                       mlx4_err(dev, "Couldn't map command register.\n");
+                       mlx4_err(dev, "Couldn't map command register\n");
                        return -ENOMEM;
                }
        }
@@ -2498,11 +2504,12 @@ int mlx4_get_vf_config(struct mlx4_dev *dev, int port, int vf, struct ifla_vf_in
        ivf->mac[4] = ((s_info->mac >> (1*8)) & 0xff);
        ivf->mac[5] = ((s_info->mac)  & 0xff);
 
-       ivf->vlan       = s_info->default_vlan;
-       ivf->qos        = s_info->default_qos;
-       ivf->tx_rate    = s_info->tx_rate;
-       ivf->spoofchk   = s_info->spoofchk;
-       ivf->linkstate  = s_info->link_state;
+       ivf->vlan               = s_info->default_vlan;
+       ivf->qos                = s_info->default_qos;
+       ivf->max_tx_rate        = s_info->tx_rate;
+       ivf->min_tx_rate        = 0;
+       ivf->spoofchk           = s_info->spoofchk;
+       ivf->linkstate          = s_info->link_state;
 
        return 0;
 }
index c90cde5b4aeee5cd91239a1cdea479490c388170..80f725228f5b7c8ab836f8bccd747fdc006782c6 100644 (file)
@@ -293,6 +293,9 @@ int mlx4_cq_alloc(struct mlx4_dev *dev, int nent,
        atomic_set(&cq->refcount, 1);
        init_completion(&cq->free);
 
+       cq->irq = priv->eq_table.eq[cq->vector].irq;
+       cq->irq_affinity_change = false;
+
        return 0;
 
 err_radix:
index c2cd8d31bcad5612395783e4d29e5141ac37ab6c..4b2130760eede3ad5f655cf08f2614d033c8ecb8 100644 (file)
@@ -125,8 +125,7 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
                                                   &cq->vector)) {
                                        cq->vector = (cq->ring + 1 + priv->port)
                                            % mdev->dev->caps.num_comp_vectors;
-                                       mlx4_warn(mdev, "Failed Assigning an EQ to "
-                                                 "%s ,Falling back to legacy EQ's\n",
+                                       mlx4_warn(mdev, "Failed assigning an EQ to %s, falling back to legacy EQ's\n",
                                                  name);
                                }
                        }
@@ -164,6 +163,13 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
                netif_napi_add(cq->dev, &cq->napi, mlx4_en_poll_tx_cq,
                               NAPI_POLL_WEIGHT);
        } else {
+               struct mlx4_en_rx_ring *ring = priv->rx_ring[cq->ring];
+
+               err = irq_set_affinity_hint(cq->mcq.irq,
+                                           ring->affinity_mask);
+               if (err)
+                       mlx4_warn(mdev, "Failed setting affinity hint\n");
+
                netif_napi_add(cq->dev, &cq->napi, mlx4_en_poll_rx_cq, 64);
                napi_hash_add(&cq->napi);
        }
@@ -180,8 +186,11 @@ void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq **pcq)
 
        mlx4_en_unmap_buffer(&cq->wqres.buf);
        mlx4_free_hwq_res(mdev->dev, &cq->wqres, cq->buf_size);
-       if (priv->mdev->dev->caps.comp_pool && cq->vector)
+       if (priv->mdev->dev->caps.comp_pool && cq->vector) {
+               if (!cq->is_tx)
+                       irq_set_affinity_hint(cq->mcq.irq, NULL);
                mlx4_release_eq(priv->mdev->dev, cq->vector);
+       }
        cq->vector = 0;
        cq->buf_size = 0;
        cq->buf = NULL;
index 3e8d33605fe7b17d7cde81092c92f132163abdc2..fa1a069e14e6f3ef21485172d612548034e00262 100644 (file)
@@ -378,8 +378,8 @@ static int mlx4_en_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
                ethtool_cmd_speed_set(cmd, priv->port_state.link_speed);
                cmd->duplex = DUPLEX_FULL;
        } else {
-               ethtool_cmd_speed_set(cmd, -1);
-               cmd->duplex = -1;
+               ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
+               cmd->duplex = DUPLEX_UNKNOWN;
        }
 
        if (trans_type > 0 && trans_type <= 0xC) {
@@ -564,7 +564,7 @@ static u32 mlx4_en_get_rxfh_indir_size(struct net_device *dev)
        return priv->rx_ring_num;
 }
 
-static int mlx4_en_get_rxfh_indir(struct net_device *dev, u32 *ring_index)
+static int mlx4_en_get_rxfh(struct net_device *dev, u32 *ring_index, u8 *key)
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_rss_map *rss_map = &priv->rss_map;
@@ -582,8 +582,8 @@ static int mlx4_en_get_rxfh_indir(struct net_device *dev, u32 *ring_index)
        return err;
 }
 
-static int mlx4_en_set_rxfh_indir(struct net_device *dev,
-               const u32 *ring_index)
+static int mlx4_en_set_rxfh(struct net_device *dev, const u32 *ring_index,
+                           const u8 *key)
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_dev *mdev = priv->mdev;
@@ -925,13 +925,13 @@ static int mlx4_en_flow_replace(struct net_device *dev,
                qpn = cmd->fs.ring_cookie & (EN_ETHTOOL_QP_ATTACH - 1);
        } else {
                if (cmd->fs.ring_cookie >= priv->rx_ring_num) {
-                       en_warn(priv, "rxnfc: RX ring (%llu) doesn't exist.\n",
+                       en_warn(priv, "rxnfc: RX ring (%llu) doesn't exist\n",
                                cmd->fs.ring_cookie);
                        return -EINVAL;
                }
                qpn = priv->rss_map.qps[cmd->fs.ring_cookie].qpn;
                if (!qpn) {
-                       en_warn(priv, "rxnfc: RX ring (%llu) is inactive.\n",
+                       en_warn(priv, "rxnfc: RX ring (%llu) is inactive\n",
                                cmd->fs.ring_cookie);
                        return -EINVAL;
                }
@@ -956,7 +956,7 @@ static int mlx4_en_flow_replace(struct net_device *dev,
        }
        err = mlx4_flow_attach(priv->mdev->dev, &rule, &reg_id);
        if (err) {
-               en_err(priv, "Fail to attach network rule at location %d.\n",
+               en_err(priv, "Fail to attach network rule at location %d\n",
                       cmd->fs.location);
                goto out_free_list;
        }
@@ -1121,7 +1121,7 @@ static int mlx4_en_set_channels(struct net_device *dev,
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_dev *mdev = priv->mdev;
-       int port_up;
+       int port_up = 0;
        int err = 0;
 
        if (channel->other_count || channel->combined_count ||
@@ -1151,7 +1151,8 @@ static int mlx4_en_set_channels(struct net_device *dev,
        netif_set_real_num_tx_queues(dev, priv->tx_ring_num);
        netif_set_real_num_rx_queues(dev, priv->rx_ring_num);
 
-       mlx4_en_setup_tc(dev, MLX4_EN_NUM_UP);
+       if (dev->num_tc)
+               mlx4_en_setup_tc(dev, MLX4_EN_NUM_UP);
 
        en_warn(priv, "Using %d TX rings\n", priv->tx_ring_num);
        en_warn(priv, "Using %d RX rings\n", priv->rx_ring_num);
@@ -1223,8 +1224,8 @@ const struct ethtool_ops mlx4_en_ethtool_ops = {
        .get_rxnfc = mlx4_en_get_rxnfc,
        .set_rxnfc = mlx4_en_set_rxnfc,
        .get_rxfh_indir_size = mlx4_en_get_rxfh_indir_size,
-       .get_rxfh_indir = mlx4_en_get_rxfh_indir,
-       .set_rxfh_indir = mlx4_en_set_rxfh_indir,
+       .get_rxfh = mlx4_en_get_rxfh,
+       .set_rxfh = mlx4_en_set_rxfh,
        .get_channels = mlx4_en_get_channels,
        .set_channels = mlx4_en_set_channels,
        .get_ts_info = mlx4_en_get_ts_info,
index 0c59d4fe7e3aae56afee09b7e279601192c9e26e..f953c1d7eae6a700a4fb7aacfacbaaa822c141d1 100644 (file)
@@ -133,7 +133,7 @@ static int mlx4_en_get_profile(struct mlx4_en_dev *mdev)
                        MLX4_EN_MAX_TX_RING_P_UP);
        if (params->udp_rss && !(mdev->dev->caps.flags
                                        & MLX4_DEV_CAP_FLAG_UDP_RSS)) {
-               mlx4_warn(mdev, "UDP RSS is not supported on this device.\n");
+               mlx4_warn(mdev, "UDP RSS is not supported on this device\n");
                params->udp_rss = 0;
        }
        for (i = 1; i <= MLX4_MAX_PORTS; i++) {
@@ -251,8 +251,7 @@ static void *mlx4_en_add(struct mlx4_dev *dev)
 
        mdev->LSO_support = !!(dev->caps.flags & (1 << 15));
        if (!mdev->LSO_support)
-               mlx4_warn(mdev, "LSO not supported, please upgrade to later "
-                               "FW version to enable LSO\n");
+               mlx4_warn(mdev, "LSO not supported, please upgrade to later FW version to enable LSO\n");
 
        if (mlx4_mr_alloc(mdev->dev, mdev->priv_pdn, 0, ~0ull,
                         MLX4_PERM_LOCAL_WRITE |  MLX4_PERM_LOCAL_READ,
@@ -268,7 +267,7 @@ static void *mlx4_en_add(struct mlx4_dev *dev)
        /* Build device profile according to supplied module parameters */
        err = mlx4_en_get_profile(mdev);
        if (err) {
-               mlx4_err(mdev, "Bad module parameters, aborting.\n");
+               mlx4_err(mdev, "Bad module parameters, aborting\n");
                goto err_mr;
        }
 
index 7e4b1720c3d1bec183957beeba7d395ce38c6e34..7d4fb7bf25933ddcddecebf0a551bbe4cfa6328a 100644 (file)
@@ -130,7 +130,7 @@ static enum mlx4_net_trans_rule_id mlx4_ip_proto_to_trans_rule_id(u8 ip_proto)
        case IPPROTO_TCP:
                return MLX4_NET_TRANS_RULE_ID_TCP;
        default:
-               return -EPROTONOSUPPORT;
+               return MLX4_NET_TRANS_RULE_NUM;
        }
 };
 
@@ -177,7 +177,7 @@ static void mlx4_en_filter_work(struct work_struct *work)
        int rc;
        __be64 mac_mask = cpu_to_be64(MLX4_MAC_MASK << 16);
 
-       if (spec_tcp_udp.id < 0) {
+       if (spec_tcp_udp.id >= MLX4_NET_TRANS_RULE_NUM) {
                en_warn(priv, "RFS: ignoring unsupported ip protocol (%d)\n",
                        filter->ip_proto);
                goto ignore;
@@ -770,11 +770,12 @@ static int mlx4_en_do_set_mac(struct mlx4_en_priv *priv)
                                          priv->dev->dev_addr, priv->prev_mac);
                if (err)
                        en_err(priv, "Failed changing HW MAC address\n");
-               memcpy(priv->prev_mac, priv->dev->dev_addr,
-                      sizeof(priv->prev_mac));
        } else
                en_dbg(HW, priv, "Port is down while registering mac, exiting...\n");
 
+       memcpy(priv->prev_mac, priv->dev->dev_addr,
+              sizeof(priv->prev_mac));
+
        return err;
 }
 
@@ -788,9 +789,8 @@ static int mlx4_en_set_mac(struct net_device *dev, void *addr)
        if (!is_valid_ether_addr(saddr->sa_data))
                return -EADDRNOTAVAIL;
 
-       memcpy(dev->dev_addr, saddr->sa_data, ETH_ALEN);
-
        mutex_lock(&mdev->state_lock);
+       memcpy(dev->dev_addr, saddr->sa_data, ETH_ALEN);
        err = mlx4_en_do_set_mac(priv);
        mutex_unlock(&mdev->state_lock);
 
@@ -1526,6 +1526,27 @@ static void mlx4_en_linkstate(struct work_struct *work)
        mutex_unlock(&mdev->state_lock);
 }
 
+static int mlx4_en_init_affinity_hint(struct mlx4_en_priv *priv, int ring_idx)
+{
+       struct mlx4_en_rx_ring *ring = priv->rx_ring[ring_idx];
+       int numa_node = priv->mdev->dev->numa_node;
+       int ret = 0;
+
+       if (!zalloc_cpumask_var(&ring->affinity_mask, GFP_KERNEL))
+               return -ENOMEM;
+
+       ret = cpumask_set_cpu_local_first(ring_idx, numa_node,
+                                         ring->affinity_mask);
+       if (ret)
+               free_cpumask_var(ring->affinity_mask);
+
+       return ret;
+}
+
+static void mlx4_en_free_affinity_hint(struct mlx4_en_priv *priv, int ring_idx)
+{
+       free_cpumask_var(priv->rx_ring[ring_idx]->affinity_mask);
+}
 
 int mlx4_en_start_port(struct net_device *dev)
 {
@@ -1567,17 +1588,25 @@ int mlx4_en_start_port(struct net_device *dev)
 
                mlx4_en_cq_init_lock(cq);
 
+               err = mlx4_en_init_affinity_hint(priv, i);
+               if (err) {
+                       en_err(priv, "Failed preparing IRQ affinity hint\n");
+                       goto cq_err;
+               }
+
                err = mlx4_en_activate_cq(priv, cq, i);
                if (err) {
                        en_err(priv, "Failed activating Rx CQ\n");
+                       mlx4_en_free_affinity_hint(priv, i);
                        goto cq_err;
                }
                for (j = 0; j < cq->size; j++)
                        cq->buf[j].owner_sr_opcode = MLX4_CQE_OWNER_MASK;
                err = mlx4_en_set_cq_moder(priv, cq);
                if (err) {
-                       en_err(priv, "Failed setting cq moderation parameters");
+                       en_err(priv, "Failed setting cq moderation parameters\n");
                        mlx4_en_deactivate_cq(priv, cq);
+                       mlx4_en_free_affinity_hint(priv, i);
                        goto cq_err;
                }
                mlx4_en_arm_cq(priv, cq);
@@ -1615,7 +1644,7 @@ int mlx4_en_start_port(struct net_device *dev)
                }
                err = mlx4_en_set_cq_moder(priv, cq);
                if (err) {
-                       en_err(priv, "Failed setting cq moderation parameters");
+                       en_err(priv, "Failed setting cq moderation parameters\n");
                        mlx4_en_deactivate_cq(priv, cq);
                        goto tx_err;
                }
@@ -1715,8 +1744,10 @@ rss_err:
 mac_err:
        mlx4_en_put_qp(priv);
 cq_err:
-       while (rx_index--)
+       while (rx_index--) {
                mlx4_en_deactivate_cq(priv, priv->rx_cq[rx_index]);
+               mlx4_en_free_affinity_hint(priv, i);
+       }
        for (i = 0; i < priv->rx_ring_num; i++)
                mlx4_en_deactivate_rx_ring(priv, priv->rx_ring[i]);
 
@@ -1847,6 +1878,8 @@ void mlx4_en_stop_port(struct net_device *dev, int detach)
                        msleep(1);
                mlx4_en_deactivate_rx_ring(priv, priv->rx_ring[i]);
                mlx4_en_deactivate_cq(priv, cq);
+
+               mlx4_en_free_affinity_hint(priv, i);
        }
 }
 
@@ -2539,7 +2572,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
        netif_set_real_num_tx_queues(dev, priv->tx_ring_num);
        netif_set_real_num_rx_queues(dev, priv->rx_ring_num);
 
-       SET_ETHTOOL_OPS(dev, &mlx4_en_ethtool_ops);
+       dev->ethtool_ops = &mlx4_en_ethtool_ops;
 
        /*
         * Set driver features
@@ -2594,8 +2627,8 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
                                    prof->tx_pause, prof->tx_ppp,
                                    prof->rx_pause, prof->rx_ppp);
        if (err) {
-               en_err(priv, "Failed setting port general configurations "
-                      "for port %d, with error %d\n", priv->port, err);
+               en_err(priv, "Failed setting port general configurations for port %d, with error %d\n",
+                      priv->port, err);
                goto out;
        }
 
index 87857a6463ebb286f0110d7fc7a831d25743d27b..d2d415732d994178117eafb39cc6d8207ce5322e 100644 (file)
@@ -270,13 +270,11 @@ static int mlx4_en_fill_rx_buffers(struct mlx4_en_priv *priv)
                                                    ring->actual_size,
                                                    GFP_KERNEL)) {
                                if (ring->actual_size < MLX4_EN_MIN_RX_SIZE) {
-                                       en_err(priv, "Failed to allocate "
-                                                    "enough rx buffers\n");
+                                       en_err(priv, "Failed to allocate enough rx buffers\n");
                                        return -ENOMEM;
                                } else {
                                        new_size = rounddown_pow_of_two(ring->actual_size);
-                                       en_warn(priv, "Only %d buffers allocated "
-                                                     "reducing ring size to %d",
+                                       en_warn(priv, "Only %d buffers allocated reducing ring size to %d\n",
                                                ring->actual_size, new_size);
                                        goto reduce_rings;
                                }
@@ -685,10 +683,9 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
                /* Drop packet on bad receive or bad checksum */
                if (unlikely((cqe->owner_sr_opcode & MLX4_CQE_OPCODE_MASK) ==
                                                MLX4_CQE_OPCODE_ERROR)) {
-                       en_err(priv, "CQE completed in error - vendor "
-                                 "syndrom:%d syndrom:%d\n",
-                                 ((struct mlx4_err_cqe *) cqe)->vendor_err_syndrome,
-                                 ((struct mlx4_err_cqe *) cqe)->syndrome);
+                       en_err(priv, "CQE completed in error - vendor syndrom:%d syndrom:%d\n",
+                              ((struct mlx4_err_cqe *)cqe)->vendor_err_syndrome,
+                              ((struct mlx4_err_cqe *)cqe)->syndrome);
                        goto next;
                }
                if (unlikely(cqe->badfcs_enc & MLX4_CQE_BAD_FCS)) {
@@ -898,10 +895,17 @@ int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget)
        mlx4_en_cq_unlock_napi(cq);
 
        /* If we used up all the quota - we're probably not done yet... */
-       if (done == budget)
+       if (done == budget) {
                INC_PERF_COUNTER(priv->pstats.napi_quota);
-       else {
+               if (unlikely(cq->mcq.irq_affinity_change)) {
+                       cq->mcq.irq_affinity_change = false;
+                       napi_complete(napi);
+                       mlx4_en_arm_cq(priv, cq);
+                       return 0;
+               }
+       } else {
                /* Done for now */
+               cq->mcq.irq_affinity_change = false;
                napi_complete(napi);
                mlx4_en_arm_cq(priv, cq);
        }
@@ -944,8 +948,8 @@ void mlx4_en_calc_rx_buf(struct net_device *dev)
        priv->rx_skb_size = eff_mtu;
        priv->log_rx_info = ROUNDUP_LOG2(i * sizeof(struct mlx4_en_rx_alloc));
 
-       en_dbg(DRV, priv, "Rx buffer scatter-list (effective-mtu:%d "
-                 "num_frags:%d):\n", eff_mtu, priv->num_frags);
+       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_err(priv,
                       "  frag:%d - size:%d prefix:%d align:%d stride:%d\n",
index bc0cc1eb214daf0029bae922f513751be071b0bb..8be7483f82368c7733e4251019672d0cd227be08 100644 (file)
@@ -108,9 +108,9 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv,
 
        ring->buf = ring->wqres.buf.direct.buf;
 
-       en_dbg(DRV, priv, "Allocated TX ring (addr:%p) - buf:%p size:%d "
-              "buf_size:%d dma:%llx\n", ring, ring->buf, ring->size,
-              ring->buf_size, (unsigned long long) ring->wqres.buf.direct.map);
+       en_dbg(DRV, priv, "Allocated TX ring (addr:%p) - buf:%p size:%d buf_size:%d dma:%llx\n",
+              ring, ring->buf, ring->size, ring->buf_size,
+              (unsigned long long) ring->wqres.buf.direct.map);
 
        ring->qpn = qpn;
        err = mlx4_qp_alloc(mdev->dev, ring->qpn, &ring->qp, GFP_KERNEL);
@@ -122,7 +122,7 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv,
 
        err = mlx4_bf_alloc(mdev->dev, &ring->bf, node);
        if (err) {
-               en_dbg(DRV, priv, "working without blueflame (%d)", err);
+               en_dbg(DRV, priv, "working without blueflame (%d)\n", err);
                ring->bf.uar = &mdev->priv_uar;
                ring->bf.uar->map = mdev->uar_map;
                ring->bf_enabled = false;
@@ -474,9 +474,15 @@ int mlx4_en_poll_tx_cq(struct napi_struct *napi, int budget)
        /* If we used up all the quota - we're probably not done yet... */
        if (done < budget) {
                /* Done for now */
+               cq->mcq.irq_affinity_change = false;
                napi_complete(napi);
                mlx4_en_arm_cq(priv, cq);
                return done;
+       } else if (unlikely(cq->mcq.irq_affinity_change)) {
+               cq->mcq.irq_affinity_change = false;
+               napi_complete(napi);
+               mlx4_en_arm_cq(priv, cq);
+               return 0;
        }
        return budget;
 }
index d501a2b0fb79f18e560fd0cd067aa4c19b83b447..d954ec1eac173752e23e57653ccd4d2cae2de944 100644 (file)
@@ -53,6 +53,11 @@ enum {
        MLX4_EQ_ENTRY_SIZE      = 0x20
 };
 
+struct mlx4_irq_notify {
+       void *arg;
+       struct irq_affinity_notify notify;
+};
+
 #define MLX4_EQ_STATUS_OK         ( 0 << 28)
 #define MLX4_EQ_STATUS_WRITE_FAIL  (10 << 28)
 #define MLX4_EQ_OWNER_SW          ( 0 << 24)
@@ -152,14 +157,13 @@ void mlx4_gen_slave_eqe(struct work_struct *work)
                                if (i != dev->caps.function &&
                                    master->slave_state[i].active)
                                        if (mlx4_GEN_EQE(dev, i, eqe))
-                                               mlx4_warn(dev, "Failed to "
-                                                         " generate event "
-                                                         "for slave %d\n", i);
+                                               mlx4_warn(dev, "Failed to generate event for slave %d\n",
+                                                         i);
                        }
                } else {
                        if (mlx4_GEN_EQE(dev, slave, eqe))
-                               mlx4_warn(dev, "Failed to generate event "
-                                              "for slave %d\n", slave);
+                               mlx4_warn(dev, "Failed to generate event for slave %d\n",
+                                         slave);
                }
                ++slave_eq->cons;
        }
@@ -177,8 +181,8 @@ static void slave_event(struct mlx4_dev *dev, u8 slave, struct mlx4_eqe *eqe)
        s_eqe = &slave_eq->event_eqe[slave_eq->prod & (SLAVE_EVENT_EQ_SIZE - 1)];
        if ((!!(s_eqe->owner & 0x80)) ^
            (!!(slave_eq->prod & SLAVE_EVENT_EQ_SIZE))) {
-               mlx4_warn(dev, "Master failed to generate an EQE for slave: %d. "
-                         "No free EQE on slave events queue\n", slave);
+               mlx4_warn(dev, "Master failed to generate an EQE for slave: %d. No free EQE on slave events queue\n",
+                         slave);
                spin_unlock_irqrestore(&slave_eq->event_lock, flags);
                return;
        }
@@ -375,9 +379,9 @@ int set_and_calc_slave_port_state(struct mlx4_dev *dev, int slave,
                }
                break;
        default:
-               pr_err("%s: BUG!!! UNKNOWN state: "
-                      "slave:%d, port:%d\n", __func__, slave, port);
-                       goto out;
+               pr_err("%s: BUG!!! UNKNOWN state: slave:%d, port:%d\n",
+                      __func__, slave, port);
+               goto out;
        }
        ret = mlx4_get_slave_port_state(dev, slave, port);
 
@@ -425,8 +429,8 @@ void mlx4_master_handle_slave_flr(struct work_struct *work)
        for (i = 0 ; i < dev->num_slaves; i++) {
 
                if (MLX4_COMM_CMD_FLR == slave_state[i].last_cmd) {
-                       mlx4_dbg(dev, "mlx4_handle_slave_flr: "
-                                "clean slave: %d\n", i);
+                       mlx4_dbg(dev, "mlx4_handle_slave_flr: clean slave: %d\n",
+                                i);
 
                        mlx4_delete_all_resources_for_slave(dev, i);
                        /*return the slave to running mode*/
@@ -438,8 +442,8 @@ void mlx4_master_handle_slave_flr(struct work_struct *work)
                        err = mlx4_cmd(dev, 0, i, 0, MLX4_CMD_INFORM_FLR_DONE,
                                       MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED);
                        if (err)
-                               mlx4_warn(dev, "Failed to notify FW on "
-                                         "FLR done (slave:%d)\n", i);
+                               mlx4_warn(dev, "Failed to notify FW on FLR done (slave:%d)\n",
+                                         i);
                }
        }
 }
@@ -490,9 +494,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
                                                be32_to_cpu(eqe->event.qp.qpn)
                                                & 0xffffff, &slave);
                                if (ret && ret != -ENOENT) {
-                                       mlx4_dbg(dev, "QP event %02x(%02x) on "
-                                                "EQ %d at index %u: could "
-                                                "not get slave id (%d)\n",
+                                       mlx4_dbg(dev, "QP event %02x(%02x) on EQ %d at index %u: could not get slave id (%d)\n",
                                                 eqe->type, eqe->subtype,
                                                 eq->eqn, eq->cons_index, ret);
                                        break;
@@ -520,23 +522,19 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
                                                & 0xffffff,
                                                &slave);
                                if (ret && ret != -ENOENT) {
-                                       mlx4_warn(dev, "SRQ event %02x(%02x) "
-                                                 "on EQ %d at index %u: could"
-                                                 " not get slave id (%d)\n",
+                                       mlx4_warn(dev, "SRQ event %02x(%02x) on EQ %d at index %u: could not get slave id (%d)\n",
                                                  eqe->type, eqe->subtype,
                                                  eq->eqn, eq->cons_index, ret);
                                        break;
                                }
-                               mlx4_warn(dev, "%s: slave:%d, srq_no:0x%x,"
-                                         " event: %02x(%02x)\n", __func__,
-                                         slave,
+                               mlx4_warn(dev, "%s: slave:%d, srq_no:0x%x, event: %02x(%02x)\n",
+                                         __func__, slave,
                                          be32_to_cpu(eqe->event.srq.srqn),
                                          eqe->type, eqe->subtype);
 
                                if (!ret && slave != dev->caps.function) {
-                                       mlx4_warn(dev, "%s: sending event "
-                                                 "%02x(%02x) to slave:%d\n",
-                                                  __func__, eqe->type,
+                                       mlx4_warn(dev, "%s: sending event %02x(%02x) to slave:%d\n",
+                                                 __func__, eqe->type,
                                                  eqe->subtype, slave);
                                        mlx4_slave_event(dev, slave, eqe);
                                        break;
@@ -569,8 +567,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
                                        if (dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH) {
                                                if (i == mlx4_master_func_num(dev))
                                                        continue;
-                                               mlx4_dbg(dev, "%s: Sending MLX4_PORT_CHANGE_SUBTYPE_DOWN"
-                                                        " to slave: %d, port:%d\n",
+                                               mlx4_dbg(dev, "%s: Sending MLX4_PORT_CHANGE_SUBTYPE_DOWN to slave: %d, port:%d\n",
                                                         __func__, i, port);
                                                s_info = &priv->mfunc.master.vf_oper[slave].vport[port].state;
                                                if (IFLA_VF_LINK_STATE_AUTO == s_info->link_state) {
@@ -634,11 +631,9 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
                                        be32_to_cpu(eqe->event.cq_err.cqn)
                                        & 0xffffff, &slave);
                                if (ret && ret != -ENOENT) {
-                                       mlx4_dbg(dev, "CQ event %02x(%02x) on "
-                                                "EQ %d at index %u: could "
-                                                 "not get slave id (%d)\n",
-                                                 eqe->type, eqe->subtype,
-                                                 eq->eqn, eq->cons_index, ret);
+                                       mlx4_dbg(dev, "CQ event %02x(%02x) on EQ %d at index %u: could not get slave id (%d)\n",
+                                                eqe->type, eqe->subtype,
+                                                eq->eqn, eq->cons_index, ret);
                                        break;
                                }
 
@@ -667,8 +662,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
 
                case MLX4_EVENT_TYPE_COMM_CHANNEL:
                        if (!mlx4_is_master(dev)) {
-                               mlx4_warn(dev, "Received comm channel event "
-                                              "for non master device\n");
+                               mlx4_warn(dev, "Received comm channel event for non master device\n");
                                break;
                        }
                        memcpy(&priv->mfunc.master.comm_arm_bit_vector,
@@ -681,8 +675,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
                case MLX4_EVENT_TYPE_FLR_EVENT:
                        flr_slave = be32_to_cpu(eqe->event.flr_event.slave_id);
                        if (!mlx4_is_master(dev)) {
-                               mlx4_warn(dev, "Non-master function received"
-                                              "FLR event\n");
+                               mlx4_warn(dev, "Non-master function received FLR event\n");
                                break;
                        }
 
@@ -711,22 +704,17 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
                        if (eqe->subtype == MLX4_FATAL_WARNING_SUBTYPE_WARMING) {
                                if (mlx4_is_master(dev))
                                        for (i = 0; i < dev->num_slaves; i++) {
-                                               mlx4_dbg(dev, "%s: Sending "
-                                                       "MLX4_FATAL_WARNING_SUBTYPE_WARMING"
-                                                       " to slave: %d\n", __func__, i);
+                                               mlx4_dbg(dev, "%s: Sending MLX4_FATAL_WARNING_SUBTYPE_WARMING to slave: %d\n",
+                                                        __func__, i);
                                                if (i == dev->caps.function)
                                                        continue;
                                                mlx4_slave_event(dev, i, eqe);
                                        }
-                               mlx4_err(dev, "Temperature Threshold was reached! "
-                                       "Threshold: %d celsius degrees; "
-                                       "Current Temperature: %d\n",
-                                       be16_to_cpu(eqe->event.warming.warning_threshold),
-                                       be16_to_cpu(eqe->event.warming.current_temperature));
+                               mlx4_err(dev, "Temperature Threshold was reached! Threshold: %d celsius degrees; Current Temperature: %d\n",
+                                        be16_to_cpu(eqe->event.warming.warning_threshold),
+                                        be16_to_cpu(eqe->event.warming.current_temperature));
                        } else
-                               mlx4_warn(dev, "Unhandled event FATAL WARNING (%02x), "
-                                         "subtype %02x on EQ %d at index %u. owner=%x, "
-                                         "nent=0x%x, slave=%x, ownership=%s\n",
+                               mlx4_warn(dev, "Unhandled event FATAL WARNING (%02x), subtype %02x on EQ %d at index %u. owner=%x, nent=0x%x, slave=%x, ownership=%s\n",
                                          eqe->type, eqe->subtype, eq->eqn,
                                          eq->cons_index, eqe->owner, eq->nent,
                                          eqe->slave_id,
@@ -743,9 +731,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
                case MLX4_EVENT_TYPE_EEC_CATAS_ERROR:
                case MLX4_EVENT_TYPE_ECC_DETECT:
                default:
-                       mlx4_warn(dev, "Unhandled event %02x(%02x) on EQ %d at "
-                                 "index %u. owner=%x, nent=0x%x, slave=%x, "
-                                 "ownership=%s\n",
+                       mlx4_warn(dev, "Unhandled event %02x(%02x) on EQ %d at index %u. owner=%x, nent=0x%x, slave=%x, ownership=%s\n",
                                  eqe->type, eqe->subtype, eq->eqn,
                                  eq->cons_index, eqe->owner, eq->nent,
                                  eqe->slave_id,
@@ -1088,7 +1074,7 @@ static int mlx4_map_clr_int(struct mlx4_dev *dev)
        priv->clr_base = ioremap(pci_resource_start(dev->pdev, priv->fw.clr_int_bar) +
                                 priv->fw.clr_int_base, MLX4_CLR_INT_SIZE);
        if (!priv->clr_base) {
-               mlx4_err(dev, "Couldn't map interrupt clear register, aborting.\n");
+               mlx4_err(dev, "Couldn't map interrupt clear register, aborting\n");
                return -ENOMEM;
        }
 
@@ -1102,6 +1088,57 @@ static void mlx4_unmap_clr_int(struct mlx4_dev *dev)
        iounmap(priv->clr_base);
 }
 
+static void mlx4_irq_notifier_notify(struct irq_affinity_notify *notify,
+                                    const cpumask_t *mask)
+{
+       struct mlx4_irq_notify *n = container_of(notify,
+                                                struct mlx4_irq_notify,
+                                                notify);
+       struct mlx4_priv *priv = (struct mlx4_priv *)n->arg;
+       struct radix_tree_iter iter;
+       void **slot;
+
+       radix_tree_for_each_slot(slot, &priv->cq_table.tree, &iter, 0) {
+               struct mlx4_cq *cq = (struct mlx4_cq *)(*slot);
+
+               if (cq->irq == notify->irq)
+                       cq->irq_affinity_change = true;
+       }
+}
+
+static void mlx4_release_irq_notifier(struct kref *ref)
+{
+       struct mlx4_irq_notify *n = container_of(ref, struct mlx4_irq_notify,
+                                                notify.kref);
+       kfree(n);
+}
+
+static void mlx4_assign_irq_notifier(struct mlx4_priv *priv,
+                                    struct mlx4_dev *dev, int irq)
+{
+       struct mlx4_irq_notify *irq_notifier = NULL;
+       int err = 0;
+
+       irq_notifier = kzalloc(sizeof(*irq_notifier), GFP_KERNEL);
+       if (!irq_notifier) {
+               mlx4_warn(dev, "Failed to allocate irq notifier. irq %d\n",
+                         irq);
+               return;
+       }
+
+       irq_notifier->notify.irq = irq;
+       irq_notifier->notify.notify = mlx4_irq_notifier_notify;
+       irq_notifier->notify.release = mlx4_release_irq_notifier;
+       irq_notifier->arg = priv;
+       err = irq_set_affinity_notifier(irq, &irq_notifier->notify);
+       if (err) {
+               kfree(irq_notifier);
+               irq_notifier = NULL;
+               mlx4_warn(dev, "Failed to set irq notifier. irq %d\n", irq);
+       }
+}
+
+
 int mlx4_alloc_eq_table(struct mlx4_dev *dev)
 {
        struct mlx4_priv *priv = mlx4_priv(dev);
@@ -1372,6 +1409,9 @@ int mlx4_assign_eq(struct mlx4_dev *dev, char *name, struct cpu_rmap *rmap,
                                continue;
                                /*we dont want to break here*/
                        }
+                       mlx4_assign_irq_notifier(priv, dev,
+                                                priv->eq_table.eq[vec].irq);
+
                        eq_set_ci(&priv->eq_table.eq[vec], 1);
                }
        }
@@ -1398,6 +1438,9 @@ void mlx4_release_eq(struct mlx4_dev *dev, int vec)
                  Belonging to a legacy EQ*/
                mutex_lock(&priv->msix_ctl.pool_lock);
                if (priv->msix_ctl.pool_bm & 1ULL << i) {
+                       irq_set_affinity_notifier(
+                               priv->eq_table.eq[vec].irq,
+                               NULL);
                        free_irq(priv->eq_table.eq[vec].irq,
                                 &priv->eq_table.eq[vec]);
                        priv->msix_ctl.pool_bm &= ~(1ULL << i);
index 01e6dd61ee3c0ea6e3356c18a29227d98ca83582..688e1eabab29d697137477ea4a05b1dbb46a6d1f 100644 (file)
@@ -437,8 +437,7 @@ int mlx4_QUERY_FUNC_CAP(struct mlx4_dev *dev, u32 gen_or_port,
        } else if (dev->caps.port_type[gen_or_port] == MLX4_PORT_TYPE_IB) {
                MLX4_GET(field, outbox, QUERY_FUNC_CAP_FLAGS0_OFFSET);
                if (field & QUERY_FUNC_CAP_FLAGS0_FORCE_PHY_WQE_GID) {
-                       mlx4_err(dev, "phy_wqe_gid is "
-                                "enforced on this ib port\n");
+                       mlx4_err(dev, "phy_wqe_gid is enforced on this ib port\n");
                        err = -EPROTONOSUPPORT;
                        goto out;
                }
@@ -1070,10 +1069,10 @@ int mlx4_map_cmd(struct mlx4_dev *dev, u16 op, struct mlx4_icm *icm, u64 virt)
                 */
                lg = ffs(mlx4_icm_addr(&iter) | mlx4_icm_size(&iter)) - 1;
                if (lg < MLX4_ICM_PAGE_SHIFT) {
-                       mlx4_warn(dev, "Got FW area not aligned to %d (%llx/%lx).\n",
-                                  MLX4_ICM_PAGE_SIZE,
-                                  (unsigned long long) mlx4_icm_addr(&iter),
-                                  mlx4_icm_size(&iter));
+                       mlx4_warn(dev, "Got FW area not aligned to %d (%llx/%lx)\n",
+                                 MLX4_ICM_PAGE_SIZE,
+                                 (unsigned long long) mlx4_icm_addr(&iter),
+                                 mlx4_icm_size(&iter));
                        err = -EINVAL;
                        goto out;
                }
@@ -1109,14 +1108,14 @@ int mlx4_map_cmd(struct mlx4_dev *dev, u16 op, struct mlx4_icm *icm, u64 virt)
 
        switch (op) {
        case MLX4_CMD_MAP_FA:
-               mlx4_dbg(dev, "Mapped %d chunks/%d KB for FW.\n", tc, ts);
+               mlx4_dbg(dev, "Mapped %d chunks/%d KB for FW\n", tc, ts);
                break;
        case MLX4_CMD_MAP_ICM_AUX:
-               mlx4_dbg(dev, "Mapped %d chunks/%d KB for ICM aux.\n", tc, ts);
+               mlx4_dbg(dev, "Mapped %d chunks/%d KB for ICM aux\n", tc, ts);
                break;
        case MLX4_CMD_MAP_ICM:
-               mlx4_dbg(dev, "Mapped %d chunks/%d KB at %llx for ICM.\n",
-                         tc, ts, (unsigned long long) virt - (ts << 10));
+               mlx4_dbg(dev, "Mapped %d chunks/%d KB at %llx for ICM\n",
+                        tc, ts, (unsigned long long) virt - (ts << 10));
                break;
        }
 
@@ -1202,14 +1201,13 @@ int mlx4_QUERY_FW(struct mlx4_dev *dev)
        MLX4_GET(cmd_if_rev, outbox, QUERY_FW_CMD_IF_REV_OFFSET);
        if (cmd_if_rev < MLX4_COMMAND_INTERFACE_MIN_REV ||
            cmd_if_rev > MLX4_COMMAND_INTERFACE_MAX_REV) {
-               mlx4_err(dev, "Installed FW has unsupported "
-                        "command interface revision %d.\n",
+               mlx4_err(dev, "Installed FW has unsupported command interface revision %d\n",
                         cmd_if_rev);
                mlx4_err(dev, "(Installed FW version is %d.%d.%03d)\n",
                         (int) (dev->caps.fw_ver >> 32),
                         (int) (dev->caps.fw_ver >> 16) & 0xffff,
                         (int) dev->caps.fw_ver & 0xffff);
-               mlx4_err(dev, "This driver version supports only revisions %d to %d.\n",
+               mlx4_err(dev, "This driver version supports only revisions %d to %d\n",
                         MLX4_COMMAND_INTERFACE_MIN_REV, MLX4_COMMAND_INTERFACE_MAX_REV);
                err = -ENODEV;
                goto out;
index 26169b3eaed8b2f521f8aa02afd90e7717bc48b8..5f42f6d6e4c6fad9be8437950b44a7cc6658de7f 100644 (file)
@@ -104,8 +104,6 @@ module_param(enable_64b_cqe_eqe, bool, 0444);
 MODULE_PARM_DESC(enable_64b_cqe_eqe,
                 "Enable 64 byte CQEs/EQEs when the FW supports this (default: True)");
 
-#define HCA_GLOBAL_CAP_MASK            0
-
 #define PF_CONTEXT_BEHAVIOUR_MASK      MLX4_FUNC_CAP_64B_EQE_CQE
 
 static char mlx4_version[] =
@@ -134,8 +132,7 @@ MODULE_PARM_DESC(log_num_vlan, "Log2 max number of VLANs per ETH port (0-7)");
 
 static bool use_prio;
 module_param_named(use_prio, use_prio, bool, 0444);
-MODULE_PARM_DESC(use_prio, "Enable steering by VLAN priority on ETH ports "
-                 "(0/1, default 0)");
+MODULE_PARM_DESC(use_prio, "Enable steering by VLAN priority on ETH ports (deprecated)");
 
 int log_mtts_per_seg = ilog2(MLX4_MTT_ENTRY_PER_SEG);
 module_param_named(log_mtts_per_seg, log_mtts_per_seg, int, 0444);
@@ -163,8 +160,7 @@ int mlx4_check_port_params(struct mlx4_dev *dev,
        for (i = 0; i < dev->caps.num_ports - 1; i++) {
                if (port_type[i] != port_type[i + 1]) {
                        if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_DPDP)) {
-                               mlx4_err(dev, "Only same port types supported "
-                                        "on this HCA, aborting.\n");
+                               mlx4_err(dev, "Only same port types supported on this HCA, aborting\n");
                                return -EINVAL;
                        }
                }
@@ -172,8 +168,8 @@ int mlx4_check_port_params(struct mlx4_dev *dev,
 
        for (i = 0; i < dev->caps.num_ports; i++) {
                if (!(port_type[i] & dev->caps.supported_type[i+1])) {
-                       mlx4_err(dev, "Requested port type for port %d is not "
-                                     "supported on this HCA\n", i + 1);
+                       mlx4_err(dev, "Requested port type for port %d is not supported on this HCA\n",
+                                i + 1);
                        return -EINVAL;
                }
        }
@@ -195,26 +191,23 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
 
        err = mlx4_QUERY_DEV_CAP(dev, dev_cap);
        if (err) {
-               mlx4_err(dev, "QUERY_DEV_CAP command failed, aborting.\n");
+               mlx4_err(dev, "QUERY_DEV_CAP command failed, aborting\n");
                return err;
        }
 
        if (dev_cap->min_page_sz > PAGE_SIZE) {
-               mlx4_err(dev, "HCA minimum page size of %d bigger than "
-                        "kernel PAGE_SIZE of %ld, aborting.\n",
+               mlx4_err(dev, "HCA minimum page size of %d bigger than kernel PAGE_SIZE of %ld, aborting\n",
                         dev_cap->min_page_sz, PAGE_SIZE);
                return -ENODEV;
        }
        if (dev_cap->num_ports > MLX4_MAX_PORTS) {
-               mlx4_err(dev, "HCA has %d ports, but we only support %d, "
-                        "aborting.\n",
+               mlx4_err(dev, "HCA has %d ports, but we only support %d, aborting\n",
                         dev_cap->num_ports, MLX4_MAX_PORTS);
                return -ENODEV;
        }
 
        if (dev_cap->uar_size > pci_resource_len(dev->pdev, 2)) {
-               mlx4_err(dev, "HCA reported UAR size of 0x%x bigger than "
-                        "PCI resource 2 size of 0x%llx, aborting.\n",
+               mlx4_err(dev, "HCA reported UAR size of 0x%x bigger than PCI resource 2 size of 0x%llx, aborting\n",
                         dev_cap->uar_size,
                         (unsigned long long) pci_resource_len(dev->pdev, 2));
                return -ENODEV;
@@ -296,7 +289,6 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
 
        dev->caps.log_num_macs  = log_num_mac;
        dev->caps.log_num_vlans = MLX4_LOG_NUM_VLANS;
-       dev->caps.log_num_prios = use_prio ? 3 : 0;
 
        for (i = 1; i <= dev->caps.num_ports; ++i) {
                dev->caps.port_type[i] = MLX4_PORT_TYPE_NONE;
@@ -347,14 +339,12 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
 
                if (dev->caps.log_num_macs > dev_cap->log_max_macs[i]) {
                        dev->caps.log_num_macs = dev_cap->log_max_macs[i];
-                       mlx4_warn(dev, "Requested number of MACs is too much "
-                                 "for port %d, reducing to %d.\n",
+                       mlx4_warn(dev, "Requested number of MACs is too much for port %d, reducing to %d\n",
                                  i, 1 << dev->caps.log_num_macs);
                }
                if (dev->caps.log_num_vlans > dev_cap->log_max_vlans[i]) {
                        dev->caps.log_num_vlans = dev_cap->log_max_vlans[i];
-                       mlx4_warn(dev, "Requested number of VLANs is too much "
-                                 "for port %d, reducing to %d.\n",
+                       mlx4_warn(dev, "Requested number of VLANs is too much for port %d, reducing to %d\n",
                                  i, 1 << dev->caps.log_num_vlans);
                }
        }
@@ -366,7 +356,6 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
                dev->caps.reserved_qps_cnt[MLX4_QP_REGION_FC_ADDR] =
                (1 << dev->caps.log_num_macs) *
                (1 << dev->caps.log_num_vlans) *
-               (1 << dev->caps.log_num_prios) *
                dev->caps.num_ports;
        dev->caps.reserved_qps_cnt[MLX4_QP_REGION_FC_EXCH] = MLX4_NUM_FEXCH;
 
@@ -584,13 +573,14 @@ static int mlx4_slave_cap(struct mlx4_dev *dev)
        memset(&hca_param, 0, sizeof(hca_param));
        err = mlx4_QUERY_HCA(dev, &hca_param);
        if (err) {
-               mlx4_err(dev, "QUERY_HCA command failed, aborting.\n");
+               mlx4_err(dev, "QUERY_HCA command failed, aborting\n");
                return err;
        }
 
-       /*fail if the hca has an unknown capability */
-       if ((hca_param.global_caps | HCA_GLOBAL_CAP_MASK) !=
-           HCA_GLOBAL_CAP_MASK) {
+       /* fail if the hca has an unknown global capability
+        * at this time global_caps should be always zeroed
+        */
+       if (hca_param.global_caps) {
                mlx4_err(dev, "Unknown hca global capabilities\n");
                return -ENOSYS;
        }
@@ -603,19 +593,18 @@ static int mlx4_slave_cap(struct mlx4_dev *dev)
        dev->caps.max_qp_dest_rdma = 1 << hca_param.log_rd_per_qp;
        err = mlx4_dev_cap(dev, &dev_cap);
        if (err) {
-               mlx4_err(dev, "QUERY_DEV_CAP command failed, aborting.\n");
+               mlx4_err(dev, "QUERY_DEV_CAP command failed, aborting\n");
                return err;
        }
 
        err = mlx4_QUERY_FW(dev);
        if (err)
-               mlx4_err(dev, "QUERY_FW command failed: could not get FW version.\n");
+               mlx4_err(dev, "QUERY_FW command failed: could not get FW version\n");
 
        page_size = ~dev->caps.page_size_cap + 1;
        mlx4_warn(dev, "HCA minimum page size:%d\n", page_size);
        if (page_size > PAGE_SIZE) {
-               mlx4_err(dev, "HCA minimum page size of %d bigger than "
-                        "kernel PAGE_SIZE of %ld, aborting.\n",
+               mlx4_err(dev, "HCA minimum page size of %d bigger than kernel PAGE_SIZE of %ld, aborting\n",
                         page_size, PAGE_SIZE);
                return -ENODEV;
        }
@@ -633,8 +622,8 @@ static int mlx4_slave_cap(struct mlx4_dev *dev)
        memset(&func_cap, 0, sizeof(func_cap));
        err = mlx4_QUERY_FUNC_CAP(dev, 0, &func_cap);
        if (err) {
-               mlx4_err(dev, "QUERY_FUNC_CAP general command failed, aborting (%d).\n",
-                         err);
+               mlx4_err(dev, "QUERY_FUNC_CAP general command failed, aborting (%d)\n",
+                        err);
                return err;
        }
 
@@ -661,8 +650,8 @@ static int mlx4_slave_cap(struct mlx4_dev *dev)
        dev->caps.num_amgms             = 0;
 
        if (dev->caps.num_ports > MLX4_MAX_PORTS) {
-               mlx4_err(dev, "HCA has %d ports, but we only support %d, "
-                        "aborting.\n", dev->caps.num_ports, MLX4_MAX_PORTS);
+               mlx4_err(dev, "HCA has %d ports, but we only support %d, aborting\n",
+                        dev->caps.num_ports, MLX4_MAX_PORTS);
                return -ENODEV;
        }
 
@@ -682,8 +671,8 @@ static int mlx4_slave_cap(struct mlx4_dev *dev)
        for (i = 1; i <= dev->caps.num_ports; ++i) {
                err = mlx4_QUERY_FUNC_CAP(dev, (u32) i, &func_cap);
                if (err) {
-                       mlx4_err(dev, "QUERY_FUNC_CAP port command failed for"
-                                " port %d, aborting (%d).\n", i, err);
+                       mlx4_err(dev, "QUERY_FUNC_CAP port command failed for port %d, aborting (%d)\n",
+                                i, err);
                        goto err_mem;
                }
                dev->caps.qp0_qkey[i - 1] = func_cap.qp0_qkey;
@@ -702,8 +691,7 @@ static int mlx4_slave_cap(struct mlx4_dev *dev)
        if (dev->caps.uar_page_size * (dev->caps.num_uars -
                                       dev->caps.reserved_uars) >
                                       pci_resource_len(dev->pdev, 2)) {
-               mlx4_err(dev, "HCA reported UAR region size of 0x%x bigger than "
-                        "PCI resource 2 size of 0x%llx, aborting.\n",
+               mlx4_err(dev, "HCA reported UAR region size of 0x%x bigger than PCI resource 2 size of 0x%llx, aborting\n",
                         dev->caps.uar_page_size * dev->caps.num_uars,
                         (unsigned long long) pci_resource_len(dev->pdev, 2));
                goto err_mem;
@@ -725,7 +713,7 @@ static int mlx4_slave_cap(struct mlx4_dev *dev)
        }
 
        dev->caps.flags2 &= ~MLX4_DEV_CAP_FLAG2_TS;
-       mlx4_warn(dev, "Timestamping is not supported in slave mode.\n");
+       mlx4_warn(dev, "Timestamping is not supported in slave mode\n");
 
        slave_adjust_steering_mode(dev, &dev_cap, &hca_param);
 
@@ -791,8 +779,8 @@ int mlx4_change_port_types(struct mlx4_dev *dev,
                        dev->caps.port_type[port] = port_types[port - 1];
                        err = mlx4_SET_PORT(dev, port, -1);
                        if (err) {
-                               mlx4_err(dev, "Failed to set port %d, "
-                                             "aborting\n", port);
+                               mlx4_err(dev, "Failed to set port %d, aborting\n",
+                                        port);
                                goto out;
                        }
                }
@@ -875,9 +863,7 @@ static ssize_t set_port_type(struct device *dev,
                }
        }
        if (err) {
-               mlx4_err(mdev, "Auto sensing is not supported on this HCA. "
-                              "Set only 'eth' or 'ib' for both ports "
-                              "(should be the same)\n");
+               mlx4_err(mdev, "Auto sensing is not supported on this HCA. Set only 'eth' or 'ib' for both ports (should be the same)\n");
                goto out;
        }
 
@@ -982,8 +968,8 @@ static ssize_t set_port_ib_mtu(struct device *dev,
                mlx4_CLOSE_PORT(mdev, port);
                err = mlx4_SET_PORT(mdev, port, -1);
                if (err) {
-                       mlx4_err(mdev, "Failed to set port %d, "
-                                     "aborting\n", port);
+                       mlx4_err(mdev, "Failed to set port %d, aborting\n",
+                                port);
                        goto err_set_port;
                }
        }
@@ -1002,19 +988,19 @@ static int mlx4_load_fw(struct mlx4_dev *dev)
        priv->fw.fw_icm = mlx4_alloc_icm(dev, priv->fw.fw_pages,
                                         GFP_HIGHUSER | __GFP_NOWARN, 0);
        if (!priv->fw.fw_icm) {
-               mlx4_err(dev, "Couldn't allocate FW area, aborting.\n");
+               mlx4_err(dev, "Couldn't allocate FW area, aborting\n");
                return -ENOMEM;
        }
 
        err = mlx4_MAP_FA(dev, priv->fw.fw_icm);
        if (err) {
-               mlx4_err(dev, "MAP_FA command failed, aborting.\n");
+               mlx4_err(dev, "MAP_FA command failed, aborting\n");
                goto err_free;
        }
 
        err = mlx4_RUN_FW(dev);
        if (err) {
-               mlx4_err(dev, "RUN_FW command failed, aborting.\n");
+               mlx4_err(dev, "RUN_FW command failed, aborting\n");
                goto err_unmap_fa;
        }
 
@@ -1098,30 +1084,30 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap,
 
        err = mlx4_SET_ICM_SIZE(dev, icm_size, &aux_pages);
        if (err) {
-               mlx4_err(dev, "SET_ICM_SIZE command failed, aborting.\n");
+               mlx4_err(dev, "SET_ICM_SIZE command failed, aborting\n");
                return err;
        }
 
-       mlx4_dbg(dev, "%lld KB of HCA context requires %lld KB aux memory.\n",
+       mlx4_dbg(dev, "%lld KB of HCA context requires %lld KB aux memory\n",
                 (unsigned long long) icm_size >> 10,
                 (unsigned long long) aux_pages << 2);
 
        priv->fw.aux_icm = mlx4_alloc_icm(dev, aux_pages,
                                          GFP_HIGHUSER | __GFP_NOWARN, 0);
        if (!priv->fw.aux_icm) {
-               mlx4_err(dev, "Couldn't allocate aux memory, aborting.\n");
+               mlx4_err(dev, "Couldn't allocate aux memory, aborting\n");
                return -ENOMEM;
        }
 
        err = mlx4_MAP_ICM_AUX(dev, priv->fw.aux_icm);
        if (err) {
-               mlx4_err(dev, "MAP_ICM_AUX command failed, aborting.\n");
+               mlx4_err(dev, "MAP_ICM_AUX command failed, aborting\n");
                goto err_free_aux;
        }
 
        err = mlx4_init_cmpt_table(dev, init_hca->cmpt_base, dev_cap->cmpt_entry_sz);
        if (err) {
-               mlx4_err(dev, "Failed to map cMPT context memory, aborting.\n");
+               mlx4_err(dev, "Failed to map cMPT context memory, aborting\n");
                goto err_unmap_aux;
        }
 
@@ -1132,7 +1118,7 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap,
                                  init_hca->eqc_base, dev_cap->eqc_entry_sz,
                                  num_eqs, num_eqs, 0, 0);
        if (err) {
-               mlx4_err(dev, "Failed to map EQ context memory, aborting.\n");
+               mlx4_err(dev, "Failed to map EQ context memory, aborting\n");
                goto err_unmap_cmpt;
        }
 
@@ -1153,7 +1139,7 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap,
                                  dev->caps.num_mtts,
                                  dev->caps.reserved_mtts, 1, 0);
        if (err) {
-               mlx4_err(dev, "Failed to map MTT context memory, aborting.\n");
+               mlx4_err(dev, "Failed to map MTT context memory, aborting\n");
                goto err_unmap_eq;
        }
 
@@ -1163,7 +1149,7 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap,
                                  dev->caps.num_mpts,
                                  dev->caps.reserved_mrws, 1, 1);
        if (err) {
-               mlx4_err(dev, "Failed to map dMPT context memory, aborting.\n");
+               mlx4_err(dev, "Failed to map dMPT context memory, aborting\n");
                goto err_unmap_mtt;
        }
 
@@ -1174,7 +1160,7 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap,
                                  dev->caps.reserved_qps_cnt[MLX4_QP_REGION_FW],
                                  0, 0);
        if (err) {
-               mlx4_err(dev, "Failed to map QP context memory, aborting.\n");
+               mlx4_err(dev, "Failed to map QP context memory, aborting\n");
                goto err_unmap_dmpt;
        }
 
@@ -1185,7 +1171,7 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap,
                                  dev->caps.reserved_qps_cnt[MLX4_QP_REGION_FW],
                                  0, 0);
        if (err) {
-               mlx4_err(dev, "Failed to map AUXC context memory, aborting.\n");
+               mlx4_err(dev, "Failed to map AUXC context memory, aborting\n");
                goto err_unmap_qp;
        }
 
@@ -1196,7 +1182,7 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap,
                                  dev->caps.reserved_qps_cnt[MLX4_QP_REGION_FW],
                                  0, 0);
        if (err) {
-               mlx4_err(dev, "Failed to map ALTC context memory, aborting.\n");
+               mlx4_err(dev, "Failed to map ALTC context memory, aborting\n");
                goto err_unmap_auxc;
        }
 
@@ -1217,7 +1203,7 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap,
                                  dev->caps.num_cqs,
                                  dev->caps.reserved_cqs, 0, 0);
        if (err) {
-               mlx4_err(dev, "Failed to map CQ context memory, aborting.\n");
+               mlx4_err(dev, "Failed to map CQ context memory, aborting\n");
                goto err_unmap_rdmarc;
        }
 
@@ -1227,7 +1213,7 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap,
                                  dev->caps.num_srqs,
                                  dev->caps.reserved_srqs, 0, 0);
        if (err) {
-               mlx4_err(dev, "Failed to map SRQ context memory, aborting.\n");
+               mlx4_err(dev, "Failed to map SRQ context memory, aborting\n");
                goto err_unmap_cq;
        }
 
@@ -1245,7 +1231,7 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap,
                                  dev->caps.num_mgms + dev->caps.num_amgms,
                                  0, 0);
        if (err) {
-               mlx4_err(dev, "Failed to map MCG context memory, aborting.\n");
+               mlx4_err(dev, "Failed to map MCG context memory, aborting\n");
                goto err_unmap_srq;
        }
 
@@ -1322,7 +1308,7 @@ static void mlx4_slave_exit(struct mlx4_dev *dev)
 
        mutex_lock(&priv->cmd.slave_cmd_mutex);
        if (mlx4_comm_cmd(dev, MLX4_COMM_CMD_RESET, 0, MLX4_COMM_TIME))
-               mlx4_warn(dev, "Failed to close slave function.\n");
+               mlx4_warn(dev, "Failed to close slave function\n");
        mutex_unlock(&priv->cmd.slave_cmd_mutex);
 }
 
@@ -1420,7 +1406,7 @@ static int mlx4_init_slave(struct mlx4_dev *dev)
        u32 cmd_channel_ver;
 
        if (atomic_read(&pf_loading)) {
-               mlx4_warn(dev, "PF is not ready. Deferring probe\n");
+               mlx4_warn(dev, "PF is not ready - Deferring probe\n");
                return -EPROBE_DEFER;
        }
 
@@ -1433,8 +1419,7 @@ static int mlx4_init_slave(struct mlx4_dev *dev)
         * NUM_OF_RESET_RETRIES times before leaving.*/
        if (ret_from_reset) {
                if (MLX4_DELAY_RESET_SLAVE == ret_from_reset) {
-                       mlx4_warn(dev, "slave is currently in the "
-                                 "middle of FLR. Deferring probe.\n");
+                       mlx4_warn(dev, "slave is currently in the middle of FLR - Deferring probe\n");
                        mutex_unlock(&priv->cmd.slave_cmd_mutex);
                        return -EPROBE_DEFER;
                } else
@@ -1448,8 +1433,7 @@ static int mlx4_init_slave(struct mlx4_dev *dev)
 
        if (MLX4_COMM_GET_IF_REV(cmd_channel_ver) !=
                MLX4_COMM_GET_IF_REV(slave_read)) {
-               mlx4_err(dev, "slave driver version is not supported"
-                        " by the master\n");
+               mlx4_err(dev, "slave driver version is not supported by the master\n");
                goto err;
        }
 
@@ -1527,8 +1511,7 @@ static void choose_steering_mode(struct mlx4_dev *dev,
 
                        if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER ||
                            dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER)
-                               mlx4_warn(dev, "Must have both UC_STEER and MC_STEER flags "
-                                         "set to use B0 steering. Falling back to A0 steering mode.\n");
+                               mlx4_warn(dev, "Must have both UC_STEER and MC_STEER flags set to use B0 steering - falling back to A0 steering mode\n");
                }
                dev->oper_log_mgm_entry_size =
                        mlx4_log_num_mgm_entry_size > 0 ?
@@ -1536,8 +1519,7 @@ static void choose_steering_mode(struct mlx4_dev *dev,
                        MLX4_DEFAULT_MGM_LOG_ENTRY_SIZE;
                dev->caps.num_qp_per_mgm = mlx4_get_qp_per_mgm(dev);
        }
-       mlx4_dbg(dev, "Steering mode is: %s, oper_log_mgm_entry_size = %d, "
-                "modparam log_num_mgm_entry_size = %d\n",
+       mlx4_dbg(dev, "Steering mode is: %s, oper_log_mgm_entry_size = %d, modparam log_num_mgm_entry_size = %d\n",
                 mlx4_steering_mode_str(dev->caps.steering_mode),
                 dev->oper_log_mgm_entry_size,
                 mlx4_log_num_mgm_entry_size);
@@ -1571,15 +1553,15 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
                err = mlx4_QUERY_FW(dev);
                if (err) {
                        if (err == -EACCES)
-                               mlx4_info(dev, "non-primary physical function, skipping.\n");
+                               mlx4_info(dev, "non-primary physical function, skipping\n");
                        else
-                               mlx4_err(dev, "QUERY_FW command failed, aborting.\n");
+                               mlx4_err(dev, "QUERY_FW command failed, aborting\n");
                        return err;
                }
 
                err = mlx4_load_fw(dev);
                if (err) {
-                       mlx4_err(dev, "Failed to start FW, aborting.\n");
+                       mlx4_err(dev, "Failed to start FW, aborting\n");
                        return err;
                }
 
@@ -1591,7 +1573,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
 
                err = mlx4_dev_cap(dev, &dev_cap);
                if (err) {
-                       mlx4_err(dev, "QUERY_DEV_CAP command failed, aborting.\n");
+                       mlx4_err(dev, "QUERY_DEV_CAP command failed, aborting\n");
                        goto err_stop_fw;
                }
 
@@ -1632,7 +1614,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
 
                err = mlx4_INIT_HCA(dev, &init_hca);
                if (err) {
-                       mlx4_err(dev, "INIT_HCA command failed, aborting.\n");
+                       mlx4_err(dev, "INIT_HCA command failed, aborting\n");
                        goto err_free_icm;
                }
                /*
@@ -1643,7 +1625,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
                        memset(&init_hca, 0, sizeof(init_hca));
                        err = mlx4_QUERY_HCA(dev, &init_hca);
                        if (err) {
-                               mlx4_err(dev, "QUERY_HCA command failed, disable timestamp.\n");
+                               mlx4_err(dev, "QUERY_HCA command failed, disable timestamp\n");
                                dev->caps.flags2 &= ~MLX4_DEV_CAP_FLAG2_TS;
                        } else {
                                dev->caps.hca_core_clock =
@@ -1656,14 +1638,14 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
                        if (!dev->caps.hca_core_clock) {
                                dev->caps.flags2 &= ~MLX4_DEV_CAP_FLAG2_TS;
                                mlx4_err(dev,
-                                        "HCA frequency is 0. Timestamping is not supported.");
+                                        "HCA frequency is 0 - timestamping is not supported\n");
                        } else if (map_internal_clock(dev)) {
                                /*
                                 * Map internal clock,
                                 * in case of failure disable timestamping
                                 */
                                dev->caps.flags2 &= ~MLX4_DEV_CAP_FLAG2_TS;
-                               mlx4_err(dev, "Failed to map internal clock. Timestamping is not supported.\n");
+                               mlx4_err(dev, "Failed to map internal clock. Timestamping is not supported\n");
                        }
                }
        } else {
@@ -1690,7 +1672,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
 
        err = mlx4_QUERY_ADAPTER(dev, &adapter);
        if (err) {
-               mlx4_err(dev, "QUERY_ADAPTER command failed, aborting.\n");
+               mlx4_err(dev, "QUERY_ADAPTER command failed, aborting\n");
                goto unmap_bf;
        }
 
@@ -1808,79 +1790,69 @@ static int mlx4_setup_hca(struct mlx4_dev *dev)
 
        err = mlx4_init_uar_table(dev);
        if (err) {
-               mlx4_err(dev, "Failed to initialize "
-                        "user access region table, aborting.\n");
-               return err;
+               mlx4_err(dev, "Failed to initialize user access region table, aborting\n");
+                return err;
        }
 
        err = mlx4_uar_alloc(dev, &priv->driver_uar);
        if (err) {
-               mlx4_err(dev, "Failed to allocate driver access region, "
-                        "aborting.\n");
+               mlx4_err(dev, "Failed to allocate driver access region, aborting\n");
                goto err_uar_table_free;
        }
 
        priv->kar = ioremap((phys_addr_t) priv->driver_uar.pfn << PAGE_SHIFT, PAGE_SIZE);
        if (!priv->kar) {
-               mlx4_err(dev, "Couldn't map kernel access region, "
-                        "aborting.\n");
+               mlx4_err(dev, "Couldn't map kernel access region, aborting\n");
                err = -ENOMEM;
                goto err_uar_free;
        }
 
        err = mlx4_init_pd_table(dev);
        if (err) {
-               mlx4_err(dev, "Failed to initialize "
-                        "protection domain table, aborting.\n");
+               mlx4_err(dev, "Failed to initialize protection domain table, aborting\n");
                goto err_kar_unmap;
        }
 
        err = mlx4_init_xrcd_table(dev);
        if (err) {
-               mlx4_err(dev, "Failed to initialize "
-                        "reliable connection domain table, aborting.\n");
+               mlx4_err(dev, "Failed to initialize reliable connection domain table, aborting\n");
                goto err_pd_table_free;
        }
 
        err = mlx4_init_mr_table(dev);
        if (err) {
-               mlx4_err(dev, "Failed to initialize "
-                        "memory region table, aborting.\n");
+               mlx4_err(dev, "Failed to initialize memory region table, aborting\n");
                goto err_xrcd_table_free;
        }
 
        if (!mlx4_is_slave(dev)) {
                err = mlx4_init_mcg_table(dev);
                if (err) {
-                       mlx4_err(dev, "Failed to initialize multicast group table, aborting.\n");
+                       mlx4_err(dev, "Failed to initialize multicast group table, aborting\n");
                        goto err_mr_table_free;
                }
        }
 
        err = mlx4_init_eq_table(dev);
        if (err) {
-               mlx4_err(dev, "Failed to initialize "
-                        "event queue table, aborting.\n");
+               mlx4_err(dev, "Failed to initialize event queue table, aborting\n");
                goto err_mcg_table_free;
        }
 
        err = mlx4_cmd_use_events(dev);
        if (err) {
-               mlx4_err(dev, "Failed to switch to event-driven "
-                        "firmware commands, aborting.\n");
+               mlx4_err(dev, "Failed to switch to event-driven firmware commands, aborting\n");
                goto err_eq_table_free;
        }
 
        err = mlx4_NOP(dev);
        if (err) {
                if (dev->flags & MLX4_FLAG_MSI_X) {
-                       mlx4_warn(dev, "NOP command failed to generate MSI-X "
-                                 "interrupt IRQ %d).\n",
+                       mlx4_warn(dev, "NOP command failed to generate MSI-X interrupt IRQ %d)\n",
                                  priv->eq_table.eq[dev->caps.num_comp_vectors].irq);
-                       mlx4_warn(dev, "Trying again without MSI-X.\n");
+                       mlx4_warn(dev, "Trying again without MSI-X\n");
                } else {
-                       mlx4_err(dev, "NOP command failed to generate interrupt "
-                                "(IRQ %d), aborting.\n",
+                       mlx4_err(dev, "NOP command failed to generate interrupt (IRQ %d), aborting\n",
                                 priv->eq_table.eq[dev->caps.num_comp_vectors].irq);
                        mlx4_err(dev, "BIOS or ACPI interrupt routing problem?\n");
                }
@@ -1892,28 +1864,25 @@ static int mlx4_setup_hca(struct mlx4_dev *dev)
 
        err = mlx4_init_cq_table(dev);
        if (err) {
-               mlx4_err(dev, "Failed to initialize "
-                        "completion queue table, aborting.\n");
+               mlx4_err(dev, "Failed to initialize completion queue table, aborting\n");
                goto err_cmd_poll;
        }
 
        err = mlx4_init_srq_table(dev);
        if (err) {
-               mlx4_err(dev, "Failed to initialize "
-                        "shared receive queue table, aborting.\n");
+               mlx4_err(dev, "Failed to initialize shared receive queue table, aborting\n");
                goto err_cq_table_free;
        }
 
        err = mlx4_init_qp_table(dev);
        if (err) {
-               mlx4_err(dev, "Failed to initialize "
-                        "queue pair table, aborting.\n");
+               mlx4_err(dev, "Failed to initialize queue pair table, aborting\n");
                goto err_srq_table_free;
        }
 
        err = mlx4_init_counters_table(dev);
        if (err && err != -ENOENT) {
-               mlx4_err(dev, "Failed to initialize counters table, aborting.\n");
+               mlx4_err(dev, "Failed to initialize counters table, aborting\n");
                goto err_qp_table_free;
        }
 
@@ -1923,9 +1892,8 @@ static int mlx4_setup_hca(struct mlx4_dev *dev)
                        err = mlx4_get_port_ib_caps(dev, port,
                                                    &ib_port_default_caps);
                        if (err)
-                               mlx4_warn(dev, "failed to get port %d default "
-                                         "ib capabilities (%d). Continuing "
-                                         "with caps = 0\n", port, err);
+                               mlx4_warn(dev, "failed to get port %d default ib capabilities (%d). Continuing with caps = 0\n",
+                                         port, err);
                        dev->caps.ib_port_def_cap[port] = ib_port_default_caps;
 
                        /* initialize per-slave default ib port capabilities */
@@ -1935,7 +1903,7 @@ static int mlx4_setup_hca(struct mlx4_dev *dev)
                                        if (i == mlx4_master_func_num(dev))
                                                continue;
                                        priv->mfunc.master.slave_state[i].ib_cap_mask[port] =
-                                                       ib_port_default_caps;
+                                               ib_port_default_caps;
                                }
                        }
 
@@ -1948,7 +1916,7 @@ static int mlx4_setup_hca(struct mlx4_dev *dev)
                                            dev->caps.pkey_table_len[port] : -1);
                        if (err) {
                                mlx4_err(dev, "Failed to set port %d, aborting\n",
-                                       port);
+                                        port);
                                goto err_counters_table_free;
                        }
                }
@@ -2024,7 +1992,7 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev)
                        kfree(entries);
                        goto no_msi;
                } else if (nreq < MSIX_LEGACY_SZ +
-                                 dev->caps.num_ports * MIN_MSIX_P_PORT) {
+                          dev->caps.num_ports * MIN_MSIX_P_PORT) {
                        /*Working in legacy mode , all EQ's shared*/
                        dev->caps.comp_pool           = 0;
                        dev->caps.num_comp_vectors = nreq - 1;
@@ -2225,8 +2193,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
 
        err = pci_enable_device(pdev);
        if (err) {
-               dev_err(&pdev->dev, "Cannot enable PCI device, "
-                       "aborting.\n");
+               dev_err(&pdev->dev, "Cannot enable PCI device, aborting\n");
                return err;
        }
 
@@ -2273,14 +2240,13 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
         */
        if (!(pci_dev_data & MLX4_PCI_DEV_IS_VF) &&
            !(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
-               dev_err(&pdev->dev, "Missing DCS, aborting."
-                       "(driver_data: 0x%x, pci_resource_flags(pdev, 0):0x%lx)\n",
+               dev_err(&pdev->dev, "Missing DCS, aborting (driver_data: 0x%x, pci_resource_flags(pdev, 0):0x%lx)\n",
                        pci_dev_data, pci_resource_flags(pdev, 0));
                err = -ENODEV;
                goto err_disable_pdev;
        }
        if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) {
-               dev_err(&pdev->dev, "Missing UAR, aborting.\n");
+               dev_err(&pdev->dev, "Missing UAR, aborting\n");
                err = -ENODEV;
                goto err_disable_pdev;
        }
@@ -2295,21 +2261,19 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
 
        err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
        if (err) {
-               dev_warn(&pdev->dev, "Warning: couldn't set 64-bit PCI DMA mask.\n");
+               dev_warn(&pdev->dev, "Warning: couldn't set 64-bit PCI DMA mask\n");
                err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
                if (err) {
-                       dev_err(&pdev->dev, "Can't set PCI DMA mask, aborting.\n");
+                       dev_err(&pdev->dev, "Can't set PCI DMA mask, aborting\n");
                        goto err_release_regions;
                }
        }
        err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
        if (err) {
-               dev_warn(&pdev->dev, "Warning: couldn't set 64-bit "
-                        "consistent PCI DMA mask.\n");
+               dev_warn(&pdev->dev, "Warning: couldn't set 64-bit consistent PCI DMA mask\n");
                err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
                if (err) {
-                       dev_err(&pdev->dev, "Can't set consistent PCI DMA mask, "
-                               "aborting.\n");
+                       dev_err(&pdev->dev, "Can't set consistent PCI DMA mask, aborting\n");
                        goto err_release_regions;
                }
        }
@@ -2340,7 +2304,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
                if (total_vfs) {
                        unsigned vfs_offset = 0;
                        for (i = 0; i < sizeof(nvfs)/sizeof(nvfs[0]) &&
-                            vfs_offset + nvfs[i] < extended_func_num(pdev);
+                                    vfs_offset + nvfs[i] < extended_func_num(pdev);
                             vfs_offset += nvfs[i], i++)
                                ;
                        if (i == sizeof(nvfs)/sizeof(nvfs[0])) {
@@ -2366,8 +2330,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
                        if (err < 0)
                                goto err_free_dev;
                        else {
-                               mlx4_warn(dev, "Multiple PFs not yet supported."
-                                         " Skipping PF.\n");
+                               mlx4_warn(dev, "Multiple PFs not yet supported - Skipping PF\n");
                                err = -EINVAL;
                                goto err_free_dev;
                        }
@@ -2377,8 +2340,8 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
                        mlx4_warn(dev, "Enabling SR-IOV with %d VFs\n",
                                  total_vfs);
                        dev->dev_vfs = kzalloc(
-                                       total_vfs * sizeof(*dev->dev_vfs),
-                                       GFP_KERNEL);
+                               total_vfs * sizeof(*dev->dev_vfs),
+                               GFP_KERNEL);
                        if (NULL == dev->dev_vfs) {
                                mlx4_err(dev, "Failed to allocate memory for VFs\n");
                                err = 0;
@@ -2386,14 +2349,14 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
                                atomic_inc(&pf_loading);
                                err = pci_enable_sriov(pdev, total_vfs);
                                if (err) {
-                                       mlx4_err(dev, "Failed to enable SR-IOV, continuing without SR-IOV (err = %d).\n",
+                                       mlx4_err(dev, "Failed to enable SR-IOV, continuing without SR-IOV (err = %d)\n",
                                                 err);
                                        atomic_dec(&pf_loading);
                                        err = 0;
                                } else {
                                        mlx4_warn(dev, "Running in master mode\n");
                                        dev->flags |= MLX4_FLAG_SRIOV |
-                                                     MLX4_FLAG_MASTER;
+                                               MLX4_FLAG_MASTER;
                                        dev->num_vfs = total_vfs;
                                        sriov_initialized = 1;
                                }
@@ -2410,7 +2373,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
                 */
                err = mlx4_reset(dev);
                if (err) {
-                       mlx4_err(dev, "Failed to reset HCA, aborting.\n");
+                       mlx4_err(dev, "Failed to reset HCA, aborting\n");
                        goto err_rel_own;
                }
        }
@@ -2418,7 +2381,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
 slave_start:
        err = mlx4_cmd_init(dev);
        if (err) {
-               mlx4_err(dev, "Failed to init command interface, aborting.\n");
+               mlx4_err(dev, "Failed to init command interface, aborting\n");
                goto err_sriov;
        }
 
@@ -2432,8 +2395,7 @@ slave_start:
                        dev->num_slaves = 0;
                        err = mlx4_multi_func_init(dev);
                        if (err) {
-                               mlx4_err(dev, "Failed to init slave mfunc"
-                                        " interface, aborting.\n");
+                               mlx4_err(dev, "Failed to init slave mfunc interface, aborting\n");
                                goto err_cmd;
                        }
                }
@@ -2465,8 +2427,7 @@ slave_start:
                unsigned sum = 0;
                err = mlx4_multi_func_init(dev);
                if (err) {
-                       mlx4_err(dev, "Failed to init master mfunc"
-                                "interface, aborting.\n");
+                       mlx4_err(dev, "Failed to init master mfunc interface, aborting\n");
                        goto err_close;
                }
                if (sriov_initialized) {
@@ -2477,10 +2438,7 @@ slave_start:
                        if (ib_ports &&
                            (num_vfs_argc > 1 || probe_vfs_argc > 1)) {
                                mlx4_err(dev,
-                                        "Invalid syntax of num_vfs/probe_vfs "
-                                        "with IB port. Single port VFs syntax"
-                                        " is only supported when all ports "
-                                        "are configured as ethernet\n");
+                                        "Invalid syntax of num_vfs/probe_vfs with IB port - single port VFs syntax is only supported when all ports are configured as ethernet\n");
                                goto err_close;
                        }
                        for (i = 0; i < sizeof(nvfs)/sizeof(nvfs[0]); i++) {
@@ -2506,8 +2464,7 @@ slave_start:
        if ((mlx4_is_mfunc(dev)) &&
            !(dev->flags & MLX4_FLAG_MSI_X)) {
                err = -ENOSYS;
-               mlx4_err(dev, "INTx is not supported in multi-function mode."
-                        " aborting.\n");
+               mlx4_err(dev, "INTx is not supported in multi-function mode, aborting\n");
                goto err_free_eq;
        }
 
@@ -2660,7 +2617,7 @@ static void __mlx4_remove_one(struct pci_dev *pdev)
        /* in SRIOV it is not allowed to unload the pf's
         * driver while there are alive vf's */
        if (mlx4_is_master(dev) && mlx4_how_many_lives_vf(dev))
-               printk(KERN_ERR "Removing PF when there are assigned VF's !!!\n");
+               pr_warn("Removing PF when there are assigned VF's !!!\n");
        mlx4_stop_sense(dev);
        mlx4_unregister_device(dev);
 
@@ -2824,7 +2781,7 @@ static struct pci_driver mlx4_driver = {
        .name           = DRV_NAME,
        .id_table       = mlx4_pci_table,
        .probe          = mlx4_init_one,
-       .shutdown       = mlx4_remove_one,
+       .shutdown       = __mlx4_remove_one,
        .remove         = mlx4_remove_one,
        .err_handler    = &mlx4_err_handler,
 };
@@ -2832,33 +2789,36 @@ static struct pci_driver mlx4_driver = {
 static int __init mlx4_verify_params(void)
 {
        if ((log_num_mac < 0) || (log_num_mac > 7)) {
-               pr_warning("mlx4_core: bad num_mac: %d\n", log_num_mac);
+               pr_warn("mlx4_core: bad num_mac: %d\n", log_num_mac);
                return -1;
        }
 
        if (log_num_vlan != 0)
-               pr_warning("mlx4_core: log_num_vlan - obsolete module param, using %d\n",
-                          MLX4_LOG_NUM_VLANS);
+               pr_warn("mlx4_core: log_num_vlan - obsolete module param, using %d\n",
+                       MLX4_LOG_NUM_VLANS);
+
+       if (use_prio != 0)
+               pr_warn("mlx4_core: use_prio - obsolete module param, ignored\n");
 
        if ((log_mtts_per_seg < 1) || (log_mtts_per_seg > 7)) {
-               pr_warning("mlx4_core: bad log_mtts_per_seg: %d\n", log_mtts_per_seg);
+               pr_warn("mlx4_core: bad log_mtts_per_seg: %d\n",
+                       log_mtts_per_seg);
                return -1;
        }
 
        /* Check if module param for ports type has legal combination */
        if (port_type_array[0] == false && port_type_array[1] == true) {
-               printk(KERN_WARNING "Module parameter configuration ETH/IB is not supported. Switching to default configuration IB/IB\n");
+               pr_warn("Module parameter configuration ETH/IB is not supported. Switching to default configuration IB/IB\n");
                port_type_array[0] = true;
        }
 
        if (mlx4_log_num_mgm_entry_size != -1 &&
            (mlx4_log_num_mgm_entry_size < MLX4_MIN_MGM_LOG_ENTRY_SIZE ||
             mlx4_log_num_mgm_entry_size > MLX4_MAX_MGM_LOG_ENTRY_SIZE)) {
-               pr_warning("mlx4_core: mlx4_log_num_mgm_entry_size (%d) not "
-                          "in legal range (-1 or %d..%d)\n",
-                          mlx4_log_num_mgm_entry_size,
-                          MLX4_MIN_MGM_LOG_ENTRY_SIZE,
-                          MLX4_MAX_MGM_LOG_ENTRY_SIZE);
+               pr_warn("mlx4_core: mlx4_log_num_mgm_entry_size (%d) not in legal range (-1 or %d..%d)\n",
+                       mlx4_log_num_mgm_entry_size,
+                       MLX4_MIN_MGM_LOG_ENTRY_SIZE,
+                       MLX4_MAX_MGM_LOG_ENTRY_SIZE);
                return -1;
        }
 
index 80ccb4edf825f8888c6487626f2f380c7b27479b..4c36def8e10f9b518a1ba8a3f05340eb63c4dc0a 100644 (file)
@@ -638,7 +638,7 @@ static int find_entry(struct mlx4_dev *dev, u8 port,
 
                if (!(be32_to_cpu(mgm->members_count) & 0xffffff)) {
                        if (*index != hash) {
-                               mlx4_err(dev, "Found zero MGID in AMGM.\n");
+                               mlx4_err(dev, "Found zero MGID in AMGM\n");
                                err = -EINVAL;
                        }
                        return err;
@@ -874,7 +874,7 @@ static void mlx4_err_rule(struct mlx4_dev *dev, char *str,
        mlx4_err(dev, "%s", buf);
 
        if (len >= BUF_SIZE)
-               mlx4_err(dev, "Network rule error message was truncated, print buffer is too small.\n");
+               mlx4_err(dev, "Network rule error message was truncated, print buffer is too small\n");
 }
 
 int mlx4_flow_attach(struct mlx4_dev *dev,
@@ -897,7 +897,7 @@ int mlx4_flow_attach(struct mlx4_dev *dev,
                ret = parse_trans_rule(dev, cur, mailbox->buf + size);
                if (ret < 0) {
                        mlx4_free_cmd_mailbox(dev, mailbox);
-                       return -EINVAL;
+                       return ret;
                }
                size += ret;
        }
@@ -905,10 +905,10 @@ int mlx4_flow_attach(struct mlx4_dev *dev,
        ret = mlx4_QP_FLOW_STEERING_ATTACH(dev, mailbox, size >> 2, reg_id);
        if (ret == -ENOMEM)
                mlx4_err_rule(dev,
-                             "mcg table is full. Fail to register network rule.\n",
+                             "mcg table is full. Fail to register network rule\n",
                              rule);
        else if (ret)
-               mlx4_err_rule(dev, "Fail to register network rule.\n", rule);
+               mlx4_err_rule(dev, "Fail to register network rule\n", rule);
 
        mlx4_free_cmd_mailbox(dev, mailbox);
 
@@ -994,7 +994,7 @@ int mlx4_qp_attach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
 
        members_count = be32_to_cpu(mgm->members_count) & 0xffffff;
        if (members_count == dev->caps.num_qp_per_mgm) {
-               mlx4_err(dev, "MGM at index %x is full.\n", index);
+               mlx4_err(dev, "MGM at index %x is full\n", index);
                err = -ENOMEM;
                goto out;
        }
@@ -1042,7 +1042,7 @@ out:
        }
        if (err && link && index != -1) {
                if (index < dev->caps.num_mgms)
-                       mlx4_warn(dev, "Got AMGM index %d < %d",
+                       mlx4_warn(dev, "Got AMGM index %d < %d\n",
                                  index, dev->caps.num_mgms);
                else
                        mlx4_bitmap_free(&priv->mcg_table.bitmap,
@@ -1133,7 +1133,7 @@ int mlx4_qp_detach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
 
                if (amgm_index) {
                        if (amgm_index < dev->caps.num_mgms)
-                               mlx4_warn(dev, "MGM entry %d had AMGM index %d < %d",
+                               mlx4_warn(dev, "MGM entry %d had AMGM index %d < %d\n",
                                          index, amgm_index, dev->caps.num_mgms);
                        else
                                mlx4_bitmap_free(&priv->mcg_table.bitmap,
@@ -1153,7 +1153,7 @@ int mlx4_qp_detach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
                        goto out;
 
                if (index < dev->caps.num_mgms)
-                       mlx4_warn(dev, "entry %d had next AMGM index %d < %d",
+                       mlx4_warn(dev, "entry %d had next AMGM index %d < %d\n",
                                  prev, index, dev->caps.num_mgms);
                else
                        mlx4_bitmap_free(&priv->mcg_table.bitmap,
index 7a0665beebb1929ace5b96d61c3a45e993b45824..1d8af7336807b649feb5a5cdb8c4c32a303d31b2 100644 (file)
@@ -221,18 +221,19 @@ extern int mlx4_debug_level;
 #define mlx4_debug_level       (0)
 #endif /* CONFIG_MLX4_DEBUG */
 
-#define mlx4_dbg(mdev, format, arg...)                                 \
+#define mlx4_dbg(mdev, format, ...)                                    \
 do {                                                                   \
        if (mlx4_debug_level)                                           \
-               dev_printk(KERN_DEBUG, &mdev->pdev->dev, format, ##arg); \
+               dev_printk(KERN_DEBUG, &(mdev)->pdev->dev, format,      \
+                          ##__VA_ARGS__);                              \
 } while (0)
 
-#define mlx4_err(mdev, format, arg...) \
-       dev_err(&mdev->pdev->dev, format, ##arg)
-#define mlx4_info(mdev, format, arg...) \
-       dev_info(&mdev->pdev->dev, format, ##arg)
-#define mlx4_warn(mdev, format, arg...) \
-       dev_warn(&mdev->pdev->dev, format, ##arg)
+#define mlx4_err(mdev, format, ...)                                    \
+       dev_err(&(mdev)->pdev->dev, format, ##__VA_ARGS__)
+#define mlx4_info(mdev, format, ...)                                   \
+       dev_info(&(mdev)->pdev->dev, format, ##__VA_ARGS__)
+#define mlx4_warn(mdev, format, ...)                                   \
+       dev_warn(&(mdev)->pdev->dev, format, ##__VA_ARGS__)
 
 extern int mlx4_log_num_mgm_entry_size;
 extern int log_mtts_per_seg;
index 04d9b6fe3e8000fdb14714b09770b11aa46079e8..0e15295bedd671a0c3fc8c1ebbf0372052c489b7 100644 (file)
@@ -313,6 +313,7 @@ struct mlx4_en_rx_ring {
        unsigned long csum_ok;
        unsigned long csum_none;
        int hwtstamp_rx_filter;
+       cpumask_var_t affinity_mask;
 };
 
 struct mlx4_en_cq {
@@ -830,26 +831,26 @@ __printf(3, 4)
 int en_print(const char *level, const struct mlx4_en_priv *priv,
             const char *format, ...);
 
-#define en_dbg(mlevel, priv, format, arg...)                   \
-do {                                                           \
-       if (NETIF_MSG_##mlevel & priv->msg_enable)              \
-               en_print(KERN_DEBUG, priv, format, ##arg);      \
+#define en_dbg(mlevel, priv, format, ...)                              \
+do {                                                                   \
+       if (NETIF_MSG_##mlevel & (priv)->msg_enable)                    \
+               en_print(KERN_DEBUG, priv, format, ##__VA_ARGS__);      \
 } while (0)
-#define en_warn(priv, format, arg...)                  \
-       en_print(KERN_WARNING, priv, format, ##arg)
-#define en_err(priv, format, arg...)                   \
-       en_print(KERN_ERR, priv, format, ##arg)
-#define en_info(priv, format, arg...)                  \
-       en_print(KERN_INFO, priv, format, ## arg)
-
-#define mlx4_err(mdev, format, arg...)                 \
-       pr_err("%s %s: " format, DRV_NAME,              \
-              dev_name(&mdev->pdev->dev), ##arg)
-#define mlx4_info(mdev, format, arg...)                        \
-       pr_info("%s %s: " format, DRV_NAME,             \
-               dev_name(&mdev->pdev->dev), ##arg)
-#define mlx4_warn(mdev, format, arg...)                        \
-       pr_warning("%s %s: " format, DRV_NAME,          \
-                  dev_name(&mdev->pdev->dev), ##arg)
+#define en_warn(priv, format, ...)                                     \
+       en_print(KERN_WARNING, priv, format, ##__VA_ARGS__)
+#define en_err(priv, format, ...)                                      \
+       en_print(KERN_ERR, priv, format, ##__VA_ARGS__)
+#define en_info(priv, format, ...)                                     \
+       en_print(KERN_INFO, priv, format, ##__VA_ARGS__)
+
+#define mlx4_err(mdev, format, ...)                                    \
+       pr_err(DRV_NAME " %s: " format,                                 \
+              dev_name(&(mdev)->pdev->dev), ##__VA_ARGS__)
+#define mlx4_info(mdev, format, ...)                                   \
+       pr_info(DRV_NAME " %s: " format,                                \
+               dev_name(&(mdev)->pdev->dev), ##__VA_ARGS__)
+#define mlx4_warn(mdev, format, ...)                                   \
+       pr_warn(DRV_NAME " %s: " format,                                \
+               dev_name(&(mdev)->pdev->dev), ##__VA_ARGS__)
 
 #endif
index 4c71dafad2175a11d3da7dfa3b554da2e9964e7c..2839abb878a6ae7d9cc0809de20b621e1865cbbe 100644 (file)
@@ -250,8 +250,8 @@ static void mlx4_free_mtt_range(struct mlx4_dev *dev, u32 offset, int order)
                                                       MLX4_CMD_TIME_CLASS_A,
                                                       MLX4_CMD_WRAPPED);
                if (err)
-                       mlx4_warn(dev, "Failed to free mtt range at:"
-                                 "%d order:%d\n", offset, order);
+                       mlx4_warn(dev, "Failed to free mtt range at:%d order:%d\n",
+                                 offset, order);
                return;
        }
         __mlx4_free_mtt_range(dev, offset, order);
@@ -436,8 +436,8 @@ static int mlx4_mr_free_reserved(struct mlx4_dev *dev, struct mlx4_mr *mr)
                                     key_to_hw_index(mr->key) &
                                     (dev->caps.num_mpts - 1));
                if (err) {
-                       mlx4_warn(dev, "HW2SW_MPT failed (%d),", err);
-                       mlx4_warn(dev, "MR has MWs bound to it.\n");
+                       mlx4_warn(dev, "HW2SW_MPT failed (%d), MR has MWs bound to it\n",
+                                 err);
                        return err;
                }
 
@@ -774,7 +774,7 @@ int mlx4_init_mr_table(struct mlx4_dev *dev)
                        mlx4_alloc_mtt_range(dev,
                                             fls(dev->caps.reserved_mtts - 1));
                if (priv->reserved_mtts < 0) {
-                       mlx4_warn(dev, "MTT table of order %u is too small.\n",
+                       mlx4_warn(dev, "MTT table of order %u is too small\n",
                                  mr_table->mtt_buddy.max_order);
                        err = -ENOMEM;
                        goto err_reserve_mtts;
@@ -955,8 +955,7 @@ void mlx4_fmr_unmap(struct mlx4_dev *dev, struct mlx4_fmr *fmr,
        mailbox = mlx4_alloc_cmd_mailbox(dev);
        if (IS_ERR(mailbox)) {
                err = PTR_ERR(mailbox);
-               printk(KERN_WARNING "mlx4_ib: mlx4_alloc_cmd_mailbox"
-                      " failed (%d)\n", err);
+               pr_warn("mlx4_ib: mlx4_alloc_cmd_mailbox failed (%d)\n", err);
                return;
        }
 
@@ -965,8 +964,7 @@ void mlx4_fmr_unmap(struct mlx4_dev *dev, struct mlx4_fmr *fmr,
                             (dev->caps.num_mpts - 1));
        mlx4_free_cmd_mailbox(dev, mailbox);
        if (err) {
-               printk(KERN_WARNING "mlx4_ib: mlx4_HW2SW_MPT failed (%d)\n",
-                      err);
+               pr_warn("mlx4_ib: mlx4_HW2SW_MPT failed (%d)\n", err);
                return;
        }
        fmr->mr.enabled = MLX4_MPT_EN_SW;
index 5ec6f203c6e61d1c8eaa30aa4b387e51a5db1649..7ab97174886d20fd7c6b02d81c58fc3961790469 100644 (file)
@@ -254,8 +254,8 @@ void __mlx4_unregister_mac(struct mlx4_dev *dev, u8 port, u64 mac)
        if (validate_index(dev, table, index))
                goto out;
        if (--table->refs[index]) {
-               mlx4_dbg(dev, "Have more references for index %d,"
-                        "no need to modify mac table\n", index);
+               mlx4_dbg(dev, "Have more references for index %d, no need to modify mac table\n",
+                        index);
                goto out;
        }
 
@@ -453,9 +453,8 @@ void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan)
        }
 
        if (--table->refs[index]) {
-               mlx4_dbg(dev, "Have %d more references for index %d,"
-                        "no need to modify vlan table\n", table->refs[index],
-                        index);
+               mlx4_dbg(dev, "Have %d more references for index %d, no need to modify vlan table\n",
+                        table->refs[index], index);
                goto out;
        }
        table->entries[index] = 0;
@@ -796,8 +795,7 @@ static int mlx4_common_set_port(struct mlx4_dev *dev, int slave, u32 in_mod,
                                        if (!memcmp(gid_entry_mbox->raw, gid_entry_tbl->raw,
                                                    sizeof(gid_entry_tbl->raw))) {
                                                /* found duplicate */
-                                               mlx4_warn(dev, "requested gid entry for slave:%d "
-                                                         "is a duplicate of gid at index %d\n",
+                                               mlx4_warn(dev, "requested gid entry for slave:%d is a duplicate of gid at index %d\n",
                                                          slave, i);
                                                mutex_unlock(&(priv->port[port].gid_table.mutex));
                                                return -EINVAL;
index 8e0c3cc2a1ec786739de7298fc450c483d8fe37a..14089d9e1667fcc4287fe08ca34590e279f66561 100644 (file)
@@ -164,18 +164,17 @@ u64 mlx4_make_profile(struct mlx4_dev *dev,
                }
 
                if (total_size > dev_cap->max_icm_sz) {
-                       mlx4_err(dev, "Profile requires 0x%llx bytes; "
-                                 "won't fit in 0x%llx bytes of context memory.\n",
-                                 (unsigned long long) total_size,
-                                 (unsigned long long) dev_cap->max_icm_sz);
+                       mlx4_err(dev, "Profile requires 0x%llx bytes; won't fit in 0x%llx bytes of context memory\n",
+                                (unsigned long long) total_size,
+                                (unsigned long long) dev_cap->max_icm_sz);
                        kfree(profile);
                        return -ENOMEM;
                }
 
                if (profile[i].size)
-                       mlx4_dbg(dev, "  profile[%2d] (%6s): 2^%02d entries @ 0x%10llx, "
-                                 "size 0x%10llx\n",
-                                i, res_name[profile[i].type], profile[i].log_num,
+                       mlx4_dbg(dev, "  profile[%2d] (%6s): 2^%02d entries @ 0x%10llx, size 0x%10llx\n",
+                                i, res_name[profile[i].type],
+                                profile[i].log_num,
                                 (unsigned long long) profile[i].start,
                                 (unsigned long long) profile[i].size);
        }
index 40af619479254d10e8db34092c53ab223e259bb8..0dc31d85fc3b8ee4e56eb7c2738239fb7fa86f76 100644 (file)
@@ -264,8 +264,8 @@ void mlx4_qp_release_range(struct mlx4_dev *dev, int base_qpn, int cnt)
                               MLX4_CMD_FREE_RES,
                               MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED);
                if (err) {
-                       mlx4_warn(dev, "Failed to release qp range"
-                                 " base:%d cnt:%d\n", base_qpn, cnt);
+                       mlx4_warn(dev, "Failed to release qp range base:%d cnt:%d\n",
+                                 base_qpn, cnt);
                }
        } else
                 __mlx4_qp_release_range(dev, base_qpn, cnt);
@@ -612,8 +612,7 @@ int mlx4_qp_to_ready(struct mlx4_dev *dev, struct mlx4_mtt *mtt,
                err = mlx4_qp_modify(dev, mtt, states[i], states[i + 1],
                                     context, 0, 0, qp);
                if (err) {
-                       mlx4_err(dev, "Failed to bring QP to state: "
-                                "%d with error: %d\n",
+                       mlx4_err(dev, "Failed to bring QP to state: %d with error: %d\n",
                                 states[i + 1], err);
                        return err;
                }
index dd1b5093d8b170812451fb3f67158e1bb8e2b908..ea1c6d092145a5d7e150e8549577edc87f3b0472 100644 (file)
@@ -72,8 +72,7 @@ int mlx4_reset(struct mlx4_dev *dev)
        hca_header = kmalloc(256, GFP_KERNEL);
        if (!hca_header) {
                err = -ENOMEM;
-               mlx4_err(dev, "Couldn't allocate memory to save HCA "
-                         "PCI header, aborting.\n");
+               mlx4_err(dev, "Couldn't allocate memory to save HCA PCI header, aborting\n");
                goto out;
        }
 
@@ -84,8 +83,7 @@ int mlx4_reset(struct mlx4_dev *dev)
                        continue;
                if (pci_read_config_dword(dev->pdev, i * 4, hca_header + i)) {
                        err = -ENODEV;
-                       mlx4_err(dev, "Couldn't save HCA "
-                                 "PCI header, aborting.\n");
+                       mlx4_err(dev, "Couldn't save HCA PCI header, aborting\n");
                        goto out;
                }
        }
@@ -94,7 +92,7 @@ int mlx4_reset(struct mlx4_dev *dev)
                        MLX4_RESET_SIZE);
        if (!reset) {
                err = -ENOMEM;
-               mlx4_err(dev, "Couldn't map HCA reset register, aborting.\n");
+               mlx4_err(dev, "Couldn't map HCA reset register, aborting\n");
                goto out;
        }
 
@@ -133,8 +131,7 @@ int mlx4_reset(struct mlx4_dev *dev)
 
        if (vendor == 0xffff) {
                err = -ENODEV;
-               mlx4_err(dev, "PCI device did not come back after reset, "
-                         "aborting.\n");
+               mlx4_err(dev, "PCI device did not come back after reset, aborting\n");
                goto out;
        }
 
@@ -144,16 +141,14 @@ int mlx4_reset(struct mlx4_dev *dev)
                if (pcie_capability_write_word(dev->pdev, PCI_EXP_DEVCTL,
                                               devctl)) {
                        err = -ENODEV;
-                       mlx4_err(dev, "Couldn't restore HCA PCI Express "
-                                "Device Control register, aborting.\n");
+                       mlx4_err(dev, "Couldn't restore HCA PCI Express Device Control register, aborting\n");
                        goto out;
                }
                linkctl = hca_header[(pcie_cap + PCI_EXP_LNKCTL) / 4];
                if (pcie_capability_write_word(dev->pdev, PCI_EXP_LNKCTL,
                                               linkctl)) {
                        err = -ENODEV;
-                       mlx4_err(dev, "Couldn't restore HCA PCI Express "
-                                "Link control register, aborting.\n");
+                       mlx4_err(dev, "Couldn't restore HCA PCI Express Link control register, aborting\n");
                        goto out;
                }
        }
@@ -164,8 +159,8 @@ int mlx4_reset(struct mlx4_dev *dev)
 
                if (pci_write_config_dword(dev->pdev, i * 4, hca_header[i])) {
                        err = -ENODEV;
-                       mlx4_err(dev, "Couldn't restore HCA reg %x, "
-                                 "aborting.\n", i);
+                       mlx4_err(dev, "Couldn't restore HCA reg %x, aborting\n",
+                                i);
                        goto out;
                }
        }
@@ -173,8 +168,7 @@ int mlx4_reset(struct mlx4_dev *dev)
        if (pci_write_config_dword(dev->pdev, PCI_COMMAND,
                                   hca_header[PCI_COMMAND / 4])) {
                err = -ENODEV;
-               mlx4_err(dev, "Couldn't restore HCA COMMAND, "
-                         "aborting.\n");
+               mlx4_err(dev, "Couldn't restore HCA COMMAND, aborting\n");
                goto out;
        }
 
index 2ba3b7623960109d25c755adbd7af21b940661a7..0efc1368e5a8c4d25b4c95edf049fcae1bec0efb 100644 (file)
@@ -279,7 +279,7 @@ enum qp_transition {
 };
 
 /* For Debug uses */
-static const char *ResourceType(enum mlx4_resource rt)
+static const char *resource_str(enum mlx4_resource rt)
 {
        switch (rt) {
        case RES_QP: return "RES_QP";
@@ -307,6 +307,7 @@ static inline int mlx4_grant_resource(struct mlx4_dev *dev, int slave,
                &priv->mfunc.master.res_tracker.res_alloc[res_type];
        int err = -EINVAL;
        int allocated, free, reserved, guaranteed, from_free;
+       int from_rsvd;
 
        if (slave > dev->num_vfs)
                return -EINVAL;
@@ -321,11 +322,16 @@ static inline int mlx4_grant_resource(struct mlx4_dev *dev, int slave,
                res_alloc->res_reserved;
        guaranteed = res_alloc->guaranteed[slave];
 
-       if (allocated + count > res_alloc->quota[slave])
+       if (allocated + count > res_alloc->quota[slave]) {
+               mlx4_warn(dev, "VF %d port %d res %s: quota exceeded, count %d alloc %d quota %d\n",
+                         slave, port, resource_str(res_type), count,
+                         allocated, res_alloc->quota[slave]);
                goto out;
+       }
 
        if (allocated + count <= guaranteed) {
                err = 0;
+               from_rsvd = count;
        } else {
                /* portion may need to be obtained from free area */
                if (guaranteed - allocated > 0)
@@ -333,8 +339,14 @@ static inline int mlx4_grant_resource(struct mlx4_dev *dev, int slave,
                else
                        from_free = count;
 
-               if (free - from_free > reserved)
+               from_rsvd = count - from_free;
+
+               if (free - from_free >= reserved)
                        err = 0;
+               else
+                       mlx4_warn(dev, "VF %d port %d res %s: free pool empty, free %d from_free %d rsvd %d\n",
+                                 slave, port, resource_str(res_type), free,
+                                 from_free, reserved);
        }
 
        if (!err) {
@@ -342,9 +354,11 @@ static inline int mlx4_grant_resource(struct mlx4_dev *dev, int slave,
                if (port > 0) {
                        res_alloc->allocated[(port - 1) * (dev->num_vfs + 1) + slave] += count;
                        res_alloc->res_port_free[port - 1] -= count;
+                       res_alloc->res_port_rsvd[port - 1] -= from_rsvd;
                } else {
                        res_alloc->allocated[slave] += count;
                        res_alloc->res_free -= count;
+                       res_alloc->res_reserved -= from_rsvd;
                }
        }
 
@@ -360,17 +374,36 @@ static inline void mlx4_release_resource(struct mlx4_dev *dev, int slave,
        struct mlx4_priv *priv = mlx4_priv(dev);
        struct resource_allocator *res_alloc =
                &priv->mfunc.master.res_tracker.res_alloc[res_type];
+       int allocated, guaranteed, from_rsvd;
 
        if (slave > dev->num_vfs)
                return;
 
        spin_lock(&res_alloc->alloc_lock);
+
+       allocated = (port > 0) ?
+               res_alloc->allocated[(port - 1) * (dev->num_vfs + 1) + slave] :
+               res_alloc->allocated[slave];
+       guaranteed = res_alloc->guaranteed[slave];
+
+       if (allocated - count >= guaranteed) {
+               from_rsvd = 0;
+       } else {
+               /* portion may need to be returned to reserved area */
+               if (allocated - guaranteed > 0)
+                       from_rsvd = count - (allocated - guaranteed);
+               else
+                       from_rsvd = count;
+       }
+
        if (port > 0) {
                res_alloc->allocated[(port - 1) * (dev->num_vfs + 1) + slave] -= count;
                res_alloc->res_port_free[port - 1] += count;
+               res_alloc->res_port_rsvd[port - 1] += from_rsvd;
        } else {
                res_alloc->allocated[slave] -= count;
                res_alloc->res_free += count;
+               res_alloc->res_reserved += from_rsvd;
        }
 
        spin_unlock(&res_alloc->alloc_lock);
@@ -963,7 +996,7 @@ static struct res_common *alloc_tr(u64 id, enum mlx4_resource type, int slave,
                ret = alloc_srq_tr(id);
                break;
        case RES_MAC:
-               printk(KERN_ERR "implementation missing\n");
+               pr_err("implementation missing\n");
                return NULL;
        case RES_COUNTER:
                ret = alloc_counter_tr(id);
@@ -1057,10 +1090,10 @@ static int remove_mtt_ok(struct res_mtt *res, int order)
 {
        if (res->com.state == RES_MTT_BUSY ||
            atomic_read(&res->ref_count)) {
-               printk(KERN_DEBUG "%s-%d: state %s, ref_count %d\n",
-                      __func__, __LINE__,
-                      mtt_states_str(res->com.state),
-                      atomic_read(&res->ref_count));
+               pr_devel("%s-%d: state %s, ref_count %d\n",
+                        __func__, __LINE__,
+                        mtt_states_str(res->com.state),
+                        atomic_read(&res->ref_count));
                return -EBUSY;
        } else if (res->com.state != RES_MTT_ALLOCATED)
                return -EPERM;
@@ -3897,7 +3930,7 @@ static int add_eth_header(struct mlx4_dev *dev, int slave,
                }
        }
        if (!be_mac) {
-               pr_err("Failed adding eth header to FS rule, Can't find matching MAC for port %d .\n",
+               pr_err("Failed adding eth header to FS rule, Can't find matching MAC for port %d\n",
                       port);
                return -EINVAL;
        }
@@ -3994,7 +4027,7 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
        qpn = be32_to_cpu(ctrl->qpn) & 0xffffff;
        err = get_res(dev, slave, qpn, RES_QP, &rqp);
        if (err) {
-               pr_err("Steering rule with qpn 0x%x rejected.\n", qpn);
+               pr_err("Steering rule with qpn 0x%x rejected\n", qpn);
                return err;
        }
        rule_header = (struct _rule_hw *)(ctrl + 1);
@@ -4012,7 +4045,7 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
        case MLX4_NET_TRANS_RULE_ID_IPV4:
        case MLX4_NET_TRANS_RULE_ID_TCP:
        case MLX4_NET_TRANS_RULE_ID_UDP:
-               pr_warn("Can't attach FS rule without L2 headers, adding L2 header.\n");
+               pr_warn("Can't attach FS rule without L2 headers, adding L2 header\n");
                if (add_eth_header(dev, slave, inbox, rlist, header_id)) {
                        err = -EINVAL;
                        goto err_put;
@@ -4021,7 +4054,7 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
                        sizeof(struct mlx4_net_trans_rule_hw_eth) >> 2;
                break;
        default:
-               pr_err("Corrupted mailbox.\n");
+               pr_err("Corrupted mailbox\n");
                err = -EINVAL;
                goto err_put;
        }
@@ -4035,7 +4068,7 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
 
        err = add_res_range(dev, slave, vhcr->out_param, 1, RES_FS_RULE, qpn);
        if (err) {
-               mlx4_err(dev, "Fail to add flow steering resources.\n ");
+               mlx4_err(dev, "Fail to add flow steering resources\n");
                /* detach rule*/
                mlx4_cmd(dev, vhcr->out_param, 0, 0,
                         MLX4_QP_FLOW_STEERING_DETACH, MLX4_CMD_TIME_CLASS_A,
@@ -4073,7 +4106,7 @@ int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave,
 
        err = rem_res_range(dev, slave, vhcr->in_param, 1, RES_FS_RULE, 0);
        if (err) {
-               mlx4_err(dev, "Fail to remove flow steering resources.\n ");
+               mlx4_err(dev, "Fail to remove flow steering resources\n");
                goto out;
        }
 
@@ -4151,7 +4184,7 @@ static int _move_all_busy(struct mlx4_dev *dev, int slave,
                                        if (print)
                                                mlx4_dbg(dev,
                                                         "%s id 0x%llx is busy\n",
-                                                         ResourceType(type),
+                                                         resource_str(type),
                                                          r->res_id);
                                        ++busy;
                                } else {
@@ -4202,8 +4235,8 @@ static void rem_slave_qps(struct mlx4_dev *dev, int slave)
 
        err = move_all_busy(dev, slave, RES_QP);
        if (err)
-               mlx4_warn(dev, "rem_slave_qps: Could not move all qps to busy"
-                         "for slave %d\n", slave);
+               mlx4_warn(dev, "rem_slave_qps: Could not move all qps to busy for slave %d\n",
+                         slave);
 
        spin_lock_irq(mlx4_tlock(dev));
        list_for_each_entry_safe(qp, tmp, qp_list, com.list) {
@@ -4241,10 +4274,8 @@ static void rem_slave_qps(struct mlx4_dev *dev, int slave)
                                                       MLX4_CMD_TIME_CLASS_A,
                                                       MLX4_CMD_NATIVE);
                                        if (err)
-                                               mlx4_dbg(dev, "rem_slave_qps: failed"
-                                                        " to move slave %d qpn %d to"
-                                                        " reset\n", slave,
-                                                        qp->local_qpn);
+                                               mlx4_dbg(dev, "rem_slave_qps: failed to move slave %d qpn %d to reset\n",
+                                                        slave, qp->local_qpn);
                                        atomic_dec(&qp->rcq->ref_count);
                                        atomic_dec(&qp->scq->ref_count);
                                        atomic_dec(&qp->mtt->ref_count);
@@ -4278,8 +4309,8 @@ static void rem_slave_srqs(struct mlx4_dev *dev, int slave)
 
        err = move_all_busy(dev, slave, RES_SRQ);
        if (err)
-               mlx4_warn(dev, "rem_slave_srqs: Could not move all srqs to "
-                         "busy for slave %d\n", slave);
+               mlx4_warn(dev, "rem_slave_srqs: Could not move all srqs - too busy for slave %d\n",
+                         slave);
 
        spin_lock_irq(mlx4_tlock(dev));
        list_for_each_entry_safe(srq, tmp, srq_list, com.list) {
@@ -4309,9 +4340,7 @@ static void rem_slave_srqs(struct mlx4_dev *dev, int slave)
                                                       MLX4_CMD_TIME_CLASS_A,
                                                       MLX4_CMD_NATIVE);
                                        if (err)
-                                               mlx4_dbg(dev, "rem_slave_srqs: failed"
-                                                        " to move slave %d srq %d to"
-                                                        " SW ownership\n",
+                                               mlx4_dbg(dev, "rem_slave_srqs: failed to move slave %d srq %d to SW ownership\n",
                                                         slave, srqn);
 
                                        atomic_dec(&srq->mtt->ref_count);
@@ -4346,8 +4375,8 @@ static void rem_slave_cqs(struct mlx4_dev *dev, int slave)
 
        err = move_all_busy(dev, slave, RES_CQ);
        if (err)
-               mlx4_warn(dev, "rem_slave_cqs: Could not move all cqs to "
-                         "busy for slave %d\n", slave);
+               mlx4_warn(dev, "rem_slave_cqs: Could not move all cqs - too busy for slave %d\n",
+                         slave);
 
        spin_lock_irq(mlx4_tlock(dev));
        list_for_each_entry_safe(cq, tmp, cq_list, com.list) {
@@ -4377,9 +4406,7 @@ static void rem_slave_cqs(struct mlx4_dev *dev, int slave)
                                                       MLX4_CMD_TIME_CLASS_A,
                                                       MLX4_CMD_NATIVE);
                                        if (err)
-                                               mlx4_dbg(dev, "rem_slave_cqs: failed"
-                                                        " to move slave %d cq %d to"
-                                                        " SW ownership\n",
+                                               mlx4_dbg(dev, "rem_slave_cqs: failed to move slave %d cq %d to SW ownership\n",
                                                         slave, cqn);
                                        atomic_dec(&cq->mtt->ref_count);
                                        state = RES_CQ_ALLOCATED;
@@ -4411,8 +4438,8 @@ static void rem_slave_mrs(struct mlx4_dev *dev, int slave)
 
        err = move_all_busy(dev, slave, RES_MPT);
        if (err)
-               mlx4_warn(dev, "rem_slave_mrs: Could not move all mpts to "
-                         "busy for slave %d\n", slave);
+               mlx4_warn(dev, "rem_slave_mrs: Could not move all mpts - too busy for slave %d\n",
+                         slave);
 
        spin_lock_irq(mlx4_tlock(dev));
        list_for_each_entry_safe(mpt, tmp, mpt_list, com.list) {
@@ -4447,9 +4474,7 @@ static void rem_slave_mrs(struct mlx4_dev *dev, int slave)
                                                     MLX4_CMD_TIME_CLASS_A,
                                                     MLX4_CMD_NATIVE);
                                        if (err)
-                                               mlx4_dbg(dev, "rem_slave_mrs: failed"
-                                                        " to move slave %d mpt %d to"
-                                                        " SW ownership\n",
+                                               mlx4_dbg(dev, "rem_slave_mrs: failed to move slave %d mpt %d to SW ownership\n",
                                                         slave, mptn);
                                        if (mpt->mtt)
                                                atomic_dec(&mpt->mtt->ref_count);
@@ -4481,8 +4506,8 @@ static void rem_slave_mtts(struct mlx4_dev *dev, int slave)
 
        err = move_all_busy(dev, slave, RES_MTT);
        if (err)
-               mlx4_warn(dev, "rem_slave_mtts: Could not move all mtts to "
-                         "busy for slave %d\n", slave);
+               mlx4_warn(dev, "rem_slave_mtts: Could not move all mtts  - too busy for slave %d\n",
+                         slave);
 
        spin_lock_irq(mlx4_tlock(dev));
        list_for_each_entry_safe(mtt, tmp, mtt_list, com.list) {
@@ -4584,8 +4609,8 @@ static void rem_slave_eqs(struct mlx4_dev *dev, int slave)
 
        err = move_all_busy(dev, slave, RES_EQ);
        if (err)
-               mlx4_warn(dev, "rem_slave_eqs: Could not move all eqs to "
-                         "busy for slave %d\n", slave);
+               mlx4_warn(dev, "rem_slave_eqs: Could not move all eqs - too busy for slave %d\n",
+                         slave);
 
        spin_lock_irq(mlx4_tlock(dev));
        list_for_each_entry_safe(eq, tmp, eq_list, com.list) {
@@ -4617,9 +4642,8 @@ static void rem_slave_eqs(struct mlx4_dev *dev, int slave)
                                                           MLX4_CMD_TIME_CLASS_A,
                                                           MLX4_CMD_NATIVE);
                                        if (err)
-                                               mlx4_dbg(dev, "rem_slave_eqs: failed"
-                                                        " to move slave %d eqs %d to"
-                                                        " SW ownership\n", slave, eqn);
+                                               mlx4_dbg(dev, "rem_slave_eqs: failed to move slave %d eqs %d to SW ownership\n",
+                                                        slave, eqn);
                                        mlx4_free_cmd_mailbox(dev, mailbox);
                                        atomic_dec(&eq->mtt->ref_count);
                                        state = RES_EQ_RESERVED;
@@ -4648,8 +4672,8 @@ static void rem_slave_counters(struct mlx4_dev *dev, int slave)
 
        err = move_all_busy(dev, slave, RES_COUNTER);
        if (err)
-               mlx4_warn(dev, "rem_slave_counters: Could not move all counters to "
-                         "busy for slave %d\n", slave);
+               mlx4_warn(dev, "rem_slave_counters: Could not move all counters - too busy for slave %d\n",
+                         slave);
 
        spin_lock_irq(mlx4_tlock(dev));
        list_for_each_entry_safe(counter, tmp, counter_list, com.list) {
@@ -4679,8 +4703,8 @@ static void rem_slave_xrcdns(struct mlx4_dev *dev, int slave)
 
        err = move_all_busy(dev, slave, RES_XRCD);
        if (err)
-               mlx4_warn(dev, "rem_slave_xrcdns: Could not move all xrcdns to "
-                         "busy for slave %d\n", slave);
+               mlx4_warn(dev, "rem_slave_xrcdns: Could not move all xrcdns - too busy for slave %d\n",
+                         slave);
 
        spin_lock_irq(mlx4_tlock(dev));
        list_for_each_entry_safe(xrcd, tmp, xrcdn_list, com.list) {
@@ -4825,10 +4849,8 @@ void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work)
                                       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);
+                               mlx4_info(dev, "UPDATE_QP failed for slave %d, port %d, qpn %d (%d)\n",
+                                         work->slave, port, qp->local_qpn, err);
                                errors++;
                        }
                }
index 405c4fbcd0ad1cb56453938012ecb9035de20f23..87d1b018a9c394309a6ee78310640690146bf245 100644 (file)
@@ -620,8 +620,8 @@ static int wait_func(struct mlx5_core_dev *dev, struct mlx5_cmd_work_ent *ent)
                               mlx5_command_str(msg_to_opcode(ent->in)),
                               msg_to_opcode(ent->in));
        }
-       mlx5_core_dbg(dev, "err %d, delivery status %s(%d)\n", err,
-                     deliv_status_to_str(ent->status), ent->status);
+       mlx5_core_dbg(dev, "err %d, delivery status %s(%d)\n",
+                     err, deliv_status_to_str(ent->status), ent->status);
 
        return err;
 }
index 64a61b286b2c959fbb67c72dcc098199d74ff68d..7f39ebcd6ad01b3dc175ffd57b3239f9f7154a8a 100644 (file)
@@ -208,7 +208,8 @@ static int mlx5_eq_int(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
                 */
                rmb();
 
-               mlx5_core_dbg(eq->dev, "eqn %d, eqe type %s\n", eq->eqn, eqe_type_str(eqe->type));
+               mlx5_core_dbg(eq->dev, "eqn %d, eqe type %s\n",
+                             eq->eqn, eqe_type_str(eqe->type));
                switch (eqe->type) {
                case MLX5_EVENT_TYPE_COMP:
                        cqn = be32_to_cpu(eqe->data.comp.cqn) & 0xffffff;
@@ -270,14 +271,16 @@ static int mlx5_eq_int(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
                                u16 func_id = be16_to_cpu(eqe->data.req_pages.func_id);
                                s32 npages = be32_to_cpu(eqe->data.req_pages.num_pages);
 
-                               mlx5_core_dbg(dev, "page request for func 0x%x, napges %d\n", func_id, npages);
+                               mlx5_core_dbg(dev, "page request for func 0x%x, npages %d\n",
+                                             func_id, npages);
                                mlx5_core_req_pages_handler(dev, func_id, npages);
                        }
                        break;
 
 
                default:
-                       mlx5_core_warn(dev, "Unhandled event 0x%x on EQ 0x%x\n", eqe->type, eq->eqn);
+                       mlx5_core_warn(dev, "Unhandled event 0x%x on EQ 0x%x\n",
+                                      eqe->type, eq->eqn);
                        break;
                }
 
index c3eee5f70051e0855abf623ad0b1b10614d38b82..ee24f132e319988daaae5f870466bfc3599e3783 100644 (file)
@@ -66,10 +66,10 @@ static int set_dma_caps(struct pci_dev *pdev)
 
        err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
        if (err) {
-               dev_warn(&pdev->dev, "Warning: couldn't set 64-bit PCI DMA mask.\n");
+               dev_warn(&pdev->dev, "Warning: couldn't set 64-bit PCI DMA mask\n");
                err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
                if (err) {
-                       dev_err(&pdev->dev, "Can't set PCI DMA mask, aborting.\n");
+                       dev_err(&pdev->dev, "Can't set PCI DMA mask, aborting\n");
                        return err;
                }
        }
@@ -77,11 +77,11 @@ static int set_dma_caps(struct pci_dev *pdev)
        err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
        if (err) {
                dev_warn(&pdev->dev,
-                        "Warning: couldn't set 64-bit consistent PCI DMA mask.\n");
+                        "Warning: couldn't set 64-bit consistent PCI DMA mask\n");
                err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
                if (err) {
                        dev_err(&pdev->dev,
-                               "Can't set consistent PCI DMA mask, aborting.\n");
+                               "Can't set consistent PCI DMA mask, aborting\n");
                        return err;
                }
        }
@@ -95,7 +95,7 @@ static int request_bar(struct pci_dev *pdev)
        int err = 0;
 
        if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
-               dev_err(&pdev->dev, "Missing registers BAR, aborting.\n");
+               dev_err(&pdev->dev, "Missing registers BAR, aborting\n");
                return -ENODEV;
        }
 
@@ -319,13 +319,13 @@ int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev)
 
        err = pci_enable_device(pdev);
        if (err) {
-               dev_err(&pdev->dev, "Cannot enable PCI device, aborting.\n");
+               dev_err(&pdev->dev, "Cannot enable PCI device, aborting\n");
                goto err_dbg;
        }
 
        err = request_bar(pdev);
        if (err) {
-               dev_err(&pdev->dev, "error requesting BARs, aborting.\n");
+               dev_err(&pdev->dev, "error requesting BARs, aborting\n");
                goto err_disable;
        }
 
index 68b74e1ae1b016c9b7b9ce5866a466dd131dccac..f0c9f9a7a36142f1a7fded7a88120e1cff213aaa 100644 (file)
 
 extern int mlx5_core_debug_mask;
 
-#define mlx5_core_dbg(dev, format, arg...)                                    \
-pr_debug("%s:%s:%d:(pid %d): " format, (dev)->priv.name, __func__, __LINE__,   \
-        current->pid, ##arg)
+#define mlx5_core_dbg(dev, format, ...)                                        \
+       pr_debug("%s:%s:%d:(pid %d): " format,                          \
+                (dev)->priv.name, __func__, __LINE__, current->pid,    \
+                ##__VA_ARGS__)
 
-#define mlx5_core_dbg_mask(dev, mask, format, arg...)                         \
-do {                                                                          \
-       if ((mask) & mlx5_core_debug_mask)                                     \
-               pr_debug("%s:%s:%d:(pid %d): " format, (dev)->priv.name,       \
-                        __func__, __LINE__, current->pid, ##arg);             \
+#define mlx5_core_dbg_mask(dev, mask, format, ...)                     \
+do {                                                                   \
+       if ((mask) & mlx5_core_debug_mask)                              \
+               mlx5_core_dbg(dev, format, ##__VA_ARGS__);              \
 } while (0)
 
-#define mlx5_core_err(dev, format, arg...) \
-pr_err("%s:%s:%d:(pid %d): " format, (dev)->priv.name, __func__, __LINE__,     \
-       current->pid, ##arg)
+#define mlx5_core_err(dev, format, ...)                                        \
+       pr_err("%s:%s:%d:(pid %d): " format,                            \
+              (dev)->priv.name, __func__, __LINE__, current->pid,      \
+              ##__VA_ARGS__)
 
-#define mlx5_core_warn(dev, format, arg...) \
-pr_warn("%s:%s:%d:(pid %d): " format, (dev)->priv.name, __func__, __LINE__,    \
-       current->pid, ##arg)
+#define mlx5_core_warn(dev, format, ...)                               \
+       pr_warn("%s:%s:%d:(pid %d): " format,                           \
+               (dev)->priv.name, __func__, __LINE__, current->pid,     \
+               ##__VA_ARGS__)
 
 enum {
        MLX5_CMD_DATA, /* print command payload only */
index ac52a0fe2d3af12ccda01d858a4f78d9eb7e2343..ba0401d4af502bc5ad83568b067b96dd5436243e 100644 (file)
@@ -73,7 +73,7 @@ int mlx5_core_create_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr,
        }
 
        if (err) {
-               mlx5_core_dbg(dev, "cmd exec faile %d\n", err);
+               mlx5_core_dbg(dev, "cmd exec failed %d\n", err);
                return err;
        }
 
@@ -195,7 +195,8 @@ int mlx5_core_create_psv(struct mlx5_core_dev *dev, u32 pdn,
        }
 
        if (out.hdr.status) {
-               mlx5_core_err(dev, "create_psv bad status %d\n", out.hdr.status);
+               mlx5_core_err(dev, "create_psv bad status %d\n",
+                             out.hdr.status);
                return mlx5_cmd_status_to_err(&out.hdr);
        }
 
@@ -224,7 +225,8 @@ int mlx5_core_destroy_psv(struct mlx5_core_dev *dev, int psv_num)
        }
 
        if (out.hdr.status) {
-               mlx5_core_err(dev, "destroy_psv bad status %d\n", out.hdr.status);
+               mlx5_core_err(dev, "destroy_psv bad status %d\n",
+                             out.hdr.status);
                err = mlx5_cmd_status_to_err(&out.hdr);
                goto out;
        }
index d59790a82bc3d5c9f3def1e4a664a6531b93ad7d..c2a953ef0e675801827ac9bec57e1aaf399dbcca 100644 (file)
@@ -311,7 +311,8 @@ retry:
        in->num_entries = cpu_to_be32(npages);
        err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out));
        if (err) {
-               mlx5_core_warn(dev, "func_id 0x%x, npages %d, err %d\n", func_id, npages, err);
+               mlx5_core_warn(dev, "func_id 0x%x, npages %d, err %d\n",
+                              func_id, npages, err);
                goto out_alloc;
        }
        dev->priv.fw_pages += npages;
@@ -319,7 +320,8 @@ retry:
        if (out.hdr.status) {
                err = mlx5_cmd_status_to_err(&out.hdr);
                if (err) {
-                       mlx5_core_warn(dev, "func_id 0x%x, npages %d, status %d\n", func_id, npages, out.hdr.status);
+                       mlx5_core_warn(dev, "func_id 0x%x, npages %d, status %d\n",
+                                      func_id, npages, out.hdr.status);
                        goto out_alloc;
                }
        }
@@ -378,7 +380,7 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages,
        mlx5_core_dbg(dev, "npages %d, outlen %d\n", npages, outlen);
        err = mlx5_cmd_exec(dev, &in, sizeof(in), out, outlen);
        if (err) {
-               mlx5_core_err(dev, "failed recliaming pages\n");
+               mlx5_core_err(dev, "failed reclaiming pages\n");
                goto out_free;
        }
        dev->priv.fw_pages -= npages;
@@ -414,8 +416,8 @@ static void pages_work_handler(struct work_struct *work)
                err = give_pages(dev, req->func_id, req->npages, 1);
 
        if (err)
-               mlx5_core_warn(dev, "%s fail %d\n", req->npages < 0 ?
-                              "reclaim" : "give", err);
+               mlx5_core_warn(dev, "%s fail %d\n",
+                              req->npages < 0 ? "reclaim" : "give", err);
 
        kfree(req);
 }
@@ -487,7 +489,8 @@ int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev)
                                            optimal_reclaimed_pages(),
                                            &nclaimed);
                        if (err) {
-                               mlx5_core_warn(dev, "failed reclaiming pages (%d)\n", err);
+                               mlx5_core_warn(dev, "failed reclaiming pages (%d)\n",
+                                              err);
                                return err;
                        }
                        if (nclaimed)
index 510576213dd0c8e823a6feecb87d427f275ca90e..8145b4668229d6a483e9b36fdc457387a6c6ab41 100644 (file)
@@ -79,7 +79,7 @@ int mlx5_core_create_qp(struct mlx5_core_dev *dev,
 
        err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out));
        if (err) {
-               mlx5_core_warn(dev, "ret %d", err);
+               mlx5_core_warn(dev, "ret %d\n", err);
                return err;
        }
 
@@ -96,7 +96,7 @@ int mlx5_core_create_qp(struct mlx5_core_dev *dev,
        err = radix_tree_insert(&table->tree, qp->qpn, qp);
        spin_unlock_irq(&table->lock);
        if (err) {
-               mlx5_core_warn(dev, "err %d", err);
+               mlx5_core_warn(dev, "err %d\n", err);
                goto err_cmd;
        }
 
index 16435b3cfa9f133a3fe937dc96670853873e7485..6c7c78baedcaf590f6f6c78f5155a1df2fa594e9 100644 (file)
@@ -1504,15 +1504,15 @@ ks8695_probe(struct platform_device *pdev)
        if (ksp->phyiface_regs && ksp->link_irq == -1) {
                ks8695_init_switch(ksp);
                ksp->dtype = KS8695_DTYPE_LAN;
-               SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops);
+               ndev->ethtool_ops = &ks8695_ethtool_ops;
        } else if (ksp->phyiface_regs && ksp->link_irq != -1) {
                ks8695_init_wan_phy(ksp);
                ksp->dtype = KS8695_DTYPE_WAN;
-               SET_ETHTOOL_OPS(ndev, &ks8695_wan_ethtool_ops);
+               ndev->ethtool_ops = &ks8695_wan_ethtool_ops;
        } else {
                /* No initialisation since HPNA does not have a PHY */
                ksp->dtype = KS8695_DTYPE_HPNA;
-               SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops);
+               ndev->ethtool_ops = &ks8695_ethtool_ops;
        }
 
        /* And bring up the net_device with the net core */
index e0c92e0e5e1d463f0242088d184394608b243cb6..66d4ab703f45a20b7949e1bd3e448c9dc182e68f 100644 (file)
@@ -26,6 +26,8 @@
 #include <linux/regulator/consumer.h>
 
 #include <linux/spi/spi.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
 
 #include "ks8851.h"
 
@@ -85,6 +87,8 @@ union ks8851_tx_hdr {
  * @eeprom_size: Companion eeprom size in Bytes, 0 if no eeprom
  * @eeprom: 93CX6 EEPROM state for accessing on-board EEPROM.
  * @vdd_reg:   Optional regulator supplying the chip
+ * @vdd_io: Optional digital power supply for IO
+ * @gpio: Optional reset_n gpio
  *
  * The @lock ensures that the chip is protected when certain operations are
  * in progress. When the read or write packet transfer is in progress, most
@@ -133,6 +137,8 @@ struct ks8851_net {
 
        struct eeprom_93cx6     eeprom;
        struct regulator        *vdd_reg;
+       struct regulator        *vdd_io;
+       int                     gpio;
 };
 
 static int msg_enable;
@@ -1404,6 +1410,7 @@ static int ks8851_probe(struct spi_device *spi)
        struct ks8851_net *ks;
        int ret;
        unsigned cider;
+       int gpio;
 
        ndev = alloc_etherdev(sizeof(struct ks8851_net));
        if (!ndev)
@@ -1417,20 +1424,53 @@ static int ks8851_probe(struct spi_device *spi)
        ks->spidev = spi;
        ks->tx_space = 6144;
 
-       ks->vdd_reg = regulator_get_optional(&spi->dev, "vdd");
-       if (IS_ERR(ks->vdd_reg)) {
-               ret = PTR_ERR(ks->vdd_reg);
-               if (ret == -EPROBE_DEFER)
-                       goto err_reg;
-       } else {
-               ret = regulator_enable(ks->vdd_reg);
+       gpio = of_get_named_gpio_flags(spi->dev.of_node, "reset-gpios",
+                                      0, NULL);
+       if (gpio == -EPROBE_DEFER) {
+               ret = gpio;
+               goto err_gpio;
+       }
+
+       ks->gpio = gpio;
+       if (gpio_is_valid(gpio)) {
+               ret = devm_gpio_request_one(&spi->dev, gpio,
+                                           GPIOF_OUT_INIT_LOW, "ks8851_rst_n");
                if (ret) {
-                       dev_err(&spi->dev, "regulator enable fail: %d\n",
-                               ret);
-                       goto err_reg_en;
+                       dev_err(&spi->dev, "reset gpio request failed\n");
+                       goto err_gpio;
                }
        }
 
+       ks->vdd_io = devm_regulator_get(&spi->dev, "vdd-io");
+       if (IS_ERR(ks->vdd_io)) {
+               ret = PTR_ERR(ks->vdd_io);
+               goto err_reg_io;
+       }
+
+       ret = regulator_enable(ks->vdd_io);
+       if (ret) {
+               dev_err(&spi->dev, "regulator vdd_io enable fail: %d\n",
+                       ret);
+               goto err_reg_io;
+       }
+
+       ks->vdd_reg = devm_regulator_get(&spi->dev, "vdd");
+       if (IS_ERR(ks->vdd_reg)) {
+               ret = PTR_ERR(ks->vdd_reg);
+               goto err_reg;
+       }
+
+       ret = regulator_enable(ks->vdd_reg);
+       if (ret) {
+               dev_err(&spi->dev, "regulator vdd enable fail: %d\n",
+                       ret);
+               goto err_reg;
+       }
+
+       if (gpio_is_valid(gpio)) {
+               usleep_range(10000, 11000);
+               gpio_set_value(gpio, 1);
+       }
 
        mutex_init(&ks->lock);
        spin_lock_init(&ks->statelock);
@@ -1471,7 +1511,7 @@ static int ks8851_probe(struct spi_device *spi)
 
        skb_queue_head_init(&ks->txq);
 
-       SET_ETHTOOL_OPS(ndev, &ks8851_ethtool_ops);
+       ndev->ethtool_ops = &ks8851_ethtool_ops;
        SET_NETDEV_DEV(ndev, &spi->dev);
 
        spi_set_drvdata(spi, ks);
@@ -1527,13 +1567,14 @@ err_netdev:
        free_irq(ndev->irq, ks);
 
 err_irq:
+       if (gpio_is_valid(gpio))
+               gpio_set_value(gpio, 0);
 err_id:
-       if (!IS_ERR(ks->vdd_reg))
-               regulator_disable(ks->vdd_reg);
-err_reg_en:
-       if (!IS_ERR(ks->vdd_reg))
-               regulator_put(ks->vdd_reg);
+       regulator_disable(ks->vdd_reg);
 err_reg:
+       regulator_disable(ks->vdd_io);
+err_reg_io:
+err_gpio:
        free_netdev(ndev);
        return ret;
 }
@@ -1547,18 +1588,24 @@ static int ks8851_remove(struct spi_device *spi)
 
        unregister_netdev(priv->netdev);
        free_irq(spi->irq, priv);
-       if (!IS_ERR(priv->vdd_reg)) {
-               regulator_disable(priv->vdd_reg);
-               regulator_put(priv->vdd_reg);
-       }
+       if (gpio_is_valid(priv->gpio))
+               gpio_set_value(priv->gpio, 0);
+       regulator_disable(priv->vdd_reg);
+       regulator_disable(priv->vdd_io);
        free_netdev(priv->netdev);
 
        return 0;
 }
 
+static const struct of_device_id ks8851_match_table[] = {
+       { .compatible = "micrel,ks8851" },
+       { }
+};
+
 static struct spi_driver ks8851_driver = {
        .driver = {
                .name = "ks8851",
+               .of_match_table = ks8851_match_table,
                .owner = THIS_MODULE,
                .pm = &ks8851_pm_ops,
        },
index 14ac0e2bc09fcbc50f65ceead949ecd7d15d6130..064a48d0c368a267826e2f77bacb2da9fa366e1e 100644 (file)
@@ -4930,7 +4930,7 @@ static void netdev_tx_timeout(struct net_device *dev)
                 * Only reset the hardware if time between calls is long
                 * enough.
                 */
-               if (jiffies - last_reset <= dev->watchdog_timeo)
+               if (time_before_eq(jiffies, last_reset + dev->watchdog_timeo))
                        hw_priv = NULL;
        }
 
@@ -7072,6 +7072,7 @@ static int pcidev_init(struct pci_dev *pdev, const struct pci_device_id *id)
                dev = alloc_etherdev(sizeof(struct dev_priv));
                if (!dev)
                        goto pcidev_init_reg_err;
+               SET_NETDEV_DEV(dev, &pdev->dev);
                info->netdev[i] = dev;
 
                priv = netdev_priv(dev);
@@ -7106,7 +7107,7 @@ static int pcidev_init(struct pci_dev *pdev, const struct pci_device_id *id)
                }
 
                dev->netdev_ops = &netdev_ops;
-               SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+               dev->ethtool_ops = &netdev_ethtool_ops;
                if (register_netdev(dev))
                        goto pcidev_init_reg_err;
                port_set_power_saving(port, true);
index c7b40aa21f22fe2a909754fe4e2a9a7b8bad5898..b1b5f66b8b6910ad2dc38c2157d8b12aba1668c1 100644 (file)
@@ -1593,7 +1593,7 @@ static int enc28j60_probe(struct spi_device *spi)
        dev->irq = spi->irq;
        dev->netdev_ops = &enc28j60_netdev_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
-       SET_ETHTOOL_OPS(dev, &enc28j60_ethtool_ops);
+       dev->ethtool_ops = &enc28j60_ethtool_ops;
 
        enc28j60_lowpower(priv, true);
 
index 130f6b204efa29cb9c97c98b4e3b0f52b569cd35..f3d5d79f1cd15de8dff66fa4aeab6fccaa25ab8e 100644 (file)
@@ -4112,7 +4112,7 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        setup_timer(&mgp->watchdog_timer, myri10ge_watchdog_timer,
                    (unsigned long)mgp);
 
-       SET_ETHTOOL_OPS(netdev, &myri10ge_ethtool_ops);
+       netdev->ethtool_ops = &myri10ge_ethtool_ops;
        INIT_WORK(&mgp->watchdog_work, myri10ge_watchdog);
        status = register_netdev(netdev);
        if (status != 0) {
index 64ec2a437f46a3280e9377c55d8226a19f82d089..291fba8b9f07351effff8ebbc9b229fc40c0e885 100644 (file)
@@ -927,7 +927,7 @@ static int natsemi_probe1(struct pci_dev *pdev, const struct pci_device_id *ent)
        dev->netdev_ops = &natsemi_netdev_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
 
-       SET_ETHTOOL_OPS(dev, &ethtool_ops);
+       dev->ethtool_ops = &ethtool_ops;
 
        if (mtu)
                dev->mtu = mtu;
index dbccf1de49ecbf7011a167585caefdeb082fa4de..19bb8244b9e3e1056a2835bf2c2434f1e6ae65a3 100644 (file)
@@ -2030,7 +2030,7 @@ static int ns83820_init_one(struct pci_dev *pci_dev,
                pci_dev->subsystem_vendor, pci_dev->subsystem_device);
 
        ndev->netdev_ops = &netdev_ops;
-       SET_ETHTOOL_OPS(ndev, &ops);
+       ndev->ethtool_ops = &ops;
        ndev->watchdog_timeo = 5 * HZ;
        pci_set_drvdata(pci_dev, ndev);
 
index a2844ff322c4c62bed8957a7f3797ad321359cbf..be587647c70657a93f86608ba6e06e731e7aecb6 100644 (file)
@@ -534,15 +534,6 @@ static inline void s2io_start_all_tx_queue(struct s2io_nic *sp)
        netif_tx_start_all_queues(sp->dev);
 }
 
-static inline void s2io_start_tx_queue(struct s2io_nic *sp, int fifo_no)
-{
-       if (!sp->config.multiq)
-               sp->mac_control.fifos[fifo_no].queue_state =
-                       FIFO_QUEUE_START;
-
-       netif_tx_start_all_queues(sp->dev);
-}
-
 static inline void s2io_wake_all_tx_queue(struct s2io_nic *sp)
 {
        if (!sp->config.multiq) {
@@ -5369,8 +5360,8 @@ static int s2io_ethtool_gset(struct net_device *dev, struct ethtool_cmd *info)
                ethtool_cmd_speed_set(info, SPEED_10000);
                info->duplex = DUPLEX_FULL;
        } else {
-               ethtool_cmd_speed_set(info, -1);
-               info->duplex = -1;
+               ethtool_cmd_speed_set(info, SPEED_UNKNOWN);
+               info->duplex = DUPLEX_UNKNOWN;
        }
 
        info->autoneg = AUTONEG_DISABLE;
@@ -7919,7 +7910,7 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
 
        /*  Driver entry points */
        dev->netdev_ops = &s2io_netdev_ops;
-       SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+       dev->ethtool_ops = &netdev_ethtool_ops;
        dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM |
                NETIF_F_TSO | NETIF_F_TSO6 |
                NETIF_F_RXCSUM | NETIF_F_LRO;
index 089b713b9f7be772c5453c26885b29fc17663dcb..2bbd01fcb9b019eaeabc1519a98799e65ea1b51a 100644 (file)
@@ -120,7 +120,6 @@ __vxge_hw_device_register_poll(void __iomem *reg, u64 mask, u32 max_millis)
 {
        u64 val64;
        u32 i = 0;
-       enum vxge_hw_status ret = VXGE_HW_FAIL;
 
        udelay(10);
 
@@ -139,7 +138,7 @@ __vxge_hw_device_register_poll(void __iomem *reg, u64 mask, u32 max_millis)
                mdelay(1);
        } while (++i <= max_millis);
 
-       return ret;
+       return VXGE_HW_FAIL;
 }
 
 static inline enum vxge_hw_status
@@ -1682,12 +1681,10 @@ enum vxge_hw_status vxge_hw_driver_stats_get(
                        struct __vxge_hw_device *hldev,
                        struct vxge_hw_device_stats_sw_info *sw_stats)
 {
-       enum vxge_hw_status status = VXGE_HW_OK;
-
        memcpy(sw_stats, &hldev->stats.sw_dev_info_stats,
                sizeof(struct vxge_hw_device_stats_sw_info));
 
-       return status;
+       return VXGE_HW_OK;
 }
 
 /*
@@ -3228,7 +3225,6 @@ enum vxge_hw_status
 vxge_hw_vpath_strip_fcs_check(struct __vxge_hw_device *hldev, u64 vpath_mask)
 {
        struct vxge_hw_vpmgmt_reg       __iomem *vpmgmt_reg;
-       enum vxge_hw_status status = VXGE_HW_OK;
        int i = 0, j = 0;
 
        for (i = 0; i < VXGE_HW_MAX_VIRTUAL_PATHS; i++) {
@@ -3241,7 +3237,7 @@ vxge_hw_vpath_strip_fcs_check(struct __vxge_hw_device *hldev, u64 vpath_mask)
                                return VXGE_HW_FAIL;
                }
        }
-       return status;
+       return VXGE_HW_OK;
 }
 /*
  * vxge_hw_mgmt_reg_Write - Write Titan register.
@@ -3979,7 +3975,6 @@ __vxge_hw_vpath_mgmt_read(
 {
        u32 i, mtu = 0, max_pyld = 0;
        u64 val64;
-       enum vxge_hw_status status = VXGE_HW_OK;
 
        for (i = 0; i < VXGE_HW_MAC_MAX_MAC_PORT_ID; i++) {
 
@@ -4009,7 +4004,7 @@ __vxge_hw_vpath_mgmt_read(
        else
                VXGE_HW_DEVICE_LINK_STATE_SET(vpath->hldev, VXGE_HW_LINK_DOWN);
 
-       return status;
+       return VXGE_HW_OK;
 }
 
 /*
@@ -4039,14 +4034,13 @@ static enum vxge_hw_status
 __vxge_hw_vpath_reset(struct __vxge_hw_device *hldev, u32 vp_id)
 {
        u64 val64;
-       enum vxge_hw_status status = VXGE_HW_OK;
 
        val64 = VXGE_HW_CMN_RSTHDLR_CFG0_SW_RESET_VPATH(1 << (16 - vp_id));
 
        __vxge_hw_pio_mem_write32_upper((u32)vxge_bVALn(val64, 0, 32),
                                &hldev->common_reg->cmn_rsthdlr_cfg0);
 
-       return status;
+       return VXGE_HW_OK;
 }
 
 /*
@@ -4227,7 +4221,6 @@ static enum vxge_hw_status
 __vxge_hw_vpath_mac_configure(struct __vxge_hw_device *hldev, u32 vp_id)
 {
        u64 val64;
-       enum vxge_hw_status status = VXGE_HW_OK;
        struct __vxge_hw_virtualpath *vpath;
        struct vxge_hw_vp_config *vp_config;
        struct vxge_hw_vpath_reg __iomem *vp_reg;
@@ -4283,7 +4276,7 @@ __vxge_hw_vpath_mac_configure(struct __vxge_hw_device *hldev, u32 vp_id)
 
                writeq(val64, &vp_reg->rxmac_vcfg1);
        }
-       return status;
+       return VXGE_HW_OK;
 }
 
 /*
@@ -4295,7 +4288,6 @@ static enum vxge_hw_status
 __vxge_hw_vpath_tim_configure(struct __vxge_hw_device *hldev, u32 vp_id)
 {
        u64 val64;
-       enum vxge_hw_status status = VXGE_HW_OK;
        struct __vxge_hw_virtualpath *vpath;
        struct vxge_hw_vpath_reg __iomem *vp_reg;
        struct vxge_hw_vp_config *config;
@@ -4545,7 +4537,7 @@ __vxge_hw_vpath_tim_configure(struct __vxge_hw_device *hldev, u32 vp_id)
        val64 |= VXGE_HW_TIM_WRKLD_CLC_CNT_RX_TX(3);
        writeq(val64, &vp_reg->tim_wrkld_clc);
 
-       return status;
+       return VXGE_HW_OK;
 }
 
 /*
index f8f073880f84bccd5f0daedb8c1f08233c43e6c8..b07d552a27d4d486079b329b238235411a103e4e 100644 (file)
@@ -62,8 +62,8 @@ static int vxge_ethtool_gset(struct net_device *dev, struct ethtool_cmd *info)
                ethtool_cmd_speed_set(info, SPEED_10000);
                info->duplex = DUPLEX_FULL;
        } else {
-               ethtool_cmd_speed_set(info, -1);
-               info->duplex = -1;
+               ethtool_cmd_speed_set(info, SPEED_UNKNOWN);
+               info->duplex = DUPLEX_UNKNOWN;
        }
 
        info->autoneg = AUTONEG_DISABLE;
@@ -1128,5 +1128,5 @@ static const struct ethtool_ops vxge_ethtool_ops = {
 
 void vxge_initialize_ethtool_ops(struct net_device *ndev)
 {
-       SET_ETHTOOL_OPS(ndev, &vxge_ethtool_ops);
+       ndev->ethtool_ops = &vxge_ethtool_ops;
 }
index d107bcbb8543035110a98a82a21a7e72c8ec1303..7a0deadd53bf14743e4c530b895c8658e59c8be2 100644 (file)
@@ -2122,7 +2122,7 @@ static int vxge_open_vpaths(struct vxgedev *vdev)
 static void adaptive_coalesce_tx_interrupts(struct vxge_fifo *fifo)
 {
        fifo->interrupt_count++;
-       if (jiffies > fifo->jiffies + HZ / 100) {
+       if (time_before(fifo->jiffies + HZ / 100, jiffies)) {
                struct __vxge_hw_fifo *hw_fifo = fifo->handle;
 
                fifo->jiffies = jiffies;
@@ -2150,7 +2150,7 @@ static void adaptive_coalesce_tx_interrupts(struct vxge_fifo *fifo)
 static void adaptive_coalesce_rx_interrupts(struct vxge_ring *ring)
 {
        ring->interrupt_count++;
-       if (jiffies > ring->jiffies + HZ / 100) {
+       if (time_before(ring->jiffies + HZ / 100, jiffies)) {
                struct __vxge_hw_ring *hw_ring = ring->handle;
 
                ring->jiffies = jiffies;
index fddb464aeab3a517c362d12ad4891eb3e2529cae..9afc536c5734ec264f0d084b88602444cb1b24bd 100644 (file)
@@ -406,7 +406,7 @@ union ring_type {
 
 #define NV_RX_DESCRIPTORVALID  (1<<16)
 #define NV_RX_MISSEDFRAME      (1<<17)
-#define NV_RX_SUBSTRACT1       (1<<18)
+#define NV_RX_SUBTRACT1                (1<<18)
 #define NV_RX_ERROR1           (1<<23)
 #define NV_RX_ERROR2           (1<<24)
 #define NV_RX_ERROR3           (1<<25)
@@ -423,7 +423,7 @@ union ring_type {
 #define NV_RX2_CHECKSUM_IP_TCP (0x14000000)
 #define NV_RX2_CHECKSUM_IP_UDP (0x18000000)
 #define NV_RX2_DESCRIPTORVALID (1<<29)
-#define NV_RX2_SUBSTRACT1      (1<<25)
+#define NV_RX2_SUBTRACT1       (1<<25)
 #define NV_RX2_ERROR1          (1<<18)
 #define NV_RX2_ERROR2          (1<<19)
 #define NV_RX2_ERROR3          (1<<20)
@@ -2832,7 +2832,7 @@ static int nv_rx_process(struct net_device *dev, int limit)
                                        }
                                        /* framing errors are soft errors */
                                        else if ((flags & NV_RX_ERROR_MASK) == NV_RX_FRAMINGERR) {
-                                               if (flags & NV_RX_SUBSTRACT1)
+                                               if (flags & NV_RX_SUBTRACT1)
                                                        len--;
                                        }
                                        /* the rest are hard errors */
@@ -2863,7 +2863,7 @@ static int nv_rx_process(struct net_device *dev, int limit)
                                        }
                                        /* framing errors are soft errors */
                                        else if ((flags & NV_RX2_ERROR_MASK) == NV_RX2_FRAMINGERR) {
-                                               if (flags & NV_RX2_SUBSTRACT1)
+                                               if (flags & NV_RX2_SUBTRACT1)
                                                        len--;
                                        }
                                        /* the rest are hard errors */
@@ -2937,7 +2937,7 @@ static int nv_rx_process_optimized(struct net_device *dev, int limit)
                                }
                                /* framing errors are soft errors */
                                else if ((flags & NV_RX2_ERROR_MASK) == NV_RX2_FRAMINGERR) {
-                                       if (flags & NV_RX2_SUBSTRACT1)
+                                       if (flags & NV_RX2_SUBTRACT1)
                                                len--;
                                }
                                /* the rest are hard errors */
@@ -4285,8 +4285,8 @@ static int nv_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
                if (np->duplex)
                        ecmd->duplex = DUPLEX_FULL;
        } else {
-               speed = -1;
-               ecmd->duplex = -1;
+               speed = SPEED_UNKNOWN;
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
        ethtool_cmd_speed_set(ecmd, speed);
        ecmd->autoneg = np->autoneg;
@@ -5766,7 +5766,7 @@ static int nv_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
                dev->netdev_ops = &nv_netdev_ops_optimized;
 
        netif_napi_add(dev, &np->napi, nv_napi_poll, RX_WORK_PER_LOOP);
-       SET_ETHTOOL_OPS(dev, &ops);
+       dev->ethtool_ops = &ops;
        dev->watchdog_timeo = NV_WATCHDOG_TIMEO;
 
        pci_set_drvdata(pci_dev, dev);
index 422d9b51ac2408da844669b1c2b0f1364e8e093a..8706c0dbd0c36a2c3b7af167eaa4c29cda3905a5 100644 (file)
@@ -1361,7 +1361,7 @@ static int lpc_eth_drv_probe(struct platform_device *pdev)
        __lpc_eth_clock_enable(pldat, true);
 
        /* Map IO space */
-       pldat->net_base = ioremap(res->start, res->end - res->start + 1);
+       pldat->net_base = ioremap(res->start, resource_size(res));
        if (!pldat->net_base) {
                dev_err(&pdev->dev, "failed to map registers\n");
                ret = -ENOMEM;
@@ -1417,10 +1417,8 @@ static int lpc_eth_drv_probe(struct platform_device *pdev)
        }
        pldat->dma_buff_base_p = dma_handle;
 
-       netdev_dbg(ndev, "IO address start     :0x%08x\n",
-                       res->start);
-       netdev_dbg(ndev, "IO address size      :%d\n",
-                       res->end - res->start + 1);
+       netdev_dbg(ndev, "IO address space     :%pR\n", res);
+       netdev_dbg(ndev, "IO address size      :%d\n", resource_size(res));
        netdev_dbg(ndev, "IO address (mapped)  :0x%p\n",
                        pldat->net_base);
        netdev_dbg(ndev, "IRQ number           :%d\n", ndev->irq);
index a588ffde970041def37cae92b215011d88b6eea6..44c8be1c68051ec9a9b79f4ba481060022613e79 100644 (file)
@@ -4,7 +4,7 @@
 
 config PCH_GBE
        tristate "OKI SEMICONDUCTOR IOH(ML7223/ML7831) GbE"
-       depends on PCI && (X86 || COMPILE_TEST)
+       depends on PCI && (X86_32 || COMPILE_TEST)
        select MII
        select PTP_1588_CLOCK_PCH
        ---help---
index 826f0ccdc23c818139d3951c953b56a3b885d6e8..4fe8ea96bd25d24f1f2296d22230ceebda1e8f64 100644 (file)
@@ -91,7 +91,7 @@ static int pch_gbe_get_settings(struct net_device *netdev,
        ecmd->advertising &= ~(ADVERTISED_TP | ADVERTISED_1000baseT_Half);
 
        if (!netif_carrier_ok(adapter->netdev))
-               ethtool_cmd_speed_set(ecmd, -1);
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
        return ret;
 }
 
@@ -508,5 +508,5 @@ static const struct ethtool_ops pch_gbe_ethtool_ops = {
 
 void pch_gbe_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &pch_gbe_ethtool_ops);
+       netdev->ethtool_ops = &pch_gbe_ethtool_ops;
 }
index b6bdeb3c19711ac960646dfdbeef2207b86f366d..9a997e4c3e084a16368ebcae6ed53d42a0c0524c 100644 (file)
@@ -724,10 +724,8 @@ static int hamachi_init_one(struct pci_dev *pdev,
 
        /* The Hamachi-specific entries in the device structure. */
        dev->netdev_ops = &hamachi_netdev_ops;
-       if (chip_tbl[hmp->chip_id].flags & CanHaveMII)
-               SET_ETHTOOL_OPS(dev, &ethtool_ops);
-       else
-               SET_ETHTOOL_OPS(dev, &ethtool_ops_no_mii);
+       dev->ethtool_ops = (chip_tbl[hmp->chip_id].flags & CanHaveMII) ?
+               &ethtool_ops : &ethtool_ops_no_mii;
        dev->watchdog_timeo = TX_TIMEOUT;
        if (mtu)
                dev->mtu = mtu;
index 9a6cb482dcd0b8bf5e26e3e2c784f57b032f5236..69a8dc0950720b7f57a83483d1cf86f0f4f9f35d 100644 (file)
@@ -472,7 +472,7 @@ static int yellowfin_init_one(struct pci_dev *pdev,
 
        /* The Yellowfin-specific entries in the device structure. */
        dev->netdev_ops = &netdev_ops;
-       SET_ETHTOOL_OPS(dev, &ethtool_ops);
+       dev->ethtool_ops = &ethtool_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
 
        if (mtu)
index c14bd3116e454edad88d27f7ab8923cb999ec885..d49cba1290814ecc175d925fca6ae2dd9227cded 100644 (file)
@@ -66,6 +66,17 @@ config QLCNIC_VXLAN
          Say Y here if you want to enable hardware offload support for
          Virtual eXtensible Local Area Network (VXLAN) in the driver.
 
+config QLCNIC_HWMON
+       bool "QLOGIC QLCNIC 82XX and 83XX family HWMON support"
+       depends on QLCNIC && HWMON && !(QLCNIC=y && HWMON=m)
+       default y
+       ---help---
+         This configuration parameter can be used to read the
+         board temperature in Converged Ethernet devices
+         supported by qlcnic.
+
+         This data is available via the hwmon sysfs interface.
+
 config QLGE
        tristate "QLogic QLGE 10Gb Ethernet Driver Support"
        depends on PCI
index f09c35d669b3ec7d8898f96f0ffd9ce7362ddb13..5bf05818a12cfa707d138c420908546daaa502a5 100644 (file)
@@ -1373,7 +1373,7 @@ netxen_setup_netdev(struct netxen_adapter *adapter,
 
        netxen_nic_change_mtu(netdev, netdev->mtu);
 
-       SET_ETHTOOL_OPS(netdev, &netxen_nic_ethtool_ops);
+       netdev->ethtool_ops = &netxen_nic_ethtool_ops;
 
        netdev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
                              NETIF_F_RXCSUM;
index 2eabd44f8914de91ac4eaebbed17d4a08c66c96f..b5d6bc1a8b0024770c919e6ccf1f49a0cc6da1a8 100644 (file)
@@ -3838,7 +3838,7 @@ static int ql3xxx_probe(struct pci_dev *pdev,
 
        /* Set driver entry points */
        ndev->netdev_ops = &ql3xxx_netdev_ops;
-       SET_ETHTOOL_OPS(ndev, &ql3xxx_ethtool_ops);
+       ndev->ethtool_ops = &ql3xxx_ethtool_ops;
        ndev->watchdog_timeo = 5 * HZ;
 
        netif_napi_add(ndev, &qdev->napi, ql_poll, 64);
index f785d01c7d123dde875a774ebf1027800af33425..be618b9e874f6a12c16d5876fc7b5302b73b4a4f 100644 (file)
@@ -39,8 +39,8 @@
 
 #define _QLCNIC_LINUX_MAJOR 5
 #define _QLCNIC_LINUX_MINOR 3
-#define _QLCNIC_LINUX_SUBVERSION 57
-#define QLCNIC_LINUX_VERSIONID  "5.3.57"
+#define _QLCNIC_LINUX_SUBVERSION 60
+#define QLCNIC_LINUX_VERSIONID  "5.3.60"
 #define QLCNIC_DRV_IDC_VER  0x01
 #define QLCNIC_DRIVER_VERSION  ((_QLCNIC_LINUX_MAJOR << 16) |\
                 (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION))
@@ -441,6 +441,8 @@ struct qlcnic_82xx_dump_template_hdr {
        u32     rsvd1[0];
 };
 
+#define QLC_PEX_DMA_READ_SIZE  (PAGE_SIZE * 16)
+
 struct qlcnic_fw_dump {
        u8      clr;    /* flag to indicate if dump is cleared */
        bool    enable; /* enable/disable dump */
@@ -537,6 +539,7 @@ struct qlcnic_hardware_context {
        u8 phys_port_id[ETH_ALEN];
        u8 lb_mode;
        u16 vxlan_port;
+       struct device *hwmon_dev;
 };
 
 struct qlcnic_adapter_stats {
@@ -1018,6 +1021,8 @@ struct qlcnic_ipaddr {
 #define QLCNIC_DEL_VXLAN_PORT          0x200000
 #endif
 
+#define QLCNIC_VLAN_FILTERING          0x800000
+
 #define QLCNIC_IS_MSI_FAMILY(adapter) \
        ((adapter)->flags & (QLCNIC_MSI_ENABLED | QLCNIC_MSIX_ENABLED))
 #define QLCNIC_IS_TSO_CAPABLE(adapter)  \
@@ -1316,6 +1321,7 @@ struct qlcnic_eswitch {
 #define QL_STATUS_INVALID_PARAM        -1
 
 #define MAX_BW                 100     /* % of link speed */
+#define MIN_BW                 1       /* % of link speed */
 #define MAX_VLAN_ID            4095
 #define MIN_VLAN_ID            2
 #define DEFAULT_MAC_LEARN      1
@@ -1692,7 +1698,7 @@ int qlcnic_read_mac_addr(struct qlcnic_adapter *);
 int qlcnic_setup_netdev(struct qlcnic_adapter *, struct net_device *, int);
 void qlcnic_set_netdev_features(struct qlcnic_adapter *,
                                struct qlcnic_esw_func_cfg *);
-void qlcnic_sriov_vf_schedule_multi(struct net_device *);
+void qlcnic_sriov_vf_set_multi(struct net_device *);
 int qlcnic_is_valid_nic_func(struct qlcnic_adapter *, u8);
 int qlcnic_get_pci_func_type(struct qlcnic_adapter *, u16, u16 *, u16 *,
                             u16 *);
@@ -2338,6 +2344,16 @@ static inline bool qlcnic_83xx_vf_check(struct qlcnic_adapter *adapter)
        return (device == PCI_DEVICE_ID_QLOGIC_VF_QLE834X) ? true : false;
 }
 
+static inline bool qlcnic_sriov_check(struct qlcnic_adapter *adapter)
+{
+       bool status;
+
+       status = (qlcnic_sriov_pf_check(adapter) ||
+                 qlcnic_sriov_vf_check(adapter)) ? true : false;
+
+       return status;
+}
+
 static inline u32 qlcnic_get_vnic_func_count(struct qlcnic_adapter *adapter)
 {
        if (qlcnic_84xx_check(adapter))
@@ -2345,4 +2361,18 @@ static inline u32 qlcnic_get_vnic_func_count(struct qlcnic_adapter *adapter)
        else
                return QLC_DEFAULT_VNIC_COUNT;
 }
+
+#ifdef CONFIG_QLCNIC_HWMON
+void qlcnic_register_hwmon_dev(struct qlcnic_adapter *);
+void qlcnic_unregister_hwmon_dev(struct qlcnic_adapter *);
+#else
+static inline void qlcnic_register_hwmon_dev(struct qlcnic_adapter *adapter)
+{
+       return;
+}
+static inline void qlcnic_unregister_hwmon_dev(struct qlcnic_adapter *adapter)
+{
+       return;
+}
+#endif
 #endif                         /* __QLCNIC_H_ */
index b7cffb46a75dbd8f215752218a6f1f4239c1cc98..a4a4ec0b68f8d5e9d7b0c6f3ed5050b5787a37c4 100644 (file)
@@ -33,6 +33,7 @@ static void qlcnic_83xx_get_beacon_state(struct qlcnic_adapter *);
 #define RSS_HASHTYPE_IP_TCP            0x3
 #define QLC_83XX_FW_MBX_CMD            0
 #define QLC_SKIP_INACTIVE_PCI_REGS     7
+#define QLC_MAX_LEGACY_FUNC_SUPP       8
 
 static const struct qlcnic_mailbox_metadata qlcnic_83xx_mbx_tbl[] = {
        {QLCNIC_CMD_CONFIGURE_IP_ADDR, 6, 1},
@@ -357,8 +358,15 @@ int qlcnic_83xx_setup_intr(struct qlcnic_adapter *adapter)
        if (!ahw->intr_tbl)
                return -ENOMEM;
 
-       if (!(adapter->flags & QLCNIC_MSIX_ENABLED))
+       if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) {
+               if (adapter->ahw->pci_func >= QLC_MAX_LEGACY_FUNC_SUPP) {
+                       dev_err(&adapter->pdev->dev, "PCI function number 8 and higher are not supported with legacy interrupt, func 0x%x\n",
+                               ahw->pci_func);
+                       return -EOPNOTSUPP;
+               }
+
                qlcnic_83xx_enable_legacy(adapter);
+       }
 
        for (i = 0; i < num_msix; i++) {
                if (adapter->flags & QLCNIC_MSIX_ENABLED)
@@ -879,6 +887,9 @@ int qlcnic_83xx_alloc_mbx_args(struct qlcnic_cmd_args *mbx,
                        return 0;
                }
        }
+
+       dev_err(&adapter->pdev->dev, "%s: Invalid mailbox command opcode 0x%x\n",
+               __func__, type);
        return -EINVAL;
 }
 
@@ -3026,19 +3037,18 @@ void qlcnic_83xx_unlock_driver(struct qlcnic_adapter *adapter)
        QLCRDX(adapter->ahw, QLC_83XX_DRV_UNLOCK);
 }
 
-int qlcnic_83xx_ms_mem_write128(struct qlcnic_adapter *adapter, u64 addr,
+int qlcnic_ms_mem_write128(struct qlcnic_adapter *adapter, u64 addr,
                                u32 *data, u32 count)
 {
        int i, j, ret = 0;
        u32 temp;
-       int err = 0;
 
        /* Check alignment */
        if (addr & 0xF)
                return -EIO;
 
        mutex_lock(&adapter->ahw->mem_lock);
-       qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_ADDR_HI, 0);
+       qlcnic_ind_wr(adapter, QLCNIC_MS_ADDR_HI, 0);
 
        for (i = 0; i < count; i++, addr += 16) {
                if (!((ADDR_IN_RANGE(addr, QLCNIC_ADDR_QDR_NET,
@@ -3049,26 +3059,16 @@ int qlcnic_83xx_ms_mem_write128(struct qlcnic_adapter *adapter, u64 addr,
                        return -EIO;
                }
 
-               qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_ADDR_LO, addr);
-               qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_WRTDATA_LO,
-                                            *data++);
-               qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_WRTDATA_HI,
-                                            *data++);
-               qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_WRTDATA_ULO,
-                                            *data++);
-               qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_WRTDATA_UHI,
-                                            *data++);
-               qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_CTRL,
-                                            QLCNIC_TA_WRITE_ENABLE);
-               qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_CTRL,
-                                            QLCNIC_TA_WRITE_START);
+               qlcnic_ind_wr(adapter, QLCNIC_MS_ADDR_LO, addr);
+               qlcnic_ind_wr(adapter, QLCNIC_MS_WRTDATA_LO, *data++);
+               qlcnic_ind_wr(adapter, QLCNIC_MS_WRTDATA_HI, *data++);
+               qlcnic_ind_wr(adapter, QLCNIC_MS_WRTDATA_ULO, *data++);
+               qlcnic_ind_wr(adapter, QLCNIC_MS_WRTDATA_UHI, *data++);
+               qlcnic_ind_wr(adapter, QLCNIC_MS_CTRL, QLCNIC_TA_WRITE_ENABLE);
+               qlcnic_ind_wr(adapter, QLCNIC_MS_CTRL, QLCNIC_TA_WRITE_START);
 
                for (j = 0; j < MAX_CTL_CHECK; j++) {
-                       temp = QLCRD32(adapter, QLCNIC_MS_CTRL, &err);
-                       if (err == -EIO) {
-                               mutex_unlock(&adapter->ahw->mem_lock);
-                               return err;
-                       }
+                       temp = qlcnic_ind_rd(adapter, QLCNIC_MS_CTRL);
 
                        if ((temp & TA_CTL_BUSY) == 0)
                                break;
index 88d809c356334675026fb1a71e37107ded60c709..2bf101a47d02db1afbd153e032aab728f2ecb159 100644 (file)
@@ -418,7 +418,6 @@ enum qlcnic_83xx_states {
 #define QLC_83XX_GET_FUNC_MODE_FROM_NPAR_INFO(val)     (val & 0x80000000)
 #define QLC_83XX_GET_LRO_CAPABILITY(val)               (val & 0x20)
 #define QLC_83XX_GET_LSO_CAPABILITY(val)               (val & 0x40)
-#define QLC_83XX_GET_LSO_CAPABILITY(val)               (val & 0x40)
 #define QLC_83XX_GET_HW_LRO_CAPABILITY(val)            (val & 0x400)
 #define QLC_83XX_GET_VLAN_ALIGN_CAPABILITY(val)        (val & 0x4000)
 #define QLC_83XX_GET_FW_LRO_MSS_CAPABILITY(val)        (val & 0x20000)
@@ -560,7 +559,7 @@ void qlcnic_83xx_napi_del(struct qlcnic_adapter *);
 void qlcnic_83xx_napi_enable(struct qlcnic_adapter *);
 void qlcnic_83xx_napi_disable(struct qlcnic_adapter *);
 int qlcnic_83xx_config_led(struct qlcnic_adapter *, u32, u32);
-void qlcnic_ind_wr(struct qlcnic_adapter *, u32, u32);
+int qlcnic_ind_wr(struct qlcnic_adapter *, u32, u32);
 int qlcnic_ind_rd(struct qlcnic_adapter *, u32);
 int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *);
 int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *,
@@ -617,7 +616,6 @@ void qlcnic_83xx_idc_request_reset(struct qlcnic_adapter *, u32);
 int qlcnic_83xx_lock_driver(struct qlcnic_adapter *);
 void qlcnic_83xx_unlock_driver(struct qlcnic_adapter *);
 int qlcnic_83xx_set_default_offload_settings(struct qlcnic_adapter *);
-int qlcnic_83xx_ms_mem_write128(struct qlcnic_adapter *, u64, u32 *, u32);
 int qlcnic_83xx_idc_vnic_pf_entry(struct qlcnic_adapter *);
 int qlcnic_83xx_disable_vnic_mode(struct qlcnic_adapter *, int);
 int qlcnic_83xx_config_vnic_opmode(struct qlcnic_adapter *);
@@ -659,4 +657,5 @@ void qlcnic_83xx_cache_tmpl_hdr_values(struct qlcnic_fw_dump *);
 u32 qlcnic_83xx_get_cap_size(void *, int);
 void qlcnic_83xx_set_sys_info(void *, int, u32);
 void qlcnic_83xx_store_cap_mask(void *, u32);
+int qlcnic_ms_mem_write128(struct qlcnic_adapter *, u64, u32 *, u32);
 #endif
index ba20c721ee97f59d05f18a126471cb4a4a277f0b..f33559b725283cf69b08e80a8179fb488b89acb2 100644 (file)
@@ -1363,8 +1363,8 @@ static int qlcnic_83xx_copy_bootloader(struct qlcnic_adapter *adapter)
                return ret;
        }
        /* 16 byte write to MS memory */
-       ret = qlcnic_83xx_ms_mem_write128(adapter, dest, (u32 *)p_cache,
-                                         size / 16);
+       ret = qlcnic_ms_mem_write128(adapter, dest, (u32 *)p_cache,
+                                    size / 16);
        if (ret) {
                vfree(p_cache);
                return ret;
@@ -1389,8 +1389,8 @@ static int qlcnic_83xx_copy_fw_file(struct qlcnic_adapter *adapter)
        p_cache = (u32 *)fw->data;
        addr = (u64)dest;
 
-       ret = qlcnic_83xx_ms_mem_write128(adapter, addr,
-                                         p_cache, size / 16);
+       ret = qlcnic_ms_mem_write128(adapter, addr,
+                                    p_cache, size / 16);
        if (ret) {
                dev_err(&adapter->pdev->dev, "MS memory write failed\n");
                release_firmware(fw);
@@ -1405,8 +1405,8 @@ static int qlcnic_83xx_copy_fw_file(struct qlcnic_adapter *adapter)
                        data[i] = fw->data[size + i];
                for (; i < 16; i++)
                        data[i] = 0;
-               ret = qlcnic_83xx_ms_mem_write128(adapter, addr,
-                                                 (u32 *)data, 1);
+               ret = qlcnic_ms_mem_write128(adapter, addr,
+                                            (u32 *)data, 1);
                if (ret) {
                        dev_err(&adapter->pdev->dev,
                                "MS memory write failed\n");
@@ -2181,6 +2181,8 @@ int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter)
                max_sds_rings = QLCNIC_MAX_SDS_RINGS;
                max_tx_rings = QLCNIC_MAX_TX_RINGS;
        } else {
+               dev_err(&adapter->pdev->dev, "%s: Invalid opmode %d\n",
+                       __func__, ret);
                return -EIO;
        }
 
index c1e11f5715b056c0e90ba096de8f397eb50fce97..304e247bdf339c59b30c816839da1bb0ccb9a6ac 100644 (file)
@@ -1027,8 +1027,11 @@ int qlcnic_config_port_mirroring(struct qlcnic_adapter *adapter, u8 id,
        u32 arg1;
 
        if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC ||
-           !(adapter->eswitch[id].flags & QLCNIC_SWITCH_ENABLE))
+           !(adapter->eswitch[id].flags & QLCNIC_SWITCH_ENABLE)) {
+               dev_err(&adapter->pdev->dev, "%s: Not a management function\n",
+                       __func__);
                return err;
+       }
 
        arg1 = id | (enable_mirroring ? BIT_4 : 0);
        arg1 |= pci_func << 8;
@@ -1318,8 +1321,12 @@ int qlcnic_config_switch_port(struct qlcnic_adapter *adapter,
        u32 arg1, arg2 = 0;
        u8 pci_func;
 
-       if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC)
+       if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC) {
+               dev_err(&adapter->pdev->dev, "%s: Not a management function\n",
+                       __func__);
                return err;
+       }
+
        pci_func = esw_cfg->pci_func;
        index = qlcnic_is_valid_nic_func(adapter, pci_func);
        if (index < 0)
@@ -1363,6 +1370,8 @@ int qlcnic_config_switch_port(struct qlcnic_adapter *adapter,
                        arg1 &= ~(0x0ffff << 16);
                        break;
        default:
+               dev_err(&adapter->pdev->dev, "%s: Invalid opmode 0x%x\n",
+                       __func__, esw_cfg->op_mode);
                return err;
        }
 
index 5bacf5210aed658abc12c639be38f0649bf96cbf..1b7f3dbae2899d9ade666176a7cfdb0e21a82d6f 100644 (file)
@@ -726,6 +726,11 @@ static int qlcnic_set_channels(struct net_device *dev,
        struct qlcnic_adapter *adapter = netdev_priv(dev);
        int err;
 
+       if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) {
+               netdev_err(dev, "No RSS/TSS support in non MSI-X mode\n");
+               return -EINVAL;
+       }
+
        if (channel->other_count || channel->combined_count)
                return -EINVAL;
 
@@ -734,7 +739,7 @@ static int qlcnic_set_channels(struct net_device *dev,
        if (err)
                return err;
 
-       if (channel->rx_count) {
+       if (adapter->drv_sds_rings != channel->rx_count) {
                err = qlcnic_validate_rings(adapter, channel->rx_count,
                                            QLCNIC_RX_QUEUE);
                if (err) {
@@ -745,7 +750,7 @@ static int qlcnic_set_channels(struct net_device *dev,
                adapter->drv_rss_rings = channel->rx_count;
        }
 
-       if (channel->tx_count) {
+       if (adapter->drv_tx_rings != channel->tx_count) {
                err = qlcnic_validate_rings(adapter, channel->tx_count,
                                            QLCNIC_TX_QUEUE);
                if (err) {
index 9f3adf4e70b5f31a2d143c8d946ce6d88201a096..851cb4a80d50a6d4b2733ac2693fd799cca37885 100644 (file)
@@ -373,12 +373,16 @@ int qlcnic_ind_rd(struct qlcnic_adapter *adapter, u32 addr)
        return data;
 }
 
-void qlcnic_ind_wr(struct qlcnic_adapter *adapter, u32 addr, u32 data)
+int qlcnic_ind_wr(struct qlcnic_adapter *adapter, u32 addr, u32 data)
 {
+       int ret = 0;
+
        if (qlcnic_82xx_check(adapter))
                qlcnic_write_window_reg(addr, adapter->ahw->pci_base0, data);
        else
-               qlcnic_83xx_wrt_reg_indirect(adapter, addr, data);
+               ret = qlcnic_83xx_wrt_reg_indirect(adapter, addr, data);
+
+       return ret;
 }
 
 static int
@@ -567,28 +571,14 @@ static void __qlcnic_set_multi(struct net_device *netdev, u16 vlan)
 void qlcnic_set_multi(struct net_device *netdev)
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
-       struct qlcnic_mac_vlan_list *cur;
-       struct netdev_hw_addr *ha;
-       size_t temp;
 
        if (!test_bit(__QLCNIC_FW_ATTACHED, &adapter->state))
                return;
-       if (qlcnic_sriov_vf_check(adapter)) {
-               if (!netdev_mc_empty(netdev)) {
-                       netdev_for_each_mc_addr(ha, netdev) {
-                               temp = sizeof(struct qlcnic_mac_vlan_list);
-                               cur = kzalloc(temp, GFP_ATOMIC);
-                               if (cur == NULL)
-                                       break;
-                               memcpy(cur->mac_addr,
-                                      ha->addr, ETH_ALEN);
-                               list_add_tail(&cur->list, &adapter->vf_mc_list);
-                       }
-               }
-               qlcnic_sriov_vf_schedule_multi(adapter->netdev);
-               return;
-       }
-       __qlcnic_set_multi(netdev, 0);
+
+       if (qlcnic_sriov_vf_check(adapter))
+               qlcnic_sriov_vf_set_multi(netdev);
+       else
+               __qlcnic_set_multi(netdev, 0);
 }
 
 int qlcnic_82xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode)
@@ -630,7 +620,7 @@ void qlcnic_prune_lb_filters(struct qlcnic_adapter *adapter)
        struct hlist_node *n;
        struct hlist_head *head;
        int i;
-       unsigned long time;
+       unsigned long expires;
        u8 cmd;
 
        for (i = 0; i < adapter->fhash.fbucket_size; i++) {
@@ -638,8 +628,8 @@ void qlcnic_prune_lb_filters(struct qlcnic_adapter *adapter)
                hlist_for_each_entry_safe(tmp_fil, n, head, fnode) {
                        cmd =  tmp_fil->vlan_id ? QLCNIC_MAC_VLAN_DEL :
                                                  QLCNIC_MAC_DEL;
-                       time = tmp_fil->ftime;
-                       if (jiffies > (QLCNIC_FILTER_AGE * HZ + time)) {
+                       expires = tmp_fil->ftime + QLCNIC_FILTER_AGE * HZ;
+                       if (time_before(expires, jiffies)) {
                                qlcnic_sre_macaddr_change(adapter,
                                                          tmp_fil->faddr,
                                                          tmp_fil->vlan_id,
@@ -657,8 +647,8 @@ void qlcnic_prune_lb_filters(struct qlcnic_adapter *adapter)
 
                hlist_for_each_entry_safe(tmp_fil, n, head, fnode)
                {
-                       time = tmp_fil->ftime;
-                       if (jiffies > (QLCNIC_FILTER_AGE * HZ + time)) {
+                       expires = tmp_fil->ftime + QLCNIC_FILTER_AGE * HZ;
+                       if (time_before(expires, jiffies)) {
                                spin_lock_bh(&adapter->rx_mac_learn_lock);
                                adapter->rx_fhash.fnum--;
                                hlist_del(&tmp_fil->fnode);
index 173b3d12991f55a62751d5e6a213d20ee02c3174..e45bf09af0c9fe4dbe9cdc629370af792ab88c01 100644 (file)
@@ -305,7 +305,6 @@ static void qlcnic_send_filter(struct qlcnic_adapter *adapter,
 {
        struct vlan_ethhdr *vh = (struct vlan_ethhdr *)(skb->data);
        struct ethhdr *phdr = (struct ethhdr *)(skb->data);
-       struct net_device *netdev = adapter->netdev;
        u16 protocol = ntohs(skb->protocol);
        struct qlcnic_filter *fil, *tmp_fil;
        struct hlist_head *head;
@@ -314,27 +313,16 @@ static void qlcnic_send_filter(struct qlcnic_adapter *adapter,
        u16 vlan_id = 0;
        u8 hindex, hval;
 
-       if (!qlcnic_sriov_pf_check(adapter)) {
-               if (ether_addr_equal(phdr->h_source, adapter->mac_addr))
-                       return;
-       } else {
+       if (ether_addr_equal(phdr->h_source, adapter->mac_addr))
+               return;
+
+       if (adapter->flags & QLCNIC_VLAN_FILTERING) {
                if (protocol == ETH_P_8021Q) {
                        vh = (struct vlan_ethhdr *)skb->data;
                        vlan_id = ntohs(vh->h_vlan_TCI);
                } else if (vlan_tx_tag_present(skb)) {
                        vlan_id = vlan_tx_tag_get(skb);
                }
-
-               if (ether_addr_equal(phdr->h_source, adapter->mac_addr) &&
-                   !vlan_id)
-                       return;
-       }
-
-       if (adapter->fhash.fnum >= adapter->fhash.fmax) {
-               adapter->stats.mac_filter_limit_overrun++;
-               netdev_info(netdev, "Can not add more than %d mac-vlan filters, configured %d\n",
-                           adapter->fhash.fmax, adapter->fhash.fnum);
-               return;
        }
 
        memcpy(&src_addr, phdr->h_source, ETH_ALEN);
@@ -353,6 +341,11 @@ static void qlcnic_send_filter(struct qlcnic_adapter *adapter,
                }
        }
 
+       if (unlikely(adapter->fhash.fnum >= adapter->fhash.fmax)) {
+               adapter->stats.mac_filter_limit_overrun++;
+               return;
+       }
+
        fil = kzalloc(sizeof(struct qlcnic_filter), GFP_ATOMIC);
        if (!fil)
                return;
@@ -1216,8 +1209,7 @@ qlcnic_process_rcv(struct qlcnic_adapter *adapter,
        if (!skb)
                return buffer;
 
-       if (adapter->drv_mac_learn &&
-           (adapter->flags & QLCNIC_ESWITCH_ENABLED)) {
+       if (adapter->rx_mac_learn) {
                t_vid = 0;
                is_lb_pkt = qlcnic_82xx_is_lb_pkt(sts_data0);
                qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, t_vid);
@@ -1293,8 +1285,7 @@ qlcnic_process_lro(struct qlcnic_adapter *adapter,
        if (!skb)
                return buffer;
 
-       if (adapter->drv_mac_learn &&
-           (adapter->flags & QLCNIC_ESWITCH_ENABLED)) {
+       if (adapter->rx_mac_learn) {
                t_vid = 0;
                is_lb_pkt = qlcnic_82xx_is_lb_pkt(sts_data0);
                qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, t_vid);
index 7e55e88a81bf26ede0928225fa85998254280195..4fc186713b660c8d823744f842cf3551dd660092 100644 (file)
@@ -378,7 +378,8 @@ static int qlcnic_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
        if (!adapter->fdb_mac_learn)
                return ndo_dflt_fdb_del(ndm, tb, netdev, addr);
 
-       if (adapter->flags & QLCNIC_ESWITCH_ENABLED) {
+       if ((adapter->flags & QLCNIC_ESWITCH_ENABLED) ||
+           qlcnic_sriov_check(adapter)) {
                if (is_unicast_ether_addr(addr)) {
                        err = dev_uc_del(netdev, addr);
                        if (!err)
@@ -402,7 +403,8 @@ static int qlcnic_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
        if (!adapter->fdb_mac_learn)
                return ndo_dflt_fdb_add(ndm, tb, netdev, addr, flags);
 
-       if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) {
+       if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED) &&
+           !qlcnic_sriov_check(adapter)) {
                pr_info("%s: FDB e-switch is not enabled\n", __func__);
                return -EOPNOTSUPP;
        }
@@ -432,7 +434,8 @@ static int qlcnic_fdb_dump(struct sk_buff *skb, struct netlink_callback *ncb,
        if (!adapter->fdb_mac_learn)
                return ndo_dflt_fdb_dump(skb, ncb, netdev, idx);
 
-       if (adapter->flags & QLCNIC_ESWITCH_ENABLED)
+       if ((adapter->flags & QLCNIC_ESWITCH_ENABLED) ||
+           qlcnic_sriov_check(adapter))
                idx = ndo_dflt_fdb_dump(skb, ncb, netdev, idx);
 
        return idx;
@@ -522,7 +525,7 @@ static const struct net_device_ops qlcnic_netdev_ops = {
 #endif
 #ifdef CONFIG_QLCNIC_SRIOV
        .ndo_set_vf_mac         = qlcnic_sriov_set_vf_mac,
-       .ndo_set_vf_tx_rate     = qlcnic_sriov_set_vf_tx_rate,
+       .ndo_set_vf_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,
@@ -690,10 +693,10 @@ int qlcnic_setup_tss_rss_intr(struct qlcnic_adapter *adapter)
                adapter->msix_entries[vector].entry = vector;
 
 restore:
-       err = pci_enable_msix(pdev, adapter->msix_entries, num_msix);
-       if (err > 0) {
+       err = pci_enable_msix_exact(pdev, adapter->msix_entries, num_msix);
+       if (err == -ENOSPC) {
                if (!adapter->drv_tss_rings && !adapter->drv_rss_rings)
-                       return -ENOSPC;
+                       return err;
 
                netdev_info(adapter->netdev,
                            "Unable to allocate %d MSI-X vectors, Available vectors %d\n",
@@ -1014,6 +1017,8 @@ int qlcnic_init_pci_info(struct qlcnic_adapter *adapter)
 
                if (pfn >= ahw->max_vnic_func) {
                        ret = QL_STATUS_INVALID_PARAM;
+                       dev_err(&adapter->pdev->dev, "%s: Invalid function 0x%x, max 0x%x\n",
+                               __func__, pfn, ahw->max_vnic_func);
                        goto err_eswitch;
                }
 
@@ -1915,8 +1920,6 @@ void __qlcnic_down(struct qlcnic_adapter *adapter, struct net_device *netdev)
        if (!test_and_clear_bit(__QLCNIC_DEV_UP, &adapter->state))
                return;
 
-       if (qlcnic_sriov_vf_check(adapter))
-               qlcnic_sriov_cleanup_async_list(&adapter->ahw->sriov->bc);
        smp_mb();
        netif_carrier_off(netdev);
        adapter->ahw->linkup = 0;
@@ -1928,6 +1931,8 @@ void __qlcnic_down(struct qlcnic_adapter *adapter, struct net_device *netdev)
                qlcnic_delete_lb_filters(adapter);
 
        qlcnic_nic_set_promisc(adapter, QLCNIC_NIU_NON_PROMISC_MODE);
+       if (qlcnic_sriov_vf_check(adapter))
+               qlcnic_sriov_cleanup_async_list(&adapter->ahw->sriov->bc);
 
        qlcnic_napi_disable(adapter);
 
@@ -2052,6 +2057,7 @@ out:
 
 static int qlcnic_alloc_adapter_resources(struct qlcnic_adapter *adapter)
 {
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
        int err = 0;
 
        adapter->recv_ctx = kzalloc(sizeof(struct qlcnic_recv_context),
@@ -2061,6 +2067,18 @@ static int qlcnic_alloc_adapter_resources(struct qlcnic_adapter *adapter)
                goto err_out;
        }
 
+       if (qlcnic_83xx_check(adapter)) {
+               ahw->coal.type = QLCNIC_INTR_COAL_TYPE_RX_TX;
+               ahw->coal.tx_time_us = QLCNIC_DEF_INTR_COALESCE_TX_TIME_US;
+               ahw->coal.tx_packets = QLCNIC_DEF_INTR_COALESCE_TX_PACKETS;
+               ahw->coal.rx_time_us = QLCNIC_DEF_INTR_COALESCE_RX_TIME_US;
+               ahw->coal.rx_packets = QLCNIC_DEF_INTR_COALESCE_RX_PACKETS;
+       } else {
+               ahw->coal.type = QLCNIC_INTR_COAL_TYPE_RX;
+               ahw->coal.rx_time_us = QLCNIC_DEF_INTR_COALESCE_RX_TIME_US;
+               ahw->coal.rx_packets = QLCNIC_DEF_INTR_COALESCE_RX_PACKETS;
+       }
+
        /* clear stats */
        memset(&adapter->stats, 0, sizeof(adapter->stats));
 err_out:
@@ -2069,12 +2087,20 @@ err_out:
 
 static void qlcnic_free_adapter_resources(struct qlcnic_adapter *adapter)
 {
+       struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
+
        kfree(adapter->recv_ctx);
        adapter->recv_ctx = NULL;
 
-       if (adapter->ahw->fw_dump.tmpl_hdr) {
-               vfree(adapter->ahw->fw_dump.tmpl_hdr);
-               adapter->ahw->fw_dump.tmpl_hdr = NULL;
+       if (fw_dump->tmpl_hdr) {
+               vfree(fw_dump->tmpl_hdr);
+               fw_dump->tmpl_hdr = NULL;
+       }
+
+       if (fw_dump->dma_buffer) {
+               dma_free_coherent(&adapter->pdev->dev, QLC_PEX_DMA_READ_SIZE,
+                                 fw_dump->dma_buffer, fw_dump->phys_addr);
+               fw_dump->dma_buffer = NULL;
        }
 
        kfree(adapter->ahw->reset.buff);
@@ -2247,10 +2273,8 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev,
 
        qlcnic_change_mtu(netdev, netdev->mtu);
 
-       if (qlcnic_sriov_vf_check(adapter))
-               SET_ETHTOOL_OPS(netdev, &qlcnic_sriov_vf_ethtool_ops);
-       else
-               SET_ETHTOOL_OPS(netdev, &qlcnic_ethtool_ops);
+       netdev->ethtool_ops = (qlcnic_sriov_vf_check(adapter)) ?
+               &qlcnic_sriov_vf_ethtool_ops : &qlcnic_ethtool_ops;
 
        netdev->features |= (NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
                             NETIF_F_IPV6_CSUM | NETIF_F_GRO |
@@ -2417,9 +2441,6 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        int err, pci_using_dac = -1;
        char board_name[QLCNIC_MAX_BOARD_NAME_LEN + 19]; /* MAC + ": " + name */
 
-       if (pdev->is_virtfn)
-               return -ENODEV;
-
        err = pci_enable_device(pdev);
        if (err)
                return err;
@@ -2552,9 +2573,11 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                        case -ENOMEM:
                                dev_err(&pdev->dev, "Adapter initialization failed. Please reboot\n");
                                goto err_out_free_hw;
+                       case -EOPNOTSUPP:
+                               dev_err(&pdev->dev, "Adapter initialization failed\n");
+                               goto err_out_free_hw;
                        default:
-                               dev_err(&pdev->dev, "Adapter initialization failed. A reboot may be required to recover from this failure\n");
-                               dev_err(&pdev->dev, "If reboot does not help to recover from this failure, try a flash update of the adapter\n");
+                               dev_err(&pdev->dev, "Adapter initialization failed. Driver will load in maintenance mode to recover the adapter using the application\n");
                                goto err_out_maintenance_mode;
                        }
                }
@@ -2628,7 +2651,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                qlcnic_alloc_lb_filters_mem(adapter);
 
        qlcnic_add_sysfs(adapter);
-
+       qlcnic_register_hwmon_dev(adapter);
        return 0;
 
 err_out_disable_mbx_intr:
@@ -2665,7 +2688,7 @@ err_out_disable_pdev:
 err_out_maintenance_mode:
        set_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state);
        netdev->netdev_ops = &qlcnic_netdev_failed_ops;
-       SET_ETHTOOL_OPS(netdev, &qlcnic_ethtool_failed_ops);
+       netdev->ethtool_ops = &qlcnic_ethtool_failed_ops;
        ahw->port_type = QLCNIC_XGBE;
 
        if (qlcnic_83xx_check(adapter))
@@ -2698,9 +2721,9 @@ static void qlcnic_remove(struct pci_dev *pdev)
                return;
 
        netdev = adapter->netdev;
-       qlcnic_sriov_pf_disable(adapter);
 
        qlcnic_cancel_idc_work(adapter);
+       qlcnic_sriov_pf_disable(adapter);
        ahw = adapter->ahw;
 
        unregister_netdev(netdev);
@@ -2735,6 +2758,8 @@ static void qlcnic_remove(struct pci_dev *pdev)
 
        qlcnic_remove_sysfs(adapter);
 
+       qlcnic_unregister_hwmon_dev(adapter);
+
        qlcnic_cleanup_pci_map(adapter->ahw);
 
        qlcnic_release_firmware(adapter);
@@ -2828,6 +2853,8 @@ static int qlcnic_close(struct net_device *netdev)
        return 0;
 }
 
+#define QLCNIC_VF_LB_BUCKET_SIZE 1
+
 void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter)
 {
        void *head;
@@ -2843,7 +2870,10 @@ void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter)
        spin_lock_init(&adapter->mac_learn_lock);
        spin_lock_init(&adapter->rx_mac_learn_lock);
 
-       if (qlcnic_82xx_check(adapter)) {
+       if (qlcnic_sriov_vf_check(adapter)) {
+               filter_size = QLCNIC_83XX_SRIOV_VF_MAX_MAC - 1;
+               adapter->fhash.fbucket_size = QLCNIC_VF_LB_BUCKET_SIZE;
+       } else if (qlcnic_82xx_check(adapter)) {
                filter_size = QLCNIC_LB_MAX_FILTERS;
                adapter->fhash.fbucket_size = QLCNIC_LB_BUCKET_SIZE;
        } else {
@@ -3973,16 +4003,6 @@ int qlcnic_validate_rings(struct qlcnic_adapter *adapter, __u32 ring_cnt,
                strcpy(buf, "Tx");
        }
 
-       if (!QLCNIC_IS_MSI_FAMILY(adapter)) {
-               netdev_err(netdev, "No RSS/TSS support in INT-x mode\n");
-               return -EINVAL;
-       }
-
-       if (adapter->flags & QLCNIC_MSI_ENABLED) {
-               netdev_err(netdev, "No RSS/TSS support in MSI mode\n");
-               return -EINVAL;
-       }
-
        if (!is_power_of_2(ring_cnt)) {
                netdev_err(netdev, "%s rings value should be a power of 2\n",
                           buf);
@@ -4122,7 +4142,7 @@ void qlcnic_restore_indev_addr(struct net_device *netdev, unsigned long event)
 
        rcu_read_lock();
        for_each_set_bit(vid, adapter->vlans, VLAN_N_VID) {
-               dev = __vlan_find_dev_deep(netdev, htons(ETH_P_8021Q), vid);
+               dev = __vlan_find_dev_deep_rcu(netdev, htons(ETH_P_8021Q), vid);
                if (!dev)
                        continue;
                qlcnic_config_indev_addr(adapter, dev, event);
index 37b979b1266bc2e0aa552f84dd8a14a149c82665..e46fc39d425d45ee1d0d081b45954801c46f89ed 100644 (file)
@@ -238,6 +238,8 @@ void qlcnic_82xx_cache_tmpl_hdr_values(struct qlcnic_fw_dump *fw_dump)
 
        hdr->drv_cap_mask = hdr->cap_mask;
        fw_dump->cap_mask = hdr->cap_mask;
+
+       fw_dump->use_pex_dma = (hdr->capabilities & BIT_0) ? true : false;
 }
 
 inline u32 qlcnic_82xx_get_cap_size(void *t_hdr, int index)
@@ -276,6 +278,8 @@ inline void qlcnic_83xx_set_saved_state(void *t_hdr, u32 index,
        hdr->saved_state[index] = value;
 }
 
+#define QLCNIC_TEMPLATE_VERSION (0x20001)
+
 void qlcnic_83xx_cache_tmpl_hdr_values(struct qlcnic_fw_dump *fw_dump)
 {
        struct qlcnic_83xx_dump_template_hdr *hdr;
@@ -288,6 +292,9 @@ void qlcnic_83xx_cache_tmpl_hdr_values(struct qlcnic_fw_dump *fw_dump)
 
        hdr->drv_cap_mask = hdr->cap_mask;
        fw_dump->cap_mask = hdr->cap_mask;
+
+       fw_dump->use_pex_dma = (fw_dump->version & 0xfffff) >=
+                              QLCNIC_TEMPLATE_VERSION;
 }
 
 inline u32 qlcnic_83xx_get_cap_size(void *t_hdr, int index)
@@ -653,34 +660,31 @@ out:
 #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_83xx_dump_template_hdr *tmpl_hdr;
        struct device *dev = &adapter->pdev->dev;
        u32 dma_no, dma_base_addr, temp_addr;
        int i, ret, dma_sts;
+       void *tmpl_hdr;
 
        tmpl_hdr = adapter->ahw->fw_dump.tmpl_hdr;
-       dma_no = tmpl_hdr->saved_state[QLC_83XX_DMA_ENGINE_INDEX];
+       dma_no = qlcnic_get_saved_state(adapter, tmpl_hdr,
+                                       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);
+       ret = qlcnic_ind_wr(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);
+       ret = qlcnic_ind_wr(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);
+       ret = qlcnic_ind_wr(adapter, temp_addr, mem->start_dma_cmd);
        if (ret)
                return ret;
 
@@ -710,15 +714,16 @@ static u32 qlcnic_read_memory_pexdma(struct qlcnic_adapter *adapter,
        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_83xx_dump_template_hdr *tmpl_hdr;
        struct device *dev = &adapter->pdev->dev;
        dma_addr_t dma_phys_addr;
        void *dma_buffer;
+       void *tmpl_hdr;
 
        tmpl_hdr = fw_dump->tmpl_hdr;
 
        /* Check if DMA engine is available */
-       temp = tmpl_hdr->saved_state[QLC_83XX_DMA_ENGINE_INDEX];
+       temp = qlcnic_get_saved_state(adapter, tmpl_hdr,
+                                     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);
@@ -764,8 +769,8 @@ static u32 qlcnic_read_memory_pexdma(struct qlcnic_adapter *adapter,
 
                /* 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);
+               *ret = qlcnic_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);
@@ -1141,8 +1146,6 @@ free_mem:
        return err;
 }
 
-#define QLCNIC_TEMPLATE_VERSION (0x20001)
-
 int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter)
 {
        struct qlcnic_hardware_context *ahw;
@@ -1150,6 +1153,7 @@ int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter)
        u32 version, csum, *tmp_buf;
        u8 use_flash_temp = 0;
        u32 temp_size = 0;
+       void *temp_buffer;
        int err;
 
        ahw = adapter->ahw;
@@ -1199,16 +1203,23 @@ flash_temp:
 
        qlcnic_cache_tmpl_hdr_values(adapter, fw_dump);
 
+       if (fw_dump->use_pex_dma) {
+               fw_dump->dma_buffer = NULL;
+               temp_buffer = dma_alloc_coherent(&adapter->pdev->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;
+       }
+
+
        dev_info(&adapter->pdev->dev,
                 "Default minidump capture mask 0x%x\n",
                 fw_dump->cap_mask);
 
-       if (qlcnic_83xx_check(adapter) &&
-           (fw_dump->version & 0xfffff) >= QLCNIC_TEMPLATE_VERSION)
-               fw_dump->use_pex_dma = true;
-       else
-               fw_dump->use_pex_dma = false;
-
        qlcnic_enable_fw_dump_state(adapter);
 
        return 0;
@@ -1224,7 +1235,7 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
        struct device *dev = &adapter->pdev->dev;
        struct qlcnic_hardware_context *ahw;
        struct qlcnic_dump_entry *entry;
-       void *temp_buffer, *tmpl_hdr;
+       void *tmpl_hdr;
        u32 ocm_window;
        __le32 *buffer;
        char mesg[64];
@@ -1268,16 +1279,6 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
        qlcnic_set_sys_info(adapter, tmpl_hdr, 0, QLCNIC_DRIVER_VERSION);
        qlcnic_set_sys_info(adapter, tmpl_hdr, 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;
@@ -1335,10 +1336,6 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
        /* Send a udev event to notify availability of FW dump */
        kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, msg);
 
-       if (fw_dump->use_pex_dma)
-               dma_free_coherent(dev, QLC_PEX_DMA_READ_SIZE,
-                                 fw_dump->dma_buffer, fw_dump->phys_addr);
-
        return 0;
 }
 
index 396bd1fd1d277deb56d70e185280ed3172902a49..4677b2edccca79fa1072e8a67315add790271c93 100644 (file)
@@ -52,6 +52,7 @@ enum qlcnic_bc_commands {
        QLCNIC_BC_CMD_CFG_GUEST_VLAN = 0x3,
 };
 
+#define QLCNIC_83XX_SRIOV_VF_MAX_MAC 2
 #define QLC_BC_CMD 1
 
 struct qlcnic_trans_list {
@@ -151,13 +152,14 @@ struct qlcnic_vf_info {
        struct qlcnic_trans_list        rcv_pend;
        struct qlcnic_adapter           *adapter;
        struct qlcnic_vport             *vp;
-       struct mutex                    vlan_list_lock; /* Lock for VLAN list */
+       spinlock_t                      vlan_list_lock; /* Lock for VLAN list */
 };
 
 struct qlcnic_async_work_list {
        struct list_head        list;
        struct work_struct      work;
        void                    *ptr;
+       struct qlcnic_cmd_args  *cmd;
 };
 
 struct qlcnic_back_channel {
@@ -231,7 +233,7 @@ bool qlcnic_sriov_soft_flr_check(struct qlcnic_adapter *,
 void qlcnic_sriov_pf_reset(struct qlcnic_adapter *);
 int qlcnic_sriov_pf_reinit(struct qlcnic_adapter *);
 int qlcnic_sriov_set_vf_mac(struct net_device *, int, u8 *);
-int qlcnic_sriov_set_vf_tx_rate(struct net_device *, int, int);
+int qlcnic_sriov_set_vf_tx_rate(struct net_device *, int, 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);
index 6afe9c1f5ab9337a3fa7c64e7863664af3db99b7..1659c804f1d5f4fc530f7d9b2ac11c7f9f023c6b 100644 (file)
@@ -39,6 +39,8 @@ static int qlcnic_sriov_channel_cfg_cmd(struct qlcnic_adapter *, u8);
 static void qlcnic_sriov_process_bc_cmd(struct work_struct *);
 static int qlcnic_sriov_vf_shutdown(struct pci_dev *);
 static int qlcnic_sriov_vf_resume(struct qlcnic_adapter *);
+static int qlcnic_sriov_async_issue_cmd(struct qlcnic_adapter *,
+                                       struct qlcnic_cmd_args *);
 
 static struct qlcnic_hardware_ops qlcnic_sriov_vf_hw_ops = {
        .read_crb                       = qlcnic_83xx_read_crb,
@@ -181,7 +183,7 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs)
                vf->adapter = adapter;
                vf->pci_func = qlcnic_sriov_virtid_fn(adapter, i);
                mutex_init(&vf->send_cmd_lock);
-               mutex_init(&vf->vlan_list_lock);
+               spin_lock_init(&vf->vlan_list_lock);
                INIT_LIST_HEAD(&vf->rcv_act.wait_list);
                INIT_LIST_HEAD(&vf->rcv_pend.wait_list);
                spin_lock_init(&vf->rcv_act.lock);
@@ -197,8 +199,10 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs)
                                goto qlcnic_destroy_async_wq;
                        }
                        sriov->vf_info[i].vp = vp;
+                       vp->vlan_mode = QLC_GUEST_VLAN_MODE;
                        vp->max_tx_bw = MAX_BW;
-                       vp->spoofchk = true;
+                       vp->min_tx_bw = MIN_BW;
+                       vp->spoofchk = false;
                        random_ether_addr(vp->mac);
                        dev_info(&adapter->pdev->dev,
                                 "MAC Address %pM is configured for VF %d\n",
@@ -454,6 +458,7 @@ static int qlcnic_sriov_get_vf_acl(struct qlcnic_adapter *adapter)
        struct qlcnic_cmd_args cmd;
        int ret = 0;
 
+       memset(&cmd, 0, sizeof(cmd));
        ret = qlcnic_sriov_alloc_bc_mbx_args(&cmd, QLCNIC_BC_CMD_GET_ACL);
        if (ret)
                return ret;
@@ -515,6 +520,8 @@ static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter,
 {
        int err;
 
+       adapter->flags |= QLCNIC_VLAN_FILTERING;
+       adapter->ahw->total_nic_func = 1;
        INIT_LIST_HEAD(&adapter->vf_mc_list);
        if (!qlcnic_use_msi_x && !!qlcnic_use_msi)
                dev_warn(&adapter->pdev->dev,
@@ -770,6 +777,7 @@ static int qlcnic_sriov_prepare_bc_hdr(struct qlcnic_bc_trans *trans,
                cmd->req.arg = (u32 *)trans->req_pay;
                cmd->rsp.arg = (u32 *)trans->rsp_pay;
                cmd_op = cmd->req.arg[0] & 0xff;
+               cmd->cmd_op = cmd_op;
                remainder = (trans->rsp_pay_size) % (bc_pay_sz);
                num_frags = (trans->rsp_pay_size) / (bc_pay_sz);
                if (remainder)
@@ -1356,7 +1364,7 @@ static int qlcnic_sriov_retry_bc_cmd(struct qlcnic_adapter *adapter,
        return -EIO;
 }
 
-static int qlcnic_sriov_issue_cmd(struct qlcnic_adapter *adapter,
+static int __qlcnic_sriov_issue_cmd(struct qlcnic_adapter *adapter,
                                  struct qlcnic_cmd_args *cmd)
 {
        struct qlcnic_hardware_context *ahw = adapter->ahw;
@@ -1408,12 +1416,17 @@ retry:
            (mbx_err_code == QLCNIC_MBX_PORT_RSP_OK)) {
                rsp = QLCNIC_RCODE_SUCCESS;
        } else {
-               rsp = mbx_err_code;
-               if (!rsp)
-                       rsp = 1;
-               dev_err(dev,
-                       "MBX command 0x%x failed with err:0x%x for VF %d\n",
-                       opcode, mbx_err_code, func);
+               if (cmd->type == QLC_83XX_MBX_CMD_NO_WAIT) {
+                       rsp = QLCNIC_RCODE_SUCCESS;
+               } else {
+                       rsp = mbx_err_code;
+                       if (!rsp)
+                               rsp = 1;
+
+                       dev_err(dev,
+                               "MBX command 0x%x failed with err:0x%x for VF %d\n",
+                               opcode, mbx_err_code, func);
+               }
        }
 
 err_out:
@@ -1435,12 +1448,23 @@ free_cmd:
        return rsp;
 }
 
+
+static int qlcnic_sriov_issue_cmd(struct qlcnic_adapter *adapter,
+                                 struct qlcnic_cmd_args *cmd)
+{
+       if (cmd->type == QLC_83XX_MBX_CMD_NO_WAIT)
+               return qlcnic_sriov_async_issue_cmd(adapter, cmd);
+       else
+               return __qlcnic_sriov_issue_cmd(adapter, cmd);
+}
+
 static int qlcnic_sriov_channel_cfg_cmd(struct qlcnic_adapter *adapter, u8 cmd_op)
 {
        struct qlcnic_cmd_args cmd;
        struct qlcnic_vf_info *vf = &adapter->ahw->sriov->vf_info[0];
        int ret;
 
+       memset(&cmd, 0, sizeof(cmd));
        if (qlcnic_sriov_alloc_bc_mbx_args(&cmd, cmd_op))
                return -ENOMEM;
 
@@ -1465,58 +1489,28 @@ out:
        return ret;
 }
 
-static void qlcnic_vf_add_mc_list(struct net_device *netdev)
+static void qlcnic_vf_add_mc_list(struct net_device *netdev, const u8 *mac)
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        struct qlcnic_sriov *sriov = adapter->ahw->sriov;
-       struct qlcnic_mac_vlan_list *cur;
-       struct list_head *head, tmp_list;
        struct qlcnic_vf_info *vf;
        u16 vlan_id;
        int i;
 
-       static const u8 bcast_addr[ETH_ALEN] = {
-               0xff, 0xff, 0xff, 0xff, 0xff, 0xff
-       };
-
        vf = &adapter->ahw->sriov->vf_info[0];
-       INIT_LIST_HEAD(&tmp_list);
-       head = &adapter->vf_mc_list;
-       netif_addr_lock_bh(netdev);
 
-       while (!list_empty(head)) {
-               cur = list_entry(head->next, struct qlcnic_mac_vlan_list, list);
-               list_move(&cur->list, &tmp_list);
-       }
-
-       netif_addr_unlock_bh(netdev);
-
-       while (!list_empty(&tmp_list)) {
-               cur = list_entry((&tmp_list)->next,
-                                struct qlcnic_mac_vlan_list, list);
-               if (!qlcnic_sriov_check_any_vlan(vf)) {
-                       qlcnic_nic_add_mac(adapter, bcast_addr, 0);
-                       qlcnic_nic_add_mac(adapter, cur->mac_addr, 0);
-               } else {
-                       mutex_lock(&vf->vlan_list_lock);
-                       for (i = 0; i < sriov->num_allowed_vlans; i++) {
-                               vlan_id = vf->sriov_vlans[i];
-                               if (vlan_id) {
-                                       qlcnic_nic_add_mac(adapter, bcast_addr,
-                                                          vlan_id);
-                                       qlcnic_nic_add_mac(adapter,
-                                                          cur->mac_addr,
-                                                          vlan_id);
-                               }
-                       }
-                       mutex_unlock(&vf->vlan_list_lock);
-                       if (qlcnic_84xx_check(adapter)) {
-                               qlcnic_nic_add_mac(adapter, bcast_addr, 0);
-                               qlcnic_nic_add_mac(adapter, cur->mac_addr, 0);
-                       }
+       if (!qlcnic_sriov_check_any_vlan(vf)) {
+               qlcnic_nic_add_mac(adapter, mac, 0);
+       } else {
+               spin_lock(&vf->vlan_list_lock);
+               for (i = 0; i < sriov->num_allowed_vlans; i++) {
+                       vlan_id = vf->sriov_vlans[i];
+                       if (vlan_id)
+                               qlcnic_nic_add_mac(adapter, mac, vlan_id);
                }
-               list_del(&cur->list);
-               kfree(cur);
+               spin_unlock(&vf->vlan_list_lock);
+               if (qlcnic_84xx_check(adapter))
+                       qlcnic_nic_add_mac(adapter, mac, 0);
        }
 }
 
@@ -1525,6 +1519,7 @@ void qlcnic_sriov_cleanup_async_list(struct qlcnic_back_channel *bc)
        struct list_head *head = &bc->async_list;
        struct qlcnic_async_work_list *entry;
 
+       flush_workqueue(bc->bc_async_wq);
        while (!list_empty(head)) {
                entry = list_entry(head->next, struct qlcnic_async_work_list,
                                   list);
@@ -1534,10 +1529,14 @@ void qlcnic_sriov_cleanup_async_list(struct qlcnic_back_channel *bc)
        }
 }
 
-static void qlcnic_sriov_vf_set_multi(struct net_device *netdev)
+void qlcnic_sriov_vf_set_multi(struct net_device *netdev)
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        struct qlcnic_hardware_context *ahw = adapter->ahw;
+       static const u8 bcast_addr[ETH_ALEN] = {
+               0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+       };
+       struct netdev_hw_addr *ha;
        u32 mode = VPORT_MISS_MODE_DROP;
 
        if (!test_bit(__QLCNIC_FW_ATTACHED, &adapter->state))
@@ -1549,23 +1548,49 @@ static void qlcnic_sriov_vf_set_multi(struct net_device *netdev)
        } else if ((netdev->flags & IFF_ALLMULTI) ||
                   (netdev_mc_count(netdev) > ahw->max_mc_count)) {
                mode = VPORT_MISS_MODE_ACCEPT_MULTI;
+       } else {
+               qlcnic_vf_add_mc_list(netdev, bcast_addr);
+               if (!netdev_mc_empty(netdev)) {
+                       netdev_for_each_mc_addr(ha, netdev)
+                               qlcnic_vf_add_mc_list(netdev, ha->addr);
+               }
        }
 
-       if (qlcnic_sriov_vf_check(adapter))
-               qlcnic_vf_add_mc_list(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_vf_add_mc_list(netdev, ha->addr);
+       }
+
+       if (adapter->pdev->is_virtfn) {
+               if (mode == VPORT_MISS_MODE_ACCEPT_ALL &&
+                   !adapter->fdb_mac_learn) {
+                       qlcnic_alloc_lb_filters_mem(adapter);
+                       adapter->drv_mac_learn = 1;
+                       adapter->rx_mac_learn = true;
+               } else {
+                       adapter->drv_mac_learn = 0;
+                       adapter->rx_mac_learn = false;
+               }
+       }
 
        qlcnic_nic_set_promisc(adapter, mode);
 }
 
-static void qlcnic_sriov_handle_async_multi(struct work_struct *work)
+static void qlcnic_sriov_handle_async_issue_cmd(struct work_struct *work)
 {
        struct qlcnic_async_work_list *entry;
-       struct net_device *netdev;
+       struct qlcnic_adapter *adapter;
+       struct qlcnic_cmd_args *cmd;
 
        entry = container_of(work, struct qlcnic_async_work_list, work);
-       netdev = (struct net_device *)entry->ptr;
-
-       qlcnic_sriov_vf_set_multi(netdev);
+       adapter = entry->ptr;
+       cmd = entry->cmd;
+       __qlcnic_sriov_issue_cmd(adapter, cmd);
        return;
 }
 
@@ -1595,8 +1620,9 @@ qlcnic_sriov_get_free_node_async_work(struct qlcnic_back_channel *bc)
        return entry;
 }
 
-static void qlcnic_sriov_schedule_bc_async_work(struct qlcnic_back_channel *bc,
-                                               work_func_t func, void *data)
+static void qlcnic_sriov_schedule_async_cmd(struct qlcnic_back_channel *bc,
+                                           work_func_t func, void *data,
+                                           struct qlcnic_cmd_args *cmd)
 {
        struct qlcnic_async_work_list *entry = NULL;
 
@@ -1605,21 +1631,23 @@ static void qlcnic_sriov_schedule_bc_async_work(struct qlcnic_back_channel *bc,
                return;
 
        entry->ptr = data;
+       entry->cmd = cmd;
        INIT_WORK(&entry->work, func);
        queue_work(bc->bc_async_wq, &entry->work);
 }
 
-void qlcnic_sriov_vf_schedule_multi(struct net_device *netdev)
+static int qlcnic_sriov_async_issue_cmd(struct qlcnic_adapter *adapter,
+                                       struct qlcnic_cmd_args *cmd)
 {
 
-       struct qlcnic_adapter *adapter = netdev_priv(netdev);
        struct qlcnic_back_channel *bc = &adapter->ahw->sriov->bc;
 
        if (adapter->need_fw_reset)
-               return;
+               return -EIO;
 
-       qlcnic_sriov_schedule_bc_async_work(bc, qlcnic_sriov_handle_async_multi,
-                                           netdev);
+       qlcnic_sriov_schedule_async_cmd(bc, qlcnic_sriov_handle_async_issue_cmd,
+                                       adapter, cmd);
+       return 0;
 }
 
 static int qlcnic_sriov_vf_reinit_driver(struct qlcnic_adapter *adapter)
@@ -1843,6 +1871,12 @@ static int qlcnic_sriov_vf_idc_unknown_state(struct qlcnic_adapter *adapter)
        return 0;
 }
 
+static void qlcnic_sriov_vf_periodic_tasks(struct qlcnic_adapter *adapter)
+{
+       if (adapter->fhash.fnum)
+               qlcnic_prune_lb_filters(adapter);
+}
+
 static void qlcnic_sriov_vf_poll_dev_state(struct work_struct *work)
 {
        struct qlcnic_adapter *adapter;
@@ -1874,6 +1908,8 @@ static void qlcnic_sriov_vf_poll_dev_state(struct work_struct *work)
        }
 
        idc->prev_state = idc->curr_state;
+       qlcnic_sriov_vf_periodic_tasks(adapter);
+
        if (!ret && test_bit(QLC_83XX_MODULE_LOADED, &idc->status))
                qlcnic_schedule_work(adapter, qlcnic_sriov_vf_poll_dev_state,
                                     idc->delay);
@@ -1897,7 +1933,7 @@ static int qlcnic_sriov_check_vlan_id(struct qlcnic_sriov *sriov,
        if (!vf->sriov_vlans)
                return err;
 
-       mutex_lock(&vf->vlan_list_lock);
+       spin_lock_bh(&vf->vlan_list_lock);
 
        for (i = 0; i < sriov->num_allowed_vlans; i++) {
                if (vf->sriov_vlans[i] == vlan_id) {
@@ -1906,7 +1942,7 @@ static int qlcnic_sriov_check_vlan_id(struct qlcnic_sriov *sriov,
                }
        }
 
-       mutex_unlock(&vf->vlan_list_lock);
+       spin_unlock_bh(&vf->vlan_list_lock);
        return err;
 }
 
@@ -1915,12 +1951,12 @@ static int qlcnic_sriov_validate_num_vlans(struct qlcnic_sriov *sriov,
 {
        int err = 0;
 
-       mutex_lock(&vf->vlan_list_lock);
+       spin_lock_bh(&vf->vlan_list_lock);
 
        if (vf->num_vlan >= sriov->num_allowed_vlans)
                err = -EINVAL;
 
-       mutex_unlock(&vf->vlan_list_lock);
+       spin_unlock_bh(&vf->vlan_list_lock);
        return err;
 }
 
@@ -1973,7 +2009,7 @@ static void qlcnic_sriov_vlan_operation(struct qlcnic_vf_info *vf, u16 vlan_id,
        if (!vf->sriov_vlans)
                return;
 
-       mutex_lock(&vf->vlan_list_lock);
+       spin_lock_bh(&vf->vlan_list_lock);
 
        switch (opcode) {
        case QLC_VLAN_ADD:
@@ -1986,7 +2022,7 @@ static void qlcnic_sriov_vlan_operation(struct qlcnic_vf_info *vf, u16 vlan_id,
                netdev_err(adapter->netdev, "Invalid VLAN operation\n");
        }
 
-       mutex_unlock(&vf->vlan_list_lock);
+       spin_unlock_bh(&vf->vlan_list_lock);
        return;
 }
 
@@ -1994,10 +2030,12 @@ int qlcnic_sriov_cfg_vf_guest_vlan(struct qlcnic_adapter *adapter,
                                   u16 vid, u8 enable)
 {
        struct qlcnic_sriov *sriov = adapter->ahw->sriov;
+       struct net_device *netdev = adapter->netdev;
        struct qlcnic_vf_info *vf;
        struct qlcnic_cmd_args cmd;
        int ret;
 
+       memset(&cmd, 0, sizeof(cmd));
        if (vid == 0)
                return 0;
 
@@ -2019,14 +2057,18 @@ int qlcnic_sriov_cfg_vf_guest_vlan(struct qlcnic_adapter *adapter,
                dev_err(&adapter->pdev->dev,
                        "Failed to configure guest VLAN, err=%d\n", ret);
        } else {
+               netif_addr_lock_bh(netdev);
                qlcnic_free_mac_list(adapter);
+               netif_addr_unlock_bh(netdev);
 
                if (enable)
                        qlcnic_sriov_vlan_operation(vf, vid, QLC_VLAN_ADD);
                else
                        qlcnic_sriov_vlan_operation(vf, vid, QLC_VLAN_DELETE);
 
-               qlcnic_set_multi(adapter->netdev);
+               netif_addr_lock_bh(netdev);
+               qlcnic_set_multi(netdev);
+               netif_addr_unlock_bh(netdev);
        }
 
        qlcnic_free_mbx_args(&cmd);
@@ -2157,11 +2199,11 @@ bool qlcnic_sriov_check_any_vlan(struct qlcnic_vf_info *vf)
 {
        bool err = false;
 
-       mutex_lock(&vf->vlan_list_lock);
+       spin_lock_bh(&vf->vlan_list_lock);
 
        if (vf->num_vlan)
                err = true;
 
-       mutex_unlock(&vf->vlan_list_lock);
+       spin_unlock_bh(&vf->vlan_list_lock);
        return err;
 }
index 2801379915447dc54683719c40058d9d89f8387f..a29538b86edfcac99010cedbea1fff0a29db799b 100644 (file)
@@ -16,6 +16,7 @@
 #define QLC_VF_FLOOD_BIT       BIT_16
 #define QLC_FLOOD_MODE         0x5
 #define QLC_SRIOV_ALLOW_VLAN0  BIT_19
+#define QLC_INTR_COAL_TYPE_MASK        0x7
 
 static int qlcnic_sriov_pf_get_vport_handle(struct qlcnic_adapter *, u8);
 
@@ -83,7 +84,7 @@ static int qlcnic_sriov_pf_cal_res_limit(struct qlcnic_adapter *adapter,
        info->max_tx_ques = res->num_tx_queues / max;
 
        if (qlcnic_83xx_pf_check(adapter))
-               num_macs = 1;
+               num_macs = QLCNIC_83XX_SRIOV_VF_MAX_MAC;
 
        info->max_rx_mcast_mac_filters = res->num_rx_mcast_mac_filters;
 
@@ -337,9 +338,12 @@ static int qlcnic_sriov_pf_cfg_vlan_filtering(struct qlcnic_adapter *adapter,
 
        cmd.req.arg[1] = 0x4;
        if (enable) {
+               adapter->flags |= QLCNIC_VLAN_FILTERING;
                cmd.req.arg[1] |= BIT_16;
                if (qlcnic_84xx_check(adapter))
                        cmd.req.arg[1] |= QLC_SRIOV_ALLOW_VLAN0;
+       } else {
+               adapter->flags &= ~QLCNIC_VLAN_FILTERING;
        }
 
        err = qlcnic_issue_cmd(adapter, &cmd);
@@ -471,12 +475,12 @@ static int qlcnic_pci_sriov_disable(struct qlcnic_adapter *adapter)
                return -EPERM;
        }
 
+       qlcnic_sriov_pf_disable(adapter);
+
        rtnl_lock();
        if (netif_running(netdev))
                __qlcnic_down(adapter, netdev);
 
-       qlcnic_sriov_pf_disable(adapter);
-
        qlcnic_sriov_free_vlans(adapter);
 
        qlcnic_sriov_pf_cleanup(adapter);
@@ -595,7 +599,6 @@ static int __qlcnic_pci_sriov_enable(struct qlcnic_adapter *adapter,
 
        qlcnic_sriov_alloc_vlans(adapter);
 
-       err = qlcnic_sriov_pf_enable(adapter, num_vfs);
        return err;
 
 del_flr_queue:
@@ -626,25 +629,36 @@ static int qlcnic_pci_sriov_enable(struct qlcnic_adapter *adapter, int num_vfs)
                __qlcnic_down(adapter, netdev);
 
        err = __qlcnic_pci_sriov_enable(adapter, num_vfs);
-       if (err) {
-               netdev_info(netdev, "Failed to enable SR-IOV on port %d\n",
-                           adapter->portnum);
+       if (err)
+               goto error;
 
-               err = -EIO;
-               if (qlcnic_83xx_configure_opmode(adapter))
-                       goto error;
-       } else {
+       if (netif_running(netdev))
+               __qlcnic_up(adapter, netdev);
+
+       rtnl_unlock();
+       err = qlcnic_sriov_pf_enable(adapter, num_vfs);
+       if (!err) {
                netdev_info(netdev,
                            "SR-IOV is enabled successfully on port %d\n",
                            adapter->portnum);
                /* Return number of vfs enabled */
-               err = num_vfs;
+               return num_vfs;
        }
+
+       rtnl_lock();
        if (netif_running(netdev))
-               __qlcnic_up(adapter, netdev);
+               __qlcnic_down(adapter, netdev);
 
 error:
+       if (!qlcnic_83xx_configure_opmode(adapter)) {
+               if (netif_running(netdev))
+                       __qlcnic_up(adapter, netdev);
+       }
+
        rtnl_unlock();
+       netdev_info(netdev, "Failed to enable SR-IOV on port %d\n",
+                   adapter->portnum);
+
        return err;
 }
 
@@ -773,7 +787,7 @@ static int qlcnic_sriov_cfg_vf_def_mac(struct qlcnic_adapter *adapter,
                                       struct qlcnic_vf_info *vf,
                                       u16 vlan, u8 op)
 {
-       struct qlcnic_cmd_args cmd;
+       struct qlcnic_cmd_args *cmd;
        struct qlcnic_macvlan_mbx mv;
        struct qlcnic_vport *vp;
        u8 *addr;
@@ -783,21 +797,27 @@ static int qlcnic_sriov_cfg_vf_def_mac(struct qlcnic_adapter *adapter,
 
        vp = vf->vp;
 
-       if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_MAC_VLAN))
+       cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC);
+       if (!cmd)
                return -ENOMEM;
 
+       err = qlcnic_alloc_mbx_args(cmd, adapter, QLCNIC_CMD_CONFIG_MAC_VLAN);
+       if (err)
+               goto free_cmd;
+
+       cmd->type = QLC_83XX_MBX_CMD_NO_WAIT;
        vpid = qlcnic_sriov_pf_get_vport_handle(adapter, vf->pci_func);
        if (vpid < 0) {
                err = -EINVAL;
-               goto out;
+               goto free_args;
        }
 
        if (vlan)
                op = ((op == QLCNIC_MAC_ADD || op == QLCNIC_MAC_VLAN_ADD) ?
                      QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_VLAN_DEL);
 
-       cmd.req.arg[1] = op | (1 << 8) | (3 << 6);
-       cmd.req.arg[1] |= ((vpid & 0xffff) << 16) | BIT_31;
+       cmd->req.arg[1] = op | (1 << 8) | (3 << 6);
+       cmd->req.arg[1] |= ((vpid & 0xffff) << 16) | BIT_31;
 
        addr = vp->mac;
        mv.vlan = vlan;
@@ -807,18 +827,18 @@ static int qlcnic_sriov_cfg_vf_def_mac(struct qlcnic_adapter *adapter,
        mv.mac_addr3 = addr[3];
        mv.mac_addr4 = addr[4];
        mv.mac_addr5 = addr[5];
-       buf = &cmd.req.arg[2];
+       buf = &cmd->req.arg[2];
        memcpy(buf, &mv, sizeof(struct qlcnic_macvlan_mbx));
 
-       err = qlcnic_issue_cmd(adapter, &cmd);
+       err = qlcnic_issue_cmd(adapter, cmd);
 
-       if (err)
-               dev_err(&adapter->pdev->dev,
-                       "MAC-VLAN %s to CAM failed, err=%d.\n",
-                       ((op == 1) ? "add " : "delete "), err);
+       if (!err)
+               return err;
 
-out:
-       qlcnic_free_mbx_args(&cmd);
+free_args:
+       qlcnic_free_mbx_args(cmd);
+free_cmd:
+       kfree(cmd);
        return err;
 }
 
@@ -840,7 +860,7 @@ static void qlcnic_83xx_cfg_default_mac_vlan(struct qlcnic_adapter *adapter,
 
        sriov = adapter->ahw->sriov;
 
-       mutex_lock(&vf->vlan_list_lock);
+       spin_lock_bh(&vf->vlan_list_lock);
        if (vf->num_vlan) {
                for (i = 0; i < sriov->num_allowed_vlans; i++) {
                        vlan = vf->sriov_vlans[i];
@@ -849,7 +869,7 @@ static void qlcnic_83xx_cfg_default_mac_vlan(struct qlcnic_adapter *adapter,
                                                            opcode);
                }
        }
-       mutex_unlock(&vf->vlan_list_lock);
+       spin_unlock_bh(&vf->vlan_list_lock);
 
        if (vf->vp->vlan_mode != QLC_PVID_MODE) {
                if (qlcnic_83xx_pf_check(adapter) &&
@@ -1178,19 +1198,41 @@ static int qlcnic_sriov_validate_cfg_intrcoal(struct qlcnic_adapter *adapter,
 {
        struct qlcnic_nic_intr_coalesce *coal = &adapter->ahw->coal;
        u16 ctx_id, pkts, time;
+       int err = -EINVAL;
+       u8 type;
 
+       type = cmd->req.arg[1] & QLC_INTR_COAL_TYPE_MASK;
        ctx_id = cmd->req.arg[1] >> 16;
        pkts = cmd->req.arg[2] & 0xffff;
        time = cmd->req.arg[2] >> 16;
 
-       if (ctx_id != vf->rx_ctx_id)
-               return -EINVAL;
-       if (pkts > coal->rx_packets)
-               return -EINVAL;
-       if (time < coal->rx_time_us)
-               return -EINVAL;
+       switch (type) {
+       case QLCNIC_INTR_COAL_TYPE_RX:
+               if (ctx_id != vf->rx_ctx_id || pkts > coal->rx_packets ||
+                   time < coal->rx_time_us)
+                       goto err_label;
+               break;
+       case QLCNIC_INTR_COAL_TYPE_TX:
+               if (ctx_id != vf->tx_ctx_id || pkts > coal->tx_packets ||
+                   time < coal->tx_time_us)
+                       goto err_label;
+               break;
+       default:
+               netdev_err(adapter->netdev, "Invalid coalescing type 0x%x received\n",
+                          type);
+               return err;
+       }
 
        return 0;
+
+err_label:
+       netdev_err(adapter->netdev, "Expected: rx_ctx_id 0x%x rx_packets 0x%x rx_time_us 0x%x tx_ctx_id 0x%x tx_packets 0x%x tx_time_us 0x%x\n",
+                  vf->rx_ctx_id, coal->rx_packets, coal->rx_time_us,
+                  vf->tx_ctx_id, coal->tx_packets, coal->tx_time_us);
+       netdev_err(adapter->netdev, "Received: ctx_id 0x%x packets 0x%x time_us 0x%x type 0x%x\n",
+                  ctx_id, pkts, time, type);
+
+       return err;
 }
 
 static int qlcnic_sriov_pf_cfg_intrcoal_cmd(struct qlcnic_bc_trans *tran,
@@ -1214,7 +1256,6 @@ static int qlcnic_sriov_validate_cfg_macvlan(struct qlcnic_adapter *adapter,
                                             struct qlcnic_vf_info *vf,
                                             struct qlcnic_cmd_args *cmd)
 {
-       struct qlcnic_macvlan_mbx *macvlan;
        struct qlcnic_vport *vp = vf->vp;
        u8 op, new_op;
 
@@ -1224,14 +1265,6 @@ static int qlcnic_sriov_validate_cfg_macvlan(struct qlcnic_adapter *adapter,
        cmd->req.arg[1] |= (vf->vp->handle << 16);
        cmd->req.arg[1] |= BIT_31;
 
-       macvlan = (struct qlcnic_macvlan_mbx *)&cmd->req.arg[2];
-       if (!(macvlan->mac_addr0 & BIT_0)) {
-               dev_err(&adapter->pdev->dev,
-                       "MAC address change is not allowed from VF %d",
-                       vf->pci_func);
-               return -EINVAL;
-       }
-
        if (vp->vlan_mode == QLC_PVID_MODE) {
                op = cmd->req.arg[1] & 0x7;
                cmd->req.arg[1] &= ~0x7;
@@ -1815,7 +1848,8 @@ int qlcnic_sriov_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
        return 0;
 }
 
-int qlcnic_sriov_set_vf_tx_rate(struct net_device *netdev, int vf, int tx_rate)
+int qlcnic_sriov_set_vf_tx_rate(struct net_device *netdev, int vf,
+                               int min_tx_rate, int max_tx_rate)
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        struct qlcnic_sriov *sriov = adapter->ahw->sriov;
@@ -1830,35 +1864,52 @@ int qlcnic_sriov_set_vf_tx_rate(struct net_device *netdev, int vf, int tx_rate)
        if (vf >= sriov->num_vfs)
                return -EINVAL;
 
-       if (tx_rate >= 10000 || tx_rate < 100) {
+       vf_info = &sriov->vf_info[vf];
+       vp = vf_info->vp;
+       vpid = vp->handle;
+
+       if (!min_tx_rate)
+               min_tx_rate = QLC_VF_MIN_TX_RATE;
+
+       if (max_tx_rate &&
+           (max_tx_rate >= 10000 || max_tx_rate < min_tx_rate)) {
                netdev_err(netdev,
-                          "Invalid Tx rate, allowed range is [%d - %d]",
-                          QLC_VF_MIN_TX_RATE, QLC_VF_MAX_TX_RATE);
+                          "Invalid max Tx rate, allowed range is [%d - %d]",
+                          min_tx_rate, QLC_VF_MAX_TX_RATE);
                return -EINVAL;
        }
 
-       if (tx_rate == 0)
-               tx_rate = 10000;
+       if (!max_tx_rate)
+               max_tx_rate = 10000;
 
-       vf_info = &sriov->vf_info[vf];
-       vp = vf_info->vp;
-       vpid = vp->handle;
+       if (min_tx_rate &&
+           (min_tx_rate > max_tx_rate || min_tx_rate < QLC_VF_MIN_TX_RATE)) {
+               netdev_err(netdev,
+                          "Invalid min Tx rate, allowed range is [%d - %d]",
+                          QLC_VF_MIN_TX_RATE, max_tx_rate);
+               return -EINVAL;
+       }
 
        if (test_bit(QLC_BC_VF_STATE, &vf_info->state)) {
                if (qlcnic_sriov_get_vf_vport_info(adapter, &nic_info, vpid))
                        return -EIO;
 
-               nic_info.max_tx_bw = tx_rate / 100;
+               nic_info.max_tx_bw = max_tx_rate / 100;
+               nic_info.min_tx_bw = min_tx_rate / 100;
                nic_info.bit_offsets = BIT_0;
 
                if (qlcnic_sriov_pf_set_vport_info(adapter, &nic_info, vpid))
                        return -EIO;
        }
 
-       vp->max_tx_bw = tx_rate / 100;
+       vp->max_tx_bw = max_tx_rate / 100;
        netdev_info(netdev,
-                   "Setting Tx rate %d (Mbps), %d %% of PF bandwidth, for VF %d\n",
-                   tx_rate, vp->max_tx_bw, vf);
+                   "Setting Max Tx rate %d (Mbps), %d %% of PF bandwidth, for VF %d\n",
+                   max_tx_rate, vp->max_tx_bw, vf);
+       vp->min_tx_bw = min_tx_rate / 100;
+       netdev_info(netdev,
+                   "Setting Min Tx rate %d (Mbps), %d %% of PF bandwidth, for VF %d\n",
+                   min_tx_rate, vp->min_tx_bw, vf);
        return 0;
 }
 
@@ -1957,9 +2008,13 @@ int qlcnic_sriov_get_vf_config(struct net_device *netdev,
        ivi->qos = vp->qos;
        ivi->spoofchk = vp->spoofchk;
        if (vp->max_tx_bw == MAX_BW)
-               ivi->tx_rate = 0;
+               ivi->max_tx_rate = 0;
+       else
+               ivi->max_tx_rate = vp->max_tx_bw * 100;
+       if (vp->min_tx_bw == MIN_BW)
+               ivi->min_tx_rate = 0;
        else
-               ivi->tx_rate = vp->max_tx_bw * 100;
+               ivi->min_tx_rate = vp->min_tx_bw * 100;
 
        ivi->vf = vf;
        return 0;
index cd346e27f2e1270078a7580c5e563bc178d5ef37..f5786d5792df06fe16db6f7ffd2276f9bdabe96f 100644 (file)
 #include <linux/sysfs.h>
 #include <linux/aer.h>
 #include <linux/log2.h>
+#ifdef CONFIG_QLCNIC_HWMON
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#endif
 
 #define QLC_STATUS_UNSUPPORTED_CMD     -2
 
@@ -358,6 +362,8 @@ int qlcnic_is_valid_nic_func(struct qlcnic_adapter *adapter, u8 pci_func)
                if (adapter->npars[i].pci_func == pci_func)
                        return i;
        }
+
+       dev_err(&adapter->pdev->dev, "%s: Invalid nic function\n", __func__);
        return -EINVAL;
 }
 
@@ -1243,6 +1249,68 @@ static struct bin_attribute bin_attr_flash = {
        .write = qlcnic_83xx_sysfs_flash_write_handler,
 };
 
+#ifdef CONFIG_QLCNIC_HWMON
+
+static ssize_t qlcnic_hwmon_show_temp(struct device *dev,
+                                     struct device_attribute *dev_attr,
+                                     char *buf)
+{
+       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+       unsigned int temperature = 0, value = 0;
+
+       if (qlcnic_83xx_check(adapter))
+               value = QLCRDX(adapter->ahw, QLC_83XX_ASIC_TEMP);
+       else if (qlcnic_82xx_check(adapter))
+               value = QLC_SHARED_REG_RD32(adapter, QLCNIC_ASIC_TEMP);
+
+       temperature = qlcnic_get_temp_val(value);
+       /* display millidegree celcius */
+       temperature *= 1000;
+       return sprintf(buf, "%u\n", temperature);
+}
+
+/* hwmon-sysfs attributes */
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
+                         qlcnic_hwmon_show_temp, NULL, 1);
+
+static struct attribute *qlcnic_hwmon_attrs[] = {
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       NULL
+};
+
+ATTRIBUTE_GROUPS(qlcnic_hwmon);
+
+void qlcnic_register_hwmon_dev(struct qlcnic_adapter *adapter)
+{
+       struct device *dev = &adapter->pdev->dev;
+       struct device *hwmon_dev;
+
+       /* Skip hwmon registration for a VF device */
+       if (qlcnic_sriov_vf_check(adapter)) {
+               adapter->ahw->hwmon_dev = NULL;
+               return;
+       }
+       hwmon_dev = hwmon_device_register_with_groups(dev, qlcnic_driver_name,
+                                                     adapter,
+                                                     qlcnic_hwmon_groups);
+       if (IS_ERR(hwmon_dev)) {
+               dev_err(dev, "Cannot register with hwmon, err=%ld\n",
+                       PTR_ERR(hwmon_dev));
+               hwmon_dev = NULL;
+       }
+       adapter->ahw->hwmon_dev = hwmon_dev;
+}
+
+void qlcnic_unregister_hwmon_dev(struct qlcnic_adapter *adapter)
+{
+       struct device *hwmon_dev = adapter->ahw->hwmon_dev;
+       if (hwmon_dev) {
+               hwmon_device_unregister(hwmon_dev);
+               adapter->ahw->hwmon_dev = NULL;
+       }
+}
+#endif
+
 void qlcnic_create_sysfs_entries(struct qlcnic_adapter *adapter)
 {
        struct device *dev = &adapter->pdev->dev;
index 0a1d76acab8171929e3c6f9ad6139b48484ebbcc..b40050e03a56f7524e19dc7c215165d0a0c75585 100644 (file)
@@ -3595,7 +3595,7 @@ static int ql_request_irq(struct ql_adapter *qdev)
        }
        return status;
 err_irq:
-       netif_err(qdev, ifup, qdev->ndev, "Failed to get the interrupts!!!/n");
+       netif_err(qdev, ifup, qdev->ndev, "Failed to get the interrupts!!!\n");
        ql_free_irq(qdev);
        return status;
 }
@@ -4770,7 +4770,7 @@ static int qlge_probe(struct pci_dev *pdev,
        ndev->irq = pdev->irq;
 
        ndev->netdev_ops = &qlge_netdev_ops;
-       SET_ETHTOOL_OPS(ndev, &qlge_ethtool_ops);
+       ndev->ethtool_ops = &qlge_ethtool_ops;
        ndev->watchdog_timeo = 10 * HZ;
 
        err = register_netdev(ndev);
index aa1c079f231dc6f2cfc017cd4950f9ebb8e1e9da..be425ad5e82487e94a91b7371466f7f93ab558eb 100644 (file)
@@ -7125,7 +7125,7 @@ rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        for (i = 0; i < ETH_ALEN; i++)
                dev->dev_addr[i] = RTL_R8(MAC0 + i);
 
-       SET_ETHTOOL_OPS(dev, &rtl8169_ethtool_ops);
+       dev->ethtool_ops = &rtl8169_ethtool_ops;
        dev->watchdog_timeo = RTL8169_TX_TIMEOUT;
 
        netif_napi_add(dev, &tp->napi, rtl8169_poll, R8169_NAPI_WEIGHT);
index 6a9509ccd33b29dd84ddcd0b48269f1c24b8da63..7622213beef167e04a3a837c63c890935a8d703b 100644 (file)
@@ -307,6 +307,27 @@ static const u16 sh_eth_offset_fast_sh4[SH_ETH_MAX_REGISTER_OFFSET] = {
 };
 
 static const u16 sh_eth_offset_fast_sh3_sh2[SH_ETH_MAX_REGISTER_OFFSET] = {
+       [EDMR]          = 0x0000,
+       [EDTRR]         = 0x0004,
+       [EDRRR]         = 0x0008,
+       [TDLAR]         = 0x000c,
+       [RDLAR]         = 0x0010,
+       [EESR]          = 0x0014,
+       [EESIPR]        = 0x0018,
+       [TRSCER]        = 0x001c,
+       [RMFCR]         = 0x0020,
+       [TFTR]          = 0x0024,
+       [FDR]           = 0x0028,
+       [RMCR]          = 0x002c,
+       [EDOCR]         = 0x0030,
+       [FCFTR]         = 0x0034,
+       [RPADIR]        = 0x0038,
+       [TRIMD]         = 0x003c,
+       [RBWAR]         = 0x0040,
+       [RDFAR]         = 0x0044,
+       [TBRAR]         = 0x004c,
+       [TDFAR]         = 0x0050,
+
        [ECMR]          = 0x0160,
        [ECSR]          = 0x0164,
        [ECSIPR]        = 0x0168,
@@ -546,7 +567,6 @@ static struct sh_eth_cpu_data sh7757_data = {
        .register_type  = SH_ETH_REG_FAST_SH4,
 
        .eesipr_value   = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff,
-       .rmcr_value     = RMCR_RNC,
 
        .tx_check       = EESR_FTC | EESR_CND | EESR_DLC | EESR_CD | EESR_RTO,
        .eesr_err_check = EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE |
@@ -624,7 +644,6 @@ static struct sh_eth_cpu_data sh7757_data_giga = {
                          EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE |
                          EESR_TDE | EESR_ECI,
        .fdr_value      = 0x0000072f,
-       .rmcr_value     = RMCR_RNC,
 
        .irq_flags      = IRQF_SHARED,
        .apr            = 1,
@@ -752,7 +771,6 @@ static struct sh_eth_cpu_data r8a7740_data = {
                          EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE |
                          EESR_TDE | EESR_ECI,
        .fdr_value      = 0x0000070f,
-       .rmcr_value     = RMCR_RNC,
 
        .apr            = 1,
        .mpr            = 1,
@@ -784,7 +802,6 @@ static struct sh_eth_cpu_data r7s72100_data = {
                          EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE |
                          EESR_TDE | EESR_ECI,
        .fdr_value      = 0x0000070f,
-       .rmcr_value     = RMCR_RNC,
 
        .no_psr         = 1,
        .apr            = 1,
@@ -833,9 +850,6 @@ static void sh_eth_set_default_cpu_data(struct sh_eth_cpu_data *cd)
        if (!cd->fdr_value)
                cd->fdr_value = DEFAULT_FDR_INIT;
 
-       if (!cd->rmcr_value)
-               cd->rmcr_value = DEFAULT_RMCR_VALUE;
-
        if (!cd->tx_check)
                cd->tx_check = DEFAULT_TX_CHECK;
 
@@ -1287,8 +1301,8 @@ static int sh_eth_dev_init(struct net_device *ndev, bool start)
        sh_eth_write(ndev, mdp->cd->fdr_value, FDR);
        sh_eth_write(ndev, 0, TFTR);
 
-       /* Frame recv control */
-       sh_eth_write(ndev, mdp->cd->rmcr_value, RMCR);
+       /* Frame recv control (enable multiple-packets per rx irq) */
+       sh_eth_write(ndev, RMCR_RNC, RMCR);
 
        sh_eth_write(ndev, DESC_I_RINT8 | DESC_I_RINT5 | DESC_I_TINT2, TRSCER);
 
@@ -1385,7 +1399,6 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
        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;
 
@@ -1397,10 +1410,9 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
                if (--boguscnt < 0)
                        break;
 
-               if (*quota <= 0) {
-                       exceeded = 1;
+               if (*quota <= 0)
                        break;
-               }
+
                (*quota)--;
 
                if (!(desc_status & RDFEND))
@@ -1448,7 +1460,6 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
                        ndev->stats.rx_packets++;
                        ndev->stats.rx_bytes += pkt_len;
                }
-               rxdesc->status |= cpu_to_edmac(mdp, RD_RACT);
                entry = (++mdp->cur_rx) % mdp->num_rx_ring;
                rxdesc = &mdp->rx_ring[entry];
        }
@@ -1494,7 +1505,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
                sh_eth_write(ndev, EDRRR_R, EDRRR);
        }
 
-       return exceeded;
+       return *quota <= 0;
 }
 
 static void sh_eth_rcv_snd_disable(struct net_device *ndev)
@@ -2627,8 +2638,8 @@ static int sh_mdio_init(struct sh_eth_private *mdp,
                 pdev->name, pdev->id);
 
        /* PHY IRQ */
-       mdp->mii_bus->irq = devm_kzalloc(dev, sizeof(int) * PHY_MAX_ADDR,
-                                        GFP_KERNEL);
+       mdp->mii_bus->irq = devm_kmalloc_array(dev, PHY_MAX_ADDR, sizeof(int),
+                                              GFP_KERNEL);
        if (!mdp->mii_bus->irq) {
                ret = -ENOMEM;
                goto out_free_bus;
@@ -2843,7 +2854,7 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
                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->ethtool_ops = &sh_eth_ethtool_ops;
        ndev->watchdog_timeo = TX_TIMEOUT;
 
        /* debug message level */
index d55e37cd5fec04abcd6bc4e52827351e74d5fbdb..b37c427144ee0a1010794ff2beff42bc298fd1e9 100644 (file)
@@ -319,7 +319,6 @@ enum TD_STS_BIT {
 enum RMCR_BIT {
        RMCR_RNC = 0x00000001,
 };
-#define DEFAULT_RMCR_VALUE     0x00000000
 
 /* ECMR */
 enum FELIC_MODE_BIT {
@@ -466,7 +465,6 @@ struct sh_eth_cpu_data {
        unsigned long fdr_value;
        unsigned long fcftr_value;
        unsigned long rpadir_value;
-       unsigned long rmcr_value;
 
        /* interrupt checking mask */
        unsigned long tx_check;
index 0415fa50eeb77b82376214708419f6e4186e876f..c0981ae45874acc5c247a2b37d84a90296e1196b 100644 (file)
@@ -520,5 +520,5 @@ static const struct ethtool_ops sxgbe_ethtool_ops = {
 
 void sxgbe_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &sxgbe_ethtool_ops);
+       netdev->ethtool_ops = &sxgbe_ethtool_ops;
 }
index 82a9a983869fe6ae711c5b4503941611f1f32657..698494481d18072ca00ddecb825ea92bd4e86c56 100644 (file)
@@ -425,8 +425,8 @@ dmamem_err:
  * @rx_rsize: ring size
  * Description:  this function initializes the DMA RX descriptor
  */
-void free_rx_ring(struct device *dev, struct sxgbe_rx_queue *rx_ring,
-                 int rx_rsize)
+static void free_rx_ring(struct device *dev, struct sxgbe_rx_queue *rx_ring,
+                        int rx_rsize)
 {
        dma_free_coherent(dev, rx_rsize * sizeof(struct sxgbe_rx_norm_desc),
                          rx_ring->dma_rx, rx_ring->dma_rx_phy);
@@ -519,8 +519,8 @@ error:
  * @tx_rsize: ring size
  * Description:  this function initializes the DMA TX descriptor
  */
-void free_tx_ring(struct device *dev, struct sxgbe_tx_queue *tx_ring,
-                 int tx_rsize)
+static void free_tx_ring(struct device *dev, struct sxgbe_tx_queue *tx_ring,
+                        int tx_rsize)
 {
        dma_free_coherent(dev, tx_rsize * sizeof(struct sxgbe_tx_norm_desc),
                          tx_ring->dma_tx, tx_ring->dma_tx_phy);
@@ -1221,11 +1221,10 @@ static int sxgbe_release(struct net_device *dev)
 
        return 0;
 }
-
 /* Prepare first Tx descriptor for doing TSO operation */
-void sxgbe_tso_prepare(struct sxgbe_priv_data *priv,
-                      struct sxgbe_tx_norm_desc *first_desc,
-                      struct sk_buff *skb)
+static void sxgbe_tso_prepare(struct sxgbe_priv_data *priv,
+                             struct sxgbe_tx_norm_desc *first_desc,
+                             struct sk_buff *skb)
 {
        unsigned int total_hdr_len, tcp_hdr_len;
 
@@ -1914,40 +1913,6 @@ static void sxgbe_set_rx_mode(struct net_device *dev)
                   readl(ioaddr + SXGBE_HASH_LOW));
 }
 
-/**
- * sxgbe_config - entry point for changing configuration mode passed on by
- * ifconfig
- * @dev : pointer to the device structure
- * @map : pointer to the device mapping structure
- * Description:
- * This function is a driver entry point which gets called by the kernel
- * whenever some device configuration is changed.
- * Return value:
- * This function returns 0 if success and appropriate error otherwise.
- */
-static int sxgbe_config(struct net_device *dev, struct ifmap *map)
-{
-       struct sxgbe_priv_data *priv = netdev_priv(dev);
-
-       /* Can't act on a running interface */
-       if (dev->flags & IFF_UP)
-               return -EBUSY;
-
-       /* Don't allow changing the I/O address */
-       if (map->base_addr != (unsigned long)priv->ioaddr) {
-               netdev_warn(dev, "can't change I/O address\n");
-               return -EOPNOTSUPP;
-       }
-
-       /* Don't allow changing the IRQ */
-       if (map->irq != priv->irq) {
-               netdev_warn(dev, "not change IRQ number %d\n", priv->irq);
-               return -EOPNOTSUPP;
-       }
-
-       return 0;
-}
-
 #ifdef CONFIG_NET_POLL_CONTROLLER
 /**
  * sxgbe_poll_controller - entry point for polling receive by device
@@ -2009,7 +1974,6 @@ static const struct net_device_ops sxgbe_netdev_ops = {
        .ndo_set_rx_mode        = sxgbe_set_rx_mode,
        .ndo_tx_timeout         = sxgbe_tx_timeout,
        .ndo_do_ioctl           = sxgbe_ioctl,
-       .ndo_set_config         = sxgbe_config,
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller    = sxgbe_poll_controller,
 #endif
index 56f8bf5a3f1b99564a2b055830810fe3d156e6c4..81437d91df99ded30f4d34594b03126ef034d42d 100644 (file)
 
 /* L3/L4 function registers */
 #define SXGBE_CORE_L34_ADDCTL_REG      0x0C00
-#define SXGBE_CORE_L34_ADDCTL_REG      0x0C00
 #define SXGBE_CORE_L34_DATA_REG                0x0C04
 
 /* ARP registers */
index 63d595fd3cc5f5a9df298dfdd2583abcec9a3a03..1e274045970fa011c6dacb6baeb77db95178b164 100644 (file)
@@ -2248,7 +2248,7 @@ static int efx_register_netdev(struct efx_nic *efx)
        } else {
                net_dev->netdev_ops = &efx_farch_netdev_ops;
        }
-       SET_ETHTOOL_OPS(net_dev, &efx_ethtool_ops);
+       net_dev->ethtool_ops = &efx_ethtool_ops;
        net_dev->gso_max_segs = EFX_TSO_MAX_SEGS;
 
        rtnl_lock();
index 0de8b07c24c2bf39cd62a06021a42c217a3cba4c..74739c4b9997e433b69571f99886290abed49098 100644 (file)
@@ -1033,7 +1033,7 @@ static u32 efx_ethtool_get_rxfh_indir_size(struct net_device *net_dev)
                0 : ARRAY_SIZE(efx->rx_indir_table));
 }
 
-static int efx_ethtool_get_rxfh_indir(struct net_device *net_dev, u32 *indir)
+static int efx_ethtool_get_rxfh(struct net_device *net_dev, u32 *indir, u8 *key)
 {
        struct efx_nic *efx = netdev_priv(net_dev);
 
@@ -1041,8 +1041,8 @@ static int efx_ethtool_get_rxfh_indir(struct net_device *net_dev, u32 *indir)
        return 0;
 }
 
-static int efx_ethtool_set_rxfh_indir(struct net_device *net_dev,
-                                     const u32 *indir)
+static int efx_ethtool_set_rxfh(struct net_device *net_dev,
+                               const u32 *indir, const u8 *key)
 {
        struct efx_nic *efx = netdev_priv(net_dev);
 
@@ -1125,8 +1125,8 @@ const struct ethtool_ops efx_ethtool_ops = {
        .get_rxnfc              = efx_ethtool_get_rxnfc,
        .set_rxnfc              = efx_ethtool_set_rxnfc,
        .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_rxfh               = efx_ethtool_get_rxfh,
+       .set_rxfh               = efx_ethtool_set_rxfh,
        .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 4d3f119b67b38ec35719ebac40d59f49ea9b844f..afb94aa2c15e9f8eb8ec5ebfc8a02ef8f8bc505e 100644 (file)
 #define EFX_USE_QWORD_IO 1
 #endif
 
+/* Hardware issue requires that only 64-bit naturally aligned writes
+ * are seen by hardware. Its not strictly necessary to restrict to
+ * x86_64 arch, but done for safety since unusual write combining behaviour
+ * can break PIO.
+ */
+#ifdef CONFIG_X86_64
 /* PIO is a win only if write-combining is possible */
 #ifdef ARCH_HAS_IOREMAP_WC
 #define EFX_USE_PIO 1
 #endif
+#endif
 
 #ifdef EFX_USE_QWORD_IO
 static inline void _efx_writeq(struct efx_nic *efx, __le64 value,
index 9a9205e778964186d09094128a933179926f8006..43d2e64546ed1d924c91d26900b37d1b100dd994 100644 (file)
@@ -1633,7 +1633,8 @@ int efx_sriov_get_vf_config(struct net_device *net_dev, int vf_i,
 
        ivi->vf = vf_i;
        ether_addr_copy(ivi->mac, vf->addr.mac_addr);
-       ivi->tx_rate = 0;
+       ivi->max_tx_rate = 0;
+       ivi->min_tx_rate = 0;
        tci = ntohs(vf->addr.tci);
        ivi->vlan = tci & VLAN_VID_MASK;
        ivi->qos = (tci >> VLAN_PRIO_SHIFT) & 0x7;
index fa9475300411507e447fd51e21dfbd4c5f600aa3..ede8dcca0ff3516c33a2aee884952b7514924676 100644 (file)
@@ -189,6 +189,18 @@ struct efx_short_copy_buffer {
        u8 buf[L1_CACHE_BYTES];
 };
 
+/* Copy in explicit 64-bit writes. */
+static void efx_memcpy_64(void __iomem *dest, void *src, size_t len)
+{
+       u64 *src64 = src;
+       u64 __iomem *dest64 = dest;
+       size_t l64 = len / 8;
+       size_t i;
+
+       for (i = 0; i < l64; i++)
+               writeq(src64[i], &dest64[i]);
+}
+
 /* Copy to PIO, respecting that writes to PIO buffers must be dword aligned.
  * Advances piobuf pointer. Leaves additional data in the copy buffer.
  */
@@ -198,7 +210,7 @@ static void efx_memcpy_toio_aligned(struct efx_nic *efx, u8 __iomem **piobuf,
 {
        int block_len = len & ~(sizeof(copy_buf->buf) - 1);
 
-       memcpy_toio(*piobuf, data, block_len);
+       efx_memcpy_64(*piobuf, data, block_len);
        *piobuf += block_len;
        len -= block_len;
 
@@ -230,7 +242,7 @@ static void efx_memcpy_toio_aligned_cb(struct efx_nic *efx, u8 __iomem **piobuf,
                if (copy_buf->used < sizeof(copy_buf->buf))
                        return;
 
-               memcpy_toio(*piobuf, copy_buf->buf, sizeof(copy_buf->buf));
+               efx_memcpy_64(*piobuf, copy_buf->buf, sizeof(copy_buf->buf));
                *piobuf += sizeof(copy_buf->buf);
                data += copy_to_buf;
                len -= copy_to_buf;
@@ -245,7 +257,7 @@ static void efx_flush_copy_buffer(struct efx_nic *efx, u8 __iomem *piobuf,
 {
        /* if there's anything in it, write the whole buffer, including junk */
        if (copy_buf->used)
-               memcpy_toio(piobuf, copy_buf->buf, sizeof(copy_buf->buf));
+               efx_memcpy_64(piobuf, copy_buf->buf, sizeof(copy_buf->buf));
 }
 
 /* Traverse skb structure and copy fragments in to PIO buffer.
@@ -304,8 +316,8 @@ efx_enqueue_skb_pio(struct efx_tx_queue *tx_queue, struct sk_buff *skb)
                 */
                BUILD_BUG_ON(L1_CACHE_BYTES >
                             SKB_DATA_ALIGN(sizeof(struct skb_shared_info)));
-               memcpy_toio(tx_queue->piobuf, skb->data,
-                           ALIGN(skb->len, L1_CACHE_BYTES));
+               efx_memcpy_64(tx_queue->piobuf, skb->data,
+                             ALIGN(skb->len, L1_CACHE_BYTES));
        }
 
        EFX_POPULATE_QWORD_5(buffer->option,
index acbbe48a519c0c673ff8de55c31b1690c3976cb5..a86339903b9b0662a835df431f2b79d0677aa93f 100644 (file)
@@ -1877,7 +1877,7 @@ static int sis190_init_one(struct pci_dev *pdev,
 
        dev->netdev_ops = &sis190_netdev_ops;
 
-       SET_ETHTOOL_OPS(dev, &sis190_ethtool_ops);
+       dev->ethtool_ops = &sis190_ethtool_ops;
        dev->watchdog_timeo = SIS190_TX_TIMEOUT;
 
        spin_lock_init(&tp->lock);
index c7a4868571f9f81f1607dc50225dec8bdaf6f017..6b33127ab352a43ed6a787af7eedde554241e1b3 100644 (file)
@@ -318,7 +318,7 @@ static int smc91c92_probe(struct pcmcia_device *link)
 
     /* The SMC91c92-specific entries in the device structure. */
     dev->netdev_ops = &smc_netdev_ops;
-    SET_ETHTOOL_OPS(dev, &ethtool_ops);
+    dev->ethtool_ops = &ethtool_ops;
     dev->watchdog_timeo = TX_TIMEOUT;
 
     smc->mii_if.dev = dev;
index a0fc151da40db13c20681e04c07d5e923566a14b..5e13fa5524ae911f5516f060a5acdb729fec7be0 100644 (file)
@@ -2477,6 +2477,8 @@ static int smsc911x_drv_probe(struct platform_device *pdev)
                goto out_disable_resources;
        }
 
+       netif_carrier_off(dev);
+
        retval = register_netdev(dev);
        if (retval) {
                SMSC_WARN(pdata, probe, "Error %i registering device", retval);
index c5f9cb85c8ef9c31c2ce05894faffa2a484a8093..c62e67f3c2f0692f85b39c42a69de3fc5ed37b22 100644 (file)
@@ -322,9 +322,7 @@ static int stmmac_ethtool_getsettings(struct net_device *dev,
                return -EBUSY;
        }
        cmd->transceiver = XCVR_INTERNAL;
-       spin_lock_irq(&priv->lock);
        rc = phy_ethtool_gset(phy, cmd);
-       spin_unlock_irq(&priv->lock);
        return rc;
 }
 
@@ -431,8 +429,6 @@ stmmac_get_pauseparam(struct net_device *netdev,
        if (priv->pcs)  /* FIXME */
                return;
 
-       spin_lock(&priv->lock);
-
        pause->rx_pause = 0;
        pause->tx_pause = 0;
        pause->autoneg = priv->phydev->autoneg;
@@ -442,7 +438,6 @@ stmmac_get_pauseparam(struct net_device *netdev,
        if (priv->flow_ctrl & FLOW_TX)
                pause->tx_pause = 1;
 
-       spin_unlock(&priv->lock);
 }
 
 static int
@@ -457,8 +452,6 @@ stmmac_set_pauseparam(struct net_device *netdev,
        if (priv->pcs)  /* FIXME */
                return -EOPNOTSUPP;
 
-       spin_lock(&priv->lock);
-
        if (pause->rx_pause)
                new_pause |= FLOW_RX;
        if (pause->tx_pause)
@@ -473,7 +466,6 @@ stmmac_set_pauseparam(struct net_device *netdev,
        } else
                priv->hw->mac->flow_ctrl(priv->ioaddr, phy->duplex,
                                         priv->flow_ctrl, priv->pause);
-       spin_unlock(&priv->lock);
        return ret;
 }
 
@@ -784,5 +776,5 @@ static const struct ethtool_ops stmmac_ethtool_ops = {
 
 void stmmac_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &stmmac_ethtool_ops);
+       netdev->ethtool_ops = &stmmac_ethtool_ops;
 }
index 0f4841d2e8dc9b556bde072a1786697ea7041460..057a1208e5947f9c6ae473c0ab2d6d978091fff7 100644 (file)
@@ -1753,7 +1753,7 @@ static int stmmac_open(struct net_device *dev)
        }
 
        /* Request the IRQ lines */
-       if (priv->lpi_irq != -ENXIO) {
+       if (priv->lpi_irq > 0) {
                ret = request_irq(priv->lpi_irq, stmmac_interrupt, IRQF_SHARED,
                                  dev->name, dev);
                if (unlikely(ret < 0)) {
@@ -1813,7 +1813,7 @@ static int stmmac_release(struct net_device *dev)
        free_irq(dev->irq, dev);
        if (priv->wol_irq != dev->irq)
                free_irq(priv->wol_irq, dev);
-       if (priv->lpi_irq != -ENXIO)
+       if (priv->lpi_irq > 0)
                free_irq(priv->lpi_irq, dev);
 
        /* Stop TX/RX DMA and clear the descriptors */
@@ -2212,27 +2212,6 @@ static void stmmac_tx_timeout(struct net_device *dev)
        stmmac_tx_err(priv);
 }
 
-/* Configuration changes (passed on by ifconfig) */
-static int stmmac_config(struct net_device *dev, struct ifmap *map)
-{
-       if (dev->flags & IFF_UP)        /* can't act on a running interface */
-               return -EBUSY;
-
-       /* Don't allow changing the I/O address */
-       if (map->base_addr != dev->base_addr) {
-               pr_warn("%s: can't change I/O address\n", dev->name);
-               return -EOPNOTSUPP;
-       }
-
-       /* Don't allow changing the IRQ */
-       if (map->irq != dev->irq) {
-               pr_warn("%s: not change IRQ number %d\n", dev->name, dev->irq);
-               return -EOPNOTSUPP;
-       }
-
-       return 0;
-}
-
 /**
  *  stmmac_set_rx_mode - entry point for multicast addressing
  *  @dev : pointer to the device structure
@@ -2598,7 +2577,6 @@ static const struct net_device_ops stmmac_netdev_ops = {
        .ndo_set_rx_mode = stmmac_set_rx_mode,
        .ndo_tx_timeout = stmmac_tx_timeout,
        .ndo_do_ioctl = stmmac_ioctl,
-       .ndo_set_config = stmmac_config,
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller = stmmac_poll_controller,
 #endif
index a468eb10782361e31fd0b0af5ce6be33d68a2578..a5b1e1b776fe3313c5c9852062ed66ea1547c285 100644 (file)
@@ -205,10 +205,13 @@ int stmmac_mdio_register(struct net_device *ndev)
        if (new_bus == NULL)
                return -ENOMEM;
 
-       if (mdio_bus_data->irqs)
+       if (mdio_bus_data->irqs) {
                irqlist = mdio_bus_data->irqs;
-       else
+       } else {
+               for (addr = 0; addr < PHY_MAX_ADDR; addr++)
+                       priv->mii_irq[addr] = PHY_POLL;
                irqlist = priv->mii_irq;
+       }
 
 #ifdef CONFIG_OF
        if (priv->device->of_node)
index 46aef5108bea47c7d6e49b891937d05ac2610c85..ea7a65be1f9af4208748d808128a344bfdb74377 100644 (file)
@@ -237,10 +237,12 @@ static int stmmac_pltfr_probe(struct platform_device *pdev)
 
        /* Get the MAC information */
        priv->dev->irq = platform_get_irq_byname(pdev, "macirq");
-       if (priv->dev->irq == -ENXIO) {
-               pr_err("%s: ERROR: MAC IRQ configuration "
-                      "information not found\n", __func__);
-               return -ENXIO;
+       if (priv->dev->irq < 0) {
+               if (priv->dev->irq != -EPROBE_DEFER) {
+                       netdev_err(priv->dev,
+                                  "MAC IRQ configuration information not found\n");
+               }
+               return priv->dev->irq;
        }
 
        /*
@@ -252,10 +254,15 @@ static int stmmac_pltfr_probe(struct platform_device *pdev)
         * so the driver will continue to use the mac irq (ndev->irq)
         */
        priv->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq");
-       if (priv->wol_irq == -ENXIO)
+       if (priv->wol_irq < 0) {
+               if (priv->wol_irq == -EPROBE_DEFER)
+                       return -EPROBE_DEFER;
                priv->wol_irq = priv->dev->irq;
+       }
 
        priv->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi");
+       if (priv->lpi_irq == -EPROBE_DEFER)
+               return -EPROBE_DEFER;
 
        platform_set_drvdata(pdev, priv->dev);
 
index 2ead87759ab411819be6cb03fa8de3cef8ea2a1f..38da73a2a886b52b3753d3b9dd31f214d345e4fd 100644 (file)
@@ -2413,7 +2413,7 @@ static void bdx_set_ethtool_ops(struct net_device *netdev)
                .get_ethtool_stats = bdx_get_ethtool_stats,
        };
 
-       SET_ETHTOOL_OPS(netdev, &bdx_ethtool_ops);
+       netdev->ethtool_ops = &bdx_ethtool_ops;
 }
 
 /**
index 73f74f369437174144b59a8f1426cbbcbaf21c34..7399a52f7c260aa7a037cbe134b35ca82b6cf840 100644 (file)
@@ -313,19 +313,6 @@ static int mii_irqs[PHY_MAX_ADDR] = { PHY_POLL, };
 
 static struct mii_bus *cpmac_mii;
 
-static int cpmac_config(struct net_device *dev, struct ifmap *map)
-{
-       if (dev->flags & IFF_UP)
-               return -EBUSY;
-
-       /* Don't allow changing the I/O address */
-       if (map->base_addr != dev->base_addr)
-               return -EOPNOTSUPP;
-
-       /* ignore other fields */
-       return 0;
-}
-
 static void cpmac_set_multicast_list(struct net_device *dev)
 {
        struct netdev_hw_addr *ha;
@@ -1100,7 +1087,6 @@ static const struct net_device_ops cpmac_netdev_ops = {
        .ndo_tx_timeout         = cpmac_tx_timeout,
        .ndo_set_rx_mode        = cpmac_set_multicast_list,
        .ndo_do_ioctl           = cpmac_ioctl,
-       .ndo_set_config         = cpmac_config,
        .ndo_change_mtu         = eth_change_mtu,
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_set_mac_address    = eth_mac_addr,
index 148da9ae83666ce7cd2284c1cc75f18e9957f15f..aa8bf45e53dc9b0ddab4265b2e527067339a00fc 100644 (file)
@@ -29,6 +29,8 @@
 #define AM33XX_GMII_SEL_RMII2_IO_CLK_EN        BIT(7)
 #define AM33XX_GMII_SEL_RMII1_IO_CLK_EN        BIT(6)
 
+#define GMII_SEL_MODE_MASK             0x3
+
 struct cpsw_phy_sel_priv {
        struct device   *dev;
        u32 __iomem     *gmii_sel;
@@ -65,7 +67,7 @@ static void cpsw_gmii_sel_am3352(struct cpsw_phy_sel_priv *priv,
                break;
        };
 
-       mask = 0x3 << (slave * 2) | BIT(slave + 6);
+       mask = GMII_SEL_MODE_MASK << (slave * 2) | BIT(slave + 6);
        mode <<= slave * 2;
 
        if (priv->rmii_clock_external) {
@@ -81,6 +83,55 @@ static void cpsw_gmii_sel_am3352(struct cpsw_phy_sel_priv *priv,
        writel(reg, priv->gmii_sel);
 }
 
+static void cpsw_gmii_sel_dra7xx(struct cpsw_phy_sel_priv *priv,
+                                phy_interface_t phy_mode, int slave)
+{
+       u32 reg;
+       u32 mask;
+       u32 mode = 0;
+
+       reg = readl(priv->gmii_sel);
+
+       switch (phy_mode) {
+       case PHY_INTERFACE_MODE_RMII:
+               mode = AM33XX_GMII_SEL_MODE_RMII;
+               break;
+
+       case PHY_INTERFACE_MODE_RGMII:
+       case PHY_INTERFACE_MODE_RGMII_ID:
+       case PHY_INTERFACE_MODE_RGMII_RXID:
+       case PHY_INTERFACE_MODE_RGMII_TXID:
+               mode = AM33XX_GMII_SEL_MODE_RGMII;
+               break;
+
+       case PHY_INTERFACE_MODE_MII:
+       default:
+               mode = AM33XX_GMII_SEL_MODE_MII;
+               break;
+       };
+
+       switch (slave) {
+       case 0:
+               mask = GMII_SEL_MODE_MASK;
+               break;
+       case 1:
+               mask = GMII_SEL_MODE_MASK << 4;
+               mode <<= 4;
+               break;
+       default:
+               dev_err(priv->dev, "invalid slave number...\n");
+               return;
+       }
+
+       if (priv->rmii_clock_external)
+               dev_err(priv->dev, "RMII External clock is not supported\n");
+
+       reg &= ~mask;
+       reg |= mode;
+
+       writel(reg, priv->gmii_sel);
+}
+
 static struct platform_driver cpsw_phy_sel_driver;
 static int match(struct device *dev, void *data)
 {
@@ -112,6 +163,14 @@ static const struct of_device_id cpsw_phy_sel_id_table[] = {
                .compatible     = "ti,am3352-cpsw-phy-sel",
                .data           = &cpsw_gmii_sel_am3352,
        },
+       {
+               .compatible     = "ti,dra7xx-cpsw-phy-sel",
+               .data           = &cpsw_gmii_sel_dra7xx,
+       },
+       {
+               .compatible     = "ti,am43xx-cpsw-phy-sel",
+               .data           = &cpsw_gmii_sel_am3352,
+       },
        {}
 };
 MODULE_DEVICE_TABLE(of, cpsw_phy_sel_id_table);
@@ -132,6 +191,7 @@ static int cpsw_phy_sel_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
+       priv->dev = &pdev->dev;
        priv->cpsw_phy_sel = of_id->data;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gmii-sel");
index c331b7ebc8124585f989d6fb3dcdbaf8a3d7d3ed..ff380dac6629d16f4d3642f9ed6789ec2da7f2ab 100644 (file)
@@ -143,13 +143,13 @@ do {                                                              \
                u32 i;          \
                for (i = 0; i < priv->num_irqs; i++) \
                        enable_irq(priv->irqs_table[i]); \
-       } while (0);
+       } while (0)
 #define cpsw_disable_irq(priv) \
        do {                    \
                u32 i;          \
                for (i = 0; i < priv->num_irqs; i++) \
                        disable_irq_nosync(priv->irqs_table[i]); \
-       } while (0);
+       } while (0)
 
 #define cpsw_slave_index(priv)                         \
                ((priv->data.dual_emac) ? priv->emac_port :     \
@@ -248,20 +248,31 @@ struct cpsw_ss_regs {
 #define TS_131              (1<<11) /* Time Sync Dest IP Addr 131 enable */
 #define TS_130              (1<<10) /* Time Sync Dest IP Addr 130 enable */
 #define TS_129              (1<<9)  /* Time Sync Dest IP Addr 129 enable */
-#define TS_BIT8             (1<<8)  /* ts_ttl_nonzero? */
+#define TS_TTL_NONZERO      (1<<8)  /* Time Sync Time To Live Non-zero enable */
+#define TS_ANNEX_F_EN       (1<<6)  /* Time Sync Annex F enable */
 #define TS_ANNEX_D_EN       (1<<4)  /* Time Sync Annex D enable */
 #define TS_LTYPE2_EN        (1<<3)  /* Time Sync LTYPE 2 enable */
 #define TS_LTYPE1_EN        (1<<2)  /* Time Sync LTYPE 1 enable */
 #define TS_TX_EN            (1<<1)  /* Time Sync Transmit Enable */
 #define TS_RX_EN            (1<<0)  /* Time Sync Receive Enable */
 
-#define CTRL_TS_BITS \
-       (TS_320 | TS_319 | TS_132 | TS_131 | TS_130 | TS_129 | TS_BIT8 | \
-        TS_ANNEX_D_EN | TS_LTYPE1_EN)
+#define CTRL_V2_TS_BITS \
+       (TS_320 | TS_319 | TS_132 | TS_131 | TS_130 | TS_129 |\
+        TS_TTL_NONZERO  | TS_ANNEX_D_EN | TS_LTYPE1_EN)
+
+#define CTRL_V2_ALL_TS_MASK (CTRL_V2_TS_BITS | TS_TX_EN | TS_RX_EN)
+#define CTRL_V2_TX_TS_BITS  (CTRL_V2_TS_BITS | TS_TX_EN)
+#define CTRL_V2_RX_TS_BITS  (CTRL_V2_TS_BITS | TS_RX_EN)
+
 
-#define CTRL_ALL_TS_MASK (CTRL_TS_BITS | TS_TX_EN | TS_RX_EN)
-#define CTRL_TX_TS_BITS  (CTRL_TS_BITS | TS_TX_EN)
-#define CTRL_RX_TS_BITS  (CTRL_TS_BITS | TS_RX_EN)
+#define CTRL_V3_TS_BITS \
+       (TS_320 | TS_319 | TS_132 | TS_131 | TS_130 | TS_129 |\
+        TS_TTL_NONZERO | TS_ANNEX_F_EN | TS_ANNEX_D_EN |\
+        TS_LTYPE1_EN)
+
+#define CTRL_V3_ALL_TS_MASK (CTRL_V3_TS_BITS | TS_TX_EN | TS_RX_EN)
+#define CTRL_V3_TX_TS_BITS  (CTRL_V3_TS_BITS | TS_TX_EN)
+#define CTRL_V3_RX_TS_BITS  (CTRL_V3_TS_BITS | TS_RX_EN)
 
 /* Bit definitions for the CPSW2_TS_SEQ_MTYPE register */
 #define TS_SEQ_ID_OFFSET_SHIFT   (16)    /* Time Sync Sequence ID Offset */
@@ -1376,13 +1387,27 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv)
                slave = &priv->slaves[priv->data.active_slave];
 
        ctrl = slave_read(slave, CPSW2_CONTROL);
-       ctrl &= ~CTRL_ALL_TS_MASK;
+       switch (priv->version) {
+       case CPSW_VERSION_2:
+               ctrl &= ~CTRL_V2_ALL_TS_MASK;
 
-       if (priv->cpts->tx_enable)
-               ctrl |= CTRL_TX_TS_BITS;
+               if (priv->cpts->tx_enable)
+                       ctrl |= CTRL_V2_TX_TS_BITS;
 
-       if (priv->cpts->rx_enable)
-               ctrl |= CTRL_RX_TS_BITS;
+               if (priv->cpts->rx_enable)
+                       ctrl |= CTRL_V2_RX_TS_BITS;
+       break;
+       case CPSW_VERSION_3:
+       default:
+               ctrl &= ~CTRL_V3_ALL_TS_MASK;
+
+               if (priv->cpts->tx_enable)
+                       ctrl |= CTRL_V3_TX_TS_BITS;
+
+               if (priv->cpts->rx_enable)
+                       ctrl |= CTRL_V3_RX_TS_BITS;
+       break;
+       }
 
        mtype = (30 << TS_SEQ_ID_OFFSET_SHIFT) | EVENT_MSG_BITS;
 
@@ -1398,7 +1423,8 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
        struct hwtstamp_config cfg;
 
        if (priv->version != CPSW_VERSION_1 &&
-           priv->version != CPSW_VERSION_2)
+           priv->version != CPSW_VERSION_2 &&
+           priv->version != CPSW_VERSION_3)
                return -EOPNOTSUPP;
 
        if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
@@ -1443,6 +1469,7 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
                cpsw_hwtstamp_v1(priv);
                break;
        case CPSW_VERSION_2:
+       case CPSW_VERSION_3:
                cpsw_hwtstamp_v2(priv);
                break;
        default:
@@ -1459,7 +1486,8 @@ static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
        struct hwtstamp_config cfg;
 
        if (priv->version != CPSW_VERSION_1 &&
-           priv->version != CPSW_VERSION_2)
+           priv->version != CPSW_VERSION_2 &&
+           priv->version != CPSW_VERSION_3)
                return -EOPNOTSUPP;
 
        cfg.flags = 0;
@@ -1780,25 +1808,25 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
                return -EINVAL;
 
        if (of_property_read_u32(node, "slaves", &prop)) {
-               pr_err("Missing slaves property in the DT.\n");
+               dev_err(&pdev->dev, "Missing slaves property in the DT.\n");
                return -EINVAL;
        }
        data->slaves = prop;
 
        if (of_property_read_u32(node, "active_slave", &prop)) {
-               pr_err("Missing active_slave property in the DT.\n");
+               dev_err(&pdev->dev, "Missing active_slave property in the DT.\n");
                return -EINVAL;
        }
        data->active_slave = prop;
 
        if (of_property_read_u32(node, "cpts_clock_mult", &prop)) {
-               pr_err("Missing cpts_clock_mult property in the DT.\n");
+               dev_err(&pdev->dev, "Missing cpts_clock_mult property in the DT.\n");
                return -EINVAL;
        }
        data->cpts_clock_mult = prop;
 
        if (of_property_read_u32(node, "cpts_clock_shift", &prop)) {
-               pr_err("Missing cpts_clock_shift property in the DT.\n");
+               dev_err(&pdev->dev, "Missing cpts_clock_shift property in the DT.\n");
                return -EINVAL;
        }
        data->cpts_clock_shift = prop;
@@ -1810,31 +1838,31 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
                return -ENOMEM;
 
        if (of_property_read_u32(node, "cpdma_channels", &prop)) {
-               pr_err("Missing cpdma_channels property in the DT.\n");
+               dev_err(&pdev->dev, "Missing cpdma_channels property in the DT.\n");
                return -EINVAL;
        }
        data->channels = prop;
 
        if (of_property_read_u32(node, "ale_entries", &prop)) {
-               pr_err("Missing ale_entries property in the DT.\n");
+               dev_err(&pdev->dev, "Missing ale_entries property in the DT.\n");
                return -EINVAL;
        }
        data->ale_entries = prop;
 
        if (of_property_read_u32(node, "bd_ram_size", &prop)) {
-               pr_err("Missing bd_ram_size property in the DT.\n");
+               dev_err(&pdev->dev, "Missing bd_ram_size property in the DT.\n");
                return -EINVAL;
        }
        data->bd_ram_size = prop;
 
        if (of_property_read_u32(node, "rx_descs", &prop)) {
-               pr_err("Missing rx_descs property in the DT.\n");
+               dev_err(&pdev->dev, "Missing rx_descs property in the DT.\n");
                return -EINVAL;
        }
        data->rx_descs = prop;
 
        if (of_property_read_u32(node, "mac_control", &prop)) {
-               pr_err("Missing mac_control property in the DT.\n");
+               dev_err(&pdev->dev, "Missing mac_control property in the DT.\n");
                return -EINVAL;
        }
        data->mac_control = prop;
@@ -1848,7 +1876,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
        ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
        /* We do not want to force this, as in some cases may not have child */
        if (ret)
-               pr_warn("Doesn't have any child node\n");
+               dev_warn(&pdev->dev, "Doesn't have any child node\n");
 
        for_each_child_of_node(node, slave_node) {
                struct cpsw_slave_data *slave_data = data->slave_data + i;
@@ -1865,7 +1893,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
 
                parp = of_get_property(slave_node, "phy_id", &lenp);
                if ((parp == NULL) || (lenp != (sizeof(void *) * 2))) {
-                       pr_err("Missing slave[%d] phy_id property\n", i);
+                       dev_err(&pdev->dev, "Missing slave[%d] phy_id property\n", i);
                        return -EINVAL;
                }
                mdio_node = of_find_node_by_phandle(be32_to_cpup(parp));
@@ -1885,18 +1913,18 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
 
                slave_data->phy_if = of_get_phy_mode(slave_node);
                if (slave_data->phy_if < 0) {
-                       pr_err("Missing or malformed slave[%d] phy-mode property\n",
-                              i);
+                       dev_err(&pdev->dev, "Missing or malformed slave[%d] phy-mode property\n",
+                               i);
                        return slave_data->phy_if;
                }
 
                if (data->dual_emac) {
                        if (of_property_read_u32(slave_node, "dual_emac_res_vlan",
                                                 &prop)) {
-                               pr_err("Missing dual_emac_res_vlan in DT.\n");
+                               dev_err(&pdev->dev, "Missing dual_emac_res_vlan in DT.\n");
                                slave_data->dual_emac_res_vlan = i+1;
-                               pr_err("Using %d as Reserved VLAN for %d slave\n",
-                                      slave_data->dual_emac_res_vlan, i);
+                               dev_err(&pdev->dev, "Using %d as Reserved VLAN for %d slave\n",
+                                       slave_data->dual_emac_res_vlan, i);
                        } else {
                                slave_data->dual_emac_res_vlan = prop;
                        }
@@ -1920,7 +1948,7 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev,
 
        ndev = alloc_etherdev(sizeof(struct cpsw_priv));
        if (!ndev) {
-               pr_err("cpsw: error allocating net_device\n");
+               dev_err(&pdev->dev, "cpsw: error allocating net_device\n");
                return -ENOMEM;
        }
 
@@ -1936,10 +1964,10 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev,
        if (is_valid_ether_addr(data->slave_data[1].mac_addr)) {
                memcpy(priv_sl2->mac_addr, data->slave_data[1].mac_addr,
                        ETH_ALEN);
-               pr_info("cpsw: Detected MACID = %pM\n", priv_sl2->mac_addr);
+               dev_info(&pdev->dev, "cpsw: Detected MACID = %pM\n", priv_sl2->mac_addr);
        } else {
                random_ether_addr(priv_sl2->mac_addr);
-               pr_info("cpsw: Random MACID = %pM\n", priv_sl2->mac_addr);
+               dev_info(&pdev->dev, "cpsw: Random MACID = %pM\n", priv_sl2->mac_addr);
        }
        memcpy(ndev->dev_addr, priv_sl2->mac_addr, ETH_ALEN);
 
@@ -1970,14 +1998,14 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev,
        ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
 
        ndev->netdev_ops = &cpsw_netdev_ops;
-       SET_ETHTOOL_OPS(ndev, &cpsw_ethtool_ops);
+       ndev->ethtool_ops = &cpsw_ethtool_ops;
        netif_napi_add(ndev, &priv_sl2->napi, cpsw_poll, CPSW_POLL_WEIGHT);
 
        /* register the network device */
        SET_NETDEV_DEV(ndev, &pdev->dev);
        ret = register_netdev(ndev);
        if (ret) {
-               pr_err("cpsw: error registering net device\n");
+               dev_err(&pdev->dev, "cpsw: error registering net device\n");
                free_netdev(ndev);
                ret = -ENODEV;
        }
@@ -1999,7 +2027,7 @@ static int cpsw_probe(struct platform_device *pdev)
 
        ndev = alloc_etherdev(sizeof(struct cpsw_priv));
        if (!ndev) {
-               pr_err("error allocating net_device\n");
+               dev_err(&pdev->dev, "error allocating net_device\n");
                return -ENOMEM;
        }
 
@@ -2014,7 +2042,7 @@ static int cpsw_probe(struct platform_device *pdev)
        priv->cpts = devm_kzalloc(&pdev->dev, sizeof(struct cpts), GFP_KERNEL);
        priv->irq_enabled = true;
        if (!priv->cpts) {
-               pr_err("error allocating cpts\n");
+               dev_err(&pdev->dev, "error allocating cpts\n");
                goto clean_ndev_ret;
        }
 
@@ -2027,7 +2055,7 @@ static int cpsw_probe(struct platform_device *pdev)
        pinctrl_pm_select_default_state(&pdev->dev);
 
        if (cpsw_probe_dt(&priv->data, pdev)) {
-               pr_err("cpsw: platform data missing\n");
+               dev_err(&pdev->dev, "cpsw: platform data missing\n");
                ret = -ENODEV;
                goto clean_runtime_disable_ret;
        }
@@ -2035,10 +2063,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\n", priv->mac_addr);
+               dev_info(&pdev->dev, "Detected MACID = %pM\n", priv->mac_addr);
        } else {
                eth_random_addr(priv->mac_addr);
-               pr_info("Random MACID = %pM\n", priv->mac_addr);
+               dev_info(&pdev->dev, "Random MACID = %pM\n", priv->mac_addr);
        }
 
        memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN);
@@ -2199,7 +2227,7 @@ static int cpsw_probe(struct platform_device *pdev)
        ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
 
        ndev->netdev_ops = &cpsw_netdev_ops;
-       SET_ETHTOOL_OPS(ndev, &cpsw_ethtool_ops);
+       ndev->ethtool_ops = &cpsw_ethtool_ops;
        netif_napi_add(ndev, &priv->napi, cpsw_poll, CPSW_POLL_WEIGHT);
 
        /* register the network device */
index 243513980b51511a9c3054d71c8a270d63949cb1..6b56f85951e581826afc152109d0eee4b53dd08d 100644 (file)
@@ -236,13 +236,11 @@ static void cpts_overflow_check(struct work_struct *work)
        schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD);
 }
 
-#define CPTS_REF_CLOCK_NAME "cpsw_cpts_rft_clk"
-
-static void cpts_clk_init(struct cpts *cpts)
+static void cpts_clk_init(struct device *dev, struct cpts *cpts)
 {
-       cpts->refclk = clk_get(NULL, CPTS_REF_CLOCK_NAME);
+       cpts->refclk = devm_clk_get(dev, "cpts");
        if (IS_ERR(cpts->refclk)) {
-               pr_err("Failed to clk_get %s\n", CPTS_REF_CLOCK_NAME);
+               dev_err(dev, "Failed to get cpts refclk\n");
                cpts->refclk = NULL;
                return;
        }
@@ -252,7 +250,6 @@ static void cpts_clk_init(struct cpts *cpts)
 static void cpts_clk_release(struct cpts *cpts)
 {
        clk_disable(cpts->refclk);
-       clk_put(cpts->refclk);
 }
 
 static int cpts_match(struct sk_buff *skb, unsigned int ptp_class,
@@ -390,7 +387,7 @@ int cpts_register(struct device *dev, struct cpts *cpts,
        for (i = 0; i < CPTS_MAX_EVENTS; i++)
                list_add(&cpts->pool_data[i].list, &cpts->pool);
 
-       cpts_clk_init(cpts);
+       cpts_clk_init(dev, cpts);
        cpts_write32(cpts, CPTS_EN, control);
        cpts_write32(cpts, TS_PEND_EN, int_enable);
 
index 88ef27067bf24a8b2569f533b63ac223d52280fe..4a000f6dd6fc35d274f585a8536bd325666fdcb2 100644 (file)
@@ -158,9 +158,9 @@ cpdma_desc_pool_create(struct device *dev, u32 phys, u32 hw_addr,
        int bitmap_size;
        struct cpdma_desc_pool *pool;
 
-       pool = kzalloc(sizeof(*pool), GFP_KERNEL);
+       pool = devm_kzalloc(dev, sizeof(*pool), GFP_KERNEL);
        if (!pool)
-               return NULL;
+               goto fail;
 
        spin_lock_init(&pool->lock);
 
@@ -170,7 +170,7 @@ cpdma_desc_pool_create(struct device *dev, u32 phys, u32 hw_addr,
        pool->num_desc  = size / pool->desc_size;
 
        bitmap_size  = (pool->num_desc / BITS_PER_LONG) * sizeof(long);
-       pool->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
+       pool->bitmap = devm_kzalloc(dev, bitmap_size, GFP_KERNEL);
        if (!pool->bitmap)
                goto fail;
 
@@ -187,10 +187,7 @@ cpdma_desc_pool_create(struct device *dev, u32 phys, u32 hw_addr,
 
        if (pool->iomap)
                return pool;
-
 fail:
-       kfree(pool->bitmap);
-       kfree(pool);
        return NULL;
 }
 
@@ -203,7 +200,6 @@ static void cpdma_desc_pool_destroy(struct cpdma_desc_pool *pool)
 
        spin_lock_irqsave(&pool->lock, flags);
        WARN_ON(pool->used_desc);
-       kfree(pool->bitmap);
        if (pool->cpumap) {
                dma_free_coherent(pool->dev, pool->mem_size, pool->cpumap,
                                  pool->phys);
@@ -211,7 +207,6 @@ static void cpdma_desc_pool_destroy(struct cpdma_desc_pool *pool)
                iounmap(pool->iomap);
        }
        spin_unlock_irqrestore(&pool->lock, flags);
-       kfree(pool);
 }
 
 static inline dma_addr_t desc_phys(struct cpdma_desc_pool *pool,
@@ -276,7 +271,7 @@ struct cpdma_ctlr *cpdma_ctlr_create(struct cpdma_params *params)
 {
        struct cpdma_ctlr *ctlr;
 
-       ctlr = kzalloc(sizeof(*ctlr), GFP_KERNEL);
+       ctlr = devm_kzalloc(params->dev, sizeof(*ctlr), GFP_KERNEL);
        if (!ctlr)
                return NULL;
 
@@ -290,10 +285,8 @@ struct cpdma_ctlr *cpdma_ctlr_create(struct cpdma_params *params)
                                            ctlr->params.desc_hw_addr,
                                            ctlr->params.desc_mem_size,
                                            ctlr->params.desc_align);
-       if (!ctlr->pool) {
-               kfree(ctlr);
+       if (!ctlr->pool)
                return NULL;
-       }
 
        if (WARN_ON(ctlr->num_chan > CPDMA_MAX_CHANNELS))
                ctlr->num_chan = CPDMA_MAX_CHANNELS;
@@ -468,7 +461,6 @@ int cpdma_ctlr_destroy(struct cpdma_ctlr *ctlr)
 
        cpdma_desc_pool_destroy(ctlr->pool);
        spin_unlock_irqrestore(&ctlr->lock, flags);
-       kfree(ctlr);
        return ret;
 }
 EXPORT_SYMBOL_GPL(cpdma_ctlr_destroy);
@@ -507,21 +499,22 @@ struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
                                     cpdma_handler_fn handler)
 {
        struct cpdma_chan *chan;
-       int ret, offset = (chan_num % CPDMA_MAX_CHANNELS) * 4;
+       int offset = (chan_num % CPDMA_MAX_CHANNELS) * 4;
        unsigned long flags;
 
        if (__chan_linear(chan_num) >= ctlr->num_chan)
                return NULL;
 
-       ret = -ENOMEM;
-       chan = kzalloc(sizeof(*chan), GFP_KERNEL);
+       chan = devm_kzalloc(ctlr->dev, sizeof(*chan), GFP_KERNEL);
        if (!chan)
-               goto err_chan_alloc;
+               return ERR_PTR(-ENOMEM);
 
        spin_lock_irqsave(&ctlr->lock, flags);
-       ret = -EBUSY;
-       if (ctlr->channels[chan_num])
-               goto err_chan_busy;
+       if (ctlr->channels[chan_num]) {
+               spin_unlock_irqrestore(&ctlr->lock, flags);
+               devm_kfree(ctlr->dev, chan);
+               return ERR_PTR(-EBUSY);
+       }
 
        chan->ctlr      = ctlr;
        chan->state     = CPDMA_STATE_IDLE;
@@ -551,12 +544,6 @@ struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
        ctlr->channels[chan_num] = chan;
        spin_unlock_irqrestore(&ctlr->lock, flags);
        return chan;
-
-err_chan_busy:
-       spin_unlock_irqrestore(&ctlr->lock, flags);
-       kfree(chan);
-err_chan_alloc:
-       return ERR_PTR(ret);
 }
 EXPORT_SYMBOL_GPL(cpdma_chan_create);
 
index 8f0e69ce07ca3e03cfbf4cf1b22c92c9af27beeb..35a139e9a833520a195d16a7c620d423638eb90c 100644 (file)
@@ -1567,7 +1567,6 @@ static int emac_dev_open(struct net_device *ndev)
        while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ,
                                            res_num))) {
                for (irq_num = res->start; irq_num <= res->end; irq_num++) {
-                       dev_err(emac_dev, "Request IRQ %d\n", irq_num);
                        if (request_irq(irq_num, emac_irq, 0, ndev->name,
                                        ndev)) {
                                dev_err(emac_dev,
@@ -1865,7 +1864,6 @@ static int davinci_emac_probe(struct platform_device *pdev)
        struct emac_priv *priv;
        unsigned long hw_ram_addr;
        struct emac_platform_data *pdata;
-       struct device *emac_dev;
        struct cpdma_params dma_params;
        struct clk *emac_clk;
        unsigned long emac_bus_frequency;
@@ -1911,7 +1909,6 @@ static int davinci_emac_probe(struct platform_device *pdev)
        priv->coal_intvl = 0;
        priv->bus_freq_mhz = (u32)(emac_bus_frequency / 1000000);
 
-       emac_dev = &ndev->dev;
        /* Get EMAC platform data */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        priv->emac_base_phys = res->start + pdata->ctrl_reg_offset;
@@ -1930,7 +1927,7 @@ static int davinci_emac_probe(struct platform_device *pdev)
                hw_ram_addr = (u32 __force)res->start + pdata->ctrl_ram_offset;
 
        memset(&dma_params, 0, sizeof(dma_params));
-       dma_params.dev                  = emac_dev;
+       dma_params.dev                  = &pdev->dev;
        dma_params.dmaregs              = priv->emac_base;
        dma_params.rxthresh             = priv->emac_base + 0x120;
        dma_params.rxfree               = priv->emac_base + 0x140;
@@ -1980,7 +1977,7 @@ static int davinci_emac_probe(struct platform_device *pdev)
        }
 
        ndev->netdev_ops = &emac_netdev_ops;
-       SET_ETHTOOL_OPS(ndev, &ethtool_ops);
+       ndev->ethtool_ops = &ethtool_ops;
        netif_napi_add(ndev, &priv->napi, emac_poll, EMAC_POLL_WEIGHT);
 
        /* register the network device */
@@ -1994,7 +1991,7 @@ static int davinci_emac_probe(struct platform_device *pdev)
 
 
        if (netif_msg_probe(priv)) {
-               dev_notice(emac_dev, "DaVinci EMAC Probe found device "\
+               dev_notice(&pdev->dev, "DaVinci EMAC Probe found device "
                           "(regs: %p, irq: %d)\n",
                           (void *)priv->emac_base_phys, ndev->irq);
        }
index 0cca9dec5d8277542a4439aed4bb8953f3943b29..735dc53d4b0163be05eec83c83a4fb8bb4612497 100644 (file)
@@ -303,7 +303,7 @@ static int davinci_mdio_probe_dt(struct mdio_platform_data *data,
                return -EINVAL;
 
        if (of_property_read_u32(node, "bus_freq", &prop)) {
-               pr_err("Missing bus_freq property in the DT.\n");
+               dev_err(&pdev->dev, "Missing bus_freq property in the DT.\n");
                return -EINVAL;
        }
        data->bus_freq = prop;
@@ -321,15 +321,14 @@ static int davinci_mdio_probe(struct platform_device *pdev)
        struct phy_device *phy;
        int ret, addr;
 
-       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
 
-       data->bus = mdiobus_alloc();
+       data->bus = devm_mdiobus_alloc(dev);
        if (!data->bus) {
                dev_err(dev, "failed to alloc mii bus\n");
-               ret = -ENOMEM;
-               goto bail_out;
+               return -ENOMEM;
        }
 
        if (dev->of_node) {
@@ -349,12 +348,9 @@ static int davinci_mdio_probe(struct platform_device *pdev)
        data->bus->parent       = dev;
        data->bus->priv         = data;
 
-       /* Select default pin state */
-       pinctrl_pm_select_default_state(&pdev->dev);
-
        pm_runtime_enable(&pdev->dev);
        pm_runtime_get_sync(&pdev->dev);
-       data->clk = clk_get(&pdev->dev, "fck");
+       data->clk = devm_clk_get(dev, "fck");
        if (IS_ERR(data->clk)) {
                dev_err(dev, "failed to get device clock\n");
                ret = PTR_ERR(data->clk);
@@ -367,24 +363,9 @@ static int davinci_mdio_probe(struct platform_device *pdev)
        spin_lock_init(&data->lock);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(dev, "could not find register map resource\n");
-               ret = -ENOENT;
-               goto bail_out;
-       }
-
-       res = devm_request_mem_region(dev, res->start, resource_size(res),
-                                           dev_name(dev));
-       if (!res) {
-               dev_err(dev, "could not allocate register map resource\n");
-               ret = -ENXIO;
-               goto bail_out;
-       }
-
-       data->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
-       if (!data->regs) {
-               dev_err(dev, "could not map mdio registers\n");
-               ret = -ENOMEM;
+       data->regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(data->regs)) {
+               ret = PTR_ERR(data->regs);
                goto bail_out;
        }
 
@@ -406,16 +387,9 @@ static int davinci_mdio_probe(struct platform_device *pdev)
        return 0;
 
 bail_out:
-       if (data->bus)
-               mdiobus_free(data->bus);
-
-       if (data->clk)
-               clk_put(data->clk);
        pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
-       kfree(data);
-
        return ret;
 }
 
@@ -423,18 +397,12 @@ static int davinci_mdio_remove(struct platform_device *pdev)
 {
        struct davinci_mdio_data *data = platform_get_drvdata(pdev);
 
-       if (data->bus) {
+       if (data->bus)
                mdiobus_unregister(data->bus);
-               mdiobus_free(data->bus);
-       }
 
-       if (data->clk)
-               clk_put(data->clk);
        pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
-       kfree(data);
-
        return 0;
 }
 
index 449011b0e007d6b798a91728eaa845e05e581305..14389f841d431c1236f5ea90d0d3eb995a41c39c 100644 (file)
@@ -2192,7 +2192,6 @@ static void tile_net_dev_init(const char *name, const uint8_t *mac)
 {
        int ret;
        int i;
-       int nz_addr = 0;
        struct net_device *dev;
        struct tile_net_priv *priv;
 
@@ -2212,7 +2211,6 @@ static void tile_net_dev_init(const char *name, const uint8_t *mac)
 
        /* Initialize "priv". */
        priv = netdev_priv(dev);
-       memset(priv, 0, sizeof(*priv));
        priv->dev = dev;
        priv->channel = -1;
        priv->loopify_channel = -1;
@@ -2223,15 +2221,10 @@ static void tile_net_dev_init(const char *name, const uint8_t *mac)
         * be done before the device is opened.  If the MAC is all zeroes,
         * we use a random address, since we're probably on the simulator.
         */
-       for (i = 0; i < 6; i++)
-               nz_addr |= mac[i];
-
-       if (nz_addr) {
-               memcpy(dev->dev_addr, mac, ETH_ALEN);
-               dev->addr_len = 6;
-       } else {
+       if (!is_zero_ether_addr(mac))
+               ether_addr_copy(dev->dev_addr, mac);
+       else
                eth_hw_addr_random(dev);
-       }
 
        /* Register the network device. */
        ret = register_netdev(dev);
index d899d0072ae050d481b9d4510956bfb6146efac2..bb79928046645d62fbb238614777b00600b62b0d 100644 (file)
@@ -1561,7 +1561,7 @@ static struct gelic_card *gelic_alloc_card_net(struct net_device **netdev)
         * alloc netdev
         */
        *netdev = alloc_etherdev(sizeof(struct gelic_port));
-       if (!netdev) {
+       if (!*netdev) {
                kfree(card->unalign);
                return NULL;
        }
index 8a049a2b44742aabe24221b12a6bcb2a4caf357f..f66ddaee0c877f1620ad123fd9ef8402703aa1a6 100644 (file)
@@ -19,7 +19,7 @@ if NET_VENDOR_VIA
 
 config VIA_RHINE
        tristate "VIA Rhine support"
-       depends on PCI
+       depends on (PCI || USE_OF)
        select CRC32
        select MII
        ---help---
index f61dc2b72bb2f43780ace58a503bd2e3b89f61a9..2d72f96a9e2cf93d4f1dca3aad98e780bcfc5bca 100644 (file)
@@ -94,6 +94,10 @@ static const int multicast_filter_limit = 32;
 #include <linux/ioport.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -116,13 +120,6 @@ static const int multicast_filter_limit = 32;
 static const char version[] =
        "v1.10-LK" DRV_VERSION " " DRV_RELDATE " Written by Donald Becker";
 
-/* This driver was written to use PCI memory space. Some early versions
-   of the Rhine may only work correctly with I/O space accesses. */
-#ifdef CONFIG_VIA_RHINE_MMIO
-#define USE_MMIO
-#else
-#endif
-
 MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
 MODULE_DESCRIPTION("VIA Rhine PCI Fast Ethernet driver");
 MODULE_LICENSE("GPL");
@@ -260,6 +257,12 @@ enum rhine_quirks {
        rq6patterns     = 0x0040,       /* 6 instead of 4 patterns for WOL */
        rqStatusWBRace  = 0x0080,       /* Tx Status Writeback Error possible */
        rqRhineI        = 0x0100,       /* See comment below */
+       rqIntPHY        = 0x0200,       /* Integrated PHY */
+       rqMgmt          = 0x0400,       /* Management adapter */
+       rqNeedEnMMIO    = 0x0800,       /* Whether the core needs to be
+                                        * switched from PIO mode to MMIO
+                                        * (only applies to PCI)
+                                        */
 };
 /*
  * rqRhineI: VT86C100A (aka Rhine-I) uses different bits to enable
@@ -279,6 +282,15 @@ static DEFINE_PCI_DEVICE_TABLE(rhine_pci_tbl) = {
 };
 MODULE_DEVICE_TABLE(pci, rhine_pci_tbl);
 
+/* OpenFirmware identifiers for platform-bus devices
+ * The .data field is currently only used to store quirks
+ */
+static u32 vt8500_quirks = rqWOL | rqForceReset | rq6patterns;
+static struct of_device_id rhine_of_tbl[] = {
+       { .compatible = "via,vt8500-rhine", .data = &vt8500_quirks },
+       { }     /* terminate list */
+};
+MODULE_DEVICE_TABLE(of, rhine_of_tbl);
 
 /* Offsets to the device registers. */
 enum register_offsets {
@@ -338,13 +350,11 @@ enum bcr1_bits {
        BCR1_MED1=0x80,         /* for VT6102 */
 };
 
-#ifdef USE_MMIO
 /* Registers we check that mmio and reg are the same. */
 static const int mmio_verify_registers[] = {
        RxConfig, TxConfig, IntrEnable, ConfigA, ConfigB, ConfigC, ConfigD,
        0
 };
-#endif
 
 /* Bits in the interrupt status/mask registers. */
 enum intr_status_bits {
@@ -446,7 +456,7 @@ struct rhine_private {
        unsigned char *tx_bufs;
        dma_addr_t tx_bufs_dma;
 
-       struct pci_dev *pdev;
+       int irq;
        long pioaddr;
        struct net_device *dev;
        struct napi_struct napi;
@@ -649,20 +659,46 @@ static void rhine_chip_reset(struct net_device *dev)
                   "failed" : "succeeded");
 }
 
-#ifdef USE_MMIO
 static void enable_mmio(long pioaddr, u32 quirks)
 {
        int n;
-       if (quirks & rqRhineI) {
-               /* More recent docs say that this bit is reserved ... */
-               n = inb(pioaddr + ConfigA) | 0x20;
-               outb(n, pioaddr + ConfigA);
-       } else {
-               n = inb(pioaddr + ConfigD) | 0x80;
-               outb(n, pioaddr + ConfigD);
+
+       if (quirks & rqNeedEnMMIO) {
+               if (quirks & rqRhineI) {
+                       /* More recent docs say that this bit is reserved */
+                       n = inb(pioaddr + ConfigA) | 0x20;
+                       outb(n, pioaddr + ConfigA);
+               } else {
+                       n = inb(pioaddr + ConfigD) | 0x80;
+                       outb(n, pioaddr + ConfigD);
+               }
        }
 }
-#endif
+
+static inline int verify_mmio(struct device *hwdev,
+                             long pioaddr,
+                             void __iomem *ioaddr,
+                             u32 quirks)
+{
+       if (quirks & rqNeedEnMMIO) {
+               int i = 0;
+
+               /* Check that selected MMIO registers match the PIO ones */
+               while (mmio_verify_registers[i]) {
+                       int reg = mmio_verify_registers[i++];
+                       unsigned char a = inb(pioaddr+reg);
+                       unsigned char b = readb(ioaddr+reg);
+
+                       if (a != b) {
+                               dev_err(hwdev,
+                                       "MMIO do not match PIO [%02x] (%02x != %02x)\n",
+                                       reg, a, b);
+                               return -EIO;
+                       }
+               }
+       }
+       return 0;
+}
 
 /*
  * Loads bytes 0x00-0x05, 0x6E-0x6F, 0x78-0x7B from EEPROM
@@ -682,14 +718,12 @@ static void rhine_reload_eeprom(long pioaddr, struct net_device *dev)
        if (i > 512)
                pr_info("%4d cycles used @ %s:%d\n", i, __func__, __LINE__);
 
-#ifdef USE_MMIO
        /*
         * Reloading from EEPROM overwrites ConfigA-D, so we must re-enable
         * MMIO. If reloading EEPROM was done first this could be avoided, but
         * it is not known if that still works with the "win98-reboot" problem.
         */
        enable_mmio(pioaddr, rp->quirks);
-#endif
 
        /* Turn off EEPROM-controlled wake-up (magic packet) */
        if (rp->quirks & rqWOL)
@@ -701,7 +735,7 @@ static void rhine_reload_eeprom(long pioaddr, struct net_device *dev)
 static void rhine_poll(struct net_device *dev)
 {
        struct rhine_private *rp = netdev_priv(dev);
-       const int irq = rp->pdev->irq;
+       const int irq = rp->irq;
 
        disable_irq(irq);
        rhine_interrupt(irq, dev);
@@ -846,7 +880,8 @@ static void rhine_hw_init(struct net_device *dev, long pioaddr)
                msleep(5);
 
        /* Reload EEPROM controlled bytes cleared by soft reset */
-       rhine_reload_eeprom(pioaddr, dev);
+       if (dev_is_pci(dev->dev.parent))
+               rhine_reload_eeprom(pioaddr, dev);
 }
 
 static const struct net_device_ops rhine_netdev_ops = {
@@ -867,125 +902,37 @@ static const struct net_device_ops rhine_netdev_ops = {
 #endif
 };
 
-static int rhine_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+static int rhine_init_one_common(struct device *hwdev, u32 quirks,
+                                long pioaddr, void __iomem *ioaddr, int irq)
 {
        struct net_device *dev;
        struct rhine_private *rp;
-       int i, rc;
-       u32 quirks;
-       long pioaddr;
-       long memaddr;
-       void __iomem *ioaddr;
-       int io_size, phy_id;
+       int i, rc, phy_id;
        const char *name;
-#ifdef USE_MMIO
-       int bar = 1;
-#else
-       int bar = 0;
-#endif
-
-/* when built into the kernel, we only print version if device is found */
-#ifndef MODULE
-       pr_info_once("%s\n", version);
-#endif
-
-       io_size = 256;
-       phy_id = 0;
-       quirks = 0;
-       name = "Rhine";
-       if (pdev->revision < VTunknown0) {
-               quirks = rqRhineI;
-               io_size = 128;
-       }
-       else if (pdev->revision >= VT6102) {
-               quirks = rqWOL | rqForceReset;
-               if (pdev->revision < VT6105) {
-                       name = "Rhine II";
-                       quirks |= rqStatusWBRace;       /* Rhine-II exclusive */
-               }
-               else {
-                       phy_id = 1;     /* Integrated PHY, phy_id fixed to 1 */
-                       if (pdev->revision >= VT6105_B0)
-                               quirks |= rq6patterns;
-                       if (pdev->revision < VT6105M)
-                               name = "Rhine III";
-                       else
-                               name = "Rhine III (Management Adapter)";
-               }
-       }
-
-       rc = pci_enable_device(pdev);
-       if (rc)
-               goto err_out;
 
        /* this should always be supported */
-       rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+       rc = dma_set_mask(hwdev, DMA_BIT_MASK(32));
        if (rc) {
-               dev_err(&pdev->dev,
-                       "32-bit PCI DMA addresses not supported by the card!?\n");
-               goto err_out_pci_disable;
-       }
-
-       /* sanity check */
-       if ((pci_resource_len(pdev, 0) < io_size) ||
-           (pci_resource_len(pdev, 1) < io_size)) {
-               rc = -EIO;
-               dev_err(&pdev->dev, "Insufficient PCI resources, aborting\n");
-               goto err_out_pci_disable;
+               dev_err(hwdev, "32-bit DMA addresses not supported by the card!?\n");
+               goto err_out;
        }
 
-       pioaddr = pci_resource_start(pdev, 0);
-       memaddr = pci_resource_start(pdev, 1);
-
-       pci_set_master(pdev);
-
        dev = alloc_etherdev(sizeof(struct rhine_private));
        if (!dev) {
                rc = -ENOMEM;
-               goto err_out_pci_disable;
+               goto err_out;
        }
-       SET_NETDEV_DEV(dev, &pdev->dev);
+       SET_NETDEV_DEV(dev, hwdev);
 
        rp = netdev_priv(dev);
        rp->dev = dev;
        rp->quirks = quirks;
        rp->pioaddr = pioaddr;
-       rp->pdev = pdev;
+       rp->base = ioaddr;
+       rp->irq = irq;
        rp->msg_enable = netif_msg_init(debug, RHINE_MSG_DEFAULT);
 
-       rc = pci_request_regions(pdev, DRV_NAME);
-       if (rc)
-               goto err_out_free_netdev;
-
-       ioaddr = pci_iomap(pdev, bar, io_size);
-       if (!ioaddr) {
-               rc = -EIO;
-               dev_err(&pdev->dev,
-                       "ioremap failed for device %s, region 0x%X @ 0x%lX\n",
-                       pci_name(pdev), io_size, memaddr);
-               goto err_out_free_res;
-       }
-
-#ifdef USE_MMIO
-       enable_mmio(pioaddr, quirks);
-
-       /* Check that selected MMIO registers match the PIO ones */
-       i = 0;
-       while (mmio_verify_registers[i]) {
-               int reg = mmio_verify_registers[i++];
-               unsigned char a = inb(pioaddr+reg);
-               unsigned char b = readb(ioaddr+reg);
-               if (a != b) {
-                       rc = -EIO;
-                       dev_err(&pdev->dev,
-                               "MMIO do not match PIO [%02x] (%02x != %02x)\n",
-                               reg, a, b);
-                       goto err_out_unmap;
-               }
-       }
-#endif /* USE_MMIO */
-
-       rp->base = ioaddr;
+       phy_id = rp->quirks & rqIntPHY ? 1 : 0;
 
        u64_stats_init(&rp->tx_stats.syncp);
        u64_stats_init(&rp->rx_stats.syncp);
@@ -1030,7 +977,7 @@ static int rhine_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (rp->quirks & rqRhineI)
                dev->features |= NETIF_F_SG|NETIF_F_HW_CSUM;
 
-       if (pdev->revision >= VT6105M)
+       if (rp->quirks & rqMgmt)
                dev->features |= NETIF_F_HW_VLAN_CTAG_TX |
                                 NETIF_F_HW_VLAN_CTAG_RX |
                                 NETIF_F_HW_VLAN_CTAG_FILTER;
@@ -1038,18 +985,21 @@ static int rhine_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        /* dev->name not defined before register_netdev()! */
        rc = register_netdev(dev);
        if (rc)
-               goto err_out_unmap;
+               goto err_out_free_netdev;
+
+       if (rp->quirks & rqRhineI)
+               name = "Rhine";
+       else if (rp->quirks & rqStatusWBRace)
+               name = "Rhine II";
+       else if (rp->quirks & rqMgmt)
+               name = "Rhine III (Management Adapter)";
+       else
+               name = "Rhine III";
 
        netdev_info(dev, "VIA %s at 0x%lx, %pM, IRQ %d\n",
-                   name,
-#ifdef USE_MMIO
-                   memaddr,
-#else
-                   (long)ioaddr,
-#endif
-                   dev->dev_addr, pdev->irq);
+                   name, (long)ioaddr, dev->dev_addr, rp->irq);
 
-       pci_set_drvdata(pdev, dev);
+       dev_set_drvdata(hwdev, dev);
 
        {
                u16 mii_cmd;
@@ -1078,41 +1028,158 @@ static int rhine_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        return 0;
 
+err_out_free_netdev:
+       free_netdev(dev);
+err_out:
+       return rc;
+}
+
+static int rhine_init_one_pci(struct pci_dev *pdev,
+                             const struct pci_device_id *ent)
+{
+       struct device *hwdev = &pdev->dev;
+       int rc;
+       long pioaddr, memaddr;
+       void __iomem *ioaddr;
+       int io_size = pdev->revision < VTunknown0 ? 128 : 256;
+
+/* This driver was written to use PCI memory space. Some early versions
+ * of the Rhine may only work correctly with I/O space accesses.
+ * TODO: determine for which revisions this is true and assign the flag
+ *      in code as opposed to this Kconfig option (???)
+ */
+#ifdef CONFIG_VIA_RHINE_MMIO
+       u32 quirks = rqNeedEnMMIO;
+#else
+       u32 quirks = 0;
+#endif
+
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+       pr_info_once("%s\n", version);
+#endif
+
+       rc = pci_enable_device(pdev);
+       if (rc)
+               goto err_out;
+
+       if (pdev->revision < VTunknown0) {
+               quirks |= rqRhineI;
+       } else if (pdev->revision >= VT6102) {
+               quirks |= rqWOL | rqForceReset;
+               if (pdev->revision < VT6105) {
+                       quirks |= rqStatusWBRace;
+               } else {
+                       quirks |= rqIntPHY;
+                       if (pdev->revision >= VT6105_B0)
+                               quirks |= rq6patterns;
+                       if (pdev->revision >= VT6105M)
+                               quirks |= rqMgmt;
+               }
+       }
+
+       /* sanity check */
+       if ((pci_resource_len(pdev, 0) < io_size) ||
+           (pci_resource_len(pdev, 1) < io_size)) {
+               rc = -EIO;
+               dev_err(hwdev, "Insufficient PCI resources, aborting\n");
+               goto err_out_pci_disable;
+       }
+
+       pioaddr = pci_resource_start(pdev, 0);
+       memaddr = pci_resource_start(pdev, 1);
+
+       pci_set_master(pdev);
+
+       rc = pci_request_regions(pdev, DRV_NAME);
+       if (rc)
+               goto err_out_pci_disable;
+
+       ioaddr = pci_iomap(pdev, (quirks & rqNeedEnMMIO ? 1 : 0), io_size);
+       if (!ioaddr) {
+               rc = -EIO;
+               dev_err(hwdev,
+                       "ioremap failed for device %s, region 0x%X @ 0x%lX\n",
+                       dev_name(hwdev), io_size, memaddr);
+               goto err_out_free_res;
+       }
+
+       enable_mmio(pioaddr, quirks);
+
+       rc = verify_mmio(hwdev, pioaddr, ioaddr, quirks);
+       if (rc)
+               goto err_out_unmap;
+
+       rc = rhine_init_one_common(&pdev->dev, quirks,
+                                  pioaddr, ioaddr, pdev->irq);
+       if (!rc)
+               return 0;
+
 err_out_unmap:
        pci_iounmap(pdev, ioaddr);
 err_out_free_res:
        pci_release_regions(pdev);
-err_out_free_netdev:
-       free_netdev(dev);
 err_out_pci_disable:
        pci_disable_device(pdev);
 err_out:
        return rc;
 }
 
+static int rhine_init_one_platform(struct platform_device *pdev)
+{
+       const struct of_device_id *match;
+       const u32 *quirks;
+       int irq;
+       struct resource *res;
+       void __iomem *ioaddr;
+
+       match = of_match_device(rhine_of_tbl, &pdev->dev);
+       if (!match)
+               return -EINVAL;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       ioaddr = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(ioaddr))
+               return PTR_ERR(ioaddr);
+
+       irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+       if (!irq)
+               return -EINVAL;
+
+       quirks = match->data;
+       if (!quirks)
+               return -EINVAL;
+
+       return rhine_init_one_common(&pdev->dev, *quirks,
+                                    (long)ioaddr, ioaddr, irq);
+}
+
 static int alloc_ring(struct net_device* dev)
 {
        struct rhine_private *rp = netdev_priv(dev);
+       struct device *hwdev = dev->dev.parent;
        void *ring;
        dma_addr_t ring_dma;
 
-       ring = pci_alloc_consistent(rp->pdev,
-                                   RX_RING_SIZE * sizeof(struct rx_desc) +
-                                   TX_RING_SIZE * sizeof(struct tx_desc),
-                                   &ring_dma);
+       ring = dma_alloc_coherent(hwdev,
+                                 RX_RING_SIZE * sizeof(struct rx_desc) +
+                                 TX_RING_SIZE * sizeof(struct tx_desc),
+                                 &ring_dma,
+                                 GFP_ATOMIC);
        if (!ring) {
                netdev_err(dev, "Could not allocate DMA memory\n");
                return -ENOMEM;
        }
        if (rp->quirks & rqRhineI) {
-               rp->tx_bufs = pci_alloc_consistent(rp->pdev,
-                                                  PKT_BUF_SZ * TX_RING_SIZE,
-                                                  &rp->tx_bufs_dma);
+               rp->tx_bufs = dma_alloc_coherent(hwdev,
+                                                PKT_BUF_SZ * TX_RING_SIZE,
+                                                &rp->tx_bufs_dma,
+                                                GFP_ATOMIC);
                if (rp->tx_bufs == NULL) {
-                       pci_free_consistent(rp->pdev,
-                                   RX_RING_SIZE * sizeof(struct rx_desc) +
-                                   TX_RING_SIZE * sizeof(struct tx_desc),
-                                   ring, ring_dma);
+                       dma_free_coherent(hwdev,
+                                         RX_RING_SIZE * sizeof(struct rx_desc) +
+                                         TX_RING_SIZE * sizeof(struct tx_desc),
+                                         ring, ring_dma);
                        return -ENOMEM;
                }
        }
@@ -1128,16 +1195,17 @@ static int alloc_ring(struct net_device* dev)
 static void free_ring(struct net_device* dev)
 {
        struct rhine_private *rp = netdev_priv(dev);
+       struct device *hwdev = dev->dev.parent;
 
-       pci_free_consistent(rp->pdev,
-                           RX_RING_SIZE * sizeof(struct rx_desc) +
-                           TX_RING_SIZE * sizeof(struct tx_desc),
-                           rp->rx_ring, rp->rx_ring_dma);
+       dma_free_coherent(hwdev,
+                         RX_RING_SIZE * sizeof(struct rx_desc) +
+                         TX_RING_SIZE * sizeof(struct tx_desc),
+                         rp->rx_ring, rp->rx_ring_dma);
        rp->tx_ring = NULL;
 
        if (rp->tx_bufs)
-               pci_free_consistent(rp->pdev, PKT_BUF_SZ * TX_RING_SIZE,
-                                   rp->tx_bufs, rp->tx_bufs_dma);
+               dma_free_coherent(hwdev, PKT_BUF_SZ * TX_RING_SIZE,
+                                 rp->tx_bufs, rp->tx_bufs_dma);
 
        rp->tx_bufs = NULL;
 
@@ -1146,6 +1214,7 @@ static void free_ring(struct net_device* dev)
 static void alloc_rbufs(struct net_device *dev)
 {
        struct rhine_private *rp = netdev_priv(dev);
+       struct device *hwdev = dev->dev.parent;
        dma_addr_t next;
        int i;
 
@@ -1174,9 +1243,9 @@ static void alloc_rbufs(struct net_device *dev)
                        break;
 
                rp->rx_skbuff_dma[i] =
-                       pci_map_single(rp->pdev, skb->data, rp->rx_buf_sz,
-                                      PCI_DMA_FROMDEVICE);
-               if (dma_mapping_error(&rp->pdev->dev, rp->rx_skbuff_dma[i])) {
+                       dma_map_single(hwdev, skb->data, rp->rx_buf_sz,
+                                      DMA_FROM_DEVICE);
+               if (dma_mapping_error(hwdev, rp->rx_skbuff_dma[i])) {
                        rp->rx_skbuff_dma[i] = 0;
                        dev_kfree_skb(skb);
                        break;
@@ -1190,6 +1259,7 @@ static void alloc_rbufs(struct net_device *dev)
 static void free_rbufs(struct net_device* dev)
 {
        struct rhine_private *rp = netdev_priv(dev);
+       struct device *hwdev = dev->dev.parent;
        int i;
 
        /* Free all the skbuffs in the Rx queue. */
@@ -1197,9 +1267,9 @@ static void free_rbufs(struct net_device* dev)
                rp->rx_ring[i].rx_status = 0;
                rp->rx_ring[i].addr = cpu_to_le32(0xBADF00D0); /* An invalid address. */
                if (rp->rx_skbuff[i]) {
-                       pci_unmap_single(rp->pdev,
+                       dma_unmap_single(hwdev,
                                         rp->rx_skbuff_dma[i],
-                                        rp->rx_buf_sz, PCI_DMA_FROMDEVICE);
+                                        rp->rx_buf_sz, DMA_FROM_DEVICE);
                        dev_kfree_skb(rp->rx_skbuff[i]);
                }
                rp->rx_skbuff[i] = NULL;
@@ -1230,6 +1300,7 @@ static void alloc_tbufs(struct net_device* dev)
 static void free_tbufs(struct net_device* dev)
 {
        struct rhine_private *rp = netdev_priv(dev);
+       struct device *hwdev = dev->dev.parent;
        int i;
 
        for (i = 0; i < TX_RING_SIZE; i++) {
@@ -1238,10 +1309,10 @@ static void free_tbufs(struct net_device* dev)
                rp->tx_ring[i].addr = cpu_to_le32(0xBADF00D0); /* An invalid address. */
                if (rp->tx_skbuff[i]) {
                        if (rp->tx_skbuff_dma[i]) {
-                               pci_unmap_single(rp->pdev,
+                               dma_unmap_single(hwdev,
                                                 rp->tx_skbuff_dma[i],
                                                 rp->tx_skbuff[i]->len,
-                                                PCI_DMA_TODEVICE);
+                                                DMA_TO_DEVICE);
                        }
                        dev_kfree_skb(rp->tx_skbuff[i]);
                }
@@ -1278,8 +1349,9 @@ static void rhine_set_carrier(struct mii_if_info *mii)
                /* autoneg is off: Link is always assumed to be up */
                if (!netif_carrier_ok(dev))
                        netif_carrier_on(dev);
-       } else  /* Let MMI library update carrier status */
-               rhine_check_media(dev, 0);
+       }
+
+       rhine_check_media(dev, 0);
 
        netif_info(rp, link, dev, "force_media %d, carrier %d\n",
                   mii->force_media, netif_carrier_ok(dev));
@@ -1469,7 +1541,7 @@ static void init_registers(struct net_device *dev)
 
        rhine_set_rx_mode(dev);
 
-       if (rp->pdev->revision >= VT6105M)
+       if (rp->quirks & rqMgmt)
                rhine_init_cam_filter(dev);
 
        napi_enable(&rp->napi);
@@ -1581,16 +1653,15 @@ static int rhine_open(struct net_device *dev)
        void __iomem *ioaddr = rp->base;
        int rc;
 
-       rc = request_irq(rp->pdev->irq, rhine_interrupt, IRQF_SHARED, dev->name,
-                       dev);
+       rc = request_irq(rp->irq, rhine_interrupt, IRQF_SHARED, dev->name, dev);
        if (rc)
                return rc;
 
-       netif_dbg(rp, ifup, dev, "%s() irq %d\n", __func__, rp->pdev->irq);
+       netif_dbg(rp, ifup, dev, "%s() irq %d\n", __func__, rp->irq);
 
        rc = alloc_ring(dev);
        if (rc) {
-               free_irq(rp->pdev->irq, dev);
+               free_irq(rp->irq, dev);
                return rc;
        }
        alloc_rbufs(dev);
@@ -1659,6 +1730,7 @@ static netdev_tx_t rhine_start_tx(struct sk_buff *skb,
                                  struct net_device *dev)
 {
        struct rhine_private *rp = netdev_priv(dev);
+       struct device *hwdev = dev->dev.parent;
        void __iomem *ioaddr = rp->base;
        unsigned entry;
 
@@ -1695,9 +1767,9 @@ static netdev_tx_t rhine_start_tx(struct sk_buff *skb,
                                                       rp->tx_bufs));
        } else {
                rp->tx_skbuff_dma[entry] =
-                       pci_map_single(rp->pdev, skb->data, skb->len,
-                                      PCI_DMA_TODEVICE);
-               if (dma_mapping_error(&rp->pdev->dev, rp->tx_skbuff_dma[entry])) {
+                       dma_map_single(hwdev, skb->data, skb->len,
+                                      DMA_TO_DEVICE);
+               if (dma_mapping_error(hwdev, rp->tx_skbuff_dma[entry])) {
                        dev_kfree_skb_any(skb);
                        rp->tx_skbuff_dma[entry] = 0;
                        dev->stats.tx_dropped++;
@@ -1788,6 +1860,7 @@ static irqreturn_t rhine_interrupt(int irq, void *dev_instance)
 static void rhine_tx(struct net_device *dev)
 {
        struct rhine_private *rp = netdev_priv(dev);
+       struct device *hwdev = dev->dev.parent;
        int txstatus = 0, entry = rp->dirty_tx % TX_RING_SIZE;
 
        /* find and cleanup dirty tx descriptors */
@@ -1831,10 +1904,10 @@ static void rhine_tx(struct net_device *dev)
                }
                /* Free the original skb. */
                if (rp->tx_skbuff_dma[entry]) {
-                       pci_unmap_single(rp->pdev,
+                       dma_unmap_single(hwdev,
                                         rp->tx_skbuff_dma[entry],
                                         rp->tx_skbuff[entry]->len,
-                                        PCI_DMA_TODEVICE);
+                                        DMA_TO_DEVICE);
                }
                dev_consume_skb_any(rp->tx_skbuff[entry]);
                rp->tx_skbuff[entry] = NULL;
@@ -1863,6 +1936,7 @@ static inline u16 rhine_get_vlan_tci(struct sk_buff *skb, int data_size)
 static int rhine_rx(struct net_device *dev, int limit)
 {
        struct rhine_private *rp = netdev_priv(dev);
+       struct device *hwdev = dev->dev.parent;
        int count;
        int entry = rp->cur_rx % RX_RING_SIZE;
 
@@ -1924,19 +1998,19 @@ static int rhine_rx(struct net_device *dev, int limit)
                        if (pkt_len < rx_copybreak)
                                skb = netdev_alloc_skb_ip_align(dev, pkt_len);
                        if (skb) {
-                               pci_dma_sync_single_for_cpu(rp->pdev,
-                                                           rp->rx_skbuff_dma[entry],
-                                                           rp->rx_buf_sz,
-                                                           PCI_DMA_FROMDEVICE);
+                               dma_sync_single_for_cpu(hwdev,
+                                                       rp->rx_skbuff_dma[entry],
+                                                       rp->rx_buf_sz,
+                                                       DMA_FROM_DEVICE);
 
                                skb_copy_to_linear_data(skb,
                                                 rp->rx_skbuff[entry]->data,
                                                 pkt_len);
                                skb_put(skb, pkt_len);
-                               pci_dma_sync_single_for_device(rp->pdev,
-                                                              rp->rx_skbuff_dma[entry],
-                                                              rp->rx_buf_sz,
-                                                              PCI_DMA_FROMDEVICE);
+                               dma_sync_single_for_device(hwdev,
+                                                          rp->rx_skbuff_dma[entry],
+                                                          rp->rx_buf_sz,
+                                                          DMA_FROM_DEVICE);
                        } else {
                                skb = rp->rx_skbuff[entry];
                                if (skb == NULL) {
@@ -1945,10 +2019,10 @@ static int rhine_rx(struct net_device *dev, int limit)
                                }
                                rp->rx_skbuff[entry] = NULL;
                                skb_put(skb, pkt_len);
-                               pci_unmap_single(rp->pdev,
+                               dma_unmap_single(hwdev,
                                                 rp->rx_skbuff_dma[entry],
                                                 rp->rx_buf_sz,
-                                                PCI_DMA_FROMDEVICE);
+                                                DMA_FROM_DEVICE);
                        }
 
                        if (unlikely(desc_length & DescTag))
@@ -1979,10 +2053,11 @@ static int rhine_rx(struct net_device *dev, int limit)
                        if (skb == NULL)
                                break;  /* Better luck next round. */
                        rp->rx_skbuff_dma[entry] =
-                               pci_map_single(rp->pdev, skb->data,
+                               dma_map_single(hwdev, skb->data,
                                               rp->rx_buf_sz,
-                                              PCI_DMA_FROMDEVICE);
-                       if (dma_mapping_error(&rp->pdev->dev, rp->rx_skbuff_dma[entry])) {
+                                              DMA_FROM_DEVICE);
+                       if (dma_mapping_error(hwdev,
+                                             rp->rx_skbuff_dma[entry])) {
                                dev_kfree_skb(skb);
                                rp->rx_skbuff_dma[entry] = 0;
                                break;
@@ -2103,7 +2178,7 @@ static void rhine_set_rx_mode(struct net_device *dev)
                /* Too many to match, or accept all multicasts. */
                iowrite32(0xffffffff, ioaddr + MulticastFilter0);
                iowrite32(0xffffffff, ioaddr + MulticastFilter1);
-       } else if (rp->pdev->revision >= VT6105M) {
+       } else if (rp->quirks & rqMgmt) {
                int i = 0;
                u32 mCAMmask = 0;       /* 32 mCAMs (6105M and better) */
                netdev_for_each_mc_addr(ha, dev) {
@@ -2125,7 +2200,7 @@ static void rhine_set_rx_mode(struct net_device *dev)
                iowrite32(mc_filter[1], ioaddr + MulticastFilter1);
        }
        /* enable/disable VLAN receive filtering */
-       if (rp->pdev->revision >= VT6105M) {
+       if (rp->quirks & rqMgmt) {
                if (dev->flags & IFF_PROMISC)
                        BYTE_REG_BITS_OFF(BCR1_VIDFR, ioaddr + PCIBusConfig1);
                else
@@ -2136,11 +2211,11 @@ static void rhine_set_rx_mode(struct net_device *dev)
 
 static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
 {
-       struct rhine_private *rp = netdev_priv(dev);
+       struct device *hwdev = dev->dev.parent;
 
        strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
        strlcpy(info->version, DRV_VERSION, sizeof(info->version));
-       strlcpy(info->bus_info, pci_name(rp->pdev), sizeof(info->bus_info));
+       strlcpy(info->bus_info, dev_name(hwdev), sizeof(info->bus_info));
 }
 
 static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
@@ -2277,7 +2352,7 @@ static int rhine_close(struct net_device *dev)
        /* Stop the chip's Tx and Rx processes. */
        iowrite16(CmdStop, ioaddr + ChipCmd);
 
-       free_irq(rp->pdev->irq, dev);
+       free_irq(rp->irq, dev);
        free_rbufs(dev);
        free_tbufs(dev);
        free_ring(dev);
@@ -2286,7 +2361,7 @@ static int rhine_close(struct net_device *dev)
 }
 
 
-static void rhine_remove_one(struct pci_dev *pdev)
+static void rhine_remove_one_pci(struct pci_dev *pdev)
 {
        struct net_device *dev = pci_get_drvdata(pdev);
        struct rhine_private *rp = netdev_priv(dev);
@@ -2300,7 +2375,21 @@ static void rhine_remove_one(struct pci_dev *pdev)
        pci_disable_device(pdev);
 }
 
-static void rhine_shutdown (struct pci_dev *pdev)
+static int rhine_remove_one_platform(struct platform_device *pdev)
+{
+       struct net_device *dev = platform_get_drvdata(pdev);
+       struct rhine_private *rp = netdev_priv(dev);
+
+       unregister_netdev(dev);
+
+       iounmap(rp->base);
+
+       free_netdev(dev);
+
+       return 0;
+}
+
+static void rhine_shutdown_pci(struct pci_dev *pdev)
 {
        struct net_device *dev = pci_get_drvdata(pdev);
        struct rhine_private *rp = netdev_priv(dev);
@@ -2354,8 +2443,7 @@ static void rhine_shutdown (struct pci_dev *pdev)
 #ifdef CONFIG_PM_SLEEP
 static int rhine_suspend(struct device *device)
 {
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct net_device *dev = pci_get_drvdata(pdev);
+       struct net_device *dev = dev_get_drvdata(device);
        struct rhine_private *rp = netdev_priv(dev);
 
        if (!netif_running(dev))
@@ -2367,23 +2455,21 @@ static int rhine_suspend(struct device *device)
 
        netif_device_detach(dev);
 
-       rhine_shutdown(pdev);
+       if (dev_is_pci(device))
+               rhine_shutdown_pci(to_pci_dev(device));
 
        return 0;
 }
 
 static int rhine_resume(struct device *device)
 {
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct net_device *dev = pci_get_drvdata(pdev);
+       struct net_device *dev = dev_get_drvdata(device);
        struct rhine_private *rp = netdev_priv(dev);
 
        if (!netif_running(dev))
                return 0;
 
-#ifdef USE_MMIO
        enable_mmio(rp->pioaddr, rp->quirks);
-#endif
        rhine_power_init(dev);
        free_tbufs(dev);
        free_rbufs(dev);
@@ -2408,15 +2494,26 @@ static SIMPLE_DEV_PM_OPS(rhine_pm_ops, rhine_suspend, rhine_resume);
 
 #endif /* !CONFIG_PM_SLEEP */
 
-static struct pci_driver rhine_driver = {
+static struct pci_driver rhine_driver_pci = {
        .name           = DRV_NAME,
        .id_table       = rhine_pci_tbl,
-       .probe          = rhine_init_one,
-       .remove         = rhine_remove_one,
-       .shutdown       = rhine_shutdown,
+       .probe          = rhine_init_one_pci,
+       .remove         = rhine_remove_one_pci,
+       .shutdown       = rhine_shutdown_pci,
        .driver.pm      = RHINE_PM_OPS,
 };
 
+static struct platform_driver rhine_driver_platform = {
+       .probe          = rhine_init_one_platform,
+       .remove         = rhine_remove_one_platform,
+       .driver = {
+               .name   = DRV_NAME,
+               .owner  = THIS_MODULE,
+               .of_match_table = rhine_of_tbl,
+               .pm             = RHINE_PM_OPS,
+       }
+};
+
 static struct dmi_system_id rhine_dmi_table[] __initdata = {
        {
                .ident = "EPIA-M",
@@ -2437,6 +2534,8 @@ static struct dmi_system_id rhine_dmi_table[] __initdata = {
 
 static int __init rhine_init(void)
 {
+       int ret_pci, ret_platform;
+
 /* when a module, this is printed whether or not devices are found in probe */
 #ifdef MODULE
        pr_info("%s\n", version);
@@ -2449,13 +2548,19 @@ static int __init rhine_init(void)
        else if (avoid_D3)
                pr_info("avoid_D3 set\n");
 
-       return pci_register_driver(&rhine_driver);
+       ret_pci = pci_register_driver(&rhine_driver_pci);
+       ret_platform = platform_driver_register(&rhine_driver_platform);
+       if ((ret_pci < 0) && (ret_platform < 0))
+               return ret_pci;
+
+       return 0;
 }
 
 
 static void __exit rhine_cleanup(void)
 {
-       pci_unregister_driver(&rhine_driver);
+       platform_driver_unregister(&rhine_driver_platform);
+       pci_unregister_driver(&rhine_driver_pci);
 }
 
 
index fa193c4688da78719257ac982af8be1f81b270c1..4ef818a7a6c623719f0507cfc64b56ef3de709d9 100644 (file)
@@ -75,7 +75,7 @@ int temac_indirect_busywait(struct temac_local *lp)
        long end = jiffies + 2;
 
        while (!(temac_ior(lp, XTE_RDY0_OFFSET) & XTE_RDY0_HARD_ACS_RDY_MASK)) {
-               if (end - jiffies <= 0) {
+               if (time_before_eq(end, jiffies)) {
                        WARN_ON(1);
                        return -ETIMEDOUT;
                }
index 64b4639f43b6bea6b0e69155a7cb7043a14abcc4..d4abf478e2bbf6ae25f5925f406d27923b2b949c 100644 (file)
@@ -22,7 +22,7 @@ int axienet_mdio_wait_until_ready(struct axienet_local *lp)
        long end = jiffies + 2;
        while (!(axienet_ior(lp, XAE_MDIO_MCR_OFFSET) &
                 XAE_MDIO_MCR_READY_MASK)) {
-               if (end - jiffies <= 0) {
+               if (time_before_eq(end, jiffies)) {
                        WARN_ON(1);
                        return -ETIMEDOUT;
                }
index 0d87c67a5ff7208e807a980c406a934214c9d4a6..8c4aed3053ebc0a3a3757dcae408f25249f8e630 100644 (file)
@@ -702,7 +702,7 @@ static int xemaclite_mdio_wait(struct net_local *lp)
        */
        while (__raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET) &
                        XEL_MDIOCTRL_MDIOSTS_MASK) {
-               if (end - jiffies <= 0) {
+               if (time_before_eq(end, jiffies)) {
                        WARN_ON(1);
                        return -ETIMEDOUT;
                }
index d18f711d0b0cd45ae50cc30cc09a9468d4e40d22..6cc37c15e0bf98341131a8229bd7fbd2567994a8 100644 (file)
 #include <linux/hyperv.h>
 #include <linux/rndis.h>
 
-/* Fwd declaration */
-struct hv_netvsc_packet;
-struct ndis_tcp_ip_checksum_info;
+/* RSS related */
+#define OID_GEN_RECEIVE_SCALE_CAPABILITIES 0x00010203  /* query only */
+#define OID_GEN_RECEIVE_SCALE_PARAMETERS 0x00010204  /* query and set */
 
-/* Represent the xfer page packet which contains 1 or more netvsc packet */
-struct xferpage_packet {
-       struct list_head list_ent;
-       u32 status;
+#define NDIS_OBJECT_TYPE_RSS_CAPABILITIES 0x88
+#define NDIS_OBJECT_TYPE_RSS_PARAMETERS 0x89
 
-       /* # of netvsc packets this xfer packet contains */
-       u32 count;
+#define NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2 2
+#define NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2 2
+
+struct ndis_obj_header {
+       u8 type;
+       u8 rev;
+       u16 size;
+} __packed;
+
+/* ndis_recv_scale_cap/cap_flag */
+#define NDIS_RSS_CAPS_MESSAGE_SIGNALED_INTERRUPTS 0x01000000
+#define NDIS_RSS_CAPS_CLASSIFICATION_AT_ISR       0x02000000
+#define NDIS_RSS_CAPS_CLASSIFICATION_AT_DPC       0x04000000
+#define NDIS_RSS_CAPS_USING_MSI_X                 0x08000000
+#define NDIS_RSS_CAPS_RSS_AVAILABLE_ON_PORTS      0x10000000
+#define NDIS_RSS_CAPS_SUPPORTS_MSI_X              0x20000000
+#define NDIS_RSS_CAPS_HASH_TYPE_TCP_IPV4          0x00000100
+#define NDIS_RSS_CAPS_HASH_TYPE_TCP_IPV6          0x00000200
+#define NDIS_RSS_CAPS_HASH_TYPE_TCP_IPV6_EX       0x00000400
+
+struct ndis_recv_scale_cap { /* NDIS_RECEIVE_SCALE_CAPABILITIES */
+       struct ndis_obj_header hdr;
+       u32 cap_flag;
+       u32 num_int_msg;
+       u32 num_recv_que;
+       u16 num_indirect_tabent;
+} __packed;
+
+
+/* ndis_recv_scale_param flags */
+#define NDIS_RSS_PARAM_FLAG_BASE_CPU_UNCHANGED     0x0001
+#define NDIS_RSS_PARAM_FLAG_HASH_INFO_UNCHANGED    0x0002
+#define NDIS_RSS_PARAM_FLAG_ITABLE_UNCHANGED       0x0004
+#define NDIS_RSS_PARAM_FLAG_HASH_KEY_UNCHANGED     0x0008
+#define NDIS_RSS_PARAM_FLAG_DISABLE_RSS            0x0010
+
+/* Hash info bits */
+#define NDIS_HASH_FUNC_TOEPLITZ 0x00000001
+#define NDIS_HASH_IPV4          0x00000100
+#define NDIS_HASH_TCP_IPV4      0x00000200
+#define NDIS_HASH_IPV6          0x00000400
+#define NDIS_HASH_IPV6_EX       0x00000800
+#define NDIS_HASH_TCP_IPV6      0x00001000
+#define NDIS_HASH_TCP_IPV6_EX   0x00002000
+
+#define NDIS_RSS_INDIRECTION_TABLE_MAX_SIZE_REVISION_2 (128 * 4)
+#define NDIS_RSS_HASH_SECRET_KEY_MAX_SIZE_REVISION_2   40
+
+#define ITAB_NUM 128
+#define HASH_KEYLEN NDIS_RSS_HASH_SECRET_KEY_MAX_SIZE_REVISION_2
+extern u8 netvsc_hash_key[];
+
+struct ndis_recv_scale_param { /* NDIS_RECEIVE_SCALE_PARAMETERS */
+       struct ndis_obj_header hdr;
+
+       /* Qualifies the rest of the information */
+       u16 flag;
+
+       /* The base CPU number to do receive processing. not used */
+       u16 base_cpu_number;
+
+       /* This describes the hash function and type being enabled */
+       u32 hashinfo;
+
+       /* The size of indirection table array */
+       u16 indirect_tabsize;
+
+       /* The offset of the indirection table from the beginning of this
+        * structure
+        */
+       u32 indirect_taboffset;
+
+       /* The size of the hash secret key */
+       u16 hashkey_size;
+
+       /* The offset of the secret key from the beginning of this structure */
+       u32 kashkey_offset;
+
+       u32 processor_masks_offset;
+       u32 num_processor_masks;
+       u32 processor_masks_entry_size;
 };
 
+/* Fwd declaration */
+struct ndis_tcp_ip_checksum_info;
+
 /*
  * Represent netvsc packet which contains 1 RNDIS and 1 ethernet frame
  * within the RNDIS
  */
 struct hv_netvsc_packet {
        /* Bookkeeping stuff */
-       struct list_head list_ent;
        u32 status;
 
        struct hv_device *device;
        bool is_data_pkt;
        u16 vlan_tci;
 
-       /*
-        * Valid only for receives when we break a xfer page packet
-        * into multiple netvsc packets
-        */
-       struct xferpage_packet *xfer_page_pkt;
+       u16 q_idx;
+       struct vmbus_channel *channel;
 
-       union {
-               struct {
-                       u64 recv_completion_tid;
-                       void *recv_completion_ctx;
-                       void (*recv_completion)(void *context);
-               } recv;
-               struct {
-                       u64 send_completion_tid;
-                       void *send_completion_ctx;
-                       void (*send_completion)(void *context);
-               } send;
-       } completion;
+       u64 send_completion_tid;
+       void *send_completion_ctx;
+       void (*send_completion)(void *context);
+
+       u32 send_buf_index;
 
        /* This points to the memory after page_buf */
        struct rndis_message *rndis_msg;
@@ -120,6 +189,7 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj,
 int netvsc_recv_callback(struct hv_device *device_obj,
                        struct hv_netvsc_packet *packet,
                        struct ndis_tcp_ip_checksum_info *csum_info);
+void netvsc_channel_cb(void *context);
 int rndis_filter_open(struct hv_device *dev);
 int rndis_filter_close(struct hv_device *dev);
 int rndis_filter_device_add(struct hv_device *dev,
@@ -514,14 +584,16 @@ struct nvsp_message {
 
 #define NETVSC_RECEIVE_BUFFER_SIZE             (1024*1024*16)  /* 16MB */
 #define NETVSC_RECEIVE_BUFFER_SIZE_LEGACY      (1024*1024*15)  /* 15MB */
+#define NETVSC_SEND_BUFFER_SIZE                        (1024 * 1024)   /* 1MB */
+#define NETVSC_INVALID_INDEX                   -1
 
-#define NETVSC_RECEIVE_BUFFER_ID               0xcafe
 
-/* Preallocated receive packets */
-#define NETVSC_RECEIVE_PACKETLIST_COUNT                256
+#define NETVSC_RECEIVE_BUFFER_ID               0xcafe
 
 #define NETVSC_PACKET_SIZE                      2048
 
+#define VRSS_SEND_TAB_SIZE 16
+
 /* Per netvsc channel-specific */
 struct netvsc_device {
        struct hv_device *dev;
@@ -532,12 +604,6 @@ struct netvsc_device {
        wait_queue_head_t wait_drain;
        bool start_remove;
        bool destroy;
-       /*
-        * List of free preallocated hv_netvsc_packet to represent receive
-        * packet
-        */
-       struct list_head recv_pkt_list;
-       spinlock_t recv_pkt_list_lock;
 
        /* Receive buffer allocated by us but manages by NetVSP */
        void *recv_buf;
@@ -546,6 +612,15 @@ struct netvsc_device {
        u32 recv_section_cnt;
        struct nvsp_1_receive_buffer_section *recv_section;
 
+       /* Send buffer allocated by us */
+       void *send_buf;
+       u32 send_buf_size;
+       u32 send_buf_gpadl_handle;
+       u32 send_section_cnt;
+       u32 send_section_size;
+       unsigned long *send_section_map;
+       int map_words;
+
        /* Used for NetVSP initialization protocol */
        struct completion channel_init_wait;
        struct nvsp_message channel_init_pkt;
@@ -555,10 +630,20 @@ struct netvsc_device {
 
        struct net_device *ndev;
 
+       struct vmbus_channel *chn_table[NR_CPUS];
+       u32 send_table[VRSS_SEND_TAB_SIZE];
+       u32 num_chn;
+       atomic_t queue_sends[NR_CPUS];
+
        /* Holds rndis device info */
        void *extension;
-       /* The recive buffer for this device */
+
+       int ring_size;
+
+       /* The primary channel callback buffer */
        unsigned char cb_buffer[NETVSC_PACKET_SIZE];
+       /* The sub channel callback buffer */
+       unsigned char *sub_cb_buf;
 };
 
 /* NdisInitialize message */
@@ -706,6 +791,7 @@ enum ndis_per_pkt_info_type {
        IEEE_8021Q_INFO,
        ORIGINAL_PKTINFO,
        PACKET_CANCEL_ID,
+       NBL_HASH_VALUE = PACKET_CANCEL_ID,
        ORIGINAL_NET_BUFLIST,
        CACHED_NET_BUFLIST,
        SHORT_PKT_PADINFO,
@@ -852,6 +938,9 @@ struct ndis_tcp_lso_info {
 #define NDIS_LSO_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \
                sizeof(struct ndis_tcp_lso_info))
 
+#define NDIS_HASH_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \
+               sizeof(u32))
+
 /* Format of Information buffer passed in a SetRequest for the OID */
 /* OID_GEN_RNDIS_CONFIG_PARAMETER. */
 struct rndis_config_parameter_info {
index f7629ecefa84a6d9c2d1cbb7b308a3d46a359915..c041f63a6d3053f51d5e3f6651bc1db0d0d6d25c 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/slab.h>
 #include <linux/netdevice.h>
 #include <linux/if_ether.h>
+#include <asm/sync_bitops.h>
 
 #include "hyperv_net.h"
 
@@ -80,7 +81,7 @@ get_in_err:
 }
 
 
-static int netvsc_destroy_recv_buf(struct netvsc_device *net_device)
+static int netvsc_destroy_buf(struct netvsc_device *net_device)
 {
        struct nvsp_message *revoke_packet;
        int ret = 0;
@@ -146,10 +147,62 @@ static int netvsc_destroy_recv_buf(struct netvsc_device *net_device)
                net_device->recv_section = NULL;
        }
 
+       /* Deal with the send buffer we may have setup.
+        * If we got a  send section size, it means we received a
+        * SendsendBufferComplete msg (ie sent
+        * NvspMessage1TypeSendReceiveBuffer msg) therefore, we need
+        * to send a revoke msg here
+        */
+       if (net_device->send_section_size) {
+               /* Send the revoke receive buffer */
+               revoke_packet = &net_device->revoke_packet;
+               memset(revoke_packet, 0, sizeof(struct nvsp_message));
+
+               revoke_packet->hdr.msg_type =
+                       NVSP_MSG1_TYPE_REVOKE_SEND_BUF;
+               revoke_packet->msg.v1_msg.revoke_recv_buf.id = 0;
+
+               ret = vmbus_sendpacket(net_device->dev->channel,
+                                      revoke_packet,
+                                      sizeof(struct nvsp_message),
+                                      (unsigned long)revoke_packet,
+                                      VM_PKT_DATA_INBAND, 0);
+               /* If we failed here, we might as well return and
+                * have a leak rather than continue and a bugchk
+                */
+               if (ret != 0) {
+                       netdev_err(ndev, "unable to send "
+                                  "revoke send buffer to netvsp\n");
+                       return ret;
+               }
+       }
+       /* Teardown the gpadl on the vsp end */
+       if (net_device->send_buf_gpadl_handle) {
+               ret = vmbus_teardown_gpadl(net_device->dev->channel,
+                                          net_device->send_buf_gpadl_handle);
+
+               /* If we failed here, we might as well return and have a leak
+                * rather than continue and a bugchk
+                */
+               if (ret != 0) {
+                       netdev_err(ndev,
+                                  "unable to teardown send buffer's gpadl\n");
+                       return ret;
+               }
+               net_device->recv_buf_gpadl_handle = 0;
+       }
+       if (net_device->send_buf) {
+               /* Free up the receive buffer */
+               free_pages((unsigned long)net_device->send_buf,
+                          get_order(net_device->send_buf_size));
+               net_device->send_buf = NULL;
+       }
+       kfree(net_device->send_section_map);
+
        return ret;
 }
 
-static int netvsc_init_recv_buf(struct hv_device *device)
+static int netvsc_init_buf(struct hv_device *device)
 {
        int ret = 0;
        int t;
@@ -248,10 +301,90 @@ static int netvsc_init_recv_buf(struct hv_device *device)
                goto cleanup;
        }
 
+       /* Now setup the send buffer.
+        */
+       net_device->send_buf =
+               (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO,
+                                        get_order(net_device->send_buf_size));
+       if (!net_device->send_buf) {
+               netdev_err(ndev, "unable to allocate send "
+                          "buffer of size %d\n", net_device->send_buf_size);
+               ret = -ENOMEM;
+               goto cleanup;
+       }
+
+       /* Establish the gpadl handle for this buffer on this
+        * channel.  Note: This call uses the vmbus connection rather
+        * than the channel to establish the gpadl handle.
+        */
+       ret = vmbus_establish_gpadl(device->channel, net_device->send_buf,
+                                   net_device->send_buf_size,
+                                   &net_device->send_buf_gpadl_handle);
+       if (ret != 0) {
+               netdev_err(ndev,
+                          "unable to establish send buffer's gpadl\n");
+               goto cleanup;
+       }
+
+       /* Notify the NetVsp of the gpadl handle */
+       init_packet = &net_device->channel_init_pkt;
+       memset(init_packet, 0, sizeof(struct nvsp_message));
+       init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_SEND_BUF;
+       init_packet->msg.v1_msg.send_recv_buf.gpadl_handle =
+               net_device->send_buf_gpadl_handle;
+       init_packet->msg.v1_msg.send_recv_buf.id = 0;
+
+       /* Send the gpadl notification request */
+       ret = vmbus_sendpacket(device->channel, init_packet,
+                              sizeof(struct nvsp_message),
+                              (unsigned long)init_packet,
+                              VM_PKT_DATA_INBAND,
+                              VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+       if (ret != 0) {
+               netdev_err(ndev,
+                          "unable to send send buffer's gpadl to netvsp\n");
+               goto cleanup;
+       }
+
+       t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ);
+       BUG_ON(t == 0);
+
+       /* Check the response */
+       if (init_packet->msg.v1_msg.
+           send_send_buf_complete.status != NVSP_STAT_SUCCESS) {
+               netdev_err(ndev, "Unable to complete send buffer "
+                          "initialization with NetVsp - status %d\n",
+                          init_packet->msg.v1_msg.
+                          send_recv_buf_complete.status);
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       /* Parse the response */
+       net_device->send_section_size = init_packet->msg.
+                               v1_msg.send_send_buf_complete.section_size;
+
+       /* Section count is simply the size divided by the section size.
+        */
+       net_device->send_section_cnt =
+               net_device->send_buf_size/net_device->send_section_size;
+
+       dev_info(&device->device, "Send section size: %d, Section count:%d\n",
+                net_device->send_section_size, net_device->send_section_cnt);
+
+       /* Setup state for managing the send buffer. */
+       net_device->map_words = DIV_ROUND_UP(net_device->send_section_cnt,
+                                            BITS_PER_LONG);
+
+       net_device->send_section_map =
+               kzalloc(net_device->map_words * sizeof(ulong), GFP_KERNEL);
+       if (net_device->send_section_map == NULL)
+               goto cleanup;
+
        goto exit;
 
 cleanup:
-       netvsc_destroy_recv_buf(net_device);
+       netvsc_destroy_buf(net_device);
 
 exit:
        return ret;
@@ -369,8 +502,9 @@ static int netvsc_connect_vsp(struct hv_device *device)
                net_device->recv_buf_size = NETVSC_RECEIVE_BUFFER_SIZE_LEGACY;
        else
                net_device->recv_buf_size = NETVSC_RECEIVE_BUFFER_SIZE;
+       net_device->send_buf_size = NETVSC_SEND_BUFFER_SIZE;
 
-       ret = netvsc_init_recv_buf(device);
+       ret = netvsc_init_buf(device);
 
 cleanup:
        return ret;
@@ -378,7 +512,7 @@ cleanup:
 
 static void netvsc_disconnect_vsp(struct netvsc_device *net_device)
 {
-       netvsc_destroy_recv_buf(net_device);
+       netvsc_destroy_buf(net_device);
 }
 
 /*
@@ -387,7 +521,6 @@ static void netvsc_disconnect_vsp(struct netvsc_device *net_device)
 int netvsc_device_remove(struct hv_device *device)
 {
        struct netvsc_device *net_device;
-       struct hv_netvsc_packet *netvsc_packet, *pos;
        unsigned long flags;
 
        net_device = hv_get_drvdata(device);
@@ -416,11 +549,8 @@ int netvsc_device_remove(struct hv_device *device)
        vmbus_close(device->channel);
 
        /* Release all resources */
-       list_for_each_entry_safe(netvsc_packet, pos,
-                                &net_device->recv_pkt_list, list_ent) {
-               list_del(&netvsc_packet->list_ent);
-               kfree(netvsc_packet);
-       }
+       if (net_device->sub_cb_buf)
+               vfree(net_device->sub_cb_buf);
 
        kfree(net_device);
        return 0;
@@ -444,6 +574,12 @@ static inline u32 hv_ringbuf_avail_percent(
        return avail_write * 100 / ring_info->ring_datasize;
 }
 
+static inline void netvsc_free_send_slot(struct netvsc_device *net_device,
+                                        u32 index)
+{
+       sync_change_bit(index, net_device->send_section_map);
+}
+
 static void netvsc_send_completion(struct netvsc_device *net_device,
                                   struct hv_device *device,
                                   struct vmpacket_descriptor *packet)
@@ -451,6 +587,7 @@ static void netvsc_send_completion(struct netvsc_device *net_device,
        struct nvsp_message *nvsp_packet;
        struct hv_netvsc_packet *nvsc_packet;
        struct net_device *ndev;
+       u32 send_index;
 
        ndev = net_device->ndev;
 
@@ -461,7 +598,9 @@ static void netvsc_send_completion(struct netvsc_device *net_device,
            (nvsp_packet->hdr.msg_type ==
             NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE) ||
            (nvsp_packet->hdr.msg_type ==
-            NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE)) {
+            NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE) ||
+           (nvsp_packet->hdr.msg_type ==
+            NVSP_MSG5_TYPE_SUBCHANNEL)) {
                /* Copy the response back */
                memcpy(&net_device->channel_init_pkt, nvsp_packet,
                       sizeof(struct nvsp_message));
@@ -469,28 +608,39 @@ static void netvsc_send_completion(struct netvsc_device *net_device,
        } else if (nvsp_packet->hdr.msg_type ==
                   NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE) {
                int num_outstanding_sends;
+               u16 q_idx = 0;
+               struct vmbus_channel *channel = device->channel;
+               int queue_sends;
 
                /* Get the send context */
                nvsc_packet = (struct hv_netvsc_packet *)(unsigned long)
                        packet->trans_id;
 
                /* Notify the layer above us */
-               if (nvsc_packet)
-                       nvsc_packet->completion.send.send_completion(
-                               nvsc_packet->completion.send.
-                               send_completion_ctx);
+               if (nvsc_packet) {
+                       send_index = nvsc_packet->send_buf_index;
+                       if (send_index != NETVSC_INVALID_INDEX)
+                               netvsc_free_send_slot(net_device, send_index);
+                       q_idx = nvsc_packet->q_idx;
+                       channel = nvsc_packet->channel;
+                       nvsc_packet->send_completion(nvsc_packet->
+                                                    send_completion_ctx);
+               }
 
                num_outstanding_sends =
                        atomic_dec_return(&net_device->num_outstanding_sends);
+               queue_sends = atomic_dec_return(&net_device->
+                                               queue_sends[q_idx]);
 
                if (net_device->destroy && num_outstanding_sends == 0)
                        wake_up(&net_device->wait_drain);
 
-               if (netif_queue_stopped(ndev) && !net_device->start_remove &&
-                       (hv_ringbuf_avail_percent(&device->channel->outbound)
-                       > RING_AVAIL_PERCENT_HIWATER ||
-                       num_outstanding_sends < 1))
-                               netif_wake_queue(ndev);
+               if (netif_tx_queue_stopped(netdev_get_tx_queue(ndev, q_idx)) &&
+                   !net_device->start_remove &&
+                   (hv_ringbuf_avail_percent(&channel->outbound) >
+                    RING_AVAIL_PERCENT_HIWATER || queue_sends < 1))
+                               netif_tx_wake_queue(netdev_get_tx_queue(
+                                                   ndev, q_idx));
        } else {
                netdev_err(ndev, "Unknown send completion packet type- "
                           "%d received!!\n", nvsp_packet->hdr.msg_type);
@@ -498,6 +648,52 @@ static void netvsc_send_completion(struct netvsc_device *net_device,
 
 }
 
+static u32 netvsc_get_next_send_section(struct netvsc_device *net_device)
+{
+       unsigned long index;
+       u32 max_words = net_device->map_words;
+       unsigned long *map_addr = (unsigned long *)net_device->send_section_map;
+       u32 section_cnt = net_device->send_section_cnt;
+       int ret_val = NETVSC_INVALID_INDEX;
+       int i;
+       int prev_val;
+
+       for (i = 0; i < max_words; i++) {
+               if (!~(map_addr[i]))
+                       continue;
+               index = ffz(map_addr[i]);
+               prev_val = sync_test_and_set_bit(index, &map_addr[i]);
+               if (prev_val)
+                       continue;
+               if ((index + (i * BITS_PER_LONG)) >= section_cnt)
+                       break;
+               ret_val = (index + (i * BITS_PER_LONG));
+               break;
+       }
+       return ret_val;
+}
+
+u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device,
+                           unsigned int section_index,
+                           struct hv_netvsc_packet *packet)
+{
+       char *start = net_device->send_buf;
+       char *dest = (start + (section_index * net_device->send_section_size));
+       int i;
+       u32 msg_size = 0;
+
+       for (i = 0; i < packet->page_buf_cnt; i++) {
+               char *src = phys_to_virt(packet->page_buf[i].pfn << PAGE_SHIFT);
+               u32 offset = packet->page_buf[i].offset;
+               u32 len = packet->page_buf[i].len;
+
+               memcpy(dest, (src + offset), len);
+               msg_size += len;
+               dest += len;
+       }
+       return msg_size;
+}
+
 int netvsc_send(struct hv_device *device,
                        struct hv_netvsc_packet *packet)
 {
@@ -505,7 +701,12 @@ int netvsc_send(struct hv_device *device,
        int ret = 0;
        struct nvsp_message sendMessage;
        struct net_device *ndev;
+       struct vmbus_channel *out_channel = NULL;
        u64 req_id;
+       unsigned int section_index = NETVSC_INVALID_INDEX;
+       u32 msg_size = 0;
+       struct sk_buff *skb;
+
 
        net_device = get_outbound_net_device(device);
        if (!net_device)
@@ -521,25 +722,46 @@ int netvsc_send(struct hv_device *device,
                sendMessage.msg.v1_msg.send_rndis_pkt.channel_type = 1;
        }
 
-       /* Not using send buffer section */
+       /* Attempt to send via sendbuf */
+       if (packet->total_data_buflen < net_device->send_section_size) {
+               section_index = netvsc_get_next_send_section(net_device);
+               if (section_index != NETVSC_INVALID_INDEX) {
+                       msg_size = netvsc_copy_to_send_buf(net_device,
+                                                          section_index,
+                                                          packet);
+                       skb = (struct sk_buff *)
+                             (unsigned long)packet->send_completion_tid;
+                       if (skb)
+                               dev_kfree_skb_any(skb);
+                       packet->page_buf_cnt = 0;
+               }
+       }
+       packet->send_buf_index = section_index;
+
+
        sendMessage.msg.v1_msg.send_rndis_pkt.send_buf_section_index =
-               0xFFFFFFFF;
-       sendMessage.msg.v1_msg.send_rndis_pkt.send_buf_section_size = 0;
+               section_index;
+       sendMessage.msg.v1_msg.send_rndis_pkt.send_buf_section_size = msg_size;
 
-       if (packet->completion.send.send_completion)
+       if (packet->send_completion)
                req_id = (ulong)packet;
        else
                req_id = 0;
 
+       out_channel = net_device->chn_table[packet->q_idx];
+       if (out_channel == NULL)
+               out_channel = device->channel;
+       packet->channel = out_channel;
+
        if (packet->page_buf_cnt) {
-               ret = vmbus_sendpacket_pagebuffer(device->channel,
+               ret = vmbus_sendpacket_pagebuffer(out_channel,
                                                  packet->page_buf,
                                                  packet->page_buf_cnt,
                                                  &sendMessage,
                                                  sizeof(struct nvsp_message),
                                                  req_id);
        } else {
-               ret = vmbus_sendpacket(device->channel, &sendMessage,
+               ret = vmbus_sendpacket(out_channel, &sendMessage,
                                sizeof(struct nvsp_message),
                                req_id,
                                VM_PKT_DATA_INBAND,
@@ -548,17 +770,24 @@ int netvsc_send(struct hv_device *device,
 
        if (ret == 0) {
                atomic_inc(&net_device->num_outstanding_sends);
-               if (hv_ringbuf_avail_percent(&device->channel->outbound) <
+               atomic_inc(&net_device->queue_sends[packet->q_idx]);
+
+               if (hv_ringbuf_avail_percent(&out_channel->outbound) <
                        RING_AVAIL_PERCENT_LOWATER) {
-                       netif_stop_queue(ndev);
+                       netif_tx_stop_queue(netdev_get_tx_queue(
+                                           ndev, packet->q_idx));
+
                        if (atomic_read(&net_device->
-                               num_outstanding_sends) < 1)
-                               netif_wake_queue(ndev);
+                               queue_sends[packet->q_idx]) < 1)
+                               netif_tx_wake_queue(netdev_get_tx_queue(
+                                                   ndev, packet->q_idx));
                }
        } else if (ret == -EAGAIN) {
-               netif_stop_queue(ndev);
-               if (atomic_read(&net_device->num_outstanding_sends) < 1) {
-                       netif_wake_queue(ndev);
+               netif_tx_stop_queue(netdev_get_tx_queue(
+                                   ndev, packet->q_idx));
+               if (atomic_read(&net_device->queue_sends[packet->q_idx]) < 1) {
+                       netif_tx_wake_queue(netdev_get_tx_queue(
+                                           ndev, packet->q_idx));
                        ret = -ENOSPC;
                }
        } else {
@@ -570,6 +799,7 @@ int netvsc_send(struct hv_device *device,
 }
 
 static void netvsc_send_recv_completion(struct hv_device *device,
+                                       struct vmbus_channel *channel,
                                        struct netvsc_device *net_device,
                                        u64 transaction_id, u32 status)
 {
@@ -587,7 +817,7 @@ static void netvsc_send_recv_completion(struct hv_device *device,
 
 retry_send_cmplt:
        /* Send the completion */
-       ret = vmbus_sendpacket(device->channel, &recvcompMessage,
+       ret = vmbus_sendpacket(channel, &recvcompMessage,
                               sizeof(struct nvsp_message), transaction_id,
                               VM_PKT_COMP, 0);
        if (ret == 0) {
@@ -613,76 +843,20 @@ retry_send_cmplt:
        }
 }
 
-/* Send a receive completion packet to RNDIS device (ie NetVsp) */
-static void netvsc_receive_completion(void *context)
-{
-       struct hv_netvsc_packet *packet = context;
-       struct hv_device *device = packet->device;
-       struct netvsc_device *net_device;
-       u64 transaction_id = 0;
-       bool fsend_receive_comp = false;
-       unsigned long flags;
-       struct net_device *ndev;
-       u32 status = NVSP_STAT_NONE;
-
-       /*
-        * Even though it seems logical to do a GetOutboundNetDevice() here to
-        * send out receive completion, we are using GetInboundNetDevice()
-        * since we may have disable outbound traffic already.
-        */
-       net_device = get_inbound_net_device(device);
-       if (!net_device)
-               return;
-       ndev = net_device->ndev;
-
-       /* Overloading use of the lock. */
-       spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags);
-
-       if (packet->status != NVSP_STAT_SUCCESS)
-               packet->xfer_page_pkt->status = NVSP_STAT_FAIL;
-
-       packet->xfer_page_pkt->count--;
-
-       /*
-        * Last one in the line that represent 1 xfer page packet.
-        * Return the xfer page packet itself to the freelist
-        */
-       if (packet->xfer_page_pkt->count == 0) {
-               fsend_receive_comp = true;
-               transaction_id = packet->completion.recv.recv_completion_tid;
-               status = packet->xfer_page_pkt->status;
-               list_add_tail(&packet->xfer_page_pkt->list_ent,
-                             &net_device->recv_pkt_list);
-
-       }
-
-       /* Put the packet back */
-       list_add_tail(&packet->list_ent, &net_device->recv_pkt_list);
-       spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, flags);
-
-       /* Send a receive completion for the xfer page packet */
-       if (fsend_receive_comp)
-               netvsc_send_recv_completion(device, net_device, transaction_id,
-                                       status);
-
-}
-
 static void netvsc_receive(struct netvsc_device *net_device,
+                       struct vmbus_channel *channel,
                        struct hv_device *device,
                        struct vmpacket_descriptor *packet)
 {
        struct vmtransfer_page_packet_header *vmxferpage_packet;
        struct nvsp_message *nvsp_packet;
-       struct hv_netvsc_packet *netvsc_packet = NULL;
-       /* struct netvsc_driver *netvscDriver; */
-       struct xferpage_packet *xferpage_packet = NULL;
+       struct hv_netvsc_packet nv_pkt;
+       struct hv_netvsc_packet *netvsc_packet = &nv_pkt;
+       u32 status = NVSP_STAT_SUCCESS;
        int i;
        int count = 0;
-       unsigned long flags;
        struct net_device *ndev;
 
-       LIST_HEAD(listHead);
-
        ndev = net_device->ndev;
 
        /*
@@ -715,77 +889,14 @@ static void netvsc_receive(struct netvsc_device *net_device,
                return;
        }
 
-       /*
-        * Grab free packets (range count + 1) to represent this xfer
-        * page packet. +1 to represent the xfer page packet itself.
-        * We grab it here so that we know exactly how many we can
-        * fulfil
-        */
-       spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags);
-       while (!list_empty(&net_device->recv_pkt_list)) {
-               list_move_tail(net_device->recv_pkt_list.next, &listHead);
-               if (++count == vmxferpage_packet->range_cnt + 1)
-                       break;
-       }
-       spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, flags);
-
-       /*
-        * We need at least 2 netvsc pkts (1 to represent the xfer
-        * page and at least 1 for the range) i.e. we can handled
-        * some of the xfer page packet ranges...
-        */
-       if (count < 2) {
-               netdev_err(ndev, "Got only %d netvsc pkt...needed "
-                       "%d pkts. Dropping this xfer page packet completely!\n",
-                       count, vmxferpage_packet->range_cnt + 1);
-
-               /* Return it to the freelist */
-               spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags);
-               for (i = count; i != 0; i--) {
-                       list_move_tail(listHead.next,
-                                      &net_device->recv_pkt_list);
-               }
-               spin_unlock_irqrestore(&net_device->recv_pkt_list_lock,
-                                      flags);
-
-               netvsc_send_recv_completion(device, net_device,
-                                           vmxferpage_packet->d.trans_id,
-                                           NVSP_STAT_FAIL);
-
-               return;
-       }
-
-       /* Remove the 1st packet to represent the xfer page packet itself */
-       xferpage_packet = (struct xferpage_packet *)listHead.next;
-       list_del(&xferpage_packet->list_ent);
-       xferpage_packet->status = NVSP_STAT_SUCCESS;
-
-       /* This is how much we can satisfy */
-       xferpage_packet->count = count - 1;
-
-       if (xferpage_packet->count != vmxferpage_packet->range_cnt) {
-               netdev_err(ndev, "Needed %d netvsc pkts to satisfy "
-                       "this xfer page...got %d\n",
-                       vmxferpage_packet->range_cnt, xferpage_packet->count);
-       }
+       count = vmxferpage_packet->range_cnt;
+       netvsc_packet->device = device;
+       netvsc_packet->channel = channel;
 
        /* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */
-       for (i = 0; i < (count - 1); i++) {
-               netvsc_packet = (struct hv_netvsc_packet *)listHead.next;
-               list_del(&netvsc_packet->list_ent);
-
+       for (i = 0; i < count; i++) {
                /* Initialize the netvsc packet */
                netvsc_packet->status = NVSP_STAT_SUCCESS;
-               netvsc_packet->xfer_page_pkt = xferpage_packet;
-               netvsc_packet->completion.recv.recv_completion =
-                                       netvsc_receive_completion;
-               netvsc_packet->completion.recv.recv_completion_ctx =
-                                       netvsc_packet;
-               netvsc_packet->device = device;
-               /* Save this so that we can send it back */
-               netvsc_packet->completion.recv.recv_completion_tid =
-                                       vmxferpage_packet->d.trans_id;
-
                netvsc_packet->data = (void *)((unsigned long)net_device->
                        recv_buf + vmxferpage_packet->ranges[i].byte_offset);
                netvsc_packet->total_data_buflen =
@@ -794,16 +905,53 @@ static void netvsc_receive(struct netvsc_device *net_device,
                /* Pass it to the upper layer */
                rndis_filter_receive(device, netvsc_packet);
 
-               netvsc_receive_completion(netvsc_packet->
-                               completion.recv.recv_completion_ctx);
+               if (netvsc_packet->status != NVSP_STAT_SUCCESS)
+                       status = NVSP_STAT_FAIL;
+       }
+
+       netvsc_send_recv_completion(device, channel, net_device,
+                                   vmxferpage_packet->d.trans_id, status);
+}
+
+
+static void netvsc_send_table(struct hv_device *hdev,
+                             struct vmpacket_descriptor *vmpkt)
+{
+       struct netvsc_device *nvscdev;
+       struct net_device *ndev;
+       struct nvsp_message *nvmsg;
+       int i;
+       u32 count, *tab;
+
+       nvscdev = get_outbound_net_device(hdev);
+       if (!nvscdev)
+               return;
+       ndev = nvscdev->ndev;
+
+       nvmsg = (struct nvsp_message *)((unsigned long)vmpkt +
+                                       (vmpkt->offset8 << 3));
+
+       if (nvmsg->hdr.msg_type != NVSP_MSG5_TYPE_SEND_INDIRECTION_TABLE)
+               return;
+
+       count = nvmsg->msg.v5_msg.send_table.count;
+       if (count != VRSS_SEND_TAB_SIZE) {
+               netdev_err(ndev, "Received wrong send-table size:%u\n", count);
+               return;
        }
 
+       tab = (u32 *)((unsigned long)&nvmsg->msg.v5_msg.send_table +
+                     nvmsg->msg.v5_msg.send_table.offset);
+
+       for (i = 0; i < count; i++)
+               nvscdev->send_table[i] = tab[i];
 }
 
-static void netvsc_channel_cb(void *context)
+void netvsc_channel_cb(void *context)
 {
        int ret;
-       struct hv_device *device = context;
+       struct vmbus_channel *channel = (struct vmbus_channel *)context;
+       struct hv_device *device;
        struct netvsc_device *net_device;
        u32 bytes_recvd;
        u64 request_id;
@@ -812,14 +960,19 @@ static void netvsc_channel_cb(void *context)
        int bufferlen = NETVSC_PACKET_SIZE;
        struct net_device *ndev;
 
+       if (channel->primary_channel != NULL)
+               device = channel->primary_channel->device_obj;
+       else
+               device = channel->device_obj;
+
        net_device = get_inbound_net_device(device);
        if (!net_device)
                return;
        ndev = net_device->ndev;
-       buffer = net_device->cb_buffer;
+       buffer = get_per_channel_state(channel);
 
        do {
-               ret = vmbus_recvpacket_raw(device->channel, buffer, bufferlen,
+               ret = vmbus_recvpacket_raw(channel, buffer, bufferlen,
                                           &bytes_recvd, &request_id);
                if (ret == 0) {
                        if (bytes_recvd > 0) {
@@ -831,8 +984,12 @@ static void netvsc_channel_cb(void *context)
                                        break;
 
                                case VM_PKT_DATA_USING_XFER_PAGES:
-                                       netvsc_receive(net_device,
-                                                       device, desc);
+                                       netvsc_receive(net_device, channel,
+                                                      device, desc);
+                                       break;
+
+                               case VM_PKT_DATA_INBAND:
+                                       netvsc_send_table(device, desc);
                                        break;
 
                                default:
@@ -880,11 +1037,9 @@ static void netvsc_channel_cb(void *context)
 int netvsc_device_add(struct hv_device *device, void *additional_info)
 {
        int ret = 0;
-       int i;
        int ring_size =
        ((struct netvsc_device_info *)additional_info)->ring_size;
        struct netvsc_device *net_device;
-       struct hv_netvsc_packet *packet, *pos;
        struct net_device *ndev;
 
        net_device = alloc_net_device(device);
@@ -893,6 +1048,8 @@ int netvsc_device_add(struct hv_device *device, void *additional_info)
                goto cleanup;
        }
 
+       net_device->ring_size = ring_size;
+
        /*
         * Coming into this function, struct net_device * is
         * registered as the driver private data.
@@ -903,24 +1060,14 @@ int netvsc_device_add(struct hv_device *device, void *additional_info)
        ndev = net_device->ndev;
 
        /* Initialize the NetVSC channel extension */
-       spin_lock_init(&net_device->recv_pkt_list_lock);
-
-       INIT_LIST_HEAD(&net_device->recv_pkt_list);
-
-       for (i = 0; i < NETVSC_RECEIVE_PACKETLIST_COUNT; i++) {
-               packet = kzalloc(sizeof(struct hv_netvsc_packet), GFP_KERNEL);
-               if (!packet)
-                       break;
-
-               list_add_tail(&packet->list_ent,
-                             &net_device->recv_pkt_list);
-       }
        init_completion(&net_device->channel_init_wait);
 
+       set_per_channel_state(device->channel, net_device->cb_buffer);
+
        /* Open the channel */
        ret = vmbus_open(device->channel, ring_size * PAGE_SIZE,
                         ring_size * PAGE_SIZE, NULL, 0,
-                        netvsc_channel_cb, device);
+                        netvsc_channel_cb, device->channel);
 
        if (ret != 0) {
                netdev_err(ndev, "unable to open channel: %d\n", ret);
@@ -930,6 +1077,8 @@ int netvsc_device_add(struct hv_device *device, void *additional_info)
        /* Channel is opened */
        pr_info("hv_netvsc channel opened successfully\n");
 
+       net_device->chn_table[0] = device->channel;
+
        /* Connect with the NetVsp */
        ret = netvsc_connect_vsp(device);
        if (ret != 0) {
@@ -946,16 +1095,8 @@ close:
 
 cleanup:
 
-       if (net_device) {
-               list_for_each_entry_safe(packet, pos,
-                                        &net_device->recv_pkt_list,
-                                        list_ent) {
-                       list_del(&packet->list_ent);
-                       kfree(packet);
-               }
-
+       if (net_device)
                kfree(net_device);
-       }
 
        return ret;
 }
index 7918d5132c1f14764cda43daf9f98d7f7c01f310..4fd71b75e666418ab7063447160d561a294a0966 100644 (file)
@@ -101,7 +101,7 @@ static int netvsc_open(struct net_device *net)
                return ret;
        }
 
-       netif_start_queue(net);
+       netif_tx_start_all_queues(net);
 
        nvdev = hv_get_drvdata(device_obj);
        rdev = nvdev->extension;
@@ -149,15 +149,100 @@ static void *init_ppi_data(struct rndis_message *msg, u32 ppi_size,
        return ppi;
 }
 
+union sub_key {
+       u64 k;
+       struct {
+               u8 pad[3];
+               u8 kb;
+               u32 ka;
+       };
+};
+
+/* Toeplitz hash function
+ * data: network byte order
+ * return: host byte order
+ */
+static u32 comp_hash(u8 *key, int klen, u8 *data, int dlen)
+{
+       union sub_key subk;
+       int k_next = 4;
+       u8 dt;
+       int i, j;
+       u32 ret = 0;
+
+       subk.k = 0;
+       subk.ka = ntohl(*(u32 *)key);
+
+       for (i = 0; i < dlen; i++) {
+               subk.kb = key[k_next];
+               k_next = (k_next + 1) % klen;
+               dt = data[i];
+               for (j = 0; j < 8; j++) {
+                       if (dt & 0x80)
+                               ret ^= subk.ka;
+                       dt <<= 1;
+                       subk.k <<= 1;
+               }
+       }
+
+       return ret;
+}
+
+static bool netvsc_set_hash(u32 *hash, struct sk_buff *skb)
+{
+       struct iphdr *iphdr;
+       int data_len;
+       bool ret = false;
+
+       if (eth_hdr(skb)->h_proto != htons(ETH_P_IP))
+               return false;
+
+       iphdr = ip_hdr(skb);
+
+       if (iphdr->version == 4) {
+               if (iphdr->protocol == IPPROTO_TCP)
+                       data_len = 12;
+               else
+                       data_len = 8;
+               *hash = comp_hash(netvsc_hash_key, HASH_KEYLEN,
+                                 (u8 *)&iphdr->saddr, data_len);
+               ret = true;
+       }
+
+       return ret;
+}
+
+static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb,
+                       void *accel_priv, select_queue_fallback_t fallback)
+{
+       struct net_device_context *net_device_ctx = netdev_priv(ndev);
+       struct hv_device *hdev =  net_device_ctx->device_ctx;
+       struct netvsc_device *nvsc_dev = hv_get_drvdata(hdev);
+       u32 hash;
+       u16 q_idx = 0;
+
+       if (nvsc_dev == NULL || ndev->real_num_tx_queues <= 1)
+               return 0;
+
+       if (netvsc_set_hash(&hash, skb)) {
+               q_idx = nvsc_dev->send_table[hash % VRSS_SEND_TAB_SIZE] %
+                       ndev->real_num_tx_queues;
+               skb_set_hash(skb, hash, PKT_HASH_TYPE_L3);
+       }
+
+       return q_idx;
+}
+
 static void netvsc_xmit_completion(void *context)
 {
        struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context;
        struct sk_buff *skb = (struct sk_buff *)
-               (unsigned long)packet->completion.send.send_completion_tid;
+               (unsigned long)packet->send_completion_tid;
+       u32 index = packet->send_buf_index;
 
        kfree(packet);
 
-       if (skb)
+       if (skb && (index == NETVSC_INVALID_INDEX))
                dev_kfree_skb_any(skb);
 }
 
@@ -301,6 +386,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
        struct ndis_tcp_lso_info *lso_info;
        int  hdr_offset;
        u32 net_trans_info;
+       u32 hash;
 
 
        /* We will atmost need two pages to describe the rndis
@@ -319,9 +405,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
        packet = kzalloc(sizeof(struct hv_netvsc_packet) +
                         (num_data_pgs * sizeof(struct hv_page_buffer)) +
                         sizeof(struct rndis_message) +
-                        NDIS_VLAN_PPI_SIZE +
-                        NDIS_CSUM_PPI_SIZE +
-                        NDIS_LSO_PPI_SIZE, GFP_ATOMIC);
+                        NDIS_VLAN_PPI_SIZE + NDIS_CSUM_PPI_SIZE +
+                        NDIS_LSO_PPI_SIZE + NDIS_HASH_PPI_SIZE, GFP_ATOMIC);
        if (!packet) {
                /* out of memory, drop packet */
                netdev_err(net, "unable to allocate hv_netvsc_packet\n");
@@ -333,6 +418,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
 
        packet->vlan_tci = skb->vlan_tci;
 
+       packet->q_idx = skb_get_queue_mapping(skb);
+
        packet->is_data_pkt = true;
        packet->total_data_buflen = skb->len;
 
@@ -341,9 +428,9 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
                                (num_data_pgs * sizeof(struct hv_page_buffer)));
 
        /* Set the completion routine */
-       packet->completion.send.send_completion = netvsc_xmit_completion;
-       packet->completion.send.send_completion_ctx = packet;
-       packet->completion.send.send_completion_tid = (unsigned long)skb;
+       packet->send_completion = netvsc_xmit_completion;
+       packet->send_completion_ctx = packet;
+       packet->send_completion_tid = (unsigned long)skb;
 
        isvlan = packet->vlan_tci & VLAN_TAG_PRESENT;
 
@@ -358,6 +445,14 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
 
        rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet);
 
+       hash = skb_get_hash_raw(skb);
+       if (hash != 0 && net->real_num_tx_queues > 1) {
+               rndis_msg_size += NDIS_HASH_PPI_SIZE;
+               ppi = init_ppi_data(rndis_msg, NDIS_HASH_PPI_SIZE,
+                                   NBL_HASH_VALUE);
+               *(u32 *)((void *)ppi + ppi->ppi_offset) = hash;
+       }
+
        if (isvlan) {
                struct ndis_pkt_8021q_info *vlan;
 
@@ -558,6 +653,9 @@ int netvsc_recv_callback(struct hv_device *device_obj,
                __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
                                       packet->vlan_tci);
 
+       skb_record_rx_queue(skb, packet->channel->
+                           offermsg.offer.sub_channel_index);
+
        net->stats.rx_packets++;
        net->stats.rx_bytes += packet->total_data_buflen;
 
@@ -606,7 +704,7 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu)
        hv_set_drvdata(hdev, ndev);
        device_info.ring_size = ring_size;
        rndis_filter_device_add(hdev, &device_info);
-       netif_wake_queue(ndev);
+       netif_tx_wake_all_queues(ndev);
 
        return 0;
 }
@@ -652,6 +750,7 @@ static const struct net_device_ops device_ops = {
        .ndo_change_mtu =               netvsc_change_mtu,
        .ndo_validate_addr =            eth_validate_addr,
        .ndo_set_mac_address =          netvsc_set_mac_addr,
+       .ndo_select_queue =             netvsc_select_queue,
 };
 
 /*
@@ -698,9 +797,11 @@ static int netvsc_probe(struct hv_device *dev,
        struct net_device *net = NULL;
        struct net_device_context *net_device_ctx;
        struct netvsc_device_info device_info;
+       struct netvsc_device *nvdev;
        int ret;
 
-       net = alloc_etherdev(sizeof(struct net_device_context));
+       net = alloc_etherdev_mq(sizeof(struct net_device_context),
+                               num_online_cpus());
        if (!net)
                return -ENOMEM;
 
@@ -719,7 +820,7 @@ static int netvsc_probe(struct hv_device *dev,
        net->features = NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_SG | NETIF_F_RXCSUM |
                        NETIF_F_IP_CSUM | NETIF_F_TSO;
 
-       SET_ETHTOOL_OPS(net, &ethtool_ops);
+       net->ethtool_ops = &ethtool_ops;
        SET_NETDEV_DEV(net, &dev->device);
 
        /* Notify the netvsc driver of the new device */
@@ -733,6 +834,10 @@ static int netvsc_probe(struct hv_device *dev,
        }
        memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN);
 
+       nvdev = hv_get_drvdata(dev);
+       netif_set_real_num_tx_queues(net, nvdev->num_chn);
+       netif_set_real_num_rx_queues(net, nvdev->num_chn);
+
        ret = register_netdev(net);
        if (ret != 0) {
                pr_err("Unable to register netdev.\n");
index 143a98caf618a90cd45ac002005c506f5ca14a33..99c527adae5bf1ee154b2a02eb0f93a6df32e333 100644 (file)
@@ -31,7 +31,7 @@
 #include "hyperv_net.h"
 
 
-#define RNDIS_EXT_LEN 100
+#define RNDIS_EXT_LEN PAGE_SIZE
 struct rndis_request {
        struct list_head list_ent;
        struct completion  wait_event;
@@ -94,6 +94,8 @@ static struct rndis_request *get_rndis_request(struct rndis_device *dev,
        rndis_msg->ndis_msg_type = msg_type;
        rndis_msg->msg_len = msg_len;
 
+       request->pkt.q_idx = 0;
+
        /*
         * Set the request id. This field is always after the rndis header for
         * request/response packet types so we just used the SetRequest as a
@@ -234,7 +236,7 @@ static int rndis_filter_send_request(struct rndis_device *dev,
                        packet->page_buf[0].len;
        }
 
-       packet->completion.send.send_completion = NULL;
+       packet->send_completion = NULL;
 
        ret = netvsc_send(dev->net_dev->dev, packet);
        return ret;
@@ -399,8 +401,6 @@ static void rndis_filter_receive_data(struct rndis_device *dev,
        pkt->total_data_buflen = rndis_pkt->data_len;
        pkt->data = (void *)((unsigned long)pkt->data + data_offset);
 
-       pkt->is_data_pkt = true;
-
        vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO);
        if (vlan) {
                pkt->vlan_tci = VLAN_TAG_PRESENT | vlan->vlanid |
@@ -509,6 +509,19 @@ static int rndis_filter_query_device(struct rndis_device *dev, u32 oid,
        query->info_buflen = 0;
        query->dev_vc_handle = 0;
 
+       if (oid == OID_GEN_RECEIVE_SCALE_CAPABILITIES) {
+               struct ndis_recv_scale_cap *cap;
+
+               request->request_msg.msg_len +=
+                       sizeof(struct ndis_recv_scale_cap);
+               query->info_buflen = sizeof(struct ndis_recv_scale_cap);
+               cap = (struct ndis_recv_scale_cap *)((unsigned long)query +
+                                                    query->info_buf_offset);
+               cap->hdr.type = NDIS_OBJECT_TYPE_RSS_CAPABILITIES;
+               cap->hdr.rev = NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2;
+               cap->hdr.size = sizeof(struct ndis_recv_scale_cap);
+       }
+
        ret = rndis_filter_send_request(dev, request);
        if (ret != 0)
                goto cleanup;
@@ -695,6 +708,89 @@ cleanup:
        return ret;
 }
 
+u8 netvsc_hash_key[HASH_KEYLEN] = {
+       0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
+       0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
+       0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
+       0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
+       0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
+};
+
+int rndis_filter_set_rss_param(struct rndis_device *rdev, int num_queue)
+{
+       struct net_device *ndev = rdev->net_dev->ndev;
+       struct rndis_request *request;
+       struct rndis_set_request *set;
+       struct rndis_set_complete *set_complete;
+       u32 extlen = sizeof(struct ndis_recv_scale_param) +
+                    4*ITAB_NUM + HASH_KEYLEN;
+       struct ndis_recv_scale_param *rssp;
+       u32 *itab;
+       u8 *keyp;
+       int i, t, ret;
+
+       request = get_rndis_request(
+                       rdev, RNDIS_MSG_SET,
+                       RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
+       if (!request)
+               return -ENOMEM;
+
+       set = &request->request_msg.msg.set_req;
+       set->oid = OID_GEN_RECEIVE_SCALE_PARAMETERS;
+       set->info_buflen = extlen;
+       set->info_buf_offset = sizeof(struct rndis_set_request);
+       set->dev_vc_handle = 0;
+
+       rssp = (struct ndis_recv_scale_param *)(set + 1);
+       rssp->hdr.type = NDIS_OBJECT_TYPE_RSS_PARAMETERS;
+       rssp->hdr.rev = NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2;
+       rssp->hdr.size = sizeof(struct ndis_recv_scale_param);
+       rssp->flag = 0;
+       rssp->hashinfo = NDIS_HASH_FUNC_TOEPLITZ | NDIS_HASH_IPV4 |
+                        NDIS_HASH_TCP_IPV4;
+       rssp->indirect_tabsize = 4*ITAB_NUM;
+       rssp->indirect_taboffset = sizeof(struct ndis_recv_scale_param);
+       rssp->hashkey_size = HASH_KEYLEN;
+       rssp->kashkey_offset = rssp->indirect_taboffset +
+                              rssp->indirect_tabsize;
+
+       /* Set indirection table entries */
+       itab = (u32 *)(rssp + 1);
+       for (i = 0; i < ITAB_NUM; i++)
+               itab[i] = i % num_queue;
+
+       /* Set hask key values */
+       keyp = (u8 *)((unsigned long)rssp + rssp->kashkey_offset);
+       for (i = 0; i < HASH_KEYLEN; i++)
+               keyp[i] = netvsc_hash_key[i];
+
+
+       ret = rndis_filter_send_request(rdev, request);
+       if (ret != 0)
+               goto cleanup;
+
+       t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
+       if (t == 0) {
+               netdev_err(ndev, "timeout before we got a set response...\n");
+               /* can't put_rndis_request, since we may still receive a
+                * send-completion.
+                */
+               return -ETIMEDOUT;
+       } else {
+               set_complete = &request->response_msg.msg.set_complete;
+               if (set_complete->status != RNDIS_STATUS_SUCCESS) {
+                       netdev_err(ndev, "Fail to set RSS parameters:0x%x\n",
+                                  set_complete->status);
+                       ret = -EINVAL;
+               }
+       }
+
+cleanup:
+       put_rndis_request(rdev, request);
+       return ret;
+}
+
+
 static int rndis_filter_query_device_link_status(struct rndis_device *dev)
 {
        u32 size = sizeof(u32);
@@ -886,6 +982,28 @@ static int rndis_filter_close_device(struct rndis_device *dev)
        return ret;
 }
 
+static void netvsc_sc_open(struct vmbus_channel *new_sc)
+{
+       struct netvsc_device *nvscdev;
+       u16 chn_index = new_sc->offermsg.offer.sub_channel_index;
+       int ret;
+
+       nvscdev = hv_get_drvdata(new_sc->primary_channel->device_obj);
+
+       if (chn_index >= nvscdev->num_chn)
+               return;
+
+       set_per_channel_state(new_sc, nvscdev->sub_cb_buf + (chn_index - 1) *
+                             NETVSC_PACKET_SIZE);
+
+       ret = vmbus_open(new_sc, nvscdev->ring_size * PAGE_SIZE,
+                        nvscdev->ring_size * PAGE_SIZE, NULL, 0,
+                        netvsc_channel_cb, new_sc);
+
+       if (ret == 0)
+               nvscdev->chn_table[chn_index] = new_sc;
+}
+
 int rndis_filter_device_add(struct hv_device *dev,
                                  void *additional_info)
 {
@@ -894,6 +1012,10 @@ int rndis_filter_device_add(struct hv_device *dev,
        struct rndis_device *rndis_device;
        struct netvsc_device_info *device_info = additional_info;
        struct ndis_offload_params offloads;
+       struct nvsp_message *init_packet;
+       int t;
+       struct ndis_recv_scale_cap rsscap;
+       u32 rsscap_size = sizeof(struct ndis_recv_scale_cap);
 
        rndis_device = get_rndis_device();
        if (!rndis_device)
@@ -913,6 +1035,7 @@ int rndis_filter_device_add(struct hv_device *dev,
 
        /* Initialize the rndis device */
        net_device = hv_get_drvdata(dev);
+       net_device->num_chn = 1;
 
        net_device->extension = rndis_device;
        rndis_device->net_dev = net_device;
@@ -952,7 +1075,6 @@ int rndis_filter_device_add(struct hv_device *dev,
        if (ret)
                goto err_dev_remv;
 
-
        rndis_filter_query_device_link_status(rndis_device);
 
        device_info->link_state = rndis_device->link_state;
@@ -961,7 +1083,66 @@ int rndis_filter_device_add(struct hv_device *dev,
                 rndis_device->hw_mac_adr,
                 device_info->link_state ? "down" : "up");
 
-       return ret;
+       if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5)
+               return 0;
+
+       /* vRSS setup */
+       memset(&rsscap, 0, rsscap_size);
+       ret = rndis_filter_query_device(rndis_device,
+                                       OID_GEN_RECEIVE_SCALE_CAPABILITIES,
+                                       &rsscap, &rsscap_size);
+       if (ret || rsscap.num_recv_que < 2)
+               goto out;
+
+       net_device->num_chn = (num_online_cpus() < rsscap.num_recv_que) ?
+                              num_online_cpus() : rsscap.num_recv_que;
+       if (net_device->num_chn == 1)
+               goto out;
+
+       net_device->sub_cb_buf = vzalloc((net_device->num_chn - 1) *
+                                        NETVSC_PACKET_SIZE);
+       if (!net_device->sub_cb_buf) {
+               net_device->num_chn = 1;
+               dev_info(&dev->device, "No memory for subchannels.\n");
+               goto out;
+       }
+
+       vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open);
+
+       init_packet = &net_device->channel_init_pkt;
+       memset(init_packet, 0, sizeof(struct nvsp_message));
+       init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL;
+       init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE;
+       init_packet->msg.v5_msg.subchn_req.num_subchannels =
+                                               net_device->num_chn - 1;
+       ret = vmbus_sendpacket(dev->channel, init_packet,
+                              sizeof(struct nvsp_message),
+                              (unsigned long)init_packet,
+                              VM_PKT_DATA_INBAND,
+                              VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+       if (ret)
+               goto out;
+       t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ);
+       if (t == 0) {
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+       if (init_packet->msg.v5_msg.subchn_comp.status !=
+           NVSP_STAT_SUCCESS) {
+               ret = -ENODEV;
+               goto out;
+       }
+       net_device->num_chn = 1 +
+               init_packet->msg.v5_msg.subchn_comp.num_subchannels;
+
+       vmbus_are_subchannels_present(dev->channel);
+
+       ret = rndis_filter_set_rss_param(rndis_device, net_device->num_chn);
+
+out:
+       if (ret)
+               net_device->num_chn = 1;
+       return 0; /* return 0 because primary channel can be used alone */
 
 err_dev_remv:
        rndis_filter_device_remove(dev);
index e36f194673a45a2035a15830571e4e2c02039839..4517b149ed0786946e44eb1a699fe232c2fbd166 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/interrupt.h>
+#include <linux/irq.h>
 #include <linux/gpio.h>
 #include <linux/delay.h>
 #include <linux/mutex.h>
@@ -692,10 +693,7 @@ at86rf230_xmit(struct ieee802154_dev *dev, struct sk_buff *skb)
        if (rc < 0)
                goto err_rx;
 
-       rc = at86rf230_start(dev);
-
-       return rc;
-
+       return at86rf230_start(dev);
 err_rx:
        at86rf230_start(dev);
 err:
@@ -963,33 +961,24 @@ static irqreturn_t at86rf230_isr_level(int irq, void *data)
        return at86rf230_isr(irq, data);
 }
 
-static int at86rf230_irq_polarity(struct at86rf230_local *lp, int pol)
-{
-       return at86rf230_write_subreg(lp, SR_IRQ_POLARITY, pol);
-}
-
 static int at86rf230_hw_init(struct at86rf230_local *lp)
 {
-       struct at86rf230_platform_data *pdata = lp->spi->dev.platform_data;
-       int rc, irq_pol;
-       u8 status;
+       int rc, irq_pol, irq_type;
+       u8 dvdd;
        u8 csma_seed[2];
 
-       rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status);
-       if (rc)
-               return rc;
-
        rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_FORCE_TRX_OFF);
        if (rc)
                return rc;
 
+       irq_type = irq_get_trigger_type(lp->spi->irq);
        /* configure irq polarity, defaults to high active */
-       if (pdata->irq_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW))
+       if (irq_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW))
                irq_pol = IRQ_ACTIVE_LOW;
        else
                irq_pol = IRQ_ACTIVE_HIGH;
 
-       rc = at86rf230_irq_polarity(lp, irq_pol);
+       rc = at86rf230_write_subreg(lp, SR_IRQ_POLARITY, irq_pol);
        if (rc)
                return rc;
 
@@ -1017,10 +1006,10 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
        /* Wait the next SLEEP cycle */
        msleep(100);
 
-       rc = at86rf230_read_subreg(lp, SR_DVDD_OK, &status);
+       rc = at86rf230_read_subreg(lp, SR_DVDD_OK, &dvdd);
        if (rc)
                return rc;
-       if (!status) {
+       if (!dvdd) {
                dev_err(&lp->spi->dev, "DVDD error\n");
                return -EINVAL;
        }
@@ -1032,7 +1021,6 @@ static struct at86rf230_platform_data *
 at86rf230_get_pdata(struct spi_device *spi)
 {
        struct at86rf230_platform_data *pdata;
-       const char *irq_type;
 
        if (!IS_ENABLED(CONFIG_OF) || !spi->dev.of_node)
                return spi->dev.platform_data;
@@ -1044,19 +1032,6 @@ at86rf230_get_pdata(struct spi_device *spi)
        pdata->rstn = of_get_named_gpio(spi->dev.of_node, "reset-gpio", 0);
        pdata->slp_tr = of_get_named_gpio(spi->dev.of_node, "sleep-gpio", 0);
 
-       pdata->irq_type = IRQF_TRIGGER_RISING;
-       of_property_read_string(spi->dev.of_node, "irq-type", &irq_type);
-       if (!strcmp(irq_type, "level-high"))
-               pdata->irq_type = IRQF_TRIGGER_HIGH;
-       else if (!strcmp(irq_type, "level-low"))
-               pdata->irq_type = IRQF_TRIGGER_LOW;
-       else if (!strcmp(irq_type, "edge-rising"))
-               pdata->irq_type = IRQF_TRIGGER_RISING;
-       else if (!strcmp(irq_type, "edge-falling"))
-               pdata->irq_type = IRQF_TRIGGER_FALLING;
-       else
-               dev_warn(&spi->dev, "wrong irq-type specified using edge-rising\n");
-
        spi->dev.platform_data = pdata;
 done:
        return pdata;
@@ -1071,7 +1046,7 @@ static int at86rf230_probe(struct spi_device *spi)
        u8 part = 0, version = 0, status;
        irq_handler_t irq_handler;
        work_func_t irq_worker;
-       int rc;
+       int rc, irq_type;
        const char *chip;
        struct ieee802154_ops *ops = NULL;
 
@@ -1087,27 +1062,17 @@ static int at86rf230_probe(struct spi_device *spi)
        }
 
        if (gpio_is_valid(pdata->rstn)) {
-               rc = gpio_request(pdata->rstn, "rstn");
+               rc = devm_gpio_request_one(&spi->dev, pdata->rstn,
+                                          GPIOF_OUT_INIT_HIGH, "rstn");
                if (rc)
                        return rc;
        }
 
        if (gpio_is_valid(pdata->slp_tr)) {
-               rc = gpio_request(pdata->slp_tr, "slp_tr");
-               if (rc)
-                       goto err_slp_tr;
-       }
-
-       if (gpio_is_valid(pdata->rstn)) {
-               rc = gpio_direction_output(pdata->rstn, 1);
-               if (rc)
-                       goto err_gpio_dir;
-       }
-
-       if (gpio_is_valid(pdata->slp_tr)) {
-               rc = gpio_direction_output(pdata->slp_tr, 0);
+               rc = devm_gpio_request_one(&spi->dev, pdata->slp_tr,
+                                          GPIOF_OUT_INIT_LOW, "slp_tr");
                if (rc)
-                       goto err_gpio_dir;
+                       return rc;
        }
 
        /* Reset */
@@ -1121,13 +1086,12 @@ static int at86rf230_probe(struct spi_device *spi)
 
        rc = __at86rf230_detect_device(spi, &man_id, &part, &version);
        if (rc < 0)
-               goto err_gpio_dir;
+               return rc;
 
        if (man_id != 0x001f) {
                dev_err(&spi->dev, "Non-Atmel dev found (MAN_ID %02x %02x)\n",
                        man_id >> 8, man_id & 0xFF);
-               rc = -EINVAL;
-               goto err_gpio_dir;
+               return -EINVAL;
        }
 
        switch (part) {
@@ -1154,16 +1118,12 @@ static int at86rf230_probe(struct spi_device *spi)
        }
 
        dev_info(&spi->dev, "Detected %s chip version %d\n", chip, version);
-       if (!ops) {
-               rc = -ENOTSUPP;
-               goto err_gpio_dir;
-       }
+       if (!ops)
+               return -ENOTSUPP;
 
        dev = ieee802154_alloc_device(sizeof(*lp), ops);
-       if (!dev) {
-               rc = -ENOMEM;
-               goto err_gpio_dir;
-       }
+       if (!dev)
+               return -ENOMEM;
 
        lp = dev->priv;
        lp->dev = dev;
@@ -1176,7 +1136,8 @@ static int at86rf230_probe(struct spi_device *spi)
        dev->extra_tx_headroom = 0;
        dev->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK;
 
-       if (pdata->irq_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
+       irq_type = irq_get_trigger_type(spi->irq);
+       if (irq_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
                irq_worker = at86rf230_irqwork;
                irq_handler = at86rf230_isr;
        } else {
@@ -1202,75 +1163,65 @@ static int at86rf230_probe(struct spi_device *spi)
        if (rc)
                goto err_hw_init;
 
-       rc = request_irq(spi->irq, irq_handler,
-                        IRQF_SHARED | pdata->irq_type,
-                        dev_name(&spi->dev), lp);
+       /* Read irq status register to reset irq line */
+       rc = at86rf230_read_subreg(lp, RG_IRQ_STATUS, 0xff, 0, &status);
        if (rc)
                goto err_hw_init;
 
-       /* Read irq status register to reset irq line */
-       rc = at86rf230_read_subreg(lp, RG_IRQ_STATUS, 0xff, 0, &status);
+       rc = devm_request_irq(&spi->dev, spi->irq, irq_handler, IRQF_SHARED,
+                             dev_name(&spi->dev), lp);
        if (rc)
-               goto err_irq;
+               goto err_hw_init;
 
        rc = ieee802154_register_device(lp->dev);
        if (rc)
-               goto err_irq;
+               goto err_hw_init;
 
        return rc;
 
-err_irq:
-       free_irq(spi->irq, lp);
 err_hw_init:
        flush_work(&lp->irqwork);
-       spi_set_drvdata(spi, NULL);
        mutex_destroy(&lp->bmux);
        ieee802154_free_device(lp->dev);
 
-err_gpio_dir:
-       if (gpio_is_valid(pdata->slp_tr))
-               gpio_free(pdata->slp_tr);
-err_slp_tr:
-       if (gpio_is_valid(pdata->rstn))
-               gpio_free(pdata->rstn);
        return rc;
 }
 
 static int at86rf230_remove(struct spi_device *spi)
 {
        struct at86rf230_local *lp = spi_get_drvdata(spi);
-       struct at86rf230_platform_data *pdata = spi->dev.platform_data;
 
        /* mask all at86rf230 irq's */
        at86rf230_write_subreg(lp, SR_IRQ_MASK, 0);
        ieee802154_unregister_device(lp->dev);
-
-       free_irq(spi->irq, lp);
        flush_work(&lp->irqwork);
-
-       if (gpio_is_valid(pdata->slp_tr))
-               gpio_free(pdata->slp_tr);
-       if (gpio_is_valid(pdata->rstn))
-               gpio_free(pdata->rstn);
-
        mutex_destroy(&lp->bmux);
        ieee802154_free_device(lp->dev);
-
        dev_dbg(&spi->dev, "unregistered at86rf230\n");
+
        return 0;
 }
 
-#if IS_ENABLED(CONFIG_OF)
-static struct of_device_id at86rf230_of_match[] = {
+static const struct of_device_id at86rf230_of_match[] = {
        { .compatible = "atmel,at86rf230", },
        { .compatible = "atmel,at86rf231", },
        { .compatible = "atmel,at86rf233", },
        { .compatible = "atmel,at86rf212", },
        { },
 };
-#endif
+MODULE_DEVICE_TABLE(of, at86rf230_of_match);
+
+static const struct spi_device_id at86rf230_device_id[] = {
+       { .name = "at86rf230", },
+       { .name = "at86rf231", },
+       { .name = "at86rf233", },
+       { .name = "at86rf212", },
+       { },
+};
+MODULE_DEVICE_TABLE(spi, at86rf230_device_id);
 
 static struct spi_driver at86rf230_driver = {
+       .id_table = at86rf230_device_id,
        .driver = {
                .of_match_table = of_match_ptr(at86rf230_of_match),
                .name   = "at86rf230",
index b8d22173925dee1aed62df3ccd4d23deb44b976b..27d83207d24ce0de722144c74e98ce65601fcfc0 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/timer.h>
 #include <linux/platform_device.h>
 #include <linux/netdevice.h>
+#include <linux/device.h>
 #include <linux/spinlock.h>
 #include <net/mac802154.h>
 #include <net/wpan-phy.h>
@@ -228,7 +229,8 @@ static int fakelb_probe(struct platform_device *pdev)
        int err = -ENOMEM;
        int i;
 
-       priv = kzalloc(sizeof(struct fakelb_priv), GFP_KERNEL);
+       priv = devm_kzalloc(&pdev->dev, sizeof(struct fakelb_priv),
+                           GFP_KERNEL);
        if (!priv)
                goto err_alloc;
 
@@ -248,7 +250,6 @@ static int fakelb_probe(struct platform_device *pdev)
 err_slave:
        list_for_each_entry(dp, &priv->list, list)
                fakelb_del(dp);
-       kfree(priv);
 err_alloc:
        return err;
 }
@@ -260,7 +261,6 @@ static int fakelb_remove(struct platform_device *pdev)
 
        list_for_each_entry_safe(dp, temp, &priv->list, list)
                fakelb_del(dp);
-       kfree(priv);
 
        return 0;
 }
index 78a6552ed7072d04672b98d7731c0ffd4af6117b..4048062011ba29bdafad313f06ef23aaf7be01c8 100644 (file)
@@ -618,12 +618,12 @@ static int mrf24j40_probe(struct spi_device *spi)
 
        printk(KERN_INFO "mrf24j40: probe(). IRQ: %d\n", spi->irq);
 
-       devrec = kzalloc(sizeof(struct mrf24j40), GFP_KERNEL);
+       devrec = devm_kzalloc(&spi->dev, sizeof(struct mrf24j40), GFP_KERNEL);
        if (!devrec)
-               goto err_devrec;
-       devrec->buf = kzalloc(3, GFP_KERNEL);
+               goto err_ret;
+       devrec->buf = devm_kzalloc(&spi->dev, 3, GFP_KERNEL);
        if (!devrec->buf)
-               goto err_buf;
+               goto err_ret;
 
        spi->mode = SPI_MODE_0; /* TODO: Is this appropriate for right here? */
        if (spi->max_speed_hz > MAX_SPI_SPEED_HZ)
@@ -638,7 +638,7 @@ static int mrf24j40_probe(struct spi_device *spi)
 
        devrec->dev = ieee802154_alloc_device(0, &mrf24j40_ops);
        if (!devrec->dev)
-               goto err_alloc_dev;
+               goto err_ret;
 
        devrec->dev->priv = devrec;
        devrec->dev->parent = &devrec->spi->dev;
@@ -676,12 +676,13 @@ static int mrf24j40_probe(struct spi_device *spi)
        val &= ~0x3; /* Clear RX mode (normal) */
        write_short_reg(devrec, REG_RXMCR, val);
 
-       ret = request_threaded_irq(spi->irq,
-                                  NULL,
-                                  mrf24j40_isr,
-                                  IRQF_TRIGGER_LOW|IRQF_ONESHOT,
-                                  dev_name(&spi->dev),
-                                  devrec);
+       ret = devm_request_threaded_irq(&spi->dev,
+                                       spi->irq,
+                                       NULL,
+                                       mrf24j40_isr,
+                                       IRQF_TRIGGER_LOW|IRQF_ONESHOT,
+                                       dev_name(&spi->dev),
+                                       devrec);
 
        if (ret) {
                dev_err(printdev(devrec), "Unable to get IRQ");
@@ -695,11 +696,7 @@ err_read_reg:
        ieee802154_unregister_device(devrec->dev);
 err_register_device:
        ieee802154_free_device(devrec->dev);
-err_alloc_dev:
-       kfree(devrec->buf);
-err_buf:
-       kfree(devrec);
-err_devrec:
+err_ret:
        return ret;
 }
 
@@ -709,15 +706,11 @@ static int mrf24j40_remove(struct spi_device *spi)
 
        dev_dbg(printdev(devrec), "remove\n");
 
-       free_irq(spi->irq, devrec);
        ieee802154_unregister_device(devrec->dev);
        ieee802154_free_device(devrec->dev);
        /* TODO: Will ieee802154_free_device() wait until ->xmit() is
         * complete? */
 
-       /* Clean up the SPI stuff. */
-       kfree(devrec->buf);
-       kfree(devrec);
        return 0;
 }
 
index 3da44d5d91497801a141b373c60f8cd5890a1bf9..8d101d63abca9a48466edfed2db11ab54df32d63 100644 (file)
@@ -396,7 +396,8 @@ config MCS_FIR
 
 config SH_IRDA
        tristate "SuperH IrDA driver"
-       depends on IRDA && ARCH_SHMOBILE
+       depends on IRDA
+       depends on ARCH_SHMOBILE || COMPILE_TEST
        help
          Say Y here if your want to enable SuperH IrDA devices.
 
index 2900af091c2d5f718bd14a083fb42cf11a72af3b..998bb89ede71c75ea1d67d4c07cbc77963d79c72 100644 (file)
@@ -510,10 +510,8 @@ static void via_hw_init(struct via_ircc_cb *self)
  */
 static int via_ircc_read_dongle_id(int iobase)
 {
-       int dongle_id = 9;      /* Default to IBM */
-
        IRDA_ERROR("via-ircc: dongle probing not supported, please specify dongle_id module parameter.\n");
-       return dongle_id;
+       return 9;       /* Default to IBM */
 }
 
 /*
@@ -926,7 +924,6 @@ static int via_ircc_dma_xmit(struct via_ircc_cb *self, u16 iobase)
 static int via_ircc_dma_xmit_complete(struct via_ircc_cb *self)
 {
        int iobase;
-       int ret = TRUE;
        u8 Tx_status;
 
        IRDA_DEBUG(3, "%s()\n", __func__);
@@ -983,7 +980,7 @@ F01_E*/
        // Tell the network layer, that we can accept more frames 
        netif_wake_queue(self->netdev);
 //F01   }
-       return ret;
+       return TRUE;
 }
 
 /*
index e641bb2403624fdd91fb565ea646d70d03c2c5fd..11dbdf36d9c1b328ff70b6e5d0e5d2f7729dbb2a 100644 (file)
 #include "w83977af.h"
 #include "w83977af_ir.h"
 
-#ifdef  CONFIG_ARCH_NETWINDER            /* Adjust to NetWinder differences */
-#undef  CONFIG_NETWINDER_TX_DMA_PROBLEMS /* Not needed */
-#define CONFIG_NETWINDER_RX_DMA_PROBLEMS /* Must have this one! */
-#endif
 #define CONFIG_USE_W977_PNP        /* Currently needed */
 #define PIO_MAX_SPEED       115200 
 
@@ -332,7 +328,7 @@ static int w83977af_probe(int iobase, int irq, int dma)
                w977_write_reg(0x74, dma+1, efbase[i]);
 #else
                w977_write_reg(0x74, dma, efbase[i]);   
-#endif /*CONFIG_ARCH_NETWINDER */
+#endif /* CONFIG_ARCH_NETWINDER */
                w977_write_reg(0x75, 0x04, efbase[i]);  /* Disable Tx DMA */
        
                /* Set append hardware CRC, enable IR bank selection */ 
@@ -563,10 +559,6 @@ static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb,
 static void w83977af_dma_write(struct w83977af_ir *self, int iobase)
 {
        __u8 set;
-#ifdef CONFIG_NETWINDER_TX_DMA_PROBLEMS
-       unsigned long flags;
-       __u8 hcr;
-#endif
         IRDA_DEBUG(4, "%s(), len=%d\n", __func__ , self->tx_buff.len);
 
        /* Save current set */
@@ -579,30 +571,13 @@ static void w83977af_dma_write(struct w83977af_ir *self, int iobase)
        /* Choose transmit DMA channel  */ 
        switch_bank(iobase, SET2);
        outb(ADCR1_D_CHSW|/*ADCR1_DMA_F|*/ADCR1_ADV_SL, iobase+ADCR1);
-#ifdef CONFIG_NETWINDER_TX_DMA_PROBLEMS
-       spin_lock_irqsave(&self->lock, flags);
-
-       disable_dma(self->io.dma);
-       clear_dma_ff(self->io.dma);
-       set_dma_mode(self->io.dma, DMA_MODE_READ);
-       set_dma_addr(self->io.dma, self->tx_buff_dma);
-       set_dma_count(self->io.dma, self->tx_buff.len);
-#else
        irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len,
                       DMA_MODE_WRITE); 
-#endif
        self->io.direction = IO_XMIT;
        
        /* Enable DMA */
        switch_bank(iobase, SET0);
-#ifdef CONFIG_NETWINDER_TX_DMA_PROBLEMS
-       hcr = inb(iobase+HCR);
-       outb(hcr | HCR_EN_DMA, iobase+HCR);
-       enable_dma(self->io.dma);
-       spin_unlock_irqrestore(&self->lock, flags);
-#else  
        outb(inb(iobase+HCR) | HCR_EN_DMA | HCR_TX_WT, iobase+HCR);
-#endif
 
        /* Restore set register */
        outb(set, iobase+SSR);
@@ -711,7 +686,7 @@ static int w83977af_dma_receive(struct w83977af_ir *self)
 {
        int iobase;
        __u8 set;
-#ifdef CONFIG_NETWINDER_RX_DMA_PROBLEMS
+#ifdef CONFIG_ARCH_NETWINDER
        unsigned long flags;
        __u8 hcr;
 #endif
@@ -736,7 +711,7 @@ static int w83977af_dma_receive(struct w83977af_ir *self)
        self->io.direction = IO_RECV;
        self->rx_buff.data = self->rx_buff.head;
 
-#ifdef CONFIG_NETWINDER_RX_DMA_PROBLEMS
+#ifdef CONFIG_ARCH_NETWINDER
        spin_lock_irqsave(&self->lock, flags);
 
        disable_dma(self->io.dma);
@@ -759,7 +734,7 @@ static int w83977af_dma_receive(struct w83977af_ir *self)
        
        /* Enable DMA */
        switch_bank(iobase, SET0);
-#ifdef CONFIG_NETWINDER_RX_DMA_PROBLEMS
+#ifdef CONFIG_ARCH_NETWINDER
        hcr = inb(iobase+HCR);
        outb(hcr | HCR_EN_DMA, iobase+HCR);
        enable_dma(self->io.dma);
index d53e299ae1d97ca39c4f3af511b97c5dacb32827..958df383068a534d01bf9632463a22689184ea50 100644 (file)
 #include <linux/if_link.h>
 #include <linux/if_macvlan.h>
 #include <linux/hash.h>
+#include <linux/workqueue.h>
 #include <net/rtnetlink.h>
 #include <net/xfrm.h>
+#include <linux/netpoll.h>
 
 #define MACVLAN_HASH_SIZE      (1 << BITS_PER_BYTE)
 
@@ -40,10 +42,19 @@ struct macvlan_port {
        struct hlist_head       vlan_hash[MACVLAN_HASH_SIZE];
        struct list_head        vlans;
        struct rcu_head         rcu;
+       struct sk_buff_head     bc_queue;
+       struct work_struct      bc_work;
        bool                    passthru;
-       int                     count;
 };
 
+#define MACVLAN_PORT_IS_EMPTY(port)    list_empty(&port->vlans)
+
+struct macvlan_skb_cb {
+       const struct macvlan_dev *src;
+};
+
+#define MACVLAN_SKB_CB(__skb) ((struct macvlan_skb_cb *)&((__skb)->cb[0]))
+
 static void macvlan_port_destroy(struct net_device *dev);
 
 static struct macvlan_port *macvlan_port_get_rcu(const struct net_device *dev)
@@ -120,7 +131,7 @@ static int macvlan_broadcast_one(struct sk_buff *skb,
        struct net_device *dev = vlan->dev;
 
        if (local)
-               return dev_forward_skb(dev, skb);
+               return __dev_forward_skb(dev, skb);
 
        skb->dev = dev;
        if (ether_addr_equal_64bits(eth->h_dest, dev->broadcast))
@@ -128,7 +139,7 @@ static int macvlan_broadcast_one(struct sk_buff *skb,
        else
                skb->pkt_type = PACKET_MULTICAST;
 
-       return netif_rx(skb);
+       return 0;
 }
 
 static u32 macvlan_hash_mix(const struct macvlan_dev *vlan)
@@ -175,32 +186,32 @@ static void macvlan_broadcast(struct sk_buff *skb,
                        if (likely(nskb))
                                err = macvlan_broadcast_one(
                                        nskb, vlan, eth,
-                                       mode == MACVLAN_MODE_BRIDGE);
+                                       mode == MACVLAN_MODE_BRIDGE) ?:
+                                     netif_rx_ni(nskb);
                        macvlan_count_rx(vlan, skb->len + ETH_HLEN,
                                         err == NET_RX_SUCCESS, 1);
                }
        }
 }
 
-/* called under rcu_read_lock() from netif_receive_skb */
-static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
+static void macvlan_process_broadcast(struct work_struct *w)
 {
-       struct macvlan_port *port;
-       struct sk_buff *skb = *pskb;
-       const struct ethhdr *eth = eth_hdr(skb);
-       const struct macvlan_dev *vlan;
-       const struct macvlan_dev *src;
-       struct net_device *dev;
-       unsigned int len = 0;
-       int ret = NET_RX_DROP;
+       struct macvlan_port *port = container_of(w, struct macvlan_port,
+                                                bc_work);
+       struct sk_buff *skb;
+       struct sk_buff_head list;
+
+       skb_queue_head_init(&list);
+
+       spin_lock_bh(&port->bc_queue.lock);
+       skb_queue_splice_tail_init(&port->bc_queue, &list);
+       spin_unlock_bh(&port->bc_queue.lock);
+
+       while ((skb = __skb_dequeue(&list))) {
+               const struct macvlan_dev *src = MACVLAN_SKB_CB(skb)->src;
+
+               rcu_read_lock();
 
-       port = macvlan_port_get_rcu(skb->dev);
-       if (is_multicast_ether_addr(eth->h_dest)) {
-               skb = ip_check_defrag(skb, IP_DEFRAG_MACVLAN);
-               if (!skb)
-                       return RX_HANDLER_CONSUMED;
-               eth = eth_hdr(skb);
-               src = macvlan_hash_lookup(port, eth->h_source);
                if (!src)
                        /* frame comes from an external address */
                        macvlan_broadcast(skb, port, NULL,
@@ -213,20 +224,80 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
                        macvlan_broadcast(skb, port, src->dev,
                                          MACVLAN_MODE_VEPA |
                                          MACVLAN_MODE_BRIDGE);
-               else if (src->mode == MACVLAN_MODE_BRIDGE)
+               else
                        /*
                         * flood only to VEPA ports, bridge ports
                         * already saw the frame on the way out.
                         */
                        macvlan_broadcast(skb, port, src->dev,
                                          MACVLAN_MODE_VEPA);
-               else {
+
+               rcu_read_unlock();
+
+               kfree_skb(skb);
+       }
+}
+
+static void macvlan_broadcast_enqueue(struct macvlan_port *port,
+                                     struct sk_buff *skb)
+{
+       struct sk_buff *nskb;
+       int err = -ENOMEM;
+
+       nskb = skb_clone(skb, GFP_ATOMIC);
+       if (!nskb)
+               goto err;
+
+       spin_lock(&port->bc_queue.lock);
+       if (skb_queue_len(&port->bc_queue) < skb->dev->tx_queue_len) {
+               __skb_queue_tail(&port->bc_queue, nskb);
+               err = 0;
+       }
+       spin_unlock(&port->bc_queue.lock);
+
+       if (err)
+               goto free_nskb;
+
+       schedule_work(&port->bc_work);
+       return;
+
+free_nskb:
+       kfree_skb(nskb);
+err:
+       atomic_long_inc(&skb->dev->rx_dropped);
+}
+
+/* called under rcu_read_lock() from netif_receive_skb */
+static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
+{
+       struct macvlan_port *port;
+       struct sk_buff *skb = *pskb;
+       const struct ethhdr *eth = eth_hdr(skb);
+       const struct macvlan_dev *vlan;
+       const struct macvlan_dev *src;
+       struct net_device *dev;
+       unsigned int len = 0;
+       int ret = NET_RX_DROP;
+
+       port = macvlan_port_get_rcu(skb->dev);
+       if (is_multicast_ether_addr(eth->h_dest)) {
+               skb = ip_check_defrag(skb, IP_DEFRAG_MACVLAN);
+               if (!skb)
+                       return RX_HANDLER_CONSUMED;
+               eth = eth_hdr(skb);
+               src = macvlan_hash_lookup(port, eth->h_source);
+               if (src && src->mode != MACVLAN_MODE_VEPA &&
+                   src->mode != MACVLAN_MODE_BRIDGE) {
                        /* forward to original port. */
                        vlan = src;
-                       ret = macvlan_broadcast_one(skb, vlan, eth, 0);
+                       ret = macvlan_broadcast_one(skb, vlan, eth, 0) ?:
+                             netif_rx(skb);
                        goto out;
                }
 
+               MACVLAN_SKB_CB(skb)->src = src;
+               macvlan_broadcast_enqueue(port, skb);
+
                return RX_HANDLER_PASS;
        }
 
@@ -287,12 +358,26 @@ xmit_world:
        return dev_queue_xmit(skb);
 }
 
+static inline netdev_tx_t macvlan_netpoll_send_skb(struct macvlan_dev *vlan, struct sk_buff *skb)
+{
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       if (vlan->netpoll)
+               netpoll_send_skb(vlan->netpoll, skb);
+#else
+       BUG();
+#endif
+       return NETDEV_TX_OK;
+}
+
 static netdev_tx_t macvlan_start_xmit(struct sk_buff *skb,
                                      struct net_device *dev)
 {
        unsigned int len = skb->len;
        int ret;
-       const struct macvlan_dev *vlan = netdev_priv(dev);
+       struct macvlan_dev *vlan = netdev_priv(dev);
+
+       if (unlikely(netpoll_tx_running(dev)))
+               return macvlan_netpoll_send_skb(vlan, skb);
 
        if (vlan->fwd_priv) {
                skb->dev = vlan->lowerdev;
@@ -424,35 +509,49 @@ hash_del:
        return 0;
 }
 
-static int macvlan_set_mac_address(struct net_device *dev, void *p)
+static int macvlan_sync_address(struct net_device *dev, unsigned char *addr)
 {
        struct macvlan_dev *vlan = netdev_priv(dev);
        struct net_device *lowerdev = vlan->lowerdev;
-       struct sockaddr *addr = p;
        int err;
 
-       if (!is_valid_ether_addr(addr->sa_data))
-               return -EADDRNOTAVAIL;
-
        if (!(dev->flags & IFF_UP)) {
                /* Just copy in the new address */
-               memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+               ether_addr_copy(dev->dev_addr, addr);
        } else {
                /* Rehash and update the device filters */
-               if (macvlan_addr_busy(vlan->port, addr->sa_data))
+               if (macvlan_addr_busy(vlan->port, addr))
                        return -EBUSY;
 
-               err = dev_uc_add(lowerdev, addr->sa_data);
-               if (err)
-                       return err;
+               if (!vlan->port->passthru) {
+                       err = dev_uc_add(lowerdev, addr);
+                       if (err)
+                               return err;
 
-               dev_uc_del(lowerdev, dev->dev_addr);
+                       dev_uc_del(lowerdev, dev->dev_addr);
+               }
 
-               macvlan_hash_change_addr(vlan, addr->sa_data);
+               macvlan_hash_change_addr(vlan, addr);
        }
        return 0;
 }
 
+static int macvlan_set_mac_address(struct net_device *dev, void *p)
+{
+       struct macvlan_dev *vlan = netdev_priv(dev);
+       struct sockaddr *addr = p;
+
+       if (!is_valid_ether_addr(addr->sa_data))
+               return -EADDRNOTAVAIL;
+
+       if (vlan->mode == MACVLAN_MODE_PASSTHRU) {
+               dev_set_mac_address(vlan->lowerdev, addr);
+               return 0;
+       }
+
+       return macvlan_sync_address(dev, addr->sa_data);
+}
+
 static void macvlan_change_rx_flags(struct net_device *dev, int change)
 {
        struct macvlan_dev *vlan = netdev_priv(dev);
@@ -567,8 +666,7 @@ static void macvlan_uninit(struct net_device *dev)
 
        free_percpu(vlan->pcpu_stats);
 
-       port->count -= 1;
-       if (!port->count)
+       if (MACVLAN_PORT_IS_EMPTY(port))
                macvlan_port_destroy(port->dev);
 }
 
@@ -705,6 +803,50 @@ static netdev_features_t macvlan_fix_features(struct net_device *dev,
        return features;
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void macvlan_dev_poll_controller(struct net_device *dev)
+{
+       return;
+}
+
+static int macvlan_dev_netpoll_setup(struct net_device *dev, struct netpoll_info *npinfo)
+{
+       struct macvlan_dev *vlan = netdev_priv(dev);
+       struct net_device *real_dev = vlan->lowerdev;
+       struct netpoll *netpoll;
+       int err = 0;
+
+       netpoll = kzalloc(sizeof(*netpoll), GFP_KERNEL);
+       err = -ENOMEM;
+       if (!netpoll)
+               goto out;
+
+       err = __netpoll_setup(netpoll, real_dev);
+       if (err) {
+               kfree(netpoll);
+               goto out;
+       }
+
+       vlan->netpoll = netpoll;
+
+out:
+       return err;
+}
+
+static void macvlan_dev_netpoll_cleanup(struct net_device *dev)
+{
+       struct macvlan_dev *vlan = netdev_priv(dev);
+       struct netpoll *netpoll = vlan->netpoll;
+
+       if (!netpoll)
+               return;
+
+       vlan->netpoll = NULL;
+
+       __netpoll_free_async(netpoll);
+}
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
 static const struct ethtool_ops macvlan_ethtool_ops = {
        .get_link               = ethtool_op_get_link,
        .get_settings           = macvlan_ethtool_get_settings,
@@ -730,6 +872,11 @@ static const struct net_device_ops macvlan_netdev_ops = {
        .ndo_fdb_del            = macvlan_fdb_del,
        .ndo_fdb_dump           = ndo_dflt_fdb_dump,
        .ndo_get_lock_subclass  = macvlan_get_nest_level,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = macvlan_dev_poll_controller,
+       .ndo_netpoll_setup      = macvlan_dev_netpoll_setup,
+       .ndo_netpoll_cleanup    = macvlan_dev_netpoll_cleanup,
+#endif
 };
 
 void macvlan_common_setup(struct net_device *dev)
@@ -770,6 +917,9 @@ static int macvlan_port_create(struct net_device *dev)
        for (i = 0; i < MACVLAN_HASH_SIZE; i++)
                INIT_HLIST_HEAD(&port->vlan_hash[i]);
 
+       skb_queue_head_init(&port->bc_queue);
+       INIT_WORK(&port->bc_work, macvlan_process_broadcast);
+
        err = netdev_rx_handler_register(dev, macvlan_handle_frame, port);
        if (err)
                kfree(port);
@@ -782,6 +932,7 @@ static void macvlan_port_destroy(struct net_device *dev)
 {
        struct macvlan_port *port = macvlan_port_get_rtnl(dev);
 
+       cancel_work_sync(&port->bc_work);
        dev->priv_flags &= ~IFF_MACVLAN_PORT;
        netdev_rx_handler_unregister(dev);
        kfree_rcu(port, rcu);
@@ -868,13 +1019,12 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
                vlan->flags = nla_get_u16(data[IFLA_MACVLAN_FLAGS]);
 
        if (vlan->mode == MACVLAN_MODE_PASSTHRU) {
-               if (port->count)
+               if (!MACVLAN_PORT_IS_EMPTY(port))
                        return -EINVAL;
                port->passthru = true;
                eth_hw_addr_inherit(dev, lowerdev);
        }
 
-       port->count += 1;
        err = register_netdevice(dev);
        if (err < 0)
                goto destroy_port;
@@ -892,8 +1042,7 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
 unregister_netdev:
        unregister_netdevice(dev);
 destroy_port:
-       port->count -= 1;
-       if (!port->count)
+       if (MACVLAN_PORT_IS_EMPTY(port))
                macvlan_port_destroy(lowerdev);
 
        return err;
@@ -1027,6 +1176,25 @@ static int macvlan_device_event(struct notifier_block *unused,
                        vlan->dev->gso_max_size = dev->gso_max_size;
                        netdev_update_features(vlan->dev);
                }
+               break;
+       case NETDEV_CHANGEMTU:
+               list_for_each_entry(vlan, &port->vlans, list) {
+                       if (vlan->dev->mtu <= dev->mtu)
+                               continue;
+                       dev_set_mtu(vlan->dev, dev->mtu);
+               }
+               break;
+       case NETDEV_CHANGEADDR:
+               if (!port->passthru)
+                       return NOTIFY_DONE;
+
+               vlan = list_first_entry_or_null(&port->vlans,
+                                               struct macvlan_dev,
+                                               list);
+
+               if (macvlan_sync_address(vlan->dev, dev->dev_addr))
+                       return NOTIFY_BAD;
+
                break;
        case NETDEV_UNREGISTER:
                /* twiddle thumbs on netns device moves */
@@ -1036,11 +1204,17 @@ static int macvlan_device_event(struct notifier_block *unused,
                list_for_each_entry_safe(vlan, next, &port->vlans, list)
                        vlan->dev->rtnl_link_ops->dellink(vlan->dev, &list_kill);
                unregister_netdevice_many(&list_kill);
-               list_del(&list_kill);
                break;
        case NETDEV_PRE_TYPE_CHANGE:
                /* Forbid underlaying device to change its type. */
                return NOTIFY_BAD;
+
+       case NETDEV_NOTIFY_PEERS:
+       case NETDEV_BONDING_FAILOVER:
+       case NETDEV_RESEND_IGMP:
+               /* Propagate to all vlans */
+               list_for_each_entry(vlan, &port->vlans, list)
+                       call_netdevice_notifiers(event, vlan->dev);
        }
        return NOTIFY_DONE;
 }
index 63aa9d9e34c52b1c2150fa7876ce3658a7c11fe5..5a7e6397440ab18487a87dd9951aa8ed12caf21a 100644 (file)
@@ -298,7 +298,6 @@ static int ntb_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 {
        cmd->supported = SUPPORTED_Backplane;
        cmd->advertising = ADVERTISED_Backplane;
-       cmd->speed = SPEED_UNKNOWN;
        ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
        cmd->duplex = DUPLEX_FULL;
        cmd->port = PORT_OTHER;
@@ -348,7 +347,7 @@ static int ntb_netdev_probe(struct pci_dev *pdev)
        memcpy(ndev->dev_addr, ndev->perm_addr, ndev->addr_len);
 
        ndev->netdev_ops = &ntb_netdev_ops;
-       SET_ETHTOOL_OPS(ndev, &ntb_ethtool_ops);
+       ndev->ethtool_ops = &ntb_ethtool_ops;
 
        dev->qp = ntb_transport_create_queue(ndev, pdev, &ntb_netdev_handlers);
        if (!dev->qp) {
index 6a17f92153b31b3b2fe95a5d7e087eeb3373032b..65de0cab8d07795c5c2255d7397a95e3720477fc 100644 (file)
@@ -24,6 +24,12 @@ config AMD_PHY
        ---help---
          Currently supports the am79c874
 
+config AMD_XGBE_PHY
+       tristate "Driver for the AMD 10GbE (amd-xgbe) PHYs"
+       depends on OF
+       ---help---
+         Currently supports the AMD 10GbE PHY
+
 config MARVELL_PHY
        tristate "Drivers for Marvell PHYs"
        ---help---
index 07d24024863e0805af4127c72e4663174a47babb..7dc3d5b304cfc250a2618e096abecf428ed05bad 100644 (file)
@@ -33,3 +33,4 @@ 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
 obj-$(CONFIG_MDIO_MOXART)      += mdio-moxart.o
+obj-$(CONFIG_AMD_XGBE_PHY)     += amd-xgbe-phy.o
diff --git a/drivers/net/phy/amd-xgbe-phy.c b/drivers/net/phy/amd-xgbe-phy.c
new file mode 100644 (file)
index 0000000..b57c224
--- /dev/null
@@ -0,0 +1,1357 @@
+/*
+ * AMD 10Gb Ethernet PHY driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute 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 file 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/>.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Advanced Micro Devices, Inc. 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 <COPYRIGHT HOLDER> 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/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+#include <linux/mdio.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+#include <linux/uaccess.h>
+#include <asm/irq.h>
+
+
+MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION("1.0.0-a");
+MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
+
+#define XGBE_PHY_ID    0x000162d0
+#define XGBE_PHY_MASK  0xfffffff0
+
+#define XGBE_AN_INT_CMPLT              0x01
+#define XGBE_AN_INC_LINK               0x02
+#define XGBE_AN_PG_RCV                 0x04
+
+#define XNP_MCF_NULL_MESSAGE           0x001
+#define XNP_ACK_PROCESSED              (1 << 12)
+#define XNP_MP_FORMATTED               (1 << 13)
+#define XNP_NP_EXCHANGE                        (1 << 15)
+
+#ifndef MDIO_PMA_10GBR_PMD_CTRL
+#define MDIO_PMA_10GBR_PMD_CTRL                0x0096
+#endif
+#ifndef MDIO_PMA_10GBR_FEC_CTRL
+#define MDIO_PMA_10GBR_FEC_CTRL                0x00ab
+#endif
+#ifndef MDIO_AN_XNP
+#define MDIO_AN_XNP                    0x0016
+#endif
+
+#ifndef MDIO_AN_INTMASK
+#define MDIO_AN_INTMASK                        0x8001
+#endif
+#ifndef MDIO_AN_INT
+#define MDIO_AN_INT                    0x8002
+#endif
+
+#ifndef MDIO_CTRL1_SPEED1G
+#define MDIO_CTRL1_SPEED1G             (MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100)
+#endif
+
+/* SerDes integration register offsets */
+#define SIR0_STATUS                    0x0040
+#define SIR1_SPEED                     0x0000
+
+/* SerDes integration register entry bit positions and sizes */
+#define SIR0_STATUS_RX_READY_INDEX     0
+#define SIR0_STATUS_RX_READY_WIDTH     1
+#define SIR0_STATUS_TX_READY_INDEX     8
+#define SIR0_STATUS_TX_READY_WIDTH     1
+#define SIR1_SPEED_DATARATE_INDEX      4
+#define SIR1_SPEED_DATARATE_WIDTH      2
+#define SIR1_SPEED_PI_SPD_SEL_INDEX    12
+#define SIR1_SPEED_PI_SPD_SEL_WIDTH    4
+#define SIR1_SPEED_PLLSEL_INDEX                3
+#define SIR1_SPEED_PLLSEL_WIDTH                1
+#define SIR1_SPEED_RATECHANGE_INDEX    6
+#define SIR1_SPEED_RATECHANGE_WIDTH    1
+#define SIR1_SPEED_TXAMP_INDEX         8
+#define SIR1_SPEED_TXAMP_WIDTH         4
+#define SIR1_SPEED_WORDMODE_INDEX      0
+#define SIR1_SPEED_WORDMODE_WIDTH      3
+
+#define SPEED_10000_CDR                        0x7
+#define SPEED_10000_PLL                        0x1
+#define SPEED_10000_RATE               0x0
+#define SPEED_10000_TXAMP              0xa
+#define SPEED_10000_WORD               0x7
+
+#define SPEED_2500_CDR                 0x2
+#define SPEED_2500_PLL                 0x0
+#define SPEED_2500_RATE                        0x2
+#define SPEED_2500_TXAMP               0xf
+#define SPEED_2500_WORD                        0x1
+
+#define SPEED_1000_CDR                 0x2
+#define SPEED_1000_PLL                 0x0
+#define SPEED_1000_RATE                        0x3
+#define SPEED_1000_TXAMP               0xf
+#define SPEED_1000_WORD                        0x1
+
+
+/* SerDes RxTx register offsets */
+#define RXTX_REG20                     0x0050
+#define RXTX_REG114                    0x01c8
+
+/* SerDes RxTx register entry bit positions and sizes */
+#define RXTX_REG20_BLWC_ENA_INDEX      2
+#define RXTX_REG20_BLWC_ENA_WIDTH      1
+#define RXTX_REG114_PQ_REG_INDEX       9
+#define RXTX_REG114_PQ_REG_WIDTH       7
+
+#define RXTX_10000_BLWC                        0
+#define RXTX_10000_PQ                  0x1e
+
+#define RXTX_2500_BLWC                 1
+#define RXTX_2500_PQ                   0xa
+
+#define RXTX_1000_BLWC                 1
+#define RXTX_1000_PQ                   0xa
+
+/* Bit setting and getting macros
+ *  The get macro will extract the current bit field value from within
+ *  the variable
+ *
+ *  The set macro will clear the current bit field value within the
+ *  variable and then set the bit field of the variable to the
+ *  specified value
+ */
+#define GET_BITS(_var, _index, _width)                                 \
+       (((_var) >> (_index)) & ((0x1 << (_width)) - 1))
+
+#define SET_BITS(_var, _index, _width, _val)                           \
+do {                                                                   \
+       (_var) &= ~(((0x1 << (_width)) - 1) << (_index));               \
+       (_var) |= (((_val) & ((0x1 << (_width)) - 1)) << (_index));     \
+} while (0)
+
+/* Macros for reading or writing SerDes integration registers
+ *  The ioread macros will get bit fields or full values using the
+ *  register definitions formed using the input names
+ *
+ *  The iowrite macros will set bit fields or full values using the
+ *  register definitions formed using the input names
+ */
+#define XSIR0_IOREAD(_priv, _reg)                                      \
+       ioread16((_priv)->sir0_regs + _reg)
+
+#define XSIR0_IOREAD_BITS(_priv, _reg, _field)                         \
+       GET_BITS(XSIR0_IOREAD((_priv), _reg),                           \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH)
+
+#define XSIR0_IOWRITE(_priv, _reg, _val)                               \
+       iowrite16((_val), (_priv)->sir0_regs + _reg)
+
+#define XSIR0_IOWRITE_BITS(_priv, _reg, _field, _val)                  \
+do {                                                                   \
+       u16 reg_val = XSIR0_IOREAD((_priv), _reg);                      \
+       SET_BITS(reg_val,                                               \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH, (_val));                      \
+       XSIR0_IOWRITE((_priv), _reg, reg_val);                          \
+} while (0)
+
+#define XSIR1_IOREAD(_priv, _reg)                                      \
+       ioread16((_priv)->sir1_regs + _reg)
+
+#define XSIR1_IOREAD_BITS(_priv, _reg, _field)                         \
+       GET_BITS(XSIR1_IOREAD((_priv), _reg),                           \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH)
+
+#define XSIR1_IOWRITE(_priv, _reg, _val)                               \
+       iowrite16((_val), (_priv)->sir1_regs + _reg)
+
+#define XSIR1_IOWRITE_BITS(_priv, _reg, _field, _val)                  \
+do {                                                                   \
+       u16 reg_val = XSIR1_IOREAD((_priv), _reg);                      \
+       SET_BITS(reg_val,                                               \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH, (_val));                      \
+       XSIR1_IOWRITE((_priv), _reg, reg_val);                          \
+} while (0)
+
+
+/* Macros for reading or writing SerDes RxTx registers
+ *  The ioread macros will get bit fields or full values using the
+ *  register definitions formed using the input names
+ *
+ *  The iowrite macros will set bit fields or full values using the
+ *  register definitions formed using the input names
+ */
+#define XRXTX_IOREAD(_priv, _reg)                                      \
+       ioread16((_priv)->rxtx_regs + _reg)
+
+#define XRXTX_IOREAD_BITS(_priv, _reg, _field)                         \
+       GET_BITS(XRXTX_IOREAD((_priv), _reg),                           \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH)
+
+#define XRXTX_IOWRITE(_priv, _reg, _val)                               \
+       iowrite16((_val), (_priv)->rxtx_regs + _reg)
+
+#define XRXTX_IOWRITE_BITS(_priv, _reg, _field, _val)                  \
+do {                                                                   \
+       u16 reg_val = XRXTX_IOREAD((_priv), _reg);                      \
+       SET_BITS(reg_val,                                               \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH, (_val));                      \
+       XRXTX_IOWRITE((_priv), _reg, reg_val);                          \
+} while (0)
+
+
+enum amd_xgbe_phy_an {
+       AMD_XGBE_AN_READY = 0,
+       AMD_XGBE_AN_START,
+       AMD_XGBE_AN_EVENT,
+       AMD_XGBE_AN_PAGE_RECEIVED,
+       AMD_XGBE_AN_INCOMPAT_LINK,
+       AMD_XGBE_AN_COMPLETE,
+       AMD_XGBE_AN_NO_LINK,
+       AMD_XGBE_AN_EXIT,
+       AMD_XGBE_AN_ERROR,
+};
+
+enum amd_xgbe_phy_rx {
+       AMD_XGBE_RX_READY = 0,
+       AMD_XGBE_RX_BPA,
+       AMD_XGBE_RX_XNP,
+       AMD_XGBE_RX_COMPLETE,
+};
+
+enum amd_xgbe_phy_mode {
+       AMD_XGBE_MODE_KR,
+       AMD_XGBE_MODE_KX,
+};
+
+struct amd_xgbe_phy_priv {
+       struct platform_device *pdev;
+       struct device *dev;
+
+       struct phy_device *phydev;
+
+       /* SerDes related mmio resources */
+       struct resource *rxtx_res;
+       struct resource *sir0_res;
+       struct resource *sir1_res;
+
+       /* SerDes related mmio registers */
+       void __iomem *rxtx_regs;        /* SerDes Rx/Tx CSRs */
+       void __iomem *sir0_regs;        /* SerDes integration registers (1/2) */
+       void __iomem *sir1_regs;        /* SerDes integration registers (2/2) */
+
+       /* Maintain link status for re-starting auto-negotiation */
+       unsigned int link;
+       enum amd_xgbe_phy_mode mode;
+
+       /* Auto-negotiation state machine support */
+       struct mutex an_mutex;
+       enum amd_xgbe_phy_an an_result;
+       enum amd_xgbe_phy_an an_state;
+       enum amd_xgbe_phy_rx kr_state;
+       enum amd_xgbe_phy_rx kx_state;
+       struct work_struct an_work;
+       struct workqueue_struct *an_workqueue;
+};
+
+static int amd_xgbe_an_enable_kr_training(struct phy_device *phydev)
+{
+       int ret;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL);
+       if (ret < 0)
+               return ret;
+
+       ret |= 0x02;
+       phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret);
+
+       return 0;
+}
+
+static int amd_xgbe_an_disable_kr_training(struct phy_device *phydev)
+{
+       int ret;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL);
+       if (ret < 0)
+               return ret;
+
+       ret &= ~0x02;
+       phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret);
+
+       return 0;
+}
+
+static int amd_xgbe_phy_pcs_power_cycle(struct phy_device *phydev)
+{
+       int ret;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
+       if (ret < 0)
+               return ret;
+
+       ret |= MDIO_CTRL1_LPOWER;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
+
+       usleep_range(75, 100);
+
+       ret &= ~MDIO_CTRL1_LPOWER;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
+
+       return 0;
+}
+
+static void amd_xgbe_phy_serdes_start_ratechange(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+
+       /* Assert Rx and Tx ratechange */
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, RATECHANGE, 1);
+}
+
+static void amd_xgbe_phy_serdes_complete_ratechange(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+
+       /* Release Rx and Tx ratechange */
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, RATECHANGE, 0);
+
+       /* Wait for Rx and Tx ready */
+       while (!XSIR0_IOREAD_BITS(priv, SIR0_STATUS, RX_READY) &&
+              !XSIR0_IOREAD_BITS(priv, SIR0_STATUS, TX_READY))
+               usleep_range(10, 20);
+}
+
+static int amd_xgbe_phy_xgmii_mode(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       int ret;
+
+       /* Enable KR training */
+       ret = amd_xgbe_an_enable_kr_training(phydev);
+       if (ret < 0)
+               return ret;
+
+       /* Set PCS to KR/10G speed */
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
+       if (ret < 0)
+               return ret;
+
+       ret &= ~MDIO_PCS_CTRL2_TYPE;
+       ret |= MDIO_PCS_CTRL2_10GBR;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2, ret);
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
+       if (ret < 0)
+               return ret;
+
+       ret &= ~MDIO_CTRL1_SPEEDSEL;
+       ret |= MDIO_CTRL1_SPEED10G;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
+
+       ret = amd_xgbe_phy_pcs_power_cycle(phydev);
+       if (ret < 0)
+               return ret;
+
+       /* Set SerDes to 10G speed */
+       amd_xgbe_phy_serdes_start_ratechange(phydev);
+
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_10000_RATE);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_10000_WORD);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_10000_TXAMP);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_10000_PLL);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_10000_CDR);
+
+       XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_10000_BLWC);
+       XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_10000_PQ);
+
+       amd_xgbe_phy_serdes_complete_ratechange(phydev);
+
+       priv->mode = AMD_XGBE_MODE_KR;
+
+       return 0;
+}
+
+static int amd_xgbe_phy_gmii_2500_mode(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       int ret;
+
+       /* Disable KR training */
+       ret = amd_xgbe_an_disable_kr_training(phydev);
+       if (ret < 0)
+               return ret;
+
+       /* Set PCS to KX/1G speed */
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
+       if (ret < 0)
+               return ret;
+
+       ret &= ~MDIO_PCS_CTRL2_TYPE;
+       ret |= MDIO_PCS_CTRL2_10GBX;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2, ret);
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
+       if (ret < 0)
+               return ret;
+
+       ret &= ~MDIO_CTRL1_SPEEDSEL;
+       ret |= MDIO_CTRL1_SPEED1G;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
+
+       ret = amd_xgbe_phy_pcs_power_cycle(phydev);
+       if (ret < 0)
+               return ret;
+
+       /* Set SerDes to 2.5G speed */
+       amd_xgbe_phy_serdes_start_ratechange(phydev);
+
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_2500_RATE);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_2500_WORD);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_2500_TXAMP);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_2500_PLL);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_2500_CDR);
+
+       XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_2500_BLWC);
+       XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_2500_PQ);
+
+       amd_xgbe_phy_serdes_complete_ratechange(phydev);
+
+       priv->mode = AMD_XGBE_MODE_KX;
+
+       return 0;
+}
+
+static int amd_xgbe_phy_gmii_mode(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       int ret;
+
+       /* Disable KR training */
+       ret = amd_xgbe_an_disable_kr_training(phydev);
+       if (ret < 0)
+               return ret;
+
+       /* Set PCS to KX/1G speed */
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
+       if (ret < 0)
+               return ret;
+
+       ret &= ~MDIO_PCS_CTRL2_TYPE;
+       ret |= MDIO_PCS_CTRL2_10GBX;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2, ret);
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
+       if (ret < 0)
+               return ret;
+
+       ret &= ~MDIO_CTRL1_SPEEDSEL;
+       ret |= MDIO_CTRL1_SPEED1G;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
+
+       ret = amd_xgbe_phy_pcs_power_cycle(phydev);
+       if (ret < 0)
+               return ret;
+
+       /* Set SerDes to 1G speed */
+       amd_xgbe_phy_serdes_start_ratechange(phydev);
+
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_1000_RATE);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_1000_WORD);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_1000_TXAMP);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_1000_PLL);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_1000_CDR);
+
+       XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_1000_BLWC);
+       XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_1000_PQ);
+
+       amd_xgbe_phy_serdes_complete_ratechange(phydev);
+
+       priv->mode = AMD_XGBE_MODE_KX;
+
+       return 0;
+}
+
+static int amd_xgbe_phy_switch_mode(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       int ret;
+
+       /* If we are in KR switch to KX, and vice-versa */
+       if (priv->mode == AMD_XGBE_MODE_KR)
+               ret = amd_xgbe_phy_gmii_mode(phydev);
+       else
+               ret = amd_xgbe_phy_xgmii_mode(phydev);
+
+       return ret;
+}
+
+static enum amd_xgbe_phy_an amd_xgbe_an_switch_mode(struct phy_device *phydev)
+{
+       int ret;
+
+       ret = amd_xgbe_phy_switch_mode(phydev);
+       if (ret < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       return AMD_XGBE_AN_START;
+}
+
+static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev,
+                                                   enum amd_xgbe_phy_rx *state)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       int ad_reg, lp_reg, ret;
+
+       *state = AMD_XGBE_RX_COMPLETE;
+
+       /* If we're in KX mode then we're done */
+       if (priv->mode == AMD_XGBE_MODE_KX)
+               return AMD_XGBE_AN_EVENT;
+
+       /* Enable/Disable FEC */
+       ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2);
+       if (ad_reg < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       lp_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA + 2);
+       if (lp_reg < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FEC_CTRL);
+       if (ret < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       if ((ad_reg & 0xc000) && (lp_reg & 0xc000))
+               ret |= 0x01;
+       else
+               ret &= ~0x01;
+
+       phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FEC_CTRL, ret);
+
+       /* Start KR training */
+       ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL);
+       if (ret < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       ret |= 0x01;
+       phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret);
+
+       return AMD_XGBE_AN_EVENT;
+}
+
+static enum amd_xgbe_phy_an amd_xgbe_an_tx_xnp(struct phy_device *phydev,
+                                              enum amd_xgbe_phy_rx *state)
+{
+       u16 msg;
+
+       *state = AMD_XGBE_RX_XNP;
+
+       msg = XNP_MCF_NULL_MESSAGE;
+       msg |= XNP_MP_FORMATTED;
+
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP + 2, 0);
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP + 1, 0);
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP, msg);
+
+       return AMD_XGBE_AN_EVENT;
+}
+
+static enum amd_xgbe_phy_an amd_xgbe_an_rx_bpa(struct phy_device *phydev,
+                                              enum amd_xgbe_phy_rx *state)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       unsigned int link_support;
+       int ret, ad_reg, lp_reg;
+
+       /* Read Base Ability register 2 first */
+       ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA + 1);
+       if (ret < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       /* Check for a supported mode, otherwise restart in a different one */
+       link_support = (priv->mode == AMD_XGBE_MODE_KR) ? 0x80 : 0x20;
+       if (!(ret & link_support))
+               return amd_xgbe_an_switch_mode(phydev);
+
+       /* Check Extended Next Page support */
+       ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
+       if (ad_reg < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       lp_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA);
+       if (lp_reg < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       return ((ad_reg & XNP_NP_EXCHANGE) || (lp_reg & XNP_NP_EXCHANGE)) ?
+              amd_xgbe_an_tx_xnp(phydev, state) :
+              amd_xgbe_an_tx_training(phydev, state);
+}
+
+static enum amd_xgbe_phy_an amd_xgbe_an_rx_xnp(struct phy_device *phydev,
+                                              enum amd_xgbe_phy_rx *state)
+{
+       int ad_reg, lp_reg;
+
+       /* Check Extended Next Page support */
+       ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
+       if (ad_reg < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       lp_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA);
+       if (lp_reg < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       return ((ad_reg & XNP_NP_EXCHANGE) || (lp_reg & XNP_NP_EXCHANGE)) ?
+              amd_xgbe_an_tx_xnp(phydev, state) :
+              amd_xgbe_an_tx_training(phydev, state);
+}
+
+static enum amd_xgbe_phy_an amd_xgbe_an_start(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       int ret;
+
+       /* Be sure we aren't looping trying to negotiate */
+       if (priv->mode == AMD_XGBE_MODE_KR) {
+               if (priv->kr_state != AMD_XGBE_RX_READY)
+                       return AMD_XGBE_AN_NO_LINK;
+               priv->kr_state = AMD_XGBE_RX_BPA;
+       } else {
+               if (priv->kx_state != AMD_XGBE_RX_READY)
+                       return AMD_XGBE_AN_NO_LINK;
+               priv->kx_state = AMD_XGBE_RX_BPA;
+       }
+
+       /* Set up Advertisement register 3 first */
+       ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2);
+       if (ret < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       if (phydev->supported & SUPPORTED_10000baseR_FEC)
+               ret |= 0xc000;
+       else
+               ret &= ~0xc000;
+
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2, ret);
+
+       /* Set up Advertisement register 2 next */
+       ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1);
+       if (ret < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       if (phydev->supported & SUPPORTED_10000baseKR_Full)
+               ret |= 0x80;
+       else
+               ret &= ~0x80;
+
+       if (phydev->supported & SUPPORTED_1000baseKX_Full)
+               ret |= 0x20;
+       else
+               ret &= ~0x20;
+
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1, ret);
+
+       /* Set up Advertisement register 1 last */
+       ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
+       if (ret < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       if (phydev->supported & SUPPORTED_Pause)
+               ret |= 0x400;
+       else
+               ret &= ~0x400;
+
+       if (phydev->supported & SUPPORTED_Asym_Pause)
+               ret |= 0x800;
+       else
+               ret &= ~0x800;
+
+       /* We don't intend to perform XNP */
+       ret &= ~XNP_NP_EXCHANGE;
+
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE, ret);
+
+       /* Enable and start auto-negotiation */
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0);
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
+       if (ret < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       ret |= MDIO_AN_CTRL1_ENABLE;
+       ret |= MDIO_AN_CTRL1_RESTART;
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, ret);
+
+       return AMD_XGBE_AN_EVENT;
+}
+
+static enum amd_xgbe_phy_an amd_xgbe_an_event(struct phy_device *phydev)
+{
+       enum amd_xgbe_phy_an new_state;
+       int ret;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT);
+       if (ret < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       new_state = AMD_XGBE_AN_EVENT;
+       if (ret & XGBE_AN_PG_RCV)
+               new_state = AMD_XGBE_AN_PAGE_RECEIVED;
+       else if (ret & XGBE_AN_INC_LINK)
+               new_state = AMD_XGBE_AN_INCOMPAT_LINK;
+       else if (ret & XGBE_AN_INT_CMPLT)
+               new_state = AMD_XGBE_AN_COMPLETE;
+
+       if (new_state != AMD_XGBE_AN_EVENT)
+               phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0);
+
+       return new_state;
+}
+
+static enum amd_xgbe_phy_an amd_xgbe_an_page_received(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       enum amd_xgbe_phy_rx *state;
+       int ret;
+
+       state = (priv->mode == AMD_XGBE_MODE_KR) ? &priv->kr_state
+                                                : &priv->kx_state;
+
+       switch (*state) {
+       case AMD_XGBE_RX_BPA:
+               ret = amd_xgbe_an_rx_bpa(phydev, state);
+               break;
+
+       case AMD_XGBE_RX_XNP:
+               ret = amd_xgbe_an_rx_xnp(phydev, state);
+               break;
+
+       default:
+               ret = AMD_XGBE_AN_ERROR;
+       }
+
+       return ret;
+}
+
+static enum amd_xgbe_phy_an amd_xgbe_an_incompat_link(struct phy_device *phydev)
+{
+       return amd_xgbe_an_switch_mode(phydev);
+}
+
+static void amd_xgbe_an_state_machine(struct work_struct *work)
+{
+       struct amd_xgbe_phy_priv *priv = container_of(work,
+                                                     struct amd_xgbe_phy_priv,
+                                                     an_work);
+       struct phy_device *phydev = priv->phydev;
+       enum amd_xgbe_phy_an cur_state;
+       int sleep;
+
+       while (1) {
+               mutex_lock(&priv->an_mutex);
+
+               cur_state = priv->an_state;
+
+               switch (priv->an_state) {
+               case AMD_XGBE_AN_START:
+                       priv->an_state = amd_xgbe_an_start(phydev);
+                       break;
+
+               case AMD_XGBE_AN_EVENT:
+                       priv->an_state = amd_xgbe_an_event(phydev);
+                       break;
+
+               case AMD_XGBE_AN_PAGE_RECEIVED:
+                       priv->an_state = amd_xgbe_an_page_received(phydev);
+                       break;
+
+               case AMD_XGBE_AN_INCOMPAT_LINK:
+                       priv->an_state = amd_xgbe_an_incompat_link(phydev);
+                       break;
+
+               case AMD_XGBE_AN_COMPLETE:
+               case AMD_XGBE_AN_NO_LINK:
+               case AMD_XGBE_AN_EXIT:
+                       goto exit_unlock;
+
+               default:
+                       priv->an_state = AMD_XGBE_AN_ERROR;
+               }
+
+               if (priv->an_state == AMD_XGBE_AN_ERROR) {
+                       netdev_err(phydev->attached_dev,
+                                  "error during auto-negotiation, state=%u\n",
+                                  cur_state);
+                       goto exit_unlock;
+               }
+
+               sleep = (priv->an_state == AMD_XGBE_AN_EVENT) ? 1 : 0;
+
+               mutex_unlock(&priv->an_mutex);
+
+               if (sleep)
+                       usleep_range(20, 50);
+       }
+
+exit_unlock:
+       priv->an_result = priv->an_state;
+       priv->an_state = AMD_XGBE_AN_READY;
+
+       mutex_unlock(&priv->an_mutex);
+}
+
+static int amd_xgbe_phy_soft_reset(struct phy_device *phydev)
+{
+       int count, ret;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
+       if (ret < 0)
+               return ret;
+
+       ret |= MDIO_CTRL1_RESET;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
+
+       count = 50;
+       do {
+               msleep(20);
+               ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
+               if (ret < 0)
+                       return ret;
+       } while ((ret & MDIO_CTRL1_RESET) && --count);
+
+       if (ret & MDIO_CTRL1_RESET)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int amd_xgbe_phy_config_init(struct phy_device *phydev)
+{
+       /* Initialize supported features */
+       phydev->supported = SUPPORTED_Autoneg;
+       phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+       phydev->supported |= SUPPORTED_Backplane;
+       phydev->supported |= SUPPORTED_1000baseKX_Full |
+                            SUPPORTED_2500baseX_Full;
+       phydev->supported |= SUPPORTED_10000baseKR_Full |
+                            SUPPORTED_10000baseR_FEC;
+       phydev->advertising = phydev->supported;
+
+       /* Turn off and clear interrupts */
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INTMASK, 0);
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0);
+
+       return 0;
+}
+
+static int amd_xgbe_phy_setup_forced(struct phy_device *phydev)
+{
+       int ret;
+
+       /* Disable auto-negotiation */
+       ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
+       if (ret < 0)
+               return ret;
+
+       ret &= ~MDIO_AN_CTRL1_ENABLE;
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, ret);
+
+       /* Validate/Set specified speed */
+       switch (phydev->speed) {
+       case SPEED_10000:
+               ret = amd_xgbe_phy_xgmii_mode(phydev);
+               break;
+
+       case SPEED_2500:
+               ret = amd_xgbe_phy_gmii_2500_mode(phydev);
+               break;
+
+       case SPEED_1000:
+               ret = amd_xgbe_phy_gmii_mode(phydev);
+               break;
+
+       default:
+               ret = -EINVAL;
+       }
+
+       if (ret < 0)
+               return ret;
+
+       /* Validate duplex mode */
+       if (phydev->duplex != DUPLEX_FULL)
+               return -EINVAL;
+
+       phydev->pause = 0;
+       phydev->asym_pause = 0;
+
+       return 0;
+}
+
+static int amd_xgbe_phy_config_aneg(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       u32 mmd_mask = phydev->c45_ids.devices_in_package;
+       int ret;
+
+       if (phydev->autoneg != AUTONEG_ENABLE)
+               return amd_xgbe_phy_setup_forced(phydev);
+
+       /* Make sure we have the AN MMD present */
+       if (!(mmd_mask & MDIO_DEVS_AN))
+               return -EINVAL;
+
+       /* Get the current speed mode */
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
+       if (ret < 0)
+               return ret;
+
+       /* Start/Restart the auto-negotiation state machine */
+       mutex_lock(&priv->an_mutex);
+       priv->an_result = AMD_XGBE_AN_READY;
+       priv->an_state = AMD_XGBE_AN_START;
+       priv->kr_state = AMD_XGBE_RX_READY;
+       priv->kx_state = AMD_XGBE_RX_READY;
+       mutex_unlock(&priv->an_mutex);
+
+       queue_work(priv->an_workqueue, &priv->an_work);
+
+       return 0;
+}
+
+static int amd_xgbe_phy_aneg_done(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       enum amd_xgbe_phy_an state;
+
+       mutex_lock(&priv->an_mutex);
+       state = priv->an_result;
+       mutex_unlock(&priv->an_mutex);
+
+       return (state == AMD_XGBE_AN_COMPLETE);
+}
+
+static int amd_xgbe_phy_update_link(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       enum amd_xgbe_phy_an state;
+       unsigned int check_again, autoneg;
+       int ret;
+
+       /* If we're doing auto-negotiation don't report link down */
+       mutex_lock(&priv->an_mutex);
+       state = priv->an_state;
+       mutex_unlock(&priv->an_mutex);
+
+       if (state != AMD_XGBE_AN_READY) {
+               phydev->link = 1;
+               return 0;
+       }
+
+       /* Since the device can be in the wrong mode when a link is
+        * (re-)established (cable connected after the interface is
+        * up, etc.), the link status may report no link. If there
+        * is no link, try switching modes and checking the status
+        * again.
+        */
+       check_again = 1;
+again:
+       /* Link status is latched low, so read once to clear
+        * and then read again to get current state
+        */
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1);
+       if (ret < 0)
+               return ret;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1);
+       if (ret < 0)
+               return ret;
+
+       phydev->link = (ret & MDIO_STAT1_LSTATUS) ? 1 : 0;
+
+       if (!phydev->link) {
+               ret = amd_xgbe_phy_switch_mode(phydev);
+               if (check_again) {
+                       check_again = 0;
+                       goto again;
+               }
+       }
+
+       autoneg = (phydev->link && !priv->link) ? 1 : 0;
+       priv->link = phydev->link;
+       if (autoneg) {
+               /* Link is (back) up, re-start auto-negotiation */
+               ret = amd_xgbe_phy_config_aneg(phydev);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int amd_xgbe_phy_read_status(struct phy_device *phydev)
+{
+       u32 mmd_mask = phydev->c45_ids.devices_in_package;
+       int ret, mode, ad_ret, lp_ret;
+
+       ret = amd_xgbe_phy_update_link(phydev);
+       if (ret)
+               return ret;
+
+       mode = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
+       if (mode < 0)
+               return mode;
+       mode &= MDIO_PCS_CTRL2_TYPE;
+
+       if (phydev->autoneg == AUTONEG_ENABLE) {
+               if (!(mmd_mask & MDIO_DEVS_AN))
+                       return -EINVAL;
+
+               if (!amd_xgbe_phy_aneg_done(phydev))
+                       return 0;
+
+               /* Compare Advertisement and Link Partner register 1 */
+               ad_ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
+               if (ad_ret < 0)
+                       return ad_ret;
+               lp_ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA);
+               if (lp_ret < 0)
+                       return lp_ret;
+
+               ad_ret &= lp_ret;
+               phydev->pause = (ad_ret & 0x400) ? 1 : 0;
+               phydev->asym_pause = (ad_ret & 0x800) ? 1 : 0;
+
+               /* Compare Advertisement and Link Partner register 2 */
+               ad_ret = phy_read_mmd(phydev, MDIO_MMD_AN,
+                                     MDIO_AN_ADVERTISE + 1);
+               if (ad_ret < 0)
+                       return ad_ret;
+               lp_ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA + 1);
+               if (lp_ret < 0)
+                       return lp_ret;
+
+               ad_ret &= lp_ret;
+               if (ad_ret & 0x80) {
+                       phydev->speed = SPEED_10000;
+                       if (mode != MDIO_PCS_CTRL2_10GBR) {
+                               ret = amd_xgbe_phy_xgmii_mode(phydev);
+                               if (ret < 0)
+                                       return ret;
+                       }
+               } else {
+                       phydev->speed = SPEED_1000;
+                       if (mode == MDIO_PCS_CTRL2_10GBR) {
+                               ret = amd_xgbe_phy_gmii_mode(phydev);
+                               if (ret < 0)
+                                       return ret;
+                       }
+               }
+
+               phydev->duplex = DUPLEX_FULL;
+       } else {
+               phydev->speed = (mode == MDIO_PCS_CTRL2_10GBR) ? SPEED_10000
+                                                              : SPEED_1000;
+               phydev->duplex = DUPLEX_FULL;
+               phydev->pause = 0;
+               phydev->asym_pause = 0;
+       }
+
+       return 0;
+}
+
+static int amd_xgbe_phy_suspend(struct phy_device *phydev)
+{
+       int ret;
+
+       mutex_lock(&phydev->lock);
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
+       if (ret < 0)
+               goto unlock;
+
+       ret |= MDIO_CTRL1_LPOWER;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
+
+       ret = 0;
+
+unlock:
+       mutex_unlock(&phydev->lock);
+
+       return ret;
+}
+
+static int amd_xgbe_phy_resume(struct phy_device *phydev)
+{
+       int ret;
+
+       mutex_lock(&phydev->lock);
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
+       if (ret < 0)
+               goto unlock;
+
+       ret &= ~MDIO_CTRL1_LPOWER;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
+
+       ret = 0;
+
+unlock:
+       mutex_unlock(&phydev->lock);
+
+       return ret;
+}
+
+static int amd_xgbe_phy_probe(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv;
+       struct platform_device *pdev;
+       struct device *dev;
+       char *wq_name;
+       int ret;
+
+       if (!phydev->dev.of_node)
+               return -EINVAL;
+
+       pdev = of_find_device_by_node(phydev->dev.of_node);
+       if (!pdev)
+               return -EINVAL;
+       dev = &pdev->dev;
+
+       wq_name = kasprintf(GFP_KERNEL, "%s-amd-xgbe-phy", phydev->bus->name);
+       if (!wq_name) {
+               ret = -ENOMEM;
+               goto err_pdev;
+       }
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               ret = -ENOMEM;
+               goto err_name;
+       }
+
+       priv->pdev = pdev;
+       priv->dev = dev;
+       priv->phydev = phydev;
+
+       /* Get the device mmio areas */
+       priv->rxtx_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       priv->rxtx_regs = devm_ioremap_resource(dev, priv->rxtx_res);
+       if (IS_ERR(priv->rxtx_regs)) {
+               dev_err(dev, "rxtx ioremap failed\n");
+               ret = PTR_ERR(priv->rxtx_regs);
+               goto err_priv;
+       }
+
+       priv->sir0_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       priv->sir0_regs = devm_ioremap_resource(dev, priv->sir0_res);
+       if (IS_ERR(priv->sir0_regs)) {
+               dev_err(dev, "sir0 ioremap failed\n");
+               ret = PTR_ERR(priv->sir0_regs);
+               goto err_rxtx;
+       }
+
+       priv->sir1_res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+       priv->sir1_regs = devm_ioremap_resource(dev, priv->sir1_res);
+       if (IS_ERR(priv->sir1_regs)) {
+               dev_err(dev, "sir1 ioremap failed\n");
+               ret = PTR_ERR(priv->sir1_regs);
+               goto err_sir0;
+       }
+
+       priv->link = 1;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
+       if (ret < 0)
+               goto err_sir1;
+       if ((ret & MDIO_PCS_CTRL2_TYPE) == MDIO_PCS_CTRL2_10GBR)
+               priv->mode = AMD_XGBE_MODE_KR;
+       else
+               priv->mode = AMD_XGBE_MODE_KX;
+
+       mutex_init(&priv->an_mutex);
+       INIT_WORK(&priv->an_work, amd_xgbe_an_state_machine);
+       priv->an_workqueue = create_singlethread_workqueue(wq_name);
+       if (!priv->an_workqueue) {
+               ret = -ENOMEM;
+               goto err_sir1;
+       }
+
+       phydev->priv = priv;
+
+       kfree(wq_name);
+       of_dev_put(pdev);
+
+       return 0;
+
+err_sir1:
+       devm_iounmap(dev, priv->sir1_regs);
+       devm_release_mem_region(dev, priv->sir1_res->start,
+                               resource_size(priv->sir1_res));
+
+err_sir0:
+       devm_iounmap(dev, priv->sir0_regs);
+       devm_release_mem_region(dev, priv->sir0_res->start,
+                               resource_size(priv->sir0_res));
+
+err_rxtx:
+       devm_iounmap(dev, priv->rxtx_regs);
+       devm_release_mem_region(dev, priv->rxtx_res->start,
+                               resource_size(priv->rxtx_res));
+
+err_priv:
+       devm_kfree(dev, priv);
+
+err_name:
+       kfree(wq_name);
+
+err_pdev:
+       of_dev_put(pdev);
+
+       return ret;
+}
+
+static void amd_xgbe_phy_remove(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       struct device *dev = priv->dev;
+
+       /* Stop any in process auto-negotiation */
+       mutex_lock(&priv->an_mutex);
+       priv->an_state = AMD_XGBE_AN_EXIT;
+       mutex_unlock(&priv->an_mutex);
+
+       flush_workqueue(priv->an_workqueue);
+       destroy_workqueue(priv->an_workqueue);
+
+       /* Release resources */
+       devm_iounmap(dev, priv->sir1_regs);
+       devm_release_mem_region(dev, priv->sir1_res->start,
+                               resource_size(priv->sir1_res));
+
+       devm_iounmap(dev, priv->sir0_regs);
+       devm_release_mem_region(dev, priv->sir0_res->start,
+                               resource_size(priv->sir0_res));
+
+       devm_iounmap(dev, priv->rxtx_regs);
+       devm_release_mem_region(dev, priv->rxtx_res->start,
+                               resource_size(priv->rxtx_res));
+
+       devm_kfree(dev, priv);
+}
+
+static int amd_xgbe_match_phy_device(struct phy_device *phydev)
+{
+       return phydev->c45_ids.device_ids[MDIO_MMD_PCS] == XGBE_PHY_ID;
+}
+
+static struct phy_driver amd_xgbe_phy_driver[] = {
+       {
+               .phy_id                 = XGBE_PHY_ID,
+               .phy_id_mask            = XGBE_PHY_MASK,
+               .name                   = "AMD XGBE PHY",
+               .features               = 0,
+               .probe                  = amd_xgbe_phy_probe,
+               .remove                 = amd_xgbe_phy_remove,
+               .soft_reset             = amd_xgbe_phy_soft_reset,
+               .config_init            = amd_xgbe_phy_config_init,
+               .suspend                = amd_xgbe_phy_suspend,
+               .resume                 = amd_xgbe_phy_resume,
+               .config_aneg            = amd_xgbe_phy_config_aneg,
+               .aneg_done              = amd_xgbe_phy_aneg_done,
+               .read_status            = amd_xgbe_phy_read_status,
+               .match_phy_device       = amd_xgbe_match_phy_device,
+               .driver                 = {
+                       .owner = THIS_MODULE,
+               },
+       },
+};
+
+static int __init amd_xgbe_phy_init(void)
+{
+       return phy_drivers_register(amd_xgbe_phy_driver,
+                                   ARRAY_SIZE(amd_xgbe_phy_driver));
+}
+
+static void __exit amd_xgbe_phy_exit(void)
+{
+       phy_drivers_unregister(amd_xgbe_phy_driver,
+                              ARRAY_SIZE(amd_xgbe_phy_driver));
+}
+
+module_init(amd_xgbe_phy_init);
+module_exit(amd_xgbe_phy_exit);
+
+static struct mdio_device_id __maybe_unused amd_xgbe_phy_ids[] = {
+       { XGBE_PHY_ID, XGBE_PHY_MASK },
+       { }
+};
+MODULE_DEVICE_TABLE(mdio, amd_xgbe_phy_ids);
index 643464d5a727b3b937fbe803072861de15a1414f..6c622aedbae111842b0e8ec86aede54653b84ec6 100644 (file)
@@ -144,41 +144,11 @@ static int at803x_resume(struct phy_device *phydev)
 
 static int at803x_config_init(struct phy_device *phydev)
 {
-       int val;
        int ret;
-       u32 features;
-
-       features = SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI |
-                  SUPPORTED_FIBRE | SUPPORTED_BNC;
-
-       val = phy_read(phydev, MII_BMSR);
-       if (val < 0)
-               return val;
-
-       if (val & BMSR_ANEGCAPABLE)
-               features |= SUPPORTED_Autoneg;
-       if (val & BMSR_100FULL)
-               features |= SUPPORTED_100baseT_Full;
-       if (val & BMSR_100HALF)
-               features |= SUPPORTED_100baseT_Half;
-       if (val & BMSR_10FULL)
-               features |= SUPPORTED_10baseT_Full;
-       if (val & BMSR_10HALF)
-               features |= SUPPORTED_10baseT_Half;
-
-       if (val & BMSR_ESTATEN) {
-               val = phy_read(phydev, MII_ESTATUS);
-               if (val < 0)
-                       return val;
-
-               if (val & ESTATUS_1000_TFULL)
-                       features |= SUPPORTED_1000baseT_Full;
-               if (val & ESTATUS_1000_THALF)
-                       features |= SUPPORTED_1000baseT_Half;
-       }
 
-       phydev->supported = features;
-       phydev->advertising = features;
+       ret = genphy_config_init(phydev);
+       if (ret < 0)
+               return ret;
 
        if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
                ret = phy_write(phydev, AT803X_DEBUG_ADDR,
@@ -283,8 +253,7 @@ static int __init atheros_init(void)
 
 static void __exit atheros_exit(void)
 {
-       return phy_drivers_unregister(at803x_driver,
-                                     ARRAY_SIZE(at803x_driver));
+       phy_drivers_unregister(at803x_driver, ARRAY_SIZE(at803x_driver));
 }
 
 module_init(atheros_init);
index ba55adfc7aaef00b7cfee22fe0fd2ff8eb67ffe8..d60d875cb4450ab6cf72114c35b4c816e2e031fd 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/phy_fixed.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/of.h>
 
 #define MII_REGS_NUM 29
 
@@ -31,7 +32,7 @@ struct fixed_mdio_bus {
 };
 
 struct fixed_phy {
-       int id;
+       int addr;
        u16 regs[MII_REGS_NUM];
        struct phy_device *phydev;
        struct fixed_phy_status status;
@@ -104,8 +105,8 @@ static int fixed_phy_update_regs(struct fixed_phy *fp)
        if (fp->status.asym_pause)
                lpa |= LPA_PAUSE_ASYM;
 
-       fp->regs[MII_PHYSID1] = fp->id >> 16;
-       fp->regs[MII_PHYSID2] = fp->id;
+       fp->regs[MII_PHYSID1] = 0;
+       fp->regs[MII_PHYSID2] = 0;
 
        fp->regs[MII_BMSR] = bmsr;
        fp->regs[MII_BMCR] = bmcr;
@@ -115,7 +116,7 @@ static int fixed_phy_update_regs(struct fixed_phy *fp)
        return 0;
 }
 
-static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num)
+static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
 {
        struct fixed_mdio_bus *fmb = bus->priv;
        struct fixed_phy *fp;
@@ -124,7 +125,7 @@ static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num)
                return -1;
 
        list_for_each_entry(fp, &fmb->phys, node) {
-               if (fp->id == phy_id) {
+               if (fp->addr == phy_addr) {
                        /* Issue callback if user registered it. */
                        if (fp->link_update) {
                                fp->link_update(fp->phydev->attached_dev,
@@ -138,7 +139,7 @@ static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num)
        return 0xFFFF;
 }
 
-static int fixed_mdio_write(struct mii_bus *bus, int phy_id, int reg_num,
+static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num,
                            u16 val)
 {
        return 0;
@@ -160,7 +161,7 @@ int fixed_phy_set_link_update(struct phy_device *phydev,
                return -EINVAL;
 
        list_for_each_entry(fp, &fmb->phys, node) {
-               if (fp->id == phydev->phy_id) {
+               if (fp->addr == phydev->addr) {
                        fp->link_update = link_update;
                        fp->phydev = phydev;
                        return 0;
@@ -171,7 +172,7 @@ int fixed_phy_set_link_update(struct phy_device *phydev,
 }
 EXPORT_SYMBOL_GPL(fixed_phy_set_link_update);
 
-int fixed_phy_add(unsigned int irq, int phy_id,
+int fixed_phy_add(unsigned int irq, int phy_addr,
                  struct fixed_phy_status *status)
 {
        int ret;
@@ -184,9 +185,9 @@ int fixed_phy_add(unsigned int irq, int phy_id,
 
        memset(fp->regs, 0xFF,  sizeof(fp->regs[0]) * MII_REGS_NUM);
 
-       fmb->irqs[phy_id] = irq;
+       fmb->irqs[phy_addr] = irq;
 
-       fp->id = phy_id;
+       fp->addr = phy_addr;
        fp->status = *status;
 
        ret = fixed_phy_update_regs(fp);
@@ -203,6 +204,66 @@ err_regs:
 }
 EXPORT_SYMBOL_GPL(fixed_phy_add);
 
+void fixed_phy_del(int phy_addr)
+{
+       struct fixed_mdio_bus *fmb = &platform_fmb;
+       struct fixed_phy *fp, *tmp;
+
+       list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
+               if (fp->addr == phy_addr) {
+                       list_del(&fp->node);
+                       kfree(fp);
+                       return;
+               }
+       }
+}
+EXPORT_SYMBOL_GPL(fixed_phy_del);
+
+static int phy_fixed_addr;
+static DEFINE_SPINLOCK(phy_fixed_addr_lock);
+
+int fixed_phy_register(unsigned int irq,
+                      struct fixed_phy_status *status,
+                      struct device_node *np)
+{
+       struct fixed_mdio_bus *fmb = &platform_fmb;
+       struct phy_device *phy;
+       int phy_addr;
+       int ret;
+
+       /* Get the next available PHY address, up to PHY_MAX_ADDR */
+       spin_lock(&phy_fixed_addr_lock);
+       if (phy_fixed_addr == PHY_MAX_ADDR) {
+               spin_unlock(&phy_fixed_addr_lock);
+               return -ENOSPC;
+       }
+       phy_addr = phy_fixed_addr++;
+       spin_unlock(&phy_fixed_addr_lock);
+
+       ret = fixed_phy_add(PHY_POLL, phy_addr, status);
+       if (ret < 0)
+               return ret;
+
+       phy = get_phy_device(fmb->mii_bus, phy_addr, false);
+       if (!phy || IS_ERR(phy)) {
+               fixed_phy_del(phy_addr);
+               return -EINVAL;
+       }
+
+       of_node_get(np);
+       phy->dev.of_node = np;
+
+       ret = phy_device_register(phy);
+       if (ret) {
+               phy_device_free(phy);
+               of_node_put(np);
+               fixed_phy_del(phy_addr);
+               return ret;
+       }
+
+       return 0;
+}
+
 static int __init fixed_mdio_bus_init(void)
 {
        struct fixed_mdio_bus *fmb = &platform_fmb;
index 76f54b32a120832f2ce212c129592a6f30ab83df..2e58aa54484c9ca4e3154e231a3af2766bc84933 100644 (file)
@@ -69,6 +69,73 @@ struct mii_bus *mdiobus_alloc_size(size_t size)
 }
 EXPORT_SYMBOL(mdiobus_alloc_size);
 
+static void _devm_mdiobus_free(struct device *dev, void *res)
+{
+       mdiobus_free(*(struct mii_bus **)res);
+}
+
+static int devm_mdiobus_match(struct device *dev, void *res, void *data)
+{
+       struct mii_bus **r = res;
+
+       if (WARN_ON(!r || !*r))
+               return 0;
+
+       return *r == data;
+}
+
+/**
+ * devm_mdiobus_alloc_size - Resource-managed mdiobus_alloc_size()
+ * @dev:               Device to allocate mii_bus for
+ * @sizeof_priv:       Space to allocate for private structure.
+ *
+ * Managed mdiobus_alloc_size. mii_bus allocated with this function is
+ * automatically freed on driver detach.
+ *
+ * If an mii_bus allocated with this function needs to be freed separately,
+ * devm_mdiobus_free() must be used.
+ *
+ * RETURNS:
+ * Pointer to allocated mii_bus on success, NULL on failure.
+ */
+struct mii_bus *devm_mdiobus_alloc_size(struct device *dev, int sizeof_priv)
+{
+       struct mii_bus **ptr, *bus;
+
+       ptr = devres_alloc(_devm_mdiobus_free, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return NULL;
+
+       /* use raw alloc_dr for kmalloc caller tracing */
+       bus = mdiobus_alloc_size(sizeof_priv);
+       if (bus) {
+               *ptr = bus;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return bus;
+}
+EXPORT_SYMBOL_GPL(devm_mdiobus_alloc_size);
+
+/**
+ * devm_mdiobus_free - Resource-managed mdiobus_free()
+ * @dev:               Device this mii_bus belongs to
+ * @bus:               the mii_bus associated with the device
+ *
+ * Free mii_bus allocated with devm_mdiobus_alloc_size().
+ */
+void devm_mdiobus_free(struct device *dev, struct mii_bus *bus)
+{
+       int rc;
+
+       rc = devres_release(dev, _devm_mdiobus_free,
+                           devm_mdiobus_match, bus);
+       WARN_ON(rc);
+}
+EXPORT_SYMBOL_GPL(devm_mdiobus_free);
+
 /**
  * mdiobus_release - mii_bus device release callback
  * @d: the target struct device that contains the mii_bus
@@ -233,6 +300,12 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)
        if (IS_ERR(phydev) || phydev == NULL)
                return phydev;
 
+       /*
+        * For DT, see if the auto-probed phy has a correspoding child
+        * in the bus node, and set the of_node pointer in this case.
+        */
+       of_mdiobus_link_phydev(bus, phydev);
+
        err = phy_device_register(phydev);
        if (err) {
                phy_device_free(phydev);
index d849684231c14919727cb66d5e6e37b14278943d..bc7c7d2f75f26e41ccd205eeaa6402eee25e0233 100644 (file)
@@ -283,6 +283,110 @@ static int ksz9021_config_init(struct phy_device *phydev)
        return 0;
 }
 
+#define MII_KSZ9031RN_MMD_CTRL_REG     0x0d
+#define MII_KSZ9031RN_MMD_REGDATA_REG  0x0e
+#define OP_DATA                                1
+#define KSZ9031_PS_TO_REG              60
+
+/* Extended registers */
+#define MII_KSZ9031RN_CONTROL_PAD_SKEW 4
+#define MII_KSZ9031RN_RX_DATA_PAD_SKEW 5
+#define MII_KSZ9031RN_TX_DATA_PAD_SKEW 6
+#define MII_KSZ9031RN_CLK_PAD_SKEW     8
+
+static int ksz9031_extended_write(struct phy_device *phydev,
+                                 u8 mode, u32 dev_addr, u32 regnum, u16 val)
+{
+       phy_write(phydev, MII_KSZ9031RN_MMD_CTRL_REG, dev_addr);
+       phy_write(phydev, MII_KSZ9031RN_MMD_REGDATA_REG, regnum);
+       phy_write(phydev, MII_KSZ9031RN_MMD_CTRL_REG, (mode << 14) | dev_addr);
+       return phy_write(phydev, MII_KSZ9031RN_MMD_REGDATA_REG, val);
+}
+
+static int ksz9031_extended_read(struct phy_device *phydev,
+                                u8 mode, u32 dev_addr, u32 regnum)
+{
+       phy_write(phydev, MII_KSZ9031RN_MMD_CTRL_REG, dev_addr);
+       phy_write(phydev, MII_KSZ9031RN_MMD_REGDATA_REG, regnum);
+       phy_write(phydev, MII_KSZ9031RN_MMD_CTRL_REG, (mode << 14) | dev_addr);
+       return phy_read(phydev, MII_KSZ9031RN_MMD_REGDATA_REG);
+}
+
+static int ksz9031_of_load_skew_values(struct phy_device *phydev,
+                                      struct device_node *of_node,
+                                      u16 reg, size_t field_sz,
+                                      char *field[], u8 numfields)
+{
+       int val[4] = {-1, -2, -3, -4};
+       int matches = 0;
+       u16 mask;
+       u16 maxval;
+       u16 newval;
+       int i;
+
+       for (i = 0; i < numfields; i++)
+               if (!of_property_read_u32(of_node, field[i], val + i))
+                       matches++;
+
+       if (!matches)
+               return 0;
+
+       if (matches < numfields)
+               newval = ksz9031_extended_read(phydev, OP_DATA, 2, reg);
+       else
+               newval = 0;
+
+       maxval = (field_sz == 4) ? 0xf : 0x1f;
+       for (i = 0; i < numfields; i++)
+               if (val[i] != -(i + 1)) {
+                       mask = 0xffff;
+                       mask ^= maxval << (field_sz * i);
+                       newval = (newval & mask) |
+                               (((val[i] / KSZ9031_PS_TO_REG) & maxval)
+                                       << (field_sz * i));
+               }
+
+       return ksz9031_extended_write(phydev, OP_DATA, 2, reg, newval);
+}
+
+static int ksz9031_config_init(struct phy_device *phydev)
+{
+       struct device *dev = &phydev->dev;
+       struct device_node *of_node = dev->of_node;
+       char *clk_skews[2] = {"rxc-skew-ps", "txc-skew-ps"};
+       char *rx_data_skews[4] = {
+               "rxd0-skew-ps", "rxd1-skew-ps",
+               "rxd2-skew-ps", "rxd3-skew-ps"
+       };
+       char *tx_data_skews[4] = {
+               "txd0-skew-ps", "txd1-skew-ps",
+               "txd2-skew-ps", "txd3-skew-ps"
+       };
+       char *control_skews[2] = {"txen-skew-ps", "rxdv-skew-ps"};
+
+       if (!of_node && dev->parent->of_node)
+               of_node = dev->parent->of_node;
+
+       if (of_node) {
+               ksz9031_of_load_skew_values(phydev, of_node,
+                               MII_KSZ9031RN_CLK_PAD_SKEW, 5,
+                               clk_skews, 2);
+
+               ksz9031_of_load_skew_values(phydev, of_node,
+                               MII_KSZ9031RN_CONTROL_PAD_SKEW, 4,
+                               control_skews, 2);
+
+               ksz9031_of_load_skew_values(phydev, of_node,
+                               MII_KSZ9031RN_RX_DATA_PAD_SKEW, 4,
+                               rx_data_skews, 4);
+
+               ksz9031_of_load_skew_values(phydev, of_node,
+                               MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4,
+                               tx_data_skews, 4);
+       }
+       return 0;
+}
+
 #define KSZ8873MLL_GLOBAL_CONTROL_4    0x06
 #define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX     (1 << 6)
 #define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED      (1 << 4)
@@ -469,7 +573,7 @@ static struct phy_driver ksphy_driver[] = {
        .features       = (PHY_GBIT_FEATURES | SUPPORTED_Pause
                                | SUPPORTED_Asym_Pause),
        .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
-       .config_init    = kszphy_config_init,
+       .config_init    = ksz9031_config_init,
        .config_aneg    = genphy_config_aneg,
        .read_status    = genphy_read_status,
        .ack_interrupt  = kszphy_ack_interrupt,
index 4987a1c6dc52e63220edb6fc94eea925a9fdf122..35d753d22f78b91d643548029df5b2e6eea64d49 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/mdio.h>
 #include <linux/io.h>
 #include <linux/uaccess.h>
+#include <linux/of.h>
 
 #include <asm/irq.h>
 
@@ -1067,14 +1068,11 @@ int genphy_soft_reset(struct phy_device *phydev)
 }
 EXPORT_SYMBOL(genphy_soft_reset);
 
-static int genphy_config_init(struct phy_device *phydev)
+int genphy_config_init(struct phy_device *phydev)
 {
        int val;
        u32 features;
 
-       /* For now, I'll claim that the generic driver supports
-        * all possible port types
-        */
        features = (SUPPORTED_TP | SUPPORTED_MII
                        | SUPPORTED_AUI | SUPPORTED_FIBRE |
                        SUPPORTED_BNC);
@@ -1107,8 +1105,8 @@ static int genphy_config_init(struct phy_device *phydev)
                        features |= SUPPORTED_1000baseT_Half;
        }
 
-       phydev->supported = features;
-       phydev->advertising = features;
+       phydev->supported &= features;
+       phydev->advertising &= features;
 
        return 0;
 }
@@ -1118,6 +1116,7 @@ static int gen10g_soft_reset(struct phy_device *phydev)
        /* Do nothing for now */
        return 0;
 }
+EXPORT_SYMBOL(genphy_config_init);
 
 static int gen10g_config_init(struct phy_device *phydev)
 {
@@ -1168,6 +1167,38 @@ static int gen10g_resume(struct phy_device *phydev)
        return 0;
 }
 
+static void of_set_phy_supported(struct phy_device *phydev)
+{
+       struct device_node *node = phydev->dev.of_node;
+       u32 max_speed;
+
+       if (!IS_ENABLED(CONFIG_OF_MDIO))
+               return;
+
+       if (!node)
+               return;
+
+       if (!of_property_read_u32(node, "max-speed", &max_speed)) {
+               /* The default values for phydev->supported are provided by the PHY
+                * driver "features" member, we want to reset to sane defaults fist
+                * before supporting higher speeds.
+                */
+               phydev->supported &= PHY_DEFAULT_FEATURES;
+
+               switch (max_speed) {
+               default:
+                       return;
+
+               case SPEED_1000:
+                       phydev->supported |= PHY_1000BT_FEATURES;
+               case SPEED_100:
+                       phydev->supported |= PHY_100BT_FEATURES;
+               case SPEED_10:
+                       phydev->supported |= PHY_10BT_FEATURES;
+               }
+       }
+}
+
 /**
  * phy_probe - probe and init a PHY device
  * @dev: device to probe and init
@@ -1202,7 +1233,8 @@ static int phy_probe(struct device *dev)
         * or both of these values
         */
        phydev->supported = phydrv->features;
-       phydev->advertising = phydrv->features;
+       of_set_phy_supported(phydev);
+       phydev->advertising = phydev->supported;
 
        /* Set the state to READY by default */
        phydev->state = PHY_READY;
@@ -1295,7 +1327,9 @@ static struct phy_driver genphy_driver[] = {
        .name           = "Generic PHY",
        .soft_reset     = genphy_soft_reset,
        .config_init    = genphy_config_init,
-       .features       = 0,
+       .features       = PHY_GBIT_FEATURES | SUPPORTED_MII |
+                         SUPPORTED_AUI | SUPPORTED_FIBRE |
+                         SUPPORTED_BNC,
        .config_aneg    = genphy_config_aneg,
        .aneg_done      = genphy_aneg_done,
        .read_status    = genphy_read_status,
index fa1d69a38ccf9cd374f0292d653e4c587b1ecb88..45483fdfbe063647c620f3b46db9772c0c17bd7a 100644 (file)
@@ -64,65 +64,51 @@ static int rtl8211e_config_intr(struct phy_device *phydev)
        return err;
 }
 
-/* RTL8201CP */
-static struct phy_driver rtl8201cp_driver = {
-       .phy_id         = 0x00008201,
-       .name           = "RTL8201CP Ethernet",
-       .phy_id_mask    = 0x0000ffff,
-       .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
-       .config_aneg    = &genphy_config_aneg,
-       .read_status    = &genphy_read_status,
-       .driver         = { .owner = THIS_MODULE,},
-};
-
-/* RTL8211B */
-static struct phy_driver rtl8211b_driver = {
-       .phy_id         = 0x001cc912,
-       .name           = "RTL8211B Gigabit Ethernet",
-       .phy_id_mask    = 0x001fffff,
-       .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
-       .config_aneg    = &genphy_config_aneg,
-       .read_status    = &genphy_read_status,
-       .ack_interrupt  = &rtl821x_ack_interrupt,
-       .config_intr    = &rtl8211b_config_intr,
-       .driver         = { .owner = THIS_MODULE,},
-};
-
-/* RTL8211E */
-static struct phy_driver rtl8211e_driver = {
-       .phy_id         = 0x001cc915,
-       .name           = "RTL8211E Gigabit Ethernet",
-       .phy_id_mask    = 0x001fffff,
-       .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
-       .config_aneg    = &genphy_config_aneg,
-       .read_status    = &genphy_read_status,
-       .ack_interrupt  = &rtl821x_ack_interrupt,
-       .config_intr    = &rtl8211e_config_intr,
-       .suspend        = genphy_suspend,
-       .resume         = genphy_resume,
-       .driver         = { .owner = THIS_MODULE,},
+static struct phy_driver realtek_drvs[] = {
+       {
+               .phy_id         = 0x00008201,
+               .name           = "RTL8201CP Ethernet",
+               .phy_id_mask    = 0x0000ffff,
+               .features       = PHY_BASIC_FEATURES,
+               .flags          = PHY_HAS_INTERRUPT,
+               .config_aneg    = &genphy_config_aneg,
+               .read_status    = &genphy_read_status,
+               .driver         = { .owner = THIS_MODULE,},
+       }, {
+               .phy_id         = 0x001cc912,
+               .name           = "RTL8211B Gigabit Ethernet",
+               .phy_id_mask    = 0x001fffff,
+               .features       = PHY_GBIT_FEATURES,
+               .flags          = PHY_HAS_INTERRUPT,
+               .config_aneg    = &genphy_config_aneg,
+               .read_status    = &genphy_read_status,
+               .ack_interrupt  = &rtl821x_ack_interrupt,
+               .config_intr    = &rtl8211b_config_intr,
+               .driver         = { .owner = THIS_MODULE,},
+       }, {
+               .phy_id         = 0x001cc915,
+               .name           = "RTL8211E Gigabit Ethernet",
+               .phy_id_mask    = 0x001fffff,
+               .features       = PHY_GBIT_FEATURES,
+               .flags          = PHY_HAS_INTERRUPT,
+               .config_aneg    = &genphy_config_aneg,
+               .read_status    = &genphy_read_status,
+               .ack_interrupt  = &rtl821x_ack_interrupt,
+               .config_intr    = &rtl8211e_config_intr,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
+               .driver         = { .owner = THIS_MODULE,},
+       },
 };
 
 static int __init realtek_init(void)
 {
-       int ret;
-
-       ret = phy_driver_register(&rtl8201cp_driver);
-       if (ret < 0)
-               return -ENODEV;
-       ret = phy_driver_register(&rtl8211b_driver);
-       if (ret < 0)
-               return -ENODEV;
-       return phy_driver_register(&rtl8211e_driver);
+       return phy_drivers_register(realtek_drvs, ARRAY_SIZE(realtek_drvs));
 }
 
 static void __exit realtek_exit(void)
 {
-       phy_driver_unregister(&rtl8211b_driver);
-       phy_driver_unregister(&rtl8211e_driver);
+       phy_drivers_unregister(realtek_drvs, ARRAY_SIZE(realtek_drvs));
 }
 
 module_init(realtek_init);
index 11f34813e23fb5423ccf90cf9d25e208ed2275f3..180c49479c42f9b4a19f070056b782923de5084c 100644 (file)
@@ -249,8 +249,7 @@ static int __init smsc_init(void)
 
 static void __exit smsc_exit(void)
 {
-       return phy_drivers_unregister(smsc_phy_driver,
-               ARRAY_SIZE(smsc_phy_driver));
+       phy_drivers_unregister(smsc_phy_driver, ARRAY_SIZE(smsc_phy_driver));
 }
 
 MODULE_DESCRIPTION("SMSC PHY driver");
index 14372c65a7e8209b5f97da6416ef80d1f41a522c..5dc0935da99c52a07ba7c09da27503b8ee0b2695 100644 (file)
@@ -319,8 +319,7 @@ static int __init vsc82xx_init(void)
 
 static void __exit vsc82xx_exit(void)
 {
-       return phy_drivers_unregister(vsc82xx_driver,
-               ARRAY_SIZE(vsc82xx_driver));
+       phy_drivers_unregister(vsc82xx_driver, ARRAY_SIZE(vsc82xx_driver));
 }
 
 module_init(vsc82xx_init);
index e3923ebb693fccc276db45de7c7d9551ee6bc208..91d6c1272fcf0ae4a655fa74ca6b91fb578a6108 100644 (file)
@@ -757,7 +757,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
                err = get_filter(argp, &code);
                if (err >= 0) {
-                       struct sock_fprog fprog = {
+                       struct sock_fprog_kern fprog = {
                                .len = err,
                                .filter = code,
                        };
@@ -778,7 +778,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
                err = get_filter(argp, &code);
                if (err >= 0) {
-                       struct sock_fprog fprog = {
+                       struct sock_fprog_kern fprog = {
                                .len = err,
                                .filter = code,
                        };
index 01805319e1e0335a70f86109e058cac44d669ee0..1aff970be33ec88ec8ff6781467155f68cf5b3b4 100644 (file)
@@ -281,7 +281,7 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
        nf_reset(skb);
 
        skb->ip_summed = CHECKSUM_NONE;
-       ip_select_ident(skb, &rt->dst, NULL);
+       ip_select_ident(skb, NULL);
        ip_send_check(iph);
 
        ip_local_out(skb);
index a8497183ff8b079985b7930fd5eb26dc9fb44813..dac7a0d9bb46e5d9d2385250f990a2a0acf5996c 100644 (file)
@@ -494,7 +494,7 @@ static int rionet_setup_netdev(struct rio_mport *mport, struct net_device *ndev)
        ndev->mtu = RIO_MAX_MSG_SIZE - 14;
        ndev->features = NETIF_F_LLTX;
        SET_NETDEV_DEV(ndev, &mport->dev);
-       SET_ETHTOOL_OPS(ndev, &rionet_ethtool_ops);
+       ndev->ethtool_ops = &rionet_ethtool_ops;
 
        spin_lock_init(&rnet->lock);
        spin_lock_init(&rnet->tx_lock);
index ce4989be86d96940401cbae318a75931a0d0b60c..b4958c7ffa8415a84921995ce6123081e6596348 100644 (file)
@@ -968,7 +968,7 @@ static void team_port_disable(struct team *team,
 static void __team_compute_features(struct team *team)
 {
        struct team_port *port;
-       u32 vlan_features = TEAM_VLAN_FEATURES;
+       u32 vlan_features = TEAM_VLAN_FEATURES & NETIF_F_ALL_FOR_ALL;
        unsigned short max_hard_header_len = ETH_HLEN;
        unsigned int flags, dst_release_flag = IFF_XMIT_DST_RELEASE;
 
index dbde3412ee5eafdeb39cd238e8af4f19368917c6..a58dfebb5512326db7065a433c37676c99820407 100644 (file)
@@ -49,7 +49,7 @@ struct lb_port_mapping {
 struct lb_priv_ex {
        struct team *team;
        struct lb_port_mapping tx_hash_to_port_mapping[LB_TX_HASHTABLE_SIZE];
-       struct sock_fprog *orig_fprog;
+       struct sock_fprog_kern *orig_fprog;
        struct {
                unsigned int refresh_interval; /* in tenths of second */
                struct delayed_work refresh_dw;
@@ -241,15 +241,15 @@ static int lb_bpf_func_get(struct team *team, struct team_gsetter_ctx *ctx)
        return 0;
 }
 
-static int __fprog_create(struct sock_fprog **pfprog, u32 data_len,
+static int __fprog_create(struct sock_fprog_kern **pfprog, u32 data_len,
                          const void *data)
 {
-       struct sock_fprog *fprog;
+       struct sock_fprog_kern *fprog;
        struct sock_filter *filter = (struct sock_filter *) data;
 
        if (data_len % sizeof(struct sock_filter))
                return -EINVAL;
-       fprog = kmalloc(sizeof(struct sock_fprog), GFP_KERNEL);
+       fprog = kmalloc(sizeof(*fprog), GFP_KERNEL);
        if (!fprog)
                return -ENOMEM;
        fprog->filter = kmemdup(filter, data_len, GFP_KERNEL);
@@ -262,7 +262,7 @@ static int __fprog_create(struct sock_fprog **pfprog, u32 data_len,
        return 0;
 }
 
-static void __fprog_destroy(struct sock_fprog *fprog)
+static void __fprog_destroy(struct sock_fprog_kern *fprog)
 {
        kfree(fprog->filter);
        kfree(fprog);
@@ -273,7 +273,7 @@ static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx)
        struct lb_priv *lb_priv = get_lb_priv(team);
        struct sk_filter *fp = NULL;
        struct sk_filter *orig_fp;
-       struct sock_fprog *fprog = NULL;
+       struct sock_fprog_kern *fprog = NULL;
        int err;
 
        if (ctx->data.bin_val.len) {
index ee328ba101e72a9e3f150d8c24068182be86abc5..98bad1fb1bfb1ce66ea4219c2e767256f05d6cbb 100644 (file)
@@ -498,12 +498,12 @@ static void tun_detach_all(struct net_device *dev)
        for (i = 0; i < n; i++) {
                tfile = rtnl_dereference(tun->tfiles[i]);
                BUG_ON(!tfile);
-               wake_up_all(&tfile->wq.wait);
+               tfile->socket.sk->sk_data_ready(tfile->socket.sk);
                RCU_INIT_POINTER(tfile->tun, NULL);
                --tun->numqueues;
        }
        list_for_each_entry(tfile, &tun->disabled, next) {
-               wake_up_all(&tfile->wq.wait);
+               tfile->socket.sk->sk_data_ready(tfile->socket.sk);
                RCU_INIT_POINTER(tfile->tun, NULL);
        }
        BUG_ON(tun->numqueues != 0);
@@ -807,8 +807,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
        /* Notify and wake up reader process */
        if (tfile->flags & TUN_FASYNC)
                kill_fasync(&tfile->fasync, SIGIO, POLL_IN);
-       wake_up_interruptible_poll(&tfile->wq.wait, POLLIN |
-                                  POLLRDNORM | POLLRDBAND);
+       tfile->socket.sk->sk_data_ready(tfile->socket.sk);
 
        rcu_read_unlock();
        return NETDEV_TX_OK;
@@ -965,7 +964,7 @@ static unsigned int tun_chr_poll(struct file *file, poll_table *wait)
 
        tun_debug(KERN_INFO, tun, "tun_chr_poll\n");
 
-       poll_wait(file, &tfile->wq.wait, wait);
+       poll_wait(file, sk_sleep(sk), wait);
 
        if (!skb_queue_empty(&sk->sk_receive_queue))
                mask |= POLLIN | POLLRDNORM;
@@ -1330,47 +1329,26 @@ done:
 static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile,
                           const struct iovec *iv, ssize_t len, int noblock)
 {
-       DECLARE_WAITQUEUE(wait, current);
        struct sk_buff *skb;
        ssize_t ret = 0;
+       int peeked, err, off = 0;
 
        tun_debug(KERN_INFO, tun, "tun_do_read\n");
 
-       if (unlikely(!noblock))
-               add_wait_queue(&tfile->wq.wait, &wait);
-       while (len) {
-               if (unlikely(!noblock))
-                       current->state = TASK_INTERRUPTIBLE;
+       if (!len)
+               return ret;
 
-               /* Read frames from the queue */
-               if (!(skb = skb_dequeue(&tfile->socket.sk->sk_receive_queue))) {
-                       if (noblock) {
-                               ret = -EAGAIN;
-                               break;
-                       }
-                       if (signal_pending(current)) {
-                               ret = -ERESTARTSYS;
-                               break;
-                       }
-                       if (tun->dev->reg_state != NETREG_REGISTERED) {
-                               ret = -EIO;
-                               break;
-                       }
-
-                       /* Nothing to read, let's sleep */
-                       schedule();
-                       continue;
-               }
+       if (tun->dev->reg_state != NETREG_REGISTERED)
+               return -EIO;
 
+       /* Read frames from queue */
+       skb = __skb_recv_datagram(tfile->socket.sk, noblock ? MSG_DONTWAIT : 0,
+                                 &peeked, &off, &err);
+       if (skb) {
                ret = tun_put_user(tun, tfile, skb, iv, len);
                kfree_skb(skb);
-               break;
-       }
-
-       if (unlikely(!noblock)) {
-               current->state = TASK_RUNNING;
-               remove_wait_queue(&tfile->wq.wait, &wait);
-       }
+       } else
+               ret = err;
 
        return ret;
 }
@@ -2199,8 +2177,8 @@ static int tun_chr_open(struct inode *inode, struct file * file)
        tfile->flags = 0;
        tfile->ifindex = 0;
 
-       rcu_assign_pointer(tfile->socket.wq, &tfile->wq);
        init_waitqueue_head(&tfile->wq.wait);
+       RCU_INIT_POINTER(tfile->socket.wq, &tfile->wq);
 
        tfile->socket.file = file;
        tfile->socket.ops = &tun_socket_ops;
index 630caf48f63aab7c2023d4cadb7044d23a2af86d..8cfc3bb0c6a672a288784ab0dd5f09597265c39d 100644 (file)
@@ -793,7 +793,7 @@ static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id
 
        netdev->netdev_ops = &catc_netdev_ops;
        netdev->watchdog_timeo = TX_TIMEOUT;
-       SET_ETHTOOL_OPS(netdev, &ops);
+       netdev->ethtool_ops = &ops;
 
        catc->usbdev = usbdev;
        catc->netdev = netdev;
index 2e025ddcef210bc888dd8a8ff81473a32c3dd154..5ee7a1dbc023833a5907a6db48695600bb728fd9 100644 (file)
 #include <net/ipv6.h>
 #include <net/addrconf.h>
 
+/* alternative VLAN for IP session 0 if not untagged */
+#define MBIM_IPS0_VID  4094
+
 /* driver specific data - must match cdc_ncm usage */
 struct cdc_mbim_state {
        struct cdc_ncm_ctx *ctx;
        atomic_t pmcount;
        struct usb_driver *subdriver;
-       struct usb_interface *control;
-       struct usb_interface *data;
+       unsigned long _unused;
+       unsigned long flags;
+};
+
+/* flags for the cdc_mbim_state.flags field */
+enum cdc_mbim_flags {
+       FLAG_IPS0_VLAN = 1 << 0,        /* IP session 0 is tagged  */
 };
 
 /* using a counter to merge subdriver requests with our own into a combined state */
@@ -62,16 +70,91 @@ static int cdc_mbim_wdm_manage_power(struct usb_interface *intf, int status)
        return cdc_mbim_manage_power(dev, status);
 }
 
+static int cdc_mbim_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid)
+{
+       struct usbnet *dev = netdev_priv(netdev);
+       struct cdc_mbim_state *info = (void *)&dev->data;
+
+       /* creation of this VLAN is a request to tag IP session 0 */
+       if (vid == MBIM_IPS0_VID)
+               info->flags |= FLAG_IPS0_VLAN;
+       else
+               if (vid >= 512) /* we don't map these to MBIM session */
+                       return -EINVAL;
+       return 0;
+}
+
+static int cdc_mbim_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid)
+{
+       struct usbnet *dev = netdev_priv(netdev);
+       struct cdc_mbim_state *info = (void *)&dev->data;
+
+       /* this is a request for an untagged IP session 0 */
+       if (vid == MBIM_IPS0_VID)
+               info->flags &= ~FLAG_IPS0_VLAN;
+       return 0;
+}
+
+static const struct net_device_ops cdc_mbim_netdev_ops = {
+       .ndo_open             = usbnet_open,
+       .ndo_stop             = usbnet_stop,
+       .ndo_start_xmit       = usbnet_start_xmit,
+       .ndo_tx_timeout       = usbnet_tx_timeout,
+       .ndo_change_mtu       = usbnet_change_mtu,
+       .ndo_set_mac_address  = eth_mac_addr,
+       .ndo_validate_addr    = eth_validate_addr,
+       .ndo_vlan_rx_add_vid  = cdc_mbim_rx_add_vid,
+       .ndo_vlan_rx_kill_vid = cdc_mbim_rx_kill_vid,
+};
+
+/* Change the control interface altsetting and update the .driver_info
+ * pointer if the matching entry after changing class codes points to
+ * a different struct
+ */
+static int cdc_mbim_set_ctrlalt(struct usbnet *dev, struct usb_interface *intf, u8 alt)
+{
+       struct usb_driver *driver = to_usb_driver(intf->dev.driver);
+       const struct usb_device_id *id;
+       struct driver_info *info;
+       int ret;
+
+       ret = usb_set_interface(dev->udev,
+                               intf->cur_altsetting->desc.bInterfaceNumber,
+                               alt);
+       if (ret)
+               return ret;
+
+       id = usb_match_id(intf, driver->id_table);
+       if (!id)
+               return -ENODEV;
+
+       info = (struct driver_info *)id->driver_info;
+       if (info != dev->driver_info) {
+               dev_dbg(&intf->dev, "driver_info updated to '%s'\n",
+                       info->description);
+               dev->driver_info = info;
+       }
+       return 0;
+}
 
 static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf)
 {
        struct cdc_ncm_ctx *ctx;
        struct usb_driver *subdriver = ERR_PTR(-ENODEV);
        int ret = -ENODEV;
-       u8 data_altsetting = cdc_ncm_select_altsetting(dev, intf);
+       u8 data_altsetting = 1;
        struct cdc_mbim_state *info = (void *)&dev->data;
 
-       /* Probably NCM, defer for cdc_ncm_bind */
+       /* should we change control altsetting on a NCM/MBIM function? */
+       if (cdc_ncm_select_altsetting(intf) == CDC_NCM_COMM_ALTSETTING_MBIM) {
+               data_altsetting = CDC_NCM_DATA_ALTSETTING_MBIM;
+               ret = cdc_mbim_set_ctrlalt(dev, intf, CDC_NCM_COMM_ALTSETTING_MBIM);
+               if (ret)
+                       goto err;
+               ret = -ENODEV;
+       }
+
+       /* we will hit this for NCM/MBIM functions if prefer_mbim is false */
        if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
                goto err;
 
@@ -101,7 +184,10 @@ static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf)
        dev->net->flags |= IFF_NOARP;
 
        /* no need to put the VLAN tci in the packet headers */
-       dev->net->features |= NETIF_F_HW_VLAN_CTAG_TX;
+       dev->net->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_FILTER;
+
+       /* monitor VLAN additions and removals */
+       dev->net->netdev_ops = &cdc_mbim_netdev_ops;
 err:
        return ret;
 }
@@ -164,12 +250,24 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb
                        skb_pull(skb, ETH_HLEN);
                }
 
+               /* Is IP session <0> tagged too? */
+               if (info->flags & FLAG_IPS0_VLAN) {
+                       /* drop all untagged packets */
+                       if (!tci)
+                               goto error;
+                       /* map MBIM_IPS0_VID to IPS<0> */
+                       if (tci == MBIM_IPS0_VID)
+                               tci = 0;
+               }
+
                /* mapping VLANs to MBIM sessions:
-                *   no tag     => IPS session <0>
+                *   no tag     => IPS session <0> if !FLAG_IPS0_VLAN
                 *   1 - 255    => IPS session <vlanid>
                 *   256 - 511  => DSS session <vlanid - 256>
-                *   512 - 4095 => unsupported, drop
+                *   512 - 4093 => unsupported, drop
+                *   4094       => IPS session <0> if FLAG_IPS0_VLAN
                 */
+
                switch (tci & 0x0f00) {
                case 0x0000: /* VLAN ID 0 - 255 */
                        if (!is_ip)
@@ -178,6 +276,8 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb
                        c[3] = tci;
                        break;
                case 0x0100: /* VLAN ID 256 - 511 */
+                       if (is_ip)
+                               goto error;
                        sign = cpu_to_le32(USB_CDC_MBIM_NDP16_DSS_SIGN);
                        c = (u8 *)&sign;
                        c[3] = tci;
@@ -223,8 +323,8 @@ static void do_neigh_solicit(struct usbnet *dev, u8 *buf, u16 tci)
        /* need to send the NA on the VLAN dev, if any */
        rcu_read_lock();
        if (tci) {
-               netdev = __vlan_find_dev_deep(dev->net, htons(ETH_P_8021Q),
-                                             tci);
+               netdev = __vlan_find_dev_deep_rcu(dev->net, htons(ETH_P_8021Q),
+                                                 tci);
                if (!netdev) {
                        rcu_read_unlock();
                        return;
@@ -268,7 +368,7 @@ static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_
        __be16 proto = htons(ETH_P_802_3);
        struct sk_buff *skb = NULL;
 
-       if (tci < 256) { /* IPS session? */
+       if (tci < 256 || tci == MBIM_IPS0_VID) { /* IPS session? */
                if (len < sizeof(struct iphdr))
                        goto err;
 
@@ -320,6 +420,7 @@ static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
        struct usb_cdc_ncm_dpe16 *dpe16;
        int ndpoffset;
        int loopcount = 50; /* arbitrary max preventing infinite loop */
+       u32 payload = 0;
        u8 *c;
        u16 tci;
 
@@ -338,6 +439,9 @@ next_ndp:
        case cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN):
                c = (u8 *)&ndp16->dwSignature;
                tci = c[3];
+               /* tag IPS<0> packets too if MBIM_IPS0_VID exists */
+               if (!tci && info->flags & FLAG_IPS0_VLAN)
+                       tci = MBIM_IPS0_VID;
                break;
        case cpu_to_le32(USB_CDC_MBIM_NDP16_DSS_SIGN):
                c = (u8 *)&ndp16->dwSignature;
@@ -379,6 +483,7 @@ next_ndp:
                        if (!skb)
                                goto error;
                        usbnet_skb_return(dev, skb);
+                       payload += len; /* count payload bytes in this NTB */
                }
        }
 err_ndp:
@@ -387,6 +492,10 @@ err_ndp:
        if (ndpoffset && loopcount--)
                goto next_ndp;
 
+       /* update stats */
+       ctx->rx_overhead += skb_in->len - payload;
+       ctx->rx_ntbs++;
+
        return 1;
 error:
        return 0;
index 9a2bd11943ebf391a5678c7ded07ee0bc7b50f5e..80a844e0ae0383303d8fa4a6d4147fc99337f268 100644 (file)
@@ -65,19 +65,384 @@ static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx);
 static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *hr_timer);
 static struct usb_driver cdc_ncm_driver;
 
-static int cdc_ncm_setup(struct usbnet *dev)
+struct cdc_ncm_stats {
+       char stat_string[ETH_GSTRING_LEN];
+       int sizeof_stat;
+       int stat_offset;
+};
+
+#define CDC_NCM_STAT(str, m) { \
+               .stat_string = str, \
+               .sizeof_stat = sizeof(((struct cdc_ncm_ctx *)0)->m), \
+               .stat_offset = offsetof(struct cdc_ncm_ctx, m) }
+#define CDC_NCM_SIMPLE_STAT(m) CDC_NCM_STAT(__stringify(m), m)
+
+static const struct cdc_ncm_stats cdc_ncm_gstrings_stats[] = {
+       CDC_NCM_SIMPLE_STAT(tx_reason_ntb_full),
+       CDC_NCM_SIMPLE_STAT(tx_reason_ndp_full),
+       CDC_NCM_SIMPLE_STAT(tx_reason_timeout),
+       CDC_NCM_SIMPLE_STAT(tx_reason_max_datagram),
+       CDC_NCM_SIMPLE_STAT(tx_overhead),
+       CDC_NCM_SIMPLE_STAT(tx_ntbs),
+       CDC_NCM_SIMPLE_STAT(rx_overhead),
+       CDC_NCM_SIMPLE_STAT(rx_ntbs),
+};
+
+static int cdc_ncm_get_sset_count(struct net_device __always_unused *netdev, int sset)
+{
+       switch (sset) {
+       case ETH_SS_STATS:
+               return ARRAY_SIZE(cdc_ncm_gstrings_stats);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static void cdc_ncm_get_ethtool_stats(struct net_device *netdev,
+                                   struct ethtool_stats __always_unused *stats,
+                                   u64 *data)
+{
+       struct usbnet *dev = netdev_priv(netdev);
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       int i;
+       char *p = NULL;
+
+       for (i = 0; i < ARRAY_SIZE(cdc_ncm_gstrings_stats); i++) {
+               p = (char *)ctx + cdc_ncm_gstrings_stats[i].stat_offset;
+               data[i] = (cdc_ncm_gstrings_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+       }
+}
+
+static void cdc_ncm_get_strings(struct net_device __always_unused *netdev, u32 stringset, u8 *data)
+{
+       u8 *p = data;
+       int i;
+
+       switch (stringset) {
+       case ETH_SS_STATS:
+               for (i = 0; i < ARRAY_SIZE(cdc_ncm_gstrings_stats); i++) {
+                       memcpy(p, cdc_ncm_gstrings_stats[i].stat_string, ETH_GSTRING_LEN);
+                       p += ETH_GSTRING_LEN;
+               }
+       }
+}
+
+static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx);
+
+static const struct ethtool_ops cdc_ncm_ethtool_ops = {
+       .get_settings      = usbnet_get_settings,
+       .set_settings      = usbnet_set_settings,
+       .get_link          = usbnet_get_link,
+       .nway_reset        = usbnet_nway_reset,
+       .get_drvinfo       = usbnet_get_drvinfo,
+       .get_msglevel      = usbnet_get_msglevel,
+       .set_msglevel      = usbnet_set_msglevel,
+       .get_ts_info       = ethtool_op_get_ts_info,
+       .get_sset_count    = cdc_ncm_get_sset_count,
+       .get_strings       = cdc_ncm_get_strings,
+       .get_ethtool_stats = cdc_ncm_get_ethtool_stats,
+};
+
+static u32 cdc_ncm_check_rx_max(struct usbnet *dev, u32 new_rx)
+{
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       u32 val, max, min;
+
+       /* clamp new_rx to sane values */
+       min = USB_CDC_NCM_NTB_MIN_IN_SIZE;
+       max = min_t(u32, CDC_NCM_NTB_MAX_SIZE_RX, le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize));
+
+       /* dwNtbInMaxSize spec violation? Use MIN size for both limits */
+       if (max < min) {
+               dev_warn(&dev->intf->dev, "dwNtbInMaxSize=%u is too small. Using %u\n",
+                        le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize), min);
+               max = min;
+       }
+
+       val = clamp_t(u32, new_rx, min, max);
+       if (val != new_rx)
+               dev_dbg(&dev->intf->dev, "rx_max must be in the [%u, %u] range\n", min, max);
+
+       return val;
+}
+
+static u32 cdc_ncm_check_tx_max(struct usbnet *dev, u32 new_tx)
+{
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       u32 val, max, min;
+
+       /* clamp new_tx to sane values */
+       min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth16);
+       max = min_t(u32, CDC_NCM_NTB_MAX_SIZE_TX, le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize));
+
+       /* some devices set dwNtbOutMaxSize too low for the above default */
+       min = min(min, max);
+
+       val = clamp_t(u32, new_tx, min, max);
+       if (val != new_tx)
+               dev_dbg(&dev->intf->dev, "tx_max must be in the [%u, %u] range\n", min, max);
+
+       return val;
+}
+
+static ssize_t cdc_ncm_show_min_tx_pkt(struct device *d, struct device_attribute *attr, char *buf)
+{
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+
+       return sprintf(buf, "%u\n", ctx->min_tx_pkt);
+}
+
+static ssize_t cdc_ncm_show_rx_max(struct device *d, struct device_attribute *attr, char *buf)
+{
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+
+       return sprintf(buf, "%u\n", ctx->rx_max);
+}
+
+static ssize_t cdc_ncm_show_tx_max(struct device *d, struct device_attribute *attr, char *buf)
+{
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+
+       return sprintf(buf, "%u\n", ctx->tx_max);
+}
+
+static ssize_t cdc_ncm_show_tx_timer_usecs(struct device *d, struct device_attribute *attr, char *buf)
+{
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+
+       return sprintf(buf, "%u\n", ctx->timer_interval / (u32)NSEC_PER_USEC);
+}
+
+static ssize_t cdc_ncm_store_min_tx_pkt(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
+{
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       unsigned long val;
+
+       /* no need to restrict values - anything from 0 to infinity is OK */
+       if (kstrtoul(buf, 0, &val))
+               return -EINVAL;
+
+       ctx->min_tx_pkt = val;
+       return len;
+}
+
+static ssize_t cdc_ncm_store_rx_max(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
+{
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       unsigned long val;
+
+       if (kstrtoul(buf, 0, &val) || cdc_ncm_check_rx_max(dev, val) != val)
+               return -EINVAL;
+
+       cdc_ncm_update_rxtx_max(dev, val, ctx->tx_max);
+       return len;
+}
+
+static ssize_t cdc_ncm_store_tx_max(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
 {
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
        struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       unsigned long val;
+
+       if (kstrtoul(buf, 0, &val) || cdc_ncm_check_tx_max(dev, val) != val)
+               return -EINVAL;
+
+       cdc_ncm_update_rxtx_max(dev, ctx->rx_max, val);
+       return len;
+}
+
+static ssize_t cdc_ncm_store_tx_timer_usecs(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
+{
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       ssize_t ret;
+       unsigned long val;
+
+       ret = kstrtoul(buf, 0, &val);
+       if (ret)
+               return ret;
+       if (val && (val < CDC_NCM_TIMER_INTERVAL_MIN || val > CDC_NCM_TIMER_INTERVAL_MAX))
+               return -EINVAL;
+
+       spin_lock_bh(&ctx->mtx);
+       ctx->timer_interval = val * NSEC_PER_USEC;
+       if (!ctx->timer_interval)
+               ctx->tx_timer_pending = 0;
+       spin_unlock_bh(&ctx->mtx);
+       return len;
+}
+
+static DEVICE_ATTR(min_tx_pkt, S_IRUGO | S_IWUSR, cdc_ncm_show_min_tx_pkt, cdc_ncm_store_min_tx_pkt);
+static DEVICE_ATTR(rx_max, S_IRUGO | S_IWUSR, cdc_ncm_show_rx_max, cdc_ncm_store_rx_max);
+static DEVICE_ATTR(tx_max, S_IRUGO | S_IWUSR, cdc_ncm_show_tx_max, cdc_ncm_store_tx_max);
+static DEVICE_ATTR(tx_timer_usecs, S_IRUGO | S_IWUSR, cdc_ncm_show_tx_timer_usecs, cdc_ncm_store_tx_timer_usecs);
+
+#define NCM_PARM_ATTR(name, format, tocpu)                             \
+static ssize_t cdc_ncm_show_##name(struct device *d, struct device_attribute *attr, char *buf) \
+{ \
+       struct usbnet *dev = netdev_priv(to_net_dev(d)); \
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; \
+       return sprintf(buf, format "\n", tocpu(ctx->ncm_parm.name));    \
+} \
+static DEVICE_ATTR(name, S_IRUGO, cdc_ncm_show_##name, NULL)
+
+NCM_PARM_ATTR(bmNtbFormatsSupported, "0x%04x", le16_to_cpu);
+NCM_PARM_ATTR(dwNtbInMaxSize, "%u", le32_to_cpu);
+NCM_PARM_ATTR(wNdpInDivisor, "%u", le16_to_cpu);
+NCM_PARM_ATTR(wNdpInPayloadRemainder, "%u", le16_to_cpu);
+NCM_PARM_ATTR(wNdpInAlignment, "%u", le16_to_cpu);
+NCM_PARM_ATTR(dwNtbOutMaxSize, "%u", le32_to_cpu);
+NCM_PARM_ATTR(wNdpOutDivisor, "%u", le16_to_cpu);
+NCM_PARM_ATTR(wNdpOutPayloadRemainder, "%u", le16_to_cpu);
+NCM_PARM_ATTR(wNdpOutAlignment, "%u", le16_to_cpu);
+NCM_PARM_ATTR(wNtbOutMaxDatagrams, "%u", le16_to_cpu);
+
+static struct attribute *cdc_ncm_sysfs_attrs[] = {
+       &dev_attr_min_tx_pkt.attr,
+       &dev_attr_rx_max.attr,
+       &dev_attr_tx_max.attr,
+       &dev_attr_tx_timer_usecs.attr,
+       &dev_attr_bmNtbFormatsSupported.attr,
+       &dev_attr_dwNtbInMaxSize.attr,
+       &dev_attr_wNdpInDivisor.attr,
+       &dev_attr_wNdpInPayloadRemainder.attr,
+       &dev_attr_wNdpInAlignment.attr,
+       &dev_attr_dwNtbOutMaxSize.attr,
+       &dev_attr_wNdpOutDivisor.attr,
+       &dev_attr_wNdpOutPayloadRemainder.attr,
+       &dev_attr_wNdpOutAlignment.attr,
+       &dev_attr_wNtbOutMaxDatagrams.attr,
+       NULL,
+};
+
+static struct attribute_group cdc_ncm_sysfs_attr_group = {
+       .name = "cdc_ncm",
+       .attrs = cdc_ncm_sysfs_attrs,
+};
+
+/* handle rx_max and tx_max changes */
+static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx)
+{
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
        u32 val;
-       u8 flags;
-       u8 iface_no;
-       int err;
-       int eth_hlen;
-       u16 mbim_mtu;
-       u16 ntb_fmt_supported;
-       __le16 max_datagram_size;
 
-       iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
+       val = cdc_ncm_check_rx_max(dev, new_rx);
+
+       /* inform device about NTB input size changes */
+       if (val != ctx->rx_max) {
+               __le32 dwNtbInMaxSize = cpu_to_le32(val);
+
+               dev_info(&dev->intf->dev, "setting rx_max = %u\n", val);
+
+               /* tell device to use new size */
+               if (usbnet_write_cmd(dev, USB_CDC_SET_NTB_INPUT_SIZE,
+                                    USB_TYPE_CLASS | USB_DIR_OUT
+                                    | USB_RECIP_INTERFACE,
+                                    0, iface_no, &dwNtbInMaxSize, 4) < 0)
+                       dev_dbg(&dev->intf->dev, "Setting NTB Input Size failed\n");
+               else
+                       ctx->rx_max = val;
+       }
+
+       /* usbnet use these values for sizing rx queues */
+       if (dev->rx_urb_size != ctx->rx_max) {
+               dev->rx_urb_size = ctx->rx_max;
+               if (netif_running(dev->net))
+                       usbnet_unlink_rx_urbs(dev);
+       }
+
+       val = cdc_ncm_check_tx_max(dev, new_tx);
+       if (val != ctx->tx_max)
+               dev_info(&dev->intf->dev, "setting tx_max = %u\n", val);
+
+       /* Adding a pad byte here if necessary simplifies the handling
+        * in cdc_ncm_fill_tx_frame, making tx_max always represent
+        * the real skb max size.
+        *
+        * We cannot use dev->maxpacket here because this is called from
+        * .bind which is called before usbnet sets up dev->maxpacket
+        */
+       if (val != le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize) &&
+           val % usb_maxpacket(dev->udev, dev->out, 1) == 0)
+               val++;
+
+       /* we might need to flush any pending tx buffers if running */
+       if (netif_running(dev->net) && val > ctx->tx_max) {
+               netif_tx_lock_bh(dev->net);
+               usbnet_start_xmit(NULL, dev->net);
+               /* make sure tx_curr_skb is reallocated if it was empty */
+               if (ctx->tx_curr_skb) {
+                       dev_kfree_skb_any(ctx->tx_curr_skb);
+                       ctx->tx_curr_skb = NULL;
+               }
+               ctx->tx_max = val;
+               netif_tx_unlock_bh(dev->net);
+       } else {
+               ctx->tx_max = val;
+       }
+
+       dev->hard_mtu = ctx->tx_max;
+
+       /* max qlen depend on hard_mtu and rx_urb_size */
+       usbnet_update_max_qlen(dev);
+
+       /* never pad more than 3 full USB packets per transfer */
+       ctx->min_tx_pkt = clamp_t(u16, ctx->tx_max - 3 * usb_maxpacket(dev->udev, dev->out, 1),
+                                 CDC_NCM_MIN_TX_PKT, ctx->tx_max);
+}
+
+/* helpers for NCM and MBIM differences */
+static u8 cdc_ncm_flags(struct usbnet *dev)
+{
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+
+       if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting) && ctx->mbim_desc)
+               return ctx->mbim_desc->bmNetworkCapabilities;
+       if (ctx->func_desc)
+               return ctx->func_desc->bmNetworkCapabilities;
+       return 0;
+}
+
+static int cdc_ncm_eth_hlen(struct usbnet *dev)
+{
+       if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting))
+               return 0;
+       return ETH_HLEN;
+}
+
+static u32 cdc_ncm_min_dgram_size(struct usbnet *dev)
+{
+       if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting))
+               return CDC_MBIM_MIN_DATAGRAM_SIZE;
+       return CDC_NCM_MIN_DATAGRAM_SIZE;
+}
+
+static u32 cdc_ncm_max_dgram_size(struct usbnet *dev)
+{
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+
+       if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting) && ctx->mbim_desc)
+               return le16_to_cpu(ctx->mbim_desc->wMaxSegmentSize);
+       if (ctx->ether_desc)
+               return le16_to_cpu(ctx->ether_desc->wMaxSegmentSize);
+       return CDC_NCM_MAX_DATAGRAM_SIZE;
+}
+
+/* initial one-time device setup.  MUST be called with the data interface
+ * in altsetting 0
+ */
+static int cdc_ncm_init(struct usbnet *dev)
+{
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
+       int err;
 
        err = usbnet_read_cmd(dev, USB_CDC_GET_NTB_PARAMETERS,
                              USB_TYPE_CLASS | USB_DIR_IN
@@ -89,7 +454,36 @@ static int cdc_ncm_setup(struct usbnet *dev)
                return err; /* GET_NTB_PARAMETERS is required */
        }
 
-       /* read correct set of parameters according to device mode */
+       /* set CRC Mode */
+       if (cdc_ncm_flags(dev) & USB_CDC_NCM_NCAP_CRC_MODE) {
+               dev_dbg(&dev->intf->dev, "Setting CRC mode off\n");
+               err = usbnet_write_cmd(dev, USB_CDC_SET_CRC_MODE,
+                                      USB_TYPE_CLASS | USB_DIR_OUT
+                                      | USB_RECIP_INTERFACE,
+                                      USB_CDC_NCM_CRC_NOT_APPENDED,
+                                      iface_no, NULL, 0);
+               if (err < 0)
+                       dev_err(&dev->intf->dev, "SET_CRC_MODE failed\n");
+       }
+
+       /* set NTB format, if both formats are supported.
+        *
+        * "The host shall only send this command while the NCM Data
+        *  Interface is in alternate setting 0."
+        */
+       if (le16_to_cpu(ctx->ncm_parm.bmNtbFormatsSupported) &
+                                               USB_CDC_NCM_NTB32_SUPPORTED) {
+               dev_dbg(&dev->intf->dev, "Setting NTB format to 16-bit\n");
+               err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT,
+                                      USB_TYPE_CLASS | USB_DIR_OUT
+                                      | USB_RECIP_INTERFACE,
+                                      USB_CDC_NCM_NTB16_FORMAT,
+                                      iface_no, NULL, 0);
+               if (err < 0)
+                       dev_err(&dev->intf->dev, "SET_NTB_FORMAT failed\n");
+       }
+
+       /* set initial device values */
        ctx->rx_max = le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize);
        ctx->tx_max = le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize);
        ctx->tx_remainder = le16_to_cpu(ctx->ncm_parm.wNdpOutPayloadRemainder);
@@ -97,72 +491,79 @@ static int cdc_ncm_setup(struct usbnet *dev)
        ctx->tx_ndp_modulus = le16_to_cpu(ctx->ncm_parm.wNdpOutAlignment);
        /* devices prior to NCM Errata shall set this field to zero */
        ctx->tx_max_datagrams = le16_to_cpu(ctx->ncm_parm.wNtbOutMaxDatagrams);
-       ntb_fmt_supported = le16_to_cpu(ctx->ncm_parm.bmNtbFormatsSupported);
-
-       /* there are some minor differences in NCM and MBIM defaults */
-       if (cdc_ncm_comm_intf_is_mbim(ctx->control->cur_altsetting)) {
-               if (!ctx->mbim_desc)
-                       return -EINVAL;
-               eth_hlen = 0;
-               flags = ctx->mbim_desc->bmNetworkCapabilities;
-               ctx->max_datagram_size = le16_to_cpu(ctx->mbim_desc->wMaxSegmentSize);
-               if (ctx->max_datagram_size < CDC_MBIM_MIN_DATAGRAM_SIZE)
-                       ctx->max_datagram_size = CDC_MBIM_MIN_DATAGRAM_SIZE;
-       } else {
-               if (!ctx->func_desc)
-                       return -EINVAL;
-               eth_hlen = ETH_HLEN;
-               flags = ctx->func_desc->bmNetworkCapabilities;
-               ctx->max_datagram_size = le16_to_cpu(ctx->ether_desc->wMaxSegmentSize);
-               if (ctx->max_datagram_size < CDC_NCM_MIN_DATAGRAM_SIZE)
-                       ctx->max_datagram_size = CDC_NCM_MIN_DATAGRAM_SIZE;
-       }
-
-       /* common absolute max for NCM and MBIM */
-       if (ctx->max_datagram_size > CDC_NCM_MAX_DATAGRAM_SIZE)
-               ctx->max_datagram_size = CDC_NCM_MAX_DATAGRAM_SIZE;
 
        dev_dbg(&dev->intf->dev,
                "dwNtbInMaxSize=%u dwNtbOutMaxSize=%u wNdpOutPayloadRemainder=%u wNdpOutDivisor=%u wNdpOutAlignment=%u wNtbOutMaxDatagrams=%u flags=0x%x\n",
                ctx->rx_max, ctx->tx_max, ctx->tx_remainder, ctx->tx_modulus,
-               ctx->tx_ndp_modulus, ctx->tx_max_datagrams, flags);
+               ctx->tx_ndp_modulus, ctx->tx_max_datagrams, cdc_ncm_flags(dev));
 
        /* max count of tx datagrams */
        if ((ctx->tx_max_datagrams == 0) ||
                        (ctx->tx_max_datagrams > CDC_NCM_DPT_DATAGRAMS_MAX))
                ctx->tx_max_datagrams = CDC_NCM_DPT_DATAGRAMS_MAX;
 
-       /* verify maximum size of received NTB in bytes */
-       if (ctx->rx_max < USB_CDC_NCM_NTB_MIN_IN_SIZE) {
-               dev_dbg(&dev->intf->dev, "Using min receive length=%d\n",
-                       USB_CDC_NCM_NTB_MIN_IN_SIZE);
-               ctx->rx_max = USB_CDC_NCM_NTB_MIN_IN_SIZE;
-       }
+       /* set up maximum NDP size */
+       ctx->max_ndp_size = sizeof(struct usb_cdc_ncm_ndp16) + (ctx->tx_max_datagrams + 1) * sizeof(struct usb_cdc_ncm_dpe16);
 
-       if (ctx->rx_max > CDC_NCM_NTB_MAX_SIZE_RX) {
-               dev_dbg(&dev->intf->dev, "Using default maximum receive length=%d\n",
-                       CDC_NCM_NTB_MAX_SIZE_RX);
-               ctx->rx_max = CDC_NCM_NTB_MAX_SIZE_RX;
-       }
+       /* initial coalescing timer interval */
+       ctx->timer_interval = CDC_NCM_TIMER_INTERVAL_USEC * NSEC_PER_USEC;
 
-       /* inform device about NTB input size changes */
-       if (ctx->rx_max != le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)) {
-               __le32 dwNtbInMaxSize = cpu_to_le32(ctx->rx_max);
+       return 0;
+}
 
-               err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_INPUT_SIZE,
-                                      USB_TYPE_CLASS | USB_DIR_OUT
-                                      | USB_RECIP_INTERFACE,
-                                      0, iface_no, &dwNtbInMaxSize, 4);
-               if (err < 0)
-                       dev_dbg(&dev->intf->dev, "Setting NTB Input Size failed\n");
+/* set a new max datagram size */
+static void cdc_ncm_set_dgram_size(struct usbnet *dev, int new_size)
+{
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
+       __le16 max_datagram_size;
+       u16 mbim_mtu;
+       int err;
+
+       /* set default based on descriptors */
+       ctx->max_datagram_size = clamp_t(u32, new_size,
+                                        cdc_ncm_min_dgram_size(dev),
+                                        CDC_NCM_MAX_DATAGRAM_SIZE);
+
+       /* inform the device about the selected Max Datagram Size? */
+       if (!(cdc_ncm_flags(dev) & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE))
+               goto out;
+
+       /* read current mtu value from device */
+       err = usbnet_read_cmd(dev, USB_CDC_GET_MAX_DATAGRAM_SIZE,
+                             USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE,
+                             0, iface_no, &max_datagram_size, 2);
+       if (err < 0) {
+               dev_dbg(&dev->intf->dev, "GET_MAX_DATAGRAM_SIZE failed\n");
+               goto out;
        }
 
-       /* verify maximum size of transmitted NTB in bytes */
-       if (ctx->tx_max > CDC_NCM_NTB_MAX_SIZE_TX) {
-               dev_dbg(&dev->intf->dev, "Using default maximum transmit length=%d\n",
-                       CDC_NCM_NTB_MAX_SIZE_TX);
-               ctx->tx_max = CDC_NCM_NTB_MAX_SIZE_TX;
+       if (le16_to_cpu(max_datagram_size) == ctx->max_datagram_size)
+               goto out;
+
+       max_datagram_size = cpu_to_le16(ctx->max_datagram_size);
+       err = usbnet_write_cmd(dev, USB_CDC_SET_MAX_DATAGRAM_SIZE,
+                              USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE,
+                              0, iface_no, &max_datagram_size, 2);
+       if (err < 0)
+               dev_dbg(&dev->intf->dev, "SET_MAX_DATAGRAM_SIZE failed\n");
+
+out:
+       /* set MTU to max supported by the device if necessary */
+       dev->net->mtu = min_t(int, dev->net->mtu, ctx->max_datagram_size - cdc_ncm_eth_hlen(dev));
+
+       /* do not exceed operater preferred MTU */
+       if (ctx->mbim_extended_desc) {
+               mbim_mtu = le16_to_cpu(ctx->mbim_extended_desc->wMTU);
+               if (mbim_mtu != 0 && mbim_mtu < dev->net->mtu)
+                       dev->net->mtu = mbim_mtu;
        }
+}
+
+static void cdc_ncm_fix_modulus(struct usbnet *dev)
+{
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       u32 val;
 
        /*
         * verify that the structure alignment is:
@@ -199,68 +600,31 @@ static int cdc_ncm_setup(struct usbnet *dev)
        }
 
        /* adjust TX-remainder according to NCM specification. */
-       ctx->tx_remainder = ((ctx->tx_remainder - eth_hlen) &
+       ctx->tx_remainder = ((ctx->tx_remainder - cdc_ncm_eth_hlen(dev)) &
                             (ctx->tx_modulus - 1));
+}
 
-       /* additional configuration */
-
-       /* set CRC Mode */
-       if (flags & USB_CDC_NCM_NCAP_CRC_MODE) {
-               err = usbnet_write_cmd(dev, USB_CDC_SET_CRC_MODE,
-                                      USB_TYPE_CLASS | USB_DIR_OUT
-                                      | USB_RECIP_INTERFACE,
-                                      USB_CDC_NCM_CRC_NOT_APPENDED,
-                                      iface_no, NULL, 0);
-               if (err < 0)
-                       dev_dbg(&dev->intf->dev, "Setting CRC mode off failed\n");
-       }
-
-       /* set NTB format, if both formats are supported */
-       if (ntb_fmt_supported & USB_CDC_NCM_NTH32_SIGN) {
-               err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT,
-                                      USB_TYPE_CLASS | USB_DIR_OUT
-                                      | USB_RECIP_INTERFACE,
-                                      USB_CDC_NCM_NTB16_FORMAT,
-                                      iface_no, NULL, 0);
-               if (err < 0)
-                       dev_dbg(&dev->intf->dev, "Setting NTB format to 16-bit failed\n");
-       }
-
-       /* inform the device about the selected Max Datagram Size */
-       if (!(flags & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE))
-               goto out;
-
-       /* read current mtu value from device */
-       err = usbnet_read_cmd(dev, USB_CDC_GET_MAX_DATAGRAM_SIZE,
-                             USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE,
-                             0, iface_no, &max_datagram_size, 2);
-       if (err < 0) {
-               dev_dbg(&dev->intf->dev, "GET_MAX_DATAGRAM_SIZE failed\n");
-               goto out;
-       }
-
-       if (le16_to_cpu(max_datagram_size) == ctx->max_datagram_size)
-               goto out;
+static int cdc_ncm_setup(struct usbnet *dev)
+{
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       u32 def_rx, def_tx;
 
-       max_datagram_size = cpu_to_le16(ctx->max_datagram_size);
-       err = usbnet_write_cmd(dev, USB_CDC_SET_MAX_DATAGRAM_SIZE,
-                              USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE,
-                              0, iface_no, &max_datagram_size, 2);
-       if (err < 0)
-               dev_dbg(&dev->intf->dev, "SET_MAX_DATAGRAM_SIZE failed\n");
+       /* be conservative when selecting intial buffer size to
+        * increase the number of hosts this will work for
+        */
+       def_rx = min_t(u32, CDC_NCM_NTB_DEF_SIZE_RX,
+                      le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize));
+       def_tx = min_t(u32, CDC_NCM_NTB_DEF_SIZE_TX,
+                      le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize));
 
-out:
-       /* set MTU to max supported by the device if necessary */
-       if (dev->net->mtu > ctx->max_datagram_size - eth_hlen)
-               dev->net->mtu = ctx->max_datagram_size - eth_hlen;
+       /* clamp rx_max and tx_max and inform device */
+       cdc_ncm_update_rxtx_max(dev, def_rx, def_tx);
 
-       /* do not exceed operater preferred MTU */
-       if (ctx->mbim_extended_desc) {
-               mbim_mtu = le16_to_cpu(ctx->mbim_extended_desc->wMTU);
-               if (mbim_mtu != 0 && mbim_mtu < dev->net->mtu)
-                       dev->net->mtu = mbim_mtu;
-       }
+       /* sanitize the modulus and remainder values */
+       cdc_ncm_fix_modulus(dev);
 
+       /* set max datagram size */
+       cdc_ncm_set_dgram_size(dev, cdc_ncm_max_dgram_size(dev));
        return 0;
 }
 
@@ -424,10 +788,21 @@ advance:
        }
 
        /* check if we got everything */
-       if (!ctx->data || (!ctx->mbim_desc && !ctx->ether_desc)) {
-               dev_dbg(&intf->dev, "CDC descriptors missing\n");
+       if (!ctx->data) {
+               dev_dbg(&intf->dev, "CDC Union missing and no IAD found\n");
                goto error;
        }
+       if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) {
+               if (!ctx->mbim_desc) {
+                       dev_dbg(&intf->dev, "MBIM functional descriptor missing\n");
+                       goto error;
+               }
+       } else {
+               if (!ctx->ether_desc || !ctx->func_desc) {
+                       dev_dbg(&intf->dev, "NCM or ECM functional descriptors missing\n");
+                       goto error;
+               }
+       }
 
        /* claim data interface, if different from control */
        if (ctx->data != ctx->control) {
@@ -447,8 +822,8 @@ advance:
                goto error2;
        }
 
-       /* initialize data interface */
-       if (cdc_ncm_setup(dev))
+       /* initialize basic device settings */
+       if (cdc_ncm_init(dev))
                goto error2;
 
        /* configure data interface */
@@ -477,18 +852,14 @@ advance:
                dev_info(&intf->dev, "MAC-Address: %pM\n", dev->net->dev_addr);
        }
 
-       /* usbnet use these values for sizing tx/rx queues */
-       dev->hard_mtu = ctx->tx_max;
-       dev->rx_urb_size = ctx->rx_max;
+       /* finish setting up the device specific data */
+       cdc_ncm_setup(dev);
 
-       /* cdc_ncm_setup will override dwNtbOutMaxSize if it is
-        * outside the sane range. Adding a pad byte here if necessary
-        * simplifies the handling in cdc_ncm_fill_tx_frame, making
-        * tx_max always represent the real skb max size.
-        */
-       if (ctx->tx_max != le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize) &&
-           ctx->tx_max % usb_maxpacket(dev->udev, dev->out, 1) == 0)
-               ctx->tx_max++;
+       /* override ethtool_ops */
+       dev->net->ethtool_ops = &cdc_ncm_ethtool_ops;
+
+       /* add our sysfs attrs */
+       dev->net->sysfs_groups[0] = &cdc_ncm_sysfs_attr_group;
 
        return 0;
 
@@ -541,10 +912,10 @@ void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf)
 }
 EXPORT_SYMBOL_GPL(cdc_ncm_unbind);
 
-/* Select the MBIM altsetting iff it is preferred and available,
- * returning the number of the corresponding data interface altsetting
+/* Return the number of the MBIM control interface altsetting iff it
+ * is preferred and available,
  */
-u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf)
+u8 cdc_ncm_select_altsetting(struct usb_interface *intf)
 {
        struct usb_host_interface *alt;
 
@@ -563,15 +934,15 @@ u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf)
         *   the rules given in section 6 (USB Device Model) of this
         *   specification."
         */
-       if (prefer_mbim && intf->num_altsetting == 2) {
+       if (intf->num_altsetting < 2)
+               return intf->cur_altsetting->desc.bAlternateSetting;
+
+       if (prefer_mbim) {
                alt = usb_altnum_to_altsetting(intf, CDC_NCM_COMM_ALTSETTING_MBIM);
-               if (alt && cdc_ncm_comm_intf_is_mbim(alt) &&
-                   !usb_set_interface(dev->udev,
-                                      intf->cur_altsetting->desc.bInterfaceNumber,
-                                      CDC_NCM_COMM_ALTSETTING_MBIM))
-                       return CDC_NCM_DATA_ALTSETTING_MBIM;
+               if (alt && cdc_ncm_comm_intf_is_mbim(alt))
+                       return CDC_NCM_COMM_ALTSETTING_MBIM;
        }
-       return CDC_NCM_DATA_ALTSETTING_NCM;
+       return CDC_NCM_COMM_ALTSETTING_NCM;
 }
 EXPORT_SYMBOL_GPL(cdc_ncm_select_altsetting);
 
@@ -580,12 +951,11 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
        int ret;
 
        /* MBIM backwards compatible function? */
-       cdc_ncm_select_altsetting(dev, intf);
-       if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
+       if (cdc_ncm_select_altsetting(intf) != CDC_NCM_COMM_ALTSETTING_NCM)
                return -ENODEV;
 
-       /* NCM data altsetting is always 1 */
-       ret = cdc_ncm_bind_common(dev, intf, 1);
+       /* The NCM data altsetting is fixed */
+       ret = cdc_ncm_bind_common(dev, intf, CDC_NCM_DATA_ALTSETTING_NCM);
 
        /*
         * We should get an event when network connection is "connected" or
@@ -628,7 +998,7 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_
        cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_max);
 
        /* verify that there is room for the NDP and the datagram (reserve) */
-       if ((ctx->tx_max - skb->len - reserve) < CDC_NCM_NDP_SIZE)
+       if ((ctx->tx_max - skb->len - reserve) < ctx->max_ndp_size)
                return NULL;
 
        /* link to it */
@@ -638,7 +1008,7 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_
                nth16->wNdpIndex = cpu_to_le16(skb->len);
 
        /* push a new empty NDP */
-       ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, CDC_NCM_NDP_SIZE), 0, CDC_NCM_NDP_SIZE);
+       ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, ctx->max_ndp_size), 0, ctx->max_ndp_size);
        ndp16->dwSignature = sign;
        ndp16->wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp16) + sizeof(struct usb_cdc_ncm_dpe16));
        return ndp16;
@@ -683,6 +1053,9 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
 
                /* count total number of frames in this NTB */
                ctx->tx_curr_frame_num = 0;
+
+               /* recent payload counter for this skb_out */
+               ctx->tx_curr_frame_payload = 0;
        }
 
        for (n = ctx->tx_curr_frame_num; n < ctx->tx_max_datagrams; n++) {
@@ -720,6 +1093,7 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
                                ctx->tx_rem_sign = sign;
                                skb = NULL;
                                ready2send = 1;
+                               ctx->tx_reason_ntb_full++;      /* count reason for transmitting */
                        }
                        break;
                }
@@ -733,12 +1107,14 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
                ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len);
                ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16));
                memcpy(skb_put(skb_out, skb->len), skb->data, skb->len);
+               ctx->tx_curr_frame_payload += skb->len; /* count real tx payload data */
                dev_kfree_skb_any(skb);
                skb = NULL;
 
                /* send now if this NDP is full */
                if (index >= CDC_NCM_DPT_DATAGRAMS_MAX) {
                        ready2send = 1;
+                       ctx->tx_reason_ndp_full++;      /* count reason for transmitting */
                        break;
                }
        }
@@ -758,7 +1134,7 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
                ctx->tx_curr_skb = skb_out;
                goto exit_no_skb;
 
-       } else if ((n < ctx->tx_max_datagrams) && (ready2send == 0)) {
+       } else if ((n < ctx->tx_max_datagrams) && (ready2send == 0) && (ctx->timer_interval > 0)) {
                /* wait for more frames */
                /* push variables */
                ctx->tx_curr_skb = skb_out;
@@ -768,11 +1144,13 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
                goto exit_no_skb;
 
        } else {
+               if (n == ctx->tx_max_datagrams)
+                       ctx->tx_reason_max_datagram++;  /* count reason for transmitting */
                /* frame goes out */
                /* variables will be reset at next call */
        }
 
-       /* If collected data size is less or equal CDC_NCM_MIN_TX_PKT
+       /* If collected data size is less or equal ctx->min_tx_pkt
         * bytes, we send buffers as it is. If we get more data, it
         * would be more efficient for USB HS mobile device with DMA
         * engine to receive a full size NTB, than canceling DMA
@@ -782,7 +1160,7 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
         * a ZLP after full sized NTBs.
         */
        if (!(dev->driver_info->flags & FLAG_SEND_ZLP) &&
-           skb_out->len > CDC_NCM_MIN_TX_PKT)
+           skb_out->len > ctx->min_tx_pkt)
                memset(skb_put(skb_out, ctx->tx_max - skb_out->len), 0,
                       ctx->tx_max - skb_out->len);
        else if (skb_out->len < ctx->tx_max && (skb_out->len % dev->maxpacket) == 0)
@@ -795,11 +1173,22 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
        /* return skb */
        ctx->tx_curr_skb = NULL;
        dev->net->stats.tx_packets += ctx->tx_curr_frame_num;
+
+       /* keep private stats: framing overhead and number of NTBs */
+       ctx->tx_overhead += skb_out->len - ctx->tx_curr_frame_payload;
+       ctx->tx_ntbs++;
+
+       /* usbnet has already counted all the framing overhead.
+        * Adjust the stats so that the tx_bytes counter show real
+        * payload data instead.
+        */
+       dev->net->stats.tx_bytes -= skb_out->len - ctx->tx_curr_frame_payload;
+
        return skb_out;
 
 exit_no_skb:
-       /* Start timer, if there is a remaining skb */
-       if (ctx->tx_curr_skb != NULL)
+       /* Start timer, if there is a remaining non-empty skb */
+       if (ctx->tx_curr_skb != NULL && n > 0)
                cdc_ncm_tx_timeout_start(ctx);
        return NULL;
 }
@@ -810,7 +1199,7 @@ static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx)
        /* start timer, if not already started */
        if (!(hrtimer_active(&ctx->tx_timer) || atomic_read(&ctx->stop)))
                hrtimer_start(&ctx->tx_timer,
-                               ktime_set(0, CDC_NCM_TIMER_INTERVAL),
+                               ktime_set(0, ctx->timer_interval),
                                HRTIMER_MODE_REL);
 }
 
@@ -835,6 +1224,7 @@ static void cdc_ncm_txpath_bh(unsigned long param)
                cdc_ncm_tx_timeout_start(ctx);
                spin_unlock_bh(&ctx->mtx);
        } else if (dev->net != NULL) {
+               ctx->tx_reason_timeout++;       /* count reason for transmitting */
                spin_unlock_bh(&ctx->mtx);
                netif_tx_lock_bh(dev->net);
                usbnet_start_xmit(NULL, dev->net);
@@ -970,6 +1360,7 @@ int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
        struct usb_cdc_ncm_dpe16 *dpe16;
        int ndpoffset;
        int loopcount = 50; /* arbitrary max preventing infinite loop */
+       u32 payload = 0;
 
        ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in);
        if (ndpoffset < 0)
@@ -1015,13 +1406,13 @@ next_ndp:
                        break;
 
                } else {
-                       skb = skb_clone(skb_in, GFP_ATOMIC);
+                       /* create a fresh copy to reduce truesize */
+                       skb = netdev_alloc_skb_ip_align(dev->net,  len);
                        if (!skb)
                                goto error;
-                       skb->len = len;
-                       skb->data = ((u8 *)skb_in->data) + offset;
-                       skb_set_tail_pointer(skb, len);
+                       memcpy(skb_put(skb, len), skb_in->data + offset, len);
                        usbnet_skb_return(dev, skb);
+                       payload += len; /* count payload bytes in this NTB */
                }
        }
 err_ndp:
@@ -1030,6 +1421,10 @@ err_ndp:
        if (ndpoffset && loopcount--)
                goto next_ndp;
 
+       /* update stats */
+       ctx->rx_overhead += skb_in->len - payload;
+       ctx->rx_ntbs++;
+
        return 1;
 error:
        return 0;
@@ -1049,14 +1444,14 @@ cdc_ncm_speed_change(struct usbnet *dev,
         */
        if ((tx_speed > 1000000) && (rx_speed > 1000000)) {
                netif_info(dev, link, dev->net,
-                      "%u mbit/s downlink %u mbit/s uplink\n",
-                      (unsigned int)(rx_speed / 1000000U),
-                      (unsigned int)(tx_speed / 1000000U));
+                          "%u mbit/s downlink %u mbit/s uplink\n",
+                          (unsigned int)(rx_speed / 1000000U),
+                          (unsigned int)(tx_speed / 1000000U));
        } else {
                netif_info(dev, link, dev->net,
-                      "%u kbit/s downlink %u kbit/s uplink\n",
-                      (unsigned int)(rx_speed / 1000U),
-                      (unsigned int)(tx_speed / 1000U));
+                          "%u kbit/s downlink %u kbit/s uplink\n",
+                          (unsigned int)(rx_speed / 1000U),
+                          (unsigned int)(tx_speed / 1000U));
        }
 }
 
@@ -1086,11 +1481,10 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)
                 * USB_CDC_NOTIFY_NETWORK_CONNECTION notification shall be
                 * sent by device after USB_CDC_NOTIFY_SPEED_CHANGE.
                 */
-               ctx->connected = le16_to_cpu(event->wValue);
                netif_info(dev, link, dev->net,
                           "network connection: %sconnected\n",
-                          ctx->connected ? "" : "dis");
-               usbnet_link_change(dev, ctx->connected, 0);
+                          !!event->wValue ? "" : "dis");
+               usbnet_link_change(dev, !!event->wValue, 0);
                break;
 
        case USB_CDC_NOTIFY_SPEED_CHANGE:
@@ -1110,23 +1504,11 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)
        }
 }
 
-static int cdc_ncm_check_connect(struct usbnet *dev)
-{
-       struct cdc_ncm_ctx *ctx;
-
-       ctx = (struct cdc_ncm_ctx *)dev->data[0];
-       if (ctx == NULL)
-               return 1;       /* disconnected */
-
-       return !ctx->connected;
-}
-
 static const struct driver_info cdc_ncm_info = {
        .description = "CDC NCM",
        .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET,
        .bind = cdc_ncm_bind,
        .unbind = cdc_ncm_unbind,
-       .check_connect = cdc_ncm_check_connect,
        .manage_power = usbnet_manage_power,
        .status = cdc_ncm_status,
        .rx_fixup = cdc_ncm_rx_fixup,
@@ -1140,7 +1522,6 @@ static const struct driver_info wwan_info = {
                        | FLAG_WWAN,
        .bind = cdc_ncm_bind,
        .unbind = cdc_ncm_unbind,
-       .check_connect = cdc_ncm_check_connect,
        .manage_power = usbnet_manage_power,
        .status = cdc_ncm_status,
        .rx_fixup = cdc_ncm_rx_fixup,
@@ -1154,7 +1535,6 @@ static const struct driver_info wwan_noarp_info = {
                        | FLAG_WWAN | FLAG_NOARP,
        .bind = cdc_ncm_bind,
        .unbind = cdc_ncm_unbind,
-       .check_connect = cdc_ncm_check_connect,
        .manage_power = usbnet_manage_power,
        .status = cdc_ncm_status,
        .rx_fixup = cdc_ncm_rx_fixup,
index 660bd5ea9fc0b311918812af8d3959c830b96cf4..a3a05869309df6a1ac34cdb00c6ff4d031dc921b 100644 (file)
@@ -2425,7 +2425,7 @@ static void hso_net_init(struct net_device *net)
        net->type = ARPHRD_NONE;
        net->mtu = DEFAULT_MTU - 14;
        net->tx_queue_len = 10;
-       SET_ETHTOOL_OPS(net, &ops);
+       net->ethtool_ops = &ops;
 
        /* and initialize the semaphore */
        spin_lock_init(&hso_net->net_lock);
index 312178d7b698e06a6bc97ede9c7c1eb49510c322..f9822bc75425a9bacc2dcd8915344c6373778a2c 100644 (file)
@@ -172,24 +172,11 @@ err:
        return ret;
 }
 
-static int huawei_cdc_ncm_check_connect(struct usbnet *usbnet_dev)
-{
-       struct cdc_ncm_ctx *ctx;
-
-       ctx = (struct cdc_ncm_ctx *)usbnet_dev->data[0];
-
-       if (ctx == NULL)
-               return 1; /* disconnected */
-
-       return !ctx->connected;
-}
-
 static const struct driver_info huawei_cdc_ncm_info = {
        .description = "Huawei CDC NCM device",
        .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN,
        .bind = huawei_cdc_ncm_bind,
        .unbind = huawei_cdc_ncm_unbind,
-       .check_connect = huawei_cdc_ncm_check_connect,
        .manage_power = huawei_cdc_ncm_manage_power,
        .rx_fixup = cdc_ncm_rx_fixup,
        .tx_fixup = cdc_ncm_tx_fixup,
index 973275fef25047402f31f105af87311b4637a532..76465b117b72aaf0c2fdd36f3d78b15eed204b0e 100644 (file)
@@ -534,7 +534,7 @@ static int ipheth_probe(struct usb_interface *intf,
        usb_set_intfdata(intf, dev);
 
        SET_NETDEV_DEV(netdev, &intf->dev);
-       SET_ETHTOOL_OPS(netdev, &ops);
+       netdev->ethtool_ops = &ops;
 
        retval = register_netdev(netdev);
        if (retval) {
index a359d3bb7c5b125422cf59dea69c8a905099fed7..dcb6d33141e0640f545555848434d8efd7822878 100644 (file)
@@ -1171,7 +1171,7 @@ err_fw:
        netdev->netdev_ops = &kaweth_netdev_ops;
        netdev->watchdog_timeo = KAWETH_TX_TIMEOUT;
        netdev->mtu = le16_to_cpu(kaweth->configuration.segment_size);
-       SET_ETHTOOL_OPS(netdev, &ops);
+       netdev->ethtool_ops = &ops;
 
        /* kaweth is zeroed as part of alloc_netdev */
        INIT_DELAYED_WORK(&kaweth->lowmem_work, kaweth_resubmit_tl);
index 03e8a15d7deb74d328d5e563ed2077f62c8d9687..f840802159158b56a5b840d7abb83152fcd0c83f 100644 (file)
@@ -1159,7 +1159,7 @@ static int pegasus_probe(struct usb_interface *intf,
 
        net->watchdog_timeo = PEGASUS_TX_TIMEOUT;
        net->netdev_ops = &pegasus_netdev_ops;
-       SET_ETHTOOL_OPS(net, &ops);
+       net->ethtool_ops = &ops;
        pegasus->mii.dev = net;
        pegasus->mii.mdio_read = mdio_read;
        pegasus->mii.mdio_write = mdio_write;
index dc4bf06948c7224eaecd0b57742096a8440edfe6..cf62d7e8329f11858859257c170b605c6b9a0940 100644 (file)
@@ -763,7 +763,12 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x2357, 0x9000, 4)},    /* TP-LINK MA260 */
        {QMI_FIXED_INTF(0x1bc7, 0x1200, 5)},    /* Telit LE920 */
        {QMI_FIXED_INTF(0x1bc7, 0x1201, 2)},    /* Telit LE920 */
-       {QMI_FIXED_INTF(0x0b3c, 0xc005, 6)},    /* Olivetti Olicard 200 */
+       {QMI_FIXED_INTF(0x0b3c, 0xc000, 4)},    /* Olivetti Olicard 100 */
+       {QMI_FIXED_INTF(0x0b3c, 0xc001, 4)},    /* Olivetti Olicard 120 */
+       {QMI_FIXED_INTF(0x0b3c, 0xc002, 4)},    /* Olivetti Olicard 140 */
+       {QMI_FIXED_INTF(0x0b3c, 0xc004, 6)},    /* Olivetti Olicard 155 */
+       {QMI_FIXED_INTF(0x0b3c, 0xc005, 6)},    /* Olivetti Olicard 200 */
+       {QMI_FIXED_INTF(0x0b3c, 0xc00a, 6)},    /* Olivetti Olicard 160 */
        {QMI_FIXED_INTF(0x0b3c, 0xc00b, 4)},    /* Olivetti Olicard 500 */
        {QMI_FIXED_INTF(0x1e2d, 0x0060, 4)},    /* Cinterion PLxx */
        {QMI_FIXED_INTF(0x1e2d, 0x0053, 4)},    /* Cinterion PHxx,PXxx */
index 3fbfb0869030aeeb6c540766bdbf924d959abcd6..25431965a625a63d99fd3fa6d3167183ea45606a 100644 (file)
@@ -630,12 +630,10 @@ int set_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data)
        int ret;
        void *tmp;
 
-       tmp = kmalloc(size, GFP_KERNEL);
+       tmp = kmemdup(data, size, GFP_KERNEL);
        if (!tmp)
                return -ENOMEM;
 
-       memcpy(tmp, data, size);
-
        ret = usb_control_msg(tp->udev, usb_sndctrlpipe(tp->udev, 0),
                               RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE,
                               value, index, tmp, size, 500);
@@ -3452,7 +3450,7 @@ static int rtl8152_probe(struct usb_interface *intf,
                              NETIF_F_TSO | NETIF_F_FRAGLIST |
                              NETIF_F_IPV6_CSUM | NETIF_F_TSO6;
 
-       SET_ETHTOOL_OPS(netdev, &ops);
+       netdev->ethtool_ops = &ops;
        netif_set_gso_max_size(netdev, RTL_LIMITED_TSO_SIZE);
 
        tp->mii.dev = netdev;
index da2c4583bd2d9d4894ea64ae566a028e9da96b2b..6e87e5710048cf952c1505950d954a493b6b8878 100644 (file)
@@ -878,7 +878,7 @@ static int rtl8150_probe(struct usb_interface *intf,
        dev->netdev = netdev;
        netdev->netdev_ops = &rtl8150_netdev_ops;
        netdev->watchdog_timeo = RTL8150_TX_TIMEOUT;
-       SET_ETHTOOL_OPS(netdev, &ops);
+       netdev->ethtool_ops = &ops;
        dev->intr_interval = 100;       /* 100ms */
 
        if (!alloc_all_urbs(dev)) {
index 8a852b5f215f1c5b3b42c3bc93cf6655a1178b68..7d9f84a91f37dd96fbd835d170dc8ed24c281b6c 100644 (file)
@@ -1646,7 +1646,7 @@ static int virtnet_probe(struct virtio_device *vdev)
        dev->netdev_ops = &virtnet_netdev;
        dev->features = NETIF_F_HIGHDMA;
 
-       SET_ETHTOOL_OPS(dev, &virtnet_ethtool_ops);
+       dev->ethtool_ops = &virtnet_ethtool_ops;
        SET_NETDEV_DEV(dev, &vdev->dev);
 
        /* Do we support "hardware" checksums? */
@@ -1724,6 +1724,13 @@ static int virtnet_probe(struct virtio_device *vdev)
        if (virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ))
                vi->has_cvq = true;
 
+       if (vi->any_header_sg) {
+               if (vi->mergeable_rx_bufs)
+                       dev->needed_headroom = sizeof(struct virtio_net_hdr_mrg_rxbuf);
+               else
+                       dev->needed_headroom = sizeof(struct virtio_net_hdr);
+       }
+
        /* Use single tx/rx queue pair as default */
        vi->curr_queue_pairs = 1;
        vi->max_queue_pairs = max_queue_pairs;
index 600ab56c0008bab49251505e38ca911b611da2c1..40c1c7b0d9e02adb9b25b6e16a15ad8e4fac3a7c 100644 (file)
@@ -431,8 +431,8 @@ vmxnet3_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
                ethtool_cmd_speed_set(ecmd, adapter->link_speed);
                ecmd->duplex = DUPLEX_FULL;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
        return 0;
 }
@@ -579,7 +579,7 @@ vmxnet3_get_rss_indir_size(struct net_device *netdev)
 }
 
 static int
-vmxnet3_get_rss_indir(struct net_device *netdev, u32 *p)
+vmxnet3_get_rss(struct net_device *netdev, u32 *p, u8 *key)
 {
        struct vmxnet3_adapter *adapter = netdev_priv(netdev);
        struct UPT1_RSSConf *rssConf = adapter->rss_conf;
@@ -592,7 +592,7 @@ vmxnet3_get_rss_indir(struct net_device *netdev, u32 *p)
 }
 
 static int
-vmxnet3_set_rss_indir(struct net_device *netdev, const u32 *p)
+vmxnet3_set_rss(struct net_device *netdev, const u32 *p, const u8 *key)
 {
        unsigned int i;
        unsigned long flags;
@@ -628,12 +628,12 @@ static const struct ethtool_ops vmxnet3_ethtool_ops = {
        .get_rxnfc         = vmxnet3_get_rxnfc,
 #ifdef VMXNET3_RSS
        .get_rxfh_indir_size = vmxnet3_get_rss_indir_size,
-       .get_rxfh_indir    = vmxnet3_get_rss_indir,
-       .set_rxfh_indir    = vmxnet3_set_rss_indir,
+       .get_rxfh          = vmxnet3_get_rss,
+       .set_rxfh          = vmxnet3_set_rss,
 #endif
 };
 
 void vmxnet3_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &vmxnet3_ethtool_ops);
+       netdev->ethtool_ops = &vmxnet3_ethtool_ops;
 }
index 4dbb2ed85b972492e1dbbbdc51a81e32652f975b..ade33ef82823b230a34890d77af039d8531aefb7 100644 (file)
@@ -127,6 +127,7 @@ struct vxlan_dev {
        struct list_head  next;         /* vxlan's per namespace list */
        struct vxlan_sock *vn_sock;     /* listening socket */
        struct net_device *dev;
+       struct net        *net;         /* netns for packet i/o */
        struct vxlan_rdst default_dst;  /* default destination */
        union vxlan_addr  saddr;        /* source address */
        __be16            dst_port;
@@ -134,7 +135,7 @@ struct vxlan_dev {
        __u16             port_max;
        __u8              tos;          /* TOS override */
        __u8              ttl;
-       u32               flags;        /* VXLAN_F_* below */
+       u32               flags;        /* VXLAN_F_* in vxlan.h */
 
        struct work_struct sock_work;
        struct work_struct igmp_join;
@@ -149,13 +150,6 @@ struct vxlan_dev {
        struct hlist_head fdb_head[FDB_HASH_SIZE];
 };
 
-#define VXLAN_F_LEARN  0x01
-#define VXLAN_F_PROXY  0x02
-#define VXLAN_F_RSC    0x04
-#define VXLAN_F_L2MISS 0x08
-#define VXLAN_F_L3MISS 0x10
-#define VXLAN_F_IPV6   0x20 /* internal flag */
-
 /* salt for hash table */
 static u32 vxlan_salt __read_mostly;
 static struct workqueue_struct *vxlan_wq;
@@ -571,6 +565,7 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, struct sk_buff
                        goto out;
        }
        skb_gro_pull(skb, sizeof(struct vxlanhdr)); /* pull vxlan header */
+       skb_gro_postpull_rcsum(skb, vh, sizeof(struct vxlanhdr));
 
        off_eth = skb_gro_offset(skb);
        hlen = off_eth + sizeof(*eh);
@@ -605,6 +600,7 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, struct sk_buff
        }
 
        skb_gro_pull(skb, sizeof(*eh)); /* pull inner eth header */
+       skb_gro_postpull_rcsum(skb, eh, sizeof(*eh));
        pp = ptype->callbacks.gro_receive(head, skb);
 
 out_unlock:
@@ -1160,15 +1156,7 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
        if (!vs)
                goto drop;
 
-       /* If the NIC driver gave us an encapsulated packet
-        * with the encapsulation mark, the device checksummed it
-        * for us. Otherwise force the upper layers to verify it.
-        */
-       if ((skb->ip_summed != CHECKSUM_UNNECESSARY && skb->ip_summed != CHECKSUM_PARTIAL) ||
-           !skb->encapsulation)
-               skb->ip_summed = CHECKSUM_NONE;
-
-       skb->encapsulation = 0;
+       skb_pop_rcv_encapsulation(skb);
 
        vs->rcv(vs, skb, vxh->vx_vni);
        return 0;
@@ -1203,7 +1191,9 @@ static void vxlan_rcv(struct vxlan_sock *vs,
 
        remote_ip = &vxlan->default_dst.remote_ip;
        skb_reset_mac_header(skb);
+       skb_scrub_packet(skb, !net_eq(vxlan->net, dev_net(vxlan->dev)));
        skb->protocol = eth_type_trans(skb, vxlan->dev);
+       skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
 
        /* Ignore packet loops (and multicast echo) */
        if (ether_addr_equal(eth_hdr(skb)->h_source, vxlan->dev->dev_addr))
@@ -1599,18 +1589,11 @@ __be16 vxlan_src_port(__u16 port_min, __u16 port_max, struct sk_buff *skb)
 }
 EXPORT_SYMBOL_GPL(vxlan_src_port);
 
-static int handle_offloads(struct sk_buff *skb)
+static inline struct sk_buff *vxlan_handle_offloads(struct sk_buff *skb,
+                                                   bool udp_csum)
 {
-       if (skb_is_gso(skb)) {
-               int err = skb_unclone(skb, GFP_ATOMIC);
-               if (unlikely(err))
-                       return err;
-
-               skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL;
-       } else if (skb->ip_summed != CHECKSUM_PARTIAL)
-               skb->ip_summed = CHECKSUM_NONE;
-
-       return 0;
+       int type = udp_csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
+       return iptunnel_handle_offloads(skb, udp_csum, type);
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
@@ -1618,7 +1601,8 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
                           struct dst_entry *dst, struct sk_buff *skb,
                           struct net_device *dev, struct in6_addr *saddr,
                           struct in6_addr *daddr, __u8 prio, __u8 ttl,
-                          __be16 src_port, __be16 dst_port, __be32 vni)
+                          __be16 src_port, __be16 dst_port, __be32 vni,
+                          bool xnet)
 {
        struct ipv6hdr *ip6h;
        struct vxlanhdr *vxh;
@@ -1626,12 +1610,11 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
        int min_headroom;
        int err;
 
-       if (!skb->encapsulation) {
-               skb_reset_inner_headers(skb);
-               skb->encapsulation = 1;
-       }
+       skb = vxlan_handle_offloads(skb, !udp_get_no_check6_tx(vs->sock->sk));
+       if (IS_ERR(skb))
+               return -EINVAL;
 
-       skb_scrub_packet(skb, false);
+       skb_scrub_packet(skb, xnet);
 
        min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len
                        + VXLAN_HLEN + sizeof(struct ipv6hdr)
@@ -1663,27 +1646,14 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
        uh->source = src_port;
 
        uh->len = htons(skb->len);
-       uh->check = 0;
 
        memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
        IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
                              IPSKB_REROUTED);
        skb_dst_set(skb, dst);
 
-       if (!skb_is_gso(skb) && !(dst->dev->features & NETIF_F_IPV6_CSUM)) {
-               __wsum csum = skb_checksum(skb, 0, skb->len, 0);
-               skb->ip_summed = CHECKSUM_UNNECESSARY;
-               uh->check = csum_ipv6_magic(saddr, daddr, skb->len,
-                                           IPPROTO_UDP, csum);
-               if (uh->check == 0)
-                       uh->check = CSUM_MANGLED_0;
-       } else {
-               skb->ip_summed = CHECKSUM_PARTIAL;
-               skb->csum_start = skb_transport_header(skb) - skb->head;
-               skb->csum_offset = offsetof(struct udphdr, check);
-               uh->check = ~csum_ipv6_magic(saddr, daddr,
-                                            skb->len, IPPROTO_UDP, 0);
-       }
+       udp6_set_csum(udp_get_no_check6_tx(vs->sock->sk), skb,
+                     saddr, daddr, skb->len);
 
        __skb_push(skb, sizeof(*ip6h));
        skb_reset_network_header(skb);
@@ -1699,10 +1669,6 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
        ip6h->daddr       = *daddr;
        ip6h->saddr       = *saddr;
 
-       err = handle_offloads(skb);
-       if (err)
-               return err;
-
        ip6tunnel_xmit(skb, dev);
        return 0;
 }
@@ -1711,17 +1677,16 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
 int vxlan_xmit_skb(struct vxlan_sock *vs,
                   struct rtable *rt, struct sk_buff *skb,
                   __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df,
-                  __be16 src_port, __be16 dst_port, __be32 vni)
+                  __be16 src_port, __be16 dst_port, __be32 vni, bool xnet)
 {
        struct vxlanhdr *vxh;
        struct udphdr *uh;
        int min_headroom;
        int err;
 
-       if (!skb->encapsulation) {
-               skb_reset_inner_headers(skb);
-               skb->encapsulation = 1;
-       }
+       skb = vxlan_handle_offloads(skb, !vs->sock->sk->sk_no_check_tx);
+       if (IS_ERR(skb))
+               return -EINVAL;
 
        min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len
                        + VXLAN_HLEN + sizeof(struct iphdr)
@@ -1753,14 +1718,12 @@ int vxlan_xmit_skb(struct vxlan_sock *vs,
        uh->source = src_port;
 
        uh->len = htons(skb->len);
-       uh->check = 0;
 
-       err = handle_offloads(skb);
-       if (err)
-               return err;
+       udp_set_csum(vs->sock->sk->sk_no_check_tx, skb,
+                    src, dst, skb->len);
 
        return iptunnel_xmit(vs->sock->sk, rt, skb, src, dst, IPPROTO_UDP,
-                            tos, ttl, df, false);
+                            tos, ttl, df, xnet);
 }
 EXPORT_SYMBOL_GPL(vxlan_xmit_skb);
 
@@ -1853,7 +1816,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                fl4.daddr = dst->sin.sin_addr.s_addr;
                fl4.saddr = vxlan->saddr.sin.sin_addr.s_addr;
 
-               rt = ip_route_output_key(dev_net(dev), &fl4);
+               rt = ip_route_output_key(vxlan->net, &fl4);
                if (IS_ERR(rt)) {
                        netdev_dbg(dev, "no route to %pI4\n",
                                   &dst->sin.sin_addr.s_addr);
@@ -1874,7 +1837,7 @@ static void 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_port);
+                       dst_vxlan = vxlan_find_vni(vxlan->net, vni, dst_port);
                        if (!dst_vxlan)
                                goto tx_error;
                        vxlan_encap_bypass(skb, vxlan, dst_vxlan);
@@ -1887,7 +1850,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                err = vxlan_xmit_skb(vxlan->vn_sock, rt, skb,
                                     fl4.saddr, dst->sin.sin_addr.s_addr,
                                     tos, ttl, df, src_port, dst_port,
-                                    htonl(vni << 8));
+                                    htonl(vni << 8),
+                                    !net_eq(vxlan->net, dev_net(vxlan->dev)));
 
                if (err < 0)
                        goto rt_tx_error;
@@ -1927,7 +1891,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                        struct vxlan_dev *dst_vxlan;
 
                        dst_release(ndst);
-                       dst_vxlan = vxlan_find_vni(dev_net(dev), vni, dst_port);
+                       dst_vxlan = vxlan_find_vni(vxlan->net, vni, dst_port);
                        if (!dst_vxlan)
                                goto tx_error;
                        vxlan_encap_bypass(skb, vxlan, dst_vxlan);
@@ -1938,7 +1902,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
 
                err = vxlan6_xmit_skb(vxlan->vn_sock, ndst, skb,
                                      dev, &fl6.saddr, &fl6.daddr, 0, ttl,
-                                     src_port, dst_port, htonl(vni << 8));
+                                     src_port, dst_port, htonl(vni << 8),
+                                     !net_eq(vxlan->net, dev_net(vxlan->dev)));
 #endif
        }
 
@@ -2082,7 +2047,7 @@ static void vxlan_vs_add_dev(struct vxlan_sock *vs, struct vxlan_dev *vxlan)
 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_net *vn = net_generic(vxlan->net, vxlan_net_id);
        struct vxlan_sock *vs;
 
        dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
@@ -2090,7 +2055,7 @@ static int vxlan_init(struct net_device *dev)
                return -ENOMEM;
 
        spin_lock(&vn->sock_lock);
-       vs = vxlan_find_sock(dev_net(dev), vxlan->dst_port);
+       vs = vxlan_find_sock(vxlan->net, vxlan->dst_port);
        if (vs) {
                /* If we have a socket with same port already, reuse it */
                atomic_inc(&vs->refcnt);
@@ -2172,8 +2137,8 @@ static void vxlan_flush(struct vxlan_dev *vxlan)
 /* Cleanup timer and forwarding table on shutdown */
 static int vxlan_stop(struct net_device *dev)
 {
-       struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
        struct vxlan_dev *vxlan = netdev_priv(dev);
+       struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
        struct vxlan_sock *vs = vxlan->vn_sock;
 
        if (vs && vxlan_addr_multicast(&vxlan->default_dst.remote_ip) &&
@@ -2202,7 +2167,7 @@ static int vxlan_change_mtu(struct net_device *dev, int new_mtu)
        struct net_device *lowerdev;
        int max_mtu;
 
-       lowerdev = __dev_get_by_index(dev_net(dev), dst->remote_ifindex);
+       lowerdev = __dev_get_by_index(vxlan->net, dst->remote_ifindex);
        if (lowerdev == NULL)
                return eth_change_mtu(dev, new_mtu);
 
@@ -2275,9 +2240,9 @@ static void vxlan_setup(struct net_device *dev)
        eth_hw_addr_random(dev);
        ether_setup(dev);
        if (vxlan->default_dst.remote_ip.sa.sa_family == AF_INET6)
-               dev->hard_header_len = ETH_HLEN + VXLAN6_HEADROOM;
+               dev->needed_headroom = ETH_HLEN + VXLAN6_HEADROOM;
        else
-               dev->hard_header_len = ETH_HLEN + VXLAN_HEADROOM;
+               dev->needed_headroom = ETH_HLEN + VXLAN_HEADROOM;
 
        dev->netdev_ops = &vxlan_netdev_ops;
        dev->destructor = free_netdev;
@@ -2285,7 +2250,6 @@ static void vxlan_setup(struct net_device *dev)
 
        dev->tx_queue_len = 0;
        dev->features   |= NETIF_F_LLTX;
-       dev->features   |= NETIF_F_NETNS_LOCAL;
        dev->features   |= NETIF_F_SG | NETIF_F_HW_CSUM;
        dev->features   |= NETIF_F_RXCSUM;
        dev->features   |= NETIF_F_GSO_SOFTWARE;
@@ -2401,7 +2365,7 @@ static void vxlan_del_work(struct work_struct *work)
  * could be used for both IPv4 and IPv6 communications, but
  * users may set bindv6only=1.
  */
-static struct socket *create_v6_sock(struct net *net, __be16 port)
+static struct socket *create_v6_sock(struct net *net, __be16 port, u32 flags)
 {
        struct sock *sk;
        struct socket *sock;
@@ -2438,18 +2402,25 @@ static struct socket *create_v6_sock(struct net *net, __be16 port)
 
        /* Disable multicast loopback */
        inet_sk(sk)->mc_loop = 0;
+
+       if (flags & VXLAN_F_UDP_ZERO_CSUM6_TX)
+               udp_set_no_check6_tx(sk, true);
+
+       if (flags & VXLAN_F_UDP_ZERO_CSUM6_RX)
+               udp_set_no_check6_rx(sk, true);
+
        return sock;
 }
 
 #else
 
-static struct socket *create_v6_sock(struct net *net, __be16 port)
+static struct socket *create_v6_sock(struct net *net, __be16 port, u32 flags)
 {
                return ERR_PTR(-EPFNOSUPPORT);
 }
 #endif
 
-static struct socket *create_v4_sock(struct net *net, __be16 port)
+static struct socket *create_v4_sock(struct net *net, __be16 port, u32 flags)
 {
        struct sock *sk;
        struct socket *sock;
@@ -2482,18 +2453,24 @@ static struct socket *create_v4_sock(struct net *net, __be16 port)
 
        /* Disable multicast loopback */
        inet_sk(sk)->mc_loop = 0;
+
+       if (!(flags & VXLAN_F_UDP_CSUM))
+               sock->sk->sk_no_check_tx = 1;
+
        return sock;
 }
 
 /* Create new listen socket if needed */
 static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
-                                             vxlan_rcv_t *rcv, void *data, bool ipv6)
+                                             vxlan_rcv_t *rcv, void *data,
+                                             u32 flags)
 {
        struct vxlan_net *vn = net_generic(net, vxlan_net_id);
        struct vxlan_sock *vs;
        struct socket *sock;
        struct sock *sk;
        unsigned int h;
+       bool ipv6 = !!(flags & VXLAN_F_IPV6);
 
        vs = kzalloc(sizeof(*vs), GFP_KERNEL);
        if (!vs)
@@ -2505,9 +2482,9 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
        INIT_WORK(&vs->del_work, vxlan_del_work);
 
        if (ipv6)
-               sock = create_v6_sock(net, port);
+               sock = create_v6_sock(net, port, flags);
        else
-               sock = create_v4_sock(net, port);
+               sock = create_v4_sock(net, port, flags);
        if (IS_ERR(sock)) {
                kfree(vs);
                return ERR_CAST(sock);
@@ -2545,12 +2522,12 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
 
 struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
                                  vxlan_rcv_t *rcv, void *data,
-                                 bool no_share, bool ipv6)
+                                 bool no_share, u32 flags)
 {
        struct vxlan_net *vn = net_generic(net, vxlan_net_id);
        struct vxlan_sock *vs;
 
-       vs = vxlan_socket_create(net, port, rcv, data, ipv6);
+       vs = vxlan_socket_create(net, port, rcv, data, flags);
        if (!IS_ERR(vs))
                return vs;
 
@@ -2578,12 +2555,12 @@ EXPORT_SYMBOL_GPL(vxlan_sock_add);
 static void vxlan_sock_work(struct work_struct *work)
 {
        struct vxlan_dev *vxlan = container_of(work, struct vxlan_dev, sock_work);
-       struct net *net = dev_net(vxlan->dev);
+       struct net *net = vxlan->net;
        struct vxlan_net *vn = net_generic(net, vxlan_net_id);
        __be16 port = vxlan->dst_port;
        struct vxlan_sock *nvs;
 
-       nvs = vxlan_sock_add(net, port, vxlan_rcv, NULL, false, vxlan->flags & VXLAN_F_IPV6);
+       nvs = vxlan_sock_add(net, port, vxlan_rcv, NULL, false, vxlan->flags);
        spin_lock(&vn->sock_lock);
        if (!IS_ERR(nvs))
                vxlan_vs_add_dev(nvs, vxlan);
@@ -2605,6 +2582,8 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
        if (!data[IFLA_VXLAN_ID])
                return -EINVAL;
 
+       vxlan->net = dev_net(dev);
+
        vni = nla_get_u32(data[IFLA_VXLAN_ID]);
        dst->remote_vni = vni;
 
@@ -2660,8 +2639,7 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
                if (!tb[IFLA_MTU])
                        dev->mtu = lowerdev->mtu - (use_ipv6 ? VXLAN6_HEADROOM : VXLAN_HEADROOM);
 
-               /* update header length based on lower device */
-               dev->hard_header_len = lowerdev->hard_header_len +
+               dev->needed_headroom = lowerdev->hard_header_len +
                                       (use_ipv6 ? VXLAN6_HEADROOM : VXLAN_HEADROOM);
        } else if (use_ipv6)
                vxlan->flags |= VXLAN_F_IPV6;
@@ -2705,12 +2683,23 @@ 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 (data[IFLA_VXLAN_UDP_CSUM] && nla_get_u8(data[IFLA_VXLAN_UDP_CSUM]))
+               vxlan->flags |= VXLAN_F_UDP_CSUM;
+
+       if (data[IFLA_VXLAN_UDP_ZERO_CSUM6_TX] &&
+           nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]))
+               vxlan->flags |= VXLAN_F_UDP_ZERO_CSUM6_TX;
+
+       if (data[IFLA_VXLAN_UDP_ZERO_CSUM6_RX] &&
+           nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]))
+               vxlan->flags |= VXLAN_F_UDP_ZERO_CSUM6_RX;
+
        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);
+       dev->ethtool_ops = &vxlan_ethtool_ops;
 
        /* create an fdb entry for a valid default destination */
        if (!vxlan_addr_any(&vxlan->default_dst.remote_ip)) {
@@ -2739,8 +2728,8 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
 
 static void vxlan_dellink(struct net_device *dev, struct list_head *head)
 {
-       struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
        struct vxlan_dev *vxlan = netdev_priv(dev);
+       struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
 
        spin_lock(&vn->sock_lock);
        if (!hlist_unhashed(&vxlan->hlist))
@@ -2768,7 +2757,10 @@ static size_t vxlan_get_size(const struct net_device *dev)
                nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_AGEING */
                nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_LIMIT */
                nla_total_size(sizeof(struct ifla_vxlan_port_range)) +
-               nla_total_size(sizeof(__be16))+ /* IFLA_VXLAN_PORT */
+               nla_total_size(sizeof(__be16)) + /* IFLA_VXLAN_PORT */
+               nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_CSUM */
+               nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_ZERO_CSUM6_TX */
+               nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_ZERO_CSUM6_RX */
                0;
 }
 
@@ -2828,7 +2820,13 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
                        !!(vxlan->flags & VXLAN_F_L3MISS)) ||
            nla_put_u32(skb, IFLA_VXLAN_AGEING, vxlan->age_interval) ||
            nla_put_u32(skb, IFLA_VXLAN_LIMIT, vxlan->addrmax) ||
-           nla_put_be16(skb, IFLA_VXLAN_PORT, vxlan->dst_port))
+           nla_put_be16(skb, IFLA_VXLAN_PORT, vxlan->dst_port) ||
+           nla_put_u8(skb, IFLA_VXLAN_UDP_CSUM,
+                       !!(vxlan->flags & VXLAN_F_UDP_CSUM)) ||
+           nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
+                       !!(vxlan->flags & VXLAN_F_UDP_ZERO_CSUM6_TX)) ||
+           nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
+                       !!(vxlan->flags & VXLAN_F_UDP_ZERO_CSUM6_RX)))
                goto nla_put_failure;
 
        if (nla_put(skb, IFLA_VXLAN_PORT_RANGE, sizeof(ports), &ports))
@@ -2905,8 +2903,33 @@ static __net_init int vxlan_init_net(struct net *net)
        return 0;
 }
 
+static void __net_exit vxlan_exit_net(struct net *net)
+{
+       struct vxlan_net *vn = net_generic(net, vxlan_net_id);
+       struct vxlan_dev *vxlan, *next;
+       struct net_device *dev, *aux;
+       LIST_HEAD(list);
+
+       rtnl_lock();
+       for_each_netdev_safe(net, dev, aux)
+               if (dev->rtnl_link_ops == &vxlan_link_ops)
+                       unregister_netdevice_queue(dev, &list);
+
+       list_for_each_entry_safe(vxlan, next, &vn->vxlan_list, next) {
+               /* If vxlan->dev is in the same netns, it has already been added
+                * to the list by the previous loop.
+                */
+               if (!net_eq(dev_net(vxlan->dev), net))
+                       unregister_netdevice_queue(dev, &list);
+       }
+
+       unregister_netdevice_many(&list);
+       rtnl_unlock();
+}
+
 static struct pernet_operations vxlan_net_ops = {
        .init = vxlan_init_net,
+       .exit = vxlan_exit_net,
        .id   = &vxlan_net_id,
        .size = sizeof(struct vxlan_net),
 };
index bcfff0d62de4f2070d5ac644a3becfedb74e3462..93ace042d0aa71b007ec80ab638a5cdbcaa790dd 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/ioport.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
+#include <linux/delay.h>
 #include <linux/if.h>
 #include <linux/hdlc.h>
 #include <asm/io.h>
@@ -678,7 +679,6 @@ static inline void
 fst_cpureset(struct fst_card_info *card)
 {
        unsigned char interrupt_line_register;
-       unsigned long j = jiffies + 1;
        unsigned int regval;
 
        if (card->family == FST_FAMILY_TXU) {
@@ -696,16 +696,12 @@ fst_cpureset(struct fst_card_info *card)
                /*
                 * We are delaying here to allow the 9054 to reset itself
                 */
-               j = jiffies + 1;
-               while (jiffies < j)
-                       /* Do nothing */ ;
+               usleep_range(10, 20);
                outw(0x240f, card->pci_conf + CNTRL_9054 + 2);
                /*
                 * We are delaying here to allow the 9054 to reload its eeprom
                 */
-               j = jiffies + 1;
-               while (jiffies < j)
-                       /* Do nothing */ ;
+               usleep_range(10, 20);
                outw(0x040f, card->pci_conf + CNTRL_9054 + 2);
 
                if (pci_write_config_byte
@@ -886,20 +882,18 @@ fst_rx_dma_complete(struct fst_card_info *card, struct fst_port_info *port,
  *      Receive a frame through the DMA
  */
 static inline void
-fst_rx_dma(struct fst_card_info *card, dma_addr_t skb,
-          dma_addr_t mem, int len)
+fst_rx_dma(struct fst_card_info *card, dma_addr_t dma, u32 mem, int len)
 {
        /*
         * This routine will setup the DMA and start it
         */
 
-       dbg(DBG_RX, "In fst_rx_dma %lx %lx %d\n",
-           (unsigned long) skb, (unsigned long) mem, len);
+       dbg(DBG_RX, "In fst_rx_dma %x %x %d\n", (u32)dma, mem, len);
        if (card->dmarx_in_progress) {
                dbg(DBG_ASS, "In fst_rx_dma while dma in progress\n");
        }
 
-       outl(skb, card->pci_conf + DMAPADR0);   /* Copy to here */
+       outl(dma, card->pci_conf + DMAPADR0);   /* Copy to here */
        outl(mem, card->pci_conf + DMALADR0);   /* from here */
        outl(len, card->pci_conf + DMASIZ0);    /* for this length */
        outl(0x00000000c, card->pci_conf + DMADPR0);    /* In this direction */
@@ -915,20 +909,19 @@ fst_rx_dma(struct fst_card_info *card, dma_addr_t skb,
  *      Send a frame through the DMA
  */
 static inline void
-fst_tx_dma(struct fst_card_info *card, unsigned char *skb,
-          unsigned char *mem, int len)
+fst_tx_dma(struct fst_card_info *card, dma_addr_t dma, u32 mem, int len)
 {
        /*
         * This routine will setup the DMA and start it.
         */
 
-       dbg(DBG_TX, "In fst_tx_dma %p %p %d\n", skb, mem, len);
+       dbg(DBG_TX, "In fst_tx_dma %x %x %d\n", (u32)dma, mem, len);
        if (card->dmatx_in_progress) {
                dbg(DBG_ASS, "In fst_tx_dma while dma in progress\n");
        }
 
-       outl((unsigned long) skb, card->pci_conf + DMAPADR1);   /* Copy from here */
-       outl((unsigned long) mem, card->pci_conf + DMALADR1);   /* to here */
+       outl(dma, card->pci_conf + DMAPADR1);   /* Copy from here */
+       outl(mem, card->pci_conf + DMALADR1);   /* to here */
        outl(len, card->pci_conf + DMASIZ1);    /* for this length */
        outl(0x000000004, card->pci_conf + DMADPR1);    /* In this direction */
 
@@ -1405,9 +1398,7 @@ do_bottom_half_tx(struct fst_card_info *card)
                                        card->dma_len_tx = skb->len;
                                        card->dma_txpos = port->txpos;
                                        fst_tx_dma(card,
-                                                  (char *) card->
-                                                  tx_dma_handle_card,
-                                                  (char *)
+                                                  card->tx_dma_handle_card,
                                                   BUF_OFFSET(txBuffer[pi]
                                                              [port->txpos][0]),
                                                   skb->len);
index de3bbf43fc5ac14f41ee9d76eeacb1f059eeaab3..cdd45fb8a1f6892587abddf2abbf0963bd7a5653 100644 (file)
@@ -1322,10 +1322,6 @@ NOTE:  This is rather a useless action right now, as the
 
 static int sdla_change_mtu(struct net_device *dev, int new_mtu)
 {
-       struct frad_local *flp;
-
-       flp = netdev_priv(dev);
-
        if (netif_running(dev))
                return -EBUSY;
 
index 4a01e5c7fe098e43def42b3d06d091abbabd8795..4c417903e9be9f5bb74a7901f179488e812d8038 100644 (file)
@@ -1061,7 +1061,7 @@ int i2400m_firmware_check(struct i2400m *i2400m)
                goto error_bad_major;
        }
        result = 0;
-       if (minor < I2400M_HDIv_MINOR_2 && minor > I2400M_HDIv_MINOR)
+       if (minor > I2400M_HDIv_MINOR_2 || minor < I2400M_HDIv_MINOR)
                dev_warn(dev, "untested minor fw version %u.%u.%u\n",
                         major, minor, branch);
        /* Yes, we ignore the branch -- we don't have to track it */
index 9c34d2fccfac61508705a4021f436e9a9024e936..9c78090e72f87dae118dff22fd366d5ce11bacd8 100644 (file)
@@ -500,26 +500,23 @@ int i2400m_pm_notifier(struct notifier_block *notifier,
  */
 int i2400m_pre_reset(struct i2400m *i2400m)
 {
-       int result;
        struct device *dev = i2400m_dev(i2400m);
 
        d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
        d_printf(1, dev, "pre-reset shut down\n");
 
-       result = 0;
        mutex_lock(&i2400m->init_mutex);
        if (i2400m->updown) {
                netif_tx_disable(i2400m->wimax_dev.net_dev);
                __i2400m_dev_stop(i2400m);
-               result = 0;
                /* down't set updown to zero -- this way
                 * post_reset can restore properly */
        }
        mutex_unlock(&i2400m->init_mutex);
        if (i2400m->bus_release)
                i2400m->bus_release(i2400m);
-       d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
-       return result;
+       d_fnend(3, dev, "(i2400m %p) = 0\n", i2400m);
+       return 0;
 }
 EXPORT_SYMBOL_GPL(i2400m_pre_reset);
 
index 99b3bfa717d55403b45effb585b0293b46f03ced..d48776e4f343e050787510923bc01e689a2bd9cc 100644 (file)
@@ -365,15 +365,15 @@ static inline unsigned long at76_get_timeout(struct dfu_status *s)
 static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,
                                int manifest_sync_timeout)
 {
-       u8 *block;
-       struct dfu_status dfu_stat_buf;
        int ret = 0;
        int need_dfu_state = 1;
        int is_done = 0;
-       u8 dfu_state = 0;
        u32 dfu_timeout = 0;
        int bsize = 0;
        int blockno = 0;
+       struct dfu_status *dfu_stat_buf = NULL;
+       u8 *dfu_state = NULL;
+       u8 *block = NULL;
 
        at76_dbg(DBG_DFU, "%s( %p, %u, %d)", __func__, buf, size,
                 manifest_sync_timeout);
@@ -383,13 +383,28 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,
                return -EINVAL;
        }
 
+       dfu_stat_buf = kmalloc(sizeof(struct dfu_status), GFP_KERNEL);
+       if (!dfu_stat_buf) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
        block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL);
-       if (!block)
-               return -ENOMEM;
+       if (!block) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       dfu_state = kmalloc(sizeof(u8), GFP_KERNEL);
+       if (!dfu_state) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+       *dfu_state = 0;
 
        do {
                if (need_dfu_state) {
-                       ret = at76_dfu_get_state(udev, &dfu_state);
+                       ret = at76_dfu_get_state(udev, dfu_state);
                        if (ret < 0) {
                                dev_err(&udev->dev,
                                        "cannot get DFU state: %d\n", ret);
@@ -398,13 +413,13 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,
                        need_dfu_state = 0;
                }
 
-               switch (dfu_state) {
+               switch (*dfu_state) {
                case STATE_DFU_DOWNLOAD_SYNC:
                        at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_SYNC");
-                       ret = at76_dfu_get_status(udev, &dfu_stat_buf);
+                       ret = at76_dfu_get_status(udev, dfu_stat_buf);
                        if (ret >= 0) {
-                               dfu_state = dfu_stat_buf.state;
-                               dfu_timeout = at76_get_timeout(&dfu_stat_buf);
+                               *dfu_state = dfu_stat_buf->state;
+                               dfu_timeout = at76_get_timeout(dfu_stat_buf);
                                need_dfu_state = 0;
                        } else
                                dev_err(&udev->dev,
@@ -447,12 +462,12 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,
                case STATE_DFU_MANIFEST_SYNC:
                        at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_SYNC");
 
-                       ret = at76_dfu_get_status(udev, &dfu_stat_buf);
+                       ret = at76_dfu_get_status(udev, dfu_stat_buf);
                        if (ret < 0)
                                break;
 
-                       dfu_state = dfu_stat_buf.state;
-                       dfu_timeout = at76_get_timeout(&dfu_stat_buf);
+                       *dfu_state = dfu_stat_buf->state;
+                       dfu_timeout = at76_get_timeout(dfu_stat_buf);
                        need_dfu_state = 0;
 
                        /* override the timeout from the status response,
@@ -484,14 +499,17 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,
                        break;
 
                default:
-                       at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", dfu_state);
+                       at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", *dfu_state);
                        ret = -EINVAL;
                        break;
                }
        } while (!is_done && (ret >= 0));
 
 exit:
+       kfree(dfu_state);
        kfree(block);
+       kfree(dfu_stat_buf);
+
        if (ret >= 0)
                ret = 0;
 
@@ -1277,6 +1295,7 @@ static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe)
                        dev_err(&udev->dev,
                                "loading %dth firmware block failed: %d\n",
                                blockno, ret);
+                       ret = -EIO;
                        goto exit;
                }
                buf += bsize;
@@ -1410,6 +1429,8 @@ static int at76_startup_device(struct at76_priv *priv)
        /* remove BSSID from previous run */
        memset(priv->bssid, 0, ETH_ALEN);
 
+       priv->scanning = false;
+
        if (at76_set_radio(priv, 1) == 1)
                at76_wait_completion(priv, CMD_RADIO_ON);
 
@@ -1483,6 +1504,52 @@ static void at76_work_submit_rx(struct work_struct *work)
        mutex_unlock(&priv->mtx);
 }
 
+/* This is a workaround to make scan working:
+ * currently mac80211 does not process frames with no frequency
+ * information.
+ * However during scan the HW performs a sweep by itself, and we
+ * are unable to know where the radio is actually tuned.
+ * This function tries to do its best to guess this information..
+ * During scan, If the current frame is a beacon or a probe response,
+ * the channel information is extracted from it.
+ * When not scanning, for other frames, or if it happens that for
+ * whatever reason we fail to parse beacons and probe responses, this
+ * function returns the priv->channel information, that should be correct
+ * at least when we are not scanning.
+ */
+static inline int at76_guess_freq(struct at76_priv *priv)
+{
+       size_t el_off;
+       const u8 *el;
+       int channel = priv->channel;
+       int len = priv->rx_skb->len;
+       struct ieee80211_hdr *hdr = (void *)priv->rx_skb->data;
+
+       if (!priv->scanning)
+               goto exit;
+
+       if (len < 24)
+               goto exit;
+
+       if (ieee80211_is_probe_resp(hdr->frame_control)) {
+               el_off = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+               el = ((struct ieee80211_mgmt *)hdr)->u.probe_resp.variable;
+       } else if (ieee80211_is_beacon(hdr->frame_control)) {
+               el_off = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+               el = ((struct ieee80211_mgmt *)hdr)->u.beacon.variable;
+       } else {
+               goto exit;
+       }
+       len -= el_off;
+
+       el = cfg80211_find_ie(WLAN_EID_DS_PARAMS, el, len);
+       if (el && el[1] > 0)
+               channel = el[2];
+
+exit:
+       return ieee80211_channel_to_frequency(channel, IEEE80211_BAND_2GHZ);
+}
+
 static void at76_rx_tasklet(unsigned long param)
 {
        struct urb *urb = (struct urb *)param;
@@ -1523,6 +1590,8 @@ static void at76_rx_tasklet(unsigned long param)
        rx_status.signal = buf->rssi;
        rx_status.flag |= RX_FLAG_DECRYPTED;
        rx_status.flag |= RX_FLAG_IV_STRIPPED;
+       rx_status.band = IEEE80211_BAND_2GHZ;
+       rx_status.freq = at76_guess_freq(priv);
 
        at76_dbg(DBG_MAC80211, "calling ieee80211_rx_irqsafe(): %d/%d",
                 priv->rx_skb->len, priv->rx_skb->data_len);
@@ -1875,6 +1944,8 @@ static void at76_dwork_hw_scan(struct work_struct *work)
        if (is_valid_ether_addr(priv->bssid))
                at76_join(priv);
 
+       priv->scanning = false;
+
        mutex_unlock(&priv->mtx);
 
        ieee80211_scan_completed(priv->hw, false);
@@ -1929,6 +2000,7 @@ static int at76_hw_scan(struct ieee80211_hw *hw,
                goto exit;
        }
 
+       priv->scanning = true;
        ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan,
                                     SCAN_POLL_INTERVAL);
 
@@ -2020,6 +2092,44 @@ static void at76_configure_filter(struct ieee80211_hw *hw,
        ieee80211_queue_work(hw, &priv->work_set_promisc);
 }
 
+static int at76_set_wep(struct at76_priv *priv)
+{
+       int ret = 0;
+       struct mib_mac_wep *mib_data = &priv->mib_buf.data.wep_mib;
+
+       priv->mib_buf.type = MIB_MAC_WEP;
+       priv->mib_buf.size = sizeof(struct mib_mac_wep);
+       priv->mib_buf.index = 0;
+
+       memset(mib_data, 0, sizeof(*mib_data));
+
+       if (priv->wep_enabled) {
+               if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN)
+                       mib_data->encryption_level = 2;
+               else
+                       mib_data->encryption_level = 1;
+
+               /* always exclude unencrypted if WEP is active */
+               mib_data->exclude_unencrypted = 1;
+       } else {
+               mib_data->exclude_unencrypted = 0;
+               mib_data->encryption_level = 0;
+       }
+
+       mib_data->privacy_invoked = priv->wep_enabled;
+       mib_data->wep_default_key_id = priv->wep_key_id;
+       memcpy(mib_data->wep_default_keyvalue, priv->wep_keys,
+              sizeof(priv->wep_keys));
+
+       ret = at76_set_mib(priv, &priv->mib_buf);
+
+       if (ret < 0)
+               wiphy_err(priv->hw->wiphy,
+                         "set_mib (wep) failed: %d\n", ret);
+
+       return ret;
+}
+
 static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                        struct ieee80211_vif *vif, struct ieee80211_sta *sta,
                        struct ieee80211_key_conf *key)
@@ -2062,7 +2172,7 @@ static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                        priv->wep_enabled = 1;
        }
 
-       at76_startup_device(priv);
+       at76_set_wep(priv);
 
        mutex_unlock(&priv->mtx);
 
@@ -2330,16 +2440,22 @@ static int at76_probe(struct usb_interface *interface,
        struct usb_device *udev;
        int op_mode;
        int need_ext_fw = 0;
-       struct mib_fw_version fwv;
+       struct mib_fw_version *fwv = NULL;
        int board_type = (int)id->driver_info;
 
        udev = usb_get_dev(interface_to_usbdev(interface));
 
+       fwv = kmalloc(sizeof(*fwv), GFP_KERNEL);
+       if (!fwv) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
        /* Load firmware into kernel memory */
        fwe = at76_load_firmware(udev, board_type);
        if (!fwe) {
                ret = -ENOENT;
-               goto error;
+               goto exit;
        }
 
        op_mode = at76_get_op_mode(udev);
@@ -2353,7 +2469,7 @@ static int at76_probe(struct usb_interface *interface,
                dev_err(&interface->dev,
                        "cannot handle a device in HW_CONFIG_MODE\n");
                ret = -EBUSY;
-               goto error;
+               goto exit;
        }
 
        if (op_mode != OPMODE_NORMAL_NIC_WITH_FLASH
@@ -2366,10 +2482,10 @@ static int at76_probe(struct usb_interface *interface,
                        dev_err(&interface->dev,
                                "error %d downloading internal firmware\n",
                                ret);
-                       goto error;
+                       goto exit;
                }
                usb_put_dev(udev);
-               return ret;
+               goto exit;
        }
 
        /* Internal firmware already inside the device.  Get firmware
@@ -2382,8 +2498,8 @@ static int at76_probe(struct usb_interface *interface,
         * query the device for the fw version */
        if ((fwe->fw_version.major > 0 || fwe->fw_version.minor >= 100)
            || (op_mode == OPMODE_NORMAL_NIC_WITH_FLASH)) {
-               ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv));
-               if (ret < 0 || (fwv.major | fwv.minor) == 0)
+               ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv));
+               if (ret < 0 || (fwv->major | fwv->minor) == 0)
                        need_ext_fw = 1;
        } else
                /* No way to check firmware version, reload to be sure */
@@ -2394,37 +2510,37 @@ static int at76_probe(struct usb_interface *interface,
                           "downloading external firmware\n");
 
                ret = at76_load_external_fw(udev, fwe);
-               if (ret)
-                       goto error;
+               if (ret < 0)
+                       goto exit;
 
                /* Re-check firmware version */
-               ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv));
+               ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv));
                if (ret < 0) {
                        dev_err(&interface->dev,
                                "error %d getting firmware version\n", ret);
-                       goto error;
+                       goto exit;
                }
        }
 
        priv = at76_alloc_new_device(udev);
        if (!priv) {
                ret = -ENOMEM;
-               goto error;
+               goto exit;
        }
 
        usb_set_intfdata(interface, priv);
 
-       memcpy(&priv->fw_version, &fwv, sizeof(struct mib_fw_version));
+       memcpy(&priv->fw_version, fwv, sizeof(struct mib_fw_version));
        priv->board_type = board_type;
 
        ret = at76_init_new_device(priv, interface);
        if (ret < 0)
                at76_delete_device(priv);
 
-       return ret;
-
-error:
-       usb_put_dev(udev);
+exit:
+       kfree(fwv);
+       if (ret < 0)
+               usb_put_dev(udev);
        return ret;
 }
 
index f14a65473fe8466062bf70a156dd620038787fb5..55090a38ac9549eb546a747144b9f274ae1b2faf 100644 (file)
@@ -219,18 +219,6 @@ struct at76_req_join {
        u8 reserved;
 } __packed;
 
-struct set_mib_buffer {
-       u8 type;
-       u8 size;
-       u8 index;
-       u8 reserved;
-       union {
-               u8 byte;
-               __le16 word;
-               u8 addr[ETH_ALEN];
-       } data;
-} __packed;
-
 struct mib_local {
        u16 reserved0;
        u8 beacon_enable;
@@ -334,6 +322,19 @@ struct mib_mdomain {
        u8 channel_list[14];    /* 0 for invalid channels */
 } __packed;
 
+struct set_mib_buffer {
+       u8 type;
+       u8 size;
+       u8 index;
+       u8 reserved;
+       union {
+               u8 byte;
+               __le16 word;
+               u8 addr[ETH_ALEN];
+               struct mib_mac_wep wep_mib;
+       } data;
+} __packed;
+
 struct at76_fw_header {
        __le32 crc;             /* CRC32 of the whole image */
        __le32 board_type;      /* firmware compatibility code */
@@ -417,6 +418,7 @@ struct at76_priv {
        int scan_max_time;      /* scan max channel time */
        int scan_mode;          /* SCAN_TYPE_ACTIVE, SCAN_TYPE_PASSIVE */
        int scan_need_any;      /* if set, need to scan for any ESSID */
+       bool scanning;          /* if set, the scan is running */
 
        u16 assoc_id;           /* current association ID, if associated */
 
index 507d9a9ee69ad4b61ece2d334434691801682efe..f92050617ae682e02bb48b6676a16298ae2dfa4f 100644 (file)
@@ -1090,7 +1090,8 @@ static int ar5523_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
        return ret;
 }
 
-static void ar5523_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static void ar5523_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                        u32 queues, bool drop)
 {
        struct ar5523 *ar = hw->priv;
 
index a1f0996288508e3cad8ecd8e03f33153980e593e..17d221abd58c0bb70d72a6fbf14f1e7422f8ced5 100644 (file)
@@ -175,7 +175,7 @@ int ath10k_bmi_write_memory(struct ath10k *ar,
        return 0;
 }
 
-int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param)
+int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result)
 {
        struct bmi_cmd cmd;
        union bmi_resp resp;
@@ -184,7 +184,7 @@ int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param)
        int ret;
 
        ath10k_dbg(ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n",
-                  address, *param);
+                  address, param);
 
        if (ar->bmi.done_sent) {
                ath10k_warn("command disallowed\n");
@@ -193,7 +193,7 @@ int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param)
 
        cmd.id            = __cpu_to_le32(BMI_EXECUTE);
        cmd.execute.addr  = __cpu_to_le32(address);
-       cmd.execute.param = __cpu_to_le32(*param);
+       cmd.execute.param = __cpu_to_le32(param);
 
        ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
        if (ret) {
@@ -204,10 +204,13 @@ int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param)
        if (resplen < sizeof(resp.execute)) {
                ath10k_warn("invalid execute response length (%d)\n",
                            resplen);
-               return ret;
+               return -EIO;
        }
 
-       *param = __le32_to_cpu(resp.execute.result);
+       *result = __le32_to_cpu(resp.execute.result);
+
+       ath10k_dbg(ATH10K_DBG_BMI, "bmi execute result 0x%x\n", *result);
+
        return 0;
 }
 
index 8d81ce1cec216c7b55fa1c0ab47b65cc41cafb0d..111ab701465c0e2dfa731b3e5409278151545a3a 100644 (file)
@@ -201,7 +201,8 @@ int ath10k_bmi_write_memory(struct ath10k *ar, u32 address,
                                                                        \
                addr = host_interest_item_address(HI_ITEM(item));       \
                ret = ath10k_bmi_read_memory(ar, addr, (u8 *)&tmp, 4); \
-               *val = __le32_to_cpu(tmp);                              \
+               if (!ret)                                               \
+                       *val = __le32_to_cpu(tmp);                      \
                ret;                                                    \
         })
 
@@ -217,7 +218,7 @@ int ath10k_bmi_write_memory(struct ath10k *ar, u32 address,
                ret;                                                    \
        })
 
-int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param);
+int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result);
 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,
index a79499c8235009f701073c83b3974d66d183e001..d185dc0cd12b2f739535a6bc6fe77020b0764133 100644 (file)
@@ -329,6 +329,33 @@ exit:
        return ret;
 }
 
+void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe)
+{
+       struct ath10k *ar = pipe->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ath10k_ce_ring *src_ring = pipe->src_ring;
+       u32 ctrl_addr = pipe->ctrl_addr;
+
+       lockdep_assert_held(&ar_pci->ce_lock);
+
+       /*
+        * This function must be called only if there is an incomplete
+        * scatter-gather transfer (before index register is updated)
+        * that needs to be cleaned up.
+        */
+       if (WARN_ON_ONCE(src_ring->write_index == src_ring->sw_index))
+               return;
+
+       if (WARN_ON_ONCE(src_ring->write_index ==
+                        ath10k_ce_src_ring_write_index_get(ar, ctrl_addr)))
+               return;
+
+       src_ring->write_index--;
+       src_ring->write_index &= src_ring->nentries_mask;
+
+       src_ring->per_transfer_context[src_ring->write_index] = NULL;
+}
+
 int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
                   void *per_transfer_context,
                   u32 buffer,
@@ -840,35 +867,17 @@ void ath10k_ce_recv_cb_register(struct ath10k_ce_pipe *ce_state,
 
 static int ath10k_ce_init_src_ring(struct ath10k *ar,
                                   unsigned int ce_id,
-                                  struct ath10k_ce_pipe *ce_state,
                                   const struct ce_attr *attr)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       struct ath10k_ce_ring *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 ath10k_ce_ring) + (nentries * sizeof(void *));
-       ptr = kzalloc(ce_nbytes, GFP_KERNEL);
-       if (ptr == NULL)
-               return -ENOMEM;
+       struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
+       struct ath10k_ce_ring *src_ring = ce_state->src_ring;
+       u32 nentries, ctrl_addr = ath10k_ce_base_address(ce_id);
 
-       ce_state->src_ring = (struct ath10k_ce_ring *)ptr;
-       src_ring = ce_state->src_ring;
+       nentries = roundup_pow_of_two(attr->src_nentries);
 
-       ptr += sizeof(struct ath10k_ce_ring);
-       src_ring->nentries = nentries;
-       src_ring->nentries_mask = nentries - 1;
+       memset(src_ring->per_transfer_context, 0,
+              nentries * sizeof(*src_ring->per_transfer_context));
 
        src_ring->sw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
        src_ring->sw_index &= src_ring->nentries_mask;
@@ -878,21 +887,87 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar,
                ath10k_ce_src_ring_write_index_get(ar, ctrl_addr);
        src_ring->write_index &= src_ring->nentries_mask;
 
-       src_ring->per_transfer_context = (void **)ptr;
+       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_dbg(ATH10K_DBG_BOOT,
+                  "boot init ce src ring id %d entries %d base_addr %p\n",
+                  ce_id, nentries, src_ring->base_addr_owner_space);
+
+       return 0;
+}
+
+static int ath10k_ce_init_dest_ring(struct ath10k *ar,
+                                   unsigned int ce_id,
+                                   const struct ce_attr *attr)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
+       struct ath10k_ce_ring *dest_ring = ce_state->dest_ring;
+       u32 nentries, ctrl_addr = ath10k_ce_base_address(ce_id);
+
+       nentries = roundup_pow_of_two(attr->dest_nentries);
+
+       memset(dest_ring->per_transfer_context, 0,
+              nentries * sizeof(*dest_ring->per_transfer_context));
+
+       dest_ring->sw_index = ath10k_ce_dest_ring_read_index_get(ar, ctrl_addr);
+       dest_ring->sw_index &= dest_ring->nentries_mask;
+       dest_ring->write_index =
+               ath10k_ce_dest_ring_write_index_get(ar, ctrl_addr);
+       dest_ring->write_index &= dest_ring->nentries_mask;
+
+       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_dbg(ATH10K_DBG_BOOT,
+                  "boot ce dest ring id %d entries %d base_addr %p\n",
+                  ce_id, nentries, dest_ring->base_addr_owner_space);
+
+       return 0;
+}
+
+static struct ath10k_ce_ring *
+ath10k_ce_alloc_src_ring(struct ath10k *ar, unsigned int ce_id,
+                        const struct ce_attr *attr)
+{
+       struct ath10k_ce_ring *src_ring;
+       u32 nentries = attr->src_nentries;
+       dma_addr_t base_addr;
+
+       nentries = roundup_pow_of_two(nentries);
+
+       src_ring = kzalloc(sizeof(*src_ring) +
+                          (nentries *
+                           sizeof(*src_ring->per_transfer_context)),
+                          GFP_KERNEL);
+       if (src_ring == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       src_ring->nentries = nentries;
+       src_ring->nentries_mask = nentries - 1;
 
        /*
         * 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);
+               dma_alloc_coherent(ar->dev,
+                                  (nentries * sizeof(struct ce_desc) +
+                                   CE_DESC_RING_ALIGN),
+                                  &base_addr, GFP_KERNEL);
        if (!src_ring->base_addr_owner_space_unaligned) {
-               kfree(ce_state->src_ring);
-               ce_state->src_ring = NULL;
-               return -ENOMEM;
+               kfree(src_ring);
+               return ERR_PTR(-ENOMEM);
        }
 
        src_ring->base_addr_ce_space_unaligned = base_addr;
@@ -912,88 +987,54 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar,
                kmalloc((nentries * sizeof(struct ce_desc) +
                         CE_DESC_RING_ALIGN), GFP_KERNEL);
        if (!src_ring->shadow_base_unaligned) {
-               pci_free_consistent(ar_pci->pdev,
-                                   (nentries * sizeof(struct ce_desc) +
-                                    CE_DESC_RING_ALIGN),
-                                   src_ring->base_addr_owner_space,
-                                   src_ring->base_addr_ce_space);
-               kfree(ce_state->src_ring);
-               ce_state->src_ring = NULL;
-               return -ENOMEM;
+               dma_free_coherent(ar->dev,
+                                 (nentries * sizeof(struct ce_desc) +
+                                  CE_DESC_RING_ALIGN),
+                                 src_ring->base_addr_owner_space,
+                                 src_ring->base_addr_ce_space);
+               kfree(src_ring);
+               return ERR_PTR(-ENOMEM);
        }
 
        src_ring->shadow_base = PTR_ALIGN(
                        src_ring->shadow_base_unaligned,
                        CE_DESC_RING_ALIGN);
 
-       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_dbg(ATH10K_DBG_BOOT,
-                  "boot ce src ring id %d entries %d base_addr %p\n",
-                  ce_id, nentries, src_ring->base_addr_owner_space);
-
-       return 0;
+       return src_ring;
 }
 
-static int ath10k_ce_init_dest_ring(struct ath10k *ar,
-                                   unsigned int ce_id,
-                                   struct ath10k_ce_pipe *ce_state,
-                                   const struct ce_attr *attr)
+static struct ath10k_ce_ring *
+ath10k_ce_alloc_dest_ring(struct ath10k *ar, unsigned int ce_id,
+                         const struct ce_attr *attr)
 {
-       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        struct ath10k_ce_ring *dest_ring;
-       unsigned int nentries = attr->dest_nentries;
-       unsigned int ce_nbytes;
-       u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+       u32 nentries;
        dma_addr_t base_addr;
-       char *ptr;
 
-       nentries = roundup_pow_of_two(nentries);
+       nentries = roundup_pow_of_two(attr->dest_nentries);
 
-       if (ce_state->dest_ring) {
-               WARN_ON(ce_state->dest_ring->nentries != nentries);
-               return 0;
-       }
-
-       ce_nbytes = sizeof(struct ath10k_ce_ring) + (nentries * sizeof(void *));
-       ptr = kzalloc(ce_nbytes, GFP_KERNEL);
-       if (ptr == NULL)
-               return -ENOMEM;
+       dest_ring = kzalloc(sizeof(*dest_ring) +
+                           (nentries *
+                            sizeof(*dest_ring->per_transfer_context)),
+                           GFP_KERNEL);
+       if (dest_ring == NULL)
+               return ERR_PTR(-ENOMEM);
 
-       ce_state->dest_ring = (struct ath10k_ce_ring *)ptr;
-       dest_ring = ce_state->dest_ring;
-
-       ptr += sizeof(struct ath10k_ce_ring);
        dest_ring->nentries = nentries;
        dest_ring->nentries_mask = nentries - 1;
 
-       dest_ring->sw_index = ath10k_ce_dest_ring_read_index_get(ar, ctrl_addr);
-       dest_ring->sw_index &= dest_ring->nentries_mask;
-       dest_ring->write_index =
-               ath10k_ce_dest_ring_write_index_get(ar, ctrl_addr);
-       dest_ring->write_index &= dest_ring->nentries_mask;
-
-       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);
+               dma_alloc_coherent(ar->dev,
+                                  (nentries * sizeof(struct ce_desc) +
+                                   CE_DESC_RING_ALIGN),
+                                  &base_addr, GFP_KERNEL);
        if (!dest_ring->base_addr_owner_space_unaligned) {
-               kfree(ce_state->dest_ring);
-               ce_state->dest_ring = NULL;
-               return -ENOMEM;
+               kfree(dest_ring);
+               return ERR_PTR(-ENOMEM);
        }
 
        dest_ring->base_addr_ce_space_unaligned = base_addr;
@@ -1012,39 +1053,7 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar,
                        dest_ring->base_addr_ce_space_unaligned,
                        CE_DESC_RING_ALIGN);
 
-       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_dbg(ATH10K_DBG_BOOT,
-                  "boot ce dest ring id %d entries %d base_addr %p\n",
-                  ce_id, nentries, dest_ring->base_addr_owner_space);
-
-       return 0;
-}
-
-static struct ath10k_ce_pipe *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 ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
-       u32 ctrl_addr = ath10k_ce_base_address(ce_id);
-
-       spin_lock_bh(&ar_pci->ce_lock);
-
-       ce_state->ar = ar;
-       ce_state->id = ce_id;
-       ce_state->ctrl_addr = ctrl_addr;
-       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;
+       return dest_ring;
 }
 
 /*
@@ -1054,11 +1063,11 @@ static struct ath10k_ce_pipe *ath10k_ce_init_state(struct ath10k *ar,
  * initialization. It may be that only one side or the other is
  * initialized by software/firmware.
  */
-struct ath10k_ce_pipe *ath10k_ce_init(struct ath10k *ar,
-                               unsigned int ce_id,
-                               const struct ce_attr *attr)
+int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id,
+                       const struct ce_attr *attr)
 {
-       struct ath10k_ce_pipe *ce_state;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
        int ret;
 
        /*
@@ -1074,64 +1083,128 @@ struct ath10k_ce_pipe *ath10k_ce_init(struct ath10k *ar,
 
        ret = ath10k_pci_wake(ar);
        if (ret)
-               return NULL;
+               return ret;
 
-       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);
-               goto out;
-       }
+       spin_lock_bh(&ar_pci->ce_lock);
+       ce_state->ar = ar;
+       ce_state->id = ce_id;
+       ce_state->ctrl_addr = ath10k_ce_base_address(ce_id);
+       ce_state->attr_flags = attr->flags;
+       ce_state->src_sz_max = attr->src_sz_max;
+       spin_unlock_bh(&ar_pci->ce_lock);
 
        if (attr->src_nentries) {
-               ret = ath10k_ce_init_src_ring(ar, ce_id, ce_state, attr);
+               ret = ath10k_ce_init_src_ring(ar, ce_id, attr);
                if (ret) {
                        ath10k_err("Failed to initialize CE src ring for ID: %d (%d)\n",
                                   ce_id, ret);
-                       ath10k_ce_deinit(ce_state);
-                       ce_state = NULL;
                        goto out;
                }
        }
 
        if (attr->dest_nentries) {
-               ret = ath10k_ce_init_dest_ring(ar, ce_id, ce_state, attr);
+               ret = ath10k_ce_init_dest_ring(ar, ce_id, attr);
                if (ret) {
                        ath10k_err("Failed to initialize CE dest ring for ID: %d (%d)\n",
                                   ce_id, ret);
-                       ath10k_ce_deinit(ce_state);
-                       ce_state = NULL;
                        goto out;
                }
        }
 
 out:
        ath10k_pci_sleep(ar);
-       return ce_state;
+       return ret;
 }
 
-void ath10k_ce_deinit(struct ath10k_ce_pipe *ce_state)
+static void ath10k_ce_deinit_src_ring(struct ath10k *ar, unsigned int ce_id)
+{
+       u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+
+       ath10k_ce_src_ring_base_addr_set(ar, ctrl_addr, 0);
+       ath10k_ce_src_ring_size_set(ar, ctrl_addr, 0);
+       ath10k_ce_src_ring_dmax_set(ar, ctrl_addr, 0);
+       ath10k_ce_src_ring_highmark_set(ar, ctrl_addr, 0);
+}
+
+static void ath10k_ce_deinit_dest_ring(struct ath10k *ar, unsigned int ce_id)
+{
+       u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+
+       ath10k_ce_dest_ring_base_addr_set(ar, ctrl_addr, 0);
+       ath10k_ce_dest_ring_size_set(ar, ctrl_addr, 0);
+       ath10k_ce_dest_ring_highmark_set(ar, ctrl_addr, 0);
+}
+
+void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id)
+{
+       int ret;
+
+       ret = ath10k_pci_wake(ar);
+       if (ret)
+               return;
+
+       ath10k_ce_deinit_src_ring(ar, ce_id);
+       ath10k_ce_deinit_dest_ring(ar, ce_id);
+
+       ath10k_pci_sleep(ar);
+}
+
+int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
+                        const struct ce_attr *attr)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
+       int ret;
+
+       if (attr->src_nentries) {
+               ce_state->src_ring = ath10k_ce_alloc_src_ring(ar, ce_id, attr);
+               if (IS_ERR(ce_state->src_ring)) {
+                       ret = PTR_ERR(ce_state->src_ring);
+                       ath10k_err("failed to allocate copy engine source ring %d: %d\n",
+                                  ce_id, ret);
+                       ce_state->src_ring = NULL;
+                       return ret;
+               }
+       }
+
+       if (attr->dest_nentries) {
+               ce_state->dest_ring = ath10k_ce_alloc_dest_ring(ar, ce_id,
+                                                               attr);
+               if (IS_ERR(ce_state->dest_ring)) {
+                       ret = PTR_ERR(ce_state->dest_ring);
+                       ath10k_err("failed to allocate copy engine destination ring %d: %d\n",
+                                  ce_id, ret);
+                       ce_state->dest_ring = NULL;
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id)
 {
-       struct ath10k *ar = ce_state->ar;
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
 
        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);
+               dma_free_coherent(ar->dev,
+                                 (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);
+               dma_free_coherent(ar->dev,
+                                 (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);
        }
 
index 8eb7f99ed992277b0efb3e7ae4f971b8e4eb7557..7a5a36fc59c1ad19c51e2c69496660f75aeca073 100644 (file)
@@ -104,7 +104,8 @@ struct ath10k_ce_ring {
        void *shadow_base_unaligned;
        struct ce_desc *shadow_base;
 
-       void **per_transfer_context;
+       /* keep last */
+       void *per_transfer_context[0];
 };
 
 struct ath10k_ce_pipe {
@@ -159,6 +160,8 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
                          unsigned int transfer_id,
                          unsigned int flags);
 
+void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe);
+
 void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state,
                                void (*send_cb)(struct ath10k_ce_pipe *),
                                int disable_interrupts);
@@ -210,10 +213,12 @@ int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state,
 
 /*==================CE Engine Initialization=======================*/
 
-/* Initialize an instance of a CE */
-struct ath10k_ce_pipe *ath10k_ce_init(struct ath10k *ar,
-                               unsigned int ce_id,
-                               const struct ce_attr *attr);
+int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id,
+                       const struct ce_attr *attr);
+void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id);
+int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
+                         const struct ce_attr *attr);
+void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id);
 
 /*==================CE Engine Shutdown=======================*/
 /*
@@ -236,8 +241,6 @@ int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state,
                               unsigned int *nbytesp,
                               unsigned int *transfer_idp);
 
-void ath10k_ce_deinit(struct ath10k_ce_pipe *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);
index ebc5fc2ede75cbac75da3a2f789a946d4e1274ad..82017f56e6613a484b224a2d9d41f8aa796f16c9 100644 (file)
@@ -58,36 +58,6 @@ static void ath10k_send_suspend_complete(struct ath10k *ar)
        complete(&ar->target_suspend);
 }
 
-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_BOOT, "boot 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;
@@ -249,30 +219,40 @@ exit:
 
 static int ath10k_download_and_run_otp(struct ath10k *ar)
 {
-       u32 address = ar->hw_params.patch_load_addr;
-       u32 exec_param;
+       u32 result, address = ar->hw_params.patch_load_addr;
        int ret;
 
        /* OTP is optional */
 
-       if (!ar->otp_data || !ar->otp_len)
+       if (!ar->otp_data || !ar->otp_len) {
+               ath10k_warn("Not running otp, calibration will be incorrect (otp-data %p otp_len %zd)!\n",
+                           ar->otp_data, ar->otp_len);
                return 0;
+       }
+
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot upload otp to 0x%x len %zd\n",
+                  address, ar->otp_len);
 
        ret = ath10k_bmi_fast_download(ar, address, ar->otp_data, ar->otp_len);
        if (ret) {
                ath10k_err("could not write otp (%d)\n", ret);
-               goto exit;
+               return ret;
        }
 
-       exec_param = 0;
-       ret = ath10k_bmi_execute(ar, address, &exec_param);
+       ret = ath10k_bmi_execute(ar, address, 0, &result);
        if (ret) {
                ath10k_err("could not execute otp (%d)\n", ret);
-               goto exit;
+               return ret;
        }
 
-exit:
-       return ret;
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot otp execute result %d\n", result);
+
+       if (result != 0) {
+               ath10k_err("otp calibration failed: %d", result);
+               return -EINVAL;
+       }
+
+       return 0;
 }
 
 static int ath10k_download_fw(struct ath10k *ar)
@@ -389,8 +369,8 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name)
        /* first fetch the firmware file (firmware-*.bin) */
        ar->firmware = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, name);
        if (IS_ERR(ar->firmware)) {
-               ath10k_err("Could not fetch firmware file '%s': %ld\n",
-                          name, PTR_ERR(ar->firmware));
+               ath10k_err("could not fetch firmware file '%s/%s': %ld\n",
+                          ar->hw_params.fw.dir, name, PTR_ERR(ar->firmware));
                return PTR_ERR(ar->firmware);
        }
 
@@ -401,14 +381,14 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name)
        magic_len = strlen(ATH10K_FIRMWARE_MAGIC) + 1;
 
        if (len < magic_len) {
-               ath10k_err("firmware image too small to contain magic: %zu\n",
-                          len);
+               ath10k_err("firmware file '%s/%s' too small to contain magic: %zu\n",
+                          ar->hw_params.fw.dir, name, len);
                ret = -EINVAL;
                goto err;
        }
 
        if (memcmp(data, ATH10K_FIRMWARE_MAGIC, magic_len) != 0) {
-               ath10k_err("Invalid firmware magic\n");
+               ath10k_err("invalid firmware magic\n");
                ret = -EINVAL;
                goto err;
        }
@@ -430,7 +410,7 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name)
                data += sizeof(*hdr);
 
                if (len < ie_len) {
-                       ath10k_err("Invalid length for FW IE %d (%zu < %zu)\n",
+                       ath10k_err("invalid length for FW IE %d (%zu < %zu)\n",
                                   ie_id, len, ie_len);
                        ret = -EINVAL;
                        goto err;
@@ -513,8 +493,8 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name)
        }
 
        if (!ar->firmware_data || !ar->firmware_len) {
-               ath10k_warn("No ATH10K_FW_IE_FW_IMAGE found from %s, skipping\n",
-                           name);
+               ath10k_warn("No ATH10K_FW_IE_FW_IMAGE found from '%s/%s', skipping\n",
+                           ar->hw_params.fw.dir, name);
                ret = -ENOMEDIUM;
                goto err;
        }
@@ -531,7 +511,9 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name)
                                         ar->hw_params.fw.board);
        if (IS_ERR(ar->board)) {
                ret = PTR_ERR(ar->board);
-               ath10k_err("could not fetch board data (%d)\n", ret);
+               ath10k_err("could not fetch board data '%s/%s' (%d)\n",
+                          ar->hw_params.fw.dir, ar->hw_params.fw.board,
+                          ret);
                goto err;
        }
 
@@ -549,19 +531,21 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
 {
        int ret;
 
+       ar->fw_api = 2;
+       ath10k_dbg(ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api);
+
        ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_API2_FILE);
-       if (ret == 0) {
-               ar->fw_api = 2;
-               goto out;
-       }
+       if (ret == 0)
+               goto success;
+
+       ar->fw_api = 1;
+       ath10k_dbg(ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api);
 
        ret = ath10k_core_fetch_firmware_api_1(ar);
        if (ret)
                return ret;
 
-       ar->fw_api = 1;
-
-out:
+success:
        ath10k_dbg(ATH10K_DBG_BOOT, "using fw api %d\n", ar->fw_api);
 
        return 0;
@@ -572,16 +556,22 @@ static int ath10k_init_download_firmware(struct ath10k *ar)
        int ret;
 
        ret = ath10k_download_board_data(ar);
-       if (ret)
+       if (ret) {
+               ath10k_err("failed to download board data: %d\n", ret);
                return ret;
+       }
 
        ret = ath10k_download_and_run_otp(ar);
-       if (ret)
+       if (ret) {
+               ath10k_err("failed to run otp: %d\n", ret);
                return ret;
+       }
 
        ret = ath10k_download_fw(ar);
-       if (ret)
+       if (ret) {
+               ath10k_err("failed to download firmware: %d\n", ret);
                return ret;
+       }
 
        return ret;
 }
@@ -660,8 +650,9 @@ static void ath10k_core_restart(struct work_struct *work)
 
        switch (ar->state) {
        case ATH10K_STATE_ON:
-               ath10k_halt(ar);
                ar->state = ATH10K_STATE_RESTARTING;
+               del_timer_sync(&ar->scan.timeout);
+               ath10k_reset_scan((unsigned long)ar);
                ieee80211_restart_hw(ar->hw);
                break;
        case ATH10K_STATE_OFF:
@@ -670,6 +661,8 @@ static void ath10k_core_restart(struct work_struct *work)
                ath10k_warn("cannot restart a device that hasn't been started\n");
                break;
        case ATH10K_STATE_RESTARTING:
+               /* hw restart might be requested from multiple places */
+               break;
        case ATH10K_STATE_RESTARTED:
                ar->state = ATH10K_STATE_WEDGED;
                /* fall through */
@@ -681,70 +674,6 @@ static void ath10k_core_restart(struct work_struct *work)
        mutex_unlock(&ar->conf_mutex);
 }
 
-struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
-                                 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;
-
-       init_completion(&ar->scan.started);
-       init_completion(&ar->scan.completed);
-       init_completion(&ar->scan.on_channel);
-       init_completion(&ar->target_suspend);
-
-       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_WORK(&ar->wmi_mgmt_tx_work, ath10k_mgmt_over_wmi_tx_work);
-       skb_queue_head_init(&ar->wmi_mgmt_tx_queue);
-
-       INIT_WORK(&ar->restart_work, ath10k_core_restart);
-
-       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_start(struct ath10k *ar)
 {
        int status;
@@ -785,10 +714,28 @@ int ath10k_core_start(struct ath10k *ar)
                goto err;
        }
 
+       status = ath10k_htt_init(ar);
+       if (status) {
+               ath10k_err("failed to init htt: %d\n", status);
+               goto err_wmi_detach;
+       }
+
+       status = ath10k_htt_tx_alloc(&ar->htt);
+       if (status) {
+               ath10k_err("failed to alloc htt tx: %d\n", status);
+               goto err_wmi_detach;
+       }
+
+       status = ath10k_htt_rx_alloc(&ar->htt);
+       if (status) {
+               ath10k_err("failed to alloc htt rx: %d\n", status);
+               goto err_htt_tx_detach;
+       }
+
        status = ath10k_hif_start(ar);
        if (status) {
                ath10k_err("could not start HIF: %d\n", status);
-               goto err_wmi_detach;
+               goto err_htt_rx_detach;
        }
 
        status = ath10k_htc_wait_target(&ar->htc);
@@ -797,15 +744,30 @@ int ath10k_core_start(struct ath10k *ar)
                goto err_hif_stop;
        }
 
-       status = ath10k_htt_attach(ar);
+       status = ath10k_htt_connect(&ar->htt);
        if (status) {
-               ath10k_err("could not attach htt (%d)\n", status);
+               ath10k_err("failed to connect htt (%d)\n", status);
                goto err_hif_stop;
        }
 
-       status = ath10k_init_connect_htc(ar);
-       if (status)
-               goto err_htt_detach;
+       status = ath10k_wmi_connect(ar);
+       if (status) {
+               ath10k_err("could not connect wmi: %d\n", status);
+               goto err_hif_stop;
+       }
+
+       status = ath10k_htc_start(&ar->htc);
+       if (status) {
+               ath10k_err("failed to start htc: %d\n", status);
+               goto err_hif_stop;
+       }
+
+       status = ath10k_wmi_wait_for_service_ready(ar);
+       if (status <= 0) {
+               ath10k_warn("wmi service ready event not received");
+               status = -ETIMEDOUT;
+               goto err_htc_stop;
+       }
 
        ath10k_dbg(ATH10K_DBG_BOOT, "firmware %s booted\n",
                   ar->hw->wiphy->fw_version);
@@ -813,31 +775,36 @@ int ath10k_core_start(struct ath10k *ar)
        status = ath10k_wmi_cmd_init(ar);
        if (status) {
                ath10k_err("could not send WMI init command (%d)\n", status);
-               goto err_disconnect_htc;
+               goto err_htc_stop;
        }
 
        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;
+               goto err_htc_stop;
        }
 
-       status = ath10k_htt_attach_target(&ar->htt);
-       if (status)
-               goto err_disconnect_htc;
+       status = ath10k_htt_setup(&ar->htt);
+       if (status) {
+               ath10k_err("failed to setup htt: %d\n", status);
+               goto err_htc_stop;
+       }
 
        status = ath10k_debug_start(ar);
        if (status)
-               goto err_disconnect_htc;
+               goto err_htc_stop;
 
        ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
        INIT_LIST_HEAD(&ar->arvifs);
 
        if (!test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags))
-               ath10k_info("%s (0x%x) fw %s api %d htt %d.%d\n",
-                           ar->hw_params.name, ar->target_version,
-                           ar->hw->wiphy->fw_version, ar->fw_api,
+               ath10k_info("%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d\n",
+                           ar->hw_params.name,
+                           ar->target_version,
+                           ar->chip_id,
+                           ar->hw->wiphy->fw_version,
+                           ar->fw_api,
                            ar->htt.target_version_major,
                            ar->htt.target_version_minor);
 
@@ -845,12 +812,14 @@ int ath10k_core_start(struct ath10k *ar)
 
        return 0;
 
-err_disconnect_htc:
+err_htc_stop:
        ath10k_htc_stop(&ar->htc);
-err_htt_detach:
-       ath10k_htt_detach(&ar->htt);
 err_hif_stop:
        ath10k_hif_stop(ar);
+err_htt_rx_detach:
+       ath10k_htt_rx_free(&ar->htt);
+err_htt_tx_detach:
+       ath10k_htt_tx_free(&ar->htt);
 err_wmi_detach:
        ath10k_wmi_detach(ar);
 err:
@@ -885,10 +854,14 @@ void ath10k_core_stop(struct ath10k *ar)
        lockdep_assert_held(&ar->conf_mutex);
 
        /* try to suspend target */
-       ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND_AND_DISABLE_INTR);
+       if (ar->state != ATH10K_STATE_RESTARTING)
+               ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND_AND_DISABLE_INTR);
+
        ath10k_debug_stop(ar);
        ath10k_htc_stop(&ar->htc);
-       ath10k_htt_detach(&ar->htt);
+       ath10k_hif_stop(ar);
+       ath10k_htt_tx_free(&ar->htt);
+       ath10k_htt_rx_free(&ar->htt);
        ath10k_wmi_detach(ar);
 }
 EXPORT_SYMBOL(ath10k_core_stop);
@@ -980,22 +953,15 @@ static int ath10k_core_check_chip_id(struct ath10k *ar)
        return 0;
 }
 
-int ath10k_core_register(struct ath10k *ar, u32 chip_id)
+static void ath10k_core_register_work(struct work_struct *work)
 {
+       struct ath10k *ar = container_of(work, struct ath10k, register_work);
        int status;
 
-       ar->chip_id = chip_id;
-
-       status = ath10k_core_check_chip_id(ar);
-       if (status) {
-               ath10k_err("Unsupported chip id 0x%08x\n", ar->chip_id);
-               return status;
-       }
-
        status = ath10k_core_probe_fw(ar);
        if (status) {
                ath10k_err("could not probe fw (%d)\n", status);
-               return status;
+               goto err;
        }
 
        status = ath10k_mac_register(ar);
@@ -1010,18 +976,43 @@ int ath10k_core_register(struct ath10k *ar, u32 chip_id)
                goto err_unregister_mac;
        }
 
-       return 0;
+       set_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags);
+       return;
 
 err_unregister_mac:
        ath10k_mac_unregister(ar);
 err_release_fw:
        ath10k_core_free_firmware_files(ar);
-       return status;
+err:
+       device_release_driver(ar->dev);
+       return;
+}
+
+int ath10k_core_register(struct ath10k *ar, u32 chip_id)
+{
+       int status;
+
+       ar->chip_id = chip_id;
+
+       status = ath10k_core_check_chip_id(ar);
+       if (status) {
+               ath10k_err("Unsupported chip id 0x%08x\n", ar->chip_id);
+               return status;
+       }
+
+       queue_work(ar->workqueue, &ar->register_work);
+
+       return 0;
 }
 EXPORT_SYMBOL(ath10k_core_register);
 
 void ath10k_core_unregister(struct ath10k *ar)
 {
+       cancel_work_sync(&ar->register_work);
+
+       if (!test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags))
+               return;
+
        /* 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. */
@@ -1033,6 +1024,71 @@ void ath10k_core_unregister(struct ath10k *ar)
 }
 EXPORT_SYMBOL(ath10k_core_unregister);
 
+struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
+                                 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;
+
+       init_completion(&ar->scan.started);
+       init_completion(&ar->scan.completed);
+       init_completion(&ar->scan.on_channel);
+       init_completion(&ar->target_suspend);
+
+       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_WORK(&ar->wmi_mgmt_tx_work, ath10k_mgmt_over_wmi_tx_work);
+       skb_queue_head_init(&ar->wmi_mgmt_tx_queue);
+
+       INIT_WORK(&ar->register_work, ath10k_core_register_work);
+       INIT_WORK(&ar->restart_work, ath10k_core_restart);
+
+       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);
+
 MODULE_AUTHOR("Qualcomm Atheros");
 MODULE_DESCRIPTION("Core module for QCA988X PCIe devices.");
 MODULE_LICENSE("Dual BSD/GPL");
index 0e71979d837cf90888c74e4d85035c2a4d6fd4ef..68ceef61933db6b769122ab2500fa524619ac465 100644 (file)
@@ -119,6 +119,7 @@ struct ath10k_peer_stat {
        u8 peer_macaddr[ETH_ALEN];
        u32 peer_rssi;
        u32 peer_tx_rate;
+       u32 peer_rx_rate; /* 10x only */
 };
 
 struct ath10k_target_stats {
@@ -130,6 +131,12 @@ struct ath10k_target_stats {
        u32 cycle_count;
        u32 phy_err_count;
        u32 chan_tx_power;
+       u32 ack_rx_bad;
+       u32 rts_bad;
+       u32 rts_good;
+       u32 fcs_bad;
+       u32 no_beacons;
+       u32 mib_int_count;
 
        /* PDEV TX stats */
        s32 comp_queued;
@@ -260,6 +267,8 @@ struct ath10k_vif {
        u8 fixed_rate;
        u8 fixed_nss;
        u8 force_sgi;
+       bool use_cts_prot;
+       int num_legacy_stations;
 };
 
 struct ath10k_vif_iter {
@@ -326,6 +335,7 @@ enum ath10k_dev_flags {
        /* Indicates that ath10k device is during CAC phase of DFS */
        ATH10K_CAC_RUNNING,
        ATH10K_FLAG_FIRST_BOOT_DONE,
+       ATH10K_FLAG_CORE_REGISTERED,
 };
 
 struct ath10k {
@@ -419,13 +429,24 @@ struct ath10k {
        struct cfg80211_chan_def chandef;
 
        int free_vdev_map;
+       bool promisc;
+       bool monitor;
        int monitor_vdev_id;
-       bool monitor_enabled;
-       bool monitor_present;
+       bool monitor_started;
        unsigned int filter_flags;
        unsigned long dev_flags;
        u32 dfs_block_radar_events;
 
+       /* protected by conf_mutex */
+       bool radar_enabled;
+       int num_started_vdevs;
+
+       /* Protected by conf-mutex */
+       u8 supp_tx_chainmask;
+       u8 supp_rx_chainmask;
+       u8 cfg_tx_chainmask;
+       u8 cfg_rx_chainmask;
+
        struct wmi_pdev_set_wmm_params_arg wmm_params;
        struct completion install_key_done;
 
@@ -456,6 +477,7 @@ struct ath10k {
 
        enum ath10k_state state;
 
+       struct work_struct register_work;
        struct work_struct restart_work;
 
        /* cycle count is reported twice for each visited channel during scan.
index 6debd281350aeb840978606212655fba6d6fb7a3..1b7ff4ba122ce42af61265eae30d8fb98d97201e 100644 (file)
@@ -161,7 +161,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
        u8 *tmp = ev->data;
        struct ath10k_target_stats *stats;
        int num_pdev_stats, num_vdev_stats, num_peer_stats;
-       struct wmi_pdev_stats *ps;
+       struct wmi_pdev_stats_10x *ps;
        int i;
 
        spin_lock_bh(&ar->data_lock);
@@ -173,7 +173,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
        num_peer_stats = __le32_to_cpu(ev->num_peer_stats); /* 0 or max peers */
 
        if (num_pdev_stats) {
-               ps = (struct wmi_pdev_stats *)tmp;
+               ps = (struct wmi_pdev_stats_10x *)tmp;
 
                stats->ch_noise_floor = __le32_to_cpu(ps->chan_nf);
                stats->tx_frame_count = __le32_to_cpu(ps->tx_frame_count);
@@ -228,7 +228,18 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
                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);
+               if (test_bit(ATH10K_FW_FEATURE_WMI_10X,
+                            ar->fw_features)) {
+                       stats->ack_rx_bad = __le32_to_cpu(ps->ack_rx_bad);
+                       stats->rts_bad = __le32_to_cpu(ps->rts_bad);
+                       stats->rts_good = __le32_to_cpu(ps->rts_good);
+                       stats->fcs_bad = __le32_to_cpu(ps->fcs_bad);
+                       stats->no_beacons = __le32_to_cpu(ps->no_beacons);
+                       stats->mib_int_count = __le32_to_cpu(ps->mib_int_count);
+                       tmp += sizeof(struct wmi_pdev_stats_10x);
+               } else {
+                       tmp += sizeof(struct wmi_pdev_stats_old);
+               }
        }
 
        /* 0 or max vdevs */
@@ -243,22 +254,29 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
        }
 
        if (num_peer_stats) {
-               struct wmi_peer_stats *peer_stats;
+               struct wmi_peer_stats_10x *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;
+                       peer_stats = (struct wmi_peer_stats_10x *)tmp;
                        s = &stats->peer_stat[i];
 
-                       WMI_MAC_ADDR_TO_CHAR_ARRAY(&peer_stats->peer_macaddr,
-                                                  s->peer_macaddr);
+                       memcpy(s->peer_macaddr, &peer_stats->peer_macaddr.addr,
+                              ETH_ALEN);
                        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);
+                       if (test_bit(ATH10K_FW_FEATURE_WMI_10X,
+                                    ar->fw_features)) {
+                               s->peer_rx_rate =
+                                       __le32_to_cpu(peer_stats->peer_rx_rate);
+                               tmp += sizeof(struct wmi_peer_stats_10x);
+
+                       } else {
+                               tmp += sizeof(struct wmi_peer_stats_old);
+                       }
                }
        }
 
@@ -272,7 +290,7 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
        struct ath10k *ar = file->private_data;
        struct ath10k_target_stats *fw_stats;
        char *buf = NULL;
-       unsigned int len = 0, buf_len = 2500;
+       unsigned int len = 0, buf_len = 8000;
        ssize_t ret_cnt = 0;
        long left;
        int i;
@@ -320,6 +338,16 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
                         "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, "%30s %10u\n",
+                        "RTS bad count", fw_stats->rts_bad);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "RTS good count", fw_stats->rts_good);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "FCS bad count", fw_stats->fcs_bad);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "No beacon count", fw_stats->no_beacons);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "MIB int count", fw_stats->mib_int_count);
 
        len += scnprintf(buf + len, buf_len - len, "\n");
        len += scnprintf(buf + len, buf_len - len, "%30s\n",
@@ -411,8 +439,8 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
                         "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 (%d)\n",
+                        "ath10k PEER stats", fw_stats->peers);
        len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
                                 "=================");
 
@@ -425,6 +453,9 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
                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, "%30s %u\n",
+                                "Peer RX rate",
+                                fw_stats->peer_stat[i].peer_rx_rate);
                len += scnprintf(buf + len, buf_len - len, "\n");
        }
        spin_unlock_bh(&ar->data_lock);
@@ -451,27 +482,37 @@ static ssize_t ath10k_read_simulate_fw_crash(struct file *file,
                                             char __user *user_buf,
                                             size_t count, loff_t *ppos)
 {
-       const char buf[] = "To simulate firmware crash write the keyword"
-                          " `crash` to this file.\nThis will force firmware"
-                          " to report a crash to the host system.\n";
+       const char buf[] = "To simulate firmware crash write one of the"
+                          " keywords to this file:\n `soft` - this will send"
+                          " WMI_FORCE_FW_HANG_ASSERT to firmware if FW"
+                          " supports that command.\n `hard` - this will send"
+                          " to firmware command with illegal parameters"
+                          " causing firmware crash.\n";
+
        return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
 }
 
+/* Simulate firmware crash:
+ * 'soft': Call wmi command causing firmware hang. This firmware hang is
+ * recoverable by warm firmware reset.
+ * 'hard': Force firmware crash by setting any vdev parameter for not allowed
+ * vdev id. This is hard firmware crash because it is recoverable only by cold
+ * firmware reset.
+ */
 static ssize_t ath10k_write_simulate_fw_crash(struct file *file,
                                              const char __user *user_buf,
                                              size_t count, loff_t *ppos)
 {
        struct ath10k *ar = file->private_data;
-       char buf[32] = {};
+       char buf[32];
        int ret;
 
        mutex_lock(&ar->conf_mutex);
 
        simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
-       if (strcmp(buf, "crash") && strcmp(buf, "crash\n")) {
-               ret = -EINVAL;
-               goto exit;
-       }
+
+       /* make sure that buf is null terminated */
+       buf[sizeof(buf) - 1] = 0;
 
        if (ar->state != ATH10K_STATE_ON &&
            ar->state != ATH10K_STATE_RESTARTED) {
@@ -479,14 +520,30 @@ static ssize_t ath10k_write_simulate_fw_crash(struct file *file,
                goto exit;
        }
 
-       ath10k_info("simulating firmware crash\n");
+       /* drop the possible '\n' from the end */
+       if (buf[count - 1] == '\n') {
+               buf[count - 1] = 0;
+               count--;
+       }
 
-       ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0);
-       if (ret)
-               ath10k_warn("failed to force fw hang (%d)\n", ret);
+       if (!strcmp(buf, "soft")) {
+               ath10k_info("simulating soft firmware crash\n");
+               ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0);
+       } else if (!strcmp(buf, "hard")) {
+               ath10k_info("simulating hard firmware crash\n");
+               ret = ath10k_wmi_vdev_set_param(ar, TARGET_NUM_VDEVS + 1,
+                                       ar->wmi.vdev_param->rts_threshold, 0);
+       } else {
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       if (ret) {
+               ath10k_warn("failed to simulate firmware crash: %d\n", ret);
+               goto exit;
+       }
 
-       if (ret == 0)
-               ret = count;
+       ret = count;
 
 exit:
        mutex_unlock(&ar->conf_mutex);
index 7f1bccd3597f1bb2a3b40d5a482f308e99e9e27c..e493db4b4a41fade30415e85c3dede5d4ea3f584 100644 (file)
@@ -157,6 +157,9 @@ int ath10k_htc_send(struct ath10k_htc *htc,
                        goto err_pull;
                }
                ep->tx_credits -= credits;
+               ath10k_dbg(ATH10K_DBG_HTC,
+                          "htc ep %d consumed %d credits (total %d)\n",
+                          eid, credits, ep->tx_credits);
                spin_unlock_bh(&htc->tx_lock);
        }
 
@@ -185,6 +188,9 @@ err_credits:
        if (ep->tx_credit_flow_enabled) {
                spin_lock_bh(&htc->tx_lock);
                ep->tx_credits += credits;
+               ath10k_dbg(ATH10K_DBG_HTC,
+                          "htc ep %d reverted %d credits back (total %d)\n",
+                          eid, credits, ep->tx_credits);
                spin_unlock_bh(&htc->tx_lock);
 
                if (ep->ep_ops.ep_tx_credits)
@@ -234,12 +240,12 @@ ath10k_htc_process_credit_report(struct ath10k_htc *htc,
                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;
 
+               ath10k_dbg(ATH10K_DBG_HTC, "htc ep %d got %d credits (total %d)\n",
+                          report->eid, report->credits, ep->tx_credits);
+
                if (ep->ep_ops.ep_tx_credits) {
                        spin_unlock_bh(&htc->tx_lock);
                        ep->ep_ops.ep_tx_credits(htc->ar);
@@ -824,17 +830,11 @@ int ath10k_htc_start(struct ath10k_htc *htc)
        return 0;
 }
 
-/*
- * stop HTC communications, i.e. stop interrupt reception, and flush all
- * queued buffers
- */
 void ath10k_htc_stop(struct ath10k_htc *htc)
 {
        spin_lock_bh(&htc->tx_lock);
        htc->stopped = true;
        spin_unlock_bh(&htc->tx_lock);
-
-       ath10k_hif_stop(htc->ar);
 }
 
 /* registered target arrival callback from the HIF layer */
index 69697af59ce06f9ab070cf01562e240607c9fc93..19c12cc8d66317ebbb8123b4a54b6654c5705c26 100644 (file)
@@ -22,7 +22,7 @@
 #include "core.h"
 #include "debug.h"
 
-static int ath10k_htt_htc_attach(struct ath10k_htt *htt)
+int ath10k_htt_connect(struct ath10k_htt *htt)
 {
        struct ath10k_htc_svc_conn_req conn_req;
        struct ath10k_htc_svc_conn_resp conn_resp;
@@ -48,38 +48,13 @@ static int ath10k_htt_htc_attach(struct ath10k_htt *htt)
        return 0;
 }
 
-int ath10k_htt_attach(struct ath10k *ar)
+int ath10k_htt_init(struct ath10k *ar)
 {
        struct ath10k_htt *htt = &ar->htt;
-       int ret;
 
        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.
-        */
-       ret = ath10k_htt_htc_attach(htt);
-       if (ret) {
-               ath10k_err("could not attach htt htc (%d)\n", ret);
-               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;
-       }
-
-       ret = ath10k_htt_rx_attach(htt);
-       if (ret) {
-               ath10k_err("could not attach htt rx (%d)\n", ret);
-               goto err_rx_attach;
-       }
-
        /*
         * Prefetch enough data to satisfy target
         * classification engine.
@@ -93,11 +68,6 @@ int ath10k_htt_attach(struct ath10k *ar)
                2; /* ip4 dscp or ip6 priority */
 
        return 0;
-
-err_rx_attach:
-       ath10k_htt_tx_detach(htt);
-err_htc_attach:
-       return ret;
 }
 
 #define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ)
@@ -117,7 +87,7 @@ static int ath10k_htt_verify_version(struct ath10k_htt *htt)
        return 0;
 }
 
-int ath10k_htt_attach_target(struct ath10k_htt *htt)
+int ath10k_htt_setup(struct ath10k_htt *htt)
 {
        int status;
 
@@ -140,9 +110,3 @@ int ath10k_htt_attach_target(struct ath10k_htt *htt)
 
        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);
-}
index 654867fc1ae73bbd7a13cf4dc61f8ac89a0b7823..9a263462c79353fa4b6a3c646ff6d025087c9463 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/bug.h>
 #include <linux/interrupt.h>
 #include <linux/dmapool.h>
+#include <net/mac80211.h>
 
 #include "htc.h"
 #include "rx_desc.h"
@@ -1172,23 +1173,6 @@ 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;
-
-       u32 tsf;
-       bool fcs_err;
-       bool amsdu_more;
-       bool mic_err;
-};
-
 struct ath10k_htt_txbuf {
        struct htt_data_tx_desc_frag frags[2];
        struct ath10k_htc_hdr htc_hdr;
@@ -1289,6 +1273,9 @@ struct ath10k_htt {
        struct tasklet_struct txrx_compl_task;
        struct sk_buff_head tx_compl_q;
        struct sk_buff_head rx_compl_q;
+
+       /* rx_status template */
+       struct ieee80211_rx_status rx_status;
 };
 
 #define RX_HTT_HDR_STATUS_LEN 64
@@ -1341,14 +1328,16 @@ struct htt_rx_desc {
 #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)
 
-int 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_connect(struct ath10k_htt *htt);
+int ath10k_htt_init(struct ath10k *ar);
+int ath10k_htt_setup(struct ath10k_htt *htt);
+
+int ath10k_htt_tx_alloc(struct ath10k_htt *htt);
+void ath10k_htt_tx_free(struct ath10k_htt *htt);
+
+int ath10k_htt_rx_alloc(struct ath10k_htt *htt);
+void ath10k_htt_rx_free(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);
index cdcbe2de95f97d602cb086c301f0778aad5bc49c..6c102b1312ff955db686022aa76e1a7ccc6e42b3 100644 (file)
@@ -225,10 +225,26 @@ static void ath10k_htt_rx_ring_refill_retry(unsigned long arg)
        ath10k_htt_rx_msdu_buff_replenish(htt);
 }
 
-void ath10k_htt_rx_detach(struct ath10k_htt *htt)
+static void ath10k_htt_rx_ring_clean_up(struct ath10k_htt *htt)
 {
-       int sw_rd_idx = htt->rx_ring.sw_rd_idx.msdu_payld;
+       struct sk_buff *skb;
+       int i;
+
+       for (i = 0; i < htt->rx_ring.size; i++) {
+               skb = htt->rx_ring.netbufs_ring[i];
+               if (!skb)
+                       continue;
 
+               dma_unmap_single(htt->ar->dev, ATH10K_SKB_CB(skb)->paddr,
+                                skb->len + skb_tailroom(skb),
+                                DMA_FROM_DEVICE);
+               dev_kfree_skb_any(skb);
+               htt->rx_ring.netbufs_ring[i] = NULL;
+       }
+}
+
+void ath10k_htt_rx_free(struct ath10k_htt *htt)
+{
        del_timer_sync(&htt->rx_ring.refill_retry_timer);
        tasklet_kill(&htt->rx_replenish_task);
        tasklet_kill(&htt->txrx_compl_task);
@@ -236,18 +252,7 @@ void ath10k_htt_rx_detach(struct ath10k_htt *htt)
        skb_queue_purge(&htt->tx_compl_q);
        skb_queue_purge(&htt->rx_compl_q);
 
-       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;
-       }
+       ath10k_htt_rx_ring_clean_up(htt);
 
        dma_free_coherent(htt->ar->dev,
                          (htt->rx_ring.size *
@@ -277,6 +282,7 @@ static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt)
 
        idx = htt->rx_ring.sw_rd_idx.msdu_payld;
        msdu = htt->rx_ring.netbufs_ring[idx];
+       htt->rx_ring.netbufs_ring[idx] = NULL;
 
        idx++;
        idx &= htt->rx_ring.size_mask;
@@ -297,6 +303,7 @@ static void ath10k_htt_rx_free_msdu_chain(struct sk_buff *skb)
        }
 }
 
+/* return: < 0 fatal error, 0 - non chained msdu, 1 chained msdu */
 static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
                                   u8 **fw_desc, int *fw_desc_len,
                                   struct sk_buff **head_msdu,
@@ -305,12 +312,13 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
        int msdu_len, msdu_chaining = 0;
        struct sk_buff *msdu;
        struct htt_rx_desc *rx_desc;
+       bool corrupted = false;
 
        lockdep_assert_held(&htt->rx_ring.lock);
 
        if (htt->rx_confused) {
                ath10k_warn("htt is confused. refusing rx\n");
-               return 0;
+               return -1;
        }
 
        msdu = *head_msdu = ath10k_htt_rx_netbuf_pop(htt);
@@ -398,7 +406,6 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
                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;
-               msdu_chaining = msdu_chained;
 
                if (msdu_len_invalid)
                        msdu_len = 0;
@@ -426,11 +433,15 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
 
                        msdu->next = next;
                        msdu = next;
+                       msdu_chaining = 1;
                }
 
                last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) &
                                RX_MSDU_END_INFO0_LAST_MSDU;
 
+               if (msdu_chaining && !last_msdu)
+                       corrupted = true;
+
                if (last_msdu) {
                        msdu->next = NULL;
                        break;
@@ -442,6 +453,23 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
        }
        *tail_msdu = msdu;
 
+       if (*head_msdu == NULL)
+               msdu_chaining = -1;
+
+       /*
+        * Apparently FW sometimes reports weird chained MSDU sequences with
+        * more than one rx descriptor. This seems like a bug but needs more
+        * analyzing. For the time being fix it by dropping such sequences to
+        * avoid blowing up the host system.
+        */
+       if (corrupted) {
+               ath10k_warn("failed to pop chained msdus, dropping\n");
+               ath10k_htt_rx_free_msdu_chain(*head_msdu);
+               *head_msdu = NULL;
+               *tail_msdu = NULL;
+               msdu_chaining = -EINVAL;
+       }
+
        /*
         * Don't refill the ring yet.
         *
@@ -464,7 +492,7 @@ static void ath10k_htt_rx_replenish_task(unsigned long ptr)
        ath10k_htt_rx_msdu_buff_replenish(htt);
 }
 
-int ath10k_htt_rx_attach(struct ath10k_htt *htt)
+int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
 {
        dma_addr_t paddr;
        void *vaddr;
@@ -490,7 +518,7 @@ int ath10k_htt_rx_attach(struct ath10k_htt *htt)
        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 *),
+               kzalloc(htt->rx_ring.size * sizeof(struct sk_buff *),
                        GFP_KERNEL);
        if (!htt->rx_ring.netbufs_ring)
                goto err_netbuf;
@@ -636,6 +664,203 @@ struct amsdu_subframe_hdr {
        __be16 len;
 } __packed;
 
+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 ath10k_htt_rx_h_rates(struct ath10k *ar,
+                                 enum ieee80211_band band,
+                                 u8 info0, u32 info1, u32 info2,
+                                 struct ieee80211_rx_status *status)
+{
+       u8 cck, rate, rate_idx, bw, sgi, mcs, nss;
+       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) + 1;
+               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->vht_flag |= RX_VHT_FLAG_80MHZ;
+               }
+
+               status->flag |= RX_FLAG_VHT;
+               break;
+       default:
+               break;
+       }
+}
+
+static void ath10k_htt_rx_h_protected(struct ath10k_htt *htt,
+                                     struct ieee80211_rx_status *rx_status,
+                                     struct sk_buff *skb,
+                                     enum htt_rx_mpdu_encrypt_type enctype,
+                                     enum rx_msdu_decap_format fmt,
+                                     bool dot11frag)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+       rx_status->flag &= ~(RX_FLAG_DECRYPTED |
+                            RX_FLAG_IV_STRIPPED |
+                            RX_FLAG_MMIC_STRIPPED);
+
+       if (enctype == HTT_RX_MPDU_ENCRYPT_NONE)
+               return;
+
+       /*
+        * There's no explicit rx descriptor flag to indicate whether a given
+        * frame has been decrypted or not. We're forced to use the decap
+        * format as an implicit indication. However fragmentation rx is always
+        * raw and it probably never reports undecrypted raws.
+        *
+        * This makes sure sniffed frames are reported as-is without stripping
+        * the protected flag.
+        */
+       if (fmt == RX_MSDU_DECAP_RAW && !dot11frag)
+               return;
+
+       rx_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);
+}
+
+static bool ath10k_htt_rx_h_channel(struct ath10k *ar,
+                                   struct ieee80211_rx_status *status)
+{
+       struct ieee80211_channel *ch;
+
+       spin_lock_bh(&ar->data_lock);
+       ch = ar->scan_channel;
+       if (!ch)
+               ch = ar->rx_channel;
+       spin_unlock_bh(&ar->data_lock);
+
+       if (!ch)
+               return false;
+
+       status->band = ch->band;
+       status->freq = ch->center_freq;
+
+       return true;
+}
+
+static void ath10k_process_rx(struct ath10k *ar,
+                             struct ieee80211_rx_status *rx_status,
+                             struct sk_buff *skb)
+{
+       struct ieee80211_rx_status *status;
+
+       status = IEEE80211_SKB_RXCB(skb);
+       *status = *rx_status;
+
+       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 flag 0x%x fcs-err %imic-err %i\n",
+                  skb,
+                  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->vht_flag & RX_VHT_FLAG_80MHZ ? "80" : "",
+                  status->flag & RX_FLAG_SHORT_GI ? "sgi " : "",
+                  status->rate_idx,
+                  status->vht_nss,
+                  status->freq,
+                  status->band, status->flag,
+                  !!(status->flag & RX_FLAG_FAILED_FCS_CRC),
+                  !!(status->flag & RX_FLAG_MMIC_ERROR));
+       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ",
+                       skb->data, skb->len);
+
+       ieee80211_rx(ar->hw, skb);
+}
+
 static int ath10k_htt_rx_nwifi_hdrlen(struct ieee80211_hdr *hdr)
 {
        /* nwifi header is padded to 4 bytes. this fixes 4addr rx */
@@ -643,11 +868,12 @@ static int ath10k_htt_rx_nwifi_hdrlen(struct ieee80211_hdr *hdr)
 }
 
 static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
-                               struct htt_rx_info *info)
+                               struct ieee80211_rx_status *rx_status,
+                               struct sk_buff *skb_in)
 {
        struct htt_rx_desc *rxd;
+       struct sk_buff *skb = skb_in;
        struct sk_buff *first;
-       struct sk_buff *skb = info->skb;
        enum rx_msdu_decap_format fmt;
        enum htt_rx_mpdu_encrypt_type enctype;
        struct ieee80211_hdr *hdr;
@@ -728,24 +954,28 @@ static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
                        break;
                }
 
-               info->skb = skb;
-               info->encrypt_type = enctype;
+               skb_in = skb;
+               ath10k_htt_rx_h_protected(htt, rx_status, skb_in, enctype, fmt,
+                                         false);
                skb = skb->next;
-               info->skb->next = NULL;
+               skb_in->next = NULL;
 
                if (skb)
-                       info->amsdu_more = true;
+                       rx_status->flag |= RX_FLAG_AMSDU_MORE;
+               else
+                       rx_status->flag &= ~RX_FLAG_AMSDU_MORE;
 
-               ath10k_process_rx(htt->ar, info);
+               ath10k_process_rx(htt->ar, rx_status, skb_in);
        }
 
        /* FIXME: It might be nice to re-assemble the A-MSDU when there's a
         * monitor interface active for sniffing purposes. */
 }
 
-static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
+static void ath10k_htt_rx_msdu(struct ath10k_htt *htt,
+                              struct ieee80211_rx_status *rx_status,
+                              struct sk_buff *skb)
 {
-       struct sk_buff *skb = info->skb;
        struct htt_rx_desc *rxd;
        struct ieee80211_hdr *hdr;
        enum rx_msdu_decap_format fmt;
@@ -808,66 +1038,9 @@ static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
                break;
        }
 
-       info->skb = skb;
-       info->encrypt_type = enctype;
+       ath10k_htt_rx_h_protected(htt, rx_status, skb, enctype, fmt, false);
 
-       ath10k_process_rx(htt->ar, info);
-}
-
-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 bool ath10k_htt_rx_has_mic_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_TKIP_MIC_ERR)
-               return true;
-
-       return false;
-}
-
-static bool ath10k_htt_rx_is_mgmt(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_MGMT_TYPE)
-               return true;
-
-       return false;
+       ath10k_process_rx(htt->ar, rx_status, skb);
 }
 
 static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb)
@@ -952,21 +1125,73 @@ static int ath10k_unchain_msdu(struct sk_buff *msdu_head)
        return 0;
 }
 
+static bool ath10k_htt_rx_amsdu_allowed(struct ath10k_htt *htt,
+                                       struct sk_buff *head,
+                                       enum htt_rx_mpdu_status status,
+                                       bool channel_set,
+                                       u32 attention)
+{
+       if (head->len == 0) {
+               ath10k_dbg(ATH10K_DBG_HTT,
+                          "htt rx dropping due to zero-len\n");
+               return false;
+       }
+
+       if (attention & RX_ATTENTION_FLAGS_DECRYPT_ERR) {
+               ath10k_dbg(ATH10K_DBG_HTT,
+                          "htt rx dropping due to decrypt-err\n");
+               return false;
+       }
+
+       if (!channel_set) {
+               ath10k_warn("no channel configured; ignoring frame!\n");
+               return false;
+       }
+
+       /* Skip mgmt frames while we handle this in WMI */
+       if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL ||
+           attention & RX_ATTENTION_FLAGS_MGMT_TYPE) {
+               ath10k_dbg(ATH10K_DBG_HTT, "htt rx mgmt ctrl\n");
+               return false;
+       }
+
+       if (status != HTT_RX_IND_MPDU_STATUS_OK &&
+           status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR &&
+           status != HTT_RX_IND_MPDU_STATUS_ERR_INV_PEER &&
+           !htt->ar->monitor_started) {
+               ath10k_dbg(ATH10K_DBG_HTT,
+                          "htt rx ignoring frame w/ status %d\n",
+                          status);
+               return false;
+       }
+
+       if (test_bit(ATH10K_CAC_RUNNING, &htt->ar->dev_flags)) {
+               ath10k_dbg(ATH10K_DBG_HTT,
+                          "htt rx CAC running\n");
+               return false;
+       }
+
+       return true;
+}
+
 static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
                                  struct htt_rx_indication *rx)
 {
-       struct htt_rx_info info;
+       struct ieee80211_rx_status *rx_status = &htt->rx_status;
        struct htt_rx_indication_mpdu_range *mpdu_ranges;
+       struct htt_rx_desc *rxd;
+       enum htt_rx_mpdu_status status;
        struct ieee80211_hdr *hdr;
        int num_mpdu_ranges;
+       u32 attention;
        int fw_desc_len;
        u8 *fw_desc;
+       bool channel_set;
        int i, j;
+       int ret;
 
        lockdep_assert_held(&htt->rx_ring.lock);
 
-       memset(&info, 0, sizeof(info));
-
        fw_desc_len = __le16_to_cpu(rx->prefix.fw_rx_desc_bytes);
        fw_desc = (u8 *)&rx->fw_desc;
 
@@ -974,106 +1199,90 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
                             HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
        mpdu_ranges = htt_rx_ind_get_mpdu_ranges(rx);
 
+       /* Fill this once, while this is per-ppdu */
+       if (rx->ppdu.info0 & HTT_RX_INDICATION_INFO0_START_VALID) {
+               memset(rx_status, 0, sizeof(*rx_status));
+               rx_status->signal  = ATH10K_DEFAULT_NOISE_FLOOR +
+                                    rx->ppdu.combined_rssi;
+       }
+
+       if (rx->ppdu.info0 & HTT_RX_INDICATION_INFO0_END_VALID) {
+               /* TSF available only in 32-bit */
+               rx_status->mactime = __le32_to_cpu(rx->ppdu.tsf) & 0xffffffff;
+               rx_status->flag |= RX_FLAG_MACTIME_END;
+       }
+
+       channel_set = ath10k_htt_rx_h_channel(htt->ar, rx_status);
+
+       if (channel_set) {
+               ath10k_htt_rx_h_rates(htt->ar, rx_status->band,
+                                     rx->ppdu.info0,
+                                     __le32_to_cpu(rx->ppdu.info1),
+                                     __le32_to_cpu(rx->ppdu.info2),
+                                     rx_status);
+       }
+
        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;
+               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");
+                       ret = ath10k_htt_rx_amsdu_pop(htt,
+                                                     &fw_desc,
+                                                     &fw_desc_len,
+                                                     &msdu_head,
+                                                     &msdu_tail);
+
+                       if (ret < 0) {
+                               ath10k_warn("failed to pop amsdu from htt rx ring %d\n",
+                                           ret);
                                ath10k_htt_rx_free_msdu_chain(msdu_head);
                                continue;
                        }
 
-                       if (ath10k_htt_rx_has_decrypt_err(msdu_head)) {
-                               ath10k_dbg(ATH10K_DBG_HTT,
-                                          "htt rx dropping due to decrypt-err\n");
-                               ath10k_htt_rx_free_msdu_chain(msdu_head);
-                               continue;
-                       }
+                       rxd = container_of((void *)msdu_head->data,
+                                          struct htt_rx_desc,
+                                          msdu_payload);
+                       attention = __le32_to_cpu(rxd->attention.flags);
 
-                       status = info.status;
-
-                       /* Skip mgmt frames while we handle this in WMI */
-                       if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL ||
-                           ath10k_htt_rx_is_mgmt(msdu_head)) {
-                               ath10k_dbg(ATH10K_DBG_HTT, "htt rx mgmt ctrl\n");
+                       if (!ath10k_htt_rx_amsdu_allowed(htt, msdu_head,
+                                                        status,
+                                                        channel_set,
+                                                        attention)) {
                                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 &&
-                           status != HTT_RX_IND_MPDU_STATUS_ERR_INV_PEER &&
-                           !htt->ar->monitor_enabled) {
-                               ath10k_dbg(ATH10K_DBG_HTT,
-                                          "htt rx ignoring frame w/ status %d\n",
-                                          status);
+                       if (ret > 0 &&
+                           ath10k_unchain_msdu(msdu_head) < 0) {
                                ath10k_htt_rx_free_msdu_chain(msdu_head);
                                continue;
                        }
 
-                       if (test_bit(ATH10K_CAC_RUNNING, &htt->ar->dev_flags)) {
-                               ath10k_dbg(ATH10K_DBG_HTT,
-                                          "htt rx CAC running\n");
-                               ath10k_htt_rx_free_msdu_chain(msdu_head);
-                               continue;
-                       }
-
-                       if (msdu_chaining &&
-                           (ath10k_unchain_msdu(msdu_head) < 0)) {
-                               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.mic_err = ath10k_htt_rx_has_mic_err(msdu_head);
-
-                       if (info.fcs_err)
-                               ath10k_dbg(ATH10K_DBG_HTT,
-                                          "htt rx has FCS err\n");
-
-                       if (info.mic_err)
-                               ath10k_dbg(ATH10K_DBG_HTT,
-                                          "htt rx has MIC err\n");
-
-                       info.signal  = ATH10K_DEFAULT_NOISE_FLOOR;
-                       info.signal += rx->ppdu.combined_rssi;
+                       if (attention & RX_ATTENTION_FLAGS_FCS_ERR)
+                               rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+                       else
+                               rx_status->flag &= ~RX_FLAG_FAILED_FCS_CRC;
 
-                       info.rate.info0 = rx->ppdu.info0;
-                       info.rate.info1 = __le32_to_cpu(rx->ppdu.info1);
-                       info.rate.info2 = __le32_to_cpu(rx->ppdu.info2);
-                       info.tsf = __le32_to_cpu(rx->ppdu.tsf);
+                       if (attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR)
+                               rx_status->flag |= RX_FLAG_MMIC_ERROR;
+                       else
+                               rx_status->flag &= ~RX_FLAG_MMIC_ERROR;
 
                        hdr = ath10k_htt_rx_skb_get_hdr(msdu_head);
 
                        if (ath10k_htt_rx_hdr_is_amsdu(hdr))
-                               ath10k_htt_rx_amsdu(htt, &info);
+                               ath10k_htt_rx_amsdu(htt, rx_status, msdu_head);
                        else
-                               ath10k_htt_rx_msdu(htt, &info);
+                               ath10k_htt_rx_msdu(htt, rx_status, msdu_head);
                }
        }
 
@@ -1084,11 +1293,12 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
                                struct htt_rx_fragment_indication *frag)
 {
        struct sk_buff *msdu_head, *msdu_tail;
+       enum htt_rx_mpdu_encrypt_type enctype;
        struct htt_rx_desc *rxd;
        enum rx_msdu_decap_format fmt;
-       struct htt_rx_info info = {};
+       struct ieee80211_rx_status *rx_status = &htt->rx_status;
        struct ieee80211_hdr *hdr;
-       int msdu_chaining;
+       int ret;
        bool tkip_mic_err;
        bool decrypt_err;
        u8 *fw_desc;
@@ -1102,24 +1312,21 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
        msdu_tail = NULL;
 
        spin_lock_bh(&htt->rx_ring.lock);
-       msdu_chaining = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len,
-                                               &msdu_head, &msdu_tail);
+       ret = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len,
+                                     &msdu_head, &msdu_tail);
        spin_unlock_bh(&htt->rx_ring.lock);
 
        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");
+       if (ret) {
+               ath10k_warn("failed to pop amsdu from httr rx ring for fragmented rx %d\n",
+                           ret);
                ath10k_htt_rx_free_msdu_chain(msdu_head);
                return;
        }
 
        /* FIXME: implement signal strength */
+       rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
 
        hdr = (struct ieee80211_hdr *)msdu_head->data;
        rxd = (void *)msdu_head->data - sizeof(*rxd);
@@ -1136,57 +1343,55 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
                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);
-       info.skb->ip_summed = ath10k_htt_rx_get_csum_state(info.skb);
+       enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
+                    RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+       ath10k_htt_rx_h_protected(htt, rx_status, msdu_head, enctype, fmt,
+                                 true);
+       msdu_head->ip_summed = ath10k_htt_rx_get_csum_state(msdu_head);
 
-       if (tkip_mic_err) {
+       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);
+               dev_kfree_skb_any(msdu_head);
                goto end;
        }
 
-       if (info.encrypt_type != HTT_RX_MPDU_ENCRYPT_NONE) {
+       if (enctype != HTT_RX_MPDU_ENCRYPT_NONE) {
                hdrlen = ieee80211_hdrlen(hdr->frame_control);
-               paramlen = ath10k_htt_rx_crypto_param_len(info.encrypt_type);
+               paramlen = ath10k_htt_rx_crypto_param_len(enctype);
 
                /* It is more efficient to move the header than the payload */
-               memmove((void *)info.skb->data + paramlen,
-                       (void *)info.skb->data,
+               memmove((void *)msdu_head->data + paramlen,
+                       (void *)msdu_head->data,
                        hdrlen);
-               skb_pull(info.skb, paramlen);
-               hdr = (struct ieee80211_hdr *)info.skb->data;
+               skb_pull(msdu_head, paramlen);
+               hdr = (struct ieee80211_hdr *)msdu_head->data;
        }
 
        /* remove trailing FCS */
        trim  = 4;
 
        /* remove crypto trailer */
-       trim += ath10k_htt_rx_crypto_tail_len(info.encrypt_type);
+       trim += ath10k_htt_rx_crypto_tail_len(enctype);
 
        /* last fragment of TKIP frags has MIC */
        if (!ieee80211_has_morefrags(hdr->frame_control) &&
-           info.encrypt_type == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
+           enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
                trim += 8;
 
-       if (trim > info.skb->len) {
+       if (trim > msdu_head->len) {
                ath10k_warn("htt rx fragment: trailer longer than the frame itself? drop\n");
-               dev_kfree_skb_any(info.skb);
+               dev_kfree_skb_any(msdu_head);
                goto end;
        }
 
-       skb_trim(info.skb, info.skb->len - trim);
+       skb_trim(msdu_head, msdu_head->len - trim);
 
        ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx frag mpdu: ",
-                       info.skb->data, info.skb->len);
-       ath10k_process_rx(htt->ar, &info);
+                       msdu_head->data, msdu_head->len);
+       ath10k_process_rx(htt->ar, rx_status, msdu_head);
 
 end:
        if (fw_desc_len > 0) {
index 7a3e2e40dd5c587215b94e859705f9a24da9acb5..7064354d1f4f0aa82f4624a7d878dea220aa4b2a 100644 (file)
@@ -83,7 +83,7 @@ void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id)
        __clear_bit(msdu_id, htt->used_msdu_ids);
 }
 
-int ath10k_htt_tx_attach(struct ath10k_htt *htt)
+int ath10k_htt_tx_alloc(struct ath10k_htt *htt)
 {
        spin_lock_init(&htt->tx_lock);
        init_waitqueue_head(&htt->empty_tx_wq);
@@ -120,7 +120,7 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt)
        return 0;
 }
 
-static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt)
+static void ath10k_htt_tx_free_pending(struct ath10k_htt *htt)
 {
        struct htt_tx_done tx_done = {0};
        int msdu_id;
@@ -141,9 +141,9 @@ static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt)
        spin_unlock_bh(&htt->tx_lock);
 }
 
-void ath10k_htt_tx_detach(struct ath10k_htt *htt)
+void ath10k_htt_tx_free(struct ath10k_htt *htt)
 {
-       ath10k_htt_tx_cleanup_pending(htt);
+       ath10k_htt_tx_free_pending(htt);
        kfree(htt->pending_tx);
        kfree(htt->used_msdu_ids);
        dma_pool_destroy(htt->tx_pool);
index 35fc44e281f57968171283d7d336cce5b20eddac..007e855f4ba99f9067725a11b85fdeadb3412483 100644 (file)
@@ -28,6 +28,7 @@
 #define QCA988X_HW_2_0_CHIP_ID_REV     0x2
 #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_FW_2_FILE       "firmware-2.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
index 511a2f81e7afc190419623235cdbefe9a66e4039..a21080028c54eeedff71b9b00a327620d9780dc3 100644 (file)
@@ -54,7 +54,10 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
        switch (key->cipher) {
        case WLAN_CIPHER_SUITE_CCMP:
                arg.key_cipher = WMI_CIPHER_AES_CCM;
-               key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
+               if (arvif->vdev_type == WMI_VDEV_TYPE_AP)
+                       key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT;
+               else
+                       key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
                break;
        case WLAN_CIPHER_SUITE_TKIP:
                arg.key_cipher = WMI_CIPHER_TKIP;
@@ -165,7 +168,7 @@ static int ath10k_clear_peer_keys(struct ath10k_vif *arvif,
                        first_errno = ret;
 
                if (ret)
-                       ath10k_warn("could not remove peer wep key %d (%d)\n",
+                       ath10k_warn("failed to remove peer wep key %d: %d\n",
                                    i, ret);
 
                peer->keys[i] = NULL;
@@ -213,7 +216,8 @@ static int ath10k_clear_vdev_key(struct ath10k_vif *arvif,
                        first_errno = ret;
 
                if (ret)
-                       ath10k_warn("could not remove key for %pM\n", addr);
+                       ath10k_warn("failed to remove key for %pM: %d\n",
+                                   addr, ret);
        }
 
        return first_errno;
@@ -323,14 +327,14 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
 
        ret = ath10k_wmi_peer_create(ar, vdev_id, addr);
        if (ret) {
-               ath10k_warn("Failed to create wmi peer %pM on vdev %i: %i\n",
+               ath10k_warn("failed to create wmi peer %pM on vdev %i: %i\n",
                            addr, vdev_id, ret);
                return ret;
        }
 
        ret = ath10k_wait_for_peer_created(ar, vdev_id, addr);
        if (ret) {
-               ath10k_warn("Failed to wait for created wmi peer %pM on vdev %i: %i\n",
+               ath10k_warn("failed to wait for created wmi peer %pM on vdev %i: %i\n",
                            addr, vdev_id, ret);
                return ret;
        }
@@ -351,7 +355,7 @@ static int ath10k_mac_set_kickout(struct ath10k_vif *arvif)
        ret = ath10k_wmi_pdev_set_param(ar, param,
                                        ATH10K_KICKOUT_THRESHOLD);
        if (ret) {
-               ath10k_warn("Failed to set kickout threshold on vdev %i: %d\n",
+               ath10k_warn("failed to set kickout threshold on vdev %i: %d\n",
                            arvif->vdev_id, ret);
                return ret;
        }
@@ -360,7 +364,7 @@ static int ath10k_mac_set_kickout(struct ath10k_vif *arvif)
        ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
                                        ATH10K_KEEPALIVE_MIN_IDLE);
        if (ret) {
-               ath10k_warn("Failed to set keepalive minimum idle time on vdev %i : %d\n",
+               ath10k_warn("failed to set keepalive minimum idle time on vdev %i: %d\n",
                            arvif->vdev_id, ret);
                return ret;
        }
@@ -369,7 +373,7 @@ static int ath10k_mac_set_kickout(struct ath10k_vif *arvif)
        ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
                                        ATH10K_KEEPALIVE_MAX_IDLE);
        if (ret) {
-               ath10k_warn("Failed to set keepalive maximum idle time on vdev %i: %d\n",
+               ath10k_warn("failed to set keepalive maximum idle time on vdev %i: %d\n",
                            arvif->vdev_id, ret);
                return ret;
        }
@@ -378,7 +382,7 @@ static int ath10k_mac_set_kickout(struct ath10k_vif *arvif)
        ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
                                        ATH10K_KEEPALIVE_MAX_UNRESPONSIVE);
        if (ret) {
-               ath10k_warn("Failed to set keepalive maximum unresponsive time on vdev %i: %d\n",
+               ath10k_warn("failed to set keepalive maximum unresponsive time on vdev %i: %d\n",
                            arvif->vdev_id, ret);
                return ret;
        }
@@ -488,92 +492,20 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
        return 0;
 }
 
-static int ath10k_vdev_start(struct ath10k_vif *arvif)
+static bool ath10k_monitor_is_enabled(struct ath10k *ar)
 {
-       struct ath10k *ar = arvif->ar;
-       struct cfg80211_chan_def *chandef = &ar->chandef;
-       struct wmi_vdev_start_request_arg arg = {};
-       int ret = 0;
-
        lockdep_assert_held(&ar->conf_mutex);
 
-       reinit_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 = chandef->chan->center_freq;
-       arg.channel.band_center_freq1 = chandef->center_freq1;
-       arg.channel.mode = chan_to_phymode(chandef);
-
-       arg.channel.min_power = 0;
-       arg.channel.max_power = chandef->chan->max_power * 2;
-       arg.channel.max_reg_power = chandef->chan->max_reg_power * 2;
-       arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain * 2;
-
-       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;
-
-               /* For now allow DFS for AP mode */
-               arg.channel.chan_radar =
-                       !!(chandef->chan->flags & IEEE80211_CHAN_RADAR);
-       } 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;
-       }
-
        ath10k_dbg(ATH10K_DBG_MAC,
-                  "mac vdev %d start center_freq %d phymode %s\n",
-                  arg.vdev_id, arg.channel.freq,
-                  ath10k_wmi_phymode_str(arg.channel.mode));
+                  "mac monitor refs: promisc %d monitor %d cac %d\n",
+                  ar->promisc, ar->monitor,
+                  test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags));
 
-       ret = ath10k_wmi_vdev_start(ar, &arg);
-       if (ret) {
-               ath10k_warn("WMI vdev %i start failed: ret %d\n",
-                           arg.vdev_id, ret);
-               return ret;
-       }
-
-       ret = ath10k_vdev_setup_sync(ar);
-       if (ret) {
-               ath10k_warn("vdev %i setup failed %d\n",
-                           arg.vdev_id, ret);
-               return ret;
-       }
-
-       return ret;
+       return ar->promisc || ar->monitor ||
+              test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
 }
 
-static int ath10k_vdev_stop(struct ath10k_vif *arvif)
-{
-       struct ath10k *ar = arvif->ar;
-       int ret;
-
-       lockdep_assert_held(&ar->conf_mutex);
-
-       reinit_completion(&ar->vdev_setup_done);
-
-       ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id);
-       if (ret) {
-               ath10k_warn("WMI vdev %i stop failed: ret %d\n",
-                           arvif->vdev_id, ret);
-               return ret;
-       }
-
-       ret = ath10k_vdev_setup_sync(ar);
-       if (ret) {
-               ath10k_warn("vdev %i setup sync failed %d\n",
-                           arvif->vdev_id, ret);
-               return ret;
-       }
-
-       return ret;
-}
-
-static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
+static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id)
 {
        struct cfg80211_chan_def *chandef = &ar->chandef;
        struct ieee80211_channel *channel = chandef->chan;
@@ -582,11 +514,6 @@ static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       if (!ar->monitor_present) {
-               ath10k_warn("mac montor stop -- monitor is not present\n");
-               return -EINVAL;
-       }
-
        arg.vdev_id = vdev_id;
        arg.channel.freq = channel->center_freq;
        arg.channel.band_center_freq1 = chandef->center_freq1;
@@ -604,88 +531,75 @@ static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
 
        ret = ath10k_wmi_vdev_start(ar, &arg);
        if (ret) {
-               ath10k_warn("Monitor vdev %i start failed: ret %d\n",
+               ath10k_warn("failed to request monitor vdev %i start: %d\n",
                            vdev_id, ret);
                return ret;
        }
 
        ret = ath10k_vdev_setup_sync(ar);
        if (ret) {
-               ath10k_warn("Monitor vdev %i setup failed %d\n",
+               ath10k_warn("failed to synchronize setup for monitor vdev %i: %d\n",
                            vdev_id, ret);
                return ret;
        }
 
        ret = ath10k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr);
        if (ret) {
-               ath10k_warn("Monitor vdev %i up failed: %d\n",
+               ath10k_warn("failed to put up monitor vdev %i: %d\n",
                            vdev_id, ret);
                goto vdev_stop;
        }
 
        ar->monitor_vdev_id = vdev_id;
-       ar->monitor_enabled = true;
 
+       ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %i started\n",
+                  ar->monitor_vdev_id);
        return 0;
 
 vdev_stop:
        ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
        if (ret)
-               ath10k_warn("Monitor vdev %i stop failed: %d\n",
+               ath10k_warn("failed to stop monitor vdev %i after start failure: %d\n",
                            ar->monitor_vdev_id, ret);
 
        return ret;
 }
 
-static int ath10k_monitor_stop(struct ath10k *ar)
+static int ath10k_monitor_vdev_stop(struct ath10k *ar)
 {
        int ret = 0;
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       if (!ar->monitor_present) {
-               ath10k_warn("mac montor stop -- monitor is not present\n");
-               return -EINVAL;
-       }
-
-       if (!ar->monitor_enabled) {
-               ath10k_warn("mac montor stop -- monitor is not enabled\n");
-               return -EINVAL;
-       }
-
        ret = ath10k_wmi_vdev_down(ar, ar->monitor_vdev_id);
        if (ret)
-               ath10k_warn("Monitor vdev %i down failed: %d\n",
+               ath10k_warn("failed to put down monitor vdev %i: %d\n",
                            ar->monitor_vdev_id, ret);
 
        ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
        if (ret)
-               ath10k_warn("Monitor vdev %i stop failed: %d\n",
+               ath10k_warn("failed to to request monitor vdev %i stop: %d\n",
                            ar->monitor_vdev_id, ret);
 
        ret = ath10k_vdev_setup_sync(ar);
        if (ret)
-               ath10k_warn("Monitor_down sync failed, vdev %i: %d\n",
+               ath10k_warn("failed to synchronise monitor vdev %i: %d\n",
                            ar->monitor_vdev_id, ret);
 
-       ar->monitor_enabled = false;
+       ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %i stopped\n",
+                  ar->monitor_vdev_id);
        return ret;
 }
 
-static int ath10k_monitor_create(struct ath10k *ar)
+static int ath10k_monitor_vdev_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");
+               ath10k_warn("failed to find free vdev id for monitor vdev\n");
                return -ENOMEM;
        }
 
@@ -696,7 +610,7 @@ static int ath10k_monitor_create(struct ath10k *ar)
                                     WMI_VDEV_TYPE_MONITOR,
                                     0, ar->mac_addr);
        if (ret) {
-               ath10k_warn("WMI vdev %i monitor create failed: ret %d\n",
+               ath10k_warn("failed to request monitor vdev %i creation: %d\n",
                            ar->monitor_vdev_id, ret);
                goto vdev_fail;
        }
@@ -704,7 +618,6 @@ static int ath10k_monitor_create(struct ath10k *ar)
        ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d created\n",
                   ar->monitor_vdev_id);
 
-       ar->monitor_present = true;
        return 0;
 
 vdev_fail:
@@ -715,48 +628,123 @@ vdev_fail:
        return ret;
 }
 
-static int ath10k_monitor_destroy(struct ath10k *ar)
+static int ath10k_monitor_vdev_delete(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 %i monitor delete failed: %d\n",
+               ath10k_warn("failed to request wmi monitor vdev %i removal: %d\n",
                            ar->monitor_vdev_id, ret);
                return ret;
        }
 
        ar->free_vdev_map |= 1 << (ar->monitor_vdev_id);
-       ar->monitor_present = false;
 
        ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d deleted\n",
                   ar->monitor_vdev_id);
        return ret;
 }
 
-static int ath10k_start_cac(struct ath10k *ar)
+static int ath10k_monitor_start(struct ath10k *ar)
 {
        int ret;
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       set_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+       if (!ath10k_monitor_is_enabled(ar)) {
+               ath10k_warn("trying to start monitor with no references\n");
+               return 0;
+       }
+
+       if (ar->monitor_started) {
+               ath10k_dbg(ATH10K_DBG_MAC, "mac monitor already started\n");
+               return 0;
+       }
 
-       ret = ath10k_monitor_create(ar);
+       ret = ath10k_monitor_vdev_create(ar);
        if (ret) {
-               clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+               ath10k_warn("failed to create monitor vdev: %d\n", ret);
                return ret;
        }
 
-       ret = ath10k_monitor_start(ar, ar->monitor_vdev_id);
+       ret = ath10k_monitor_vdev_start(ar, ar->monitor_vdev_id);
+       if (ret) {
+               ath10k_warn("failed to start monitor vdev: %d\n", ret);
+               ath10k_monitor_vdev_delete(ar);
+               return ret;
+       }
+
+       ar->monitor_started = true;
+       ath10k_dbg(ATH10K_DBG_MAC, "mac monitor started\n");
+
+       return 0;
+}
+
+static void ath10k_monitor_stop(struct ath10k *ar)
+{
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       if (ath10k_monitor_is_enabled(ar)) {
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "mac monitor will be stopped later\n");
+               return;
+       }
+
+       if (!ar->monitor_started) {
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "mac monitor probably failed to start earlier\n");
+               return;
+       }
+
+       ret = ath10k_monitor_vdev_stop(ar);
+       if (ret)
+               ath10k_warn("failed to stop monitor vdev: %d\n", ret);
+
+       ret = ath10k_monitor_vdev_delete(ar);
+       if (ret)
+               ath10k_warn("failed to delete monitor vdev: %d\n", ret);
+
+       ar->monitor_started = false;
+       ath10k_dbg(ATH10K_DBG_MAC, "mac monitor stopped\n");
+}
+
+static int ath10k_recalc_rtscts_prot(struct ath10k_vif *arvif)
+{
+       struct ath10k *ar = arvif->ar;
+       u32 vdev_param, rts_cts = 0;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       vdev_param = ar->wmi.vdev_param->enable_rtscts;
+
+       if (arvif->use_cts_prot || arvif->num_legacy_stations > 0)
+               rts_cts |= SM(WMI_RTSCTS_ENABLED, WMI_RTSCTS_SET);
+
+       if (arvif->num_legacy_stations > 0)
+               rts_cts |= SM(WMI_RTSCTS_ACROSS_SW_RETRIES,
+                             WMI_RTSCTS_PROFILE);
+
+       return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
+                                        rts_cts);
+}
+
+static int ath10k_start_cac(struct ath10k *ar)
+{
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       set_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+
+       ret = ath10k_monitor_start(ar);
        if (ret) {
+               ath10k_warn("failed to start monitor (cac): %d\n", ret);
                clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
-               ath10k_monitor_destroy(ar);
                return ret;
        }
 
@@ -774,58 +762,26 @@ static int ath10k_stop_cac(struct ath10k *ar)
        if (!test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags))
                return 0;
 
-       ath10k_monitor_stop(ar);
-       ath10k_monitor_destroy(ar);
        clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+       ath10k_monitor_stop(ar);
 
        ath10k_dbg(ATH10K_DBG_MAC, "mac cac finished\n");
 
        return 0;
 }
 
-static const char *ath10k_dfs_state(enum nl80211_dfs_state dfs_state)
+static void ath10k_recalc_radar_detection(struct ath10k *ar)
 {
-       switch (dfs_state) {
-       case NL80211_DFS_USABLE:
-               return "USABLE";
-       case NL80211_DFS_UNAVAILABLE:
-               return "UNAVAILABLE";
-       case NL80211_DFS_AVAILABLE:
-               return "AVAILABLE";
-       default:
-               WARN_ON(1);
-               return "bug";
-       }
-}
-
-static void ath10k_config_radar_detection(struct ath10k *ar)
-{
-       struct ieee80211_channel *chan = ar->hw->conf.chandef.chan;
-       bool radar = ar->hw->conf.radar_enabled;
-       bool chan_radar = !!(chan->flags & IEEE80211_CHAN_RADAR);
-       enum nl80211_dfs_state dfs_state = chan->dfs_state;
        int ret;
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       ath10k_dbg(ATH10K_DBG_MAC,
-                  "mac radar config update: chan %dMHz radar %d chan radar %d chan state %s\n",
-                  chan->center_freq, radar, chan_radar,
-                  ath10k_dfs_state(dfs_state));
-
-       /*
-        * It's safe to call it even if CAC is not started.
-        * This call here guarantees changing channel, etc. will stop CAC.
-        */
        ath10k_stop_cac(ar);
 
-       if (!radar)
-               return;
-
-       if (!chan_radar)
+       if (!ar->radar_enabled)
                return;
 
-       if (dfs_state != NL80211_DFS_USABLE)
+       if (ar->num_started_vdevs > 0)
                return;
 
        ret = ath10k_start_cac(ar);
@@ -835,11 +791,106 @@ static void ath10k_config_radar_detection(struct ath10k *ar)
                 * radiation is not allowed, make this channel DFS_UNAVAILABLE
                 * by indicating that radar was detected.
                 */
-               ath10k_warn("failed to start CAC (%d)\n", ret);
+               ath10k_warn("failed to start CAC: %d\n", ret);
                ieee80211_radar_detected(ar->hw);
        }
 }
 
+static int ath10k_vdev_start(struct ath10k_vif *arvif)
+{
+       struct ath10k *ar = arvif->ar;
+       struct cfg80211_chan_def *chandef = &ar->chandef;
+       struct wmi_vdev_start_request_arg arg = {};
+       int ret = 0;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       reinit_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 = chandef->chan->center_freq;
+       arg.channel.band_center_freq1 = chandef->center_freq1;
+       arg.channel.mode = chan_to_phymode(chandef);
+
+       arg.channel.min_power = 0;
+       arg.channel.max_power = chandef->chan->max_power * 2;
+       arg.channel.max_reg_power = chandef->chan->max_reg_power * 2;
+       arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain * 2;
+
+       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;
+
+               /* For now allow DFS for AP mode */
+               arg.channel.chan_radar =
+                       !!(chandef->chan->flags & IEEE80211_CHAN_RADAR);
+       } 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;
+       }
+
+       ath10k_dbg(ATH10K_DBG_MAC,
+                  "mac vdev %d start center_freq %d phymode %s\n",
+                  arg.vdev_id, arg.channel.freq,
+                  ath10k_wmi_phymode_str(arg.channel.mode));
+
+       ret = ath10k_wmi_vdev_start(ar, &arg);
+       if (ret) {
+               ath10k_warn("failed to start WMI vdev %i: %d\n",
+                           arg.vdev_id, ret);
+               return ret;
+       }
+
+       ret = ath10k_vdev_setup_sync(ar);
+       if (ret) {
+               ath10k_warn("failed to synchronise setup for vdev %i: %d\n",
+                           arg.vdev_id, ret);
+               return ret;
+       }
+
+       ar->num_started_vdevs++;
+       ath10k_recalc_radar_detection(ar);
+
+       return ret;
+}
+
+static int ath10k_vdev_stop(struct ath10k_vif *arvif)
+{
+       struct ath10k *ar = arvif->ar;
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       reinit_completion(&ar->vdev_setup_done);
+
+       ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id);
+       if (ret) {
+               ath10k_warn("failed to stop WMI vdev %i: %d\n",
+                           arvif->vdev_id, ret);
+               return ret;
+       }
+
+       ret = ath10k_vdev_setup_sync(ar);
+       if (ret) {
+               ath10k_warn("failed to syncronise setup for vdev %i: %d\n",
+                           arvif->vdev_id, ret);
+               return ret;
+       }
+
+       WARN_ON(ar->num_started_vdevs == 0);
+
+       if (ar->num_started_vdevs != 0) {
+               ar->num_started_vdevs--;
+               ath10k_recalc_radar_detection(ar);
+       }
+
+       return ret;
+}
+
 static void ath10k_control_beaconing(struct ath10k_vif *arvif,
                                struct ieee80211_bss_conf *info)
 {
@@ -880,7 +931,7 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif,
        ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
                                 arvif->bssid);
        if (ret) {
-               ath10k_warn("Failed to bring up vdev %d: %i\n",
+               ath10k_warn("failed to bring up vdev %d: %i\n",
                            arvif->vdev_id, ret);
                ath10k_vdev_stop(arvif);
                return;
@@ -904,7 +955,7 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
        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",
+                       ath10k_warn("failed to delete IBSS self peer %pM for vdev %d: %d\n",
                                    self_peer, arvif->vdev_id, ret);
 
                if (is_zero_ether_addr(arvif->bssid))
@@ -913,7 +964,7 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
                ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id,
                                         arvif->bssid);
                if (ret) {
-                       ath10k_warn("Failed to delete IBSS BSSID peer:%pM for VDEV:%d ret:%d\n",
+                       ath10k_warn("failed to delete IBSS BSSID peer %pM for vdev %d: %d\n",
                                    arvif->bssid, arvif->vdev_id, ret);
                        return;
                }
@@ -925,7 +976,7 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
 
        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",
+               ath10k_warn("failed to create IBSS self peer %pM for vdev %d: %d\n",
                            self_peer, arvif->vdev_id, ret);
                return;
        }
@@ -934,7 +985,7 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
        ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, vdev_param,
                                        ATH10K_DEFAULT_ATIM);
        if (ret)
-               ath10k_warn("Failed to set IBSS ATIM for VDEV:%d ret:%d\n",
+               ath10k_warn("failed to set IBSS ATIM for vdev %d: %d\n",
                            arvif->vdev_id, ret);
 }
 
@@ -961,7 +1012,7 @@ static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
                ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param,
                                                  conf->dynamic_ps_timeout);
                if (ret) {
-                       ath10k_warn("Failed to set inactivity time for vdev %d: %i\n",
+                       ath10k_warn("failed to set inactivity time for vdev %d: %i\n",
                                    arvif->vdev_id, ret);
                        return ret;
                }
@@ -974,8 +1025,8 @@ static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
 
        ret = ath10k_wmi_set_psmode(ar, arvif->vdev_id, psmode);
        if (ret) {
-               ath10k_warn("Failed to set PS Mode: %d for VDEV: %d\n",
-                           psmode, arvif->vdev_id);
+               ath10k_warn("failed to set PS Mode %d for vdev %d: %d\n",
+                           psmode, arvif->vdev_id, ret);
                return ret;
        }
 
@@ -1429,7 +1480,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
 
        ap_sta = ieee80211_find_sta(vif, bss_conf->bssid);
        if (!ap_sta) {
-               ath10k_warn("Failed to find station entry for %pM, vdev %i\n",
+               ath10k_warn("failed to find station entry for bss %pM vdev %i\n",
                            bss_conf->bssid, arvif->vdev_id);
                rcu_read_unlock();
                return;
@@ -1442,7 +1493,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
        ret = ath10k_peer_assoc_prepare(ar, arvif, ap_sta,
                                        bss_conf, &peer_arg);
        if (ret) {
-               ath10k_warn("Peer assoc prepare failed for %pM vdev %i\n: %d",
+               ath10k_warn("failed to prepare peer assoc for %pM vdev %i: %d\n",
                            bss_conf->bssid, arvif->vdev_id, ret);
                rcu_read_unlock();
                return;
@@ -1452,7 +1503,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
 
        ret = ath10k_wmi_peer_assoc(ar, &peer_arg);
        if (ret) {
-               ath10k_warn("Peer assoc failed for %pM vdev %i\n: %d",
+               ath10k_warn("failed to run peer assoc for %pM vdev %i: %d\n",
                            bss_conf->bssid, arvif->vdev_id, ret);
                return;
        }
@@ -1473,7 +1524,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
 
        ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, arvif->aid, arvif->bssid);
        if (ret) {
-               ath10k_warn("VDEV: %d up failed: ret %d\n",
+               ath10k_warn("failed to set vdev %d up: %d\n",
                            arvif->vdev_id, ret);
                return;
        }
@@ -1524,7 +1575,7 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
 }
 
 static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
-                               struct ieee80211_sta *sta)
+                               struct ieee80211_sta *sta, bool reassoc)
 {
        struct wmi_peer_assoc_complete_arg peer_arg;
        int ret = 0;
@@ -1533,34 +1584,46 @@ static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
 
        ret = ath10k_peer_assoc_prepare(ar, arvif, sta, NULL, &peer_arg);
        if (ret) {
-               ath10k_warn("WMI peer assoc prepare failed for %pM vdev %i: %i\n",
+               ath10k_warn("failed to prepare WMI peer assoc for %pM vdev %i: %i\n",
                            sta->addr, arvif->vdev_id, ret);
                return ret;
        }
 
+       peer_arg.peer_reassoc = reassoc;
        ret = ath10k_wmi_peer_assoc(ar, &peer_arg);
        if (ret) {
-               ath10k_warn("Peer assoc failed for STA %pM vdev %i: %d\n",
+               ath10k_warn("failed to run peer assoc for STA %pM vdev %i: %d\n",
                            sta->addr, arvif->vdev_id, ret);
                return ret;
        }
 
        ret = ath10k_setup_peer_smps(ar, arvif, sta->addr, &sta->ht_cap);
        if (ret) {
-               ath10k_warn("failed to setup peer SMPS for vdev: %d\n", ret);
+               ath10k_warn("failed to setup peer SMPS for vdev %d: %d\n",
+                           arvif->vdev_id, ret);
                return ret;
        }
 
+       if (!sta->wme) {
+               arvif->num_legacy_stations++;
+               ret  = ath10k_recalc_rtscts_prot(arvif);
+               if (ret) {
+                       ath10k_warn("failed to recalculate rts/cts prot for vdev %d: %d\n",
+                                   arvif->vdev_id, ret);
+                       return ret;
+               }
+       }
+
        ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
        if (ret) {
-               ath10k_warn("could not install peer wep keys for vdev %i: %d\n",
+               ath10k_warn("failed to install peer wep keys for vdev %i: %d\n",
                            arvif->vdev_id, ret);
                return ret;
        }
 
        ret = ath10k_peer_assoc_qos_ap(ar, arvif, sta);
        if (ret) {
-               ath10k_warn("could not set qos params for STA %pM for vdev %i: %d\n",
+               ath10k_warn("failed to set qos params for STA %pM for vdev %i: %d\n",
                            sta->addr, arvif->vdev_id, ret);
                return ret;
        }
@@ -1575,9 +1638,19 @@ static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif,
 
        lockdep_assert_held(&ar->conf_mutex);
 
+       if (!sta->wme) {
+               arvif->num_legacy_stations--;
+               ret = ath10k_recalc_rtscts_prot(arvif);
+               if (ret) {
+                       ath10k_warn("failed to recalculate rts/cts prot for vdev %d: %d\n",
+                                   arvif->vdev_id, ret);
+                       return ret;
+               }
+       }
+
        ret = ath10k_clear_peer_keys(arvif, sta->addr);
        if (ret) {
-               ath10k_warn("could not clear all peer wep keys for vdev %i: %d\n",
+               ath10k_warn("failed to clear all peer wep keys for vdev %i: %d\n",
                            arvif->vdev_id, ret);
                return ret;
        }
@@ -1685,19 +1758,44 @@ static int ath10k_update_channel_list(struct ath10k *ar)
        return ret;
 }
 
+static enum wmi_dfs_region
+ath10k_mac_get_dfs_region(enum nl80211_dfs_regions dfs_region)
+{
+       switch (dfs_region) {
+       case NL80211_DFS_UNSET:
+               return WMI_UNINIT_DFS_DOMAIN;
+       case NL80211_DFS_FCC:
+               return WMI_FCC_DFS_DOMAIN;
+       case NL80211_DFS_ETSI:
+               return WMI_ETSI_DFS_DOMAIN;
+       case NL80211_DFS_JP:
+               return WMI_MKK4_DFS_DOMAIN;
+       }
+       return WMI_UNINIT_DFS_DOMAIN;
+}
+
 static void ath10k_regd_update(struct ath10k *ar)
 {
        struct reg_dmn_pair_mapping *regpair;
        int ret;
+       enum wmi_dfs_region wmi_dfs_reg;
+       enum nl80211_dfs_regions nl_dfs_reg;
 
        lockdep_assert_held(&ar->conf_mutex);
 
        ret = ath10k_update_channel_list(ar);
        if (ret)
-               ath10k_warn("could not update channel list (%d)\n", ret);
+               ath10k_warn("failed to update channel list: %d\n", ret);
 
        regpair = ar->ath_common.regulatory.regpair;
 
+       if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector) {
+               nl_dfs_reg = ar->dfs_detector->region;
+               wmi_dfs_reg = ath10k_mac_get_dfs_region(nl_dfs_reg);
+       } else {
+               wmi_dfs_reg = WMI_UNINIT_DFS_DOMAIN;
+       }
+
        /* Target allows setting up per-band regdomain but ath_common provides
         * a combined one only */
        ret = ath10k_wmi_pdev_set_regdomain(ar,
@@ -1705,9 +1803,10 @@ static void ath10k_regd_update(struct ath10k *ar)
                                            regpair->reg_domain, /* 2ghz */
                                            regpair->reg_domain, /* 5ghz */
                                            regpair->reg_2ghz_ctl,
-                                           regpair->reg_5ghz_ctl);
+                                           regpair->reg_5ghz_ctl,
+                                           wmi_dfs_reg);
        if (ret)
-               ath10k_warn("could not set pdev regdomain (%d)\n", ret);
+               ath10k_warn("failed to set pdev regdomain: %d\n", ret);
 }
 
 static void ath10k_reg_notifier(struct wiphy *wiphy,
@@ -1725,7 +1824,7 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
                result = ar->dfs_detector->set_dfs_domain(ar->dfs_detector,
                                                          request->dfs_region);
                if (!result)
-                       ath10k_warn("dfs region 0x%X not supported, will trigger radar for every pulse\n",
+                       ath10k_warn("DFS region 0x%X not supported, will trigger radar for every pulse\n",
                                    request->dfs_region);
        }
 
@@ -1759,10 +1858,10 @@ static u8 ath10k_tx_h_get_vdev_id(struct ath10k *ar,
        if (info->control.vif)
                return ath10k_vif_to_arvif(info->control.vif)->vdev_id;
 
-       if (ar->monitor_enabled)
+       if (ar->monitor_started)
                return ar->monitor_vdev_id;
 
-       ath10k_warn("could not resolve vdev id\n");
+       ath10k_warn("failed to resolve vdev id\n");
        return 0;
 }
 
@@ -1792,8 +1891,13 @@ static void ath10k_tx_wep_key_work(struct work_struct *work)
                                                wep_key_work);
        int ret, keyidx = arvif->def_wep_key_newidx;
 
+       mutex_lock(&arvif->ar->conf_mutex);
+
+       if (arvif->ar->state != ATH10K_STATE_ON)
+               goto unlock;
+
        if (arvif->def_wep_key_idx == keyidx)
-               return;
+               goto unlock;
 
        ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n",
                   arvif->vdev_id, keyidx);
@@ -1803,11 +1907,16 @@ static void ath10k_tx_wep_key_work(struct work_struct *work)
                                        arvif->ar->wmi.vdev_param->def_keyid,
                                        keyidx);
        if (ret) {
-               ath10k_warn("could not update wep keyidx (%d)\n", ret);
-               return;
+               ath10k_warn("failed to update wep key index for vdev %d: %d\n",
+                           arvif->vdev_id,
+                           ret);
+               goto unlock;
        }
 
        arvif->def_wep_key_idx = keyidx;
+
+unlock:
+       mutex_unlock(&arvif->ar->conf_mutex);
 }
 
 static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
@@ -1879,7 +1988,7 @@ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
                             ar->fw_features)) {
                        if (skb_queue_len(&ar->wmi_mgmt_tx_queue) >=
                            ATH10K_MAX_NUM_MGMT_PENDING) {
-                               ath10k_warn("wmi mgmt_tx queue limit reached\n");
+                               ath10k_warn("reached WMI management tranmist queue limit\n");
                                ret = -EBUSY;
                                goto exit;
                        }
@@ -1903,7 +2012,7 @@ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
 
 exit:
        if (ret) {
-               ath10k_warn("tx failed (%d). dropping packet.\n", ret);
+               ath10k_warn("failed to transmit packet, dropping: %d\n", ret);
                ieee80211_free_txskb(ar->hw, skb);
        }
 }
@@ -1964,7 +2073,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
                if (!peer) {
                        ret = ath10k_peer_create(ar, vdev_id, peer_addr);
                        if (ret)
-                               ath10k_warn("peer %pM on vdev %d not created (%d)\n",
+                               ath10k_warn("failed to create peer %pM on vdev %d: %d\n",
                                            peer_addr, vdev_id, ret);
                }
 
@@ -1984,7 +2093,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
                if (!peer) {
                        ret = ath10k_peer_delete(ar, vdev_id, peer_addr);
                        if (ret)
-                               ath10k_warn("peer %pM on vdev %d not deleted (%d)\n",
+                               ath10k_warn("failed to delete peer %pM on vdev %d: %d\n",
                                            peer_addr, vdev_id, ret);
                }
 
@@ -2018,7 +2127,8 @@ void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work)
 
                ret = ath10k_wmi_mgmt_tx(ar, skb);
                if (ret) {
-                       ath10k_warn("wmi mgmt_tx failed (%d)\n", ret);
+                       ath10k_warn("failed to transmit management frame via WMI: %d\n",
+                                   ret);
                        ieee80211_free_txskb(ar->hw, skb);
                }
        }
@@ -2043,7 +2153,7 @@ void ath10k_reset_scan(unsigned long ptr)
                return;
        }
 
-       ath10k_warn("scan timeout. resetting. fw issue?\n");
+       ath10k_warn("scan timed out, firmware problem?\n");
 
        if (ar->scan.is_roc)
                ieee80211_remain_on_channel_expired(ar->hw);
@@ -2079,7 +2189,7 @@ static int ath10k_abort_scan(struct ath10k *ar)
 
        ret = ath10k_wmi_stop_scan(ar, &arg);
        if (ret) {
-               ath10k_warn("could not submit wmi stop scan (%d)\n", ret);
+               ath10k_warn("failed to stop wmi scan: %d\n", ret);
                spin_lock_bh(&ar->data_lock);
                ar->scan.in_progress = false;
                ath10k_offchan_tx_purge(ar);
@@ -2099,7 +2209,7 @@ static int ath10k_abort_scan(struct ath10k *ar)
 
        spin_lock_bh(&ar->data_lock);
        if (ar->scan.in_progress) {
-               ath10k_warn("could not stop scan. its still in progress\n");
+               ath10k_warn("failed to stop scan, it's still in progress\n");
                ar->scan.in_progress = false;
                ath10k_offchan_tx_purge(ar);
                ret = -ETIMEDOUT;
@@ -2187,72 +2297,171 @@ static void ath10k_tx(struct ieee80211_hw *hw,
        ath10k_tx_htt(ar, skb);
 }
 
-/*
- * Initialize various parameters with default vaules.
- */
+/* Must not be called with conf_mutex held as workers can use that also. */
+static void ath10k_drain_tx(struct ath10k *ar)
+{
+       /* make sure rcu-protected mac80211 tx path itself is drained */
+       synchronize_net();
+
+       ath10k_offchan_tx_purge(ar);
+       ath10k_mgmt_over_wmi_tx_purge(ar);
+
+       cancel_work_sync(&ar->offchan_tx_work);
+       cancel_work_sync(&ar->wmi_mgmt_tx_work);
+}
+
 void ath10k_halt(struct ath10k *ar)
 {
+       struct ath10k_vif *arvif;
+
        lockdep_assert_held(&ar->conf_mutex);
 
-       ath10k_stop_cac(ar);
+       if (ath10k_monitor_is_enabled(ar)) {
+               clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+               ar->promisc = false;
+               ar->monitor = false;
+               ath10k_monitor_stop(ar);
+       }
+
        del_timer_sync(&ar->scan.timeout);
-       ath10k_offchan_tx_purge(ar);
-       ath10k_mgmt_over_wmi_tx_purge(ar);
+       ath10k_reset_scan((unsigned long)ar);
        ath10k_peer_cleanup_all(ar);
        ath10k_core_stop(ar);
        ath10k_hif_power_down(ar);
 
        spin_lock_bh(&ar->data_lock);
-       if (ar->scan.in_progress) {
-               del_timer(&ar->scan.timeout);
-               ar->scan.in_progress = false;
-               ieee80211_scan_completed(ar->hw, true);
+       list_for_each_entry(arvif, &ar->arvifs, list) {
+               if (!arvif->beacon)
+                       continue;
+
+               dma_unmap_single(arvif->ar->dev,
+                                ATH10K_SKB_CB(arvif->beacon)->paddr,
+                                arvif->beacon->len, DMA_TO_DEVICE);
+               dev_kfree_skb_any(arvif->beacon);
+               arvif->beacon = NULL;
        }
        spin_unlock_bh(&ar->data_lock);
 }
 
+static int ath10k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
+{
+       struct ath10k *ar = hw->priv;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (ar->cfg_tx_chainmask) {
+               *tx_ant = ar->cfg_tx_chainmask;
+               *rx_ant = ar->cfg_rx_chainmask;
+       } else {
+               *tx_ant = ar->supp_tx_chainmask;
+               *rx_ant = ar->supp_rx_chainmask;
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+
+       return 0;
+}
+
+static int __ath10k_set_antenna(struct ath10k *ar, u32 tx_ant, u32 rx_ant)
+{
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       ar->cfg_tx_chainmask = tx_ant;
+       ar->cfg_rx_chainmask = rx_ant;
+
+       if ((ar->state != ATH10K_STATE_ON) &&
+           (ar->state != ATH10K_STATE_RESTARTED))
+               return 0;
+
+       ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->tx_chain_mask,
+                                       tx_ant);
+       if (ret) {
+               ath10k_warn("failed to set tx-chainmask: %d, req 0x%x\n",
+                           ret, tx_ant);
+               return ret;
+       }
+
+       ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->rx_chain_mask,
+                                       rx_ant);
+       if (ret) {
+               ath10k_warn("failed to set rx-chainmask: %d, req 0x%x\n",
+                           ret, rx_ant);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int ath10k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+       ret = __ath10k_set_antenna(ar, tx_ant, rx_ant);
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
 static int ath10k_start(struct ieee80211_hw *hw)
 {
        struct ath10k *ar = hw->priv;
        int ret = 0;
 
+       /*
+        * This makes sense only when restarting hw. It is harmless to call
+        * uncoditionally. This is necessary to make sure no HTT/WMI tx
+        * commands will be submitted while restarting.
+        */
+       ath10k_drain_tx(ar);
+
        mutex_lock(&ar->conf_mutex);
 
-       if (ar->state != ATH10K_STATE_OFF &&
-           ar->state != ATH10K_STATE_RESTARTING) {
+       switch (ar->state) {
+       case ATH10K_STATE_OFF:
+               ar->state = ATH10K_STATE_ON;
+               break;
+       case ATH10K_STATE_RESTARTING:
+               ath10k_halt(ar);
+               ar->state = ATH10K_STATE_RESTARTED;
+               break;
+       case ATH10K_STATE_ON:
+       case ATH10K_STATE_RESTARTED:
+       case ATH10K_STATE_WEDGED:
+               WARN_ON(1);
                ret = -EINVAL;
-               goto exit;
+               goto err;
        }
 
        ret = ath10k_hif_power_up(ar);
        if (ret) {
-               ath10k_err("could not init hif (%d)\n", ret);
-               ar->state = ATH10K_STATE_OFF;
-               goto exit;
+               ath10k_err("Could not init hif: %d\n", ret);
+               goto err_off;
        }
 
        ret = ath10k_core_start(ar);
        if (ret) {
-               ath10k_err("could not init core (%d)\n", ret);
-               ath10k_hif_power_down(ar);
-               ar->state = ATH10K_STATE_OFF;
-               goto exit;
+               ath10k_err("Could not init core: %d\n", ret);
+               goto err_power_down;
        }
 
-       if (ar->state == ATH10K_STATE_OFF)
-               ar->state = ATH10K_STATE_ON;
-       else if (ar->state == ATH10K_STATE_RESTARTING)
-               ar->state = ATH10K_STATE_RESTARTED;
-
        ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->pmf_qos, 1);
-       if (ret)
-               ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n",
-                           ret);
+       if (ret) {
+               ath10k_warn("failed to enable PMF QOS: %d\n", ret);
+               goto err_core_stop;
+       }
 
        ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->dynamic_bw, 1);
-       if (ret)
-               ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
-                           ret);
+       if (ret) {
+               ath10k_warn("failed to enable dynamic BW: %d\n", ret);
+               goto err_core_stop;
+       }
+
+       if (ar->cfg_tx_chainmask)
+               __ath10k_set_antenna(ar, ar->cfg_tx_chainmask,
+                                    ar->cfg_rx_chainmask);
 
        /*
         * By default FW set ARP frames ac to voice (6). In that case ARP
@@ -2266,15 +2475,27 @@ static int ath10k_start(struct ieee80211_hw *hw)
        ret = ath10k_wmi_pdev_set_param(ar,
                                        ar->wmi.pdev_param->arp_ac_override, 0);
        if (ret) {
-               ath10k_warn("could not set arp ac override parameter: %d\n",
+               ath10k_warn("failed to set arp ac override parameter: %d\n",
                            ret);
-               goto exit;
+               goto err_core_stop;
        }
 
+       ar->num_started_vdevs = 0;
        ath10k_regd_update(ar);
-       ret = 0;
 
-exit:
+       mutex_unlock(&ar->conf_mutex);
+       return 0;
+
+err_core_stop:
+       ath10k_core_stop(ar);
+
+err_power_down:
+       ath10k_hif_power_down(ar);
+
+err_off:
+       ar->state = ATH10K_STATE_OFF;
+
+err:
        mutex_unlock(&ar->conf_mutex);
        return ret;
 }
@@ -2283,19 +2504,15 @@ static void ath10k_stop(struct ieee80211_hw *hw)
 {
        struct ath10k *ar = hw->priv;
 
+       ath10k_drain_tx(ar);
+
        mutex_lock(&ar->conf_mutex);
-       if (ar->state == ATH10K_STATE_ON ||
-           ar->state == ATH10K_STATE_RESTARTED ||
-           ar->state == ATH10K_STATE_WEDGED)
+       if (ar->state != ATH10K_STATE_OFF) {
                ath10k_halt(ar);
-
-       ar->state = ATH10K_STATE_OFF;
+               ar->state = ATH10K_STATE_OFF;
+       }
        mutex_unlock(&ar->conf_mutex);
 
-       ath10k_mgmt_over_wmi_tx_purge(ar);
-
-       cancel_work_sync(&ar->offchan_tx_work);
-       cancel_work_sync(&ar->wmi_mgmt_tx_work);
        cancel_work_sync(&ar->restart_work);
 }
 
@@ -2309,7 +2526,7 @@ static int ath10k_config_ps(struct ath10k *ar)
        list_for_each_entry(arvif, &ar->arvifs, list) {
                ret = ath10k_mac_vif_setup_ps(arvif);
                if (ret) {
-                       ath10k_warn("could not setup powersave (%d)\n", ret);
+                       ath10k_warn("failed to setup powersave: %d\n", ret);
                        break;
                }
        }
@@ -2343,7 +2560,6 @@ static const char *chandef_get_width(enum nl80211_chan_width width)
 static void ath10k_config_chan(struct ath10k *ar)
 {
        struct ath10k_vif *arvif;
-       bool monitor_was_enabled;
        int ret;
 
        lockdep_assert_held(&ar->conf_mutex);
@@ -2357,10 +2573,8 @@ static void ath10k_config_chan(struct ath10k *ar)
 
        /* First stop monitor interface. Some FW versions crash if there's a
         * lone monitor interface. */
-       monitor_was_enabled = ar->monitor_enabled;
-
-       if (ar->monitor_enabled)
-               ath10k_monitor_stop(ar);
+       if (ar->monitor_started)
+               ath10k_monitor_vdev_stop(ar);
 
        list_for_each_entry(arvif, &ar->arvifs, list) {
                if (!arvif->is_started)
@@ -2371,7 +2585,7 @@ static void ath10k_config_chan(struct ath10k *ar)
 
                ret = ath10k_vdev_stop(arvif);
                if (ret) {
-                       ath10k_warn("could not stop vdev %d (%d)\n",
+                       ath10k_warn("failed to stop vdev %d: %d\n",
                                    arvif->vdev_id, ret);
                        continue;
                }
@@ -2388,7 +2602,7 @@ static void ath10k_config_chan(struct ath10k *ar)
 
                ret = ath10k_vdev_start(arvif);
                if (ret) {
-                       ath10k_warn("could not start vdev %d (%d)\n",
+                       ath10k_warn("failed to start vdev %d: %d\n",
                                    arvif->vdev_id, ret);
                        continue;
                }
@@ -2399,14 +2613,14 @@ static void ath10k_config_chan(struct ath10k *ar)
                ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
                                         arvif->bssid);
                if (ret) {
-                       ath10k_warn("could not bring vdev up %d (%d)\n",
+                       ath10k_warn("failed to bring vdev up %d: %d\n",
                                    arvif->vdev_id, ret);
                        continue;
                }
        }
 
-       if (monitor_was_enabled)
-               ath10k_monitor_start(ar, ar->monitor_vdev_id);
+       if (ath10k_monitor_is_enabled(ar))
+               ath10k_monitor_vdev_start(ar, ar->monitor_vdev_id);
 }
 
 static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
@@ -2420,15 +2634,17 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
 
        if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
                ath10k_dbg(ATH10K_DBG_MAC,
-                          "mac config channel %d mhz flags 0x%x\n",
+                          "mac config channel %dMHz flags 0x%x radar %d\n",
                           conf->chandef.chan->center_freq,
-                          conf->chandef.chan->flags);
+                          conf->chandef.chan->flags,
+                          conf->radar_enabled);
 
                spin_lock_bh(&ar->data_lock);
                ar->rx_channel = conf->chandef.chan;
                spin_unlock_bh(&ar->data_lock);
 
-               ath10k_config_radar_detection(ar);
+               ar->radar_enabled = conf->radar_enabled;
+               ath10k_recalc_radar_detection(ar);
 
                if (!cfg80211_chandef_identical(&ar->chandef, &conf->chandef)) {
                        ar->chandef = conf->chandef;
@@ -2444,14 +2660,14 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
                ret = ath10k_wmi_pdev_set_param(ar, param,
                                                hw->conf.power_level * 2);
                if (ret)
-                       ath10k_warn("mac failed to set 2g txpower %d (%d)\n",
+                       ath10k_warn("failed to set 2g txpower %d: %d\n",
                                    hw->conf.power_level, ret);
 
                param = ar->wmi.pdev_param->txpower_limit5g;
                ret = ath10k_wmi_pdev_set_param(ar, param,
                                                hw->conf.power_level * 2);
                if (ret)
-                       ath10k_warn("mac failed to set 5g txpower %d (%d)\n",
+                       ath10k_warn("failed to set 5g txpower %d: %d\n",
                                    hw->conf.power_level, ret);
        }
 
@@ -2459,10 +2675,19 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
                ath10k_config_ps(ar);
 
        if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
-               if (conf->flags & IEEE80211_CONF_MONITOR)
-                       ret = ath10k_monitor_create(ar);
-               else
-                       ret = ath10k_monitor_destroy(ar);
+               if (conf->flags & IEEE80211_CONF_MONITOR && !ar->monitor) {
+                       ar->monitor = true;
+                       ret = ath10k_monitor_start(ar);
+                       if (ret) {
+                               ath10k_warn("failed to start monitor (config): %d\n",
+                                           ret);
+                               ar->monitor = false;
+                       }
+               } else if (!(conf->flags & IEEE80211_CONF_MONITOR) &&
+                          ar->monitor) {
+                       ar->monitor = false;
+                       ath10k_monitor_stop(ar);
+               }
        }
 
        mutex_unlock(&ar->conf_mutex);
@@ -2497,12 +2722,6 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        INIT_WORK(&arvif->wep_key_work, ath10k_tx_wep_key_work);
        INIT_LIST_HEAD(&arvif->list);
 
-       if ((vif->type == NL80211_IFTYPE_MONITOR) && ar->monitor_present) {
-               ath10k_warn("Only one monitor interface allowed\n");
-               ret = -EBUSY;
-               goto err;
-       }
-
        bit = ffs(ar->free_vdev_map);
        if (bit == 0) {
                ret = -EBUSY;
@@ -2545,7 +2764,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type,
                                     arvif->vdev_subtype, vif->addr);
        if (ret) {
-               ath10k_warn("WMI vdev %i create failed: ret %d\n",
+               ath10k_warn("failed to create WMI vdev %i: %d\n",
                            arvif->vdev_id, ret);
                goto err;
        }
@@ -2557,7 +2776,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param,
                                        arvif->def_wep_key_idx);
        if (ret) {
-               ath10k_warn("Failed to set vdev %i default keyid: %d\n",
+               ath10k_warn("failed to set vdev %i default key id: %d\n",
                            arvif->vdev_id, ret);
                goto err_vdev_delete;
        }
@@ -2567,7 +2786,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                                        ATH10K_HW_TXRX_NATIVE_WIFI);
        /* 10.X firmware does not support this VDEV parameter. Do not warn */
        if (ret && ret != -EOPNOTSUPP) {
-               ath10k_warn("Failed to set vdev %i TX encap: %d\n",
+               ath10k_warn("failed to set vdev %i TX encapsulation: %d\n",
                            arvif->vdev_id, ret);
                goto err_vdev_delete;
        }
@@ -2575,14 +2794,14 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        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 vdev %i peer for AP: %d\n",
+                       ath10k_warn("failed to create vdev %i peer for AP: %d\n",
                                    arvif->vdev_id, ret);
                        goto err_vdev_delete;
                }
 
                ret = ath10k_mac_set_kickout(arvif);
                if (ret) {
-                       ath10k_warn("Failed to set vdev %i kickout parameters: %d\n",
+                       ath10k_warn("failed to set vdev %i kickout parameters: %d\n",
                                    arvif->vdev_id, ret);
                        goto err_peer_delete;
                }
@@ -2594,7 +2813,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
                                                  param, value);
                if (ret) {
-                       ath10k_warn("Failed to set vdev %i RX wake policy: %d\n",
+                       ath10k_warn("failed to set vdev %i RX wake policy: %d\n",
                                    arvif->vdev_id, ret);
                        goto err_peer_delete;
                }
@@ -2604,7 +2823,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
                                                  param, value);
                if (ret) {
-                       ath10k_warn("Failed to set vdev %i TX wake thresh: %d\n",
+                       ath10k_warn("failed to set vdev %i TX wake thresh: %d\n",
                                    arvif->vdev_id, ret);
                        goto err_peer_delete;
                }
@@ -2614,7 +2833,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
                                                  param, value);
                if (ret) {
-                       ath10k_warn("Failed to set vdev %i PSPOLL count: %d\n",
+                       ath10k_warn("failed to set vdev %i PSPOLL count: %d\n",
                                    arvif->vdev_id, ret);
                        goto err_peer_delete;
                }
@@ -2622,21 +2841,18 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
 
        ret = ath10k_mac_set_rts(arvif, ar->hw->wiphy->rts_threshold);
        if (ret) {
-               ath10k_warn("failed to set rts threshold for vdev %d (%d)\n",
+               ath10k_warn("failed to set rts threshold for vdev %d: %d\n",
                            arvif->vdev_id, ret);
                goto err_peer_delete;
        }
 
        ret = ath10k_mac_set_frag(arvif, ar->hw->wiphy->frag_threshold);
        if (ret) {
-               ath10k_warn("failed to set frag threshold for vdev %d (%d)\n",
+               ath10k_warn("failed to set frag threshold for vdev %d: %d\n",
                            arvif->vdev_id, ret);
                goto err_peer_delete;
        }
 
-       if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
-               ar->monitor_present = true;
-
        mutex_unlock(&ar->conf_mutex);
        return 0;
 
@@ -2668,6 +2884,9 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
 
        spin_lock_bh(&ar->data_lock);
        if (arvif->beacon) {
+               dma_unmap_single(arvif->ar->dev,
+                                ATH10K_SKB_CB(arvif->beacon)->paddr,
+                                arvif->beacon->len, DMA_TO_DEVICE);
                dev_kfree_skb_any(arvif->beacon);
                arvif->beacon = NULL;
        }
@@ -2679,7 +2898,7 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
        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 vdev %i: %d\n",
+                       ath10k_warn("failed to remove peer for AP vdev %i: %d\n",
                                    arvif->vdev_id, ret);
 
                kfree(arvif->u.ap.noa_data);
@@ -2690,12 +2909,9 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
 
        ret = ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
        if (ret)
-               ath10k_warn("WMI vdev %i delete failed: %d\n",
+               ath10k_warn("failed to delete WMI vdev %i: %d\n",
                            arvif->vdev_id, 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);
@@ -2728,28 +2944,17 @@ static void ath10k_configure_filter(struct ieee80211_hw *hw,
        *total_flags &= SUPPORTED_FILTERS;
        ar->filter_flags = *total_flags;
 
-       /* Monitor must not be started if it wasn't created first.
-        * Promiscuous mode may be started on a non-monitor interface - in
-        * such case the monitor vdev is not created so starting the
-        * monitor makes no sense. Since ath10k uses no special RX filters
-        * (only BSS filter in STA mode) there's no need for any special
-        * action here. */
-       if ((ar->filter_flags & FIF_PROMISC_IN_BSS) &&
-           !ar->monitor_enabled && ar->monitor_present) {
-               ath10k_dbg(ATH10K_DBG_MAC, "mac monitor %d start\n",
-                          ar->monitor_vdev_id);
-
-               ret = ath10k_monitor_start(ar, ar->monitor_vdev_id);
-               if (ret)
-                       ath10k_warn("Unable to start monitor mode\n");
-       } else if (!(ar->filter_flags & FIF_PROMISC_IN_BSS) &&
-                  ar->monitor_enabled && ar->monitor_present) {
-               ath10k_dbg(ATH10K_DBG_MAC, "mac monitor %d stop\n",
-                          ar->monitor_vdev_id);
-
-               ret = ath10k_monitor_stop(ar);
-               if (ret)
-                       ath10k_warn("Unable to stop monitor mode\n");
+       if (ar->filter_flags & FIF_PROMISC_IN_BSS && !ar->promisc) {
+               ar->promisc = true;
+               ret = ath10k_monitor_start(ar);
+               if (ret) {
+                       ath10k_warn("failed to start monitor (promisc): %d\n",
+                                   ret);
+                       ar->promisc = false;
+               }
+       } else if (!(ar->filter_flags & FIF_PROMISC_IN_BSS) && ar->promisc) {
+               ar->promisc = false;
+               ath10k_monitor_stop(ar);
        }
 
        mutex_unlock(&ar->conf_mutex);
@@ -2780,7 +2985,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                           arvif->vdev_id, arvif->beacon_interval);
 
                if (ret)
-                       ath10k_warn("Failed to set beacon interval for vdev %d: %i\n",
+                       ath10k_warn("failed to set beacon interval for vdev %d: %i\n",
                                    arvif->vdev_id, ret);
        }
 
@@ -2793,7 +2998,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                ret = ath10k_wmi_pdev_set_param(ar, pdev_param,
                                                WMI_BEACON_STAGGERED_MODE);
                if (ret)
-                       ath10k_warn("Failed to set beacon mode for vdev %d: %i\n",
+                       ath10k_warn("failed to set beacon mode for vdev %d: %i\n",
                                    arvif->vdev_id, ret);
        }
 
@@ -2808,7 +3013,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
                                                arvif->dtim_period);
                if (ret)
-                       ath10k_warn("Failed to set dtim period for vdev %d: %i\n",
+                       ath10k_warn("failed to set dtim period for vdev %d: %i\n",
                                    arvif->vdev_id, ret);
        }
 
@@ -2820,7 +3025,12 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                arvif->u.ap.hidden_ssid = info->hidden_ssid;
        }
 
-       if (changed & BSS_CHANGED_BSSID) {
+       /*
+        * Firmware manages AP self-peer internally so make sure to not create
+        * it in driver. Otherwise AP self-peer deletion may timeout later.
+        */
+       if (changed & BSS_CHANGED_BSSID &&
+           vif->type != NL80211_IFTYPE_AP) {
                if (!is_zero_ether_addr(info->bssid)) {
                        ath10k_dbg(ATH10K_DBG_MAC,
                                   "mac vdev %d create peer %pM\n",
@@ -2829,7 +3039,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                        ret = ath10k_peer_create(ar, arvif->vdev_id,
                                                 info->bssid);
                        if (ret)
-                               ath10k_warn("Failed to add peer %pM for vdev %d when changing bssid: %i\n",
+                               ath10k_warn("failed to add peer %pM for vdev %d when changing bssid: %i\n",
                                            info->bssid, arvif->vdev_id, ret);
 
                        if (vif->type == NL80211_IFTYPE_STATION) {
@@ -2868,20 +3078,13 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                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;
-
+               arvif->use_cts_prot = info->use_cts_prot;
                ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d cts_prot %d\n",
-                          arvif->vdev_id, cts_prot);
+                          arvif->vdev_id, info->use_cts_prot);
 
-               vdev_param = ar->wmi.vdev_param->enable_rtscts;
-               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
-                                               cts_prot);
+               ret = ath10k_recalc_rtscts_prot(arvif);
                if (ret)
-                       ath10k_warn("Failed to set CTS prot for vdev %d: %d\n",
+                       ath10k_warn("failed to recalculate rts/cts prot for vdev %d: %d\n",
                                    arvif->vdev_id, ret);
        }
 
@@ -2900,7 +3103,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
                                                slottime);
                if (ret)
-                       ath10k_warn("Failed to set erp slot for vdev %d: %i\n",
+                       ath10k_warn("failed to set erp slot for vdev %d: %i\n",
                                    arvif->vdev_id, ret);
        }
 
@@ -2919,7 +3122,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
                                                preamble);
                if (ret)
-                       ath10k_warn("Failed to set preamble for vdev %d: %i\n",
+                       ath10k_warn("failed to set preamble for vdev %d: %i\n",
                                    arvif->vdev_id, ret);
        }
 
@@ -2990,7 +3193,7 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw,
 
        ret = ath10k_start_scan(ar, &arg);
        if (ret) {
-               ath10k_warn("could not start hw scan (%d)\n", ret);
+               ath10k_warn("failed to start hw scan: %d\n", ret);
                spin_lock_bh(&ar->data_lock);
                ar->scan.in_progress = false;
                spin_unlock_bh(&ar->data_lock);
@@ -3010,8 +3213,7 @@ static void ath10k_cancel_hw_scan(struct ieee80211_hw *hw,
        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);
+               ath10k_warn("failed to abort scan: %d\n", ret);
                ieee80211_scan_completed(hw, 1 /* aborted */);
        }
        mutex_unlock(&ar->conf_mutex);
@@ -3089,7 +3291,7 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 
        if (!peer) {
                if (cmd == SET_KEY) {
-                       ath10k_warn("cannot install key for non-existent peer %pM\n",
+                       ath10k_warn("failed to install key for non-existent peer %pM\n",
                                    peer_addr);
                        ret = -EOPNOTSUPP;
                        goto exit;
@@ -3112,7 +3314,7 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 
        ret = ath10k_install_key(arvif, key, cmd, peer_addr);
        if (ret) {
-               ath10k_warn("key installation failed for vdev %i peer %pM: %d\n",
+               ath10k_warn("failed to install key for vdev %i peer %pM: %d\n",
                            arvif->vdev_id, peer_addr, ret);
                goto exit;
        }
@@ -3127,7 +3329,7 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                peer->keys[key->keyidx] = NULL;
        else if (peer == NULL)
                /* impossible unless FW goes crazy */
-               ath10k_warn("peer %pM disappeared!\n", peer_addr);
+               ath10k_warn("Peer %pM disappeared!\n", peer_addr);
        spin_unlock_bh(&ar->data_lock);
 
 exit:
@@ -3195,6 +3397,16 @@ static void ath10k_sta_rc_update_wk(struct work_struct *wk)
                                    sta->addr, smps, err);
        }
 
+       if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) {
+               ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM supp rates\n",
+                          sta->addr);
+
+               err = ath10k_station_assoc(ar, arvif, sta, true);
+               if (err)
+                       ath10k_warn("failed to reassociate station: %pM\n",
+                                   sta->addr);
+       }
+
        mutex_unlock(&ar->conf_mutex);
 }
 
@@ -3236,7 +3448,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                        max_num_peers = TARGET_NUM_PEERS;
 
                if (ar->num_peers >= max_num_peers) {
-                       ath10k_warn("Number of peers exceeded: peers number %d (max peers %d)\n",
+                       ath10k_warn("number of peers exceeded: peers number %d (max peers %d)\n",
                                    ar->num_peers, max_num_peers);
                        ret = -ENOBUFS;
                        goto exit;
@@ -3248,7 +3460,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
 
                ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
                if (ret)
-                       ath10k_warn("Failed to add peer %pM for vdev %d when adding a new sta: %i\n",
+                       ath10k_warn("failed to add peer %pM for vdev %d when adding a new sta: %i\n",
                                    sta->addr, arvif->vdev_id, ret);
        } else if ((old_state == IEEE80211_STA_NONE &&
                    new_state == IEEE80211_STA_NOTEXIST)) {
@@ -3260,7 +3472,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                           arvif->vdev_id, sta->addr);
                ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
                if (ret)
-                       ath10k_warn("Failed to delete peer %pM for vdev %d: %i\n",
+                       ath10k_warn("failed to delete peer %pM for vdev %d: %i\n",
                                    sta->addr, arvif->vdev_id, ret);
 
                if (vif->type == NL80211_IFTYPE_STATION)
@@ -3275,9 +3487,9 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                ath10k_dbg(ATH10K_DBG_MAC, "mac sta %pM associated\n",
                           sta->addr);
 
-               ret = ath10k_station_assoc(ar, arvif, sta);
+               ret = ath10k_station_assoc(ar, arvif, sta, false);
                if (ret)
-                       ath10k_warn("Failed to associate station %pM for vdev %i: %i\n",
+                       ath10k_warn("failed to associate station %pM for vdev %i: %i\n",
                                    sta->addr, arvif->vdev_id, ret);
        } else if (old_state == IEEE80211_STA_ASSOC &&
                   new_state == IEEE80211_STA_AUTH &&
@@ -3291,7 +3503,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
 
                ret = ath10k_station_disassoc(ar, arvif, sta);
                if (ret)
-                       ath10k_warn("Failed to disassociate station: %pM vdev %i ret %i\n",
+                       ath10k_warn("failed to disassociate station: %pM vdev %i: %i\n",
                                    sta->addr, arvif->vdev_id, ret);
        }
 exit:
@@ -3339,7 +3551,7 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
                                          WMI_STA_PS_PARAM_UAPSD,
                                          arvif->u.sta.uapsd);
        if (ret) {
-               ath10k_warn("could not set uapsd params %d\n", ret);
+               ath10k_warn("failed to set uapsd params: %d\n", ret);
                goto exit;
        }
 
@@ -3352,7 +3564,7 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
                                          WMI_STA_PS_PARAM_RX_WAKE_POLICY,
                                          value);
        if (ret)
-               ath10k_warn("could not set rx wake param %d\n", ret);
+               ath10k_warn("failed to set rx wake param: %d\n", ret);
 
 exit:
        return ret;
@@ -3402,13 +3614,13 @@ static int ath10k_conf_tx(struct ieee80211_hw *hw,
        /* 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);
+               ath10k_warn("failed to 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);
+               ath10k_warn("failed to set sta uapsd: %d\n", ret);
 
 exit:
        mutex_unlock(&ar->conf_mutex);
@@ -3461,7 +3673,7 @@ static int ath10k_remain_on_channel(struct ieee80211_hw *hw,
 
        ret = ath10k_start_scan(ar, &arg);
        if (ret) {
-               ath10k_warn("could not start roc scan (%d)\n", ret);
+               ath10k_warn("failed to start roc scan: %d\n", ret);
                spin_lock_bh(&ar->data_lock);
                ar->scan.in_progress = false;
                spin_unlock_bh(&ar->data_lock);
@@ -3470,7 +3682,7 @@ static int ath10k_remain_on_channel(struct ieee80211_hw *hw,
 
        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_warn("failed to switch to channel for roc scan\n");
                ath10k_abort_scan(ar);
                ret = -ETIMEDOUT;
                goto exit;
@@ -3511,7 +3723,7 @@ static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
 
                ret = ath10k_mac_set_rts(arvif, value);
                if (ret) {
-                       ath10k_warn("could not set rts threshold for vdev %d (%d)\n",
+                       ath10k_warn("failed to set rts threshold for vdev %d: %d\n",
                                    arvif->vdev_id, ret);
                        break;
                }
@@ -3534,7 +3746,7 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
 
                ret = ath10k_mac_set_rts(arvif, value);
                if (ret) {
-                       ath10k_warn("could not set fragmentation threshold for vdev %d (%d)\n",
+                       ath10k_warn("failed to set fragmentation threshold for vdev %d: %d\n",
                                    arvif->vdev_id, ret);
                        break;
                }
@@ -3544,7 +3756,8 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
        return ret;
 }
 
-static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static void ath10k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                        u32 queues, bool drop)
 {
        struct ath10k *ar = hw->priv;
        bool skip;
@@ -3573,7 +3786,7 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
                }), ATH10K_FLUSH_TIMEOUT_HZ);
 
        if (ret <= 0 || skip)
-               ath10k_warn("tx not flushed (skip %i ar-state %i): %i\n",
+               ath10k_warn("failed to flush transmit queue (skip %i ar-state %i): %i\n",
                            skip, ar->state, ret);
 
 skip:
@@ -3608,7 +3821,7 @@ static int ath10k_suspend(struct ieee80211_hw *hw,
 
        ret = ath10k_hif_suspend(ar);
        if (ret) {
-               ath10k_warn("could not suspend hif (%d)\n", ret);
+               ath10k_warn("failed to suspend hif: %d\n", ret);
                goto resume;
        }
 
@@ -3617,7 +3830,7 @@ static int ath10k_suspend(struct ieee80211_hw *hw,
 resume:
        ret = ath10k_wmi_pdev_resume_target(ar);
        if (ret)
-               ath10k_warn("could not resume target (%d)\n", ret);
+               ath10k_warn("failed to resume target: %d\n", ret);
 
        ret = 1;
 exit:
@@ -3634,14 +3847,14 @@ static int ath10k_resume(struct ieee80211_hw *hw)
 
        ret = ath10k_hif_resume(ar);
        if (ret) {
-               ath10k_warn("could not resume hif (%d)\n", ret);
+               ath10k_warn("failed to resume hif: %d\n", ret);
                ret = 1;
                goto exit;
        }
 
        ret = ath10k_wmi_pdev_resume_target(ar);
        if (ret) {
-               ath10k_warn("could not resume target (%d)\n", ret);
+               ath10k_warn("failed to resume target: %d\n", ret);
                ret = 1;
                goto exit;
        }
@@ -3964,7 +4177,7 @@ static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif,
        ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
                                        vdev_param, fixed_rate);
        if (ret) {
-               ath10k_warn("Could not set fixed_rate param 0x%02x: %d\n",
+               ath10k_warn("failed to set fixed rate param 0x%02x: %d\n",
                            fixed_rate, ret);
                ret = -EINVAL;
                goto exit;
@@ -3977,7 +4190,7 @@ static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif,
                                        vdev_param, fixed_nss);
 
        if (ret) {
-               ath10k_warn("Could not set fixed_nss param %d: %d\n",
+               ath10k_warn("failed to set fixed nss param %d: %d\n",
                            fixed_nss, ret);
                ret = -EINVAL;
                goto exit;
@@ -3990,7 +4203,7 @@ static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif,
                                        force_sgi);
 
        if (ret) {
-               ath10k_warn("Could not set sgi param %d: %d\n",
+               ath10k_warn("failed to set sgi param %d: %d\n",
                            force_sgi, ret);
                ret = -EINVAL;
                goto exit;
@@ -4026,7 +4239,7 @@ static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw,
        }
 
        if (fixed_rate == WMI_FIXED_RATE_NONE && force_sgi) {
-               ath10k_warn("Could not force SGI usage for default rate settings\n");
+               ath10k_warn("failed to force SGI usage for default rate settings\n");
                return -EINVAL;
        }
 
@@ -4034,14 +4247,6 @@ static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw,
                                           fixed_nss, force_sgi);
 }
 
-static void ath10k_channel_switch_beacon(struct ieee80211_hw *hw,
-                                        struct ieee80211_vif *vif,
-                                        struct cfg80211_chan_def *chandef)
-{
-       /* there's no need to do anything here. vif->csa_active is enough */
-       return;
-}
-
 static void ath10k_sta_rc_update(struct ieee80211_hw *hw,
                                 struct ieee80211_vif *vif,
                                 struct ieee80211_sta *sta,
@@ -4072,8 +4277,8 @@ static void ath10k_sta_rc_update(struct ieee80211_hw *hw,
                        bw = WMI_PEER_CHWIDTH_80MHZ;
                        break;
                case IEEE80211_STA_RX_BW_160:
-                       ath10k_warn("mac sta rc update for %pM: invalid bw %d\n",
-                                   sta->addr, sta->bandwidth);
+                       ath10k_warn("Invalid bandwith %d in rc update for %pM\n",
+                                   sta->bandwidth, sta->addr);
                        bw = WMI_PEER_CHWIDTH_20MHZ;
                        break;
                }
@@ -4099,8 +4304,8 @@ static void ath10k_sta_rc_update(struct ieee80211_hw *hw,
                        smps = WMI_PEER_SMPS_DYNAMIC;
                        break;
                case IEEE80211_SMPS_NUM_MODES:
-                       ath10k_warn("mac sta rc update for %pM: invalid smps: %d\n",
-                                   sta->addr, sta->smps_mode);
+                       ath10k_warn("Invalid smps %d in sta rc update for %pM\n",
+                                   sta->smps_mode, sta->addr);
                        smps = WMI_PEER_SMPS_PS_NONE;
                        break;
                }
@@ -4108,15 +4313,6 @@ static void ath10k_sta_rc_update(struct ieee80211_hw *hw,
                arsta->smps = smps;
        }
 
-       if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) {
-               /* FIXME: Not implemented. Probably the only way to do it would
-                * be to re-assoc the peer. */
-               changed &= ~IEEE80211_RC_SUPP_RATES_CHANGED;
-               ath10k_dbg(ATH10K_DBG_MAC,
-                          "mac sta rc update for %pM: changing supported rates not implemented\n",
-                          sta->addr);
-       }
-
        arsta->changed |= changed;
 
        spin_unlock_bh(&ar->data_lock);
@@ -4154,10 +4350,11 @@ static const struct ieee80211_ops ath10k_ops = {
        .set_frag_threshold             = ath10k_set_frag_threshold,
        .flush                          = ath10k_flush,
        .tx_last_beacon                 = ath10k_tx_last_beacon,
+       .set_antenna                    = ath10k_set_antenna,
+       .get_antenna                    = ath10k_get_antenna,
        .restart_complete               = ath10k_restart_complete,
        .get_survey                     = ath10k_get_survey,
        .set_bitrate_mask               = ath10k_set_bitrate_mask,
-       .channel_switch_beacon          = ath10k_channel_switch_beacon,
        .sta_rc_update                  = ath10k_sta_rc_update,
        .get_tsf                        = ath10k_get_tsf,
 #ifdef CONFIG_PM
@@ -4503,6 +4700,18 @@ int ath10k_mac_register(struct ath10k *ar)
                BIT(NL80211_IFTYPE_ADHOC) |
                BIT(NL80211_IFTYPE_AP);
 
+       if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+               /* TODO:  Have to deal with 2x2 chips if/when the come out. */
+               ar->supp_tx_chainmask = TARGET_10X_TX_CHAIN_MASK;
+               ar->supp_rx_chainmask = TARGET_10X_RX_CHAIN_MASK;
+       } else {
+               ar->supp_tx_chainmask = TARGET_TX_CHAIN_MASK;
+               ar->supp_rx_chainmask = TARGET_RX_CHAIN_MASK;
+       }
+
+       ar->hw->wiphy->available_antennas_rx = ar->supp_rx_chainmask;
+       ar->hw->wiphy->available_antennas_tx = ar->supp_tx_chainmask;
+
        if (!test_bit(ATH10K_FW_FEATURE_NO_P2P, ar->fw_features))
                ar->hw->wiphy->interface_modes |=
                        BIT(NL80211_IFTYPE_P2P_CLIENT) |
@@ -4516,7 +4725,6 @@ int ath10k_mac_register(struct ath10k *ar)
                        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 |
                        IEEE80211_HW_SPECTRUM_MGMT;
 
@@ -4570,19 +4778,19 @@ int ath10k_mac_register(struct ath10k *ar)
                                                             NL80211_DFS_UNSET);
 
                if (!ar->dfs_detector)
-                       ath10k_warn("dfs pattern detector init failed\n");
+                       ath10k_warn("failed to initialise DFS pattern detector\n");
        }
 
        ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy,
                            ath10k_reg_notifier);
        if (ret) {
-               ath10k_err("Regulatory initialization failed: %i\n", ret);
+               ath10k_err("failed to initialise regulatory: %i\n", ret);
                goto err_free;
        }
 
        ret = ieee80211_register_hw(ar->hw);
        if (ret) {
-               ath10k_err("ieee80211 registration failed: %d\n", ret);
+               ath10k_err("failed to register ieee80211: %d\n", ret);
                goto err_free;
        }
 
index 9d242d801d9d354f772b74257826427f0a598180..d0004d59c97ec6c9292266662d9fd93cdd9eaf5e 100644 (file)
@@ -39,15 +39,28 @@ enum ath10k_pci_irq_mode {
        ATH10K_PCI_IRQ_MSI = 2,
 };
 
-static unsigned int ath10k_target_ps;
+enum ath10k_pci_reset_mode {
+       ATH10K_PCI_RESET_AUTO = 0,
+       ATH10K_PCI_RESET_WARM_ONLY = 1,
+};
+
+static unsigned int ath10k_pci_target_ps;
 static unsigned int ath10k_pci_irq_mode = ATH10K_PCI_IRQ_AUTO;
+static unsigned int ath10k_pci_reset_mode = ATH10K_PCI_RESET_AUTO;
 
-module_param(ath10k_target_ps, uint, 0644);
-MODULE_PARM_DESC(ath10k_target_ps, "Enable ath10k Target (SoC) PS option");
+module_param_named(target_ps, ath10k_pci_target_ps, uint, 0644);
+MODULE_PARM_DESC(target_ps, "Enable ath10k Target (SoC) PS option");
 
 module_param_named(irq_mode, ath10k_pci_irq_mode, uint, 0644);
 MODULE_PARM_DESC(irq_mode, "0: auto, 1: legacy, 2: msi (default: 0)");
 
+module_param_named(reset_mode, ath10k_pci_reset_mode, uint, 0644);
+MODULE_PARM_DESC(reset_mode, "0: auto, 1: warm only (default: 0)");
+
+/* how long wait to wait for target to initialise, in ms */
+#define ATH10K_PCI_TARGET_WAIT 3000
+#define ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS 3
+
 #define QCA988X_2_0_DEVICE_ID  (0x003c)
 
 static DEFINE_PCI_DEVICE_TABLE(ath10k_pci_id_table) = {
@@ -346,9 +359,10 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
         *   2) Buffer in DMA-able space
         */
        orig_nbytes = nbytes;
-       data_buf = (unsigned char *)pci_alloc_consistent(ar_pci->pdev,
-                                                        orig_nbytes,
-                                                        &ce_data_base);
+       data_buf = (unsigned char *)dma_alloc_coherent(ar->dev,
+                                                      orig_nbytes,
+                                                      &ce_data_base,
+                                                      GFP_ATOMIC);
 
        if (!data_buf) {
                ret = -ENOMEM;
@@ -442,12 +456,12 @@ done:
                                __le32_to_cpu(((__le32 *)data_buf)[i]);
                }
        } else
-               ath10k_dbg(ATH10K_DBG_PCI, "%s failure (0x%x)\n",
-                          __func__, address);
+               ath10k_warn("failed to read diag value at 0x%x: %d\n",
+                           address, ret);
 
        if (data_buf)
-               pci_free_consistent(ar_pci->pdev, orig_nbytes,
-                                   data_buf, ce_data_base);
+               dma_free_coherent(ar->dev, orig_nbytes, data_buf,
+                                 ce_data_base);
 
        return ret;
 }
@@ -490,9 +504,10 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
         *   2) Buffer in DMA-able space
         */
        orig_nbytes = nbytes;
-       data_buf = (unsigned char *)pci_alloc_consistent(ar_pci->pdev,
-                                                        orig_nbytes,
-                                                        &ce_data_base);
+       data_buf = (unsigned char *)dma_alloc_coherent(ar->dev,
+                                                      orig_nbytes,
+                                                      &ce_data_base,
+                                                      GFP_ATOMIC);
        if (!data_buf) {
                ret = -ENOMEM;
                goto done;
@@ -588,13 +603,13 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
 
 done:
        if (data_buf) {
-               pci_free_consistent(ar_pci->pdev, orig_nbytes, data_buf,
-                                   ce_data_base);
+               dma_free_coherent(ar->dev, orig_nbytes, data_buf,
+                                 ce_data_base);
        }
 
        if (ret != 0)
-               ath10k_dbg(ATH10K_DBG_PCI, "%s failure (0x%x)\n", __func__,
-                          address);
+               ath10k_warn("failed to write diag value at 0x%x: %d\n",
+                           address, ret);
 
        return ret;
 }
@@ -747,17 +762,21 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
        struct ath10k_pci_pipe *pci_pipe = &ar_pci->pipe_info[pipe_id];
        struct ath10k_ce_pipe *ce_pipe = pci_pipe->ce_hdl;
        struct ath10k_ce_ring *src_ring = ce_pipe->src_ring;
-       unsigned int nentries_mask = src_ring->nentries_mask;
-       unsigned int sw_index = src_ring->sw_index;
-       unsigned int write_index = src_ring->write_index;
-       int err, i;
+       unsigned int nentries_mask;
+       unsigned int sw_index;
+       unsigned int write_index;
+       int err, i = 0;
 
        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 (unlikely(CE_RING_DELTA(nentries_mask,
                                   write_index, sw_index - 1) < n_items)) {
                err = -ENOBUFS;
-               goto unlock;
+               goto err;
        }
 
        for (i = 0; i < n_items - 1; i++) {
@@ -774,7 +793,7 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
                                            items[i].transfer_id,
                                            CE_SEND_FLAG_GATHER);
                if (err)
-                       goto unlock;
+                       goto err;
        }
 
        /* `i` is equal to `n_items -1` after for() */
@@ -792,10 +811,15 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
                                    items[i].transfer_id,
                                    0);
        if (err)
-               goto unlock;
+               goto err;
+
+       spin_unlock_bh(&ar_pci->ce_lock);
+       return 0;
+
+err:
+       for (; i > 0; i--)
+               __ath10k_ce_send_revert(ce_pipe);
 
-       err = 0;
-unlock:
        spin_unlock_bh(&ar_pci->ce_lock);
        return err;
 }
@@ -803,6 +827,9 @@ unlock:
 static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       ath10k_dbg(ATH10K_DBG_PCI, "pci hif get free queue number\n");
+
        return ath10k_ce_num_free_src_entries(ar_pci->pipe_info[pipe].ce_hdl);
 }
 
@@ -854,6 +881,8 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar)
 static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
                                               int force)
 {
+       ath10k_dbg(ATH10K_DBG_PCI, "pci hif send complete check\n");
+
        if (!force) {
                int resources;
                /*
@@ -880,7 +909,7 @@ static void ath10k_pci_hif_set_callbacks(struct ath10k *ar,
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
 
-       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+       ath10k_dbg(ATH10K_DBG_PCI, "pci hif set callbacks\n");
 
        memcpy(&ar_pci->msg_callbacks_current, callbacks,
               sizeof(ar_pci->msg_callbacks_current));
@@ -938,6 +967,8 @@ static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar,
 {
        int ret = 0;
 
+       ath10k_dbg(ATH10K_DBG_PCI, "pci hif map service\n");
+
        /* polling for received messages not supported */
        *dl_is_polled = 0;
 
@@ -997,6 +1028,8 @@ static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar,
 {
        int ul_is_polled, dl_is_polled;
 
+       ath10k_dbg(ATH10K_DBG_PCI, "pci hif get default pipe\n");
+
        (void)ath10k_pci_hif_map_service_to_pipe(ar,
                                                 ATH10K_HTC_SVC_ID_RSVD_CTRL,
                                                 ul_pipe,
@@ -1098,6 +1131,8 @@ static int ath10k_pci_hif_start(struct ath10k *ar)
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        int ret, ret_early;
 
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot hif start\n");
+
        ath10k_pci_free_early_irq(ar);
        ath10k_pci_kill_tasklet(ar);
 
@@ -1233,18 +1268,10 @@ static void ath10k_pci_buffer_cleanup(struct ath10k *ar)
 
 static void ath10k_pci_ce_deinit(struct ath10k *ar)
 {
-       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       struct ath10k_pci_pipe *pipe_info;
-       int pipe_num;
+       int i;
 
-       for (pipe_num = 0; pipe_num < 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;
-               }
-       }
+       for (i = 0; i < CE_COUNT; i++)
+               ath10k_ce_deinit_pipe(ar, i);
 }
 
 static void ath10k_pci_hif_stop(struct ath10k *ar)
@@ -1252,7 +1279,10 @@ static void ath10k_pci_hif_stop(struct ath10k *ar)
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        int ret;
 
-       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot hif stop\n");
+
+       if (WARN_ON(!ar_pci->started))
+               return;
 
        ret = ath10k_ce_disable_interrupts(ar);
        if (ret)
@@ -1697,30 +1727,49 @@ static int ath10k_pci_init_config(struct ath10k *ar)
        return 0;
 }
 
+static int ath10k_pci_alloc_ce(struct ath10k *ar)
+{
+       int i, ret;
+
+       for (i = 0; i < CE_COUNT; i++) {
+               ret = ath10k_ce_alloc_pipe(ar, i, &host_ce_config_wlan[i]);
+               if (ret) {
+                       ath10k_err("failed to allocate copy engine pipe %d: %d\n",
+                                  i, ret);
+                       return ret;
+               }
+       }
 
+       return 0;
+}
+
+static void ath10k_pci_free_ce(struct ath10k *ar)
+{
+       int i;
+
+       for (i = 0; i < CE_COUNT; i++)
+               ath10k_ce_free_pipe(ar, i);
+}
 
 static int ath10k_pci_ce_init(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        struct ath10k_pci_pipe *pipe_info;
        const struct ce_attr *attr;
-       int pipe_num;
+       int pipe_num, ret;
 
        for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) {
                pipe_info = &ar_pci->pipe_info[pipe_num];
+               pipe_info->ce_hdl = &ar_pci->ce_states[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("failed 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;
+               ret = ath10k_ce_init_pipe(ar, pipe_num, attr);
+               if (ret) {
+                       ath10k_err("failed to initialize copy engine pipe %d: %d\n",
+                                  pipe_num, ret);
+                       return ret;
                }
 
                if (pipe_num == CE_COUNT - 1) {
@@ -1741,16 +1790,15 @@ static int ath10k_pci_ce_init(struct ath10k *ar)
 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;
+       u32 fw_indicator;
 
        ath10k_pci_wake(ar);
 
-       fw_indicator_address = ar_pci->fw_indicator_address;
-       fw_indicator = ath10k_pci_read32(ar, 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,
+               ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS,
                                   fw_indicator & ~FW_IND_EVENT_PENDING);
 
                if (ar_pci->started) {
@@ -1767,13 +1815,32 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar)
        ath10k_pci_sleep(ar);
 }
 
+/* this function effectively clears target memory controller assert line */
+static void ath10k_pci_warm_reset_si0(struct ath10k *ar)
+{
+       u32 val;
+
+       val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS);
+       ath10k_pci_soc_write32(ar, SOC_RESET_CONTROL_ADDRESS,
+                              val | SOC_RESET_CONTROL_SI0_RST_MASK);
+       val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS);
+
+       msleep(10);
+
+       val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS);
+       ath10k_pci_soc_write32(ar, SOC_RESET_CONTROL_ADDRESS,
+                              val & ~SOC_RESET_CONTROL_SI0_RST_MASK);
+       val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS);
+
+       msleep(10);
+}
+
 static int ath10k_pci_warm_reset(struct ath10k *ar)
 {
-       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        int ret = 0;
        u32 val;
 
-       ath10k_dbg(ATH10K_DBG_BOOT, "boot performing warm chip reset\n");
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot warm reset\n");
 
        ret = ath10k_do_pci_wake(ar);
        if (ret) {
@@ -1801,7 +1868,7 @@ static int ath10k_pci_warm_reset(struct ath10k *ar)
        msleep(100);
 
        /* clear fw indicator */
-       ath10k_pci_write32(ar, ar_pci->fw_indicator_address, 0);
+       ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, 0);
 
        /* clear target LF timer interrupts */
        val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
@@ -1826,6 +1893,8 @@ static int ath10k_pci_warm_reset(struct ath10k *ar)
                                SOC_RESET_CONTROL_ADDRESS);
        msleep(10);
 
+       ath10k_pci_warm_reset_si0(ar);
+
        /* debug */
        val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
                                PCIE_INTR_CAUSE_ADDRESS);
@@ -1934,7 +2003,9 @@ static int __ath10k_pci_hif_power_up(struct ath10k *ar, bool cold_reset)
                irq_mode = "legacy";
 
        if (!test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags))
-               ath10k_info("pci irq %s\n", irq_mode);
+               ath10k_info("pci irq %s irq_mode %d reset_mode %d\n",
+                           irq_mode, ath10k_pci_irq_mode,
+                           ath10k_pci_reset_mode);
 
        return 0;
 
@@ -1952,23 +2023,52 @@ err:
        return ret;
 }
 
+static int ath10k_pci_hif_power_up_warm(struct ath10k *ar)
+{
+       int i, ret;
+
+       /*
+        * Sometime warm reset succeeds after retries.
+        *
+        * FIXME: It might be possible to tune ath10k_pci_warm_reset() to work
+        * at first try.
+        */
+       for (i = 0; i < ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS; i++) {
+               ret = __ath10k_pci_hif_power_up(ar, false);
+               if (ret == 0)
+                       break;
+
+               ath10k_warn("failed to warm reset (attempt %d out of %d): %d\n",
+                           i + 1, ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS, ret);
+       }
+
+       return ret;
+}
+
 static int ath10k_pci_hif_power_up(struct ath10k *ar)
 {
        int ret;
 
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot hif power up\n");
+
        /*
         * Hardware CUS232 version 2 has some issues with cold reset and the
         * preferred (and safer) way to perform a device reset is through a
         * warm reset.
         *
-        * Warm reset doesn't always work though (notably after a firmware
-        * crash) so fall back to cold reset if necessary.
+        * Warm reset doesn't always work though so fall back to cold reset may
+        * be necessary.
         */
-       ret = __ath10k_pci_hif_power_up(ar, false);
+       ret = ath10k_pci_hif_power_up_warm(ar);
        if (ret) {
-               ath10k_warn("failed to power up target using warm reset (%d), trying cold reset\n",
+               ath10k_warn("failed to power up target using warm reset: %d\n",
                            ret);
 
+               if (ath10k_pci_reset_mode == ATH10K_PCI_RESET_WARM_ONLY)
+                       return ret;
+
+               ath10k_warn("trying cold reset\n");
+
                ret = __ath10k_pci_hif_power_up(ar, true);
                if (ret) {
                        ath10k_err("failed to power up target using cold reset too (%d)\n",
@@ -1984,12 +2084,14 @@ static void ath10k_pci_hif_power_down(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
 
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot hif power down\n");
+
        ath10k_pci_free_early_irq(ar);
        ath10k_pci_kill_tasklet(ar);
        ath10k_pci_deinit_irq(ar);
+       ath10k_pci_ce_deinit(ar);
        ath10k_pci_warm_reset(ar);
 
-       ath10k_pci_ce_deinit(ar);
        if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
                ath10k_do_pci_sleep(ar);
 }
@@ -2137,7 +2239,6 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg)
 static void ath10k_pci_early_irq_tasklet(unsigned long data)
 {
        struct ath10k *ar = (struct ath10k *)data;
-       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        u32 fw_ind;
        int ret;
 
@@ -2148,14 +2249,11 @@ static void ath10k_pci_early_irq_tasklet(unsigned long data)
                return;
        }
 
-       fw_ind = ath10k_pci_read32(ar, ar_pci->fw_indicator_address);
+       fw_ind = ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS);
        if (fw_ind & FW_IND_EVENT_PENDING) {
-               ath10k_pci_write32(ar, ar_pci->fw_indicator_address,
+               ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS,
                                   fw_ind & ~FW_IND_EVENT_PENDING);
-
-               /* Some structures are unavailable during early boot or at
-                * driver teardown so just print that the device has crashed. */
-               ath10k_warn("device crashed - no diagnostics available\n");
+               ath10k_pci_hif_dump_area(ar);
        }
 
        ath10k_pci_sleep(ar);
@@ -2385,33 +2483,69 @@ static int ath10k_pci_deinit_irq(struct ath10k *ar)
 static int ath10k_pci_wait_for_target_init(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       int wait_limit = 300; /* 3 sec */
+       unsigned long timeout;
        int ret;
+       u32 val;
+
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot waiting target to initialise\n");
 
        ret = ath10k_pci_wake(ar);
        if (ret) {
-               ath10k_err("failed to wake up target: %d\n", ret);
+               ath10k_err("failed to wake up target for init: %d\n", ret);
                return ret;
        }
 
-       while (wait_limit-- &&
-              !(ioread32(ar_pci->mem + FW_INDICATOR_ADDRESS) &
-                FW_IND_INITIALIZED)) {
+       timeout = jiffies + msecs_to_jiffies(ATH10K_PCI_TARGET_WAIT);
+
+       do {
+               val = ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS);
+
+               ath10k_dbg(ATH10K_DBG_BOOT, "boot target indicator %x\n", val);
+
+               /* target should never return this */
+               if (val == 0xffffffff)
+                       continue;
+
+               /* the device has crashed so don't bother trying anymore */
+               if (val & FW_IND_EVENT_PENDING)
+                       break;
+
+               if (val & FW_IND_INITIALIZED)
+                       break;
+
                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));
+                       ath10k_pci_soc_write32(ar, PCIE_INTR_ENABLE_ADDRESS,
+                                              PCIE_INTR_FIRMWARE_MASK |
+                                              PCIE_INTR_CE_MASK_ALL);
+
                mdelay(10);
-       }
+       } while (time_before(jiffies, timeout));
 
-       if (wait_limit < 0) {
-               ath10k_err("target stalled\n");
+       if (val == 0xffffffff) {
+               ath10k_err("failed to read device register, device is gone\n");
                ret = -EIO;
                goto out;
        }
 
+       if (val & FW_IND_EVENT_PENDING) {
+               ath10k_warn("device has crashed during init\n");
+               ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS,
+                                  val & ~FW_IND_EVENT_PENDING);
+               ath10k_pci_hif_dump_area(ar);
+               ret = -ECOMM;
+               goto out;
+       }
+
+       if (!(val & FW_IND_INITIALIZED)) {
+               ath10k_err("failed to receive initialized event from target: %08x\n",
+                          val);
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot target initialised\n");
+
 out:
        ath10k_pci_sleep(ar);
        return ret;
@@ -2422,6 +2556,8 @@ static int ath10k_pci_cold_reset(struct ath10k *ar)
        int i, ret;
        u32 val;
 
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot cold reset\n");
+
        ret = ath10k_do_pci_wake(ar);
        if (ret) {
                ath10k_err("failed to wake up target: %d\n",
@@ -2453,6 +2589,9 @@ static int ath10k_pci_cold_reset(struct ath10k *ar)
        }
 
        ath10k_do_pci_sleep(ar);
+
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot cold reset complete\n");
+
        return 0;
 }
 
@@ -2484,7 +2623,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
        struct ath10k_pci *ar_pci;
        u32 lcr_val, chip_id;
 
-       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+       ath10k_dbg(ATH10K_DBG_PCI, "pci probe\n");
 
        ar_pci = kzalloc(sizeof(*ar_pci), GFP_KERNEL);
        if (ar_pci == NULL)
@@ -2503,7 +2642,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
                goto err_ar_pci;
        }
 
-       if (ath10k_target_ps)
+       if (ath10k_pci_target_ps)
                set_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features);
 
        ath10k_pci_dump_features(ar_pci);
@@ -2516,23 +2655,10 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
        }
 
        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("failed to assign PCI space: %d\n", ret);
-               goto err_ar;
-       }
-
        ret = pci_enable_device(pdev);
        if (ret) {
                ath10k_err("failed to enable PCI device: %d\n", ret);
@@ -2594,16 +2720,24 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
 
        ath10k_do_pci_sleep(ar);
 
+       ret = ath10k_pci_alloc_ce(ar);
+       if (ret) {
+               ath10k_err("failed to allocate copy engine pipes: %d\n", ret);
+               goto err_iomap;
+       }
+
        ath10k_dbg(ATH10K_DBG_BOOT, "boot pci_mem 0x%p\n", ar_pci->mem);
 
        ret = ath10k_core_register(ar, chip_id);
        if (ret) {
                ath10k_err("failed to register driver core: %d\n", ret);
-               goto err_iomap;
+               goto err_free_ce;
        }
 
        return 0;
 
+err_free_ce:
+       ath10k_pci_free_ce(ar);
 err_iomap:
        pci_iounmap(pdev, mem);
 err_master:
@@ -2626,7 +2760,7 @@ 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__);
+       ath10k_dbg(ATH10K_DBG_PCI, "pci remove\n");
 
        if (!ar)
                return;
@@ -2636,9 +2770,8 @@ static void ath10k_pci_remove(struct pci_dev *pdev)
        if (!ar_pci)
                return;
 
-       tasklet_kill(&ar_pci->msi_fw_err);
-
        ath10k_core_unregister(ar);
+       ath10k_pci_free_ce(ar);
 
        pci_iounmap(pdev, ar_pci->mem);
        pci_release_region(pdev, BAR_NUM);
@@ -2680,6 +2813,5 @@ 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_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_FW_2_FILE);
 MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_BOARD_DATA_FILE);
index b43fdb4f731973544e1a55f700c779e4191d76bb..dfdebb4157aa177acde13ea1149b5a7490c5593e 100644 (file)
@@ -189,9 +189,6 @@ struct ath10k_pci {
 
        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 ath10k_ce_pipe *ce_diag;
 
index 0541dd939ce9d8be7dc7e195ef952526abab0255..82669a77e553b8f6902c7dc03f99e24b42a3dea6 100644 (file)
@@ -100,189 +100,6 @@ exit:
                wake_up(&htt->empty_tx_wq);
 }
 
-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) + 1;
-               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->vht_flag |= RX_VHT_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->mic_err)
-               status->flag |= RX_FLAG_MMIC_ERROR;
-
-       if (info->fcs_err)
-               status->flag |= RX_FLAG_FAILED_FCS_CRC;
-
-       if (info->amsdu_more)
-               status->flag |= RX_FLAG_AMSDU_MORE;
-
-       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;
-
-       if (info->rate.info0 & HTT_RX_INDICATION_INFO0_END_VALID) {
-               /* TSF available only in 32-bit */
-               status->mactime = info->tsf & 0xffffffff;
-               status->flag |= RX_FLAG_MACTIME_END;
-       }
-
-       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 flag 0x%x fcs-err %i\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->vht_flag & RX_VHT_FLAG_80MHZ ? "80" : "",
-                  status->flag & RX_FLAG_SHORT_GI ? "sgi " : "",
-                  status->rate_idx,
-                  status->vht_nss,
-                  status->freq,
-                  status->band, status->flag, info->fcs_err);
-       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ",
-                       info->skb->data, info->skb->len);
-
-       ieee80211_rx(ar->hw, info->skb);
-}
-
 struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id,
                                     const u8 *addr)
 {
index 356dc9c04c9e3981feaeb04d8df4e8f45fc36f7f..aee3e20058f814f2e7a774005d84d77c684442f4 100644 (file)
@@ -21,7 +21,6 @@
 
 void ath10k_txrx_tx_unref(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);
index cb1f7b5bcf4cdefa774411e09fa39b80728d82e4..4b7782a529ac6b03c6a1bcd764dabc6568b70a6b 100644 (file)
@@ -639,6 +639,7 @@ int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb)
        struct sk_buff *wmi_skb;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        int len;
+       u32 buf_len = skb->len;
        u16 fc;
 
        hdr = (struct ieee80211_hdr *)skb->data;
@@ -648,6 +649,15 @@ int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb)
                return -EINVAL;
 
        len = sizeof(cmd->hdr) + skb->len;
+
+       if ((ieee80211_is_action(hdr->frame_control) ||
+            ieee80211_is_deauth(hdr->frame_control) ||
+            ieee80211_is_disassoc(hdr->frame_control)) &&
+            ieee80211_has_protected(hdr->frame_control)) {
+               len += IEEE80211_CCMP_MIC_LEN;
+               buf_len += IEEE80211_CCMP_MIC_LEN;
+       }
+
        len = round_up(len, 4);
 
        wmi_skb = ath10k_wmi_alloc_skb(len);
@@ -659,7 +669,7 @@ int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb)
        cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(skb)->vdev_id);
        cmd->hdr.tx_rate = 0;
        cmd->hdr.tx_power = 0;
-       cmd->hdr.buf_len = __cpu_to_le32((u32)(skb->len));
+       cmd->hdr.buf_len = __cpu_to_le32(buf_len);
 
        memcpy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr), ETH_ALEN);
        memcpy(cmd->buf, skb->data, skb->len);
@@ -957,10 +967,16 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
         * frames with Protected Bit set. */
        if (ieee80211_has_protected(hdr->frame_control) &&
            !ieee80211_is_auth(hdr->frame_control)) {
-               status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED |
-                               RX_FLAG_MMIC_STRIPPED;
-               hdr->frame_control = __cpu_to_le16(fc &
+               status->flag |= RX_FLAG_DECRYPTED;
+
+               if (!ieee80211_is_action(hdr->frame_control) &&
+                   !ieee80211_is_deauth(hdr->frame_control) &&
+                   !ieee80211_is_disassoc(hdr->frame_control)) {
+                       status->flag |= RX_FLAG_IV_STRIPPED |
+                                       RX_FLAG_MMIC_STRIPPED;
+                       hdr->frame_control = __cpu_to_le16(fc &
                                        ~IEEE80211_FCTL_PROTECTED);
+               }
        }
 
        ath10k_dbg(ATH10K_DBG_MGMT,
@@ -1362,13 +1378,10 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
        struct sk_buff *bcn;
        int ret, vdev_id = 0;
 
-       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",
+       ath10k_dbg(ATH10K_DBG_MGMT, "mgmt swba vdev_map 0x%x\n",
                   ev->vdev_map);
 
        for (; map; map >>= 1, vdev_id++) {
@@ -1385,12 +1398,7 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
                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",
+                          "mgmt event bcn_info %d tim_len %d mcast %d changed %d num_ps_pending %d 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),
@@ -1439,6 +1447,7 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
                                         ATH10K_SKB_CB(arvif->beacon)->paddr,
                                         arvif->beacon->len, DMA_TO_DEVICE);
                        dev_kfree_skb_any(arvif->beacon);
+                       arvif->beacon = NULL;
                }
 
                ATH10K_SKB_CB(bcn)->paddr = dma_map_single(arvif->ar->dev,
@@ -1448,6 +1457,7 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
                                        ATH10K_SKB_CB(bcn)->paddr);
                if (ret) {
                        ath10k_warn("failed to map beacon: %d\n", ret);
+                       dev_kfree_skb_any(bcn);
                        goto skip;
                }
 
@@ -2365,7 +2375,7 @@ void ath10k_wmi_detach(struct ath10k *ar)
        ar->wmi.num_mem_chunks = 0;
 }
 
-int ath10k_wmi_connect_htc_service(struct ath10k *ar)
+int ath10k_wmi_connect(struct ath10k *ar)
 {
        int status;
        struct ath10k_htc_svc_conn_req conn_req;
@@ -2393,8 +2403,9 @@ int ath10k_wmi_connect_htc_service(struct ath10k *ar)
        return 0;
 }
 
-int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
-                                 u16 rd5g, u16 ctl2g, u16 ctl5g)
+static int ath10k_wmi_main_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;
@@ -2418,6 +2429,46 @@ int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
                                   ar->wmi.cmd->pdev_set_regdomain_cmdid);
 }
 
+static int ath10k_wmi_10x_pdev_set_regdomain(struct ath10k *ar, u16 rd,
+                                            u16 rd2g, u16 rd5g,
+                                            u16 ctl2g, u16 ctl5g,
+                                            enum wmi_dfs_region dfs_reg)
+{
+       struct wmi_pdev_set_regdomain_cmd_10x *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_pdev_set_regdomain_cmd_10x *)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);
+       cmd->dfs_domain = __cpu_to_le32(dfs_reg);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x dfs_region %x\n",
+                  rd, rd2g, rd5g, ctl2g, ctl5g, dfs_reg);
+
+       return ath10k_wmi_cmd_send(ar, skb,
+                                  ar->wmi.cmd->pdev_set_regdomain_cmdid);
+}
+
+int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
+                                 u16 rd5g, u16 ctl2g, u16 ctl5g,
+                                 enum wmi_dfs_region dfs_reg)
+{
+       if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+               return ath10k_wmi_10x_pdev_set_regdomain(ar, rd, rd2g, rd5g,
+                                                       ctl2g, ctl5g, dfs_reg);
+       else
+               return ath10k_wmi_main_pdev_set_regdomain(ar, rd, rd2g, rd5g,
+                                                        ctl2g, ctl5g);
+}
+
 int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
                                const struct wmi_channel_arg *arg)
 {
@@ -3456,8 +3507,9 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar,
                __cpu_to_le32(arg->peer_vht_rates.tx_mcs_set);
 
        ath10k_dbg(ATH10K_DBG_WMI,
-                  "wmi peer assoc vdev %d addr %pM\n",
-                  arg->vdev_id, arg->addr);
+                  "wmi peer assoc vdev %d addr %pM (%s)\n",
+                  arg->vdev_id, arg->addr,
+                  arg->peer_reassoc ? "reassociate" : "new");
        return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_assoc_cmdid);
 }
 
index f51d5ca0141fc61cfe2e84859c2ea2ca66160eca..e93df2c104136a10f03052908ef45ca38f514e6c 100644 (file)
@@ -198,16 +198,6 @@ struct wmi_mac_addr {
        } __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)
-
 struct wmi_cmd_map {
        u32 init_cmdid;
        u32 start_scan_cmdid;
@@ -2185,6 +2175,31 @@ struct wmi_pdev_set_regdomain_cmd {
        __le32 conformance_test_limit_5G;
 } __packed;
 
+enum wmi_dfs_region {
+       /* Uninitialized dfs domain */
+       WMI_UNINIT_DFS_DOMAIN = 0,
+
+       /* FCC3 dfs domain */
+       WMI_FCC_DFS_DOMAIN = 1,
+
+       /* ETSI dfs domain */
+       WMI_ETSI_DFS_DOMAIN = 2,
+
+       /*Japan dfs domain */
+       WMI_MKK4_DFS_DOMAIN = 3,
+};
+
+struct wmi_pdev_set_regdomain_cmd_10x {
+       __le32 reg_domain;
+       __le32 reg_domain_2G;
+       __le32 reg_domain_5G;
+       __le32 conformance_test_limit_2G;
+       __le32 conformance_test_limit_5G;
+
+       /* dfs domain from wmi_dfs_region */
+       __le32 dfs_domain;
+} __packed;
+
 /* Command to set/unset chip in quiet mode */
 struct wmi_pdev_set_quiet_cmd {
        /* period in TUs */
@@ -2210,6 +2225,19 @@ enum ath10k_protmode {
        ATH10K_PROT_RTSCTS   = 2,    /* RTS-CTS */
 };
 
+enum wmi_rtscts_profile {
+       WMI_RTSCTS_FOR_NO_RATESERIES = 0,
+       WMI_RTSCTS_FOR_SECOND_RATESERIES,
+       WMI_RTSCTS_ACROSS_SW_RETRIES
+};
+
+#define WMI_RTSCTS_ENABLED             1
+#define WMI_RTSCTS_SET_MASK            0x0f
+#define WMI_RTSCTS_SET_LSB             0
+
+#define WMI_RTSCTS_PROFILE_MASK                0xf0
+#define WMI_RTSCTS_PROFILE_LSB         4
+
 enum wmi_beacon_gen_mode {
        WMI_BEACON_STAGGERED_MODE = 0,
        WMI_BEACON_BURST_MODE = 1
@@ -2295,9 +2323,9 @@ struct wmi_pdev_param_map {
 #define WMI_PDEV_PARAM_UNSUPPORTED 0
 
 enum wmi_pdev_param {
-       /* TX chian mask */
+       /* TX chain mask */
        WMI_PDEV_PARAM_TX_CHAIN_MASK = 0x1,
-       /* RX chian mask */
+       /* RX chain mask */
        WMI_PDEV_PARAM_RX_CHAIN_MASK,
        /* TX power limit for 2G Radio */
        WMI_PDEV_PARAM_TXPOWER_LIMIT2G,
@@ -2682,6 +2710,9 @@ struct wal_dbg_tx_stats {
        /* wal pdev resets  */
        __le32 pdev_resets;
 
+       /* frames dropped due to non-availability of stateless TIDs */
+       __le32 stateless_tid_alloc_failure;
+
        __le32 phy_underrun;
 
        /* MPDU is more than txop limit */
@@ -2738,13 +2769,21 @@ enum wmi_stats_id {
        WMI_REQUEST_AP_STAT     = 0x02
 };
 
+struct wlan_inst_rssi_args {
+       __le16 cfg_retry_count;
+       __le16 retry_count;
+};
+
 struct wmi_request_stats_cmd {
        __le32 stats_id;
 
-       /*
-        * Space to add parameters like
-        * peer mac addr
-        */
+       __le32 vdev_id;
+
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+
+       /* Instantaneous RSSI arguments */
+       struct wlan_inst_rssi_args inst_rssi_args;
 } __packed;
 
 /* Suspend option */
@@ -2795,7 +2834,18 @@ struct wmi_stats_event {
  * PDEV statistics
  * TODO: add all PDEV stats here
  */
-struct wmi_pdev_stats {
+struct wmi_pdev_stats_old {
+       __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;
+
+struct wmi_pdev_stats_10x {
        __le32 chan_nf;        /* Channel noise floor */
        __le32 tx_frame_count; /* TX frame count */
        __le32 rx_frame_count; /* RX frame count */
@@ -2804,6 +2854,12 @@ struct wmi_pdev_stats {
        __le32 phy_err_count;  /* Phy error count */
        __le32 chan_tx_pwr;    /* channel tx power */
        struct wal_dbg_stats wal; /* WAL dbg stats */
+       __le32 ack_rx_bad;
+       __le32 rts_bad;
+       __le32 rts_good;
+       __le32 fcs_bad;
+       __le32 no_beacons;
+       __le32 mib_int_count;
 } __packed;
 
 /*
@@ -2818,10 +2874,17 @@ struct wmi_vdev_stats {
  * peer statistics.
  * TODO: add more stats
  */
-struct wmi_peer_stats {
+struct wmi_peer_stats_old {
+       struct wmi_mac_addr peer_macaddr;
+       __le32 peer_rssi;
+       __le32 peer_tx_rate;
+} __packed;
+
+struct wmi_peer_stats_10x {
        struct wmi_mac_addr peer_macaddr;
        __le32 peer_rssi;
        __le32 peer_tx_rate;
+       __le32 peer_rx_rate;
 } __packed;
 
 struct wmi_vdev_create_cmd {
@@ -4196,13 +4259,14 @@ 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);
 
-int ath10k_wmi_connect_htc_service(struct ath10k *ar);
+int ath10k_wmi_connect(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, u32 suspend_opt);
 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);
+                                 u16 rd5g, u16 ctl2g, u16 ctl5g,
+                                 enum wmi_dfs_region dfs_reg);
 int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 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 *);
index 1a2973b7acf2500f5c97c992315793d0b4b9a9a7..0fce1c76638e9c9ae103e01ac6752dced8b1670d 100644 (file)
@@ -3709,8 +3709,8 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
                        AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CHIRP),
                        AR5K_TPC);
        } else {
-               ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX |
-                       AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX);
+               ath5k_hw_reg_write(ah, AR5K_TUNE_MAX_TXPOWER,
+                       AR5K_PHY_TXPOWER_RATE_MAX);
        }
 
        return 0;
index e39e5860a2e9347a2d44318e69b07410c4bd2dc5..9c125ff083f73de2c766bd5ccd6f3dc16aad7ae2 100644 (file)
@@ -1,11 +1,19 @@
 config ATH6KL
        tristate "Atheros mobile chipsets support"
+       depends on CFG80211
+        ---help---
+         This module adds core support for wireless adapters based on
+         Atheros AR6003 and AR6004 chipsets. You still need separate
+         bus drivers for USB and SDIO to be able to use real devices.
+
+         If you choose to build it as a module, it will be called
+         ath6kl_core. Please note that AR6002 and AR6001 are not
+         supported by this driver.
 
 config ATH6KL_SDIO
        tristate "Atheros ath6kl SDIO support"
        depends on ATH6KL
        depends on MMC
-       depends on CFG80211
        ---help---
          This module adds support for wireless adapters based on
          Atheros AR6003 and AR6004 chipsets running over SDIO. If you
@@ -17,25 +25,31 @@ config ATH6KL_USB
        tristate "Atheros ath6kl USB support"
        depends on ATH6KL
        depends on USB
-       depends on CFG80211
        ---help---
          This module adds support for wireless adapters based on
-         Atheros AR6004 chipset running over USB. This is still under
-         implementation and it isn't functional. If you choose to
-         build it as a module, it will be called ath6kl_usb.
+         Atheros AR6004 chipset and chipsets based on it running over
+         USB. If you choose to build it as a module, it will be
+         called ath6kl_usb.
 
 config ATH6KL_DEBUG
        bool "Atheros ath6kl debugging"
        depends on ATH6KL
        ---help---
-         Enables debug support
+         Enables ath6kl debug support, including debug messages
+         enabled with debug_mask module parameter and debugfs
+         interface.
+
+         If unsure, say Y to make it easier to debug problems.
 
 config ATH6KL_TRACING
        bool "Atheros ath6kl tracing support"
        depends on ATH6KL
        depends on EVENT_TRACING
        ---help---
-         Select this to ath6kl use tracing infrastructure.
+         Select this to ath6kl use tracing infrastructure which, for
+         example, can be enabled with help of trace-cmd. All debug
+         messages and commands are delivered to using individually
+         enablable trace points.
 
          If unsure, say Y to make it easier to debug problems.
 
@@ -47,3 +61,5 @@ config ATH6KL_REGDOMAIN
          Enabling this makes it possible to change the regdomain in
          the firmware. This can be only enabled if regulatory requirements
          are taken into account.
+
+         If unsure, say N.
index c2c6f460495859ae3517c4f20daa2c15caebdde0..0e26f4a34fda329910ecc278de9fe7bea8fa6c57 100644 (file)
@@ -724,8 +724,9 @@ ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
                        ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
                                   "added bss %pM to cfg80211\n", bssid);
                kfree(ie);
-       } else
+       } else {
                ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n");
+       }
 
        return bss;
 }
@@ -970,7 +971,6 @@ static int ath6kl_set_probed_ssids(struct ath6kl *ar,
                                          ssid_list[i].flag,
                                          ssid_list[i].ssid.ssid_len,
                                          ssid_list[i].ssid.ssid);
-
        }
 
        /* Make sure no old entries are left behind */
@@ -1759,7 +1759,7 @@ static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
 }
 
 static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
-                             u8 *mac, struct station_info *sinfo)
+                             const u8 *mac, struct station_info *sinfo)
 {
        struct ath6kl *ar = ath6kl_priv(dev);
        struct ath6kl_vif *vif = netdev_priv(dev);
@@ -1897,7 +1897,6 @@ static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif,
 
        /* Configure the patterns that we received from the user. */
        for (i = 0; i < wow->n_patterns; i++) {
-
                /*
                 * Convert given nl80211 specific mask value to equivalent
                 * driver specific mask value and send it to the chip along
@@ -2850,8 +2849,9 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
        if (p.prwise_crypto_type == 0) {
                p.prwise_crypto_type = NONE_CRYPT;
                ath6kl_set_cipher(vif, 0, true);
-       } else if (info->crypto.n_ciphers_pairwise == 1)
+       } else if (info->crypto.n_ciphers_pairwise == 1) {
                ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
+       }
 
        switch (info->crypto.cipher_group) {
        case WLAN_CIPHER_SUITE_WEP40:
@@ -2897,7 +2897,6 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
        }
 
        if (info->inactivity_timeout) {
-
                inactivity_timeout = info->inactivity_timeout;
 
                if (ar->hw.flags & ATH6KL_HW_AP_INACTIVITY_MINS)
@@ -2975,7 +2974,7 @@ static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
 static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
 static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
-                             u8 *mac)
+                             const u8 *mac)
 {
        struct ath6kl *ar = ath6kl_priv(dev);
        struct ath6kl_vif *vif = netdev_priv(dev);
@@ -2986,7 +2985,8 @@ static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
 }
 
 static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
-                                u8 *mac, struct station_parameters *params)
+                                const u8 *mac,
+                                struct station_parameters *params)
 {
        struct ath6kl *ar = ath6kl_priv(dev);
        struct ath6kl_vif *vif = netdev_priv(dev);
index 4b46adbe8c923b7dd410c919dc3cbc289541ea4f..b0b6520427600a05687e299318f78f873e3fd948 100644 (file)
@@ -45,9 +45,9 @@ module_param(testmode, uint, 0644);
 module_param(recovery_enable, uint, 0644);
 module_param(heart_beat_poll, uint, 0644);
 MODULE_PARM_DESC(recovery_enable, "Enable recovery from firmware error");
-MODULE_PARM_DESC(heart_beat_poll, "Enable fw error detection periodic"   \
-                "polling. This also specifies the polling interval in"  \
-                "msecs. Set reocvery_enable for this to be effective");
+MODULE_PARM_DESC(heart_beat_poll,
+                "Enable fw error detection periodic polling in msecs - Also set recovery_enable for this to be effective");
+
 
 void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb)
 {
index dbfd17d0a5faa33fa390b150d8d39e03a42d3746..55c4064dd5067f26b8dfaf54689f85c9eb67a88e 100644 (file)
@@ -172,7 +172,6 @@ void ath6kl_dump_registers(struct ath6kl_device *dev,
                           struct ath6kl_irq_proc_registers *irq_proc_reg,
                           struct ath6kl_irq_enable_reg *irq_enable_reg)
 {
-
        ath6kl_dbg(ATH6KL_DBG_IRQ, ("<------- Register Table -------->\n"));
 
        if (irq_proc_reg != NULL) {
@@ -219,7 +218,6 @@ void ath6kl_dump_registers(struct ath6kl_device *dev,
                                   "GMBOX lookahead alias 1:   0x%x\n",
                                   irq_proc_reg->rx_gmbox_lkahd_alias[1]);
                }
-
        }
 
        if (irq_enable_reg != NULL) {
@@ -1396,7 +1394,6 @@ static ssize_t ath6kl_create_qos_write(struct file *file,
                                                const char __user *user_buf,
                                                size_t count, loff_t *ppos)
 {
-
        struct ath6kl *ar = file->private_data;
        struct ath6kl_vif *vif;
        char buf[200];
@@ -1575,7 +1572,6 @@ static ssize_t ath6kl_delete_qos_write(struct file *file,
                                const char __user *user_buf,
                                size_t count, loff_t *ppos)
 {
-
        struct ath6kl *ar = file->private_data;
        struct ath6kl_vif *vif;
        char buf[100];
index ca9ba005f2871f3e42bbc914bb5ca90f6e8b90e9..e194c10d9f0071725c1c5eba917347ae8209b0a0 100644 (file)
@@ -97,8 +97,8 @@ static inline void ath6kl_dump_registers(struct ath6kl_device *dev,
                struct ath6kl_irq_proc_registers *irq_proc_reg,
                struct ath6kl_irq_enable_reg *irq_en_reg)
 {
-
 }
+
 static inline void dump_cred_dist_stats(struct htc_target *target)
 {
 }
index fea7709b5dda59aa26fa0fbc5eb70caccea77918..18c070850a09b870900624a83ee5576551281cdb 100644 (file)
@@ -37,7 +37,6 @@ static int ath6kl_hif_cp_scat_dma_buf(struct hif_scatter_req *req,
        buf = req->virt_dma_buf;
 
        for (i = 0; i < req->scat_entries; i++) {
-
                if (from_dma)
                        memcpy(req->scat_list[i].buf, buf,
                               req->scat_list[i].len);
@@ -116,7 +115,6 @@ static void ath6kl_hif_dump_fw_crash(struct ath6kl *ar)
                            le32_to_cpu(regdump_val[i + 2]),
                            le32_to_cpu(regdump_val[i + 3]));
        }
-
 }
 
 static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev)
@@ -701,5 +699,4 @@ int ath6kl_hif_setup(struct ath6kl_device *dev)
 
 fail_setup:
        return status;
-
 }
index 61f6b21fb0aeeaa4e6ef20dfca3baddf6ad638cd..dc6bd8cd9b837d85155d494a0afd2b32b7775240 100644 (file)
@@ -197,9 +197,9 @@ struct hif_scatter_req {
        /* bounce buffer for upper layers to copy to/from */
        u8 *virt_dma_buf;
 
-       struct hif_scatter_item scat_list[1];
-
        u32 scat_q_depth;
+
+       struct hif_scatter_item scat_list[0];
 };
 
 struct ath6kl_irq_proc_registers {
index 65e5b719093d47943b3135e902cf68001eefd988..e481f14b98787e88354278d26e3c59615ff2851c 100644 (file)
@@ -112,9 +112,9 @@ static void ath6kl_credit_init(struct ath6kl_htc_credit_info *cred_info,
                if (cur_ep_dist->endpoint == ENDPOINT_0)
                        continue;
 
-               if (cur_ep_dist->svc_id == WMI_CONTROL_SVC)
+               if (cur_ep_dist->svc_id == WMI_CONTROL_SVC) {
                        cur_ep_dist->cred_norm = cur_ep_dist->cred_per_msg;
-               else {
+               else {
                        /*
                         * For the remaining data endpoints, we assume that
                         * each cred_per_msg are the same. We use a simple
@@ -129,7 +129,6 @@ static void ath6kl_credit_init(struct ath6kl_htc_credit_info *cred_info,
                        count = (count * 3) >> 2;
                        count = max(count, cur_ep_dist->cred_per_msg);
                        cur_ep_dist->cred_norm = count;
-
                }
 
                ath6kl_dbg(ATH6KL_DBG_CREDIT,
@@ -549,7 +548,6 @@ static int htc_check_credits(struct htc_target *target,
                             enum htc_endpoint_id eid, unsigned int len,
                             int *req_cred)
 {
-
        *req_cred = (len > target->tgt_cred_sz) ?
                     DIV_ROUND_UP(len, target->tgt_cred_sz) : 1;
 
@@ -608,7 +606,6 @@ static void ath6kl_htc_tx_pkts_get(struct htc_target *target,
        unsigned int len;
 
        while (true) {
-
                flags = 0;
 
                if (list_empty(&endpoint->txq))
@@ -889,7 +886,6 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target,
                ac = target->dev->ar->ep2ac_map[endpoint->eid];
 
        while (true) {
-
                if (list_empty(&endpoint->txq))
                        break;
 
@@ -1190,7 +1186,6 @@ static void ath6kl_htc_mbox_flush_txep(struct htc_target *target,
                list_add_tail(&packet->list, &container);
                htc_tx_complete(endpoint, &container);
        }
-
 }
 
 static void ath6kl_htc_flush_txep_all(struct htc_target *target)
@@ -1394,7 +1389,6 @@ static int ath6kl_htc_rx_setup(struct htc_target *target,
 
        ep_cb = ep->ep_cb;
        for (j = 0; j < n_msg; j++) {
-
                /*
                 * Reset flag, any packets allocated using the
                 * rx_alloc() API cannot be recycled on
@@ -1424,9 +1418,9 @@ static int ath6kl_htc_rx_setup(struct htc_target *target,
                                }
                        }
 
-                       if (list_empty(&ep->rx_bufq))
+                       if (list_empty(&ep->rx_bufq)) {
                                packet = NULL;
-                       else {
+                       else {
                                packet = list_first_entry(&ep->rx_bufq,
                                                struct htc_packet, list);
                                list_del(&packet->list);
@@ -1487,7 +1481,6 @@ static int ath6kl_htc_rx_alloc(struct htc_target *target,
        spin_lock_bh(&target->rx_lock);
 
        for (i = 0; i < msg; i++) {
-
                htc_hdr = (struct htc_frame_hdr *)&lk_ahds[i];
 
                if (htc_hdr->eid >= ENDPOINT_MAX) {
@@ -1708,7 +1701,6 @@ static int htc_parse_trailer(struct htc_target *target,
                lk_ahd = (struct htc_lookahead_report *) record_buf;
                if ((lk_ahd->pre_valid == ((~lk_ahd->post_valid) & 0xFF)) &&
                    next_lk_ahds) {
-
                        ath6kl_dbg(ATH6KL_DBG_HTC,
                                   "htc rx lk_ahd found pre_valid 0x%x post_valid 0x%x\n",
                                   lk_ahd->pre_valid, lk_ahd->post_valid);
@@ -1755,7 +1747,6 @@ static int htc_parse_trailer(struct htc_target *target,
        }
 
        return 0;
-
 }
 
 static int htc_proc_trailer(struct htc_target *target,
@@ -1776,7 +1767,6 @@ static int htc_proc_trailer(struct htc_target *target,
        status = 0;
 
        while (len > 0) {
-
                if (len < sizeof(struct htc_record_hdr)) {
                        status = -ENOMEM;
                        break;
@@ -2098,7 +2088,6 @@ static int ath6kl_htc_rx_fetch(struct htc_target *target,
                }
 
                if (!fetched_pkts) {
-
                        packet = list_first_entry(rx_pktq, struct htc_packet,
                                                   list);
 
@@ -2173,7 +2162,6 @@ int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target,
        look_aheads[0] = msg_look_ahead;
 
        while (true) {
-
                /*
                 * First lookahead sets the expected endpoint IDs for all
                 * packets in a bundle.
@@ -2825,8 +2813,9 @@ static int ath6kl_htc_reset(struct htc_target *target)
                        packet->buf = packet->buf_start;
                        packet->endpoint = ENDPOINT_0;
                        list_add_tail(&packet->list, &target->free_ctrl_rxbuf);
-               } else
+               } else {
                        list_add_tail(&packet->list, &target->free_ctrl_txbuf);
+               }
        }
 
        return 0;
index 67aa924ed8b317f9d5240ad1560a820553790c24..756fe52a12c8ad5a3496de58bbfec01d0ad9820b 100644 (file)
@@ -137,7 +137,6 @@ static void get_htc_packet_credit_based(struct htc_target *target,
                        credits_required = 0;
 
                } else {
-
                        if (ep->cred_dist.credits < credits_required)
                                break;
 
@@ -169,7 +168,6 @@ static void get_htc_packet_credit_based(struct htc_target *target,
                /* queue this packet into the caller's queue */
                list_add_tail(&packet->list, queue);
        }
-
 }
 
 static void get_htc_packet(struct htc_target *target,
@@ -279,7 +277,6 @@ static int htc_issue_packets(struct htc_target *target,
                        list_add(&packet->list, pkt_queue);
                        break;
                }
-
        }
 
        if (status != 0) {
@@ -385,7 +382,6 @@ static enum htc_send_queue_result htc_try_send(struct htc_target *target,
                         */
                        list_for_each_entry_safe(packet, tmp_pkt,
                                                 txq, list) {
-
                                ath6kl_dbg(ATH6KL_DBG_HTC,
                                           "%s: Indicat overflowed TX pkts: %p\n",
                                           __func__, packet);
@@ -403,7 +399,6 @@ static enum htc_send_queue_result htc_try_send(struct htc_target *target,
                                        list_move_tail(&packet->list,
                                                       &send_queue);
                                }
-
                        }
 
                        if (list_empty(&send_queue)) {
@@ -454,7 +449,6 @@ static enum htc_send_queue_result htc_try_send(struct htc_target *target,
         * enough transmit resources.
         */
        while (true) {
-
                if (get_queue_depth(&ep->txq) == 0)
                        break;
 
@@ -495,8 +489,8 @@ static enum htc_send_queue_result htc_try_send(struct htc_target *target,
                }
 
                spin_lock_bh(&target->tx_lock);
-
        }
+
        /* done with this endpoint, we can clear the count */
        ep->tx_proc_cnt = 0;
        spin_unlock_bh(&target->tx_lock);
@@ -1106,7 +1100,6 @@ free_skb:
        dev_kfree_skb(skb);
 
        return status;
-
 }
 
 static void htc_flush_rx_queue(struct htc_target *target,
@@ -1258,7 +1251,6 @@ static int ath6kl_htc_pipe_conn_service(struct htc_target *target,
                tx_alloc = 0;
 
        } else {
-
                tx_alloc = htc_get_credit_alloc(target, conn_req->svc_id);
                if (tx_alloc == 0) {
                        status = -ENOMEM;
index 4f316bdcbab58da3911c6a2365da1989ba412744..d5ef211f261c2c19e6e8deeef985dc2b83130794 100644 (file)
@@ -1192,7 +1192,6 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)
 
        if (board_ext_address &&
            ar->fw_board_len == (board_data_size + board_ext_data_size)) {
-
                /* write extended board data */
                ath6kl_dbg(ATH6KL_DBG_BOOT,
                           "writing extended board data to 0x%x (%d B)\n",
index 5839fc23bdc789d5013f1c89e48f0f65eff37efe..d56554674da47924477c02423ad08c50caef2918 100644 (file)
@@ -571,7 +571,6 @@ void ath6kl_scan_complete_evt(struct ath6kl_vif *vif, int status)
 
 static int ath6kl_commit_ch_switch(struct ath6kl_vif *vif, u16 channel)
 {
-
        struct ath6kl *ar = vif->ar;
 
        vif->profile.ch = cpu_to_le16(channel);
@@ -600,7 +599,6 @@ static int ath6kl_commit_ch_switch(struct ath6kl_vif *vif, u16 channel)
 
 static void ath6kl_check_ch_switch(struct ath6kl *ar, u16 channel)
 {
-
        struct ath6kl_vif *vif;
        int res = 0;
 
@@ -692,9 +690,9 @@ void ath6kl_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid, bool ismcast)
                cfg80211_michael_mic_failure(vif->ndev, sta->mac,
                                             NL80211_KEYTYPE_PAIRWISE, keyid,
                                             tsc, GFP_KERNEL);
-       } else
+       } else {
                ath6kl_cfg80211_tkip_micerr_event(vif, keyid, ismcast);
-
+       }
 }
 
 static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len)
@@ -1093,8 +1091,9 @@ static int ath6kl_open(struct net_device *dev)
        if (test_bit(CONNECTED, &vif->flags)) {
                netif_carrier_on(dev);
                netif_wake_queue(dev);
-       } else
+       } else {
                netif_carrier_off(dev);
+       }
 
        return 0;
 }
@@ -1146,7 +1145,6 @@ static int ath6kl_set_features(struct net_device *dev,
                        dev->features = features | NETIF_F_RXCSUM;
                        return err;
                }
-
        }
 
        return err;
index 7126bdd4236c2b1e78dd3a9d2c600dbadbb53f0e..339d89f14d32b1991c3d3d646f460b90f41411bb 100644 (file)
@@ -348,7 +348,7 @@ static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio,
        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_list_sz = n_scat_entry * sizeof(struct hif_scatter_item);
        scat_req_sz = sizeof(*s_req) + scat_list_sz;
 
        if (!virt_scat)
@@ -425,8 +425,9 @@ static int ath6kl_sdio_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf,
                        memcpy(tbuf, buf, len);
 
                bounced = true;
-       } else
+       } else {
                tbuf = buf;
+       }
 
        ret = ath6kl_sdio_io(ar_sdio->func, request, addr, tbuf, len);
        if ((request & HIF_READ) && bounced)
@@ -441,9 +442,9 @@ static int ath6kl_sdio_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf,
 static void __ath6kl_sdio_write_async(struct ath6kl_sdio *ar_sdio,
                                      struct bus_request *req)
 {
-       if (req->scat_req)
+       if (req->scat_req) {
                ath6kl_sdio_scat_rw(ar_sdio, req);
-       else {
+       else {
                void *context;
                int status;
 
@@ -656,7 +657,6 @@ static void ath6kl_sdio_scatter_req_add(struct ath6kl *ar,
        list_add_tail(&s_req->list, &ar_sdio->scat_req);
 
        spin_unlock_bh(&ar_sdio->scat_lock);
-
 }
 
 /* scatter gather read write request */
@@ -674,9 +674,9 @@ static int ath6kl_sdio_async_rw_scatter(struct ath6kl *ar,
                   "hif-scatter: total len: %d scatter entries: %d\n",
                   scat_req->len, scat_req->scat_entries);
 
-       if (request & HIF_SYNCHRONOUS)
+       if (request & HIF_SYNCHRONOUS) {
                status = ath6kl_sdio_scat_rw(ar_sdio, scat_req->busrequest);
-       else {
+       else {
                spin_lock_bh(&ar_sdio->wr_async_lock);
                list_add_tail(&scat_req->busrequest->list, &ar_sdio->wr_asyncq);
                spin_unlock_bh(&ar_sdio->wr_async_lock);
@@ -856,7 +856,6 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
 
        if (ar->suspend_mode == WLAN_POWER_STATE_WOW ||
            (!ar->suspend_mode && wow)) {
-
                ret = ath6kl_set_sdio_pm_caps(ar);
                if (ret)
                        goto cut_pwr;
@@ -878,7 +877,6 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
 
        if (ar->suspend_mode == WLAN_POWER_STATE_DEEP_SLEEP ||
            !ar->suspend_mode || try_deepsleep) {
-
                flags = sdio_get_host_pm_caps(func);
                if (!(flags & MMC_PM_KEEP_POWER))
                        goto cut_pwr;
@@ -1061,7 +1059,6 @@ static int ath6kl_sdio_bmi_credits(struct ath6kl *ar)
 
        timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT);
        while (time_before(jiffies, timeout) && !ar->bmi.cmd_credits) {
-
                /*
                 * Hit the credit counter with a 4-byte access, the first byte
                 * read will hit the counter and cause a decrement, while the
index a580a629a0da6ba24b251dab8147e7de74c34f7c..d5eeeae7711b253c7dcddbf4214613cd5aff86f7 100644 (file)
@@ -289,7 +289,7 @@ struct host_interest {
        u32 hi_hp_rx_traffic_ratio;                    /* 0xd8 */
 
        /* test applications flags */
-       u32 hi_test_apps_related    ;                  /* 0xdc */
+       u32 hi_test_apps_related;                      /* 0xdc */
        /* location of test script */
        u32 hi_ota_testscript;                         /* 0xe0 */
        /* location of CAL data */
index ebb24045a8ae6cbcac844a239d11a05ee6f86e14..40432fe7a5d2cc112a4ce6d68bb8fd43a115e01f 100644 (file)
@@ -125,8 +125,9 @@ static bool ath6kl_process_uapsdq(struct ath6kl_sta *conn,
                *flags |= WMI_DATA_HDR_FLAGS_UAPSD;
                spin_unlock_bh(&conn->psq_lock);
                return false;
-       } else if (!conn->apsd_info)
+       } else if (!conn->apsd_info) {
                return false;
+       }
 
        if (test_bit(WMM_ENABLED, &vif->flags)) {
                ether_type = be16_to_cpu(datap->h_proto);
@@ -316,8 +317,9 @@ int ath6kl_control_tx(void *devt, struct sk_buff *skb,
                cookie = NULL;
                ath6kl_err("wmi ctrl ep full, dropping pkt : 0x%p, len:%d\n",
                           skb, skb->len);
-       } else
+       } else {
                cookie = ath6kl_alloc_cookie(ar);
+       }
 
        if (cookie == NULL) {
                spin_unlock_bh(&ar->lock);
@@ -359,7 +361,7 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev)
        struct ath6kl_vif *vif = netdev_priv(dev);
        u32 map_no = 0;
        u16 htc_tag = ATH6KL_DATA_PKT_TAG;
-       u8 ac = 99 ; /* initialize to unmapped ac */
+       u8 ac = 99; /* initialize to unmapped ac */
        bool chk_adhoc_ps_mapping = false;
        int ret;
        struct wmi_tx_meta_v2 meta_v2;
@@ -449,8 +451,9 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev)
                        if (ret)
                                goto fail_tx;
                }
-       } else
+       } else {
                goto fail_tx;
+       }
 
        spin_lock_bh(&ar->lock);
 
@@ -702,7 +705,6 @@ void ath6kl_tx_complete(struct htc_target *target,
 
        /* reap completed packets */
        while (!list_empty(packet_queue)) {
-
                packet = list_first_entry(packet_queue, struct htc_packet,
                                          list);
                list_del(&packet->list);
@@ -1089,8 +1091,9 @@ static void aggr_deque_frms(struct aggr_info_conn *agg_conn, u8 tid,
                        else
                                skb_queue_tail(&rxtid->q, node->skb);
                        node->skb = NULL;
-               } else
+               } else {
                        stats->num_hole++;
+               }
 
                rxtid->seq_next = ATH6KL_NEXT_SEQ_NO(rxtid->seq_next);
                idx = AGGR_WIN_IDX(rxtid->seq_next, rxtid->hold_q_sz);
@@ -1211,7 +1214,7 @@ static bool aggr_process_recv_frm(struct aggr_info_conn *agg_conn, u8 tid,
                return is_queued;
 
        spin_lock_bh(&rxtid->lock);
-       for (idx = 0 ; idx < rxtid->hold_q_sz; idx++) {
+       for (idx = 0; idx < rxtid->hold_q_sz; idx++) {
                if (rxtid->hold_q[idx].skb) {
                        /*
                         * There is a frame in the queue and no
@@ -1265,7 +1268,6 @@ static void ath6kl_uapsd_trigger_frame_rx(struct ath6kl_vif *vif,
        is_apsdq_empty_at_start = is_apsdq_empty;
 
        while ((!is_apsdq_empty) && (num_frames_to_deliver)) {
-
                spin_lock_bh(&conn->psq_lock);
                skb = skb_dequeue(&conn->apsdq);
                is_apsdq_empty = skb_queue_empty(&conn->apsdq);
@@ -1606,16 +1608,18 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
                        if (!conn)
                                return;
                        aggr_conn = conn->aggr_conn;
-               } else
+               } else {
                        aggr_conn = vif->aggr_cntxt->aggr_conn;
+               }
 
                if (aggr_process_recv_frm(aggr_conn, tid, seq_no,
                                          is_amsdu, skb)) {
                        /* aggregation code will handle the skb */
                        return;
                }
-       } else if (!is_broadcast_ether_addr(datap->h_dest))
+       } else if (!is_broadcast_ether_addr(datap->h_dest)) {
                vif->net_stats.multicast++;
+       }
 
        ath6kl_deliver_frames_to_nw_stack(vif->ndev, skb);
 }
@@ -1710,8 +1714,9 @@ void aggr_recv_addba_req_evt(struct ath6kl_vif *vif, u8 tid_mux, u16 seq_no,
                sta = ath6kl_find_sta_by_aid(vif->ar, aid);
                if (sta)
                        aggr_conn = sta->aggr_conn;
-       } else
+       } else {
                aggr_conn = vif->aggr_cntxt->aggr_conn;
+       }
 
        if (!aggr_conn)
                return;
@@ -1766,7 +1771,6 @@ void aggr_conn_init(struct ath6kl_vif *vif, struct aggr_info *aggr_info,
                skb_queue_head_init(&rxtid->q);
                spin_lock_init(&rxtid->lock);
        }
-
 }
 
 struct aggr_info *aggr_init(struct ath6kl_vif *vif)
@@ -1806,8 +1810,9 @@ void aggr_recv_delba_req_evt(struct ath6kl_vif *vif, u8 tid_mux)
                sta = ath6kl_find_sta_by_aid(vif->ar, aid);
                if (sta)
                        aggr_conn = sta->aggr_conn;
-       } else
+       } else {
                aggr_conn = vif->aggr_cntxt->aggr_conn;
+       }
 
        if (!aggr_conn)
                return;
index 56c3fd5cef65a07e915d63d8c0dc24727a87406b..3afc5a463d06f822f339250deecfb1cefadea592 100644 (file)
@@ -236,7 +236,6 @@ static void ath6kl_usb_free_pipe_resources(struct ath6kl_usb_pipe *pipe)
                        break;
                kfree(urb_context);
        }
-
 }
 
 static void ath6kl_usb_cleanup_pipe_resources(struct ath6kl_usb *ar_usb)
@@ -245,7 +244,6 @@ static void ath6kl_usb_cleanup_pipe_resources(struct ath6kl_usb *ar_usb)
 
        for (i = 0; i < ATH6KL_USB_PIPE_MAX; i++)
                ath6kl_usb_free_pipe_resources(&ar_usb->pipes[i]);
-
 }
 
 static u8 ath6kl_usb_get_logical_pipe_num(struct ath6kl_usb *ar_usb,
index 8b4ce28e3ce8f51f7cda070364394a117d5aef66..4d7f9e4712e991deea8553f7505a7f79b9a6f6e7 100644 (file)
@@ -289,8 +289,9 @@ int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, u8 if_idx,
                           ath6kl_wmi_determine_user_priority(((u8 *) llc_hdr) +
                                        sizeof(struct ath6kl_llc_snap_hdr),
                                        layer2_priority);
-               } else
+               } else {
                        usr_pri = layer2_priority & 0x7;
+               }
 
                /*
                 * Queue the EAPOL frames in the same WMM_AC_VO queue
@@ -359,8 +360,9 @@ int ath6kl_wmi_dot11_hdr_remove(struct wmi *wmi, struct sk_buff *skb)
                hdr_size = roundup(sizeof(struct ieee80211_qos_hdr),
                                   sizeof(u32));
                skb_pull(skb, hdr_size);
-       } else if (sub_type == cpu_to_le16(IEEE80211_STYPE_DATA))
+       } else if (sub_type == cpu_to_le16(IEEE80211_STYPE_DATA)) {
                skb_pull(skb, sizeof(struct ieee80211_hdr_3addr));
+       }
 
        datap = skb->data;
        llc_hdr = (struct ath6kl_llc_snap_hdr *)(datap);
@@ -936,7 +938,6 @@ ath6kl_regd_find_country_by_rd(u16 regdmn)
 
 static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len)
 {
-
        struct ath6kl_wmi_regdomain *ev;
        struct country_code_to_enum_rd *country = NULL;
        struct reg_dmn_pair_mapping *regpair = NULL;
@@ -946,10 +947,9 @@ static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len)
        ev = (struct ath6kl_wmi_regdomain *) datap;
        reg_code = le32_to_cpu(ev->reg_code);
 
-       if ((reg_code >> ATH6KL_COUNTRY_RD_SHIFT) & COUNTRY_ERD_FLAG)
+       if ((reg_code >> ATH6KL_COUNTRY_RD_SHIFT) & COUNTRY_ERD_FLAG) {
                country = ath6kl_regd_find_country((u16) reg_code);
-       else if (!(((u16) reg_code & WORLD_SKU_MASK) == WORLD_SKU_PREFIX)) {
-
+       } else if (!(((u16) reg_code & WORLD_SKU_MASK) == WORLD_SKU_PREFIX)) {
                regpair = ath6kl_get_regpair((u16) reg_code);
                country = ath6kl_regd_find_country_by_rd((u16) reg_code);
                if (regpair)
@@ -1499,7 +1499,6 @@ static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len,
 
        if ((reply->cac_indication == CAC_INDICATION_ADMISSION_RESP) &&
            (reply->status_code != IEEE80211_TSPEC_STATUS_ADMISS_ACCEPTED)) {
-
                ts = (struct ieee80211_tspec_ie *) &(reply->tspec_suggestion);
                tsinfo = le16_to_cpu(ts->tsinfo);
                tsid = (tsinfo >> IEEE80211_WMM_IE_TSPEC_TID_SHIFT) &
@@ -1530,7 +1529,6 @@ static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len,
         * for delete qos stream from AP
         */
        else if (reply->cac_indication == CAC_INDICATION_DELETE) {
-
                ts = (struct ieee80211_tspec_ie *) &(reply->tspec_suggestion);
                tsinfo = le16_to_cpu(ts->tsinfo);
                ts_id = ((tsinfo >> IEEE80211_WMM_IE_TSPEC_TID_SHIFT) &
@@ -2322,7 +2320,7 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index,
        return ret;
 }
 
-int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, u8 *krk)
+int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, const u8 *krk)
 {
        struct sk_buff *skb;
        struct wmi_add_krk_cmd *cmd;
@@ -2479,7 +2477,6 @@ static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx)
                goto free_data_skb;
 
        for (index = 0; index < num_pri_streams; index++) {
-
                if (WARN_ON(!data_sync_bufs[index].skb))
                        goto free_data_skb;
 
@@ -2704,7 +2701,6 @@ static void ath6kl_wmi_relinquish_implicit_pstream_credits(struct wmi *wmi)
 
        for (i = 0; i < WMM_NUM_AC; i++) {
                if (stream_exist & (1 << i)) {
-
                        /*
                         * FIXME: Is this lock & unlock inside
                         * for loop correct? may need rework.
@@ -2870,8 +2866,9 @@ int ath6kl_wmi_set_host_sleep_mode_cmd(struct wmi *wmi, u8 if_idx,
        if (host_mode == ATH6KL_HOST_MODE_ASLEEP) {
                ath6kl_wmi_relinquish_implicit_pstream_credits(wmi);
                cmd->asleep = cpu_to_le32(1);
-       } else
+       } else {
                cmd->awake = cpu_to_le32(1);
+       }
 
        ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb,
                                  WMI_SET_HOST_SLEEP_MODE_CMDID,
index 5c702ae4d9f8683edf91063c0acc92a32036c697..bb23fc00111dc6c3329762ee976ce93489015b90 100644 (file)
@@ -898,7 +898,6 @@ struct wmi_start_scan_cmd {
  *  flags here
  */
 enum wmi_scan_ctrl_flags_bits {
-
        /* set if can scan in the connect cmd */
        CONNECT_SCAN_CTRL_FLAGS = 0x01,
 
@@ -2617,7 +2616,7 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index,
                          u8 *key_material,
                          u8 key_op_ctrl, u8 *mac_addr,
                          enum wmi_sync_flag sync_flag);
-int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, u8 *krk);
+int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, const u8 *krk);
 int ath6kl_wmi_deletekey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index);
 int ath6kl_wmi_setpmkid_cmd(struct wmi *wmi, u8 if_idx, const u8 *bssid,
                            const u8 *pmkid, bool set);
index 8e1c7b0fe76c178567fb2c03e70bd8d4e303f337..8fcd586d1c3980c413c8499cb3aefd518f507f69 100644 (file)
@@ -53,7 +53,8 @@ obj-$(CONFIG_ATH9K_HW) += ath9k_hw.o
 obj-$(CONFIG_ATH9K_COMMON) += ath9k_common.o
 ath9k_common-y:=       common.o \
                        common-init.o \
-                       common-beacon.o
+                       common-beacon.o \
+                       common-debug.o
 
 ath9k_htc-y += htc_hst.o \
                hif_usb.o \
index 0a6163e9248c0fdefa0f0e78a695201fb8a7d0f2..c38399bc9aa96e84fce4929319599e1d4db694c7 100644 (file)
@@ -410,7 +410,7 @@ static const u32 ar9300_2p2_baseband_core[][2] = {
        {0x00009e30, 0x06336f77},
        {0x00009e34, 0x6af6532f},
        {0x00009e38, 0x0cc80c00},
-       {0x00009e40, 0x0d261820},
+       {0x00009e40, 0x0d261800},
        {0x00009e4c, 0x00001004},
        {0x00009e50, 0x00ff03f1},
        {0x00009e54, 0x00000000},
index f76139bbb74f0fcf144e22dd382be140c48cf08b..2c42ff05efa38f507cdd0f54905d8b79c0c77d32 100644 (file)
@@ -592,7 +592,7 @@ static const u32 ar9331_1p1_baseband_core[][2] = {
        {0x00009e30, 0x06336f77},
        {0x00009e34, 0x6af6532f},
        {0x00009e38, 0x0cc80c00},
-       {0x00009e40, 0x0d261820},
+       {0x00009e40, 0x0d261800},
        {0x00009e4c, 0x00001004},
        {0x00009e50, 0x00ff03f1},
        {0x00009fc0, 0x803e4788},
index 0ac8be96097f2e70f436f75aadb4be1b2d2bc44b..2154efcd3900514af944619a174db08d2514a140 100644 (file)
@@ -231,7 +231,7 @@ static const u32 ar9331_1p2_baseband_core[][2] = {
        {0x00009e30, 0x06336f77},
        {0x00009e34, 0x6af6532f},
        {0x00009e38, 0x0cc80c00},
-       {0x00009e40, 0x0d261820},
+       {0x00009e40, 0x0d261800},
        {0x00009e4c, 0x00001004},
        {0x00009e50, 0x00ff03f1},
        {0x00009fc0, 0x803e4788},
index a01f0edb65182a16b95181038786c9d8073a1cab..b995ffe88b33bb8af6ca6a7aaa47e23adee34857 100644 (file)
@@ -318,7 +318,7 @@ static const u32 ar9340_1p0_baseband_core[][2] = {
        {0x00009e30, 0x06336f77},
        {0x00009e34, 0x6af6532f},
        {0x00009e38, 0x0cc80c00},
-       {0x00009e40, 0x0d261820},
+       {0x00009e40, 0x0d261800},
        {0x00009e4c, 0x00001004},
        {0x00009e50, 0x00ff03f1},
        {0x00009e54, 0x00000000},
@@ -348,9 +348,9 @@ static const u32 ar9340_1p0_baseband_core[][2] = {
        {0x0000a370, 0x00000000},
        {0x0000a390, 0x00000001},
        {0x0000a394, 0x00000444},
-       {0x0000a398, 0x00000000},
-       {0x0000a39c, 0x210d0401},
-       {0x0000a3a0, 0xab9a7144},
+       {0x0000a398, 0x001f0e0f},
+       {0x0000a39c, 0x0075393f},
+       {0x0000a3a0, 0xb79f6427},
        {0x0000a3a4, 0x00000000},
        {0x0000a3a8, 0xaaaaaaaa},
        {0x0000a3ac, 0x3c466478},
index 3c9113d9b1bc3cfe3aa7e9e07be8efa1a2df4d14..8e5c3b9786e3ac3fab8cf42e0d80bf84f268d4d2 100644 (file)
@@ -257,9 +257,9 @@ static const u32 qca953x_1p0_baseband_core[][2] = {
        {0x0000a370, 0x00000000},
        {0x0000a390, 0x00000001},
        {0x0000a394, 0x00000444},
-       {0x0000a398, 0x1f020503},
-       {0x0000a39c, 0x29180c03},
-       {0x0000a3a0, 0x9a8b6844},
+       {0x0000a398, 0x001f0e0f},
+       {0x0000a39c, 0x0075393f},
+       {0x0000a3a0, 0xb79f6427},
        {0x0000a3a4, 0x000000ff},
        {0x0000a3a8, 0x6a6a6a6a},
        {0x0000a3ac, 0x6a6a6a6a},
index e6aec2c0207ff43754a79a236a769a33bacf401e..a5ca65240af30b8980b5732a610d73ef155063c9 100644 (file)
@@ -90,7 +90,7 @@ static const u32 ar9580_1p0_baseband_core[][2] = {
        {0x00009e30, 0x06336f77},
        {0x00009e34, 0x6af6532f},
        {0x00009e38, 0x0cc80c00},
-       {0x00009e40, 0x0d261820},
+       {0x00009e40, 0x0d261800},
        {0x00009e4c, 0x00001004},
        {0x00009e50, 0x00ff03f1},
        {0x00009e54, 0x00000000},
index 3ba03dde42150b746bca7f2571a225e34d34af8e..2ca8f7e061742420fe00a9bddf61fb366feb93dc 100644 (file)
@@ -23,8 +23,8 @@
 #include <linux/leds.h>
 #include <linux/completion.h>
 
-#include "debug.h"
 #include "common.h"
+#include "debug.h"
 #include "mci.h"
 #include "dfs.h"
 #include "spectral.h"
@@ -114,6 +114,9 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
 #define ATH_TXFIFO_DEPTH           8
 #define ATH_TX_ERROR               0x01
 
+/* Stop tx traffic 1ms before the GO goes away */
+#define ATH_P2P_PS_STOP_TIME       1000
+
 #define IEEE80211_SEQ_SEQ_SHIFT    4
 #define IEEE80211_SEQ_MAX          4096
 #define IEEE80211_WEP_IVLEN        3
@@ -271,6 +274,7 @@ struct ath_node {
 #ifdef CONFIG_ATH9K_STATION_STATISTICS
        struct ath_rx_rate_stats rx_rate_stats;
 #endif
+       u8 key_idx[4];
 };
 
 struct ath_tx_control {
@@ -366,11 +370,15 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
 /********/
 
 struct ath_vif {
+       struct ieee80211_vif *vif;
        struct ath_node mcast_node;
        int av_bslot;
        bool primary_sta_vif;
        __le64 tsf_adjust; /* TSF adjustment for staggered beacons */
        struct ath_buf *av_bcbuf;
+
+       /* P2P Client */
+       struct ieee80211_noa_data noa;
 };
 
 struct ath9k_vif_iter_data {
@@ -463,6 +471,8 @@ int ath_update_survey_stats(struct ath_softc *sc);
 void ath_update_survey_nf(struct ath_softc *sc, int channel);
 void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type);
 void ath_ps_full_sleep(unsigned long data);
+void ath9k_p2p_ps_timer(void *priv);
+void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif);
 
 /**********/
 /* BTCOEX */
@@ -713,6 +723,9 @@ struct ath_softc {
        struct completion paprd_complete;
        wait_queue_head_t tx_wait;
 
+       struct ath_gen_timer *p2p_ps_timer;
+       struct ath_vif *p2p_ps_vif;
+
        unsigned long driver_data;
 
        u8 gtt_cnt;
@@ -757,6 +770,7 @@ struct ath_softc {
        struct ath_ant_comb ant_comb;
        u8 ant_tx, ant_rx;
        struct dfs_pattern_detector *dfs_detector;
+       u64 dfs_prev_pulse_ts;
        u32 wow_enabled;
        /* relay(fs) channel for spectral scan */
        struct rchan *rfs_chan_spec_scan;
index bd9e634879e69d4b2b33d741f7c4a8c51801ba9f..e387f0b2954a0cf5500610b330f744a5b68cd69c 100644 (file)
@@ -537,8 +537,6 @@ static void ath9k_cache_beacon_config(struct ath_softc *sc,
        cur_conf->dtim_period = bss_conf->dtim_period;
        cur_conf->dtim_count = 1;
        cur_conf->ibss_creator = bss_conf->ibss_creator;
-       cur_conf->bmiss_timeout =
-               ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval;
 
        /*
         * It looks like mac80211 may end up using beacon interval of zero in
@@ -549,6 +547,9 @@ static void ath9k_cache_beacon_config(struct ath_softc *sc,
        if (cur_conf->beacon_interval == 0)
                cur_conf->beacon_interval = 100;
 
+       cur_conf->bmiss_timeout =
+               ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval;
+
        /*
         * We don't parse dtim period from mac80211 during the driver
         * initialization as it breaks association with hidden-ssid
diff --git a/drivers/net/wireless/ath/ath9k/common-debug.c b/drivers/net/wireless/ath/ath9k/common-debug.c
new file mode 100644 (file)
index 0000000..3b289f9
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2008-2011 Atheros Communications 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 "common.h"
+
+static ssize_t read_file_modal_eeprom(struct file *file, char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+       struct ath_hw *ah = file->private_data;
+       u32 len = 0, size = 6000;
+       char *buf;
+       size_t retval;
+
+       buf = kzalloc(size, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       len = ah->eep_ops->dump_eeprom(ah, false, buf, len, size);
+
+       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       kfree(buf);
+
+       return retval;
+}
+
+static const struct file_operations fops_modal_eeprom = {
+       .read = read_file_modal_eeprom,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+
+void ath9k_cmn_debug_modal_eeprom(struct dentry *debugfs_phy,
+                                 struct ath_hw *ah)
+{
+       debugfs_create_file("modal_eeprom", S_IRUSR, debugfs_phy, ah,
+                           &fops_modal_eeprom);
+}
+EXPORT_SYMBOL(ath9k_cmn_debug_modal_eeprom);
+
+static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+       struct ath_hw *ah = file->private_data;
+       u32 len = 0, size = 1500;
+       ssize_t retval = 0;
+       char *buf;
+
+       buf = kzalloc(size, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       len = ah->eep_ops->dump_eeprom(ah, true, buf, len, size);
+
+       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       kfree(buf);
+
+       return retval;
+}
+
+static const struct file_operations fops_base_eeprom = {
+       .read = read_file_base_eeprom,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+void ath9k_cmn_debug_base_eeprom(struct dentry *debugfs_phy,
+                                struct ath_hw *ah)
+{
+       debugfs_create_file("base_eeprom", S_IRUSR, debugfs_phy, ah,
+                           &fops_base_eeprom);
+}
+EXPORT_SYMBOL(ath9k_cmn_debug_base_eeprom);
+
+void ath9k_cmn_debug_stat_rx(struct ath_rx_stats *rxstats,
+                            struct ath_rx_status *rs)
+{
+#define RX_PHY_ERR_INC(c) rxstats->phy_err_stats[c]++
+#define RX_CMN_STAT_INC(c) (rxstats->c++)
+
+       RX_CMN_STAT_INC(rx_pkts_all);
+       rxstats->rx_bytes_all += rs->rs_datalen;
+
+       if (rs->rs_status & ATH9K_RXERR_CRC)
+               RX_CMN_STAT_INC(crc_err);
+       if (rs->rs_status & ATH9K_RXERR_DECRYPT)
+               RX_CMN_STAT_INC(decrypt_crc_err);
+       if (rs->rs_status & ATH9K_RXERR_MIC)
+               RX_CMN_STAT_INC(mic_err);
+       if (rs->rs_status & ATH9K_RX_DELIM_CRC_PRE)
+               RX_CMN_STAT_INC(pre_delim_crc_err);
+       if (rs->rs_status & ATH9K_RX_DELIM_CRC_POST)
+               RX_CMN_STAT_INC(post_delim_crc_err);
+       if (rs->rs_status & ATH9K_RX_DECRYPT_BUSY)
+               RX_CMN_STAT_INC(decrypt_busy_err);
+
+       if (rs->rs_status & ATH9K_RXERR_PHY) {
+               RX_CMN_STAT_INC(phy_err);
+               if (rs->rs_phyerr < ATH9K_PHYERR_MAX)
+                       RX_PHY_ERR_INC(rs->rs_phyerr);
+       }
+
+#undef RX_CMN_STAT_INC
+#undef RX_PHY_ERR_INC
+}
+EXPORT_SYMBOL(ath9k_cmn_debug_stat_rx);
+
+static ssize_t read_file_recv(struct file *file, char __user *user_buf,
+                             size_t count, loff_t *ppos)
+{
+#define RXS_ERR(s, e)                                  \
+       do {                                            \
+               len += scnprintf(buf + len, size - len, \
+                                "%18s : %10u\n", s,    \
+                                rxstats->e);           \
+       } while (0)
+
+       struct ath_rx_stats *rxstats = file->private_data;
+       char *buf;
+       unsigned int len = 0, size = 1600;
+       ssize_t retval = 0;
+
+       buf = kzalloc(size, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       RXS_ERR("PKTS-ALL", rx_pkts_all);
+       RXS_ERR("BYTES-ALL", rx_bytes_all);
+       RXS_ERR("BEACONS", rx_beacons);
+       RXS_ERR("FRAGS", rx_frags);
+       RXS_ERR("SPECTRAL", rx_spectral);
+
+       RXS_ERR("CRC ERR", crc_err);
+       RXS_ERR("DECRYPT CRC ERR", decrypt_crc_err);
+       RXS_ERR("PHY ERR", phy_err);
+       RXS_ERR("MIC ERR", mic_err);
+       RXS_ERR("PRE-DELIM CRC ERR", pre_delim_crc_err);
+       RXS_ERR("POST-DELIM CRC ERR", post_delim_crc_err);
+       RXS_ERR("DECRYPT BUSY ERR", decrypt_busy_err);
+       RXS_ERR("LENGTH-ERR", rx_len_err);
+       RXS_ERR("OOM-ERR", rx_oom_err);
+       RXS_ERR("RATE-ERR", rx_rate_err);
+       RXS_ERR("TOO-MANY-FRAGS", rx_too_many_frags_err);
+
+       if (len > size)
+               len = size;
+
+       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       kfree(buf);
+
+       return retval;
+
+#undef RXS_ERR
+}
+
+static const struct file_operations fops_recv = {
+       .read = read_file_recv,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+void ath9k_cmn_debug_recv(struct dentry *debugfs_phy,
+                         struct ath_rx_stats *rxstats)
+{
+       debugfs_create_file("recv", S_IRUSR, debugfs_phy, rxstats,
+                           &fops_recv);
+}
+EXPORT_SYMBOL(ath9k_cmn_debug_recv);
+
+static ssize_t read_file_phy_err(struct file *file, char __user *user_buf,
+                                size_t count, loff_t *ppos)
+{
+#define PHY_ERR(s, p) \
+       len += scnprintf(buf + len, size - len, "%22s : %10u\n", s, \
+                        rxstats->phy_err_stats[p]);
+
+       struct ath_rx_stats *rxstats = file->private_data;
+       char *buf;
+       unsigned int len = 0, size = 1600;
+       ssize_t retval = 0;
+
+       buf = kzalloc(size, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN);
+       PHY_ERR("TIMING ERR", ATH9K_PHYERR_TIMING);
+       PHY_ERR("PARITY ERR", ATH9K_PHYERR_PARITY);
+       PHY_ERR("RATE ERR", ATH9K_PHYERR_RATE);
+       PHY_ERR("LENGTH ERR", ATH9K_PHYERR_LENGTH);
+       PHY_ERR("RADAR ERR", ATH9K_PHYERR_RADAR);
+       PHY_ERR("SERVICE ERR", ATH9K_PHYERR_SERVICE);
+       PHY_ERR("TOR ERR", ATH9K_PHYERR_TOR);
+       PHY_ERR("OFDM-TIMING ERR", ATH9K_PHYERR_OFDM_TIMING);
+       PHY_ERR("OFDM-SIGNAL-PARITY ERR", ATH9K_PHYERR_OFDM_SIGNAL_PARITY);
+       PHY_ERR("OFDM-RATE ERR", ATH9K_PHYERR_OFDM_RATE_ILLEGAL);
+       PHY_ERR("OFDM-LENGTH ERR", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL);
+       PHY_ERR("OFDM-POWER-DROP ERR", ATH9K_PHYERR_OFDM_POWER_DROP);
+       PHY_ERR("OFDM-SERVICE ERR", ATH9K_PHYERR_OFDM_SERVICE);
+       PHY_ERR("OFDM-RESTART ERR", ATH9K_PHYERR_OFDM_RESTART);
+       PHY_ERR("FALSE-RADAR-EXT ERR", ATH9K_PHYERR_FALSE_RADAR_EXT);
+       PHY_ERR("CCK-TIMING ERR", ATH9K_PHYERR_CCK_TIMING);
+       PHY_ERR("CCK-HEADER-CRC ERR", ATH9K_PHYERR_CCK_HEADER_CRC);
+       PHY_ERR("CCK-RATE ERR", ATH9K_PHYERR_CCK_RATE_ILLEGAL);
+       PHY_ERR("CCK-SERVICE ERR", ATH9K_PHYERR_CCK_SERVICE);
+       PHY_ERR("CCK-RESTART ERR", ATH9K_PHYERR_CCK_RESTART);
+       PHY_ERR("CCK-LENGTH ERR", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL);
+       PHY_ERR("CCK-POWER-DROP ERR", ATH9K_PHYERR_CCK_POWER_DROP);
+       PHY_ERR("HT-CRC ERR", ATH9K_PHYERR_HT_CRC_ERROR);
+       PHY_ERR("HT-LENGTH ERR", ATH9K_PHYERR_HT_LENGTH_ILLEGAL);
+       PHY_ERR("HT-RATE ERR", ATH9K_PHYERR_HT_RATE_ILLEGAL);
+
+       if (len > size)
+               len = size;
+
+       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       kfree(buf);
+
+       return retval;
+
+#undef PHY_ERR
+}
+
+static const struct file_operations fops_phy_err = {
+       .read = read_file_phy_err,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+void ath9k_cmn_debug_phy_err(struct dentry *debugfs_phy,
+                            struct ath_rx_stats *rxstats)
+{
+       debugfs_create_file("phy_err", S_IRUSR, debugfs_phy, rxstats,
+                           &fops_phy_err);
+}
+EXPORT_SYMBOL(ath9k_cmn_debug_phy_err);
diff --git a/drivers/net/wireless/ath/ath9k/common-debug.h b/drivers/net/wireless/ath/ath9k/common-debug.h
new file mode 100644 (file)
index 0000000..7c97884
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2008-2011 Atheros Communications 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.
+ */
+
+
+
+/**
+ * struct ath_rx_stats - RX Statistics
+ * @rx_pkts_all:  No. of total frames received, including ones that
+       may have had errors.
+ * @rx_bytes_all:  No. of total bytes received, including ones that
+       may have had errors.
+ * @crc_err: No. of frames with incorrect CRC value
+ * @decrypt_crc_err: No. of frames whose CRC check failed after
+       decryption process completed
+ * @phy_err: No. of frames whose reception failed because the PHY
+       encountered an error
+ * @mic_err: No. of frames with incorrect TKIP MIC verification failure
+ * @pre_delim_crc_err: Pre-Frame delimiter CRC error detections
+ * @post_delim_crc_err: Post-Frame delimiter CRC error detections
+ * @decrypt_busy_err: Decryption interruptions counter
+ * @phy_err_stats: Individual PHY error statistics
+ * @rx_len_err:  No. of frames discarded due to bad length.
+ * @rx_oom_err:  No. of frames dropped due to OOM issues.
+ * @rx_rate_err:  No. of frames dropped due to rate errors.
+ * @rx_too_many_frags_err:  Frames dropped due to too-many-frags received.
+ * @rx_beacons:  No. of beacons received.
+ * @rx_frags:  No. of rx-fragements received.
+ * @rx_spectral: No of spectral packets received.
+ */
+struct ath_rx_stats {
+       u32 rx_pkts_all;
+       u32 rx_bytes_all;
+       u32 crc_err;
+       u32 decrypt_crc_err;
+       u32 phy_err;
+       u32 mic_err;
+       u32 pre_delim_crc_err;
+       u32 post_delim_crc_err;
+       u32 decrypt_busy_err;
+       u32 phy_err_stats[ATH9K_PHYERR_MAX];
+       u32 rx_len_err;
+       u32 rx_oom_err;
+       u32 rx_rate_err;
+       u32 rx_too_many_frags_err;
+       u32 rx_beacons;
+       u32 rx_frags;
+       u32 rx_spectral;
+};
+
+void ath9k_cmn_debug_modal_eeprom(struct dentry *debugfs_phy,
+                                 struct ath_hw *ah);
+void ath9k_cmn_debug_base_eeprom(struct dentry *debugfs_phy,
+                                struct ath_hw *ah);
+void ath9k_cmn_debug_stat_rx(struct ath_rx_stats *rxstats,
+                            struct ath_rx_status *rs);
+void ath9k_cmn_debug_recv(struct dentry *debugfs_phy,
+                         struct ath_rx_stats *rxstats);
+void ath9k_cmn_debug_phy_err(struct dentry *debugfs_phy,
+                            struct ath_rx_stats *rxstats);
index ca38116838f00e5206c5ec35b3589c9bc4cbba58..ffc454b18637588f37964857c5a1b4997cc2ee5c 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "common-init.h"
 #include "common-beacon.h"
+#include "common-debug.h"
 
 /* Common header for Atheros 802.11n base driver cores */
 
index 780ff1bee6f69ceac8729a9b75913c0bdad6c28b..6cc42be48d4e60e68f3e2603ebfc465f89159a0d 100644 (file)
@@ -948,151 +948,11 @@ static const struct file_operations fops_reset = {
        .llseek = default_llseek,
 };
 
-static ssize_t read_file_recv(struct file *file, char __user *user_buf,
-                             size_t count, loff_t *ppos)
-{
-#define RXS_ERR(s, e)                                      \
-       do {                                                \
-               len += scnprintf(buf + len, size - len,     \
-                                "%18s : %10u\n", s,        \
-                                sc->debug.stats.rxstats.e);\
-       } while (0)
-
-       struct ath_softc *sc = file->private_data;
-       char *buf;
-       unsigned int len = 0, size = 1600;
-       ssize_t retval = 0;
-
-       buf = kzalloc(size, GFP_KERNEL);
-       if (buf == NULL)
-               return -ENOMEM;
-
-       RXS_ERR("PKTS-ALL", rx_pkts_all);
-       RXS_ERR("BYTES-ALL", rx_bytes_all);
-       RXS_ERR("BEACONS", rx_beacons);
-       RXS_ERR("FRAGS", rx_frags);
-       RXS_ERR("SPECTRAL", rx_spectral);
-
-       RXS_ERR("CRC ERR", crc_err);
-       RXS_ERR("DECRYPT CRC ERR", decrypt_crc_err);
-       RXS_ERR("PHY ERR", phy_err);
-       RXS_ERR("MIC ERR", mic_err);
-       RXS_ERR("PRE-DELIM CRC ERR", pre_delim_crc_err);
-       RXS_ERR("POST-DELIM CRC ERR", post_delim_crc_err);
-       RXS_ERR("DECRYPT BUSY ERR", decrypt_busy_err);
-       RXS_ERR("LENGTH-ERR", rx_len_err);
-       RXS_ERR("OOM-ERR", rx_oom_err);
-       RXS_ERR("RATE-ERR", rx_rate_err);
-       RXS_ERR("TOO-MANY-FRAGS", rx_too_many_frags_err);
-
-       if (len > size)
-               len = size;
-
-       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-       kfree(buf);
-
-       return retval;
-
-#undef RXS_ERR
-}
-
 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]++
-
-       RX_STAT_INC(rx_pkts_all);
-       sc->debug.stats.rxstats.rx_bytes_all += rs->rs_datalen;
-
-       if (rs->rs_status & ATH9K_RXERR_CRC)
-               RX_STAT_INC(crc_err);
-       if (rs->rs_status & ATH9K_RXERR_DECRYPT)
-               RX_STAT_INC(decrypt_crc_err);
-       if (rs->rs_status & ATH9K_RXERR_MIC)
-               RX_STAT_INC(mic_err);
-       if (rs->rs_status & ATH9K_RX_DELIM_CRC_PRE)
-               RX_STAT_INC(pre_delim_crc_err);
-       if (rs->rs_status & ATH9K_RX_DELIM_CRC_POST)
-               RX_STAT_INC(post_delim_crc_err);
-       if (rs->rs_status & ATH9K_RX_DECRYPT_BUSY)
-               RX_STAT_INC(decrypt_busy_err);
-
-       if (rs->rs_status & ATH9K_RXERR_PHY) {
-               RX_STAT_INC(phy_err);
-               if (rs->rs_phyerr < ATH9K_PHYERR_MAX)
-                       RX_PHY_ERR_INC(rs->rs_phyerr);
-       }
-
-#undef RX_PHY_ERR_INC
+       ath9k_cmn_debug_stat_rx(&sc->debug.stats.rxstats, rs);
 }
 
-static const struct file_operations fops_recv = {
-       .read = read_file_recv,
-       .open = simple_open,
-       .owner = THIS_MODULE,
-       .llseek = default_llseek,
-};
-
-static ssize_t read_file_phy_err(struct file *file, char __user *user_buf,
-                                size_t count, loff_t *ppos)
-{
-#define PHY_ERR(s, p) \
-       len += scnprintf(buf + len, size - len, "%22s : %10u\n", s, \
-                        sc->debug.stats.rxstats.phy_err_stats[p]);
-
-       struct ath_softc *sc = file->private_data;
-       char *buf;
-       unsigned int len = 0, size = 1600;
-       ssize_t retval = 0;
-
-       buf = kzalloc(size, GFP_KERNEL);
-       if (buf == NULL)
-               return -ENOMEM;
-
-       PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN);
-       PHY_ERR("TIMING ERR", ATH9K_PHYERR_TIMING);
-       PHY_ERR("PARITY ERR", ATH9K_PHYERR_PARITY);
-       PHY_ERR("RATE ERR", ATH9K_PHYERR_RATE);
-       PHY_ERR("LENGTH ERR", ATH9K_PHYERR_LENGTH);
-       PHY_ERR("RADAR ERR", ATH9K_PHYERR_RADAR);
-       PHY_ERR("SERVICE ERR", ATH9K_PHYERR_SERVICE);
-       PHY_ERR("TOR ERR", ATH9K_PHYERR_TOR);
-       PHY_ERR("OFDM-TIMING ERR", ATH9K_PHYERR_OFDM_TIMING);
-       PHY_ERR("OFDM-SIGNAL-PARITY ERR", ATH9K_PHYERR_OFDM_SIGNAL_PARITY);
-       PHY_ERR("OFDM-RATE ERR", ATH9K_PHYERR_OFDM_RATE_ILLEGAL);
-       PHY_ERR("OFDM-LENGTH ERR", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL);
-       PHY_ERR("OFDM-POWER-DROP ERR", ATH9K_PHYERR_OFDM_POWER_DROP);
-       PHY_ERR("OFDM-SERVICE ERR", ATH9K_PHYERR_OFDM_SERVICE);
-       PHY_ERR("OFDM-RESTART ERR", ATH9K_PHYERR_OFDM_RESTART);
-       PHY_ERR("FALSE-RADAR-EXT ERR", ATH9K_PHYERR_FALSE_RADAR_EXT);
-       PHY_ERR("CCK-TIMING ERR", ATH9K_PHYERR_CCK_TIMING);
-       PHY_ERR("CCK-HEADER-CRC ERR", ATH9K_PHYERR_CCK_HEADER_CRC);
-       PHY_ERR("CCK-RATE ERR", ATH9K_PHYERR_CCK_RATE_ILLEGAL);
-       PHY_ERR("CCK-SERVICE ERR", ATH9K_PHYERR_CCK_SERVICE);
-       PHY_ERR("CCK-RESTART ERR", ATH9K_PHYERR_CCK_RESTART);
-       PHY_ERR("CCK-LENGTH ERR", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL);
-       PHY_ERR("CCK-POWER-DROP ERR", ATH9K_PHYERR_CCK_POWER_DROP);
-       PHY_ERR("HT-CRC ERR", ATH9K_PHYERR_HT_CRC_ERROR);
-       PHY_ERR("HT-LENGTH ERR", ATH9K_PHYERR_HT_LENGTH_ILLEGAL);
-       PHY_ERR("HT-RATE ERR", ATH9K_PHYERR_HT_RATE_ILLEGAL);
-
-       if (len > size)
-               len = size;
-
-       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-       kfree(buf);
-
-       return retval;
-
-#undef PHY_ERR
-}
-
-static const struct file_operations fops_phy_err = {
-       .read = read_file_phy_err,
-       .open = simple_open,
-       .owner = THIS_MODULE,
-       .llseek = default_llseek,
-};
-
 static ssize_t read_file_regidx(struct file *file, char __user *user_buf,
                                 size_t count, loff_t *ppos)
 {
@@ -1268,62 +1128,6 @@ static const struct file_operations fops_dump_nfcal = {
        .llseek = default_llseek,
 };
 
-static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf,
-                                    size_t count, loff_t *ppos)
-{
-       struct ath_softc *sc = file->private_data;
-       struct ath_hw *ah = sc->sc_ah;
-       u32 len = 0, size = 1500;
-       ssize_t retval = 0;
-       char *buf;
-
-       buf = kzalloc(size, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
-       len = ah->eep_ops->dump_eeprom(ah, true, buf, len, size);
-
-       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-       kfree(buf);
-
-       return retval;
-}
-
-static const struct file_operations fops_base_eeprom = {
-       .read = read_file_base_eeprom,
-       .open = simple_open,
-       .owner = THIS_MODULE,
-       .llseek = default_llseek,
-};
-
-static ssize_t read_file_modal_eeprom(struct file *file, char __user *user_buf,
-                                     size_t count, loff_t *ppos)
-{
-       struct ath_softc *sc = file->private_data;
-       struct ath_hw *ah = sc->sc_ah;
-       u32 len = 0, size = 6000;
-       char *buf;
-       size_t retval;
-
-       buf = kzalloc(size, GFP_KERNEL);
-       if (buf == NULL)
-               return -ENOMEM;
-
-       len = ah->eep_ops->dump_eeprom(ah, false, buf, len, size);
-
-       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-       kfree(buf);
-
-       return retval;
-}
-
-static const struct file_operations fops_modal_eeprom = {
-       .read = read_file_modal_eeprom,
-       .open = simple_open,
-       .owner = THIS_MODULE,
-       .llseek = default_llseek,
-};
-
 #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
 static ssize_t read_file_btcoex(struct file *file, char __user *user_buf,
                                size_t count, loff_t *ppos)
@@ -1524,10 +1328,10 @@ int ath9k_init_debug(struct ath_hw *ah)
                            &fops_misc);
        debugfs_create_file("reset", S_IRUSR, sc->debug.debugfs_phy, sc,
                            &fops_reset);
-       debugfs_create_file("recv", S_IRUSR, sc->debug.debugfs_phy, sc,
-                           &fops_recv);
-       debugfs_create_file("phy_err", S_IRUSR, sc->debug.debugfs_phy, sc,
-                           &fops_phy_err);
+
+       ath9k_cmn_debug_recv(sc->debug.debugfs_phy, &sc->debug.stats.rxstats);
+       ath9k_cmn_debug_phy_err(sc->debug.debugfs_phy, &sc->debug.stats.rxstats);
+
        debugfs_create_u8("rx_chainmask", S_IRUSR, sc->debug.debugfs_phy,
                          &ah->rxchainmask);
        debugfs_create_u8("tx_chainmask", S_IRUSR, sc->debug.debugfs_phy,
@@ -1547,10 +1351,10 @@ int ath9k_init_debug(struct ath_hw *ah)
                            &fops_regdump);
        debugfs_create_file("dump_nfcal", S_IRUSR, sc->debug.debugfs_phy, sc,
                            &fops_dump_nfcal);
-       debugfs_create_file("base_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc,
-                           &fops_base_eeprom);
-       debugfs_create_file("modal_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc,
-                           &fops_modal_eeprom);
+
+       ath9k_cmn_debug_base_eeprom(sc->debug.debugfs_phy, sc->sc_ah);
+       ath9k_cmn_debug_modal_eeprom(sc->debug.debugfs_phy, sc->sc_ah);
+
        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 559a68c2709cc882d5edf646417dd7348d8fffc8..53ae15bd0c9d4ac8b40a7a9de3689c607d56c3cd 100644 (file)
@@ -221,50 +221,6 @@ struct ath_rx_rate_stats {
        } cck_stats[4];
 };
 
-/**
- * struct ath_rx_stats - RX Statistics
- * @rx_pkts_all:  No. of total frames received, including ones that
-       may have had errors.
- * @rx_bytes_all:  No. of total bytes received, including ones that
-       may have had errors.
- * @crc_err: No. of frames with incorrect CRC value
- * @decrypt_crc_err: No. of frames whose CRC check failed after
-       decryption process completed
- * @phy_err: No. of frames whose reception failed because the PHY
-       encountered an error
- * @mic_err: No. of frames with incorrect TKIP MIC verification failure
- * @pre_delim_crc_err: Pre-Frame delimiter CRC error detections
- * @post_delim_crc_err: Post-Frame delimiter CRC error detections
- * @decrypt_busy_err: Decryption interruptions counter
- * @phy_err_stats: Individual PHY error statistics
- * @rx_len_err:  No. of frames discarded due to bad length.
- * @rx_oom_err:  No. of frames dropped due to OOM issues.
- * @rx_rate_err:  No. of frames dropped due to rate errors.
- * @rx_too_many_frags_err:  Frames dropped due to too-many-frags received.
- * @rx_beacons:  No. of beacons received.
- * @rx_frags:  No. of rx-fragements received.
- * @rx_spectral: No of spectral packets received.
- */
-struct ath_rx_stats {
-       u32 rx_pkts_all;
-       u32 rx_bytes_all;
-       u32 crc_err;
-       u32 decrypt_crc_err;
-       u32 phy_err;
-       u32 mic_err;
-       u32 pre_delim_crc_err;
-       u32 post_delim_crc_err;
-       u32 decrypt_busy_err;
-       u32 phy_err_stats[ATH9K_PHYERR_MAX];
-       u32 rx_len_err;
-       u32 rx_oom_err;
-       u32 rx_rate_err;
-       u32 rx_too_many_frags_err;
-       u32 rx_beacons;
-       u32 rx_frags;
-       u32 rx_spectral;
-};
-
 #define ANT_MAIN 0
 #define ANT_ALT  1
 
index 857bb28b389411ea2e1586abd3f5c749c0127327..726271c7c3306e8a9255fd645bcbe0d3c6d8fb3e 100644 (file)
@@ -178,12 +178,12 @@ void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data,
        pe.ts = mactime;
        if (ath9k_postprocess_radar_event(sc, &ard, &pe)) {
                struct dfs_pattern_detector *pd = sc->dfs_detector;
-               static u64 last_ts;
                ath_dbg(common, DFS,
                        "ath9k_dfs_process_phyerr: channel=%d, ts=%llu, "
                        "width=%d, rssi=%d, delta_ts=%llu\n",
-                       pe.freq, pe.ts, pe.width, pe.rssi, pe.ts-last_ts);
-               last_ts = pe.ts;
+                       pe.freq, pe.ts, pe.width, pe.rssi,
+                       pe.ts - sc->dfs_prev_pulse_ts);
+               sc->dfs_prev_pulse_ts = pe.ts;
                DFS_STAT_INC(sc, pulses_processed);
                if (pd != NULL && pd->add_pulse(pd, &pe)) {
                        DFS_STAT_INC(sc, radar_detected);
index dab1f0cab9937d17fd0df75d88c3eafb0ff8ec54..09a5d72f3ff5b4e9cf27b6c0305193e738c1aba5 100644 (file)
@@ -325,14 +325,14 @@ static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb)
 
 #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 RX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.skbrx_stats.c++)
+#define RX_STAT_ADD(c, a) (hif_dev->htc_handle->drv_priv->debug.skbrx_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]++)
 
 void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
-                          struct ath_htc_rx_status *rxs);
+                          struct ath_rx_status *rs);
 
 struct ath_tx_stats {
        u32 buf_queued;
@@ -345,25 +345,18 @@ struct ath_tx_stats {
        u32 queue_stats[IEEE80211_NUM_ACS];
 };
 
-struct ath_rx_stats {
+struct ath_skbrx_stats {
        u32 skb_allocated;
        u32 skb_completed;
        u32 skb_completed_bytes;
        u32 skb_dropped;
-       u32 err_crc;
-       u32 err_decrypt_crc;
-       u32 err_mic;
-       u32 err_pre_delim;
-       u32 err_post_delim;
-       u32 err_decrypt_busy;
-       u32 err_phy;
-       u32 err_phy_stats[ATH9K_PHYERR_MAX];
 };
 
 struct ath9k_debug {
        struct dentry *debugfs_phy;
        struct ath_tx_stats tx_stats;
        struct ath_rx_stats rx_stats;
+       struct ath_skbrx_stats skbrx_stats;
 };
 
 void ath9k_htc_get_et_strings(struct ieee80211_hw *hw,
@@ -385,7 +378,7 @@ void ath9k_htc_get_et_stats(struct ieee80211_hw *hw,
 #define TX_QSTAT_INC(c) do { } while (0)
 
 static inline void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
-                                        struct ath_htc_rx_status *rxs)
+                                        struct ath_rx_status *rs)
 {
 }
 
index fb071ee4fcfb3dd5969005a40537908ec92791c6..8b529e4b8ac4f37019426102b5d8d4b428882701 100644 (file)
@@ -243,39 +243,14 @@ static const struct file_operations fops_xmit = {
 };
 
 void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
-                          struct ath_htc_rx_status *rxs)
+                            struct ath_rx_status *rs)
 {
-#define RX_PHY_ERR_INC(c) priv->debug.rx_stats.err_phy_stats[c]++
-
-       if (rxs->rs_status & ATH9K_RXERR_CRC)
-               priv->debug.rx_stats.err_crc++;
-       if (rxs->rs_status & ATH9K_RXERR_DECRYPT)
-               priv->debug.rx_stats.err_decrypt_crc++;
-       if (rxs->rs_status & ATH9K_RXERR_MIC)
-               priv->debug.rx_stats.err_mic++;
-       if (rxs->rs_status & ATH9K_RX_DELIM_CRC_PRE)
-               priv->debug.rx_stats.err_pre_delim++;
-       if (rxs->rs_status & ATH9K_RX_DELIM_CRC_POST)
-               priv->debug.rx_stats.err_post_delim++;
-       if (rxs->rs_status & ATH9K_RX_DECRYPT_BUSY)
-               priv->debug.rx_stats.err_decrypt_busy++;
-
-       if (rxs->rs_status & ATH9K_RXERR_PHY) {
-               priv->debug.rx_stats.err_phy++;
-               if (rxs->rs_phyerr < ATH9K_PHYERR_MAX)
-                       RX_PHY_ERR_INC(rxs->rs_phyerr);
-       }
-
-#undef RX_PHY_ERR_INC
+       ath9k_cmn_debug_stat_rx(&priv->debug.rx_stats, rs);
 }
 
-static ssize_t read_file_recv(struct file *file, char __user *user_buf,
+static ssize_t read_file_skb_rx(struct file *file, char __user *user_buf,
                              size_t count, loff_t *ppos)
 {
-#define PHY_ERR(s, p)                                                  \
-       len += scnprintf(buf + len, size - len, "%20s : %10u\n", s,     \
-                        priv->debug.rx_stats.err_phy_stats[p]);
-
        struct ath9k_htc_priv *priv = file->private_data;
        char *buf;
        unsigned int len = 0, size = 1500;
@@ -287,63 +262,13 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf,
 
        len += scnprintf(buf + len, size - len,
                         "%20s : %10u\n", "SKBs allocated",
-                        priv->debug.rx_stats.skb_allocated);
+                        priv->debug.skbrx_stats.skb_allocated);
        len += scnprintf(buf + len, size - len,
                         "%20s : %10u\n", "SKBs completed",
-                        priv->debug.rx_stats.skb_completed);
+                        priv->debug.skbrx_stats.skb_completed);
        len += scnprintf(buf + len, size - len,
                         "%20s : %10u\n", "SKBs Dropped",
-                        priv->debug.rx_stats.skb_dropped);
-
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10u\n", "CRC ERR",
-                        priv->debug.rx_stats.err_crc);
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10u\n", "DECRYPT CRC ERR",
-                        priv->debug.rx_stats.err_decrypt_crc);
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10u\n", "MIC ERR",
-                        priv->debug.rx_stats.err_mic);
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10u\n", "PRE-DELIM CRC ERR",
-                        priv->debug.rx_stats.err_pre_delim);
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10u\n", "POST-DELIM CRC ERR",
-                        priv->debug.rx_stats.err_post_delim);
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10u\n", "DECRYPT BUSY ERR",
-                        priv->debug.rx_stats.err_decrypt_busy);
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10u\n", "TOTAL PHY ERR",
-                        priv->debug.rx_stats.err_phy);
-
-
-       PHY_ERR("UNDERRUN", ATH9K_PHYERR_UNDERRUN);
-       PHY_ERR("TIMING", ATH9K_PHYERR_TIMING);
-       PHY_ERR("PARITY", ATH9K_PHYERR_PARITY);
-       PHY_ERR("RATE", ATH9K_PHYERR_RATE);
-       PHY_ERR("LENGTH", ATH9K_PHYERR_LENGTH);
-       PHY_ERR("RADAR", ATH9K_PHYERR_RADAR);
-       PHY_ERR("SERVICE", ATH9K_PHYERR_SERVICE);
-       PHY_ERR("TOR", ATH9K_PHYERR_TOR);
-       PHY_ERR("OFDM-TIMING", ATH9K_PHYERR_OFDM_TIMING);
-       PHY_ERR("OFDM-SIGNAL-PARITY", ATH9K_PHYERR_OFDM_SIGNAL_PARITY);
-       PHY_ERR("OFDM-RATE", ATH9K_PHYERR_OFDM_RATE_ILLEGAL);
-       PHY_ERR("OFDM-LENGTH", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL);
-       PHY_ERR("OFDM-POWER-DROP", ATH9K_PHYERR_OFDM_POWER_DROP);
-       PHY_ERR("OFDM-SERVICE", ATH9K_PHYERR_OFDM_SERVICE);
-       PHY_ERR("OFDM-RESTART", ATH9K_PHYERR_OFDM_RESTART);
-       PHY_ERR("FALSE-RADAR-EXT", ATH9K_PHYERR_FALSE_RADAR_EXT);
-       PHY_ERR("CCK-TIMING", ATH9K_PHYERR_CCK_TIMING);
-       PHY_ERR("CCK-HEADER-CRC", ATH9K_PHYERR_CCK_HEADER_CRC);
-       PHY_ERR("CCK-RATE", ATH9K_PHYERR_CCK_RATE_ILLEGAL);
-       PHY_ERR("CCK-SERVICE", ATH9K_PHYERR_CCK_SERVICE);
-       PHY_ERR("CCK-RESTART", ATH9K_PHYERR_CCK_RESTART);
-       PHY_ERR("CCK-LENGTH", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL);
-       PHY_ERR("CCK-POWER-DROP", ATH9K_PHYERR_CCK_POWER_DROP);
-       PHY_ERR("HT-CRC", ATH9K_PHYERR_HT_CRC_ERROR);
-       PHY_ERR("HT-LENGTH", ATH9K_PHYERR_HT_LENGTH_ILLEGAL);
-       PHY_ERR("HT-RATE", ATH9K_PHYERR_HT_RATE_ILLEGAL);
+                        priv->debug.skbrx_stats.skb_dropped);
 
        if (len > size)
                len = size;
@@ -352,12 +277,10 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf,
        kfree(buf);
 
        return retval;
-
-#undef PHY_ERR
 }
 
-static const struct file_operations fops_recv = {
-       .read = read_file_recv,
+static const struct file_operations fops_skb_rx = {
+       .read = read_file_skb_rx,
        .open = simple_open,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
@@ -486,423 +409,6 @@ static const struct file_operations fops_debug = {
        .llseek = default_llseek,
 };
 
-static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf,
-                                    size_t count, loff_t *ppos)
-{
-       struct ath9k_htc_priv *priv = file->private_data;
-       struct ath_common *common = ath9k_hw_common(priv->ah);
-       struct base_eep_header *pBase = NULL;
-       unsigned int len = 0, size = 1500;
-       ssize_t retval = 0;
-       char *buf;
-
-       pBase = ath9k_htc_get_eeprom_base(priv);
-
-       if (pBase == NULL) {
-               ath_err(common, "Unknown EEPROM type\n");
-               return 0;
-       }
-
-       buf = kzalloc(size, GFP_KERNEL);
-       if (buf == NULL)
-               return -ENOMEM;
-
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10d\n", "Major Version",
-                        pBase->version >> 12);
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10d\n", "Minor Version",
-                        pBase->version & 0xFFF);
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10d\n", "Checksum",
-                        pBase->checksum);
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10d\n", "Length",
-                        pBase->length);
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10d\n", "RegDomain1",
-                        pBase->regDmn[0]);
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10d\n", "RegDomain2",
-                        pBase->regDmn[1]);
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10d\n",
-                        "TX Mask", pBase->txMask);
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10d\n",
-                        "RX Mask", pBase->rxMask);
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10d\n",
-                        "Allow 5GHz",
-                        !!(pBase->opCapFlags & AR5416_OPFLAGS_11A));
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10d\n",
-                        "Allow 2GHz",
-                        !!(pBase->opCapFlags & AR5416_OPFLAGS_11G));
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10d\n",
-                        "Disable 2GHz HT20",
-                        !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT20));
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10d\n",
-                        "Disable 2GHz HT40",
-                        !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT40));
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10d\n",
-                        "Disable 5Ghz HT20",
-                        !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT20));
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10d\n",
-                        "Disable 5Ghz HT40",
-                        !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT40));
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10d\n",
-                        "Big Endian",
-                        !!(pBase->eepMisc & 0x01));
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10d\n",
-                        "Cal Bin Major Ver",
-                        (pBase->binBuildNumber >> 24) & 0xFF);
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10d\n",
-                        "Cal Bin Minor Ver",
-                        (pBase->binBuildNumber >> 16) & 0xFF);
-       len += scnprintf(buf + len, size - len,
-                        "%20s : %10d\n",
-                        "Cal Bin Build",
-                        (pBase->binBuildNumber >> 8) & 0xFF);
-
-       /*
-        * UB91 specific data.
-        */
-       if (AR_SREV_9271(priv->ah)) {
-               struct base_eep_header_4k *pBase4k =
-                       &priv->ah->eeprom.map4k.baseEepHeader;
-
-               len += scnprintf(buf + len, size - len,
-                                "%20s : %10d\n",
-                                "TX Gain type",
-                                pBase4k->txGainType);
-       }
-
-       /*
-        * UB95 specific data.
-        */
-       if (priv->ah->hw_version.usbdev == AR9287_USB) {
-               struct base_eep_ar9287_header *pBase9287 =
-                       &priv->ah->eeprom.map9287.baseEepHeader;
-
-               len += scnprintf(buf + len, size - len,
-                                "%20s : %10ddB\n",
-                                "Power Table Offset",
-                                pBase9287->pwrTableOffset);
-
-               len += scnprintf(buf + len, size - len,
-                                "%20s : %10d\n",
-                                "OpenLoop Power Ctrl",
-                                pBase9287->openLoopPwrCntl);
-       }
-
-       len += scnprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
-                        pBase->macAddr);
-       if (len > size)
-               len = size;
-
-       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-       kfree(buf);
-
-       return retval;
-}
-
-static const struct file_operations fops_base_eeprom = {
-       .read = read_file_base_eeprom,
-       .open = simple_open,
-       .owner = THIS_MODULE,
-       .llseek = default_llseek,
-};
-
-static ssize_t read_4k_modal_eeprom(struct file *file,
-                                   char __user *user_buf,
-                                   size_t count, loff_t *ppos)
-{
-#define PR_EEP(_s, _val)                                               \
-       do {                                                            \
-               len += scnprintf(buf + len, size - len, "%20s : %10d\n",\
-                                _s, (_val));                           \
-       } while (0)
-
-       struct ath9k_htc_priv *priv = file->private_data;
-       struct modal_eep_4k_header *pModal = &priv->ah->eeprom.map4k.modalHeader;
-       unsigned int len = 0, size = 2048;
-       ssize_t retval = 0;
-       char *buf;
-
-       buf = kzalloc(size, GFP_KERNEL);
-       if (buf == NULL)
-               return -ENOMEM;
-
-       PR_EEP("Chain0 Ant. Control", pModal->antCtrlChain[0]);
-       PR_EEP("Ant. Common Control", pModal->antCtrlCommon);
-       PR_EEP("Chain0 Ant. Gain", pModal->antennaGainCh[0]);
-       PR_EEP("Switch Settle", pModal->switchSettling);
-       PR_EEP("Chain0 TxRxAtten", pModal->txRxAttenCh[0]);
-       PR_EEP("Chain0 RxTxMargin", pModal->rxTxMarginCh[0]);
-       PR_EEP("ADC Desired size", pModal->adcDesiredSize);
-       PR_EEP("PGA Desired size", pModal->pgaDesiredSize);
-       PR_EEP("Chain0 xlna Gain", pModal->xlnaGainCh[0]);
-       PR_EEP("txEndToXpaOff", pModal->txEndToXpaOff);
-       PR_EEP("txEndToRxOn", pModal->txEndToRxOn);
-       PR_EEP("txFrameToXpaOn", pModal->txFrameToXpaOn);
-       PR_EEP("CCA Threshold)", pModal->thresh62);
-       PR_EEP("Chain0 NF Threshold", pModal->noiseFloorThreshCh[0]);
-       PR_EEP("xpdGain", pModal->xpdGain);
-       PR_EEP("External PD", pModal->xpd);
-       PR_EEP("Chain0 I Coefficient", pModal->iqCalICh[0]);
-       PR_EEP("Chain0 Q Coefficient", pModal->iqCalQCh[0]);
-       PR_EEP("pdGainOverlap", pModal->pdGainOverlap);
-       PR_EEP("O/D Bias Version", pModal->version);
-       PR_EEP("CCK OutputBias", pModal->ob_0);
-       PR_EEP("BPSK OutputBias", pModal->ob_1);
-       PR_EEP("QPSK OutputBias", pModal->ob_2);
-       PR_EEP("16QAM OutputBias", pModal->ob_3);
-       PR_EEP("64QAM OutputBias", pModal->ob_4);
-       PR_EEP("CCK Driver1_Bias", pModal->db1_0);
-       PR_EEP("BPSK Driver1_Bias", pModal->db1_1);
-       PR_EEP("QPSK Driver1_Bias", pModal->db1_2);
-       PR_EEP("16QAM Driver1_Bias", pModal->db1_3);
-       PR_EEP("64QAM Driver1_Bias", pModal->db1_4);
-       PR_EEP("CCK Driver2_Bias", pModal->db2_0);
-       PR_EEP("BPSK Driver2_Bias", pModal->db2_1);
-       PR_EEP("QPSK Driver2_Bias", pModal->db2_2);
-       PR_EEP("16QAM Driver2_Bias", pModal->db2_3);
-       PR_EEP("64QAM Driver2_Bias", pModal->db2_4);
-       PR_EEP("xPA Bias Level", pModal->xpaBiasLvl);
-       PR_EEP("txFrameToDataStart", pModal->txFrameToDataStart);
-       PR_EEP("txFrameToPaOn", pModal->txFrameToPaOn);
-       PR_EEP("HT40 Power Inc.", pModal->ht40PowerIncForPdadc);
-       PR_EEP("Chain0 bswAtten", pModal->bswAtten[0]);
-       PR_EEP("Chain0 bswMargin", pModal->bswMargin[0]);
-       PR_EEP("HT40 Switch Settle", pModal->swSettleHt40);
-       PR_EEP("Chain0 xatten2Db", pModal->xatten2Db[0]);
-       PR_EEP("Chain0 xatten2Margin", pModal->xatten2Margin[0]);
-       PR_EEP("Ant. Diversity ctl1", pModal->antdiv_ctl1);
-       PR_EEP("Ant. Diversity ctl2", pModal->antdiv_ctl2);
-       PR_EEP("TX Diversity", pModal->tx_diversity);
-
-       if (len > size)
-               len = size;
-
-       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-       kfree(buf);
-
-       return retval;
-
-#undef PR_EEP
-}
-
-static ssize_t read_def_modal_eeprom(struct file *file,
-                                    char __user *user_buf,
-                                    size_t count, loff_t *ppos)
-{
-#define PR_EEP(_s, _val)                                               \
-       do {                                                            \
-               if (pBase->opCapFlags & AR5416_OPFLAGS_11G) {           \
-                       pModal = &priv->ah->eeprom.def.modalHeader[1];  \
-                       len += scnprintf(buf + len, size - len, "%20s : %8d%7s", \
-                                        _s, (_val), "|");              \
-               }                                                       \
-               if (pBase->opCapFlags & AR5416_OPFLAGS_11A) {           \
-                       pModal = &priv->ah->eeprom.def.modalHeader[0];  \
-                       len += scnprintf(buf + len, size - len, "%9d\n",\
-                                       (_val));                        \
-               }                                                       \
-       } while (0)
-
-       struct ath9k_htc_priv *priv = file->private_data;
-       struct base_eep_header *pBase = &priv->ah->eeprom.def.baseEepHeader;
-       struct modal_eep_header *pModal = NULL;
-       unsigned int len = 0, size = 3500;
-       ssize_t retval = 0;
-       char *buf;
-
-       buf = kzalloc(size, GFP_KERNEL);
-       if (buf == NULL)
-               return -ENOMEM;
-
-       len += scnprintf(buf + len, size - len,
-                        "%31s %15s\n", "2G", "5G");
-       len += scnprintf(buf + len, size - len,
-                        "%32s %16s\n", "====", "====\n");
-
-       PR_EEP("Chain0 Ant. Control", pModal->antCtrlChain[0]);
-       PR_EEP("Chain1 Ant. Control", pModal->antCtrlChain[1]);
-       PR_EEP("Chain2 Ant. Control", pModal->antCtrlChain[2]);
-       PR_EEP("Ant. Common Control", pModal->antCtrlCommon);
-       PR_EEP("Chain0 Ant. Gain", pModal->antennaGainCh[0]);
-       PR_EEP("Chain1 Ant. Gain", pModal->antennaGainCh[1]);
-       PR_EEP("Chain2 Ant. Gain", pModal->antennaGainCh[2]);
-       PR_EEP("Switch Settle", pModal->switchSettling);
-       PR_EEP("Chain0 TxRxAtten", pModal->txRxAttenCh[0]);
-       PR_EEP("Chain1 TxRxAtten", pModal->txRxAttenCh[1]);
-       PR_EEP("Chain2 TxRxAtten", pModal->txRxAttenCh[2]);
-       PR_EEP("Chain0 RxTxMargin", pModal->rxTxMarginCh[0]);
-       PR_EEP("Chain1 RxTxMargin", pModal->rxTxMarginCh[1]);
-       PR_EEP("Chain2 RxTxMargin", pModal->rxTxMarginCh[2]);
-       PR_EEP("ADC Desired size", pModal->adcDesiredSize);
-       PR_EEP("PGA Desired size", pModal->pgaDesiredSize);
-       PR_EEP("Chain0 xlna Gain", pModal->xlnaGainCh[0]);
-       PR_EEP("Chain1 xlna Gain", pModal->xlnaGainCh[1]);
-       PR_EEP("Chain2 xlna Gain", pModal->xlnaGainCh[2]);
-       PR_EEP("txEndToXpaOff", pModal->txEndToXpaOff);
-       PR_EEP("txEndToRxOn", pModal->txEndToRxOn);
-       PR_EEP("txFrameToXpaOn", pModal->txFrameToXpaOn);
-       PR_EEP("CCA Threshold)", pModal->thresh62);
-       PR_EEP("Chain0 NF Threshold", pModal->noiseFloorThreshCh[0]);
-       PR_EEP("Chain1 NF Threshold", pModal->noiseFloorThreshCh[1]);
-       PR_EEP("Chain2 NF Threshold", pModal->noiseFloorThreshCh[2]);
-       PR_EEP("xpdGain", pModal->xpdGain);
-       PR_EEP("External PD", pModal->xpd);
-       PR_EEP("Chain0 I Coefficient", pModal->iqCalICh[0]);
-       PR_EEP("Chain1 I Coefficient", pModal->iqCalICh[1]);
-       PR_EEP("Chain2 I Coefficient", pModal->iqCalICh[2]);
-       PR_EEP("Chain0 Q Coefficient", pModal->iqCalQCh[0]);
-       PR_EEP("Chain1 Q Coefficient", pModal->iqCalQCh[1]);
-       PR_EEP("Chain2 Q Coefficient", pModal->iqCalQCh[2]);
-       PR_EEP("pdGainOverlap", pModal->pdGainOverlap);
-       PR_EEP("Chain0 OutputBias", pModal->ob);
-       PR_EEP("Chain0 DriverBias", pModal->db);
-       PR_EEP("xPA Bias Level", pModal->xpaBiasLvl);
-       PR_EEP("2chain pwr decrease", pModal->pwrDecreaseFor2Chain);
-       PR_EEP("3chain pwr decrease", pModal->pwrDecreaseFor3Chain);
-       PR_EEP("txFrameToDataStart", pModal->txFrameToDataStart);
-       PR_EEP("txFrameToPaOn", pModal->txFrameToPaOn);
-       PR_EEP("HT40 Power Inc.", pModal->ht40PowerIncForPdadc);
-       PR_EEP("Chain0 bswAtten", pModal->bswAtten[0]);
-       PR_EEP("Chain1 bswAtten", pModal->bswAtten[1]);
-       PR_EEP("Chain2 bswAtten", pModal->bswAtten[2]);
-       PR_EEP("Chain0 bswMargin", pModal->bswMargin[0]);
-       PR_EEP("Chain1 bswMargin", pModal->bswMargin[1]);
-       PR_EEP("Chain2 bswMargin", pModal->bswMargin[2]);
-       PR_EEP("HT40 Switch Settle", pModal->swSettleHt40);
-       PR_EEP("Chain0 xatten2Db", pModal->xatten2Db[0]);
-       PR_EEP("Chain1 xatten2Db", pModal->xatten2Db[1]);
-       PR_EEP("Chain2 xatten2Db", pModal->xatten2Db[2]);
-       PR_EEP("Chain0 xatten2Margin", pModal->xatten2Margin[0]);
-       PR_EEP("Chain1 xatten2Margin", pModal->xatten2Margin[1]);
-       PR_EEP("Chain2 xatten2Margin", pModal->xatten2Margin[2]);
-       PR_EEP("Chain1 OutputBias", pModal->ob_ch1);
-       PR_EEP("Chain1 DriverBias", pModal->db_ch1);
-       PR_EEP("LNA Control", pModal->lna_ctl);
-       PR_EEP("XPA Bias Freq0", pModal->xpaBiasLvlFreq[0]);
-       PR_EEP("XPA Bias Freq1", pModal->xpaBiasLvlFreq[1]);
-       PR_EEP("XPA Bias Freq2", pModal->xpaBiasLvlFreq[2]);
-
-       if (len > size)
-               len = size;
-
-       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-       kfree(buf);
-
-       return retval;
-
-#undef PR_EEP
-}
-
-static ssize_t read_9287_modal_eeprom(struct file *file,
-                                     char __user *user_buf,
-                                     size_t count, loff_t *ppos)
-{
-#define PR_EEP(_s, _val)                                               \
-       do {                                                            \
-               len += scnprintf(buf + len, size - len, "%20s : %10d\n",\
-                                _s, (_val));                           \
-       } while (0)
-
-       struct ath9k_htc_priv *priv = file->private_data;
-       struct modal_eep_ar9287_header *pModal = &priv->ah->eeprom.map9287.modalHeader;
-       unsigned int len = 0, size = 3000;
-       ssize_t retval = 0;
-       char *buf;
-
-       buf = kzalloc(size, GFP_KERNEL);
-       if (buf == NULL)
-               return -ENOMEM;
-
-       PR_EEP("Chain0 Ant. Control", pModal->antCtrlChain[0]);
-       PR_EEP("Chain1 Ant. Control", pModal->antCtrlChain[1]);
-       PR_EEP("Ant. Common Control", pModal->antCtrlCommon);
-       PR_EEP("Chain0 Ant. Gain", pModal->antennaGainCh[0]);
-       PR_EEP("Chain1 Ant. Gain", pModal->antennaGainCh[1]);
-       PR_EEP("Switch Settle", pModal->switchSettling);
-       PR_EEP("Chain0 TxRxAtten", pModal->txRxAttenCh[0]);
-       PR_EEP("Chain1 TxRxAtten", pModal->txRxAttenCh[1]);
-       PR_EEP("Chain0 RxTxMargin", pModal->rxTxMarginCh[0]);
-       PR_EEP("Chain1 RxTxMargin", pModal->rxTxMarginCh[1]);
-       PR_EEP("ADC Desired size", pModal->adcDesiredSize);
-       PR_EEP("txEndToXpaOff", pModal->txEndToXpaOff);
-       PR_EEP("txEndToRxOn", pModal->txEndToRxOn);
-       PR_EEP("txFrameToXpaOn", pModal->txFrameToXpaOn);
-       PR_EEP("CCA Threshold)", pModal->thresh62);
-       PR_EEP("Chain0 NF Threshold", pModal->noiseFloorThreshCh[0]);
-       PR_EEP("Chain1 NF Threshold", pModal->noiseFloorThreshCh[1]);
-       PR_EEP("xpdGain", pModal->xpdGain);
-       PR_EEP("External PD", pModal->xpd);
-       PR_EEP("Chain0 I Coefficient", pModal->iqCalICh[0]);
-       PR_EEP("Chain1 I Coefficient", pModal->iqCalICh[1]);
-       PR_EEP("Chain0 Q Coefficient", pModal->iqCalQCh[0]);
-       PR_EEP("Chain1 Q Coefficient", pModal->iqCalQCh[1]);
-       PR_EEP("pdGainOverlap", pModal->pdGainOverlap);
-       PR_EEP("xPA Bias Level", pModal->xpaBiasLvl);
-       PR_EEP("txFrameToDataStart", pModal->txFrameToDataStart);
-       PR_EEP("txFrameToPaOn", pModal->txFrameToPaOn);
-       PR_EEP("HT40 Power Inc.", pModal->ht40PowerIncForPdadc);
-       PR_EEP("Chain0 bswAtten", pModal->bswAtten[0]);
-       PR_EEP("Chain1 bswAtten", pModal->bswAtten[1]);
-       PR_EEP("Chain0 bswMargin", pModal->bswMargin[0]);
-       PR_EEP("Chain1 bswMargin", pModal->bswMargin[1]);
-       PR_EEP("HT40 Switch Settle", pModal->swSettleHt40);
-       PR_EEP("AR92x7 Version", pModal->version);
-       PR_EEP("DriverBias1", pModal->db1);
-       PR_EEP("DriverBias2", pModal->db1);
-       PR_EEP("CCK OutputBias", pModal->ob_cck);
-       PR_EEP("PSK OutputBias", pModal->ob_psk);
-       PR_EEP("QAM OutputBias", pModal->ob_qam);
-       PR_EEP("PAL_OFF OutputBias", pModal->ob_pal_off);
-
-       if (len > size)
-               len = size;
-
-       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-       kfree(buf);
-
-       return retval;
-
-#undef PR_EEP
-}
-
-static ssize_t read_file_modal_eeprom(struct file *file, char __user *user_buf,
-                                     size_t count, loff_t *ppos)
-{
-       struct ath9k_htc_priv *priv = file->private_data;
-
-       if (AR_SREV_9271(priv->ah))
-               return read_4k_modal_eeprom(file, user_buf, count, ppos);
-       else if (priv->ah->hw_version.usbdev == AR9280_USB)
-               return read_def_modal_eeprom(file, user_buf, count, ppos);
-       else if (priv->ah->hw_version.usbdev == AR9287_USB)
-               return read_9287_modal_eeprom(file, user_buf, count, ppos);
-
-       return 0;
-}
-
-static const struct file_operations fops_modal_eeprom = {
-       .read = read_file_modal_eeprom,
-       .open = simple_open,
-       .owner = THIS_MODULE,
-       .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] = {
@@ -947,6 +453,8 @@ int ath9k_htc_get_et_sset_count(struct ieee80211_hw *hw,
 
 #define STXBASE priv->debug.tx_stats
 #define SRXBASE priv->debug.rx_stats
+#define SKBTXBASE priv->debug.tx_stats
+#define SKBRXBASE priv->debug.skbrx_stats
 #define ASTXQ(a)                                       \
        data[i++] = STXBASE.a[IEEE80211_AC_BE];         \
        data[i++] = STXBASE.a[IEEE80211_AC_BK];         \
@@ -960,24 +468,24 @@ void ath9k_htc_get_et_stats(struct ieee80211_hw *hw,
        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;
+       data[i++] = SKBTXBASE.skb_success;
+       data[i++] = SKBTXBASE.skb_success_bytes;
+       data[i++] = SKBRXBASE.skb_completed;
+       data[i++] = SKBRXBASE.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.crc_err;
+       data[i++] = SRXBASE.decrypt_crc_err;
+       data[i++] = SRXBASE.phy_err;
+       data[i++] = SRXBASE.mic_err;
+       data[i++] = SRXBASE.pre_delim_crc_err;
+       data[i++] = SRXBASE.post_delim_crc_err;
+       data[i++] = SRXBASE.decrypt_busy_err;
 
-       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];
+       data[i++] = SRXBASE.phy_err_stats[ATH9K_PHYERR_RADAR];
+       data[i++] = SRXBASE.phy_err_stats[ATH9K_PHYERR_OFDM_TIMING];
+       data[i++] = SRXBASE.phy_err_stats[ATH9K_PHYERR_CCK_TIMING];
 
        WARN_ON(i != ATH9K_HTC_SSTATS_LEN);
 }
@@ -1001,18 +509,21 @@ int ath9k_htc_init_debug(struct ath_hw *ah)
                            priv, &fops_tgt_rx_stats);
        debugfs_create_file("xmit", S_IRUSR, priv->debug.debugfs_phy,
                            priv, &fops_xmit);
-       debugfs_create_file("recv", S_IRUSR, priv->debug.debugfs_phy,
-                           priv, &fops_recv);
+       debugfs_create_file("skb_rx", S_IRUSR, priv->debug.debugfs_phy,
+                           priv, &fops_skb_rx);
+
+       ath9k_cmn_debug_recv(priv->debug.debugfs_phy, &priv->debug.rx_stats);
+       ath9k_cmn_debug_phy_err(priv->debug.debugfs_phy, &priv->debug.rx_stats);
+
        debugfs_create_file("slot", S_IRUSR, priv->debug.debugfs_phy,
                            priv, &fops_slot);
        debugfs_create_file("queue", S_IRUSR, priv->debug.debugfs_phy,
                            priv, &fops_queue);
        debugfs_create_file("debug", S_IRUSR | S_IWUSR, priv->debug.debugfs_phy,
                            priv, &fops_debug);
-       debugfs_create_file("base_eeprom", S_IRUSR, priv->debug.debugfs_phy,
-                           priv, &fops_base_eeprom);
-       debugfs_create_file("modal_eeprom", S_IRUSR, priv->debug.debugfs_phy,
-                           priv, &fops_modal_eeprom);
+
+       ath9k_cmn_debug_base_eeprom(priv->debug.debugfs_phy, priv->ah);
+       ath9k_cmn_debug_modal_eeprom(priv->debug.debugfs_phy, priv->ah);
 
        return 0;
 }
index 289f3d8924b5735cb773d2b1d527b5cf106b5d64..bb86eb2ffc953545d759647d70cacc86a4227022 100644 (file)
@@ -996,8 +996,6 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv,
                goto rx_next;
        }
 
-       ath9k_htc_err_stat_rx(priv, rxstatus);
-
        /* Get the RX status information */
 
        memset(rx_status, 0, sizeof(struct ieee80211_rx_status));
@@ -1005,6 +1003,7 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv,
        /* Copy everything from ath_htc_rx_status (HTC_RX_FRAME_HEADER).
         * After this, we can drop this part of skb. */
        rx_status_htc_to_ath(&rx_stats, rxstatus);
+       ath9k_htc_err_stat_rx(priv, &rx_stats);
        rx_status->mactime = be64_to_cpu(rxstatus->rs_tstamp);
        skb_pull(skb, HTC_RX_FRAME_HEADER_SIZE);
 
index c8a9dfab1fee2ea4778046b186428ba3f014e054..2a8ed8375ec0584771f4f7dcc833d17131ec34b2 100644 (file)
@@ -26,7 +26,6 @@
 #include "ar9003_mac.h"
 #include "ar9003_mci.h"
 #include "ar9003_phy.h"
-#include "debug.h"
 #include "ath9k.h"
 
 static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type);
@@ -246,6 +245,8 @@ static void ath9k_hw_read_revisions(struct ath_hw *ah)
                return;
        case AR9300_DEVID_AR953X:
                ah->hw_version.macVersion = AR_SREV_VERSION_9531;
+               if (ah->get_mac_revision)
+                       ah->hw_version.macRev = ah->get_mac_revision();
                return;
        }
 
index 36ae6490e5543af837f046f169115e3dd5d65d6b..0246b990fe87ade49ffa26ca417a8ff999179ebc 100644 (file)
@@ -61,6 +61,10 @@ static int ath9k_ps_enable;
 module_param_named(ps_enable, ath9k_ps_enable, int, 0444);
 MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave");
 
+static int ath9k_use_chanctx;
+module_param_named(use_chanctx, ath9k_use_chanctx, int, 0444);
+MODULE_PARM_DESC(use_chanctx, "Enable channel context for concurrency");
+
 bool is_ath9k_unloaded;
 
 #ifdef CONFIG_MAC80211_LEDS
@@ -508,7 +512,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        sc->tx99_power = MAX_RATE_POWER + 1;
        init_waitqueue_head(&sc->tx_wait);
 
-       if (!pdata) {
+       if (!pdata || pdata->use_eeprom) {
                ah->ah_flags |= AH_USE_EEPROM;
                sc->sc_ah->led_pin = -1;
        } else {
@@ -589,6 +593,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        if (ret)
                goto err_btcoex;
 
+       sc->p2p_ps_timer = ath_gen_timer_alloc(sc->sc_ah, ath9k_p2p_ps_timer,
+               NULL, sc, AR_FIRST_NDP_TIMER);
+
        ath9k_cmn_init_crypto(sc->sc_ah);
        ath9k_init_misc(sc);
        ath_fill_led_pin(sc);
@@ -643,17 +650,20 @@ static void ath9k_init_txpower_limits(struct ath_softc *sc)
 }
 
 static const struct ieee80211_iface_limit if_limits[] = {
-       { .max = 2048,  .types = BIT(NL80211_IFTYPE_STATION) |
-                                BIT(NL80211_IFTYPE_P2P_CLIENT) |
-                                BIT(NL80211_IFTYPE_WDS) },
+       { .max = 2048,  .types = BIT(NL80211_IFTYPE_STATION) },
        { .max = 8,     .types =
 #ifdef CONFIG_MAC80211_MESH
                                 BIT(NL80211_IFTYPE_MESH_POINT) |
 #endif
-                                BIT(NL80211_IFTYPE_AP) |
+                                BIT(NL80211_IFTYPE_AP) },
+       { .max = 1,     .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
                                 BIT(NL80211_IFTYPE_P2P_GO) },
 };
 
+static const struct ieee80211_iface_limit wds_limits[] = {
+       { .max = 2048,  .types = BIT(NL80211_IFTYPE_WDS) },
+};
+
 static const struct ieee80211_iface_limit if_dfs_limits[] = {
        { .max = 1,     .types = BIT(NL80211_IFTYPE_AP) |
 #ifdef CONFIG_MAC80211_MESH
@@ -670,6 +680,13 @@ static const struct ieee80211_iface_combination if_comb[] = {
                .num_different_channels = 1,
                .beacon_int_infra_match = true,
        },
+       {
+               .limits = wds_limits,
+               .n_limits = ARRAY_SIZE(wds_limits),
+               .max_interfaces = 2048,
+               .num_different_channels = 1,
+               .beacon_int_infra_match = true,
+       },
 #ifdef CONFIG_ATH9K_DFS_CERTIFIED
        {
                .limits = if_dfs_limits,
@@ -711,19 +728,23 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
        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->features |= (NL80211_FEATURE_ACTIVE_MONITOR |
+                               NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE);
 
        if (!config_enabled(CONFIG_ATH9K_TX99)) {
                hw->wiphy->interface_modes =
                        BIT(NL80211_IFTYPE_P2P_GO) |
                        BIT(NL80211_IFTYPE_P2P_CLIENT) |
                        BIT(NL80211_IFTYPE_AP) |
-                       BIT(NL80211_IFTYPE_WDS) |
                        BIT(NL80211_IFTYPE_STATION) |
                        BIT(NL80211_IFTYPE_ADHOC) |
                        BIT(NL80211_IFTYPE_MESH_POINT);
                hw->wiphy->iface_combinations = if_comb;
-               hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
+               if (!ath9k_use_chanctx) {
+                       hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
+                       hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_WDS);
+               } else
+                       hw->wiphy->n_iface_combinations = 1;
        }
 
        hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
@@ -855,6 +876,9 @@ static void ath9k_deinit_softc(struct ath_softc *sc)
 {
        int i = 0;
 
+       if (sc->p2p_ps_timer)
+               ath_gen_timer_free(sc->sc_ah, sc->p2p_ps_timer);
+
        ath9k_deinit_btcoex(sc);
 
        for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
index 51ce36f108f9a8f0f6619e6b73e613722c2a72a8..275205ab5f15ea15a00f341942efbe6f3bdc6adc 100644 (file)
@@ -958,3 +958,25 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah)
        return;
 }
 EXPORT_SYMBOL(ath9k_hw_set_interrupts);
+
+#define ATH9K_HW_MAX_DCU       10
+#define ATH9K_HW_SLICE_PER_DCU 16
+#define ATH9K_HW_BIT_IN_SLICE  16
+void ath9k_hw_set_tx_filter(struct ath_hw *ah, u8 destidx, bool set)
+{
+       int dcu_idx;
+       u32 filter;
+
+       for (dcu_idx = 0; dcu_idx < 10; dcu_idx++) {
+               filter = SM(set, AR_D_TXBLK_WRITE_COMMAND);
+               filter |= SM(dcu_idx, AR_D_TXBLK_WRITE_DCU);
+               filter |= SM((destidx / ATH9K_HW_SLICE_PER_DCU),
+                            AR_D_TXBLK_WRITE_SLICE);
+               filter |= BIT(destidx % ATH9K_HW_BIT_IN_SLICE);
+               ath_dbg(ath9k_hw_common(ah), PS,
+                       "DCU%d staid %d set %d txfilter %08x\n",
+                       dcu_idx, destidx, set, filter);
+               REG_WRITE(ah, AR_D_TXBLK_BASE, filter);
+       }
+}
+EXPORT_SYMBOL(ath9k_hw_set_tx_filter);
index 89df634e81f9a703d6dcf5eefee8881ef738570b..da768675753595c3c27ef3fd920327af3c1a547e 100644 (file)
@@ -729,6 +729,7 @@ void ath9k_hw_startpcureceive(struct ath_hw *ah, bool is_scanning);
 void ath9k_hw_abortpcurecv(struct ath_hw *ah);
 bool ath9k_hw_stopdmarecv(struct ath_hw *ah, bool *reset);
 int ath9k_hw_beaconq_setup(struct ath_hw *ah);
+void ath9k_hw_set_tx_filter(struct ath_hw *ah, u8 destidx, bool set);
 
 /* Interrupt Handling */
 bool ath9k_hw_intrpend(struct ath_hw *ah);
index d69853b848ce1f10167275c4e85d6026ee41c5f1..62ac95d6bb9d6e60b3bbc47e4f7715eecbe56bb9 100644 (file)
@@ -261,6 +261,8 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
        sc->gtt_cnt = 0;
        ieee80211_wake_queues(sc->hw);
 
+       ath9k_p2p_ps_timer(sc);
+
        return true;
 }
 
@@ -419,6 +421,7 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta,
        an->sc = sc;
        an->sta = sta;
        an->vif = vif;
+       memset(&an->key_idx, 0, sizeof(an->key_idx));
 
        ath_tx_node_init(sc, an);
 }
@@ -1119,6 +1122,8 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
        if (ath9k_uses_beacons(vif->type))
                ath9k_beacon_assign_slot(sc, vif);
 
+       avp->vif = vif;
+
        an->sc = sc;
        an->sta = NULL;
        an->vif = vif;
@@ -1163,6 +1168,29 @@ static int ath9k_change_interface(struct ieee80211_hw *hw,
        return 0;
 }
 
+static void
+ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       s32 tsf, target_tsf;
+
+       if (!avp || !avp->noa.has_next_tsf)
+               return;
+
+       ath9k_hw_gen_timer_stop(ah, sc->p2p_ps_timer);
+
+       tsf = ath9k_hw_gettsf32(sc->sc_ah);
+
+       target_tsf = avp->noa.next_tsf;
+       if (!avp->noa.absent)
+               target_tsf -= ATH_P2P_PS_STOP_TIME;
+
+       if (target_tsf - tsf < ATH_P2P_PS_STOP_TIME)
+               target_tsf = tsf + ATH_P2P_PS_STOP_TIME;
+
+       ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, (u32) target_tsf, 1000000);
+}
+
 static void ath9k_remove_interface(struct ieee80211_hw *hw,
                                   struct ieee80211_vif *vif)
 {
@@ -1174,6 +1202,13 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
 
        mutex_lock(&sc->mutex);
 
+       spin_lock_bh(&sc->sc_pcu_lock);
+       if (avp == sc->p2p_ps_vif) {
+               sc->p2p_ps_vif = NULL;
+               ath9k_update_p2p_ps_timer(sc, NULL);
+       }
+       spin_unlock_bh(&sc->sc_pcu_lock);
+
        sc->nvifs--;
        sc->tx99_vif = NULL;
 
@@ -1427,8 +1462,10 @@ static int ath9k_sta_add(struct ieee80211_hw *hw,
                return 0;
 
        key = ath_key_config(common, vif, sta, &ps_key);
-       if (key > 0)
+       if (key > 0) {
                an->ps_key = key;
+               an->key_idx[0] = key;
+       }
 
        return 0;
 }
@@ -1446,6 +1483,7 @@ static void ath9k_del_ps_key(struct ath_softc *sc,
 
        ath_key_delete(common, &ps_key);
        an->ps_key = 0;
+       an->key_idx[0] = 0;
 }
 
 static int ath9k_sta_remove(struct ieee80211_hw *hw,
@@ -1460,6 +1498,19 @@ static int ath9k_sta_remove(struct ieee80211_hw *hw,
        return 0;
 }
 
+static void ath9k_sta_set_tx_filter(struct ath_hw *ah,
+                                   struct ath_node *an,
+                                   bool set)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) {
+               if (!an->key_idx[i])
+                       continue;
+               ath9k_hw_set_tx_filter(ah, an->key_idx[i], set);
+       }
+}
+
 static void ath9k_sta_notify(struct ieee80211_hw *hw,
                         struct ieee80211_vif *vif,
                         enum sta_notify_cmd cmd,
@@ -1472,8 +1523,10 @@ static void ath9k_sta_notify(struct ieee80211_hw *hw,
        case STA_NOTIFY_SLEEP:
                an->sleeping = true;
                ath_tx_aggr_sleep(sta, sc, an);
+               ath9k_sta_set_tx_filter(sc->sc_ah, an, true);
                break;
        case STA_NOTIFY_AWAKE:
+               ath9k_sta_set_tx_filter(sc->sc_ah, an, false);
                an->sleeping = false;
                ath_tx_aggr_wakeup(sc, an);
                break;
@@ -1529,7 +1582,8 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
 {
        struct ath_softc *sc = hw->priv;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       int ret = 0;
+       struct ath_node *an = NULL;
+       int ret = 0, i;
 
        if (ath9k_modparam_nohwcrypt)
                return -ENOSPC;
@@ -1551,13 +1605,16 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
 
        mutex_lock(&sc->mutex);
        ath9k_ps_wakeup(sc);
-       ath_dbg(common, CONFIG, "Set HW Key\n");
+       ath_dbg(common, CONFIG, "Set HW Key %d\n", cmd);
+       if (sta)
+               an = (struct ath_node *)sta->drv_priv;
 
        switch (cmd) {
        case SET_KEY:
                if (sta)
                        ath9k_del_ps_key(sc, vif, sta);
 
+               key->hw_key_idx = 0;
                ret = ath_key_config(common, vif, sta, key);
                if (ret >= 0) {
                        key->hw_key_idx = ret;
@@ -1570,9 +1627,27 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
                                key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
                        ret = 0;
                }
+               if (an && key->hw_key_idx) {
+                       for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) {
+                               if (an->key_idx[i])
+                                       continue;
+                               an->key_idx[i] = key->hw_key_idx;
+                               break;
+                       }
+                       WARN_ON(i == ARRAY_SIZE(an->key_idx));
+               }
                break;
        case DISABLE_KEY:
                ath_key_delete(common, key);
+               if (an) {
+                       for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) {
+                               if (an->key_idx[i] != key->hw_key_idx)
+                                       continue;
+                               an->key_idx[i] = 0;
+                               break;
+                       }
+               }
+               key->hw_key_idx = 0;
                break;
        default:
                ret = -EINVAL;
@@ -1636,6 +1711,66 @@ static void ath9k_bss_assoc_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
                ath9k_set_assoc_state(sc, vif);
 }
 
+void ath9k_p2p_ps_timer(void *priv)
+{
+       struct ath_softc *sc = priv;
+       struct ath_vif *avp = sc->p2p_ps_vif;
+       struct ieee80211_vif *vif;
+       struct ieee80211_sta *sta;
+       struct ath_node *an;
+       u32 tsf;
+
+       if (!avp)
+               return;
+
+       tsf = ath9k_hw_gettsf32(sc->sc_ah);
+       if (!avp->noa.absent)
+               tsf += ATH_P2P_PS_STOP_TIME;
+
+       if (!avp->noa.has_next_tsf ||
+           avp->noa.next_tsf - tsf > BIT(31))
+               ieee80211_update_p2p_noa(&avp->noa, tsf);
+
+       ath9k_update_p2p_ps_timer(sc, avp);
+
+       rcu_read_lock();
+
+       vif = avp->vif;
+       sta = ieee80211_find_sta(vif, vif->bss_conf.bssid);
+       if (!sta)
+               goto out;
+
+       an = (void *) sta->drv_priv;
+       if (an->sleeping == !!avp->noa.absent)
+               goto out;
+
+       an->sleeping = avp->noa.absent;
+       if (an->sleeping)
+               ath_tx_aggr_sleep(sta, sc, an);
+       else
+               ath_tx_aggr_wakeup(sc, an);
+
+out:
+       rcu_read_unlock();
+}
+
+void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif)
+{
+       struct ath_vif *avp = (void *)vif->drv_priv;
+       u32 tsf;
+
+       if (!sc->p2p_ps_timer)
+               return;
+
+       if (vif->type != NL80211_IFTYPE_STATION || !vif->p2p)
+               return;
+
+       sc->p2p_ps_vif = avp;
+       tsf = ath9k_hw_gettsf32(sc->sc_ah);
+       ieee80211_parse_p2p_noa(&vif->bss_conf.p2p_noa_attr, &avp->noa, tsf);
+       ath9k_update_p2p_ps_timer(sc, avp);
+}
+
 static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
                                   struct ieee80211_vif *vif,
                                   struct ieee80211_bss_conf *bss_conf,
@@ -1650,6 +1785,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        struct ath_vif *avp = (void *)vif->drv_priv;
+       unsigned long flags;
        int slottime;
 
        ath9k_ps_wakeup(sc);
@@ -1710,6 +1846,15 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
                }
        }
 
+       if (changed & BSS_CHANGED_P2P_PS) {
+               spin_lock_bh(&sc->sc_pcu_lock);
+               spin_lock_irqsave(&sc->sc_pm_lock, flags);
+               if (!(sc->ps_flags & PS_BEACON_SYNC))
+                       ath9k_update_p2p_ps(sc, vif);
+               spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
+               spin_unlock_bh(&sc->sc_pcu_lock);
+       }
+
        if (changed & CHECK_ANI)
                ath_check_ani(sc);
 
@@ -1883,7 +2028,8 @@ static bool ath9k_has_tx_pending(struct ath_softc *sc)
        return !!npend;
 }
 
-static void ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                       u32 queues, bool drop)
 {
        struct ath_softc *sc = hw->priv;
        struct ath_hw *ah = sc->sc_ah;
@@ -2084,14 +2230,6 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
        clear_bit(ATH_OP_SCANNING, &common->op_flags);
 }
 
-static void ath9k_channel_switch_beacon(struct ieee80211_hw *hw,
-                                       struct ieee80211_vif *vif,
-                                       struct cfg80211_chan_def *chandef)
-{
-       /* depend on vif->csa_active only */
-       return;
-}
-
 struct ieee80211_ops ath9k_ops = {
        .tx                 = ath9k_tx,
        .start              = ath9k_start,
@@ -2139,5 +2277,4 @@ struct ieee80211_ops ath9k_ops = {
 #endif
        .sw_scan_start      = ath9k_sw_scan_start,
        .sw_scan_complete   = ath9k_sw_scan_complete,
-       .channel_switch_beacon     = ath9k_channel_switch_beacon,
 };
index 914dbc6b17208df991e92d3c3539508ea31f57ad..4dec09e565ed865470ff6bc5738e86c21d523906 100644 (file)
@@ -686,7 +686,7 @@ static bool ath_pci_eeprom_read(struct ath_common *common, u32 off, u16 *data)
        struct ath_softc *sc = (struct ath_softc *) common->priv;
        struct ath9k_platform_data *pdata = sc->dev->platform_data;
 
-       if (pdata) {
+       if (pdata && !pdata->use_eeprom) {
                if (off >= (ARRAY_SIZE(pdata->eeprom_data))) {
                        ath_err(common,
                                "%s: eeprom read failed, offset %08x is out of range\n",
@@ -914,6 +914,7 @@ static int ath_pci_suspend(struct device *device)
         */
        ath9k_stop_btcoex(sc);
        ath9k_hw_disable(sc->sc_ah);
+       del_timer_sync(&sc->sleep_timer);
        ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP);
 
        return 0;
index 19df969ec9093235eff8075d558e076edca2785a..9105a92364f78241fa565b661a017ce59f7400f5 100644 (file)
@@ -34,7 +34,8 @@ static inline bool ath9k_check_auto_sleep(struct ath_softc *sc)
  * buffer (or rx fifo). This can incorrectly acknowledge packets
  * to a sender if last desc is self-linked.
  */
-static void ath_rx_buf_link(struct ath_softc *sc, struct ath_rxbuf *bf)
+static void ath_rx_buf_link(struct ath_softc *sc, struct ath_rxbuf *bf,
+                           bool flush)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
@@ -59,18 +60,19 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_rxbuf *bf)
                             common->rx_bufsize,
                             0);
 
-       if (sc->rx.rxlink == NULL)
-               ath9k_hw_putrxbuf(ah, bf->bf_daddr);
-       else
+       if (sc->rx.rxlink)
                *sc->rx.rxlink = bf->bf_daddr;
+       else if (!flush)
+               ath9k_hw_putrxbuf(ah, bf->bf_daddr);
 
        sc->rx.rxlink = &ds->ds_link;
 }
 
-static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_rxbuf *bf)
+static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_rxbuf *bf,
+                             bool flush)
 {
        if (sc->rx.buf_hold)
-               ath_rx_buf_link(sc, sc->rx.buf_hold);
+               ath_rx_buf_link(sc, sc->rx.buf_hold, flush);
 
        sc->rx.buf_hold = bf;
 }
@@ -442,7 +444,7 @@ int ath_startrecv(struct ath_softc *sc)
        sc->rx.buf_hold = NULL;
        sc->rx.rxlink = NULL;
        list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) {
-               ath_rx_buf_link(sc, bf);
+               ath_rx_buf_link(sc, bf, false);
        }
 
        /* We could have deleted elements so the list may be empty now */
@@ -538,7 +540,10 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb)
                sc->ps_flags &= ~PS_BEACON_SYNC;
                ath_dbg(common, PS,
                        "Reconfigure beacon timers based on synchronized timestamp\n");
-               ath9k_set_beacon(sc);
+               if (!(WARN_ON_ONCE(sc->cur_beacon_conf.beacon_interval == 0)))
+                       ath9k_set_beacon(sc);
+               if (sc->p2p_ps_vif)
+                       ath9k_update_p2p_ps(sc, sc->p2p_ps_vif->vif);
        }
 
        if (ath_beacon_dtim_pending_cab(skb)) {
@@ -1115,12 +1120,12 @@ requeue_drop_frag:
 requeue:
                list_add_tail(&bf->list, &sc->rx.rxbuf);
 
-               if (edma) {
-                       ath_rx_edma_buf_link(sc, qtype);
-               } else {
-                       ath_rx_buf_relink(sc, bf);
+               if (!edma) {
+                       ath_rx_buf_relink(sc, bf, flush);
                        if (!flush)
                                ath9k_hw_rxena(ah);
+               } else if (!flush) {
+                       ath_rx_edma_buf_link(sc, qtype);
                }
 
                if (!budget--)
index b1fd3fa84983df0e0b85d86b86b02c158883fd35..f1bbce3f7774ee16a9e29b8bb62f72ade1a9d99f 100644 (file)
 #define AR_D_QCUMASK         0x000003FF
 #define AR_D_QCUMASK_RESV0   0xFFFFFC00
 
-#define AR_D_TXBLK_CMD  0x1038
-#define AR_D_TXBLK_DATA(i) (AR_D_TXBLK_CMD+(i))
-
 #define AR_D0_LCL_IFS     0x1040
 #define AR_D1_LCL_IFS     0x1044
 #define AR_D2_LCL_IFS     0x1048
index 4c8cdb097b6599c08816a4e184537d764bc9ef43..f8ded84b7be8c5e3b6038910a8829364a5e5ba1e 100644 (file)
@@ -1707,7 +1707,9 @@ found:
        return 0;
 }
 
-static void carl9170_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static void carl9170_op_flush(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif,
+                             u32 queues, bool drop)
 {
        struct ar9170 *ar = hw->priv;
        unsigned int vid;
index ca115f33746f228bc4f9007e0397b567f644e2a8..f35c7f30f9a6f66f234a4816260d0ea473fc573e 100644 (file)
@@ -1076,8 +1076,14 @@ static int carl9170_usb_probe(struct usb_interface *intf,
 
        carl9170_set_state(ar, CARL9170_STOPPED);
 
-       return request_firmware_nowait(THIS_MODULE, 1, CARL9170FW_NAME,
+       err = request_firmware_nowait(THIS_MODULE, 1, CARL9170FW_NAME,
                &ar->udev->dev, GFP_KERNEL, ar, carl9170_usb_firmware_step2);
+       if (err) {
+               usb_put_dev(udev);
+               usb_put_dev(udev);
+               carl9170_free(ar);
+       }
+       return err;
 }
 
 static void carl9170_usb_disconnect(struct usb_interface *intf)
index a1a69c5db409262e3f63a1f1246d6590e448cbfa..650be79c7ac97070f0fccd62cea916ce56d8c097 100644 (file)
@@ -73,9 +73,52 @@ static const struct radar_types etsi_radar_types_v15 = {
        .radar_types            = etsi_radar_ref_types_v15,
 };
 
-/* for now, we support ETSI radar types, FCC and JP are TODO */
+#define FCC_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB)      \
+{                                                              \
+       ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX),               \
+       PMIN - PRI_TOLERANCE,                                   \
+       PMAX * PRF + PRI_TOLERANCE, PRF, PPB * PRF,             \
+       PPB_THRESH(PPB), PRI_TOLERANCE,                         \
+}
+
+static const struct radar_detector_specs fcc_radar_ref_types[] = {
+       FCC_PATTERN(0, 0, 1, 1428, 1428, 1, 18),
+       FCC_PATTERN(1, 0, 5, 150, 230, 1, 23),
+       FCC_PATTERN(2, 6, 10, 200, 500, 1, 16),
+       FCC_PATTERN(3, 11, 20, 200, 500, 1, 12),
+       FCC_PATTERN(4, 50, 100, 1000, 2000, 20, 1),
+       FCC_PATTERN(5, 0, 1, 333, 333, 1, 9),
+};
+
+static const struct radar_types fcc_radar_types = {
+       .region                 = NL80211_DFS_FCC,
+       .num_radar_types        = ARRAY_SIZE(fcc_radar_ref_types),
+       .radar_types            = fcc_radar_ref_types,
+};
+
+#define JP_PATTERN FCC_PATTERN
+static const struct radar_detector_specs jp_radar_ref_types[] = {
+       JP_PATTERN(0, 0, 1, 1428, 1428, 1, 18),
+       JP_PATTERN(1, 2, 3, 3846, 3846, 1, 18),
+       JP_PATTERN(2, 0, 1, 1388, 1388, 1, 18),
+       JP_PATTERN(3, 1, 2, 4000, 4000, 1, 18),
+       JP_PATTERN(4, 0, 5, 150, 230, 1, 23),
+       JP_PATTERN(5, 6, 10, 200, 500, 1, 16),
+       JP_PATTERN(6, 11, 20, 200, 500, 1, 12),
+       JP_PATTERN(7, 50, 100, 1000, 2000, 20, 1),
+       JP_PATTERN(5, 0, 1, 333, 333, 1, 9),
+};
+
+static const struct radar_types jp_radar_types = {
+       .region                 = NL80211_DFS_JP,
+       .num_radar_types        = ARRAY_SIZE(jp_radar_ref_types),
+       .radar_types            = jp_radar_ref_types,
+};
+
 static const struct radar_types *dfs_domains[] = {
        &etsi_radar_types_v15,
+       &fcc_radar_types,
+       &jp_radar_types,
 };
 
 /**
index 7bf0ef8a1f56f176e14cf0a7905410a0593362a3..63986931829eb6cd76ecf04e467cab30523a460f 100644 (file)
@@ -2068,7 +2068,7 @@ static void wcn36xx_smd_rsp_process(struct wcn36xx *wcn, void *buf, size_t len)
                if (!msg_ind)
                        goto nomem;
                msg_ind->msg_len = len;
-               msg_ind->msg = kmalloc(len, GFP_KERNEL);
+               msg_ind->msg = kmemdup(buf, len, GFP_KERNEL);
                if (!msg_ind->msg) {
                        kfree(msg_ind);
 nomem:
@@ -2080,7 +2080,6 @@ nomem:
                                    msg_header->msg_type);
                        break;
                }
-               memcpy(msg_ind->msg, buf, len);
                mutex_lock(&wcn->hal_ind_mutex);
                list_add_tail(&msg_ind->list, &wcn->hal_ind_queue);
                queue_work(wcn->hal_ind_wq, &wcn->hal_ind_work);
index 4806a49cb61b139e0250e8152e27c91b79b50a0e..820d4ebd93222b6e3a7b28dd68d1d590e2b97a7b 100644 (file)
@@ -172,7 +172,7 @@ static int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
 
 static int wil_cfg80211_get_station(struct wiphy *wiphy,
                                    struct net_device *ndev,
-                                   u8 *mac, struct station_info *sinfo)
+                                   const u8 *mac, struct station_info *sinfo)
 {
        struct wil6210_priv *wil = wiphy_to_wil(wiphy);
        int rc;
@@ -288,6 +288,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
        }
 
        wil->scan_request = request;
+       mod_timer(&wil->scan_timer, jiffies + WIL6210_SCAN_TO);
 
        memset(&cmd, 0, sizeof(cmd));
        cmd.cmd.num_channels = 0;
@@ -671,7 +672,7 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
 }
 
 static int wil_cfg80211_del_station(struct wiphy *wiphy,
-                                   struct net_device *dev, u8 *mac)
+                                   struct net_device *dev, const u8 *mac)
 {
        struct wil6210_priv *wil = wiphy_to_wil(wiphy);
 
index ecdabe4adec3bb59b90cce9f00571edf2ca5bad6..8d4bc4bfb6643a64609b2e06c3b303fa72fc27fa 100644 (file)
@@ -35,7 +35,7 @@ static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil,
        void __iomem *x = wmi_addr(wil, vring->hwtail);
 
        seq_printf(s, "VRING %s = {\n", name);
-       seq_printf(s, "  pa     = 0x%016llx\n", (unsigned long long)vring->pa);
+       seq_printf(s, "  pa     = %pad\n", &vring->pa);
        seq_printf(s, "  va     = 0x%p\n", vring->va);
        seq_printf(s, "  size   = %d\n", vring->size);
        seq_printf(s, "  swtail = %d\n", vring->swtail);
@@ -473,7 +473,7 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
                           u[0], u[1], u[2], u[3]);
                seq_printf(s, "  DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n",
                           u[4], u[5], u[6], u[7]);
-               seq_printf(s, "  SKB = %p\n", skb);
+               seq_printf(s, "  SKB = 0x%p\n", skb);
 
                if (skb) {
                        skb_get(skb);
index 5824cd41e4bac6d387087ab84739df3225b8f799..73593aa3cd9813e2bd6849b969427f83f1a9a578 100644 (file)
@@ -338,7 +338,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
        }
 
        if (isr)
-               wil_err(wil, "un-handled MISC ISR bits 0x%08x\n", isr);
+               wil_dbg_irq(wil, "un-handled MISC ISR bits 0x%08x\n", isr);
 
        wil->isr_misc = 0;
 
index 95f4efe9ef37c652722a5ca381af4529a6ea65e9..11e6d9d22eae47faa34a51c813238f2acd1a1eea 100644 (file)
@@ -81,7 +81,7 @@ static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)
        memset(&sta->stats, 0, sizeof(sta->stats));
 }
 
-static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
+static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
 {
        int cid = -ENOENT;
        struct net_device *ndev = wil_to_ndev(wil);
@@ -150,6 +150,15 @@ static void wil_connect_timer_fn(ulong x)
        schedule_work(&wil->disconnect_worker);
 }
 
+static void wil_scan_timer_fn(ulong x)
+{
+       struct wil6210_priv *wil = (void *)x;
+
+       clear_bit(wil_status_fwready, &wil->status);
+       wil_err(wil, "Scan timeout detected, start fw error recovery\n");
+       schedule_work(&wil->fw_error_worker);
+}
+
 static void wil_fw_error_worker(struct work_struct *work)
 {
        struct wil6210_priv *wil = container_of(work,
@@ -161,12 +170,30 @@ static void wil_fw_error_worker(struct work_struct *work)
        if (no_fw_recovery)
                return;
 
+       /* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO
+        * passed since last recovery attempt
+        */
+       if (time_is_after_jiffies(wil->last_fw_recovery +
+                                 WIL6210_FW_RECOVERY_TO))
+               wil->recovery_count++;
+       else
+               wil->recovery_count = 1; /* fw was alive for a long time */
+
+       if (wil->recovery_count > WIL6210_FW_RECOVERY_RETRIES) {
+               wil_err(wil, "too many recovery attempts (%d), giving up\n",
+                       wil->recovery_count);
+               return;
+       }
+
+       wil->last_fw_recovery = jiffies;
+
        mutex_lock(&wil->mutex);
        switch (wdev->iftype) {
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_MONITOR:
-               wil_info(wil, "fw error recovery started...\n");
+               wil_info(wil, "fw error recovery started (try %d)...\n",
+                        wil->recovery_count);
                wil_reset(wil);
 
                /* need to re-allocate Rx ring after reset */
@@ -230,6 +257,7 @@ int wil_priv_init(struct wil6210_priv *wil)
 
        wil->pending_connect_cid = -1;
        setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil);
+       setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil);
 
        INIT_WORK(&wil->connect_worker, wil_connect_worker);
        INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker);
@@ -249,10 +277,12 @@ int wil_priv_init(struct wil6210_priv *wil)
                return -EAGAIN;
        }
 
+       wil->last_fw_recovery = jiffies;
+
        return 0;
 }
 
-void wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
+void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
 {
        del_timer_sync(&wil->connect_timer);
        _wil6210_disconnect(wil, bssid);
@@ -260,6 +290,7 @@ void wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
 
 void wil_priv_deinit(struct wil6210_priv *wil)
 {
+       del_timer_sync(&wil->scan_timer);
        cancel_work_sync(&wil->disconnect_worker);
        cancel_work_sync(&wil->fw_error_worker);
        mutex_lock(&wil->mutex);
@@ -363,8 +394,8 @@ static int wil_wait_for_fw_ready(struct wil6210_priv *wil)
                wil_err(wil, "Firmware not ready\n");
                return -ETIME;
        } else {
-               wil_dbg_misc(wil, "FW ready after %d ms\n",
-                            jiffies_to_msecs(to-left));
+               wil_info(wil, "FW ready after %d ms. HW version 0x%08x\n",
+                        jiffies_to_msecs(to-left), wil->hw_version);
        }
        return 0;
 }
@@ -391,6 +422,7 @@ int wil_reset(struct wil6210_priv *wil)
        if (wil->scan_request) {
                wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
                             wil->scan_request);
+               del_timer_sync(&wil->scan_timer);
                cfg80211_scan_done(wil->scan_request, true);
                wil->scan_request = NULL;
        }
@@ -520,6 +552,7 @@ static int __wil_down(struct wil6210_priv *wil)
        napi_disable(&wil->napi_tx);
 
        if (wil->scan_request) {
+               del_timer_sync(&wil->scan_timer);
                cfg80211_scan_done(wil->scan_request, true);
                wil->scan_request = NULL;
        }
index fdcaeb820e75857469ab4d0a6d854cdb4d6aa373..106b6dcb773a35fe3e57eba85423f4e2135ae24c 100644 (file)
@@ -32,12 +32,26 @@ static int wil_stop(struct net_device *ndev)
        return wil_down(wil);
 }
 
+static int wil_change_mtu(struct net_device *ndev, int new_mtu)
+{
+       struct wil6210_priv *wil = ndev_to_wil(ndev);
+
+       if (new_mtu < 68 || new_mtu > IEEE80211_MAX_DATA_LEN_DMG)
+               return -EINVAL;
+
+       wil_dbg_misc(wil, "change MTU %d -> %d\n", ndev->mtu, new_mtu);
+       ndev->mtu = new_mtu;
+
+       return 0;
+}
+
 static const struct net_device_ops wil_netdev_ops = {
        .ndo_open               = wil_open,
        .ndo_stop               = wil_stop,
        .ndo_start_xmit         = wil_start_xmit,
        .ndo_set_mac_address    = eth_mac_addr,
        .ndo_validate_addr      = eth_validate_addr,
+       .ndo_change_mtu         = wil_change_mtu,
 };
 
 static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget)
index f1e1bb338d681e71c96b130363bf04387c3ffa85..1e2e07b9d13d9d6b2957849b4792679c77a6dcf5 100644 (file)
@@ -74,8 +74,6 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
        if (rc)
                goto release_irq;
 
-       wil_info(wil, "HW version: 0x%08x\n", wil->hw_version);
-
        return 0;
 
  release_irq:
@@ -140,7 +138,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                goto err_release_reg;
        }
        /* rollback to err_iounmap */
-       dev_info(&pdev->dev, "CSR at %pR -> %p\n", &pdev->resource[0], csr);
+       dev_info(&pdev->dev, "CSR at %pR -> 0x%p\n", &pdev->resource[0], csr);
 
        wil = wil_if_alloc(dev, csr);
        if (IS_ERR(wil)) {
index d04629fe053f5e2f864bc1342ebdadf144d17008..747ae127587763d0bbdd652f674749960cadb1f8 100644 (file)
@@ -49,10 +49,17 @@ static void wil_release_reorder_frames(struct wil6210_priv *wil,
 {
        int index;
 
-       while (seq_less(r->head_seq_num, hseq)) {
+       /* note: this function is never called with
+        * hseq preceding r->head_seq_num, i.e it is always true
+        * !seq_less(hseq, r->head_seq_num)
+        * and thus on loop exit it should be
+        * r->head_seq_num == hseq
+        */
+       while (seq_less(r->head_seq_num, hseq) && r->stored_mpdu_num) {
                index = reorder_index(r, r->head_seq_num);
                wil_release_reorder_frame(wil, r, index);
        }
+       r->head_seq_num = hseq;
 }
 
 static void wil_reorder_release(struct wil6210_priv *wil,
@@ -91,6 +98,22 @@ void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb)
 
        spin_lock(&r->reorder_lock);
 
+       /** Due to the race between WMI events, where BACK establishment
+        * reported, and data Rx, few packets may be pass up before reorder
+        * buffer get allocated. Catch up by pretending SSN is what we
+        * see in the 1-st Rx packet
+        */
+       if (r->first_time) {
+               r->first_time = false;
+               if (seq != r->head_seq_num) {
+                       wil_err(wil, "Error: 1-st frame with wrong sequence"
+                               " %d, should be %d. Fixing...\n", seq,
+                               r->head_seq_num);
+                       r->head_seq_num = seq;
+                       r->ssn = seq;
+               }
+       }
+
        /* frame with out of date sequence number */
        if (seq_less(seq, r->head_seq_num)) {
                dev_kfree_skb(skb);
@@ -162,6 +185,7 @@ struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
        r->head_seq_num = ssn;
        r->buf_size = size;
        r->stored_mpdu_num = 0;
+       r->first_time = true;
        return r;
 }
 
index c8c547457eb4f73dd5751df4324c5b41f18971e2..0784ef3d4ce2795b68091cef9353e47d7712239b 100644 (file)
@@ -64,6 +64,22 @@ static inline int wil_vring_avail_tx(struct vring *vring)
        return vring->size - used - 1;
 }
 
+/**
+ * wil_vring_wmark_low - low watermark for available descriptor space
+ */
+static inline int wil_vring_wmark_low(struct vring *vring)
+{
+       return vring->size/8;
+}
+
+/**
+ * wil_vring_wmark_high - high watermark for available descriptor space
+ */
+static inline int wil_vring_wmark_high(struct vring *vring)
+{
+       return vring->size/4;
+}
+
 static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
 {
        struct device *dev = wil_to_dev(wil);
@@ -98,8 +114,8 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
                _d->dma.status = TX_DMA_STATUS_DU;
        }
 
-       wil_dbg_misc(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size,
-                    vring->va, (unsigned long long)vring->pa, vring->ctx);
+       wil_dbg_misc(wil, "vring[%d] 0x%p:%pad 0x%p\n", vring->size,
+                    vring->va, &vring->pa, vring->ctx);
 
        return 0;
 }
@@ -880,8 +896,8 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        pa = dma_map_single(dev, skb->data,
                        skb_headlen(skb), DMA_TO_DEVICE);
 
-       wil_dbg_txrx(wil, "Tx skb %d bytes %p -> %#08llx\n", skb_headlen(skb),
-                    skb->data, (unsigned long long)pa);
+       wil_dbg_txrx(wil, "Tx skb %d bytes 0x%p -> %pad\n", skb_headlen(skb),
+                    skb->data, &pa);
        wil_hex_dump_txrx("Tx ", DUMP_PREFIX_OFFSET, 16, 1,
                          skb->data, skb_headlen(skb), false);
 
@@ -1007,7 +1023,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
        rc = wil_tx_vring(wil, vring, skb);
 
        /* do we still have enough room in the vring? */
-       if (wil_vring_avail_tx(vring) < vring->size/8)
+       if (wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring))
                netif_tx_stop_all_queues(wil_to_ndev(wil));
 
        switch (rc) {
@@ -1116,7 +1132,7 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
                        done++;
                }
        }
-       if (wil_vring_avail_tx(vring) > vring->size/4)
+       if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring))
                netif_tx_wake_all_queues(wil_to_ndev(wil));
 
        return done;
index 2a2dec75f02606c9ea71ecd95d846287520157d5..e25edc52398fed91bce54cb0b3fb723a9d1ff443 100644 (file)
@@ -35,11 +35,14 @@ 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_TX_RING_SIZE   (512)
 #define WIL6210_MAX_TX_RINGS   (24) /* HW limit */
 #define WIL6210_MAX_CID                (8) /* HW limit */
 #define WIL6210_NAPI_BUDGET    (16) /* arbitrary */
 #define WIL6210_ITR_TRSH       (10000) /* arbitrary - about 15 IRQs/msec */
+#define WIL6210_FW_RECOVERY_RETRIES    (5) /* try to recover this many times */
+#define WIL6210_FW_RECOVERY_TO msecs_to_jiffies(5000)
+#define WIL6210_SCAN_TO                msecs_to_jiffies(10000)
 
 /* Hardware definitions begin */
 
@@ -301,6 +304,7 @@ struct wil_tid_ampdu_rx {
        u16 buf_size;
        u16 timeout;
        u8 dialog_token;
+       bool first_time; /* is it 1-st time this buffer used? */
 };
 
 struct wil6210_stats {
@@ -360,6 +364,8 @@ struct wil6210_priv {
        u32 fw_version;
        u32 hw_version;
        u8 n_mids; /* number of additional MIDs as reported by FW */
+       int recovery_count; /* num of FW recovery attempts in a short time */
+       unsigned long last_fw_recovery; /* jiffies of last fw recovery */
        /* profile */
        u32 monitor_flags;
        u32 secure_pcp; /* create secure PCP? */
@@ -381,6 +387,7 @@ struct wil6210_priv {
        struct work_struct disconnect_worker;
        struct work_struct fw_error_worker;     /* for FW error recovery */
        struct timer_list connect_timer;
+       struct timer_list scan_timer; /* detect scan timeout */
        int pending_connect_cid;
        struct list_head pending_wmi_ev;
        /*
@@ -507,7 +514,7 @@ void wil_wdev_free(struct wil6210_priv *wil);
 int wmi_set_mac_address(struct wil6210_priv *wil, void *addr);
 int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan);
 int wmi_pcp_stop(struct wil6210_priv *wil);
-void wil6210_disconnect(struct wil6210_priv *wil, void *bssid);
+void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid);
 
 int wil_rx_init(struct wil6210_priv *wil);
 void wil_rx_fini(struct wil6210_priv *wil);
index 2ba56eef0c457d4397c27fa84a1291b011d3884f..6cc0e182cc703c1f0c8ad243a008f8f6129590a6 100644 (file)
@@ -192,7 +192,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
        might_sleep();
 
        if (!test_bit(wil_status_fwready, &wil->status)) {
-               wil_err(wil, "FW not ready\n");
+               wil_err(wil, "WMI: cannot send command while FW not ready\n");
                return -EAGAIN;
        }
 
@@ -276,8 +276,8 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)
        wil->fw_version = le32_to_cpu(evt->sw_version);
        wil->n_mids = evt->numof_additional_mids;
 
-       wil_dbg_wmi(wil, "FW ver. %d; MAC %pM; %d MID's\n", wil->fw_version,
-                   evt->mac, wil->n_mids);
+       wil_info(wil, "FW ver. %d; MAC %pM; %d MID's\n", wil->fw_version,
+                evt->mac, wil->n_mids);
 
        if (!is_valid_ether_addr(ndev->dev_addr)) {
                memcpy(ndev->dev_addr, evt->mac, ETH_ALEN);
@@ -290,7 +290,7 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)
 static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d,
                             int len)
 {
-       wil_dbg_wmi(wil, "WMI: FW ready\n");
+       wil_dbg_wmi(wil, "WMI: got FW ready event\n");
 
        set_bit(wil_status_fwready, &wil->status);
        /* reuse wmi_ready for the firmware ready indication */
@@ -348,9 +348,10 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id,
 {
        if (wil->scan_request) {
                struct wmi_scan_complete_event *data = d;
-               bool aborted = (data->status != 0);
+               bool aborted = (data->status != WMI_SCAN_SUCCESS);
 
                wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", data->status);
+               del_timer_sync(&wil->scan_timer);
                cfg80211_scan_done(wil->scan_request, aborted);
                wil->scan_request = NULL;
        } else {
@@ -658,21 +659,27 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
        u8 *cmd;
        void __iomem *src;
        ulong flags;
+       unsigned n;
 
        if (!test_bit(wil_status_reset_done, &wil->status)) {
                wil_err(wil, "Reset not completed\n");
                return;
        }
 
-       for (;;) {
+       for (n = 0;; n++) {
                u16 len;
 
                r->head = ioread32(wil->csr + HOST_MBOX +
                                   offsetof(struct wil6210_mbox_ctl, rx.head));
-               if (r->tail == r->head)
+               if (r->tail == r->head) {
+                       if (n == 0)
+                               wil_dbg_wmi(wil, "No events?\n");
                        return;
+               }
 
-               /* read cmd from tail */
+               wil_dbg_wmi(wil, "Mbox head %08x tail %08x\n",
+                           r->head, r->tail);
+               /* read cmd descriptor from tail */
                wil_memcpy_fromio_32(&d_tail, wil->csr + HOSTADDR(r->tail),
                                     sizeof(struct wil6210_mbox_ring_desc));
                if (d_tail.sync == 0) {
@@ -680,13 +687,18 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
                        return;
                }
 
+               /* read cmd header from descriptor */
                if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) {
                        wil_err(wil, "Mbox evt at 0x%08x?\n",
                                le32_to_cpu(d_tail.addr));
                        return;
                }
-
                len = le16_to_cpu(hdr.len);
+               wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n",
+                           le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type),
+                           hdr.flags);
+
+               /* read cmd buffer from descriptor */
                src = wmi_buffer(wil, d_tail.addr) +
                      sizeof(struct wil6210_mbox_hdr);
                evt = kmalloc(ALIGN(offsetof(struct pending_wmi_event,
@@ -702,9 +714,6 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
                iowrite32(0, wil->csr + HOSTADDR(r->tail) +
                          offsetof(struct wil6210_mbox_ring_desc, sync));
                /* indicate */
-               wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n",
-                           le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type),
-                           hdr.flags);
                if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) &&
                    (len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
                        struct wil6210_mbox_hdr_wmi *wmi = &evt->event.wmi;
@@ -734,6 +743,8 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
                        wil_dbg_wmi(wil, "queue_work -> %d\n", q);
                }
        }
+       if (n > 1)
+               wil_dbg_wmi(wil, "%s -> %d events processed\n", __func__, n);
 }
 
 int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
@@ -802,6 +813,7 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan)
                .network_type = wmi_nettype,
                .disable_sec_offload = 1,
                .channel = chan - 1,
+               .pcp_max_assoc_sta = WIL6210_MAX_CID,
        };
        struct {
                struct wil6210_mbox_hdr_wmi wmi;
index 50b8528394f4050b8098098e9bbdd0520ae1bf2a..17334c852866c3cc8a061eecb829052ebd9415cf 100644 (file)
@@ -28,7 +28,7 @@
 #define __WILOCITY_WMI_H__
 
 /* General */
-
+#define WILOCITY_MAX_ASSOC_STA (8)
 #define WMI_MAC_LEN            (6)
 #define WMI_PROX_RANGE_NUM     (3)
 
@@ -219,15 +219,6 @@ struct wmi_disconnect_sta_cmd {
        __le16 disconnect_reason;
 } __packed;
 
-/*
- * WMI_RECONNECT_CMDID
- */
-struct wmi_reconnect_cmd {
-       u8 channel;                     /* hint */
-       u8 reserved;
-       u8 bssid[WMI_MAC_LEN];          /* mandatory if set */
-} __packed;
-
 
 /*
  * WMI_SET_PMK_CMDID
@@ -296,11 +287,13 @@ enum wmi_scan_type {
        WMI_LONG_SCAN           = 0,
        WMI_SHORT_SCAN          = 1,
        WMI_PBC_SCAN            = 2,
+       WMI_ACTIVE_SCAN         = 3,
+       WMI_DIRECT_SCAN         = 4,
 };
 
 struct wmi_start_scan_cmd {
-       u8 reserved[8];
-
+       u8 direct_scan_mac_addr[6];
+       u8 reserved[2];
        __le32 home_dwell_time; /* Max duration in the home channel(ms) */
        __le32 force_scan_interval;     /* Time interval between scans (ms)*/
        u8 scan_type;           /* wmi_scan_type */
@@ -332,6 +325,7 @@ struct wmi_probed_ssid_cmd {
        u8 ssid[WMI_MAX_SSID_LEN];
 } __packed;
 
+
 /*
  * WMI_SET_APPIE_CMDID
  * Add Application specified IE to a management frame
@@ -427,7 +421,7 @@ struct wmi_bcon_ctrl_cmd {
        __le16 frag_num;
        __le64 ss_mask;
        u8 network_type;
-       u8 reserved;
+       u8 pcp_max_assoc_sta;
        u8 disable_sec_offload;
        u8 disable_sec;
 } __packed;
@@ -450,7 +444,7 @@ enum wmi_port_role {
 struct wmi_port_allocate_cmd {
        u8 mac[WMI_MAC_LEN];
        u8 port_role;
-       u8 midid;
+       u8 mid;
 } __packed;
 
 /*
@@ -467,6 +461,7 @@ struct wmi_delete_port_cmd {
 enum wmi_discovery_mode {
        WMI_DISCOVERY_MODE_NON_OFFLOAD  = 0,
        WMI_DISCOVERY_MODE_OFFLOAD      = 1,
+       WMI_DISCOVERY_MODE_PEER2PEER    = 2,
 };
 
 struct wmi_p2p_cfg_cmd {
@@ -493,7 +488,8 @@ struct wmi_power_mgmt_cfg_cmd {
  */
 struct wmi_pcp_start_cmd {
        __le16 bcon_interval;
-       u8 reserved0[10];
+       u8 pcp_max_assoc_sta;
+       u8 reserved0[9];
        u8 network_type;
        u8 channel;
        u8 disable_sec_offload;
@@ -857,6 +853,7 @@ enum wmi_event_id {
        WMI_RF_MGMT_STATUS_EVENTID              = 0x1853,
        WMI_BF_SM_MGMT_DONE_EVENTID             = 0x1838,
        WMI_RX_MGMT_PACKET_EVENTID              = 0x1840,
+       WMI_TX_MGMT_PACKET_EVENTID              = 0x1841,
 
        /* Performance monitoring events */
        WMI_DATA_PORT_OPEN_EVENTID              = 0x1860,
@@ -1040,16 +1037,23 @@ enum wmi_disconnect_reason {
 struct wmi_disconnect_event {
        __le16 protocol_reason_status;  /* reason code, see 802.11 spec. */
        u8 bssid[WMI_MAC_LEN];          /* set if known */
-       u8 disconnect_reason;           /* see wmi_disconnect_reason_e */
-       u8 assoc_resp_len;
-       u8 assoc_info[0];
+       u8 disconnect_reason;           /* see wmi_disconnect_reason */
+       u8 assoc_resp_len;              /* not in use */
+       u8 assoc_info[0];               /* not in use */
 } __packed;
 
 /*
  * WMI_SCAN_COMPLETE_EVENTID
  */
+enum scan_status {
+       WMI_SCAN_SUCCESS        = 0,
+       WMI_SCAN_FAILED         = 1,
+       WMI_SCAN_ABORTED        = 2,
+       WMI_SCAN_REJECTED       = 3,
+};
+
 struct wmi_scan_complete_event {
-       __le32 status;
+       __le32 status;  /* scan_status */
 } __packed;
 
 /*
@@ -1256,6 +1260,14 @@ struct wmi_rx_mgmt_info {
        u8 channel;     /* From Radio MNGR */
 } __packed;
 
+
+/*
+ * WMI_TX_MGMT_PACKET_EVENTID
+ */
+struct wmi_tx_mgmt_packet_event {
+       u8 payload[0];
+} __packed;
+
 struct wmi_rx_mgmt_packet_event {
        struct wmi_rx_mgmt_info info;
        u8 payload[0];
index 088d544ec63f940b2a7b2234f1eac9b7458a19ce..e3f67b8d3f8003d546867b51648d25fda81d0f15 100644 (file)
@@ -1,7 +1,8 @@
 config B43
        tristate "Broadcom 43xx wireless support (mac80211 stack)"
-       depends on SSB_POSSIBLE && MAC80211 && HAS_DMA
-       select SSB
+       depends on (BCMA_POSSIBLE || SSB_POSSIBLE) && MAC80211 && HAS_DMA
+       select BCMA if B43_BCMA
+       select SSB if B43_SSB
        select FW_LOADER
        ---help---
          b43 is a driver for the Broadcom 43xx series wireless devices.
@@ -27,14 +28,33 @@ config B43
          If unsure, say M.
 
 config B43_BCMA
-       bool "Support for BCMA bus"
-       depends on B43 && (BCMA = y || BCMA = B43)
-       default y
+       bool
 
 config B43_SSB
        bool
-       depends on B43 && (SSB = y || SSB = B43)
-       default y
+
+choice
+       prompt "Supported bus types"
+       depends on B43
+       default B43_BCMA_AND_SSB
+
+config B43_BUSES_BCMA_AND_SSB
+       bool "BCMA and SSB"
+       depends on BCMA_POSSIBLE && SSB_POSSIBLE
+       select B43_BCMA
+       select B43_SSB
+
+config B43_BUSES_BCMA
+       bool "BCMA only"
+       depends on BCMA_POSSIBLE
+       select B43_BCMA
+
+config B43_BUSES_SSB
+       bool "SSB only"
+       depends on SSB_POSSIBLE
+       select B43_SSB
+
+endchoice
 
 # Auto-select SSB PCI-HOST support, if possible
 config B43_PCI_AUTOSELECT
@@ -53,7 +73,7 @@ config B43_PCICORE_AUTOSELECT
 
 config B43_PCMCIA
        bool "Broadcom 43xx PCMCIA device support"
-       depends on B43 && SSB_PCMCIAHOST_POSSIBLE
+       depends on B43 && B43_SSB && SSB_PCMCIAHOST_POSSIBLE
        select SSB_PCMCIAHOST
        ---help---
          Broadcom 43xx PCMCIA device support.
@@ -73,7 +93,7 @@ config B43_PCMCIA
 
 config B43_SDIO
        bool "Broadcom 43xx SDIO device support"
-       depends on B43 && SSB_SDIOHOST_POSSIBLE
+       depends on B43 && B43_SSB && SSB_SDIOHOST_POSSIBLE
        select SSB_SDIOHOST
        ---help---
          Broadcom 43xx device support for Soft-MAC SDIO devices.
@@ -98,7 +118,7 @@ config B43_BCMA_PIO
 
 config B43_PIO
        bool
-       depends on B43
+       depends on B43 && B43_SSB
        select SSB_BLOCKIO
        default y
 
@@ -116,7 +136,7 @@ config B43_PHY_N
 
 config B43_PHY_LP
        bool "Support for low-power (LP-PHY) devices"
-       depends on B43
+       depends on B43 && B43_SSB
        default y
        ---help---
          Support for the LP-PHY.
index 54376fddfaf9f11fef6e4a7a0ae5554d5e427f4c..4113b69347640359aa22020eb0dcfd7efe0b7c14 100644 (file)
@@ -915,10 +915,6 @@ struct b43_wl {
        char rng_name[30 + 1];
 #endif /* CONFIG_B43_HWRNG */
 
-       /* List of all wireless devices on this chip */
-       struct list_head devlist;
-       u8 nr_devs;
-
        bool radiotap_enabled;
        bool radio_enabled;
 
index 184c95659279070f2a64edfa6628f109ded51f22..f3205c6988bc4354a14a5226011f762868253936 100644 (file)
@@ -5,7 +5,9 @@ enum b43_bus_type {
 #ifdef CONFIG_B43_BCMA
        B43_BUS_BCMA,
 #endif
+#ifdef CONFIG_B43_SSB
        B43_BUS_SSB,
+#endif
 };
 
 struct b43_bus_dev {
@@ -52,13 +54,21 @@ struct b43_bus_dev {
 
 static inline bool b43_bus_host_is_pcmcia(struct b43_bus_dev *dev)
 {
+#ifdef CONFIG_B43_SSB
        return (dev->bus_type == B43_BUS_SSB &&
                dev->sdev->bus->bustype == SSB_BUSTYPE_PCMCIA);
+#else
+       return false;
+#endif
 }
 static inline bool b43_bus_host_is_sdio(struct b43_bus_dev *dev)
 {
+#ifdef CONFIG_B43_SSB
        return (dev->bus_type == B43_BUS_SSB &&
                dev->sdev->bus->bustype == SSB_BUSTYPE_SDIO);
+#else
+       return false;
+#endif
 }
 
 struct b43_bus_dev *b43_bus_dev_bcma_init(struct bcma_device *core);
index 69fc3d65531a7eeb88c4901f804ab197dbbb939e..32538ac5f7e4c0e821d5669329c7d2bd692c7ddd 100644 (file)
@@ -182,7 +182,7 @@ static struct ieee80211_rate __b43_ratetable[] = {
 #define b43_g_ratetable                (__b43_ratetable + 0)
 #define b43_g_ratetable_size   12
 
-#define CHAN4G(_channel, _freq, _flags) {                      \
+#define CHAN2G(_channel, _freq, _flags) {                      \
        .band                   = IEEE80211_BAND_2GHZ,          \
        .center_freq            = (_freq),                      \
        .hw_value               = (_channel),                   \
@@ -191,23 +191,31 @@ static struct ieee80211_rate __b43_ratetable[] = {
        .max_power              = 30,                           \
 }
 static struct ieee80211_channel b43_2ghz_chantable[] = {
-       CHAN4G(1, 2412, 0),
-       CHAN4G(2, 2417, 0),
-       CHAN4G(3, 2422, 0),
-       CHAN4G(4, 2427, 0),
-       CHAN4G(5, 2432, 0),
-       CHAN4G(6, 2437, 0),
-       CHAN4G(7, 2442, 0),
-       CHAN4G(8, 2447, 0),
-       CHAN4G(9, 2452, 0),
-       CHAN4G(10, 2457, 0),
-       CHAN4G(11, 2462, 0),
-       CHAN4G(12, 2467, 0),
-       CHAN4G(13, 2472, 0),
-       CHAN4G(14, 2484, 0),
+       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),
 };
-#undef CHAN4G
+#undef CHAN2G
 
+#define CHAN4G(_channel, _flags) {                             \
+       .band                   = IEEE80211_BAND_5GHZ,          \
+       .center_freq            = 4000 + (5 * (_channel)),      \
+       .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)),      \
@@ -217,6 +225,18 @@ static struct ieee80211_channel b43_2ghz_chantable[] = {
        .max_power              = 30,                           \
 }
 static struct ieee80211_channel b43_5ghz_nphy_chantable[] = {
+       CHAN4G(184, 0),         CHAN4G(186, 0),
+       CHAN4G(188, 0),         CHAN4G(190, 0),
+       CHAN4G(192, 0),         CHAN4G(194, 0),
+       CHAN4G(196, 0),         CHAN4G(198, 0),
+       CHAN4G(200, 0),         CHAN4G(202, 0),
+       CHAN4G(204, 0),         CHAN4G(206, 0),
+       CHAN4G(208, 0),         CHAN4G(210, 0),
+       CHAN4G(212, 0),         CHAN4G(214, 0),
+       CHAN4G(216, 0),         CHAN4G(218, 0),
+       CHAN4G(220, 0),         CHAN4G(222, 0),
+       CHAN4G(224, 0),         CHAN4G(226, 0),
+       CHAN4G(228, 0),
        CHAN5G(32, 0),          CHAN5G(34, 0),
        CHAN5G(36, 0),          CHAN5G(38, 0),
        CHAN5G(40, 0),          CHAN5G(42, 0),
@@ -260,18 +280,7 @@ static struct ieee80211_channel b43_5ghz_nphy_chantable[] = {
        CHAN5G(170, 0),         CHAN5G(172, 0),
        CHAN5G(174, 0),         CHAN5G(176, 0),
        CHAN5G(178, 0),         CHAN5G(180, 0),
-       CHAN5G(182, 0),         CHAN5G(184, 0),
-       CHAN5G(186, 0),         CHAN5G(188, 0),
-       CHAN5G(190, 0),         CHAN5G(192, 0),
-       CHAN5G(194, 0),         CHAN5G(196, 0),
-       CHAN5G(198, 0),         CHAN5G(200, 0),
-       CHAN5G(202, 0),         CHAN5G(204, 0),
-       CHAN5G(206, 0),         CHAN5G(208, 0),
-       CHAN5G(210, 0),         CHAN5G(212, 0),
-       CHAN5G(214, 0),         CHAN5G(216, 0),
-       CHAN5G(218, 0),         CHAN5G(220, 0),
-       CHAN5G(222, 0),         CHAN5G(224, 0),
-       CHAN5G(226, 0),         CHAN5G(228, 0),
+       CHAN5G(182, 0),
 };
 
 static struct ieee80211_channel b43_5ghz_aphy_chantable[] = {
@@ -295,6 +304,7 @@ static struct ieee80211_channel b43_5ghz_aphy_chantable[] = {
        CHAN5G(208, 0),         CHAN5G(212, 0),
        CHAN5G(216, 0),
 };
+#undef CHAN4G
 #undef CHAN5G
 
 static struct ieee80211_supported_band b43_band_5GHz_nphy = {
@@ -1175,18 +1185,7 @@ static void b43_bcma_phy_reset(struct b43_wldev *dev)
        bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, flags);
        udelay(2);
 
-       /* Take PHY out of reset */
-       flags = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
-       flags &= ~B43_BCMA_IOCTL_PHY_RESET;
-       flags |= BCMA_IOCTL_FGC;
-       bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, flags);
-       udelay(1);
-
-       /* Do not force clock anymore */
-       flags = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
-       flags &= ~BCMA_IOCTL_FGC;
-       bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, flags);
-       udelay(1);
+       b43_phy_take_out_of_reset(dev);
 }
 
 static void b43_bcma_wireless_core_reset(struct b43_wldev *dev, bool gmode)
@@ -1195,18 +1194,22 @@ static void b43_bcma_wireless_core_reset(struct b43_wldev *dev, bool gmode)
                  B43_BCMA_CLKCTLST_PHY_PLL_REQ;
        u32 status = B43_BCMA_CLKCTLST_80211_PLL_ST |
                     B43_BCMA_CLKCTLST_PHY_PLL_ST;
+       u32 flags;
+
+       flags = B43_BCMA_IOCTL_PHY_CLKEN;
+       if (gmode)
+               flags |= B43_BCMA_IOCTL_GMODE;
+       b43_device_enable(dev, flags);
 
-       b43_device_enable(dev, B43_BCMA_IOCTL_PHY_CLKEN);
        bcma_core_set_clockmode(dev->dev->bdev, BCMA_CLKMODE_FAST);
        b43_bcma_phy_reset(dev);
        bcma_core_pll_ctl(dev->dev->bdev, req, status, true);
 }
 #endif
 
+#ifdef CONFIG_B43_SSB
 static void b43_ssb_wireless_core_reset(struct b43_wldev *dev, bool gmode)
 {
-       struct ssb_device *sdev = dev->dev->sdev;
-       u32 tmslow;
        u32 flags = 0;
 
        if (gmode)
@@ -1218,18 +1221,9 @@ static void b43_ssb_wireless_core_reset(struct b43_wldev *dev, bool gmode)
        b43_device_enable(dev, flags);
        msleep(2);              /* Wait for the PLL to turn on. */
 
-       /* Now take the PHY out of Reset again */
-       tmslow = ssb_read32(sdev, SSB_TMSLOW);
-       tmslow |= SSB_TMSLOW_FGC;
-       tmslow &= ~B43_TMSLOW_PHYRESET;
-       ssb_write32(sdev, SSB_TMSLOW, tmslow);
-       ssb_read32(sdev, SSB_TMSLOW);   /* flush */
-       msleep(1);
-       tmslow &= ~SSB_TMSLOW_FGC;
-       ssb_write32(sdev, SSB_TMSLOW, tmslow);
-       ssb_read32(sdev, SSB_TMSLOW);   /* flush */
-       msleep(1);
+       b43_phy_take_out_of_reset(dev);
 }
+#endif
 
 void b43_wireless_core_reset(struct b43_wldev *dev, bool gmode)
 {
@@ -2704,32 +2698,37 @@ static int b43_upload_initvals(struct b43_wldev *dev)
        struct b43_firmware *fw = &dev->fw;
        const struct b43_iv *ivals;
        size_t count;
-       int err;
 
        hdr = (const struct b43_fw_header *)(fw->initvals.data->data);
        ivals = (const struct b43_iv *)(fw->initvals.data->data + hdr_len);
        count = be32_to_cpu(hdr->size);
-       err = b43_write_initvals(dev, ivals, count,
+       return b43_write_initvals(dev, ivals, count,
                                 fw->initvals.data->size - hdr_len);
-       if (err)
-               goto out;
-       if (fw->initvals_band.data) {
-               hdr = (const struct b43_fw_header *)(fw->initvals_band.data->data);
-               ivals = (const struct b43_iv *)(fw->initvals_band.data->data + hdr_len);
-               count = be32_to_cpu(hdr->size);
-               err = b43_write_initvals(dev, ivals, count,
-                                        fw->initvals_band.data->size - hdr_len);
-               if (err)
-                       goto out;
-       }
-out:
+}
 
-       return err;
+static int b43_upload_initvals_band(struct b43_wldev *dev)
+{
+       const size_t hdr_len = sizeof(struct b43_fw_header);
+       const struct b43_fw_header *hdr;
+       struct b43_firmware *fw = &dev->fw;
+       const struct b43_iv *ivals;
+       size_t count;
+
+       if (!fw->initvals_band.data)
+               return 0;
+
+       hdr = (const struct b43_fw_header *)(fw->initvals_band.data->data);
+       ivals = (const struct b43_iv *)(fw->initvals_band.data->data + hdr_len);
+       count = be32_to_cpu(hdr->size);
+       return b43_write_initvals(dev, ivals, count,
+                                 fw->initvals_band.data->size - hdr_len);
 }
 
 /* Initialize the GPIOs
  * http://bcm-specs.sipsolutions.net/GPIO
  */
+
+#ifdef CONFIG_B43_SSB
 static struct ssb_device *b43_ssb_gpio_dev(struct b43_wldev *dev)
 {
        struct ssb_bus *bus = dev->dev->sdev->bus;
@@ -2740,10 +2739,13 @@ static struct ssb_device *b43_ssb_gpio_dev(struct b43_wldev *dev)
        return bus->chipco.dev;
 #endif
 }
+#endif
 
 static int b43_gpio_init(struct b43_wldev *dev)
 {
+#ifdef CONFIG_B43_SSB
        struct ssb_device *gpiodev;
+#endif
        u32 mask, set;
 
        b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_GPOUTSMSK, 0);
@@ -2802,7 +2804,9 @@ static int b43_gpio_init(struct b43_wldev *dev)
 /* Turn off all GPIO stuff. Call this on module unload, for example. */
 static void b43_gpio_cleanup(struct b43_wldev *dev)
 {
+#ifdef CONFIG_B43_SSB
        struct ssb_device *gpiodev;
+#endif
 
        switch (dev->dev->bus_type) {
 #ifdef CONFIG_B43_BCMA
@@ -3086,6 +3090,10 @@ static int b43_chip_init(struct b43_wldev *dev)
        if (err)
                goto err_gpio_clean;
 
+       err = b43_upload_initvals_band(dev);
+       if (err)
+               goto err_gpio_clean;
+
        /* Turn the Analog on and initialize the PHY. */
        phy->ops->switch_analog(dev, 1);
        err = b43_phy_init(dev);
@@ -3685,37 +3693,6 @@ static void b43_op_set_tsf(struct ieee80211_hw *hw,
        mutex_unlock(&wl->mutex);
 }
 
-static void b43_put_phy_into_reset(struct b43_wldev *dev)
-{
-       u32 tmp;
-
-       switch (dev->dev->bus_type) {
-#ifdef CONFIG_B43_BCMA
-       case B43_BUS_BCMA:
-               b43err(dev->wl,
-                      "Putting PHY into reset not supported on BCMA\n");
-               break;
-#endif
-#ifdef CONFIG_B43_SSB
-       case B43_BUS_SSB:
-               tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW);
-               tmp &= ~B43_TMSLOW_GMODE;
-               tmp |= B43_TMSLOW_PHYRESET;
-               tmp |= SSB_TMSLOW_FGC;
-               ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp);
-               msleep(1);
-
-               tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW);
-               tmp &= ~SSB_TMSLOW_FGC;
-               tmp |= B43_TMSLOW_PHYRESET;
-               ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp);
-               msleep(1);
-
-               break;
-#endif
-       }
-}
-
 static const char *band_to_string(enum ieee80211_band band)
 {
        switch (band) {
@@ -3731,94 +3708,75 @@ static const char *band_to_string(enum ieee80211_band band)
 }
 
 /* Expects wl->mutex locked */
-static int b43_switch_band(struct b43_wl *wl, struct ieee80211_channel *chan)
+static int b43_switch_band(struct b43_wldev *dev,
+                          struct ieee80211_channel *chan)
 {
-       struct b43_wldev *up_dev = NULL;
-       struct b43_wldev *down_dev;
-       struct b43_wldev *d;
-       int err;
-       bool uninitialized_var(gmode);
-       int prev_status;
+       struct b43_phy *phy = &dev->phy;
+       bool gmode;
+       u32 tmp;
 
-       /* Find a device and PHY which supports the band. */
-       list_for_each_entry(d, &wl->devlist, list) {
-               switch (chan->band) {
-               case IEEE80211_BAND_5GHZ:
-                       if (d->phy.supports_5ghz) {
-                               up_dev = d;
-                               gmode = false;
-                       }
-                       break;
-               case IEEE80211_BAND_2GHZ:
-                       if (d->phy.supports_2ghz) {
-                               up_dev = d;
-                               gmode = true;
-                       }
-                       break;
-               default:
-                       B43_WARN_ON(1);
-                       return -EINVAL;
-               }
-               if (up_dev)
-                       break;
+       switch (chan->band) {
+       case IEEE80211_BAND_5GHZ:
+               gmode = false;
+               break;
+       case IEEE80211_BAND_2GHZ:
+               gmode = true;
+               break;
+       default:
+               B43_WARN_ON(1);
+               return -EINVAL;
        }
-       if (!up_dev) {
-               b43err(wl, "Could not find a device for %s-GHz band operation\n",
+
+       if (!((gmode && phy->supports_2ghz) ||
+             (!gmode && phy->supports_5ghz))) {
+               b43err(dev->wl, "This device doesn't support %s-GHz band\n",
                       band_to_string(chan->band));
                return -ENODEV;
        }
-       if ((up_dev == wl->current_dev) &&
-           (!!wl->current_dev->phy.gmode == !!gmode)) {
+
+       if (!!phy->gmode == !!gmode) {
                /* This device is already running. */
                return 0;
        }
-       b43dbg(wl, "Switching to %s-GHz band\n",
+
+       b43dbg(dev->wl, "Switching to %s GHz band\n",
               band_to_string(chan->band));
-       down_dev = wl->current_dev;
 
-       prev_status = b43_status(down_dev);
-       /* Shutdown the currently running core. */
-       if (prev_status >= B43_STAT_STARTED)
-               down_dev = b43_wireless_core_stop(down_dev);
-       if (prev_status >= B43_STAT_INITIALIZED)
-               b43_wireless_core_exit(down_dev);
+       /* Some new devices don't need disabling radio for band switching */
+       if (!(phy->type == B43_PHYTYPE_N && phy->rev >= 3))
+               b43_software_rfkill(dev, true);
 
-       if (down_dev != up_dev) {
-               /* We switch to a different core, so we put PHY into
-                * RESET on the old core. */
-               b43_put_phy_into_reset(down_dev);
+       phy->gmode = gmode;
+       b43_phy_put_into_reset(dev);
+       switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+       case B43_BUS_BCMA:
+               tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
+               if (gmode)
+                       tmp |= B43_BCMA_IOCTL_GMODE;
+               else
+                       tmp &= ~B43_BCMA_IOCTL_GMODE;
+               bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp);
+               break;
+#endif
+#ifdef CONFIG_B43_SSB
+       case B43_BUS_SSB:
+               tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW);
+               if (gmode)
+                       tmp |= B43_TMSLOW_GMODE;
+               else
+                       tmp &= ~B43_TMSLOW_GMODE;
+               ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp);
+               break;
+#endif
        }
+       b43_phy_take_out_of_reset(dev);
 
-       /* Now start the new core. */
-       up_dev->phy.gmode = gmode;
-       if (prev_status >= B43_STAT_INITIALIZED) {
-               err = b43_wireless_core_init(up_dev);
-               if (err) {
-                       b43err(wl, "Fatal: Could not initialize device for "
-                              "selected %s-GHz band\n",
-                              band_to_string(chan->band));
-                       goto init_failure;
-               }
-       }
-       if (prev_status >= B43_STAT_STARTED) {
-               err = b43_wireless_core_start(up_dev);
-               if (err) {
-                       b43err(wl, "Fatal: Could not start device for "
-                              "selected %s-GHz band\n",
-                              band_to_string(chan->band));
-                       b43_wireless_core_exit(up_dev);
-                       goto init_failure;
-               }
-       }
-       B43_WARN_ON(b43_status(up_dev) != prev_status);
+       b43_upload_initvals_band(dev);
 
-       wl->current_dev = up_dev;
+       b43_phy_init(dev);
 
        return 0;
-init_failure:
-       /* Whoops, failed to init the new core. No core is operating now. */
-       wl->current_dev = NULL;
-       return err;
 }
 
 /* Write the short and long frame retry limit values. */
@@ -3851,8 +3809,10 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
 
        dev = wl->current_dev;
 
+       b43_mac_suspend(dev);
+
        /* Switch the band (if necessary). This might change the active core. */
-       err = b43_switch_band(wl, conf->chandef.chan);
+       err = b43_switch_band(dev, conf->chandef.chan);
        if (err)
                goto out_unlock_mutex;
 
@@ -3871,8 +3831,6 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
        else
                phy->is_40mhz = false;
 
-       b43_mac_suspend(dev);
-
        if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
                b43_set_retry_limits(dev, conf->short_frame_max_tx_count,
                                          conf->long_frame_max_tx_count);
@@ -4582,8 +4540,12 @@ static void b43_imcfglo_timeouts_workaround(struct b43_wldev *dev)
        struct ssb_bus *bus;
        u32 tmp;
 
+#ifdef CONFIG_B43_SSB
        if (dev->dev->bus_type != B43_BUS_SSB)
                return;
+#else
+       return;
+#endif
 
        bus = dev->dev->sdev->bus;
 
@@ -4738,7 +4700,7 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
        }
        if (sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW)
                hf |= B43_HF_DSCRQ; /* Disable slowclock requests from ucode. */
-#ifdef CONFIG_SSB_DRIVER_PCICORE
+#if defined(CONFIG_B43_SSB) && defined(CONFIG_SSB_DRIVER_PCICORE)
        if (dev->dev->bus_type == B43_BUS_SSB &&
            dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI &&
            dev->dev->sdev->bus->pcicore.dev->id.revision <= 10)
@@ -5129,10 +5091,82 @@ static void b43_wireless_core_detach(struct b43_wldev *dev)
        b43_phy_free(dev);
 }
 
+static void b43_supported_bands(struct b43_wldev *dev, bool *have_2ghz_phy,
+                               bool *have_5ghz_phy)
+{
+       u16 dev_id = 0;
+
+#ifdef CONFIG_B43_BCMA
+       if (dev->dev->bus_type == B43_BUS_BCMA &&
+           dev->dev->bdev->bus->hosttype == BCMA_HOSTTYPE_PCI)
+               dev_id = dev->dev->bdev->bus->host_pci->device;
+#endif
+#ifdef CONFIG_B43_SSB
+       if (dev->dev->bus_type == B43_BUS_SSB &&
+           dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI)
+               dev_id = dev->dev->sdev->bus->host_pci->device;
+#endif
+       /* Override with SPROM value if available */
+       if (dev->dev->bus_sprom->dev_id)
+               dev_id = dev->dev->bus_sprom->dev_id;
+
+       /* Note: below IDs can be "virtual" (not maching e.g. real PCI ID) */
+       switch (dev_id) {
+       case 0x4324: /* BCM4306 */
+       case 0x4312: /* BCM4311 */
+       case 0x4319: /* BCM4318 */
+       case 0x4328: /* BCM4321 */
+       case 0x432b: /* BCM4322 */
+       case 0x4350: /* BCM43222 */
+       case 0x4353: /* BCM43224 */
+       case 0x0576: /* BCM43224 */
+       case 0x435f: /* BCM6362 */
+       case 0x4331: /* BCM4331 */
+       case 0x4359: /* BCM43228 */
+       case 0x43a0: /* BCM4360 */
+       case 0x43b1: /* BCM4352 */
+               /* Dual band devices */
+               *have_2ghz_phy = true;
+               *have_5ghz_phy = true;
+               return;
+       case 0x4321: /* BCM4306 */
+       case 0x4313: /* BCM4311 */
+       case 0x431a: /* BCM4318 */
+       case 0x432a: /* BCM4321 */
+       case 0x432d: /* BCM4322 */
+       case 0x4352: /* BCM43222 */
+       case 0x4333: /* BCM4331 */
+       case 0x43a2: /* BCM4360 */
+       case 0x43b3: /* BCM4352 */
+               /* 5 GHz only devices */
+               *have_2ghz_phy = false;
+               *have_5ghz_phy = true;
+               return;
+       }
+
+       /* As a fallback, try to guess using PHY type */
+       switch (dev->phy.type) {
+       case B43_PHYTYPE_A:
+               *have_2ghz_phy = false;
+               *have_5ghz_phy = true;
+               return;
+       case B43_PHYTYPE_G:
+       case B43_PHYTYPE_N:
+       case B43_PHYTYPE_LP:
+       case B43_PHYTYPE_HT:
+       case B43_PHYTYPE_LCN:
+               *have_2ghz_phy = true;
+               *have_5ghz_phy = false;
+               return;
+       }
+
+       B43_WARN_ON(1);
+}
+
 static int b43_wireless_core_attach(struct b43_wldev *dev)
 {
        struct b43_wl *wl = dev->wl;
-       struct pci_dev *pdev = NULL;
+       struct b43_phy *phy = &dev->phy;
        int err;
        u32 tmp;
        bool have_2ghz_phy = false, have_5ghz_phy = false;
@@ -5144,19 +5178,15 @@ static int b43_wireless_core_attach(struct b43_wldev *dev)
         * that in core_init(), too.
         */
 
-#ifdef CONFIG_B43_SSB
-       if (dev->dev->bus_type == B43_BUS_SSB &&
-           dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI)
-               pdev = dev->dev->sdev->bus->host_pci;
-#endif
-
        err = b43_bus_powerup(dev, 0);
        if (err) {
                b43err(wl, "Bus powerup failed\n");
                goto out;
        }
 
-       /* Get the PHY type. */
+       phy->do_full_init = true;
+
+       /* Try to guess supported bands for the first init needs */
        switch (dev->dev->bus_type) {
 #ifdef CONFIG_B43_BCMA
        case B43_BUS_BCMA:
@@ -5178,51 +5208,31 @@ static int b43_wireless_core_attach(struct b43_wldev *dev)
        }
 
        dev->phy.gmode = have_2ghz_phy;
-       dev->phy.radio_on = true;
        b43_wireless_core_reset(dev, dev->phy.gmode);
 
+       /* Get the PHY type. */
        err = b43_phy_versioning(dev);
        if (err)
                goto err_powerdown;
-       /* Check if this device supports multiband. */
-       if (!pdev ||
-           (pdev->device != 0x4312 &&
-            pdev->device != 0x4319 && pdev->device != 0x4324)) {
-               /* No multiband support. */
-               have_2ghz_phy = false;
+
+       /* Get real info about supported bands */
+       b43_supported_bands(dev, &have_2ghz_phy, &have_5ghz_phy);
+
+       /* We don't support 5 GHz on some PHYs yet */
+       switch (dev->phy.type) {
+       case B43_PHYTYPE_A:
+       case B43_PHYTYPE_N:
+       case B43_PHYTYPE_LP:
+       case B43_PHYTYPE_HT:
+               b43warn(wl, "5 GHz band is unsupported on this PHY\n");
                have_5ghz_phy = false;
-               switch (dev->phy.type) {
-               case B43_PHYTYPE_A:
-                       have_5ghz_phy = true;
-                       break;
-               case B43_PHYTYPE_LP: //FIXME not always!
-#if 0 //FIXME enabling 5GHz causes a NULL pointer dereference
-                       have_5ghz_phy = 1;
-#endif
-               case B43_PHYTYPE_G:
-               case B43_PHYTYPE_N:
-               case B43_PHYTYPE_HT:
-               case B43_PHYTYPE_LCN:
-                       have_2ghz_phy = true;
-                       break;
-               default:
-                       B43_WARN_ON(1);
-               }
        }
-       if (dev->phy.type == B43_PHYTYPE_A) {
-               /* FIXME */
-               b43err(wl, "IEEE 802.11a devices are unsupported\n");
+
+       if (!have_2ghz_phy && !have_5ghz_phy) {
+               b43err(wl, "b43 can't support any band on this device\n");
                err = -EOPNOTSUPP;
                goto err_powerdown;
        }
-       if (1 /* disable A-PHY */) {
-               /* FIXME: For now we disable the A-PHY on multi-PHY devices. */
-               if (dev->phy.type != B43_PHYTYPE_N &&
-                   dev->phy.type != B43_PHYTYPE_LP) {
-                       have_2ghz_phy = true;
-                       have_5ghz_phy = false;
-               }
-       }
 
        err = b43_phy_allocate(dev);
        if (err)
@@ -5270,7 +5280,6 @@ static void b43_one_core_detach(struct b43_bus_dev *dev)
        b43_debugfs_remove_device(wldev);
        b43_wireless_core_detach(wldev);
        list_del(&wldev->list);
-       wl->nr_devs--;
        b43_bus_set_wldev(dev, NULL);
        kfree(wldev);
 }
@@ -5295,8 +5304,6 @@ static int b43_one_core_attach(struct b43_bus_dev *dev, struct b43_wl *wl)
        if (err)
                goto err_kfree_wldev;
 
-       list_add(&wldev->list, &wl->devlist);
-       wl->nr_devs++;
        b43_bus_set_wldev(dev, wldev);
        b43_debugfs_add_device(wldev);
 
@@ -5314,6 +5321,7 @@ static int b43_one_core_attach(struct b43_bus_dev *dev, struct b43_wl *wl)
        (pdev->subsystem_vendor == PCI_VENDOR_ID_##_subvendor) &&       \
        (pdev->subsystem_device == _subdevice)                          )
 
+#ifdef CONFIG_B43_SSB
 static void b43_sprom_fixup(struct ssb_bus *bus)
 {
        struct pci_dev *pdev;
@@ -5345,6 +5353,7 @@ static void b43_wireless_exit(struct b43_bus_dev *dev, struct b43_wl *wl)
        ssb_set_devtypedata(dev->sdev, NULL);
        ieee80211_free_hw(hw);
 }
+#endif
 
 static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev)
 {
@@ -5386,7 +5395,6 @@ static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev)
        wl->hw = hw;
        mutex_init(&wl->mutex);
        spin_lock_init(&wl->hardirq_lock);
-       INIT_LIST_HEAD(&wl->devlist);
        INIT_WORK(&wl->beacon_update_trigger, b43_beacon_update_trigger_work);
        INIT_WORK(&wl->txpower_adjust_work, b43_phy_txpower_adjust_work);
        INIT_WORK(&wl->tx_work, b43_tx_work);
@@ -5486,39 +5494,42 @@ int b43_ssb_probe(struct ssb_device *sdev, const struct ssb_device_id *id)
        struct b43_bus_dev *dev;
        struct b43_wl *wl;
        int err;
-       int first = 0;
 
        dev = b43_bus_dev_ssb_init(sdev);
        if (!dev)
                return -ENOMEM;
 
        wl = ssb_get_devtypedata(sdev);
-       if (!wl) {
-               /* Probing the first core. Must setup common struct b43_wl */
-               first = 1;
-               b43_sprom_fixup(sdev->bus);
-               wl = b43_wireless_init(dev);
-               if (IS_ERR(wl)) {
-                       err = PTR_ERR(wl);
-                       goto out;
-               }
-               ssb_set_devtypedata(sdev, wl);
-               B43_WARN_ON(ssb_get_devtypedata(sdev) != wl);
+       if (wl) {
+               b43err(NULL, "Dual-core devices are not supported\n");
+               err = -ENOTSUPP;
+               goto err_ssb_kfree_dev;
+       }
+
+       b43_sprom_fixup(sdev->bus);
+
+       wl = b43_wireless_init(dev);
+       if (IS_ERR(wl)) {
+               err = PTR_ERR(wl);
+               goto err_ssb_kfree_dev;
        }
+       ssb_set_devtypedata(sdev, wl);
+       B43_WARN_ON(ssb_get_devtypedata(sdev) != wl);
+
        err = b43_one_core_attach(dev, wl);
        if (err)
-               goto err_wireless_exit;
+               goto err_ssb_wireless_exit;
 
        /* setup and start work to load firmware */
        INIT_WORK(&wl->firmware_load, b43_request_firmware);
        schedule_work(&wl->firmware_load);
 
-      out:
        return err;
 
-      err_wireless_exit:
-       if (first)
-               b43_wireless_exit(dev, wl);
+err_ssb_wireless_exit:
+       b43_wireless_exit(dev, wl);
+err_ssb_kfree_dev:
+       kfree(dev);
        return err;
 }
 
@@ -5546,13 +5557,8 @@ static void b43_ssb_remove(struct ssb_device *sdev)
        /* Unregister HW RNG driver */
        b43_rng_exit(wl);
 
-       if (list_empty(&wl->devlist)) {
-               b43_leds_unregister(wl);
-               /* Last core on the chip unregistered.
-                * We can destroy common struct b43_wl.
-                */
-               b43_wireless_exit(dev, wl);
-       }
+       b43_leds_unregister(wl);
+       b43_wireless_exit(dev, wl);
 }
 
 static struct ssb_driver b43_ssb_driver = {
index dbaa51890198945b7552ed506bc3c8bfe4ba2359..08244b3b327e5f98f06b5d5db8943d8e624bb3a8 100644 (file)
@@ -96,12 +96,16 @@ int b43_phy_init(struct b43_wldev *dev)
 
        phy->channel = ops->get_default_chan(dev);
 
-       ops->software_rfkill(dev, false);
+       phy->ops->switch_analog(dev, true);
+       b43_software_rfkill(dev, false);
+
        err = ops->init(dev);
        if (err) {
                b43err(dev->wl, "PHY init failed\n");
                goto err_block_rf;
        }
+       phy->do_full_init = false;
+
        /* Make sure to switch hardware and firmware (SHM) to
         * the default channel. */
        err = b43_switch_channel(dev, ops->get_default_chan(dev));
@@ -113,10 +117,11 @@ int b43_phy_init(struct b43_wldev *dev)
        return 0;
 
 err_phy_exit:
+       phy->do_full_init = true;
        if (ops->exit)
                ops->exit(dev);
 err_block_rf:
-       ops->software_rfkill(dev, true);
+       b43_software_rfkill(dev, true);
 
        return err;
 }
@@ -125,7 +130,8 @@ void b43_phy_exit(struct b43_wldev *dev)
 {
        const struct b43_phy_operations *ops = dev->phy.ops;
 
-       ops->software_rfkill(dev, true);
+       b43_software_rfkill(dev, true);
+       dev->phy.do_full_init = true;
        if (ops->exit)
                ops->exit(dev);
 }
@@ -312,6 +318,90 @@ void b43_phy_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set)
        }
 }
 
+void b43_phy_put_into_reset(struct b43_wldev *dev)
+{
+       u32 tmp;
+
+       switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+       case B43_BUS_BCMA:
+               tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
+               tmp &= ~B43_BCMA_IOCTL_GMODE;
+               tmp |= B43_BCMA_IOCTL_PHY_RESET;
+               tmp |= BCMA_IOCTL_FGC;
+               bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp);
+               udelay(1);
+
+               tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
+               tmp &= ~BCMA_IOCTL_FGC;
+               bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp);
+               udelay(1);
+               break;
+#endif
+#ifdef CONFIG_B43_SSB
+       case B43_BUS_SSB:
+               tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW);
+               tmp &= ~B43_TMSLOW_GMODE;
+               tmp |= B43_TMSLOW_PHYRESET;
+               tmp |= SSB_TMSLOW_FGC;
+               ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp);
+               usleep_range(1000, 2000);
+
+               tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW);
+               tmp &= ~SSB_TMSLOW_FGC;
+               ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp);
+               usleep_range(1000, 2000);
+
+               break;
+#endif
+       }
+}
+
+void b43_phy_take_out_of_reset(struct b43_wldev *dev)
+{
+       u32 tmp;
+
+       switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+       case B43_BUS_BCMA:
+               /* Unset reset bit (with forcing clock) */
+               tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
+               tmp &= ~B43_BCMA_IOCTL_PHY_RESET;
+               tmp &= ~B43_BCMA_IOCTL_PHY_CLKEN;
+               tmp |= BCMA_IOCTL_FGC;
+               bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp);
+               udelay(1);
+
+               /* Do not force clock anymore */
+               tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
+               tmp &= ~BCMA_IOCTL_FGC;
+               tmp |= B43_BCMA_IOCTL_PHY_CLKEN;
+               bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp);
+               udelay(1);
+               break;
+#endif
+#ifdef CONFIG_B43_SSB
+       case B43_BUS_SSB:
+               /* Unset reset bit (with forcing clock) */
+               tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW);
+               tmp &= ~B43_TMSLOW_PHYRESET;
+               tmp &= ~B43_TMSLOW_PHYCLKEN;
+               tmp |= SSB_TMSLOW_FGC;
+               ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp);
+               ssb_read32(dev->dev->sdev, SSB_TMSLOW); /* flush */
+               usleep_range(1000, 2000);
+
+               tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW);
+               tmp &= ~SSB_TMSLOW_FGC;
+               tmp |= B43_TMSLOW_PHYCLKEN;
+               ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp);
+               ssb_read32(dev->dev->sdev, SSB_TMSLOW); /* flush */
+               usleep_range(1000, 2000);
+               break;
+#endif
+       }
+}
+
 int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel)
 {
        struct b43_phy *phy = &(dev->phy);
index f1b999349876bbfc8cad799858434f5e64a14b37..4ad6240d9ff40e53557b4d29b27788b3eb9602d5 100644 (file)
@@ -231,9 +231,12 @@ struct b43_phy {
        /* HT info */
        bool is_40mhz;
 
-       /* GMODE bit enabled? */
+       /* Is GMODE (2 GHz mode) bit enabled? */
        bool gmode;
 
+       /* After power reset full init has to be performed */
+       bool do_full_init;
+
        /* Analog Type */
        u8 analog;
        /* B43_PHYTYPE_ */
@@ -390,6 +393,9 @@ void b43_phy_lock(struct b43_wldev *dev);
  */
 void b43_phy_unlock(struct b43_wldev *dev);
 
+void b43_phy_put_into_reset(struct b43_wldev *dev);
+void b43_phy_take_out_of_reset(struct b43_wldev *dev);
+
 /**
  * b43_switch_channel - Switch to another channel
  */
index 12f467b8d564f1c5cdca06acbdf65ca8f80b48c8..8f5c14bc10e6fedcab2d7b88a0009470772dad8a 100644 (file)
@@ -1587,6 +1587,7 @@ static void b43_phy_initb5(struct b43_wldev *dev)
        b43_write16(dev, 0x03E4, (b43_read16(dev, 0x03E4) & 0xFFC0) | 0x0004);
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/Init/B6 */
 static void b43_phy_initb6(struct b43_wldev *dev)
 {
        struct b43_phy *phy = &dev->phy;
@@ -1670,7 +1671,7 @@ static void b43_phy_initb6(struct b43_wldev *dev)
                b43_radio_write16(dev, 0x50, 0x20);
        }
        if (phy->radio_rev <= 2) {
-               b43_radio_write16(dev, 0x7C, 0x20);
+               b43_radio_write16(dev, 0x50, 0x20);
                b43_radio_write16(dev, 0x5A, 0x70);
                b43_radio_write16(dev, 0x5B, 0x7B);
                b43_radio_write16(dev, 0x5C, 0xB0);
@@ -1686,9 +1687,8 @@ static void b43_phy_initb6(struct b43_wldev *dev)
                b43_phy_write(dev, 0x2A, 0x8AC0);
        b43_phy_write(dev, 0x0038, 0x0668);
        b43_set_txpower_g(dev, &gphy->bbatt, &gphy->rfatt, gphy->tx_control);
-       if (phy->radio_rev <= 5) {
+       if (phy->radio_rev == 4 || phy->radio_rev == 5)
                b43_phy_maskset(dev, 0x5D, 0xFF80, 0x0003);
-       }
        if (phy->radio_rev <= 2)
                b43_radio_write16(dev, 0x005D, 0x000D);
 
index 24ccbe96e0c8a0723bb165b0f32677ab6ba7ded5..86569f6a870507c1723d95ccc6d39a2c13f449a2 100644 (file)
@@ -257,6 +257,72 @@ static void b43_nphy_rf_ctl_override(struct b43_wldev *dev, u16 field,
        }
 }
 
+static void b43_nphy_rf_ctl_intc_override_rev7(struct b43_wldev *dev,
+                                              enum n_intc_override intc_override,
+                                              u16 value, u8 core_sel)
+{
+       u16 reg, tmp, tmp2, val;
+       int core;
+
+       for (core = 0; core < 2; core++) {
+               if ((core_sel == 1 && core != 0) ||
+                   (core_sel == 2 && core != 1))
+                       continue;
+
+               reg = (core == 0) ? B43_NPHY_RFCTL_INTC1 : B43_NPHY_RFCTL_INTC2;
+
+               switch (intc_override) {
+               case N_INTC_OVERRIDE_OFF:
+                       b43_phy_write(dev, reg, 0);
+                       b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX);
+                       break;
+               case N_INTC_OVERRIDE_TRSW:
+                       b43_phy_maskset(dev, reg, ~0xC0, value << 6);
+                       b43_phy_set(dev, reg, 0x400);
+
+                       b43_phy_mask(dev, 0x2ff, ~0xC000 & 0xFFFF);
+                       b43_phy_set(dev, 0x2ff, 0x2000);
+                       b43_phy_set(dev, 0x2ff, 0x0001);
+                       break;
+               case N_INTC_OVERRIDE_PA:
+                       tmp = 0x0030;
+                       if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ)
+                               val = value << 5;
+                       else
+                               val = value << 4;
+                       b43_phy_maskset(dev, reg, ~tmp, val);
+                       b43_phy_set(dev, reg, 0x1000);
+                       break;
+               case N_INTC_OVERRIDE_EXT_LNA_PU:
+                       if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+                               tmp = 0x0001;
+                               tmp2 = 0x0004;
+                               val = value;
+                       } else {
+                               tmp = 0x0004;
+                               tmp2 = 0x0001;
+                               val = value << 2;
+                       }
+                       b43_phy_maskset(dev, reg, ~tmp, val);
+                       b43_phy_mask(dev, reg, ~tmp2);
+                       break;
+               case N_INTC_OVERRIDE_EXT_LNA_GAIN:
+                       if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+                               tmp = 0x0002;
+                               tmp2 = 0x0008;
+                               val = value << 1;
+                       } else {
+                               tmp = 0x0008;
+                               tmp2 = 0x0002;
+                               val = value << 3;
+                       }
+                       b43_phy_maskset(dev, reg, ~tmp, val);
+                       b43_phy_mask(dev, reg, ~tmp2);
+                       break;
+               }
+       }
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlIntcOverride */
 static void b43_nphy_rf_ctl_intc_override(struct b43_wldev *dev,
                                          enum n_intc_override intc_override,
@@ -265,6 +331,12 @@ static void b43_nphy_rf_ctl_intc_override(struct b43_wldev *dev,
        u8 i, j;
        u16 reg, tmp, val;
 
+       if (dev->phy.rev >= 7) {
+               b43_nphy_rf_ctl_intc_override_rev7(dev, intc_override, value,
+                                                  core);
+               return;
+       }
+
        B43_WARN_ON(dev->phy.rev < 3);
 
        for (i = 0; i < 2; i++) {
@@ -419,7 +491,8 @@ static void b43_nphy_stay_in_carrier_search(struct b43_wldev *dev, bool enable)
                static const u16 clip[] = { 0xFFFF, 0xFFFF };
                if (nphy->deaf_count++ == 0) {
                        nphy->classifier_state = b43_nphy_classifier(dev, 0, 0);
-                       b43_nphy_classifier(dev, 0x7, 0);
+                       b43_nphy_classifier(dev, 0x7,
+                                           B43_NPHY_CLASSCTL_WAITEDEN);
                        b43_nphy_read_clip_detection(dev, nphy->clip_state);
                        b43_nphy_write_clip_detection(dev, clip);
                }
@@ -627,13 +700,11 @@ static void b43_radio_2057_init_post(struct b43_wldev *dev)
        b43_radio_mask(dev, R2057_RFPLL_MISC_CAL_RESETN, ~0x78);
        b43_radio_mask(dev, R2057_XTAL_CONFIG2, ~0x80);
 
-       if (dev->phy.n->init_por) {
+       if (dev->phy.do_full_init) {
                b43_radio_2057_rcal(dev);
                b43_radio_2057_rccal(dev);
        }
        b43_radio_mask(dev, R2057_RFPLL_MASTER, ~0x8);
-
-       dev->phy.n->init_por = false;
 }
 
 /* http://bcm-v4.sipsolutions.net/802.11/Radio/2057/Init */
@@ -734,9 +805,16 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
        u16 bias, cbias;
        u16 pag_boost, padg_boost, pgag_boost, mixg_boost;
        u16 paa_boost, pada_boost, pgaa_boost, mixa_boost;
+       bool is_pkg_fab_smic;
 
        B43_WARN_ON(dev->phy.rev < 3);
 
+       is_pkg_fab_smic =
+               ((dev->dev->chip_id == BCMA_CHIP_ID_BCM43224 ||
+                 dev->dev->chip_id == BCMA_CHIP_ID_BCM43225 ||
+                 dev->dev->chip_id == BCMA_CHIP_ID_BCM43421) &&
+                dev->dev->chip_pkg == BCMA_PKG_ID_BCM43224_FAB_SMIC);
+
        b43_chantab_radio_2056_upload(dev, e);
        b2056_upload_syn_pll_cp2(dev, band == IEEE80211_BAND_5GHZ);
 
@@ -744,7 +822,8 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
            b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
                b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER1, 0x1F);
                b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER2, 0x1F);
-               if (dev->dev->chip_id == 0x4716) {
+               if (dev->dev->chip_id == BCMA_CHIP_ID_BCM4716 ||
+                   dev->dev->chip_id == BCMA_CHIP_ID_BCM47162) {
                        b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER4, 0x14);
                        b43_radio_write(dev, B2056_SYN_PLL_CP2, 0);
                } else {
@@ -752,6 +831,13 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
                        b43_radio_write(dev, B2056_SYN_PLL_CP2, 0x14);
                }
        }
+       if (sprom->boardflags2_hi & B43_BFH2_GPLL_WAR2 &&
+           b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+               b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER1, 0x1f);
+               b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER2, 0x1f);
+               b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER4, 0x0b);
+               b43_radio_write(dev, B2056_SYN_PLL_CP2, 0x20);
+       }
        if (sprom->boardflags2_lo & B43_BFL2_APLL_WAR &&
            b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
                b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER1, 0x1F);
@@ -767,7 +853,8 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
                                b43_radio_write(dev,
                                        offset | B2056_TX_PADG_IDAC, 0xcc);
 
-                               if (dev->dev->chip_id == 0x4716) {
+                               if (dev->dev->chip_id == BCMA_CHIP_ID_BCM4716 ||
+                                   dev->dev->chip_id == BCMA_CHIP_ID_BCM47162) {
                                        bias = 0x40;
                                        cbias = 0x45;
                                        pag_boost = 0x5;
@@ -776,6 +863,10 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
                                } else {
                                        bias = 0x25;
                                        cbias = 0x20;
+                                       if (is_pkg_fab_smic) {
+                                               bias = 0x2a;
+                                               cbias = 0x38;
+                                       }
                                        pag_boost = 0x4;
                                        pgag_boost = 0x03;
                                        mixg_boost = 0x65;
@@ -844,6 +935,8 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
                        mixa_boost = 0xF;
                }
 
+               cbias = is_pkg_fab_smic ? 0x35 : 0x30;
+
                for (i = 0; i < 2; i++) {
                        offset = i ? B2056_TX1 : B2056_TX0;
 
@@ -862,11 +955,11 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
                        b43_radio_write(dev,
                                offset | B2056_TX_PADA_CASCBIAS, 0x03);
                        b43_radio_write(dev,
-                               offset | B2056_TX_INTPAA_IAUX_STAT, 0x50);
+                               offset | B2056_TX_INTPAA_IAUX_STAT, 0x30);
                        b43_radio_write(dev,
-                               offset | B2056_TX_INTPAA_IMAIN_STAT, 0x50);
+                               offset | B2056_TX_INTPAA_IMAIN_STAT, 0x30);
                        b43_radio_write(dev,
-                               offset | B2056_TX_INTPAA_CASCBIAS, 0x30);
+                               offset | B2056_TX_INTPAA_CASCBIAS, cbias);
                }
        }
 
@@ -933,7 +1026,7 @@ static void b43_radio_init2056_post(struct b43_wldev *dev)
        b43_radio_mask(dev, B2056_SYN_COM_RESET, ~0x2);
        b43_radio_mask(dev, B2056_SYN_PLL_MAST2, ~0xFC);
        b43_radio_mask(dev, B2056_SYN_RCCAL_CTRL0, ~0x1);
-       if (dev->phy.n->init_por)
+       if (dev->phy.do_full_init)
                b43_radio_2056_rcal(dev);
 }
 
@@ -946,8 +1039,6 @@ static void b43_radio_init2056(struct b43_wldev *dev)
        b43_radio_init2056_pre(dev);
        b2056_upload_inittabs(dev, 0, 0);
        b43_radio_init2056_post(dev);
-
-       dev->phy.n->init_por = false;
 }
 
 /**************************************************
@@ -1164,23 +1255,20 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
        u16 seq_mode;
        u32 tmp;
 
-       if (nphy->hang_avoid)
-               b43_nphy_stay_in_carrier_search(dev, true);
+       b43_nphy_stay_in_carrier_search(dev, true);
 
        if ((nphy->bb_mult_save & 0x80000000) == 0) {
                tmp = b43_ntab_read(dev, B43_NTAB16(15, 87));
                nphy->bb_mult_save = (tmp & 0xFFFF) | 0x80000000;
        }
 
+       /* TODO: add modify_bbmult argument */
        if (!dev->phy.is_40mhz)
                tmp = 0x6464;
        else
                tmp = 0x4747;
        b43_ntab_write(dev, B43_NTAB16(15, 87), tmp);
 
-       if (nphy->hang_avoid)
-               b43_nphy_stay_in_carrier_search(dev, false);
-
        b43_phy_write(dev, B43_NPHY_SAMP_DEPCNT, (samps - 1));
 
        if (loops != 0xFFFF)
@@ -1213,6 +1301,8 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
                b43err(dev->wl, "run samples timeout\n");
 
        b43_phy_write(dev, B43_NPHY_RFSEQMODE, seq_mode);
+
+       b43_nphy_stay_in_carrier_search(dev, false);
 }
 
 /**************************************************
@@ -1588,8 +1678,8 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
        struct b43_phy_n *nphy = dev->phy.n;
 
        u16 saved_regs_phy_rfctl[2];
-       u16 saved_regs_phy[13];
-       u16 regs_to_store[] = {
+       u16 saved_regs_phy[22];
+       u16 regs_to_store_rev3[] = {
                B43_NPHY_AFECTL_OVER1, B43_NPHY_AFECTL_OVER,
                B43_NPHY_AFECTL_C1, B43_NPHY_AFECTL_C2,
                B43_NPHY_TXF_40CO_B1S1, B43_NPHY_RFCTL_OVER,
@@ -1598,6 +1688,20 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
                B43_NPHY_RFCTL_LUT_TRSW_UP1, B43_NPHY_RFCTL_LUT_TRSW_UP2,
                B43_NPHY_RFCTL_RSSIO1, B43_NPHY_RFCTL_RSSIO2
        };
+       u16 regs_to_store_rev7[] = {
+               B43_NPHY_AFECTL_OVER1, B43_NPHY_AFECTL_OVER,
+               B43_NPHY_AFECTL_C1, B43_NPHY_AFECTL_C2,
+               B43_NPHY_TXF_40CO_B1S1, B43_NPHY_RFCTL_OVER,
+               0x342, 0x343, 0x346, 0x347,
+               0x2ff,
+               B43_NPHY_TXF_40CO_B1S0, B43_NPHY_TXF_40CO_B32S1,
+               B43_NPHY_RFCTL_CMD,
+               B43_NPHY_RFCTL_LUT_TRSW_UP1, B43_NPHY_RFCTL_LUT_TRSW_UP2,
+               0x340, 0x341, 0x344, 0x345,
+               B43_NPHY_RFCTL_RSSIO1, B43_NPHY_RFCTL_RSSIO2
+       };
+       u16 *regs_to_store;
+       int regs_amount;
 
        u16 class;
 
@@ -1617,6 +1721,15 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
        u8 rx_core_state;
        int core, i, j, vcm;
 
+       if (dev->phy.rev >= 7) {
+               regs_to_store = regs_to_store_rev7;
+               regs_amount = ARRAY_SIZE(regs_to_store_rev7);
+       } else {
+               regs_to_store = regs_to_store_rev3;
+               regs_amount = ARRAY_SIZE(regs_to_store_rev3);
+       }
+       BUG_ON(regs_amount > ARRAY_SIZE(saved_regs_phy));
+
        class = b43_nphy_classifier(dev, 0, 0);
        b43_nphy_classifier(dev, 7, 4);
        b43_nphy_read_clip_detection(dev, clip_state);
@@ -1624,22 +1737,29 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
 
        saved_regs_phy_rfctl[0] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1);
        saved_regs_phy_rfctl[1] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2);
-       for (i = 0; i < ARRAY_SIZE(regs_to_store); i++)
+       for (i = 0; i < regs_amount; i++)
                saved_regs_phy[i] = b43_phy_read(dev, regs_to_store[i]);
 
        b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_OFF, 0, 7);
        b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, 1, 7);
-       b43_nphy_rf_ctl_override(dev, 0x1, 0, 0, false);
-       b43_nphy_rf_ctl_override(dev, 0x2, 1, 0, false);
-       b43_nphy_rf_ctl_override(dev, 0x80, 1, 0, false);
-       b43_nphy_rf_ctl_override(dev, 0x40, 1, 0, false);
-
-       if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
-               b43_nphy_rf_ctl_override(dev, 0x20, 0, 0, false);
-               b43_nphy_rf_ctl_override(dev, 0x10, 1, 0, false);
+
+       if (dev->phy.rev >= 7) {
+               /* TODO */
+               if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+               } else {
+               }
        } else {
-               b43_nphy_rf_ctl_override(dev, 0x10, 0, 0, false);
-               b43_nphy_rf_ctl_override(dev, 0x20, 1, 0, false);
+               b43_nphy_rf_ctl_override(dev, 0x1, 0, 0, false);
+               b43_nphy_rf_ctl_override(dev, 0x2, 1, 0, false);
+               b43_nphy_rf_ctl_override(dev, 0x80, 1, 0, false);
+               b43_nphy_rf_ctl_override(dev, 0x40, 1, 0, false);
+               if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+                       b43_nphy_rf_ctl_override(dev, 0x20, 0, 0, false);
+                       b43_nphy_rf_ctl_override(dev, 0x10, 1, 0, false);
+               } else {
+                       b43_nphy_rf_ctl_override(dev, 0x10, 0, 0, false);
+                       b43_nphy_rf_ctl_override(dev, 0x20, 1, 0, false);
+               }
        }
 
        rx_core_state = b43_nphy_get_rx_core_state(dev);
@@ -1654,8 +1774,11 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
 
                /* Grab RSSI results for every possible VCM */
                for (vcm = 0; vcm < 8; vcm++) {
-                       b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC, 0xE3,
-                                       vcm << 2);
+                       if (dev->phy.rev >= 7)
+                               ;
+                       else
+                               b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC,
+                                                 0xE3, vcm << 2);
                        b43_nphy_poll_rssi(dev, N_RSSI_NB, results[vcm], 8);
                }
 
@@ -1682,8 +1805,11 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
                }
 
                /* Select the best VCM */
-               b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC, 0xE3,
-                                 vcm_final << 2);
+               if (dev->phy.rev >= 7)
+                       ;
+               else
+                       b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC,
+                                         0xE3, vcm_final << 2);
 
                for (i = 0; i < 4; i++) {
                        if (core != i / 2)
@@ -1736,9 +1862,9 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
 
        b43_phy_set(dev, B43_NPHY_RFCTL_OVER, 0x1);
        b43_phy_set(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_RXTX);
-       b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S1, ~0x1);
+       b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, ~0x1);
 
-       for (i = 0; i < ARRAY_SIZE(regs_to_store); i++)
+       for (i = 0; i < regs_amount; i++)
                b43_phy_write(dev, regs_to_store[i], saved_regs_phy[i]);
 
        /* Store for future configuration */
@@ -2494,8 +2620,8 @@ static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev)
        struct ssb_sprom *sprom = dev->dev->bus_sprom;
 
        /* TX to RX */
-       u8 tx2rx_events[8] = { 0x4, 0x3, 0x6, 0x5, 0x2, 0x1, 0x8, 0x1F };
-       u8 tx2rx_delays[8] = { 8, 4, 2, 2, 4, 4, 6, 1 };
+       u8 tx2rx_events[7] = { 0x4, 0x3, 0x5, 0x2, 0x1, 0x8, 0x1F };
+       u8 tx2rx_delays[7] = { 8, 4, 4, 4, 4, 6, 1 };
        /* RX to TX */
        u8 rx2tx_events_ipa[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0xF, 0x3,
                                        0x1F };
@@ -2503,6 +2629,23 @@ static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev)
        u8 rx2tx_events[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0x3, 0x4, 0x1F };
        u8 rx2tx_delays[9] = { 8, 6, 6, 4, 4, 18, 42, 1, 1 };
 
+       u16 vmids[5][4] = {
+               { 0xa2, 0xb4, 0xb4, 0x89, }, /* 0 */
+               { 0xb4, 0xb4, 0xb4, 0x24, }, /* 1 */
+               { 0xa2, 0xb4, 0xb4, 0x74, }, /* 2 */
+               { 0xa2, 0xb4, 0xb4, 0x270, }, /* 3 */
+               { 0xa2, 0xb4, 0xb4, 0x00, }, /* 4 and 5 */
+       };
+       u16 gains[5][4] = {
+               { 0x02, 0x02, 0x02, 0x00, }, /* 0 */
+               { 0x02, 0x02, 0x02, 0x02, }, /* 1 */
+               { 0x02, 0x02, 0x02, 0x04, }, /* 2 */
+               { 0x02, 0x02, 0x02, 0x00, }, /* 3 */
+               { 0x02, 0x02, 0x02, 0x00, }, /* 4 and 5 */
+       };
+       u16 *vmid, *gain;
+
+       u8 pdet_range;
        u16 tmp16;
        u32 tmp32;
 
@@ -2561,7 +2704,71 @@ static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev)
        b43_ntab_write(dev, B43_NTAB16(8, 0), 2);
        b43_ntab_write(dev, B43_NTAB16(8, 16), 2);
 
-       /* TODO */
+       if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+               pdet_range = sprom->fem.ghz2.pdet_range;
+       else
+               pdet_range = sprom->fem.ghz5.pdet_range;
+       vmid = vmids[min_t(u16, pdet_range, 4)];
+       gain = gains[min_t(u16, pdet_range, 4)];
+       switch (pdet_range) {
+       case 3:
+               if (!(dev->phy.rev >= 4 &&
+                     b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ))
+                       break;
+               /* FALL THROUGH */
+       case 0:
+       case 1:
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x08), 4, vmid);
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x18), 4, vmid);
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x0c), 4, gain);
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x1c), 4, gain);
+               break;
+       case 2:
+               if (dev->phy.rev >= 6) {
+                       if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+                               vmid[3] = 0x94;
+                       else
+                               vmid[3] = 0x8e;
+                       gain[3] = 3;
+               } else if (dev->phy.rev == 5) {
+                       vmid[3] = 0x84;
+                       gain[3] = 2;
+               }
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x08), 4, vmid);
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x18), 4, vmid);
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x0c), 4, gain);
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x1c), 4, gain);
+               break;
+       case 4:
+       case 5:
+               if (b43_current_band(dev->wl) != IEEE80211_BAND_2GHZ) {
+                       if (pdet_range == 4) {
+                               vmid[3] = 0x8e;
+                               tmp16 = 0x96;
+                               gain[3] = 0x2;
+                       } else {
+                               vmid[3] = 0x89;
+                               tmp16 = 0x89;
+                               gain[3] = 0;
+                       }
+               } else {
+                       if (pdet_range == 4) {
+                               vmid[3] = 0x89;
+                               tmp16 = 0x8b;
+                               gain[3] = 0x2;
+                       } else {
+                               vmid[3] = 0x74;
+                               tmp16 = 0x70;
+                               gain[3] = 0;
+                       }
+               }
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x08), 4, vmid);
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x0c), 4, gain);
+               vmid[3] = tmp16;
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x18), 4, vmid);
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x1c), 4, gain);
+               break;
+       }
 
        b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_MAST_BIAS, 0x00);
        b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_MAST_BIAS, 0x00);
@@ -2600,7 +2807,7 @@ static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev)
        /* Dropped probably-always-true condition */
        b43_phy_write(dev, B43_NPHY_ED_CRS40ASSERTTHRESH0, 0x03eb);
        b43_phy_write(dev, B43_NPHY_ED_CRS40ASSERTTHRESH1, 0x03eb);
-       b43_phy_write(dev, B43_NPHY_ED_CRS40DEASSERTTHRESH1, 0x0341);
+       b43_phy_write(dev, B43_NPHY_ED_CRS40DEASSERTTHRESH0, 0x0341);
        b43_phy_write(dev, B43_NPHY_ED_CRS40DEASSERTTHRESH1, 0x0341);
        b43_phy_write(dev, B43_NPHY_ED_CRS20LASSERTTHRESH0, 0x042b);
        b43_phy_write(dev, B43_NPHY_ED_CRS20LASSERTTHRESH1, 0x042b);
@@ -3211,6 +3418,20 @@ static void b43_nphy_tx_prepare_adjusted_power_table(struct b43_wldev *dev)
        u8 idx, delta;
        u8 i, stf_mode;
 
+       /* Array adj_pwr_tbl corresponds to the hardware table. It consists of
+        * 21 groups, each containing 4 entries.
+        *
+        * First group has entries for CCK modulation.
+        * The rest of groups has 1 entry per modulation (SISO, CDD, STBC, SDM).
+        *
+        * Group 0 is for CCK
+        * Groups 1..4 use BPSK (group per coding rate)
+        * Groups 5..8 use QPSK (group per coding rate)
+        * Groups 9..12 use 16-QAM (group per coding rate)
+        * Groups 13..16 use 64-QAM (group per coding rate)
+        * Groups 17..20 are unknown
+        */
+
        for (i = 0; i < 4; i++)
                nphy->adj_pwr_tbl[i] = nphy->tx_power_offset[i];
 
@@ -3409,10 +3630,8 @@ static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev)
        }
 
        b43_nphy_tx_prepare_adjusted_power_table(dev);
-       /*
        b43_ntab_write_bulk(dev, B43_NTAB16(26, 64), 84, nphy->adj_pwr_tbl);
        b43_ntab_write_bulk(dev, B43_NTAB16(27, 64), 84, nphy->adj_pwr_tbl);
-       */
 
        if (nphy->hang_avoid)
                b43_nphy_stay_in_carrier_search(dev, false);
@@ -5124,7 +5343,7 @@ static int b43_phy_initn(struct b43_wldev *dev)
        b43_phy_write(dev, B43_NPHY_TXMACIF_HOLDOFF, 0x0015);
        b43_phy_write(dev, B43_NPHY_TXMACDELAY, 0x0320);
        if (phy->rev >= 3 && phy->rev <= 6)
-               b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x0014);
+               b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x0032);
        b43_nphy_tx_lp_fbw(dev);
        if (phy->rev >= 3)
                b43_nphy_spur_workaround(dev);
@@ -5338,7 +5557,6 @@ static void b43_nphy_op_prepare_structs(struct b43_wldev *dev)
        nphy->hang_avoid = (phy->rev == 3 || phy->rev == 4);
        nphy->spur_avoid = (phy->rev >= 3) ?
                                B43_SPUR_AVOID_AUTO : B43_SPUR_AVOID_DISABLE;
-       nphy->init_por = true;
        nphy->gain_boost = true; /* this way we follow wl, assume it is true */
        nphy->txrx_chain = 2; /* sth different than 0 and 1 for now */
        nphy->phyrxchain = 3; /* to avoid b43_nphy_set_rx_core_state like wl */
@@ -5379,8 +5597,6 @@ static void b43_nphy_op_prepare_structs(struct b43_wldev *dev)
                nphy->ipa2g_on = sprom->fem.ghz2.extpa_gain == 2;
                nphy->ipa5g_on = sprom->fem.ghz5.extpa_gain == 2;
        }
-
-       nphy->init_por = true;
 }
 
 static void b43_nphy_op_free(struct b43_wldev *dev)
@@ -5441,8 +5657,11 @@ static u16 b43_nphy_op_radio_read(struct b43_wldev *dev, u16 reg)
 {
        /* Register 1 is a 32-bit register. */
        B43_WARN_ON(reg == 1);
-       /* N-PHY needs 0x100 for read access */
-       reg |= 0x100;
+
+       if (dev->phy.rev >= 7)
+               reg |= 0x200; /* Radio 0x2057 */
+       else
+               reg |= 0x100;
 
        b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg);
        return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW);
@@ -5488,10 +5707,12 @@ static void b43_nphy_op_software_rfkill(struct b43_wldev *dev,
                }
        } else {
                if (dev->phy.rev >= 7) {
-                       b43_radio_2057_init(dev);
+                       if (!dev->phy.radio_on)
+                               b43_radio_2057_init(dev);
                        b43_switch_channel(dev, dev->phy.channel);
                } else if (dev->phy.rev >= 3) {
-                       b43_radio_init2056(dev);
+                       if (!dev->phy.radio_on)
+                               b43_radio_init2056(dev);
                        b43_switch_channel(dev, dev->phy.channel);
                } else {
                        b43_radio_init2055(dev);
index 9a5b6bc27d24e5657d1e2ccda530283a4dc14da7..ecfbf66dbc3b7fdacc8d3e07dbf8d1476355fd16 100644 (file)
@@ -931,7 +931,6 @@ struct b43_phy_n {
        u16 papd_epsilon_offset[2];
        s32 preamble_override;
        u32 bb_mult_save;
-       bool init_por;
 
        bool gain_boost;
        bool elna_gain_config;
index b4fd9345d673a3542d94e66260ebc685eed7ef8d..2ce25607c60d389505ee3c9e664ee503adcd5406 100644 (file)
@@ -48,7 +48,7 @@ struct b2056_inittabs_pts {
        unsigned int rx_length;
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev3_syn[] = {
+static const struct b2056_inittab_entry b2056_inittab_phy_rev3_syn[] = {
        [B2056_SYN_RESERVED_ADDR2]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR3]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR4]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -232,7 +232,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev3_syn[] = {
        [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev3_tx[] = {
+static const struct b2056_inittab_entry b2056_inittab_phy_rev3_tx[] = {
        [B2056_TX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -380,7 +380,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev3_tx[] = {
        [B2056_TX_STATUS_TXLPF_RC]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev3_rx[] = {
+static const struct b2056_inittab_entry b2056_inittab_phy_rev3_rx[] = {
        [B2056_RX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -530,7 +530,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev3_rx[] = {
        [B2056_RX_STATUS_HPC_RC]        = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev4_syn[] = {
+static const struct b2056_inittab_entry b2056_inittab_phy_rev4_syn[] = {
        [B2056_SYN_RESERVED_ADDR2]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR3]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR4]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -714,7 +714,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev4_syn[] = {
        [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev4_tx[] = {
+static const struct b2056_inittab_entry b2056_inittab_phy_rev4_tx[] = {
        [B2056_TX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -862,7 +862,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev4_tx[] = {
        [B2056_TX_STATUS_TXLPF_RC]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev4_rx[] = {
+static const struct b2056_inittab_entry b2056_inittab_phy_rev4_rx[] = {
        [B2056_RX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -1012,7 +1012,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev4_rx[] = {
        [B2056_RX_STATUS_HPC_RC]        = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev5_syn[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev5_syn[] = {
        [B2056_SYN_RESERVED_ADDR2]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR3]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR4]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -1196,7 +1196,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev5_syn[] = {
        [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev5_tx[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev5_tx[] = {
        [B2056_TX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -1352,7 +1352,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev5_tx[] = {
        [B2056_TX_GMBB_IDAC7]           = { .ghz5 = 0x0075, .ghz2 = 0x0075, UPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev5_rx[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev5_rx[] = {
        [B2056_RX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -1502,7 +1502,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev5_rx[] = {
        [B2056_RX_STATUS_HPC_RC]        = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev6_syn[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev6_syn[] = {
        [B2056_SYN_RESERVED_ADDR2]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR3]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR4]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -1686,7 +1686,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev6_syn[] = {
        [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev6_tx[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev6_tx[] = {
        [B2056_TX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -1842,7 +1842,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev6_tx[] = {
        [B2056_TX_GMBB_IDAC7]           = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev6_rx[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev6_rx[] = {
        [B2056_RX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -1992,7 +1992,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev6_rx[] = {
        [B2056_RX_STATUS_HPC_RC]        = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev7_syn[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev7_9_syn[] = {
        [B2056_SYN_RESERVED_ADDR2]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR3]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR4]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -2176,7 +2176,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev7_syn[] = {
        [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev7_tx[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev7_9_tx[] = {
        [B2056_TX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -2332,7 +2332,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev7_tx[] = {
        [B2056_TX_GMBB_IDAC7]           = { .ghz5 = 0x0075, .ghz2 = 0x0075, UPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev7_rx[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev7_9_rx[] = {
        [B2056_RX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -2482,7 +2482,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev7_rx[] = {
        [B2056_RX_STATUS_HPC_RC]        = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev8_syn[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev8_syn[] = {
        [B2056_SYN_RESERVED_ADDR2]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR3]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR4]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -2666,7 +2666,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev8_syn[] = {
        [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev8_tx[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev8_tx[] = {
        [B2056_TX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -2822,7 +2822,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev8_tx[] = {
        [B2056_TX_GMBB_IDAC7]           = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev8_rx[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev8_rx[] = {
        [B2056_RX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -2972,24 +2972,69 @@ static const struct b2056_inittab_entry b2056_inittab_rev8_rx[] = {
        [B2056_RX_STATUS_HPC_RC]        = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-#define INITTABSPTS(prefix) \
-       .syn            = prefix##_syn,                 \
-       .syn_length     = ARRAY_SIZE(prefix##_syn),     \
-       .tx             = prefix##_tx,                  \
-       .tx_length      = ARRAY_SIZE(prefix##_tx),      \
-       .rx             = prefix##_rx,                  \
-       .rx_length      = ARRAY_SIZE(prefix##_rx)
+static const struct b2056_inittab_entry b2056_inittab_radio_rev11_syn[] = {
+       [B2056_SYN_PLL_PFD]             = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, },
+       [B2056_SYN_PLL_CP2]             = { .ghz5 = 0x003f, .ghz2 = 0x003f, UPLOAD, },
+       [B2056_SYN_PLL_LOOPFILTER1]     = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, },
+       [B2056_SYN_PLL_LOOPFILTER2]     = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, },
+       [B2056_SYN_PLL_LOOPFILTER4]     = { .ghz5 = 0x002b, .ghz2 = 0x002b, UPLOAD, },
+       [B2056_SYN_PLL_VCO2]            = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, },
+       [B2056_SYN_PLL_VCOCAL12]        = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, },
+       [B2056_SYN_LOGENBUF2]           = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, },
+};
 
-static const struct b2056_inittabs_pts b2056_inittabs[] = {
-       [3] = { INITTABSPTS(b2056_inittab_rev3) },
-       [4] = { INITTABSPTS(b2056_inittab_rev4) },
-       [5] = { INITTABSPTS(b2056_inittab_rev5) },
-       [6] = { INITTABSPTS(b2056_inittab_rev6) },
-       [7] = { INITTABSPTS(b2056_inittab_rev7) },
-       [8] = { INITTABSPTS(b2056_inittab_rev8) },
-       [9] = { INITTABSPTS(b2056_inittab_rev7) },
+static const struct b2056_inittab_entry b2056_inittab_radio_rev11_tx[] = {
+       [B2056_TX_PA_SPARE2]            = { .ghz5 = 0x00ee, .ghz2 = 0x00ee, UPLOAD, },
+       [B2056_TX_INTPAA_IAUX_STAT]     = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, },
+       [B2056_TX_INTPAA_IMAIN_STAT]    = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, },
+       [B2056_TX_INTPAA_PASLOPE]       = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, },
+       [B2056_TX_INTPAG_PASLOPE]       = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, },
+       [B2056_TX_PADA_IDAC]            = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, },
+       [B2056_TX_PADA_SLOPE]           = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, },
+       [B2056_TX_PADG_SLOPE]           = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, },
+       [B2056_TX_PGAA_IDAC]            = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, },
+       [B2056_TX_PGAA_SLOPE]           = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, },
+       [B2056_TX_PGAG_SLOPE]           = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, },
+       [B2056_TX_GMBB_IDAC]            = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, },
+       [B2056_TX_TXSPARE1]             = { .ghz5 = 0x0030, .ghz2 = 0x0030, UPLOAD, },
+};
+
+static const struct b2056_inittab_entry b2056_inittab_radio_rev11_rx[] = {
+       [B2056_RX_BIASPOLE_LNAA1_IDAC]  = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, },
+       [B2056_RX_LNAA2_IDAC]           = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, },
+       [B2056_RX_BIASPOLE_LNAG1_IDAC]  = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, },
+       [B2056_RX_LNAG2_IDAC]           = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, },
+       [B2056_RX_MIXA_VCM]             = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, },
+       [B2056_RX_MIXA_LOB_BIAS]        = { .ghz5 = 0x0088, .ghz2 = 0x0088, UPLOAD, },
+       [B2056_RX_MIXA_BIAS_AUX]        = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, },
+       [B2056_RX_MIXG_VCM]             = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, },
+       [B2056_RX_TIA_IOPAMP]           = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, },
+       [B2056_RX_TIA_QOPAMP]           = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, },
+       [B2056_RX_TIA_IMISC]            = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, },
+       [B2056_RX_TIA_QMISC]            = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, },
+       [B2056_RX_RXLPF_OUTVCM]         = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, },
+       [B2056_RX_VGA_BIAS_DCCANCEL]    = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, },
+       [B2056_RX_RXSPARE3]             = { .ghz5 = 0x0005, .ghz2 = 0x0005, UPLOAD, },
 };
 
+#define INITTABSPTS(prefix) \
+       static const struct b2056_inittabs_pts prefix = {       \
+               .syn            = prefix##_syn,                 \
+               .syn_length     = ARRAY_SIZE(prefix##_syn),     \
+               .tx             = prefix##_tx,                  \
+               .tx_length      = ARRAY_SIZE(prefix##_tx),      \
+               .rx             = prefix##_rx,                  \
+               .rx_length      = ARRAY_SIZE(prefix##_rx),      \
+       }
+
+INITTABSPTS(b2056_inittab_phy_rev3);
+INITTABSPTS(b2056_inittab_phy_rev4);
+INITTABSPTS(b2056_inittab_radio_rev5);
+INITTABSPTS(b2056_inittab_radio_rev6);
+INITTABSPTS(b2056_inittab_radio_rev7_9);
+INITTABSPTS(b2056_inittab_radio_rev8);
+INITTABSPTS(b2056_inittab_radio_rev11);
+
 #define RADIOREGS3(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \
                   r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, \
                   r20, r21, r22, r23, r24, r25, r26, r27, r28, r29, \
@@ -3041,7 +3086,7 @@ static const struct b2056_inittabs_pts b2056_inittabs[] = {
        .phy_regs.phy_bw6       = r5
 
 /* http://bcm-v4.sipsolutions.net/802.11/Radio/2056/ChannelTable */
-static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev3[] = {
+static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_phy_rev3[] = {
   {    .freq                   = 4920,
        RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04,
                   0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00,
@@ -4036,7 +4081,7 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev3[] =
   },
 };
 
-static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev4[] = {
+static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_phy_rev4[] = {
   {    .freq                   = 4920,
        RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04,
                   0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00,
@@ -5031,7 +5076,7 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev4[] =
   },
 };
 
-static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev5[] = {
+static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev5[] = {
   {    .freq                   = 4920,
        RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04,
                   0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00,
@@ -6026,7 +6071,7 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev5[] =
   },
 };
 
-static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev6[] = {
+static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev6[] = {
   {    .freq                   = 4920,
        RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04,
                   0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00,
@@ -7021,7 +7066,7 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev6[] =
   },
 };
 
-static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev7_9[] = {
+static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev7_9[] = {
   {    .freq                   = 4920,
        RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04,
                   0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00,
@@ -8016,7 +8061,7 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev7_9[]
   },
 };
 
-static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev8[] = {
+static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev8[] = {
   {    .freq                   = 4920,
        RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04,
                   0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00,
@@ -9011,6 +9056,1154 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev8[] =
   },
 };
 
+static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev11[] = {
+       {
+               .freq                   = 4920,
+               RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216),
+       },
+       {
+               .freq                   = 4930,
+               RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215),
+       },
+       {
+               .freq                   = 4940,
+               RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214),
+       },
+       {
+               .freq                   = 4950,
+               RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213),
+       },
+       {
+               .freq                   = 4960,
+               RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212),
+       },
+       {
+               .freq                   = 4970,
+               RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211),
+       },
+       {
+               .freq                   = 4980,
+               RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f),
+       },
+       {
+               .freq                   = 4990,
+               RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e),
+       },
+       {
+               .freq                   = 5000,
+               RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d),
+       },
+       {
+               .freq                   = 5010,
+               RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c),
+       },
+       {
+               .freq                   = 5020,
+               RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b),
+       },
+       {
+               .freq                   = 5030,
+               RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a),
+       },
+       {
+               .freq                   = 5040,
+               RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209),
+       },
+       {
+               .freq                   = 5050,
+               RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208),
+       },
+       {
+               .freq                   = 5060,
+               RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207),
+       },
+       {
+               .freq                   = 5070,
+               RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206),
+       },
+       {
+               .freq                   = 5080,
+               RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205),
+       },
+       {
+               .freq                   = 5090,
+               RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204),
+       },
+       {
+               .freq                   = 5100,
+               RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203),
+       },
+       {
+               .freq                   = 5110,
+               RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202),
+       },
+       {
+               .freq                   = 5120,
+               RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201),
+       },
+       {
+               .freq                   = 5130,
+               RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200),
+       },
+       {
+               .freq                   = 5140,
+               RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff),
+       },
+       {
+               .freq                   = 5160,
+               RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e,
+                          0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77,
+                          0x00, 0x0e, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd),
+       },
+       {
+               .freq                   = 5170,
+               RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e,
+                          0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77,
+                          0x00, 0x0e, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc),
+       },
+       {
+               .freq                   = 5180,
+               RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e,
+                          0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77,
+                          0x00, 0x0e, 0x00, 0x6f, 0x00),
+               PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb),
+       },
+       {
+               .freq                   = 5190,
+               RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d,
+                          0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77,
+                          0x00, 0x0d, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa),
+       },
+       {
+               .freq                   = 5200,
+               RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d,
+                          0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77,
+                          0x00, 0x0d, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9),
+       },
+       {
+               .freq                   = 5210,
+               RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d,
+                          0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77,
+                          0x00, 0x0d, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8),
+       },
+       {
+               .freq                   = 5220,
+               RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00,
+                          0xfe, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d,
+                          0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77,
+                          0x00, 0x0d, 0x00, 0x6f, 0x00),
+               PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7),
+       },
+       {
+               .freq                   = 5230,
+               RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00,
+                          0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d,
+                          0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77,
+                          0x00, 0x0d, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6),
+       },
+       {
+               .freq                   = 5240,
+               RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00,
+                          0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d,
+                          0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77,
+                          0x00, 0x0d, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5),
+       },
+       {
+               .freq                   = 5250,
+               RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00,
+                          0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d,
+                          0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77,
+                          0x00, 0x0d, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4),
+       },
+       {
+               .freq                   = 5260,
+               RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00,
+                          0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d,
+                          0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77,
+                          0x00, 0x0d, 0x00, 0x6f, 0x00),
+               PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3),
+       },
+       {
+               .freq                   = 5270,
+               RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00,
+                          0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c,
+                          0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77,
+                          0x00, 0x0c, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2),
+       },
+       {
+               .freq                   = 5280,
+               RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00,
+                          0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c,
+                          0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0c, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1),
+       },
+       {
+               .freq                   = 5290,
+               RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00,
+                          0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c,
+                          0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0c, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0),
+       },
+       {
+               .freq                   = 5300,
+               RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00,
+                          0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c,
+                          0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0c, 0x00, 0x6f, 0x00),
+               PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0),
+       },
+       {
+               .freq                   = 5310,
+               RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00,
+                          0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c,
+                          0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0c, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef),
+       },
+       {
+               .freq                   = 5320,
+               RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00,
+                          0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c,
+                          0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0c, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee),
+       },
+       {
+               .freq                   = 5330,
+               RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00,
+                          0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b,
+                          0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0b, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed),
+       },
+       {
+               .freq                   = 5340,
+               RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00,
+                          0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b,
+                          0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0b, 0x00, 0x6f, 0x00),
+               PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec),
+       },
+       {
+               .freq                   = 5350,
+               RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00,
+                          0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b,
+                          0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0b, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb),
+       },
+       {
+               .freq                   = 5360,
+               RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00,
+                          0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a,
+                          0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0a, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea),
+       },
+       {
+               .freq                   = 5370,
+               RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00,
+                          0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a,
+                          0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0a, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9),
+       },
+       {
+               .freq                   = 5380,
+               RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00,
+                          0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a,
+                          0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0a, 0x00, 0x6f, 0x00),
+               PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8),
+       },
+       {
+               .freq                   = 5390,
+               RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00,
+                          0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a,
+                          0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0a, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7),
+       },
+       {
+               .freq                   = 5400,
+               RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00,
+                          0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a,
+                          0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0a, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6),
+       },
+       {
+               .freq                   = 5410,
+               RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00,
+                          0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a,
+                          0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77,
+                          0x00, 0x0a, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5),
+       },
+       {
+               .freq                   = 5420,
+               RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00,
+                          0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a,
+                          0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77,
+                          0x00, 0x0a, 0x00, 0x6f, 0x00),
+               PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5),
+       },
+       {
+               .freq                   = 5430,
+               RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00,
+                          0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a,
+                          0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77,
+                          0x00, 0x0a, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4),
+       },
+       {
+               .freq                   = 5440,
+               RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00,
+                          0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3),
+       },
+       {
+               .freq                   = 5450,
+               RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00,
+                          0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2),
+       },
+       {
+               .freq                   = 5460,
+               RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00,
+                          0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1),
+       },
+       {
+               .freq                   = 5470,
+               RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00,
+                          0x94, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0),
+       },
+       {
+               .freq                   = 5480,
+               RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00,
+                          0x84, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df),
+       },
+       {
+               .freq                   = 5490,
+               RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00,
+                          0x83, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de),
+       },
+       {
+               .freq                   = 5500,
+               RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00,
+                          0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd),
+       },
+       {
+               .freq                   = 5510,
+               RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00,
+                          0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd),
+       },
+       {
+               .freq                   = 5520,
+               RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00,
+                          0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc),
+       },
+       {
+               .freq                   = 5530,
+               RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00,
+                          0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db),
+       },
+       {
+               .freq                   = 5540,
+               RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00,
+                          0x71, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da),
+       },
+       {
+               .freq                   = 5550,
+               RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00,
+                          0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9),
+       },
+       {
+               .freq                   = 5560,
+               RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00,
+                          0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8),
+       },
+       {
+               .freq                   = 5570,
+               RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00,
+                          0x61, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7),
+       },
+       {
+               .freq                   = 5580,
+               RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00,
+                          0x60, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08,
+                          0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x08, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7),
+       },
+       {
+               .freq                   = 5590,
+               RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00,
+                          0x50, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08,
+                          0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x08, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6),
+       },
+       {
+               .freq                   = 5600,
+               RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00,
+                          0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08,
+                          0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x08, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5),
+       },
+       {
+               .freq                   = 5610,
+               RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00,
+                          0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08,
+                          0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x08, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4),
+       },
+       {
+               .freq                   = 5620,
+               RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00,
+                          0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07,
+                          0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x07, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3),
+       },
+       {
+               .freq                   = 5630,
+               RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00,
+                          0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07,
+                          0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x07, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2),
+       },
+       {
+               .freq                   = 5640,
+               RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00,
+                          0x40, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07,
+                          0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x07, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2),
+       },
+       {
+               .freq                   = 5650,
+               RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00,
+                          0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07,
+                          0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x07, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1),
+       },
+       {
+               .freq                   = 5660,
+               RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00,
+                          0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0),
+       },
+       {
+               .freq                   = 5670,
+               RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00,
+                          0x40, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf),
+       },
+       {
+               .freq                   = 5680,
+               RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00,
+                          0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce),
+       },
+       {
+               .freq                   = 5690,
+               RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00,
+                          0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce),
+       },
+       {
+               .freq                   = 5700,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00,
+                          0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6e, 0x00),
+               PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd),
+       },
+       {
+               .freq                   = 5710,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00,
+                          0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6e, 0x00),
+               PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc),
+       },
+       {
+               .freq                   = 5720,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00,
+                          0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6e, 0x00),
+               PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb),
+       },
+       {
+               .freq                   = 5725,
+               RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00,
+                          0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6e, 0x00),
+               PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb),
+       },
+       {
+               .freq                   = 5730,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00,
+                          0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6e, 0x00),
+               PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca),
+       },
+       {
+               .freq                   = 5735,
+               RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00,
+                          0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6d, 0x00),
+               PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca),
+       },
+       {
+               .freq                   = 5740,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00,
+                          0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6d, 0x00),
+               PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9),
+       },
+       {
+               .freq                   = 5745,
+               RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00,
+                          0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6d, 0x00),
+               PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9),
+       },
+       {
+               .freq                   = 5750,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00,
+                          0x20, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6d, 0x00),
+               PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9),
+       },
+       {
+               .freq                   = 5755,
+               RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00,
+                          0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6c, 0x00),
+               PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8),
+       },
+       {
+               .freq                   = 5760,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00,
+                          0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6c, 0x00),
+               PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8),
+       },
+       {
+               .freq                   = 5765,
+               RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00,
+                          0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6c, 0x00),
+               PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8),
+       },
+       {
+               .freq                   = 5770,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00,
+                          0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6b, 0x00),
+               PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7),
+       },
+       {
+               .freq                   = 5775,
+               RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00,
+                          0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6b, 0x00),
+               PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7),
+       },
+       {
+               .freq                   = 5780,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00,
+                          0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6b, 0x00),
+               PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6),
+       },
+       {
+               .freq                   = 5785,
+               RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6b, 0x00),
+               PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6),
+       },
+       {
+               .freq                   = 5790,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6b, 0x00),
+               PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6),
+       },
+       {
+               .freq                   = 5795,
+               RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6b, 0x00),
+               PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5),
+       },
+       {
+               .freq                   = 5800,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6b, 0x00),
+               PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5),
+       },
+       {
+               .freq                   = 5805,
+               RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6a, 0x00),
+               PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4),
+       },
+       {
+               .freq                   = 5810,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6a, 0x00),
+               PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4),
+       },
+       {
+               .freq                   = 5815,
+               RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6a, 0x00),
+               PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4),
+       },
+       {
+               .freq                   = 5820,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6a, 0x00),
+               PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3),
+       },
+       {
+               .freq                   = 5825,
+               RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x69, 0x00),
+               PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3),
+       },
+       {
+               .freq                   = 5830,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x69, 0x00),
+               PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2),
+       },
+       {
+               .freq                   = 5840,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04,
+                          0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x04, 0x00, 0x69, 0x00),
+               PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2),
+       },
+       {
+               .freq                   = 5850,
+               RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04,
+                          0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x04, 0x00, 0x69, 0x00),
+               PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1),
+       },
+       {
+               .freq                   = 5860,
+               RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04,
+                          0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x04, 0x00, 0x69, 0x00),
+               PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0),
+       },
+       {
+               .freq                   = 5870,
+               RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04,
+                          0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x04, 0x00, 0x68, 0x00),
+               PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf),
+       },
+       {
+               .freq                   = 5880,
+               RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04,
+                          0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x04, 0x00, 0x68, 0x00),
+               PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf),
+       },
+       {
+               .freq                   = 5890,
+               RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04,
+                          0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x04, 0x00, 0x68, 0x00),
+               PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be),
+       },
+       {
+               .freq                   = 5900,
+               RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04,
+                          0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x04, 0x00, 0x68, 0x00),
+               PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd),
+       },
+       {
+               .freq                   = 5910,
+               RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04,
+                          0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x04, 0x00, 0x68, 0x00),
+               PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc),
+       },
+       {
+               .freq                   = 2412,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00,
+                          0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00,
+                          0x70, 0x00, 0x0b, 0x00, 0x0a),
+               PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443),
+       },
+       {
+               .freq                   = 2417,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00,
+                          0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00,
+                          0x70, 0x00, 0x0b, 0x00, 0x0a),
+               PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441),
+       },
+       {
+               .freq                   = 2422,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x67, 0x00, 0x03, 0x00, 0x70, 0x00,
+                          0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00,
+                          0x70, 0x00, 0x0b, 0x00, 0x0a),
+               PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f),
+       },
+       {
+               .freq                   = 2427,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x57, 0x00, 0x03, 0x00, 0x70, 0x00,
+                          0x0a, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00,
+                          0x70, 0x00, 0x0a, 0x00, 0x0a),
+               PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d),
+       },
+       {
+               .freq                   = 2432,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x56, 0x00, 0x03, 0x00, 0x70, 0x00,
+                          0x0a, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x03, 0x00,
+                          0x70, 0x00, 0x0a, 0x00, 0x0a),
+               PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a),
+       },
+       {
+               .freq                   = 2437,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x46, 0x00, 0x03, 0x00, 0x70, 0x00,
+                          0x0a, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x03, 0x00,
+                          0x70, 0x00, 0x0a, 0x00, 0x0a),
+               PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438),
+       },
+       {
+               .freq                   = 2442,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00,
+                          0x0a, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x02, 0x00,
+                          0x70, 0x00, 0x0a, 0x00, 0x0a),
+               PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436),
+       },
+       {
+               .freq                   = 2447,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00,
+                          0x0a, 0x00, 0x09, 0x00, 0x55, 0x00, 0x02, 0x00,
+                          0x70, 0x00, 0x0a, 0x00, 0x09),
+               PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434),
+       },
+       {
+               .freq                   = 2452,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x23, 0x00, 0x02, 0x00, 0x70, 0x00,
+                          0x0a, 0x00, 0x09, 0x00, 0x45, 0x00, 0x02, 0x00,
+                          0x70, 0x00, 0x0a, 0x00, 0x09),
+               PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431),
+       },
+       {
+               .freq                   = 2457,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x12, 0x00, 0x02, 0x00, 0x70, 0x00,
+                          0x0a, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00,
+                          0x70, 0x00, 0x0a, 0x00, 0x09),
+               PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f),
+       },
+       {
+               .freq                   = 2462,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x70, 0x00,
+                          0x09, 0x00, 0x09, 0x00, 0x33, 0x00, 0x02, 0x00,
+                          0x70, 0x00, 0x09, 0x00, 0x09),
+               PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d),
+       },
+       {
+               .freq                   = 2467,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00,
+                          0x09, 0x00, 0x09, 0x00, 0x22, 0x00, 0x02, 0x00,
+                          0x70, 0x00, 0x09, 0x00, 0x09),
+               PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b),
+       },
+       {
+               .freq                   = 2472,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00,
+                          0x09, 0x00, 0x09, 0x00, 0x11, 0x00, 0x02, 0x00,
+                          0x70, 0x00, 0x09, 0x00, 0x09),
+               PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429),
+       },
+       {
+               .freq                   = 2484,
+               RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00,
+                          0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00,
+                          0x70, 0x00, 0x09, 0x00, 0x09),
+               PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424),
+       },
+};
+
+static const struct b2056_inittabs_pts
+*b43_nphy_get_inittabs_rev3(struct b43_wldev *dev)
+{
+       struct b43_phy *phy = &dev->phy;
+
+       switch (dev->phy.rev) {
+       case 3:
+               return &b2056_inittab_phy_rev3;
+       case 4:
+               return &b2056_inittab_phy_rev4;
+       default:
+               switch (phy->radio_rev) {
+               case 5:
+                       return &b2056_inittab_radio_rev5;
+               case 6:
+                       return &b2056_inittab_radio_rev6;
+               case 7:
+               case 9:
+                       return &b2056_inittab_radio_rev7_9;
+               case 8:
+                       return &b2056_inittab_radio_rev8;
+               case 11:
+                       return &b2056_inittab_radio_rev11;
+               }
+       }
+
+       return NULL;
+}
+
 static void b2056_upload_inittab(struct b43_wldev *dev, bool ghz5,
                                 bool ignore_uploadflag, u16 routing,
                                 const struct b2056_inittab_entry *e,
@@ -9037,11 +10230,11 @@ void b2056_upload_inittabs(struct b43_wldev *dev,
 {
        const struct b2056_inittabs_pts *pts;
 
-       if (dev->phy.rev >= ARRAY_SIZE(b2056_inittabs)) {
+       pts = b43_nphy_get_inittabs_rev3(dev);
+       if (!pts) {
                B43_WARN_ON(1);
                return;
        }
-       pts = &b2056_inittabs[dev->phy.rev];
 
        b2056_upload_inittab(dev, ghz5, ignore_uploadflag,
                                B2056_SYN, pts->syn, pts->syn_length);
@@ -9060,11 +10253,12 @@ void b2056_upload_syn_pll_cp2(struct b43_wldev *dev, bool ghz5)
        const struct b2056_inittabs_pts *pts;
        const struct b2056_inittab_entry *e;
 
-       if (dev->phy.rev >= ARRAY_SIZE(b2056_inittabs)) {
+       pts = b43_nphy_get_inittabs_rev3(dev);
+       if (!pts) {
                B43_WARN_ON(1);
                return;
        }
-       pts = &b2056_inittabs[dev->phy.rev];
+
        e = &pts->syn[B2056_SYN_PLL_CP2];
 
        b43_radio_write(dev, B2056_SYN_PLL_CP2, ghz5 ? e->ghz5 : e->ghz2);
@@ -9073,38 +10267,46 @@ void b2056_upload_syn_pll_cp2(struct b43_wldev *dev, bool ghz5)
 const struct b43_nphy_channeltab_entry_rev3 *
 b43_nphy_get_chantabent_rev3(struct b43_wldev *dev, u16 freq)
 {
+       struct b43_phy *phy = &dev->phy;
        const struct b43_nphy_channeltab_entry_rev3 *e;
        unsigned int length, i;
 
-       switch (dev->phy.rev) {
+       switch (phy->rev) {
        case 3:
-               e = b43_nphy_channeltab_rev3;
-               length = ARRAY_SIZE(b43_nphy_channeltab_rev3);
+               e = b43_nphy_channeltab_phy_rev3;
+               length = ARRAY_SIZE(b43_nphy_channeltab_phy_rev3);
                break;
        case 4:
-               e = b43_nphy_channeltab_rev4;
-               length = ARRAY_SIZE(b43_nphy_channeltab_rev4);
-               break;
-       case 5:
-               e = b43_nphy_channeltab_rev5;
-               length = ARRAY_SIZE(b43_nphy_channeltab_rev5);
-               break;
-       case 6:
-               e = b43_nphy_channeltab_rev6;
-               length = ARRAY_SIZE(b43_nphy_channeltab_rev6);
-               break;
-       case 7:
-       case 9:
-               e = b43_nphy_channeltab_rev7_9;
-               length = ARRAY_SIZE(b43_nphy_channeltab_rev7_9);
-               break;
-       case 8:
-               e = b43_nphy_channeltab_rev8;
-               length = ARRAY_SIZE(b43_nphy_channeltab_rev8);
+               e = b43_nphy_channeltab_phy_rev4;
+               length = ARRAY_SIZE(b43_nphy_channeltab_phy_rev4);
                break;
        default:
-               B43_WARN_ON(1);
-               return NULL;
+               switch (phy->radio_rev) {
+               case 5:
+                       e = b43_nphy_channeltab_radio_rev5;
+                       length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev5);
+                       break;
+               case 6:
+                       e = b43_nphy_channeltab_radio_rev6;
+                       length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev6);
+                       break;
+               case 7:
+               case 9:
+                       e = b43_nphy_channeltab_radio_rev7_9;
+                       length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev7_9);
+                       break;
+               case 8:
+                       e = b43_nphy_channeltab_radio_rev8;
+                       length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev8);
+                       break;
+               case 11:
+                       e = b43_nphy_channeltab_radio_rev11;
+                       length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev11);
+                       break;
+               default:
+                       B43_WARN_ON(1);
+                       return NULL;
+               }
        }
 
        for (i = 0; i < length; i++, e++) {
index 94c755fdda14749eaa36716032283b8cb9fea7d2..4047c05e380759f4f0b64bbdc5188f04efcce242 100644 (file)
@@ -1627,74 +1627,7 @@ static const u32 b43_ntab_tdtrn_r3[] = {
        0xfa58fc00, 0x0b64fc7e, 0x0800f7b6, 0x00f006be,
 };
 
-static const u32 b43_ntab_noisevar0_r3[] = {
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-};
-
-static const u32 b43_ntab_noisevar1_r3[] = {
+static const u32 b43_ntab_noisevar_r3[] = {
        0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
        0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
        0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
@@ -3109,31 +3042,32 @@ static void b43_nphy_tables_init_rev3(struct b43_wldev *dev)
                antswlut = sprom->fem.ghz2.antswlut;
 
        /* Static tables */
-       ntab_upload(dev, B43_NTAB_FRAMESTRUCT_R3, b43_ntab_framestruct_r3);
-       ntab_upload(dev, B43_NTAB_PILOT_R3, b43_ntab_pilot_r3);
-       ntab_upload(dev, B43_NTAB_TMAP_R3, b43_ntab_tmap_r3);
-       ntab_upload(dev, B43_NTAB_INTLEVEL_R3, b43_ntab_intlevel_r3);
-       ntab_upload(dev, B43_NTAB_TDTRN_R3, b43_ntab_tdtrn_r3);
-       ntab_upload(dev, B43_NTAB_NOISEVAR0_R3, b43_ntab_noisevar0_r3);
-       ntab_upload(dev, B43_NTAB_NOISEVAR1_R3, b43_ntab_noisevar1_r3);
-       ntab_upload(dev, B43_NTAB_MCS_R3, b43_ntab_mcs_r3);
-       ntab_upload(dev, B43_NTAB_TDI20A0_R3, b43_ntab_tdi20a0_r3);
-       ntab_upload(dev, B43_NTAB_TDI20A1_R3, b43_ntab_tdi20a1_r3);
-       ntab_upload(dev, B43_NTAB_TDI40A0_R3, b43_ntab_tdi40a0_r3);
-       ntab_upload(dev, B43_NTAB_TDI40A1_R3, b43_ntab_tdi40a1_r3);
-       ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3);
-       ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3);
-       ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3);
-       ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3);
-       ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3);
-       ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3);
-       ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3);
-       ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3);
-       ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3);
-       ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3);
-       ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3);
-       ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3);
-       ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3);
+       if (dev->phy.do_full_init) {
+               ntab_upload(dev, B43_NTAB_FRAMESTRUCT_R3, b43_ntab_framestruct_r3);
+               ntab_upload(dev, B43_NTAB_PILOT_R3, b43_ntab_pilot_r3);
+               ntab_upload(dev, B43_NTAB_TMAP_R3, b43_ntab_tmap_r3);
+               ntab_upload(dev, B43_NTAB_INTLEVEL_R3, b43_ntab_intlevel_r3);
+               ntab_upload(dev, B43_NTAB_TDTRN_R3, b43_ntab_tdtrn_r3);
+               ntab_upload(dev, B43_NTAB_NOISEVAR_R3, b43_ntab_noisevar_r3);
+               ntab_upload(dev, B43_NTAB_MCS_R3, b43_ntab_mcs_r3);
+               ntab_upload(dev, B43_NTAB_TDI20A0_R3, b43_ntab_tdi20a0_r3);
+               ntab_upload(dev, B43_NTAB_TDI20A1_R3, b43_ntab_tdi20a1_r3);
+               ntab_upload(dev, B43_NTAB_TDI40A0_R3, b43_ntab_tdi40a0_r3);
+               ntab_upload(dev, B43_NTAB_TDI40A1_R3, b43_ntab_tdi40a1_r3);
+               ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3);
+               ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3);
+               ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3);
+               ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3);
+               ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3);
+               ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3);
+               ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3);
+               ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3);
+               ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3);
+               ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3);
+               ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3);
+               ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3);
+               ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3);
+       }
 
        /* Volatile tables */
        if (antswlut < ARRAY_SIZE(b43_ntab_antswctl_r3))
@@ -3146,20 +3080,22 @@ static void b43_nphy_tables_init_rev3(struct b43_wldev *dev)
 static void b43_nphy_tables_init_rev0(struct b43_wldev *dev)
 {
        /* Static tables */
-       ntab_upload(dev, B43_NTAB_FRAMESTRUCT, b43_ntab_framestruct);
-       ntab_upload(dev, B43_NTAB_FRAMELT, b43_ntab_framelookup);
-       ntab_upload(dev, B43_NTAB_TMAP, b43_ntab_tmap);
-       ntab_upload(dev, B43_NTAB_TDTRN, b43_ntab_tdtrn);
-       ntab_upload(dev, B43_NTAB_INTLEVEL, b43_ntab_intlevel);
-       ntab_upload(dev, B43_NTAB_PILOT, b43_ntab_pilot);
-       ntab_upload(dev, B43_NTAB_TDI20A0, b43_ntab_tdi20a0);
-       ntab_upload(dev, B43_NTAB_TDI20A1, b43_ntab_tdi20a1);
-       ntab_upload(dev, B43_NTAB_TDI40A0, b43_ntab_tdi40a0);
-       ntab_upload(dev, B43_NTAB_TDI40A1, b43_ntab_tdi40a1);
-       ntab_upload(dev, B43_NTAB_CHANEST, b43_ntab_channelest);
-       ntab_upload(dev, B43_NTAB_MCS, b43_ntab_mcs);
-       ntab_upload(dev, B43_NTAB_NOISEVAR10, b43_ntab_noisevar10);
-       ntab_upload(dev, B43_NTAB_NOISEVAR11, b43_ntab_noisevar11);
+       if (dev->phy.do_full_init) {
+               ntab_upload(dev, B43_NTAB_FRAMESTRUCT, b43_ntab_framestruct);
+               ntab_upload(dev, B43_NTAB_FRAMELT, b43_ntab_framelookup);
+               ntab_upload(dev, B43_NTAB_TMAP, b43_ntab_tmap);
+               ntab_upload(dev, B43_NTAB_TDTRN, b43_ntab_tdtrn);
+               ntab_upload(dev, B43_NTAB_INTLEVEL, b43_ntab_intlevel);
+               ntab_upload(dev, B43_NTAB_PILOT, b43_ntab_pilot);
+               ntab_upload(dev, B43_NTAB_TDI20A0, b43_ntab_tdi20a0);
+               ntab_upload(dev, B43_NTAB_TDI20A1, b43_ntab_tdi20a1);
+               ntab_upload(dev, B43_NTAB_TDI40A0, b43_ntab_tdi40a0);
+               ntab_upload(dev, B43_NTAB_TDI40A1, b43_ntab_tdi40a1);
+               ntab_upload(dev, B43_NTAB_CHANEST, b43_ntab_channelest);
+               ntab_upload(dev, B43_NTAB_MCS, b43_ntab_mcs);
+               ntab_upload(dev, B43_NTAB_NOISEVAR10, b43_ntab_noisevar10);
+               ntab_upload(dev, B43_NTAB_NOISEVAR11, b43_ntab_noisevar11);
+       }
 
        /* Volatile tables */
        ntab_upload(dev, B43_NTAB_BDI, b43_ntab_bdi);
index 9ff33adcff891cad9551bafab507924a59a54afb..3a58aee4c4cf714aa72bc22a8c85670b135eb934 100644 (file)
@@ -143,8 +143,7 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent(
 #define B43_NTAB_TMAP_R3               B43_NTAB32(12,   0) /* TM AP  */
 #define B43_NTAB_INTLEVEL_R3           B43_NTAB32(13,   0) /* INT LV  */
 #define B43_NTAB_TDTRN_R3              B43_NTAB32(14,   0) /* TD TRN  */
-#define B43_NTAB_NOISEVAR0_R3          B43_NTAB32(16,   0) /* noise variance 0  */
-#define B43_NTAB_NOISEVAR1_R3          B43_NTAB32(16, 128) /* noise variance 1  */
+#define B43_NTAB_NOISEVAR_R3           B43_NTAB32(16,   0) /* noise variance */
 #define B43_NTAB_MCS_R3                        B43_NTAB16(18,   0) /* MCS  */
 #define B43_NTAB_TDI20A0_R3            B43_NTAB32(19, 128) /* TDI 20/0  */
 #define B43_NTAB_TDI20A1_R3            B43_NTAB32(19, 256) /* TDI 20/1  */
index 9b1a038be08b860da91461b82c6bd84dd5000531..c218c08fb2f5b15ad3233b475b5d275d2f4ba0df 100644 (file)
@@ -441,7 +441,7 @@ static void b43_wa_altagc(struct b43_wldev *dev)
 
 static void b43_wa_tr_ltov(struct b43_wldev *dev) /* TR Lookup Table Original Values */
 {
-       b43_gtab_write(dev, B43_GTAB_ORIGTR, 0, 0xC480);
+       b43_gtab_write(dev, B43_GTAB_ORIGTR, 0, 0x7654);
 }
 
 static void b43_wa_cpll_nonpilot(struct b43_wldev *dev)
index 31adb8cf0291fb0bbebf1a6896c8f4ff4d7288b9..4f38f19b8e3d373847778766ec8ad820e5d1d2bd 100644 (file)
@@ -408,7 +408,7 @@ int b43_generate_txhdr(struct b43_wldev *dev,
                mac_ctl |= B43_TXH_MAC_HWSEQ;
        if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
                mac_ctl |= B43_TXH_MAC_STMSDU;
-       if (phy->type == B43_PHYTYPE_A)
+       if (!phy->gmode)
                mac_ctl |= B43_TXH_MAC_5GHZ;
 
        /* Overwrite rates[0].count to make the retry calculation
index 1d2ceac3a221bd779320daf540bc49ecc9b219c5..98e67c18f276471d804e751122ae63c2f26662bf 100644 (file)
@@ -33,7 +33,7 @@ brcmfmac-objs += \
                bcdc.o \
                dhd_common.o \
                dhd_linux.o \
-               nvram.o \
+               firmware.o \
                btcoex.o
 brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \
                dhd_sdio.o \
index 939d6b13292248563f92bc4287632341c4997072..16f9ab2568a8089c1c38eff8f8998e8fc29ee330 100644 (file)
@@ -186,7 +186,7 @@ void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx);
 void brcmf_txflowblock_if(struct brcmf_if *ifp,
                          enum brcmf_netif_stop_reason reason, bool state);
 u32 brcmf_get_chip_info(struct brcmf_if *ifp);
-void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp,
+void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx,
                      bool success);
 
 /* Sets dongle media info (drv_version, mac address). */
index c4535616064e8389125b238a50fcc74d3c6d47da..7735328fff21e6da15fd8e8a801f63638b6594b9 100644 (file)
@@ -63,7 +63,6 @@ struct brcmf_bus_dcmd {
  */
 struct brcmf_bus_ops {
        int (*preinit)(struct device *dev);
-       int (*init)(struct device *dev);
        void (*stop)(struct device *dev);
        int (*txdata)(struct device *dev, struct sk_buff *skb);
        int (*txctl)(struct device *dev, unsigned char *msg, uint len);
@@ -99,6 +98,7 @@ struct brcmf_bus {
        unsigned long tx_realloc;
        u32 chip;
        u32 chiprev;
+       bool always_use_fws_queue;
 
        struct brcmf_bus_ops *ops;
 };
@@ -113,11 +113,6 @@ static inline int brcmf_bus_preinit(struct brcmf_bus *bus)
        return bus->ops->preinit(bus->dev);
 }
 
-static inline int brcmf_bus_init(struct brcmf_bus *bus)
-{
-       return bus->ops->init(bus->dev);
-}
-
 static inline void brcmf_bus_stop(struct brcmf_bus *bus)
 {
        bus->ops->stop(bus->dev);
index 6a8983a1fb9c3451908c1cf28426d78e6600d091..ed3e32ce8c23ee8fd032ad35520c7fee1e3cb18a 100644 (file)
@@ -32,6 +32,9 @@
 #define BRCMF_DEFAULT_SCAN_UNASSOC_TIME        40
 #define BRCMF_DEFAULT_PACKET_FILTER    "100 0 0 0 0x01 0x00"
 
+/* boost value for RSSI_DELTA in preferred join selection */
+#define BRCMF_JOIN_PREF_RSSI_BOOST     8
+
 
 bool brcmf_c_prec_enq(struct device *dev, struct pktq *q,
                      struct sk_buff *pkt, int prec)
@@ -246,6 +249,7 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
 {
        s8 eventmask[BRCMF_EVENTING_MASK_LEN];
        u8 buf[BRCMF_DCMD_SMLEN];
+       struct brcmf_join_pref_params join_pref_params[2];
        char *ptr;
        s32 err;
 
@@ -298,6 +302,20 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
                goto done;
        }
 
+       /* Setup join_pref to select target by RSSI(with boost on 5GHz) */
+       join_pref_params[0].type = BRCMF_JOIN_PREF_RSSI_DELTA;
+       join_pref_params[0].len = 2;
+       join_pref_params[0].rssi_gain = BRCMF_JOIN_PREF_RSSI_BOOST;
+       join_pref_params[0].band = WLC_BAND_5G;
+       join_pref_params[1].type = BRCMF_JOIN_PREF_RSSI;
+       join_pref_params[1].len = 2;
+       join_pref_params[1].rssi_gain = 0;
+       join_pref_params[1].band = 0;
+       err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params,
+                                      sizeof(join_pref_params));
+       if (err)
+               brcmf_err("Set join_pref error (%d)\n", err);
+
        /* Setup event_msgs, enable E_IF */
        err = brcmf_fil_iovar_data_get(ifp, "event_msgs", eventmask,
                                       BRCMF_EVENTING_MASK_LEN);
index 7d28cd3850925a7af0a4b9f34c2888afa83e0d03..09dd8c13d8448392372299c6eb8954132fca9c72 100644 (file)
@@ -190,7 +190,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
        int ret;
        struct brcmf_if *ifp = netdev_priv(ndev);
        struct brcmf_pub *drvr = ifp->drvr;
-       struct ethhdr *eh;
+       struct ethhdr *eh = (struct ethhdr *)(skb->data);
 
        brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx);
 
@@ -236,6 +236,9 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
                goto done;
        }
 
+       if (eh->h_proto == htons(ETH_P_PAE))
+               atomic_inc(&ifp->pend_8021x_cnt);
+
        ret = brcmf_fws_process_skb(ifp, skb);
 
 done:
@@ -538,31 +541,26 @@ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb)
                brcmf_netif_rx(ifp, skb);
 }
 
-void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp,
+void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx,
                      bool success)
 {
        struct brcmf_if *ifp;
        struct ethhdr *eh;
-       u8 ifidx;
        u16 type;
-       int res;
-
-       res = brcmf_proto_hdrpull(drvr, false, &ifidx, txp);
 
        ifp = drvr->iflist[ifidx];
        if (!ifp)
                goto done;
 
-       if (res == 0) {
-               eh = (struct ethhdr *)(txp->data);
-               type = ntohs(eh->h_proto);
+       eh = (struct ethhdr *)(txp->data);
+       type = ntohs(eh->h_proto);
 
-               if (type == ETH_P_PAE) {
-                       atomic_dec(&ifp->pend_8021x_cnt);
-                       if (waitqueue_active(&ifp->pend_8021x_wait))
-                               wake_up(&ifp->pend_8021x_wait);
-               }
+       if (type == ETH_P_PAE) {
+               atomic_dec(&ifp->pend_8021x_cnt);
+               if (waitqueue_active(&ifp->pend_8021x_wait))
+                       wake_up(&ifp->pend_8021x_wait);
        }
+
        if (!success)
                ifp->stats.tx_errors++;
 done:
@@ -573,13 +571,17 @@ void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success)
 {
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
        struct brcmf_pub *drvr = bus_if->drvr;
+       u8 ifidx;
 
        /* await txstatus signal for firmware if active */
        if (brcmf_fws_fc_active(drvr->fws)) {
                if (!success)
                        brcmf_fws_bustxfail(drvr->fws, txp);
        } else {
-               brcmf_txfinalize(drvr, txp, success);
+               if (brcmf_proto_hdrpull(drvr, false, &ifidx, txp))
+                       brcmu_pkt_buf_free_skb(txp);
+               else
+                       brcmf_txfinalize(drvr, txp, ifidx, success);
        }
 }
 
@@ -914,13 +916,6 @@ int brcmf_bus_start(struct device *dev)
 
        brcmf_dbg(TRACE, "\n");
 
-       /* Bring up the bus */
-       ret = brcmf_bus_init(bus_if);
-       if (ret != 0) {
-               brcmf_err("brcmf_sdbrcm_bus_init failed %d\n", ret);
-               return ret;
-       }
-
        /* add primary networking interface */
        ifp = brcmf_add_if(drvr, 0, 0, "wlan%d", NULL);
        if (IS_ERR(ifp))
index 13c89a0c4ba7ec63f8d7a33688caafa3d407b9f7..8fa0dbbbda72b6ed1d1dc84020594287cf595a37 100644 (file)
@@ -42,7 +42,7 @@
 #include <soc.h>
 #include "sdio_host.h"
 #include "chip.h"
-#include "nvram.h"
+#include "firmware.h"
 
 #define DCMD_RESP_TIMEOUT  2000        /* In milli second */
 
@@ -632,43 +632,28 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = {
        { BCM4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) }
 };
 
-
-static const struct firmware *brcmf_sdio_get_fw(struct brcmf_sdio *bus,
-                                                 enum brcmf_firmware_type type)
+static const char *brcmf_sdio_get_fwname(struct brcmf_chip *ci,
+                                        enum brcmf_firmware_type type)
 {
-       const struct firmware *fw;
-       const char *name;
-       int err, i;
+       int i;
 
        for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) {
-               if (brcmf_fwname_data[i].chipid == bus->ci->chip &&
-                   brcmf_fwname_data[i].revmsk & BIT(bus->ci->chiprev)) {
+               if (brcmf_fwname_data[i].chipid == ci->chip &&
+                   brcmf_fwname_data[i].revmsk & BIT(ci->chiprev)) {
                        switch (type) {
                        case BRCMF_FIRMWARE_BIN:
-                               name = brcmf_fwname_data[i].bin;
-                               break;
+                               return brcmf_fwname_data[i].bin;
                        case BRCMF_FIRMWARE_NVRAM:
-                               name = brcmf_fwname_data[i].nv;
-                               break;
+                               return brcmf_fwname_data[i].nv;
                        default:
                                brcmf_err("invalid firmware type (%d)\n", type);
                                return NULL;
                        }
-                       goto found;
                }
        }
        brcmf_err("Unknown chipid %d [%d]\n",
-                 bus->ci->chip, bus->ci->chiprev);
+                 ci->chip, ci->chiprev);
        return NULL;
-
-found:
-       err = request_firmware(&fw, name, &bus->sdiodev->func[2]->dev);
-       if ((err) || (!fw)) {
-               brcmf_err("fail to request firmware %s (%d)\n", name, err);
-               return NULL;
-       }
-
-       return fw;
 }
 
 static void pkt_align(struct sk_buff *p, int len, int align)
@@ -3278,20 +3263,13 @@ static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus,
 }
 
 static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus,
-                                    const struct firmware *nv)
+                                    void *vars, u32 varsz)
 {
-       void *vars;
-       u32 varsz;
        int address;
        int err;
 
        brcmf_dbg(TRACE, "Enter\n");
 
-       vars = brcmf_nvram_strip(nv, &varsz);
-
-       if (vars == NULL)
-               return -EINVAL;
-
        address = bus->ci->ramsize - varsz + bus->ci->rambase;
        err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, vars, varsz);
        if (err)
@@ -3300,15 +3278,14 @@ static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus,
        else if (!brcmf_sdio_verifymemory(bus->sdiodev, address, vars, varsz))
                err = -EIO;
 
-       brcmf_nvram_free(vars);
-
        return err;
 }
 
-static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus)
+static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus,
+                                       const struct firmware *fw,
+                                       void *nvram, u32 nvlen)
 {
        int bcmerror = -EFAULT;
-       const struct firmware *fw;
        u32 rstvec;
 
        sdio_claim_host(bus->sdiodev->func[1]);
@@ -3317,12 +3294,6 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus)
        /* Keep arm in reset */
        brcmf_chip_enter_download(bus->ci);
 
-       fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_BIN);
-       if (fw == NULL) {
-               bcmerror = -ENOENT;
-               goto err;
-       }
-
        rstvec = get_unaligned_le32(fw->data);
        brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec);
 
@@ -3330,17 +3301,12 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus)
        release_firmware(fw);
        if (bcmerror) {
                brcmf_err("dongle image file download failed\n");
+               brcmf_fw_nvram_free(nvram);
                goto err;
        }
 
-       fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_NVRAM);
-       if (fw == NULL) {
-               bcmerror = -ENOENT;
-               goto err;
-       }
-
-       bcmerror = brcmf_sdio_download_nvram(bus, fw);
-       release_firmware(fw);
+       bcmerror = brcmf_sdio_download_nvram(bus, nvram, nvlen);
+       brcmf_fw_nvram_free(nvram);
        if (bcmerror) {
                brcmf_err("dongle nvram file download failed\n");
                goto err;
@@ -3490,97 +3456,6 @@ done:
        return err;
 }
 
-static int brcmf_sdio_bus_init(struct device *dev)
-{
-       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;
-       int err, ret = 0;
-       u8 saveclk;
-
-       brcmf_dbg(TRACE, "Enter\n");
-
-       /* try to download image and nvram to the dongle */
-       if (bus_if->state == BRCMF_BUS_DOWN) {
-               bus->alp_only = true;
-               err = brcmf_sdio_download_firmware(bus);
-               if (err)
-                       return err;
-               bus->alp_only = false;
-       }
-
-       if (!bus->sdiodev->bus_if->drvr)
-               return 0;
-
-       /* Start the watchdog timer */
-       bus->sdcnt.tickcnt = 0;
-       brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
-
-       sdio_claim_host(bus->sdiodev->func[1]);
-
-       /* Make sure backplane clock is on, needed to generate F2 interrupt */
-       brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
-       if (bus->clkstate != CLK_AVAIL)
-               goto exit;
-
-       /* Force clocks on backplane to be sure F2 interrupt propagates */
-       saveclk = brcmf_sdiod_regrb(bus->sdiodev,
-                                   SBSDIO_FUNC1_CHIPCLKCSR, &err);
-       if (!err) {
-               brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
-                                 (saveclk | SBSDIO_FORCE_HT), &err);
-       }
-       if (err) {
-               brcmf_err("Failed to force clock for F2: err %d\n", err);
-               goto exit;
-       }
-
-       /* Enable function 2 (frame transfers) */
-       w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT,
-                 offsetof(struct sdpcmd_regs, tosbmailboxdata));
-       err = sdio_enable_func(bus->sdiodev->func[SDIO_FUNC_2]);
-
-
-       brcmf_dbg(INFO, "enable F2: err=%d\n", err);
-
-       /* If F2 successfully enabled, set core and enable interrupts */
-       if (!err) {
-               /* Set up the interrupt mask and enable interrupts */
-               bus->hostintmask = HOSTINTMASK;
-               w_sdreg32(bus, bus->hostintmask,
-                         offsetof(struct sdpcmd_regs, hostintmask));
-
-               brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_WATERMARK, 8, &err);
-       } else {
-               /* Disable F2 again */
-               sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]);
-               ret = -ENODEV;
-       }
-
-       if (brcmf_chip_sr_capable(bus->ci)) {
-               brcmf_sdio_sr_init(bus);
-       } else {
-               /* Restore previous clock setting */
-               brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
-                                 saveclk, &err);
-       }
-
-       if (ret == 0) {
-               ret = brcmf_sdiod_intr_register(bus->sdiodev);
-               if (ret != 0)
-                       brcmf_err("intr register failed:%d\n", ret);
-       }
-
-       /* If we didn't come up, turn off backplane clock */
-       if (ret != 0)
-               brcmf_sdio_clkctl(bus, CLK_NONE, false);
-
-exit:
-       sdio_release_host(bus->sdiodev->func[1]);
-
-       return ret;
-}
-
 void brcmf_sdio_isr(struct brcmf_sdio *bus)
 {
        brcmf_dbg(TRACE, "Enter\n");
@@ -4020,13 +3895,114 @@ brcmf_sdio_watchdog(unsigned long data)
 static struct brcmf_bus_ops brcmf_sdio_bus_ops = {
        .stop = brcmf_sdio_bus_stop,
        .preinit = brcmf_sdio_bus_preinit,
-       .init = brcmf_sdio_bus_init,
        .txdata = brcmf_sdio_bus_txdata,
        .txctl = brcmf_sdio_bus_txctl,
        .rxctl = brcmf_sdio_bus_rxctl,
        .gettxq = brcmf_sdio_bus_gettxq,
 };
 
+static void brcmf_sdio_firmware_callback(struct device *dev,
+                                        const struct firmware *code,
+                                        void *nvram, u32 nvram_len)
+{
+       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;
+       int err = 0;
+       u8 saveclk;
+
+       brcmf_dbg(TRACE, "Enter: dev=%s\n", dev_name(dev));
+
+       /* try to download image and nvram to the dongle */
+       if (bus_if->state == BRCMF_BUS_DOWN) {
+               bus->alp_only = true;
+               err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len);
+               if (err)
+                       goto fail;
+               bus->alp_only = false;
+       }
+
+       if (!bus_if->drvr)
+               return;
+
+       /* Start the watchdog timer */
+       bus->sdcnt.tickcnt = 0;
+       brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
+
+       sdio_claim_host(sdiodev->func[1]);
+
+       /* Make sure backplane clock is on, needed to generate F2 interrupt */
+       brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
+       if (bus->clkstate != CLK_AVAIL)
+               goto release;
+
+       /* Force clocks on backplane to be sure F2 interrupt propagates */
+       saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err);
+       if (!err) {
+               brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+                                 (saveclk | SBSDIO_FORCE_HT), &err);
+       }
+       if (err) {
+               brcmf_err("Failed to force clock for F2: err %d\n", err);
+               goto release;
+       }
+
+       /* Enable function 2 (frame transfers) */
+       w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT,
+                 offsetof(struct sdpcmd_regs, tosbmailboxdata));
+       err = sdio_enable_func(sdiodev->func[SDIO_FUNC_2]);
+
+
+       brcmf_dbg(INFO, "enable F2: err=%d\n", err);
+
+       /* If F2 successfully enabled, set core and enable interrupts */
+       if (!err) {
+               /* Set up the interrupt mask and enable interrupts */
+               bus->hostintmask = HOSTINTMASK;
+               w_sdreg32(bus, bus->hostintmask,
+                         offsetof(struct sdpcmd_regs, hostintmask));
+
+               brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, 8, &err);
+       } else {
+               /* Disable F2 again */
+               sdio_disable_func(sdiodev->func[SDIO_FUNC_2]);
+               goto release;
+       }
+
+       if (brcmf_chip_sr_capable(bus->ci)) {
+               brcmf_sdio_sr_init(bus);
+       } else {
+               /* Restore previous clock setting */
+               brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+                                 saveclk, &err);
+       }
+
+       if (err == 0) {
+               err = brcmf_sdiod_intr_register(sdiodev);
+               if (err != 0)
+                       brcmf_err("intr register failed:%d\n", err);
+       }
+
+       /* If we didn't come up, turn off backplane clock */
+       if (err != 0)
+               brcmf_sdio_clkctl(bus, CLK_NONE, false);
+
+       sdio_release_host(sdiodev->func[1]);
+
+       err = brcmf_bus_start(dev);
+       if (err != 0) {
+               brcmf_err("dongle is not responding\n");
+               goto fail;
+       }
+       return;
+
+release:
+       sdio_release_host(sdiodev->func[1]);
+fail:
+       brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), err);
+       device_release_driver(dev);
+}
+
 struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
 {
        int ret;
@@ -4110,8 +4086,13 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
                goto fail;
        }
 
+       /* Query the F2 block size, set roundup accordingly */
+       bus->blocksize = bus->sdiodev->func[2]->cur_blksize;
+       bus->roundup = min(max_roundup, bus->blocksize);
+
        /* Allocate buffers */
        if (bus->sdiodev->bus_if->maxctl) {
+               bus->sdiodev->bus_if->maxctl += bus->roundup;
                bus->rxblen =
                    roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN),
                            ALIGNMENT) + bus->head_align;
@@ -4139,10 +4120,6 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
        bus->idletime = BRCMF_IDLE_INTERVAL;
        bus->idleclock = BRCMF_IDLE_ACTIVE;
 
-       /* Query the F2 block size, set roundup accordingly */
-       bus->blocksize = bus->sdiodev->func[2]->cur_blksize;
-       bus->roundup = min(max_roundup, bus->blocksize);
-
        /* SR state */
        bus->sleeping = false;
        bus->sr_enabled = false;
@@ -4150,10 +4127,14 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
        brcmf_sdio_debugfs_create(bus);
        brcmf_dbg(INFO, "completed!!\n");
 
-       /* if firmware path present try to download and bring up bus */
-       ret = brcmf_bus_start(bus->sdiodev->dev);
+       ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM,
+                                    brcmf_sdio_get_fwname(bus->ci,
+                                                          BRCMF_FIRMWARE_BIN),
+                                    brcmf_sdio_get_fwname(bus->ci,
+                                                          BRCMF_FIRMWARE_NVRAM),
+                                    brcmf_sdio_firmware_callback);
        if (ret != 0) {
-               brcmf_err("dongle is not responding\n");
+               brcmf_err("async firmware request failed: %d\n", ret);
                goto fail;
        }
 
@@ -4173,9 +4154,7 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus)
                /* De-register interrupt handler */
                brcmf_sdiod_intr_unregister(bus->sdiodev);
 
-               if (bus->sdiodev->bus_if->drvr) {
-                       brcmf_detach(bus->sdiodev->dev);
-               }
+               brcmf_detach(bus->sdiodev->dev);
 
                cancel_work_sync(&bus->datawork);
                if (bus->brcmf_wq)
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
new file mode 100644 (file)
index 0000000..7b7d237
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 2013 Broadcom Corporation
+ *
+ * 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/kernel.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include "dhd_dbg.h"
+#include "firmware.h"
+
+enum nvram_parser_state {
+       IDLE,
+       KEY,
+       VALUE,
+       COMMENT,
+       END
+};
+
+/**
+ * struct nvram_parser - internal info for parser.
+ *
+ * @state: current parser state.
+ * @fwnv: input buffer being parsed.
+ * @nvram: output buffer with parse result.
+ * @nvram_len: lenght of parse result.
+ * @line: current line.
+ * @column: current column in line.
+ * @pos: byte offset in input buffer.
+ * @entry: start position of key,value entry.
+ */
+struct nvram_parser {
+       enum nvram_parser_state state;
+       const struct firmware *fwnv;
+       u8 *nvram;
+       u32 nvram_len;
+       u32 line;
+       u32 column;
+       u32 pos;
+       u32 entry;
+};
+
+static bool is_nvram_char(char c)
+{
+       /* comment marker excluded */
+       if (c == '#')
+               return false;
+
+       /* key and value may have any other readable character */
+       return (c > 0x20 && c < 0x7f);
+}
+
+static bool is_whitespace(char c)
+{
+       return (c == ' ' || c == '\r' || c == '\n' || c == '\t');
+}
+
+static enum nvram_parser_state brcmf_nvram_handle_idle(struct nvram_parser *nvp)
+{
+       char c;
+
+       c = nvp->fwnv->data[nvp->pos];
+       if (c == '\n')
+               return COMMENT;
+       if (is_whitespace(c))
+               goto proceed;
+       if (c == '#')
+               return COMMENT;
+       if (is_nvram_char(c)) {
+               nvp->entry = nvp->pos;
+               return KEY;
+       }
+       brcmf_dbg(INFO, "warning: ln=%d:col=%d: ignoring invalid character\n",
+                 nvp->line, nvp->column);
+proceed:
+       nvp->column++;
+       nvp->pos++;
+       return IDLE;
+}
+
+static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp)
+{
+       enum nvram_parser_state st = nvp->state;
+       char c;
+
+       c = nvp->fwnv->data[nvp->pos];
+       if (c == '=') {
+               st = VALUE;
+       } else if (!is_nvram_char(c)) {
+               brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n",
+                         nvp->line, nvp->column);
+               return COMMENT;
+       }
+
+       nvp->column++;
+       nvp->pos++;
+       return st;
+}
+
+static enum nvram_parser_state
+brcmf_nvram_handle_value(struct nvram_parser *nvp)
+{
+       char c;
+       char *skv;
+       char *ekv;
+       u32 cplen;
+
+       c = nvp->fwnv->data[nvp->pos];
+       if (!is_nvram_char(c)) {
+               /* key,value pair complete */
+               ekv = (u8 *)&nvp->fwnv->data[nvp->pos];
+               skv = (u8 *)&nvp->fwnv->data[nvp->entry];
+               cplen = ekv - skv;
+               /* copy to output buffer */
+               memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen);
+               nvp->nvram_len += cplen;
+               nvp->nvram[nvp->nvram_len] = '\0';
+               nvp->nvram_len++;
+               return IDLE;
+       }
+       nvp->pos++;
+       nvp->column++;
+       return VALUE;
+}
+
+static enum nvram_parser_state
+brcmf_nvram_handle_comment(struct nvram_parser *nvp)
+{
+       char *eol, *sol;
+
+       sol = (char *)&nvp->fwnv->data[nvp->pos];
+       eol = strchr(sol, '\n');
+       if (eol == NULL)
+               return END;
+
+       /* eat all moving to next line */
+       nvp->line++;
+       nvp->column = 1;
+       nvp->pos += (eol - sol) + 1;
+       return IDLE;
+}
+
+static enum nvram_parser_state brcmf_nvram_handle_end(struct nvram_parser *nvp)
+{
+       /* final state */
+       return END;
+}
+
+static enum nvram_parser_state
+(*nv_parser_states[])(struct nvram_parser *nvp) = {
+       brcmf_nvram_handle_idle,
+       brcmf_nvram_handle_key,
+       brcmf_nvram_handle_value,
+       brcmf_nvram_handle_comment,
+       brcmf_nvram_handle_end
+};
+
+static int brcmf_init_nvram_parser(struct nvram_parser *nvp,
+                                  const struct firmware *nv)
+{
+       memset(nvp, 0, sizeof(*nvp));
+       nvp->fwnv = nv;
+       /* Alloc for extra 0 byte + roundup by 4 + length field */
+       nvp->nvram = kzalloc(nv->size + 1 + 3 + sizeof(u32), GFP_KERNEL);
+       if (!nvp->nvram)
+               return -ENOMEM;
+
+       nvp->line = 1;
+       nvp->column = 1;
+       return 0;
+}
+
+/* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a fil
+ * and ending in a NUL. Removes carriage returns, empty lines, comment lines,
+ * and converts newlines to NULs. Shortens buffer as needed and pads with NULs.
+ * End of buffer is completed with token identifying length of buffer.
+ */
+static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length)
+{
+       struct nvram_parser nvp;
+       u32 pad;
+       u32 token;
+       __le32 token_le;
+
+       if (brcmf_init_nvram_parser(&nvp, nv) < 0)
+               return NULL;
+
+       while (nvp.pos < nv->size) {
+               nvp.state = nv_parser_states[nvp.state](&nvp);
+               if (nvp.state == END)
+                       break;
+       }
+       pad = nvp.nvram_len;
+       *new_length = roundup(nvp.nvram_len + 1, 4);
+       while (pad != *new_length) {
+               nvp.nvram[pad] = 0;
+               pad++;
+       }
+
+       token = *new_length / 4;
+       token = (~token << 16) | (token & 0x0000FFFF);
+       token_le = cpu_to_le32(token);
+
+       memcpy(&nvp.nvram[*new_length], &token_le, sizeof(token_le));
+       *new_length += sizeof(token_le);
+
+       return nvp.nvram;
+}
+
+void brcmf_fw_nvram_free(void *nvram)
+{
+       kfree(nvram);
+}
+
+struct brcmf_fw {
+       struct device *dev;
+       u16 flags;
+       const struct firmware *code;
+       const char *nvram_name;
+       void (*done)(struct device *dev, const struct firmware *fw,
+                    void *nvram_image, u32 nvram_len);
+};
+
+static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
+{
+       struct brcmf_fw *fwctx = ctx;
+       u32 nvram_length = 0;
+       void *nvram = NULL;
+
+       brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
+       if (!fw && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
+               goto fail;
+
+       if (fw) {
+               nvram = brcmf_fw_nvram_strip(fw, &nvram_length);
+               release_firmware(fw);
+               if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
+                       goto fail;
+       }
+
+       fwctx->done(fwctx->dev, fwctx->code, nvram, nvram_length);
+       kfree(fwctx);
+       return;
+
+fail:
+       brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
+       if (fwctx->code)
+               release_firmware(fwctx->code);
+       device_release_driver(fwctx->dev);
+       kfree(fwctx);
+}
+
+static void brcmf_fw_request_code_done(const struct firmware *fw, void *ctx)
+{
+       struct brcmf_fw *fwctx = ctx;
+       int ret;
+
+       brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
+       if (!fw)
+               goto fail;
+
+       /* only requested code so done here */
+       if (!(fwctx->flags & BRCMF_FW_REQUEST_NVRAM)) {
+               fwctx->done(fwctx->dev, fw, NULL, 0);
+               kfree(fwctx);
+               return;
+       }
+       fwctx->code = fw;
+       ret = request_firmware_nowait(THIS_MODULE, true, fwctx->nvram_name,
+                                     fwctx->dev, GFP_KERNEL, fwctx,
+                                     brcmf_fw_request_nvram_done);
+
+       if (!ret)
+               return;
+
+       /* when nvram is optional call .done() callback here */
+       if (fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL) {
+               fwctx->done(fwctx->dev, fw, NULL, 0);
+               kfree(fwctx);
+               return;
+       }
+
+       /* failed nvram request */
+       release_firmware(fw);
+fail:
+       brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
+       device_release_driver(fwctx->dev);
+       kfree(fwctx);
+}
+
+int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
+                          const char *code, const char *nvram,
+                          void (*fw_cb)(struct device *dev,
+                                        const struct firmware *fw,
+                                        void *nvram_image, u32 nvram_len))
+{
+       struct brcmf_fw *fwctx;
+
+       brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev));
+       if (!fw_cb || !code)
+               return -EINVAL;
+
+       if ((flags & BRCMF_FW_REQUEST_NVRAM) && !nvram)
+               return -EINVAL;
+
+       fwctx = kzalloc(sizeof(*fwctx), GFP_KERNEL);
+       if (!fwctx)
+               return -ENOMEM;
+
+       fwctx->dev = dev;
+       fwctx->flags = flags;
+       fwctx->done = fw_cb;
+       if (flags & BRCMF_FW_REQUEST_NVRAM)
+               fwctx->nvram_name = nvram;
+
+       return request_firmware_nowait(THIS_MODULE, true, code, dev,
+                                      GFP_KERNEL, fwctx,
+                                      brcmf_fw_request_code_done);
+}
similarity index 52%
rename from drivers/net/wireless/brcm80211/brcmfmac/nvram.h
rename to drivers/net/wireless/brcm80211/brcmfmac/firmware.h
index d454580928c9ab2e41b84bc35032065570e4cdf5..6431bfd7afffc6c544fa1e292b3a6a98cfd4a664 100644 (file)
  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifndef BRCMFMAC_NVRAM_H
-#define BRCMFMAC_NVRAM_H
+#ifndef BRCMFMAC_FIRMWARE_H
+#define BRCMFMAC_FIRMWARE_H
 
+#define BRCMF_FW_REQUEST               0x000F
+#define  BRCMF_FW_REQUEST_NVRAM                0x0001
+#define BRCMF_FW_REQ_FLAGS             0x00F0
+#define  BRCMF_FW_REQ_NV_OPTIONAL      0x0010
 
-void *brcmf_nvram_strip(const struct firmware *nv, u32 *new_length);
-void brcmf_nvram_free(void *nvram);
-
+void brcmf_fw_nvram_free(void *nvram);
+/*
+ * Request firmware(s) asynchronously. When the asynchronous request
+ * fails it will not use the callback, but call device_release_driver()
+ * instead which will call the driver .remove() callback.
+ */
+int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
+                          const char *code, const char *nvram,
+                          void (*fw_cb)(struct device *dev,
+                                        const struct firmware *fw,
+                                        void *nvram_image, u32 nvram_len));
 
-#endif /* BRCMFMAC_NVRAM_H */
+#endif /* BRCMFMAC_FIRMWARE_H */
index 614e4888504fae4f517389bfb49c4d08a5519924..2bc68a2137fccb535f5a34fb34e6394db85ec645 100644 (file)
 #define BRCMF_OBSS_COEX_OFF            0
 #define BRCMF_OBSS_COEX_ON             1
 
+/* join preference types for join_pref iovar */
+enum brcmf_join_pref_types {
+       BRCMF_JOIN_PREF_RSSI = 1,
+       BRCMF_JOIN_PREF_WPA,
+       BRCMF_JOIN_PREF_BAND,
+       BRCMF_JOIN_PREF_RSSI_DELTA,
+};
+
 enum brcmf_fil_p2p_if_types {
        BRCMF_FIL_P2P_IF_CLIENT,
        BRCMF_FIL_P2P_IF_GO,
@@ -282,6 +290,22 @@ struct brcmf_assoc_params_le {
        __le16 chanspec_list[1];
 };
 
+/**
+ * struct join_pref params - parameters for preferred join selection.
+ *
+ * @type: preference type (see enum brcmf_join_pref_types).
+ * @len: length of bytes following (currently always 2).
+ * @rssi_gain: signal gain for selection (only when @type is RSSI_DELTA).
+ * @band: band to which selection preference applies.
+ *     This is used if @type is BAND or RSSI_DELTA.
+ */
+struct brcmf_join_pref_params {
+       u8 type;
+       u8 len;
+       u8 rssi_gain;
+       u8 band;
+};
+
 /* used for join with or without a specific bssid and channel list */
 struct brcmf_join_params {
        struct brcmf_ssid_le ssid_le;
index c3e7d76dbf35f508e33a1e1d88164ede54b1967a..699908de314a94ff3f382f62536756b3f4a5eed2 100644 (file)
@@ -476,6 +476,7 @@ struct brcmf_fws_info {
        bool bus_flow_blocked;
        bool creditmap_received;
        u8 mode;
+       bool avoid_queueing;
 };
 
 /*
@@ -1369,13 +1370,12 @@ done:
 }
 
 static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
-                                        struct sk_buff *skb, u32 genbit,
-                                        u16 seq)
+                                        struct sk_buff *skb, u8 ifidx,
+                                        u32 genbit, u16 seq)
 {
        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);
 
@@ -1389,29 +1389,21 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
 
        entry->generation = genbit;
 
-       ret = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
-       if (ret == 0) {
-               brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit);
-               brcmf_skbcb(skb)->htod_seq = seq;
-               if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) {
-                       brcmf_skb_htod_seq_set_field(skb, FROMDRV, 1);
-                       brcmf_skb_htod_seq_set_field(skb, FROMFW, 0);
-               } else {
-                       brcmf_skb_htod_seq_set_field(skb, FROMDRV, 0);
-               }
-               ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo,
-                                   skb);
+       brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit);
+       brcmf_skbcb(skb)->htod_seq = seq;
+       if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) {
+               brcmf_skb_htod_seq_set_field(skb, FROMDRV, 1);
+               brcmf_skb_htod_seq_set_field(skb, FROMFW, 0);
+       } else {
+               brcmf_skb_htod_seq_set_field(skb, FROMDRV, 0);
        }
+       ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb);
 
        if (ret != 0) {
-               /* suppress q is full or hdrpull failed, drop this packet */
-               brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
-                                       true);
+               /* suppress q is full drop this packet */
+               brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, true);
        } else {
-               /*
-                * Mark suppressed to avoid a double free during
-                * wlfc cleanup
-                */
+               /* Mark suppressed to avoid a double free during wlfc cleanup */
                brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot);
        }
 
@@ -1428,6 +1420,7 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
        struct sk_buff *skb;
        struct brcmf_skbuff_cb *skcb;
        struct brcmf_fws_mac_descriptor *entry = NULL;
+       u8 ifidx;
 
        brcmf_dbg(DATA, "flags %d\n", flags);
 
@@ -1476,12 +1469,15 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
        }
        brcmf_fws_macdesc_return_req_credit(skb);
 
+       if (brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb)) {
+               brcmu_pkt_buf_free_skb(skb);
+               return -EINVAL;
+       }
        if (!remove_from_hanger)
-               ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, genbit,
-                                                   seq);
-
+               ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifidx,
+                                                   genbit, seq);
        if (remove_from_hanger || ret)
-               brcmf_txfinalize(fws->drvr, skb, true);
+               brcmf_txfinalize(fws->drvr, skb, ifidx, true);
 
        return 0;
 }
@@ -1868,7 +1864,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
        struct ethhdr *eh = (struct ethhdr *)(skb->data);
        int fifo = BRCMF_FWS_FIFO_BCMC;
        bool multicast = is_multicast_ether_addr(eh->h_dest);
-       bool pae = eh->h_proto == htons(ETH_P_PAE);
+       int rc = 0;
 
        brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto));
        /* determine the priority */
@@ -1876,8 +1872,13 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
                skb->priority = cfg80211_classify8021d(skb, NULL);
 
        drvr->tx_multicast += !!multicast;
-       if (pae)
-               atomic_inc(&ifp->pend_8021x_cnt);
+
+       if (fws->avoid_queueing) {
+               rc = brcmf_proto_txdata(drvr, ifp->ifidx, 0, skb);
+               if (rc < 0)
+                       brcmf_txfinalize(drvr, skb, ifp->ifidx, false);
+               return rc;
+       }
 
        /* set control buffer information */
        skcb->if_flags = 0;
@@ -1899,15 +1900,12 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
                brcmf_fws_schedule_deq(fws);
        } else {
                brcmf_err("drop skb: no hanger slot\n");
-               if (pae) {
-                       atomic_dec(&ifp->pend_8021x_cnt);
-                       if (waitqueue_active(&ifp->pend_8021x_wait))
-                               wake_up(&ifp->pend_8021x_wait);
-               }
-               brcmu_pkt_buf_free_skb(skb);
+               brcmf_txfinalize(drvr, skb, ifp->ifidx, false);
+               rc = -ENOMEM;
        }
        brcmf_fws_unlock(fws);
-       return 0;
+
+       return rc;
 }
 
 void brcmf_fws_reset_interface(struct brcmf_if *ifp)
@@ -1982,7 +1980,8 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
                                ret = brcmf_proto_txdata(drvr, ifidx, 0, skb);
                                brcmf_fws_lock(fws);
                                if (ret < 0)
-                                       brcmf_txfinalize(drvr, skb, false);
+                                       brcmf_txfinalize(drvr, skb, ifidx,
+                                                        false);
                                if (fws->bus_flow_blocked)
                                        break;
                        }
@@ -2039,6 +2038,13 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
        fws->drvr = drvr;
        fws->fcmode = fcmode;
 
+       if ((drvr->bus_if->always_use_fws_queue == false) &&
+           (fcmode == BRCMF_FWS_FCMODE_NONE)) {
+               fws->avoid_queueing = true;
+               brcmf_dbg(INFO, "FWS queueing will be avoided\n");
+               return 0;
+       }
+
        fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq");
        if (fws->fws_wq == NULL) {
                brcmf_err("workqueue creation failed\n");
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/nvram.c b/drivers/net/wireless/brcm80211/brcmfmac/nvram.c
deleted file mode 100644 (file)
index d5ef86d..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (c) 2013 Broadcom Corporation
- *
- * 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/kernel.h>
-#include <linux/slab.h>
-#include <linux/firmware.h>
-
-#include "nvram.h"
-
-/* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a file
- * and ending in a NUL. Removes carriage returns, empty lines, comment lines,
- * and converts newlines to NULs. Shortens buffer as needed and pads with NULs.
- * End of buffer is completed with token identifying length of buffer.
- */
-void *brcmf_nvram_strip(const struct firmware *nv, u32 *new_length)
-{
-       u8 *nvram;
-       u32 i;
-       u32 len;
-       u32 column;
-       u8 val;
-       bool comment;
-       u32 token;
-       __le32 token_le;
-
-       /* Alloc for extra 0 byte + roundup by 4 + length field */
-       nvram = kmalloc(nv->size + 1 + 3 + sizeof(token_le), GFP_KERNEL);
-       if (!nvram)
-               return NULL;
-
-       len = 0;
-       column = 0;
-       comment = false;
-       for (i = 0; i < nv->size; i++) {
-               val = nv->data[i];
-               if (val == 0)
-                       break;
-               if (val == '\r')
-                       continue;
-               if (comment && (val != '\n'))
-                       continue;
-               comment = false;
-               if (val == '#') {
-                       comment = true;
-                       continue;
-               }
-               if (val == '\n') {
-                       if (column == 0)
-                               continue;
-                       nvram[len] = 0;
-                       len++;
-                       column = 0;
-                       continue;
-               }
-               nvram[len] = val;
-               len++;
-               column++;
-       }
-       column = len;
-       *new_length = roundup(len + 1, 4);
-       while (column != *new_length) {
-               nvram[column] = 0;
-               column++;
-       }
-
-       token = *new_length / 4;
-       token = (~token << 16) | (token & 0x0000FFFF);
-       token_le = cpu_to_le32(token);
-
-       memcpy(&nvram[*new_length], &token_le, sizeof(token_le));
-       *new_length += sizeof(token_le);
-
-       return nvram;
-}
-
-void brcmf_nvram_free(void *nvram)
-{
-       kfree(nvram);
-}
-
-
index 24f65cd538595a84e6c66b62b8698bbc97456288..6db51a666f619abedaee11ac4822b917b24b4f3c 100644 (file)
@@ -25,6 +25,7 @@
 #include <dhd_bus.h>
 #include <dhd_dbg.h>
 
+#include "firmware.h"
 #include "usb_rdl.h"
 #include "usb.h"
 
@@ -61,12 +62,6 @@ struct brcmf_usb_image {
        u8 *image;
        int image_len;
 };
-static struct list_head fw_image_list;
-
-struct intr_transfer_buf {
-       u32 notification;
-       u32 reserved;
-};
 
 struct brcmf_usbdev_info {
        struct brcmf_usbdev bus_pub; /* MUST BE FIRST */
@@ -75,7 +70,7 @@ struct brcmf_usbdev_info {
        struct list_head rx_postq;
        struct list_head tx_freeq;
        struct list_head tx_postq;
-       uint rx_pipe, tx_pipe, intr_pipe, rx_pipe2;
+       uint rx_pipe, tx_pipe, rx_pipe2;
 
        int rx_low_watermark;
        int tx_low_watermark;
@@ -87,7 +82,7 @@ struct brcmf_usbdev_info {
        struct brcmf_usbreq *tx_reqs;
        struct brcmf_usbreq *rx_reqs;
 
-       u8 *image;      /* buffer for combine fw and nvram */
+       const u8 *image;        /* buffer for combine fw and nvram */
        int image_len;
 
        struct usb_device *usbdev;
@@ -104,10 +99,6 @@ struct brcmf_usbdev_info {
        ulong ctl_op;
 
        struct urb *bulk_urb; /* used for FW download */
-       struct urb *intr_urb; /* URB for interrupt endpoint */
-       int intr_size;          /* Size of interrupt message */
-       int interval;           /* Interrupt polling interval */
-       struct intr_transfer_buf intr; /* Data buffer for interrupt endpoint */
 };
 
 static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo,
@@ -531,39 +522,6 @@ brcmf_usb_state_change(struct brcmf_usbdev_info *devinfo, int state)
        }
 }
 
-static void
-brcmf_usb_intr_complete(struct urb *urb)
-{
-       struct brcmf_usbdev_info *devinfo =
-                       (struct brcmf_usbdev_info *)urb->context;
-       int err;
-
-       brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status);
-
-       if (devinfo == NULL)
-               return;
-
-       if (unlikely(urb->status)) {
-               if (urb->status == -ENOENT ||
-                   urb->status == -ESHUTDOWN ||
-                   urb->status == -ENODEV) {
-                       brcmf_usb_state_change(devinfo,
-                                              BRCMFMAC_USB_STATE_DOWN);
-               }
-       }
-
-       if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_DOWN) {
-               brcmf_err("intr cb when DBUS down, ignoring\n");
-               return;
-       }
-
-       if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) {
-               err = usb_submit_urb(devinfo->intr_urb, GFP_ATOMIC);
-               if (err)
-                       brcmf_err("usb_submit_urb, err=%d\n", err);
-       }
-}
-
 static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
 {
        struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
@@ -619,7 +577,6 @@ static int brcmf_usb_up(struct device *dev)
 {
        struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
        u16 ifnum;
-       int ret;
 
        brcmf_dbg(USB, "Enter\n");
        if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP)
@@ -628,23 +585,6 @@ static int brcmf_usb_up(struct device *dev)
        /* Success, indicate devinfo is fully up */
        brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_UP);
 
-       if (devinfo->intr_urb) {
-               usb_fill_int_urb(devinfo->intr_urb, devinfo->usbdev,
-                       devinfo->intr_pipe,
-                       &devinfo->intr,
-                       devinfo->intr_size,
-                       (usb_complete_t)brcmf_usb_intr_complete,
-                       devinfo,
-                       devinfo->interval);
-
-               ret = usb_submit_urb(devinfo->intr_urb, GFP_ATOMIC);
-               if (ret) {
-                       brcmf_err("USB_SUBMIT_URB failed with status %d\n",
-                                 ret);
-                       return -EINVAL;
-               }
-       }
-
        if (devinfo->ctl_urb) {
                devinfo->ctl_in_pipe = usb_rcvctrlpipe(devinfo->usbdev, 0);
                devinfo->ctl_out_pipe = usb_sndctrlpipe(devinfo->usbdev, 0);
@@ -681,8 +621,6 @@ static void brcmf_usb_down(struct device *dev)
                return;
 
        brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_DOWN);
-       if (devinfo->intr_urb)
-               usb_kill_urb(devinfo->intr_urb);
 
        if (devinfo->ctl_urb)
                usb_kill_urb(devinfo->ctl_urb);
@@ -1021,7 +959,7 @@ brcmf_usb_fw_download(struct brcmf_usbdev_info *devinfo)
        }
 
        err = brcmf_usb_dlstart(devinfo,
-               devinfo->image, devinfo->image_len);
+               (u8 *)devinfo->image, devinfo->image_len);
        if (err == 0)
                err = brcmf_usb_dlrun(devinfo);
        return err;
@@ -1036,7 +974,6 @@ static void brcmf_usb_detach(struct brcmf_usbdev_info *devinfo)
        brcmf_usb_free_q(&devinfo->rx_freeq, false);
        brcmf_usb_free_q(&devinfo->tx_freeq, false);
 
-       usb_free_urb(devinfo->intr_urb);
        usb_free_urb(devinfo->ctl_urb);
        usb_free_urb(devinfo->bulk_urb);
 
@@ -1080,68 +1017,20 @@ static int check_file(const u8 *headers)
        return -1;
 }
 
-static int brcmf_usb_get_fw(struct brcmf_usbdev_info *devinfo)
+static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo)
 {
-       s8 *fwname;
-       const struct firmware *fw;
-       struct brcmf_usb_image *fw_image;
-       int err;
-
-       brcmf_dbg(USB, "Enter\n");
        switch (devinfo->bus_pub.devid) {
        case 43143:
-               fwname = BRCMF_USB_43143_FW_NAME;
-               break;
+               return BRCMF_USB_43143_FW_NAME;
        case 43235:
        case 43236:
        case 43238:
-               fwname = BRCMF_USB_43236_FW_NAME;
-               break;
+               return BRCMF_USB_43236_FW_NAME;
        case 43242:
-               fwname = BRCMF_USB_43242_FW_NAME;
-               break;
+               return BRCMF_USB_43242_FW_NAME;
        default:
-               return -EINVAL;
-               break;
-       }
-       brcmf_dbg(USB, "Loading FW %s\n", fwname);
-       list_for_each_entry(fw_image, &fw_image_list, list) {
-               if (fw_image->fwname == fwname) {
-                       devinfo->image = fw_image->image;
-                       devinfo->image_len = fw_image->image_len;
-                       return 0;
-               }
-       }
-       /* fw image not yet loaded. Load it now and add to list */
-       err = request_firmware(&fw, fwname, devinfo->dev);
-       if (!fw) {
-               brcmf_err("fail to request firmware %s\n", fwname);
-               return err;
-       }
-       if (check_file(fw->data) < 0) {
-               brcmf_err("invalid firmware %s\n", fwname);
-               return -EINVAL;
+               return NULL;
        }
-
-       fw_image = kzalloc(sizeof(*fw_image), GFP_ATOMIC);
-       if (!fw_image)
-               return -ENOMEM;
-       INIT_LIST_HEAD(&fw_image->list);
-       list_add_tail(&fw_image->list, &fw_image_list);
-       fw_image->fwname = fwname;
-       fw_image->image = vmalloc(fw->size);
-       if (!fw_image->image)
-               return -ENOMEM;
-
-       memcpy(fw_image->image, fw->data, fw->size);
-       fw_image->image_len = fw->size;
-
-       release_firmware(fw);
-
-       devinfo->image = fw_image->image;
-       devinfo->image_len = fw_image->image_len;
-
-       return 0;
 }
 
 
@@ -1186,11 +1075,6 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo,
                goto error;
        devinfo->tx_freecount = ntxq;
 
-       devinfo->intr_urb = usb_alloc_urb(0, GFP_ATOMIC);
-       if (!devinfo->intr_urb) {
-               brcmf_err("usb_alloc_urb (intr) failed\n");
-               goto error;
-       }
        devinfo->ctl_urb = usb_alloc_urb(0, GFP_ATOMIC);
        if (!devinfo->ctl_urb) {
                brcmf_err("usb_alloc_urb (ctl) failed\n");
@@ -1202,16 +1086,6 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo,
                goto error;
        }
 
-       if (!brcmf_usb_dlneeded(devinfo))
-               return &devinfo->bus_pub;
-
-       brcmf_dbg(USB, "Start fw downloading\n");
-       if (brcmf_usb_get_fw(devinfo))
-               goto error;
-
-       if (brcmf_usb_fw_download(devinfo))
-               goto error;
-
        return &devinfo->bus_pub;
 
 error:
@@ -1222,18 +1096,77 @@ error:
 
 static struct brcmf_bus_ops brcmf_usb_bus_ops = {
        .txdata = brcmf_usb_tx,
-       .init = brcmf_usb_up,
        .stop = brcmf_usb_down,
        .txctl = brcmf_usb_tx_ctlpkt,
        .rxctl = brcmf_usb_rx_ctlpkt,
 };
 
+static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo)
+{
+       int ret;
+
+       /* Attach to the common driver interface */
+       ret = brcmf_attach(devinfo->dev);
+       if (ret) {
+               brcmf_err("brcmf_attach failed\n");
+               return ret;
+       }
+
+       ret = brcmf_usb_up(devinfo->dev);
+       if (ret)
+               goto fail;
+
+       ret = brcmf_bus_start(devinfo->dev);
+       if (ret)
+               goto fail;
+
+       return 0;
+fail:
+       brcmf_detach(devinfo->dev);
+       return ret;
+}
+
+static void brcmf_usb_probe_phase2(struct device *dev,
+                                  const struct firmware *fw,
+                                  void *nvram, u32 nvlen)
+{
+       struct brcmf_bus *bus = dev_get_drvdata(dev);
+       struct brcmf_usbdev_info *devinfo;
+       int ret;
+
+       brcmf_dbg(USB, "Start fw downloading\n");
+       ret = check_file(fw->data);
+       if (ret < 0) {
+               brcmf_err("invalid firmware\n");
+               release_firmware(fw);
+               goto error;
+       }
+
+       devinfo = bus->bus_priv.usb->devinfo;
+       devinfo->image = fw->data;
+       devinfo->image_len = fw->size;
+
+       ret = brcmf_usb_fw_download(devinfo);
+       release_firmware(fw);
+       if (ret)
+               goto error;
+
+       ret = brcmf_usb_bus_setup(devinfo);
+       if (ret)
+               goto error;
+
+       return;
+error:
+       brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), ret);
+       device_release_driver(dev);
+}
+
 static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo)
 {
        struct brcmf_bus *bus = NULL;
        struct brcmf_usbdev *bus_pub = NULL;
-       int ret;
        struct device *dev = devinfo->dev;
+       int ret;
 
        brcmf_dbg(USB, "Enter\n");
        bus_pub = brcmf_usb_attach(devinfo, BRCMF_USB_NRXQ, BRCMF_USB_NTXQ);
@@ -1254,22 +1187,18 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo)
        bus->chip = bus_pub->devid;
        bus->chiprev = bus_pub->chiprev;
        bus->proto_type = BRCMF_PROTO_BCDC;
+       bus->always_use_fws_queue = true;
 
-       /* Attach to the common driver interface */
-       ret = brcmf_attach(dev);
-       if (ret) {
-               brcmf_err("brcmf_attach failed\n");
-               goto fail;
-       }
-
-       ret = brcmf_bus_start(dev);
-       if (ret) {
-               brcmf_err("dongle is not responding\n");
-               brcmf_detach(dev);
-               goto fail;
+       if (!brcmf_usb_dlneeded(devinfo)) {
+               ret = brcmf_usb_bus_setup(devinfo);
+               if (ret)
+                       goto fail;
        }
-
+       /* request firmware here */
+       brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo), NULL,
+                              brcmf_usb_probe_phase2);
        return 0;
+
 fail:
        /* Release resources in reverse order */
        kfree(bus);
@@ -1357,9 +1286,6 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
                goto fail;
        }
 
-       endpoint_num = endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
-       devinfo->intr_pipe = usb_rcvintpipe(usb, endpoint_num);
-
        devinfo->rx_pipe = 0;
        devinfo->rx_pipe2 = 0;
        devinfo->tx_pipe = 0;
@@ -1391,16 +1317,9 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
                }
        }
 
-       /* Allocate interrupt URB and data buffer */
-       /* RNDIS says 8-byte intr, our old drivers used 4-byte */
-       if (IFEPDESC(usb, CONTROL_IF, 0).wMaxPacketSize == cpu_to_le16(16))
-               devinfo->intr_size = 8;
-       else
-               devinfo->intr_size = 4;
-
-       devinfo->interval = IFEPDESC(usb, CONTROL_IF, 0).bInterval;
-
-       if (usb->speed == USB_SPEED_HIGH)
+       if (usb->speed == USB_SPEED_SUPER)
+               brcmf_dbg(USB, "Broadcom super speed USB wireless device detected\n");
+       else if (usb->speed == USB_SPEED_HIGH)
                brcmf_dbg(USB, "Broadcom high speed USB wireless device detected\n");
        else
                brcmf_dbg(USB, "Broadcom full speed USB wireless device detected\n");
@@ -1455,23 +1374,18 @@ static int brcmf_usb_resume(struct usb_interface *intf)
        struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
 
        brcmf_dbg(USB, "Enter\n");
-       if (!brcmf_attach(devinfo->dev))
-               return brcmf_bus_start(&usb->dev);
-
-       return 0;
+       return brcmf_usb_bus_setup(devinfo);
 }
 
 static int brcmf_usb_reset_resume(struct usb_interface *intf)
 {
        struct usb_device *usb = interface_to_usbdev(intf);
        struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
-
        brcmf_dbg(USB, "Enter\n");
 
-       if (!brcmf_usb_fw_download(devinfo))
-               return brcmf_usb_resume(intf);
-
-       return -EIO;
+       return brcmf_fw_get_firmwares(&usb->dev, 0,
+                                     brcmf_usb_get_fwname(devinfo), NULL,
+                                     brcmf_usb_probe_phase2);
 }
 
 #define BRCMF_USB_VENDOR_ID_BROADCOM   0x0a5c
@@ -1506,16 +1420,6 @@ static struct usb_driver brcmf_usbdrvr = {
        .disable_hub_initiated_lpm = 1,
 };
 
-static void brcmf_release_fw(struct list_head *q)
-{
-       struct brcmf_usb_image *fw_image, *next;
-
-       list_for_each_entry_safe(fw_image, next, q, list) {
-               vfree(fw_image->image);
-               list_del_init(&fw_image->list);
-       }
-}
-
 static int brcmf_usb_reset_device(struct device *dev, void *notused)
 {
        /* device past is the usb interface so we
@@ -1534,12 +1438,10 @@ void brcmf_usb_exit(void)
        ret = driver_for_each_device(drv, NULL, NULL,
                                     brcmf_usb_reset_device);
        usb_deregister(&brcmf_usbdrvr);
-       brcmf_release_fw(&fw_image_list);
 }
 
 void brcmf_usb_register(void)
 {
        brcmf_dbg(USB, "Enter\n");
-       INIT_LIST_HEAD(&fw_image_list);
        usb_register(&brcmf_usbdrvr);
 }
index be1985296bdc75e725cdc161aa4101cea4a19476..d8fa276e368b4fc038b62a941191b3d25393b8f6 100644 (file)
@@ -221,9 +221,9 @@ static const struct ieee80211_regdomain brcmf_regdom = {
                 */
                REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
                /* IEEE 802.11a, channel 36..64 */
-               REG_RULE(5150-10, 5350+10, 40, 6, 20, 0),
+               REG_RULE(5150-10, 5350+10, 80, 6, 20, 0),
                /* IEEE 802.11a, channel 100..165 */
-               REG_RULE(5470-10, 5850+10, 40, 6, 20, 0), }
+               REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), }
 };
 
 static const u32 __wl_cipher_suites[] = {
@@ -341,6 +341,60 @@ static u8 brcmf_mw_to_qdbm(u16 mw)
        return qdbm;
 }
 
+static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
+                              struct cfg80211_chan_def *ch)
+{
+       struct brcmu_chan ch_inf;
+       s32 primary_offset;
+
+       brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
+                 ch->chan->center_freq, ch->center_freq1, ch->width);
+       ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
+       primary_offset = ch->center_freq1 - ch->chan->center_freq;
+       switch (ch->width) {
+       case NL80211_CHAN_WIDTH_20:
+               ch_inf.bw = BRCMU_CHAN_BW_20;
+               WARN_ON(primary_offset != 0);
+               break;
+       case NL80211_CHAN_WIDTH_40:
+               ch_inf.bw = BRCMU_CHAN_BW_40;
+               if (primary_offset < 0)
+                       ch_inf.sb = BRCMU_CHAN_SB_U;
+               else
+                       ch_inf.sb = BRCMU_CHAN_SB_L;
+               break;
+       case NL80211_CHAN_WIDTH_80:
+               ch_inf.bw = BRCMU_CHAN_BW_80;
+               if (primary_offset < 0) {
+                       if (primary_offset < -CH_10MHZ_APART)
+                               ch_inf.sb = BRCMU_CHAN_SB_UU;
+                       else
+                               ch_inf.sb = BRCMU_CHAN_SB_UL;
+               } else {
+                       if (primary_offset > CH_10MHZ_APART)
+                               ch_inf.sb = BRCMU_CHAN_SB_LL;
+                       else
+                               ch_inf.sb = BRCMU_CHAN_SB_LU;
+               }
+               break;
+       default:
+               WARN_ON_ONCE(1);
+       }
+       switch (ch->chan->band) {
+       case IEEE80211_BAND_2GHZ:
+               ch_inf.band = BRCMU_CHAN_BAND_2G;
+               break;
+       case IEEE80211_BAND_5GHZ:
+               ch_inf.band = BRCMU_CHAN_BAND_5G;
+               break;
+       default:
+               WARN_ON_ONCE(1);
+       }
+       d11inf->encchspec(&ch_inf);
+
+       return ch_inf.chspec;
+}
+
 u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
                        struct ieee80211_channel *ch)
 {
@@ -586,6 +640,9 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
                if (err)
                        brcmf_err("Scan abort  failed\n");
        }
+
+       brcmf_set_mpc(ifp, 1);
+
        /*
         * e-scan can be initiated by scheduled scan
         * which takes precedence.
@@ -595,12 +652,10 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
                cfg->sched_escan = false;
                if (!aborted)
                        cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
-               brcmf_set_mpc(ifp, 1);
        } else if (scan_request) {
                brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
                          aborted ? "Aborted" : "Done");
                cfg80211_scan_done(scan_request, aborted);
-               brcmf_set_mpc(ifp, 1);
        }
        if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
                brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
@@ -1236,8 +1291,8 @@ brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
                                params->chandef.chan->center_freq);
                if (params->channel_fixed) {
                        /* adding chanspec */
-                       chanspec = channel_to_chanspec(&cfg->d11inf,
-                                                      params->chandef.chan);
+                       chanspec = chandef_to_chanspec(&cfg->d11inf,
+                                                      &params->chandef);
                        join_params.params_le.chanspec_list[0] =
                                cpu_to_le16(chanspec);
                        join_params.params_le.chanspec_num = cpu_to_le32(1);
@@ -2182,7 +2237,7 @@ brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
 
 static s32
 brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
-                          u8 *mac, struct station_info *sinfo)
+                          const u8 *mac, struct station_info *sinfo)
 {
        struct brcmf_if *ifp = netdev_priv(ndev);
        struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
@@ -3124,7 +3179,7 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
        }
 
        if (!request->n_ssids || !request->n_match_sets) {
-               brcmf_err("Invalid sched scan req!! n_ssids:%d\n",
+               brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n",
                          request->n_ssids);
                return -EINVAL;
        }
@@ -3733,23 +3788,6 @@ brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
        return err;
 }
 
-static s32
-brcmf_cfg80211_set_channel(struct brcmf_cfg80211_info *cfg,
-                          struct brcmf_if *ifp,
-                          struct ieee80211_channel *channel)
-{
-       u16 chanspec;
-       s32 err;
-
-       brcmf_dbg(TRACE, "band=%d, center_freq=%d\n", channel->band,
-                 channel->center_freq);
-
-       chanspec = channel_to_chanspec(&cfg->d11inf, channel);
-       err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
-
-       return err;
-}
-
 static s32
 brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
                        struct cfg80211_ap_settings *settings)
@@ -3765,11 +3803,12 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
        struct brcmf_join_params join_params;
        enum nl80211_iftype dev_role;
        struct brcmf_fil_bss_enable_le bss_enable;
+       u16 chanspec;
 
-       brcmf_dbg(TRACE, "channel_type=%d, beacon_interval=%d, dtim_period=%d,\n",
-                 cfg80211_get_chandef_type(&settings->chandef),
-                 settings->beacon_interval,
-                 settings->dtim_period);
+       brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
+                 settings->chandef.chan->hw_value,
+                 settings->chandef.center_freq1, settings->chandef.width,
+                 settings->beacon_interval, settings->dtim_period);
        brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
                  settings->ssid, settings->ssid_len, settings->auth_type,
                  settings->inactivity_timeout);
@@ -3826,9 +3865,10 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
 
        brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
 
-       err = brcmf_cfg80211_set_channel(cfg, ifp, settings->chandef.chan);
+       chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef);
+       err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
        if (err < 0) {
-               brcmf_err("Set Channel failed, %d\n", err);
+               brcmf_err("Set Channel failed: chspec=%d, %d\n", chanspec, err);
                goto exit;
        }
 
@@ -3975,7 +4015,7 @@ brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
 
 static int
 brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
-                          u8 *mac)
+                          const u8 *mac)
 {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
        struct brcmf_scb_val_le scbval;
@@ -4203,7 +4243,7 @@ static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
 }
 
 static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
-                                   struct net_device *ndev, u8 *peer,
+                                   struct net_device *ndev, const u8 *peer,
                                    enum nl80211_tdls_operation oper)
 {
        struct brcmf_if *ifp;
@@ -4364,6 +4404,8 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
                        WIPHY_FLAG_OFFCHAN_TX |
                        WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
                        WIPHY_FLAG_SUPPORTS_TDLS;
+       if (!brcmf_roamoff)
+               wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
        wiphy->mgmt_stypes = brcmf_txrx_stypes;
        wiphy->max_remain_on_channel_duration = 5000;
        brcmf_wiphy_pno_params(wiphy);
@@ -4685,7 +4727,6 @@ brcmf_notify_connect_status(struct brcmf_if *ifp,
        struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
        struct ieee80211_channel *chan;
        s32 err = 0;
-       u16 reason;
 
        if (brcmf_is_apmode(ifp->vif)) {
                err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
@@ -4706,16 +4747,6 @@ brcmf_notify_connect_status(struct brcmf_if *ifp,
                brcmf_dbg(CONN, "Linkdown\n");
                if (!brcmf_is_ibssmode(ifp->vif)) {
                        brcmf_bss_connect_done(cfg, ndev, e, false);
-                       if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED,
-                                              &ifp->vif->sme_state)) {
-                               reason = 0;
-                               if (((e->event_code == BRCMF_E_DEAUTH_IND) ||
-                                    (e->event_code == BRCMF_E_DISASSOC_IND)) &&
-                                   (e->reason != WLAN_REASON_UNSPECIFIED))
-                                       reason = e->reason;
-                               cfg80211_disconnected(ndev, reason, NULL, 0,
-                                                     GFP_KERNEL);
-                       }
                }
                brcmf_link_down(ifp->vif);
                brcmf_init_prof(ndev_to_prof(ndev));
@@ -5215,6 +5246,9 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg,
                if (!(bw_cap[band] & WLC_BW_40MHZ_BIT) &&
                    ch.bw == BRCMU_CHAN_BW_40)
                        continue;
+               if (!(bw_cap[band] & WLC_BW_80MHZ_BIT) &&
+                   ch.bw == BRCMU_CHAN_BW_80)
+                       continue;
                update = false;
                for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) {
                        if (band_chan_arr[j].hw_value == ch.chnum) {
@@ -5231,10 +5265,13 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg,
                                ieee80211_channel_to_frequency(ch.chnum, band);
                        band_chan_arr[index].hw_value = ch.chnum;
 
-                       if (ch.bw == BRCMU_CHAN_BW_40) {
-                               /* assuming the order is HT20, HT40 Upper,
-                                * HT40 lower from chanspecs
-                                */
+                       /* assuming the chanspecs order is HT20,
+                        * HT40 upper, HT40 lower, and VHT80.
+                        */
+                       if (ch.bw == BRCMU_CHAN_BW_80) {
+                               band_chan_arr[index].flags &=
+                                       ~IEEE80211_CHAN_NO_80MHZ;
+                       } else if (ch.bw == BRCMU_CHAN_BW_40) {
                                ht40_flag = band_chan_arr[index].flags &
                                            IEEE80211_CHAN_NO_HT40;
                                if (ch.sb == BRCMU_CHAN_SB_U) {
@@ -5255,8 +5292,13 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg,
                                                    IEEE80211_CHAN_NO_HT40MINUS;
                                }
                        } else {
+                               /* disable other bandwidths for now as mentioned
+                                * order assure they are enabled for subsequent
+                                * chanspecs.
+                                */
                                band_chan_arr[index].flags =
-                                                       IEEE80211_CHAN_NO_HT40;
+                                               IEEE80211_CHAN_NO_HT40 |
+                                               IEEE80211_CHAN_NO_80MHZ;
                                ch.bw = BRCMU_CHAN_BW_20;
                                cfg->d11inf.encchspec(&ch);
                                channel = ch.chspec;
@@ -5323,13 +5365,63 @@ static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
        }
 }
 
+static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
+                               u32 bw_cap[2], u32 nchain)
+{
+       band->ht_cap.ht_supported = true;
+       if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
+               band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
+               band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+       }
+       band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
+       band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
+       band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+       band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
+       memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
+       band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+}
+
+static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
+{
+       u16 mcs_map;
+       int i;
+
+       for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
+               mcs_map = (mcs_map << 2) | supp;
+
+       return cpu_to_le16(mcs_map);
+}
+
+static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
+                                u32 bw_cap[2], u32 nchain)
+{
+       __le16 mcs_map;
+
+       /* not allowed in 2.4G band */
+       if (band->band == IEEE80211_BAND_2GHZ)
+               return;
+
+       band->vht_cap.vht_supported = true;
+       /* 80MHz is mandatory */
+       band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
+       if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
+               band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+               band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
+       }
+       /* all support 256-QAM */
+       mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
+       band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
+       band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
+}
+
 static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
 {
        struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
        struct wiphy *wiphy;
        s32 phy_list;
        u32 band_list[3];
-       u32 nmode;
+       u32 nmode = 0;
+       u32 vhtmode = 0;
        u32 bw_cap[2] = { 0, 0 };
        u32 rxchain;
        u32 nchain;
@@ -5360,14 +5452,16 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
        brcmf_dbg(INFO, "BRCMF_C_GET_BANDLIST reported: 0x%08x 0x%08x 0x%08x phy\n",
                  band_list[0], band_list[1], band_list[2]);
 
+       (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
        err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
        if (err) {
                brcmf_err("nmode error (%d)\n", err);
        } else {
                brcmf_get_bwcap(ifp, bw_cap);
        }
-       brcmf_dbg(INFO, "nmode=%d, bw_cap=(%d, %d)\n", nmode,
-                 bw_cap[IEEE80211_BAND_2GHZ], bw_cap[IEEE80211_BAND_5GHZ]);
+       brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
+                 nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ],
+                 bw_cap[IEEE80211_BAND_5GHZ]);
 
        err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
        if (err) {
@@ -5398,17 +5492,10 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
                else
                        continue;
 
-               if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
-                       band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
-                       band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
-               }
-               band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
-               band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
-               band->ht_cap.ht_supported = true;
-               band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
-               band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
-               memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
-               band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+               if (nmode)
+                       brcmf_update_ht_cap(band, bw_cap, nchain);
+               if (vhtmode)
+                       brcmf_update_vht_cap(band, bw_cap, nchain);
                bands[band->band] = band;
        }
 
index 8c5fa4e581392d73d28ba29b790151be8f37f569..43c71bfaa4744fe4094069b28ff4d9084d6c679f 100644 (file)
@@ -897,7 +897,8 @@ static bool brcms_tx_flush_completed(struct brcms_info *wl)
        return result;
 }
 
-static void brcms_ops_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static void brcms_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                           u32 queues, bool drop)
 {
        struct brcms_info *wl = hw->priv;
        int ret;
index 9417cb5a2553f70fc2d2733f80c65d61477dc185..af8ba64ace39286e3214d69bfc23f776e66b6336 100644 (file)
@@ -4870,14 +4870,11 @@ static void brcms_c_detach_module(struct brcms_c_info *wlc)
 /*
  * low level detach
  */
-static int brcms_b_detach(struct brcms_c_info *wlc)
+static void brcms_b_detach(struct brcms_c_info *wlc)
 {
        uint i;
        struct brcms_hw_band *band;
        struct brcms_hardware *wlc_hw = wlc->hw;
-       int callbacks;
-
-       callbacks = 0;
 
        brcms_b_detach_dmapio(wlc_hw);
 
@@ -4900,9 +4897,6 @@ static int brcms_b_detach(struct brcms_c_info *wlc)
                ai_detach(wlc_hw->sih);
                wlc_hw->sih = NULL;
        }
-
-       return callbacks;
-
 }
 
 /*
@@ -4917,14 +4911,15 @@ static int brcms_b_detach(struct brcms_c_info *wlc)
  */
 uint brcms_c_detach(struct brcms_c_info *wlc)
 {
-       uint callbacks = 0;
+       uint callbacks;
 
        if (wlc == NULL)
                return 0;
 
-       callbacks += brcms_b_detach(wlc);
+       brcms_b_detach(wlc);
 
        /* delete software timers */
+       callbacks = 0;
        if (!brcms_c_radio_monitor_stop(wlc))
                callbacks++;
 
index 30e54e2c6c9b6f87ccfe618553c94cf1b2cedee4..2b2522bdd8eb516b422a779b4057b89bd959400e 100644 (file)
 #include <brcmu_wifi.h>
 #include <brcmu_d11.h>
 
-static void brcmu_d11n_encchspec(struct brcmu_chan *ch)
+static u16 d11n_sb(enum brcmu_chan_sb sb)
 {
-       ch->chspec = ch->chnum & BRCMU_CHSPEC_CH_MASK;
+       switch (sb) {
+       case BRCMU_CHAN_SB_NONE:
+               return BRCMU_CHSPEC_D11N_SB_N;
+       case BRCMU_CHAN_SB_L:
+               return BRCMU_CHSPEC_D11N_SB_L;
+       case BRCMU_CHAN_SB_U:
+               return BRCMU_CHSPEC_D11N_SB_U;
+       default:
+               WARN_ON(1);
+       }
+       return 0;
+}
 
-       switch (ch->bw) {
+static u16 d11n_bw(enum brcmu_chan_bw bw)
+{
+       switch (bw) {
        case BRCMU_CHAN_BW_20:
-               ch->chspec |= BRCMU_CHSPEC_D11N_BW_20 | BRCMU_CHSPEC_D11N_SB_N;
-               break;
+               return BRCMU_CHSPEC_D11N_BW_20;
        case BRCMU_CHAN_BW_40:
+               return BRCMU_CHSPEC_D11N_BW_40;
        default:
-               WARN_ON_ONCE(1);
-               break;
+               WARN_ON(1);
        }
+       return 0;
+}
+
+static void brcmu_d11n_encchspec(struct brcmu_chan *ch)
+{
+       if (ch->bw == BRCMU_CHAN_BW_20)
+               ch->sb = BRCMU_CHAN_SB_NONE;
+
+       ch->chspec = 0;
+       brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_CH_MASK,
+                       BRCMU_CHSPEC_CH_SHIFT, ch->chnum);
+       brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11N_SB_MASK,
+                       0, d11n_sb(ch->sb));
+       brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11N_BW_MASK,
+                       0, d11n_bw(ch->bw));
 
        if (ch->chnum <= CH_MAX_2G_CHANNEL)
                ch->chspec |= BRCMU_CHSPEC_D11N_BND_2G;
@@ -41,23 +68,34 @@ static void brcmu_d11n_encchspec(struct brcmu_chan *ch)
                ch->chspec |= BRCMU_CHSPEC_D11N_BND_5G;
 }
 
-static void brcmu_d11ac_encchspec(struct brcmu_chan *ch)
+static u16 d11ac_bw(enum brcmu_chan_bw bw)
 {
-       ch->chspec = ch->chnum & BRCMU_CHSPEC_CH_MASK;
-
-       switch (ch->bw) {
+       switch (bw) {
        case BRCMU_CHAN_BW_20:
-               ch->chspec |= BRCMU_CHSPEC_D11AC_BW_20;
-               break;
+               return BRCMU_CHSPEC_D11AC_BW_20;
        case BRCMU_CHAN_BW_40:
+               return BRCMU_CHSPEC_D11AC_BW_40;
        case BRCMU_CHAN_BW_80:
-       case BRCMU_CHAN_BW_80P80:
-       case BRCMU_CHAN_BW_160:
+               return BRCMU_CHSPEC_D11AC_BW_80;
        default:
-               WARN_ON_ONCE(1);
-               break;
+               WARN_ON(1);
        }
+       return 0;
+}
 
+static void brcmu_d11ac_encchspec(struct brcmu_chan *ch)
+{
+       if (ch->bw == BRCMU_CHAN_BW_20 || ch->sb == BRCMU_CHAN_SB_NONE)
+               ch->sb = BRCMU_CHAN_SB_L;
+
+       brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_CH_MASK,
+                       BRCMU_CHSPEC_CH_SHIFT, ch->chnum);
+       brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK,
+                       BRCMU_CHSPEC_D11AC_SB_SHIFT, ch->sb);
+       brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11AC_BW_MASK,
+                       0, d11ac_bw(ch->bw));
+
+       ch->chspec &= ~BRCMU_CHSPEC_D11AC_BND_MASK;
        if (ch->chnum <= CH_MAX_2G_CHANNEL)
                ch->chspec |= BRCMU_CHSPEC_D11AC_BND_2G;
        else
@@ -73,6 +111,7 @@ static void brcmu_d11n_decchspec(struct brcmu_chan *ch)
        switch (ch->chspec & BRCMU_CHSPEC_D11N_BW_MASK) {
        case BRCMU_CHSPEC_D11N_BW_20:
                ch->bw = BRCMU_CHAN_BW_20;
+               ch->sb = BRCMU_CHAN_SB_NONE;
                break;
        case BRCMU_CHSPEC_D11N_BW_40:
                ch->bw = BRCMU_CHAN_BW_40;
@@ -112,6 +151,7 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch)
        switch (ch->chspec & BRCMU_CHSPEC_D11AC_BW_MASK) {
        case BRCMU_CHSPEC_D11AC_BW_20:
                ch->bw = BRCMU_CHAN_BW_20;
+               ch->sb = BRCMU_CHAN_SB_NONE;
                break;
        case BRCMU_CHSPEC_D11AC_BW_40:
                ch->bw = BRCMU_CHAN_BW_40;
@@ -128,6 +168,25 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch)
                break;
        case BRCMU_CHSPEC_D11AC_BW_80:
                ch->bw = BRCMU_CHAN_BW_80;
+               ch->sb = brcmu_maskget16(ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK,
+                                        BRCMU_CHSPEC_D11AC_SB_SHIFT);
+               switch (ch->sb) {
+               case BRCMU_CHAN_SB_LL:
+                       ch->chnum -= CH_30MHZ_APART;
+                       break;
+               case BRCMU_CHAN_SB_LU:
+                       ch->chnum -= CH_10MHZ_APART;
+                       break;
+               case BRCMU_CHAN_SB_UL:
+                       ch->chnum += CH_10MHZ_APART;
+                       break;
+               case BRCMU_CHAN_SB_UU:
+                       ch->chnum += CH_30MHZ_APART;
+                       break;
+               default:
+                       WARN_ON_ONCE(1);
+                       break;
+               }
                break;
        case BRCMU_CHSPEC_D11AC_BW_8080:
        case BRCMU_CHSPEC_D11AC_BW_160:
index 8660a2cba09810428f127967c996a02cfbc174a1..f9745ea8b3e042d4cec222518d7b2fb76439eedf 100644 (file)
@@ -108,13 +108,7 @@ enum brcmu_chan_bw {
 };
 
 enum brcmu_chan_sb {
-       BRCMU_CHAN_SB_NONE = 0,
-       BRCMU_CHAN_SB_L,
-       BRCMU_CHAN_SB_U,
-       BRCMU_CHAN_SB_LL,
-       BRCMU_CHAN_SB_LU,
-       BRCMU_CHAN_SB_UL,
-       BRCMU_CHAN_SB_UU,
+       BRCMU_CHAN_SB_NONE = -1,
        BRCMU_CHAN_SB_LLL,
        BRCMU_CHAN_SB_LLU,
        BRCMU_CHAN_SB_LUL,
@@ -123,6 +117,12 @@ enum brcmu_chan_sb {
        BRCMU_CHAN_SB_ULU,
        BRCMU_CHAN_SB_UUL,
        BRCMU_CHAN_SB_UUU,
+       BRCMU_CHAN_SB_L = BRCMU_CHAN_SB_LLL,
+       BRCMU_CHAN_SB_U = BRCMU_CHAN_SB_LLU,
+       BRCMU_CHAN_SB_LL = BRCMU_CHAN_SB_LLL,
+       BRCMU_CHAN_SB_LU = BRCMU_CHAN_SB_LLU,
+       BRCMU_CHAN_SB_UL = BRCMU_CHAN_SB_LUL,
+       BRCMU_CHAN_SB_UU = BRCMU_CHAN_SB_LUU,
 };
 
 struct brcmu_chan {
index 74419d4bd123772f94e222326c61ee4e93f0f9d3..76b5d3a8629481df004c333a7bcd1990bec8e40c 100644 (file)
@@ -29,6 +29,7 @@
 #define CH_UPPER_SB                    0x01
 #define CH_LOWER_SB                    0x02
 #define CH_EWA_VALID                   0x04
+#define CH_30MHZ_APART                 6
 #define CH_20MHZ_APART                 4
 #define CH_10MHZ_APART                 2
 #define CH_5MHZ_APART                  1 /* 2G band channels are 5 Mhz apart */
index 103f7bce893208c30eb692cca9aa9adf51b8d8b9..cd0cad7f775993661af9e8f4577c89976b15b811 100644 (file)
@@ -936,7 +936,8 @@ static int __cw1200_flush(struct cw1200_common *priv, bool drop)
        return ret;
 }
 
-void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+void cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                 u32 queues, bool drop)
 {
        struct cw1200_common *priv = hw->priv;
 
index 35babb62cc6a8b5a952f0a989c15ad4edf802f17..b7e386b7662b668b8299a9ab52f3c22f2633dfe5 100644 (file)
@@ -40,7 +40,8 @@ int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
 
 int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value);
 
-void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop);
+void cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                 u32 queues, bool drop);
 
 u64 cw1200_prepare_multicast(struct ieee80211_hw *hw,
                             struct netdev_hw_addr_list *mc_list);
index 67db34e56d7eb0765c5e75025e02470cae01da64..52919ad4272622aeb92d8b9d3d74daf1715a3d40 100644 (file)
@@ -882,7 +882,7 @@ void hostap_setup_dev(struct net_device *dev, local_info_t *local,
        dev->mtu = local->mtu;
 
 
-       SET_ETHTOOL_OPS(dev, &prism2_ethtool_ops);
+       dev->ethtool_ops = &prism2_ethtool_ops;
 
 }
 
index d37a6fd90d400a6dfd90d20cdf02d73e7e536d42..b598e2803500ec7b109111f5118dde1414547a22 100644 (file)
@@ -573,7 +573,7 @@ il3945_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb)
                rx_status.flag |= RX_FLAG_SHORTPRE;
 
        if ((unlikely(rx_stats->phy_count > 20))) {
-               D_DROP("dsp size out of range [0,20]: %d/n",
+               D_DROP("dsp size out of range [0,20]: %d\n",
                       rx_stats->phy_count);
                return;
        }
index 888ad5c74639e351a3727c8b934a68f7969849e5..c159c05db6ef212b8b684c5726e61a8a97c62c39 100644 (file)
@@ -670,7 +670,7 @@ il4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb)
        }
 
        if ((unlikely(phy_res->cfg_phy_cnt > 20))) {
-               D_DROP("dsp size out of range [0,20]: %d/n",
+               D_DROP("dsp size out of range [0,20]: %d\n",
                       phy_res->cfg_phy_cnt);
                return;
        }
index 4f42174d999412102e273744fc39ff692b9a9234..ecc674627e6e10b30a5a7b11ab3150c1ad42b37e 100644 (file)
@@ -4755,7 +4755,8 @@ out:
 }
 EXPORT_SYMBOL(il_mac_change_interface);
 
-void il_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+void il_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                 u32 queues, bool drop)
 {
        struct il_priv *il = hw->priv;
        unsigned long timeout = jiffies + msecs_to_jiffies(500);
index dfb13c70efe83ea8415f93ef2dad9c97a0cb6891..ea5c0f863c4ee35b2cf6738569a5cb3253553c12 100644 (file)
@@ -1723,7 +1723,8 @@ void il_mac_remove_interface(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif);
 int il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                            enum nl80211_iftype newtype, bool newp2p);
-void il_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop);
+void il_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                 u32 queues, bool drop);
 int il_alloc_txq_mem(struct il_priv *il);
 void il_free_txq_mem(struct il_priv *il);
 
index 74b3b4de7bb7de57ef42fabfe36866eef3b05287..7fd50428b93494416db2d4c46a3862c18c5bc2e1 100644 (file)
@@ -2,10 +2,6 @@ config IWLWIFI
        tristate "Intel Wireless WiFi Next Gen AGN - Wireless-N/Advanced-N/Ultimate-N (iwlwifi) "
        depends on PCI && MAC80211 && HAS_IOMEM
        select FW_LOADER
-       select NEW_LEDS
-       select LEDS_CLASS
-       select LEDS_TRIGGERS
-       select MAC80211_LEDS
        ---help---
          Select to build the driver supporting the:
 
@@ -43,6 +39,14 @@ config IWLWIFI
          say M here and read <file:Documentation/kbuild/modules.txt>.  The
          module will be called iwlwifi.
 
+config IWLWIFI_LEDS
+       bool
+       depends on IWLWIFI
+       depends on LEDS_CLASS=y || LEDS_CLASS=IWLWIFI
+       select LEDS_TRIGGERS
+       select MAC80211_LEDS
+       default y
+
 config IWLDVM
        tristate "Intel Wireless WiFi DVM Firmware support"
        depends on IWLWIFI
@@ -124,7 +128,6 @@ config IWLWIFI_DEBUG_EXPERIMENTAL_UCODE
          Enable use of experimental ucode for testing and debugging.
 
 config IWLWIFI_DEVICE_TRACING
-
        bool "iwlwifi device access tracing"
        depends on IWLWIFI
        depends on EVENT_TRACING
index dce7ab2e0c4bfdda46c7ed5f7b1429c7633e7ce8..4d19685f31c3ad97fc48ad4a88ffb6b491abdf2c 100644 (file)
@@ -4,9 +4,10 @@ iwldvm-objs            += main.o rs.o mac80211.o ucode.o tx.o
 iwldvm-objs            += lib.o calib.o tt.o sta.o rx.o
 
 iwldvm-objs            += power.o
-iwldvm-objs            += scan.o led.o
+iwldvm-objs            += scan.o
 iwldvm-objs            += rxon.o devices.o
 
+iwldvm-$(CONFIG_IWLWIFI_LEDS) += led.o
 iwldvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
 
 ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../
index be1086c87157c158834d622546e8bfc2b25b7dff..20e6aa9107009ccc33246190534cbc7674c54d03 100644 (file)
@@ -94,7 +94,6 @@ int iwl_send_calib_results(struct iwl_priv *priv)
 {
        struct iwl_host_cmd hcmd = {
                .id = REPLY_PHY_CALIBRATION_CMD,
-               .flags = CMD_SYNC,
        };
        struct iwl_calib_result *res;
 
index d2fe2596d54ec4525a7661a9b381f3e5352bae61..0ffb6ff1a255f8ac609ba3799c02489ab853eea1 100644 (file)
@@ -1481,7 +1481,7 @@ static ssize_t iwl_dbgfs_ucode_bt_stats_read(struct file *file,
 
        /* make request to uCode to retrieve statistics information */
        mutex_lock(&priv->mutex);
-       ret = iwl_send_statistics_request(priv, CMD_SYNC, false);
+       ret = iwl_send_statistics_request(priv, 0, false);
        mutex_unlock(&priv->mutex);
 
        if (ret)
@@ -1868,7 +1868,7 @@ static ssize_t iwl_dbgfs_clear_ucode_statistics_write(struct file *file,
 
        /* make request to uCode to retrieve statistics information */
        mutex_lock(&priv->mutex);
-       iwl_send_statistics_request(priv, CMD_SYNC, true);
+       iwl_send_statistics_request(priv, 0, true);
        mutex_unlock(&priv->mutex);
 
        return count;
@@ -2188,7 +2188,6 @@ static int iwl_cmd_echo_test(struct iwl_priv *priv)
        struct iwl_host_cmd cmd = {
                .id = REPLY_ECHO,
                .len = { 0 },
-               .flags = CMD_SYNC,
        };
 
        ret = iwl_dvm_send_cmd(priv, &cmd);
@@ -2320,7 +2319,7 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct file *file,
        mutex_lock(&priv->mutex);
 
        /* take the return value to make compiler happy - it will fail anyway */
-       ret = iwl_dvm_send_cmd_pdu(priv, REPLY_ERROR, CMD_SYNC, 0, NULL);
+       ret = iwl_dvm_send_cmd_pdu(priv, REPLY_ERROR, 0, 0, NULL);
 
        mutex_unlock(&priv->mutex);
 
index 3441f70d0ff911594dfd4066e782f0ca042e02d6..a6f22c32a27994000f578c2baff08a2f62042aa0 100644 (file)
@@ -888,9 +888,11 @@ struct iwl_priv {
 
        struct iwl_event_log event_log;
 
+#ifdef CONFIG_IWLWIFI_LEDS
        struct led_classdev led;
        unsigned long blink_on, blink_off;
        bool led_registered;
+#endif
 
        /* WoWLAN GTK rekey data */
        u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN];
index 758c54eeb206718f8077aa2686304ea1a1e4c9bb..34b41e5f7cfccc34af9fdf63d405222138c18b37 100644 (file)
@@ -417,7 +417,6 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv,
        struct iwl_host_cmd hcmd = {
                .id = REPLY_CHANNEL_SWITCH,
                .len = { sizeof(cmd), },
-               .flags = CMD_SYNC,
                .data = { &cmd, },
        };
 
@@ -579,7 +578,6 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv,
        struct iwl_host_cmd hcmd = {
                .id = REPLY_CHANNEL_SWITCH,
                .len = { sizeof(*cmd), },
-               .flags = CMD_SYNC,
                .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
        };
        int err;
index 6a0817d9c4fa05f15e1ceffd4272925451994f80..1c6b2252d0f24cb8ee2b94d0f62040361304b58e 100644 (file)
@@ -36,8 +36,20 @@ struct iwl_priv;
 #define IWL_LED_ACTIVITY       (0<<1)
 #define IWL_LED_LINK           (1<<1)
 
+#ifdef CONFIG_IWLWIFI_LEDS
 void iwlagn_led_enable(struct iwl_priv *priv);
 void iwl_leds_init(struct iwl_priv *priv);
 void iwl_leds_exit(struct iwl_priv *priv);
+#else
+static inline void iwlagn_led_enable(struct iwl_priv *priv)
+{
+}
+static inline void iwl_leds_init(struct iwl_priv *priv)
+{
+}
+static inline void iwl_leds_exit(struct iwl_priv *priv)
+{
+}
+#endif
 
 #endif /* __iwl_leds_h__ */
index 576f7ee38ca5894150cba0e54eff4bcc5ff2f079..2191621d69c1618375482d3e3a1dc7d8f2b7f192 100644 (file)
@@ -81,7 +81,7 @@ int iwlagn_send_tx_power(struct iwl_priv *priv)
        else
                tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD;
 
-       return iwl_dvm_send_cmd_pdu(priv, tx_ant_cfg_cmd, CMD_SYNC,
+       return iwl_dvm_send_cmd_pdu(priv, tx_ant_cfg_cmd, 0,
                        sizeof(tx_power_cmd), &tx_power_cmd);
 }
 
@@ -141,7 +141,6 @@ int iwlagn_txfifo_flush(struct iwl_priv *priv, u32 scd_q_msk)
        struct iwl_host_cmd cmd = {
                .id = REPLY_TXFIFO_FLUSH,
                .len = { sizeof(struct iwl_txfifo_flush_cmd), },
-               .flags = CMD_SYNC,
                .data = { &flush_cmd, },
        };
 
@@ -180,7 +179,7 @@ void iwlagn_dev_txfifo_flush(struct iwl_priv *priv)
                goto done;
        }
        IWL_DEBUG_INFO(priv, "wait transmit/flush all frames\n");
-       iwl_trans_wait_tx_queue_empty(priv->trans);
+       iwl_trans_wait_tx_queue_empty(priv->trans, 0xffffffff);
 done:
        ieee80211_wake_queues(priv->hw);
        mutex_unlock(&priv->mutex);
@@ -333,12 +332,12 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv)
                memcpy(&bt_cmd_v2.basic, &basic,
                        sizeof(basic));
                ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG,
-                       CMD_SYNC, sizeof(bt_cmd_v2), &bt_cmd_v2);
+                       0, sizeof(bt_cmd_v2), &bt_cmd_v2);
        } else {
                memcpy(&bt_cmd_v1.basic, &basic,
                        sizeof(basic));
                ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG,
-                       CMD_SYNC, sizeof(bt_cmd_v1), &bt_cmd_v1);
+                       0, sizeof(bt_cmd_v1), &bt_cmd_v1);
        }
        if (ret)
                IWL_ERR(priv, "failed to send BT Coex Config\n");
@@ -1044,7 +1043,6 @@ int iwlagn_send_patterns(struct iwl_priv *priv,
        struct iwl_host_cmd cmd = {
                .id = REPLY_WOWLAN_PATTERNS,
                .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
-               .flags = CMD_SYNC,
        };
        int i, err;
 
@@ -1201,7 +1199,6 @@ int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan)
                if (key_data.use_rsc_tsc) {
                        struct iwl_host_cmd rsc_tsc_cmd = {
                                .id = REPLY_WOWLAN_TSC_RSC_PARAMS,
-                               .flags = CMD_SYNC,
                                .data[0] = key_data.rsc_tsc,
                                .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
                                .len[0] = sizeof(*key_data.rsc_tsc),
@@ -1215,7 +1212,7 @@ int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan)
                if (key_data.use_tkip) {
                        ret = iwl_dvm_send_cmd_pdu(priv,
                                                 REPLY_WOWLAN_TKIP_PARAMS,
-                                                CMD_SYNC, sizeof(tkip_cmd),
+                                                0, sizeof(tkip_cmd),
                                                 &tkip_cmd);
                        if (ret)
                                goto out;
@@ -1231,20 +1228,20 @@ int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan)
 
                        ret = iwl_dvm_send_cmd_pdu(priv,
                                                 REPLY_WOWLAN_KEK_KCK_MATERIAL,
-                                                CMD_SYNC, sizeof(kek_kck_cmd),
+                                                0, sizeof(kek_kck_cmd),
                                                 &kek_kck_cmd);
                        if (ret)
                                goto out;
                }
        }
 
-       ret = iwl_dvm_send_cmd_pdu(priv, REPLY_D3_CONFIG, CMD_SYNC,
+       ret = iwl_dvm_send_cmd_pdu(priv, REPLY_D3_CONFIG, 0,
                                     sizeof(d3_cfg_cmd), &d3_cfg_cmd);
        if (ret)
                goto out;
 
        ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_WAKEUP_FILTER,
-                                CMD_SYNC, sizeof(wakeup_filter_cmd),
+                                0, sizeof(wakeup_filter_cmd),
                                 &wakeup_filter_cmd);
        if (ret)
                goto out;
index dd55c9cf7ba80376ef3ae507b434e79d6dd1cca4..29af7b51e3708788d02f4a1651205a348a5102dd 100644 (file)
@@ -1091,7 +1091,8 @@ static void iwlagn_configure_filter(struct ieee80211_hw *hw,
                        FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL;
 }
 
-static void iwlagn_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static void iwlagn_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                            u32 queues, bool drop)
 {
        struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
 
@@ -1119,7 +1120,7 @@ static void iwlagn_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
                }
        }
        IWL_DEBUG_MAC80211(priv, "wait transmit/flush all frames\n");
-       iwl_trans_wait_tx_queue_empty(priv->trans);
+       iwl_trans_wait_tx_queue_empty(priv->trans, 0xffffffff);
 done:
        mutex_unlock(&priv->mutex);
        IWL_DEBUG_MAC80211(priv, "leave\n");
index 6a6df71af1d7ba6e4b4dfec16a2042c6cc357de4..0b7f46f0b079a442b327ecf80184fd00294c4377 100644 (file)
@@ -128,7 +128,6 @@ int iwlagn_send_beacon_cmd(struct iwl_priv *priv)
        struct iwl_tx_beacon_cmd *tx_beacon_cmd;
        struct iwl_host_cmd cmd = {
                .id = REPLY_TX_BEACON,
-               .flags = CMD_SYNC,
        };
        struct ieee80211_tx_info *info;
        u32 frame_size;
@@ -311,8 +310,7 @@ int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear)
                                        sizeof(struct iwl_statistics_cmd),
                                        &statistics_cmd);
        else
-               return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD,
-                                       CMD_SYNC,
+               return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, 0,
                                        sizeof(struct iwl_statistics_cmd),
                                        &statistics_cmd);
 }
@@ -622,7 +620,7 @@ static void iwl_rf_kill_ct_config(struct iwl_priv *priv)
 
                ret = iwl_dvm_send_cmd_pdu(priv,
                                       REPLY_CT_KILL_CONFIG_CMD,
-                                      CMD_SYNC, sizeof(adv_cmd), &adv_cmd);
+                                      0, sizeof(adv_cmd), &adv_cmd);
                if (ret)
                        IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n");
                else
@@ -637,7 +635,7 @@ static void iwl_rf_kill_ct_config(struct iwl_priv *priv)
 
                ret = iwl_dvm_send_cmd_pdu(priv,
                                       REPLY_CT_KILL_CONFIG_CMD,
-                                      CMD_SYNC, sizeof(cmd), &cmd);
+                                      0, sizeof(cmd), &cmd);
                if (ret)
                        IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n");
                else
@@ -673,9 +671,7 @@ static int iwlagn_send_tx_ant_config(struct iwl_priv *priv, u8 valid_tx_ant)
 
        if (IWL_UCODE_API(priv->fw->ucode_ver) > 1) {
                IWL_DEBUG_HC(priv, "select valid tx ant: %u\n", valid_tx_ant);
-               return iwl_dvm_send_cmd_pdu(priv,
-                                       TX_ANT_CONFIGURATION_CMD,
-                                       CMD_SYNC,
+               return iwl_dvm_send_cmd_pdu(priv, TX_ANT_CONFIGURATION_CMD, 0,
                                        sizeof(struct iwl_tx_ant_config_cmd),
                                        &tx_ant_cmd);
        } else {
@@ -703,7 +699,7 @@ static void iwl_send_bt_config(struct iwl_priv *priv)
                (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active");
 
        if (iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG,
-                            CMD_SYNC, sizeof(struct iwl_bt_cmd), &bt_cmd))
+                            0, sizeof(struct iwl_bt_cmd), &bt_cmd))
                IWL_ERR(priv, "failed to send BT Coex Config\n");
 }
 
@@ -987,7 +983,7 @@ static void iwl_bg_restart(struct work_struct *data)
                        ieee80211_restart_hw(priv->hw);
                else
                        IWL_ERR(priv,
-                               "Cannot request restart before registrating with mac80211");
+                               "Cannot request restart before registrating with mac80211\n");
        } else {
                WARN_ON(1);
        }
@@ -1127,7 +1123,6 @@ static void iwl_option_config(struct iwl_priv *priv)
 static int iwl_eeprom_init_hw_params(struct iwl_priv *priv)
 {
        struct iwl_nvm_data *data = priv->nvm_data;
-       char *debug_msg;
 
        if (data->sku_cap_11n_enable &&
            !priv->cfg->ht_params) {
@@ -1141,8 +1136,8 @@ static int iwl_eeprom_init_hw_params(struct iwl_priv *priv)
                return -EINVAL;
        }
 
-       debug_msg = "Device SKU: 24GHz %s %s, 52GHz %s %s, 11.n %s %s\n";
-       IWL_DEBUG_INFO(priv, debug_msg,
+       IWL_DEBUG_INFO(priv,
+                      "Device SKU: 24GHz %s %s, 52GHz %s %s, 11.n %s %s\n",
                       data->sku_cap_band_24GHz_enable ? "" : "NOT", "enabled",
                       data->sku_cap_band_52GHz_enable ? "" : "NOT", "enabled",
                       data->sku_cap_11n_enable ? "" : "NOT", "enabled");
@@ -1350,7 +1345,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
        iwl_set_hw_params(priv);
 
        if (!(priv->nvm_data->sku_cap_ipan_enable)) {
-               IWL_DEBUG_INFO(priv, "Your EEPROM disabled PAN");
+               IWL_DEBUG_INFO(priv, "Your EEPROM disabled PAN\n");
                ucode_flags &= ~IWL_UCODE_TLV_FLAGS_PAN;
                /*
                 * if not PAN, then don't support P2P -- might be a uCode
@@ -2019,10 +2014,10 @@ void iwlagn_lift_passive_no_rx(struct iwl_priv *priv)
 
        for (mq = 0; mq < IWLAGN_FIRST_AMPDU_QUEUE; mq++) {
                if (!test_bit(mq, &priv->transport_queue_stop)) {
-                       IWL_DEBUG_TX_QUEUES(priv, "Wake queue %d", mq);
+                       IWL_DEBUG_TX_QUEUES(priv, "Wake queue %d\n", mq);
                        ieee80211_wake_queue(priv->hw, mq);
                } else {
-                       IWL_DEBUG_TX_QUEUES(priv, "Don't wake queue %d", mq);
+                       IWL_DEBUG_TX_QUEUES(priv, "Don't wake queue %d\n", mq);
                }
        }
 
@@ -2053,6 +2048,17 @@ static bool iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
        return false;
 }
 
+static void iwl_napi_add(struct iwl_op_mode *op_mode,
+                        struct napi_struct *napi,
+                        struct net_device *napi_dev,
+                        int (*poll)(struct napi_struct *, int),
+                        int weight)
+{
+       struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
+
+       ieee80211_napi_add(priv->hw, napi, napi_dev, poll, weight);
+}
+
 static const struct iwl_op_mode_ops iwl_dvm_ops = {
        .start = iwl_op_mode_dvm_start,
        .stop = iwl_op_mode_dvm_stop,
@@ -2065,6 +2071,7 @@ static const struct iwl_op_mode_ops iwl_dvm_ops = {
        .cmd_queue_full = iwl_cmd_queue_full,
        .nic_config = iwl_nic_config,
        .wimax_active = iwl_wimax_active,
+       .napi_add = iwl_napi_add,
 };
 
 /*****************************************************************************
index b4e61417013aff585bf8dcfa33b8fa9e3c5c84c6..f2c1439566b5ffa7814c02c041bf34bb3b3391d6 100644 (file)
@@ -278,7 +278,7 @@ static int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd)
                        le32_to_cpu(cmd->sleep_interval[3]),
                        le32_to_cpu(cmd->sleep_interval[4]));
 
-       return iwl_dvm_send_cmd_pdu(priv, POWER_TABLE_CMD, CMD_SYNC,
+       return iwl_dvm_send_cmd_pdu(priv, POWER_TABLE_CMD, 0,
                                sizeof(struct iwl_powertable_cmd), cmd);
 }
 
@@ -361,7 +361,7 @@ int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd,
 
                memcpy(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd));
        } else
-               IWL_ERR(priv, "set power fail, ret = %d", ret);
+               IWL_ERR(priv, "set power fail, ret = %d\n", ret);
 
        return ret;
 }
index aa773a2da4ab877f6b5876a28024a6c2e23ffa9b..32b78a66536db90bbb1e67f10e210df49f6b2d03 100644 (file)
@@ -1453,7 +1453,7 @@ static int rs_move_legacy_other(struct iwl_priv *priv,
                        tbl->action = IWL_LEGACY_SWITCH_SISO;
                break;
        default:
-               IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load);
+               IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load);
                break;
        }
 
@@ -1628,7 +1628,7 @@ static int rs_move_siso_to_other(struct iwl_priv *priv,
                        tbl->action = IWL_SISO_SWITCH_ANTENNA1;
                break;
        default:
-               IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load);
+               IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load);
                break;
        }
 
@@ -1799,7 +1799,7 @@ static int rs_move_mimo2_to_other(struct iwl_priv *priv,
                        tbl->action = IWL_MIMO2_SWITCH_SISO_A;
                break;
        default:
-               IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load);
+               IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load);
                break;
        }
 
@@ -1969,7 +1969,7 @@ static int rs_move_mimo3_to_other(struct iwl_priv *priv,
                        tbl->action = IWL_MIMO3_SWITCH_SISO_A;
                break;
        default:
-               IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load);
+               IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load);
                break;
        }
 
@@ -2709,7 +2709,7 @@ static void rs_initialize_lq(struct iwl_priv *priv,
        rs_set_expected_tpt_table(lq_sta, tbl);
        rs_fill_link_cmd(NULL, lq_sta, rate);
        priv->stations[lq_sta->lq.sta_id].lq = &lq_sta->lq;
-       iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_SYNC, true);
+       iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, 0, true);
 }
 
 static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta,
index cd8377346aff0c2936a6f0ee773ae8fd33042025..debec963c610d4693fb40ca22a27b9cd3b6310ac 100644 (file)
@@ -786,7 +786,7 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv,
 
        memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
 
-       ieee80211_rx_ni(priv->hw, skb);
+       ieee80211_rx(priv->hw, skb);
 }
 
 static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in)
index 503a81e581855729fde646408a8ae33b0195539f..ed50de6362ed1d5dcd56b45243ff0b140dcbafe5 100644 (file)
@@ -104,7 +104,7 @@ static int iwlagn_disable_bss(struct iwl_priv *priv,
 
        send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
        ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd,
-                               CMD_SYNC, sizeof(*send), send);
+                               0, sizeof(*send), send);
 
        send->filter_flags = old_filter;
 
@@ -134,7 +134,7 @@ static int iwlagn_disable_pan(struct iwl_priv *priv,
        send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
        send->dev_type = RXON_DEV_TYPE_P2P;
        ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd,
-                               CMD_SYNC, sizeof(*send), send);
+                               0, sizeof(*send), send);
 
        send->filter_flags = old_filter;
        send->dev_type = old_dev_type;
@@ -160,7 +160,7 @@ static int iwlagn_disconn_pan(struct iwl_priv *priv,
        int ret;
 
        send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
-       ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, CMD_SYNC,
+       ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, 0,
                                sizeof(*send), send);
 
        send->filter_flags = old_filter;
@@ -189,7 +189,7 @@ static void iwlagn_update_qos(struct iwl_priv *priv,
                      ctx->qos_data.qos_active,
                      ctx->qos_data.def_qos_parm.qos_flags);
 
-       ret = iwl_dvm_send_cmd_pdu(priv, ctx->qos_cmd, CMD_SYNC,
+       ret = iwl_dvm_send_cmd_pdu(priv, ctx->qos_cmd, 0,
                               sizeof(struct iwl_qosparam_cmd),
                               &ctx->qos_data.def_qos_parm);
        if (ret)
@@ -353,7 +353,7 @@ static int iwl_send_rxon_timing(struct iwl_priv *priv,
                        le16_to_cpu(ctx->timing.atim_window));
 
        return iwl_dvm_send_cmd_pdu(priv, ctx->rxon_timing_cmd,
-                               CMD_SYNC, sizeof(ctx->timing), &ctx->timing);
+                               0, sizeof(ctx->timing), &ctx->timing);
 }
 
 static int iwlagn_rxon_disconn(struct iwl_priv *priv,
@@ -495,7 +495,7 @@ static int iwlagn_rxon_connect(struct iwl_priv *priv,
         * Associated RXON doesn't clear the station table in uCode,
         * so we don't need to restore stations etc. after this.
         */
-       ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, CMD_SYNC,
+       ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, 0,
                      sizeof(struct iwl_rxon_cmd), &ctx->staging);
        if (ret) {
                IWL_ERR(priv, "Error setting new RXON (%d)\n", ret);
@@ -610,7 +610,7 @@ int iwlagn_set_pan_params(struct iwl_priv *priv)
        cmd.slots[0].width = cpu_to_le16(slot0);
        cmd.slots[1].width = cpu_to_le16(slot1);
 
-       ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WIPAN_PARAMS, CMD_SYNC,
+       ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WIPAN_PARAMS, 0,
                        sizeof(cmd), &cmd);
        if (ret)
                IWL_ERR(priv, "Error setting PAN parameters (%d)\n", ret);
@@ -823,7 +823,7 @@ static int iwl_check_rxon_cmd(struct iwl_priv *priv,
 
        if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK))
                        == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) {
-               IWL_WARN(priv, "CCK and auto detect");
+               IWL_WARN(priv, "CCK and auto detect\n");
                errors |= BIT(8);
        }
 
@@ -1395,7 +1395,7 @@ static void iwlagn_chain_noise_reset(struct iwl_priv *priv)
                        priv->phy_calib_chain_noise_reset_cmd);
                ret = iwl_dvm_send_cmd_pdu(priv,
                                        REPLY_PHY_CALIBRATION_CMD,
-                                       CMD_SYNC, sizeof(cmd), &cmd);
+                                       0, sizeof(cmd), &cmd);
                if (ret)
                        IWL_ERR(priv,
                                "Could not send REPLY_PHY_CALIBRATION_CMD\n");
index be98b913ed582046d6598d0cf7454cf3bd8278cc..43bef901e8f9a80a7c3a56f63a7d2da93fda7076 100644 (file)
@@ -59,7 +59,7 @@ static int iwl_send_scan_abort(struct iwl_priv *priv)
        int ret;
        struct iwl_host_cmd cmd = {
                .id = REPLY_SCAN_ABORT_CMD,
-               .flags = CMD_SYNC | CMD_WANT_SKB,
+               .flags = CMD_WANT_SKB,
        };
        __le32 *status;
 
@@ -639,7 +639,6 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
        struct iwl_host_cmd cmd = {
                .id = REPLY_SCAN_CMD,
                .len = { sizeof(struct iwl_scan_cmd), },
-               .flags = CMD_SYNC,
        };
        struct iwl_scan_cmd *scan;
        struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
index 9cdd91cdf661825604e9ae8c89649213667b0b0e..6ec86adbe4a1fcc9df9aaf0d8b73ca4b48ac2e9b 100644 (file)
@@ -39,7 +39,7 @@ static int iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id)
        lockdep_assert_held(&priv->sta_lock);
 
        if (sta_id >= IWLAGN_STATION_COUNT) {
-               IWL_ERR(priv, "invalid sta_id %u", sta_id);
+               IWL_ERR(priv, "invalid sta_id %u\n", sta_id);
                return -EINVAL;
        }
        if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE))
@@ -165,7 +165,7 @@ int iwl_send_add_sta(struct iwl_priv *priv,
        iwl_free_resp(&cmd);
 
        if (cmd.handler_status)
-               IWL_ERR(priv, "%s - error in the CMD response %d", __func__,
+               IWL_ERR(priv, "%s - error in the CMD response %d\n", __func__,
                        cmd.handler_status);
 
        return cmd.handler_status;
@@ -261,7 +261,7 @@ int iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
        cmd.station_flags = flags;
        cmd.sta.sta_id = sta_id;
 
-       return iwl_send_add_sta(priv, &cmd, CMD_SYNC);
+       return iwl_send_add_sta(priv, &cmd, 0);
 }
 
 static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index,
@@ -413,7 +413,7 @@ int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
        spin_unlock_bh(&priv->sta_lock);
 
        /* Add station to device's station table */
-       ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
+       ret = iwl_send_add_sta(priv, &sta_cmd, 0);
        if (ret) {
                spin_lock_bh(&priv->sta_lock);
                IWL_ERR(priv, "Adding station %pM failed.\n",
@@ -456,7 +456,6 @@ static int iwl_send_remove_station(struct iwl_priv *priv,
        struct iwl_host_cmd cmd = {
                .id = REPLY_REMOVE_STA,
                .len = { sizeof(struct iwl_rem_sta_cmd), },
-               .flags = CMD_SYNC,
                .data = { &rm_sta_cmd, },
        };
 
@@ -740,7 +739,7 @@ void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
                                        send_lq = true;
                        }
                        spin_unlock_bh(&priv->sta_lock);
-                       ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
+                       ret = iwl_send_add_sta(priv, &sta_cmd, 0);
                        if (ret) {
                                spin_lock_bh(&priv->sta_lock);
                                IWL_ERR(priv, "Adding station %pM failed.\n",
@@ -756,8 +755,7 @@ void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
                         * current LQ command
                         */
                        if (send_lq)
-                               iwl_send_lq_cmd(priv, ctx, &lq,
-                                               CMD_SYNC, true);
+                               iwl_send_lq_cmd(priv, ctx, &lq, 0, true);
                        spin_lock_bh(&priv->sta_lock);
                        priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS;
                }
@@ -968,7 +966,7 @@ int iwlagn_add_bssid_station(struct iwl_priv *priv,
                return -ENOMEM;
        }
 
-       ret = iwl_send_lq_cmd(priv, ctx, link_cmd, CMD_SYNC, true);
+       ret = iwl_send_lq_cmd(priv, ctx, link_cmd, 0, true);
        if (ret)
                IWL_ERR(priv, "Link quality command failed (%d)\n", ret);
 
@@ -999,7 +997,6 @@ static int iwl_send_static_wepkey_cmd(struct iwl_priv *priv,
        struct iwl_host_cmd cmd = {
                .id = ctx->wep_key_cmd,
                .data = { wep_cmd, },
-               .flags = CMD_SYNC,
        };
 
        might_sleep();
@@ -1248,7 +1245,7 @@ int iwl_remove_dynamic_key(struct iwl_priv *priv,
        sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK;
        sta_cmd.mode = STA_CONTROL_MODIFY_MSK;
 
-       return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
+       return iwl_send_add_sta(priv, &sta_cmd, 0);
 }
 
 int iwl_set_dynamic_key(struct iwl_priv *priv,
@@ -1284,13 +1281,13 @@ int iwl_set_dynamic_key(struct iwl_priv *priv,
                ieee80211_get_key_rx_seq(keyconf, 0, &seq);
                ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
                ret = iwlagn_send_sta_key(priv, keyconf, sta_id,
-                                         seq.tkip.iv32, p1k, CMD_SYNC);
+                                         seq.tkip.iv32, p1k, 0);
                break;
        case WLAN_CIPHER_SUITE_CCMP:
        case WLAN_CIPHER_SUITE_WEP40:
        case WLAN_CIPHER_SUITE_WEP104:
                ret = iwlagn_send_sta_key(priv, keyconf, sta_id,
-                                         0, NULL, CMD_SYNC);
+                                         0, NULL, 0);
                break;
        default:
                IWL_ERR(priv, "Unknown cipher %x\n", keyconf->cipher);
@@ -1409,7 +1406,7 @@ int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid)
        memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
        spin_unlock_bh(&priv->sta_lock);
 
-       return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
+       return iwl_send_add_sta(priv, &sta_cmd, 0);
 }
 
 int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta,
@@ -1433,7 +1430,7 @@ int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta,
        memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
        spin_unlock_bh(&priv->sta_lock);
 
-       return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
+       return iwl_send_add_sta(priv, &sta_cmd, 0);
 }
 
 int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta,
@@ -1458,7 +1455,7 @@ int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta,
        memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
        spin_unlock_bh(&priv->sta_lock);
 
-       return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
+       return iwl_send_add_sta(priv, &sta_cmd, 0);
 }
 
 
index 058c5892c427afdf3df48038bf8e715017d379c6..acb981a0a0aaa0bb31f99e7f5306e5c03f6cdd47 100644 (file)
@@ -236,7 +236,7 @@ static void iwl_prepare_ct_kill_task(struct iwl_priv *priv)
 {
        IWL_DEBUG_TEMP(priv, "Prepare to enter IWL_TI_CT_KILL\n");
        /* make request to retrieve statistics information */
-       iwl_send_statistics_request(priv, CMD_SYNC, false);
+       iwl_send_statistics_request(priv, 0, false);
        /* Reschedule the ct_kill wait timer */
        mod_timer(&priv->thermal_throttle.ct_kill_waiting_tm,
                 jiffies + msecs_to_jiffies(CT_KILL_WAITING_DURATION));
index 398dd096674cf17bd8112e8e7d06ad4ce57427f0..3255a1723d176f59ee5547e7981a9d3bbe7d9583 100644 (file)
@@ -402,10 +402,10 @@ int iwlagn_tx_skb(struct iwl_priv *priv,
                /* aggregation is on for this <sta,tid> */
                if (info->flags & IEEE80211_TX_CTL_AMPDU &&
                    tid_data->agg.state != IWL_AGG_ON) {
-                       IWL_ERR(priv, "TX_CTL_AMPDU while not in AGG:"
-                               " Tx flags = 0x%08x, agg.state = %d",
+                       IWL_ERR(priv,
+                               "TX_CTL_AMPDU while not in AGG: Tx flags = 0x%08x, agg.state = %d\n",
                                info->flags, tid_data->agg.state);
-                       IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d",
+                       IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d\n",
                                sta_id, tid,
                                IEEE80211_SEQ_TO_SN(tid_data->seq_number));
                        goto drop_unlock_sta;
@@ -416,7 +416,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv,
                 */
                if (WARN_ONCE(tid_data->agg.state != IWL_AGG_ON &&
                              tid_data->agg.state != IWL_AGG_OFF,
-                   "Tx while agg.state = %d", tid_data->agg.state))
+                             "Tx while agg.state = %d\n", tid_data->agg.state))
                        goto drop_unlock_sta;
 
                seq_number = tid_data->seq_number;
@@ -778,8 +778,8 @@ static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid)
                /* There are no packets for this RA / TID in the HW any more */
                if (tid_data->agg.ssn == tid_data->next_reclaimed) {
                        IWL_DEBUG_TX_QUEUES(priv,
-                               "Can continue DELBA flow ssn = next_recl ="
-                               " %d", tid_data->next_reclaimed);
+                               "Can continue DELBA flow ssn = next_recl = %d\n",
+                               tid_data->next_reclaimed);
                        iwl_trans_txq_disable(priv->trans,
                                              tid_data->agg.txq_id);
                        iwlagn_dealloc_agg_txq(priv, tid_data->agg.txq_id);
@@ -791,8 +791,8 @@ static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid)
                /* There are no packets for this RA / TID in the HW any more */
                if (tid_data->agg.ssn == tid_data->next_reclaimed) {
                        IWL_DEBUG_TX_QUEUES(priv,
-                               "Can continue ADDBA flow ssn = next_recl ="
-                               " %d", tid_data->next_reclaimed);
+                               "Can continue ADDBA flow ssn = next_recl = %d\n",
+                               tid_data->next_reclaimed);
                        tid_data->agg.state = IWL_AGG_STARTING;
                        ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid);
                }
@@ -1216,8 +1216,8 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
                            ctx->vif->type == NL80211_IFTYPE_STATION) {
                                /* block and stop all queues */
                                priv->passive_no_rx = true;
-                               IWL_DEBUG_TX_QUEUES(priv, "stop all queues: "
-                                                   "passive channel");
+                               IWL_DEBUG_TX_QUEUES(priv,
+                                       "stop all queues: passive channel\n");
                                ieee80211_stop_queues(priv->hw);
 
                                IWL_DEBUG_TX_REPLY(priv,
@@ -1271,7 +1271,7 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
 
        while (!skb_queue_empty(&skbs)) {
                skb = __skb_dequeue(&skbs);
-               ieee80211_tx_status_ni(priv->hw, skb);
+               ieee80211_tx_status(priv->hw, skb);
        }
 
        return 0;
@@ -1411,7 +1411,7 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
 
        while (!skb_queue_empty(&reclaimed_skbs)) {
                skb = __skb_dequeue(&reclaimed_skbs);
-               ieee80211_tx_status_ni(priv->hw, skb);
+               ieee80211_tx_status(priv->hw, skb);
        }
 
        return 0;
index cf03ef5619d9fea602151cf573162848ffd12366..d5cee1530597b8cf2de444e98aebffd89d636715 100644 (file)
@@ -172,7 +172,7 @@ static int iwl_send_wimax_coex(struct iwl_priv *priv)
        memset(&coex_cmd, 0, sizeof(coex_cmd));
 
        return iwl_dvm_send_cmd_pdu(priv,
-                               COEX_PRIORITY_TABLE_CMD, CMD_SYNC,
+                               COEX_PRIORITY_TABLE_CMD, 0,
                                sizeof(coex_cmd), &coex_cmd);
 }
 
@@ -205,7 +205,7 @@ void iwl_send_prio_tbl(struct iwl_priv *priv)
        memcpy(prio_tbl_cmd.prio_tbl, iwl_bt_prio_tbl,
                sizeof(iwl_bt_prio_tbl));
        if (iwl_dvm_send_cmd_pdu(priv,
-                               REPLY_BT_COEX_PRIO_TABLE, CMD_SYNC,
+                               REPLY_BT_COEX_PRIO_TABLE, 0,
                                sizeof(prio_tbl_cmd), &prio_tbl_cmd))
                IWL_ERR(priv, "failed to send BT prio tbl command\n");
 }
@@ -218,7 +218,7 @@ int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type)
        env_cmd.action = action;
        env_cmd.type = type;
        ret = iwl_dvm_send_cmd_pdu(priv,
-                              REPLY_BT_COEX_PROT_ENV, CMD_SYNC,
+                              REPLY_BT_COEX_PROT_ENV, 0,
                               sizeof(env_cmd), &env_cmd);
        if (ret)
                IWL_ERR(priv, "failed to send BT env command\n");
index 854ba84ccb730995f0d786d26a85a1c2fc14c0dc..c3817fae16c04207136e5d45e8cc65bd3a125429 100644 (file)
@@ -62,6 +62,7 @@ static const struct iwl_base_params iwl1000_base_params = {
        .led_compensation = 51,
        .wd_timeout = IWL_WATCHDOG_DISABLED,
        .max_event_log_size = 128,
+       .scd_chain_ext_wa = true,
 };
 
 static const struct iwl_ht_params iwl1000_ht_params = {
index 3e63323637f3f593dd895528dac4e5fb794fdb4c..21e5d0843a62a84a0f21ff337d1b674750fa3999 100644 (file)
@@ -75,6 +75,7 @@ static const struct iwl_base_params iwl2000_base_params = {
        .wd_timeout = IWL_DEF_WD_TIMEOUT,
        .max_event_log_size = 512,
        .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+       .scd_chain_ext_wa = true,
 };
 
 
@@ -88,6 +89,7 @@ static const struct iwl_base_params iwl2030_base_params = {
        .wd_timeout = IWL_LONG_WD_TIMEOUT,
        .max_event_log_size = 512,
        .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+       .scd_chain_ext_wa = true,
 };
 
 static const struct iwl_ht_params iwl2000_ht_params = {
index 6674f2c4541c183fbae6c2a1bb0861d9eb3a966f..332bbede39e5b0fc6bb25b7ab30bbbd22c929b8b 100644 (file)
@@ -61,6 +61,7 @@ static const struct iwl_base_params iwl5000_base_params = {
        .led_compensation = 51,
        .wd_timeout = IWL_WATCHDOG_DISABLED,
        .max_event_log_size = 512,
+       .scd_chain_ext_wa = true,
 };
 
 static const struct iwl_ht_params iwl5000_ht_params = {
index 8048de90233fa038545e9d752eaaffbfc3968c72..8f2c3c8c6b843f78f346225d371ee3ad3df54f23 100644 (file)
@@ -85,6 +85,7 @@ static const struct iwl_base_params iwl6000_base_params = {
        .wd_timeout = IWL_DEF_WD_TIMEOUT,
        .max_event_log_size = 512,
        .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+       .scd_chain_ext_wa = true,
 };
 
 static const struct iwl_base_params iwl6050_base_params = {
@@ -97,6 +98,7 @@ static const struct iwl_base_params iwl6050_base_params = {
        .wd_timeout = IWL_DEF_WD_TIMEOUT,
        .max_event_log_size = 1024,
        .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+       .scd_chain_ext_wa = true,
 };
 
 static const struct iwl_base_params iwl6000_g2_base_params = {
@@ -109,6 +111,7 @@ static const struct iwl_base_params iwl6000_g2_base_params = {
        .wd_timeout = IWL_LONG_WD_TIMEOUT,
        .max_event_log_size = 512,
        .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+       .scd_chain_ext_wa = true,
 };
 
 static const struct iwl_ht_params iwl6000_ht_params = {
index 4c2d4ef28b220c719ac49f9f9726b931c2d35442..48730064da73f5e1e058f756d9473a2a0a5bc376 100644 (file)
 #define IWL3160_UCODE_API_MAX  9
 
 /* Oldest version we won't warn about */
-#define IWL7260_UCODE_API_OK   8
-#define IWL3160_UCODE_API_OK   8
+#define IWL7260_UCODE_API_OK   9
+#define IWL3160_UCODE_API_OK   9
 
 /* Lowest firmware API version supported */
-#define IWL7260_UCODE_API_MIN  7
-#define IWL3160_UCODE_API_MIN  7
+#define IWL7260_UCODE_API_MIN  8
+#define IWL3160_UCODE_API_MIN  8
 
 /* NVM versions */
 #define IWL7260_NVM_VERSION            0x0a1d
@@ -98,7 +98,7 @@
 #define NVM_HW_SECTION_NUM_FAMILY_7000         0
 
 static const struct iwl_base_params iwl7000_base_params = {
-       .eeprom_size = OTP_LOW_IMAGE_SIZE,
+       .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_7000,
        .num_of_queues = IWLAGN_NUM_QUEUES,
        .pll_cfg_val = 0,
        .shadow_ram_support = true,
@@ -107,6 +107,7 @@ static const struct iwl_base_params iwl7000_base_params = {
        .max_event_log_size = 512,
        .shadow_reg_enable = true,
        .pcie_l1_allowed = true,
+       .apmg_wake_up_wa = true,
 };
 
 static const struct iwl_ht_params iwl7000_ht_params = {
index f5bd82b885929a8c1188dc44ffdd27892dc2739b..51c41531d81d7f5af8354aced5da7e6b4e647f78 100644 (file)
 #define IWL8000_MODULE_FIRMWARE(api) IWL8000_FW_PRE __stringify(api) ".ucode"
 
 #define NVM_HW_SECTION_NUM_FAMILY_8000         10
+#define DEFAULT_NVM_FILE_FAMILY_8000           "iwl_nvm_8000.bin"
 
 static const struct iwl_base_params iwl8000_base_params = {
-       .eeprom_size = OTP_LOW_IMAGE_SIZE,
+       .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_8000,
        .num_of_queues = IWLAGN_NUM_QUEUES,
        .pll_cfg_val = 0,
        .shadow_ram_support = true,
@@ -118,6 +119,7 @@ const struct iwl_cfg iwl8260_2ac_cfg = {
        .ht_params = &iwl8000_ht_params,
        .nvm_ver = IWL8000_NVM_VERSION,
        .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
+       .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000,
 };
 
 const struct iwl_cfg iwl8260_n_cfg = {
@@ -127,6 +129,7 @@ const struct iwl_cfg iwl8260_n_cfg = {
        .ht_params = &iwl8000_ht_params,
        .nvm_ver = IWL8000_NVM_VERSION,
        .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
+       .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000,
 };
 
 MODULE_FIRMWARE(IWL8000_MODULE_FIRMWARE(IWL8000_UCODE_API_OK));
index 7f37fb86837b7a46569ba74f90df0a7bdb8d0271..04a483d386592c4cf359d741328844b2c0f02e51 100644 (file)
 
 /* EEPROM */
 #define IWLAGN_EEPROM_IMG_SIZE         2048
-/* OTP */
-/* lower blocks contain EEPROM image and calibration data */
-#define OTP_LOW_IMAGE_SIZE             (2 * 512 * sizeof(u16)) /* 2 KB */
+
 /* high blocks contain PAPD data */
 #define OTP_HIGH_IMAGE_SIZE_6x00        (6 * 512 * sizeof(u16)) /* 6 KB */
 #define OTP_HIGH_IMAGE_SIZE_1000        (0x200 * sizeof(u16)) /* 1024 bytes */
index 3f17dc3f2c8a9fdde83bddb254efb8cc8d33502f..b7047905f41a38ce6a88f27b93ad75384306958a 100644 (file)
@@ -146,6 +146,9 @@ static inline u8 num_of_ant(u8 mask)
  * @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
+ * @apmg_wake_up_wa: should the MAC access REQ be asserted when a command
+ *     is in flight. This is due to a HW bug in 7260, 3160 and 7265.
+ * @scd_chain_ext_wa: should the chain extension feature in SCD be disabled.
  */
 struct iwl_base_params {
        int eeprom_size;
@@ -160,6 +163,8 @@ struct iwl_base_params {
        u32 max_event_log_size;
        const bool shadow_reg_enable;
        const bool pcie_l1_allowed;
+       const bool apmg_wake_up_wa;
+       const bool scd_chain_ext_wa;
 };
 
 /*
@@ -188,6 +193,11 @@ struct iwl_ht_params {
 #define EEPROM_6000_REG_BAND_24_HT40_CHANNELS  0x80
 #define EEPROM_REGULATORY_BAND_NO_HT40         0
 
+/* lower blocks contain EEPROM image and calibration data */
+#define OTP_LOW_IMAGE_SIZE             (2 * 512 * sizeof(u16)) /* 2 KB */
+#define OTP_LOW_IMAGE_SIZE_FAMILY_7000 (16 * 512 * sizeof(u16)) /* 16 KB */
+#define OTP_LOW_IMAGE_SIZE_FAMILY_8000 (32 * 512 * sizeof(u16)) /* 32 KB */
+
 struct iwl_eeprom_params {
        const u8 regulatory_bands[7];
        bool enhanced_txpower;
@@ -264,6 +274,8 @@ struct iwl_cfg {
        u8   nvm_hw_section_num;
        bool lp_xtal_workaround;
        const struct iwl_pwr_tx_backoff *pwr_tx_backoffs;
+       bool no_power_up_nic_in_init;
+       const char *default_nvm_file;
 };
 
 /*
index 8a44f594528df6c4f503face66300c93d79f1f44..09feff4fa226e781b1b8350ade568e9001709335 100644 (file)
@@ -61,8 +61,6 @@
  *
  *****************************************************************************/
 
-#define DEBUG
-
 #include <linux/device.h>
 #include <linux/interrupt.h>
 #include <linux/export.h>
@@ -128,8 +126,8 @@ void __iwl_dbg(struct device *dev,
 #ifdef CONFIG_IWLWIFI_DEBUG
        if (iwl_have_debug_level(level) &&
            (!limit || net_ratelimit()))
-               dev_dbg(dev, "%c %s %pV", in_interrupt() ? 'I' : 'U',
-                       function, &vaf);
+               dev_printk(KERN_DEBUG, dev, "%c %s %pV",
+                          in_interrupt() ? 'I' : 'U', function, &vaf);
 #endif
        trace_iwlwifi_dbg(level, in_interrupt(), function, &vaf);
        va_end(args);
index c8cbdbe15924a61123d76828c0d1f6c7686dcfe4..295083510e729a76f13347a568c661acbdfdda52 100644 (file)
@@ -47,12 +47,32 @@ void __iwl_warn(struct device *dev, const char *fmt, ...) __printf(2, 3);
 void __iwl_info(struct device *dev, const char *fmt, ...) __printf(2, 3);
 void __iwl_crit(struct device *dev, const char *fmt, ...) __printf(2, 3);
 
+/* not all compilers can evaluate strlen() at compile time, so use sizeof() */
+#define CHECK_FOR_NEWLINE(f) BUILD_BUG_ON(f[sizeof(f) - 2] != '\n')
+
 /* No matter what is m (priv, bus, trans), this will work */
-#define IWL_ERR(m, f, a...) __iwl_err((m)->dev, false, false, f, ## a)
-#define IWL_ERR_DEV(d, f, a...) __iwl_err((d), false, false, f, ## a)
-#define IWL_WARN(m, f, a...) __iwl_warn((m)->dev, f, ## a)
-#define IWL_INFO(m, f, a...) __iwl_info((m)->dev, f, ## a)
-#define IWL_CRIT(m, f, a...) __iwl_crit((m)->dev, f, ## a)
+#define IWL_ERR_DEV(d, f, a...)                                                \
+       do {                                                            \
+               CHECK_FOR_NEWLINE(f);                                   \
+               __iwl_err((d), false, false, f, ## a);                  \
+       } while (0)
+#define IWL_ERR(m, f, a...)                                            \
+       IWL_ERR_DEV((m)->dev, f, ## a)
+#define IWL_WARN(m, f, a...)                                           \
+       do {                                                            \
+               CHECK_FOR_NEWLINE(f);                                   \
+               __iwl_warn((m)->dev, f, ## a);                          \
+       } while (0)
+#define IWL_INFO(m, f, a...)                                           \
+       do {                                                            \
+               CHECK_FOR_NEWLINE(f);                                   \
+               __iwl_info((m)->dev, f, ## a);                          \
+       } while (0)
+#define IWL_CRIT(m, f, a...)                                           \
+       do {                                                            \
+               CHECK_FOR_NEWLINE(f);                                   \
+               __iwl_crit((m)->dev, f, ## a);                          \
+       } while (0)
 
 #if defined(CONFIG_IWLWIFI_DEBUG) || defined(CONFIG_IWLWIFI_DEVICE_TRACING)
 void __iwl_dbg(struct device *dev,
@@ -72,12 +92,17 @@ do {                                                                        \
                       DUMP_PREFIX_OFFSET, 16, 1, p, len, 1);           \
 } while (0)
 
+#define __IWL_DEBUG_DEV(dev, level, limit, fmt, args...)               \
+       do {                                                            \
+               CHECK_FOR_NEWLINE(fmt);                                 \
+               __iwl_dbg(dev, level, limit, __func__, fmt, ##args);    \
+       } while (0)
 #define IWL_DEBUG(m, level, fmt, args...)                              \
-       __iwl_dbg((m)->dev, level, false, __func__, fmt, ##args)
+       __IWL_DEBUG_DEV((m)->dev, level, false, fmt, ##args)
 #define IWL_DEBUG_DEV(dev, level, fmt, args...)                                \
-       __iwl_dbg((dev), level, false, __func__, fmt, ##args)
+       __IWL_DEBUG_DEV(dev, level, false, fmt, ##args)
 #define IWL_DEBUG_LIMIT(m, level, fmt, args...)                                \
-       __iwl_dbg((m)->dev, level, true, __func__, fmt, ##args)
+       __IWL_DEBUG_DEV((m)->dev, level, true, fmt, ##args)
 
 #ifdef CONFIG_IWLWIFI_DEBUG
 #define iwl_print_hex_dump(m, level, p, len)                           \
index 0a3e841b44a9ebe81cbd40b0313e8cafcc355f3a..f2a5c12269a3ed7de811399580c9ea908bc89173 100644 (file)
@@ -1243,6 +1243,7 @@ struct iwl_mod_params iwlwifi_mod_params = {
        .bt_coex_active = true,
        .power_level = IWL_POWER_INDEX_1,
        .wd_disable = true,
+       .uapsd_disable = false,
        /* the rest are 0 by default */
 };
 IWL_EXPORT_SYMBOL(iwlwifi_mod_params);
@@ -1356,6 +1357,10 @@ MODULE_PARM_DESC(wd_disable,
 module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO);
 MODULE_PARM_DESC(nvm_file, "NVM file name");
 
+module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable,
+                  bool, S_IRUGO);
+MODULE_PARM_DESC(uapsd_disable, "disable U-APSD functionality (default: N)");
+
 /*
  * set bt_coex_active to true, uCode will do kill/defer
  * every time the priority line is asserted (BT is sending signals on the
similarity index 82%
rename from drivers/net/wireless/iwlwifi/mvm/fw-error-dump.h
rename to drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
index 58c8941c0d95ef6d6e0d6bb51349663dbf3b93dd..2953ffceda3881d29be37812544a374816b2130c 100644 (file)
  * enum iwl_fw_error_dump_type - types of data in the dump file
  * @IWL_FW_ERROR_DUMP_SRAM:
  * @IWL_FW_ERROR_DUMP_REG:
+ * @IWL_FW_ERROR_DUMP_RXF:
+ * @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as
+ *     &struct iwl_fw_error_dump_txcmd packets
  */
 enum iwl_fw_error_dump_type {
        IWL_FW_ERROR_DUMP_SRAM = 0,
        IWL_FW_ERROR_DUMP_REG = 1,
+       IWL_FW_ERROR_DUMP_RXF = 2,
+       IWL_FW_ERROR_DUMP_TXCMD = 3,
 
        IWL_FW_ERROR_DUMP_MAX,
 };
@@ -89,7 +94,7 @@ struct iwl_fw_error_dump_data {
        __le32 type;
        __le32 len;
        __u8 data[];
-} __packed __aligned(4);
+} __packed;
 
 /**
  * struct iwl_fw_error_dump_file - the layout of the header of the file
@@ -101,6 +106,29 @@ struct iwl_fw_error_dump_file {
        __le32 barker;
        __le32 file_len;
        u8 data[0];
-} __packed __aligned(4);
+} __packed;
+
+/**
+ * struct iwl_fw_error_dump_txcmd - TX command data
+ * @cmdlen: original length of command
+ * @caplen: captured length of command (may be less)
+ * @data: captured command data, @caplen bytes
+ */
+struct iwl_fw_error_dump_txcmd {
+       __le32 cmdlen;
+       __le32 caplen;
+       u8 data[];
+} __packed;
+
+/**
+ * iwl_mvm_fw_error_next_data - advance fw error dump data pointer
+ * @data: previous data block
+ * Returns: next data block
+ */
+static inline struct iwl_fw_error_dump_data *
+iwl_mvm_fw_error_next_data(struct iwl_fw_error_dump_data *data)
+{
+       return (void *)(data->data + le32_to_cpu(data->len));
+}
 
 #endif /* __fw_error_dump_h__ */
index d14f19339d6140607c99d1b6660b039f9ac4aa66..0aa7c0085c9fd04554b1a3314a2212638f920a78 100644 (file)
  * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w).
  * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P.
  * @IWL_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS
- * @IWL_UCODE_TLV_FLAGS_UAPSD: This uCode image supports uAPSD
+ * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: This uCode image supports uAPSD
  * @IWL_UCODE_TLV_FLAGS_SHORT_BL: 16 entries of black list instead of 64 in scan
  *     offload profile config command.
- * @IWL_UCODE_TLV_FLAGS_RX_ENERGY_API: supports rx signal strength api
- * @IWL_UCODE_TLV_FLAGS_TIME_EVENT_API_V2: using the new time event API.
  * @IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six
  *     (rather than two) IPv6 addresses
- * @IWL_UCODE_TLV_FLAGS_BF_UPDATED: new beacon filtering API
  * @IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element
  *     from the probe request template.
- * @IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API: modified D3 API to allow keeping
- *     connection when going back to D0
  * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version)
  * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version)
- * @IWL_UCODE_TLV_FLAGS_SCHED_SCAN: this uCode image supports scheduled scan.
- * @IWL_UCODE_TLV_FLAGS_STA_KEY_CMD: new ADD_STA and ADD_STA_KEY command API
- * @IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD: support device wide power command
- *     containing CAM (Continuous Active Mode) indication.
+ * @IWL_UCODE_TLV_FLAGS_P2P_PM: P2P client supports PM as a stand alone MAC
  * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_DCM: support power save on BSS station and
  *     P2P client interfaces simultaneously if they are in different bindings.
+ * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_SCM: support power save on BSS station and
+ *     P2P client interfaces simultaneously if they are in same bindings.
  * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save
  * @IWL_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering.
  * @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients
+ * @IWL_UCODE_TLV_FLAGS_EBS_SUPPORT: this uCode image supports EBS.
  */
 enum iwl_ucode_tlv_flag {
        IWL_UCODE_TLV_FLAGS_PAN                 = BIT(0),
@@ -104,22 +99,16 @@ enum iwl_ucode_tlv_flag {
        IWL_UCODE_TLV_FLAGS_MFP                 = BIT(2),
        IWL_UCODE_TLV_FLAGS_P2P                 = BIT(3),
        IWL_UCODE_TLV_FLAGS_DW_BC_TABLE         = BIT(4),
-       IWL_UCODE_TLV_FLAGS_NEWBT_COEX          = BIT(5),
-       IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT      = BIT(6),
        IWL_UCODE_TLV_FLAGS_SHORT_BL            = BIT(7),
-       IWL_UCODE_TLV_FLAGS_RX_ENERGY_API       = BIT(8),
-       IWL_UCODE_TLV_FLAGS_TIME_EVENT_API_V2   = BIT(9),
        IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS     = BIT(10),
-       IWL_UCODE_TLV_FLAGS_BF_UPDATED          = BIT(11),
        IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID       = BIT(12),
-       IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API   = BIT(14),
        IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL    = BIT(15),
        IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE    = BIT(16),
-       IWL_UCODE_TLV_FLAGS_SCHED_SCAN          = BIT(17),
-       IWL_UCODE_TLV_FLAGS_STA_KEY_CMD         = BIT(19),
-       IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD       = BIT(20),
+       IWL_UCODE_TLV_FLAGS_P2P_PM              = BIT(21),
        IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM      = BIT(22),
+       IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM      = BIT(23),
        IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT       = BIT(24),
+       IWL_UCODE_TLV_FLAGS_EBS_SUPPORT         = BIT(25),
        IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD        = BIT(26),
        IWL_UCODE_TLV_FLAGS_BCAST_FILTERING     = BIT(29),
        IWL_UCODE_TLV_FLAGS_GO_UAPSD            = BIT(30),
@@ -128,9 +117,11 @@ enum iwl_ucode_tlv_flag {
 /**
  * enum iwl_ucode_tlv_api - ucode api
  * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field.
+ * @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA.
  */
 enum iwl_ucode_tlv_api {
        IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID     = BIT(0),
+       IWL_UCODE_TLV_API_CSA_FLOW              = BIT(4),
 };
 
 /**
@@ -183,6 +174,7 @@ enum iwl_ucode_sec {
 #define IWL_UCODE_SECTION_MAX 12
 #define IWL_API_ARRAY_SIZE     1
 #define IWL_CAPABILITIES_ARRAY_SIZE    1
+#define CPU1_CPU2_SEPARATOR_SECTION    0xFFFFCCCC
 
 struct iwl_ucode_capabilities {
        u32 max_probe_length;
@@ -205,6 +197,11 @@ struct fw_img {
        bool is_dual_cpus;
 };
 
+struct iwl_sf_region {
+       u32 addr;
+       u32 size;
+};
+
 /* uCode version contains 4 values: Major/Minor/API/Serial */
 #define IWL_UCODE_MAJOR(ver)   (((ver) & 0xFF000000) >> 24)
 #define IWL_UCODE_MINOR(ver)   (((ver) & 0x00FF0000) >> 16)
index 44cc3cf45762d1465e47627b70eea990f257e3a3..5eef4ae7333bad13527327cc851f022f23e3f991 100644 (file)
@@ -33,6 +33,7 @@
 #include "iwl-io.h"
 #include "iwl-csr.h"
 #include "iwl-debug.h"
+#include "iwl-prph.h"
 #include "iwl-fh.h"
 
 #define IWL_POLL_INTERVAL 10   /* microseconds */
@@ -183,6 +184,23 @@ void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
 }
 IWL_EXPORT_SYMBOL(iwl_clear_bits_prph);
 
+void iwl_force_nmi(struct iwl_trans *trans)
+{
+       /*
+        * In HW previous to the 8000 HW family, and in the 8000 HW family
+        * itself when the revision step==0, the DEVICE_SET_NMI_REG is used
+        * to force an NMI. Otherwise, a different register -
+        * DEVICE_SET_NMI_8000B_REG - is used.
+        */
+       if ((trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) ||
+           ((trans->hw_rev & 0xc) == 0x0))
+               iwl_write_prph(trans, DEVICE_SET_NMI_REG, DEVICE_SET_NMI_VAL);
+       else
+               iwl_write_prph(trans, DEVICE_SET_NMI_8000B_REG,
+                              DEVICE_SET_NMI_8000B_VAL);
+}
+IWL_EXPORT_SYMBOL(iwl_force_nmi);
+
 static const char *get_fh_string(int cmd)
 {
 #define IWL_CMD(x) case x: return #x
index 665ddd9dbbc48ff5dec47f246b8efb0639fb5c86..705d12c079e8b2ef2492ca82a56aa92d64cfe09b 100644 (file)
@@ -80,6 +80,7 @@ void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
 void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
                            u32 bits, u32 mask);
 void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
+void iwl_force_nmi(struct iwl_trans *trans);
 
 /* Error handling */
 int iwl_dump_fh(struct iwl_trans *trans, char **buf);
index d994317db85b72cbfc11f602e2d414efc54233b1..d051857729ab8e2f238115165c482d09033147db 100644 (file)
@@ -119,6 +119,7 @@ struct iwl_mod_params {
 #endif
        int ant_coupling;
        char *nvm_file;
+       bool uapsd_disable;
 };
 
 #endif /* #__iwl_modparams_h__ */
index 6be30c69850619f81c2468febb3634fd5cf390fd..85eee79c495c8f1080d9844a3f71ffad1b0d023d 100644 (file)
@@ -62,6 +62,7 @@
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/export.h>
+#include <linux/etherdevice.h>
 #include "iwl-drv.h"
 #include "iwl-modparams.h"
 #include "iwl-nvm-parse.h"
@@ -127,19 +128,20 @@ static const u8 iwl_nvm_channels[] = {
 
 static const u8 iwl_nvm_channels_family_8000[] = {
        /* 2.4 GHz */
-       1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+       1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
        /* 5 GHz */
        36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92,
        96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
        149, 153, 157, 161, 165, 169, 173, 177, 181
 };
 
-#define IWL_NUM_CHANNELS       ARRAY_SIZE(iwl_nvm_channels)
+#define IWL_NUM_CHANNELS               ARRAY_SIZE(iwl_nvm_channels)
 #define IWL_NUM_CHANNELS_FAMILY_8000   ARRAY_SIZE(iwl_nvm_channels_family_8000)
-#define NUM_2GHZ_CHANNELS      14
-#define FIRST_2GHZ_HT_MINUS    5
-#define LAST_2GHZ_HT_PLUS      9
-#define LAST_5GHZ_HT           161
+#define NUM_2GHZ_CHANNELS              14
+#define NUM_2GHZ_CHANNELS_FAMILY_8000  14
+#define FIRST_2GHZ_HT_MINUS            5
+#define LAST_2GHZ_HT_PLUS              9
+#define LAST_5GHZ_HT                   161
 
 #define DEFAULT_MAX_TX_POWER 16
 
@@ -202,21 +204,23 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
        struct ieee80211_channel *channel;
        u16 ch_flags;
        bool is_5ghz;
-       int num_of_ch;
+       int num_of_ch, num_2ghz_channels;
        const u8 *nvm_chan;
 
        if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
                num_of_ch = IWL_NUM_CHANNELS;
                nvm_chan = &iwl_nvm_channels[0];
+               num_2ghz_channels = NUM_2GHZ_CHANNELS;
        } else {
                num_of_ch = IWL_NUM_CHANNELS_FAMILY_8000;
                nvm_chan = &iwl_nvm_channels_family_8000[0];
+               num_2ghz_channels = NUM_2GHZ_CHANNELS_FAMILY_8000;
        }
 
        for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) {
                ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx);
 
-               if (ch_idx >= NUM_2GHZ_CHANNELS &&
+               if (ch_idx >= num_2ghz_channels &&
                    !data->sku_cap_band_52GHz_enable)
                        ch_flags &= ~NVM_CHANNEL_VALID;
 
@@ -225,7 +229,7 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
                                         "Ch. %d Flags %x [%sGHz] - No traffic\n",
                                         nvm_chan[ch_idx],
                                         ch_flags,
-                                        (ch_idx >= NUM_2GHZ_CHANNELS) ?
+                                        (ch_idx >= num_2ghz_channels) ?
                                         "5.2" : "2.4");
                        continue;
                }
@@ -234,7 +238,7 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
                n_channels++;
 
                channel->hw_value = nvm_chan[ch_idx];
-               channel->band = (ch_idx < NUM_2GHZ_CHANNELS) ?
+               channel->band = (ch_idx < num_2ghz_channels) ?
                                IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
                channel->center_freq =
                        ieee80211_channel_to_frequency(
@@ -242,7 +246,7 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
 
                /* TODO: Need to be dependent to the NVM */
                channel->flags = IEEE80211_CHAN_NO_HT40;
-               if (ch_idx < NUM_2GHZ_CHANNELS &&
+               if (ch_idx < num_2ghz_channels &&
                    (ch_flags & NVM_CHANNEL_40MHZ)) {
                        if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS)
                                channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
@@ -250,7 +254,7 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
                                channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
                } else if (nvm_chan[ch_idx] <= LAST_5GHZ_HT &&
                           (ch_flags & NVM_CHANNEL_40MHZ)) {
-                       if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0)
+                       if ((ch_idx - num_2ghz_channels) % 2 == 0)
                                channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
                        else
                                channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
@@ -447,13 +451,7 @@ static void iwl_set_hw_address(const struct iwl_cfg *cfg,
                               struct iwl_nvm_data *data,
                               const __le16 *nvm_sec)
 {
-       u8 hw_addr[ETH_ALEN];
-
-       if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
-               memcpy(hw_addr, nvm_sec + HW_ADDR, ETH_ALEN);
-       else
-               memcpy(hw_addr, nvm_sec + MAC_ADDRESS_OVERRIDE_FAMILY_8000,
-                      ETH_ALEN);
+       const u8 *hw_addr = (const u8 *)(nvm_sec + HW_ADDR);
 
        /* The byte order is little endian 16 bit, meaning 214365 */
        data->hw_addr[0] = hw_addr[1];
@@ -464,6 +462,41 @@ static void iwl_set_hw_address(const struct iwl_cfg *cfg,
        data->hw_addr[5] = hw_addr[4];
 }
 
+static void iwl_set_hw_address_family_8000(const struct iwl_cfg *cfg,
+                                          struct iwl_nvm_data *data,
+                                          const __le16 *mac_override,
+                                          const __le16 *nvm_hw)
+{
+       const u8 *hw_addr;
+
+       if (mac_override) {
+               hw_addr = (const u8 *)(mac_override +
+                                MAC_ADDRESS_OVERRIDE_FAMILY_8000);
+
+               /* The byte order is little endian 16 bit, meaning 214365 */
+               data->hw_addr[0] = hw_addr[1];
+               data->hw_addr[1] = hw_addr[0];
+               data->hw_addr[2] = hw_addr[3];
+               data->hw_addr[3] = hw_addr[2];
+               data->hw_addr[4] = hw_addr[5];
+               data->hw_addr[5] = hw_addr[4];
+
+               if (is_valid_ether_addr(hw_addr))
+                       return;
+       }
+
+       /* take the MAC address from the OTP */
+       hw_addr = (const u8 *)(nvm_hw + HW_ADDR0_FAMILY_8000);
+       data->hw_addr[0] = hw_addr[3];
+       data->hw_addr[1] = hw_addr[2];
+       data->hw_addr[2] = hw_addr[1];
+       data->hw_addr[3] = hw_addr[0];
+
+       hw_addr = (const u8 *)(nvm_hw + HW_ADDR1_FAMILY_8000);
+       data->hw_addr[4] = hw_addr[1];
+       data->hw_addr[5] = hw_addr[0];
+}
+
 struct iwl_nvm_data *
 iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
                   const __le16 *nvm_hw, const __le16 *nvm_sw,
@@ -523,7 +556,7 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
                                rx_chains);
        } else {
                /* MAC address in family 8000 */
-               iwl_set_hw_address(cfg, data, mac_override);
+               iwl_set_hw_address_family_8000(cfg, data, mac_override, nvm_hw);
 
                iwl_init_sbands(dev, cfg, data, regulatory,
                                sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains,
index ea29504ac61704c39c24a117dec0a5d92aa58376..99785c892f963c7435b0048c4a097f6cae9e7808 100644 (file)
@@ -63,6 +63,7 @@
 #ifndef __iwl_op_mode_h__
 #define __iwl_op_mode_h__
 
+#include <linux/netdevice.h>
 #include <linux/debugfs.h>
 
 struct iwl_op_mode;
@@ -112,8 +113,11 @@ struct iwl_cfg;
  * @stop: stop the op_mode. Must free all the memory allocated.
  *     May sleep
  * @rx: Rx notification to the op_mode. rxb is the Rx buffer itself. Cmd is the
- *     HCMD this Rx responds to.
- *     This callback may sleep, it is called from a threaded IRQ handler.
+ *     HCMD this Rx responds to. Can't sleep.
+ * @napi_add: NAPI initialisation. The transport is fully responsible for NAPI,
+ *     but the higher layers need to know about it (in particular mac80211 to
+ *     to able to call the right NAPI RX functions); this function is needed
+ *     to eventually call netif_napi_add() with higher layer involvement.
  * @queue_full: notifies that a HW queue is full.
  *     Must be atomic and called with BH disabled.
  * @queue_not_full: notifies that a HW queue is not full any more.
@@ -143,6 +147,11 @@ struct iwl_op_mode_ops {
        void (*stop)(struct iwl_op_mode *op_mode);
        int (*rx)(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb,
                  struct iwl_device_cmd *cmd);
+       void (*napi_add)(struct iwl_op_mode *op_mode,
+                        struct napi_struct *napi,
+                        struct net_device *napi_dev,
+                        int (*poll)(struct napi_struct *, int),
+                        int weight);
        void (*queue_full)(struct iwl_op_mode *op_mode, int queue);
        void (*queue_not_full)(struct iwl_op_mode *op_mode, int queue);
        bool (*hw_rf_kill)(struct iwl_op_mode *op_mode, bool state);
@@ -180,7 +189,6 @@ static inline int iwl_op_mode_rx(struct iwl_op_mode *op_mode,
                                  struct iwl_rx_cmd_buffer *rxb,
                                  struct iwl_device_cmd *cmd)
 {
-       might_sleep();
        return op_mode->ops->rx(op_mode, rxb, cmd);
 }
 
@@ -249,4 +257,15 @@ static inline int iwl_op_mode_exit_d0i3(struct iwl_op_mode *op_mode)
        return op_mode->ops->exit_d0i3(op_mode);
 }
 
+static inline void iwl_op_mode_napi_add(struct iwl_op_mode *op_mode,
+                                       struct napi_struct *napi,
+                                       struct net_device *napi_dev,
+                                       int (*poll)(struct napi_struct *, int),
+                                       int weight)
+{
+       if (!op_mode->ops->napi_add)
+               return;
+       op_mode->ops->napi_add(op_mode, napi, napi_dev, poll, weight);
+}
+
 #endif /* __iwl_op_mode_h__ */
index b761ac4822a35b1e6a8b59f952655d39f1f0cb82..d4fb5cad07ea1d36c508c25ec83450a2e80e3807 100644 (file)
@@ -345,7 +345,6 @@ static int iwl_send_phy_db_cmd(struct iwl_phy_db *phy_db, u16 type,
        struct iwl_phy_db_cmd phy_db_cmd;
        struct iwl_host_cmd cmd = {
                .id = PHY_DB_CMD,
-               .flags = CMD_SYNC,
        };
 
        IWL_DEBUG_INFO(phy_db->trans,
@@ -393,13 +392,13 @@ static int iwl_phy_db_send_all_channel_groups(
                                          entry->data);
                if (err) {
                        IWL_ERR(phy_db->trans,
-                               "Can't SEND phy_db section %d (%d), err %d",
+                               "Can't SEND phy_db section %d (%d), err %d\n",
                                type, i, err);
                        return err;
                }
 
                IWL_DEBUG_INFO(phy_db->trans,
-                              "Sent PHY_DB HCMD, type = %d num = %d",
+                              "Sent PHY_DB HCMD, type = %d num = %d\n",
                               type, i);
        }
 
@@ -451,7 +450,7 @@ int iwl_send_phy_db_data(struct iwl_phy_db *phy_db)
                                                 IWL_NUM_PAPD_CH_GROUPS);
        if (err) {
                IWL_ERR(phy_db->trans,
-                       "Cannot send channel specific PAPD groups");
+                       "Cannot send channel specific PAPD groups\n");
                return err;
        }
 
@@ -461,7 +460,7 @@ int iwl_send_phy_db_data(struct iwl_phy_db *phy_db)
                                                 IWL_NUM_TXP_CH_GROUPS);
        if (err) {
                IWL_ERR(phy_db->trans,
-                       "Cannot send channel specific TX power groups");
+                       "Cannot send channel specific TX power groups\n");
                return err;
        }
 
index 5f657c501406cc995f7f8c065f26d9983ba43ffe..4997e27672b3ae22b4176df391a5bbb3ae9e1f4d 100644 (file)
 
 /* Device NMI register */
 #define DEVICE_SET_NMI_REG 0x00a01c30
+#define DEVICE_SET_NMI_VAL 0x1
+#define DEVICE_SET_NMI_8000B_REG 0x00a01c24
+#define DEVICE_SET_NMI_8000B_VAL 0x1000000
 
 /* Shared registers (0x0..0x3ff, via target indirect or periphery */
 #define SHR_BASE       0x00a10000
@@ -348,4 +351,12 @@ enum secure_load_status_reg {
 
 #define LMPM_SECURE_TIME_OUT   (100)
 
+/* Rx FIFO */
+#define RXF_SIZE_ADDR                  (0xa00c88)
+#define RXF_SIZE_BYTE_CND_POS          (7)
+#define RXF_SIZE_BYTE_CNT_MSK          (0x3ff << RXF_SIZE_BYTE_CND_POS)
+
+#define RXF_LD_FENCE_OFFSET_ADDR       (0xa00c10)
+#define RXF_FIFO_RD_FENCE_ADDR         (0xa00c0c)
+
 #endif                         /* __iwl_prph_h__ */
index 8cdb0dd618a6fdfcc8d57095e41974e6e22984ab..34d49e171fb4dae1f10928c931c26dc93a414e67 100644 (file)
@@ -189,10 +189,9 @@ static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt)
 /**
  * enum CMD_MODE - how to send the host commands ?
  *
- * @CMD_SYNC: The caller will be stalled until the fw responds to the command
  * @CMD_ASYNC: Return right away and don't wait 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_WANT_SKB: Not valid with CMD_ASYNC. The caller needs the buffer of
+ *     the response. The caller needs to call iwl_free_resp when done.
  * @CMD_HIGH_PRIO: The command is high priority - it goes to the front of the
  *     command queue, but after other high priority commands. valid only
  *     with CMD_ASYNC.
@@ -202,7 +201,6 @@ static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt)
  *     (i.e. mark it as non-idle).
  */
 enum CMD_MODE {
-       CMD_SYNC                = 0,
        CMD_ASYNC               = BIT(0),
        CMD_WANT_SKB            = BIT(1),
        CMD_SEND_IN_RFKILL      = BIT(2),
@@ -427,7 +425,7 @@ struct iwl_trans;
  * @send_cmd:send a host command. Must return -ERFKILL if RFkill is asserted.
  *     If RFkill is asserted in the middle of a SYNC host command, it must
  *     return -ERFKILL straight away.
- *     May sleep only if CMD_SYNC is set
+ *     May sleep only if CMD_ASYNC is not set
  * @tx: send an skb
  *     Must be atomic
  * @reclaim: free packet until ssn. Returns a list of freed packets.
@@ -437,8 +435,7 @@ struct iwl_trans;
  *     this one. The op_mode must not configure the HCMD queue. May sleep.
  * @txq_disable: de-configure a Tx queue to send AMPDUs
  *     Must be atomic
- * @wait_tx_queue_empty: wait until all tx queues are empty
- *     May sleep
+ * @wait_tx_queue_empty: wait until tx queues are empty. May sleep.
  * @dbgfs_register: add the dbgfs files under this directory. Files will be
  *     automatically deleted.
  * @write8: write a u8 to a register at offset ofs from the BAR
@@ -464,6 +461,11 @@ struct iwl_trans;
  * @unref: release a reference previously taken with @ref. Note that
  *     initially the reference count is 1, making an initial @unref
  *     necessary to allow low power states.
+ * @dump_data: fill a data dump with debug data, maybe containing last
+ *     TX'ed commands and similar. When called with a NULL buffer and
+ *     zero buffer length, provide only the (estimated) required buffer
+ *     length. Return the used buffer length.
+ *     Note that the transport must fill in the proper file headers.
  */
 struct iwl_trans_ops {
 
@@ -471,6 +473,8 @@ struct iwl_trans_ops {
        void (*op_mode_leave)(struct iwl_trans *iwl_trans);
        int (*start_fw)(struct iwl_trans *trans, const struct fw_img *fw,
                        bool run_in_rfkill);
+       int (*update_sf)(struct iwl_trans *trans,
+                        struct iwl_sf_region *st_fwrd_space);
        void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr);
        void (*stop_device)(struct iwl_trans *trans);
 
@@ -490,7 +494,7 @@ struct iwl_trans_ops {
        void (*txq_disable)(struct iwl_trans *trans, int queue);
 
        int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir);
-       int (*wait_tx_queue_empty)(struct iwl_trans *trans);
+       int (*wait_tx_queue_empty)(struct iwl_trans *trans, u32 txq_bm);
 
        void (*write8)(struct iwl_trans *trans, u32 ofs, u8 val);
        void (*write32)(struct iwl_trans *trans, u32 ofs, u32 val);
@@ -512,6 +516,10 @@ struct iwl_trans_ops {
                              u32 value);
        void (*ref)(struct iwl_trans *trans);
        void (*unref)(struct iwl_trans *trans);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       u32 (*dump_data)(struct iwl_trans *trans, void *buf, u32 buflen);
+#endif
 };
 
 /**
@@ -630,6 +638,17 @@ static inline int iwl_trans_start_fw(struct iwl_trans *trans,
        return trans->ops->start_fw(trans, fw, run_in_rfkill);
 }
 
+static inline int iwl_trans_update_sf(struct iwl_trans *trans,
+                                     struct iwl_sf_region *st_fwrd_space)
+{
+       might_sleep();
+
+       if (trans->ops->update_sf)
+               return trans->ops->update_sf(trans, st_fwrd_space);
+
+       return 0;
+}
+
 static inline void iwl_trans_stop_device(struct iwl_trans *trans)
 {
        might_sleep();
@@ -665,6 +684,16 @@ static inline void iwl_trans_unref(struct iwl_trans *trans)
                trans->ops->unref(trans);
 }
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+static inline u32 iwl_trans_dump_data(struct iwl_trans *trans,
+                                     void *buf, u32 buflen)
+{
+       if (!trans->ops->dump_data)
+               return 0;
+       return trans->ops->dump_data(trans, buf, buflen);
+}
+#endif
+
 static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
                                     struct iwl_host_cmd *cmd)
 {
@@ -678,7 +707,7 @@ static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
                return -EIO;
 
        if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) {
-               IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
+               IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
                return -EIO;
        }
 
@@ -720,7 +749,7 @@ static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb,
                return -EIO;
 
        if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
-               IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
+               IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
 
        return trans->ops->tx(trans, skb, dev_cmd, queue);
 }
@@ -729,7 +758,7 @@ static inline void iwl_trans_reclaim(struct iwl_trans *trans, int queue,
                                     int ssn, struct sk_buff_head *skbs)
 {
        if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
-               IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
+               IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
 
        trans->ops->reclaim(trans, queue, ssn, skbs);
 }
@@ -746,7 +775,7 @@ static inline void iwl_trans_txq_enable(struct iwl_trans *trans, int queue,
        might_sleep();
 
        if (unlikely((trans->state != IWL_TRANS_FW_ALIVE)))
-               IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
+               IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
 
        trans->ops->txq_enable(trans, queue, fifo, sta_id, tid,
                                 frame_limit, ssn);
@@ -759,12 +788,13 @@ static inline void iwl_trans_ac_txq_enable(struct iwl_trans *trans, int queue,
                             IWL_MAX_TID_COUNT, IWL_FRAME_LIMIT, 0);
 }
 
-static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans)
+static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans,
+                                               u32 txq_bm)
 {
        if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
-               IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
+               IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
 
-       return trans->ops->wait_tx_queue_empty(trans);
+       return trans->ops->wait_tx_queue_empty(trans, txq_bm);
 }
 
 static inline int iwl_trans_dbgfs_register(struct iwl_trans *trans,
index ccdd3b7c4cce38fb10caf66326e7c439f0c079ee..c30d7f64ec1e4e1c47a635e091c4e503c2ceaa83 100644 (file)
@@ -3,8 +3,9 @@ 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 sf.o
 iwlmvm-y += scan.o time-event.o rs.o
 iwlmvm-y += power.o coex.o
-iwlmvm-y += led.o tt.o offloading.o
+iwlmvm-y += tt.o offloading.o
 iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
+iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o
 iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
 
 ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../
index 0489314425cbdf4a4b782867644ee9a0a0ca4b63..c8c3b38228f02f9768b780a7bdd31273f49e9541 100644 (file)
@@ -104,12 +104,9 @@ static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
 #define BT_DISABLE_REDUCED_TXPOWER_THRESHOLD   (-65)
 #define BT_ANTENNA_COUPLING_THRESHOLD          (30)
 
-int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
+static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
 {
-       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX))
-               return 0;
-
-       return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, CMD_SYNC,
+       return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, 0,
                                    sizeof(struct iwl_bt_coex_prio_tbl_cmd),
                                    &iwl_bt_prio_tbl);
 }
@@ -127,10 +124,10 @@ const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX] = {
 };
 
 static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = {
-       cpu_to_le32(0xf0f0f0f0),
-       cpu_to_le32(0xc0c0c0c0),
-       cpu_to_le32(0xfcfcfcfc),
-       cpu_to_le32(0xff00ff00),
+       cpu_to_le32(0xf0f0f0f0), /* 50% */
+       cpu_to_le32(0xc0c0c0c0), /* 25% */
+       cpu_to_le32(0xfcfcfcfc), /* 75% */
+       cpu_to_le32(0xfefefefe), /* 87.5% */
 };
 
 static const __le32 iwl_single_shared_ant[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
@@ -303,8 +300,8 @@ static const __le64 iwl_ci_mask[][3] = {
 };
 
 static const __le32 iwl_bt_mprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE] = {
-       cpu_to_le32(0x22002200),
-       cpu_to_le32(0x33113311),
+       cpu_to_le32(0x28412201),
+       cpu_to_le32(0x11118451),
 };
 
 struct corunning_block_luts {
@@ -568,13 +565,13 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
                .id = BT_CONFIG,
                .len = { sizeof(*bt_cmd), },
                .dataflags = { IWL_HCMD_DFL_NOCOPY, },
-               .flags = CMD_SYNC,
        };
        int ret;
        u32 flags;
 
-       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX))
-               return 0;
+       ret = iwl_send_bt_prio_tbl(mvm);
+       if (ret)
+               return ret;
 
        bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
        if (!bt_cmd)
@@ -582,10 +579,12 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
        cmd.data[0] = bt_cmd;
 
        bt_cmd->max_kill = 5;
-       bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD,
-       bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling,
-       bt_cmd->bt4_tx_tx_delta_freq_thr = 15,
-       bt_cmd->bt4_tx_rx_max_freq0 = 15,
+       bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD;
+       bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling;
+       bt_cmd->bt4_tx_tx_delta_freq_thr = 15;
+       bt_cmd->bt4_tx_rx_max_freq0 = 15;
+       bt_cmd->override_primary_lut = BT_COEX_INVALID_LUT;
+       bt_cmd->override_secondary_lut = BT_COEX_INVALID_LUT;
 
        flags = iwlwifi_mod_params.bt_coex_active ?
                        BT_COEX_NW : BT_COEX_DISABLE;
@@ -663,7 +662,6 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
                .data[0] = &bt_cmd,
                .len = { sizeof(*bt_cmd), },
                .dataflags = { IWL_HCMD_DFL_NOCOPY, },
-               .flags = CMD_SYNC,
        };
        int ret = 0;
 
@@ -717,7 +715,8 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
        return ret;
 }
 
-int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable)
+static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
+                                      bool enable)
 {
        struct iwl_bt_coex_cmd *bt_cmd;
        /* Send ASYNC since this can be sent from an atomic context */
@@ -735,8 +734,7 @@ int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable)
                return 0;
 
        /* nothing to do */
-       if (mvmsta->bt_reduced_txpower_dbg ||
-           mvmsta->bt_reduced_txpower == enable)
+       if (mvmsta->bt_reduced_txpower == enable)
                return 0;
 
        bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC);
@@ -803,23 +801,10 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
 
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
+               /* Count BSSes vifs */
+               data->num_bss_ifaces++;
                /* default smps_mode for BSS / P2P client is AUTOMATIC */
                smps_mode = IEEE80211_SMPS_AUTOMATIC;
-               data->num_bss_ifaces++;
-
-               /*
-                * Count unassoc BSSes, relax SMSP constraints
-                * and disable reduced Tx Power
-                */
-               if (!vif->bss_conf.assoc) {
-                       iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
-                                           smps_mode);
-                       if (iwl_mvm_bt_coex_reduced_txp(mvm,
-                                                       mvmvif->ap_sta_id,
-                                                       false))
-                               IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
-                       return;
-               }
                break;
        case NL80211_IFTYPE_AP:
                /* default smps_mode for AP / GO is OFF */
@@ -845,8 +830,12 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
                /* ... relax constraints and disable rssi events */
                iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
                                    smps_mode);
-               if (vif->type == NL80211_IFTYPE_STATION)
+               data->reduced_tx_power = false;
+               if (vif->type == NL80211_IFTYPE_STATION) {
+                       iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
+                                                   false);
                        iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
+               }
                return;
        }
 
@@ -857,6 +846,11 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
                smps_mode = vif->type == NL80211_IFTYPE_AP ?
                                IEEE80211_SMPS_OFF :
                                IEEE80211_SMPS_DYNAMIC;
+
+       /* relax SMPS contraints for next association */
+       if (!vif->bss_conf.assoc)
+               smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
        IWL_DEBUG_COEX(data->mvm,
                       "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
                       mvmvif->id, data->notif->bt_status, bt_activity_grading,
@@ -903,22 +897,18 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
                /* if secondary is not NULL, it might be a GO */
                data->secondary = chanctx_conf;
 
-       /* don't reduce the Tx power if in loose scheme */
+       /*
+        * don't reduce the Tx power if one of these is true:
+        *  we are in LOOSE
+        *  single share antenna product
+        *  BT is active
+        *  we are associated
+        */
        if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
-           mvm->cfg->bt_shared_single_ant) {
-               data->reduced_tx_power = false;
-               iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
-               return;
-       }
-
-       /* reduced Txpower only if BT is on, so ...*/
-       if (!data->notif->bt_status) {
-               /* ... cancel reduced Tx power ... */
-               if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
-                       IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
+           mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc ||
+           !data->notif->bt_status) {
                data->reduced_tx_power = false;
-
-               /* ... and there is no need to get reports on RSSI any more. */
+               iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false);
                iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
                return;
        }
@@ -1022,9 +1012,9 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
 
        /* Don't spam the fw with the same command over and over */
        if (memcmp(&cmd, &mvm->last_bt_ci_cmd, sizeof(cmd))) {
-               if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, CMD_SYNC,
+               if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0,
                                         sizeof(cmd), &cmd))
-                       IWL_ERR(mvm, "Failed to send BT_CI cmd");
+                       IWL_ERR(mvm, "Failed to send BT_CI cmd\n");
                memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd));
        }
 
@@ -1039,7 +1029,6 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
                IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
 }
 
-/* upon association, the fw will send in BT Coex notification */
 int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
                             struct iwl_rx_cmd_buffer *rxb,
                             struct iwl_device_cmd *dev_cmd)
@@ -1215,6 +1204,17 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
        return iwl_get_coex_type(mvm, mvmsta->vif) == BT_COEX_TIGHT_LUT;
 }
 
+bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
+                                   enum ieee80211_band band)
+{
+       u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading);
+
+       if (band != IEEE80211_BAND_2GHZ)
+               return false;
+
+       return bt_activity >= BT_LOW_TRAFFIC;
+}
+
 u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
                           struct ieee80211_tx_info *info, u8 ac)
 {
@@ -1249,9 +1249,6 @@ u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
 
 void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm)
 {
-       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX))
-               return;
-
        iwl_mvm_bt_coex_notif_handle(mvm);
 }
 
@@ -1270,7 +1267,6 @@ int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
                .id = BT_CONFIG,
                .len = { sizeof(*bt_cmd), },
                .dataflags = { IWL_HCMD_DFL_NOCOPY, },
-               .flags = CMD_SYNC,
        };
 
        if (!IWL_MVM_BT_COEX_CORUNNING)
index e56f5a0edf855331a1411e76406a143176b5e9d5..645b3cfc29a5e5c0bcb10f6c9eaded6a1827e5ed 100644 (file)
@@ -193,8 +193,7 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw,
                        wkc.wep_key.key_offset = data->wep_key_idx;
                }
 
-               ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, CMD_SYNC,
-                                          sizeof(wkc), &wkc);
+               ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, 0, sizeof(wkc), &wkc);
                data->error = ret != 0;
 
                mvm->ptk_ivlen = key->iv_len;
@@ -341,7 +340,6 @@ static int iwl_mvm_send_patterns(struct iwl_mvm *mvm,
        struct iwl_host_cmd cmd = {
                .id = WOWLAN_PATTERNS,
                .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
-               .flags = CMD_SYNC,
        };
        int i, err;
 
@@ -518,7 +516,6 @@ static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm,
                .id = REMOTE_WAKE_CONFIG_CMD,
                .len = { sizeof(*cfg), },
                .dataflags = { IWL_HCMD_DFL_NOCOPY, },
-               .flags = CMD_SYNC,
        };
        int ret;
 
@@ -666,10 +663,8 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
        if (WARN_ON(!vif->bss_conf.assoc))
                return -EINVAL;
-       /* hack */
-       vif->bss_conf.assoc = false;
+
        ret = iwl_mvm_mac_ctxt_add(mvm, vif);
-       vif->bss_conf.assoc = true;
        if (ret)
                return ret;
 
@@ -705,7 +700,7 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                return ret;
        rcu_assign_pointer(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], ap_sta);
 
-       ret = iwl_mvm_mac_ctxt_changed(mvm, vif);
+       ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false);
        if (ret)
                return ret;
 
@@ -719,7 +714,7 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        for (i = 1; i < MAX_BINDINGS; i++)
                quota_cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID);
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, CMD_SYNC,
+       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0,
                                   sizeof(quota_cmd), &quota_cmd);
        if (ret)
                IWL_ERR(mvm, "Failed to send quota: %d\n", ret);
@@ -739,15 +734,13 @@ static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm,
        };
        struct iwl_host_cmd cmd = {
                .id = NON_QOS_TX_COUNTER_CMD,
-               .flags = CMD_SYNC | CMD_WANT_SKB,
+               .flags = CMD_WANT_SKB,
        };
        int err;
        u32 size;
 
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API) {
-               cmd.data[0] = &query_cmd;
-               cmd.len[0] = sizeof(query_cmd);
-       }
+       cmd.data[0] = &query_cmd;
+       cmd.len[0] = sizeof(query_cmd);
 
        err = iwl_mvm_send_cmd(mvm, &cmd);
        if (err)
@@ -758,10 +751,8 @@ static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm,
                err = -EINVAL;
        } else {
                err = le16_to_cpup((__le16 *)cmd.resp_pkt->data);
-               /* new API returns next, not last-used seqno */
-               if (mvm->fw->ucode_capa.flags &
-                               IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API)
-                       err = (u16) (err - 0x10);
+               /* firmware returns next, not last-used seqno */
+               err = (u16) (err - 0x10);
        }
 
        iwl_free_resp(&cmd);
@@ -785,11 +776,7 @@ void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 
        mvmvif->seqno_valid = false;
 
-       if (!(mvm->fw->ucode_capa.flags &
-                       IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API))
-               return;
-
-       if (iwl_mvm_send_cmd_pdu(mvm, NON_QOS_TX_COUNTER_CMD, CMD_SYNC,
+       if (iwl_mvm_send_cmd_pdu(mvm, NON_QOS_TX_COUNTER_CMD, 0,
                                 sizeof(query_cmd), &query_cmd))
                IWL_ERR(mvm, "failed to set non-QoS seqno\n");
 }
@@ -804,7 +791,7 @@ iwl_mvm_send_wowlan_config_cmd(struct iwl_mvm *mvm,
        if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID)
                cmd_len = sizeof(*cmd);
 
-       return iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, CMD_SYNC,
+       return iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0,
                                    cmd_len, cmd);
 }
 
@@ -833,7 +820,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
        };
        struct iwl_host_cmd d3_cfg_cmd = {
                .id = D3_CONFIG_CMD,
-               .flags = CMD_SYNC | CMD_WANT_SKB,
+               .flags = CMD_WANT_SKB,
                .data[0] = &d3_cfg_cmd_data,
                .len[0] = sizeof(d3_cfg_cmd_data),
        };
@@ -983,7 +970,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
                if (key_data.use_rsc_tsc) {
                        struct iwl_host_cmd rsc_tsc_cmd = {
                                .id = WOWLAN_TSC_RSC_PARAM,
-                               .flags = CMD_SYNC,
                                .data[0] = key_data.rsc_tsc,
                                .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
                                .len[0] = sizeof(*key_data.rsc_tsc),
@@ -997,7 +983,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
                if (key_data.use_tkip) {
                        ret = iwl_mvm_send_cmd_pdu(mvm,
                                                   WOWLAN_TKIP_PARAM,
-                                                  CMD_SYNC, sizeof(tkip_cmd),
+                                                  0, sizeof(tkip_cmd),
                                                   &tkip_cmd);
                        if (ret)
                                goto out;
@@ -1014,8 +1000,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
                        kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr;
 
                        ret = iwl_mvm_send_cmd_pdu(mvm,
-                                                  WOWLAN_KEK_KCK_MATERIAL,
-                                                  CMD_SYNC,
+                                                  WOWLAN_KEK_KCK_MATERIAL, 0,
                                                   sizeof(kek_kck_cmd),
                                                   &kek_kck_cmd);
                        if (ret)
@@ -1031,7 +1016,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
        if (ret)
                goto out;
 
-       ret = iwl_mvm_send_proto_offload(mvm, vif, false, CMD_SYNC);
+       ret = iwl_mvm_send_proto_offload(mvm, vif, false, 0);
        if (ret)
                goto out;
 
@@ -1043,7 +1028,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
        if (ret)
                goto out;
 
-       ret = iwl_mvm_power_update_mac(mvm, vif);
+       ret = iwl_mvm_power_update_mac(mvm);
        if (ret)
                goto out;
 
@@ -1082,6 +1067,15 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
 
 int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
 {
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       if (iwl_mvm_is_d0i3_supported(mvm)) {
+               mutex_lock(&mvm->d0i3_suspend_mutex);
+               __set_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
+               mutex_unlock(&mvm->d0i3_suspend_mutex);
+               return 0;
+       }
+
        return __iwl_mvm_suspend(hw, wowlan, false);
 }
 
@@ -1277,7 +1271,7 @@ static void iwl_mvm_set_tkip_rx_seq(struct tkip_sc *scs,
 }
 
 static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key,
-                                  struct iwl_wowlan_status_v6 *status)
+                                  struct iwl_wowlan_status *status)
 {
        union iwl_all_tsc_rsc *rsc = &status->gtk.rsc.all_tsc_rsc;
 
@@ -1294,7 +1288,7 @@ static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key,
 }
 
 struct iwl_mvm_d3_gtk_iter_data {
-       struct iwl_wowlan_status_v6 *status;
+       struct iwl_wowlan_status *status;
        void *last_gtk;
        u32 cipher;
        bool find_phase, unhandled_cipher;
@@ -1370,7 +1364,7 @@ static void iwl_mvm_d3_update_gtks(struct ieee80211_hw *hw,
 
 static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
                                          struct ieee80211_vif *vif,
-                                         struct iwl_wowlan_status_v6 *status)
+                                         struct iwl_wowlan_status *status)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm_d3_gtk_iter_data gtkdata = {
@@ -1465,10 +1459,10 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
        } err_info;
        struct iwl_host_cmd cmd = {
                .id = WOWLAN_GET_STATUSES,
-               .flags = CMD_SYNC | CMD_WANT_SKB,
+               .flags = CMD_WANT_SKB,
        };
        struct iwl_wowlan_status_data status;
-       struct iwl_wowlan_status_v6 *status_v6;
+       struct iwl_wowlan_status *fw_status;
        int ret, len, status_size, i;
        bool keep;
        struct ieee80211_sta *ap_sta;
@@ -1491,7 +1485,7 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
        }
 
        /* only for tracing for now */
-       ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, CMD_SYNC, 0, NULL);
+       ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, 0, 0, NULL);
        if (ret)
                IWL_ERR(mvm, "failed to query offload statistics (%d)\n", ret);
 
@@ -1505,10 +1499,7 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
        if (!cmd.resp_pkt)
                goto out_unlock;
 
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API)
-               status_size = sizeof(struct iwl_wowlan_status_v6);
-       else
-               status_size = sizeof(struct iwl_wowlan_status_v4);
+       status_size = sizeof(*fw_status);
 
        len = iwl_rx_packet_payload_len(cmd.resp_pkt);
        if (len < status_size) {
@@ -1516,35 +1507,18 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
                goto out_free_resp;
        }
 
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API) {
-               status_v6 = (void *)cmd.resp_pkt->data;
-
-               status.pattern_number = le16_to_cpu(status_v6->pattern_number);
-               for (i = 0; i < 8; i++)
-                       status.qos_seq_ctr[i] =
-                               le16_to_cpu(status_v6->qos_seq_ctr[i]);
-               status.wakeup_reasons = le32_to_cpu(status_v6->wakeup_reasons);
-               status.wake_packet_length =
-                       le32_to_cpu(status_v6->wake_packet_length);
-               status.wake_packet_bufsize =
-                       le32_to_cpu(status_v6->wake_packet_bufsize);
-               status.wake_packet = status_v6->wake_packet;
-       } else {
-               struct iwl_wowlan_status_v4 *status_v4;
-               status_v6 = NULL;
-               status_v4 = (void *)cmd.resp_pkt->data;
-
-               status.pattern_number = le16_to_cpu(status_v4->pattern_number);
-               for (i = 0; i < 8; i++)
-                       status.qos_seq_ctr[i] =
-                               le16_to_cpu(status_v4->qos_seq_ctr[i]);
-               status.wakeup_reasons = le32_to_cpu(status_v4->wakeup_reasons);
-               status.wake_packet_length =
-                       le32_to_cpu(status_v4->wake_packet_length);
-               status.wake_packet_bufsize =
-                       le32_to_cpu(status_v4->wake_packet_bufsize);
-               status.wake_packet = status_v4->wake_packet;
-       }
+       fw_status = (void *)cmd.resp_pkt->data;
+
+       status.pattern_number = le16_to_cpu(fw_status->pattern_number);
+       for (i = 0; i < 8; i++)
+               status.qos_seq_ctr[i] =
+                       le16_to_cpu(fw_status->qos_seq_ctr[i]);
+       status.wakeup_reasons = le32_to_cpu(fw_status->wakeup_reasons);
+       status.wake_packet_length =
+               le32_to_cpu(fw_status->wake_packet_length);
+       status.wake_packet_bufsize =
+               le32_to_cpu(fw_status->wake_packet_bufsize);
+       status.wake_packet = fw_status->wake_packet;
 
        if (len != status_size + ALIGN(status.wake_packet_bufsize, 4)) {
                IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
@@ -1571,7 +1545,7 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
 
        iwl_mvm_report_wakeup_reasons(mvm, vif, &status);
 
-       keep = iwl_mvm_setup_connection_keep(mvm, vif, status_v6);
+       keep = iwl_mvm_setup_connection_keep(mvm, vif, fw_status);
 
        iwl_free_resp(&cmd);
        return keep;
@@ -1674,6 +1648,19 @@ int iwl_mvm_resume(struct ieee80211_hw *hw)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 
+       if (iwl_mvm_is_d0i3_supported(mvm)) {
+               bool exit_now;
+
+               mutex_lock(&mvm->d0i3_suspend_mutex);
+               __clear_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
+               exit_now = __test_and_clear_bit(D0I3_PENDING_WAKEUP,
+                                               &mvm->d0i3_suspend_flags);
+               mutex_unlock(&mvm->d0i3_suspend_mutex);
+               if (exit_now)
+                       _iwl_mvm_exit_d0i3(mvm);
+               return 0;
+       }
+
        return __iwl_mvm_resume(mvm, false);
 }
 
index 9b59e1d7ae71ea888973992cb88363ba2371fdad..2e90ff795c13212d6d8ca7be35df935a907d6b3c 100644 (file)
@@ -103,10 +103,6 @@ static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm,
                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;
-               break;
        case MVM_DEBUGFS_PM_LPRX_ENA:
                IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled");
                dbgfs_pm->lprx_ena = val;
@@ -154,12 +150,6 @@ static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf,
                if (sscanf(buf + 16, "%d", &val) != 1)
                        return -EINVAL;
                param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT;
-       } else if (!strncmp("disable_power_off=", buf, 18) &&
-                  !(mvm->fw->ucode_capa.flags &
-                    IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD)) {
-               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;
@@ -185,7 +175,7 @@ static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf,
 
        mutex_lock(&mvm->mutex);
        iwl_dbgfs_update_pm(mvm, vif, param, val);
-       ret = iwl_mvm_power_update_mac(mvm, vif);
+       ret = iwl_mvm_power_update_mac(mvm);
        mutex_unlock(&mvm->mutex);
 
        return ret ?: count;
@@ -272,10 +262,9 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
                        struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
 
                        pos += scnprintf(buf+pos, bufsz-pos,
-                                        "ap_sta_id %d - reduced Tx power %d force %d\n",
+                                        "ap_sta_id %d - reduced Tx power %d\n",
                                         ap_sta_id,
-                                        mvm_sta->bt_reduced_txpower,
-                                        mvm_sta->bt_reduced_txpower_dbg);
+                                        mvm_sta->bt_reduced_txpower);
                }
        }
 
@@ -293,41 +282,6 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
 
-static ssize_t iwl_dbgfs_reduced_txp_write(struct ieee80211_vif *vif,
-                                          char *buf, size_t count,
-                                          loff_t *ppos)
-{
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_mvm *mvm = mvmvif->mvm;
-       struct iwl_mvm_sta *mvmsta;
-       bool reduced_tx_power;
-       int ret;
-
-       if (mvmvif->ap_sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))
-               return -ENOTCONN;
-
-       if (strtobool(buf, &reduced_tx_power) != 0)
-               return -EINVAL;
-
-       mutex_lock(&mvm->mutex);
-
-       mvmsta = iwl_mvm_sta_from_staid_protected(mvm, mvmvif->ap_sta_id);
-       if (IS_ERR_OR_NULL(mvmsta)) {
-               mutex_unlock(&mvm->mutex);
-               return -ENOTCONN;
-       }
-
-       mvmsta->bt_reduced_txpower_dbg = false;
-       ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
-                                         reduced_tx_power);
-       if (!ret)
-               mvmsta->bt_reduced_txpower_dbg = true;
-
-       mutex_unlock(&mvm->mutex);
-
-       return ret ? : count;
-}
-
 static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif,
                                enum iwl_dbgfs_bf_mask param, int value)
 {
@@ -462,9 +416,9 @@ static ssize_t iwl_dbgfs_bf_params_write(struct ieee80211_vif *vif, char *buf,
        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, CMD_SYNC);
+               ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
        else
-               ret = iwl_mvm_enable_beacon_filter(mvm, vif, CMD_SYNC);
+               ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
        mutex_unlock(&mvm->mutex);
 
        return ret ?: count;
@@ -568,7 +522,6 @@ MVM_DEBUGFS_READ_FILE_OPS(mac_params);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 10);
-MVM_DEBUGFS_WRITE_FILE_OPS(reduced_txp, 10);
 
 void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
@@ -592,8 +545,7 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
                return;
        }
 
-       if ((mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT) &&
-           iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
+       if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
            ((vif->type == NL80211_IFTYPE_STATION && !vif->p2p) ||
             (vif->type == NL80211_IFTYPE_STATION && vif->p2p &&
              mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM)))
@@ -601,7 +553,6 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
                                         S_IRUSR);
 
        MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR);
-       MVM_DEBUGFS_ADD_FILE_VIF(reduced_txp, mvmvif->dbgfs_dir, S_IWUSR);
        MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir,
                                 S_IRUSR | S_IWUSR);
 
index 1b52deea60812e4f6ac42c94412b1ecf518f44f7..29ca72695eaa60e0f53121dd45f1d080cdefba1d 100644 (file)
@@ -65,9 +65,8 @@
 #include "mvm.h"
 #include "sta.h"
 #include "iwl-io.h"
-#include "iwl-prph.h"
 #include "debugfs.h"
-#include "fw-error-dump.h"
+#include "iwl-fw-error-dump.h"
 
 static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf,
                                        size_t count, loff_t *ppos)
@@ -136,9 +135,6 @@ static int iwl_dbgfs_fw_error_dump_open(struct inode *inode, struct file *file)
 
        file->private_data = mvm->fw_error_dump;
        mvm->fw_error_dump = NULL;
-       kfree(mvm->fw_error_sram);
-       mvm->fw_error_sram = NULL;
-       mvm->fw_error_sram_len = 0;
        ret = 0;
 
 out:
@@ -684,7 +680,7 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mvm *mvm, char *buf,
                mvm->restart_fw++;
 
        /* take the return value to make compiler happy - it will fail anyway */
-       ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, CMD_SYNC, 0, NULL);
+       ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, 0, 0, NULL);
 
        mutex_unlock(&mvm->mutex);
 
@@ -694,7 +690,7 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mvm *mvm, char *buf,
 static ssize_t iwl_dbgfs_fw_nmi_write(struct iwl_mvm *mvm, char *buf,
                                      size_t count, loff_t *ppos)
 {
-       iwl_write_prph(mvm->trans, DEVICE_SET_NMI_REG, 1);
+       iwl_force_nmi(mvm->trans);
 
        return count;
 }
@@ -841,7 +837,7 @@ static ssize_t iwl_dbgfs_bcast_filters_write(struct iwl_mvm *mvm, char *buf,
        /* send updated bcast filtering configuration */
        if (mvm->dbgfs_bcast_filtering.override &&
            iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
-               err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC,
+               err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0,
                                           sizeof(cmd), &cmd);
        mutex_unlock(&mvm->mutex);
 
@@ -913,7 +909,7 @@ static ssize_t iwl_dbgfs_bcast_filters_macs_write(struct iwl_mvm *mvm,
        /* send updated bcast filtering configuration */
        if (mvm->dbgfs_bcast_filtering.override &&
            iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
-               err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC,
+               err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0,
                                           sizeof(cmd), &cmd);
        mutex_unlock(&mvm->mutex);
 
@@ -1004,6 +1000,7 @@ static ssize_t iwl_dbgfs_d0i3_refs_read(struct file *file,
        PRINT_MVM_REF(IWL_MVM_REF_P2P_CLIENT);
        PRINT_MVM_REF(IWL_MVM_REF_AP_IBSS);
        PRINT_MVM_REF(IWL_MVM_REF_USER);
+       PRINT_MVM_REF(IWL_MVM_REF_EXIT_WORK);
 
        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
@@ -1108,9 +1105,9 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8);
 
 static const struct file_operations iwl_dbgfs_fw_error_dump_ops = {
-        .open = iwl_dbgfs_fw_error_dump_open,
-        .read = iwl_dbgfs_fw_error_dump_read,
-        .release = iwl_dbgfs_fw_error_dump_release,
+       .open = iwl_dbgfs_fw_error_dump_open,
+       .read = iwl_dbgfs_fw_error_dump_read,
+       .release = iwl_dbgfs_fw_error_dump_release,
 };
 
 #ifdef CONFIG_IWLWIFI_BCAST_FILTERING
@@ -1138,9 +1135,8 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
        MVM_DEBUGFS_ADD_FILE(fw_error_dump, dbgfs_dir, S_IRUSR);
        MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR);
        MVM_DEBUGFS_ADD_FILE(bt_cmd, dbgfs_dir, S_IRUSR);
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD)
-               MVM_DEBUGFS_ADD_FILE(disable_power_off, mvm->debugfs_dir,
-                                    S_IRUSR | S_IWUSR);
+       MVM_DEBUGFS_ADD_FILE(disable_power_off, mvm->debugfs_dir,
+                            S_IRUSR | S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(fw_rx_stats, mvm->debugfs_dir, S_IRUSR);
        MVM_DEBUGFS_ADD_FILE(drv_rx_stats, mvm->debugfs_dir, S_IRUSR);
        MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR);
index 21877e5966a8093d6780d3297944c1a214dc55e3..5fe82c29c8ad07bcb7bab43726a4e09b136d8b53 100644 (file)
@@ -141,7 +141,8 @@ enum iwl_bt_coex_lut_type {
        BT_COEX_TX_DIS_LUT,
 
        BT_COEX_MAX_LUT,
-};
+       BT_COEX_INVALID_LUT = 0xff,
+}; /* BT_COEX_DECISION_LUT_INDEX_API_E_VER_1 */
 
 #define BT_COEX_LUT_SIZE (12)
 #define BT_COEX_CORUN_LUT_SIZE (32)
@@ -154,19 +155,23 @@ enum iwl_bt_coex_lut_type {
  * @flags:&enum iwl_bt_coex_flags
  * @max_kill:
  * @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power
- * @bt4_antenna_isolation:
- * @bt4_antenna_isolation_thr:
- * @bt4_tx_tx_delta_freq_thr:
- * @bt4_tx_rx_max_freq0:
- * @bt_prio_boost:
+ * @override_primary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT
+ *     should be set by default
+ * @override_secondary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT
+ *     should be set by default
+ * @bt4_antenna_isolation: antenna isolation
+ * @bt4_antenna_isolation_thr: antenna threshold value
+ * @bt4_tx_tx_delta_freq_thr: TxTx delta frequency
+ * @bt4_tx_rx_max_freq0: TxRx max frequency
+ * @bt_prio_boost: BT priority boost registers
  * @wifi_tx_prio_boost: SW boost of wifi tx priority
  * @wifi_rx_prio_boost: SW boost of wifi rx priority
- * @kill_ack_msk:
- * @kill_cts_msk:
- * @decision_lut:
- * @bt4_multiprio_lut:
- * @bt4_corun_lut20:
- * @bt4_corun_lut40:
+ * @kill_ack_msk: kill ACK mask. 1 - Tx ACK, 0 - kill Tx of ACK.
+ * @kill_cts_msk: kill CTS mask. 1 - Tx CTS, 0 - kill Tx of CTS.
+ * @decision_lut: PTA decision LUT, per Prio-Ch
+ * @bt4_multiprio_lut: multi priority LUT configuration
+ * @bt4_corun_lut20: co-running 20 MHz LUT configuration
+ * @bt4_corun_lut40: co-running 40 MHz LUT configuration
  * @valid_bit_msk: enum %iwl_bt_coex_valid_bit_msk
  *
  * The structure is used for the BT_COEX command.
@@ -175,7 +180,8 @@ struct iwl_bt_coex_cmd {
        __le32 flags;
        u8 max_kill;
        u8 bt_reduced_tx_power;
-       u8 reserved[2];
+       u8 override_primary_lut;
+       u8 override_secondary_lut;
 
        u8 bt4_antenna_isolation;
        u8 bt4_antenna_isolation_thr;
@@ -194,7 +200,7 @@ struct iwl_bt_coex_cmd {
        __le32 bt4_corun_lut40[BT_COEX_CORUN_LUT_SIZE];
 
        __le32 valid_bit_msk;
-} __packed; /* BT_COEX_CMD_API_S_VER_3 */
+} __packed; /* BT_COEX_CMD_API_S_VER_5 */
 
 /**
  * struct iwl_bt_coex_ci_cmd - bt coex channel inhibition command
@@ -282,7 +288,7 @@ enum iwl_bt_activity_grading {
        BT_ON_NO_CONNECTION     = 1,
        BT_LOW_TRAFFIC          = 2,
        BT_HIGH_TRAFFIC         = 3,
-};
+}; /* BT_COEX_BT_ACTIVITY_GRADING_API_E_VER_1 */
 
 /**
  * struct iwl_bt_coex_profile_notif - notification about BT coex
@@ -310,7 +316,7 @@ struct iwl_bt_coex_profile_notif {
        __le32 primary_ch_lut;
        __le32 secondary_ch_lut;
        __le32 bt_activity_grading;
-} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_2 */
+} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_3 */
 
 enum iwl_bt_coex_prio_table_event {
        BT_COEX_PRIO_TBL_EVT_INIT_CALIB1                = 0,
index 10fcc1a79ebddf3087d7de7c2c29389849a425fa..13696fe419b778c68c9d72d7a289a3dd3c453b39 100644 (file)
@@ -345,21 +345,6 @@ enum iwl_wowlan_wakeup_reason {
        IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET             = BIT(12),
 }; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */
 
-struct iwl_wowlan_status_v4 {
-       __le64 replay_ctr;
-       __le16 pattern_number;
-       __le16 non_qos_seq_ctr;
-       __le16 qos_seq_ctr[8];
-       __le32 wakeup_reasons;
-       __le32 rekey_status;
-       __le32 num_of_gtk_rekeys;
-       __le32 transmitted_ndps;
-       __le32 received_beacons;
-       __le32 wake_packet_length;
-       __le32 wake_packet_bufsize;
-       u8 wake_packet[]; /* can be truncated from _length to _bufsize */
-} __packed; /* WOWLAN_STATUSES_API_S_VER_4 */
-
 struct iwl_wowlan_gtk_status {
        u8 key_index;
        u8 reserved[3];
@@ -368,7 +353,7 @@ struct iwl_wowlan_gtk_status {
        struct iwl_wowlan_rsc_tsc_params_cmd rsc;
 } __packed;
 
-struct iwl_wowlan_status_v6 {
+struct iwl_wowlan_status {
        struct iwl_wowlan_gtk_status gtk;
        __le64 replay_ctr;
        __le16 pattern_number;
index 39148b5bb33262596e1dea348c1ae32c9a2c9166..8bb5b94bf9639689fa6445cd046f97eccbcec834 100644 (file)
@@ -334,7 +334,7 @@ enum {
  */
 struct iwl_lq_cmd {
        u8 sta_id;
-       u8 reserved1;
+       u8 reduced_tpc;
        u16 control;
        /* LINK_QUAL_GENERAL_PARAMS_API_S_VER_1 */
        u8 flags;
index d73a89ecd78aa0963c40a268fc7dd8e3d7fbe29d..6959fda3fe09d09e34d5fe19c7ded403fba79c37 100644 (file)
@@ -169,8 +169,12 @@ enum iwl_scan_type {
        SCAN_TYPE_DISCOVERY_FORCED      = 6,
 }; /* SCAN_ACTIVITY_TYPE_E_VER_1 */
 
-/* Maximal number of channels to scan */
-#define MAX_NUM_SCAN_CHANNELS 0x24
+/**
+ * Maximal number of channels to scan
+ * it should be equal to:
+ * max(IWL_NUM_CHANNELS, IWL_NUM_CHANNELS_FAMILY_8000)
+ */
+#define MAX_NUM_SCAN_CHANNELS 50
 
 /**
  * struct iwl_scan_cmd - scan request command
@@ -534,13 +538,16 @@ struct iwl_scan_offload_schedule {
  *
  * IWL_SCAN_OFFLOAD_FLAG_PASS_ALL: pass all results - no filtering.
  * IWL_SCAN_OFFLOAD_FLAG_CACHED_CHANNEL: add cached channels to partial scan.
- * IWL_SCAN_OFFLOAD_FLAG_ENERGY_SCAN: use energy based scan before partial scan
- *     on A band.
+ * IWL_SCAN_OFFLOAD_FLAG_EBS_QUICK_MODE: EBS duration is 100mSec - typical
+ *     beacon period. Finding channel activity in this mode is not guaranteed.
+ * IWL_SCAN_OFFLOAD_FLAG_EBS_ACCURATE_MODE: EBS duration is 200mSec.
+ *     Assuming beacon period is 100ms finding channel activity is guaranteed.
  */
 enum iwl_scan_offload_flags {
        IWL_SCAN_OFFLOAD_FLAG_PASS_ALL          = BIT(0),
        IWL_SCAN_OFFLOAD_FLAG_CACHED_CHANNEL    = BIT(2),
-       IWL_SCAN_OFFLOAD_FLAG_ENERGY_SCAN       = BIT(3),
+       IWL_SCAN_OFFLOAD_FLAG_EBS_QUICK_MODE    = BIT(5),
+       IWL_SCAN_OFFLOAD_FLAG_EBS_ACCURATE_MODE = BIT(6),
 };
 
 /**
@@ -563,17 +570,24 @@ enum iwl_scan_offload_compleate_status {
        IWL_SCAN_OFFLOAD_ABORTED        = 2,
 };
 
+enum iwl_scan_ebs_status {
+       IWL_SCAN_EBS_SUCCESS,
+       IWL_SCAN_EBS_FAILED,
+       IWL_SCAN_EBS_CHAN_NOT_FOUND,
+};
+
 /**
  * iwl_scan_offload_complete - SCAN_OFFLOAD_COMPLETE_NTF_API_S_VER_1
  * @last_schedule_line:                last schedule line executed (fast or regular)
  * @last_schedule_iteration:   last scan iteration executed before scan abort
  * @status:                    enum iwl_scan_offload_compleate_status
+ * @ebs_status: last EBS status, see IWL_SCAN_EBS_*
  */
 struct iwl_scan_offload_complete {
        u8 last_schedule_line;
        u8 last_schedule_iteration;
        u8 status;
-       u8 reserved;
+       u8 ebs_status;
 } __packed;
 
 /**
index d636478672626e9436c12dc2313d3288d94303ed..39cebee8016feaab62f005e5e843447784594429 100644 (file)
@@ -255,22 +255,19 @@ struct iwl_mvm_keyinfo {
 } __packed;
 
 /**
- * struct iwl_mvm_add_sta_cmd_v5 - Add/modify a station in the fw's sta table.
+ * struct iwl_mvm_add_sta_cmd - Add/modify a station in the fw's sta table.
  * ( REPLY_ADD_STA = 0x18 )
  * @add_modify: 1: modify existing, 0: add new station
- * @unicast_tx_key_id: unicast tx key id. Relevant only when unicast key sent
- * @multicast_tx_key_id: multicast tx key id. Relevant only when multicast key
- *     sent
+ * @awake_acs:
+ * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable
+ *     AMPDU for tid x. Set %STA_MODIFY_TID_DISABLE_TX to change this field.
  * @mac_id_n_color: the Mac context this station belongs to
  * @addr[ETH_ALEN]: station's MAC address
  * @sta_id: index of station in uCode's station table
  * @modify_mask: STA_MODIFY_*, selects which parameters to modify vs. leave
  *     alone. 1 - modify, 0 - don't change.
- * @key: look at %iwl_mvm_keyinfo
  * @station_flags: look at %iwl_sta_flags
  * @station_flags_msk: what of %station_flags have changed
- * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable
- *     AMPDU for tid x. Set %STA_MODIFY_TID_DISABLE_TX to change this field.
  * @add_immediate_ba_tid: tid for which to add block-ack support (Rx)
  *     Set %STA_MODIFY_ADD_BA_TID to use this field, and also set
  *     add_immediate_ba_ssn.
@@ -294,40 +291,7 @@ struct iwl_mvm_keyinfo {
  * ADD_STA sets up the table entry for one station, either creating a new
  * entry, or modifying a pre-existing one.
  */
-struct iwl_mvm_add_sta_cmd_v5 {
-       u8 add_modify;
-       u8 unicast_tx_key_id;
-       u8 multicast_tx_key_id;
-       u8 reserved1;
-       __le32 mac_id_n_color;
-       u8 addr[ETH_ALEN];
-       __le16 reserved2;
-       u8 sta_id;
-       u8 modify_mask;
-       __le16 reserved3;
-       struct iwl_mvm_keyinfo key;
-       __le32 station_flags;
-       __le32 station_flags_msk;
-       __le16 tid_disable_tx;
-       __le16 reserved4;
-       u8 add_immediate_ba_tid;
-       u8 remove_immediate_ba_tid;
-       __le16 add_immediate_ba_ssn;
-       __le16 sleep_tx_count;
-       __le16 sleep_state_flags;
-       __le16 assoc_id;
-       __le16 beamform_flags;
-       __le32 tfd_queue_msk;
-} __packed; /* ADD_STA_CMD_API_S_VER_5 */
-
-/**
- * struct iwl_mvm_add_sta_cmd_v7 - Add / modify a station
- * VER_7 of this command is quite similar to VER_5 except
- * exclusion of all fields related to the security key installation.
- * It only differs from VER_6 by the "awake_acs" field that is
- * reserved and ignored in VER_6.
- */
-struct iwl_mvm_add_sta_cmd_v7 {
+struct iwl_mvm_add_sta_cmd {
        u8 add_modify;
        u8 awake_acs;
        __le16 tid_disable_tx;
index 8e122f3a7a74e8a97914a13d9821a7229bc7c2bc..6cc5f52b807f1bc343ea632674e215423c3abb1d 100644 (file)
@@ -482,7 +482,8 @@ struct iwl_mvm_tx_resp {
        u8 pa_integ_res_b[3];
        u8 pa_integ_res_c[3];
        __le16 measurement_req_id;
-       __le16 reserved;
+       u8 reduced_tpc;
+       u8 reserved;
 
        __le32 tfd_info;
        __le16 seq_ctl;
index 6e75b52588de3ca68a44c41ca339df1e57eae37f..309a9b9a94fecc26918f967e7b9e7a01374d43b3 100644 (file)
@@ -71,6 +71,7 @@
 #include "fw-api-power.h"
 #include "fw-api-d3.h"
 #include "fw-api-coex.h"
+#include "fw-api-scan.h"
 
 /* maximal number of Tx queues in any platform */
 #define IWL_MVM_MAX_QUEUES     20
@@ -604,52 +605,7 @@ enum {
        TE_V1_NOTIF_INTERNAL_FRAG_END = BIT(7),
 }; /* MAC_EVENT_ACTION_API_E_VER_2 */
 
-
-/**
- * struct iwl_time_event_cmd_api_v1 - configuring Time Events
- * with struct MAC_TIME_EVENT_DATA_API_S_VER_1 (see also
- * with version 2. determined by IWL_UCODE_TLV_FLAGS)
- * ( TIME_EVENT_CMD = 0x29 )
- * @id_and_color: ID and color of the relevant MAC
- * @action: action to perform, one of FW_CTXT_ACTION_*
- * @id: this field has two meanings, depending on the action:
- *     If the action is ADD, then it means the type of event to add.
- *     For all other actions it is the unique event ID assigned when the
- *     event was added by the FW.
- * @apply_time: When to start the Time Event (in GP2)
- * @max_delay: maximum delay to event's start (apply time), in TU
- * @depends_on: the unique ID of the event we depend on (if any)
- * @interval: interval between repetitions, in TU
- * @interval_reciprocal: 2^32 / interval
- * @duration: duration of event in TU
- * @repeat: how many repetitions to do, can be TE_REPEAT_ENDLESS
- * @dep_policy: one of TE_V1_INDEPENDENT, TE_V1_DEP_OTHER, TE_V1_DEP_TSF
- *     and TE_V1_EVENT_SOCIOPATHIC
- * @is_present: 0 or 1, are we present or absent during the Time Event
- * @max_frags: maximal number of fragments the Time Event can be divided to
- * @notify: notifications using TE_V1_NOTIF_* (whom to notify when)
- */
-struct iwl_time_event_cmd_v1 {
-       /* COMMON_INDEX_HDR_API_S_VER_1 */
-       __le32 id_and_color;
-       __le32 action;
-       __le32 id;
-       /* MAC_TIME_EVENT_DATA_API_S_VER_1 */
-       __le32 apply_time;
-       __le32 max_delay;
-       __le32 dep_policy;
-       __le32 depends_on;
-       __le32 is_present;
-       __le32 max_frags;
-       __le32 interval;
-       __le32 interval_reciprocal;
-       __le32 duration;
-       __le32 repeat;
-       __le32 notify;
-} __packed; /* MAC_TIME_EVENT_CMD_API_S_VER_1 */
-
-
-/* Time event - defines for command API v2 */
+/* Time event - defines for command API */
 
 /*
  * @TE_V2_FRAG_NONE: fragmentation of the time event is NOT allowed.
@@ -680,7 +636,7 @@ enum {
 #define TE_V2_PLACEMENT_POS    12
 #define TE_V2_ABSENCE_POS      15
 
-/* Time event policy values (for time event cmd api v2)
+/* Time event policy values
  * A notification (both event and fragment) includes a status indicating weather
  * the FW was able to schedule the event or not. For fragment start/end
  * notification the status is always success. There is no start/end fragment
@@ -727,7 +683,7 @@ enum {
 };
 
 /**
- * struct iwl_time_event_cmd_api_v2 - configuring Time Events
+ * struct iwl_time_event_cmd_api - configuring Time Events
  * with struct MAC_TIME_EVENT_DATA_API_S_VER_2 (see also
  * with version 1. determined by IWL_UCODE_TLV_FLAGS)
  * ( TIME_EVENT_CMD = 0x29 )
@@ -750,7 +706,7 @@ enum {
  *     TE_EVENT_SOCIOPATHIC
  *     using TE_ABSENCE and using TE_NOTIF_*
  */
-struct iwl_time_event_cmd_v2 {
+struct iwl_time_event_cmd {
        /* COMMON_INDEX_HDR_API_S_VER_1 */
        __le32 id_and_color;
        __le32 action;
index 7ce20062f32d443be34fe87865d91afd71a0a014..883e702152d5289163f48f7464cf630f6795ec2f 100644 (file)
@@ -99,7 +99,7 @@ static int iwl_send_tx_ant_cfg(struct iwl_mvm *mvm, u8 valid_tx_ant)
        };
 
        IWL_DEBUG_FW(mvm, "select valid tx ant: %u\n", valid_tx_ant);
-       return iwl_mvm_send_cmd_pdu(mvm, TX_ANT_CONFIGURATION_CMD, CMD_SYNC,
+       return iwl_mvm_send_cmd_pdu(mvm, TX_ANT_CONFIGURATION_CMD, 0,
                                    sizeof(tx_ant_cmd), &tx_ant_cmd);
 }
 
@@ -137,6 +137,8 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
                alive_data->scd_base_addr = le32_to_cpu(palive2->scd_base_ptr);
                mvm->umac_error_event_table =
                        le32_to_cpu(palive2->error_info_addr);
+               mvm->sf_space.addr = le32_to_cpu(palive2->st_fwrd_addr);
+               mvm->sf_space.size = le32_to_cpu(palive2->st_fwrd_size);
 
                alive_data->valid = le16_to_cpu(palive2->status) ==
                                    IWL_ALIVE_STATUS_OK;
@@ -180,6 +182,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
        int ret, i;
        enum iwl_ucode_type old_type = mvm->cur_ucode;
        static const u8 alive_cmd[] = { MVM_ALIVE };
+       struct iwl_sf_region st_fwrd_space;
 
        fw = iwl_get_ucode_image(mvm, ucode_type);
        if (WARN_ON(!fw))
@@ -215,6 +218,14 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
                return -EIO;
        }
 
+       /*
+        * update the sdio allocation according to the pointer we get in the
+        * alive notification.
+        */
+       st_fwrd_space.addr = mvm->sf_space.addr;
+       st_fwrd_space.size = mvm->sf_space.size;
+       ret = iwl_trans_update_sf(mvm->trans, &st_fwrd_space);
+
        iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr);
 
        /*
@@ -256,7 +267,7 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm)
        IWL_DEBUG_INFO(mvm, "Sending Phy CFG command: 0x%x\n",
                       phy_cfg_cmd.phy_cfg);
 
-       return iwl_mvm_send_cmd_pdu(mvm, PHY_CONFIGURATION_CMD, CMD_SYNC,
+       return iwl_mvm_send_cmd_pdu(mvm, PHY_CONFIGURATION_CMD, 0,
                                    sizeof(phy_cfg_cmd), &phy_cfg_cmd);
 }
 
@@ -288,14 +299,14 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
                goto error;
        }
 
-       ret = iwl_send_bt_prio_tbl(mvm);
+       ret = iwl_send_bt_init_conf(mvm);
        if (ret)
                goto error;
 
        /* Read the NVM only at driver load time, no need to do this twice */
        if (read_nvm) {
                /* Read nvm */
-               ret = iwl_nvm_init(mvm);
+               ret = iwl_nvm_init(mvm, true);
                if (ret) {
                        IWL_ERR(mvm, "Failed to read NVM: %d\n", ret);
                        goto error;
@@ -303,7 +314,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
        }
 
        /* In case we read the NVM from external file, load it to the NIC */
-       if (iwlwifi_mod_params.nvm_file)
+       if (mvm->nvm_file_name)
                iwl_mvm_load_nvm_to_nic(mvm);
 
        ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans);
@@ -424,10 +435,6 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
        if (ret)
                goto error;
 
-       ret = iwl_send_bt_prio_tbl(mvm);
-       if (ret)
-               goto error;
-
        ret = iwl_send_bt_init_conf(mvm);
        if (ret)
                goto error;
@@ -468,12 +475,6 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
        /* Initialize tx backoffs to the minimal possible */
        iwl_mvm_tt_tx_backoff(mvm, 0);
 
-       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) {
-               ret = iwl_power_legacy_set_cam_mode(mvm);
-               if (ret)
-                       goto error;
-       }
-
        ret = iwl_mvm_power_update_device(mvm);
        if (ret)
                goto error;
index 9ccec10bba166299cc91cf1706992937127d277a..8b530277763258551cf09292298f8f14be074174 100644 (file)
@@ -667,12 +667,9 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
        if (vif->bss_conf.qos)
                cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
 
-       /* Don't use cts to self as the fw doesn't support it currently. */
        if (vif->bss_conf.use_cts_prot) {
                cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT);
-               if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 8)
-                       cmd->protection_flags |=
-                               cpu_to_le32(MAC_PROT_FLG_SELF_CTS_EN);
+               cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_SELF_CTS_EN);
        }
        IWL_DEBUG_RATE(mvm, "use_cts_prot %d, ht_operation_mode %d\n",
                       vif->bss_conf.use_cts_prot,
@@ -688,7 +685,7 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
 static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm,
                                     struct iwl_mac_ctx_cmd *cmd)
 {
-       int ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, CMD_SYNC,
+       int ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0,
                                       sizeof(*cmd), cmd);
        if (ret)
                IWL_ERR(mvm, "Failed to send MAC context (action:%d): %d\n",
@@ -696,19 +693,39 @@ static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm,
        return ret;
 }
 
-/*
- * Fill the specific data for mac context of type station or p2p client
- */
-static void iwl_mvm_mac_ctxt_cmd_fill_sta(struct iwl_mvm *mvm,
-                                         struct ieee80211_vif *vif,
-                                         struct iwl_mac_data_sta *ctxt_sta,
-                                         bool force_assoc_off)
+static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif *vif,
+                                   u32 action, bool force_assoc_off)
 {
+       struct iwl_mac_ctx_cmd cmd = {};
+       struct iwl_mac_data_sta *ctxt_sta;
+
+       WARN_ON(vif->type != NL80211_IFTYPE_STATION);
+
+       /* Fill the common data for all mac context types */
+       iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+
+       if (vif->p2p) {
+               struct ieee80211_p2p_noa_attr *noa =
+                       &vif->bss_conf.p2p_noa_attr;
+
+               cmd.p2p_sta.ctwin = cpu_to_le32(noa->oppps_ctwindow &
+                                       IEEE80211_P2P_OPPPS_CTWINDOW_MASK);
+               ctxt_sta = &cmd.p2p_sta.sta;
+       } else {
+               ctxt_sta = &cmd.sta;
+       }
+
        /* We need the dtim_period to set the MAC as associated */
        if (vif->bss_conf.assoc && vif->bss_conf.dtim_period &&
            !force_assoc_off) {
                u32 dtim_offs;
 
+               /* Allow beacons to pass through as long as we are not
+                * associated, or we do not have dtim period information.
+                */
+               cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON);
+
                /*
                 * The DTIM count counts down, so when it is N that means N
                 * more beacon intervals happen until the DTIM TBTT. Therefore
@@ -755,51 +772,6 @@ static void iwl_mvm_mac_ctxt_cmd_fill_sta(struct iwl_mvm *mvm,
 
        ctxt_sta->listen_interval = cpu_to_le32(mvm->hw->conf.listen_interval);
        ctxt_sta->assoc_id = cpu_to_le32(vif->bss_conf.aid);
-}
-
-static int iwl_mvm_mac_ctxt_cmd_station(struct iwl_mvm *mvm,
-                                       struct ieee80211_vif *vif,
-                                       u32 action)
-{
-       struct iwl_mac_ctx_cmd cmd = {};
-
-       WARN_ON(vif->type != NL80211_IFTYPE_STATION || vif->p2p);
-
-       /* Fill the common data for all mac context types */
-       iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
-
-       /* Allow beacons to pass through as long as we are not associated,or we
-        * do not have dtim period information */
-       if (!vif->bss_conf.assoc || !vif->bss_conf.dtim_period)
-               cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON);
-       else
-               cmd.filter_flags &= ~cpu_to_le32(MAC_FILTER_IN_BEACON);
-
-       /* Fill the data specific for station mode */
-       iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.sta,
-                                     action == FW_CTXT_ACTION_ADD);
-
-       return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
-}
-
-static int iwl_mvm_mac_ctxt_cmd_p2p_client(struct iwl_mvm *mvm,
-                                          struct ieee80211_vif *vif,
-                                          u32 action)
-{
-       struct iwl_mac_ctx_cmd cmd = {};
-       struct ieee80211_p2p_noa_attr *noa = &vif->bss_conf.p2p_noa_attr;
-
-       WARN_ON(vif->type != NL80211_IFTYPE_STATION || !vif->p2p);
-
-       /* Fill the common data for all mac context types */
-       iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
-
-       /* Fill the data specific for station mode */
-       iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.p2p_sta.sta,
-                                     action == FW_CTXT_ACTION_ADD);
-
-       cmd.p2p_sta.ctwin = cpu_to_le32(noa->oppps_ctwindow &
-                                       IEEE80211_P2P_OPPPS_CTWINDOW_MASK);
 
        return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
 }
@@ -1137,16 +1109,12 @@ static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm,
 }
 
 static int iwl_mvm_mac_ctx_send(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
-                               u32 action)
+                               u32 action, bool force_assoc_off)
 {
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
-               if (!vif->p2p)
-                       return iwl_mvm_mac_ctxt_cmd_station(mvm, vif,
-                                                           action);
-               else
-                       return iwl_mvm_mac_ctxt_cmd_p2p_client(mvm, vif,
-                                                              action);
+               return iwl_mvm_mac_ctxt_cmd_sta(mvm, vif, action,
+                                               force_assoc_off);
                break;
        case NL80211_IFTYPE_AP:
                if (!vif->p2p)
@@ -1176,7 +1144,8 @@ int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
                      vif->addr, ieee80211_vif_type_p2p(vif)))
                return -EIO;
 
-       ret = iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD);
+       ret = iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD,
+                                  true);
        if (ret)
                return ret;
 
@@ -1187,7 +1156,8 @@ int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
        return 0;
 }
 
-int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                            bool force_assoc_off)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 
@@ -1195,7 +1165,8 @@ int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
                      vif->addr, ieee80211_vif_type_p2p(vif)))
                return -EIO;
 
-       return iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY);
+       return iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY,
+                                   force_assoc_off);
 }
 
 int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
@@ -1214,7 +1185,7 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
                                                           mvmvif->color));
        cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, CMD_SYNC,
+       ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0,
                                   sizeof(cmd), &cmd);
        if (ret) {
                IWL_ERR(mvm, "Failed to remove MAC context: %d\n", ret);
@@ -1240,11 +1211,23 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
        u32 rate __maybe_unused =
                le32_to_cpu(beacon->beacon_notify_hdr.initial_rate);
 
+       lockdep_assert_held(&mvm->mutex);
+
        IWL_DEBUG_RX(mvm, "beacon status %#x retries:%d tsf:0x%16llX rate:%d\n",
                     status & TX_STATUS_MSK,
                     beacon->beacon_notify_hdr.failure_frame,
                     le64_to_cpu(beacon->tsf),
                     rate);
+
+       if (unlikely(mvm->csa_vif && mvm->csa_vif->csa_active)) {
+               if (!ieee80211_csa_is_complete(mvm->csa_vif)) {
+                       iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm->csa_vif);
+               } else {
+                       ieee80211_csa_finish(mvm->csa_vif);
+                       mvm->csa_vif = NULL;
+               }
+       }
+
        return 0;
 }
 
index 8735ef1f44ae8fadd5cae19b0f5b404ffe6ef901..7215f59801863d3b7d72398de8c96c7b73c3902b 100644 (file)
@@ -295,7 +295,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
            !iwlwifi_mod_params.sw_crypto)
                hw->flags |= IEEE80211_HW_MFP_CAPABLE;
 
-       if (0 && mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT) {
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT &&
+           IWL_UCODE_API(mvm->fw->ucode_ver) >= 9 &&
+           !iwlwifi_mod_params.uapsd_disable) {
                hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD;
                hw->uapsd_queues = IWL_UAPSD_AC_INFO;
                hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
@@ -309,11 +311,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                BIT(NL80211_IFTYPE_P2P_CLIENT) |
                BIT(NL80211_IFTYPE_AP) |
                BIT(NL80211_IFTYPE_P2P_GO) |
-               BIT(NL80211_IFTYPE_P2P_DEVICE);
-
-       /* IBSS has bugs in older versions */
-       if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 8)
-               hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
+               BIT(NL80211_IFTYPE_P2P_DEVICE) |
+               BIT(NL80211_IFTYPE_ADHOC);
 
        hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
        hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
@@ -322,6 +321,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD)
                hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
 
+       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_CSA_FLOW)
+               hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+
        hw->wiphy->iface_combinations = iwl_mvm_iface_combinations;
        hw->wiphy->n_iface_combinations =
                ARRAY_SIZE(iwl_mvm_iface_combinations);
@@ -365,14 +367,11 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        else
                hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
 
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SCHED_SCAN) {
-               hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
-               hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX;
-               hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES;
-               /* we create the 802.11 header and zero length SSID IE. */
-               hw->wiphy->max_sched_scan_ie_len =
-                                       SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2;
-       }
+       hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
+       hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX;
+       hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES;
+       /* we create the 802.11 header and zero length SSID IE. */
+       hw->wiphy->max_sched_scan_ie_len = SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2;
 
        hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN |
                               NL80211_FEATURE_P2P_GO_OPPPS;
@@ -386,7 +385,11 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        }
 
 #ifdef CONFIG_PM_SLEEP
-       if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
+       if (iwl_mvm_is_d0i3_supported(mvm) &&
+           device_can_wakeup(mvm->trans->dev)) {
+               mvm->wowlan.flags = WIPHY_WOWLAN_ANY;
+               hw->wiphy->wowlan = &mvm->wowlan;
+       } else if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
            mvm->trans->ops->d3_suspend &&
            mvm->trans->ops->d3_resume &&
            device_can_wakeup(mvm->trans->dev)) {
@@ -540,13 +543,22 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
                return -EACCES;
 
        /* return from D0i3 before starting a new Tx aggregation */
-       if (action == IEEE80211_AMPDU_TX_START) {
+       switch (action) {
+       case IEEE80211_AMPDU_TX_START:
+       case IEEE80211_AMPDU_TX_STOP_CONT:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+       case IEEE80211_AMPDU_TX_OPERATIONAL:
                iwl_mvm_ref(mvm, IWL_MVM_REF_TX_AGG);
                tx_agg_ref = true;
 
                /*
-                * wait synchronously until D0i3 exit to get the correct
-                * sequence number for the tid
+                * for tx start, wait synchronously until D0i3 exit to
+                * get the correct sequence number for the tid.
+                * additionally, some other ampdu actions use direct
+                * target access, which is not handled automatically
+                * by the trans layer (unlike commands), so wait for
+                * d0i3 exit in these cases as well.
                 */
                if (!wait_event_timeout(mvm->d0i3_exit_waitq,
                          !test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status), HZ)) {
@@ -554,6 +566,9 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
                        iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG);
                        return -EIO;
                }
+               break;
+       default:
+               break;
        }
 
        mutex_lock(&mvm->mutex);
@@ -758,7 +773,7 @@ static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                .pwr_restriction = cpu_to_le16(tx_power),
        };
 
-       return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, CMD_SYNC,
+       return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0,
                                    sizeof(reduce_txpwr_cmd),
                                    &reduce_txpwr_cmd);
 }
@@ -817,18 +832,17 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
        if (ret)
                goto out_release;
 
-       ret = iwl_mvm_power_update_mac(mvm, vif);
+       ret = iwl_mvm_power_update_mac(mvm);
        if (ret)
                goto out_release;
 
        /* beacon filtering */
-       ret = iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC);
+       ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
        if (ret)
                goto out_remove_mac;
 
-       if (!mvm->bf_allowed_vif && false &&
-           vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
-           mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BF_UPDATED){
+       if (!mvm->bf_allowed_vif &&
+           vif->type == NL80211_IFTYPE_STATION && !vif->p2p) {
                mvm->bf_allowed_vif = mvmvif;
                vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
                                     IEEE80211_VIF_SUPPORTS_CQM_RSSI;
@@ -969,7 +983,7 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
        if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE)
                mvm->vif_count--;
 
-       iwl_mvm_power_update_mac(mvm, vif);
+       iwl_mvm_power_update_mac(mvm);
        iwl_mvm_mac_ctxt_remove(mvm, vif);
 
 out_release:
@@ -1223,10 +1237,14 @@ static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm,
        if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING))
                return 0;
 
+       /* bcast filtering isn't supported for P2P client */
+       if (vif->p2p)
+               return 0;
+
        if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
                return 0;
 
-       return iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC,
+       return iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0,
                                    sizeof(cmd), &cmd);
 }
 #else
@@ -1253,7 +1271,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
        if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc)
                iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif);
 
-       ret = iwl_mvm_mac_ctxt_changed(mvm, vif);
+       ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false);
        if (ret)
                IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
 
@@ -1333,10 +1351,10 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                iwl_mvm_remove_time_event(mvm, mvmvif,
                                          &mvmvif->time_event_data);
                iwl_mvm_sf_update(mvm, vif, false);
-               WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, CMD_SYNC));
+               WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
        } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS |
                              BSS_CHANGED_QOS)) {
-               ret = iwl_mvm_power_update_mac(mvm, vif);
+               ret = iwl_mvm_power_update_mac(mvm);
                if (ret)
                        IWL_ERR(mvm, "failed to update power mode\n");
        }
@@ -1347,16 +1365,19 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
        }
 
        if (changes & BSS_CHANGED_CQM) {
-               IWL_DEBUG_MAC80211(mvm, "cqm info_changed");
+               IWL_DEBUG_MAC80211(mvm, "cqm info_changed\n");
                /* reset cqm events tracking */
                mvmvif->bf_data.last_cqm_event = 0;
-               ret = iwl_mvm_update_beacon_filter(mvm, vif, false, CMD_SYNC);
-               if (ret)
-                       IWL_ERR(mvm, "failed to update CQM thresholds\n");
+               if (mvmvif->bf_data.bf_enabled) {
+                       ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
+                       if (ret)
+                               IWL_ERR(mvm,
+                                       "failed to update CQM thresholds\n");
+               }
        }
 
        if (changes & BSS_CHANGED_ARP_FILTER) {
-               IWL_DEBUG_MAC80211(mvm, "arp filter changed");
+               IWL_DEBUG_MAC80211(mvm, "arp filter changed\n");
                iwl_mvm_configure_bcast_filter(mvm, vif);
        }
 }
@@ -1402,7 +1423,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
        mvmvif->ap_ibss_active = true;
 
        /* power updated needs to be done before quotas */
-       iwl_mvm_power_update_mac(mvm, vif);
+       iwl_mvm_power_update_mac(mvm);
 
        ret = iwl_mvm_update_quotas(mvm, vif);
        if (ret)
@@ -1410,7 +1431,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
 
        /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
        if (vif->p2p && mvm->p2p_device_vif)
-               iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif);
+               iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false);
 
        iwl_mvm_ref(mvm, IWL_MVM_REF_AP_IBSS);
 
@@ -1420,7 +1441,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
        return 0;
 
 out_quota_failed:
-       iwl_mvm_power_update_mac(mvm, vif);
+       iwl_mvm_power_update_mac(mvm);
        mvmvif->ap_ibss_active = false;
        iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
 out_unbind:
@@ -1450,13 +1471,13 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
 
        /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
        if (vif->p2p && mvm->p2p_device_vif)
-               iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif);
+               iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false);
 
        iwl_mvm_update_quotas(mvm, NULL);
        iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
        iwl_mvm_binding_remove_vif(mvm, vif);
 
-       iwl_mvm_power_update_mac(mvm, vif);
+       iwl_mvm_power_update_mac(mvm);
 
        iwl_mvm_mac_ctxt_remove(mvm, vif);
 
@@ -1477,7 +1498,7 @@ iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm,
 
        if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT |
                       BSS_CHANGED_BANDWIDTH) &&
-           iwl_mvm_mac_ctxt_changed(mvm, vif))
+           iwl_mvm_mac_ctxt_changed(mvm, vif, false))
                IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
 
        /* Need to send a new beacon template to the FW */
@@ -1495,6 +1516,9 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
 
        mutex_lock(&mvm->mutex);
 
+       if (changes & BSS_CHANGED_IDLE && !bss_conf->idle)
+               iwl_mvm_sched_scan_stop(mvm, true);
+
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
                iwl_mvm_bss_info_changed_station(mvm, vif, bss_conf, changes);
@@ -1525,7 +1549,7 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
 
        switch (mvm->scan_status) {
        case IWL_MVM_SCAN_SCHED:
-               ret = iwl_mvm_sched_scan_stop(mvm);
+               ret = iwl_mvm_sched_scan_stop(mvm, true);
                if (ret) {
                        ret = -EBUSY;
                        goto out;
@@ -1697,6 +1721,11 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
                ret = iwl_mvm_add_sta(mvm, vif, sta);
        } else if (old_state == IEEE80211_STA_NONE &&
                   new_state == IEEE80211_STA_AUTH) {
+               /*
+                * EBS may be disabled due to previous failures reported by FW.
+                * Reset EBS status here assuming environment has been changed.
+                */
+               mvm->last_ebs_successful = true;
                ret = 0;
        } else if (old_state == IEEE80211_STA_AUTH &&
                   new_state == IEEE80211_STA_ASSOC) {
@@ -1708,14 +1737,12 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
        } else if (old_state == IEEE80211_STA_ASSOC &&
                   new_state == IEEE80211_STA_AUTHORIZED) {
                /* enable beacon filtering */
-               if (vif->bss_conf.dtim_period)
-                       WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif,
-                                                            CMD_SYNC));
+               WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
                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, CMD_SYNC));
+               WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif, 0));
                ret = 0;
        } else if (old_state == IEEE80211_STA_ASSOC &&
                   new_state == IEEE80211_STA_AUTH) {
@@ -1772,7 +1799,7 @@ static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw,
                int ret;
 
                mutex_lock(&mvm->mutex);
-               ret = iwl_mvm_mac_ctxt_changed(mvm, vif);
+               ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false);
                mutex_unlock(&mvm->mutex);
                return ret;
        }
@@ -1865,7 +1892,7 @@ static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
        int ret;
 
        mutex_lock(&mvm->mutex);
-       ret = iwl_mvm_sched_scan_stop(mvm);
+       ret = iwl_mvm_sched_scan_stop(mvm, false);
        mutex_unlock(&mvm->mutex);
        iwl_mvm_wait_for_async_handlers(mvm);
 
@@ -2161,10 +2188,10 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
                return;
 
        mutex_lock(&mvm->mutex);
+       iwl_mvm_bt_coex_vif_change(mvm);
        iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def,
                                 ctx->rx_chains_static,
                                 ctx->rx_chains_dynamic);
-       iwl_mvm_bt_coex_vif_change(mvm);
        mutex_unlock(&mvm->mutex);
 }
 
@@ -2184,6 +2211,11 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
 
        switch (vif->type) {
        case NL80211_IFTYPE_AP:
+               /* Unless it's a CSA flow we have nothing to do here */
+               if (vif->csa_active) {
+                       mvmvif->ap_ibss_active = true;
+                       break;
+               }
        case NL80211_IFTYPE_ADHOC:
                /*
                 * The AP binding flow is handled as part of the start_ap flow
@@ -2207,7 +2239,7 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
         * Power state must be updated before quotas,
         * otherwise fw will complain.
         */
-       iwl_mvm_power_update_mac(mvm, vif);
+       iwl_mvm_power_update_mac(mvm);
 
        /* Setting the quota at this stage is only required for monitor
         * interfaces. For the other types, the bss_info changed flow
@@ -2220,11 +2252,17 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
                        goto out_remove_binding;
        }
 
+       /* Handle binding during CSA */
+       if (vif->type == NL80211_IFTYPE_AP) {
+               iwl_mvm_update_quotas(mvm, vif);
+               iwl_mvm_mac_ctxt_changed(mvm, vif, false);
+       }
+
        goto out_unlock;
 
  out_remove_binding:
        iwl_mvm_binding_remove_vif(mvm, vif);
-       iwl_mvm_power_update_mac(mvm, vif);
+       iwl_mvm_power_update_mac(mvm);
  out_unlock:
        mutex_unlock(&mvm->mutex);
        if (ret)
@@ -2244,22 +2282,29 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
        iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data);
 
        switch (vif->type) {
-       case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_ADHOC:
                goto out_unlock;
        case NL80211_IFTYPE_MONITOR:
                mvmvif->monitor_active = false;
                iwl_mvm_update_quotas(mvm, NULL);
                break;
+       case NL80211_IFTYPE_AP:
+               /* This part is triggered only during CSA */
+               if (!vif->csa_active || !mvmvif->ap_ibss_active)
+                       goto out_unlock;
+
+               mvmvif->ap_ibss_active = false;
+               iwl_mvm_update_quotas(mvm, NULL);
+               /*TODO: bt_coex notification here? */
        default:
                break;
        }
 
        iwl_mvm_binding_remove_vif(mvm, vif);
-       iwl_mvm_power_update_mac(mvm, vif);
 
 out_unlock:
        mvmvif->phy_ctxt = NULL;
+       iwl_mvm_power_update_mac(mvm);
        mutex_unlock(&mvm->mutex);
 }
 
@@ -2323,9 +2368,8 @@ static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm,
                        return -EINVAL;
 
                if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]))
-                       return iwl_mvm_enable_beacon_filter(mvm, vif,
-                                                           CMD_SYNC);
-               return iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC);
+                       return iwl_mvm_enable_beacon_filter(mvm, vif, 0);
+               return iwl_mvm_disable_beacon_filter(mvm, vif, 0);
        }
 
        return -EOPNOTSUPP;
@@ -2346,6 +2390,53 @@ static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw,
 }
 #endif
 
+static void iwl_mvm_channel_switch_beacon(struct ieee80211_hw *hw,
+                                         struct ieee80211_vif *vif,
+                                         struct cfg80211_chan_def *chandef)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       mutex_lock(&mvm->mutex);
+       if (WARN(mvm->csa_vif && mvm->csa_vif->csa_active,
+                "Another CSA is already in progress"))
+               goto out_unlock;
+
+       IWL_DEBUG_MAC80211(mvm, "CSA started to freq %d\n",
+                          chandef->center_freq1);
+       mvm->csa_vif = vif;
+
+out_unlock:
+       mutex_unlock(&mvm->mutex);
+}
+
+static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif, u32 queues, bool drop)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif;
+       struct iwl_mvm_sta *mvmsta;
+
+       if (!vif || vif->type != NL80211_IFTYPE_STATION)
+               return;
+
+       mutex_lock(&mvm->mutex);
+       mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       mvmsta = iwl_mvm_sta_from_staid_protected(mvm, mvmvif->ap_sta_id);
+
+       if (WARN_ON_ONCE(!mvmsta))
+               goto done;
+
+       if (drop) {
+               if (iwl_mvm_flush_tx_path(mvm, mvmsta->tfd_queue_msk, true))
+                       IWL_ERR(mvm, "flush request fail\n");
+       } else {
+               iwl_trans_wait_tx_queue_empty(mvm->trans,
+                                             mvmsta->tfd_queue_msk);
+       }
+done:
+       mutex_unlock(&mvm->mutex);
+}
+
 const struct ieee80211_ops iwl_mvm_hw_ops = {
        .tx = iwl_mvm_mac_tx,
        .ampdu_action = iwl_mvm_mac_ampdu_action,
@@ -2369,6 +2460,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
        .sta_rc_update = iwl_mvm_sta_rc_update,
        .conf_tx = iwl_mvm_mac_conf_tx,
        .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
+       .flush = iwl_mvm_mac_flush,
        .sched_scan_start = iwl_mvm_mac_sched_scan_start,
        .sched_scan_stop = iwl_mvm_mac_sched_scan_stop,
        .set_key = iwl_mvm_mac_set_key,
@@ -2388,6 +2480,8 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
 
        .set_tim = iwl_mvm_set_tim,
 
+       .channel_switch_beacon = iwl_mvm_channel_switch_beacon,
+
        CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
 
 #ifdef CONFIG_PM_SLEEP
index f1ec0986c3c912865f0d51e1056ec03c786d8cc2..fcc6c29482d0ef516bba48459b09230b9ead4007 100644 (file)
@@ -164,7 +164,6 @@ enum iwl_dbgfs_pm_mask {
        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),
        MVM_DEBUGFS_PM_SNOOZE_ENABLE = BIT(8),
@@ -177,7 +176,6 @@ struct iwl_dbgfs_pm {
        u32 tx_data_timeout;
        bool skip_over_dtim;
        u8 skip_dtim_periods;
-       bool disable_power_off;
        bool lprx_ena;
        u32 lprx_rssi_threshold;
        bool snooze_ena;
@@ -232,6 +230,7 @@ enum iwl_mvm_ref_type {
        IWL_MVM_REF_USER,
        IWL_MVM_REF_TX,
        IWL_MVM_REF_TX_AGG,
+       IWL_MVM_REF_EXIT_WORK,
 
        IWL_MVM_REF_COUNT,
 };
@@ -265,6 +264,7 @@ struct iwl_mvm_vif_bf_data {
  * @uploaded: indicates the MAC context has been added to the device
  * @ap_ibss_active: indicates that AP/IBSS is configured and that the interface
  *     should get quota etc.
+ * @pm_enabled - Indicate if MAC power management is allowed
  * @monitor_active: indicates that monitor context is configured, and that the
  *     interface should get quota etc.
  * @low_latency: indicates that this interface is in low-latency mode
@@ -283,6 +283,7 @@ struct iwl_mvm_vif {
 
        bool uploaded;
        bool ap_ibss_active;
+       bool pm_enabled;
        bool monitor_active;
        bool low_latency;
        struct iwl_mvm_vif_bf_data bf_data;
@@ -451,6 +452,11 @@ struct iwl_mvm_frame_stats {
        int last_frame_idx;
 };
 
+enum {
+       D0I3_DEFER_WAKEUP,
+       D0I3_PENDING_WAKEUP,
+};
+
 struct iwl_mvm {
        /* for logger access */
        struct device *dev;
@@ -484,6 +490,7 @@ struct iwl_mvm {
        u32 log_event_table;
        u32 umac_error_event_table;
        bool support_umac_log;
+       struct iwl_sf_region sf_space;
 
        u32 ampdu_ref;
 
@@ -495,6 +502,7 @@ struct iwl_mvm {
        u8 queue_to_mac80211[IWL_MAX_HW_QUEUES];
        atomic_t queue_stop_count[IWL_MAX_HW_QUEUES];
 
+       const char *nvm_file_name;
        struct iwl_nvm_data *nvm_data;
        /* NVM sections */
        struct iwl_nvm_section nvm_sections[NVM_MAX_NUM_SECTIONS];
@@ -535,6 +543,8 @@ struct iwl_mvm {
        /* Internal station */
        struct iwl_mvm_int_sta aux_sta;
 
+       bool last_ebs_successful;
+
        u8 scan_last_antenna_idx; /* to toggle TX between antennas */
        u8 mgmt_last_antenna_idx;
 
@@ -578,8 +588,12 @@ struct iwl_mvm {
        void *fw_error_dump;
        void *fw_error_sram;
        u32 fw_error_sram_len;
+       u32 *fw_error_rxf;
+       u32 fw_error_rxf_len;
 
+#ifdef CONFIG_IWLWIFI_LEDS
        struct led_classdev led;
+#endif
 
        struct ieee80211_vif *p2p_device_vif;
 
@@ -601,6 +615,9 @@ struct iwl_mvm {
        bool d0i3_offloading;
        struct work_struct d0i3_exit_work;
        struct sk_buff_head d0i3_tx;
+       /* protect d0i3_suspend_flags */
+       struct mutex d0i3_suspend_mutex;
+       unsigned long d0i3_suspend_flags;
        /* sync d0i3_tx queue and IWL_MVM_STATUS_IN_D0I3 status flag */
        spinlock_t d0i3_tx_lock;
        wait_queue_head_t d0i3_exit_waitq;
@@ -629,8 +646,8 @@ struct iwl_mvm {
 
        /* Indicate if device power save is allowed */
        bool ps_disabled;
-       /* Indicate if device power management is allowed */
-       bool pm_disabled;
+
+       struct ieee80211_vif *csa_vif;
 };
 
 /* Extract MVM priv from op_mode and _hw */
@@ -705,6 +722,7 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm);
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm);
 void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm);
+void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm);
 #endif
 u8 first_antenna(u8 mask);
 u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx);
@@ -745,7 +763,7 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
                          struct iwl_device_cmd *cmd);
 
 /* NVM */
-int iwl_nvm_init(struct iwl_mvm *mvm);
+int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic);
 int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm);
 
 int iwl_mvm_up(struct iwl_mvm *mvm);
@@ -796,7 +814,8 @@ void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm,
 int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                            bool force_assoc_off);
 int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm,
                                struct ieee80211_vif *vif);
@@ -840,7 +859,7 @@ int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm,
                                       struct cfg80211_sched_scan_request *req);
 int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
                             struct cfg80211_sched_scan_request *req);
-int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm);
+int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify);
 int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm,
                                  struct iwl_rx_cmd_buffer *rxb,
                                  struct iwl_device_cmd *cmd);
@@ -874,10 +893,8 @@ void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm,
 int rs_pretty_print_rate(char *buf, const u32 rate);
 
 /* power management */
-int iwl_power_legacy_set_cam_mode(struct iwl_mvm *mvm);
-
 int iwl_mvm_power_update_device(struct iwl_mvm *mvm);
-int iwl_mvm_power_update_mac(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_power_update_mac(struct iwl_mvm *mvm);
 int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                                 char *buf, int bufsz);
 
@@ -886,8 +903,18 @@ int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
                                             struct iwl_rx_cmd_buffer *rxb,
                                             struct iwl_device_cmd *cmd);
 
+#ifdef CONFIG_IWLWIFI_LEDS
 int iwl_mvm_leds_init(struct iwl_mvm *mvm);
 void iwl_mvm_leds_exit(struct iwl_mvm *mvm);
+#else
+static inline int iwl_mvm_leds_init(struct iwl_mvm *mvm)
+{
+       return 0;
+}
+static inline void iwl_mvm_leds_exit(struct iwl_mvm *mvm)
+{
+}
+#endif
 
 /* D3 (WoWLAN, NetDetect) */
 int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
@@ -922,9 +949,9 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
 void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
 void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
 void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq);
+int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm);
 
 /* BT Coex */
-int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm);
 int iwl_send_bt_init_conf(struct iwl_mvm *mvm);
 int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
                             struct iwl_rx_cmd_buffer *rxb,
@@ -936,9 +963,10 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,
                                struct ieee80211_sta *sta);
 bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
                                     struct ieee80211_sta *sta);
+bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
+                                   enum ieee80211_band band);
 u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
                           struct ieee80211_tx_info *info, u8 ac);
-int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable);
 
 enum iwl_bt_kill_msk {
        BT_KILL_MSK_DEFAULT,
@@ -969,17 +997,11 @@ int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
 int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
                                  struct ieee80211_vif *vif,
                                  u32 flags);
-int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
-                               struct ieee80211_vif *vif, bool enable);
-int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm,
-                                struct ieee80211_vif *vif,
-                                bool force,
-                                u32 flags);
-
 /* 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);
+bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm);
 
 /* Low latency */
 int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
index cf2d09f53782b0227f9a47d9a8761af8b5ea7ffe..808f78f6fbf9fe478553bd54264f784d98c01597 100644 (file)
 #define NVM_WRITE_OPCODE 1
 #define NVM_READ_OPCODE 0
 
+/* load nvm chunk response */
+enum {
+       READ_NVM_CHUNK_SUCCEED = 0,
+       READ_NVM_CHUNK_NOT_VALID_ADDRESS = 1
+};
+
 /*
  * prepare the NVM host command w/ the pointers to the nvm buffer
  * and send it to fw
@@ -90,7 +96,7 @@ static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section,
        struct iwl_host_cmd cmd = {
                .id = NVM_ACCESS_CMD,
                .len = { sizeof(struct iwl_nvm_access_cmd), length },
-               .flags = CMD_SYNC | CMD_SEND_IN_RFKILL,
+               .flags = CMD_SEND_IN_RFKILL,
                .data = { &nvm_access_cmd, data },
                /* data may come from vmalloc, so use _DUP */
                .dataflags = { 0, IWL_HCMD_DFL_DUP },
@@ -112,7 +118,7 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
        struct iwl_rx_packet *pkt;
        struct iwl_host_cmd cmd = {
                .id = NVM_ACCESS_CMD,
-               .flags = CMD_SYNC | CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
+               .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
                .data = { &nvm_access_cmd, },
        };
        int ret, bytes_read, offset_read;
@@ -139,10 +145,26 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
        offset_read = le16_to_cpu(nvm_resp->offset);
        resp_data = nvm_resp->data;
        if (ret) {
-               IWL_ERR(mvm,
-                       "NVM access command failed with status %d (device: %s)\n",
-                       ret, mvm->cfg->name);
-               ret = -EINVAL;
+               if ((offset != 0) &&
+                   (ret == READ_NVM_CHUNK_NOT_VALID_ADDRESS)) {
+                       /*
+                        * meaning of NOT_VALID_ADDRESS:
+                        * driver try to read chunk from address that is
+                        * multiple of 2K and got an error since addr is empty.
+                        * meaning of (offset != 0): driver already
+                        * read valid data from another chunk so this case
+                        * is not an error.
+                        */
+                       IWL_DEBUG_EEPROM(mvm->trans->dev,
+                                        "NVM access command failed on offset 0x%x since that section size is multiple 2K\n",
+                                        offset);
+                       ret = 0;
+               } else {
+                       IWL_DEBUG_EEPROM(mvm->trans->dev,
+                                        "NVM access command failed with status %d (device: %s)\n",
+                                        ret, mvm->cfg->name);
+                       ret = -EIO;
+               }
                goto exit;
        }
 
@@ -211,9 +233,9 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
        while (ret == length) {
                ret = iwl_nvm_read_chunk(mvm, section, offset, length, data);
                if (ret < 0) {
-                       IWL_ERR(mvm,
-                               "Cannot read NVM from section %d offset %d, length %d\n",
-                               section, offset, length);
+                       IWL_DEBUG_EEPROM(mvm->trans->dev,
+                                        "Cannot read NVM from section %d offset %d, length %d\n",
+                                        section, offset, length);
                        return ret;
                }
                offset += ret;
@@ -238,13 +260,20 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
                        return NULL;
                }
        } else {
+               /* SW and REGULATORY sections are mandatory */
                if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
-                   !mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data ||
                    !mvm->nvm_sections[NVM_SECTION_TYPE_REGULATORY].data) {
                        IWL_ERR(mvm,
                                "Can't parse empty family 8000 NVM sections\n");
                        return NULL;
                }
+               /* MAC_OVERRIDE or at least HW section must exist */
+               if (!mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data &&
+                   !mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data) {
+                       IWL_ERR(mvm,
+                               "Can't parse mac_address, empty sections\n");
+                       return NULL;
+               }
        }
 
        if (WARN_ON(!mvm->cfg))
@@ -311,16 +340,16 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
         * get here after that we assume the NVM request can be satisfied
         * synchronously.
         */
-       ret = request_firmware(&fw_entry, iwlwifi_mod_params.nvm_file,
+       ret = request_firmware(&fw_entry, mvm->nvm_file_name,
                               mvm->trans->dev);
        if (ret) {
                IWL_ERR(mvm, "ERROR: %s isn't available %d\n",
-                       iwlwifi_mod_params.nvm_file, ret);
+                       mvm->nvm_file_name, ret);
                return ret;
        }
 
        IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n",
-                iwlwifi_mod_params.nvm_file, fw_entry->size);
+                mvm->nvm_file_name, fw_entry->size);
 
        if (fw_entry->size < sizeof(*file_sec)) {
                IWL_ERR(mvm, "NVM file too small\n");
@@ -427,53 +456,28 @@ int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm)
        return ret;
 }
 
-int iwl_nvm_init(struct iwl_mvm *mvm)
+int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)
 {
-       int ret, i, section;
+       int ret, section;
        u8 *nvm_buffer, *temp;
-       int nvm_to_read[NVM_MAX_NUM_SECTIONS];
-       int num_of_sections_to_read;
 
        if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS))
                return -EINVAL;
 
-       /* load external NVM if configured */
-       if (iwlwifi_mod_params.nvm_file) {
-               /* move to External NVM flow */
-               ret = iwl_mvm_read_external_nvm(mvm);
-               if (ret)
-                       return ret;
-       } else {
-               /* list of NVM sections we are allowed/need to read */
-               if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
-                       nvm_to_read[0] = mvm->cfg->nvm_hw_section_num;
-                       nvm_to_read[1] = NVM_SECTION_TYPE_SW;
-                       nvm_to_read[2] = NVM_SECTION_TYPE_CALIBRATION;
-                       nvm_to_read[3] = NVM_SECTION_TYPE_PRODUCTION;
-                       num_of_sections_to_read = 4;
-               } else {
-                       nvm_to_read[0] = NVM_SECTION_TYPE_SW;
-                       nvm_to_read[1] = NVM_SECTION_TYPE_CALIBRATION;
-                       nvm_to_read[2] = NVM_SECTION_TYPE_PRODUCTION;
-                       nvm_to_read[3] = NVM_SECTION_TYPE_REGULATORY;
-                       nvm_to_read[4] = NVM_SECTION_TYPE_MAC_OVERRIDE;
-                       num_of_sections_to_read = 5;
-               }
-
+       /* load NVM values from nic */
+       if (read_nvm_from_nic) {
                /* 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);
                if (!nvm_buffer)
                        return -ENOMEM;
-               for (i = 0; i < num_of_sections_to_read; i++) {
-                       section = nvm_to_read[i];
+               for (section = 0; section < NVM_MAX_NUM_SECTIONS; section++) {
                        /* we override the constness for initial read */
                        ret = iwl_nvm_read_section(mvm, section, nvm_buffer);
                        if (ret < 0)
-                               break;
+                               continue;
                        temp = kmemdup(nvm_buffer, ret, GFP_KERNEL);
                        if (!temp) {
                                ret = -ENOMEM;
@@ -502,15 +506,21 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
                                        mvm->nvm_hw_blob.size = ret;
                                        break;
                                }
-                               WARN(1, "section: %d", section);
                        }
 #endif
                }
                kfree(nvm_buffer);
-               if (ret < 0)
+       }
+
+       /* load external NVM if configured */
+       if (mvm->nvm_file_name) {
+               /* move to External NVM flow */
+               ret = iwl_mvm_read_external_nvm(mvm);
+               if (ret)
                        return ret;
        }
 
+       /* parse the relevant nvm sections */
        mvm->nvm_data = iwl_parse_nvm_sections(mvm);
        if (!mvm->nvm_data)
                return -ENODATA;
index 9545d7fdd4bfc69dfb1fb8c4e07de097d58b6ea7..cc2f7de396deb396d2b20e261b4c2877137dfa3e 100644 (file)
@@ -79,8 +79,8 @@
 #include "iwl-prph.h"
 #include "rs.h"
 #include "fw-api-scan.h"
-#include "fw-error-dump.h"
 #include "time-event.h"
+#include "iwl-fw-error-dump.h"
 
 /*
  * module name, copyright, version, etc.
@@ -220,7 +220,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
        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(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, true),
        RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true),
        RX_HANDLER(ANTENNA_COUPLING_NOTIFICATION,
                   iwl_mvm_rx_ant_coupling_notif, true),
@@ -402,6 +402,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        mvm->sf_state = SF_UNINIT;
 
        mutex_init(&mvm->mutex);
+       mutex_init(&mvm->d0i3_suspend_mutex);
        spin_lock_init(&mvm->async_handlers_lock);
        INIT_LIST_HEAD(&mvm->time_event_list);
        INIT_LIST_HEAD(&mvm->async_handlers_list);
@@ -465,13 +466,24 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
 
        min_backoff = calc_min_backoff(trans, cfg);
        iwl_mvm_tt_initialize(mvm, min_backoff);
+       /* set the nvm_file_name according to priority */
+       if (iwlwifi_mod_params.nvm_file)
+               mvm->nvm_file_name = iwlwifi_mod_params.nvm_file;
+       else
+               mvm->nvm_file_name = mvm->cfg->default_nvm_file;
+
+       if (WARN(cfg->no_power_up_nic_in_init && !mvm->nvm_file_name,
+                "not allowing power-up and not having nvm_file\n"))
+               goto out_free;
 
        /*
-        * If the NVM exists in an external file,
-        * there is no need to unnecessarily power up the NIC at driver load
+        * Even if nvm exists in the nvm_file driver should read agin the nvm
+        * from the nic because there might be entries that exist in the OTP
+        * and not in the file.
+        * for nics with no_power_up_nic_in_init: rely completley on nvm_file
         */
-       if (iwlwifi_mod_params.nvm_file) {
-               err = iwl_nvm_init(mvm);
+       if (cfg->no_power_up_nic_in_init && mvm->nvm_file_name) {
+               err = iwl_nvm_init(mvm, false);
                if (err)
                        goto out_free;
        } else {
@@ -518,7 +530,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
  out_free:
        iwl_phy_db_free(mvm->phy_db);
        kfree(mvm->scan_cmd);
-       if (!iwlwifi_mod_params.nvm_file)
+       if (!cfg->no_power_up_nic_in_init || !mvm->nvm_file_name)
                iwl_trans_op_mode_leave(trans);
        ieee80211_free_hw(mvm->hw);
        return NULL;
@@ -538,6 +550,7 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
        kfree(mvm->scan_cmd);
        vfree(mvm->fw_error_dump);
        kfree(mvm->fw_error_sram);
+       kfree(mvm->fw_error_rxf);
        kfree(mvm->mcast_filter_cmd);
        mvm->mcast_filter_cmd = NULL;
 
@@ -814,6 +827,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
        struct iwl_fw_error_dump_file *dump_file;
        struct iwl_fw_error_dump_data *dump_data;
        u32 file_len;
+       u32 trans_len;
 
        lockdep_assert_held(&mvm->mutex);
 
@@ -821,8 +835,13 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
                return;
 
        file_len = mvm->fw_error_sram_len +
+                  mvm->fw_error_rxf_len +
                   sizeof(*dump_file) +
-                  sizeof(*dump_data);
+                  sizeof(*dump_data) * 2;
+
+       trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0);
+       if (trans_len)
+               file_len += trans_len;
 
        dump_file = vmalloc(file_len);
        if (!dump_file)
@@ -833,7 +852,12 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
        dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
        dump_file->file_len = cpu_to_le32(file_len);
        dump_data = (void *)dump_file->data;
-       dump_data->type = IWL_FW_ERROR_DUMP_SRAM;
+       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
+       dump_data->len = cpu_to_le32(mvm->fw_error_rxf_len);
+       memcpy(dump_data->data, mvm->fw_error_rxf, mvm->fw_error_rxf_len);
+
+       dump_data = iwl_mvm_fw_error_next_data(dump_data);
+       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM);
        dump_data->len = cpu_to_le32(mvm->fw_error_sram_len);
 
        /*
@@ -842,6 +866,23 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
         * mvm->fw_error_sram right now.
         */
        memcpy(dump_data->data, mvm->fw_error_sram, mvm->fw_error_sram_len);
+
+       kfree(mvm->fw_error_rxf);
+       mvm->fw_error_rxf = NULL;
+       mvm->fw_error_rxf_len = 0;
+
+       kfree(mvm->fw_error_sram);
+       mvm->fw_error_sram = NULL;
+       mvm->fw_error_sram_len = 0;
+
+       if (trans_len) {
+               void *buf = iwl_mvm_fw_error_next_data(dump_data);
+               u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf,
+                                                        trans_len);
+               dump_data = (void *)((u8 *)buf + real_trans_len);
+               dump_file->file_len =
+                       cpu_to_le32(file_len - trans_len + real_trans_len);
+       }
 }
 #endif
 
@@ -853,6 +894,7 @@ static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode)
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        iwl_mvm_fw_error_sram_dump(mvm);
+       iwl_mvm_fw_error_rxf_dump(mvm);
 #endif
 
        iwl_mvm_nic_restart(mvm);
@@ -1126,9 +1168,9 @@ static void iwl_mvm_d0i3_exit_work(struct work_struct *wk)
        struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, d0i3_exit_work);
        struct iwl_host_cmd get_status_cmd = {
                .id = WOWLAN_GET_STATUSES,
-               .flags = CMD_SYNC | CMD_HIGH_PRIO | CMD_WANT_SKB,
+               .flags = CMD_HIGH_PRIO | CMD_WANT_SKB,
        };
-       struct iwl_wowlan_status_v6 *status;
+       struct iwl_wowlan_status *status;
        int ret;
        u32 disconnection_reasons, wakeup_reasons;
        __le16 *qos_seq = NULL;
@@ -1158,18 +1200,27 @@ static void iwl_mvm_d0i3_exit_work(struct work_struct *wk)
        iwl_free_resp(&get_status_cmd);
 out:
        iwl_mvm_d0i3_enable_tx(mvm, qos_seq);
+       iwl_mvm_unref(mvm, IWL_MVM_REF_EXIT_WORK);
        mutex_unlock(&mvm->mutex);
 }
 
-static int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode)
+int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm)
 {
-       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
        u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE |
                    CMD_WAKE_UP_TRANS;
        int ret;
 
        IWL_DEBUG_RPM(mvm, "MVM exiting D0i3\n");
 
+       mutex_lock(&mvm->d0i3_suspend_mutex);
+       if (test_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags)) {
+               IWL_DEBUG_RPM(mvm, "Deferring d0i3 exit until resume\n");
+               __set_bit(D0I3_PENDING_WAKEUP, &mvm->d0i3_suspend_flags);
+               mutex_unlock(&mvm->d0i3_suspend_mutex);
+               return 0;
+       }
+       mutex_unlock(&mvm->d0i3_suspend_mutex);
+
        ret = iwl_mvm_send_cmd_pdu(mvm, D0I3_END_CMD, flags, 0, NULL);
        if (ret)
                goto out;
@@ -1183,6 +1234,25 @@ out:
        return ret;
 }
 
+static int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+
+       iwl_mvm_ref(mvm, IWL_MVM_REF_EXIT_WORK);
+       return _iwl_mvm_exit_d0i3(mvm);
+}
+
+static void iwl_mvm_napi_add(struct iwl_op_mode *op_mode,
+                            struct napi_struct *napi,
+                            struct net_device *napi_dev,
+                            int (*poll)(struct napi_struct *, int),
+                            int weight)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+
+       ieee80211_napi_add(mvm->hw, napi, napi_dev, poll, weight);
+}
+
 static const struct iwl_op_mode_ops iwl_mvm_ops = {
        .start = iwl_op_mode_mvm_start,
        .stop = iwl_op_mode_mvm_stop,
@@ -1196,4 +1266,5 @@ static const struct iwl_op_mode_ops iwl_mvm_ops = {
        .nic_config = iwl_mvm_nic_config,
        .enter_d0i3 = iwl_mvm_enter_d0i3,
        .exit_d0i3 = iwl_mvm_exit_d0i3,
+       .napi_add = iwl_mvm_napi_add,
 };
index 237efe0ac1c44dab52d375ced3cb84912aabe082..539f3a942d437565ab6ba9accd06f71874985af4 100644 (file)
@@ -156,6 +156,18 @@ static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
        idle_cnt = chains_static;
        active_cnt = chains_dynamic;
 
+       /* In scenarios where we only ever use a single-stream rates,
+        * i.e. legacy 11b/g/a associations, single-stream APs or even
+        * static SMPS, enable both chains to get diversity, improving
+        * the case where we're far enough from the AP that attenuation
+        * between the two antennas is sufficiently different to impact
+        * performance.
+        */
+       if (active_cnt == 1 && iwl_mvm_rx_diversity_allowed(mvm)) {
+               idle_cnt = 2;
+               active_cnt = 2;
+       }
+
        cmd->rxchain_info = cpu_to_le32(mvm->fw->valid_rx_ant <<
                                        PHY_RX_CHAIN_VALID_POS);
        cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
@@ -187,7 +199,7 @@ static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
        iwl_mvm_phy_ctxt_cmd_data(mvm, &cmd, chandef,
                                  chains_static, chains_dynamic);
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, CMD_SYNC,
+       ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, 0,
                                   sizeof(struct iwl_phy_context_cmd),
                                   &cmd);
        if (ret)
@@ -202,18 +214,15 @@ 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)
 {
-       int ret;
-
        WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
                ctxt->ref);
        lockdep_assert_held(&mvm->mutex);
 
        ctxt->channel = chandef->chan;
-       ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
-                                    chains_static, chains_dynamic,
-                                    FW_CTXT_ACTION_ADD, 0);
 
-       return ret;
+       return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
+                                     chains_static, chains_dynamic,
+                                     FW_CTXT_ACTION_ADD, 0);
 }
 
 /*
index 6b636eab33391cbec4957180efe2e74d2ad07388..c182a8baf685857d3c2857443d53ae978e8646a8 100644 (file)
@@ -123,28 +123,6 @@ void iwl_mvm_beacon_filter_set_cqm_params(struct iwl_mvm *mvm,
        cmd->ba_enable_beacon_abort = cpu_to_le32(mvmvif->bf_data.ba_enabled);
 }
 
-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 = cpu_to_le32(1),
-               .ba_enable_beacon_abort = cpu_to_le32(enable),
-       };
-
-       if (!mvmvif->bf_data.bf_enabled)
-               return 0;
-
-       if (mvm->cur_ucode == IWL_UCODE_WOWLAN)
-               cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3);
-
-       mvmvif->bf_data.ba_enabled = enable;
-       iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, &cmd);
-       iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
-       return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, CMD_SYNC);
-}
-
 static void iwl_mvm_power_log(struct iwl_mvm *mvm,
                              struct iwl_mac_power_cmd *cmd)
 {
@@ -268,6 +246,57 @@ static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm,
                IWL_MVM_PS_HEAVY_RX_THLD_PERCENT;
 }
 
+static void iwl_mvm_binding_iterator(void *_data, u8 *mac,
+                                     struct ieee80211_vif *vif)
+{
+       unsigned long *data = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (!mvmvif->phy_ctxt)
+               return;
+
+       if (vif->type == NL80211_IFTYPE_STATION ||
+           vif->type == NL80211_IFTYPE_AP)
+               __set_bit(mvmvif->phy_ctxt->id, data);
+}
+
+static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm,
+                                      struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       unsigned long phy_ctxt_counter = 0;
+
+       ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+                                                  IEEE80211_IFACE_ITER_NORMAL,
+                                                  iwl_mvm_binding_iterator,
+                                                  &phy_ctxt_counter);
+
+       if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
+                   ETH_ALEN))
+               return false;
+
+       if (vif->p2p &&
+           !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD))
+               return false;
+       /*
+        * Avoid using uAPSD if P2P client is associated to GO that uses
+        * opportunistic power save. This is due to current FW limitation.
+        */
+       if (vif->p2p &&
+           (vif->bss_conf.p2p_noa_attr.oppps_ctwindow &
+           IEEE80211_P2P_OPPPS_ENABLE_BIT))
+               return false;
+
+       /*
+        * Avoid using uAPSD if client is in DCM -
+        * low latency issue in Miracast
+        */
+       if (hweight8(phy_ctxt_counter) >= 2)
+               return false;
+
+       return true;
+}
+
 static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
                                    struct ieee80211_vif *vif,
                                    struct iwl_mac_power_cmd *cmd)
@@ -280,7 +309,6 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
        bool radar_detect = false;
        struct iwl_mvm_vif *mvmvif __maybe_unused =
                iwl_mvm_vif_from_mac80211(vif);
-       bool allow_uapsd = true;
 
        cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
                                                            mvmvif->color));
@@ -303,13 +331,8 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
 
        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
        if (!vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif) ||
-           mvm->pm_disabled)
+           !mvmvif->pm_enabled)
                return;
 
        cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
@@ -351,23 +374,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
                        cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
        }
 
-       if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
-                   ETH_ALEN))
-               allow_uapsd = false;
-
-       if (vif->p2p &&
-           !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD))
-               allow_uapsd = false;
-       /*
-        * Avoid using uAPSD if P2P client is associated to GO that uses
-        * opportunistic power save. This is due to current FW limitation.
-        */
-       if (vif->p2p &&
-           vif->bss_conf.p2p_noa_attr.oppps_ctwindow &
-           IEEE80211_P2P_OPPPS_ENABLE_BIT)
-               allow_uapsd = false;
-
-       if (allow_uapsd)
+       if (iwl_mvm_power_allow_uapsd(mvm, vif))
                iwl_mvm_power_configure_uapsd(mvm, vif, cmd);
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -421,20 +428,13 @@ static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm,
 {
        struct iwl_mac_power_cmd cmd = {};
 
-       if (vif->type != NL80211_IFTYPE_STATION)
-               return 0;
-
-       if (vif->p2p &&
-           !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM))
-               return 0;
-
        iwl_mvm_power_build_cmd(mvm, vif, &cmd);
        iwl_mvm_power_log(mvm, &cmd);
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        memcpy(&iwl_mvm_vif_from_mac80211(vif)->mac_pwr_cmd, &cmd, sizeof(cmd));
 #endif
 
-       return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_SYNC,
+       return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, 0,
                                    sizeof(cmd), &cmd);
 }
 
@@ -444,12 +444,6 @@ int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
                .flags = cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK),
        };
 
-       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT))
-               return 0;
-
-       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
-               return 0;
-
        if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
                mvm->ps_disabled = true;
 
@@ -466,7 +460,7 @@ int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
                        "Sending device power command with flags = 0x%X\n",
                        cmd.flags);
 
-       return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC, sizeof(cmd),
+       return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, 0, sizeof(cmd),
                                    &cmd);
 }
 
@@ -508,86 +502,69 @@ int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
        return 0;
 }
 
-struct iwl_power_constraint {
+struct iwl_power_vifs {
        struct ieee80211_vif *bf_vif;
        struct ieee80211_vif *bss_vif;
        struct ieee80211_vif *p2p_vif;
-       u16 bss_phyctx_id;
-       u16 p2p_phyctx_id;
-       bool pm_disabled;
-       bool ps_disabled;
-       struct iwl_mvm *mvm;
+       struct ieee80211_vif *ap_vif;
+       struct ieee80211_vif *monitor_vif;
+       bool p2p_active;
+       bool bss_active;
+       bool ap_active;
+       bool monitor_active;
 };
 
 static void iwl_mvm_power_iterator(void *_data, u8 *mac,
                                   struct ieee80211_vif *vif)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_power_constraint *power_iterator = _data;
-       struct iwl_mvm *mvm = power_iterator->mvm;
+       struct iwl_power_vifs *power_iterator = _data;
 
+       mvmvif->pm_enabled = false;
        switch (ieee80211_vif_type_p2p(vif)) {
        case NL80211_IFTYPE_P2P_DEVICE:
                break;
 
        case NL80211_IFTYPE_P2P_GO:
        case NL80211_IFTYPE_AP:
-               /* no BSS power mgmt if we have an active AP */
-               if (mvmvif->ap_ibss_active)
-                       power_iterator->pm_disabled = true;
+               /* only a single MAC of the same type */
+               WARN_ON(power_iterator->ap_vif);
+               power_iterator->ap_vif = vif;
+               if (mvmvif->phy_ctxt)
+                       if (mvmvif->phy_ctxt->id < MAX_PHYS)
+                               power_iterator->ap_active = true;
                break;
 
        case NL80211_IFTYPE_MONITOR:
-               /* no BSS power mgmt and no device power save */
-               power_iterator->pm_disabled = true;
-               power_iterator->ps_disabled = true;
+               /* only a single MAC of the same type */
+               WARN_ON(power_iterator->monitor_vif);
+               power_iterator->monitor_vif = vif;
+               if (mvmvif->phy_ctxt)
+                       if (mvmvif->phy_ctxt->id < MAX_PHYS)
+                               power_iterator->monitor_active = true;
                break;
 
        case NL80211_IFTYPE_P2P_CLIENT:
-               if (mvmvif->phy_ctxt)
-                       power_iterator->p2p_phyctx_id = mvmvif->phy_ctxt->id;
-
-               /* we should have only one P2P vif */
+               /* only a single MAC of the same type */
                WARN_ON(power_iterator->p2p_vif);
                power_iterator->p2p_vif = vif;
-
-               IWL_DEBUG_POWER(mvm, "p2p: p2p_id=%d, bss_id=%d\n",
-                               power_iterator->p2p_phyctx_id,
-                               power_iterator->bss_phyctx_id);
-               if (!(mvm->fw->ucode_capa.flags &
-                     IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM)) {
-                       /* no BSS power mgmt if we have a P2P client*/
-                       power_iterator->pm_disabled = true;
-               } else if (power_iterator->p2p_phyctx_id < MAX_PHYS &&
-                          power_iterator->bss_phyctx_id < MAX_PHYS &&
-                          power_iterator->p2p_phyctx_id ==
-                          power_iterator->bss_phyctx_id) {
-                       power_iterator->pm_disabled = true;
-               }
+               if (mvmvif->phy_ctxt)
+                       if (mvmvif->phy_ctxt->id < MAX_PHYS)
+                               power_iterator->p2p_active = true;
                break;
 
        case NL80211_IFTYPE_STATION:
-               if (mvmvif->phy_ctxt)
-                       power_iterator->bss_phyctx_id = mvmvif->phy_ctxt->id;
-
-               /* we should have only one BSS vif */
+               /* only a single MAC of the same type */
                WARN_ON(power_iterator->bss_vif);
                power_iterator->bss_vif = vif;
+               if (mvmvif->phy_ctxt)
+                       if (mvmvif->phy_ctxt->id < MAX_PHYS)
+                               power_iterator->bss_active = true;
 
                if (mvmvif->bf_data.bf_enabled &&
                    !WARN_ON(power_iterator->bf_vif))
                        power_iterator->bf_vif = vif;
 
-               IWL_DEBUG_POWER(mvm, "bss: p2p_id=%d, bss_id=%d\n",
-                               power_iterator->p2p_phyctx_id,
-                               power_iterator->bss_phyctx_id);
-               if (mvm->fw->ucode_capa.flags &
-                   IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM &&
-                       (power_iterator->p2p_phyctx_id < MAX_PHYS &&
-                        power_iterator->bss_phyctx_id < MAX_PHYS &&
-                        power_iterator->p2p_phyctx_id ==
-                        power_iterator->bss_phyctx_id))
-                       power_iterator->pm_disabled = true;
                break;
 
        default:
@@ -596,70 +573,73 @@ static void iwl_mvm_power_iterator(void *_data, u8 *mac,
 }
 
 static void
-iwl_mvm_power_get_global_constraint(struct iwl_mvm *mvm,
-                                   struct iwl_power_constraint *constraint)
+iwl_mvm_power_set_pm(struct iwl_mvm *mvm,
+                                   struct iwl_power_vifs *vifs)
 {
-       lockdep_assert_held(&mvm->mutex);
+       struct iwl_mvm_vif *bss_mvmvif = NULL;
+       struct iwl_mvm_vif *p2p_mvmvif = NULL;
+       struct iwl_mvm_vif *ap_mvmvif = NULL;
+       bool client_same_channel = false;
+       bool ap_same_channel = false;
 
-       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) {
-               constraint->pm_disabled = true;
-               constraint->ps_disabled = true;
-       }
+       lockdep_assert_held(&mvm->mutex);
 
+       /* get vifs info + set pm_enable to false */
        ieee80211_iterate_active_interfaces_atomic(mvm->hw,
                                            IEEE80211_IFACE_ITER_NORMAL,
-                                           iwl_mvm_power_iterator, constraint);
-}
-
-int iwl_mvm_power_update_mac(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_power_constraint constraint = {
-                   .p2p_phyctx_id = MAX_PHYS,
-                   .bss_phyctx_id = MAX_PHYS,
-                   .mvm = mvm,
-       };
-       bool ba_enable;
-       int ret;
+                                           iwl_mvm_power_iterator, vifs);
 
-       lockdep_assert_held(&mvm->mutex);
+       if (vifs->bss_vif)
+               bss_mvmvif = iwl_mvm_vif_from_mac80211(vifs->bss_vif);
 
-       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT))
-               return 0;
+       if (vifs->p2p_vif)
+               p2p_mvmvif = iwl_mvm_vif_from_mac80211(vifs->p2p_vif);
 
-       iwl_mvm_power_get_global_constraint(mvm, &constraint);
-       mvm->ps_disabled = constraint.ps_disabled;
-       mvm->pm_disabled = constraint.pm_disabled;
+       if (vifs->ap_vif)
+               ap_mvmvif = iwl_mvm_vif_from_mac80211(vifs->ap_vif);
 
-       /* don't update device power state unless we add / remove monitor */
-       if (vif->type == NL80211_IFTYPE_MONITOR) {
-               ret = iwl_mvm_power_update_device(mvm);
-               if (ret)
-                       return ret;
+       /* enable PM on bss if bss stand alone */
+       if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active) {
+               bss_mvmvif->pm_enabled = true;
+               return;
        }
 
-       if (constraint.bss_vif) {
-               ret = iwl_mvm_power_send_cmd(mvm, constraint.bss_vif);
-               if (ret)
-                       return ret;
+       /* enable PM on p2p if p2p stand alone */
+       if (vifs->p2p_active && !vifs->bss_active && !vifs->ap_active) {
+               if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM)
+                       p2p_mvmvif->pm_enabled = true;
+               return;
        }
 
-       if (constraint.p2p_vif) {
-               ret = iwl_mvm_power_send_cmd(mvm, constraint.p2p_vif);
-               if (ret)
-                       return ret;
+       if (vifs->bss_active && vifs->p2p_active)
+               client_same_channel = (bss_mvmvif->phy_ctxt->id ==
+                                      p2p_mvmvif->phy_ctxt->id);
+       if (vifs->bss_active && vifs->ap_active)
+               ap_same_channel = (bss_mvmvif->phy_ctxt->id ==
+                                  ap_mvmvif->phy_ctxt->id);
+
+       /* clients are not stand alone: enable PM if DCM */
+       if (!(client_same_channel || ap_same_channel) &&
+           (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM)) {
+               if (vifs->bss_active)
+                       bss_mvmvif->pm_enabled = true;
+               if (vifs->p2p_active &&
+                   (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM))
+                       p2p_mvmvif->pm_enabled = true;
+               return;
        }
 
-       if (!constraint.bf_vif)
-               return 0;
-
-       vif = constraint.bf_vif;
-       mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
-       ba_enable = !(constraint.pm_disabled || constraint.ps_disabled ||
-                     !vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif));
-
-       return iwl_mvm_update_beacon_abort(mvm, constraint.bf_vif, ba_enable);
+       /*
+        * There is only one channel in the system and there are only
+        * bss and p2p clients that share it
+        */
+       if (client_same_channel && !vifs->ap_active &&
+           (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM)) {
+               /* share same channel*/
+               bss_mvmvif->pm_enabled = true;
+               if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM)
+                       p2p_mvmvif->pm_enabled = true;
+       }
 }
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -671,19 +651,10 @@ int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
        struct iwl_mac_power_cmd cmd = {};
        int pos = 0;
 
-       if (WARN_ON(!(mvm->fw->ucode_capa.flags &
-                     IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)))
-               return 0;
-
        mutex_lock(&mvm->mutex);
        memcpy(&cmd, &mvmvif->mac_pwr_cmd, sizeof(cmd));
        mutex_unlock(&mvm->mutex);
 
-       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_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, "power_scheme = %d\n",
                         iwlmvm_mod_params.power_scheme);
        pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n",
@@ -790,7 +761,7 @@ static int _iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int ret;
 
-       if (mvmvif != mvm->bf_allowed_vif ||
+       if (mvmvif != mvm->bf_allowed_vif || !vif->bss_conf.dtim_period ||
            vif->type != NL80211_IFTYPE_STATION || vif->p2p)
                return 0;
 
@@ -818,6 +789,26 @@ int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
        return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, flags, false);
 }
 
+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 = cpu_to_le32(1),
+       };
+
+       if (!mvmvif->bf_data.bf_enabled)
+               return 0;
+
+       if (mvm->cur_ucode == IWL_UCODE_WOWLAN)
+               cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3);
+
+       mvmvif->bf_data.ba_enabled = enable;
+       return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, 0, false);
+}
+
 int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
                                  struct ieee80211_vif *vif,
                                  u32 flags)
@@ -826,8 +817,7 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int ret;
 
-       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BF_UPDATED) ||
-           vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
                return 0;
 
        ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, flags);
@@ -838,6 +828,55 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
        return ret;
 }
 
+int iwl_mvm_power_update_mac(struct iwl_mvm *mvm)
+{
+       struct iwl_mvm_vif *mvmvif;
+       struct iwl_power_vifs vifs = {};
+       bool ba_enable;
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       iwl_mvm_power_set_pm(mvm, &vifs);
+
+       /* disable PS if CAM */
+       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) {
+               mvm->ps_disabled = true;
+       } else {
+       /* don't update device power state unless we add / remove monitor */
+               if (vifs.monitor_vif) {
+                       if (vifs.monitor_active)
+                               mvm->ps_disabled = true;
+                       ret = iwl_mvm_power_update_device(mvm);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       if (vifs.bss_vif) {
+               ret = iwl_mvm_power_send_cmd(mvm, vifs.bss_vif);
+               if (ret)
+                       return ret;
+       }
+
+       if (vifs.p2p_vif) {
+               ret = iwl_mvm_power_send_cmd(mvm, vifs.p2p_vif);
+               if (ret)
+                       return ret;
+       }
+
+       if (!vifs.bf_vif)
+               return 0;
+
+       mvmvif = iwl_mvm_vif_from_mac80211(vifs.bf_vif);
+
+       ba_enable = !(!mvmvif->pm_enabled || mvm->ps_disabled ||
+                     !vifs.bf_vif->bss_conf.ps ||
+                     iwl_mvm_vif_low_latency(mvmvif));
+
+       return iwl_mvm_update_beacon_abort(mvm, vifs.bf_vif, ba_enable);
+}
+
 int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm,
                                   struct ieee80211_vif *vif,
                                   bool enable, u32 flags)
@@ -861,9 +900,10 @@ int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm,
                if (WARN_ON(!dtimper_msec))
                        return 0;
 
-               cmd.flags |=
-                       cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
                cmd.skip_dtim_periods = 300 / dtimper_msec;
+               if (cmd.skip_dtim_periods)
+                       cmd.flags |=
+                               cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
        }
        iwl_mvm_power_log(mvm, &cmd);
 #ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -894,33 +934,3 @@ int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm,
 
        return ret;
 }
-
-int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm,
-                                struct ieee80211_vif *vif,
-                                bool force,
-                                u32 flags)
-{
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
-       if (mvmvif != mvm->bf_allowed_vif)
-               return 0;
-
-       if (!mvmvif->bf_data.bf_enabled) {
-               /* disable beacon filtering explicitly if force is true */
-               if (force)
-                       return iwl_mvm_disable_beacon_filter(mvm, vif, flags);
-               return 0;
-       }
-
-       return iwl_mvm_enable_beacon_filter(mvm, vif, flags);
-}
-
-int iwl_power_legacy_set_cam_mode(struct iwl_mvm *mvm)
-{
-       struct iwl_powertable_cmd cmd = {
-               .keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC,
-       };
-
-       return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
-                                   sizeof(cmd), &cmd);
-}
index 35e86e06dffda924f9f4ba75d36717ac311d3602..ba68d7b8450508d9c7b123500b654d2195b5613a 100644 (file)
@@ -285,7 +285,7 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
 
        iwl_mvm_adjust_quota_for_noa(mvm, &cmd);
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, CMD_SYNC,
+       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0,
                                   sizeof(cmd), &cmd);
        if (ret)
                IWL_ERR(mvm, "Failed to send quota: %d\n", ret);
index e1c838899363b373d176a71bc32d02282d56c018..306a6caa486889b4dc27ea53fc350f246a7433f8 100644 (file)
@@ -211,7 +211,7 @@ static const struct rs_tx_column rs_tx_columns[] = {
                .next_columns = {
                        RS_COLUMN_LEGACY_ANT_B,
                        RS_COLUMN_SISO_ANT_A,
-                       RS_COLUMN_SISO_ANT_B,
+                       RS_COLUMN_MIMO2,
                        RS_COLUMN_INVALID,
                        RS_COLUMN_INVALID,
                        RS_COLUMN_INVALID,
@@ -223,8 +223,8 @@ static const struct rs_tx_column rs_tx_columns[] = {
                .ant = ANT_B,
                .next_columns = {
                        RS_COLUMN_LEGACY_ANT_A,
-                       RS_COLUMN_SISO_ANT_A,
                        RS_COLUMN_SISO_ANT_B,
+                       RS_COLUMN_MIMO2,
                        RS_COLUMN_INVALID,
                        RS_COLUMN_INVALID,
                        RS_COLUMN_INVALID,
@@ -238,10 +238,10 @@ static const struct rs_tx_column rs_tx_columns[] = {
                        RS_COLUMN_SISO_ANT_B,
                        RS_COLUMN_MIMO2,
                        RS_COLUMN_SISO_ANT_A_SGI,
-                       RS_COLUMN_SISO_ANT_B_SGI,
                        RS_COLUMN_LEGACY_ANT_A,
                        RS_COLUMN_LEGACY_ANT_B,
                        RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
                },
                .checks = {
                        rs_siso_allow,
@@ -254,10 +254,10 @@ static const struct rs_tx_column rs_tx_columns[] = {
                        RS_COLUMN_SISO_ANT_A,
                        RS_COLUMN_MIMO2,
                        RS_COLUMN_SISO_ANT_B_SGI,
-                       RS_COLUMN_SISO_ANT_A_SGI,
                        RS_COLUMN_LEGACY_ANT_A,
                        RS_COLUMN_LEGACY_ANT_B,
                        RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
                },
                .checks = {
                        rs_siso_allow,
@@ -271,10 +271,10 @@ static const struct rs_tx_column rs_tx_columns[] = {
                        RS_COLUMN_SISO_ANT_B_SGI,
                        RS_COLUMN_MIMO2_SGI,
                        RS_COLUMN_SISO_ANT_A,
-                       RS_COLUMN_SISO_ANT_B,
-                       RS_COLUMN_MIMO2,
                        RS_COLUMN_LEGACY_ANT_A,
                        RS_COLUMN_LEGACY_ANT_B,
+                       RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
                },
                .checks = {
                        rs_siso_allow,
@@ -289,10 +289,10 @@ static const struct rs_tx_column rs_tx_columns[] = {
                        RS_COLUMN_SISO_ANT_A_SGI,
                        RS_COLUMN_MIMO2_SGI,
                        RS_COLUMN_SISO_ANT_B,
-                       RS_COLUMN_SISO_ANT_A,
-                       RS_COLUMN_MIMO2,
                        RS_COLUMN_LEGACY_ANT_A,
                        RS_COLUMN_LEGACY_ANT_B,
+                       RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
                },
                .checks = {
                        rs_siso_allow,
@@ -304,12 +304,12 @@ static const struct rs_tx_column rs_tx_columns[] = {
                .ant = ANT_AB,
                .next_columns = {
                        RS_COLUMN_SISO_ANT_A,
-                       RS_COLUMN_SISO_ANT_B,
-                       RS_COLUMN_SISO_ANT_A_SGI,
-                       RS_COLUMN_SISO_ANT_B_SGI,
                        RS_COLUMN_MIMO2_SGI,
                        RS_COLUMN_LEGACY_ANT_A,
                        RS_COLUMN_LEGACY_ANT_B,
+                       RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
                },
                .checks = {
                        rs_mimo_allow,
@@ -321,12 +321,12 @@ static const struct rs_tx_column rs_tx_columns[] = {
                .sgi = true,
                .next_columns = {
                        RS_COLUMN_SISO_ANT_A_SGI,
-                       RS_COLUMN_SISO_ANT_B_SGI,
-                       RS_COLUMN_SISO_ANT_A,
-                       RS_COLUMN_SISO_ANT_B,
                        RS_COLUMN_MIMO2,
                        RS_COLUMN_LEGACY_ANT_A,
                        RS_COLUMN_LEGACY_ANT_B,
+                       RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
                },
                .checks = {
                        rs_mimo_allow,
@@ -527,6 +527,9 @@ static void rs_rate_scale_clear_tbl_windows(struct iwl_mvm *mvm,
        IWL_DEBUG_RATE(mvm, "Clearing up window stats\n");
        for (i = 0; i < IWL_RATE_COUNT; i++)
                rs_rate_scale_clear_window(&tbl->win[i]);
+
+       for (i = 0; i < ARRAY_SIZE(tbl->tpc_win); i++)
+               rs_rate_scale_clear_window(&tbl->tpc_win[i]);
 }
 
 static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type)
@@ -656,17 +659,34 @@ static int _rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
        return 0;
 }
 
-static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
-                             int scale_index, int attempts, int successes)
+static int rs_collect_tx_data(struct iwl_lq_sta *lq_sta,
+                             struct iwl_scale_tbl_info *tbl,
+                             int scale_index, int attempts, int successes,
+                             u8 reduced_txp)
 {
        struct iwl_rate_scale_data *window = NULL;
+       int ret;
 
        if (scale_index < 0 || scale_index >= IWL_RATE_COUNT)
                return -EINVAL;
 
+       if (tbl->column != RS_COLUMN_INVALID) {
+               lq_sta->tx_stats[tbl->column][scale_index].total += attempts;
+               lq_sta->tx_stats[tbl->column][scale_index].success += successes;
+       }
+
        /* Select window for current tx bit rate */
        window = &(tbl->win[scale_index]);
 
+       ret = _rs_collect_tx_data(tbl, scale_index, attempts, successes,
+                                 window);
+       if (ret)
+               return ret;
+
+       if (WARN_ON_ONCE(reduced_txp > TPC_MAX_REDUCTION))
+               return -EINVAL;
+
+       window = &tbl->tpc_win[reduced_txp];
        return _rs_collect_tx_data(tbl, scale_index, attempts, successes,
                                   window);
 }
@@ -1000,6 +1020,7 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
        u32 ucode_rate;
        struct rs_rate rate;
        struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl;
+       u8 reduced_txp = (uintptr_t)info->status.status_driver_data[0];
 
        /* Treat uninitialized rate scaling data same as non-existing. */
        if (!lq_sta) {
@@ -1141,9 +1162,10 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
        if (info->flags & IEEE80211_TX_STAT_AMPDU) {
                ucode_rate = le32_to_cpu(table->rs_table[0]);
                rs_rate_from_ucode_rate(ucode_rate, info->band, &rate);
-               rs_collect_tx_data(curr_tbl, rate.index,
+               rs_collect_tx_data(lq_sta, curr_tbl, rate.index,
                                   info->status.ampdu_len,
-                                  info->status.ampdu_ack_len);
+                                  info->status.ampdu_ack_len,
+                                  reduced_txp);
 
                /* Update success/fail counts if not searching for new mode */
                if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) {
@@ -1176,8 +1198,9 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
                        else
                                continue;
 
-                       rs_collect_tx_data(tmp_tbl, rate.index, 1,
-                                          i < retries ? 0 : legacy_success);
+                       rs_collect_tx_data(lq_sta, tmp_tbl, rate.index, 1,
+                                          i < retries ? 0 : legacy_success,
+                                          reduced_txp);
                }
 
                /* Update success/fail counts if not searching for new mode */
@@ -1188,6 +1211,7 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
        }
        /* The last TX rate is cached in lq_sta; it's set in if/else above */
        lq_sta->last_rate_n_flags = ucode_rate;
+       IWL_DEBUG_RATE(mvm, "reduced txpower: %d\n", reduced_txp);
 done:
        /* See if there's a better rate or modulation mode to try. */
        if (sta && sta->supp_rates[sband->band])
@@ -1311,105 +1335,50 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
        tbl->expected_tpt = rs_get_expected_tpt_table(lq_sta, column, rate->bw);
 }
 
-/*
- * Find starting rate for new "search" high-throughput mode of modulation.
- * Goal is to find lowest expected rate (under perfect conditions) that is
- * above the current measured throughput of "active" mode, to give new mode
- * a fair chance to prove itself without too many challenges.
- *
- * This gets called when transitioning to more aggressive modulation
- * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive
- * (i.e. MIMO to SISO).  When moving to MIMO, bit rate will typically need
- * to decrease to match "active" throughput.  When moving from MIMO to SISO,
- * bit rate will typically need to increase, but not if performance was bad.
- */
 static s32 rs_get_best_rate(struct iwl_mvm *mvm,
                            struct iwl_lq_sta *lq_sta,
                            struct iwl_scale_tbl_info *tbl,     /* "search" */
-                           u16 rate_mask, s8 index)
+                           unsigned long rate_mask, s8 index)
 {
-       /* "active" values */
        struct iwl_scale_tbl_info *active_tbl =
            &(lq_sta->lq_info[lq_sta->active_tbl]);
-       s32 active_sr = active_tbl->win[index].success_ratio;
-       s32 active_tpt = active_tbl->expected_tpt[index];
-       /* expected "search" throughput */
+       s32 success_ratio = active_tbl->win[index].success_ratio;
+       u16 expected_current_tpt = active_tbl->expected_tpt[index];
        const u16 *tpt_tbl = tbl->expected_tpt;
-
-       s32 new_rate, high, low, start_hi;
        u16 high_low;
-       s8 rate = index;
-
-       new_rate = high = low = start_hi = IWL_RATE_INVALID;
-
-       while (1) {
-               high_low = rs_get_adjacent_rate(mvm, rate, rate_mask,
-                                               tbl->rate.type);
-
-               low = high_low & 0xff;
-               high = (high_low >> 8) & 0xff;
+       u32 target_tpt;
+       int rate_idx;
 
-               /*
-                * Lower the "search" bit rate, to give new "search" mode
-                * approximately the same throughput as "active" if:
-                *
-                * 1) "Active" mode has been working modestly well (but not
-                *    great), and expected "search" throughput (under perfect
-                *    conditions) at candidate rate is above the actual
-                *    measured "active" throughput (but less than expected
-                *    "active" throughput under perfect conditions).
-                * OR
-                * 2) "Active" mode has been working perfectly or very well
-                *    and expected "search" throughput (under perfect
-                *    conditions) at candidate rate is above expected
-                *    "active" throughput (under perfect conditions).
-                */
-               if ((((100 * tpt_tbl[rate]) > lq_sta->last_tpt) &&
-                    ((active_sr > RS_SR_FORCE_DECREASE) &&
-                     (active_sr <= IWL_RATE_HIGH_TH) &&
-                     (tpt_tbl[rate] <= active_tpt))) ||
-                   ((active_sr >= IWL_RATE_SCALE_SWITCH) &&
-                    (tpt_tbl[rate] > active_tpt))) {
-                       /* (2nd or later pass)
-                        * If we've already tried to raise the rate, and are
-                        * now trying to lower it, use the higher rate. */
-                       if (start_hi != IWL_RATE_INVALID) {
-                               new_rate = start_hi;
-                               break;
-                       }
-
-                       new_rate = rate;
+       if (success_ratio > RS_SR_NO_DECREASE) {
+               target_tpt = 100 * expected_current_tpt;
+               IWL_DEBUG_RATE(mvm,
+                              "SR %d high. Find rate exceeding EXPECTED_CURRENT %d\n",
+                              success_ratio, target_tpt);
+       } else {
+               target_tpt = lq_sta->last_tpt;
+               IWL_DEBUG_RATE(mvm,
+                              "SR %d not thag good. Find rate exceeding ACTUAL_TPT %d\n",
+                              success_ratio, target_tpt);
+       }
 
-                       /* Loop again with lower rate */
-                       if (low != IWL_RATE_INVALID)
-                               rate = low;
+       rate_idx = find_first_bit(&rate_mask, BITS_PER_LONG);
 
-                       /* Lower rate not available, use the original */
-                       else
-                               break;
-
-               /* Else try to raise the "search" rate to match "active" */
-               } else {
-                       /* (2nd or later pass)
-                        * If we've already tried to lower the rate, and are
-                        * now trying to raise it, use the lower rate. */
-                       if (new_rate != IWL_RATE_INVALID)
-                               break;
+       while (rate_idx != IWL_RATE_INVALID) {
+               if (target_tpt < (100 * tpt_tbl[rate_idx]))
+                       break;
 
-                       /* Loop again with higher rate */
-                       else if (high != IWL_RATE_INVALID) {
-                               start_hi = high;
-                               rate = high;
+               high_low = rs_get_adjacent_rate(mvm, rate_idx, rate_mask,
+                                               tbl->rate.type);
 
-                       /* Higher rate not available, use the original */
-                       } else {
-                               new_rate = rate;
-                               break;
-                       }
-               }
+               rate_idx = (high_low >> 8) & 0xff;
        }
 
-       return new_rate;
+       IWL_DEBUG_RATE(mvm, "Best rate found %d target_tp %d expected_new %d\n",
+                      rate_idx, target_tpt,
+                      rate_idx != IWL_RATE_INVALID ?
+                      100 * tpt_tbl[rate_idx] : IWL_INVALID_VALUE);
+
+       return rate_idx;
 }
 
 static u32 rs_bw_from_sta_bw(struct ieee80211_sta *sta)
@@ -1584,7 +1553,7 @@ static enum rs_column rs_get_next_column(struct iwl_mvm *mvm,
 
                tpt = lq_sta->last_tpt / 100;
                expected_tpt_tbl = rs_get_expected_tpt_table(lq_sta, next_col,
-                                                            tbl->rate.bw);
+                                                    rs_bw_from_sta_bw(sta));
                if (WARN_ON_ONCE(!expected_tpt_tbl))
                        continue;
 
@@ -1625,7 +1594,7 @@ static int rs_switch_to_column(struct iwl_mvm *mvm,
        const struct rs_tx_column *curr_column = &rs_tx_columns[tbl->column];
        u32 sz = (sizeof(struct iwl_scale_tbl_info) -
                  (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
-       u16 rate_mask = 0;
+       unsigned long rate_mask = 0;
        u32 rate_idx = 0;
 
        memcpy(search_tbl, tbl, sz);
@@ -1667,7 +1636,7 @@ static int rs_switch_to_column(struct iwl_mvm *mvm,
                    !(BIT(rate_idx) & rate_mask)) {
                        IWL_DEBUG_RATE(mvm,
                                       "can not switch with index %d"
-                                      " rate mask %x\n",
+                                      " rate mask %lx\n",
                                       rate_idx, rate_mask);
 
                        goto err;
@@ -1769,6 +1738,203 @@ out:
        return action;
 }
 
+static void rs_get_adjacent_txp(struct iwl_mvm *mvm, int index,
+                               int *weaker, int *stronger)
+{
+       *weaker = index + TPC_TX_POWER_STEP;
+       if (*weaker > TPC_MAX_REDUCTION)
+               *weaker = TPC_INVALID;
+
+       *stronger = index - TPC_TX_POWER_STEP;
+       if (*stronger < 0)
+               *stronger = TPC_INVALID;
+}
+
+static bool rs_tpc_allowed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                          struct rs_rate *rate, enum ieee80211_band band)
+{
+       int index = rate->index;
+       bool cam = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM);
+       bool sta_ps_disabled = (vif->type == NL80211_IFTYPE_STATION &&
+                               !vif->bss_conf.ps);
+
+       IWL_DEBUG_RATE(mvm, "cam: %d sta_ps_disabled %d\n",
+                      cam, sta_ps_disabled);
+       /*
+        * allow tpc only if power management is enabled, or bt coex
+        * activity grade allows it and we are on 2.4Ghz.
+        */
+       if ((cam || sta_ps_disabled) &&
+           !iwl_mvm_bt_coex_is_tpc_allowed(mvm, band))
+               return false;
+
+       IWL_DEBUG_RATE(mvm, "check rate, table type: %d\n", rate->type);
+       if (is_legacy(rate))
+               return index == IWL_RATE_54M_INDEX;
+       if (is_ht(rate))
+               return index == IWL_RATE_MCS_7_INDEX;
+       if (is_vht(rate))
+               return index == IWL_RATE_MCS_7_INDEX ||
+                      index == IWL_RATE_MCS_8_INDEX ||
+                      index == IWL_RATE_MCS_9_INDEX;
+
+       WARN_ON_ONCE(1);
+       return false;
+}
+
+enum tpc_action {
+       TPC_ACTION_STAY,
+       TPC_ACTION_DECREASE,
+       TPC_ACTION_INCREASE,
+       TPC_ACTION_NO_RESTIRCTION,
+};
+
+static enum tpc_action rs_get_tpc_action(struct iwl_mvm *mvm,
+                                        s32 sr, int weak, int strong,
+                                        int current_tpt,
+                                        int weak_tpt, int strong_tpt)
+{
+       /* stay until we have valid tpt */
+       if (current_tpt == IWL_INVALID_VALUE) {
+               IWL_DEBUG_RATE(mvm, "no current tpt. stay.\n");
+               return TPC_ACTION_STAY;
+       }
+
+       /* Too many failures, increase txp */
+       if (sr <= TPC_SR_FORCE_INCREASE || current_tpt == 0) {
+               IWL_DEBUG_RATE(mvm, "increase txp because of weak SR\n");
+               return TPC_ACTION_NO_RESTIRCTION;
+       }
+
+       /* try decreasing first if applicable */
+       if (weak != TPC_INVALID) {
+               if (weak_tpt == IWL_INVALID_VALUE &&
+                   (strong_tpt == IWL_INVALID_VALUE ||
+                    current_tpt >= strong_tpt)) {
+                       IWL_DEBUG_RATE(mvm,
+                                      "no weak txp measurement. decrease txp\n");
+                       return TPC_ACTION_DECREASE;
+               }
+
+               if (weak_tpt > current_tpt) {
+                       IWL_DEBUG_RATE(mvm,
+                                      "lower txp has better tpt. decrease txp\n");
+                       return TPC_ACTION_DECREASE;
+               }
+       }
+
+       /* next, increase if needed */
+       if (sr < TPC_SR_NO_INCREASE && strong != TPC_INVALID) {
+               if (weak_tpt == IWL_INVALID_VALUE &&
+                   strong_tpt != IWL_INVALID_VALUE &&
+                   current_tpt < strong_tpt) {
+                       IWL_DEBUG_RATE(mvm,
+                                      "higher txp has better tpt. increase txp\n");
+                       return TPC_ACTION_INCREASE;
+               }
+
+               if (weak_tpt < current_tpt &&
+                   (strong_tpt == IWL_INVALID_VALUE ||
+                    strong_tpt > current_tpt)) {
+                       IWL_DEBUG_RATE(mvm,
+                                      "lower txp has worse tpt. increase txp\n");
+                       return TPC_ACTION_INCREASE;
+               }
+       }
+
+       IWL_DEBUG_RATE(mvm, "no need to increase or decrease txp - stay\n");
+       return TPC_ACTION_STAY;
+}
+
+static bool rs_tpc_perform(struct iwl_mvm *mvm,
+                          struct ieee80211_sta *sta,
+                          struct iwl_lq_sta *lq_sta,
+                          struct iwl_scale_tbl_info *tbl)
+{
+       struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+       struct ieee80211_vif *vif = mvm_sta->vif;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       enum ieee80211_band band;
+       struct iwl_rate_scale_data *window;
+       struct rs_rate *rate = &tbl->rate;
+       enum tpc_action action;
+       s32 sr;
+       u8 cur = lq_sta->lq.reduced_tpc;
+       int current_tpt;
+       int weak, strong;
+       int weak_tpt = IWL_INVALID_VALUE, strong_tpt = IWL_INVALID_VALUE;
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+       if (lq_sta->dbg_fixed_txp_reduction <= TPC_MAX_REDUCTION) {
+               IWL_DEBUG_RATE(mvm, "fixed tpc: %d\n",
+                              lq_sta->dbg_fixed_txp_reduction);
+               lq_sta->lq.reduced_tpc = lq_sta->dbg_fixed_txp_reduction;
+               return cur != lq_sta->dbg_fixed_txp_reduction;
+       }
+#endif
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+       if (WARN_ON(!chanctx_conf))
+               band = IEEE80211_NUM_BANDS;
+       else
+               band = chanctx_conf->def.chan->band;
+       rcu_read_unlock();
+
+       if (!rs_tpc_allowed(mvm, vif, rate, band)) {
+               IWL_DEBUG_RATE(mvm,
+                              "tpc is not allowed. remove txp restrictions\n");
+               lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION;
+               return cur != TPC_NO_REDUCTION;
+       }
+
+       rs_get_adjacent_txp(mvm, cur, &weak, &strong);
+
+       /* Collect measured throughputs for current and adjacent rates */
+       window = tbl->tpc_win;
+       sr = window[cur].success_ratio;
+       current_tpt = window[cur].average_tpt;
+       if (weak != TPC_INVALID)
+               weak_tpt = window[weak].average_tpt;
+       if (strong != TPC_INVALID)
+               strong_tpt = window[strong].average_tpt;
+
+       IWL_DEBUG_RATE(mvm,
+                      "(TPC: %d): cur_tpt %d SR %d weak %d strong %d weak_tpt %d strong_tpt %d\n",
+                      cur, current_tpt, sr, weak, strong,
+                      weak_tpt, strong_tpt);
+
+       action = rs_get_tpc_action(mvm, sr, weak, strong,
+                                  current_tpt, weak_tpt, strong_tpt);
+
+       /* override actions if we are on the edge */
+       if (weak == TPC_INVALID && action == TPC_ACTION_DECREASE) {
+               IWL_DEBUG_RATE(mvm, "already in lowest txp, stay\n");
+               action = TPC_ACTION_STAY;
+       } else if (strong == TPC_INVALID &&
+                  (action == TPC_ACTION_INCREASE ||
+                   action == TPC_ACTION_NO_RESTIRCTION)) {
+               IWL_DEBUG_RATE(mvm, "already in highest txp, stay\n");
+               action = TPC_ACTION_STAY;
+       }
+
+       switch (action) {
+       case TPC_ACTION_DECREASE:
+               lq_sta->lq.reduced_tpc = weak;
+               return true;
+       case TPC_ACTION_INCREASE:
+               lq_sta->lq.reduced_tpc = strong;
+               return true;
+       case TPC_ACTION_NO_RESTIRCTION:
+               lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION;
+               return true;
+       case TPC_ACTION_STAY:
+               /* do nothing */
+               break;
+       }
+       return false;
+}
+
 /*
  * Do rate scaling and search for new modulation mode.
  */
@@ -2019,6 +2185,9 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                break;
        case RS_ACTION_STAY:
                /* No change */
+               if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN)
+                       update_lq = rs_tpc_perform(mvm, sta, lq_sta, tbl);
+               break;
        default:
                break;
        }
@@ -2271,10 +2440,6 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta,
                        if (i == IWL_RATE_9M_INDEX)
                                continue;
 
-                       /* Disable MCS9 as a workaround */
-                       if (i == IWL_RATE_MCS_9_INDEX)
-                               continue;
-
                        /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */
                        if (i == IWL_RATE_MCS_9_INDEX &&
                            sta->bandwidth == IEEE80211_STA_RX_BW_20)
@@ -2293,10 +2458,6 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta,
                        if (i == IWL_RATE_9M_INDEX)
                                continue;
 
-                       /* Disable MCS9 as a workaround */
-                       if (i == IWL_RATE_MCS_9_INDEX)
-                               continue;
-
                        /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */
                        if (i == IWL_RATE_MCS_9_INDEX &&
                            sta->bandwidth == IEEE80211_STA_RX_BW_20)
@@ -2478,6 +2639,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
        lq_sta->is_agg = 0;
 #ifdef CONFIG_MAC80211_DEBUGFS
        lq_sta->dbg_fixed_rate = 0;
+       lq_sta->dbg_fixed_txp_reduction = TPC_INVALID;
 #endif
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        iwl_mvm_reset_frame_stats(mvm, &mvm->drv_rx_stats);
@@ -2653,6 +2815,7 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
                rs_build_rates_table_from_fixed(mvm, lq_cmd,
                                                lq_sta->band,
                                                lq_sta->dbg_fixed_rate);
+               lq_cmd->reduced_tpc = 0;
                ant = (lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >>
                        RATE_MCS_ANT_POS;
        } else
@@ -2783,7 +2946,6 @@ static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file,
        size_t buf_size;
        u32 parsed_rate;
 
-
        mvm = lq_sta->drv;
        memset(buf, 0, sizeof(buf));
        buf_size = min(count, sizeof(buf) -  1);
@@ -2856,6 +3018,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
                        lq_sta->lq.agg_disable_start_th,
                        lq_sta->lq.agg_frame_cnt_limit);
 
+       desc += sprintf(buff+desc, "reduced tpc=%d\n", lq_sta->lq.reduced_tpc);
        desc += sprintf(buff+desc,
                        "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n",
                        lq_sta->lq.initial_rate_index[0],
@@ -2928,6 +3091,94 @@ static const struct file_operations rs_sta_dbgfs_stats_table_ops = {
        .llseek = default_llseek,
 };
 
+static ssize_t rs_sta_dbgfs_drv_tx_stats_read(struct file *file,
+                                             char __user *user_buf,
+                                             size_t count, loff_t *ppos)
+{
+       static const char * const column_name[] = {
+               [RS_COLUMN_LEGACY_ANT_A] = "LEGACY_ANT_A",
+               [RS_COLUMN_LEGACY_ANT_B] = "LEGACY_ANT_B",
+               [RS_COLUMN_SISO_ANT_A] = "SISO_ANT_A",
+               [RS_COLUMN_SISO_ANT_B] = "SISO_ANT_B",
+               [RS_COLUMN_SISO_ANT_A_SGI] = "SISO_ANT_A_SGI",
+               [RS_COLUMN_SISO_ANT_B_SGI] = "SISO_ANT_B_SGI",
+               [RS_COLUMN_MIMO2] = "MIMO2",
+               [RS_COLUMN_MIMO2_SGI] = "MIMO2_SGI",
+       };
+
+       static const char * const rate_name[] = {
+               [IWL_RATE_1M_INDEX] = "1M",
+               [IWL_RATE_2M_INDEX] = "2M",
+               [IWL_RATE_5M_INDEX] = "5.5M",
+               [IWL_RATE_11M_INDEX] = "11M",
+               [IWL_RATE_6M_INDEX] = "6M|MCS0",
+               [IWL_RATE_9M_INDEX] = "9M",
+               [IWL_RATE_12M_INDEX] = "12M|MCS1",
+               [IWL_RATE_18M_INDEX] = "18M|MCS2",
+               [IWL_RATE_24M_INDEX] = "24M|MCS3",
+               [IWL_RATE_36M_INDEX] = "36M|MCS4",
+               [IWL_RATE_48M_INDEX] = "48M|MCS5",
+               [IWL_RATE_54M_INDEX] = "54M|MCS6",
+               [IWL_RATE_MCS_7_INDEX] = "MCS7",
+               [IWL_RATE_MCS_8_INDEX] = "MCS8",
+               [IWL_RATE_MCS_9_INDEX] = "MCS9",
+       };
+
+       char *buff, *pos, *endpos;
+       int col, rate;
+       ssize_t ret;
+       struct iwl_lq_sta *lq_sta = file->private_data;
+       struct rs_rate_stats *stats;
+       static const size_t bufsz = 1024;
+
+       buff = kmalloc(bufsz, GFP_KERNEL);
+       if (!buff)
+               return -ENOMEM;
+
+       pos = buff;
+       endpos = pos + bufsz;
+
+       pos += scnprintf(pos, endpos - pos, "COLUMN,");
+       for (rate = 0; rate < IWL_RATE_COUNT; rate++)
+               pos += scnprintf(pos, endpos - pos, "%s,", rate_name[rate]);
+       pos += scnprintf(pos, endpos - pos, "\n");
+
+       for (col = 0; col < RS_COLUMN_COUNT; col++) {
+               pos += scnprintf(pos, endpos - pos,
+                                "%s,", column_name[col]);
+
+               for (rate = 0; rate < IWL_RATE_COUNT; rate++) {
+                       stats = &(lq_sta->tx_stats[col][rate]);
+                       pos += scnprintf(pos, endpos - pos,
+                                        "%llu/%llu,",
+                                        stats->success,
+                                        stats->total);
+               }
+               pos += scnprintf(pos, endpos - pos, "\n");
+       }
+
+       ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff);
+       kfree(buff);
+       return ret;
+}
+
+static ssize_t rs_sta_dbgfs_drv_tx_stats_write(struct file *file,
+                                              const char __user *user_buf,
+                                              size_t count, loff_t *ppos)
+{
+       struct iwl_lq_sta *lq_sta = file->private_data;
+       memset(lq_sta->tx_stats, 0, sizeof(lq_sta->tx_stats));
+
+       return count;
+}
+
+static const struct file_operations rs_sta_dbgfs_drv_tx_stats_ops = {
+       .read = rs_sta_dbgfs_drv_tx_stats_read,
+       .write = rs_sta_dbgfs_drv_tx_stats_write,
+       .open = simple_open,
+       .llseek = default_llseek,
+};
+
 static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir)
 {
        struct iwl_lq_sta *lq_sta = mvm_sta;
@@ -2937,9 +3188,15 @@ static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir)
        lq_sta->rs_sta_dbgfs_stats_table_file =
                debugfs_create_file("rate_stats_table", S_IRUSR, dir,
                                    lq_sta, &rs_sta_dbgfs_stats_table_ops);
+       lq_sta->rs_sta_dbgfs_drv_tx_stats_file =
+               debugfs_create_file("drv_tx_stats", S_IRUSR | S_IWUSR, dir,
+                                   lq_sta, &rs_sta_dbgfs_drv_tx_stats_ops);
        lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file =
                debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir,
                                  &lq_sta->tx_agg_tid_en);
+       lq_sta->rs_sta_dbgfs_reduced_txp_file =
+               debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir,
+                                 &lq_sta->dbg_fixed_txp_reduction);
 }
 
 static void rs_remove_debugfs(void *mvm, void *mvm_sta)
@@ -2947,7 +3204,9 @@ static void rs_remove_debugfs(void *mvm, void *mvm_sta)
        struct iwl_lq_sta *lq_sta = mvm_sta;
        debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file);
        debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file);
+       debugfs_remove(lq_sta->rs_sta_dbgfs_drv_tx_stats_file);
        debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file);
+       debugfs_remove(lq_sta->rs_sta_dbgfs_reduced_txp_file);
 }
 #endif
 
index 0acfac96a56c6dca2d2799812231404921abd15b..374a83d7db25a98dd76da34d3fdff9557e48664f 100644 (file)
@@ -158,6 +158,13 @@ enum {
 #define RS_SR_FORCE_DECREASE           1920    /*  15% */
 #define RS_SR_NO_DECREASE              10880   /*  85% */
 
+#define TPC_SR_FORCE_INCREASE          9600    /* 75% */
+#define TPC_SR_NO_INCREASE             10880   /* 85% */
+#define TPC_TX_POWER_STEP              3
+#define TPC_MAX_REDUCTION              15
+#define TPC_NO_REDUCTION               0
+#define TPC_INVALID                    0xff
+
 #define LINK_QUAL_AGG_TIME_LIMIT_DEF   (4000) /* 4 milliseconds */
 #define LINK_QUAL_AGG_TIME_LIMIT_MAX   (8000)
 #define LINK_QUAL_AGG_TIME_LIMIT_MIN   (100)
@@ -266,9 +273,16 @@ enum rs_column {
        RS_COLUMN_MIMO2_SGI,
 
        RS_COLUMN_LAST = RS_COLUMN_MIMO2_SGI,
+       RS_COLUMN_COUNT = RS_COLUMN_LAST + 1,
        RS_COLUMN_INVALID,
 };
 
+/* Packet stats per rate */
+struct rs_rate_stats {
+       u64 success;
+       u64 total;
+};
+
 /**
  * struct iwl_scale_tbl_info -- tx params and success history for all rates
  *
@@ -280,6 +294,8 @@ struct iwl_scale_tbl_info {
        enum rs_column column;
        const u16 *expected_tpt;        /* throughput metrics; expected_tpt_G, etc. */
        struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
+       /* per txpower-reduction history */
+       struct iwl_rate_scale_data tpc_win[TPC_MAX_REDUCTION + 1];
 };
 
 enum {
@@ -315,6 +331,8 @@ struct iwl_lq_sta {
        bool is_vht;
        enum ieee80211_band band;
 
+       struct rs_rate_stats tx_stats[RS_COLUMN_COUNT][IWL_RATE_COUNT];
+
        /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
        unsigned long active_legacy_rate;
        unsigned long active_siso_rate;
@@ -334,8 +352,11 @@ struct iwl_lq_sta {
 #ifdef CONFIG_MAC80211_DEBUGFS
        struct dentry *rs_sta_dbgfs_scale_table_file;
        struct dentry *rs_sta_dbgfs_stats_table_file;
+       struct dentry *rs_sta_dbgfs_drv_tx_stats_file;
        struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file;
+       struct dentry *rs_sta_dbgfs_reduced_txp_file;
        u32 dbg_fixed_rate;
+       u8 dbg_fixed_txp_reduction;
 #endif
        struct iwl_mvm *drv;
 
@@ -345,6 +366,9 @@ struct iwl_lq_sta {
        u32 last_rate_n_flags;
        /* packets destined for this STA are aggregated */
        u8 is_agg;
+
+       /* tx power reduce for this sta */
+       int tpc_reduce;
 };
 
 /* Initialize station's rate scaling information after adding station */
index 6061553a5e444956c7b5d626695a2950fb1f3fd1..cf7276967acdec6439392c82e44e94de52d453c8 100644 (file)
@@ -60,7 +60,6 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 #include "iwl-trans.h"
-
 #include "mvm.h"
 #include "fw-api.h"
 
@@ -130,42 +129,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
 
        memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
 
-       ieee80211_rx_ni(mvm->hw, skb);
-}
-
-static void iwl_mvm_calc_rssi(struct iwl_mvm *mvm,
-                             struct iwl_rx_phy_info *phy_info,
-                             struct ieee80211_rx_status *rx_status)
-{
-       int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm;
-       u32 agc_a, agc_b;
-       u32 val;
-
-       val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_AGC_IDX]);
-       agc_a = (val & IWL_OFDM_AGC_A_MSK) >> IWL_OFDM_AGC_A_POS;
-       agc_b = (val & IWL_OFDM_AGC_B_MSK) >> IWL_OFDM_AGC_B_POS;
-
-       val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_RSSI_AB_IDX]);
-       rssi_a = (val & IWL_OFDM_RSSI_INBAND_A_MSK) >> IWL_OFDM_RSSI_A_POS;
-       rssi_b = (val & IWL_OFDM_RSSI_INBAND_B_MSK) >> IWL_OFDM_RSSI_B_POS;
-
-       /*
-        * dBm = rssi dB - agc dB - constant.
-        * Higher AGC (higher radio gain) means lower signal.
-        */
-       rssi_a_dbm = rssi_a - IWL_RSSI_OFFSET - agc_a;
-       rssi_b_dbm = rssi_b - IWL_RSSI_OFFSET - agc_b;
-       max_rssi_dbm = max_t(int, rssi_a_dbm, rssi_b_dbm);
-
-       IWL_DEBUG_STATS(mvm, "Rssi In A %d B %d Max %d AGCA %d AGCB %d\n",
-                       rssi_a_dbm, rssi_b_dbm, max_rssi_dbm, agc_a, agc_b);
-
-       rx_status->signal = max_rssi_dbm;
-       rx_status->chains = (le16_to_cpu(phy_info->phy_flags) &
-                               RX_RES_PHY_FLAGS_ANTENNA)
-                                       >> RX_RES_PHY_FLAGS_ANTENNA_POS;
-       rx_status->chain_signal[0] = rssi_a_dbm;
-       rx_status->chain_signal[1] = rssi_b_dbm;
+       ieee80211_rx(mvm->hw, skb);
 }
 
 /*
@@ -337,10 +301,7 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
         */
        /*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/
 
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_RX_ENERGY_API)
-               iwl_mvm_get_signal_strength(mvm, phy_info, &rx_status);
-       else
-               iwl_mvm_calc_rssi(mvm, phy_info, &rx_status);
+       iwl_mvm_get_signal_strength(mvm, phy_info, &rx_status);
 
        IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status.signal,
                              (unsigned long long)rx_status.mactime);
@@ -394,6 +355,8 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
                rx_status.rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
                rx_status.flag |= RX_FLAG_VHT;
                rx_status.flag |= stbc << RX_FLAG_STBC_SHIFT;
+               if (rate_n_flags & RATE_MCS_BF_MSK)
+                       rx_status.vht_flag |= RX_VHT_FLAG_BF;
        } else {
                rx_status.rate_idx =
                        iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
index c28de54c75d400d551a0be87d4501cc98fb5967a..4b6c7d4bd199ef4dbc20defd15f966e59545d6e3 100644 (file)
@@ -306,7 +306,6 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
                .id = SCAN_REQUEST_CMD,
                .len = { 0, },
                .data = { mvm->scan_cmd, },
-               .flags = CMD_SYNC,
                .dataflags = { IWL_HCMD_DFL_NOCOPY, },
        };
        struct iwl_scan_cmd *cmd = mvm->scan_cmd;
@@ -319,7 +318,10 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
        struct iwl_mvm_scan_params params = {};
 
        lockdep_assert_held(&mvm->mutex);
-       BUG_ON(mvm->scan_cmd == NULL);
+
+       /* we should have failed registration if scan_cmd was NULL */
+       if (WARN_ON(mvm->scan_cmd == NULL))
+               return -ENOMEM;
 
        IWL_DEBUG_SCAN(mvm, "Handling mac80211 scan request\n");
        mvm->scan_status = IWL_MVM_SCAN_OS;
@@ -514,7 +516,7 @@ int iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
                                   ARRAY_SIZE(scan_abort_notif),
                                   iwl_mvm_scan_abort_notif, NULL);
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD, CMD_SYNC, 0, NULL);
+       ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD, 0, 0, NULL);
        if (ret) {
                IWL_ERR(mvm, "Couldn't send SCAN_ABORT_CMD: %d\n", ret);
                /* mac80211's state will be cleaned in the nic_restart flow */
@@ -538,9 +540,13 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
        /* scan status must be locked for proper checking */
        lockdep_assert_held(&mvm->mutex);
 
-       IWL_DEBUG_SCAN(mvm, "Scheduled scan completed, status %s\n",
+       IWL_DEBUG_SCAN(mvm,
+                      "Scheduled scan completed, status %s EBS status %s:%d\n",
                       scan_notif->status == IWL_SCAN_OFFLOAD_COMPLETED ?
-                      "completed" : "aborted");
+                      "completed" : "aborted", scan_notif->ebs_status ==
+                      IWL_SCAN_EBS_SUCCESS ? "success" : "failed",
+                      scan_notif->ebs_status);
+
 
        /* only call mac80211 completion if the stop was initiated by FW */
        if (mvm->scan_status == IWL_MVM_SCAN_SCHED) {
@@ -548,6 +554,8 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
                ieee80211_sched_scan_stopped(mvm->hw);
        }
 
+       mvm->last_ebs_successful = !scan_notif->ebs_status;
+
        return 0;
 }
 
@@ -740,7 +748,6 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
        struct iwl_scan_offload_cfg *scan_cfg;
        struct iwl_host_cmd cmd = {
                .id = SCAN_OFFLOAD_CONFIG_CMD,
-               .flags = CMD_SYNC,
        };
        struct iwl_mvm_scan_params params = {};
 
@@ -798,7 +805,6 @@ int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm,
        struct iwl_scan_offload_blacklist *blacklist;
        struct iwl_host_cmd cmd = {
                .id = SCAN_OFFLOAD_UPDATE_PROFILES_CMD,
-               .flags = CMD_SYNC,
                .len[1] = sizeof(*profile_cfg),
                .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
                .dataflags[1] = IWL_HCMD_DFL_NOCOPY,
@@ -884,7 +890,12 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
                scan_req.flags |= cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_PASS_ALL);
        }
 
-       return iwl_mvm_send_cmd_pdu(mvm, SCAN_OFFLOAD_REQUEST_CMD, CMD_SYNC,
+       if (mvm->last_ebs_successful &&
+           mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT)
+               scan_req.flags |=
+                       cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_EBS_ACCURATE_MODE);
+
+       return iwl_mvm_send_cmd_pdu(mvm, SCAN_OFFLOAD_REQUEST_CMD, 0,
                                    sizeof(scan_req), &scan_req);
 }
 
@@ -893,7 +904,6 @@ static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm)
        int ret;
        struct iwl_host_cmd cmd = {
                .id = SCAN_OFFLOAD_ABORT_CMD,
-               .flags = CMD_SYNC,
        };
        u32 status;
 
@@ -922,7 +932,7 @@ static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm)
        return ret;
 }
 
-int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm)
+int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify)
 {
        int ret;
        struct iwl_notification_wait wait_scan_done;
@@ -960,5 +970,8 @@ int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm)
         */
        mvm->scan_status = IWL_MVM_SCAN_NONE;
 
+       if (notify)
+               ieee80211_sched_scan_stopped(mvm->hw);
+
        return 0;
 }
index 88809b2d165445fcf9188c8f91bcf755a9e6704f..7edfd15efc9d001f227ea2c35b046c0f47cb55af 100644 (file)
@@ -237,9 +237,6 @@ int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif,
                .sta_vif_ap_sta_id = IWL_MVM_STATION_COUNT,
        };
 
-       if (IWL_UCODE_API(mvm->fw->ucode_ver) < 8)
-               return 0;
-
        /*
         * Ignore the call if we are in HW Restart flow, or if the handled
         * vif is a p2p device.
index f339ef8842508774e2ff7d51c9bdce069e014a1b..1fb01ea2e7047201324faefdc1f181408a77d070 100644 (file)
 #include "sta.h"
 #include "rs.h"
 
-static void iwl_mvm_add_sta_cmd_v7_to_v5(struct iwl_mvm_add_sta_cmd_v7 *cmd_v7,
-                                        struct iwl_mvm_add_sta_cmd_v5 *cmd_v5)
-{
-       memset(cmd_v5, 0, sizeof(*cmd_v5));
-
-       cmd_v5->add_modify = cmd_v7->add_modify;
-       cmd_v5->tid_disable_tx = cmd_v7->tid_disable_tx;
-       cmd_v5->mac_id_n_color = cmd_v7->mac_id_n_color;
-       memcpy(cmd_v5->addr, cmd_v7->addr, ETH_ALEN);
-       cmd_v5->sta_id = cmd_v7->sta_id;
-       cmd_v5->modify_mask = cmd_v7->modify_mask;
-       cmd_v5->station_flags = cmd_v7->station_flags;
-       cmd_v5->station_flags_msk = cmd_v7->station_flags_msk;
-       cmd_v5->add_immediate_ba_tid = cmd_v7->add_immediate_ba_tid;
-       cmd_v5->remove_immediate_ba_tid = cmd_v7->remove_immediate_ba_tid;
-       cmd_v5->add_immediate_ba_ssn = cmd_v7->add_immediate_ba_ssn;
-       cmd_v5->sleep_tx_count = cmd_v7->sleep_tx_count;
-       cmd_v5->sleep_state_flags = cmd_v7->sleep_state_flags;
-       cmd_v5->assoc_id = cmd_v7->assoc_id;
-       cmd_v5->beamform_flags = cmd_v7->beamform_flags;
-       cmd_v5->tfd_queue_msk = cmd_v7->tfd_queue_msk;
-}
-
-static void
-iwl_mvm_add_sta_key_to_add_sta_cmd_v5(struct iwl_mvm_add_sta_key_cmd *key_cmd,
-                                     struct iwl_mvm_add_sta_cmd_v5 *sta_cmd,
-                                     u32 mac_id_n_color)
-{
-       memset(sta_cmd, 0, sizeof(*sta_cmd));
-
-       sta_cmd->sta_id = key_cmd->sta_id;
-       sta_cmd->add_modify = STA_MODE_MODIFY;
-       sta_cmd->modify_mask = STA_MODIFY_KEY;
-       sta_cmd->mac_id_n_color = cpu_to_le32(mac_id_n_color);
-
-       sta_cmd->key.key_offset = key_cmd->key_offset;
-       sta_cmd->key.key_flags = key_cmd->key_flags;
-       memcpy(sta_cmd->key.key, key_cmd->key, sizeof(sta_cmd->key.key));
-       sta_cmd->key.tkip_rx_tsc_byte2 = key_cmd->tkip_rx_tsc_byte2;
-       memcpy(sta_cmd->key.tkip_rx_ttak, key_cmd->tkip_rx_ttak,
-              sizeof(sta_cmd->key.tkip_rx_ttak));
-}
-
-static int iwl_mvm_send_add_sta_cmd_status(struct iwl_mvm *mvm,
-                                          struct iwl_mvm_add_sta_cmd_v7 *cmd,
-                                          int *status)
-{
-       struct iwl_mvm_add_sta_cmd_v5 cmd_v5;
-
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_STA_KEY_CMD)
-               return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(*cmd),
-                                                  cmd, status);
-
-       iwl_mvm_add_sta_cmd_v7_to_v5(cmd, &cmd_v5);
-
-       return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd_v5),
-                                          &cmd_v5, status);
-}
-
-static int iwl_mvm_send_add_sta_cmd(struct iwl_mvm *mvm, u32 flags,
-                                   struct iwl_mvm_add_sta_cmd_v7 *cmd)
-{
-       struct iwl_mvm_add_sta_cmd_v5 cmd_v5;
-
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_STA_KEY_CMD)
-               return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags,
-                                           sizeof(*cmd), cmd);
-
-       iwl_mvm_add_sta_cmd_v7_to_v5(cmd, &cmd_v5);
-
-       return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags, sizeof(cmd_v5),
-                                   &cmd_v5);
-}
-
-static int
-iwl_mvm_send_add_sta_key_cmd_status(struct iwl_mvm *mvm,
-                                   struct iwl_mvm_add_sta_key_cmd *cmd,
-                                   u32 mac_id_n_color,
-                                   int *status)
-{
-       struct iwl_mvm_add_sta_cmd_v5 sta_cmd;
-
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_STA_KEY_CMD)
-               return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY,
-                                                  sizeof(*cmd), cmd, status);
-
-       iwl_mvm_add_sta_key_to_add_sta_cmd_v5(cmd, &sta_cmd, mac_id_n_color);
-
-       return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(sta_cmd),
-                                          &sta_cmd, status);
-}
-
-static int iwl_mvm_send_add_sta_key_cmd(struct iwl_mvm *mvm,
-                                       u32 flags,
-                                       struct iwl_mvm_add_sta_key_cmd *cmd,
-                                       u32 mac_id_n_color)
-{
-       struct iwl_mvm_add_sta_cmd_v5 sta_cmd;
-
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_STA_KEY_CMD)
-               return iwl_mvm_send_cmd_pdu(mvm, ADD_STA_KEY, flags,
-                                           sizeof(*cmd), cmd);
-
-       iwl_mvm_add_sta_key_to_add_sta_cmd_v5(cmd, &sta_cmd, mac_id_n_color);
-
-       return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags, sizeof(sta_cmd),
-                                   &sta_cmd);
-}
-
 static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm,
                                    enum nl80211_iftype iftype)
 {
@@ -207,7 +98,7 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                           bool update)
 {
        struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
-       struct iwl_mvm_add_sta_cmd_v7 add_sta_cmd;
+       struct iwl_mvm_add_sta_cmd add_sta_cmd;
        int ret;
        u32 status;
        u32 agg_size = 0, mpdu_dens = 0;
@@ -295,7 +186,8 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                cpu_to_le32(mpdu_dens << STA_FLG_AGG_MPDU_DENS_SHIFT);
 
        status = ADD_STA_SUCCESS;
-       ret = iwl_mvm_send_add_sta_cmd_status(mvm, &add_sta_cmd, &status);
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(add_sta_cmd),
+                                         &add_sta_cmd, &status);
        if (ret)
                return ret;
 
@@ -380,7 +272,7 @@ int iwl_mvm_update_sta(struct iwl_mvm *mvm,
 int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
                      bool drain)
 {
-       struct iwl_mvm_add_sta_cmd_v7 cmd = {};
+       struct iwl_mvm_add_sta_cmd cmd = {};
        int ret;
        u32 status;
 
@@ -393,7 +285,8 @@ int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
        cmd.station_flags_msk = cpu_to_le32(STA_FLG_DRAIN_FLOW);
 
        status = ADD_STA_SUCCESS;
-       ret = iwl_mvm_send_add_sta_cmd_status(mvm, &cmd, &status);
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+                                         &cmd, &status);
        if (ret)
                return ret;
 
@@ -434,7 +327,7 @@ static int iwl_mvm_rm_sta_common(struct iwl_mvm *mvm, u8 sta_id)
                return -EINVAL;
        }
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, REMOVE_STA, CMD_SYNC,
+       ret = iwl_mvm_send_cmd_pdu(mvm, REMOVE_STA, 0,
                                   sizeof(rm_sta_cmd), &rm_sta_cmd);
        if (ret) {
                IWL_ERR(mvm, "Failed to remove station. Id=%d\n", sta_id);
@@ -498,7 +391,7 @@ void iwl_mvm_sta_drained_wk(struct work_struct *wk)
                                sta_id);
                        continue;
                }
-               rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], NULL);
+               RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL);
                clear_bit(sta_id, mvm->sta_drained);
        }
 
@@ -520,14 +413,6 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
                /* flush its queues here since we are freeing mvm_sta */
                ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true);
 
-               /*
-                * Put a non-NULL since the fw station isn't removed.
-                * It will be removed after the MAC will be set as
-                * unassoc.
-                */
-               rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
-                                  ERR_PTR(-EINVAL));
-
                /* if we are associated - we can't remove the AP STA now */
                if (vif->bss_conf.assoc)
                        return ret;
@@ -557,7 +442,7 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
        } else {
                spin_unlock_bh(&mvm_sta->lock);
                ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id);
-               rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL);
+               RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL);
        }
 
        return ret;
@@ -571,7 +456,7 @@ int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm,
 
        lockdep_assert_held(&mvm->mutex);
 
-       rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], NULL);
+       RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL);
        return ret;
 }
 
@@ -593,7 +478,7 @@ int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta,
 
 void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta)
 {
-       rcu_assign_pointer(mvm->fw_id_to_mac_id[sta->sta_id], NULL);
+       RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta->sta_id], NULL);
        memset(sta, 0, sizeof(struct iwl_mvm_int_sta));
        sta->sta_id = IWL_MVM_STATION_COUNT;
 }
@@ -603,13 +488,13 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm,
                                      const u8 *addr,
                                      u16 mac_id, u16 color)
 {
-       struct iwl_mvm_add_sta_cmd_v7 cmd;
+       struct iwl_mvm_add_sta_cmd cmd;
        int ret;
        u32 status;
 
        lockdep_assert_held(&mvm->mutex);
 
-       memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd_v7));
+       memset(&cmd, 0, sizeof(cmd));
        cmd.sta_id = sta->sta_id;
        cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id,
                                                             color));
@@ -619,7 +504,8 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm,
        if (addr)
                memcpy(cmd.addr, addr, ETH_ALEN);
 
-       ret = iwl_mvm_send_add_sta_cmd_status(mvm, &cmd, &status);
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+                                         &cmd, &status);
        if (ret)
                return ret;
 
@@ -753,7 +639,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                       int tid, u16 ssn, bool start)
 {
        struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
-       struct iwl_mvm_add_sta_cmd_v7 cmd = {};
+       struct iwl_mvm_add_sta_cmd cmd = {};
        int ret;
        u32 status;
 
@@ -777,7 +663,8 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                                  STA_MODIFY_REMOVE_BA_TID;
 
        status = ADD_STA_SUCCESS;
-       ret = iwl_mvm_send_add_sta_cmd_status(mvm, &cmd, &status);
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+                                         &cmd, &status);
        if (ret)
                return ret;
 
@@ -812,7 +699,7 @@ static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                              int tid, u8 queue, bool start)
 {
        struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
-       struct iwl_mvm_add_sta_cmd_v7 cmd = {};
+       struct iwl_mvm_add_sta_cmd cmd = {};
        int ret;
        u32 status;
 
@@ -834,7 +721,8 @@ static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
        cmd.tid_disable_tx = cpu_to_le16(mvm_sta->tid_disable_agg);
 
        status = ADD_STA_SUCCESS;
-       ret = iwl_mvm_send_add_sta_cmd_status(mvm, &cmd, &status);
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+                                         &cmd, &status);
        if (ret)
                return ret;
 
@@ -1129,12 +1017,11 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
                                u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k,
                                u32 cmd_flags)
 {
-       __le16 key_flags;
        struct iwl_mvm_add_sta_key_cmd cmd = {};
+       __le16 key_flags;
        int ret, status;
        u16 keyidx;
        int i;
-       u32 mac_id_n_color = mvm_sta->mac_id_n_color;
 
        keyidx = (keyconf->keyidx << STA_KEY_FLG_KEYID_POS) &
                 STA_KEY_FLG_KEYID_MSK;
@@ -1166,13 +1053,12 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
        cmd.sta_id = sta_id;
 
        status = ADD_STA_SUCCESS;
-       if (cmd_flags == CMD_SYNC)
-               ret = iwl_mvm_send_add_sta_key_cmd_status(mvm, &cmd,
-                                                         mac_id_n_color,
-                                                         &status);
+       if (cmd_flags & CMD_ASYNC)
+               ret =  iwl_mvm_send_cmd_pdu(mvm, ADD_STA_KEY, CMD_ASYNC,
+                                           sizeof(cmd), &cmd);
        else
-               ret = iwl_mvm_send_add_sta_key_cmd(mvm, CMD_ASYNC, &cmd,
-                                                  mac_id_n_color);
+               ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd),
+                                                 &cmd, &status);
 
        switch (status) {
        case ADD_STA_SUCCESS:
@@ -1225,7 +1111,7 @@ static int iwl_mvm_send_sta_igtk(struct iwl_mvm *mvm,
                       remove_key ? "removing" : "installing",
                       igtk_cmd.sta_id);
 
-       return iwl_mvm_send_cmd_pdu(mvm, MGMT_MCAST_KEY, CMD_SYNC,
+       return iwl_mvm_send_cmd_pdu(mvm, MGMT_MCAST_KEY, 0,
                                    sizeof(igtk_cmd), &igtk_cmd);
 }
 
@@ -1312,15 +1198,15 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
                ieee80211_get_key_rx_seq(keyconf, 0, &seq);
                ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
                ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id,
-                                          seq.tkip.iv32, p1k, CMD_SYNC);
+                                          seq.tkip.iv32, p1k, 0);
                break;
        case WLAN_CIPHER_SUITE_CCMP:
                ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id,
-                                          0, NULL, CMD_SYNC);
+                                          0, NULL, 0);
                break;
        default:
                ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf,
-                                          sta_id, 0, NULL, CMD_SYNC);
+                                          sta_id, 0, NULL, 0);
        }
 
        if (ret)
@@ -1399,9 +1285,8 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
        cmd.sta_id = sta_id;
 
        status = ADD_STA_SUCCESS;
-       ret = iwl_mvm_send_add_sta_key_cmd_status(mvm, &cmd,
-                                                 mvm_sta->mac_id_n_color,
-                                                 &status);
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd),
+                                         &cmd, &status);
 
        switch (status) {
        case ADD_STA_SUCCESS:
@@ -1448,7 +1333,7 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
                                struct ieee80211_sta *sta)
 {
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
-       struct iwl_mvm_add_sta_cmd_v7 cmd = {
+       struct iwl_mvm_add_sta_cmd cmd = {
                .add_modify = STA_MODE_MODIFY,
                .sta_id = mvmsta->sta_id,
                .station_flags_msk = cpu_to_le32(STA_FLG_PS),
@@ -1456,7 +1341,7 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
        };
        int ret;
 
-       ret = iwl_mvm_send_add_sta_cmd(mvm, CMD_ASYNC, &cmd);
+       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);
 }
@@ -1468,7 +1353,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
                                       bool agg)
 {
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
-       struct iwl_mvm_add_sta_cmd_v7 cmd = {
+       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,
@@ -1538,7 +1423,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
                cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_UAPSD);
        }
 
-       ret = iwl_mvm_send_add_sta_cmd(mvm, CMD_ASYNC, &cmd);
+       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 2ed84c421481c79dfa1e847b1940ac78da8a9d9c..d98e8a2142b8c6b1e3b9e0568956e0bcaa7a357c 100644 (file)
@@ -253,6 +253,8 @@ enum iwl_mvm_agg_state {
  *     This is basically (last acked packet++).
  * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the
  *     Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA).
+ * @reduced_tpc: Reduced tx power. Holds the data between the
+ *     Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA).
  * @state: state of the BA agreement establishment / tear down.
  * @txq_id: Tx queue used by the BA session
  * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or
@@ -265,6 +267,7 @@ struct iwl_mvm_tid_data {
        u16 next_reclaimed;
        /* The rest is Tx AGG related */
        u32 rate_n_flags;
+       u8 reduced_tpc;
        enum iwl_mvm_agg_state state;
        u16 txq_id;
        u16 ssn;
@@ -284,8 +287,6 @@ static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data)
  * @tid_disable_agg: bitmap: if bit(tid) is set, the fw won't send ampdus for
  *     tid.
  * @max_agg_bufsize: the maximal size of the AGG buffer for this station
- * @bt_reduced_txpower_dbg: debug mode in which %bt_reduced_txpower is forced
- *     by debugfs.
  * @bt_reduced_txpower: is reduced tx power enabled for this station
  * @next_status_eosp: the next reclaimed packet is a PS-Poll response and
  *     we need to signal the EOSP
@@ -306,7 +307,6 @@ struct iwl_mvm_sta {
        u32 mac_id_n_color;
        u16 tid_disable_agg;
        u8 max_agg_bufsize;
-       bool bt_reduced_txpower_dbg;
        bool bt_reduced_txpower;
        bool next_status_eosp;
        spinlock_t lock;
index 61331245ad9324f29ec5a86f12a3239725619673..80100f6cc12a85a79fc71bd83cfe2f6962f8f1c0 100644 (file)
@@ -273,67 +273,10 @@ static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait,
        return true;
 }
 
-/* used to convert from time event API v2 to v1 */
-#define TE_V2_DEP_POLICY_MSK (TE_V2_DEP_OTHER | TE_V2_DEP_TSF |\
-                            TE_V2_EVENT_SOCIOPATHIC)
-static inline u16 te_v2_get_notify(__le16 policy)
-{
-       return le16_to_cpu(policy) & TE_V2_NOTIF_MSK;
-}
-
-static inline u16 te_v2_get_dep_policy(__le16 policy)
-{
-       return (le16_to_cpu(policy) & TE_V2_DEP_POLICY_MSK) >>
-               TE_V2_PLACEMENT_POS;
-}
-
-static inline u16 te_v2_get_absence(__le16 policy)
-{
-       return (le16_to_cpu(policy) & TE_V2_ABSENCE) >> TE_V2_ABSENCE_POS;
-}
-
-static void iwl_mvm_te_v2_to_v1(const struct iwl_time_event_cmd_v2 *cmd_v2,
-                               struct iwl_time_event_cmd_v1 *cmd_v1)
-{
-       cmd_v1->id_and_color = cmd_v2->id_and_color;
-       cmd_v1->action = cmd_v2->action;
-       cmd_v1->id = cmd_v2->id;
-       cmd_v1->apply_time = cmd_v2->apply_time;
-       cmd_v1->max_delay = cmd_v2->max_delay;
-       cmd_v1->depends_on = cmd_v2->depends_on;
-       cmd_v1->interval = cmd_v2->interval;
-       cmd_v1->duration = cmd_v2->duration;
-       if (cmd_v2->repeat == TE_V2_REPEAT_ENDLESS)
-               cmd_v1->repeat = cpu_to_le32(TE_V1_REPEAT_ENDLESS);
-       else
-               cmd_v1->repeat = cpu_to_le32(cmd_v2->repeat);
-       cmd_v1->max_frags = cpu_to_le32(cmd_v2->max_frags);
-       cmd_v1->interval_reciprocal = 0; /* unused */
-
-       cmd_v1->dep_policy = cpu_to_le32(te_v2_get_dep_policy(cmd_v2->policy));
-       cmd_v1->is_present = cpu_to_le32(!te_v2_get_absence(cmd_v2->policy));
-       cmd_v1->notify = cpu_to_le32(te_v2_get_notify(cmd_v2->policy));
-}
-
-static int iwl_mvm_send_time_event_cmd(struct iwl_mvm *mvm,
-                                      const struct iwl_time_event_cmd_v2 *cmd)
-{
-       struct iwl_time_event_cmd_v1 cmd_v1;
-
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_TIME_EVENT_API_V2)
-               return iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC,
-                                           sizeof(*cmd), cmd);
-
-       iwl_mvm_te_v2_to_v1(cmd, &cmd_v1);
-       return iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC,
-                                   sizeof(cmd_v1), &cmd_v1);
-}
-
-
 static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm,
                                       struct ieee80211_vif *vif,
                                       struct iwl_mvm_time_event_data *te_data,
-                                      struct iwl_time_event_cmd_v2 *te_cmd)
+                                      struct iwl_time_event_cmd *te_cmd)
 {
        static const u8 time_event_response[] = { TIME_EVENT_CMD };
        struct iwl_notification_wait wait_time_event;
@@ -369,7 +312,8 @@ static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm,
                                   ARRAY_SIZE(time_event_response),
                                   iwl_mvm_time_event_response, te_data);
 
-       ret = iwl_mvm_send_time_event_cmd(mvm, te_cmd);
+       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0,
+                                           sizeof(*te_cmd), te_cmd);
        if (ret) {
                IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret);
                iwl_remove_notification(&mvm->notif_wait, &wait_time_event);
@@ -397,7 +341,7 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm,
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
-       struct iwl_time_event_cmd_v2 time_cmd = {};
+       struct iwl_time_event_cmd time_cmd = {};
 
        lockdep_assert_held(&mvm->mutex);
 
@@ -453,7 +397,7 @@ void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
                               struct iwl_mvm_vif *mvmvif,
                               struct iwl_mvm_time_event_data *te_data)
 {
-       struct iwl_time_event_cmd_v2 time_cmd = {};
+       struct iwl_time_event_cmd time_cmd = {};
        u32 id, uid;
        int ret;
 
@@ -490,7 +434,8 @@ void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
                cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
 
        IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id));
-       ret = iwl_mvm_send_time_event_cmd(mvm, &time_cmd);
+       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0,
+                                  sizeof(time_cmd), &time_cmd);
        if (WARN_ON(ret))
                return;
 }
@@ -510,7 +455,7 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
-       struct iwl_time_event_cmd_v2 time_cmd = {};
+       struct iwl_time_event_cmd time_cmd = {};
 
        lockdep_assert_held(&mvm->mutex);
        if (te_data->running) {
index 7a99fa361954e0bc1d5e9e82bf94130b0692ac6f..868561512783956617f5cae55d294f7c5207918d 100644 (file)
@@ -409,7 +409,6 @@ void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
                .id = REPLY_THERMAL_MNG_BACKOFF,
                .len = { sizeof(u32), },
                .data = { &backoff, },
-               .flags = CMD_SYNC,
        };
 
        backoff = max(backoff, mvm->thermal_throttle.min_backoff);
@@ -468,13 +467,14 @@ void iwl_mvm_tt_handler(struct iwl_mvm *mvm)
        }
 
        if (params->support_tx_backoff) {
-               tx_backoff = 0;
+               tx_backoff = tt->min_backoff;
                for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) {
                        if (temperature < params->tx_backoff[i].temperature)
                                break;
-                       tx_backoff = params->tx_backoff[i].backoff;
+                       tx_backoff = max(tt->min_backoff,
+                                        params->tx_backoff[i].backoff);
                }
-               if (tx_backoff != 0)
+               if (tx_backoff != tt->min_backoff)
                        throttle_enable = true;
                if (tt->tx_backoff != tx_backoff)
                        iwl_mvm_tt_tx_backoff(mvm, tx_backoff);
@@ -484,7 +484,8 @@ void iwl_mvm_tt_handler(struct iwl_mvm *mvm)
                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 &&
+       } else if (tt->throttle && !tt->dynamic_smps &&
+                  tt->tx_backoff == tt->min_backoff &&
                   temperature <= params->tx_protection_exit) {
                IWL_WARN(mvm,
                         "Temperature is back to normal thermal throttling stopped\n");
index 879aeac46cc103112fef914bcc2b38df9f028b06..3846a6c41eb165ffbb8ede0ff102547f36911e65 100644 (file)
@@ -636,7 +636,11 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                        seq_ctl = le16_to_cpu(hdr->seq_ctrl);
                }
 
-               ieee80211_tx_status_ni(mvm->hw, skb);
+               BUILD_BUG_ON(ARRAY_SIZE(info->status.status_driver_data) < 1);
+               info->status.status_driver_data[0] =
+                               (void *)(uintptr_t)tx_resp->reduced_tpc;
+
+               ieee80211_tx_status(mvm->hw, skb);
        }
 
        if (txq_id >= mvm->first_agg_queue) {
@@ -815,6 +819,7 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm,
                struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
                mvmsta->tid_data[tid].rate_n_flags =
                        le32_to_cpu(tx_resp->initial_rate);
+               mvmsta->tid_data[tid].reduced_tpc = tx_resp->reduced_tpc;
        }
 
        rcu_read_unlock();
@@ -928,6 +933,8 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
                        info->status.ampdu_len = ba_notif->txed;
                        iwl_mvm_hwrate_to_tx_status(tid_data->rate_n_flags,
                                                    info);
+                       info->status.status_driver_data[0] =
+                               (void *)(uintptr_t)tid_data->reduced_tpc;
                }
        }
 
@@ -937,7 +944,7 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
 
        while (!skb_queue_empty(&reclaimed_skbs)) {
                skb = __skb_dequeue(&reclaimed_skbs);
-               ieee80211_tx_status_ni(mvm->hw, skb);
+               ieee80211_tx_status(mvm->hw, skb);
        }
 
        return 0;
@@ -951,7 +958,7 @@ int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync)
                .flush_ctl = cpu_to_le16(DUMP_TX_FIFO_FLUSH),
        };
 
-       u32 flags = sync ? CMD_SYNC : CMD_ASYNC;
+       u32 flags = sync ? 0 : CMD_ASYNC;
 
        ret = iwl_mvm_send_cmd_pdu(mvm, TXPATH_FLUSH, flags,
                                   sizeof(flush_cmd), &flush_cmd);
index 2180902266ae6636513346df888c9d68abf0a57e..aa9fc77e8413b607861e370169e0b55ba4b697d1 100644 (file)
@@ -64,6 +64,7 @@
 
 #include "iwl-debug.h"
 #include "iwl-io.h"
+#include "iwl-prph.h"
 
 #include "mvm.h"
 #include "fw-api-rs.h"
@@ -143,7 +144,7 @@ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd,
                      "cmd flags %x", cmd->flags))
                return -EINVAL;
 
-       cmd->flags |= CMD_SYNC | CMD_WANT_SKB;
+       cmd->flags |= CMD_WANT_SKB;
 
        ret = iwl_trans_send_cmd(mvm->trans, cmd);
        if (ret == -ERFKILL) {
@@ -469,6 +470,8 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
                        mvm->status, table.valid);
        }
 
+       /* Do not change this output - scripts rely on it */
+
        IWL_ERR(mvm, "Loaded firmware version: %s\n", mvm->fw->fw_version);
 
        trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
@@ -516,13 +519,14 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
                iwl_mvm_dump_umac_error_log(mvm);
 }
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
 void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm)
 {
        const struct fw_img *img;
        u32 ofs, sram_len;
        void *sram;
 
-       if (!mvm->ucode_loaded || mvm->fw_error_sram)
+       if (!mvm->ucode_loaded || mvm->fw_error_sram || mvm->fw_error_dump)
                return;
 
        img = &mvm->fw->img[mvm->cur_ucode];
@@ -538,6 +542,48 @@ void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm)
        mvm->fw_error_sram_len = sram_len;
 }
 
+void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm)
+{
+       int i, reg_val;
+       unsigned long flags;
+
+       if (!mvm->ucode_loaded || mvm->fw_error_rxf || mvm->fw_error_dump)
+               return;
+
+       /* reading buffer size */
+       reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR);
+       mvm->fw_error_rxf_len =
+               (reg_val & RXF_SIZE_BYTE_CNT_MSK) >> RXF_SIZE_BYTE_CND_POS;
+
+       /* the register holds the value divided by 128 */
+       mvm->fw_error_rxf_len = mvm->fw_error_rxf_len << 7;
+
+       if (!mvm->fw_error_rxf_len)
+               return;
+
+       mvm->fw_error_rxf =  kzalloc(mvm->fw_error_rxf_len, GFP_ATOMIC);
+       if (!mvm->fw_error_rxf) {
+               mvm->fw_error_rxf_len = 0;
+               return;
+       }
+
+       if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags)) {
+               kfree(mvm->fw_error_rxf);
+               mvm->fw_error_rxf = NULL;
+               mvm->fw_error_rxf_len = 0;
+               return;
+       }
+
+       for (i = 0; i < (mvm->fw_error_rxf_len / sizeof(u32)); i++) {
+               iwl_trans_write_prph(mvm->trans, RXF_LD_FENCE_OFFSET_ADDR,
+                                    i * sizeof(u32));
+               mvm->fw_error_rxf[i] =
+                       iwl_trans_read_prph(mvm->trans, RXF_FIFO_RD_FENCE_ADDR);
+       }
+       iwl_trans_release_nic_access(mvm->trans, &flags);
+}
+#endif
+
 /**
  * iwl_mvm_send_lq_cmd() - Send link quality command
  * @init: This command is sent as part of station initialization right
@@ -553,7 +599,7 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init)
        struct iwl_host_cmd cmd = {
                .id = LQ_CMD,
                .len = { sizeof(struct iwl_lq_cmd), },
-               .flags = init ? CMD_SYNC : CMD_ASYNC,
+               .flags = init ? 0 : CMD_ASYNC,
                .data = { lq, },
        };
 
@@ -604,6 +650,39 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        ieee80211_request_smps(vif, smps_mode);
 }
 
+static void iwl_mvm_diversity_iter(void *_data, u8 *mac,
+                                  struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       bool *result = _data;
+       int i;
+
+       for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) {
+               if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC ||
+                   mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC)
+                       *result = false;
+       }
+}
+
+bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm)
+{
+       bool result = true;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (num_of_ant(mvm->fw->valid_rx_ant) == 1)
+               return false;
+
+       if (!mvm->cfg->rx_with_siso_diversity)
+               return false;
+
+       ieee80211_iterate_active_interfaces_atomic(
+                       mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+                       iwl_mvm_diversity_iter, &result);
+
+       return result;
+}
+
 int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                               bool value)
 {
@@ -623,7 +702,7 @@ int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
        iwl_mvm_bt_coex_vif_change(mvm);
 
-       return iwl_mvm_power_update_mac(mvm, vif);
+       return iwl_mvm_power_update_mac(mvm);
 }
 
 static void iwl_mvm_ll_iter(void *_data, u8 *mac, struct ieee80211_vif *vif)
index 3d1d57f9f5bc539d7350f518f5c13566917b6860..7091a18d5a72f9880f7a47f7a949fefd9de3b9f1 100644 (file)
@@ -417,7 +417,7 @@ static u64 splx_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splx)
            splx->package.count != 2 ||
            splx->package.elements[0].type != ACPI_TYPE_INTEGER ||
            splx->package.elements[0].integer.value != 0) {
-               IWL_ERR(trans, "Unsupported splx structure");
+               IWL_ERR(trans, "Unsupported splx structure\n");
                return 0;
        }
 
@@ -426,14 +426,14 @@ static u64 splx_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splx)
            limits->package.count < 2 ||
            limits->package.elements[0].type != ACPI_TYPE_INTEGER ||
            limits->package.elements[1].type != ACPI_TYPE_INTEGER) {
-               IWL_ERR(trans, "Invalid limits element");
+               IWL_ERR(trans, "Invalid limits element\n");
                return 0;
        }
 
        domain_type = &limits->package.elements[0];
        power_limit = &limits->package.elements[1];
        if (!(domain_type->integer.value & SPL_DOMAINTYPE_WIFI)) {
-               IWL_DEBUG_INFO(trans, "WiFi power is not limited");
+               IWL_DEBUG_INFO(trans, "WiFi power is not limited\n");
                return 0;
        }
 
@@ -450,26 +450,26 @@ static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev)
        pxsx_handle = ACPI_HANDLE(&pdev->dev);
        if (!pxsx_handle) {
                IWL_DEBUG_INFO(trans,
-                              "Could not retrieve root port ACPI handle");
+                              "Could not retrieve root port ACPI handle\n");
                return;
        }
 
        /* Get the method's handle */
        status = acpi_get_handle(pxsx_handle, (acpi_string)SPL_METHOD, &handle);
        if (ACPI_FAILURE(status)) {
-               IWL_DEBUG_INFO(trans, "SPL method not found");
+               IWL_DEBUG_INFO(trans, "SPL method not found\n");
                return;
        }
 
        /* Call SPLC with no arguments */
        status = acpi_evaluate_object(handle, NULL, NULL, &splx);
        if (ACPI_FAILURE(status)) {
-               IWL_ERR(trans, "SPLC invocation failed (0x%x)", status);
+               IWL_ERR(trans, "SPLC invocation failed (0x%x)\n", status);
                return;
        }
 
        trans->dflt_pwr_limit = splx_get_pwr_limit(trans, splx.pointer);
-       IWL_DEBUG_INFO(trans, "Default power limit set to %lld",
+       IWL_DEBUG_INFO(trans, "Default power limit set to %lld\n",
                       trans->dflt_pwr_limit);
        kfree(splx.pointer);
 }
index 9091513ea7388ce11f2294fbb609b3581073e2a0..6c22b23a2845723c33df6757a1ced2c545747e94 100644 (file)
@@ -102,7 +102,7 @@ struct iwl_rxq {
        u32 write_actual;
        struct list_head rx_free;
        struct list_head rx_used;
-       int need_update;
+       bool need_update;
        struct iwl_rb_status *rb_stts;
        dma_addr_t rb_stts_dma;
        spinlock_t lock;
@@ -117,21 +117,19 @@ struct iwl_dma_ptr {
 /**
  * iwl_queue_inc_wrap - increment queue index, wrap back to beginning
  * @index -- current index
- * @n_bd -- total number of entries in queue (must be power of 2)
  */
-static inline int iwl_queue_inc_wrap(int index, int n_bd)
+static inline int iwl_queue_inc_wrap(int index)
 {
-       return ++index & (n_bd - 1);
+       return ++index & (TFD_QUEUE_SIZE_MAX - 1);
 }
 
 /**
  * iwl_queue_dec_wrap - decrement queue index, wrap back to end
  * @index -- current index
- * @n_bd -- total number of entries in queue (must be power of 2)
  */
-static inline int iwl_queue_dec_wrap(int index, int n_bd)
+static inline int iwl_queue_dec_wrap(int index)
 {
-       return --index & (n_bd - 1);
+       return --index & (TFD_QUEUE_SIZE_MAX - 1);
 }
 
 struct iwl_cmd_meta {
@@ -145,13 +143,13 @@ struct iwl_cmd_meta {
  *
  * Contains common data for Rx and Tx queues.
  *
- * Note the difference between n_bd and n_window: the hardware
- * always assumes 256 descriptors, so n_bd is always 256 (unless
+ * Note the difference between TFD_QUEUE_SIZE_MAX and n_window: the hardware
+ * always assumes 256 descriptors, so TFD_QUEUE_SIZE_MAX is always 256 (unless
  * there might be HW changes in the future). For the normal TX
  * queues, n_window, which is the size of the software queue data
  * is also 256; however, for the command queue, n_window is only
  * 32 since we don't need so many commands pending. Since the HW
- * still uses 256 BDs for DMA though, n_bd stays 256. As a result,
+ * still uses 256 BDs for DMA though, TFD_QUEUE_SIZE_MAX stays 256. As a result,
  * the software buffers (in the variables @meta, @txb in struct
  * iwl_txq) only have 32 entries, while the HW buffers (@tfds in
  * the same struct) have 256.
@@ -162,7 +160,6 @@ struct iwl_cmd_meta {
  * data is a window overlayed over the HW queue.
  */
 struct iwl_queue {
-       int n_bd;              /* number of BDs in this queue */
        int write_ptr;       /* 1-st empty entry (index) host_w*/
        int read_ptr;         /* last used entry (index) host_r*/
        /* use for monitoring and recovering the stuck queue */
@@ -231,7 +228,7 @@ struct iwl_txq {
        spinlock_t lock;
        struct timer_list stuck_timer;
        struct iwl_trans_pcie *trans_pcie;
-       u8 need_update;
+       bool need_update;
        u8 active;
        bool ampdu;
 };
@@ -270,6 +267,9 @@ struct iwl_trans_pcie {
        struct iwl_trans *trans;
        struct iwl_drv *drv;
 
+       struct net_device napi_dev;
+       struct napi_struct napi;
+
        /* INT ICT Table */
        __le32 *ict_tbl;
        dma_addr_t ict_tbl_dma;
@@ -362,7 +362,7 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo,
 void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue);
 int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
                      struct iwl_device_cmd *dev_cmd, int txq_id);
-void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq);
+void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans);
 int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd);
 void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
                            struct iwl_rx_cmd_buffer *rxb, int handler_status);
@@ -370,6 +370,13 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
                            struct sk_buff_head *skbs);
 void iwl_trans_pcie_tx_reset(struct iwl_trans *trans);
 
+static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx)
+{
+       struct iwl_tfd_tb *tb = &tfd->tbs[idx];
+
+       return le16_to_cpu(tb->hi_n_len) >> 4;
+}
+
 /*****************************************************
 * Error handling
 ******************************************************/
index fdfa3969cac986c1824bd65c41512a9ac4ba7b39..a2698e5e062c990524d12e1d112818977aeccc2b 100644 (file)
@@ -145,15 +145,13 @@ 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 *rxq)
+static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans)
 {
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       struct iwl_rxq *rxq = &trans_pcie->rxq;
        u32 reg;
 
-       spin_lock(&rxq->lock);
-
-       if (rxq->need_update == 0)
-               goto exit_unlock;
+       lockdep_assert_held(&rxq->lock);
 
        /*
         * explicitly wake up the NIC if:
@@ -169,13 +167,27 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans,
                                       reg);
                        iwl_set_bit(trans, CSR_GP_CNTRL,
                                    CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
-                       goto exit_unlock;
+                       rxq->need_update = true;
+                       return;
                }
        }
 
        rxq->write_actual = round_down(rxq->write, 8);
        iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual);
-       rxq->need_update = 0;
+}
+
+static void iwl_pcie_rxq_check_wrptr(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       struct iwl_rxq *rxq = &trans_pcie->rxq;
+
+       spin_lock(&rxq->lock);
+
+       if (!rxq->need_update)
+               goto exit_unlock;
+
+       iwl_pcie_rxq_inc_wr_ptr(trans);
+       rxq->need_update = false;
 
  exit_unlock:
        spin_unlock(&rxq->lock);
@@ -236,9 +248,8 @@ static void iwl_pcie_rxq_restock(struct iwl_trans *trans)
         * Increment device's write pointer in multiples of 8. */
        if (rxq->write_actual != (rxq->write & ~0x7)) {
                spin_lock(&rxq->lock);
-               rxq->need_update = 1;
+               iwl_pcie_rxq_inc_wr_ptr(trans);
                spin_unlock(&rxq->lock);
-               iwl_pcie_rxq_inc_wr_ptr(trans, rxq);
        }
 }
 
@@ -362,20 +373,9 @@ static void iwl_pcie_rxq_free_rbs(struct iwl_trans *trans)
  * Also restock the Rx queue via iwl_pcie_rxq_restock.
  * This is called as a scheduled work item (except for during initialization)
  */
-static void iwl_pcie_rx_replenish(struct iwl_trans *trans)
-{
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
-       iwl_pcie_rxq_alloc_rbs(trans, GFP_KERNEL);
-
-       spin_lock(&trans_pcie->irq_lock);
-       iwl_pcie_rxq_restock(trans);
-       spin_unlock(&trans_pcie->irq_lock);
-}
-
-static void iwl_pcie_rx_replenish_now(struct iwl_trans *trans)
+static void iwl_pcie_rx_replenish(struct iwl_trans *trans, gfp_t gfp)
 {
-       iwl_pcie_rxq_alloc_rbs(trans, GFP_ATOMIC);
+       iwl_pcie_rxq_alloc_rbs(trans, gfp);
 
        iwl_pcie_rxq_restock(trans);
 }
@@ -385,7 +385,7 @@ static void iwl_pcie_rx_replenish_work(struct work_struct *data)
        struct iwl_trans_pcie *trans_pcie =
            container_of(data, struct iwl_trans_pcie, rx_replenish);
 
-       iwl_pcie_rx_replenish(trans_pcie->trans);
+       iwl_pcie_rx_replenish(trans_pcie->trans, GFP_KERNEL);
 }
 
 static int iwl_pcie_rx_alloc(struct iwl_trans *trans)
@@ -521,14 +521,13 @@ int iwl_pcie_rx_init(struct iwl_trans *trans)
        memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts));
        spin_unlock(&rxq->lock);
 
-       iwl_pcie_rx_replenish(trans);
+       iwl_pcie_rx_replenish(trans, GFP_KERNEL);
 
        iwl_pcie_rx_hw_init(trans, rxq);
 
-       spin_lock(&trans_pcie->irq_lock);
-       rxq->need_update = 1;
-       iwl_pcie_rxq_inc_wr_ptr(trans, rxq);
-       spin_unlock(&trans_pcie->irq_lock);
+       spin_lock(&rxq->lock);
+       iwl_pcie_rxq_inc_wr_ptr(trans);
+       spin_unlock(&rxq->lock);
 
        return 0;
 }
@@ -673,7 +672,6 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,
        /* Reuse the page if possible. For notification packets and
         * SKBs that fail to Rx correctly, add them back into the
         * rx_free list for reuse later. */
-       spin_lock(&rxq->lock);
        if (rxb->page != NULL) {
                rxb->page_dma =
                        dma_map_page(trans->dev, rxb->page, 0,
@@ -694,7 +692,6 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,
                }
        } else
                list_add_tail(&rxb->list, &rxq->rx_used);
-       spin_unlock(&rxq->lock);
 }
 
 /*
@@ -709,6 +706,8 @@ static void iwl_pcie_rx_handle(struct iwl_trans *trans)
        u32 count = 8;
        int total_empty;
 
+restart:
+       spin_lock(&rxq->lock);
        /* uCode's read index (stored in shared DRAM) indicates the last Rx
         * buffer that the driver may process (last buffer filled by ucode). */
        r = le16_to_cpu(ACCESS_ONCE(rxq->rb_stts->closed_rb_num)) & 0x0FFF;
@@ -743,18 +742,25 @@ static void iwl_pcie_rx_handle(struct iwl_trans *trans)
                        count++;
                        if (count >= 8) {
                                rxq->read = i;
-                               iwl_pcie_rx_replenish_now(trans);
+                               spin_unlock(&rxq->lock);
+                               iwl_pcie_rx_replenish(trans, GFP_ATOMIC);
                                count = 0;
+                               goto restart;
                        }
                }
        }
 
        /* Backtrack one entry */
        rxq->read = i;
+       spin_unlock(&rxq->lock);
+
        if (fill_rx)
-               iwl_pcie_rx_replenish_now(trans);
+               iwl_pcie_rx_replenish(trans, GFP_ATOMIC);
        else
                iwl_pcie_rxq_restock(trans);
+
+       if (trans_pcie->napi.poll)
+               napi_gro_flush(&trans_pcie->napi, false);
 }
 
 /*
@@ -844,7 +850,7 @@ static u32 iwl_pcie_int_cause_ict(struct iwl_trans *trans)
                                trans_pcie->ict_index, read);
                trans_pcie->ict_tbl[trans_pcie->ict_index] = 0;
                trans_pcie->ict_index =
-                       iwl_queue_inc_wrap(trans_pcie->ict_index, ICT_COUNT);
+                       ((trans_pcie->ict_index + 1) & (ICT_COUNT - 1));
 
                read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]);
                trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index,
@@ -876,7 +882,6 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
        struct isr_statistics *isr_stats = &trans_pcie->isr_stats;
        u32 inta = 0;
        u32 handled = 0;
-       u32 i;
 
        lock_map_acquire(&trans->sync_cmd_lockdep_map);
 
@@ -1028,9 +1033,8 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
        /* uCode wakes up after power-down sleep */
        if (inta & CSR_INT_BIT_WAKEUP) {
                IWL_DEBUG_ISR(trans, "Wakeup interrupt\n");
-               iwl_pcie_rxq_inc_wr_ptr(trans, &trans_pcie->rxq);
-               for (i = 0; i < trans->cfg->base_params->num_of_queues; i++)
-                       iwl_pcie_txq_inc_wr_ptr(trans, &trans_pcie->txq[i]);
+               iwl_pcie_rxq_check_wrptr(trans);
+               iwl_pcie_txq_check_wrptrs(trans);
 
                isr_stats->wakeup++;
 
@@ -1068,8 +1072,6 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
                iwl_write8(trans, CSR_INT_PERIODIC_REG,
                            CSR_INT_PERIODIC_DIS);
 
-               iwl_pcie_rx_handle(trans);
-
                /*
                 * Enable periodic interrupt in 8 msec only if we received
                 * real RX interrupt (instead of just periodic int), to catch
@@ -1082,6 +1084,10 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
                                   CSR_INT_PERIODIC_ENA);
 
                isr_stats->rx++;
+
+               local_bh_disable();
+               iwl_pcie_rx_handle(trans);
+               local_bh_enable();
        }
 
        /* This "Tx" DMA channel is used only for loading uCode */
index 2365553f1ef79d59c3e8598335e48eb43e5cfdcb..788085bc65d78e3382c7fa76ca74de30abd4cd84 100644 (file)
@@ -73,6 +73,7 @@
 #include "iwl-csr.h"
 #include "iwl-prph.h"
 #include "iwl-agn-hw.h"
+#include "iwl-fw-error-dump.h"
 #include "internal.h"
 
 static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg)
@@ -103,7 +104,6 @@ static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux)
 
 /* PCI registers */
 #define PCI_CFG_RETRY_TIMEOUT  0x041
-#define CPU1_CPU2_SEPARATOR_SECTION    0xFFFFCCCC
 
 static void iwl_pcie_apm_config(struct iwl_trans *trans)
 {
@@ -454,6 +454,7 @@ static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans)
 {
        int ret;
        int t = 0;
+       int iter;
 
        IWL_DEBUG_INFO(trans, "iwl_trans_prepare_card_hw enter\n");
 
@@ -462,18 +463,23 @@ static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans)
        if (ret >= 0)
                return 0;
 
-       /* If HW is not ready, prepare the conditions to check again */
-       iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
-                   CSR_HW_IF_CONFIG_REG_PREPARE);
+       for (iter = 0; iter < 10; iter++) {
+               /* If HW is not ready, prepare the conditions to check again */
+               iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
+                           CSR_HW_IF_CONFIG_REG_PREPARE);
+
+               do {
+                       ret = iwl_pcie_set_hw_ready(trans);
+                       if (ret >= 0)
+                               return 0;
 
-       do {
-               ret = iwl_pcie_set_hw_ready(trans);
-               if (ret >= 0)
-                       return 0;
+                       usleep_range(200, 1000);
+                       t += 200;
+               } while (t < 150000);
+               msleep(25);
+       }
 
-               usleep_range(200, 1000);
-               t += 200;
-       } while (t < 150000);
+       IWL_DEBUG_INFO(trans, "got NIC after %d iterations\n", iter);
 
        return ret;
 }
@@ -1053,6 +1059,12 @@ static void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr,
        iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WDAT, val);
 }
 
+static int iwl_pcie_dummy_napi_poll(struct napi_struct *napi, int budget)
+{
+       WARN_ON(1);
+       return 0;
+}
+
 static void iwl_trans_pcie_configure(struct iwl_trans *trans,
                                     const struct iwl_trans_config *trans_cfg)
 {
@@ -1079,6 +1091,18 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans,
 
        trans_pcie->command_names = trans_cfg->command_names;
        trans_pcie->bc_table_dword = trans_cfg->bc_table_dword;
+
+       /* Initialize NAPI here - it should be before registering to mac80211
+        * in the opmode but after the HW struct is allocated.
+        * As this function may be called again in some corner cases don't
+        * do anything if NAPI was already initialized.
+        */
+       if (!trans_pcie->napi.poll && trans->op_mode->ops->napi_add) {
+               init_dummy_netdev(&trans_pcie->napi_dev);
+               iwl_op_mode_napi_add(trans->op_mode, &trans_pcie->napi,
+                                    &trans_pcie->napi_dev,
+                                    iwl_pcie_dummy_napi_poll, 64);
+       }
 }
 
 void iwl_trans_pcie_free(struct iwl_trans *trans)
@@ -1099,6 +1123,9 @@ void iwl_trans_pcie_free(struct iwl_trans *trans)
        pci_disable_device(trans_pcie->pci_dev);
        kmem_cache_destroy(trans->dev_cmd_pool);
 
+       if (trans_pcie->napi.poll)
+               netif_napi_del(&trans_pcie->napi);
+
        kfree(trans);
 }
 
@@ -1237,7 +1264,7 @@ static int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr,
 
 #define IWL_FLUSH_WAIT_MS      2000
 
-static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans)
+static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct iwl_txq *txq;
@@ -1250,13 +1277,31 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans)
 
        /* waiting for all the tx frames complete might take a while */
        for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) {
+               u8 wr_ptr;
+
                if (cnt == trans_pcie->cmd_queue)
                        continue;
+               if (!test_bit(cnt, trans_pcie->queue_used))
+                       continue;
+               if (!(BIT(cnt) & txq_bm))
+                       continue;
+
+               IWL_DEBUG_TX_QUEUES(trans, "Emptying queue %d...\n", cnt);
                txq = &trans_pcie->txq[cnt];
                q = &txq->q;
-               while (q->read_ptr != q->write_ptr && !time_after(jiffies,
-                      now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS)))
+               wr_ptr = ACCESS_ONCE(q->write_ptr);
+
+               while (q->read_ptr != ACCESS_ONCE(q->write_ptr) &&
+                      !time_after(jiffies,
+                                  now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS))) {
+                       u8 write_ptr = ACCESS_ONCE(q->write_ptr);
+
+                       if (WARN_ONCE(wr_ptr != write_ptr,
+                                     "WR pointer moved while flushing %d -> %d\n",
+                                     wr_ptr, write_ptr))
+                               return -ETIMEDOUT;
                        msleep(1);
+               }
 
                if (q->read_ptr != q->write_ptr) {
                        IWL_ERR(trans,
@@ -1264,6 +1309,7 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans)
                        ret = -ETIMEDOUT;
                        break;
                }
+               IWL_DEBUG_TX_QUEUES(trans, "Queue %d is now empty.\n", cnt);
        }
 
        if (!ret)
@@ -1298,8 +1344,8 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans)
                IWL_ERR(trans,
                        "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n",
                        cnt, active ? "" : "in", fifo, tbl_dw,
-                       iwl_read_prph(trans,
-                                     SCD_QUEUE_RDPTR(cnt)) & (txq->q.n_bd - 1),
+                       iwl_read_prph(trans, SCD_QUEUE_RDPTR(cnt)) &
+                               (TFD_QUEUE_SIZE_MAX - 1),
                        iwl_read_prph(trans, SCD_QUEUE_WRPTR(cnt)));
        }
 
@@ -1630,6 +1676,61 @@ err:
        IWL_ERR(trans, "failed to create the trans debugfs entry\n");
        return -ENOMEM;
 }
+
+static u32 iwl_trans_pcie_get_cmdlen(struct iwl_tfd *tfd)
+{
+       u32 cmdlen = 0;
+       int i;
+
+       for (i = 0; i < IWL_NUM_OF_TBS; i++)
+               cmdlen += iwl_pcie_tfd_tb_get_len(tfd, i);
+
+       return cmdlen;
+}
+
+static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans,
+                                   void *buf, u32 buflen)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       struct iwl_fw_error_dump_data *data;
+       struct iwl_txq *cmdq = &trans_pcie->txq[trans_pcie->cmd_queue];
+       struct iwl_fw_error_dump_txcmd *txcmd;
+       u32 len;
+       int i, ptr;
+
+       if (!buf)
+               return sizeof(*data) +
+                      cmdq->q.n_window * (sizeof(*txcmd) +
+                                          TFD_MAX_PAYLOAD_SIZE);
+
+       len = 0;
+       data = buf;
+       data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXCMD);
+       txcmd = (void *)data->data;
+       spin_lock_bh(&cmdq->lock);
+       ptr = cmdq->q.write_ptr;
+       for (i = 0; i < cmdq->q.n_window; i++) {
+               u8 idx = get_cmd_index(&cmdq->q, ptr);
+               u32 caplen, cmdlen;
+
+               cmdlen = iwl_trans_pcie_get_cmdlen(&cmdq->tfds[ptr]);
+               caplen = min_t(u32, TFD_MAX_PAYLOAD_SIZE, cmdlen);
+
+               if (cmdlen) {
+                       len += sizeof(*txcmd) + caplen;
+                       txcmd->cmdlen = cpu_to_le32(cmdlen);
+                       txcmd->caplen = cpu_to_le32(caplen);
+                       memcpy(txcmd->data, cmdq->entries[idx].cmd, caplen);
+                       txcmd = (void *)((u8 *)txcmd->data + caplen);
+               }
+
+               ptr = iwl_queue_dec_wrap(ptr);
+       }
+       spin_unlock_bh(&cmdq->lock);
+
+       data->len = cpu_to_le32(len);
+       return sizeof(*data) + len;
+}
 #else
 static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
                                         struct dentry *dir)
@@ -1672,6 +1773,10 @@ static const struct iwl_trans_ops trans_ops_pcie = {
        .grab_nic_access = iwl_trans_pcie_grab_nic_access,
        .release_nic_access = iwl_trans_pcie_release_nic_access,
        .set_bits_mask = iwl_trans_pcie_set_bits_mask,
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       .dump_data = iwl_trans_pcie_dump_data,
+#endif
 };
 
 struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
index 3b0c72c1005446d2d93f7eb15ed6d3207e1c3ea1..038940afbdc57d8d176908bb4869fc8791f250eb 100644 (file)
@@ -70,20 +70,20 @@ static int iwl_queue_space(const struct iwl_queue *q)
 
        /*
         * To avoid ambiguity between empty and completely full queues, there
-        * should always be less than q->n_bd elements in the queue.
-        * If q->n_window is smaller than q->n_bd, there is no need to reserve
-        * any queue entries for this purpose.
+        * should always be less than TFD_QUEUE_SIZE_MAX elements in the queue.
+        * If q->n_window is smaller than TFD_QUEUE_SIZE_MAX, there is no need
+        * to reserve any queue entries for this purpose.
         */
-       if (q->n_window < q->n_bd)
+       if (q->n_window < TFD_QUEUE_SIZE_MAX)
                max = q->n_window;
        else
-               max = q->n_bd - 1;
+               max = TFD_QUEUE_SIZE_MAX - 1;
 
        /*
-        * q->n_bd is a power of 2, so the following is equivalent to modulo by
-        * q->n_bd and is well defined for negative dividends.
+        * TFD_QUEUE_SIZE_MAX is a power of 2, so the following is equivalent to
+        * modulo by TFD_QUEUE_SIZE_MAX and is well defined.
         */
-       used = (q->write_ptr - q->read_ptr) & (q->n_bd - 1);
+       used = (q->write_ptr - q->read_ptr) & (TFD_QUEUE_SIZE_MAX - 1);
 
        if (WARN_ON(used > max))
                return 0;
@@ -94,17 +94,11 @@ static int iwl_queue_space(const struct iwl_queue *q)
 /*
  * iwl_queue_init - Initialize queue's high/low-water and read/write indexes
  */
-static int iwl_queue_init(struct iwl_queue *q, int count, int slots_num, u32 id)
+static int iwl_queue_init(struct iwl_queue *q, int slots_num, u32 id)
 {
-       q->n_bd = count;
        q->n_window = slots_num;
        q->id = id;
 
-       /* count must be power-of-two size, otherwise iwl_queue_inc_wrap
-        * and iwl_queue_dec_wrap are broken. */
-       if (WARN_ON(!is_power_of_2(count)))
-               return -EINVAL;
-
        /* slots_num must be power-of-two size, otherwise
         * get_cmd_index is broken. */
        if (WARN_ON(!is_power_of_2(slots_num)))
@@ -197,17 +191,17 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data)
                IWL_ERR(trans,
                        "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n",
                        i, active ? "" : "in", fifo, tbl_dw,
-                       iwl_read_prph(trans,
-                                     SCD_QUEUE_RDPTR(i)) & (txq->q.n_bd - 1),
+                       iwl_read_prph(trans, SCD_QUEUE_RDPTR(i)) &
+                               (TFD_QUEUE_SIZE_MAX - 1),
                        iwl_read_prph(trans, SCD_QUEUE_WRPTR(i)));
        }
 
        for (i = q->read_ptr; i != q->write_ptr;
-            i = iwl_queue_inc_wrap(i, q->n_bd))
+            i = iwl_queue_inc_wrap(i))
                IWL_ERR(trans, "scratch %d = 0x%08x\n", i,
                        le32_to_cpu(txq->scratchbufs[i].scratch));
 
-       iwl_write_prph(trans, DEVICE_SET_NMI_REG, 1);
+       iwl_force_nmi(trans);
 }
 
 /*
@@ -287,14 +281,14 @@ static void iwl_pcie_txq_inval_byte_cnt_tbl(struct iwl_trans *trans,
 /*
  * iwl_pcie_txq_inc_wr_ptr - Send new write index to hardware
  */
-void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq)
+static void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans,
+                                   struct iwl_txq *txq)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        u32 reg = 0;
        int txq_id = txq->q.id;
 
-       if (txq->need_update == 0)
-               return;
+       lockdep_assert_held(&txq->lock);
 
        /*
         * explicitly wake up the NIC if:
@@ -317,6 +311,7 @@ void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq)
                                       txq_id, reg);
                        iwl_set_bit(trans, CSR_GP_CNTRL,
                                    CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+                       txq->need_update = true;
                        return;
                }
        }
@@ -327,8 +322,23 @@ void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq)
         */
        IWL_DEBUG_TX(trans, "Q:%d WR: 0x%x\n", txq_id, txq->q.write_ptr);
        iwl_write32(trans, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8));
+}
+
+void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       int i;
+
+       for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) {
+               struct iwl_txq *txq = &trans_pcie->txq[i];
 
-       txq->need_update = 0;
+               spin_lock_bh(&txq->lock);
+               if (trans_pcie->txq[i].need_update) {
+                       iwl_pcie_txq_inc_wr_ptr(trans, txq);
+                       trans_pcie->txq[i].need_update = false;
+               }
+               spin_unlock_bh(&txq->lock);
+       }
 }
 
 static inline dma_addr_t iwl_pcie_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx)
@@ -343,13 +353,6 @@ static inline dma_addr_t iwl_pcie_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx)
        return addr;
 }
 
-static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx)
-{
-       struct iwl_tfd_tb *tb = &tfd->tbs[idx];
-
-       return le16_to_cpu(tb->hi_n_len) >> 4;
-}
-
 static inline void iwl_pcie_tfd_set_tb(struct iwl_tfd *tfd, u8 idx,
                                       dma_addr_t addr, u16 len)
 {
@@ -409,13 +412,17 @@ static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq)
 {
        struct iwl_tfd *tfd_tmp = txq->tfds;
 
-       /* rd_ptr is bounded by n_bd and idx is bounded by n_window */
+       /* rd_ptr is bounded by TFD_QUEUE_SIZE_MAX and
+        * idx is bounded by n_window
+        */
        int rd_ptr = txq->q.read_ptr;
        int idx = get_cmd_index(&txq->q, rd_ptr);
 
        lockdep_assert_held(&txq->lock);
 
-       /* We have only q->n_window txq->entries, but we use q->n_bd tfds */
+       /* We have only q->n_window txq->entries, but we use
+        * TFD_QUEUE_SIZE_MAX tfds
+        */
        iwl_pcie_tfd_unmap(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr]);
 
        /* free SKB */
@@ -436,7 +443,7 @@ static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq)
 }
 
 static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq,
-                                 dma_addr_t addr, u16 len, u8 reset)
+                                 dma_addr_t addr, u16 len, bool reset)
 {
        struct iwl_queue *q;
        struct iwl_tfd *tfd, *tfd_tmp;
@@ -542,15 +549,14 @@ static int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq,
 {
        int ret;
 
-       txq->need_update = 0;
+       txq->need_update = false;
 
        /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
         * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */
        BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
 
        /* Initialize queue's high/low-water marks, and head/tail indexes */
-       ret = iwl_queue_init(&txq->q, TFD_QUEUE_SIZE_MAX, slots_num,
-                       txq_id);
+       ret = iwl_queue_init(&txq->q, slots_num, txq_id);
        if (ret)
                return ret;
 
@@ -575,15 +581,12 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id)
        struct iwl_txq *txq = &trans_pcie->txq[txq_id];
        struct iwl_queue *q = &txq->q;
 
-       if (!q->n_bd)
-               return;
-
        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);
+               q->read_ptr = iwl_queue_inc_wrap(q->read_ptr);
        }
        txq->active = false;
        spin_unlock_bh(&txq->lock);
@@ -620,10 +623,12 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id)
                }
 
        /* De-alloc circular buffer of TFDs */
-       if (txq->q.n_bd) {
-               dma_free_coherent(dev, sizeof(struct iwl_tfd) *
-                                 txq->q.n_bd, txq->tfds, txq->q.dma_addr);
+       if (txq->tfds) {
+               dma_free_coherent(dev,
+                                 sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX,
+                                 txq->tfds, txq->q.dma_addr);
                txq->q.dma_addr = 0;
+               txq->tfds = NULL;
 
                dma_free_coherent(dev,
                                  sizeof(*txq->scratchbufs) * txq->q.n_window,
@@ -680,7 +685,8 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr)
        /* The chain extension of the SCD doesn't work well. This feature is
         * enabled by default by the HW, so we need to disable it manually.
         */
-       iwl_write_prph(trans, SCD_CHAINEXT_EN, 0);
+       if (trans->cfg->base_params->scd_chain_ext_wa)
+               iwl_write_prph(trans, SCD_CHAINEXT_EN, 0);
 
        iwl_trans_ac_txq_enable(trans, trans_pcie->cmd_queue,
                                trans_pcie->cmd_fifo);
@@ -931,8 +937,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct iwl_txq *txq = &trans_pcie->txq[txq_id];
-       /* n_bd is usually 256 => n_bd - 1 = 0xff */
-       int tfd_num = ssn & (txq->q.n_bd - 1);
+       int tfd_num = ssn & (TFD_QUEUE_SIZE_MAX - 1);
        struct iwl_queue *q = &txq->q;
        int last_to_free;
 
@@ -956,12 +961,12 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
 
        /*Since we free until index _not_ inclusive, the one before index is
         * the last we will free. This one must be used */
-       last_to_free = iwl_queue_dec_wrap(tfd_num, q->n_bd);
+       last_to_free = iwl_queue_dec_wrap(tfd_num);
 
        if (!iwl_queue_used(q, last_to_free)) {
                IWL_ERR(trans,
                        "%s: Read index for DMA queue txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n",
-                       __func__, txq_id, last_to_free, q->n_bd,
+                       __func__, txq_id, last_to_free, TFD_QUEUE_SIZE_MAX,
                        q->write_ptr, q->read_ptr);
                goto out;
        }
@@ -971,7 +976,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
 
        for (;
             q->read_ptr != tfd_num;
-            q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
+            q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) {
 
                if (WARN_ON_ONCE(txq->entries[txq->q.read_ptr].skb == NULL))
                        continue;
@@ -1010,25 +1015,26 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx)
 
        lockdep_assert_held(&txq->lock);
 
-       if ((idx >= q->n_bd) || (!iwl_queue_used(q, idx))) {
+       if ((idx >= TFD_QUEUE_SIZE_MAX) || (!iwl_queue_used(q, idx))) {
                IWL_ERR(trans,
                        "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n",
-                       __func__, txq_id, idx, q->n_bd,
+                       __func__, txq_id, idx, TFD_QUEUE_SIZE_MAX,
                        q->write_ptr, q->read_ptr);
                return;
        }
 
-       for (idx = iwl_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx;
-            q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
+       for (idx = iwl_queue_inc_wrap(idx); q->read_ptr != idx;
+            q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) {
 
                if (nfreed++ > 0) {
                        IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n",
                                idx, q->write_ptr, q->read_ptr);
-                       iwl_write_prph(trans, DEVICE_SET_NMI_REG, 1);
+                       iwl_force_nmi(trans);
                }
        }
 
-       if (q->read_ptr == q->write_ptr) {
+       if (trans->cfg->base_params->apmg_wake_up_wa &&
+           q->read_ptr == q->write_ptr) {
                spin_lock_irqsave(&trans_pcie->reg_lock, flags);
                WARN_ON(!trans_pcie->cmd_in_flight);
                trans_pcie->cmd_in_flight = false;
@@ -1309,28 +1315,39 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
        cmd_pos = offsetof(struct iwl_device_cmd, payload);
        copy_size = sizeof(out_cmd->hdr);
        for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) {
-               int copy = 0;
+               int copy;
 
                if (!cmd->len[i])
                        continue;
 
-               /* need at least IWL_HCMD_SCRATCHBUF_SIZE copied */
-               if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) {
-                       copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size;
-
-                       if (copy > cmd->len[i])
-                               copy = cmd->len[i];
-               }
-
                /* copy everything if not nocopy/dup */
                if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY |
-                                          IWL_HCMD_DFL_DUP)))
+                                          IWL_HCMD_DFL_DUP))) {
                        copy = cmd->len[i];
 
-               if (copy) {
                        memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy);
                        cmd_pos += copy;
                        copy_size += copy;
+                       continue;
+               }
+
+               /*
+                * Otherwise we need at least IWL_HCMD_SCRATCHBUF_SIZE copied
+                * in total (for the scratchbuf handling), but copy up to what
+                * we can fit into the payload for debug dump purposes.
+                */
+               copy = min_t(int, TFD_MAX_PAYLOAD_SIZE - cmd_pos, cmd->len[i]);
+
+               memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy);
+               cmd_pos += copy;
+
+               /* However, treat copy_size the proper way, we need it below */
+               if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) {
+                       copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size;
+
+                       if (copy > cmd->len[i])
+                               copy = cmd->len[i];
+                       copy_size += copy;
                }
        }
 
@@ -1345,7 +1362,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
        memcpy(&txq->scratchbufs[q->write_ptr], &out_cmd->hdr, scratch_size);
        iwl_pcie_txq_build_tfd(trans, txq,
                               iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr),
-                              scratch_size, 1);
+                              scratch_size, true);
 
        /* map first command fragment, if any remains */
        if (copy_size > scratch_size) {
@@ -1361,7 +1378,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
                }
 
                iwl_pcie_txq_build_tfd(trans, txq, phys_addr,
-                                      copy_size - scratch_size, 0);
+                                      copy_size - scratch_size, false);
        }
 
        /* map the remaining (adjusted) nocopy/dup fragments */
@@ -1384,7 +1401,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
                        goto out;
                }
 
-               iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmdlen[i], 0);
+               iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmdlen[i], false);
        }
 
        out_meta->flags = cmd->flags;
@@ -1392,8 +1409,6 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
                kfree(txq->entries[idx].free_buf);
        txq->entries[idx].free_buf = dup_buf;
 
-       txq->need_update = 1;
-
        trace_iwlwifi_dev_hcmd(trans->dev, cmd, cmd_size, &out_cmd->hdr);
 
        /* start timer if queue currently empty */
@@ -1405,9 +1420,11 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
        /*
         * wake up the NIC to make sure that the firmware will see the host
         * command - we will let the NIC sleep once all the host commands
-        * returned.
+        * returned. This needs to be done only on NICs that have
+        * apmg_wake_up_wa set.
         */
-       if (!trans_pcie->cmd_in_flight) {
+       if (trans->cfg->base_params->apmg_wake_up_wa &&
+           !trans_pcie->cmd_in_flight) {
                trans_pcie->cmd_in_flight = true;
                __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
                                         CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
@@ -1427,7 +1444,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
        }
 
        /* Increment and update queue's write index */
-       q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
+       q->write_ptr = iwl_queue_inc_wrap(q->write_ptr);
        iwl_pcie_txq_inc_wr_ptr(trans, txq);
 
        spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
@@ -1583,7 +1600,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
                               get_cmd_string(trans_pcie, cmd->id));
                ret = -ETIMEDOUT;
 
-               iwl_write_prph(trans, DEVICE_SET_NMI_REG, 1);
+               iwl_force_nmi(trans);
                iwl_trans_fw_error(trans);
 
                goto cancel;
@@ -1661,7 +1678,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
        dma_addr_t tb0_phys, tb1_phys, scratch_phys;
        void *tb1_addr;
        u16 len, tb1_len, tb2_len;
-       u8 wait_write_ptr = 0;
+       bool wait_write_ptr;
        __le16 fc = hdr->frame_control;
        u8 hdr_len = ieee80211_hdrlen(fc);
        u16 wifi_seq;
@@ -1722,7 +1739,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
        memcpy(&txq->scratchbufs[q->write_ptr], &dev_cmd->hdr,
               IWL_HCMD_SCRATCHBUF_SIZE);
        iwl_pcie_txq_build_tfd(trans, txq, tb0_phys,
-                              IWL_HCMD_SCRATCHBUF_SIZE, 1);
+                              IWL_HCMD_SCRATCHBUF_SIZE, true);
 
        /* there must be data left over for TB1 or this code must be changed */
        BUILD_BUG_ON(sizeof(struct iwl_tx_cmd) < IWL_HCMD_SCRATCHBUF_SIZE);
@@ -1732,7 +1749,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
        tb1_phys = dma_map_single(trans->dev, tb1_addr, tb1_len, DMA_TO_DEVICE);
        if (unlikely(dma_mapping_error(trans->dev, tb1_phys)))
                goto out_err;
-       iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, 0);
+       iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, false);
 
        /*
         * Set up TFD's third entry to point directly to remainder
@@ -1748,7 +1765,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
                                           &txq->tfds[q->write_ptr]);
                        goto out_err;
                }
-               iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, 0);
+               iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, false);
        }
 
        /* Set up entry for this TFD in Tx byte-count array */
@@ -1762,12 +1779,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
        trace_iwlwifi_dev_tx_data(trans->dev, skb,
                                  skb->data + hdr_len, tb2_len);
 
-       if (!ieee80211_has_morefrags(fc)) {
-               txq->need_update = 1;
-       } else {
-               wait_write_ptr = 1;
-               txq->need_update = 0;
-       }
+       wait_write_ptr = ieee80211_has_morefrags(fc);
 
        /* start timer if queue currently empty */
        if (txq->need_update && q->read_ptr == q->write_ptr &&
@@ -1775,22 +1787,19 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
                mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout);
 
        /* Tell device the write index *just past* this latest filled TFD */
-       q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
-       iwl_pcie_txq_inc_wr_ptr(trans, txq);
+       q->write_ptr = iwl_queue_inc_wrap(q->write_ptr);
+       if (!wait_write_ptr)
+               iwl_pcie_txq_inc_wr_ptr(trans, txq);
 
        /*
         * At this point the frame is "transmitted" successfully
-        * and we will get a TX status notification eventually,
-        * regardless of the value of ret. "ret" only indicates
-        * whether or not we should update the write pointer.
+        * and we will get a TX status notification eventually.
         */
        if (iwl_queue_space(q) < q->high_mark) {
-               if (wait_write_ptr) {
-                       txq->need_update = 1;
+               if (wait_write_ptr)
                        iwl_pcie_txq_inc_wr_ptr(trans, txq);
-               } else {
+               else
                        iwl_stop_queue(trans, txq);
-               }
        }
        spin_unlock(&txq->lock);
        return 0;
index 54e344aed6e05d097c3eb60462d74064757b81fc..47a998d8f99e75bd5f59521a6593ca183cebb77a 100644 (file)
@@ -1006,9 +1006,8 @@ struct cmd_key_material {
 } __packed;
 
 static int lbs_set_key_material(struct lbs_private *priv,
-                               int key_type,
-                               int key_info,
-                               u8 *key, u16 key_len)
+                               int key_type, int key_info,
+                               const u8 *key, u16 key_len)
 {
        struct cmd_key_material cmd;
        int ret;
@@ -1610,7 +1609,7 @@ static int lbs_cfg_del_key(struct wiphy *wiphy, struct net_device *netdev,
  */
 
 static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev,
-                             u8 *mac, struct station_info *sinfo)
+                              const u8 *mac, struct station_info *sinfo)
 {
        struct lbs_private *priv = wiphy_priv(wiphy);
        s8 signal, noise;
index ab966f08024a0013194c8eaf82e472d866f3492e..407784aca627bcd69b8a8632b45be53390a17481 100644 (file)
@@ -90,7 +90,8 @@ do { if ((lbs_debug & (grp)) == (grp)) \
 #define lbs_deb_cfg80211(fmt, args...)  LBS_DEB_LL(LBS_DEB_CFG80211, " cfg80211", fmt, ##args)
 
 #ifdef DEBUG
-static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, int len)
+static inline void lbs_deb_hex(unsigned int grp, const char *prompt,
+                              const u8 *buf, int len)
 {
        int i = 0;
 
index c7366b07b568ab303985c651679b3bcdb409e051..e446fed7b3459b854b05ab7728c0c15c3ac51e7e 100644 (file)
@@ -71,8 +71,10 @@ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb)
 
        skb->ip_summed = CHECKSUM_NONE;
 
-       if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR)
-               return process_rxed_802_11_packet(priv, skb);
+       if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) {
+               ret = process_rxed_802_11_packet(priv, skb);
+               goto done;
+       }
 
        p_rx_pd = (struct rxpd *) skb->data;
        p_rx_pkt = (struct rxpackethdr *) ((u8 *)p_rx_pd +
@@ -86,7 +88,7 @@ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb)
        if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) {
                lbs_deb_rx("rx err: frame received with bad length\n");
                dev->stats.rx_length_errors++;
-               ret = 0;
+               ret = -EINVAL;
                dev_kfree_skb(skb);
                goto done;
        }
index 9d7a52f5a4102abedd2dbebc03c26c3866da2a64..a312c653d1163fcc5c4ff394a54b0c7a96370d8f 100644 (file)
@@ -1676,7 +1676,9 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,
        return 0;
 }
 
-static void mac80211_hwsim_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static void mac80211_hwsim_flush(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif,
+                                u32 queues, bool drop)
 {
        /* Not implemented, queues only on kernel side */
 }
@@ -2056,6 +2058,7 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
                            WIPHY_FLAG_AP_UAPSD |
                            WIPHY_FLAG_HAS_CHANNEL_SWITCH;
        hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
+       hw->wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE;
 
        /* ask mac80211 to reserve space for magic */
        hw->vif_data_size = sizeof(struct hwsim_vif_priv);
index c92f27aa71ede1f049c101a0ba185f31d982aaa3..706831df1fa2a4183cb3c5ad849f1aa8df8dbb14 100644 (file)
@@ -212,8 +212,7 @@ int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv,
                                      sizeof(struct mwifiex_ie_types_header));
                        memcpy((u8 *)vht_op +
                                sizeof(struct mwifiex_ie_types_header),
-                              (u8 *)bss_desc->bcn_vht_oper +
-                              sizeof(struct ieee_types_header),
+                              (u8 *)bss_desc->bcn_vht_oper,
                               le16_to_cpu(vht_op->header.len));
 
                        /* negotiate the channel width and central freq
index d14ead8beca860dba6c984d26df095b104bb1375..e1c2f67ae85e694d52b1f9e4ad69f2d50ab6ba91 100644 (file)
@@ -345,8 +345,7 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
 
                        memcpy((u8 *) ht_info +
                               sizeof(struct mwifiex_ie_types_header),
-                              (u8 *) bss_desc->bcn_ht_oper +
-                              sizeof(struct ieee_types_header),
+                              (u8 *)bss_desc->bcn_ht_oper,
                               le16_to_cpu(ht_info->header.len));
 
                        if (!(sband->ht_cap.cap &
@@ -750,3 +749,45 @@ void mwifiex_set_ba_params(struct mwifiex_private *priv)
 
        return;
 }
+
+u8 mwifiex_get_sec_chan_offset(int chan)
+{
+       u8 sec_offset;
+
+       switch (chan) {
+       case 36:
+       case 44:
+       case 52:
+       case 60:
+       case 100:
+       case 108:
+       case 116:
+       case 124:
+       case 132:
+       case 140:
+       case 149:
+       case 157:
+               sec_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+               break;
+       case 40:
+       case 48:
+       case 56:
+       case 64:
+       case 104:
+       case 112:
+       case 120:
+       case 128:
+       case 136:
+       case 144:
+       case 153:
+       case 161:
+               sec_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+               break;
+       case 165:
+       default:
+               sec_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+               break;
+       }
+
+       return sec_offset;
+}
index 40b007a00f4bd9e786c24f8f53059c38e29e79a4..0b73fa08f5d466b98d5292d0a5b16e1011c30be3 100644 (file)
@@ -63,6 +63,7 @@ int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd,
                                int cmd_action,
                                struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl);
 void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra);
+u8 mwifiex_get_sec_chan_offset(int chan);
 
 static inline u8
 mwifiex_is_station_ampdu_allowed(struct mwifiex_private *priv,
@@ -199,7 +200,7 @@ static inline int mwifiex_is_sta_11n_enabled(struct mwifiex_private *priv,
 }
 
 static inline u8
-mwifiex_tdls_peer_11n_enabled(struct mwifiex_private *priv, u8 *ra)
+mwifiex_tdls_peer_11n_enabled(struct mwifiex_private *priv, const u8 *ra)
 {
        struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ra);
        if (node)
index 63211707f93955c851bfb96d71f12d5ef1f4a313..5b32106182f81c11fbc2bd985166dad198f341b1 100644 (file)
@@ -100,6 +100,7 @@ mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv,
                            struct sk_buff *skb)
 {
        struct txpd *local_tx_pd;
+       struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb);
 
        skb_push(skb, sizeof(*local_tx_pd));
 
@@ -118,6 +119,9 @@ mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv,
        local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len -
                                                 sizeof(*local_tx_pd));
 
+       if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT)
+               local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET;
+
        if (local_tx_pd->tx_control == 0)
                /* TxCtrl set by user or default */
                local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl);
@@ -160,6 +164,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
        int pad = 0, ret;
        struct mwifiex_tx_param tx_param;
        struct txpd *ptx_pd = NULL;
+       struct timeval tv;
        int headroom = adapter->iface_type == MWIFIEX_USB ? 0 : INTF_HEADER_LEN;
 
        skb_src = skb_peek(&pra_list->skb_head);
@@ -182,8 +187,14 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
 
        tx_info_aggr->bss_type = tx_info_src->bss_type;
        tx_info_aggr->bss_num = tx_info_src->bss_num;
+
+       if (tx_info_src->flags & MWIFIEX_BUF_FLAG_TDLS_PKT)
+               tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT;
        skb_aggr->priority = skb_src->priority;
 
+       do_gettimeofday(&tv);
+       skb_aggr->tstamp = timeval_to_ktime(tv);
+
        do {
                /* Check if AMSDU can accommodate this MSDU */
                if (skb_tailroom(skb_aggr) < (skb_src->len + LLC_SNAP_LEN))
@@ -236,18 +247,11 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
                ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA,
                                                   skb_aggr, NULL);
        } else {
-               /*
-                * Padding per MSDU will affect the length of next
-                * packet and hence the exact length of next packet
-                * is uncertain here.
-                *
-                * Also, aggregation of transmission buffer, while
-                * downloading the data to the card, wont gain much
-                * on the AMSDU packets as the AMSDU packets utilizes
-                * the transmission buffer space to the maximum
-                * (adapter->tx_buf_size).
-                */
-               tx_param.next_pkt_len = 0;
+               if (skb_src)
+                       tx_param.next_pkt_len =
+                                       skb_src->len + sizeof(struct txpd);
+               else
+                       tx_param.next_pkt_len = 0;
 
                ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA,
                                                   skb_aggr, &tx_param);
index b9242c3dca435ee9a4d5123fd57ad0733a96a24d..3b55ce5690a54e226c5482f523a3c80d1e95d7bf 100644 (file)
@@ -200,4 +200,11 @@ getlog
 
        cat getlog
 
+fw_dump
+       This command is used to dump firmware memory into files.
+       Separate file will be created for each memory segment.
+       Usage:
+
+       cat fw_dump
+
 ===============================================================================
index 21ee27ab7b745261f9a398bcaacc92b24ce7c8f7..e95dec91a561e1172289dca0d6bbfb35b38add56 100644 (file)
@@ -994,7 +994,7 @@ mwifiex_dump_station_info(struct mwifiex_private *priv,
  */
 static int
 mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
-                            u8 *mac, struct station_info *sinfo)
+                            const u8 *mac, struct station_info *sinfo)
 {
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
 
@@ -1270,7 +1270,7 @@ static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy,
  */
 static int
 mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
-                            u8 *mac)
+                            const u8 *mac)
 {
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
        struct mwifiex_sta_node *sta_node;
@@ -2629,7 +2629,7 @@ static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy,
  */
 static int
 mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
-                          u8 *peer, u8 action_code, u8 dialog_token,
+                          const u8 *peer, u8 action_code, u8 dialog_token,
                           u16 status_code, u32 peer_capability,
                           const u8 *extra_ies, size_t extra_ies_len)
 {
@@ -2701,7 +2701,7 @@ mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
 
 static int
 mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
-                          u8 *peer, enum nl80211_tdls_operation action)
+                          const u8 *peer, enum nl80211_tdls_operation action)
 {
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
 
@@ -2748,9 +2748,8 @@ mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
 }
 
 static int
-mwifiex_cfg80211_add_station(struct wiphy *wiphy,
-                            struct net_device *dev,
-                            u8 *mac, struct station_parameters *params)
+mwifiex_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev,
+                            const u8 *mac, struct station_parameters *params)
 {
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
 
@@ -2765,9 +2764,9 @@ mwifiex_cfg80211_add_station(struct wiphy *wiphy,
 }
 
 static int
-mwifiex_cfg80211_change_station(struct wiphy *wiphy,
-                               struct net_device *dev,
-                               u8 *mac, struct station_parameters *params)
+mwifiex_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev,
+                               const u8 *mac,
+                               struct station_parameters *params)
 {
        int ret;
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
index 1062c918a7bffb19cf93c1aba0daa4490856ba65..8dee6c86f4f1dc91e65978b6f7443ac9f00c2118 100644 (file)
@@ -955,8 +955,6 @@ mwifiex_cmd_timeout_func(unsigned long function_context)
                        adapter->cmd_wait_q.status = -ETIMEDOUT;
                        wake_up_interruptible(&adapter->cmd_wait_q.wait);
                        mwifiex_cancel_pending_ioctl(adapter);
-                       /* reset cmd_sent flag to unblock new commands */
-                       adapter->cmd_sent = false;
                }
        }
        if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING)
index b8a49aad12fd662434ce2a29aaa8edecfb52ba0b..7b419bbcd5444f5c5abdf40ffb2368087b77e89a 100644 (file)
@@ -256,6 +256,29 @@ free_and_exit:
        return ret;
 }
 
+/*
+ * Proc firmware dump read handler.
+ *
+ * This function is called when the 'fw_dump' file is opened for
+ * reading.
+ * This function dumps firmware memory in different files
+ * (ex. DTCM, ITCM, SQRAM etc.) based on the the segments for
+ * debugging.
+ */
+static ssize_t
+mwifiex_fw_dump_read(struct file *file, char __user *ubuf,
+                    size_t count, loff_t *ppos)
+{
+       struct mwifiex_private *priv = file->private_data;
+
+       if (!priv->adapter->if_ops.fw_dump)
+               return -EIO;
+
+       priv->adapter->if_ops.fw_dump(priv->adapter);
+
+       return 0;
+}
+
 /*
  * Proc getlog file read handler.
  *
@@ -699,6 +722,7 @@ static const struct file_operations mwifiex_dfs_##name##_fops = {       \
 MWIFIEX_DFS_FILE_READ_OPS(info);
 MWIFIEX_DFS_FILE_READ_OPS(debug);
 MWIFIEX_DFS_FILE_READ_OPS(getlog);
+MWIFIEX_DFS_FILE_READ_OPS(fw_dump);
 MWIFIEX_DFS_FILE_OPS(regrdwr);
 MWIFIEX_DFS_FILE_OPS(rdeeprom);
 
@@ -722,6 +746,7 @@ mwifiex_dev_debugfs_init(struct mwifiex_private *priv)
        MWIFIEX_DFS_ADD_FILE(getlog);
        MWIFIEX_DFS_ADD_FILE(regrdwr);
        MWIFIEX_DFS_ADD_FILE(rdeeprom);
+       MWIFIEX_DFS_ADD_FILE(fw_dump);
 }
 
 /*
index e7b3e16e5d34f1f8703ec2f6d4e21e9388c49edd..38da6ff6f41623618efa22add335ffef1fa46828 100644 (file)
 #define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED      2
 #define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED      16
 
-#define MWIFIEX_STA_AMPDU_DEF_TXWINSIZE        16
-#define MWIFIEX_STA_AMPDU_DEF_RXWINSIZE        32
+#define MWIFIEX_STA_AMPDU_DEF_TXWINSIZE        64
+#define MWIFIEX_STA_AMPDU_DEF_RXWINSIZE        64
 #define MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE        32
 #define MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE        16
-#define MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE   32
-#define MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE   48
+#define MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE   64
+#define MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE   64
 #define MWIFIEX_11AC_UAP_AMPDU_DEF_TXWINSIZE   48
 #define MWIFIEX_11AC_UAP_AMPDU_DEF_RXWINSIZE   32
 
index b485dc1ae5ebc42c5606e37723189d20e7d98b16..3175dd04834b9960698c67e750fc3d8928075088 100644 (file)
@@ -169,6 +169,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define TLV_TYPE_GWK_CIPHER         (PROPRIETARY_TLV_BASE_ID + 146)
 #define TLV_TYPE_COALESCE_RULE      (PROPRIETARY_TLV_BASE_ID + 154)
 #define TLV_TYPE_KEY_PARAM_V2       (PROPRIETARY_TLV_BASE_ID + 156)
+#define TLV_TYPE_TDLS_IDLE_TIMEOUT  (PROPRIETARY_TLV_BASE_ID + 194)
 #define TLV_TYPE_FW_API_REV         (PROPRIETARY_TLV_BASE_ID + 199)
 
 #define MWIFIEX_TX_DATA_BUF_SIZE_2K        2048
@@ -229,6 +230,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define ISENABLED_40MHZ_INTOLERANT(Dot11nDevCap) (Dot11nDevCap & BIT(8))
 #define ISSUPP_RXLDPC(Dot11nDevCap) (Dot11nDevCap & BIT(22))
 #define ISSUPP_BEAMFORMING(Dot11nDevCap) (Dot11nDevCap & BIT(30))
+#define ISALLOWED_CHANWIDTH40(ht_param) (ht_param & BIT(2))
 
 /* httxcfg bitmap
  * 0           reserved
@@ -403,7 +405,7 @@ enum P2P_MODES {
 #define HS_CFG_CANCEL                  0xffffffff
 #define HS_CFG_COND_DEF                        0x00000000
 #define HS_CFG_GPIO_DEF                        0xff
-#define HS_CFG_GAP_DEF                 0
+#define HS_CFG_GAP_DEF                 0xff
 #define HS_CFG_COND_BROADCAST_DATA     0x00000001
 #define HS_CFG_COND_UNICAST_DATA       0x00000002
 #define HS_CFG_COND_MAC_EVENT          0x00000004
@@ -487,6 +489,7 @@ enum P2P_MODES {
 #define EVENT_UAP_MIC_COUNTERMEASURES   0x0000004c
 #define EVENT_HOSTWAKE_STAIE           0x0000004d
 #define EVENT_CHANNEL_SWITCH_ANN        0x00000050
+#define EVENT_TDLS_GENERIC_EVENT        0x00000052
 #define EVENT_EXT_SCAN_REPORT           0x00000058
 #define EVENT_REMAIN_ON_CHAN_EXPIRED    0x0000005f
 
@@ -519,6 +522,7 @@ enum P2P_MODES {
 #define ACT_TDLS_DELETE            0x00
 #define ACT_TDLS_CREATE            0x01
 #define ACT_TDLS_CONFIG            0x02
+#define TDLS_EVENT_LINK_TEAR_DOWN  3
 
 #define MWIFIEX_FW_V15            15
 
@@ -535,6 +539,7 @@ struct mwifiex_ie_types_data {
 #define MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET 0x01
 #define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08
 #define MWIFIEX_TXPD_FLAGS_TDLS_PACKET      0x10
+#define MWIFIEX_RXPD_FLAGS_TDLS_PACKET      0x01
 
 struct txpd {
        u8 bss_type;
@@ -577,7 +582,7 @@ struct rxpd {
         * [Bit 7] Reserved
         */
        u8 ht_info;
-       u8 reserved;
+       u8 flags;
 } __packed;
 
 struct uap_txpd {
@@ -708,6 +713,13 @@ struct mwifiex_ie_types_vendor_param_set {
        u8 ie[MWIFIEX_MAX_VSIE_LEN];
 };
 
+#define MWIFIEX_TDLS_IDLE_TIMEOUT      60
+
+struct mwifiex_ie_types_tdls_idle_timeout {
+       struct mwifiex_ie_types_header header;
+       __le16 value;
+} __packed;
+
 struct mwifiex_ie_types_rsn_param_set {
        struct mwifiex_ie_types_header header;
        u8 rsn_ie[1];
@@ -1745,6 +1757,15 @@ struct host_cmd_ds_802_11_subsc_evt {
        __le16 events;
 } __packed;
 
+struct mwifiex_tdls_generic_event {
+       __le16 type;
+       u8 peer_mac[ETH_ALEN];
+       union {
+               __le16 reason_code;
+               __le16 reserved;
+       } u;
+} __packed;
+
 struct mwifiex_ie {
        __le16 ie_index;
        __le16 mgmt_subtype_mask;
index ee494db5406097c35a0f22b365e2ad2e1ac674f8..1b576722671d5e6f228363c36c94c4ef47867980 100644 (file)
@@ -303,7 +303,7 @@ struct mwifiex_ds_ant_cfg {
        u32 rx_ant;
 };
 
-#define MWIFIEX_NUM_OF_CMD_BUFFER      20
+#define MWIFIEX_NUM_OF_CMD_BUFFER      50
 #define MWIFIEX_SIZE_OF_CMD_BUFFER     2048
 
 enum {
index 9c771b3e99186ffe838f771b217216ebc5a43454..cbabc12fbda390d063218375eb2b4cadc3911b8f 100644 (file)
@@ -521,7 +521,6 @@ done:
                release_firmware(adapter->firmware);
                adapter->firmware = NULL;
        }
-       complete(&adapter->fw_load);
        if (init_failed)
                mwifiex_free_adapter(adapter);
        up(sem);
@@ -535,7 +534,6 @@ static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter)
 {
        int ret;
 
-       init_completion(&adapter->fw_load);
        ret = request_firmware_nowait(THIS_MODULE, 1, adapter->fw_name,
                                      adapter->dev, GFP_KERNEL, adapter,
                                      mwifiex_fw_dpc);
index d53e1e8c9467a62663c4d28df86e623237cdc45f..1398afa8406401c9fd3898716b0849a22946cd42 100644 (file)
@@ -672,6 +672,7 @@ struct mwifiex_if_ops {
        int (*init_fw_port) (struct mwifiex_adapter *);
        int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *);
        void (*card_reset) (struct mwifiex_adapter *);
+       void (*fw_dump)(struct mwifiex_adapter *);
        int (*clean_pcie_ring) (struct mwifiex_adapter *adapter);
 };
 
@@ -787,7 +788,6 @@ struct mwifiex_adapter {
        struct mwifiex_wait_queue cmd_wait_q;
        u8 scan_wait_q_woken;
        spinlock_t queue_lock;          /* lock for tx queues */
-       struct completion fw_load;
        u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
        u16 max_mgmt_ie_index;
        u8 scan_delay_cnt;
@@ -910,8 +910,6 @@ int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv,
                                  struct sk_buff *skb);
 int mwifiex_process_sta_event(struct mwifiex_private *);
 int mwifiex_process_uap_event(struct mwifiex_private *);
-struct mwifiex_sta_node *
-mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac);
 void mwifiex_delete_all_station_list(struct mwifiex_private *priv);
 void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb);
 void *mwifiex_process_uap_txpd(struct mwifiex_private *, struct sk_buff *skb);
@@ -1101,7 +1099,7 @@ mwifiex_11h_get_csa_closed_channel(struct mwifiex_private *priv)
                return 0;
 
        /* Clear csa channel, if DFS channel move time has passed */
-       if (jiffies > priv->csa_expire_time) {
+       if (time_after(jiffies, priv->csa_expire_time)) {
                priv->csa_chan = 0;
                priv->csa_expire_time = 0;
        }
@@ -1220,26 +1218,26 @@ void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv);
 extern const struct ethtool_ops mwifiex_ethtool_ops;
 
 void mwifiex_del_all_sta_list(struct mwifiex_private *priv);
-void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac);
+void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac);
 void
 mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies,
                       int ies_len, struct mwifiex_sta_node *node);
 struct mwifiex_sta_node *
-mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac);
+mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac);
 struct mwifiex_sta_node *
-mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac);
-int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, u8 *peer,
+mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac);
+int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer,
                                 u8 action_code, u8 dialog_token,
                                 u16 status_code, const u8 *extra_ies,
                                 size_t extra_ies_len);
-int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv,
-                                u8 *peer, u8 action_code, u8 dialog_token,
-                                u16 status_code, const u8 *extra_ies,
-                                size_t extra_ies_len);
+int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer,
+                                  u8 action_code, u8 dialog_token,
+                                  u16 status_code, const u8 *extra_ies,
+                                  size_t extra_ies_len);
 void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
                                       u8 *buf, int len);
-int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action);
-int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, u8 *mac);
+int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action);
+int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac);
 void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv);
 bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv);
 u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band,
index a7e8b96b2d9024de8c34e5e04b317c66d2e22820..574d4b59746801cc34ac78e6e4550c7d92aa6e9d 100644 (file)
@@ -221,9 +221,6 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev)
        if (!adapter || !adapter->priv_num)
                return;
 
-       /* In case driver is removed when asynchronous FW load is in progress */
-       wait_for_completion(&adapter->fw_load);
-
        if (user_rmmod) {
 #ifdef CONFIG_PM_SLEEP
                if (adapter->is_suspended)
@@ -1074,6 +1071,7 @@ static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter)
  * is mapped to PCI device memory. Tx ring pointers are advanced accordingly.
  * Download ready interrupt to FW is deffered if Tx ring is not full and
  * additional payload can be accomodated.
+ * Caller must ensure tx_param parameter to this function is not NULL.
  */
 static int
 mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb,
index 7b3af3d29ded478ad658eed5a3836403d6dd7542..45c5b3450cf5c719886483c9e0853c327fd62811 100644 (file)
@@ -29,9 +29,6 @@
 #define MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN   14
 
 #define MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD      4
-#define MWIFIEX_LIMIT_1_CHANNEL_PER_SCAN_CMD   15
-#define MWIFIEX_LIMIT_2_CHANNELS_PER_SCAN_CMD  27
-#define MWIFIEX_LIMIT_3_CHANNELS_PER_SCAN_CMD  35
 
 /* Memory needed to store a max sized Channel List TLV for a firmware scan */
 #define CHAN_TLV_MAX_SIZE  (sizeof(struct mwifiex_ie_types_header)         \
@@ -1055,20 +1052,10 @@ mwifiex_config_scan(struct mwifiex_private *priv,
 
        /*
         * In associated state we will reduce the number of channels scanned per
-        * scan command to avoid any traffic delay/loss. This number is decided
-        * based on total number of channels to be scanned due to constraints
-        * of command buffers.
+        * scan command to 1 to avoid any traffic delay/loss.
         */
-       if (priv->media_connected) {
-               if (chan_num < MWIFIEX_LIMIT_1_CHANNEL_PER_SCAN_CMD)
+       if (priv->media_connected)
                        *max_chan_per_scan = 1;
-               else if (chan_num < MWIFIEX_LIMIT_2_CHANNELS_PER_SCAN_CMD)
-                       *max_chan_per_scan = 2;
-               else if (chan_num < MWIFIEX_LIMIT_3_CHANNELS_PER_SCAN_CMD)
-                       *max_chan_per_scan = 3;
-               else
-                       *max_chan_per_scan = 4;
-       }
 }
 
 /*
@@ -1353,23 +1340,17 @@ int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter,
                                              bss_entry->beacon_buf);
                        break;
                case WLAN_EID_BSS_COEX_2040:
-                       bss_entry->bcn_bss_co_2040 = current_ptr +
-                               sizeof(struct ieee_types_header);
-                       bss_entry->bss_co_2040_offset = (u16) (current_ptr +
-                                       sizeof(struct ieee_types_header) -
-                                               bss_entry->beacon_buf);
+                       bss_entry->bcn_bss_co_2040 = current_ptr;
+                       bss_entry->bss_co_2040_offset =
+                               (u16) (current_ptr - bss_entry->beacon_buf);
                        break;
                case WLAN_EID_EXT_CAPABILITY:
-                       bss_entry->bcn_ext_cap = current_ptr +
-                               sizeof(struct ieee_types_header);
-                       bss_entry->ext_cap_offset = (u16) (current_ptr +
-                                       sizeof(struct ieee_types_header) -
-                                       bss_entry->beacon_buf);
+                       bss_entry->bcn_ext_cap = current_ptr;
+                       bss_entry->ext_cap_offset =
+                               (u16) (current_ptr - bss_entry->beacon_buf);
                        break;
                case WLAN_EID_OPMODE_NOTIF:
-                       bss_entry->oper_mode =
-                               (void *)(current_ptr +
-                                        sizeof(struct ieee_types_header));
+                       bss_entry->oper_mode = (void *)current_ptr;
                        bss_entry->oper_mode_offset =
                                        (u16)((u8 *)bss_entry->oper_mode -
                                              bss_entry->beacon_buf);
@@ -1757,6 +1738,19 @@ mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info,
        return 0;
 }
 
+static void mwifiex_complete_scan(struct mwifiex_private *priv)
+{
+       struct mwifiex_adapter *adapter = priv->adapter;
+
+       if (adapter->curr_cmd->wait_q_enabled) {
+               adapter->cmd_wait_q.status = 0;
+               if (!priv->scan_request) {
+                       dev_dbg(adapter->dev, "complete internal scan\n");
+                       mwifiex_complete_cmd(adapter, adapter->curr_cmd);
+               }
+       }
+}
+
 static void mwifiex_check_next_scan_command(struct mwifiex_private *priv)
 {
        struct mwifiex_adapter *adapter = priv->adapter;
@@ -1770,16 +1764,9 @@ static void mwifiex_check_next_scan_command(struct mwifiex_private *priv)
                adapter->scan_processing = false;
                spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
 
-               /* Need to indicate IOCTL complete */
-               if (adapter->curr_cmd->wait_q_enabled) {
-                       adapter->cmd_wait_q.status = 0;
-                       if (!priv->scan_request) {
-                               dev_dbg(adapter->dev,
-                                       "complete internal scan\n");
-                               mwifiex_complete_cmd(adapter,
-                                                    adapter->curr_cmd);
-                       }
-               }
+               if (!adapter->ext_scan)
+                       mwifiex_complete_scan(priv);
+
                if (priv->report_scan_result)
                        priv->report_scan_result = false;
 
@@ -1984,6 +1971,9 @@ int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv,
 int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv)
 {
        dev_dbg(priv->adapter->dev, "info: EXT scan returns successfully\n");
+
+       mwifiex_complete_scan(priv);
+
        return 0;
 }
 
index d206f04d499498d6d9c7ba92a80685588ae7bc96..4ce3d7b33991ace2cdd3ba0bcfe50c728e59220d 100644 (file)
@@ -85,6 +85,8 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
                card->supports_sdio_new_mode = data->supports_sdio_new_mode;
                card->has_control_mask = data->has_control_mask;
                card->tx_buf_size = data->tx_buf_size;
+               card->mp_tx_agg_buf_size = data->mp_tx_agg_buf_size;
+               card->mp_rx_agg_buf_size = data->mp_rx_agg_buf_size;
        }
 
        sdio_claim_host(func);
@@ -177,9 +179,6 @@ mwifiex_sdio_remove(struct sdio_func *func)
        if (!adapter || !adapter->priv_num)
                return;
 
-       /* In case driver is removed when asynchronous FW load is in progress */
-       wait_for_completion(&adapter->fw_load);
-
        if (user_rmmod) {
                if (adapter->is_suspended)
                        mwifiex_sdio_resume(adapter->dev);
@@ -1679,8 +1678,12 @@ static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter,
        if (ret) {
                if (type == MWIFIEX_TYPE_CMD)
                        adapter->cmd_sent = false;
-               if (type == MWIFIEX_TYPE_DATA)
+               if (type == MWIFIEX_TYPE_DATA) {
                        adapter->data_sent = false;
+                       /* restore curr_wr_port in error cases */
+                       card->curr_wr_port = port;
+                       card->mp_wr_bitmap |= (u32)(1 << card->curr_wr_port);
+               }
        } else {
                if (type == MWIFIEX_TYPE_DATA) {
                        if (!(card->mp_wr_bitmap & (1 << card->curr_wr_port)))
@@ -1842,8 +1845,8 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter)
        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);
+                                            card->mp_tx_agg_buf_size,
+                                            card->mp_rx_agg_buf_size);
        if (ret) {
                dev_err(adapter->dev, "failed to alloc sdio mp-a buffers\n");
                kfree(card->mp_regs);
index c71201b2e2a333c20f926bacb4842395230c6926..6eea30b43ed714f3bd81f9453a5008001a11b33d 100644 (file)
 #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 */
-
-/* Multi port RX aggregation buffer size */
-#define SDIO_MP_RX_AGGR_DEF_BUF_SIZE        (16384)    /* 16K */
+#define MWIFIEX_MP_AGGR_BUF_SIZE_16K   (16384)
+#define MWIFIEX_MP_AGGR_BUF_SIZE_32K   (32768)
 
 /* Misc. Config Register : Auto Re-enable interrupts */
 #define AUTO_RE_ENABLE_INT              BIT(4)
@@ -234,6 +232,8 @@ struct sdio_mmc_card {
        bool supports_sdio_new_mode;
        bool has_control_mask;
        u16 tx_buf_size;
+       u32 mp_tx_agg_buf_size;
+       u32 mp_rx_agg_buf_size;
 
        u32 mp_rd_bitmap;
        u32 mp_wr_bitmap;
@@ -258,6 +258,8 @@ struct mwifiex_sdio_device {
        bool supports_sdio_new_mode;
        bool has_control_mask;
        u16 tx_buf_size;
+       u32 mp_tx_agg_buf_size;
+       u32 mp_rx_agg_buf_size;
 };
 
 static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = {
@@ -315,6 +317,8 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = {
        .supports_sdio_new_mode = false,
        .has_control_mask = true,
        .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
+       .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
+       .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
 };
 
 static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
@@ -325,6 +329,8 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
        .supports_sdio_new_mode = false,
        .has_control_mask = true,
        .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
+       .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
+       .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
 };
 
 static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
@@ -335,6 +341,8 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
        .supports_sdio_new_mode = false,
        .has_control_mask = true,
        .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
+       .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
+       .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
 };
 
 static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
@@ -345,6 +353,8 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
        .supports_sdio_new_mode = true,
        .has_control_mask = false,
        .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
+       .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K,
+       .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K,
 };
 
 /*
index e3cac1495cc705bcf3c05b479a16981578deff98..88202ce0c13965fdff679c506a3772ef287e6d54 100644 (file)
@@ -1546,6 +1546,7 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv,
        struct mwifiex_ie_types_extcap *extcap;
        struct mwifiex_ie_types_vhtcap *vht_capab;
        struct mwifiex_ie_types_aid *aid;
+       struct mwifiex_ie_types_tdls_idle_timeout *timeout;
        u8 *pos, qos_info;
        u16 config_len = 0;
        struct station_parameters *params = priv->sta_params;
@@ -1643,6 +1644,12 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv,
                        config_len += sizeof(struct mwifiex_ie_types_aid);
                }
 
+               timeout = (void *)(pos + config_len);
+               timeout->header.type = cpu_to_le16(TLV_TYPE_TDLS_IDLE_TIMEOUT);
+               timeout->header.len = cpu_to_le16(sizeof(timeout->value));
+               timeout->value = cpu_to_le16(MWIFIEX_TDLS_IDLE_TIMEOUT);
+               config_len += sizeof(struct mwifiex_ie_types_tdls_idle_timeout);
+
                break;
        default:
                dev_err(priv->adapter->dev, "Unknown TDLS operation\n");
index bfebb0144df5ac5ae88bd78dd909888d4bb0a0ea..577f2979ed8f2bcacbacc6af6ce9af03d3f86137 100644 (file)
@@ -865,14 +865,20 @@ static int mwifiex_ret_tdls_oper(struct mwifiex_private *priv,
 
        switch (action) {
        case ACT_TDLS_DELETE:
-               if (reason)
-                       dev_err(priv->adapter->dev,
-                               "TDLS link delete for %pM failed: reason %d\n",
-                               cmd_tdls_oper->peer_mac, reason);
-               else
+               if (reason) {
+                       if (!node || reason == TDLS_ERR_LINK_NONEXISTENT)
+                               dev_dbg(priv->adapter->dev,
+                                       "TDLS link delete for %pM failed: reason %d\n",
+                                       cmd_tdls_oper->peer_mac, reason);
+                       else
+                               dev_err(priv->adapter->dev,
+                                       "TDLS link delete for %pM failed: reason %d\n",
+                                       cmd_tdls_oper->peer_mac, reason);
+               } else {
                        dev_dbg(priv->adapter->dev,
-                               "TDLS link config for %pM successful\n",
+                               "TDLS link delete for %pM successful\n",
                                cmd_tdls_oper->peer_mac);
+               }
                break;
        case ACT_TDLS_CREATE:
                if (reason) {
index 368450cc56c7d9e19b8c2a74c47d43a9c5c91b1f..f6395ef11a721b8fc6d8ee797fb34a72b7c2f43d 100644 (file)
@@ -134,6 +134,46 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code)
                netif_carrier_off(priv->netdev);
 }
 
+static int mwifiex_parse_tdls_event(struct mwifiex_private *priv,
+                                   struct sk_buff *event_skb)
+{
+       int ret = 0;
+       struct mwifiex_adapter *adapter = priv->adapter;
+       struct mwifiex_sta_node *sta_ptr;
+       struct mwifiex_tdls_generic_event *tdls_evt =
+                       (void *)event_skb->data + sizeof(adapter->event_cause);
+
+       /* reserved 2 bytes are not mandatory in tdls event */
+       if (event_skb->len < (sizeof(struct mwifiex_tdls_generic_event) -
+                             sizeof(u16) - sizeof(adapter->event_cause))) {
+               dev_err(adapter->dev, "Invalid event length!\n");
+               return -1;
+       }
+
+       sta_ptr = mwifiex_get_sta_entry(priv, tdls_evt->peer_mac);
+       if (!sta_ptr) {
+               dev_err(adapter->dev, "cannot get sta entry!\n");
+               return -1;
+       }
+
+       switch (le16_to_cpu(tdls_evt->type)) {
+       case TDLS_EVENT_LINK_TEAR_DOWN:
+               cfg80211_tdls_oper_request(priv->netdev,
+                                          tdls_evt->peer_mac,
+                                          NL80211_TDLS_TEARDOWN,
+                                          le16_to_cpu(tdls_evt->u.reason_code),
+                                          GFP_KERNEL);
+               ret = mwifiex_tdls_oper(priv, tdls_evt->peer_mac,
+                                       MWIFIEX_TDLS_DISABLE_LINK);
+               queue_work(adapter->workqueue, &adapter->main_work);
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
 /*
  * This function handles events generated by firmware.
  *
@@ -459,6 +499,10 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
                        false);
                break;
 
+       case EVENT_TDLS_GENERIC_EVENT:
+               ret = mwifiex_parse_tdls_event(priv, adapter->event_skb);
+               break;
+
        default:
                dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
                        eventcause);
index ed26387eccf56db59bca98f7ed6fd7e6a4065aae..8b639d7fe6df263814901554a2c43363be6791e9 100644 (file)
@@ -183,6 +183,7 @@ int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv,
        struct rx_packet_hdr *rx_pkt_hdr;
        u8 ta[ETH_ALEN];
        u16 rx_pkt_type, rx_pkt_offset, rx_pkt_length, seq_num;
+       struct mwifiex_sta_node *sta_ptr;
 
        local_rx_pd = (struct rxpd *) (skb->data);
        rx_pkt_type = le16_to_cpu(local_rx_pd->rx_pkt_type);
@@ -213,14 +214,25 @@ int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv,
         * If the packet is not an unicast packet then send the packet
         * directly to os. Don't pass thru rx reordering
         */
-       if (!IS_11N_ENABLED(priv) ||
+       if ((!IS_11N_ENABLED(priv) &&
+            !(ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+              !(local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET))) ||
            !ether_addr_equal_unaligned(priv->curr_addr, rx_pkt_hdr->eth803_hdr.h_dest)) {
                mwifiex_process_rx_packet(priv, skb);
                return ret;
        }
 
-       if (mwifiex_queuing_ra_based(priv)) {
+       if (mwifiex_queuing_ra_based(priv) ||
+           (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+            local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET)) {
                memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN);
+               if (local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET &&
+                   local_rx_pd->priority < MAX_NUM_TID) {
+                       sta_ptr = mwifiex_get_sta_entry(priv, ta);
+                       if (sta_ptr)
+                               sta_ptr->rx_seq[local_rx_pd->priority] =
+                                             le16_to_cpu(local_rx_pd->seq_num);
+               }
        } else {
                if (rx_pkt_type != PKT_TYPE_BAR)
                        priv->rx_seq[local_rx_pd->priority] = seq_num;
index 1236a5de7bca833adfd0eab1ed6cce865047479b..5fce7e78a36e773c28875a7636a666b50ced36d5 100644 (file)
@@ -128,6 +128,7 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags)
 {
        struct mwifiex_adapter *adapter = priv->adapter;
        struct txpd *local_tx_pd;
+       struct mwifiex_tx_param tx_param;
 /* sizeof(struct txpd) + Interface specific header */
 #define NULL_PACKET_HDR 64
        u32 data_len = NULL_PACKET_HDR;
@@ -168,8 +169,9 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags)
                                                   skb, NULL);
        } else {
                skb_push(skb, INTF_HEADER_LEN);
+               tx_param.next_pkt_len = 0;
                ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA,
-                                                  skb, NULL);
+                                                  skb, &tx_param);
        }
        switch (ret) {
        case -EBUSY:
index 97662a1ba58cf06a08ff3372f4808e5a0c7cbcd9..e73034fbbde9263b8e234ee7cd7747a1404c8a40 100644 (file)
@@ -25,8 +25,8 @@
 #define TDLS_RESP_FIX_LEN     8
 #define TDLS_CONFIRM_FIX_LEN  6
 
-static void
-mwifiex_restore_tdls_packets(struct mwifiex_private *priv, u8 *mac, u8 status)
+static void mwifiex_restore_tdls_packets(struct mwifiex_private *priv,
+                                        const u8 *mac, u8 status)
 {
        struct mwifiex_ra_list_tbl *ra_list;
        struct list_head *tid_list;
@@ -84,7 +84,8 @@ mwifiex_restore_tdls_packets(struct mwifiex_private *priv, u8 *mac, u8 status)
        return;
 }
 
-static void mwifiex_hold_tdls_packets(struct mwifiex_private *priv, u8 *mac)
+static void mwifiex_hold_tdls_packets(struct mwifiex_private *priv,
+                                     const u8 *mac)
 {
        struct mwifiex_ra_list_tbl *ra_list;
        struct list_head *ra_list_head;
@@ -185,8 +186,50 @@ static int mwifiex_tdls_add_vht_capab(struct mwifiex_private *priv,
        return 0;
 }
 
+static int
+mwifiex_tdls_add_ht_oper(struct mwifiex_private *priv, const u8 *mac,
+                        u8 vht_enabled, struct sk_buff *skb)
+{
+       struct ieee80211_ht_operation *ht_oper;
+       struct mwifiex_sta_node *sta_ptr;
+       struct mwifiex_bssdescriptor *bss_desc =
+                                       &priv->curr_bss_params.bss_descriptor;
+       u8 *pos;
+
+       sta_ptr = mwifiex_get_sta_entry(priv, mac);
+       if (unlikely(!sta_ptr)) {
+               dev_warn(priv->adapter->dev,
+                        "TDLS peer station not found in list\n");
+               return -1;
+       }
+
+       pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_operation) + 2);
+       *pos++ = WLAN_EID_HT_OPERATION;
+       *pos++ = sizeof(struct ieee80211_ht_operation);
+       ht_oper = (void *)pos;
+
+       ht_oper->primary_chan = bss_desc->channel;
+
+       /* follow AP's channel bandwidth */
+       if (ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) &&
+           bss_desc->bcn_ht_cap &&
+           ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_oper->ht_param))
+               ht_oper->ht_param = bss_desc->bcn_ht_oper->ht_param;
+
+       if (vht_enabled) {
+               ht_oper->ht_param =
+                         mwifiex_get_sec_chan_offset(bss_desc->channel);
+               ht_oper->ht_param |= BIT(2);
+       }
+
+       memcpy(&sta_ptr->tdls_cap.ht_oper, ht_oper,
+              sizeof(struct ieee80211_ht_operation));
+
+       return 0;
+}
+
 static int mwifiex_tdls_add_vht_oper(struct mwifiex_private *priv,
-                                    u8 *mac, struct sk_buff *skb)
+                                    const u8 *mac, struct sk_buff *skb)
 {
        struct mwifiex_bssdescriptor *bss_desc;
        struct ieee80211_vht_operation *vht_oper;
@@ -325,8 +368,9 @@ static void mwifiex_tdls_add_qos_capab(struct sk_buff *skb)
 }
 
 static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv,
-                            u8 *peer, u8 action_code, u8 dialog_token,
-                            u16 status_code, struct sk_buff *skb)
+                                       const u8 *peer, u8 action_code,
+                                       u8 dialog_token,
+                                       u16 status_code, struct sk_buff *skb)
 {
        struct ieee80211_tdls_data *tf;
        int ret;
@@ -428,6 +472,17 @@ static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv,
                                dev_kfree_skb_any(skb);
                                return ret;
                        }
+                       ret = mwifiex_tdls_add_ht_oper(priv, peer, 1, skb);
+                       if (ret) {
+                               dev_kfree_skb_any(skb);
+                               return ret;
+                       }
+               } else {
+                       ret = mwifiex_tdls_add_ht_oper(priv, peer, 0, skb);
+                       if (ret) {
+                               dev_kfree_skb_any(skb);
+                               return ret;
+                       }
                }
                break;
 
@@ -453,7 +508,8 @@ static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv,
 }
 
 static void
-mwifiex_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, u8 *peer, u8 *bssid)
+mwifiex_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr,
+                        const u8 *peer, const u8 *bssid)
 {
        struct ieee80211_tdls_lnkie *lnkid;
 
@@ -467,8 +523,8 @@ mwifiex_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, u8 *peer, u8 *bssid)
        memcpy(lnkid->resp_sta, peer, ETH_ALEN);
 }
 
-int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv,
-                                u8 *peer, u8 action_code, u8 dialog_token,
+int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer,
+                                u8 action_code, u8 dialog_token,
                                 u16 status_code, const u8 *extra_ies,
                                 size_t extra_ies_len)
 {
@@ -560,7 +616,8 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv,
 }
 
 static int
-mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, u8 *peer,
+mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv,
+                                   const u8 *peer,
                                    u8 action_code, u8 dialog_token,
                                    u16 status_code, struct sk_buff *skb)
 {
@@ -638,10 +695,10 @@ mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, u8 *peer,
        return 0;
 }
 
-int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv,
-                                u8 *peer, u8 action_code, u8 dialog_token,
-                                u16 status_code, const u8 *extra_ies,
-                                size_t extra_ies_len)
+int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer,
+                                  u8 action_code, u8 dialog_token,
+                                  u16 status_code, const u8 *extra_ies,
+                                  size_t extra_ies_len)
 {
        struct sk_buff *skb;
        struct mwifiex_txinfo *tx_info;
@@ -848,7 +905,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
 }
 
 static int
-mwifiex_tdls_process_config_link(struct mwifiex_private *priv, u8 *peer)
+mwifiex_tdls_process_config_link(struct mwifiex_private *priv, const u8 *peer)
 {
        struct mwifiex_sta_node *sta_ptr;
        struct mwifiex_ds_tdls_oper tdls_oper;
@@ -869,7 +926,7 @@ mwifiex_tdls_process_config_link(struct mwifiex_private *priv, u8 *peer)
 }
 
 static int
-mwifiex_tdls_process_create_link(struct mwifiex_private *priv, u8 *peer)
+mwifiex_tdls_process_create_link(struct mwifiex_private *priv, const u8 *peer)
 {
        struct mwifiex_sta_node *sta_ptr;
        struct mwifiex_ds_tdls_oper tdls_oper;
@@ -896,7 +953,7 @@ mwifiex_tdls_process_create_link(struct mwifiex_private *priv, u8 *peer)
 }
 
 static int
-mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, u8 *peer)
+mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, const u8 *peer)
 {
        struct mwifiex_sta_node *sta_ptr;
        struct mwifiex_ds_tdls_oper tdls_oper;
@@ -925,7 +982,7 @@ mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, u8 *peer)
 }
 
 static int
-mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, u8 *peer)
+mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, const u8 *peer)
 {
        struct mwifiex_sta_node *sta_ptr;
        struct ieee80211_mcs_info mcs;
@@ -982,7 +1039,7 @@ mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, u8 *peer)
        return 0;
 }
 
-int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action)
+int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action)
 {
        switch (action) {
        case MWIFIEX_TDLS_ENABLE_LINK:
@@ -997,7 +1054,7 @@ int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action)
        return 0;
 }
 
-int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, u8 *mac)
+int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac)
 {
        struct mwifiex_sta_node *sta_ptr;
 
index 9be6544bddedf9371e79fd468ebb0aa2dbcabe54..32643555dd2a32a302d1301427e463d877c8260a 100644 (file)
@@ -175,17 +175,19 @@ mwifiex_set_ht_params(struct mwifiex_private *priv,
                switch (GET_RXSTBC(cap_info)) {
                case MWIFIEX_RX_STBC1:
                        /* HT_CAP 1X1 mode */
-                       memset(&bss_cfg->ht_cap.mcs, 0xff, 1);
+                       bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff;
                        break;
                case MWIFIEX_RX_STBC12: /* fall through */
                case MWIFIEX_RX_STBC123:
                        /* HT_CAP 2X2 mode */
-                       memset(&bss_cfg->ht_cap.mcs, 0xff, 2);
+                       bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff;
+                       bss_cfg->ht_cap.mcs.rx_mask[1] = 0xff;
                        break;
                default:
                        dev_warn(priv->adapter->dev,
                                 "Unsupported RX-STBC, default to 2x2\n");
-                       memset(&bss_cfg->ht_cap.mcs, 0xff, 2);
+                       bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff;
+                       bss_cfg->ht_cap.mcs.rx_mask[1] = 0xff;
                        break;
                }
                priv->ap_11n_enabled = 1;
index edbe4aff00d85b569534372ea34e7e017552b234..a8ce8130cfaeeda08a2a08f7b693540fe79d9f85 100644 (file)
@@ -22,9 +22,9 @@
 
 #define USB_VERSION    "1.0"
 
+static u8 user_rmmod;
 static struct mwifiex_if_ops usb_ops;
 static struct semaphore add_remove_card_sem;
-static struct usb_card_rec *usb_card;
 
 static struct usb_device_id mwifiex_usb_table[] = {
        /* 8797 */
@@ -532,28 +532,38 @@ static int mwifiex_usb_resume(struct usb_interface *intf)
 static void mwifiex_usb_disconnect(struct usb_interface *intf)
 {
        struct usb_card_rec *card = usb_get_intfdata(intf);
+       struct mwifiex_adapter *adapter;
 
-       if (!card) {
-               pr_err("%s: card is NULL\n", __func__);
+       if (!card || !card->adapter) {
+               pr_err("%s: card or card->adapter is NULL\n", __func__);
                return;
        }
 
-       mwifiex_usb_free(card);
+       adapter = card->adapter;
+       if (!adapter->priv_num)
+               return;
 
-       if (card->adapter) {
-               struct mwifiex_adapter *adapter = card->adapter;
+       if (user_rmmod) {
+#ifdef CONFIG_PM
+               if (adapter->is_suspended)
+                       mwifiex_usb_resume(intf);
+#endif
 
-               if (!adapter->priv_num)
-                       return;
+               mwifiex_deauthenticate_all(adapter);
 
-               dev_dbg(adapter->dev, "%s: removing card\n", __func__);
-               mwifiex_remove_card(adapter, &add_remove_card_sem);
+               mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter,
+                                                         MWIFIEX_BSS_ROLE_ANY),
+                                        MWIFIEX_FUNC_SHUTDOWN);
        }
 
+       mwifiex_usb_free(card);
+
+       dev_dbg(adapter->dev, "%s: removing card\n", __func__);
+       mwifiex_remove_card(adapter, &add_remove_card_sem);
+
        usb_set_intfdata(intf, NULL);
        usb_put_dev(interface_to_usbdev(intf));
        kfree(card);
-       usb_card = NULL;
 
        return;
 }
@@ -565,6 +575,7 @@ static struct usb_driver mwifiex_usb_driver = {
        .id_table = mwifiex_usb_table,
        .suspend = mwifiex_usb_suspend,
        .resume = mwifiex_usb_resume,
+       .soft_unbind = 1,
 };
 
 static int mwifiex_usb_tx_init(struct mwifiex_adapter *adapter)
@@ -762,7 +773,6 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
 
        card->adapter = adapter;
        adapter->dev = &card->udev->dev;
-       usb_card = card;
 
        switch (le16_to_cpu(card->udev->descriptor.idProduct)) {
        case USB8897_PID_1:
@@ -1025,25 +1035,8 @@ static void mwifiex_usb_cleanup_module(void)
        if (!down_interruptible(&add_remove_card_sem))
                up(&add_remove_card_sem);
 
-       if (usb_card && usb_card->adapter) {
-               struct mwifiex_adapter *adapter = usb_card->adapter;
-
-               /* In case driver is removed when asynchronous FW downloading is
-                * in progress
-                */
-               wait_for_completion(&adapter->fw_load);
-
-#ifdef CONFIG_PM
-               if (adapter->is_suspended)
-                       mwifiex_usb_resume(usb_card->intf);
-#endif
-
-               mwifiex_deauthenticate_all(adapter);
-
-               mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter,
-                                                         MWIFIEX_BSS_ROLE_ANY),
-                                        MWIFIEX_FUNC_SHUTDOWN);
-       }
+       /* set the flag as user is removing this module */
+       user_rmmod = 1;
 
        usb_deregister(&mwifiex_usb_driver);
 }
index c3824e37f3f24a30d951510a75cd4640346620d8..6da5abf52e61a4360b236411b09310205e8316ea 100644 (file)
@@ -259,7 +259,7 @@ int mwifiex_complete_cmd(struct mwifiex_adapter *adapter,
  * NULL is returned if station entry is not found in associated STA list.
  */
 struct mwifiex_sta_node *
-mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac)
+mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac)
 {
        struct mwifiex_sta_node *node;
 
@@ -280,7 +280,7 @@ mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac)
  * If received mac address is NULL, NULL is returned.
  */
 struct mwifiex_sta_node *
-mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac)
+mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac)
 {
        struct mwifiex_sta_node *node;
        unsigned long flags;
@@ -332,7 +332,7 @@ mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies,
 }
 
 /* This function will delete a station entry from station list */
-void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac)
+void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac)
 {
        struct mwifiex_sta_node *node;
        unsigned long flags;
index 0a7cc742aed71e0fd31267305be7a26ec71b8b52..d3671d009f6c3c89c4f6e91e06220edd2c481da9 100644 (file)
@@ -92,7 +92,7 @@ mwifiex_wmm_ac_debug_print(const struct ieee_types_wmm_ac_parameters *ac_param)
  * The function also initializes the list with the provided RA.
  */
 static struct mwifiex_ra_list_tbl *
-mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, u8 *ra)
+mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, const u8 *ra)
 {
        struct mwifiex_ra_list_tbl *ra_list;
 
@@ -139,8 +139,7 @@ static u8 mwifiex_get_random_ba_threshold(void)
  * This function allocates and adds a RA list for all TIDs
  * with the given RA.
  */
-void
-mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra)
+void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra)
 {
        int i;
        struct mwifiex_ra_list_tbl *ra_list;
@@ -164,6 +163,7 @@ mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra)
                if (!mwifiex_queuing_ra_based(priv)) {
                        if (mwifiex_get_tdls_link_status(priv, ra) ==
                            TDLS_SETUP_COMPLETE) {
+                               ra_list->tdls_link = true;
                                ra_list->is_11n_enabled =
                                        mwifiex_tdls_peer_11n_enabled(priv, ra);
                        } else {
@@ -426,15 +426,6 @@ mwifiex_wmm_init(struct mwifiex_adapter *adapter)
                                                        priv->tos_to_tid_inv[i];
                }
 
-               priv->aggr_prio_tbl[6].amsdu
-                                       = priv->aggr_prio_tbl[6].ampdu_ap
-                                       = priv->aggr_prio_tbl[6].ampdu_user
-                                       = BA_STREAM_NOT_ALLOWED;
-
-               priv->aggr_prio_tbl[7].amsdu = priv->aggr_prio_tbl[7].ampdu_ap
-                                       = priv->aggr_prio_tbl[7].ampdu_user
-                                       = BA_STREAM_NOT_ALLOWED;
-
                mwifiex_set_ba_params(priv);
                mwifiex_reset_11n_rx_seq_num(priv);
 
@@ -575,7 +566,7 @@ mwifiex_clean_txrx(struct mwifiex_private *priv)
  */
 static struct mwifiex_ra_list_tbl *
 mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid,
-                           u8 *ra_addr)
+                           const u8 *ra_addr)
 {
        struct mwifiex_ra_list_tbl *ra_list;
 
@@ -596,7 +587,8 @@ mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid,
  * retrieved.
  */
 struct mwifiex_ra_list_tbl *
-mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, u8 *ra_addr)
+mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid,
+                           const u8 *ra_addr)
 {
        struct mwifiex_ra_list_tbl *ra_list;
 
@@ -657,7 +649,7 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv,
                if (ntohs(eth_hdr->h_proto) == ETH_P_TDLS)
                        dev_dbg(adapter->dev,
                                "TDLS setup packet for %pM. Don't block\n", ra);
-               else
+               else if (memcmp(priv->cfg_bssid, ra, ETH_ALEN))
                        tdls_status = mwifiex_get_tdls_link_status(priv, ra);
        }
 
index 83e42083ebff8cbc3b248f333111691e8a21fc1d..eca56e371a57bb5afb2df6165c24fbbc4d34e561 100644 (file)
@@ -99,7 +99,7 @@ mwifiex_wmm_is_ra_list_empty(struct list_head *ra_list_hhead)
 
 void mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv,
                                 struct sk_buff *skb);
-void mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra);
+void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra);
 void mwifiex_rotate_priolists(struct mwifiex_private *priv,
                              struct mwifiex_ra_list_tbl *ra, int tid);
 
@@ -123,7 +123,8 @@ void mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv);
 int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv,
                               const struct host_cmd_ds_command *resp);
 struct mwifiex_ra_list_tbl *
-mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, u8 *ra_addr);
+mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid,
+                           const u8 *ra_addr);
 u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid);
 
 #endif /* !_MWIFIEX_WMM_H_ */
index 49300d04efdf0e7221a2b0f64f0475ad6f73ee59..e27e32851f1e50f8116c14c55f3bd2f405aee17f 100644 (file)
@@ -988,8 +988,8 @@ int __orinoco_hw_setup_enc(struct orinoco_private *priv)
  * tsc must be NULL or up to 8 bytes
  */
 int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx,
-                             int set_tx, u8 *key, u8 *rsc, size_t rsc_len,
-                             u8 *tsc, size_t tsc_len)
+                             int set_tx, const u8 *key, const u8 *rsc,
+                             size_t rsc_len, const u8 *tsc, size_t tsc_len)
 {
        struct {
                __le16 idx;
index 8f6831f4e328a8b5761611a6c79965ccdfa372ac..466d1ede76f16ee5436b680631e77047af2798af 100644 (file)
@@ -38,8 +38,8 @@ int __orinoco_hw_set_wap(struct orinoco_private *priv);
 int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv);
 int __orinoco_hw_setup_enc(struct orinoco_private *priv);
 int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx,
-                             int set_tx, u8 *key, u8 *rsc, size_t rsc_len,
-                             u8 *tsc, size_t tsc_len);
+                             int set_tx, const u8 *key, const u8 *rsc,
+                             size_t rsc_len, const u8 *tsc, size_t tsc_len);
 int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx);
 int __orinoco_hw_set_multicast_list(struct orinoco_private *priv,
                                    struct net_device *dev,
index 3ac71339d040a418465e0fd0ecaeb0a2066ddb3a..c90939ced0e483ae04c490c60a5f18eabbea1b9f 100644 (file)
@@ -1673,7 +1673,7 @@ static int ezusb_probe(struct usb_interface *interface,
                firmware.code = fw_entry->data;
        }
        if (firmware.size && firmware.code) {
-               if (ezusb_firmware_download(upriv, &firmware))
+               if (ezusb_firmware_download(upriv, &firmware) < 0)
                        goto error;
        } else {
                err("No firmware to download");
index b7a867b50b9476fb2c54ad9ccbb28815e6fa70e6..6abdaf0aa052253800697eb1631d100683ba2ec7 100644 (file)
@@ -52,9 +52,9 @@ static int orinoco_set_key(struct orinoco_private *priv, int index,
        priv->keys[index].seq_len = seq_len;
 
        if (key_len)
-               memcpy(priv->keys[index].key, key, key_len);
+               memcpy((void *)priv->keys[index].key, key, key_len);
        if (seq_len)
-               memcpy(priv->keys[index].seq, seq, seq_len);
+               memcpy((void *)priv->keys[index].seq, seq, seq_len);
 
        switch (alg) {
        case ORINOCO_ALG_TKIP:
index eede90b63f847934a8a0bc45e63695696d88a82d..7be3a4839640c6eda6b8254fd7b81bbe5d01fd33 100644 (file)
@@ -669,7 +669,8 @@ static unsigned int p54_flush_count(struct p54_common *priv)
        return total;
 }
 
-static void p54_flush(struct ieee80211_hw *dev, u32 queues, bool drop)
+static void p54_flush(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+                     u32 queues, bool drop)
 {
        struct p54_common *priv = dev->priv;
        unsigned int total, i;
index cbf0a589d32af08c392271c7bbc3afa50f8e9c84..8330fa33e50b1e2f933f813ee187c407184780ae 100644 (file)
@@ -343,7 +343,7 @@ static void ray_detach(struct pcmcia_device *link)
        ray_release(link);
 
        local = netdev_priv(dev);
-       del_timer(&local->timer);
+       del_timer_sync(&local->timer);
 
        if (link->priv) {
                unregister_netdev(dev);
index 39d22a154341019fe8038bdfa1f7e1b4cb560cf2..d2a9a08210be1379b4e56ad266c1695ca6486d17 100644 (file)
@@ -517,7 +517,7 @@ static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev,
                                 u8 key_index, bool unicast, bool multicast);
 
 static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev,
-                                       u8 *mac, struct station_info *sinfo);
+                            const u8 *mac, struct station_info *sinfo);
 
 static int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev,
                               int idx, u8 *mac, struct station_info *sinfo);
@@ -2490,7 +2490,7 @@ static void rndis_fill_station_info(struct usbnet *usbdev,
 }
 
 static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev,
-                                       u8 *mac, struct station_info *sinfo)
+                            const u8 *mac, struct station_info *sinfo)
 {
        struct rndis_wlan_private *priv = wiphy_priv(wiphy);
        struct usbnet *usbdev = priv->usbdev;
index 84164747ace057ee11be9bc25bf8b63bb30407b2..54aaeb09debf568c9dcad47ae3892153c2d827d6 100644 (file)
@@ -656,6 +656,7 @@ static int rsi_mac80211_ampdu_action(struct ieee80211_hw *hw,
        case IEEE80211_AMPDU_TX_START:
                common->vif_info[ii].seq_start = seq_no;
                ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               status = 0;
                break;
 
        case IEEE80211_AMPDU_TX_STOP_CONT:
index 1b28cda6ca88124deff6c112a060f5af6692cefa..2eefbf159bc0d0abdcead9ff1caf6b867d95793f 100644 (file)
@@ -1083,7 +1083,7 @@ void rsi_inform_bss_status(struct rsi_common *common,
 {
        if (status) {
                rsi_hal_send_sta_notify_frame(common,
-                                             NL80211_IFTYPE_STATION,
+                                             RSI_IFTYPE_STATION,
                                              STA_CONNECTED,
                                              bssid,
                                              qos_enable,
@@ -1092,7 +1092,7 @@ void rsi_inform_bss_status(struct rsi_common *common,
                        rsi_send_auto_rate_request(common);
        } else {
                rsi_hal_send_sta_notify_frame(common,
-                                             NL80211_IFTYPE_STATION,
+                                             RSI_IFTYPE_STATION,
                                              STA_DISCONNECTED,
                                              bssid,
                                              qos_enable,
index f2f70784d4ade1e3dcccc0d55230385515c4600c..d3fbe33d23244bb37cf287736303365301e2e52e 100644 (file)
@@ -63,7 +63,7 @@ static inline int rsi_create_kthread(struct rsi_common *common,
                                     u8 *name)
 {
        init_completion(&thread->completion);
-       thread->task = kthread_run(func_ptr, common, name);
+       thread->task = kthread_run(func_ptr, common, "%s", name);
        if (IS_ERR(thread->task))
                return (int)PTR_ERR(thread->task);
 
index ac67c4ad63c2d3177e3e70386ff27538b69b536f..225215a3b8bb484d76b47ed853afb3aeb6eb2130 100644 (file)
@@ -73,6 +73,7 @@
 #define RX_BA_INDICATION                1
 #define RSI_TBL_SZ                      40
 #define MAX_RETRIES                     8
+#define RSI_IFTYPE_STATION              0
 
 #define STD_RATE_MCS7                   0x07
 #define STD_RATE_MCS6                   0x06
index 41d4a8167dc32f368a8fdf061bea4fe9944fd0f1..c17fcf272728cb06ae25e95787003f6f59f52dba 100644 (file)
@@ -1005,10 +1005,9 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
                                   entry->skb->len + padding_len);
 
        /*
-        * Enable beaconing again.
+        * Restore beaconing state.
         */
-       rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 1);
-       rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+       rt2800_register_write(rt2x00dev, BCN_TIME_CFG, orig_reg);
 
        /*
         * Clean up beacon skb.
@@ -1039,13 +1038,14 @@ static inline void rt2800_clear_beacon_register(struct rt2x00_dev *rt2x00dev,
 void rt2800_clear_beacon(struct queue_entry *entry)
 {
        struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
-       u32 reg;
+       u32 orig_reg, reg;
 
        /*
         * Disable beaconing while we are reloading the beacon data,
         * otherwise we might be sending out invalid data.
         */
-       rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+       rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &orig_reg);
+       reg = orig_reg;
        rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 0);
        rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
 
@@ -1055,10 +1055,9 @@ void rt2800_clear_beacon(struct queue_entry *entry)
        rt2800_clear_beacon_register(rt2x00dev, entry->entry_idx);
 
        /*
-        * Enabled beaconing again.
+        * Restore beaconing state.
         */
-       rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 1);
-       rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+       rt2800_register_write(rt2x00dev, BCN_TIME_CFG, orig_reg);
 }
 EXPORT_SYMBOL_GPL(rt2800_clear_beacon);
 
index e3b885d8f7dbfddda2f4ae71161b24edeefdc02c..010b76505243ed1cf15d1f176033cabd5ac23f3d 100644 (file)
@@ -1448,7 +1448,8 @@ int rt2x00mac_conf_tx(struct ieee80211_hw *hw,
                      struct ieee80211_vif *vif, u16 queue,
                      const struct ieee80211_tx_queue_params *params);
 void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw);
-void rt2x00mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop);
+void rt2x00mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                    u32 queues, bool drop);
 int rt2x00mac_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant);
 int rt2x00mac_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant);
 void rt2x00mac_get_ringparam(struct ieee80211_hw *hw,
index a87ee9b6585a72cdad0394d773b17a69a8b80298..212ac4842c1628a0d141104188626d55c616c487 100644 (file)
@@ -749,7 +749,8 @@ void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw)
 }
 EXPORT_SYMBOL_GPL(rt2x00mac_rfkill_poll);
 
-void rt2x00mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+void rt2x00mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                    u32 queues, bool drop)
 {
        struct rt2x00_dev *rt2x00dev = hw->priv;
        struct data_queue *queue;
index 10572452cc21b76cc8fe4457298ea06bc3c875be..86c43d112a4b7d4f25bfbf123b7cbbfb98bd75b9 100644 (file)
@@ -68,6 +68,12 @@ int rt2x00usb_vendor_request(struct rt2x00_dev *rt2x00dev,
                }
        }
 
+       /* If the port is powered down, we get a -EPROTO error, and this
+        * leads to a endless loop. So just say that the device is gone.
+        */
+       if (status == -EPROTO)
+               clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags);
+
        rt2x00_err(rt2x00dev,
                   "Vendor Request 0x%02x failed for offset 0x%04x with error %d\n",
                   request, offset, status);
index 24402984ee5749f272609d82907cda4a68f750f6..9048a9cbe52cb929cfbd60797a00baa40c5d6583 100644 (file)
@@ -2031,13 +2031,14 @@ static void rt61pci_write_beacon(struct queue_entry *entry,
 static void rt61pci_clear_beacon(struct queue_entry *entry)
 {
        struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
-       u32 reg;
+       u32 orig_reg, reg;
 
        /*
         * Disable beaconing while we are reloading the beacon data,
         * otherwise we might be sending out invalid data.
         */
-       rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, &reg);
+       rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, &orig_reg);
+       reg = orig_reg;
        rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_GEN, 0);
        rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg);
 
@@ -2048,10 +2049,9 @@ static void rt61pci_clear_beacon(struct queue_entry *entry)
                                  HW_BEACON_OFFSET(entry->entry_idx), 0);
 
        /*
-        * Enable beaconing again.
+        * Restore global beaconing state.
         */
-       rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_GEN, 1);
-       rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg);
+       rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, orig_reg);
 }
 
 /*
index a140170b1eb3e63625ecde7b4cc43ec6bf1b87b1..95724ff9c7268700628866f433b66e82a8a9f7c4 100644 (file)
@@ -1597,13 +1597,14 @@ static void rt73usb_clear_beacon(struct queue_entry *entry)
 {
        struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
        unsigned int beacon_base;
-       u32 reg;
+       u32 orig_reg, reg;
 
        /*
         * Disable beaconing while we are reloading the beacon data,
         * otherwise we might be sending out invalid data.
         */
-       rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, &reg);
+       rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, &orig_reg);
+       reg = orig_reg;
        rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_GEN, 0);
        rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg);
 
@@ -1614,10 +1615,9 @@ static void rt73usb_clear_beacon(struct queue_entry *entry)
        rt2x00usb_register_write(rt2x00dev, beacon_base, 0);
 
        /*
-        * Enable beaconing again.
+        * Restore beaconing state.
         */
-       rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_GEN, 1);
-       rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg);
+       rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, orig_reg);
 }
 
 static int rt73usb_get_tx_data_len(struct queue_entry *entry)
index 08b056db4a3b795282d1d9e878ab400d7090bcf2..21005bd8b43c973da6ebd24556da848032727bcf 100644 (file)
@@ -1,5 +1,5 @@
-rtl8180-objs           := dev.o rtl8225.o sa2400.o max2820.o grf5101.o rtl8225se.o
+rtl818x_pci-objs       := dev.o rtl8225.o sa2400.o max2820.o grf5101.o rtl8225se.o
 
-obj-$(CONFIG_RTL8180)  += rtl8180.o
+obj-$(CONFIG_RTL8180)  += rtl818x_pci.o
 
 ccflags-y += -Idrivers/net/wireless/rtl818x
index 98d8256f037788a4d9af76c02a4e939758a08e0e..2c1c02bafa10bbfe1f198279400a84ad08256bb4 100644 (file)
@@ -284,6 +284,8 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev)
                        rx_status.band = dev->conf.chandef.chan->band;
                        rx_status.mactime = tsft;
                        rx_status.flag |= RX_FLAG_MACTIME_START;
+                       if (flags & RTL818X_RX_DESC_FLAG_SPLCP)
+                               rx_status.flag |= RX_FLAG_SHORTPRE;
                        if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR)
                                rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
 
@@ -461,18 +463,23 @@ static void rtl8180_tx(struct ieee80211_hw *dev,
                            RTL818X_TX_DESC_FLAG_NO_ENC;
 
        rc_flags = info->control.rates[0].flags;
+
+       /* HW will perform RTS-CTS when only RTS flags is set.
+        * HW will perform CTS-to-self when both RTS and CTS flags are set.
+        * RTS rate and RTS duration will be used also for CTS-to-self.
+        */
        if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
                tx_flags |= RTL818X_TX_DESC_FLAG_RTS;
                tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19;
+               rts_duration = ieee80211_rts_duration(dev, priv->vif,
+                                               skb->len, info);
        } else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
-               tx_flags |= RTL818X_TX_DESC_FLAG_CTS;
+               tx_flags |= RTL818X_TX_DESC_FLAG_RTS | RTL818X_TX_DESC_FLAG_CTS;
                tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19;
+               rts_duration = ieee80211_ctstoself_duration(dev, priv->vif,
+                                               skb->len, info);
        }
 
-       if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS)
-               rts_duration = ieee80211_rts_duration(dev, priv->vif, skb->len,
-                                                     info);
-
        if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8180) {
                unsigned int remainder;
 
@@ -683,9 +690,8 @@ static void rtl8180_int_enable(struct ieee80211_hw *dev)
        struct rtl8180_priv *priv = dev->priv;
 
        if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) {
-               rtl818x_iowrite32(priv, &priv->map->IMR, IMR_TMGDOK |
-                         IMR_TBDER | IMR_THPDER |
-                         IMR_THPDER | IMR_THPDOK |
+               rtl818x_iowrite32(priv, &priv->map->IMR,
+                         IMR_TBDER | IMR_TBDOK |
                          IMR_TVODER | IMR_TVODOK |
                          IMR_TVIDER | IMR_TVIDOK |
                          IMR_TBEDER | IMR_TBEDOK |
@@ -911,7 +917,10 @@ static int rtl8180_init_hw(struct ieee80211_hw *dev)
                reg32 &= 0x00ffff00;
                reg32 |= 0xb8000054;
                rtl818x_iowrite32(priv, &priv->map->RF_PARA, reg32);
-       }
+       } else
+               /* stop unused queus (no dma alloc) */
+               rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING,
+                           (1<<1) | (1<<2));
 
        priv->rf->init(dev);
 
index 0ca17cda48fa1c01b3a8dd2ed98c5fe0c5646640..629ad8cfa17b59bf4b5f8b11202602e34c9cce33 100644 (file)
@@ -253,14 +253,21 @@ static void rtl8187_tx(struct ieee80211_hw *dev,
        flags |= ieee80211_get_tx_rate(dev, info)->hw_value << 24;
        if (ieee80211_has_morefrags(tx_hdr->frame_control))
                flags |= RTL818X_TX_DESC_FLAG_MOREFRAG;
+
+       /* HW will perform RTS-CTS when only RTS flags is set.
+        * HW will perform CTS-to-self when both RTS and CTS flags are set.
+        * RTS rate and RTS duration will be used also for CTS-to-self.
+        */
        if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
                flags |= RTL818X_TX_DESC_FLAG_RTS;
                flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19;
                rts_dur = ieee80211_rts_duration(dev, priv->vif,
                                                 skb->len, info);
        } else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
-               flags |= RTL818X_TX_DESC_FLAG_CTS;
+               flags |= RTL818X_TX_DESC_FLAG_RTS | RTL818X_TX_DESC_FLAG_CTS;
                flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19;
+               rts_dur = ieee80211_ctstoself_duration(dev, priv->vif,
+                                                skb->len, info);
        }
 
        if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
@@ -381,6 +388,8 @@ static void rtl8187_rx_cb(struct urb *urb)
        rx_status.freq = dev->conf.chandef.chan->center_freq;
        rx_status.band = dev->conf.chandef.chan->band;
        rx_status.flag |= RX_FLAG_MACTIME_START;
+       if (flags & RTL818X_RX_DESC_FLAG_SPLCP)
+               rx_status.flag |= RX_FLAG_SHORTPRE;
        if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR)
                rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
        memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
index 45ea4e1c4abe157ad952be2d8a5022efc1e1715a..7abef95d278bc61336d2829f40520afc1af81a1a 100644 (file)
@@ -334,9 +334,9 @@ struct rtl818x_csr {
  * I don't like to introduce a ton of "reserved"..
  * They are for RTL8187SE
  */
-#define REG_ADDR1(addr)        ((u8 __iomem *)priv->map + addr)
-#define REG_ADDR2(addr)        ((__le16 __iomem *)priv->map + (addr >> 1))
-#define REG_ADDR4(addr)        ((__le32 __iomem *)priv->map + (addr >> 2))
+#define REG_ADDR1(addr)        ((u8 __iomem *)priv->map + (addr))
+#define REG_ADDR2(addr)        ((__le16 __iomem *)priv->map + ((addr) >> 1))
+#define REG_ADDR4(addr)        ((__le32 __iomem *)priv->map + ((addr) >> 2))
 
 #define FEMR_SE                REG_ADDR2(0x1D4)
 #define ARFR           REG_ADDR2(0x1E0)
index 4ec424f26672028550ab8b19b944d451766ee08c..b1ed6d0796f67e187fb928423edda6977c91f863 100644 (file)
@@ -1387,7 +1387,8 @@ static void rtl_op_rfkill_poll(struct ieee80211_hw *hw)
  * before switch channel or power save, or tx buffer packet
  * maybe send after offchannel or rf sleep, this may cause
  * dis-association by AP */
-static void rtl_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static void rtl_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                        u32 queues, bool drop)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
 
index 94cd9df98381008e53f6ecb60cbcb7f56a6aef1b..b14cf5a10f4421127e8f6ce414eee52054129163 100644 (file)
@@ -2515,23 +2515,3 @@ void rtl88ee_suspend(struct ieee80211_hw *hw)
 void rtl88ee_resume(struct ieee80211_hw *hw)
 {
 }
-
-/* Turn on AAP (RCR:bit 0) for promicuous mode. */
-void rtl88ee_allow_all_destaddr(struct ieee80211_hw *hw,
-                               bool allow_all_da, bool write_into_reg)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
-
-       if (allow_all_da) /* Set BIT0 */
-               rtlpci->receive_config |= RCR_AAP;
-        else /* Clear BIT0 */
-               rtlpci->receive_config &= ~RCR_AAP;
-
-       if (write_into_reg)
-               rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config);
-
-       RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD,
-                "receive_config = 0x%08X, write_into_reg =%d\n",
-                rtlpci->receive_config, write_into_reg);
-}
index b4460a41bd0159cac18b1161e489803c98afe244..1850fde881b587c6bbb15a630a58dfaaa16ece07 100644 (file)
@@ -61,8 +61,6 @@ void rtl8188ee_bt_reg_init(struct ieee80211_hw *hw);
 void rtl8188ee_bt_hw_init(struct ieee80211_hw *hw);
 void rtl88ee_suspend(struct ieee80211_hw *hw);
 void rtl88ee_resume(struct ieee80211_hw *hw);
-void rtl88ee_allow_all_destaddr(struct ieee80211_hw *hw,
-                               bool allow_all_da, bool write_into_reg);
 void rtl88ee_fw_clk_off_timer_callback(unsigned long data);
 
 #endif
index 1b4101bf9974e8243124f70f40e295e841a3f0a1..842d69349a37ca0812731beb9d4b6effe2173e39 100644 (file)
@@ -93,7 +93,7 @@ int rtl88e_init_sw_vars(struct ieee80211_hw *hw)
        u8 tid;
 
        rtl8188ee_bt_reg_init(hw);
-       rtlpci->msi_support = true;
+       rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
 
        rtlpriv->dm.dm_initialgain_enable = 1;
        rtlpriv->dm.dm_flag = 0;
@@ -255,7 +255,6 @@ static struct rtl_hal_ops rtl8188ee_hal_ops = {
        .enable_hw_sec = rtl88ee_enable_hw_security_config,
        .set_key = rtl88ee_set_key,
        .init_sw_leds = rtl88ee_init_sw_leds,
-       .allow_all_destaddr = rtl88ee_allow_all_destaddr,
        .get_bbreg = rtl88e_phy_query_bb_reg,
        .set_bbreg = rtl88e_phy_set_bb_reg,
        .get_rfreg = rtl88e_phy_query_rf_reg,
@@ -267,6 +266,7 @@ static struct rtl_mod_params rtl88ee_mod_params = {
        .inactiveps = true,
        .swctrl_lps = false,
        .fwctrl_lps = true,
+       .msi_support = false,
        .debug = DBG_EMERG,
 };
 
@@ -383,10 +383,12 @@ module_param_named(debug, rtl88ee_mod_params.debug, int, 0444);
 module_param_named(ips, rtl88ee_mod_params.inactiveps, bool, 0444);
 module_param_named(swlps, rtl88ee_mod_params.swctrl_lps, bool, 0444);
 module_param_named(fwlps, rtl88ee_mod_params.fwctrl_lps, bool, 0444);
+module_param_named(msi, rtl88ee_mod_params.msi_support, bool, 0444);
 MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n");
 MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n");
 MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n");
 MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n");
+MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 0)\n");
 MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)");
 
 static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume);
index 55adf043aef7e250759d9c993b224217ad974d37..cdecb0fd4d8edb531c34cb929b8a13f568f40eed 100644 (file)
@@ -2423,24 +2423,3 @@ void rtl92ce_suspend(struct ieee80211_hw *hw)
 void rtl92ce_resume(struct ieee80211_hw *hw)
 {
 }
-
-/* Turn on AAP (RCR:bit 0) for promicuous mode. */
-void rtl92ce_allow_all_destaddr(struct ieee80211_hw *hw,
-       bool allow_all_da, bool write_into_reg)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
-
-       if (allow_all_da) {/* Set BIT0 */
-               rtlpci->receive_config |= RCR_AAP;
-       } else {/* Clear BIT0 */
-               rtlpci->receive_config &= ~RCR_AAP;
-       }
-
-       if (write_into_reg)
-               rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config);
-
-       RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD,
-                "receive_config=0x%08X, write_into_reg=%d\n",
-                rtlpci->receive_config, write_into_reg);
-}
index 2d063b0c77609a608adc8d831bde0d946263a7a1..5533070f266c4c0706b4d83c2477d4c8421f17eb 100644 (file)
@@ -76,7 +76,5 @@ void rtl8192ce_bt_reg_init(struct ieee80211_hw *hw);
 void rtl8192ce_bt_hw_init(struct ieee80211_hw *hw);
 void rtl92ce_suspend(struct ieee80211_hw *hw);
 void rtl92ce_resume(struct ieee80211_hw *hw);
-void rtl92ce_allow_all_destaddr(struct ieee80211_hw *hw,
-                               bool allow_all_da, bool write_into_reg);
 
 #endif
index b790320d20305427c5ed4fa5fd9f37d944957e31..12f21f4073e887c497d4e7efda968d9217515936 100644 (file)
@@ -229,7 +229,6 @@ static struct rtl_hal_ops rtl8192ce_hal_ops = {
        .enable_hw_sec = rtl92ce_enable_hw_security_config,
        .set_key = rtl92ce_set_key,
        .init_sw_leds = rtl92ce_init_sw_leds,
-       .allow_all_destaddr = rtl92ce_allow_all_destaddr,
        .get_bbreg = rtl92c_phy_query_bb_reg,
        .set_bbreg = rtl92c_phy_set_bb_reg,
        .set_rfreg = rtl92ce_phy_set_rf_reg,
index 07cb06da67297244131394e60fa19c9ddf86044e..a903c2671b4d1701c0c71416748b5d4fa8ec6bf9 100644 (file)
@@ -511,7 +511,7 @@ static int _rtl92cu_init_power_on(struct ieee80211_hw *hw)
                        pr_info("MAC auto ON okay!\n");
                        break;
                }
-               if (pollingCount++ > 100) {
+               if (pollingCount++ > 1000) {
                        RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG,
                                 "Failed to polling REG_APS_FSMCO[APFM_ONMAC] done!\n");
                        return -ENODEV;
index c61311084d7e46615af164674337049bade62c20..361435f8608a125ca4cfdfd38b1bfa6212215ec9 100644 (file)
@@ -395,9 +395,6 @@ static struct usb_driver rtl8192cu_driver = {
        /* .resume = rtl_usb_resume, */
        /* .reset_resume = rtl8192c_resume, */
 #endif /* CONFIG_PM */
-#ifdef CONFIG_AUTOSUSPEND
-       .supports_autosuspend = 1,
-#endif
        .disable_hub_initiated_lpm = 1,
 };
 
index 9098558d916dee6ae6daf3567eb91291ad234b72..1c7101bcd79034c486be9713d99c19c335478cb6 100644 (file)
@@ -2544,23 +2544,3 @@ void rtl92se_resume(struct ieee80211_hw *hw)
                pci_write_config_dword(rtlpci->pdev, 0x40,
                        val & 0xffff00ff);
 }
-
-/* Turn on AAP (RCR:bit 0) for promicuous mode. */
-void rtl92se_allow_all_destaddr(struct ieee80211_hw *hw,
-                               bool allow_all_da, bool write_into_reg)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
-
-       if (allow_all_da) /* Set BIT0 */
-               rtlpci->receive_config |= RCR_AAP;
-       else /* Clear BIT0 */
-               rtlpci->receive_config &= ~RCR_AAP;
-
-       if (write_into_reg)
-               rtl_write_dword(rtlpriv, RCR, rtlpci->receive_config);
-
-       RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD,
-                "receive_config=0x%08X, write_into_reg=%d\n",
-                rtlpci->receive_config, write_into_reg);
-}
index da48aa8cbe6f8cb8e97ca2943e643217bfaea170..4cacee10f31eb81dd3a34bf8c2cbfd4cab22d60b 100644 (file)
@@ -74,7 +74,5 @@ void rtl92se_set_key(struct ieee80211_hw *hw,
                     u8 enc_algo, bool is_wepkey, bool clear_all);
 void rtl92se_suspend(struct ieee80211_hw *hw);
 void rtl92se_resume(struct ieee80211_hw *hw);
-void rtl92se_allow_all_destaddr(struct ieee80211_hw *hw,
-                               bool allow_all_da, bool write_into_reg);
 
 #endif
index 2e8e6f8d2d513e18a77a3710542656c2e1953ec5..1bff2a0f760087d0a510e247f01d1c6a33eee891 100644 (file)
@@ -290,7 +290,6 @@ static struct rtl_hal_ops rtl8192se_hal_ops = {
        .enable_hw_sec = rtl92se_enable_hw_security_config,
        .set_key = rtl92se_set_key,
        .init_sw_leds = rtl92se_init_sw_leds,
-       .allow_all_destaddr = rtl92se_allow_all_destaddr,
        .get_bbreg = rtl92s_phy_query_bb_reg,
        .set_bbreg = rtl92s_phy_set_bb_reg,
        .get_rfreg = rtl92s_phy_query_rf_reg,
index 48fee1be78c2f54e1e839cf1cec8e05be65b9094..5b4a714f3c8ce9905d762e08068a568827af1458 100644 (file)
@@ -32,7 +32,6 @@
 #include "dm.h"
 #include "fw.h"
 #include "../rtl8723com/fw_common.h"
-#include "../rtl8723com/fw_common.h"
 #include "phy.h"
 #include "reg.h"
 #include "hal_btc.h"
index 65c9e80e1f78ad23988bb962732396153bf1f137..87f69166a7eda86b2517feb6237da073c0ebe9da 100644 (file)
@@ -2383,24 +2383,3 @@ void rtl8723ae_suspend(struct ieee80211_hw *hw)
 void rtl8723ae_resume(struct ieee80211_hw *hw)
 {
 }
-
-/* Turn on AAP (RCR:bit 0) for promicuous mode. */
-void rtl8723ae_allow_all_destaddr(struct ieee80211_hw *hw,
-       bool allow_all_da, bool write_into_reg)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
-
-       if (allow_all_da) /* Set BIT0 */
-               rtlpci->receive_config |= RCR_AAP;
-       else /* Clear BIT0 */
-               rtlpci->receive_config &= ~RCR_AAP;
-
-       if (write_into_reg)
-               rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config);
-
-
-       RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD,
-                "receive_config=0x%08X, write_into_reg=%d\n",
-                rtlpci->receive_config, write_into_reg);
-}
index 6fa24f79b1d73551d9b40f4b355d5d1590636b68..d3bc39fb27a559dd0b527ebd5a377bb29a394dfe 100644 (file)
@@ -67,7 +67,5 @@ void rtl8723ae_bt_reg_init(struct ieee80211_hw *hw);
 void rtl8723ae_bt_hw_init(struct ieee80211_hw *hw);
 void rtl8723ae_suspend(struct ieee80211_hw *hw);
 void rtl8723ae_resume(struct ieee80211_hw *hw);
-void rtl8723ae_allow_all_destaddr(struct ieee80211_hw *hw,
-                                 bool allow_all_da, bool write_into_reg);
 
 #endif
index 1087a3bd07fa2dbd51f9983540ac44351374c58d..73cba1eec8cf9ce331afde11c69e6fdc7f75e59d 100644 (file)
@@ -238,7 +238,6 @@ static struct rtl_hal_ops rtl8723ae_hal_ops = {
        .enable_hw_sec = rtl8723ae_enable_hw_security_config,
        .set_key = rtl8723ae_set_key,
        .init_sw_leds = rtl8723ae_init_sw_leds,
-       .allow_all_destaddr = rtl8723ae_allow_all_destaddr,
        .get_bbreg = rtl8723_phy_query_bb_reg,
        .set_bbreg = rtl8723_phy_set_bb_reg,
        .get_rfreg = rtl8723ae_phy_query_rf_reg,
index 0fdf0909321f234c827107a13fd0a839c7c86be8..3d555495b45319b8d287d9edd5e1bc1c6162e625 100644 (file)
@@ -2501,23 +2501,3 @@ void rtl8723be_suspend(struct ieee80211_hw *hw)
 void rtl8723be_resume(struct ieee80211_hw *hw)
 {
 }
-
-/* Turn on AAP (RCR:bit 0) for promicuous mode. */
-void rtl8723be_allow_all_destaddr(struct ieee80211_hw *hw, bool allow_all_da,
-                                 bool write_into_reg)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
-
-       if (allow_all_da) /* Set BIT0 */
-               rtlpci->receive_config |= RCR_AAP;
-       else /* Clear BIT0 */
-               rtlpci->receive_config &= ~RCR_AAP;
-
-       if (write_into_reg)
-               rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config);
-
-       RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD,
-                "receive_config = 0x%08X, write_into_reg =%d\n",
-                rtlpci->receive_config, write_into_reg);
-}
index b7449a9b57e47652489b18df83ab5a4502e40d0e..64c7551af6b797969e98086e600d2ece0240c9f4 100644 (file)
@@ -59,6 +59,4 @@ void rtl8723be_bt_reg_init(struct ieee80211_hw *hw);
 void rtl8723be_bt_hw_init(struct ieee80211_hw *hw);
 void rtl8723be_suspend(struct ieee80211_hw *hw);
 void rtl8723be_resume(struct ieee80211_hw *hw);
-void rtl8723be_allow_all_destaddr(struct ieee80211_hw *hw, bool allow_all_da,
-                                 bool write_into_reg);
 #endif
index b4577ebc4bb0bb5c16d068039cddc7f3fe41b744..ff12bf41644bbfd3909bb6506f825f1d0d7e81fa 100644 (file)
@@ -92,7 +92,7 @@ int rtl8723be_init_sw_vars(struct ieee80211_hw *hw)
        struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
 
        rtl8723be_bt_reg_init(hw);
-       rtlpci->msi_support = true;
+       rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
        rtlpriv->btcoexist.btc_ops = rtl_btc_get_ops_pointer();
 
        rtlpriv->dm.dm_initialgain_enable = 1;
@@ -253,6 +253,7 @@ static struct rtl_mod_params rtl8723be_mod_params = {
        .inactiveps = true,
        .swctrl_lps = false,
        .fwctrl_lps = true,
+       .msi_support = false,
        .debug = DBG_EMERG,
 };
 
@@ -365,9 +366,11 @@ module_param_named(debug, rtl8723be_mod_params.debug, int, 0444);
 module_param_named(ips, rtl8723be_mod_params.inactiveps, bool, 0444);
 module_param_named(swlps, rtl8723be_mod_params.swctrl_lps, bool, 0444);
 module_param_named(fwlps, rtl8723be_mod_params.fwctrl_lps, bool, 0444);
+module_param_named(msi, rtl8723be_mod_params.msi_support, bool, 0444);
 MODULE_PARM_DESC(swenc, "using hardware crypto (default 0 [hardware])\n");
 MODULE_PARM_DESC(ips, "using no link power save (default 1 is open)\n");
 MODULE_PARM_DESC(fwlps, "using linked fw control power save (default 1 is open)\n");
+MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 0)\n");
 MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)");
 
 static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume);
index e0a0d8c8fed556f66b0c766e490b16a59aee74f7..969eaea5eddd8d23862b272edfc6edbf6d9ac5df 100644 (file)
@@ -33,7 +33,6 @@
 #include "trx.h"
 #include "led.h"
 #include "dm.h"
-#include "phy.h"
 
 static u8 _rtl8723be_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue)
 {
index 6965afdf572a9d57c06fabd8b070fbc8cc58b9ce..407a7936d3642f39fb4fb0364ab0568e20f75153 100644 (file)
@@ -1960,8 +1960,6 @@ struct rtl_hal_ops {
                          u32 regaddr, u32 bitmask);
        void (*set_rfreg) (struct ieee80211_hw *hw, enum radio_path rfpath,
                           u32 regaddr, u32 bitmask, u32 data);
-       void (*allow_all_destaddr)(struct ieee80211_hw *hw,
-               bool allow_all_da, bool write_into_reg);
        void (*linked_set_reg) (struct ieee80211_hw *hw);
        void (*chk_switch_dmdp) (struct ieee80211_hw *hw);
        void (*dualmac_easy_concurrent) (struct ieee80211_hw *hw);
@@ -2030,6 +2028,10 @@ struct rtl_mod_params {
 
        /* default: 1 = using linked fw power save */
        bool fwctrl_lps;
+
+       /* default: 0 = not using MSI interrupts mode */
+       /* submodules should set their own defalut value */
+       bool msi_support;
 };
 
 struct rtl_hal_usbint_cfg {
index 5a4ec56c83d0aa15e7e00226a6cfe69c9146696b..5695628757ee17db6c4da9ba40d81000f9fa57b5 100644 (file)
@@ -2,7 +2,6 @@
 
 #include <linux/module.h>
 #include <linux/slab.h>
-#include <linux/crc7.h>
 
 #include "wl1251.h"
 #include "reg.h"
index bf1fa18b9786253159d634ec1f846f70d8551bc8..ede31f048ef98d28b96756f912edbdd2b4df8eb5 100644 (file)
@@ -2,7 +2,6 @@
 
 #include <linux/module.h>
 #include <linux/slab.h>
-#include <linux/crc7.h>
 #include <linux/etherdevice.h>
 
 #include "wl1251.h"
index db0105313745f08a02c9d408564c5d8029aa05ca..c98630394a1a299b77b907ce53851b62b7d84030 100644 (file)
@@ -124,11 +124,12 @@ static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox)
                        return ret;
        }
 
-       if (wl->vif && vector & SYNCHRONIZATION_TIMEOUT_EVENT_ID) {
+       if (vector & SYNCHRONIZATION_TIMEOUT_EVENT_ID) {
                wl1251_debug(DEBUG_EVENT, "SYNCHRONIZATION_TIMEOUT_EVENT");
 
                /* indicate to the stack, that beacons have been lost */
-               ieee80211_beacon_loss(wl->vif);
+               if (wl->vif && wl->vif->type == NL80211_IFTYPE_STATION)
+                       ieee80211_beacon_loss(wl->vif);
        }
 
        if (vector & REGAINED_BSS_EVENT_ID) {
index 757e25784a8a22f503b3483c0b55cedca5a05d3f..4e782f18ae3431600a66923216faa536d42c46b6 100644 (file)
@@ -550,6 +550,34 @@ static void wl1251_op_remove_interface(struct ieee80211_hw *hw,
        mutex_unlock(&wl->mutex);
 }
 
+static int wl1251_build_null_data(struct wl1251 *wl)
+{
+       struct sk_buff *skb = NULL;
+       int size;
+       void *ptr;
+       int ret = -ENOMEM;
+
+       if (wl->bss_type == BSS_TYPE_IBSS) {
+               size = sizeof(struct wl12xx_null_data_template);
+               ptr = NULL;
+       } else {
+               skb = ieee80211_nullfunc_get(wl->hw, wl->vif);
+               if (!skb)
+                       goto out;
+               size = skb->len;
+               ptr = skb->data;
+       }
+
+       ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, ptr, size);
+
+out:
+       dev_kfree_skb(skb);
+       if (ret)
+               wl1251_warning("cmd buld null data failed: %d", ret);
+
+       return ret;
+}
+
 static int wl1251_build_qos_null_data(struct wl1251 *wl)
 {
        struct ieee80211_qos_hdr template;
@@ -687,16 +715,6 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
                wl->power_level = conf->power_level;
        }
 
-       /*
-        * Tell stack that connection is lost because hw encryption isn't
-        * supported in monitor mode.
-        * This requires temporary enabling of the hw connection monitor flag
-        */
-       if ((changed & IEEE80211_CONF_CHANGE_MONITOR) && wl->vif) {
-               wl->hw->flags |= IEEE80211_HW_CONNECTION_MONITOR;
-               ieee80211_connection_loss(wl->vif);
-       }
-
 out_sleep:
        wl1251_ps_elp_sleep(wl);
 
@@ -1103,24 +1121,19 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
                wl->rssi_thold = bss_conf->cqm_rssi_thold;
        }
 
-       if (changed & BSS_CHANGED_BSSID) {
+       if ((changed & BSS_CHANGED_BSSID) &&
+           memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
                memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
 
-               skb = ieee80211_nullfunc_get(wl->hw, wl->vif);
-               if (!skb)
-                       goto out_sleep;
-
-               ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA,
-                                             skb->data, skb->len);
-               dev_kfree_skb(skb);
-               if (ret < 0)
-                       goto out_sleep;
+               if (!is_zero_ether_addr(wl->bssid)) {
+                       ret = wl1251_build_null_data(wl);
+                       if (ret < 0)
+                               goto out_sleep;
 
-               ret = wl1251_build_qos_null_data(wl);
-               if (ret < 0)
-                       goto out;
+                       ret = wl1251_build_qos_null_data(wl);
+                       if (ret < 0)
+                               goto out_sleep;
 
-               if (wl->bss_type != BSS_TYPE_IBSS) {
                        ret = wl1251_join(wl, wl->bss_type, wl->channel,
                                          wl->beacon_int, wl->dtim_period);
                        if (ret < 0)
@@ -1129,9 +1142,6 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
        }
 
        if (changed & BSS_CHANGED_ASSOC) {
-               /* Disable temporary enabled hw connection monitor flag */
-               wl->hw->flags &= ~IEEE80211_HW_CONNECTION_MONITOR;
-
                if (bss_conf->assoc) {
                        wl->beacon_int = bss_conf->beacon_int;
 
@@ -1216,8 +1226,8 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
                if (ret < 0)
                        goto out_sleep;
 
-               ret = wl1251_join(wl, wl->bss_type, wl->beacon_int,
-                                 wl->channel, wl->dtim_period);
+               ret = wl1251_join(wl, wl->bss_type, wl->channel,
+                                 wl->beacon_int, wl->dtim_period);
 
                if (ret < 0)
                        goto out_sleep;
index b06d36d99362703c1b7aa35e6aadd5cab29e0d8f..a0aa8fa72392830f65f8c8b52303be5ad44019b9 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/irq.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/swab.h>
 #include <linux/crc7.h>
 #include <linux/spi/spi.h>
 #include <linux/wl12xx.h>
@@ -83,47 +84,44 @@ static void wl1251_spi_reset(struct wl1251 *wl)
 
 static void wl1251_spi_wake(struct wl1251 *wl)
 {
-       u8 crc[WSPI_INIT_CMD_CRC_LEN], *cmd;
        struct spi_transfer t;
        struct spi_message m;
+       u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
 
-       cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
        if (!cmd) {
                wl1251_error("could not allocate cmd for spi init");
                return;
        }
 
-       memset(crc, 0, sizeof(crc));
        memset(&t, 0, sizeof(t));
        spi_message_init(&m);
 
        /* Set WSPI_INIT_COMMAND
         * the data is being send from the MSB to LSB
         */
-       cmd[2] = 0xff;
-       cmd[3] = 0xff;
-       cmd[1] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX;
-       cmd[0] = 0;
-       cmd[7] = 0;
-       cmd[6] |= HW_ACCESS_WSPI_INIT_CMD_MASK << 3;
-       cmd[6] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN;
+       cmd[0] = 0xff;
+       cmd[1] = 0xff;
+       cmd[2] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX;
+       cmd[3] = 0;
+       cmd[4] = 0;
+       cmd[5] = HW_ACCESS_WSPI_INIT_CMD_MASK << 3;
+       cmd[5] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN;
+
+       cmd[6] = WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS
+               | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS;
 
        if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0)
-               cmd[5] |=  WSPI_INIT_CMD_DIS_FIXEDBUSY;
+               cmd[6] |= WSPI_INIT_CMD_DIS_FIXEDBUSY;
        else
-               cmd[5] |= WSPI_INIT_CMD_EN_FIXEDBUSY;
-
-       cmd[5] |= WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS
-               | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS;
-
-       crc[0] = cmd[1];
-       crc[1] = cmd[0];
-       crc[2] = cmd[7];
-       crc[3] = cmd[6];
-       crc[4] = cmd[5];
+               cmd[6] |= WSPI_INIT_CMD_EN_FIXEDBUSY;
 
-       cmd[4] |= crc7(0, crc, WSPI_INIT_CMD_CRC_LEN) << 1;
-       cmd[4] |= WSPI_INIT_CMD_END;
+       cmd[7] = crc7_be(0, cmd+2, WSPI_INIT_CMD_CRC_LEN) | WSPI_INIT_CMD_END;
+       /*
+        * The above is the logical order; it must actually be stored
+        * in the buffer byte-swapped.
+        */
+       __swab32s((u32 *)cmd);
+       __swab32s((u32 *)cmd+1);
 
        t.tx_buf = cmd;
        t.len = WSPI_INIT_CMD_LEN;
index f7381dd69009a150e1901a876494d225e0267f5e..0f2cfb0d2a9ec38fe013872e6d4339c2db1345e3 100644 (file)
@@ -57,7 +57,7 @@ static const struct file_operations name## _ops = {                   \
                                            wl, &name## _ops);          \
                if (!entry || IS_ERR(entry))                            \
                        goto err;                                       \
-       } while (0);
+       } while (0)
 
 
 #define DEBUGFS_ADD_PREFIX(prefix, name, parent)                       \
@@ -66,7 +66,7 @@ static const struct file_operations name## _ops = {                   \
                                    wl, &prefix## _## name## _ops);     \
                if (!entry || IS_ERR(entry))                            \
                        goto err;                                       \
-       } while (0);
+       } while (0)
 
 #define DEBUGFS_FWSTATS_FILE(sub, name, fmt, struct_type)              \
 static ssize_t sub## _ ##name## _read(struct file *file,               \
index e71eae35336874cacca99c48d80713704142b6f1..3d6028e62750279299431ce56315bdf67fa1f8cc 100644 (file)
@@ -1416,7 +1416,7 @@ void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter)
 
 int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter,
                                 u16 offset, u8 flags,
-                                u8 *pattern, u8 len)
+                                const u8 *pattern, u8 len)
 {
        struct wl12xx_rx_filter_field *field;
 
@@ -5184,7 +5184,8 @@ out:
        mutex_unlock(&wl->mutex);
 }
 
-static void wlcore_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static void wlcore_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                           u32 queues, bool drop)
 {
        struct wl1271 *wl = hw->priv;
 
index 29ef2492951fcdaa372611449fbc4c3cde4322f1..d3dd7bfdf3f1f33efbfbd9e0be762f664c892926 100644 (file)
@@ -217,7 +217,7 @@ static struct wl1271_if_operations sdio_ops = {
 static int wl1271_probe(struct sdio_func *func,
                                  const struct sdio_device_id *id)
 {
-       struct wlcore_platdev_data *pdev_data;
+       struct wlcore_platdev_data pdev_data;
        struct wl12xx_sdio_glue *glue;
        struct resource res[1];
        mmc_pm_flag_t mmcflags;
@@ -228,16 +228,13 @@ static int wl1271_probe(struct sdio_func *func,
        if (func->num != 0x02)
                return -ENODEV;
 
-       pdev_data = kzalloc(sizeof(*pdev_data), GFP_KERNEL);
-       if (!pdev_data)
-               goto out;
-
-       pdev_data->if_ops = &sdio_ops;
+       memset(&pdev_data, 0x00, sizeof(pdev_data));
+       pdev_data.if_ops = &sdio_ops;
 
        glue = kzalloc(sizeof(*glue), GFP_KERNEL);
        if (!glue) {
                dev_err(&func->dev, "can't allocate glue\n");
-               goto out_free_pdev_data;
+               goto out;
        }
 
        glue->dev = &func->dev;
@@ -248,9 +245,9 @@ static int wl1271_probe(struct sdio_func *func,
        /* Use block mode for transferring over one block size of data */
        func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
 
-       pdev_data->pdata = wl12xx_get_platform_data();
-       if (IS_ERR(pdev_data->pdata)) {
-               ret = PTR_ERR(pdev_data->pdata);
+       pdev_data.pdata = wl12xx_get_platform_data();
+       if (IS_ERR(pdev_data.pdata)) {
+               ret = PTR_ERR(pdev_data.pdata);
                dev_err(glue->dev, "missing wlan platform data: %d\n", ret);
                goto out_free_glue;
        }
@@ -260,7 +257,7 @@ static int wl1271_probe(struct sdio_func *func,
        dev_dbg(glue->dev, "sdio PM caps = 0x%x\n", mmcflags);
 
        if (mmcflags & MMC_PM_KEEP_POWER)
-               pdev_data->pdata->pwr_in_suspend = true;
+               pdev_data.pdata->pwr_in_suspend = true;
 
        sdio_set_drvdata(func, glue);
 
@@ -289,7 +286,7 @@ static int wl1271_probe(struct sdio_func *func,
 
        memset(res, 0x00, sizeof(res));
 
-       res[0].start = pdev_data->pdata->irq;
+       res[0].start = pdev_data.pdata->irq;
        res[0].flags = IORESOURCE_IRQ;
        res[0].name = "irq";
 
@@ -299,8 +296,8 @@ static int wl1271_probe(struct sdio_func *func,
                goto out_dev_put;
        }
 
-       ret = platform_device_add_data(glue->core, pdev_data,
-                                      sizeof(*pdev_data));
+       ret = platform_device_add_data(glue->core, &pdev_data,
+                                      sizeof(pdev_data));
        if (ret) {
                dev_err(glue->dev, "can't add platform data\n");
                goto out_dev_put;
@@ -319,9 +316,6 @@ out_dev_put:
 out_free_glue:
        kfree(glue);
 
-out_free_pdev_data:
-       kfree(pdev_data);
-
 out:
        return ret;
 }
index dbe826dd7c23c49a38a08988cb24c50764d3efaa..392c882b28f03d9da2be51c6487db2d423b58976 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/swab.h>
 #include <linux/crc7.h>
 #include <linux/spi/spi.h>
 #include <linux/wl12xx.h>
 #include <linux/platform_device.h>
-#include <linux/slab.h>
 
 #include "wlcore.h"
 #include "wl12xx_80211.h"
@@ -110,18 +111,16 @@ static void wl12xx_spi_reset(struct device *child)
 static void wl12xx_spi_init(struct device *child)
 {
        struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
-       u8 crc[WSPI_INIT_CMD_CRC_LEN], *cmd;
        struct spi_transfer t;
        struct spi_message m;
+       u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
 
-       cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
        if (!cmd) {
                dev_err(child->parent,
                        "could not allocate cmd for spi init\n");
                return;
        }
 
-       memset(crc, 0, sizeof(crc));
        memset(&t, 0, sizeof(t));
        spi_message_init(&m);
 
@@ -129,30 +128,29 @@ static void wl12xx_spi_init(struct device *child)
         * Set WSPI_INIT_COMMAND
         * the data is being send from the MSB to LSB
         */
-       cmd[2] = 0xff;
-       cmd[3] = 0xff;
-       cmd[1] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX;
-       cmd[0] = 0;
-       cmd[7] = 0;
-       cmd[6] |= HW_ACCESS_WSPI_INIT_CMD_MASK << 3;
-       cmd[6] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN;
+       cmd[0] = 0xff;
+       cmd[1] = 0xff;
+       cmd[2] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX;
+       cmd[3] = 0;
+       cmd[4] = 0;
+       cmd[5] = HW_ACCESS_WSPI_INIT_CMD_MASK << 3;
+       cmd[5] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN;
+
+       cmd[6] = WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS
+               | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS;
 
        if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0)
-               cmd[5] |=  WSPI_INIT_CMD_DIS_FIXEDBUSY;
+               cmd[6] |= WSPI_INIT_CMD_DIS_FIXEDBUSY;
        else
-               cmd[5] |= WSPI_INIT_CMD_EN_FIXEDBUSY;
-
-       cmd[5] |= WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS
-               | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS;
-
-       crc[0] = cmd[1];
-       crc[1] = cmd[0];
-       crc[2] = cmd[7];
-       crc[3] = cmd[6];
-       crc[4] = cmd[5];
+               cmd[6] |= WSPI_INIT_CMD_EN_FIXEDBUSY;
 
-       cmd[4] |= crc7(0, crc, WSPI_INIT_CMD_CRC_LEN) << 1;
-       cmd[4] |= WSPI_INIT_CMD_END;
+       cmd[7] = crc7_be(0, cmd+2, WSPI_INIT_CMD_CRC_LEN) | WSPI_INIT_CMD_END;
+       /*
+        * The above is the logical order; it must actually be stored
+        * in the buffer byte-swapped.
+        */
+       __swab32s((u32 *)cmd);
+       __swab32s((u32 *)cmd+1);
 
        t.tx_buf = cmd;
        t.len = WSPI_INIT_CMD_LEN;
@@ -327,27 +325,25 @@ static struct wl1271_if_operations spi_ops = {
 static int wl1271_probe(struct spi_device *spi)
 {
        struct wl12xx_spi_glue *glue;
-       struct wlcore_platdev_data *pdev_data;
+       struct wlcore_platdev_data pdev_data;
        struct resource res[1];
        int ret = -ENOMEM;
 
-       pdev_data = kzalloc(sizeof(*pdev_data), GFP_KERNEL);
-       if (!pdev_data)
-               goto out;
+       memset(&pdev_data, 0x00, sizeof(pdev_data));
 
-       pdev_data->pdata = dev_get_platdata(&spi->dev);
-       if (!pdev_data->pdata) {
+       pdev_data.pdata = dev_get_platdata(&spi->dev);
+       if (!pdev_data.pdata) {
                dev_err(&spi->dev, "no platform data\n");
                ret = -ENODEV;
-               goto out_free_pdev_data;
+               goto out;
        }
 
-       pdev_data->if_ops = &spi_ops;
+       pdev_data.if_ops = &spi_ops;
 
        glue = kzalloc(sizeof(*glue), GFP_KERNEL);
        if (!glue) {
                dev_err(&spi->dev, "can't allocate glue\n");
-               goto out_free_pdev_data;
+               goto out;
        }
 
        glue->dev = &spi->dev;
@@ -385,8 +381,8 @@ static int wl1271_probe(struct spi_device *spi)
                goto out_dev_put;
        }
 
-       ret = platform_device_add_data(glue->core, pdev_data,
-                                      sizeof(*pdev_data));
+       ret = platform_device_add_data(glue->core, &pdev_data,
+                                      sizeof(pdev_data));
        if (ret) {
                dev_err(glue->dev, "can't add platform data\n");
                goto out_dev_put;
@@ -406,9 +402,6 @@ out_dev_put:
 out_free_glue:
        kfree(glue);
 
-out_free_pdev_data:
-       kfree(pdev_data);
-
 out:
        return ret;
 }
index 756e890bc5ee2be0a5f68ef3e37de397ccc8dd52..c2c34a84ff3d4bf7231f84933564b0e78dcc415b 100644 (file)
@@ -512,8 +512,8 @@ int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 void wl12xx_queue_recovery_work(struct wl1271 *wl);
 size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen);
 int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter,
-                                       u16 offset, u8 flags,
-                                       u8 *pattern, u8 len);
+                                u16 offset, u8 flags,
+                                const u8 *pattern, u8 len);
 void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter);
 struct wl12xx_rx_filter *wl1271_rx_filter_alloc(void);
 int wl1271_rx_filter_get_fields_size(struct wl12xx_rx_filter *filter);
index 0d4a285cbd7edb45408360a83952ab288debf62d..4dd7c4a1923ba4222d6ed0c238e3dbe97509a06e 100644 (file)
@@ -99,22 +99,43 @@ struct xenvif_rx_meta {
  */
 #define XEN_NETBK_LEGACY_SLOTS_MAX XEN_NETIF_NR_SLOTS_MIN
 
-struct xenvif {
-       /* Unique identifier for this interface. */
-       domid_t          domid;
-       unsigned int     handle;
+/* Queue name is interface name with "-qNNN" appended */
+#define QUEUE_NAME_SIZE (IFNAMSIZ + 5)
 
-       /* Is this interface disabled? True when backend discovers
-        * frontend is rogue.
+/* IRQ name is queue name with "-tx" or "-rx" appended */
+#define IRQ_NAME_SIZE (QUEUE_NAME_SIZE + 3)
+
+struct xenvif;
+
+struct xenvif_stats {
+       /* Stats fields to be updated per-queue.
+        * A subset of struct net_device_stats that contains only the
+        * fields that are updated in netback.c for each queue.
         */
-       bool disabled;
+       unsigned int rx_bytes;
+       unsigned int rx_packets;
+       unsigned int tx_bytes;
+       unsigned int tx_packets;
+
+       /* Additional stats used by xenvif */
+       unsigned long rx_gso_checksum_fixup;
+       unsigned long tx_zerocopy_sent;
+       unsigned long tx_zerocopy_success;
+       unsigned long tx_zerocopy_fail;
+       unsigned long tx_frag_overflow;
+};
+
+struct xenvif_queue { /* Per-queue data for xenvif */
+       unsigned int id; /* Queue ID, 0-based */
+       char name[QUEUE_NAME_SIZE]; /* DEVNAME-qN */
+       struct xenvif *vif; /* Parent VIF */
 
        /* Use NAPI for guest TX */
        struct napi_struct napi;
        /* When feature-split-event-channels = 0, tx_irq = rx_irq. */
        unsigned int tx_irq;
        /* Only used when feature-split-event-channels = 1 */
-       char tx_irq_name[IFNAMSIZ+4]; /* DEVNAME-tx */
+       char tx_irq_name[IRQ_NAME_SIZE]; /* DEVNAME-qN-tx */
        struct xen_netif_tx_back_ring tx;
        struct sk_buff_head tx_queue;
        struct page *mmap_pages[MAX_PENDING_REQS];
@@ -150,7 +171,7 @@ struct xenvif {
        /* When feature-split-event-channels = 0, tx_irq = rx_irq. */
        unsigned int rx_irq;
        /* Only used when feature-split-event-channels = 1 */
-       char rx_irq_name[IFNAMSIZ+4]; /* DEVNAME-rx */
+       char rx_irq_name[IRQ_NAME_SIZE]; /* DEVNAME-qN-rx */
        struct xen_netif_rx_back_ring rx;
        struct sk_buff_head rx_queue;
        RING_IDX rx_last_skb_slots;
@@ -158,14 +179,29 @@ struct xenvif {
 
        struct timer_list wake_queue;
 
-       /* This array is allocated seperately as it is large */
-       struct gnttab_copy *grant_copy_op;
+       struct gnttab_copy grant_copy_op[MAX_GRANT_COPY_OPS];
 
        /* We create one meta structure per ring request we consume, so
         * the maximum number is the same as the ring size.
         */
        struct xenvif_rx_meta meta[XEN_NETIF_RX_RING_SIZE];
 
+       /* Transmit shaping: allow 'credit_bytes' every 'credit_usec'. */
+       unsigned long   credit_bytes;
+       unsigned long   credit_usec;
+       unsigned long   remaining_credit;
+       struct timer_list credit_timeout;
+       u64 credit_window_start;
+
+       /* Statistics */
+       struct xenvif_stats stats;
+};
+
+struct xenvif {
+       /* Unique identifier for this interface. */
+       domid_t          domid;
+       unsigned int     handle;
+
        u8               fe_dev_addr[6];
 
        /* Frontend feature information. */
@@ -179,19 +215,13 @@ struct xenvif {
        /* Internal feature information. */
        u8 can_queue:1;     /* can queue packets for receiver? */
 
-       /* Transmit shaping: allow 'credit_bytes' every 'credit_usec'. */
-       unsigned long   credit_bytes;
-       unsigned long   credit_usec;
-       unsigned long   remaining_credit;
-       struct timer_list credit_timeout;
-       u64 credit_window_start;
+       /* Is this interface disabled? True when backend discovers
+        * frontend is rogue.
+        */
+       bool disabled;
 
-       /* Statistics */
-       unsigned long rx_gso_checksum_fixup;
-       unsigned long tx_zerocopy_sent;
-       unsigned long tx_zerocopy_success;
-       unsigned long tx_zerocopy_fail;
-       unsigned long tx_frag_overflow;
+       /* Queues */
+       struct xenvif_queue *queues;
 
        /* Miscellaneous private stuff. */
        struct net_device *dev;
@@ -206,7 +236,10 @@ struct xenvif *xenvif_alloc(struct device *parent,
                            domid_t domid,
                            unsigned int handle);
 
-int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref,
+int xenvif_init_queue(struct xenvif_queue *queue);
+void xenvif_deinit_queue(struct xenvif_queue *queue);
+
+int xenvif_connect(struct xenvif_queue *queue, unsigned long tx_ring_ref,
                   unsigned long rx_ring_ref, unsigned int tx_evtchn,
                   unsigned int rx_evtchn);
 void xenvif_disconnect(struct xenvif *vif);
@@ -217,44 +250,47 @@ void xenvif_xenbus_fini(void);
 
 int xenvif_schedulable(struct xenvif *vif);
 
-int xenvif_must_stop_queue(struct xenvif *vif);
+int xenvif_must_stop_queue(struct xenvif_queue *queue);
+
+int xenvif_queue_stopped(struct xenvif_queue *queue);
+void xenvif_wake_queue(struct xenvif_queue *queue);
 
 /* (Un)Map communication rings. */
-void xenvif_unmap_frontend_rings(struct xenvif *vif);
-int xenvif_map_frontend_rings(struct xenvif *vif,
+void xenvif_unmap_frontend_rings(struct xenvif_queue *queue);
+int xenvif_map_frontend_rings(struct xenvif_queue *queue,
                              grant_ref_t tx_ring_ref,
                              grant_ref_t rx_ring_ref);
 
 /* Check for SKBs from frontend and schedule backend processing */
-void xenvif_napi_schedule_or_enable_events(struct xenvif *vif);
+void xenvif_napi_schedule_or_enable_events(struct xenvif_queue *queue);
 
 /* Prevent the device from generating any further traffic. */
 void xenvif_carrier_off(struct xenvif *vif);
 
-int xenvif_tx_action(struct xenvif *vif, int budget);
+int xenvif_tx_action(struct xenvif_queue *queue, int budget);
 
 int xenvif_kthread_guest_rx(void *data);
-void xenvif_kick_thread(struct xenvif *vif);
+void xenvif_kick_thread(struct xenvif_queue *queue);
 
 int xenvif_dealloc_kthread(void *data);
 
 /* Determine whether the needed number of slots (req) are available,
  * and set req_event if not.
  */
-bool xenvif_rx_ring_slots_available(struct xenvif *vif, int needed);
+bool xenvif_rx_ring_slots_available(struct xenvif_queue *queue, int needed);
 
-void xenvif_stop_queue(struct xenvif *vif);
+void xenvif_carrier_on(struct xenvif *vif);
 
 /* Callback from stack when TX packet can be released */
 void xenvif_zerocopy_callback(struct ubuf_info *ubuf, bool zerocopy_success);
 
 /* Unmap a pending page and release it back to the guest */
-void xenvif_idx_unmap(struct xenvif *vif, u16 pending_idx);
+void xenvif_idx_unmap(struct xenvif_queue *queue, u16 pending_idx);
 
-static inline pending_ring_idx_t nr_pending_reqs(struct xenvif *vif)
+static inline pending_ring_idx_t nr_pending_reqs(struct xenvif_queue *queue)
 {
        return MAX_PENDING_REQS -
-               vif->pending_prod + vif->pending_cons;
+               queue->pending_prod + queue->pending_cons;
 }
 
 /* Callback from stack when TX packet can be released */
@@ -264,5 +300,6 @@ extern bool separate_tx_rx_irq;
 
 extern unsigned int rx_drain_timeout_msecs;
 extern unsigned int rx_drain_timeout_jiffies;
+extern unsigned int xenvif_max_queues;
 
 #endif /* __XEN_NETBACK__COMMON_H__ */
index 20e9defa10606d7d54a0a5412eb93365ce748f5c..852da34b89615ae7c663f18ea210d1a5ea046808 100644 (file)
 #define XENVIF_QUEUE_LENGTH 32
 #define XENVIF_NAPI_WEIGHT  64
 
+static inline void xenvif_stop_queue(struct xenvif_queue *queue)
+{
+       struct net_device *dev = queue->vif->dev;
+
+       if (!queue->vif->can_queue)
+               return;
+
+       netif_tx_stop_queue(netdev_get_tx_queue(dev, queue->id));
+}
+
 int xenvif_schedulable(struct xenvif *vif)
 {
        return netif_running(vif->dev) && netif_carrier_ok(vif->dev);
@@ -50,33 +60,34 @@ int xenvif_schedulable(struct xenvif *vif)
 
 static irqreturn_t xenvif_tx_interrupt(int irq, void *dev_id)
 {
-       struct xenvif *vif = dev_id;
+       struct xenvif_queue *queue = dev_id;
 
-       if (RING_HAS_UNCONSUMED_REQUESTS(&vif->tx))
-               napi_schedule(&vif->napi);
+       if (RING_HAS_UNCONSUMED_REQUESTS(&queue->tx))
+               napi_schedule(&queue->napi);
 
        return IRQ_HANDLED;
 }
 
-static int xenvif_poll(struct napi_struct *napi, int budget)
+int xenvif_poll(struct napi_struct *napi, int budget)
 {
-       struct xenvif *vif = container_of(napi, struct xenvif, napi);
+       struct xenvif_queue *queue =
+               container_of(napi, struct xenvif_queue, napi);
        int work_done;
 
        /* This vif is rogue, we pretend we've there is nothing to do
         * for this vif to deschedule it from NAPI. But this interface
         * will be turned off in thread context later.
         */
-       if (unlikely(vif->disabled)) {
+       if (unlikely(queue->vif->disabled)) {
                napi_complete(napi);
                return 0;
        }
 
-       work_done = xenvif_tx_action(vif, budget);
+       work_done = xenvif_tx_action(queue, budget);
 
        if (work_done < budget) {
                napi_complete(napi);
-               xenvif_napi_schedule_or_enable_events(vif);
+               xenvif_napi_schedule_or_enable_events(queue);
        }
 
        return work_done;
@@ -84,9 +95,9 @@ static int xenvif_poll(struct napi_struct *napi, int budget)
 
 static irqreturn_t xenvif_rx_interrupt(int irq, void *dev_id)
 {
-       struct xenvif *vif = dev_id;
+       struct xenvif_queue *queue = dev_id;
 
-       xenvif_kick_thread(vif);
+       xenvif_kick_thread(queue);
 
        return IRQ_HANDLED;
 }
@@ -99,28 +110,80 @@ static irqreturn_t xenvif_interrupt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-static void xenvif_wake_queue(unsigned long data)
+int xenvif_queue_stopped(struct xenvif_queue *queue)
 {
-       struct xenvif *vif = (struct xenvif *)data;
+       struct net_device *dev = queue->vif->dev;
+       unsigned int id = queue->id;
+       return netif_tx_queue_stopped(netdev_get_tx_queue(dev, id));
+}
 
-       if (netif_queue_stopped(vif->dev)) {
-               netdev_err(vif->dev, "draining TX queue\n");
-               vif->rx_queue_purge = true;
-               xenvif_kick_thread(vif);
-               netif_wake_queue(vif->dev);
+void xenvif_wake_queue(struct xenvif_queue *queue)
+{
+       struct net_device *dev = queue->vif->dev;
+       unsigned int id = queue->id;
+       netif_tx_wake_queue(netdev_get_tx_queue(dev, id));
+}
+
+/* Callback to wake the queue and drain it on timeout */
+static void xenvif_wake_queue_callback(unsigned long data)
+{
+       struct xenvif_queue *queue = (struct xenvif_queue *)data;
+
+       if (xenvif_queue_stopped(queue)) {
+               netdev_err(queue->vif->dev, "draining TX queue\n");
+               queue->rx_queue_purge = true;
+               xenvif_kick_thread(queue);
+               xenvif_wake_queue(queue);
        }
 }
 
+static u16 xenvif_select_queue(struct net_device *dev, struct sk_buff *skb,
+                              void *accel_priv, select_queue_fallback_t fallback)
+{
+       unsigned int num_queues = dev->real_num_tx_queues;
+       u32 hash;
+       u16 queue_index;
+
+       /* First, check if there is only one queue to optimise the
+        * single-queue or old frontend scenario.
+        */
+       if (num_queues == 1) {
+               queue_index = 0;
+       } else {
+               /* Use skb_get_hash to obtain an L4 hash if available */
+               hash = skb_get_hash(skb);
+               queue_index = hash % num_queues;
+       }
+
+       return queue_index;
+}
+
 static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct xenvif *vif = netdev_priv(dev);
+       struct xenvif_queue *queue = NULL;
+       unsigned int num_queues = dev->real_num_tx_queues;
+       u16 index;
        int min_slots_needed;
 
        BUG_ON(skb->dev != dev);
 
-       /* Drop the packet if vif is not ready */
-       if (vif->task == NULL ||
-           vif->dealloc_task == NULL ||
+       /* Drop the packet if queues are not set up */
+       if (num_queues < 1)
+               goto drop;
+
+       /* Obtain the queue to be used to transmit this packet */
+       index = skb_get_queue_mapping(skb);
+       if (index >= num_queues) {
+               pr_warn_ratelimited("Invalid queue %hu for packet on interface %s\n.",
+                                   index, vif->dev->name);
+               index %= num_queues;
+       }
+       queue = &vif->queues[index];
+
+       /* Drop the packet if queue is not ready */
+       if (queue->task == NULL ||
+           queue->dealloc_task == NULL ||
            !xenvif_schedulable(vif))
                goto drop;
 
@@ -139,16 +202,16 @@ static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
         * then turn off the queue to give the ring a chance to
         * drain.
         */
-       if (!xenvif_rx_ring_slots_available(vif, min_slots_needed)) {
-               vif->wake_queue.function = xenvif_wake_queue;
-               vif->wake_queue.data = (unsigned long)vif;
-               xenvif_stop_queue(vif);
-               mod_timer(&vif->wake_queue,
+       if (!xenvif_rx_ring_slots_available(queue, min_slots_needed)) {
+               queue->wake_queue.function = xenvif_wake_queue_callback;
+               queue->wake_queue.data = (unsigned long)queue;
+               xenvif_stop_queue(queue);
+               mod_timer(&queue->wake_queue,
                        jiffies + rx_drain_timeout_jiffies);
        }
 
-       skb_queue_tail(&vif->rx_queue, skb);
-       xenvif_kick_thread(vif);
+       skb_queue_tail(&queue->rx_queue, skb);
+       xenvif_kick_thread(queue);
 
        return NETDEV_TX_OK;
 
@@ -161,25 +224,65 @@ static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
 static struct net_device_stats *xenvif_get_stats(struct net_device *dev)
 {
        struct xenvif *vif = netdev_priv(dev);
+       struct xenvif_queue *queue = NULL;
+       unsigned int num_queues = dev->real_num_tx_queues;
+       unsigned long rx_bytes = 0;
+       unsigned long rx_packets = 0;
+       unsigned long tx_bytes = 0;
+       unsigned long tx_packets = 0;
+       unsigned int index;
+
+       if (vif->queues == NULL)
+               goto out;
+
+       /* Aggregate tx and rx stats from each queue */
+       for (index = 0; index < num_queues; ++index) {
+               queue = &vif->queues[index];
+               rx_bytes += queue->stats.rx_bytes;
+               rx_packets += queue->stats.rx_packets;
+               tx_bytes += queue->stats.tx_bytes;
+               tx_packets += queue->stats.tx_packets;
+       }
+
+out:
+       vif->dev->stats.rx_bytes = rx_bytes;
+       vif->dev->stats.rx_packets = rx_packets;
+       vif->dev->stats.tx_bytes = tx_bytes;
+       vif->dev->stats.tx_packets = tx_packets;
+
        return &vif->dev->stats;
 }
 
 static void xenvif_up(struct xenvif *vif)
 {
-       napi_enable(&vif->napi);
-       enable_irq(vif->tx_irq);
-       if (vif->tx_irq != vif->rx_irq)
-               enable_irq(vif->rx_irq);
-       xenvif_napi_schedule_or_enable_events(vif);
+       struct xenvif_queue *queue = NULL;
+       unsigned int num_queues = vif->dev->real_num_tx_queues;
+       unsigned int queue_index;
+
+       for (queue_index = 0; queue_index < num_queues; ++queue_index) {
+               queue = &vif->queues[queue_index];
+               napi_enable(&queue->napi);
+               enable_irq(queue->tx_irq);
+               if (queue->tx_irq != queue->rx_irq)
+                       enable_irq(queue->rx_irq);
+               xenvif_napi_schedule_or_enable_events(queue);
+       }
 }
 
 static void xenvif_down(struct xenvif *vif)
 {
-       napi_disable(&vif->napi);
-       disable_irq(vif->tx_irq);
-       if (vif->tx_irq != vif->rx_irq)
-               disable_irq(vif->rx_irq);
-       del_timer_sync(&vif->credit_timeout);
+       struct xenvif_queue *queue = NULL;
+       unsigned int num_queues = vif->dev->real_num_tx_queues;
+       unsigned int queue_index;
+
+       for (queue_index = 0; queue_index < num_queues; ++queue_index) {
+               queue = &vif->queues[queue_index];
+               napi_disable(&queue->napi);
+               disable_irq(queue->tx_irq);
+               if (queue->tx_irq != queue->rx_irq)
+                       disable_irq(queue->rx_irq);
+               del_timer_sync(&queue->credit_timeout);
+       }
 }
 
 static int xenvif_open(struct net_device *dev)
@@ -187,7 +290,7 @@ static int xenvif_open(struct net_device *dev)
        struct xenvif *vif = netdev_priv(dev);
        if (netif_carrier_ok(dev))
                xenvif_up(vif);
-       netif_start_queue(dev);
+       netif_tx_start_all_queues(dev);
        return 0;
 }
 
@@ -196,7 +299,7 @@ static int xenvif_close(struct net_device *dev)
        struct xenvif *vif = netdev_priv(dev);
        if (netif_carrier_ok(dev))
                xenvif_down(vif);
-       netif_stop_queue(dev);
+       netif_tx_stop_all_queues(dev);
        return 0;
 }
 
@@ -236,29 +339,29 @@ static const struct xenvif_stat {
 } xenvif_stats[] = {
        {
                "rx_gso_checksum_fixup",
-               offsetof(struct xenvif, rx_gso_checksum_fixup)
+               offsetof(struct xenvif_stats, rx_gso_checksum_fixup)
        },
        /* If (sent != success + fail), there are probably packets never
         * freed up properly!
         */
        {
                "tx_zerocopy_sent",
-               offsetof(struct xenvif, tx_zerocopy_sent),
+               offsetof(struct xenvif_stats, tx_zerocopy_sent),
        },
        {
                "tx_zerocopy_success",
-               offsetof(struct xenvif, tx_zerocopy_success),
+               offsetof(struct xenvif_stats, tx_zerocopy_success),
        },
        {
                "tx_zerocopy_fail",
-               offsetof(struct xenvif, tx_zerocopy_fail)
+               offsetof(struct xenvif_stats, tx_zerocopy_fail)
        },
        /* Number of packets exceeding MAX_SKB_FRAG slots. You should use
         * a guest with the same MAX_SKB_FRAG
         */
        {
                "tx_frag_overflow",
-               offsetof(struct xenvif, tx_frag_overflow)
+               offsetof(struct xenvif_stats, tx_frag_overflow)
        },
 };
 
@@ -275,11 +378,20 @@ static int xenvif_get_sset_count(struct net_device *dev, int string_set)
 static void xenvif_get_ethtool_stats(struct net_device *dev,
                                     struct ethtool_stats *stats, u64 * data)
 {
-       void *vif = netdev_priv(dev);
+       struct xenvif *vif = netdev_priv(dev);
+       unsigned int num_queues = dev->real_num_tx_queues;
        int i;
-
-       for (i = 0; i < ARRAY_SIZE(xenvif_stats); i++)
-               data[i] = *(unsigned long *)(vif + xenvif_stats[i].offset);
+       unsigned int queue_index;
+       struct xenvif_stats *vif_stats;
+
+       for (i = 0; i < ARRAY_SIZE(xenvif_stats); i++) {
+               unsigned long accum = 0;
+               for (queue_index = 0; queue_index < num_queues; ++queue_index) {
+                       vif_stats = &vif->queues[queue_index].stats;
+                       accum += *(unsigned long *)(vif_stats + xenvif_stats[i].offset);
+               }
+               data[i] = accum;
+       }
 }
 
 static void xenvif_get_strings(struct net_device *dev, u32 stringset, u8 * data)
@@ -312,6 +424,7 @@ static const struct net_device_ops xenvif_netdev_ops = {
        .ndo_fix_features = xenvif_fix_features,
        .ndo_set_mac_address = eth_mac_addr,
        .ndo_validate_addr   = eth_validate_addr,
+       .ndo_select_queue = xenvif_select_queue,
 };
 
 struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
@@ -321,10 +434,14 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
        struct net_device *dev;
        struct xenvif *vif;
        char name[IFNAMSIZ] = {};
-       int i;
 
        snprintf(name, IFNAMSIZ - 1, "vif%u.%u", domid, handle);
-       dev = alloc_netdev(sizeof(struct xenvif), name, ether_setup);
+       /* Allocate a netdev with the max. supported number of queues.
+        * When the guest selects the desired number, it will be updated
+        * via netif_set_real_num_tx_queues().
+        */
+       dev = alloc_netdev_mq(sizeof(struct xenvif), name, ether_setup,
+                             xenvif_max_queues);
        if (dev == NULL) {
                pr_warn("Could not allocate netdev for %s\n", name);
                return ERR_PTR(-ENOMEM);
@@ -334,66 +451,28 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
 
        vif = netdev_priv(dev);
 
-       vif->grant_copy_op = vmalloc(sizeof(struct gnttab_copy) *
-                                    MAX_GRANT_COPY_OPS);
-       if (vif->grant_copy_op == NULL) {
-               pr_warn("Could not allocate grant copy space for %s\n", name);
-               free_netdev(dev);
-               return ERR_PTR(-ENOMEM);
-       }
-
        vif->domid  = domid;
        vif->handle = handle;
        vif->can_sg = 1;
        vif->ip_csum = 1;
        vif->dev = dev;
-
        vif->disabled = false;
 
-       vif->credit_bytes = vif->remaining_credit = ~0UL;
-       vif->credit_usec  = 0UL;
-       init_timer(&vif->credit_timeout);
-       vif->credit_window_start = get_jiffies_64();
-
-       init_timer(&vif->wake_queue);
+       /* Start out with no queues. The call below does not require
+        * rtnl_lock() as it happens before register_netdev().
+        */
+       vif->queues = NULL;
+       netif_set_real_num_tx_queues(dev, 0);
 
        dev->netdev_ops = &xenvif_netdev_ops;
        dev->hw_features = NETIF_F_SG |
                NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
                NETIF_F_TSO | NETIF_F_TSO6;
        dev->features = dev->hw_features | NETIF_F_RXCSUM;
-       SET_ETHTOOL_OPS(dev, &xenvif_ethtool_ops);
+       dev->ethtool_ops = &xenvif_ethtool_ops;
 
        dev->tx_queue_len = XENVIF_QUEUE_LENGTH;
 
-       skb_queue_head_init(&vif->rx_queue);
-       skb_queue_head_init(&vif->tx_queue);
-
-       vif->pending_cons = 0;
-       vif->pending_prod = MAX_PENDING_REQS;
-       for (i = 0; i < MAX_PENDING_REQS; i++)
-               vif->pending_ring[i] = i;
-       spin_lock_init(&vif->callback_lock);
-       spin_lock_init(&vif->response_lock);
-       /* If ballooning is disabled, this will consume real memory, so you
-        * better enable it. The long term solution would be to use just a
-        * bunch of valid page descriptors, without dependency on ballooning
-        */
-       err = alloc_xenballooned_pages(MAX_PENDING_REQS,
-                                      vif->mmap_pages,
-                                      false);
-       if (err) {
-               netdev_err(dev, "Could not reserve mmap_pages\n");
-               return ERR_PTR(-ENOMEM);
-       }
-       for (i = 0; i < MAX_PENDING_REQS; i++) {
-               vif->pending_tx_info[i].callback_struct = (struct ubuf_info)
-                       { .callback = xenvif_zerocopy_callback,
-                         .ctx = NULL,
-                         .desc = i };
-               vif->grant_tx_handle[i] = NETBACK_INVALID_HANDLE;
-       }
-
        /*
         * Initialise a dummy MAC address. We choose the numerically
         * largest non-broadcast address to prevent the address getting
@@ -403,8 +482,6 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
        memset(dev->dev_addr, 0xFF, ETH_ALEN);
        dev->dev_addr[0] &= ~0x01;
 
-       netif_napi_add(dev, &vif->napi, xenvif_poll, XENVIF_NAPI_WEIGHT);
-
        netif_carrier_off(dev);
 
        err = register_netdev(dev);
@@ -421,98 +498,147 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
        return vif;
 }
 
-int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref,
+int xenvif_init_queue(struct xenvif_queue *queue)
+{
+       int err, i;
+
+       queue->credit_bytes = queue->remaining_credit = ~0UL;
+       queue->credit_usec  = 0UL;
+       init_timer(&queue->credit_timeout);
+       queue->credit_window_start = get_jiffies_64();
+
+       skb_queue_head_init(&queue->rx_queue);
+       skb_queue_head_init(&queue->tx_queue);
+
+       queue->pending_cons = 0;
+       queue->pending_prod = MAX_PENDING_REQS;
+       for (i = 0; i < MAX_PENDING_REQS; ++i)
+               queue->pending_ring[i] = i;
+
+       spin_lock_init(&queue->callback_lock);
+       spin_lock_init(&queue->response_lock);
+
+       /* If ballooning is disabled, this will consume real memory, so you
+        * better enable it. The long term solution would be to use just a
+        * bunch of valid page descriptors, without dependency on ballooning
+        */
+       err = alloc_xenballooned_pages(MAX_PENDING_REQS,
+                                      queue->mmap_pages,
+                                      false);
+       if (err) {
+               netdev_err(queue->vif->dev, "Could not reserve mmap_pages\n");
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < MAX_PENDING_REQS; i++) {
+               queue->pending_tx_info[i].callback_struct = (struct ubuf_info)
+                       { .callback = xenvif_zerocopy_callback,
+                         .ctx = NULL,
+                         .desc = i };
+               queue->grant_tx_handle[i] = NETBACK_INVALID_HANDLE;
+       }
+
+       init_timer(&queue->wake_queue);
+
+       netif_napi_add(queue->vif->dev, &queue->napi, xenvif_poll,
+                       XENVIF_NAPI_WEIGHT);
+
+       return 0;
+}
+
+void xenvif_carrier_on(struct xenvif *vif)
+{
+       rtnl_lock();
+       if (!vif->can_sg && vif->dev->mtu > ETH_DATA_LEN)
+               dev_set_mtu(vif->dev, ETH_DATA_LEN);
+       netdev_update_features(vif->dev);
+       netif_carrier_on(vif->dev);
+       if (netif_running(vif->dev))
+               xenvif_up(vif);
+       rtnl_unlock();
+}
+
+int xenvif_connect(struct xenvif_queue *queue, unsigned long tx_ring_ref,
                   unsigned long rx_ring_ref, unsigned int tx_evtchn,
                   unsigned int rx_evtchn)
 {
        struct task_struct *task;
        int err = -ENOMEM;
 
-       BUG_ON(vif->tx_irq);
-       BUG_ON(vif->task);
-       BUG_ON(vif->dealloc_task);
+       BUG_ON(queue->tx_irq);
+       BUG_ON(queue->task);
+       BUG_ON(queue->dealloc_task);
 
-       err = xenvif_map_frontend_rings(vif, tx_ring_ref, rx_ring_ref);
+       err = xenvif_map_frontend_rings(queue, tx_ring_ref, rx_ring_ref);
        if (err < 0)
                goto err;
 
-       init_waitqueue_head(&vif->wq);
-       init_waitqueue_head(&vif->dealloc_wq);
+       init_waitqueue_head(&queue->wq);
+       init_waitqueue_head(&queue->dealloc_wq);
 
        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);
+                       queue->vif->domid, tx_evtchn, xenvif_interrupt, 0,
+                       queue->name, queue);
                if (err < 0)
                        goto err_unmap;
-               vif->tx_irq = vif->rx_irq = err;
-               disable_irq(vif->tx_irq);
+               queue->tx_irq = queue->rx_irq = err;
+               disable_irq(queue->tx_irq);
        } else {
                /* feature-split-event-channels == 1 */
-               snprintf(vif->tx_irq_name, sizeof(vif->tx_irq_name),
-                        "%s-tx", vif->dev->name);
+               snprintf(queue->tx_irq_name, sizeof(queue->tx_irq_name),
+                        "%s-tx", queue->name);
                err = bind_interdomain_evtchn_to_irqhandler(
-                       vif->domid, tx_evtchn, xenvif_tx_interrupt, 0,
-                       vif->tx_irq_name, vif);
+                       queue->vif->domid, tx_evtchn, xenvif_tx_interrupt, 0,
+                       queue->tx_irq_name, queue);
                if (err < 0)
                        goto err_unmap;
-               vif->tx_irq = err;
-               disable_irq(vif->tx_irq);
+               queue->tx_irq = err;
+               disable_irq(queue->tx_irq);
 
-               snprintf(vif->rx_irq_name, sizeof(vif->rx_irq_name),
-                        "%s-rx", vif->dev->name);
+               snprintf(queue->rx_irq_name, sizeof(queue->rx_irq_name),
+                        "%s-rx", queue->name);
                err = bind_interdomain_evtchn_to_irqhandler(
-                       vif->domid, rx_evtchn, xenvif_rx_interrupt, 0,
-                       vif->rx_irq_name, vif);
+                       queue->vif->domid, rx_evtchn, xenvif_rx_interrupt, 0,
+                       queue->rx_irq_name, queue);
                if (err < 0)
                        goto err_tx_unbind;
-               vif->rx_irq = err;
-               disable_irq(vif->rx_irq);
+               queue->rx_irq = err;
+               disable_irq(queue->rx_irq);
        }
 
        task = kthread_create(xenvif_kthread_guest_rx,
-                             (void *)vif, "%s-guest-rx", vif->dev->name);
+                             (void *)queue, "%s-guest-rx", queue->name);
        if (IS_ERR(task)) {
-               pr_warn("Could not allocate kthread for %s\n", vif->dev->name);
+               pr_warn("Could not allocate kthread for %s\n", queue->name);
                err = PTR_ERR(task);
                goto err_rx_unbind;
        }
-
-       vif->task = task;
+       queue->task = task;
 
        task = kthread_create(xenvif_dealloc_kthread,
-                             (void *)vif, "%s-dealloc", vif->dev->name);
+                             (void *)queue, "%s-dealloc", queue->name);
        if (IS_ERR(task)) {
-               pr_warn("Could not allocate kthread for %s\n", vif->dev->name);
+               pr_warn("Could not allocate kthread for %s\n", queue->name);
                err = PTR_ERR(task);
                goto err_rx_unbind;
        }
+       queue->dealloc_task = task;
 
-       vif->dealloc_task = task;
-
-       rtnl_lock();
-       if (!vif->can_sg && vif->dev->mtu > ETH_DATA_LEN)
-               dev_set_mtu(vif->dev, ETH_DATA_LEN);
-       netdev_update_features(vif->dev);
-       netif_carrier_on(vif->dev);
-       if (netif_running(vif->dev))
-               xenvif_up(vif);
-       rtnl_unlock();
-
-       wake_up_process(vif->task);
-       wake_up_process(vif->dealloc_task);
+       wake_up_process(queue->task);
+       wake_up_process(queue->dealloc_task);
 
        return 0;
 
 err_rx_unbind:
-       unbind_from_irqhandler(vif->rx_irq, vif);
-       vif->rx_irq = 0;
+       unbind_from_irqhandler(queue->rx_irq, queue);
+       queue->rx_irq = 0;
 err_tx_unbind:
-       unbind_from_irqhandler(vif->tx_irq, vif);
-       vif->tx_irq = 0;
+       unbind_from_irqhandler(queue->tx_irq, queue);
+       queue->tx_irq = 0;
 err_unmap:
-       xenvif_unmap_frontend_rings(vif);
+       xenvif_unmap_frontend_rings(queue);
 err:
        module_put(THIS_MODULE);
        return err;
@@ -529,38 +655,77 @@ void xenvif_carrier_off(struct xenvif *vif)
        rtnl_unlock();
 }
 
+static void xenvif_wait_unmap_timeout(struct xenvif_queue *queue,
+                                     unsigned int worst_case_skb_lifetime)
+{
+       int i, unmap_timeout = 0;
+
+       for (i = 0; i < MAX_PENDING_REQS; ++i) {
+               if (queue->grant_tx_handle[i] != NETBACK_INVALID_HANDLE) {
+                       unmap_timeout++;
+                       schedule_timeout(msecs_to_jiffies(1000));
+                       if (unmap_timeout > worst_case_skb_lifetime &&
+                           net_ratelimit())
+                               netdev_err(queue->vif->dev,
+                                          "Page still granted! Index: %x\n",
+                                          i);
+                       i = -1;
+               }
+       }
+}
+
 void xenvif_disconnect(struct xenvif *vif)
 {
+       struct xenvif_queue *queue = NULL;
+       unsigned int num_queues = vif->dev->real_num_tx_queues;
+       unsigned int queue_index;
+
        if (netif_carrier_ok(vif->dev))
                xenvif_carrier_off(vif);
 
-       if (vif->task) {
-               del_timer_sync(&vif->wake_queue);
-               kthread_stop(vif->task);
-               vif->task = NULL;
-       }
+       for (queue_index = 0; queue_index < num_queues; ++queue_index) {
+               queue = &vif->queues[queue_index];
 
-       if (vif->dealloc_task) {
-               kthread_stop(vif->dealloc_task);
-               vif->dealloc_task = NULL;
-       }
+               if (queue->task) {
+                       del_timer_sync(&queue->wake_queue);
+                       kthread_stop(queue->task);
+                       queue->task = NULL;
+               }
 
-       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);
+               if (queue->dealloc_task) {
+                       kthread_stop(queue->dealloc_task);
+                       queue->dealloc_task = NULL;
                }
-               vif->tx_irq = 0;
+
+               if (queue->tx_irq) {
+                       if (queue->tx_irq == queue->rx_irq)
+                               unbind_from_irqhandler(queue->tx_irq, queue);
+                       else {
+                               unbind_from_irqhandler(queue->tx_irq, queue);
+                               unbind_from_irqhandler(queue->rx_irq, queue);
+                       }
+                       queue->tx_irq = 0;
+               }
+
+               xenvif_unmap_frontend_rings(queue);
        }
+}
 
-       xenvif_unmap_frontend_rings(vif);
+/* Reverse the relevant parts of xenvif_init_queue().
+ * Used for queue teardown from xenvif_free(), and on the
+ * error handling paths in xenbus.c:connect().
+ */
+void xenvif_deinit_queue(struct xenvif_queue *queue)
+{
+       free_xenballooned_pages(MAX_PENDING_REQS, queue->mmap_pages);
+       netif_napi_del(&queue->napi);
 }
 
 void xenvif_free(struct xenvif *vif)
 {
-       int i, unmap_timeout = 0;
+       struct xenvif_queue *queue = NULL;
+       unsigned int num_queues = vif->dev->real_num_tx_queues;
+       unsigned int queue_index;
        /* Here we want to avoid timeout messages if an skb can be legitimately
         * stuck somewhere else. Realistically this could be an another vif's
         * internal or QDisc queue. That another vif also has this
@@ -575,33 +740,21 @@ void xenvif_free(struct xenvif *vif)
        unsigned int worst_case_skb_lifetime = (rx_drain_timeout_msecs/1000) *
                DIV_ROUND_UP(XENVIF_QUEUE_LENGTH, (XEN_NETIF_RX_RING_SIZE / MAX_SKB_FRAGS));
 
-       for (i = 0; i < MAX_PENDING_REQS; ++i) {
-               if (vif->grant_tx_handle[i] != NETBACK_INVALID_HANDLE) {
-                       unmap_timeout++;
-                       schedule_timeout(msecs_to_jiffies(1000));
-                       if (unmap_timeout > worst_case_skb_lifetime &&
-                           net_ratelimit())
-                               netdev_err(vif->dev,
-                                          "Page still granted! Index: %x\n",
-                                          i);
-                       /* If there are still unmapped pages, reset the loop to
-                        * start checking again. We shouldn't exit here until
-                        * dealloc thread and NAPI instance release all the
-                        * pages. If a kernel bug causes the skbs to stall
-                        * somewhere, the interface cannot be brought down
-                        * properly.
-                        */
-                       i = -1;
-               }
-       }
-
-       free_xenballooned_pages(MAX_PENDING_REQS, vif->mmap_pages);
+       unregister_netdev(vif->dev);
 
-       netif_napi_del(&vif->napi);
+       for (queue_index = 0; queue_index < num_queues; ++queue_index) {
+               queue = &vif->queues[queue_index];
+               xenvif_wait_unmap_timeout(queue, worst_case_skb_lifetime);
+               xenvif_deinit_queue(queue);
+       }
 
-       unregister_netdev(vif->dev);
+       /* Free the array of queues. The call below does not require
+        * rtnl_lock() because it happens after unregister_netdev().
+        */
+       netif_set_real_num_tx_queues(vif->dev, 0);
+       vfree(vif->queues);
+       vif->queues = NULL;
 
-       vfree(vif->grant_copy_op);
        free_netdev(vif->dev);
 
        module_put(THIS_MODULE);
index 7367208ee8cdd8b324ce661b48aa69c1d884855b..1844a47636b67c821f03fc8a565092ab59749426 100644 (file)
@@ -62,6 +62,11 @@ unsigned int rx_drain_timeout_msecs = 10000;
 module_param(rx_drain_timeout_msecs, uint, 0444);
 unsigned int rx_drain_timeout_jiffies;
 
+unsigned int xenvif_max_queues;
+module_param_named(max_queues, xenvif_max_queues, uint, 0644);
+MODULE_PARM_DESC(max_queues,
+                "Maximum number of queues per virtual interface");
+
 /*
  * This is the maximum slots a skb can have. If a guest sends a skb
  * which exceeds this limit it is considered malicious.
@@ -70,33 +75,33 @@ unsigned int rx_drain_timeout_jiffies;
 static unsigned int fatal_skb_slots = FATAL_SKB_SLOTS_DEFAULT;
 module_param(fatal_skb_slots, uint, 0444);
 
-static void xenvif_idx_release(struct xenvif *vif, u16 pending_idx,
+static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx,
                               u8 status);
 
-static void make_tx_response(struct xenvif *vif,
+static void make_tx_response(struct xenvif_queue *queue,
                             struct xen_netif_tx_request *txp,
                             s8       st);
 
-static inline int tx_work_todo(struct xenvif *vif);
-static inline int rx_work_todo(struct xenvif *vif);
+static inline int tx_work_todo(struct xenvif_queue *queue);
+static inline int rx_work_todo(struct xenvif_queue *queue);
 
-static struct xen_netif_rx_response *make_rx_response(struct xenvif *vif,
+static struct xen_netif_rx_response *make_rx_response(struct xenvif_queue *queue,
                                             u16      id,
                                             s8       st,
                                             u16      offset,
                                             u16      size,
                                             u16      flags);
 
-static inline unsigned long idx_to_pfn(struct xenvif *vif,
+static inline unsigned long idx_to_pfn(struct xenvif_queue *queue,
                                       u16 idx)
 {
-       return page_to_pfn(vif->mmap_pages[idx]);
+       return page_to_pfn(queue->mmap_pages[idx]);
 }
 
-static inline unsigned long idx_to_kaddr(struct xenvif *vif,
+static inline unsigned long idx_to_kaddr(struct xenvif_queue *queue,
                                         u16 idx)
 {
-       return (unsigned long)pfn_to_kaddr(idx_to_pfn(vif, idx));
+       return (unsigned long)pfn_to_kaddr(idx_to_pfn(queue, idx));
 }
 
 #define callback_param(vif, pending_idx) \
@@ -104,13 +109,13 @@ static inline unsigned long idx_to_kaddr(struct xenvif *vif,
 
 /* Find the containing VIF's structure from a pointer in pending_tx_info array
  */
-static inline struct xenvif *ubuf_to_vif(const struct ubuf_info *ubuf)
+static inline struct xenvif_queue *ubuf_to_queue(const struct ubuf_info *ubuf)
 {
        u16 pending_idx = ubuf->desc;
        struct pending_tx_info *temp =
                container_of(ubuf, struct pending_tx_info, callback_struct);
        return container_of(temp - pending_idx,
-                           struct xenvif,
+                           struct xenvif_queue,
                            pending_tx_info[0]);
 }
 
@@ -136,24 +141,24 @@ static inline pending_ring_idx_t pending_index(unsigned i)
        return i & (MAX_PENDING_REQS-1);
 }
 
-bool xenvif_rx_ring_slots_available(struct xenvif *vif, int needed)
+bool xenvif_rx_ring_slots_available(struct xenvif_queue *queue, int needed)
 {
        RING_IDX prod, cons;
 
        do {
-               prod = vif->rx.sring->req_prod;
-               cons = vif->rx.req_cons;
+               prod = queue->rx.sring->req_prod;
+               cons = queue->rx.req_cons;
 
                if (prod - cons >= needed)
                        return true;
 
-               vif->rx.sring->req_event = prod + 1;
+               queue->rx.sring->req_event = prod + 1;
 
                /* Make sure event is visible before we check prod
                 * again.
                 */
                mb();
-       } while (vif->rx.sring->req_prod != prod);
+       } while (queue->rx.sring->req_prod != prod);
 
        return false;
 }
@@ -163,7 +168,8 @@ bool xenvif_rx_ring_slots_available(struct xenvif *vif, int needed)
  * adding 'size' bytes to a buffer which currently contains 'offset'
  * bytes.
  */
-static bool start_new_rx_buffer(int offset, unsigned long size, int head)
+static bool start_new_rx_buffer(int offset, unsigned long size, int head,
+                               bool full_coalesce)
 {
        /* simple case: we have completely filled the current buffer. */
        if (offset == MAX_BUFFER_OFFSET)
@@ -175,6 +181,7 @@ static bool start_new_rx_buffer(int offset, unsigned long size, int head)
         *     (i)   this frag would fit completely in the next buffer
         * and (ii)  there is already some data in the current buffer
         * and (iii) this is not the head buffer.
+        * and (iv)  there is no need to fully utilize the buffers
         *
         * Where:
         * - (i) stops us splitting a frag into two copies
@@ -185,6 +192,8 @@ static bool start_new_rx_buffer(int offset, unsigned long size, int head)
         *   by (ii) but is explicitly checked because
         *   netfront relies on the first buffer being
         *   non-empty and can crash otherwise.
+        * - (iv) is needed for skbs which can use up more than MAX_SKB_FRAGS
+        *   slot
         *
         * This means we will effectively linearise small
         * frags but do not needlessly split large buffers
@@ -192,7 +201,8 @@ static bool start_new_rx_buffer(int offset, unsigned long size, int head)
         * own buffers as before.
         */
        BUG_ON(size > MAX_BUFFER_OFFSET);
-       if ((offset + size > MAX_BUFFER_OFFSET) && offset && !head)
+       if ((offset + size > MAX_BUFFER_OFFSET) && offset && !head &&
+           !full_coalesce)
                return true;
 
        return false;
@@ -207,13 +217,13 @@ struct netrx_pending_operations {
        grant_ref_t copy_gref;
 };
 
-static struct xenvif_rx_meta *get_next_rx_buffer(struct xenvif *vif,
+static struct xenvif_rx_meta *get_next_rx_buffer(struct xenvif_queue *queue,
                                                 struct netrx_pending_operations *npo)
 {
        struct xenvif_rx_meta *meta;
        struct xen_netif_rx_request *req;
 
-       req = RING_GET_REQUEST(&vif->rx, vif->rx.req_cons++);
+       req = RING_GET_REQUEST(&queue->rx, queue->rx.req_cons++);
 
        meta = npo->meta + npo->meta_prod++;
        meta->gso_type = XEN_NETIF_GSO_TYPE_NONE;
@@ -227,15 +237,22 @@ static struct xenvif_rx_meta *get_next_rx_buffer(struct xenvif *vif,
        return meta;
 }
 
+struct xenvif_rx_cb {
+       int meta_slots_used;
+       bool full_coalesce;
+};
+
+#define XENVIF_RX_CB(skb) ((struct xenvif_rx_cb *)(skb)->cb)
+
 /*
  * Set up the grant operations for this fragment. If it's a flipping
  * interface, we also set up the unmap request from here.
  */
-static void xenvif_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
+static void xenvif_gop_frag_copy(struct xenvif_queue *queue, struct sk_buff *skb,
                                 struct netrx_pending_operations *npo,
                                 struct page *page, unsigned long size,
                                 unsigned long offset, int *head,
-                                struct xenvif *foreign_vif,
+                                struct xenvif_queue *foreign_queue,
                                 grant_ref_t foreign_gref)
 {
        struct gnttab_copy *copy_gop;
@@ -261,14 +278,17 @@ static void xenvif_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
                if (bytes > size)
                        bytes = size;
 
-               if (start_new_rx_buffer(npo->copy_off, bytes, *head)) {
+               if (start_new_rx_buffer(npo->copy_off,
+                                       bytes,
+                                       *head,
+                                       XENVIF_RX_CB(skb)->full_coalesce)) {
                        /*
                         * Netfront requires there to be some data in the head
                         * buffer.
                         */
                        BUG_ON(*head);
 
-                       meta = get_next_rx_buffer(vif, npo);
+                       meta = get_next_rx_buffer(queue, npo);
                }
 
                if (npo->copy_off + bytes > MAX_BUFFER_OFFSET)
@@ -278,8 +298,8 @@ static void xenvif_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
                copy_gop->flags = GNTCOPY_dest_gref;
                copy_gop->len = bytes;
 
-               if (foreign_vif) {
-                       copy_gop->source.domid = foreign_vif->domid;
+               if (foreign_queue) {
+                       copy_gop->source.domid = foreign_queue->vif->domid;
                        copy_gop->source.u.ref = foreign_gref;
                        copy_gop->flags |= GNTCOPY_source_gref;
                } else {
@@ -289,7 +309,7 @@ static void xenvif_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
                }
                copy_gop->source.offset = offset;
 
-               copy_gop->dest.domid = vif->domid;
+               copy_gop->dest.domid = queue->vif->domid;
                copy_gop->dest.offset = npo->copy_off;
                copy_gop->dest.u.ref = npo->copy_gref;
 
@@ -314,8 +334,8 @@ static void xenvif_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
                                gso_type = XEN_NETIF_GSO_TYPE_TCPV6;
                }
 
-               if (*head && ((1 << gso_type) & vif->gso_mask))
-                       vif->rx.req_cons++;
+               if (*head && ((1 << gso_type) & queue->vif->gso_mask))
+                       queue->rx.req_cons++;
 
                *head = 0; /* There must be something in this buffer now. */
 
@@ -337,13 +357,13 @@ static const struct ubuf_info *xenvif_find_gref(const struct sk_buff *const skb,
                                                const int i,
                                                const struct ubuf_info *ubuf)
 {
-       struct xenvif *foreign_vif = ubuf_to_vif(ubuf);
+       struct xenvif_queue *foreign_queue = ubuf_to_queue(ubuf);
 
        do {
                u16 pending_idx = ubuf->desc;
 
                if (skb_shinfo(skb)->frags[i].page.p ==
-                   foreign_vif->mmap_pages[pending_idx])
+                   foreign_queue->mmap_pages[pending_idx])
                        break;
                ubuf = (struct ubuf_info *) ubuf->ctx;
        } while (ubuf);
@@ -364,7 +384,8 @@ static const struct ubuf_info *xenvif_find_gref(const struct sk_buff *const skb,
  * frontend-side LRO).
  */
 static int xenvif_gop_skb(struct sk_buff *skb,
-                         struct netrx_pending_operations *npo)
+                         struct netrx_pending_operations *npo,
+                         struct xenvif_queue *queue)
 {
        struct xenvif *vif = netdev_priv(skb->dev);
        int nr_frags = skb_shinfo(skb)->nr_frags;
@@ -390,7 +411,7 @@ static int xenvif_gop_skb(struct sk_buff *skb,
 
        /* Set up a GSO prefix descriptor, if necessary */
        if ((1 << gso_type) & vif->gso_prefix_mask) {
-               req = RING_GET_REQUEST(&vif->rx, vif->rx.req_cons++);
+               req = RING_GET_REQUEST(&queue->rx, queue->rx.req_cons++);
                meta = npo->meta + npo->meta_prod++;
                meta->gso_type = gso_type;
                meta->gso_size = skb_shinfo(skb)->gso_size;
@@ -398,7 +419,7 @@ static int xenvif_gop_skb(struct sk_buff *skb,
                meta->id = req->id;
        }
 
-       req = RING_GET_REQUEST(&vif->rx, vif->rx.req_cons++);
+       req = RING_GET_REQUEST(&queue->rx, queue->rx.req_cons++);
        meta = npo->meta + npo->meta_prod++;
 
        if ((1 << gso_type) & vif->gso_mask) {
@@ -422,7 +443,7 @@ static int xenvif_gop_skb(struct sk_buff *skb,
                if (data + len > skb_tail_pointer(skb))
                        len = skb_tail_pointer(skb) - data;
 
-               xenvif_gop_frag_copy(vif, skb, npo,
+               xenvif_gop_frag_copy(queue, skb, npo,
                                     virt_to_page(data), len, offset, &head,
                                     NULL,
                                     0);
@@ -433,7 +454,7 @@ static int xenvif_gop_skb(struct sk_buff *skb,
                /* This variable also signals whether foreign_gref has a real
                 * value or not.
                 */
-               struct xenvif *foreign_vif = NULL;
+               struct xenvif_queue *foreign_queue = NULL;
                grant_ref_t foreign_gref;
 
                if ((skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) &&
@@ -458,8 +479,9 @@ static int xenvif_gop_skb(struct sk_buff *skb,
                        if (likely(ubuf)) {
                                u16 pending_idx = ubuf->desc;
 
-                               foreign_vif = ubuf_to_vif(ubuf);
-                               foreign_gref = foreign_vif->pending_tx_info[pending_idx].req.gref;
+                               foreign_queue = ubuf_to_queue(ubuf);
+                               foreign_gref =
+                                       foreign_queue->pending_tx_info[pending_idx].req.gref;
                                /* Just a safety measure. If this was the last
                                 * element on the list, the for loop will
                                 * iterate again if a local page were added to
@@ -477,13 +499,13 @@ static int xenvif_gop_skb(struct sk_buff *skb,
                                 */
                                ubuf = head_ubuf;
                }
-               xenvif_gop_frag_copy(vif, skb, npo,
+               xenvif_gop_frag_copy(queue, skb, npo,
                                     skb_frag_page(&skb_shinfo(skb)->frags[i]),
                                     skb_frag_size(&skb_shinfo(skb)->frags[i]),
                                     skb_shinfo(skb)->frags[i].page_offset,
                                     &head,
-                                    foreign_vif,
-                                    foreign_vif ? foreign_gref : UINT_MAX);
+                                    foreign_queue,
+                                    foreign_queue ? foreign_gref : UINT_MAX);
        }
 
        return npo->meta_prod - old_meta_prod;
@@ -515,7 +537,7 @@ static int xenvif_check_gop(struct xenvif *vif, int nr_meta_slots,
        return status;
 }
 
-static void xenvif_add_frag_responses(struct xenvif *vif, int status,
+static void xenvif_add_frag_responses(struct xenvif_queue *queue, int status,
                                      struct xenvif_rx_meta *meta,
                                      int nr_meta_slots)
 {
@@ -536,23 +558,17 @@ static void xenvif_add_frag_responses(struct xenvif *vif, int status,
                        flags = XEN_NETRXF_more_data;
 
                offset = 0;
-               make_rx_response(vif, meta[i].id, status, offset,
+               make_rx_response(queue, meta[i].id, status, offset,
                                 meta[i].size, flags);
        }
 }
 
-struct xenvif_rx_cb {
-       int meta_slots_used;
-};
-
-#define XENVIF_RX_CB(skb) ((struct xenvif_rx_cb *)(skb)->cb)
-
-void xenvif_kick_thread(struct xenvif *vif)
+void xenvif_kick_thread(struct xenvif_queue *queue)
 {
-       wake_up(&vif->wq);
+       wake_up(&queue->wq);
 }
 
-static void xenvif_rx_action(struct xenvif *vif)
+static void xenvif_rx_action(struct xenvif_queue *queue)
 {
        s8 status;
        u16 flags;
@@ -565,13 +581,13 @@ static void xenvif_rx_action(struct xenvif *vif)
        bool need_to_notify = false;
 
        struct netrx_pending_operations npo = {
-               .copy  = vif->grant_copy_op,
-               .meta  = vif->meta,
+               .copy  = queue->grant_copy_op,
+               .meta  = queue->meta,
        };
 
        skb_queue_head_init(&rxq);
 
-       while ((skb = skb_dequeue(&vif->rx_queue)) != NULL) {
+       while ((skb = skb_dequeue(&queue->rx_queue)) != NULL) {
                RING_IDX max_slots_needed;
                RING_IDX old_req_cons;
                RING_IDX ring_slots_used;
@@ -602,10 +618,15 @@ static void xenvif_rx_action(struct xenvif *vif)
 
                /* To avoid the estimate becoming too pessimal for some
                 * frontends that limit posted rx requests, cap the estimate
-                * at MAX_SKB_FRAGS.
+                * at MAX_SKB_FRAGS. In this case netback will fully coalesce
+                * the skb into the provided slots.
                 */
-               if (max_slots_needed > MAX_SKB_FRAGS)
+               if (max_slots_needed > MAX_SKB_FRAGS) {
                        max_slots_needed = MAX_SKB_FRAGS;
+                       XENVIF_RX_CB(skb)->full_coalesce = true;
+               } else {
+                       XENVIF_RX_CB(skb)->full_coalesce = false;
+               }
 
                /* We may need one more slot for GSO metadata */
                if (skb_is_gso(skb) &&
@@ -614,42 +635,42 @@ static void xenvif_rx_action(struct xenvif *vif)
                        max_slots_needed++;
 
                /* If the skb may not fit then bail out now */
-               if (!xenvif_rx_ring_slots_available(vif, max_slots_needed)) {
-                       skb_queue_head(&vif->rx_queue, skb);
+               if (!xenvif_rx_ring_slots_available(queue, max_slots_needed)) {
+                       skb_queue_head(&queue->rx_queue, skb);
                        need_to_notify = true;
-                       vif->rx_last_skb_slots = max_slots_needed;
+                       queue->rx_last_skb_slots = max_slots_needed;
                        break;
                } else
-                       vif->rx_last_skb_slots = 0;
+                       queue->rx_last_skb_slots = 0;
 
-               old_req_cons = vif->rx.req_cons;
-               XENVIF_RX_CB(skb)->meta_slots_used = xenvif_gop_skb(skb, &npo);
-               ring_slots_used = vif->rx.req_cons - old_req_cons;
+               old_req_cons = queue->rx.req_cons;
+               XENVIF_RX_CB(skb)->meta_slots_used = xenvif_gop_skb(skb, &npo, queue);
+               ring_slots_used = queue->rx.req_cons - old_req_cons;
 
                BUG_ON(ring_slots_used > max_slots_needed);
 
                __skb_queue_tail(&rxq, skb);
        }
 
-       BUG_ON(npo.meta_prod > ARRAY_SIZE(vif->meta));
+       BUG_ON(npo.meta_prod > ARRAY_SIZE(queue->meta));
 
        if (!npo.copy_prod)
                goto done;
 
        BUG_ON(npo.copy_prod > MAX_GRANT_COPY_OPS);
-       gnttab_batch_copy(vif->grant_copy_op, npo.copy_prod);
+       gnttab_batch_copy(queue->grant_copy_op, npo.copy_prod);
 
        while ((skb = __skb_dequeue(&rxq)) != NULL) {
 
-               if ((1 << vif->meta[npo.meta_cons].gso_type) &
-                   vif->gso_prefix_mask) {
-                       resp = RING_GET_RESPONSE(&vif->rx,
-                                                vif->rx.rsp_prod_pvt++);
+               if ((1 << queue->meta[npo.meta_cons].gso_type) &
+                   queue->vif->gso_prefix_mask) {
+                       resp = RING_GET_RESPONSE(&queue->rx,
+                                                queue->rx.rsp_prod_pvt++);
 
                        resp->flags = XEN_NETRXF_gso_prefix | XEN_NETRXF_more_data;
 
-                       resp->offset = vif->meta[npo.meta_cons].gso_size;
-                       resp->id = vif->meta[npo.meta_cons].id;
+                       resp->offset = queue->meta[npo.meta_cons].gso_size;
+                       resp->id = queue->meta[npo.meta_cons].id;
                        resp->status = XENVIF_RX_CB(skb)->meta_slots_used;
 
                        npo.meta_cons++;
@@ -657,10 +678,10 @@ static void xenvif_rx_action(struct xenvif *vif)
                }
 
 
-               vif->dev->stats.tx_bytes += skb->len;
-               vif->dev->stats.tx_packets++;
+               queue->stats.tx_bytes += skb->len;
+               queue->stats.tx_packets++;
 
-               status = xenvif_check_gop(vif,
+               status = xenvif_check_gop(queue->vif,
                                          XENVIF_RX_CB(skb)->meta_slots_used,
                                          &npo);
 
@@ -676,22 +697,22 @@ static void xenvif_rx_action(struct xenvif *vif)
                        flags |= XEN_NETRXF_data_validated;
 
                offset = 0;
-               resp = make_rx_response(vif, vif->meta[npo.meta_cons].id,
+               resp = make_rx_response(queue, queue->meta[npo.meta_cons].id,
                                        status, offset,
-                                       vif->meta[npo.meta_cons].size,
+                                       queue->meta[npo.meta_cons].size,
                                        flags);
 
-               if ((1 << vif->meta[npo.meta_cons].gso_type) &
-                   vif->gso_mask) {
+               if ((1 << queue->meta[npo.meta_cons].gso_type) &
+                   queue->vif->gso_mask) {
                        struct xen_netif_extra_info *gso =
                                (struct xen_netif_extra_info *)
-                               RING_GET_RESPONSE(&vif->rx,
-                                                 vif->rx.rsp_prod_pvt++);
+                               RING_GET_RESPONSE(&queue->rx,
+                                                 queue->rx.rsp_prod_pvt++);
 
                        resp->flags |= XEN_NETRXF_extra_info;
 
-                       gso->u.gso.type = vif->meta[npo.meta_cons].gso_type;
-                       gso->u.gso.size = vif->meta[npo.meta_cons].gso_size;
+                       gso->u.gso.type = queue->meta[npo.meta_cons].gso_type;
+                       gso->u.gso.size = queue->meta[npo.meta_cons].gso_size;
                        gso->u.gso.pad = 0;
                        gso->u.gso.features = 0;
 
@@ -699,11 +720,11 @@ static void xenvif_rx_action(struct xenvif *vif)
                        gso->flags = 0;
                }
 
-               xenvif_add_frag_responses(vif, status,
-                                         vif->meta + npo.meta_cons + 1,
+               xenvif_add_frag_responses(queue, status,
+                                         queue->meta + npo.meta_cons + 1,
                                          XENVIF_RX_CB(skb)->meta_slots_used);
 
-               RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&vif->rx, ret);
+               RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&queue->rx, ret);
 
                need_to_notify |= !!ret;
 
@@ -713,20 +734,20 @@ static void xenvif_rx_action(struct xenvif *vif)
 
 done:
        if (need_to_notify)
-               notify_remote_via_irq(vif->rx_irq);
+               notify_remote_via_irq(queue->rx_irq);
 }
 
-void xenvif_napi_schedule_or_enable_events(struct xenvif *vif)
+void xenvif_napi_schedule_or_enable_events(struct xenvif_queue *queue)
 {
        int more_to_do;
 
-       RING_FINAL_CHECK_FOR_REQUESTS(&vif->tx, more_to_do);
+       RING_FINAL_CHECK_FOR_REQUESTS(&queue->tx, more_to_do);
 
        if (more_to_do)
-               napi_schedule(&vif->napi);
+               napi_schedule(&queue->napi);
 }
 
-static void tx_add_credit(struct xenvif *vif)
+static void tx_add_credit(struct xenvif_queue *queue)
 {
        unsigned long max_burst, max_credit;
 
@@ -734,55 +755,57 @@ static void tx_add_credit(struct xenvif *vif)
         * Allow a burst big enough to transmit a jumbo packet of up to 128kB.
         * Otherwise the interface can seize up due to insufficient credit.
         */
-       max_burst = RING_GET_REQUEST(&vif->tx, vif->tx.req_cons)->size;
+       max_burst = RING_GET_REQUEST(&queue->tx, queue->tx.req_cons)->size;
        max_burst = min(max_burst, 131072UL);
-       max_burst = max(max_burst, vif->credit_bytes);
+       max_burst = max(max_burst, queue->credit_bytes);
 
        /* Take care that adding a new chunk of credit doesn't wrap to zero. */
-       max_credit = vif->remaining_credit + vif->credit_bytes;
-       if (max_credit < vif->remaining_credit)
+       max_credit = queue->remaining_credit + queue->credit_bytes;
+       if (max_credit < queue->remaining_credit)
                max_credit = ULONG_MAX; /* wrapped: clamp to ULONG_MAX */
 
-       vif->remaining_credit = min(max_credit, max_burst);
+       queue->remaining_credit = min(max_credit, max_burst);
 }
 
 static void tx_credit_callback(unsigned long data)
 {
-       struct xenvif *vif = (struct xenvif *)data;
-       tx_add_credit(vif);
-       xenvif_napi_schedule_or_enable_events(vif);
+       struct xenvif_queue *queue = (struct xenvif_queue *)data;
+       tx_add_credit(queue);
+       xenvif_napi_schedule_or_enable_events(queue);
 }
 
-static void xenvif_tx_err(struct xenvif *vif,
+static void xenvif_tx_err(struct xenvif_queue *queue,
                          struct xen_netif_tx_request *txp, RING_IDX end)
 {
-       RING_IDX cons = vif->tx.req_cons;
+       RING_IDX cons = queue->tx.req_cons;
        unsigned long flags;
 
        do {
-               spin_lock_irqsave(&vif->response_lock, flags);
-               make_tx_response(vif, txp, XEN_NETIF_RSP_ERROR);
-               spin_unlock_irqrestore(&vif->response_lock, flags);
+               spin_lock_irqsave(&queue->response_lock, flags);
+               make_tx_response(queue, txp, XEN_NETIF_RSP_ERROR);
+               spin_unlock_irqrestore(&queue->response_lock, flags);
                if (cons == end)
                        break;
-               txp = RING_GET_REQUEST(&vif->tx, cons++);
+               txp = RING_GET_REQUEST(&queue->tx, cons++);
        } while (1);
-       vif->tx.req_cons = cons;
+       queue->tx.req_cons = cons;
 }
 
 static void xenvif_fatal_tx_err(struct xenvif *vif)
 {
        netdev_err(vif->dev, "fatal error; disabling device\n");
        vif->disabled = true;
-       xenvif_kick_thread(vif);
+       /* Disable the vif from queue 0's kthread */
+       if (vif->queues)
+               xenvif_kick_thread(&vif->queues[0]);
 }
 
-static int xenvif_count_requests(struct xenvif *vif,
+static int xenvif_count_requests(struct xenvif_queue *queue,
                                 struct xen_netif_tx_request *first,
                                 struct xen_netif_tx_request *txp,
                                 int work_to_do)
 {
-       RING_IDX cons = vif->tx.req_cons;
+       RING_IDX cons = queue->tx.req_cons;
        int slots = 0;
        int drop_err = 0;
        int more_data;
@@ -794,10 +817,10 @@ static int xenvif_count_requests(struct xenvif *vif,
                struct xen_netif_tx_request dropped_tx = { 0 };
 
                if (slots >= work_to_do) {
-                       netdev_err(vif->dev,
+                       netdev_err(queue->vif->dev,
                                   "Asked for %d slots but exceeds this limit\n",
                                   work_to_do);
-                       xenvif_fatal_tx_err(vif);
+                       xenvif_fatal_tx_err(queue->vif);
                        return -ENODATA;
                }
 
@@ -805,10 +828,10 @@ static int xenvif_count_requests(struct xenvif *vif,
                 * considered malicious.
                 */
                if (unlikely(slots >= fatal_skb_slots)) {
-                       netdev_err(vif->dev,
+                       netdev_err(queue->vif->dev,
                                   "Malicious frontend using %d slots, threshold %u\n",
                                   slots, fatal_skb_slots);
-                       xenvif_fatal_tx_err(vif);
+                       xenvif_fatal_tx_err(queue->vif);
                        return -E2BIG;
                }
 
@@ -821,7 +844,7 @@ static int xenvif_count_requests(struct xenvif *vif,
                 */
                if (!drop_err && slots >= XEN_NETBK_LEGACY_SLOTS_MAX) {
                        if (net_ratelimit())
-                               netdev_dbg(vif->dev,
+                               netdev_dbg(queue->vif->dev,
                                           "Too many slots (%d) exceeding limit (%d), dropping packet\n",
                                           slots, XEN_NETBK_LEGACY_SLOTS_MAX);
                        drop_err = -E2BIG;
@@ -830,7 +853,7 @@ static int xenvif_count_requests(struct xenvif *vif,
                if (drop_err)
                        txp = &dropped_tx;
 
-               memcpy(txp, RING_GET_REQUEST(&vif->tx, cons + slots),
+               memcpy(txp, RING_GET_REQUEST(&queue->tx, cons + slots),
                       sizeof(*txp));
 
                /* If the guest submitted a frame >= 64 KiB then
@@ -844,7 +867,7 @@ static int xenvif_count_requests(struct xenvif *vif,
                 */
                if (!drop_err && txp->size > first->size) {
                        if (net_ratelimit())
-                               netdev_dbg(vif->dev,
+                               netdev_dbg(queue->vif->dev,
                                           "Invalid tx request, slot size %u > remaining size %u\n",
                                           txp->size, first->size);
                        drop_err = -EIO;
@@ -854,9 +877,9 @@ static int xenvif_count_requests(struct xenvif *vif,
                slots++;
 
                if (unlikely((txp->offset + txp->size) > PAGE_SIZE)) {
-                       netdev_err(vif->dev, "Cross page boundary, txp->offset: %x, size: %u\n",
+                       netdev_err(queue->vif->dev, "Cross page boundary, txp->offset: %x, size: %u\n",
                                 txp->offset, txp->size);
-                       xenvif_fatal_tx_err(vif);
+                       xenvif_fatal_tx_err(queue->vif);
                        return -EINVAL;
                }
 
@@ -868,7 +891,7 @@ static int xenvif_count_requests(struct xenvif *vif,
        } while (more_data);
 
        if (drop_err) {
-               xenvif_tx_err(vif, first, cons + slots);
+               xenvif_tx_err(queue, first, cons + slots);
                return drop_err;
        }
 
@@ -882,17 +905,17 @@ struct xenvif_tx_cb {
 
 #define XENVIF_TX_CB(skb) ((struct xenvif_tx_cb *)(skb)->cb)
 
-static inline void xenvif_tx_create_map_op(struct xenvif *vif,
+static inline void xenvif_tx_create_map_op(struct xenvif_queue *queue,
                                          u16 pending_idx,
                                          struct xen_netif_tx_request *txp,
                                          struct gnttab_map_grant_ref *mop)
 {
-       vif->pages_to_map[mop-vif->tx_map_ops] = vif->mmap_pages[pending_idx];
-       gnttab_set_map_op(mop, idx_to_kaddr(vif, pending_idx),
+       queue->pages_to_map[mop-queue->tx_map_ops] = queue->mmap_pages[pending_idx];
+       gnttab_set_map_op(mop, idx_to_kaddr(queue, pending_idx),
                          GNTMAP_host_map | GNTMAP_readonly,
-                         txp->gref, vif->domid);
+                         txp->gref, queue->vif->domid);
 
-       memcpy(&vif->pending_tx_info[pending_idx].req, txp,
+       memcpy(&queue->pending_tx_info[pending_idx].req, txp,
               sizeof(*txp));
 }
 
@@ -913,7 +936,7 @@ static inline struct sk_buff *xenvif_alloc_skb(unsigned int size)
        return skb;
 }
 
-static struct gnttab_map_grant_ref *xenvif_get_requests(struct xenvif *vif,
+static struct gnttab_map_grant_ref *xenvif_get_requests(struct xenvif_queue *queue,
                                                        struct sk_buff *skb,
                                                        struct xen_netif_tx_request *txp,
                                                        struct gnttab_map_grant_ref *gop)
@@ -940,9 +963,9 @@ static struct gnttab_map_grant_ref *xenvif_get_requests(struct xenvif *vif,
 
        for (shinfo->nr_frags = start; shinfo->nr_frags < nr_slots;
             shinfo->nr_frags++, txp++, gop++) {
-               index = pending_index(vif->pending_cons++);
-               pending_idx = vif->pending_ring[index];
-               xenvif_tx_create_map_op(vif, pending_idx, txp, gop);
+               index = pending_index(queue->pending_cons++);
+               pending_idx = queue->pending_ring[index];
+               xenvif_tx_create_map_op(queue, pending_idx, txp, gop);
                frag_set_pending_idx(&frags[shinfo->nr_frags], pending_idx);
        }
 
@@ -950,7 +973,7 @@ static struct gnttab_map_grant_ref *xenvif_get_requests(struct xenvif *vif,
                struct sk_buff *nskb = xenvif_alloc_skb(0);
                if (unlikely(nskb == NULL)) {
                        if (net_ratelimit())
-                               netdev_err(vif->dev,
+                               netdev_err(queue->vif->dev,
                                           "Can't allocate the frag_list skb.\n");
                        return NULL;
                }
@@ -960,9 +983,9 @@ static struct gnttab_map_grant_ref *xenvif_get_requests(struct xenvif *vif,
 
                for (shinfo->nr_frags = 0; shinfo->nr_frags < frag_overflow;
                     shinfo->nr_frags++, txp++, gop++) {
-                       index = pending_index(vif->pending_cons++);
-                       pending_idx = vif->pending_ring[index];
-                       xenvif_tx_create_map_op(vif, pending_idx, txp, gop);
+                       index = pending_index(queue->pending_cons++);
+                       pending_idx = queue->pending_ring[index];
+                       xenvif_tx_create_map_op(queue, pending_idx, txp, gop);
                        frag_set_pending_idx(&frags[shinfo->nr_frags],
                                             pending_idx);
                }
@@ -973,34 +996,34 @@ static struct gnttab_map_grant_ref *xenvif_get_requests(struct xenvif *vif,
        return gop;
 }
 
-static inline void xenvif_grant_handle_set(struct xenvif *vif,
+static inline void xenvif_grant_handle_set(struct xenvif_queue *queue,
                                           u16 pending_idx,
                                           grant_handle_t handle)
 {
-       if (unlikely(vif->grant_tx_handle[pending_idx] !=
+       if (unlikely(queue->grant_tx_handle[pending_idx] !=
                     NETBACK_INVALID_HANDLE)) {
-               netdev_err(vif->dev,
+               netdev_err(queue->vif->dev,
                           "Trying to overwrite active handle! pending_idx: %x\n",
                           pending_idx);
                BUG();
        }
-       vif->grant_tx_handle[pending_idx] = handle;
+       queue->grant_tx_handle[pending_idx] = handle;
 }
 
-static inline void xenvif_grant_handle_reset(struct xenvif *vif,
+static inline void xenvif_grant_handle_reset(struct xenvif_queue *queue,
                                             u16 pending_idx)
 {
-       if (unlikely(vif->grant_tx_handle[pending_idx] ==
+       if (unlikely(queue->grant_tx_handle[pending_idx] ==
                     NETBACK_INVALID_HANDLE)) {
-               netdev_err(vif->dev,
+               netdev_err(queue->vif->dev,
                           "Trying to unmap invalid handle! pending_idx: %x\n",
                           pending_idx);
                BUG();
        }
-       vif->grant_tx_handle[pending_idx] = NETBACK_INVALID_HANDLE;
+       queue->grant_tx_handle[pending_idx] = NETBACK_INVALID_HANDLE;
 }
 
-static int xenvif_tx_check_gop(struct xenvif *vif,
+static int xenvif_tx_check_gop(struct xenvif_queue *queue,
                               struct sk_buff *skb,
                               struct gnttab_map_grant_ref **gopp_map,
                               struct gnttab_copy **gopp_copy)
@@ -1017,12 +1040,12 @@ static int xenvif_tx_check_gop(struct xenvif *vif,
        (*gopp_copy)++;
        if (unlikely(err)) {
                if (net_ratelimit())
-                       netdev_dbg(vif->dev,
+                       netdev_dbg(queue->vif->dev,
                                   "Grant copy of header failed! status: %d pending_idx: %u ref: %u\n",
                                   (*gopp_copy)->status,
                                   pending_idx,
                                   (*gopp_copy)->source.u.ref);
-               xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_ERROR);
+               xenvif_idx_release(queue, pending_idx, XEN_NETIF_RSP_ERROR);
        }
 
 check_frags:
@@ -1035,24 +1058,24 @@ check_frags:
                newerr = gop_map->status;
 
                if (likely(!newerr)) {
-                       xenvif_grant_handle_set(vif,
+                       xenvif_grant_handle_set(queue,
                                                pending_idx,
                                                gop_map->handle);
                        /* Had a previous error? Invalidate this fragment. */
                        if (unlikely(err))
-                               xenvif_idx_unmap(vif, pending_idx);
+                               xenvif_idx_unmap(queue, pending_idx);
                        continue;
                }
 
                /* Error on this fragment: respond to client with an error. */
                if (net_ratelimit())
-                       netdev_dbg(vif->dev,
+                       netdev_dbg(queue->vif->dev,
                                   "Grant map of %d. frag failed! status: %d pending_idx: %u ref: %u\n",
                                   i,
                                   gop_map->status,
                                   pending_idx,
                                   gop_map->ref);
-               xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_ERROR);
+               xenvif_idx_release(queue, pending_idx, XEN_NETIF_RSP_ERROR);
 
                /* Not the first error? Preceding frags already invalidated. */
                if (err)
@@ -1060,7 +1083,7 @@ check_frags:
                /* First error: invalidate preceding fragments. */
                for (j = 0; j < i; j++) {
                        pending_idx = frag_get_pending_idx(&shinfo->frags[j]);
-                       xenvif_idx_unmap(vif, pending_idx);
+                       xenvif_idx_unmap(queue, pending_idx);
                }
 
                /* Remember the error: invalidate all subsequent fragments. */
@@ -1084,7 +1107,7 @@ check_frags:
                shinfo = skb_shinfo(first_skb);
                for (j = 0; j < shinfo->nr_frags; j++) {
                        pending_idx = frag_get_pending_idx(&shinfo->frags[j]);
-                       xenvif_idx_unmap(vif, pending_idx);
+                       xenvif_idx_unmap(queue, pending_idx);
                }
        }
 
@@ -1092,7 +1115,7 @@ check_frags:
        return err;
 }
 
-static void xenvif_fill_frags(struct xenvif *vif, struct sk_buff *skb)
+static void xenvif_fill_frags(struct xenvif_queue *queue, struct sk_buff *skb)
 {
        struct skb_shared_info *shinfo = skb_shinfo(skb);
        int nr_frags = shinfo->nr_frags;
@@ -1110,23 +1133,23 @@ static void xenvif_fill_frags(struct xenvif *vif, struct sk_buff *skb)
                /* If this is not the first frag, chain it to the previous*/
                if (prev_pending_idx == INVALID_PENDING_IDX)
                        skb_shinfo(skb)->destructor_arg =
-                               &callback_param(vif, pending_idx);
+                               &callback_param(queue, pending_idx);
                else
-                       callback_param(vif, prev_pending_idx).ctx =
-                               &callback_param(vif, pending_idx);
+                       callback_param(queue, prev_pending_idx).ctx =
+                               &callback_param(queue, pending_idx);
 
-               callback_param(vif, pending_idx).ctx = NULL;
+               callback_param(queue, pending_idx).ctx = NULL;
                prev_pending_idx = pending_idx;
 
-               txp = &vif->pending_tx_info[pending_idx].req;
-               page = virt_to_page(idx_to_kaddr(vif, pending_idx));
+               txp = &queue->pending_tx_info[pending_idx].req;
+               page = virt_to_page(idx_to_kaddr(queue, pending_idx));
                __skb_fill_page_desc(skb, i, page, txp->offset, txp->size);
                skb->len += txp->size;
                skb->data_len += txp->size;
                skb->truesize += txp->size;
 
                /* Take an extra reference to offset network stack's put_page */
-               get_page(vif->mmap_pages[pending_idx]);
+               get_page(queue->mmap_pages[pending_idx]);
        }
        /* FIXME: __skb_fill_page_desc set this to true because page->pfmemalloc
         * overlaps with "index", and "mapping" is not set. I think mapping
@@ -1136,33 +1159,33 @@ static void xenvif_fill_frags(struct xenvif *vif, struct sk_buff *skb)
        skb->pfmemalloc = false;
 }
 
-static int xenvif_get_extras(struct xenvif *vif,
+static int xenvif_get_extras(struct xenvif_queue *queue,
                                struct xen_netif_extra_info *extras,
                                int work_to_do)
 {
        struct xen_netif_extra_info extra;
-       RING_IDX cons = vif->tx.req_cons;
+       RING_IDX cons = queue->tx.req_cons;
 
        do {
                if (unlikely(work_to_do-- <= 0)) {
-                       netdev_err(vif->dev, "Missing extra info\n");
-                       xenvif_fatal_tx_err(vif);
+                       netdev_err(queue->vif->dev, "Missing extra info\n");
+                       xenvif_fatal_tx_err(queue->vif);
                        return -EBADR;
                }
 
-               memcpy(&extra, RING_GET_REQUEST(&vif->tx, cons),
+               memcpy(&extra, RING_GET_REQUEST(&queue->tx, cons),
                       sizeof(extra));
                if (unlikely(!extra.type ||
                             extra.type >= XEN_NETIF_EXTRA_TYPE_MAX)) {
-                       vif->tx.req_cons = ++cons;
-                       netdev_err(vif->dev,
+                       queue->tx.req_cons = ++cons;
+                       netdev_err(queue->vif->dev,
                                   "Invalid extra type: %d\n", extra.type);
-                       xenvif_fatal_tx_err(vif);
+                       xenvif_fatal_tx_err(queue->vif);
                        return -EINVAL;
                }
 
                memcpy(&extras[extra.type - 1], &extra, sizeof(extra));
-               vif->tx.req_cons = ++cons;
+               queue->tx.req_cons = ++cons;
        } while (extra.flags & XEN_NETIF_EXTRA_FLAG_MORE);
 
        return work_to_do;
@@ -1197,7 +1220,7 @@ static int xenvif_set_skb_gso(struct xenvif *vif,
        return 0;
 }
 
-static int checksum_setup(struct xenvif *vif, struct sk_buff *skb)
+static int checksum_setup(struct xenvif_queue *queue, struct sk_buff *skb)
 {
        bool recalculate_partial_csum = false;
 
@@ -1207,7 +1230,7 @@ static int checksum_setup(struct xenvif *vif, struct sk_buff *skb)
         * recalculate the partial checksum.
         */
        if (skb->ip_summed != CHECKSUM_PARTIAL && skb_is_gso(skb)) {
-               vif->rx_gso_checksum_fixup++;
+               queue->stats.rx_gso_checksum_fixup++;
                skb->ip_summed = CHECKSUM_PARTIAL;
                recalculate_partial_csum = true;
        }
@@ -1219,31 +1242,31 @@ static int checksum_setup(struct xenvif *vif, struct sk_buff *skb)
        return skb_checksum_setup(skb, recalculate_partial_csum);
 }
 
-static bool tx_credit_exceeded(struct xenvif *vif, unsigned size)
+static bool tx_credit_exceeded(struct xenvif_queue *queue, unsigned size)
 {
        u64 now = get_jiffies_64();
-       u64 next_credit = vif->credit_window_start +
-               msecs_to_jiffies(vif->credit_usec / 1000);
+       u64 next_credit = queue->credit_window_start +
+               msecs_to_jiffies(queue->credit_usec / 1000);
 
        /* Timer could already be pending in rare cases. */
-       if (timer_pending(&vif->credit_timeout))
+       if (timer_pending(&queue->credit_timeout))
                return true;
 
        /* Passed the point where we can replenish credit? */
        if (time_after_eq64(now, next_credit)) {
-               vif->credit_window_start = now;
-               tx_add_credit(vif);
+               queue->credit_window_start = now;
+               tx_add_credit(queue);
        }
 
        /* Still too big to send right now? Set a callback. */
-       if (size > vif->remaining_credit) {
-               vif->credit_timeout.data     =
-                       (unsigned long)vif;
-               vif->credit_timeout.function =
+       if (size > queue->remaining_credit) {
+               queue->credit_timeout.data     =
+                       (unsigned long)queue;
+               queue->credit_timeout.function =
                        tx_credit_callback;
-               mod_timer(&vif->credit_timeout,
+               mod_timer(&queue->credit_timeout,
                          next_credit);
-               vif->credit_window_start = next_credit;
+               queue->credit_window_start = next_credit;
 
                return true;
        }
@@ -1251,16 +1274,16 @@ static bool tx_credit_exceeded(struct xenvif *vif, unsigned size)
        return false;
 }
 
-static void xenvif_tx_build_gops(struct xenvif *vif,
+static void xenvif_tx_build_gops(struct xenvif_queue *queue,
                                     int budget,
                                     unsigned *copy_ops,
                                     unsigned *map_ops)
 {
-       struct gnttab_map_grant_ref *gop = vif->tx_map_ops, *request_gop;
+       struct gnttab_map_grant_ref *gop = queue->tx_map_ops, *request_gop;
        struct sk_buff *skb;
        int ret;
 
-       while (skb_queue_len(&vif->tx_queue) < budget) {
+       while (skb_queue_len(&queue->tx_queue) < budget) {
                struct xen_netif_tx_request txreq;
                struct xen_netif_tx_request txfrags[XEN_NETBK_LEGACY_SLOTS_MAX];
                struct xen_netif_extra_info extras[XEN_NETIF_EXTRA_TYPE_MAX-1];
@@ -1270,69 +1293,69 @@ static void xenvif_tx_build_gops(struct xenvif *vif,
                unsigned int data_len;
                pending_ring_idx_t index;
 
-               if (vif->tx.sring->req_prod - vif->tx.req_cons >
+               if (queue->tx.sring->req_prod - queue->tx.req_cons >
                    XEN_NETIF_TX_RING_SIZE) {
-                       netdev_err(vif->dev,
+                       netdev_err(queue->vif->dev,
                                   "Impossible number of requests. "
                                   "req_prod %d, req_cons %d, size %ld\n",
-                                  vif->tx.sring->req_prod, vif->tx.req_cons,
+                                  queue->tx.sring->req_prod, queue->tx.req_cons,
                                   XEN_NETIF_TX_RING_SIZE);
-                       xenvif_fatal_tx_err(vif);
+                       xenvif_fatal_tx_err(queue->vif);
                        break;
                }
 
-               work_to_do = RING_HAS_UNCONSUMED_REQUESTS(&vif->tx);
+               work_to_do = RING_HAS_UNCONSUMED_REQUESTS(&queue->tx);
                if (!work_to_do)
                        break;
 
-               idx = vif->tx.req_cons;
+               idx = queue->tx.req_cons;
                rmb(); /* Ensure that we see the request before we copy it. */
-               memcpy(&txreq, RING_GET_REQUEST(&vif->tx, idx), sizeof(txreq));
+               memcpy(&txreq, RING_GET_REQUEST(&queue->tx, idx), sizeof(txreq));
 
                /* Credit-based scheduling. */
-               if (txreq.size > vif->remaining_credit &&
-                   tx_credit_exceeded(vif, txreq.size))
+               if (txreq.size > queue->remaining_credit &&
+                   tx_credit_exceeded(queue, txreq.size))
                        break;
 
-               vif->remaining_credit -= txreq.size;
+               queue->remaining_credit -= txreq.size;
 
                work_to_do--;
-               vif->tx.req_cons = ++idx;
+               queue->tx.req_cons = ++idx;
 
                memset(extras, 0, sizeof(extras));
                if (txreq.flags & XEN_NETTXF_extra_info) {
-                       work_to_do = xenvif_get_extras(vif, extras,
+                       work_to_do = xenvif_get_extras(queue, extras,
                                                       work_to_do);
-                       idx = vif->tx.req_cons;
+                       idx = queue->tx.req_cons;
                        if (unlikely(work_to_do < 0))
                                break;
                }
 
-               ret = xenvif_count_requests(vif, &txreq, txfrags, work_to_do);
+               ret = xenvif_count_requests(queue, &txreq, txfrags, work_to_do);
                if (unlikely(ret < 0))
                        break;
 
                idx += ret;
 
                if (unlikely(txreq.size < ETH_HLEN)) {
-                       netdev_dbg(vif->dev,
+                       netdev_dbg(queue->vif->dev,
                                   "Bad packet size: %d\n", txreq.size);
-                       xenvif_tx_err(vif, &txreq, idx);
+                       xenvif_tx_err(queue, &txreq, idx);
                        break;
                }
 
                /* No crossing a page as the payload mustn't fragment. */
                if (unlikely((txreq.offset + txreq.size) > PAGE_SIZE)) {
-                       netdev_err(vif->dev,
+                       netdev_err(queue->vif->dev,
                                   "txreq.offset: %x, size: %u, end: %lu\n",
                                   txreq.offset, txreq.size,
                                   (txreq.offset&~PAGE_MASK) + txreq.size);
-                       xenvif_fatal_tx_err(vif);
+                       xenvif_fatal_tx_err(queue->vif);
                        break;
                }
 
-               index = pending_index(vif->pending_cons);
-               pending_idx = vif->pending_ring[index];
+               index = pending_index(queue->pending_cons);
+               pending_idx = queue->pending_ring[index];
 
                data_len = (txreq.size > PKT_PROT_LEN &&
                            ret < XEN_NETBK_LEGACY_SLOTS_MAX) ?
@@ -1340,9 +1363,9 @@ static void xenvif_tx_build_gops(struct xenvif *vif,
 
                skb = xenvif_alloc_skb(data_len);
                if (unlikely(skb == NULL)) {
-                       netdev_dbg(vif->dev,
+                       netdev_dbg(queue->vif->dev,
                                   "Can't allocate a skb in start_xmit.\n");
-                       xenvif_tx_err(vif, &txreq, idx);
+                       xenvif_tx_err(queue, &txreq, idx);
                        break;
                }
 
@@ -1350,7 +1373,7 @@ static void xenvif_tx_build_gops(struct xenvif *vif,
                        struct xen_netif_extra_info *gso;
                        gso = &extras[XEN_NETIF_EXTRA_TYPE_GSO - 1];
 
-                       if (xenvif_set_skb_gso(vif, skb, gso)) {
+                       if (xenvif_set_skb_gso(queue->vif, skb, gso)) {
                                /* Failure in xenvif_set_skb_gso is fatal. */
                                kfree_skb(skb);
                                break;
@@ -1360,18 +1383,18 @@ static void xenvif_tx_build_gops(struct xenvif *vif,
                XENVIF_TX_CB(skb)->pending_idx = pending_idx;
 
                __skb_put(skb, data_len);
-               vif->tx_copy_ops[*copy_ops].source.u.ref = txreq.gref;
-               vif->tx_copy_ops[*copy_ops].source.domid = vif->domid;
-               vif->tx_copy_ops[*copy_ops].source.offset = txreq.offset;
+               queue->tx_copy_ops[*copy_ops].source.u.ref = txreq.gref;
+               queue->tx_copy_ops[*copy_ops].source.domid = queue->vif->domid;
+               queue->tx_copy_ops[*copy_ops].source.offset = txreq.offset;
 
-               vif->tx_copy_ops[*copy_ops].dest.u.gmfn =
+               queue->tx_copy_ops[*copy_ops].dest.u.gmfn =
                        virt_to_mfn(skb->data);
-               vif->tx_copy_ops[*copy_ops].dest.domid = DOMID_SELF;
-               vif->tx_copy_ops[*copy_ops].dest.offset =
+               queue->tx_copy_ops[*copy_ops].dest.domid = DOMID_SELF;
+               queue->tx_copy_ops[*copy_ops].dest.offset =
                        offset_in_page(skb->data);
 
-               vif->tx_copy_ops[*copy_ops].len = data_len;
-               vif->tx_copy_ops[*copy_ops].flags = GNTCOPY_source_gref;
+               queue->tx_copy_ops[*copy_ops].len = data_len;
+               queue->tx_copy_ops[*copy_ops].flags = GNTCOPY_source_gref;
 
                (*copy_ops)++;
 
@@ -1380,42 +1403,42 @@ static void xenvif_tx_build_gops(struct xenvif *vif,
                        skb_shinfo(skb)->nr_frags++;
                        frag_set_pending_idx(&skb_shinfo(skb)->frags[0],
                                             pending_idx);
-                       xenvif_tx_create_map_op(vif, pending_idx, &txreq, gop);
+                       xenvif_tx_create_map_op(queue, pending_idx, &txreq, gop);
                        gop++;
                } else {
                        frag_set_pending_idx(&skb_shinfo(skb)->frags[0],
                                             INVALID_PENDING_IDX);
-                       memcpy(&vif->pending_tx_info[pending_idx].req, &txreq,
+                       memcpy(&queue->pending_tx_info[pending_idx].req, &txreq,
                               sizeof(txreq));
                }
 
-               vif->pending_cons++;
+               queue->pending_cons++;
 
-               request_gop = xenvif_get_requests(vif, skb, txfrags, gop);
+               request_gop = xenvif_get_requests(queue, skb, txfrags, gop);
                if (request_gop == NULL) {
                        kfree_skb(skb);
-                       xenvif_tx_err(vif, &txreq, idx);
+                       xenvif_tx_err(queue, &txreq, idx);
                        break;
                }
                gop = request_gop;
 
-               __skb_queue_tail(&vif->tx_queue, skb);
+               __skb_queue_tail(&queue->tx_queue, skb);
 
-               vif->tx.req_cons = idx;
+               queue->tx.req_cons = idx;
 
-               if (((gop-vif->tx_map_ops) >= ARRAY_SIZE(vif->tx_map_ops)) ||
-                   (*copy_ops >= ARRAY_SIZE(vif->tx_copy_ops)))
+               if (((gop-queue->tx_map_ops) >= ARRAY_SIZE(queue->tx_map_ops)) ||
+                   (*copy_ops >= ARRAY_SIZE(queue->tx_copy_ops)))
                        break;
        }
 
-       (*map_ops) = gop - vif->tx_map_ops;
+       (*map_ops) = gop - queue->tx_map_ops;
        return;
 }
 
 /* Consolidate skb with a frag_list into a brand new one with local pages on
  * frags. Returns 0 or -ENOMEM if can't allocate new pages.
  */
-static int xenvif_handle_frag_list(struct xenvif *vif, struct sk_buff *skb)
+static int xenvif_handle_frag_list(struct xenvif_queue *queue, struct sk_buff *skb)
 {
        unsigned int offset = skb_headlen(skb);
        skb_frag_t frags[MAX_SKB_FRAGS];
@@ -1423,10 +1446,10 @@ static int xenvif_handle_frag_list(struct xenvif *vif, struct sk_buff *skb)
        struct ubuf_info *uarg;
        struct sk_buff *nskb = skb_shinfo(skb)->frag_list;
 
-       vif->tx_zerocopy_sent += 2;
-       vif->tx_frag_overflow++;
+       queue->stats.tx_zerocopy_sent += 2;
+       queue->stats.tx_frag_overflow++;
 
-       xenvif_fill_frags(vif, nskb);
+       xenvif_fill_frags(queue, nskb);
        /* Subtract frags size, we will correct it later */
        skb->truesize -= skb->data_len;
        skb->len += nskb->len;
@@ -1478,37 +1501,37 @@ static int xenvif_handle_frag_list(struct xenvif *vif, struct sk_buff *skb)
        return 0;
 }
 
-static int xenvif_tx_submit(struct xenvif *vif)
+static int xenvif_tx_submit(struct xenvif_queue *queue)
 {
-       struct gnttab_map_grant_ref *gop_map = vif->tx_map_ops;
-       struct gnttab_copy *gop_copy = vif->tx_copy_ops;
+       struct gnttab_map_grant_ref *gop_map = queue->tx_map_ops;
+       struct gnttab_copy *gop_copy = queue->tx_copy_ops;
        struct sk_buff *skb;
        int work_done = 0;
 
-       while ((skb = __skb_dequeue(&vif->tx_queue)) != NULL) {
+       while ((skb = __skb_dequeue(&queue->tx_queue)) != NULL) {
                struct xen_netif_tx_request *txp;
                u16 pending_idx;
                unsigned data_len;
 
                pending_idx = XENVIF_TX_CB(skb)->pending_idx;
-               txp = &vif->pending_tx_info[pending_idx].req;
+               txp = &queue->pending_tx_info[pending_idx].req;
 
                /* Check the remap error code. */
-               if (unlikely(xenvif_tx_check_gop(vif, skb, &gop_map, &gop_copy))) {
+               if (unlikely(xenvif_tx_check_gop(queue, skb, &gop_map, &gop_copy))) {
                        skb_shinfo(skb)->nr_frags = 0;
                        kfree_skb(skb);
                        continue;
                }
 
                data_len = skb->len;
-               callback_param(vif, pending_idx).ctx = NULL;
+               callback_param(queue, pending_idx).ctx = NULL;
                if (data_len < txp->size) {
                        /* Append the packet payload as a fragment. */
                        txp->offset += data_len;
                        txp->size -= data_len;
                } else {
                        /* Schedule a response immediately. */
-                       xenvif_idx_release(vif, pending_idx,
+                       xenvif_idx_release(queue, pending_idx,
                                           XEN_NETIF_RSP_OKAY);
                }
 
@@ -1517,12 +1540,12 @@ static int xenvif_tx_submit(struct xenvif *vif)
                else if (txp->flags & XEN_NETTXF_data_validated)
                        skb->ip_summed = CHECKSUM_UNNECESSARY;
 
-               xenvif_fill_frags(vif, skb);
+               xenvif_fill_frags(queue, skb);
 
                if (unlikely(skb_has_frag_list(skb))) {
-                       if (xenvif_handle_frag_list(vif, skb)) {
+                       if (xenvif_handle_frag_list(queue, skb)) {
                                if (net_ratelimit())
-                                       netdev_err(vif->dev,
+                                       netdev_err(queue->vif->dev,
                                                   "Not enough memory to consolidate frag_list!\n");
                                skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY;
                                kfree_skb(skb);
@@ -1535,12 +1558,12 @@ static int xenvif_tx_submit(struct xenvif *vif)
                        __pskb_pull_tail(skb, target - skb_headlen(skb));
                }
 
-               skb->dev      = vif->dev;
+               skb->dev      = queue->vif->dev;
                skb->protocol = eth_type_trans(skb, skb->dev);
                skb_reset_network_header(skb);
 
-               if (checksum_setup(vif, skb)) {
-                       netdev_dbg(vif->dev,
+               if (checksum_setup(queue, skb)) {
+                       netdev_dbg(queue->vif->dev,
                                   "Can't setup checksum in net_tx_action\n");
                        /* We have to set this flag to trigger the callback */
                        if (skb_shinfo(skb)->destructor_arg)
@@ -1565,8 +1588,8 @@ static int xenvif_tx_submit(struct xenvif *vif)
                                DIV_ROUND_UP(skb->len - hdrlen, mss);
                }
 
-               vif->dev->stats.rx_bytes += skb->len;
-               vif->dev->stats.rx_packets++;
+               queue->stats.rx_bytes += skb->len;
+               queue->stats.rx_packets++;
 
                work_done++;
 
@@ -1577,7 +1600,7 @@ static int xenvif_tx_submit(struct xenvif *vif)
                 */
                if (skb_shinfo(skb)->destructor_arg) {
                        skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY;
-                       vif->tx_zerocopy_sent++;
+                       queue->stats.tx_zerocopy_sent++;
                }
 
                netif_receive_skb(skb);
@@ -1590,47 +1613,47 @@ void xenvif_zerocopy_callback(struct ubuf_info *ubuf, bool zerocopy_success)
 {
        unsigned long flags;
        pending_ring_idx_t index;
-       struct xenvif *vif = ubuf_to_vif(ubuf);
+       struct xenvif_queue *queue = ubuf_to_queue(ubuf);
 
        /* This is the only place where we grab this lock, to protect callbacks
         * from each other.
         */
-       spin_lock_irqsave(&vif->callback_lock, flags);
+       spin_lock_irqsave(&queue->callback_lock, flags);
        do {
                u16 pending_idx = ubuf->desc;
                ubuf = (struct ubuf_info *) ubuf->ctx;
-               BUG_ON(vif->dealloc_prod - vif->dealloc_cons >=
+               BUG_ON(queue->dealloc_prod - queue->dealloc_cons >=
                        MAX_PENDING_REQS);
-               index = pending_index(vif->dealloc_prod);
-               vif->dealloc_ring[index] = pending_idx;
+               index = pending_index(queue->dealloc_prod);
+               queue->dealloc_ring[index] = pending_idx;
                /* Sync with xenvif_tx_dealloc_action:
                 * insert idx then incr producer.
                 */
                smp_wmb();
-               vif->dealloc_prod++;
+               queue->dealloc_prod++;
        } while (ubuf);
-       wake_up(&vif->dealloc_wq);
-       spin_unlock_irqrestore(&vif->callback_lock, flags);
+       wake_up(&queue->dealloc_wq);
+       spin_unlock_irqrestore(&queue->callback_lock, flags);
 
        if (likely(zerocopy_success))
-               vif->tx_zerocopy_success++;
+               queue->stats.tx_zerocopy_success++;
        else
-               vif->tx_zerocopy_fail++;
+               queue->stats.tx_zerocopy_fail++;
 }
 
-static inline void xenvif_tx_dealloc_action(struct xenvif *vif)
+static inline void xenvif_tx_dealloc_action(struct xenvif_queue *queue)
 {
        struct gnttab_unmap_grant_ref *gop;
        pending_ring_idx_t dc, dp;
        u16 pending_idx, pending_idx_release[MAX_PENDING_REQS];
        unsigned int i = 0;
 
-       dc = vif->dealloc_cons;
-       gop = vif->tx_unmap_ops;
+       dc = queue->dealloc_cons;
+       gop = queue->tx_unmap_ops;
 
        /* Free up any grants we have finished using */
        do {
-               dp = vif->dealloc_prod;
+               dp = queue->dealloc_prod;
 
                /* Ensure we see all indices enqueued by all
                 * xenvif_zerocopy_callback().
@@ -1638,38 +1661,38 @@ static inline void xenvif_tx_dealloc_action(struct xenvif *vif)
                smp_rmb();
 
                while (dc != dp) {
-                       BUG_ON(gop - vif->tx_unmap_ops > MAX_PENDING_REQS);
+                       BUG_ON(gop - queue->tx_unmap_ops > MAX_PENDING_REQS);
                        pending_idx =
-                               vif->dealloc_ring[pending_index(dc++)];
+                               queue->dealloc_ring[pending_index(dc++)];
 
-                       pending_idx_release[gop-vif->tx_unmap_ops] =
+                       pending_idx_release[gop-queue->tx_unmap_ops] =
                                pending_idx;
-                       vif->pages_to_unmap[gop-vif->tx_unmap_ops] =
-                               vif->mmap_pages[pending_idx];
+                       queue->pages_to_unmap[gop-queue->tx_unmap_ops] =
+                               queue->mmap_pages[pending_idx];
                        gnttab_set_unmap_op(gop,
-                                           idx_to_kaddr(vif, pending_idx),
+                                           idx_to_kaddr(queue, pending_idx),
                                            GNTMAP_host_map,
-                                           vif->grant_tx_handle[pending_idx]);
-                       xenvif_grant_handle_reset(vif, pending_idx);
+                                           queue->grant_tx_handle[pending_idx]);
+                       xenvif_grant_handle_reset(queue, pending_idx);
                        ++gop;
                }
 
-       } while (dp != vif->dealloc_prod);
+       } while (dp != queue->dealloc_prod);
 
-       vif->dealloc_cons = dc;
+       queue->dealloc_cons = dc;
 
-       if (gop - vif->tx_unmap_ops > 0) {
+       if (gop - queue->tx_unmap_ops > 0) {
                int ret;
-               ret = gnttab_unmap_refs(vif->tx_unmap_ops,
+               ret = gnttab_unmap_refs(queue->tx_unmap_ops,
                                        NULL,
-                                       vif->pages_to_unmap,
-                                       gop - vif->tx_unmap_ops);
+                                       queue->pages_to_unmap,
+                                       gop - queue->tx_unmap_ops);
                if (ret) {
-                       netdev_err(vif->dev, "Unmap fail: nr_ops %tx ret %d\n",
-                                  gop - vif->tx_unmap_ops, ret);
-                       for (i = 0; i < gop - vif->tx_unmap_ops; ++i) {
+                       netdev_err(queue->vif->dev, "Unmap fail: nr_ops %tx ret %d\n",
+                                  gop - queue->tx_unmap_ops, ret);
+                       for (i = 0; i < gop - queue->tx_unmap_ops; ++i) {
                                if (gop[i].status != GNTST_okay)
-                                       netdev_err(vif->dev,
+                                       netdev_err(queue->vif->dev,
                                                   " host_addr: %llx handle: %x status: %d\n",
                                                   gop[i].host_addr,
                                                   gop[i].handle,
@@ -1679,91 +1702,91 @@ static inline void xenvif_tx_dealloc_action(struct xenvif *vif)
                }
        }
 
-       for (i = 0; i < gop - vif->tx_unmap_ops; ++i)
-               xenvif_idx_release(vif, pending_idx_release[i],
+       for (i = 0; i < gop - queue->tx_unmap_ops; ++i)
+               xenvif_idx_release(queue, pending_idx_release[i],
                                   XEN_NETIF_RSP_OKAY);
 }
 
 
 /* Called after netfront has transmitted */
-int xenvif_tx_action(struct xenvif *vif, int budget)
+int xenvif_tx_action(struct xenvif_queue *queue, int budget)
 {
        unsigned nr_mops, nr_cops = 0;
        int work_done, ret;
 
-       if (unlikely(!tx_work_todo(vif)))
+       if (unlikely(!tx_work_todo(queue)))
                return 0;
 
-       xenvif_tx_build_gops(vif, budget, &nr_cops, &nr_mops);
+       xenvif_tx_build_gops(queue, budget, &nr_cops, &nr_mops);
 
        if (nr_cops == 0)
                return 0;
 
-       gnttab_batch_copy(vif->tx_copy_ops, nr_cops);
+       gnttab_batch_copy(queue->tx_copy_ops, nr_cops);
        if (nr_mops != 0) {
-               ret = gnttab_map_refs(vif->tx_map_ops,
+               ret = gnttab_map_refs(queue->tx_map_ops,
                                      NULL,
-                                     vif->pages_to_map,
+                                     queue->pages_to_map,
                                      nr_mops);
                BUG_ON(ret);
        }
 
-       work_done = xenvif_tx_submit(vif);
+       work_done = xenvif_tx_submit(queue);
 
        return work_done;
 }
 
-static void xenvif_idx_release(struct xenvif *vif, u16 pending_idx,
+static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx,
                               u8 status)
 {
        struct pending_tx_info *pending_tx_info;
        pending_ring_idx_t index;
        unsigned long flags;
 
-       pending_tx_info = &vif->pending_tx_info[pending_idx];
-       spin_lock_irqsave(&vif->response_lock, flags);
-       make_tx_response(vif, &pending_tx_info->req, status);
-       index = pending_index(vif->pending_prod);
-       vif->pending_ring[index] = pending_idx;
+       pending_tx_info = &queue->pending_tx_info[pending_idx];
+       spin_lock_irqsave(&queue->response_lock, flags);
+       make_tx_response(queue, &pending_tx_info->req, status);
+       index = pending_index(queue->pending_prod);
+       queue->pending_ring[index] = pending_idx;
        /* TX shouldn't use the index before we give it back here */
        mb();
-       vif->pending_prod++;
-       spin_unlock_irqrestore(&vif->response_lock, flags);
+       queue->pending_prod++;
+       spin_unlock_irqrestore(&queue->response_lock, flags);
 }
 
 
-static void make_tx_response(struct xenvif *vif,
+static void make_tx_response(struct xenvif_queue *queue,
                             struct xen_netif_tx_request *txp,
                             s8       st)
 {
-       RING_IDX i = vif->tx.rsp_prod_pvt;
+       RING_IDX i = queue->tx.rsp_prod_pvt;
        struct xen_netif_tx_response *resp;
        int notify;
 
-       resp = RING_GET_RESPONSE(&vif->tx, i);
+       resp = RING_GET_RESPONSE(&queue->tx, i);
        resp->id     = txp->id;
        resp->status = st;
 
        if (txp->flags & XEN_NETTXF_extra_info)
-               RING_GET_RESPONSE(&vif->tx, ++i)->status = XEN_NETIF_RSP_NULL;
+               RING_GET_RESPONSE(&queue->tx, ++i)->status = XEN_NETIF_RSP_NULL;
 
-       vif->tx.rsp_prod_pvt = ++i;
-       RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&vif->tx, notify);
+       queue->tx.rsp_prod_pvt = ++i;
+       RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&queue->tx, notify);
        if (notify)
-               notify_remote_via_irq(vif->tx_irq);
+               notify_remote_via_irq(queue->tx_irq);
 }
 
-static struct xen_netif_rx_response *make_rx_response(struct xenvif *vif,
+static struct xen_netif_rx_response *make_rx_response(struct xenvif_queue *queue,
                                             u16      id,
                                             s8       st,
                                             u16      offset,
                                             u16      size,
                                             u16      flags)
 {
-       RING_IDX i = vif->rx.rsp_prod_pvt;
+       RING_IDX i = queue->rx.rsp_prod_pvt;
        struct xen_netif_rx_response *resp;
 
-       resp = RING_GET_RESPONSE(&vif->rx, i);
+       resp = RING_GET_RESPONSE(&queue->rx, i);
        resp->offset     = offset;
        resp->flags      = flags;
        resp->id         = id;
@@ -1771,26 +1794,26 @@ static struct xen_netif_rx_response *make_rx_response(struct xenvif *vif,
        if (st < 0)
                resp->status = (s16)st;
 
-       vif->rx.rsp_prod_pvt = ++i;
+       queue->rx.rsp_prod_pvt = ++i;
 
        return resp;
 }
 
-void xenvif_idx_unmap(struct xenvif *vif, u16 pending_idx)
+void xenvif_idx_unmap(struct xenvif_queue *queue, u16 pending_idx)
 {
        int ret;
        struct gnttab_unmap_grant_ref tx_unmap_op;
 
        gnttab_set_unmap_op(&tx_unmap_op,
-                           idx_to_kaddr(vif, pending_idx),
+                           idx_to_kaddr(queue, pending_idx),
                            GNTMAP_host_map,
-                           vif->grant_tx_handle[pending_idx]);
-       xenvif_grant_handle_reset(vif, pending_idx);
+                           queue->grant_tx_handle[pending_idx]);
+       xenvif_grant_handle_reset(queue, pending_idx);
 
        ret = gnttab_unmap_refs(&tx_unmap_op, NULL,
-                               &vif->mmap_pages[pending_idx], 1);
+                               &queue->mmap_pages[pending_idx], 1);
        if (ret) {
-               netdev_err(vif->dev,
+               netdev_err(queue->vif->dev,
                           "Unmap fail: ret: %d pending_idx: %d host_addr: %llx handle: %x status: %d\n",
                           ret,
                           pending_idx,
@@ -1800,41 +1823,40 @@ void xenvif_idx_unmap(struct xenvif *vif, u16 pending_idx)
                BUG();
        }
 
-       xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_OKAY);
+       xenvif_idx_release(queue, pending_idx, XEN_NETIF_RSP_OKAY);
 }
 
-static inline int rx_work_todo(struct xenvif *vif)
+static inline int rx_work_todo(struct xenvif_queue *queue)
 {
-       return (!skb_queue_empty(&vif->rx_queue) &&
-              xenvif_rx_ring_slots_available(vif, vif->rx_last_skb_slots)) ||
-              vif->rx_queue_purge;
+       return (!skb_queue_empty(&queue->rx_queue) &&
+              xenvif_rx_ring_slots_available(queue, queue->rx_last_skb_slots)) ||
+              queue->rx_queue_purge;
 }
 
-static inline int tx_work_todo(struct xenvif *vif)
+static inline int tx_work_todo(struct xenvif_queue *queue)
 {
-
-       if (likely(RING_HAS_UNCONSUMED_REQUESTS(&vif->tx)))
+       if (likely(RING_HAS_UNCONSUMED_REQUESTS(&queue->tx)))
                return 1;
 
        return 0;
 }
 
-static inline bool tx_dealloc_work_todo(struct xenvif *vif)
+static inline bool tx_dealloc_work_todo(struct xenvif_queue *queue)
 {
-       return vif->dealloc_cons != vif->dealloc_prod;
+       return queue->dealloc_cons != queue->dealloc_prod;
 }
 
-void xenvif_unmap_frontend_rings(struct xenvif *vif)
+void xenvif_unmap_frontend_rings(struct xenvif_queue *queue)
 {
-       if (vif->tx.sring)
-               xenbus_unmap_ring_vfree(xenvif_to_xenbus_device(vif),
-                                       vif->tx.sring);
-       if (vif->rx.sring)
-               xenbus_unmap_ring_vfree(xenvif_to_xenbus_device(vif),
-                                       vif->rx.sring);
+       if (queue->tx.sring)
+               xenbus_unmap_ring_vfree(xenvif_to_xenbus_device(queue->vif),
+                                       queue->tx.sring);
+       if (queue->rx.sring)
+               xenbus_unmap_ring_vfree(xenvif_to_xenbus_device(queue->vif),
+                                       queue->rx.sring);
 }
 
-int xenvif_map_frontend_rings(struct xenvif *vif,
+int xenvif_map_frontend_rings(struct xenvif_queue *queue,
                              grant_ref_t tx_ring_ref,
                              grant_ref_t rx_ring_ref)
 {
@@ -1844,85 +1866,78 @@ int xenvif_map_frontend_rings(struct xenvif *vif,
 
        int err = -ENOMEM;
 
-       err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(vif),
+       err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(queue->vif),
                                     tx_ring_ref, &addr);
        if (err)
                goto err;
 
        txs = (struct xen_netif_tx_sring *)addr;
-       BACK_RING_INIT(&vif->tx, txs, PAGE_SIZE);
+       BACK_RING_INIT(&queue->tx, txs, PAGE_SIZE);
 
-       err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(vif),
+       err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(queue->vif),
                                     rx_ring_ref, &addr);
        if (err)
                goto err;
 
        rxs = (struct xen_netif_rx_sring *)addr;
-       BACK_RING_INIT(&vif->rx, rxs, PAGE_SIZE);
+       BACK_RING_INIT(&queue->rx, rxs, PAGE_SIZE);
 
        return 0;
 
 err:
-       xenvif_unmap_frontend_rings(vif);
+       xenvif_unmap_frontend_rings(queue);
        return err;
 }
 
-void xenvif_stop_queue(struct xenvif *vif)
+static void xenvif_start_queue(struct xenvif_queue *queue)
 {
-       if (!vif->can_queue)
-               return;
-
-       netif_stop_queue(vif->dev);
-}
-
-static void xenvif_start_queue(struct xenvif *vif)
-{
-       if (xenvif_schedulable(vif))
-               netif_wake_queue(vif->dev);
+       if (xenvif_schedulable(queue->vif))
+               xenvif_wake_queue(queue);
 }
 
 int xenvif_kthread_guest_rx(void *data)
 {
-       struct xenvif *vif = data;
+       struct xenvif_queue *queue = data;
        struct sk_buff *skb;
 
        while (!kthread_should_stop()) {
-               wait_event_interruptible(vif->wq,
-                                        rx_work_todo(vif) ||
-                                        vif->disabled ||
+               wait_event_interruptible(queue->wq,
+                                        rx_work_todo(queue) ||
+                                        queue->vif->disabled ||
                                         kthread_should_stop());
 
                /* This frontend is found to be rogue, disable it in
                 * kthread context. Currently this is only set when
                 * netback finds out frontend sends malformed packet,
                 * but we cannot disable the interface in softirq
-                * context so we defer it here.
+                * context so we defer it here, if this thread is
+                * associated with queue 0.
                 */
-               if (unlikely(vif->disabled && netif_carrier_ok(vif->dev)))
-                       xenvif_carrier_off(vif);
+               if (unlikely(queue->vif->disabled && netif_carrier_ok(queue->vif->dev) && queue->id == 0))
+                       xenvif_carrier_off(queue->vif);
 
                if (kthread_should_stop())
                        break;
 
-               if (vif->rx_queue_purge) {
-                       skb_queue_purge(&vif->rx_queue);
-                       vif->rx_queue_purge = false;
+               if (queue->rx_queue_purge) {
+                       skb_queue_purge(&queue->rx_queue);
+                       queue->rx_queue_purge = false;
                }
 
-               if (!skb_queue_empty(&vif->rx_queue))
-                       xenvif_rx_action(vif);
+               if (!skb_queue_empty(&queue->rx_queue))
+                       xenvif_rx_action(queue);
 
-               if (skb_queue_empty(&vif->rx_queue) &&
-                   netif_queue_stopped(vif->dev)) {
-                       del_timer_sync(&vif->wake_queue);
-                       xenvif_start_queue(vif);
+               if (skb_queue_empty(&queue->rx_queue) &&
+                   xenvif_queue_stopped(queue)) {
+                       del_timer_sync(&queue->wake_queue);
+                       xenvif_start_queue(queue);
                }
 
                cond_resched();
        }
 
        /* Bin any remaining skbs */
-       while ((skb = skb_dequeue(&vif->rx_queue)) != NULL)
+       while ((skb = skb_dequeue(&queue->rx_queue)) != NULL)
                dev_kfree_skb(skb);
 
        return 0;
@@ -1930,22 +1945,22 @@ int xenvif_kthread_guest_rx(void *data)
 
 int xenvif_dealloc_kthread(void *data)
 {
-       struct xenvif *vif = data;
+       struct xenvif_queue *queue = data;
 
        while (!kthread_should_stop()) {
-               wait_event_interruptible(vif->dealloc_wq,
-                                        tx_dealloc_work_todo(vif) ||
+               wait_event_interruptible(queue->dealloc_wq,
+                                        tx_dealloc_work_todo(queue) ||
                                         kthread_should_stop());
                if (kthread_should_stop())
                        break;
 
-               xenvif_tx_dealloc_action(vif);
+               xenvif_tx_dealloc_action(queue);
                cond_resched();
        }
 
        /* Unmap anything remaining*/
-       if (tx_dealloc_work_todo(vif))
-               xenvif_tx_dealloc_action(vif);
+       if (tx_dealloc_work_todo(queue))
+               xenvif_tx_dealloc_action(queue);
 
        return 0;
 }
@@ -1957,6 +1972,9 @@ static int __init netback_init(void)
        if (!xen_domain())
                return -ENODEV;
 
+       /* Allow as many queues as there are CPUs, by default */
+       xenvif_max_queues = num_online_cpus();
+
        if (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);
index 7a206cffb062c0b3a2521ca7a6ee22d4cd3bcefd..96c63dc2509e325c18ce0c63a243971a33d4d925 100644 (file)
@@ -19,6 +19,8 @@
 */
 
 #include "common.h"
+#include <linux/vmalloc.h>
+#include <linux/rtnetlink.h>
 
 struct backend_info {
        struct xenbus_device *dev;
@@ -34,8 +36,9 @@ struct backend_info {
        u8 have_hotplug_status_watch:1;
 };
 
-static int connect_rings(struct backend_info *);
-static void connect(struct backend_info *);
+static int connect_rings(struct backend_info *be, struct xenvif_queue *queue);
+static void connect(struct backend_info *be);
+static int read_xenbus_vif_flags(struct backend_info *be);
 static void backend_create_xenvif(struct backend_info *be);
 static void unregister_hotplug_status_watch(struct backend_info *be);
 static void set_backend_state(struct backend_info *be,
@@ -157,6 +160,12 @@ static int netback_probe(struct xenbus_device *dev,
        if (err)
                pr_debug("Error writing feature-split-event-channels\n");
 
+       /* Multi-queue support: This is an optional feature. */
+       err = xenbus_printf(XBT_NIL, dev->nodename,
+                           "multi-queue-max-queues", "%u", xenvif_max_queues);
+       if (err)
+               pr_debug("Error writing multi-queue-max-queues\n");
+
        err = xenbus_switch_state(dev, XenbusStateInitWait);
        if (err)
                goto fail;
@@ -485,10 +494,26 @@ static void connect(struct backend_info *be)
 {
        int err;
        struct xenbus_device *dev = be->dev;
+       unsigned long credit_bytes, credit_usec;
+       unsigned int queue_index;
+       unsigned int requested_num_queues;
+       struct xenvif_queue *queue;
 
-       err = connect_rings(be);
-       if (err)
+       /* Check whether the frontend requested multiple queues
+        * and read the number requested.
+        */
+       err = xenbus_scanf(XBT_NIL, dev->otherend,
+                          "multi-queue-num-queues",
+                          "%u", &requested_num_queues);
+       if (err < 0) {
+               requested_num_queues = 1; /* Fall back to single queue */
+       } else if (requested_num_queues > xenvif_max_queues) {
+               /* buggy or malicious guest */
+               xenbus_dev_fatal(dev, err,
+                                "guest requested %u queues, exceeding the maximum of %u.",
+                                requested_num_queues, xenvif_max_queues);
                return;
+       }
 
        err = xen_net_read_mac(dev, be->vif->fe_dev_addr);
        if (err) {
@@ -496,9 +521,54 @@ static void connect(struct backend_info *be)
                return;
        }
 
-       xen_net_read_rate(dev, &be->vif->credit_bytes,
-                         &be->vif->credit_usec);
-       be->vif->remaining_credit = be->vif->credit_bytes;
+       xen_net_read_rate(dev, &credit_bytes, &credit_usec);
+       read_xenbus_vif_flags(be);
+
+       /* Use the number of queues requested by the frontend */
+       be->vif->queues = vzalloc(requested_num_queues *
+                                 sizeof(struct xenvif_queue));
+       rtnl_lock();
+       netif_set_real_num_tx_queues(be->vif->dev, requested_num_queues);
+       rtnl_unlock();
+
+       for (queue_index = 0; queue_index < requested_num_queues; ++queue_index) {
+               queue = &be->vif->queues[queue_index];
+               queue->vif = be->vif;
+               queue->id = queue_index;
+               snprintf(queue->name, sizeof(queue->name), "%s-q%u",
+                               be->vif->dev->name, queue->id);
+
+               err = xenvif_init_queue(queue);
+               if (err) {
+                       /* xenvif_init_queue() cleans up after itself on
+                        * failure, but we need to clean up any previously
+                        * initialised queues. Set num_queues to i so that
+                        * earlier queues can be destroyed using the regular
+                        * disconnect logic.
+                        */
+                       rtnl_lock();
+                       netif_set_real_num_tx_queues(be->vif->dev, queue_index);
+                       rtnl_unlock();
+                       goto err;
+               }
+
+               queue->remaining_credit = credit_bytes;
+
+               err = connect_rings(be, queue);
+               if (err) {
+                       /* connect_rings() cleans up after itself on failure,
+                        * but we need to clean up after xenvif_init_queue() here,
+                        * and also clean up any previously initialised queues.
+                        */
+                       xenvif_deinit_queue(queue);
+                       rtnl_lock();
+                       netif_set_real_num_tx_queues(be->vif->dev, queue_index);
+                       rtnl_unlock();
+                       goto err;
+               }
+       }
+
+       xenvif_carrier_on(be->vif);
 
        unregister_hotplug_status_watch(be);
        err = xenbus_watch_pathfmt(dev, &be->hotplug_status_watch,
@@ -507,45 +577,109 @@ static void connect(struct backend_info *be)
        if (!err)
                be->have_hotplug_status_watch = 1;
 
-       netif_wake_queue(be->vif->dev);
+       netif_tx_wake_all_queues(be->vif->dev);
+
+       return;
+
+err:
+       if (be->vif->dev->real_num_tx_queues > 0)
+               xenvif_disconnect(be->vif); /* Clean up existing queues */
+       vfree(be->vif->queues);
+       be->vif->queues = NULL;
+       rtnl_lock();
+       netif_set_real_num_tx_queues(be->vif->dev, 0);
+       rtnl_unlock();
+       return;
 }
 
 
-static int connect_rings(struct backend_info *be)
+static int connect_rings(struct backend_info *be, struct xenvif_queue *queue)
 {
-       struct xenvif *vif = be->vif;
        struct xenbus_device *dev = be->dev;
+       unsigned int num_queues = queue->vif->dev->real_num_tx_queues;
        unsigned long tx_ring_ref, rx_ring_ref;
-       unsigned int tx_evtchn, rx_evtchn, rx_copy;
+       unsigned int tx_evtchn, rx_evtchn;
        int err;
-       int val;
+       char *xspath;
+       size_t xspathsize;
+       const size_t xenstore_path_ext_size = 11; /* sufficient for "/queue-NNN" */
+
+       /* If the frontend requested 1 queue, or we have fallen back
+        * to single queue due to lack of frontend support for multi-
+        * queue, expect the remaining XenStore keys in the toplevel
+        * directory. Otherwise, expect them in a subdirectory called
+        * queue-N.
+        */
+       if (num_queues == 1) {
+               xspath = kzalloc(strlen(dev->otherend) + 1, GFP_KERNEL);
+               if (!xspath) {
+                       xenbus_dev_fatal(dev, -ENOMEM,
+                                        "reading ring references");
+                       return -ENOMEM;
+               }
+               strcpy(xspath, dev->otherend);
+       } else {
+               xspathsize = strlen(dev->otherend) + xenstore_path_ext_size;
+               xspath = kzalloc(xspathsize, GFP_KERNEL);
+               if (!xspath) {
+                       xenbus_dev_fatal(dev, -ENOMEM,
+                                        "reading ring references");
+                       return -ENOMEM;
+               }
+               snprintf(xspath, xspathsize, "%s/queue-%u", dev->otherend,
+                        queue->id);
+       }
 
-       err = xenbus_gather(XBT_NIL, dev->otherend,
+       err = xenbus_gather(XBT_NIL, xspath,
                            "tx-ring-ref", "%lu", &tx_ring_ref,
                            "rx-ring-ref", "%lu", &rx_ring_ref, NULL);
        if (err) {
                xenbus_dev_fatal(dev, err,
                                 "reading %s/ring-ref",
-                                dev->otherend);
-               return err;
+                                xspath);
+               goto err;
        }
 
        /* Try split event channels first, then single event channel. */
-       err = xenbus_gather(XBT_NIL, dev->otherend,
+       err = xenbus_gather(XBT_NIL, xspath,
                            "event-channel-tx", "%u", &tx_evtchn,
                            "event-channel-rx", "%u", &rx_evtchn, NULL);
        if (err < 0) {
-               err = xenbus_scanf(XBT_NIL, dev->otherend,
+               err = xenbus_scanf(XBT_NIL, xspath,
                                   "event-channel", "%u", &tx_evtchn);
                if (err < 0) {
                        xenbus_dev_fatal(dev, err,
                                         "reading %s/event-channel(-tx/rx)",
-                                        dev->otherend);
-                       return err;
+                                        xspath);
+                       goto err;
                }
                rx_evtchn = tx_evtchn;
        }
 
+       /* Map the shared frame, irq etc. */
+       err = xenvif_connect(queue, tx_ring_ref, rx_ring_ref,
+                            tx_evtchn, rx_evtchn);
+       if (err) {
+               xenbus_dev_fatal(dev, err,
+                                "mapping shared-frames %lu/%lu port tx %u rx %u",
+                                tx_ring_ref, rx_ring_ref,
+                                tx_evtchn, rx_evtchn);
+               goto err;
+       }
+
+       err = 0;
+err: /* Regular return falls through with err == 0 */
+       kfree(xspath);
+       return err;
+}
+
+static int read_xenbus_vif_flags(struct backend_info *be)
+{
+       struct xenvif *vif = be->vif;
+       struct xenbus_device *dev = be->dev;
+       unsigned int rx_copy;
+       int err, val;
+
        err = xenbus_scanf(XBT_NIL, dev->otherend, "request-rx-copy", "%u",
                           &rx_copy);
        if (err == -ENOENT) {
@@ -621,16 +755,6 @@ static int connect_rings(struct backend_info *be)
                val = 0;
        vif->ipv6_csum = !!val;
 
-       /* Map the shared frame, irq etc. */
-       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 tx %u rx %u",
-                                tx_ring_ref, rx_ring_ref,
-                                tx_evtchn, rx_evtchn);
-               return err;
-       }
        return 0;
 }
 
index 158b5e639fc7307d5a98580cfd65ca23b9d3db8a..5a7872ac35661bd039633c4cb77102f40fa58dbb 100644 (file)
 #include <xen/interface/memory.h>
 #include <xen/interface/grant_table.h>
 
+/* Module parameters */
+static unsigned int xennet_max_queues;
+module_param_named(max_queues, xennet_max_queues, uint, 0644);
+MODULE_PARM_DESC(max_queues,
+                "Maximum number of queues per virtual interface");
+
 static const struct ethtool_ops xennet_ethtool_ops;
 
 struct netfront_cb {
@@ -73,6 +79,12 @@ struct netfront_cb {
 #define NET_RX_RING_SIZE __CONST_RING_SIZE(xen_netif_rx, PAGE_SIZE)
 #define TX_MAX_TARGET min_t(int, NET_TX_RING_SIZE, 256)
 
+/* Queue name is interface name with "-qNNN" appended */
+#define QUEUE_NAME_SIZE (IFNAMSIZ + 6)
+
+/* IRQ name is queue name with "-tx" or "-rx" appended */
+#define IRQ_NAME_SIZE (QUEUE_NAME_SIZE + 3)
+
 struct netfront_stats {
        u64                     rx_packets;
        u64                     tx_packets;
@@ -81,9 +93,12 @@ struct netfront_stats {
        struct u64_stats_sync   syncp;
 };
 
-struct netfront_info {
-       struct list_head list;
-       struct net_device *netdev;
+struct netfront_info;
+
+struct netfront_queue {
+       unsigned int id; /* Queue ID, 0-based */
+       char name[QUEUE_NAME_SIZE]; /* DEVNAME-qN */
+       struct netfront_info *info;
 
        struct napi_struct napi;
 
@@ -93,10 +108,8 @@ struct netfront_info {
        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;
+       char tx_irq_name[IRQ_NAME_SIZE]; /* DEVNAME-qN-tx */
+       char rx_irq_name[IRQ_NAME_SIZE]; /* DEVNAME-qN-rx */
 
        spinlock_t   tx_lock;
        struct xen_netif_tx_front_ring tx;
@@ -140,11 +153,21 @@ struct netfront_info {
        unsigned long rx_pfn_array[NET_RX_RING_SIZE];
        struct multicall_entry rx_mcl[NET_RX_RING_SIZE+1];
        struct mmu_update rx_mmu[NET_RX_RING_SIZE];
+};
+
+struct netfront_info {
+       struct list_head list;
+       struct net_device *netdev;
+
+       struct xenbus_device *xbdev;
+
+       /* Multi-queue support */
+       struct netfront_queue *queues;
 
        /* Statistics */
        struct netfront_stats __percpu *stats;
 
-       unsigned long rx_gso_checksum_fixup;
+       atomic_t rx_gso_checksum_fixup;
 };
 
 struct netfront_rx_info {
@@ -187,21 +210,21 @@ static int xennet_rxidx(RING_IDX idx)
        return idx & (NET_RX_RING_SIZE - 1);
 }
 
-static struct sk_buff *xennet_get_rx_skb(struct netfront_info *np,
+static struct sk_buff *xennet_get_rx_skb(struct netfront_queue *queue,
                                         RING_IDX ri)
 {
        int i = xennet_rxidx(ri);
-       struct sk_buff *skb = np->rx_skbs[i];
-       np->rx_skbs[i] = NULL;
+       struct sk_buff *skb = queue->rx_skbs[i];
+       queue->rx_skbs[i] = NULL;
        return skb;
 }
 
-static grant_ref_t xennet_get_rx_ref(struct netfront_info *np,
+static grant_ref_t xennet_get_rx_ref(struct netfront_queue *queue,
                                            RING_IDX ri)
 {
        int i = xennet_rxidx(ri);
-       grant_ref_t ref = np->grant_rx_ref[i];
-       np->grant_rx_ref[i] = GRANT_INVALID_REF;
+       grant_ref_t ref = queue->grant_rx_ref[i];
+       queue->grant_rx_ref[i] = GRANT_INVALID_REF;
        return ref;
 }
 
@@ -221,41 +244,40 @@ static bool xennet_can_sg(struct net_device *dev)
 
 static void rx_refill_timeout(unsigned long data)
 {
-       struct net_device *dev = (struct net_device *)data;
-       struct netfront_info *np = netdev_priv(dev);
-       napi_schedule(&np->napi);
+       struct netfront_queue *queue = (struct netfront_queue *)data;
+       napi_schedule(&queue->napi);
 }
 
-static int netfront_tx_slot_available(struct netfront_info *np)
+static int netfront_tx_slot_available(struct netfront_queue *queue)
 {
-       return (np->tx.req_prod_pvt - np->tx.rsp_cons) <
+       return (queue->tx.req_prod_pvt - queue->tx.rsp_cons) <
                (TX_MAX_TARGET - MAX_SKB_FRAGS - 2);
 }
 
-static void xennet_maybe_wake_tx(struct net_device *dev)
+static void xennet_maybe_wake_tx(struct netfront_queue *queue)
 {
-       struct netfront_info *np = netdev_priv(dev);
+       struct net_device *dev = queue->info->netdev;
+       struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, queue->id);
 
-       if (unlikely(netif_queue_stopped(dev)) &&
-           netfront_tx_slot_available(np) &&
+       if (unlikely(netif_tx_queue_stopped(dev_queue)) &&
+           netfront_tx_slot_available(queue) &&
            likely(netif_running(dev)))
-               netif_wake_queue(dev);
+               netif_tx_wake_queue(netdev_get_tx_queue(dev, queue->id));
 }
 
-static void xennet_alloc_rx_buffers(struct net_device *dev)
+static void xennet_alloc_rx_buffers(struct netfront_queue *queue)
 {
        unsigned short id;
-       struct netfront_info *np = netdev_priv(dev);
        struct sk_buff *skb;
        struct page *page;
        int i, batch_target, notify;
-       RING_IDX req_prod = np->rx.req_prod_pvt;
+       RING_IDX req_prod = queue->rx.req_prod_pvt;
        grant_ref_t ref;
        unsigned long pfn;
        void *vaddr;
        struct xen_netif_rx_request *req;
 
-       if (unlikely(!netif_carrier_ok(dev)))
+       if (unlikely(!netif_carrier_ok(queue->info->netdev)))
                return;
 
        /*
@@ -264,9 +286,10 @@ static void xennet_alloc_rx_buffers(struct net_device *dev)
         * allocator, so should reduce the chance of failed allocation requests
         * both for ourself and for other kernel subsystems.
         */
-       batch_target = np->rx_target - (req_prod - np->rx.rsp_cons);
-       for (i = skb_queue_len(&np->rx_batch); i < batch_target; i++) {
-               skb = __netdev_alloc_skb(dev, RX_COPY_THRESHOLD + NET_IP_ALIGN,
+       batch_target = queue->rx_target - (req_prod - queue->rx.rsp_cons);
+       for (i = skb_queue_len(&queue->rx_batch); i < batch_target; i++) {
+               skb = __netdev_alloc_skb(queue->info->netdev,
+                                        RX_COPY_THRESHOLD + NET_IP_ALIGN,
                                         GFP_ATOMIC | __GFP_NOWARN);
                if (unlikely(!skb))
                        goto no_skb;
@@ -279,7 +302,7 @@ static void xennet_alloc_rx_buffers(struct net_device *dev)
                        kfree_skb(skb);
 no_skb:
                        /* Could not allocate any skbuffs. Try again later. */
-                       mod_timer(&np->rx_refill_timer,
+                       mod_timer(&queue->rx_refill_timer,
                                  jiffies + (HZ/10));
 
                        /* Any skbuffs queued for refill? Force them out. */
@@ -289,44 +312,44 @@ no_skb:
                }
 
                skb_add_rx_frag(skb, 0, page, 0, 0, PAGE_SIZE);
-               __skb_queue_tail(&np->rx_batch, skb);
+               __skb_queue_tail(&queue->rx_batch, skb);
        }
 
        /* Is the batch large enough to be worthwhile? */
-       if (i < (np->rx_target/2)) {
-               if (req_prod > np->rx.sring->req_prod)
+       if (i < (queue->rx_target/2)) {
+               if (req_prod > queue->rx.sring->req_prod)
                        goto push;
                return;
        }
 
        /* Adjust our fill target if we risked running out of buffers. */
-       if (((req_prod - np->rx.sring->rsp_prod) < (np->rx_target / 4)) &&
-           ((np->rx_target *= 2) > np->rx_max_target))
-               np->rx_target = np->rx_max_target;
+       if (((req_prod - queue->rx.sring->rsp_prod) < (queue->rx_target / 4)) &&
+           ((queue->rx_target *= 2) > queue->rx_max_target))
+               queue->rx_target = queue->rx_max_target;
 
  refill:
        for (i = 0; ; i++) {
-               skb = __skb_dequeue(&np->rx_batch);
+               skb = __skb_dequeue(&queue->rx_batch);
                if (skb == NULL)
                        break;
 
-               skb->dev = dev;
+               skb->dev = queue->info->netdev;
 
                id = xennet_rxidx(req_prod + i);
 
-               BUG_ON(np->rx_skbs[id]);
-               np->rx_skbs[id] = skb;
+               BUG_ON(queue->rx_skbs[id]);
+               queue->rx_skbs[id] = skb;
 
-               ref = gnttab_claim_grant_reference(&np->gref_rx_head);
+               ref = gnttab_claim_grant_reference(&queue->gref_rx_head);
                BUG_ON((signed short)ref < 0);
-               np->grant_rx_ref[id] = ref;
+               queue->grant_rx_ref[id] = ref;
 
                pfn = page_to_pfn(skb_frag_page(&skb_shinfo(skb)->frags[0]));
                vaddr = page_address(skb_frag_page(&skb_shinfo(skb)->frags[0]));
 
-               req = RING_GET_REQUEST(&np->rx, req_prod + i);
+               req = RING_GET_REQUEST(&queue->rx, req_prod + i);
                gnttab_grant_foreign_access_ref(ref,
-                                               np->xbdev->otherend_id,
+                                               queue->info->xbdev->otherend_id,
                                                pfn_to_mfn(pfn),
                                                0);
 
@@ -337,72 +360,77 @@ no_skb:
        wmb();          /* barrier so backend seens requests */
 
        /* Above is a suitable barrier to ensure backend will see requests. */
-       np->rx.req_prod_pvt = req_prod + i;
+       queue->rx.req_prod_pvt = req_prod + i;
  push:
-       RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&np->rx, notify);
+       RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&queue->rx, notify);
        if (notify)
-               notify_remote_via_irq(np->rx_irq);
+               notify_remote_via_irq(queue->rx_irq);
 }
 
 static int xennet_open(struct net_device *dev)
 {
        struct netfront_info *np = netdev_priv(dev);
-
-       napi_enable(&np->napi);
-
-       spin_lock_bh(&np->rx_lock);
-       if (netif_carrier_ok(dev)) {
-               xennet_alloc_rx_buffers(dev);
-               np->rx.sring->rsp_event = np->rx.rsp_cons + 1;
-               if (RING_HAS_UNCONSUMED_RESPONSES(&np->rx))
-                       napi_schedule(&np->napi);
+       unsigned int num_queues = dev->real_num_tx_queues;
+       unsigned int i = 0;
+       struct netfront_queue *queue = NULL;
+
+       for (i = 0; i < num_queues; ++i) {
+               queue = &np->queues[i];
+               napi_enable(&queue->napi);
+
+               spin_lock_bh(&queue->rx_lock);
+               if (netif_carrier_ok(dev)) {
+                       xennet_alloc_rx_buffers(queue);
+                       queue->rx.sring->rsp_event = queue->rx.rsp_cons + 1;
+                       if (RING_HAS_UNCONSUMED_RESPONSES(&queue->rx))
+                               napi_schedule(&queue->napi);
+               }
+               spin_unlock_bh(&queue->rx_lock);
        }
-       spin_unlock_bh(&np->rx_lock);
 
-       netif_start_queue(dev);
+       netif_tx_start_all_queues(dev);
 
        return 0;
 }
 
-static void xennet_tx_buf_gc(struct net_device *dev)
+static void xennet_tx_buf_gc(struct netfront_queue *queue)
 {
        RING_IDX cons, prod;
        unsigned short id;
-       struct netfront_info *np = netdev_priv(dev);
        struct sk_buff *skb;
 
-       BUG_ON(!netif_carrier_ok(dev));
+       BUG_ON(!netif_carrier_ok(queue->info->netdev));
 
        do {
-               prod = np->tx.sring->rsp_prod;
+               prod = queue->tx.sring->rsp_prod;
                rmb(); /* Ensure we see responses up to 'rp'. */
 
-               for (cons = np->tx.rsp_cons; cons != prod; cons++) {
+               for (cons = queue->tx.rsp_cons; cons != prod; cons++) {
                        struct xen_netif_tx_response *txrsp;
 
-                       txrsp = RING_GET_RESPONSE(&np->tx, cons);
+                       txrsp = RING_GET_RESPONSE(&queue->tx, cons);
                        if (txrsp->status == XEN_NETIF_RSP_NULL)
                                continue;
 
                        id  = txrsp->id;
-                       skb = np->tx_skbs[id].skb;
+                       skb = queue->tx_skbs[id].skb;
                        if (unlikely(gnttab_query_foreign_access(
-                               np->grant_tx_ref[id]) != 0)) {
+                               queue->grant_tx_ref[id]) != 0)) {
                                pr_alert("%s: warning -- grant still in use by backend domain\n",
                                         __func__);
                                BUG();
                        }
                        gnttab_end_foreign_access_ref(
-                               np->grant_tx_ref[id], GNTMAP_readonly);
+                               queue->grant_tx_ref[id], GNTMAP_readonly);
                        gnttab_release_grant_reference(
-                               &np->gref_tx_head, np->grant_tx_ref[id]);
-                       np->grant_tx_ref[id] = GRANT_INVALID_REF;
-                       np->grant_tx_page[id] = NULL;
-                       add_id_to_freelist(&np->tx_skb_freelist, np->tx_skbs, id);
+                               &queue->gref_tx_head, queue->grant_tx_ref[id]);
+                       queue->grant_tx_ref[id] = GRANT_INVALID_REF;
+                       queue->grant_tx_page[id] = NULL;
+                       add_id_to_freelist(&queue->tx_skb_freelist, queue->tx_skbs, id);
                        dev_kfree_skb_irq(skb);
                }
 
-               np->tx.rsp_cons = prod;
+               queue->tx.rsp_cons = prod;
 
                /*
                 * Set a new event, then check for race with update of tx_cons.
@@ -412,21 +440,20 @@ static void xennet_tx_buf_gc(struct net_device *dev)
                 * data is outstanding: in such cases notification from Xen is
                 * likely to be the only kick that we'll get.
                 */
-               np->tx.sring->rsp_event =
-                       prod + ((np->tx.sring->req_prod - prod) >> 1) + 1;
+               queue->tx.sring->rsp_event =
+                       prod + ((queue->tx.sring->req_prod - prod) >> 1) + 1;
                mb();           /* update shared area */
-       } while ((cons == prod) && (prod != np->tx.sring->rsp_prod));
+       } while ((cons == prod) && (prod != queue->tx.sring->rsp_prod));
 
-       xennet_maybe_wake_tx(dev);
+       xennet_maybe_wake_tx(queue);
 }
 
-static void xennet_make_frags(struct sk_buff *skb, struct net_device *dev,
+static void xennet_make_frags(struct sk_buff *skb, struct netfront_queue *queue,
                              struct xen_netif_tx_request *tx)
 {
-       struct netfront_info *np = netdev_priv(dev);
        char *data = skb->data;
        unsigned long mfn;
-       RING_IDX prod = np->tx.req_prod_pvt;
+       RING_IDX prod = queue->tx.req_prod_pvt;
        int frags = skb_shinfo(skb)->nr_frags;
        unsigned int offset = offset_in_page(data);
        unsigned int len = skb_headlen(skb);
@@ -443,19 +470,19 @@ static void xennet_make_frags(struct sk_buff *skb, struct net_device *dev,
                data += tx->size;
                offset = 0;
 
-               id = get_id_from_freelist(&np->tx_skb_freelist, np->tx_skbs);
-               np->tx_skbs[id].skb = skb_get(skb);
-               tx = RING_GET_REQUEST(&np->tx, prod++);
+               id = get_id_from_freelist(&queue->tx_skb_freelist, queue->tx_skbs);
+               queue->tx_skbs[id].skb = skb_get(skb);
+               tx = RING_GET_REQUEST(&queue->tx, prod++);
                tx->id = id;
-               ref = gnttab_claim_grant_reference(&np->gref_tx_head);
+               ref = gnttab_claim_grant_reference(&queue->gref_tx_head);
                BUG_ON((signed short)ref < 0);
 
                mfn = virt_to_mfn(data);
-               gnttab_grant_foreign_access_ref(ref, np->xbdev->otherend_id,
+               gnttab_grant_foreign_access_ref(ref, queue->info->xbdev->otherend_id,
                                                mfn, GNTMAP_readonly);
 
-               np->grant_tx_page[id] = virt_to_page(data);
-               tx->gref = np->grant_tx_ref[id] = ref;
+               queue->grant_tx_page[id] = virt_to_page(data);
+               tx->gref = queue->grant_tx_ref[id] = ref;
                tx->offset = offset;
                tx->size = len;
                tx->flags = 0;
@@ -487,21 +514,21 @@ static void xennet_make_frags(struct sk_buff *skb, struct net_device *dev,
 
                        tx->flags |= XEN_NETTXF_more_data;
 
-                       id = get_id_from_freelist(&np->tx_skb_freelist,
-                                                 np->tx_skbs);
-                       np->tx_skbs[id].skb = skb_get(skb);
-                       tx = RING_GET_REQUEST(&np->tx, prod++);
+                       id = get_id_from_freelist(&queue->tx_skb_freelist,
+                                                 queue->tx_skbs);
+                       queue->tx_skbs[id].skb = skb_get(skb);
+                       tx = RING_GET_REQUEST(&queue->tx, prod++);
                        tx->id = id;
-                       ref = gnttab_claim_grant_reference(&np->gref_tx_head);
+                       ref = gnttab_claim_grant_reference(&queue->gref_tx_head);
                        BUG_ON((signed short)ref < 0);
 
                        mfn = pfn_to_mfn(page_to_pfn(page));
                        gnttab_grant_foreign_access_ref(ref,
-                                                       np->xbdev->otherend_id,
+                                                       queue->info->xbdev->otherend_id,
                                                        mfn, GNTMAP_readonly);
 
-                       np->grant_tx_page[id] = page;
-                       tx->gref = np->grant_tx_ref[id] = ref;
+                       queue->grant_tx_page[id] = page;
+                       tx->gref = queue->grant_tx_ref[id] = ref;
                        tx->offset = offset;
                        tx->size = bytes;
                        tx->flags = 0;
@@ -518,7 +545,7 @@ static void xennet_make_frags(struct sk_buff *skb, struct net_device *dev,
                }
        }
 
-       np->tx.req_prod_pvt = prod;
+       queue->tx.req_prod_pvt = prod;
 }
 
 /*
@@ -544,6 +571,24 @@ static int xennet_count_skb_frag_slots(struct sk_buff *skb)
        return pages;
 }
 
+static u16 xennet_select_queue(struct net_device *dev, struct sk_buff *skb,
+                              void *accel_priv, select_queue_fallback_t fallback)
+{
+       unsigned int num_queues = dev->real_num_tx_queues;
+       u32 hash;
+       u16 queue_idx;
+
+       /* First, check if there is only one queue */
+       if (num_queues == 1) {
+               queue_idx = 0;
+       } else {
+               hash = skb_get_hash(skb);
+               queue_idx = hash % num_queues;
+       }
+
+       return queue_idx;
+}
+
 static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        unsigned short id;
@@ -559,6 +604,16 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
        unsigned int offset = offset_in_page(data);
        unsigned int len = skb_headlen(skb);
        unsigned long flags;
+       struct netfront_queue *queue = NULL;
+       unsigned int num_queues = dev->real_num_tx_queues;
+       u16 queue_index;
+
+       /* Drop the packet if no queues are set up */
+       if (num_queues < 1)
+               goto drop;
+       /* Determine which queue to transmit this SKB on */
+       queue_index = skb_get_queue_mapping(skb);
+       queue = &np->queues[queue_index];
 
        /* If skb->len is too big for wire format, drop skb and alert
         * user about misconfiguration.
@@ -578,30 +633,30 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
                goto drop;
        }
 
-       spin_lock_irqsave(&np->tx_lock, flags);
+       spin_lock_irqsave(&queue->tx_lock, flags);
 
        if (unlikely(!netif_carrier_ok(dev) ||
                     (slots > 1 && !xennet_can_sg(dev)) ||
                     netif_needs_gso(skb, netif_skb_features(skb)))) {
-               spin_unlock_irqrestore(&np->tx_lock, flags);
+               spin_unlock_irqrestore(&queue->tx_lock, flags);
                goto drop;
        }
 
-       i = np->tx.req_prod_pvt;
+       i = queue->tx.req_prod_pvt;
 
-       id = get_id_from_freelist(&np->tx_skb_freelist, np->tx_skbs);
-       np->tx_skbs[id].skb = skb;
+       id = get_id_from_freelist(&queue->tx_skb_freelist, queue->tx_skbs);
+       queue->tx_skbs[id].skb = skb;
 
-       tx = RING_GET_REQUEST(&np->tx, i);
+       tx = RING_GET_REQUEST(&queue->tx, i);
 
        tx->id   = id;
-       ref = gnttab_claim_grant_reference(&np->gref_tx_head);
+       ref = gnttab_claim_grant_reference(&queue->gref_tx_head);
        BUG_ON((signed short)ref < 0);
        mfn = virt_to_mfn(data);
        gnttab_grant_foreign_access_ref(
-               ref, np->xbdev->otherend_id, mfn, GNTMAP_readonly);
-       np->grant_tx_page[id] = virt_to_page(data);
-       tx->gref = np->grant_tx_ref[id] = ref;
+               ref, queue->info->xbdev->otherend_id, mfn, GNTMAP_readonly);
+       queue->grant_tx_page[id] = virt_to_page(data);
+       tx->gref = queue->grant_tx_ref[id] = ref;
        tx->offset = offset;
        tx->size = len;
 
@@ -617,7 +672,7 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
                struct xen_netif_extra_info *gso;
 
                gso = (struct xen_netif_extra_info *)
-                       RING_GET_REQUEST(&np->tx, ++i);
+                       RING_GET_REQUEST(&queue->tx, ++i);
 
                tx->flags |= XEN_NETTXF_extra_info;
 
@@ -632,14 +687,14 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
                gso->flags = 0;
        }
 
-       np->tx.req_prod_pvt = i + 1;
+       queue->tx.req_prod_pvt = i + 1;
 
-       xennet_make_frags(skb, dev, tx);
+       xennet_make_frags(skb, queue, tx);
        tx->size = skb->len;
 
-       RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&np->tx, notify);
+       RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&queue->tx, notify);
        if (notify)
-               notify_remote_via_irq(np->tx_irq);
+               notify_remote_via_irq(queue->tx_irq);
 
        u64_stats_update_begin(&stats->syncp);
        stats->tx_bytes += skb->len;
@@ -647,12 +702,12 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
        u64_stats_update_end(&stats->syncp);
 
        /* Note: It is not safe to access skb after xennet_tx_buf_gc()! */
-       xennet_tx_buf_gc(dev);
+       xennet_tx_buf_gc(queue);
 
-       if (!netfront_tx_slot_available(np))
-               netif_stop_queue(dev);
+       if (!netfront_tx_slot_available(queue))
+               netif_tx_stop_queue(netdev_get_tx_queue(dev, queue->id));
 
-       spin_unlock_irqrestore(&np->tx_lock, flags);
+       spin_unlock_irqrestore(&queue->tx_lock, flags);
 
        return NETDEV_TX_OK;
 
@@ -665,32 +720,38 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
 static int xennet_close(struct net_device *dev)
 {
        struct netfront_info *np = netdev_priv(dev);
-       netif_stop_queue(np->netdev);
-       napi_disable(&np->napi);
+       unsigned int num_queues = dev->real_num_tx_queues;
+       unsigned int i;
+       struct netfront_queue *queue;
+       netif_tx_stop_all_queues(np->netdev);
+       for (i = 0; i < num_queues; ++i) {
+               queue = &np->queues[i];
+               napi_disable(&queue->napi);
+       }
        return 0;
 }
 
-static void xennet_move_rx_slot(struct netfront_info *np, struct sk_buff *skb,
+static void xennet_move_rx_slot(struct netfront_queue *queue, struct sk_buff *skb,
                                grant_ref_t ref)
 {
-       int new = xennet_rxidx(np->rx.req_prod_pvt);
-
-       BUG_ON(np->rx_skbs[new]);
-       np->rx_skbs[new] = skb;
-       np->grant_rx_ref[new] = ref;
-       RING_GET_REQUEST(&np->rx, np->rx.req_prod_pvt)->id = new;
-       RING_GET_REQUEST(&np->rx, np->rx.req_prod_pvt)->gref = ref;
-       np->rx.req_prod_pvt++;
+       int new = xennet_rxidx(queue->rx.req_prod_pvt);
+
+       BUG_ON(queue->rx_skbs[new]);
+       queue->rx_skbs[new] = skb;
+       queue->grant_rx_ref[new] = ref;
+       RING_GET_REQUEST(&queue->rx, queue->rx.req_prod_pvt)->id = new;
+       RING_GET_REQUEST(&queue->rx, queue->rx.req_prod_pvt)->gref = ref;
+       queue->rx.req_prod_pvt++;
 }
 
-static int xennet_get_extras(struct netfront_info *np,
+static int xennet_get_extras(struct netfront_queue *queue,
                             struct xen_netif_extra_info *extras,
                             RING_IDX rp)
 
 {
        struct xen_netif_extra_info *extra;
-       struct device *dev = &np->netdev->dev;
-       RING_IDX cons = np->rx.rsp_cons;
+       struct device *dev = &queue->info->netdev->dev;
+       RING_IDX cons = queue->rx.rsp_cons;
        int err = 0;
 
        do {
@@ -705,7 +766,7 @@ static int xennet_get_extras(struct netfront_info *np,
                }
 
                extra = (struct xen_netif_extra_info *)
-                       RING_GET_RESPONSE(&np->rx, ++cons);
+                       RING_GET_RESPONSE(&queue->rx, ++cons);
 
                if (unlikely(!extra->type ||
                             extra->type >= XEN_NETIF_EXTRA_TYPE_MAX)) {
@@ -718,33 +779,33 @@ static int xennet_get_extras(struct netfront_info *np,
                               sizeof(*extra));
                }
 
-               skb = xennet_get_rx_skb(np, cons);
-               ref = xennet_get_rx_ref(np, cons);
-               xennet_move_rx_slot(np, skb, ref);
+               skb = xennet_get_rx_skb(queue, cons);
+               ref = xennet_get_rx_ref(queue, cons);
+               xennet_move_rx_slot(queue, skb, ref);
        } while (extra->flags & XEN_NETIF_EXTRA_FLAG_MORE);
 
-       np->rx.rsp_cons = cons;
+       queue->rx.rsp_cons = cons;
        return err;
 }
 
-static int xennet_get_responses(struct netfront_info *np,
+static int xennet_get_responses(struct netfront_queue *queue,
                                struct netfront_rx_info *rinfo, RING_IDX rp,
                                struct sk_buff_head *list)
 {
        struct xen_netif_rx_response *rx = &rinfo->rx;
        struct xen_netif_extra_info *extras = rinfo->extras;
-       struct device *dev = &np->netdev->dev;
-       RING_IDX cons = np->rx.rsp_cons;
-       struct sk_buff *skb = xennet_get_rx_skb(np, cons);
-       grant_ref_t ref = xennet_get_rx_ref(np, cons);
+       struct device *dev = &queue->info->netdev->dev;
+       RING_IDX cons = queue->rx.rsp_cons;
+       struct sk_buff *skb = xennet_get_rx_skb(queue, cons);
+       grant_ref_t ref = xennet_get_rx_ref(queue, cons);
        int max = MAX_SKB_FRAGS + (rx->status <= RX_COPY_THRESHOLD);
        int slots = 1;
        int err = 0;
        unsigned long ret;
 
        if (rx->flags & XEN_NETRXF_extra_info) {
-               err = xennet_get_extras(np, extras, rp);
-               cons = np->rx.rsp_cons;
+               err = xennet_get_extras(queue, extras, rp);
+               cons = queue->rx.rsp_cons;
        }
 
        for (;;) {
@@ -753,7 +814,7 @@ static int xennet_get_responses(struct netfront_info *np,
                        if (net_ratelimit())
                                dev_warn(dev, "rx->offset: %x, size: %u\n",
                                         rx->offset, rx->status);
-                       xennet_move_rx_slot(np, skb, ref);
+                       xennet_move_rx_slot(queue, skb, ref);
                        err = -EINVAL;
                        goto next;
                }
@@ -774,7 +835,7 @@ static int xennet_get_responses(struct netfront_info *np,
                ret = gnttab_end_foreign_access_ref(ref, 0);
                BUG_ON(!ret);
 
-               gnttab_release_grant_reference(&np->gref_rx_head, ref);
+               gnttab_release_grant_reference(&queue->gref_rx_head, ref);
 
                __skb_queue_tail(list, skb);
 
@@ -789,9 +850,9 @@ next:
                        break;
                }
 
-               rx = RING_GET_RESPONSE(&np->rx, cons + slots);
-               skb = xennet_get_rx_skb(np, cons + slots);
-               ref = xennet_get_rx_ref(np, cons + slots);
+               rx = RING_GET_RESPONSE(&queue->rx, cons + slots);
+               skb = xennet_get_rx_skb(queue, cons + slots);
+               ref = xennet_get_rx_ref(queue, cons + slots);
                slots++;
        }
 
@@ -802,7 +863,7 @@ next:
        }
 
        if (unlikely(err))
-               np->rx.rsp_cons = cons + slots;
+               queue->rx.rsp_cons = cons + slots;
 
        return err;
 }
@@ -836,17 +897,17 @@ static int xennet_set_skb_gso(struct sk_buff *skb,
        return 0;
 }
 
-static RING_IDX xennet_fill_frags(struct netfront_info *np,
+static RING_IDX xennet_fill_frags(struct netfront_queue *queue,
                                  struct sk_buff *skb,
                                  struct sk_buff_head *list)
 {
        struct skb_shared_info *shinfo = skb_shinfo(skb);
-       RING_IDX cons = np->rx.rsp_cons;
+       RING_IDX cons = queue->rx.rsp_cons;
        struct sk_buff *nskb;
 
        while ((nskb = __skb_dequeue(list))) {
                struct xen_netif_rx_response *rx =
-                       RING_GET_RESPONSE(&np->rx, ++cons);
+                       RING_GET_RESPONSE(&queue->rx, ++cons);
                skb_frag_t *nfrag = &skb_shinfo(nskb)->frags[0];
 
                if (shinfo->nr_frags == MAX_SKB_FRAGS) {
@@ -879,7 +940,7 @@ static int checksum_setup(struct net_device *dev, struct sk_buff *skb)
         */
        if (skb->ip_summed != CHECKSUM_PARTIAL && skb_is_gso(skb)) {
                struct netfront_info *np = netdev_priv(dev);
-               np->rx_gso_checksum_fixup++;
+               atomic_inc(&np->rx_gso_checksum_fixup);
                skb->ip_summed = CHECKSUM_PARTIAL;
                recalculate_partial_csum = true;
        }
@@ -891,11 +952,10 @@ static int checksum_setup(struct net_device *dev, struct sk_buff *skb)
        return skb_checksum_setup(skb, recalculate_partial_csum);
 }
 
-static int handle_incoming_queue(struct net_device *dev,
+static int handle_incoming_queue(struct netfront_queue *queue,
                                 struct sk_buff_head *rxq)
 {
-       struct netfront_info *np = netdev_priv(dev);
-       struct netfront_stats *stats = this_cpu_ptr(np->stats);
+       struct netfront_stats *stats = this_cpu_ptr(queue->info->stats);
        int packets_dropped = 0;
        struct sk_buff *skb;
 
@@ -906,13 +966,13 @@ static int handle_incoming_queue(struct net_device *dev,
                        __pskb_pull_tail(skb, pull_to - skb_headlen(skb));
 
                /* Ethernet work: Delayed to here as it peeks the header. */
-               skb->protocol = eth_type_trans(skb, dev);
+               skb->protocol = eth_type_trans(skb, queue->info->netdev);
                skb_reset_network_header(skb);
 
-               if (checksum_setup(dev, skb)) {
+               if (checksum_setup(queue->info->netdev, skb)) {
                        kfree_skb(skb);
                        packets_dropped++;
-                       dev->stats.rx_errors++;
+                       queue->info->netdev->stats.rx_errors++;
                        continue;
                }
 
@@ -922,7 +982,7 @@ static int handle_incoming_queue(struct net_device *dev,
                u64_stats_update_end(&stats->syncp);
 
                /* Pass it up. */
-               napi_gro_receive(&np->napi, skb);
+               napi_gro_receive(&queue->napi, skb);
        }
 
        return packets_dropped;
@@ -930,8 +990,8 @@ static int handle_incoming_queue(struct net_device *dev,
 
 static int xennet_poll(struct napi_struct *napi, int budget)
 {
-       struct netfront_info *np = container_of(napi, struct netfront_info, napi);
-       struct net_device *dev = np->netdev;
+       struct netfront_queue *queue = container_of(napi, struct netfront_queue, napi);
+       struct net_device *dev = queue->info->netdev;
        struct sk_buff *skb;
        struct netfront_rx_info rinfo;
        struct xen_netif_rx_response *rx = &rinfo.rx;
@@ -944,29 +1004,29 @@ static int xennet_poll(struct napi_struct *napi, int budget)
        unsigned long flags;
        int err;
 
-       spin_lock(&np->rx_lock);
+       spin_lock(&queue->rx_lock);
 
        skb_queue_head_init(&rxq);
        skb_queue_head_init(&errq);
        skb_queue_head_init(&tmpq);
 
-       rp = np->rx.sring->rsp_prod;
+       rp = queue->rx.sring->rsp_prod;
        rmb(); /* Ensure we see queued responses up to 'rp'. */
 
-       i = np->rx.rsp_cons;
+       i = queue->rx.rsp_cons;
        work_done = 0;
        while ((i != rp) && (work_done < budget)) {
-               memcpy(rx, RING_GET_RESPONSE(&np->rx, i), sizeof(*rx));
+               memcpy(rx, RING_GET_RESPONSE(&queue->rx, i), sizeof(*rx));
                memset(extras, 0, sizeof(rinfo.extras));
 
-               err = xennet_get_responses(np, &rinfo, rp, &tmpq);
+               err = xennet_get_responses(queue, &rinfo, rp, &tmpq);
 
                if (unlikely(err)) {
 err:
                        while ((skb = __skb_dequeue(&tmpq)))
                                __skb_queue_tail(&errq, skb);
                        dev->stats.rx_errors++;
-                       i = np->rx.rsp_cons;
+                       i = queue->rx.rsp_cons;
                        continue;
                }
 
@@ -978,7 +1038,7 @@ err:
 
                        if (unlikely(xennet_set_skb_gso(skb, gso))) {
                                __skb_queue_head(&tmpq, skb);
-                               np->rx.rsp_cons += skb_queue_len(&tmpq);
+                               queue->rx.rsp_cons += skb_queue_len(&tmpq);
                                goto err;
                        }
                }
@@ -992,7 +1052,7 @@ err:
                skb->data_len = rx->status;
                skb->len += rx->status;
 
-               i = xennet_fill_frags(np, skb, &tmpq);
+               i = xennet_fill_frags(queue, skb, &tmpq);
 
                if (rx->flags & XEN_NETRXF_csum_blank)
                        skb->ip_summed = CHECKSUM_PARTIAL;
@@ -1001,22 +1061,22 @@ err:
 
                __skb_queue_tail(&rxq, skb);
 
-               np->rx.rsp_cons = ++i;
+               queue->rx.rsp_cons = ++i;
                work_done++;
        }
 
        __skb_queue_purge(&errq);
 
-       work_done -= handle_incoming_queue(dev, &rxq);
+       work_done -= handle_incoming_queue(queue, &rxq);
 
        /* If we get a callback with very few responses, reduce fill target. */
        /* NB. Note exponential increase, linear decrease. */
-       if (((np->rx.req_prod_pvt - np->rx.sring->rsp_prod) >
-            ((3*np->rx_target) / 4)) &&
-           (--np->rx_target < np->rx_min_target))
-               np->rx_target = np->rx_min_target;
+       if (((queue->rx.req_prod_pvt - queue->rx.sring->rsp_prod) >
+            ((3*queue->rx_target) / 4)) &&
+           (--queue->rx_target < queue->rx_min_target))
+               queue->rx_target = queue->rx_min_target;
 
-       xennet_alloc_rx_buffers(dev);
+       xennet_alloc_rx_buffers(queue);
 
        if (work_done < budget) {
                int more_to_do = 0;
@@ -1025,14 +1085,14 @@ err:
 
                local_irq_save(flags);
 
-               RING_FINAL_CHECK_FOR_RESPONSES(&np->rx, more_to_do);
+               RING_FINAL_CHECK_FOR_RESPONSES(&queue->rx, more_to_do);
                if (!more_to_do)
                        __napi_complete(napi);
 
                local_irq_restore(flags);
        }
 
-       spin_unlock(&np->rx_lock);
+       spin_unlock(&queue->rx_lock);
 
        return work_done;
 }
@@ -1080,43 +1140,43 @@ static struct rtnl_link_stats64 *xennet_get_stats64(struct net_device *dev,
        return tot;
 }
 
-static void xennet_release_tx_bufs(struct netfront_info *np)
+static void xennet_release_tx_bufs(struct netfront_queue *queue)
 {
        struct sk_buff *skb;
        int i;
 
        for (i = 0; i < NET_TX_RING_SIZE; i++) {
                /* Skip over entries which are actually freelist references */
-               if (skb_entry_is_link(&np->tx_skbs[i]))
+               if (skb_entry_is_link(&queue->tx_skbs[i]))
                        continue;
 
-               skb = np->tx_skbs[i].skb;
-               get_page(np->grant_tx_page[i]);
-               gnttab_end_foreign_access(np->grant_tx_ref[i],
+               skb = queue->tx_skbs[i].skb;
+               get_page(queue->grant_tx_page[i]);
+               gnttab_end_foreign_access(queue->grant_tx_ref[i],
                                          GNTMAP_readonly,
-                                         (unsigned long)page_address(np->grant_tx_page[i]));
-               np->grant_tx_page[i] = NULL;
-               np->grant_tx_ref[i] = GRANT_INVALID_REF;
-               add_id_to_freelist(&np->tx_skb_freelist, np->tx_skbs, i);
+                                         (unsigned long)page_address(queue->grant_tx_page[i]));
+               queue->grant_tx_page[i] = NULL;
+               queue->grant_tx_ref[i] = GRANT_INVALID_REF;
+               add_id_to_freelist(&queue->tx_skb_freelist, queue->tx_skbs, i);
                dev_kfree_skb_irq(skb);
        }
 }
 
-static void xennet_release_rx_bufs(struct netfront_info *np)
+static void xennet_release_rx_bufs(struct netfront_queue *queue)
 {
        int id, ref;
 
-       spin_lock_bh(&np->rx_lock);
+       spin_lock_bh(&queue->rx_lock);
 
        for (id = 0; id < NET_RX_RING_SIZE; id++) {
                struct sk_buff *skb;
                struct page *page;
 
-               skb = np->rx_skbs[id];
+               skb = queue->rx_skbs[id];
                if (!skb)
                        continue;
 
-               ref = np->grant_rx_ref[id];
+               ref = queue->grant_rx_ref[id];
                if (ref == GRANT_INVALID_REF)
                        continue;
 
@@ -1128,21 +1188,28 @@ static void xennet_release_rx_bufs(struct netfront_info *np)
                get_page(page);
                gnttab_end_foreign_access(ref, 0,
                                          (unsigned long)page_address(page));
-               np->grant_rx_ref[id] = GRANT_INVALID_REF;
+               queue->grant_rx_ref[id] = GRANT_INVALID_REF;
 
                kfree_skb(skb);
        }
 
-       spin_unlock_bh(&np->rx_lock);
+       spin_unlock_bh(&queue->rx_lock);
 }
 
 static void xennet_uninit(struct net_device *dev)
 {
        struct netfront_info *np = netdev_priv(dev);
-       xennet_release_tx_bufs(np);
-       xennet_release_rx_bufs(np);
-       gnttab_free_grant_references(np->gref_tx_head);
-       gnttab_free_grant_references(np->gref_rx_head);
+       unsigned int num_queues = dev->real_num_tx_queues;
+       struct netfront_queue *queue;
+       unsigned int i;
+
+       for (i = 0; i < num_queues; ++i) {
+               queue = &np->queues[i];
+               xennet_release_tx_bufs(queue);
+               xennet_release_rx_bufs(queue);
+               gnttab_free_grant_references(queue->gref_tx_head);
+               gnttab_free_grant_references(queue->gref_rx_head);
+       }
 }
 
 static netdev_features_t xennet_fix_features(struct net_device *dev,
@@ -1203,25 +1270,24 @@ static int xennet_set_features(struct net_device *dev,
 
 static irqreturn_t xennet_tx_interrupt(int irq, void *dev_id)
 {
-       struct netfront_info *np = dev_id;
-       struct net_device *dev = np->netdev;
+       struct netfront_queue *queue = dev_id;
        unsigned long flags;
 
-       spin_lock_irqsave(&np->tx_lock, flags);
-       xennet_tx_buf_gc(dev);
-       spin_unlock_irqrestore(&np->tx_lock, flags);
+       spin_lock_irqsave(&queue->tx_lock, flags);
+       xennet_tx_buf_gc(queue);
+       spin_unlock_irqrestore(&queue->tx_lock, flags);
 
        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;
+       struct netfront_queue *queue = dev_id;
+       struct net_device *dev = queue->info->netdev;
 
        if (likely(netif_carrier_ok(dev) &&
-                  RING_HAS_UNCONSUMED_RESPONSES(&np->rx)))
-                       napi_schedule(&np->napi);
+                  RING_HAS_UNCONSUMED_RESPONSES(&queue->rx)))
+                       napi_schedule(&queue->napi);
 
        return IRQ_HANDLED;
 }
@@ -1236,7 +1302,12 @@ static irqreturn_t xennet_interrupt(int irq, void *dev_id)
 #ifdef CONFIG_NET_POLL_CONTROLLER
 static void xennet_poll_controller(struct net_device *dev)
 {
-       xennet_interrupt(0, dev);
+       /* Poll each queue */
+       struct netfront_info *info = netdev_priv(dev);
+       unsigned int num_queues = dev->real_num_tx_queues;
+       unsigned int i;
+       for (i = 0; i < num_queues; ++i)
+               xennet_interrupt(0, &info->queues[i]);
 }
 #endif
 
@@ -1251,6 +1322,7 @@ static const struct net_device_ops xennet_netdev_ops = {
        .ndo_validate_addr   = eth_validate_addr,
        .ndo_fix_features    = xennet_fix_features,
        .ndo_set_features    = xennet_set_features,
+       .ndo_select_queue    = xennet_select_queue,
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller = xennet_poll_controller,
 #endif
@@ -1258,66 +1330,30 @@ static const struct net_device_ops xennet_netdev_ops = {
 
 static struct net_device *xennet_create_dev(struct xenbus_device *dev)
 {
-       int i, err;
+       int err;
        struct net_device *netdev;
        struct netfront_info *np;
 
-       netdev = alloc_etherdev(sizeof(struct netfront_info));
+       netdev = alloc_etherdev_mq(sizeof(struct netfront_info), xennet_max_queues);
        if (!netdev)
                return ERR_PTR(-ENOMEM);
 
        np                   = netdev_priv(netdev);
        np->xbdev            = dev;
 
-       spin_lock_init(&np->tx_lock);
-       spin_lock_init(&np->rx_lock);
-
-       skb_queue_head_init(&np->rx_batch);
-       np->rx_target     = RX_DFL_MIN_TARGET;
-       np->rx_min_target = RX_DFL_MIN_TARGET;
-       np->rx_max_target = RX_MAX_TARGET;
-
-       init_timer(&np->rx_refill_timer);
-       np->rx_refill_timer.data = (unsigned long)netdev;
-       np->rx_refill_timer.function = rx_refill_timeout;
+       /* No need to use rtnl_lock() before the call below as it
+        * happens before register_netdev().
+        */
+       netif_set_real_num_tx_queues(netdev, 0);
+       np->queues = NULL;
 
        err = -ENOMEM;
        np->stats = netdev_alloc_pcpu_stats(struct netfront_stats);
        if (np->stats == NULL)
                goto exit;
 
-       /* Initialise tx_skbs as a free chain containing every entry. */
-       np->tx_skb_freelist = 0;
-       for (i = 0; i < NET_TX_RING_SIZE; i++) {
-               skb_entry_set_link(&np->tx_skbs[i], i+1);
-               np->grant_tx_ref[i] = GRANT_INVALID_REF;
-               np->grant_tx_page[i] = NULL;
-       }
-
-       /* Clear out rx_skbs */
-       for (i = 0; i < NET_RX_RING_SIZE; i++) {
-               np->rx_skbs[i] = NULL;
-               np->grant_rx_ref[i] = GRANT_INVALID_REF;
-       }
-
-       /* A grant for every tx ring slot */
-       if (gnttab_alloc_grant_references(TX_MAX_TARGET,
-                                         &np->gref_tx_head) < 0) {
-               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) {
-               pr_alert("can't alloc rx grant refs\n");
-               err = -ENOMEM;
-               goto exit_free_tx;
-       }
-
        netdev->netdev_ops      = &xennet_netdev_ops;
 
-       netif_napi_add(netdev, &np->napi, xennet_poll, 64);
        netdev->features        = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
                                  NETIF_F_GSO_ROBUST;
        netdev->hw_features     = NETIF_F_SG |
@@ -1332,7 +1368,7 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev)
          */
        netdev->features |= netdev->hw_features;
 
-       SET_ETHTOOL_OPS(netdev, &xennet_ethtool_ops);
+       netdev->ethtool_ops = &xennet_ethtool_ops;
        SET_NETDEV_DEV(netdev, &dev->dev);
 
        netif_set_gso_max_size(netdev, XEN_NETIF_MAX_TX_SIZE - MAX_TCP_HEADER);
@@ -1343,10 +1379,6 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev)
 
        return netdev;
 
- exit_free_tx:
-       gnttab_free_grant_references(np->gref_tx_head);
- exit_free_stats:
-       free_percpu(np->stats);
  exit:
        free_netdev(netdev);
        return ERR_PTR(err);
@@ -1404,30 +1436,36 @@ static void xennet_end_access(int ref, void *page)
 
 static void xennet_disconnect_backend(struct netfront_info *info)
 {
-       /* Stop old i/f to prevent errors whilst we rebuild the state. */
-       spin_lock_bh(&info->rx_lock);
-       spin_lock_irq(&info->tx_lock);
-       netif_carrier_off(info->netdev);
-       spin_unlock_irq(&info->tx_lock);
-       spin_unlock_bh(&info->rx_lock);
-
-       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;
+       unsigned int i = 0;
+       struct netfront_queue *queue = NULL;
+       unsigned int num_queues = info->netdev->real_num_tx_queues;
+
+       for (i = 0; i < num_queues; ++i) {
+               /* Stop old i/f to prevent errors whilst we rebuild the state. */
+               spin_lock_bh(&queue->rx_lock);
+               spin_lock_irq(&queue->tx_lock);
+               netif_carrier_off(queue->info->netdev);
+               spin_unlock_irq(&queue->tx_lock);
+               spin_unlock_bh(&queue->rx_lock);
+
+               if (queue->tx_irq && (queue->tx_irq == queue->rx_irq))
+                       unbind_from_irqhandler(queue->tx_irq, queue);
+               if (queue->tx_irq && (queue->tx_irq != queue->rx_irq)) {
+                       unbind_from_irqhandler(queue->tx_irq, queue);
+                       unbind_from_irqhandler(queue->rx_irq, queue);
+               }
+               queue->tx_evtchn = queue->rx_evtchn = 0;
+               queue->tx_irq = queue->rx_irq = 0;
 
-       /* End access and free the pages */
-       xennet_end_access(info->tx_ring_ref, info->tx.sring);
-       xennet_end_access(info->rx_ring_ref, info->rx.sring);
+               /* End access and free the pages */
+               xennet_end_access(queue->tx_ring_ref, queue->tx.sring);
+               xennet_end_access(queue->rx_ring_ref, queue->rx.sring);
 
-       info->tx_ring_ref = GRANT_INVALID_REF;
-       info->rx_ring_ref = GRANT_INVALID_REF;
-       info->tx.sring = NULL;
-       info->rx.sring = NULL;
+               queue->tx_ring_ref = GRANT_INVALID_REF;
+               queue->rx_ring_ref = GRANT_INVALID_REF;
+               queue->tx.sring = NULL;
+               queue->rx.sring = NULL;
+       }
 }
 
 /**
@@ -1468,100 +1506,86 @@ static int xen_net_read_mac(struct xenbus_device *dev, u8 mac[])
        return 0;
 }
 
-static int setup_netfront_single(struct netfront_info *info)
+static int setup_netfront_single(struct netfront_queue *queue)
 {
        int err;
 
-       err = xenbus_alloc_evtchn(info->xbdev, &info->tx_evtchn);
+       err = xenbus_alloc_evtchn(queue->info->xbdev, &queue->tx_evtchn);
        if (err < 0)
                goto fail;
 
-       err = bind_evtchn_to_irqhandler(info->tx_evtchn,
+       err = bind_evtchn_to_irqhandler(queue->tx_evtchn,
                                        xennet_interrupt,
-                                       0, info->netdev->name, info);
+                                       0, queue->info->netdev->name, queue);
        if (err < 0)
                goto bind_fail;
-       info->rx_evtchn = info->tx_evtchn;
-       info->rx_irq = info->tx_irq = err;
+       queue->rx_evtchn = queue->tx_evtchn;
+       queue->rx_irq = queue->tx_irq = err;
 
        return 0;
 
 bind_fail:
-       xenbus_free_evtchn(info->xbdev, info->tx_evtchn);
-       info->tx_evtchn = 0;
+       xenbus_free_evtchn(queue->info->xbdev, queue->tx_evtchn);
+       queue->tx_evtchn = 0;
 fail:
        return err;
 }
 
-static int setup_netfront_split(struct netfront_info *info)
+static int setup_netfront_split(struct netfront_queue *queue)
 {
        int err;
 
-       err = xenbus_alloc_evtchn(info->xbdev, &info->tx_evtchn);
+       err = xenbus_alloc_evtchn(queue->info->xbdev, &queue->tx_evtchn);
        if (err < 0)
                goto fail;
-       err = xenbus_alloc_evtchn(info->xbdev, &info->rx_evtchn);
+       err = xenbus_alloc_evtchn(queue->info->xbdev, &queue->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,
+       snprintf(queue->tx_irq_name, sizeof(queue->tx_irq_name),
+                "%s-tx", queue->name);
+       err = bind_evtchn_to_irqhandler(queue->tx_evtchn,
                                        xennet_tx_interrupt,
-                                       0, info->tx_irq_name, info);
+                                       0, queue->tx_irq_name, queue);
        if (err < 0)
                goto bind_tx_fail;
-       info->tx_irq = err;
+       queue->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,
+       snprintf(queue->rx_irq_name, sizeof(queue->rx_irq_name),
+                "%s-rx", queue->name);
+       err = bind_evtchn_to_irqhandler(queue->rx_evtchn,
                                        xennet_rx_interrupt,
-                                       0, info->rx_irq_name, info);
+                                       0, queue->rx_irq_name, queue);
        if (err < 0)
                goto bind_rx_fail;
-       info->rx_irq = err;
+       queue->rx_irq = err;
 
        return 0;
 
 bind_rx_fail:
-       unbind_from_irqhandler(info->tx_irq, info);
-       info->tx_irq = 0;
+       unbind_from_irqhandler(queue->tx_irq, queue);
+       queue->tx_irq = 0;
 bind_tx_fail:
-       xenbus_free_evtchn(info->xbdev, info->rx_evtchn);
-       info->rx_evtchn = 0;
+       xenbus_free_evtchn(queue->info->xbdev, queue->rx_evtchn);
+       queue->rx_evtchn = 0;
 alloc_rx_evtchn_fail:
-       xenbus_free_evtchn(info->xbdev, info->tx_evtchn);
-       info->tx_evtchn = 0;
+       xenbus_free_evtchn(queue->info->xbdev, queue->tx_evtchn);
+       queue->tx_evtchn = 0;
 fail:
        return err;
 }
 
-static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info)
+static int setup_netfront(struct xenbus_device *dev,
+                       struct netfront_queue *queue, unsigned int feature_split_evtchn)
 {
        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;
-       info->rx.sring = NULL;
-       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);
-               goto fail;
-       }
+       queue->tx_ring_ref = GRANT_INVALID_REF;
+       queue->rx_ring_ref = GRANT_INVALID_REF;
+       queue->rx.sring = NULL;
+       queue->tx.sring = NULL;
 
        txs = (struct xen_netif_tx_sring *)get_zeroed_page(GFP_NOIO | __GFP_HIGH);
        if (!txs) {
@@ -1570,13 +1594,13 @@ static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info)
                goto fail;
        }
        SHARED_RING_INIT(txs);
-       FRONT_RING_INIT(&info->tx, txs, PAGE_SIZE);
+       FRONT_RING_INIT(&queue->tx, txs, PAGE_SIZE);
 
        err = xenbus_grant_ring(dev, virt_to_mfn(txs));
        if (err < 0)
                goto grant_tx_ring_fail;
+       queue->tx_ring_ref = err;
 
-       info->tx_ring_ref = err;
        rxs = (struct xen_netif_rx_sring *)get_zeroed_page(GFP_NOIO | __GFP_HIGH);
        if (!rxs) {
                err = -ENOMEM;
@@ -1584,21 +1608,21 @@ static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info)
                goto alloc_rx_ring_fail;
        }
        SHARED_RING_INIT(rxs);
-       FRONT_RING_INIT(&info->rx, rxs, PAGE_SIZE);
+       FRONT_RING_INIT(&queue->rx, rxs, PAGE_SIZE);
 
        err = xenbus_grant_ring(dev, virt_to_mfn(rxs));
        if (err < 0)
                goto grant_rx_ring_fail;
-       info->rx_ring_ref = err;
+       queue->rx_ring_ref = err;
 
        if (feature_split_evtchn)
-               err = setup_netfront_split(info);
+               err = setup_netfront_split(queue);
        /* 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);
+               err = setup_netfront_single(queue);
 
        if (err)
                goto alloc_evtchn_fail;
@@ -1609,17 +1633,163 @@ static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info)
         * granted pages because backend is not accessing it at this point.
         */
 alloc_evtchn_fail:
-       gnttab_end_foreign_access_ref(info->rx_ring_ref, 0);
+       gnttab_end_foreign_access_ref(queue->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);
+       gnttab_end_foreign_access_ref(queue->tx_ring_ref, 0);
 grant_tx_ring_fail:
        free_page((unsigned long)txs);
 fail:
        return err;
 }
 
+/* Queue-specific initialisation
+ * This used to be done in xennet_create_dev() but must now
+ * be run per-queue.
+ */
+static int xennet_init_queue(struct netfront_queue *queue)
+{
+       unsigned short i;
+       int err = 0;
+
+       spin_lock_init(&queue->tx_lock);
+       spin_lock_init(&queue->rx_lock);
+
+       skb_queue_head_init(&queue->rx_batch);
+       queue->rx_target     = RX_DFL_MIN_TARGET;
+       queue->rx_min_target = RX_DFL_MIN_TARGET;
+       queue->rx_max_target = RX_MAX_TARGET;
+
+       init_timer(&queue->rx_refill_timer);
+       queue->rx_refill_timer.data = (unsigned long)queue;
+       queue->rx_refill_timer.function = rx_refill_timeout;
+
+       snprintf(queue->name, sizeof(queue->name), "%s-q%u",
+                queue->info->netdev->name, queue->id);
+
+       /* Initialise tx_skbs as a free chain containing every entry. */
+       queue->tx_skb_freelist = 0;
+       for (i = 0; i < NET_TX_RING_SIZE; i++) {
+               skb_entry_set_link(&queue->tx_skbs[i], i+1);
+               queue->grant_tx_ref[i] = GRANT_INVALID_REF;
+               queue->grant_tx_page[i] = NULL;
+       }
+
+       /* Clear out rx_skbs */
+       for (i = 0; i < NET_RX_RING_SIZE; i++) {
+               queue->rx_skbs[i] = NULL;
+               queue->grant_rx_ref[i] = GRANT_INVALID_REF;
+       }
+
+       /* A grant for every tx ring slot */
+       if (gnttab_alloc_grant_references(TX_MAX_TARGET,
+                                         &queue->gref_tx_head) < 0) {
+               pr_alert("can't alloc tx grant refs\n");
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       /* A grant for every rx ring slot */
+       if (gnttab_alloc_grant_references(RX_MAX_TARGET,
+                                         &queue->gref_rx_head) < 0) {
+               pr_alert("can't alloc rx grant refs\n");
+               err = -ENOMEM;
+               goto exit_free_tx;
+       }
+
+       netif_napi_add(queue->info->netdev, &queue->napi, xennet_poll, 64);
+
+       return 0;
+
+ exit_free_tx:
+       gnttab_free_grant_references(queue->gref_tx_head);
+ exit:
+       return err;
+}
+
+static int write_queue_xenstore_keys(struct netfront_queue *queue,
+                          struct xenbus_transaction *xbt, int write_hierarchical)
+{
+       /* Write the queue-specific keys into XenStore in the traditional
+        * way for a single queue, or in a queue subkeys for multiple
+        * queues.
+        */
+       struct xenbus_device *dev = queue->info->xbdev;
+       int err;
+       const char *message;
+       char *path;
+       size_t pathsize;
+
+       /* Choose the correct place to write the keys */
+       if (write_hierarchical) {
+               pathsize = strlen(dev->nodename) + 10;
+               path = kzalloc(pathsize, GFP_KERNEL);
+               if (!path) {
+                       err = -ENOMEM;
+                       message = "out of memory while writing ring references";
+                       goto error;
+               }
+               snprintf(path, pathsize, "%s/queue-%u",
+                               dev->nodename, queue->id);
+       } else {
+               path = (char *)dev->nodename;
+       }
+
+       /* Write ring references */
+       err = xenbus_printf(*xbt, path, "tx-ring-ref", "%u",
+                       queue->tx_ring_ref);
+       if (err) {
+               message = "writing tx-ring-ref";
+               goto error;
+       }
+
+       err = xenbus_printf(*xbt, path, "rx-ring-ref", "%u",
+                       queue->rx_ring_ref);
+       if (err) {
+               message = "writing rx-ring-ref";
+               goto error;
+       }
+
+       /* Write event channels; taking into account both shared
+        * and split event channel scenarios.
+        */
+       if (queue->tx_evtchn == queue->rx_evtchn) {
+               /* Shared event channel */
+               err = xenbus_printf(*xbt, path,
+                               "event-channel", "%u", queue->tx_evtchn);
+               if (err) {
+                       message = "writing event-channel";
+                       goto error;
+               }
+       } else {
+               /* Split event channels */
+               err = xenbus_printf(*xbt, path,
+                               "event-channel-tx", "%u", queue->tx_evtchn);
+               if (err) {
+                       message = "writing event-channel-tx";
+                       goto error;
+               }
+
+               err = xenbus_printf(*xbt, path,
+                               "event-channel-rx", "%u", queue->rx_evtchn);
+               if (err) {
+                       message = "writing event-channel-rx";
+                       goto error;
+               }
+       }
+
+       if (write_hierarchical)
+               kfree(path);
+       return 0;
+
+error:
+       if (write_hierarchical)
+               kfree(path);
+       xenbus_dev_fatal(dev, err, "%s", message);
+       return err;
+}
+
 /* Common code used when first setting up, and when resuming. */
 static int talk_to_netback(struct xenbus_device *dev,
                           struct netfront_info *info)
@@ -1627,11 +1797,83 @@ static int talk_to_netback(struct xenbus_device *dev,
        const char *message;
        struct xenbus_transaction xbt;
        int err;
+       unsigned int feature_split_evtchn;
+       unsigned int i = 0;
+       unsigned int max_queues = 0;
+       struct netfront_queue *queue = NULL;
+       unsigned int num_queues = 1;
 
-       /* Create shared ring, alloc event channel. */
-       err = setup_netfront(dev, info);
-       if (err)
+       info->netdev->irq = 0;
+
+       /* Check if backend supports multiple queues */
+       err = xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+                          "multi-queue-max-queues", "%u", &max_queues);
+       if (err < 0)
+               max_queues = 1;
+       num_queues = min(max_queues, xennet_max_queues);
+
+       /* Check feature-split-event-channels */
+       err = xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+                          "feature-split-event-channels", "%u",
+                          &feature_split_evtchn);
+       if (err < 0)
+               feature_split_evtchn = 0;
+
+       /* Read mac addr. */
+       err = xen_net_read_mac(dev, info->netdev->dev_addr);
+       if (err) {
+               xenbus_dev_fatal(dev, err, "parsing %s/mac", dev->nodename);
+               goto out;
+       }
+
+       /* Allocate array of queues */
+       info->queues = kcalloc(num_queues, sizeof(struct netfront_queue), GFP_KERNEL);
+       if (!info->queues) {
+               err = -ENOMEM;
                goto out;
+       }
+       rtnl_lock();
+       netif_set_real_num_tx_queues(info->netdev, num_queues);
+       rtnl_unlock();
+
+       /* Create shared ring, alloc event channel -- for each queue */
+       for (i = 0; i < num_queues; ++i) {
+               queue = &info->queues[i];
+               queue->id = i;
+               queue->info = info;
+               err = xennet_init_queue(queue);
+               if (err) {
+                       /* xennet_init_queue() cleans up after itself on failure,
+                        * but we still have to clean up any previously initialised
+                        * queues. If i > 0, set num_queues to i, then goto
+                        * destroy_ring, which calls xennet_disconnect_backend()
+                        * to tidy up.
+                        */
+                       if (i > 0) {
+                               rtnl_lock();
+                               netif_set_real_num_tx_queues(info->netdev, i);
+                               rtnl_unlock();
+                               goto destroy_ring;
+                       } else {
+                               goto out;
+                       }
+               }
+               err = setup_netfront(dev, queue, feature_split_evtchn);
+               if (err) {
+                       /* As for xennet_init_queue(), setup_netfront() will tidy
+                        * up the current queue on error, but we need to clean up
+                        * those already allocated.
+                        */
+                       if (i > 0) {
+                               rtnl_lock();
+                               netif_set_real_num_tx_queues(info->netdev, i);
+                               rtnl_unlock();
+                               goto destroy_ring;
+                       } else {
+                               goto out;
+                       }
+               }
+       }
 
 again:
        err = xenbus_transaction_start(&xbt);
@@ -1640,41 +1882,29 @@ again:
                goto destroy_ring;
        }
 
-       err = xenbus_printf(xbt, dev->nodename, "tx-ring-ref", "%u",
-                           info->tx_ring_ref);
-       if (err) {
-               message = "writing tx ring-ref";
-               goto abort_transaction;
-       }
-       err = xenbus_printf(xbt, dev->nodename, "rx-ring-ref", "%u",
-                           info->rx_ring_ref);
-       if (err) {
-               message = "writing rx ring-ref";
-               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;
-               }
+       if (num_queues == 1) {
+               err = write_queue_xenstore_keys(&info->queues[0], &xbt, 0); /* flat */
+               if (err)
+                       goto abort_transaction_no_dev_fatal;
        } else {
-               err = xenbus_printf(xbt, dev->nodename,
-                                   "event-channel-tx", "%u", info->tx_evtchn);
+               /* Write the number of queues */
+               err = xenbus_printf(xbt, dev->nodename, "multi-queue-num-queues",
+                                   "%u", num_queues);
                if (err) {
-                       message = "writing event-channel-tx";
-                       goto abort_transaction;
+                       message = "writing multi-queue-num-queues";
+                       goto abort_transaction_no_dev_fatal;
                }
-               err = xenbus_printf(xbt, dev->nodename,
-                                   "event-channel-rx", "%u", info->rx_evtchn);
-               if (err) {
-                       message = "writing event-channel-rx";
-                       goto abort_transaction;
+
+               /* Write the keys for each queue */
+               for (i = 0; i < num_queues; ++i) {
+                       queue = &info->queues[i];
+                       err = write_queue_xenstore_keys(queue, &xbt, 1); /* hierarchical */
+                       if (err)
+                               goto abort_transaction_no_dev_fatal;
                }
        }
 
+       /* The remaining keys are not queue-specific */
        err = xenbus_printf(xbt, dev->nodename, "request-rx-copy", "%u",
                            1);
        if (err) {
@@ -1724,10 +1954,16 @@ again:
        return 0;
 
  abort_transaction:
-       xenbus_transaction_end(xbt, 1);
        xenbus_dev_fatal(dev, err, "%s", message);
+abort_transaction_no_dev_fatal:
+       xenbus_transaction_end(xbt, 1);
  destroy_ring:
        xennet_disconnect_backend(info);
+       kfree(info->queues);
+       info->queues = NULL;
+       rtnl_lock();
+       netif_set_real_num_tx_queues(info->netdev, 0);
+       rtnl_lock();
  out:
        return err;
 }
@@ -1735,11 +1971,14 @@ again:
 static int xennet_connect(struct net_device *dev)
 {
        struct netfront_info *np = netdev_priv(dev);
+       unsigned int num_queues = 0;
        int i, requeue_idx, err;
        struct sk_buff *skb;
        grant_ref_t ref;
        struct xen_netif_rx_request *req;
        unsigned int feature_rx_copy;
+       unsigned int j = 0;
+       struct netfront_queue *queue = NULL;
 
        err = xenbus_scanf(XBT_NIL, np->xbdev->otherend,
                           "feature-rx-copy", "%u", &feature_rx_copy);
@@ -1756,40 +1995,47 @@ static int xennet_connect(struct net_device *dev)
        if (err)
                return err;
 
+       /* talk_to_netback() sets the correct number of queues */
+       num_queues = dev->real_num_tx_queues;
+
        rtnl_lock();
        netdev_update_features(dev);
        rtnl_unlock();
 
-       spin_lock_bh(&np->rx_lock);
-       spin_lock_irq(&np->tx_lock);
+       /* By now, the queue structures have been set up */
+       for (j = 0; j < num_queues; ++j) {
+               queue = &np->queues[j];
+               spin_lock_bh(&queue->rx_lock);
+               spin_lock_irq(&queue->tx_lock);
 
-       /* Step 1: Discard all pending TX packet fragments. */
-       xennet_release_tx_bufs(np);
+               /* Step 1: Discard all pending TX packet fragments. */
+               xennet_release_tx_bufs(queue);
 
-       /* Step 2: Rebuild the RX buffer freelist and the RX ring itself. */
-       for (requeue_idx = 0, i = 0; i < NET_RX_RING_SIZE; i++) {
-               skb_frag_t *frag;
-               const struct page *page;
-               if (!np->rx_skbs[i])
-                       continue;
+               /* Step 2: Rebuild the RX buffer freelist and the RX ring itself. */
+               for (requeue_idx = 0, i = 0; i < NET_RX_RING_SIZE; i++) {
+                       skb_frag_t *frag;
+                       const struct page *page;
+                       if (!queue->rx_skbs[i])
+                               continue;
 
-               skb = np->rx_skbs[requeue_idx] = xennet_get_rx_skb(np, i);
-               ref = np->grant_rx_ref[requeue_idx] = xennet_get_rx_ref(np, i);
-               req = RING_GET_REQUEST(&np->rx, requeue_idx);
+                       skb = queue->rx_skbs[requeue_idx] = xennet_get_rx_skb(queue, i);
+                       ref = queue->grant_rx_ref[requeue_idx] = xennet_get_rx_ref(queue, i);
+                       req = RING_GET_REQUEST(&queue->rx, requeue_idx);
 
-               frag = &skb_shinfo(skb)->frags[0];
-               page = skb_frag_page(frag);
-               gnttab_grant_foreign_access_ref(
-                       ref, np->xbdev->otherend_id,
-                       pfn_to_mfn(page_to_pfn(page)),
-                       0);
-               req->gref = ref;
-               req->id   = requeue_idx;
+                       frag = &skb_shinfo(skb)->frags[0];
+                       page = skb_frag_page(frag);
+                       gnttab_grant_foreign_access_ref(
+                               ref, queue->info->xbdev->otherend_id,
+                               pfn_to_mfn(page_to_pfn(page)),
+                               0);
+                       req->gref = ref;
+                       req->id   = requeue_idx;
 
-               requeue_idx++;
-       }
+                       requeue_idx++;
+               }
 
-       np->rx.req_prod_pvt = requeue_idx;
+               queue->rx.req_prod_pvt = requeue_idx;
+       }
 
        /*
         * Step 3: All public and private state should now be sane.  Get
@@ -1798,14 +2044,17 @@ static int xennet_connect(struct net_device *dev)
         * packets.
         */
        netif_carrier_on(np->netdev);
-       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);
-
-       spin_unlock_irq(&np->tx_lock);
-       spin_unlock_bh(&np->rx_lock);
+       for (j = 0; j < num_queues; ++j) {
+               queue = &np->queues[j];
+               notify_remote_via_irq(queue->tx_irq);
+               if (queue->tx_irq != queue->rx_irq)
+                       notify_remote_via_irq(queue->rx_irq);
+               xennet_tx_buf_gc(queue);
+               xennet_alloc_rx_buffers(queue);
+
+               spin_unlock_irq(&queue->tx_lock);
+               spin_unlock_bh(&queue->rx_lock);
+       }
 
        return 0;
 }
@@ -1878,7 +2127,7 @@ static void xennet_get_ethtool_stats(struct net_device *dev,
        int i;
 
        for (i = 0; i < ARRAY_SIZE(xennet_stats); i++)
-               data[i] = *(unsigned long *)(np + xennet_stats[i].offset);
+               data[i] = atomic_read((atomic_t *)(np + xennet_stats[i].offset));
 }
 
 static void xennet_get_strings(struct net_device *dev, u32 stringset, u8 * data)
@@ -1909,8 +2158,12 @@ static ssize_t show_rxbuf_min(struct device *dev,
 {
        struct net_device *netdev = to_net_dev(dev);
        struct netfront_info *info = netdev_priv(netdev);
+       unsigned int num_queues = netdev->real_num_tx_queues;
 
-       return sprintf(buf, "%u\n", info->rx_min_target);
+       if (num_queues)
+               return sprintf(buf, "%u\n", info->queues[0].rx_min_target);
+       else
+               return sprintf(buf, "%u\n", RX_MIN_TARGET);
 }
 
 static ssize_t store_rxbuf_min(struct device *dev,
@@ -1919,8 +2172,11 @@ static ssize_t store_rxbuf_min(struct device *dev,
 {
        struct net_device *netdev = to_net_dev(dev);
        struct netfront_info *np = netdev_priv(netdev);
+       unsigned int num_queues = netdev->real_num_tx_queues;
        char *endp;
        unsigned long target;
+       unsigned int i;
+       struct netfront_queue *queue;
 
        if (!capable(CAP_NET_ADMIN))
                return -EPERM;
@@ -1934,16 +2190,19 @@ static ssize_t store_rxbuf_min(struct device *dev,
        if (target > RX_MAX_TARGET)
                target = RX_MAX_TARGET;
 
-       spin_lock_bh(&np->rx_lock);
-       if (target > np->rx_max_target)
-               np->rx_max_target = target;
-       np->rx_min_target = target;
-       if (target > np->rx_target)
-               np->rx_target = target;
+       for (i = 0; i < num_queues; ++i) {
+               queue = &np->queues[i];
+               spin_lock_bh(&queue->rx_lock);
+               if (target > queue->rx_max_target)
+                       queue->rx_max_target = target;
+               queue->rx_min_target = target;
+               if (target > queue->rx_target)
+                       queue->rx_target = target;
 
-       xennet_alloc_rx_buffers(netdev);
+               xennet_alloc_rx_buffers(queue);
 
-       spin_unlock_bh(&np->rx_lock);
+               spin_unlock_bh(&queue->rx_lock);
+       }
        return len;
 }
 
@@ -1952,8 +2211,12 @@ static ssize_t show_rxbuf_max(struct device *dev,
 {
        struct net_device *netdev = to_net_dev(dev);
        struct netfront_info *info = netdev_priv(netdev);
+       unsigned int num_queues = netdev->real_num_tx_queues;
 
-       return sprintf(buf, "%u\n", info->rx_max_target);
+       if (num_queues)
+               return sprintf(buf, "%u\n", info->queues[0].rx_max_target);
+       else
+               return sprintf(buf, "%u\n", RX_MAX_TARGET);
 }
 
 static ssize_t store_rxbuf_max(struct device *dev,
@@ -1962,8 +2225,11 @@ static ssize_t store_rxbuf_max(struct device *dev,
 {
        struct net_device *netdev = to_net_dev(dev);
        struct netfront_info *np = netdev_priv(netdev);
+       unsigned int num_queues = netdev->real_num_tx_queues;
        char *endp;
        unsigned long target;
+       unsigned int i = 0;
+       struct netfront_queue *queue = NULL;
 
        if (!capable(CAP_NET_ADMIN))
                return -EPERM;
@@ -1977,16 +2243,19 @@ static ssize_t store_rxbuf_max(struct device *dev,
        if (target > RX_MAX_TARGET)
                target = RX_MAX_TARGET;
 
-       spin_lock_bh(&np->rx_lock);
-       if (target < np->rx_min_target)
-               np->rx_min_target = target;
-       np->rx_max_target = target;
-       if (target < np->rx_target)
-               np->rx_target = target;
+       for (i = 0; i < num_queues; ++i) {
+               queue = &np->queues[i];
+               spin_lock_bh(&queue->rx_lock);
+               if (target < queue->rx_min_target)
+                       queue->rx_min_target = target;
+               queue->rx_max_target = target;
+               if (target < queue->rx_target)
+                       queue->rx_target = target;
 
-       xennet_alloc_rx_buffers(netdev);
+               xennet_alloc_rx_buffers(queue);
 
-       spin_unlock_bh(&np->rx_lock);
+               spin_unlock_bh(&queue->rx_lock);
+       }
        return len;
 }
 
@@ -1995,8 +2264,12 @@ static ssize_t show_rxbuf_cur(struct device *dev,
 {
        struct net_device *netdev = to_net_dev(dev);
        struct netfront_info *info = netdev_priv(netdev);
+       unsigned int num_queues = netdev->real_num_tx_queues;
 
-       return sprintf(buf, "%u\n", info->rx_target);
+       if (num_queues)
+               return sprintf(buf, "%u\n", info->queues[0].rx_target);
+       else
+               return sprintf(buf, "0\n");
 }
 
 static struct device_attribute xennet_attrs[] = {
@@ -2043,6 +2316,9 @@ static const struct xenbus_device_id netfront_ids[] = {
 static int xennet_remove(struct xenbus_device *dev)
 {
        struct netfront_info *info = dev_get_drvdata(&dev->dev);
+       unsigned int num_queues = info->netdev->real_num_tx_queues;
+       struct netfront_queue *queue = NULL;
+       unsigned int i = 0;
 
        dev_dbg(&dev->dev, "%s\n", dev->nodename);
 
@@ -2052,7 +2328,15 @@ static int xennet_remove(struct xenbus_device *dev)
 
        unregister_netdev(info->netdev);
 
-       del_timer_sync(&info->rx_refill_timer);
+       for (i = 0; i < num_queues; ++i) {
+               queue = &info->queues[i];
+               del_timer_sync(&queue->rx_refill_timer);
+       }
+
+       if (num_queues) {
+               kfree(info->queues);
+               info->queues = NULL;
+       }
 
        free_percpu(info->stats);
 
@@ -2078,6 +2362,9 @@ static int __init netif_init(void)
 
        pr_info("Initialising Xen virtual ethernet driver\n");
 
+       /* Allow as many queues as there are CPUs, by default */
+       xennet_max_queues = num_online_cpus();
+
        return xenbus_register_frontend(&netfront_driver);
 }
 module_init(netif_init);
index 65d4ca19d1328ec737c68684c30de01fc8347b8d..26c66a1265518c32f5262d3aa98afb668e2715ff 100644 (file)
@@ -71,5 +71,6 @@ config NFC_PORT100
 source "drivers/nfc/pn544/Kconfig"
 source "drivers/nfc/microread/Kconfig"
 source "drivers/nfc/nfcmrvl/Kconfig"
+source "drivers/nfc/st21nfca/Kconfig"
 
 endmenu
index ae42a3fa60c981b965bbb0cdb004b7c3c591d01d..23225b0287fdf2e7b6f809466e58b5cc5fc53d2c 100644 (file)
@@ -11,5 +11,6 @@ obj-$(CONFIG_NFC_SIM)         += nfcsim.o
 obj-$(CONFIG_NFC_PORT100)      += port100.o
 obj-$(CONFIG_NFC_MRVL)         += nfcmrvl/
 obj-$(CONFIG_NFC_TRF7970A)     += trf7970a.o
+obj-$(CONFIG_NFC_ST21NFCA)  += st21nfca/
 
 ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
index f2acd85be86ea1d2016562c38aa4f9b6767ae754..440291ab7263202fc8017c3b9f09f8318f5f872a 100644 (file)
@@ -22,6 +22,8 @@
 #include <linux/module.h>
 #include <linux/i2c.h>
 #include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
 #include <linux/miscdevice.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
@@ -857,6 +859,92 @@ exit_state_wait_secure_write_answer:
        }
 }
 
+#ifdef CONFIG_OF
+
+static int pn544_hci_i2c_of_request_resources(struct i2c_client *client)
+{
+       struct pn544_i2c_phy *phy = i2c_get_clientdata(client);
+       struct device_node *pp;
+       int ret;
+
+       pp = client->dev.of_node;
+       if (!pp) {
+               ret = -ENODEV;
+               goto err_dt;
+       }
+
+       /* Obtention of EN GPIO from device tree */
+       ret = of_get_named_gpio(pp, "enable-gpios", 0);
+       if (ret < 0) {
+               if (ret != -EPROBE_DEFER)
+                       nfc_err(&client->dev,
+                               "Failed to get EN gpio, error: %d\n", ret);
+               goto err_dt;
+       }
+       phy->gpio_en = ret;
+
+       /* Configuration of EN GPIO */
+       ret = gpio_request(phy->gpio_en, "pn544_en");
+       if (ret) {
+               nfc_err(&client->dev, "Fail EN pin\n");
+               goto err_dt;
+       }
+       ret = gpio_direction_output(phy->gpio_en, 0);
+       if (ret) {
+               nfc_err(&client->dev, "Fail EN pin direction\n");
+               goto err_gpio_en;
+       }
+
+       /* Obtention of FW GPIO from device tree */
+       ret = of_get_named_gpio(pp, "firmware-gpios", 0);
+       if (ret < 0) {
+               if (ret != -EPROBE_DEFER)
+                       nfc_err(&client->dev,
+                               "Failed to get FW gpio, error: %d\n", ret);
+               goto err_gpio_en;
+       }
+       phy->gpio_fw = ret;
+
+       /* Configuration of FW GPIO */
+       ret = gpio_request(phy->gpio_fw, "pn544_fw");
+       if (ret) {
+               nfc_err(&client->dev, "Fail FW pin\n");
+               goto err_gpio_en;
+       }
+       ret = gpio_direction_output(phy->gpio_fw, 0);
+       if (ret) {
+               nfc_err(&client->dev, "Fail FW pin direction\n");
+               goto err_gpio_fw;
+       }
+
+       /* IRQ */
+       ret = irq_of_parse_and_map(pp, 0);
+       if (ret < 0) {
+               nfc_err(&client->dev,
+                       "Unable to get irq, error: %d\n", ret);
+               goto err_gpio_fw;
+       }
+       client->irq = ret;
+
+       return 0;
+
+err_gpio_fw:
+       gpio_free(phy->gpio_fw);
+err_gpio_en:
+       gpio_free(phy->gpio_en);
+err_dt:
+       return ret;
+}
+
+#else
+
+static int pn544_hci_i2c_of_request_resources(struct i2c_client *client)
+{
+       return -ENODEV;
+}
+
+#endif
+
 static int pn544_hci_i2c_probe(struct i2c_client *client,
                               const struct i2c_device_id *id)
 {
@@ -887,25 +975,36 @@ static int pn544_hci_i2c_probe(struct i2c_client *client,
        i2c_set_clientdata(client, phy);
 
        pdata = client->dev.platform_data;
-       if (pdata == NULL) {
-               nfc_err(&client->dev, "No platform data\n");
-               return -EINVAL;
-       }
 
-       if (pdata->request_resources == NULL) {
-               nfc_err(&client->dev, "request_resources() missing\n");
-               return -EINVAL;
-       }
+       /* No platform data, using device tree. */
+       if (!pdata && client->dev.of_node) {
+               r = pn544_hci_i2c_of_request_resources(client);
+               if (r) {
+                       nfc_err(&client->dev, "No DT data\n");
+                       return r;
+               }
+       /* Using platform data. */
+       } else if (pdata) {
 
-       r = pdata->request_resources(client);
-       if (r) {
-               nfc_err(&client->dev, "Cannot get platform resources\n");
-               return r;
-       }
+               if (pdata->request_resources == NULL) {
+                       nfc_err(&client->dev, "request_resources() missing\n");
+                       return -EINVAL;
+               }
 
-       phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE);
-       phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET);
-       phy->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ);
+               r = pdata->request_resources(client);
+               if (r) {
+                       nfc_err(&client->dev,
+                               "Cannot get platform resources\n");
+                       return r;
+               }
+
+               phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE);
+               phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET);
+               phy->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ);
+       } else {
+               nfc_err(&client->dev, "No platform data\n");
+               return -EINVAL;
+       }
 
        pn544_hci_i2c_platform_init(phy);
 
@@ -930,8 +1029,12 @@ err_hci:
        free_irq(client->irq, phy);
 
 err_rti:
-       if (pdata->free_resources != NULL)
+       if (!pdata) {
+               gpio_free(phy->gpio_en);
+               gpio_free(phy->gpio_fw);
+       } else if (pdata->free_resources) {
                pdata->free_resources();
+       }
 
        return r;
 }
@@ -953,15 +1056,30 @@ static int pn544_hci_i2c_remove(struct i2c_client *client)
                pn544_hci_i2c_disable(phy);
 
        free_irq(client->irq, phy);
-       if (pdata->free_resources)
+
+       /* No platform data, GPIOs have been requested by this driver */
+       if (!pdata) {
+               gpio_free(phy->gpio_en);
+               gpio_free(phy->gpio_fw);
+       /* Using platform data */
+       } else if (pdata->free_resources) {
                pdata->free_resources();
+       }
 
        return 0;
 }
 
+static const struct of_device_id of_pn544_i2c_match[] = {
+       { .compatible = "nxp,pn544-i2c", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, of_pn544_i2c_match);
+
 static struct i2c_driver pn544_hci_i2c_driver = {
        .driver = {
                   .name = PN544_HCI_I2C_DRIVER_NAME,
+                  .owner  = THIS_MODULE,
+                  .of_match_table = of_match_ptr(of_pn544_i2c_match),
                  },
        .probe = pn544_hci_i2c_probe,
        .id_table = pn544_hci_i2c_id_table,
index b7a372af5eb75c7a99a108e76faedd8c7bb7c1c4..4ac4d31f6c598309a9c9e35f871cd0316419ba5f 100644 (file)
@@ -28,7 +28,8 @@
                           NFC_PROTO_MIFARE_MASK   | \
                           NFC_PROTO_FELICA_MASK   | \
                           NFC_PROTO_NFC_DEP_MASK  | \
-                          NFC_PROTO_ISO14443_MASK)
+                          NFC_PROTO_ISO14443_MASK | \
+                          NFC_PROTO_ISO14443_B_MASK)
 
 #define PORT100_CAPABILITIES (NFC_DIGITAL_DRV_CAPS_IN_CRC | \
                              NFC_DIGITAL_DRV_CAPS_TG_CRC)
@@ -120,6 +121,7 @@ struct port100_in_rf_setting {
 #define PORT100_COMM_TYPE_IN_212F 0x01
 #define PORT100_COMM_TYPE_IN_424F 0x02
 #define PORT100_COMM_TYPE_IN_106A 0x03
+#define PORT100_COMM_TYPE_IN_106B 0x07
 
 static const struct port100_in_rf_setting in_rf_settings[] = {
        [NFC_DIGITAL_RF_TECH_212F] = {
@@ -140,6 +142,12 @@ static const struct port100_in_rf_setting in_rf_settings[] = {
                .in_recv_set_number = 15,
                .in_recv_comm_type  = PORT100_COMM_TYPE_IN_106A,
        },
+       [NFC_DIGITAL_RF_TECH_106B] = {
+               .in_send_set_number = 3,
+               .in_send_comm_type  = PORT100_COMM_TYPE_IN_106B,
+               .in_recv_set_number = 15,
+               .in_recv_comm_type  = PORT100_COMM_TYPE_IN_106B,
+       },
        /* Ensures the array has NFC_DIGITAL_RF_TECH_LAST elements */
        [NFC_DIGITAL_RF_TECH_LAST] = { 0 },
 };
@@ -340,6 +348,32 @@ in_protocols[][PORT100_IN_MAX_NUM_PROTOCOLS + 1] = {
        [NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED] = {
                { PORT100_IN_PROT_END, 0 },
        },
+       [NFC_DIGITAL_FRAMING_NFCB] = {
+               { PORT100_IN_PROT_INITIAL_GUARD_TIME,     20 },
+               { PORT100_IN_PROT_ADD_CRC,                 1 },
+               { PORT100_IN_PROT_CHECK_CRC,               1 },
+               { PORT100_IN_PROT_MULTI_CARD,              0 },
+               { PORT100_IN_PROT_ADD_PARITY,              0 },
+               { PORT100_IN_PROT_CHECK_PARITY,            0 },
+               { PORT100_IN_PROT_BITWISE_AC_RECV_MODE,    0 },
+               { PORT100_IN_PROT_VALID_BIT_NUMBER,        8 },
+               { PORT100_IN_PROT_CRYPTO1,                 0 },
+               { PORT100_IN_PROT_ADD_SOF,                 1 },
+               { PORT100_IN_PROT_CHECK_SOF,               1 },
+               { PORT100_IN_PROT_ADD_EOF,                 1 },
+               { PORT100_IN_PROT_CHECK_EOF,               1 },
+               { PORT100_IN_PROT_DEAF_TIME,               4 },
+               { PORT100_IN_PROT_CRM,                     0 },
+               { PORT100_IN_PROT_CRM_MIN_LEN,             0 },
+               { PORT100_IN_PROT_T1_TAG_FRAME,            0 },
+               { PORT100_IN_PROT_RFCA,                    0 },
+               { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 },
+               { PORT100_IN_PROT_END,                     0 },
+       },
+       [NFC_DIGITAL_FRAMING_NFCB_T4T] = {
+               /* nfc_digital_framing_nfcb */
+               { PORT100_IN_PROT_END,                     0 },
+       },
        /* Ensures the array has NFC_DIGITAL_FRAMING_LAST elements */
        [NFC_DIGITAL_FRAMING_LAST] = {
                { PORT100_IN_PROT_END, 0 },
diff --git a/drivers/nfc/st21nfca/Kconfig b/drivers/nfc/st21nfca/Kconfig
new file mode 100644 (file)
index 0000000..ee459f0
--- /dev/null
@@ -0,0 +1,23 @@
+config NFC_ST21NFCA
+       tristate "STMicroelectronics ST21NFCA NFC driver"
+       depends on NFC_HCI
+       select CRC_CCITT
+       default n
+       ---help---
+         STMicroelectronics ST21NFCA core driver. It implements the chipset
+         HCI logic and hooks into the NFC kernel APIs. Physical layers will
+         register against it.
+
+         To compile this driver as a module, choose m here. The module will
+         be called st21nfca.
+         Say N if unsure.
+
+config NFC_ST21NFCA_I2C
+       tristate "NFC ST21NFCA i2c support"
+       depends on NFC_ST21NFCA && I2C && NFC_SHDLC
+       ---help---
+         This module adds support for the STMicroelectronics st21nfca i2c interface.
+         Select this if your platform is using the i2c bus.
+
+         If you choose to build a module, it'll be called st21nfca_i2c.
+         Say N if unsure.
diff --git a/drivers/nfc/st21nfca/Makefile b/drivers/nfc/st21nfca/Makefile
new file mode 100644 (file)
index 0000000..038ed09
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# Makefile for ST21NFCA HCI based NFC driver
+#
+
+st21nfca_i2c-objs  = i2c.o
+
+obj-$(CONFIG_NFC_ST21NFCA)     += st21nfca.o
+obj-$(CONFIG_NFC_ST21NFCA_I2C) += st21nfca_i2c.o
diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c
new file mode 100644 (file)
index 0000000..3f954ed
--- /dev/null
@@ -0,0 +1,724 @@
+/*
+ * I2C Link Layer for ST21NFCA HCI based Driver
+ * Copyright (C) 2014  STMicroelectronics SAS. 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 that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/crc-ccitt.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/miscdevice.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/nfc.h>
+#include <linux/firmware.h>
+#include <linux/unaligned/access_ok.h>
+#include <linux/platform_data/st21nfca.h>
+
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+#include <net/nfc/nfc.h>
+
+#include "st21nfca.h"
+
+/*
+ * Every frame starts with ST21NFCA_SOF_EOF and ends with ST21NFCA_SOF_EOF.
+ * Because ST21NFCA_SOF_EOF is a possible data value, there is a mecanism
+ * called byte stuffing has been introduced.
+ *
+ * if byte == ST21NFCA_SOF_EOF or ST21NFCA_ESCAPE_BYTE_STUFFING
+ * - insert ST21NFCA_ESCAPE_BYTE_STUFFING (escape byte)
+ * - xor byte with ST21NFCA_BYTE_STUFFING_MASK
+ */
+#define ST21NFCA_SOF_EOF               0x7e
+#define ST21NFCA_BYTE_STUFFING_MASK    0x20
+#define ST21NFCA_ESCAPE_BYTE_STUFFING  0x7d
+
+/* SOF + 00 */
+#define ST21NFCA_FRAME_HEADROOM                        2
+
+/* 2 bytes crc + EOF */
+#define ST21NFCA_FRAME_TAILROOM 3
+#define IS_START_OF_FRAME(buf) (buf[0] == ST21NFCA_SOF_EOF && \
+                               buf[1] == 0)
+
+#define ST21NFCA_HCI_I2C_DRIVER_NAME "st21nfca_hci_i2c"
+
+static struct i2c_device_id st21nfca_hci_i2c_id_table[] = {
+       {ST21NFCA_HCI_DRIVER_NAME, 0},
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, st21nfca_hci_i2c_id_table);
+
+struct st21nfca_i2c_phy {
+       struct i2c_client *i2c_dev;
+       struct nfc_hci_dev *hdev;
+
+       unsigned int gpio_ena;
+       unsigned int gpio_irq;
+       unsigned int irq_polarity;
+
+       struct sk_buff *pending_skb;
+       int current_read_len;
+       /*
+        * crc might have fail because i2c macro
+        * is disable due to other interface activity
+        */
+       int crc_trials;
+
+       int powered;
+       int run_mode;
+
+       /*
+        * < 0 if hardware error occured (e.g. i2c err)
+        * and prevents normal operation.
+        */
+       int hard_fault;
+       struct mutex phy_lock;
+};
+static u8 len_seq[] = { 13, 24, 15, 29 };
+static u16 wait_tab[] = { 2, 3, 5, 15, 20, 40};
+
+#define I2C_DUMP_SKB(info, skb)                                        \
+do {                                                           \
+       pr_debug("%s:\n", info);                                \
+       print_hex_dump(KERN_DEBUG, "i2c: ", DUMP_PREFIX_OFFSET, \
+                      16, 1, (skb)->data, (skb)->len, 0);      \
+} while (0)
+
+/*
+ * In order to get the CLF in a known state we generate an internal reboot
+ * using a proprietary command.
+ * Once the reboot is completed, we expect to receive a ST21NFCA_SOF_EOF
+ * fill buffer.
+ */
+static int st21nfca_hci_platform_init(struct st21nfca_i2c_phy *phy)
+{
+       u16 wait_reboot[] = { 50, 300, 1000 };
+       char reboot_cmd[] = { 0x7E, 0x66, 0x48, 0xF6, 0x7E };
+       u8 tmp[ST21NFCA_HCI_LLC_MAX_SIZE];
+       int i, r = -1;
+
+       for (i = 0; i < ARRAY_SIZE(wait_reboot) && r < 0; i++) {
+               r = i2c_master_send(phy->i2c_dev, reboot_cmd,
+                                   sizeof(reboot_cmd));
+               if (r < 0)
+                       msleep(wait_reboot[i]);
+       }
+       if (r < 0)
+               return r;
+
+       /* CLF is spending about 20ms to do an internal reboot */
+       msleep(20);
+       r = -1;
+       for (i = 0; i < ARRAY_SIZE(wait_reboot) && r < 0; i++) {
+               r = i2c_master_recv(phy->i2c_dev, tmp,
+                                   ST21NFCA_HCI_LLC_MAX_SIZE);
+               if (r < 0)
+                       msleep(wait_reboot[i]);
+       }
+       if (r < 0)
+               return r;
+
+       for (i = 0; i < ST21NFCA_HCI_LLC_MAX_SIZE &&
+               tmp[i] == ST21NFCA_SOF_EOF; i++)
+               ;
+
+       if (r != ST21NFCA_HCI_LLC_MAX_SIZE)
+               return -ENODEV;
+
+       usleep_range(1000, 1500);
+       return 0;
+}
+
+static int st21nfca_hci_i2c_enable(void *phy_id)
+{
+       struct st21nfca_i2c_phy *phy = phy_id;
+
+       gpio_set_value(phy->gpio_ena, 1);
+       phy->powered = 1;
+       phy->run_mode = ST21NFCA_HCI_MODE;
+
+       usleep_range(10000, 15000);
+
+       return 0;
+}
+
+static void st21nfca_hci_i2c_disable(void *phy_id)
+{
+       struct st21nfca_i2c_phy *phy = phy_id;
+
+       pr_info("\n");
+       gpio_set_value(phy->gpio_ena, 0);
+
+       phy->powered = 0;
+}
+
+static void st21nfca_hci_add_len_crc(struct sk_buff *skb)
+{
+       u16 crc;
+       u8 tmp;
+
+       *skb_push(skb, 1) = 0;
+
+       crc = crc_ccitt(0xffff, skb->data, skb->len);
+       crc = ~crc;
+
+       tmp = crc & 0x00ff;
+       *skb_put(skb, 1) = tmp;
+
+       tmp = (crc >> 8) & 0x00ff;
+       *skb_put(skb, 1) = tmp;
+}
+
+static void st21nfca_hci_remove_len_crc(struct sk_buff *skb)
+{
+       skb_pull(skb, ST21NFCA_FRAME_HEADROOM);
+       skb_trim(skb, skb->len - ST21NFCA_FRAME_TAILROOM);
+}
+
+/*
+ * Writing a frame must not return the number of written bytes.
+ * It must return either zero for success, or <0 for error.
+ * In addition, it must not alter the skb
+ */
+static int st21nfca_hci_i2c_write(void *phy_id, struct sk_buff *skb)
+{
+       int r = -1, i, j;
+       struct st21nfca_i2c_phy *phy = phy_id;
+       struct i2c_client *client = phy->i2c_dev;
+       u8 tmp[ST21NFCA_HCI_LLC_MAX_SIZE * 2];
+
+       I2C_DUMP_SKB("st21nfca_hci_i2c_write", skb);
+
+
+       if (phy->hard_fault != 0)
+               return phy->hard_fault;
+
+       /*
+        * Compute CRC before byte stuffing computation on frame
+        * Note st21nfca_hci_add_len_crc is doing a byte stuffing
+        * on its own value
+        */
+       st21nfca_hci_add_len_crc(skb);
+
+       /* add ST21NFCA_SOF_EOF on tail */
+       *skb_put(skb, 1) = ST21NFCA_SOF_EOF;
+       /* add ST21NFCA_SOF_EOF on head */
+       *skb_push(skb, 1) = ST21NFCA_SOF_EOF;
+
+       /*
+        * Compute byte stuffing
+        * if byte == ST21NFCA_SOF_EOF or ST21NFCA_ESCAPE_BYTE_STUFFING
+        * insert ST21NFCA_ESCAPE_BYTE_STUFFING (escape byte)
+        * xor byte with ST21NFCA_BYTE_STUFFING_MASK
+        */
+       tmp[0] = skb->data[0];
+       for (i = 1, j = 1; i < skb->len - 1; i++, j++) {
+               if (skb->data[i] == ST21NFCA_SOF_EOF
+                   || skb->data[i] == ST21NFCA_ESCAPE_BYTE_STUFFING) {
+                       tmp[j] = ST21NFCA_ESCAPE_BYTE_STUFFING;
+                       j++;
+                       tmp[j] = skb->data[i] ^ ST21NFCA_BYTE_STUFFING_MASK;
+               } else {
+                       tmp[j] = skb->data[i];
+               }
+       }
+       tmp[j] = skb->data[i];
+       j++;
+
+       /*
+        * Manage sleep mode
+        * Try 3 times to send data with delay between each
+        */
+       mutex_lock(&phy->phy_lock);
+       for (i = 0; i < ARRAY_SIZE(wait_tab) && r < 0; i++) {
+               r = i2c_master_send(client, tmp, j);
+               if (r < 0)
+                       msleep(wait_tab[i]);
+       }
+       mutex_unlock(&phy->phy_lock);
+
+       if (r >= 0) {
+               if (r != j)
+                       r = -EREMOTEIO;
+               else
+                       r = 0;
+       }
+
+       st21nfca_hci_remove_len_crc(skb);
+
+       return r;
+}
+
+static int get_frame_size(u8 *buf, int buflen)
+{
+       int len = 0;
+       if (buf[len + 1] == ST21NFCA_SOF_EOF)
+               return 0;
+
+       for (len = 1; len < buflen && buf[len] != ST21NFCA_SOF_EOF; len++)
+               ;
+
+       return len;
+}
+
+static int check_crc(u8 *buf, int buflen)
+{
+       u16 crc;
+
+       crc = crc_ccitt(0xffff, buf, buflen - 2);
+       crc = ~crc;
+
+       if (buf[buflen - 2] != (crc & 0xff) || buf[buflen - 1] != (crc >> 8)) {
+               pr_err(ST21NFCA_HCI_DRIVER_NAME
+                      ": CRC error 0x%x != 0x%x 0x%x\n", crc, buf[buflen - 1],
+                      buf[buflen - 2]);
+
+               pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__);
+               print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
+                              16, 2, buf, buflen, false);
+               return -EPERM;
+       }
+       return 0;
+}
+
+/*
+ * Prepare received data for upper layer.
+ * Received data include byte stuffing, crc and sof/eof
+ * which is not usable by hci part.
+ * returns:
+ * frame size without sof/eof, header and byte stuffing
+ * -EBADMSG : frame was incorrect and discarded
+ */
+static int st21nfca_hci_i2c_repack(struct sk_buff *skb)
+{
+       int i, j, r, size;
+       if (skb->len < 1 || (skb->len > 1 && skb->data[1] != 0))
+               return -EBADMSG;
+
+       size = get_frame_size(skb->data, skb->len);
+       if (size > 0) {
+               skb_trim(skb, size);
+               /* remove ST21NFCA byte stuffing for upper layer */
+               for (i = 1, j = 0; i < skb->len; i++) {
+                       if (skb->data[i + j] ==
+                                       (u8) ST21NFCA_ESCAPE_BYTE_STUFFING) {
+                               skb->data[i] = skb->data[i + j + 1]
+                                               | ST21NFCA_BYTE_STUFFING_MASK;
+                               i++;
+                               j++;
+                       }
+                       skb->data[i] = skb->data[i + j];
+               }
+               /* remove byte stuffing useless byte */
+               skb_trim(skb, i - j);
+               /* remove ST21NFCA_SOF_EOF from head */
+               skb_pull(skb, 1);
+
+               r = check_crc(skb->data, skb->len);
+               if (r != 0) {
+                       i = 0;
+                       return -EBADMSG;
+               }
+
+               /* remove headbyte */
+               skb_pull(skb, 1);
+               /* remove crc. Byte Stuffing is already removed here */
+               skb_trim(skb, skb->len - 2);
+               return skb->len;
+       }
+       return 0;
+}
+
+/*
+ * Reads an shdlc frame and returns it in a newly allocated sk_buff. Guarantees
+ * that i2c bus will be flushed and that next read will start on a new frame.
+ * returned skb contains only LLC header and payload.
+ * returns:
+ * frame size : if received frame is complete (find ST21NFCA_SOF_EOF at
+ * end of read)
+ * -EAGAIN : if received frame is incomplete (not find ST21NFCA_SOF_EOF
+ * at end of read)
+ * -EREMOTEIO : i2c read error (fatal)
+ * -EBADMSG : frame was incorrect and discarded
+ * (value returned from st21nfca_hci_i2c_repack)
+ * -EIO : if no ST21NFCA_SOF_EOF is found after reaching
+ * the read length end sequence
+ */
+static int st21nfca_hci_i2c_read(struct st21nfca_i2c_phy *phy,
+                                struct sk_buff *skb)
+{
+       int r, i;
+       u8 len;
+       u8 buf[ST21NFCA_HCI_LLC_MAX_PAYLOAD];
+       struct i2c_client *client = phy->i2c_dev;
+
+       if (phy->current_read_len < ARRAY_SIZE(len_seq)) {
+               len = len_seq[phy->current_read_len];
+
+               /*
+                * Add retry mecanism
+                * Operation on I2C interface may fail in case of operation on
+                * RF or SWP interface
+                */
+               r = 0;
+               mutex_lock(&phy->phy_lock);
+               for (i = 0; i < ARRAY_SIZE(wait_tab) && r <= 0; i++) {
+                       r = i2c_master_recv(client, buf, len);
+                       if (r < 0)
+                               msleep(wait_tab[i]);
+               }
+               mutex_unlock(&phy->phy_lock);
+
+               if (r != len) {
+                       phy->current_read_len = 0;
+                       return -EREMOTEIO;
+               }
+
+               /*
+                * The first read sequence does not start with SOF.
+                * Data is corrupeted so we drop it.
+                */
+               if (!phy->current_read_len && buf[0] != ST21NFCA_SOF_EOF) {
+                       skb_trim(skb, 0);
+                       phy->current_read_len = 0;
+                       return -EIO;
+               } else if (phy->current_read_len &&
+                       IS_START_OF_FRAME(buf)) {
+                       /*
+                        * Previous frame transmission was interrupted and
+                        * the frame got repeated.
+                        * Received frame start with ST21NFCA_SOF_EOF + 00.
+                        */
+                       skb_trim(skb, 0);
+                       phy->current_read_len = 0;
+               }
+
+               memcpy(skb_put(skb, len), buf, len);
+
+               if (skb->data[skb->len - 1] == ST21NFCA_SOF_EOF) {
+                       phy->current_read_len = 0;
+                       return st21nfca_hci_i2c_repack(skb);
+               }
+               phy->current_read_len++;
+               return -EAGAIN;
+       }
+       return -EIO;
+}
+
+/*
+ * Reads an shdlc frame from the chip. This is not as straightforward as it
+ * seems. The frame format is data-crc, and corruption can occur anywhere
+ * while transiting on i2c bus, such that we could read an invalid data.
+ * The tricky case is when we read a corrupted data or crc. We must detect
+ * this here in order to determine that data can be transmitted to the hci
+ * core. This is the reason why we check the crc here.
+ * The CLF will repeat a frame until we send a RR on that frame.
+ *
+ * On ST21NFCA, IRQ goes in idle when read starts. As no size information are
+ * available in the incoming data, other IRQ might come. Every IRQ will trigger
+ * a read sequence with different length and will fill the current frame.
+ * The reception is complete once we reach a ST21NFCA_SOF_EOF.
+ */
+static irqreturn_t st21nfca_hci_irq_thread_fn(int irq, void *phy_id)
+{
+       struct st21nfca_i2c_phy *phy = phy_id;
+       struct i2c_client *client;
+
+       int r;
+
+       if (!phy || irq != phy->i2c_dev->irq) {
+               WARN_ON_ONCE(1);
+               return IRQ_NONE;
+       }
+
+       client = phy->i2c_dev;
+       dev_dbg(&client->dev, "IRQ\n");
+
+       if (phy->hard_fault != 0)
+               return IRQ_HANDLED;
+
+       r = st21nfca_hci_i2c_read(phy, phy->pending_skb);
+       if (r == -EREMOTEIO) {
+               phy->hard_fault = r;
+
+               nfc_hci_recv_frame(phy->hdev, NULL);
+
+               return IRQ_HANDLED;
+       } else if (r == -EAGAIN || r == -EIO) {
+               return IRQ_HANDLED;
+       } else if (r == -EBADMSG && phy->crc_trials < ARRAY_SIZE(wait_tab)) {
+               /*
+                * With ST21NFCA, only one interface (I2C, RF or SWP)
+                * may be active at a time.
+                * Having incorrect crc is usually due to i2c macrocell
+                * deactivation in the middle of a transmission.
+                * It may generate corrupted data on i2c.
+                * We give sometime to get i2c back.
+                * The complete frame will be repeated.
+                */
+               msleep(wait_tab[phy->crc_trials]);
+               phy->crc_trials++;
+               phy->current_read_len = 0;
+               kfree_skb(phy->pending_skb);
+       } else if (r > 0) {
+               /*
+                * We succeeded to read data from the CLF and
+                * data is valid.
+                * Reset counter.
+                */
+               nfc_hci_recv_frame(phy->hdev, phy->pending_skb);
+               phy->crc_trials = 0;
+       }
+
+       phy->pending_skb = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE * 2, GFP_KERNEL);
+       if (phy->pending_skb == NULL) {
+               phy->hard_fault = -ENOMEM;
+               nfc_hci_recv_frame(phy->hdev, NULL);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static struct nfc_phy_ops i2c_phy_ops = {
+       .write = st21nfca_hci_i2c_write,
+       .enable = st21nfca_hci_i2c_enable,
+       .disable = st21nfca_hci_i2c_disable,
+};
+
+#ifdef CONFIG_OF
+static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client)
+{
+       struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client);
+       struct device_node *pp;
+       int gpio;
+       int r;
+
+       pp = client->dev.of_node;
+       if (!pp)
+               return -ENODEV;
+
+       /* Get GPIO from device tree */
+       gpio = of_get_named_gpio(pp, "enable-gpios", 0);
+       if (gpio < 0) {
+               nfc_err(&client->dev, "Failed to retrieve enable-gpios from device tree\n");
+               return gpio;
+       }
+
+       /* GPIO request and configuration */
+       r = devm_gpio_request(&client->dev, gpio, "clf_enable");
+       if (r) {
+               nfc_err(&client->dev, "Failed to request enable pin\n");
+               return -ENODEV;
+       }
+
+       r = gpio_direction_output(gpio, 1);
+       if (r) {
+               nfc_err(&client->dev, "Failed to set enable pin direction as output\n");
+               return -ENODEV;
+       }
+       phy->gpio_ena = gpio;
+
+       /* IRQ */
+       r = irq_of_parse_and_map(pp, 0);
+       if (r < 0) {
+               nfc_err(&client->dev,
+                               "Unable to get irq, error: %d\n", r);
+               return r;
+       }
+
+       phy->irq_polarity = irq_get_trigger_type(r);
+       client->irq = r;
+
+       return 0;
+}
+#else
+static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client)
+{
+       return -ENODEV;
+}
+#endif
+
+static int st21nfca_hci_i2c_request_resources(struct i2c_client *client)
+{
+       struct st21nfca_nfc_platform_data *pdata;
+       struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client);
+       int r;
+       int irq;
+
+       pdata = client->dev.platform_data;
+       if (pdata == NULL) {
+               nfc_err(&client->dev, "No platform data\n");
+               return -EINVAL;
+       }
+
+       /* store for later use */
+       phy->gpio_irq = pdata->gpio_irq;
+       phy->gpio_ena = pdata->gpio_ena;
+       phy->irq_polarity = pdata->irq_polarity;
+
+       r = devm_gpio_request(&client->dev, phy->gpio_irq, "wake_up");
+       if (r) {
+               pr_err("%s : gpio_request failed\n", __FILE__);
+               return -ENODEV;
+       }
+
+       r = gpio_direction_input(phy->gpio_irq);
+       if (r) {
+               pr_err("%s : gpio_direction_input failed\n", __FILE__);
+               return -ENODEV;
+       }
+
+       if (phy->gpio_ena > 0) {
+               r = devm_gpio_request(&client->dev,
+                                       phy->gpio_ena, "clf_enable");
+               if (r) {
+                       pr_err("%s : ena gpio_request failed\n", __FILE__);
+                       return -ENODEV;
+               }
+               r = gpio_direction_output(phy->gpio_ena, 1);
+
+               if (r) {
+                       pr_err("%s : ena gpio_direction_output failed\n",
+                              __FILE__);
+                       return -ENODEV;
+               }
+       }
+
+       /* IRQ */
+       irq = gpio_to_irq(phy->gpio_irq);
+       if (irq < 0) {
+               nfc_err(&client->dev,
+                               "Unable to get irq number for GPIO %d error %d\n",
+                               phy->gpio_irq, r);
+               return -ENODEV;
+       }
+       client->irq = irq;
+
+       return 0;
+}
+
+static int st21nfca_hci_i2c_probe(struct i2c_client *client,
+                                 const struct i2c_device_id *id)
+{
+       struct st21nfca_i2c_phy *phy;
+       struct st21nfca_nfc_platform_data *pdata;
+       int r;
+
+       dev_dbg(&client->dev, "%s\n", __func__);
+       dev_dbg(&client->dev, "IRQ: %d\n", client->irq);
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               nfc_err(&client->dev, "Need I2C_FUNC_I2C\n");
+               return -ENODEV;
+       }
+
+       phy = devm_kzalloc(&client->dev, sizeof(struct st21nfca_i2c_phy),
+                          GFP_KERNEL);
+       if (!phy) {
+               nfc_err(&client->dev,
+                       "Cannot allocate memory for st21nfca i2c phy.\n");
+               return -ENOMEM;
+       }
+
+       phy->i2c_dev = client;
+       phy->pending_skb = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE * 2, GFP_KERNEL);
+       if (phy->pending_skb == NULL)
+               return -ENOMEM;
+
+       phy->current_read_len = 0;
+       phy->crc_trials = 0;
+       mutex_init(&phy->phy_lock);
+       i2c_set_clientdata(client, phy);
+
+       pdata = client->dev.platform_data;
+       if (!pdata && client->dev.of_node) {
+               r = st21nfca_hci_i2c_of_request_resources(client);
+               if (r) {
+                       nfc_err(&client->dev, "No platform data\n");
+                       return r;
+               }
+       } else if (pdata) {
+               r = st21nfca_hci_i2c_request_resources(client);
+               if (r) {
+                       nfc_err(&client->dev, "Cannot get platform resources\n");
+                       return r;
+               }
+       } else {
+               nfc_err(&client->dev, "st21nfca platform resources not available\n");
+               return -ENODEV;
+       }
+
+       r = st21nfca_hci_platform_init(phy);
+       if (r < 0) {
+               nfc_err(&client->dev, "Unable to reboot st21nfca\n");
+               return -ENODEV;
+       }
+
+       r = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+                               st21nfca_hci_irq_thread_fn,
+                               phy->irq_polarity | IRQF_ONESHOT,
+                               ST21NFCA_HCI_DRIVER_NAME, phy);
+       if (r < 0) {
+               nfc_err(&client->dev, "Unable to register IRQ handler\n");
+               return r;
+       }
+
+       return st21nfca_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME,
+                              ST21NFCA_FRAME_HEADROOM, ST21NFCA_FRAME_TAILROOM,
+                              ST21NFCA_HCI_LLC_MAX_PAYLOAD, &phy->hdev);
+}
+
+static int st21nfca_hci_i2c_remove(struct i2c_client *client)
+{
+       struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client);
+
+       dev_dbg(&client->dev, "%s\n", __func__);
+
+       st21nfca_hci_remove(phy->hdev);
+
+       if (phy->powered)
+               st21nfca_hci_i2c_disable(phy);
+
+       return 0;
+}
+
+static const struct of_device_id of_st21nfca_i2c_match[] = {
+       { .compatible = "st,st21nfca_i2c", },
+       {}
+};
+
+static struct i2c_driver st21nfca_hci_i2c_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = ST21NFCA_HCI_I2C_DRIVER_NAME,
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(of_st21nfca_i2c_match),
+       },
+       .probe = st21nfca_hci_i2c_probe,
+       .id_table = st21nfca_hci_i2c_id_table,
+       .remove = st21nfca_hci_i2c_remove,
+};
+
+module_i2c_driver(st21nfca_hci_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/st21nfca/st21nfca.c b/drivers/nfc/st21nfca/st21nfca.c
new file mode 100644 (file)
index 0000000..51e0f00
--- /dev/null
@@ -0,0 +1,698 @@
+/*
+ * HCI based Driver for STMicroelectronics NFC Chip
+ *
+ * Copyright (C) 2014  STMicroelectronics SAS. 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 that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+
+#include "st21nfca.h"
+
+#define DRIVER_DESC "HCI NFC driver for ST21NFCA"
+
+#define FULL_VERSION_LEN 3
+
+/* Proprietary gates, events, commands and registers */
+
+/* Commands that apply to all RF readers */
+#define ST21NFCA_RF_READER_CMD_PRESENCE_CHECK  0x30
+
+#define ST21NFCA_RF_READER_ISO15693_GATE       0x12
+#define ST21NFCA_RF_READER_ISO15693_INVENTORY 0x01
+
+/*
+ * Reader gate for communication with contact-less cards using Type A
+ * protocol ISO14443-3 but not compliant with ISO14443-4
+ */
+#define ST21NFCA_RF_READER_14443_3_A_GATE      0x15
+#define ST21NFCA_RF_READER_14443_3_A_UID       0x02
+#define ST21NFCA_RF_READER_14443_3_A_ATQA      0x03
+#define ST21NFCA_RF_READER_14443_3_A_SAK       0x04
+
+#define ST21NFCA_DEVICE_MGNT_GATE              0x01
+#define ST21NFCA_DEVICE_MGNT_PIPE              0x02
+
+#define ST21NFCA_DM_GETINFO         0x13
+#define ST21NFCA_DM_GETINFO_PIPE_LIST       0x02
+#define ST21NFCA_DM_GETINFO_PIPE_INFO       0x01
+#define ST21NFCA_DM_PIPE_CREATED        0x02
+#define ST21NFCA_DM_PIPE_OPEN           0x04
+#define ST21NFCA_DM_RF_ACTIVE           0x80
+
+#define ST21NFCA_DM_IS_PIPE_OPEN(p) \
+       ((p & 0x0f) == (ST21NFCA_DM_PIPE_CREATED | ST21NFCA_DM_PIPE_OPEN))
+
+#define ST21NFCA_NFC_MODE      0x03    /* NFC_MODE parameter*/
+
+static DECLARE_BITMAP(dev_mask, ST21NFCA_NUM_DEVICES);
+
+static struct nfc_hci_gate st21nfca_gates[] = {
+       {NFC_HCI_ADMIN_GATE, NFC_HCI_ADMIN_PIPE},
+       {NFC_HCI_LOOPBACK_GATE, NFC_HCI_INVALID_PIPE},
+       {NFC_HCI_ID_MGMT_GATE, NFC_HCI_INVALID_PIPE},
+       {NFC_HCI_LINK_MGMT_GATE, NFC_HCI_LINK_MGMT_PIPE},
+       {NFC_HCI_RF_READER_B_GATE, NFC_HCI_INVALID_PIPE},
+       {NFC_HCI_RF_READER_A_GATE, NFC_HCI_INVALID_PIPE},
+       {ST21NFCA_DEVICE_MGNT_GATE, ST21NFCA_DEVICE_MGNT_PIPE},
+       {ST21NFCA_RF_READER_F_GATE, NFC_HCI_INVALID_PIPE},
+       {ST21NFCA_RF_READER_14443_3_A_GATE, NFC_HCI_INVALID_PIPE},
+       {ST21NFCA_RF_READER_ISO15693_GATE, NFC_HCI_INVALID_PIPE},
+};
+
+struct st21nfca_pipe_info {
+       u8 pipe_state;
+       u8 src_host_id;
+       u8 src_gate_id;
+       u8 dst_host_id;
+       u8 dst_gate_id;
+} __packed;
+
+/* Largest headroom needed for outgoing custom commands */
+#define ST21NFCA_CMDS_HEADROOM  7
+
+static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev)
+{
+       int i, j, r;
+       struct sk_buff *skb_pipe_list, *skb_pipe_info;
+       struct st21nfca_pipe_info *info;
+
+       u8 pipe_list[] = { ST21NFCA_DM_GETINFO_PIPE_LIST,
+               NFC_HCI_TERMINAL_HOST_ID
+       };
+       u8 pipe_info[] = { ST21NFCA_DM_GETINFO_PIPE_INFO,
+               NFC_HCI_TERMINAL_HOST_ID, 0
+       };
+
+       skb_pipe_list = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE, GFP_KERNEL);
+       if (!skb_pipe_list) {
+               r = -ENOMEM;
+               goto free_list;
+       }
+
+       skb_pipe_info = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE, GFP_KERNEL);
+       if (!skb_pipe_info) {
+               r = -ENOMEM;
+               goto free_info;
+       }
+
+       /* On ST21NFCA device pipes number are dynamics
+        * A maximum of 16 pipes can be created at the same time
+        * If pipes are already created, hci_dev_up will fail.
+        * Doing a clear all pipe is a bad idea because:
+        * - It does useless EEPROM cycling
+        * - It might cause issue for secure elements support
+        * (such as removing connectivity or APDU reader pipe)
+        * A better approach on ST21NFCA is to:
+        * - get a pipe list for each host.
+        * (eg: NFC_HCI_HOST_CONTROLLER_ID for now).
+        * (TODO Later on UICC HOST and eSE HOST)
+        * - get pipe information
+        * - match retrieved pipe list in st21nfca_gates
+        * ST21NFCA_DEVICE_MGNT_GATE is a proprietary gate
+        * with ST21NFCA_DEVICE_MGNT_PIPE.
+        * Pipe can be closed and need to be open.
+        */
+       r = nfc_hci_connect_gate(hdev, NFC_HCI_HOST_CONTROLLER_ID,
+               ST21NFCA_DEVICE_MGNT_GATE, ST21NFCA_DEVICE_MGNT_PIPE);
+       if (r < 0)
+               goto free_info;
+
+       /* Get pipe list */
+       r = nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+                       ST21NFCA_DM_GETINFO, pipe_list, sizeof(pipe_list),
+                       &skb_pipe_list);
+       if (r < 0)
+               goto free_info;
+
+       /* Complete the existing gate_pipe table */
+       for (i = 0; i < skb_pipe_list->len; i++) {
+               pipe_info[2] = skb_pipe_list->data[i];
+               r = nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+                                       ST21NFCA_DM_GETINFO, pipe_info,
+                                       sizeof(pipe_info), &skb_pipe_info);
+
+               if (r)
+                       continue;
+
+               /*
+                * Match pipe ID and gate ID
+                * Output format from ST21NFC_DM_GETINFO is:
+                * - pipe state (1byte)
+                * - source hid (1byte)
+                * - source gid (1byte)
+                * - destination hid (1byte)
+                * - destination gid (1byte)
+                */
+               info = (struct st21nfca_pipe_info *) skb_pipe_info->data;
+               for (j = 0; (j < ARRAY_SIZE(st21nfca_gates)) &&
+                       (st21nfca_gates[j].gate != info->dst_gate_id);
+                       j++)
+                       ;
+
+               if (j < ARRAY_SIZE(st21nfca_gates) &&
+                       st21nfca_gates[j].gate == info->dst_gate_id &&
+                       ST21NFCA_DM_IS_PIPE_OPEN(info->pipe_state)) {
+                       st21nfca_gates[j].pipe = pipe_info[2];
+                       hdev->gate2pipe[st21nfca_gates[j].gate] =
+                               st21nfca_gates[j].pipe;
+               }
+       }
+
+       /*
+        * 3 gates have a well known pipe ID.
+        * They will never appear in the pipe list
+        */
+       if (skb_pipe_list->len + 3 < ARRAY_SIZE(st21nfca_gates)) {
+               for (i = skb_pipe_list->len + 3;
+                               i < ARRAY_SIZE(st21nfca_gates); i++) {
+                       r = nfc_hci_connect_gate(hdev,
+                                       NFC_HCI_HOST_CONTROLLER_ID,
+                                       st21nfca_gates[i].gate,
+                                       st21nfca_gates[i].pipe);
+                       if (r < 0)
+                               goto free_info;
+               }
+       }
+
+       memcpy(hdev->init_data.gates, st21nfca_gates, sizeof(st21nfca_gates));
+free_info:
+       kfree_skb(skb_pipe_info);
+free_list:
+       kfree_skb(skb_pipe_list);
+       return r;
+}
+
+static int st21nfca_hci_open(struct nfc_hci_dev *hdev)
+{
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+       int r;
+
+       mutex_lock(&info->info_lock);
+
+       if (info->state != ST21NFCA_ST_COLD) {
+               r = -EBUSY;
+               goto out;
+       }
+
+       r = info->phy_ops->enable(info->phy_id);
+
+       if (r == 0)
+               info->state = ST21NFCA_ST_READY;
+
+out:
+       mutex_unlock(&info->info_lock);
+       return r;
+}
+
+static void st21nfca_hci_close(struct nfc_hci_dev *hdev)
+{
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       mutex_lock(&info->info_lock);
+
+       if (info->state == ST21NFCA_ST_COLD)
+               goto out;
+
+       info->phy_ops->disable(info->phy_id);
+       info->state = ST21NFCA_ST_COLD;
+
+out:
+       mutex_unlock(&info->info_lock);
+}
+
+static int st21nfca_hci_ready(struct nfc_hci_dev *hdev)
+{
+       struct sk_buff *skb;
+
+       u8 param;
+       int r;
+
+       param = NFC_HCI_UICC_HOST_ID;
+       r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE,
+                             NFC_HCI_ADMIN_WHITELIST, &param, 1);
+       if (r < 0)
+               return r;
+
+       /* Set NFC_MODE in device management gate to enable */
+       r = nfc_hci_get_param(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+                             ST21NFCA_NFC_MODE, &skb);
+       if (r < 0)
+               return r;
+
+       if (skb->data[0] == 0) {
+               kfree_skb(skb);
+               param = 1;
+
+               r = nfc_hci_set_param(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+                                       ST21NFCA_NFC_MODE, &param, 1);
+               if (r < 0)
+                       return r;
+       }
+
+       r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
+                              NFC_HCI_EVT_END_OPERATION, NULL, 0);
+       if (r < 0)
+               return r;
+
+       r = nfc_hci_get_param(hdev, NFC_HCI_ID_MGMT_GATE,
+                             NFC_HCI_ID_MGMT_VERSION_SW, &skb);
+       if (r < 0)
+               return r;
+
+       if (skb->len != FULL_VERSION_LEN) {
+               kfree_skb(skb);
+               return -EINVAL;
+       }
+
+       print_hex_dump(KERN_DEBUG, "FULL VERSION SOFTWARE INFO: ",
+                      DUMP_PREFIX_NONE, 16, 1,
+                      skb->data, FULL_VERSION_LEN, false);
+
+       kfree_skb(skb);
+
+       return 0;
+}
+
+static int st21nfca_hci_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       return info->phy_ops->write(info->phy_id, skb);
+}
+
+static int st21nfca_hci_start_poll(struct nfc_hci_dev *hdev,
+                                  u32 im_protocols, u32 tm_protocols)
+{
+       int r;
+
+       pr_info(DRIVER_DESC ": %s protocols 0x%x 0x%x\n",
+               __func__, im_protocols, tm_protocols);
+
+       r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
+                              NFC_HCI_EVT_END_OPERATION, NULL, 0);
+       if (r < 0)
+               return r;
+       if (im_protocols) {
+               /*
+                * enable polling according to im_protocols & tm_protocols
+                * - CLOSE pipe according to im_protocols & tm_protocols
+                */
+               if ((NFC_HCI_RF_READER_B_GATE & im_protocols) == 0) {
+                       r = nfc_hci_disconnect_gate(hdev,
+                                       NFC_HCI_RF_READER_B_GATE);
+                       if (r < 0)
+                               return r;
+               }
+
+               if ((NFC_HCI_RF_READER_A_GATE & im_protocols) == 0) {
+                       r = nfc_hci_disconnect_gate(hdev,
+                                       NFC_HCI_RF_READER_A_GATE);
+                       if (r < 0)
+                               return r;
+               }
+
+               if ((ST21NFCA_RF_READER_F_GATE & im_protocols) == 0) {
+                       r = nfc_hci_disconnect_gate(hdev,
+                                       ST21NFCA_RF_READER_F_GATE);
+                       if (r < 0)
+                               return r;
+               }
+
+               if ((ST21NFCA_RF_READER_14443_3_A_GATE & im_protocols) == 0) {
+                       r = nfc_hci_disconnect_gate(hdev,
+                                       ST21NFCA_RF_READER_14443_3_A_GATE);
+                       if (r < 0)
+                               return r;
+               }
+
+               if ((ST21NFCA_RF_READER_ISO15693_GATE & im_protocols) == 0) {
+                       r = nfc_hci_disconnect_gate(hdev,
+                                       ST21NFCA_RF_READER_ISO15693_GATE);
+                       if (r < 0)
+                               return r;
+               }
+
+               r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
+                                      NFC_HCI_EVT_READER_REQUESTED, NULL, 0);
+               if (r < 0)
+                       nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
+                                          NFC_HCI_EVT_END_OPERATION, NULL, 0);
+       }
+       return r;
+}
+
+static int st21nfca_get_iso14443_3_atqa(struct nfc_hci_dev *hdev, u16 *atqa)
+{
+       int r;
+       struct sk_buff *atqa_skb = NULL;
+
+       r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE,
+                             ST21NFCA_RF_READER_14443_3_A_ATQA, &atqa_skb);
+       if (r < 0)
+               goto exit;
+
+       if (atqa_skb->len != 2) {
+               r = -EPROTO;
+               goto exit;
+       }
+
+       *atqa = be16_to_cpu(*(__be16 *) atqa_skb->data);
+
+exit:
+       kfree_skb(atqa_skb);
+       return r;
+}
+
+static int st21nfca_get_iso14443_3_sak(struct nfc_hci_dev *hdev, u8 *sak)
+{
+       int r;
+       struct sk_buff *sak_skb = NULL;
+
+       r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE,
+                             ST21NFCA_RF_READER_14443_3_A_SAK, &sak_skb);
+       if (r < 0)
+               goto exit;
+
+       if (sak_skb->len != 1) {
+               r = -EPROTO;
+               goto exit;
+       }
+
+       *sak = sak_skb->data[0];
+
+exit:
+       kfree_skb(sak_skb);
+       return r;
+}
+
+static int st21nfca_get_iso14443_3_uid(struct nfc_hci_dev *hdev, u8 *gate,
+                                      int *len)
+{
+       int r;
+       struct sk_buff *uid_skb = NULL;
+
+       r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE,
+                             ST21NFCA_RF_READER_14443_3_A_UID, &uid_skb);
+       if (r < 0)
+               goto exit;
+
+       if (uid_skb->len == 0 || uid_skb->len > NFC_NFCID1_MAXSIZE) {
+               r = -EPROTO;
+               goto exit;
+       }
+
+       gate = uid_skb->data;
+       *len = uid_skb->len;
+exit:
+       kfree_skb(uid_skb);
+       return r;
+}
+
+static int st21nfca_get_iso15693_inventory(struct nfc_hci_dev *hdev,
+                                          struct nfc_target *target)
+{
+       int r;
+       struct sk_buff *inventory_skb = NULL;
+
+       r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_ISO15693_GATE,
+                             ST21NFCA_RF_READER_ISO15693_INVENTORY,
+                             &inventory_skb);
+       if (r < 0)
+               goto exit;
+
+       skb_pull(inventory_skb, 2);
+
+       if (inventory_skb->len == 0 ||
+           inventory_skb->len > NFC_ISO15693_UID_MAXSIZE) {
+               r = -EPROTO;
+               goto exit;
+       }
+
+       memcpy(target->iso15693_uid, inventory_skb->data, inventory_skb->len);
+       target->iso15693_dsfid  = inventory_skb->data[1];
+       target->is_iso15693 = 1;
+exit:
+       kfree_skb(inventory_skb);
+       return r;
+}
+
+static int st21nfca_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,
+                                        struct nfc_target *target)
+{
+       int r, len;
+       u16 atqa;
+       u8 sak;
+       u8 uid[NFC_NFCID1_MAXSIZE];
+
+       switch (gate) {
+       case ST21NFCA_RF_READER_F_GATE:
+               target->supported_protocols = NFC_PROTO_FELICA_MASK;
+               break;
+       case ST21NFCA_RF_READER_14443_3_A_GATE:
+               /* ISO14443-3 type 1 or 2 tags */
+               r = st21nfca_get_iso14443_3_atqa(hdev, &atqa);
+               if (r < 0)
+                       return r;
+               if (atqa == 0x000c) {
+                       target->supported_protocols = NFC_PROTO_JEWEL_MASK;
+                       target->sens_res = 0x0c00;
+               } else {
+                       r = st21nfca_get_iso14443_3_sak(hdev, &sak);
+                       if (r < 0)
+                               return r;
+
+                       r = st21nfca_get_iso14443_3_uid(hdev, uid, &len);
+                       if (r < 0)
+                               return r;
+
+                       target->supported_protocols =
+                           nfc_hci_sak_to_protocol(sak);
+                       if (target->supported_protocols == 0xffffffff)
+                               return -EPROTO;
+
+                       target->sens_res = atqa;
+                       target->sel_res = sak;
+                       memcpy(target->nfcid1, uid, len);
+                       target->nfcid1_len = len;
+               }
+
+               break;
+       case ST21NFCA_RF_READER_ISO15693_GATE:
+               target->supported_protocols = NFC_PROTO_ISO15693_MASK;
+               r = st21nfca_get_iso15693_inventory(hdev, target);
+               if (r < 0)
+                       return r;
+               break;
+       default:
+               return -EPROTO;
+       }
+
+       return 0;
+}
+
+#define ST21NFCA_CB_TYPE_READER_ISO15693 1
+static void st21nfca_hci_data_exchange_cb(void *context, struct sk_buff *skb,
+                                         int err)
+{
+       struct st21nfca_hci_info *info = context;
+
+       switch (info->async_cb_type) {
+       case ST21NFCA_CB_TYPE_READER_ISO15693:
+               if (err == 0)
+                       skb_trim(skb, skb->len - 1);
+               info->async_cb(info->async_cb_context, skb, err);
+               break;
+       default:
+               if (err == 0)
+                       kfree_skb(skb);
+               break;
+       }
+}
+
+/*
+ * Returns:
+ * <= 0: driver handled the data exchange
+ *    1: driver doesn't especially handle, please do standard processing
+ */
+static int st21nfca_hci_im_transceive(struct nfc_hci_dev *hdev,
+                                     struct nfc_target *target,
+                                     struct sk_buff *skb,
+                                     data_exchange_cb_t cb, void *cb_context)
+{
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       pr_info(DRIVER_DESC ": %s for gate=%d len=%d\n", __func__,
+               target->hci_reader_gate, skb->len);
+
+       switch (target->hci_reader_gate) {
+       case ST21NFCA_RF_READER_F_GATE:
+               *skb_push(skb, 1) = 0x1a;
+               return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+                                             ST21NFCA_WR_XCHG_DATA, skb->data,
+                                             skb->len, cb, cb_context);
+       case ST21NFCA_RF_READER_14443_3_A_GATE:
+               *skb_push(skb, 1) = 0x1a;       /* CTR, see spec:10.2.2.1 */
+
+               return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+                                             ST21NFCA_WR_XCHG_DATA, skb->data,
+                                             skb->len, cb, cb_context);
+       case ST21NFCA_RF_READER_ISO15693_GATE:
+               info->async_cb_type = ST21NFCA_CB_TYPE_READER_ISO15693;
+               info->async_cb = cb;
+               info->async_cb_context = cb_context;
+
+               *skb_push(skb, 1) = 0x17;
+
+               return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+                                             ST21NFCA_WR_XCHG_DATA, skb->data,
+                                             skb->len,
+                                             st21nfca_hci_data_exchange_cb,
+                                             info);
+               break;
+       default:
+               return 1;
+       }
+}
+
+static int st21nfca_hci_check_presence(struct nfc_hci_dev *hdev,
+                                      struct nfc_target *target)
+{
+       u8 fwi = 0x11;
+       switch (target->hci_reader_gate) {
+       case NFC_HCI_RF_READER_A_GATE:
+       case NFC_HCI_RF_READER_B_GATE:
+               /*
+                * PRESENCE_CHECK on those gates is available
+                * However, the answer to this command is taking 3 * fwi
+                * if the card is no present.
+                * Instead, we send an empty I-Frame with a very short
+                * configurable fwi ~604µs.
+                */
+               return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
+                                       ST21NFCA_WR_XCHG_DATA, &fwi, 1, NULL);
+       case ST21NFCA_RF_READER_14443_3_A_GATE:
+               return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
+                                       ST21NFCA_RF_READER_CMD_PRESENCE_CHECK,
+                                       NULL, 0, NULL);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static struct nfc_hci_ops st21nfca_hci_ops = {
+       .open = st21nfca_hci_open,
+       .close = st21nfca_hci_close,
+       .load_session = st21nfca_hci_load_session,
+       .hci_ready = st21nfca_hci_ready,
+       .xmit = st21nfca_hci_xmit,
+       .start_poll = st21nfca_hci_start_poll,
+       .target_from_gate = st21nfca_hci_target_from_gate,
+       .im_transceive = st21nfca_hci_im_transceive,
+       .check_presence = st21nfca_hci_check_presence,
+};
+
+int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
+                      char *llc_name, int phy_headroom, int phy_tailroom,
+                      int phy_payload, struct nfc_hci_dev **hdev)
+{
+       struct st21nfca_hci_info *info;
+       int r = 0;
+       int dev_num;
+       u32 protocols;
+       struct nfc_hci_init_data init_data;
+       unsigned long quirks = 0;
+
+       info = kzalloc(sizeof(struct st21nfca_hci_info), GFP_KERNEL);
+       if (!info) {
+               r = -ENOMEM;
+               goto err_alloc_hdev;
+       }
+
+       info->phy_ops = phy_ops;
+       info->phy_id = phy_id;
+       info->state = ST21NFCA_ST_COLD;
+       mutex_init(&info->info_lock);
+
+       init_data.gate_count = ARRAY_SIZE(st21nfca_gates);
+
+       memcpy(init_data.gates, st21nfca_gates, sizeof(st21nfca_gates));
+
+       /*
+        * Session id must include the driver name + i2c bus addr
+        * persistent info to discriminate 2 identical chips
+        */
+       dev_num = find_first_zero_bit(dev_mask, ST21NFCA_NUM_DEVICES);
+       if (dev_num >= ST21NFCA_NUM_DEVICES)
+               goto err_alloc_hdev;
+
+       scnprintf(init_data.session_id, sizeof(init_data.session_id), "%s%2x",
+                 "ST21AH", dev_num);
+
+       protocols = NFC_PROTO_JEWEL_MASK |
+           NFC_PROTO_MIFARE_MASK |
+           NFC_PROTO_FELICA_MASK |
+           NFC_PROTO_ISO14443_MASK |
+           NFC_PROTO_ISO14443_B_MASK |
+           NFC_PROTO_ISO15693_MASK;
+
+       set_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &quirks);
+
+       info->hdev =
+           nfc_hci_allocate_device(&st21nfca_hci_ops, &init_data, quirks,
+                                   protocols, llc_name,
+                                   phy_headroom + ST21NFCA_CMDS_HEADROOM,
+                                   phy_tailroom, phy_payload);
+
+       if (!info->hdev) {
+               pr_err("Cannot allocate nfc hdev.\n");
+               r = -ENOMEM;
+               goto err_alloc_hdev;
+       }
+
+       nfc_hci_set_clientdata(info->hdev, info);
+
+       r = nfc_hci_register_device(info->hdev);
+       if (r)
+               goto err_regdev;
+
+       *hdev = info->hdev;
+
+       return 0;
+
+err_regdev:
+       nfc_hci_free_device(info->hdev);
+
+err_alloc_hdev:
+       kfree(info);
+
+       return r;
+}
+EXPORT_SYMBOL(st21nfca_hci_probe);
+
+void st21nfca_hci_remove(struct nfc_hci_dev *hdev)
+{
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       nfc_hci_unregister_device(hdev);
+       nfc_hci_free_device(hdev);
+       kfree(info);
+}
+EXPORT_SYMBOL(st21nfca_hci_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/st21nfca/st21nfca.h b/drivers/nfc/st21nfca/st21nfca.h
new file mode 100644 (file)
index 0000000..334cd90
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014  STMicroelectronics SAS. 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 that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LOCAL_ST21NFCA_H_
+#define __LOCAL_ST21NFCA_H_
+
+#include <net/nfc/hci.h>
+
+#define HCI_MODE 0
+
+/* framing in HCI mode */
+#define ST21NFCA_SOF_EOF_LEN    2
+
+/* Almost every time value is 0 */
+#define ST21NFCA_HCI_LLC_LEN    1
+
+/* Size in worst case :
+ * In normal case CRC len = 2 but byte stuffing
+ * may appear in case one CRC byte = ST21NFCA_SOF_EOF
+ */
+#define ST21NFCA_HCI_LLC_CRC    4
+
+#define ST21NFCA_HCI_LLC_LEN_CRC        (ST21NFCA_SOF_EOF_LEN + \
+                                               ST21NFCA_HCI_LLC_LEN + \
+                                               ST21NFCA_HCI_LLC_CRC)
+#define ST21NFCA_HCI_LLC_MIN_SIZE       (1 + ST21NFCA_HCI_LLC_LEN_CRC)
+
+/* Worst case when adding byte stuffing between each byte */
+#define ST21NFCA_HCI_LLC_MAX_PAYLOAD    29
+#define ST21NFCA_HCI_LLC_MAX_SIZE       (ST21NFCA_HCI_LLC_LEN_CRC + 1 + \
+                                       ST21NFCA_HCI_LLC_MAX_PAYLOAD)
+
+#define DRIVER_DESC "HCI NFC driver for ST21NFCA"
+
+#define ST21NFCA_HCI_MODE 0
+
+#define ST21NFCA_NUM_DEVICES 256
+
+int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
+                      char *llc_name, int phy_headroom, int phy_tailroom,
+                      int phy_payload, struct nfc_hci_dev **hdev);
+void st21nfca_hci_remove(struct nfc_hci_dev *hdev);
+
+enum st21nfca_state {
+       ST21NFCA_ST_COLD,
+       ST21NFCA_ST_READY,
+};
+
+struct st21nfca_hci_info {
+       struct nfc_phy_ops *phy_ops;
+       void *phy_id;
+
+       struct nfc_hci_dev *hdev;
+
+       enum st21nfca_state state;
+
+       struct mutex info_lock;
+
+       int async_cb_type;
+       data_exchange_cb_t async_cb;
+       void *async_cb_context;
+
+} __packed;
+
+/* Reader RF commands */
+#define ST21NFCA_WR_XCHG_DATA            0x10
+
+#define ST21NFCA_RF_READER_F_GATE               0x14
+#define ST21NFCA_RF_READER_F_DATARATE 0x01
+#define ST21NFCA_RF_READER_F_DATARATE_106 0x01
+#define ST21NFCA_RF_READER_F_DATARATE_212 0x02
+#define ST21NFCA_RF_READER_F_DATARATE_424 0x04
+
+#endif /* __LOCAL_ST21NFCA_H_ */
index d9babe986473f11de9a4c62eabac003a3c313e02..3b78b031e617df51e98101a231519a000cd159ed 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/device.h>
 #include <linux/netdevice.h>
 #include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
 #include <linux/nfc.h>
 #include <linux/skbuff.h>
 #include <linux/delay.h>
  * only the SRX bit set, it means that all of the data has been received
  * (once what's in the fifo has been read).  However, depending on timing
  * an interrupt status with only the SRX bit set may not be recived.  In
- * those cases, the timeout mechanism is used to wait 5 ms in case more
- * data arrives.  After 5 ms, it is assumed that all of the data has been
+ * those cases, the timeout mechanism is used to wait 20 ms in case more
+ * data arrives.  After 20 ms, it is assumed that all of the data has been
  * received and the accumulated rx data is sent upstream.  The
  * 'TRF7970A_ST_WAIT_FOR_RX_DATA_CONT' state is used for this purpose
  * (i.e., it indicates that some data has been received but we're not sure
  * if there is more coming so a timeout in this state means all data has
- * been received and there isn't an error).  The delay is 5 ms since delays
- * over 2 ms have been observed during testing (a little extra just in case).
+ * been received and there isn't an error).  The delay is 20 ms since delays
+ * of ~16 ms have been observed during testing.
  *
  * Type 2 write and sector select commands respond with a 4-bit ACK or NACK.
  * Having only 4 bits in the FIFO won't normally generate an interrupt so
 
 #define TRF7970A_SUPPORTED_PROTOCOLS \
                (NFC_PROTO_MIFARE_MASK | NFC_PROTO_ISO14443_MASK |      \
+                NFC_PROTO_ISO14443_B_MASK | NFC_PROTO_FELICA_MASK | \
                 NFC_PROTO_ISO15693_MASK)
 
+#define TRF7970A_AUTOSUSPEND_DELAY             30000 /* 30 seconds */
+
 /* TX data must be prefixed with a FIFO reset cmd, a cmd that depends
  * on what the current framing is, the address of the TX length byte 1
  * register (0x1d), and the 2 byte length of the data to be transmitted.
 /* TX length is 3 nibbles long ==> 4KB - 1 bytes max */
 #define TRF7970A_TX_MAX                                (4096 - 1)
 
-#define TRF7970A_WAIT_FOR_RX_DATA_TIMEOUT      5
+#define TRF7970A_WAIT_FOR_RX_DATA_TIMEOUT      20
 #define TRF7970A_WAIT_FOR_FIFO_DRAIN_TIMEOUT   3
 #define TRF7970A_WAIT_TO_ISSUE_ISO15693_EOF    20
 
@@ -330,13 +334,15 @@ struct trf7970a {
        struct regulator                *regulator;
        struct nfc_digital_dev          *ddev;
        u32                             quirks;
-       bool                            powering_up;
        bool                            aborting;
        struct sk_buff                  *tx_skb;
        struct sk_buff                  *rx_skb;
        nfc_digital_cmd_complete_t      cb;
        void                            *cb_arg;
+       u8                              chip_status_ctrl;
        u8                              iso_ctrl;
+       u8                              iso_ctrl_tech;
+       u8                              modulator_sys_clk_ctrl;
        u8                              special_fcn_reg1;
        int                             technology;
        int                             framing;
@@ -681,7 +687,9 @@ static irqreturn_t trf7970a_irq(int irq, void *dev_id)
                        trf->ignore_timeout =
                                !cancel_delayed_work(&trf->timeout_work);
                        trf7970a_drain_fifo(trf, status);
-               } else if (!(status & TRF7970A_IRQ_STATUS_TX)) {
+               } else if (status == TRF7970A_IRQ_STATUS_TX) {
+                       trf7970a_cmd(trf, TRF7970A_CMD_FIFO_RESET);
+               } else {
                        trf7970a_send_err_upstream(trf, -EIO);
                }
                break;
@@ -757,8 +765,8 @@ static int trf7970a_init(struct trf7970a *trf)
        if (ret)
                goto err_out;
 
-       ret = trf7970a_write(trf, TRF7970A_MODULATOR_SYS_CLK_CTRL,
-                       TRF7970A_MODULATOR_DEPTH_OOK);
+       /* Must clear NFC Target Detection Level reg due to erratum */
+       ret = trf7970a_write(trf, TRF7970A_NFC_TARGET_LEVEL, 0);
        if (ret)
                goto err_out;
 
@@ -774,12 +782,7 @@ static int trf7970a_init(struct trf7970a *trf)
 
        trf->special_fcn_reg1 = 0;
 
-       ret = trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL,
-                       TRF7970A_CHIP_STATUS_RF_ON |
-                               TRF7970A_CHIP_STATUS_VRS5_3);
-       if (ret)
-               goto err_out;
-
+       trf->iso_ctrl = 0xff;
        return 0;
 
 err_out:
@@ -791,53 +794,29 @@ static void trf7970a_switch_rf_off(struct trf7970a *trf)
 {
        dev_dbg(trf->dev, "Switching rf off\n");
 
-       gpio_set_value(trf->en_gpio, 0);
-       gpio_set_value(trf->en2_gpio, 0);
+       trf->chip_status_ctrl &= ~TRF7970A_CHIP_STATUS_RF_ON;
+
+       trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL, trf->chip_status_ctrl);
 
        trf->aborting = false;
        trf->state = TRF7970A_ST_OFF;
+
+       pm_runtime_mark_last_busy(trf->dev);
+       pm_runtime_put_autosuspend(trf->dev);
 }
 
-static int trf7970a_switch_rf_on(struct trf7970a *trf)
+static void trf7970a_switch_rf_on(struct trf7970a *trf)
 {
-       unsigned long delay;
-       int ret;
-
        dev_dbg(trf->dev, "Switching rf on\n");
 
-       if (trf->powering_up)
-               usleep_range(5000, 6000);
-
-       gpio_set_value(trf->en2_gpio, 1);
-       usleep_range(1000, 2000);
-       gpio_set_value(trf->en_gpio, 1);
+       pm_runtime_get_sync(trf->dev);
 
-       /* The delay between enabling the trf7970a and issuing the first
-        * command is significantly longer the very first time after powering
-        * up.  Make sure the longer delay is only done the first time.
-        */
-       if (trf->powering_up) {
-               delay = 20000;
-               trf->powering_up = false;
-       } else {
-               delay = 5000;
-       }
-
-       usleep_range(delay, delay + 1000);
-
-       ret = trf7970a_init(trf);
-       if (ret)
-               trf7970a_switch_rf_off(trf);
-       else
-               trf->state = TRF7970A_ST_IDLE;
-
-       return ret;
+       trf->state = TRF7970A_ST_IDLE;
 }
 
 static int trf7970a_switch_rf(struct nfc_digital_dev *ddev, bool on)
 {
        struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
-       int ret = 0;
 
        dev_dbg(trf->dev, "Switching RF - state: %d, on: %d\n", trf->state, on);
 
@@ -846,7 +825,7 @@ static int trf7970a_switch_rf(struct nfc_digital_dev *ddev, bool on)
        if (on) {
                switch (trf->state) {
                case TRF7970A_ST_OFF:
-                       ret = trf7970a_switch_rf_on(trf);
+                       trf7970a_switch_rf_on(trf);
                        break;
                case TRF7970A_ST_IDLE:
                case TRF7970A_ST_IDLE_RX_BLOCKED:
@@ -871,7 +850,7 @@ static int trf7970a_switch_rf(struct nfc_digital_dev *ddev, bool on)
        }
 
        mutex_unlock(&trf->lock);
-       return ret;
+       return 0;
 }
 
 static int trf7970a_config_rf_tech(struct trf7970a *trf, int tech)
@@ -882,10 +861,24 @@ static int trf7970a_config_rf_tech(struct trf7970a *trf, int tech)
 
        switch (tech) {
        case NFC_DIGITAL_RF_TECH_106A:
-               trf->iso_ctrl = TRF7970A_ISO_CTRL_14443A_106;
+               trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_14443A_106;
+               trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_OOK;
+               break;
+       case NFC_DIGITAL_RF_TECH_106B:
+               trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_14443B_106;
+               trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10;
+               break;
+       case NFC_DIGITAL_RF_TECH_212F:
+               trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_FELICA_212;
+               trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10;
+               break;
+       case NFC_DIGITAL_RF_TECH_424F:
+               trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_FELICA_424;
+               trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10;
                break;
        case NFC_DIGITAL_RF_TECH_ISO15693:
-               trf->iso_ctrl = TRF7970A_ISO_CTRL_15693_SGL_1OF4_2648;
+               trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_15693_SGL_1OF4_2648;
+               trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_OOK;
                break;
        default:
                dev_dbg(trf->dev, "Unsupported rf technology: %d\n", tech);
@@ -899,24 +892,31 @@ static int trf7970a_config_rf_tech(struct trf7970a *trf, int tech)
 
 static int trf7970a_config_framing(struct trf7970a *trf, int framing)
 {
+       u8 iso_ctrl = trf->iso_ctrl_tech;
+       int ret;
+
        dev_dbg(trf->dev, "framing: %d\n", framing);
 
        switch (framing) {
        case NFC_DIGITAL_FRAMING_NFCA_SHORT:
        case NFC_DIGITAL_FRAMING_NFCA_STANDARD:
                trf->tx_cmd = TRF7970A_CMD_TRANSMIT_NO_CRC;
-               trf->iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N;
+               iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N;
                break;
        case NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A:
        case NFC_DIGITAL_FRAMING_NFCA_T4T:
+       case NFC_DIGITAL_FRAMING_NFCB:
+       case NFC_DIGITAL_FRAMING_NFCB_T4T:
+       case NFC_DIGITAL_FRAMING_NFCF:
+       case NFC_DIGITAL_FRAMING_NFCF_T3T:
        case NFC_DIGITAL_FRAMING_ISO15693_INVENTORY:
        case NFC_DIGITAL_FRAMING_ISO15693_T5T:
                trf->tx_cmd = TRF7970A_CMD_TRANSMIT;
-               trf->iso_ctrl &= ~TRF7970A_ISO_CTRL_RX_CRC_N;
+               iso_ctrl &= ~TRF7970A_ISO_CTRL_RX_CRC_N;
                break;
        case NFC_DIGITAL_FRAMING_NFCA_T2T:
                trf->tx_cmd = TRF7970A_CMD_TRANSMIT;
-               trf->iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N;
+               iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N;
                break;
        default:
                dev_dbg(trf->dev, "Unsupported Framing: %d\n", framing);
@@ -925,24 +925,46 @@ static int trf7970a_config_framing(struct trf7970a *trf, int framing)
 
        trf->framing = framing;
 
-       return trf7970a_write(trf, TRF7970A_ISO_CTRL, trf->iso_ctrl);
+       if (iso_ctrl != trf->iso_ctrl) {
+               ret = trf7970a_write(trf, TRF7970A_ISO_CTRL, iso_ctrl);
+               if (ret)
+                       return ret;
+
+               trf->iso_ctrl = iso_ctrl;
+
+               ret = trf7970a_write(trf, TRF7970A_MODULATOR_SYS_CLK_CTRL,
+                               trf->modulator_sys_clk_ctrl);
+               if (ret)
+                       return ret;
+       }
+
+       if (!(trf->chip_status_ctrl & TRF7970A_CHIP_STATUS_RF_ON)) {
+               ret = trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL,
+                               trf->chip_status_ctrl |
+                                       TRF7970A_CHIP_STATUS_RF_ON);
+               if (ret)
+                       return ret;
+
+               trf->chip_status_ctrl |= TRF7970A_CHIP_STATUS_RF_ON;
+
+               usleep_range(5000, 6000);
+       }
+
+       return 0;
 }
 
 static int trf7970a_in_configure_hw(struct nfc_digital_dev *ddev, int type,
                int param)
 {
        struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
-       int ret = 0;
+       int ret;
 
        dev_dbg(trf->dev, "Configure hw - type: %d, param: %d\n", type, param);
 
        mutex_lock(&trf->lock);
 
-       if (trf->state == TRF7970A_ST_OFF) {
-               ret = trf7970a_switch_rf_on(trf);
-               if (ret)
-                       goto err_out;
-       }
+       if (trf->state == TRF7970A_ST_OFF)
+               trf7970a_switch_rf_on(trf);
 
        switch (type) {
        case NFC_DIGITAL_CONFIG_RF_TECH:
@@ -956,7 +978,6 @@ static int trf7970a_in_configure_hw(struct nfc_digital_dev *ddev, int type,
                ret = -EINVAL;
        }
 
-err_out:
        mutex_unlock(&trf->lock);
        return ret;
 }
@@ -1191,7 +1212,18 @@ static void trf7970a_abort_cmd(struct nfc_digital_dev *ddev)
        dev_dbg(trf->dev, "Abort process initiated\n");
 
        mutex_lock(&trf->lock);
-       trf->aborting = true;
+
+       switch (trf->state) {
+       case TRF7970A_ST_WAIT_FOR_TX_FIFO:
+       case TRF7970A_ST_WAIT_FOR_RX_DATA:
+       case TRF7970A_ST_WAIT_FOR_RX_DATA_CONT:
+       case TRF7970A_ST_WAIT_TO_ISSUE_EOF:
+               trf->aborting = true;
+               break;
+       default:
+               break;
+       }
+
        mutex_unlock(&trf->lock);
 }
 
@@ -1206,12 +1238,25 @@ static struct nfc_digital_ops trf7970a_nfc_ops = {
        .abort_cmd              = trf7970a_abort_cmd,
 };
 
+static int trf7970a_get_autosuspend_delay(struct device_node *np)
+{
+       int autosuspend_delay, ret;
+
+       ret = of_property_read_u32(np, "autosuspend-delay", &autosuspend_delay);
+       if (ret)
+               autosuspend_delay = TRF7970A_AUTOSUSPEND_DELAY;
+
+       of_node_put(np);
+
+       return autosuspend_delay;
+}
+
 static int trf7970a_probe(struct spi_device *spi)
 {
        struct device_node *np = spi->dev.of_node;
        const struct spi_device_id *id = spi_get_device_id(spi);
        struct trf7970a *trf;
-       int ret;
+       int uvolts, autosuspend_delay, ret;
 
        if (!np) {
                dev_err(&spi->dev, "No Device Tree entry\n");
@@ -1281,7 +1326,10 @@ static int trf7970a_probe(struct spi_device *spi)
                goto err_destroy_lock;
        }
 
-       trf->powering_up = true;
+       uvolts = regulator_get_voltage(trf->regulator);
+
+       if (uvolts > 4000000)
+               trf->chip_status_ctrl = TRF7970A_CHIP_STATUS_VRS5_3;
 
        trf->ddev = nfc_digital_allocate_device(&trf7970a_nfc_ops,
                        TRF7970A_SUPPORTED_PROTOCOLS,
@@ -1297,6 +1345,12 @@ static int trf7970a_probe(struct spi_device *spi)
        nfc_digital_set_drvdata(trf->ddev, trf);
        spi_set_drvdata(spi, trf);
 
+       autosuspend_delay = trf7970a_get_autosuspend_delay(np);
+
+       pm_runtime_set_autosuspend_delay(trf->dev, autosuspend_delay);
+       pm_runtime_use_autosuspend(trf->dev);
+       pm_runtime_enable(trf->dev);
+
        ret = nfc_digital_register_device(trf->ddev);
        if (ret) {
                dev_err(trf->dev, "Can't register NFC digital device: %d\n",
@@ -1307,6 +1361,7 @@ static int trf7970a_probe(struct spi_device *spi)
        return 0;
 
 err_free_ddev:
+       pm_runtime_disable(trf->dev);
        nfc_digital_free_device(trf->ddev);
 err_disable_regulator:
        regulator_disable(trf->regulator);
@@ -1321,15 +1376,16 @@ static int trf7970a_remove(struct spi_device *spi)
 
        mutex_lock(&trf->lock);
 
-       trf7970a_switch_rf_off(trf);
-       trf7970a_init(trf);
-
        switch (trf->state) {
        case TRF7970A_ST_WAIT_FOR_TX_FIFO:
        case TRF7970A_ST_WAIT_FOR_RX_DATA:
        case TRF7970A_ST_WAIT_FOR_RX_DATA_CONT:
        case TRF7970A_ST_WAIT_TO_ISSUE_EOF:
                trf7970a_send_err_upstream(trf, -ECANCELED);
+               /* FALLTHROUGH */
+       case TRF7970A_ST_IDLE:
+       case TRF7970A_ST_IDLE_RX_BLOCKED:
+               pm_runtime_put_sync(trf->dev);
                break;
        default:
                break;
@@ -1337,6 +1393,8 @@ static int trf7970a_remove(struct spi_device *spi)
 
        mutex_unlock(&trf->lock);
 
+       pm_runtime_disable(trf->dev);
+
        nfc_digital_unregister_device(trf->ddev);
        nfc_digital_free_device(trf->ddev);
 
@@ -1347,6 +1405,70 @@ static int trf7970a_remove(struct spi_device *spi)
        return 0;
 }
 
+#ifdef CONFIG_PM_RUNTIME
+static int trf7970a_pm_runtime_suspend(struct device *dev)
+{
+       struct spi_device *spi = container_of(dev, struct spi_device, dev);
+       struct trf7970a *trf = spi_get_drvdata(spi);
+       int ret;
+
+       dev_dbg(dev, "Runtime suspend\n");
+
+       if (trf->state != TRF7970A_ST_OFF) {
+               dev_dbg(dev, "Can't suspend - not in OFF state (%d)\n",
+                               trf->state);
+               return -EBUSY;
+       }
+
+       gpio_set_value(trf->en_gpio, 0);
+       gpio_set_value(trf->en2_gpio, 0);
+
+       ret = regulator_disable(trf->regulator);
+       if (ret)
+               dev_err(dev, "%s - Can't disable VIN: %d\n", __func__, ret);
+
+       return ret;
+}
+
+static int trf7970a_pm_runtime_resume(struct device *dev)
+{
+       struct spi_device *spi = container_of(dev, struct spi_device, dev);
+       struct trf7970a *trf = spi_get_drvdata(spi);
+       int ret;
+
+       dev_dbg(dev, "Runtime resume\n");
+
+       ret = regulator_enable(trf->regulator);
+       if (ret) {
+               dev_err(dev, "%s - Can't enable VIN: %d\n", __func__, ret);
+               return ret;
+       }
+
+       usleep_range(5000, 6000);
+
+       gpio_set_value(trf->en2_gpio, 1);
+       usleep_range(1000, 2000);
+       gpio_set_value(trf->en_gpio, 1);
+
+       usleep_range(20000, 21000);
+
+       ret = trf7970a_init(trf);
+       if (ret) {
+               dev_err(dev, "%s - Can't initialize: %d\n", __func__, ret);
+               return ret;
+       }
+
+       pm_runtime_mark_last_busy(dev);
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops trf7970a_pm_ops = {
+       SET_RUNTIME_PM_OPS(trf7970a_pm_runtime_suspend,
+                       trf7970a_pm_runtime_resume, NULL)
+};
+
 static const struct spi_device_id trf7970a_id_table[] = {
        { "trf7970a", TRF7970A_QUIRK_IRQ_STATUS_READ_ERRATA },
        { }
@@ -1360,6 +1482,7 @@ static struct spi_driver trf7970a_spi_driver = {
        .driver         = {
                .name   = "trf7970a",
                .owner  = THIS_MODULE,
+               .pm     = &trf7970a_pm_ops,
        },
 };
 
index 9a95831bd065c2ba1c5af83f6a73927a3b9d8181..fb4a5983064824cad8338cad9327121a615566b4 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/netdevice.h>
 #include <linux/err.h>
 #include <linux/phy.h>
+#include <linux/phy_fixed.h>
 #include <linux/of.h>
 #include <linux/of_irq.h>
 #include <linux/of_mdio.h>
 MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
 MODULE_LICENSE("GPL");
 
-static void of_set_phy_supported(struct phy_device *phydev, u32 max_speed)
-{
-       /* The default values for phydev->supported are provided by the PHY
-        * driver "features" member, we want to reset to sane defaults fist
-        * before supporting higher speeds.
-        */
-       phydev->supported &= PHY_DEFAULT_FEATURES;
-
-       switch (max_speed) {
-       default:
-               return;
-
-       case SPEED_1000:
-               phydev->supported |= PHY_1000BT_FEATURES;
-       case SPEED_100:
-               phydev->supported |= PHY_100BT_FEATURES;
-       case SPEED_10:
-               phydev->supported |= PHY_10BT_FEATURES;
-       }
-}
-
 /* Extract the clause 22 phy ID from the compatible string of the form
  * ethernet-phy-idAAAA.BBBB */
 static int of_get_phy_id(struct device_node *device, u32 *phy_id)
@@ -66,7 +46,6 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi
        struct phy_device *phy;
        bool is_c45;
        int rc;
-       u32 max_speed = 0;
        u32 phy_id;
 
        is_c45 = of_device_is_compatible(child,
@@ -103,17 +82,33 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi
                return 1;
        }
 
-       /* Set phydev->supported based on the "max-speed" property
-        * if present */
-       if (!of_property_read_u32(child, "max-speed", &max_speed))
-               of_set_phy_supported(phy, max_speed);
-
        dev_dbg(&mdio->dev, "registered phy %s at address %i\n",
                child->name, addr);
 
        return 0;
 }
 
+static int of_mdio_parse_addr(struct device *dev, const struct device_node *np)
+{
+       u32 addr;
+       int ret;
+
+       ret = of_property_read_u32(np, "reg", &addr);
+       if (ret < 0) {
+               dev_err(dev, "%s has invalid PHY address\n", np->full_name);
+               return ret;
+       }
+
+       /* A PHY must have a reg property in the range [0-31] */
+       if (addr >= PHY_MAX_ADDR) {
+               dev_err(dev, "%s PHY address %i is too large\n",
+                       np->full_name, addr);
+               return -EINVAL;
+       }
+
+       return addr;
+}
+
 /**
  * of_mdiobus_register - Register mii_bus and create PHYs from the device tree
  * @mdio: pointer to mii_bus structure
@@ -126,9 +121,8 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
 {
        struct device_node *child;
        const __be32 *paddr;
-       u32 addr;
        bool scanphys = false;
-       int rc, i, len;
+       int addr, rc, i;
 
        /* Mask out all PHYs from auto probing.  Instead the PHYs listed in
         * the device tree are populated after the bus has been registered */
@@ -148,19 +142,9 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
 
        /* Loop over the child nodes and register a phy_device for each one */
        for_each_available_child_of_node(np, child) {
-               /* A PHY must have a reg property in the range [0-31] */
-               paddr = of_get_property(child, "reg", &len);
-               if (!paddr || len < sizeof(*paddr)) {
+               addr = of_mdio_parse_addr(&mdio->dev, child);
+               if (addr < 0) {
                        scanphys = true;
-                       dev_err(&mdio->dev, "%s has invalid PHY address\n",
-                               child->full_name);
-                       continue;
-               }
-
-               addr = be32_to_cpup(paddr);
-               if (addr >= PHY_MAX_ADDR) {
-                       dev_err(&mdio->dev, "%s PHY address %i is too large\n",
-                               child->full_name, addr);
                        continue;
                }
 
@@ -175,7 +159,7 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
        /* auto scan for PHYs with empty reg property */
        for_each_available_child_of_node(np, child) {
                /* Skip PHYs with reg property set */
-               paddr = of_get_property(child, "reg", &len);
+               paddr = of_get_property(child, "reg", NULL);
                if (paddr)
                        continue;
 
@@ -198,6 +182,40 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
 }
 EXPORT_SYMBOL(of_mdiobus_register);
 
+/**
+ * of_mdiobus_link_phydev - Find a device node for a phy
+ * @mdio: pointer to mii_bus structure
+ * @phydev: phydev for which the of_node pointer should be set
+ *
+ * Walk the list of subnodes of a mdio bus and look for a node that matches the
+ * phy's address with its 'reg' property. If found, set the of_node pointer for
+ * the phy. This allows auto-probed pyh devices to be supplied with information
+ * passed in via DT.
+ */
+void of_mdiobus_link_phydev(struct mii_bus *mdio,
+                           struct phy_device *phydev)
+{
+       struct device *dev = &phydev->dev;
+       struct device_node *child;
+
+       if (dev->of_node || !mdio->dev.of_node)
+               return;
+
+       for_each_available_child_of_node(mdio->dev.of_node, child) {
+               int addr;
+
+               addr = of_mdio_parse_addr(&mdio->dev, child);
+               if (addr < 0)
+                       continue;
+
+               if (addr == phydev->addr) {
+                       dev->of_node = child;
+                       return;
+               }
+       }
+}
+EXPORT_SYMBOL(of_mdiobus_link_phydev);
+
 /* Helper function for of_phy_find_device */
 static int of_phy_match(struct device *dev, void *phy_np)
 {
@@ -244,44 +262,6 @@ struct phy_device *of_phy_connect(struct net_device *dev,
 }
 EXPORT_SYMBOL(of_phy_connect);
 
-/**
- * of_phy_connect_fixed_link - Parse fixed-link property and return a dummy phy
- * @dev: pointer to net_device claiming the phy
- * @hndlr: Link state callback for the network device
- * @iface: PHY data interface type
- *
- * This function is a temporary stop-gap and will be removed soon.  It is
- * only to support the fs_enet, ucc_geth and gianfar Ethernet drivers.  Do
- * not call this function from new drivers.
- */
-struct phy_device *of_phy_connect_fixed_link(struct net_device *dev,
-                                            void (*hndlr)(struct net_device *),
-                                            phy_interface_t iface)
-{
-       struct device_node *net_np;
-       char bus_id[MII_BUS_ID_SIZE + 3];
-       struct phy_device *phy;
-       const __be32 *phy_id;
-       int sz;
-
-       if (!dev->dev.parent)
-               return NULL;
-
-       net_np = dev->dev.parent->of_node;
-       if (!net_np)
-               return NULL;
-
-       phy_id = of_get_property(net_np, "fixed-link", &sz);
-       if (!phy_id || sz < sizeof(*phy_id))
-               return NULL;
-
-       sprintf(bus_id, PHY_ID_FMT, "fixed-0", be32_to_cpu(phy_id[0]));
-
-       phy = phy_connect(dev, bus_id, hndlr, iface);
-       return IS_ERR(phy) ? NULL : phy;
-}
-EXPORT_SYMBOL(of_phy_connect_fixed_link);
-
 /**
  * of_phy_attach - Attach to a PHY without starting the state machine
  * @dev: pointer to net_device claiming the phy
@@ -301,3 +281,69 @@ struct phy_device *of_phy_attach(struct net_device *dev,
        return phy_attach_direct(dev, phy, flags, iface) ? NULL : phy;
 }
 EXPORT_SYMBOL(of_phy_attach);
+
+#if defined(CONFIG_FIXED_PHY)
+/*
+ * of_phy_is_fixed_link() and of_phy_register_fixed_link() must
+ * support two DT bindings:
+ * - the old DT binding, where 'fixed-link' was a property with 5
+ *   cells encoding various informations about the fixed PHY
+ * - the new DT binding, where 'fixed-link' is a sub-node of the
+ *   Ethernet device.
+ */
+bool of_phy_is_fixed_link(struct device_node *np)
+{
+       struct device_node *dn;
+       int len;
+
+       /* New binding */
+       dn = of_get_child_by_name(np, "fixed-link");
+       if (dn) {
+               of_node_put(dn);
+               return true;
+       }
+
+       /* Old binding */
+       if (of_get_property(np, "fixed-link", &len) &&
+           len == (5 * sizeof(__be32)))
+               return true;
+
+       return false;
+}
+EXPORT_SYMBOL(of_phy_is_fixed_link);
+
+int of_phy_register_fixed_link(struct device_node *np)
+{
+       struct fixed_phy_status status = {};
+       struct device_node *fixed_link_node;
+       const __be32 *fixed_link_prop;
+       int len;
+
+       /* New binding */
+       fixed_link_node = of_get_child_by_name(np, "fixed-link");
+       if (fixed_link_node) {
+               status.link = 1;
+               status.duplex = of_property_read_bool(np, "full-duplex");
+               if (of_property_read_u32(fixed_link_node, "speed", &status.speed))
+                       return -EINVAL;
+               status.pause = of_property_read_bool(np, "pause");
+               status.asym_pause = of_property_read_bool(np, "asym-pause");
+               of_node_put(fixed_link_node);
+               return fixed_phy_register(PHY_POLL, &status, np);
+       }
+
+       /* Old binding */
+       fixed_link_prop = of_get_property(np, "fixed-link", &len);
+       if (fixed_link_prop && len == (5 * sizeof(__be32))) {
+               status.link = 1;
+               status.duplex = be32_to_cpu(fixed_link_prop[1]);
+               status.speed = be32_to_cpu(fixed_link_prop[2]);
+               status.pause = be32_to_cpu(fixed_link_prop[3]);
+               status.asym_pause = be32_to_cpu(fixed_link_prop[4]);
+               return fixed_phy_register(PHY_POLL, &status, np);
+       }
+
+       return -ENODEV;
+}
+EXPORT_SYMBOL(of_phy_register_fixed_link);
+#endif
index 8c148f39e8d76870cc5f8fc1b6be71c4511a3480..d292d7cb3417062643379805a969f5d29718de2f 100644 (file)
@@ -231,10 +231,7 @@ static int pci_vpd_pci22_wait(struct pci_dev *dev)
                }
 
                if (time_after(jiffies, timeout)) {
-                       dev_printk(KERN_DEBUG, &dev->dev,
-                                  "vpd r/w failed.  This is likely a firmware "
-                                  "bug on this device.  Contact the card "
-                                  "vendor for a firmware update.");
+                       dev_printk(KERN_DEBUG, &dev->dev, "vpd r/w failed.  This is likely a firmware bug on this device.  Contact the card vendor for a firmware update\n");
                        return -ETIMEDOUT;
                }
                if (fatal_signal_pending(current))
index 447d393725e1fef6c5f1a21d7ebd3790b176e59e..73aef51a28f0760fefa6b4344235e7f341bebb3d 100644 (file)
@@ -226,6 +226,7 @@ int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
                                         type_mask, alignf, alignf_data,
                                         &pci_32_bit);
 }
+EXPORT_SYMBOL(pci_bus_alloc_resource);
 
 void __weak pcibios_resource_survey_bus(struct pci_bus *bus) { }
 
@@ -253,6 +254,7 @@ void pci_bus_add_device(struct pci_dev *dev)
 
        dev->is_added = 1;
 }
+EXPORT_SYMBOL_GPL(pci_bus_add_device);
 
 /**
  * pci_bus_add_devices - start driver for PCI devices
@@ -279,6 +281,7 @@ void pci_bus_add_devices(const struct pci_bus *bus)
                        pci_bus_add_devices(child);
        }
 }
+EXPORT_SYMBOL(pci_bus_add_devices);
 
 /** pci_walk_bus - walk devices on/under bus, calling callback.
  *  @top      bus whose devices should be walked
@@ -344,6 +347,3 @@ void pci_bus_put(struct pci_bus *bus)
 }
 EXPORT_SYMBOL(pci_bus_put);
 
-EXPORT_SYMBOL(pci_bus_alloc_resource);
-EXPORT_SYMBOL_GPL(pci_bus_add_device);
-EXPORT_SYMBOL(pci_bus_add_devices);
index 1632661c5b7f693dfe41a4157feba9672f9500c2..c5d0ca3845028d65e1476ea2e279a70beb6a7049 100644 (file)
@@ -545,7 +545,6 @@ static int __init add_pcie_port(struct pcie_port *pp,
        pp->root_bus_nr = -1;
        pp->ops = &exynos_pcie_host_ops;
 
-       spin_lock_init(&pp->conf_lock);
        ret = dw_pcie_host_init(pp);
        if (ret) {
                dev_err(&pdev->dev, "failed to initialize host\n");
index a5645ae4aef032592d1923e542e3d9b2241e9a8c..a568efaa331c57cc35144b08fe3f3b386118c926 100644 (file)
@@ -507,7 +507,6 @@ static int __init imx6_add_pcie_port(struct pcie_port *pp,
        pp->root_bus_nr = -1;
        pp->ops = &imx6_pcie_host_ops;
 
-       spin_lock_init(&pp->conf_lock);
        ret = dw_pcie_host_init(pp);
        if (ret) {
                dev_err(&pdev->dev, "failed to initialize host\n");
index e384e2534594731a95eb7524fb75583fce208541..ce23e0f076b6663297ca403508b2e5244b639102 100644 (file)
@@ -113,7 +113,6 @@ struct mvebu_pcie {
 struct mvebu_pcie_port {
        char *name;
        void __iomem *base;
-       spinlock_t conf_lock;
        u32 port;
        u32 lane;
        int devfn;
@@ -329,9 +328,11 @@ static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port,
                ret = mvebu_mbus_add_window_remap_by_id(target, attribute, base,
                                                        sz, remap);
                if (ret) {
+                       phys_addr_t end = base + sz - 1;
+
                        dev_err(&port->pcie->pdev->dev,
-                               "Could not create MBus window at 0x%x, size 0x%x: %d\n",
-                               base, sz, ret);
+                               "Could not create MBus window at [mem %pa-%pa]: %d\n",
+                               &base, &end, ret);
                        mvebu_pcie_del_windows(port, base - size_mapped,
                                               size_mapped);
                        return;
@@ -613,9 +614,9 @@ static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys)
        return sys->private_data;
 }
 
-static struct mvebu_pcie_port *
-mvebu_pcie_find_port(struct mvebu_pcie *pcie, struct pci_bus *bus,
-                    int devfn)
+static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,
+                                                   struct pci_bus *bus,
+                                                   int devfn)
 {
        int i;
 
@@ -638,7 +639,6 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
 {
        struct mvebu_pcie *pcie = sys_to_pcie(bus->sysdata);
        struct mvebu_pcie_port *port;
-       unsigned long flags;
        int ret;
 
        port = mvebu_pcie_find_port(pcie, bus, devfn);
@@ -664,10 +664,8 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
                return PCIBIOS_DEVICE_NOT_FOUND;
 
        /* Access the real PCIe interface */
-       spin_lock_irqsave(&port->conf_lock, flags);
        ret = mvebu_pcie_hw_wr_conf(port, bus, devfn,
                                    where, size, val);
-       spin_unlock_irqrestore(&port->conf_lock, flags);
 
        return ret;
 }
@@ -678,7 +676,6 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
 {
        struct mvebu_pcie *pcie = sys_to_pcie(bus->sysdata);
        struct mvebu_pcie_port *port;
-       unsigned long flags;
        int ret;
 
        port = mvebu_pcie_find_port(pcie, bus, devfn);
@@ -710,10 +707,8 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
        }
 
        /* Access the real PCIe interface */
-       spin_lock_irqsave(&port->conf_lock, flags);
        ret = mvebu_pcie_hw_rd_conf(port, bus, devfn,
                                    where, size, val);
-       spin_unlock_irqrestore(&port->conf_lock, flags);
 
        return ret;
 }
@@ -786,10 +781,10 @@ static void mvebu_pcie_add_bus(struct pci_bus *bus)
 }
 
 static resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev,
-                                               const struct resource *res,
-                                               resource_size_t start,
-                                               resource_size_t size,
-                                               resource_size_t align)
+                                                const struct resource *res,
+                                                resource_size_t start,
+                                                resource_size_t size,
+                                                resource_size_t align)
 {
        if (dev->bus->number != 0)
                return start;
@@ -839,7 +834,8 @@ static void mvebu_pcie_enable(struct mvebu_pcie *pcie)
  * found, maps it.
  */
 static void __iomem *mvebu_pcie_map_registers(struct platform_device *pdev,
-                     struct device_node *np, struct mvebu_pcie_port *port)
+                                             struct device_node *np,
+                                             struct mvebu_pcie_port *port)
 {
        struct resource regs;
        int ret = 0;
@@ -1060,7 +1056,6 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
                mvebu_pcie_set_local_dev_nr(port, 1);
 
                port->dn = child;
-               spin_lock_init(&port->conf_lock);
                mvebu_sw_pci_bridge_init(port);
                i++;
        }
index e3bf9e6d5d9ad975f8a8ae377efbe28b5813b691..1eaf4df3618a18a57e0eee175272d5a19646f2bf 100644 (file)
@@ -643,7 +643,6 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
                        int size, u32 *val)
 {
        struct pcie_port *pp = sys_to_pcie(bus->sysdata);
-       unsigned long flags;
        int ret;
 
        if (!pp) {
@@ -656,13 +655,11 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
                return PCIBIOS_DEVICE_NOT_FOUND;
        }
 
-       spin_lock_irqsave(&pp->conf_lock, flags);
        if (bus->number != pp->root_bus_nr)
                ret = dw_pcie_rd_other_conf(pp, bus, devfn,
                                                where, size, val);
        else
                ret = dw_pcie_rd_own_conf(pp, where, size, val);
-       spin_unlock_irqrestore(&pp->conf_lock, flags);
 
        return ret;
 }
@@ -671,7 +668,6 @@ static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
                        int where, int size, u32 val)
 {
        struct pcie_port *pp = sys_to_pcie(bus->sysdata);
-       unsigned long flags;
        int ret;
 
        if (!pp) {
@@ -682,13 +678,11 @@ static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
        if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
                return PCIBIOS_DEVICE_NOT_FOUND;
 
-       spin_lock_irqsave(&pp->conf_lock, flags);
        if (bus->number != pp->root_bus_nr)
                ret = dw_pcie_wr_other_conf(pp, bus, devfn,
                                                where, size, val);
        else
                ret = dw_pcie_wr_own_conf(pp, where, size, val);
-       spin_unlock_irqrestore(&pp->conf_lock, flags);
 
        return ret;
 }
index a169d22d517e027fc629545201184e4096b5d90c..77f592faa7bf28ee6d86f84cad6ee7cdfd345158 100644 (file)
@@ -41,7 +41,6 @@ struct pcie_port {
        void __iomem            *va_cfg1_base;
        u64                     io_base;
        u64                     mem_base;
-       spinlock_t              conf_lock;
        struct resource         cfg;
        struct resource         io;
        struct resource         mem;
index 8e06124aa80fb99903352cb32e2622971631986f..f7d3de32c9a0314f8fee3112848aa24f64aa7de8 100644 (file)
@@ -277,9 +277,8 @@ static int rcar_pcie_read_conf(struct pci_bus *bus, unsigned int devfn,
        else if (size == 2)
                *val = (*val >> (8 * (where & 2))) & 0xffff;
 
-       dev_dbg(&bus->dev, "pcie-config-read: bus=%3d devfn=0x%04x "
-               "where=0x%04x size=%d val=0x%08lx\n", bus->number,
-               devfn, where, size, (unsigned long)*val);
+       dev_dbg(&bus->dev, "pcie-config-read: bus=%3d devfn=0x%04x where=0x%04x size=%d val=0x%08lx\n",
+               bus->number, devfn, where, size, (unsigned long)*val);
 
        return ret;
 }
@@ -302,9 +301,8 @@ static int rcar_pcie_write_conf(struct pci_bus *bus, unsigned int devfn,
        if (ret != PCIBIOS_SUCCESSFUL)
                return ret;
 
-       dev_dbg(&bus->dev, "pcie-config-write: bus=%3d devfn=0x%04x "
-               "where=0x%04x size=%d val=0x%08lx\n", bus->number,
-               devfn, where, size, (unsigned long)val);
+       dev_dbg(&bus->dev, "pcie-config-write: bus=%3d devfn=0x%04x where=0x%04x size=%d val=0x%08lx\n",
+               bus->number, devfn, where, size, (unsigned long)val);
 
        if (size == 1) {
                shift = 8 * (where & 3);
index 2b859249303b8088352995bc23b57c47f7c36472..b0e61bf261a7cd8ef15e6293fd1f1acbe871482a 100644 (file)
@@ -142,6 +142,16 @@ static inline acpi_handle func_to_handle(struct acpiphp_func *func)
        return func_to_acpi_device(func)->handle;
 }
 
+struct acpiphp_root_context {
+       struct acpi_hotplug_context hp;
+       struct acpiphp_bridge *root_bridge;
+};
+
+static inline struct acpiphp_root_context *to_acpiphp_root_context(struct acpi_hotplug_context *hp)
+{
+       return container_of(hp, struct acpiphp_root_context, hp);
+}
+
 /*
  * struct acpiphp_attention_info - device specific attention registration
  *
index 728c31f4c2c50399593bee71ee6a4c277df849b6..e291efcd02a2d4c969161989162302d675ae3ecf 100644 (file)
@@ -63,10 +63,6 @@ MODULE_LICENSE("GPL");
 MODULE_PARM_DESC(disable, "disable acpiphp driver");
 module_param_named(disable, acpiphp_disabled, bool, 0444);
 
-/* export the attention callback registration methods */
-EXPORT_SYMBOL_GPL(acpiphp_register_attention);
-EXPORT_SYMBOL_GPL(acpiphp_unregister_attention);
-
 static int enable_slot         (struct hotplug_slot *slot);
 static int disable_slot                (struct hotplug_slot *slot);
 static int set_attention_status (struct hotplug_slot *slot, u8 value);
@@ -104,6 +100,7 @@ int acpiphp_register_attention(struct acpiphp_attention_info *info)
        }
        return retval;
 }
+EXPORT_SYMBOL_GPL(acpiphp_register_attention);
 
 
 /**
@@ -124,6 +121,7 @@ int acpiphp_unregister_attention(struct acpiphp_attention_info *info)
        }
        return retval;
 }
+EXPORT_SYMBOL_GPL(acpiphp_unregister_attention);
 
 
 /**
index 75e178330215147d9ec30037ef7d785eb4dce217..602d153c7055b0227416c2d960a1efe4688aa847 100644 (file)
@@ -351,11 +351,9 @@ static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data,
                        slot->slot = NULL;
                        bridge->nr_slots--;
                        if (retval == -EBUSY)
-                               pr_warn("Slot %llu already registered by another "
-                                       "hotplug driver\n", sun);
+                               pr_warn("Slot %llu already registered by another hotplug driver\n", sun);
                        else
-                               pr_warn("acpiphp_register_hotplug_slot failed "
-                                       "(err code = 0x%x)\n", retval);
+                               pr_warn("acpiphp_register_hotplug_slot failed (err code = 0x%x)\n", retval);
                }
                /* Even if the slot registration fails, we can still use it. */
        }
@@ -373,17 +371,13 @@ static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data,
 
 static struct acpiphp_bridge *acpiphp_dev_to_bridge(struct acpi_device *adev)
 {
-       struct acpiphp_context *context;
        struct acpiphp_bridge *bridge = NULL;
 
        acpi_lock_hp_context();
-       context = acpiphp_get_context(adev);
-       if (context) {
-               bridge = context->bridge;
+       if (adev->hp) {
+               bridge = to_acpiphp_root_context(adev->hp)->root_bridge;
                if (bridge)
                        get_bridge(bridge);
-
-               acpiphp_put_context(context);
        }
        acpi_unlock_hp_context();
        return bridge;
@@ -881,7 +875,17 @@ void acpiphp_enumerate_slots(struct pci_bus *bus)
         */
        get_device(&bus->dev);
 
-       if (!pci_is_root_bus(bridge->pci_bus)) {
+       acpi_lock_hp_context();
+       if (pci_is_root_bus(bridge->pci_bus)) {
+               struct acpiphp_root_context *root_context;
+
+               root_context = kzalloc(sizeof(*root_context), GFP_KERNEL);
+               if (!root_context)
+                       goto err;
+
+               root_context->root_bridge = bridge;
+               acpi_set_hp_context(adev, &root_context->hp, NULL, NULL, NULL);
+       } else {
                struct acpiphp_context *context;
 
                /*
@@ -890,21 +894,16 @@ void acpiphp_enumerate_slots(struct pci_bus *bus)
                 * parent is going to be handled by pciehp, in which case this
                 * bridge is not interesting to us either.
                 */
-               acpi_lock_hp_context();
                context = acpiphp_get_context(adev);
-               if (!context) {
-                       acpi_unlock_hp_context();
-                       put_device(&bus->dev);
-                       pci_dev_put(bridge->pci_dev);
-                       kfree(bridge);
-                       return;
-               }
+               if (!context)
+                       goto err;
+
                bridge->context = context;
                context->bridge = bridge;
                /* Get a reference to the parent bridge. */
                get_bridge(context->func.parent);
-               acpi_unlock_hp_context();
        }
+       acpi_unlock_hp_context();
 
        /* Must be added to the list prior to calling acpiphp_add_context(). */
        mutex_lock(&bridge_mutex);
@@ -919,6 +918,30 @@ void acpiphp_enumerate_slots(struct pci_bus *bus)
                cleanup_bridge(bridge);
                put_bridge(bridge);
        }
+       return;
+
+ err:
+       acpi_unlock_hp_context();
+       put_device(&bus->dev);
+       pci_dev_put(bridge->pci_dev);
+       kfree(bridge);
+}
+
+void acpiphp_drop_bridge(struct acpiphp_bridge *bridge)
+{
+       if (pci_is_root_bus(bridge->pci_bus)) {
+               struct acpiphp_root_context *root_context;
+               struct acpi_device *adev;
+
+               acpi_lock_hp_context();
+               adev = ACPI_COMPANION(bridge->pci_bus->bridge);
+               root_context = to_acpiphp_root_context(adev->hp);
+               adev->hp = NULL;
+               acpi_unlock_hp_context();
+               kfree(root_context);
+       }
+       cleanup_bridge(bridge);
+       put_bridge(bridge);
 }
 
 /**
@@ -936,8 +959,7 @@ void acpiphp_remove_slots(struct pci_bus *bus)
        list_for_each_entry(bridge, &bridge_list, list)
                if (bridge->pci_bus == bus) {
                        mutex_unlock(&bridge_mutex);
-                       cleanup_bridge(bridge);
-                       put_bridge(bridge);
+                       acpiphp_drop_bridge(bridge);
                        return;
                }
 
index 1356211431d0d464406be033993af91d3b87362a..6a0ddf757349d70f267e44030324aca3f2e821d1 100644 (file)
@@ -56,9 +56,9 @@ struct cpci_hp_controller_ops {
        int (*enable_irq) (void);
        int (*disable_irq) (void);
        int (*check_irq) (void *dev_id);
-       int (*hardware_test) (struct slotslot, u32 value);
-       u8  (*get_power) (struct slotslot);
-       int (*set_power) (struct slotslot, int value);
+       int (*hardware_test) (struct slot *slot, u32 value);
+       u8  (*get_power) (struct slot *slot);
+       int (*set_power) (struct slot *slot, int value);
 };
 
 struct cpci_hp_controller {
@@ -89,13 +89,13 @@ int cpci_hp_stop(void);
 u8 cpci_get_attention_status(struct slot *slot);
 u8 cpci_get_latch_status(struct slot *slot);
 u8 cpci_get_adapter_status(struct slot *slot);
-u16 cpci_get_hs_csr(struct slot * slot);
+u16 cpci_get_hs_csr(struct slot *slot);
 int cpci_set_attention_status(struct slot *slot, int status);
-int cpci_check_and_clear_ins(struct slot * slot);
-int cpci_check_ext(struct slot * slot);
-int cpci_clear_ext(struct slot * slot);
-int cpci_led_on(struct slot * slot);
-int cpci_led_off(struct slot * slot);
+int cpci_check_and_clear_ins(struct slot *slot);
+int cpci_check_ext(struct slot *slot);
+int cpci_clear_ext(struct slot *slot);
+int cpci_led_on(struct slot *slot);
+int cpci_led_off(struct slot *slot);
 int cpci_configure_slot(struct slot *slot);
 int cpci_unconfigure_slot(struct slot *slot);
 
index 00c81a3cefc9be63e6bf47c5af9bdda0eefde37f..e09cf7827d68a3cf9b8f1755cd11a209e0f473f8 100644 (file)
@@ -65,10 +65,10 @@ static int thread_finished;
 static int enable_slot(struct hotplug_slot *slot);
 static int disable_slot(struct hotplug_slot *slot);
 static int set_attention_status(struct hotplug_slot *slot, u8 value);
-static int get_power_status(struct hotplug_slot *slot, u8 * value);
-static int get_attention_status(struct hotplug_slot *slot, u8 * value);
-static int get_adapter_status(struct hotplug_slot *slot, u8 * value);
-static int get_latch_status(struct hotplug_slot *slot, u8 * value);
+static int get_power_status(struct hotplug_slot *slot, u8 *value);
+static int get_attention_status(struct hotplug_slot *slot, u8 *value);
+static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
+static int get_latch_status(struct hotplug_slot *slot, u8 *value);
 
 static struct hotplug_slot_ops cpci_hotplug_slot_ops = {
        .enable_slot = enable_slot,
@@ -168,7 +168,7 @@ cpci_get_power_status(struct slot *slot)
 }
 
 static int
-get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
+get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
        struct slot *slot = hotplug_slot->private;
 
@@ -177,7 +177,7 @@ get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
 }
 
 static int
-get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
+get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
        struct slot *slot = hotplug_slot->private;
 
@@ -192,14 +192,14 @@ set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
 }
 
 static int
-get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value)
+get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
        *value = hotplug_slot->info->adapter_status;
        return 0;
 }
 
 static int
-get_latch_status(struct hotplug_slot *hotplug_slot, u8 * value)
+get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
        *value = hotplug_slot->info->latch_status;
        return 0;
@@ -299,6 +299,7 @@ error_slot:
 error:
        return status;
 }
+EXPORT_SYMBOL_GPL(cpci_hp_register_bus);
 
 int
 cpci_hp_unregister_bus(struct pci_bus *bus)
@@ -329,6 +330,7 @@ cpci_hp_unregister_bus(struct pci_bus *bus)
        up_write(&list_rwsem);
        return status;
 }
+EXPORT_SYMBOL_GPL(cpci_hp_unregister_bus);
 
 /* This is the interrupt mode interrupt handler */
 static irqreturn_t
@@ -360,7 +362,7 @@ static int
 init_slots(int clear_ins)
 {
        struct slot *slot;
-       struct pci_devdev;
+       struct pci_dev *dev;
 
        dbg("%s - enter", __func__);
        down_read(&list_rwsem);
@@ -614,6 +616,7 @@ cpci_hp_register_controller(struct cpci_hp_controller *new_controller)
                controller = new_controller;
        return status;
 }
+EXPORT_SYMBOL_GPL(cpci_hp_register_controller);
 
 static void
 cleanup_slots(void)
@@ -653,6 +656,7 @@ cpci_hp_unregister_controller(struct cpci_hp_controller *old_controller)
                status = -ENODEV;
        return status;
 }
+EXPORT_SYMBOL_GPL(cpci_hp_unregister_controller);
 
 int
 cpci_hp_start(void)
@@ -690,6 +694,7 @@ cpci_hp_start(void)
        dbg("%s - exit", __func__);
        return 0;
 }
+EXPORT_SYMBOL_GPL(cpci_hp_start);
 
 int
 cpci_hp_stop(void)
@@ -704,6 +709,7 @@ cpci_hp_stop(void)
        cpci_stop_thread();
        return 0;
 }
+EXPORT_SYMBOL_GPL(cpci_hp_stop);
 
 int __init
 cpci_hotplug_init(int debug)
@@ -721,10 +727,3 @@ cpci_hotplug_exit(void)
        cpci_hp_stop();
        cpci_hp_unregister_controller(controller);
 }
-
-EXPORT_SYMBOL_GPL(cpci_hp_register_controller);
-EXPORT_SYMBOL_GPL(cpci_hp_unregister_controller);
-EXPORT_SYMBOL_GPL(cpci_hp_register_bus);
-EXPORT_SYMBOL_GPL(cpci_hp_unregister_bus);
-EXPORT_SYMBOL_GPL(cpci_hp_start);
-EXPORT_SYMBOL_GPL(cpci_hp_stop);
index f6ef64c2ccb5a90b42febc19d738d048141ecabf..7d48ecae6695581e7ded9caa59adf782d5bf0d3f 100644 (file)
@@ -46,7 +46,7 @@ extern int cpci_debug;
 #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
 
 
-u8 cpci_get_attention_status(struct slotslot)
+u8 cpci_get_attention_status(struct slot *slot)
 {
        int hs_cap;
        u16 hs_csr;
@@ -66,7 +66,7 @@ u8 cpci_get_attention_status(struct slot* slot)
        return hs_csr & 0x0008 ? 1 : 0;
 }
 
-int cpci_set_attention_status(struct slotslot, int status)
+int cpci_set_attention_status(struct slot *slot, int status)
 {
        int hs_cap;
        u16 hs_csr;
@@ -93,7 +93,7 @@ int cpci_set_attention_status(struct slot* slot, int status)
        return 1;
 }
 
-u16 cpci_get_hs_csr(struct slotslot)
+u16 cpci_get_hs_csr(struct slot *slot)
 {
        int hs_cap;
        u16 hs_csr;
@@ -111,7 +111,7 @@ u16 cpci_get_hs_csr(struct slot* slot)
        return hs_csr;
 }
 
-int cpci_check_and_clear_ins(struct slotslot)
+int cpci_check_and_clear_ins(struct slot *slot)
 {
        int hs_cap;
        u16 hs_csr;
@@ -140,7 +140,7 @@ int cpci_check_and_clear_ins(struct slot* slot)
        return ins;
 }
 
-int cpci_check_ext(struct slotslot)
+int cpci_check_ext(struct slot *slot)
 {
        int hs_cap;
        u16 hs_csr;
@@ -161,7 +161,7 @@ int cpci_check_ext(struct slot* slot)
        return ext;
 }
 
-int cpci_clear_ext(struct slotslot)
+int cpci_clear_ext(struct slot *slot)
 {
        int hs_cap;
        u16 hs_csr;
@@ -187,7 +187,7 @@ int cpci_clear_ext(struct slot* slot)
        return 0;
 }
 
-int cpci_led_on(struct slotslot)
+int cpci_led_on(struct slot *slot)
 {
        int hs_cap;
        u16 hs_csr;
@@ -216,7 +216,7 @@ int cpci_led_on(struct slot* slot)
        return 0;
 }
 
-int cpci_led_off(struct slotslot)
+int cpci_led_off(struct slot *slot)
 {
        int hs_cap;
        u16 hs_csr;
@@ -303,7 +303,7 @@ int cpci_configure_slot(struct slot *slot)
        return ret;
 }
 
-int cpci_unconfigure_slot(struct slotslot)
+int cpci_unconfigure_slot(struct slot *slot)
 {
        struct pci_dev *dev, *temp;
 
index 7536eef620b0909b724cae3106dbfbc29d9dc2ec..04fcd7811400bdfb6e3afe3b592bcfbbc7121f14 100644 (file)
@@ -78,8 +78,8 @@ static struct cpci_hp_controller generic_hpc;
 
 static int __init validate_parameters(void)
 {
-       charstr;
-       charp;
+       char *str;
+       char *p;
        unsigned long tmp;
 
        if(!bridge) {
@@ -142,8 +142,8 @@ static int query_enum(void)
 static int __init cpcihp_generic_init(void)
 {
        int status;
-       struct resourcer;
-       struct pci_devdev;
+       struct resource *r;
+       struct pci_dev *dev;
 
        info(DRIVER_DESC " version: " DRIVER_VERSION);
        status = validate_parameters();
index e8c4a7ccf5788f73c0cf489ea09b0f0955c9e233..6757b3ef7e10217333302f52ba4b21ab9fd6363f 100644 (file)
@@ -295,7 +295,7 @@ static struct pci_driver zt5550_hc_driver = {
 
 static int __init zt5550_init(void)
 {
-       struct resourcer;
+       struct resource *r;
        int rc;
 
        info(DRIVER_DESC " version: " DRIVER_VERSION);
index 516b87738b6e522d7c6fea05c77a9b4da4c08e49..0450f405807de036060d043e688a0840f809dcc4 100644 (file)
@@ -255,7 +255,7 @@ struct pci_func {
        struct pci_resource *io_head;
        struct pci_resource *bus_head;
        struct timer_list *p_task_event;
-       struct pci_devpci_dev;
+       struct pci_dev *pci_dev;
 };
 
 struct slot {
@@ -278,7 +278,7 @@ struct slot {
 };
 
 struct pci_resource {
-       struct pci_resource * next;
+       struct pci_resource *next;
        u32 base;
        u32 length;
 };
index 037e2612c5bd036df0afb7a56c710439839cf31c..4aaee746df88424dbf730f5431ddee927947c0fb 100644 (file)
@@ -94,7 +94,7 @@ static inline int is_slot66mhz(struct slot *slot)
  *
  * Returns pointer to the head of the SMBIOS tables (or %NULL).
  */
-static void __iomem * detect_SMBIOS_pointer(void __iomem *begin, void __iomem *end)
+static void __iomem *detect_SMBIOS_pointer(void __iomem *begin, void __iomem *end)
 {
        void __iomem *fp;
        void __iomem *endp;
@@ -131,7 +131,7 @@ static void __iomem * detect_SMBIOS_pointer(void __iomem *begin, void __iomem *e
  *
  * For unexpected switch opens
  */
-static int init_SERR(struct controller * ctrl)
+static int init_SERR(struct controller *ctrl)
 {
        u32 tempdword;
        u32 number_of_slots;
@@ -291,7 +291,7 @@ static void release_slot(struct hotplug_slot *hotplug_slot)
        kfree(slot);
 }
 
-static int ctrl_slot_cleanup (struct controller * ctrl)
+static int ctrl_slot_cleanup (struct controller *ctrl)
 {
        struct slot *old_slot, *next_slot;
 
@@ -706,8 +706,7 @@ static int ctrl_slot_setup(struct controller *ctrl,
                hotplug_slot_info->adapter_status =
                        get_presence_status(ctrl, slot);
 
-               dbg("registering bus %d, dev %d, number %d, "
-                               "ctrl->slot_device_offset %d, slot %d\n",
+               dbg("registering bus %d, dev %d, number %d, ctrl->slot_device_offset %d, slot %d\n",
                                slot->bus, slot->device,
                                slot->number, ctrl->slot_device_offset,
                                slot_number);
@@ -837,8 +836,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        bus = pdev->subordinate;
        if (!bus) {
-               dev_notice(&pdev->dev, "the device is not a bridge, "
-                               "skipping\n");
+               dev_notice(&pdev->dev, "the device is not a bridge, skipping\n");
                rc = -ENODEV;
                goto err_disable_device;
        }
index f593585f2784db118b2a944154e5714f9acf7fab..bde47fce324822c5efd5b59b20d0d15cbfb0544c 100644 (file)
@@ -39,9 +39,9 @@
 #include <linux/kthread.h>
 #include "cpqphp.h"
 
-static u32 configure_new_device(struct controllerctrl, struct pci_func *func,
+static u32 configure_new_device(struct controller *ctrl, struct pci_func *func,
                        u8 behind_bridge, struct resource_lists *resources);
-static int configure_new_function(struct controllerctrl, struct pci_func *func,
+static int configure_new_function(struct controller *ctrl, struct pci_func *func,
                        u8 behind_bridge, struct resource_lists *resources);
 static void interrupt_event_handler(struct controller *ctrl);
 
@@ -64,7 +64,7 @@ static void long_delay(int delay)
 
 /* FIXME: The following line needs to be somewhere else... */
 #define WRONG_BUS_FREQUENCY 0x07
-static u8 handle_switch_change(u8 change, struct controller * ctrl)
+static u8 handle_switch_change(u8 change, struct controller *ctrl)
 {
        int hp_slot;
        u8 rc = 0;
@@ -138,7 +138,7 @@ static struct slot *cpqhp_find_slot(struct controller *ctrl, u8 device)
 }
 
 
-static u8 handle_presence_change(u16 change, struct controller * ctrl)
+static u8 handle_presence_change(u16 change, struct controller *ctrl)
 {
        int hp_slot;
        u8 rc = 0;
@@ -232,7 +232,7 @@ static u8 handle_presence_change(u16 change, struct controller * ctrl)
 }
 
 
-static u8 handle_power_fault(u8 change, struct controller * ctrl)
+static u8 handle_power_fault(u8 change, struct controller *ctrl)
 {
        int hp_slot;
        u8 rc = 0;
@@ -997,7 +997,7 @@ struct pci_func *cpqhp_slot_create(u8 busnumber)
  *
  * Returns %0 if successful, !0 otherwise.
  */
-static int slot_remove(struct pci_func * old_slot)
+static int slot_remove(struct pci_func *old_slot)
 {
        struct pci_func *next;
 
@@ -1109,7 +1109,7 @@ struct pci_func *cpqhp_slot_find(u8 bus, u8 device, u8 index)
 
 /* DJZ: I don't think is_bridge will work as is.
  * FIXME */
-static int is_bridge(struct pci_func * func)
+static int is_bridge(struct pci_func *func)
 {
        /* Check the header type */
        if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01)
@@ -1625,7 +1625,7 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl)
  * @replace_flag: whether replacing or adding a new device
  * @ctrl: target controller
  */
-static u32 remove_board(struct pci_func * func, u32 replace_flag, struct controller * ctrl)
+static u32 remove_board(struct pci_func *func, u32 replace_flag, struct controller *ctrl)
 {
        int index;
        u8 skip = 0;
@@ -1742,7 +1742,7 @@ static void pushbutton_helper_thread(unsigned long data)
 
 
 /* this is the main worker thread */
-static int event_thread(voiddata)
+static int event_thread(void *data)
 {
        struct controller *ctrl;
 
@@ -1992,7 +1992,7 @@ int cpqhp_process_SI(struct controller *ctrl, struct pci_func *func)
        u16 temp_word;
        u32 tempdword;
        int rc;
-       struct slotp_slot;
+       struct slot *p_slot;
        int physical_slot = 0;
 
        tempdword = 0;
@@ -2088,7 +2088,7 @@ int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func)
        u8 replace_flag;
        u32 rc = 0;
        unsigned int devfn;
-       struct slotp_slot;
+       struct slot *p_slot;
        struct pci_bus *pci_bus = ctrl->pci_bus;
        int physical_slot=0;
 
@@ -2270,8 +2270,8 @@ int cpqhp_hardware_test(struct controller *ctrl, int test_num)
  *
  * Returns 0 if success.
  */
-static u32 configure_new_device(struct controller * ctrl, struct pci_func * func,
-                                u8 behind_bridge, struct resource_lists resources)
+static u32 configure_new_device(struct controller  *ctrl, struct pci_func  *func,
+                                u8 behind_bridge, struct resource_lists  *resources)
 {
        u8 temp_byte, function, max_functions, stop_it;
        int rc;
index 9600a392eaae8612436beadf4cc2373817b40c18..0968a9bcb34568d9d83ecd5c9d7834e29dcf1616 100644 (file)
@@ -107,7 +107,7 @@ static spinlock_t int15_lock;
  */
 
 
-static u32 add_byte( u32 **p_buffer, u8 value, u32 *used, u32 *avail)
+static u32 add_byte(u32 **p_buffer, u8 value, u32 *used, u32 *avail)
 {
        u8 **tByte;
 
@@ -122,7 +122,7 @@ static u32 add_byte( u32 **p_buffer, u8 value, u32 *used, u32 *avail)
 }
 
 
-static u32 add_dword( u32 **p_buffer, u32 value, u32 *used, u32 *avail)
+static u32 add_dword(u32 **p_buffer, u32 value, u32 *used, u32 *avail)
 {
        if ((*used + 4) > *avail)
                return(1);
@@ -267,12 +267,12 @@ static u32 store_HRT (void __iomem *rom_start)
        ctrl = cpqhp_ctrl_list;
 
        /* The revision of this structure */
-       rc = add_byte( &pFill, 1 + ctrl->push_flag, &usedbytes, &available);
+       rc = add_byte(&pFill, 1 + ctrl->push_flag, &usedbytes, &available);
        if (rc)
                return(rc);
 
        /* The number of controllers */
-       rc = add_byte( &pFill, 1, &usedbytes, &available);
+       rc = add_byte(&pFill, 1, &usedbytes, &available);
        if (rc)
                return(rc);
 
@@ -282,22 +282,22 @@ static u32 store_HRT (void __iomem *rom_start)
                numCtrl++;
 
                /* The bus number */
-               rc = add_byte( &pFill, ctrl->bus, &usedbytes, &available);
+               rc = add_byte(&pFill, ctrl->bus, &usedbytes, &available);
                if (rc)
                        return(rc);
 
                /* The device Number */
-               rc = add_byte( &pFill, PCI_SLOT(ctrl->pci_dev->devfn), &usedbytes, &available);
+               rc = add_byte(&pFill, PCI_SLOT(ctrl->pci_dev->devfn), &usedbytes, &available);
                if (rc)
                        return(rc);
 
                /* The function Number */
-               rc = add_byte( &pFill, PCI_FUNC(ctrl->pci_dev->devfn), &usedbytes, &available);
+               rc = add_byte(&pFill, PCI_FUNC(ctrl->pci_dev->devfn), &usedbytes, &available);
                if (rc)
                        return(rc);
 
                /* Skip the number of available entries */
-               rc = add_dword( &pFill, 0, &usedbytes, &available);
+               rc = add_dword(&pFill, 0, &usedbytes, &available);
                if (rc)
                        return(rc);
 
@@ -311,12 +311,12 @@ static u32 store_HRT (void __iomem *rom_start)
                        loop ++;
 
                        /* base */
-                       rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
+                       rc = add_dword(&pFill, resNode->base, &usedbytes, &available);
                        if (rc)
                                return(rc);
 
                        /* length */
-                       rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
+                       rc = add_dword(&pFill, resNode->length, &usedbytes, &available);
                        if (rc)
                                return(rc);
 
@@ -336,12 +336,12 @@ static u32 store_HRT (void __iomem *rom_start)
                        loop ++;
 
                        /* base */
-                       rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
+                       rc = add_dword(&pFill, resNode->base, &usedbytes, &available);
                        if (rc)
                                return(rc);
 
                        /* length */
-                       rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
+                       rc = add_dword(&pFill, resNode->length, &usedbytes, &available);
                        if (rc)
                                return(rc);
 
@@ -361,12 +361,12 @@ static u32 store_HRT (void __iomem *rom_start)
                        loop ++;
 
                        /* base */
-                       rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
+                       rc = add_dword(&pFill, resNode->base, &usedbytes, &available);
                        if (rc)
                                return(rc);
 
                        /* length */
-                       rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
+                       rc = add_dword(&pFill, resNode->length, &usedbytes, &available);
                        if (rc)
                                return(rc);
 
@@ -386,12 +386,12 @@ static u32 store_HRT (void __iomem *rom_start)
                        loop ++;
 
                        /* base */
-                       rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
+                       rc = add_dword(&pFill, resNode->base, &usedbytes, &available);
                        if (rc)
                                return(rc);
 
                        /* length */
-                       rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
+                       rc = add_dword(&pFill, resNode->length, &usedbytes, &available);
                        if (rc)
                                return(rc);
 
index a3e3c2002b58e97b9260525947db723d7c6b00e7..1c8c2f130d31684ee6761d2909bcf8776a22d0d3 100644 (file)
@@ -81,7 +81,7 @@ static void __iomem *detect_HRT_floating_pointer(void __iomem *begin, void __iom
 }
 
 
-int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func)
+int cpqhp_configure_device (struct controller *ctrl, struct pci_func *func)
 {
        struct pci_bus *child;
        int num;
@@ -121,7 +121,7 @@ int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func)
 }
 
 
-int cpqhp_unconfigure_device(struct pci_funcfunc)
+int cpqhp_unconfigure_device(struct pci_func *func)
 {
        int j;
 
@@ -129,7 +129,7 @@ int cpqhp_unconfigure_device(struct pci_func* func)
 
        pci_lock_rescan_remove();
        for (j=0; j<8 ; j++) {
-               struct pci_devtemp = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, j));
+               struct pci_dev *temp = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, j));
                if (temp) {
                        pci_dev_put(temp);
                        pci_stop_and_remove_bus_device(temp);
@@ -203,7 +203,7 @@ int cpqhp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num)
 }
 
 
-static int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 * dev_num)
+static int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 *dev_num)
 {
        u16 tdevice;
        u32 work;
@@ -280,7 +280,7 @@ static int PCI_GetBusDevHelper(struct controller *ctrl, u8 *bus_num, u8 *dev_num
 }
 
 
-int cpqhp_get_bus_dev (struct controller *ctrl, u8 * bus_num, u8 * dev_num, u8 slot)
+int cpqhp_get_bus_dev (struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot)
 {
        /* plain (bridges allowed) */
        return PCI_GetBusDevHelper(ctrl, bus_num, dev_num, slot, 0);
@@ -465,7 +465,7 @@ int cpqhp_save_config(struct controller *ctrl, int busnumber, int is_hot_plug)
  *
  * returns 0 if success
  */
-int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot)
+int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func *new_slot)
 {
        long rc;
        u8 class_code;
@@ -549,7 +549,7 @@ int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot)
  *
  * returns 0 if success
  */
-int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func * func)
+int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func *func)
 {
        u8 cloop;
        u8 header_type;
@@ -686,7 +686,7 @@ int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func * func)
  *
  * returns 0 if success
  */
-int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func)
+int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func *func)
 {
        u8 cloop;
        u8 header_type;
@@ -949,7 +949,7 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func)
  *
  * returns 0 if success
  */
-int cpqhp_configure_board(struct controller *ctrl, struct pci_func * func)
+int cpqhp_configure_board(struct controller *ctrl, struct pci_func *func)
 {
        int cloop;
        u8 header_type;
@@ -1027,7 +1027,7 @@ int cpqhp_configure_board(struct controller *ctrl, struct pci_func * func)
  *
  * returns 0 if the board is the same nonzero otherwise
  */
-int cpqhp_valid_replace(struct controller *ctrl, struct pci_func * func)
+int cpqhp_valid_replace(struct controller *ctrl, struct pci_func *func)
 {
        u8 cloop;
        u8 header_type;
@@ -1419,7 +1419,7 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st
  *
  * returns 0 if success
  */
-int cpqhp_return_board_resources(struct pci_func * func, struct resource_lists * resources)
+int cpqhp_return_board_resources(struct pci_func *func, struct resource_lists *resources)
 {
        int rc = 0;
        struct pci_resource *node;
@@ -1475,7 +1475,7 @@ int cpqhp_return_board_resources(struct pci_func * func, struct resource_lists *
  *
  * Puts node back in the resource list pointed to by head
  */
-void cpqhp_destroy_resource_list (struct resource_lists * resources)
+void cpqhp_destroy_resource_list (struct resource_lists *resources)
 {
        struct pci_resource *res, *tres;
 
@@ -1522,7 +1522,7 @@ void cpqhp_destroy_resource_list (struct resource_lists * resources)
  *
  * Puts node back in the resource list pointed to by head
  */
-void cpqhp_destroy_board_resources (struct pci_func * func)
+void cpqhp_destroy_board_resources (struct pci_func *func)
 {
        struct pci_resource *res, *tres;
 
index 17c1f36315d191b46bf81734efa69fff344bfa84..4a392c44e3d3abacef8717fac3489ede76e24376 100644 (file)
@@ -79,7 +79,7 @@ static int show_ctrl (struct controller *ctrl, char *buf)
 
 static int show_dev (struct controller *ctrl, char *buf)
 {
-       char * out = buf;
+       char *out = buf;
        int index;
        struct pci_resource *res;
        struct pci_func *new_slot;
index cf3ac1e4b099b381862131fddb91ac2e7df10ac0..f7b8684a773969df4c88b9d0e358d19e5c7f069d 100644 (file)
@@ -74,7 +74,7 @@ static inline int get_max_adapter_speed (struct hotplug_slot *hs, u8 *value)
 static inline int get_cur_bus_info(struct slot **sl)
 {
        int rc = 1;
-       struct slot * slot_cur = *sl;
+       struct slot *slot_cur = *sl;
 
        debug("options = %x\n", slot_cur->ctrl->options);
        debug("revision = %x\n", slot_cur->ctrl->revision);
@@ -114,8 +114,8 @@ static inline int slot_update(struct slot **sl)
 
 static int __init get_max_slots (void)
 {
-       struct slot * slot_cur;
-       struct list_head * tmp;
+       struct slot *slot_cur;
+       struct list_head *tmp;
        u8 slot_count = 0;
 
        list_for_each(tmp, &ibmphp_slot_head) {
@@ -280,7 +280,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
        return rc;
 }
 
-static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
+static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
        int rc = -ENODEV;
        struct slot *pslot;
@@ -311,7 +311,7 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
        return rc;
 }
 
-static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 * value)
+static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
        int rc = -ENODEV;
        struct slot *pslot;
@@ -338,7 +338,7 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 * value)
 }
 
 
-static int get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
+static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
        int rc = -ENODEV;
        struct slot *pslot;
@@ -364,7 +364,7 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
        return rc;
 }
 
-static int get_adapter_present(struct hotplug_slot *hotplug_slot, u8 * value)
+static int get_adapter_present(struct hotplug_slot *hotplug_slot, u8 *value)
 {
        int rc = -ENODEV;
        struct slot *pslot;
@@ -433,7 +433,7 @@ static int get_max_bus_speed(struct slot *slot)
 }
 
 /*
-static int get_max_adapter_speed_1(struct hotplug_slot *hotplug_slot, u8 * value, u8 flag)
+static int get_max_adapter_speed_1(struct hotplug_slot *hotplug_slot, u8 *value, u8 flag)
 {
        int rc = -ENODEV;
        struct slot *pslot;
@@ -471,7 +471,7 @@ static int get_max_adapter_speed_1(struct hotplug_slot *hotplug_slot, u8 * value
        return rc;
 }
 
-static int get_bus_name(struct hotplug_slot *hotplug_slot, char * value)
+static int get_bus_name(struct hotplug_slot *hotplug_slot, char *value)
 {
        int rc = -ENODEV;
        struct slot *pslot = NULL;
@@ -671,7 +671,7 @@ static struct pci_func *ibm_slot_find(u8 busno, u8 device, u8 function)
 {
        struct pci_func *func_cur;
        struct slot *slot_cur;
-       struct list_head * tmp;
+       struct list_head *tmp;
        list_for_each(tmp, &ibmphp_slot_head) {
                slot_cur = list_entry(tmp, struct slot, ibm_slot_list);
                if (slot_cur->func) {
@@ -696,8 +696,8 @@ static struct pci_func *ibm_slot_find(u8 busno, u8 device, u8 function)
 static void free_slots(void)
 {
        struct slot *slot_cur;
-       struct list_head * tmp;
-       struct list_head * next;
+       struct list_head *tmp;
+       struct list_head *next;
 
        debug("%s -- enter\n", __func__);
 
@@ -825,10 +825,10 @@ static int ibm_configure_device(struct pci_func *func)
 /*******************************************************
  * Returns whether the bus is empty or not
  *******************************************************/
-static int is_bus_empty(struct slot * slot_cur)
+static int is_bus_empty(struct slot *slot_cur)
 {
        int rc;
-       struct slot * tmp_slot;
+       struct slot *tmp_slot;
        u8 i = slot_cur->bus_on->slot_min;
 
        while (i <= slot_cur->bus_on->slot_max) {
@@ -856,7 +856,7 @@ static int is_bus_empty(struct slot * slot_cur)
  * Parameters: slot
  * Returns: bus is set (0) or error code
  ***********************************************************/
-static int set_bus(struct slot * slot_cur)
+static int set_bus(struct slot *slot_cur)
 {
        int rc;
        u8 speed;
@@ -956,7 +956,7 @@ static int set_bus(struct slot * slot_cur)
 static int check_limitations(struct slot *slot_cur)
 {
        u8 i;
-       struct slot * tmp_slot;
+       struct slot *tmp_slot;
        u8 count = 0;
        u8 limitation = 0;
 
@@ -1045,8 +1045,7 @@ static int enable_slot(struct hotplug_slot *hs)
        rc = check_limitations(slot_cur);
        if (rc) {
                err("Adding this card exceeds the limitations of this bus.\n");
-               err("(i.e., >1 133MHz cards running on same bus, or "
-                    ">2 66 PCI cards running on same bus.\n");
+               err("(i.e., >1 133MHz cards running on same bus, or >2 66 PCI cards running on same bus.\n");
                err("Try hot-adding into another bus\n");
                rc = -EINVAL;
                goto error_nopower;
@@ -1070,12 +1069,10 @@ static int enable_slot(struct hotplug_slot *hs)
                                        !(SLOT_PWRGD(slot_cur->status)))
                        err("power fault occurred trying to power up\n");
                else if (SLOT_BUS_SPEED(slot_cur->status)) {
-                       err("bus speed mismatch occurred.  please check "
-                               "current bus speed and card capability\n");
+                       err("bus speed mismatch occurred.  please check current bus speed and card capability\n");
                        print_card_capability(slot_cur);
                } else if (SLOT_BUS_MODE(slot_cur->ext_status)) {
-                       err("bus mode mismatch occurred.  please check "
-                               "current bus mode and card capability\n");
+                       err("bus mode mismatch occurred.  please check current bus mode and card capability\n");
                        print_card_capability(slot_cur);
                }
                ibmphp_update_slot_info(slot_cur);
@@ -1098,8 +1095,7 @@ static int enable_slot(struct hotplug_slot *hs)
                goto error_power;
        }
        if (SLOT_POWER(slot_cur->status) && (SLOT_BUS_SPEED(slot_cur->status))) {
-               err("bus speed mismatch occurred.  please check current bus "
-                                       "speed and card capability\n");
+               err("bus speed mismatch occurred.  please check current bus speed and card capability\n");
                print_card_capability(slot_cur);
                goto error_power;
        }
index bd044158b36c3ba963476ea1d32c139952de90cf..0f65ac5554344ce4a3c1bc474af73d12e0833dda 100644 (file)
@@ -563,7 +563,7 @@ static int first_slot_num (u8 slot_num, u8 first_slot, u8 var)
        return rc;
 }
 
-static struct opt_rio_lo * find_rxe_num (u8 slot_num)
+static struct opt_rio_lo *find_rxe_num (u8 slot_num)
 {
        struct opt_rio_lo *opt_lo_ptr;
 
@@ -575,7 +575,7 @@ static struct opt_rio_lo * find_rxe_num (u8 slot_num)
        return NULL;
 }
 
-static struct opt_rio * find_chassis_num (u8 slot_num)
+static struct opt_rio *find_chassis_num (u8 slot_num)
 {
        struct opt_rio *opt_vg_ptr;
 
@@ -593,7 +593,7 @@ static struct opt_rio * find_chassis_num (u8 slot_num)
 static u8 calculate_first_slot (u8 slot_num)
 {
        u8 first_slot = 1;
-       struct slot * slot_cur;
+       struct slot *slot_cur;
 
        list_for_each_entry(slot_cur, &ibmphp_slot_head, ibm_slot_list) {
                if (slot_cur->ctrl) {
@@ -607,7 +607,7 @@ static u8 calculate_first_slot (u8 slot_num)
 
 #define SLOT_NAME_SIZE 30
 
-static char *create_file_name (struct slot * slot_cur)
+static char *create_file_name (struct slot *slot_cur)
 {
        struct opt_rio *opt_vg_ptr = NULL;
        struct opt_rio_lo *opt_lo_ptr = NULL;
@@ -1192,7 +1192,7 @@ int ibmphp_register_pci (void)
        }
        return rc;
 }
-static int ibmphp_probe (struct pci_dev * dev, const struct pci_device_id *ids)
+static int ibmphp_probe (struct pci_dev *dev, const struct pci_device_id *ids)
 {
        struct controller *ctrl;
 
index 5fc7a089f5323ce629175bdb568da1584d84d19a..a936022956e6d4fc8416c8992b827744952f7109 100644 (file)
@@ -533,7 +533,7 @@ static u8 hpc_readcmdtoindex (u8 cmd, u8 index)
 *
 * Return   0 or error codes
 *---------------------------------------------------------------------*/
-int ibmphp_hpc_readslot (struct slot * pslot, u8 cmd, u8 * pstatus)
+int ibmphp_hpc_readslot (struct slot *pslot, u8 cmd, u8 *pstatus)
 {
        void __iomem *wpg_bbar = NULL;
        struct controller *ctlr_ptr;
@@ -672,7 +672,7 @@ int ibmphp_hpc_readslot (struct slot * pslot, u8 cmd, u8 * pstatus)
 *
 * Action: issue a WRITE command to HPC
 *---------------------------------------------------------------------*/
-int ibmphp_hpc_writeslot (struct slot * pslot, u8 cmd)
+int ibmphp_hpc_writeslot (struct slot *pslot, u8 cmd)
 {
        void __iomem *wpg_bbar = NULL;
        struct controller *ctlr_ptr;
@@ -1102,7 +1102,7 @@ void __exit ibmphp_hpc_stop_poll_thread (void)
 * Value:
 *---------------------------------------------------------------------*/
 static int hpc_wait_ctlr_notworking (int timeout, struct controller *ctlr_ptr, void __iomem *wpg_bbar,
-                                   u8 * pstatus)
+                                   u8 *pstatus)
 {
        int rc = 0;
        u8 done = 0;
index 639ea3a75e1452ed825e7a2650fe635c2785f892..2fd296706ce7f7a056ba5a2813c97f11d68ecdd1 100644 (file)
@@ -47,7 +47,7 @@ static u8 find_sec_number (u8 primary_busno, u8 slotno);
  * We also assign the same irq numbers for multi function devices.
  * These are PIC mode, so shouldn't matter n.e.ways (hopefully)
  */
-static void assign_alt_irq (struct pci_func * cur_func, u8 class_code)
+static void assign_alt_irq (struct pci_func *cur_func, u8 class_code)
 {
        int j;
        for (j = 0; j < 4; j++) {
@@ -137,8 +137,8 @@ int ibmphp_configure_card (struct pci_func *func, u8 slotno)
                                     "Please choose another device.\n", cur_func->device);
                                return -ENODEV;
                        } else if (class == PCI_CLASS_DISPLAY_VGA) {
-                               err ("The device %x is not supported for hot plugging. "
-                                    "Please choose another device.\n", cur_func->device);
+                               err ("The device %x is not supported for hot plugging. Please choose another device.\n",
+                                    cur_func->device);
                                return -ENODEV;
                        }
                        switch (hdr_type) {
@@ -179,8 +179,8 @@ int ibmphp_configure_card (struct pci_func *func, u8 slotno)
                                case PCI_HEADER_TYPE_MULTIBRIDGE:
                                        class >>= 8;
                                        if (class != PCI_CLASS_BRIDGE_PCI) {
-                                               err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging. "
-                                                    "Please insert another card.\n", cur_func->device);
+                                               err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging.  Please insert another card.\n",
+                                                    cur_func->device);
                                                return -ENODEV;
                                        }
                                        assign_alt_irq (cur_func, class_code);
@@ -247,8 +247,8 @@ int ibmphp_configure_card (struct pci_func *func, u8 slotno)
                                        class >>= 8;
                                        debug ("class now is %x\n", class);
                                        if (class != PCI_CLASS_BRIDGE_PCI) {
-                                               err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging. "
-                                                    "Please insert another card.\n", cur_func->device);
+                                               err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging.  Please insert another card.\n",
+                                                    cur_func->device);
                                                return -ENODEV;
                                        }
 
@@ -1073,7 +1073,7 @@ error:
  * Input: bridge function
  * Output: amount of resources needed
  *****************************************************************************/
-static struct res_needed *scan_behind_bridge (struct pci_func * func, u8 busno)
+static struct res_needed *scan_behind_bridge (struct pci_func *func, u8 busno)
 {
        int count, len[6];
        u16 vendor_id;
@@ -1125,13 +1125,11 @@ static struct res_needed *scan_behind_bridge (struct pci_func * func, u8 busno)
 
                                class >>= 8;    /* to take revision out, class = class.subclass.prog i/f */
                                if (class == PCI_CLASS_NOT_DEFINED_VGA) {
-                                       err ("The device %x is VGA compatible and as is not supported for hot plugging. "
-                                            "Please choose another device.\n", device);
+                                       err ("The device %x is VGA compatible and as is not supported for hot plugging.  Please choose another device.\n", device);
                                        amount->not_correct = 1;
                                        return amount;
                                } else if (class == PCI_CLASS_DISPLAY_VGA) {
-                                       err ("The device %x is not supported for hot plugging. "
-                                            "Please choose another device.\n", device);
+                                       err ("The device %x is not supported for hot plugging.  Please choose another device.\n", device);
                                        amount->not_correct = 1;
                                        return amount;
                                }
@@ -1483,12 +1481,10 @@ static int unconfigure_boot_card (struct slot *slot_cur)
                        debug ("hdr_type %x, class %x\n", hdr_type, class);
                        class >>= 8;    /* to take revision out, class = class.subclass.prog i/f */
                        if (class == PCI_CLASS_NOT_DEFINED_VGA) {
-                               err ("The device %x function %x is VGA compatible and is not supported for hot removing. "
-                                    "Please choose another device.\n", device, function);
+                               err ("The device %x function %x is VGA compatible and is not supported for hot removing.  Please choose another device.\n", device, function);
                                return -ENODEV;
                        } else if (class == PCI_CLASS_DISPLAY_VGA) {
-                               err ("The device %x function %x is not supported for hot removing. "
-                                    "Please choose another device.\n", device, function);
+                               err ("The device %x function %x is not supported for hot removing.  Please choose another device.\n", device, function);
                                return -ENODEV;
                        }
 
@@ -1513,9 +1509,7 @@ static int unconfigure_boot_card (struct slot *slot_cur)
                                case PCI_HEADER_TYPE_BRIDGE:
                                        class >>= 8;
                                        if (class != PCI_CLASS_BRIDGE_PCI) {
-                                               err ("This device %x function %x is not PCI-to-PCI bridge, "
-                                                    "and is not supported for hot-removing. "
-                                                    "Please try another card.\n", device, function);
+                                               err ("This device %x function %x is not PCI-to-PCI bridge, and is not supported for hot-removing.  Please try another card.\n", device, function);
                                                return -ENODEV;
                                        }
                                        rc = unconfigure_boot_bridge (busno, device, function);
@@ -1529,9 +1523,7 @@ static int unconfigure_boot_card (struct slot *slot_cur)
                                case PCI_HEADER_TYPE_MULTIBRIDGE:
                                        class >>= 8;
                                        if (class != PCI_CLASS_BRIDGE_PCI) {
-                                               err ("This device %x function %x is not PCI-to-PCI bridge, "
-                                                    "and is not supported for hot-removing. "
-                                                    "Please try another card.\n", device, function);
+                                               err ("This device %x function %x is not PCI-to-PCI bridge,  and is not supported for hot-removing.  Please try another card.\n", device, function);
                                                return -ENODEV;
                                        }
                                        rc = unconfigure_boot_bridge (busno, device, function);
index a265acb2d5186502d7517c3405797b3882d305c7..f34745abd5b625e3d2588cbee2ca0568d8f98128 100644 (file)
@@ -46,9 +46,9 @@ static struct bus_node *find_bus_wprev (u8, struct bus_node **, u8);
 
 static LIST_HEAD(gbuses);
 
-static struct bus_node * __init alloc_error_bus (struct ebda_pci_rsrc * curr, u8 busno, int flag)
+static struct bus_node * __init alloc_error_bus (struct ebda_pci_rsrc *curr, u8 busno, int flag)
 {
-       struct bus_node * newbus;
+       struct bus_node *newbus;
 
        if (!(curr) && !(flag)) {
                err ("NULL pointer passed\n");
@@ -69,7 +69,7 @@ static struct bus_node * __init alloc_error_bus (struct ebda_pci_rsrc * curr, u8
        return newbus;
 }
 
-static struct resource_node * __init alloc_resources (struct ebda_pci_rsrc * curr)
+static struct resource_node * __init alloc_resources (struct ebda_pci_rsrc *curr)
 {
        struct resource_node *rs;
 
@@ -93,7 +93,7 @@ static struct resource_node * __init alloc_resources (struct ebda_pci_rsrc * cur
 
 static int __init alloc_bus_range (struct bus_node **new_bus, struct range_node **new_range, struct ebda_pci_rsrc *curr, int flag, u8 first_bus)
 {
-       struct bus_node * newbus;
+       struct bus_node *newbus;
        struct range_node *newrange;
        u8 num_ranges = 0;
 
@@ -789,8 +789,7 @@ int ibmphp_remove_resource (struct resource_node *res)
        bus_cur = find_bus_wprev (res->busno, NULL, 0);
 
        if (!bus_cur) {
-               err ("cannot find corresponding bus of the io resource to remove  "
-                       "bailing out...\n");
+               err ("cannot find corresponding bus of the io resource to remove  bailing out...\n");
                return -ENODEV;
        }
 
@@ -934,9 +933,9 @@ int ibmphp_remove_resource (struct resource_node *res)
        return 0;
 }
 
-static struct range_node * find_range (struct bus_node *bus_cur, struct resource_node * res)
+static struct range_node *find_range (struct bus_node *bus_cur, struct resource_node *res)
 {
-       struct range_node * range = NULL;
+       struct range_node *range = NULL;
 
        switch (res->type) {
                case IO:
index cfa92a984e622ff0c8719488191daf0f618d8297..56d8486dc16704d1f93726d03131cbedfe0b4bed 100644 (file)
@@ -59,14 +59,12 @@ static bool debug;
 #define DRIVER_DESC    "PCI Hot Plug PCI Core"
 
 
-//////////////////////////////////////////////////////////////////
-
 static LIST_HEAD(pci_hotplug_slot_list);
 static DEFINE_MUTEX(pci_hp_mutex);
 
 /* Weee, fun with macros... */
-#define GET_STATUS(name,type)  \
-static int get_##name (struct hotplug_slot *slot, type *value)         \
+#define GET_STATUS(name, type) \
+static int get_##name(struct hotplug_slot *slot, type *value)          \
 {                                                                      \
        struct hotplug_slot_ops *ops = slot->ops;                       \
        int retval = 0;                                                 \
@@ -92,42 +90,41 @@ static ssize_t power_read_file(struct pci_slot *slot, char *buf)
 
        retval = get_power_status(slot->hotplug, &value);
        if (retval)
-               goto exit;
-       retval = sprintf (buf, "%d\n", value);
-exit:
-       return retval;
+               return retval;
+
+       return sprintf(buf, "%d\n", value);
 }
 
 static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
-               size_t count)
+                               size_t count)
 {
        struct hotplug_slot *slot = pci_slot->hotplug;
        unsigned long lpower;
        u8 power;
        int retval = 0;
 
-       lpower = simple_strtoul (buf, NULL, 10);
+       lpower = simple_strtoul(buf, NULL, 10);
        power = (u8)(lpower & 0xff);
-       dbg ("power = %d\n", power);
+       dbg("power = %d\n", power);
 
        if (!try_module_get(slot->ops->owner)) {
                retval = -ENODEV;
                goto exit;
        }
        switch (power) {
-               case 0:
-                       if (slot->ops->disable_slot)
-                               retval = slot->ops->disable_slot(slot);
-                       break;
-
-               case 1:
-                       if (slot->ops->enable_slot)
-                               retval = slot->ops->enable_slot(slot);
-                       break;
-
-               default:
-                       err ("Illegal value specified for power\n");
-                       retval = -EINVAL;
+       case 0:
+               if (slot->ops->disable_slot)
+                       retval = slot->ops->disable_slot(slot);
+               break;
+
+       case 1:
+               if (slot->ops->enable_slot)
+                       retval = slot->ops->enable_slot(slot);
+               break;
+
+       default:
+               err("Illegal value specified for power\n");
+               retval = -EINVAL;
        }
        module_put(slot->ops->owner);
 
@@ -150,24 +147,22 @@ static ssize_t attention_read_file(struct pci_slot *slot, char *buf)
 
        retval = get_attention_status(slot->hotplug, &value);
        if (retval)
-               goto exit;
-       retval = sprintf(buf, "%d\n", value);
+               return retval;
 
-exit:
-       return retval;
+       return sprintf(buf, "%d\n", value);
 }
 
 static ssize_t attention_write_file(struct pci_slot *slot, const char *buf,
-               size_t count)
+                                   size_t count)
 {
        struct hotplug_slot_ops *ops = slot->hotplug->ops;
        unsigned long lattention;
        u8 attention;
        int retval = 0;
 
-       lattention = simple_strtoul (buf, NULL, 10);
+       lattention = simple_strtoul(buf, NULL, 10);
        attention = (u8)(lattention & 0xff);
-       dbg (" - attention = %d\n", attention);
+       dbg(" - attention = %d\n", attention);
 
        if (!try_module_get(ops->owner)) {
                retval = -ENODEV;
@@ -196,11 +191,9 @@ static ssize_t latch_read_file(struct pci_slot *slot, char *buf)
 
        retval = get_latch_status(slot->hotplug, &value);
        if (retval)
-               goto exit;
-       retval = sprintf (buf, "%d\n", value);
+               return retval;
 
-exit:
-       return retval;
+       return sprintf(buf, "%d\n", value);
 }
 
 static struct pci_slot_attribute hotplug_slot_attr_latch = {
@@ -215,11 +208,9 @@ static ssize_t presence_read_file(struct pci_slot *slot, char *buf)
 
        retval = get_adapter_status(slot->hotplug, &value);
        if (retval)
-               goto exit;
-       retval = sprintf (buf, "%d\n", value);
+               return retval;
 
-exit:
-       return retval;
+       return sprintf(buf, "%d\n", value);
 }
 
 static struct pci_slot_attribute hotplug_slot_attr_presence = {
@@ -228,7 +219,7 @@ static struct pci_slot_attribute hotplug_slot_attr_presence = {
 };
 
 static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
-               size_t count)
+                              size_t count)
 {
        struct hotplug_slot *slot = pci_slot->hotplug;
        unsigned long ltest;
@@ -237,7 +228,7 @@ static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
 
        ltest = simple_strtoul (buf, NULL, 10);
        test = (u32)(ltest & 0xffffffff);
-       dbg ("test = %d\n", test);
+       dbg("test = %d\n", test);
 
        if (!try_module_get(slot->ops->owner)) {
                retval = -ENODEV;
@@ -261,6 +252,7 @@ static struct pci_slot_attribute hotplug_slot_attr_test = {
 static bool has_power_file(struct pci_slot *pci_slot)
 {
        struct hotplug_slot *slot = pci_slot->hotplug;
+
        if ((!slot) || (!slot->ops))
                return false;
        if ((slot->ops->enable_slot) ||
@@ -273,6 +265,7 @@ static bool has_power_file(struct pci_slot *pci_slot)
 static bool has_attention_file(struct pci_slot *pci_slot)
 {
        struct hotplug_slot *slot = pci_slot->hotplug;
+
        if ((!slot) || (!slot->ops))
                return false;
        if ((slot->ops->set_attention_status) ||
@@ -284,6 +277,7 @@ static bool has_attention_file(struct pci_slot *pci_slot)
 static bool has_latch_file(struct pci_slot *pci_slot)
 {
        struct hotplug_slot *slot = pci_slot->hotplug;
+
        if ((!slot) || (!slot->ops))
                return false;
        if (slot->ops->get_latch_status)
@@ -294,6 +288,7 @@ static bool has_latch_file(struct pci_slot *pci_slot)
 static bool has_adapter_file(struct pci_slot *pci_slot)
 {
        struct hotplug_slot *slot = pci_slot->hotplug;
+
        if ((!slot) || (!slot->ops))
                return false;
        if (slot->ops->get_adapter_status)
@@ -304,6 +299,7 @@ static bool has_adapter_file(struct pci_slot *pci_slot)
 static bool has_test_file(struct pci_slot *pci_slot)
 {
        struct hotplug_slot *slot = pci_slot->hotplug;
+
        if ((!slot) || (!slot->ops))
                return false;
        if (slot->ops->hardware_test)
@@ -397,13 +393,13 @@ static void fs_remove_slot(struct pci_slot *slot)
        pci_hp_remove_module_link(slot);
 }
 
-static struct hotplug_slot *get_slot_from_name (const char *name)
+static struct hotplug_slot *get_slot_from_name(const char *name)
 {
        struct hotplug_slot *slot;
        struct list_head *tmp;
 
-       list_for_each (tmp, &pci_hotplug_slot_list) {
-               slot = list_entry (tmp, struct hotplug_slot, slot_list);
+       list_for_each(tmp, &pci_hotplug_slot_list) {
+               slot = list_entry(tmp, struct hotplug_slot, slot_list);
                if (strcmp(hotplug_slot_name(slot), name) == 0)
                        return slot;
        }
@@ -436,8 +432,7 @@ int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus,
        if ((slot->info == NULL) || (slot->ops == NULL))
                return -EINVAL;
        if (slot->release == NULL) {
-               dbg("Why are you trying to register a hotplug slot "
-                   "without a proper release function?\n");
+               dbg("Why are you trying to register a hotplug slot without a proper release function?\n");
                return -EINVAL;
        }
 
@@ -468,6 +463,7 @@ out:
        mutex_unlock(&pci_hp_mutex);
        return result;
 }
+EXPORT_SYMBOL_GPL(__pci_hp_register);
 
 /**
  * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
@@ -506,6 +502,7 @@ int pci_hp_deregister(struct hotplug_slot *hotplug)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(pci_hp_deregister);
 
 /**
  * pci_hp_change_slot_info - changes the slot's information structure in the core
@@ -527,24 +524,23 @@ int pci_hp_change_slot_info(struct hotplug_slot *hotplug,
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
 
-static int __init pci_hotplug_init (void)
+static int __init pci_hotplug_init(void)
 {
        int result;
 
        result = cpci_hotplug_init(debug);
        if (result) {
-               err ("cpci_hotplug_init with error %d\n", result);
-               goto err_cpci;
+               err("cpci_hotplug_init with error %d\n", result);
+               return result;
        }
 
-       info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
-
-err_cpci:
+       info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
        return result;
 }
 
-static void __exit pci_hotplug_exit (void)
+static void __exit pci_hotplug_exit(void)
 {
        cpci_hotplug_exit();
 }
@@ -557,7 +553,3 @@ MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 module_param(debug, bool, 0644);
 MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
-
-EXPORT_SYMBOL_GPL(__pci_hp_register);
-EXPORT_SYMBOL_GPL(pci_hp_deregister);
-EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
index 20fea57d2149c7ecf43e31c6ee16d820415ba85d..93cc9266e8cb8caa38075e044849413116ed9b71 100644 (file)
@@ -103,10 +103,10 @@ static int __init dummy_probe(struct pcie_device *dev)
 }
 
 static struct pcie_port_service_driver __initdata dummy_driver = {
-        .name           = "pciehp_dummy",
+       .name           = "pciehp_dummy",
        .port_type      = PCIE_ANY_PORT,
        .service        = PCIE_PORT_SERVICE_HP,
-        .probe          = dummy_probe,
+       .probe          = dummy_probe,
 };
 
 static int __init select_detection_mode(void)
index 0e0a2fff20a39be57f7c9abc8bb422831c771416..a2297db8081322a6ca51f2b6d32fadf385e1236c 100644 (file)
@@ -266,8 +266,7 @@ static int pciehp_probe(struct pcie_device *dev)
        rc = init_slot(ctrl);
        if (rc) {
                if (rc == -EBUSY)
-                       ctrl_warn(ctrl, "Slot already registered by another "
-                                 "hotplug driver\n");
+                       ctrl_warn(ctrl, "Slot already registered by another hotplug driver\n");
                else
                        ctrl_err(ctrl, "Slot initialization failed\n");
                goto err_out_release_ctlr;
@@ -312,12 +311,12 @@ static void pciehp_remove(struct pcie_device *dev)
 }
 
 #ifdef CONFIG_PM
-static int pciehp_suspend (struct pcie_device *dev)
+static int pciehp_suspend(struct pcie_device *dev)
 {
        return 0;
 }
 
-static int pciehp_resume (struct pcie_device *dev)
+static int pciehp_resume(struct pcie_device *dev)
 {
        struct controller *ctrl;
        struct slot *slot;
index c75e6a678dcc22f19707fc8d77200e1ee307afe6..ff32e85e1de6fb53972e2f58e62f273e350ad969 100644 (file)
@@ -175,7 +175,7 @@ void pciehp_handle_linkstate_change(struct slot *p_slot)
    hotplug controller logic
  */
 
-static void set_slot_off(struct controller *ctrl, struct slot * pslot)
+static void set_slot_off(struct controller *ctrl, struct slot *pslot)
 {
        /* turn off slot, turn on Amber LED, turn off Green LED if supported*/
        if (POWER_CTRL(ctrl)) {
@@ -376,14 +376,12 @@ static void handle_button_press_event(struct slot *p_slot)
                pciehp_get_power_status(p_slot, &getstatus);
                if (getstatus) {
                        p_slot->state = BLINKINGOFF_STATE;
-                       ctrl_info(ctrl,
-                                 "PCI slot #%s - powering off due to button "
-                                 "press.\n", slot_name(p_slot));
+                       ctrl_info(ctrl, "PCI slot #%s - powering off due to button press\n",
+                                 slot_name(p_slot));
                } else {
                        p_slot->state = BLINKINGON_STATE;
-                       ctrl_info(ctrl,
-                                 "PCI slot #%s - powering on due to button "
-                                 "press.\n", slot_name(p_slot));
+                       ctrl_info(ctrl, "PCI slot #%s - powering on due to button press\n",
+                                 slot_name(p_slot));
                }
                /* blink green LED and turn off amber */
                pciehp_green_led_blink(p_slot);
@@ -404,8 +402,8 @@ static void handle_button_press_event(struct slot *p_slot)
                else
                        pciehp_green_led_off(p_slot);
                pciehp_set_attention_status(p_slot, 0);
-               ctrl_info(ctrl, "PCI slot #%s - action canceled "
-                         "due to button press\n", slot_name(p_slot));
+               ctrl_info(ctrl, "PCI slot #%s - action canceled due to button press\n",
+                         slot_name(p_slot));
                p_slot->state = STATIC_STATE;
                break;
        case POWEROFF_STATE:
index 1463412cf7f8e9b1d63cfc31a6d4a2085de3b21d..42914e04d11070e4daefccc9eeb80d52a610b549 100644 (file)
@@ -174,12 +174,10 @@ static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
                         * event even though it supports none of power
                         * controller, attention led, power led and EMI.
                         */
-                       ctrl_dbg(ctrl, "Unexpected CMD_COMPLETED. Need to "
-                                "wait for command completed event.\n");
+                       ctrl_dbg(ctrl, "Unexpected CMD_COMPLETED. Need to wait for command completed event\n");
                        ctrl->no_cmd_complete = 0;
                } else {
-                       ctrl_dbg(ctrl, "Unexpected CMD_COMPLETED. Maybe "
-                                "the controller is broken.\n");
+                       ctrl_dbg(ctrl, "Unexpected CMD_COMPLETED. Maybe the controller is broken\n");
                }
        }
 
@@ -203,7 +201,7 @@ static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
                if (!(slot_ctrl & PCI_EXP_SLTCTL_HPIE) ||
                    !(slot_ctrl & PCI_EXP_SLTCTL_CCIE))
                        poll = 1;
-                pcie_wait_cmd(ctrl, poll);
+               pcie_wait_cmd(ctrl, poll);
        }
        mutex_unlock(&ctrl->ctrl_lock);
 }
@@ -276,15 +274,15 @@ int pciehp_check_link_status(struct controller *ctrl)
        bool found;
        u16 lnk_status;
 
-        /*
-         * Data Link Layer Link Active Reporting must be capable for
-         * hot-plug capable downstream port. But old controller might
-         * not implement it. In this case, we wait for 1000 ms.
-         */
-        if (ctrl->link_active_reporting)
-                pcie_wait_link_active(ctrl);
-        else
-                msleep(1000);
+       /*
+        * Data Link Layer Link Active Reporting must be capable for
+        * hot-plug capable downstream port. But old controller might
+        * not implement it. In this case, we wait for 1000 ms.
+       */
+       if (ctrl->link_active_reporting)
+               pcie_wait_link_active(ctrl);
+       else
+               msleep(1000);
 
        /* wait 100ms before read pci conf, and try in 1s */
        msleep(100);
@@ -295,7 +293,7 @@ int pciehp_check_link_status(struct controller *ctrl)
        ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status);
        if ((lnk_status & PCI_EXP_LNKSTA_LT) ||
            !(lnk_status & PCI_EXP_LNKSTA_NLW)) {
-               ctrl_err(ctrl, "Link Training Error occurs \n");
+               ctrl_err(ctrl, "Link Training Error occurs\n");
                return -1;
        }
 
@@ -414,7 +412,7 @@ void pciehp_set_attention_status(struct slot *slot, u8 value)
                return;
 
        switch (value) {
-       case 0 :        /* turn off */
+       case 0        /* turn off */
                slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_OFF;
                break;
        case 1:         /* turn on */
@@ -470,7 +468,7 @@ void pciehp_green_led_blink(struct slot *slot)
                 PCI_EXP_SLTCTL_PWR_IND_BLINK);
 }
 
-int pciehp_power_on_slot(struct slot * slot)
+int pciehp_power_on_slot(struct slot *slot)
 {
        struct controller *ctrl = slot->ctrl;
        struct pci_dev *pdev = ctrl_dev(ctrl);
@@ -496,7 +494,7 @@ int pciehp_power_on_slot(struct slot * slot)
        return retval;
 }
 
-void pciehp_power_off_slot(struct slot * slot)
+void pciehp_power_off_slot(struct slot *slot)
 {
        struct controller *ctrl = slot->ctrl;
 
@@ -756,7 +754,7 @@ static inline void dbg_ctrl(struct controller *ctrl)
        ctrl_info(ctrl, "Slot Control           : 0x%04x\n", reg16);
 }
 
-#define FLAG(x,y)      (((x) & (y)) ? '+' : '-')
+#define FLAG(x, y)     (((x) & (y)) ? '+' : '-')
 
 struct controller *pcie_init(struct pcie_device *dev)
 {
@@ -783,14 +781,14 @@ struct controller *pcie_init(struct pcie_device *dev)
         */
        if (NO_CMD_CMPL(ctrl) ||
            !(POWER_CTRL(ctrl) | ATTN_LED(ctrl) | PWR_LED(ctrl) | EMI(ctrl)))
-           ctrl->no_cmd_complete = 1;
-
-        /* Check if Data Link Layer Link Active Reporting is implemented */
-        pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap);
-        if (link_cap & PCI_EXP_LNKCAP_DLLLARC) {
-                ctrl_dbg(ctrl, "Link Active Reporting supported\n");
-                ctrl->link_active_reporting = 1;
-        }
+               ctrl->no_cmd_complete = 1;
+
+       /* Check if Data Link Layer Link Active Reporting is implemented */
+       pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap);
+       if (link_cap & PCI_EXP_LNKCAP_DLLLARC) {
+               ctrl_dbg(ctrl, "Link Active Reporting supported\n");
+               ctrl->link_active_reporting = 1;
+       }
 
        /* Clear all remaining event bits in Slot Status register */
        pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
index b6cb1df670972bf9abe711c8ed1e1e16d069ada4..5f871f4c4af10944e3417712f99194c2c1936349 100644 (file)
@@ -46,9 +46,8 @@ int pciehp_configure_device(struct slot *p_slot)
 
        dev = pci_get_slot(parent, PCI_DEVFN(0, 0));
        if (dev) {
-               ctrl_err(ctrl, "Device %s already exists "
-                        "at %04x:%02x:00, cannot hot-add\n", pci_name(dev),
-                        pci_domain_nr(parent), parent->number);
+               ctrl_err(ctrl, "Device %s already exists at %04x:%02x:00, cannot hot-add\n",
+                        pci_name(dev), pci_domain_nr(parent), parent->number);
                pci_dev_put(dev);
                ret = -EEXIST;
                goto out;
index ac69094e4b2089804ce41b00730d81c487adebf7..d062c008fc95ffa0f075a7189e9467bbed9737dd 100644 (file)
@@ -51,7 +51,7 @@ static LIST_HEAD(slot_list);
 #define dbg(format, arg...)                                    \
        do {                                                    \
                if (debug)                                      \
-                       printk (KERN_DEBUG "%s: " format "\n",  \
+                       printk(KERN_DEBUG "%s: " format "\n",   \
                                MY_NAME , ## arg);              \
        } while (0)
 #define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
@@ -128,18 +128,18 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
        dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
 
        switch (status) {
-               case 0:
-                       /*
-                        * Fill in code here to turn light off
-                        */
-                       break;
-
-               case 1:
-               default:
-                       /*
-                        * Fill in code here to turn light on
-                        */
-                       break;
+       case 0:
+               /*
+                * Fill in code here to turn light off
+                */
+               break;
+
+       case 1:
+       default:
+               /*
+                * Fill in code here to turn light on
+                */
+               break;
        }
 
        return retval;
@@ -153,12 +153,12 @@ static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value)
        dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
 
        switch (value) {
-               case 0:
-                       /* Specify a test here */
-                       break;
-               case 1:
-                       /* Specify another test here */
-                       break;
+       case 0:
+               /* Specify a test here */
+               break;
+       case 1:
+               /* Specify another test here */
+               break;
        }
 
        return retval;
index 984d708552f6f950d000a52ce5d1e9f225f2d728..93aa29f6d39c5b3a06e699f13f72a8c960b7da34 100644 (file)
@@ -39,6 +39,7 @@
 
 bool rpaphp_debug;
 LIST_HEAD(rpaphp_slot_head);
+EXPORT_SYMBOL_GPL(rpaphp_slot_head);
 
 #define DRIVER_VERSION "0.1"
 #define DRIVER_AUTHOR  "Linda Xie <lxie@us.ibm.com>"
@@ -88,7 +89,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
  * @hotplug_slot: slot to get status
  * @value: pointer to store status
  */
-static int get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
+static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
        int retval, level;
        struct slot *slot = (struct slot *)hotplug_slot->private;
@@ -104,14 +105,14 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
  * @hotplug_slot: slot to get status
  * @value: pointer to store status
  */
-static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
+static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
        struct slot *slot = (struct slot *)hotplug_slot->private;
        *value = slot->hotplug_slot->info->attention_status;
        return 0;
 }
 
-static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value)
+static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
        struct slot *slot = (struct slot *)hotplug_slot->private;
        int rc, state;
@@ -241,6 +242,7 @@ int rpaphp_get_drc_props(struct device_node *dn, int *drc_index,
 
        return -EINVAL;
 }
+EXPORT_SYMBOL_GPL(rpaphp_get_drc_props);
 
 static int is_php_type(char *drc_type)
 {
@@ -350,6 +352,7 @@ int rpaphp_add_slot(struct device_node *dn)
        /* XXX FIXME: reports a failure only if last entry in loop failed */
        return retval;
 }
+EXPORT_SYMBOL_GPL(rpaphp_add_slot);
 
 static void __exit cleanup_slots(void)
 {
@@ -443,7 +446,3 @@ struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {
 
 module_init(rpaphp_init);
 module_exit(rpaphp_exit);
-
-EXPORT_SYMBOL_GPL(rpaphp_add_slot);
-EXPORT_SYMBOL_GPL(rpaphp_slot_head);
-EXPORT_SYMBOL_GPL(rpaphp_get_drc_props);
index 613043f7576f302c75bb5727d4dff910dd0aaaf2..bada209998705732e488a9e5e4dbb0139a43ad90 100644 (file)
@@ -188,7 +188,7 @@ static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot,
        return 0;
 }
 
-static struct hotplug_slot * sn_hp_destroy(void)
+static struct hotplug_slot *sn_hp_destroy(void)
 {
        struct slot *slot;
        struct pci_slot *pci_slot;
@@ -250,15 +250,13 @@ static int sn_slot_enable(struct hotplug_slot *bss_hotplug_slot,
        }
 
        if (rc == PCI_L1_ERR) {
-               dev_dbg(&slot->pci_bus->self->dev,
-                       "L1 failure %d with message: %s",
+               dev_dbg(&slot->pci_bus->self->dev, "L1 failure %d with message: %s",
                        resp.resp_sub_errno, resp.resp_l1_msg);
                return -EPERM;
        }
 
        if (rc) {
-               dev_dbg(&slot->pci_bus->self->dev,
-                       "insert failed with error %d sub-error %d\n",
+               dev_dbg(&slot->pci_bus->self->dev, "insert failed with error %d sub-error %d\n",
                        rc, resp.resp_sub_errno);
                return -EIO;
        }
@@ -288,21 +286,18 @@ static int sn_slot_disable(struct hotplug_slot *bss_hotplug_slot,
        }
 
        if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc == PCI_EMPTY_33MHZ)) {
-               dev_dbg(&slot->pci_bus->self->dev,
-                       "Cannot remove last 33MHz card\n");
+               dev_dbg(&slot->pci_bus->self->dev, "Cannot remove last 33MHz card\n");
                return -EPERM;
        }
 
        if ((action == PCI_REQ_SLOT_ELIGIBLE) && (rc == PCI_L1_ERR)) {
-               dev_dbg(&slot->pci_bus->self->dev,
-                       "L1 failure %d with message \n%s\n",
+               dev_dbg(&slot->pci_bus->self->dev, "L1 failure %d with message \n%s\n",
                        resp.resp_sub_errno, resp.resp_l1_msg);
                return -EPERM;
        }
 
        if ((action == PCI_REQ_SLOT_ELIGIBLE) && rc) {
-               dev_dbg(&slot->pci_bus->self->dev,
-                       "remove failed with error %d sub-error %d\n",
+               dev_dbg(&slot->pci_bus->self->dev, "remove failed with error %d sub-error %d\n",
                        rc, resp.resp_sub_errno);
                return -EIO;
        }
@@ -417,8 +412,7 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
                phandle = acpi_device_handle(PCI_CONTROLLER(slot->pci_bus)->companion);
 
                if (acpi_bus_get_device(phandle, &pdevice)) {
-                       dev_dbg(&slot->pci_bus->self->dev,
-                               "no parent device, assuming NULL\n");
+                       dev_dbg(&slot->pci_bus->self->dev, "no parent device, assuming NULL\n");
                        pdevice = NULL;
                }
 
@@ -447,10 +441,8 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
 
                                ret = acpi_bus_scan(chandle);
                                if (ACPI_FAILURE(ret)) {
-                                       printk(KERN_ERR "%s: acpi_bus_scan "
-                                              "failed (0x%x) for slot %d "
-                                              "func %d\n", __func__,
-                                              ret, (int)(adr>>16),
+                                       printk(KERN_ERR "%s: acpi_bus_scan failed (0x%x) for slot %d func %d\n",
+                                              __func__, ret, (int)(adr>>16),
                                               (int)(adr&0xffff));
                                        /* try to continue on */
                                }
@@ -471,11 +463,9 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
        mutex_unlock(&sn_hotplug_mutex);
 
        if (rc == 0)
-               dev_dbg(&slot->pci_bus->self->dev,
-                       "insert operation successful\n");
+               dev_dbg(&slot->pci_bus->self->dev, "insert operation successful\n");
        else
-               dev_dbg(&slot->pci_bus->self->dev,
-                       "insert operation failed rc = %d\n", rc);
+               dev_dbg(&slot->pci_bus->self->dev, "insert operation failed rc = %d\n", rc);
 
        return rc;
 }
@@ -561,8 +551,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
                acpi_status ret;
                ret = acpi_unload_table_id(ssdt_id);
                if (ACPI_FAILURE(ret)) {
-                       printk(KERN_ERR "%s: acpi_unload_table_id "
-                              "failed (0x%x) for id %d\n",
+                       printk(KERN_ERR "%s: acpi_unload_table_id failed (0x%x) for id %d\n",
                               __func__, ret, ssdt_id);
                        /* try to continue on */
                }
index 61529097464d4402f19d22dd33b77433b16c5413..5897d516427baca60ef032f781e14ffec7db78bc 100644 (file)
@@ -180,7 +180,7 @@ int shpchp_configure_device(struct slot *p_slot);
 int shpchp_unconfigure_device(struct slot *p_slot);
 void cleanup_slots(struct controller *ctrl);
 void shpchp_queue_pushbutton_work(struct work_struct *work);
-int shpc_init( struct controller *ctrl, struct pci_dev *pdev);
+int shpc_init(struct controller *ctrl, struct pci_dev *pdev);
 
 static inline const char *slot_name(struct slot *slot)
 {
@@ -295,7 +295,7 @@ static inline void amd_pogo_errata_restore_misc_reg(struct slot *p_slot)
                pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MEM_BASE_LIMIT_OFFSET, rse_set);
        }
        /* restore MiscII register */
-       pci_read_config_dword( p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, &pcix_misc2_temp );
+       pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, &pcix_misc2_temp );
 
        if (p_slot->ctrl->pcix_misc2_reg & SERRFATALENABLE_MASK)
                pcix_misc2_temp |= SERRFATALENABLE_MASK;
index faf13abd5b99ded8d19fe2461c6807ac0e883ebf..294ef4b10cf198031da6a67e91c10ef965cffccf 100644 (file)
@@ -143,8 +143,7 @@ static int init_slots(struct controller *ctrl)
                snprintf(name, SLOT_NAME_SIZE, "%d", slot->number);
                hotplug_slot->ops = &shpchp_hotplug_slot_ops;
 
-               ctrl_dbg(ctrl, "Registering domain:bus:dev=%04x:%02x:%02x "
-                        "hp_slot=%x sun=%x slot_device_offset=%x\n",
+               ctrl_dbg(ctrl, "Registering domain:bus:dev=%04x:%02x:%02x hp_slot=%x sun=%x slot_device_offset=%x\n",
                         pci_domain_nr(ctrl->pci_dev->subordinate),
                         slot->bus, slot->device, slot->hp_slot, slot->number,
                         ctrl->slot_device_offset);
index 6efc2ec5e4db0823758a409eb95c2d3054a8ba48..a81fb67ea9a18a405dce84c2bd4b88aee4b9e6fc 100644 (file)
@@ -162,7 +162,7 @@ u8 shpchp_handle_power_fault(u8 hp_slot, struct controller *ctrl)
 
        p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
 
-       if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) {
+       if (!(p_slot->hpc_ops->query_power_fault(p_slot))) {
                /*
                 * Power fault Cleared
                 */
@@ -196,8 +196,8 @@ static int change_bus_speed(struct controller *ctrl, struct slot *p_slot,
 
        ctrl_dbg(ctrl, "Change speed to %d\n", speed);
        if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, speed))) {
-               ctrl_err(ctrl, "%s: Issue of set bus speed mode command "
-                        "failed\n", __func__);
+               ctrl_err(ctrl, "%s: Issue of set bus speed mode command failed\n",
+                        __func__);
                return WRONG_BUS_FREQUENCY;
        }
        return rc;
@@ -215,8 +215,8 @@ static int fix_bus_speed(struct controller *ctrl, struct slot *pslot,
         */
        if (flag) {
                if (asp < bsp) {
-                       ctrl_err(ctrl, "Speed of bus %x and adapter %x "
-                                "mismatch\n", bsp, asp);
+                       ctrl_err(ctrl, "Speed of bus %x and adapter %x mismatch\n",
+                                bsp, asp);
                        rc = WRONG_BUS_FREQUENCY;
                }
                return rc;
@@ -250,8 +250,7 @@ static int board_added(struct slot *p_slot)
 
        hp_slot = p_slot->device - ctrl->slot_device_offset;
 
-       ctrl_dbg(ctrl,
-                "%s: p_slot->device, slot_offset, hp_slot = %d, %d ,%d\n",
+       ctrl_dbg(ctrl, "%s: p_slot->device, slot_offset, hp_slot = %d, %d ,%d\n",
                 __func__, p_slot->device, ctrl->slot_device_offset, hp_slot);
 
        /* Power on slot without connecting to bus */
@@ -263,8 +262,8 @@ static int board_added(struct slot *p_slot)
 
        if ((ctrl->pci_dev->vendor == 0x8086) && (ctrl->pci_dev->device == 0x0332)) {
                if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, PCI_SPEED_33MHz))) {
-                       ctrl_err(ctrl, "%s: Issue of set bus speed mode command"
-                                " failed\n", __func__);
+                       ctrl_err(ctrl, "%s: Issue of set bus speed mode command failed\n",
+                                __func__);
                        return WRONG_BUS_FREQUENCY;
                }
 
@@ -277,8 +276,7 @@ static int board_added(struct slot *p_slot)
 
        rc = p_slot->hpc_ops->get_adapter_speed(p_slot, &asp);
        if (rc) {
-               ctrl_err(ctrl, "Can't get adapter speed or "
-                        "bus mode mismatch\n");
+               ctrl_err(ctrl, "Can't get adapter speed or bus mode mismatch\n");
                return WRONG_BUS_FREQUENCY;
        }
 
@@ -289,8 +287,8 @@ static int board_added(struct slot *p_slot)
        if (!list_empty(&ctrl->pci_dev->subordinate->devices))
                slots_not_empty = 1;
 
-       ctrl_dbg(ctrl, "%s: slots_not_empty %d, adapter_speed %d, bus_speed %d,"
-                " max_bus_speed %d\n", __func__, slots_not_empty, asp,
+       ctrl_dbg(ctrl, "%s: slots_not_empty %d, adapter_speed %d, bus_speed %d, max_bus_speed %d\n",
+                __func__, slots_not_empty, asp,
                 bsp, msp);
 
        rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, asp, bsp, msp);
@@ -490,12 +488,12 @@ static void handle_button_press_event(struct slot *p_slot)
                p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
                if (getstatus) {
                        p_slot->state = BLINKINGOFF_STATE;
-                       ctrl_info(ctrl, "PCI slot #%s - powering off due to "
-                                 "button press.\n", slot_name(p_slot));
+                       ctrl_info(ctrl, "PCI slot #%s - powering off due to button press\n",
+                                 slot_name(p_slot));
                } else {
                        p_slot->state = BLINKINGON_STATE;
-                       ctrl_info(ctrl, "PCI slot #%s - powering on due to "
-                                 "button press.\n", slot_name(p_slot));
+                       ctrl_info(ctrl, "PCI slot #%s - powering on due to button press\n",
+                                 slot_name(p_slot));
                }
                /* blink green LED and turn off amber */
                p_slot->hpc_ops->green_led_blink(p_slot);
@@ -518,8 +516,8 @@ static void handle_button_press_event(struct slot *p_slot)
                else
                        p_slot->hpc_ops->green_led_off(p_slot);
                p_slot->hpc_ops->set_attention_status(p_slot, 0);
-               ctrl_info(ctrl, "PCI slot #%s - action canceled due to "
-                         "button press\n", slot_name(p_slot));
+               ctrl_info(ctrl, "PCI slot #%s - action canceled due to button press\n",
+                         slot_name(p_slot));
                p_slot->state = STATIC_STATE;
                break;
        case POWEROFF_STATE:
index 2d7f474ca0ec7036d023dbc1b413375bb0fd37ae..29e22352822cb64ee7c3ae8bf3341e1d19abac1d 100644 (file)
@@ -341,8 +341,7 @@ static int shpc_write_cmd(struct slot *slot, u8 t_slot, u8 cmd)
 
        cmd_status = hpc_check_cmd_status(slot->ctrl);
        if (cmd_status) {
-               ctrl_err(ctrl,
-                        "Failed to issued command 0x%x (error code = %d)\n",
+               ctrl_err(ctrl, "Failed to issued command 0x%x (error code = %d)\n",
                         cmd, cmd_status);
                retval = -EIO;
        }
@@ -404,7 +403,7 @@ static int hpc_get_attention_status(struct slot *slot, u8 *status)
        return 0;
 }
 
-static int hpc_get_power_status(struct slot * slot, u8 *status)
+static int hpc_get_power_status(struct slot *slot, u8 *status)
 {
        struct controller *ctrl = slot->ctrl;
        u32 slot_reg = shpc_readl(ctrl, SLOT_REG(slot->hp_slot));
@@ -528,7 +527,7 @@ static int hpc_get_mode1_ECC_cap(struct slot *slot, u8 *mode)
        return retval;
 }
 
-static int hpc_query_power_fault(struct slot * slot)
+static int hpc_query_power_fault(struct slot *slot)
 {
        struct controller *ctrl = slot->ctrl;
        u32 slot_reg = shpc_readl(ctrl, SLOT_REG(slot->hp_slot));
@@ -614,7 +613,7 @@ static void hpc_release_ctlr(struct controller *ctrl)
        release_mem_region(ctrl->mmio_base, ctrl->mmio_size);
 }
 
-static int hpc_power_on_slot(struct slot * slot)
+static int hpc_power_on_slot(struct slot *slot)
 {
        int retval;
 
@@ -625,7 +624,7 @@ static int hpc_power_on_slot(struct slot * slot)
        return retval;
 }
 
-static int hpc_slot_enable(struct slot * slot)
+static int hpc_slot_enable(struct slot *slot)
 {
        int retval;
 
@@ -638,7 +637,7 @@ static int hpc_slot_enable(struct slot * slot)
        return retval;
 }
 
-static int hpc_slot_disable(struct slot * slot)
+static int hpc_slot_disable(struct slot *slot)
 {
        int retval;
 
@@ -720,7 +719,7 @@ static int shpc_get_cur_bus_speed(struct controller *ctrl)
 }
 
 
-static int hpc_set_bus_speed_mode(struct slot * slot, enum pci_bus_speed value)
+static int hpc_set_bus_speed_mode(struct slot *slot, enum pci_bus_speed value)
 {
        int retval;
        struct controller *ctrl = slot->ctrl;
@@ -974,8 +973,8 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev)
                for (i = 0; i < 9 + num_slots; i++) {
                        rc = shpc_indirect_read(ctrl, i, &tempdword);
                        if (rc) {
-                               ctrl_err(ctrl,
-                                        "Cannot read creg (index = %d)\n", i);
+                               ctrl_err(ctrl, "Cannot read creg (index = %d)\n",
+                                        i);
                                goto abort;
                        }
                        ctrl_dbg(ctrl, " offset %d: value %x\n", i, tempdword);
@@ -1060,10 +1059,8 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev)
                /* Installs the interrupt handler */
                rc = pci_enable_msi(pdev);
                if (rc) {
-                       ctrl_info(ctrl,
-                                 "Can't get msi for the hotplug controller\n");
-                       ctrl_info(ctrl,
-                                 "Use INTx for the hotplug controller\n");
+                       ctrl_info(ctrl, "Can't get msi for the hotplug controller\n");
+                       ctrl_info(ctrl, "Use INTx for the hotplug controller\n");
                }
 
                rc = request_irq(ctrl->pci_dev->irq, shpc_isr, IRQF_SHARED,
@@ -1071,8 +1068,8 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev)
                ctrl_dbg(ctrl, "request_irq %d (returns %d)\n",
                         ctrl->pci_dev->irq, rc);
                if (rc) {
-                       ctrl_err(ctrl, "Can't get irq %d for the hotplug "
-                                "controller\n", ctrl->pci_dev->irq);
+                       ctrl_err(ctrl, "Can't get irq %d for the hotplug controller\n",
+                                ctrl->pci_dev->irq);
                        goto abort_iounmap;
                }
        }
index 9202d133485ceb13e4ceb0ef78179a61cb161367..469454e0cc48d8eca080dc2f5035f87d83769f53 100644 (file)
@@ -46,9 +46,9 @@ int shpchp_configure_device(struct slot *p_slot)
 
        dev = pci_get_slot(parent, PCI_DEVFN(p_slot->device, 0));
        if (dev) {
-               ctrl_err(ctrl, "Device %s already exists "
-                        "at %04x:%02x:%02x, cannot hot-add\n", pci_name(dev),
-                        pci_domain_nr(parent), p_slot->bus, p_slot->device);
+               ctrl_err(ctrl, "Device %s already exists at %04x:%02x:%02x, cannot hot-add\n",
+                        pci_name(dev), pci_domain_nr(parent),
+                        p_slot->bus, p_slot->device);
                pci_dev_put(dev);
                ret = -EINVAL;
                goto out;
index e8c31fe20566fbd839c7c236b234ab873f2eb428..52875b36046384fd8937a15d7a9d9b9cd06bebce 100644 (file)
@@ -38,7 +38,7 @@
 static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct pci_dev *pdev;
-       char * out = buf;
+       char *out = buf;
        int index, busnr;
        struct resource *res;
        struct pci_bus *bus;
index d68b030ab5338b2f945a0dce447b8641eeb629ac..a94dd2c4183a0ddc7ac118b1cfc41a7014d2fc4c 100644 (file)
@@ -102,7 +102,7 @@ int __ht_create_irq(struct pci_dev *dev, int idx, ht_irq_update_t *update)
        spin_unlock_irqrestore(&ht_irq_lock, flags);
 
        max_irq = (data >> 16) & 0xff;
-       if ( idx > max_irq)
+       if (idx > max_irq)
                return -EINVAL;
 
        cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
@@ -131,6 +131,7 @@ int __ht_create_irq(struct pci_dev *dev, int idx, ht_irq_update_t *update)
 
        return irq;
 }
+EXPORT_SYMBOL(__ht_create_irq);
 
 /**
  * ht_create_irq - create an irq and attach it to a device.
@@ -146,6 +147,7 @@ int ht_create_irq(struct pci_dev *dev, int idx)
 {
        return __ht_create_irq(dev, idx, NULL);
 }
+EXPORT_SYMBOL(ht_create_irq);
 
 /**
  * ht_destroy_irq - destroy an irq created with ht_create_irq
@@ -165,7 +167,4 @@ void ht_destroy_irq(unsigned int irq)
 
        kfree(cfg);
 }
-
-EXPORT_SYMBOL(__ht_create_irq);
-EXPORT_SYMBOL(ht_create_irq);
 EXPORT_SYMBOL(ht_destroy_irq);
index 27a7e67ddfe4c53617eea8c506fe96683a3d668d..13f3d303727296821f25fde0b7acd22106a3fc3b 100644 (file)
@@ -413,7 +413,7 @@ static void free_msi_irqs(struct pci_dev *dev)
        if (dev->msi_irq_groups) {
                sysfs_remove_groups(&dev->dev.kobj, dev->msi_irq_groups);
                msi_attrs = dev->msi_irq_groups[0]->attrs;
-               list_for_each_entry(entry, &dev->msi_list, list) {
+               while (msi_attrs[count]) {
                        dev_attr = container_of(msi_attrs[count],
                                                struct device_attribute, attr);
                        kfree(dev_attr->attr.name);
@@ -980,8 +980,7 @@ int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec)
 
        /* Check whether driver already requested for MSI irq */
        if (dev->msi_enabled) {
-               dev_info(&dev->dev, "can't enable MSI-X "
-                      "(MSI IRQ already assigned)\n");
+               dev_info(&dev->dev, "can't enable MSI-X (MSI IRQ already assigned)\n");
                return -EINVAL;
        }
        status = msix_capability_init(dev, entries, nvec);
index 837d71f5390b20fd888a219139e7abe9dd830238..3f8e3dbcaa7cbcc4d9cabb4b89685c56461f0a7d 100644 (file)
@@ -77,6 +77,7 @@ int pci_add_dynid(struct pci_driver *drv,
 
        return retval;
 }
+EXPORT_SYMBOL_GPL(pci_add_dynid);
 
 static void pci_free_dynids(struct pci_driver *drv)
 {
@@ -98,15 +99,15 @@ static void pci_free_dynids(struct pci_driver *drv)
  *
  * Allow PCI IDs to be added to an existing driver via sysfs.
  */
-static ssize_t
-store_new_id(struct device_driver *driver, const char *buf, size_t count)
+static ssize_t store_new_id(struct device_driver *driver, const char *buf,
+                           size_t count)
 {
        struct pci_driver *pdrv = to_pci_driver(driver);
        const struct pci_device_id *ids = pdrv->id_table;
-       __u32 vendor, device, subvendor=PCI_ANY_ID,
-               subdevice=PCI_ANY_ID, class=0, class_mask=0;
-       unsigned long driver_data=0;
-       int fields=0;
+       __u32 vendor, device, subvendor = PCI_ANY_ID,
+               subdevice = PCI_ANY_ID, class = 0, class_mask = 0;
+       unsigned long driver_data = 0;
+       int fields = 0;
        int retval = 0;
 
        fields = sscanf(buf, "%x %x %x %x %x %x %lx",
@@ -166,8 +167,8 @@ static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
  *
  * Removes a dynamic pci device ID to this driver.
  */
-static ssize_t
-store_remove_id(struct device_driver *driver, const char *buf, size_t count)
+static ssize_t store_remove_id(struct device_driver *driver, const char *buf,
+                              size_t count)
 {
        struct pci_dynid *dynid, *n;
        struct pci_driver *pdrv = to_pci_driver(driver);
@@ -235,6 +236,7 @@ const struct pci_device_id *pci_match_id(const struct pci_device_id *ids,
        }
        return NULL;
 }
+EXPORT_SYMBOL(pci_match_id);
 
 static const struct pci_device_id pci_device_id_any = {
        .vendor = PCI_ANY_ID,
@@ -372,8 +374,7 @@ static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev,
  * returns 0 on success, else error.
  * side-effect: pci_dev->driver is set to drv when drv claims pci_dev.
  */
-static int
-__pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev)
+static int __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev)
 {
        const struct pci_device_id *id;
        int error = 0;
@@ -390,7 +391,7 @@ __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev)
        return error;
 }
 
-static int pci_device_probe(struct device * dev)
+static int pci_device_probe(struct device *dev)
 {
        int error = 0;
        struct pci_driver *drv;
@@ -406,10 +407,10 @@ static int pci_device_probe(struct device * dev)
        return error;
 }
 
-static int pci_device_remove(struct device * dev)
+static int pci_device_remove(struct device *dev)
 {
-       struct pci_dev * pci_dev = to_pci_dev(dev);
-       struct pci_driver * drv = pci_dev->driver;
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       struct pci_driver *drv = pci_dev->driver;
 
        if (drv) {
                if (drv->remove) {
@@ -537,8 +538,8 @@ static int pci_pm_reenable_device(struct pci_dev *pci_dev)
 
 static int pci_legacy_suspend(struct device *dev, pm_message_t state)
 {
-       struct pci_dev * pci_dev = to_pci_dev(dev);
-       struct pci_driver * drv = pci_dev->driver;
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       struct pci_driver *drv = pci_dev->driver;
 
        if (drv && drv->suspend) {
                pci_power_t prev = pci_dev->current_state;
@@ -564,8 +565,8 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state)
 
 static int pci_legacy_suspend_late(struct device *dev, pm_message_t state)
 {
-       struct pci_dev * pci_dev = to_pci_dev(dev);
-       struct pci_driver * drv = pci_dev->driver;
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       struct pci_driver *drv = pci_dev->driver;
 
        if (drv && drv->suspend_late) {
                pci_power_t prev = pci_dev->current_state;
@@ -595,8 +596,8 @@ static int pci_legacy_suspend_late(struct device *dev, pm_message_t state)
 
 static int pci_legacy_resume_early(struct device *dev)
 {
-       struct pci_dev * pci_dev = to_pci_dev(dev);
-       struct pci_driver * drv = pci_dev->driver;
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       struct pci_driver *drv = pci_dev->driver;
 
        return drv && drv->resume_early ?
                        drv->resume_early(pci_dev) : 0;
@@ -604,8 +605,8 @@ static int pci_legacy_resume_early(struct device *dev)
 
 static int pci_legacy_resume(struct device *dev)
 {
-       struct pci_dev * pci_dev = to_pci_dev(dev);
-       struct pci_driver * drv = pci_dev->driver;
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       struct pci_driver *drv = pci_dev->driver;
 
        pci_fixup_device(pci_fixup_resume, pci_dev);
 
@@ -1255,6 +1256,7 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
        /* register with core */
        return driver_register(&drv->driver);
 }
+EXPORT_SYMBOL(__pci_register_driver);
 
 /**
  * pci_unregister_driver - unregister a pci driver
@@ -1266,12 +1268,12 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
  * driverless.
  */
 
-void
-pci_unregister_driver(struct pci_driver *drv)
+void pci_unregister_driver(struct pci_driver *drv)
 {
        driver_unregister(&drv->driver);
        pci_free_dynids(drv);
 }
+EXPORT_SYMBOL(pci_unregister_driver);
 
 static struct pci_driver pci_compat_driver = {
        .name = "compat"
@@ -1284,19 +1286,19 @@ static struct pci_driver pci_compat_driver = {
  * Returns the appropriate pci_driver structure or %NULL if there is no
  * registered driver for the device.
  */
-struct pci_driver *
-pci_dev_driver(const struct pci_dev *dev)
+struct pci_driver *pci_dev_driver(const struct pci_dev *dev)
 {
        if (dev->driver)
                return dev->driver;
        else {
                int i;
-               for(i=0; i<=PCI_ROM_RESOURCE; i++)
+               for (i = 0; i <= PCI_ROM_RESOURCE; i++)
                        if (dev->resource[i].flags & IORESOURCE_BUSY)
                                return &pci_compat_driver;
        }
        return NULL;
 }
+EXPORT_SYMBOL(pci_dev_driver);
 
 /**
  * pci_bus_match - Tell if a PCI device structure has a matching PCI device id structure
@@ -1342,6 +1344,7 @@ struct pci_dev *pci_dev_get(struct pci_dev *dev)
                get_device(&dev->dev);
        return dev;
 }
+EXPORT_SYMBOL(pci_dev_get);
 
 /**
  * pci_dev_put - release a use of the pci device structure
@@ -1355,6 +1358,7 @@ void pci_dev_put(struct pci_dev *dev)
        if (dev)
                put_device(&dev->dev);
 }
+EXPORT_SYMBOL(pci_dev_put);
 
 static int pci_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
@@ -1400,19 +1404,10 @@ struct bus_type pci_bus_type = {
        .drv_groups     = pci_drv_groups,
        .pm             = PCI_PM_OPS_PTR,
 };
+EXPORT_SYMBOL(pci_bus_type);
 
 static int __init pci_driver_init(void)
 {
        return bus_register(&pci_bus_type);
 }
-
 postcore_initcall(pci_driver_init);
-
-EXPORT_SYMBOL_GPL(pci_add_dynid);
-EXPORT_SYMBOL(pci_match_id);
-EXPORT_SYMBOL(__pci_register_driver);
-EXPORT_SYMBOL(pci_unregister_driver);
-EXPORT_SYMBOL(pci_dev_driver);
-EXPORT_SYMBOL(pci_bus_type);
-EXPORT_SYMBOL(pci_dev_get);
-EXPORT_SYMBOL(pci_dev_put);
index 45113daaa778f384e7796a3ca43b6304e30dbf84..a3fbe2012ea3ebe9f0d2749431cc3003c99ff923 100644 (file)
@@ -40,9 +40,8 @@ enum smbios_attr_enum {
        SMBIOS_ATTR_INSTANCE_SHOW,
 };
 
-static size_t
-find_smbios_instance_string(struct pci_dev *pdev, char *buf,
-                           enum smbios_attr_enum attribute)
+static size_t find_smbios_instance_string(struct pci_dev *pdev, char *buf,
+                                         enum smbios_attr_enum attribute)
 {
        const struct dmi_device *dmi;
        struct dmi_dev_onboard *donboard;
@@ -74,9 +73,8 @@ find_smbios_instance_string(struct pci_dev *pdev, char *buf,
        return 0;
 }
 
-static umode_t
-smbios_instance_string_exist(struct kobject *kobj, struct attribute *attr,
-                            int n)
+static umode_t smbios_instance_string_exist(struct kobject *kobj,
+                                           struct attribute *attr, int n)
 {
        struct device *dev;
        struct pci_dev *pdev;
@@ -88,8 +86,8 @@ smbios_instance_string_exist(struct kobject *kobj, struct attribute *attr,
                                           S_IRUGO : 0;
 }
 
-static ssize_t
-smbioslabel_show(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t smbioslabel_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
 {
        struct pci_dev *pdev;
        pdev = to_pci_dev(dev);
@@ -98,9 +96,8 @@ smbioslabel_show(struct device *dev, struct device_attribute *attr, char *buf)
                                           SMBIOS_ATTR_LABEL_SHOW);
 }
 
-static ssize_t
-smbiosinstance_show(struct device *dev,
-                   struct device_attribute *attr, char *buf)
+static ssize_t smbiosinstance_show(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
 {
        struct pci_dev *pdev;
        pdev = to_pci_dev(dev);
@@ -130,26 +127,22 @@ static struct attribute_group smbios_attr_group = {
        .is_visible = smbios_instance_string_exist,
 };
 
-static int
-pci_create_smbiosname_file(struct pci_dev *pdev)
+static int pci_create_smbiosname_file(struct pci_dev *pdev)
 {
        return sysfs_create_group(&pdev->dev.kobj, &smbios_attr_group);
 }
 
-static void
-pci_remove_smbiosname_file(struct pci_dev *pdev)
+static void pci_remove_smbiosname_file(struct pci_dev *pdev)
 {
        sysfs_remove_group(&pdev->dev.kobj, &smbios_attr_group);
 }
 #else
-static inline int
-pci_create_smbiosname_file(struct pci_dev *pdev)
+static inline int pci_create_smbiosname_file(struct pci_dev *pdev)
 {
        return -1;
 }
 
-static inline void
-pci_remove_smbiosname_file(struct pci_dev *pdev)
+static inline void pci_remove_smbiosname_file(struct pci_dev *pdev)
 {
 }
 #endif
@@ -175,8 +168,8 @@ static void dsm_label_utf16s_to_utf8s(union acpi_object *obj, char *buf)
        buf[len] = '\n';
 }
 
-static int
-dsm_get_label(struct device *dev, char *buf, enum acpi_attr_enum attr)
+static int dsm_get_label(struct device *dev, char *buf,
+                        enum acpi_attr_enum attr)
 {
        acpi_handle handle;
        union acpi_object *obj, *tmp;
@@ -212,8 +205,7 @@ dsm_get_label(struct device *dev, char *buf, enum acpi_attr_enum attr)
        return len;
 }
 
-static bool
-device_has_dsm(struct device *dev)
+static bool device_has_dsm(struct device *dev)
 {
        acpi_handle handle;
 
@@ -225,8 +217,8 @@ device_has_dsm(struct device *dev)
                                1 << DEVICE_LABEL_DSM);
 }
 
-static umode_t
-acpi_index_string_exist(struct kobject *kobj, struct attribute *attr, int n)
+static umode_t acpi_index_string_exist(struct kobject *kobj,
+                                      struct attribute *attr, int n)
 {
        struct device *dev;
 
@@ -238,14 +230,14 @@ acpi_index_string_exist(struct kobject *kobj, struct attribute *attr, int n)
        return 0;
 }
 
-static ssize_t
-acpilabel_show(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t acpilabel_show(struct device *dev,
+                             struct device_attribute *attr, char *buf)
 {
        return dsm_get_label(dev, buf, ACPI_ATTR_LABEL_SHOW);
 }
 
-static ssize_t
-acpiindex_show(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t acpiindex_show(struct device *dev,
+                             struct device_attribute *attr, char *buf)
 {
        return dsm_get_label(dev, buf, ACPI_ATTR_INDEX_SHOW);
 }
@@ -271,33 +263,28 @@ static struct attribute_group acpi_attr_group = {
        .is_visible = acpi_index_string_exist,
 };
 
-static int
-pci_create_acpi_index_label_files(struct pci_dev *pdev)
+static int pci_create_acpi_index_label_files(struct pci_dev *pdev)
 {
        return sysfs_create_group(&pdev->dev.kobj, &acpi_attr_group);
 }
 
-static int
-pci_remove_acpi_index_label_files(struct pci_dev *pdev)
+static int pci_remove_acpi_index_label_files(struct pci_dev *pdev)
 {
        sysfs_remove_group(&pdev->dev.kobj, &acpi_attr_group);
        return 0;
 }
 #else
-static inline int
-pci_create_acpi_index_label_files(struct pci_dev *pdev)
+static inline int pci_create_acpi_index_label_files(struct pci_dev *pdev)
 {
        return -1;
 }
 
-static inline int
-pci_remove_acpi_index_label_files(struct pci_dev *pdev)
+static inline int pci_remove_acpi_index_label_files(struct pci_dev *pdev)
 {
        return -1;
 }
 
-static inline bool
-device_has_dsm(struct device *dev)
+static inline bool device_has_dsm(struct device *dev)
 {
        return false;
 }
index 2ff77509d8e53b505dc3f73b35567920491b2f7d..886fb3570278c87ddde5131c34541bfc60eae096 100644 (file)
@@ -55,7 +55,7 @@ static int __init pci_stub_init(void)
        p = ids;
        while ((id = strsep(&p, ","))) {
                unsigned int vendor, device, subvendor = PCI_ANY_ID,
-                       subdevice = PCI_ANY_ID, class=0, class_mask=0;
+                       subdevice = PCI_ANY_ID, class = 0, class_mask = 0;
                int fields;
 
                if (!strlen(id))
index 84c350994b06097d36e3fffa0b5465f11376d4db..9ff0a901ecf7ed2691418845bf46d82bb2d68754 100644 (file)
@@ -41,8 +41,8 @@ field##_show(struct device *dev, struct device_attribute *attr, char *buf)                            \
 {                                                                      \
        struct pci_dev *pdev;                                           \
                                                                        \
-       pdev = to_pci_dev (dev);                                        \
-       return sprintf (buf, format_string, pdev->field);               \
+       pdev = to_pci_dev(dev);                                         \
+       return sprintf(buf, format_string, pdev->field);                \
 }                                                                      \
 static DEVICE_ATTR_RO(field)
 
@@ -58,7 +58,7 @@ static ssize_t broken_parity_status_show(struct device *dev,
                                         char *buf)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
-       return sprintf (buf, "%u\n", pdev->broken_parity_status);
+       return sprintf(buf, "%u\n", pdev->broken_parity_status);
 }
 
 static ssize_t broken_parity_status_store(struct device *dev,
@@ -77,10 +77,8 @@ static ssize_t broken_parity_status_store(struct device *dev,
 }
 static DEVICE_ATTR_RW(broken_parity_status);
 
-static ssize_t pci_dev_show_local_cpu(struct device *dev,
-               int type,
-               struct device_attribute *attr,
-               char *buf)
+static ssize_t pci_dev_show_local_cpu(struct device *dev, int type,
+                                     struct device_attribute *attr, char *buf)
 {
        const struct cpumask *mask;
        int len;
@@ -101,14 +99,14 @@ static ssize_t pci_dev_show_local_cpu(struct device *dev,
 }
 
 static ssize_t local_cpus_show(struct device *dev,
-                       struct device_attribute *attr, char *buf)
+                              struct device_attribute *attr, char *buf)
 {
        return pci_dev_show_local_cpu(dev, 1, attr, buf);
 }
 static DEVICE_ATTR_RO(local_cpus);
 
 static ssize_t local_cpulist_show(struct device *dev,
-                       struct device_attribute *attr, char *buf)
+                                 struct device_attribute *attr, char *buf)
 {
        return pci_dev_show_local_cpu(dev, 0, attr, buf);
 }
@@ -117,8 +115,7 @@ static DEVICE_ATTR_RO(local_cpulist);
 /*
  * PCI Bus Class Devices
  */
-static ssize_t pci_bus_show_cpuaffinity(struct device *dev,
-                                       int type,
+static ssize_t pci_bus_show_cpuaffinity(struct device *dev, int type,
                                        struct device_attribute *attr,
                                        char *buf)
 {
@@ -149,11 +146,11 @@ static ssize_t cpulistaffinity_show(struct device *dev,
 static DEVICE_ATTR_RO(cpulistaffinity);
 
 /* show resources */
-static ssize_t
-resource_show(struct device * dev, struct device_attribute *attr, char * buf)
+static ssize_t resource_show(struct device *dev, struct device_attribute *attr,
+                            char *buf)
 {
-       struct pci_dev * pci_dev = to_pci_dev(dev);
-       char * str = buf;
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       char *str = buf;
        int i;
        int max;
        resource_size_t start, end;
@@ -166,7 +163,7 @@ resource_show(struct device * dev, struct device_attribute *attr, char * buf)
        for (i = 0; i < max; i++) {
                struct resource *res =  &pci_dev->resource[i];
                pci_resource_to_user(pci_dev, i, res, &start, &end);
-               str += sprintf(str,"0x%016llx 0x%016llx 0x%016llx\n",
+               str += sprintf(str, "0x%016llx 0x%016llx 0x%016llx\n",
                               (unsigned long long)start,
                               (unsigned long long)end,
                               (unsigned long long)res->flags);
@@ -175,7 +172,8 @@ resource_show(struct device * dev, struct device_attribute *attr, char * buf)
 }
 static DEVICE_ATTR_RO(resource);
 
-static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+                            char *buf)
 {
        struct pci_dev *pci_dev = to_pci_dev(dev);
 
@@ -187,9 +185,8 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RO(modalias);
 
-static ssize_t enabled_store(struct device *dev,
-                            struct device_attribute *attr, const char *buf,
-                            size_t count)
+static ssize_t enabled_store(struct device *dev, struct device_attribute *attr,
+                            const char *buf, size_t count)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
        unsigned long val;
@@ -213,57 +210,56 @@ static ssize_t enabled_store(struct device *dev,
        return result < 0 ? result : count;
 }
 
-static ssize_t enabled_show(struct device *dev,
-                           struct device_attribute *attr, char *buf)
+static ssize_t enabled_show(struct device *dev, struct device_attribute *attr,
+                           char *buf)
 {
        struct pci_dev *pdev;
 
-       pdev = to_pci_dev (dev);
-       return sprintf (buf, "%u\n", atomic_read(&pdev->enable_cnt));
+       pdev = to_pci_dev(dev);
+       return sprintf(buf, "%u\n", atomic_read(&pdev->enable_cnt));
 }
 static DEVICE_ATTR_RW(enabled);
 
 #ifdef CONFIG_NUMA
-static ssize_t
-numa_node_show(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t numa_node_show(struct device *dev, struct device_attribute *attr,
+                             char *buf)
 {
-       return sprintf (buf, "%d\n", dev->numa_node);
+       return sprintf(buf, "%d\n", dev->numa_node);
 }
 static DEVICE_ATTR_RO(numa_node);
 #endif
 
-static ssize_t
-dma_mask_bits_show(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t dma_mask_bits_show(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
 
-       return sprintf (buf, "%d\n", fls64(pdev->dma_mask));
+       return sprintf(buf, "%d\n", fls64(pdev->dma_mask));
 }
 static DEVICE_ATTR_RO(dma_mask_bits);
 
-static ssize_t
-consistent_dma_mask_bits_show(struct device *dev, struct device_attribute *attr,
-                                char *buf)
+static ssize_t consistent_dma_mask_bits_show(struct device *dev,
+                                            struct device_attribute *attr,
+                                            char *buf)
 {
-       return sprintf (buf, "%d\n", fls64(dev->coherent_dma_mask));
+       return sprintf(buf, "%d\n", fls64(dev->coherent_dma_mask));
 }
 static DEVICE_ATTR_RO(consistent_dma_mask_bits);
 
-static ssize_t
-msi_bus_show(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t msi_bus_show(struct device *dev, struct device_attribute *attr,
+                           char *buf)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
 
        if (!pdev->subordinate)
                return 0;
 
-       return sprintf (buf, "%u\n",
-                       !(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI));
+       return sprintf(buf, "%u\n",
+                      !(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI));
 }
 
-static ssize_t
-msi_bus_store(struct device *dev, struct device_attribute *attr,
-             const char *buf, size_t count)
+static ssize_t msi_bus_store(struct device *dev, struct device_attribute *attr,
+                            const char *buf, size_t count)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
        unsigned long val;
@@ -290,8 +286,8 @@ msi_bus_store(struct device *dev, struct device_attribute *attr,
            !!val) {
                pdev->subordinate->bus_flags ^= PCI_BUS_FLAGS_NO_MSI;
 
-               dev_warn(&pdev->dev, "forced subordinate bus to%s support MSI,"
-                        " bad things could happen\n", val ? "" : " not");
+               dev_warn(&pdev->dev, "forced subordinate bus to%s support MSI, bad things could happen\n",
+                        val ? "" : " not");
        }
 
        return count;
@@ -331,9 +327,9 @@ const struct attribute_group *pci_bus_groups[] = {
        NULL,
 };
 
-static ssize_t
-dev_rescan_store(struct device *dev, struct device_attribute *attr,
-                const char *buf, size_t count)
+static ssize_t dev_rescan_store(struct device *dev,
+                               struct device_attribute *attr, const char *buf,
+                               size_t count)
 {
        unsigned long val;
        struct pci_dev *pdev = to_pci_dev(dev);
@@ -352,9 +348,8 @@ static struct device_attribute dev_rescan_attr = __ATTR(rescan,
                                                        (S_IWUSR|S_IWGRP),
                                                        NULL, dev_rescan_store);
 
-static ssize_t
-remove_store(struct device *dev, struct device_attribute *attr,
-            const char *buf, size_t count)
+static ssize_t remove_store(struct device *dev, struct device_attribute *attr,
+                           const char *buf, size_t count)
 {
        unsigned long val;
 
@@ -369,9 +364,9 @@ static struct device_attribute dev_remove_attr = __ATTR(remove,
                                                        (S_IWUSR|S_IWGRP),
                                                        NULL, remove_store);
 
-static ssize_t
-dev_bus_rescan_store(struct device *dev, struct device_attribute *attr,
-                const char *buf, size_t count)
+static ssize_t dev_bus_rescan_store(struct device *dev,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t count)
 {
        unsigned long val;
        struct pci_bus *bus = to_pci_bus(dev);
@@ -412,7 +407,7 @@ static ssize_t d3cold_allowed_show(struct device *dev,
                                   struct device_attribute *attr, char *buf)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
-       return sprintf (buf, "%u\n", pdev->d3cold_allowed);
+       return sprintf(buf, "%u\n", pdev->d3cold_allowed);
 }
 static DEVICE_ATTR_RW(d3cold_allowed);
 #endif
@@ -607,8 +602,8 @@ const struct attribute_group *pcibus_groups[] = {
        NULL,
 };
 
-static ssize_t
-boot_vga_show(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t boot_vga_show(struct device *dev, struct device_attribute *attr,
+                            char *buf)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
        struct pci_dev *vga_dev = vga_default_device();
@@ -622,22 +617,21 @@ boot_vga_show(struct device *dev, struct device_attribute *attr, char *buf)
 }
 static struct device_attribute vga_attr = __ATTR_RO(boot_vga);
 
-static ssize_t
-pci_read_config(struct file *filp, struct kobject *kobj,
-               struct bin_attribute *bin_attr,
-               char *buf, loff_t off, size_t count)
+static ssize_t pci_read_config(struct file *filp, struct kobject *kobj,
+                              struct bin_attribute *bin_attr, char *buf,
+                              loff_t off, size_t count)
 {
-       struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
+       struct pci_dev *dev = to_pci_dev(container_of(kobj, struct device,
+                                                     kobj));
        unsigned int size = 64;
        loff_t init_off = off;
-       u8 *data = (u8*) buf;
+       u8 *data = (u8 *) buf;
 
        /* Several chips lock up trying to read undefined config space */
-       if (security_capable(filp->f_cred, &init_user_ns, CAP_SYS_ADMIN) == 0) {
+       if (security_capable(filp->f_cred, &init_user_ns, CAP_SYS_ADMIN) == 0)
                size = dev->cfg_size;
-       } else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) {
+       else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
                size = 128;
-       }
 
        if (off > size)
                return 0;
@@ -700,15 +694,15 @@ pci_read_config(struct file *filp, struct kobject *kobj,
        return count;
 }
 
-static ssize_t
-pci_write_config(struct file* filp, struct kobject *kobj,
-                struct bin_attribute *bin_attr,
-                char *buf, loff_t off, size_t count)
+static ssize_t pci_write_config(struct file *filp, struct kobject *kobj,
+                               struct bin_attribute *bin_attr, char *buf,
+                               loff_t off, size_t count)
 {
-       struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
+       struct pci_dev *dev = to_pci_dev(container_of(kobj, struct device,
+                                                     kobj));
        unsigned int size = count;
        loff_t init_off = off;
-       u8 *data = (u8*) buf;
+       u8 *data = (u8 *) buf;
 
        if (off > dev->cfg_size)
                return 0;
@@ -728,10 +722,10 @@ pci_write_config(struct file* filp, struct kobject *kobj,
        if ((off & 3) && size > 2) {
                u16 val = data[off - init_off];
                val |= (u16) data[off - init_off + 1] << 8;
-                pci_user_write_config_word(dev, off, val);
-                off += 2;
-                size -= 2;
-        }
+               pci_user_write_config_word(dev, off, val);
+               off += 2;
+               size -= 2;
+       }
 
        while (size > 3) {
                u32 val = data[off - init_off];
@@ -762,10 +756,9 @@ pci_write_config(struct file* filp, struct kobject *kobj,
        return count;
 }
 
-static ssize_t
-read_vpd_attr(struct file *filp, struct kobject *kobj,
-             struct bin_attribute *bin_attr,
-             char *buf, loff_t off, size_t count)
+static ssize_t read_vpd_attr(struct file *filp, struct kobject *kobj,
+                            struct bin_attribute *bin_attr, char *buf,
+                            loff_t off, size_t count)
 {
        struct pci_dev *dev =
                to_pci_dev(container_of(kobj, struct device, kobj));
@@ -778,10 +771,9 @@ read_vpd_attr(struct file *filp, struct kobject *kobj,
        return pci_read_vpd(dev, off, count, buf);
 }
 
-static ssize_t
-write_vpd_attr(struct file *filp, struct kobject *kobj,
-              struct bin_attribute *bin_attr,
-              char *buf, loff_t off, size_t count)
+static ssize_t write_vpd_attr(struct file *filp, struct kobject *kobj,
+                             struct bin_attribute *bin_attr, char *buf,
+                             loff_t off, size_t count)
 {
        struct pci_dev *dev =
                to_pci_dev(container_of(kobj, struct device, kobj));
@@ -807,20 +799,18 @@ write_vpd_attr(struct file *filp, struct kobject *kobj,
  * Reads 1, 2, or 4 bytes from legacy I/O port space using an arch specific
  * callback routine (pci_legacy_read).
  */
-static ssize_t
-pci_read_legacy_io(struct file *filp, struct kobject *kobj,
-                  struct bin_attribute *bin_attr,
-                  char *buf, loff_t off, size_t count)
+static ssize_t pci_read_legacy_io(struct file *filp, struct kobject *kobj,
+                                 struct bin_attribute *bin_attr, char *buf,
+                                 loff_t off, size_t count)
 {
-        struct pci_bus *bus = to_pci_bus(container_of(kobj,
-                                                      struct device,
+       struct pci_bus *bus = to_pci_bus(container_of(kobj, struct device,
                                                      kobj));
 
-        /* Only support 1, 2 or 4 byte accesses */
-        if (count != 1 && count != 2 && count != 4)
-                return -EINVAL;
+       /* Only support 1, 2 or 4 byte accesses */
+       if (count != 1 && count != 2 && count != 4)
+               return -EINVAL;
 
-        return pci_legacy_read(bus, off, (u32 *)buf, count);
+       return pci_legacy_read(bus, off, (u32 *)buf, count);
 }
 
 /**
@@ -835,19 +825,18 @@ pci_read_legacy_io(struct file *filp, struct kobject *kobj,
  * Writes 1, 2, or 4 bytes from legacy I/O port space using an arch specific
  * callback routine (pci_legacy_write).
  */
-static ssize_t
-pci_write_legacy_io(struct file *filp, struct kobject *kobj,
-                   struct bin_attribute *bin_attr,
-                   char *buf, loff_t off, size_t count)
+static ssize_t pci_write_legacy_io(struct file *filp, struct kobject *kobj,
+                                  struct bin_attribute *bin_attr, char *buf,
+                                  loff_t off, size_t count)
 {
-        struct pci_bus *bus = to_pci_bus(container_of(kobj,
-                                                     struct device,
+       struct pci_bus *bus = to_pci_bus(container_of(kobj, struct device,
                                                      kobj));
-        /* Only support 1, 2 or 4 byte accesses */
-        if (count != 1 && count != 2 && count != 4)
-                return -EINVAL;
 
-        return pci_legacy_write(bus, off, *(u32 *)buf, count);
+       /* Only support 1, 2 or 4 byte accesses */
+       if (count != 1 && count != 2 && count != 4)
+               return -EINVAL;
+
+       return pci_legacy_write(bus, off, *(u32 *)buf, count);
 }
 
 /**
@@ -861,16 +850,14 @@ pci_write_legacy_io(struct file *filp, struct kobject *kobj,
  * legacy memory space (first meg of bus space) into application virtual
  * memory space.
  */
-static int
-pci_mmap_legacy_mem(struct file *filp, struct kobject *kobj,
-                   struct bin_attribute *attr,
-                    struct vm_area_struct *vma)
+static int pci_mmap_legacy_mem(struct file *filp, struct kobject *kobj,
+                              struct bin_attribute *attr,
+                              struct vm_area_struct *vma)
 {
-        struct pci_bus *bus = to_pci_bus(container_of(kobj,
-                                                      struct device,
+       struct pci_bus *bus = to_pci_bus(container_of(kobj, struct device,
                                                      kobj));
 
-        return pci_mmap_legacy_page_range(bus, vma, pci_mmap_mem);
+       return pci_mmap_legacy_page_range(bus, vma, pci_mmap_mem);
 }
 
 /**
@@ -884,16 +871,14 @@ pci_mmap_legacy_mem(struct file *filp, struct kobject *kobj,
  * legacy IO space (first meg of bus space) into application virtual
  * memory space. Returns -ENOSYS if the operation isn't supported
  */
-static int
-pci_mmap_legacy_io(struct file *filp, struct kobject *kobj,
-                  struct bin_attribute *attr,
-                  struct vm_area_struct *vma)
+static int pci_mmap_legacy_io(struct file *filp, struct kobject *kobj,
+                             struct bin_attribute *attr,
+                             struct vm_area_struct *vma)
 {
-        struct pci_bus *bus = to_pci_bus(container_of(kobj,
-                                                      struct device,
+       struct pci_bus *bus = to_pci_bus(container_of(kobj, struct device,
                                                      kobj));
 
-        return pci_mmap_legacy_page_range(bus, vma, pci_mmap_io);
+       return pci_mmap_legacy_page_range(bus, vma, pci_mmap_io);
 }
 
 /**
@@ -903,10 +888,9 @@ pci_mmap_legacy_io(struct file *filp, struct kobject *kobj,
  *
  * Stub implementation. Can be overridden by arch if necessary.
  */
-void __weak
-pci_adjust_legacy_attr(struct pci_bus *b, enum pci_mmap_state mmap_type)
+void __weak pci_adjust_legacy_attr(struct pci_bus *b,
+                                  enum pci_mmap_state mmap_type)
 {
-       return;
 }
 
 /**
@@ -961,8 +945,7 @@ legacy_io_err:
        kfree(b->legacy_io);
        b->legacy_io = NULL;
 kzalloc_err:
-       printk(KERN_WARNING "pci: warning: could not create legacy I/O port "
-              "and ISA memory resources to sysfs\n");
+       printk(KERN_WARNING "pci: warning: could not create legacy I/O port and ISA memory resources to sysfs\n");
        return;
 }
 
@@ -1005,9 +988,8 @@ int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma,
  *
  * Use the regular PCI mapping routines to map a PCI resource into userspace.
  */
-static int
-pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
-                 struct vm_area_struct *vma, int write_combine)
+static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
+                            struct vm_area_struct *vma, int write_combine)
 {
        struct pci_dev *pdev = to_pci_dev(container_of(kobj,
                                                       struct device, kobj));
@@ -1023,8 +1005,7 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
                return -ENODEV;
 
        if (!pci_mmap_fits(pdev, i, vma, PCI_MMAP_SYSFS)) {
-               WARN(1, "process \"%s\" tried to map 0x%08lx bytes "
-                       "at page 0x%08lx on %s BAR %d (start 0x%16Lx, size 0x%16Lx)\n",
+               WARN(1, "process \"%s\" tried to map 0x%08lx bytes at page 0x%08lx on %s BAR %d (start 0x%16Lx, size 0x%16Lx)\n",
                        current->comm, vma->vm_end-vma->vm_start, vma->vm_pgoff,
                        pci_name(pdev), i,
                        (u64)pci_resource_start(pdev, i),
@@ -1046,26 +1027,23 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
        return pci_mmap_page_range(pdev, vma, mmap_type, write_combine);
 }
 
-static int
-pci_mmap_resource_uc(struct file *filp, struct kobject *kobj,
-                    struct bin_attribute *attr,
-                    struct vm_area_struct *vma)
+static int pci_mmap_resource_uc(struct file *filp, struct kobject *kobj,
+                               struct bin_attribute *attr,
+                               struct vm_area_struct *vma)
 {
        return pci_mmap_resource(kobj, attr, vma, 0);
 }
 
-static int
-pci_mmap_resource_wc(struct file *filp, struct kobject *kobj,
-                    struct bin_attribute *attr,
-                    struct vm_area_struct *vma)
+static int pci_mmap_resource_wc(struct file *filp, struct kobject *kobj,
+                               struct bin_attribute *attr,
+                               struct vm_area_struct *vma)
 {
        return pci_mmap_resource(kobj, attr, vma, 1);
 }
 
-static ssize_t
-pci_resource_io(struct file *filp, struct kobject *kobj,
-               struct bin_attribute *attr, char *buf,
-               loff_t off, size_t count, bool write)
+static ssize_t pci_resource_io(struct file *filp, struct kobject *kobj,
+                              struct bin_attribute *attr, char *buf,
+                              loff_t off, size_t count, bool write)
 {
        struct pci_dev *pdev = to_pci_dev(container_of(kobj,
                                                       struct device, kobj));
@@ -1110,18 +1088,16 @@ pci_resource_io(struct file *filp, struct kobject *kobj,
        return -EINVAL;
 }
 
-static ssize_t
-pci_read_resource_io(struct file *filp, struct kobject *kobj,
-                    struct bin_attribute *attr, char *buf,
-                    loff_t off, size_t count)
+static ssize_t pci_read_resource_io(struct file *filp, struct kobject *kobj,
+                                   struct bin_attribute *attr, char *buf,
+                                   loff_t off, size_t count)
 {
        return pci_resource_io(filp, kobj, attr, buf, off, count, false);
 }
 
-static ssize_t
-pci_write_resource_io(struct file *filp, struct kobject *kobj,
-                     struct bin_attribute *attr, char *buf,
-                     loff_t off, size_t count)
+static ssize_t pci_write_resource_io(struct file *filp, struct kobject *kobj,
+                                    struct bin_attribute *attr, char *buf,
+                                    loff_t off, size_t count)
 {
        return pci_resource_io(filp, kobj, attr, buf, off, count, true);
 }
@@ -1133,8 +1109,7 @@ pci_write_resource_io(struct file *filp, struct kobject *kobj,
  * If we created resource files for @pdev, remove them from sysfs and
  * free their resources.
  */
-static void
-pci_remove_resource_files(struct pci_dev *pdev)
+static void pci_remove_resource_files(struct pci_dev *pdev)
 {
        int i;
 
@@ -1237,10 +1212,9 @@ void __weak pci_remove_resource_files(struct pci_dev *dev) { return; }
  *
  * writing anything except 0 enables it
  */
-static ssize_t
-pci_write_rom(struct file *filp, struct kobject *kobj,
-             struct bin_attribute *bin_attr,
-             char *buf, loff_t off, size_t count)
+static ssize_t pci_write_rom(struct file *filp, struct kobject *kobj,
+                            struct bin_attribute *bin_attr, char *buf,
+                            loff_t off, size_t count)
 {
        struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
 
@@ -1264,10 +1238,9 @@ pci_write_rom(struct file *filp, struct kobject *kobj,
  * Put @count bytes starting at @off into @buf from the ROM in the PCI
  * device corresponding to @kobj.
  */
-static ssize_t
-pci_read_rom(struct file *filp, struct kobject *kobj,
-            struct bin_attribute *bin_attr,
-            char *buf, loff_t off, size_t count)
+static ssize_t pci_read_rom(struct file *filp, struct kobject *kobj,
+                           struct bin_attribute *bin_attr, char *buf,
+                           loff_t off, size_t count)
 {
        struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
        void __iomem *rom;
@@ -1313,9 +1286,8 @@ static struct bin_attribute pcie_config_attr = {
        .write = pci_write_config,
 };
 
-static ssize_t reset_store(struct device *dev,
-                          struct device_attribute *attr, const char *buf,
-                          size_t count)
+static ssize_t reset_store(struct device *dev, struct device_attribute *attr,
+                          const char *buf, size_t count)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
        unsigned long val;
@@ -1382,7 +1354,7 @@ error:
        return retval;
 }
 
-int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
+int __must_check pci_create_sysfs_dev_files(struct pci_dev *pdev)
 {
        int retval;
        int rom_size = 0;
@@ -1520,7 +1492,6 @@ static int __init pci_sysfs_init(void)
 
        return 0;
 }
-
 late_initcall(pci_sysfs_init);
 
 static struct attribute *pci_dev_dev_attrs[] = {
@@ -1529,7 +1500,7 @@ static struct attribute *pci_dev_dev_attrs[] = {
 };
 
 static umode_t pci_dev_attrs_are_visible(struct kobject *kobj,
-                                               struct attribute *a, int n)
+                                        struct attribute *a, int n)
 {
        struct device *dev = container_of(kobj, struct device, kobj);
        struct pci_dev *pdev = to_pci_dev(dev);
@@ -1548,7 +1519,7 @@ static struct attribute *pci_dev_hp_attrs[] = {
 };
 
 static umode_t pci_dev_hp_attrs_are_visible(struct kobject *kobj,
-                                               struct attribute *a, int n)
+                                           struct attribute *a, int n)
 {
        struct device *dev = container_of(kobj, struct device, kobj);
        struct pci_dev *pdev = to_pci_dev(dev);
@@ -1572,7 +1543,7 @@ static struct attribute *sriov_dev_attrs[] = {
 };
 
 static umode_t sriov_attrs_are_visible(struct kobject *kobj,
-                                        struct attribute *a, int n)
+                                      struct attribute *a, int n)
 {
        struct device *dev = container_of(kobj, struct device, kobj);
 
index 212c63d780e755eacc91d6ad163faf397fa31728..63a54a3408639c249202685c1bfd4f81e25c8ae1 100644 (file)
@@ -114,7 +114,7 @@ unsigned char pci_bus_max_busnr(struct pci_bus *bus)
        max = bus->busn_res.end;
        list_for_each_entry(tmp, &bus->children, node) {
                n = pci_bus_max_busnr(tmp);
-               if(n > max)
+               if (n > max)
                        max = n;
        }
        return max;
@@ -226,6 +226,7 @@ int pci_find_capability(struct pci_dev *dev, int cap)
 
        return pos;
 }
+EXPORT_SYMBOL(pci_find_capability);
 
 /**
  * pci_bus_find_capability - query for devices' capabilities
@@ -253,6 +254,7 @@ int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap)
 
        return pos;
 }
+EXPORT_SYMBOL(pci_bus_find_capability);
 
 /**
  * pci_find_next_ext_capability - Find an extended capability
@@ -403,8 +405,8 @@ EXPORT_SYMBOL_GPL(pci_find_ht_capability);
  *  For given resource region of given device, return the resource
  *  region of parent bus the given region is contained in.
  */
-struct resource *
-pci_find_parent_resource(const struct pci_dev *dev, struct resource *res)
+struct resource *pci_find_parent_resource(const struct pci_dev *dev,
+                                         struct resource *res)
 {
        const struct pci_bus *bus = dev->bus;
        struct resource *r;
@@ -436,6 +438,7 @@ pci_find_parent_resource(const struct pci_dev *dev, struct resource *res)
        }
        return NULL;
 }
+EXPORT_SYMBOL(pci_find_parent_resource);
 
 /**
  * pci_wait_for_pending - wait for @mask bit(s) to clear in status word @pos
@@ -470,8 +473,7 @@ int pci_wait_for_pending(struct pci_dev *dev, int pos, u16 mask)
  * Restore the BAR values for a given device, so as to make it
  * accessible by its driver.
  */
-static void
-pci_restore_bars(struct pci_dev *dev)
+static void pci_restore_bars(struct pci_dev *dev)
 {
        int i;
 
@@ -496,7 +498,7 @@ static inline bool platform_pci_power_manageable(struct pci_dev *dev)
 }
 
 static inline int platform_pci_set_power_state(struct pci_dev *dev,
-                                                pci_power_t t)
+                                              pci_power_t t)
 {
        return pci_platform_pm ? pci_platform_pm->set_state(dev, t) : -ENOSYS;
 }
@@ -553,8 +555,8 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
         */
        if (state != PCI_D0 && dev->current_state <= PCI_D3cold
            && dev->current_state > state) {
-               dev_err(&dev->dev, "invalid power transition "
-                       "(from state %d to %d)\n", dev->current_state, state);
+               dev_err(&dev->dev, "invalid power transition (from state %d to %d)\n",
+                       dev->current_state, state);
                return -EINVAL;
        }
 
@@ -601,8 +603,8 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
        pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
        dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
        if (dev->current_state != state && printk_ratelimit())
-               dev_info(&dev->dev, "Refused to change power state, "
-                       "currently in D%d\n", dev->current_state);
+               dev_info(&dev->dev, "Refused to change power state, currently in D%d\n",
+                        dev->current_state);
 
        /*
         * According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT
@@ -846,6 +848,7 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
 
        return error;
 }
+EXPORT_SYMBOL(pci_set_power_state);
 
 /**
  * pci_choose_state - Choose the power state of a PCI device
@@ -884,12 +887,10 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
        }
        return PCI_D0;
 }
-
 EXPORT_SYMBOL(pci_choose_state);
 
 #define PCI_EXP_SAVE_REGS      7
 
-
 static struct pci_cap_saved_state *_pci_find_saved_cap(struct pci_dev *pci_dev,
                                                       u16 cap, bool extended)
 {
@@ -1001,8 +1002,7 @@ static void pci_restore_pcix_state(struct pci_dev *dev)
  * pci_save_state - save the PCI configuration space of a device before suspending
  * @dev: - PCI device that we're dealing with
  */
-int
-pci_save_state(struct pci_dev *dev)
+int pci_save_state(struct pci_dev *dev)
 {
        int i;
        /* XXX: 100% dword access ok here? */
@@ -1017,6 +1017,7 @@ pci_save_state(struct pci_dev *dev)
                return i;
        return 0;
 }
+EXPORT_SYMBOL(pci_save_state);
 
 static void pci_restore_config_dword(struct pci_dev *pdev, int offset,
                                     u32 saved_val, int retry)
@@ -1028,8 +1029,8 @@ static void pci_restore_config_dword(struct pci_dev *pdev, int offset,
                return;
 
        for (;;) {
-               dev_dbg(&pdev->dev, "restoring config space at offset "
-                       "%#x (was %#x, writing %#x)\n", offset, val, saved_val);
+               dev_dbg(&pdev->dev, "restoring config space at offset %#x (was %#x, writing %#x)\n",
+                       offset, val, saved_val);
                pci_write_config_dword(pdev, offset, saved_val);
                if (retry-- <= 0)
                        return;
@@ -1087,6 +1088,7 @@ void pci_restore_state(struct pci_dev *dev)
 
        dev->state_saved = false;
 }
+EXPORT_SYMBOL(pci_restore_state);
 
 struct pci_saved_state {
        u32 config_space[16];
@@ -1231,6 +1233,7 @@ int pci_reenable_device(struct pci_dev *dev)
                return do_pci_enable_device(dev, (1 << PCI_NUM_RESOURCES) - 1);
        return 0;
 }
+EXPORT_SYMBOL(pci_reenable_device);
 
 static void pci_enable_bridge(struct pci_dev *dev)
 {
@@ -1305,6 +1308,7 @@ int pci_enable_device_io(struct pci_dev *dev)
 {
        return pci_enable_device_flags(dev, IORESOURCE_IO);
 }
+EXPORT_SYMBOL(pci_enable_device_io);
 
 /**
  * pci_enable_device_mem - Initialize a device for use with Memory space
@@ -1318,6 +1322,7 @@ int pci_enable_device_mem(struct pci_dev *dev)
 {
        return pci_enable_device_flags(dev, IORESOURCE_MEM);
 }
+EXPORT_SYMBOL(pci_enable_device_mem);
 
 /**
  * pci_enable_device - Initialize device before it's used by a driver.
@@ -1334,6 +1339,7 @@ int pci_enable_device(struct pci_dev *dev)
 {
        return pci_enable_device_flags(dev, IORESOURCE_MEM | IORESOURCE_IO);
 }
+EXPORT_SYMBOL(pci_enable_device);
 
 /*
  * Managed PCI resources.  This manages device on/off, intx/msi/msix
@@ -1416,6 +1422,7 @@ int pcim_enable_device(struct pci_dev *pdev)
        }
        return rc;
 }
+EXPORT_SYMBOL(pcim_enable_device);
 
 /**
  * pcim_pin_device - Pin managed PCI device
@@ -1434,6 +1441,7 @@ void pcim_pin_device(struct pci_dev *pdev)
        if (dr)
                dr->pinned = 1;
 }
+EXPORT_SYMBOL(pcim_pin_device);
 
 /*
  * pcibios_add_device - provide arch specific hooks when adding device dev
@@ -1443,7 +1451,7 @@ void pcim_pin_device(struct pci_dev *pdev)
  * devices are added. This is the default implementation. Architecture
  * implementations can override this.
  */
-int __weak pcibios_add_device (struct pci_dev *dev)
+int __weak pcibios_add_device(struct pci_dev *dev)
 {
        return 0;
 }
@@ -1515,8 +1523,7 @@ void pci_disable_enabled_device(struct pci_dev *dev)
  * Note we don't actually disable the device until all callers of
  * pci_enable_device() have called pci_disable_device().
  */
-void
-pci_disable_device(struct pci_dev *dev)
+void pci_disable_device(struct pci_dev *dev)
 {
        struct pci_devres *dr;
 
@@ -1534,6 +1541,7 @@ pci_disable_device(struct pci_dev *dev)
 
        dev->is_busmaster = 0;
 }
+EXPORT_SYMBOL(pci_disable_device);
 
 /**
  * pcibios_set_pcie_reset_state - set reset state for device dev
@@ -1562,6 +1570,7 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
 {
        return pcibios_set_pcie_reset_state(dev, state);
 }
+EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state);
 
 /**
  * pci_check_pme_status - Check if given device has generated PME.
@@ -1641,6 +1650,7 @@ bool pci_pme_capable(struct pci_dev *dev, pci_power_t state)
 
        return !!(dev->pme_support & (1 << state));
 }
+EXPORT_SYMBOL(pci_pme_capable);
 
 static void pci_pme_list_scan(struct work_struct *work)
 {
@@ -1745,6 +1755,7 @@ void pci_pme_active(struct pci_dev *dev, bool enable)
 
        dev_dbg(&dev->dev, "PME# %s\n", enable ? "enabled" : "disabled");
 }
+EXPORT_SYMBOL(pci_pme_active);
 
 /**
  * __pci_enable_wake - enable PCI device as wakeup event source
@@ -1830,6 +1841,7 @@ int pci_wake_from_d3(struct pci_dev *dev, bool enable)
                        pci_enable_wake(dev, PCI_D3cold, enable) :
                        pci_enable_wake(dev, PCI_D3hot, enable);
 }
+EXPORT_SYMBOL(pci_wake_from_d3);
 
 /**
  * pci_target_state - find an appropriate low power state for a given PCI dev
@@ -1908,6 +1920,7 @@ int pci_prepare_to_sleep(struct pci_dev *dev)
 
        return error;
 }
+EXPORT_SYMBOL(pci_prepare_to_sleep);
 
 /**
  * pci_back_from_sleep - turn PCI device on during system-wide transition into working state
@@ -1920,6 +1933,7 @@ int pci_back_from_sleep(struct pci_dev *dev)
        pci_enable_wake(dev, PCI_D0, false);
        return pci_set_power_state(dev, PCI_D0);
 }
+EXPORT_SYMBOL(pci_back_from_sleep);
 
 /**
  * pci_finish_runtime_suspend - Carry out PCI-specific part of runtime suspend.
@@ -2415,8 +2429,7 @@ u8 pci_swizzle_interrupt_pin(const struct pci_dev *dev, u8 pin)
        return (((pin - 1) + slot) % 4) + 1;
 }
 
-int
-pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge)
+int pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge)
 {
        u8 pin;
 
@@ -2478,6 +2491,7 @@ void pci_release_region(struct pci_dev *pdev, int bar)
        if (dr)
                dr->region_mask &= ~(1 << bar);
 }
+EXPORT_SYMBOL(pci_release_region);
 
 /**
  *     __pci_request_region - Reserved PCI I/O and memory resource
@@ -2498,8 +2512,8 @@ void pci_release_region(struct pci_dev *pdev, int bar)
  *     Returns 0 on success, or %EBUSY on error.  A warning
  *     message is also printed on failure.
  */
-static int __pci_request_region(struct pci_dev *pdev, int bar, const char *res_name,
-                                                                       int exclusive)
+static int __pci_request_region(struct pci_dev *pdev, int bar,
+                               const char *res_name, int exclusive)
 {
        struct pci_devres *dr;
 
@@ -2510,8 +2524,7 @@ static int __pci_request_region(struct pci_dev *pdev, int bar, const char *res_n
                if (!request_region(pci_resource_start(pdev, bar),
                            pci_resource_len(pdev, bar), res_name))
                        goto err_out;
-       }
-       else if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
+       } else if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
                if (!__request_mem_region(pci_resource_start(pdev, bar),
                                        pci_resource_len(pdev, bar), res_name,
                                        exclusive))
@@ -2548,6 +2561,7 @@ int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name)
 {
        return __pci_request_region(pdev, bar, res_name, 0);
 }
+EXPORT_SYMBOL(pci_request_region);
 
 /**
  *     pci_request_region_exclusive - Reserved PCI I/O and memory resource
@@ -2567,10 +2581,13 @@ int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name)
  *     explicitly not allowed to map the resource via /dev/mem or
  *     sysfs.
  */
-int pci_request_region_exclusive(struct pci_dev *pdev, int bar, const char *res_name)
+int pci_request_region_exclusive(struct pci_dev *pdev, int bar,
+                                const char *res_name)
 {
        return __pci_request_region(pdev, bar, res_name, IORESOURCE_EXCLUSIVE);
 }
+EXPORT_SYMBOL(pci_request_region_exclusive);
+
 /**
  * pci_release_selected_regions - Release selected PCI I/O and memory resources
  * @pdev: PCI device whose resources were previously reserved
@@ -2587,9 +2604,10 @@ void pci_release_selected_regions(struct pci_dev *pdev, int bars)
                if (bars & (1 << i))
                        pci_release_region(pdev, i);
 }
+EXPORT_SYMBOL(pci_release_selected_regions);
 
 static int __pci_request_selected_regions(struct pci_dev *pdev, int bars,
-                                const char *res_name, int excl)
+                                         const char *res_name, int excl)
 {
        int i;
 
@@ -2600,7 +2618,7 @@ static int __pci_request_selected_regions(struct pci_dev *pdev, int bars,
        return 0;
 
 err_out:
-       while(--i >= 0)
+       while (--i >= 0)
                if (bars & (1 << i))
                        pci_release_region(pdev, i);
 
@@ -2619,13 +2637,15 @@ int pci_request_selected_regions(struct pci_dev *pdev, int bars,
 {
        return __pci_request_selected_regions(pdev, bars, res_name, 0);
 }
+EXPORT_SYMBOL(pci_request_selected_regions);
 
-int pci_request_selected_regions_exclusive(struct pci_dev *pdev,
-                                int bars, const char *res_name)
+int pci_request_selected_regions_exclusive(struct pci_dev *pdev, int bars,
+                                          const char *res_name)
 {
        return __pci_request_selected_regions(pdev, bars, res_name,
                        IORESOURCE_EXCLUSIVE);
 }
+EXPORT_SYMBOL(pci_request_selected_regions_exclusive);
 
 /**
  *     pci_release_regions - Release reserved PCI I/O and memory resources
@@ -2640,6 +2660,7 @@ void pci_release_regions(struct pci_dev *pdev)
 {
        pci_release_selected_regions(pdev, (1 << 6) - 1);
 }
+EXPORT_SYMBOL(pci_release_regions);
 
 /**
  *     pci_request_regions - Reserved PCI I/O and memory resources
@@ -2658,6 +2679,7 @@ int pci_request_regions(struct pci_dev *pdev, const char *res_name)
 {
        return pci_request_selected_regions(pdev, ((1 << 6) - 1), res_name);
 }
+EXPORT_SYMBOL(pci_request_regions);
 
 /**
  *     pci_request_regions_exclusive - Reserved PCI I/O and memory resources
@@ -2680,6 +2702,7 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
        return pci_request_selected_regions_exclusive(pdev,
                                        ((1 << 6) - 1), res_name);
 }
+EXPORT_SYMBOL(pci_request_regions_exclusive);
 
 static void __pci_set_master(struct pci_dev *dev, bool enable)
 {
@@ -2749,6 +2772,7 @@ void pci_set_master(struct pci_dev *dev)
        __pci_set_master(dev, true);
        pcibios_set_master(dev);
 }
+EXPORT_SYMBOL(pci_set_master);
 
 /**
  * pci_clear_master - disables bus-mastering for device dev
@@ -2758,6 +2782,7 @@ void pci_clear_master(struct pci_dev *dev)
 {
        __pci_set_master(dev, false);
 }
+EXPORT_SYMBOL(pci_clear_master);
 
 /**
  * pci_set_cacheline_size - ensure the CACHE_LINE_SIZE register is programmed
@@ -2790,30 +2815,13 @@ int pci_set_cacheline_size(struct pci_dev *dev)
        if (cacheline_size == pci_cache_line_size)
                return 0;
 
-       dev_printk(KERN_DEBUG, &dev->dev, "cache line size of %d is not "
-                  "supported\n", pci_cache_line_size << 2);
+       dev_printk(KERN_DEBUG, &dev->dev, "cache line size of %d is not supported\n",
+                  pci_cache_line_size << 2);
 
        return -EINVAL;
 }
 EXPORT_SYMBOL_GPL(pci_set_cacheline_size);
 
-#ifdef PCI_DISABLE_MWI
-int pci_set_mwi(struct pci_dev *dev)
-{
-       return 0;
-}
-
-int pci_try_set_mwi(struct pci_dev *dev)
-{
-       return 0;
-}
-
-void pci_clear_mwi(struct pci_dev *dev)
-{
-}
-
-#else
-
 /**
  * pci_set_mwi - enables memory-write-invalidate PCI transaction
  * @dev: the PCI device for which MWI is enabled
@@ -2822,9 +2830,11 @@ void pci_clear_mwi(struct pci_dev *dev)
  *
  * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
  */
-int
-pci_set_mwi(struct pci_dev *dev)
+int pci_set_mwi(struct pci_dev *dev)
 {
+#ifdef PCI_DISABLE_MWI
+       return 0;
+#else
        int rc;
        u16 cmd;
 
@@ -2833,14 +2843,15 @@ pci_set_mwi(struct pci_dev *dev)
                return rc;
 
        pci_read_config_word(dev, PCI_COMMAND, &cmd);
-       if (! (cmd & PCI_COMMAND_INVALIDATE)) {
+       if (!(cmd & PCI_COMMAND_INVALIDATE)) {
                dev_dbg(&dev->dev, "enabling Mem-Wr-Inval\n");
                cmd |= PCI_COMMAND_INVALIDATE;
                pci_write_config_word(dev, PCI_COMMAND, cmd);
        }
-
        return 0;
+#endif
 }
+EXPORT_SYMBOL(pci_set_mwi);
 
 /**
  * pci_try_set_mwi - enables memory-write-invalidate PCI transaction
@@ -2853,9 +2864,13 @@ pci_set_mwi(struct pci_dev *dev)
  */
 int pci_try_set_mwi(struct pci_dev *dev)
 {
-       int rc = pci_set_mwi(dev);
-       return rc;
+#ifdef PCI_DISABLE_MWI
+       return 0;
+#else
+       return pci_set_mwi(dev);
+#endif
 }
+EXPORT_SYMBOL(pci_try_set_mwi);
 
 /**
  * pci_clear_mwi - disables Memory-Write-Invalidate for device dev
@@ -2863,9 +2878,9 @@ int pci_try_set_mwi(struct pci_dev *dev)
  *
  * Disables PCI Memory-Write-Invalidate transaction on the device
  */
-void
-pci_clear_mwi(struct pci_dev *dev)
+void pci_clear_mwi(struct pci_dev *dev)
 {
+#ifndef PCI_DISABLE_MWI
        u16 cmd;
 
        pci_read_config_word(dev, PCI_COMMAND, &cmd);
@@ -2873,8 +2888,9 @@ pci_clear_mwi(struct pci_dev *dev)
                cmd &= ~PCI_COMMAND_INVALIDATE;
                pci_write_config_word(dev, PCI_COMMAND, cmd);
        }
+#endif
 }
-#endif /* ! PCI_DISABLE_MWI */
+EXPORT_SYMBOL(pci_clear_mwi);
 
 /**
  * pci_intx - enables/disables PCI INTx for device dev
@@ -2883,18 +2899,16 @@ pci_clear_mwi(struct pci_dev *dev)
  *
  * Enables/disables PCI INTx for device dev
  */
-void
-pci_intx(struct pci_dev *pdev, int enable)
+void pci_intx(struct pci_dev *pdev, int enable)
 {
        u16 pci_command, new;
 
        pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
 
-       if (enable) {
+       if (enable)
                new = pci_command & ~PCI_COMMAND_INTX_DISABLE;
-       } else {
+       else
                new = pci_command | PCI_COMMAND_INTX_DISABLE;
-       }
 
        if (new != pci_command) {
                struct pci_devres *dr;
@@ -2908,6 +2922,7 @@ pci_intx(struct pci_dev *pdev, int enable)
                }
        }
 }
+EXPORT_SYMBOL_GPL(pci_intx);
 
 /**
  * pci_intx_mask_supported - probe for INTx masking support
@@ -2937,8 +2952,8 @@ bool pci_intx_mask_supported(struct pci_dev *dev)
         * go ahead and check it.
         */
        if ((new ^ orig) & ~PCI_COMMAND_INTX_DISABLE) {
-               dev_err(&dev->dev, "Command register changed from "
-                       "0x%x to 0x%x: driver or hardware bug?\n", orig, new);
+               dev_err(&dev->dev, "Command register changed from 0x%x to 0x%x: driver or hardware bug?\n",
+                       orig, new);
        } else if ((new ^ orig) & PCI_COMMAND_INTX_DISABLE) {
                mask_supported = true;
                pci_write_config_word(dev, PCI_COMMAND, orig);
@@ -3124,8 +3139,7 @@ static int pci_af_flr(struct pci_dev *dev, int probe)
        if (pci_wait_for_pending(dev, pos + PCI_AF_STATUS, PCI_AF_STATUS_TP))
                goto clear;
 
-       dev_err(&dev->dev, "transaction is not cleared; "
-                       "proceeding with reset anyway\n");
+       dev_err(&dev->dev, "transaction is not cleared; proceeding with reset anyway\n");
 
 clear:
        pci_write_config_byte(dev, pos + PCI_AF_CTRL, PCI_AF_CTRL_FLR);
@@ -4100,6 +4114,7 @@ int pci_select_bars(struct pci_dev *dev, unsigned long flags)
                        bars |= (1 << i);
        return bars;
 }
+EXPORT_SYMBOL(pci_select_bars);
 
 /**
  * pci_resource_bar - get position of the BAR associated with a resource
@@ -4139,7 +4154,7 @@ void __init pci_register_set_vga_state(arch_set_vga_state_t func)
 }
 
 static int pci_set_vga_state_arch(struct pci_dev *dev, bool decode,
-                     unsigned int command_bits, u32 flags)
+                                 unsigned int command_bits, u32 flags)
 {
        if (arch_set_vga_state)
                return arch_set_vga_state(dev, decode, command_bits,
@@ -4251,11 +4266,10 @@ static resource_size_t pci_specified_resource_alignment(struct pci_dev *dev)
                        bus == dev->bus->number &&
                        slot == PCI_SLOT(dev->devfn) &&
                        func == PCI_FUNC(dev->devfn)) {
-                       if (align_order == -1) {
+                       if (align_order == -1)
                                align = PAGE_SIZE;
-                       } else {
+                       else
                                align = 1 << align_order;
-                       }
                        /* Found */
                        break;
                }
@@ -4373,7 +4387,6 @@ static int __init pci_resource_alignment_sysfs_init(void)
        return bus_create_file(&pci_bus_type,
                                        &bus_attr_resource_alignment);
 }
-
 late_initcall(pci_resource_alignment_sysfs_init);
 
 static void pci_no_domains(void)
@@ -4452,41 +4465,3 @@ static int __init pci_setup(char *str)
        return 0;
 }
 early_param("pci", pci_setup);
-
-EXPORT_SYMBOL(pci_reenable_device);
-EXPORT_SYMBOL(pci_enable_device_io);
-EXPORT_SYMBOL(pci_enable_device_mem);
-EXPORT_SYMBOL(pci_enable_device);
-EXPORT_SYMBOL(pcim_enable_device);
-EXPORT_SYMBOL(pcim_pin_device);
-EXPORT_SYMBOL(pci_disable_device);
-EXPORT_SYMBOL(pci_find_capability);
-EXPORT_SYMBOL(pci_bus_find_capability);
-EXPORT_SYMBOL(pci_release_regions);
-EXPORT_SYMBOL(pci_request_regions);
-EXPORT_SYMBOL(pci_request_regions_exclusive);
-EXPORT_SYMBOL(pci_release_region);
-EXPORT_SYMBOL(pci_request_region);
-EXPORT_SYMBOL(pci_request_region_exclusive);
-EXPORT_SYMBOL(pci_release_selected_regions);
-EXPORT_SYMBOL(pci_request_selected_regions);
-EXPORT_SYMBOL(pci_request_selected_regions_exclusive);
-EXPORT_SYMBOL(pci_set_master);
-EXPORT_SYMBOL(pci_clear_master);
-EXPORT_SYMBOL(pci_set_mwi);
-EXPORT_SYMBOL(pci_try_set_mwi);
-EXPORT_SYMBOL(pci_clear_mwi);
-EXPORT_SYMBOL_GPL(pci_intx);
-EXPORT_SYMBOL(pci_assign_resource);
-EXPORT_SYMBOL(pci_find_parent_resource);
-EXPORT_SYMBOL(pci_select_bars);
-
-EXPORT_SYMBOL(pci_set_power_state);
-EXPORT_SYMBOL(pci_save_state);
-EXPORT_SYMBOL(pci_restore_state);
-EXPORT_SYMBOL(pci_pme_capable);
-EXPORT_SYMBOL(pci_pme_active);
-EXPORT_SYMBOL(pci_wake_from_d3);
-EXPORT_SYMBOL(pci_prepare_to_sleep);
-EXPORT_SYMBOL(pci_back_from_sleep);
-EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state);
index 587e7e853107136b81f416d0e1e932db855bfa61..182224acedbe79c18dbc357cb45763afef40e387 100644 (file)
@@ -397,16 +397,14 @@ static int aer_inject(struct aer_error_inj *einj)
        if (!aer_mask_override && einj->cor_status &&
            !(einj->cor_status & ~cor_mask)) {
                ret = -EINVAL;
-               printk(KERN_WARNING "The correctable error(s) is masked "
-                               "by device\n");
+               printk(KERN_WARNING "The correctable error(s) is masked by device\n");
                spin_unlock_irqrestore(&inject_lock, flags);
                goto out_put;
        }
        if (!aer_mask_override && einj->uncor_status &&
            !(einj->uncor_status & ~uncor_mask)) {
                ret = -EINVAL;
-               printk(KERN_WARNING "The uncorrectable error(s) is masked "
-                               "by device\n");
+               printk(KERN_WARNING "The uncorrectable error(s) is masked by device\n");
                spin_unlock_irqrestore(&inject_lock, flags);
                goto out_put;
        }
@@ -464,8 +462,7 @@ static int aer_inject(struct aer_error_inj *einj)
                        goto out_put;
                }
                aer_irq(-1, edev);
-       }
-       else
+       } else
                ret = -EINVAL;
 out_put:
        kfree(err_alloc);
index b2c8881da764e582982cebe9a5062e69029dfca8..5653ea94547fc8a53caf0c050f0d499936cb4a41 100644 (file)
@@ -542,8 +542,7 @@ static void aer_recover_work_func(struct work_struct *work);
 #define AER_RECOVER_RING_ORDER         4
 #define AER_RECOVER_RING_SIZE          (1 << AER_RECOVER_RING_ORDER)
 
-struct aer_recover_entry
-{
+struct aer_recover_entry {
        u8      bus;
        u8      devfn;
        u16     domain;
index 34ff7026440cac0e929df3db06cba8f93b2a5c00..36ed31b52198804524746a060d8bb1275516d099 100644 (file)
@@ -172,9 +172,7 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info)
        int id = ((dev->bus->number << 8) | dev->devfn);
 
        if (!info->status) {
-               dev_err(&dev->dev,
-                       "PCIe Bus Error: severity=%s, type=Unaccessible, "
-                       "id=%04x(Unregistered Agent ID)\n",
+               dev_err(&dev->dev, "PCIe Bus Error: severity=%s, type=Unaccessible, id=%04x(Unregistered Agent ID)\n",
                        aer_error_severity_string[info->severity], id);
                goto out;
        }
@@ -182,13 +180,11 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info)
        layer = AER_GET_LAYER_ERROR(info->severity, info->status);
        agent = AER_GET_AGENT(info->severity, info->status);
 
-       dev_err(&dev->dev,
-               "PCIe Bus Error: severity=%s, type=%s, id=%04x(%s)\n",
+       dev_err(&dev->dev, "PCIe Bus Error: severity=%s, type=%s, id=%04x(%s)\n",
                aer_error_severity_string[info->severity],
                aer_error_layer[layer], id, aer_agent_string[agent]);
 
-       dev_err(&dev->dev,
-               "  device [%04x:%04x] error status/mask=%08x/%08x\n",
+       dev_err(&dev->dev, "  device [%04x:%04x] error status/mask=%08x/%08x\n",
                dev->vendor, dev->device,
                info->status, info->mask);
 
index bbc3bdd2b189f8a91fa73fa8c8bca28b818359d9..82e06a86cd77b38ae99316e624b1cf72f26c7dd2 100644 (file)
@@ -199,8 +199,7 @@ static void pcie_pme_handle_request(struct pci_dev *port, u16 req_id)
                 * assuming that the PME was reported by a PCIe-PCI bridge that
                 * used devfn different from zero.
                 */
-               dev_dbg(&port->dev, "PME interrupt generated for "
-                       "non-existent device %02x:%02x.%d\n",
+               dev_dbg(&port->dev, "PME interrupt generated for non-existent device %02x:%02x.%d\n",
                        busnr, PCI_SLOT(devfn), PCI_FUNC(devfn));
                found = pcie_pme_from_pci_bridge(bus, 0);
        }
index 0d8fdc48e642ffcc421464ca226ebec78211148c..80887eaa0668f5c62393e466fc7e8eb70fe4bdc4 100644 (file)
@@ -204,8 +204,8 @@ static int pcie_portdrv_probe(struct pci_dev *dev,
                return -ENODEV;
 
        if (!dev->irq && dev->pin) {
-               dev_warn(&dev->dev, "device [%04x:%04x] has invalid IRQ; "
-                        "check vendor BIOS\n", dev->vendor, dev->device);
+               dev_warn(&dev->dev, "device [%04x:%04x] has invalid IRQ; check vendor BIOS\n",
+                        dev->vendor, dev->device);
        }
        status = pcie_port_device_register(dev);
        if (status)
@@ -397,7 +397,7 @@ static struct pci_driver pcie_portdriver = {
 static int __init dmi_pcie_pme_disable_msi(const struct dmi_system_id *d)
 {
        pr_notice("%s detected: will not use MSI for PCIe PME signaling\n",
-                       d->ident);
+                 d->ident);
        pcie_pme_disable_msi();
        return 0;
 }
index 2bbf5221afb391e303328f19cdc5034bdc74379a..e3cf8a2e629216208750bc96ed84285e7a2c0118 100644 (file)
@@ -168,7 +168,7 @@ static inline unsigned long decode_bar(struct pci_dev *dev, u32 bar)
  * Returns 1 if the BAR is 64-bit, or 0 if 32-bit.
  */
 int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
-                       struct resource *res, unsigned int pos)
+                   struct resource *res, unsigned int pos)
 {
        u32 l, sz, mask;
        u64 l64, sz64, mask64;
@@ -433,8 +433,7 @@ static void pci_read_bridge_mmio_pref(struct pci_bus *child)
                        limit |= ((unsigned long) mem_limit_hi) << 32;
 #else
                        if (mem_base_hi || mem_limit_hi) {
-                               dev_err(&dev->dev, "can't handle 64-bit "
-                                       "address space for bridge\n");
+                               dev_err(&dev->dev, "can't handle 64-bit address space for bridge\n");
                                return;
                        }
 #endif
@@ -604,7 +603,6 @@ static enum pci_bus_speed agp_speed(int agp3, int agpstat)
        return agp_speeds[index];
 }
 
-
 static void pci_set_bus_speed(struct pci_bus *bus)
 {
        struct pci_dev *bridge = bus->self;
@@ -636,11 +634,10 @@ static void pci_set_bus_speed(struct pci_bus *bus)
                } else if (status & PCI_X_SSTATUS_266MHZ) {
                        max = PCI_SPEED_133MHz_PCIX_266;
                } else if (status & PCI_X_SSTATUS_133MHZ) {
-                       if ((status & PCI_X_SSTATUS_VERS) == PCI_X_SSTATUS_V2) {
+                       if ((status & PCI_X_SSTATUS_VERS) == PCI_X_SSTATUS_V2)
                                max = PCI_SPEED_133MHz_PCIX_ECC;
-                       } else {
+                       else
                                max = PCI_SPEED_133MHz_PCIX;
-                       }
                } else {
                        max = PCI_SPEED_66MHz_PCIX;
                }
@@ -664,7 +661,6 @@ static void pci_set_bus_speed(struct pci_bus *bus)
        }
 }
 
-
 static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
                                           struct pci_dev *bridge, int busnr)
 {
@@ -729,7 +725,8 @@ add_dev:
        return child;
 }
 
-struct pci_bus *pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr)
+struct pci_bus *pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev,
+                               int busnr)
 {
        struct pci_bus *child;
 
@@ -741,6 +738,7 @@ struct pci_bus *pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int
        }
        return child;
 }
+EXPORT_SYMBOL(pci_add_new_bus);
 
 /*
  * If it's a bridge, configure it and scan the bus behind it.
@@ -887,7 +885,7 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass)
                         * as cards with a PCI-to-PCI bridge can be
                         * inserted later.
                         */
-                       for (i=0; i<CARDBUS_RESERVE_BUSNR; i++) {
+                       for (i = 0; i < CARDBUS_RESERVE_BUSNR; i++) {
                                struct pci_bus *parent = bus;
                                if (pci_find_bus(pci_domain_nr(bus),
                                                        max+i+1))
@@ -934,8 +932,7 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass)
                    (child->number > bus->busn_res.end) ||
                    (child->number < bus->number) ||
                    (child->busn_res.end < bus->number)) {
-                       dev_info(&child->dev, "%pR %s "
-                               "hidden behind%s bridge %s %pR\n",
+                       dev_info(&child->dev, "%pR %s hidden behind%s bridge %s %pR\n",
                                &child->busn_res,
                                (bus->number > child->busn_res.end &&
                                 bus->busn_res.end < child->number) ?
@@ -952,6 +949,7 @@ out:
 
        return max;
 }
+EXPORT_SYMBOL(pci_scan_bridge);
 
 /*
  * Read interrupt line and base address registers.
@@ -992,7 +990,6 @@ void set_pcie_hotplug_bridge(struct pci_dev *pdev)
                pdev->is_hotplug_bridge = 1;
 }
 
-
 /**
  * pci_ext_cfg_is_aliased - is ext config space just an alias of std config?
  * @dev: PCI device
@@ -1225,13 +1222,13 @@ int pci_setup_device(struct pci_dev *dev)
                break;
 
        default:                                    /* unknown header */
-               dev_err(&dev->dev, "unknown header type %02x, "
-                       "ignoring device\n", dev->hdr_type);
+               dev_err(&dev->dev, "unknown header type %02x, ignoring device\n",
+                       dev->hdr_type);
                return -EIO;
 
        bad:
-               dev_err(&dev->dev, "ignoring class %#08x (doesn't match header "
-                       "type %02x)\n", dev->class, dev->hdr_type);
+               dev_err(&dev->dev, "ignoring class %#08x (doesn't match header type %02x)\n",
+                       dev->class, dev->hdr_type);
                dev->class = PCI_CLASS_NOT_DEFINED;
        }
 
@@ -1283,7 +1280,7 @@ struct pci_dev *pci_alloc_dev(struct pci_bus *bus)
 EXPORT_SYMBOL(pci_alloc_dev);
 
 bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
-                                int crs_timeout)
+                               int crs_timeout)
 {
        int delay = 1;
 
@@ -1306,10 +1303,9 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
                        return false;
                /* Card hasn't responded in 60 seconds?  Must be stuck. */
                if (delay > crs_timeout) {
-                       printk(KERN_WARNING "pci %04x:%02x:%02x.%d: not "
-                                       "responding\n", pci_domain_nr(bus),
-                                       bus->number, PCI_SLOT(devfn),
-                                       PCI_FUNC(devfn));
+                       printk(KERN_WARNING "pci %04x:%02x:%02x.%d: not responding\n",
+                              pci_domain_nr(bus), bus->number, PCI_SLOT(devfn),
+                              PCI_FUNC(devfn));
                        return false;
                }
        }
@@ -1519,6 +1515,7 @@ int pci_scan_slot(struct pci_bus *bus, int devfn)
 
        return nr;
 }
+EXPORT_SYMBOL(pci_scan_slot);
 
 static int pcie_find_smpss(struct pci_dev *dev, void *data)
 {
@@ -1613,9 +1610,7 @@ static void pcie_write_mrrs(struct pci_dev *dev)
        }
 
        if (mrrs < 128)
-               dev_err(&dev->dev, "MRRS was unable to be configured with a "
-                       "safe value.  If problems are experienced, try running "
-                       "with pci=pcie_bus_safe.\n");
+               dev_err(&dev->dev, "MRRS was unable to be configured with a safe value.  If problems are experienced, try running with pci=pcie_bus_safe\n");
 }
 
 static void pcie_bus_detect_mps(struct pci_dev *dev)
@@ -1652,8 +1647,8 @@ static int pcie_bus_configure_set(struct pci_dev *dev, void *data)
        pcie_write_mps(dev, mps);
        pcie_write_mrrs(dev);
 
-       dev_info(&dev->dev, "Max Payload Size set to %4d/%4d (was %4d), "
-                "Max Read Rq %4d\n", pcie_get_mps(dev), 128 << dev->pcie_mpss,
+       dev_info(&dev->dev, "Max Payload Size set to %4d/%4d (was %4d), Max Read Rq %4d\n",
+                pcie_get_mps(dev), 128 << dev->pcie_mpss,
                 orig_mps, pcie_get_readrq(dev));
 
        return 0;
@@ -1716,7 +1711,7 @@ unsigned int pci_scan_child_bus(struct pci_bus *bus)
                bus->is_added = 1;
        }
 
-       for (pass=0; pass < 2; pass++)
+       for (pass = 0; pass < 2; pass++)
                list_for_each_entry(dev, &bus->devices, bus_list) {
                        if (pci_is_bridge(dev))
                                max = pci_scan_bridge(bus, dev, max, pass);
@@ -1732,6 +1727,7 @@ unsigned int pci_scan_child_bus(struct pci_bus *bus)
        dev_dbg(&bus->dev, "bus scan returning with max=%02x\n", max);
        return max;
 }
+EXPORT_SYMBOL_GPL(pci_scan_child_bus);
 
 /**
  * pcibios_root_bridge_prepare - Platform-specific host bridge setup.
@@ -2040,11 +2036,6 @@ unsigned int pci_rescan_bus(struct pci_bus *bus)
 }
 EXPORT_SYMBOL_GPL(pci_rescan_bus);
 
-EXPORT_SYMBOL(pci_add_new_bus);
-EXPORT_SYMBOL(pci_scan_slot);
-EXPORT_SYMBOL(pci_scan_bridge);
-EXPORT_SYMBOL_GPL(pci_scan_child_bus);
-
 /*
  * pci_rescan_bus(), pci_rescan_bus_bridge_resize() and PCI device removal
  * routines should always be executed under this mutex.
@@ -2063,7 +2054,8 @@ void pci_unlock_rescan_remove(void)
 }
 EXPORT_SYMBOL_GPL(pci_unlock_rescan_remove);
 
-static int __init pci_sort_bf_cmp(const struct device *d_a, const struct device *d_b)
+static int __init pci_sort_bf_cmp(const struct device *d_a,
+                                 const struct device *d_b)
 {
        const struct pci_dev *a = to_pci_dev(d_a);
        const struct pci_dev *b = to_pci_dev(d_b);
index 46d1378f2e9ebd1aeb6ee8252af551a0350bb80b..3f155e78513fd4171dc4b57c5b7b3b41e7afea20 100644 (file)
 
 static int proc_initialized;   /* = 0 */
 
-static loff_t
-proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
+static loff_t proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
 {
        struct pci_dev *dev = PDE_DATA(file_inode(file));
        return fixed_size_llseek(file, off, whence, dev->cfg_size);
 }
 
-static ssize_t
-proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
+static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
+                                size_t nbytes, loff_t *ppos)
 {
        struct pci_dev *dev = PDE_DATA(file_inode(file));
        unsigned int pos = *ppos;
@@ -108,8 +107,8 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp
        return nbytes;
 }
 
-static ssize_t
-proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, loff_t *ppos)
+static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf,
+                                 size_t nbytes, loff_t *ppos)
 {
        struct inode *ino = file_inode(file);
        struct pci_dev *dev = PDE_DATA(ino);
@@ -413,7 +412,7 @@ int pci_proc_detach_device(struct pci_dev *dev)
        return 0;
 }
 
-int pci_proc_detach_bus(struct pci_busbus)
+int pci_proc_detach_bus(struct pci_bus *bus)
 {
        proc_remove(bus->procdir);
        return 0;
@@ -423,6 +422,7 @@ static int proc_bus_pci_dev_open(struct inode *inode, struct file *file)
 {
        return seq_open(file, &proc_bus_pci_devices_op);
 }
+
 static const struct file_operations proc_bus_pci_dev_operations = {
        .owner          = THIS_MODULE,
        .open           = proc_bus_pci_dev_open,
@@ -443,6 +443,4 @@ static int __init pci_proc_init(void)
 
        return 0;
 }
-
 device_initcall(pci_proc_init);
-
index 92e68c7747f7dedfd43ec15e551bd6ab7c9b0c92..d0f69269eb6c4ced67b7ef688d20ff2bbd1f25cd 100644 (file)
@@ -48,8 +48,8 @@ static void quirk_mellanox_tavor(struct pci_dev *dev)
 {
        dev->broken_parity_status = 1;  /* This device gives false positives */
 }
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MELLANOX,PCI_DEVICE_ID_MELLANOX_TAVOR,quirk_mellanox_tavor);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MELLANOX,PCI_DEVICE_ID_MELLANOX_TAVOR_BRIDGE,quirk_mellanox_tavor);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MELLANOX, PCI_DEVICE_ID_MELLANOX_TAVOR, quirk_mellanox_tavor);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MELLANOX, PCI_DEVICE_ID_MELLANOX_TAVOR_BRIDGE, quirk_mellanox_tavor);
 
 /* Deal with broken BIOSes that neglect to enable passive release,
    which can cause problems in combination with the 82441FX/PPro MTRRs */
@@ -82,7 +82,7 @@ DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441,      quirk_p
 static void quirk_isa_dma_hangs(struct pci_dev *dev)
 {
        if (!isa_dma_bridge_buggy) {
-               isa_dma_bridge_buggy=1;
+               isa_dma_bridge_buggy = 1;
                dev_info(&dev->dev, "Activating ISA DMA hang workarounds\n");
        }
 }
@@ -123,7 +123,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TGP_LPC, quirk
  */
 static void quirk_nopcipci(struct pci_dev *dev)
 {
-       if ((pci_pci_problems & PCIPCI_FAIL)==0) {
+       if ((pci_pci_problems & PCIPCI_FAIL) == 0) {
                dev_info(&dev->dev, "Disabling direct PCI/PCI transfers\n");
                pci_pci_problems |= PCIPCI_FAIL;
        }
@@ -148,7 +148,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD,  PCI_DEVICE_ID_AMD_8151_0,       quirk_nopci
  */
 static void quirk_triton(struct pci_dev *dev)
 {
-       if ((pci_pci_problems&PCIPCI_TRITON)==0) {
+       if ((pci_pci_problems&PCIPCI_TRITON) == 0) {
                dev_info(&dev->dev, "Limiting direct PCI/PCI transfers\n");
                pci_pci_problems |= PCIPCI_TRITON;
        }
@@ -163,8 +163,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,        PCI_DEVICE_ID_INTEL_82439TX,    quirk_
  *     Made according to a windows driver based patch by George E. Breese
  *     see PCI Latency Adjust on http://www.viahardware.com/download/viatweak.shtm
  *     and http://www.georgebreese.com/net/software/#PCI
- *      Also see http://www.au-ja.org/review-kt133a-1-en.phtml for
- *      the info on which Mr Breese based his work.
+ *     Also see http://www.au-ja.org/review-kt133a-1-en.phtml for
+ *     the info on which Mr Breese based his work.
  *
  *     Updated based on further information from the site and also on
  *     information provided by VIA
@@ -177,14 +177,14 @@ static void quirk_vialatency(struct pci_dev *dev)
           a buggy southbridge */
 
        p = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, NULL);
-       if (p!=NULL) {
+       if (p != NULL) {
                /* 0x40 - 0x4f == 686B, 0x10 - 0x2f == 686A; thanks Dan Hollis */
                /* Check for buggy part revisions */
                if (p->revision < 0x40 || p->revision > 0x42)
                        goto exit;
        } else {
                p = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8231, NULL);
-               if (p==NULL)    /* No problem parts */
+               if (p == NULL)  /* No problem parts */
                        goto exit;
                /* Check for buggy part revisions */
                if (p->revision < 0x10 || p->revision > 0x12)
@@ -227,7 +227,7 @@ DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8361,         quirk_viala
  */
 static void quirk_viaetbf(struct pci_dev *dev)
 {
-       if ((pci_pci_problems&PCIPCI_VIAETBF)==0) {
+       if ((pci_pci_problems&PCIPCI_VIAETBF) == 0) {
                dev_info(&dev->dev, "Limiting direct PCI/PCI transfers\n");
                pci_pci_problems |= PCIPCI_VIAETBF;
        }
@@ -236,7 +236,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA,  PCI_DEVICE_ID_VIA_82C597_0,     quirk_via
 
 static void quirk_vsfx(struct pci_dev *dev)
 {
-       if ((pci_pci_problems&PCIPCI_VSFX)==0) {
+       if ((pci_pci_problems&PCIPCI_VSFX) == 0) {
                dev_info(&dev->dev, "Limiting direct PCI/PCI transfers\n");
                pci_pci_problems |= PCIPCI_VSFX;
        }
@@ -251,7 +251,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA,  PCI_DEVICE_ID_VIA_82C576,       quirk_vsfx)
  */
 static void quirk_alimagik(struct pci_dev *dev)
 {
-       if ((pci_pci_problems&PCIPCI_ALIMAGIK)==0) {
+       if ((pci_pci_problems&PCIPCI_ALIMAGIK) == 0) {
                dev_info(&dev->dev, "Limiting direct PCI/PCI transfers\n");
                pci_pci_problems |= PCIPCI_ALIMAGIK|PCIPCI_TRITON;
        }
@@ -265,7 +265,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL,   PCI_DEVICE_ID_AL_M1651,         quirk_alimagi
  */
 static void quirk_natoma(struct pci_dev *dev)
 {
-       if ((pci_pci_problems&PCIPCI_NATOMA)==0) {
+       if ((pci_pci_problems&PCIPCI_NATOMA) == 0) {
                dev_info(&dev->dev, "Limiting direct PCI/PCI transfers\n");
                pci_pci_problems |= PCIPCI_NATOMA;
        }
@@ -315,8 +315,7 @@ static void quirk_cs5536_vsa(struct pci_dev *dev)
        if (pci_resource_len(dev, 0) != 8) {
                struct resource *res = &dev->resource[0];
                res->end = res->start + 8 - 1;
-               dev_info(&dev->dev, "CS5536 ISA bridge bug detected "
-                               "(incorrect header); workaround applied.\n");
+               dev_info(&dev->dev, "CS5536 ISA bridge bug detected (incorrect header); workaround applied\n");
        }
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA, quirk_cs5536_vsa);
@@ -400,7 +399,8 @@ static void piix4_io_quirk(struct pci_dev *dev, const char *name, unsigned int p
         * let's get enough confirmation reports first.
         */
        base &= -size;
-       dev_info(&dev->dev, "%s PIO at %04x-%04x\n", name, base, base + size - 1);
+       dev_info(&dev->dev, "%s PIO at %04x-%04x\n", name, base,
+                base + size - 1);
 }
 
 static void piix4_mem_quirk(struct pci_dev *dev, const char *name, unsigned int port, unsigned int enable)
@@ -425,7 +425,8 @@ static void piix4_mem_quirk(struct pci_dev *dev, const char *name, unsigned int
         * reserve it, but let's get enough confirmation reports first.
         */
        base &= -size;
-       dev_info(&dev->dev, "%s MMIO at %04x-%04x\n", name, base, base + size - 1);
+       dev_info(&dev->dev, "%s MMIO at %04x-%04x\n", name, base,
+                base + size - 1);
 }
 
 /*
@@ -668,8 +669,7 @@ static void quirk_xio2000a(struct pci_dev *dev)
        struct pci_dev *pdev;
        u16 command;
 
-       dev_warn(&dev->dev, "TI XIO2000a quirk detected; "
-               "secondary bus fast back-to-back transfers disabled\n");
+       dev_warn(&dev->dev, "TI XIO2000a quirk detected; secondary bus fast back-to-back transfers disabled\n");
        list_for_each_entry(pdev, &dev->subordinate->devices, bus_list) {
                pci_read_config_word(pdev, PCI_COMMAND, &command);
                if (command & PCI_COMMAND_FAST_BACK)
@@ -703,7 +703,7 @@ static void quirk_via_ioapic(struct pci_dev *dev)
               tmp == 0 ? "Disa" : "Ena");
 
        /* Offset 0x58: External APIC IRQ output control */
-       pci_write_config_byte (dev, 0x58, tmp);
+       pci_write_config_byte(dev, 0x58, tmp);
 }
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA,     PCI_DEVICE_ID_VIA_82C686,       quirk_via_ioapic);
 DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_VIA,      PCI_DEVICE_ID_VIA_82C686,       quirk_via_ioapic);
@@ -761,8 +761,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI,   PCI_ANY_ID,                     quirk_ioapic_rmw);
 static void quirk_amd_8131_mmrbc(struct pci_dev *dev)
 {
        if (dev->subordinate && dev->revision <= 0x12) {
-               dev_info(&dev->dev, "AMD8131 rev %x detected; "
-                       "disabling PCI-X MMRBC\n", dev->revision);
+               dev_info(&dev->dev, "AMD8131 rev %x detected; disabling PCI-X MMRBC\n",
+                        dev->revision);
                dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MMRBC;
        }
 }
@@ -916,12 +916,12 @@ static void quirk_amd_ordering(struct pci_dev *dev)
 {
        u32 pcic;
        pci_read_config_dword(dev, 0x4C, &pcic);
-       if ((pcic&6)!=6) {
+       if ((pcic & 6) != 6) {
                pcic |= 6;
                dev_warn(&dev->dev, "BIOS failed to enable PCI standards compliance; fixing this error\n");
                pci_write_config_dword(dev, 0x4C, pcic);
                pci_read_config_dword(dev, 0x84, &pcic);
-               pcic |= (1<<23);        /* Required in this mode */
+               pcic |= (1 << 23);      /* Required in this mode */
                pci_write_config_dword(dev, 0x84, pcic);
        }
 }
@@ -937,7 +937,7 @@ DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD,   PCI_DEVICE_ID_AMD_FE_GATE_700C
  */
 static void quirk_dunord(struct pci_dev *dev)
 {
-       struct resource *r = &dev->resource [1];
+       struct resource *r = &dev->resource[1];
 
        r->flags |= IORESOURCE_UNSET;
        r->start = 0;
@@ -967,11 +967,13 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TOSHIBA,   0x605,  quirk_transparent_bridge)
 static void quirk_mediagx_master(struct pci_dev *dev)
 {
        u8 reg;
+
        pci_read_config_byte(dev, 0x41, &reg);
        if (reg & 2) {
                reg &= ~2;
-               dev_info(&dev->dev, "Fixup for MediaGX/Geode Slave Disconnect Boundary (0x41=0x%02x)\n", reg);
-                pci_write_config_byte(dev, 0x41, reg);
+               dev_info(&dev->dev, "Fixup for MediaGX/Geode Slave Disconnect Boundary (0x41=0x%02x)\n",
+                        reg);
+               pci_write_config_byte(dev, 0x41, reg);
        }
 }
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CYRIX,   PCI_DEVICE_ID_CYRIX_PCI_MASTER, quirk_mediagx_master);
@@ -1120,7 +1122,7 @@ static void asus_hides_smbus_hostbridge(struct pci_dev *dev)
 {
        if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_ASUSTEK)) {
                if (dev->device == PCI_DEVICE_ID_INTEL_82845_HB)
-                       switch(dev->subsystem_device) {
+                       switch (dev->subsystem_device) {
                        case 0x8025: /* P4B-LX */
                        case 0x8070: /* P4B */
                        case 0x8088: /* P4B533 */
@@ -1128,14 +1130,14 @@ static void asus_hides_smbus_hostbridge(struct pci_dev *dev)
                                asus_hides_smbus = 1;
                        }
                else if (dev->device == PCI_DEVICE_ID_INTEL_82845G_HB)
-                       switch(dev->subsystem_device) {
+                       switch (dev->subsystem_device) {
                        case 0x80b1: /* P4GE-V */
                        case 0x80b2: /* P4PE */
                        case 0x8093: /* P4B533-V */
                                asus_hides_smbus = 1;
                        }
                else if (dev->device == PCI_DEVICE_ID_INTEL_82850_HB)
-                       switch(dev->subsystem_device) {
+                       switch (dev->subsystem_device) {
                        case 0x8030: /* P4T533 */
                                asus_hides_smbus = 1;
                        }
@@ -1175,7 +1177,7 @@ static void asus_hides_smbus_hostbridge(struct pci_dev *dev)
                        }
        } else if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_HP)) {
                if (dev->device ==  PCI_DEVICE_ID_INTEL_82855PM_HB)
-                       switch(dev->subsystem_device) {
+                       switch (dev->subsystem_device) {
                        case 0x088C: /* HP Compaq nc8000 */
                        case 0x0890: /* HP Compaq nc6000 */
                                asus_hides_smbus = 1;
@@ -1192,20 +1194,20 @@ static void asus_hides_smbus_hostbridge(struct pci_dev *dev)
                        case 0x12bf: /* HP xw4100 */
                                asus_hides_smbus = 1;
                        }
-       } else if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG)) {
-               if (dev->device ==  PCI_DEVICE_ID_INTEL_82855PM_HB)
-                       switch(dev->subsystem_device) {
-                       case 0xC00C: /* Samsung P35 notebook */
-                               asus_hides_smbus = 1;
-                       }
+       } else if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG)) {
+               if (dev->device ==  PCI_DEVICE_ID_INTEL_82855PM_HB)
+                       switch (dev->subsystem_device) {
+                       case 0xC00C: /* Samsung P35 notebook */
+                               asus_hides_smbus = 1;
+               }
        } else if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_COMPAQ)) {
                if (dev->device == PCI_DEVICE_ID_INTEL_82855PM_HB)
-                       switch(dev->subsystem_device) {
+                       switch (dev->subsystem_device) {
                        case 0x0058: /* Compaq Evo N620c */
                                asus_hides_smbus = 1;
                        }
                else if (dev->device == PCI_DEVICE_ID_INTEL_82810_IG3)
-                       switch(dev->subsystem_device) {
+                       switch (dev->subsystem_device) {
                        case 0xB16C: /* Compaq Deskpro EP 401963-001 (PCA# 010174) */
                                /* Motherboard doesn't have Host bridge
                                 * subvendor/subdevice IDs, therefore checking
@@ -1213,7 +1215,7 @@ static void asus_hides_smbus_hostbridge(struct pci_dev *dev)
                                asus_hides_smbus = 1;
                        }
                else if (dev->device == PCI_DEVICE_ID_INTEL_82801DB_2)
-                       switch(dev->subsystem_device) {
+                       switch (dev->subsystem_device) {
                        case 0x00b8: /* Compaq Evo D510 CMT */
                        case 0x00b9: /* Compaq Evo D510 SFF */
                        case 0x00ba: /* Compaq Evo D510 USDT */
@@ -1261,7 +1263,8 @@ static void asus_hides_smbus_lpc(struct pci_dev *dev)
                pci_write_config_word(dev, 0xF2, val & (~0x8));
                pci_read_config_word(dev, 0xF2, &val);
                if (val & 0x8)
-                       dev_info(&dev->dev, "i801 SMBus device continues to play 'hide and seek'! 0x%x\n", val);
+                       dev_info(&dev->dev, "i801 SMBus device continues to play 'hide and seek'! 0x%x\n",
+                                val);
                else
                        dev_info(&dev->dev, "Enabled i801 SMBus device\n");
        }
@@ -1409,7 +1412,8 @@ static void asus_hides_ac97_lpc(struct pci_dev *dev)
                pci_write_config_byte(dev, 0x50, val & (~0xc0));
                pci_read_config_byte(dev, 0x50, &val);
                if (val & 0xc0)
-                       dev_info(&dev->dev, "Onboard AC97/MC97 devices continue to play 'hide and seek'! 0x%x\n", val);
+                       dev_info(&dev->dev, "Onboard AC97/MC97 devices continue to play 'hide and seek'! 0x%x\n",
+                                val);
                else
                        dev_info(&dev->dev, "Enabled onboard AC97/MC97 devices\n");
        }
@@ -1514,10 +1518,8 @@ static void quirk_alder_ioapic(struct pci_dev *pdev)
 
        /* The next five BARs all seem to be rubbish, so just clean
         * them out */
-       for (i=1; i < 6; i++) {
+       for (i = 1; i < 6; i++)
                memset(&pdev->resource[i], 0, sizeof(pdev->resource[i]));
-       }
-
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,  PCI_DEVICE_ID_INTEL_EESSC,      quirk_alder_ioapic);
 #endif
@@ -1552,7 +1554,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL,      PCI_DEVICE_ID_INTEL_PXHV,       quirk_pci
  * Some Intel PCI Express chipsets have trouble with downstream
  * device power management.
  */
-static void quirk_intel_pcie_pm(struct pci_dev * dev)
+static void quirk_intel_pcie_pm(struct pci_dev *dev)
 {
        pci_pm_d3_delay = 120;
        dev->no_d1d2 = 1;
@@ -1721,8 +1723,8 @@ static void quirk_disable_amd_8111_boot_interrupt(struct pci_dev *dev)
 
        pci_read_config_word(dev, AMD_8111_PCI_IRQ_ROUTING, &pci_config_word);
        if (!pci_config_word) {
-               dev_info(&dev->dev, "boot interrupts on device [%04x:%04x] "
-                        "already disabled\n", dev->vendor, dev->device);
+               dev_info(&dev->dev, "boot interrupts on device [%04x:%04x] already disabled\n",
+                        dev->vendor, dev->device);
                return;
        }
        pci_write_config_word(dev, AMD_8111_PCI_IRQ_ROUTING, 0);
@@ -1770,8 +1772,7 @@ static void quirk_plx_pci9050(struct pci_dev *dev)
                if (pci_resource_len(dev, bar) == 0x80 &&
                    (pci_resource_start(dev, bar) & 0x80)) {
                        struct resource *r = &dev->resource[bar];
-                       dev_info(&dev->dev,
-                                "Re-allocating PLX PCI 9050 BAR %u to length 256 to avoid bit 7 bug\n",
+                       dev_info(&dev->dev, "Re-allocating PLX PCI 9050 BAR %u to length 256 to avoid bit 7 bug\n",
                                 bar);
                        r->flags |= IORESOURCE_UNSET;
                        r->start = 0;
@@ -1818,9 +1819,7 @@ static void quirk_netmos(struct pci_dev *dev)
        case PCI_DEVICE_ID_NETMOS_9845:
        case PCI_DEVICE_ID_NETMOS_9855:
                if (num_parallel) {
-                       dev_info(&dev->dev, "Netmos %04x (%u parallel, "
-                               "%u serial); changing class SERIAL to OTHER "
-                               "(use parport_serial)\n",
+                       dev_info(&dev->dev, "Netmos %04x (%u parallel, %u serial); changing class SERIAL to OTHER (use parport_serial)\n",
                                dev->device, num_parallel, num_serial);
                        dev->class = (PCI_CLASS_COMMUNICATION_OTHER << 8) |
                            (dev->class & 0xff);
@@ -1887,8 +1886,7 @@ static void quirk_e100_interrupt(struct pci_dev *dev)
 
        cmd_hi = readb(csr + 3);
        if (cmd_hi == 0) {
-               dev_warn(&dev->dev, "Firmware left e100 interrupts enabled; "
-                       "disabling\n");
+               dev_warn(&dev->dev, "Firmware left e100 interrupts enabled; disabling\n");
                writeb(1, csr + 3);
        }
 
@@ -1958,8 +1956,7 @@ static void quirk_nvidia_ck804_pcie_aer_ext_cap(struct pci_dev *dev)
        if (pci_read_config_byte(dev, 0xf41, &b) == 0) {
                if (!(b & 0x20)) {
                        pci_write_config_byte(dev, 0xf41, b | 0x20);
-                       dev_info(&dev->dev,
-                              "Linking AER extended capability\n");
+                       dev_info(&dev->dev, "Linking AER extended capability\n");
                }
        }
 }
@@ -1997,8 +1994,7 @@ static void quirk_via_cx700_pci_parking_caching(struct pci_dev *dev)
                        /* Turn off PCI Bus Parking */
                        pci_write_config_byte(dev, 0x76, b ^ 0x40);
 
-                       dev_info(&dev->dev,
-                               "Disabling VIA CX700 PCI parking\n");
+                       dev_info(&dev->dev, "Disabling VIA CX700 PCI parking\n");
                }
        }
 
@@ -2013,8 +2009,7 @@ static void quirk_via_cx700_pci_parking_caching(struct pci_dev *dev)
                        /* Disable "Read FIFO Timer" */
                        pci_write_config_byte(dev, 0x77, 0x0);
 
-                       dev_info(&dev->dev,
-                               "Disabling VIA CX700 PCI caching\n");
+                       dev_info(&dev->dev, "Disabling VIA CX700 PCI caching\n");
                }
        }
 }
@@ -2149,8 +2144,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8380_0, quirk_disab
 static void quirk_disable_msi(struct pci_dev *dev)
 {
        if (dev->subordinate) {
-               dev_warn(&dev->dev, "MSI quirk detected; "
-                       "subordinate MSI disabled\n");
+               dev_warn(&dev->dev, "MSI quirk detected; subordinate MSI disabled\n");
                dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
        }
 }
@@ -2189,8 +2183,7 @@ static int msi_ht_cap_enabled(struct pci_dev *dev)
                u8 flags;
 
                if (pci_read_config_byte(dev, pos + HT_MSI_FLAGS,
-                                        &flags) == 0)
-               {
+                                        &flags) == 0) {
                        dev_info(&dev->dev, "Found %s HT MSI Mapping\n",
                                flags & HT_MSI_FLAGS_ENABLE ?
                                "enabled" : "disabled");
@@ -2207,8 +2200,7 @@ static int msi_ht_cap_enabled(struct pci_dev *dev)
 static void quirk_msi_ht_cap(struct pci_dev *dev)
 {
        if (dev->subordinate && !msi_ht_cap_enabled(dev)) {
-               dev_warn(&dev->dev, "MSI quirk detected; "
-                       "subordinate MSI disabled\n");
+               dev_warn(&dev->dev, "MSI quirk detected; subordinate MSI disabled\n");
                dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
        }
 }
@@ -2232,8 +2224,7 @@ static void quirk_nvidia_ck804_msi_ht_cap(struct pci_dev *dev)
        if (!pdev)
                return;
        if (!msi_ht_cap_enabled(dev) && !msi_ht_cap_enabled(pdev)) {
-               dev_warn(&dev->dev, "MSI quirk detected; "
-                       "subordinate MSI disabled\n");
+               dev_warn(&dev->dev, "MSI quirk detected; subordinate MSI disabled\n");
                dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
        }
        pci_dev_put(pdev);
@@ -2279,8 +2270,7 @@ static void nvenet_msi_disable(struct pci_dev *dev)
        if (board_name &&
            (strstr(board_name, "P5N32-SLI PREMIUM") ||
             strstr(board_name, "P5N32-E SLI"))) {
-               dev_info(&dev->dev,
-                        "Disabling msi for MCP55 NIC on P5N32-SLI\n");
+               dev_info(&dev->dev, "Disabling msi for MCP55 NIC on P5N32-SLI\n");
                dev->no_msi = 1;
        }
 }
@@ -2489,8 +2479,7 @@ static void __nv_msi_ht_cap_quirk(struct pci_dev *dev, int all)
         */
        host_bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0));
        if (host_bridge == NULL) {
-               dev_warn(&dev->dev,
-                        "nv_msi_ht_cap_quirk didn't locate host bridge\n");
+               dev_warn(&dev->dev, "nv_msi_ht_cap_quirk didn't locate host bridge\n");
                return;
        }
 
@@ -2817,8 +2806,7 @@ static void quirk_intel_mc_errata(struct pci_dev *dev)
         */
        err = pci_read_config_word(dev, 0x48, &rcc);
        if (err) {
-               dev_err(&dev->dev, "Error attempting to read the read "
-                       "completion coalescing register.\n");
+               dev_err(&dev->dev, "Error attempting to read the read completion coalescing register\n");
                return;
        }
 
@@ -2829,13 +2817,11 @@ static void quirk_intel_mc_errata(struct pci_dev *dev)
 
        err = pci_write_config_word(dev, 0x48, rcc);
        if (err) {
-               dev_err(&dev->dev, "Error attempting to write the read "
-                       "completion coalescing register.\n");
+               dev_err(&dev->dev, "Error attempting to write the read completion coalescing register\n");
                return;
        }
 
-       pr_info_once("Read completion coalescing disabled due to hardware "
-                    "errata relating to 256B MPS.\n");
+       pr_info_once("Read completion coalescing disabled due to hardware errata relating to 256B MPS\n");
 }
 /* Intel 5000 series memory controllers and ports 2-7 */
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x25c0, quirk_intel_mc_errata);
@@ -2944,8 +2930,7 @@ static void disable_igfx_irq(struct pci_dev *dev)
 
        /* Check if any interrupt line is still enabled */
        if (readl(regs + I915_DEIER_REG) != 0) {
-               dev_warn(&dev->dev, "BIOS left Intel GPU interrupts enabled; "
-                       "disabling\n");
+               dev_warn(&dev->dev, "BIOS left Intel GPU interrupts enabled; disabling\n");
 
                writel(0, regs + I915_DEIER_REG);
        }
@@ -3040,7 +3025,7 @@ void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev)
 {
        struct pci_fixup *start, *end;
 
-       switch(pass) {
+       switch (pass) {
        case pci_fixup_early:
                start = __start_pci_fixups_early;
                end = __end_pci_fixups_early;
@@ -3112,8 +3097,8 @@ static int __init pci_apply_final_quirks(void)
                        if (!tmp || cls == tmp)
                                continue;
 
-                       printk(KERN_DEBUG "PCI: CLS mismatch (%u != %u), "
-                              "using %u bytes\n", cls << 2, tmp << 2,
+                       printk(KERN_DEBUG "PCI: CLS mismatch (%u != %u), using %u bytes\n",
+                              cls << 2, tmp << 2,
                               pci_dfl_cache_line_size << 2);
                        pci_cache_line_size = pci_dfl_cache_line_size;
                }
@@ -3342,6 +3327,85 @@ int pci_dev_specific_reset(struct pci_dev *dev, int probe)
        return -ENOTTY;
 }
 
+static void quirk_dma_func0_alias(struct pci_dev *dev)
+{
+       if (PCI_FUNC(dev->devfn) != 0) {
+               dev->dma_alias_devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 0);
+               dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN;
+       }
+}
+
+/*
+ * https://bugzilla.redhat.com/show_bug.cgi?id=605888
+ *
+ * Some Ricoh devices use function 0 as the PCIe requester ID for DMA.
+ */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_RICOH, 0xe832, quirk_dma_func0_alias);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_RICOH, 0xe476, quirk_dma_func0_alias);
+
+static void quirk_dma_func1_alias(struct pci_dev *dev)
+{
+       if (PCI_FUNC(dev->devfn) != 1) {
+               dev->dma_alias_devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 1);
+               dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN;
+       }
+}
+
+/*
+ * Marvell 88SE9123 uses function 1 as the requester ID for DMA.  In some
+ * SKUs function 1 is present and is a legacy IDE controller, in other
+ * SKUs this function is not present, making this a ghost requester.
+ * https://bugzilla.kernel.org/show_bug.cgi?id=42679
+ */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9123,
+                        quirk_dma_func1_alias);
+/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c14 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9130,
+                        quirk_dma_func1_alias);
+/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c47 + c57 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9172,
+                        quirk_dma_func1_alias);
+/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c59 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x917a,
+                        quirk_dma_func1_alias);
+/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c46 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x91a0,
+                        quirk_dma_func1_alias);
+/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c49 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9230,
+                        quirk_dma_func1_alias);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TTI, 0x0642,
+                        quirk_dma_func1_alias);
+/* https://bugs.gentoo.org/show_bug.cgi?id=497630 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_JMICRON,
+                        PCI_DEVICE_ID_JMICRON_JMB388_ESD,
+                        quirk_dma_func1_alias);
+
+/*
+ * A few PCIe-to-PCI bridges fail to expose a PCIe capability, resulting in
+ * using the wrong DMA alias for the device.  Some of these devices can be
+ * used as either forward or reverse bridges, so we need to test whether the
+ * device is operating in the correct mode.  We could probably apply this
+ * quirk to PCI_ANY_ID, but for now we'll just use known offenders.  The test
+ * is for a non-root, non-PCIe bridge where the upstream device is PCIe and
+ * is not a PCIe-to-PCI bridge, then @pdev is actually a PCIe-to-PCI bridge.
+ */
+static void quirk_use_pcie_bridge_dma_alias(struct pci_dev *pdev)
+{
+       if (!pci_is_root_bus(pdev->bus) &&
+           pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
+           !pci_is_pcie(pdev) && pci_is_pcie(pdev->bus->self) &&
+           pci_pcie_type(pdev->bus->self) != PCI_EXP_TYPE_PCI_BRIDGE)
+               pdev->dev_flags |= PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS;
+}
+/* ASM1083/1085, https://bugzilla.kernel.org/show_bug.cgi?id=44881#c46 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ASMEDIA, 0x1080,
+                        quirk_use_pcie_bridge_dma_alias);
+/* Tundra 8113, https://bugzilla.kernel.org/show_bug.cgi?id=44881#c43 */
+DECLARE_PCI_FIXUP_HEADER(0x10e3, 0x8113, quirk_use_pcie_bridge_dma_alias);
+/* ITE 8892, https://bugzilla.kernel.org/show_bug.cgi?id=73551 */
+DECLARE_PCI_FIXUP_HEADER(0x1283, 0x8892, quirk_use_pcie_bridge_dma_alias);
+
 static struct pci_dev *pci_func_0_dma_source(struct pci_dev *dev)
 {
        if (!PCI_FUNC(dev->devfn))
index c1839450d4d6cc4d77a5b37e983b7dbecdafe31b..f955edb9bea7e801b186d2b431359dd0bf7d6047 100644 (file)
@@ -38,6 +38,7 @@ int pci_enable_rom(struct pci_dev *pdev)
        pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
        return 0;
 }
+EXPORT_SYMBOL_GPL(pci_enable_rom);
 
 /**
  * pci_disable_rom - disable ROM decoding for a PCI device
@@ -53,6 +54,7 @@ void pci_disable_rom(struct pci_dev *pdev)
        rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
        pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
 }
+EXPORT_SYMBOL_GPL(pci_disable_rom);
 
 /**
  * pci_get_rom_size - obtain the actual size of the ROM image
@@ -135,7 +137,7 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
                } else {
                        /* assign the ROM an address if it doesn't have one */
                        if (res->parent == NULL &&
-                           pci_assign_resource(pdev,PCI_ROM_RESOURCE))
+                           pci_assign_resource(pdev, PCI_ROM_RESOURCE))
                                return NULL;
                        start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
                        *size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
@@ -166,6 +168,7 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
        *size = pci_get_rom_size(pdev, rom, *size);
        return rom;
 }
+EXPORT_SYMBOL(pci_map_rom);
 
 /**
  * pci_unmap_rom - unmap the ROM from kernel space
@@ -187,6 +190,7 @@ void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom)
        if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
                pci_disable_rom(pdev);
 }
+EXPORT_SYMBOL(pci_unmap_rom);
 
 /**
  * pci_cleanup_rom - free the ROM copy created by pci_map_rom_copy
@@ -199,7 +203,7 @@ void pci_cleanup_rom(struct pci_dev *pdev)
        struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
 
        if (res->flags & IORESOURCE_ROM_COPY) {
-               kfree((void*)(unsigned long)res->start);
+               kfree((void *)(unsigned long)res->start);
                res->flags |= IORESOURCE_UNSET;
                res->flags &= ~IORESOURCE_ROM_COPY;
                res->start = 0;
@@ -222,9 +226,4 @@ void __iomem *pci_platform_rom(struct pci_dev *pdev, size_t *size)
 
        return NULL;
 }
-
-EXPORT_SYMBOL(pci_map_rom);
-EXPORT_SYMBOL(pci_unmap_rom);
-EXPORT_SYMBOL_GPL(pci_enable_rom);
-EXPORT_SYMBOL_GPL(pci_disable_rom);
 EXPORT_SYMBOL(pci_platform_rom);
index 8e495bda678f2e0e9a927ac993c27da8f585e37a..827ad831f1dd67335cf3918ed960f81579fc8405 100644 (file)
 DECLARE_RWSEM(pci_bus_sem);
 EXPORT_SYMBOL_GPL(pci_bus_sem);
 
+/*
+ * pci_for_each_dma_alias - Iterate over DMA aliases for a device
+ * @pdev: starting downstream device
+ * @fn: function to call for each alias
+ * @data: opaque data to pass to @fn
+ *
+ * Starting @pdev, walk up the bus calling @fn for each possible alias
+ * of @pdev at the root bus.
+ */
+int pci_for_each_dma_alias(struct pci_dev *pdev,
+                          int (*fn)(struct pci_dev *pdev,
+                                    u16 alias, void *data), void *data)
+{
+       struct pci_bus *bus;
+       int ret;
+
+       ret = fn(pdev, PCI_DEVID(pdev->bus->number, pdev->devfn), data);
+       if (ret)
+               return ret;
+
+       /*
+        * If the device is broken and uses an alias requester ID for
+        * DMA, iterate over that too.
+        */
+       if (unlikely(pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN)) {
+               ret = fn(pdev, PCI_DEVID(pdev->bus->number,
+                                        pdev->dma_alias_devfn), data);
+               if (ret)
+                       return ret;
+       }
+
+       for (bus = pdev->bus; !pci_is_root_bus(bus); bus = bus->parent) {
+               struct pci_dev *tmp;
+
+               /* Skip virtual buses */
+               if (!bus->self)
+                       continue;
+
+               tmp = bus->self;
+
+               /*
+                * PCIe-to-PCI/X bridges alias transactions from downstream
+                * devices using the subordinate bus number (PCI Express to
+                * PCI/PCI-X Bridge Spec, rev 1.0, sec 2.3).  For all cases
+                * where the upstream bus is PCI/X we alias to the bridge
+                * (there are various conditions in the previous reference
+                * where the bridge may take ownership of transactions, even
+                * when the secondary interface is PCI-X).
+                */
+               if (pci_is_pcie(tmp)) {
+                       switch (pci_pcie_type(tmp)) {
+                       case PCI_EXP_TYPE_ROOT_PORT:
+                       case PCI_EXP_TYPE_UPSTREAM:
+                       case PCI_EXP_TYPE_DOWNSTREAM:
+                               continue;
+                       case PCI_EXP_TYPE_PCI_BRIDGE:
+                               ret = fn(tmp,
+                                        PCI_DEVID(tmp->subordinate->number,
+                                                  PCI_DEVFN(0, 0)), data);
+                               if (ret)
+                                       return ret;
+                               continue;
+                       case PCI_EXP_TYPE_PCIE_BRIDGE:
+                               ret = fn(tmp,
+                                        PCI_DEVID(tmp->bus->number,
+                                                  tmp->devfn), data);
+                               if (ret)
+                                       return ret;
+                               continue;
+                       }
+               } else {
+                       if (tmp->dev_flags & PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS)
+                               ret = fn(tmp,
+                                        PCI_DEVID(tmp->subordinate->number,
+                                                  PCI_DEVFN(0, 0)), data);
+                       else
+                               ret = fn(tmp,
+                                        PCI_DEVID(tmp->bus->number,
+                                                  tmp->devfn), data);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       return ret;
+}
+
 /*
  * find the upstream PCIe-to-PCI bridge of a PCI device
  * if the device is PCIE, return NULL
@@ -23,8 +110,7 @@ EXPORT_SYMBOL_GPL(pci_bus_sem);
  * legacy PCI bridge and the bridge is directly connected to bus 0), return its
  * parent
  */
-struct pci_dev *
-pci_find_upstream_pcie_bridge(struct pci_dev *pdev)
+struct pci_dev *pci_find_upstream_pcie_bridge(struct pci_dev *pdev)
 {
        struct pci_dev *tmp = NULL;
 
@@ -56,12 +142,12 @@ static struct pci_bus *pci_do_find_bus(struct pci_bus *bus, unsigned char busnr)
        struct pci_bus *child;
        struct pci_bus *tmp;
 
-       if(bus->number == busnr)
+       if (bus->number == busnr)
                return bus;
 
        list_for_each_entry(tmp, &bus->children, node) {
                child = pci_do_find_bus(tmp, busnr);
-               if(child)
+               if (child)
                        return child;
        }
        return NULL;
@@ -76,7 +162,7 @@ static struct pci_bus *pci_do_find_bus(struct pci_bus *bus, unsigned char busnr)
  * in the global list of PCI buses.  If the bus is found, a pointer to its
  * data structure is returned.  If no bus is found, %NULL is returned.
  */
-struct pci_bus * pci_find_bus(int domain, int busnr)
+struct pci_bus *pci_find_bus(int domain, int busnr)
 {
        struct pci_bus *bus = NULL;
        struct pci_bus *tmp_bus;
@@ -90,6 +176,7 @@ struct pci_bus * pci_find_bus(int domain, int busnr)
        }
        return NULL;
 }
+EXPORT_SYMBOL(pci_find_bus);
 
 /**
  * pci_find_next_bus - begin or continue searching for a PCI bus
@@ -100,8 +187,7 @@ struct pci_bus * pci_find_bus(int domain, int busnr)
  * @from is not %NULL, searches continue from next device on the
  * global list.
  */
-struct pci_bus *
-pci_find_next_bus(const struct pci_bus *from)
+struct pci_bus *pci_find_next_bus(const struct pci_bus *from)
 {
        struct list_head *n;
        struct pci_bus *b = NULL;
@@ -114,6 +200,7 @@ pci_find_next_bus(const struct pci_bus *from)
        up_read(&pci_bus_sem);
        return b;
 }
+EXPORT_SYMBOL(pci_find_next_bus);
 
 /**
  * pci_get_slot - locate PCI device for a given PCI slot
@@ -147,6 +234,7 @@ struct pci_dev *pci_get_slot(struct pci_bus *bus, unsigned int devfn)
        up_read(&pci_bus_sem);
        return dev;
 }
+EXPORT_SYMBOL(pci_get_slot);
 
 /**
  * pci_get_domain_bus_and_slot - locate PCI device for a given PCI domain (segment), bus, and slot
@@ -251,6 +339,7 @@ struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device,
 
        return pci_get_dev_by_id(&id, from);
 }
+EXPORT_SYMBOL(pci_get_subsys);
 
 /**
  * pci_get_device - begin or continue searching for a PCI device by vendor/device id
@@ -266,11 +355,12 @@ struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device,
  * from next device on the global list.  The reference count for @from is
  * always decremented if it is not %NULL.
  */
-struct pci_dev *
-pci_get_device(unsigned int vendor, unsigned int device, struct pci_dev *from)
+struct pci_dev *pci_get_device(unsigned int vendor, unsigned int device,
+                              struct pci_dev *from)
 {
        return pci_get_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from);
 }
+EXPORT_SYMBOL(pci_get_device);
 
 /**
  * pci_get_class - begin or continue searching for a PCI device by class
@@ -299,6 +389,7 @@ struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from)
 
        return pci_get_dev_by_id(&id, from);
 }
+EXPORT_SYMBOL(pci_get_class);
 
 /**
  * pci_dev_present - Returns 1 if device matching the device list is present, 0 if not.
@@ -328,12 +419,3 @@ int pci_dev_present(const struct pci_device_id *ids)
        return 0;
 }
 EXPORT_SYMBOL(pci_dev_present);
-
-/* For boot time work */
-EXPORT_SYMBOL(pci_find_bus);
-EXPORT_SYMBOL(pci_find_next_bus);
-/* For everyone */
-EXPORT_SYMBOL(pci_get_device);
-EXPORT_SYMBOL(pci_get_subsys);
-EXPORT_SYMBOL(pci_get_slot);
-EXPORT_SYMBOL(pci_get_class);
index fd9b545c3cf5f32bd51a15460063b86c363e91f1..a5a63ecfb6281837637cdc32846034ed2f226210 100644 (file)
@@ -68,7 +68,7 @@ static int add_to_list(struct list_head *head,
 
        tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
        if (!tmp) {
-               pr_warning("add_to_list: kmalloc() failed!\n");
+               pr_warn("add_to_list: kmalloc() failed!\n");
                return -ENOMEM;
        }
 
@@ -148,8 +148,7 @@ static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head)
 
                tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
                if (!tmp)
-                       panic("pdev_sort_resources(): "
-                             "kmalloc() failed!\n");
+                       panic("pdev_sort_resources(): kmalloc() failed!\n");
                tmp->res = r;
                tmp->dev = dev;
 
@@ -736,7 +735,7 @@ static resource_size_t calculate_iosize(resource_size_t size,
 {
        if (size < min_size)
                size = min_size;
-       if (old_size == 1 )
+       if (old_size == 1)
                old_size = 0;
        /* To be fixed in 2.5: we should have sort of HAVE_ISA
           flag in the struct pci_bus. */
@@ -757,7 +756,7 @@ static resource_size_t calculate_memsize(resource_size_t size,
 {
        if (size < min_size)
                size = min_size;
-       if (old_size == 1 )
+       if (old_size == 1)
                old_size = 0;
        if (size < old_size)
                size = old_size;
@@ -859,9 +858,8 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
                        resource_size(b_res), min_align);
        if (!size0 && !size1) {
                if (b_res->start || b_res->end)
-                       dev_info(&bus->self->dev, "disabling bridge window "
-                                "%pR to %pR (unused)\n", b_res,
-                                &bus->busn_res);
+                       dev_info(&bus->self->dev, "disabling bridge window %pR to %pR (unused)\n",
+                                b_res, &bus->busn_res);
                b_res->flags = 0;
                return;
        }
@@ -872,10 +870,9 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
        if (size1 > size0 && realloc_head) {
                add_to_list(realloc_head, bus->self, b_res, size1-size0,
                            min_align);
-               dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window "
-                                "%pR to %pR add_size %llx\n", b_res,
-                                &bus->busn_res,
-                                (unsigned long long)size1-size0);
+               dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window %pR to %pR add_size %llx\n",
+                          b_res, &bus->busn_res,
+                          (unsigned long long)size1-size0);
        }
 }
 
@@ -974,9 +971,8 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
                        if (order < 0)
                                order = 0;
                        if (order >= ARRAY_SIZE(aligns)) {
-                               dev_warn(&dev->dev, "disabling BAR %d: %pR "
-                                        "(bad alignment %#llx)\n", i, r,
-                                        (unsigned long long) align);
+                               dev_warn(&dev->dev, "disabling BAR %d: %pR (bad alignment %#llx)\n",
+                                        i, r, (unsigned long long) align);
                                r->flags = 0;
                                continue;
                        }
@@ -1003,9 +999,8 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
                                resource_size(b_res), min_align);
        if (!size0 && !size1) {
                if (b_res->start || b_res->end)
-                       dev_info(&bus->self->dev, "disabling bridge window "
-                                "%pR to %pR (unused)\n", b_res,
-                                &bus->busn_res);
+                       dev_info(&bus->self->dev, "disabling bridge window %pR to %pR (unused)\n",
+                                b_res, &bus->busn_res);
                b_res->flags = 0;
                return 0;
        }
@@ -1014,9 +1009,9 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
        b_res->flags |= IORESOURCE_STARTALIGN;
        if (size1 > size0 && realloc_head) {
                add_to_list(realloc_head, bus->self, b_res, size1-size0, min_align);
-               dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window "
-                                "%pR to %pR add_size %llx\n", b_res,
-                                &bus->busn_res, (unsigned long long)size1-size0);
+               dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window %pR to %pR add_size %llx\n",
+                          b_res, &bus->busn_res,
+                          (unsigned long long)size1-size0);
        }
        return 0;
 }
@@ -1274,8 +1269,8 @@ void __pci_bus_assign_resources(const struct pci_bus *bus,
                        break;
 
                default:
-                       dev_info(&dev->dev, "not setting up bridge for bus "
-                                "%04x:%02x\n", pci_domain_nr(b), b->number);
+                       dev_info(&dev->dev, "not setting up bridge for bus %04x:%02x\n",
+                                pci_domain_nr(b), b->number);
                        break;
                }
        }
@@ -1312,8 +1307,8 @@ static void __pci_bridge_assign_resources(const struct pci_dev *bridge,
                break;
 
        default:
-               dev_info(&bridge->dev, "not setting up bridge for bus "
-                        "%04x:%02x\n", pci_domain_nr(b), b->number);
+               dev_info(&bridge->dev, "not setting up bridge for bus %04x:%02x\n",
+                        pci_domain_nr(b), b->number);
                break;
        }
 }
@@ -1430,10 +1425,10 @@ static void pci_bus_dump_res(struct pci_bus *bus)
 
        pci_bus_for_each_resource(bus, res, i) {
                if (!res || !res->end || !res->flags)
-                        continue;
+                       continue;
 
                dev_printk(KERN_DEBUG, &bus->dev, "resource %d %pR\n", i, res);
-        }
+       }
 }
 
 static void pci_bus_dump_resources(struct pci_bus *bus)
@@ -1458,7 +1453,7 @@ static int pci_bus_get_depth(struct pci_bus *bus)
        int depth = 0;
        struct pci_bus *child_bus;
 
-       list_for_each_entry(child_bus, &bus->children, node){
+       list_for_each_entry(child_bus, &bus->children, node) {
                int ret;
 
                ret = pci_bus_get_depth(child_bus);
index dbc4ffcf42decd31fbc2fd595cfc3bbbb08a9196..4e2d595d50ca19c8a63ff537f5e89b0bab6c32d6 100644 (file)
@@ -22,10 +22,9 @@ void __weak pcibios_update_irq(struct pci_dev *dev, int irq)
        pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
 }
 
-static void
-pdev_fixup_irq(struct pci_dev *dev,
-              u8 (*swizzle)(struct pci_dev *, u8 *),
-              int (*map_irq)(const struct pci_dev *, u8, u8))
+static void pdev_fixup_irq(struct pci_dev *dev,
+                          u8 (*swizzle)(struct pci_dev *, u8 *),
+                          int (*map_irq)(const struct pci_dev *, u8, u8))
 {
        u8 pin, slot;
        int irq = 0;
@@ -58,11 +57,11 @@ pdev_fixup_irq(struct pci_dev *dev,
        pcibios_update_irq(dev, irq);
 }
 
-void
-pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *),
-              int (*map_irq)(const struct pci_dev *, u8, u8))
+void pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *),
+                   int (*map_irq)(const struct pci_dev *, u8, u8))
 {
        struct pci_dev *dev = NULL;
+
        for_each_pci_dev(dev)
                pdev_fixup_irq(dev, swizzle, map_irq);
 }
index 33f9e32d94d0cba92fbb2c339cb06f6df8106a52..caed1ce6facd8b65f59edf76b0061a3813c13549 100644 (file)
@@ -96,8 +96,8 @@ void pci_update_resource(struct pci_dev *dev, int resno)
                pci_write_config_dword(dev, reg + 4, new);
                pci_read_config_dword(dev, reg + 4, &check);
                if (check != new) {
-                       dev_err(&dev->dev, "BAR %d: error updating "
-                              "(high %#08x != %#08x)\n", resno, new, check);
+                       dev_err(&dev->dev, "BAR %d: error updating (high %#08x != %#08x)\n",
+                               resno, new, check);
                }
        }
 
@@ -289,8 +289,8 @@ int pci_assign_resource(struct pci_dev *dev, int resno)
        res->flags |= IORESOURCE_UNSET;
        align = pci_resource_alignment(dev, res);
        if (!align) {
-               dev_info(&dev->dev, "BAR %d: can't assign %pR "
-                        "(bogus alignment)\n", resno, res);
+               dev_info(&dev->dev, "BAR %d: can't assign %pR (bogus alignment)\n",
+                        resno, res);
                return -EINVAL;
        }
 
@@ -314,6 +314,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno)
        }
        return ret;
 }
+EXPORT_SYMBOL(pci_assign_resource);
 
 int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsize,
                        resource_size_t min_align)
@@ -324,8 +325,8 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz
 
        res->flags |= IORESOURCE_UNSET;
        if (!res->parent) {
-               dev_info(&dev->dev, "BAR %d: can't reassign an unassigned resource %pR "
-                        "\n", resno, res);
+               dev_info(&dev->dev, "BAR %d: can't reassign an unassigned resource %pR\n",
+                        resno, res);
                return -EINVAL;
        }
 
index 24750a1b39b67cd9b84229204beb0945b4c09f8e..b91c4da6836574f78df3785ae9ef419b80e069be 100644 (file)
@@ -99,7 +99,7 @@ SYSCALL_DEFINE5(pciconfig_write, unsigned long, bus, unsigned long, dfn,
        if (!dev)
                return -ENODEV;
 
-       switch(len) {
+       switch (len) {
        case 1:
                err = get_user(byte, (u8 __user *)buf);
                if (err)
index e25d2bc898e5b6e4eb7b43e6df87129a2a19781e..296b0ec8744da915763f8444c2ae8e902376c33e 100644 (file)
@@ -142,7 +142,10 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct timex *tx)
                delta = ktime_to_ns(kt);
                err = ops->adjtime(ops, delta);
        } else if (tx->modes & ADJ_FREQUENCY) {
-               err = ops->adjfreq(ops, scaled_ppm_to_ppb(tx->freq));
+               s32 ppb = scaled_ppm_to_ppb(tx->freq);
+               if (ppb > ops->max_adj || ppb < -ops->max_adj)
+                       return -ERANGE;
+               err = ops->adjfreq(ops, ppb);
                ptp->dialed_frequency = tx->freq;
        } else if (tx->modes == 0) {
                tx->freq = ptp->dialed_frequency;
index f53e78b9a84eadf1c9b2cded2c1ac00f7b7f055c..6ff95b0459849c84a1d206548defc40f469ec963 100644 (file)
@@ -266,11 +266,11 @@ static ssize_t set_mode(struct device *dev, struct device_attribute *attr,
        return count;
 }
 
-static DEVICE_ATTR(min_microvolts, 0666, show_min_uV, set_min_uV);
-static DEVICE_ATTR(max_microvolts, 0666, show_max_uV, set_max_uV);
-static DEVICE_ATTR(min_microamps, 0666, show_min_uA, set_min_uA);
-static DEVICE_ATTR(max_microamps, 0666, show_max_uA, set_max_uA);
-static DEVICE_ATTR(mode, 0666, show_mode, set_mode);
+static DEVICE_ATTR(min_microvolts, 0664, show_min_uV, set_min_uV);
+static DEVICE_ATTR(max_microvolts, 0664, show_max_uV, set_max_uV);
+static DEVICE_ATTR(min_microamps, 0664, show_min_uA, set_min_uA);
+static DEVICE_ATTR(max_microamps, 0664, show_max_uA, set_max_uA);
+static DEVICE_ATTR(mode, 0664, show_mode, set_mode);
 
 static struct attribute *regulator_virtual_attributes[] = {
        &dev_attr_min_microvolts.attr,
index 1e1fc671f89a4a431b4a04f59fea16e302ad3a30..d2c0b442bce5fb010fc43414a266c34ee245ca54 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/module.h>
 #include <linux/io.h>
 #include <linux/kvm_para.h>
+#include <linux/notifier.h>
 #include <asm/setup.h>
 #include <asm/irq.h>
 #include <asm/cio.h>
@@ -62,6 +63,7 @@ struct virtio_ccw_device {
        struct vq_config_block *config_block;
        bool is_thinint;
        bool going_away;
+       bool device_lost;
        void *airq_info;
 };
 
@@ -1010,11 +1012,14 @@ static void virtio_ccw_remove(struct ccw_device *cdev)
        unsigned long flags;
        struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev);
 
-       if (vcdev && cdev->online)
+       if (vcdev && cdev->online) {
+               if (vcdev->device_lost)
+                       virtio_break_device(&vcdev->vdev);
                unregister_virtio_device(&vcdev->vdev);
-       spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
-       dev_set_drvdata(&cdev->dev, NULL);
-       spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
+               spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
+               dev_set_drvdata(&cdev->dev, NULL);
+               spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
+       }
        cdev->handler = NULL;
 }
 
@@ -1023,12 +1028,14 @@ static int virtio_ccw_offline(struct ccw_device *cdev)
        unsigned long flags;
        struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev);
 
-       if (vcdev) {
-               unregister_virtio_device(&vcdev->vdev);
-               spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
-               dev_set_drvdata(&cdev->dev, NULL);
-               spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
-       }
+       if (!vcdev)
+               return 0;
+       if (vcdev->device_lost)
+               virtio_break_device(&vcdev->vdev);
+       unregister_virtio_device(&vcdev->vdev);
+       spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
+       dev_set_drvdata(&cdev->dev, NULL);
+       spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
        return 0;
 }
 
@@ -1096,8 +1103,26 @@ out_free:
 
 static int virtio_ccw_cio_notify(struct ccw_device *cdev, int event)
 {
-       /* TODO: Check whether we need special handling here. */
-       return 0;
+       int rc;
+       struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev);
+
+       /*
+        * Make sure vcdev is set
+        * i.e. set_offline/remove callback not already running
+        */
+       if (!vcdev)
+               return NOTIFY_DONE;
+
+       switch (event) {
+       case CIO_GONE:
+               vcdev->device_lost = true;
+               rc = NOTIFY_DONE;
+               break;
+       default:
+               rc = NOTIFY_DONE;
+               break;
+       }
+       return rc;
 }
 
 static struct ccw_device_id virtio_ids[] = {
index fd7b3bd807896556d0743660069aff8a44c3e072..d837c3c5330fab5c2c77548f31996db3dacad763 100644 (file)
@@ -3348,7 +3348,7 @@ static int __init claw_init(void)
        }
        CLAW_DBF_TEXT(2, setup, "init_mod");
        claw_root_dev = root_device_register("claw");
-       ret = PTR_RET(claw_root_dev);
+       ret = PTR_ERR_OR_ZERO(claw_root_dev);
        if (ret)
                goto register_err;
        ret = ccw_driver_register(&claw_ccw_driver);
index 70b3a023100ef769180d8234f2ac39c3caa91232..03b6ad035577e28553da16fbf4481d9c249a9e6d 100644 (file)
@@ -1837,7 +1837,7 @@ static int __init ctcm_init(void)
        if (ret)
                goto out_err;
        ctcm_root_dev = root_device_register("ctcm");
-       ret = PTR_RET(ctcm_root_dev);
+       ret = PTR_ERR_OR_ZERO(ctcm_root_dev);
        if (ret)
                goto register_err;
        ret = ccw_driver_register(&ctcm_ccw_driver);
index 985b5dcbdac8b348dc6394898ad55bcddf9b8aa9..6bcfbbb20f04c6525fa3363e66422009fe6072b6 100644 (file)
@@ -34,8 +34,9 @@ static ssize_t ctcm_buffer_write(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t count)
 {
        struct net_device *ndev;
-       int bs1;
+       unsigned int bs1;
        struct ctcm_priv *priv = dev_get_drvdata(dev);
+       int rc;
 
        ndev = priv->channel[CTCM_READ]->netdev;
        if (!(priv && priv->channel[CTCM_READ] && ndev)) {
@@ -43,7 +44,9 @@ static ssize_t ctcm_buffer_write(struct device *dev,
                return -ENODEV;
        }
 
-       sscanf(buf, "%u", &bs1);
+       rc = sscanf(buf, "%u", &bs1);
+       if (rc != 1)
+               goto einval;
        if (bs1 > CTCM_BUFSIZE_LIMIT)
                                        goto einval;
        if (bs1 < (576 + LL_HEADER_LENGTH + 2))
@@ -143,13 +146,14 @@ static ssize_t ctcm_proto_show(struct device *dev,
 static ssize_t ctcm_proto_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t count)
 {
-       int value;
+       int value, rc;
        struct ctcm_priv *priv = dev_get_drvdata(dev);
 
        if (!priv)
                return -ENODEV;
-       sscanf(buf, "%u", &value);
-       if (!((value == CTCM_PROTO_S390)  ||
+       rc = sscanf(buf, "%d", &value);
+       if ((rc != 1) ||
+           !((value == CTCM_PROTO_S390)  ||
              (value == CTCM_PROTO_LINUX) ||
              (value == CTCM_PROTO_MPC) ||
              (value == CTCM_PROTO_OS390)))
index c461f2aac610ea6a8580c504c9a2fbb9dcb92979..0a7d87c372b8414e17c41e05b50cf974e5ba0bd3 100644 (file)
@@ -1943,14 +1943,16 @@ static ssize_t
 lcs_portno_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
         struct lcs_card *card;
-        int value;
+       int value, rc;
 
        card = dev_get_drvdata(dev);
 
         if (!card)
                 return 0;
 
-        sscanf(buf, "%u", &value);
+       rc = sscanf(buf, "%d", &value);
+       if (rc != 1)
+               return -EINVAL;
         /* TODO: sanity checks */
         card->portno = value;
 
@@ -1997,14 +1999,17 @@ static ssize_t
 lcs_timeout_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
         struct lcs_card *card;
-        int value;
+       unsigned int value;
+       int rc;
 
        card = dev_get_drvdata(dev);
 
         if (!card)
                 return 0;
 
-        sscanf(buf, "%u", &value);
+       rc = sscanf(buf, "%u", &value);
+       if (rc != 1)
+               return -EINVAL;
         /* TODO: sanity checks */
         card->lancmd_timeout = value;
 
@@ -2442,7 +2447,7 @@ __init lcs_init_module(void)
        if (rc)
                goto out_err;
        lcs_root_dev = root_device_register("lcs");
-       rc = PTR_RET(lcs_root_dev);
+       rc = PTR_ERR_OR_ZERO(lcs_root_dev);
        if (rc)
                goto register_err;
        rc = ccw_driver_register(&lcs_ccw_driver);
index 5333b2c018e781541905e855c7cf3ff0a5d84a9e..a2088af51cc5d809d7513f0c0cb628c0b81910b6 100644 (file)
@@ -268,10 +268,8 @@ static inline int qeth_is_ipa_enabled(struct qeth_ipa_info *ipa,
 #define QETH_NO_PRIO_QUEUEING 0
 #define QETH_PRIO_Q_ING_PREC  1
 #define QETH_PRIO_Q_ING_TOS   2
-#define IP_TOS_LOWDELAY 0x10
-#define IP_TOS_HIGHTHROUGHPUT 0x08
-#define IP_TOS_HIGHRELIABILITY 0x04
-#define IP_TOS_NOTIMPORTANT 0x02
+#define QETH_PRIO_Q_ING_SKB   3
+#define QETH_PRIO_Q_ING_VLAN  4
 
 /* Packing */
 #define QETH_LOW_WATERMARK_PACK  2
index e89f38c3117606dd8d4a83226c44baf2e4f22bb1..f54bec54d677635154f190fc73e26f896d17bd34 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/kthread.h>
 #include <linux/slab.h>
 #include <net/iucv/af_iucv.h>
+#include <net/dsfield.h>
 
 #include <asm/ebcdic.h>
 #include <asm/chpid.h>
@@ -1013,7 +1014,7 @@ static long __qeth_check_irb_error(struct ccw_device *cdev,
 
        card = CARD_FROM_CDEV(cdev);
 
-       if (!IS_ERR(irb))
+       if (!card || !IS_ERR(irb))
                return 0;
 
        switch (PTR_ERR(irb)) {
@@ -1029,7 +1030,7 @@ static long __qeth_check_irb_error(struct ccw_device *cdev,
                QETH_CARD_TEXT(card, 2, "ckirberr");
                QETH_CARD_TEXT_(card, 2, "  rc%d", -ETIMEDOUT);
                if (intparm == QETH_RCD_PARM) {
-                       if (card && (card->data.ccwdev == cdev)) {
+                       if (card->data.ccwdev == cdev) {
                                card->data.state = CH_STATE_DOWN;
                                wake_up(&card->wait_q);
                        }
@@ -3662,42 +3663,56 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev,
 }
 EXPORT_SYMBOL_GPL(qeth_qdio_output_handler);
 
+/**
+ * Note: Function assumes that we have 4 outbound queues.
+ */
 int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb,
                        int ipv, int cast_type)
 {
-       if (!ipv && (card->info.type == QETH_CARD_TYPE_OSD ||
-                    card->info.type == QETH_CARD_TYPE_OSX))
-               return card->qdio.default_out_queue;
-       switch (card->qdio.no_out_queues) {
-       case 4:
-               if (cast_type && card->info.is_multicast_different)
-                       return card->info.is_multicast_different &
-                               (card->qdio.no_out_queues - 1);
-               if (card->qdio.do_prio_queueing && (ipv == 4)) {
-                       const u8 tos = ip_hdr(skb)->tos;
-
-                       if (card->qdio.do_prio_queueing ==
-                               QETH_PRIO_Q_ING_TOS) {
-                               if (tos & IP_TOS_NOTIMPORTANT)
-                                       return 3;
-                               if (tos & IP_TOS_HIGHRELIABILITY)
-                                       return 2;
-                               if (tos & IP_TOS_HIGHTHROUGHPUT)
-                                       return 1;
-                               if (tos & IP_TOS_LOWDELAY)
-                                       return 0;
-                       }
-                       if (card->qdio.do_prio_queueing ==
-                               QETH_PRIO_Q_ING_PREC)
-                               return 3 - (tos >> 6);
-               } else if (card->qdio.do_prio_queueing && (ipv == 6)) {
-                       /* TODO: IPv6!!! */
+       __be16 *tci;
+       u8 tos;
+
+       if (cast_type && card->info.is_multicast_different)
+               return card->info.is_multicast_different &
+                       (card->qdio.no_out_queues - 1);
+
+       switch (card->qdio.do_prio_queueing) {
+       case QETH_PRIO_Q_ING_TOS:
+       case QETH_PRIO_Q_ING_PREC:
+               switch (ipv) {
+               case 4:
+                       tos = ipv4_get_dsfield(ip_hdr(skb));
+                       break;
+               case 6:
+                       tos = ipv6_get_dsfield(ipv6_hdr(skb));
+                       break;
+               default:
+                       return card->qdio.default_out_queue;
                }
-               return card->qdio.default_out_queue;
-       case 1: /* fallthrough for single-out-queue 1920-device */
+               if (card->qdio.do_prio_queueing == QETH_PRIO_Q_ING_PREC)
+                       return ~tos >> 6 & 3;
+               if (tos & IPTOS_MINCOST)
+                       return 3;
+               if (tos & IPTOS_RELIABILITY)
+                       return 2;
+               if (tos & IPTOS_THROUGHPUT)
+                       return 1;
+               if (tos & IPTOS_LOWDELAY)
+                       return 0;
+               break;
+       case QETH_PRIO_Q_ING_SKB:
+               if (skb->priority > 5)
+                       return 0;
+               return ~skb->priority >> 1 & 3;
+       case QETH_PRIO_Q_ING_VLAN:
+               tci = &((struct ethhdr *)skb->data)->h_proto;
+               if (*tci == ETH_P_8021Q)
+                       return ~*(tci + 1) >> (VLAN_PRIO_SHIFT + 1) & 3;
+               break;
        default:
-               return card->qdio.default_out_queue;
+               break;
        }
+       return card->qdio.default_out_queue;
 }
 EXPORT_SYMBOL_GPL(qeth_get_priority_queue);
 
@@ -5703,6 +5718,7 @@ int qeth_core_ethtool_get_settings(struct net_device *netdev,
        struct qeth_card *card = netdev->ml_priv;
        enum qeth_link_types link_type;
        struct carrier_info carrier_info;
+       u32 speed;
 
        if ((card->info.type == QETH_CARD_TYPE_IQD) || (card->info.guestlan))
                link_type = QETH_LINK_TYPE_10GBIT_ETH;
@@ -5717,28 +5733,29 @@ int qeth_core_ethtool_get_settings(struct net_device *netdev,
        case QETH_LINK_TYPE_FAST_ETH:
        case QETH_LINK_TYPE_LANE_ETH100:
                qeth_set_ecmd_adv_sup(ecmd, SPEED_100, PORT_TP);
-               ecmd->speed = SPEED_100;
+               speed = SPEED_100;
                ecmd->port = PORT_TP;
                break;
 
        case QETH_LINK_TYPE_GBIT_ETH:
        case QETH_LINK_TYPE_LANE_ETH1000:
                qeth_set_ecmd_adv_sup(ecmd, SPEED_1000, PORT_FIBRE);
-               ecmd->speed = SPEED_1000;
+               speed = SPEED_1000;
                ecmd->port = PORT_FIBRE;
                break;
 
        case QETH_LINK_TYPE_10GBIT_ETH:
                qeth_set_ecmd_adv_sup(ecmd, SPEED_10000, PORT_FIBRE);
-               ecmd->speed = SPEED_10000;
+               speed = SPEED_10000;
                ecmd->port = PORT_FIBRE;
                break;
 
        default:
                qeth_set_ecmd_adv_sup(ecmd, SPEED_10, PORT_TP);
-               ecmd->speed = SPEED_10;
+               speed = SPEED_10;
                ecmd->port = PORT_TP;
        }
+       ethtool_cmd_speed_set(ecmd, speed);
 
        /* Check if we can obtain more accurate information.     */
        /* If QUERY_CARD_INFO command is not supported or fails, */
@@ -5783,18 +5800,19 @@ int qeth_core_ethtool_get_settings(struct net_device *netdev,
 
        switch (carrier_info.port_speed) {
        case CARD_INFO_PORTS_10M:
-               ecmd->speed = SPEED_10;
+               speed = SPEED_10;
                break;
        case CARD_INFO_PORTS_100M:
-               ecmd->speed = SPEED_100;
+               speed = SPEED_100;
                break;
        case CARD_INFO_PORTS_1G:
-               ecmd->speed = SPEED_1000;
+               speed = SPEED_1000;
                break;
        case CARD_INFO_PORTS_10G:
-               ecmd->speed = SPEED_10000;
+               speed = SPEED_10000;
                break;
        }
+       ethtool_cmd_speed_set(ecmd, speed);
 
        return 0;
 }
@@ -5816,7 +5834,7 @@ static int __init qeth_core_init(void)
        if (rc)
                goto out_err;
        qeth_core_root_dev = root_device_register("qeth");
-       rc = PTR_RET(qeth_core_root_dev);
+       rc = PTR_ERR_OR_ZERO(qeth_core_root_dev);
        if (rc)
                goto register_err;
        qeth_core_header_cache = kmem_cache_create("qeth_hdr",
index 425c0ecf1f3b9fd2ae3c2c55f61b1ad10f08c028..8a25a2be9890e7e09af1c9845c5b9b9773472f00 100644 (file)
@@ -217,6 +217,10 @@ static ssize_t qeth_dev_prioqing_show(struct device *dev,
                return sprintf(buf, "%s\n", "by precedence");
        case QETH_PRIO_Q_ING_TOS:
                return sprintf(buf, "%s\n", "by type of service");
+       case QETH_PRIO_Q_ING_SKB:
+               return sprintf(buf, "%s\n", "by skb-priority");
+       case QETH_PRIO_Q_ING_VLAN:
+               return sprintf(buf, "%s\n", "by VLAN headers");
        default:
                return sprintf(buf, "always queue %i\n",
                               card->qdio.default_out_queue);
@@ -250,11 +254,23 @@ static ssize_t qeth_dev_prioqing_store(struct device *dev,
        }
 
        tmp = strsep((char **) &buf, "\n");
-       if (!strcmp(tmp, "prio_queueing_prec"))
+       if (!strcmp(tmp, "prio_queueing_prec")) {
                card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_PREC;
-       else if (!strcmp(tmp, "prio_queueing_tos"))
+               card->qdio.default_out_queue = QETH_DEFAULT_QUEUE;
+       } else if (!strcmp(tmp, "prio_queueing_skb")) {
+               card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_SKB;
+               card->qdio.default_out_queue = QETH_DEFAULT_QUEUE;
+       } else if (!strcmp(tmp, "prio_queueing_tos")) {
                card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_TOS;
-       else if (!strcmp(tmp, "no_prio_queueing:0")) {
+               card->qdio.default_out_queue = QETH_DEFAULT_QUEUE;
+       } else if (!strcmp(tmp, "prio_queueing_vlan")) {
+               if (!card->options.layer2) {
+                       rc = -ENOTSUPP;
+                       goto out;
+               }
+               card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_VLAN;
+               card->qdio.default_out_queue = QETH_DEFAULT_QUEUE;
+       } else if (!strcmp(tmp, "no_prio_queueing:0")) {
                card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING;
                card->qdio.default_out_queue = 0;
        } else if (!strcmp(tmp, "no_prio_queueing:1")) {
index 8dea3f12ccc1714defe7d4d65869817dd6b69135..5ef5b4f45758cd226bb58becde5a3fa81ff33bb2 100644 (file)
@@ -725,15 +725,20 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        int elements = 0;
        struct qeth_card *card = dev->ml_priv;
        struct sk_buff *new_skb = skb;
-       int ipv = qeth_get_ip_version(skb);
        int cast_type = qeth_l2_get_cast_type(card, skb);
-       struct qeth_qdio_out_q *queue = card->qdio.out_qs
-               [qeth_get_priority_queue(card, skb, ipv, cast_type)];
+       struct qeth_qdio_out_q *queue;
        int tx_bytes = skb->len;
        int data_offset = -1;
        int elements_needed = 0;
        int hd_len = 0;
 
+       if (card->qdio.do_prio_queueing || (cast_type &&
+                                       card->info.is_multicast_different))
+               queue = card->qdio.out_qs[qeth_get_priority_queue(card, skb,
+                                       qeth_get_ip_version(skb), cast_type)];
+       else
+               queue = card->qdio.out_qs[card->qdio.default_out_queue];
+
        if ((card->state != CARD_STATE_UP) || !card->lan_online) {
                card->stats.tx_carrier_errors++;
                goto tx_drop;
@@ -964,10 +969,9 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
        card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
        card->dev->mtu = card->info.initial_mtu;
        card->dev->netdev_ops = &qeth_l2_netdev_ops;
-       if (card->info.type != QETH_CARD_TYPE_OSN)
-               SET_ETHTOOL_OPS(card->dev, &qeth_l2_ethtool_ops);
-       else
-               SET_ETHTOOL_OPS(card->dev, &qeth_l2_osn_ops);
+       card->dev->ethtool_ops =
+               (card->info.type != QETH_CARD_TYPE_OSN) ?
+               &qeth_l2_ethtool_ops : &qeth_l2_osn_ops;
        card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
        card->info.broadcast_capable = 1;
        qeth_l2_request_initial_mac(card);
index 3524d34ff694c273afefc7d85bfa17b6bce38af9..14e0b5810e8c1cde11a8835552158bab8b59dd1a 100644 (file)
@@ -63,7 +63,7 @@ void qeth_l3_ipaddr4_to_string(const __u8 *addr, char *buf)
 int qeth_l3_string_to_ipaddr4(const char *buf, __u8 *addr)
 {
        int count = 0, rc = 0;
-       int in[4];
+       unsigned int in[4];
        char c;
 
        rc = sscanf(buf, "%u.%u.%u.%u%c",
@@ -1659,7 +1659,7 @@ static void qeth_l3_add_vlan_mc(struct qeth_card *card)
        for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) {
                struct net_device *netdev;
 
-               netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q),
+               netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q),
                                              vid);
                if (netdev == NULL ||
                    !(netdev->flags & IFF_UP))
@@ -1721,7 +1721,7 @@ static void qeth_l3_add_vlan_mc6(struct qeth_card *card)
        for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) {
                struct net_device *netdev;
 
-               netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q),
+               netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q),
                                              vid);
                if (netdev == NULL ||
                    !(netdev->flags & IFF_UP))
@@ -1766,7 +1766,7 @@ static void qeth_l3_free_vlan_addresses4(struct qeth_card *card,
 
        QETH_CARD_TEXT(card, 4, "frvaddr4");
 
-       netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q), vid);
+       netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid);
        if (!netdev)
                return;
        in_dev = in_dev_get(netdev);
@@ -1796,7 +1796,7 @@ static void qeth_l3_free_vlan_addresses6(struct qeth_card *card,
 
        QETH_CARD_TEXT(card, 4, "frvaddr6");
 
-       netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q), vid);
+       netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid);
        if (!netdev)
                return;
        in6_dev = in6_dev_get(netdev);
@@ -2089,7 +2089,7 @@ static int qeth_l3_verify_vlan_dev(struct net_device *dev,
                struct net_device *netdev;
 
                rcu_read_lock();
-               netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q),
+               netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q),
                                              vid);
                rcu_read_unlock();
                if (netdev == dev) {
@@ -2926,8 +2926,11 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        struct sk_buff *new_skb = NULL;
        int ipv = qeth_get_ip_version(skb);
        int cast_type = qeth_l3_get_cast_type(card, skb);
-       struct qeth_qdio_out_q *queue = card->qdio.out_qs
-               [qeth_get_priority_queue(card, skb, ipv, cast_type)];
+       struct qeth_qdio_out_q *queue =
+               card->qdio.out_qs[card->qdio.do_prio_queueing
+                       || (cast_type && card->info.is_multicast_different) ?
+                       qeth_get_priority_queue(card, skb, ipv, cast_type) :
+                       card->qdio.default_out_queue];
        int tx_bytes = skb->len;
        bool large_send;
        int data_offset = -1;
@@ -3298,7 +3301,7 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)
        card->dev->ml_priv = card;
        card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
        card->dev->mtu = card->info.initial_mtu;
-       SET_ETHTOOL_OPS(card->dev, &qeth_l3_ethtool_ops);
+       card->dev->ethtool_ops = &qeth_l3_ethtool_ops;
        card->dev->features |=  NETIF_F_HW_VLAN_CTAG_TX |
                                NETIF_F_HW_VLAN_CTAG_RX |
                                NETIF_F_HW_VLAN_CTAG_FILTER;
index 02832d64d9187ea0ece202820bd11e1902979cdb..baca5897039fcaf23ea001b69eaef368564762c9 100644 (file)
@@ -1773,6 +1773,7 @@ config SCSI_BFA_FC
 config SCSI_VIRTIO
        tristate "virtio-scsi support"
        depends on VIRTIO
+       select BLK_DEV_INTEGRITY
        help
           This is the virtual HBA driver for virtio.  If the kernel will
           be used in a virtual machine, say Y or M.
index 5858600bfe593255592dd009fcdb02b82463f04d..31184b35370fe472099f072cdd7979b428fd07fc 100644 (file)
@@ -48,6 +48,7 @@
 #include <linux/bitmap.h>
 #include <linux/atomic.h>
 #include <linux/jiffies.h>
+#include <linux/percpu.h>
 #include <asm/div64.h>
 #include "hpsa_cmd.h"
 #include "hpsa.h"
@@ -193,7 +194,8 @@ static int number_of_controllers;
 static irqreturn_t do_hpsa_intr_intx(int irq, void *dev_id);
 static irqreturn_t do_hpsa_intr_msi(int irq, void *dev_id);
 static int hpsa_ioctl(struct scsi_device *dev, int cmd, void *arg);
-static void start_io(struct ctlr_info *h);
+static void lock_and_start_io(struct ctlr_info *h);
+static void start_io(struct ctlr_info *h, unsigned long *flags);
 
 #ifdef CONFIG_COMPAT
 static int hpsa_compat_ioctl(struct scsi_device *dev, int cmd, void *arg);
@@ -695,7 +697,7 @@ static inline void addQ(struct list_head *list, struct CommandList *c)
 static inline u32 next_command(struct ctlr_info *h, u8 q)
 {
        u32 a;
-       struct reply_pool *rq = &h->reply_queue[q];
+       struct reply_queue_buffer *rq = &h->reply_queue[q];
        unsigned long flags;
 
        if (h->transMethod & CFGTBL_Trans_io_accel1)
@@ -844,8 +846,8 @@ static void enqueue_cmd_and_start_io(struct ctlr_info *h,
        spin_lock_irqsave(&h->lock, flags);
        addQ(&h->reqQ, c);
        h->Qdepth++;
+       start_io(h, &flags);
        spin_unlock_irqrestore(&h->lock, flags);
-       start_io(h);
 }
 
 static inline void removeQ(struct CommandList *c)
@@ -1554,9 +1556,13 @@ static int handle_ioaccel_mode2_error(struct ctlr_info *h,
                        dev_warn(&h->pdev->dev,
                                "%s: task complete with check condition.\n",
                                "HP SSD Smart Path");
+                       cmd->result |= SAM_STAT_CHECK_CONDITION;
                        if (c2->error_data.data_present !=
-                                       IOACCEL2_SENSE_DATA_PRESENT)
+                                       IOACCEL2_SENSE_DATA_PRESENT) {
+                               memset(cmd->sense_buffer, 0,
+                                       SCSI_SENSE_BUFFERSIZE);
                                break;
+                       }
                        /* copy the sense data */
                        data_len = c2->error_data.sense_data_len;
                        if (data_len > SCSI_SENSE_BUFFERSIZE)
@@ -1566,7 +1572,6 @@ static int handle_ioaccel_mode2_error(struct ctlr_info *h,
                                        sizeof(c2->error_data.sense_data_buff);
                        memcpy(cmd->sense_buffer,
                                c2->error_data.sense_data_buff, data_len);
-                       cmd->result |= SAM_STAT_CHECK_CONDITION;
                        retry = 1;
                        break;
                case IOACCEL2_STATUS_SR_TASK_COMP_BUSY:
@@ -1651,16 +1656,6 @@ static void process_ioaccel2_completion(struct ctlr_info *h,
        if (is_logical_dev_addr_mode(dev->scsi3addr) &&
                c2->error_data.serv_response ==
                        IOACCEL2_SERV_RESPONSE_FAILURE) {
-               if (c2->error_data.status ==
-                       IOACCEL2_STATUS_SR_IOACCEL_DISABLED)
-                       dev_warn(&h->pdev->dev,
-                               "%s: Path is unavailable, retrying on standard path.\n",
-                               "HP SSD Smart Path");
-               else
-                       dev_warn(&h->pdev->dev,
-                               "%s: Error 0x%02x, retrying on standard path.\n",
-                               "HP SSD Smart Path", c2->error_data.status);
-
                dev->offload_enabled = 0;
                h->drv_req_rescan = 1;  /* schedule controller for a rescan */
                cmd->result = DID_SOFT_ERROR << 16;
@@ -1991,20 +1986,26 @@ static inline void hpsa_scsi_do_simple_cmd_core(struct ctlr_info *h,
        wait_for_completion(&wait);
 }
 
+static u32 lockup_detected(struct ctlr_info *h)
+{
+       int cpu;
+       u32 rc, *lockup_detected;
+
+       cpu = get_cpu();
+       lockup_detected = per_cpu_ptr(h->lockup_detected, cpu);
+       rc = *lockup_detected;
+       put_cpu();
+       return rc;
+}
+
 static void hpsa_scsi_do_simple_cmd_core_if_no_lockup(struct ctlr_info *h,
        struct CommandList *c)
 {
-       unsigned long flags;
-
        /* If controller lockup detected, fake a hardware error. */
-       spin_lock_irqsave(&h->lock, flags);
-       if (unlikely(h->lockup_detected)) {
-               spin_unlock_irqrestore(&h->lock, flags);
+       if (unlikely(lockup_detected(h)))
                c->err_info->CommandStatus = CMD_HARDWARE_ERR;
-       } else {
-               spin_unlock_irqrestore(&h->lock, flags);
+       else
                hpsa_scsi_do_simple_cmd_core(h, c);
-       }
 }
 
 #define MAX_DRIVER_CMD_RETRIES 25
@@ -2429,7 +2430,7 @@ static int hpsa_get_device_id(struct ctlr_info *h, unsigned char *scsi3addr,
                buflen = 16;
        buf = kzalloc(64, GFP_KERNEL);
        if (!buf)
-               return -1;
+               return -ENOMEM;
        rc = hpsa_scsi_do_inquiry(h, scsi3addr, VPD_PAGE | 0x83, buf, 64);
        if (rc == 0)
                memcpy(device_id, &buf[8], buflen);
@@ -2515,27 +2516,21 @@ static int hpsa_get_volume_status(struct ctlr_info *h,
                return HPSA_VPD_LV_STATUS_UNSUPPORTED;
 
        /* Does controller have VPD for logical volume status? */
-       if (!hpsa_vpd_page_supported(h, scsi3addr, HPSA_VPD_LV_STATUS)) {
-               dev_warn(&h->pdev->dev, "Logical volume status VPD page is unsupported.\n");
+       if (!hpsa_vpd_page_supported(h, scsi3addr, HPSA_VPD_LV_STATUS))
                goto exit_failed;
-       }
 
        /* Get the size of the VPD return buffer */
        rc = hpsa_scsi_do_inquiry(h, scsi3addr, VPD_PAGE | HPSA_VPD_LV_STATUS,
                                        buf, HPSA_VPD_HEADER_SZ);
-       if (rc != 0) {
-               dev_warn(&h->pdev->dev, "Logical volume status VPD inquiry failed.\n");
+       if (rc != 0)
                goto exit_failed;
-       }
        size = buf[3];
 
        /* Now get the whole VPD buffer */
        rc = hpsa_scsi_do_inquiry(h, scsi3addr, VPD_PAGE | HPSA_VPD_LV_STATUS,
                                        buf, size + HPSA_VPD_HEADER_SZ);
-       if (rc != 0) {
-               dev_warn(&h->pdev->dev, "Logical volume status VPD inquiry failed.\n");
+       if (rc != 0)
                goto exit_failed;
-       }
        status = buf[4]; /* status byte */
 
        kfree(buf);
@@ -2548,11 +2543,11 @@ exit_failed:
 /* Determine offline status of a volume.
  * Return either:
  *  0 (not offline)
- * -1 (offline for unknown reasons)
+ *  0xff (offline for unknown reasons)
  *  # (integer code indicating one of several NOT READY states
  *     describing why a volume is to be kept offline)
  */
-static unsigned char hpsa_volume_offline(struct ctlr_info *h,
+static int hpsa_volume_offline(struct ctlr_info *h,
                                        unsigned char scsi3addr[])
 {
        struct CommandList *c;
@@ -2651,11 +2646,15 @@ static int hpsa_update_device_info(struct ctlr_info *h,
 
        if (this_device->devtype == TYPE_DISK &&
                is_logical_dev_addr_mode(scsi3addr)) {
+               int volume_offline;
+
                hpsa_get_raid_level(h, scsi3addr, &this_device->raid_level);
                if (h->fw_support & MISC_FW_RAID_OFFLOAD_BASIC)
                        hpsa_get_ioaccel_status(h, scsi3addr, this_device);
-               this_device->volume_offline =
-                       hpsa_volume_offline(h, scsi3addr);
+               volume_offline = hpsa_volume_offline(h, scsi3addr);
+               if (volume_offline < 0 || volume_offline > 0xff)
+                       volume_offline = HPSA_VPD_LV_STATUS_UNSUPPORTED;
+               this_device->volume_offline = volume_offline & 0xff;
        } else {
                this_device->raid_level = RAID_UNKNOWN;
                this_device->offload_config = 0;
@@ -2861,26 +2860,20 @@ static int hpsa_get_pdisk_of_ioaccel2(struct ctlr_info *h,
        nphysicals = be32_to_cpu(*((__be32 *)physicals->LUNListLength)) /
                                                        responsesize;
 
-
        /* find ioaccel2 handle in list of physicals: */
        for (i = 0; i < nphysicals; i++) {
+               struct ext_report_lun_entry *entry = &physicals->LUN[i];
+
                /* handle is in bytes 28-31 of each lun */
-               if (memcmp(&((struct ReportExtendedLUNdata *)
-                               physicals)->LUN[i][20], &find, 4) != 0) {
+               if (entry->ioaccel_handle != find)
                        continue; /* didn't match */
-               }
                found = 1;
-               memcpy(scsi3addr, &((struct ReportExtendedLUNdata *)
-                                       physicals)->LUN[i][0], 8);
+               memcpy(scsi3addr, entry->lunid, 8);
                if (h->raid_offload_debug > 0)
                        dev_info(&h->pdev->dev,
-                               "%s: Searched h=0x%08x, Found h=0x%08x, scsiaddr 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+                               "%s: Searched h=0x%08x, Found h=0x%08x, scsiaddr 0x%8phN\n",
                                __func__, find,
-                               ((struct ReportExtendedLUNdata *)
-                                       physicals)->LUN[i][20],
-                               scsi3addr[0], scsi3addr[1], scsi3addr[2],
-                               scsi3addr[3], scsi3addr[4], scsi3addr[5],
-                               scsi3addr[6], scsi3addr[7]);
+                               entry->ioaccel_handle, scsi3addr);
                break; /* found it */
        }
 
@@ -2965,7 +2958,8 @@ u8 *figure_lunaddrbytes(struct ctlr_info *h, int raid_ctlr_position, int i,
                return RAID_CTLR_LUNID;
 
        if (i < logicals_start)
-               return &physdev_list->LUN[i - (raid_ctlr_position == 0)][0];
+               return &physdev_list->LUN[i -
+                               (raid_ctlr_position == 0)].lunid[0];
 
        if (i < last_device)
                return &logdev_list->LUN[i - nphysicals -
@@ -3074,7 +3068,7 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
                ndev_allocated++;
        }
 
-       if (unlikely(is_scsi_rev_5(h)))
+       if (is_scsi_rev_5(h))
                raid_ctlr_position = 0;
        else
                raid_ctlr_position = nphysicals + nlogicals;
@@ -3971,7 +3965,6 @@ static int hpsa_scsi_queue_command_lck(struct scsi_cmnd *cmd,
        struct hpsa_scsi_dev_t *dev;
        unsigned char scsi3addr[8];
        struct CommandList *c;
-       unsigned long flags;
        int rc = 0;
 
        /* Get the ptr to our adapter structure out of cmd->host. */
@@ -3984,14 +3977,11 @@ static int hpsa_scsi_queue_command_lck(struct scsi_cmnd *cmd,
        }
        memcpy(scsi3addr, dev->scsi3addr, sizeof(scsi3addr));
 
-       spin_lock_irqsave(&h->lock, flags);
-       if (unlikely(h->lockup_detected)) {
-               spin_unlock_irqrestore(&h->lock, flags);
+       if (unlikely(lockup_detected(h))) {
                cmd->result = DID_ERROR << 16;
                done(cmd);
                return 0;
        }
-       spin_unlock_irqrestore(&h->lock, flags);
        c = cmd_alloc(h);
        if (c == NULL) {                        /* trouble... */
                dev_err(&h->pdev->dev, "cmd_alloc returned NULL!\n");
@@ -4103,16 +4093,13 @@ static int do_not_scan_if_controller_locked_up(struct ctlr_info *h)
         * we can prevent new rescan threads from piling up on a
         * locked up controller.
         */
-       spin_lock_irqsave(&h->lock, flags);
-       if (unlikely(h->lockup_detected)) {
-               spin_unlock_irqrestore(&h->lock, flags);
+       if (unlikely(lockup_detected(h))) {
                spin_lock_irqsave(&h->scan_lock, flags);
                h->scan_finished = 1;
                wake_up_all(&h->scan_wait_queue);
                spin_unlock_irqrestore(&h->scan_lock, flags);
                return 1;
        }
-       spin_unlock_irqrestore(&h->lock, flags);
        return 0;
 }
 
@@ -4963,7 +4950,7 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp)
                buff = kmalloc(iocommand.buf_size, GFP_KERNEL);
                if (buff == NULL)
                        return -EFAULT;
-               if (iocommand.Request.Type.Direction == XFER_WRITE) {
+               if (iocommand.Request.Type.Direction & XFER_WRITE) {
                        /* Copy the data into the buffer we created */
                        if (copy_from_user(buff, iocommand.buf,
                                iocommand.buf_size)) {
@@ -5026,7 +5013,7 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp)
                rc = -EFAULT;
                goto out;
        }
-       if (iocommand.Request.Type.Direction == XFER_READ &&
+       if ((iocommand.Request.Type.Direction & XFER_READ) &&
                iocommand.buf_size > 0) {
                /* Copy the data out of the buffer we created */
                if (copy_to_user(iocommand.buf, buff, iocommand.buf_size)) {
@@ -5103,7 +5090,7 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp)
                        status = -ENOMEM;
                        goto cleanup1;
                }
-               if (ioc->Request.Type.Direction == XFER_WRITE) {
+               if (ioc->Request.Type.Direction & XFER_WRITE) {
                        if (copy_from_user(buff[sg_used], data_ptr, sz)) {
                                status = -ENOMEM;
                                goto cleanup1;
@@ -5155,7 +5142,7 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp)
                status = -EFAULT;
                goto cleanup0;
        }
-       if (ioc->Request.Type.Direction == XFER_READ && ioc->buf_size > 0) {
+       if ((ioc->Request.Type.Direction & XFER_READ) && ioc->buf_size > 0) {
                /* Copy the data out of the buffer we created */
                BYTE __user *ptr = ioc->buf;
                for (i = 0; i < sg_used; i++) {
@@ -5459,13 +5446,12 @@ static void __iomem *remap_pci_mem(ulong base, ulong size)
 
 /* Takes cmds off the submission queue and sends them to the hardware,
  * then puts them on the queue of cmds waiting for completion.
+ * Assumes h->lock is held
  */
-static void start_io(struct ctlr_info *h)
+static void start_io(struct ctlr_info *h, unsigned long *flags)
 {
        struct CommandList *c;
-       unsigned long flags;
 
-       spin_lock_irqsave(&h->lock, flags);
        while (!list_empty(&h->reqQ)) {
                c = list_entry(h->reqQ.next, struct CommandList, list);
                /* can't do anything if fifo is full */
@@ -5488,14 +5474,20 @@ static void start_io(struct ctlr_info *h)
                 * condition.
                 */
                h->commands_outstanding++;
-               if (h->commands_outstanding > h->max_outstanding)
-                       h->max_outstanding = h->commands_outstanding;
 
                /* Tell the controller execute command */
-               spin_unlock_irqrestore(&h->lock, flags);
+               spin_unlock_irqrestore(&h->lock, *flags);
                h->access.submit_command(h, c);
-               spin_lock_irqsave(&h->lock, flags);
+               spin_lock_irqsave(&h->lock, *flags);
        }
+}
+
+static void lock_and_start_io(struct ctlr_info *h)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&h->lock, flags);
+       start_io(h, &flags);
        spin_unlock_irqrestore(&h->lock, flags);
 }
 
@@ -5563,7 +5555,7 @@ static inline void finish_cmd(struct CommandList *c)
        else if (c->cmd_type == CMD_IOCTL_PEND)
                complete(c->waiting);
        if (unlikely(io_may_be_stalled))
-               start_io(h);
+               lock_and_start_io(h);
 }
 
 static inline u32 hpsa_tag_contains_index(u32 tag)
@@ -5840,12 +5832,12 @@ static int hpsa_controller_hard_reset(struct pci_dev *pdev,
                dev_info(&pdev->dev, "using doorbell to reset controller\n");
                writel(use_doorbell, vaddr + SA5_DOORBELL);
 
-               /* PMC hardware guys tell us we need a 5 second delay after
+               /* PMC hardware guys tell us we need a 10 second delay after
                 * doorbell reset and before any attempt to talk to the board
                 * at all to ensure that this actually works and doesn't fall
                 * over in some weird corner cases.
                 */
-               msleep(5000);
+               msleep(10000);
        } else { /* Try to do it the PCI power state way */
 
                /* Quoting from the Open CISS Specification: "The Power
@@ -6166,6 +6158,8 @@ static void hpsa_interrupt_mode(struct ctlr_info *h)
        if (pci_find_capability(h->pdev, PCI_CAP_ID_MSIX)) {
                dev_info(&h->pdev->dev, "MSIX\n");
                h->msix_vector = MAX_REPLY_QUEUES;
+               if (h->msix_vector > num_online_cpus())
+                       h->msix_vector = num_online_cpus();
                err = pci_enable_msix(h->pdev, hpsa_msix_entries,
                                      h->msix_vector);
                if (err > 0) {
@@ -6615,6 +6609,17 @@ static void hpsa_free_cmd_pool(struct ctlr_info *h)
                        h->ioaccel_cmd_pool, h->ioaccel_cmd_pool_dhandle);
 }
 
+static void hpsa_irq_affinity_hints(struct ctlr_info *h)
+{
+       int i, cpu, rc;
+
+       cpu = cpumask_first(cpu_online_mask);
+       for (i = 0; i < h->msix_vector; i++) {
+               rc = irq_set_affinity_hint(h->intr[i], get_cpu_mask(cpu));
+               cpu = cpumask_next(cpu, cpu_online_mask);
+       }
+}
+
 static int hpsa_request_irq(struct ctlr_info *h,
        irqreturn_t (*msixhandler)(int, void *),
        irqreturn_t (*intxhandler)(int, void *))
@@ -6634,6 +6639,7 @@ static int hpsa_request_irq(struct ctlr_info *h,
                        rc = request_irq(h->intr[i], msixhandler,
                                        0, h->devname,
                                        &h->q[i]);
+               hpsa_irq_affinity_hints(h);
        } else {
                /* Use single reply pool */
                if (h->msix_vector > 0 || h->msi_vector) {
@@ -6685,12 +6691,15 @@ static void free_irqs(struct ctlr_info *h)
        if (!h->msix_vector || h->intr_mode != PERF_MODE_INT) {
                /* Single reply queue, only one irq to free */
                i = h->intr_mode;
+               irq_set_affinity_hint(h->intr[i], NULL);
                free_irq(h->intr[i], &h->q[i]);
                return;
        }
 
-       for (i = 0; i < h->msix_vector; i++)
+       for (i = 0; i < h->msix_vector; i++) {
+               irq_set_affinity_hint(h->intr[i], NULL);
                free_irq(h->intr[i], &h->q[i]);
+       }
 }
 
 static void hpsa_free_irqs_and_disable_msix(struct ctlr_info *h)
@@ -6707,6 +6716,20 @@ static void hpsa_free_irqs_and_disable_msix(struct ctlr_info *h)
 #endif /* CONFIG_PCI_MSI */
 }
 
+static void hpsa_free_reply_queues(struct ctlr_info *h)
+{
+       int i;
+
+       for (i = 0; i < h->nreply_queues; i++) {
+               if (!h->reply_queue[i].head)
+                       continue;
+               pci_free_consistent(h->pdev, h->reply_queue_size,
+                       h->reply_queue[i].head, h->reply_queue[i].busaddr);
+               h->reply_queue[i].head = NULL;
+               h->reply_queue[i].busaddr = 0;
+       }
+}
+
 static void hpsa_undo_allocations_after_kdump_soft_reset(struct ctlr_info *h)
 {
        hpsa_free_irqs_and_disable_msix(h);
@@ -6714,8 +6737,7 @@ static void hpsa_undo_allocations_after_kdump_soft_reset(struct ctlr_info *h)
        hpsa_free_cmd_pool(h);
        kfree(h->ioaccel1_blockFetchTable);
        kfree(h->blockFetchTable);
-       pci_free_consistent(h->pdev, h->reply_pool_size,
-               h->reply_pool, h->reply_pool_dhandle);
+       hpsa_free_reply_queues(h);
        if (h->vaddr)
                iounmap(h->vaddr);
        if (h->transtable)
@@ -6740,16 +6762,38 @@ static void fail_all_cmds_on_list(struct ctlr_info *h, struct list_head *list)
        }
 }
 
+static void set_lockup_detected_for_all_cpus(struct ctlr_info *h, u32 value)
+{
+       int i, cpu;
+
+       cpu = cpumask_first(cpu_online_mask);
+       for (i = 0; i < num_online_cpus(); i++) {
+               u32 *lockup_detected;
+               lockup_detected = per_cpu_ptr(h->lockup_detected, cpu);
+               *lockup_detected = value;
+               cpu = cpumask_next(cpu, cpu_online_mask);
+       }
+       wmb(); /* be sure the per-cpu variables are out to memory */
+}
+
 static void controller_lockup_detected(struct ctlr_info *h)
 {
        unsigned long flags;
+       u32 lockup_detected;
 
        h->access.set_intr_mask(h, HPSA_INTR_OFF);
        spin_lock_irqsave(&h->lock, flags);
-       h->lockup_detected = readl(h->vaddr + SA5_SCRATCHPAD_OFFSET);
+       lockup_detected = readl(h->vaddr + SA5_SCRATCHPAD_OFFSET);
+       if (!lockup_detected) {
+               /* no heartbeat, but controller gave us a zero. */
+               dev_warn(&h->pdev->dev,
+                       "lockup detected but scratchpad register is zero\n");
+               lockup_detected = 0xffffffff;
+       }
+       set_lockup_detected_for_all_cpus(h, lockup_detected);
        spin_unlock_irqrestore(&h->lock, flags);
        dev_warn(&h->pdev->dev, "Controller lockup detected: 0x%08x\n",
-                       h->lockup_detected);
+                       lockup_detected);
        pci_disable_device(h->pdev);
        spin_lock_irqsave(&h->lock, flags);
        fail_all_cmds_on_list(h, &h->cmpQ);
@@ -6884,7 +6928,7 @@ static void hpsa_monitor_ctlr_worker(struct work_struct *work)
        struct ctlr_info *h = container_of(to_delayed_work(work),
                                        struct ctlr_info, monitor_ctlr_work);
        detect_controller_lockup(h);
-       if (h->lockup_detected)
+       if (lockup_detected(h))
                return;
 
        if (hpsa_ctlr_needs_rescan(h) || hpsa_offline_devices_ready(h)) {
@@ -6934,7 +6978,6 @@ reinit_after_soft_reset:
         * the 5 lower bits of the address are used by the hardware. and by
         * the driver.  See comments in hpsa.h for more info.
         */
-#define COMMANDLIST_ALIGNMENT 128
        BUILD_BUG_ON(sizeof(struct CommandList) % COMMANDLIST_ALIGNMENT);
        h = kzalloc(sizeof(*h), GFP_KERNEL);
        if (!h)
@@ -6949,6 +6992,13 @@ reinit_after_soft_reset:
        spin_lock_init(&h->offline_device_lock);
        spin_lock_init(&h->scan_lock);
        spin_lock_init(&h->passthru_count_lock);
+
+       /* Allocate and clear per-cpu variable lockup_detected */
+       h->lockup_detected = alloc_percpu(u32);
+       if (!h->lockup_detected)
+               goto clean1;
+       set_lockup_detected_for_all_cpus(h, 0);
+
        rc = hpsa_pci_init(h);
        if (rc != 0)
                goto clean1;
@@ -7072,6 +7122,8 @@ clean4:
        free_irqs(h);
 clean2:
 clean1:
+       if (h->lockup_detected)
+               free_percpu(h->lockup_detected);
        kfree(h);
        return rc;
 }
@@ -7080,16 +7132,10 @@ static void hpsa_flush_cache(struct ctlr_info *h)
 {
        char *flush_buf;
        struct CommandList *c;
-       unsigned long flags;
 
        /* Don't bother trying to flush the cache if locked up */
-       spin_lock_irqsave(&h->lock, flags);
-       if (unlikely(h->lockup_detected)) {
-               spin_unlock_irqrestore(&h->lock, flags);
+       if (unlikely(lockup_detected(h)))
                return;
-       }
-       spin_unlock_irqrestore(&h->lock, flags);
-
        flush_buf = kzalloc(4, GFP_KERNEL);
        if (!flush_buf)
                return;
@@ -7165,8 +7211,7 @@ static void hpsa_remove_one(struct pci_dev *pdev)
        pci_free_consistent(h->pdev,
                h->nr_cmds * sizeof(struct ErrorInfo),
                h->errinfo_pool, h->errinfo_pool_dhandle);
-       pci_free_consistent(h->pdev, h->reply_pool_size,
-               h->reply_pool, h->reply_pool_dhandle);
+       hpsa_free_reply_queues(h);
        kfree(h->cmd_pool_bits);
        kfree(h->blockFetchTable);
        kfree(h->ioaccel1_blockFetchTable);
@@ -7174,6 +7219,7 @@ static void hpsa_remove_one(struct pci_dev *pdev)
        kfree(h->hba_inquiry_data);
        pci_disable_device(pdev);
        pci_release_regions(pdev);
+       free_percpu(h->lockup_detected);
        kfree(h);
 }
 
@@ -7278,8 +7324,16 @@ static void hpsa_enter_performant_mode(struct ctlr_info *h, u32 trans_support)
         * 10 = 6 s/g entry or 24k
         */
 
+       /* If the controller supports either ioaccel method then
+        * we can also use the RAID stack submit path that does not
+        * perform the superfluous readl() after each command submission.
+        */
+       if (trans_support & (CFGTBL_Trans_io_accel1 | CFGTBL_Trans_io_accel2))
+               access = SA5_performant_access_no_read;
+
        /* Controller spec: zero out this buffer. */
-       memset(h->reply_pool, 0, h->reply_pool_size);
+       for (i = 0; i < h->nreply_queues; i++)
+               memset(h->reply_queue[i].head, 0, h->reply_queue_size);
 
        bft[7] = SG_ENTRIES_IN_CMD + 4;
        calc_bucket_map(bft, ARRAY_SIZE(bft),
@@ -7295,8 +7349,7 @@ static void hpsa_enter_performant_mode(struct ctlr_info *h, u32 trans_support)
 
        for (i = 0; i < h->nreply_queues; i++) {
                writel(0, &h->transtable->RepQAddr[i].upper);
-               writel(h->reply_pool_dhandle +
-                       (h->max_commands * sizeof(u64) * i),
+               writel(h->reply_queue[i].busaddr,
                        &h->transtable->RepQAddr[i].lower);
        }
 
@@ -7344,8 +7397,10 @@ static void hpsa_enter_performant_mode(struct ctlr_info *h, u32 trans_support)
                                h->ioaccel1_blockFetchTable);
 
                /* initialize all reply queue entries to unused */
-               memset(h->reply_pool, (u8) IOACCEL_MODE1_REPLY_UNUSED,
-                               h->reply_pool_size);
+               for (i = 0; i < h->nreply_queues; i++)
+                       memset(h->reply_queue[i].head,
+                               (u8) IOACCEL_MODE1_REPLY_UNUSED,
+                               h->reply_queue_size);
 
                /* set all the constant fields in the accelerator command
                 * frames once at init time to save CPU cycles later.
@@ -7407,7 +7462,6 @@ static int hpsa_alloc_ioaccel_cmd_and_bft(struct ctlr_info *h)
         * because the 7 lower bits of the address are used by the
         * hardware.
         */
-#define IOACCEL1_COMMANDLIST_ALIGNMENT 128
        BUILD_BUG_ON(sizeof(struct io_accel1_cmd) %
                        IOACCEL1_COMMANDLIST_ALIGNMENT);
        h->ioaccel_cmd_pool =
@@ -7445,7 +7499,6 @@ static int ioaccel2_alloc_cmds_and_bft(struct ctlr_info *h)
        if (h->ioaccel_maxsg > IOACCEL2_MAXSGENTRIES)
                h->ioaccel_maxsg = IOACCEL2_MAXSGENTRIES;
 
-#define IOACCEL2_COMMANDLIST_ALIGNMENT 128
        BUILD_BUG_ON(sizeof(struct io_accel2_cmd) %
                        IOACCEL2_COMMANDLIST_ALIGNMENT);
        h->ioaccel2_cmd_pool =
@@ -7503,16 +7556,17 @@ static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
                }
        }
 
-       /* TODO, check that this next line h->nreply_queues is correct */
        h->nreply_queues = h->msix_vector > 0 ? h->msix_vector : 1;
        hpsa_get_max_perf_mode_cmds(h);
        /* Performant mode ring buffer and supporting data structures */
-       h->reply_pool_size = h->max_commands * sizeof(u64) * h->nreply_queues;
-       h->reply_pool = pci_alloc_consistent(h->pdev, h->reply_pool_size,
-                               &(h->reply_pool_dhandle));
+       h->reply_queue_size = h->max_commands * sizeof(u64);
 
        for (i = 0; i < h->nreply_queues; i++) {
-               h->reply_queue[i].head = &h->reply_pool[h->max_commands * i];
+               h->reply_queue[i].head = pci_alloc_consistent(h->pdev,
+                                               h->reply_queue_size,
+                                               &(h->reply_queue[i].busaddr));
+               if (!h->reply_queue[i].head)
+                       goto clean_up;
                h->reply_queue[i].size = h->max_commands;
                h->reply_queue[i].wraparound = 1;  /* spec: init to 1 */
                h->reply_queue[i].current_entry = 0;
@@ -7521,18 +7575,14 @@ static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
        /* Need a block fetch table for performant mode */
        h->blockFetchTable = kmalloc(((SG_ENTRIES_IN_CMD + 1) *
                                sizeof(u32)), GFP_KERNEL);
-
-       if ((h->reply_pool == NULL)
-               || (h->blockFetchTable == NULL))
+       if (!h->blockFetchTable)
                goto clean_up;
 
        hpsa_enter_performant_mode(h, trans_support);
        return;
 
 clean_up:
-       if (h->reply_pool)
-               pci_free_consistent(h->pdev, h->reply_pool_size,
-                       h->reply_pool, h->reply_pool_dhandle);
+       hpsa_free_reply_queues(h);
        kfree(h->blockFetchTable);
 }
 
index 1e3cf33a82cf12750fa9336e087774dcb7c5becd..24472cec7de34dce8e975c545f45d8c502cf9afb 100644 (file)
@@ -57,11 +57,12 @@ struct hpsa_scsi_dev_t {
 
 };
 
-struct reply_pool {
+struct reply_queue_buffer {
        u64 *head;
        size_t size;
        u8 wraparound;
        u32 current_entry;
+       dma_addr_t busaddr;
 };
 
 #pragma pack(1)
@@ -116,11 +117,8 @@ struct ctlr_info {
        int     nr_cmds; /* Number of commands allowed on this controller */
        struct CfgTable __iomem *cfgtable;
        int     interrupts_enabled;
-       int     major;
        int     max_commands;
        int     commands_outstanding;
-       int     max_outstanding; /* Debug */
-       int     usage_count;  /* number of opens all all minor devices */
 #      define PERF_MODE_INT    0
 #      define DOORBELL_INT     1
 #      define SIMPLE_MODE_INT  2
@@ -177,11 +175,9 @@ struct ctlr_info {
        /*
         * Performant mode completion buffers
         */
-       u64 *reply_pool;
-       size_t reply_pool_size;
-       struct reply_pool reply_queue[MAX_REPLY_QUEUES];
+       size_t reply_queue_size;
+       struct reply_queue_buffer reply_queue[MAX_REPLY_QUEUES];
        u8 nreply_queues;
-       dma_addr_t reply_pool_dhandle;
        u32 *blockFetchTable;
        u32 *ioaccel1_blockFetchTable;
        u32 *ioaccel2_blockFetchTable;
@@ -196,7 +192,7 @@ struct ctlr_info {
        u64 last_heartbeat_timestamp;
        u32 heartbeat_sample_interval;
        atomic_t firmware_flash_in_progress;
-       u32 lockup_detected;
+       u32 *lockup_detected;
        struct delayed_work monitor_ctlr_work;
        int remove_in_progress;
        u32 fifo_recently_full;
@@ -233,11 +229,9 @@ struct ctlr_info {
 #define CTLR_STATE_CHANGE_EVENT_AIO_CONFIG_CHANGE      (1 << 31)
 
 #define RESCAN_REQUIRED_EVENT_BITS \
-               (CTLR_STATE_CHANGE_EVENT | \
-               CTLR_ENCLOSURE_HOT_PLUG_EVENT | \
+               (CTLR_ENCLOSURE_HOT_PLUG_EVENT | \
                CTLR_STATE_CHANGE_EVENT_PHYSICAL_DRV | \
                CTLR_STATE_CHANGE_EVENT_LOGICAL_DRV | \
-               CTLR_STATE_CHANGE_EVENT_REDUNDANT_CNTRL | \
                CTLR_STATE_CHANGE_EVENT_AIO_ENABLED_DISABLED | \
                CTLR_STATE_CHANGE_EVENT_AIO_CONFIG_CHANGE)
        spinlock_t offline_device_lock;
@@ -346,22 +340,23 @@ struct offline_device_entry {
 static void SA5_submit_command(struct ctlr_info *h,
        struct CommandList *c)
 {
-       dev_dbg(&h->pdev->dev, "Sending %x, tag = %x\n", c->busaddr,
-               c->Header.Tag.lower);
        writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
        (void) readl(h->vaddr + SA5_SCRATCHPAD_OFFSET);
 }
 
+static void SA5_submit_command_no_read(struct ctlr_info *h,
+       struct CommandList *c)
+{
+       writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
+}
+
 static void SA5_submit_command_ioaccel2(struct ctlr_info *h,
        struct CommandList *c)
 {
-       dev_dbg(&h->pdev->dev, "Sending %x, tag = %x\n", c->busaddr,
-               c->Header.Tag.lower);
        if (c->cmd_type == CMD_IOACCEL2)
                writel(c->busaddr, h->vaddr + IOACCEL2_INBOUND_POSTQ_32);
        else
                writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
-       (void) readl(h->vaddr + SA5_SCRATCHPAD_OFFSET);
 }
 
 /*
@@ -399,7 +394,7 @@ static void SA5_performant_intr_mask(struct ctlr_info *h, unsigned long val)
 
 static unsigned long SA5_performant_completed(struct ctlr_info *h, u8 q)
 {
-       struct reply_pool *rq = &h->reply_queue[q];
+       struct reply_queue_buffer *rq = &h->reply_queue[q];
        unsigned long flags, register_value = FIFO_EMPTY;
 
        /* msi auto clears the interrupt pending bit. */
@@ -478,7 +473,6 @@ static bool SA5_intr_pending(struct ctlr_info *h)
 {
        unsigned long register_value  =
                readl(h->vaddr + SA5_INTR_STATUS);
-       dev_dbg(&h->pdev->dev, "intr_pending %lx\n", register_value);
        return register_value & SA5_INTR_PENDING;
 }
 
@@ -515,7 +509,7 @@ static bool SA5_ioaccel_mode1_intr_pending(struct ctlr_info *h)
 static unsigned long SA5_ioaccel_mode1_completed(struct ctlr_info *h, u8 q)
 {
        u64 register_value;
-       struct reply_pool *rq = &h->reply_queue[q];
+       struct reply_queue_buffer *rq = &h->reply_queue[q];
        unsigned long flags;
 
        BUG_ON(q >= h->nreply_queues);
@@ -573,6 +567,14 @@ static struct access_method SA5_performant_access = {
        SA5_performant_completed,
 };
 
+static struct access_method SA5_performant_access_no_read = {
+       SA5_submit_command_no_read,
+       SA5_performant_intr_mask,
+       SA5_fifo_full,
+       SA5_performant_intr_pending,
+       SA5_performant_completed,
+};
+
 struct board_type {
        u32     board_id;
        char    *product_name;
index b5cc7052339f91ed3f157c28c836a6788d433b68..b5125dc3143912233213ff8f83c0d86ddd78efaf 100644 (file)
 #define HPSA_VPD_HEADER_SZ              4
 
 /* Logical volume states */
-#define HPSA_VPD_LV_STATUS_UNSUPPORTED                 -1
+#define HPSA_VPD_LV_STATUS_UNSUPPORTED                 0xff
 #define HPSA_LV_OK                                      0x0
 #define HPSA_LV_UNDERGOING_ERASE                       0x0F
 #define HPSA_LV_UNDERGOING_RPI                         0x12
@@ -238,11 +238,21 @@ struct ReportLUNdata {
        u8 LUN[HPSA_MAX_LUN][8];
 };
 
+struct ext_report_lun_entry {
+       u8 lunid[8];
+       u8 wwid[8];
+       u8 device_type;
+       u8 device_flags;
+       u8 lun_count; /* multi-lun device, how many luns */
+       u8 redundant_paths;
+       u32 ioaccel_handle; /* ioaccel1 only uses lower 16 bits */
+};
+
 struct ReportExtendedLUNdata {
        u8 LUNListLength[4];
        u8 extended_response_flag;
        u8 reserved[3];
-       u8 LUN[HPSA_MAX_LUN][24];
+       struct ext_report_lun_entry LUN[HPSA_MAX_LUN];
 };
 
 struct SenseSubsystem_info {
@@ -375,6 +385,7 @@ struct ctlr_info; /* defined in hpsa.h */
  *        or a bus address.
  */
 
+#define COMMANDLIST_ALIGNMENT 128
 struct CommandList {
        struct CommandListHeader Header;
        struct RequestBlock      Request;
@@ -389,21 +400,7 @@ struct CommandList {
        struct list_head list;
        struct completion *waiting;
        void   *scsi_cmd;
-
-/* on 64 bit architectures, to get this to be 32-byte-aligned
- * it so happens we need PAD_64 bytes of padding, on 32 bit systems,
- * we need PAD_32 bytes of padding (see below).   This does that.
- * If it happens that 64 bit and 32 bit systems need different
- * padding, PAD_32 and PAD_64 can be set independently, and.
- * the code below will do the right thing.
- */
-#define IS_32_BIT ((8 - sizeof(long))/4)
-#define IS_64_BIT (!IS_32_BIT)
-#define PAD_32 (40)
-#define PAD_64 (12)
-#define COMMANDLIST_PAD (IS_32_BIT * PAD_32 + IS_64_BIT * PAD_64)
-       u8 pad[COMMANDLIST_PAD];
-};
+} __aligned(COMMANDLIST_ALIGNMENT);
 
 /* Max S/G elements in I/O accelerator command */
 #define IOACCEL1_MAXSGENTRIES           24
@@ -413,6 +410,7 @@ struct CommandList {
  * Structure for I/O accelerator (mode 1) commands.
  * Note that this structure must be 128-byte aligned in size.
  */
+#define IOACCEL1_COMMANDLIST_ALIGNMENT 128
 struct io_accel1_cmd {
        u16 dev_handle;                 /* 0x00 - 0x01 */
        u8  reserved1;                  /* 0x02 */
@@ -440,12 +438,7 @@ struct io_accel1_cmd {
        struct vals32 host_addr;        /* 0x70 - 0x77 */
        u8  CISS_LUN[8];                /* 0x78 - 0x7F */
        struct SGDescriptor SG[IOACCEL1_MAXSGENTRIES];
-#define IOACCEL1_PAD_64 0
-#define IOACCEL1_PAD_32 0
-#define IOACCEL1_PAD (IS_32_BIT * IOACCEL1_PAD_32 + \
-                       IS_64_BIT * IOACCEL1_PAD_64)
-       u8 pad[IOACCEL1_PAD];
-};
+} __aligned(IOACCEL1_COMMANDLIST_ALIGNMENT);
 
 #define IOACCEL1_FUNCTION_SCSIIO        0x00
 #define IOACCEL1_SGLOFFSET              32
@@ -510,14 +503,11 @@ struct io_accel2_scsi_response {
        u8 sense_data_buff[32];         /* sense/response data buffer */
 };
 
-#define IOACCEL2_64_PAD 76
-#define IOACCEL2_32_PAD 76
-#define IOACCEL2_PAD (IS_32_BIT * IOACCEL2_32_PAD + \
-                       IS_64_BIT * IOACCEL2_64_PAD)
 /*
  * Structure for I/O accelerator (mode 2 or m2) commands.
  * Note that this structure must be 128-byte aligned in size.
  */
+#define IOACCEL2_COMMANDLIST_ALIGNMENT 128
 struct io_accel2_cmd {
        u8  IU_type;                    /* IU Type */
        u8  direction;                  /* direction, memtype, and encryption */
@@ -544,8 +534,7 @@ struct io_accel2_cmd {
        u32 tweak_upper;                /* Encryption tweak, upper 4 bytes */
        struct ioaccel2_sg_element sg[IOACCEL2_MAXSGENTRIES];
        struct io_accel2_scsi_response error_data;
-       u8 pad[IOACCEL2_PAD];
-};
+} __aligned(IOACCEL2_COMMANDLIST_ALIGNMENT);
 
 /*
  * defines for Mode 2 command struct
@@ -636,7 +625,7 @@ struct TransTable_struct {
        u32            RepQCount;
        u32            RepQCtrAddrLow32;
        u32            RepQCtrAddrHigh32;
-#define MAX_REPLY_QUEUES 8
+#define MAX_REPLY_QUEUES 64
        struct vals32  RepQAddr[MAX_REPLY_QUEUES];
 };
 
index 11854845393bf9cc13688ff3895339997822fa61..a669f2d11c314e380eb2aa90714b91eb0064f781 100644 (file)
@@ -244,7 +244,7 @@ iscsi_sw_tcp_conn_restore_callbacks(struct iscsi_conn *conn)
        sk->sk_data_ready   = tcp_sw_conn->old_data_ready;
        sk->sk_state_change = tcp_sw_conn->old_state_change;
        sk->sk_write_space  = tcp_sw_conn->old_write_space;
-       sk->sk_no_check  = 0;
+       sk->sk_no_check_tx = 0;
        write_unlock_bh(&sk->sk_callback_lock);
 }
 
index ecd7bd304efebb51ce99580e7810edb6a3cf84be..3d1bc67bac9dc58ac11b9785694e568b3c3ab285 100644 (file)
@@ -338,7 +338,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
        struct iscsi_session *session = conn->session;
        struct scsi_cmnd *sc = task->sc;
        struct iscsi_scsi_req *hdr;
-       unsigned hdrlength, cmd_len;
+       unsigned hdrlength, cmd_len, transfer_length;
        itt_t itt;
        int rc;
 
@@ -391,11 +391,11 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
        if (scsi_get_prot_op(sc) != SCSI_PROT_NORMAL)
                task->protected = true;
 
+       transfer_length = scsi_transfer_length(sc);
+       hdr->data_length = cpu_to_be32(transfer_length);
        if (sc->sc_data_direction == DMA_TO_DEVICE) {
-               unsigned out_len = scsi_out(sc)->length;
                struct iscsi_r2t_info *r2t = &task->unsol_r2t;
 
-               hdr->data_length = cpu_to_be32(out_len);
                hdr->flags |= ISCSI_FLAG_CMD_WRITE;
                /*
                 * Write counters:
@@ -414,18 +414,19 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
                memset(r2t, 0, sizeof(*r2t));
 
                if (session->imm_data_en) {
-                       if (out_len >= session->first_burst)
+                       if (transfer_length >= session->first_burst)
                                task->imm_count = min(session->first_burst,
                                                        conn->max_xmit_dlength);
                        else
-                               task->imm_count = min(out_len,
-                                                       conn->max_xmit_dlength);
+                               task->imm_count = min(transfer_length,
+                                                     conn->max_xmit_dlength);
                        hton24(hdr->dlength, task->imm_count);
                } else
                        zero_data(hdr->dlength);
 
                if (!session->initial_r2t_en) {
-                       r2t->data_length = min(session->first_burst, out_len) -
+                       r2t->data_length = min(session->first_burst,
+                                              transfer_length) -
                                               task->imm_count;
                        r2t->data_offset = task->imm_count;
                        r2t->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);
@@ -438,7 +439,6 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
        } else {
                hdr->flags |= ISCSI_FLAG_CMD_FINAL;
                zero_data(hdr->dlength);
-               hdr->data_length = cpu_to_be32(scsi_in(sc)->length);
 
                if (sc->sc_data_direction == DMA_FROM_DEVICE)
                        hdr->flags |= ISCSI_FLAG_CMD_READ;
@@ -466,7 +466,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
                          scsi_bidi_cmnd(sc) ? "bidirectional" :
                          sc->sc_data_direction == DMA_TO_DEVICE ?
                          "write" : "read", conn->id, sc, sc->cmnd[0],
-                         task->itt, scsi_bufflen(sc),
+                         task->itt, transfer_length,
                          scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0,
                          session->cmdsn,
                          session->max_cmdsn - session->exp_cmdsn + 1);
index 94a3cafe7197ded339c889aa07c32c9ac41b32e5..434e9037908ef5eb0203d0b127d0fe953ec825b3 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2013 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2014 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
@@ -640,6 +640,7 @@ struct lpfc_hba {
 #define HBA_DEVLOSS_TMO         0x2000 /* HBA in devloss timeout */
 #define HBA_RRQ_ACTIVE         0x4000 /* process the rrq active list */
 #define HBA_FCP_IOQ_FLUSH      0x8000 /* FCP I/O queues being flushed */
+#define HBA_FW_DUMP_OP         0x10000 /* Skips fn reset before FW dump */
        uint32_t fcp_ring_in_use; /* When polling test if intr-hndlr active*/
        struct lpfc_dmabuf slim2p;
 
index 8d5b6ceec9c9d2a3710fe650d122a806fb5ca2f8..1d7a5c34ee8cdb333ac7fd0a243c9dfc5850b6f1 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2013 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2014 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
@@ -919,10 +919,15 @@ lpfc_sli4_pdev_reg_request(struct lpfc_hba *phba, uint32_t opcode)
                phba->cfg_sriov_nr_virtfn = 0;
        }
 
+       if (opcode == LPFC_FW_DUMP)
+               phba->hba_flag |= HBA_FW_DUMP_OP;
+
        status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE);
 
-       if (status != 0)
+       if (status != 0) {
+               phba->hba_flag &= ~HBA_FW_DUMP_OP;
                return status;
+       }
 
        /* wait for the device to be quiesced before firmware reset */
        msleep(100);
@@ -2364,7 +2369,7 @@ lpfc_oas_tgt_store(struct device *dev, struct device_attribute *attr,
        uint8_t wwpn[WWN_SZ];
        int rc;
 
-       if (!phba->cfg_EnableXLane)
+       if (!phba->cfg_fof)
                return -EPERM;
 
        /* count may include a LF at end of string */
@@ -2432,7 +2437,7 @@ lpfc_oas_vpt_store(struct device *dev, struct device_attribute *attr,
        uint8_t wwpn[WWN_SZ];
        int rc;
 
-       if (!phba->cfg_EnableXLane)
+       if (!phba->cfg_fof)
                return -EPERM;
 
        /* count may include a LF at end of string */
@@ -2499,7 +2504,7 @@ lpfc_oas_lun_state_store(struct device *dev, struct device_attribute *attr,
        struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
        int val = 0;
 
-       if (!phba->cfg_EnableXLane)
+       if (!phba->cfg_fof)
                return -EPERM;
 
        if (!isdigit(buf[0]))
@@ -2565,7 +2570,7 @@ lpfc_oas_lun_state_set(struct lpfc_hba *phba, uint8_t vpt_wwpn[],
 
        int rc = 0;
 
-       if (!phba->cfg_EnableXLane)
+       if (!phba->cfg_fof)
                return -EPERM;
 
        if (oas_state) {
@@ -2670,7 +2675,7 @@ lpfc_oas_lun_show(struct device *dev, struct device_attribute *attr,
        uint64_t oas_lun;
        int len = 0;
 
-       if (!phba->cfg_EnableXLane)
+       if (!phba->cfg_fof)
                return -EPERM;
 
        if (wwn_to_u64(phba->cfg_oas_vpt_wwpn) == 0)
@@ -2716,7 +2721,7 @@ lpfc_oas_lun_store(struct device *dev, struct device_attribute *attr,
        uint64_t scsi_lun;
        ssize_t rc;
 
-       if (!phba->cfg_EnableXLane)
+       if (!phba->cfg_fof)
                return -EPERM;
 
        if (wwn_to_u64(phba->cfg_oas_vpt_wwpn) == 0)
@@ -4655,7 +4660,7 @@ LPFC_ATTR_R(EnableXLane, 0, 0, 1, "Enable Express Lane Feature.");
 #       0x0 - 0x7f  = CS_CTL field in FC header (high 7 bits)
 # Value range is [0x0,0x7f]. Default value is 0
 */
-LPFC_ATTR_R(XLanePriority, 0, 0x0, 0x7f, "CS_CTL for Express Lane Feature.");
+LPFC_ATTR_RW(XLanePriority, 0, 0x0, 0x7f, "CS_CTL for Express Lane Feature.");
 
 /*
 # lpfc_enable_bg: Enable BlockGuard (Emulex's Implementation of T10-DIF)
index ca2f4ea7cdefcde51d6381123584e480f16eb655..5b5c825d95767751295b06928c2440d8257ae1d1 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2009-2013 Emulex.  All rights reserved.           *
+ * Copyright (C) 2009-2014 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  *                                                                 *
index a94d4c9dfaa52dc2f3c161b3d725bbb33b79e4c6..928ef609f3630367b0a1f7734c2fb01a3d65a3e0 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2010-2012 Emulex.  All rights reserved.                *
+ * Copyright (C) 2010-2014 Emulex.  All rights reserved.                *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  *                                                                 *
index adda0bf7a244476627644f4828ddfbcb8fb911a4..db5604f01a1a0c1f9fd8b9df5bf05f103b931e6e 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2013 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2014 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  *                                                                 *
@@ -289,6 +289,7 @@ int lpfc_sli_issue_iocb(struct lpfc_hba *, uint32_t,
 void lpfc_sli_pcimem_bcopy(void *, void *, uint32_t);
 void lpfc_sli_bemem_bcopy(void *, void *, uint32_t);
 void lpfc_sli_abort_iocb_ring(struct lpfc_hba *, struct lpfc_sli_ring *);
+void lpfc_sli_abort_fcp_rings(struct lpfc_hba *phba);
 void lpfc_sli_hba_iocb_abort(struct lpfc_hba *);
 void lpfc_sli_flush_fcp_rings(struct lpfc_hba *);
 int lpfc_sli_ringpostbuf_put(struct lpfc_hba *, struct lpfc_sli_ring *,
@@ -310,6 +311,9 @@ int lpfc_sli_issue_abort_iotag(struct lpfc_hba *, struct lpfc_sli_ring *,
 int lpfc_sli_sum_iocb(struct lpfc_vport *, uint16_t, uint64_t, lpfc_ctx_cmd);
 int lpfc_sli_abort_iocb(struct lpfc_vport *, struct lpfc_sli_ring *, uint16_t,
                        uint64_t, lpfc_ctx_cmd);
+int
+lpfc_sli_abort_taskmgmt(struct lpfc_vport *, struct lpfc_sli_ring *,
+                       uint16_t, uint64_t, lpfc_ctx_cmd);
 
 void lpfc_mbox_timeout(unsigned long);
 void lpfc_mbox_timeout_handler(struct lpfc_hba *);
index 828c08e9389eaae396ff39e4fb5e647ab5263b66..b0aedce3f54b0fda28e00c3cf964959a56591b6d 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2007-2012 Emulex.  All rights reserved.           *
+ * Copyright (C) 2007-2014 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  *                                                                 *
@@ -2314,7 +2314,7 @@ proc_cq:
                        goto too_big;
        }
 
-       if (phba->cfg_EnableXLane) {
+       if (phba->cfg_fof) {
 
                /* OAS CQ */
                qp = phba->sli4_hba.oas_cq;
index 624fe0b3cc0b508d5fbd8c23f4717e8418ef5050..7a5d81a65be869107a9835a7182a72bff2c05242 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2013 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2014 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
index 294c072e90835efbb72012f2789dc412bf8c9686..2a17e31265b8df5b33428300228e456bbac9e810 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2013 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2014 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
@@ -5634,6 +5634,9 @@ lpfc_nlp_init(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
                ndlp->active_rrqs_xri_bitmap =
                                mempool_alloc(vport->phba->active_rrq_pool,
                                              GFP_KERNEL);
+               if (ndlp->active_rrqs_xri_bitmap)
+                       memset(ndlp->active_rrqs_xri_bitmap, 0,
+                              ndlp->phba->cfg_rrq_xri_bitmap_sz);
        }
 
 
index 3d9438ce59aba9fa87a1e85b5c54e5e7b2fceb9a..23625925237920e0ed6f6b20241c6618ceb91f4a 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2013 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2014 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  *                                                                 *
index fd79f7de7666e9a500f9aeac3c64e559af747b3b..f432ec180cf819d3c05ed70f121733362c001ed1 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2009-2013 Emulex.  All rights reserved.                *
+ * Copyright (C) 2009-2014 Emulex.  All rights reserved.                *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  *                                                                 *
index 635eeb3d69877f36f549109bac187a120bb0554e..06f9a5b79e66d226c8c2e447b924f7d2ed554aa6 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2013 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2014 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
@@ -820,57 +820,153 @@ lpfc_hba_down_prep(struct lpfc_hba *phba)
 }
 
 /**
- * lpfc_hba_down_post_s3 - Perform lpfc uninitialization after HBA reset
+ * lpfc_sli4_free_sp_events - Cleanup sp_queue_events to free
+ * rspiocb which got deferred
+ *
  * @phba: pointer to lpfc HBA data structure.
  *
- * This routine will do uninitialization after the HBA is reset when bring
- * down the SLI Layer.
+ * This routine will cleanup completed slow path events after HBA is reset
+ * when bringing down the SLI Layer.
+ *
  *
  * Return codes
- *   0 - success.
- *   Any other value - error.
+ *   void.
  **/
-static int
-lpfc_hba_down_post_s3(struct lpfc_hba *phba)
+static void
+lpfc_sli4_free_sp_events(struct lpfc_hba *phba)
+{
+       struct lpfc_iocbq *rspiocbq;
+       struct hbq_dmabuf *dmabuf;
+       struct lpfc_cq_event *cq_event;
+
+       spin_lock_irq(&phba->hbalock);
+       phba->hba_flag &= ~HBA_SP_QUEUE_EVT;
+       spin_unlock_irq(&phba->hbalock);
+
+       while (!list_empty(&phba->sli4_hba.sp_queue_event)) {
+               /* Get the response iocb from the head of work queue */
+               spin_lock_irq(&phba->hbalock);
+               list_remove_head(&phba->sli4_hba.sp_queue_event,
+                                cq_event, struct lpfc_cq_event, list);
+               spin_unlock_irq(&phba->hbalock);
+
+               switch (bf_get(lpfc_wcqe_c_code, &cq_event->cqe.wcqe_cmpl)) {
+               case CQE_CODE_COMPL_WQE:
+                       rspiocbq = container_of(cq_event, struct lpfc_iocbq,
+                                                cq_event);
+                       lpfc_sli_release_iocbq(phba, rspiocbq);
+                       break;
+               case CQE_CODE_RECEIVE:
+               case CQE_CODE_RECEIVE_V1:
+                       dmabuf = container_of(cq_event, struct hbq_dmabuf,
+                                             cq_event);
+                       lpfc_in_buf_free(phba, &dmabuf->dbuf);
+               }
+       }
+}
+
+/**
+ * lpfc_hba_free_post_buf - Perform lpfc uninitialization after HBA reset
+ * @phba: pointer to lpfc HBA data structure.
+ *
+ * This routine will cleanup posted ELS buffers after the HBA is reset
+ * when bringing down the SLI Layer.
+ *
+ *
+ * Return codes
+ *   void.
+ **/
+static void
+lpfc_hba_free_post_buf(struct lpfc_hba *phba)
 {
        struct lpfc_sli *psli = &phba->sli;
        struct lpfc_sli_ring *pring;
        struct lpfc_dmabuf *mp, *next_mp;
-       LIST_HEAD(completions);
-       int i;
+       LIST_HEAD(buflist);
+       int count;
 
        if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED)
                lpfc_sli_hbqbuf_free_all(phba);
        else {
                /* Cleanup preposted buffers on the ELS ring */
                pring = &psli->ring[LPFC_ELS_RING];
-               list_for_each_entry_safe(mp, next_mp, &pring->postbufq, list) {
+               spin_lock_irq(&phba->hbalock);
+               list_splice_init(&pring->postbufq, &buflist);
+               spin_unlock_irq(&phba->hbalock);
+
+               count = 0;
+               list_for_each_entry_safe(mp, next_mp, &buflist, list) {
                        list_del(&mp->list);
-                       pring->postbufq_cnt--;
+                       count++;
                        lpfc_mbuf_free(phba, mp->virt, mp->phys);
                        kfree(mp);
                }
+
+               spin_lock_irq(&phba->hbalock);
+               pring->postbufq_cnt -= count;
+               spin_unlock_irq(&phba->hbalock);
        }
+}
+
+/**
+ * lpfc_hba_clean_txcmplq - Perform lpfc uninitialization after HBA reset
+ * @phba: pointer to lpfc HBA data structure.
+ *
+ * This routine will cleanup the txcmplq after the HBA is reset when bringing
+ * down the SLI Layer.
+ *
+ * Return codes
+ *   void
+ **/
+static void
+lpfc_hba_clean_txcmplq(struct lpfc_hba *phba)
+{
+       struct lpfc_sli *psli = &phba->sli;
+       struct lpfc_sli_ring *pring;
+       LIST_HEAD(completions);
+       int i;
 
-       spin_lock_irq(&phba->hbalock);
        for (i = 0; i < psli->num_rings; i++) {
                pring = &psli->ring[i];
-
+               if (phba->sli_rev >= LPFC_SLI_REV4)
+                       spin_lock_irq(&pring->ring_lock);
+               else
+                       spin_lock_irq(&phba->hbalock);
                /* At this point in time the HBA is either reset or DOA. Either
                 * way, nothing should be on txcmplq as it will NEVER complete.
                 */
                list_splice_init(&pring->txcmplq, &completions);
-               spin_unlock_irq(&phba->hbalock);
+               pring->txcmplq_cnt = 0;
+
+               if (phba->sli_rev >= LPFC_SLI_REV4)
+                       spin_unlock_irq(&pring->ring_lock);
+               else
+                       spin_unlock_irq(&phba->hbalock);
 
                /* Cancel all the IOCBs from the completions list */
                lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
                                      IOERR_SLI_ABORTED);
-
                lpfc_sli_abort_iocb_ring(phba, pring);
-               spin_lock_irq(&phba->hbalock);
        }
-       spin_unlock_irq(&phba->hbalock);
+}
 
+/**
+ * lpfc_hba_down_post_s3 - Perform lpfc uninitialization after HBA reset
+       int i;
+ * @phba: pointer to lpfc HBA data structure.
+ *
+ * This routine will do uninitialization after the HBA is reset when bring
+ * down the SLI Layer.
+ *
+ * Return codes
+ *   0 - success.
+ *   Any other value - error.
+ **/
+static int
+lpfc_hba_down_post_s3(struct lpfc_hba *phba)
+{
+       lpfc_hba_free_post_buf(phba);
+       lpfc_hba_clean_txcmplq(phba);
        return 0;
 }
 
@@ -890,13 +986,12 @@ lpfc_hba_down_post_s4(struct lpfc_hba *phba)
 {
        struct lpfc_scsi_buf *psb, *psb_next;
        LIST_HEAD(aborts);
-       int ret;
        unsigned long iflag = 0;
        struct lpfc_sglq *sglq_entry = NULL;
 
-       ret = lpfc_hba_down_post_s3(phba);
-       if (ret)
-               return ret;
+       lpfc_hba_free_post_buf(phba);
+       lpfc_hba_clean_txcmplq(phba);
+
        /* At this point in time the HBA is either reset or DOA. Either
         * way, nothing should be on lpfc_abts_els_sgl_list, it needs to be
         * on the lpfc_sgl_list so that it can either be freed if the
@@ -932,6 +1027,8 @@ lpfc_hba_down_post_s4(struct lpfc_hba *phba)
        spin_lock_irqsave(&phba->scsi_buf_list_put_lock, iflag);
        list_splice(&aborts, &phba->lpfc_scsi_buf_list_put);
        spin_unlock_irqrestore(&phba->scsi_buf_list_put_lock, iflag);
+
+       lpfc_sli4_free_sp_events(phba);
        return 0;
 }
 
@@ -1250,7 +1347,6 @@ static void
 lpfc_handle_deferred_eratt(struct lpfc_hba *phba)
 {
        uint32_t old_host_status = phba->work_hs;
-       struct lpfc_sli_ring  *pring;
        struct lpfc_sli *psli = &phba->sli;
 
        /* If the pci channel is offline, ignore possible errors,
@@ -1279,8 +1375,7 @@ lpfc_handle_deferred_eratt(struct lpfc_hba *phba)
         * dropped by the firmware. Error iocb (I/O) on txcmplq and let the
         * SCSI layer retry it after re-establishing link.
         */
-       pring = &psli->ring[psli->fcp_ring];
-       lpfc_sli_abort_iocb_ring(phba, pring);
+       lpfc_sli_abort_fcp_rings(phba);
 
        /*
         * There was a firmware error. Take the hba offline and then
@@ -1348,7 +1443,6 @@ lpfc_handle_eratt_s3(struct lpfc_hba *phba)
 {
        struct lpfc_vport *vport = phba->pport;
        struct lpfc_sli   *psli = &phba->sli;
-       struct lpfc_sli_ring  *pring;
        uint32_t event_data;
        unsigned long temperature;
        struct temp_event temp_event_data;
@@ -1400,8 +1494,7 @@ lpfc_handle_eratt_s3(struct lpfc_hba *phba)
                * Error iocb (I/O) on txcmplq and let the SCSI layer
                * retry it after re-establishing link.
                */
-               pring = &psli->ring[psli->fcp_ring];
-               lpfc_sli_abort_iocb_ring(phba, pring);
+               lpfc_sli_abort_fcp_rings(phba);
 
                /*
                 * There was a firmware error.  Take the hba offline and then
@@ -1940,78 +2033,81 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
 
        switch (dev_id) {
        case PCI_DEVICE_ID_FIREFLY:
-               m = (typeof(m)){"LP6000", "PCI", "Fibre Channel Adapter"};
+               m = (typeof(m)){"LP6000", "PCI",
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_SUPERFLY:
                if (vp->rev.biuRev >= 1 && vp->rev.biuRev <= 3)
-                       m = (typeof(m)){"LP7000", "PCI",
-                                       "Fibre Channel Adapter"};
+                       m = (typeof(m)){"LP7000", "PCI", ""};
                else
-                       m = (typeof(m)){"LP7000E", "PCI",
-                                       "Fibre Channel Adapter"};
+                       m = (typeof(m)){"LP7000E", "PCI", ""};
+               m.function = "Obsolete, Unsupported Fibre Channel Adapter";
                break;
        case PCI_DEVICE_ID_DRAGONFLY:
                m = (typeof(m)){"LP8000", "PCI",
-                               "Fibre Channel Adapter"};
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_CENTAUR:
                if (FC_JEDEC_ID(vp->rev.biuRev) == CENTAUR_2G_JEDEC_ID)
-                       m = (typeof(m)){"LP9002", "PCI",
-                                       "Fibre Channel Adapter"};
+                       m = (typeof(m)){"LP9002", "PCI", ""};
                else
-                       m = (typeof(m)){"LP9000", "PCI",
-                                       "Fibre Channel Adapter"};
+                       m = (typeof(m)){"LP9000", "PCI", ""};
+               m.function = "Obsolete, Unsupported Fibre Channel Adapter";
                break;
        case PCI_DEVICE_ID_RFLY:
                m = (typeof(m)){"LP952", "PCI",
-                               "Fibre Channel Adapter"};
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_PEGASUS:
                m = (typeof(m)){"LP9802", "PCI-X",
-                               "Fibre Channel Adapter"};
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_THOR:
                m = (typeof(m)){"LP10000", "PCI-X",
-                               "Fibre Channel Adapter"};
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_VIPER:
                m = (typeof(m)){"LPX1000",  "PCI-X",
-                               "Fibre Channel Adapter"};
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_PFLY:
                m = (typeof(m)){"LP982", "PCI-X",
-                               "Fibre Channel Adapter"};
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_TFLY:
                m = (typeof(m)){"LP1050", "PCI-X",
-                               "Fibre Channel Adapter"};
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_HELIOS:
                m = (typeof(m)){"LP11000", "PCI-X2",
-                               "Fibre Channel Adapter"};
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_HELIOS_SCSP:
                m = (typeof(m)){"LP11000-SP", "PCI-X2",
-                               "Fibre Channel Adapter"};
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_HELIOS_DCSP:
                m = (typeof(m)){"LP11002-SP",  "PCI-X2",
-                               "Fibre Channel Adapter"};
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_NEPTUNE:
-               m = (typeof(m)){"LPe1000", "PCIe", "Fibre Channel Adapter"};
+               m = (typeof(m)){"LPe1000", "PCIe",
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_NEPTUNE_SCSP:
-               m = (typeof(m)){"LPe1000-SP", "PCIe", "Fibre Channel Adapter"};
+               m = (typeof(m)){"LPe1000-SP", "PCIe",
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_NEPTUNE_DCSP:
-               m = (typeof(m)){"LPe1002-SP", "PCIe", "Fibre Channel Adapter"};
+               m = (typeof(m)){"LPe1002-SP", "PCIe",
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_BMID:
                m = (typeof(m)){"LP1150", "PCI-X2", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_BSMB:
-               m = (typeof(m)){"LP111", "PCI-X2", "Fibre Channel Adapter"};
+               m = (typeof(m)){"LP111", "PCI-X2",
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_ZEPHYR:
                m = (typeof(m)){"LPe11000", "PCIe", "Fibre Channel Adapter"};
@@ -2030,16 +2126,20 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
                m = (typeof(m)){"LPe111", "PCIe", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_LP101:
-               m = (typeof(m)){"LP101", "PCI-X", "Fibre Channel Adapter"};
+               m = (typeof(m)){"LP101", "PCI-X",
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_LP10000S:
-               m = (typeof(m)){"LP10000-S", "PCI", "Fibre Channel Adapter"};
+               m = (typeof(m)){"LP10000-S", "PCI",
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_LP11000S:
-               m = (typeof(m)){"LP11000-S", "PCI-X2", "Fibre Channel Adapter"};
+               m = (typeof(m)){"LP11000-S", "PCI-X2",
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_LPE11000S:
-               m = (typeof(m)){"LPe11000-S", "PCIe", "Fibre Channel Adapter"};
+               m = (typeof(m)){"LPe11000-S", "PCIe",
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_SAT:
                m = (typeof(m)){"LPe12000", "PCIe", "Fibre Channel Adapter"};
@@ -2060,20 +2160,21 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
                m = (typeof(m)){"LPe12000-S", "PCIe", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_HORNET:
-               m = (typeof(m)){"LP21000", "PCIe", "FCoE Adapter"};
+               m = (typeof(m)){"LP21000", "PCIe",
+                               "Obsolete, Unsupported FCoE Adapter"};
                GE = 1;
                break;
        case PCI_DEVICE_ID_PROTEUS_VF:
                m = (typeof(m)){"LPev12000", "PCIe IOV",
-                               "Fibre Channel Adapter"};
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_PROTEUS_PF:
                m = (typeof(m)){"LPev12000", "PCIe IOV",
-                               "Fibre Channel Adapter"};
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_PROTEUS_S:
                m = (typeof(m)){"LPemv12002-S", "PCIe IOV",
-                               "Fibre Channel Adapter"};
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_TIGERSHARK:
                oneConnect = 1;
@@ -2089,17 +2190,24 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
                break;
        case PCI_DEVICE_ID_BALIUS:
                m = (typeof(m)){"LPVe12002", "PCIe Shared I/O",
-                               "Fibre Channel Adapter"};
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_LANCER_FC:
-       case PCI_DEVICE_ID_LANCER_FC_VF:
                m = (typeof(m)){"LPe16000", "PCIe", "Fibre Channel Adapter"};
                break;
+       case PCI_DEVICE_ID_LANCER_FC_VF:
+               m = (typeof(m)){"LPe16000", "PCIe",
+                               "Obsolete, Unsupported Fibre Channel Adapter"};
+               break;
        case PCI_DEVICE_ID_LANCER_FCOE:
-       case PCI_DEVICE_ID_LANCER_FCOE_VF:
                oneConnect = 1;
                m = (typeof(m)){"OCe15100", "PCIe", "FCoE"};
                break;
+       case PCI_DEVICE_ID_LANCER_FCOE_VF:
+               oneConnect = 1;
+               m = (typeof(m)){"OCe15100", "PCIe",
+                               "Obsolete, Unsupported FCoE"};
+               break;
        case PCI_DEVICE_ID_SKYHAWK:
        case PCI_DEVICE_ID_SKYHAWK_VF:
                oneConnect = 1;
@@ -4614,7 +4722,10 @@ lpfc_reset_hba(struct lpfc_hba *phba)
                phba->link_state = LPFC_HBA_ERROR;
                return;
        }
-       lpfc_offline_prep(phba, LPFC_MBX_WAIT);
+       if (phba->sli.sli_flag & LPFC_SLI_ACTIVE)
+               lpfc_offline_prep(phba, LPFC_MBX_WAIT);
+       else
+               lpfc_offline_prep(phba, LPFC_MBX_NO_WAIT);
        lpfc_offline(phba);
        lpfc_sli_brdrestart(phba);
        lpfc_online(phba);
@@ -9663,9 +9774,6 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev)
 static void
 lpfc_sli_prep_dev_for_recover(struct lpfc_hba *phba)
 {
-       struct lpfc_sli *psli = &phba->sli;
-       struct lpfc_sli_ring  *pring;
-
        lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
                        "2723 PCI channel I/O abort preparing for recovery\n");
 
@@ -9673,8 +9781,7 @@ lpfc_sli_prep_dev_for_recover(struct lpfc_hba *phba)
         * There may be errored I/Os through HBA, abort all I/Os on txcmplq
         * and let the SCSI mid-layer to retry them to recover.
         */
-       pring = &psli->ring[psli->fcp_ring];
-       lpfc_sli_abort_iocb_ring(phba, pring);
+       lpfc_sli_abort_fcp_rings(phba);
 }
 
 /**
@@ -10417,17 +10524,13 @@ lpfc_pci_resume_one_s4(struct pci_dev *pdev)
 static void
 lpfc_sli4_prep_dev_for_recover(struct lpfc_hba *phba)
 {
-       struct lpfc_sli *psli = &phba->sli;
-       struct lpfc_sli_ring  *pring;
-
        lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
                        "2828 PCI channel I/O abort preparing for recovery\n");
        /*
         * There may be errored I/Os through HBA, abort all I/Os on txcmplq
         * and let the SCSI mid-layer to retry them to recover.
         */
-       pring = &psli->ring[psli->fcp_ring];
-       lpfc_sli_abort_iocb_ring(phba, pring);
+       lpfc_sli_abort_fcp_rings(phba);
 }
 
 /**
@@ -10898,7 +11001,7 @@ lpfc_sli4_oas_verify(struct lpfc_hba *phba)
        if (phba->sli4_hba.pc_sli4_params.oas_supported) {
                phba->cfg_fof = 1;
        } else {
-               phba->cfg_EnableXLane = 0;
+               phba->cfg_fof = 0;
                if (phba->device_data_mem_pool)
                        mempool_destroy(phba->device_data_mem_pool);
                phba->device_data_mem_pool = NULL;
@@ -10928,7 +11031,7 @@ lpfc_fof_queue_setup(struct lpfc_hba *phba)
        if (rc)
                return -ENOMEM;
 
-       if (phba->cfg_EnableXLane) {
+       if (phba->cfg_fof) {
 
                rc = lpfc_cq_create(phba, phba->sli4_hba.oas_cq,
                                    phba->sli4_hba.fof_eq, LPFC_WCQ, LPFC_FCP);
@@ -10947,8 +11050,7 @@ lpfc_fof_queue_setup(struct lpfc_hba *phba)
        return 0;
 
 out_oas_wq:
-       if (phba->cfg_EnableXLane)
-               lpfc_cq_destroy(phba, phba->sli4_hba.oas_cq);
+       lpfc_cq_destroy(phba, phba->sli4_hba.oas_cq);
 out_oas_cq:
        lpfc_eq_destroy(phba, phba->sli4_hba.fof_eq);
        return rc;
@@ -10982,7 +11084,7 @@ lpfc_fof_queue_create(struct lpfc_hba *phba)
 
        phba->sli4_hba.fof_eq = qdesc;
 
-       if (phba->cfg_EnableXLane) {
+       if (phba->cfg_fof) {
 
                /* Create OAS CQ */
                qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize,
index ed419aad2b1f2cdab74a4377308d2ec800d0f77b..3fa65338d3f55614628c2710afaacd8ebaa53b2b 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2012 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2014 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
index 462453ee0bdaf1c184d19e3bc2985fa6100bb7bb..2df11daad85ba2e64f6f4284a9e6f3384ed8a2e7 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2013 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2014 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
@@ -73,7 +73,7 @@ lpfc_rport_data_from_scsi_device(struct scsi_device *sdev)
 {
        struct lpfc_vport *vport = (struct lpfc_vport *)sdev->host->hostdata;
 
-       if (vport->phba->cfg_EnableXLane)
+       if (vport->phba->cfg_fof)
                return ((struct lpfc_device_data *)sdev->hostdata)->rport_data;
        else
                return (struct lpfc_rport_data *)sdev->hostdata;
@@ -3462,7 +3462,7 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
         * If the OAS driver feature is enabled and the lun is enabled for
         * OAS, set the oas iocb related flags.
         */
-       if ((phba->cfg_EnableXLane) && ((struct lpfc_device_data *)
+       if ((phba->cfg_fof) && ((struct lpfc_device_data *)
                scsi_cmnd->device->hostdata)->oas_enabled)
                lpfc_cmd->cur_iocbq.iocb_flag |= LPFC_IO_OAS;
        return 0;
@@ -4314,6 +4314,7 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
                fcp_cmnd->fcpCntl1 = SIMPLE_Q;
 
        sli4 = (phba->sli_rev == LPFC_SLI_REV4);
+       piocbq->iocb.un.fcpi.fcpi_XRdy = 0;
 
        /*
         * There are three possibilities here - use scatter-gather segment, use
@@ -4782,7 +4783,9 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
        struct lpfc_scsi_buf *lpfc_cmd;
        IOCB_t *cmd, *icmd;
        int ret = SUCCESS, status = 0;
-       unsigned long flags;
+       struct lpfc_sli_ring *pring_s4;
+       int ring_number, ret_val;
+       unsigned long flags, iflags;
        DECLARE_WAIT_QUEUE_HEAD_ONSTACK(waitq);
 
        status = fc_block_scsi_eh(cmnd);
@@ -4833,6 +4836,14 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
 
        BUG_ON(iocb->context1 != lpfc_cmd);
 
+       /* abort issued in recovery is still in progress */
+       if (iocb->iocb_flag & LPFC_DRIVER_ABORTED) {
+               lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
+                        "3389 SCSI Layer I/O Abort Request is pending\n");
+               spin_unlock_irqrestore(&phba->hbalock, flags);
+               goto wait_for_cmpl;
+       }
+
        abtsiocb = __lpfc_sli_get_iocbq(phba);
        if (abtsiocb == NULL) {
                ret = FAILED;
@@ -4871,11 +4882,23 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
 
        abtsiocb->iocb_cmpl = lpfc_sli_abort_fcp_cmpl;
        abtsiocb->vport = vport;
+       if (phba->sli_rev == LPFC_SLI_REV4) {
+               ring_number = MAX_SLI3_CONFIGURED_RINGS + iocb->fcp_wqidx;
+               pring_s4 = &phba->sli.ring[ring_number];
+               /* Note: both hbalock and ring_lock must be set here */
+               spin_lock_irqsave(&pring_s4->ring_lock, iflags);
+               ret_val = __lpfc_sli_issue_iocb(phba, pring_s4->ringno,
+                                               abtsiocb, 0);
+               spin_unlock_irqrestore(&pring_s4->ring_lock, iflags);
+       } else {
+               ret_val = __lpfc_sli_issue_iocb(phba, LPFC_FCP_RING,
+                                               abtsiocb, 0);
+       }
        /* no longer need the lock after this point */
        spin_unlock_irqrestore(&phba->hbalock, flags);
 
-       if (lpfc_sli_issue_iocb(phba, LPFC_FCP_RING, abtsiocb, 0) ==
-           IOCB_ERROR) {
+
+       if (ret_val == IOCB_ERROR) {
                lpfc_sli_release_iocbq(phba, abtsiocb);
                ret = FAILED;
                goto out;
@@ -4885,12 +4908,16 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
                lpfc_sli_handle_fast_ring_event(phba,
                        &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ);
 
+wait_for_cmpl:
        lpfc_cmd->waitq = &waitq;
        /* Wait for abort to complete */
        wait_event_timeout(waitq,
                          (lpfc_cmd->pCmd != cmnd),
                           msecs_to_jiffies(2*vport->cfg_devloss_tmo*1000));
+
+       spin_lock_irqsave(shost->host_lock, flags);
        lpfc_cmd->waitq = NULL;
+       spin_unlock_irqrestore(shost->host_lock, flags);
 
        if (lpfc_cmd->pCmd == cmnd) {
                ret = FAILED;
@@ -5172,8 +5199,9 @@ lpfc_reset_flush_io_context(struct lpfc_vport *vport, uint16_t tgt_id,
 
        cnt = lpfc_sli_sum_iocb(vport, tgt_id, lun_id, context);
        if (cnt)
-               lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring],
-                                   tgt_id, lun_id, context);
+               lpfc_sli_abort_taskmgmt(vport,
+                                       &phba->sli.ring[phba->sli.fcp_ring],
+                                       tgt_id, lun_id, context);
        later = msecs_to_jiffies(2 * vport->cfg_devloss_tmo * 1000) + jiffies;
        while (time_after(later, jiffies) && cnt) {
                schedule_timeout_uninterruptible(msecs_to_jiffies(20));
@@ -5491,7 +5519,7 @@ lpfc_slave_alloc(struct scsi_device *sdev)
        if (!rport || fc_remote_port_chkready(rport))
                return -ENXIO;
 
-       if (phba->cfg_EnableXLane) {
+       if (phba->cfg_fof) {
 
                /*
                 * Check to see if the device data structure for the lun
@@ -5616,7 +5644,7 @@ lpfc_slave_destroy(struct scsi_device *sdev)
        struct lpfc_device_data *device_data = sdev->hostdata;
 
        atomic_dec(&phba->sdev_cnt);
-       if ((phba->cfg_EnableXLane) && (device_data)) {
+       if ((phba->cfg_fof) && (device_data)) {
                spin_lock_irqsave(&phba->devicelock, flags);
                device_data->available = false;
                if (!device_data->oas_enabled)
@@ -5655,7 +5683,7 @@ lpfc_create_device_data(struct lpfc_hba *phba, struct lpfc_name *vport_wwpn,
        int memory_flags;
 
        if (unlikely(!phba) || !vport_wwpn || !target_wwpn  ||
-           !(phba->cfg_EnableXLane))
+           !(phba->cfg_fof))
                return NULL;
 
        /* Attempt to create the device data to contain lun info */
@@ -5693,7 +5721,7 @@ lpfc_delete_device_data(struct lpfc_hba *phba,
 {
 
        if (unlikely(!phba) || !lun_info  ||
-           !(phba->cfg_EnableXLane))
+           !(phba->cfg_fof))
                return;
 
        if (!list_empty(&lun_info->listentry))
@@ -5727,7 +5755,7 @@ __lpfc_get_device_data(struct lpfc_hba *phba, struct list_head *list,
        struct lpfc_device_data *lun_info;
 
        if (unlikely(!phba) || !list || !vport_wwpn || !target_wwpn ||
-           !phba->cfg_EnableXLane)
+           !phba->cfg_fof)
                return NULL;
 
        /* Check to see if the lun is already enabled for OAS. */
@@ -5789,7 +5817,7 @@ lpfc_find_next_oas_lun(struct lpfc_hba *phba, struct lpfc_name *vport_wwpn,
            !starting_lun || !found_vport_wwpn ||
            !found_target_wwpn || !found_lun || !found_lun_status ||
            (*starting_lun == NO_MORE_OAS_LUN) ||
-           !phba->cfg_EnableXLane)
+           !phba->cfg_fof)
                return false;
 
        lun = *starting_lun;
@@ -5873,7 +5901,7 @@ lpfc_enable_oas_lun(struct lpfc_hba *phba, struct lpfc_name *vport_wwpn,
        unsigned long flags;
 
        if (unlikely(!phba) || !vport_wwpn || !target_wwpn ||
-           !phba->cfg_EnableXLane)
+           !phba->cfg_fof)
                return false;
 
        spin_lock_irqsave(&phba->devicelock, flags);
@@ -5930,7 +5958,7 @@ lpfc_disable_oas_lun(struct lpfc_hba *phba, struct lpfc_name *vport_wwpn,
        unsigned long flags;
 
        if (unlikely(!phba) || !vport_wwpn || !target_wwpn ||
-           !phba->cfg_EnableXLane)
+           !phba->cfg_fof)
                return false;
 
        spin_lock_irqsave(&phba->devicelock, flags);
index 0120bfccf50bfd57a6f9f0b1e6f6f7516fd4eb6c..0389ac1e7b8320100676d78ed88ae2767d0a08b5 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2013 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2014 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  *                                                                 *
index 393662c24df5086014277cbe411528ca6e6680d2..32ada0505576bcb0dc2abe2d1247887e212997cf 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2013 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2014 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  * Portions Copyright (C) 2004-2005 Christoph Hellwig              *
@@ -3532,20 +3532,63 @@ lpfc_sli_abort_iocb_ring(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
        /* Error everything on txq and txcmplq
         * First do the txq.
         */
-       spin_lock_irq(&phba->hbalock);
-       list_splice_init(&pring->txq, &completions);
+       if (phba->sli_rev >= LPFC_SLI_REV4) {
+               spin_lock_irq(&pring->ring_lock);
+               list_splice_init(&pring->txq, &completions);
+               pring->txq_cnt = 0;
+               spin_unlock_irq(&pring->ring_lock);
 
-       /* Next issue ABTS for everything on the txcmplq */
-       list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list)
-               lpfc_sli_issue_abort_iotag(phba, pring, iocb);
+               spin_lock_irq(&phba->hbalock);
+               /* Next issue ABTS for everything on the txcmplq */
+               list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list)
+                       lpfc_sli_issue_abort_iotag(phba, pring, iocb);
+               spin_unlock_irq(&phba->hbalock);
+       } else {
+               spin_lock_irq(&phba->hbalock);
+               list_splice_init(&pring->txq, &completions);
+               pring->txq_cnt = 0;
 
-       spin_unlock_irq(&phba->hbalock);
+               /* Next issue ABTS for everything on the txcmplq */
+               list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list)
+                       lpfc_sli_issue_abort_iotag(phba, pring, iocb);
+               spin_unlock_irq(&phba->hbalock);
+       }
 
        /* Cancel all the IOCBs from the completions list */
        lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
                              IOERR_SLI_ABORTED);
 }
 
+/**
+ * lpfc_sli_abort_fcp_rings - Abort all iocbs in all FCP rings
+ * @phba: Pointer to HBA context object.
+ * @pring: Pointer to driver SLI ring object.
+ *
+ * This function aborts all iocbs in FCP rings and frees all the iocb
+ * objects in txq. This function issues an abort iocb for all the iocb commands
+ * in txcmplq. The iocbs in the txcmplq is not guaranteed to complete before
+ * the return of this function. The caller is not required to hold any locks.
+ **/
+void
+lpfc_sli_abort_fcp_rings(struct lpfc_hba *phba)
+{
+       struct lpfc_sli *psli = &phba->sli;
+       struct lpfc_sli_ring  *pring;
+       uint32_t i;
+
+       /* Look on all the FCP Rings for the iotag */
+       if (phba->sli_rev >= LPFC_SLI_REV4) {
+               for (i = 0; i < phba->cfg_fcp_io_channel; i++) {
+                       pring = &psli->ring[i + MAX_SLI3_CONFIGURED_RINGS];
+                       lpfc_sli_abort_iocb_ring(phba, pring);
+               }
+       } else {
+               pring = &psli->ring[psli->fcp_ring];
+               lpfc_sli_abort_iocb_ring(phba, pring);
+       }
+}
+
+
 /**
  * lpfc_sli_flush_fcp_rings - flush all iocbs in the fcp ring
  * @phba: Pointer to HBA context object.
@@ -3563,28 +3606,55 @@ lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba)
        LIST_HEAD(txcmplq);
        struct lpfc_sli *psli = &phba->sli;
        struct lpfc_sli_ring  *pring;
-
-       /* Currently, only one fcp ring */
-       pring = &psli->ring[psli->fcp_ring];
+       uint32_t i;
 
        spin_lock_irq(&phba->hbalock);
-       /* Retrieve everything on txq */
-       list_splice_init(&pring->txq, &txq);
-
-       /* Retrieve everything on the txcmplq */
-       list_splice_init(&pring->txcmplq, &txcmplq);
-
        /* Indicate the I/O queues are flushed */
        phba->hba_flag |= HBA_FCP_IOQ_FLUSH;
        spin_unlock_irq(&phba->hbalock);
 
-       /* Flush the txq */
-       lpfc_sli_cancel_iocbs(phba, &txq, IOSTAT_LOCAL_REJECT,
-                             IOERR_SLI_DOWN);
+       /* Look on all the FCP Rings for the iotag */
+       if (phba->sli_rev >= LPFC_SLI_REV4) {
+               for (i = 0; i < phba->cfg_fcp_io_channel; i++) {
+                       pring = &psli->ring[i + MAX_SLI3_CONFIGURED_RINGS];
+
+                       spin_lock_irq(&pring->ring_lock);
+                       /* Retrieve everything on txq */
+                       list_splice_init(&pring->txq, &txq);
+                       /* Retrieve everything on the txcmplq */
+                       list_splice_init(&pring->txcmplq, &txcmplq);
+                       pring->txq_cnt = 0;
+                       pring->txcmplq_cnt = 0;
+                       spin_unlock_irq(&pring->ring_lock);
+
+                       /* Flush the txq */
+                       lpfc_sli_cancel_iocbs(phba, &txq,
+                                             IOSTAT_LOCAL_REJECT,
+                                             IOERR_SLI_DOWN);
+                       /* Flush the txcmpq */
+                       lpfc_sli_cancel_iocbs(phba, &txcmplq,
+                                             IOSTAT_LOCAL_REJECT,
+                                             IOERR_SLI_DOWN);
+               }
+       } else {
+               pring = &psli->ring[psli->fcp_ring];
 
-       /* Flush the txcmpq */
-       lpfc_sli_cancel_iocbs(phba, &txcmplq, IOSTAT_LOCAL_REJECT,
-                             IOERR_SLI_DOWN);
+               spin_lock_irq(&phba->hbalock);
+               /* Retrieve everything on txq */
+               list_splice_init(&pring->txq, &txq);
+               /* Retrieve everything on the txcmplq */
+               list_splice_init(&pring->txcmplq, &txcmplq);
+               pring->txq_cnt = 0;
+               pring->txcmplq_cnt = 0;
+               spin_unlock_irq(&phba->hbalock);
+
+               /* Flush the txq */
+               lpfc_sli_cancel_iocbs(phba, &txq, IOSTAT_LOCAL_REJECT,
+                                     IOERR_SLI_DOWN);
+               /* Flush the txcmpq */
+               lpfc_sli_cancel_iocbs(phba, &txcmplq, IOSTAT_LOCAL_REJECT,
+                                     IOERR_SLI_DOWN);
+       }
 }
 
 /**
@@ -3987,12 +4057,13 @@ lpfc_sli4_brdreset(struct lpfc_hba *phba)
 {
        struct lpfc_sli *psli = &phba->sli;
        uint16_t cfg_value;
-       int rc;
+       int rc = 0;
 
        /* Reset HBA */
        lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
-                       "0295 Reset HBA Data: x%x x%x\n",
-                       phba->pport->port_state, psli->sli_flag);
+                       "0295 Reset HBA Data: x%x x%x x%x\n",
+                       phba->pport->port_state, psli->sli_flag,
+                       phba->hba_flag);
 
        /* perform board reset */
        phba->fc_eventTag = 0;
@@ -4005,6 +4076,12 @@ lpfc_sli4_brdreset(struct lpfc_hba *phba)
        phba->fcf.fcf_flag = 0;
        spin_unlock_irq(&phba->hbalock);
 
+       /* SLI4 INTF 2: if FW dump is being taken skip INIT_PORT */
+       if (phba->hba_flag & HBA_FW_DUMP_OP) {
+               phba->hba_flag &= ~HBA_FW_DUMP_OP;
+               return rc;
+       }
+
        /* Now physically reset the device */
        lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
                        "0389 Performing PCI function reset!\n");
@@ -5002,7 +5079,7 @@ lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba)
                } while (++fcp_eqidx < phba->cfg_fcp_io_channel);
        }
 
-       if (phba->cfg_EnableXLane)
+       if (phba->cfg_fof)
                lpfc_sli4_cq_release(phba->sli4_hba.oas_cq, LPFC_QUEUE_REARM);
 
        if (phba->sli4_hba.hba_eq) {
@@ -6722,7 +6799,6 @@ lpfc_mbox_timeout_handler(struct lpfc_hba *phba)
        LPFC_MBOXQ_t *pmbox = phba->sli.mbox_active;
        MAILBOX_t *mb = &pmbox->u.mb;
        struct lpfc_sli *psli = &phba->sli;
-       struct lpfc_sli_ring *pring;
 
        /* If the mailbox completed, process the completion and return */
        if (lpfc_sli4_process_missed_mbox_completions(phba))
@@ -6764,8 +6840,7 @@ lpfc_mbox_timeout_handler(struct lpfc_hba *phba)
        psli->sli_flag &= ~LPFC_SLI_ACTIVE;
        spin_unlock_irq(&phba->hbalock);
 
-       pring = &psli->ring[psli->fcp_ring];
-       lpfc_sli_abort_iocb_ring(phba, pring);
+       lpfc_sli_abort_fcp_rings(phba);
 
        lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
                        "0345 Resetting board due to mailbox timeout\n");
@@ -8133,6 +8208,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
        abort_tag = (uint32_t) iocbq->iotag;
        xritag = iocbq->sli4_xritag;
        wqe->generic.wqe_com.word7 = 0; /* The ct field has moved so reset */
+       wqe->generic.wqe_com.word10 = 0;
        /* words0-2 bpl convert bde */
        if (iocbq->iocb.un.genreq64.bdl.bdeFlags == BUFF_TYPE_BLP_64) {
                numBdes = iocbq->iocb.un.genreq64.bdl.bdeSize /
@@ -8639,8 +8715,7 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
 
        if ((piocb->iocb_flag & LPFC_IO_FCP) ||
            (piocb->iocb_flag & LPFC_USE_FCPWQIDX)) {
-               if (!phba->cfg_EnableXLane || (!(piocb->iocb_flag &
-                       LPFC_IO_OAS))) {
+               if (!phba->cfg_fof || (!(piocb->iocb_flag & LPFC_IO_OAS))) {
                        wq = phba->sli4_hba.fcp_wq[piocb->fcp_wqidx];
                } else {
                        wq = phba->sli4_hba.oas_wq;
@@ -8735,7 +8810,7 @@ lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number,
 
        if (phba->sli_rev == LPFC_SLI_REV4) {
                if (piocb->iocb_flag &  LPFC_IO_FCP) {
-                       if (!phba->cfg_EnableXLane || (!(piocb->iocb_flag &
+                       if (!phba->cfg_fof || (!(piocb->iocb_flag &
                                LPFC_IO_OAS))) {
                                if (unlikely(!phba->sli4_hba.fcp_wq))
                                        return IOCB_ERROR;
@@ -9170,6 +9245,7 @@ lpfc_sli_queue_setup(struct lpfc_hba *phba)
                pring->sli.sli3.next_cmdidx  = 0;
                pring->sli.sli3.local_getidx = 0;
                pring->sli.sli3.cmdidx = 0;
+               pring->flag = 0;
                INIT_LIST_HEAD(&pring->txq);
                INIT_LIST_HEAD(&pring->txcmplq);
                INIT_LIST_HEAD(&pring->iocb_continueq);
@@ -9804,43 +9880,6 @@ abort_iotag_exit:
        return retval;
 }
 
-/**
- * lpfc_sli_iocb_ring_abort - Unconditionally abort all iocbs on an iocb ring
- * @phba: Pointer to HBA context object.
- * @pring: Pointer to driver SLI ring object.
- *
- * This function aborts all iocbs in the given ring and frees all the iocb
- * objects in txq. This function issues abort iocbs unconditionally for all
- * the iocb commands in txcmplq. The iocbs in the txcmplq is not guaranteed
- * to complete before the return of this function. The caller is not required
- * to hold any locks.
- **/
-static void
-lpfc_sli_iocb_ring_abort(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
-{
-       LIST_HEAD(completions);
-       struct lpfc_iocbq *iocb, *next_iocb;
-
-       if (pring->ringno == LPFC_ELS_RING)
-               lpfc_fabric_abort_hba(phba);
-
-       spin_lock_irq(&phba->hbalock);
-
-       /* Take off all the iocbs on txq for cancelling */
-       list_splice_init(&pring->txq, &completions);
-       pring->txq_cnt = 0;
-
-       /* Next issue ABTS for everything on the txcmplq */
-       list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list)
-               lpfc_sli_abort_iotag_issue(phba, pring, iocb);
-
-       spin_unlock_irq(&phba->hbalock);
-
-       /* Cancel all the IOCBs from the completions list */
-       lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT,
-                             IOERR_SLI_ABORTED);
-}
-
 /**
  * lpfc_sli_hba_iocb_abort - Abort all iocbs to an hba.
  * @phba: pointer to lpfc HBA data structure.
@@ -9856,7 +9895,7 @@ lpfc_sli_hba_iocb_abort(struct lpfc_hba *phba)
 
        for (i = 0; i < psli->num_rings; i++) {
                pring = &psli->ring[i];
-               lpfc_sli_iocb_ring_abort(phba, pring);
+               lpfc_sli_abort_iocb_ring(phba, pring);
        }
 }
 
@@ -10080,6 +10119,124 @@ lpfc_sli_abort_iocb(struct lpfc_vport *vport, struct lpfc_sli_ring *pring,
        return errcnt;
 }
 
+/**
+ * lpfc_sli_abort_taskmgmt - issue abort for all commands on a host/target/LUN
+ * @vport: Pointer to virtual port.
+ * @pring: Pointer to driver SLI ring object.
+ * @tgt_id: SCSI ID of the target.
+ * @lun_id: LUN ID of the scsi device.
+ * @taskmgmt_cmd: LPFC_CTX_LUN/LPFC_CTX_TGT/LPFC_CTX_HOST.
+ *
+ * This function sends an abort command for every SCSI command
+ * associated with the given virtual port pending on the ring
+ * filtered by lpfc_sli_validate_fcp_iocb function.
+ * When taskmgmt_cmd == LPFC_CTX_LUN, the function sends abort only to the
+ * FCP iocbs associated with lun specified by tgt_id and lun_id
+ * parameters
+ * When taskmgmt_cmd == LPFC_CTX_TGT, the function sends abort only to the
+ * FCP iocbs associated with SCSI target specified by tgt_id parameter.
+ * When taskmgmt_cmd == LPFC_CTX_HOST, the function sends abort to all
+ * FCP iocbs associated with virtual port.
+ * This function returns number of iocbs it aborted .
+ * This function is called with no locks held right after a taskmgmt
+ * command is sent.
+ **/
+int
+lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring,
+                       uint16_t tgt_id, uint64_t lun_id, lpfc_ctx_cmd cmd)
+{
+       struct lpfc_hba *phba = vport->phba;
+       struct lpfc_iocbq *abtsiocbq;
+       struct lpfc_iocbq *iocbq;
+       IOCB_t *icmd;
+       int sum, i, ret_val;
+       unsigned long iflags;
+       struct lpfc_sli_ring *pring_s4;
+       uint32_t ring_number;
+
+       spin_lock_irq(&phba->hbalock);
+
+       /* all I/Os are in process of being flushed */
+       if (phba->hba_flag & HBA_FCP_IOQ_FLUSH) {
+               spin_unlock_irq(&phba->hbalock);
+               return 0;
+       }
+       sum = 0;
+
+       for (i = 1; i <= phba->sli.last_iotag; i++) {
+               iocbq = phba->sli.iocbq_lookup[i];
+
+               if (lpfc_sli_validate_fcp_iocb(iocbq, vport, tgt_id, lun_id,
+                                              cmd) != 0)
+                       continue;
+
+               /*
+                * If the iocbq is already being aborted, don't take a second
+                * action, but do count it.
+                */
+               if (iocbq->iocb_flag & LPFC_DRIVER_ABORTED)
+                       continue;
+
+               /* issue ABTS for this IOCB based on iotag */
+               abtsiocbq = __lpfc_sli_get_iocbq(phba);
+               if (abtsiocbq == NULL)
+                       continue;
+
+               icmd = &iocbq->iocb;
+               abtsiocbq->iocb.un.acxri.abortType = ABORT_TYPE_ABTS;
+               abtsiocbq->iocb.un.acxri.abortContextTag = icmd->ulpContext;
+               if (phba->sli_rev == LPFC_SLI_REV4)
+                       abtsiocbq->iocb.un.acxri.abortIoTag =
+                                                        iocbq->sli4_xritag;
+               else
+                       abtsiocbq->iocb.un.acxri.abortIoTag = icmd->ulpIoTag;
+               abtsiocbq->iocb.ulpLe = 1;
+               abtsiocbq->iocb.ulpClass = icmd->ulpClass;
+               abtsiocbq->vport = vport;
+
+               /* ABTS WQE must go to the same WQ as the WQE to be aborted */
+               abtsiocbq->fcp_wqidx = iocbq->fcp_wqidx;
+               if (iocbq->iocb_flag & LPFC_IO_FCP)
+                       abtsiocbq->iocb_flag |= LPFC_USE_FCPWQIDX;
+
+               if (lpfc_is_link_up(phba))
+                       abtsiocbq->iocb.ulpCommand = CMD_ABORT_XRI_CN;
+               else
+                       abtsiocbq->iocb.ulpCommand = CMD_CLOSE_XRI_CN;
+
+               /* Setup callback routine and issue the command. */
+               abtsiocbq->iocb_cmpl = lpfc_sli_abort_fcp_cmpl;
+
+               /*
+                * Indicate the IO is being aborted by the driver and set
+                * the caller's flag into the aborted IO.
+                */
+               iocbq->iocb_flag |= LPFC_DRIVER_ABORTED;
+
+               if (phba->sli_rev == LPFC_SLI_REV4) {
+                       ring_number = MAX_SLI3_CONFIGURED_RINGS +
+                                        iocbq->fcp_wqidx;
+                       pring_s4 = &phba->sli.ring[ring_number];
+                       /* Note: both hbalock and ring_lock must be set here */
+                       spin_lock_irqsave(&pring_s4->ring_lock, iflags);
+                       ret_val = __lpfc_sli_issue_iocb(phba, pring_s4->ringno,
+                                                       abtsiocbq, 0);
+                       spin_unlock_irqrestore(&pring_s4->ring_lock, iflags);
+               } else {
+                       ret_val = __lpfc_sli_issue_iocb(phba, pring->ringno,
+                                                       abtsiocbq, 0);
+               }
+
+
+               if (ret_val == IOCB_ERROR)
+                       __lpfc_sli_release_iocbq(phba, abtsiocbq);
+               else
+                       sum++;
+       }
+       spin_unlock_irq(&phba->hbalock);
+       return sum;
+}
+
 /**
  * lpfc_sli_wake_iocb_wait - lpfc_sli_issue_iocb_wait's completion handler
  * @phba: Pointer to HBA context object.
index 6f04080f4ea857f328e6eb7cbe4b48854064001d..edb48832c39bdd76ec8fdf329260a0822b129198 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2013 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2014 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  *                                                                 *
index 9b8cda866176feae55b3450be1233e1d63090e34..7f50aa04d66a1821e609e70d71758ae225cb7168 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2009-2013 Emulex.  All rights reserved.           *
+ * Copyright (C) 2009-2014 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  *                                                                 *
index e32cbec70324b6d6732ae071f73a0173486b3fee..41675c1193e7582db29d8bb0bfe8420abbfec768 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************
  * This file is part of the Emulex Linux Device Driver for         *
  * Fibre Channel Host Bus Adapters.                                *
- * Copyright (C) 2004-2013 Emulex.  All rights reserved.           *
+ * Copyright (C) 2004-2014 Emulex.  All rights reserved.           *
  * EMULEX and SLI are trademarks of Emulex.                        *
  * www.emulex.com                                                  *
  *                                                                 *
@@ -18,7 +18,7 @@
  * included with this package.                                     *
  *******************************************************************/
 
-#define LPFC_DRIVER_VERSION "8.3.45"
+#define LPFC_DRIVER_VERSION "10.2.8001.0."
 #define LPFC_DRIVER_NAME               "lpfc"
 
 /* Used for SLI 2/3 */
@@ -30,4 +30,4 @@
 
 #define LPFC_MODULE_DESC "Emulex LightPulse Fibre Channel SCSI driver " \
                LPFC_DRIVER_VERSION
-#define LPFC_COPYRIGHT "Copyright(c) 2004-2013 Emulex.  All rights reserved."
+#define LPFC_COPYRIGHT "Copyright(c) 2004-2014 Emulex.  All rights reserved."
index fe5eee4d0a1133645116fb7af6e799e0154bddf7..a368d77b8d4100dbe267da5f9f419e0a2dbaa68f 100644 (file)
@@ -732,7 +732,7 @@ static ssize_t pm8001_show_update_fw(struct device *cdev,
                        flash_error_table[i].reason);
 }
 
-static DEVICE_ATTR(update_fw, S_IRUGO|S_IWUGO,
+static DEVICE_ATTR(update_fw, S_IRUGO|S_IWUSR|S_IWGRP,
        pm8001_show_update_fw, pm8001_store_update_fw);
 struct device_attribute *pm8001_host_attrs[] = {
        &dev_attr_interface_rev,
index 1fa01044866637845d06a18fba953580a4696a5d..de5d0ae19d83ce995701183eb5c4586d528aeaa3 100644 (file)
@@ -1648,16 +1648,16 @@ typedef struct {
  */
 struct crc_context {
        uint32_t handle;                /* System handle. */
-       uint32_t ref_tag;
-       uint16_t app_tag;
+       __le32 ref_tag;
+       __le16 app_tag;
        uint8_t ref_tag_mask[4];        /* Validation/Replacement Mask*/
        uint8_t app_tag_mask[2];        /* Validation/Replacement Mask*/
-       uint16_t guard_seed;            /* Initial Guard Seed */
-       uint16_t prot_opts;             /* Requested Data Protection Mode */
-       uint16_t blk_size;              /* Data size in bytes */
+       __le16 guard_seed;              /* Initial Guard Seed */
+       __le16 prot_opts;               /* Requested Data Protection Mode */
+       __le16 blk_size;                /* Data size in bytes */
        uint16_t runt_blk_guard;        /* Guard value for runt block (tape
                                         * only) */
-       uint32_t byte_count;            /* Total byte count/ total data
+       __le32 byte_count;              /* Total byte count/ total data
                                         * transfer count */
        union {
                struct {
@@ -1671,10 +1671,10 @@ struct crc_context {
                        uint32_t        reserved_6;
                } nobundling;
                struct {
-                       uint32_t        dif_byte_count; /* Total DIF byte
+                       __le32  dif_byte_count; /* Total DIF byte
                                                         * count */
                        uint16_t        reserved_1;
-                       uint16_t        dseg_count;     /* Data segment count */
+                       __le16  dseg_count;     /* Data segment count */
                        uint32_t        reserved_2;
                        uint32_t        data_address[2];
                        uint32_t        data_length;
index b1d10f9935c7caac0f85cc7cdb17ccee12c9e8df..4b188b0164e9de40edebb44cf306ebcb92976f34 100644 (file)
@@ -104,7 +104,6 @@ static void qlt_reject_free_srr_imm(struct scsi_qla_host *ha,
 /*
  * Global Variables
  */
-static struct kmem_cache *qla_tgt_cmd_cachep;
 static struct kmem_cache *qla_tgt_mgmt_cmd_cachep;
 static mempool_t *qla_tgt_mgmt_cmd_mempool;
 static struct workqueue_struct *qla_tgt_wq;
@@ -1997,7 +1996,7 @@ qlt_set_t10dif_tags(struct se_cmd *se_cmd, struct crc_context *ctx)
         * have been immplemented by TCM, before AppTag is avail.
         * Look for modesense_handlers[]
         */
-       ctx->app_tag = __constant_cpu_to_le16(0);
+       ctx->app_tag = 0;
        ctx->app_tag_mask[0] = 0x0;
        ctx->app_tag_mask[1] = 0x0;
 
@@ -2079,6 +2078,7 @@ qlt_build_ctio_crc2_pkt(struct qla_tgt_prm *prm, scsi_qla_host_t *vha)
        struct se_cmd           *se_cmd = &cmd->se_cmd;
        uint32_t h;
        struct atio_from_isp *atio = &prm->cmd->atio;
+       uint16_t t16;
 
        sgc = 0;
        ha = vha->hw;
@@ -2175,8 +2175,13 @@ qlt_build_ctio_crc2_pkt(struct qla_tgt_prm *prm, scsi_qla_host_t *vha)
        pkt->initiator_id[1] = atio->u.isp24.fcp_hdr.s_id[1];
        pkt->initiator_id[2] = atio->u.isp24.fcp_hdr.s_id[0];
        pkt->exchange_addr   = atio->u.isp24.exchange_addr;
-       pkt->ox_id  = swab16(atio->u.isp24.fcp_hdr.ox_id);
-       pkt->flags |= (atio->u.isp24.attr << 9);
+
+       /* silence compile warning */
+       t16 = be16_to_cpu(atio->u.isp24.fcp_hdr.ox_id);
+       pkt->ox_id  = cpu_to_le16(t16);
+
+       t16 = (atio->u.isp24.attr << 9);
+       pkt->flags |= cpu_to_le16(t16);
        pkt->relative_offset = cpu_to_le32(prm->cmd->offset);
 
        /* Set transfer direction */
@@ -2251,8 +2256,7 @@ qlt_build_ctio_crc2_pkt(struct qla_tgt_prm *prm, scsi_qla_host_t *vha)
 
        if (bundling && prm->prot_seg_cnt) {
                /* Walks dif segments */
-               pkt->add_flags |=
-                       __constant_cpu_to_le16(CTIO_CRC2_AF_DIF_DSD_ENA);
+               pkt->add_flags |= CTIO_CRC2_AF_DIF_DSD_ENA;
 
                cur_dsd = (uint32_t *) &crc_ctx_pkt->u.bundling.dif_address;
                if (qla24xx_walk_and_build_prot_sglist(ha, NULL, cur_dsd,
@@ -2705,6 +2709,8 @@ done:
 
 void qlt_free_cmd(struct qla_tgt_cmd *cmd)
 {
+       struct qla_tgt_sess *sess = cmd->sess;
+
        ql_dbg(ql_dbg_tgt, cmd->vha, 0xe074,
            "%s: se_cmd[%p] ox_id %04x\n",
            __func__, &cmd->se_cmd,
@@ -2713,7 +2719,12 @@ void qlt_free_cmd(struct qla_tgt_cmd *cmd)
        BUG_ON(cmd->sg_mapped);
        if (unlikely(cmd->free_sg))
                kfree(cmd->sg);
-       kmem_cache_free(qla_tgt_cmd_cachep, cmd);
+
+       if (!sess || !sess->se_sess) {
+               WARN_ON(1);
+               return;
+       }
+       percpu_ida_free(&sess->se_sess->sess_tag_pool, cmd->se_cmd.map_tag);
 }
 EXPORT_SYMBOL(qlt_free_cmd);
 
@@ -3075,13 +3086,12 @@ static struct qla_tgt_sess *qlt_make_local_sess(struct scsi_qla_host *,
 /*
  * Process context for I/O path into tcm_qla2xxx code
  */
-static void qlt_do_work(struct work_struct *work)
+static void __qlt_do_work(struct qla_tgt_cmd *cmd)
 {
-       struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work);
        scsi_qla_host_t *vha = cmd->vha;
        struct qla_hw_data *ha = vha->hw;
        struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
-       struct qla_tgt_sess *sess = NULL;
+       struct qla_tgt_sess *sess = cmd->sess;
        struct atio_from_isp *atio = &cmd->atio;
        unsigned char *cdb;
        unsigned long flags;
@@ -3091,41 +3101,6 @@ static void qlt_do_work(struct work_struct *work)
        if (tgt->tgt_stop)
                goto out_term;
 
-       spin_lock_irqsave(&ha->hardware_lock, flags);
-       sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha,
-           atio->u.isp24.fcp_hdr.s_id);
-       /* Do kref_get() before dropping qla_hw_data->hardware_lock. */
-       if (sess)
-               kref_get(&sess->se_sess->sess_kref);
-       spin_unlock_irqrestore(&ha->hardware_lock, flags);
-
-       if (unlikely(!sess)) {
-               uint8_t *s_id = atio->u.isp24.fcp_hdr.s_id;
-
-               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf022,
-                       "qla_target(%d): Unable to find wwn login"
-                       " (s_id %x:%x:%x), trying to create it manually\n",
-                       vha->vp_idx, s_id[0], s_id[1], s_id[2]);
-
-               if (atio->u.raw.entry_count > 1) {
-                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf023,
-                               "Dropping multy entry cmd %p\n", cmd);
-                       goto out_term;
-               }
-
-               mutex_lock(&vha->vha_tgt.tgt_mutex);
-               sess = qlt_make_local_sess(vha, s_id);
-               /* sess has an extra creation ref. */
-               mutex_unlock(&vha->vha_tgt.tgt_mutex);
-
-               if (!sess)
-                       goto out_term;
-       }
-
-       cmd->sess = sess;
-       cmd->loop_id = sess->loop_id;
-       cmd->conf_compl_supported = sess->conf_compl_supported;
-
        cdb = &atio->u.isp24.fcp_cmnd.cdb[0];
        cmd->tag = atio->u.isp24.exchange_addr;
        cmd->unpacked_lun = scsilun_to_int(
@@ -3153,8 +3128,8 @@ static void qlt_do_work(struct work_struct *work)
                cmd, &cmd->se_cmd, cmd->unpacked_lun, cmd->tag, data_length,
                cmd->atio.u.isp24.fcp_hdr.ox_id);
 
-       ret = vha->hw->tgt.tgt_ops->handle_cmd(vha, cmd, cdb, data_length,
-           fcp_task_attr, data_dir, bidi);
+       ret = ha->tgt.tgt_ops->handle_cmd(vha, cmd, cdb, data_length,
+                                         fcp_task_attr, data_dir, bidi);
        if (ret != 0)
                goto out_term;
        /*
@@ -3173,17 +3148,114 @@ out_term:
         */
        spin_lock_irqsave(&ha->hardware_lock, flags);
        qlt_send_term_exchange(vha, NULL, &cmd->atio, 1);
-       kmem_cache_free(qla_tgt_cmd_cachep, cmd);
-       if (sess)
+       percpu_ida_free(&sess->se_sess->sess_tag_pool, cmd->se_cmd.map_tag);
+       ha->tgt.tgt_ops->put_sess(sess);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+static void qlt_do_work(struct work_struct *work)
+{
+       struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work);
+
+       __qlt_do_work(cmd);
+}
+
+static struct qla_tgt_cmd *qlt_get_tag(scsi_qla_host_t *vha,
+                                      struct qla_tgt_sess *sess,
+                                      struct atio_from_isp *atio)
+{
+       struct se_session *se_sess = sess->se_sess;
+       struct qla_tgt_cmd *cmd;
+       int tag;
+
+       tag = percpu_ida_alloc(&se_sess->sess_tag_pool, TASK_RUNNING);
+       if (tag < 0)
+               return NULL;
+
+       cmd = &((struct qla_tgt_cmd *)se_sess->sess_cmd_map)[tag];
+       memset(cmd, 0, sizeof(struct qla_tgt_cmd));
+
+       memcpy(&cmd->atio, atio, sizeof(*atio));
+       cmd->state = QLA_TGT_STATE_NEW;
+       cmd->tgt = vha->vha_tgt.qla_tgt;
+       cmd->vha = vha;
+       cmd->se_cmd.map_tag = tag;
+       cmd->sess = sess;
+       cmd->loop_id = sess->loop_id;
+       cmd->conf_compl_supported = sess->conf_compl_supported;
+
+       return cmd;
+}
+
+static void qlt_send_busy(struct scsi_qla_host *, struct atio_from_isp *,
+                         uint16_t);
+
+static void qlt_create_sess_from_atio(struct work_struct *work)
+{
+       struct qla_tgt_sess_op *op = container_of(work,
+                                       struct qla_tgt_sess_op, work);
+       scsi_qla_host_t *vha = op->vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_sess *sess;
+       struct qla_tgt_cmd *cmd;
+       unsigned long flags;
+       uint8_t *s_id = op->atio.u.isp24.fcp_hdr.s_id;
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf022,
+               "qla_target(%d): Unable to find wwn login"
+               " (s_id %x:%x:%x), trying to create it manually\n",
+               vha->vp_idx, s_id[0], s_id[1], s_id[2]);
+
+       if (op->atio.u.raw.entry_count > 1) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf023,
+                       "Dropping multy entry atio %p\n", &op->atio);
+               goto out_term;
+       }
+
+       mutex_lock(&vha->vha_tgt.tgt_mutex);
+       sess = qlt_make_local_sess(vha, s_id);
+       /* sess has an extra creation ref. */
+       mutex_unlock(&vha->vha_tgt.tgt_mutex);
+
+       if (!sess)
+               goto out_term;
+       /*
+        * Now obtain a pre-allocated session tag using the original op->atio
+        * packet header, and dispatch into __qlt_do_work() using the existing
+        * process context.
+        */
+       cmd = qlt_get_tag(vha, sess, &op->atio);
+       if (!cmd) {
+               spin_lock_irqsave(&ha->hardware_lock, flags);
+               qlt_send_busy(vha, &op->atio, SAM_STAT_BUSY);
                ha->tgt.tgt_ops->put_sess(sess);
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+               kfree(op);
+               return;
+       }
+       /*
+        * __qlt_do_work() will call ha->tgt.tgt_ops->put_sess() to release
+        * the extra reference taken above by qlt_make_local_sess()
+        */
+       __qlt_do_work(cmd);
+       kfree(op);
+       return;
+
+out_term:
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       qlt_send_term_exchange(vha, NULL, &op->atio, 1);
        spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       kfree(op);
+
 }
 
 /* ha->hardware_lock supposed to be held on entry */
 static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha,
        struct atio_from_isp *atio)
 {
+       struct qla_hw_data *ha = vha->hw;
        struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
+       struct qla_tgt_sess *sess;
        struct qla_tgt_cmd *cmd;
 
        if (unlikely(tgt->tgt_stop)) {
@@ -3192,18 +3264,31 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha,
                return -EFAULT;
        }
 
-       cmd = kmem_cache_zalloc(qla_tgt_cmd_cachep, GFP_ATOMIC);
+       sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, atio->u.isp24.fcp_hdr.s_id);
+       if (unlikely(!sess)) {
+               struct qla_tgt_sess_op *op = kzalloc(sizeof(struct qla_tgt_sess_op),
+                                                    GFP_ATOMIC);
+               if (!op)
+                       return -ENOMEM;
+
+               memcpy(&op->atio, atio, sizeof(*atio));
+               INIT_WORK(&op->work, qlt_create_sess_from_atio);
+               queue_work(qla_tgt_wq, &op->work);
+               return 0;
+       }
+       /*
+        * Do kref_get() before returning + dropping qla_hw_data->hardware_lock.
+        */
+       kref_get(&sess->se_sess->sess_kref);
+
+       cmd = qlt_get_tag(vha, sess, atio);
        if (!cmd) {
                ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05e,
                    "qla_target(%d): Allocation of cmd failed\n", vha->vp_idx);
+               ha->tgt.tgt_ops->put_sess(sess);
                return -ENOMEM;
        }
 
-       memcpy(&cmd->atio, atio, sizeof(*atio));
-       cmd->state = QLA_TGT_STATE_NEW;
-       cmd->tgt = vha->vha_tgt.qla_tgt;
-       cmd->vha = vha;
-
        INIT_WORK(&cmd->work, qlt_do_work);
        queue_work(qla_tgt_wq, &cmd->work);
        return 0;
@@ -5501,23 +5586,13 @@ int __init qlt_init(void)
        if (!QLA_TGT_MODE_ENABLED())
                return 0;
 
-       qla_tgt_cmd_cachep = kmem_cache_create("qla_tgt_cmd_cachep",
-           sizeof(struct qla_tgt_cmd), __alignof__(struct qla_tgt_cmd), 0,
-           NULL);
-       if (!qla_tgt_cmd_cachep) {
-               ql_log(ql_log_fatal, NULL, 0xe06c,
-                   "kmem_cache_create for qla_tgt_cmd_cachep failed\n");
-               return -ENOMEM;
-       }
-
        qla_tgt_mgmt_cmd_cachep = kmem_cache_create("qla_tgt_mgmt_cmd_cachep",
            sizeof(struct qla_tgt_mgmt_cmd), __alignof__(struct
            qla_tgt_mgmt_cmd), 0, NULL);
        if (!qla_tgt_mgmt_cmd_cachep) {
                ql_log(ql_log_fatal, NULL, 0xe06d,
                    "kmem_cache_create for qla_tgt_mgmt_cmd_cachep failed\n");
-               ret = -ENOMEM;
-               goto out;
+               return -ENOMEM;
        }
 
        qla_tgt_mgmt_cmd_mempool = mempool_create(25, mempool_alloc_slab,
@@ -5545,8 +5620,6 @@ out_cmd_mempool:
        mempool_destroy(qla_tgt_mgmt_cmd_mempool);
 out_mgmt_cmd_cachep:
        kmem_cache_destroy(qla_tgt_mgmt_cmd_cachep);
-out:
-       kmem_cache_destroy(qla_tgt_cmd_cachep);
        return ret;
 }
 
@@ -5558,5 +5631,4 @@ void qlt_exit(void)
        destroy_workqueue(qla_tgt_wq);
        mempool_destroy(qla_tgt_mgmt_cmd_mempool);
        kmem_cache_destroy(qla_tgt_mgmt_cmd_cachep);
-       kmem_cache_destroy(qla_tgt_cmd_cachep);
 }
index f873e10451d29758ffd65b87a7b400af301e38c7..e0a58fd13f66d2a348230b8456183382839fa692 100644 (file)
@@ -316,7 +316,7 @@ struct fcp_hdr {
        uint8_t  seq_id;
        uint8_t  df_ctl;
        uint16_t seq_cnt;
-       uint16_t ox_id;
+       __be16   ox_id;
        uint16_t rx_id;
        uint32_t parameter;
 } __packed;
@@ -441,7 +441,7 @@ struct ctio7_to_24xx {
        union {
                struct {
                        uint16_t reserved1;
-                       uint16_t flags;
+                       __le16 flags;
                        uint32_t residual;
                        uint16_t ox_id;
                        uint16_t scsi_status;
@@ -527,7 +527,7 @@ struct ctio_crc2_to_fw {
 
        uint32_t handle;                /* System handle. */
        uint16_t nport_handle;          /* N_PORT handle. */
-       uint16_t timeout;               /* Command timeout. */
+       __le16 timeout;         /* Command timeout. */
 
        uint16_t dseg_count;            /* Data segment count. */
        uint8_t  vp_index;
@@ -538,15 +538,15 @@ struct ctio_crc2_to_fw {
        uint8_t  reserved1;
        uint32_t exchange_addr;         /* rcv exchange address */
        uint16_t reserved2;
-       uint16_t flags;                 /* refer to CTIO7 flags values */
+       __le16 flags;                   /* refer to CTIO7 flags values */
        uint32_t residual;
-       uint16_t ox_id;
+       __le16 ox_id;
        uint16_t scsi_status;
-       uint32_t relative_offset;
+       __le32 relative_offset;
        uint32_t reserved5;
-       uint32_t transfer_length;               /* total fc transfer length */
+       __le32 transfer_length;         /* total fc transfer length */
        uint32_t reserved6;
-       uint32_t crc_context_address[2];/* Data segment address. */
+       __le32 crc_context_address[2];/* Data segment address. */
        uint16_t crc_context_len;       /* Data segment length. */
        uint16_t reserved_1;            /* MUST be set to 0. */
 } __packed;
@@ -870,6 +870,12 @@ struct qla_tgt {
        struct list_head tgt_list_entry;
 };
 
+struct qla_tgt_sess_op {
+       struct scsi_qla_host *vha;
+       struct atio_from_isp atio;
+       struct work_struct work;
+};
+
 /*
  * Equivilant to IT Nexus (Initiator-Target)
  */
index 896cb23adb77f2e0fcb32de955a4c1c1ec5c035a..e2beab962096cd10d18cf1df63920c79fad216b3 100644 (file)
@@ -1501,6 +1501,8 @@ static int tcm_qla2xxx_check_initiator_node_acl(
        struct qla_tgt_sess *sess = qla_tgt_sess;
        unsigned char port_name[36];
        unsigned long flags;
+       int num_tags = (ha->fw_xcb_count) ? ha->fw_xcb_count :
+                      TCM_QLA2XXX_DEFAULT_TAGS;
 
        lport = vha->vha_tgt.target_lport_ptr;
        if (!lport) {
@@ -1518,7 +1520,9 @@ static int tcm_qla2xxx_check_initiator_node_acl(
        }
        se_tpg = &tpg->se_tpg;
 
-       se_sess = transport_init_session(TARGET_PROT_NORMAL);
+       se_sess = transport_init_session_tags(num_tags,
+                                             sizeof(struct qla_tgt_cmd),
+                                             TARGET_PROT_NORMAL);
        if (IS_ERR(se_sess)) {
                pr_err("Unable to initialize struct se_session\n");
                return PTR_ERR(se_sess);
index 33aaac8c7d5936bbdd3e2d9f934f2c92f2083331..10c002145648cfb2deeda14c0d83568025ec5bef 100644 (file)
@@ -4,6 +4,11 @@
 #define TCM_QLA2XXX_VERSION    "v0.1"
 /* length of ASCII WWPNs including pad */
 #define TCM_QLA2XXX_NAMELEN    32
+/*
+ * Number of pre-allocated per-session tags, based upon the worst-case
+ * per port number of iocbs
+ */
+#define TCM_QLA2XXX_DEFAULT_TAGS 2088
 
 #include "qla_target.h"
 
index d4727b3394749bcc6ab4265a19c20f83c5328bea..89ee5929eb6de4060536e89885aba5f13f19577c 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/virtio_config.h>
 #include <linux/virtio_scsi.h>
 #include <linux/cpu.h>
+#include <linux/blkdev.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_cmnd.h>
@@ -37,6 +38,7 @@ struct virtio_scsi_cmd {
        struct completion *comp;
        union {
                struct virtio_scsi_cmd_req       cmd;
+               struct virtio_scsi_cmd_req_pi    cmd_pi;
                struct virtio_scsi_ctrl_tmf_req  tmf;
                struct virtio_scsi_ctrl_an_req   an;
        } req;
@@ -393,14 +395,13 @@ static void virtscsi_event_done(struct virtqueue *vq)
  * @cmd                : command structure
  * @req_size   : size of the request buffer
  * @resp_size  : size of the response buffer
- * @gfp        : flags to use for memory allocations
  */
 static int virtscsi_add_cmd(struct virtqueue *vq,
                            struct virtio_scsi_cmd *cmd,
-                           size_t req_size, size_t resp_size, gfp_t gfp)
+                           size_t req_size, size_t resp_size)
 {
        struct scsi_cmnd *sc = cmd->sc;
-       struct scatterlist *sgs[4], req, resp;
+       struct scatterlist *sgs[6], req, resp;
        struct sg_table *out, *in;
        unsigned out_num = 0, in_num = 0;
 
@@ -418,30 +419,38 @@ static int virtscsi_add_cmd(struct virtqueue *vq,
        sgs[out_num++] = &req;
 
        /* Data-out buffer.  */
-       if (out)
+       if (out) {
+               /* Place WRITE protection SGLs before Data OUT payload */
+               if (scsi_prot_sg_count(sc))
+                       sgs[out_num++] = scsi_prot_sglist(sc);
                sgs[out_num++] = out->sgl;
+       }
 
        /* Response header.  */
        sg_init_one(&resp, &cmd->resp, resp_size);
        sgs[out_num + in_num++] = &resp;
 
        /* Data-in buffer */
-       if (in)
+       if (in) {
+               /* Place READ protection SGLs before Data IN payload */
+               if (scsi_prot_sg_count(sc))
+                       sgs[out_num + in_num++] = scsi_prot_sglist(sc);
                sgs[out_num + in_num++] = in->sgl;
+       }
 
-       return virtqueue_add_sgs(vq, sgs, out_num, in_num, cmd, gfp);
+       return virtqueue_add_sgs(vq, sgs, out_num, in_num, cmd, GFP_ATOMIC);
 }
 
 static int virtscsi_kick_cmd(struct virtio_scsi_vq *vq,
                             struct virtio_scsi_cmd *cmd,
-                            size_t req_size, size_t resp_size, gfp_t gfp)
+                            size_t req_size, size_t resp_size)
 {
        unsigned long flags;
        int err;
        bool needs_kick = false;
 
        spin_lock_irqsave(&vq->vq_lock, flags);
-       err = virtscsi_add_cmd(vq->vq, cmd, req_size, resp_size, gfp);
+       err = virtscsi_add_cmd(vq->vq, cmd, req_size, resp_size);
        if (!err)
                needs_kick = virtqueue_kick_prepare(vq->vq);
 
@@ -452,12 +461,45 @@ static int virtscsi_kick_cmd(struct virtio_scsi_vq *vq,
        return err;
 }
 
+static void virtio_scsi_init_hdr(struct virtio_scsi_cmd_req *cmd,
+                                struct scsi_cmnd *sc)
+{
+       cmd->lun[0] = 1;
+       cmd->lun[1] = sc->device->id;
+       cmd->lun[2] = (sc->device->lun >> 8) | 0x40;
+       cmd->lun[3] = sc->device->lun & 0xff;
+       cmd->tag = (unsigned long)sc;
+       cmd->task_attr = VIRTIO_SCSI_S_SIMPLE;
+       cmd->prio = 0;
+       cmd->crn = 0;
+}
+
+static void virtio_scsi_init_hdr_pi(struct virtio_scsi_cmd_req_pi *cmd_pi,
+                                   struct scsi_cmnd *sc)
+{
+       struct request *rq = sc->request;
+       struct blk_integrity *bi;
+
+       virtio_scsi_init_hdr((struct virtio_scsi_cmd_req *)cmd_pi, sc);
+
+       if (!rq || !scsi_prot_sg_count(sc))
+               return;
+
+       bi = blk_get_integrity(rq->rq_disk);
+
+       if (sc->sc_data_direction == DMA_TO_DEVICE)
+               cmd_pi->pi_bytesout = blk_rq_sectors(rq) * bi->tuple_size;
+       else if (sc->sc_data_direction == DMA_FROM_DEVICE)
+               cmd_pi->pi_bytesin = blk_rq_sectors(rq) * bi->tuple_size;
+}
+
 static int virtscsi_queuecommand(struct virtio_scsi *vscsi,
                                 struct virtio_scsi_vq *req_vq,
                                 struct scsi_cmnd *sc)
 {
        struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev);
        struct virtio_scsi_cmd *cmd = scsi_cmd_priv(sc);
+       int req_size;
 
        BUG_ON(scsi_sg_count(sc) > shost->sg_tablesize);
 
@@ -469,23 +511,20 @@ static int virtscsi_queuecommand(struct virtio_scsi *vscsi,
 
        memset(cmd, 0, sizeof(*cmd));
        cmd->sc = sc;
-       cmd->req.cmd = (struct virtio_scsi_cmd_req){
-               .lun[0] = 1,
-               .lun[1] = sc->device->id,
-               .lun[2] = (sc->device->lun >> 8) | 0x40,
-               .lun[3] = sc->device->lun & 0xff,
-               .tag = (unsigned long)sc,
-               .task_attr = VIRTIO_SCSI_S_SIMPLE,
-               .prio = 0,
-               .crn = 0,
-       };
 
        BUG_ON(sc->cmd_len > VIRTIO_SCSI_CDB_SIZE);
-       memcpy(cmd->req.cmd.cdb, sc->cmnd, sc->cmd_len);
 
-       if (virtscsi_kick_cmd(req_vq, cmd,
-                             sizeof cmd->req.cmd, sizeof cmd->resp.cmd,
-                             GFP_ATOMIC) != 0)
+       if (virtio_has_feature(vscsi->vdev, VIRTIO_SCSI_F_T10_PI)) {
+               virtio_scsi_init_hdr_pi(&cmd->req.cmd_pi, sc);
+               memcpy(cmd->req.cmd_pi.cdb, sc->cmnd, sc->cmd_len);
+               req_size = sizeof(cmd->req.cmd_pi);
+       } else {
+               virtio_scsi_init_hdr(&cmd->req.cmd, sc);
+               memcpy(cmd->req.cmd.cdb, sc->cmnd, sc->cmd_len);
+               req_size = sizeof(cmd->req.cmd);
+       }
+
+       if (virtscsi_kick_cmd(req_vq, cmd, req_size, sizeof(cmd->resp.cmd)) != 0)
                return SCSI_MLQUEUE_HOST_BUSY;
        return 0;
 }
@@ -542,8 +581,7 @@ static int virtscsi_tmf(struct virtio_scsi *vscsi, struct virtio_scsi_cmd *cmd)
 
        cmd->comp = &comp;
        if (virtscsi_kick_cmd(&vscsi->ctrl_vq, cmd,
-                             sizeof cmd->req.tmf, sizeof cmd->resp.tmf,
-                             GFP_NOIO) < 0)
+                             sizeof cmd->req.tmf, sizeof cmd->resp.tmf) < 0)
                goto out;
 
        wait_for_completion(&comp);
@@ -823,7 +861,7 @@ static int virtscsi_probe(struct virtio_device *vdev)
 {
        struct Scsi_Host *shost;
        struct virtio_scsi *vscsi;
-       int err;
+       int err, host_prot;
        u32 sg_elems, num_targets;
        u32 cmd_per_lun;
        u32 num_queues;
@@ -873,6 +911,16 @@ static int virtscsi_probe(struct virtio_device *vdev)
        shost->max_id = num_targets;
        shost->max_channel = 0;
        shost->max_cmd_len = VIRTIO_SCSI_CDB_SIZE;
+
+       if (virtio_has_feature(vdev, VIRTIO_SCSI_F_T10_PI)) {
+               host_prot = SHOST_DIF_TYPE1_PROTECTION | SHOST_DIF_TYPE2_PROTECTION |
+                           SHOST_DIF_TYPE3_PROTECTION | SHOST_DIX_TYPE1_PROTECTION |
+                           SHOST_DIX_TYPE2_PROTECTION | SHOST_DIX_TYPE3_PROTECTION;
+
+               scsi_host_set_prot(shost, host_prot);
+               scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC);
+       }
+
        err = scsi_add_host(shost, &vdev->dev);
        if (err)
                goto scsi_add_host_failed;
@@ -942,6 +990,7 @@ static struct virtio_device_id id_table[] = {
 static unsigned int features[] = {
        VIRTIO_SCSI_F_HOTPLUG,
        VIRTIO_SCSI_F_CHANGE,
+       VIRTIO_SCSI_F_T10_PI,
 };
 
 static struct virtio_driver virtio_scsi_driver = {
index 0901ef5d6e8ad4af2868c9f70b76d37513bfbfab..08356b6955a4dc4cb618fc2ff4de1f9169adad2f 100644 (file)
@@ -4605,7 +4605,7 @@ static int et131x_pci_setup(struct pci_dev *pdev,
        netdev->netdev_ops     = &et131x_netdev_ops;
 
        SET_NETDEV_DEV(netdev, &pdev->dev);
-       SET_ETHTOOL_OPS(netdev, &et131x_ethtool_ops);
+       netdev->ethtool_ops = &et131x_ethtool_ops;
 
        adapter = et131x_adapter_init(netdev, pdev);
 
index d6421b9b5981c2265a1c796a03ade4ec5416e878..a6158bef58e54c63dc19ebfcb3ef89daf2b0874e 100644 (file)
@@ -2249,7 +2249,7 @@ struct net_device *init_ft1000_card(struct pcmcia_device *link,
 
        ft1000InitProc(dev);
        ft1000_card_present = 1;
-       SET_ETHTOOL_OPS(dev, &ops);
+       dev->ethtool_ops = &ops;
        printk(KERN_INFO "ft1000: %s: addr 0x%04lx irq %d, MAC addr %pM\n",
                        dev->name, dev->base_addr, dev->irq, dev->dev_addr);
        return dev;
index c6e8ba7b3e4eaa26297c778101eb1063bebc60ce..82fb758a29bce2e84be2e52265b3adcddfc3310a 100644 (file)
@@ -39,19 +39,10 @@ config DRM_IMX_LDB
          Choose this to enable the internal LVDS Display Bridge (LDB)
          found on i.MX53 and i.MX6 processors.
 
-config DRM_IMX_IPUV3_CORE
-       tristate "IPUv3 core support"
-       depends on DRM_IMX
-       depends on RESET_CONTROLLER
-       help
-         Choose this if you have a i.MX5/6 system and want
-         to use the IPU. This option only enables IPU base
-         support.
-
 config DRM_IMX_IPUV3
        tristate "DRM Support for i.MX IPUv3"
        depends on DRM_IMX
-       depends on DRM_IMX_IPUV3_CORE
+       depends on IMX_IPUV3_CORE
        help
          Choose this if you have a i.MX5 or i.MX6 processor.
 
index 129e3a3f59f100847231a90e2d8b7740ce4f4ce0..582c438d8cbdb941e4fb76ffcb55978cda079828 100644 (file)
@@ -6,7 +6,6 @@ obj-$(CONFIG_DRM_IMX) += imxdrm.o
 obj-$(CONFIG_DRM_IMX_PARALLEL_DISPLAY) += parallel-display.o
 obj-$(CONFIG_DRM_IMX_TVE) += imx-tve.o
 obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o
-obj-$(CONFIG_DRM_IMX_IPUV3_CORE) += ipu-v3/
 
 imx-ipuv3-crtc-objs  := ipuv3-crtc.o ipuv3-plane.o
 obj-$(CONFIG_DRM_IMX_IPUV3)    += imx-ipuv3-crtc.o
index c270c9ae6d27711d531ebb54a07b1cdc125be9da..def8280d7ee664ba949ae2b2157d1d6bfefddd23 100644 (file)
@@ -200,13 +200,6 @@ static const struct file_operations imx_drm_driver_fops = {
        .llseek = noop_llseek,
 };
 
-int imx_drm_connector_mode_valid(struct drm_connector *connector,
-       struct drm_display_mode *mode)
-{
-       return MODE_OK;
-}
-EXPORT_SYMBOL(imx_drm_connector_mode_valid);
-
 void imx_drm_connector_destroy(struct drm_connector *connector)
 {
        drm_sysfs_connector_remove(connector);
@@ -305,7 +298,7 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags)
                        dev_err(drm->dev,
                                "[CONNECTOR:%d:%s] drm_sysfs_connector_add failed: %d\n",
                                connector->base.id,
-                               drm_get_connector_name(connector), ret);
+                               connector->name, ret);
                        goto err_unbind;
                }
        }
index a322bac55414fb0242e65417befb469a672083c6..7453ae00c412fbc25a90f6ca783603784edbbae4 100644 (file)
@@ -50,8 +50,6 @@ int imx_drm_encoder_get_mux_id(struct device_node *node,
 int imx_drm_encoder_parse_of(struct drm_device *drm,
        struct drm_encoder *encoder, struct device_node *np);
 
-int imx_drm_connector_mode_valid(struct drm_connector *connector,
-       struct drm_display_mode *mode);
 void imx_drm_connector_destroy(struct drm_connector *connector);
 void imx_drm_encoder_destroy(struct drm_encoder *encoder);
 
index 1b440483f28f1e95599c6bcb0fd81611f7734af4..18c9ccd460b7f6a6fef3a085abe48998f3eb68ed 100644 (file)
@@ -27,8 +27,8 @@
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_encoder_slave.h>
+#include <video/imx-ipu-v3.h>
 
-#include "ipu-v3/imx-ipu-v3.h"
 #include "imx-hdmi.h"
 #include "imx-drm.h"
 
@@ -1490,7 +1490,6 @@ static struct drm_connector_funcs imx_hdmi_connector_funcs = {
 
 static struct drm_connector_helper_funcs imx_hdmi_connector_helper_funcs = {
        .get_modes = imx_hdmi_connector_get_modes,
-       .mode_valid = imx_drm_connector_mode_valid,
        .best_encoder = imx_hdmi_connector_best_encoder,
 };
 
index fe4c1ef4e7a509afda8f5d3587f1f8eadad6ea42..7e3f019d7e72543c40dd11299a051383a1a57224 100644 (file)
@@ -317,7 +317,6 @@ static struct drm_connector_funcs imx_ldb_connector_funcs = {
 static struct drm_connector_helper_funcs imx_ldb_connector_helper_funcs = {
        .get_modes = imx_ldb_connector_get_modes,
        .best_encoder = imx_ldb_connector_best_encoder,
-       .mode_valid = imx_drm_connector_mode_valid,
 };
 
 static struct drm_encoder_funcs imx_ldb_encoder_funcs = {
index a23f4f773146a8891925165622cb5167d30252df..c628fcdc22aef31d80844e72e5198a27ecd98aa9 100644 (file)
@@ -30,8 +30,8 @@
 #include <drm/drmP.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_crtc_helper.h>
+#include <video/imx-ipu-v3.h>
 
-#include "ipu-v3/imx-ipu-v3.h"
 #include "imx-drm.h"
 
 #define TVE_COM_CONF_REG       0x00
@@ -249,11 +249,6 @@ static int imx_tve_connector_mode_valid(struct drm_connector *connector,
 {
        struct imx_tve *tve = con_to_tve(connector);
        unsigned long rate;
-       int ret;
-
-       ret = imx_drm_connector_mode_valid(connector, mode);
-       if (ret != MODE_OK)
-               return ret;
 
        /* pixel clock with 2x oversampling */
        rate = clk_round_rate(tve->clk, 2000UL * mode->clock) / 2000;
index 47bec5e17358658adee84f4b0a53eafd43c1a405..720868bff35bb9368d6ddad7cee8ca735a101c5b 100644 (file)
@@ -30,7 +30,7 @@
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 
-#include "ipu-v3/imx-ipu-v3.h"
+#include <video/imx-ipu-v3.h>
 #include "imx-drm.h"
 #include "ipuv3-plane.h"
 
index 5697e59ddf1dc01b1da0aaf9f0f161b4bf71cfdc..6f393a11f44d2d57fa074defc77ce048796b36cc 100644 (file)
@@ -17,7 +17,7 @@
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 
-#include "ipu-v3/imx-ipu-v3.h"
+#include "video/imx-ipu-v3.h"
 #include "ipuv3-plane.h"
 
 #define to_ipu_plane(x)        container_of(x, struct ipu_plane, base)
index eaf4dda1a0c442ce53acfe01f181892d7bed4253..b5678328fc40c7c1cea9ba090ec53c47d453d7fd 100644 (file)
@@ -148,7 +148,6 @@ static struct drm_connector_funcs imx_pd_connector_funcs = {
 static struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = {
        .get_modes = imx_pd_connector_get_modes,
        .best_encoder = imx_pd_connector_best_encoder,
-       .mode_valid = imx_drm_connector_mode_valid,
 };
 
 static struct drm_encoder_funcs imx_pd_encoder_funcs = {
index 827209ea6bd0e9685344b845f44b7560fdf5e1fe..386a36c00f572f871bd668f1fa5873a098714e73 100644 (file)
@@ -82,16 +82,7 @@ struct ccc_io {
        /**
         * I/O vector information to or from which read/write is going.
         */
-       struct iovec *cui_iov;
-       unsigned long cui_nrsegs;
-       /**
-        * Total iov count for left IO.
-        */
-       unsigned long cui_tot_nrsegs;
-       /**
-        * Old length for iov that was truncated partially.
-        */
-       size_t cui_iov_olen;
+       struct iov_iter *cui_iter;
        /**
         * Total size for the left IO.
         */
index dc24cfa5803722dd86669102189c27d793b83ecf..1b0c216bc5687742198c89c8d6f21b07036a11bb 100644 (file)
@@ -720,31 +720,12 @@ int ccc_io_one_lock_index(const struct lu_env *env, struct cl_io *io,
 void ccc_io_update_iov(const struct lu_env *env,
                       struct ccc_io *cio, struct cl_io *io)
 {
-       int i;
        size_t size = io->u.ci_rw.crw_count;
 
-       cio->cui_iov_olen = 0;
-       if (!cl_is_normalio(env, io) || cio->cui_tot_nrsegs == 0)
+       if (!cl_is_normalio(env, io) || cio->cui_iter == NULL)
                return;
 
-       for (i = 0; i < cio->cui_tot_nrsegs; i++) {
-               struct iovec *iv = &cio->cui_iov[i];
-
-               if (iv->iov_len < size)
-                       size -= iv->iov_len;
-               else {
-                       if (iv->iov_len > size) {
-                               cio->cui_iov_olen = iv->iov_len;
-                               iv->iov_len = size;
-                       }
-                       break;
-               }
-       }
-
-       cio->cui_nrsegs = i + 1;
-       LASSERTF(cio->cui_tot_nrsegs >= cio->cui_nrsegs,
-                "tot_nrsegs: %lu, nrsegs: %lu\n",
-                cio->cui_tot_nrsegs, cio->cui_nrsegs);
+       iov_iter_truncate(cio->cui_iter, size);
 }
 
 int ccc_io_one_lock(const struct lu_env *env, struct cl_io *io,
@@ -775,30 +756,7 @@ void ccc_io_advance(const struct lu_env *env,
        if (!cl_is_normalio(env, io))
                return;
 
-       LASSERT(cio->cui_tot_nrsegs >= cio->cui_nrsegs);
-       LASSERT(cio->cui_tot_count  >= nob);
-
-       cio->cui_iov    += cio->cui_nrsegs;
-       cio->cui_tot_nrsegs -= cio->cui_nrsegs;
-       cio->cui_tot_count  -= nob;
-
-       /* update the iov */
-       if (cio->cui_iov_olen > 0) {
-               struct iovec *iv;
-
-               cio->cui_iov--;
-               cio->cui_tot_nrsegs++;
-               iv = &cio->cui_iov[0];
-               if (io->ci_continue) {
-                       iv->iov_base += iv->iov_len;
-                       LASSERT(cio->cui_iov_olen > iv->iov_len);
-                       iv->iov_len = cio->cui_iov_olen - iv->iov_len;
-               } else {
-                       /* restore the iov_len, in case of restart io. */
-                       iv->iov_len = cio->cui_iov_olen;
-               }
-               cio->cui_iov_olen = 0;
-       }
+       iov_iter_reexpand(cio->cui_iter, cio->cui_tot_count  -= nob);
 }
 
 /**
index c4ddec2b3589eb743475f022c29d47ce49dbbec8..716e1ee0104f6fe0c2c1323689f216acff807bdc 100644 (file)
@@ -1114,9 +1114,7 @@ restart:
 
                switch (vio->cui_io_subtype) {
                case IO_NORMAL:
-                       cio->cui_iov = args->u.normal.via_iov;
-                       cio->cui_nrsegs = args->u.normal.via_nrsegs;
-                       cio->cui_tot_nrsegs = cio->cui_nrsegs;
+                       cio->cui_iter = args->u.normal.via_iter;
                        cio->cui_iocb = args->u.normal.via_iocb;
                        if ((iot == CIT_WRITE) &&
                            !(cio->cui_fd->fd_flags & LL_FILE_GROUP_LOCKED)) {
@@ -1180,58 +1178,23 @@ out:
        return result;
 }
 
-static ssize_t ll_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
-                               unsigned long nr_segs, loff_t pos)
+static ssize_t ll_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
 {
        struct lu_env      *env;
        struct vvp_io_args *args;
-       size_t        count = 0;
        ssize_t      result;
        int              refcheck;
 
-       result = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE);
-       if (result)
-               return result;
-
        env = cl_env_get(&refcheck);
        if (IS_ERR(env))
                return PTR_ERR(env);
 
        args = vvp_env_args(env, IO_NORMAL);
-       args->u.normal.via_iov = (struct iovec *)iov;
-       args->u.normal.via_nrsegs = nr_segs;
+       args->u.normal.via_iter = to;
        args->u.normal.via_iocb = iocb;
 
        result = ll_file_io_generic(env, args, iocb->ki_filp, CIT_READ,
-                                   &iocb->ki_pos, count);
-       cl_env_put(env, &refcheck);
-       return result;
-}
-
-static ssize_t ll_file_read(struct file *file, char *buf, size_t count,
-                           loff_t *ppos)
-{
-       struct lu_env *env;
-       struct iovec  *local_iov;
-       struct kiocb  *kiocb;
-       ssize_t result;
-       int         refcheck;
-
-       env = cl_env_get(&refcheck);
-       if (IS_ERR(env))
-               return PTR_ERR(env);
-
-       local_iov = &vvp_env_info(env)->vti_local_iov;
-       kiocb = &vvp_env_info(env)->vti_kiocb;
-       local_iov->iov_base = (void __user *)buf;
-       local_iov->iov_len = count;
-       init_sync_kiocb(kiocb, file);
-       kiocb->ki_pos = *ppos;
-       kiocb->ki_nbytes = count;
-
-       result = ll_file_aio_read(kiocb, local_iov, 1, kiocb->ki_pos);
-       *ppos = kiocb->ki_pos;
-
+                                   &iocb->ki_pos, iov_iter_count(to));
        cl_env_put(env, &refcheck);
        return result;
 }
@@ -1239,64 +1202,27 @@ static ssize_t ll_file_read(struct file *file, char *buf, size_t count,
 /*
  * Write to a file (through the page cache).
  */
-static ssize_t ll_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
-                                unsigned long nr_segs, loff_t pos)
+static ssize_t ll_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
 {
        struct lu_env      *env;
        struct vvp_io_args *args;
-       size_t        count = 0;
        ssize_t      result;
        int              refcheck;
 
-       result = generic_segment_checks(iov, &nr_segs, &count, VERIFY_READ);
-       if (result)
-               return result;
-
        env = cl_env_get(&refcheck);
        if (IS_ERR(env))
                return PTR_ERR(env);
 
        args = vvp_env_args(env, IO_NORMAL);
-       args->u.normal.via_iov = (struct iovec *)iov;
-       args->u.normal.via_nrsegs = nr_segs;
+       args->u.normal.via_iter = from;
        args->u.normal.via_iocb = iocb;
 
        result = ll_file_io_generic(env, args, iocb->ki_filp, CIT_WRITE,
-                                 &iocb->ki_pos, count);
+                                 &iocb->ki_pos, iov_iter_count(from));
        cl_env_put(env, &refcheck);
        return result;
 }
 
-static ssize_t ll_file_write(struct file *file, const char *buf, size_t count,
-                            loff_t *ppos)
-{
-       struct lu_env *env;
-       struct iovec  *local_iov;
-       struct kiocb  *kiocb;
-       ssize_t result;
-       int         refcheck;
-
-       env = cl_env_get(&refcheck);
-       if (IS_ERR(env))
-               return PTR_ERR(env);
-
-       local_iov = &vvp_env_info(env)->vti_local_iov;
-       kiocb = &vvp_env_info(env)->vti_kiocb;
-       local_iov->iov_base = (void __user *)buf;
-       local_iov->iov_len = count;
-       init_sync_kiocb(kiocb, file);
-       kiocb->ki_pos = *ppos;
-       kiocb->ki_nbytes = count;
-
-       result = ll_file_aio_write(kiocb, local_iov, 1, kiocb->ki_pos);
-       *ppos = kiocb->ki_pos;
-
-       cl_env_put(env, &refcheck);
-       return result;
-}
-
-
-
 /*
  * Send file content (through pagecache) somewhere with helper
  */
@@ -3143,10 +3069,10 @@ int ll_inode_permission(struct inode *inode, int mask)
 
 /* -o localflock - only provides locally consistent flock locks */
 struct file_operations ll_file_operations = {
-       .read      = ll_file_read,
-       .aio_read = ll_file_aio_read,
-       .write    = ll_file_write,
-       .aio_write = ll_file_aio_write,
+       .read      = new_sync_read,
+       .read_iter = ll_file_read_iter,
+       .write    = new_sync_write,
+       .write_iter = ll_file_write_iter,
        .unlocked_ioctl = ll_file_ioctl,
        .open      = ll_file_open,
        .release        = ll_file_release,
@@ -3158,10 +3084,10 @@ struct file_operations ll_file_operations = {
 };
 
 struct file_operations ll_file_operations_flock = {
-       .read      = ll_file_read,
-       .aio_read    = ll_file_aio_read,
-       .write    = ll_file_write,
-       .aio_write   = ll_file_aio_write,
+       .read      = new_sync_read,
+       .read_iter    = ll_file_read_iter,
+       .write    = new_sync_write,
+       .write_iter   = ll_file_write_iter,
        .unlocked_ioctl = ll_file_ioctl,
        .open      = ll_file_open,
        .release        = ll_file_release,
@@ -3176,10 +3102,10 @@ struct file_operations ll_file_operations_flock = {
 
 /* These are for -o noflock - to return ENOSYS on flock calls */
 struct file_operations ll_file_operations_noflock = {
-       .read      = ll_file_read,
-       .aio_read    = ll_file_aio_read,
-       .write    = ll_file_write,
-       .aio_write   = ll_file_aio_write,
+       .read      = new_sync_read,
+       .read_iter    = ll_file_read_iter,
+       .write    = new_sync_write,
+       .write_iter   = ll_file_write_iter,
        .unlocked_ioctl = ll_file_ioctl,
        .open      = ll_file_open,
        .release        = ll_file_release,
index dde7632ba01fa8dab671fb43995723bba1f5d3dd..140ee947ba4949ea547ac03ebaf9a9efb9e51ab1 100644 (file)
@@ -917,8 +917,7 @@ struct vvp_io_args {
        union {
                struct {
                        struct kiocb      *via_iocb;
-                       struct iovec      *via_iov;
-                       unsigned long      via_nrsegs;
+                       struct iov_iter   *via_iter;
                } normal;
                struct {
                        struct pipe_inode_info  *via_pipe;
index f0122c568a099fbb2ea519ddc4bca5d97a34495a..56162103cc79c2dad9038abb11d715c6e9909f45 100644 (file)
@@ -151,8 +151,7 @@ static struct ll_cl_context *ll_cl_init(struct file *file,
                result = cl_io_rw_init(env, io, CIT_WRITE, pos, PAGE_CACHE_SIZE);
                if (result == 0) {
                        cio->cui_fd = LUSTRE_FPRIVATE(file);
-                       cio->cui_iov = NULL;
-                       cio->cui_nrsegs = 0;
+                       cio->cui_iter = NULL;
                        result = cl_io_iter_init(env, io);
                        if (result == 0) {
                                result = cl_io_lock(env, io);
index 55ca8d3c3e46451b654acdc298333035fcb11e4b..af84c1aaa5f83f6c994a64da4b164535cbe54ac0 100644 (file)
@@ -218,14 +218,11 @@ static void ll_free_user_pages(struct page **pages, int npages, int do_dirty)
        int i;
 
        for (i = 0; i < npages; i++) {
-               if (pages[i] == NULL)
-                       break;
                if (do_dirty)
                        set_page_dirty_lock(pages[i]);
                page_cache_release(pages[i]);
        }
-
-       OBD_FREE_LARGE(pages, npages * sizeof(*pages));
+       kvfree(pages);
 }
 
 ssize_t ll_direct_rw_pages(const struct lu_env *env, struct cl_io *io,
@@ -363,18 +360,16 @@ static ssize_t ll_direct_IO_26_seg(const struct lu_env *env, struct cl_io *io,
 #define MAX_DIO_SIZE ((MAX_MALLOC / sizeof(struct brw_page) * PAGE_CACHE_SIZE) & \
                      ~(DT_MAX_BRW_SIZE - 1))
 static ssize_t ll_direct_IO_26(int rw, struct kiocb *iocb,
-                              const struct iovec *iov, loff_t file_offset,
-                              unsigned long nr_segs)
+                              struct iov_iter *iter, loff_t file_offset)
 {
        struct lu_env *env;
        struct cl_io *io;
        struct file *file = iocb->ki_filp;
        struct inode *inode = file->f_mapping->host;
        struct ccc_object *obj = cl_inode2ccc(inode);
-       long count = iov_length(iov, nr_segs);
-       long tot_bytes = 0, result = 0;
+       ssize_t count = iov_iter_count(iter);
+       ssize_t tot_bytes = 0, result = 0;
        struct ll_inode_info *lli = ll_i2info(inode);
-       unsigned long seg = 0;
        long size = MAX_DIO_SIZE;
        int refcheck;
 
@@ -392,11 +387,8 @@ static ssize_t ll_direct_IO_26(int rw, struct kiocb *iocb,
               MAX_DIO_SIZE >> PAGE_CACHE_SHIFT);
 
        /* Check that all user buffers are aligned as well */
-       for (seg = 0; seg < nr_segs; seg++) {
-               if (((unsigned long)iov[seg].iov_base & ~CFS_PAGE_MASK) ||
-                   (iov[seg].iov_len & ~CFS_PAGE_MASK))
-                       return -EINVAL;
-       }
+       if (iov_iter_alignment(iter) & ~CFS_PAGE_MASK)
+               return -EINVAL;
 
        env = cl_env_get(&refcheck);
        LASSERT(!IS_ERR(env));
@@ -411,63 +403,49 @@ static ssize_t ll_direct_IO_26(int rw, struct kiocb *iocb,
                mutex_lock(&inode->i_mutex);
 
        LASSERT(obj->cob_transient_pages == 0);
-       for (seg = 0; seg < nr_segs; seg++) {
-               long iov_left = iov[seg].iov_len;
-               unsigned long user_addr = (unsigned long)iov[seg].iov_base;
+       while (iov_iter_count(iter)) {
+               struct page **pages;
+               size_t offs;
 
+               count = min_t(size_t, iov_iter_count(iter), size);
                if (rw == READ) {
                        if (file_offset >= i_size_read(inode))
                                break;
-                       if (file_offset + iov_left > i_size_read(inode))
-                               iov_left = i_size_read(inode) - file_offset;
+                       if (file_offset + count > i_size_read(inode))
+                               count = i_size_read(inode) - file_offset;
                }
 
-               while (iov_left > 0) {
-                       struct page **pages;
-                       int page_count, max_pages = 0;
-                       long bytes;
-
-                       bytes = min(size, iov_left);
-                       page_count = ll_get_user_pages(rw, user_addr, bytes,
-                                                      &pages, &max_pages);
-                       if (likely(page_count > 0)) {
-                               if (unlikely(page_count <  max_pages))
-                                       bytes = page_count << PAGE_CACHE_SHIFT;
-                               result = ll_direct_IO_26_seg(env, io, rw, inode,
-                                                            file->f_mapping,
-                                                            bytes, file_offset,
-                                                            pages, page_count);
-                               ll_free_user_pages(pages, max_pages, rw==READ);
-                       } else if (page_count == 0) {
-                               GOTO(out, result = -EFAULT);
-                       } else {
-                               result = page_count;
-                       }
-                       if (unlikely(result <= 0)) {
-                               /* If we can't allocate a large enough buffer
-                                * for the request, shrink it to a smaller
-                                * PAGE_SIZE multiple and try again.
-                                * We should always be able to kmalloc for a
-                                * page worth of page pointers = 4MB on i386. */
-                               if (result == -ENOMEM &&
-                                   size > (PAGE_CACHE_SIZE / sizeof(*pages)) *
-                                          PAGE_CACHE_SIZE) {
-                                       size = ((((size / 2) - 1) |
-                                                ~CFS_PAGE_MASK) + 1) &
-                                               CFS_PAGE_MASK;
-                                       CDEBUG(D_VFSTRACE,"DIO size now %lu\n",
-                                              size);
-                                       continue;
-                               }
-
-                               GOTO(out, result);
+               result = iov_iter_get_pages_alloc(iter, &pages, count, &offs);
+               if (likely(result > 0)) {
+                       int n = (result + offs + PAGE_SIZE - 1) / PAGE_SIZE;
+                       result = ll_direct_IO_26_seg(env, io, rw, inode,
+                                                    file->f_mapping,
+                                                    result, file_offset,
+                                                    pages, n);
+                       ll_free_user_pages(pages, n, rw==READ);
+               }
+               if (unlikely(result <= 0)) {
+                       /* If we can't allocate a large enough buffer
+                        * for the request, shrink it to a smaller
+                        * PAGE_SIZE multiple and try again.
+                        * We should always be able to kmalloc for a
+                        * page worth of page pointers = 4MB on i386. */
+                       if (result == -ENOMEM &&
+                           size > (PAGE_CACHE_SIZE / sizeof(*pages)) *
+                                  PAGE_CACHE_SIZE) {
+                               size = ((((size / 2) - 1) |
+                                        ~CFS_PAGE_MASK) + 1) &
+                                       CFS_PAGE_MASK;
+                               CDEBUG(D_VFSTRACE,"DIO size now %lu\n",
+                                      size);
+                               continue;
                        }
 
-                       tot_bytes += result;
-                       file_offset += result;
-                       iov_left -= result;
-                       user_addr += result;
+                       GOTO(out, result);
                }
+               iov_iter_advance(iter, result);
+               tot_bytes += result;
+               file_offset += result;
        }
 out:
        LASSERT(obj->cob_transient_pages == 0);
index 7dd2b4723c5fd6fdded98fadbcb63a68ce8c80fa..0e0b404cb5e6cc3b33dc8b736675485a617cdd55 100644 (file)
@@ -211,27 +211,26 @@ static int vvp_mmap_locks(const struct lu_env *env,
        struct cl_lock_descr   *descr = &cti->cti_descr;
        ldlm_policy_data_t      policy;
        unsigned long      addr;
-       unsigned long      seg;
        ssize_t          count;
        int                  result;
+       struct iov_iter i;
+       struct iovec iov;
 
        LASSERT(io->ci_type == CIT_READ || io->ci_type == CIT_WRITE);
 
        if (!cl_is_normalio(env, io))
                return 0;
 
-       if (vio->cui_iov == NULL) /* nfs or loop back device write */
+       if (vio->cui_iter == NULL) /* nfs or loop back device write */
                return 0;
 
        /* No MM (e.g. NFS)? No vmas too. */
        if (mm == NULL)
                return 0;
 
-       for (seg = 0; seg < vio->cui_nrsegs; seg++) {
-               const struct iovec *iv = &vio->cui_iov[seg];
-
-               addr = (unsigned long)iv->iov_base;
-               count = iv->iov_len;
+       iov_for_each(iov, i, *(vio->cui_iter)) {
+               addr = (unsigned long)iov.iov_base;
+               count = iov.iov_len;
                if (count == 0)
                        continue;
 
@@ -527,9 +526,7 @@ static int vvp_io_read_start(const struct lu_env *env,
        switch (vio->cui_io_subtype) {
        case IO_NORMAL:
                LASSERT(cio->cui_iocb->ki_pos == pos);
-               result = generic_file_aio_read(cio->cui_iocb,
-                                              cio->cui_iov, cio->cui_nrsegs,
-                                              cio->cui_iocb->ki_pos);
+               result = generic_file_read_iter(cio->cui_iocb, cio->cui_iter);
                break;
        case IO_SPLICE:
                result = generic_file_splice_read(file, &pos,
@@ -595,12 +592,11 @@ static int vvp_io_write_start(const struct lu_env *env,
 
        CDEBUG(D_VFSTRACE, "write: [%lli, %lli)\n", pos, pos + (long long)cnt);
 
-       if (cio->cui_iov == NULL) /* from a temp io in ll_cl_init(). */
+       if (cio->cui_iter == NULL) /* from a temp io in ll_cl_init(). */
                result = 0;
        else
-               result = generic_file_aio_write(cio->cui_iocb,
-                                               cio->cui_iov, cio->cui_nrsegs,
-                                               cio->cui_iocb->ki_pos);
+               result = generic_file_write_iter(cio->cui_iocb, cio->cui_iter);
+
        if (result > 0) {
                if (result < cnt)
                        io->ci_continue = 0;
@@ -1162,10 +1158,9 @@ int vvp_io_init(const struct lu_env *env, struct cl_object *obj,
                 *  results."  -- Single Unix Spec */
                if (count == 0)
                        result = 1;
-               else {
+               else
                        cio->cui_tot_count = count;
-                       cio->cui_tot_nrsegs = 0;
-               }
+
                /* for read/write, we store the jobid in the inode, and
                 * it'll be fetched by osc when building RPC.
                 *
index ded31ea6bd39545382d4d64b30cc523c18bb1d1f..cbf455d66f73e2942f5218450e367f2d65102752 100644 (file)
@@ -396,7 +396,7 @@ static void iss_video_buf_queue(struct vb2_buffer *vb)
        }
 }
 
-static struct vb2_ops iss_video_vb2ops = {
+static const struct vb2_ops iss_video_vb2ops = {
        .queue_setup    = iss_video_queue_setup,
        .buf_prepare    = iss_video_buf_prepare,
        .buf_queue      = iss_video_buf_queue,
index 75d7c63cb41328e8f88a86fdbf93ee091a147abd..e320d6bae913ed44be09d219e30af34e84f64dc2 100644 (file)
@@ -1067,7 +1067,7 @@ static int xlr_net_probe(struct platform_device *pdev)
        xlr_set_rx_mode(ndev);
 
        priv->num_rx_desc += MAX_NUM_DESC_SPILL;
-       SET_ETHTOOL_OPS(ndev, &xlr_ethtool_ops);
+       ndev->ethtool_ops = &xlr_ethtool_ops;
        SET_NETDEV_DEV(ndev, &pdev->dev);
 
        /* Common registers, do one time initialization */
index ff7214aac9dd6ed05c524c69fa23e35767d2dcce..da9dd6bc56600f2fe094df6a59d3836ad491be72 100644 (file)
@@ -469,7 +469,7 @@ int cvm_oct_common_init(struct net_device *dev)
 
        /* We do our own locking, Linux doesn't need to */
        dev->features |= NETIF_F_LLTX;
-       SET_ETHTOOL_OPS(dev, &cvm_oct_ethtool_ops);
+       dev->ethtool_ops = &cvm_oct_ethtool_ops;
 
        cvm_oct_phy_setup_device(dev);
        cvm_oct_set_mac_filter(dev);
index 76ea356163b68c0c41000980b98a30e6f95ca009..7f6accd59986772717d99d0b05f14c8a1adaaa94 100644 (file)
@@ -322,7 +322,7 @@ static void _rtl_add_wowlan_patterns(struct ieee80211_hw *hw,
        struct rtl_mac *mac = &(rtlpriv->mac80211);
        struct cfg80211_pkt_pattern *patterns = wow->patterns;
        struct rtl_wow_pattern rtl_pattern;
-       u8 *pattern_os, *mask_os;
+       const u8 *pattern_os, *mask_os;
        u8 mask[MAX_WOL_BIT_MASK_SIZE] = {0};
        u8 content[MAX_WOL_PATTERN_SIZE] = {0};
        u8 broadcast_addr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
@@ -1561,7 +1561,7 @@ static void rtl_op_rfkill_poll(struct ieee80211_hw *hw)
  * before switch channle or power save, or tx buffer packet
  * maybe send after offchannel or rf sleep, this may cause
  * dis-association by AP */
-static void rtl_op_flush(struct ieee80211_hw *hw,
+static void rtl_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                         u32 queues, bool drop)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
index 0c9f5cebfb4271f059ff4eefa701dd96de466f77..f0839f6a9345073a23f5b2db4d4f613a071e9369 100644 (file)
@@ -1227,7 +1227,7 @@ static int cfg80211_rtw_set_default_key(struct wiphy *wiphy,
 
 static int cfg80211_rtw_get_station(struct wiphy *wiphy,
                                    struct net_device *ndev,
-                                   u8 *mac, struct station_info *sinfo)
+                                   const u8 *mac, struct station_info *sinfo)
 {
        int ret = 0;
        struct rtw_adapter *padapter = wiphy_to_adapter(wiphy);
@@ -2903,7 +2903,7 @@ static int cfg80211_rtw_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
 }
 
 static int cfg80211_rtw_add_station(struct wiphy *wiphy,
-                                   struct net_device *ndev, u8 *mac,
+                                   struct net_device *ndev, const u8 *mac,
                                    struct station_parameters *params)
 {
        DBG_8723A("%s(%s)\n", __func__, ndev->name);
@@ -2912,7 +2912,7 @@ static int cfg80211_rtw_add_station(struct wiphy *wiphy,
 }
 
 static int cfg80211_rtw_del_station(struct wiphy *wiphy,
-                                   struct net_device *ndev, u8 *mac)
+                                   struct net_device *ndev, const u8 *mac)
 {
        int ret = 0;
        struct list_head *phead, *plist, *ptmp;
@@ -2988,7 +2988,7 @@ static int cfg80211_rtw_del_station(struct wiphy *wiphy,
 }
 
 static int cfg80211_rtw_change_station(struct wiphy *wiphy,
-                                      struct net_device *ndev, u8 *mac,
+                                      struct net_device *ndev, const u8 *mac,
                                       struct station_parameters *params)
 {
        DBG_8723A("%s(%s)\n", __func__, ndev->name);
index 9a37408708f4a84fcf5c4da2afb53fe5a4b8325c..046be2ce9c1a8a12070de43f8b489d5fae7defe4 100644 (file)
@@ -1278,7 +1278,9 @@ static void rtl_op_rfkill_poll(struct ieee80211_hw *hw)
  * before switch channel or power save, or tx buffer packet
  * maybe send after offchannel or rf sleep, this may cause
  * dis-association by AP */
-static void rtl_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static void rtl_op_flush(struct ieee80211_hw *hw,
+                        struct ieee80211_vif *vif,
+                        u32 queues, bool drop)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
 
index f76f95c29617b3b7058a1999b7896d4e3d8a875a..723319ee08f39af6a7a27b21de6f8467742717cb 100644 (file)
@@ -84,7 +84,7 @@ static int prism2_domibset_uint32(wlandevice_t *wlandev, u32 did, u32 data)
 }
 
 static int prism2_domibset_pstr32(wlandevice_t *wlandev,
-                                 u32 did, u8 len, u8 *data)
+                                 u32 did, u8 len, const u8 *data)
 {
        struct p80211msg_dot11req_mibset msg;
        p80211item_pstr32_t *mibitem =
@@ -298,7 +298,7 @@ static int prism2_set_default_key(struct wiphy *wiphy, struct net_device *dev,
 
 
 static int prism2_get_station(struct wiphy *wiphy, struct net_device *dev,
-                             u8 *mac, struct station_info *sinfo)
+                             const u8 *mac, struct station_info *sinfo)
 {
        wlandevice_t *wlandev = dev->ml_priv;
        struct p80211msg_lnxreq_commsquality quality;
index 9189bc0a87aef18df10bf2c0003d2c1e4c0bc65a..5663f4d19d028120ef51efb689dbff39d47e9e4c 100644 (file)
@@ -300,7 +300,7 @@ bool iscsit_check_np_match(
                port = ntohs(sock_in->sin_port);
        }
 
-       if ((ip_match == true) && (np->np_port == port) &&
+       if (ip_match && (np->np_port == port) &&
            (np->np_network_transport == network_transport))
                return true;
 
@@ -325,7 +325,7 @@ static struct iscsi_np *iscsit_get_np(
                }
 
                match = iscsit_check_np_match(sockaddr, np, network_transport);
-               if (match == true) {
+               if (match) {
                        /*
                         * Increment the np_exports reference count now to
                         * prevent iscsit_del_np() below from being called
@@ -1121,7 +1121,7 @@ iscsit_get_immediate_data(struct iscsi_cmd *cmd, struct iscsi_scsi_req *hdr,
        /*
         * Special case for Unsupported SAM WRITE Opcodes and ImmediateData=Yes.
         */
-       if (dump_payload == true)
+       if (dump_payload)
                goto after_immediate_data;
 
        immed_ret = iscsit_handle_immediate_data(cmd, hdr,
@@ -3390,7 +3390,9 @@ static bool iscsit_check_inaddr_any(struct iscsi_np *np)
 
 #define SENDTARGETS_BUF_LIMIT 32768U
 
-static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd)
+static int
+iscsit_build_sendtargets_response(struct iscsi_cmd *cmd,
+                                 enum iscsit_transport_type network_transport)
 {
        char *payload = NULL;
        struct iscsi_conn *conn = cmd->conn;
@@ -3467,6 +3469,9 @@ static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd)
                                struct iscsi_np *np = tpg_np->tpg_np;
                                bool inaddr_any = iscsit_check_inaddr_any(np);
 
+                               if (np->np_network_transport != network_transport)
+                                       continue;
+
                                if (!target_name_printed) {
                                        len = sprintf(buf, "TargetName=%s",
                                                      tiqn->tiqn);
@@ -3485,10 +3490,8 @@ static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd)
 
                                len = sprintf(buf, "TargetAddress="
                                        "%s:%hu,%hu",
-                                       (inaddr_any == false) ?
-                                               np->np_ip : conn->local_ip,
-                                       (inaddr_any == false) ?
-                                               np->np_port : conn->local_port,
+                                       inaddr_any ? conn->local_ip : np->np_ip,
+                                       inaddr_any ? conn->local_port : np->np_port,
                                        tpg->tpgt);
                                len += 1;
 
@@ -3520,11 +3523,12 @@ eob:
 
 int
 iscsit_build_text_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
-                     struct iscsi_text_rsp *hdr)
+                     struct iscsi_text_rsp *hdr,
+                     enum iscsit_transport_type network_transport)
 {
        int text_length, padding;
 
-       text_length = iscsit_build_sendtargets_response(cmd);
+       text_length = iscsit_build_sendtargets_response(cmd, network_transport);
        if (text_length < 0)
                return text_length;
 
@@ -3562,7 +3566,7 @@ static int iscsit_send_text_rsp(
        u32 tx_size = 0;
        int text_length, iov_count = 0, rc;
 
-       rc = iscsit_build_text_rsp(cmd, conn, hdr);
+       rc = iscsit_build_text_rsp(cmd, conn, hdr, ISCSI_TCP);
        if (rc < 0)
                return rc;
 
@@ -4234,8 +4238,6 @@ int iscsit_close_connection(
        if (conn->conn_transport->iscsit_wait_conn)
                conn->conn_transport->iscsit_wait_conn(conn);
 
-       iscsit_free_queue_reqs_for_conn(conn);
-
        /*
         * During Connection recovery drop unacknowledged out of order
         * commands for this connection, and prepare the other commands
@@ -4252,6 +4254,7 @@ int iscsit_close_connection(
                iscsit_clear_ooo_cmdsns_for_conn(conn);
                iscsit_release_commands_from_conn(conn);
        }
+       iscsit_free_queue_reqs_for_conn(conn);
 
        /*
         * Handle decrementing session or connection usage count if
index de77d9aa22c6329b41d515a157e49d54451edf50..19b842c3e0b39222e125c04f165fccbcf68f338d 100644 (file)
@@ -71,6 +71,40 @@ static void chap_gen_challenge(
                        challenge_asciihex);
 }
 
+static int chap_check_algorithm(const char *a_str)
+{
+       char *tmp, *orig, *token;
+
+       tmp = kstrdup(a_str, GFP_KERNEL);
+       if (!tmp) {
+               pr_err("Memory allocation failed for CHAP_A temporary buffer\n");
+               return CHAP_DIGEST_UNKNOWN;
+       }
+       orig = tmp;
+
+       token = strsep(&tmp, "=");
+       if (!token)
+               goto out;
+
+       if (strcmp(token, "CHAP_A")) {
+               pr_err("Unable to locate CHAP_A key\n");
+               goto out;
+       }
+       while (token) {
+               token = strsep(&tmp, ",");
+               if (!token)
+                       goto out;
+
+               if (!strncmp(token, "5", 1)) {
+                       pr_debug("Selected MD5 Algorithm\n");
+                       kfree(orig);
+                       return CHAP_DIGEST_MD5;
+               }
+       }
+out:
+       kfree(orig);
+       return CHAP_DIGEST_UNKNOWN;
+}
 
 static struct iscsi_chap *chap_server_open(
        struct iscsi_conn *conn,
@@ -79,6 +113,7 @@ static struct iscsi_chap *chap_server_open(
        char *aic_str,
        unsigned int *aic_len)
 {
+       int ret;
        struct iscsi_chap *chap;
 
        if (!(auth->naf_flags & NAF_USERID_SET) ||
@@ -93,21 +128,24 @@ static struct iscsi_chap *chap_server_open(
                return NULL;
 
        chap = conn->auth_protocol;
-       /*
-        * We only support MD5 MDA presently.
-        */
-       if (strncmp(a_str, "CHAP_A=5", 8)) {
-               pr_err("CHAP_A is not MD5.\n");
+       ret = chap_check_algorithm(a_str);
+       switch (ret) {
+       case CHAP_DIGEST_MD5:
+               pr_debug("[server] Got CHAP_A=5\n");
+               /*
+                * Send back CHAP_A set to MD5.
+               */
+               *aic_len = sprintf(aic_str, "CHAP_A=5");
+               *aic_len += 1;
+               chap->digest_type = CHAP_DIGEST_MD5;
+               pr_debug("[server] Sending CHAP_A=%d\n", chap->digest_type);
+               break;
+       case CHAP_DIGEST_UNKNOWN:
+       default:
+               pr_err("Unsupported CHAP_A value\n");
                return NULL;
        }
-       pr_debug("[server] Got CHAP_A=5\n");
-       /*
-        * Send back CHAP_A set to MD5.
-        */
-       *aic_len = sprintf(aic_str, "CHAP_A=5");
-       *aic_len += 1;
-       chap->digest_type = CHAP_DIGEST_MD5;
-       pr_debug("[server] Sending CHAP_A=%d\n", chap->digest_type);
+
        /*
         * Set Identifier.
         */
@@ -313,6 +351,16 @@ static int chap_server_compute_md5(
                pr_err("Unable to convert incoming challenge\n");
                goto out;
        }
+       /*
+        * During mutual authentication, the CHAP_C generated by the
+        * initiator must not match the original CHAP_C generated by
+        * the target.
+        */
+       if (!memcmp(challenge_binhex, chap->challenge, CHAP_CHALLENGE_LENGTH)) {
+               pr_err("initiator CHAP_C matches target CHAP_C, failing"
+                      " login attempt\n");
+               goto out;
+       }
        /*
         * Generate CHAP_N and CHAP_R for mutual authentication.
         */
index 2f463c09626d9232df18fd391daf716440ee543a..d22f7b96a06ca98aa3bd83f669d92eb686cfcec9 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef _ISCSI_CHAP_H_
 #define _ISCSI_CHAP_H_
 
+#define CHAP_DIGEST_UNKNOWN    0
 #define CHAP_DIGEST_MD5                5
 #define CHAP_DIGEST_SHA                6
 
index d9b1d88e1ad382f07ff8d64c5374b3caaac3a3ce..fecb69535a1583abe6f70f663f1ecd18142c555f 100644 (file)
@@ -1145,7 +1145,7 @@ iscsit_conn_set_transport(struct iscsi_conn *conn, struct iscsit_transport *t)
 void iscsi_target_login_sess_out(struct iscsi_conn *conn,
                struct iscsi_np *np, bool zero_tsih, bool new_sess)
 {
-       if (new_sess == false)
+       if (!new_sess)
                goto old_sess_out;
 
        pr_err("iSCSI Login negotiation failed.\n");
index 75b685960e80d31e2d6e19439a98257fea41ab15..62a095f36bf2f78b77d2a9b75cea99b9c2f14ecb 100644 (file)
@@ -404,7 +404,7 @@ static void iscsi_target_sk_data_ready(struct sock *sk)
        }
 
        rc = schedule_delayed_work(&conn->login_work, 0);
-       if (rc == false) {
+       if (!rc) {
                pr_debug("iscsi_target_sk_data_ready, schedule_delayed_work"
                         " got false\n");
        }
@@ -513,7 +513,7 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
        state = (tpg->tpg_state == TPG_STATE_ACTIVE);
        spin_unlock(&tpg->tpg_state_lock);
 
-       if (state == false) {
+       if (!state) {
                pr_debug("iscsi_target_do_login_rx: tpg_state != TPG_STATE_ACTIVE\n");
                iscsi_target_restore_sock_callbacks(conn);
                iscsi_target_login_drop(conn, login);
@@ -528,7 +528,7 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
                state = iscsi_target_sk_state_check(sk);
                read_unlock_bh(&sk->sk_callback_lock);
 
-               if (state == false) {
+               if (!state) {
                        pr_debug("iscsi_target_do_login_rx, TCP state CLOSE\n");
                        iscsi_target_restore_sock_callbacks(conn);
                        iscsi_target_login_drop(conn, login);
@@ -773,6 +773,12 @@ static int iscsi_target_handle_csg_zero(
                }
 
                goto do_auth;
+       } else if (!payload_length) {
+               pr_err("Initiator sent zero length security payload,"
+                      " login failed\n");
+               iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+                                   ISCSI_LOGIN_STATUS_AUTH_FAILED);
+               return -1;
        }
 
        if (login->first_request)
index 4d2e23fc76fda72be2b7ec59c801f0696a8470e3..02f9de26f38ab930b9810a20abaf8b972f42f93d 100644 (file)
@@ -474,10 +474,10 @@ int iscsi_set_keys_to_negotiate(
                if (!strcmp(param->name, AUTHMETHOD)) {
                        SET_PSTATE_NEGOTIATE(param);
                } else if (!strcmp(param->name, HEADERDIGEST)) {
-                       if (iser == false)
+                       if (!iser)
                                SET_PSTATE_NEGOTIATE(param);
                } else if (!strcmp(param->name, DATADIGEST)) {
-                       if (iser == false)
+                       if (!iser)
                                SET_PSTATE_NEGOTIATE(param);
                } else if (!strcmp(param->name, MAXCONNECTIONS)) {
                        SET_PSTATE_NEGOTIATE(param);
@@ -497,7 +497,7 @@ int iscsi_set_keys_to_negotiate(
                } else if (!strcmp(param->name, IMMEDIATEDATA)) {
                        SET_PSTATE_NEGOTIATE(param);
                } else if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) {
-                       if (iser == false)
+                       if (!iser)
                                SET_PSTATE_NEGOTIATE(param);
                } else if (!strcmp(param->name, MAXXMITDATASEGMENTLENGTH)) {
                        continue;
@@ -528,13 +528,13 @@ int iscsi_set_keys_to_negotiate(
                } else if (!strcmp(param->name, OFMARKINT)) {
                        SET_PSTATE_NEGOTIATE(param);
                } else if (!strcmp(param->name, RDMAEXTENSIONS)) {
-                       if (iser == true)
+                       if (iser)
                                SET_PSTATE_NEGOTIATE(param);
                } else if (!strcmp(param->name, INITIATORRECVDATASEGMENTLENGTH)) {
-                       if (iser == true)
+                       if (iser)
                                SET_PSTATE_NEGOTIATE(param);
                } else if (!strcmp(param->name, TARGETRECVDATASEGMENTLENGTH)) {
-                       if (iser == true)
+                       if (iser)
                                SET_PSTATE_NEGOTIATE(param);
                }
        }
@@ -1605,7 +1605,7 @@ int iscsi_decode_text_input(
 
        tmpbuf = kzalloc(length + 1, GFP_KERNEL);
        if (!tmpbuf) {
-               pr_err("Unable to allocate memory for tmpbuf.\n");
+               pr_err("Unable to allocate %u + 1 bytes for tmpbuf.\n", length);
                return -1;
        }
 
index 1431e8400d28b41c4ca21fc0563a8e3df1b7828a..c3cb5c15efdaa4fe1e5ea2c4a90cd6eb0e167a15 100644 (file)
@@ -189,7 +189,7 @@ static void iscsit_clear_tpg_np_login_thread(
        iscsit_reset_np_thread(tpg_np->tpg_np, tpg_np, tpg, shutdown);
 }
 
-void iscsit_clear_tpg_np_login_threads(
+static void iscsit_clear_tpg_np_login_threads(
        struct iscsi_portal_group *tpg,
        bool shutdown)
 {
@@ -276,8 +276,6 @@ int iscsit_tpg_del_portal_group(
        tpg->tpg_state = TPG_STATE_INACTIVE;
        spin_unlock(&tpg->tpg_state_lock);
 
-       iscsit_clear_tpg_np_login_threads(tpg, true);
-
        if (iscsit_release_sessions_for_tpg(tpg, force) < 0) {
                pr_err("Unable to delete iSCSI Target Portal Group:"
                        " %hu while active sessions exist, and force=0\n",
@@ -453,7 +451,7 @@ static bool iscsit_tpg_check_network_portal(
 
                        match = iscsit_check_np_match(sockaddr, np,
                                                network_transport);
-                       if (match == true)
+                       if (match)
                                break;
                }
                spin_unlock(&tpg->tpg_np_lock);
@@ -475,7 +473,7 @@ struct iscsi_tpg_np *iscsit_tpg_add_network_portal(
 
        if (!tpg_np_parent) {
                if (iscsit_tpg_check_network_portal(tpg->tpg_tiqn, sockaddr,
-                               network_transport) == true) {
+                               network_transport)) {
                        pr_err("Network Portal: %s already exists on a"
                                " different TPG on %s\n", ip_str,
                                tpg->tpg_tiqn->tiqn);
index 0a182f2aa8a25ea07cad39ac58a0d954010abd8b..e7265337bc43c1f4cf789d4e4c980d8fdbd6add6 100644 (file)
@@ -8,7 +8,6 @@ extern struct iscsi_portal_group *iscsit_get_tpg_from_np(struct iscsi_tiqn *,
                        struct iscsi_np *, struct iscsi_tpg_np **);
 extern int iscsit_get_tpg(struct iscsi_portal_group *);
 extern void iscsit_put_tpg(struct iscsi_portal_group *);
-extern void iscsit_clear_tpg_np_login_threads(struct iscsi_portal_group *, bool);
 extern void iscsit_tpg_dump_params(struct iscsi_portal_group *);
 extern int iscsit_tpg_add_portal_group(struct iscsi_tiqn *, struct iscsi_portal_group *);
 extern int iscsit_tpg_del_portal_group(struct iscsi_tiqn *, struct iscsi_portal_group *,
index 73ab75ddaf42e9b3fc8e378cf3ae3669d1e040e8..6d2f37578b29cc0509d3898b7910bb35cd9d484c 100644 (file)
@@ -179,7 +179,7 @@ static void tcm_loop_submission_work(struct work_struct *work)
        struct tcm_loop_hba *tl_hba;
        struct tcm_loop_tpg *tl_tpg;
        struct scatterlist *sgl_bidi = NULL;
-       u32 sgl_bidi_count = 0;
+       u32 sgl_bidi_count = 0, transfer_length;
        int rc;
 
        tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host);
@@ -213,12 +213,21 @@ static void tcm_loop_submission_work(struct work_struct *work)
 
        }
 
-       if (!scsi_prot_sg_count(sc) && scsi_get_prot_op(sc) != SCSI_PROT_NORMAL)
+       transfer_length = scsi_transfer_length(sc);
+       if (!scsi_prot_sg_count(sc) &&
+           scsi_get_prot_op(sc) != SCSI_PROT_NORMAL) {
                se_cmd->prot_pto = true;
+               /*
+                * loopback transport doesn't support
+                * WRITE_GENERATE, READ_STRIP protection
+                * information operations, go ahead unprotected.
+                */
+               transfer_length = scsi_bufflen(sc);
+       }
 
        rc = target_submit_cmd_map_sgls(se_cmd, tl_nexus->se_sess, sc->cmnd,
                        &tl_cmd->tl_sense_buf[0], tl_cmd->sc->device->lun,
-                       scsi_bufflen(sc), tcm_loop_sam_attr(sc),
+                       transfer_length, tcm_loop_sam_attr(sc),
                        sc->sc_data_direction, 0,
                        scsi_sglist(sc), scsi_sg_count(sc),
                        sgl_bidi, sgl_bidi_count,
index e0229592ec5509656aed292970af719be1f9111d..bd78d9235ac645678aeaacc3a564c1c4c13c5742 100644 (file)
@@ -81,7 +81,7 @@ sbc_emulate_readcapacity(struct se_cmd *cmd)
                transport_kunmap_data_sg(cmd);
        }
 
-       target_complete_cmd(cmd, GOOD);
+       target_complete_cmd_with_length(cmd, GOOD, 8);
        return 0;
 }
 
@@ -137,7 +137,7 @@ sbc_emulate_readcapacity_16(struct se_cmd *cmd)
                transport_kunmap_data_sg(cmd);
        }
 
-       target_complete_cmd(cmd, GOOD);
+       target_complete_cmd_with_length(cmd, GOOD, 32);
        return 0;
 }
 
@@ -176,24 +176,6 @@ static inline u32 sbc_get_size(struct se_cmd *cmd, u32 sectors)
        return cmd->se_dev->dev_attrib.block_size * sectors;
 }
 
-static int sbc_check_valid_sectors(struct se_cmd *cmd)
-{
-       struct se_device *dev = cmd->se_dev;
-       unsigned long long end_lba;
-       u32 sectors;
-
-       sectors = cmd->data_length / dev->dev_attrib.block_size;
-       end_lba = dev->transport->get_blocks(dev) + 1;
-
-       if (cmd->t_task_lba + sectors > end_lba) {
-               pr_err("target: lba %llu, sectors %u exceeds end lba %llu\n",
-                       cmd->t_task_lba, sectors, end_lba);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
 static inline u32 transport_get_sectors_6(unsigned char *cdb)
 {
        /*
@@ -665,8 +647,19 @@ sbc_check_prot(struct se_device *dev, struct se_cmd *cmd, unsigned char *cdb,
 
        cmd->prot_type = dev->dev_attrib.pi_prot_type;
        cmd->prot_length = dev->prot_length * sectors;
-       pr_debug("%s: prot_type=%d, prot_length=%d prot_op=%d prot_checks=%d\n",
-                __func__, cmd->prot_type, cmd->prot_length,
+
+       /**
+        * In case protection information exists over the wire
+        * we modify command data length to describe pure data.
+        * The actual transfer length is data length + protection
+        * length
+        **/
+       if (protect)
+               cmd->data_length = sectors * dev->dev_attrib.block_size;
+
+       pr_debug("%s: prot_type=%d, data_length=%d, prot_length=%d "
+                "prot_op=%d prot_checks=%d\n",
+                __func__, cmd->prot_type, cmd->data_length, cmd->prot_length,
                 cmd->prot_op, cmd->prot_checks);
 
        return true;
@@ -877,15 +870,6 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                break;
        case SYNCHRONIZE_CACHE:
        case SYNCHRONIZE_CACHE_16:
-               if (!ops->execute_sync_cache) {
-                       size = 0;
-                       cmd->execute_cmd = sbc_emulate_noop;
-                       break;
-               }
-
-               /*
-                * Extract LBA and range to be flushed for emulated SYNCHRONIZE_CACHE
-                */
                if (cdb[0] == SYNCHRONIZE_CACHE) {
                        sectors = transport_get_sectors_10(cdb);
                        cmd->t_task_lba = transport_lba_32(cdb);
@@ -893,18 +877,12 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                        sectors = transport_get_sectors_16(cdb);
                        cmd->t_task_lba = transport_lba_64(cdb);
                }
-
-               size = sbc_get_size(cmd, sectors);
-
-               /*
-                * Check to ensure that LBA + Range does not exceed past end of
-                * device for IBLOCK and FILEIO ->do_sync_cache() backend calls
-                */
-               if (cmd->t_task_lba || sectors) {
-                       if (sbc_check_valid_sectors(cmd) < 0)
-                               return TCM_ADDRESS_OUT_OF_RANGE;
+               if (ops->execute_sync_cache) {
+                       cmd->execute_cmd = ops->execute_sync_cache;
+                       goto check_lba;
                }
-               cmd->execute_cmd = ops->execute_sync_cache;
+               size = 0;
+               cmd->execute_cmd = sbc_emulate_noop;
                break;
        case UNMAP:
                if (!ops->execute_unmap)
@@ -947,8 +925,10 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                break;
        case VERIFY:
                size = 0;
+               sectors = transport_get_sectors_10(cdb);
+               cmd->t_task_lba = transport_lba_32(cdb);
                cmd->execute_cmd = sbc_emulate_noop;
-               break;
+               goto check_lba;
        case REZERO_UNIT:
        case SEEK_6:
        case SEEK_10:
@@ -988,7 +968,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                                dev->dev_attrib.hw_max_sectors);
                        return TCM_INVALID_CDB_FIELD;
                }
-
+check_lba:
                end_lba = dev->transport->get_blocks(dev) + 1;
                if (cmd->t_task_lba + sectors > end_lba) {
                        pr_err("cmd exceeds last lba %llu "
index 8653666612a802f5cbfcde2c4d82ac7d20ca9294..6cd7222738fc4697f8c1be7c337ef7418fd09075 100644 (file)
@@ -129,15 +129,10 @@ static sense_reason_t
 spc_emulate_evpd_80(struct se_cmd *cmd, unsigned char *buf)
 {
        struct se_device *dev = cmd->se_dev;
-       u16 len = 0;
+       u16 len;
 
        if (dev->dev_flags & DF_EMULATED_VPD_UNIT_SERIAL) {
-               u32 unit_serial_len;
-
-               unit_serial_len = strlen(dev->t10_wwn.unit_serial);
-               unit_serial_len++; /* For NULL Terminator */
-
-               len += sprintf(&buf[4], "%s", dev->t10_wwn.unit_serial);
+               len = sprintf(&buf[4], "%s", dev->t10_wwn.unit_serial);
                len++; /* Extra Byte for NULL Terminator */
                buf[3] = len;
        }
@@ -721,6 +716,7 @@ spc_emulate_inquiry(struct se_cmd *cmd)
        unsigned char *buf;
        sense_reason_t ret;
        int p;
+       int len = 0;
 
        buf = kzalloc(SE_INQUIRY_BUF, GFP_KERNEL);
        if (!buf) {
@@ -742,6 +738,7 @@ spc_emulate_inquiry(struct se_cmd *cmd)
                }
 
                ret = spc_emulate_inquiry_std(cmd, buf);
+               len = buf[4] + 5;
                goto out;
        }
 
@@ -749,6 +746,7 @@ spc_emulate_inquiry(struct se_cmd *cmd)
                if (cdb[2] == evpd_handlers[p].page) {
                        buf[1] = cdb[2];
                        ret = evpd_handlers[p].emulate(cmd, buf);
+                       len = get_unaligned_be16(&buf[2]) + 4;
                        goto out;
                }
        }
@@ -765,7 +763,7 @@ out:
        kfree(buf);
 
        if (!ret)
-               target_complete_cmd(cmd, GOOD);
+               target_complete_cmd_with_length(cmd, GOOD, len);
        return ret;
 }
 
@@ -1103,7 +1101,7 @@ set_length:
                transport_kunmap_data_sg(cmd);
        }
 
-       target_complete_cmd(cmd, GOOD);
+       target_complete_cmd_with_length(cmd, GOOD, length);
        return 0;
 }
 
@@ -1279,7 +1277,7 @@ done:
        buf[3] = (lun_count & 0xff);
        transport_kunmap_data_sg(cmd);
 
-       target_complete_cmd(cmd, GOOD);
+       target_complete_cmd_with_length(cmd, GOOD, 8 + lun_count * 8);
        return 0;
 }
 EXPORT_SYMBOL(spc_emulate_report_luns);
index 2179feed0d63aa83017dff321ddc86c11e8432d4..7fa62fc93e0b52d70ac49c67f5455a139935f55c 100644 (file)
@@ -504,7 +504,7 @@ void transport_deregister_session(struct se_session *se_sess)
         * ->acl_free_comp caller to wakeup configfs se_node_acl->acl_group
         * removal context.
         */
-       if (se_nacl && comp_nacl == true)
+       if (se_nacl && comp_nacl)
                target_put_nacl(se_nacl);
 
        transport_free_session(se_sess);
@@ -562,7 +562,7 @@ static int transport_cmd_check_stop(struct se_cmd *cmd, bool remove_from_lists,
 
                spin_unlock_irqrestore(&cmd->t_state_lock, flags);
 
-               complete(&cmd->t_transport_stop_comp);
+               complete_all(&cmd->t_transport_stop_comp);
                return 1;
        }
 
@@ -687,7 +687,7 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
        if (cmd->transport_state & CMD_T_ABORTED &&
            cmd->transport_state & CMD_T_STOP) {
                spin_unlock_irqrestore(&cmd->t_state_lock, flags);
-               complete(&cmd->t_transport_stop_comp);
+               complete_all(&cmd->t_transport_stop_comp);
                return;
        } else if (!success) {
                INIT_WORK(&cmd->work, target_complete_failure_work);
@@ -703,6 +703,23 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
 }
 EXPORT_SYMBOL(target_complete_cmd);
 
+void target_complete_cmd_with_length(struct se_cmd *cmd, u8 scsi_status, int length)
+{
+       if (scsi_status == SAM_STAT_GOOD && length < cmd->data_length) {
+               if (cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) {
+                       cmd->residual_count += cmd->data_length - length;
+               } else {
+                       cmd->se_cmd_flags |= SCF_UNDERFLOW_BIT;
+                       cmd->residual_count = cmd->data_length - length;
+               }
+
+               cmd->data_length = length;
+       }
+
+       target_complete_cmd(cmd, scsi_status);
+}
+EXPORT_SYMBOL(target_complete_cmd_with_length);
+
 static void target_add_to_state_list(struct se_cmd *cmd)
 {
        struct se_device *dev = cmd->se_dev;
@@ -1761,7 +1778,7 @@ void target_execute_cmd(struct se_cmd *cmd)
                        cmd->se_tfo->get_task_tag(cmd));
 
                spin_unlock_irq(&cmd->t_state_lock);
-               complete(&cmd->t_transport_stop_comp);
+               complete_all(&cmd->t_transport_stop_comp);
                return;
        }
 
@@ -2363,7 +2380,7 @@ int target_get_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd,
         * fabric acknowledgement that requires two target_put_sess_cmd()
         * invocations before se_cmd descriptor release.
         */
-       if (ack_kref == true) {
+       if (ack_kref) {
                kref_get(&se_cmd->cmd_kref);
                se_cmd->se_cmd_flags |= SCF_ACK_KREF;
        }
@@ -2407,6 +2424,10 @@ static void target_release_cmd_kref(struct kref *kref)
  */
 int target_put_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd)
 {
+       if (!se_sess) {
+               se_cmd->se_tfo->release_cmd(se_cmd);
+               return 1;
+       }
        return kref_put_spinlock_irqsave(&se_cmd->cmd_kref, target_release_cmd_kref,
                        &se_sess->sess_cmd_lock);
 }
@@ -2934,6 +2955,12 @@ static void target_tmr_work(struct work_struct *work)
 int transport_generic_handle_tmr(
        struct se_cmd *cmd)
 {
+       unsigned long flags;
+
+       spin_lock_irqsave(&cmd->t_state_lock, flags);
+       cmd->transport_state |= CMD_T_ACTIVE;
+       spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+
        INIT_WORK(&cmd->work, target_tmr_work);
        queue_work(cmd->se_dev->tmr_wq, &cmd->work);
        return 0;
index 669c536fd959575da69816e4a8714b5f78a362c7..e9186cdf35e962d12abe59429370306579e9a25d 100644 (file)
@@ -70,7 +70,7 @@ static int target_xcopy_locate_se_dev_e4(struct se_cmd *se_cmd, struct xcopy_op
        unsigned char tmp_dev_wwn[XCOPY_NAA_IEEE_REGEX_LEN], *dev_wwn;
        int rc;
 
-       if (src == true)
+       if (src)
                dev_wwn = &xop->dst_tid_wwn[0];
        else
                dev_wwn = &xop->src_tid_wwn[0];
@@ -88,7 +88,7 @@ static int target_xcopy_locate_se_dev_e4(struct se_cmd *se_cmd, struct xcopy_op
                if (rc != 0)
                        continue;
 
-               if (src == true) {
+               if (src) {
                        xop->dst_dev = se_dev;
                        pr_debug("XCOPY 0xe4: Setting xop->dst_dev: %p from located"
                                " se_dev\n", xop->dst_dev);
@@ -166,7 +166,7 @@ static int target_xcopy_parse_tiddesc_e4(struct se_cmd *se_cmd, struct xcopy_op
                return -EINVAL;
        }
 
-       if (src == true) {
+       if (src) {
                memcpy(&xop->src_tid_wwn[0], &desc[8], XCOPY_NAA_IEEE_REGEX_LEN);
                /*
                 * Determine if the source designator matches the local device
@@ -236,7 +236,7 @@ static int target_xcopy_parse_target_descriptors(struct se_cmd *se_cmd,
                        /*
                         * Assume target descriptors are in source -> destination order..
                         */
-                       if (src == true)
+                       if (src)
                                src = false;
                        else
                                src = true;
@@ -560,7 +560,7 @@ static int target_xcopy_init_pt_lun(
         * reservations.  The pt_cmd->se_lun pointer will be setup from within
         * target_xcopy_setup_pt_port()
         */
-       if (remote_port == false) {
+       if (!remote_port) {
                pt_cmd->se_cmd_flags |= SCF_SE_LUN_CMD | SCF_CMD_XCOPY_PASSTHROUGH;
                return 0;
        }
index f5fd515b2bee266dd9c8279ea804bc7372d955f3..be0c0d08c56a91ff9acc97e8db92868c39c25f0c 100644 (file)
@@ -128,6 +128,7 @@ int ft_queue_status(struct se_cmd *se_cmd)
        struct fc_lport *lport;
        struct fc_exch *ep;
        size_t len;
+       int rc;
 
        if (cmd->aborted)
                return 0;
@@ -137,9 +138,10 @@ int ft_queue_status(struct se_cmd *se_cmd)
        len = sizeof(*fcp) + se_cmd->scsi_sense_length;
        fp = fc_frame_alloc(lport, len);
        if (!fp) {
-               /* XXX shouldn't just drop it - requeue and retry? */
-               return 0;
+               se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL;
+               return -ENOMEM;
        }
+
        fcp = fc_frame_payload_get(fp, len);
        memset(fcp, 0, len);
        fcp->resp.fr_status = se_cmd->scsi_status;
@@ -170,7 +172,18 @@ int ft_queue_status(struct se_cmd *se_cmd)
        fc_fill_fc_hdr(fp, FC_RCTL_DD_CMD_STATUS, ep->did, ep->sid, FC_TYPE_FCP,
                       FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ, 0);
 
-       lport->tt.seq_send(lport, cmd->seq, fp);
+       rc = lport->tt.seq_send(lport, cmd->seq, fp);
+       if (rc) {
+               pr_info_ratelimited("%s: Failed to send response frame %p, "
+                                   "xid <0x%x>\n", __func__, fp, ep->xid);
+               /*
+                * Generate a TASK_SET_FULL status to notify the initiator
+                * to reduce it's queue_depth after the se_cmd response has
+                * been re-queued by target-core.
+                */
+               se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL;
+               return -ENOMEM;
+       }
        lport->tt.exch_done(cmd->seq);
        return 0;
 }
index e415af32115a80bd7927627e826731b2c8b461c3..97b486c3dda136a21824d02ea0f70c828b214d46 100644 (file)
@@ -82,6 +82,10 @@ int ft_queue_data_in(struct se_cmd *se_cmd)
 
        if (cmd->aborted)
                return 0;
+
+       if (se_cmd->scsi_status == SAM_STAT_TASK_SET_FULL)
+               goto queue_status;
+
        ep = fc_seq_exch(cmd->seq);
        lport = ep->lp;
        cmd->seq = lport->tt.seq_start_next(cmd->seq);
@@ -178,14 +182,23 @@ int ft_queue_data_in(struct se_cmd *se_cmd)
                               FC_TYPE_FCP, f_ctl, fh_off);
                error = lport->tt.seq_send(lport, seq, fp);
                if (error) {
-                       /* XXX For now, initiator will retry */
-                       pr_err_ratelimited("%s: Failed to send frame %p, "
+                       pr_info_ratelimited("%s: Failed to send frame %p, "
                                                "xid <0x%x>, remaining %zu, "
                                                "lso_max <0x%x>\n",
                                                __func__, fp, ep->xid,
                                                remaining, lport->lso_max);
+                       /*
+                        * Go ahead and set TASK_SET_FULL status ignoring the
+                        * rest of the DataIN, and immediately attempt to
+                        * send the response via ft_queue_status() in order
+                        * to notify the initiator that it should reduce it's
+                        * per LUN queue_depth.
+                        */
+                       se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL;
+                       break;
                }
        }
+queue_status:
        return ft_queue_status(se_cmd);
 }
 
index 147d49e95db2b1a61437581aec0c1bbb6cbf35c6..df374860037cd2c64efe6617b485797b11f3acf9 100644 (file)
@@ -196,7 +196,7 @@ static int __init hvc_tile_init(void)
 #ifndef __tilegx__
        struct hvc_struct *hp;
        hp = hvc_alloc(0, 0, &hvc_tile_get_put_ops, 128);
-       return IS_ERR(hp) ? PTR_ERR(hp) : 0;
+       return PTR_ERR_OR_ZERO(hp);
 #else
        platform_device_register(&hvc_tile_pdev);
        return platform_driver_register(&hvc_tile_driver);
index ff205a7bc55c9aefcd98994aeb3c25fe4c37eb28..648f9e489b39bb3a291f091771dcd099013176db 100644 (file)
@@ -220,11 +220,11 @@ int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
         * If we can't read the file, it's no good.
         * If we can't write the file, use it read-only.
         */
-       if (!(filp->f_op->read || filp->f_op->aio_read)) {
+       if (!(filp->f_mode & FMODE_CAN_READ)) {
                LINFO(curlun, "file not readable: %s\n", filename);
                goto out;
        }
-       if (!(filp->f_op->write || filp->f_op->aio_write))
+       if (!(filp->f_mode & FMODE_CAN_WRITE))
                ro = 1;
 
        size = i_size_read(inode->i_mapping->host);
index fe0880d0873e04c973c1db5699c947b9ffc1d69a..3d78a8844e438f7e1904c5429655af8a4708d95d 100644 (file)
@@ -793,7 +793,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g,
 
        net->netdev_ops = &eth_netdev_ops;
 
-       SET_ETHTOOL_OPS(net, &ops);
+       net->ethtool_ops = &ops;
 
        dev->gadget = g;
        SET_NETDEV_DEV(net, &g->dev);
@@ -850,7 +850,7 @@ struct net_device *gether_setup_name_default(const char *netname)
 
        net->netdev_ops = &eth_netdev_ops;
 
-       SET_ETHTOOL_OPS(net, &ops);
+       net->ethtool_ops = &ops;
        SET_NETDEV_DEVTYPE(net, &gadget_type);
 
        return net;
index be414d2b2b22d8005723b0c1c45674c3c33aa41b..971a760af4a123f6c7c8982d0dcf2fadf09618b7 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/workqueue.h>
 #include <linux/file.h>
 #include <linux/slab.h>
+#include <linux/vmalloc.h>
 
 #include <linux/net.h>
 #include <linux/if_packet.h>
@@ -373,7 +374,7 @@ static void handle_tx(struct vhost_net *net)
                              % UIO_MAXIOV == nvq->done_idx))
                        break;
 
-               head = vhost_get_vq_desc(&net->dev, vq, vq->iov,
+               head = vhost_get_vq_desc(vq, vq->iov,
                                         ARRAY_SIZE(vq->iov),
                                         &out, &in,
                                         NULL, NULL);
@@ -505,7 +506,7 @@ static int get_rx_bufs(struct vhost_virtqueue *vq,
                        r = -ENOBUFS;
                        goto err;
                }
-               r = vhost_get_vq_desc(vq->dev, vq, vq->iov + seg,
+               r = vhost_get_vq_desc(vq, vq->iov + seg,
                                      ARRAY_SIZE(vq->iov) - seg, &out,
                                      &in, log, log_num);
                if (unlikely(r < 0))
@@ -584,9 +585,9 @@ static void handle_rx(struct vhost_net *net)
        vhost_hlen = nvq->vhost_hlen;
        sock_hlen = nvq->sock_hlen;
 
-       vq_log = unlikely(vhost_has_feature(&net->dev, VHOST_F_LOG_ALL)) ?
+       vq_log = unlikely(vhost_has_feature(vq, VHOST_F_LOG_ALL)) ?
                vq->log : NULL;
-       mergeable = vhost_has_feature(&net->dev, VIRTIO_NET_F_MRG_RXBUF);
+       mergeable = vhost_has_feature(vq, VIRTIO_NET_F_MRG_RXBUF);
 
        while ((sock_len = peek_head_len(sock->sk))) {
                sock_len += sock_hlen;
@@ -699,18 +700,30 @@ static void handle_rx_net(struct vhost_work *work)
        handle_rx(net);
 }
 
+static void vhost_net_free(void *addr)
+{
+       if (is_vmalloc_addr(addr))
+               vfree(addr);
+       else
+               kfree(addr);
+}
+
 static int vhost_net_open(struct inode *inode, struct file *f)
 {
-       struct vhost_net *n = kmalloc(sizeof *n, GFP_KERNEL);
+       struct vhost_net *n;
        struct vhost_dev *dev;
        struct vhost_virtqueue **vqs;
        int i;
 
-       if (!n)
-               return -ENOMEM;
+       n = kmalloc(sizeof *n, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT);
+       if (!n) {
+               n = vmalloc(sizeof *n);
+               if (!n)
+                       return -ENOMEM;
+       }
        vqs = kmalloc(VHOST_NET_VQ_MAX * sizeof(*vqs), GFP_KERNEL);
        if (!vqs) {
-               kfree(n);
+               vhost_net_free(n);
                return -ENOMEM;
        }
 
@@ -827,7 +840,7 @@ static int vhost_net_release(struct inode *inode, struct file *f)
         * since jobs can re-queue themselves. */
        vhost_net_flush(n);
        kfree(n->dev.vqs);
-       kfree(n);
+       vhost_net_free(n);
        return 0;
 }
 
@@ -1038,15 +1051,13 @@ static int vhost_net_set_features(struct vhost_net *n, u64 features)
                mutex_unlock(&n->dev.mutex);
                return -EFAULT;
        }
-       n->dev.acked_features = features;
-       smp_wmb();
        for (i = 0; i < VHOST_NET_VQ_MAX; ++i) {
                mutex_lock(&n->vqs[i].vq.mutex);
+               n->vqs[i].vq.acked_features = features;
                n->vqs[i].vhost_hlen = vhost_hlen;
                n->vqs[i].sock_hlen = sock_hlen;
                mutex_unlock(&n->vqs[i].vq.mutex);
        }
-       vhost_net_flush(n);
        mutex_unlock(&n->dev.mutex);
        return 0;
 }
index aeb513108448d7c87e3dfa6cf43accec47b13cd4..4f4ffa4c604e081755a3b77dba0fccb24c6ded7d 100644 (file)
@@ -57,7 +57,8 @@
 #define TCM_VHOST_MAX_CDB_SIZE 32
 #define TCM_VHOST_DEFAULT_TAGS 256
 #define TCM_VHOST_PREALLOC_SGLS 2048
-#define TCM_VHOST_PREALLOC_PAGES 2048
+#define TCM_VHOST_PREALLOC_UPAGES 2048
+#define TCM_VHOST_PREALLOC_PROT_SGLS 512
 
 struct vhost_scsi_inflight {
        /* Wait for the flush operation to finish */
@@ -79,10 +80,12 @@ struct tcm_vhost_cmd {
        u64 tvc_tag;
        /* The number of scatterlists associated with this cmd */
        u32 tvc_sgl_count;
+       u32 tvc_prot_sgl_count;
        /* Saved unpacked SCSI LUN for tcm_vhost_submission_work() */
        u32 tvc_lun;
        /* Pointer to the SGL formatted memory from virtio-scsi */
        struct scatterlist *tvc_sgl;
+       struct scatterlist *tvc_prot_sgl;
        struct page **tvc_upages;
        /* Pointer to response */
        struct virtio_scsi_cmd_resp __user *tvc_resp;
@@ -166,7 +169,8 @@ enum {
 };
 
 enum {
-       VHOST_SCSI_FEATURES = VHOST_FEATURES | (1ULL << VIRTIO_SCSI_F_HOTPLUG)
+       VHOST_SCSI_FEATURES = VHOST_FEATURES | (1ULL << VIRTIO_SCSI_F_HOTPLUG) |
+                                              (1ULL << VIRTIO_SCSI_F_T10_PI)
 };
 
 #define VHOST_SCSI_MAX_TARGET  256
@@ -456,12 +460,16 @@ static void tcm_vhost_release_cmd(struct se_cmd *se_cmd)
        struct tcm_vhost_cmd *tv_cmd = container_of(se_cmd,
                                struct tcm_vhost_cmd, tvc_se_cmd);
        struct se_session *se_sess = se_cmd->se_sess;
+       int i;
 
        if (tv_cmd->tvc_sgl_count) {
-               u32 i;
                for (i = 0; i < tv_cmd->tvc_sgl_count; i++)
                        put_page(sg_page(&tv_cmd->tvc_sgl[i]));
        }
+       if (tv_cmd->tvc_prot_sgl_count) {
+               for (i = 0; i < tv_cmd->tvc_prot_sgl_count; i++)
+                       put_page(sg_page(&tv_cmd->tvc_prot_sgl[i]));
+       }
 
        tcm_vhost_put_inflight(tv_cmd->inflight);
        percpu_ida_free(&se_sess->sess_tag_pool, se_cmd->map_tag);
@@ -606,7 +614,7 @@ tcm_vhost_do_evt_work(struct vhost_scsi *vs, struct tcm_vhost_evt *evt)
 
 again:
        vhost_disable_notify(&vs->dev, vq);
-       head = vhost_get_vq_desc(&vs->dev, vq, vq->iov,
+       head = vhost_get_vq_desc(vq, vq->iov,
                        ARRAY_SIZE(vq->iov), &out, &in,
                        NULL, NULL);
        if (head < 0) {
@@ -713,16 +721,14 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work)
 }
 
 static struct tcm_vhost_cmd *
-vhost_scsi_get_tag(struct vhost_virtqueue *vq,
-                       struct tcm_vhost_tpg *tpg,
-                       struct virtio_scsi_cmd_req *v_req,
-                       u32 exp_data_len,
-                       int data_direction)
+vhost_scsi_get_tag(struct vhost_virtqueue *vq, struct tcm_vhost_tpg *tpg,
+                  unsigned char *cdb, u64 scsi_tag, u16 lun, u8 task_attr,
+                  u32 exp_data_len, int data_direction)
 {
        struct tcm_vhost_cmd *cmd;
        struct tcm_vhost_nexus *tv_nexus;
        struct se_session *se_sess;
-       struct scatterlist *sg;
+       struct scatterlist *sg, *prot_sg;
        struct page **pages;
        int tag;
 
@@ -741,19 +747,24 @@ vhost_scsi_get_tag(struct vhost_virtqueue *vq,
 
        cmd = &((struct tcm_vhost_cmd *)se_sess->sess_cmd_map)[tag];
        sg = cmd->tvc_sgl;
+       prot_sg = cmd->tvc_prot_sgl;
        pages = cmd->tvc_upages;
        memset(cmd, 0, sizeof(struct tcm_vhost_cmd));
 
        cmd->tvc_sgl = sg;
+       cmd->tvc_prot_sgl = prot_sg;
        cmd->tvc_upages = pages;
        cmd->tvc_se_cmd.map_tag = tag;
-       cmd->tvc_tag = v_req->tag;
-       cmd->tvc_task_attr = v_req->task_attr;
+       cmd->tvc_tag = scsi_tag;
+       cmd->tvc_lun = lun;
+       cmd->tvc_task_attr = task_attr;
        cmd->tvc_exp_data_len = exp_data_len;
        cmd->tvc_data_direction = data_direction;
        cmd->tvc_nexus = tv_nexus;
        cmd->inflight = tcm_vhost_get_inflight(vq);
 
+       memcpy(cmd->tvc_cdb, cdb, TCM_VHOST_MAX_CDB_SIZE);
+
        return cmd;
 }
 
@@ -767,35 +778,28 @@ vhost_scsi_map_to_sgl(struct tcm_vhost_cmd *tv_cmd,
                      struct scatterlist *sgl,
                      unsigned int sgl_count,
                      struct iovec *iov,
-                     int write)
+                     struct page **pages,
+                     bool write)
 {
        unsigned int npages = 0, pages_nr, offset, nbytes;
        struct scatterlist *sg = sgl;
        void __user *ptr = iov->iov_base;
        size_t len = iov->iov_len;
-       struct page **pages;
        int ret, i;
 
-       if (sgl_count > TCM_VHOST_PREALLOC_SGLS) {
-               pr_err("vhost_scsi_map_to_sgl() psgl_count: %u greater than"
-                      " preallocated TCM_VHOST_PREALLOC_SGLS: %u\n",
-                       sgl_count, TCM_VHOST_PREALLOC_SGLS);
-               return -ENOBUFS;
-       }
-
        pages_nr = iov_num_pages(iov);
-       if (pages_nr > sgl_count)
+       if (pages_nr > sgl_count) {
+               pr_err("vhost_scsi_map_to_sgl() pages_nr: %u greater than"
+                      " sgl_count: %u\n", pages_nr, sgl_count);
                return -ENOBUFS;
-
-       if (pages_nr > TCM_VHOST_PREALLOC_PAGES) {
+       }
+       if (pages_nr > TCM_VHOST_PREALLOC_UPAGES) {
                pr_err("vhost_scsi_map_to_sgl() pages_nr: %u greater than"
-                      " preallocated TCM_VHOST_PREALLOC_PAGES: %u\n",
-                       pages_nr, TCM_VHOST_PREALLOC_PAGES);
+                      " preallocated TCM_VHOST_PREALLOC_UPAGES: %u\n",
+                       pages_nr, TCM_VHOST_PREALLOC_UPAGES);
                return -ENOBUFS;
        }
 
-       pages = tv_cmd->tvc_upages;
-
        ret = get_user_pages_fast((unsigned long)ptr, pages_nr, write, pages);
        /* No pages were pinned */
        if (ret < 0)
@@ -825,33 +829,32 @@ out:
 static int
 vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *cmd,
                          struct iovec *iov,
-                         unsigned int niov,
-                         int write)
+                         int niov,
+                         bool write)
 {
-       int ret;
-       unsigned int i;
-       u32 sgl_count;
-       struct scatterlist *sg;
+       struct scatterlist *sg = cmd->tvc_sgl;
+       unsigned int sgl_count = 0;
+       int ret, i;
 
-       /*
-        * Find out how long sglist needs to be
-        */
-       sgl_count = 0;
        for (i = 0; i < niov; i++)
                sgl_count += iov_num_pages(&iov[i]);
 
-       /* TODO overflow checking */
+       if (sgl_count > TCM_VHOST_PREALLOC_SGLS) {
+               pr_err("vhost_scsi_map_iov_to_sgl() sgl_count: %u greater than"
+                       " preallocated TCM_VHOST_PREALLOC_SGLS: %u\n",
+                       sgl_count, TCM_VHOST_PREALLOC_SGLS);
+               return -ENOBUFS;
+       }
 
-       sg = cmd->tvc_sgl;
        pr_debug("%s sg %p sgl_count %u\n", __func__, sg, sgl_count);
        sg_init_table(sg, sgl_count);
-
        cmd->tvc_sgl_count = sgl_count;
 
-       pr_debug("Mapping %u iovecs for %u pages\n", niov, sgl_count);
+       pr_debug("Mapping iovec %p for %u pages\n", &iov[0], sgl_count);
+
        for (i = 0; i < niov; i++) {
                ret = vhost_scsi_map_to_sgl(cmd, sg, sgl_count, &iov[i],
-                                           write);
+                                           cmd->tvc_upages, write);
                if (ret < 0) {
                        for (i = 0; i < cmd->tvc_sgl_count; i++)
                                put_page(sg_page(&cmd->tvc_sgl[i]));
@@ -859,31 +862,70 @@ vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *cmd,
                        cmd->tvc_sgl_count = 0;
                        return ret;
                }
-
                sg += ret;
                sgl_count -= ret;
        }
        return 0;
 }
 
+static int
+vhost_scsi_map_iov_to_prot(struct tcm_vhost_cmd *cmd,
+                          struct iovec *iov,
+                          int niov,
+                          bool write)
+{
+       struct scatterlist *prot_sg = cmd->tvc_prot_sgl;
+       unsigned int prot_sgl_count = 0;
+       int ret, i;
+
+       for (i = 0; i < niov; i++)
+               prot_sgl_count += iov_num_pages(&iov[i]);
+
+       if (prot_sgl_count > TCM_VHOST_PREALLOC_PROT_SGLS) {
+               pr_err("vhost_scsi_map_iov_to_prot() sgl_count: %u greater than"
+                       " preallocated TCM_VHOST_PREALLOC_PROT_SGLS: %u\n",
+                       prot_sgl_count, TCM_VHOST_PREALLOC_PROT_SGLS);
+               return -ENOBUFS;
+       }
+
+       pr_debug("%s prot_sg %p prot_sgl_count %u\n", __func__,
+                prot_sg, prot_sgl_count);
+       sg_init_table(prot_sg, prot_sgl_count);
+       cmd->tvc_prot_sgl_count = prot_sgl_count;
+
+       for (i = 0; i < niov; i++) {
+               ret = vhost_scsi_map_to_sgl(cmd, prot_sg, prot_sgl_count, &iov[i],
+                                           cmd->tvc_upages, write);
+               if (ret < 0) {
+                       for (i = 0; i < cmd->tvc_prot_sgl_count; i++)
+                               put_page(sg_page(&cmd->tvc_prot_sgl[i]));
+
+                       cmd->tvc_prot_sgl_count = 0;
+                       return ret;
+               }
+               prot_sg += ret;
+               prot_sgl_count -= ret;
+       }
+       return 0;
+}
+
 static void tcm_vhost_submission_work(struct work_struct *work)
 {
        struct tcm_vhost_cmd *cmd =
                container_of(work, struct tcm_vhost_cmd, work);
        struct tcm_vhost_nexus *tv_nexus;
        struct se_cmd *se_cmd = &cmd->tvc_se_cmd;
-       struct scatterlist *sg_ptr, *sg_bidi_ptr = NULL;
-       int rc, sg_no_bidi = 0;
+       struct scatterlist *sg_ptr, *sg_prot_ptr = NULL;
+       int rc;
 
+       /* FIXME: BIDI operation */
        if (cmd->tvc_sgl_count) {
                sg_ptr = cmd->tvc_sgl;
-/* FIXME: Fix BIDI operation in tcm_vhost_submission_work() */
-#if 0
-               if (se_cmd->se_cmd_flags & SCF_BIDI) {
-                       sg_bidi_ptr = NULL;
-                       sg_no_bidi = 0;
-               }
-#endif
+
+               if (cmd->tvc_prot_sgl_count)
+                       sg_prot_ptr = cmd->tvc_prot_sgl;
+               else
+                       se_cmd->prot_pto = true;
        } else {
                sg_ptr = NULL;
        }
@@ -894,7 +936,7 @@ static void tcm_vhost_submission_work(struct work_struct *work)
                        cmd->tvc_lun, cmd->tvc_exp_data_len,
                        cmd->tvc_task_attr, cmd->tvc_data_direction,
                        TARGET_SCF_ACK_KREF, sg_ptr, cmd->tvc_sgl_count,
-                       sg_bidi_ptr, sg_no_bidi, NULL, 0);
+                       NULL, 0, sg_prot_ptr, cmd->tvc_prot_sgl_count);
        if (rc < 0) {
                transport_send_check_condition_and_sense(se_cmd,
                                TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0);
@@ -926,12 +968,18 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
 {
        struct tcm_vhost_tpg **vs_tpg;
        struct virtio_scsi_cmd_req v_req;
+       struct virtio_scsi_cmd_req_pi v_req_pi;
        struct tcm_vhost_tpg *tpg;
        struct tcm_vhost_cmd *cmd;
-       u32 exp_data_len, data_first, data_num, data_direction;
+       u64 tag;
+       u32 exp_data_len, data_first, data_num, data_direction, prot_first;
        unsigned out, in, i;
-       int head, ret;
-       u8 target;
+       int head, ret, data_niov, prot_niov, prot_bytes;
+       size_t req_size;
+       u16 lun;
+       u8 *target, *lunp, task_attr;
+       bool hdr_pi;
+       void *req, *cdb;
 
        mutex_lock(&vq->mutex);
        /*
@@ -945,7 +993,7 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
        vhost_disable_notify(&vs->dev, vq);
 
        for (;;) {
-               head = vhost_get_vq_desc(&vs->dev, vq, vq->iov,
+               head = vhost_get_vq_desc(vq, vq->iov,
                                        ARRAY_SIZE(vq->iov), &out, &in,
                                        NULL, NULL);
                pr_debug("vhost_get_vq_desc: head: %d, out: %u in: %u\n",
@@ -962,7 +1010,7 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
                        break;
                }
 
-/* FIXME: BIDI operation */
+               /* FIXME: BIDI operation */
                if (out == 1 && in == 1) {
                        data_direction = DMA_NONE;
                        data_first = 0;
@@ -992,29 +1040,38 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
                        break;
                }
 
-               if (unlikely(vq->iov[0].iov_len != sizeof(v_req))) {
-                       vq_err(vq, "Expecting virtio_scsi_cmd_req, got %zu"
-                               " bytes\n", vq->iov[0].iov_len);
+               if (vhost_has_feature(vq, VIRTIO_SCSI_F_T10_PI)) {
+                       req = &v_req_pi;
+                       lunp = &v_req_pi.lun[0];
+                       target = &v_req_pi.lun[1];
+                       req_size = sizeof(v_req_pi);
+                       hdr_pi = true;
+               } else {
+                       req = &v_req;
+                       lunp = &v_req.lun[0];
+                       target = &v_req.lun[1];
+                       req_size = sizeof(v_req);
+                       hdr_pi = false;
+               }
+
+               if (unlikely(vq->iov[0].iov_len < req_size)) {
+                       pr_err("Expecting virtio-scsi header: %zu, got %zu\n",
+                              req_size, vq->iov[0].iov_len);
                        break;
                }
-               pr_debug("Calling __copy_from_user: vq->iov[0].iov_base: %p,"
-                       " len: %zu\n", vq->iov[0].iov_base, sizeof(v_req));
-               ret = __copy_from_user(&v_req, vq->iov[0].iov_base,
-                               sizeof(v_req));
+               ret = memcpy_fromiovecend(req, &vq->iov[0], 0, req_size);
                if (unlikely(ret)) {
                        vq_err(vq, "Faulted on virtio_scsi_cmd_req\n");
                        break;
                }
 
                /* virtio-scsi spec requires byte 0 of the lun to be 1 */
-               if (unlikely(v_req.lun[0] != 1)) {
+               if (unlikely(*lunp != 1)) {
                        vhost_scsi_send_bad_target(vs, vq, head, out);
                        continue;
                }
 
-               /* Extract the tpgt */
-               target = v_req.lun[1];
-               tpg = ACCESS_ONCE(vs_tpg[target]);
+               tpg = ACCESS_ONCE(vs_tpg[*target]);
 
                /* Target does not exist, fail the request */
                if (unlikely(!tpg)) {
@@ -1022,17 +1079,79 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
                        continue;
                }
 
+               data_niov = data_num;
+               prot_niov = prot_first = prot_bytes = 0;
+               /*
+                * Determine if any protection information iovecs are preceeding
+                * the actual data payload, and adjust data_first + data_niov
+                * values accordingly for vhost_scsi_map_iov_to_sgl() below.
+                *
+                * Also extract virtio_scsi header bits for vhost_scsi_get_tag()
+                */
+               if (hdr_pi) {
+                       if (v_req_pi.pi_bytesout) {
+                               if (data_direction != DMA_TO_DEVICE) {
+                                       vq_err(vq, "Received non zero do_pi_niov"
+                                               ", but wrong data_direction\n");
+                                       goto err_cmd;
+                               }
+                               prot_bytes = v_req_pi.pi_bytesout;
+                       } else if (v_req_pi.pi_bytesin) {
+                               if (data_direction != DMA_FROM_DEVICE) {
+                                       vq_err(vq, "Received non zero di_pi_niov"
+                                               ", but wrong data_direction\n");
+                                       goto err_cmd;
+                               }
+                               prot_bytes = v_req_pi.pi_bytesin;
+                       }
+                       if (prot_bytes) {
+                               int tmp = 0;
+
+                               for (i = 0; i < data_num; i++) {
+                                       tmp += vq->iov[data_first + i].iov_len;
+                                       prot_niov++;
+                                       if (tmp >= prot_bytes)
+                                               break;
+                               }
+                               prot_first = data_first;
+                               data_first += prot_niov;
+                               data_niov = data_num - prot_niov;
+                       }
+                       tag = v_req_pi.tag;
+                       task_attr = v_req_pi.task_attr;
+                       cdb = &v_req_pi.cdb[0];
+                       lun = ((v_req_pi.lun[2] << 8) | v_req_pi.lun[3]) & 0x3FFF;
+               } else {
+                       tag = v_req.tag;
+                       task_attr = v_req.task_attr;
+                       cdb = &v_req.cdb[0];
+                       lun = ((v_req.lun[2] << 8) | v_req.lun[3]) & 0x3FFF;
+               }
                exp_data_len = 0;
-               for (i = 0; i < data_num; i++)
+               for (i = 0; i < data_niov; i++)
                        exp_data_len += vq->iov[data_first + i].iov_len;
+               /*
+                * Check that the recieved CDB size does not exceeded our
+                * hardcoded max for vhost-scsi
+                *
+                * TODO what if cdb was too small for varlen cdb header?
+                */
+               if (unlikely(scsi_command_size(cdb) > TCM_VHOST_MAX_CDB_SIZE)) {
+                       vq_err(vq, "Received SCSI CDB with command_size: %d that"
+                               " exceeds SCSI_MAX_VARLEN_CDB_SIZE: %d\n",
+                               scsi_command_size(cdb), TCM_VHOST_MAX_CDB_SIZE);
+                       goto err_cmd;
+               }
 
-               cmd = vhost_scsi_get_tag(vq, tpg, &v_req,
-                                        exp_data_len, data_direction);
+               cmd = vhost_scsi_get_tag(vq, tpg, cdb, tag, lun, task_attr,
+                                        exp_data_len + prot_bytes,
+                                        data_direction);
                if (IS_ERR(cmd)) {
                        vq_err(vq, "vhost_scsi_get_tag failed %ld\n",
                                        PTR_ERR(cmd));
                        goto err_cmd;
                }
+
                pr_debug("Allocated tv_cmd: %p exp_data_len: %d, data_direction"
                        ": %d\n", cmd, exp_data_len, data_direction);
 
@@ -1040,40 +1159,28 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
                cmd->tvc_vq = vq;
                cmd->tvc_resp = vq->iov[out].iov_base;
 
-               /*
-                * Copy in the recieved CDB descriptor into cmd->tvc_cdb
-                * that will be used by tcm_vhost_new_cmd_map() and down into
-                * target_setup_cmd_from_cdb()
-                */
-               memcpy(cmd->tvc_cdb, v_req.cdb, TCM_VHOST_MAX_CDB_SIZE);
-               /*
-                * Check that the recieved CDB size does not exceeded our
-                * hardcoded max for tcm_vhost
-                */
-               /* TODO what if cdb was too small for varlen cdb header? */
-               if (unlikely(scsi_command_size(cmd->tvc_cdb) >
-                                       TCM_VHOST_MAX_CDB_SIZE)) {
-                       vq_err(vq, "Received SCSI CDB with command_size: %d that"
-                               " exceeds SCSI_MAX_VARLEN_CDB_SIZE: %d\n",
-                               scsi_command_size(cmd->tvc_cdb),
-                               TCM_VHOST_MAX_CDB_SIZE);
-                       goto err_free;
-               }
-               cmd->tvc_lun = ((v_req.lun[2] << 8) | v_req.lun[3]) & 0x3FFF;
-
                pr_debug("vhost_scsi got command opcode: %#02x, lun: %d\n",
                        cmd->tvc_cdb[0], cmd->tvc_lun);
 
+               if (prot_niov) {
+                       ret = vhost_scsi_map_iov_to_prot(cmd,
+                                       &vq->iov[prot_first], prot_niov,
+                                       data_direction == DMA_FROM_DEVICE);
+                       if (unlikely(ret)) {
+                               vq_err(vq, "Failed to map iov to"
+                                       " prot_sgl\n");
+                               goto err_free;
+                       }
+               }
                if (data_direction != DMA_NONE) {
                        ret = vhost_scsi_map_iov_to_sgl(cmd,
-                                       &vq->iov[data_first], data_num,
+                                       &vq->iov[data_first], data_niov,
                                        data_direction == DMA_FROM_DEVICE);
                        if (unlikely(ret)) {
                                vq_err(vq, "Failed to map iov to sgl\n");
                                goto err_free;
                        }
                }
-
                /*
                 * Save the descriptor from vhost_get_vq_desc() to be used to
                 * complete the virtio-scsi request in TCM callback context via
@@ -1373,6 +1480,9 @@ err_dev:
 
 static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features)
 {
+       struct vhost_virtqueue *vq;
+       int i;
+
        if (features & ~VHOST_SCSI_FEATURES)
                return -EOPNOTSUPP;
 
@@ -1382,9 +1492,13 @@ static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features)
                mutex_unlock(&vs->dev.mutex);
                return -EFAULT;
        }
-       vs->dev.acked_features = features;
-       smp_wmb();
-       vhost_scsi_flush(vs);
+
+       for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) {
+               vq = &vs->vqs[i].vq;
+               mutex_lock(&vq->mutex);
+               vq->acked_features = features;
+               mutex_unlock(&vq->mutex);
+       }
        mutex_unlock(&vs->dev.mutex);
        return 0;
 }
@@ -1591,10 +1705,6 @@ tcm_vhost_do_plug(struct tcm_vhost_tpg *tpg,
                return;
 
        mutex_lock(&vs->dev.mutex);
-       if (!vhost_has_feature(&vs->dev, VIRTIO_SCSI_F_HOTPLUG)) {
-               mutex_unlock(&vs->dev.mutex);
-               return;
-       }
 
        if (plug)
                reason = VIRTIO_SCSI_EVT_RESET_RESCAN;
@@ -1603,8 +1713,9 @@ tcm_vhost_do_plug(struct tcm_vhost_tpg *tpg,
 
        vq = &vs->vqs[VHOST_SCSI_VQ_EVT].vq;
        mutex_lock(&vq->mutex);
-       tcm_vhost_send_evt(vs, tpg, lun,
-                       VIRTIO_SCSI_T_TRANSPORT_RESET, reason);
+       if (vhost_has_feature(vq, VIRTIO_SCSI_F_HOTPLUG))
+               tcm_vhost_send_evt(vs, tpg, lun,
+                                  VIRTIO_SCSI_T_TRANSPORT_RESET, reason);
        mutex_unlock(&vq->mutex);
        mutex_unlock(&vs->dev.mutex);
 }
@@ -1712,6 +1823,7 @@ static void tcm_vhost_free_cmd_map_res(struct tcm_vhost_nexus *nexus,
                tv_cmd = &((struct tcm_vhost_cmd *)se_sess->sess_cmd_map)[i];
 
                kfree(tv_cmd->tvc_sgl);
+               kfree(tv_cmd->tvc_prot_sgl);
                kfree(tv_cmd->tvc_upages);
        }
 }
@@ -1746,7 +1858,7 @@ static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tpg,
        tv_nexus->tvn_se_sess = transport_init_session_tags(
                                        TCM_VHOST_DEFAULT_TAGS,
                                        sizeof(struct tcm_vhost_cmd),
-                                       TARGET_PROT_NORMAL);
+                                       TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS);
        if (IS_ERR(tv_nexus->tvn_se_sess)) {
                mutex_unlock(&tpg->tv_tpg_mutex);
                kfree(tv_nexus);
@@ -1765,12 +1877,20 @@ static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tpg,
                }
 
                tv_cmd->tvc_upages = kzalloc(sizeof(struct page *) *
-                                       TCM_VHOST_PREALLOC_PAGES, GFP_KERNEL);
+                                       TCM_VHOST_PREALLOC_UPAGES, GFP_KERNEL);
                if (!tv_cmd->tvc_upages) {
                        mutex_unlock(&tpg->tv_tpg_mutex);
                        pr_err("Unable to allocate tv_cmd->tvc_upages\n");
                        goto out;
                }
+
+               tv_cmd->tvc_prot_sgl = kzalloc(sizeof(struct scatterlist) *
+                                       TCM_VHOST_PREALLOC_PROT_SGLS, GFP_KERNEL);
+               if (!tv_cmd->tvc_prot_sgl) {
+                       mutex_unlock(&tpg->tv_tpg_mutex);
+                       pr_err("Unable to allocate tv_cmd->tvc_prot_sgl\n");
+                       goto out;
+               }
        }
        /*
         * Since we are running in 'demo mode' this call with generate a
index c2a54fbf7f996e9065702a5aab1d284447c9785b..d9c501eaa6c3362b7ea89cdb39d33334f435af47 100644 (file)
@@ -53,7 +53,7 @@ static void handle_vq(struct vhost_test *n)
        vhost_disable_notify(&n->dev, vq);
 
        for (;;) {
-               head = vhost_get_vq_desc(&n->dev, vq, vq->iov,
+               head = vhost_get_vq_desc(vq, vq->iov,
                                         ARRAY_SIZE(vq->iov),
                                         &out, &in,
                                         NULL, NULL);
@@ -241,15 +241,18 @@ done:
 
 static int vhost_test_set_features(struct vhost_test *n, u64 features)
 {
+       struct vhost_virtqueue *vq;
+
        mutex_lock(&n->dev.mutex);
        if ((features & (1 << VHOST_F_LOG_ALL)) &&
            !vhost_log_access_ok(&n->dev)) {
                mutex_unlock(&n->dev.mutex);
                return -EFAULT;
        }
-       n->dev.acked_features = features;
-       smp_wmb();
-       vhost_test_flush(n);
+       vq = &n->vqs[VHOST_TEST_VQ];
+       mutex_lock(&vq->mutex);
+       vq->acked_features = features;
+       mutex_unlock(&vq->mutex);
        mutex_unlock(&n->dev.mutex);
        return 0;
 }
index 78987e481bc6a3e578f1ea5789802ae9b1ae7045..c90f4374442a571610da46b56dc94d51a3e26feb 100644 (file)
@@ -18,7 +18,6 @@
 #include <linux/mmu_context.h>
 #include <linux/miscdevice.h>
 #include <linux/mutex.h>
-#include <linux/rcupdate.h>
 #include <linux/poll.h>
 #include <linux/file.h>
 #include <linux/highmem.h>
@@ -191,6 +190,7 @@ static void vhost_vq_reset(struct vhost_dev *dev,
        vq->log_used = false;
        vq->log_addr = -1ull;
        vq->private_data = NULL;
+       vq->acked_features = 0;
        vq->log_base = NULL;
        vq->error_ctx = NULL;
        vq->error = NULL;
@@ -198,6 +198,7 @@ static void vhost_vq_reset(struct vhost_dev *dev,
        vq->call_ctx = NULL;
        vq->call = NULL;
        vq->log_ctx = NULL;
+       vq->memory = NULL;
 }
 
 static int vhost_worker(void *data)
@@ -415,11 +416,18 @@ EXPORT_SYMBOL_GPL(vhost_dev_reset_owner_prepare);
 /* Caller should have device mutex */
 void vhost_dev_reset_owner(struct vhost_dev *dev, struct vhost_memory *memory)
 {
+       int i;
+
        vhost_dev_cleanup(dev, true);
 
        /* Restore memory to default empty mapping. */
        memory->nregions = 0;
-       RCU_INIT_POINTER(dev->memory, memory);
+       dev->memory = memory;
+       /* We don't need VQ locks below since vhost_dev_cleanup makes sure
+        * VQs aren't running.
+        */
+       for (i = 0; i < dev->nvqs; ++i)
+               dev->vqs[i]->memory = memory;
 }
 EXPORT_SYMBOL_GPL(vhost_dev_reset_owner);
 
@@ -462,10 +470,8 @@ void vhost_dev_cleanup(struct vhost_dev *dev, bool locked)
                fput(dev->log_file);
        dev->log_file = NULL;
        /* No one will access memory at this point */
-       kfree(rcu_dereference_protected(dev->memory,
-                                       locked ==
-                                               lockdep_is_held(&dev->mutex)));
-       RCU_INIT_POINTER(dev->memory, NULL);
+       kfree(dev->memory);
+       dev->memory = NULL;
        WARN_ON(!list_empty(&dev->work_list));
        if (dev->worker) {
                kthread_stop(dev->worker);
@@ -524,11 +530,13 @@ static int memory_access_ok(struct vhost_dev *d, struct vhost_memory *mem,
 
        for (i = 0; i < d->nvqs; ++i) {
                int ok;
+               bool log;
+
                mutex_lock(&d->vqs[i]->mutex);
+               log = log_all || vhost_has_feature(d->vqs[i], VHOST_F_LOG_ALL);
                /* If ring is inactive, will check when it's enabled. */
                if (d->vqs[i]->private_data)
-                       ok = vq_memory_access_ok(d->vqs[i]->log_base, mem,
-                                                log_all);
+                       ok = vq_memory_access_ok(d->vqs[i]->log_base, mem, log);
                else
                        ok = 1;
                mutex_unlock(&d->vqs[i]->mutex);
@@ -538,12 +546,12 @@ static int memory_access_ok(struct vhost_dev *d, struct vhost_memory *mem,
        return 1;
 }
 
-static int vq_access_ok(struct vhost_dev *d, unsigned int num,
+static int vq_access_ok(struct vhost_virtqueue *vq, unsigned int num,
                        struct vring_desc __user *desc,
                        struct vring_avail __user *avail,
                        struct vring_used __user *used)
 {
-       size_t s = vhost_has_feature(d, VIRTIO_RING_F_EVENT_IDX) ? 2 : 0;
+       size_t s = vhost_has_feature(vq, VIRTIO_RING_F_EVENT_IDX) ? 2 : 0;
        return access_ok(VERIFY_READ, desc, num * sizeof *desc) &&
               access_ok(VERIFY_READ, avail,
                         sizeof *avail + num * sizeof *avail->ring + s) &&
@@ -555,26 +563,19 @@ static int vq_access_ok(struct vhost_dev *d, unsigned int num,
 /* Caller should have device mutex but not vq mutex */
 int vhost_log_access_ok(struct vhost_dev *dev)
 {
-       struct vhost_memory *mp;
-
-       mp = rcu_dereference_protected(dev->memory,
-                                      lockdep_is_held(&dev->mutex));
-       return memory_access_ok(dev, mp, 1);
+       return memory_access_ok(dev, dev->memory, 1);
 }
 EXPORT_SYMBOL_GPL(vhost_log_access_ok);
 
 /* Verify access for write logging. */
 /* Caller should have vq mutex and device mutex */
-static int vq_log_access_ok(struct vhost_dev *d, struct vhost_virtqueue *vq,
+static int vq_log_access_ok(struct vhost_virtqueue *vq,
                            void __user *log_base)
 {
-       struct vhost_memory *mp;
-       size_t s = vhost_has_feature(d, VIRTIO_RING_F_EVENT_IDX) ? 2 : 0;
+       size_t s = vhost_has_feature(vq, VIRTIO_RING_F_EVENT_IDX) ? 2 : 0;
 
-       mp = rcu_dereference_protected(vq->dev->memory,
-                                      lockdep_is_held(&vq->mutex));
-       return vq_memory_access_ok(log_base, mp,
-                           vhost_has_feature(vq->dev, VHOST_F_LOG_ALL)) &&
+       return vq_memory_access_ok(log_base, vq->memory,
+                                  vhost_has_feature(vq, VHOST_F_LOG_ALL)) &&
                (!vq->log_used || log_access_ok(log_base, vq->log_addr,
                                        sizeof *vq->used +
                                        vq->num * sizeof *vq->used->ring + s));
@@ -584,8 +585,8 @@ static int vq_log_access_ok(struct vhost_dev *d, struct vhost_virtqueue *vq,
 /* Caller should have vq mutex and device mutex */
 int vhost_vq_access_ok(struct vhost_virtqueue *vq)
 {
-       return vq_access_ok(vq->dev, vq->num, vq->desc, vq->avail, vq->used) &&
-               vq_log_access_ok(vq->dev, vq, vq->log_base);
+       return vq_access_ok(vq, vq->num, vq->desc, vq->avail, vq->used) &&
+               vq_log_access_ok(vq, vq->log_base);
 }
 EXPORT_SYMBOL_GPL(vhost_vq_access_ok);
 
@@ -593,6 +594,7 @@ static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m)
 {
        struct vhost_memory mem, *newmem, *oldmem;
        unsigned long size = offsetof(struct vhost_memory, regions);
+       int i;
 
        if (copy_from_user(&mem, m, size))
                return -EFAULT;
@@ -611,15 +613,19 @@ static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m)
                return -EFAULT;
        }
 
-       if (!memory_access_ok(d, newmem,
-                             vhost_has_feature(d, VHOST_F_LOG_ALL))) {
+       if (!memory_access_ok(d, newmem, 0)) {
                kfree(newmem);
                return -EFAULT;
        }
-       oldmem = rcu_dereference_protected(d->memory,
-                                          lockdep_is_held(&d->mutex));
-       rcu_assign_pointer(d->memory, newmem);
-       synchronize_rcu();
+       oldmem = d->memory;
+       d->memory = newmem;
+
+       /* All memory accesses are done under some VQ mutex. */
+       for (i = 0; i < d->nvqs; ++i) {
+               mutex_lock(&d->vqs[i]->mutex);
+               d->vqs[i]->memory = newmem;
+               mutex_unlock(&d->vqs[i]->mutex);
+       }
        kfree(oldmem);
        return 0;
 }
@@ -718,7 +724,7 @@ long vhost_vring_ioctl(struct vhost_dev *d, int ioctl, void __user *argp)
                 * If it is not, we don't as size might not have been setup.
                 * We will verify when backend is configured. */
                if (vq->private_data) {
-                       if (!vq_access_ok(d, vq->num,
+                       if (!vq_access_ok(vq, vq->num,
                                (void __user *)(unsigned long)a.desc_user_addr,
                                (void __user *)(unsigned long)a.avail_user_addr,
                                (void __user *)(unsigned long)a.used_user_addr)) {
@@ -858,7 +864,7 @@ long vhost_dev_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *argp)
                        vq = d->vqs[i];
                        mutex_lock(&vq->mutex);
                        /* If ring is inactive, will check when it's enabled. */
-                       if (vq->private_data && !vq_log_access_ok(d, vq, base))
+                       if (vq->private_data && !vq_log_access_ok(vq, base))
                                r = -EFAULT;
                        else
                                vq->log_base = base;
@@ -1044,7 +1050,7 @@ int vhost_init_used(struct vhost_virtqueue *vq)
 }
 EXPORT_SYMBOL_GPL(vhost_init_used);
 
-static int translate_desc(struct vhost_dev *dev, u64 addr, u32 len,
+static int translate_desc(struct vhost_virtqueue *vq, u64 addr, u32 len,
                          struct iovec iov[], int iov_size)
 {
        const struct vhost_memory_region *reg;
@@ -1053,9 +1059,7 @@ static int translate_desc(struct vhost_dev *dev, u64 addr, u32 len,
        u64 s = 0;
        int ret = 0;
 
-       rcu_read_lock();
-
-       mem = rcu_dereference(dev->memory);
+       mem = vq->memory;
        while ((u64)len > s) {
                u64 size;
                if (unlikely(ret >= iov_size)) {
@@ -1077,7 +1081,6 @@ static int translate_desc(struct vhost_dev *dev, u64 addr, u32 len,
                ++ret;
        }
 
-       rcu_read_unlock();
        return ret;
 }
 
@@ -1102,7 +1105,7 @@ static unsigned next_desc(struct vring_desc *desc)
        return next;
 }
 
-static int get_indirect(struct vhost_dev *dev, struct vhost_virtqueue *vq,
+static int get_indirect(struct vhost_virtqueue *vq,
                        struct iovec iov[], unsigned int iov_size,
                        unsigned int *out_num, unsigned int *in_num,
                        struct vhost_log *log, unsigned int *log_num,
@@ -1121,7 +1124,7 @@ static int get_indirect(struct vhost_dev *dev, struct vhost_virtqueue *vq,
                return -EINVAL;
        }
 
-       ret = translate_desc(dev, indirect->addr, indirect->len, vq->indirect,
+       ret = translate_desc(vq, indirect->addr, indirect->len, vq->indirect,
                             UIO_MAXIOV);
        if (unlikely(ret < 0)) {
                vq_err(vq, "Translation failure %d in indirect.\n", ret);
@@ -1161,7 +1164,7 @@ static int get_indirect(struct vhost_dev *dev, struct vhost_virtqueue *vq,
                        return -EINVAL;
                }
 
-               ret = translate_desc(dev, desc.addr, desc.len, iov + iov_count,
+               ret = translate_desc(vq, desc.addr, desc.len, iov + iov_count,
                                     iov_size - iov_count);
                if (unlikely(ret < 0)) {
                        vq_err(vq, "Translation failure %d indirect idx %d\n",
@@ -1198,7 +1201,7 @@ static int get_indirect(struct vhost_dev *dev, struct vhost_virtqueue *vq,
  * This function returns the descriptor number found, or vq->num (which is
  * never a valid descriptor number) if none was found.  A negative code is
  * returned on error. */
-int vhost_get_vq_desc(struct vhost_dev *dev, struct vhost_virtqueue *vq,
+int vhost_get_vq_desc(struct vhost_virtqueue *vq,
                      struct iovec iov[], unsigned int iov_size,
                      unsigned int *out_num, unsigned int *in_num,
                      struct vhost_log *log, unsigned int *log_num)
@@ -1272,7 +1275,7 @@ int vhost_get_vq_desc(struct vhost_dev *dev, struct vhost_virtqueue *vq,
                        return -EFAULT;
                }
                if (desc.flags & VRING_DESC_F_INDIRECT) {
-                       ret = get_indirect(dev, vq, iov, iov_size,
+                       ret = get_indirect(vq, iov, iov_size,
                                           out_num, in_num,
                                           log, log_num, &desc);
                        if (unlikely(ret < 0)) {
@@ -1283,7 +1286,7 @@ int vhost_get_vq_desc(struct vhost_dev *dev, struct vhost_virtqueue *vq,
                        continue;
                }
 
-               ret = translate_desc(dev, desc.addr, desc.len, iov + iov_count,
+               ret = translate_desc(vq, desc.addr, desc.len, iov + iov_count,
                                     iov_size - iov_count);
                if (unlikely(ret < 0)) {
                        vq_err(vq, "Translation failure %d descriptor idx %d\n",
@@ -1426,11 +1429,11 @@ static bool vhost_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq)
         * interrupts. */
        smp_mb();
 
-       if (vhost_has_feature(dev, VIRTIO_F_NOTIFY_ON_EMPTY) &&
+       if (vhost_has_feature(vq, VIRTIO_F_NOTIFY_ON_EMPTY) &&
            unlikely(vq->avail_idx == vq->last_avail_idx))
                return true;
 
-       if (!vhost_has_feature(dev, VIRTIO_RING_F_EVENT_IDX)) {
+       if (!vhost_has_feature(vq, VIRTIO_RING_F_EVENT_IDX)) {
                __u16 flags;
                if (__get_user(flags, &vq->avail->flags)) {
                        vq_err(vq, "Failed to get flags");
@@ -1491,7 +1494,7 @@ bool vhost_enable_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq)
        if (!(vq->used_flags & VRING_USED_F_NO_NOTIFY))
                return false;
        vq->used_flags &= ~VRING_USED_F_NO_NOTIFY;
-       if (!vhost_has_feature(dev, VIRTIO_RING_F_EVENT_IDX)) {
+       if (!vhost_has_feature(vq, VIRTIO_RING_F_EVENT_IDX)) {
                r = vhost_update_used_flags(vq);
                if (r) {
                        vq_err(vq, "Failed to enable notification at %p: %d\n",
@@ -1528,7 +1531,7 @@ void vhost_disable_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq)
        if (vq->used_flags & VRING_USED_F_NO_NOTIFY)
                return;
        vq->used_flags |= VRING_USED_F_NO_NOTIFY;
-       if (!vhost_has_feature(dev, VIRTIO_RING_F_EVENT_IDX)) {
+       if (!vhost_has_feature(vq, VIRTIO_RING_F_EVENT_IDX)) {
                r = vhost_update_used_flags(vq);
                if (r)
                        vq_err(vq, "Failed to enable notification at %p: %d\n",
index 35eeb2a1badaf8e90e2af8215ff376b23a2e9853..3eda654b8f5a0d8191060f553f3687983505edbe 100644 (file)
@@ -104,20 +104,18 @@ struct vhost_virtqueue {
        struct iovec *indirect;
        struct vring_used_elem *heads;
        /* Protected by virtqueue mutex. */
+       struct vhost_memory *memory;
        void *private_data;
+       unsigned acked_features;
        /* Log write descriptors */
        void __user *log_base;
        struct vhost_log *log;
 };
 
 struct vhost_dev {
-       /* Readers use RCU to access memory table pointer
-        * log base pointer and features.
-        * Writers use mutex below.*/
-       struct vhost_memory __rcu *memory;
+       struct vhost_memory *memory;
        struct mm_struct *mm;
        struct mutex mutex;
-       unsigned acked_features;
        struct vhost_virtqueue **vqs;
        int nvqs;
        struct file *log_file;
@@ -140,7 +138,7 @@ long vhost_vring_ioctl(struct vhost_dev *d, int ioctl, void __user *argp);
 int vhost_vq_access_ok(struct vhost_virtqueue *vq);
 int vhost_log_access_ok(struct vhost_dev *);
 
-int vhost_get_vq_desc(struct vhost_dev *, struct vhost_virtqueue *,
+int vhost_get_vq_desc(struct vhost_virtqueue *,
                      struct iovec iov[], unsigned int iov_count,
                      unsigned int *out_num, unsigned int *in_num,
                      struct vhost_log *log, unsigned int *log_num);
@@ -174,13 +172,8 @@ enum {
                         (1ULL << VHOST_F_LOG_ALL),
 };
 
-static inline int vhost_has_feature(struct vhost_dev *dev, int bit)
+static inline int vhost_has_feature(struct vhost_virtqueue *vq, int bit)
 {
-       unsigned acked_features;
-
-       /* TODO: check that we are running from vhost_worker or dev mutex is
-        * held? */
-       acked_features = rcu_dereference_index_check(dev->acked_features, 1);
-       return acked_features & (1 << bit);
+       return vq->acked_features & (1 << bit);
 }
 #endif
index c7b4f0f927b1b710c8b8e95811743e76ed0ee977..8bf495ffb020789811358eef574b6aba446cb141 100644 (file)
@@ -20,6 +20,7 @@ source "drivers/char/agp/Kconfig"
 source "drivers/gpu/vga/Kconfig"
 
 source "drivers/gpu/host1x/Kconfig"
+source "drivers/gpu/ipu-v3/Kconfig"
 
 menu "Direct Rendering Manager"
 source "drivers/gpu/drm/Kconfig"
index 5a3eb2ecb525c2b52a2d1f5d8decb688a93bb99d..5d449059a55637daa07bc5908e52e6a7bbf69f8d 100644 (file)
@@ -29,7 +29,7 @@ if LCD_CLASS_DEVICE
 
 config LCD_CORGI
        tristate "LCD Panel support for SHARP corgi/spitz model"
-       depends on SPI_MASTER && PXA_SHARPSL
+       depends on SPI_MASTER && PXA_SHARPSL && BACKLIGHT_CLASS_DEVICE
        help
          Say y here to support the LCD panels usually found on SHARP
          corgi (C7x0) and spitz (Cxx00) models.
@@ -370,7 +370,7 @@ config BACKLIGHT_AAT2870
 
 config BACKLIGHT_LM3630A
        tristate "Backlight Driver for LM3630A"
-       depends on BACKLIGHT_CLASS_DEVICE && I2C
+       depends on BACKLIGHT_CLASS_DEVICE && I2C && PWM
        select REGMAP_I2C
        help
          This supports TI LM3630A Backlight Driver
@@ -386,14 +386,14 @@ config BACKLIGHT_LM3639
 
 config BACKLIGHT_LP855X
        tristate "Backlight driver for TI LP855X"
-       depends on BACKLIGHT_CLASS_DEVICE && I2C
+       depends on BACKLIGHT_CLASS_DEVICE && I2C && PWM
        help
          This supports TI LP8550, LP8551, LP8552, LP8553, LP8555, LP8556 and
          LP8557 backlight driver.
 
 config BACKLIGHT_LP8788
        tristate "Backlight driver for TI LP8788 MFD"
-       depends on BACKLIGHT_CLASS_DEVICE && MFD_LP8788
+       depends on BACKLIGHT_CLASS_DEVICE && MFD_LP8788 && PWM
        help
          This supports TI LP8788 backlight driver.
 
index a2eba12e1cb78b36fcfd96a0f38ce98bce2406a2..1cea68848f1ad9da9426dfd47e5e5fc4df4039ca 100644 (file)
@@ -38,7 +38,8 @@ static int gpio_backlight_update_status(struct backlight_device *bl)
            bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
                brightness = 0;
 
-       gpio_set_value(gbl->gpio, brightness ? gbl->active : !gbl->active);
+       gpio_set_value_cansleep(gbl->gpio,
+                               brightness ? gbl->active : !gbl->active);
 
        return 0;
 }
index 510a1bcf76f11d95a5d936099846abc5c4c49bdf..2d6d48196c6d09f385a54c80a0ff1692a99351c5 100644 (file)
@@ -703,7 +703,7 @@ static ssize_t s6e63m0_sysfs_show_gamma_table(struct device *dev,
        struct s6e63m0 *lcd = dev_get_drvdata(dev);
        char temp[3];
 
-       sprintf(temp, "%d\n", lcd->gamma_table_count);
+       sprintf(temp, "%u\n", lcd->gamma_table_count);
        strcpy(buf, temp);
 
        return strlen(buf);
index 1501979099dce08b1b17cff6d0dc281135a9dcb0..c2c8eb668784f08766444b1cabb159c37c92f455 100644 (file)
@@ -1215,7 +1215,7 @@ static ssize_t sm501fb_crtsrc_store(struct device *dev,
 }
 
 /* Prepare the device_attr for registration with sysfs later */
-static DEVICE_ATTR(crt_src, 0666, sm501fb_crtsrc_show, sm501fb_crtsrc_store);
+static DEVICE_ATTR(crt_src, 0664, sm501fb_crtsrc_show, sm501fb_crtsrc_store);
 
 /* sm501fb_show_regs
  *
index 1e443629f76d725f7223e4d9234b0cf47e942b0e..4d08f45a9c29aeba18fe4c174bce0a644a090d57 100644 (file)
@@ -865,4 +865,19 @@ bool virtqueue_is_broken(struct virtqueue *_vq)
 }
 EXPORT_SYMBOL_GPL(virtqueue_is_broken);
 
+/*
+ * This should prevent the device from being used, allowing drivers to
+ * recover.  You may need to grab appropriate locks to flush.
+ */
+void virtio_break_device(struct virtio_device *dev)
+{
+       struct virtqueue *_vq;
+
+       list_for_each_entry(_vq, &dev->vqs, list) {
+               struct vring_virtqueue *vq = to_vvq(_vq);
+               vq->broken = true;
+       }
+}
+EXPORT_SYMBOL_GPL(virtio_break_device);
+
 MODULE_LICENSE("GPL");
index cbb09ce9730ac0494dc43fbef5d7d6f2ddd05b0b..5747417069cadf3501cf8210a7cb834b6b29c942 100644 (file)
@@ -4,10 +4,10 @@
 
 # Create $(fwabs) from $(CONFIG_EXTRA_FIRMWARE_DIR) -- if it doesn't have a
 # leading /, it's relative to $(srctree).
-fwdir := $(subst ",,$(CONFIG_EXTRA_FIRMWARE_DIR))
+fwdir := $(subst $(quote),,$(CONFIG_EXTRA_FIRMWARE_DIR))
 fwabs := $(addprefix $(srctree)/,$(filter-out /%,$(fwdir)))$(filter /%,$(fwdir))
 
-fw-external-y := $(subst ",,$(CONFIG_EXTRA_FIRMWARE))
+fw-external-y := $(subst $(quote),,$(CONFIG_EXTRA_FIRMWARE))
 
 # There are three cases to care about:
 # 1. Building kernel with CONFIG_FIRMWARE_IN_KERNEL=y -- $(fw-shipped-y) should
@@ -138,12 +138,6 @@ fw-shipped-$(CONFIG_YAM) += yam/1200.bin yam/9600.bin
 
 fw-shipped-all := $(fw-shipped-y) $(fw-shipped-m) $(fw-shipped-)
 
-# Directories which we _might_ need to create, so we have a rule for them.
-firmware-dirs := $(sort $(addprefix $(objtree)/$(obj)/,$(dir $(fw-external-y) $(fw-shipped-all))))
-
-quiet_cmd_mkdir = MKDIR   $(patsubst $(objtree)/%,%,$@)
-      cmd_mkdir = mkdir -p $@
-
 quiet_cmd_ihex  = IHEX    $@
       cmd_ihex  = $(OBJCOPY) -Iihex -Obinary $< $@
 
@@ -184,21 +178,10 @@ wordsize_deps := $(wildcard include/config/64bit.h include/config/32bit.h \
                include/config/superh32.h include/config/superh64.h \
                include/config/x86_32.h include/config/x86_64.h)
 
-# Workaround for make < 3.81, where .SECONDEXPANSION doesn't work.
-# It'll end up depending on these targets, so make them a PHONY rule which
-# depends on _all_ the directories in $(firmware-dirs), and it'll work out OK.
-PHONY += $(objtree)/$$(%) $(objtree)/$(obj)/$$(%)
-$(objtree)/$$(%) $(objtree)/$(obj)/$$(%): $(firmware-dirs)
-       @true
-
-# For the $$(dir %) trick, where we need % to be expanded first.
-.SECONDEXPANSION:
-
-$(patsubst %,$(obj)/%.gen.S, $(fw-shipped-y)): %: $(wordsize_deps) \
-               | $(objtree)/$$(dir %)
+$(patsubst %,$(obj)/%.gen.S, $(fw-shipped-y)): %: $(wordsize_deps)
        $(call cmd,fwbin,$(patsubst %.gen.S,%,$@))
 $(patsubst %,$(obj)/%.gen.S, $(fw-external-y)): %: $(wordsize_deps) \
-               include/config/extra/firmware/dir.h | $(objtree)/$$(dir %)
+               include/config/extra/firmware/dir.h
        $(call cmd,fwbin,$(fwabs)/$(patsubst $(obj)/%.gen.S,%,$@))
 
 # The .o files depend on the binaries directly; the .S files don't.
@@ -207,7 +190,7 @@ $(patsubst %,$(obj)/%.gen.o, $(fw-external-y)): $(obj)/%.gen.o: $(fwdir)/%
 
 # .ihex is used just as a simple way to hold binary files in a source tree
 # where binaries are frowned upon. They are directly converted with objcopy.
-$(obj)/%: $(obj)/%.ihex | $(objtree)/$(obj)/$$(dir %)
+$(obj)/%: $(obj)/%.ihex
        $(call cmd,ihex)
 
 # Don't depend on ihex2fw if we're installing and it already exists.
@@ -226,16 +209,13 @@ endif
 # is actually meaningful, because the firmware has to be loaded in a certain
 # order rather than as a single binary blob. Thus, we convert them into our
 # more compact binary representation of ihex records (<linux/ihex.h>)
-$(obj)/%.fw: $(obj)/%.HEX $(ihex2fw_dep) | $(objtree)/$(obj)/$$(dir %)
+$(obj)/%.fw: $(obj)/%.HEX $(ihex2fw_dep)
        $(call cmd,ihex2fw)
 
 # .H16 is our own modified form of Intel HEX, with 16-bit length for records.
-$(obj)/%.fw: $(obj)/%.H16 $(ihex2fw_dep) | $(objtree)/$(obj)/$$(dir %)
+$(obj)/%.fw: $(obj)/%.H16 $(ihex2fw_dep)
        $(call cmd,h16tofw)
 
-$(firmware-dirs):
-       $(call cmd,mkdir)
-
 obj-y                           += $(patsubst %,%.gen.o, $(fw-external-y))
 obj-$(CONFIG_FIRMWARE_IN_KERNEL) += $(patsubst %,%.gen.o, $(fw-shipped-y))
 
index c71e88602ff49a5836a0698a033e3042bdf95f9c..cc1cfae726b38fdc645fc7af79b4879371727b0a 100644 (file)
@@ -259,8 +259,7 @@ static int v9fs_launder_page(struct page *page)
  *
  */
 static ssize_t
-v9fs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
-              loff_t pos, unsigned long nr_segs)
+v9fs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, loff_t pos)
 {
        /*
         * FIXME
@@ -269,7 +268,7 @@ v9fs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
         */
        p9_debug(P9_DEBUG_VFS, "v9fs_direct_IO: v9fs_direct_IO (%s) off/no(%lld/%lu) EINVAL\n",
                 iocb->ki_filp->f_path.dentry->d_name.name,
-                (long long)pos, nr_segs);
+                (long long)pos, iter->nr_segs);
 
        return -EINVAL;
 }
index 96e550760699a8895cbb58a4dd26a18ab1e050a3..520c11c2dcca4c9ff31a591600ca0c8ced52481c 100644 (file)
@@ -692,7 +692,7 @@ v9fs_cached_file_read(struct file *filp, char __user *data, size_t count,
 {
        if (filp->f_flags & O_DIRECT)
                return v9fs_direct_read(filp, data, count, offset);
-       return do_sync_read(filp, data, count, offset);
+       return new_sync_read(filp, data, count, offset);
 }
 
 /**
@@ -760,7 +760,7 @@ err_out:
 
 buff_write:
        mutex_unlock(&inode->i_mutex);
-       return do_sync_write(filp, data, count, offsetp);
+       return new_sync_write(filp, data, count, offsetp);
 }
 
 /**
@@ -778,7 +778,7 @@ v9fs_cached_file_write(struct file *filp, const char __user * data,
 
        if (filp->f_flags & O_DIRECT)
                return v9fs_direct_write(filp, data, count, offset);
-       return do_sync_write(filp, data, count, offset);
+       return new_sync_write(filp, data, count, offset);
 }
 
 
@@ -847,8 +847,8 @@ const struct file_operations v9fs_cached_file_operations = {
        .llseek = generic_file_llseek,
        .read = v9fs_cached_file_read,
        .write = v9fs_cached_file_write,
-       .aio_read = generic_file_aio_read,
-       .aio_write = generic_file_aio_write,
+       .read_iter = generic_file_read_iter,
+       .write_iter = generic_file_write_iter,
        .open = v9fs_file_open,
        .release = v9fs_dir_release,
        .lock = v9fs_file_lock,
@@ -860,8 +860,8 @@ const struct file_operations v9fs_cached_file_operations_dotl = {
        .llseek = generic_file_llseek,
        .read = v9fs_cached_file_read,
        .write = v9fs_cached_file_write,
-       .aio_read = generic_file_aio_read,
-       .aio_write = generic_file_aio_write,
+       .read_iter = generic_file_read_iter,
+       .write_iter = generic_file_write_iter,
        .open = v9fs_file_open,
        .release = v9fs_dir_release,
        .lock = v9fs_file_lock_dotl,
index a36da5382b40dc9c09ab6aa59762d9c1a7808b72..07c9edce5aa768ddeb7ae203c8c9cab489ea8417 100644 (file)
 
 const struct file_operations adfs_file_operations = {
        .llseek         = generic_file_llseek,
-       .read           = do_sync_read,
-       .aio_read       = generic_file_aio_read,
+       .read           = new_sync_read,
+       .read_iter      = generic_file_read_iter,
        .mmap           = generic_file_mmap,
        .fsync          = generic_file_fsync,
-       .write          = do_sync_write,
-       .aio_write      = generic_file_aio_write,
+       .write          = new_sync_write,
+       .write_iter     = generic_file_write_iter,
        .splice_read    = generic_file_splice_read,
 };
 
index 0270303388ee669515c8829f7370da24db6d16e2..a7fe57d2cd9a0aa6a59df2cd90778127ebc2bbc3 100644 (file)
@@ -27,10 +27,10 @@ static int affs_file_release(struct inode *inode, struct file *filp);
 
 const struct file_operations affs_file_operations = {
        .llseek         = generic_file_llseek,
-       .read           = do_sync_read,
-       .aio_read       = generic_file_aio_read,
-       .write          = do_sync_write,
-       .aio_write      = generic_file_aio_write,
+       .read           = new_sync_read,
+       .read_iter      = generic_file_read_iter,
+       .write          = new_sync_write,
+       .write_iter     = generic_file_write_iter,
        .mmap           = generic_file_mmap,
        .open           = affs_file_open,
        .release        = affs_file_release,
index 66d50fe2ee459a887511381e8e375db72d2bf1f3..932ce07948b387d7aa75dffb3a9de1e21f1f50b8 100644 (file)
@@ -31,10 +31,10 @@ const struct file_operations afs_file_operations = {
        .open           = afs_open,
        .release        = afs_release,
        .llseek         = generic_file_llseek,
-       .read           = do_sync_read,
-       .write          = do_sync_write,
-       .aio_read       = generic_file_aio_read,
-       .aio_write      = afs_file_write,
+       .read           = new_sync_read,
+       .write          = new_sync_write,
+       .read_iter      = generic_file_read_iter,
+       .write_iter     = afs_file_write,
        .mmap           = generic_file_readonly_mmap,
        .splice_read    = generic_file_splice_read,
        .fsync          = afs_fsync,
index 590b55f46d61dd1ca169ff78a3dbf4f5d32b813c..71d5982312f3d11dd6e3dd23079e5c6bef7c23a6 100644 (file)
@@ -747,8 +747,7 @@ extern int afs_write_end(struct file *file, struct address_space *mapping,
 extern int afs_writepage(struct page *, struct writeback_control *);
 extern int afs_writepages(struct address_space *, struct writeback_control *);
 extern void afs_pages_written_back(struct afs_vnode *, struct afs_call *);
-extern ssize_t afs_file_write(struct kiocb *, const struct iovec *,
-                             unsigned long, loff_t);
+extern ssize_t afs_file_write(struct kiocb *, struct iov_iter *);
 extern int afs_writeback_all(struct afs_vnode *);
 extern int afs_fsync(struct file *, loff_t, loff_t, int);
 
index a890db4b9898fc1d888c5e7285da55db85e4da54..ab6adfd525168800524349791c061512a47a7d81 100644 (file)
@@ -625,15 +625,14 @@ void afs_pages_written_back(struct afs_vnode *vnode, struct afs_call *call)
 /*
  * write to an AFS file
  */
-ssize_t afs_file_write(struct kiocb *iocb, const struct iovec *iov,
-                      unsigned long nr_segs, loff_t pos)
+ssize_t afs_file_write(struct kiocb *iocb, struct iov_iter *from)
 {
        struct afs_vnode *vnode = AFS_FS_I(file_inode(iocb->ki_filp));
        ssize_t result;
-       size_t count = iov_length(iov, nr_segs);
+       size_t count = iov_iter_count(from);
 
-       _enter("{%x.%u},{%zu},%lu,",
-              vnode->fid.vid, vnode->fid.vnode, count, nr_segs);
+       _enter("{%x.%u},{%zu},",
+              vnode->fid.vid, vnode->fid.vnode, count);
 
        if (IS_SWAPFILE(&vnode->vfs_inode)) {
                printk(KERN_INFO
@@ -644,7 +643,7 @@ ssize_t afs_file_write(struct kiocb *iocb, const struct iovec *iov,
        if (!count)
                return 0;
 
-       result = generic_file_aio_write(iocb, iov, nr_segs, pos);
+       result = generic_file_write_iter(iocb, from);
        if (IS_ERR_VALUE(result)) {
                _leave(" = %zd", result);
                return result;
index a0ed6c7d2cd2a3e91a5d12e48af705d75afe315a..4f078c054b41608fe3a8ed325c55d59898b049cb 100644 (file)
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -477,7 +477,7 @@ void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel)
 }
 EXPORT_SYMBOL(kiocb_set_cancel_fn);
 
-static int kiocb_cancel(struct kioctx *ctx, struct kiocb *kiocb)
+static int kiocb_cancel(struct kiocb *kiocb)
 {
        kiocb_cancel_fn *old, *cancel;
 
@@ -538,7 +538,7 @@ static void free_ioctx_users(struct percpu_ref *ref)
                                       struct kiocb, ki_list);
 
                list_del_init(&req->ki_list);
-               kiocb_cancel(ctx, req);
+               kiocb_cancel(req);
        }
 
        spin_unlock_irq(&ctx->ctx_lock);
@@ -727,42 +727,42 @@ err:
  *     when the processes owning a context have all exited to encourage
  *     the rapid destruction of the kioctx.
  */
-static void kill_ioctx(struct mm_struct *mm, struct kioctx *ctx,
+static int kill_ioctx(struct mm_struct *mm, struct kioctx *ctx,
                struct completion *requests_done)
 {
-       if (!atomic_xchg(&ctx->dead, 1)) {
-               struct kioctx_table *table;
+       struct kioctx_table *table;
 
-               spin_lock(&mm->ioctx_lock);
-               rcu_read_lock();
-               table = rcu_dereference(mm->ioctx_table);
+       if (atomic_xchg(&ctx->dead, 1))
+               return -EINVAL;
 
-               WARN_ON(ctx != table->table[ctx->id]);
-               table->table[ctx->id] = NULL;
-               rcu_read_unlock();
-               spin_unlock(&mm->ioctx_lock);
 
-               /* percpu_ref_kill() will do the necessary call_rcu() */
-               wake_up_all(&ctx->wait);
+       spin_lock(&mm->ioctx_lock);
+       rcu_read_lock();
+       table = rcu_dereference(mm->ioctx_table);
 
-               /*
-                * It'd be more correct to do this in free_ioctx(), after all
-                * the outstanding kiocbs have finished - but by then io_destroy
-                * has already returned, so io_setup() could potentially return
-                * -EAGAIN with no ioctxs actually in use (as far as userspace
-                *  could tell).
-                */
-               aio_nr_sub(ctx->max_reqs);
+       WARN_ON(ctx != table->table[ctx->id]);
+       table->table[ctx->id] = NULL;
+       rcu_read_unlock();
+       spin_unlock(&mm->ioctx_lock);
 
-               if (ctx->mmap_size)
-                       vm_munmap(ctx->mmap_base, ctx->mmap_size);
+       /* percpu_ref_kill() will do the necessary call_rcu() */
+       wake_up_all(&ctx->wait);
 
-               ctx->requests_done = requests_done;
-               percpu_ref_kill(&ctx->users);
-       } else {
-               if (requests_done)
-                       complete(requests_done);
-       }
+       /*
+        * It'd be more correct to do this in free_ioctx(), after all
+        * the outstanding kiocbs have finished - but by then io_destroy
+        * has already returned, so io_setup() could potentially return
+        * -EAGAIN with no ioctxs actually in use (as far as userspace
+        *  could tell).
+        */
+       aio_nr_sub(ctx->max_reqs);
+
+       if (ctx->mmap_size)
+               vm_munmap(ctx->mmap_base, ctx->mmap_size);
+
+       ctx->requests_done = requests_done;
+       percpu_ref_kill(&ctx->users);
+       return 0;
 }
 
 /* wait_on_sync_kiocb:
@@ -1219,21 +1219,23 @@ SYSCALL_DEFINE1(io_destroy, aio_context_t, ctx)
        if (likely(NULL != ioctx)) {
                struct completion requests_done =
                        COMPLETION_INITIALIZER_ONSTACK(requests_done);
+               int ret;
 
                /* Pass requests_done to kill_ioctx() where it can be set
                 * in a thread-safe way. If we try to set it here then we have
                 * a race condition if two io_destroy() called simultaneously.
                 */
-               kill_ioctx(current->mm, ioctx, &requests_done);
+               ret = kill_ioctx(current->mm, ioctx, &requests_done);
                percpu_ref_put(&ioctx->users);
 
                /* Wait until all IO for the context are done. Otherwise kernel
                 * keep using user-space buffers even if user thinks the context
                 * is destroyed.
                 */
-               wait_for_completion(&requests_done);
+               if (!ret)
+                       wait_for_completion(&requests_done);
 
-               return 0;
+               return ret;
        }
        pr_debug("EINVAL: io_destroy: invalid context id\n");
        return -EINVAL;
@@ -1241,6 +1243,7 @@ SYSCALL_DEFINE1(io_destroy, aio_context_t, ctx)
 
 typedef ssize_t (aio_rw_op)(struct kiocb *, const struct iovec *,
                            unsigned long, loff_t);
+typedef ssize_t (rw_iter_op)(struct kiocb *, struct iov_iter *);
 
 static ssize_t aio_setup_vectored_rw(struct kiocb *kiocb,
                                     int rw, char __user *buf,
@@ -1298,7 +1301,9 @@ static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode,
        int rw;
        fmode_t mode;
        aio_rw_op *rw_op;
+       rw_iter_op *iter_op;
        struct iovec inline_vec, *iovec = &inline_vec;
+       struct iov_iter iter;
 
        switch (opcode) {
        case IOCB_CMD_PREAD:
@@ -1306,6 +1311,7 @@ static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode,
                mode    = FMODE_READ;
                rw      = READ;
                rw_op   = file->f_op->aio_read;
+               iter_op = file->f_op->read_iter;
                goto rw_common;
 
        case IOCB_CMD_PWRITE:
@@ -1313,12 +1319,13 @@ static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode,
                mode    = FMODE_WRITE;
                rw      = WRITE;
                rw_op   = file->f_op->aio_write;
+               iter_op = file->f_op->write_iter;
                goto rw_common;
 rw_common:
                if (unlikely(!(file->f_mode & mode)))
                        return -EBADF;
 
-               if (!rw_op)
+               if (!rw_op && !iter_op)
                        return -EINVAL;
 
                ret = (opcode == IOCB_CMD_PREADV ||
@@ -1347,7 +1354,12 @@ rw_common:
                if (rw == WRITE)
                        file_start_write(file);
 
-               ret = rw_op(req, iovec, nr_segs, req->ki_pos);
+               if (iter_op) {
+                       iov_iter_init(&iter, rw, iovec, nr_segs, req->ki_nbytes);
+                       ret = iter_op(req, &iter);
+               } else {
+                       ret = rw_op(req, iovec, nr_segs, req->ki_pos);
+               }
 
                if (rw == WRITE)
                        file_end_write(file);
@@ -1585,7 +1597,7 @@ SYSCALL_DEFINE3(io_cancel, aio_context_t, ctx_id, struct iocb __user *, iocb,
 
        kiocb = lookup_kiocb(ctx, iocb, key);
        if (kiocb)
-               ret = kiocb_cancel(ctx, kiocb);
+               ret = kiocb_cancel(kiocb);
        else
                ret = -EINVAL;
 
index ae28922183357d4c0e4d491a452919e125ba8bc3..e7f88ace1a2508d260ea8feae3addc5e2752980c 100644 (file)
 
 const struct file_operations bfs_file_operations = {
        .llseek         = generic_file_llseek,
-       .read           = do_sync_read,
-       .aio_read       = generic_file_aio_read,
-       .write          = do_sync_write,
-       .aio_write      = generic_file_aio_write,
+       .read           = new_sync_read,
+       .read_iter      = generic_file_read_iter,
+       .write          = new_sync_write,
+       .write_iter     = generic_file_write_iter,
        .mmap           = generic_file_mmap,
        .splice_read    = generic_file_splice_read,
 };
index 83fba15cc394071a53b57245b95942c18177d9c7..6d7274619bf916c2dcf0d7744ba8d888d948d711 100644 (file)
@@ -165,14 +165,15 @@ blkdev_get_block(struct inode *inode, sector_t iblock,
 }
 
 static ssize_t
-blkdev_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
-                       loff_t offset, unsigned long nr_segs)
+blkdev_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter,
+                       loff_t offset)
 {
        struct file *file = iocb->ki_filp;
        struct inode *inode = file->f_mapping->host;
 
-       return __blockdev_direct_IO(rw, iocb, inode, I_BDEV(inode), iov, offset,
-                                   nr_segs, blkdev_get_block, NULL, NULL, 0);
+       return __blockdev_direct_IO(rw, iocb, inode, I_BDEV(inode), iter,
+                                   offset, blkdev_get_block,
+                                   NULL, NULL, 0);
 }
 
 int __sync_blockdev(struct block_device *bdev, int wait)
@@ -1571,43 +1572,38 @@ static long block_ioctl(struct file *file, unsigned cmd, unsigned long arg)
  * Does not take i_mutex for the write and thus is not for general purpose
  * use.
  */
-ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov,
-                        unsigned long nr_segs, loff_t pos)
+ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
 {
        struct file *file = iocb->ki_filp;
        struct blk_plug plug;
        ssize_t ret;
 
-       BUG_ON(iocb->ki_pos != pos);
-
        blk_start_plug(&plug);
-       ret = __generic_file_aio_write(iocb, iov, nr_segs);
+       ret = __generic_file_write_iter(iocb, from);
        if (ret > 0) {
                ssize_t err;
-
-               err = generic_write_sync(file, pos, ret);
+               err = generic_write_sync(file, iocb->ki_pos - ret, ret);
                if (err < 0)
                        ret = err;
        }
        blk_finish_plug(&plug);
        return ret;
 }
-EXPORT_SYMBOL_GPL(blkdev_aio_write);
+EXPORT_SYMBOL_GPL(blkdev_write_iter);
 
-static ssize_t blkdev_aio_read(struct kiocb *iocb, const struct iovec *iov,
-                        unsigned long nr_segs, loff_t pos)
+static ssize_t blkdev_read_iter(struct kiocb *iocb, struct iov_iter *to)
 {
        struct file *file = iocb->ki_filp;
        struct inode *bd_inode = file->f_mapping->host;
        loff_t size = i_size_read(bd_inode);
+       loff_t pos = iocb->ki_pos;
 
        if (pos >= size)
                return 0;
 
        size -= pos;
-       if (size < iocb->ki_nbytes)
-               nr_segs = iov_shorten((struct iovec *)iov, nr_segs, size);
-       return generic_file_aio_read(iocb, iov, nr_segs, pos);
+       iov_iter_truncate(to, size);
+       return generic_file_read_iter(iocb, to);
 }
 
 /*
@@ -1639,10 +1635,10 @@ const struct file_operations def_blk_fops = {
        .open           = blkdev_open,
        .release        = blkdev_close,
        .llseek         = block_llseek,
-       .read           = do_sync_read,
-       .write          = do_sync_write,
-       .aio_read       = blkdev_aio_read,
-       .aio_write      = blkdev_aio_write,
+       .read           = new_sync_read,
+       .write          = new_sync_write,
+       .read_iter      = blkdev_read_iter,
+       .write_iter     = blkdev_write_iter,
        .mmap           = generic_file_mmap,
        .fsync          = blkdev_fsync,
        .unlocked_ioctl = block_ioctl,
@@ -1650,7 +1646,7 @@ const struct file_operations def_blk_fops = {
        .compat_ioctl   = compat_blkdev_ioctl,
 #endif
        .splice_read    = generic_file_splice_read,
-       .splice_write   = generic_file_splice_write,
+       .splice_write   = iter_file_splice_write,
 };
 
 int ioctl_by_bdev(struct block_device *bdev, unsigned cmd, unsigned long arg)
index f25a9092b946758af5f063591011ff30acf8a623..a389820d158b5b7ae64a2c6dfecabbb6ed3ed9bb 100644 (file)
@@ -2354,7 +2354,7 @@ int end_extent_writepage(struct page *page, int err, u64 start, u64 end)
 {
        int uptodate = (err == 0);
        struct extent_io_tree *tree;
-       int ret;
+       int ret = 0;
 
        tree = &BTRFS_I(page->mapping->host)->io_tree;
 
@@ -5068,6 +5068,43 @@ void read_extent_buffer(struct extent_buffer *eb, void *dstv,
        }
 }
 
+int read_extent_buffer_to_user(struct extent_buffer *eb, void __user *dstv,
+                       unsigned long start,
+                       unsigned long len)
+{
+       size_t cur;
+       size_t offset;
+       struct page *page;
+       char *kaddr;
+       char __user *dst = (char __user *)dstv;
+       size_t start_offset = eb->start & ((u64)PAGE_CACHE_SIZE - 1);
+       unsigned long i = (start_offset + start) >> PAGE_CACHE_SHIFT;
+       int ret = 0;
+
+       WARN_ON(start > eb->len);
+       WARN_ON(start + len > eb->start + eb->len);
+
+       offset = (start_offset + start) & (PAGE_CACHE_SIZE - 1);
+
+       while (len > 0) {
+               page = extent_buffer_page(eb, i);
+
+               cur = min(len, (PAGE_CACHE_SIZE - offset));
+               kaddr = page_address(page);
+               if (copy_to_user(dst, kaddr + offset, cur)) {
+                       ret = -EFAULT;
+                       break;
+               }
+
+               dst += cur;
+               len -= cur;
+               offset = 0;
+               i++;
+       }
+
+       return ret;
+}
+
 int map_private_extent_buffer(struct extent_buffer *eb, unsigned long start,
                               unsigned long min_len, char **map,
                               unsigned long *map_start,
index 8b63f2d46518e82a9e140a398c2bd12df3a7f93f..15ce5f2a2b624ff0a28d9276637084327a9afdcd 100644 (file)
@@ -304,6 +304,9 @@ int memcmp_extent_buffer(struct extent_buffer *eb, const void *ptrv,
 void read_extent_buffer(struct extent_buffer *eb, void *dst,
                        unsigned long start,
                        unsigned long len);
+int read_extent_buffer_to_user(struct extent_buffer *eb, void __user *dst,
+                              unsigned long start,
+                              unsigned long len);
 void write_extent_buffer(struct extent_buffer *eb, const void *src,
                         unsigned long start, unsigned long len);
 void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src,
index e472441feb5de80ce68c85e6f892531893706a13..1f2b99cb55eaef682c51ae3c8b227e88aafbf7e7 100644 (file)
@@ -448,7 +448,7 @@ static noinline int btrfs_copy_from_user(loff_t pos, int num_pages,
                write_bytes -= copied;
                total_copied += copied;
 
-               /* Return to btrfs_file_aio_write to fault page */
+               /* Return to btrfs_file_write_iter to fault page */
                if (unlikely(copied == 0))
                        break;
 
@@ -1675,27 +1675,22 @@ again:
 }
 
 static ssize_t __btrfs_direct_write(struct kiocb *iocb,
-                                   const struct iovec *iov,
-                                   unsigned long nr_segs, loff_t pos,
-                                   size_t count, size_t ocount)
+                                   struct iov_iter *from,
+                                   loff_t pos)
 {
        struct file *file = iocb->ki_filp;
-       struct iov_iter i;
        ssize_t written;
        ssize_t written_buffered;
        loff_t endbyte;
        int err;
 
-       written = generic_file_direct_write(iocb, iov, &nr_segs, pos,
-                                           count, ocount);
+       written = generic_file_direct_write(iocb, from, pos);
 
-       if (written < 0 || written == count)
+       if (written < 0 || !iov_iter_count(from))
                return written;
 
        pos += written;
-       count -= written;
-       iov_iter_init(&i, iov, nr_segs, count, written);
-       written_buffered = __btrfs_buffered_write(file, &i, pos);
+       written_buffered = __btrfs_buffered_write(file, from, pos);
        if (written_buffered < 0) {
                err = written_buffered;
                goto out;
@@ -1730,9 +1725,8 @@ static void update_time_for_write(struct inode *inode)
                inode_inc_iversion(inode);
 }
 
-static ssize_t btrfs_file_aio_write(struct kiocb *iocb,
-                                   const struct iovec *iov,
-                                   unsigned long nr_segs, loff_t pos)
+static ssize_t btrfs_file_write_iter(struct kiocb *iocb,
+                                   struct iov_iter *from)
 {
        struct file *file = iocb->ki_filp;
        struct inode *inode = file_inode(file);
@@ -1741,18 +1735,12 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb,
        u64 end_pos;
        ssize_t num_written = 0;
        ssize_t err = 0;
-       size_t count, ocount;
+       size_t count = iov_iter_count(from);
        bool sync = (file->f_flags & O_DSYNC) || IS_SYNC(file->f_mapping->host);
+       loff_t pos = iocb->ki_pos;
 
        mutex_lock(&inode->i_mutex);
 
-       err = generic_segment_checks(iov, &nr_segs, &ocount, VERIFY_READ);
-       if (err) {
-               mutex_unlock(&inode->i_mutex);
-               goto out;
-       }
-       count = ocount;
-
        current->backing_dev_info = inode->i_mapping->backing_dev_info;
        err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode));
        if (err) {
@@ -1765,6 +1753,8 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb,
                goto out;
        }
 
+       iov_iter_truncate(from, count);
+
        err = file_remove_suid(file);
        if (err) {
                mutex_unlock(&inode->i_mutex);
@@ -1806,14 +1796,9 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb,
                atomic_inc(&BTRFS_I(inode)->sync_writers);
 
        if (unlikely(file->f_flags & O_DIRECT)) {
-               num_written = __btrfs_direct_write(iocb, iov, nr_segs,
-                                                  pos, count, ocount);
+               num_written = __btrfs_direct_write(iocb, from, pos);
        } else {
-               struct iov_iter i;
-
-               iov_iter_init(&i, iov, nr_segs, count, num_written);
-
-               num_written = __btrfs_buffered_write(file, &i, pos);
+               num_written = __btrfs_buffered_write(file, from, pos);
                if (num_written > 0)
                        iocb->ki_pos = pos + num_written;
        }
@@ -2740,11 +2725,11 @@ out:
 
 const struct file_operations btrfs_file_operations = {
        .llseek         = btrfs_file_llseek,
-       .read           = do_sync_read,
-       .write          = do_sync_write,
-       .aio_read       = generic_file_aio_read,
+       .read           = new_sync_read,
+       .write          = new_sync_write,
+       .read_iter      = generic_file_read_iter,
        .splice_read    = generic_file_splice_read,
-       .aio_write      = btrfs_file_aio_write,
+       .write_iter     = btrfs_file_write_iter,
        .mmap           = btrfs_file_mmap,
        .open           = generic_file_open,
        .release        = btrfs_release_file,
index 7fa5f7fd7bc79259ed5a5e51131cbf6c8d07d919..8925f66a14115c9d733182f2ec4d113be5be5edd 100644 (file)
@@ -7445,39 +7445,30 @@ free_ordered:
 }
 
 static ssize_t check_direct_IO(struct btrfs_root *root, int rw, struct kiocb *iocb,
-                       const struct iovec *iov, loff_t offset,
-                       unsigned long nr_segs)
+                       const struct iov_iter *iter, loff_t offset)
 {
        int seg;
        int i;
-       size_t size;
-       unsigned long addr;
        unsigned blocksize_mask = root->sectorsize - 1;
        ssize_t retval = -EINVAL;
-       loff_t end = offset;
 
        if (offset & blocksize_mask)
                goto out;
 
-       /* Check the memory alignment.  Blocks cannot straddle pages */
-       for (seg = 0; seg < nr_segs; seg++) {
-               addr = (unsigned long)iov[seg].iov_base;
-               size = iov[seg].iov_len;
-               end += size;
-               if ((addr & blocksize_mask) || (size & blocksize_mask))
-                       goto out;
-
-               /* If this is a write we don't need to check anymore */
-               if (rw & WRITE)
-                       continue;
+       if (iov_iter_alignment(iter) & blocksize_mask)
+               goto out;
 
-               /*
-                * Check to make sure we don't have duplicate iov_base's in this
-                * iovec, if so return EINVAL, otherwise we'll get csum errors
-                * when reading back.
-                */
-               for (i = seg + 1; i < nr_segs; i++) {
-                       if (iov[seg].iov_base == iov[i].iov_base)
+       /* If this is a write we don't need to check anymore */
+       if (rw & WRITE)
+               return 0;
+       /*
+        * Check to make sure we don't have duplicate iov_base's in this
+        * iovec, if so return EINVAL, otherwise we'll get csum errors
+        * when reading back.
+        */
+       for (seg = 0; seg < iter->nr_segs; seg++) {
+               for (i = seg + 1; i < iter->nr_segs; i++) {
+                       if (iter->iov[seg].iov_base == iter->iov[i].iov_base)
                                goto out;
                }
        }
@@ -7487,8 +7478,7 @@ out:
 }
 
 static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
-                       const struct iovec *iov, loff_t offset,
-                       unsigned long nr_segs)
+                       struct iov_iter *iter, loff_t offset)
 {
        struct file *file = iocb->ki_filp;
        struct inode *inode = file->f_mapping->host;
@@ -7498,8 +7488,7 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
        bool relock = false;
        ssize_t ret;
 
-       if (check_direct_IO(BTRFS_I(inode)->root, rw, iocb, iov,
-                           offset, nr_segs))
+       if (check_direct_IO(BTRFS_I(inode)->root, rw, iocb, iter, offset))
                return 0;
 
        atomic_inc(&inode->i_dio_count);
@@ -7511,7 +7500,7 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
         * we need to flush the dirty pages again to make absolutely sure
         * that any outstanding dirty pages are on disk.
         */
-       count = iov_length(iov, nr_segs);
+       count = iov_iter_count(iter);
        if (test_bit(BTRFS_INODE_HAS_ASYNC_EXTENT,
                     &BTRFS_I(inode)->runtime_flags))
                filemap_fdatawrite_range(inode->i_mapping, offset, count);
@@ -7538,7 +7527,7 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
 
        ret = __blockdev_direct_IO(rw, iocb, inode,
                        BTRFS_I(inode)->root->fs_info->fs_devices->latest_bdev,
-                       iov, offset, nr_segs, btrfs_get_blocks_direct, NULL,
+                       iter, offset, btrfs_get_blocks_direct, NULL,
                        btrfs_submit_direct, flags);
        if (rw & WRITE) {
                if (ret < 0 && ret != -EIOCBQUEUED)
index 82c18ba12e3f62e4ff3c71daf2269d353c4f18ce..0d321c23069a0aa9ea1aeac570bdf837cf208d24 100644 (file)
@@ -1957,7 +1957,8 @@ static noinline int copy_to_sk(struct btrfs_root *root,
                               struct btrfs_path *path,
                               struct btrfs_key *key,
                               struct btrfs_ioctl_search_key *sk,
-                              char *buf,
+                              size_t *buf_size,
+                              char __user *ubuf,
                               unsigned long *sk_offset,
                               int *num_found)
 {
@@ -1989,13 +1990,25 @@ static noinline int copy_to_sk(struct btrfs_root *root,
                if (!key_in_sk(key, sk))
                        continue;
 
-               if (sizeof(sh) + item_len > BTRFS_SEARCH_ARGS_BUFSIZE)
+               if (sizeof(sh) + item_len > *buf_size) {
+                       if (*num_found) {
+                               ret = 1;
+                               goto out;
+                       }
+
+                       /*
+                        * return one empty item back for v1, which does not
+                        * handle -EOVERFLOW
+                        */
+
+                       *buf_size = sizeof(sh) + item_len;
                        item_len = 0;
+                       ret = -EOVERFLOW;
+               }
 
-               if (sizeof(sh) + item_len + *sk_offset >
-                   BTRFS_SEARCH_ARGS_BUFSIZE) {
+               if (sizeof(sh) + item_len + *sk_offset > *buf_size) {
                        ret = 1;
-                       goto overflow;
+                       goto out;
                }
 
                sh.objectid = key->objectid;
@@ -2005,20 +2018,33 @@ static noinline int copy_to_sk(struct btrfs_root *root,
                sh.transid = found_transid;
 
                /* copy search result header */
-               memcpy(buf + *sk_offset, &sh, sizeof(sh));
+               if (copy_to_user(ubuf + *sk_offset, &sh, sizeof(sh))) {
+                       ret = -EFAULT;
+                       goto out;
+               }
+
                *sk_offset += sizeof(sh);
 
                if (item_len) {
-                       char *p = buf + *sk_offset;
+                       char __user *up = ubuf + *sk_offset;
                        /* copy the item */
-                       read_extent_buffer(leaf, p,
-                                          item_off, item_len);
+                       if (read_extent_buffer_to_user(leaf, up,
+                                                      item_off, item_len)) {
+                               ret = -EFAULT;
+                               goto out;
+                       }
+
                        *sk_offset += item_len;
                }
                (*num_found)++;
 
-               if (*num_found >= sk->nr_items)
-                       break;
+               if (ret) /* -EOVERFLOW from above */
+                       goto out;
+
+               if (*num_found >= sk->nr_items) {
+                       ret = 1;
+                       goto out;
+               }
        }
 advance_key:
        ret = 0;
@@ -2033,22 +2059,37 @@ advance_key:
                key->objectid++;
        } else
                ret = 1;
-overflow:
+out:
+       /*
+        *  0: all items from this leaf copied, continue with next
+        *  1: * more items can be copied, but unused buffer is too small
+        *     * all items were found
+        *     Either way, it will stops the loop which iterates to the next
+        *     leaf
+        *  -EOVERFLOW: item was to large for buffer
+        *  -EFAULT: could not copy extent buffer back to userspace
+        */
        return ret;
 }
 
 static noinline int search_ioctl(struct inode *inode,
-                                struct btrfs_ioctl_search_args *args)
+                                struct btrfs_ioctl_search_key *sk,
+                                size_t *buf_size,
+                                char __user *ubuf)
 {
        struct btrfs_root *root;
        struct btrfs_key key;
        struct btrfs_path *path;
-       struct btrfs_ioctl_search_key *sk = &args->key;
        struct btrfs_fs_info *info = BTRFS_I(inode)->root->fs_info;
        int ret;
        int num_found = 0;
        unsigned long sk_offset = 0;
 
+       if (*buf_size < sizeof(struct btrfs_ioctl_search_header)) {
+               *buf_size = sizeof(struct btrfs_ioctl_search_header);
+               return -EOVERFLOW;
+       }
+
        path = btrfs_alloc_path();
        if (!path)
                return -ENOMEM;
@@ -2082,14 +2123,15 @@ static noinline int search_ioctl(struct inode *inode,
                                ret = 0;
                        goto err;
                }
-               ret = copy_to_sk(root, path, &key, sk, args->buf,
+               ret = copy_to_sk(root, path, &key, sk, buf_size, ubuf,
                                 &sk_offset, &num_found);
                btrfs_release_path(path);
-               if (ret || num_found >= sk->nr_items)
+               if (ret)
                        break;
 
        }
-       ret = 0;
+       if (ret > 0)
+               ret = 0;
 err:
        sk->nr_items = num_found;
        btrfs_free_path(path);
@@ -2099,22 +2141,73 @@ err:
 static noinline int btrfs_ioctl_tree_search(struct file *file,
                                           void __user *argp)
 {
-        struct btrfs_ioctl_search_args *args;
-        struct inode *inode;
-        int ret;
+       struct btrfs_ioctl_search_args __user *uargs;
+       struct btrfs_ioctl_search_key sk;
+       struct inode *inode;
+       int ret;
+       size_t buf_size;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
 
-       args = memdup_user(argp, sizeof(*args));
-       if (IS_ERR(args))
-               return PTR_ERR(args);
+       uargs = (struct btrfs_ioctl_search_args __user *)argp;
+
+       if (copy_from_user(&sk, &uargs->key, sizeof(sk)))
+               return -EFAULT;
+
+       buf_size = sizeof(uargs->buf);
 
        inode = file_inode(file);
-       ret = search_ioctl(inode, args);
-       if (ret == 0 && copy_to_user(argp, args, sizeof(*args)))
+       ret = search_ioctl(inode, &sk, &buf_size, uargs->buf);
+
+       /*
+        * In the origin implementation an overflow is handled by returning a
+        * search header with a len of zero, so reset ret.
+        */
+       if (ret == -EOVERFLOW)
+               ret = 0;
+
+       if (ret == 0 && copy_to_user(&uargs->key, &sk, sizeof(sk)))
                ret = -EFAULT;
-       kfree(args);
+       return ret;
+}
+
+static noinline int btrfs_ioctl_tree_search_v2(struct file *file,
+                                              void __user *argp)
+{
+       struct btrfs_ioctl_search_args_v2 __user *uarg;
+       struct btrfs_ioctl_search_args_v2 args;
+       struct inode *inode;
+       int ret;
+       size_t buf_size;
+       const size_t buf_limit = 16 * 1024 * 1024;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       /* copy search header and buffer size */
+       uarg = (struct btrfs_ioctl_search_args_v2 __user *)argp;
+       if (copy_from_user(&args, uarg, sizeof(args)))
+               return -EFAULT;
+
+       buf_size = args.buf_size;
+
+       if (buf_size < sizeof(struct btrfs_ioctl_search_header))
+               return -EOVERFLOW;
+
+       /* limit result size to 16MB */
+       if (buf_size > buf_limit)
+               buf_size = buf_limit;
+
+       inode = file_inode(file);
+       ret = search_ioctl(inode, &args.key, &buf_size,
+                          (char *)(&uarg->buf[0]));
+       if (ret == 0 && copy_to_user(&uarg->key, &args.key, sizeof(args.key)))
+               ret = -EFAULT;
+       else if (ret == -EOVERFLOW &&
+               copy_to_user(&uarg->buf_size, &buf_size, sizeof(buf_size)))
+               ret = -EFAULT;
+
        return ret;
 }
 
@@ -5198,6 +5291,8 @@ long btrfs_ioctl(struct file *file, unsigned int
                return btrfs_ioctl_trans_end(file);
        case BTRFS_IOC_TREE_SEARCH:
                return btrfs_ioctl_tree_search(file, argp);
+       case BTRFS_IOC_TREE_SEARCH_V2:
+               return btrfs_ioctl_tree_search_v2(file, argp);
        case BTRFS_IOC_INO_LOOKUP:
                return btrfs_ioctl_ino_lookup(file, argp);
        case BTRFS_IOC_INO_PATHS:
index cf5aead95a7f6d2eac95c3cd47fc7d56cab92e4b..98cb6b2630f9aac60972b5aea3c372e9e99eeba5 100644 (file)
@@ -1798,8 +1798,10 @@ static int qgroup_shared_accounting(struct btrfs_trans_handle *trans,
                return -ENOMEM;
 
        tmp = ulist_alloc(GFP_NOFS);
-       if (!tmp)
+       if (!tmp) {
+               ulist_free(qgroups);
                return -ENOMEM;
+       }
 
        btrfs_get_tree_mod_seq(fs_info, &elem);
        ret = btrfs_find_all_roots(trans, fs_info, oper->bytenr, elem.seq,
index 30947f923620678f9bc1a27fda49359c6b36e9a4..09230cf3a2447b3541826f21b2f3fe1dcce202ce 100644 (file)
@@ -428,8 +428,13 @@ static struct reada_extent *reada_find_extent(struct btrfs_root *root,
                        continue;
                }
                if (!dev->bdev) {
-                       /* cannot read ahead on missing device */
-                       continue;
+                       /*
+                        * cannot read ahead on missing device, but for RAID5/6,
+                        * REQ_GET_READ_MIRRORS return 1. So don't skip missing
+                        * device for such case.
+                        */
+                       if (nzones > 1)
+                               continue;
                }
                if (dev_replace_is_ongoing &&
                    dev == fs_info->dev_replace.tgtdev) {
index a5dcacb5df9cc16027240549101e078c03b9220f..9626252ee6b47d2b391f3383cfa9b3bb80e4110c 100644 (file)
@@ -135,7 +135,7 @@ restart:
        radix_tree_for_each_slot(slot, &fs_info->buffer_radix, &iter, 0) {
                struct extent_buffer *eb;
 
-               eb = radix_tree_deref_slot(slot);
+               eb = radix_tree_deref_slot_protected(slot, &fs_info->buffer_lock);
                if (!eb)
                        continue;
                /* Shouldn't happen but that kind of thinking creates CVE's */
index fa691b754aafff33bcc0dd8080a200df5421ef36..ec3dcb20235774044e4b92e1230b1b0af593aea4 100644 (file)
@@ -415,6 +415,8 @@ int btrfs_test_qgroups(void)
                ret = -ENOMEM;
                goto out;
        }
+       btrfs_set_header_level(root->node, 0);
+       btrfs_set_header_nritems(root->node, 0);
        root->alloc_bytenr += 8192;
 
        tmp_root = btrfs_alloc_dummy_root();
index 9630f10f8e1ea3eea4f724e6835d9ba25a1a1436..511839c04f11bf1130475815aca9cd79bb775d0a 100644 (file)
@@ -1284,11 +1284,13 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
                goto fail;
        }
 
-       pending->error = btrfs_qgroup_inherit(trans, fs_info,
-                                             root->root_key.objectid,
-                                             objectid, pending->inherit);
-       if (pending->error)
-               goto no_free_objectid;
+       ret = btrfs_qgroup_inherit(trans, fs_info,
+                                  root->root_key.objectid,
+                                  objectid, pending->inherit);
+       if (ret) {
+               btrfs_abort_transaction(trans, root, ret);
+               goto fail;
+       }
 
        /* see comments in should_cow_block() */
        set_bit(BTRFS_ROOT_FORCE_COW, &root->state);
index 21887d63dad589a53e3da21fccb00c45a5c28ab6..469f2e8657e8426bfb3ad94fb12dde25e06ccb18 100644 (file)
@@ -104,12 +104,6 @@ int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type)
        umode_t new_mode = inode->i_mode, old_mode = inode->i_mode;
        struct dentry *dentry;
 
-       if (acl) {
-               ret = posix_acl_valid(acl);
-               if (ret < 0)
-                       goto out;
-       }
-
        switch (type) {
        case ACL_TYPE_ACCESS:
                name = POSIX_ACL_XATTR_ACCESS;
index 65a30e817dd80ab9c7264ade89b9cae563998465..90b3954d48edfa88b10b22dd98d5fcc0bc6c6ab7 100644 (file)
@@ -211,18 +211,15 @@ static int readpage_nounlock(struct file *filp, struct page *page)
                SetPageError(page);
                ceph_fscache_readpage_cancel(inode, page);
                goto out;
-       } else {
-               if (err < PAGE_CACHE_SIZE) {
-               /* zero fill remainder of page */
-                       zero_user_segment(page, err, PAGE_CACHE_SIZE);
-               } else {
-                       flush_dcache_page(page);
-               }
        }
-       SetPageUptodate(page);
+       if (err < PAGE_CACHE_SIZE)
+               /* zero fill remainder of page */
+               zero_user_segment(page, err, PAGE_CACHE_SIZE);
+       else
+               flush_dcache_page(page);
 
-       if (err >= 0)
-               ceph_readpage_to_fscache(inode, page);
+       SetPageUptodate(page);
+       ceph_readpage_to_fscache(inode, page);
 
 out:
        return err < 0 ? err : 0;
@@ -1187,8 +1184,8 @@ static int ceph_write_end(struct file *file, struct address_space *mapping,
  * never get called.
  */
 static ssize_t ceph_direct_io(int rw, struct kiocb *iocb,
-                             const struct iovec *iov,
-                             loff_t pos, unsigned long nr_segs)
+                             struct iov_iter *iter,
+                             loff_t pos)
 {
        WARN_ON(1);
        return -EINVAL;
index c561b628ebce519d111d159f541b9df88242a5b1..1fde164b74b54a258cdff8e7c89f52191713c986 100644 (file)
@@ -221,8 +221,8 @@ int ceph_unreserve_caps(struct ceph_mds_client *mdsc,
        return 0;
 }
 
-static struct ceph_cap *get_cap(struct ceph_mds_client *mdsc,
-                               struct ceph_cap_reservation *ctx)
+struct ceph_cap *ceph_get_cap(struct ceph_mds_client *mdsc,
+                             struct ceph_cap_reservation *ctx)
 {
        struct ceph_cap *cap = NULL;
 
@@ -508,15 +508,14 @@ static void __check_cap_issue(struct ceph_inode_info *ci, struct ceph_cap *cap,
  * it is < 0.  (This is so we can atomically add the cap and add an
  * open file reference to it.)
  */
-int ceph_add_cap(struct inode *inode,
-                struct ceph_mds_session *session, u64 cap_id,
-                int fmode, unsigned issued, unsigned wanted,
-                unsigned seq, unsigned mseq, u64 realmino, int flags,
-                struct ceph_cap_reservation *caps_reservation)
+void ceph_add_cap(struct inode *inode,
+                 struct ceph_mds_session *session, u64 cap_id,
+                 int fmode, unsigned issued, unsigned wanted,
+                 unsigned seq, unsigned mseq, u64 realmino, int flags,
+                 struct ceph_cap **new_cap)
 {
        struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
        struct ceph_inode_info *ci = ceph_inode(inode);
-       struct ceph_cap *new_cap = NULL;
        struct ceph_cap *cap;
        int mds = session->s_mds;
        int actual_wanted;
@@ -531,20 +530,10 @@ int ceph_add_cap(struct inode *inode,
        if (fmode >= 0)
                wanted |= ceph_caps_for_mode(fmode);
 
-retry:
-       spin_lock(&ci->i_ceph_lock);
        cap = __get_cap_for_mds(ci, mds);
        if (!cap) {
-               if (new_cap) {
-                       cap = new_cap;
-                       new_cap = NULL;
-               } else {
-                       spin_unlock(&ci->i_ceph_lock);
-                       new_cap = get_cap(mdsc, caps_reservation);
-                       if (new_cap == NULL)
-                               return -ENOMEM;
-                       goto retry;
-               }
+               cap = *new_cap;
+               *new_cap = NULL;
 
                cap->issued = 0;
                cap->implemented = 0;
@@ -562,9 +551,6 @@ retry:
                session->s_nr_caps++;
                spin_unlock(&session->s_cap_lock);
        } else {
-               if (new_cap)
-                       ceph_put_cap(mdsc, new_cap);
-
                /*
                 * auth mds of the inode changed. we received the cap export
                 * message, but still haven't received the cap import message.
@@ -626,7 +612,6 @@ retry:
                        ci->i_auth_cap = cap;
                        cap->mds_wanted = wanted;
                }
-               ci->i_cap_exporting_issued = 0;
        } else {
                WARN_ON(ci->i_auth_cap == cap);
        }
@@ -648,9 +633,6 @@ retry:
 
        if (fmode >= 0)
                __ceph_get_fmode(ci, fmode);
-       spin_unlock(&ci->i_ceph_lock);
-       wake_up_all(&ci->i_cap_wq);
-       return 0;
 }
 
 /*
@@ -685,7 +667,7 @@ static int __cap_is_valid(struct ceph_cap *cap)
  */
 int __ceph_caps_issued(struct ceph_inode_info *ci, int *implemented)
 {
-       int have = ci->i_snap_caps | ci->i_cap_exporting_issued;
+       int have = ci->i_snap_caps;
        struct ceph_cap *cap;
        struct rb_node *p;
 
@@ -900,7 +882,7 @@ int __ceph_caps_mds_wanted(struct ceph_inode_info *ci)
  */
 static int __ceph_is_any_caps(struct ceph_inode_info *ci)
 {
-       return !RB_EMPTY_ROOT(&ci->i_caps) || ci->i_cap_exporting_issued;
+       return !RB_EMPTY_ROOT(&ci->i_caps);
 }
 
 int ceph_is_any_caps(struct inode *inode)
@@ -2397,32 +2379,30 @@ static void invalidate_aliases(struct inode *inode)
  * actually be a revocation if it specifies a smaller cap set.)
  *
  * caller holds s_mutex and i_ceph_lock, we drop both.
- *
- * return value:
- *  0 - ok
- *  1 - check_caps on auth cap only (writeback)
- *  2 - check_caps (ack revoke)
  */
-static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant,
+static void handle_cap_grant(struct ceph_mds_client *mdsc,
+                            struct inode *inode, struct ceph_mds_caps *grant,
+                            void *snaptrace, int snaptrace_len,
+                            struct ceph_buffer *xattr_buf,
                             struct ceph_mds_session *session,
-                            struct ceph_cap *cap,
-                            struct ceph_buffer *xattr_buf)
-               __releases(ci->i_ceph_lock)
+                            struct ceph_cap *cap, int issued)
+       __releases(ci->i_ceph_lock)
 {
        struct ceph_inode_info *ci = ceph_inode(inode);
        int mds = session->s_mds;
        int seq = le32_to_cpu(grant->seq);
        int newcaps = le32_to_cpu(grant->caps);
-       int issued, implemented, used, wanted, dirty;
+       int used, wanted, dirty;
        u64 size = le64_to_cpu(grant->size);
        u64 max_size = le64_to_cpu(grant->max_size);
        struct timespec mtime, atime, ctime;
        int check_caps = 0;
-       int wake = 0;
-       int writeback = 0;
-       int queue_invalidate = 0;
-       int deleted_inode = 0;
-       int queue_revalidate = 0;
+       bool wake = 0;
+       bool writeback = 0;
+       bool queue_trunc = 0;
+       bool queue_invalidate = 0;
+       bool queue_revalidate = 0;
+       bool deleted_inode = 0;
 
        dout("handle_cap_grant inode %p cap %p mds%d seq %d %s\n",
             inode, cap, mds, seq, ceph_cap_string(newcaps));
@@ -2466,16 +2446,13 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant,
        }
 
        /* side effects now are allowed */
-
-       issued = __ceph_caps_issued(ci, &implemented);
-       issued |= implemented | __ceph_caps_dirty(ci);
-
        cap->cap_gen = session->s_cap_gen;
        cap->seq = seq;
 
        __check_cap_issue(ci, cap, newcaps);
 
-       if ((issued & CEPH_CAP_AUTH_EXCL) == 0) {
+       if ((newcaps & CEPH_CAP_AUTH_SHARED) &&
+           (issued & CEPH_CAP_AUTH_EXCL) == 0) {
                inode->i_mode = le32_to_cpu(grant->mode);
                inode->i_uid = make_kuid(&init_user_ns, le32_to_cpu(grant->uid));
                inode->i_gid = make_kgid(&init_user_ns, le32_to_cpu(grant->gid));
@@ -2484,7 +2461,8 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant,
                     from_kgid(&init_user_ns, inode->i_gid));
        }
 
-       if ((issued & CEPH_CAP_LINK_EXCL) == 0) {
+       if ((newcaps & CEPH_CAP_AUTH_SHARED) &&
+           (issued & CEPH_CAP_LINK_EXCL) == 0) {
                set_nlink(inode, le32_to_cpu(grant->nlink));
                if (inode->i_nlink == 0 &&
                    (newcaps & (CEPH_CAP_LINK_SHARED | CEPH_CAP_LINK_EXCL)))
@@ -2511,30 +2489,35 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant,
        if ((issued & CEPH_CAP_FILE_CACHE) && ci->i_rdcache_gen > 1)
                queue_revalidate = 1;
 
-       /* size/ctime/mtime/atime? */
-       ceph_fill_file_size(inode, issued,
-                           le32_to_cpu(grant->truncate_seq),
-                           le64_to_cpu(grant->truncate_size), size);
-       ceph_decode_timespec(&mtime, &grant->mtime);
-       ceph_decode_timespec(&atime, &grant->atime);
-       ceph_decode_timespec(&ctime, &grant->ctime);
-       ceph_fill_file_time(inode, issued,
-                           le32_to_cpu(grant->time_warp_seq), &ctime, &mtime,
-                           &atime);
-
-
-       /* file layout may have changed */
-       ci->i_layout = grant->layout;
-
-       /* max size increase? */
-       if (ci->i_auth_cap == cap && max_size != ci->i_max_size) {
-               dout("max_size %lld -> %llu\n", ci->i_max_size, max_size);
-               ci->i_max_size = max_size;
-               if (max_size >= ci->i_wanted_max_size) {
-                       ci->i_wanted_max_size = 0;  /* reset */
-                       ci->i_requested_max_size = 0;
+       if (newcaps & CEPH_CAP_ANY_RD) {
+               /* ctime/mtime/atime? */
+               ceph_decode_timespec(&mtime, &grant->mtime);
+               ceph_decode_timespec(&atime, &grant->atime);
+               ceph_decode_timespec(&ctime, &grant->ctime);
+               ceph_fill_file_time(inode, issued,
+                                   le32_to_cpu(grant->time_warp_seq),
+                                   &ctime, &mtime, &atime);
+       }
+
+       if (newcaps & (CEPH_CAP_ANY_FILE_RD | CEPH_CAP_ANY_FILE_WR)) {
+               /* file layout may have changed */
+               ci->i_layout = grant->layout;
+               /* size/truncate_seq? */
+               queue_trunc = ceph_fill_file_size(inode, issued,
+                                       le32_to_cpu(grant->truncate_seq),
+                                       le64_to_cpu(grant->truncate_size),
+                                       size);
+               /* max size increase? */
+               if (ci->i_auth_cap == cap && max_size != ci->i_max_size) {
+                       dout("max_size %lld -> %llu\n",
+                            ci->i_max_size, max_size);
+                       ci->i_max_size = max_size;
+                       if (max_size >= ci->i_wanted_max_size) {
+                               ci->i_wanted_max_size = 0;  /* reset */
+                               ci->i_requested_max_size = 0;
+                       }
+                       wake = 1;
                }
-               wake = 1;
        }
 
        /* check cap bits */
@@ -2595,6 +2578,23 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant,
 
        spin_unlock(&ci->i_ceph_lock);
 
+       if (le32_to_cpu(grant->op) == CEPH_CAP_OP_IMPORT) {
+               down_write(&mdsc->snap_rwsem);
+               ceph_update_snap_trace(mdsc, snaptrace,
+                                      snaptrace + snaptrace_len, false);
+               downgrade_write(&mdsc->snap_rwsem);
+               kick_flushing_inode_caps(mdsc, session, inode);
+               up_read(&mdsc->snap_rwsem);
+               if (newcaps & ~issued)
+                       wake = 1;
+       }
+
+       if (queue_trunc) {
+               ceph_queue_vmtruncate(inode);
+               ceph_queue_revalidate(inode);
+       } else if (queue_revalidate)
+               ceph_queue_revalidate(inode);
+
        if (writeback)
                /*
                 * queue inode for writeback: we can't actually call
@@ -2606,8 +2606,6 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant,
                ceph_queue_invalidate(inode);
        if (deleted_inode)
                invalidate_aliases(inode);
-       if (queue_revalidate)
-               ceph_queue_revalidate(inode);
        if (wake)
                wake_up_all(&ci->i_cap_wq);
 
@@ -2784,7 +2782,7 @@ static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex,
 {
        struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
        struct ceph_mds_session *tsession = NULL;
-       struct ceph_cap *cap, *tcap;
+       struct ceph_cap *cap, *tcap, *new_cap = NULL;
        struct ceph_inode_info *ci = ceph_inode(inode);
        u64 t_cap_id;
        unsigned mseq = le32_to_cpu(ex->migrate_seq);
@@ -2807,7 +2805,7 @@ static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex,
 retry:
        spin_lock(&ci->i_ceph_lock);
        cap = __get_cap_for_mds(ci, mds);
-       if (!cap)
+       if (!cap || cap->cap_id != le64_to_cpu(ex->cap_id))
                goto out_unlock;
 
        if (target < 0) {
@@ -2846,15 +2844,14 @@ retry:
                }
                __ceph_remove_cap(cap, false);
                goto out_unlock;
-       }
-
-       if (tsession) {
-               int flag = (cap == ci->i_auth_cap) ? CEPH_CAP_FLAG_AUTH : 0;
-               spin_unlock(&ci->i_ceph_lock);
+       } else if (tsession) {
                /* add placeholder for the export tagert */
+               int flag = (cap == ci->i_auth_cap) ? CEPH_CAP_FLAG_AUTH : 0;
                ceph_add_cap(inode, tsession, t_cap_id, -1, issued, 0,
-                            t_seq - 1, t_mseq, (u64)-1, flag, NULL);
-               goto retry;
+                            t_seq - 1, t_mseq, (u64)-1, flag, &new_cap);
+
+               __ceph_remove_cap(cap, false);
+               goto out_unlock;
        }
 
        spin_unlock(&ci->i_ceph_lock);
@@ -2873,6 +2870,7 @@ retry:
                                          SINGLE_DEPTH_NESTING);
                }
                ceph_add_cap_releases(mdsc, tsession);
+               new_cap = ceph_get_cap(mdsc, NULL);
        } else {
                WARN_ON(1);
                tsession = NULL;
@@ -2887,24 +2885,27 @@ out_unlock:
                mutex_unlock(&tsession->s_mutex);
                ceph_put_mds_session(tsession);
        }
+       if (new_cap)
+               ceph_put_cap(mdsc, new_cap);
 }
 
 /*
- * Handle cap IMPORT.  If there are temp bits from an older EXPORT,
- * clean them up.
+ * Handle cap IMPORT.
  *
- * caller holds s_mutex.
+ * caller holds s_mutex. acquires i_ceph_lock
  */
 static void handle_cap_import(struct ceph_mds_client *mdsc,
                              struct inode *inode, struct ceph_mds_caps *im,
                              struct ceph_mds_cap_peer *ph,
                              struct ceph_mds_session *session,
-                             void *snaptrace, int snaptrace_len)
+                             struct ceph_cap **target_cap, int *old_issued)
+       __acquires(ci->i_ceph_lock)
 {
        struct ceph_inode_info *ci = ceph_inode(inode);
-       struct ceph_cap *cap;
+       struct ceph_cap *cap, *ocap, *new_cap = NULL;
        int mds = session->s_mds;
-       unsigned issued = le32_to_cpu(im->caps);
+       int issued;
+       unsigned caps = le32_to_cpu(im->caps);
        unsigned wanted = le32_to_cpu(im->wanted);
        unsigned seq = le32_to_cpu(im->seq);
        unsigned mseq = le32_to_cpu(im->migrate_seq);
@@ -2924,40 +2925,52 @@ static void handle_cap_import(struct ceph_mds_client *mdsc,
        dout("handle_cap_import inode %p ci %p mds%d mseq %d peer %d\n",
             inode, ci, mds, mseq, peer);
 
+retry:
        spin_lock(&ci->i_ceph_lock);
-       cap = peer >= 0 ? __get_cap_for_mds(ci, peer) : NULL;
-       if (cap && cap->cap_id == p_cap_id) {
+       cap = __get_cap_for_mds(ci, mds);
+       if (!cap) {
+               if (!new_cap) {
+                       spin_unlock(&ci->i_ceph_lock);
+                       new_cap = ceph_get_cap(mdsc, NULL);
+                       goto retry;
+               }
+               cap = new_cap;
+       } else {
+               if (new_cap) {
+                       ceph_put_cap(mdsc, new_cap);
+                       new_cap = NULL;
+               }
+       }
+
+       __ceph_caps_issued(ci, &issued);
+       issued |= __ceph_caps_dirty(ci);
+
+       ceph_add_cap(inode, session, cap_id, -1, caps, wanted, seq, mseq,
+                    realmino, CEPH_CAP_FLAG_AUTH, &new_cap);
+
+       ocap = peer >= 0 ? __get_cap_for_mds(ci, peer) : NULL;
+       if (ocap && ocap->cap_id == p_cap_id) {
                dout(" remove export cap %p mds%d flags %d\n",
-                    cap, peer, ph->flags);
+                    ocap, peer, ph->flags);
                if ((ph->flags & CEPH_CAP_FLAG_AUTH) &&
-                   (cap->seq != le32_to_cpu(ph->seq) ||
-                    cap->mseq != le32_to_cpu(ph->mseq))) {
+                   (ocap->seq != le32_to_cpu(ph->seq) ||
+                    ocap->mseq != le32_to_cpu(ph->mseq))) {
                        pr_err("handle_cap_import: mismatched seq/mseq: "
                               "ino (%llx.%llx) mds%d seq %d mseq %d "
                               "importer mds%d has peer seq %d mseq %d\n",
-                              ceph_vinop(inode), peer, cap->seq,
-                              cap->mseq, mds, le32_to_cpu(ph->seq),
+                              ceph_vinop(inode), peer, ocap->seq,
+                              ocap->mseq, mds, le32_to_cpu(ph->seq),
                               le32_to_cpu(ph->mseq));
                }
-               ci->i_cap_exporting_issued = cap->issued;
-               __ceph_remove_cap(cap, (ph->flags & CEPH_CAP_FLAG_RELEASE));
+               __ceph_remove_cap(ocap, (ph->flags & CEPH_CAP_FLAG_RELEASE));
        }
 
        /* make sure we re-request max_size, if necessary */
        ci->i_wanted_max_size = 0;
        ci->i_requested_max_size = 0;
-       spin_unlock(&ci->i_ceph_lock);
-
-       down_write(&mdsc->snap_rwsem);
-       ceph_update_snap_trace(mdsc, snaptrace, snaptrace+snaptrace_len,
-                              false);
-       downgrade_write(&mdsc->snap_rwsem);
-       ceph_add_cap(inode, session, cap_id, -1,
-                    issued, wanted, seq, mseq, realmino, CEPH_CAP_FLAG_AUTH,
-                    NULL /* no caps context */);
-       kick_flushing_inode_caps(mdsc, session, inode);
-       up_read(&mdsc->snap_rwsem);
 
+       *old_issued = issued;
+       *target_cap = cap;
 }
 
 /*
@@ -2977,7 +2990,7 @@ void ceph_handle_caps(struct ceph_mds_session *session,
        struct ceph_mds_caps *h;
        struct ceph_mds_cap_peer *peer = NULL;
        int mds = session->s_mds;
-       int op;
+       int op, issued;
        u32 seq, mseq;
        struct ceph_vino vino;
        u64 cap_id;
@@ -3069,7 +3082,10 @@ void ceph_handle_caps(struct ceph_mds_session *session,
 
        case CEPH_CAP_OP_IMPORT:
                handle_cap_import(mdsc, inode, h, peer, session,
-                                 snaptrace, snaptrace_len);
+                                 &cap, &issued);
+               handle_cap_grant(mdsc, inode, h,  snaptrace, snaptrace_len,
+                                msg->middle, session, cap, issued);
+               goto done_unlocked;
        }
 
        /* the rest require a cap */
@@ -3086,8 +3102,10 @@ void ceph_handle_caps(struct ceph_mds_session *session,
        switch (op) {
        case CEPH_CAP_OP_REVOKE:
        case CEPH_CAP_OP_GRANT:
-       case CEPH_CAP_OP_IMPORT:
-               handle_cap_grant(inode, h, session, cap, msg->middle);
+               __ceph_caps_issued(ci, &issued);
+               issued |= __ceph_caps_dirty(ci);
+               handle_cap_grant(mdsc, inode, h, NULL, 0, msg->middle,
+                                session, cap, issued);
                goto done_unlocked;
 
        case CEPH_CAP_OP_FLUSH_ACK:
index 00d6af6a32ec9a37f204705da091122e7ddd7151..8d7d782f43822d2fd29b1f6fe941093ff7e247d3 100644 (file)
@@ -169,7 +169,7 @@ static struct dentry *__get_parent(struct super_block *sb,
        return dentry;
 }
 
-struct dentry *ceph_get_parent(struct dentry *child)
+static struct dentry *ceph_get_parent(struct dentry *child)
 {
        /* don't re-export snaps */
        if (ceph_snap(child->d_inode) != CEPH_NOSNAP)
index 88a6df4cbe6d8a52bd083a756ac452b798c33708..302085100c28af1a2ed67269e955b3d0839539be 100644 (file)
@@ -418,7 +418,7 @@ static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *i,
        struct page **pages;
        u64 off = iocb->ki_pos;
        int num_pages, ret;
-       size_t len = i->count;
+       size_t len = iov_iter_count(i);
 
        dout("sync_read on file %p %llu~%u %s\n", file, off,
             (unsigned)len,
@@ -436,25 +436,26 @@ static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *i,
 
        if (file->f_flags & O_DIRECT) {
                while (iov_iter_count(i)) {
-                       void __user *data = i->iov[0].iov_base + i->iov_offset;
-                       size_t len = i->iov[0].iov_len - i->iov_offset;
+                       size_t start;
+                       ssize_t n;
 
-                       num_pages = calc_pages_for((unsigned long)data, len);
-                       pages = ceph_get_direct_page_vector(data,
-                                                           num_pages, true);
-                       if (IS_ERR(pages))
-                               return PTR_ERR(pages);
+                       n = iov_iter_get_pages_alloc(i, &pages, INT_MAX, &start);
+                       if (n < 0)
+                               return n;
 
-                       ret = striped_read(inode, off, len,
+                       num_pages = (n + start + PAGE_SIZE - 1) / PAGE_SIZE;
+
+                       ret = striped_read(inode, off, n,
                                           pages, num_pages, checkeof,
-                                          1, (unsigned long)data & ~PAGE_MASK);
+                                          1, start);
+
                        ceph_put_page_vector(pages, num_pages, true);
 
                        if (ret <= 0)
                                break;
                        off += ret;
                        iov_iter_advance(i, ret);
-                       if (ret < len)
+                       if (ret < n)
                                break;
                }
        } else {
@@ -466,25 +467,14 @@ static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *i,
                                        num_pages, checkeof, 0, 0);
                if (ret > 0) {
                        int l, k = 0;
-                       size_t left = len = ret;
+                       size_t left = ret;
 
                        while (left) {
-                               void __user *data = i->iov[0].iov_base
-                                                       + i->iov_offset;
-                               l = min(i->iov[0].iov_len - i->iov_offset,
-                                       left);
-
-                               ret = ceph_copy_page_vector_to_user(&pages[k],
-                                                                   data, off,
-                                                                   l);
-                               if (ret > 0) {
-                                       iov_iter_advance(i, ret);
-                                       left -= ret;
-                                       off += ret;
-                                       k = calc_pages_for(iocb->ki_pos,
-                                                          len - left + 1) - 1;
-                                       BUG_ON(k >= num_pages && left);
-                               } else
+                               int copy = min_t(size_t, PAGE_SIZE, left);
+                               l = copy_page_to_iter(pages[k++], 0, copy, i);
+                               off += l;
+                               left -= l;
+                               if (l < copy)
                                        break;
                        }
                }
@@ -541,8 +531,7 @@ static void ceph_sync_write_unsafe(struct ceph_osd_request *req, bool unsafe)
  * objects, rollback on failure, etc.)
  */
 static ssize_t
-ceph_sync_direct_write(struct kiocb *iocb, const struct iovec *iov,
-                      unsigned long nr_segs, size_t count)
+ceph_sync_direct_write(struct kiocb *iocb, struct iov_iter *from)
 {
        struct file *file = iocb->ki_filp;
        struct inode *inode = file_inode(file);
@@ -556,11 +545,10 @@ ceph_sync_direct_write(struct kiocb *iocb, const struct iovec *iov,
        int written = 0;
        int flags;
        int check_caps = 0;
-       int page_align;
        int ret;
        struct timespec mtime = CURRENT_TIME;
        loff_t pos = iocb->ki_pos;
-       struct iov_iter i;
+       size_t count = iov_iter_count(from);
 
        if (ceph_snap(file_inode(file)) != CEPH_NOSNAP)
                return -EROFS;
@@ -582,13 +570,10 @@ ceph_sync_direct_write(struct kiocb *iocb, const struct iovec *iov,
                CEPH_OSD_FLAG_ONDISK |
                CEPH_OSD_FLAG_WRITE;
 
-       iov_iter_init(&i, iov, nr_segs, count, 0);
-
-       while (iov_iter_count(&i) > 0) {
-               void __user *data = i.iov->iov_base + i.iov_offset;
-               u64 len = i.iov->iov_len - i.iov_offset;
-
-               page_align = (unsigned long)data & ~PAGE_MASK;
+       while (iov_iter_count(from) > 0) {
+               u64 len = iov_iter_single_seg_count(from);
+               size_t start;
+               ssize_t n;
 
                snapc = ci->i_snap_realm->cached_context;
                vino = ceph_vino(inode);
@@ -604,20 +589,21 @@ ceph_sync_direct_write(struct kiocb *iocb, const struct iovec *iov,
                        break;
                }
 
-               num_pages = calc_pages_for(page_align, len);
-               pages = ceph_get_direct_page_vector(data, num_pages, false);
-               if (IS_ERR(pages)) {
-                       ret = PTR_ERR(pages);
-                       goto out;
+               n = iov_iter_get_pages_alloc(from, &pages, len, &start);
+               if (unlikely(n < 0)) {
+                       ret = n;
+                       ceph_osdc_put_request(req);
+                       break;
                }
 
+               num_pages = (n + start + PAGE_SIZE - 1) / PAGE_SIZE;
                /*
                 * throw out any page cache pages in this range. this
                 * may block.
                 */
                truncate_inode_pages_range(inode->i_mapping, pos,
-                                  (pos+len) | (PAGE_CACHE_SIZE-1));
-               osd_req_op_extent_osd_data_pages(req, 0, pages, len, page_align,
+                                  (pos+n) | (PAGE_CACHE_SIZE-1));
+               osd_req_op_extent_osd_data_pages(req, 0, pages, n, start,
                                                false, false);
 
                /* BUG_ON(vino.snap != CEPH_NOSNAP); */
@@ -629,22 +615,20 @@ ceph_sync_direct_write(struct kiocb *iocb, const struct iovec *iov,
 
                ceph_put_page_vector(pages, num_pages, false);
 
-out:
                ceph_osdc_put_request(req);
-               if (ret == 0) {
-                       pos += len;
-                       written += len;
-                       iov_iter_advance(&i, (size_t)len);
-
-                       if (pos > i_size_read(inode)) {
-                               check_caps = ceph_inode_set_size(inode, pos);
-                               if (check_caps)
-                                       ceph_check_caps(ceph_inode(inode),
-                                                       CHECK_CAPS_AUTHONLY,
-                                                       NULL);
-                       }
-               } else
+               if (ret)
                        break;
+               pos += n;
+               written += n;
+               iov_iter_advance(from, n);
+
+               if (pos > i_size_read(inode)) {
+                       check_caps = ceph_inode_set_size(inode, pos);
+                       if (check_caps)
+                               ceph_check_caps(ceph_inode(inode),
+                                               CHECK_CAPS_AUTHONLY,
+                                               NULL);
+               }
        }
 
        if (ret != -EOLDSNAPC && written > 0) {
@@ -662,8 +646,7 @@ out:
  * correct atomic write, we should e.g. take write locks on all
  * objects, rollback on failure, etc.)
  */
-static ssize_t ceph_sync_write(struct kiocb *iocb, const struct iovec *iov,
-                              unsigned long nr_segs, size_t count)
+static ssize_t ceph_sync_write(struct kiocb *iocb, struct iov_iter *from)
 {
        struct file *file = iocb->ki_filp;
        struct inode *inode = file_inode(file);
@@ -681,7 +664,7 @@ static ssize_t ceph_sync_write(struct kiocb *iocb, const struct iovec *iov,
        int ret;
        struct timespec mtime = CURRENT_TIME;
        loff_t pos = iocb->ki_pos;
-       struct iov_iter i;
+       size_t count = iov_iter_count(from);
 
        if (ceph_snap(file_inode(file)) != CEPH_NOSNAP)
                return -EROFS;
@@ -703,9 +686,7 @@ static ssize_t ceph_sync_write(struct kiocb *iocb, const struct iovec *iov,
                CEPH_OSD_FLAG_WRITE |
                CEPH_OSD_FLAG_ACK;
 
-       iov_iter_init(&i, iov, nr_segs, count, 0);
-
-       while ((len = iov_iter_count(&i)) > 0) {
+       while ((len = iov_iter_count(from)) > 0) {
                size_t left;
                int n;
 
@@ -737,13 +718,12 @@ static ssize_t ceph_sync_write(struct kiocb *iocb, const struct iovec *iov,
                left = len;
                for (n = 0; n < num_pages; n++) {
                        size_t plen = min_t(size_t, left, PAGE_SIZE);
-                       ret = iov_iter_copy_from_user(pages[n], &i, 0, plen);
+                       ret = copy_page_from_iter(pages[n], 0, plen, from);
                        if (ret != plen) {
                                ret = -EFAULT;
                                break;
                        }
                        left -= ret;
-                       iov_iter_advance(&i, ret);
                }
 
                if (ret < 0) {
@@ -796,8 +776,7 @@ out:
  *
  * Hmm, the sync read case isn't actually async... should it be?
  */
-static ssize_t ceph_aio_read(struct kiocb *iocb, const struct iovec *iov,
-                            unsigned long nr_segs, loff_t pos)
+static ssize_t ceph_read_iter(struct kiocb *iocb, struct iov_iter *to)
 {
        struct file *filp = iocb->ki_filp;
        struct ceph_file_info *fi = filp->private_data;
@@ -823,40 +802,20 @@ again:
        if ((got & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) == 0 ||
            (iocb->ki_filp->f_flags & O_DIRECT) ||
            (fi->flags & CEPH_F_SYNC)) {
-               struct iov_iter i;
 
                dout("aio_sync_read %p %llx.%llx %llu~%u got cap refs on %s\n",
                     inode, ceph_vinop(inode), iocb->ki_pos, (unsigned)len,
                     ceph_cap_string(got));
 
-               if (!read) {
-                       ret = generic_segment_checks(iov, &nr_segs,
-                                                       &len, VERIFY_WRITE);
-                       if (ret)
-                               goto out;
-               }
-
-               iov_iter_init(&i, iov, nr_segs, len, read);
-
                /* hmm, this isn't really async... */
-               ret = ceph_sync_read(iocb, &i, &checkeof);
+               ret = ceph_sync_read(iocb, to, &checkeof);
        } else {
-               /*
-                * We can't modify the content of iov,
-                * so we only read from beginning.
-                */
-               if (read) {
-                       iocb->ki_pos = pos;
-                       len = iocb->ki_nbytes;
-                       read = 0;
-               }
                dout("aio_read %p %llx.%llx %llu~%u got cap refs on %s\n",
-                    inode, ceph_vinop(inode), pos, (unsigned)len,
+                    inode, ceph_vinop(inode), iocb->ki_pos, (unsigned)len,
                     ceph_cap_string(got));
 
-               ret = generic_file_aio_read(iocb, iov, nr_segs, pos);
+               ret = generic_file_read_iter(iocb, to);
        }
-out:
        dout("aio_read %p %llx.%llx dropping cap refs on %s = %d\n",
             inode, ceph_vinop(inode), ceph_cap_string(got), (int)ret);
        ceph_put_cap_refs(ci, got);
@@ -872,6 +831,7 @@ out:
                             ", reading more\n", iocb->ki_pos,
                             inode->i_size);
 
+                       iov_iter_advance(to, ret);
                        read += ret;
                        len -= ret;
                        checkeof = 0;
@@ -895,8 +855,7 @@ out:
  *
  * If we are near ENOSPC, write synchronously.
  */
-static ssize_t ceph_aio_write(struct kiocb *iocb, const struct iovec *iov,
-                      unsigned long nr_segs, loff_t pos)
+static ssize_t ceph_write_iter(struct kiocb *iocb, struct iov_iter *from)
 {
        struct file *file = iocb->ki_filp;
        struct ceph_file_info *fi = file->private_data;
@@ -904,18 +863,15 @@ static ssize_t ceph_aio_write(struct kiocb *iocb, const struct iovec *iov,
        struct ceph_inode_info *ci = ceph_inode(inode);
        struct ceph_osd_client *osdc =
                &ceph_sb_to_client(inode->i_sb)->client->osdc;
-       ssize_t count, written = 0;
+       ssize_t count = iov_iter_count(from), written = 0;
        int err, want, got;
+       loff_t pos = iocb->ki_pos;
 
        if (ceph_snap(inode) != CEPH_NOSNAP)
                return -EROFS;
 
        mutex_lock(&inode->i_mutex);
 
-       err = generic_segment_checks(iov, &nr_segs, &count, VERIFY_READ);
-       if (err)
-               goto out;
-
        /* We can write back this queue in page reclaim */
        current->backing_dev_info = file->f_mapping->backing_dev_info;
 
@@ -925,6 +881,7 @@ static ssize_t ceph_aio_write(struct kiocb *iocb, const struct iovec *iov,
 
        if (count == 0)
                goto out;
+       iov_iter_truncate(from, count);
 
        err = file_remove_suid(file);
        if (err)
@@ -956,23 +913,26 @@ retry_snap:
 
        if ((got & (CEPH_CAP_FILE_BUFFER|CEPH_CAP_FILE_LAZYIO)) == 0 ||
            (file->f_flags & O_DIRECT) || (fi->flags & CEPH_F_SYNC)) {
+               struct iov_iter data;
                mutex_unlock(&inode->i_mutex);
+               /* we might need to revert back to that point */
+               data = *from;
                if (file->f_flags & O_DIRECT)
-                       written = ceph_sync_direct_write(iocb, iov,
-                                                        nr_segs, count);
+                       written = ceph_sync_direct_write(iocb, &data);
                else
-                       written = ceph_sync_write(iocb, iov, nr_segs, count);
+                       written = ceph_sync_write(iocb, &data);
                if (written == -EOLDSNAPC) {
                        dout("aio_write %p %llx.%llx %llu~%u"
                                "got EOLDSNAPC, retrying\n",
                                inode, ceph_vinop(inode),
-                               pos, (unsigned)iov->iov_len);
+                               pos, (unsigned)count);
                        mutex_lock(&inode->i_mutex);
                        goto retry_snap;
                }
+               if (written > 0)
+                       iov_iter_advance(from, written);
        } else {
                loff_t old_size = inode->i_size;
-               struct iov_iter from;
                /*
                 * No need to acquire the i_truncate_mutex. Because
                 * the MDS revokes Fwb caps before sending truncate
@@ -980,8 +940,7 @@ retry_snap:
                 * are pending vmtruncate. So write and vmtruncate
                 * can not run at the same time
                 */
-               iov_iter_init(&from, iov, nr_segs, count, 0);
-               written = generic_perform_write(file, &from, pos);
+               written = generic_perform_write(file, from, pos);
                if (likely(written >= 0))
                        iocb->ki_pos = pos + written;
                if (inode->i_size > old_size)
@@ -999,7 +958,7 @@ retry_snap:
        }
 
        dout("aio_write %p %llx.%llx %llu~%u  dropping cap refs on %s\n",
-            inode, ceph_vinop(inode), pos, (unsigned)iov->iov_len,
+            inode, ceph_vinop(inode), pos, (unsigned)count,
             ceph_cap_string(got));
        ceph_put_cap_refs(ci, got);
 
@@ -1276,16 +1235,16 @@ const struct file_operations ceph_file_fops = {
        .open = ceph_open,
        .release = ceph_release,
        .llseek = ceph_llseek,
-       .read = do_sync_read,
-       .write = do_sync_write,
-       .aio_read = ceph_aio_read,
-       .aio_write = ceph_aio_write,
+       .read = new_sync_read,
+       .write = new_sync_write,
+       .read_iter = ceph_read_iter,
+       .write_iter = ceph_write_iter,
        .mmap = ceph_mmap,
        .fsync = ceph_fsync,
        .lock = ceph_lock,
        .flock = ceph_flock,
        .splice_read = generic_file_splice_read,
-       .splice_write = generic_file_splice_write,
+       .splice_write = iter_file_splice_write,
        .unlocked_ioctl = ceph_ioctl,
        .compat_ioctl   = ceph_ioctl,
        .fallocate      = ceph_fallocate,
index e4fff9ff1c27c890c939c8836afe1d60504d6ab7..04c89c266cecffa2396d0b63774d4811063bbb0f 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/writeback.h>
 #include <linux/vmalloc.h>
 #include <linux/posix_acl.h>
+#include <linux/random.h>
 
 #include "super.h"
 #include "mds_client.h"
@@ -179,9 +180,8 @@ struct ceph_inode_frag *__ceph_find_frag(struct ceph_inode_info *ci, u32 f)
  * specified, copy the frag delegation info to the caller if
  * it is present.
  */
-u32 ceph_choose_frag(struct ceph_inode_info *ci, u32 v,
-                    struct ceph_inode_frag *pfrag,
-                    int *found)
+static u32 __ceph_choose_frag(struct ceph_inode_info *ci, u32 v,
+                             struct ceph_inode_frag *pfrag, int *found)
 {
        u32 t = ceph_frag_make(0, 0);
        struct ceph_inode_frag *frag;
@@ -191,7 +191,6 @@ u32 ceph_choose_frag(struct ceph_inode_info *ci, u32 v,
        if (found)
                *found = 0;
 
-       mutex_lock(&ci->i_fragtree_mutex);
        while (1) {
                WARN_ON(!ceph_frag_contains_value(t, v));
                frag = __ceph_find_frag(ci, t);
@@ -220,10 +219,19 @@ u32 ceph_choose_frag(struct ceph_inode_info *ci, u32 v,
        }
        dout("choose_frag(%x) = %x\n", v, t);
 
-       mutex_unlock(&ci->i_fragtree_mutex);
        return t;
 }
 
+u32 ceph_choose_frag(struct ceph_inode_info *ci, u32 v,
+                    struct ceph_inode_frag *pfrag, int *found)
+{
+       u32 ret;
+       mutex_lock(&ci->i_fragtree_mutex);
+       ret = __ceph_choose_frag(ci, v, pfrag, found);
+       mutex_unlock(&ci->i_fragtree_mutex);
+       return ret;
+}
+
 /*
  * Process dirfrag (delegation) info from the mds.  Include leaf
  * fragment in tree ONLY if ndist > 0.  Otherwise, only
@@ -237,11 +245,17 @@ static int ceph_fill_dirfrag(struct inode *inode,
        u32 id = le32_to_cpu(dirinfo->frag);
        int mds = le32_to_cpu(dirinfo->auth);
        int ndist = le32_to_cpu(dirinfo->ndist);
+       int diri_auth = -1;
        int i;
        int err = 0;
 
+       spin_lock(&ci->i_ceph_lock);
+       if (ci->i_auth_cap)
+               diri_auth = ci->i_auth_cap->mds;
+       spin_unlock(&ci->i_ceph_lock);
+
        mutex_lock(&ci->i_fragtree_mutex);
-       if (ndist == 0) {
+       if (ndist == 0 && mds == diri_auth) {
                /* no delegation info needed. */
                frag = __ceph_find_frag(ci, id);
                if (!frag)
@@ -286,6 +300,75 @@ out:
        return err;
 }
 
+static int ceph_fill_fragtree(struct inode *inode,
+                             struct ceph_frag_tree_head *fragtree,
+                             struct ceph_mds_reply_dirfrag *dirinfo)
+{
+       struct ceph_inode_info *ci = ceph_inode(inode);
+       struct ceph_inode_frag *frag;
+       struct rb_node *rb_node;
+       int i;
+       u32 id, nsplits;
+       bool update = false;
+
+       mutex_lock(&ci->i_fragtree_mutex);
+       nsplits = le32_to_cpu(fragtree->nsplits);
+       if (nsplits) {
+               i = prandom_u32() % nsplits;
+               id = le32_to_cpu(fragtree->splits[i].frag);
+               if (!__ceph_find_frag(ci, id))
+                       update = true;
+       } else if (!RB_EMPTY_ROOT(&ci->i_fragtree)) {
+               rb_node = rb_first(&ci->i_fragtree);
+               frag = rb_entry(rb_node, struct ceph_inode_frag, node);
+               if (frag->frag != ceph_frag_make(0, 0) || rb_next(rb_node))
+                       update = true;
+       }
+       if (!update && dirinfo) {
+               id = le32_to_cpu(dirinfo->frag);
+               if (id != __ceph_choose_frag(ci, id, NULL, NULL))
+                       update = true;
+       }
+       if (!update)
+               goto out_unlock;
+
+       dout("fill_fragtree %llx.%llx\n", ceph_vinop(inode));
+       rb_node = rb_first(&ci->i_fragtree);
+       for (i = 0; i < nsplits; i++) {
+               id = le32_to_cpu(fragtree->splits[i].frag);
+               frag = NULL;
+               while (rb_node) {
+                       frag = rb_entry(rb_node, struct ceph_inode_frag, node);
+                       if (ceph_frag_compare(frag->frag, id) >= 0) {
+                               if (frag->frag != id)
+                                       frag = NULL;
+                               else
+                                       rb_node = rb_next(rb_node);
+                               break;
+                       }
+                       rb_node = rb_next(rb_node);
+                       rb_erase(&frag->node, &ci->i_fragtree);
+                       kfree(frag);
+                       frag = NULL;
+               }
+               if (!frag) {
+                       frag = __get_or_create_frag(ci, id);
+                       if (IS_ERR(frag))
+                               continue;
+               }
+               frag->split_by = le32_to_cpu(fragtree->splits[i].by);
+               dout(" frag %x split by %d\n", frag->frag, frag->split_by);
+       }
+       while (rb_node) {
+               frag = rb_entry(rb_node, struct ceph_inode_frag, node);
+               rb_node = rb_next(rb_node);
+               rb_erase(&frag->node, &ci->i_fragtree);
+               kfree(frag);
+       }
+out_unlock:
+       mutex_unlock(&ci->i_fragtree_mutex);
+       return 0;
+}
 
 /*
  * initialize a newly allocated inode.
@@ -341,7 +424,6 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
        INIT_LIST_HEAD(&ci->i_cap_snaps);
        ci->i_head_snapc = NULL;
        ci->i_snap_caps = 0;
-       ci->i_cap_exporting_issued = 0;
 
        for (i = 0; i < CEPH_FILE_MODE_NUM; i++)
                ci->i_nr_by_mode[i] = 0;
@@ -407,7 +489,7 @@ void ceph_destroy_inode(struct inode *inode)
 
        /*
         * we may still have a snap_realm reference if there are stray
-        * caps in i_cap_exporting_issued or i_snap_caps.
+        * caps in i_snap_caps.
         */
        if (ci->i_snap_realm) {
                struct ceph_mds_client *mdsc =
@@ -582,22 +664,26 @@ static int fill_inode(struct inode *inode,
                      unsigned long ttl_from, int cap_fmode,
                      struct ceph_cap_reservation *caps_reservation)
 {
+       struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
        struct ceph_mds_reply_inode *info = iinfo->in;
        struct ceph_inode_info *ci = ceph_inode(inode);
-       int i;
-       int issued = 0, implemented;
+       int issued = 0, implemented, new_issued;
        struct timespec mtime, atime, ctime;
-       u32 nsplits;
-       struct ceph_inode_frag *frag;
-       struct rb_node *rb_node;
        struct ceph_buffer *xattr_blob = NULL;
+       struct ceph_cap *new_cap = NULL;
        int err = 0;
-       int queue_trunc = 0;
+       bool wake = false;
+       bool queue_trunc = false;
+       bool new_version = false;
 
        dout("fill_inode %p ino %llx.%llx v %llu had %llu\n",
             inode, ceph_vinop(inode), le64_to_cpu(info->version),
             ci->i_version);
 
+       /* prealloc new cap struct */
+       if (info->cap.caps && ceph_snap(inode) == CEPH_NOSNAP)
+               new_cap = ceph_get_cap(mdsc, caps_reservation);
+
        /*
         * prealloc xattr data, if it looks like we'll need it.  only
         * if len > 4 (meaning there are actually xattrs; the first 4
@@ -623,19 +709,23 @@ static int fill_inode(struct inode *inode,
         *   3    2     skip
         *   3    3     update
         */
-       if (le64_to_cpu(info->version) > 0 &&
-           (ci->i_version & ~1) >= le64_to_cpu(info->version))
-               goto no_change;
-       
+       if (ci->i_version == 0 ||
+           ((info->cap.flags & CEPH_CAP_FLAG_AUTH) &&
+            le64_to_cpu(info->version) > (ci->i_version & ~1)))
+               new_version = true;
+
        issued = __ceph_caps_issued(ci, &implemented);
        issued |= implemented | __ceph_caps_dirty(ci);
+       new_issued = ~issued & le32_to_cpu(info->cap.caps);
 
        /* update inode */
        ci->i_version = le64_to_cpu(info->version);
        inode->i_version++;
        inode->i_rdev = le32_to_cpu(info->rdev);
+       inode->i_blkbits = fls(le32_to_cpu(info->layout.fl_stripe_unit)) - 1;
 
-       if ((issued & CEPH_CAP_AUTH_EXCL) == 0) {
+       if ((new_version || (new_issued & CEPH_CAP_AUTH_SHARED)) &&
+           (issued & CEPH_CAP_AUTH_EXCL) == 0) {
                inode->i_mode = le32_to_cpu(info->mode);
                inode->i_uid = make_kuid(&init_user_ns, le32_to_cpu(info->uid));
                inode->i_gid = make_kgid(&init_user_ns, le32_to_cpu(info->gid));
@@ -644,23 +734,35 @@ static int fill_inode(struct inode *inode,
                     from_kgid(&init_user_ns, inode->i_gid));
        }
 
-       if ((issued & CEPH_CAP_LINK_EXCL) == 0)
+       if ((new_version || (new_issued & CEPH_CAP_LINK_SHARED)) &&
+           (issued & CEPH_CAP_LINK_EXCL) == 0)
                set_nlink(inode, le32_to_cpu(info->nlink));
 
-       /* be careful with mtime, atime, size */
-       ceph_decode_timespec(&atime, &info->atime);
-       ceph_decode_timespec(&mtime, &info->mtime);
-       ceph_decode_timespec(&ctime, &info->ctime);
-       queue_trunc = ceph_fill_file_size(inode, issued,
-                                         le32_to_cpu(info->truncate_seq),
-                                         le64_to_cpu(info->truncate_size),
-                                         le64_to_cpu(info->size));
-       ceph_fill_file_time(inode, issued,
-                           le32_to_cpu(info->time_warp_seq),
-                           &ctime, &mtime, &atime);
-
-       ci->i_layout = info->layout;
-       inode->i_blkbits = fls(le32_to_cpu(info->layout.fl_stripe_unit)) - 1;
+       if (new_version || (new_issued & CEPH_CAP_ANY_RD)) {
+               /* be careful with mtime, atime, size */
+               ceph_decode_timespec(&atime, &info->atime);
+               ceph_decode_timespec(&mtime, &info->mtime);
+               ceph_decode_timespec(&ctime, &info->ctime);
+               ceph_fill_file_time(inode, issued,
+                               le32_to_cpu(info->time_warp_seq),
+                               &ctime, &mtime, &atime);
+       }
+
+       if (new_version ||
+           (new_issued & (CEPH_CAP_ANY_FILE_RD | CEPH_CAP_ANY_FILE_WR))) {
+               ci->i_layout = info->layout;
+               queue_trunc = ceph_fill_file_size(inode, issued,
+                                       le32_to_cpu(info->truncate_seq),
+                                       le64_to_cpu(info->truncate_size),
+                                       le64_to_cpu(info->size));
+               /* only update max_size on auth cap */
+               if ((info->cap.flags & CEPH_CAP_FLAG_AUTH) &&
+                   ci->i_max_size != le64_to_cpu(info->max_size)) {
+                       dout("max_size %lld -> %llu\n", ci->i_max_size,
+                                       le64_to_cpu(info->max_size));
+                       ci->i_max_size = le64_to_cpu(info->max_size);
+               }
+       }
 
        /* xattrs */
        /* note that if i_xattrs.len <= 4, i_xattrs.data will still be NULL. */
@@ -745,58 +847,6 @@ static int fill_inode(struct inode *inode,
                dout(" marking %p complete (empty)\n", inode);
                __ceph_dir_set_complete(ci, atomic_read(&ci->i_release_count));
        }
-no_change:
-       /* only update max_size on auth cap */
-       if ((info->cap.flags & CEPH_CAP_FLAG_AUTH) &&
-           ci->i_max_size != le64_to_cpu(info->max_size)) {
-               dout("max_size %lld -> %llu\n", ci->i_max_size,
-                    le64_to_cpu(info->max_size));
-               ci->i_max_size = le64_to_cpu(info->max_size);
-       }
-
-       spin_unlock(&ci->i_ceph_lock);
-
-       /* queue truncate if we saw i_size decrease */
-       if (queue_trunc)
-               ceph_queue_vmtruncate(inode);
-
-       /* populate frag tree */
-       /* FIXME: move me up, if/when version reflects fragtree changes */
-       nsplits = le32_to_cpu(info->fragtree.nsplits);
-       mutex_lock(&ci->i_fragtree_mutex);
-       rb_node = rb_first(&ci->i_fragtree);
-       for (i = 0; i < nsplits; i++) {
-               u32 id = le32_to_cpu(info->fragtree.splits[i].frag);
-               frag = NULL;
-               while (rb_node) {
-                       frag = rb_entry(rb_node, struct ceph_inode_frag, node);
-                       if (ceph_frag_compare(frag->frag, id) >= 0) {
-                               if (frag->frag != id)
-                                       frag = NULL;
-                               else
-                                       rb_node = rb_next(rb_node);
-                               break;
-                       }
-                       rb_node = rb_next(rb_node);
-                       rb_erase(&frag->node, &ci->i_fragtree);
-                       kfree(frag);
-                       frag = NULL;
-               }
-               if (!frag) {
-                       frag = __get_or_create_frag(ci, id);
-                       if (IS_ERR(frag))
-                               continue;
-               }
-               frag->split_by = le32_to_cpu(info->fragtree.splits[i].by);
-               dout(" frag %x split by %d\n", frag->frag, frag->split_by);
-       }
-       while (rb_node) {
-               frag = rb_entry(rb_node, struct ceph_inode_frag, node);
-               rb_node = rb_next(rb_node);
-               rb_erase(&frag->node, &ci->i_fragtree);
-               kfree(frag);
-       }
-       mutex_unlock(&ci->i_fragtree_mutex);
 
        /* were we issued a capability? */
        if (info->cap.caps) {
@@ -809,30 +859,41 @@ no_change:
                                     le32_to_cpu(info->cap.seq),
                                     le32_to_cpu(info->cap.mseq),
                                     le64_to_cpu(info->cap.realm),
-                                    info->cap.flags,
-                                    caps_reservation);
+                                    info->cap.flags, &new_cap);
+                       wake = true;
                } else {
-                       spin_lock(&ci->i_ceph_lock);
                        dout(" %p got snap_caps %s\n", inode,
                             ceph_cap_string(le32_to_cpu(info->cap.caps)));
                        ci->i_snap_caps |= le32_to_cpu(info->cap.caps);
                        if (cap_fmode >= 0)
                                __ceph_get_fmode(ci, cap_fmode);
-                       spin_unlock(&ci->i_ceph_lock);
                }
        } else if (cap_fmode >= 0) {
                pr_warn("mds issued no caps on %llx.%llx\n",
                           ceph_vinop(inode));
                __ceph_get_fmode(ci, cap_fmode);
        }
+       spin_unlock(&ci->i_ceph_lock);
+
+       if (wake)
+               wake_up_all(&ci->i_cap_wq);
+
+       /* queue truncate if we saw i_size decrease */
+       if (queue_trunc)
+               ceph_queue_vmtruncate(inode);
+
+       /* populate frag tree */
+       if (S_ISDIR(inode->i_mode))
+               ceph_fill_fragtree(inode, &info->fragtree, dirinfo);
 
        /* update delegation info? */
        if (dirinfo)
                ceph_fill_dirfrag(inode, dirinfo);
 
        err = 0;
-
 out:
+       if (new_cap)
+               ceph_put_cap(mdsc, new_cap);
        if (xattr_blob)
                ceph_buffer_put(xattr_blob);
        return err;
@@ -1485,7 +1546,7 @@ static void ceph_invalidate_work(struct work_struct *work)
        orig_gen = ci->i_rdcache_gen;
        spin_unlock(&ci->i_ceph_lock);
 
-       truncate_inode_pages(inode->i_mapping, 0);
+       truncate_pagecache(inode, 0);
 
        spin_lock(&ci->i_ceph_lock);
        if (orig_gen == ci->i_rdcache_gen &&
@@ -1588,7 +1649,7 @@ retry:
             ci->i_truncate_pending, to);
        spin_unlock(&ci->i_ceph_lock);
 
-       truncate_inode_pages(inode->i_mapping, to);
+       truncate_pagecache(inode, to);
 
        spin_lock(&ci->i_ceph_lock);
        if (to == ci->i_truncate_size) {
index 9a33b98cb0000df58714bbc842db7a80b9999099..92a2548278fca0c52609d120db33cf070db621c6 100644 (file)
@@ -1558,6 +1558,8 @@ ceph_mdsc_create_request(struct ceph_mds_client *mdsc, int op, int mode)
        init_completion(&req->r_safe_completion);
        INIT_LIST_HEAD(&req->r_unsafe_item);
 
+       req->r_stamp = CURRENT_TIME;
+
        req->r_op = op;
        req->r_direct_mode = mode;
        return req;
@@ -1783,7 +1785,8 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc,
        }
 
        len = sizeof(*head) +
-               pathlen1 + pathlen2 + 2*(1 + sizeof(u32) + sizeof(u64));
+               pathlen1 + pathlen2 + 2*(1 + sizeof(u32) + sizeof(u64)) +
+               sizeof(struct timespec);
 
        /* calculate (max) length for cap releases */
        len += sizeof(struct ceph_mds_request_release) *
@@ -1800,6 +1803,7 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc,
                goto out_free2;
        }
 
+       msg->hdr.version = 2;
        msg->hdr.tid = cpu_to_le64(req->r_tid);
 
        head = msg->front.iov_base;
@@ -1836,6 +1840,9 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc,
                      mds, req->r_old_inode_drop, req->r_old_inode_unless, 0);
        head->num_releases = cpu_to_le16(releases);
 
+       /* time stamp */
+       ceph_encode_copy(&p, &req->r_stamp, sizeof(req->r_stamp));
+
        BUG_ON(p > end);
        msg->front.iov_len = p - msg->front.iov_base;
        msg->hdr.front_len = cpu_to_le32(msg->front.iov_len);
index e90cfccf93bd9e90a21ef3c22ebfa9b54e43b3a1..e00737cf523c0ae237121fe0fdcef0d147b2d9f2 100644 (file)
@@ -194,6 +194,7 @@ struct ceph_mds_request {
        int r_fmode;        /* file mode, if expecting cap */
        kuid_t r_uid;
        kgid_t r_gid;
+       struct timespec r_stamp;
 
        /* for choosing which mds to send this request to */
        int r_direct_mode;
index ead05cc1f447562271578131ab25769257080915..12b20744e386f1281f454f6fb636729beb01e873 100644 (file)
@@ -292,7 +292,6 @@ struct ceph_inode_info {
        struct ceph_snap_context *i_head_snapc;  /* set if wr_buffer_head > 0 or
                                                    dirty|flushing caps */
        unsigned i_snap_caps;           /* cap bits for snapped files */
-       unsigned i_cap_exporting_issued;
 
        int i_nr_by_mode[CEPH_FILE_MODE_NUM];  /* open file counts */
 
@@ -775,11 +774,13 @@ static inline void ceph_forget_all_cached_acls(struct inode *inode)
 extern const char *ceph_cap_string(int c);
 extern void ceph_handle_caps(struct ceph_mds_session *session,
                             struct ceph_msg *msg);
-extern int ceph_add_cap(struct inode *inode,
-                       struct ceph_mds_session *session, u64 cap_id,
-                       int fmode, unsigned issued, unsigned wanted,
-                       unsigned cap, unsigned seq, u64 realmino, int flags,
-                       struct ceph_cap_reservation *caps_reservation);
+extern struct ceph_cap *ceph_get_cap(struct ceph_mds_client *mdsc,
+                                    struct ceph_cap_reservation *ctx);
+extern void ceph_add_cap(struct inode *inode,
+                        struct ceph_mds_session *session, u64 cap_id,
+                        int fmode, unsigned issued, unsigned wanted,
+                        unsigned cap, unsigned seq, u64 realmino, int flags,
+                        struct ceph_cap **new_cap);
 extern void __ceph_remove_cap(struct ceph_cap *cap, bool queue_release);
 extern void ceph_put_cap(struct ceph_mds_client *mdsc,
                         struct ceph_cap *cap);
index 6aaa8112c538a73c82b15eaf8dd733abd21f39f0..2c90d07c0b3aa3a6db836e0290fd0ecc2137b317 100644 (file)
@@ -725,8 +725,7 @@ out_nls:
        goto out;
 }
 
-static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
-                                  unsigned long nr_segs, loff_t pos)
+static ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
 {
        struct inode *inode = file_inode(iocb->ki_filp);
        struct cifsInodeInfo *cinode = CIFS_I(inode);
@@ -737,14 +736,14 @@ static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
        if (written)
                return written;
 
-       written = generic_file_aio_write(iocb, iov, nr_segs, pos);
+       written = generic_file_write_iter(iocb, from);
 
        if (CIFS_CACHE_WRITE(CIFS_I(inode)))
                goto out;
 
        rc = filemap_fdatawrite(inode->i_mapping);
        if (rc)
-               cifs_dbg(FYI, "cifs_file_aio_write: %d rc on %p inode\n",
+               cifs_dbg(FYI, "cifs_file_write_iter: %d rc on %p inode\n",
                         rc, inode);
 
 out:
@@ -880,10 +879,10 @@ const struct inode_operations cifs_symlink_inode_ops = {
 };
 
 const struct file_operations cifs_file_ops = {
-       .read = do_sync_read,
-       .write = do_sync_write,
-       .aio_read = generic_file_aio_read,
-       .aio_write = cifs_file_aio_write,
+       .read = new_sync_read,
+       .write = new_sync_write,
+       .read_iter = generic_file_read_iter,
+       .write_iter = cifs_file_write_iter,
        .open = cifs_open,
        .release = cifs_close,
        .lock = cifs_lock,
@@ -899,10 +898,10 @@ const struct file_operations cifs_file_ops = {
 };
 
 const struct file_operations cifs_file_strict_ops = {
-       .read = do_sync_read,
-       .write = do_sync_write,
-       .aio_read = cifs_strict_readv,
-       .aio_write = cifs_strict_writev,
+       .read = new_sync_read,
+       .write = new_sync_write,
+       .read_iter = cifs_strict_readv,
+       .write_iter = cifs_strict_writev,
        .open = cifs_open,
        .release = cifs_close,
        .lock = cifs_lock,
@@ -919,10 +918,10 @@ const struct file_operations cifs_file_strict_ops = {
 
 const struct file_operations cifs_file_direct_ops = {
        /* BB reevaluate whether they can be done with directio, no cache */
-       .read = do_sync_read,
-       .write = do_sync_write,
-       .aio_read = cifs_user_readv,
-       .aio_write = cifs_user_writev,
+       .read = new_sync_read,
+       .write = new_sync_write,
+       .read_iter = cifs_user_readv,
+       .write_iter = cifs_user_writev,
        .open = cifs_open,
        .release = cifs_close,
        .lock = cifs_lock,
@@ -938,10 +937,10 @@ const struct file_operations cifs_file_direct_ops = {
 };
 
 const struct file_operations cifs_file_nobrl_ops = {
-       .read = do_sync_read,
-       .write = do_sync_write,
-       .aio_read = generic_file_aio_read,
-       .aio_write = cifs_file_aio_write,
+       .read = new_sync_read,
+       .write = new_sync_write,
+       .read_iter = generic_file_read_iter,
+       .write_iter = cifs_file_write_iter,
        .open = cifs_open,
        .release = cifs_close,
        .fsync = cifs_fsync,
@@ -956,10 +955,10 @@ const struct file_operations cifs_file_nobrl_ops = {
 };
 
 const struct file_operations cifs_file_strict_nobrl_ops = {
-       .read = do_sync_read,
-       .write = do_sync_write,
-       .aio_read = cifs_strict_readv,
-       .aio_write = cifs_strict_writev,
+       .read = new_sync_read,
+       .write = new_sync_write,
+       .read_iter = cifs_strict_readv,
+       .write_iter = cifs_strict_writev,
        .open = cifs_open,
        .release = cifs_close,
        .fsync = cifs_strict_fsync,
@@ -975,10 +974,10 @@ const struct file_operations cifs_file_strict_nobrl_ops = {
 
 const struct file_operations cifs_file_direct_nobrl_ops = {
        /* BB reevaluate whether they can be done with directio, no cache */
-       .read = do_sync_read,
-       .write = do_sync_write,
-       .aio_read = cifs_user_readv,
-       .aio_write = cifs_user_writev,
+       .read = new_sync_read,
+       .write = new_sync_write,
+       .read_iter = cifs_user_readv,
+       .write_iter = cifs_user_writev,
        .open = cifs_open,
        .release = cifs_close,
        .fsync = cifs_fsync,
index 8fe51166d6e3192bb8aadf2d86f5a0acf494622a..70f178a7c759525a17fc758e0637bf1d0c941ead 100644 (file)
@@ -95,14 +95,10 @@ extern const struct file_operations cifs_file_strict_nobrl_ops;
 extern int cifs_open(struct inode *inode, struct file *file);
 extern int cifs_close(struct inode *inode, struct file *file);
 extern int cifs_closedir(struct inode *inode, struct file *file);
-extern ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov,
-                              unsigned long nr_segs, loff_t pos);
-extern ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
-                                unsigned long nr_segs, loff_t pos);
-extern ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,
-                               unsigned long nr_segs, loff_t pos);
-extern ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
-                                 unsigned long nr_segs, loff_t pos);
+extern ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to);
+extern ssize_t cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to);
+extern ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from);
+extern ssize_t cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from);
 extern int cifs_lock(struct file *, int, struct file_lock *);
 extern int cifs_fsync(struct file *, loff_t, loff_t, int);
 extern int cifs_strict_fsync(struct file *, loff_t, loff_t, int);
index 208f56eca4bf4de164d8af873b0050ac4884c5ea..e90a1e9aa627642c9ccefd428319f43b3d379c2f 100644 (file)
@@ -2385,14 +2385,12 @@ cifs_uncached_retry_writev(struct cifs_writedata *wdata)
 }
 
 static ssize_t
-cifs_iovec_write(struct file *file, const struct iovec *iov,
-                unsigned long nr_segs, loff_t *poffset)
+cifs_iovec_write(struct file *file, struct iov_iter *from, loff_t *poffset)
 {
        unsigned long nr_pages, i;
        size_t bytes, copied, len, cur_len;
        ssize_t total_written = 0;
        loff_t offset;
-       struct iov_iter it;
        struct cifsFileInfo *open_file;
        struct cifs_tcon *tcon;
        struct cifs_sb_info *cifs_sb;
@@ -2401,14 +2399,16 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
        int rc;
        pid_t pid;
 
-       len = iov_length(iov, nr_segs);
-       if (!len)
-               return 0;
-
+       len = iov_iter_count(from);
        rc = generic_write_checks(file, poffset, &len, 0);
        if (rc)
                return rc;
 
+       if (!len)
+               return 0;
+
+       iov_iter_truncate(from, len);
+
        INIT_LIST_HEAD(&wdata_list);
        cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
        open_file = file->private_data;
@@ -2424,7 +2424,6 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
        else
                pid = current->tgid;
 
-       iov_iter_init(&it, iov, nr_segs, len, 0);
        do {
                size_t save_len;
 
@@ -2444,11 +2443,10 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
 
                save_len = cur_len;
                for (i = 0; i < nr_pages; i++) {
-                       bytes = min_t(const size_t, cur_len, PAGE_SIZE);
-                       copied = iov_iter_copy_from_user(wdata->pages[i], &it,
-                                                        0, bytes);
+                       bytes = min_t(size_t, cur_len, PAGE_SIZE);
+                       copied = copy_page_from_iter(wdata->pages[i], 0, bytes,
+                                                    from);
                        cur_len -= copied;
-                       iov_iter_advance(&it, copied);
                        /*
                         * If we didn't copy as much as we expected, then that
                         * may mean we trod into an unmapped area. Stop copying
@@ -2546,11 +2544,11 @@ restart_loop:
        return total_written ? total_written : (ssize_t)rc;
 }
 
-ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,
-                               unsigned long nr_segs, loff_t pos)
+ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from)
 {
        ssize_t written;
        struct inode *inode;
+       loff_t pos = iocb->ki_pos;
 
        inode = file_inode(iocb->ki_filp);
 
@@ -2560,7 +2558,7 @@ ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,
         * write request.
         */
 
-       written = cifs_iovec_write(iocb->ki_filp, iov, nr_segs, &pos);
+       written = cifs_iovec_write(iocb->ki_filp, from, &pos);
        if (written > 0) {
                set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(inode)->flags);
                iocb->ki_pos = pos;
@@ -2570,8 +2568,7 @@ ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,
 }
 
 static ssize_t
-cifs_writev(struct kiocb *iocb, const struct iovec *iov,
-           unsigned long nr_segs, loff_t pos)
+cifs_writev(struct kiocb *iocb, struct iov_iter *from)
 {
        struct file *file = iocb->ki_filp;
        struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
@@ -2589,10 +2586,10 @@ cifs_writev(struct kiocb *iocb, const struct iovec *iov,
        mutex_lock(&inode->i_mutex);
        if (file->f_flags & O_APPEND)
                lock_pos = i_size_read(inode);
-       if (!cifs_find_lock_conflict(cfile, lock_pos, iov_length(iov, nr_segs),
+       if (!cifs_find_lock_conflict(cfile, lock_pos, iov_iter_count(from),
                                     server->vals->exclusive_lock_type, NULL,
                                     CIFS_WRITE_OP)) {
-               rc = __generic_file_aio_write(iocb, iov, nr_segs);
+               rc = __generic_file_write_iter(iocb, from);
                mutex_unlock(&inode->i_mutex);
 
                if (rc > 0) {
@@ -2610,8 +2607,7 @@ cifs_writev(struct kiocb *iocb, const struct iovec *iov,
 }
 
 ssize_t
-cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
-                  unsigned long nr_segs, loff_t pos)
+cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from)
 {
        struct inode *inode = file_inode(iocb->ki_filp);
        struct cifsInodeInfo *cinode = CIFS_I(inode);
@@ -2629,11 +2625,10 @@ cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
                if (cap_unix(tcon->ses) &&
                (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))
                  && ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) {
-                       written = generic_file_aio_write(
-                                       iocb, iov, nr_segs, pos);
+                       written = generic_file_write_iter(iocb, from);
                        goto out;
                }
-               written = cifs_writev(iocb, iov, nr_segs, pos);
+               written = cifs_writev(iocb, from);
                goto out;
        }
        /*
@@ -2642,7 +2637,7 @@ cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
         * affected pages because it may cause a error with mandatory locks on
         * these pages but not on the region from pos to ppos+len-1.
         */
-       written = cifs_user_writev(iocb, iov, nr_segs, pos);
+       written = cifs_user_writev(iocb, from);
        if (written > 0 && CIFS_CACHE_READ(cinode)) {
                /*
                 * Windows 7 server can delay breaking level2 oplock if a write
@@ -2831,32 +2826,25 @@ cifs_uncached_read_into_pages(struct TCP_Server_Info *server,
        return total_read > 0 ? total_read : result;
 }
 
-ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov,
-                              unsigned long nr_segs, loff_t pos)
+ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to)
 {
        struct file *file = iocb->ki_filp;
        ssize_t rc;
        size_t len, cur_len;
        ssize_t total_read = 0;
-       loff_t offset = pos;
+       loff_t offset = iocb->ki_pos;
        unsigned int npages;
        struct cifs_sb_info *cifs_sb;
        struct cifs_tcon *tcon;
        struct cifsFileInfo *open_file;
        struct cifs_readdata *rdata, *tmp;
        struct list_head rdata_list;
-       struct iov_iter to;
        pid_t pid;
 
-       if (!nr_segs)
-               return 0;
-
-       len = iov_length(iov, nr_segs);
+       len = iov_iter_count(to);
        if (!len)
                return 0;
 
-       iov_iter_init(&to, iov, nr_segs, len, 0);
-
        INIT_LIST_HEAD(&rdata_list);
        cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
        open_file = file->private_data;
@@ -2914,7 +2902,7 @@ error:
        if (!list_empty(&rdata_list))
                rc = 0;
 
-       len = iov_iter_count(&to);
+       len = iov_iter_count(to);
        /* the loop below should proceed in the order of increasing offsets */
        list_for_each_entry_safe(rdata, tmp, &rdata_list, list) {
        again:
@@ -2931,7 +2919,7 @@ error:
                                        goto again;
                                }
                        } else {
-                               rc = cifs_readdata_to_iov(rdata, &to);
+                               rc = cifs_readdata_to_iov(rdata, to);
                        }
 
                }
@@ -2939,7 +2927,7 @@ error:
                kref_put(&rdata->refcount, cifs_uncached_readdata_release);
        }
 
-       total_read = len - iov_iter_count(&to);
+       total_read = len - iov_iter_count(to);
 
        cifs_stats_bytes_read(tcon, total_read);
 
@@ -2948,15 +2936,14 @@ error:
                rc = 0;
 
        if (total_read) {
-               iocb->ki_pos = pos + total_read;
+               iocb->ki_pos += total_read;
                return total_read;
        }
        return rc;
 }
 
 ssize_t
-cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
-                 unsigned long nr_segs, loff_t pos)
+cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to)
 {
        struct inode *inode = file_inode(iocb->ki_filp);
        struct cifsInodeInfo *cinode = CIFS_I(inode);
@@ -2975,22 +2962,22 @@ cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
         * pos+len-1.
         */
        if (!CIFS_CACHE_READ(cinode))
-               return cifs_user_readv(iocb, iov, nr_segs, pos);
+               return cifs_user_readv(iocb, to);
 
        if (cap_unix(tcon->ses) &&
            (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
            ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
-               return generic_file_aio_read(iocb, iov, nr_segs, pos);
+               return generic_file_read_iter(iocb, to);
 
        /*
         * We need to hold the sem to be sure nobody modifies lock list
         * with a brlock that prevents reading.
         */
        down_read(&cinode->lock_sem);
-       if (!cifs_find_lock_conflict(cfile, pos, iov_length(iov, nr_segs),
+       if (!cifs_find_lock_conflict(cfile, iocb->ki_pos, iov_iter_count(to),
                                     tcon->ses->server->vals->shared_lock_type,
                                     NULL, CIFS_READ_OP))
-               rc = generic_file_aio_read(iocb, iov, nr_segs, pos);
+               rc = generic_file_read_iter(iocb, to);
        up_read(&cinode->lock_sem);
        return rc;
 }
@@ -3703,8 +3690,8 @@ void cifs_oplock_break(struct work_struct *work)
  * Direct IO is not yet supported in the cached mode. 
  */
 static ssize_t
-cifs_direct_io(int rw, struct kiocb *iocb, const struct iovec *iov,
-               loff_t pos, unsigned long nr_segs)
+cifs_direct_io(int rw, struct kiocb *iocb, struct iov_iter *iter,
+               loff_t pos)
 {
         /*
          * FIXME
index 1792d6075b4f80ced75e04d17c02b8d362b51aac..06f65857a855725247c1190d243c0e19cccd8570 100644 (file)
@@ -532,10 +532,12 @@ static inline struct dentry *lock_parent(struct dentry *dentry)
        struct dentry *parent = dentry->d_parent;
        if (IS_ROOT(dentry))
                return NULL;
+       if (unlikely((int)dentry->d_lockref.count < 0))
+               return NULL;
        if (likely(spin_trylock(&parent->d_lock)))
                return parent;
-       spin_unlock(&dentry->d_lock);
        rcu_read_lock();
+       spin_unlock(&dentry->d_lock);
 again:
        parent = ACCESS_ONCE(dentry->d_parent);
        spin_lock(&parent->d_lock);
index 31ba0935e32ed2f271253a1d828778a91193b211..98040ba388ac1e2db62f96f253bc141758013b10 100644 (file)
@@ -77,7 +77,6 @@ struct dio_submit {
        unsigned blocks_available;      /* At block_in_file.  changes */
        int reap_counter;               /* rate limit reaping */
        sector_t final_block_in_request;/* doesn't change */
-       unsigned first_block_in_page;   /* doesn't change, Used only once */
        int boundary;                   /* prev block is at a boundary */
        get_block_t *get_block;         /* block mapping function */
        dio_submit_t *submit_io;        /* IO submition function */
@@ -98,19 +97,14 @@ struct dio_submit {
        sector_t cur_page_block;        /* Where it starts */
        loff_t cur_page_fs_offset;      /* Offset in file */
 
-       /*
-        * Page fetching state. These variables belong to dio_refill_pages().
-        */
-       int curr_page;                  /* changes */
-       int total_pages;                /* doesn't change */
-       unsigned long curr_user_address;/* changes */
-
+       struct iov_iter *iter;
        /*
         * Page queue.  These variables belong to dio_refill_pages() and
         * dio_get_page().
         */
        unsigned head;                  /* next page to process */
        unsigned tail;                  /* last valid page + 1 */
+       size_t from, to;
 };
 
 /* dio_state communicated between submission path and end_io */
@@ -163,15 +157,10 @@ static inline unsigned dio_pages_present(struct dio_submit *sdio)
  */
 static inline int dio_refill_pages(struct dio *dio, struct dio_submit *sdio)
 {
-       int ret;
-       int nr_pages;
+       ssize_t ret;
 
-       nr_pages = min(sdio->total_pages - sdio->curr_page, DIO_PAGES);
-       ret = get_user_pages_fast(
-               sdio->curr_user_address,                /* Where from? */
-               nr_pages,                       /* How many pages? */
-               dio->rw == READ,                /* Write to memory? */
-               &dio->pages[0]);                /* Put results here */
+       ret = iov_iter_get_pages(sdio->iter, dio->pages, DIO_PAGES * PAGE_SIZE,
+                               &sdio->from);
 
        if (ret < 0 && sdio->blocks_available && (dio->rw & WRITE)) {
                struct page *page = ZERO_PAGE(0);
@@ -186,18 +175,19 @@ static inline int dio_refill_pages(struct dio *dio, struct dio_submit *sdio)
                dio->pages[0] = page;
                sdio->head = 0;
                sdio->tail = 1;
-               ret = 0;
-               goto out;
+               sdio->from = 0;
+               sdio->to = PAGE_SIZE;
+               return 0;
        }
 
        if (ret >= 0) {
-               sdio->curr_user_address += ret * PAGE_SIZE;
-               sdio->curr_page += ret;
+               iov_iter_advance(sdio->iter, ret);
+               ret += sdio->from;
                sdio->head = 0;
-               sdio->tail = ret;
-               ret = 0;
+               sdio->tail = (ret + PAGE_SIZE - 1) / PAGE_SIZE;
+               sdio->to = ((ret - 1) & (PAGE_SIZE - 1)) + 1;
+               return 0;
        }
-out:
        return ret;     
 }
 
@@ -208,8 +198,9 @@ out:
  * L1 cache.
  */
 static inline struct page *dio_get_page(struct dio *dio,
-               struct dio_submit *sdio)
+               struct dio_submit *sdio, size_t *from, size_t *to)
 {
+       int n;
        if (dio_pages_present(sdio) == 0) {
                int ret;
 
@@ -218,7 +209,10 @@ static inline struct page *dio_get_page(struct dio *dio,
                        return ERR_PTR(ret);
                BUG_ON(dio_pages_present(sdio) == 0);
        }
-       return dio->pages[sdio->head++];
+       n = sdio->head++;
+       *from = n ? 0 : sdio->from;
+       *to = (n == sdio->tail - 1) ? sdio->to : PAGE_SIZE;
+       return dio->pages[n];
 }
 
 /**
@@ -422,8 +416,8 @@ static inline void dio_bio_submit(struct dio *dio, struct dio_submit *sdio)
  */
 static inline void dio_cleanup(struct dio *dio, struct dio_submit *sdio)
 {
-       while (dio_pages_present(sdio))
-               page_cache_release(dio_get_page(dio, sdio));
+       while (sdio->head < sdio->tail)
+               page_cache_release(dio->pages[sdio->head++]);
 }
 
 /*
@@ -912,23 +906,18 @@ static int do_direct_IO(struct dio *dio, struct dio_submit *sdio,
                        struct buffer_head *map_bh)
 {
        const unsigned blkbits = sdio->blkbits;
-       const unsigned blocks_per_page = PAGE_SIZE >> blkbits;
-       struct page *page;
-       unsigned block_in_page;
        int ret = 0;
 
-       /* The I/O can start at any block offset within the first page */
-       block_in_page = sdio->first_block_in_page;
-
        while (sdio->block_in_file < sdio->final_block_in_request) {
-               page = dio_get_page(dio, sdio);
+               struct page *page;
+               size_t from, to;
+               page = dio_get_page(dio, sdio, &from, &to);
                if (IS_ERR(page)) {
                        ret = PTR_ERR(page);
                        goto out;
                }
 
-               while (block_in_page < blocks_per_page) {
-                       unsigned offset_in_page = block_in_page << blkbits;
+               while (from < to) {
                        unsigned this_chunk_bytes;      /* # of bytes mapped */
                        unsigned this_chunk_blocks;     /* # of blocks */
                        unsigned u;
@@ -999,10 +988,10 @@ do_holes:
                                        page_cache_release(page);
                                        goto out;
                                }
-                               zero_user(page, block_in_page << blkbits,
-                                               1 << blkbits);
+                               zero_user(page, from, 1 << blkbits);
                                sdio->block_in_file++;
-                               block_in_page++;
+                               from += 1 << blkbits;
+                               dio->result += 1 << blkbits;
                                goto next_block;
                        }
 
@@ -1019,7 +1008,7 @@ do_holes:
                         * can add to this page
                         */
                        this_chunk_blocks = sdio->blocks_available;
-                       u = (PAGE_SIZE - offset_in_page) >> blkbits;
+                       u = (to - from) >> blkbits;
                        if (this_chunk_blocks > u)
                                this_chunk_blocks = u;
                        u = sdio->final_block_in_request - sdio->block_in_file;
@@ -1031,7 +1020,7 @@ do_holes:
                        if (this_chunk_blocks == sdio->blocks_available)
                                sdio->boundary = buffer_boundary(map_bh);
                        ret = submit_page_section(dio, sdio, page,
-                                                 offset_in_page,
+                                                 from,
                                                  this_chunk_bytes,
                                                  sdio->next_block_for_io,
                                                  map_bh);
@@ -1042,7 +1031,8 @@ do_holes:
                        sdio->next_block_for_io += this_chunk_blocks;
 
                        sdio->block_in_file += this_chunk_blocks;
-                       block_in_page += this_chunk_blocks;
+                       from += this_chunk_bytes;
+                       dio->result += this_chunk_bytes;
                        sdio->blocks_available -= this_chunk_blocks;
 next_block:
                        BUG_ON(sdio->block_in_file > sdio->final_block_in_request);
@@ -1052,7 +1042,6 @@ next_block:
 
                /* Drop the ref which was taken in get_user_pages() */
                page_cache_release(page);
-               block_in_page = 0;
        }
 out:
        return ret;
@@ -1107,24 +1096,20 @@ static inline int drop_refcount(struct dio *dio)
  */
 static inline ssize_t
 do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
-       struct block_device *bdev, const struct iovec *iov, loff_t offset, 
-       unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io,
+       struct block_device *bdev, struct iov_iter *iter, loff_t offset, 
+       get_block_t get_block, dio_iodone_t end_io,
        dio_submit_t submit_io, int flags)
 {
-       int seg;
-       size_t size;
-       unsigned long addr;
        unsigned i_blkbits = ACCESS_ONCE(inode->i_blkbits);
        unsigned blkbits = i_blkbits;
        unsigned blocksize_mask = (1 << blkbits) - 1;
        ssize_t retval = -EINVAL;
-       loff_t end = offset;
+       loff_t end = offset + iov_iter_count(iter);
        struct dio *dio;
        struct dio_submit sdio = { 0, };
-       unsigned long user_addr;
-       size_t bytes;
        struct buffer_head map_bh = { 0, };
        struct blk_plug plug;
+       unsigned long align = offset | iov_iter_alignment(iter);
 
        if (rw & WRITE)
                rw = WRITE_ODIRECT;
@@ -1134,32 +1119,16 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
         * the early prefetch in the caller enough time.
         */
 
-       if (offset & blocksize_mask) {
+       if (align & blocksize_mask) {
                if (bdev)
                        blkbits = blksize_bits(bdev_logical_block_size(bdev));
                blocksize_mask = (1 << blkbits) - 1;
-               if (offset & blocksize_mask)
+               if (align & blocksize_mask)
                        goto out;
        }
 
-       /* Check the memory alignment.  Blocks cannot straddle pages */
-       for (seg = 0; seg < nr_segs; seg++) {
-               addr = (unsigned long)iov[seg].iov_base;
-               size = iov[seg].iov_len;
-               end += size;
-               if (unlikely((addr & blocksize_mask) ||
-                            (size & blocksize_mask))) {
-                       if (bdev)
-                               blkbits = blksize_bits(
-                                        bdev_logical_block_size(bdev));
-                       blocksize_mask = (1 << blkbits) - 1;
-                       if ((addr & blocksize_mask) || (size & blocksize_mask))
-                               goto out;
-               }
-       }
-
        /* watch out for a 0 len io from a tricksy fs */
-       if (rw == READ && end == offset)
+       if (rw == READ && !iov_iter_count(iter))
                return 0;
 
        dio = kmem_cache_alloc(dio_cache, GFP_KERNEL);
@@ -1249,6 +1218,10 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
        spin_lock_init(&dio->bio_lock);
        dio->refcount = 1;
 
+       sdio.iter = iter;
+       sdio.final_block_in_request =
+               (offset + iov_iter_count(iter)) >> blkbits;
+
        /*
         * In case of non-aligned buffers, we may need 2 more
         * pages since we need to zero out first and last block.
@@ -1256,47 +1229,13 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
        if (unlikely(sdio.blkfactor))
                sdio.pages_in_io = 2;
 
-       for (seg = 0; seg < nr_segs; seg++) {
-               user_addr = (unsigned long)iov[seg].iov_base;
-               sdio.pages_in_io +=
-                       ((user_addr + iov[seg].iov_len + PAGE_SIZE-1) /
-                               PAGE_SIZE - user_addr / PAGE_SIZE);
-       }
+       sdio.pages_in_io += iov_iter_npages(iter, INT_MAX);
 
        blk_start_plug(&plug);
 
-       for (seg = 0; seg < nr_segs; seg++) {
-               user_addr = (unsigned long)iov[seg].iov_base;
-               sdio.size += bytes = iov[seg].iov_len;
-
-               /* Index into the first page of the first block */
-               sdio.first_block_in_page = (user_addr & ~PAGE_MASK) >> blkbits;
-               sdio.final_block_in_request = sdio.block_in_file +
-                                               (bytes >> blkbits);
-               /* Page fetching state */
-               sdio.head = 0;
-               sdio.tail = 0;
-               sdio.curr_page = 0;
-
-               sdio.total_pages = 0;
-               if (user_addr & (PAGE_SIZE-1)) {
-                       sdio.total_pages++;
-                       bytes -= PAGE_SIZE - (user_addr & (PAGE_SIZE - 1));
-               }
-               sdio.total_pages += (bytes + PAGE_SIZE - 1) / PAGE_SIZE;
-               sdio.curr_user_address = user_addr;
-
-               retval = do_direct_IO(dio, &sdio, &map_bh);
-
-               dio->result += iov[seg].iov_len -
-                       ((sdio.final_block_in_request - sdio.block_in_file) <<
-                                       blkbits);
-
-               if (retval) {
-                       dio_cleanup(dio, &sdio);
-                       break;
-               }
-       } /* end iovec loop */
+       retval = do_direct_IO(dio, &sdio, &map_bh);
+       if (retval)
+               dio_cleanup(dio, &sdio);
 
        if (retval == -ENOTBLK) {
                /*
@@ -1365,8 +1304,8 @@ out:
 
 ssize_t
 __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
-       struct block_device *bdev, const struct iovec *iov, loff_t offset,
-       unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io,
+       struct block_device *bdev, struct iov_iter *iter, loff_t offset,
+       get_block_t get_block, dio_iodone_t end_io,
        dio_submit_t submit_io, int flags)
 {
        /*
@@ -1381,9 +1320,8 @@ __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
        prefetch(bdev->bd_queue);
        prefetch((char *)bdev->bd_queue + SMP_CACHE_BYTES);
 
-       return do_blockdev_direct_IO(rw, iocb, inode, bdev, iov, offset,
-                                    nr_segs, get_block, end_io,
-                                    submit_io, flags);
+       return do_blockdev_direct_IO(rw, iocb, inode, bdev, iter, offset,
+                                    get_block, end_io, submit_io, flags);
 }
 
 EXPORT_SYMBOL(__blockdev_direct_IO);
index 1e5b45359509590f90910a90797e901da7629745..d08e079ea5d3aa37cf685cce89eb00122fe7ba02 100644 (file)
@@ -617,6 +617,11 @@ static void retry_failed_sctp_send(struct connection *recv_con,
        int nodeid = sn_send_failed->ssf_info.sinfo_ppid;
 
        log_print("Retry sending %d bytes to node id %d", len, nodeid);
+       
+       if (!nodeid) {
+               log_print("Shouldn't resend data via listening connection.");
+               return;
+       }
 
        con = nodeid2con(nodeid, 0);
        if (!con) {
index b1eaa7a1f82cd0ce03ebb0bad15c2d8f5b6f6e9c..db0fad3269c0395f230c39cc0fcd6c9975f6d633 100644 (file)
  * The function to be used for directory reads is ecryptfs_read.
  */
 static ssize_t ecryptfs_read_update_atime(struct kiocb *iocb,
-                               const struct iovec *iov,
-                               unsigned long nr_segs, loff_t pos)
+                               struct iov_iter *to)
 {
        ssize_t rc;
        struct path *path;
        struct file *file = iocb->ki_filp;
 
-       rc = generic_file_aio_read(iocb, iov, nr_segs, pos);
+       rc = generic_file_read_iter(iocb, to);
        /*
         * Even though this is a async interface, we need to wait
         * for IO to finish to update atime
@@ -352,10 +351,10 @@ const struct file_operations ecryptfs_dir_fops = {
 
 const struct file_operations ecryptfs_main_fops = {
        .llseek = generic_file_llseek,
-       .read = do_sync_read,
-       .aio_read = ecryptfs_read_update_atime,
-       .write = do_sync_write,
-       .aio_write = generic_file_aio_write,
+       .read = new_sync_read,
+       .read_iter = ecryptfs_read_update_atime,
+       .write = new_sync_write,
+       .write_iter = generic_file_write_iter,
        .iterate = ecryptfs_readdir,
        .unlocked_ioctl = ecryptfs_unlocked_ioctl,
 #ifdef CONFIG_COMPAT
index 238b7aa26f68ab538df0cc219073a3d26541cc11..a3d33fe592d6d95619506b6f4aac33621284d3f8 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1046,13 +1046,13 @@ EXPORT_SYMBOL_GPL(get_task_comm);
  * so that a new one can be started
  */
 
-void set_task_comm(struct task_struct *tsk, const char *buf)
+void __set_task_comm(struct task_struct *tsk, const char *buf, bool exec)
 {
        task_lock(tsk);
        trace_task_rename(tsk, buf);
        strlcpy(tsk->comm, buf, sizeof(tsk->comm));
        task_unlock(tsk);
-       perf_event_comm(tsk);
+       perf_event_comm(tsk, exec);
 }
 
 int flush_old_exec(struct linux_binprm * bprm)
@@ -1110,7 +1110,8 @@ void setup_new_exec(struct linux_binprm * bprm)
        else
                set_dumpable(current->mm, suid_dumpable);
 
-       set_task_comm(current, kbasename(bprm->filename));
+       perf_event_exec();
+       __set_task_comm(current, kbasename(bprm->filename), true);
 
        /* Set the new mm task size. We have to do that late because it may
         * depend on TIF_32BIT which is only updated in flush_thread() on
index 491c6c078e7f5e0ac420646288452d93cca86ce2..71bf8e4fb5d427c660e9913a57054dec59f99dfc 100644 (file)
@@ -67,17 +67,17 @@ static int exofs_flush(struct file *file, fl_owner_t id)
 
 const struct file_operations exofs_file_operations = {
        .llseek         = generic_file_llseek,
-       .read           = do_sync_read,
-       .write          = do_sync_write,
-       .aio_read       = generic_file_aio_read,
-       .aio_write      = generic_file_aio_write,
+       .read           = new_sync_read,
+       .write          = new_sync_write,
+       .read_iter      = generic_file_read_iter,
+       .write_iter     = generic_file_write_iter,
        .mmap           = generic_file_mmap,
        .open           = generic_file_open,
        .release        = exofs_release_file,
        .fsync          = exofs_file_fsync,
        .flush          = exofs_flush,
        .splice_read    = generic_file_splice_read,
-       .splice_write   = generic_file_splice_write,
+       .splice_write   = iter_file_splice_write,
 };
 
 const struct inode_operations exofs_file_inode_operations = {
index d1c244d676679c8d087bafeada190ce268a02c6e..3f9cafd739312bd1392b21bc6ed3dc6bb9b666a1 100644 (file)
@@ -964,7 +964,7 @@ static void exofs_invalidatepage(struct page *page, unsigned int offset,
 
  /* TODO: Should be easy enough to do proprly */
 static ssize_t exofs_direct_IO(int rw, struct kiocb *iocb,
-               const struct iovec *iov, loff_t offset, unsigned long nr_segs)
+               struct iov_iter *iter, loff_t offset)
 {
        return 0;
 }
index 44c36e5907655982cc10e26b822cc1061312fbd1..7c87b22a7228c4ce9ed3c915c64283e2aa72a328 100644 (file)
@@ -62,10 +62,10 @@ int ext2_fsync(struct file *file, loff_t start, loff_t end, int datasync)
  */
 const struct file_operations ext2_file_operations = {
        .llseek         = generic_file_llseek,
-       .read           = do_sync_read,
-       .write          = do_sync_write,
-       .aio_read       = generic_file_aio_read,
-       .aio_write      = generic_file_aio_write,
+       .read           = new_sync_read,
+       .write          = new_sync_write,
+       .read_iter      = generic_file_read_iter,
+       .write_iter     = generic_file_write_iter,
        .unlocked_ioctl = ext2_ioctl,
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = ext2_compat_ioctl,
@@ -75,7 +75,7 @@ const struct file_operations ext2_file_operations = {
        .release        = ext2_release_file,
        .fsync          = ext2_fsync,
        .splice_read    = generic_file_splice_read,
-       .splice_write   = generic_file_splice_write,
+       .splice_write   = iter_file_splice_write,
 };
 
 #ifdef CONFIG_EXT2_FS_XIP
index b1d2a4675d4280e10ff10f9f89ffaa25d109d920..36d35c36311d69a025c5b804e8d8597cbd9cb2b2 100644 (file)
@@ -850,18 +850,18 @@ static sector_t ext2_bmap(struct address_space *mapping, sector_t block)
 }
 
 static ssize_t
-ext2_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
-                       loff_t offset, unsigned long nr_segs)
+ext2_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter,
+                       loff_t offset)
 {
        struct file *file = iocb->ki_filp;
        struct address_space *mapping = file->f_mapping;
        struct inode *inode = mapping->host;
+       size_t count = iov_iter_count(iter);
        ssize_t ret;
 
-       ret = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs,
-                                ext2_get_block);
+       ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, ext2_get_block);
        if (ret < 0 && (rw & WRITE))
-               ext2_write_failed(mapping, offset + iov_length(iov, nr_segs));
+               ext2_write_failed(mapping, offset + count);
        return ret;
 }
 
index aad05311392a046f3df724102580c511185c0fa2..a062fa1e1b113e56e7545bb4c6bac7ac1bf7d741 100644 (file)
@@ -50,10 +50,10 @@ static int ext3_release_file (struct inode * inode, struct file * filp)
 
 const struct file_operations ext3_file_operations = {
        .llseek         = generic_file_llseek,
-       .read           = do_sync_read,
-       .write          = do_sync_write,
-       .aio_read       = generic_file_aio_read,
-       .aio_write      = generic_file_aio_write,
+       .read           = new_sync_read,
+       .write          = new_sync_write,
+       .read_iter      = generic_file_read_iter,
+       .write_iter     = generic_file_write_iter,
        .unlocked_ioctl = ext3_ioctl,
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = ext3_compat_ioctl,
@@ -63,7 +63,7 @@ const struct file_operations ext3_file_operations = {
        .release        = ext3_release_file,
        .fsync          = ext3_sync_file,
        .splice_read    = generic_file_splice_read,
-       .splice_write   = generic_file_splice_write,
+       .splice_write   = iter_file_splice_write,
 };
 
 const struct inode_operations ext3_file_inode_operations = {
index 695abe738a2409f4c32f4ef7d5749757d98b6f15..2c6ccc49ba279cacf77fe6609fe44a50b970898c 100644 (file)
@@ -1821,8 +1821,7 @@ static int ext3_releasepage(struct page *page, gfp_t wait)
  * VFS code falls back into buffered path in that case so we are safe.
  */
 static ssize_t ext3_direct_IO(int rw, struct kiocb *iocb,
-                       const struct iovec *iov, loff_t offset,
-                       unsigned long nr_segs)
+                       struct iov_iter *iter, loff_t offset)
 {
        struct file *file = iocb->ki_filp;
        struct inode *inode = file->f_mapping->host;
@@ -1830,10 +1829,10 @@ static ssize_t ext3_direct_IO(int rw, struct kiocb *iocb,
        handle_t *handle;
        ssize_t ret;
        int orphan = 0;
-       size_t count = iov_length(iov, nr_segs);
+       size_t count = iov_iter_count(iter);
        int retries = 0;
 
-       trace_ext3_direct_IO_enter(inode, offset, iov_length(iov, nr_segs), rw);
+       trace_ext3_direct_IO_enter(inode, offset, count, rw);
 
        if (rw == WRITE) {
                loff_t final_size = offset + count;
@@ -1857,15 +1856,14 @@ static ssize_t ext3_direct_IO(int rw, struct kiocb *iocb,
        }
 
 retry:
-       ret = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs,
-                                ext3_get_block);
+       ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, ext3_get_block);
        /*
         * In case of error extending write may have instantiated a few
         * blocks outside i_size. Trim these off again.
         */
        if (unlikely((rw & WRITE) && ret < 0)) {
                loff_t isize = i_size_read(inode);
-               loff_t end = offset + iov_length(iov, nr_segs);
+               loff_t end = offset + count;
 
                if (end > isize)
                        ext3_truncate_failed_direct_write(inode);
@@ -1910,8 +1908,7 @@ retry:
                        ret = err;
        }
 out:
-       trace_ext3_direct_IO_exit(inode, offset,
-                               iov_length(iov, nr_segs), rw, ret);
+       trace_ext3_direct_IO_exit(inode, offset, count, rw, ret);
        return ret;
 }
 
index 1479e2ae00d28e83e8d1c175752a61b59828cd55..7cc5a0e23688e1a2ce071dcb646b725a266ff890 100644 (file)
@@ -2140,8 +2140,7 @@ extern void ext4_da_update_reserve_space(struct inode *inode,
 extern int ext4_ind_map_blocks(handle_t *handle, struct inode *inode,
                                struct ext4_map_blocks *map, int flags);
 extern ssize_t ext4_ind_direct_IO(int rw, struct kiocb *iocb,
-                               const struct iovec *iov, loff_t offset,
-                               unsigned long nr_segs);
+                               struct iov_iter *iter, loff_t offset);
 extern int ext4_ind_calc_metadata_amount(struct inode *inode, sector_t lblock);
 extern int ext4_ind_trans_blocks(struct inode *inode, int nrblocks);
 extern void ext4_ind_truncate(handle_t *, struct inode *inode);
index 4e8bc284ec0e96296e8bbcf68423b9ea9ee8c921..8695f70af1ef2046c2f68a24a5ed4e195cd6dc88 100644 (file)
@@ -74,26 +74,22 @@ static void ext4_unwritten_wait(struct inode *inode)
  * or one thread will zero the other's data, causing corruption.
  */
 static int
-ext4_unaligned_aio(struct inode *inode, const struct iovec *iov,
-                  unsigned long nr_segs, loff_t pos)
+ext4_unaligned_aio(struct inode *inode, struct iov_iter *from, loff_t pos)
 {
        struct super_block *sb = inode->i_sb;
        int blockmask = sb->s_blocksize - 1;
-       size_t count = iov_length(iov, nr_segs);
-       loff_t final_size = pos + count;
 
        if (pos >= i_size_read(inode))
                return 0;
 
-       if ((pos & blockmask) || (final_size & blockmask))
+       if ((pos | iov_iter_alignment(from)) & blockmask)
                return 1;
 
        return 0;
 }
 
 static ssize_t
-ext4_file_write(struct kiocb *iocb, const struct iovec *iov,
-               unsigned long nr_segs, loff_t pos)
+ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
 {
        struct file *file = iocb->ki_filp;
        struct inode *inode = file_inode(iocb->ki_filp);
@@ -101,10 +97,9 @@ ext4_file_write(struct kiocb *iocb, const struct iovec *iov,
        struct blk_plug plug;
        int o_direct = file->f_flags & O_DIRECT;
        int overwrite = 0;
-       size_t length = iov_length(iov, nr_segs);
+       size_t length = iov_iter_count(from);
        ssize_t ret;
-
-       BUG_ON(iocb->ki_pos != pos);
+       loff_t pos = iocb->ki_pos;
 
        /*
         * Unaligned direct AIO must be serialized; see comment above
@@ -114,7 +109,7 @@ ext4_file_write(struct kiocb *iocb, const struct iovec *iov,
            ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS) &&
            !is_sync_kiocb(iocb) &&
            (file->f_flags & O_APPEND ||
-            ext4_unaligned_aio(inode, iov, nr_segs, pos))) {
+            ext4_unaligned_aio(inode, from, pos))) {
                aio_mutex = ext4_aio_mutex(inode);
                mutex_lock(aio_mutex);
                ext4_unwritten_wait(inode);
@@ -138,10 +133,8 @@ ext4_file_write(struct kiocb *iocb, const struct iovec *iov,
                        goto errout;
                }
 
-               if (pos + length > sbi->s_bitmap_maxbytes) {
-                       nr_segs = iov_shorten((struct iovec *)iov, nr_segs,
-                                             sbi->s_bitmap_maxbytes - pos);
-               }
+               if (pos + length > sbi->s_bitmap_maxbytes)
+                       iov_iter_truncate(from, sbi->s_bitmap_maxbytes - pos);
        }
 
        if (o_direct) {
@@ -179,7 +172,7 @@ ext4_file_write(struct kiocb *iocb, const struct iovec *iov,
                }
        }
 
-       ret = __generic_file_aio_write(iocb, iov, nr_segs);
+       ret = __generic_file_write_iter(iocb, from);
        mutex_unlock(&inode->i_mutex);
 
        if (ret > 0) {
@@ -594,10 +587,10 @@ loff_t ext4_llseek(struct file *file, loff_t offset, int whence)
 
 const struct file_operations ext4_file_operations = {
        .llseek         = ext4_llseek,
-       .read           = do_sync_read,
-       .write          = do_sync_write,
-       .aio_read       = generic_file_aio_read,
-       .aio_write      = ext4_file_write,
+       .read           = new_sync_read,
+       .write          = new_sync_write,
+       .read_iter      = generic_file_read_iter,
+       .write_iter     = ext4_file_write_iter,
        .unlocked_ioctl = ext4_ioctl,
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = ext4_compat_ioctl,
@@ -607,7 +600,7 @@ const struct file_operations ext4_file_operations = {
        .release        = ext4_release_file,
        .fsync          = ext4_sync_file,
        .splice_read    = generic_file_splice_read,
-       .splice_write   = generic_file_splice_write,
+       .splice_write   = iter_file_splice_write,
        .fallocate      = ext4_fallocate,
 };
 
index 594009f5f523f0fd2228a72f1aa593a6f3ebf66f..8a57e9fcd1b987bdab029e7658ae100d10949d5a 100644 (file)
@@ -639,8 +639,7 @@ out:
  * VFS code falls back into buffered path in that case so we are safe.
  */
 ssize_t ext4_ind_direct_IO(int rw, struct kiocb *iocb,
-                          const struct iovec *iov, loff_t offset,
-                          unsigned long nr_segs)
+                          struct iov_iter *iter, loff_t offset)
 {
        struct file *file = iocb->ki_filp;
        struct inode *inode = file->f_mapping->host;
@@ -648,7 +647,7 @@ ssize_t ext4_ind_direct_IO(int rw, struct kiocb *iocb,
        handle_t *handle;
        ssize_t ret;
        int orphan = 0;
-       size_t count = iov_length(iov, nr_segs);
+       size_t count = iov_iter_count(iter);
        int retries = 0;
 
        if (rw == WRITE) {
@@ -687,18 +686,17 @@ retry:
                        goto locked;
                }
                ret = __blockdev_direct_IO(rw, iocb, inode,
-                                inode->i_sb->s_bdev, iov,
-                                offset, nr_segs,
+                                inode->i_sb->s_bdev, iter, offset,
                                 ext4_get_block, NULL, NULL, 0);
                inode_dio_done(inode);
        } else {
 locked:
-               ret = blockdev_direct_IO(rw, iocb, inode, iov,
-                                offset, nr_segs, ext4_get_block);
+               ret = blockdev_direct_IO(rw, iocb, inode, iter,
+                                offset, ext4_get_block);
 
                if (unlikely((rw & WRITE) && ret < 0)) {
                        loff_t isize = i_size_read(inode);
-                       loff_t end = offset + iov_length(iov, nr_segs);
+                       loff_t end = offset + count;
 
                        if (end > isize)
                                ext4_truncate_failed_write(inode);
index 7fcd68ee915500cd53ef79cbea1c187e71024004..8a064734e6eb3ed06461e9954d036da6ff1e8147 100644 (file)
@@ -3093,13 +3093,12 @@ static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset,
  *
  */
 static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb,
-                             const struct iovec *iov, loff_t offset,
-                             unsigned long nr_segs)
+                             struct iov_iter *iter, loff_t offset)
 {
        struct file *file = iocb->ki_filp;
        struct inode *inode = file->f_mapping->host;
        ssize_t ret;
-       size_t count = iov_length(iov, nr_segs);
+       size_t count = iov_iter_count(iter);
        int overwrite = 0;
        get_block_t *get_block_func = NULL;
        int dio_flags = 0;
@@ -3108,7 +3107,7 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb,
 
        /* Use the old path for reads and writes beyond i_size. */
        if (rw != WRITE || final_size > inode->i_size)
-               return ext4_ind_direct_IO(rw, iocb, iov, offset, nr_segs);
+               return ext4_ind_direct_IO(rw, iocb, iter, offset);
 
        BUG_ON(iocb->private == NULL);
 
@@ -3175,8 +3174,8 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb,
                dio_flags = DIO_LOCKING;
        }
        ret = __blockdev_direct_IO(rw, iocb, inode,
-                                  inode->i_sb->s_bdev, iov,
-                                  offset, nr_segs,
+                                  inode->i_sb->s_bdev, iter,
+                                  offset,
                                   get_block_func,
                                   ext4_end_io_dio,
                                   NULL,
@@ -3230,11 +3229,11 @@ retake_lock:
 }
 
 static ssize_t ext4_direct_IO(int rw, struct kiocb *iocb,
-                             const struct iovec *iov, loff_t offset,
-                             unsigned long nr_segs)
+                             struct iov_iter *iter, loff_t offset)
 {
        struct file *file = iocb->ki_filp;
        struct inode *inode = file->f_mapping->host;
+       size_t count = iov_iter_count(iter);
        ssize_t ret;
 
        /*
@@ -3247,13 +3246,12 @@ static ssize_t ext4_direct_IO(int rw, struct kiocb *iocb,
        if (ext4_has_inline_data(inode))
                return 0;
 
-       trace_ext4_direct_IO_enter(inode, offset, iov_length(iov, nr_segs), rw);
+       trace_ext4_direct_IO_enter(inode, offset, count, rw);
        if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
-               ret = ext4_ext_direct_IO(rw, iocb, iov, offset, nr_segs);
+               ret = ext4_ext_direct_IO(rw, iocb, iter, offset);
        else
-               ret = ext4_ind_direct_IO(rw, iocb, iov, offset, nr_segs);
-       trace_ext4_direct_IO_exit(inode, offset,
-                               iov_length(iov, nr_segs), rw, ret);
+               ret = ext4_ind_direct_IO(rw, iocb, iter, offset);
+       trace_ext4_direct_IO_exit(inode, offset, count, rw, ret);
        return ret;
 }
 
index c1fb6dd10911c01e9b37d533a7588ee6bf934ecb..0924521306b40c5087f2c2170c92fe7b03452862 100644 (file)
@@ -1017,10 +1017,9 @@ static int f2fs_write_end(struct file *file,
 }
 
 static int check_direct_IO(struct inode *inode, int rw,
-               const struct iovec *iov, loff_t offset, unsigned long nr_segs)
+               struct iov_iter *iter, loff_t offset)
 {
        unsigned blocksize_mask = inode->i_sb->s_blocksize - 1;
-       int i;
 
        if (rw == READ)
                return 0;
@@ -1028,14 +1027,14 @@ static int check_direct_IO(struct inode *inode, int rw,
        if (offset & blocksize_mask)
                return -EINVAL;
 
-       for (i = 0; i < nr_segs; i++)
-               if (iov[i].iov_len & blocksize_mask)
-                       return -EINVAL;
+       if (iov_iter_alignment(iter) & blocksize_mask)
+               return -EINVAL;
+
        return 0;
 }
 
 static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb,
-               const struct iovec *iov, loff_t offset, unsigned long nr_segs)
+               struct iov_iter *iter, loff_t offset)
 {
        struct file *file = iocb->ki_filp;
        struct inode *inode = file->f_mapping->host;
@@ -1044,14 +1043,14 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb,
        if (f2fs_has_inline_data(inode))
                return 0;
 
-       if (check_direct_IO(inode, rw, iov, offset, nr_segs))
+       if (check_direct_IO(inode, rw, iter, offset))
                return 0;
 
        /* clear fsync mark to recover these blocks */
        fsync_mark_clear(F2FS_SB(inode->i_sb), inode->i_ino);
 
-       return blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs,
-                                                       get_data_block);
+       return blockdev_direct_IO(rw, iocb, inode, iter, offset,
+                                 get_data_block);
 }
 
 static void f2fs_invalidate_data_page(struct page *page, unsigned int offset,
index 9c49c593d8eb4ab39a1aa28c1b841f949d02c050..c58e330757191392656d2819fd937a1cc564cb37 100644 (file)
@@ -808,10 +808,10 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
 const struct file_operations f2fs_file_operations = {
        .llseek         = f2fs_llseek,
-       .read           = do_sync_read,
-       .write          = do_sync_write,
-       .aio_read       = generic_file_aio_read,
-       .aio_write      = generic_file_aio_write,
+       .read           = new_sync_read,
+       .write          = new_sync_write,
+       .read_iter      = generic_file_read_iter,
+       .write_iter     = generic_file_write_iter,
        .open           = generic_file_open,
        .mmap           = f2fs_file_mmap,
        .fsync          = f2fs_sync_file,
@@ -821,5 +821,5 @@ const struct file_operations f2fs_file_operations = {
        .compat_ioctl   = f2fs_compat_ioctl,
 #endif
        .splice_read    = generic_file_splice_read,
-       .splice_write   = generic_file_splice_write,
+       .splice_write   = iter_file_splice_write,
 };
index 9b104f543056238016c683ef822046a784169f50..85f79a89e7474658c8c552cfe027e72ff048d542 100644 (file)
@@ -170,10 +170,10 @@ int fat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
 
 const struct file_operations fat_file_operations = {
        .llseek         = generic_file_llseek,
-       .read           = do_sync_read,
-       .write          = do_sync_write,
-       .aio_read       = generic_file_aio_read,
-       .aio_write      = generic_file_aio_write,
+       .read           = new_sync_read,
+       .write          = new_sync_write,
+       .read_iter      = generic_file_read_iter,
+       .write_iter     = generic_file_write_iter,
        .mmap           = generic_file_mmap,
        .release        = fat_file_release,
        .unlocked_ioctl = fat_generic_ioctl,
index 9c83594d7fb5dbb03e5eb7ca378c4b35ffd3d85f..756aead10d9618593e3267e697df4915d528fbc9 100644 (file)
@@ -247,12 +247,13 @@ static int fat_write_end(struct file *file, struct address_space *mapping,
 }
 
 static ssize_t fat_direct_IO(int rw, struct kiocb *iocb,
-                            const struct iovec *iov,
-                            loff_t offset, unsigned long nr_segs)
+                            struct iov_iter *iter,
+                            loff_t offset)
 {
        struct file *file = iocb->ki_filp;
        struct address_space *mapping = file->f_mapping;
        struct inode *inode = mapping->host;
+       size_t count = iov_iter_count(iter);
        ssize_t ret;
 
        if (rw == WRITE) {
@@ -265,7 +266,7 @@ static ssize_t fat_direct_IO(int rw, struct kiocb *iocb,
                 *
                 * Return 0, and fallback to normal buffered write.
                 */
-               loff_t size = offset + iov_length(iov, nr_segs);
+               loff_t size = offset + count;
                if (MSDOS_I(inode)->mmu_private < size)
                        return 0;
        }
@@ -274,10 +275,9 @@ static ssize_t fat_direct_IO(int rw, struct kiocb *iocb,
         * FAT need to use the DIO_LOCKING for avoiding the race
         * condition of fat_get_block() and ->truncate().
         */
-       ret = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs,
-                                fat_get_block);
+       ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, fat_get_block);
        if (ret < 0 && (rw & WRITE))
-               fat_write_failed(mapping, offset + iov_length(iov, nr_segs));
+               fat_write_failed(mapping, offset + count);
 
        return ret;
 }
index 8f294cfac69749024c2c2e19d4b156755130e9ed..66923fe3176e49b03617e2f5477bb8e05fc89dc3 100644 (file)
--- a/fs/file.c
+++ b/fs/file.c
@@ -44,15 +44,10 @@ static void *alloc_fdmem(size_t size)
        return vmalloc(size);
 }
 
-static void free_fdmem(void *ptr)
-{
-       is_vmalloc_addr(ptr) ? vfree(ptr) : kfree(ptr);
-}
-
 static void __free_fdtable(struct fdtable *fdt)
 {
-       free_fdmem(fdt->fd);
-       free_fdmem(fdt->open_fds);
+       kvfree(fdt->fd);
+       kvfree(fdt->open_fds);
        kfree(fdt);
 }
 
@@ -130,7 +125,7 @@ static struct fdtable * alloc_fdtable(unsigned int nr)
        return fdt;
 
 out_arr:
-       free_fdmem(fdt->fd);
+       kvfree(fdt->fd);
 out_fdt:
        kfree(fdt);
 out:
index 40bf4660f0a3aa18bf881c2b3c09dea95e0808ec..385bfd31512a17f4e4c6869a3ee8f32c456cd327 100644 (file)
@@ -175,6 +175,12 @@ struct file *alloc_file(struct path *path, fmode_t mode,
        file->f_path = *path;
        file->f_inode = path->dentry->d_inode;
        file->f_mapping = path->dentry->d_inode->i_mapping;
+       if ((mode & FMODE_READ) &&
+            likely(fop->read || fop->aio_read || fop->read_iter))
+               mode |= FMODE_CAN_READ;
+       if ((mode & FMODE_WRITE) &&
+            likely(fop->write || fop->aio_write || fop->write_iter))
+               mode |= FMODE_CAN_WRITE;
        file->f_mode = mode;
        file->f_op = fop;
        if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
index 13b691a8a7d2ea4403161213486538dadf75a217..966ace8b243fa39796fbdd788c4d83ebd6024217 100644 (file)
@@ -94,8 +94,10 @@ static ssize_t cuse_read(struct file *file, char __user *buf, size_t count,
        loff_t pos = 0;
        struct iovec iov = { .iov_base = buf, .iov_len = count };
        struct fuse_io_priv io = { .async = 0, .file = file };
+       struct iov_iter ii;
+       iov_iter_init(&ii, READ, &iov, 1, count);
 
-       return fuse_direct_io(&io, &iov, 1, count, &pos, FUSE_DIO_CUSE);
+       return fuse_direct_io(&io, &ii, &pos, FUSE_DIO_CUSE);
 }
 
 static ssize_t cuse_write(struct file *file, const char __user *buf,
@@ -104,12 +106,14 @@ static ssize_t cuse_write(struct file *file, const char __user *buf,
        loff_t pos = 0;
        struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = count };
        struct fuse_io_priv io = { .async = 0, .file = file };
+       struct iov_iter ii;
+       iov_iter_init(&ii, WRITE, &iov, 1, count);
 
        /*
         * No locking or generic_write_checks(), the server is
         * responsible for locking and sanity checks.
         */
-       return fuse_direct_io(&io, &iov, 1, count, &pos,
+       return fuse_direct_io(&io, &ii, &pos,
                              FUSE_DIO_WRITE | FUSE_DIO_CUSE);
 }
 
index 903cbc9cd6bd3a471f565e9fd3e2115539b58aca..6e16dad13e9b16de0358f8caaec9833d9f00a84b 100644 (file)
@@ -933,8 +933,7 @@ out:
        return err;
 }
 
-static ssize_t fuse_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
-                                 unsigned long nr_segs, loff_t pos)
+static ssize_t fuse_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
 {
        struct inode *inode = iocb->ki_filp->f_mapping->host;
        struct fuse_conn *fc = get_fuse_conn(inode);
@@ -945,14 +944,14 @@ static ssize_t fuse_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
         * i_size is up to date).
         */
        if (fc->auto_inval_data ||
-           (pos + iov_length(iov, nr_segs) > i_size_read(inode))) {
+           (iocb->ki_pos + iov_iter_count(to) > i_size_read(inode))) {
                int err;
                err = fuse_update_attributes(inode, NULL, iocb->ki_filp, NULL);
                if (err)
                        return err;
        }
 
-       return generic_file_aio_read(iocb, iov, nr_segs, pos);
+       return generic_file_read_iter(iocb, to);
 }
 
 static void fuse_write_fill(struct fuse_req *req, struct fuse_file *ff,
@@ -1181,19 +1180,17 @@ static ssize_t fuse_perform_write(struct file *file,
        return res > 0 ? res : err;
 }
 
-static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
-                                  unsigned long nr_segs, loff_t pos)
+static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
 {
        struct file *file = iocb->ki_filp;
        struct address_space *mapping = file->f_mapping;
-       size_t count = 0;
-       size_t ocount = 0;
+       size_t count = iov_iter_count(from);
        ssize_t written = 0;
        ssize_t written_buffered = 0;
        struct inode *inode = mapping->host;
        ssize_t err;
-       struct iov_iter i;
        loff_t endbyte = 0;
+       loff_t pos = iocb->ki_pos;
 
        if (get_fuse_conn(inode)->writeback_cache) {
                /* Update size (EOF optimization) and mode (SUID clearing) */
@@ -1201,17 +1198,9 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
                if (err)
                        return err;
 
-               return generic_file_aio_write(iocb, iov, nr_segs, pos);
+               return generic_file_write_iter(iocb, from);
        }
 
-       WARN_ON(iocb->ki_pos != pos);
-
-       ocount = 0;
-       err = generic_segment_checks(iov, &nr_segs, &ocount, VERIFY_READ);
-       if (err)
-               return err;
-
-       count = ocount;
        mutex_lock(&inode->i_mutex);
 
        /* We can write back this queue in page reclaim */
@@ -1224,6 +1213,7 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
        if (count == 0)
                goto out;
 
+       iov_iter_truncate(from, count);
        err = file_remove_suid(file);
        if (err)
                goto out;
@@ -1233,16 +1223,13 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
                goto out;
 
        if (file->f_flags & O_DIRECT) {
-               written = generic_file_direct_write(iocb, iov, &nr_segs, pos, 
-                                                   count, ocount);
-               if (written < 0 || written == count)
+               written = generic_file_direct_write(iocb, from, pos);
+               if (written < 0 || !iov_iter_count(from))
                        goto out;
 
                pos += written;
-               count -= written;
 
-               iov_iter_init(&i, iov, nr_segs, count, written);
-               written_buffered = fuse_perform_write(file, mapping, &i, pos);
+               written_buffered = fuse_perform_write(file, mapping, from, pos);
                if (written_buffered < 0) {
                        err = written_buffered;
                        goto out;
@@ -1261,8 +1248,7 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
                written += written_buffered;
                iocb->ki_pos = pos + written_buffered;
        } else {
-               iov_iter_init(&i, iov, nr_segs, count, 0);
-               written = fuse_perform_write(file, mapping, &i, pos);
+               written = fuse_perform_write(file, mapping, from, pos);
                if (written >= 0)
                        iocb->ki_pos = pos + written;
        }
@@ -1300,7 +1286,7 @@ static int fuse_get_user_pages(struct fuse_req *req, struct iov_iter *ii,
        size_t nbytes = 0;  /* # bytes already packed in req */
 
        /* Special case for kernel I/O: can copy directly into the buffer */
-       if (segment_eq(get_fs(), KERNEL_DS)) {
+       if (ii->type & ITER_KVEC) {
                unsigned long user_addr = fuse_get_user_addr(ii);
                size_t frag_size = fuse_get_frag_size(ii, *nbytesp);
 
@@ -1316,35 +1302,26 @@ static int fuse_get_user_pages(struct fuse_req *req, struct iov_iter *ii,
 
        while (nbytes < *nbytesp && req->num_pages < req->max_pages) {
                unsigned npages;
-               unsigned long user_addr = fuse_get_user_addr(ii);
-               unsigned offset = user_addr & ~PAGE_MASK;
-               size_t frag_size = fuse_get_frag_size(ii, *nbytesp - nbytes);
-               int ret;
-
+               size_t start;
                unsigned n = req->max_pages - req->num_pages;
-               frag_size = min_t(size_t, frag_size, n << PAGE_SHIFT);
-
-               npages = (frag_size + offset + PAGE_SIZE - 1) >> PAGE_SHIFT;
-               npages = clamp(npages, 1U, n);
-
-               ret = get_user_pages_fast(user_addr, npages, !write,
-                                         &req->pages[req->num_pages]);
+               ssize_t ret = iov_iter_get_pages(ii,
+                                       &req->pages[req->num_pages],
+                                       n * PAGE_SIZE, &start);
                if (ret < 0)
                        return ret;
 
-               npages = ret;
-               frag_size = min_t(size_t, frag_size,
-                                 (npages << PAGE_SHIFT) - offset);
-               iov_iter_advance(ii, frag_size);
+               iov_iter_advance(ii, ret);
+               nbytes += ret;
+
+               ret += start;
+               npages = (ret + PAGE_SIZE - 1) / PAGE_SIZE;
 
-               req->page_descs[req->num_pages].offset = offset;
+               req->page_descs[req->num_pages].offset = start;
                fuse_page_descs_length_init(req, req->num_pages, npages);
 
                req->num_pages += npages;
                req->page_descs[req->num_pages - 1].length -=
-                       (npages << PAGE_SHIFT) - offset - frag_size;
-
-               nbytes += frag_size;
+                       (PAGE_SIZE - ret) & (PAGE_SIZE - 1);
        }
 
        if (write)
@@ -1359,24 +1336,11 @@ static int fuse_get_user_pages(struct fuse_req *req, struct iov_iter *ii,
 
 static inline int fuse_iter_npages(const struct iov_iter *ii_p)
 {
-       struct iov_iter ii = *ii_p;
-       int npages = 0;
-
-       while (iov_iter_count(&ii) && npages < FUSE_MAX_PAGES_PER_REQ) {
-               unsigned long user_addr = fuse_get_user_addr(&ii);
-               unsigned offset = user_addr & ~PAGE_MASK;
-               size_t frag_size = iov_iter_single_seg_count(&ii);
-
-               npages += (frag_size + offset + PAGE_SIZE - 1) >> PAGE_SHIFT;
-               iov_iter_advance(&ii, frag_size);
-       }
-
-       return min(npages, FUSE_MAX_PAGES_PER_REQ);
+       return iov_iter_npages(ii_p, FUSE_MAX_PAGES_PER_REQ);
 }
 
-ssize_t fuse_direct_io(struct fuse_io_priv *io, const struct iovec *iov,
-                      unsigned long nr_segs, size_t count, loff_t *ppos,
-                      int flags)
+ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
+                      loff_t *ppos, int flags)
 {
        int write = flags & FUSE_DIO_WRITE;
        int cuse = flags & FUSE_DIO_CUSE;
@@ -1386,18 +1350,16 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, const struct iovec *iov,
        struct fuse_conn *fc = ff->fc;
        size_t nmax = write ? fc->max_write : fc->max_read;
        loff_t pos = *ppos;
+       size_t count = iov_iter_count(iter);
        pgoff_t idx_from = pos >> PAGE_CACHE_SHIFT;
        pgoff_t idx_to = (pos + count - 1) >> PAGE_CACHE_SHIFT;
        ssize_t res = 0;
        struct fuse_req *req;
-       struct iov_iter ii;
-
-       iov_iter_init(&ii, iov, nr_segs, count, 0);
 
        if (io->async)
-               req = fuse_get_req_for_background(fc, fuse_iter_npages(&ii));
+               req = fuse_get_req_for_background(fc, fuse_iter_npages(iter));
        else
-               req = fuse_get_req(fc, fuse_iter_npages(&ii));
+               req = fuse_get_req(fc, fuse_iter_npages(iter));
        if (IS_ERR(req))
                return PTR_ERR(req);
 
@@ -1413,7 +1375,7 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, const struct iovec *iov,
                size_t nres;
                fl_owner_t owner = current->files;
                size_t nbytes = min(count, nmax);
-               int err = fuse_get_user_pages(req, &ii, &nbytes, write);
+               int err = fuse_get_user_pages(req, iter, &nbytes, write);
                if (err) {
                        res = err;
                        break;
@@ -1443,9 +1405,9 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, const struct iovec *iov,
                        fuse_put_request(fc, req);
                        if (io->async)
                                req = fuse_get_req_for_background(fc,
-                                       fuse_iter_npages(&ii));
+                                       fuse_iter_npages(iter));
                        else
-                               req = fuse_get_req(fc, fuse_iter_npages(&ii));
+                               req = fuse_get_req(fc, fuse_iter_npages(iter));
                        if (IS_ERR(req))
                                break;
                }
@@ -1460,9 +1422,8 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, const struct iovec *iov,
 EXPORT_SYMBOL_GPL(fuse_direct_io);
 
 static ssize_t __fuse_direct_read(struct fuse_io_priv *io,
-                                 const struct iovec *iov,
-                                 unsigned long nr_segs, loff_t *ppos,
-                                 size_t count)
+                                 struct iov_iter *iter,
+                                 loff_t *ppos)
 {
        ssize_t res;
        struct file *file = io->file;
@@ -1471,7 +1432,7 @@ static ssize_t __fuse_direct_read(struct fuse_io_priv *io,
        if (is_bad_inode(inode))
                return -EIO;
 
-       res = fuse_direct_io(io, iov, nr_segs, count, ppos, 0);
+       res = fuse_direct_io(io, iter, ppos, 0);
 
        fuse_invalidate_attr(inode);
 
@@ -1483,22 +1444,26 @@ static ssize_t fuse_direct_read(struct file *file, char __user *buf,
 {
        struct fuse_io_priv io = { .async = 0, .file = file };
        struct iovec iov = { .iov_base = buf, .iov_len = count };
-       return __fuse_direct_read(&io, &iov, 1, ppos, count);
+       struct iov_iter ii;
+       iov_iter_init(&ii, READ, &iov, 1, count);
+       return __fuse_direct_read(&io, &ii, ppos);
 }
 
 static ssize_t __fuse_direct_write(struct fuse_io_priv *io,
-                                  const struct iovec *iov,
-                                  unsigned long nr_segs, loff_t *ppos)
+                                  struct iov_iter *iter,
+                                  loff_t *ppos)
 {
        struct file *file = io->file;
        struct inode *inode = file_inode(file);
-       size_t count = iov_length(iov, nr_segs);
+       size_t count = iov_iter_count(iter);
        ssize_t res;
 
+
        res = generic_write_checks(file, ppos, &count, 0);
-       if (!res)
-               res = fuse_direct_io(io, iov, nr_segs, count, ppos,
-                                    FUSE_DIO_WRITE);
+       if (!res) {
+               iov_iter_truncate(iter, count);
+               res = fuse_direct_io(io, iter, ppos, FUSE_DIO_WRITE);
+       }
 
        fuse_invalidate_attr(inode);
 
@@ -1512,13 +1477,15 @@ static ssize_t fuse_direct_write(struct file *file, const char __user *buf,
        struct inode *inode = file_inode(file);
        ssize_t res;
        struct fuse_io_priv io = { .async = 0, .file = file };
+       struct iov_iter ii;
+       iov_iter_init(&ii, WRITE, &iov, 1, count);
 
        if (is_bad_inode(inode))
                return -EIO;
 
        /* Don't allow parallel writes to the same file */
        mutex_lock(&inode->i_mutex);
-       res = __fuse_direct_write(&io, &iov, 1, ppos);
+       res = __fuse_direct_write(&io, &ii, ppos);
        if (res > 0)
                fuse_write_update_size(inode, *ppos);
        mutex_unlock(&inode->i_mutex);
@@ -2372,7 +2339,7 @@ static int fuse_ioctl_copy_user(struct page **pages, struct iovec *iov,
        if (!bytes)
                return 0;
 
-       iov_iter_init(&ii, iov, nr_segs, bytes, 0);
+       iov_iter_init(&ii, to_user ? READ : WRITE, iov, nr_segs, bytes);
 
        while (iov_iter_count(&ii)) {
                struct page *page = pages[page_idx++];
@@ -2894,8 +2861,8 @@ static inline loff_t fuse_round_up(loff_t off)
 }
 
 static ssize_t
-fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
-                       loff_t offset, unsigned long nr_segs)
+fuse_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter,
+                       loff_t offset)
 {
        ssize_t ret = 0;
        struct file *file = iocb->ki_filp;
@@ -2904,7 +2871,7 @@ fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
        loff_t pos = 0;
        struct inode *inode;
        loff_t i_size;
-       size_t count = iov_length(iov, nr_segs);
+       size_t count = iov_iter_count(iter);
        struct fuse_io_priv *io;
 
        pos = offset;
@@ -2919,6 +2886,7 @@ fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
                if (offset >= i_size)
                        return 0;
                count = min_t(loff_t, count, fuse_round_up(i_size - offset));
+               iov_iter_truncate(iter, count);
        }
 
        io = kmalloc(sizeof(struct fuse_io_priv), GFP_KERNEL);
@@ -2948,9 +2916,9 @@ fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
                io->async = false;
 
        if (rw == WRITE)
-               ret = __fuse_direct_write(io, iov, nr_segs, &pos);
+               ret = __fuse_direct_write(io, iter, &pos);
        else
-               ret = __fuse_direct_read(io, iov, nr_segs, &pos, count);
+               ret = __fuse_direct_read(io, iter, &pos);
 
        if (io->async) {
                fuse_aio_complete(io, ret < 0 ? ret : 0, -1);
@@ -3061,10 +3029,10 @@ out:
 
 static const struct file_operations fuse_file_operations = {
        .llseek         = fuse_file_llseek,
-       .read           = do_sync_read,
-       .aio_read       = fuse_file_aio_read,
-       .write          = do_sync_write,
-       .aio_write      = fuse_file_aio_write,
+       .read           = new_sync_read,
+       .read_iter      = fuse_file_read_iter,
+       .write          = new_sync_write,
+       .write_iter     = fuse_file_write_iter,
        .mmap           = fuse_file_mmap,
        .open           = fuse_open,
        .flush          = fuse_flush,
index 7aa5c75e0de13dcc9728ba890983e842c1a98554..e8e47a6ab5186be8df5889d65df28bc92bf8c97d 100644 (file)
@@ -880,9 +880,8 @@ int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
 /** CUSE pass fuse_direct_io() a file which f_mapping->host is not from FUSE */
 #define FUSE_DIO_CUSE  (1 << 1)
 
-ssize_t fuse_direct_io(struct fuse_io_priv *io, const struct iovec *iov,
-                      unsigned long nr_segs, size_t count, loff_t *ppos,
-                      int flags);
+ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
+                      loff_t *ppos, int flags);
 long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
                   unsigned int flags);
 long fuse_ioctl_common(struct file *file, unsigned int cmd,
index 492123cda64ab5d325db6a640d29d7640eeb6f10..805b37fed6383fc71abcb573de809ee8f3e41c53 100644 (file)
@@ -1040,8 +1040,7 @@ static int gfs2_ok_for_dio(struct gfs2_inode *ip, int rw, loff_t offset)
 
 
 static ssize_t gfs2_direct_IO(int rw, struct kiocb *iocb,
-                             const struct iovec *iov, loff_t offset,
-                             unsigned long nr_segs)
+                             struct iov_iter *iter, loff_t offset)
 {
        struct file *file = iocb->ki_filp;
        struct inode *inode = file->f_mapping->host;
@@ -1081,7 +1080,7 @@ static ssize_t gfs2_direct_IO(int rw, struct kiocb *iocb,
         */
        if (mapping->nrpages) {
                loff_t lstart = offset & (PAGE_CACHE_SIZE - 1);
-               loff_t len = iov_length(iov, nr_segs);
+               loff_t len = iov_iter_count(iter);
                loff_t end = PAGE_ALIGN(offset + len) - 1;
 
                rv = 0;
@@ -1096,9 +1095,9 @@ static ssize_t gfs2_direct_IO(int rw, struct kiocb *iocb,
                        truncate_inode_pages_range(mapping, lstart, end);
        }
 
-       rv = __blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
-                                 offset, nr_segs, gfs2_get_block_direct,
-                                 NULL, NULL, 0);
+       rv = __blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev,
+                                 iter, offset,
+                                 gfs2_get_block_direct, NULL, NULL, 0);
 out:
        gfs2_glock_dq(&gh);
        gfs2_holder_uninit(&gh);
index 6ab0cfb2e891014436816e7ce2021a745291d2d6..4fc3a3046174dc9a296c90a0d0ca6d53485e277b 100644 (file)
@@ -684,7 +684,7 @@ static int gfs2_fsync(struct file *file, loff_t start, loff_t end,
 }
 
 /**
- * gfs2_file_aio_write - Perform a write to a file
+ * gfs2_file_write_iter - Perform a write to a file
  * @iocb: The io context
  * @iov: The data to write
  * @nr_segs: Number of @iov segments
@@ -697,11 +697,9 @@ static int gfs2_fsync(struct file *file, loff_t start, loff_t end,
  *
  */
 
-static ssize_t gfs2_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
-                                  unsigned long nr_segs, loff_t pos)
+static ssize_t gfs2_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
 {
        struct file *file = iocb->ki_filp;
-       size_t writesize = iov_length(iov, nr_segs);
        struct gfs2_inode *ip = GFS2_I(file_inode(file));
        int ret;
 
@@ -709,7 +707,7 @@ static ssize_t gfs2_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
        if (ret)
                return ret;
 
-       gfs2_size_hint(file, pos, writesize);
+       gfs2_size_hint(file, iocb->ki_pos, iov_iter_count(from));
 
        if (file->f_flags & O_APPEND) {
                struct gfs2_holder gh;
@@ -720,7 +718,7 @@ static ssize_t gfs2_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
                gfs2_glock_dq_uninit(&gh);
        }
 
-       return generic_file_aio_write(iocb, iov, nr_segs, pos);
+       return generic_file_write_iter(iocb, from);
 }
 
 static int fallocate_chunk(struct inode *inode, loff_t offset, loff_t len,
@@ -1058,10 +1056,10 @@ static int gfs2_flock(struct file *file, int cmd, struct file_lock *fl)
 
 const struct file_operations gfs2_file_fops = {
        .llseek         = gfs2_llseek,
-       .read           = do_sync_read,
-       .aio_read       = generic_file_aio_read,
-       .write          = do_sync_write,
-       .aio_write      = gfs2_file_aio_write,
+       .read           = new_sync_read,
+       .read_iter      = generic_file_read_iter,
+       .write          = new_sync_write,
+       .write_iter     = gfs2_file_write_iter,
        .unlocked_ioctl = gfs2_ioctl,
        .mmap           = gfs2_mmap,
        .open           = gfs2_open,
@@ -1070,7 +1068,7 @@ const struct file_operations gfs2_file_fops = {
        .lock           = gfs2_lock,
        .flock          = gfs2_flock,
        .splice_read    = generic_file_splice_read,
-       .splice_write   = generic_file_splice_write,
+       .splice_write   = iter_file_splice_write,
        .setlease       = gfs2_setlease,
        .fallocate      = gfs2_fallocate,
 };
@@ -1090,17 +1088,17 @@ const struct file_operations gfs2_dir_fops = {
 
 const struct file_operations gfs2_file_fops_nolock = {
        .llseek         = gfs2_llseek,
-       .read           = do_sync_read,
-       .aio_read       = generic_file_aio_read,
-       .write          = do_sync_write,
-       .aio_write      = gfs2_file_aio_write,
+       .read           = new_sync_read,
+       .read_iter      = generic_file_read_iter,
+       .write          = new_sync_write,
+       .write_iter     = gfs2_file_write_iter,
        .unlocked_ioctl = gfs2_ioctl,
        .mmap           = gfs2_mmap,
        .open           = gfs2_open,
        .release        = gfs2_release,
        .fsync          = gfs2_fsync,
        .splice_read    = generic_file_splice_read,
-       .splice_write   = generic_file_splice_write,
+       .splice_write   = iter_file_splice_write,
        .setlease       = generic_setlease,
        .fallocate      = gfs2_fallocate,
 };
index 9e2fecd62f6245b1a48afe12aac49c5d2573fbf2..d0929bc817826e012cc829bb0f021832eea24379 100644 (file)
@@ -125,15 +125,15 @@ static int hfs_releasepage(struct page *page, gfp_t mask)
 }
 
 static ssize_t hfs_direct_IO(int rw, struct kiocb *iocb,
-               const struct iovec *iov, loff_t offset, unsigned long nr_segs)
+               struct iov_iter *iter, loff_t offset)
 {
        struct file *file = iocb->ki_filp;
        struct address_space *mapping = file->f_mapping;
        struct inode *inode = file_inode(file)->i_mapping->host;
+       size_t count = iov_iter_count(iter);
        ssize_t ret;
 
-       ret = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs,
-                                hfs_get_block);
+       ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, hfs_get_block);
 
        /*
         * In case of error extending write may have instantiated a few
@@ -141,7 +141,7 @@ static ssize_t hfs_direct_IO(int rw, struct kiocb *iocb,
         */
        if (unlikely((rw & WRITE) && ret < 0)) {
                loff_t isize = i_size_read(inode);
-               loff_t end = offset + iov_length(iov, nr_segs);
+               loff_t end = offset + count;
 
                if (end > isize)
                        hfs_write_failed(mapping, end);
@@ -674,10 +674,10 @@ static int hfs_file_fsync(struct file *filp, loff_t start, loff_t end,
 
 static const struct file_operations hfs_file_operations = {
        .llseek         = generic_file_llseek,
-       .read           = do_sync_read,
-       .aio_read       = generic_file_aio_read,
-       .write          = do_sync_write,
-       .aio_write      = generic_file_aio_write,
+       .read           = new_sync_read,
+       .read_iter      = generic_file_read_iter,
+       .write          = new_sync_write,
+       .write_iter     = generic_file_write_iter,
        .mmap           = generic_file_mmap,
        .splice_read    = generic_file_splice_read,
        .fsync          = hfs_file_fsync,
index a4f45bd88a631ad5a153a8f45fdd1044a3637c1c..0cf786f2d046f9fbae9b110a2a2d212c008fb3aa 100644 (file)
@@ -123,14 +123,15 @@ static int hfsplus_releasepage(struct page *page, gfp_t mask)
 }
 
 static ssize_t hfsplus_direct_IO(int rw, struct kiocb *iocb,
-               const struct iovec *iov, loff_t offset, unsigned long nr_segs)
+               struct iov_iter *iter, loff_t offset)
 {
        struct file *file = iocb->ki_filp;
        struct address_space *mapping = file->f_mapping;
        struct inode *inode = file_inode(file)->i_mapping->host;
+       size_t count = iov_iter_count(iter);
        ssize_t ret;
 
-       ret = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs,
+       ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, 
                                 hfsplus_get_block);
 
        /*
@@ -139,7 +140,7 @@ static ssize_t hfsplus_direct_IO(int rw, struct kiocb *iocb,
         */
        if (unlikely((rw & WRITE) && ret < 0)) {
                loff_t isize = i_size_read(inode);
-               loff_t end = offset + iov_length(iov, nr_segs);
+               loff_t end = offset + count;
 
                if (end > isize)
                        hfsplus_write_failed(mapping, end);
@@ -340,10 +341,10 @@ static const struct inode_operations hfsplus_file_inode_operations = {
 
 static const struct file_operations hfsplus_file_operations = {
        .llseek         = generic_file_llseek,
-       .read           = do_sync_read,
-       .aio_read       = generic_file_aio_read,
-       .write          = do_sync_write,
-       .aio_write      = generic_file_aio_write,
+       .read           = new_sync_read,
+       .read_iter      = generic_file_read_iter,
+       .write          = new_sync_write,
+       .write_iter     = generic_file_write_iter,
        .mmap           = generic_file_mmap,
        .splice_read    = generic_file_splice_read,
        .fsync          = hfsplus_file_fsync,
index 9c470fde9878eae280eeb62b7ef9ca3c263c9b4c..bb529f3b7f2bf8a119ec5f2eba3c260e09c361c7 100644 (file)
@@ -378,11 +378,11 @@ static int hostfs_fsync(struct file *file, loff_t start, loff_t end,
 
 static const struct file_operations hostfs_file_fops = {
        .llseek         = generic_file_llseek,
-       .read           = do_sync_read,
+       .read           = new_sync_read,
        .splice_read    = generic_file_splice_read,
-       .aio_read       = generic_file_aio_read,
-       .aio_write      = generic_file_aio_write,
-       .write          = do_sync_write,
+       .read_iter      = generic_file_read_iter,
+       .write_iter     = generic_file_write_iter,
+       .write          = new_sync_write,
        .mmap           = generic_file_mmap,
        .open           = hostfs_file_open,
        .release        = hostfs_file_release,
index 67c1a61e09558e0bb632638b0f65d8316ab9b5f2..7f54e5f76cececd4bf76354edb13eb246a81df78 100644 (file)
@@ -197,10 +197,10 @@ const struct address_space_operations hpfs_aops = {
 const struct file_operations hpfs_file_ops =
 {
        .llseek         = generic_file_llseek,
-       .read           = do_sync_read,
-       .aio_read       = generic_file_aio_read,
-       .write          = do_sync_write,
-       .aio_write      = generic_file_aio_write,
+       .read           = new_sync_read,
+       .read_iter      = generic_file_read_iter,
+       .write          = new_sync_write,
+       .write_iter     = generic_file_write_iter,
        .mmap           = generic_file_mmap,
        .release        = hpfs_file_release,
        .fsync          = hpfs_file_fsync,
index 256cd19a3b78c006a1439f893b1b51a0341c938a..64989ca9ba90b71e3a8d16ff90dda67a913cc988 100644 (file)
@@ -51,10 +51,10 @@ const struct file_operations jffs2_file_operations =
 {
        .llseek =       generic_file_llseek,
        .open =         generic_file_open,
-       .read =         do_sync_read,
-       .aio_read =     generic_file_aio_read,
-       .write =        do_sync_write,
-       .aio_write =    generic_file_aio_write,
+       .read =         new_sync_read,
+       .read_iter =    generic_file_read_iter,
+       .write =        new_sync_write,
+       .write_iter =   generic_file_write_iter,
        .unlocked_ioctl=jffs2_ioctl,
        .mmap =         generic_file_readonly_mmap,
        .fsync =        jffs2_fsync,
index 794da944d5cd29c63d8db31040340e83d7079d87..33aa0cc1f8b863b7b101bec470af4238d0c6eeb1 100644 (file)
@@ -151,13 +151,13 @@ const struct inode_operations jfs_file_inode_operations = {
 const struct file_operations jfs_file_operations = {
        .open           = jfs_open,
        .llseek         = generic_file_llseek,
-       .write          = do_sync_write,
-       .read           = do_sync_read,
-       .aio_read       = generic_file_aio_read,
-       .aio_write      = generic_file_aio_write,
+       .write          = new_sync_write,
+       .read           = new_sync_read,
+       .read_iter      = generic_file_read_iter,
+       .write_iter     = generic_file_write_iter,
        .mmap           = generic_file_mmap,
        .splice_read    = generic_file_splice_read,
-       .splice_write   = generic_file_splice_write,
+       .splice_write   = iter_file_splice_write,
        .fsync          = jfs_fsync,
        .release        = jfs_release,
        .unlocked_ioctl = jfs_ioctl,
index 6f8fe72c2a7ae201be639a94e0aca529e9270019..bd3df1ca3c9b7f955571c056f86f98e97beda7b9 100644 (file)
@@ -331,15 +331,15 @@ static sector_t jfs_bmap(struct address_space *mapping, sector_t block)
 }
 
 static ssize_t jfs_direct_IO(int rw, struct kiocb *iocb,
-       const struct iovec *iov, loff_t offset, unsigned long nr_segs)
+       struct iov_iter *iter, loff_t offset)
 {
        struct file *file = iocb->ki_filp;
        struct address_space *mapping = file->f_mapping;
        struct inode *inode = file->f_mapping->host;
+       size_t count = iov_iter_count(iter);
        ssize_t ret;
 
-       ret = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs,
-                                jfs_get_block);
+       ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, jfs_get_block);
 
        /*
         * In case of error extending write may have instantiated a few
@@ -347,7 +347,7 @@ static ssize_t jfs_direct_IO(int rw, struct kiocb *iocb,
         */
        if (unlikely((rw & WRITE) && ret < 0)) {
                loff_t isize = i_size_read(inode);
-               loff_t end = offset + iov_length(iov, nr_segs);
+               loff_t end = offset + count;
 
                if (end > isize)
                        jfs_write_failed(mapping, end);
index 57914fc32b62538f43909d35ffc031742b98a881..8538752df2f6a7dbb3dad0119e4e8bc47f4a25f4 100644 (file)
@@ -264,15 +264,15 @@ const struct inode_operations logfs_reg_iops = {
 };
 
 const struct file_operations logfs_reg_fops = {
-       .aio_read       = generic_file_aio_read,
-       .aio_write      = generic_file_aio_write,
+       .read_iter      = generic_file_read_iter,
+       .write_iter     = generic_file_write_iter,
        .fsync          = logfs_fsync,
        .unlocked_ioctl = logfs_ioctl,
        .llseek         = generic_file_llseek,
        .mmap           = generic_file_readonly_mmap,
        .open           = generic_file_open,
-       .read           = do_sync_read,
-       .write          = do_sync_write,
+       .read           = new_sync_read,
+       .write          = new_sync_write,
 };
 
 const struct address_space_operations logfs_reg_aops = {
index adc6f5494231bc947f45d8a3c526db0b36f39bf3..a967de085ac0f4cf7193101cd4e54a08bb4fff50 100644 (file)
  */
 const struct file_operations minix_file_operations = {
        .llseek         = generic_file_llseek,
-       .read           = do_sync_read,
-       .aio_read       = generic_file_aio_read,
-       .write          = do_sync_write,
-       .aio_write      = generic_file_aio_write,
+       .read           = new_sync_read,
+       .read_iter      = generic_file_read_iter,
+       .write          = new_sync_write,
+       .write_iter     = generic_file_write_iter,
        .mmap           = generic_file_mmap,
        .fsync          = generic_file_fsync,
        .splice_read    = generic_file_splice_read,
index 4ad7bc3886791b0078ebc3ae4b326ed5e4c6566b..8f98138cbc4385ba63b3af77ae907219d22e6991 100644 (file)
@@ -212,20 +212,20 @@ static int nfs_direct_cmp_commit_data_verf(struct nfs_direct_req *dreq,
  * shunt off direct read and write requests before the VFS gets them,
  * so this method is only ever called for swap.
  */
-ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t pos, unsigned long nr_segs)
+ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, loff_t pos)
 {
 #ifndef CONFIG_NFS_SWAP
        dprintk("NFS: nfs_direct_IO (%pD) off/no(%Ld/%lu) EINVAL\n",
-                       iocb->ki_filp, (long long) pos, nr_segs);
+                       iocb->ki_filp, (long long) pos, iter->nr_segs);
 
        return -EINVAL;
 #else
        VM_BUG_ON(iocb->ki_nbytes != PAGE_SIZE);
 
        if (rw == READ || rw == KERNEL_READ)
-               return nfs_file_direct_read(iocb, iov, nr_segs, pos,
+               return nfs_file_direct_read(iocb, iter, pos,
                                rw == READ ? true : false);
-       return nfs_file_direct_write(iocb, iov, nr_segs, pos,
+       return nfs_file_direct_write(iocb, iter, pos,
                                rw == WRITE ? true : false);
 #endif /* CONFIG_NFS_SWAP */
 }
@@ -414,60 +414,37 @@ static const struct nfs_pgio_completion_ops nfs_direct_read_completion_ops = {
  * handled automatically by nfs_direct_read_result().  Otherwise, if
  * no requests have been sent, just return an error.
  */
-static ssize_t nfs_direct_read_schedule_segment(struct nfs_pageio_descriptor *desc,
-                                               const struct iovec *iov,
-                                               loff_t pos, bool uio)
-{
-       struct nfs_direct_req *dreq = desc->pg_dreq;
-       struct nfs_open_context *ctx = dreq->ctx;
-       struct inode *inode = ctx->dentry->d_inode;
-       unsigned long user_addr = (unsigned long)iov->iov_base;
-       size_t count = iov->iov_len;
-       size_t rsize = NFS_SERVER(inode)->rsize;
-       unsigned int pgbase;
-       int result;
-       ssize_t started = 0;
-       struct page **pagevec = NULL;
-       unsigned int npages;
-
-       do {
-               size_t bytes;
-               int i;
 
-               pgbase = user_addr & ~PAGE_MASK;
-               bytes = min(max_t(size_t, rsize, PAGE_SIZE), count);
+static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
+                                             struct iov_iter *iter,
+                                             loff_t pos)
+{
+       struct nfs_pageio_descriptor desc;
+       struct inode *inode = dreq->inode;
+       ssize_t result = -EINVAL;
+       size_t requested_bytes = 0;
+       size_t rsize = max_t(size_t, NFS_SERVER(inode)->rsize, PAGE_SIZE);
 
-               result = -ENOMEM;
-               npages = nfs_page_array_len(pgbase, bytes);
-               if (!pagevec)
-                       pagevec = kmalloc(npages * sizeof(struct page *),
-                                         GFP_KERNEL);
-               if (!pagevec)
-                       break;
-               if (uio) {
-                       down_read(&current->mm->mmap_sem);
-                       result = get_user_pages(current, current->mm, user_addr,
-                                       npages, 1, 0, pagevec, NULL);
-                       up_read(&current->mm->mmap_sem);
-                       if (result < 0)
-                               break;
-               } else {
-                       WARN_ON(npages != 1);
-                       result = get_kernel_page(user_addr, 1, pagevec);
-                       if (WARN_ON(result != 1))
-                               break;
-               }
+       nfs_pageio_init_read(&desc, dreq->inode, false,
+                            &nfs_direct_read_completion_ops);
+       get_dreq(dreq);
+       desc.pg_dreq = dreq;
+       atomic_inc(&inode->i_dio_count);
 
-               if ((unsigned)result < npages) {
-                       bytes = result * PAGE_SIZE;
-                       if (bytes <= pgbase) {
-                               nfs_direct_release_pages(pagevec, result);
-                               break;
-                       }
-                       bytes -= pgbase;
-                       npages = result;
-               }
+       while (iov_iter_count(iter)) {
+               struct page **pagevec;
+               size_t bytes;
+               size_t pgbase;
+               unsigned npages, i;
 
+               result = iov_iter_get_pages_alloc(iter, &pagevec, 
+                                                 rsize, &pgbase);
+               if (result < 0)
+                       break;
+       
+               bytes = result;
+               iov_iter_advance(iter, bytes);
+               npages = (result + pgbase + PAGE_SIZE - 1) / PAGE_SIZE;
                for (i = 0; i < npages; i++) {
                        struct nfs_page *req;
                        unsigned int req_len = min_t(size_t, bytes, PAGE_SIZE - pgbase);
@@ -480,56 +457,21 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_pageio_descriptor *de
                        }
                        req->wb_index = pos >> PAGE_SHIFT;
                        req->wb_offset = pos & ~PAGE_MASK;
-                       if (!nfs_pageio_add_request(desc, req)) {
-                               result = desc->pg_error;
+                       if (!nfs_pageio_add_request(&desc, req)) {
+                               result = desc.pg_error;
                                nfs_release_request(req);
                                break;
                        }
                        pgbase = 0;
                        bytes -= req_len;
-                       started += req_len;
-                       user_addr += req_len;
+                       requested_bytes += req_len;
                        pos += req_len;
-                       count -= req_len;
                        dreq->bytes_left -= req_len;
                }
-               /* The nfs_page now hold references to these pages */
                nfs_direct_release_pages(pagevec, npages);
-       } while (count != 0 && result >= 0);
-
-       kfree(pagevec);
-
-       if (started)
-               return started;
-       return result < 0 ? (ssize_t) result : -EFAULT;
-}
-
-static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
-                                             const struct iovec *iov,
-                                             unsigned long nr_segs,
-                                             loff_t pos, bool uio)
-{
-       struct nfs_pageio_descriptor desc;
-       struct inode *inode = dreq->inode;
-       ssize_t result = -EINVAL;
-       size_t requested_bytes = 0;
-       unsigned long seg;
-
-       nfs_pageio_init_read(&desc, dreq->inode, false,
-                            &nfs_direct_read_completion_ops);
-       get_dreq(dreq);
-       desc.pg_dreq = dreq;
-       atomic_inc(&inode->i_dio_count);
-
-       for (seg = 0; seg < nr_segs; seg++) {
-               const struct iovec *vec = &iov[seg];
-               result = nfs_direct_read_schedule_segment(&desc, vec, pos, uio);
+               kvfree(pagevec);
                if (result < 0)
                        break;
-               requested_bytes += result;
-               if ((size_t)result < vec->iov_len)
-                       break;
-               pos += vec->iov_len;
        }
 
        nfs_pageio_complete(&desc);
@@ -552,8 +494,7 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
 /**
  * nfs_file_direct_read - file direct read operation for NFS files
  * @iocb: target I/O control block
- * @iov: vector of user buffers into which to read data
- * @nr_segs: size of iov vector
+ * @iter: vector of user buffers into which to read data
  * @pos: byte offset in file where reading starts
  *
  * We use this function for direct reads instead of calling
@@ -570,8 +511,8 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
  * client must read the updated atime from the server back into its
  * cache.
  */
-ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov,
-                               unsigned long nr_segs, loff_t pos, bool uio)
+ssize_t nfs_file_direct_read(struct kiocb *iocb, struct iov_iter *iter,
+                               loff_t pos, bool uio)
 {
        struct file *file = iocb->ki_filp;
        struct address_space *mapping = file->f_mapping;
@@ -579,9 +520,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov,
        struct nfs_direct_req *dreq;
        struct nfs_lock_context *l_ctx;
        ssize_t result = -EINVAL;
-       size_t count;
-
-       count = iov_length(iov, nr_segs);
+       size_t count = iov_iter_count(iter);
        nfs_add_stats(mapping->host, NFSIOS_DIRECTREADBYTES, count);
 
        dfprintk(FILE, "NFS: direct read(%pD2, %zd@%Ld)\n",
@@ -604,7 +543,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov,
                goto out_unlock;
 
        dreq->inode = inode;
-       dreq->bytes_left = iov_length(iov, nr_segs);
+       dreq->bytes_left = count;
        dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp));
        l_ctx = nfs_get_lock_context(dreq->ctx);
        if (IS_ERR(l_ctx)) {
@@ -615,8 +554,8 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov,
        if (!is_sync_kiocb(iocb))
                dreq->iocb = iocb;
 
-       NFS_I(inode)->read_io += iov_length(iov, nr_segs);
-       result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos, uio);
+       NFS_I(inode)->read_io += count;
+       result = nfs_direct_read_schedule_iovec(dreq, iter, pos);
 
        mutex_unlock(&inode->i_mutex);
 
@@ -772,108 +711,6 @@ static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode
 }
 #endif
 
-/*
- * NB: Return the value of the first error return code.  Subsequent
- *     errors after the first one are ignored.
- */
-/*
- * For each wsize'd chunk of the user's buffer, dispatch an NFS WRITE
- * operation.  If nfs_writedata_alloc() or get_user_pages() fails,
- * bail and stop sending more writes.  Write length accounting is
- * handled automatically by nfs_direct_write_result().  Otherwise, if
- * no requests have been sent, just return an error.
- */
-static ssize_t nfs_direct_write_schedule_segment(struct nfs_pageio_descriptor *desc,
-                                                const struct iovec *iov,
-                                                loff_t pos, bool uio)
-{
-       struct nfs_direct_req *dreq = desc->pg_dreq;
-       struct nfs_open_context *ctx = dreq->ctx;
-       struct inode *inode = ctx->dentry->d_inode;
-       unsigned long user_addr = (unsigned long)iov->iov_base;
-       size_t count = iov->iov_len;
-       size_t wsize = NFS_SERVER(inode)->wsize;
-       unsigned int pgbase;
-       int result;
-       ssize_t started = 0;
-       struct page **pagevec = NULL;
-       unsigned int npages;
-
-       do {
-               size_t bytes;
-               int i;
-
-               pgbase = user_addr & ~PAGE_MASK;
-               bytes = min(max_t(size_t, wsize, PAGE_SIZE), count);
-
-               result = -ENOMEM;
-               npages = nfs_page_array_len(pgbase, bytes);
-               if (!pagevec)
-                       pagevec = kmalloc(npages * sizeof(struct page *), GFP_KERNEL);
-               if (!pagevec)
-                       break;
-
-               if (uio) {
-                       down_read(&current->mm->mmap_sem);
-                       result = get_user_pages(current, current->mm, user_addr,
-                                               npages, 0, 0, pagevec, NULL);
-                       up_read(&current->mm->mmap_sem);
-                       if (result < 0)
-                               break;
-               } else {
-                       WARN_ON(npages != 1);
-                       result = get_kernel_page(user_addr, 0, pagevec);
-                       if (WARN_ON(result != 1))
-                               break;
-               }
-
-               if ((unsigned)result < npages) {
-                       bytes = result * PAGE_SIZE;
-                       if (bytes <= pgbase) {
-                               nfs_direct_release_pages(pagevec, result);
-                               break;
-                       }
-                       bytes -= pgbase;
-                       npages = result;
-               }
-
-               for (i = 0; i < npages; i++) {
-                       struct nfs_page *req;
-                       unsigned int req_len = min_t(size_t, bytes, PAGE_SIZE - pgbase);
-
-                       req = nfs_create_request(dreq->ctx, pagevec[i], NULL,
-                                                pgbase, req_len);
-                       if (IS_ERR(req)) {
-                               result = PTR_ERR(req);
-                               break;
-                       }
-                       nfs_lock_request(req);
-                       req->wb_index = pos >> PAGE_SHIFT;
-                       req->wb_offset = pos & ~PAGE_MASK;
-                       if (!nfs_pageio_add_request(desc, req)) {
-                               result = desc->pg_error;
-                               nfs_unlock_and_release_request(req);
-                               break;
-                       }
-                       pgbase = 0;
-                       bytes -= req_len;
-                       started += req_len;
-                       user_addr += req_len;
-                       pos += req_len;
-                       count -= req_len;
-                       dreq->bytes_left -= req_len;
-               }
-               /* The nfs_page now hold references to these pages */
-               nfs_direct_release_pages(pagevec, npages);
-       } while (count != 0 && result >= 0);
-
-       kfree(pagevec);
-
-       if (started)
-               return started;
-       return result < 0 ? (ssize_t) result : -EFAULT;
-}
-
 static void nfs_direct_write_completion(struct nfs_pgio_header *hdr)
 {
        struct nfs_direct_req *dreq = hdr->dreq;
@@ -956,16 +793,27 @@ static const struct nfs_pgio_completion_ops nfs_direct_write_completion_ops = {
        .completion = nfs_direct_write_completion,
 };
 
+
+/*
+ * NB: Return the value of the first error return code.  Subsequent
+ *     errors after the first one are ignored.
+ */
+/*
+ * For each wsize'd chunk of the user's buffer, dispatch an NFS WRITE
+ * operation.  If nfs_writedata_alloc() or get_user_pages() fails,
+ * bail and stop sending more writes.  Write length accounting is
+ * handled automatically by nfs_direct_write_result().  Otherwise, if
+ * no requests have been sent, just return an error.
+ */
 static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
-                                              const struct iovec *iov,
-                                              unsigned long nr_segs,
-                                              loff_t pos, bool uio)
+                                              struct iov_iter *iter,
+                                              loff_t pos)
 {
        struct nfs_pageio_descriptor desc;
        struct inode *inode = dreq->inode;
        ssize_t result = 0;
        size_t requested_bytes = 0;
-       unsigned long seg;
+       size_t wsize = max_t(size_t, NFS_SERVER(inode)->wsize, PAGE_SIZE);
 
        nfs_pageio_init_write(&desc, inode, FLUSH_COND_STABLE, false,
                              &nfs_direct_write_completion_ops);
@@ -973,16 +821,49 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
        get_dreq(dreq);
        atomic_inc(&inode->i_dio_count);
 
-       NFS_I(dreq->inode)->write_io += iov_length(iov, nr_segs);
-       for (seg = 0; seg < nr_segs; seg++) {
-               const struct iovec *vec = &iov[seg];
-               result = nfs_direct_write_schedule_segment(&desc, vec, pos, uio);
+       NFS_I(inode)->write_io += iov_iter_count(iter);
+       while (iov_iter_count(iter)) {
+               struct page **pagevec;
+               size_t bytes;
+               size_t pgbase;
+               unsigned npages, i;
+
+               result = iov_iter_get_pages_alloc(iter, &pagevec, 
+                                                 wsize, &pgbase);
                if (result < 0)
                        break;
-               requested_bytes += result;
-               if ((size_t)result < vec->iov_len)
+
+               bytes = result;
+               iov_iter_advance(iter, bytes);
+               npages = (result + pgbase + PAGE_SIZE - 1) / PAGE_SIZE;
+               for (i = 0; i < npages; i++) {
+                       struct nfs_page *req;
+                       unsigned int req_len = min_t(size_t, bytes, PAGE_SIZE - pgbase);
+
+                       req = nfs_create_request(dreq->ctx, pagevec[i], NULL,
+                                                pgbase, req_len);
+                       if (IS_ERR(req)) {
+                               result = PTR_ERR(req);
+                               break;
+                       }
+                       nfs_lock_request(req);
+                       req->wb_index = pos >> PAGE_SHIFT;
+                       req->wb_offset = pos & ~PAGE_MASK;
+                       if (!nfs_pageio_add_request(&desc, req)) {
+                               result = desc.pg_error;
+                               nfs_unlock_and_release_request(req);
+                               break;
+                       }
+                       pgbase = 0;
+                       bytes -= req_len;
+                       requested_bytes += req_len;
+                       pos += req_len;
+                       dreq->bytes_left -= req_len;
+               }
+               nfs_direct_release_pages(pagevec, npages);
+               kvfree(pagevec);
+               if (result < 0)
                        break;
-               pos += vec->iov_len;
        }
        nfs_pageio_complete(&desc);
 
@@ -1004,8 +885,7 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
 /**
  * nfs_file_direct_write - file direct write operation for NFS files
  * @iocb: target I/O control block
- * @iov: vector of user buffers from which to write data
- * @nr_segs: size of iov vector
+ * @iter: vector of user buffers from which to write data
  * @pos: byte offset in file where writing starts
  *
  * We use this function for direct writes instead of calling
@@ -1023,8 +903,8 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
  * Note that O_APPEND is not supported for NFS direct writes, as there
  * is no atomic O_APPEND write facility in the NFS protocol.
  */
-ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
-                               unsigned long nr_segs, loff_t pos, bool uio)
+ssize_t nfs_file_direct_write(struct kiocb *iocb, struct iov_iter *iter,
+                               loff_t pos, bool uio)
 {
        ssize_t result = -EINVAL;
        struct file *file = iocb->ki_filp;
@@ -1033,9 +913,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
        struct nfs_direct_req *dreq;
        struct nfs_lock_context *l_ctx;
        loff_t end;
-       size_t count;
-
-       count = iov_length(iov, nr_segs);
+       size_t count = iov_iter_count(iter);
        end = (pos + count - 1) >> PAGE_CACHE_SHIFT;
 
        nfs_add_stats(mapping->host, NFSIOS_DIRECTWRITTENBYTES, count);
@@ -1086,7 +964,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
        if (!is_sync_kiocb(iocb))
                dreq->iocb = iocb;
 
-       result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos, uio);
+       result = nfs_direct_write_schedule_iovec(dreq, iter, pos);
 
        if (mapping->nrpages) {
                invalidate_inode_pages2_range(mapping,
index c1edf7336315c3f8ddffe45261d814b8f1877771..4042ff58fe3f3d0b18d705774c3f6d975e642248 100644 (file)
@@ -165,22 +165,21 @@ nfs_file_flush(struct file *file, fl_owner_t id)
 EXPORT_SYMBOL_GPL(nfs_file_flush);
 
 ssize_t
-nfs_file_read(struct kiocb *iocb, const struct iovec *iov,
-               unsigned long nr_segs, loff_t pos)
+nfs_file_read(struct kiocb *iocb, struct iov_iter *to)
 {
        struct inode *inode = file_inode(iocb->ki_filp);
        ssize_t result;
 
        if (iocb->ki_filp->f_flags & O_DIRECT)
-               return nfs_file_direct_read(iocb, iov, nr_segs, pos, true);
+               return nfs_file_direct_read(iocb, to, iocb->ki_pos, true);
 
-       dprintk("NFS: read(%pD2, %lu@%lu)\n",
+       dprintk("NFS: read(%pD2, %zu@%lu)\n",
                iocb->ki_filp,
-               (unsigned long) iov_length(iov, nr_segs), (unsigned long) pos);
+               iov_iter_count(to), (unsigned long) iocb->ki_pos);
 
        result = nfs_revalidate_mapping(inode, iocb->ki_filp->f_mapping);
        if (!result) {
-               result = generic_file_aio_read(iocb, iov, nr_segs, pos);
+               result = generic_file_read_iter(iocb, to);
                if (result > 0)
                        nfs_add_stats(inode, NFSIOS_NORMALREADBYTES, result);
        }
@@ -635,24 +634,24 @@ static int nfs_need_sync_write(struct file *filp, struct inode *inode)
        return 0;
 }
 
-ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
-                      unsigned long nr_segs, loff_t pos)
+ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
 {
        struct file *file = iocb->ki_filp;
        struct inode *inode = file_inode(file);
        unsigned long written = 0;
        ssize_t result;
-       size_t count = iov_length(iov, nr_segs);
+       size_t count = iov_iter_count(from);
+       loff_t pos = iocb->ki_pos;
 
        result = nfs_key_timeout_notify(file, inode);
        if (result)
                return result;
 
        if (file->f_flags & O_DIRECT)
-               return nfs_file_direct_write(iocb, iov, nr_segs, pos, true);
+               return nfs_file_direct_write(iocb, from, pos, true);
 
-       dprintk("NFS: write(%pD2, %lu@%Ld)\n",
-               file, (unsigned long) count, (long long) pos);
+       dprintk("NFS: write(%pD2, %zu@%Ld)\n",
+               file, count, (long long) pos);
 
        result = -EBUSY;
        if (IS_SWAPFILE(inode))
@@ -670,7 +669,7 @@ ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
        if (!count)
                goto out;
 
-       result = generic_file_aio_write(iocb, iov, nr_segs, pos);
+       result = generic_file_write_iter(iocb, from);
        if (result > 0)
                written = result;
 
@@ -691,36 +690,6 @@ out_swapfile:
 }
 EXPORT_SYMBOL_GPL(nfs_file_write);
 
-ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe,
-                             struct file *filp, loff_t *ppos,
-                             size_t count, unsigned int flags)
-{
-       struct inode *inode = file_inode(filp);
-       unsigned long written = 0;
-       ssize_t ret;
-
-       dprintk("NFS splice_write(%pD2, %lu@%llu)\n",
-               filp, (unsigned long) count, (unsigned long long) *ppos);
-
-       /*
-        * The combination of splice and an O_APPEND destination is disallowed.
-        */
-
-       ret = generic_file_splice_write(pipe, filp, ppos, count, flags);
-       if (ret > 0)
-               written = ret;
-
-       if (ret >= 0 && nfs_need_sync_write(filp, inode)) {
-               int err = vfs_fsync(filp, 0);
-               if (err < 0)
-                       ret = err;
-       }
-       if (ret > 0)
-               nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, written);
-       return ret;
-}
-EXPORT_SYMBOL_GPL(nfs_file_splice_write);
-
 static int
 do_getlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
 {
@@ -935,10 +904,10 @@ EXPORT_SYMBOL_GPL(nfs_setlease);
 
 const struct file_operations nfs_file_operations = {
        .llseek         = nfs_file_llseek,
-       .read           = do_sync_read,
-       .write          = do_sync_write,
-       .aio_read       = nfs_file_read,
-       .aio_write      = nfs_file_write,
+       .read           = new_sync_read,
+       .write          = new_sync_write,
+       .read_iter      = nfs_file_read,
+       .write_iter     = nfs_file_write,
        .mmap           = nfs_file_mmap,
        .open           = nfs_file_open,
        .flush          = nfs_file_flush,
@@ -947,7 +916,7 @@ const struct file_operations nfs_file_operations = {
        .lock           = nfs_lock,
        .flock          = nfs_flock,
        .splice_read    = nfs_file_splice_read,
-       .splice_write   = nfs_file_splice_write,
+       .splice_write   = iter_file_splice_write,
        .check_flags    = nfs_check_flags,
        .setlease       = nfs_setlease,
 };
index 8b69cba1bb04d9b177bca18a2f95c7b0162b8cf1..82ddbf46660e3c1be7d499f2ca014ce619da8603 100644 (file)
@@ -327,16 +327,14 @@ int nfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *)
 int nfs_file_fsync_commit(struct file *, loff_t, loff_t, int);
 loff_t nfs_file_llseek(struct file *, loff_t, int);
 int nfs_file_flush(struct file *, fl_owner_t);
-ssize_t nfs_file_read(struct kiocb *, const struct iovec *, unsigned long, loff_t);
+ssize_t nfs_file_read(struct kiocb *, struct iov_iter *);
 ssize_t nfs_file_splice_read(struct file *, loff_t *, struct pipe_inode_info *,
                             size_t, unsigned int);
 int nfs_file_mmap(struct file *, struct vm_area_struct *);
-ssize_t nfs_file_write(struct kiocb *, const struct iovec *, unsigned long, loff_t);
+ssize_t nfs_file_write(struct kiocb *, struct iov_iter *);
 int nfs_file_release(struct inode *, struct file *);
 int nfs_lock(struct file *, int, struct file_lock *);
 int nfs_flock(struct file *, int, struct file_lock *);
-ssize_t nfs_file_splice_write(struct pipe_inode_info *, struct file *, loff_t *,
-                             size_t, unsigned int);
 int nfs_check_flags(int);
 int nfs_setlease(struct file *, long, struct file_lock **);
 
index 464db9dd63180dc7baf3695f51471747426144fb..a816f0627a6ce03cda2502c42c780c5ab6a2742c 100644 (file)
@@ -117,10 +117,10 @@ nfs4_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
 
 const struct file_operations nfs4_file_operations = {
        .llseek         = nfs_file_llseek,
-       .read           = do_sync_read,
-       .write          = do_sync_write,
-       .aio_read       = nfs_file_read,
-       .aio_write      = nfs_file_write,
+       .read           = new_sync_read,
+       .write          = new_sync_write,
+       .read_iter      = nfs_file_read,
+       .write_iter     = nfs_file_write,
        .mmap           = nfs_file_mmap,
        .open           = nfs4_file_open,
        .flush          = nfs_file_flush,
@@ -129,7 +129,7 @@ const struct file_operations nfs4_file_operations = {
        .lock           = nfs_lock,
        .flock          = nfs_flock,
        .splice_read    = nfs_file_splice_read,
-       .splice_write   = nfs_file_splice_write,
+       .splice_write   = iter_file_splice_write,
        .check_flags    = nfs_check_flags,
        .setlease       = nfs_setlease,
 };
index f3a82fbcae026357431720d998b5eb6e7eafee66..24978153c0c4daefd04f2853576db34aa3e8e0fe 100644 (file)
@@ -152,10 +152,10 @@ static int nilfs_file_mmap(struct file *file, struct vm_area_struct *vma)
  */
 const struct file_operations nilfs_file_operations = {
        .llseek         = generic_file_llseek,
-       .read           = do_sync_read,
-       .write          = do_sync_write,
-       .aio_read       = generic_file_aio_read,
-       .aio_write      = generic_file_aio_write,
+       .read           = new_sync_read,
+       .write          = new_sync_write,
+       .read_iter      = generic_file_read_iter,
+       .write_iter     = generic_file_write_iter,
        .unlocked_ioctl = nilfs_ioctl,
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = nilfs_compat_ioctl,
index b9c5726120e32acb70d20df47986433a6ca5f153..6252b173a46590225e2ba29f7e07cf13aa62eeac 100644 (file)
@@ -298,19 +298,20 @@ static int nilfs_write_end(struct file *file, struct address_space *mapping,
 }
 
 static ssize_t
-nilfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
-               loff_t offset, unsigned long nr_segs)
+nilfs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter,
+               loff_t offset)
 {
        struct file *file = iocb->ki_filp;
        struct address_space *mapping = file->f_mapping;
        struct inode *inode = file->f_mapping->host;
+       size_t count = iov_iter_count(iter);
        ssize_t size;
 
        if (rw == WRITE)
                return 0;
 
        /* Needs synchronization with the cleaner */
-       size = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs,
+       size = blockdev_direct_IO(rw, iocb, inode, iter, offset,
                                  nilfs_get_block);
 
        /*
@@ -319,7 +320,7 @@ nilfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
         */
        if (unlikely((rw & WRITE) && size < 0)) {
                loff_t isize = i_size_read(inode);
-               loff_t end = offset + iov_length(iov, nr_segs);
+               loff_t end = offset + count;
 
                if (end > isize)
                        nilfs_write_failed(mapping, end);
index 86ddab916b6607e3cab28c276359b8b98971a46c..5c9e2c81cb11db029ece7873766041ada8c65024 100644 (file)
@@ -2090,10 +2090,7 @@ static ssize_t ntfs_file_aio_write_nolock(struct kiocb *iocb,
        size_t count;           /* after file limit checks */
        ssize_t written, err;
 
-       count = 0;
-       err = generic_segment_checks(iov, &nr_segs, &count, VERIFY_READ);
-       if (err)
-               return err;
+       count = iov_length(iov, nr_segs);
        pos = *ppos;
        /* We can write back this queue in page reclaim. */
        current->backing_dev_info = mapping->backing_dev_info;
@@ -2202,8 +2199,8 @@ static int ntfs_file_fsync(struct file *filp, loff_t start, loff_t end,
 
 const struct file_operations ntfs_file_ops = {
        .llseek         = generic_file_llseek,   /* Seek inside file. */
-       .read           = do_sync_read,          /* Read from file. */
-       .aio_read       = generic_file_aio_read, /* Async read from file. */
+       .read           = new_sync_read,         /* Read from file. */
+       .read_iter      = generic_file_read_iter, /* Async read from file. */
 #ifdef NTFS_RW
        .write          = do_sync_write,         /* Write to file. */
        .aio_write      = ntfs_file_aio_write,   /* Async write to file. */
index d310d12a9adc481187c78fb65bffba53a16a75e7..4a231a166cf88d6f76495ab9420e7406ba10307a 100644 (file)
@@ -599,9 +599,8 @@ static int ocfs2_releasepage(struct page *page, gfp_t wait)
 
 static ssize_t ocfs2_direct_IO(int rw,
                               struct kiocb *iocb,
-                              const struct iovec *iov,
-                              loff_t offset,
-                              unsigned long nr_segs)
+                              struct iov_iter *iter,
+                              loff_t offset)
 {
        struct file *file = iocb->ki_filp;
        struct inode *inode = file_inode(file)->i_mapping->host;
@@ -618,7 +617,7 @@ static ssize_t ocfs2_direct_IO(int rw,
                return 0;
 
        return __blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev,
-                                   iov, offset, nr_segs,
+                                   iter, offset,
                                    ocfs2_direct_IO_get_blocks,
                                    ocfs2_dio_end_io, NULL, 0);
 }
index 8eb6e5732d3b73b115abea0681cdd95d9195becf..2930e231f3f9fbda2807190726d6ceffffa2a6b5 100644 (file)
@@ -2233,16 +2233,13 @@ out:
        return ret;
 }
 
-static ssize_t ocfs2_file_aio_write(struct kiocb *iocb,
-                                   const struct iovec *iov,
-                                   unsigned long nr_segs,
-                                   loff_t pos)
+static ssize_t ocfs2_file_write_iter(struct kiocb *iocb,
+                                   struct iov_iter *from)
 {
        int ret, direct_io, appending, rw_level, have_alloc_sem  = 0;
        int can_do_direct, has_refcount = 0;
        ssize_t written = 0;
-       size_t ocount;          /* original count */
-       size_t count;           /* after file limit checks */
+       size_t count = iov_iter_count(from);
        loff_t old_size, *ppos = &iocb->ki_pos;
        u32 old_clusters;
        struct file *file = iocb->ki_filp;
@@ -2256,7 +2253,7 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb,
                (unsigned long long)OCFS2_I(inode)->ip_blkno,
                file->f_path.dentry->d_name.len,
                file->f_path.dentry->d_name.name,
-               (unsigned int)nr_segs);
+               (unsigned int)from->nr_segs);   /* GRRRRR */
 
        if (iocb->ki_nbytes == 0)
                return 0;
@@ -2354,29 +2351,21 @@ relock:
        /* communicate with ocfs2_dio_end_io */
        ocfs2_iocb_set_rw_locked(iocb, rw_level);
 
-       ret = generic_segment_checks(iov, &nr_segs, &ocount,
-                                    VERIFY_READ);
-       if (ret)
-               goto out_dio;
-
-       count = ocount;
        ret = generic_write_checks(file, ppos, &count,
                                   S_ISBLK(inode->i_mode));
        if (ret)
                goto out_dio;
 
+       iov_iter_truncate(from, count);
        if (direct_io) {
-               written = generic_file_direct_write(iocb, iov, &nr_segs, *ppos,
-                                                   count, ocount);
+               written = generic_file_direct_write(iocb, from, *ppos);
                if (written < 0) {
                        ret = written;
                        goto out_dio;
                }
        } else {
-               struct iov_iter from;
-               iov_iter_init(&from, iov, nr_segs, count, 0);
                current->backing_dev_info = file->f_mapping->backing_dev_info;
-               written = generic_perform_write(file, &from, *ppos);
+               written = generic_perform_write(file, from, *ppos);
                if (likely(written >= 0))
                        iocb->ki_pos = *ppos + written;
                current->backing_dev_info = NULL;
@@ -2441,84 +2430,6 @@ out_sems:
        return ret;
 }
 
-static int ocfs2_splice_to_file(struct pipe_inode_info *pipe,
-                               struct file *out,
-                               struct splice_desc *sd)
-{
-       int ret;
-
-       ret = ocfs2_prepare_inode_for_write(out, &sd->pos,
-                                           sd->total_len, 0, NULL, NULL);
-       if (ret < 0) {
-               mlog_errno(ret);
-               return ret;
-       }
-
-       return splice_from_pipe_feed(pipe, sd, pipe_to_file);
-}
-
-static ssize_t ocfs2_file_splice_write(struct pipe_inode_info *pipe,
-                                      struct file *out,
-                                      loff_t *ppos,
-                                      size_t len,
-                                      unsigned int flags)
-{
-       int ret;
-       struct address_space *mapping = out->f_mapping;
-       struct inode *inode = mapping->host;
-       struct splice_desc sd = {
-               .total_len = len,
-               .flags = flags,
-               .pos = *ppos,
-               .u.file = out,
-       };
-
-
-       trace_ocfs2_file_splice_write(inode, out, out->f_path.dentry,
-                       (unsigned long long)OCFS2_I(inode)->ip_blkno,
-                       out->f_path.dentry->d_name.len,
-                       out->f_path.dentry->d_name.name, len);
-
-       pipe_lock(pipe);
-
-       splice_from_pipe_begin(&sd);
-       do {
-               ret = splice_from_pipe_next(pipe, &sd);
-               if (ret <= 0)
-                       break;
-
-               mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
-               ret = ocfs2_rw_lock(inode, 1);
-               if (ret < 0)
-                       mlog_errno(ret);
-               else {
-                       ret = ocfs2_splice_to_file(pipe, out, &sd);
-                       ocfs2_rw_unlock(inode, 1);
-               }
-               mutex_unlock(&inode->i_mutex);
-       } while (ret > 0);
-       splice_from_pipe_end(pipe, &sd);
-
-       pipe_unlock(pipe);
-
-       if (sd.num_spliced)
-               ret = sd.num_spliced;
-
-       if (ret > 0) {
-               int err;
-
-               err = generic_write_sync(out, *ppos, ret);
-               if (err)
-                       ret = err;
-               else
-                       *ppos += ret;
-
-               balance_dirty_pages_ratelimited(mapping);
-       }
-
-       return ret;
-}
-
 static ssize_t ocfs2_file_splice_read(struct file *in,
                                      loff_t *ppos,
                                      struct pipe_inode_info *pipe,
@@ -2534,7 +2445,7 @@ static ssize_t ocfs2_file_splice_read(struct file *in,
                        in->f_path.dentry->d_name.name, len);
 
        /*
-        * See the comment in ocfs2_file_aio_read()
+        * See the comment in ocfs2_file_read_iter()
         */
        ret = ocfs2_inode_lock_atime(inode, in->f_path.mnt, &lock_level);
        if (ret < 0) {
@@ -2549,10 +2460,8 @@ bail:
        return ret;
 }
 
-static ssize_t ocfs2_file_aio_read(struct kiocb *iocb,
-                                  const struct iovec *iov,
-                                  unsigned long nr_segs,
-                                  loff_t pos)
+static ssize_t ocfs2_file_read_iter(struct kiocb *iocb,
+                                  struct iov_iter *to)
 {
        int ret = 0, rw_level = -1, have_alloc_sem = 0, lock_level = 0;
        struct file *filp = iocb->ki_filp;
@@ -2561,7 +2470,8 @@ static ssize_t ocfs2_file_aio_read(struct kiocb *iocb,
        trace_ocfs2_file_aio_read(inode, filp, filp->f_path.dentry,
                        (unsigned long long)OCFS2_I(inode)->ip_blkno,
                        filp->f_path.dentry->d_name.len,
-                       filp->f_path.dentry->d_name.name, nr_segs);
+                       filp->f_path.dentry->d_name.name,
+                       to->nr_segs);   /* GRRRRR */
 
 
        if (!inode) {
@@ -2606,13 +2516,13 @@ static ssize_t ocfs2_file_aio_read(struct kiocb *iocb,
        }
        ocfs2_inode_unlock(inode, lock_level);
 
-       ret = generic_file_aio_read(iocb, iov, nr_segs, iocb->ki_pos);
+       ret = generic_file_read_iter(iocb, to);
        trace_generic_file_aio_read_ret(ret);
 
        /* buffered aio wouldn't have proper lock coverage today */
        BUG_ON(ret == -EIOCBQUEUED && !(filp->f_flags & O_DIRECT));
 
-       /* see ocfs2_file_aio_write */
+       /* see ocfs2_file_write_iter */
        if (ret == -EIOCBQUEUED || !ocfs2_iocb_is_rw_locked(iocb)) {
                rw_level = -1;
                have_alloc_sem = 0;
@@ -2705,14 +2615,14 @@ const struct inode_operations ocfs2_special_file_iops = {
  */
 const struct file_operations ocfs2_fops = {
        .llseek         = ocfs2_file_llseek,
-       .read           = do_sync_read,
-       .write          = do_sync_write,
+       .read           = new_sync_read,
+       .write          = new_sync_write,
        .mmap           = ocfs2_mmap,
        .fsync          = ocfs2_sync_file,
        .release        = ocfs2_file_release,
        .open           = ocfs2_file_open,
-       .aio_read       = ocfs2_file_aio_read,
-       .aio_write      = ocfs2_file_aio_write,
+       .read_iter      = ocfs2_file_read_iter,
+       .write_iter     = ocfs2_file_write_iter,
        .unlocked_ioctl = ocfs2_ioctl,
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = ocfs2_compat_ioctl,
@@ -2720,7 +2630,7 @@ const struct file_operations ocfs2_fops = {
        .lock           = ocfs2_lock,
        .flock          = ocfs2_flock,
        .splice_read    = ocfs2_file_splice_read,
-       .splice_write   = ocfs2_file_splice_write,
+       .splice_write   = iter_file_splice_write,
        .fallocate      = ocfs2_fallocate,
 };
 
@@ -2753,21 +2663,21 @@ const struct file_operations ocfs2_dops = {
  */
 const struct file_operations ocfs2_fops_no_plocks = {
        .llseek         = ocfs2_file_llseek,
-       .read           = do_sync_read,
-       .write          = do_sync_write,
+       .read           = new_sync_read,
+       .write          = new_sync_write,
        .mmap           = ocfs2_mmap,
        .fsync          = ocfs2_sync_file,
        .release        = ocfs2_file_release,
        .open           = ocfs2_file_open,
-       .aio_read       = ocfs2_file_aio_read,
-       .aio_write      = ocfs2_file_aio_write,
+       .read_iter      = ocfs2_file_read_iter,
+       .write_iter     = ocfs2_file_write_iter,
        .unlocked_ioctl = ocfs2_ioctl,
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = ocfs2_compat_ioctl,
 #endif
        .flock          = ocfs2_flock,
        .splice_read    = ocfs2_file_splice_read,
-       .splice_write   = ocfs2_file_splice_write,
+       .splice_write   = iter_file_splice_write,
        .fallocate      = ocfs2_fallocate,
 };
 
index 54d57d6ba68dd5b91df6cbc9269e1ccf3c05a950..902e88527fcec443244bd12a9a25e1cf895ba763 100644 (file)
@@ -337,10 +337,10 @@ static sector_t omfs_bmap(struct address_space *mapping, sector_t block)
 
 const struct file_operations omfs_file_operations = {
        .llseek = generic_file_llseek,
-       .read = do_sync_read,
-       .write = do_sync_write,
-       .aio_read = generic_file_aio_read,
-       .aio_write = generic_file_aio_write,
+       .read = new_sync_read,
+       .write = new_sync_write,
+       .read_iter = generic_file_read_iter,
+       .write_iter = generic_file_write_iter,
        .mmap = generic_file_mmap,
        .fsync = generic_file_fsync,
        .splice_read = generic_file_splice_read,
index 9d64679cec73b00fc4685e23d69374ca122fed09..36662d0362379698fcb5764fbb142337732b3919 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -725,6 +725,12 @@ static int do_dentry_open(struct file *f,
        }
        if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
                i_readcount_inc(inode);
+       if ((f->f_mode & FMODE_READ) &&
+            likely(f->f_op->read || f->f_op->aio_read || f->f_op->read_iter))
+               f->f_mode |= FMODE_CAN_READ;
+       if ((f->f_mode & FMODE_WRITE) &&
+            likely(f->f_op->write || f->f_op->aio_write || f->f_op->write_iter))
+               f->f_mode |= FMODE_CAN_WRITE;
 
        f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
 
index 034bffac3f9724c6121f4635ba9740d61e106d06..21981e58e2a634c09b9ebb9b327860d849fb6b53 100644 (file)
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -116,50 +116,6 @@ void pipe_wait(struct pipe_inode_info *pipe)
        pipe_lock(pipe);
 }
 
-static int
-pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len,
-                       int atomic)
-{
-       unsigned long copy;
-
-       while (len > 0) {
-               while (!iov->iov_len)
-                       iov++;
-               copy = min_t(unsigned long, len, iov->iov_len);
-
-               if (atomic) {
-                       if (__copy_from_user_inatomic(to, iov->iov_base, copy))
-                               return -EFAULT;
-               } else {
-                       if (copy_from_user(to, iov->iov_base, copy))
-                               return -EFAULT;
-               }
-               to += copy;
-               len -= copy;
-               iov->iov_base += copy;
-               iov->iov_len -= copy;
-       }
-       return 0;
-}
-
-/*
- * Pre-fault in the user memory, so we can use atomic copies.
- */
-static void iov_fault_in_pages_read(struct iovec *iov, unsigned long len)
-{
-       while (!iov->iov_len)
-               iov++;
-
-       while (len > 0) {
-               unsigned long this_len;
-
-               this_len = min_t(unsigned long, len, iov->iov_len);
-               fault_in_pages_readable(iov->iov_base, this_len);
-               len -= this_len;
-               iov++;
-       }
-}
-
 static void anon_pipe_buf_release(struct pipe_inode_info *pipe,
                                  struct pipe_buffer *buf)
 {
@@ -271,24 +227,18 @@ static const struct pipe_buf_operations packet_pipe_buf_ops = {
 };
 
 static ssize_t
-pipe_read(struct kiocb *iocb, const struct iovec *_iov,
-          unsigned long nr_segs, loff_t pos)
+pipe_read(struct kiocb *iocb, struct iov_iter *to)
 {
+       size_t total_len = iov_iter_count(to);
        struct file *filp = iocb->ki_filp;
        struct pipe_inode_info *pipe = filp->private_data;
        int do_wakeup;
        ssize_t ret;
-       struct iovec *iov = (struct iovec *)_iov;
-       size_t total_len;
-       struct iov_iter iter;
 
-       total_len = iov_length(iov, nr_segs);
        /* Null read succeeds. */
        if (unlikely(total_len == 0))
                return 0;
 
-       iov_iter_init(&iter, iov, nr_segs, total_len, 0);
-
        do_wakeup = 0;
        ret = 0;
        __pipe_lock(pipe);
@@ -312,7 +262,7 @@ pipe_read(struct kiocb *iocb, const struct iovec *_iov,
                                break;
                        }
 
-                       written = copy_page_to_iter(buf->page, buf->offset, chars, &iter);
+                       written = copy_page_to_iter(buf->page, buf->offset, chars, to);
                        if (unlikely(written < chars)) {
                                if (!ret)
                                        ret = -EFAULT;
@@ -386,24 +336,19 @@ static inline int is_packetized(struct file *file)
 }
 
 static ssize_t
-pipe_write(struct kiocb *iocb, const struct iovec *_iov,
-           unsigned long nr_segs, loff_t ppos)
+pipe_write(struct kiocb *iocb, struct iov_iter *from)
 {
        struct file *filp = iocb->ki_filp;
        struct pipe_inode_info *pipe = filp->private_data;
-       ssize_t ret;
-       int do_wakeup;
-       struct iovec *iov = (struct iovec *)_iov;
-       size_t total_len;
+       ssize_t ret = 0;
+       int do_wakeup = 0;
+       size_t total_len = iov_iter_count(from);
        ssize_t chars;
 
-       total_len = iov_length(iov, nr_segs);
        /* Null write succeeds. */
        if (unlikely(total_len == 0))
                return 0;
 
-       do_wakeup = 0;
-       ret = 0;
        __pipe_lock(pipe);
 
        if (!pipe->readers) {
@@ -422,38 +367,19 @@ pipe_write(struct kiocb *iocb, const struct iovec *_iov,
                int offset = buf->offset + buf->len;
 
                if (ops->can_merge && offset + chars <= PAGE_SIZE) {
-                       int error, atomic = 1;
-                       void *addr;
-
-                       error = ops->confirm(pipe, buf);
+                       int error = ops->confirm(pipe, buf);
                        if (error)
                                goto out;
 
-                       iov_fault_in_pages_read(iov, chars);
-redo1:
-                       if (atomic)
-                               addr = kmap_atomic(buf->page);
-                       else
-                               addr = kmap(buf->page);
-                       error = pipe_iov_copy_from_user(offset + addr, iov,
-                                                       chars, atomic);
-                       if (atomic)
-                               kunmap_atomic(addr);
-                       else
-                               kunmap(buf->page);
-                       ret = error;
-                       do_wakeup = 1;
-                       if (error) {
-                               if (atomic) {
-                                       atomic = 0;
-                                       goto redo1;
-                               }
+                       ret = copy_page_from_iter(buf->page, offset, chars, from);
+                       if (unlikely(ret < chars)) {
+                               error = -EFAULT;
                                goto out;
                        }
+                       do_wakeup = 1;
                        buf->len += chars;
-                       total_len -= chars;
                        ret = chars;
-                       if (!total_len)
+                       if (!iov_iter_count(from))
                                goto out;
                }
        }
@@ -472,8 +398,7 @@ redo1:
                        int newbuf = (pipe->curbuf + bufs) & (pipe->buffers-1);
                        struct pipe_buffer *buf = pipe->bufs + newbuf;
                        struct page *page = pipe->tmp_page;
-                       char *src;
-                       int error, atomic = 1;
+                       int copied;
 
                        if (!page) {
                                page = alloc_page(GFP_HIGHUSER);
@@ -489,40 +414,19 @@ redo1:
                         * FIXME! Is this really true?
                         */
                        do_wakeup = 1;
-                       chars = PAGE_SIZE;
-                       if (chars > total_len)
-                               chars = total_len;
-
-                       iov_fault_in_pages_read(iov, chars);
-redo2:
-                       if (atomic)
-                               src = kmap_atomic(page);
-                       else
-                               src = kmap(page);
-
-                       error = pipe_iov_copy_from_user(src, iov, chars,
-                                                       atomic);
-                       if (atomic)
-                               kunmap_atomic(src);
-                       else
-                               kunmap(page);
-
-                       if (unlikely(error)) {
-                               if (atomic) {
-                                       atomic = 0;
-                                       goto redo2;
-                               }
+                       copied = copy_page_from_iter(page, 0, PAGE_SIZE, from);
+                       if (unlikely(copied < PAGE_SIZE && iov_iter_count(from))) {
                                if (!ret)
-                                       ret = error;
+                                       ret = -EFAULT;
                                break;
                        }
-                       ret += chars;
+                       ret += copied;
 
                        /* Insert it into the buffer array */
                        buf->page = page;
                        buf->ops = &anon_pipe_buf_ops;
                        buf->offset = 0;
-                       buf->len = chars;
+                       buf->len = copied;
                        buf->flags = 0;
                        if (is_packetized(filp)) {
                                buf->ops = &packet_pipe_buf_ops;
@@ -531,8 +435,7 @@ redo2:
                        pipe->nrbufs = ++bufs;
                        pipe->tmp_page = NULL;
 
-                       total_len -= chars;
-                       if (!total_len)
+                       if (!iov_iter_count(from))
                                break;
                }
                if (bufs < pipe->buffers)
@@ -1044,10 +947,10 @@ err:
 const struct file_operations pipefifo_fops = {
        .open           = fifo_open,
        .llseek         = no_llseek,
-       .read           = do_sync_read,
-       .aio_read       = pipe_read,
-       .write          = do_sync_write,
-       .aio_write      = pipe_write,
+       .read           = new_sync_read,
+       .read_iter      = pipe_read,
+       .write          = new_sync_write,
+       .write_iter     = pipe_write,
        .poll           = pipe_poll,
        .unlocked_ioctl = pipe_ioctl,
        .release        = pipe_release,
index 1e56a4e8cf7cd47d4886731ea063c95be9f078ee..4f56de822d2f5995b81006e0bc9783d321b1621d 100644 (file)
 #include "internal.h"
 
 const struct file_operations ramfs_file_operations = {
-       .read           = do_sync_read,
-       .aio_read       = generic_file_aio_read,
-       .write          = do_sync_write,
-       .aio_write      = generic_file_aio_write,
+       .read           = new_sync_read,
+       .read_iter      = generic_file_read_iter,
+       .write          = new_sync_write,
+       .write_iter     = generic_file_write_iter,
        .mmap           = generic_file_mmap,
        .fsync          = noop_fsync,
        .splice_read    = generic_file_splice_read,
-       .splice_write   = generic_file_splice_write,
+       .splice_write   = iter_file_splice_write,
        .llseek         = generic_file_llseek,
 };
 
index 0b3d8e4cb2fa00dd8b906d1ddc811bc17a4c8e07..dda012ad4208d3192521c80a082af5ff038f5b3b 100644 (file)
@@ -37,13 +37,13 @@ static int ramfs_nommu_mmap(struct file *file, struct vm_area_struct *vma);
 const struct file_operations ramfs_file_operations = {
        .mmap                   = ramfs_nommu_mmap,
        .get_unmapped_area      = ramfs_nommu_get_unmapped_area,
-       .read                   = do_sync_read,
-       .aio_read               = generic_file_aio_read,
-       .write                  = do_sync_write,
-       .aio_write              = generic_file_aio_write,
+       .read                   = new_sync_read,
+       .read_iter              = generic_file_read_iter,
+       .write                  = new_sync_write,
+       .write_iter             = generic_file_write_iter,
        .fsync                  = noop_fsync,
        .splice_read            = generic_file_splice_read,
-       .splice_write           = generic_file_splice_write,
+       .splice_write           = iter_file_splice_write,
        .llseek                 = generic_file_llseek,
 };
 
index 31c6efa431839e41f4b39b314cb640dd89b68ecc..009d8542a889c7d2b4b98334f0c4868d36076739 100644 (file)
 typedef ssize_t (*io_fn_t)(struct file *, char __user *, size_t, loff_t *);
 typedef ssize_t (*iov_fn_t)(struct kiocb *, const struct iovec *,
                unsigned long, loff_t);
+typedef ssize_t (*iter_fn_t)(struct kiocb *, struct iov_iter *);
 
 const struct file_operations generic_ro_fops = {
        .llseek         = generic_file_llseek,
-       .read           = do_sync_read,
-       .aio_read       = generic_file_aio_read,
+       .read           = new_sync_read,
+       .read_iter      = generic_file_read_iter,
        .mmap           = generic_file_readonly_mmap,
        .splice_read    = generic_file_splice_read,
 };
@@ -390,13 +391,34 @@ ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *pp
 
 EXPORT_SYMBOL(do_sync_read);
 
+ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
+{
+       struct iovec iov = { .iov_base = buf, .iov_len = len };
+       struct kiocb kiocb;
+       struct iov_iter iter;
+       ssize_t ret;
+
+       init_sync_kiocb(&kiocb, filp);
+       kiocb.ki_pos = *ppos;
+       kiocb.ki_nbytes = len;
+       iov_iter_init(&iter, READ, &iov, 1, len);
+
+       ret = filp->f_op->read_iter(&kiocb, &iter);
+       if (-EIOCBQUEUED == ret)
+               ret = wait_on_sync_kiocb(&kiocb);
+       *ppos = kiocb.ki_pos;
+       return ret;
+}
+
+EXPORT_SYMBOL(new_sync_read);
+
 ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
 {
        ssize_t ret;
 
        if (!(file->f_mode & FMODE_READ))
                return -EBADF;
-       if (!file->f_op->read && !file->f_op->aio_read)
+       if (!(file->f_mode & FMODE_CAN_READ))
                return -EINVAL;
        if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))
                return -EFAULT;
@@ -406,8 +428,10 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
                count = ret;
                if (file->f_op->read)
                        ret = file->f_op->read(file, buf, count, pos);
-               else
+               else if (file->f_op->aio_read)
                        ret = do_sync_read(file, buf, count, pos);
+               else
+                       ret = new_sync_read(file, buf, count, pos);
                if (ret > 0) {
                        fsnotify_access(file);
                        add_rchar(current, ret);
@@ -439,13 +463,34 @@ ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, lof
 
 EXPORT_SYMBOL(do_sync_write);
 
+ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
+{
+       struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len };
+       struct kiocb kiocb;
+       struct iov_iter iter;
+       ssize_t ret;
+
+       init_sync_kiocb(&kiocb, filp);
+       kiocb.ki_pos = *ppos;
+       kiocb.ki_nbytes = len;
+       iov_iter_init(&iter, WRITE, &iov, 1, len);
+
+       ret = filp->f_op->write_iter(&kiocb, &iter);
+       if (-EIOCBQUEUED == ret)
+               ret = wait_on_sync_kiocb(&kiocb);
+       *ppos = kiocb.ki_pos;
+       return ret;
+}
+
+EXPORT_SYMBOL(new_sync_write);
+
 ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos)
 {
        mm_segment_t old_fs;
        const char __user *p;
        ssize_t ret;
 
-       if (!file->f_op->write && !file->f_op->aio_write)
+       if (!(file->f_mode & FMODE_CAN_WRITE))
                return -EINVAL;
 
        old_fs = get_fs();
@@ -455,8 +500,10 @@ ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t
                count =  MAX_RW_COUNT;
        if (file->f_op->write)
                ret = file->f_op->write(file, p, count, pos);
-       else
+       else if (file->f_op->aio_write)
                ret = do_sync_write(file, p, count, pos);
+       else
+               ret = new_sync_write(file, p, count, pos);
        set_fs(old_fs);
        if (ret > 0) {
                fsnotify_modify(file);
@@ -472,7 +519,7 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_
 
        if (!(file->f_mode & FMODE_WRITE))
                return -EBADF;
-       if (!file->f_op->write && !file->f_op->aio_write)
+       if (!(file->f_mode & FMODE_CAN_WRITE))
                return -EINVAL;
        if (unlikely(!access_ok(VERIFY_READ, buf, count)))
                return -EFAULT;
@@ -483,8 +530,10 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_
                file_start_write(file);
                if (file->f_op->write)
                        ret = file->f_op->write(file, buf, count, pos);
-               else
+               else if (file->f_op->aio_write)
                        ret = do_sync_write(file, buf, count, pos);
+               else
+                       ret = new_sync_write(file, buf, count, pos);
                if (ret > 0) {
                        fsnotify_modify(file);
                        add_wchar(current, ret);
@@ -601,6 +650,25 @@ unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to)
 }
 EXPORT_SYMBOL(iov_shorten);
 
+static ssize_t do_iter_readv_writev(struct file *filp, int rw, const struct iovec *iov,
+               unsigned long nr_segs, size_t len, loff_t *ppos, iter_fn_t fn)
+{
+       struct kiocb kiocb;
+       struct iov_iter iter;
+       ssize_t ret;
+
+       init_sync_kiocb(&kiocb, filp);
+       kiocb.ki_pos = *ppos;
+       kiocb.ki_nbytes = len;
+
+       iov_iter_init(&iter, rw, iov, nr_segs, len);
+       ret = fn(&kiocb, &iter);
+       if (ret == -EIOCBQUEUED)
+               ret = wait_on_sync_kiocb(&kiocb);
+       *ppos = kiocb.ki_pos;
+       return ret;
+}
+
 static ssize_t do_sync_readv_writev(struct file *filp, const struct iovec *iov,
                unsigned long nr_segs, size_t len, loff_t *ppos, iov_fn_t fn)
 {
@@ -738,6 +806,7 @@ static ssize_t do_readv_writev(int type, struct file *file,
        ssize_t ret;
        io_fn_t fn;
        iov_fn_t fnv;
+       iter_fn_t iter_fn;
 
        ret = rw_copy_check_uvector(type, uvector, nr_segs,
                                    ARRAY_SIZE(iovstack), iovstack, &iov);
@@ -753,13 +822,18 @@ static ssize_t do_readv_writev(int type, struct file *file,
        if (type == READ) {
                fn = file->f_op->read;
                fnv = file->f_op->aio_read;
+               iter_fn = file->f_op->read_iter;
        } else {
                fn = (io_fn_t)file->f_op->write;
                fnv = file->f_op->aio_write;
+               iter_fn = file->f_op->write_iter;
                file_start_write(file);
        }
 
-       if (fnv)
+       if (iter_fn)
+               ret = do_iter_readv_writev(file, type, iov, nr_segs, tot_len,
+                                               pos, iter_fn);
+       else if (fnv)
                ret = do_sync_readv_writev(file, iov, nr_segs, tot_len,
                                                pos, fnv);
        else
@@ -785,7 +859,7 @@ ssize_t vfs_readv(struct file *file, const struct iovec __user *vec,
 {
        if (!(file->f_mode & FMODE_READ))
                return -EBADF;
-       if (!file->f_op->aio_read && !file->f_op->read)
+       if (!(file->f_mode & FMODE_CAN_READ))
                return -EINVAL;
 
        return do_readv_writev(READ, file, vec, vlen, pos);
@@ -798,7 +872,7 @@ ssize_t vfs_writev(struct file *file, const struct iovec __user *vec,
 {
        if (!(file->f_mode & FMODE_WRITE))
                return -EBADF;
-       if (!file->f_op->aio_write && !file->f_op->write)
+       if (!(file->f_mode & FMODE_CAN_WRITE))
                return -EINVAL;
 
        return do_readv_writev(WRITE, file, vec, vlen, pos);
@@ -912,6 +986,7 @@ static ssize_t compat_do_readv_writev(int type, struct file *file,
        ssize_t ret;
        io_fn_t fn;
        iov_fn_t fnv;
+       iter_fn_t iter_fn;
 
        ret = compat_rw_copy_check_uvector(type, uvector, nr_segs,
                                               UIO_FASTIOV, iovstack, &iov);
@@ -927,13 +1002,18 @@ static ssize_t compat_do_readv_writev(int type, struct file *file,
        if (type == READ) {
                fn = file->f_op->read;
                fnv = file->f_op->aio_read;
+               iter_fn = file->f_op->read_iter;
        } else {
                fn = (io_fn_t)file->f_op->write;
                fnv = file->f_op->aio_write;
+               iter_fn = file->f_op->write_iter;
                file_start_write(file);
        }
 
-       if (fnv)
+       if (iter_fn)
+               ret = do_iter_readv_writev(file, type, iov, nr_segs, tot_len,
+                                               pos, iter_fn);
+       else if (fnv)
                ret = do_sync_readv_writev(file, iov, nr_segs, tot_len,
                                                pos, fnv);
        else
@@ -964,7 +1044,7 @@ static size_t compat_readv(struct file *file,
                goto out;
 
        ret = -EINVAL;
-       if (!file->f_op->aio_read && !file->f_op->read)
+       if (!(file->f_mode & FMODE_CAN_READ))
                goto out;
 
        ret = compat_do_readv_writev(READ, file, vec, vlen, pos);
@@ -1041,7 +1121,7 @@ static size_t compat_writev(struct file *file,
                goto out;
 
        ret = -EINVAL;
-       if (!file->f_op->aio_write && !file->f_op->write)
+       if (!(file->f_mode & FMODE_CAN_WRITE))
                goto out;
 
        ret = compat_do_readv_writev(WRITE, file, vec, vlen, pos);
index 5f6c32c668b68816584f19c982c4b9a22ded751b..db9e80ba53a0db5abe4910fa128bab1e6a2ee6ad 100644 (file)
@@ -243,8 +243,8 @@ drop_write_lock:
 }
 
 const struct file_operations reiserfs_file_operations = {
-       .read = do_sync_read,
-       .write = do_sync_write,
+       .read = new_sync_read,
+       .write = new_sync_write,
        .unlocked_ioctl = reiserfs_ioctl,
 #ifdef CONFIG_COMPAT
        .compat_ioctl = reiserfs_compat_ioctl,
@@ -253,10 +253,10 @@ const struct file_operations reiserfs_file_operations = {
        .open = reiserfs_file_open,
        .release = reiserfs_file_release,
        .fsync = reiserfs_sync_file,
-       .aio_read = generic_file_aio_read,
-       .aio_write = generic_file_aio_write,
+       .read_iter = generic_file_read_iter,
+       .write_iter = generic_file_write_iter,
        .splice_read = generic_file_splice_read,
-       .splice_write = generic_file_splice_write,
+       .splice_write = iter_file_splice_write,
        .llseek = generic_file_llseek,
 };
 
index e3ca04894919c4d0a38f2623676d7ffe1ce6aff3..63b2b0ec49e6afacd955abf9f172751768ee08ee 100644 (file)
@@ -3279,15 +3279,15 @@ static int reiserfs_releasepage(struct page *page, gfp_t unused_gfp_flags)
  * to do in this section of the code.
  */
 static ssize_t reiserfs_direct_IO(int rw, struct kiocb *iocb,
-                                 const struct iovec *iov, loff_t offset,
-                                 unsigned long nr_segs)
+                                 struct iov_iter *iter, loff_t offset)
 {
        struct file *file = iocb->ki_filp;
        struct inode *inode = file->f_mapping->host;
+       size_t count = iov_iter_count(iter);
        ssize_t ret;
 
-       ret = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs,
-                                 reiserfs_get_blocks_direct_io);
+       ret = blockdev_direct_IO(rw, iocb, inode, iter, offset,
+                                reiserfs_get_blocks_direct_io);
 
        /*
         * In case of error extending write may have instantiated a few
@@ -3295,7 +3295,7 @@ static ssize_t reiserfs_direct_IO(int rw, struct kiocb *iocb,
         */
        if (unlikely((rw & WRITE) && ret < 0)) {
                loff_t isize = i_size_read(inode);
-               loff_t end = offset + iov_length(iov, nr_segs);
+               loff_t end = offset + count;
 
                if ((end > isize) && inode_newsize_ok(inode, isize) == 0) {
                        truncate_setsize(inode, isize);
index f373bde8f545da481ba0a7caa873271dd599b30d..ea06c7554860d7ada89db9ba0a8838e17b40d3fc 100644 (file)
@@ -72,8 +72,8 @@ static int romfs_mmap(struct file *file, struct vm_area_struct *vma)
 
 const struct file_operations romfs_ro_fops = {
        .llseek                 = generic_file_llseek,
-       .read                   = do_sync_read,
-       .aio_read               = generic_file_aio_read,
+       .read                   = new_sync_read,
+       .read_iter              = generic_file_read_iter,
        .splice_read            = generic_file_splice_read,
        .mmap                   = romfs_mmap,
        .get_unmapped_area      = romfs_get_unmapped_area,
index e246954ea48cb486b1c8101e6f621364d6844535..f5cb9ba84510fe5632a62af0bbf3843a45eeba23 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/gfp.h>
 #include <linux/socket.h>
 #include <linux/compat.h>
+#include <linux/aio.h>
 #include "internal.h"
 
 /*
@@ -717,63 +718,6 @@ static int pipe_to_sendpage(struct pipe_inode_info *pipe,
                                    sd->len, &pos, more);
 }
 
-/*
- * This is a little more tricky than the file -> pipe splicing. There are
- * basically three cases:
- *
- *     - Destination page already exists in the address space and there
- *       are users of it. For that case we have no other option that
- *       copying the data. Tough luck.
- *     - Destination page already exists in the address space, but there
- *       are no users of it. Make sure it's uptodate, then drop it. Fall
- *       through to last case.
- *     - Destination page does not exist, we can add the pipe page to
- *       the page cache and avoid the copy.
- *
- * If asked to move pages to the output file (SPLICE_F_MOVE is set in
- * sd->flags), we attempt to migrate pages from the pipe to the output
- * file address space page cache. This is possible if no one else has
- * the pipe page referenced outside of the pipe and page cache. If
- * SPLICE_F_MOVE isn't set, or we cannot move the page, we simply create
- * a new page in the output file page cache and fill/dirty that.
- */
-int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
-                struct splice_desc *sd)
-{
-       struct file *file = sd->u.file;
-       struct address_space *mapping = file->f_mapping;
-       unsigned int offset, this_len;
-       struct page *page;
-       void *fsdata;
-       int ret;
-
-       offset = sd->pos & ~PAGE_CACHE_MASK;
-
-       this_len = sd->len;
-       if (this_len + offset > PAGE_CACHE_SIZE)
-               this_len = PAGE_CACHE_SIZE - offset;
-
-       ret = pagecache_write_begin(file, mapping, sd->pos, this_len,
-                               AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata);
-       if (unlikely(ret))
-               goto out;
-
-       if (buf->page != page) {
-               char *src = kmap_atomic(buf->page);
-               char *dst = kmap_atomic(page);
-
-               memcpy(dst + offset, src + buf->offset, this_len);
-               flush_dcache_page(page);
-               kunmap_atomic(dst);
-               kunmap_atomic(src);
-       }
-       ret = pagecache_write_end(file, mapping, sd->pos, this_len, this_len,
-                               page, fsdata);
-out:
-       return ret;
-}
-EXPORT_SYMBOL(pipe_to_file);
-
 static void wakeup_pipe_writers(struct pipe_inode_info *pipe)
 {
        smp_mb();
@@ -802,7 +746,7 @@ static void wakeup_pipe_writers(struct pipe_inode_info *pipe)
  *    locking is required around copying the pipe buffers to the
  *    destination.
  */
-int splice_from_pipe_feed(struct pipe_inode_info *pipe, struct splice_desc *sd,
+static int splice_from_pipe_feed(struct pipe_inode_info *pipe, struct splice_desc *sd,
                          splice_actor *actor)
 {
        int ret;
@@ -849,7 +793,6 @@ int splice_from_pipe_feed(struct pipe_inode_info *pipe, struct splice_desc *sd,
 
        return 1;
 }
-EXPORT_SYMBOL(splice_from_pipe_feed);
 
 /**
  * splice_from_pipe_next - wait for some data to splice from
@@ -861,7 +804,7 @@ EXPORT_SYMBOL(splice_from_pipe_feed);
  *    value (one) if pipe buffers are available.  It will return zero
  *    or -errno if no more data needs to be spliced.
  */
-int splice_from_pipe_next(struct pipe_inode_info *pipe, struct splice_desc *sd)
+static int splice_from_pipe_next(struct pipe_inode_info *pipe, struct splice_desc *sd)
 {
        while (!pipe->nrbufs) {
                if (!pipe->writers)
@@ -886,7 +829,6 @@ int splice_from_pipe_next(struct pipe_inode_info *pipe, struct splice_desc *sd)
 
        return 1;
 }
-EXPORT_SYMBOL(splice_from_pipe_next);
 
 /**
  * splice_from_pipe_begin - start splicing from pipe
@@ -897,12 +839,11 @@ EXPORT_SYMBOL(splice_from_pipe_next);
  *    splice_from_pipe_next() and splice_from_pipe_feed() to
  *    initialize the necessary fields of @sd.
  */
-void splice_from_pipe_begin(struct splice_desc *sd)
+static void splice_from_pipe_begin(struct splice_desc *sd)
 {
        sd->num_spliced = 0;
        sd->need_wakeup = false;
 }
-EXPORT_SYMBOL(splice_from_pipe_begin);
 
 /**
  * splice_from_pipe_end - finish splicing from pipe
@@ -914,12 +855,11 @@ EXPORT_SYMBOL(splice_from_pipe_begin);
  *    be called after a loop containing splice_from_pipe_next() and
  *    splice_from_pipe_feed().
  */
-void splice_from_pipe_end(struct pipe_inode_info *pipe, struct splice_desc *sd)
+static void splice_from_pipe_end(struct pipe_inode_info *pipe, struct splice_desc *sd)
 {
        if (sd->need_wakeup)
                wakeup_pipe_writers(pipe);
 }
-EXPORT_SYMBOL(splice_from_pipe_end);
 
 /**
  * __splice_from_pipe - splice data from a pipe to given actor
@@ -985,7 +925,7 @@ ssize_t splice_from_pipe(struct pipe_inode_info *pipe, struct file *out,
 }
 
 /**
- * generic_file_splice_write - splice data from a pipe to a file
+ * iter_file_splice_write - splice data from a pipe to a file
  * @pipe:      pipe info
  * @out:       file to write to
  * @ppos:      position in @out
@@ -995,40 +935,122 @@ ssize_t splice_from_pipe(struct pipe_inode_info *pipe, struct file *out,
  * Description:
  *    Will either move or copy pages (determined by @flags options) from
  *    the given pipe inode to the given file.
+ *    This one is ->write_iter-based.
  *
  */
 ssize_t
-generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
+iter_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
                          loff_t *ppos, size_t len, unsigned int flags)
 {
-       struct address_space *mapping = out->f_mapping;
-       struct inode *inode = mapping->host;
        struct splice_desc sd = {
                .total_len = len,
                .flags = flags,
                .pos = *ppos,
                .u.file = out,
        };
+       int nbufs = pipe->buffers;
+       struct bio_vec *array = kcalloc(nbufs, sizeof(struct bio_vec),
+                                       GFP_KERNEL);
        ssize_t ret;
 
+       if (unlikely(!array))
+               return -ENOMEM;
+
        pipe_lock(pipe);
 
        splice_from_pipe_begin(&sd);
-       do {
+       while (sd.total_len) {
+               struct iov_iter from;
+               struct kiocb kiocb;
+               size_t left;
+               int n, idx;
+
                ret = splice_from_pipe_next(pipe, &sd);
                if (ret <= 0)
                        break;
 
-               mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
-               ret = file_remove_suid(out);
-               if (!ret) {
-                       ret = file_update_time(out);
-                       if (!ret)
-                               ret = splice_from_pipe_feed(pipe, &sd,
-                                                           pipe_to_file);
+               if (unlikely(nbufs < pipe->buffers)) {
+                       kfree(array);
+                       nbufs = pipe->buffers;
+                       array = kcalloc(nbufs, sizeof(struct bio_vec),
+                                       GFP_KERNEL);
+                       if (!array) {
+                               ret = -ENOMEM;
+                               break;
+                       }
                }
-               mutex_unlock(&inode->i_mutex);
-       } while (ret > 0);
+
+               /* build the vector */
+               left = sd.total_len;
+               for (n = 0, idx = pipe->curbuf; left && n < pipe->nrbufs; n++, idx++) {
+                       struct pipe_buffer *buf = pipe->bufs + idx;
+                       size_t this_len = buf->len;
+
+                       if (this_len > left)
+                               this_len = left;
+
+                       if (idx == pipe->buffers - 1)
+                               idx = -1;
+
+                       ret = buf->ops->confirm(pipe, buf);
+                       if (unlikely(ret)) {
+                               if (ret == -ENODATA)
+                                       ret = 0;
+                               goto done;
+                       }
+
+                       array[n].bv_page = buf->page;
+                       array[n].bv_len = this_len;
+                       array[n].bv_offset = buf->offset;
+                       left -= this_len;
+               }
+
+               /* ... iov_iter */
+               from.type = ITER_BVEC | WRITE;
+               from.bvec = array;
+               from.nr_segs = n;
+               from.count = sd.total_len - left;
+               from.iov_offset = 0;
+
+               /* ... and iocb */
+               init_sync_kiocb(&kiocb, out);
+               kiocb.ki_pos = sd.pos;
+               kiocb.ki_nbytes = sd.total_len - left;
+
+               /* now, send it */
+               ret = out->f_op->write_iter(&kiocb, &from);
+               if (-EIOCBQUEUED == ret)
+                       ret = wait_on_sync_kiocb(&kiocb);
+
+               if (ret <= 0)
+                       break;
+
+               sd.num_spliced += ret;
+               sd.total_len -= ret;
+               *ppos = sd.pos = kiocb.ki_pos;
+
+               /* dismiss the fully eaten buffers, adjust the partial one */
+               while (ret) {
+                       struct pipe_buffer *buf = pipe->bufs + pipe->curbuf;
+                       if (ret >= buf->len) {
+                               const struct pipe_buf_operations *ops = buf->ops;
+                               ret -= buf->len;
+                               buf->len = 0;
+                               buf->ops = NULL;
+                               ops->release(pipe, buf);
+                               pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1);
+                               pipe->nrbufs--;
+                               if (pipe->files)
+                                       sd.need_wakeup = true;
+                       } else {
+                               buf->offset += ret;
+                               buf->len -= ret;
+                               ret = 0;
+                       }
+               }
+       }
+done:
+       kfree(array);
        splice_from_pipe_end(pipe, &sd);
 
        pipe_unlock(pipe);
@@ -1036,21 +1058,10 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
        if (sd.num_spliced)
                ret = sd.num_spliced;
 
-       if (ret > 0) {
-               int err;
-
-               err = generic_write_sync(out, *ppos, ret);
-               if (err)
-                       ret = err;
-               else
-                       *ppos += ret;
-               balance_dirty_pages_ratelimited(mapping);
-       }
-
        return ret;
 }
 
-EXPORT_SYMBOL(generic_file_splice_write);
+EXPORT_SYMBOL(iter_file_splice_write);
 
 static int write_pipe_buf(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
                          struct splice_desc *sd)
@@ -1549,7 +1560,7 @@ static long vmsplice_to_user(struct file *file, const struct iovec __user *uiov,
                goto out;
 
        count = ret;
-       iov_iter_init(&iter, iov, nr_segs, count, 0);
+       iov_iter_init(&iter, READ, iov, nr_segs, count);
 
        sd.len = 0;
        sd.total_len = count;
index 9d4dc6831792a23270148c2d26b0423f656b505c..b00811c75b24f63acb1651991f6972f4b0b6ff54 100644 (file)
  */
 const struct file_operations sysv_file_operations = {
        .llseek         = generic_file_llseek,
-       .read           = do_sync_read,
-       .aio_read       = generic_file_aio_read,
-       .write          = do_sync_write,
-       .aio_write      = generic_file_aio_write,
+       .read           = new_sync_read,
+       .read_iter      = generic_file_read_iter,
+       .write          = new_sync_write,
+       .write_iter     = generic_file_write_iter,
        .mmap           = generic_file_mmap,
        .fsync          = generic_file_fsync,
        .splice_read    = generic_file_splice_read,
index 0ab7f7dfb98b632818a9b1dde1e74f4799633b8b..b5b593c4527005ba50fe0745f2651095dba79331 100644 (file)
@@ -1364,17 +1364,17 @@ static inline int mctime_update_needed(const struct inode *inode,
 
 /**
  * update_ctime - update mtime and ctime of an inode.
- * @c: UBIFS file-system description object
  * @inode: inode to update
  *
  * This function updates mtime and ctime of the inode if it is not equivalent to
  * current time. Returns zero in case of success and a negative error code in
  * case of failure.
  */
-static int update_mctime(struct ubifs_info *c, struct inode *inode)
+static int update_mctime(struct inode *inode)
 {
        struct timespec now = ubifs_current_time(inode);
        struct ubifs_inode *ui = ubifs_inode(inode);
+       struct ubifs_info *c = inode->i_sb->s_fs_info;
 
        if (mctime_update_needed(inode, &now)) {
                int err, release;
@@ -1397,18 +1397,13 @@ static int update_mctime(struct ubifs_info *c, struct inode *inode)
        return 0;
 }
 
-static ssize_t ubifs_aio_write(struct kiocb *iocb, const struct iovec *iov,
-                              unsigned long nr_segs, loff_t pos)
+static ssize_t ubifs_write_iter(struct kiocb *iocb, struct iov_iter *from)
 {
-       int err;
-       struct inode *inode = iocb->ki_filp->f_mapping->host;
-       struct ubifs_info *c = inode->i_sb->s_fs_info;
-
-       err = update_mctime(c, inode);
+       int err = update_mctime(file_inode(iocb->ki_filp));
        if (err)
                return err;
 
-       return generic_file_aio_write(iocb, iov, nr_segs, pos);
+       return generic_file_write_iter(iocb, from);
 }
 
 static int ubifs_set_page_dirty(struct page *page)
@@ -1582,15 +1577,15 @@ const struct inode_operations ubifs_symlink_inode_operations = {
 
 const struct file_operations ubifs_file_operations = {
        .llseek         = generic_file_llseek,
-       .read           = do_sync_read,
-       .write          = do_sync_write,
-       .aio_read       = generic_file_aio_read,
-       .aio_write      = ubifs_aio_write,
+       .read           = new_sync_read,
+       .write          = new_sync_write,
+       .read_iter      = generic_file_read_iter,
+       .write_iter     = ubifs_write_iter,
        .mmap           = ubifs_file_mmap,
        .fsync          = ubifs_fsync,
        .unlocked_ioctl = ubifs_ioctl,
        .splice_read    = generic_file_splice_read,
-       .splice_write   = generic_file_splice_write,
+       .splice_write   = iter_file_splice_write,
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = ubifs_compat_ioctl,
 #endif
index d2c170f8b035a4b21ef6eac2274e74b137346d56..d80738fdf424cd61579a0544eab720bb8d7b0a64 100644 (file)
@@ -119,8 +119,8 @@ static int udf_adinicb_write_end(struct file *file,
 }
 
 static ssize_t udf_adinicb_direct_IO(int rw, struct kiocb *iocb,
-                                    const struct iovec *iov,
-                                    loff_t offset, unsigned long nr_segs)
+                                    struct iov_iter *iter,
+                                    loff_t offset)
 {
        /* Fallback to buffered I/O. */
        return 0;
@@ -134,8 +134,7 @@ const struct address_space_operations udf_adinicb_aops = {
        .direct_IO      = udf_adinicb_direct_IO,
 };
 
-static ssize_t udf_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
-                                 unsigned long nr_segs, loff_t ppos)
+static ssize_t udf_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
 {
        ssize_t retval;
        struct file *file = iocb->ki_filp;
@@ -150,7 +149,7 @@ static ssize_t udf_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
                if (file->f_flags & O_APPEND)
                        pos = inode->i_size;
                else
-                       pos = ppos;
+                       pos = iocb->ki_pos;
 
                if (inode->i_sb->s_blocksize <
                                (udf_file_entry_alloc_offset(inode) +
@@ -171,7 +170,7 @@ static ssize_t udf_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
        } else
                up_write(&iinfo->i_data_sem);
 
-       retval = __generic_file_aio_write(iocb, iov, nr_segs);
+       retval = __generic_file_write_iter(iocb, from);
        mutex_unlock(&inode->i_mutex);
 
        if (retval > 0) {
@@ -252,13 +251,13 @@ static int udf_release_file(struct inode *inode, struct file *filp)
 }
 
 const struct file_operations udf_file_operations = {
-       .read                   = do_sync_read,
-       .aio_read               = generic_file_aio_read,
+       .read                   = new_sync_read,
+       .read_iter              = generic_file_read_iter,
        .unlocked_ioctl         = udf_ioctl,
        .open                   = generic_file_open,
        .mmap                   = generic_file_mmap,
-       .write                  = do_sync_write,
-       .aio_write              = udf_file_aio_write,
+       .write                  = new_sync_write,
+       .write_iter             = udf_file_write_iter,
        .release                = udf_release_file,
        .fsync                  = generic_file_fsync,
        .splice_read            = generic_file_splice_read,
index 5d643706212f411a63b0804d4c896941aecb13d8..236cd48184c2df20e75bc9fee098ded782f2560f 100644 (file)
@@ -217,18 +217,18 @@ static int udf_write_begin(struct file *file, struct address_space *mapping,
 }
 
 static ssize_t udf_direct_IO(int rw, struct kiocb *iocb,
-                            const struct iovec *iov,
-                            loff_t offset, unsigned long nr_segs)
+                            struct iov_iter *iter,
+                            loff_t offset)
 {
        struct file *file = iocb->ki_filp;
        struct address_space *mapping = file->f_mapping;
        struct inode *inode = mapping->host;
+       size_t count = iov_iter_count(iter);
        ssize_t ret;
 
-       ret = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs,
-                                 udf_get_block);
+       ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, udf_get_block);
        if (unlikely(ret < 0 && (rw & WRITE)))
-               udf_write_failed(mapping, offset + iov_length(iov, nr_segs));
+               udf_write_failed(mapping, offset + count);
        return ret;
 }
 
index 33afa20d450982eafb4e1bcc77193cce152270d1..c84ec010a6761ff683e732973bd8780d989e6531 100644 (file)
  
 const struct file_operations ufs_file_operations = {
        .llseek         = generic_file_llseek,
-       .read           = do_sync_read,
-       .aio_read       = generic_file_aio_read,
-       .write          = do_sync_write,
-       .aio_write      = generic_file_aio_write,
+       .read           = new_sync_read,
+       .read_iter      = generic_file_read_iter,
+       .write          = new_sync_write,
+       .write_iter     = generic_file_write_iter,
        .mmap           = generic_file_mmap,
        .open           = generic_file_open,
        .fsync          = generic_file_fsync,
index e32640eedea6430759310ab7b73ce909ab3bc445..faaf716e2080ad5d41cd86dd05c1ac8f4e3e2fad 100644 (file)
@@ -1486,9 +1486,8 @@ STATIC ssize_t
 xfs_vm_direct_IO(
        int                     rw,
        struct kiocb            *iocb,
-       const struct iovec      *iov,
-       loff_t                  offset,
-       unsigned long           nr_segs)
+       struct iov_iter         *iter,
+       loff_t                  offset)
 {
        struct inode            *inode = iocb->ki_filp->f_mapping->host;
        struct block_device     *bdev = xfs_find_bdev_for_inode(inode);
@@ -1496,7 +1495,7 @@ xfs_vm_direct_IO(
        ssize_t                 ret;
 
        if (rw & WRITE) {
-               size_t size = iov_length(iov, nr_segs);
+               size_t size = iov_iter_count(iter);
 
                /*
                 * We cannot preallocate a size update transaction here as we
@@ -1508,17 +1507,15 @@ xfs_vm_direct_IO(
                if (offset + size > XFS_I(inode)->i_d.di_size)
                        ioend->io_isdirect = 1;
 
-               ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iov,
-                                           offset, nr_segs,
-                                           xfs_get_blocks_direct,
+               ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iter,
+                                           offset, xfs_get_blocks_direct,
                                            xfs_end_io_direct_write, NULL,
                                            DIO_ASYNC_EXTEND);
                if (ret != -EIOCBQUEUED && iocb->private)
                        goto out_destroy_ioend;
        } else {
-               ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iov,
-                                           offset, nr_segs,
-                                           xfs_get_blocks_direct,
+               ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iter,
+                                           offset, xfs_get_blocks_direct,
                                            NULL, NULL, 0);
        }
 
index 1b8160dc04d120326de6bf39634073b9b7d7e98f..1f66779d7a46628cf3a068dd5c08b36368fb6545 100644 (file)
@@ -229,34 +229,27 @@ xfs_file_fsync(
 }
 
 STATIC ssize_t
-xfs_file_aio_read(
+xfs_file_read_iter(
        struct kiocb            *iocb,
-       const struct iovec      *iovp,
-       unsigned long           nr_segs,
-       loff_t                  pos)
+       struct iov_iter         *to)
 {
        struct file             *file = iocb->ki_filp;
        struct inode            *inode = file->f_mapping->host;
        struct xfs_inode        *ip = XFS_I(inode);
        struct xfs_mount        *mp = ip->i_mount;
-       size_t                  size = 0;
+       size_t                  size = iov_iter_count(to);
        ssize_t                 ret = 0;
        int                     ioflags = 0;
        xfs_fsize_t             n;
+       loff_t                  pos = iocb->ki_pos;
 
        XFS_STATS_INC(xs_read_calls);
 
-       BUG_ON(iocb->ki_pos != pos);
-
        if (unlikely(file->f_flags & O_DIRECT))
                ioflags |= IO_ISDIRECT;
        if (file->f_mode & FMODE_NOCMTIME)
                ioflags |= IO_INVIS;
 
-       ret = generic_segment_checks(iovp, &nr_segs, &size, VERIFY_WRITE);
-       if (ret < 0)
-               return ret;
-
        if (unlikely(ioflags & IO_ISDIRECT)) {
                xfs_buftarg_t   *target =
                        XFS_IS_REALTIME_INODE(ip) ?
@@ -309,7 +302,7 @@ xfs_file_aio_read(
 
        trace_xfs_file_read(ip, size, pos, ioflags);
 
-       ret = generic_file_aio_read(iocb, iovp, nr_segs, pos);
+       ret = generic_file_read_iter(iocb, to);
        if (ret > 0)
                XFS_STATS_ADD(xs_read_bytes, ret);
 
@@ -349,47 +342,6 @@ xfs_file_splice_read(
        return ret;
 }
 
-/*
- * xfs_file_splice_write() does not use xfs_rw_ilock() because
- * generic_file_splice_write() takes the i_mutex itself. This, in theory,
- * couuld cause lock inversions between the aio_write path and the splice path
- * if someone is doing concurrent splice(2) based writes and write(2) based
- * writes to the same inode. The only real way to fix this is to re-implement
- * the generic code here with correct locking orders.
- */
-STATIC ssize_t
-xfs_file_splice_write(
-       struct pipe_inode_info  *pipe,
-       struct file             *outfilp,
-       loff_t                  *ppos,
-       size_t                  count,
-       unsigned int            flags)
-{
-       struct inode            *inode = outfilp->f_mapping->host;
-       struct xfs_inode        *ip = XFS_I(inode);
-       int                     ioflags = 0;
-       ssize_t                 ret;
-
-       XFS_STATS_INC(xs_write_calls);
-
-       if (outfilp->f_mode & FMODE_NOCMTIME)
-               ioflags |= IO_INVIS;
-
-       if (XFS_FORCED_SHUTDOWN(ip->i_mount))
-               return -EIO;
-
-       xfs_ilock(ip, XFS_IOLOCK_EXCL);
-
-       trace_xfs_file_splice_write(ip, count, *ppos, ioflags);
-
-       ret = generic_file_splice_write(pipe, outfilp, ppos, count, flags);
-       if (ret > 0)
-               XFS_STATS_ADD(xs_write_bytes, ret);
-
-       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
-       return ret;
-}
-
 /*
  * This routine is called to handle zeroing any space in the last block of the
  * file that is beyond the EOF.  We do this since the size is being increased
@@ -625,10 +577,7 @@ restart:
 STATIC ssize_t
 xfs_file_dio_aio_write(
        struct kiocb            *iocb,
-       const struct iovec      *iovp,
-       unsigned long           nr_segs,
-       loff_t                  pos,
-       size_t                  ocount)
+       struct iov_iter         *from)
 {
        struct file             *file = iocb->ki_filp;
        struct address_space    *mapping = file->f_mapping;
@@ -636,9 +585,10 @@ xfs_file_dio_aio_write(
        struct xfs_inode        *ip = XFS_I(inode);
        struct xfs_mount        *mp = ip->i_mount;
        ssize_t                 ret = 0;
-       size_t                  count = ocount;
        int                     unaligned_io = 0;
        int                     iolock;
+       size_t                  count = iov_iter_count(from);
+       loff_t                  pos = iocb->ki_pos;
        struct xfs_buftarg      *target = XFS_IS_REALTIME_INODE(ip) ?
                                        mp->m_rtdev_targp : mp->m_ddev_targp;
 
@@ -677,6 +627,7 @@ xfs_file_dio_aio_write(
        ret = xfs_file_aio_write_checks(file, &pos, &count, &iolock);
        if (ret)
                goto out;
+       iov_iter_truncate(from, count);
 
        if (mapping->nrpages) {
                ret = filemap_write_and_wait_range(VFS_I(ip)->i_mapping,
@@ -698,8 +649,7 @@ xfs_file_dio_aio_write(
        }
 
        trace_xfs_file_direct_write(ip, count, iocb->ki_pos, 0);
-       ret = generic_file_direct_write(iocb, iovp,
-                       &nr_segs, pos, count, ocount);
+       ret = generic_file_direct_write(iocb, from, pos);
 
 out:
        xfs_rw_iunlock(ip, iolock);
@@ -712,10 +662,7 @@ out:
 STATIC ssize_t
 xfs_file_buffered_aio_write(
        struct kiocb            *iocb,
-       const struct iovec      *iovp,
-       unsigned long           nr_segs,
-       loff_t                  pos,
-       size_t                  count)
+       struct iov_iter         *from)
 {
        struct file             *file = iocb->ki_filp;
        struct address_space    *mapping = file->f_mapping;
@@ -724,7 +671,8 @@ xfs_file_buffered_aio_write(
        ssize_t                 ret;
        int                     enospc = 0;
        int                     iolock = XFS_IOLOCK_EXCL;
-       struct iov_iter         from;
+       loff_t                  pos = iocb->ki_pos;
+       size_t                  count = iov_iter_count(from);
 
        xfs_rw_ilock(ip, iolock);
 
@@ -732,13 +680,13 @@ xfs_file_buffered_aio_write(
        if (ret)
                goto out;
 
-       iov_iter_init(&from, iovp, nr_segs, count, 0);
+       iov_iter_truncate(from, count);
        /* We can write back this queue in page reclaim */
        current->backing_dev_info = mapping->backing_dev_info;
 
 write_retry:
        trace_xfs_file_buffered_write(ip, count, iocb->ki_pos, 0);
-       ret = generic_perform_write(file, &from, pos);
+       ret = generic_perform_write(file, from, pos);
        if (likely(ret >= 0))
                iocb->ki_pos = pos + ret;
        /*
@@ -759,40 +707,29 @@ out:
 }
 
 STATIC ssize_t
-xfs_file_aio_write(
+xfs_file_write_iter(
        struct kiocb            *iocb,
-       const struct iovec      *iovp,
-       unsigned long           nr_segs,
-       loff_t                  pos)
+       struct iov_iter         *from)
 {
        struct file             *file = iocb->ki_filp;
        struct address_space    *mapping = file->f_mapping;
        struct inode            *inode = mapping->host;
        struct xfs_inode        *ip = XFS_I(inode);
        ssize_t                 ret;
-       size_t                  ocount = 0;
+       size_t                  ocount = iov_iter_count(from);
 
        XFS_STATS_INC(xs_write_calls);
 
-       BUG_ON(iocb->ki_pos != pos);
-
-       ret = generic_segment_checks(iovp, &nr_segs, &ocount, VERIFY_READ);
-       if (ret)
-               return ret;
-
        if (ocount == 0)
                return 0;
 
-       if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
-               ret = -EIO;
-               goto out;
-       }
+       if (XFS_FORCED_SHUTDOWN(ip->i_mount))
+               return -EIO;
 
        if (unlikely(file->f_flags & O_DIRECT))
-               ret = xfs_file_dio_aio_write(iocb, iovp, nr_segs, pos, ocount);
+               ret = xfs_file_dio_aio_write(iocb, from);
        else
-               ret = xfs_file_buffered_aio_write(iocb, iovp, nr_segs, pos,
-                                                 ocount);
+               ret = xfs_file_buffered_aio_write(iocb, from);
 
        if (ret > 0) {
                ssize_t err;
@@ -804,8 +741,6 @@ xfs_file_aio_write(
                if (err < 0)
                        ret = err;
        }
-
-out:
        return ret;
 }
 
@@ -1461,12 +1396,12 @@ xfs_file_llseek(
 
 const struct file_operations xfs_file_operations = {
        .llseek         = xfs_file_llseek,
-       .read           = do_sync_read,
-       .write          = do_sync_write,
-       .aio_read       = xfs_file_aio_read,
-       .aio_write      = xfs_file_aio_write,
+       .read           = new_sync_read,
+       .write          = new_sync_write,
+       .read_iter      = xfs_file_read_iter,
+       .write_iter     = xfs_file_write_iter,
        .splice_read    = xfs_file_splice_read,
-       .splice_write   = xfs_file_splice_write,
+       .splice_write   = iter_file_splice_write,
        .unlocked_ioctl = xfs_file_ioctl,
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = xfs_file_compat_ioctl,
index 6910458915cfea9133cc3c39ff56e5f7f775c065..152f82782630222321bcd234b20c0ffb0a626e34 100644 (file)
@@ -1118,7 +1118,6 @@ DEFINE_RW_EVENT(xfs_file_read);
 DEFINE_RW_EVENT(xfs_file_buffered_write);
 DEFINE_RW_EVENT(xfs_file_direct_write);
 DEFINE_RW_EVENT(xfs_file_splice_read);
-DEFINE_RW_EVENT(xfs_file_splice_write);
 
 DECLARE_EVENT_CLASS(xfs_page_class,
        TP_PROTO(struct inode *inode, struct page *page, unsigned long off,
diff --git a/include/asm-generic/qrwlock.h b/include/asm-generic/qrwlock.h
new file mode 100644 (file)
index 0000000..6383d54
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Queue read/write lock
+ *
+ * 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.
+ *
+ * (C) Copyright 2013-2014 Hewlett-Packard Development Company, L.P.
+ *
+ * Authors: Waiman Long <waiman.long@hp.com>
+ */
+#ifndef __ASM_GENERIC_QRWLOCK_H
+#define __ASM_GENERIC_QRWLOCK_H
+
+#include <linux/atomic.h>
+#include <asm/barrier.h>
+#include <asm/processor.h>
+
+#include <asm-generic/qrwlock_types.h>
+
+/*
+ * Writer states & reader shift and bias
+ */
+#define        _QW_WAITING     1               /* A writer is waiting     */
+#define        _QW_LOCKED      0xff            /* A writer holds the lock */
+#define        _QW_WMASK       0xff            /* Writer mask             */
+#define        _QR_SHIFT       8               /* Reader count shift      */
+#define _QR_BIAS       (1U << _QR_SHIFT)
+
+/*
+ * External function declarations
+ */
+extern void queue_read_lock_slowpath(struct qrwlock *lock);
+extern void queue_write_lock_slowpath(struct qrwlock *lock);
+
+/**
+ * queue_read_can_lock- would read_trylock() succeed?
+ * @lock: Pointer to queue rwlock structure
+ */
+static inline int queue_read_can_lock(struct qrwlock *lock)
+{
+       return !(atomic_read(&lock->cnts) & _QW_WMASK);
+}
+
+/**
+ * queue_write_can_lock- would write_trylock() succeed?
+ * @lock: Pointer to queue rwlock structure
+ */
+static inline int queue_write_can_lock(struct qrwlock *lock)
+{
+       return !atomic_read(&lock->cnts);
+}
+
+/**
+ * queue_read_trylock - try to acquire read lock of a queue rwlock
+ * @lock : Pointer to queue rwlock structure
+ * Return: 1 if lock acquired, 0 if failed
+ */
+static inline int queue_read_trylock(struct qrwlock *lock)
+{
+       u32 cnts;
+
+       cnts = atomic_read(&lock->cnts);
+       if (likely(!(cnts & _QW_WMASK))) {
+               cnts = (u32)atomic_add_return(_QR_BIAS, &lock->cnts);
+               if (likely(!(cnts & _QW_WMASK)))
+                       return 1;
+               atomic_sub(_QR_BIAS, &lock->cnts);
+       }
+       return 0;
+}
+
+/**
+ * queue_write_trylock - try to acquire write lock of a queue rwlock
+ * @lock : Pointer to queue rwlock structure
+ * Return: 1 if lock acquired, 0 if failed
+ */
+static inline int queue_write_trylock(struct qrwlock *lock)
+{
+       u32 cnts;
+
+       cnts = atomic_read(&lock->cnts);
+       if (unlikely(cnts))
+               return 0;
+
+       return likely(atomic_cmpxchg(&lock->cnts,
+                                    cnts, cnts | _QW_LOCKED) == cnts);
+}
+/**
+ * queue_read_lock - acquire read lock of a queue rwlock
+ * @lock: Pointer to queue rwlock structure
+ */
+static inline void queue_read_lock(struct qrwlock *lock)
+{
+       u32 cnts;
+
+       cnts = atomic_add_return(_QR_BIAS, &lock->cnts);
+       if (likely(!(cnts & _QW_WMASK)))
+               return;
+
+       /* The slowpath will decrement the reader count, if necessary. */
+       queue_read_lock_slowpath(lock);
+}
+
+/**
+ * queue_write_lock - acquire write lock of a queue rwlock
+ * @lock : Pointer to queue rwlock structure
+ */
+static inline void queue_write_lock(struct qrwlock *lock)
+{
+       /* Optimize for the unfair lock case where the fair flag is 0. */
+       if (atomic_cmpxchg(&lock->cnts, 0, _QW_LOCKED) == 0)
+               return;
+
+       queue_write_lock_slowpath(lock);
+}
+
+/**
+ * queue_read_unlock - release read lock of a queue rwlock
+ * @lock : Pointer to queue rwlock structure
+ */
+static inline void queue_read_unlock(struct qrwlock *lock)
+{
+       /*
+        * Atomically decrement the reader count
+        */
+       smp_mb__before_atomic();
+       atomic_sub(_QR_BIAS, &lock->cnts);
+}
+
+#ifndef queue_write_unlock
+/**
+ * queue_write_unlock - release write lock of a queue rwlock
+ * @lock : Pointer to queue rwlock structure
+ */
+static inline void queue_write_unlock(struct qrwlock *lock)
+{
+       /*
+        * If the writer field is atomic, it can be cleared directly.
+        * Otherwise, an atomic subtraction will be used to clear it.
+        */
+       smp_mb__before_atomic();
+       atomic_sub(_QW_LOCKED, &lock->cnts);
+}
+#endif
+
+/*
+ * Remapping rwlock architecture specific functions to the corresponding
+ * queue rwlock functions.
+ */
+#define arch_read_can_lock(l)  queue_read_can_lock(l)
+#define arch_write_can_lock(l) queue_write_can_lock(l)
+#define arch_read_lock(l)      queue_read_lock(l)
+#define arch_write_lock(l)     queue_write_lock(l)
+#define arch_read_trylock(l)   queue_read_trylock(l)
+#define arch_write_trylock(l)  queue_write_trylock(l)
+#define arch_read_unlock(l)    queue_read_unlock(l)
+#define arch_write_unlock(l)   queue_write_unlock(l)
+
+#endif /* __ASM_GENERIC_QRWLOCK_H */
diff --git a/include/asm-generic/qrwlock_types.h b/include/asm-generic/qrwlock_types.h
new file mode 100644 (file)
index 0000000..4d76f24
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef __ASM_GENERIC_QRWLOCK_TYPES_H
+#define __ASM_GENERIC_QRWLOCK_TYPES_H
+
+#include <linux/types.h>
+#include <asm/spinlock_types.h>
+
+/*
+ * The queue read/write lock data structure
+ */
+
+typedef struct qrwlock {
+       atomic_t                cnts;
+       arch_spinlock_t         lock;
+} arch_rwlock_t;
+
+#define        __ARCH_RW_LOCK_UNLOCKED {               \
+       .cnts = ATOMIC_INIT(0),                 \
+       .lock = __ARCH_SPIN_LOCK_UNLOCKED,      \
+}
+
+#endif /* __ASM_GENERIC_QRWLOCK_TYPES_H */
index d647637cd699df949b01616112878cc9b1ce1ef7..471ba48c7ae40608c540bfda459b33ae0f4bb3b7 100644 (file)
 #define BRANCH_PROFILE()
 #endif
 
+#ifdef CONFIG_KPROBES
+#define KPROBE_BLACKLIST()     . = ALIGN(8);                                 \
+                               VMLINUX_SYMBOL(__start_kprobe_blacklist) = .; \
+                               *(_kprobe_blacklist)                          \
+                               VMLINUX_SYMBOL(__stop_kprobe_blacklist) = .;
+#else
+#define KPROBE_BLACKLIST()
+#endif
+
 #ifdef CONFIG_EVENT_TRACING
 #define FTRACE_EVENTS()        . = ALIGN(8);                                   \
                        VMLINUX_SYMBOL(__start_ftrace_events) = .;      \
        *(.init.rodata)                                                 \
        FTRACE_EVENTS()                                                 \
        TRACE_SYSCALLS()                                                \
+       KPROBE_BLACKLIST()                                              \
        MEM_DISCARD(init.rodata)                                        \
        CLK_OF_TABLES()                                                 \
        RESERVEDMEM_OF_TABLES()                                         \
index a7c2a862b4f48ab286f20b3860ea8eeb442bd1a9..8af71a8e2c0056ba5edb627061910aa0661fcc99 100644 (file)
@@ -143,11 +143,6 @@ int drm_err(const char *func, const char *format, ...);
 #define DRIVER_PRIME       0x4000
 #define DRIVER_RENDER      0x8000
 
-#define DRIVER_BUS_PCI 0x1
-#define DRIVER_BUS_PLATFORM 0x2
-#define DRIVER_BUS_USB 0x3
-#define DRIVER_BUS_HOST1X 0x4
-
 /***********************************************************************/
 /** \name Begin the DRM... */
 /*@{*/
@@ -239,8 +234,6 @@ int drm_err(const char *func, const char *format, ...);
 /** \name Internal types and structures */
 /*@{*/
 
-#define DRM_ARRAY_SIZE(x) ARRAY_SIZE(x)
-
 #define DRM_IF_VERSION(maj, min) (maj << 16 | min)
 
 /**
@@ -731,13 +724,7 @@ struct drm_master {
 #define DRM_SCANOUTPOS_ACCURATE     (1 << 2)
 
 struct drm_bus {
-       int bus_type;
-       int (*get_irq)(struct drm_device *dev);
-       const char *(*get_name)(struct drm_device *dev);
        int (*set_busid)(struct drm_device *dev, struct drm_master *master);
-       int (*set_unique)(struct drm_device *dev, struct drm_master *master,
-                         struct drm_unique *unique);
-       int (*irq_by_busid)(struct drm_device *dev, struct drm_irq_busid *p);
 };
 
 /**
@@ -974,11 +961,6 @@ struct drm_driver {
        const struct drm_ioctl_desc *ioctls;
        int num_ioctls;
        const struct file_operations *fops;
-       union {
-               struct pci_driver *pci;
-               struct platform_device *platform_device;
-               struct usb_driver *usb;
-       } kdriver;
        struct drm_bus *bus;
 
        /* List of devices hanging off this driver with stealth attach. */
@@ -1040,14 +1022,17 @@ struct drm_pending_vblank_event {
 };
 
 struct drm_vblank_crtc {
+       struct drm_device *dev;         /* pointer to the drm_device */
        wait_queue_head_t queue;        /**< VBLANK wait queue */
        struct timeval time[DRM_VBLANKTIME_RBSIZE];     /**< timestamp of current count */
+       struct timer_list disable_timer;                /* delayed disable timer */
        atomic_t count;                 /**< number of VBLANK interrupts */
        atomic_t refcount;              /* number of users of vblank interruptsper crtc */
        u32 last;                       /* protected by dev->vbl_lock, used */
                                        /* for wraparound handling */
        u32 last_wait;                  /* Last vblank seqno waited per CRTC */
        unsigned int inmodeset;         /* Display driver is setting mode */
+       int crtc;                       /* crtc index */
        bool enabled;                   /* so we don't call enable more than
                                           once per disable */
 };
@@ -1058,7 +1043,6 @@ struct drm_vblank_crtc {
  */
 struct drm_device {
        struct list_head legacy_dev_list;/**< list of devices per driver for stealth attach cleanup */
-       char *devname;                  /**< For /proc/interrupts */
        int if_version;                 /**< Highest interface version set */
 
        /** \name Lifetime Management */
@@ -1072,18 +1056,19 @@ struct drm_device {
        struct drm_minor *render;               /**< Render node */
        atomic_t unplugged;                     /**< Flag whether dev is dead */
        struct inode *anon_inode;               /**< inode for private address-space */
+       char *unique;                           /**< unique name of the device */
        /*@} */
 
        /** \name Locks */
        /*@{ */
-       spinlock_t count_lock;          /**< For inuse, drm_device::open_count, drm_device::buf_use */
        struct mutex struct_mutex;      /**< For others */
        struct mutex master_mutex;      /**< For drm_minor::master and drm_file::is_master */
        /*@} */
 
        /** \name Usage Counters */
        /*@{ */
-       int open_count;                 /**< Outstanding files open */
+       int open_count;                 /**< Outstanding files open, protected by drm_global_mutex. */
+       spinlock_t buf_lock;            /**< For drm_device::buf_use and a few other things. */
        int buf_use;                    /**< Buffers in use -- cannot alloc */
        atomic_t buf_alloc;             /**< Buffer allocation in progress */
        /*@} */
@@ -1114,6 +1099,8 @@ struct drm_device {
        /** \name Context support */
        /*@{ */
        bool irq_enabled;               /**< True if irq handler is enabled */
+       int irq;
+
        __volatile__ long context_flag; /**< Context swapping flag */
        int last_context;               /**< Last current context */
        /*@} */
@@ -1134,7 +1121,6 @@ struct drm_device {
 
        spinlock_t vblank_time_lock;    /**< Protects vblank count and time updates during vblank enable/disable */
        spinlock_t vbl_lock;
-       struct timer_list vblank_disable_timer;
 
        u32 max_vblank_count;           /**< size of vblank counter register */
 
@@ -1186,11 +1172,6 @@ static __inline__ int drm_core_check_feature(struct drm_device *dev,
        return ((dev->driver->driver_features & feature) ? 1 : 0);
 }
 
-static inline int drm_dev_to_irq(struct drm_device *dev)
-{
-       return dev->driver->bus->get_irq(dev);
-}
-
 static inline void drm_device_set_unplugged(struct drm_device *dev)
 {
        smp_wmb();
@@ -1204,11 +1185,6 @@ static inline int drm_device_is_unplugged(struct drm_device *dev)
        return ret;
 }
 
-static inline bool drm_modeset_is_locked(struct drm_device *dev)
-{
-       return mutex_is_locked(&dev->mode_config.mutex);
-}
-
 static inline bool drm_is_render_client(const struct drm_file *file_priv)
 {
        return file_priv->minor->type == DRM_MINOR_RENDER;
@@ -1310,7 +1286,7 @@ extern int drm_remove_magic(struct drm_master *master, drm_magic_t magic);
 /* Cache management (drm_cache.c) */
 void drm_clflush_pages(struct page *pages[], unsigned long num_pages);
 void drm_clflush_sg(struct sg_table *st);
-void drm_clflush_virt_range(char *addr, unsigned long length);
+void drm_clflush_virt_range(void *addr, unsigned long length);
 
                                /* Locking IOCTL support (drm_lock.h) */
 extern int drm_lock(struct drm_device *dev, void *data,
@@ -1363,7 +1339,7 @@ extern void drm_core_reclaim_buffers(struct drm_device *dev,
                                /* IRQ support (drm_irq.h) */
 extern int drm_control(struct drm_device *dev, void *data,
                       struct drm_file *file_priv);
-extern int drm_irq_install(struct drm_device *dev);
+extern int drm_irq_install(struct drm_device *dev, int irq);
 extern int drm_irq_uninstall(struct drm_device *dev);
 
 extern int drm_vblank_init(struct drm_device *dev, int num_crtcs);
@@ -1377,8 +1353,14 @@ extern void drm_send_vblank_event(struct drm_device *dev, int crtc,
 extern bool drm_handle_vblank(struct drm_device *dev, int crtc);
 extern int drm_vblank_get(struct drm_device *dev, int crtc);
 extern void drm_vblank_put(struct drm_device *dev, int crtc);
+extern int drm_crtc_vblank_get(struct drm_crtc *crtc);
+extern void drm_crtc_vblank_put(struct drm_crtc *crtc);
 extern void drm_vblank_off(struct drm_device *dev, int crtc);
+extern void drm_vblank_on(struct drm_device *dev, int crtc);
+extern void drm_crtc_vblank_off(struct drm_crtc *crtc);
+extern void drm_crtc_vblank_on(struct drm_crtc *crtc);
 extern void drm_vblank_cleanup(struct drm_device *dev);
+
 extern u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc,
                                     struct timeval *tvblank, unsigned flags);
 extern int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
@@ -1522,6 +1504,9 @@ extern drm_dma_handle_t *drm_pci_alloc(struct drm_device *dev, size_t size,
                                       size_t align);
 extern void __drm_pci_free(struct drm_device *dev, drm_dma_handle_t * dmah);
 extern void drm_pci_free(struct drm_device *dev, drm_dma_handle_t * dmah);
+extern int drm_pci_set_unique(struct drm_device *dev,
+                             struct drm_master *master,
+                             struct drm_unique *u);
 
                               /* sysfs support (drm_sysfs.c) */
 struct drm_sysfs_class;
@@ -1631,6 +1616,7 @@ void drm_dev_ref(struct drm_device *dev);
 void drm_dev_unref(struct drm_device *dev);
 int drm_dev_register(struct drm_device *dev, unsigned long flags);
 void drm_dev_unregister(struct drm_device *dev);
+int drm_dev_set_unique(struct drm_device *dev, const char *fmt, ...);
 
 struct drm_minor *drm_minor_acquire(unsigned int minor_id);
 void drm_minor_release(struct drm_minor *minor);
index e55fccbe7c42373940ff8fee459d076cd5e6d4bf..251b75e6bf7a173243a9623d8a013e8606c6ae62 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/hdmi.h>
 #include <drm/drm_mode.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_modeset_lock.h>
 
 struct drm_device;
 struct drm_mode_set;
@@ -50,6 +51,7 @@ struct drm_clip_rect;
 #define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb
 #define DRM_MODE_OBJECT_PLANE 0xeeeeeeee
 #define DRM_MODE_OBJECT_BRIDGE 0xbdbdbdbd
+#define DRM_MODE_OBJECT_ANY 0
 
 struct drm_mode_object {
        uint32_t id;
@@ -64,6 +66,15 @@ struct drm_object_properties {
        uint64_t values[DRM_OBJECT_MAX_PROPERTY];
 };
 
+static inline int64_t U642I64(uint64_t val)
+{
+       return (int64_t)*((int64_t *)&val);
+}
+static inline uint64_t I642U64(int64_t val)
+{
+       return (uint64_t)*((uint64_t *)&val);
+}
+
 enum drm_connector_force {
        DRM_FORCE_UNSPECIFIED,
        DRM_FORCE_OFF,
@@ -110,6 +121,9 @@ struct drm_display_info {
        enum subpixel_order subpixel_order;
        u32 color_formats;
 
+       /* Mask of supported hdmi deep color modes */
+       u8 edid_hdmi_dc_modes;
+
        u8 cea_rev;
 };
 
@@ -190,10 +204,15 @@ struct drm_property {
        char name[DRM_PROP_NAME_LEN];
        uint32_t num_values;
        uint64_t *values;
+       struct drm_device *dev;
 
        struct list_head enum_blob_list;
 };
 
+void drm_modeset_lock_all(struct drm_device *dev);
+void drm_modeset_unlock_all(struct drm_device *dev);
+void drm_warn_on_modeset_not_all_locked(struct drm_device *dev);
+
 struct drm_crtc;
 struct drm_connector;
 struct drm_encoder;
@@ -269,6 +288,7 @@ struct drm_crtc_funcs {
  * drm_crtc - central CRTC control structure
  * @dev: parent DRM device
  * @head: list management
+ * @mutex: per-CRTC locking
  * @base: base KMS object for ID tracking etc.
  * @primary: primary plane for this CRTC
  * @cursor: cursor plane for this CRTC
@@ -303,7 +323,7 @@ struct drm_crtc {
         * state, ...) and a write lock for everything which can be update
         * without a full modeset (fb, cursor data, ...)
         */
-       struct mutex mutex;
+       struct drm_modeset_lock mutex;
 
        struct drm_mode_object base;
 
@@ -400,6 +420,7 @@ struct drm_encoder_funcs {
  * @dev: parent DRM device
  * @head: list management
  * @base: base KMS object
+ * @name: encoder name
  * @encoder_type: one of the %DRM_MODE_ENCODER_<foo> types in drm_mode.h
  * @possible_crtcs: bitmask of potential CRTC bindings
  * @possible_clones: bitmask of potential sibling encoders for cloning
@@ -416,6 +437,7 @@ struct drm_encoder {
        struct list_head head;
 
        struct drm_mode_object base;
+       char *name;
        int encoder_type;
        uint32_t possible_crtcs;
        uint32_t possible_clones;
@@ -444,6 +466,7 @@ struct drm_encoder {
  * @attr: sysfs attributes
  * @head: list management
  * @base: base KMS object
+ * @name: connector name
  * @connector_type: one of the %DRM_MODE_CONNECTOR_<foo> types from drm_mode.h
  * @connector_type_id: index into connector type enum
  * @interlace_allowed: can this connector handle interlaced modes?
@@ -482,6 +505,7 @@ struct drm_connector {
 
        struct drm_mode_object base;
 
+       char *name;
        int connector_type;
        int connector_type_id;
        bool interlace_allowed;
@@ -723,6 +747,8 @@ struct drm_mode_group {
  */
 struct drm_mode_config {
        struct mutex mutex; /* protects configuration (mode lists etc.) */
+       struct drm_modeset_lock connection_mutex; /* protects connector->encoder and encoder->crtc links */
+       struct drm_modeset_acquire_ctx *acquire_ctx; /* for legacy _lock_all() / _unlock_all() */
        struct mutex idr_mutex; /* for IDR management */
        struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, connector, modes - just makes life easier */
        /* this is limited to one for now */
@@ -823,10 +849,6 @@ struct drm_prop_enum_list {
        char *name;
 };
 
-extern void drm_modeset_lock_all(struct drm_device *dev);
-extern void drm_modeset_unlock_all(struct drm_device *dev);
-extern void drm_warn_on_modeset_not_all_locked(struct drm_device *dev);
-
 extern int drm_crtc_init_with_planes(struct drm_device *dev,
                                     struct drm_crtc *crtc,
                                     struct drm_plane *primary,
@@ -905,7 +927,6 @@ extern int drm_crtc_check_viewport(const struct drm_crtc *crtc,
 
 extern void drm_encoder_cleanup(struct drm_encoder *encoder);
 
-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_subpixel_order_name(enum subpixel_order order);
 extern const char *drm_get_dpms_name(int val);
@@ -915,6 +936,7 @@ 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 void drm_mode_group_destroy(struct drm_mode_group *group);
 extern bool drm_probe_ddc(struct i2c_adapter *adapter);
 extern struct edid *drm_get_edid(struct drm_connector *connector,
                                 struct i2c_adapter *adapter);
@@ -926,6 +948,23 @@ extern void drm_mode_config_cleanup(struct drm_device *dev);
 
 extern int drm_mode_connector_update_edid_property(struct drm_connector *connector,
                                                struct edid *edid);
+
+static inline bool drm_property_type_is(struct drm_property *property,
+               uint32_t type)
+{
+       /* instanceof for props.. handles extended type vs original types: */
+       if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
+               return (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) == type;
+       return property->flags & type;
+}
+
+static inline bool drm_property_type_valid(struct drm_property *property)
+{
+       if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
+               return !(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
+       return !!(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
+}
+
 extern int drm_object_property_set_value(struct drm_mode_object *obj,
                                         struct drm_property *property,
                                         uint64_t val);
@@ -959,6 +998,11 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
 struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
                                         const char *name,
                                         uint64_t min, uint64_t max);
+struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
+                                        int flags, const char *name,
+                                        int64_t min, int64_t max);
+struct drm_property *drm_property_create_object(struct drm_device *dev,
+                                        int flags, const char *name, uint32_t type);
 extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property);
 extern int drm_property_add_enum(struct drm_property *property, int index,
                                 uint64_t value, const char *name);
@@ -967,7 +1011,6 @@ extern int drm_mode_create_tv_properties(struct drm_device *dev, int num_formats
                                     char *formats[]);
 extern int drm_mode_create_scaling_mode_property(struct drm_device *dev);
 extern int drm_mode_create_dirty_info_property(struct drm_device *dev);
-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);
@@ -975,6 +1018,7 @@ extern int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
                                         int gamma_size);
 extern struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
                uint32_t id, uint32_t type);
+
 /* IOCTLs */
 extern int drm_mode_getresources(struct drm_device *dev,
                                 void *data, struct drm_file *file_priv);
@@ -1020,6 +1064,7 @@ extern int drm_mode_gamma_get_ioctl(struct drm_device *dev,
 extern int drm_mode_gamma_set_ioctl(struct drm_device *dev,
                                    void *data, struct drm_file *file_priv);
 extern u8 drm_match_cea_mode(const struct drm_display_mode *to_match);
+extern enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code);
 extern bool drm_detect_hdmi_monitor(struct edid *edid);
 extern bool drm_detect_monitor_audio(struct edid *edid);
 extern bool drm_rgb_quant_range_selectable(struct edid *edid);
@@ -1057,6 +1102,15 @@ extern int drm_format_vert_chroma_subsampling(uint32_t format);
 extern const char *drm_get_format_name(uint32_t format);
 
 /* Helpers */
+
+static inline struct drm_plane *drm_plane_find(struct drm_device *dev,
+               uint32_t id)
+{
+       struct drm_mode_object *mo;
+       mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PLANE);
+       return mo ? obj_to_plane(mo) : NULL;
+}
+
 static inline struct drm_crtc *drm_crtc_find(struct drm_device *dev,
        uint32_t id)
 {
@@ -1073,6 +1127,30 @@ static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev,
        return mo ? obj_to_encoder(mo) : NULL;
 }
 
+static inline struct drm_connector *drm_connector_find(struct drm_device *dev,
+               uint32_t id)
+{
+       struct drm_mode_object *mo;
+       mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_CONNECTOR);
+       return mo ? obj_to_connector(mo) : NULL;
+}
+
+static inline struct drm_property *drm_property_find(struct drm_device *dev,
+               uint32_t id)
+{
+       struct drm_mode_object *mo;
+       mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PROPERTY);
+       return mo ? obj_to_property(mo) : NULL;
+}
+
+static inline struct drm_property_blob *
+drm_property_blob_find(struct drm_device *dev, uint32_t id)
+{
+       struct drm_mode_object *mo;
+       mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_BLOB);
+       return mo ? obj_to_blob(mo) : NULL;
+}
+
 /* Plane list iterator for legacy (overlay only) planes. */
 #define drm_for_each_legacy_plane(plane, planelist) \
        list_for_each_entry(plane, planelist, head) \
index 36a5febac2a600927d6915642baf62179ff9266c..a3d75fefd01020b520be0d9b35958f9462b4aec4 100644 (file)
@@ -114,7 +114,7 @@ struct drm_encoder_helper_funcs {
 /**
  * drm_connector_helper_funcs - helper operations for connectors
  * @get_modes: get mode list for this connector
- * @mode_valid: is this mode valid on the given connector?
+ * @mode_valid (optional): is this mode valid on the given connector?
  *
  * The helper operations are called by the mid-layer CRTC helper.
  */
@@ -165,6 +165,10 @@ extern void drm_helper_resume_force_mode(struct drm_device *dev);
 extern int drm_helper_probe_single_connector_modes(struct drm_connector
                                                   *connector, uint32_t maxX,
                                                   uint32_t maxY);
+extern int drm_helper_probe_single_connector_modes_nomerge(struct drm_connector
+                                                          *connector,
+                                                          uint32_t maxX,
+                                                          uint32_t maxY);
 extern void drm_kms_helper_poll_init(struct drm_device *dev);
 extern void drm_kms_helper_poll_fini(struct drm_device *dev);
 extern bool drm_helper_hpd_irq_event(struct drm_device *dev);
index cfcacec5b89db818efb8346eb1dfd89b16fa2838..a21568bf15145940cecdc86623d9c4b79dd1111c 100644 (file)
@@ -37,6 +37,7 @@
  * eDP: Embedded DisplayPort version 1
  * DPI: DisplayPort Interoperability Guideline v1.1a
  * 1.2: DisplayPort 1.2
+ * MST: Multistream Transport - part of DP 1.2a
  *
  * 1.2 formally includes both eDP and DPI definitions.
  */
 #define DP_TRAINING_AUX_RD_INTERVAL         0x00e   /* XXX 1.2? */
 
 /* Multiple stream transport */
+#define DP_FAUX_CAP                        0x020   /* 1.2 */
+# define DP_FAUX_CAP_1                     (1 << 0)
+
 #define DP_MSTM_CAP                        0x021   /* 1.2 */
 # define DP_MST_CAP                        (1 << 0)
 
+#define DP_GUID                                    0x030   /* 1.2 */
+
 #define DP_PSR_SUPPORT                      0x070   /* XXX 1.2? */
 # define DP_PSR_IS_SUPPORTED                1
 #define DP_PSR_CAPS                         0x071   /* XXX 1.2? */
 # define DP_PSR_CRC_VERIFICATION           (1 << 2)
 # define DP_PSR_FRAME_CAPTURE              (1 << 3)
 
+#define DP_ADAPTER_CTRL                            0x1a0
+# define DP_ADAPTER_CTRL_FORCE_LOAD_SENSE   (1 << 0)
+
+#define DP_BRANCH_DEVICE_CTRL              0x1a1
+# define DP_BRANCH_DEVICE_IRQ_HPD          (1 << 0)
+
+#define DP_PAYLOAD_ALLOCATE_SET                    0x1c0
+#define DP_PAYLOAD_ALLOCATE_START_TIME_SLOT 0x1c1
+#define DP_PAYLOAD_ALLOCATE_TIME_SLOT_COUNT 0x1c2
+
 #define DP_SINK_COUNT                      0x200
 /* prior to 1.2 bit 7 was reserved mbz */
 # define DP_GET_SINK_COUNT(x)              ((((x) & 0x80) >> 1) | ((x) & 0x3f))
 # define DP_REMOTE_CONTROL_COMMAND_PENDING  (1 << 0)
 # define DP_AUTOMATED_TEST_REQUEST         (1 << 1)
 # define DP_CP_IRQ                         (1 << 2)
+# define DP_MCCS_IRQ                       (1 << 3)
+# define DP_DOWN_REP_MSG_RDY               (1 << 4) /* 1.2 MST */
+# define DP_UP_REQ_MSG_RDY                 (1 << 5) /* 1.2 MST */
 # define DP_SINK_SPECIFIC_IRQ              (1 << 6)
 
 #define DP_LANE0_1_STATUS                  0x202
 # define DP_TEST_NAK                       (1 << 1)
 # define DP_TEST_EDID_CHECKSUM_WRITE       (1 << 2)
 
+#define DP_TEST_EDID_CHECKSUM              0x261
+
 #define DP_TEST_SINK                       0x270
 #define DP_TEST_SINK_START         (1 << 0)
 
+#define DP_PAYLOAD_TABLE_UPDATE_STATUS      0x2c0   /* 1.2 MST */
+# define DP_PAYLOAD_TABLE_UPDATED           (1 << 0)
+# define DP_PAYLOAD_ACT_HANDLED             (1 << 1)
+
+#define DP_VC_PAYLOAD_ID_SLOT_1             0x2c1   /* 1.2 MST */
+/* up to ID_SLOT_63 at 0x2ff */
+
 #define DP_SOURCE_OUI                      0x300
 #define DP_SINK_OUI                        0x400
 #define DP_BRANCH_OUI                      0x500
 # define DP_SET_POWER_D3                    0x2
 # define DP_SET_POWER_MASK                  0x3
 
+#define DP_SIDEBAND_MSG_DOWN_REQ_BASE      0x1000   /* 1.2 MST */
+#define DP_SIDEBAND_MSG_UP_REP_BASE        0x1200   /* 1.2 MST */
+#define DP_SIDEBAND_MSG_DOWN_REP_BASE      0x1400   /* 1.2 MST */
+#define DP_SIDEBAND_MSG_UP_REQ_BASE        0x1600   /* 1.2 MST */
+
+#define DP_SINK_COUNT_ESI                  0x2002   /* 1.2 */
+/* 0-5 sink count */
+# define DP_SINK_COUNT_CP_READY             (1 << 6)
+
+#define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0   0x2003   /* 1.2 */
+
+#define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI1   0x2004   /* 1.2 */
+
+#define DP_LINK_SERVICE_IRQ_VECTOR_ESI0     0x2005   /* 1.2 */
+
 #define DP_PSR_ERROR_STATUS                 0x2006  /* XXX 1.2? */
 # define DP_PSR_LINK_CRC_ERROR              (1 << 0)
 # define DP_PSR_RFB_STORAGE_ERROR           (1 << 1)
 # define DP_PSR_SINK_INTERNAL_ERROR         7
 # define DP_PSR_SINK_STATE_MASK             0x07
 
+/* DP 1.2 Sideband message defines */
+/* peer device type - DP 1.2a Table 2-92 */
+#define DP_PEER_DEVICE_NONE            0x0
+#define DP_PEER_DEVICE_SOURCE_OR_SST   0x1
+#define DP_PEER_DEVICE_MST_BRANCHING   0x2
+#define DP_PEER_DEVICE_SST_SINK                0x3
+#define DP_PEER_DEVICE_DP_LEGACY_CONV  0x4
+
+/* DP 1.2 MST sideband request names DP 1.2a Table 2-80 */
+#define DP_LINK_ADDRESS                        0x01
+#define DP_CONNECTION_STATUS_NOTIFY    0x02
+#define DP_ENUM_PATH_RESOURCES         0x10
+#define DP_ALLOCATE_PAYLOAD            0x11
+#define DP_QUERY_PAYLOAD               0x12
+#define DP_RESOURCE_STATUS_NOTIFY      0x13
+#define DP_CLEAR_PAYLOAD_ID_TABLE      0x14
+#define DP_REMOTE_DPCD_READ            0x20
+#define DP_REMOTE_DPCD_WRITE           0x21
+#define DP_REMOTE_I2C_READ             0x22
+#define DP_REMOTE_I2C_WRITE            0x23
+#define DP_POWER_UP_PHY                        0x24
+#define DP_POWER_DOWN_PHY              0x25
+#define DP_SINK_EVENT_NOTIFY           0x30
+#define DP_QUERY_STREAM_ENC_STATUS     0x38
+
+/* DP 1.2 MST sideband nak reasons - table 2.84 */
+#define DP_NAK_WRITE_FAILURE           0x01
+#define DP_NAK_INVALID_READ            0x02
+#define DP_NAK_CRC_FAILURE             0x03
+#define DP_NAK_BAD_PARAM               0x04
+#define DP_NAK_DEFER                   0x05
+#define DP_NAK_LINK_FAILURE            0x06
+#define DP_NAK_NO_RESOURCES            0x07
+#define DP_NAK_DPCD_FAIL               0x08
+#define DP_NAK_I2C_NAK                 0x09
+#define DP_NAK_ALLOCATE_FAIL           0x0a
+
 #define MODE_I2C_START 1
 #define MODE_I2C_WRITE 2
 #define MODE_I2C_READ  4
@@ -431,8 +511,10 @@ struct drm_dp_aux_msg {
 
 /**
  * struct drm_dp_aux - DisplayPort AUX channel
+ * @name: user-visible name of this AUX channel and the I2C-over-AUX adapter
  * @ddc: I2C adapter that can be used for I2C-over-AUX communication
  * @dev: pointer to struct device that is the parent for this AUX channel
+ * @hw_mutex: internal mutex used for locking transfers
  * @transfer: transfers a message representing a single AUX transaction
  *
  * The .dev field should be set to a pointer to the device that implements
@@ -465,7 +547,7 @@ struct drm_dp_aux {
        const char *name;
        struct i2c_adapter ddc;
        struct device *dev;
-
+       struct mutex hw_mutex;
        ssize_t (*transfer)(struct drm_dp_aux *aux,
                            struct drm_dp_aux_msg *msg);
 };
@@ -524,7 +606,7 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link);
 int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link);
 int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link);
 
-int drm_dp_aux_register_i2c_bus(struct drm_dp_aux *aux);
-void drm_dp_aux_unregister_i2c_bus(struct drm_dp_aux *aux);
+int drm_dp_aux_register(struct drm_dp_aux *aux);
+void drm_dp_aux_unregister(struct drm_dp_aux *aux);
 
 #endif /* _DRM_DP_HELPER_H_ */
index a1441c5ac63d535faca20d9775c6e09bea6feb75..b96031d947a0c5a56eb9cb57072fc7e5d655f901 100644 (file)
@@ -202,6 +202,11 @@ struct detailed_timing {
 #define DRM_EDID_FEATURE_PM_SUSPEND       (1 << 6)
 #define DRM_EDID_FEATURE_PM_STANDBY       (1 << 7)
 
+#define DRM_EDID_HDMI_DC_48               (1 << 6)
+#define DRM_EDID_HDMI_DC_36               (1 << 5)
+#define DRM_EDID_HDMI_DC_30               (1 << 4)
+#define DRM_EDID_HDMI_DC_Y444             (1 << 3)
+
 struct edid {
        u8 header[8];
        /* Vendor & product info */
index 6e622f7d481d85b76219edab75815423b69ba47b..7997246d4039fdfc9cf0dd183484187f04624c15 100644 (file)
@@ -108,7 +108,7 @@ int drm_fb_helper_set_par(struct fb_info *info);
 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
                            struct fb_info *info);
 
-bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper);
+bool drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper);
 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
                            uint32_t fb_width, uint32_t fb_height);
 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
index 35c776ae7d3b438b9e497733426c98ae4df5dec4..9eed34dcd6afea74a8438f5571552cd472191a45 100644 (file)
@@ -57,6 +57,7 @@ typedef void (*drm_flip_func_t)(struct drm_flip_work *work, void *val);
  * @count: number of committed items
  * @func: callback fxn called for each committed item
  * @worker: worker which calls @func
+ * @fifo: queue of committed items
  */
 struct drm_flip_work {
        const char *name;
index 7209df15a3cd1e1929989a180ff6aa5bf3ec86f1..944f33f8ba38d2072b2762306a9de0ae49c74ef2 100644 (file)
@@ -135,11 +135,13 @@ ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, unsigned int channel,
  * @driver: device driver model driver
  * @probe: callback for device binding
  * @remove: callback for device unbinding
+ * @shutdown: called at shutdown time to quiesce the device
  */
 struct mipi_dsi_driver {
        struct device_driver driver;
        int(*probe)(struct mipi_dsi_device *dsi);
        int(*remove)(struct mipi_dsi_device *dsi);
+       void (*shutdown)(struct mipi_dsi_device *dsi);
 };
 
 #define to_mipi_dsi_driver(d) container_of(d, struct mipi_dsi_driver, driver)
index 2dbbf997666937fc3a4efb57519d87ccb85c59cf..91d0582f924e3e90539f0373b4daaaa2b20ae9cb 100644 (file)
@@ -223,7 +223,7 @@ void drm_mode_validate_size(struct drm_device *dev,
 void drm_mode_prune_invalid(struct drm_device *dev,
                            struct list_head *mode_list, bool verbose);
 void drm_mode_sort(struct list_head *mode_list);
-void drm_mode_connector_list_update(struct drm_connector *connector);
+void drm_mode_connector_list_update(struct drm_connector *connector, bool merge_type_bits);
 
 /* parsing cmdline modes */
 bool
diff --git a/include/drm/drm_modeset_lock.h b/include/drm/drm_modeset_lock.h
new file mode 100644 (file)
index 0000000..402aa7a
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2014 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * 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 DRM_MODESET_LOCK_H_
+#define DRM_MODESET_LOCK_H_
+
+#include <linux/ww_mutex.h>
+
+struct drm_modeset_lock;
+
+/**
+ * drm_modeset_acquire_ctx - locking context (see ww_acquire_ctx)
+ * @ww_ctx: base acquire ctx
+ * @contended: used internally for -EDEADLK handling
+ * @locked: list of held locks
+ *
+ * Each thread competing for a set of locks must use one acquire
+ * ctx.  And if any lock fxn returns -EDEADLK, it must backoff and
+ * retry.
+ */
+struct drm_modeset_acquire_ctx {
+
+       struct ww_acquire_ctx ww_ctx;
+
+       /**
+        * Contended lock: if a lock is contended you should only call
+        * drm_modeset_backoff() which drops locks and slow-locks the
+        * contended lock.
+        */
+       struct drm_modeset_lock *contended;
+
+       /**
+        * list of held locks (drm_modeset_lock)
+        */
+       struct list_head locked;
+};
+
+/**
+ * drm_modeset_lock - used for locking modeset resources.
+ * @mutex: resource locking
+ * @head: used to hold it's place on state->locked list when
+ *    part of an atomic update
+ *
+ * Used for locking CRTCs and other modeset resources.
+ */
+struct drm_modeset_lock {
+       /**
+        * modeset lock
+        */
+       struct ww_mutex mutex;
+
+       /**
+        * Resources that are locked as part of an atomic update are added
+        * to a list (so we know what to unlock at the end).
+        */
+       struct list_head head;
+};
+
+extern struct ww_class crtc_ww_class;
+
+void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
+               uint32_t flags);
+void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx);
+void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx);
+void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx);
+int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx);
+
+/**
+ * drm_modeset_lock_init - initialize lock
+ * @lock: lock to init
+ */
+static inline void drm_modeset_lock_init(struct drm_modeset_lock *lock)
+{
+       ww_mutex_init(&lock->mutex, &crtc_ww_class);
+       INIT_LIST_HEAD(&lock->head);
+}
+
+/**
+ * drm_modeset_lock_fini - cleanup lock
+ * @lock: lock to cleanup
+ */
+static inline void drm_modeset_lock_fini(struct drm_modeset_lock *lock)
+{
+       WARN_ON(!list_empty(&lock->head));
+}
+
+/**
+ * drm_modeset_is_locked - equivalent to mutex_is_locked()
+ * @lock: lock to check
+ */
+static inline bool drm_modeset_is_locked(struct drm_modeset_lock *lock)
+{
+       return ww_mutex_is_locked(&lock->mutex);
+}
+
+int drm_modeset_lock(struct drm_modeset_lock *lock,
+               struct drm_modeset_acquire_ctx *ctx);
+int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
+               struct drm_modeset_acquire_ctx *ctx);
+void drm_modeset_unlock(struct drm_modeset_lock *lock);
+
+struct drm_device;
+int drm_modeset_lock_all_crtcs(struct drm_device *dev,
+               struct drm_modeset_acquire_ctx *ctx);
+
+#endif /* DRM_MODESET_LOCK_H_ */
index 09824becee3eb332631720afa74d8a774942dbcf..52e6870534b24ad90bec35526d23a4dda690765d 100644 (file)
 #ifndef DRM_PLANE_HELPER_H
 #define DRM_PLANE_HELPER_H
 
+#include <drm/drm_rect.h>
+
+/*
+ * Drivers that don't allow primary plane scaling may pass this macro in place
+ * of the min/max scale parameters of the update checker function.
+ *
+ * Due to src being in 16.16 fixed point and dest being in integer pixels,
+ * 1<<16 represents no scaling.
+ */
+#define DRM_PLANE_HELPER_NO_SCALING (1<<16)
+
 /**
  * DOC: plane helpers
  *
  * planes.
  */
 
+extern int drm_plane_helper_check_update(struct drm_plane *plane,
+                                        struct drm_crtc *crtc,
+                                        struct drm_framebuffer *fb,
+                                        struct drm_rect *src,
+                                        struct drm_rect *dest,
+                                        const struct drm_rect *clip,
+                                        int min_scale,
+                                        int max_scale,
+                                        bool can_position,
+                                        bool can_update_disabled,
+                                        bool *visible);
 extern int drm_primary_helper_update(struct drm_plane *plane,
                                     struct drm_crtc *crtc,
                                     struct drm_framebuffer *fb,
@@ -42,7 +64,7 @@ extern int drm_primary_helper_disable(struct drm_plane *plane);
 extern void drm_primary_helper_destroy(struct drm_plane *plane);
 extern const struct drm_plane_funcs drm_primary_helper_funcs;
 extern struct drm_plane *drm_primary_helper_create_plane(struct drm_device *dev,
-                                                        uint32_t *formats,
+                                                        const uint32_t *formats,
                                                         int num_formats);
 
 
index 012d58fa8ff0ebb69812e2b863e6dee6a2ae6c01..0572035673f3bbd884ac988f78b701d6efd3ae63 100644 (file)
        _INTEL_BDW_D(gt, 0x160A, info), /* Server */ \
        _INTEL_BDW_D(gt, 0x160D, info) /* Workstation */
 
-#define INTEL_BDW_M_IDS(info) \
+#define INTEL_BDW_GT12M_IDS(info) \
        _INTEL_BDW_M_IDS(1, info), \
-       _INTEL_BDW_M_IDS(2, info), \
-       _INTEL_BDW_M_IDS(3, info)
+       _INTEL_BDW_M_IDS(2, info)
 
-#define INTEL_BDW_D_IDS(info) \
+#define INTEL_BDW_GT12D_IDS(info) \
        _INTEL_BDW_D_IDS(1, info), \
-       _INTEL_BDW_D_IDS(2, info), \
+       _INTEL_BDW_D_IDS(2, info)
+
+#define INTEL_BDW_GT3M_IDS(info) \
+       _INTEL_BDW_M_IDS(3, info)
+
+#define INTEL_BDW_GT3D_IDS(info) \
        _INTEL_BDW_D_IDS(3, info)
 
+#define INTEL_BDW_M_IDS(info) \
+       INTEL_BDW_GT12M_IDS(info), \
+       INTEL_BDW_GT3M_IDS(info)
+
+#define INTEL_BDW_D_IDS(info) \
+       INTEL_BDW_GT12D_IDS(info), \
+       INTEL_BDW_GT3D_IDS(info)
+
+#define INTEL_CHV_IDS(info) \
+       INTEL_VGA_DEVICE(0x22b0, info), \
+       INTEL_VGA_DEVICE(0x22b1, info), \
+       INTEL_VGA_DEVICE(0x22b2, info), \
+       INTEL_VGA_DEVICE(0x22b3, info)
+
 #endif /* _I915_PCIIDS_H */
index ee127ec33c608b06899ee9d69807f9918a78f2c5..7526c5bf56105e95e6e614f4ea1517f1ac6ee64d 100644 (file)
@@ -485,13 +485,12 @@ extern int ttm_bo_init(struct ttm_bo_device *bdev,
                        void (*destroy) (struct ttm_buffer_object *));
 
 /**
- * ttm_bo_synccpu_object_init
+ * ttm_bo_create
  *
  * @bdev: Pointer to a ttm_bo_device struct.
- * @bo: Pointer to a ttm_buffer_object to be initialized.
  * @size: Requested size of buffer object.
  * @type: Requested type of buffer object.
- * @flags: Initial placement flags.
+ * @placement: Initial placement.
  * @page_alignment: Data alignment in pages.
  * @interruptible: If needing to sleep while waiting for GPU resources,
  * sleep interruptible.
diff --git a/include/dt-bindings/clk/ti-dra7-atl.h b/include/dt-bindings/clk/ti-dra7-atl.h
new file mode 100644 (file)
index 0000000..42dd416
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * This header provides constants for DRA7 ATL (Audio Tracking Logic)
+ *
+ * The constants defined in this header are used in dts files
+ *
+ * Copyright (C) 2013 Texas Instruments, Inc.
+ *
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _DT_BINDINGS_CLK_DRA7_ATL_H
+#define _DT_BINDINGS_CLK_DRA7_ATL_H
+
+#define DRA7_ATL_WS_MCASP1_FSR         0
+#define DRA7_ATL_WS_MCASP1_FSX         1
+#define DRA7_ATL_WS_MCASP2_FSR         2
+#define DRA7_ATL_WS_MCASP2_FSX         3
+#define DRA7_ATL_WS_MCASP3_FSX         4
+#define DRA7_ATL_WS_MCASP4_FSX         5
+#define DRA7_ATL_WS_MCASP5_FSX         6
+#define DRA7_ATL_WS_MCASP6_FSX         7
+#define DRA7_ATL_WS_MCASP7_FSX         8
+#define DRA7_ATL_WS_MCASP8_FSX         9
+#define DRA7_ATL_WS_MCASP8_AHCLKX      10
+#define DRA7_ATL_WS_XREF_CLK3          11
+#define DRA7_ATL_WS_XREF_CLK0          12
+#define DRA7_ATL_WS_XREF_CLK1          13
+#define DRA7_ATL_WS_XREF_CLK2          14
+#define DRA7_ATL_WS_OSC1_X1            15
+
+#endif
index 8598f8eacb20e175aacb638edd41e514afc7f9d8..a495a959e8a754939b9c8c9d9bd748a73f54f587 100644 (file)
@@ -36,6 +36,8 @@ struct ath9k_platform_data {
 
        int (*get_mac_revision)(void);
        int (*external_reset)(void);
+
+       bool use_eeprom;
 };
 
 #endif /* _LINUX_ATH9K_PLATFORM_H */
index d8e4cea23a257c1b9c8b1514493c407b13d912e0..66c2167f04a9d5788b68e97f446757bad082d780 100644 (file)
@@ -5,8 +5,6 @@
 #ifndef __LINUX_BLK_TYPES_H
 #define __LINUX_BLK_TYPES_H
 
-#ifdef CONFIG_BLOCK
-
 #include <linux/types.h>
 
 struct bio_set;
@@ -28,6 +26,8 @@ struct bio_vec {
        unsigned int    bv_offset;
 };
 
+#ifdef CONFIG_BLOCK
+
 struct bvec_iter {
        sector_t                bi_sector;      /* device address in 512 byte
                                                   sectors */
index 78c6c52073ad62948a7d750951ed94d232957048..a0875001b13c84ad70a9b2909654e9ffb6824c58 100644 (file)
@@ -10,8 +10,8 @@
  *
  */
 
-#ifndef CAN_CORE_H
-#define CAN_CORE_H
+#ifndef _CAN_CORE_H
+#define _CAN_CORE_H
 
 #include <linux/can.h>
 #include <linux/skbuff.h>
@@ -58,4 +58,4 @@ extern void can_rx_unregister(struct net_device *dev, canid_t can_id,
 extern int can_send(struct sk_buff *skb, int loop);
 extern int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
 
-#endif /* CAN_CORE_H */
+#endif /* !_CAN_CORE_H */
index 3ce5e526525f852f37ea242700363036c85068ca..6992afc6ba7f96fda9dba202f771fe7e28f807fe 100644 (file)
@@ -10,8 +10,8 @@
  *
  */
 
-#ifndef CAN_DEV_H
-#define CAN_DEV_H
+#ifndef _CAN_DEV_H
+#define _CAN_DEV_H
 
 #include <linux/can.h>
 #include <linux/can/netlink.h>
@@ -132,4 +132,4 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev,
 struct sk_buff *alloc_can_err_skb(struct net_device *dev,
                                  struct can_frame **cf);
 
-#endif /* CAN_DEV_H */
+#endif /* !_CAN_DEV_H */
index 9c1167baf273e7e62cb29dbfb89266e11f1c29d6..e0475c5cbb92aac6fe1163bc2ac65a4664048152 100644 (file)
@@ -6,8 +6,8 @@
  * published by the Free Software Foundation.
  */
 
-#ifndef CAN_LED_H
-#define CAN_LED_H
+#ifndef _CAN_LED_H
+#define _CAN_LED_H
 
 #include <linux/if.h>
 #include <linux/leds.h>
@@ -48,4 +48,4 @@ static inline void can_led_notifier_exit(void)
 
 #endif
 
-#endif
+#endif /* !_CAN_LED_H */
index 7702641f87ee032b76d2cfa68fdd5f75aec4ec2b..78b2d44f04cffc83f7ce7feb4cd08c94cea84ce8 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef _CAN_PLATFORM_CC770_H_
-#define _CAN_PLATFORM_CC770_H_
+#ifndef _CAN_PLATFORM_CC770_H
+#define _CAN_PLATFORM_CC770_H
 
 /* CPU Interface Register (0x02) */
 #define CPUIF_CEN      0x01    /* Clock Out Enable */
@@ -30,4 +30,4 @@ struct cc770_platform_data {
        u8 bcr;         /* Bus Configuration Register */
 };
 
-#endif /* !_CAN_PLATFORM_CC770_H_ */
+#endif /* !_CAN_PLATFORM_CC770_H */
index dc029dba7a030d384d9389038b6f7d600eee3dbf..d44fcae274ff2a0877c06091bfaafe68e0cc8d6d 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef __CAN_PLATFORM_MCP251X_H__
-#define __CAN_PLATFORM_MCP251X_H__
+#ifndef _CAN_PLATFORM_MCP251X_H
+#define _CAN_PLATFORM_MCP251X_H
 
 /*
  *
@@ -18,4 +18,4 @@ struct mcp251x_platform_data {
        unsigned long oscillator_frequency;
 };
 
-#endif /* __CAN_PLATFORM_MCP251X_H__ */
+#endif /* !_CAN_PLATFORM_MCP251X_H */
diff --git a/include/linux/can/platform/rcar_can.h b/include/linux/can/platform/rcar_can.h
new file mode 100644 (file)
index 0000000..0f4a2f3
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _CAN_PLATFORM_RCAR_CAN_H_
+#define _CAN_PLATFORM_RCAR_CAN_H_
+
+#include <linux/types.h>
+
+/* Clock Select Register settings */
+enum CLKR {
+       CLKR_CLKP1 = 0, /* Peripheral clock (clkp1) */
+       CLKR_CLKP2 = 1, /* Peripheral clock (clkp2) */
+       CLKR_CLKEXT = 3 /* Externally input clock */
+};
+
+struct rcar_can_platform_data {
+       enum CLKR clock_select; /* Clock source select */
+};
+
+#endif /* !_CAN_PLATFORM_RCAR_CAN_H_ */
index 96f8fcc78d787a3b826967b77328ea6f4e1dc8ef..93570b61ec6c58bfa433e6b4f710fb9a4e466121 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef _CAN_PLATFORM_SJA1000_H_
-#define _CAN_PLATFORM_SJA1000_H_
+#ifndef _CAN_PLATFORM_SJA1000_H
+#define _CAN_PLATFORM_SJA1000_H
 
 /* clock divider register */
 #define CDR_CLKOUT_MASK 0x07
@@ -32,4 +32,4 @@ struct sja1000_platform_data {
        u8 cdr;         /* clock divider register */
 };
 
-#endif /* !_CAN_PLATFORM_SJA1000_H_ */
+#endif /* !_CAN_PLATFORM_SJA1000_H */
index af17cb3f7a8402bdd3816911abb9f98ebf60632f..a52f47ca6c8ad9c5159c34b4f568b5d84eccbd18 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef __CAN_PLATFORM_TI_HECC_H__
-#define __CAN_PLATFORM_TI_HECC_H__
+#ifndef _CAN_PLATFORM_TI_HECC_H
+#define _CAN_PLATFORM_TI_HECC_H
 
 /*
  * TI HECC (High End CAN Controller) driver platform header
@@ -41,4 +41,4 @@ struct ti_hecc_platform_data {
        u32 version;
        void (*transceiver_switch) (int);
 };
-#endif
+#endif /* !_CAN_PLATFORM_TI_HECC_H */
index f9bbbb472663af08aef78ac152e8293f36736ea4..cc00d15c6107be8893b024e3eabb2ef1ba243016 100644 (file)
@@ -7,8 +7,8 @@
  *
  */
 
-#ifndef CAN_SKB_H
-#define CAN_SKB_H
+#ifndef _CAN_SKB_H
+#define _CAN_SKB_H
 
 #include <linux/types.h>
 #include <linux/skbuff.h>
@@ -80,4 +80,4 @@ static inline struct sk_buff *can_create_echo_skb(struct sk_buff *skb)
        return skb;
 }
 
-#endif /* CAN_SKB_H */
+#endif /* !_CAN_SKB_H */
index 5f6db18d72e8845748a5b506949a1d7d1247cf06..3c97d5e9b951ed419bfb1663be0c4f1c9d416fd7 100644 (file)
@@ -625,6 +625,8 @@ int ceph_flags_to_mode(int flags);
                           CEPH_CAP_LINK_EXCL |         \
                           CEPH_CAP_XATTR_EXCL |        \
                           CEPH_CAP_FILE_EXCL)
+#define CEPH_CAP_ANY_FILE_RD (CEPH_CAP_FILE_RD | CEPH_CAP_FILE_CACHE | \
+                             CEPH_CAP_FILE_SHARED)
 #define CEPH_CAP_ANY_FILE_WR (CEPH_CAP_FILE_WR | CEPH_CAP_FILE_BUFFER |        \
                              CEPH_CAP_FILE_EXCL)
 #define CEPH_CAP_ANY_WR   (CEPH_CAP_ANY_EXCL | CEPH_CAP_ANY_FILE_WR)
index 2f49aa4c4f7f1d2feb361422e2b5f024c55a9519..279b0afac1c112e7eee89dddce0847ddcff2d5a2 100644 (file)
@@ -222,8 +222,6 @@ extern void ceph_copy_to_page_vector(struct page **pages,
 extern void ceph_copy_from_page_vector(struct page **pages,
                                    void *data,
                                    loff_t off, size_t len);
-extern int ceph_copy_page_vector_to_user(struct page **pages, void __user *data,
-                                   loff_t off, size_t len);
 extern void ceph_zero_page_vector_range(int off, int len, struct page **pages);
 
 
index a486f390dfbeeb9d4004921bb973852bd586a808..deb47e45ac7c29b11642842a733a351f05bedbb3 100644 (file)
@@ -40,9 +40,9 @@ struct ceph_mon_request {
 };
 
 /*
- * ceph_mon_generic_request is being used for the statfs and poolop requests
- * which are bening done a bit differently because we need to get data back
- * to the caller
+ * ceph_mon_generic_request is being used for the statfs, poolop and
+ * mon_get_version requests which are being done a bit differently
+ * because we need to get data back to the caller
  */
 struct ceph_mon_generic_request {
        struct kref kref;
@@ -104,10 +104,15 @@ extern int ceph_monc_got_mdsmap(struct ceph_mon_client *monc, u32 have);
 extern int ceph_monc_got_osdmap(struct ceph_mon_client *monc, u32 have);
 
 extern void ceph_monc_request_next_osdmap(struct ceph_mon_client *monc);
+extern int ceph_monc_wait_osdmap(struct ceph_mon_client *monc, u32 epoch,
+                                unsigned long timeout);
 
 extern int ceph_monc_do_statfs(struct ceph_mon_client *monc,
                               struct ceph_statfs *buf);
 
+extern int ceph_monc_do_get_version(struct ceph_mon_client *monc,
+                                   const char *what, u64 *newest);
+
 extern int ceph_monc_open_session(struct ceph_mon_client *monc);
 
 extern int ceph_monc_validate_auth(struct ceph_mon_client *monc);
index 4a21a872dbbd6423025f1a9253832f228cfe1c48..e8d8a35034a5e81c95e0471d0530f7c87a784db9 100644 (file)
@@ -41,6 +41,8 @@
  * @idlest_reg: register containing the DPLL idle status bitfield
  * @autoidle_mask: mask of the DPLL autoidle mode bitfield in @autoidle_reg
  * @freqsel_mask: mask of the DPLL jitter correction bitfield in @control_reg
+ * @dcc_mask: mask of the DPLL DCC correction bitfield @mult_div1_reg
+ * @dcc_rate: rate atleast which DCC @dcc_mask must be set
  * @idlest_mask: mask of the DPLL idle status bitfield in @idlest_reg
  * @lpmode_mask: mask of the DPLL low-power mode bitfield in @control_reg
  * @m4xen_mask: mask of the DPLL M4X multiplier bitfield in @control_reg
@@ -86,6 +88,8 @@ struct dpll_data {
        u32                     idlest_mask;
        u32                     dco_mask;
        u32                     sddiv_mask;
+       u32                     dcc_mask;
+       unsigned long           dcc_rate;
        u32                     lpmode_mask;
        u32                     m4xen_mask;
        u8                      auto_recal_bit;
@@ -94,7 +98,26 @@ struct dpll_data {
        u8                      flags;
 };
 
-struct clk_hw_omap_ops;
+struct clk_hw_omap;
+
+/**
+ * struct clk_hw_omap_ops - OMAP clk ops
+ * @find_idlest: find idlest register information for a clock
+ * @find_companion: find companion clock register information for a clock,
+ *                 basically converts CM_ICLKEN* <-> CM_FCLKEN*
+ * @allow_idle: enables autoidle hardware functionality for a clock
+ * @deny_idle: prevent autoidle hardware functionality for a clock
+ */
+struct clk_hw_omap_ops {
+       void    (*find_idlest)(struct clk_hw_omap *oclk,
+                              void __iomem **idlest_reg,
+                              u8 *idlest_bit, u8 *idlest_val);
+       void    (*find_companion)(struct clk_hw_omap *oclk,
+                                 void __iomem **other_reg,
+                                 u8 *other_bit);
+       void    (*allow_idle)(struct clk_hw_omap *oclk);
+       void    (*deny_idle)(struct clk_hw_omap *oclk);
+};
 
 /**
  * struct clk_hw_omap - OMAP struct clk
@@ -259,6 +282,12 @@ int omap2_dflt_clk_enable(struct clk_hw *hw);
 void omap2_dflt_clk_disable(struct clk_hw *hw);
 int omap2_dflt_clk_is_enabled(struct clk_hw *hw);
 void omap3_clk_lock_dpll5(void);
+unsigned long omap2_dpllcore_recalc(struct clk_hw *hw,
+                                   unsigned long parent_rate);
+int omap2_reprogram_dpllcore(struct clk_hw *clk, unsigned long rate,
+                            unsigned long parent_rate);
+void omap2xxx_clkt_dpllcore_init(struct clk_hw *hw);
+void omap2xxx_clkt_vps_init(void);
 
 void __iomem *ti_clk_get_reg_addr(struct device_node *node, int index);
 void ti_dt_clocks_register(struct ti_dt_clk *oclks);
@@ -278,6 +307,8 @@ int omap5xxx_dt_clk_init(void);
 int dra7xx_dt_clk_init(void);
 int am33xx_dt_clk_init(void);
 int am43xx_dt_clk_init(void);
+int omap2420_dt_clk_init(void);
+int omap2430_dt_clk_init(void);
 
 #ifdef CONFIG_OF
 void of_ti_clk_allow_autoidle_all(void);
@@ -287,6 +318,8 @@ static inline void of_ti_clk_allow_autoidle_all(void) { }
 static inline void of_ti_clk_deny_autoidle_all(void) { }
 #endif
 
+extern const struct clk_hw_omap_ops clkhwops_omap2xxx_dpll;
+extern const struct clk_hw_omap_ops clkhwops_omap2430_i2chs_wait;
 extern const struct clk_hw_omap_ops clkhwops_omap3_dpll;
 extern const struct clk_hw_omap_ops clkhwops_omap4_dpllmx;
 extern const struct clk_hw_omap_ops clkhwops_wait;
index 64fdfe1cfcf0c8848ef80a7953dc975fdab7c501..d5ad7b1118fc10748377d9a90d2e960e0f7b611a 100644 (file)
@@ -383,7 +383,9 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect);
 /* Ignore/forbid kprobes attach on very low level functions marked by this attribute: */
 #ifdef CONFIG_KPROBES
 # define __kprobes     __attribute__((__section__(".kprobes.text")))
+# define nokprobe_inline       __always_inline
 #else
 # define __kprobes
+# define nokprobe_inline       inline
 #endif
 #endif /* __LINUX_COMPILER_H */
index 3f458896d45c9784e4cd34848c1a9e12793e4485..ec4112d257bca140886bdadea4de5a8ef468c080 100644 (file)
@@ -75,6 +75,7 @@ struct cpufreq_policy {
        unsigned int            max;    /* in kHz */
        unsigned int            cur;    /* in kHz, only needed if cpufreq
                                         * governors are used */
+       unsigned int            restore_freq; /* = policy->cur before transition */
        unsigned int            suspend_freq; /* freq to set during suspend */
 
        unsigned int            policy; /* see above */
@@ -221,11 +222,35 @@ struct cpufreq_driver {
 
        /* define one out of two */
        int     (*setpolicy)    (struct cpufreq_policy *policy);
+
+       /*
+        * On failure, should always restore frequency to policy->restore_freq
+        * (i.e. old freq).
+        */
        int     (*target)       (struct cpufreq_policy *policy, /* Deprecated */
                                 unsigned int target_freq,
                                 unsigned int relation);
        int     (*target_index) (struct cpufreq_policy *policy,
                                 unsigned int index);
+       /*
+        * Only for drivers with target_index() and CPUFREQ_ASYNC_NOTIFICATION
+        * unset.
+        *
+        * get_intermediate should return a stable intermediate frequency
+        * platform wants to switch to and target_intermediate() should set CPU
+        * to to that frequency, before jumping to the frequency corresponding
+        * to 'index'. Core will take care of sending notifications and driver
+        * doesn't have to handle them in target_intermediate() or
+        * target_index().
+        *
+        * Drivers can return '0' from get_intermediate() in case they don't
+        * wish to switch to intermediate frequency for some target frequency.
+        * In that case core will directly call ->target_index().
+        */
+       unsigned int (*get_intermediate)(struct cpufreq_policy *policy,
+                                        unsigned int index);
+       int     (*target_intermediate)(struct cpufreq_policy *policy,
+                                      unsigned int index);
 
        /* should be defined, if possible */
        unsigned int    (*get)  (unsigned int cpu);
index d08e4d2a9b92550af4b395bb62d101873d9d7731..2997af6d2ccd257a15612c45875d80308cb99d73 100644 (file)
@@ -142,6 +142,13 @@ static inline unsigned int cpumask_any_but(const struct cpumask *mask,
        return 1;
 }
 
+static inline int cpumask_set_cpu_local_first(int i, int numa_node, cpumask_t *dstp)
+{
+       set_bit(0, cpumask_bits(dstp));
+
+       return 0;
+}
+
 #define for_each_cpu(cpu, mask)                        \
        for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask)
 #define for_each_cpu_not(cpu, mask)            \
@@ -192,6 +199,7 @@ static inline unsigned int cpumask_next_zero(int n, const struct cpumask *srcp)
 
 int cpumask_next_and(int n, const struct cpumask *, const struct cpumask *);
 int cpumask_any_but(const struct cpumask *mask, unsigned int cpu);
+int cpumask_set_cpu_local_first(int i, int numa_node, cpumask_t *dstp);
 
 /**
  * for_each_cpu - iterate over every cpu in a mask
@@ -600,7 +608,7 @@ static inline int cpulist_scnprintf(char *buf, int len,
 static inline int cpumask_parse(const char *buf, struct cpumask *dstp)
 {
        char *nl = strchr(buf, '\n');
-       int len = nl ? nl - buf : strlen(buf);
+       unsigned int len = nl ? (unsigned int)(nl - buf) : strlen(buf);
 
        return bitmap_parse(buf, len, cpumask_bits(dstp), nr_cpumask_bits);
 }
index 1786e772d5c6e9a2634eb13d916d8d8bd6c25fcb..d590765106f3d226449ccf5228181baef53c4c17 100644 (file)
@@ -2,13 +2,13 @@
 #define _LINUX_CRC7_H
 #include <linux/types.h>
 
-extern const u8 crc7_syndrome_table[256];
+extern const u8 crc7_be_syndrome_table[256];
 
-static inline u8 crc7_byte(u8 crc, u8 data)
+static inline u8 crc7_be_byte(u8 crc, u8 data)
 {
-       return crc7_syndrome_table[(crc << 1) ^ data];
+       return crc7_be_syndrome_table[crc ^ data];
 }
 
-extern u8 crc7(u8 crc, const u8 *buffer, size_t len);
+extern u8 crc7_be(u8 crc, const u8 *buffer, size_t len);
 
 #endif
diff --git a/include/linux/dell-led.h b/include/linux/dell-led.h
new file mode 100644 (file)
index 0000000..7009b8b
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __DELL_LED_H__
+#define __DELL_LED_H__
+
+enum {
+       DELL_LED_MICMUTE,
+};
+
+int dell_app_wmi_led_set(int whichled, int on);
+
+#endif
index 63da56ed979620b5fb8275f847fafb9e8e5212ba..e1707de043ae7bad7f6165ee34841f39abd5cc6c 100644 (file)
@@ -115,12 +115,6 @@ typedef int (*dm_busy_fn) (struct dm_target *ti);
 
 void dm_error(const char *message);
 
-/*
- * Combine device limits.
- */
-int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
-                        sector_t start, sector_t len, void *data);
-
 struct dm_dev {
        struct block_device *bdev;
        fmode_t mode;
@@ -132,7 +126,7 @@ struct dm_dev {
  * are opened/closed correctly.
  */
 int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
-                                                struct dm_dev **result);
+                 struct dm_dev **result);
 void dm_put_device(struct dm_target *ti, struct dm_dev *d);
 
 /*
@@ -291,6 +285,7 @@ struct dm_target_io {
        struct dm_io *io;
        struct dm_target *ti;
        unsigned target_bio_nr;
+       unsigned *len_ptr;
        struct bio clone;
 };
 
@@ -401,6 +396,7 @@ int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid);
 struct gendisk *dm_disk(struct mapped_device *md);
 int dm_suspended(struct dm_target *ti);
 int dm_noflush_suspending(struct dm_target *ti);
+void dm_accept_partial_bio(struct bio *bio, unsigned n_sectors);
 union map_info *dm_get_rq_mapinfo(struct request *rq);
 
 struct queue_limits *dm_get_queue_limits(struct mapped_device *md);
index 0a114d05f68d35bd275924a7290a6c3b75ef17fd..e658229fee39c17ea8673eadcf7b49cfdce508e1 100644 (file)
@@ -154,13 +154,20 @@ static inline u32 ethtool_rxfh_indir_default(u32 index, u32 n_rx_rings)
  * @reset: Reset (part of) the device, as specified by a bitmask of
  *     flags from &enum ethtool_reset_flags.  Returns a negative
  *     error code or zero.
+ * @get_rxfh_key_size: Get the size of the RX flow hash key.
+ *     Returns zero if not supported for this specific device.
  * @get_rxfh_indir_size: Get the size of the RX flow hash indirection table.
  *     Returns zero if not supported for this specific device.
- * @get_rxfh_indir: Get the contents of the RX flow hash indirection table.
- *     Will not be called if @get_rxfh_indir_size returns zero.
+ * @get_rxfh: Get the contents of the RX flow hash indirection table and hash
+ *     key.
+ *     Will only be called if one or both of @get_rxfh_indir_size and
+ *     @get_rxfh_key_size are implemented and return non-zero.
  *     Returns a negative error code or zero.
- * @set_rxfh_indir: Set the contents of the RX flow hash indirection table.
- *     Will not be called if @get_rxfh_indir_size returns zero.
+ * @set_rxfh: Set the contents of the RX flow hash indirection table and/or
+ *     hash key.  In case only the indirection table or hash key is to be
+ *     changed, the other argument will be %NULL.
+ *     Will only be called if one or both of @get_rxfh_indir_size and
+ *     @get_rxfh_key_size are implemented and return non-zero.
  *     Returns a negative error code or zero.
  * @get_channels: Get number of channels.
  * @set_channels: Set number of channels.  Returns a negative error code or
@@ -232,9 +239,11 @@ struct ethtool_ops {
        int     (*set_rxnfc)(struct net_device *, struct ethtool_rxnfc *);
        int     (*flash_device)(struct net_device *, struct ethtool_flash *);
        int     (*reset)(struct net_device *, u32 *);
+       u32     (*get_rxfh_key_size)(struct net_device *);
        u32     (*get_rxfh_indir_size)(struct net_device *);
-       int     (*get_rxfh_indir)(struct net_device *, u32 *);
-       int     (*set_rxfh_indir)(struct net_device *, const u32 *);
+       int     (*get_rxfh)(struct net_device *, u32 *indir, u8 *key);
+       int     (*set_rxfh)(struct net_device *, const u32 *indir,
+                           const u8 *key);
        void    (*get_channels)(struct net_device *, struct ethtool_channels *);
        int     (*set_channels)(struct net_device *, struct ethtool_channels *);
        int     (*get_dump_flag)(struct net_device *, struct ethtool_dump *);
index 024fd03e5d182d5670ee2c60005cbea43f8a83e8..a7e3c48d73a70677f88fcbf68c89e62f1caf159f 100644 (file)
 #define BPF_CALL       0x80    /* function call */
 #define BPF_EXIT       0x90    /* function return */
 
+/* Register numbers */
+enum {
+       BPF_REG_0 = 0,
+       BPF_REG_1,
+       BPF_REG_2,
+       BPF_REG_3,
+       BPF_REG_4,
+       BPF_REG_5,
+       BPF_REG_6,
+       BPF_REG_7,
+       BPF_REG_8,
+       BPF_REG_9,
+       BPF_REG_10,
+       __MAX_BPF_REG,
+};
+
 /* BPF has 10 general purpose 64-bit registers and stack frame. */
-#define MAX_BPF_REG    11
+#define MAX_BPF_REG    __MAX_BPF_REG
+
+/* ArgX, context and stack frame pointer register positions. Note,
+ * Arg1, Arg2, Arg3, etc are used as argument mappings of function
+ * calls in BPF_CALL instruction.
+ */
+#define BPF_REG_ARG1   BPF_REG_1
+#define BPF_REG_ARG2   BPF_REG_2
+#define BPF_REG_ARG3   BPF_REG_3
+#define BPF_REG_ARG4   BPF_REG_4
+#define BPF_REG_ARG5   BPF_REG_5
+#define BPF_REG_CTX    BPF_REG_6
+#define BPF_REG_FP     BPF_REG_10
+
+/* Additional register mappings for converted user programs. */
+#define BPF_REG_A      BPF_REG_0
+#define BPF_REG_X      BPF_REG_7
+#define BPF_REG_TMP    BPF_REG_8
 
 /* BPF program can access up to 512 bytes of stack space. */
 #define MAX_BPF_STACK  512
 
-/* Arg1, context and stack frame pointer register positions. */
-#define ARG1_REG       1
-#define CTX_REG                6
-#define FP_REG         10
+/* Helper macros for filter block array initializers. */
+
+/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */
+
+#define BPF_ALU64_REG(OP, DST, SRC)                            \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU64 | BPF_OP(OP) | BPF_X,        \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
+#define BPF_ALU32_REG(OP, DST, SRC)                            \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU | BPF_OP(OP) | BPF_X,          \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
+/* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */
+
+#define BPF_ALU64_IMM(OP, DST, IMM)                            \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU64 | BPF_OP(OP) | BPF_K,        \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+#define BPF_ALU32_IMM(OP, DST, IMM)                            \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU | BPF_OP(OP) | BPF_K,          \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+/* Endianess conversion, cpu_to_{l,b}e(), {l,b}e_to_cpu() */
+
+#define BPF_ENDIAN(TYPE, DST, LEN)                             \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU | BPF_END | BPF_SRC(TYPE),     \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = LEN })
+
+/* Short form of mov, dst_reg = src_reg */
+
+#define BPF_MOV64_REG(DST, SRC)                                        \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU64 | BPF_MOV | BPF_X,           \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
+#define BPF_MOV32_REG(DST, SRC)                                        \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU | BPF_MOV | BPF_X,             \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
+/* Short form of mov, dst_reg = imm32 */
+
+#define BPF_MOV64_IMM(DST, IMM)                                        \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU64 | BPF_MOV | BPF_K,           \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+#define BPF_MOV32_IMM(DST, IMM)                                        \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU | BPF_MOV | BPF_K,             \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+/* Short form of mov based on type, BPF_X: dst_reg = src_reg, BPF_K: dst_reg = imm32 */
+
+#define BPF_MOV64_RAW(TYPE, DST, SRC, IMM)                     \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU64 | BPF_MOV | BPF_SRC(TYPE),   \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+#define BPF_MOV32_RAW(TYPE, DST, SRC, IMM)                     \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU | BPF_MOV | BPF_SRC(TYPE),     \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+/* Direct packet access, R0 = *(uint *) (skb->data + imm32) */
+
+#define BPF_LD_ABS(SIZE, IMM)                                  \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS,     \
+               .dst_reg = 0,                                   \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+/* Indirect packet access, R0 = *(uint *) (skb->data + src_reg + imm32) */
+
+#define BPF_LD_IND(SIZE, SRC, IMM)                             \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_LD | BPF_SIZE(SIZE) | BPF_IND,     \
+               .dst_reg = 0,                                   \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+/* Memory load, dst_reg = *(uint *) (src_reg + off16) */
+
+#define BPF_LDX_MEM(SIZE, DST, SRC, OFF)                       \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM,    \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = OFF,                                   \
+               .imm   = 0 })
+
+/* Memory store, *(uint *) (dst_reg + off16) = src_reg */
+
+#define BPF_STX_MEM(SIZE, DST, SRC, OFF)                       \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM,    \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = OFF,                                   \
+               .imm   = 0 })
+
+/* Memory store, *(uint *) (dst_reg + off16) = imm32 */
+
+#define BPF_ST_MEM(SIZE, DST, OFF, IMM)                                \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM,     \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = OFF,                                   \
+               .imm   = IMM })
+
+/* Conditional jumps against registers, if (dst_reg 'op' src_reg) goto pc + off16 */
+
+#define BPF_JMP_REG(OP, DST, SRC, OFF)                         \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_JMP | BPF_OP(OP) | BPF_X,          \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = OFF,                                   \
+               .imm   = 0 })
+
+/* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */
+
+#define BPF_JMP_IMM(OP, DST, IMM, OFF)                         \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_JMP | BPF_OP(OP) | BPF_K,          \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = OFF,                                   \
+               .imm   = IMM })
+
+/* Function call */
+
+#define BPF_EMIT_CALL(FUNC)                                    \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_JMP | BPF_CALL,                    \
+               .dst_reg = 0,                                   \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = ((FUNC) - __bpf_call_base) })
+
+/* Raw code statement block */
+
+#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM)                 \
+       ((struct sock_filter_int) {                             \
+               .code  = CODE,                                  \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = OFF,                                   \
+               .imm   = IMM })
+
+/* Program exit */
+
+#define BPF_EXIT_INSN()                                                \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_JMP | BPF_EXIT,                    \
+               .dst_reg = 0,                                   \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
+#define bytes_to_bpf_size(bytes)                               \
+({                                                             \
+       int bpf_size = -EINVAL;                                 \
+                                                               \
+       if (bytes == sizeof(u8))                                \
+               bpf_size = BPF_B;                               \
+       else if (bytes == sizeof(u16))                          \
+               bpf_size = BPF_H;                               \
+       else if (bytes == sizeof(u32))                          \
+               bpf_size = BPF_W;                               \
+       else if (bytes == sizeof(u64))                          \
+               bpf_size = BPF_DW;                              \
+                                                               \
+       bpf_size;                                               \
+})
+
+/* Macro to invoke filter function. */
+#define SK_RUN_FILTER(filter, ctx)  (*filter->bpf_func)(ctx, filter->insnsi)
 
 struct sock_filter_int {
        __u8    code;           /* opcode */
-       __u8    a_reg:4;        /* dest register */
-       __u8    x_reg:4;        /* source register */
+       __u8    dst_reg:4;      /* dest register */
+       __u8    src_reg:4;      /* source register */
        __s16   off;            /* signed offset */
        __s32   imm;            /* signed immediate constant */
 };
@@ -97,21 +346,16 @@ static inline unsigned int sk_filter_size(unsigned int proglen)
 #define sk_filter_proglen(fprog)                       \
                (fprog->len * sizeof(fprog->filter[0]))
 
-#define SK_RUN_FILTER(filter, ctx)                     \
-               (*filter->bpf_func)(ctx, filter->insnsi)
-
 int sk_filter(struct sock *sk, struct sk_buff *skb);
 
-u32 sk_run_filter_int_seccomp(const struct seccomp_data *ctx,
-                             const struct sock_filter_int *insni);
-u32 sk_run_filter_int_skb(const struct sk_buff *ctx,
-                         const struct sock_filter_int *insni);
+void sk_filter_select_runtime(struct sk_filter *fp);
+void sk_filter_free(struct sk_filter *fp);
 
 int sk_convert_filter(struct sock_filter *prog, int len,
                      struct sock_filter_int *new_prog, int *new_len);
 
 int sk_unattached_filter_create(struct sk_filter **pfp,
-                               struct sock_fprog *fprog);
+                               struct sock_fprog_kern *fprog);
 void sk_unattached_filter_destroy(struct sk_filter *fp);
 
 int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
@@ -120,11 +364,48 @@ int sk_detach_filter(struct sock *sk);
 int sk_chk_filter(struct sock_filter *filter, unsigned int flen);
 int sk_get_filter(struct sock *sk, struct sock_filter __user *filter,
                  unsigned int len);
-void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to);
 
 void sk_filter_charge(struct sock *sk, struct sk_filter *fp);
 void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp);
 
+u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
+void bpf_int_jit_compile(struct sk_filter *fp);
+
+#define BPF_ANC                BIT(15)
+
+static inline u16 bpf_anc_helper(const struct sock_filter *ftest)
+{
+       BUG_ON(ftest->code & BPF_ANC);
+
+       switch (ftest->code) {
+       case BPF_LD | BPF_W | BPF_ABS:
+       case BPF_LD | BPF_H | BPF_ABS:
+       case BPF_LD | BPF_B | BPF_ABS:
+#define BPF_ANCILLARY(CODE)    case SKF_AD_OFF + SKF_AD_##CODE:        \
+                               return BPF_ANC | SKF_AD_##CODE
+               switch (ftest->k) {
+               BPF_ANCILLARY(PROTOCOL);
+               BPF_ANCILLARY(PKTTYPE);
+               BPF_ANCILLARY(IFINDEX);
+               BPF_ANCILLARY(NLATTR);
+               BPF_ANCILLARY(NLATTR_NEST);
+               BPF_ANCILLARY(MARK);
+               BPF_ANCILLARY(QUEUE);
+               BPF_ANCILLARY(HATYPE);
+               BPF_ANCILLARY(RXHASH);
+               BPF_ANCILLARY(CPU);
+               BPF_ANCILLARY(ALU_XOR_X);
+               BPF_ANCILLARY(VLAN_TAG);
+               BPF_ANCILLARY(VLAN_TAG_PRESENT);
+               BPF_ANCILLARY(PAY_OFFSET);
+               BPF_ANCILLARY(RANDOM);
+               }
+               /* Fallthrough. */
+       default:
+               return ftest->code;
+       }
+}
+
 #ifdef CONFIG_BPF_JIT
 #include <stdarg.h>
 #include <linux/linkage.h>
@@ -144,85 +425,20 @@ static inline void bpf_jit_dump(unsigned int flen, unsigned int proglen,
 }
 #else
 #include <linux/slab.h>
+
 static inline void bpf_jit_compile(struct sk_filter *fp)
 {
 }
+
 static inline void bpf_jit_free(struct sk_filter *fp)
 {
        kfree(fp);
 }
-#endif
+#endif /* CONFIG_BPF_JIT */
 
 static inline int bpf_tell_extensions(void)
 {
        return SKF_AD_MAX;
 }
 
-enum {
-       BPF_S_RET_K = 1,
-       BPF_S_RET_A,
-       BPF_S_ALU_ADD_K,
-       BPF_S_ALU_ADD_X,
-       BPF_S_ALU_SUB_K,
-       BPF_S_ALU_SUB_X,
-       BPF_S_ALU_MUL_K,
-       BPF_S_ALU_MUL_X,
-       BPF_S_ALU_DIV_X,
-       BPF_S_ALU_MOD_K,
-       BPF_S_ALU_MOD_X,
-       BPF_S_ALU_AND_K,
-       BPF_S_ALU_AND_X,
-       BPF_S_ALU_OR_K,
-       BPF_S_ALU_OR_X,
-       BPF_S_ALU_XOR_K,
-       BPF_S_ALU_XOR_X,
-       BPF_S_ALU_LSH_K,
-       BPF_S_ALU_LSH_X,
-       BPF_S_ALU_RSH_K,
-       BPF_S_ALU_RSH_X,
-       BPF_S_ALU_NEG,
-       BPF_S_LD_W_ABS,
-       BPF_S_LD_H_ABS,
-       BPF_S_LD_B_ABS,
-       BPF_S_LD_W_LEN,
-       BPF_S_LD_W_IND,
-       BPF_S_LD_H_IND,
-       BPF_S_LD_B_IND,
-       BPF_S_LD_IMM,
-       BPF_S_LDX_W_LEN,
-       BPF_S_LDX_B_MSH,
-       BPF_S_LDX_IMM,
-       BPF_S_MISC_TAX,
-       BPF_S_MISC_TXA,
-       BPF_S_ALU_DIV_K,
-       BPF_S_LD_MEM,
-       BPF_S_LDX_MEM,
-       BPF_S_ST,
-       BPF_S_STX,
-       BPF_S_JMP_JA,
-       BPF_S_JMP_JEQ_K,
-       BPF_S_JMP_JEQ_X,
-       BPF_S_JMP_JGE_K,
-       BPF_S_JMP_JGE_X,
-       BPF_S_JMP_JGT_K,
-       BPF_S_JMP_JGT_X,
-       BPF_S_JMP_JSET_K,
-       BPF_S_JMP_JSET_X,
-       /* Ancillary data */
-       BPF_S_ANC_PROTOCOL,
-       BPF_S_ANC_PKTTYPE,
-       BPF_S_ANC_IFINDEX,
-       BPF_S_ANC_NLATTR,
-       BPF_S_ANC_NLATTR_NEST,
-       BPF_S_ANC_MARK,
-       BPF_S_ANC_QUEUE,
-       BPF_S_ANC_HATYPE,
-       BPF_S_ANC_RXHASH,
-       BPF_S_ANC_CPU,
-       BPF_S_ANC_ALU_XOR_X,
-       BPF_S_ANC_VLAN_TAG,
-       BPF_S_ANC_VLAN_TAG_PRESENT,
-       BPF_S_ANC_PAY_OFFSET,
-};
-
 #endif /* __LINUX_FILTER_H__ */
index c3f46e499dd0027eed7f2bd0a0bc3cd465c3ac44..338e6f758c6d922be7d8163361da051efa0e3cbc 100644 (file)
@@ -128,6 +128,10 @@ typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
 #define FMODE_ATOMIC_POS       ((__force fmode_t)0x8000)
 /* Write access to underlying fs */
 #define FMODE_WRITER           ((__force fmode_t)0x10000)
+/* Has read method(s) */
+#define FMODE_CAN_READ          ((__force fmode_t)0x20000)
+/* Has write method(s) */
+#define FMODE_CAN_WRITE         ((__force fmode_t)0x40000)
 
 /* File was opened by fanotify and shouldn't generate fanotify events */
 #define FMODE_NONOTIFY         ((__force fmode_t)0x1000000)
@@ -343,8 +347,7 @@ struct address_space_operations {
        void (*invalidatepage) (struct page *, unsigned int, unsigned int);
        int (*releasepage) (struct page *, gfp_t);
        void (*freepage)(struct page *);
-       ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
-                       loff_t offset, unsigned long nr_segs);
+       ssize_t (*direct_IO)(int, struct kiocb *, struct iov_iter *iter, loff_t offset);
        int (*get_xip_mem)(struct address_space *, pgoff_t, int,
                                                void **, unsigned long *);
        /*
@@ -1448,6 +1451,8 @@ struct block_device_operations;
 #define HAVE_COMPAT_IOCTL 1
 #define HAVE_UNLOCKED_IOCTL 1
 
+struct iov_iter;
+
 struct file_operations {
        struct module *owner;
        loff_t (*llseek) (struct file *, loff_t, int);
@@ -1455,6 +1460,8 @@ struct file_operations {
        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
+       ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
+       ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
        int (*iterate) (struct file *, struct dir_context *);
        unsigned int (*poll) (struct file *, struct poll_table_struct *);
        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
@@ -2404,20 +2411,18 @@ extern int generic_file_readonly_mmap(struct file *, struct vm_area_struct *);
 extern int generic_file_remap_pages(struct vm_area_struct *, unsigned long addr,
                unsigned long size, pgoff_t pgoff);
 int generic_write_checks(struct file *file, loff_t *pos, size_t *count, int isblk);
-extern ssize_t generic_file_aio_read(struct kiocb *, const struct iovec *, unsigned long, loff_t);
-extern ssize_t __generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long);
-extern ssize_t generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long, loff_t);
-extern ssize_t generic_file_direct_write(struct kiocb *, const struct iovec *,
-               unsigned long *, loff_t, size_t, size_t);
+extern ssize_t generic_file_read_iter(struct kiocb *, struct iov_iter *);
+extern ssize_t __generic_file_write_iter(struct kiocb *, struct iov_iter *);
+extern ssize_t generic_file_write_iter(struct kiocb *, struct iov_iter *);
+extern ssize_t generic_file_direct_write(struct kiocb *, struct iov_iter *, loff_t);
 extern ssize_t generic_perform_write(struct file *, struct iov_iter *, loff_t);
 extern ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos);
 extern ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos);
-extern int generic_segment_checks(const struct iovec *iov,
-               unsigned long *nr_segs, size_t *count, int access_flags);
+extern ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos);
+extern ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos);
 
 /* fs/block_dev.c */
-extern ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov,
-                               unsigned long nr_segs, loff_t pos);
+extern ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from);
 extern int blkdev_fsync(struct file *filp, loff_t start, loff_t end,
                        int datasync);
 extern void block_sync_page(struct page *page);
@@ -2427,7 +2432,7 @@ extern ssize_t generic_file_splice_read(struct file *, loff_t *,
                struct pipe_inode_info *, size_t, unsigned int);
 extern ssize_t default_file_splice_read(struct file *, loff_t *,
                struct pipe_inode_info *, size_t, unsigned int);
-extern ssize_t generic_file_splice_write(struct pipe_inode_info *,
+extern ssize_t iter_file_splice_write(struct pipe_inode_info *,
                struct file *, loff_t *, size_t, unsigned int);
 extern ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe,
                struct file *out, loff_t *, size_t len, unsigned int flags);
@@ -2477,16 +2482,16 @@ enum {
 void dio_end_io(struct bio *bio, int error);
 
 ssize_t __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
-       struct block_device *bdev, const struct iovec *iov, loff_t offset,
-       unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io,
+       struct block_device *bdev, struct iov_iter *iter, loff_t offset,
+       get_block_t get_block, dio_iodone_t end_io,
        dio_submit_t submit_io, int flags);
 
 static inline ssize_t blockdev_direct_IO(int rw, struct kiocb *iocb,
-               struct inode *inode, const struct iovec *iov, loff_t offset,
-               unsigned long nr_segs, get_block_t get_block)
+               struct inode *inode, struct iov_iter *iter, loff_t offset,
+               get_block_t get_block)
 {
-       return __blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
-                                   offset, nr_segs, get_block, NULL, NULL,
+       return __blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iter,
+                                   offset, get_block, NULL, NULL,
                                    DIO_LOCKING | DIO_SKIP_HOLES);
 }
 #endif
index d2b16704624c6c6122a09457e00ef716ef8d0393..8cfb50f385299820d40a0bd193a60348713945ba 100644 (file)
@@ -498,7 +498,10 @@ static inline int twl6030_mmc_card_detect(struct device *dev, int slot)
 #define RES_GRP_ALL            0x7     /* All resource groups */
 
 #define RES_TYPE2_R0           0x0
+#define RES_TYPE2_R1           0x1
+#define RES_TYPE2_R2           0x2
 
+#define RES_TYPE_R0            0x0
 #define RES_TYPE_ALL           0x7
 
 /* Resource states */
@@ -671,6 +674,7 @@ struct twl4030_power_data {
        struct twl4030_script **scripts;
        unsigned num;
        struct twl4030_resconfig *resource_config;
+       struct twl4030_resconfig *board_config;
 #define TWL4030_RESCONFIG_UNDEF        ((u8)-1)
        bool use_poweroff;      /* Board is wired for TWL poweroff */
 };
index f194ccb8539c9b7d95af35b00a426ce2a06b61a6..6bff13f740505090eb53be8b4d91e0fe805a5191 100644 (file)
@@ -1711,6 +1711,7 @@ enum ieee80211_eid {
        WLAN_EID_RRM_ENABLED_CAPABILITIES = 70,
        WLAN_EID_MULTIPLE_BSSID = 71,
        WLAN_EID_BSS_COEX_2040 = 72,
+       WLAN_EID_BSS_INTOLERANT_CHL_REPORT = 73,
        WLAN_EID_OVERLAP_BSS_SCAN_PARAM = 74,
        WLAN_EID_RIC_DESCRIPTOR = 75,
        WLAN_EID_MMIE = 76,
index 1085ffeef956589d77dea0a426feedf6450fd20f..fd22789d7b2ed3e8003829d6da9be5e57d21a74c 100644 (file)
 #include <linux/netdevice.h>
 #include <uapi/linux/if_bridge.h>
 
+struct br_ip {
+       union {
+               __be32  ip4;
+#if IS_ENABLED(CONFIG_IPV6)
+               struct in6_addr ip6;
+#endif
+       } u;
+       __be16          proto;
+       __u16           vid;
+};
+
+struct br_ip_list {
+       struct list_head list;
+       struct br_ip addr;
+};
+
 extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __user *));
 
 typedef int br_should_route_hook_t(struct sk_buff *skb);
 extern br_should_route_hook_t __rcu *br_should_route_hook;
+int br_multicast_list_adjacent(struct net_device *dev,
+                              struct list_head *br_ip_list);
+bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto);
 
 #endif
index a86784dec3d34fcafb7e5d1b377a9d73b2d7717e..119130e9298b21ef0aa2a75e90a7c756318492a7 100644 (file)
@@ -10,8 +10,9 @@ struct ifla_vf_info {
        __u8 mac[32];
        __u32 vlan;
        __u32 qos;
-       __u32 tx_rate;
        __u32 spoofchk;
        __u32 linkstate;
+       __u32 min_tx_rate;
+       __u32 max_tx_rate;
 };
 #endif /* _LINUX_IF_LINK_H */
index a9a53b12397b0c4fd29a11cbf5f9cbbd18944fd8..6b2c7cf352a5582aff86da52f4feac7ae7d208e4 100644 (file)
@@ -57,6 +57,9 @@ struct macvlan_dev {
        netdev_features_t       tap_features;
        int                     minor;
        int                     nest_level;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       struct netpoll          *netpoll;
+#endif
 };
 
 static inline void macvlan_count_rx(const struct macvlan_dev *vlan,
index b2acc4a1b13c9bd906869bc52d501f53f67d3c87..4967916fe4ac8c6732930d768a2e60fe1bec35c7 100644 (file)
@@ -106,7 +106,7 @@ struct vlan_pcpu_stats {
 
 #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
 
-extern struct net_device *__vlan_find_dev_deep(struct net_device *real_dev,
+extern struct net_device *__vlan_find_dev_deep_rcu(struct net_device *real_dev,
                                               __be16 vlan_proto, u16 vlan_id);
 extern struct net_device *vlan_dev_real_dev(const struct net_device *dev);
 extern u16 vlan_dev_vlan_id(const struct net_device *dev);
@@ -206,7 +206,7 @@ static inline int vlan_get_encap_level(struct net_device *dev)
 }
 #else
 static inline struct net_device *
-__vlan_find_dev_deep(struct net_device *real_dev,
+__vlan_find_dev_deep_rcu(struct net_device *real_dev,
                     __be16 vlan_proto, u16 vlan_id)
 {
        return NULL;
index 5a52f2c94f3f68530466c349024220c255068b4d..44bd6046e6e212db8a8e0146b0aa04ee10a7e70d 100644 (file)
@@ -164,11 +164,6 @@ unsigned capi_cmsg_header(_cmsg * cmsg, __u16 _ApplId,
                          __u8 _Command, __u8 _Subcommand,
                          __u16 _Messagenumber, __u32 _Controller);
 
-/*
- * capi_info2str generated a readable string for Capi2.0 reasons.
- */
-char *capi_info2str(__u16 reason);
-
 /*-----------------------------------------------------------------------*/
 
 /*
index 7bd2ad01e39c625ec4da93f095763b31a38b1f34..f7296e57d614f4f75f31e67754f0edc5ea36b563 100644 (file)
@@ -205,10 +205,10 @@ struct kretprobe_blackpoint {
        void *addr;
 };
 
-struct kprobe_blackpoint {
-       const char *name;
+struct kprobe_blacklist_entry {
+       struct list_head list;
        unsigned long start_addr;
-       unsigned long range;
+       unsigned long end_addr;
 };
 
 #ifdef CONFIG_KPROBES
@@ -265,6 +265,7 @@ extern void arch_disarm_kprobe(struct kprobe *p);
 extern int arch_init_kprobes(void);
 extern void show_registers(struct pt_regs *regs);
 extern void kprobes_inc_nmissed_count(struct kprobe *p);
+extern bool arch_within_kprobe_blacklist(unsigned long addr);
 
 struct kprobe_insn_cache {
        struct mutex mutex;
@@ -476,4 +477,18 @@ static inline int enable_jprobe(struct jprobe *jp)
        return enable_kprobe(&jp->kp);
 }
 
+#ifdef CONFIG_KPROBES
+/*
+ * Blacklist ganerating macro. Specify functions which is not probed
+ * by using this macro.
+ */
+#define __NOKPROBE_SYMBOL(fname)                       \
+static unsigned long __used                            \
+       __attribute__((section("_kprobe_blacklist")))   \
+       _kbl_addr_##fname = (unsigned long)fname;
+#define NOKPROBE_SYMBOL(fname) __NOKPROBE_SYMBOL(fname)
+#else
+#define NOKPROBE_SYMBOL(fname)
+#endif
+
 #endif /* _LINUX_KPROBES_H */
index 31c0cd1c941a6c122506cc3158be203391617460..de9e46e6bcc97aa66b3500cad82bcdaf14402556 100644 (file)
@@ -304,6 +304,30 @@ static inline int ktime_compare(const ktime_t cmp1, const ktime_t cmp2)
        return 0;
 }
 
+/**
+ * ktime_after - Compare if a ktime_t value is bigger than another one.
+ * @cmp1:      comparable1
+ * @cmp2:      comparable2
+ *
+ * Return: true if cmp1 happened after cmp2.
+ */
+static inline bool ktime_after(const ktime_t cmp1, const ktime_t cmp2)
+{
+       return ktime_compare(cmp1, cmp2) > 0;
+}
+
+/**
+ * ktime_before - Compare if a ktime_t value is smaller than another one.
+ * @cmp1:      comparable1
+ * @cmp2:      comparable2
+ *
+ * Return: true if cmp1 happened before cmp2.
+ */
+static inline bool ktime_before(const ktime_t cmp1, const ktime_t cmp2)
+{
+       return ktime_compare(cmp1, cmp2) < 0;
+}
+
 static inline s64 ktime_to_us(const ktime_t kt)
 {
        struct timeval tv = ktime_to_timeval(kt);
index 970c68197c698898df483198995d9a0c558c0770..ec4e3bd83d474e581bb3e4607c3c9c5f4e5319c8 100644 (file)
@@ -586,7 +586,7 @@ void mark_page_dirty(struct kvm *kvm, gfn_t gfn);
 
 void kvm_vcpu_block(struct kvm_vcpu *vcpu);
 void kvm_vcpu_kick(struct kvm_vcpu *vcpu);
-bool kvm_vcpu_yield_to(struct kvm_vcpu *target);
+int kvm_vcpu_yield_to(struct kvm_vcpu *target);
 void kvm_vcpu_on_spin(struct kvm_vcpu *vcpu);
 void kvm_load_guest_fpu(struct kvm_vcpu *vcpu);
 void kvm_put_guest_fpu(struct kvm_vcpu *vcpu);
index 3447bead962015d1dce95a694d85c2d4ba7d4fd2..b12f4bbd064ce891c0f844b4d5180710ff613b0d 100644 (file)
@@ -450,7 +450,6 @@ struct mlx4_caps {
        int                     reserved_qps_base[MLX4_NUM_QP_REGION];
        int                     log_num_macs;
        int                     log_num_vlans;
-       int                     log_num_prios;
        enum mlx4_port_type     port_type[MLX4_MAX_PORTS + 1];
        u8                      supported_type[MLX4_MAX_PORTS + 1];
        u8                      suggested_type[MLX4_MAX_PORTS + 1];
@@ -578,6 +577,9 @@ struct mlx4_cq {
 
        u32                     cons_index;
 
+       u16                     irq;
+       bool                    irq_affinity_change;
+
        __be32                 *set_ci_db;
        __be32                 *arm_db;
        int                     arm_sn;
index 204a677438049b13570e779f36078e0450298614..b1990c5524e1cea5a0d52f618f0846a1fd442dfe 100644 (file)
@@ -321,7 +321,7 @@ extern bool parameq(const char *name1, const char *name2);
 extern bool parameqn(const char *name1, const char *name2, size_t n);
 
 /* Called on module insert or kernel boot */
-extern int parse_args(const char *name,
+extern char *parse_args(const char *name,
                      char *args,
                      const struct kernel_param *params,
                      unsigned num,
index c26d0ec2ef3a90b7a988da8c31f9667716c78392..d99800cbdcf3b7b4029e4b3f325aec6b1e4382dc 100644 (file)
@@ -42,9 +42,11 @@ enum {
        NETIF_F_TSO6_BIT,               /* ... TCPv6 segmentation */
        NETIF_F_FSO_BIT,                /* ... FCoE segmentation */
        NETIF_F_GSO_GRE_BIT,            /* ... GRE with TSO */
+       NETIF_F_GSO_GRE_CSUM_BIT,       /* ... GRE with csum with TSO */
        NETIF_F_GSO_IPIP_BIT,           /* ... IPIP tunnel with TSO */
        NETIF_F_GSO_SIT_BIT,            /* ... SIT tunnel with TSO */
        NETIF_F_GSO_UDP_TUNNEL_BIT,     /* ... UDP TUNNEL with TSO */
+       NETIF_F_GSO_UDP_TUNNEL_CSUM_BIT,/* ... UDP TUNNEL with TSO & CSUM */
        NETIF_F_GSO_MPLS_BIT,           /* ... MPLS segmentation */
        /**/NETIF_F_GSO_LAST =          /* last bit, see GSO_MASK */
                NETIF_F_GSO_MPLS_BIT,
@@ -111,9 +113,11 @@ enum {
 #define NETIF_F_RXFCS          __NETIF_F(RXFCS)
 #define NETIF_F_RXALL          __NETIF_F(RXALL)
 #define NETIF_F_GSO_GRE                __NETIF_F(GSO_GRE)
+#define NETIF_F_GSO_GRE_CSUM   __NETIF_F(GSO_GRE_CSUM)
 #define NETIF_F_GSO_IPIP       __NETIF_F(GSO_IPIP)
 #define NETIF_F_GSO_SIT                __NETIF_F(GSO_SIT)
 #define NETIF_F_GSO_UDP_TUNNEL __NETIF_F(GSO_UDP_TUNNEL)
+#define NETIF_F_GSO_UDP_TUNNEL_CSUM __NETIF_F(GSO_UDP_TUNNEL_CSUM)
 #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)
index 6c1ae9fd9505e079eb7b186ce860ff8139d7ef9d..66f9a04ec27041445afddbb70729b039719387c5 100644 (file)
@@ -56,9 +56,6 @@ struct device;
 struct phy_device;
 /* 802.11 specific */
 struct wireless_dev;
-                                       /* source back-compat hooks */
-#define SET_ETHTOOL_OPS(netdev,ops) \
-       ( (netdev)->ethtool_ops = (ops) )
 
 void netdev_set_default_ethtool_ops(struct net_device *dev,
                                    const struct ethtool_ops *ops);
@@ -853,7 +850,8 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
  *     SR-IOV management functions.
  * int (*ndo_set_vf_mac)(struct net_device *dev, int vf, u8* mac);
  * int (*ndo_set_vf_vlan)(struct net_device *dev, int vf, u16 vlan, u8 qos);
- * int (*ndo_set_vf_tx_rate)(struct net_device *dev, int vf, int rate);
+ * int (*ndo_set_vf_rate)(struct net_device *dev, int vf, int min_tx_rate,
+ *                       int max_tx_rate);
  * 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);
@@ -1047,8 +1045,9 @@ struct net_device_ops {
                                                  int queue, u8 *mac);
        int                     (*ndo_set_vf_vlan)(struct net_device *dev,
                                                   int queue, u16 vlan, u8 qos);
-       int                     (*ndo_set_vf_tx_rate)(struct net_device *dev,
-                                                     int vf, int rate);
+       int                     (*ndo_set_vf_rate)(struct net_device *dev,
+                                                  int vf, int min_tx_rate,
+                                                  int max_tx_rate);
        int                     (*ndo_set_vf_spoofchk)(struct net_device *dev,
                                                       int vf, bool setting);
        int                     (*ndo_get_vf_config)(struct net_device *dev,
@@ -2634,6 +2633,7 @@ int dev_get_phys_port_id(struct net_device *dev,
                         struct netdev_phys_port_id *ppid);
 int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
                        struct netdev_queue *txq);
+int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb);
 int dev_forward_skb(struct net_device *dev, struct sk_buff *skb);
 bool is_skb_forwardable(struct net_device *dev, struct sk_buff *skb);
 
@@ -3003,6 +3003,15 @@ int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
                   struct netdev_hw_addr_list *from_list, int addr_len);
 void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
                      struct netdev_hw_addr_list *from_list, int addr_len);
+int __hw_addr_sync_dev(struct netdev_hw_addr_list *list,
+                      struct net_device *dev,
+                      int (*sync)(struct net_device *, const unsigned char *),
+                      int (*unsync)(struct net_device *,
+                                    const unsigned char *));
+void __hw_addr_unsync_dev(struct netdev_hw_addr_list *list,
+                         struct net_device *dev,
+                         int (*unsync)(struct net_device *,
+                                       const unsigned char *));
 void __hw_addr_init(struct netdev_hw_addr_list *list);
 
 /* Functions used for device addresses handling */
@@ -3023,6 +3032,38 @@ void dev_uc_unsync(struct net_device *to, struct net_device *from);
 void dev_uc_flush(struct net_device *dev);
 void dev_uc_init(struct net_device *dev);
 
+/**
+ *  __dev_uc_sync - Synchonize device's unicast list
+ *  @dev:  device to sync
+ *  @sync: function to call if address should be added
+ *  @unsync: function to call if address should be removed
+ *
+ *  Add newly added addresses to the interface, and release
+ *  addresses that have been deleted.
+ **/
+static inline int __dev_uc_sync(struct net_device *dev,
+                               int (*sync)(struct net_device *,
+                                           const unsigned char *),
+                               int (*unsync)(struct net_device *,
+                                             const unsigned char *))
+{
+       return __hw_addr_sync_dev(&dev->uc, dev, sync, unsync);
+}
+
+/**
+ *  __dev_uc_unsync - Remove synchonized addresses from device
+ *  @dev:  device to sync
+ *  @unsync: function to call if address should be removed
+ *
+ *  Remove all addresses that were added to the device by dev_uc_sync().
+ **/
+static inline void __dev_uc_unsync(struct net_device *dev,
+                                  int (*unsync)(struct net_device *,
+                                                const unsigned char *))
+{
+       __hw_addr_unsync_dev(&dev->uc, dev, unsync);
+}
+
 /* Functions used for multicast addresses handling */
 int dev_mc_add(struct net_device *dev, const unsigned char *addr);
 int dev_mc_add_global(struct net_device *dev, const unsigned char *addr);
@@ -3035,6 +3076,38 @@ void dev_mc_unsync(struct net_device *to, struct net_device *from);
 void dev_mc_flush(struct net_device *dev);
 void dev_mc_init(struct net_device *dev);
 
+/**
+ *  __dev_mc_sync - Synchonize device's multicast list
+ *  @dev:  device to sync
+ *  @sync: function to call if address should be added
+ *  @unsync: function to call if address should be removed
+ *
+ *  Add newly added addresses to the interface, and release
+ *  addresses that have been deleted.
+ **/
+static inline int __dev_mc_sync(struct net_device *dev,
+                               int (*sync)(struct net_device *,
+                                           const unsigned char *),
+                               int (*unsync)(struct net_device *,
+                                             const unsigned char *))
+{
+       return __hw_addr_sync_dev(&dev->mc, dev, sync, unsync);
+}
+
+/**
+ *  __dev_mc_unsync - Remove synchonized addresses from device
+ *  @dev:  device to sync
+ *  @unsync: function to call if address should be removed
+ *
+ *  Remove all addresses that were added to the device by dev_mc_sync().
+ **/
+static inline void __dev_mc_unsync(struct net_device *dev,
+                                  int (*unsync)(struct net_device *,
+                                                const unsigned char *))
+{
+       __hw_addr_unsync_dev(&dev->mc, dev, unsync);
+}
+
 /* Functions used for secondary unicast and multicast support */
 void dev_set_rx_mode(struct net_device *dev);
 void __dev_set_rx_mode(struct net_device *dev);
@@ -3180,6 +3253,20 @@ const char *netdev_drivername(const struct net_device *dev);
 
 void linkwatch_run_queue(void);
 
+static inline netdev_features_t netdev_intersect_features(netdev_features_t f1,
+                                                         netdev_features_t f2)
+{
+       if (f1 & NETIF_F_GEN_CSUM)
+               f1 |= (NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM);
+       if (f2 & NETIF_F_GEN_CSUM)
+               f2 |= (NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM);
+       f1 &= f2;
+       if (f1 & NETIF_F_GEN_CSUM)
+               f1 &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM);
+
+       return f1;
+}
+
 static inline netdev_features_t netdev_get_wanted_features(
        struct net_device *dev)
 {
@@ -3218,6 +3305,13 @@ static inline bool net_gso_ok(netdev_features_t features, int gso_type)
        BUILD_BUG_ON(SKB_GSO_TCP_ECN != (NETIF_F_TSO_ECN >> NETIF_F_GSO_SHIFT));
        BUILD_BUG_ON(SKB_GSO_TCPV6   != (NETIF_F_TSO6 >> NETIF_F_GSO_SHIFT));
        BUILD_BUG_ON(SKB_GSO_FCOE    != (NETIF_F_FSO >> NETIF_F_GSO_SHIFT));
+       BUILD_BUG_ON(SKB_GSO_GRE     != (NETIF_F_GSO_GRE >> NETIF_F_GSO_SHIFT));
+       BUILD_BUG_ON(SKB_GSO_GRE_CSUM != (NETIF_F_GSO_GRE_CSUM >> NETIF_F_GSO_SHIFT));
+       BUILD_BUG_ON(SKB_GSO_IPIP    != (NETIF_F_GSO_IPIP >> NETIF_F_GSO_SHIFT));
+       BUILD_BUG_ON(SKB_GSO_SIT     != (NETIF_F_GSO_SIT >> NETIF_F_GSO_SHIFT));
+       BUILD_BUG_ON(SKB_GSO_UDP_TUNNEL != (NETIF_F_GSO_UDP_TUNNEL >> NETIF_F_GSO_SHIFT));
+       BUILD_BUG_ON(SKB_GSO_UDP_TUNNEL_CSUM != (NETIF_F_GSO_UDP_TUNNEL_CSUM >> NETIF_F_GSO_SHIFT));
+       BUILD_BUG_ON(SKB_GSO_MPLS    != (NETIF_F_GSO_MPLS >> NETIF_F_GSO_SHIFT));
 
        return (features & feature) == feature;
 }
index b2e85e59f76085cb0e56294c78b63d3a04a0762a..6ec975748742793fd51c274314a208ea5cb697db 100644 (file)
@@ -3,11 +3,17 @@
 
 #include <uapi/linux/netfilter/nfnetlink_acct.h>
 
+enum {
+       NFACCT_NO_QUOTA         = -1,
+       NFACCT_UNDERQUOTA,
+       NFACCT_OVERQUOTA,
+};
 
 struct nf_acct;
 
 struct nf_acct *nfnl_acct_find_get(const char *filter_name);
 void nfnl_acct_put(struct nf_acct *acct);
 void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct);
-
+extern int nfnl_acct_overquota(const struct sk_buff *skb,
+                             struct nf_acct *nfacct);
 #endif /* _NFNL_ACCT_H */
index 034cda789a150f3d6a5b8416863bd48469aaacf8..9e572daa15d568cc0d7c82342a0d7fb3ad37c327 100644 (file)
@@ -46,7 +46,8 @@ struct netlink_kernel_cfg {
        unsigned int    flags;
        void            (*input)(struct sk_buff *skb);
        struct mutex    *cb_mutex;
-       void            (*bind)(int group);
+       int             (*bind)(int group);
+       void            (*unbind)(int group);
        bool            (*compare)(struct net *net, struct sock *sk);
 };
 
index 919576b8e2cfd612d5a2b852f1aa9674811585d1..e30f6059ecd642b44c0cc599344c0421b713958f 100644 (file)
@@ -459,13 +459,12 @@ extern int nfs3_removexattr (struct dentry *, const char *name);
 /*
  * linux/fs/nfs/direct.c
  */
-extern ssize_t nfs_direct_IO(int, struct kiocb *, const struct iovec *, loff_t,
-                       unsigned long);
+extern ssize_t nfs_direct_IO(int, struct kiocb *, struct iov_iter *, loff_t);
 extern ssize_t nfs_file_direct_read(struct kiocb *iocb,
-                       const struct iovec *iov, unsigned long nr_segs,
+                       struct iov_iter *iter,
                        loff_t pos, bool uio);
 extern ssize_t nfs_file_direct_write(struct kiocb *iocb,
-                       const struct iovec *iov, unsigned long nr_segs,
+                       struct iov_iter *iter,
                        loff_t pos, bool uio);
 
 /*
index c8d7f3965fff913a55f158a53b7bc0944c1d4b61..20163b9a0eae70cfdfba688bab5dc08eed5fcfdb 100644 (file)
@@ -80,6 +80,22 @@ enum {
 
        IEEE802154_ATTR_FRAME_RETRIES,
 
+       IEEE802154_ATTR_LLSEC_ENABLED,
+       IEEE802154_ATTR_LLSEC_SECLEVEL,
+       IEEE802154_ATTR_LLSEC_KEY_MODE,
+       IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT,
+       IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED,
+       IEEE802154_ATTR_LLSEC_KEY_ID,
+       IEEE802154_ATTR_LLSEC_FRAME_COUNTER,
+       IEEE802154_ATTR_LLSEC_KEY_BYTES,
+       IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES,
+       IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS,
+       IEEE802154_ATTR_LLSEC_FRAME_TYPE,
+       IEEE802154_ATTR_LLSEC_CMD_FRAME_ID,
+       IEEE802154_ATTR_LLSEC_SECLEVELS,
+       IEEE802154_ATTR_LLSEC_DEV_OVERRIDE,
+       IEEE802154_ATTR_LLSEC_DEV_KEY_MODE,
+
        __IEEE802154_ATTR_MAX,
 };
 
@@ -134,6 +150,21 @@ enum {
 
        IEEE802154_SET_MACPARAMS,
 
+       IEEE802154_LLSEC_GETPARAMS,
+       IEEE802154_LLSEC_SETPARAMS,
+       IEEE802154_LLSEC_LIST_KEY,
+       IEEE802154_LLSEC_ADD_KEY,
+       IEEE802154_LLSEC_DEL_KEY,
+       IEEE802154_LLSEC_LIST_DEV,
+       IEEE802154_LLSEC_ADD_DEV,
+       IEEE802154_LLSEC_DEL_DEV,
+       IEEE802154_LLSEC_LIST_DEVKEY,
+       IEEE802154_LLSEC_ADD_DEVKEY,
+       IEEE802154_LLSEC_DEL_DEVKEY,
+       IEEE802154_LLSEC_LIST_SECLEVEL,
+       IEEE802154_LLSEC_ADD_SECLEVEL,
+       IEEE802154_LLSEC_DEL_SECLEVEL,
+
        __IEEE802154_CMD_MAX,
 };
 
index a50173ca1d729aba84bc00c2572b40a91ec93140..2bf403195c09fd5470cab096409c8ee7c260285f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Definitions for the NVM Express interface
- * Copyright (c) 2011-2013, Intel Corporation.
+ * Copyright (c) 2011-2014, 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,
  * 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 _LINUX_NVME_H
@@ -66,8 +62,8 @@ enum {
 
 #define NVME_VS(major, minor)  (major << 16 | minor)
 
-extern unsigned char io_timeout;
-#define NVME_IO_TIMEOUT        (io_timeout * HZ)
+extern unsigned char nvme_io_timeout;
+#define NVME_IO_TIMEOUT        (nvme_io_timeout * HZ)
 
 /*
  * Represents an NVM Express device.  Each nvme_dev is a PCI function.
@@ -94,7 +90,7 @@ struct nvme_dev {
        struct miscdevice miscdev;
        work_func_t reset_workfn;
        struct work_struct reset_work;
-       struct notifier_block nb;
+       struct work_struct cpu_work;
        char name[12];
        char serial[20];
        char model[40];
@@ -103,6 +99,7 @@ struct nvme_dev {
        u32 stripe_size;
        u16 oncs;
        u16 abort_limit;
+       u8 vwc;
        u8 initialized;
 };
 
@@ -159,7 +156,6 @@ struct nvme_iod *nvme_map_user_pages(struct nvme_dev *dev, int write,
 void nvme_unmap_user_pages(struct nvme_dev *dev, int write,
                        struct nvme_iod *iod);
 int nvme_submit_io_cmd(struct nvme_dev *, struct nvme_command *, u32 *);
-int nvme_submit_flush_data(struct nvme_queue *nvmeq, struct nvme_ns *ns);
 int nvme_submit_admin_cmd(struct nvme_dev *, struct nvme_command *,
                                                        u32 *result);
 int nvme_identify(struct nvme_dev *, unsigned nsid, unsigned cns,
index 881a7c3571f4617b99e0845995800d98eb4f9349..a70c9493d55a4c01976b093b965cc91b270aaf92 100644 (file)
@@ -22,12 +22,12 @@ extern struct phy_device *of_phy_connect(struct net_device *dev,
 struct phy_device *of_phy_attach(struct net_device *dev,
                                 struct device_node *phy_np, u32 flags,
                                 phy_interface_t iface);
-extern struct phy_device *of_phy_connect_fixed_link(struct net_device *dev,
-                                        void (*hndlr)(struct net_device *),
-                                        phy_interface_t iface);
 
 extern struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np);
 
+extern void of_mdiobus_link_phydev(struct mii_bus *mdio,
+                                  struct phy_device *phydev);
+
 #else /* CONFIG_OF */
 static inline int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
 {
@@ -59,17 +59,30 @@ static inline struct phy_device *of_phy_attach(struct net_device *dev,
        return NULL;
 }
 
-static inline struct phy_device *of_phy_connect_fixed_link(struct net_device *dev,
-                                                          void (*hndlr)(struct net_device *),
-                                                          phy_interface_t iface)
+static inline struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np)
 {
        return NULL;
 }
 
-static inline struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np)
+static inline void of_mdiobus_link_phydev(struct mii_bus *mdio,
+                                         struct phy_device *phydev)
 {
-       return NULL;
 }
 #endif /* CONFIG_OF */
 
+#if defined(CONFIG_OF) && defined(CONFIG_FIXED_PHY)
+extern int of_phy_register_fixed_link(struct device_node *np);
+extern bool of_phy_is_fixed_link(struct device_node *np);
+#else
+static inline int of_phy_register_fixed_link(struct device_node *np)
+{
+       return -ENOSYS;
+}
+static inline bool of_phy_is_fixed_link(struct device_node *np)
+{
+       return false;
+}
+#endif
+
+
 #endif /* __LINUX_OF_MDIO_H */
index 71d9673c1b2c4532130240055939974679dd3dea..466bcd111d85d80916af2f7592dec3c3c539e441 100644 (file)
@@ -164,13 +164,17 @@ enum pci_dev_flags {
        /* INTX_DISABLE in PCI_COMMAND register disables MSI
         * generation too.
         */
-       PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG = (__force pci_dev_flags_t) 1,
+       PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG = (__force pci_dev_flags_t) (1 << 0),
        /* Device configuration is irrevocably lost if disabled into D3 */
-       PCI_DEV_FLAGS_NO_D3 = (__force pci_dev_flags_t) 2,
+       PCI_DEV_FLAGS_NO_D3 = (__force pci_dev_flags_t) (1 << 1),
        /* Provide indication device is assigned by a Virtual Machine Manager */
-       PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) 4,
+       PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) (1 << 2),
        /* Flag for quirk use to store if quirk-specific ACS is enabled */
-       PCI_DEV_FLAGS_ACS_ENABLED_QUIRK = (__force pci_dev_flags_t) 8,
+       PCI_DEV_FLAGS_ACS_ENABLED_QUIRK = (__force pci_dev_flags_t) (1 << 3),
+       /* Flag to indicate the device uses dma_alias_devfn */
+       PCI_DEV_FLAGS_DMA_ALIAS_DEVFN = (__force pci_dev_flags_t) (1 << 4),
+       /* Use a PCIe-to-PCI bridge alias even if !pci_is_pcie */
+       PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS = (__force pci_dev_flags_t) (1 << 5),
 };
 
 enum pci_irq_reroute_variant {
@@ -268,6 +272,7 @@ struct pci_dev {
        u8              rom_base_reg;   /* which config register controls the ROM */
        u8              pin;            /* which interrupt pin this device uses */
        u16             pcie_flags_reg; /* cached PCIe Capabilities Register */
+       u8              dma_alias_devfn;/* devfn of DMA alias, if any */
 
        struct pci_driver *driver;      /* which driver has allocated this device */
        u64             dma_mask;       /* Mask of the bits of bus address this
@@ -1809,6 +1814,10 @@ static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev)
 }
 #endif
 
+int pci_for_each_dma_alias(struct pci_dev *pdev,
+                          int (*fn)(struct pci_dev *pdev,
+                                    u16 alias, void *data), void *data);
+
 /**
  * pci_find_upstream_pcie_bridge - find upstream PCIe-to-PCI bridge of a device
  * @pdev: the PCI device
index a9209118d80f9b61ccef036a3b1e67e91ceee925..707617a8c0f6c647b8f3c9d210b833638c20eb02 100644 (file)
@@ -166,6 +166,11 @@ struct perf_event;
  */
 #define PERF_EVENT_TXN 0x1
 
+/**
+ * pmu::capabilities flags
+ */
+#define PERF_PMU_CAP_NO_INTERRUPT              0x01
+
 /**
  * struct pmu - generic performance monitoring unit
  */
@@ -178,6 +183,11 @@ struct pmu {
        const char                      *name;
        int                             type;
 
+       /*
+        * various common per-pmu feature flags
+        */
+       int                             capabilities;
+
        int * __percpu                  pmu_disable_count;
        struct perf_cpu_context * __percpu pmu_cpu_context;
        int                             task_ctx_nr;
@@ -696,7 +706,8 @@ extern struct perf_guest_info_callbacks *perf_guest_cbs;
 extern int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks);
 extern int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks);
 
-extern void perf_event_comm(struct task_struct *tsk);
+extern void perf_event_exec(void);
+extern void perf_event_comm(struct task_struct *tsk, bool exec);
 extern void perf_event_fork(struct task_struct *tsk);
 
 /* Callchains */
@@ -773,7 +784,7 @@ extern void perf_event_enable(struct perf_event *event);
 extern void perf_event_disable(struct perf_event *event);
 extern int __perf_event_disable(void *info);
 extern void perf_event_task_tick(void);
-#else
+#else /* !CONFIG_PERF_EVENTS: */
 static inline void
 perf_event_task_sched_in(struct task_struct *prev,
                         struct task_struct *task)                      { }
@@ -803,7 +814,8 @@ static inline int perf_unregister_guest_info_callbacks
 (struct perf_guest_info_callbacks *callbacks)                          { return 0; }
 
 static inline void perf_event_mmap(struct vm_area_struct *vma)         { }
-static inline void perf_event_comm(struct task_struct *tsk)            { }
+static inline void perf_event_exec(void)                               { }
+static inline void perf_event_comm(struct task_struct *tsk, bool exec) { }
 static inline void perf_event_fork(struct task_struct *tsk)            { }
 static inline void perf_event_init(void)                               { }
 static inline int  perf_swevent_get_recursion_context(void)            { return -1; }
index 4d0221fd0688df5d572072d8b9f871a5637ff460..864ddafad8cc2f0697b181d9c113ca02ef715613 100644 (file)
@@ -198,6 +198,13 @@ static inline struct mii_bus *mdiobus_alloc(void)
 int mdiobus_register(struct mii_bus *bus);
 void mdiobus_unregister(struct mii_bus *bus);
 void mdiobus_free(struct mii_bus *bus);
+struct mii_bus *devm_mdiobus_alloc_size(struct device *dev, int sizeof_priv);
+static inline struct mii_bus *devm_mdiobus_alloc(struct device *dev)
+{
+       return devm_mdiobus_alloc_size(dev, 0);
+}
+
+void devm_mdiobus_free(struct device *dev, struct mii_bus *bus);
 struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr);
 int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
 int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
@@ -666,6 +673,7 @@ static inline int phy_read_status(struct phy_device *phydev)
        return phydev->drv->read_status(phydev);
 }
 
+int genphy_config_init(struct phy_device *phydev);
 int genphy_setup_forced(struct phy_device *phydev);
 int genphy_restart_aneg(struct phy_device *phydev);
 int genphy_config_aneg(struct phy_device *phydev);
index 509d8f5f984e3985cffe178b8a335498f30bcf50..ae612acebb53d2f0bf121226afde877cb4d32f91 100644 (file)
@@ -9,15 +9,31 @@ struct fixed_phy_status {
        int asym_pause;
 };
 
+struct device_node;
+
 #ifdef CONFIG_FIXED_PHY
 extern int fixed_phy_add(unsigned int irq, int phy_id,
                         struct fixed_phy_status *status);
+extern int fixed_phy_register(unsigned int irq,
+                             struct fixed_phy_status *status,
+                             struct device_node *np);
+extern void fixed_phy_del(int phy_addr);
 #else
 static inline int fixed_phy_add(unsigned int irq, int phy_id,
                                struct fixed_phy_status *status)
 {
        return -ENODEV;
 }
+static inline int fixed_phy_register(unsigned int irq,
+                                    struct fixed_phy_status *status,
+                                    struct device_node *np)
+{
+       return -ENODEV;
+}
+static inline int fixed_phy_del(int phy_addr)
+{
+       return -ENODEV;
+}
 #endif /* CONFIG_FIXED_PHY */
 
 /*
diff --git a/include/linux/platform_data/leds-pca9685.h b/include/linux/platform_data/leds-pca9685.h
deleted file mode 100644 (file)
index 778e9e4..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2013 Maximilian Güntner <maximilian.guentner@gmail.com>
- *
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License.  See the file COPYING in the main
- * directory of this archive for more details.
- *
- * Based on leds-pca963x.h by Peter Meerwald <p.meerwald@bct-electronic.com>
- *
- * LED driver for the NXP PCA9685 PWM chip
- *
- */
-
-#ifndef __LINUX_PCA9685_H
-#define __LINUX_PCA9685_H
-
-#include <linux/leds.h>
-
-enum pca9685_outdrv {
-       PCA9685_OPEN_DRAIN,
-       PCA9685_TOTEM_POLE,
-};
-
-enum pca9685_inverted {
-       PCA9685_NOT_INVERTED,
-       PCA9685_INVERTED,
-};
-
-struct pca9685_platform_data {
-       struct led_platform_data leds;
-       enum pca9685_outdrv outdrv;
-       enum pca9685_inverted inverted;
-};
-
-#endif /* __LINUX_PCA9685_H */
diff --git a/include/linux/platform_data/shtc1.h b/include/linux/platform_data/shtc1.h
new file mode 100644 (file)
index 0000000..7b8c353
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2014 Sensirion AG, Switzerland
+ * Author: Johannes Winkelmann <johannes.winkelmann@sensirion.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __SHTC1_H_
+#define __SHTC1_H_
+
+struct shtc1_platform_data {
+       bool blocking_io;
+       bool high_precision;
+};
+#endif /* __SHTC1_H_ */
diff --git a/include/linux/platform_data/st21nfca.h b/include/linux/platform_data/st21nfca.h
new file mode 100644 (file)
index 0000000..1730312
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Driver include for the ST21NFCA NFC chip.
+ *
+ * Copyright (C) 2014  STMicroelectronics SAS. 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 that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _ST21NFCA_HCI_H_
+#define _ST21NFCA_HCI_H_
+
+#include <linux/i2c.h>
+
+#define ST21NFCA_HCI_DRIVER_NAME "st21nfca_hci"
+
+struct st21nfca_nfc_platform_data {
+       unsigned int gpio_irq;
+       unsigned int gpio_ena;
+       unsigned int irq_polarity;
+};
+
+#endif /* _ST21NFCA_HCI_H_ */
index 4d09f6eab359f8e78b2c5b89b45797dd8fec17de..20bcb55498cd5714028bf7ccf9474b9fa36aa7aa 100644 (file)
  * struct rfkill_gpio_platform_data - platform data for rfkill gpio device.
  * for unused gpio's, the expected value is -1.
  * @name:              name for the gpio rf kill instance
- * @reset_gpio:                GPIO which is used for reseting rfkill switch
- * @shutdown_gpio:     GPIO which is used for shutdown of rfkill switch
- * @power_clk_name:    [optional] name of clk to turn off while blocked
- * @gpio_runtime_close:        clean up platform specific gpio configuration
- * @gpio_runtime_setup:        set up platform specific gpio configuration
  */
 
 struct rfkill_gpio_platform_data {
        char                    *name;
-       int                     reset_gpio;
-       int                     shutdown_gpio;
-       const char              *power_clk_name;
        enum rfkill_type        type;
-       void    (*gpio_runtime_close)(struct platform_device *);
-       int     (*gpio_runtime_setup)(struct platform_device *);
 };
 
 #endif /* __RFKILL_GPIO_H */
index d69cf637a15a3093863cd33f09929b87d73fd0a9..49a4d6f59108f957d4534190c161344981c48e46 100644 (file)
@@ -97,7 +97,7 @@ __ring_buffer_alloc(unsigned long size, unsigned flags, struct lock_class_key *k
        __ring_buffer_alloc((size), (flags), &__key);   \
 })
 
-void ring_buffer_wait(struct ring_buffer *buffer, int cpu);
+int ring_buffer_wait(struct ring_buffer *buffer, int cpu);
 int ring_buffer_poll_wait(struct ring_buffer *buffer, int cpu,
                          struct file *filp, poll_table *poll_table);
 
index 03f3b05e8ec17dda4d7b8dcefcc1e723bc6e606c..8d79708146aa47d642d37e82ca3beea2eaa7c014 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <linux/atomic.h>
 
+struct optimistic_spin_queue;
 struct rw_semaphore;
 
 #ifdef CONFIG_RWSEM_GENERIC_SPINLOCK
@@ -23,9 +24,17 @@ struct rw_semaphore;
 #else
 /* All arch specific implementations share the same struct */
 struct rw_semaphore {
-       long                    count;
-       raw_spinlock_t          wait_lock;
-       struct list_head        wait_list;
+       long count;
+       raw_spinlock_t wait_lock;
+       struct list_head wait_list;
+#ifdef CONFIG_SMP
+       /*
+        * Write owner. Used as a speculative check to see
+        * if the owner is running on the cpu.
+        */
+       struct task_struct *owner;
+       struct optimistic_spin_queue *osq; /* spinner MCS lock */
+#endif
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
        struct lockdep_map      dep_map;
 #endif
@@ -55,11 +64,21 @@ static inline int rwsem_is_locked(struct rw_semaphore *sem)
 # define __RWSEM_DEP_MAP_INIT(lockname)
 #endif
 
+#if defined(CONFIG_SMP) && !defined(CONFIG_RWSEM_GENERIC_SPINLOCK)
+#define __RWSEM_INITIALIZER(name)                      \
+       { RWSEM_UNLOCKED_VALUE,                         \
+         __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock),     \
+         LIST_HEAD_INIT((name).wait_list),             \
+         NULL, /* owner */                             \
+         NULL /* mcs lock */                           \
+         __RWSEM_DEP_MAP_INIT(name) }
+#else
 #define __RWSEM_INITIALIZER(name)                      \
        { RWSEM_UNLOCKED_VALUE,                         \
          __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock),     \
          LIST_HEAD_INIT((name).wait_list)              \
          __RWSEM_DEP_MAP_INIT(name) }
+#endif
 
 #define DECLARE_RWSEM(name) \
        struct rw_semaphore name = __RWSEM_INITIALIZER(name)
index ea74596014a2175b4e379bb955096f386819227e..306f4f0c987a006f43f520413f7de3a780f98a23 100644 (file)
@@ -847,10 +847,10 @@ enum cpu_idle_type {
 };
 
 /*
- * Increase resolution of cpu_power calculations
+ * Increase resolution of cpu_capacity calculations
  */
-#define SCHED_POWER_SHIFT      10
-#define SCHED_POWER_SCALE      (1L << SCHED_POWER_SHIFT)
+#define SCHED_CAPACITY_SHIFT   10
+#define SCHED_CAPACITY_SCALE   (1L << SCHED_CAPACITY_SHIFT)
 
 /*
  * sched-domains (multiprocessor balancing) declarations:
@@ -862,7 +862,7 @@ enum cpu_idle_type {
 #define SD_BALANCE_FORK                0x0008  /* Balance on fork, clone */
 #define SD_BALANCE_WAKE                0x0010  /* Balance on wakeup */
 #define SD_WAKE_AFFINE         0x0020  /* Wake task to waking CPU */
-#define SD_SHARE_CPUPOWER      0x0080  /* Domain members share cpu power */
+#define SD_SHARE_CPUCAPACITY   0x0080  /* Domain members share cpu power */
 #define SD_SHARE_POWERDOMAIN   0x0100  /* Domain members share power domain */
 #define SD_SHARE_PKG_RESOURCES 0x0200  /* Domain members share cpu pkg resources */
 #define SD_SERIALIZE           0x0400  /* Only a single load balancing instance */
@@ -874,7 +874,7 @@ enum cpu_idle_type {
 #ifdef CONFIG_SCHED_SMT
 static inline const int cpu_smt_flags(void)
 {
-       return SD_SHARE_CPUPOWER | SD_SHARE_PKG_RESOURCES;
+       return SD_SHARE_CPUCAPACITY | SD_SHARE_PKG_RESOURCES;
 }
 #endif
 
@@ -1006,7 +1006,7 @@ typedef const int (*sched_domain_flags_f)(void);
 struct sd_data {
        struct sched_domain **__percpu sd;
        struct sched_group **__percpu sg;
-       struct sched_group_power **__percpu sgp;
+       struct sched_group_capacity **__percpu sgc;
 };
 
 struct sched_domain_topology_level {
@@ -2173,7 +2173,7 @@ static inline void sched_autogroup_fork(struct signal_struct *sig) { }
 static inline void sched_autogroup_exit(struct signal_struct *sig) { }
 #endif
 
-extern bool yield_to(struct task_struct *p, bool preempt);
+extern int yield_to(struct task_struct *p, bool preempt);
 extern void set_user_nice(struct task_struct *p, long nice);
 extern int task_prio(const struct task_struct *p);
 /**
@@ -2421,7 +2421,11 @@ extern long do_fork(unsigned long, unsigned long, unsigned long, int __user *, i
 struct task_struct *fork_idle(int);
 extern pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
 
-extern void set_task_comm(struct task_struct *tsk, const char *from);
+extern void __set_task_comm(struct task_struct *tsk, const char *from, bool exec);
+static inline void set_task_comm(struct task_struct *tsk, const char *from)
+{
+       __set_task_comm(tsk, from, false);
+}
 extern char *get_task_comm(char *to, struct task_struct *tsk);
 
 #ifdef CONFIG_SMP
index 08074a8101646d0c438415979124b3a5ff8283bb..ec89301ada418aff749bf924da1b7f63040ac048 100644 (file)
@@ -338,13 +338,18 @@ enum {
 
        SKB_GSO_GRE = 1 << 6,
 
-       SKB_GSO_IPIP = 1 << 7,
+       SKB_GSO_GRE_CSUM = 1 << 7,
 
-       SKB_GSO_SIT = 1 << 8,
+       SKB_GSO_IPIP = 1 << 8,
 
-       SKB_GSO_UDP_TUNNEL = 1 << 9,
+       SKB_GSO_SIT = 1 << 9,
+
+       SKB_GSO_UDP_TUNNEL = 1 << 10,
+
+       SKB_GSO_UDP_TUNNEL_CSUM = 1 << 11,
+
+       SKB_GSO_MPLS = 1 << 12,
 
-       SKB_GSO_MPLS = 1 << 10,
 };
 
 #if BITS_PER_LONG > 32
@@ -426,7 +431,7 @@ static inline u32 skb_mstamp_us_delta(const struct skb_mstamp *t1,
  *     @csum_start: Offset from skb->head where checksumming should start
  *     @csum_offset: Offset from csum_start where checksum should be stored
  *     @priority: Packet queueing priority
- *     @local_df: allow local fragmentation
+ *     @ignore_df: allow local fragmentation
  *     @cloned: Head may be cloned (check refcnt to be sure)
  *     @ip_summed: Driver fed us an IP checksum
  *     @nohdr: Payload reference only, must not modify header
@@ -514,7 +519,7 @@ struct sk_buff {
        };
        __u32                   priority;
        kmemcheck_bitfield_begin(flags1);
-       __u8                    local_df:1,
+       __u8                    ignore_df:1,
                                cloned:1,
                                ip_summed:2,
                                nohdr:1,
@@ -567,7 +572,10 @@ struct sk_buff {
         * headers if needed
         */
        __u8                    encapsulation:1;
-       /* 6/8 bit hole (depending on ndisc_nodetype presence) */
+       __u8                    encap_hdr_csum:1;
+       __u8                    csum_valid:1;
+       __u8                    csum_complete_sw:1;
+       /* 3/5 bit hole (depending on ndisc_nodetype presence) */
        kmemcheck_bitfield_end(flags2);
 
 #if defined CONFIG_NET_DMA || defined CONFIG_NET_RX_BUSY_POLL
@@ -739,7 +747,13 @@ struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src);
 int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask);
 struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t priority);
 struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t priority);
-struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask);
+struct sk_buff *__pskb_copy_fclone(struct sk_buff *skb, int headroom,
+                                  gfp_t gfp_mask, bool fclone);
+static inline struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom,
+                                         gfp_t gfp_mask)
+{
+       return __pskb_copy_fclone(skb, headroom, gfp_mask, false);
+}
 
 int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, gfp_t gfp_mask);
 struct sk_buff *skb_realloc_headroom(struct sk_buff *skb,
@@ -1840,6 +1854,18 @@ static inline int pskb_network_may_pull(struct sk_buff *skb, unsigned int len)
        return pskb_may_pull(skb, skb_network_offset(skb) + len);
 }
 
+static inline void skb_pop_rcv_encapsulation(struct sk_buff *skb)
+{
+       /* Only continue with checksum unnecessary if device indicated
+        * it is valid across encapsulation (skb->encapsulation was set).
+        */
+       if (skb->ip_summed == CHECKSUM_UNNECESSARY && !skb->encapsulation)
+               skb->ip_summed = CHECKSUM_NONE;
+
+       skb->encapsulation = 0;
+       skb->csum_valid = 0;
+}
+
 /*
  * CPUs often take a performance hit when accessing unaligned memory
  * locations. The actual performance hit varies, it can be small if the
@@ -2233,6 +2259,14 @@ static inline struct sk_buff *pskb_copy(struct sk_buff *skb,
        return __pskb_copy(skb, skb_headroom(skb), gfp_mask);
 }
 
+
+static inline struct sk_buff *pskb_copy_for_clone(struct sk_buff *skb,
+                                                 gfp_t gfp_mask)
+{
+       return __pskb_copy_fclone(skb, skb_headroom(skb), gfp_mask, true);
+}
+
+
 /**
  *     skb_clone_writable - is the header of a clone writable
  *     @skb: buffer to check
@@ -2716,7 +2750,7 @@ __sum16 __skb_checksum_complete(struct sk_buff *skb);
 
 static inline int skb_csum_unnecessary(const struct sk_buff *skb)
 {
-       return skb->ip_summed & CHECKSUM_UNNECESSARY;
+       return ((skb->ip_summed & CHECKSUM_UNNECESSARY) || skb->csum_valid);
 }
 
 /**
@@ -2741,6 +2775,103 @@ static inline __sum16 skb_checksum_complete(struct sk_buff *skb)
               0 : __skb_checksum_complete(skb);
 }
 
+/* Check if we need to perform checksum complete validation.
+ *
+ * Returns true if checksum complete is needed, false otherwise
+ * (either checksum is unnecessary or zero checksum is allowed).
+ */
+static inline bool __skb_checksum_validate_needed(struct sk_buff *skb,
+                                                 bool zero_okay,
+                                                 __sum16 check)
+{
+       if (skb_csum_unnecessary(skb) || (zero_okay && !check)) {
+               skb->csum_valid = 1;
+               return false;
+       }
+
+       return true;
+}
+
+/* For small packets <= CHECKSUM_BREAK peform checksum complete directly
+ * in checksum_init.
+ */
+#define CHECKSUM_BREAK 76
+
+/* Validate (init) checksum based on checksum complete.
+ *
+ * Return values:
+ *   0: checksum is validated or try to in skb_checksum_complete. In the latter
+ *     case the ip_summed will not be CHECKSUM_UNNECESSARY and the pseudo
+ *     checksum is stored in skb->csum for use in __skb_checksum_complete
+ *   non-zero: value of invalid checksum
+ *
+ */
+static inline __sum16 __skb_checksum_validate_complete(struct sk_buff *skb,
+                                                      bool complete,
+                                                      __wsum psum)
+{
+       if (skb->ip_summed == CHECKSUM_COMPLETE) {
+               if (!csum_fold(csum_add(psum, skb->csum))) {
+                       skb->csum_valid = 1;
+                       return 0;
+               }
+       }
+
+       skb->csum = psum;
+
+       if (complete || skb->len <= CHECKSUM_BREAK) {
+               __sum16 csum;
+
+               csum = __skb_checksum_complete(skb);
+               skb->csum_valid = !csum;
+               return csum;
+       }
+
+       return 0;
+}
+
+static inline __wsum null_compute_pseudo(struct sk_buff *skb, int proto)
+{
+       return 0;
+}
+
+/* Perform checksum validate (init). Note that this is a macro since we only
+ * want to calculate the pseudo header which is an input function if necessary.
+ * First we try to validate without any computation (checksum unnecessary) and
+ * then calculate based on checksum complete calling the function to compute
+ * pseudo header.
+ *
+ * Return values:
+ *   0: checksum is validated or try to in skb_checksum_complete
+ *   non-zero: value of invalid checksum
+ */
+#define __skb_checksum_validate(skb, proto, complete,                  \
+                               zero_okay, check, compute_pseudo)       \
+({                                                                     \
+       __sum16 __ret = 0;                                              \
+       skb->csum_valid = 0;                                            \
+       if (__skb_checksum_validate_needed(skb, zero_okay, check))      \
+               __ret = __skb_checksum_validate_complete(skb,           \
+                               complete, compute_pseudo(skb, proto));  \
+       __ret;                                                          \
+})
+
+#define skb_checksum_init(skb, proto, compute_pseudo)                  \
+       __skb_checksum_validate(skb, proto, false, false, 0, compute_pseudo)
+
+#define skb_checksum_init_zero_check(skb, proto, check, compute_pseudo)        \
+       __skb_checksum_validate(skb, proto, false, true, check, compute_pseudo)
+
+#define skb_checksum_validate(skb, proto, compute_pseudo)              \
+       __skb_checksum_validate(skb, proto, true, false, 0, compute_pseudo)
+
+#define skb_checksum_validate_zero_check(skb, proto, check,            \
+                                        compute_pseudo)                \
+       __skb_checksum_validate_(skb, proto, true, true, check, compute_pseudo)
+
+#define skb_checksum_simple_validate(skb)                              \
+       __skb_checksum_validate(skb, 0, true, false, 0, null_compute_pseudo)
+
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
 void nf_conntrack_destroy(struct nf_conntrack *nfct);
 static inline void nf_conntrack_put(struct nf_conntrack *nfct)
@@ -2895,6 +3026,7 @@ static inline struct sec_path *skb_sec_path(struct sk_buff *skb)
 struct skb_gso_cb {
        int     mac_offset;
        int     encap_level;
+       __u16   csum_start;
 };
 #define SKB_GSO_CB(skb) ((struct skb_gso_cb *)(skb)->cb)
 
@@ -2919,6 +3051,28 @@ static inline int gso_pskb_expand_head(struct sk_buff *skb, int extra)
        return 0;
 }
 
+/* Compute the checksum for a gso segment. First compute the checksum value
+ * from the start of transport header to SKB_GSO_CB(skb)->csum_start, and
+ * then add in skb->csum (checksum from csum_start to end of packet).
+ * skb->csum and csum_start are then updated to reflect the checksum of the
+ * resultant packet starting from the transport header-- the resultant checksum
+ * is in the res argument (i.e. normally zero or ~ of checksum of a pseudo
+ * header.
+ */
+static inline __sum16 gso_make_checksum(struct sk_buff *skb, __wsum res)
+{
+       int plen = SKB_GSO_CB(skb)->csum_start - skb_headroom(skb) -
+           skb_transport_offset(skb);
+       __u16 csum;
+
+       csum = csum_fold(csum_partial(skb_transport_header(skb),
+                                     plen, skb->csum));
+       skb->csum = res;
+       SKB_GSO_CB(skb)->csum_start -= plen;
+
+       return csum;
+}
+
 static inline bool skb_is_gso(const struct sk_buff *skb)
 {
        return skb_shinfo(skb)->gso_size;
index aa327a8105ada0d2502d697c6f66699e8f8c516b..b2b1afbb32024ebbb80be196ea276a7ff53ae43e 100644 (file)
@@ -26,20 +26,6 @@ struct at86rf230_platform_data {
        int rstn;
        int slp_tr;
        int dig2;
-
-       /* Setting the irq_type will configure the driver to request
-        * the platform irq trigger type according to the given value
-        * and configure the interrupt polarity of the device to the
-        * corresponding polarity.
-        *
-        * Allowed values are: IRQF_TRIGGER_RISING, IRQF_TRIGGER_FALLING,
-        *                     IRQF_TRIGGER_HIGH and IRQF_TRIGGER_LOW
-        *
-        * Setting it to 0, the driver does not touch the trigger type
-        * configuration of the interrupt and sets the interrupt polarity
-        * of the device to high active (the default value).
-        */
-       int irq_type;
 };
 
 #endif
index 0e43906d2fda6dc68cffc6343594178465d6e461..da2751d3b93db503b5572b27e97a0e7cc516fc91 100644 (file)
@@ -70,16 +70,6 @@ extern ssize_t splice_from_pipe(struct pipe_inode_info *, struct file *,
                                splice_actor *);
 extern ssize_t __splice_from_pipe(struct pipe_inode_info *,
                                  struct splice_desc *, splice_actor *);
-extern int splice_from_pipe_feed(struct pipe_inode_info *, struct splice_desc *,
-                                splice_actor *);
-extern int splice_from_pipe_next(struct pipe_inode_info *,
-                                struct splice_desc *);
-extern void splice_from_pipe_begin(struct splice_desc *);
-extern void splice_from_pipe_end(struct pipe_inode_info *,
-                                struct splice_desc *);
-extern int pipe_to_file(struct pipe_inode_info *, struct pipe_buffer *,
-                       struct splice_desc *);
-
 extern ssize_t splice_to_pipe(struct pipe_inode_info *,
                              struct splice_pipe_desc *);
 extern ssize_t splice_direct_to_actor(struct file *, struct splice_desc *,
index 07ef9b82b66da9088f0a8811d21d14d8f0b66db2..4568a5cc9ab851c5c53e536cfbcd5e62003d6a47 100644 (file)
@@ -33,6 +33,7 @@ struct ssb_sprom {
        u8 et1phyaddr;          /* MII address for enet1 */
        u8 et0mdcport;          /* MDIO for enet0 */
        u8 et1mdcport;          /* MDIO for enet1 */
+       u16 dev_id;             /* Device ID overriding e.g. PCI ID */
        u16 board_rev;          /* Board revision number from SPROM. */
        u16 board_num;          /* Board number from SPROM. */
        u16 board_type;         /* Board type from SPROM. */
index 239946868142cec2893e89259555d3b86884d616..a0513210798fc9027af01dccccc5ff6c677d3d7e 100644 (file)
@@ -197,7 +197,8 @@ struct tcp_sock {
        u8      do_early_retrans:1,/* Enable RFC5827 early-retransmit  */
                syn_data:1,     /* SYN includes data */
                syn_fastopen:1, /* SYN includes Fast Open option */
-               syn_data_acked:1;/* data in SYN is acked by SYN-ACK */
+               syn_data_acked:1,/* data in SYN is acked by SYN-ACK */
+               is_cwnd_limited:1;/* forward progress limited by snd_cwnd? */
        u32     tlp_high_seq;   /* snd_nxt at the time of TLP retransmit. */
 
 /* RTT measurement */
@@ -209,6 +210,8 @@ struct tcp_sock {
 
        u32     packets_out;    /* Packets which are "in flight"        */
        u32     retrans_out;    /* Retransmitted packets out            */
+       u32     max_packets_out;  /* max packets_out in last window */
+       u32     max_packets_seq;  /* right edge of max_packets_out flight */
 
        u16     urg_data;       /* Saved octet of OOB data and control flags */
        u8      ecn_flags;      /* ECN status bits.                     */
@@ -365,11 +368,6 @@ static inline bool tcp_passive_fastopen(const struct sock *sk)
                tcp_sk(sk)->fastopen_rsk != NULL);
 }
 
-static inline bool fastopen_cookie_present(struct tcp_fastopen_cookie *foc)
-{
-       return foc->len != -1;
-}
-
 extern void tcp_sock_destruct(struct sock *sk);
 
 static inline int fastopen_init_queue(struct sock *sk, int backlog)
index 42278bbf7a882d90360d141db9c8cc1b1eca1719..247cfdcc4b08bbf377ff5819ebd02683806b0c83 100644 (file)
@@ -47,7 +47,9 @@ struct udp_sock {
 #define udp_portaddr_node      inet.sk.__sk_common.skc_portaddr_node
        int              pending;       /* Any pending frames ? */
        unsigned int     corkflag;      /* Cork is required */
-       __u16            encap_type;    /* Is this an Encapsulation socket? */
+       __u8             encap_type;    /* Is this an Encapsulation socket? */
+       unsigned char    no_check6_tx:1,/* Send zero UDP6 checksums on TX? */
+                        no_check6_rx:1;/* Allow zero UDP6 checksums on RX? */
        /*
         * Following member retains the information to create a UDP header
         * when the socket is uncorked.
@@ -76,6 +78,26 @@ static inline struct udp_sock *udp_sk(const struct sock *sk)
        return (struct udp_sock *)sk;
 }
 
+static inline void udp_set_no_check6_tx(struct sock *sk, bool val)
+{
+       udp_sk(sk)->no_check6_tx = val;
+}
+
+static inline void udp_set_no_check6_rx(struct sock *sk, bool val)
+{
+       udp_sk(sk)->no_check6_rx = val;
+}
+
+static inline bool udp_get_no_check6_tx(struct sock *sk)
+{
+       return udp_sk(sk)->no_check6_tx;
+}
+
+static inline bool udp_get_no_check6_rx(struct sock *sk)
+{
+       return udp_sk(sk)->no_check6_rx;
+}
+
 #define udp_portaddr_for_each_entry(__sk, node, list) \
        hlist_nulls_for_each_entry(__sk, node, list, __sk_common.skc_portaddr_node)
 
index 199bcc34241ba0155a367f11d05edf5d9c138a02..e2231e47cec131f8de84459568b11aa6d294f62d 100644 (file)
@@ -19,11 +19,21 @@ struct kvec {
        size_t iov_len;
 };
 
+enum {
+       ITER_IOVEC = 0,
+       ITER_KVEC = 2,
+       ITER_BVEC = 4,
+};
+
 struct iov_iter {
-       const struct iovec *iov;
-       unsigned long nr_segs;
+       int type;
        size_t iov_offset;
        size_t count;
+       union {
+               const struct iovec *iov;
+               const struct bio_vec *bvec;
+       };
+       unsigned long nr_segs;
 };
 
 /*
@@ -53,6 +63,7 @@ static inline struct iovec iov_iter_iovec(const struct iov_iter *iter)
 }
 
 #define iov_for_each(iov, iter, start)                         \
+       if (!((start).type & ITER_BVEC))                        \
        for (iter = (start);                                    \
             (iter).count &&                                    \
             ((iov = iov_iter_iovec(&(iter))), 1);              \
@@ -62,32 +73,44 @@ unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to);
 
 size_t iov_iter_copy_from_user_atomic(struct page *page,
                struct iov_iter *i, unsigned long offset, size_t bytes);
-size_t iov_iter_copy_from_user(struct page *page,
-               struct iov_iter *i, unsigned long offset, size_t bytes);
 void iov_iter_advance(struct iov_iter *i, size_t bytes);
 int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes);
 size_t iov_iter_single_seg_count(const struct iov_iter *i);
 size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
                         struct iov_iter *i);
+size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes,
+                        struct iov_iter *i);
+unsigned long iov_iter_alignment(const struct iov_iter *i);
+void iov_iter_init(struct iov_iter *i, int direction, const struct iovec *iov,
+                       unsigned long nr_segs, size_t count);
+ssize_t iov_iter_get_pages(struct iov_iter *i, struct page **pages,
+                       size_t maxsize, size_t *start);
+ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, struct page ***pages,
+                       size_t maxsize, size_t *start);
+int iov_iter_npages(const struct iov_iter *i, int maxpages);
 
-static inline void iov_iter_init(struct iov_iter *i,
-                       const struct iovec *iov, unsigned long nr_segs,
-                       size_t count, size_t written)
+static inline size_t iov_iter_count(struct iov_iter *i)
 {
-       i->iov = iov;
-       i->nr_segs = nr_segs;
-       i->iov_offset = 0;
-       i->count = count + written;
+       return i->count;
+}
 
-       iov_iter_advance(i, written);
+static inline void iov_iter_truncate(struct iov_iter *i, size_t count)
+{
+       if (i->count > count)
+               i->count = count;
 }
 
-static inline size_t iov_iter_count(struct iov_iter *i)
+/*
+ * reexpand a previously truncated iterator; count must be no more than how much
+ * we had shrunk it.
+ */
+static inline void iov_iter_reexpand(struct iov_iter *i, size_t count)
 {
-       return i->count;
+       i->count = count;
 }
 
 int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len);
 int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len);
 
+
 #endif
index c52f827ba6ce6449c3e6c62736d97d5c11192c39..4f844c6b03ee2c8c04bd4c745fd292e4229b7989 100644 (file)
@@ -103,6 +103,7 @@ extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, u
 extern bool __weak is_swbp_insn(uprobe_opcode_t *insn);
 extern bool __weak is_trap_insn(uprobe_opcode_t *insn);
 extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs);
+extern unsigned long uprobe_get_trap_addr(struct pt_regs *regs);
 extern int uprobe_write_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t);
 extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc);
 extern int uprobe_apply(struct inode *inode, loff_t offset, struct uprobe_consumer *uc, bool);
@@ -133,6 +134,9 @@ extern void __weak arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
 #else /* !CONFIG_UPROBES */
 struct uprobes_state {
 };
+
+#define uprobe_get_trap_addr(regs)     instruction_pointer(regs)
+
 static inline int
 uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc)
 {
index 44b38b92236a5834f022185e99707ea9ac6edc39..7c9b484735c533ed4243be050d8ad2e8c67d581a 100644 (file)
 #define        CDC_NCM_NTB_MAX_SIZE_TX                 32768   /* bytes */
 #define        CDC_NCM_NTB_MAX_SIZE_RX                 32768   /* bytes */
 
+/* Initial NTB length */
+#define        CDC_NCM_NTB_DEF_SIZE_TX                 16384   /* bytes */
+#define        CDC_NCM_NTB_DEF_SIZE_RX                 16384   /* bytes */
+
 /* Minimum value for MaxDatagramSize, ch. 6.2.9 */
 #define        CDC_NCM_MIN_DATAGRAM_SIZE               1514    /* bytes */
 
 /* Restart the timer, if amount of datagrams is less than given value */
 #define        CDC_NCM_RESTART_TIMER_DATAGRAM_CNT      3
 #define        CDC_NCM_TIMER_PENDING_CNT               2
-#define CDC_NCM_TIMER_INTERVAL                 (400UL * NSEC_PER_USEC)
-
-/* The following macro defines the minimum header space */
-#define        CDC_NCM_MIN_HDR_SIZE \
-       (sizeof(struct usb_cdc_ncm_nth16) + sizeof(struct usb_cdc_ncm_ndp16) + \
-       (CDC_NCM_DPT_DATAGRAMS_MAX + 1) * sizeof(struct usb_cdc_ncm_dpe16))
-
-#define CDC_NCM_NDP_SIZE \
-       (sizeof(struct usb_cdc_ncm_ndp16) +                             \
-             (CDC_NCM_DPT_DATAGRAMS_MAX + 1) * sizeof(struct usb_cdc_ncm_dpe16))
+#define CDC_NCM_TIMER_INTERVAL_USEC            400UL
+#define CDC_NCM_TIMER_INTERVAL_MIN             5UL
+#define CDC_NCM_TIMER_INTERVAL_MAX             (U32_MAX / NSEC_PER_USEC)
 
 #define cdc_ncm_comm_intf_is_mbim(x)  ((x)->desc.bInterfaceSubClass == USB_CDC_SUBCLASS_MBIM && \
                                       (x)->desc.bInterfaceProtocol == USB_CDC_PROTO_NONE)
@@ -107,6 +104,9 @@ struct cdc_ncm_ctx {
        spinlock_t mtx;
        atomic_t stop;
 
+       u32 timer_interval;
+       u32 max_ndp_size;
+
        u32 tx_timer_pending;
        u32 tx_curr_frame_num;
        u32 rx_max;
@@ -118,10 +118,21 @@ struct cdc_ncm_ctx {
        u16 tx_ndp_modulus;
        u16 tx_seq;
        u16 rx_seq;
-       u16 connected;
+       u16 min_tx_pkt;
+
+       /* statistics */
+       u32 tx_curr_frame_payload;
+       u32 tx_reason_ntb_full;
+       u32 tx_reason_ndp_full;
+       u32 tx_reason_timeout;
+       u32 tx_reason_max_datagram;
+       u64 tx_overhead;
+       u64 tx_ntbs;
+       u64 rx_overhead;
+       u64 rx_ntbs;
 };
 
-u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf);
+u8 cdc_ncm_select_altsetting(struct usb_interface *intf);
 int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting);
 void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf);
 struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign);
index e4abb84199bea7599940a4fe4ee026ee40f6a48d..b46671e28de29a39d52810e2d51ca36562692889 100644 (file)
@@ -106,6 +106,8 @@ static inline struct virtio_device *dev_to_virtio(struct device *_dev)
 int register_virtio_device(struct virtio_device *dev);
 void unregister_virtio_device(struct virtio_device *dev);
 
+void virtio_break_device(struct virtio_device *dev);
+
 /**
  * virtio_driver - operations for a virtio I/O driver
  * @driver: underlying device driver (populate name and owner).
index 4195b97a3def6a84574519da4a6fe37b7e954406..de429d1f4357a89d2a6a9506611a93a6c201fdc6 100644 (file)
@@ -35,11 +35,23 @@ struct virtio_scsi_cmd_req {
        u8 lun[8];              /* Logical Unit Number */
        u64 tag;                /* Command identifier */
        u8 task_attr;           /* Task attribute */
-       u8 prio;
+       u8 prio;                /* SAM command priority field */
        u8 crn;
        u8 cdb[VIRTIO_SCSI_CDB_SIZE];
 } __packed;
 
+/* SCSI command request, followed by protection information */
+struct virtio_scsi_cmd_req_pi {
+       u8 lun[8];              /* Logical Unit Number */
+       u64 tag;                /* Command identifier */
+       u8 task_attr;           /* Task attribute */
+       u8 prio;                /* SAM command priority field */
+       u8 crn;
+       u32 pi_bytesout;        /* DataOUT PI Number of bytes */
+       u32 pi_bytesin;         /* DataIN PI Number of bytes */
+       u8 cdb[VIRTIO_SCSI_CDB_SIZE];
+} __packed;
+
 /* Response, followed by sense data and data-in */
 struct virtio_scsi_cmd_resp {
        u32 sense_len;          /* Sense data length */
@@ -97,6 +109,7 @@ struct virtio_scsi_config {
 #define VIRTIO_SCSI_F_INOUT                    0
 #define VIRTIO_SCSI_F_HOTPLUG                  1
 #define VIRTIO_SCSI_F_CHANGE                   2
+#define VIRTIO_SCSI_F_T10_PI                   3
 
 /* Response codes */
 #define VIRTIO_SCSI_S_OK                       0
index bca25dc53f9d204ef0773db521dbeba797705728..8fab6fa0dbfb08c7c8f272f7fa751db780a169a5 100644 (file)
@@ -432,6 +432,7 @@ void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no);
 void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no);
 
 void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state);
+void vb2_discard_done(struct vb2_queue *q);
 int vb2_wait_for_all_buffers(struct vb2_queue *q);
 
 int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b);
index f7d372b7d4ff672d79a6ce16c437e016449cba4e..79b530fb2c4dd40205595d73831846e5ee56c7ac 100644 (file)
@@ -54,6 +54,7 @@
 #define __6LOWPAN_H__
 
 #include <net/ipv6.h>
+#include <net/net_namespace.h>
 
 #define UIP_802154_SHORTADDR_LEN       2  /* compressed ipv6 address length */
 #define UIP_IPH_LEN                    40 /* ipv6 fixed header size */
index 933a9f22a05ff63abb4379f94cb907c95f6beac4..f679877bb6017dd4a6da7d1fe0e6ea217ba3b3e4 100644 (file)
@@ -306,11 +306,6 @@ static inline void addrconf_addr_solict_mult(const struct in6_addr *addr,
                      htonl(0xFF000000) | addr->s6_addr32[3]);
 }
 
-static inline bool ipv6_addr_is_multicast(const struct in6_addr *addr)
-{
-       return (addr->s6_addr32[0] & htonl(0xFF000000)) == htonl(0xFF000000);
-}
-
 static inline bool ipv6_addr_is_ll_all_nodes(const struct in6_addr *addr)
 {
 #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64
index f79ae2aa76d6a45fb9a1e452d53f82d05e101717..085940f7eeec0e56d5f95226034b32b906bc3380 100644 (file)
@@ -57,6 +57,14 @@ struct sockaddr_ieee802154 {
 /* get/setsockopt */
 #define SOL_IEEE802154 0
 
-#define WPAN_WANTACK   0
+#define WPAN_WANTACK           0
+#define WPAN_SECURITY          1
+#define WPAN_SECURITY_LEVEL    2
+
+#define WPAN_SECURITY_DEFAULT  0
+#define WPAN_SECURITY_OFF      1
+#define WPAN_SECURITY_ON       2
+
+#define WPAN_SECURITY_LEVEL_DEFAULT    (-1)
 
 #endif
index be150cf8cd432298d47860268a1d4566af74481d..16587dcd6a9181b74814f1a4d0865332aa1bd81d 100644 (file)
@@ -367,6 +367,7 @@ enum {
 #define HCI_ERROR_REMOTE_POWER_OFF     0x15
 #define HCI_ERROR_LOCAL_HOST_TERM      0x16
 #define HCI_ERROR_PAIRING_NOT_ALLOWED  0x18
+#define HCI_ERROR_ADVERTISING_TIMEOUT  0x3c
 
 /* Flow control modes */
 #define HCI_FLOW_CTL_MODE_PACKET_BASED 0x00
@@ -1053,6 +1054,17 @@ struct hci_cp_write_page_scan_activity {
        __le16   window;
 } __packed;
 
+#define HCI_OP_READ_TX_POWER           0x0c2d
+struct hci_cp_read_tx_power {
+       __le16   handle;
+       __u8     type;
+} __packed;
+struct hci_rp_read_tx_power {
+       __u8     status;
+       __le16   handle;
+       __s8     tx_power;
+} __packed;
+
 #define HCI_OP_READ_PAGE_SCAN_TYPE     0x0c46
 struct hci_rp_read_page_scan_type {
        __u8     status;
@@ -1063,6 +1075,16 @@ struct hci_rp_read_page_scan_type {
        #define PAGE_SCAN_TYPE_STANDARD         0x00
        #define PAGE_SCAN_TYPE_INTERLACED       0x01
 
+#define HCI_OP_READ_RSSI               0x1405
+struct hci_cp_read_rssi {
+       __le16   handle;
+} __packed;
+struct hci_rp_read_rssi {
+       __u8     status;
+       __le16   handle;
+       __s8     rssi;
+} __packed;
+
 #define HCI_OP_READ_LOCAL_AMP_INFO     0x1409
 struct hci_rp_read_local_amp_info {
        __u8     status;
index 5f8bc05694ac665159bb25f7a9eaa704c3784e91..b386bf17e6c2c10808c8306ad7cbb4bb42e4713d 100644 (file)
@@ -68,6 +68,11 @@ struct discovery_state {
        struct list_head        unknown;        /* Name state not known */
        struct list_head        resolve;        /* Name needs to be resolved */
        __u32                   timestamp;
+       bdaddr_t                last_adv_addr;
+       u8                      last_adv_addr_type;
+       s8                      last_adv_rssi;
+       u8                      last_adv_data[HCI_MAX_AD_LENGTH];
+       u8                      last_adv_data_len;
 };
 
 struct hci_conn_hash {
@@ -140,6 +145,10 @@ struct oob_data {
 /* Default LE RPA expiry time, 15 minutes */
 #define HCI_DEFAULT_RPA_TIMEOUT                (15 * 60)
 
+/* Default min/max age of connection information (1s/3s) */
+#define DEFAULT_CONN_INFO_MIN_AGE      1000
+#define DEFAULT_CONN_INFO_MAX_AGE      3000
+
 struct amp_assoc {
        __u16   len;
        __u16   offset;
@@ -194,6 +203,9 @@ struct hci_dev {
        __u16           le_scan_window;
        __u16           le_conn_min_interval;
        __u16           le_conn_max_interval;
+       __u16           discov_interleaved_timeout;
+       __u16           conn_info_min_age;
+       __u16           conn_info_max_age;
        __u8            ssp_debug_mode;
 
        __u16           devid_source;
@@ -368,8 +380,13 @@ struct hci_conn {
        __u16           setting;
        __u16           le_conn_min_interval;
        __u16           le_conn_max_interval;
+       __s8            rssi;
+       __s8            tx_power;
+       __s8            max_tx_power;
        unsigned long   flags;
 
+       unsigned long   conn_info_timestamp;
+
        __u8            remote_cap;
        __u8            remote_auth;
        __u8            remote_id;
@@ -1204,8 +1221,8 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event);
  */
 #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_LE_TIMEOUT              10240   /* msec */
+#define DISCOV_INTERLEAVED_TIMEOUT     5120    /* msec */
 #define DISCOV_INTERLEAVED_INQUIRY_LEN 0x04
 #define DISCOV_BREDR_INQUIRY_LEN       0x08
 
@@ -1265,7 +1282,8 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
                                       u8 *randomizer256, u8 status);
 void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                       u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name,
-                      u8 ssp, u8 *eir, u16 eir_len);
+                      u8 ssp, u8 *eir, u16 eir_len, u8 *scan_rsp,
+                      u8 scan_rsp_len);
 void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                      u8 addr_type, s8 rssi, u8 *name, u8 name_len);
 void mgmt_discovering(struct hci_dev *hdev, u8 discovering);
index d4b571c2f9fd33b65ec95dbf9ca6b691f1567b61..bcffc9ae0c89bec0bd3237d708e76d0911abfa36 100644 (file)
@@ -181,6 +181,9 @@ struct mgmt_cp_load_link_keys {
 } __packed;
 #define MGMT_LOAD_LINK_KEYS_SIZE       3
 
+#define MGMT_LTK_UNAUTHENTICATED       0x00
+#define MGMT_LTK_AUTHENTICATED         0x01
+
 struct mgmt_ltk_info {
        struct mgmt_addr_info addr;
        __u8    type;
@@ -409,6 +412,18 @@ struct mgmt_cp_load_irks {
 } __packed;
 #define MGMT_LOAD_IRKS_SIZE            2
 
+#define MGMT_OP_GET_CONN_INFO          0x0031
+struct mgmt_cp_get_conn_info {
+       struct mgmt_addr_info addr;
+} __packed;
+#define MGMT_GET_CONN_INFO_SIZE                MGMT_ADDR_INFO_SIZE
+struct mgmt_rp_get_conn_info {
+       struct mgmt_addr_info addr;
+       __s8    rssi;
+       __s8    tx_power;
+       __s8    max_tx_power;
+} __packed;
+
 #define MGMT_EV_CMD_COMPLETE           0x0001
 struct mgmt_ev_cmd_complete {
        __le16  opcode;
index 2611cc389d7d65d03cf298061bf3e4ed32c304cd..578b83127af18545e51c26b859dfa6f80100c263 100644 (file)
@@ -173,7 +173,7 @@ struct rfcomm_dlc {
        struct sk_buff_head   tx_queue;
        struct timer_list     timer;
 
-       spinlock_t    lock;
+       struct mutex  lock;
        unsigned long state;
        unsigned long flags;
        atomic_t      refcnt;
@@ -244,8 +244,8 @@ int  rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig);
 void rfcomm_dlc_accept(struct rfcomm_dlc *d);
 struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel);
 
-#define rfcomm_dlc_lock(d)     spin_lock(&d->lock)
-#define rfcomm_dlc_unlock(d)   spin_unlock(&d->lock)
+#define rfcomm_dlc_lock(d)     mutex_lock(&d->lock)
+#define rfcomm_dlc_unlock(d)   mutex_unlock(&d->lock)
 
 static inline void rfcomm_dlc_hold(struct rfcomm_dlc *d)
 {
index f856e5a746fae66e2c91357cfe94fc89d0acae18..e46c437944f73e66cb275f4adc345a1f869923c8 100644 (file)
@@ -109,6 +109,13 @@ enum ieee80211_band {
  *     channel as the control or any of the secondary channels.
  *     This may be due to the driver or due to regulatory bandwidth
  *     restrictions.
+ * @IEEE80211_CHAN_INDOOR_ONLY: see %NL80211_FREQUENCY_ATTR_INDOOR_ONLY
+ * @IEEE80211_CHAN_GO_CONCURRENT: see %NL80211_FREQUENCY_ATTR_GO_CONCURRENT
+ * @IEEE80211_CHAN_NO_20MHZ: 20 MHz bandwidth is not permitted
+ *     on this channel.
+ * @IEEE80211_CHAN_NO_10MHZ: 10 MHz bandwidth is not permitted
+ *     on this channel.
+ *
  */
 enum ieee80211_channel_flags {
        IEEE80211_CHAN_DISABLED         = 1<<0,
@@ -120,6 +127,10 @@ enum ieee80211_channel_flags {
        IEEE80211_CHAN_NO_OFDM          = 1<<6,
        IEEE80211_CHAN_NO_80MHZ         = 1<<7,
        IEEE80211_CHAN_NO_160MHZ        = 1<<8,
+       IEEE80211_CHAN_INDOOR_ONLY      = 1<<9,
+       IEEE80211_CHAN_GO_CONCURRENT    = 1<<10,
+       IEEE80211_CHAN_NO_20MHZ         = 1<<11,
+       IEEE80211_CHAN_NO_10MHZ         = 1<<12,
 };
 
 #define IEEE80211_CHAN_NO_HT40 \
@@ -330,8 +341,8 @@ struct vif_params {
  * @seq_len: length of @seq.
  */
 struct key_params {
-       u8 *key;
-       u8 *seq;
+       const u8 *key;
+       const u8 *seq;
        int key_len;
        int seq_len;
        u32 cipher;
@@ -441,10 +452,13 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
  * cfg80211_chandef_dfs_required - checks if radar detection is required
  * @wiphy: the wiphy to validate against
  * @chandef: the channel definition to check
- * Return: 1 if radar detection is required, 0 if it is not, < 0 on error
+ * @iftype: the interface type as specified in &enum nl80211_iftype
+ * Returns:
+ *     1 if radar detection is required, 0 if it is not, < 0 on error
  */
 int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
-                                 const struct cfg80211_chan_def *chandef);
+                                 const struct cfg80211_chan_def *chandef,
+                                 enum nl80211_iftype iftype);
 
 /**
  * ieee80211_chandef_rate_flags - returns rate flags for a channel
@@ -654,7 +668,6 @@ struct cfg80211_acl_data {
  * @p2p_opp_ps: P2P opportunistic PS
  * @acl: ACL configuration used by the drivers which has support for
  *     MAC address based access control
- * @radar_required: set if radar detection is required
  */
 struct cfg80211_ap_settings {
        struct cfg80211_chan_def chandef;
@@ -672,7 +685,6 @@ struct cfg80211_ap_settings {
        u8 p2p_ctwindow;
        bool p2p_opp_ps;
        const struct cfg80211_acl_data *acl;
-       bool radar_required;
 };
 
 /**
@@ -682,8 +694,10 @@ struct cfg80211_ap_settings {
  *
  * @chandef: defines the channel to use after the switch
  * @beacon_csa: beacon data while performing the switch
- * @counter_offset_beacon: offset for the counter within the beacon (tail)
- * @counter_offset_presp: offset for the counter within the probe response
+ * @counter_offsets_beacon: offsets of the counters within the beacon (tail)
+ * @counter_offsets_presp: offsets of the counters within the probe response
+ * @n_counter_offsets_beacon: number of csa counters the beacon (tail)
+ * @n_counter_offsets_presp: number of csa counters in the probe response
  * @beacon_after: beacon data to be used on the new channel
  * @radar_required: whether radar detection is required on the new channel
  * @block_tx: whether transmissions should be blocked while changing
@@ -692,7 +706,10 @@ struct cfg80211_ap_settings {
 struct cfg80211_csa_settings {
        struct cfg80211_chan_def chandef;
        struct cfg80211_beacon_data beacon_csa;
-       u16 counter_offset_beacon, counter_offset_presp;
+       const u16 *counter_offsets_beacon;
+       const u16 *counter_offsets_presp;
+       unsigned int n_counter_offsets_beacon;
+       unsigned int n_counter_offsets_presp;
        struct cfg80211_beacon_data beacon_after;
        bool radar_required;
        bool block_tx;
@@ -856,36 +873,38 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
  * @STATION_INFO_NONPEER_PM: @nonpeer_pm filled
  * @STATION_INFO_CHAIN_SIGNAL: @chain_signal filled
  * @STATION_INFO_CHAIN_SIGNAL_AVG: @chain_signal_avg filled
+ * @STATION_INFO_EXPECTED_THROUGHPUT: @expected_throughput filled
  */
 enum station_info_flags {
-       STATION_INFO_INACTIVE_TIME      = 1<<0,
-       STATION_INFO_RX_BYTES           = 1<<1,
-       STATION_INFO_TX_BYTES           = 1<<2,
-       STATION_INFO_LLID               = 1<<3,
-       STATION_INFO_PLID               = 1<<4,
-       STATION_INFO_PLINK_STATE        = 1<<5,
-       STATION_INFO_SIGNAL             = 1<<6,
-       STATION_INFO_TX_BITRATE         = 1<<7,
-       STATION_INFO_RX_PACKETS         = 1<<8,
-       STATION_INFO_TX_PACKETS         = 1<<9,
-       STATION_INFO_TX_RETRIES         = 1<<10,
-       STATION_INFO_TX_FAILED          = 1<<11,
-       STATION_INFO_RX_DROP_MISC       = 1<<12,
-       STATION_INFO_SIGNAL_AVG         = 1<<13,
-       STATION_INFO_RX_BITRATE         = 1<<14,
-       STATION_INFO_BSS_PARAM          = 1<<15,
-       STATION_INFO_CONNECTED_TIME     = 1<<16,
-       STATION_INFO_ASSOC_REQ_IES      = 1<<17,
-       STATION_INFO_STA_FLAGS          = 1<<18,
-       STATION_INFO_BEACON_LOSS_COUNT  = 1<<19,
-       STATION_INFO_T_OFFSET           = 1<<20,
-       STATION_INFO_LOCAL_PM           = 1<<21,
-       STATION_INFO_PEER_PM            = 1<<22,
-       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,
+       STATION_INFO_INACTIVE_TIME              = BIT(0),
+       STATION_INFO_RX_BYTES                   = BIT(1),
+       STATION_INFO_TX_BYTES                   = BIT(2),
+       STATION_INFO_LLID                       = BIT(3),
+       STATION_INFO_PLID                       = BIT(4),
+       STATION_INFO_PLINK_STATE                = BIT(5),
+       STATION_INFO_SIGNAL                     = BIT(6),
+       STATION_INFO_TX_BITRATE                 = BIT(7),
+       STATION_INFO_RX_PACKETS                 = BIT(8),
+       STATION_INFO_TX_PACKETS                 = BIT(9),
+       STATION_INFO_TX_RETRIES                 = BIT(10),
+       STATION_INFO_TX_FAILED                  = BIT(11),
+       STATION_INFO_RX_DROP_MISC               = BIT(12),
+       STATION_INFO_SIGNAL_AVG                 = BIT(13),
+       STATION_INFO_RX_BITRATE                 = BIT(14),
+       STATION_INFO_BSS_PARAM                  = BIT(15),
+       STATION_INFO_CONNECTED_TIME             = BIT(16),
+       STATION_INFO_ASSOC_REQ_IES              = BIT(17),
+       STATION_INFO_STA_FLAGS                  = BIT(18),
+       STATION_INFO_BEACON_LOSS_COUNT          = BIT(19),
+       STATION_INFO_T_OFFSET                   = BIT(20),
+       STATION_INFO_LOCAL_PM                   = BIT(21),
+       STATION_INFO_PEER_PM                    = BIT(22),
+       STATION_INFO_NONPEER_PM                 = BIT(23),
+       STATION_INFO_RX_BYTES64                 = BIT(24),
+       STATION_INFO_TX_BYTES64                 = BIT(25),
+       STATION_INFO_CHAIN_SIGNAL               = BIT(26),
+       STATION_INFO_CHAIN_SIGNAL_AVG           = BIT(27),
+       STATION_INFO_EXPECTED_THROUGHPUT        = BIT(28),
 };
 
 /**
@@ -1007,6 +1026,8 @@ struct sta_bss_parameters {
  * @local_pm: local mesh STA power save mode
  * @peer_pm: peer mesh STA power save mode
  * @nonpeer_pm: non-peer mesh STA power save mode
+ * @expected_throughput: expected throughput in kbps (including 802.11 headers)
+ *     towards this station.
  */
 struct station_info {
        u32 filled;
@@ -1045,12 +1066,27 @@ struct station_info {
        enum nl80211_mesh_power_mode peer_pm;
        enum nl80211_mesh_power_mode nonpeer_pm;
 
+       u32 expected_throughput;
+
        /*
         * Note: Add a new enum station_info_flags value for each new field and
         * use it to check which fields are initialized.
         */
 };
 
+/**
+ * cfg80211_get_station - retrieve information about a given station
+ * @dev: the device where the station is supposed to be connected to
+ * @mac_addr: the mac address of the station of interest
+ * @sinfo: pointer to the structure to fill with the information
+ *
+ * Returns 0 on success and sinfo is filled with the available information
+ * otherwise returns a negative error code and the content of sinfo has to be
+ * considered undefined.
+ */
+int cfg80211_get_station(struct net_device *dev, const u8 *mac_addr,
+                        struct station_info *sinfo);
+
 /**
  * enum monitor_flags - monitor flags
  *
@@ -1152,7 +1188,7 @@ struct bss_parameters {
        int use_cts_prot;
        int use_short_preamble;
        int use_short_slot_time;
-       u8 *basic_rates;
+       const u8 *basic_rates;
        u8 basic_rates_len;
        int ap_isolate;
        int ht_opmode;
@@ -1682,10 +1718,10 @@ struct cfg80211_disassoc_request {
  * @ht_capa_mask:  The bits of ht_capa which are to be used.
  */
 struct cfg80211_ibss_params {
-       u8 *ssid;
-       u8 *bssid;
+       const u8 *ssid;
+       const u8 *bssid;
        struct cfg80211_chan_def chandef;
-       u8 *ie;
+       const u8 *ie;
        u8 ssid_len, ie_len;
        u16 beacon_interval;
        u32 basic_rates;
@@ -1794,8 +1830,8 @@ struct cfg80211_bitrate_mask {
  * @pmkid: The PMK material itself.
  */
 struct cfg80211_pmksa {
-       u8 *bssid;
-       u8 *pmkid;
+       const u8 *bssid;
+       const u8 *pmkid;
 };
 
 /**
@@ -1810,7 +1846,7 @@ struct cfg80211_pmksa {
  * memory, free @mask only!
  */
 struct cfg80211_pkt_pattern {
-       u8 *mask, *pattern;
+       const u8 *mask, *pattern;
        int pattern_len;
        int pkt_offset;
 };
@@ -1974,6 +2010,8 @@ struct cfg80211_update_ft_ies_params {
  * @len: buffer length
  * @no_cck: don't use cck rates for this frame
  * @dont_wait_for_ack: tells the low level not to wait for an ack
+ * @n_csa_offsets: length of csa_offsets array
+ * @csa_offsets: array of all the csa offsets in the frame
  */
 struct cfg80211_mgmt_tx_params {
        struct ieee80211_channel *chan;
@@ -1983,6 +2021,8 @@ struct cfg80211_mgmt_tx_params {
        size_t len;
        bool no_cck;
        bool dont_wait_for_ack;
+       int n_csa_offsets;
+       const u16 *csa_offsets;
 };
 
 /**
@@ -2278,6 +2318,10 @@ struct cfg80211_qos_map {
  * @channel_switch: initiate channel-switch procedure (with CSA)
  *
  * @set_qos_map: Set QoS mapping information to the driver
+ *
+ * @set_ap_chanwidth: Set the AP (including P2P GO) mode channel width for the
+ *     given interface This is used e.g. for dynamic HT 20/40 MHz channel width
+ *     changes during the lifetime of the BSS.
  */
 struct cfg80211_ops {
        int     (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -2320,28 +2364,29 @@ struct cfg80211_ops {
 
 
        int     (*add_station)(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *mac, struct station_parameters *params);
+                              const u8 *mac,
+                              struct station_parameters *params);
        int     (*del_station)(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *mac);
+                              const u8 *mac);
        int     (*change_station)(struct wiphy *wiphy, struct net_device *dev,
-                                 u8 *mac, struct station_parameters *params);
+                                 const u8 *mac,
+                                 struct station_parameters *params);
        int     (*get_station)(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *mac, struct station_info *sinfo);
+                              const u8 *mac, struct station_info *sinfo);
        int     (*dump_station)(struct wiphy *wiphy, struct net_device *dev,
-                              int idx, u8 *mac, struct station_info *sinfo);
+                               int idx, u8 *mac, struct station_info *sinfo);
 
        int     (*add_mpath)(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *dst, u8 *next_hop);
+                              const u8 *dst, const u8 *next_hop);
        int     (*del_mpath)(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *dst);
+                              const u8 *dst);
        int     (*change_mpath)(struct wiphy *wiphy, struct net_device *dev,
-                                 u8 *dst, u8 *next_hop);
+                                 const u8 *dst, const u8 *next_hop);
        int     (*get_mpath)(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *dst, u8 *next_hop,
-                              struct mpath_info *pinfo);
+                            u8 *dst, u8 *next_hop, struct mpath_info *pinfo);
        int     (*dump_mpath)(struct wiphy *wiphy, struct net_device *dev,
-                              int idx, u8 *dst, u8 *next_hop,
-                              struct mpath_info *pinfo);
+                             int idx, u8 *dst, u8 *next_hop,
+                             struct mpath_info *pinfo);
        int     (*get_mesh_config)(struct wiphy *wiphy,
                                struct net_device *dev,
                                struct mesh_config *conf);
@@ -2471,11 +2516,11 @@ struct cfg80211_ops {
                                  struct cfg80211_gtk_rekey_data *data);
 
        int     (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev,
-                            u8 *peer, u8 action_code,  u8 dialog_token,
+                            const u8 *peer, u8 action_code,  u8 dialog_token,
                             u16 status_code, u32 peer_capability,
                             const u8 *buf, size_t len);
        int     (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev,
-                            u8 *peer, enum nl80211_tdls_operation oper);
+                            const u8 *peer, enum nl80211_tdls_operation oper);
 
        int     (*probe_client)(struct wiphy *wiphy, struct net_device *dev,
                                const u8 *peer, u64 *cookie);
@@ -2521,9 +2566,13 @@ struct cfg80211_ops {
        int     (*channel_switch)(struct wiphy *wiphy,
                                  struct net_device *dev,
                                  struct cfg80211_csa_settings *params);
+
        int     (*set_qos_map)(struct wiphy *wiphy,
                               struct net_device *dev,
                               struct cfg80211_qos_map *qos_map);
+
+       int     (*set_ap_chanwidth)(struct wiphy *wiphy, struct net_device *dev,
+                                   struct cfg80211_chan_def *chandef);
 };
 
 /*
@@ -2618,6 +2667,7 @@ struct ieee80211_iface_limit {
  *     between infrastructure and AP types must match. This is required
  *     only in special cases.
  * @radar_detect_widths: bitmap of channel widths supported for radar detection
+ * @radar_detect_regions: bitmap of regions supported for radar detection
  *
  * With this structure the driver can describe which interface
  * combinations it supports concurrently.
@@ -2675,6 +2725,7 @@ struct ieee80211_iface_combination {
        u8 n_limits;
        bool beacon_int_infra_match;
        u8 radar_detect_widths;
+       u8 radar_detect_regions;
 };
 
 struct ieee80211_txrx_stypes {
@@ -2905,6 +2956,17 @@ struct wiphy_vendor_command {
  *     (including P2P GO) or 0 to indicate no such limit is advertised. The
  *     driver is allowed to advertise a theoretical limit that it can reach in
  *     some cases, but may not always reach.
+ *
+ * @max_num_csa_counters: Number of supported csa_counters in beacons
+ *     and probe responses.  This value should be set if the driver
+ *     wishes to limit the number of csa counters. Default (0) means
+ *     infinite.
+ * @max_adj_channel_rssi_comp: max offset of between the channel on which the
+ *     frame was sent and the channel on which the frame was heard for which
+ *     the reported rssi is still valid. If a driver is able to compensate the
+ *     low rssi when a frame is heard on different channel, then it should set
+ *     this variable to the maximal offset for which it can compensate.
+ *     This value should be set in MHz.
  */
 struct wiphy {
        /* assign these fields before you register the wiphy */
@@ -3022,6 +3084,9 @@ struct wiphy {
 
        u16 max_ap_assoc_sta;
 
+       u8 max_num_csa_counters;
+       u8 max_adj_channel_rssi_comp;
+
        char priv[0] __aligned(NETDEV_ALIGN);
 };
 
@@ -3194,6 +3259,7 @@ struct cfg80211_cached_keys;
  * @ibss_dfs_possible: (private) IBSS may change to a DFS channel
  * @event_list: (private) list for internal event processing
  * @event_lock: (private) lock for event list
+ * @owner_nlportid: (private) owner socket port ID
  */
 struct wireless_dev {
        struct wiphy *wiphy;
@@ -3241,13 +3307,15 @@ struct wireless_dev {
        unsigned long cac_start_time;
        unsigned int cac_time_ms;
 
+       u32 owner_nlportid;
+
 #ifdef CONFIG_CFG80211_WEXT
        /* wext data */
        struct {
                struct cfg80211_ibss_params ibss;
                struct cfg80211_connect_params connect;
                struct cfg80211_cached_keys *keys;
-               u8 *ie;
+               const u8 *ie;
                size_t ie_len;
                u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
                u8 ssid[IEEE80211_MAX_SSID_LEN];
@@ -3488,7 +3556,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
  * Return: 0 on success, or a negative error code.
  */
 int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
-                            enum nl80211_iftype iftype, u8 *bssid, bool qos);
+                            enum nl80211_iftype iftype, const u8 *bssid,
+                            bool qos);
 
 /**
  * ieee80211_amsdu_to_8023s - decode an IEEE 802.11n A-MSDU frame
@@ -3600,7 +3669,7 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2);
  * default channel settings will be disregarded. If no rule is found for a
  * channel on the regulatory domain the channel will be disabled.
  * Drivers using this for a wiphy should also set the wiphy flag
- * WIPHY_FLAG_CUSTOM_REGULATORY or cfg80211 will set it for the wiphy
+ * REGULATORY_CUSTOM_REG or cfg80211 will set it for the wiphy
  * that called this helper.
  */
 void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
@@ -4289,7 +4358,7 @@ void cfg80211_roamed_bss(struct net_device *dev, struct cfg80211_bss *bss,
  * and not try to connect to any AP any more.
  */
 void cfg80211_disconnected(struct net_device *dev, u16 reason,
-                          u8 *ie, size_t ie_len, gfp_t gfp);
+                          const u8 *ie, size_t ie_len, gfp_t gfp);
 
 /**
  * cfg80211_ready_on_channel - notification of remain_on_channel start
@@ -4543,12 +4612,14 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy,
  * cfg80211_reg_can_beacon - check if beaconing is allowed
  * @wiphy: the wiphy
  * @chandef: the channel definition
+ * @iftype: interface type
  *
  * Return: %true if there is no secondary channel or the secondary channel(s)
  * can be used for beaconing (i.e. is not a radar channel etc.)
  */
 bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
-                            struct cfg80211_chan_def *chandef);
+                            struct cfg80211_chan_def *chandef,
+                            enum nl80211_iftype iftype);
 
 /*
  * cfg80211_ch_switch_notify - update wdev channel and notify userspace
@@ -4694,6 +4765,84 @@ void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp);
  */
 unsigned int ieee80211_get_num_supported_channels(struct wiphy *wiphy);
 
+/**
+ * cfg80211_check_combinations - check interface combinations
+ *
+ * @wiphy: the wiphy
+ * @num_different_channels: the number of different channels we want
+ *     to use for verification
+ * @radar_detect: a bitmap where each bit corresponds to a channel
+ *     width where radar detection is needed, as in the definition of
+ *     &struct ieee80211_iface_combination.@radar_detect_widths
+ * @iftype_num: array with the numbers of interfaces of each interface
+ *     type.  The index is the interface type as specified in &enum
+ *     nl80211_iftype.
+ *
+ * This function can be called by the driver to check whether a
+ * combination of interfaces and their types are allowed according to
+ * the interface combinations.
+ */
+int cfg80211_check_combinations(struct wiphy *wiphy,
+                               const int num_different_channels,
+                               const u8 radar_detect,
+                               const int iftype_num[NUM_NL80211_IFTYPES]);
+
+/**
+ * cfg80211_iter_combinations - iterate over matching combinations
+ *
+ * @wiphy: the wiphy
+ * @num_different_channels: the number of different channels we want
+ *     to use for verification
+ * @radar_detect: a bitmap where each bit corresponds to a channel
+ *     width where radar detection is needed, as in the definition of
+ *     &struct ieee80211_iface_combination.@radar_detect_widths
+ * @iftype_num: array with the numbers of interfaces of each interface
+ *     type.  The index is the interface type as specified in &enum
+ *     nl80211_iftype.
+ * @iter: function to call for each matching combination
+ * @data: pointer to pass to iter function
+ *
+ * This function can be called by the driver to check what possible
+ * combinations it fits in at a given moment, e.g. for channel switching
+ * purposes.
+ */
+int cfg80211_iter_combinations(struct wiphy *wiphy,
+                              const int num_different_channels,
+                              const u8 radar_detect,
+                              const int iftype_num[NUM_NL80211_IFTYPES],
+                              void (*iter)(const struct ieee80211_iface_combination *c,
+                                           void *data),
+                              void *data);
+
+/*
+ * cfg80211_stop_iface - trigger interface disconnection
+ *
+ * @wiphy: the wiphy
+ * @wdev: wireless device
+ * @gfp: context flags
+ *
+ * Trigger interface to be stopped as if AP was stopped, IBSS/mesh left, STA
+ * disconnected.
+ *
+ * Note: This doesn't need any locks and is asynchronous.
+ */
+void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev,
+                        gfp_t gfp);
+
+/**
+ * cfg80211_shutdown_all_interfaces - shut down all interfaces for a wiphy
+ * @wiphy: the wiphy to shut down
+ *
+ * This function shuts down all interfaces belonging to this wiphy by
+ * calling dev_close() (and treating non-netdev interfaces as needed).
+ * It shouldn't really be used unless there are some fatal device errors
+ * that really can't be recovered in any other way.
+ *
+ * Callers must hold the RTNL and be able to deal with callbacks into
+ * the driver while the function is running.
+ */
+void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy);
+
 /* Logging, debugging and troubleshooting/diagnostic helpers. */
 
 /* wiphy_printk helpers, similar to dev_printk */
index a28f4e0f625193b0682932207a46578f094f52a9..87cb1903640d63ccfed890c957294af46a93cf24 100644 (file)
@@ -57,12 +57,14 @@ static __inline__ __wsum csum_and_copy_to_user
 }
 #endif
 
+#ifndef HAVE_ARCH_CSUM_ADD
 static inline __wsum csum_add(__wsum csum, __wsum addend)
 {
        u32 res = (__force u32)csum;
        res += (__force u32)addend;
        return (__force __wsum)(res + (res < (__force u32)addend));
 }
+#endif
 
 static inline __wsum csum_sub(__wsum csum, __wsum addend)
 {
index 7828ebf99ee132241b76e500681a43188143da99..6efce384451e56f16d8aab0847a6a7be4e94e0a0 100644 (file)
@@ -181,6 +181,11 @@ struct dsa_switch_driver {
 void register_switch_driver(struct dsa_switch_driver *type);
 void unregister_switch_driver(struct dsa_switch_driver *type);
 
+static inline void *ds_to_priv(struct dsa_switch *ds)
+{
+       return (void *)(ds + 1);
+}
+
 /*
  * The original DSA tag format and some other tag formats have no
  * ethertype, which means that we need to add a little hack to the
index 70046a0b0b89c13d0f71f6180ed64e7dacf869e6..b53182018743f4383e525c43f30fc25bd5b4f5a8 100644 (file)
@@ -37,9 +37,10 @@ void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
                      int hdr_len);
 
 static inline struct sk_buff *gre_handle_offloads(struct sk_buff *skb,
-                                                 bool gre_csum)
+                                                 bool csum)
 {
-       return iptunnel_handle_offloads(skb, gre_csum, SKB_GSO_GRE);
+       return iptunnel_handle_offloads(skb, csum,
+                                       csum ? SKB_GSO_GRE_CSUM : SKB_GSO_GRE);
 }
 
 
index c7ae0ac528dc1e5e1d3c2d5456c2d0e5221b6933..0aa7122e8f15390b4b6158429a245057fdd23e5d 100644 (file)
 #define IEEE802154_SCF_KEY_SHORT_INDEX         2
 #define IEEE802154_SCF_KEY_HW_INDEX            3
 
+#define IEEE802154_SCF_SECLEVEL_NONE           0
+#define IEEE802154_SCF_SECLEVEL_MIC32          1
+#define IEEE802154_SCF_SECLEVEL_MIC64          2
+#define IEEE802154_SCF_SECLEVEL_MIC128         3
+#define IEEE802154_SCF_SECLEVEL_ENC            4
+#define IEEE802154_SCF_SECLEVEL_ENC_MIC32      5
+#define IEEE802154_SCF_SECLEVEL_ENC_MIC64      6
+#define IEEE802154_SCF_SECLEVEL_ENC_MIC128     7
+
 /* MAC footer size */
 #define IEEE802154_MFR_SIZE    2 /* 2 octets */
 
index 5a719ca892f41c24eb45e849a64327cd6221d407..3b53c8e405e48143f667c20850db1666c402d8fa 100644 (file)
@@ -27,6 +27,7 @@
 #ifndef IEEE802154_NETDEVICE_H
 #define IEEE802154_NETDEVICE_H
 
+#include <net/ieee802154.h>
 #include <net/af_ieee802154.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
@@ -114,6 +115,34 @@ int ieee802154_hdr_pull(struct sk_buff *skb, struct ieee802154_hdr *hdr);
 int ieee802154_hdr_peek_addrs(const struct sk_buff *skb,
                              struct ieee802154_hdr *hdr);
 
+/* parses the full 802.15.4 header a given skb and stores them into hdr,
+ * performing pan id decompression and length checks to be suitable for use in
+ * header_ops.parse
+ */
+int ieee802154_hdr_peek(const struct sk_buff *skb, struct ieee802154_hdr *hdr);
+
+int ieee802154_max_payload(const struct ieee802154_hdr *hdr);
+
+static inline int
+ieee802154_sechdr_authtag_len(const struct ieee802154_sechdr *sec)
+{
+       switch (sec->level) {
+       case IEEE802154_SCF_SECLEVEL_MIC32:
+       case IEEE802154_SCF_SECLEVEL_ENC_MIC32:
+               return 4;
+       case IEEE802154_SCF_SECLEVEL_MIC64:
+       case IEEE802154_SCF_SECLEVEL_ENC_MIC64:
+               return 8;
+       case IEEE802154_SCF_SECLEVEL_MIC128:
+       case IEEE802154_SCF_SECLEVEL_ENC_MIC128:
+               return 16;
+       case IEEE802154_SCF_SECLEVEL_NONE:
+       case IEEE802154_SCF_SECLEVEL_ENC:
+       default:
+               return 0;
+       }
+}
+
 static inline int ieee802154_hdr_length(struct sk_buff *skb)
 {
        struct ieee802154_hdr hdr;
@@ -193,8 +222,12 @@ static inline void ieee802154_addr_to_sa(struct ieee802154_addr_sa *sa,
  */
 struct ieee802154_mac_cb {
        u8 lqi;
-       u8 flags;
-       u8 seq;
+       u8 type;
+       bool ackreq;
+       bool secen;
+       bool secen_override;
+       u8 seclevel;
+       bool seclevel_override;
        struct ieee802154_addr source;
        struct ieee802154_addr dest;
 };
@@ -204,25 +237,96 @@ static inline struct ieee802154_mac_cb *mac_cb(struct sk_buff *skb)
        return (struct ieee802154_mac_cb *)skb->cb;
 }
 
-#define MAC_CB_FLAG_TYPEMASK           ((1 << 3) - 1)
-
-#define MAC_CB_FLAG_ACKREQ             (1 << 3)
-#define MAC_CB_FLAG_SECEN              (1 << 4)
-
-static inline bool mac_cb_is_ackreq(struct sk_buff *skb)
+static inline struct ieee802154_mac_cb *mac_cb_init(struct sk_buff *skb)
 {
-       return mac_cb(skb)->flags & MAC_CB_FLAG_ACKREQ;
-}
+       BUILD_BUG_ON(sizeof(struct ieee802154_mac_cb) > sizeof(skb->cb));
 
-static inline bool mac_cb_is_secen(struct sk_buff *skb)
-{
-       return mac_cb(skb)->flags & MAC_CB_FLAG_SECEN;
+       memset(skb->cb, 0, sizeof(struct ieee802154_mac_cb));
+       return mac_cb(skb);
 }
 
-static inline int mac_cb_type(struct sk_buff *skb)
-{
-       return mac_cb(skb)->flags & MAC_CB_FLAG_TYPEMASK;
-}
+#define IEEE802154_LLSEC_KEY_SIZE 16
+
+struct ieee802154_llsec_key_id {
+       u8 mode;
+       u8 id;
+       union {
+               struct ieee802154_addr device_addr;
+               __le32 short_source;
+               __le64 extended_source;
+       };
+};
+
+struct ieee802154_llsec_key {
+       u8 frame_types;
+       u32 cmd_frame_ids;
+       u8 key[IEEE802154_LLSEC_KEY_SIZE];
+};
+
+struct ieee802154_llsec_key_entry {
+       struct list_head list;
+
+       struct ieee802154_llsec_key_id id;
+       struct ieee802154_llsec_key *key;
+};
+
+struct ieee802154_llsec_device_key {
+       struct list_head list;
+
+       struct ieee802154_llsec_key_id key_id;
+       u32 frame_counter;
+};
+
+enum {
+       IEEE802154_LLSEC_DEVKEY_IGNORE,
+       IEEE802154_LLSEC_DEVKEY_RESTRICT,
+       IEEE802154_LLSEC_DEVKEY_RECORD,
+
+       __IEEE802154_LLSEC_DEVKEY_MAX,
+};
+
+struct ieee802154_llsec_device {
+       struct list_head list;
+
+       __le16 pan_id;
+       __le16 short_addr;
+       __le64 hwaddr;
+       u32 frame_counter;
+       bool seclevel_exempt;
+
+       u8 key_mode;
+       struct list_head keys;
+};
+
+struct ieee802154_llsec_seclevel {
+       struct list_head list;
+
+       u8 frame_type;
+       u8 cmd_frame_id;
+       bool device_override;
+       u32 sec_levels;
+};
+
+struct ieee802154_llsec_params {
+       bool enabled;
+
+       __be32 frame_counter;
+       u8 out_level;
+       struct ieee802154_llsec_key_id out_key;
+
+       __le64 default_key_source;
+
+       __le16 pan_id;
+       __le64 hwaddr;
+       __le64 coord_hwaddr;
+       __le16 coord_shortaddr;
+};
+
+struct ieee802154_llsec_table {
+       struct list_head keys;
+       struct list_head devices;
+       struct list_head security_levels;
+};
 
 #define IEEE802154_MAC_SCAN_ED         0
 #define IEEE802154_MAC_SCAN_ACTIVE     1
@@ -242,6 +346,53 @@ struct ieee802154_mac_params {
 };
 
 struct wpan_phy;
+
+enum {
+       IEEE802154_LLSEC_PARAM_ENABLED = 1 << 0,
+       IEEE802154_LLSEC_PARAM_FRAME_COUNTER = 1 << 1,
+       IEEE802154_LLSEC_PARAM_OUT_LEVEL = 1 << 2,
+       IEEE802154_LLSEC_PARAM_OUT_KEY = 1 << 3,
+       IEEE802154_LLSEC_PARAM_KEY_SOURCE = 1 << 4,
+       IEEE802154_LLSEC_PARAM_PAN_ID = 1 << 5,
+       IEEE802154_LLSEC_PARAM_HWADDR = 1 << 6,
+       IEEE802154_LLSEC_PARAM_COORD_HWADDR = 1 << 7,
+       IEEE802154_LLSEC_PARAM_COORD_SHORTADDR = 1 << 8,
+};
+
+struct ieee802154_llsec_ops {
+       int (*get_params)(struct net_device *dev,
+                         struct ieee802154_llsec_params *params);
+       int (*set_params)(struct net_device *dev,
+                         const struct ieee802154_llsec_params *params,
+                         int changed);
+
+       int (*add_key)(struct net_device *dev,
+                      const struct ieee802154_llsec_key_id *id,
+                      const struct ieee802154_llsec_key *key);
+       int (*del_key)(struct net_device *dev,
+                      const struct ieee802154_llsec_key_id *id);
+
+       int (*add_dev)(struct net_device *dev,
+                      const struct ieee802154_llsec_device *llsec_dev);
+       int (*del_dev)(struct net_device *dev, __le64 dev_addr);
+
+       int (*add_devkey)(struct net_device *dev,
+                         __le64 device_addr,
+                         const struct ieee802154_llsec_device_key *key);
+       int (*del_devkey)(struct net_device *dev,
+                         __le64 device_addr,
+                         const struct ieee802154_llsec_device_key *key);
+
+       int (*add_seclevel)(struct net_device *dev,
+                           const struct ieee802154_llsec_seclevel *sl);
+       int (*del_seclevel)(struct net_device *dev,
+                           const struct ieee802154_llsec_seclevel *sl);
+
+       void (*lock_table)(struct net_device *dev);
+       void (*get_table)(struct net_device *dev,
+                         struct ieee802154_llsec_table **t);
+       void (*unlock_table)(struct net_device *dev);
+};
 /*
  * This should be located at net_device->ml_priv
  *
@@ -272,6 +423,8 @@ struct ieee802154_mlme_ops {
        void (*get_mac_params)(struct net_device *dev,
                               struct ieee802154_mac_params *params);
 
+       struct ieee802154_llsec_ops *llsec;
+
        /* The fields below are required. */
 
        struct wpan_phy *(*get_phy)(const struct net_device *dev);
index 3bd22795c3e259e1f1f55176c808c6fdcc994600..84b20835b736c53b55a19eac0bbe187e65626fec 100644 (file)
@@ -150,7 +150,7 @@ static inline int INET_ECN_set_ce(struct sk_buff *skb)
 }
 
 /*
- * RFC 6080 4.2
+ * RFC 6040 4.2
  *  To decapsulate the inner header at the tunnel egress, a compliant
  *  tunnel egress MUST set the outgoing ECN field to the codepoint at the
  *  intersection of the appropriate arriving inner header (row) and outer
index 1bdb47715def0e21496ae89a59d3d9bd5f1f2c81..dd1950a7e2730e0024f1c82fac8ab20f9b533fe5 100644 (file)
@@ -292,12 +292,12 @@ static inline struct sock *inet_lookup_listener(struct net *net,
 #define INET_ADDR_COOKIE(__name, __saddr, __daddr) \
        const __addrpair __name = (__force __addrpair) ( \
                                   (((__force __u64)(__be32)(__saddr)) << 32) | \
-                                  ((__force __u64)(__be32)(__daddr)));
+                                  ((__force __u64)(__be32)(__daddr)))
 #else /* __LITTLE_ENDIAN */
 #define INET_ADDR_COOKIE(__name, __saddr, __daddr) \
        const __addrpair __name = (__force __addrpair) ( \
                                   (((__force __u64)(__be32)(__daddr)) << 32) | \
-                                  ((__force __u64)(__be32)(__saddr)));
+                                  ((__force __u64)(__be32)(__saddr)))
 #endif /* __BIG_ENDIAN */
 #define INET_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif)    \
        (((__sk)->sk_portpair == (__ports))                     &&      \
@@ -306,7 +306,9 @@ static inline struct sock *inet_lookup_listener(struct net *net,
           ((__sk)->sk_bound_dev_if == (__dif)))                &&      \
         net_eq(sock_net(__sk), (__net)))
 #else /* 32-bit arch */
-#define INET_ADDR_COOKIE(__name, __saddr, __daddr)
+#define INET_ADDR_COOKIE(__name, __saddr, __daddr) \
+       const int __name __deprecated __attribute__((unused))
+
 #define INET_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif) \
        (((__sk)->sk_portpair == (__ports))             &&              \
         ((__sk)->sk_daddr      == (__saddr))           &&              \
index 1833c3f389ee64a0c6b3862d4f2fbc6db0984b0a..b1edf17bec01130f9751747c4d092e5de50aaeac 100644 (file)
@@ -90,6 +90,7 @@ struct inet_request_sock {
        kmemcheck_bitfield_end(flags);
        struct ip_options_rcu   *opt;
        struct sk_buff          *pktopts;
+       u32                     ir_mark;
 };
 
 static inline struct inet_request_sock *inet_rsk(const struct request_sock *sk)
@@ -97,6 +98,15 @@ static inline struct inet_request_sock *inet_rsk(const struct request_sock *sk)
        return (struct inet_request_sock *)sk;
 }
 
+static inline u32 inet_request_mark(struct sock *sk, struct sk_buff *skb)
+{
+       if (!sk->sk_mark && sock_net(sk)->ipv4.sysctl_tcp_fwmark_accept) {
+               return skb->mark;
+       } else {
+               return sk->sk_mark;
+       }
+}
+
 struct inet_cork {
        unsigned int            flags;
        __be32                  addr;
index 058271bde27a52b59dbc1b495433eec8a63cf18e..01d590ee5e7eb5250eaef9e0880153c257b706bd 100644 (file)
@@ -41,14 +41,13 @@ struct inet_peer {
                struct rcu_head     gc_rcu;
        };
        /*
-        * Once inet_peer is queued for deletion (refcnt == -1), following fields
-        * are not available: rid, ip_id_count
+        * Once inet_peer is queued for deletion (refcnt == -1), following field
+        * is not available: rid
         * We can share memory with rcu_head to help keep inet_peer small.
         */
        union {
                struct {
                        atomic_t                        rid;            /* Frag reception counter */
-                       atomic_t                        ip_id_count;    /* IP ID for the next packet */
                };
                struct rcu_head         rcu;
                struct inet_peer        *gc_next;
@@ -165,21 +164,11 @@ bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout);
 void inetpeer_invalidate_tree(struct inet_peer_base *);
 
 /*
- * temporary check to make sure we dont access rid, ip_id_count, tcp_ts,
+ * temporary check to make sure we dont access rid, tcp_ts,
  * tcp_ts_stamp if no refcount is taken on inet_peer
  */
 static inline void inet_peer_refcheck(const struct inet_peer *p)
 {
        WARN_ON_ONCE(atomic_read(&p->refcnt) <= 0);
 }
-
-
-/* can be called with or without local BH being disabled */
-static inline int inet_getid(struct inet_peer *p, int more)
-{
-       more++;
-       inet_peer_refcheck(p);
-       return atomic_add_return(more, &p->ip_id_count) - more;
-}
-
 #endif /* _NET_INETPEER_H */
index 3ec2b0fb9d8395384373917691f49c433262a8db..0e795df05ec983d07d7acc52cddabd728ea6e0b1 100644 (file)
@@ -196,35 +196,31 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
 #define NET_ADD_STATS_BH(net, field, adnd) SNMP_ADD_STATS_BH((net)->mib.net_statistics, field, adnd)
 #define NET_ADD_STATS_USER(net, field, adnd) SNMP_ADD_STATS_USER((net)->mib.net_statistics, field, adnd)
 
-unsigned long snmp_fold_field(void __percpu *mib[], int offt);
+unsigned long snmp_fold_field(void __percpu *mib, int offt);
 #if BITS_PER_LONG==32
-u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t sync_off);
+u64 snmp_fold_field64(void __percpu *mib, int offt, size_t sync_off);
 #else
-static inline u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t syncp_off)
+static inline u64 snmp_fold_field64(void __percpu *mib, int offt, size_t syncp_off)
 {
        return snmp_fold_field(mib, offt);
 }
 #endif
-int snmp_mib_init(void __percpu *ptr[2], size_t mibsize, size_t align);
-
-static inline void snmp_mib_free(void __percpu *ptr[SNMP_ARRAY_SZ])
-{
-       int i;
-
-       BUG_ON(ptr == NULL);
-       for (i = 0; i < SNMP_ARRAY_SZ; i++) {
-               free_percpu(ptr[i]);
-               ptr[i] = NULL;
-       }
-}
 
 void inet_get_local_port_range(struct net *net, int *low, int *high);
 
-extern unsigned long *sysctl_local_reserved_ports;
-static inline int inet_is_reserved_local_port(int port)
+#ifdef CONFIG_SYSCTL
+static inline int inet_is_local_reserved_port(struct net *net, int port)
 {
-       return test_bit(port, sysctl_local_reserved_ports);
+       if (!net->ipv4.sysctl_local_reserved_ports)
+               return 0;
+       return test_bit(port, net->ipv4.sysctl_local_reserved_ports);
 }
+#else
+static inline int inet_is_local_reserved_port(struct net *net, int port)
+{
+       return 0;
+}
+#endif
 
 extern int sysctl_ip_nonlocal_bind;
 
@@ -243,6 +239,9 @@ void ipfrag_init(void);
 
 void ip_static_sysctl_init(void);
 
+#define IP4_REPLY_MARK(net, mark) \
+       ((net)->ipv4.sysctl_fwmark_reflect ? (mark) : 0)
+
 static inline bool ip_is_fragment(const struct iphdr *iph)
 {
        return (iph->frag_off & htons(IP_MF | IP_OFFSET)) != 0;
@@ -281,7 +280,7 @@ static inline bool ip_sk_use_pmtu(const struct sock *sk)
        return inet_sk(sk)->pmtudisc < IP_PMTUDISC_PROBE;
 }
 
-static inline bool ip_sk_local_df(const struct sock *sk)
+static inline bool ip_sk_ignore_df(const struct sock *sk)
 {
        return inet_sk(sk)->pmtudisc < IP_PMTUDISC_DO ||
               inet_sk(sk)->pmtudisc == IP_PMTUDISC_OMIT;
@@ -310,36 +309,48 @@ static inline unsigned int ip_skb_dst_mtu(const struct sk_buff *skb)
        }
 }
 
-void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst, int more);
+#define IP_IDENTS_SZ 2048u
+extern atomic_t *ip_idents;
+
+static inline u32 ip_idents_reserve(u32 hash, int segs)
+{
+       atomic_t *id_ptr = ip_idents + hash % IP_IDENTS_SZ;
+
+       return atomic_add_return(segs, id_ptr) - segs;
+}
+
+void __ip_select_ident(struct iphdr *iph, int segs);
 
-static inline void ip_select_ident(struct sk_buff *skb, struct dst_entry *dst, struct sock *sk)
+static inline void ip_select_ident_segs(struct sk_buff *skb, struct sock *sk, int segs)
 {
        struct iphdr *iph = ip_hdr(skb);
 
-       if ((iph->frag_off & htons(IP_DF)) && !skb->local_df) {
+       if ((iph->frag_off & htons(IP_DF)) && !skb->ignore_df) {
                /* This is only to work around buggy Windows95/2000
                 * VJ compression implementations.  If the ID field
                 * does not change, they drop every other packet in
                 * a TCP stream using header compression.
                 */
-               iph->id = (sk && inet_sk(sk)->inet_daddr) ?
-                                       htons(inet_sk(sk)->inet_id++) : 0;
-       } else
-               __ip_select_ident(iph, dst, 0);
+               if (sk && inet_sk(sk)->inet_daddr) {
+                       iph->id = htons(inet_sk(sk)->inet_id);
+                       inet_sk(sk)->inet_id += segs;
+               } else {
+                       iph->id = 0;
+               }
+       } else {
+               __ip_select_ident(iph, segs);
+       }
 }
 
-static inline void ip_select_ident_more(struct sk_buff *skb, struct dst_entry *dst, struct sock *sk, int more)
+static inline void ip_select_ident(struct sk_buff *skb, struct sock *sk)
 {
-       struct iphdr *iph = ip_hdr(skb);
+       ip_select_ident_segs(skb, sk, 1);
+}
 
-       if ((iph->frag_off & htons(IP_DF)) && !skb->local_df) {
-               if (sk && inet_sk(sk)->inet_daddr) {
-                       iph->id = htons(inet_sk(sk)->inet_id);
-                       inet_sk(sk)->inet_id += 1 + more;
-               } else
-                       iph->id = 0;
-       } else
-               __ip_select_ident(iph, dst, more);
+static inline __wsum inet_compute_pseudo(struct sk_buff *skb, int proto)
+{
+       return csum_tcpudp_nofold(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr,
+                                 skb->len, proto, 0);
 }
 
 /*
index 9e3c540c1b110c71b65003a6aac22cc6c333be5a..55236cb711745fc76ef4c897bcf70e957b34611c 100644 (file)
@@ -41,6 +41,13 @@ __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
                        __wsum csum);
 #endif
 
+static inline __wsum ip6_compute_pseudo(struct sk_buff *skb, int proto)
+{
+       return ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+                                           &ipv6_hdr(skb)->daddr,
+                                           skb->len, proto, 0));
+}
+
 static __inline__ __sum16 tcp_v6_check(int len,
                                   const struct in6_addr *saddr,
                                   const struct in6_addr *daddr,
@@ -75,5 +82,17 @@ static inline void tcp_v6_send_check(struct sock *sk, struct sk_buff *skb)
 }
 #endif
 
+static inline __sum16 udp_v6_check(int len,
+                                  const struct in6_addr *saddr,
+                                  const struct in6_addr *daddr,
+                                  __wsum base)
+{
+       return csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, base);
+}
+
+void udp6_set_csum(bool nocheck, struct sk_buff *skb,
+                  const struct in6_addr *saddr,
+                  const struct in6_addr *daddr, int len);
+
 int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto);
 #endif
index 216cecce65e9e1200cd760de64c27ac852ffc85b..1d09b46c1e489325b95f9987327d95ca8affed08 100644 (file)
@@ -186,7 +186,7 @@ static inline bool ip6_sk_accept_pmtu(const struct sock *sk)
               inet6_sk(sk)->pmtudisc != IPV6_PMTUDISC_OMIT;
 }
 
-static inline bool ip6_sk_local_df(const struct sock *sk)
+static inline bool ip6_sk_ignore_df(const struct sock *sk)
 {
        return inet6_sk(sk)->pmtudisc < IPV6_PMTUDISC_DO ||
               inet6_sk(sk)->pmtudisc == IPV6_PMTUDISC_OMIT;
index d640925bc4543bdfb30bf15d164bececbda7e798..574337fe72ddbd3038c061dab5aef142ea0f068b 100644 (file)
@@ -113,6 +113,9 @@ struct frag_hdr {
 #define        IP6_MF          0x0001
 #define        IP6_OFFSET      0xFFF8
 
+#define IP6_REPLY_MARK(net, mark) \
+       ((net)->ipv6.sysctl.fwmark_reflect ? (mark) : 0)
+
 #include <net/sock.h>
 
 /* sysctls */
@@ -583,6 +586,11 @@ static inline bool ipv6_addr_orchid(const struct in6_addr *a)
        return (a->s6_addr32[0] & htonl(0xfffffff0)) == htonl(0x20010010);
 }
 
+static inline bool ipv6_addr_is_multicast(const struct in6_addr *addr)
+{
+       return (addr->s6_addr32[0] & htonl(0xFF000000)) == htonl(0xFF000000);
+}
+
 static inline void ipv6_addr_set_v4mapped(const __be32 addr,
                                          struct in6_addr *v4mapped)
 {
@@ -660,10 +668,22 @@ static inline int ipv6_addr_diff(const struct in6_addr *a1, const struct in6_add
        return __ipv6_addr_diff(a1, a2, sizeof(struct in6_addr));
 }
 
-void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt);
-
 int ip6_dst_hoplimit(struct dst_entry *dst);
 
+static inline int ip6_sk_dst_hoplimit(struct ipv6_pinfo *np, struct flowi6 *fl6,
+                                     struct dst_entry *dst)
+{
+       int hlimit;
+
+       if (ipv6_addr_is_multicast(&fl6->daddr))
+               hlimit = np->mcast_hops;
+       else
+               hlimit = np->hop_limit;
+       if (hlimit < 0)
+               hlimit = ip6_dst_hoplimit(dst);
+       return hlimit;
+}
+
 /*
  *     Header manipulation
  */
index 8248e3909fdf7d8531890e15bc3f18c5b90ac95f..421b6ecb4b2cdee892379212f9d252f3f3ecffbc 100644 (file)
@@ -188,6 +188,43 @@ struct ieee80211_chanctx_conf {
        u8 drv_priv[0] __aligned(sizeof(void *));
 };
 
+/**
+ * enum ieee80211_chanctx_switch_mode - channel context switch mode
+ * @CHANCTX_SWMODE_REASSIGN_VIF: Both old and new contexts already
+ *     exist (and will continue to exist), but the virtual interface
+ *     needs to be switched from one to the other.
+ * @CHANCTX_SWMODE_SWAP_CONTEXTS: The old context exists but will stop
+ *      to exist with this call, the new context doesn't exist but
+ *      will be active after this call, the virtual interface switches
+ *      from the old to the new (note that the driver may of course
+ *      implement this as an on-the-fly chandef switch of the existing
+ *      hardware context, but the mac80211 pointer for the old context
+ *      will cease to exist and only the new one will later be used
+ *      for changes/removal.)
+ */
+enum ieee80211_chanctx_switch_mode {
+       CHANCTX_SWMODE_REASSIGN_VIF,
+       CHANCTX_SWMODE_SWAP_CONTEXTS,
+};
+
+/**
+ * struct ieee80211_vif_chanctx_switch - vif chanctx switch information
+ *
+ * This is structure is used to pass information about a vif that
+ * needs to switch from one chanctx to another.  The
+ * &ieee80211_chanctx_switch_mode defines how the switch should be
+ * done.
+ *
+ * @vif: the vif that should be switched from old_ctx to new_ctx
+ * @old_ctx: the old context to which the vif was assigned
+ * @new_ctx: the new context to which the vif must be assigned
+ */
+struct ieee80211_vif_chanctx_switch {
+       struct ieee80211_vif *vif;
+       struct ieee80211_chanctx_conf *old_ctx;
+       struct ieee80211_chanctx_conf *new_ctx;
+};
+
 /**
  * enum ieee80211_bss_change - BSS change notification flags
  *
@@ -1113,7 +1150,9 @@ enum ieee80211_vif_flags {
  * @addr: address of this interface
  * @p2p: indicates whether this AP or STA interface is a p2p
  *     interface, i.e. a GO or p2p-sta respectively
- * @csa_active: marks whether a channel switch is going on
+ * @csa_active: marks whether a channel switch is going on. Internally it is
+ *     write-protected by sdata_lock and local->mtx so holding either is fine
+ *     for read access.
  * @driver_flags: flags/capabilities the driver has for this interface,
  *     these need to be set (or cleared) when the interface is added
  *     or, if supported by the driver, the interface type is changed
@@ -1202,14 +1241,18 @@ struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev);
  *     fall back to software crypto. Note that this flag deals only with
  *     RX, if your crypto engine can't deal with TX you can also set the
  *     %IEEE80211_KEY_FLAG_SW_MGMT_TX flag to encrypt such frames in SW.
+ * @IEEE80211_KEY_FLAG_GENERATE_IV_MGMT: This flag should be set by the
+ *     driver for a CCMP key to indicate that is requires IV generation
+ *     only for managment frames (MFP).
  */
 enum ieee80211_key_flags {
-       IEEE80211_KEY_FLAG_GENERATE_IV  = 1<<1,
-       IEEE80211_KEY_FLAG_GENERATE_MMIC= 1<<2,
-       IEEE80211_KEY_FLAG_PAIRWISE     = 1<<3,
-       IEEE80211_KEY_FLAG_SW_MGMT_TX   = 1<<4,
-       IEEE80211_KEY_FLAG_PUT_IV_SPACE = 1<<5,
-       IEEE80211_KEY_FLAG_RX_MGMT      = 1<<6,
+       IEEE80211_KEY_FLAG_GENERATE_IV_MGMT     = BIT(0),
+       IEEE80211_KEY_FLAG_GENERATE_IV          = BIT(1),
+       IEEE80211_KEY_FLAG_GENERATE_MMIC        = BIT(2),
+       IEEE80211_KEY_FLAG_PAIRWISE             = BIT(3),
+       IEEE80211_KEY_FLAG_SW_MGMT_TX           = BIT(4),
+       IEEE80211_KEY_FLAG_PUT_IV_SPACE         = BIT(5),
+       IEEE80211_KEY_FLAG_RX_MGMT              = BIT(6),
 };
 
 /**
@@ -1370,6 +1413,7 @@ struct ieee80211_sta_rates {
  *     the station moves to associated state.
  * @smps_mode: current SMPS mode (off, static or dynamic)
  * @rates: rate control selection table
+ * @tdls: indicates whether the STA is a TDLS peer
  */
 struct ieee80211_sta {
        u32 supp_rates[IEEE80211_NUM_BANDS];
@@ -1384,6 +1428,7 @@ struct ieee80211_sta {
        enum ieee80211_sta_rx_bandwidth bandwidth;
        enum ieee80211_smps_mode smps_mode;
        struct ieee80211_sta_rates __rcu *rates;
+       bool tdls;
 
        /* must be last */
        u8 drv_priv[0] __aligned(sizeof(void *));
@@ -1555,6 +1600,12 @@ struct ieee80211_tx_control {
  *     for a single active channel while using channel contexts. When support
  *     is not enabled the default action is to disconnect when getting the
  *     CSA frame.
+ *
+ * @IEEE80211_HW_CHANGE_RUNNING_CHANCTX: The hardware can change a
+ *     channel context on-the-fly.  This is needed for channel switch
+ *     on single-channel hardware.  It can also be used as an
+ *     optimization in certain channel switch cases with
+ *     multi-channel.
  */
 enum ieee80211_hw_flags {
        IEEE80211_HW_HAS_RATE_CONTROL                   = 1<<0,
@@ -1586,6 +1637,7 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_TIMING_BEACON_ONLY                 = 1<<26,
        IEEE80211_HW_SUPPORTS_HT_CCK_RATES              = 1<<27,
        IEEE80211_HW_CHANCTX_STA_CSA                    = 1<<28,
+       IEEE80211_HW_CHANGE_RUNNING_CHANCTX             = 1<<29,
 };
 
 /**
@@ -2609,6 +2661,7 @@ enum ieee80211_roc_type {
  *     of queues to flush, which is useful if different virtual interfaces
  *     use different hardware queues; it may also indicate all queues.
  *     If the parameter @drop is set to %true, pending frames may be dropped.
+ *     Note that vif can be NULL.
  *     The callback can sleep.
  *
  * @channel_switch: Drivers that need (or want) to offload the channel
@@ -2720,6 +2773,11 @@ enum ieee80211_roc_type {
  *     to vif. Possible use is for hw queue remapping.
  * @unassign_vif_chanctx: Notifies device driver about channel context being
  *     unbound from vif.
+ * @switch_vif_chanctx: switch a number of vifs from one chanctx to
+ *     another, as specified in the list of
+ *     @ieee80211_vif_chanctx_switch passed to the driver, according
+ *     to the mode defined in &ieee80211_chanctx_switch_mode.
+ *
  * @start_ap: Start operation on the AP interface, this is called after all the
  *     information in bss_conf is set and beacon can be retrieved. A channel
  *     context is bound before this is called. Note that if the driver uses
@@ -2753,6 +2811,10 @@ enum ieee80211_roc_type {
  *     information in bss_conf is set up and the beacon can be retrieved. A
  *     channel context is bound before this is called.
  * @leave_ibss: Leave the IBSS again.
+ *
+ * @get_expected_throughput: extract the expected throughput towards the
+ *     specified station. The returned value is expressed in Kbps. It returns 0
+ *     if the RC algorithm does not have proper data to provide.
  */
 struct ieee80211_ops {
        void (*tx)(struct ieee80211_hw *hw,
@@ -2871,7 +2933,8 @@ struct ieee80211_ops {
                             struct netlink_callback *cb,
                             void *data, int len);
 #endif
-       void (*flush)(struct ieee80211_hw *hw, u32 queues, bool drop);
+       void (*flush)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                     u32 queues, bool drop);
        void (*channel_switch)(struct ieee80211_hw *hw,
                               struct ieee80211_channel_switch *ch_switch);
        int (*set_antenna)(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant);
@@ -2931,6 +2994,10 @@ struct ieee80211_ops {
        void (*unassign_vif_chanctx)(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif,
                                     struct ieee80211_chanctx_conf *ctx);
+       int (*switch_vif_chanctx)(struct ieee80211_hw *hw,
+                                 struct ieee80211_vif_chanctx_switch *vifs,
+                                 int n_vifs,
+                                 enum ieee80211_chanctx_switch_mode mode);
 
        void (*restart_complete)(struct ieee80211_hw *hw);
 
@@ -2945,6 +3012,7 @@ struct ieee80211_ops {
 
        int (*join_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
        void (*leave_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+       u32 (*get_expected_throughput)(struct ieee80211_sta *sta);
 };
 
 /**
@@ -3394,6 +3462,47 @@ void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
  */
 void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets);
 
+#define IEEE80211_MAX_CSA_COUNTERS_NUM 2
+
+/**
+ * struct ieee80211_mutable_offsets - mutable beacon offsets
+ * @tim_offset: position of TIM element
+ * @tim_length: size of TIM element
+ * @csa_counter_offs: array of IEEE80211_MAX_CSA_COUNTERS_NUM offsets
+ *     to CSA counters.  This array can contain zero values which
+ *     should be ignored.
+ */
+struct ieee80211_mutable_offsets {
+       u16 tim_offset;
+       u16 tim_length;
+
+       u16 csa_counter_offs[IEEE80211_MAX_CSA_COUNTERS_NUM];
+};
+
+/**
+ * ieee80211_beacon_get_template - beacon template generation function
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @offs: &struct ieee80211_mutable_offsets pointer to struct that will
+ *     receive the offsets that may be updated by the driver.
+ *
+ * If the driver implements beaconing modes, it must use this function to
+ * obtain the beacon template.
+ *
+ * This function should be used if the beacon frames are generated by the
+ * device, and then the driver must use the returned beacon as the template
+ * The driver or the device are responsible to update the DTIM and, when
+ * applicable, the CSA count.
+ *
+ * The driver is responsible for freeing the returned skb.
+ *
+ * Return: The beacon template. %NULL on error.
+ */
+struct sk_buff *
+ieee80211_beacon_get_template(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif,
+                             struct ieee80211_mutable_offsets *offs);
+
 /**
  * ieee80211_beacon_get_tim - beacon generation function
  * @hw: pointer obtained from ieee80211_alloc_hw().
@@ -3405,16 +3514,12 @@ void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets);
  *     Set to 0 if invalid (in non-AP modes).
  *
  * If the driver implements beaconing modes, it must use this function to
- * obtain the beacon frame/template.
+ * obtain the beacon frame.
  *
  * If the beacon frames are generated by the host system (i.e., not in
  * hardware/firmware), the driver uses this function to get each beacon
- * frame from mac80211 -- it is responsible for calling this function
- * before the beacon is needed (e.g. based on hardware interrupt).
- *
- * If the beacon frames are generated by the device, then the driver
- * must use the returned beacon as the template and change the TIM IE
- * according to the current DTIM parameters/TIM bitmap.
+ * frame from mac80211 -- it is responsible for calling this function exactly
+ * once before the beacon is needed (e.g. based on hardware interrupt).
  *
  * The driver is responsible for freeing the returned skb.
  *
@@ -3439,6 +3544,20 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
        return ieee80211_beacon_get_tim(hw, vif, NULL, NULL);
 }
 
+/**
+ * ieee80211_csa_update_counter - request mac80211 to decrement the csa counter
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * The csa counter should be updated after each beacon transmission.
+ * This function is called implicitly when
+ * ieee80211_beacon_get/ieee80211_beacon_get_tim are called, however if the
+ * beacon frames are generated by the device, the driver should call this
+ * function after each beacon transmission to sync mac80211's csa counters.
+ *
+ * Return: new csa counter value
+ */
+u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif);
+
 /**
  * ieee80211_csa_finish - notify mac80211 about channel switch
  * @vif: &struct ieee80211_vif pointer from the add_interface callback.
@@ -4467,6 +4586,8 @@ struct rate_control_ops {
        void (*add_sta_debugfs)(void *priv, void *priv_sta,
                                struct dentry *dir);
        void (*remove_sta_debugfs)(void *priv, void *priv_sta);
+
+       u32 (*get_expected_throughput)(void *priv_sta);
 };
 
 static inline int rate_supported(struct ieee80211_sta *sta,
@@ -4576,7 +4697,9 @@ conf_is_ht40(struct ieee80211_conf *conf)
 static inline bool
 conf_is_ht(struct ieee80211_conf *conf)
 {
-       return conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT;
+       return (conf->chandef.width != NL80211_CHAN_WIDTH_5) &&
+               (conf->chandef.width != NL80211_CHAN_WIDTH_10) &&
+               (conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT);
 }
 
 static inline enum nl80211_iftype
index 5f9eb260990f6507d61f42d2463d45455346e812..361d26077196678af5bd7ce217715a2cc847f2af 100644 (file)
@@ -373,6 +373,14 @@ static inline void rt_genid_bump_ipv6(struct net *net)
 }
 #endif
 
+#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
+static inline struct netns_ieee802154_lowpan *
+net_ieee802154_lowpan(struct net *net)
+{
+       return &net->ieee802154_lowpan;
+}
+#endif
+
 /* For callers who don't really care about whether it's IPv4 or IPv6 */
 static inline void rt_genid_bump_all(struct net *net)
 {
index 07eaaf60409215198961cea9834c2d770a90f02e..a71dd333ac6869fdce096dce3a26be133e4d4aae 100644 (file)
@@ -48,6 +48,8 @@ unsigned int nf_nat_setup_info(struct nf_conn *ct,
 extern unsigned int nf_nat_alloc_null_binding(struct nf_conn *ct,
                                              unsigned int hooknum);
 
+struct nf_conn_nat *nf_ct_nat_ext_add(struct nf_conn *ct);
+
 /* Is this tuple already taken? (not by us)*/
 int nf_nat_used_tuple(const struct nf_conntrack_tuple *tuple,
                      const struct nf_conn *ignored_conntrack);
index e6bc14d8fa9a9a4b324fac9df5a47e64277f2aa8..7ee6ce6564aecc6b98eb6247e6d6c6263a3e130c 100644 (file)
@@ -72,21 +72,23 @@ static inline void nft_data_debug(const struct nft_data *data)
  *     struct nft_ctx - nf_tables rule/set context
  *
  *     @net: net namespace
- *     @skb: netlink skb
- *     @nlh: netlink message header
  *     @afi: address family info
  *     @table: the table the chain is contained in
  *     @chain: the chain the rule is contained in
  *     @nla: netlink attributes
+ *     @portid: netlink portID of the original message
+ *     @seq: netlink sequence number
+ *     @report: notify via unicast netlink message
  */
 struct nft_ctx {
        struct net                      *net;
-       const struct sk_buff            *skb;
-       const struct nlmsghdr           *nlh;
-       const struct nft_af_info        *afi;
-       const struct nft_table          *table;
-       const struct nft_chain          *chain;
+       struct nft_af_info              *afi;
+       struct nft_table                *table;
+       struct nft_chain                *chain;
        const struct nlattr * const     *nla;
+       u32                             portid;
+       u32                             seq;
+       bool                            report;
 };
 
 struct nft_data_desc {
@@ -145,6 +147,44 @@ struct nft_set_iter {
                              const struct nft_set_elem *elem);
 };
 
+/**
+ *     struct nft_set_desc - description of set elements
+ *
+ *     @klen: key length
+ *     @dlen: data length
+ *     @size: number of set elements
+ */
+struct nft_set_desc {
+       unsigned int            klen;
+       unsigned int            dlen;
+       unsigned int            size;
+};
+
+/**
+ *     enum nft_set_class - performance class
+ *
+ *     @NFT_LOOKUP_O_1: constant, O(1)
+ *     @NFT_LOOKUP_O_LOG_N: logarithmic, O(log N)
+ *     @NFT_LOOKUP_O_N: linear, O(N)
+ */
+enum nft_set_class {
+       NFT_SET_CLASS_O_1,
+       NFT_SET_CLASS_O_LOG_N,
+       NFT_SET_CLASS_O_N,
+};
+
+/**
+ *     struct nft_set_estimate - estimation of memory and performance
+ *                               characteristics
+ *
+ *     @size: required memory
+ *     @class: lookup performance class
+ */
+struct nft_set_estimate {
+       unsigned int            size;
+       enum nft_set_class      class;
+};
+
 /**
  *     struct nft_set_ops - nf_tables set operations
  *
@@ -174,7 +214,11 @@ struct nft_set_ops {
                                                struct nft_set_iter *iter);
 
        unsigned int                    (*privsize)(const struct nlattr * const nla[]);
+       bool                            (*estimate)(const struct nft_set_desc *desc,
+                                                   u32 features,
+                                                   struct nft_set_estimate *est);
        int                             (*init)(const struct nft_set *set,
+                                               const struct nft_set_desc *desc,
                                                const struct nlattr * const nla[]);
        void                            (*destroy)(const struct nft_set *set);
 
@@ -194,6 +238,8 @@ void nft_unregister_set(struct nft_set_ops *ops);
  *     @name: name of the set
  *     @ktype: key type (numeric type defined by userspace, not used in the kernel)
  *     @dtype: data type (verdict or numeric type defined by userspace)
+ *     @size: maximum set size
+ *     @nelems: number of elements
  *     @ops: set ops
  *     @flags: set flags
  *     @klen: key length
@@ -206,6 +252,8 @@ struct nft_set {
        char                            name[IFNAMSIZ];
        u32                             ktype;
        u32                             dtype;
+       u32                             size;
+       u32                             nelems;
        /* runtime data below here */
        const struct nft_set_ops        *ops ____cacheline_aligned;
        u16                             flags;
@@ -222,6 +270,8 @@ static inline void *nft_set_priv(const struct nft_set *set)
 
 struct nft_set *nf_tables_set_lookup(const struct nft_table *table,
                                     const struct nlattr *nla);
+struct nft_set *nf_tables_set_lookup_byid(const struct net *net,
+                                         const struct nlattr *nla);
 
 /**
  *     struct nft_set_binding - nf_tables set binding
@@ -341,18 +391,75 @@ struct nft_rule {
 };
 
 /**
- *     struct nft_rule_trans - nf_tables rule update in transaction
+ *     struct nft_trans - nf_tables object update in transaction
  *
+ *     @rcu_head: rcu head to defer release of transaction data
  *     @list: used internally
- *     @ctx: rule context
- *     @rule: rule that needs to be updated
+ *     @msg_type: message type
+ *     @ctx: transaction context
+ *     @data: internal information related to the transaction
  */
-struct nft_rule_trans {
+struct nft_trans {
+       struct rcu_head                 rcu_head;
        struct list_head                list;
+       int                             msg_type;
        struct nft_ctx                  ctx;
+       char                            data[0];
+};
+
+struct nft_trans_rule {
        struct nft_rule                 *rule;
 };
 
+#define nft_trans_rule(trans)  \
+       (((struct nft_trans_rule *)trans->data)->rule)
+
+struct nft_trans_set {
+       struct nft_set  *set;
+       u32             set_id;
+};
+
+#define nft_trans_set(trans)   \
+       (((struct nft_trans_set *)trans->data)->set)
+#define nft_trans_set_id(trans)        \
+       (((struct nft_trans_set *)trans->data)->set_id)
+
+struct nft_trans_chain {
+       bool            update;
+       char            name[NFT_CHAIN_MAXNAMELEN];
+       struct nft_stats __percpu *stats;
+       u8              policy;
+};
+
+#define nft_trans_chain_update(trans)  \
+       (((struct nft_trans_chain *)trans->data)->update)
+#define nft_trans_chain_name(trans)    \
+       (((struct nft_trans_chain *)trans->data)->name)
+#define nft_trans_chain_stats(trans)   \
+       (((struct nft_trans_chain *)trans->data)->stats)
+#define nft_trans_chain_policy(trans)  \
+       (((struct nft_trans_chain *)trans->data)->policy)
+
+struct nft_trans_table {
+       bool            update;
+       bool            enable;
+};
+
+#define nft_trans_table_update(trans)  \
+       (((struct nft_trans_table *)trans->data)->update)
+#define nft_trans_table_enable(trans)  \
+       (((struct nft_trans_table *)trans->data)->enable)
+
+struct nft_trans_elem {
+       struct nft_set          *set;
+       struct nft_set_elem     elem;
+};
+
+#define nft_trans_elem_set(trans)      \
+       (((struct nft_trans_elem *)trans->data)->set)
+#define nft_trans_elem(trans)  \
+       (((struct nft_trans_elem *)trans->data)->elem)
+
 static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule)
 {
        return (struct nft_expr *)&rule->data[0];
@@ -385,6 +492,7 @@ static inline void *nft_userdata(const struct nft_rule *rule)
 
 enum nft_chain_flags {
        NFT_BASE_CHAIN                  = 0x1,
+       NFT_CHAIN_INACTIVE              = 0x2,
 };
 
 /**
diff --git a/include/net/netfilter/nft_meta.h b/include/net/netfilter/nft_meta.h
new file mode 100644 (file)
index 0000000..0ee47c3
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef _NFT_META_H_
+#define _NFT_META_H_
+
+struct nft_meta {
+       enum nft_meta_keys      key:8;
+       union {
+               enum nft_registers      dreg:8;
+               enum nft_registers      sreg:8;
+       };
+};
+
+extern const struct nla_policy nft_meta_policy[];
+
+int nft_meta_get_init(const struct nft_ctx *ctx,
+                     const struct nft_expr *expr,
+                     const struct nlattr * const tb[]);
+
+int nft_meta_set_init(const struct nft_ctx *ctx,
+                     const struct nft_expr *expr,
+                     const struct nlattr * const tb[]);
+
+int nft_meta_get_dump(struct sk_buff *skb,
+                     const struct nft_expr *expr);
+
+int nft_meta_set_dump(struct sk_buff *skb,
+                     const struct nft_expr *expr);
+
+void nft_meta_get_eval(const struct nft_expr *expr,
+                      struct nft_data data[NFT_REG_MAX + 1],
+                      const struct nft_pktinfo *pkt);
+
+void nft_meta_set_eval(const struct nft_expr *expr,
+                      struct nft_data data[NFT_REG_MAX + 1],
+                      const struct nft_pktinfo *pkt);
+
+#endif
index b2704fd0ec80d714bc9e7dd9b87721da1853173c..aec5e12f9f19f1a6c506e47f60cc3056d7ce2a3d 100644 (file)
@@ -77,10 +77,17 @@ struct netns_ipv4 {
        int sysctl_ip_no_pmtu_disc;
        int sysctl_ip_fwd_use_pmtu;
 
+       int sysctl_fwmark_reflect;
+       int sysctl_tcp_fwmark_accept;
+
        struct ping_group_range ping_group_range;
 
        atomic_t dev_addr_genid;
 
+#ifdef CONFIG_SYSCTL
+       unsigned long *sysctl_local_reserved_ports;
+#endif
+
 #ifdef CONFIG_IP_MROUTE
 #ifndef CONFIG_IP_MROUTE_MULTIPLE_TABLES
        struct mr_table         *mrt;
index 21edaf1f79161535af7ae1ae3ae7535ff1a236e3..19d3446e59d2555639e9553b1958d7354792af1e 100644 (file)
@@ -30,6 +30,7 @@ struct netns_sysctl_ipv6 {
        int flowlabel_consistency;
        int icmpv6_time;
        int anycast_src_echo_reply;
+       int fwmark_reflect;
 };
 
 struct netns_ipv6 {
index 7655cfe27c3465f726dc0d1eed26040e61b6b366..bdf55c3b7a19ee1756c528ba067d79cef7e89d57 100644 (file)
@@ -36,6 +36,7 @@ enum {
        NFC_DIGITAL_RF_TECH_212F,
        NFC_DIGITAL_RF_TECH_424F,
        NFC_DIGITAL_RF_TECH_ISO15693,
+       NFC_DIGITAL_RF_TECH_106B,
 
        NFC_DIGITAL_RF_TECH_LAST,
 };
@@ -62,6 +63,9 @@ enum {
        NFC_DIGITAL_FRAMING_ISO15693_INVENTORY,
        NFC_DIGITAL_FRAMING_ISO15693_T5T,
 
+       NFC_DIGITAL_FRAMING_NFCB,
+       NFC_DIGITAL_FRAMING_NFCB_T4T,
+
        NFC_DIGITAL_FRAMING_LAST,
 };
 
index 03c4650b548ca7b01c24ba0f95317139949c7cdb..61286db54388b9d03b6a49b494d1492e7cfd8e5c 100644 (file)
@@ -27,6 +27,7 @@ struct nfc_hci_dev;
 struct nfc_hci_ops {
        int (*open) (struct nfc_hci_dev *hdev);
        void (*close) (struct nfc_hci_dev *hdev);
+       int (*load_session) (struct nfc_hci_dev *hdev);
        int (*hci_ready) (struct nfc_hci_dev *hdev);
        /*
         * xmit must always send the complete buffer before
index 2e8b40c16274f73d1ef98d6ee17fa17da172354e..6c583e244de2198d41effbf8a21086296a2c7e40 100644 (file)
@@ -264,4 +264,7 @@ int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type);
 int nfc_remove_se(struct nfc_dev *dev, u32 se_idx);
 struct nfc_se *nfc_find_se(struct nfc_dev *dev, u32 se_idx);
 
+void nfc_send_to_raw_sock(struct nfc_dev *dev, struct sk_buff *skb,
+                         u8 payload_type, u8 direction);
+
 #endif /* __NET_NFC_H */
index a2441fb1428f3f2e181df63319ca2b3fdc15dc4e..6da46dcf1049789f492cefd9472d0df84d4db91d 100644 (file)
@@ -136,7 +136,7 @@ tcf_exts_exec(struct sk_buff *skb, struct tcf_exts *exts,
 
 int tcf_exts_validate(struct net *net, struct tcf_proto *tp,
                      struct nlattr **tb, struct nlattr *rate_tlv,
-                     struct tcf_exts *exts);
+                     struct tcf_exts *exts, bool ovr);
 void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts);
 void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
                     struct tcf_exts *src);
index 891d80d2c4d2a98cfdad75deee1c28bdce9c9f9f..ec030cd7661693d3812035d350e242f9b271edc4 100644 (file)
@@ -96,7 +96,7 @@ struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r,
                                        struct nlattr *tab);
 void qdisc_put_rtab(struct qdisc_rate_table *tab);
 void qdisc_put_stab(struct qdisc_size_table *tab);
-void qdisc_warn_nonwc(char *txt, struct Qdisc *qdisc);
+void qdisc_warn_nonwc(const char *txt, struct Qdisc *qdisc);
 int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,
                    struct net_device *dev, struct netdev_queue *txq,
                    spinlock_t *root_lock);
index a7e986b081474a51da5011fc19b300d358b0b871..d6fcc1fcdb5b0928a0bd89279e11819a9cc3169d 100644 (file)
@@ -86,7 +86,6 @@ struct inet_protosw {
        struct proto     *prot;
        const struct proto_ops *ops;
   
-       char             no_check;   /* checksum on rcv/xmit/none? */
        unsigned char    flags;      /* See INET_PROTOSW_* below.  */
 };
 #define INET_PROTOSW_REUSE 0x01             /* Are ports automatically reusable? */
index 75fc1f5a948d685fcfff12e04cc6b85e194cd541..259992444e80ae0b88eaec4ff345d23fd8f81c75 100644 (file)
@@ -131,6 +131,11 @@ struct regulatory_request {
  *     all country IE information processed by the regulatory core. This will
  *     override %REGULATORY_COUNTRY_IE_FOLLOW_POWER as all country IEs will
  *     be ignored.
+ * @REGULATORY_ENABLE_RELAX_NO_IR: for devices that wish to allow the
+ *      NO_IR relaxation, which enables transmissions on channels on which
+ *      otherwise initiating radiation is not allowed. This will enable the
+ *      relaxations enabled under the CFG80211_REG_RELAX_NO_IR configuration
+ *      option
  */
 enum ieee80211_regulatory_flags {
        REGULATORY_CUSTOM_REG                   = BIT(0),
@@ -138,6 +143,7 @@ enum ieee80211_regulatory_flags {
        REGULATORY_DISABLE_BEACON_HINTS         = BIT(2),
        REGULATORY_COUNTRY_IE_FOLLOW_POWER      = BIT(3),
        REGULATORY_COUNTRY_IE_IGNORE            = BIT(4),
+       REGULATORY_ENABLE_RELAX_NO_IR           = BIT(5),
 };
 
 struct ieee80211_freq_range {
index d062f81c692f1ee3e61ba1a06bd27e3a9edb761a..624f9857c83e3d7f2987ef95ecc410ad6f8c744f 100644 (file)
@@ -199,7 +199,7 @@ struct tcf_proto_ops {
        int                     (*change)(struct net *net, struct sk_buff *,
                                        struct tcf_proto*, unsigned long,
                                        u32 handle, struct nlattr **,
-                                       unsigned long *);
+                                       unsigned long *, bool);
        int                     (*delete)(struct tcf_proto*, unsigned long);
        void                    (*walk)(struct tcf_proto*, struct tcf_walker *arg);
 
index 0dfcc92600e86cd3263a7378b613d8a3e81ff767..f38588bf3462d9e2374258bb6c383e38413507ed 100644 (file)
@@ -838,10 +838,10 @@ struct sctp_transport {
        unsigned long sackdelay;
        __u32 sackfreq;
 
-       /* When was the last time (in jiffies) that we heard from this
-        * transport?  We use this to pick new active and retran paths.
+       /* When was the last time that we heard from this transport? We use
+        * this to pick new active and retran paths.
         */
-       unsigned long last_time_heard;
+       ktime_t last_time_heard;
 
        /* Last time(in jiffies) when cwnd is reduced due to the congestion
         * indication based on ECNE chunk.
index f257486f17be4bed528826544359c569e6a4b2dd..3f36d45b714a4ba295fe253dbca56e54e0dd0b32 100644 (file)
@@ -3,8 +3,6 @@
 
 #include <linux/types.h>
 
-__u32 secure_ip_id(__be32 daddr);
-__u32 secure_ipv6_id(const __be32 daddr[4]);
 u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport);
 u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
                               __be16 dport);
index 71596261fa997ec7014b77f0bbee9b47b6146493..f1f27fdbb0d5738d6f3f3fbb93a79d240a1129b5 100644 (file)
@@ -116,51 +116,49 @@ struct linux_xfrm_mib {
        unsigned long   mibs[LINUX_MIB_XFRMMAX];
 };
 
-#define SNMP_ARRAY_SZ 1
-
 #define DEFINE_SNMP_STAT(type, name)   \
-       __typeof__(type) __percpu *name[SNMP_ARRAY_SZ]
+       __typeof__(type) __percpu *name
 #define DEFINE_SNMP_STAT_ATOMIC(type, name)    \
        __typeof__(type) *name
 #define DECLARE_SNMP_STAT(type, name)  \
-       extern __typeof__(type) __percpu *name[SNMP_ARRAY_SZ]
+       extern __typeof__(type) __percpu *name
 
 #define SNMP_INC_STATS_BH(mib, field)  \
-                       __this_cpu_inc(mib[0]->mibs[field])
+                       __this_cpu_inc(mib->mibs[field])
 
 #define SNMP_INC_STATS_USER(mib, field)        \
-                       this_cpu_inc(mib[0]->mibs[field])
+                       this_cpu_inc(mib->mibs[field])
 
 #define SNMP_INC_STATS_ATOMIC_LONG(mib, field) \
                        atomic_long_inc(&mib->mibs[field])
 
 #define SNMP_INC_STATS(mib, field)     \
-                       this_cpu_inc(mib[0]->mibs[field])
+                       this_cpu_inc(mib->mibs[field])
 
 #define SNMP_DEC_STATS(mib, field)     \
-                       this_cpu_dec(mib[0]->mibs[field])
+                       this_cpu_dec(mib->mibs[field])
 
 #define SNMP_ADD_STATS_BH(mib, field, addend)  \
-                       __this_cpu_add(mib[0]->mibs[field], addend)
+                       __this_cpu_add(mib->mibs[field], addend)
 
 #define SNMP_ADD_STATS_USER(mib, field, addend)        \
-                       this_cpu_add(mib[0]->mibs[field], addend)
+                       this_cpu_add(mib->mibs[field], addend)
 
 #define SNMP_ADD_STATS(mib, field, addend)     \
-                       this_cpu_add(mib[0]->mibs[field], addend)
+                       this_cpu_add(mib->mibs[field], addend)
 /*
- * Use "__typeof__(*mib[0]) *ptr" instead of "__typeof__(mib[0]) ptr"
+ * Use "__typeof__(*mib) *ptr" instead of "__typeof__(mib) ptr"
  * to make @ptr a non-percpu pointer.
  */
 #define SNMP_UPD_PO_STATS(mib, basefield, addend)      \
        do { \
-               __typeof__(*mib[0]->mibs) *ptr = mib[0]->mibs;  \
+               __typeof__(*mib->mibs) *ptr = mib->mibs;        \
                this_cpu_inc(ptr[basefield##PKTS]);             \
                this_cpu_add(ptr[basefield##OCTETS], addend);   \
        } while (0)
 #define SNMP_UPD_PO_STATS_BH(mib, basefield, addend)   \
        do { \
-               __typeof__(*mib[0]->mibs) *ptr = mib[0]->mibs;  \
+               __typeof__(*mib->mibs) *ptr = mib->mibs;        \
                __this_cpu_inc(ptr[basefield##PKTS]);           \
                __this_cpu_add(ptr[basefield##OCTETS], addend); \
        } while (0)
@@ -170,7 +168,7 @@ struct linux_xfrm_mib {
 
 #define SNMP_ADD_STATS64_BH(mib, field, addend)                        \
        do {                                                            \
-               __typeof__(*mib[0]) *ptr = __this_cpu_ptr((mib)[0]);    \
+               __typeof__(*mib) *ptr = __this_cpu_ptr(mib);            \
                u64_stats_update_begin(&ptr->syncp);                    \
                ptr->mibs[field] += addend;                             \
                u64_stats_update_end(&ptr->syncp);                      \
@@ -191,8 +189,8 @@ struct linux_xfrm_mib {
 #define SNMP_INC_STATS64(mib, field) SNMP_ADD_STATS64(mib, field, 1)
 #define SNMP_UPD_PO_STATS64_BH(mib, basefield, addend)                 \
        do {                                                            \
-               __typeof__(*mib[0]) *ptr;                               \
-               ptr = __this_cpu_ptr((mib)[0]);                         \
+               __typeof__(*mib) *ptr;                                  \
+               ptr = __this_cpu_ptr(mib);                              \
                u64_stats_update_begin(&ptr->syncp);                    \
                ptr->mibs[basefield##PKTS]++;                           \
                ptr->mibs[basefield##OCTETS] += addend;                 \
index 21569cf456ed54459a537e5a6cf02349a2a8413c..07b7fcd60d808a33f9e6fff208c07fe412da8c7e 100644 (file)
@@ -243,7 +243,8 @@ struct cg_proto;
   *    @sk_sndbuf: size of send buffer in bytes
   *    @sk_flags: %SO_LINGER (l_onoff), %SO_BROADCAST, %SO_KEEPALIVE,
   *               %SO_OOBINLINE settings, %SO_TIMESTAMPING settings
-  *    @sk_no_check: %SO_NO_CHECK setting, whether or not checkup packets
+  *    @sk_no_check_tx: %SO_NO_CHECK setting, set checksum in TX packets
+  *    @sk_no_check_rx: allow zero checksum in RX packets
   *    @sk_route_caps: route capabilities (e.g. %NETIF_F_TSO)
   *    @sk_route_nocaps: forbidden route capabilities (e.g NETIF_F_GSO_MASK)
   *    @sk_gso_type: GSO type (e.g. %SKB_GSO_TCPV4)
@@ -371,7 +372,8 @@ struct sock {
        struct sk_buff_head     sk_write_queue;
        kmemcheck_bitfield_begin(flags);
        unsigned int            sk_shutdown  : 2,
-                               sk_no_check  : 2,
+                               sk_no_check_tx : 1,
+                               sk_no_check_rx : 1,
                                sk_userlocks : 4,
                                sk_protocol  : 8,
                                sk_type      : 16;
index 87d87740818867b8b50074c8a5634658be46199c..7286db80e8b8b6532cb4148a283f2f6d8eb3b530 100644 (file)
@@ -220,8 +220,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo);
 #define        TFO_SERVER_ENABLE       2
 #define        TFO_CLIENT_NO_COOKIE    4       /* Data in SYN w/o cookie option */
 
-/* Process SYN data but skip cookie validation */
-#define        TFO_SERVER_COOKIE_NOT_CHKED     0x100
 /* Accept SYN data w/o any cookie option */
 #define        TFO_SERVER_COOKIE_NOT_REQD      0x200
 
@@ -230,10 +228,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo);
  */
 #define        TFO_SERVER_WO_SOCKOPT1  0x400
 #define        TFO_SERVER_WO_SOCKOPT2  0x800
-/* Always create TFO child sockets on a TFO listener even when
- * cookie/data not present. (For testing purpose!)
- */
-#define        TFO_SERVER_ALWAYS       0x1000
 
 extern struct inet_timewait_death_row tcp_death_row;
 
@@ -541,7 +535,7 @@ void tcp_retransmit_timer(struct sock *sk);
 void tcp_xmit_retransmit_queue(struct sock *);
 void tcp_simple_retransmit(struct sock *);
 int tcp_trim_head(struct sock *, struct sk_buff *, u32);
-int tcp_fragment(struct sock *, struct sk_buff *, u32, unsigned int);
+int tcp_fragment(struct sock *, struct sk_buff *, u32, unsigned int, gfp_t);
 
 void tcp_send_probe0(struct sock *);
 void tcp_send_partial(struct sock *);
@@ -558,7 +552,6 @@ void tcp_send_loss_probe(struct sock *sk);
 bool tcp_schedule_loss_probe(struct sock *sk);
 
 /* tcp_input.c */
-void tcp_cwnd_application_limited(struct sock *sk);
 void tcp_resume_early_retransmit(struct sock *sk);
 void tcp_rearm_rto(struct sock *sk);
 void tcp_reset(struct sock *sk);
@@ -797,7 +790,7 @@ struct tcp_congestion_ops {
        /* return slow start threshold (required) */
        u32 (*ssthresh)(struct sock *sk);
        /* do new cwnd calculation (required) */
-       void (*cong_avoid)(struct sock *sk, u32 ack, u32 acked, u32 in_flight);
+       void (*cong_avoid)(struct sock *sk, u32 ack, u32 acked);
        /* call before changing ca_state (optional) */
        void (*set_state)(struct sock *sk, u8 new_state);
        /* call when cwnd event occurs (optional) */
@@ -829,7 +822,7 @@ void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w);
 
 extern struct tcp_congestion_ops tcp_init_congestion_ops;
 u32 tcp_reno_ssthresh(struct sock *sk);
-void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight);
+void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked);
 extern struct tcp_congestion_ops tcp_reno;
 
 static inline void tcp_set_ca_state(struct sock *sk, const u8 ca_state)
@@ -975,7 +968,30 @@ static inline u32 tcp_wnd_end(const struct tcp_sock *tp)
 {
        return tp->snd_una + tp->snd_wnd;
 }
-bool tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight);
+
+/* We follow the spirit of RFC2861 to validate cwnd but implement a more
+ * flexible approach. The RFC suggests cwnd should not be raised unless
+ * it was fully used previously. And that's exactly what we do in
+ * congestion avoidance mode. But in slow start we allow cwnd to grow
+ * as long as the application has used half the cwnd.
+ * Example :
+ *    cwnd is 10 (IW10), but application sends 9 frames.
+ *    We allow cwnd to reach 18 when all frames are ACKed.
+ * This check is safe because it's as aggressive as slow start which already
+ * risks 100% overshoot. The advantage is that we discourage application to
+ * either send more filler packets or data to artificially blow up the cwnd
+ * usage, and allow application-limited process to probe bw more aggressively.
+ */
+static inline bool tcp_is_cwnd_limited(const struct sock *sk)
+{
+       const struct tcp_sock *tp = tcp_sk(sk);
+
+       /* If in slow start, ensure cwnd grows to twice what was ACKed. */
+       if (tp->snd_cwnd <= tp->snd_ssthresh)
+               return tp->snd_cwnd < 2 * tp->max_packets_out;
+
+       return tp->is_cwnd_limited;
+}
 
 static inline void tcp_check_probe_timer(struct sock *sk)
 {
@@ -1103,6 +1119,9 @@ static inline void tcp_openreq_init(struct request_sock *req,
        ireq->ir_num = ntohs(tcp_hdr(skb)->dest);
 }
 
+extern void tcp_openreq_init_rwin(struct request_sock *req,
+                                 struct sock *sk, struct dst_entry *dst);
+
 void tcp_enter_memory_pressure(struct sock *sk);
 
 static inline int keepalive_intvl_when(const struct tcp_sock *tp)
@@ -1312,8 +1331,10 @@ void tcp_free_fastopen_req(struct tcp_sock *tp);
 
 extern struct tcp_fastopen_context __rcu *tcp_fastopen_ctx;
 int tcp_fastopen_reset_cipher(void *key, unsigned int len);
-void tcp_fastopen_cookie_gen(__be32 src, __be32 dst,
-                            struct tcp_fastopen_cookie *foc);
+bool tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
+                     struct request_sock *req,
+                     struct tcp_fastopen_cookie *foc,
+                     struct dst_entry *dst);
 void tcp_fastopen_init_key_once(bool publish);
 #define TCP_FASTOPEN_KEY_LENGTH 16
 
diff --git a/include/net/tso.h b/include/net/tso.h
new file mode 100644 (file)
index 0000000..47e5444
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef _TSO_H
+#define _TSO_H
+
+#include <net/ip.h>
+
+struct tso_t {
+       int next_frag_idx;
+       void *data;
+       size_t size;
+       u16 ip_id;
+       u32 tcp_seq;
+};
+
+int tso_count_descs(struct sk_buff *skb);
+void tso_build_hdr(struct sk_buff *skb, char *hdr, struct tso_t *tso,
+                  int size, bool is_last);
+void tso_build_data(struct sk_buff *skb, struct tso_t *tso, int size);
+void tso_start(struct sk_buff *skb, struct tso_t *tso);
+
+#endif /* _TSO_H */
index a24f0f3e107f67c71b256cd4fb85d04b2389fb7a..68a1fefe3dfe46c3fc8f4847fa074fdb237573bb 100644 (file)
@@ -95,15 +95,6 @@ static inline struct udp_hslot *udp_hashslot2(struct udp_table *table,
        return &table->hash2[hash & table->mask];
 }
 
-/* Note: this must match 'valbool' in sock_setsockopt */
-#define UDP_CSUM_NOXMIT                1
-
-/* Used by SunRPC/xprt layer. */
-#define UDP_CSUM_NORCV         2
-
-/* Default, as per the RFC, is to always do csums. */
-#define UDP_CSUM_DEFAULT       0
-
 extern struct proto udp_prot;
 
 extern atomic_long_t udp_memory_allocated;
@@ -120,7 +111,9 @@ struct sk_buff;
  */
 static inline __sum16 __udp_lib_checksum_complete(struct sk_buff *skb)
 {
-       return __skb_checksum_complete_head(skb, UDP_SKB_CB(skb)->cscov);
+       return (UDP_SKB_CB(skb)->cscov == skb->len ?
+               __skb_checksum_complete(skb) :
+               __skb_checksum_complete_head(skb, UDP_SKB_CB(skb)->cscov));
 }
 
 static inline int udp_lib_checksum_complete(struct sk_buff *skb)
@@ -156,6 +149,15 @@ static inline __wsum udp_csum(struct sk_buff *skb)
        return csum;
 }
 
+static inline __sum16 udp_v4_check(int len, __be32 saddr,
+                                  __be32 daddr, __wsum base)
+{
+       return csum_tcpudp_magic(saddr, daddr, len, IPPROTO_UDP, base);
+}
+
+void udp_set_csum(bool nocheck, struct sk_buff *skb,
+                 __be32 saddr, __be32 daddr, int len);
+
 /* hash routines shared between UDPv4/6 and UDP-Litev4/6 */
 static inline void udp_lib_hash(struct sock *sk)
 {
index 5deef1ae78c964608d629d29d5628dfef52fdf6e..12196ce661d9e288a3d3928ccd66cefff10920a9 100644 (file)
@@ -24,16 +24,26 @@ struct vxlan_sock {
        struct udp_offload udp_offloads;
 };
 
+#define VXLAN_F_LEARN                  0x01
+#define VXLAN_F_PROXY                  0x02
+#define VXLAN_F_RSC                    0x04
+#define VXLAN_F_L2MISS                 0x08
+#define VXLAN_F_L3MISS                 0x10
+#define VXLAN_F_IPV6                   0x20
+#define VXLAN_F_UDP_CSUM               0x40
+#define VXLAN_F_UDP_ZERO_CSUM6_TX      0x80
+#define VXLAN_F_UDP_ZERO_CSUM6_RX      0x100
+
 struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
                                  vxlan_rcv_t *rcv, void *data,
-                                 bool no_share, bool ipv6);
+                                 bool no_share, u32 flags);
 
 void vxlan_sock_release(struct vxlan_sock *vs);
 
 int vxlan_xmit_skb(struct vxlan_sock *vs,
                   struct rtable *rt, struct sk_buff *skb,
                   __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df,
-                  __be16 src_port, __be16 dst_port, __be32 vni);
+                  __be16 src_port, __be16 dst_port, __be32 vni, bool xnet);
 
 __be16 vxlan_src_port(__u16 port_min, __u16 port_max, struct sk_buff *skb);
 
index 116e9c7e19cbbe00272bbf4adc6de7681b0c27ee..721e9c3b11bddb208927852d223a36f548a3cade 100644 (file)
@@ -691,13 +691,6 @@ struct xfrm_spi_skb_cb {
 
 #define XFRM_SPI_SKB_CB(__skb) ((struct xfrm_spi_skb_cb *)&((__skb)->cb[0]))
 
-/* Audit Information */
-struct xfrm_audit {
-       u32     secid;
-       kuid_t  loginuid;
-       unsigned int sessionid;
-};
-
 #ifdef CONFIG_AUDITSYSCALL
 static inline struct audit_buffer *xfrm_audit_start(const char *op)
 {
@@ -713,30 +706,24 @@ static inline struct audit_buffer *xfrm_audit_start(const char *op)
        return audit_buf;
 }
 
-static inline void xfrm_audit_helper_usrinfo(kuid_t auid, unsigned int ses, u32 secid,
+static inline void xfrm_audit_helper_usrinfo(bool task_valid,
                                             struct audit_buffer *audit_buf)
 {
-       char *secctx;
-       u32 secctx_len;
-
-       audit_log_format(audit_buf, " auid=%u ses=%u",
-                        from_kuid(&init_user_ns, auid), ses);
-       if (secid != 0 &&
-           security_secid_to_secctx(secid, &secctx, &secctx_len) == 0) {
-               audit_log_format(audit_buf, " subj=%s", secctx);
-               security_release_secctx(secctx, secctx_len);
-       } else
-               audit_log_task_context(audit_buf);
-}
-
-void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, kuid_t auid,
-                          unsigned int ses, u32 secid);
-void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, kuid_t auid,
-                             unsigned int ses, u32 secid);
-void xfrm_audit_state_add(struct xfrm_state *x, int result, kuid_t auid,
-                         unsigned int ses, u32 secid);
-void xfrm_audit_state_delete(struct xfrm_state *x, int result, kuid_t auid,
-                            unsigned int ses, u32 secid);
+       const unsigned int auid = from_kuid(&init_user_ns, task_valid ?
+                                           audit_get_loginuid(current) :
+                                           INVALID_UID);
+       const unsigned int ses = task_valid ? audit_get_sessionid(current) :
+               (unsigned int) -1;
+
+       audit_log_format(audit_buf, " auid=%u ses=%u", auid, ses);
+       audit_log_task_context(audit_buf);
+}
+
+void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, bool task_valid);
+void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result,
+                             bool task_valid);
+void xfrm_audit_state_add(struct xfrm_state *x, int result, bool task_valid);
+void xfrm_audit_state_delete(struct xfrm_state *x, int result, bool task_valid);
 void xfrm_audit_state_replay_overflow(struct xfrm_state *x,
                                      struct sk_buff *skb);
 void xfrm_audit_state_replay(struct xfrm_state *x, struct sk_buff *skb,
@@ -749,22 +736,22 @@ void xfrm_audit_state_icvfail(struct xfrm_state *x, struct sk_buff *skb,
 #else
 
 static inline void xfrm_audit_policy_add(struct xfrm_policy *xp, int result,
-                                 kuid_t auid, unsigned int ses, u32 secid)
+                                        bool task_valid)
 {
 }
 
 static inline void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result,
-                                 kuid_t auid, unsigned int ses, u32 secid)
+                                           bool task_valid)
 {
 }
 
 static inline void xfrm_audit_state_add(struct xfrm_state *x, int result,
-                                kuid_t auid, unsigned int ses, u32 secid)
+                                       bool task_valid)
 {
 }
 
 static inline void xfrm_audit_state_delete(struct xfrm_state *x, int result,
-                                   kuid_t auid, unsigned int ses, u32 secid)
+                                          bool task_valid)
 {
 }
 
@@ -1508,7 +1495,7 @@ struct xfrmk_spdinfo {
 
 struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq);
 int xfrm_state_delete(struct xfrm_state *x);
-int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info);
+int xfrm_state_flush(struct net *net, u8 proto, bool task_valid);
 void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si);
 void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si);
 u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq);
@@ -1603,7 +1590,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark,
                                          int *err);
 struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8, int dir,
                                     u32 id, int delete, int *err);
-int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info);
+int xfrm_policy_flush(struct net *net, u8 type, bool task_valid);
 u32 xfrm_get_acqseq(void);
 int verify_spi_info(u8 proto, u32 min, u32 max);
 int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi);
index e016e2ac38df8f6c570980a4624e9ccaefd0dd5e..42ed789ebafcf9ab04c759d7ef167e981aab2bc6 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/types.h>
 #include <linux/timer.h>
 #include <linux/scatterlist.h>
+#include <scsi/scsi_device.h>
 
 struct Scsi_Host;
 struct scsi_device;
@@ -315,4 +316,20 @@ static inline void set_driver_byte(struct scsi_cmnd *cmd, char status)
        cmd->result = (cmd->result & 0x00ffffff) | (status << 24);
 }
 
+static inline unsigned scsi_transfer_length(struct scsi_cmnd *scmd)
+{
+       unsigned int xfer_len = blk_rq_bytes(scmd->request);
+       unsigned int prot_op = scsi_get_prot_op(scmd);
+       unsigned int sector_size = scmd->device->sector_size;
+
+       switch (prot_op) {
+       case SCSI_PROT_NORMAL:
+       case SCSI_PROT_WRITE_STRIP:
+       case SCSI_PROT_READ_INSERT:
+               return xfer_len;
+       }
+
+       return xfer_len + (xfer_len >> ilog2(sector_size)) * 8;
+}
+
 #endif /* _SCSI_SCSI_CMND_H */
index b4d6697085fef71c2d31160c58ed70ff0f350686..d854fb31c000756e46bc9c677e6a9979232b8453 100644 (file)
@@ -932,7 +932,7 @@ static inline void snd_pcm_gettime(struct snd_pcm_runtime *runtime,
                                   struct timespec *tv)
 {
        if (runtime->tstamp_type == SNDRV_PCM_TSTAMP_TYPE_MONOTONIC)
-               do_posix_clock_monotonic_gettime(tv);
+               ktime_get_ts(tv);
        else
                getnstimeofday(tv);
 }
index 33b487b5da92dc76f549df5d09a3852518c14159..daef9daa500c11f0ff7d92151fafbf9080a80e02 100644 (file)
@@ -70,7 +70,8 @@ extern void iscsit_build_nopin_rsp(struct iscsi_cmd *, struct iscsi_conn *,
 extern void iscsit_build_task_mgt_rsp(struct iscsi_cmd *, struct iscsi_conn *,
                                struct iscsi_tm_rsp *);
 extern int iscsit_build_text_rsp(struct iscsi_cmd *, struct iscsi_conn *,
-                               struct iscsi_text_rsp *);
+                               struct iscsi_text_rsp *,
+                               enum iscsit_transport_type);
 extern void iscsit_build_reject(struct iscsi_cmd *, struct iscsi_conn *,
                                struct iscsi_reject *);
 extern int iscsit_build_logout_rsp(struct iscsi_cmd *, struct iscsi_conn *,
index 3a1c1eea1fffcaee767d4bef1a8ea2700f1884e2..9adc1bca1178ba36482f63bf8cef1444045f7709 100644 (file)
@@ -59,6 +59,7 @@ int   transport_subsystem_register(struct se_subsystem_api *);
 void   transport_subsystem_release(struct se_subsystem_api *);
 
 void   target_complete_cmd(struct se_cmd *, u8);
+void   target_complete_cmd_with_length(struct se_cmd *, u8, int);
 
 sense_reason_t spc_parse_cdb(struct se_cmd *cmd, unsigned int *size);
 sense_reason_t spc_emulate_report_luns(struct se_cmd *cmd);
index 9a7e08d6125814406a7e2f5198e8abdaa8395877..d19840b0cac844c8cd2fca6f4dac5d4bc62c75a3 100644 (file)
@@ -7,6 +7,9 @@
 #include <linux/ktime.h>
 #include <linux/pm_qos.h>
 #include <linux/tracepoint.h>
+#include <linux/ftrace_event.h>
+
+#define TPS(x)  tracepoint_string(x)
 
 DECLARE_EVENT_CLASS(cpu,
 
@@ -90,6 +93,17 @@ TRACE_EVENT(pstate_sample,
 #define PWR_EVENT_EXIT -1
 #endif
 
+#define pm_verb_symbolic(event) \
+       __print_symbolic(event, \
+               { PM_EVENT_SUSPEND, "suspend" }, \
+               { PM_EVENT_RESUME, "resume" }, \
+               { PM_EVENT_FREEZE, "freeze" }, \
+               { PM_EVENT_QUIESCE, "quiesce" }, \
+               { PM_EVENT_HIBERNATE, "hibernate" }, \
+               { PM_EVENT_THAW, "thaw" }, \
+               { PM_EVENT_RESTORE, "restore" }, \
+               { PM_EVENT_RECOVER, "recover" })
+
 DEFINE_EVENT(cpu, cpu_frequency,
 
        TP_PROTO(unsigned int frequency, unsigned int cpu_id),
@@ -97,58 +111,76 @@ DEFINE_EVENT(cpu, cpu_frequency,
        TP_ARGS(frequency, cpu_id)
 );
 
-TRACE_EVENT(machine_suspend,
+TRACE_EVENT(device_pm_callback_start,
 
-       TP_PROTO(unsigned int state),
+       TP_PROTO(struct device *dev, const char *pm_ops, int event),
 
-       TP_ARGS(state),
+       TP_ARGS(dev, pm_ops, event),
 
        TP_STRUCT__entry(
-               __field(        u32,            state           )
+               __string(device, dev_name(dev))
+               __string(driver, dev_driver_string(dev))
+               __string(parent, dev->parent ? dev_name(dev->parent) : "none")
+               __string(pm_ops, pm_ops ? pm_ops : "none ")
+               __field(int, event)
        ),
 
        TP_fast_assign(
-               __entry->state = state;
+               __assign_str(device, dev_name(dev));
+               __assign_str(driver, dev_driver_string(dev));
+               __assign_str(parent,
+                       dev->parent ? dev_name(dev->parent) : "none");
+               __assign_str(pm_ops, pm_ops ? pm_ops : "none ");
+               __entry->event = event;
        ),
 
-       TP_printk("state=%lu", (unsigned long)__entry->state)
+       TP_printk("%s %s, parent: %s, %s[%s]", __get_str(driver),
+               __get_str(device), __get_str(parent), __get_str(pm_ops),
+               pm_verb_symbolic(__entry->event))
 );
 
-TRACE_EVENT(device_pm_report_time,
+TRACE_EVENT(device_pm_callback_end,
 
-       TP_PROTO(struct device *dev, const char *pm_ops, s64 ops_time,
-                char *pm_event_str, int error),
+       TP_PROTO(struct device *dev, int error),
 
-       TP_ARGS(dev, pm_ops, ops_time, pm_event_str, error),
+       TP_ARGS(dev, error),
 
        TP_STRUCT__entry(
                __string(device, dev_name(dev))
                __string(driver, dev_driver_string(dev))
-               __string(parent, dev->parent ? dev_name(dev->parent) : "none")
-               __string(pm_ops, pm_ops ? pm_ops : "none ")
-               __string(pm_event_str, pm_event_str)
-               __field(s64, ops_time)
                __field(int, error)
        ),
 
        TP_fast_assign(
-               const char *tmp = dev->parent ? dev_name(dev->parent) : "none";
-               const char *tmp_i = pm_ops ? pm_ops : "none ";
-
                __assign_str(device, dev_name(dev));
                __assign_str(driver, dev_driver_string(dev));
-               __assign_str(parent, tmp);
-               __assign_str(pm_ops, tmp_i);
-               __assign_str(pm_event_str, pm_event_str);
-               __entry->ops_time = ops_time;
                __entry->error = error;
        ),
 
-       /* ops_str has an extra space at the end */
-       TP_printk("%s %s parent=%s state=%s ops=%snsecs=%lld err=%d",
-               __get_str(driver), __get_str(device), __get_str(parent),
-               __get_str(pm_event_str), __get_str(pm_ops),
-               __entry->ops_time, __entry->error)
+       TP_printk("%s %s, err=%d",
+               __get_str(driver), __get_str(device), __entry->error)
+);
+
+TRACE_EVENT(suspend_resume,
+
+       TP_PROTO(const char *action, int val, bool start),
+
+       TP_ARGS(action, val, start),
+
+       TP_STRUCT__entry(
+               __field(const char *, action)
+               __field(int, val)
+               __field(bool, start)
+       ),
+
+       TP_fast_assign(
+               __entry->action = action;
+               __entry->val = val;
+               __entry->start = start;
+       ),
+
+       TP_printk("%s[%u] %s", __entry->action, (unsigned int)__entry->val,
+               (__entry->start)?"begin":"end")
 );
 
 DECLARE_EVENT_CLASS(wakeup_source,
index 67e1bbf836954dbc486d1d7debb3a2bfa3cfd3d1..0a68d5ae584e9dfada372fdae49c5d0934176402 100644 (file)
@@ -530,6 +530,26 @@ TRACE_EVENT(sched_swap_numa,
                        __entry->dst_pid, __entry->dst_tgid, __entry->dst_ngid,
                        __entry->dst_cpu, __entry->dst_nid)
 );
+
+/*
+ * Tracepoint for waking a polling cpu without an IPI.
+ */
+TRACE_EVENT(sched_wake_idle_without_ipi,
+
+       TP_PROTO(int cpu),
+
+       TP_ARGS(cpu),
+
+       TP_STRUCT__entry(
+               __field(        int,    cpu     )
+       ),
+
+       TP_fast_assign(
+               __entry->cpu    = cpu;
+       ),
+
+       TP_printk("cpu=%d", __entry->cpu)
+);
 #endif /* _TRACE_SCHED_H */
 
 /* This part must be outside protection */
index f104c2603ebe44397d62a45c50e39e18c5ae2b2e..def54f9e07ca26c48afb3af2c3cd63d593be4c02 100644 (file)
@@ -181,6 +181,7 @@ struct drm_mode_get_plane_res {
 #define DRM_MODE_ENCODER_TVDAC 4
 #define DRM_MODE_ENCODER_VIRTUAL 5
 #define DRM_MODE_ENCODER_DSI   6
+#define DRM_MODE_ENCODER_DPMST 7
 
 struct drm_mode_get_encoder {
        __u32 encoder_id;
@@ -251,6 +252,21 @@ struct drm_mode_get_connector {
 #define DRM_MODE_PROP_BLOB     (1<<4)
 #define DRM_MODE_PROP_BITMASK  (1<<5) /* bitmask of enumerated types */
 
+/* non-extended types: legacy bitmask, one bit per type: */
+#define DRM_MODE_PROP_LEGACY_TYPE  ( \
+               DRM_MODE_PROP_RANGE | \
+               DRM_MODE_PROP_ENUM | \
+               DRM_MODE_PROP_BLOB | \
+               DRM_MODE_PROP_BITMASK)
+
+/* extended-types: rather than continue to consume a bit per type,
+ * grab a chunk of the bits to use as integer type id.
+ */
+#define DRM_MODE_PROP_EXTENDED_TYPE    0x0000ffc0
+#define DRM_MODE_PROP_TYPE(n)          ((n) << 6)
+#define DRM_MODE_PROP_OBJECT           DRM_MODE_PROP_TYPE(1)
+#define DRM_MODE_PROP_SIGNED_RANGE     DRM_MODE_PROP_TYPE(2)
+
 struct drm_mode_property_enum {
        __u64 value;
        char name[DRM_PROP_NAME_LEN];
index 126bfaa8bb6be45caf723889c077673da8c992f4..ff57f07c32498933be7be28d874fb09a164f5b04 100644 (file)
@@ -223,6 +223,7 @@ typedef struct _drm_i915_sarea {
 #define DRM_I915_GEM_GET_CACHING       0x30
 #define DRM_I915_REG_READ              0x31
 #define DRM_I915_GET_RESET_STATS       0x32
+#define DRM_I915_GEM_USERPTR           0x33
 
 #define DRM_IOCTL_I915_INIT            DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
 #define DRM_IOCTL_I915_FLUSH           DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH)
@@ -273,6 +274,7 @@ typedef struct _drm_i915_sarea {
 #define DRM_IOCTL_I915_GEM_CONTEXT_DESTROY     DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_DESTROY, struct drm_i915_gem_context_destroy)
 #define DRM_IOCTL_I915_REG_READ                        DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_REG_READ, struct drm_i915_reg_read)
 #define DRM_IOCTL_I915_GET_RESET_STATS         DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GET_RESET_STATS, struct drm_i915_reset_stats)
+#define DRM_IOCTL_I915_GEM_USERPTR                     DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_USERPTR, struct drm_i915_gem_userptr)
 
 /* Allow drivers to submit batchbuffers directly to hardware, relying
  * on the security mechanisms provided by hardware.
@@ -337,6 +339,7 @@ typedef struct drm_i915_irq_wait {
 #define I915_PARAM_HAS_EXEC_NO_RELOC    25
 #define I915_PARAM_HAS_EXEC_HANDLE_LUT   26
 #define I915_PARAM_HAS_WT               27
+#define I915_PARAM_CMD_PARSER_VERSION   28
 
 typedef struct drm_i915_getparam {
        int param;
@@ -1049,4 +1052,18 @@ struct drm_i915_reset_stats {
        __u32 pad;
 };
 
+struct drm_i915_gem_userptr {
+       __u64 user_ptr;
+       __u64 user_size;
+       __u32 flags;
+#define I915_USERPTR_READ_ONLY 0x1
+#define I915_USERPTR_UNSYNCHRONIZED 0x80000000
+       /**
+        * Returned handle for the object.
+        *
+        * Object handles are nonzero.
+        */
+       __u32 handle;
+};
+
 #endif /* _UAPI_I915_DRM_H_ */
index aefa2f6afa3ba3ccba6b86826db289fa9b21a5d4..1cc0b610f162928a5a9564bdc091e22fc5ab9a86 100644 (file)
@@ -1007,7 +1007,7 @@ struct drm_radeon_cs {
 #define RADEON_INFO_NUM_BYTES_MOVED    0x1d
 #define RADEON_INFO_VRAM_USAGE         0x1e
 #define RADEON_INFO_GTT_USAGE          0x1f
-
+#define RADEON_INFO_ACTIVE_CU_COUNT    0x20
 
 struct drm_radeon_info {
        uint32_t                request;
index 4c31a366be1639f08d2487309ff8602e4f7f4704..cf6714752b69ab12b5adc92461bb793aad39a431 100644 (file)
@@ -385,6 +385,14 @@ enum {
  */
 #define AUDIT_MESSAGE_TEXT_MAX 8560
 
+/* Multicast Netlink socket groups (default up to 32) */
+enum audit_nlgrps {
+       AUDIT_NLGRP_NONE,       /* Group 0 not used */
+       AUDIT_NLGRP_READLOG,    /* "best effort" read only socket */
+       __AUDIT_NLGRP_MAX
+};
+#define AUDIT_NLGRP_MAX                (__AUDIT_NLGRP_MAX - 1)
+
 struct audit_status {
        __u32           mask;           /* Bit mask for valid entries */
        __u32           enabled;        /* 1 = enabled, 0 = disabled */
index 7554fd381a566a8f4a70043365799028377d4037..6f9c38ce45c7d89ff7564bfb6a911eb1683ac00b 100644 (file)
@@ -306,6 +306,14 @@ struct btrfs_ioctl_search_args {
        char buf[BTRFS_SEARCH_ARGS_BUFSIZE];
 };
 
+struct btrfs_ioctl_search_args_v2 {
+       struct btrfs_ioctl_search_key key; /* in/out - search parameters */
+       __u64 buf_size;            /* in - size of buffer
+                                           * out - on EOVERFLOW: needed size
+                                           *       to store item */
+       __u64 buf[0];                       /* out - found items */
+};
+
 struct btrfs_ioctl_clone_range_args {
   __s64 src_fd;
   __u64 src_offset, src_length;
@@ -558,6 +566,8 @@ static inline char *btrfs_err_str(enum btrfs_err_code err_code)
                                struct btrfs_ioctl_defrag_range_args)
 #define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \
                                   struct btrfs_ioctl_search_args)
+#define BTRFS_IOC_TREE_SEARCH_V2 _IOWR(BTRFS_IOCTL_MAGIC, 17, \
+                                          struct btrfs_ioctl_search_args_v2)
 #define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \
                                   struct btrfs_ioctl_ino_lookup_args)
 #define BTRFS_IOC_DEFAULT_SUBVOL _IOW(BTRFS_IOCTL_MAGIC, 19, __u64)
index 5d9d1d1407180a9291c0f986945e3a34f2ccf51e..41892f720057df2cc23f96c7d44d6ab2808fdfdd 100644 (file)
@@ -42,8 +42,8 @@
  * DAMAGE.
  */
 
-#ifndef CAN_H
-#define CAN_H
+#ifndef _UAPI_CAN_H
+#define _UAPI_CAN_H
 
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -191,4 +191,4 @@ struct can_filter {
 
 #define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
 
-#endif /* CAN_H */
+#endif /* !_UAPI_CAN_H */
index 382251a1d21403acd817577d83c21f47d0389865..89ddb9dc9bdf7ca8bd191c9dedf7019f24573931 100644 (file)
@@ -41,8 +41,8 @@
  * DAMAGE.
  */
 
-#ifndef CAN_BCM_H
-#define CAN_BCM_H
+#ifndef _UAPI_CAN_BCM_H
+#define _UAPI_CAN_BCM_H
 
 #include <linux/types.h>
 #include <linux/can.h>
@@ -95,4 +95,4 @@ enum {
 #define TX_RESET_MULTI_IDX  0x0200
 #define RX_RTR_FRAME        0x0400
 
-#endif /* CAN_BCM_H */
+#endif /* !_UAPI_CAN_BCM_H */
index b632045453202074ada263866052bc2a806e85bc..c247446ab25a4e564a068ae97e154cf7972f0a1d 100644 (file)
@@ -41,8 +41,8 @@
  * DAMAGE.
  */
 
-#ifndef CAN_ERROR_H
-#define CAN_ERROR_H
+#ifndef _UAPI_CAN_ERROR_H
+#define _UAPI_CAN_ERROR_H
 
 #define CAN_ERR_DLC 8 /* dlc for error message frames */
 
 
 /* controller specific additional information / data[5..7] */
 
-#endif /* CAN_ERROR_H */
+#endif /* _UAPI_CAN_ERROR_H */
index 844c8964bdfee3a3f4a7308bf0fd832e82754a89..3e6184cf2f6dc5b2318f87f49db09ef119182683 100644 (file)
@@ -41,8 +41,8 @@
  * DAMAGE.
  */
 
-#ifndef CAN_GW_H
-#define CAN_GW_H
+#ifndef _UAPI_CAN_GW_H
+#define _UAPI_CAN_GW_H
 
 #include <linux/types.h>
 #include <linux/can.h>
@@ -200,4 +200,4 @@ enum {
  *         Beware of sending unpacked or aligned structs!
  */
 
-#endif
+#endif /* !_UAPI_CAN_GW_H */
index 7e2e1863db16e02fa15e1edc109adefda8236ba7..813d11f549774aadf5f3d87ba28be840e7f6e399 100644 (file)
@@ -15,8 +15,8 @@
  * GNU General Public License for more details.
  */
 
-#ifndef CAN_NETLINK_H
-#define CAN_NETLINK_H
+#ifndef _UAPI_CAN_NETLINK_H
+#define _UAPI_CAN_NETLINK_H
 
 #include <linux/types.h>
 
@@ -130,4 +130,4 @@ enum {
 
 #define IFLA_CAN_MAX   (__IFLA_CAN_MAX - 1)
 
-#endif /* CAN_NETLINK_H */
+#endif /* !_UAPI_CAN_NETLINK_H */
index c7d8c334e0ce26838c7cc611bd3ad1eb5a31a6c4..78ec76fd89a6ce4fe70161576ec0c01e5d6156d3 100644 (file)
@@ -42,8 +42,8 @@
  * DAMAGE.
  */
 
-#ifndef CAN_RAW_H
-#define CAN_RAW_H
+#ifndef _UAPI_CAN_RAW_H
+#define _UAPI_CAN_RAW_H
 
 #include <linux/can.h>
 
@@ -59,4 +59,4 @@ enum {
        CAN_RAW_FD_FRAMES,      /* allow CAN FD frames (default:off) */
 };
 
-#endif
+#endif /* !_UAPI_CAN_RAW_H */
index 154dd6d3c8fedaa54a04817567580b11727f2807..12c37a197d247ca980fef9c6e81ed0c067f27987 100644 (file)
@@ -347,7 +347,12 @@ struct vfs_cap_data {
 
 #define CAP_BLOCK_SUSPEND    36
 
-#define CAP_LAST_CAP         CAP_BLOCK_SUSPEND
+/* Allow reading the audit log via multicast netlink socket */
+
+#define CAP_AUDIT_READ         37
+
+
+#define CAP_LAST_CAP         CAP_AUDIT_READ
 
 #define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP)
 
index fd161e91b6d7e711270da030ed84b38f9b718f0c..e3c7a719c76b4a586c40da9506adaa095857ede4 100644 (file)
@@ -846,6 +846,38 @@ struct ethtool_rxfh_indir {
        __u32   ring_index[0];
 };
 
+/**
+ * struct ethtool_rxfh - command to get/set RX flow hash indir or/and hash key.
+ * @cmd: Specific command number - %ETHTOOL_GRSSH or %ETHTOOL_SRSSH
+ * @rss_context: RSS context identifier.
+ * @indir_size: On entry, the array size of the user buffer for the
+ *     indirection table, which may be zero, or (for %ETHTOOL_SRSSH),
+ *     %ETH_RXFH_INDIR_NO_CHANGE.  On return from %ETHTOOL_GRSSH,
+ *     the array size of the hardware indirection table.
+ * @key_size: On entry, the array size of the user buffer for the hash key,
+ *     which may be zero.  On return from %ETHTOOL_GRSSH, the size of the
+ *     hardware hash key.
+ * @rsvd:      Reserved for future extensions.
+ * @rss_config: RX ring/queue index for each hash value i.e., indirection table
+ *     of @indir_size __u32 elements, followed by hash key of @key_size
+ *     bytes.
+ *
+ * For %ETHTOOL_GRSSH, a @indir_size and key_size of zero means that only the
+ * size should be returned.  For %ETHTOOL_SRSSH, an @indir_size of
+ * %ETH_RXFH_INDIR_NO_CHANGE means that indir table setting is not requested
+ * and a @indir_size of zero means the indir table should be reset to default
+ * values.
+ */
+struct ethtool_rxfh {
+       __u32   cmd;
+       __u32   rss_context;
+       __u32   indir_size;
+       __u32   key_size;
+       __u32   rsvd[2];
+       __u32   rss_config[0];
+};
+#define ETH_RXFH_INDIR_NO_CHANGE       0xffffffff
+
 /**
  * struct ethtool_rx_ntuple_flow_spec - specification for RX flow filter
  * @flow_type: Type of match to perform, e.g. %TCP_V4_FLOW
@@ -1118,6 +1150,9 @@ enum ethtool_sfeatures_retval_bits {
 #define ETHTOOL_GEEE           0x00000044 /* Get EEE settings */
 #define ETHTOOL_SEEE           0x00000045 /* Set EEE settings */
 
+#define ETHTOOL_GRSSH          0x00000046 /* Get RX flow hash configuration */
+#define ETHTOOL_SRSSH          0x00000047 /* Set RX flow hash configuration */
+
 /* compatibility with older code */
 #define SPARC_ETH_GSET         ETHTOOL_GSET
 #define SPARC_ETH_SSET         ETHTOOL_SSET
index 8eb9ccaa5b48124b716e5abe7741aef4ca711d9f..253b4d42cf2bb31517a8a159f5e1c37f13c075f2 100644 (file)
@@ -130,7 +130,8 @@ struct sock_fprog { /* Required for SO_ATTACH_FILTER. */
 #define SKF_AD_VLAN_TAG        44
 #define SKF_AD_VLAN_TAG_PRESENT 48
 #define SKF_AD_PAY_OFFSET      52
-#define SKF_AD_MAX     56
+#define SKF_AD_RANDOM  56
+#define SKF_AD_MAX     60
 #define SKF_NET_OFF   (-0x100000)
 #define SKF_LL_OFF    (-0x200000)
 
index 0d36909c3aefa27f26aaf60d1d356d462ae94d0e..1086cd9f675473b21f2c316eac16d591743af777 100644 (file)
  *  Define max and min legal sizes.  The frame sizes do not include
  *  4 byte FCS/CRC (frame check sequence).
  */
-#define FDDI_K_ALEN                    6               /* Octets in one FDDI address */
-#define FDDI_K_8022_HLEN       16              /* Total octets in 802.2 header */
-#define FDDI_K_SNAP_HLEN       21              /* Total octets in 802.2 SNAP header */
-#define FDDI_K_8022_ZLEN       16              /* Min octets in 802.2 frame sans FCS */
-#define FDDI_K_SNAP_ZLEN       21              /* Min octets in 802.2 SNAP frame sans FCS */
+#define FDDI_K_ALEN            6       /* Octets in one FDDI address */
+#define FDDI_K_8022_HLEN       16      /* Total octets in 802.2 header */
+#define FDDI_K_SNAP_HLEN       21      /* Total octets in 802.2 SNAP header */
+#define FDDI_K_8022_ZLEN       16      /* Min octets in 802.2 frame sans
+                                          FCS */
+#define FDDI_K_SNAP_ZLEN       21      /* Min octets in 802.2 SNAP frame sans
+                                          FCS */
 #define FDDI_K_8022_DLEN       4475    /* Max octets in 802.2 payload */
 #define FDDI_K_SNAP_DLEN       4470    /* Max octets in 802.2 SNAP payload */
-#define FDDI_K_LLC_ZLEN                13              /* Min octets in LLC frame sans FCS */
+#define FDDI_K_LLC_ZLEN                13      /* Min octets in LLC frame sans FCS */
 #define FDDI_K_LLC_LEN         4491    /* Max octets in LLC frame sans FCS */
+#define FDDI_K_OUI_LEN         3       /* Octets in OUI in 802.2 SNAP
+                                          header */
 
 /* Define FDDI Frame Control (FC) Byte values */
-#define FDDI_FC_K_VOID                                 0x00    
-#define FDDI_FC_K_NON_RESTRICTED_TOKEN 0x80    
-#define FDDI_FC_K_RESTRICTED_TOKEN             0xC0    
-#define FDDI_FC_K_SMT_MIN                              0x41
-#define FDDI_FC_K_SMT_MAX                              0x4F
-#define FDDI_FC_K_MAC_MIN                              0xC1
-#define FDDI_FC_K_MAC_MAX                              0xCF    
-#define FDDI_FC_K_ASYNC_LLC_MIN                        0x50
-#define FDDI_FC_K_ASYNC_LLC_DEF                        0x54
-#define FDDI_FC_K_ASYNC_LLC_MAX                        0x5F
-#define FDDI_FC_K_SYNC_LLC_MIN                 0xD0
-#define FDDI_FC_K_SYNC_LLC_MAX                 0xD7
-#define FDDI_FC_K_IMPLEMENTOR_MIN              0x60
-#define FDDI_FC_K_IMPLEMENTOR_MAX              0x6F
-#define FDDI_FC_K_RESERVED_MIN                 0x70
-#define FDDI_FC_K_RESERVED_MAX                 0x7F
+#define FDDI_FC_K_VOID                 0x00
+#define FDDI_FC_K_NON_RESTRICTED_TOKEN 0x80
+#define FDDI_FC_K_RESTRICTED_TOKEN     0xC0
+#define FDDI_FC_K_SMT_MIN              0x41
+#define FDDI_FC_K_SMT_MAX              0x4F
+#define FDDI_FC_K_MAC_MIN              0xC1
+#define FDDI_FC_K_MAC_MAX              0xCF
+#define FDDI_FC_K_ASYNC_LLC_MIN                0x50
+#define FDDI_FC_K_ASYNC_LLC_DEF                0x54
+#define FDDI_FC_K_ASYNC_LLC_MAX                0x5F
+#define FDDI_FC_K_SYNC_LLC_MIN         0xD0
+#define FDDI_FC_K_SYNC_LLC_MAX         0xD7
+#define FDDI_FC_K_IMPLEMENTOR_MIN      0x60
+#define FDDI_FC_K_IMPLEMENTOR_MAX      0x6F
+#define FDDI_FC_K_RESERVED_MIN         0x70
+#define FDDI_FC_K_RESERVED_MAX         0x7F
 
 /* Define LLC and SNAP constants */
-#define FDDI_EXTENDED_SAP      0xAA
+#define FDDI_EXTENDED_SAP              0xAA
 #define FDDI_UI_CMD                    0x03
 
 /* Define 802.2 Type 1 header */
 struct fddi_8022_1_hdr {
-       __u8    dsap;                                   /* destination service access point */
-       __u8    ssap;                                   /* source service access point */
-       __u8    ctrl;                                   /* control byte #1 */
+       __u8    dsap;                   /* destination service access point */
+       __u8    ssap;                   /* source service access point */
+       __u8    ctrl;                   /* control byte #1 */
 } __attribute__((packed));
 
 /* Define 802.2 Type 2 header */
 struct fddi_8022_2_hdr {
-       __u8    dsap;                                   /* destination service access point */
-       __u8    ssap;                                   /* source service access point */
-       __u8    ctrl_1;                                 /* control byte #1 */
-       __u8    ctrl_2;                                 /* control byte #2 */
+       __u8    dsap;                   /* destination service access point */
+       __u8    ssap;                   /* source service access point */
+       __u8    ctrl_1;                 /* control byte #1 */
+       __u8    ctrl_2;                 /* control byte #2 */
 } __attribute__((packed));
 
 /* Define 802.2 SNAP header */
-#define FDDI_K_OUI_LEN 3
 struct fddi_snap_hdr {
-       __u8    dsap;                                   /* always 0xAA */
-       __u8    ssap;                                   /* always 0xAA */
-       __u8    ctrl;                                   /* always 0x03 */
+       __u8    dsap;                   /* always 0xAA */
+       __u8    ssap;                   /* always 0xAA */
+       __u8    ctrl;                   /* always 0x03 */
        __u8    oui[FDDI_K_OUI_LEN];    /* organizational universal id */
-       __be16  ethertype;                              /* packet type ID field */
+       __be16  ethertype;              /* packet type ID field */
 } __attribute__((packed));
 
 /* Define FDDI LLC frame header */
 struct fddihdr {
-       __u8    fc;                                             /* frame control */
-       __u8    daddr[FDDI_K_ALEN];             /* destination address */
-       __u8    saddr[FDDI_K_ALEN];             /* source address */
-       union
-               {
-               struct fddi_8022_1_hdr          llc_8022_1;
-               struct fddi_8022_2_hdr          llc_8022_2;
-               struct fddi_snap_hdr            llc_snap;
-               } hdr;
+       __u8    fc;                     /* frame control */
+       __u8    daddr[FDDI_K_ALEN];     /* destination address */
+       __u8    saddr[FDDI_K_ALEN];     /* source address */
+       union {
+               struct fddi_8022_1_hdr  llc_8022_1;
+               struct fddi_8022_2_hdr  llc_8022_2;
+               struct fddi_snap_hdr    llc_snap;
+       } hdr;
 } __attribute__((packed));
 
 
index 9a7f7ace66494e144c55c08d5bbba085de3a2b7c..b38534895db5608b9ddad5be74f50e989263277c 100644 (file)
@@ -319,6 +319,9 @@ enum {
        IFLA_VXLAN_PORT,        /* destination port */
        IFLA_VXLAN_GROUP6,
        IFLA_VXLAN_LOCAL6,
+       IFLA_VXLAN_UDP_CSUM,
+       IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
+       IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
        __IFLA_VXLAN_MAX
 };
 #define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
@@ -399,9 +402,10 @@ enum {
        IFLA_VF_UNSPEC,
        IFLA_VF_MAC,            /* Hardware queue specific attributes */
        IFLA_VF_VLAN,
-       IFLA_VF_TX_RATE,        /* TX Bandwidth Allocation */
+       IFLA_VF_TX_RATE,        /* Max TX Bandwidth Allocation */
        IFLA_VF_SPOOFCHK,       /* Spoof Checking on/off switch */
        IFLA_VF_LINK_STATE,     /* link state enable/disable/auto switch */
+       IFLA_VF_RATE,           /* Min and Max TX Bandwidth Allocation */
        __IFLA_VF_MAX,
 };
 
@@ -423,6 +427,12 @@ struct ifla_vf_tx_rate {
        __u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */
 };
 
+struct ifla_vf_rate {
+       __u32 vf;
+       __u32 min_tx_rate; /* Min Bandwidth in Mbps */
+       __u32 max_tx_rate; /* Max Bandwidth in Mbps */
+};
+
 struct ifla_vf_spoofchk {
        __u32 vf;
        __u32 setting;
index aee73d0611fb9bbb72f3ab52a0b91c09ba861c12..3bce9e9d9f7c8d82c1e8064904098a73cdd4fb81 100644 (file)
@@ -100,7 +100,7 @@ enum {
 #define IFLA_GRE_MAX   (__IFLA_GRE_MAX - 1)
 
 /* VTI-mode i_flags */
-#define VTI_ISVTI 0x0001
+#define VTI_ISVTI ((__force __be16)0x0001)
 
 enum {
        IFLA_VTI_UNSPEC,
index 8adb681603273c287f25be055c0258c8a2a73c65..21caa2631c209fdc0f0c7e21591e0507fc3e0b9a 100644 (file)
@@ -124,6 +124,8 @@ enum {
        L2TP_ATTR_STATS,                /* nested */
        L2TP_ATTR_IP6_SADDR,            /* struct in6_addr */
        L2TP_ATTR_IP6_DADDR,            /* struct in6_addr */
+       L2TP_ATTR_UDP_ZERO_CSUM6_TX,    /* u8 */
+       L2TP_ATTR_UDP_ZERO_CSUM6_RX,    /* u8 */
        __L2TP_ATTR_MAX,
 };
 
index d3ef583104e0c905503a3911c33ca4018294e7de..4a1d7e96dfe3d7ccf8669428a943ed53af37828e 100644 (file)
@@ -24,6 +24,7 @@ enum {
        NDA_PORT,
        NDA_VNI,
        NDA_IFINDEX,
+       NDA_MASTER,
        __NDA_MAX
 };
 
index c88ccbfda5f1b111a5fa43e1d1803bcccf95b521..2a88f645a5d821c47d7a53a05dc7a0e083a72342 100644 (file)
@@ -211,6 +211,29 @@ enum nft_set_flags {
        NFT_SET_MAP                     = 0x8,
 };
 
+/**
+ * enum nft_set_policies - set selection policy
+ *
+ * @NFT_SET_POL_PERFORMANCE: prefer high performance over low memory use
+ * @NFT_SET_POL_MEMORY: prefer low memory use over high performance
+ */
+enum nft_set_policies {
+       NFT_SET_POL_PERFORMANCE,
+       NFT_SET_POL_MEMORY,
+};
+
+/**
+ * enum nft_set_desc_attributes - set element description
+ *
+ * @NFTA_SET_DESC_SIZE: number of elements in set (NLA_U32)
+ */
+enum nft_set_desc_attributes {
+       NFTA_SET_DESC_UNSPEC,
+       NFTA_SET_DESC_SIZE,
+       __NFTA_SET_DESC_MAX
+};
+#define NFTA_SET_DESC_MAX      (__NFTA_SET_DESC_MAX - 1)
+
 /**
  * enum nft_set_attributes - nf_tables set netlink attributes
  *
@@ -221,6 +244,9 @@ enum nft_set_flags {
  * @NFTA_SET_KEY_LEN: key data length (NLA_U32)
  * @NFTA_SET_DATA_TYPE: mapping data type (NLA_U32)
  * @NFTA_SET_DATA_LEN: mapping data length (NLA_U32)
+ * @NFTA_SET_POLICY: selection policy (NLA_U32)
+ * @NFTA_SET_DESC: set description (NLA_NESTED)
+ * @NFTA_SET_ID: uniquely identifies a set in a transaction (NLA_U32)
  */
 enum nft_set_attributes {
        NFTA_SET_UNSPEC,
@@ -231,6 +257,9 @@ enum nft_set_attributes {
        NFTA_SET_KEY_LEN,
        NFTA_SET_DATA_TYPE,
        NFTA_SET_DATA_LEN,
+       NFTA_SET_POLICY,
+       NFTA_SET_DESC,
+       NFTA_SET_ID,
        __NFTA_SET_MAX
 };
 #define NFTA_SET_MAX           (__NFTA_SET_MAX - 1)
@@ -266,12 +295,14 @@ enum nft_set_elem_attributes {
  * @NFTA_SET_ELEM_LIST_TABLE: table of the set to be changed (NLA_STRING)
  * @NFTA_SET_ELEM_LIST_SET: name of the set to be changed (NLA_STRING)
  * @NFTA_SET_ELEM_LIST_ELEMENTS: list of set elements (NLA_NESTED: nft_set_elem_attributes)
+ * @NFTA_SET_ELEM_LIST_SET_ID: uniquely identifies a set in a transaction (NLA_U32)
  */
 enum nft_set_elem_list_attributes {
        NFTA_SET_ELEM_LIST_UNSPEC,
        NFTA_SET_ELEM_LIST_TABLE,
        NFTA_SET_ELEM_LIST_SET,
        NFTA_SET_ELEM_LIST_ELEMENTS,
+       NFTA_SET_ELEM_LIST_SET_ID,
        __NFTA_SET_ELEM_LIST_MAX
 };
 #define NFTA_SET_ELEM_LIST_MAX (__NFTA_SET_ELEM_LIST_MAX - 1)
@@ -457,12 +488,14 @@ enum nft_cmp_attributes {
  * @NFTA_LOOKUP_SET: name of the set where to look for (NLA_STRING)
  * @NFTA_LOOKUP_SREG: source register of the data to look for (NLA_U32: nft_registers)
  * @NFTA_LOOKUP_DREG: destination register (NLA_U32: nft_registers)
+ * @NFTA_LOOKUP_SET_ID: uniquely identifies a set in a transaction (NLA_U32)
  */
 enum nft_lookup_attributes {
        NFTA_LOOKUP_UNSPEC,
        NFTA_LOOKUP_SET,
        NFTA_LOOKUP_SREG,
        NFTA_LOOKUP_DREG,
+       NFTA_LOOKUP_SET_ID,
        __NFTA_LOOKUP_MAX
 };
 #define NFTA_LOOKUP_MAX                (__NFTA_LOOKUP_MAX - 1)
@@ -536,6 +569,8 @@ enum nft_exthdr_attributes {
  * @NFT_META_SECMARK: packet secmark (skb->secmark)
  * @NFT_META_NFPROTO: netfilter protocol
  * @NFT_META_L4PROTO: layer 4 protocol number
+ * @NFT_META_BRI_IIFNAME: packet input bridge interface name
+ * @NFT_META_BRI_OIFNAME: packet output bridge interface name
  */
 enum nft_meta_keys {
        NFT_META_LEN,
@@ -555,6 +590,8 @@ enum nft_meta_keys {
        NFT_META_SECMARK,
        NFT_META_NFPROTO,
        NFT_META_L4PROTO,
+       NFT_META_BRI_IIFNAME,
+       NFT_META_BRI_OIFNAME,
 };
 
 /**
index 596ddd45253c02b1f611c0655b649e651109485a..354a7e5e50f22e1c079b86dd1977ad7584d7d7c0 100644 (file)
@@ -20,6 +20,8 @@ enum nfnetlink_groups {
 #define NFNLGRP_CONNTRACK_EXP_DESTROY  NFNLGRP_CONNTRACK_EXP_DESTROY
        NFNLGRP_NFTABLES,
 #define NFNLGRP_NFTABLES                NFNLGRP_NFTABLES
+       NFNLGRP_ACCT_QUOTA,
+#define NFNLGRP_ACCT_QUOTA             NFNLGRP_ACCT_QUOTA
        __NFNLGRP_MAX,
 };
 #define NFNLGRP_MAX    (__NFNLGRP_MAX - 1)
index c7b6269e760b83bfbf6c30849f7724c0c67e3673..51404ec190223a84f9f0c72393d435c674b8f11c 100644 (file)
@@ -10,15 +10,24 @@ enum nfnl_acct_msg_types {
        NFNL_MSG_ACCT_GET,
        NFNL_MSG_ACCT_GET_CTRZERO,
        NFNL_MSG_ACCT_DEL,
+       NFNL_MSG_ACCT_OVERQUOTA,
        NFNL_MSG_ACCT_MAX
 };
 
+enum nfnl_acct_flags {
+       NFACCT_F_QUOTA_PKTS     = (1 << 0),
+       NFACCT_F_QUOTA_BYTES    = (1 << 1),
+       NFACCT_F_OVERQUOTA      = (1 << 2), /* can't be set from userspace */
+};
+
 enum nfnl_acct_type {
        NFACCT_UNSPEC,
        NFACCT_NAME,
        NFACCT_PKTS,
        NFACCT_BYTES,
        NFACCT_USE,
+       NFACCT_FLAGS,
+       NFACCT_QUOTA,
        __NFACCT_MAX
 };
 #define NFACCT_MAX (__NFACCT_MAX - 1)
index 9789dc95b6a8fb775a16e4fff1612101cae879c7..9b19b44619286616b04f1aa9f04d026878730744 100644 (file)
@@ -273,11 +273,19 @@ struct sockaddr_nfc_llcp {
  * First byte is the adapter index
  * Second byte contains flags
  *  - 0x01 - Direction (0=RX, 1=TX)
- *  - 0x02-0x80 - Reserved
+ *  - 0x02-0x04 - Payload type (000=LLCP, 001=NCI, 010=HCI, 011=Digital,
+ *                              100=Proprietary)
+ *  - 0x05-0x80 - Reserved
  **/
-#define NFC_LLCP_RAW_HEADER_SIZE       2
-#define NFC_LLCP_DIRECTION_RX          0x00
-#define NFC_LLCP_DIRECTION_TX          0x01
+#define NFC_RAW_HEADER_SIZE    2
+#define NFC_DIRECTION_RX               0x00
+#define NFC_DIRECTION_TX               0x01
+
+#define RAW_PAYLOAD_LLCP 0
+#define RAW_PAYLOAD_NCI        1
+#define RAW_PAYLOAD_HCI        2
+#define RAW_PAYLOAD_DIGITAL    3
+#define RAW_PAYLOAD_PROPRIETARY        4
 
 /* socket option names */
 #define NFC_LLCP_RW            0
index 194c1eab04d8ad9cb37ae855d8c011d48722c829..be9519b52bb10edef5e5be12ddd3ff2065d706ac 100644 (file)
  *     TX status event pertaining to the TX request.
  *     %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
  *     management frames at CCK rate or not in 2GHz band.
+ *     %NL80211_ATTR_CSA_C_OFFSETS_TX is an array of offsets to CSA
+ *     counters which will be updated to the current value. This attribute
+ *     is used during CSA period.
  * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this
  *     command may be used with the corresponding cookie to cancel the wait
  *     time if it is known that it is no longer necessary.
@@ -1525,10 +1528,10 @@ enum nl80211_commands {
  *     operation).
  * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information
  *     for the time while performing a channel switch.
- * @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter
- *     field in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
- * @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter
- *     field in the probe response (%NL80211_ATTR_PROBE_RESP).
+ * @NL80211_ATTR_CSA_C_OFF_BEACON: An array of offsets (u16) to the channel
+ *     switch counters in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
+ * @NL80211_ATTR_CSA_C_OFF_PRESP: An array of offsets (u16) to the channel
+ *     switch counters in the probe response (%NL80211_ATTR_PROBE_RESP).
  *
  * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32.
  *     As specified in the &enum nl80211_rxmgmt_flags.
@@ -1576,9 +1579,18 @@ enum nl80211_commands {
  *     advertise values that cannot always be met. In such cases, an attempt
  *     to add a new station entry with @NL80211_CMD_NEW_STATION may fail.
  *
+ * @NL80211_ATTR_CSA_C_OFFSETS_TX: An array of csa counter offsets (u16) which
+ *     should be updated when the frame is transmitted.
+ * @NL80211_ATTR_MAX_CSA_COUNTERS: U8 attribute used to advertise the maximum
+ *     supported number of csa counters.
+ *
  * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32.
  *     As specified in the &enum nl80211_tdls_peer_capability.
  *
+ * @NL80211_ATTR_IFACE_SOCKET_OWNER: flag attribute, if set during interface
+ *     creation then the new interface will be owned by the netlink socket
+ *     that created it and will be destroyed when the socket is closed
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1914,6 +1926,11 @@ enum nl80211_attrs {
 
        NL80211_ATTR_TDLS_PEER_CAPABILITY,
 
+       NL80211_ATTR_IFACE_SOCKET_OWNER,
+
+       NL80211_ATTR_CSA_C_OFFSETS_TX,
+       NL80211_ATTR_MAX_CSA_COUNTERS,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -2182,6 +2199,8 @@ enum nl80211_sta_bss_param {
  *     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_EXPECTED_THROUGHPUT: expected throughput considering also the
+ *     802.11 header (u32, kbps)
  * @__NL80211_STA_INFO_AFTER_LAST: internal
  * @NL80211_STA_INFO_MAX: highest possible station info attribute
  */
@@ -2213,6 +2232,7 @@ enum nl80211_sta_info {
        NL80211_STA_INFO_TX_BYTES64,
        NL80211_STA_INFO_CHAIN_SIGNAL,
        NL80211_STA_INFO_CHAIN_SIGNAL_AVG,
+       NL80211_STA_INFO_EXPECTED_THROUGHPUT,
 
        /* keep last */
        __NL80211_STA_INFO_AFTER_LAST,
@@ -2336,9 +2356,34 @@ enum nl80211_band_attr {
  *     using this channel as the primary or any of the secondary channels
  *     isn't possible
  * @NL80211_FREQUENCY_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
+ * @NL80211_FREQUENCY_ATTR_INDOOR_ONLY: Only indoor use is permitted on this
+ *     channel. A channel that has the INDOOR_ONLY attribute can only be
+ *     used when there is a clear assessment that the device is operating in
+ *     an indoor surroundings, i.e., it is connected to AC power (and not
+ *     through portable DC inverters) or is under the control of a master
+ *     that is acting as an AP and is connected to AC power.
+ * @NL80211_FREQUENCY_ATTR_GO_CONCURRENT: GO operation is allowed on this
+ *     channel if it's connected concurrently to a BSS on the same channel on
+ *     the 2 GHz band or to a channel in the same UNII band (on the 5 GHz
+ *     band), and IEEE80211_CHAN_RADAR is not set. Instantiating a GO on a
+ *     channel that has the GO_CONCURRENT attribute set can be done when there
+ *     is a clear assessment that the device is operating under the guidance of
+ *     an authorized master, i.e., setting up a GO while the device is also
+ *     connected to an AP with DFS and radar detection on the UNII band (it is
+ *     up to user-space, i.e., wpa_supplicant to perform the required
+ *     verifications)
+ * @NL80211_FREQUENCY_ATTR_NO_20MHZ: 20 MHz operation is not allowed
+ *     on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed
+ *     on this channel in current regulatory domain.
  * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
  *     currently defined
  * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
+ *
+ * See https://apps.fcc.gov/eas/comments/GetPublishedDocument.html?id=327&tn=528122
+ * for more information on the FCC description of the relaxations allowed
+ * by NL80211_FREQUENCY_ATTR_INDOOR_ONLY and
+ * NL80211_FREQUENCY_ATTR_GO_CONCURRENT.
  */
 enum nl80211_frequency_attr {
        __NL80211_FREQUENCY_ATTR_INVALID,
@@ -2355,6 +2400,10 @@ enum nl80211_frequency_attr {
        NL80211_FREQUENCY_ATTR_NO_80MHZ,
        NL80211_FREQUENCY_ATTR_NO_160MHZ,
        NL80211_FREQUENCY_ATTR_DFS_CAC_TIME,
+       NL80211_FREQUENCY_ATTR_INDOOR_ONLY,
+       NL80211_FREQUENCY_ATTR_GO_CONCURRENT,
+       NL80211_FREQUENCY_ATTR_NO_20MHZ,
+       NL80211_FREQUENCY_ATTR_NO_10MHZ,
 
        /* keep last */
        __NL80211_FREQUENCY_ATTR_AFTER_LAST,
@@ -2573,10 +2622,13 @@ enum nl80211_dfs_regions {
  *     present has been registered with the wireless core that
  *     has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a
  *     supported feature.
+ * @NL80211_USER_REG_HINT_INDOOR: a user sent an hint indicating that the
+ *     platform is operating in an indoor environment.
  */
 enum nl80211_user_reg_hint_type {
        NL80211_USER_REG_HINT_USER      = 0,
        NL80211_USER_REG_HINT_CELL_BASE = 1,
+       NL80211_USER_REG_HINT_INDOOR    = 2,
 };
 
 /**
@@ -3650,6 +3702,8 @@ enum nl80211_iface_limit_attrs {
  *     different channels may be used within this group.
  * @NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: u32 attribute containing the bitmap
  *     of supported channel widths for radar detection.
+ * @NL80211_IFACE_COMB_RADAR_DETECT_REGIONS: u32 attribute containing the bitmap
+ *     of supported regulatory regions for radar detection.
  * @NUM_NL80211_IFACE_COMB: number of attributes
  * @MAX_NL80211_IFACE_COMB: highest attribute number
  *
@@ -3683,6 +3737,7 @@ enum nl80211_if_combination_attrs {
        NL80211_IFACE_COMB_STA_AP_BI_MATCH,
        NL80211_IFACE_COMB_NUM_CHANNELS,
        NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
+       NL80211_IFACE_COMB_RADAR_DETECT_REGIONS,
 
        /* keep last */
        NUM_NL80211_IFACE_COMB,
@@ -3893,6 +3948,9 @@ enum nl80211_ap_sme_features {
  *     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.
+ * @NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE: This driver supports dynamic
+ *     channel bandwidth change (e.g., HT 20 <-> 40 MHz channel) during the
+ *     lifetime of a BSS.
  */
 enum nl80211_feature_flags {
        NL80211_FEATURE_SK_TX_STATUS                    = 1 << 0,
@@ -3913,6 +3971,7 @@ enum nl80211_feature_flags {
        NL80211_FEATURE_FULL_AP_CLIENT_STATE            = 1 << 15,
        NL80211_FEATURE_USERSPACE_MPM                   = 1 << 16,
        NL80211_FEATURE_ACTIVE_MONITOR                  = 1 << 17,
+       NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE       = 1 << 18,
 };
 
 /**
index 096fe1c6f83de5ef80bd56e69b177550c36fc0f3..29a7d8619d8dc2567b1119f30a37e8e247da5dc9 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Definitions for the NVM Express interface
- * Copyright (c) 2011-2013, Intel Corporation.
+ * Copyright (c) 2011-2014, 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,
  * 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 _UAPI_LINUX_NVME_H
@@ -31,7 +27,12 @@ struct nvme_id_power_state {
        __u8                    read_lat;
        __u8                    write_tput;
        __u8                    write_lat;
-       __u8                    rsvd16[16];
+       __le16                  idle_power;
+       __u8                    idle_scale;
+       __u8                    rsvd19;
+       __le16                  active_power;
+       __u8                    active_work_scale;
+       __u8                    rsvd23[9];
 };
 
 enum {
@@ -49,7 +50,9 @@ struct nvme_id_ctrl {
        __u8                    ieee[3];
        __u8                    mic;
        __u8                    mdts;
-       __u8                    rsvd78[178];
+       __u16                   cntlid;
+       __u32                   ver;
+       __u8                    rsvd84[172];
        __le16                  oacs;
        __u8                    acl;
        __u8                    aerl;
@@ -57,7 +60,11 @@ struct nvme_id_ctrl {
        __u8                    lpa;
        __u8                    elpe;
        __u8                    npss;
-       __u8                    rsvd264[248];
+       __u8                    avscc;
+       __u8                    apsta;
+       __le16                  wctemp;
+       __le16                  cctemp;
+       __u8                    rsvd270[242];
        __u8                    sqes;
        __u8                    cqes;
        __u8                    rsvd514[2];
@@ -68,7 +75,12 @@ struct nvme_id_ctrl {
        __u8                    vwc;
        __le16                  awun;
        __le16                  awupf;
-       __u8                    rsvd530[1518];
+       __u8                    nvscc;
+       __u8                    rsvd531;
+       __le16                  acwu;
+       __u8                    rsvd534[2];
+       __le32                  sgls;
+       __u8                    rsvd540[1508];
        struct nvme_id_power_state      psd[32];
        __u8                    vs[1024];
 };
@@ -77,6 +89,7 @@ enum {
        NVME_CTRL_ONCS_COMPARE                  = 1 << 0,
        NVME_CTRL_ONCS_WRITE_UNCORRECTABLE      = 1 << 1,
        NVME_CTRL_ONCS_DSM                      = 1 << 2,
+       NVME_CTRL_VWC_PRESENT                   = 1 << 0,
 };
 
 struct nvme_lbaf {
@@ -95,7 +108,15 @@ struct nvme_id_ns {
        __u8                    mc;
        __u8                    dpc;
        __u8                    dps;
-       __u8                    rsvd30[98];
+       __u8                    nmic;
+       __u8                    rescap;
+       __u8                    fpi;
+       __u8                    rsvd33;
+       __le16                  nawun;
+       __le16                  nawupf;
+       __le16                  nacwu;
+       __u8                    rsvd40[80];
+       __u8                    eui64[8];
        struct nvme_lbaf        lbaf[16];
        __u8                    rsvd192[192];
        __u8                    vs[3712];
@@ -126,7 +147,10 @@ struct nvme_smart_log {
        __u8                    unsafe_shutdowns[16];
        __u8                    media_errors[16];
        __u8                    num_err_log_entries[16];
-       __u8                    rsvd192[320];
+       __le32                  warning_temp_time;
+       __le32                  critical_comp_time;
+       __le16                  temp_sensor[8];
+       __u8                    rsvd216[296];
 };
 
 enum {
@@ -282,6 +306,10 @@ enum {
        NVME_FEAT_WRITE_ATOMIC  = 0x0a,
        NVME_FEAT_ASYNC_EVENT   = 0x0b,
        NVME_FEAT_SW_PROGRESS   = 0x0c,
+       NVME_LOG_ERROR          = 0x01,
+       NVME_LOG_SMART          = 0x02,
+       NVME_LOG_FW_SLOT        = 0x03,
+       NVME_LOG_RESERVATION    = 0x80,
        NVME_FWACT_REPL         = (0 << 3),
        NVME_FWACT_REPL_ACTV    = (1 << 3),
        NVME_FWACT_ACTV         = (2 << 3),
index 970553cbbc8e9d69dca1167aedfbfee20947e257..0b979ee4bfc0dea7e7d3f2f743708c997b2138cb 100644 (file)
@@ -395,7 +395,9 @@ struct ovs_key_nd {
  * @OVS_FLOW_ATTR_ACTIONS: Nested %OVS_ACTION_ATTR_* attributes specifying
  * the actions to take for packets that match the key.  Always present in
  * notifications.  Required for %OVS_FLOW_CMD_NEW requests, optional for
- * %OVS_FLOW_CMD_SET requests.
+ * %OVS_FLOW_CMD_SET requests.  An %OVS_FLOW_CMD_SET without
+ * %OVS_FLOW_ATTR_ACTIONS will not modify the actions.  To clear the actions,
+ * an %OVS_FLOW_ATTR_ACTIONS without any nested attributes must be given.
  * @OVS_FLOW_ATTR_STATS: &struct ovs_flow_stats giving statistics for this
  * flow.  Present in notifications if the stats would be nonzero.  Ignored in
  * requests.
index e3fc8f09d110ce50bb02c255e3ec3a26f17c321c..5312fae472187c4c768787f1df4f799db1104455 100644 (file)
@@ -163,8 +163,9 @@ enum perf_branch_sample_type {
        PERF_SAMPLE_BRANCH_ABORT_TX     = 1U << 7, /* transaction aborts */
        PERF_SAMPLE_BRANCH_IN_TX        = 1U << 8, /* in transaction */
        PERF_SAMPLE_BRANCH_NO_TX        = 1U << 9, /* not in transaction */
+       PERF_SAMPLE_BRANCH_COND         = 1U << 10, /* conditional branches */
 
-       PERF_SAMPLE_BRANCH_MAX          = 1U << 10, /* non-ABI */
+       PERF_SAMPLE_BRANCH_MAX          = 1U << 11, /* non-ABI */
 };
 
 #define PERF_SAMPLE_BRANCH_PLM_ALL \
@@ -301,8 +302,8 @@ struct perf_event_attr {
                                exclude_callchain_kernel : 1, /* exclude kernel callchains */
                                exclude_callchain_user   : 1, /* exclude user callchains */
                                mmap2          :  1, /* include mmap with inode data     */
-
-                               __reserved_1   : 40;
+                               comm_exec      :  1, /* flag comm events that are due to an exec */
+                               __reserved_1   : 39;
 
        union {
                __u32           wakeup_events;    /* wakeup every n events */
@@ -501,7 +502,12 @@ struct perf_event_mmap_page {
 #define PERF_RECORD_MISC_GUEST_KERNEL          (4 << 0)
 #define PERF_RECORD_MISC_GUEST_USER            (5 << 0)
 
+/*
+ * PERF_RECORD_MISC_MMAP_DATA and PERF_RECORD_MISC_COMM_EXEC are used on
+ * different events so can reuse the same bit position.
+ */
 #define PERF_RECORD_MISC_MMAP_DATA             (1 << 13)
+#define PERF_RECORD_MISC_COMM_EXEC             (1 << 13)
 /*
  * Indicates that the content of PERF_SAMPLE_IP points to
  * the actual instruction that triggered the event. See also
index 852373d27dbb2bdd2016bf6a9d2ba00cd68e2954..6f71b9b4159581eac01241f9c443948584f30f6e 100644 (file)
@@ -38,6 +38,7 @@
 #define _LINUX_TIPC_H_
 
 #include <linux/types.h>
+#include <linux/sockios.h>
 
 /*
  * TIPC addressing primitives
@@ -87,6 +88,7 @@ static inline unsigned int tipc_node(__u32 addr)
 
 #define TIPC_CFG_SRV           0       /* configuration service name type */
 #define TIPC_TOP_SRV           1       /* topology service name type */
+#define TIPC_LINK_STATE                2       /* link state name type */
 #define TIPC_RESERVED_TYPES    64      /* lowest user-publishable name type */
 
 /*
@@ -206,4 +208,25 @@ struct sockaddr_tipc {
 #define TIPC_NODE_RECVQ_DEPTH  131     /* Default: none (read only) */
 #define TIPC_SOCK_RECVQ_DEPTH  132     /* Default: none (read only) */
 
+/*
+ * Maximum sizes of TIPC bearer-related names (including terminating NULL)
+ * The string formatting for each name element is:
+ * media: media
+ * interface: media:interface name
+ * link: Z.C.N:interface-Z.C.N:interface
+ *
+ */
+
+#define TIPC_MAX_MEDIA_NAME    16
+#define TIPC_MAX_IF_NAME       16
+#define TIPC_MAX_BEARER_NAME   32
+#define TIPC_MAX_LINK_NAME     60
+
+#define SIOCGETLINKNAME                SIOCPROTOPRIVATE
+
+struct tipc_sioc_ln_req {
+       __u32 peer;
+       __u32 bearer_id;
+       char linkname[TIPC_MAX_LINK_NAME];
+};
 #endif
index 6b0bff09b3a7ced5dc7cf2c1a07dd4f82112b088..41a76acbb305f85cb4cb0ec6dfab9cab1e20e1d4 100644 (file)
@@ -39,6 +39,7 @@
 
 #include <linux/types.h>
 #include <linux/string.h>
+#include <linux/tipc.h>
 #include <asm/byteorder.h>
 
 #ifndef __KERNEL__
 #define TIPC_TLV_NAME_TBL_QUERY        25      /* struct tipc_name_table_query */
 #define TIPC_TLV_PORT_REF      26      /* 32-bit port reference */
 
-/*
- * Maximum sizes of TIPC bearer-related names (including terminating NUL)
- */
-
-#define TIPC_MAX_MEDIA_NAME    16      /* format = media */
-#define TIPC_MAX_IF_NAME       16      /* format = interface */
-#define TIPC_MAX_BEARER_NAME   32      /* format = media:interface */
-#define TIPC_MAX_LINK_NAME     60      /* format = Z.C.N:interface-Z.C.N:interface */
-
 /*
  * Link priority limits (min, default, max, media default)
  */
index e2bcfd75a30d38d37475cef6e0382e1081d5a3b0..16574ea18f0cf62d743287779ffaed6205d120d0 100644 (file)
@@ -29,6 +29,8 @@ struct udphdr {
 /* UDP socket options */
 #define UDP_CORK       1       /* Never send partially complete segments */
 #define UDP_ENCAP      100     /* Set the socket to accept encapsulated packets */
+#define UDP_NO_CHECK6_TX 101   /* Disable sending checksum for UDP6X */
+#define UDP_NO_CHECK6_RX 102   /* Disable accpeting checksum for UDP6 */
 
 /* UDP encapsulation types */
 #define UDP_ENCAP_ESPINUDP_NON_IKE     1 /* draft-ietf-ipsec-nat-t-ike-00/01 */
index 5759810e1c1b9768f67f8e703c625b7ce9d76e94..21eed488783f2d4d475dbb0282fb5f2ddae966d1 100644 (file)
@@ -80,7 +80,7 @@ struct snd_compr_tstamp {
 struct snd_compr_avail {
        __u64 avail;
        struct snd_compr_tstamp tstamp;
-};
+} __attribute__((packed));
 
 enum snd_compr_direction {
        SND_COMPRESS_PLAYBACK = 0,
similarity index 95%
rename from drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h
rename to include/video/imx-ipu-v3.h
index c2c6fab05eaab33190c374386d0dab7620f92b33..3e43e22cdff9cd053e7389f69609c4d952d752c5 100644 (file)
@@ -104,6 +104,7 @@ int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms);
 
 void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
                bool doublebuffer);
+int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel);
 void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num);
 
 /*
@@ -165,6 +166,20 @@ int ipu_dp_set_window_pos(struct ipu_dp *, u16 x_pos, u16 y_pos);
 int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, u8 alpha,
                bool bg_chan);
 
+/*
+ * IPU CMOS Sensor Interface (csi) functions
+ */
+int ipu_csi_enable(struct ipu_soc *ipu, int csi);
+int ipu_csi_disable(struct ipu_soc *ipu, int csi);
+
+/*
+ * IPU Sensor Multiple FIFO Controller (SMFC) functions
+ */
+int ipu_smfc_enable(struct ipu_soc *ipu);
+int ipu_smfc_disable(struct ipu_soc *ipu);
+int ipu_smfc_map_channel(struct ipu_soc *ipu, int channel, int csi_id, int mipi_id);
+int ipu_smfc_set_burstsize(struct ipu_soc *ipu, int channel, int burstsize);
+
 #define IPU_CPMEM_WORD(word, ofs, size) ((((word) * 160 + (ofs)) << 8) | (size))
 
 #define IPU_FIELD_UBO          IPU_CPMEM_WORD(0, 46, 22)
@@ -321,6 +336,7 @@ static inline void ipu_cpmem_set_burstsize(struct ipu_ch_param __iomem *p,
 };
 
 struct ipu_client_platformdata {
+       int csi;
        int di;
        int dc;
        int dp;
index c50061db609893a924160556203f7dcf73e24700..70054cc0708d0aed1bc4fa23ce6bec09d65435b8 100644 (file)
  * node as before.
  */
 
+/*
+ * Multiple transmit and receive queues:
+ * If supported, the backend will write the key "multi-queue-max-queues" to
+ * the directory for that vif, and set its value to the maximum supported
+ * number of queues.
+ * Frontends that are aware of this feature and wish to use it can write the
+ * key "multi-queue-num-queues", set to the number they wish to use, which
+ * must be greater than zero, and no more than the value reported by the backend
+ * in "multi-queue-max-queues".
+ *
+ * Queues replicate the shared rings and event channels.
+ * "feature-split-event-channels" may optionally be used when using
+ * multiple queues, but is not mandatory.
+ *
+ * Each queue consists of one shared ring pair, i.e. there must be the same
+ * number of tx and rx rings.
+ *
+ * For frontends requesting just one queue, the usual event-channel and
+ * ring-ref keys are written as before, simplifying the backend processing
+ * to avoid distinguishing between a frontend that doesn't understand the
+ * multi-queue feature, and one that does, but requested only one queue.
+ *
+ * Frontends requesting two or more queues must not write the toplevel
+ * event-channel (or event-channel-{tx,rx}) and {tx,rx}-ring-ref keys,
+ * instead writing those keys under sub-keys having the name "queue-N" where
+ * N is the integer ID of the queue for which those keys belong. Queues
+ * are indexed from zero. For example, a frontend with two queues and split
+ * event channels must write the following set of queue-related keys:
+ *
+ * /local/domain/1/device/vif/0/multi-queue-num-queues = "2"
+ * /local/domain/1/device/vif/0/queue-0 = ""
+ * /local/domain/1/device/vif/0/queue-0/tx-ring-ref = "<ring-ref-tx0>"
+ * /local/domain/1/device/vif/0/queue-0/rx-ring-ref = "<ring-ref-rx0>"
+ * /local/domain/1/device/vif/0/queue-0/event-channel-tx = "<evtchn-tx0>"
+ * /local/domain/1/device/vif/0/queue-0/event-channel-rx = "<evtchn-rx0>"
+ * /local/domain/1/device/vif/0/queue-1 = ""
+ * /local/domain/1/device/vif/0/queue-1/tx-ring-ref = "<ring-ref-tx1>"
+ * /local/domain/1/device/vif/0/queue-1/rx-ring-ref = "<ring-ref-rx1"
+ * /local/domain/1/device/vif/0/queue-1/event-channel-tx = "<evtchn-tx1>"
+ * /local/domain/1/device/vif/0/queue-1/event-channel-rx = "<evtchn-rx1>"
+ *
+ * If there is any inconsistency in the XenStore data, the backend may
+ * choose not to connect any queues, instead treating the request as an
+ * error. This includes scenarios where more (or fewer) queues were
+ * requested than the frontend provided details for.
+ *
+ * Mapping of packets to queues is considered to be a function of the
+ * transmitting system (backend or frontend) and is not negotiated
+ * between the two. Guests are free to transmit packets on any queue
+ * they choose, provided it has been set up correctly. Guests must be
+ * prepared to receive packets on any queue they have requested be set up.
+ */
+
 /*
  * "feature-no-csum-offload" should be used to turn IPv4 TCP/UDP checksum
  * offload off or on. If it is missing then the feature is assumed to be on.
index 0ec25157deef71160819c326e7c229d431c5de00..e8ae1fef0908965b0c5fff7ba7ab8f043e83a980 100644 (file)
@@ -253,6 +253,27 @@ static int __init repair_env_string(char *param, char *val, const char *unused)
        return 0;
 }
 
+/* Anything after -- gets handed straight to init. */
+static int __init set_init_arg(char *param, char *val, const char *unused)
+{
+       unsigned int i;
+
+       if (panic_later)
+               return 0;
+
+       repair_env_string(param, val, unused);
+
+       for (i = 0; argv_init[i]; i++) {
+               if (i == MAX_INIT_ARGS) {
+                       panic_later = "init";
+                       panic_param = param;
+                       return 0;
+               }
+       }
+       argv_init[i] = param;
+       return 0;
+}
+
 /*
  * Unknown boot options get handed to init, unless they look like
  * unused parameters (modprobe will find them in /proc/cmdline).
@@ -479,7 +500,7 @@ static void __init mm_init(void)
 
 asmlinkage __visible void __init start_kernel(void)
 {
-       char * command_line;
+       char * command_line, *after_dashes;
        extern const struct kernel_param __start___param[], __stop___param[];
 
        /*
@@ -519,9 +540,13 @@ asmlinkage __visible void __init start_kernel(void)
 
        pr_notice("Kernel command line: %s\n", boot_command_line);
        parse_early_param();
-       parse_args("Booting kernel", static_command_line, __start___param,
-                  __stop___param - __start___param,
-                  -1, -1, &unknown_bootoption);
+       after_dashes = parse_args("Booting kernel",
+                                 static_command_line, __start___param,
+                                 __stop___param - __start___param,
+                                 -1, -1, &unknown_bootoption);
+       if (after_dashes)
+               parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
+                          set_init_arg);
 
        jump_label_init();
 
index d2b32ac27a394095e87229030e010eb428ae7174..35536d9c096420f3718b93e583c10d5fdfc07cec 100644 (file)
@@ -223,3 +223,10 @@ endif
 config MUTEX_SPIN_ON_OWNER
        def_bool y
        depends on SMP && !DEBUG_MUTEXES
+
+config ARCH_USE_QUEUE_RWLOCK
+       bool
+
+config QUEUE_RWLOCK
+       def_bool y if ARCH_USE_QUEUE_RWLOCK
+       depends on SMP
index f30106459a3243945d75038d8c069b06906efa13..3ef2e0e797e8e576cc6c922d65ef2dc6e40e2d86 100644 (file)
@@ -423,6 +423,38 @@ static void kauditd_send_skb(struct sk_buff *skb)
                consume_skb(skb);
 }
 
+/*
+ * kauditd_send_multicast_skb - send the skb to multicast userspace listeners
+ *
+ * This function doesn't consume an skb as might be expected since it has to
+ * copy it anyways.
+ */
+static void kauditd_send_multicast_skb(struct sk_buff *skb)
+{
+       struct sk_buff          *copy;
+       struct audit_net        *aunet = net_generic(&init_net, audit_net_id);
+       struct sock             *sock = aunet->nlsk;
+
+       if (!netlink_has_listeners(sock, AUDIT_NLGRP_READLOG))
+               return;
+
+       /*
+        * The seemingly wasteful skb_copy() rather than bumping the refcount
+        * using skb_get() is necessary because non-standard mods are made to
+        * the skb by the original kaudit unicast socket send routine.  The
+        * existing auditd daemon assumes this breakage.  Fixing this would
+        * require co-ordinating a change in the established protocol between
+        * the kaudit kernel subsystem and the auditd userspace code.  There is
+        * no reason for new multicast clients to continue with this
+        * non-compliance.
+        */
+       copy = skb_copy(skb, GFP_KERNEL);
+       if (!copy)
+               return;
+
+       nlmsg_multicast(sock, copy, 0, AUDIT_NLGRP_READLOG, GFP_KERNEL);
+}
+
 /*
  * flush_hold_queue - empty the hold queue if auditd appears
  *
@@ -1076,10 +1108,22 @@ static void audit_receive(struct sk_buff  *skb)
        mutex_unlock(&audit_cmd_mutex);
 }
 
+/* Run custom bind function on netlink socket group connect or bind requests. */
+static int audit_bind(int group)
+{
+       if (!capable(CAP_AUDIT_READ))
+               return -EPERM;
+
+       return 0;
+}
+
 static int __net_init audit_net_init(struct net *net)
 {
        struct netlink_kernel_cfg cfg = {
                .input  = audit_receive,
+               .bind   = audit_bind,
+               .flags  = NL_CFG_F_NONROOT_RECV,
+               .groups = AUDIT_NLGRP_MAX,
        };
 
        struct audit_net *aunet = net_generic(net, audit_net_id);
@@ -1901,10 +1945,10 @@ out:
  * audit_log_end - end one audit record
  * @ab: the audit_buffer
  *
- * The netlink_* functions cannot be called inside an irq context, so
- * the audit buffer is placed on a queue and a tasklet is scheduled to
- * remove them from the queue outside the irq context.  May be called in
- * any context.
+ * netlink_unicast() cannot be called inside an irq context because it blocks
+ * (last arg, flags, is not set to MSG_DONTWAIT), so the audit buffer is placed
+ * on a queue and a tasklet is scheduled to remove them from the queue outside
+ * the irq context.  May be called in any context.
  */
 void audit_log_end(struct audit_buffer *ab)
 {
@@ -1914,6 +1958,18 @@ void audit_log_end(struct audit_buffer *ab)
                audit_log_lost("rate limit exceeded");
        } else {
                struct nlmsghdr *nlh = nlmsg_hdr(ab->skb);
+
+               kauditd_send_multicast_skb(ab->skb);
+
+               /*
+                * The original kaudit unicast socket sends up messages with
+                * nlmsg_len set to the payload length rather than the entire
+                * message length.  This breaks the standard set by netlink.
+                * The existing auditd daemon assumes this breakage.  Fixing
+                * this would require co-ordinating a change in the established
+                * protocol between the kaudit kernel subsystem and the auditd
+                * userspace code.
+                */
                nlh->nlmsg_len = ab->skb->len - NLMSG_HDRLEN;
 
                if (audit_pid) {
index acf791c55b71591a684d09fc6de6850e2c19b1f8..a343bde710b1025d6519dd6efc7d890bdff86118 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/gfp.h>
 #include <linux/suspend.h>
 #include <linux/lockdep.h>
+#include <trace/events/power.h>
 
 #include "smpboot.h"
 
@@ -520,7 +521,9 @@ int disable_nonboot_cpus(void)
        for_each_online_cpu(cpu) {
                if (cpu == first_cpu)
                        continue;
+               trace_suspend_resume(TPS("CPU_OFF"), cpu, true);
                error = _cpu_down(cpu, 1);
+               trace_suspend_resume(TPS("CPU_OFF"), cpu, false);
                if (!error)
                        cpumask_set_cpu(cpu, frozen_cpus);
                else {
@@ -563,7 +566,9 @@ void __ref enable_nonboot_cpus(void)
        arch_enable_nonboot_cpus_begin();
 
        for_each_cpu(cpu, frozen_cpus) {
+               trace_suspend_resume(TPS("CPU_ON"), cpu, true);
                error = _cpu_up(cpu, 1);
+               trace_suspend_resume(TPS("CPU_ON"), cpu, false);
                if (!error) {
                        pr_info("CPU%d is up\n", cpu);
                        continue;
index 24d35cc38e42ab080400e3ae82f93d475c19e177..5fa58e4cffac3a7f2c7144fda8aeb72e6fc05a98 100644 (file)
@@ -2974,6 +2974,22 @@ out:
        local_irq_restore(flags);
 }
 
+void perf_event_exec(void)
+{
+       struct perf_event_context *ctx;
+       int ctxn;
+
+       rcu_read_lock();
+       for_each_task_context_nr(ctxn) {
+               ctx = current->perf_event_ctxp[ctxn];
+               if (!ctx)
+                       continue;
+
+               perf_event_enable_on_exec(ctx);
+       }
+       rcu_read_unlock();
+}
+
 /*
  * Cross CPU call to read the hardware event
  */
@@ -5075,21 +5091,9 @@ static void perf_event_comm_event(struct perf_comm_event *comm_event)
                       NULL);
 }
 
-void perf_event_comm(struct task_struct *task)
+void perf_event_comm(struct task_struct *task, bool exec)
 {
        struct perf_comm_event comm_event;
-       struct perf_event_context *ctx;
-       int ctxn;
-
-       rcu_read_lock();
-       for_each_task_context_nr(ctxn) {
-               ctx = task->perf_event_ctxp[ctxn];
-               if (!ctx)
-                       continue;
-
-               perf_event_enable_on_exec(ctx);
-       }
-       rcu_read_unlock();
 
        if (!atomic_read(&nr_comm_events))
                return;
@@ -5101,7 +5105,7 @@ void perf_event_comm(struct task_struct *task)
                .event_id  = {
                        .header = {
                                .type = PERF_RECORD_COMM,
-                               .misc = 0,
+                               .misc = exec ? PERF_RECORD_MISC_COMM_EXEC : 0,
                                /* .size */
                        },
                        /* .pid */
@@ -7122,6 +7126,13 @@ SYSCALL_DEFINE5(perf_event_open,
                }
        }
 
+       if (is_sampling_event(event)) {
+               if (event->pmu->capabilities & PERF_PMU_CAP_NO_INTERRUPT) {
+                       err = -ENOTSUPP;
+                       goto err_alloc;
+               }
+       }
+
        account_event(event);
 
        /*
@@ -7433,7 +7444,7 @@ __perf_event_exit_task(struct perf_event *child_event,
 
 static void perf_event_exit_task_context(struct task_struct *child, int ctxn)
 {
-       struct perf_event *child_event;
+       struct perf_event *child_event, *next;
        struct perf_event_context *child_ctx;
        unsigned long flags;
 
@@ -7487,7 +7498,7 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn)
         */
        mutex_lock(&child_ctx->mutex);
 
-       list_for_each_entry_rcu(child_event, &child_ctx->event_list, event_entry)
+       list_for_each_entry_safe(child_event, next, &child_ctx->event_list, event_entry)
                __perf_event_exit_task(child_event, child_ctx, child);
 
        mutex_unlock(&child_ctx->mutex);
index adcd76a968397a2a2c6f6226dfd10224d6a24db5..c445e392e93ff1a977f2f61ca25dc69de7e61bcc 100644 (file)
@@ -36,6 +36,7 @@
 #include "../../mm/internal.h" /* munlock_vma_page */
 #include <linux/percpu-rwsem.h>
 #include <linux/task_work.h>
+#include <linux/shmem_fs.h>
 
 #include <linux/uprobes.h>
 
@@ -127,7 +128,7 @@ struct xol_area {
  */
 static bool valid_vma(struct vm_area_struct *vma, bool is_register)
 {
-       vm_flags_t flags = VM_HUGETLB | VM_MAYEXEC | VM_SHARED;
+       vm_flags_t flags = VM_HUGETLB | VM_MAYEXEC | VM_MAYSHARE;
 
        if (is_register)
                flags |= VM_WRITE;
@@ -279,18 +280,13 @@ static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t
  * supported by that architecture then we need to modify is_trap_at_addr and
  * uprobe_write_opcode accordingly. This would never be a problem for archs
  * that have fixed length instructions.
- */
-
-/*
+ *
  * uprobe_write_opcode - write the opcode at a given virtual address.
  * @mm: the probed process address space.
  * @vaddr: the virtual address to store the opcode.
  * @opcode: opcode to be written at @vaddr.
  *
- * Called with mm->mmap_sem held (for read and with a reference to
- * mm).
- *
- * For mm @mm, write the opcode at @vaddr.
+ * Called with mm->mmap_sem held for write.
  * Return 0 (success) or a negative errno.
  */
 int uprobe_write_opcode(struct mm_struct *mm, unsigned long vaddr,
@@ -310,21 +306,25 @@ retry:
        if (ret <= 0)
                goto put_old;
 
+       ret = anon_vma_prepare(vma);
+       if (ret)
+               goto put_old;
+
        ret = -ENOMEM;
        new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr);
        if (!new_page)
                goto put_old;
 
-       __SetPageUptodate(new_page);
+       if (mem_cgroup_charge_anon(new_page, mm, GFP_KERNEL))
+               goto put_new;
 
+       __SetPageUptodate(new_page);
        copy_highpage(new_page, old_page);
        copy_to_page(new_page, vaddr, &opcode, UPROBE_SWBP_INSN_SIZE);
 
-       ret = anon_vma_prepare(vma);
-       if (ret)
-               goto put_new;
-
        ret = __replace_page(vma, vaddr, old_page, new_page);
+       if (ret)
+               mem_cgroup_uncharge_page(new_page);
 
 put_new:
        page_cache_release(new_page);
@@ -537,14 +537,15 @@ static int __copy_insn(struct address_space *mapping, struct file *filp,
                        void *insn, int nbytes, loff_t offset)
 {
        struct page *page;
-
-       if (!mapping->a_ops->readpage)
-               return -EIO;
        /*
-        * Ensure that the page that has the original instruction is
-        * populated and in page-cache.
+        * Ensure that the page that has the original instruction is populated
+        * and in page-cache. If ->readpage == NULL it must be shmem_mapping(),
+        * see uprobe_register().
         */
-       page = read_mapping_page(mapping, offset >> PAGE_CACHE_SHIFT, filp);
+       if (mapping->a_ops->readpage)
+               page = read_mapping_page(mapping, offset >> PAGE_CACHE_SHIFT, filp);
+       else
+               page = shmem_read_mapping_page(mapping, offset >> PAGE_CACHE_SHIFT);
        if (IS_ERR(page))
                return PTR_ERR(page);
 
@@ -880,6 +881,9 @@ int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *
        if (!uc->handler && !uc->ret_handler)
                return -EINVAL;
 
+       /* copy_insn() uses read_mapping_page() or shmem_read_mapping_page() */
+       if (!inode->i_mapping->a_ops->readpage && !shmem_mapping(inode->i_mapping))
+               return -EIO;
        /* Racy, just to catch the obvious mistakes */
        if (offset > i_size_read(inode))
                return -EINVAL;
@@ -1361,6 +1365,16 @@ unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs)
        return instruction_pointer(regs) - UPROBE_SWBP_INSN_SIZE;
 }
 
+unsigned long uprobe_get_trap_addr(struct pt_regs *regs)
+{
+       struct uprobe_task *utask = current->utask;
+
+       if (unlikely(utask && utask->active_uprobe))
+               return utask->vaddr;
+
+       return instruction_pointer(regs);
+}
+
 /*
  * Called with no locks held.
  * Called in context of a exiting or a exec-ing thread.
index ceeadfcabb7611defdbb10f66f3a6b23e60fb0ed..3214289df5a7a8f6917718a9a00f418794efeab1 100644 (file)
@@ -86,21 +86,8 @@ static raw_spinlock_t *kretprobe_table_lock_ptr(unsigned long hash)
        return &(kretprobe_table_locks[hash].lock);
 }
 
-/*
- * Normally, functions that we'd want to prohibit kprobes in, are marked
- * __kprobes. But, there are cases where such functions already belong to
- * a different section (__sched for preempt_schedule)
- *
- * For such cases, we now have a blacklist
- */
-static struct kprobe_blackpoint kprobe_blacklist[] = {
-       {"preempt_schedule",},
-       {"native_get_debugreg",},
-       {"irq_entries_start",},
-       {"common_interrupt",},
-       {"mcount",},    /* mcount can be called from everywhere */
-       {NULL}    /* Terminator */
-};
+/* Blacklist -- list of struct kprobe_blacklist_entry */
+static LIST_HEAD(kprobe_blacklist);
 
 #ifdef __ARCH_WANT_KPROBES_INSN_SLOT
 /*
@@ -151,13 +138,13 @@ struct kprobe_insn_cache kprobe_insn_slots = {
        .insn_size = MAX_INSN_SIZE,
        .nr_garbage = 0,
 };
-static int __kprobes collect_garbage_slots(struct kprobe_insn_cache *c);
+static int collect_garbage_slots(struct kprobe_insn_cache *c);
 
 /**
  * __get_insn_slot() - Find a slot on an executable page for an instruction.
  * We allocate an executable page if there's no room on existing ones.
  */
-kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c)
+kprobe_opcode_t *__get_insn_slot(struct kprobe_insn_cache *c)
 {
        struct kprobe_insn_page *kip;
        kprobe_opcode_t *slot = NULL;
@@ -214,7 +201,7 @@ out:
 }
 
 /* Return 1 if all garbages are collected, otherwise 0. */
-static int __kprobes collect_one_slot(struct kprobe_insn_page *kip, int idx)
+static int collect_one_slot(struct kprobe_insn_page *kip, int idx)
 {
        kip->slot_used[idx] = SLOT_CLEAN;
        kip->nused--;
@@ -235,7 +222,7 @@ static int __kprobes collect_one_slot(struct kprobe_insn_page *kip, int idx)
        return 0;
 }
 
-static int __kprobes collect_garbage_slots(struct kprobe_insn_cache *c)
+static int collect_garbage_slots(struct kprobe_insn_cache *c)
 {
        struct kprobe_insn_page *kip, *next;
 
@@ -257,8 +244,8 @@ static int __kprobes collect_garbage_slots(struct kprobe_insn_cache *c)
        return 0;
 }
 
-void __kprobes __free_insn_slot(struct kprobe_insn_cache *c,
-                               kprobe_opcode_t *slot, int dirty)
+void __free_insn_slot(struct kprobe_insn_cache *c,
+                     kprobe_opcode_t *slot, int dirty)
 {
        struct kprobe_insn_page *kip;
 
@@ -314,7 +301,7 @@ static inline void reset_kprobe_instance(void)
  *                             OR
  *     - with preemption disabled - from arch/xxx/kernel/kprobes.c
  */
-struct kprobe __kprobes *get_kprobe(void *addr)
+struct kprobe *get_kprobe(void *addr)
 {
        struct hlist_head *head;
        struct kprobe *p;
@@ -327,8 +314,9 @@ struct kprobe __kprobes *get_kprobe(void *addr)
 
        return NULL;
 }
+NOKPROBE_SYMBOL(get_kprobe);
 
-static int __kprobes aggr_pre_handler(struct kprobe *p, struct pt_regs *regs);
+static int aggr_pre_handler(struct kprobe *p, struct pt_regs *regs);
 
 /* Return true if the kprobe is an aggregator */
 static inline int kprobe_aggrprobe(struct kprobe *p)
@@ -360,7 +348,7 @@ static bool kprobes_allow_optimization;
  * Call all pre_handler on the list, but ignores its return value.
  * This must be called from arch-dep optimized caller.
  */
-void __kprobes opt_pre_handler(struct kprobe *p, struct pt_regs *regs)
+void opt_pre_handler(struct kprobe *p, struct pt_regs *regs)
 {
        struct kprobe *kp;
 
@@ -372,9 +360,10 @@ void __kprobes opt_pre_handler(struct kprobe *p, struct pt_regs *regs)
                reset_kprobe_instance();
        }
 }
+NOKPROBE_SYMBOL(opt_pre_handler);
 
 /* Free optimized instructions and optimized_kprobe */
-static __kprobes void free_aggr_kprobe(struct kprobe *p)
+static void free_aggr_kprobe(struct kprobe *p)
 {
        struct optimized_kprobe *op;
 
@@ -412,7 +401,7 @@ static inline int kprobe_disarmed(struct kprobe *p)
 }
 
 /* Return true(!0) if the probe is queued on (un)optimizing lists */
-static int __kprobes kprobe_queued(struct kprobe *p)
+static int kprobe_queued(struct kprobe *p)
 {
        struct optimized_kprobe *op;
 
@@ -428,7 +417,7 @@ static int __kprobes kprobe_queued(struct kprobe *p)
  * Return an optimized kprobe whose optimizing code replaces
  * instructions including addr (exclude breakpoint).
  */
-static struct kprobe *__kprobes get_optimized_kprobe(unsigned long addr)
+static struct kprobe *get_optimized_kprobe(unsigned long addr)
 {
        int i;
        struct kprobe *p = NULL;
@@ -460,7 +449,7 @@ static DECLARE_DELAYED_WORK(optimizing_work, kprobe_optimizer);
  * Optimize (replace a breakpoint with a jump) kprobes listed on
  * optimizing_list.
  */
-static __kprobes void do_optimize_kprobes(void)
+static void do_optimize_kprobes(void)
 {
        /* Optimization never be done when disarmed */
        if (kprobes_all_disarmed || !kprobes_allow_optimization ||
@@ -488,7 +477,7 @@ static __kprobes void do_optimize_kprobes(void)
  * Unoptimize (replace a jump with a breakpoint and remove the breakpoint
  * if need) kprobes listed on unoptimizing_list.
  */
-static __kprobes void do_unoptimize_kprobes(void)
+static void do_unoptimize_kprobes(void)
 {
        struct optimized_kprobe *op, *tmp;
 
@@ -520,7 +509,7 @@ static __kprobes void do_unoptimize_kprobes(void)
 }
 
 /* Reclaim all kprobes on the free_list */
-static __kprobes void do_free_cleaned_kprobes(void)
+static void do_free_cleaned_kprobes(void)
 {
        struct optimized_kprobe *op, *tmp;
 
@@ -532,13 +521,13 @@ static __kprobes void do_free_cleaned_kprobes(void)
 }
 
 /* Start optimizer after OPTIMIZE_DELAY passed */
-static __kprobes void kick_kprobe_optimizer(void)
+static void kick_kprobe_optimizer(void)
 {
        schedule_delayed_work(&optimizing_work, OPTIMIZE_DELAY);
 }
 
 /* Kprobe jump optimizer */
-static __kprobes void kprobe_optimizer(struct work_struct *work)
+static void kprobe_optimizer(struct work_struct *work)
 {
        mutex_lock(&kprobe_mutex);
        /* Lock modules while optimizing kprobes */
@@ -574,7 +563,7 @@ static __kprobes void kprobe_optimizer(struct work_struct *work)
 }
 
 /* Wait for completing optimization and unoptimization */
-static __kprobes void wait_for_kprobe_optimizer(void)
+static void wait_for_kprobe_optimizer(void)
 {
        mutex_lock(&kprobe_mutex);
 
@@ -593,7 +582,7 @@ static __kprobes void wait_for_kprobe_optimizer(void)
 }
 
 /* Optimize kprobe if p is ready to be optimized */
-static __kprobes void optimize_kprobe(struct kprobe *p)
+static void optimize_kprobe(struct kprobe *p)
 {
        struct optimized_kprobe *op;
 
@@ -627,7 +616,7 @@ static __kprobes void optimize_kprobe(struct kprobe *p)
 }
 
 /* Short cut to direct unoptimizing */
-static __kprobes void force_unoptimize_kprobe(struct optimized_kprobe *op)
+static void force_unoptimize_kprobe(struct optimized_kprobe *op)
 {
        get_online_cpus();
        arch_unoptimize_kprobe(op);
@@ -637,7 +626,7 @@ static __kprobes void force_unoptimize_kprobe(struct optimized_kprobe *op)
 }
 
 /* Unoptimize a kprobe if p is optimized */
-static __kprobes void unoptimize_kprobe(struct kprobe *p, bool force)
+static void unoptimize_kprobe(struct kprobe *p, bool force)
 {
        struct optimized_kprobe *op;
 
@@ -697,7 +686,7 @@ static void reuse_unused_kprobe(struct kprobe *ap)
 }
 
 /* Remove optimized instructions */
-static void __kprobes kill_optimized_kprobe(struct kprobe *p)
+static void kill_optimized_kprobe(struct kprobe *p)
 {
        struct optimized_kprobe *op;
 
@@ -723,7 +712,7 @@ static void __kprobes kill_optimized_kprobe(struct kprobe *p)
 }
 
 /* Try to prepare optimized instructions */
-static __kprobes void prepare_optimized_kprobe(struct kprobe *p)
+static void prepare_optimized_kprobe(struct kprobe *p)
 {
        struct optimized_kprobe *op;
 
@@ -732,7 +721,7 @@ static __kprobes void prepare_optimized_kprobe(struct kprobe *p)
 }
 
 /* Allocate new optimized_kprobe and try to prepare optimized instructions */
-static __kprobes struct kprobe *alloc_aggr_kprobe(struct kprobe *p)
+static struct kprobe *alloc_aggr_kprobe(struct kprobe *p)
 {
        struct optimized_kprobe *op;
 
@@ -747,13 +736,13 @@ static __kprobes struct kprobe *alloc_aggr_kprobe(struct kprobe *p)
        return &op->kp;
 }
 
-static void __kprobes init_aggr_kprobe(struct kprobe *ap, struct kprobe *p);
+static void init_aggr_kprobe(struct kprobe *ap, struct kprobe *p);
 
 /*
  * Prepare an optimized_kprobe and optimize it
  * NOTE: p must be a normal registered kprobe
  */
-static __kprobes void try_to_optimize_kprobe(struct kprobe *p)
+static void try_to_optimize_kprobe(struct kprobe *p)
 {
        struct kprobe *ap;
        struct optimized_kprobe *op;
@@ -787,7 +776,7 @@ out:
 }
 
 #ifdef CONFIG_SYSCTL
-static void __kprobes optimize_all_kprobes(void)
+static void optimize_all_kprobes(void)
 {
        struct hlist_head *head;
        struct kprobe *p;
@@ -810,7 +799,7 @@ out:
        mutex_unlock(&kprobe_mutex);
 }
 
-static void __kprobes unoptimize_all_kprobes(void)
+static void unoptimize_all_kprobes(void)
 {
        struct hlist_head *head;
        struct kprobe *p;
@@ -861,7 +850,7 @@ int proc_kprobes_optimization_handler(struct ctl_table *table, int write,
 #endif /* CONFIG_SYSCTL */
 
 /* Put a breakpoint for a probe. Must be called with text_mutex locked */
-static void __kprobes __arm_kprobe(struct kprobe *p)
+static void __arm_kprobe(struct kprobe *p)
 {
        struct kprobe *_p;
 
@@ -876,7 +865,7 @@ static void __kprobes __arm_kprobe(struct kprobe *p)
 }
 
 /* Remove the breakpoint of a probe. Must be called with text_mutex locked */
-static void __kprobes __disarm_kprobe(struct kprobe *p, bool reopt)
+static void __disarm_kprobe(struct kprobe *p, bool reopt)
 {
        struct kprobe *_p;
 
@@ -911,13 +900,13 @@ static void reuse_unused_kprobe(struct kprobe *ap)
        BUG_ON(kprobe_unused(ap));
 }
 
-static __kprobes void free_aggr_kprobe(struct kprobe *p)
+static void free_aggr_kprobe(struct kprobe *p)
 {
        arch_remove_kprobe(p);
        kfree(p);
 }
 
-static __kprobes struct kprobe *alloc_aggr_kprobe(struct kprobe *p)
+static struct kprobe *alloc_aggr_kprobe(struct kprobe *p)
 {
        return kzalloc(sizeof(struct kprobe), GFP_KERNEL);
 }
@@ -931,7 +920,7 @@ static struct ftrace_ops kprobe_ftrace_ops __read_mostly = {
 static int kprobe_ftrace_enabled;
 
 /* Must ensure p->addr is really on ftrace */
-static int __kprobes prepare_kprobe(struct kprobe *p)
+static int prepare_kprobe(struct kprobe *p)
 {
        if (!kprobe_ftrace(p))
                return arch_prepare_kprobe(p);
@@ -940,7 +929,7 @@ static int __kprobes prepare_kprobe(struct kprobe *p)
 }
 
 /* Caller must lock kprobe_mutex */
-static void __kprobes arm_kprobe_ftrace(struct kprobe *p)
+static void arm_kprobe_ftrace(struct kprobe *p)
 {
        int ret;
 
@@ -955,7 +944,7 @@ static void __kprobes arm_kprobe_ftrace(struct kprobe *p)
 }
 
 /* Caller must lock kprobe_mutex */
-static void __kprobes disarm_kprobe_ftrace(struct kprobe *p)
+static void disarm_kprobe_ftrace(struct kprobe *p)
 {
        int ret;
 
@@ -975,7 +964,7 @@ static void __kprobes disarm_kprobe_ftrace(struct kprobe *p)
 #endif
 
 /* Arm a kprobe with text_mutex */
-static void __kprobes arm_kprobe(struct kprobe *kp)
+static void arm_kprobe(struct kprobe *kp)
 {
        if (unlikely(kprobe_ftrace(kp))) {
                arm_kprobe_ftrace(kp);
@@ -992,7 +981,7 @@ static void __kprobes arm_kprobe(struct kprobe *kp)
 }
 
 /* Disarm a kprobe with text_mutex */
-static void __kprobes disarm_kprobe(struct kprobe *kp, bool reopt)
+static void disarm_kprobe(struct kprobe *kp, bool reopt)
 {
        if (unlikely(kprobe_ftrace(kp))) {
                disarm_kprobe_ftrace(kp);
@@ -1008,7 +997,7 @@ static void __kprobes disarm_kprobe(struct kprobe *kp, bool reopt)
  * Aggregate handlers for multiple kprobes support - these handlers
  * take care of invoking the individual kprobe handlers on p->list
  */
-static int __kprobes aggr_pre_handler(struct kprobe *p, struct pt_regs *regs)
+static int aggr_pre_handler(struct kprobe *p, struct pt_regs *regs)
 {
        struct kprobe *kp;
 
@@ -1022,9 +1011,10 @@ static int __kprobes aggr_pre_handler(struct kprobe *p, struct pt_regs *regs)
        }
        return 0;
 }
+NOKPROBE_SYMBOL(aggr_pre_handler);
 
-static void __kprobes aggr_post_handler(struct kprobe *p, struct pt_regs *regs,
-                                       unsigned long flags)
+static void aggr_post_handler(struct kprobe *p, struct pt_regs *regs,
+                             unsigned long flags)
 {
        struct kprobe *kp;
 
@@ -1036,9 +1026,10 @@ static void __kprobes aggr_post_handler(struct kprobe *p, struct pt_regs *regs,
                }
        }
 }
+NOKPROBE_SYMBOL(aggr_post_handler);
 
-static int __kprobes aggr_fault_handler(struct kprobe *p, struct pt_regs *regs,
-                                       int trapnr)
+static int aggr_fault_handler(struct kprobe *p, struct pt_regs *regs,
+                             int trapnr)
 {
        struct kprobe *cur = __this_cpu_read(kprobe_instance);
 
@@ -1052,8 +1043,9 @@ static int __kprobes aggr_fault_handler(struct kprobe *p, struct pt_regs *regs,
        }
        return 0;
 }
+NOKPROBE_SYMBOL(aggr_fault_handler);
 
-static int __kprobes aggr_break_handler(struct kprobe *p, struct pt_regs *regs)
+static int aggr_break_handler(struct kprobe *p, struct pt_regs *regs)
 {
        struct kprobe *cur = __this_cpu_read(kprobe_instance);
        int ret = 0;
@@ -1065,9 +1057,10 @@ static int __kprobes aggr_break_handler(struct kprobe *p, struct pt_regs *regs)
        reset_kprobe_instance();
        return ret;
 }
+NOKPROBE_SYMBOL(aggr_break_handler);
 
 /* Walks the list and increments nmissed count for multiprobe case */
-void __kprobes kprobes_inc_nmissed_count(struct kprobe *p)
+void kprobes_inc_nmissed_count(struct kprobe *p)
 {
        struct kprobe *kp;
        if (!kprobe_aggrprobe(p)) {
@@ -1078,9 +1071,10 @@ void __kprobes kprobes_inc_nmissed_count(struct kprobe *p)
        }
        return;
 }
+NOKPROBE_SYMBOL(kprobes_inc_nmissed_count);
 
-void __kprobes recycle_rp_inst(struct kretprobe_instance *ri,
-                               struct hlist_head *head)
+void recycle_rp_inst(struct kretprobe_instance *ri,
+                    struct hlist_head *head)
 {
        struct kretprobe *rp = ri->rp;
 
@@ -1095,8 +1089,9 @@ void __kprobes recycle_rp_inst(struct kretprobe_instance *ri,
                /* Unregistering */
                hlist_add_head(&ri->hlist, head);
 }
+NOKPROBE_SYMBOL(recycle_rp_inst);
 
-void __kprobes kretprobe_hash_lock(struct task_struct *tsk,
+void kretprobe_hash_lock(struct task_struct *tsk,
                         struct hlist_head **head, unsigned long *flags)
 __acquires(hlist_lock)
 {
@@ -1107,17 +1102,19 @@ __acquires(hlist_lock)
        hlist_lock = kretprobe_table_lock_ptr(hash);
        raw_spin_lock_irqsave(hlist_lock, *flags);
 }
+NOKPROBE_SYMBOL(kretprobe_hash_lock);
 
-static void __kprobes kretprobe_table_lock(unsigned long hash,
-       unsigned long *flags)
+static void kretprobe_table_lock(unsigned long hash,
+                                unsigned long *flags)
 __acquires(hlist_lock)
 {
        raw_spinlock_t *hlist_lock = kretprobe_table_lock_ptr(hash);
        raw_spin_lock_irqsave(hlist_lock, *flags);
 }
+NOKPROBE_SYMBOL(kretprobe_table_lock);
 
-void __kprobes kretprobe_hash_unlock(struct task_struct *tsk,
-       unsigned long *flags)
+void kretprobe_hash_unlock(struct task_struct *tsk,
+                          unsigned long *flags)
 __releases(hlist_lock)
 {
        unsigned long hash = hash_ptr(tsk, KPROBE_HASH_BITS);
@@ -1126,14 +1123,16 @@ __releases(hlist_lock)
        hlist_lock = kretprobe_table_lock_ptr(hash);
        raw_spin_unlock_irqrestore(hlist_lock, *flags);
 }
+NOKPROBE_SYMBOL(kretprobe_hash_unlock);
 
-static void __kprobes kretprobe_table_unlock(unsigned long hash,
-       unsigned long *flags)
+static void kretprobe_table_unlock(unsigned long hash,
+                                  unsigned long *flags)
 __releases(hlist_lock)
 {
        raw_spinlock_t *hlist_lock = kretprobe_table_lock_ptr(hash);
        raw_spin_unlock_irqrestore(hlist_lock, *flags);
 }
+NOKPROBE_SYMBOL(kretprobe_table_unlock);
 
 /*
  * This function is called from finish_task_switch when task tk becomes dead,
@@ -1141,7 +1140,7 @@ __releases(hlist_lock)
  * with this task. These left over instances represent probed functions
  * that have been called but will never return.
  */
-void __kprobes kprobe_flush_task(struct task_struct *tk)
+void kprobe_flush_task(struct task_struct *tk)
 {
        struct kretprobe_instance *ri;
        struct hlist_head *head, empty_rp;
@@ -1166,6 +1165,7 @@ void __kprobes kprobe_flush_task(struct task_struct *tk)
                kfree(ri);
        }
 }
+NOKPROBE_SYMBOL(kprobe_flush_task);
 
 static inline void free_rp_inst(struct kretprobe *rp)
 {
@@ -1178,7 +1178,7 @@ static inline void free_rp_inst(struct kretprobe *rp)
        }
 }
 
-static void __kprobes cleanup_rp_inst(struct kretprobe *rp)
+static void cleanup_rp_inst(struct kretprobe *rp)
 {
        unsigned long flags, hash;
        struct kretprobe_instance *ri;
@@ -1197,12 +1197,13 @@ static void __kprobes cleanup_rp_inst(struct kretprobe *rp)
        }
        free_rp_inst(rp);
 }
+NOKPROBE_SYMBOL(cleanup_rp_inst);
 
 /*
 * Add the new probe to ap->list. Fail if this is the
 * second jprobe at the address - two jprobes can't coexist
 */
-static int __kprobes add_new_kprobe(struct kprobe *ap, struct kprobe *p)
+static int add_new_kprobe(struct kprobe *ap, struct kprobe *p)
 {
        BUG_ON(kprobe_gone(ap) || kprobe_gone(p));
 
@@ -1226,7 +1227,7 @@ static int __kprobes add_new_kprobe(struct kprobe *ap, struct kprobe *p)
  * Fill in the required fields of the "manager kprobe". Replace the
  * earlier kprobe in the hlist with the manager kprobe
  */
-static void __kprobes init_aggr_kprobe(struct kprobe *ap, struct kprobe *p)
+static void init_aggr_kprobe(struct kprobe *ap, struct kprobe *p)
 {
        /* Copy p's insn slot to ap */
        copy_kprobe(p, ap);
@@ -1252,8 +1253,7 @@ static void __kprobes init_aggr_kprobe(struct kprobe *ap, struct kprobe *p)
  * This is the second or subsequent kprobe at the address - handle
  * the intricacies
  */
-static int __kprobes register_aggr_kprobe(struct kprobe *orig_p,
-                                         struct kprobe *p)
+static int register_aggr_kprobe(struct kprobe *orig_p, struct kprobe *p)
 {
        int ret = 0;
        struct kprobe *ap = orig_p;
@@ -1324,25 +1324,29 @@ out:
        return ret;
 }
 
-static int __kprobes in_kprobes_functions(unsigned long addr)
+bool __weak arch_within_kprobe_blacklist(unsigned long addr)
 {
-       struct kprobe_blackpoint *kb;
+       /* The __kprobes marked functions and entry code must not be probed */
+       return addr >= (unsigned long)__kprobes_text_start &&
+              addr < (unsigned long)__kprobes_text_end;
+}
 
-       if (addr >= (unsigned long)__kprobes_text_start &&
-           addr < (unsigned long)__kprobes_text_end)
-               return -EINVAL;
+static bool within_kprobe_blacklist(unsigned long addr)
+{
+       struct kprobe_blacklist_entry *ent;
+
+       if (arch_within_kprobe_blacklist(addr))
+               return true;
        /*
         * If there exists a kprobe_blacklist, verify and
         * fail any probe registration in the prohibited area
         */
-       for (kb = kprobe_blacklist; kb->name != NULL; kb++) {
-               if (kb->start_addr) {
-                       if (addr >= kb->start_addr &&
-                           addr < (kb->start_addr + kb->range))
-                               return -EINVAL;
-               }
+       list_for_each_entry(ent, &kprobe_blacklist, list) {
+               if (addr >= ent->start_addr && addr < ent->end_addr)
+                       return true;
        }
-       return 0;
+
+       return false;
 }
 
 /*
@@ -1351,7 +1355,7 @@ static int __kprobes in_kprobes_functions(unsigned long addr)
  * This returns encoded errors if it fails to look up symbol or invalid
  * combination of parameters.
  */
-static kprobe_opcode_t __kprobes *kprobe_addr(struct kprobe *p)
+static kprobe_opcode_t *kprobe_addr(struct kprobe *p)
 {
        kprobe_opcode_t *addr = p->addr;
 
@@ -1374,7 +1378,7 @@ invalid:
 }
 
 /* Check passed kprobe is valid and return kprobe in kprobe_table. */
-static struct kprobe * __kprobes __get_valid_kprobe(struct kprobe *p)
+static struct kprobe *__get_valid_kprobe(struct kprobe *p)
 {
        struct kprobe *ap, *list_p;
 
@@ -1406,8 +1410,8 @@ static inline int check_kprobe_rereg(struct kprobe *p)
        return ret;
 }
 
-static __kprobes int check_kprobe_address_safe(struct kprobe *p,
-                                              struct module **probed_mod)
+static int check_kprobe_address_safe(struct kprobe *p,
+                                    struct module **probed_mod)
 {
        int ret = 0;
        unsigned long ftrace_addr;
@@ -1433,7 +1437,7 @@ static __kprobes int check_kprobe_address_safe(struct kprobe *p,
 
        /* Ensure it is not in reserved area nor out of text */
        if (!kernel_text_address((unsigned long) p->addr) ||
-           in_kprobes_functions((unsigned long) p->addr) ||
+           within_kprobe_blacklist((unsigned long) p->addr) ||
            jump_label_text_reserved(p->addr, p->addr)) {
                ret = -EINVAL;
                goto out;
@@ -1469,7 +1473,7 @@ out:
        return ret;
 }
 
-int __kprobes register_kprobe(struct kprobe *p)
+int register_kprobe(struct kprobe *p)
 {
        int ret;
        struct kprobe *old_p;
@@ -1531,7 +1535,7 @@ out:
 EXPORT_SYMBOL_GPL(register_kprobe);
 
 /* Check if all probes on the aggrprobe are disabled */
-static int __kprobes aggr_kprobe_disabled(struct kprobe *ap)
+static int aggr_kprobe_disabled(struct kprobe *ap)
 {
        struct kprobe *kp;
 
@@ -1547,7 +1551,7 @@ static int __kprobes aggr_kprobe_disabled(struct kprobe *ap)
 }
 
 /* Disable one kprobe: Make sure called under kprobe_mutex is locked */
-static struct kprobe *__kprobes __disable_kprobe(struct kprobe *p)
+static struct kprobe *__disable_kprobe(struct kprobe *p)
 {
        struct kprobe *orig_p;
 
@@ -1574,7 +1578,7 @@ static struct kprobe *__kprobes __disable_kprobe(struct kprobe *p)
 /*
  * Unregister a kprobe without a scheduler synchronization.
  */
-static int __kprobes __unregister_kprobe_top(struct kprobe *p)
+static int __unregister_kprobe_top(struct kprobe *p)
 {
        struct kprobe *ap, *list_p;
 
@@ -1631,7 +1635,7 @@ disarmed:
        return 0;
 }
 
-static void __kprobes __unregister_kprobe_bottom(struct kprobe *p)
+static void __unregister_kprobe_bottom(struct kprobe *p)
 {
        struct kprobe *ap;
 
@@ -1647,7 +1651,7 @@ static void __kprobes __unregister_kprobe_bottom(struct kprobe *p)
        /* Otherwise, do nothing. */
 }
 
-int __kprobes register_kprobes(struct kprobe **kps, int num)
+int register_kprobes(struct kprobe **kps, int num)
 {
        int i, ret = 0;
 
@@ -1665,13 +1669,13 @@ int __kprobes register_kprobes(struct kprobe **kps, int num)
 }
 EXPORT_SYMBOL_GPL(register_kprobes);
 
-void __kprobes unregister_kprobe(struct kprobe *p)
+void unregister_kprobe(struct kprobe *p)
 {
        unregister_kprobes(&p, 1);
 }
 EXPORT_SYMBOL_GPL(unregister_kprobe);
 
-void __kprobes unregister_kprobes(struct kprobe **kps, int num)
+void unregister_kprobes(struct kprobe **kps, int num)
 {
        int i;
 
@@ -1700,7 +1704,7 @@ unsigned long __weak arch_deref_entry_point(void *entry)
        return (unsigned long)entry;
 }
 
-int __kprobes register_jprobes(struct jprobe **jps, int num)
+int register_jprobes(struct jprobe **jps, int num)
 {
        struct jprobe *jp;
        int ret = 0, i;
@@ -1731,19 +1735,19 @@ int __kprobes register_jprobes(struct jprobe **jps, int num)
 }
 EXPORT_SYMBOL_GPL(register_jprobes);
 
-int __kprobes register_jprobe(struct jprobe *jp)
+int register_jprobe(struct jprobe *jp)
 {
        return register_jprobes(&jp, 1);
 }
 EXPORT_SYMBOL_GPL(register_jprobe);
 
-void __kprobes unregister_jprobe(struct jprobe *jp)
+void unregister_jprobe(struct jprobe *jp)
 {
        unregister_jprobes(&jp, 1);
 }
 EXPORT_SYMBOL_GPL(unregister_jprobe);
 
-void __kprobes unregister_jprobes(struct jprobe **jps, int num)
+void unregister_jprobes(struct jprobe **jps, int num)
 {
        int i;
 
@@ -1768,8 +1772,7 @@ EXPORT_SYMBOL_GPL(unregister_jprobes);
  * This kprobe pre_handler is registered with every kretprobe. When probe
  * hits it will set up the return probe.
  */
-static int __kprobes pre_handler_kretprobe(struct kprobe *p,
-                                          struct pt_regs *regs)
+static int pre_handler_kretprobe(struct kprobe *p, struct pt_regs *regs)
 {
        struct kretprobe *rp = container_of(p, struct kretprobe, kp);
        unsigned long hash, flags = 0;
@@ -1807,8 +1810,9 @@ static int __kprobes pre_handler_kretprobe(struct kprobe *p,
        }
        return 0;
 }
+NOKPROBE_SYMBOL(pre_handler_kretprobe);
 
-int __kprobes register_kretprobe(struct kretprobe *rp)
+int register_kretprobe(struct kretprobe *rp)
 {
        int ret = 0;
        struct kretprobe_instance *inst;
@@ -1861,7 +1865,7 @@ int __kprobes register_kretprobe(struct kretprobe *rp)
 }
 EXPORT_SYMBOL_GPL(register_kretprobe);
 
-int __kprobes register_kretprobes(struct kretprobe **rps, int num)
+int register_kretprobes(struct kretprobe **rps, int num)
 {
        int ret = 0, i;
 
@@ -1879,13 +1883,13 @@ int __kprobes register_kretprobes(struct kretprobe **rps, int num)
 }
 EXPORT_SYMBOL_GPL(register_kretprobes);
 
-void __kprobes unregister_kretprobe(struct kretprobe *rp)
+void unregister_kretprobe(struct kretprobe *rp)
 {
        unregister_kretprobes(&rp, 1);
 }
 EXPORT_SYMBOL_GPL(unregister_kretprobe);
 
-void __kprobes unregister_kretprobes(struct kretprobe **rps, int num)
+void unregister_kretprobes(struct kretprobe **rps, int num)
 {
        int i;
 
@@ -1908,38 +1912,38 @@ void __kprobes unregister_kretprobes(struct kretprobe **rps, int num)
 EXPORT_SYMBOL_GPL(unregister_kretprobes);
 
 #else /* CONFIG_KRETPROBES */
-int __kprobes register_kretprobe(struct kretprobe *rp)
+int register_kretprobe(struct kretprobe *rp)
 {
        return -ENOSYS;
 }
 EXPORT_SYMBOL_GPL(register_kretprobe);
 
-int __kprobes register_kretprobes(struct kretprobe **rps, int num)
+int register_kretprobes(struct kretprobe **rps, int num)
 {
        return -ENOSYS;
 }
 EXPORT_SYMBOL_GPL(register_kretprobes);
 
-void __kprobes unregister_kretprobe(struct kretprobe *rp)
+void unregister_kretprobe(struct kretprobe *rp)
 {
 }
 EXPORT_SYMBOL_GPL(unregister_kretprobe);
 
-void __kprobes unregister_kretprobes(struct kretprobe **rps, int num)
+void unregister_kretprobes(struct kretprobe **rps, int num)
 {
 }
 EXPORT_SYMBOL_GPL(unregister_kretprobes);
 
-static int __kprobes pre_handler_kretprobe(struct kprobe *p,
-                                          struct pt_regs *regs)
+static int pre_handler_kretprobe(struct kprobe *p, struct pt_regs *regs)
 {
        return 0;
 }
+NOKPROBE_SYMBOL(pre_handler_kretprobe);
 
 #endif /* CONFIG_KRETPROBES */
 
 /* Set the kprobe gone and remove its instruction buffer. */
-static void __kprobes kill_kprobe(struct kprobe *p)
+static void kill_kprobe(struct kprobe *p)
 {
        struct kprobe *kp;
 
@@ -1963,7 +1967,7 @@ static void __kprobes kill_kprobe(struct kprobe *p)
 }
 
 /* Disable one kprobe */
-int __kprobes disable_kprobe(struct kprobe *kp)
+int disable_kprobe(struct kprobe *kp)
 {
        int ret = 0;
 
@@ -1979,7 +1983,7 @@ int __kprobes disable_kprobe(struct kprobe *kp)
 EXPORT_SYMBOL_GPL(disable_kprobe);
 
 /* Enable one kprobe */
-int __kprobes enable_kprobe(struct kprobe *kp)
+int enable_kprobe(struct kprobe *kp)
 {
        int ret = 0;
        struct kprobe *p;
@@ -2012,16 +2016,49 @@ out:
 }
 EXPORT_SYMBOL_GPL(enable_kprobe);
 
-void __kprobes dump_kprobe(struct kprobe *kp)
+void dump_kprobe(struct kprobe *kp)
 {
        printk(KERN_WARNING "Dumping kprobe:\n");
        printk(KERN_WARNING "Name: %s\nAddress: %p\nOffset: %x\n",
               kp->symbol_name, kp->addr, kp->offset);
 }
+NOKPROBE_SYMBOL(dump_kprobe);
+
+/*
+ * Lookup and populate the kprobe_blacklist.
+ *
+ * Unlike the kretprobe blacklist, we'll need to determine
+ * the range of addresses that belong to the said functions,
+ * since a kprobe need not necessarily be at the beginning
+ * of a function.
+ */
+static int __init populate_kprobe_blacklist(unsigned long *start,
+                                            unsigned long *end)
+{
+       unsigned long *iter;
+       struct kprobe_blacklist_entry *ent;
+       unsigned long offset = 0, size = 0;
+
+       for (iter = start; iter < end; iter++) {
+               if (!kallsyms_lookup_size_offset(*iter, &size, &offset)) {
+                       pr_err("Failed to find blacklist %p\n", (void *)*iter);
+                       continue;
+               }
+
+               ent = kmalloc(sizeof(*ent), GFP_KERNEL);
+               if (!ent)
+                       return -ENOMEM;
+               ent->start_addr = *iter;
+               ent->end_addr = *iter + size;
+               INIT_LIST_HEAD(&ent->list);
+               list_add_tail(&ent->list, &kprobe_blacklist);
+       }
+       return 0;
+}
 
 /* Module notifier call back, checking kprobes on the module */
-static int __kprobes kprobes_module_callback(struct notifier_block *nb,
-                                            unsigned long val, void *data)
+static int kprobes_module_callback(struct notifier_block *nb,
+                                  unsigned long val, void *data)
 {
        struct module *mod = data;
        struct hlist_head *head;
@@ -2062,14 +2099,13 @@ static struct notifier_block kprobe_module_nb = {
        .priority = 0
 };
 
+/* Markers of _kprobe_blacklist section */
+extern unsigned long __start_kprobe_blacklist[];
+extern unsigned long __stop_kprobe_blacklist[];
+
 static int __init init_kprobes(void)
 {
        int i, err = 0;
-       unsigned long offset = 0, size = 0;
-       char *modname, namebuf[KSYM_NAME_LEN];
-       const char *symbol_name;
-       void *addr;
-       struct kprobe_blackpoint *kb;
 
        /* FIXME allocate the probe table, currently defined statically */
        /* initialize all list heads */
@@ -2079,26 +2115,11 @@ static int __init init_kprobes(void)
                raw_spin_lock_init(&(kretprobe_table_locks[i].lock));
        }
 
-       /*
-        * Lookup and populate the kprobe_blacklist.
-        *
-        * Unlike the kretprobe blacklist, we'll need to determine
-        * the range of addresses that belong to the said functions,
-        * since a kprobe need not necessarily be at the beginning
-        * of a function.
-        */
-       for (kb = kprobe_blacklist; kb->name != NULL; kb++) {
-               kprobe_lookup_name(kb->name, addr);
-               if (!addr)
-                       continue;
-
-               kb->start_addr = (unsigned long)addr;
-               symbol_name = kallsyms_lookup(kb->start_addr,
-                               &size, &offset, &modname, namebuf);
-               if (!symbol_name)
-                       kb->range = 0;
-               else
-                       kb->range = size;
+       err = populate_kprobe_blacklist(__start_kprobe_blacklist,
+                                       __stop_kprobe_blacklist);
+       if (err) {
+               pr_err("kprobes: failed to populate blacklist: %d\n", err);
+               pr_err("Please take care of using kprobes.\n");
        }
 
        if (kretprobe_blacklist_size) {
@@ -2138,7 +2159,7 @@ static int __init init_kprobes(void)
 }
 
 #ifdef CONFIG_DEBUG_FS
-static void __kprobes report_probe(struct seq_file *pi, struct kprobe *p,
+static void report_probe(struct seq_file *pi, struct kprobe *p,
                const char *sym, int offset, char *modname, struct kprobe *pp)
 {
        char *kprobe_type;
@@ -2167,12 +2188,12 @@ static void __kprobes report_probe(struct seq_file *pi, struct kprobe *p,
                (kprobe_ftrace(pp) ? "[FTRACE]" : ""));
 }
 
-static void __kprobes *kprobe_seq_start(struct seq_file *f, loff_t *pos)
+static void *kprobe_seq_start(struct seq_file *f, loff_t *pos)
 {
        return (*pos < KPROBE_TABLE_SIZE) ? pos : NULL;
 }
 
-static void __kprobes *kprobe_seq_next(struct seq_file *f, void *v, loff_t *pos)
+static void *kprobe_seq_next(struct seq_file *f, void *v, loff_t *pos)
 {
        (*pos)++;
        if (*pos >= KPROBE_TABLE_SIZE)
@@ -2180,12 +2201,12 @@ static void __kprobes *kprobe_seq_next(struct seq_file *f, void *v, loff_t *pos)
        return pos;
 }
 
-static void __kprobes kprobe_seq_stop(struct seq_file *f, void *v)
+static void kprobe_seq_stop(struct seq_file *f, void *v)
 {
        /* Nothing to do */
 }
 
-static int __kprobes show_kprobe_addr(struct seq_file *pi, void *v)
+static int show_kprobe_addr(struct seq_file *pi, void *v)
 {
        struct hlist_head *head;
        struct kprobe *p, *kp;
@@ -2216,7 +2237,7 @@ static const struct seq_operations kprobes_seq_ops = {
        .show  = show_kprobe_addr
 };
 
-static int __kprobes kprobes_open(struct inode *inode, struct file *filp)
+static int kprobes_open(struct inode *inode, struct file *filp)
 {
        return seq_open(filp, &kprobes_seq_ops);
 }
@@ -2228,7 +2249,47 @@ static const struct file_operations debugfs_kprobes_operations = {
        .release        = seq_release,
 };
 
-static void __kprobes arm_all_kprobes(void)
+/* kprobes/blacklist -- shows which functions can not be probed */
+static void *kprobe_blacklist_seq_start(struct seq_file *m, loff_t *pos)
+{
+       return seq_list_start(&kprobe_blacklist, *pos);
+}
+
+static void *kprobe_blacklist_seq_next(struct seq_file *m, void *v, loff_t *pos)
+{
+       return seq_list_next(v, &kprobe_blacklist, pos);
+}
+
+static int kprobe_blacklist_seq_show(struct seq_file *m, void *v)
+{
+       struct kprobe_blacklist_entry *ent =
+               list_entry(v, struct kprobe_blacklist_entry, list);
+
+       seq_printf(m, "0x%p-0x%p\t%ps\n", (void *)ent->start_addr,
+                  (void *)ent->end_addr, (void *)ent->start_addr);
+       return 0;
+}
+
+static const struct seq_operations kprobe_blacklist_seq_ops = {
+       .start = kprobe_blacklist_seq_start,
+       .next  = kprobe_blacklist_seq_next,
+       .stop  = kprobe_seq_stop,       /* Reuse void function */
+       .show  = kprobe_blacklist_seq_show,
+};
+
+static int kprobe_blacklist_open(struct inode *inode, struct file *filp)
+{
+       return seq_open(filp, &kprobe_blacklist_seq_ops);
+}
+
+static const struct file_operations debugfs_kprobe_blacklist_ops = {
+       .open           = kprobe_blacklist_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release,
+};
+
+static void arm_all_kprobes(void)
 {
        struct hlist_head *head;
        struct kprobe *p;
@@ -2256,7 +2317,7 @@ already_enabled:
        return;
 }
 
-static void __kprobes disarm_all_kprobes(void)
+static void disarm_all_kprobes(void)
 {
        struct hlist_head *head;
        struct kprobe *p;
@@ -2340,7 +2401,7 @@ static const struct file_operations fops_kp = {
        .llseek =       default_llseek,
 };
 
-static int __kprobes debugfs_kprobe_init(void)
+static int __init debugfs_kprobe_init(void)
 {
        struct dentry *dir, *file;
        unsigned int value = 1;
@@ -2351,19 +2412,24 @@ static int __kprobes debugfs_kprobe_init(void)
 
        file = debugfs_create_file("list", 0444, dir, NULL,
                                &debugfs_kprobes_operations);
-       if (!file) {
-               debugfs_remove(dir);
-               return -ENOMEM;
-       }
+       if (!file)
+               goto error;
 
        file = debugfs_create_file("enabled", 0600, dir,
                                        &value, &fops_kp);
-       if (!file) {
-               debugfs_remove(dir);
-               return -ENOMEM;
-       }
+       if (!file)
+               goto error;
+
+       file = debugfs_create_file("blacklist", 0444, dir, NULL,
+                               &debugfs_kprobe_blacklist_ops);
+       if (!file)
+               goto error;
 
        return 0;
+
+error:
+       debugfs_remove(dir);
+       return -ENOMEM;
 }
 
 late_initcall(debugfs_kprobe_init);
index b8bdcd4785b767d5fc1ee732d778b07ee74cf102..8541bfdfd232bb4213629f265cbb68a6bfb50c72 100644 (file)
@@ -24,4 +24,5 @@ obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock_debug.o
 obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
 obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem-xadd.o
 obj-$(CONFIG_PERCPU_RWSEM) += percpu-rwsem.o
+obj-$(CONFIG_QUEUE_RWLOCK) += qrwlock.o
 obj-$(CONFIG_LOCK_TORTURE_TEST) += locktorture.o
diff --git a/kernel/locking/qrwlock.c b/kernel/locking/qrwlock.c
new file mode 100644 (file)
index 0000000..fb5b8ac
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Queue read/write lock
+ *
+ * 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.
+ *
+ * (C) Copyright 2013-2014 Hewlett-Packard Development Company, L.P.
+ *
+ * Authors: Waiman Long <waiman.long@hp.com>
+ */
+#include <linux/smp.h>
+#include <linux/bug.h>
+#include <linux/cpumask.h>
+#include <linux/percpu.h>
+#include <linux/hardirq.h>
+#include <linux/mutex.h>
+#include <asm/qrwlock.h>
+
+/**
+ * rspin_until_writer_unlock - inc reader count & spin until writer is gone
+ * @lock  : Pointer to queue rwlock structure
+ * @writer: Current queue rwlock writer status byte
+ *
+ * In interrupt context or at the head of the queue, the reader will just
+ * increment the reader count & wait until the writer releases the lock.
+ */
+static __always_inline void
+rspin_until_writer_unlock(struct qrwlock *lock, u32 cnts)
+{
+       while ((cnts & _QW_WMASK) == _QW_LOCKED) {
+               arch_mutex_cpu_relax();
+               cnts = smp_load_acquire((u32 *)&lock->cnts);
+       }
+}
+
+/**
+ * queue_read_lock_slowpath - acquire read lock of a queue rwlock
+ * @lock: Pointer to queue rwlock structure
+ */
+void queue_read_lock_slowpath(struct qrwlock *lock)
+{
+       u32 cnts;
+
+       /*
+        * Readers come here when they cannot get the lock without waiting
+        */
+       if (unlikely(in_interrupt())) {
+               /*
+                * Readers in interrupt context will spin until the lock is
+                * available without waiting in the queue.
+                */
+               cnts = smp_load_acquire((u32 *)&lock->cnts);
+               rspin_until_writer_unlock(lock, cnts);
+               return;
+       }
+       atomic_sub(_QR_BIAS, &lock->cnts);
+
+       /*
+        * Put the reader into the wait queue
+        */
+       arch_spin_lock(&lock->lock);
+
+       /*
+        * At the head of the wait queue now, wait until the writer state
+        * goes to 0 and then try to increment the reader count and get
+        * the lock. It is possible that an incoming writer may steal the
+        * lock in the interim, so it is necessary to check the writer byte
+        * to make sure that the write lock isn't taken.
+        */
+       while (atomic_read(&lock->cnts) & _QW_WMASK)
+               arch_mutex_cpu_relax();
+
+       cnts = atomic_add_return(_QR_BIAS, &lock->cnts) - _QR_BIAS;
+       rspin_until_writer_unlock(lock, cnts);
+
+       /*
+        * Signal the next one in queue to become queue head
+        */
+       arch_spin_unlock(&lock->lock);
+}
+EXPORT_SYMBOL(queue_read_lock_slowpath);
+
+/**
+ * queue_write_lock_slowpath - acquire write lock of a queue rwlock
+ * @lock : Pointer to queue rwlock structure
+ */
+void queue_write_lock_slowpath(struct qrwlock *lock)
+{
+       u32 cnts;
+
+       /* Put the writer into the wait queue */
+       arch_spin_lock(&lock->lock);
+
+       /* Try to acquire the lock directly if no reader is present */
+       if (!atomic_read(&lock->cnts) &&
+           (atomic_cmpxchg(&lock->cnts, 0, _QW_LOCKED) == 0))
+               goto unlock;
+
+       /*
+        * Set the waiting flag to notify readers that a writer is pending,
+        * or wait for a previous writer to go away.
+        */
+       for (;;) {
+               cnts = atomic_read(&lock->cnts);
+               if (!(cnts & _QW_WMASK) &&
+                   (atomic_cmpxchg(&lock->cnts, cnts,
+                                   cnts | _QW_WAITING) == cnts))
+                       break;
+
+               arch_mutex_cpu_relax();
+       }
+
+       /* When no more readers, set the locked flag */
+       for (;;) {
+               cnts = atomic_read(&lock->cnts);
+               if ((cnts == _QW_WAITING) &&
+                   (atomic_cmpxchg(&lock->cnts, _QW_WAITING,
+                                   _QW_LOCKED) == _QW_WAITING))
+                       break;
+
+               arch_mutex_cpu_relax();
+       }
+unlock:
+       arch_spin_unlock(&lock->lock);
+}
+EXPORT_SYMBOL(queue_write_lock_slowpath);
index b4219ff87b8c6dc95ae85bb2a4f38118681c0df6..dacc32142fccaec5222ba82c01801ed7768a819f 100644 (file)
@@ -5,11 +5,17 @@
  *
  * Writer lock-stealing by Alex Shi <alex.shi@intel.com>
  * and Michel Lespinasse <walken@google.com>
+ *
+ * Optimistic spinning by Tim Chen <tim.c.chen@intel.com>
+ * and Davidlohr Bueso <davidlohr@hp.com>. Based on mutexes.
  */
 #include <linux/rwsem.h>
 #include <linux/sched.h>
 #include <linux/init.h>
 #include <linux/export.h>
+#include <linux/sched/rt.h>
+
+#include "mcs_spinlock.h"
 
 /*
  * Guide to the rw_semaphore's count field for common values.
@@ -76,6 +82,10 @@ void __init_rwsem(struct rw_semaphore *sem, const char *name,
        sem->count = RWSEM_UNLOCKED_VALUE;
        raw_spin_lock_init(&sem->wait_lock);
        INIT_LIST_HEAD(&sem->wait_list);
+#ifdef CONFIG_SMP
+       sem->owner = NULL;
+       sem->osq = NULL;
+#endif
 }
 
 EXPORT_SYMBOL(__init_rwsem);
@@ -190,7 +200,7 @@ __rwsem_do_wake(struct rw_semaphore *sem, enum rwsem_wake_type wake_type)
 }
 
 /*
- * wait for the read lock to be granted
+ * Wait for the read lock to be granted
  */
 __visible
 struct rw_semaphore __sched *rwsem_down_read_failed(struct rw_semaphore *sem)
@@ -237,64 +247,221 @@ struct rw_semaphore __sched *rwsem_down_read_failed(struct rw_semaphore *sem)
        return sem;
 }
 
+static inline bool rwsem_try_write_lock(long count, struct rw_semaphore *sem)
+{
+       if (!(count & RWSEM_ACTIVE_MASK)) {
+               /* try acquiring the write lock */
+               if (sem->count == RWSEM_WAITING_BIAS &&
+                   cmpxchg(&sem->count, RWSEM_WAITING_BIAS,
+                           RWSEM_ACTIVE_WRITE_BIAS) == RWSEM_WAITING_BIAS) {
+                       if (!list_is_singular(&sem->wait_list))
+                               rwsem_atomic_update(RWSEM_WAITING_BIAS, sem);
+                       return true;
+               }
+       }
+       return false;
+}
+
+#ifdef CONFIG_SMP
 /*
- * wait until we successfully acquire the write lock
+ * Try to acquire write lock before the writer has been put on wait queue.
+ */
+static inline bool rwsem_try_write_lock_unqueued(struct rw_semaphore *sem)
+{
+       long old, count = ACCESS_ONCE(sem->count);
+
+       while (true) {
+               if (!(count == 0 || count == RWSEM_WAITING_BIAS))
+                       return false;
+
+               old = cmpxchg(&sem->count, count, count + RWSEM_ACTIVE_WRITE_BIAS);
+               if (old == count)
+                       return true;
+
+               count = old;
+       }
+}
+
+static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem)
+{
+       struct task_struct *owner;
+       bool on_cpu = true;
+
+       if (need_resched())
+               return 0;
+
+       rcu_read_lock();
+       owner = ACCESS_ONCE(sem->owner);
+       if (owner)
+               on_cpu = owner->on_cpu;
+       rcu_read_unlock();
+
+       /*
+        * If sem->owner is not set, the rwsem owner may have
+        * just acquired it and not set the owner yet or the rwsem
+        * has been released.
+        */
+       return on_cpu;
+}
+
+static inline bool owner_running(struct rw_semaphore *sem,
+                                struct task_struct *owner)
+{
+       if (sem->owner != owner)
+               return false;
+
+       /*
+        * Ensure we emit the owner->on_cpu, dereference _after_ checking
+        * sem->owner still matches owner, if that fails, owner might
+        * point to free()d memory, if it still matches, the rcu_read_lock()
+        * ensures the memory stays valid.
+        */
+       barrier();
+
+       return owner->on_cpu;
+}
+
+static noinline
+bool rwsem_spin_on_owner(struct rw_semaphore *sem, struct task_struct *owner)
+{
+       rcu_read_lock();
+       while (owner_running(sem, owner)) {
+               if (need_resched())
+                       break;
+
+               arch_mutex_cpu_relax();
+       }
+       rcu_read_unlock();
+
+       /*
+        * We break out the loop above on need_resched() or when the
+        * owner changed, which is a sign for heavy contention. Return
+        * success only when sem->owner is NULL.
+        */
+       return sem->owner == NULL;
+}
+
+static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
+{
+       struct task_struct *owner;
+       bool taken = false;
+
+       preempt_disable();
+
+       /* sem->wait_lock should not be held when doing optimistic spinning */
+       if (!rwsem_can_spin_on_owner(sem))
+               goto done;
+
+       if (!osq_lock(&sem->osq))
+               goto done;
+
+       while (true) {
+               owner = ACCESS_ONCE(sem->owner);
+               if (owner && !rwsem_spin_on_owner(sem, owner))
+                       break;
+
+               /* wait_lock will be acquired if write_lock is obtained */
+               if (rwsem_try_write_lock_unqueued(sem)) {
+                       taken = true;
+                       break;
+               }
+
+               /*
+                * When there's no owner, we might have preempted between the
+                * owner acquiring the lock and setting the owner field. If
+                * we're an RT task that will live-lock because we won't let
+                * the owner complete.
+                */
+               if (!owner && (need_resched() || rt_task(current)))
+                       break;
+
+               /*
+                * The cpu_relax() call is a compiler barrier which forces
+                * everything in this loop to be re-loaded. We don't need
+                * memory barriers as we'll eventually observe the right
+                * values at the cost of a few extra spins.
+                */
+               arch_mutex_cpu_relax();
+       }
+       osq_unlock(&sem->osq);
+done:
+       preempt_enable();
+       return taken;
+}
+
+#else
+static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
+{
+       return false;
+}
+#endif
+
+/*
+ * Wait until we successfully acquire the write lock
  */
 __visible
 struct rw_semaphore __sched *rwsem_down_write_failed(struct rw_semaphore *sem)
 {
-       long count, adjustment = -RWSEM_ACTIVE_WRITE_BIAS;
+       long count;
+       bool waiting = true; /* any queued threads before us */
        struct rwsem_waiter waiter;
-       struct task_struct *tsk = current;
 
-       /* set up my own style of waitqueue */
-       waiter.task = tsk;
+       /* undo write bias from down_write operation, stop active locking */
+       count = rwsem_atomic_update(-RWSEM_ACTIVE_WRITE_BIAS, sem);
+
+       /* do optimistic spinning and steal lock if possible */
+       if (rwsem_optimistic_spin(sem))
+               return sem;
+
+       /*
+        * Optimistic spinning failed, proceed to the slowpath
+        * and block until we can acquire the sem.
+        */
+       waiter.task = current;
        waiter.type = RWSEM_WAITING_FOR_WRITE;
 
        raw_spin_lock_irq(&sem->wait_lock);
+
+       /* account for this before adding a new element to the list */
        if (list_empty(&sem->wait_list))
-               adjustment += RWSEM_WAITING_BIAS;
+               waiting = false;
+
        list_add_tail(&waiter.list, &sem->wait_list);
 
        /* we're now waiting on the lock, but no longer actively locking */
-       count = rwsem_atomic_update(adjustment, sem);
+       if (waiting) {
+               count = ACCESS_ONCE(sem->count);
 
-       /* If there were already threads queued before us and there are no
-        * active writers, the lock must be read owned; so we try to wake
-        * any read locks that were queued ahead of us. */
-       if (count > RWSEM_WAITING_BIAS &&
-           adjustment == -RWSEM_ACTIVE_WRITE_BIAS)
-               sem = __rwsem_do_wake(sem, RWSEM_WAKE_READERS);
+               /*
+                * If there were already threads queued before us and there are
+                * no active writers, the lock must be read owned; so we try to
+                * wake any read locks that were queued ahead of us.
+                */
+               if (count > RWSEM_WAITING_BIAS)
+                       sem = __rwsem_do_wake(sem, RWSEM_WAKE_READERS);
+
+       } else
+               count = rwsem_atomic_update(RWSEM_WAITING_BIAS, sem);
 
        /* wait until we successfully acquire the lock */
-       set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+       set_current_state(TASK_UNINTERRUPTIBLE);
        while (true) {
-               if (!(count & RWSEM_ACTIVE_MASK)) {
-                       /* Try acquiring the write lock. */
-                       count = RWSEM_ACTIVE_WRITE_BIAS;
-                       if (!list_is_singular(&sem->wait_list))
-                               count += RWSEM_WAITING_BIAS;
-
-                       if (sem->count == RWSEM_WAITING_BIAS &&
-                           cmpxchg(&sem->count, RWSEM_WAITING_BIAS, count) ==
-                                                       RWSEM_WAITING_BIAS)
-                               break;
-               }
-
+               if (rwsem_try_write_lock(count, sem))
+                       break;
                raw_spin_unlock_irq(&sem->wait_lock);
 
                /* Block until there are no active lockers. */
                do {
                        schedule();
-                       set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+                       set_current_state(TASK_UNINTERRUPTIBLE);
                } while ((count = sem->count) & RWSEM_ACTIVE_MASK);
 
                raw_spin_lock_irq(&sem->wait_lock);
        }
+       __set_current_state(TASK_RUNNING);
 
        list_del(&waiter.list);
        raw_spin_unlock_irq(&sem->wait_lock);
-       tsk->state = TASK_RUNNING;
 
        return sem;
 }
index cfff1435bdfb2f1e6d8a88d797c2a205f9145daf..42f806de49d421092a7bd077c8efb4df9546cb94 100644 (file)
 
 #include <linux/atomic.h>
 
+#if defined(CONFIG_SMP) && defined(CONFIG_RWSEM_XCHGADD_ALGORITHM)
+static inline void rwsem_set_owner(struct rw_semaphore *sem)
+{
+       sem->owner = current;
+}
+
+static inline void rwsem_clear_owner(struct rw_semaphore *sem)
+{
+       sem->owner = NULL;
+}
+
+#else
+static inline void rwsem_set_owner(struct rw_semaphore *sem)
+{
+}
+
+static inline void rwsem_clear_owner(struct rw_semaphore *sem)
+{
+}
+#endif
+
 /*
  * lock for reading
  */
@@ -48,6 +69,7 @@ void __sched down_write(struct rw_semaphore *sem)
        rwsem_acquire(&sem->dep_map, 0, 0, _RET_IP_);
 
        LOCK_CONTENDED(sem, __down_write_trylock, __down_write);
+       rwsem_set_owner(sem);
 }
 
 EXPORT_SYMBOL(down_write);
@@ -59,8 +81,11 @@ int down_write_trylock(struct rw_semaphore *sem)
 {
        int ret = __down_write_trylock(sem);
 
-       if (ret == 1)
+       if (ret == 1) {
                rwsem_acquire(&sem->dep_map, 0, 1, _RET_IP_);
+               rwsem_set_owner(sem);
+       }
+
        return ret;
 }
 
@@ -85,6 +110,7 @@ void up_write(struct rw_semaphore *sem)
 {
        rwsem_release(&sem->dep_map, 1, _RET_IP_);
 
+       rwsem_clear_owner(sem);
        __up_write(sem);
 }
 
@@ -99,6 +125,7 @@ void downgrade_write(struct rw_semaphore *sem)
         * lockdep: a downgraded write will live on as a write
         * dependency.
         */
+       rwsem_clear_owner(sem);
        __downgrade_write(sem);
 }
 
@@ -122,6 +149,7 @@ void _down_write_nest_lock(struct rw_semaphore *sem, struct lockdep_map *nest)
        rwsem_acquire_nest(&sem->dep_map, 0, 0, nest, _RET_IP_);
 
        LOCK_CONTENDED(sem, __down_write_trylock, __down_write);
+       rwsem_set_owner(sem);
 }
 
 EXPORT_SYMBOL(_down_write_nest_lock);
@@ -141,6 +169,7 @@ void down_write_nested(struct rw_semaphore *sem, int subclass)
        rwsem_acquire(&sem->dep_map, subclass, 0, _RET_IP_);
 
        LOCK_CONTENDED(sem, __down_write_trylock, __down_write);
+       rwsem_set_owner(sem);
 }
 
 EXPORT_SYMBOL(down_write_nested);
index 079c4615607d6ed266330a5416529bfcc37e4db0..81e727cf6df97d8477edad3e69f97c0f3242dae3 100644 (file)
@@ -3020,21 +3020,6 @@ static int do_init_module(struct module *mod)
         */
        current->flags &= ~PF_USED_ASYNC;
 
-       blocking_notifier_call_chain(&module_notify_list,
-                       MODULE_STATE_COMING, mod);
-
-       /* Set RO and NX regions for core */
-       set_section_ro_nx(mod->module_core,
-                               mod->core_text_size,
-                               mod->core_ro_size,
-                               mod->core_size);
-
-       /* Set RO and NX regions for init */
-       set_section_ro_nx(mod->module_init,
-                               mod->init_text_size,
-                               mod->init_ro_size,
-                               mod->init_size);
-
        do_mod_ctors(mod);
        /* Start the module */
        if (mod->init != NULL)
@@ -3165,9 +3150,26 @@ static int complete_formation(struct module *mod, struct load_info *info)
        /* This relies on module_mutex for list integrity. */
        module_bug_finalize(info->hdr, info->sechdrs, mod);
 
+       /* Set RO and NX regions for core */
+       set_section_ro_nx(mod->module_core,
+                               mod->core_text_size,
+                               mod->core_ro_size,
+                               mod->core_size);
+
+       /* Set RO and NX regions for init */
+       set_section_ro_nx(mod->module_init,
+                               mod->init_text_size,
+                               mod->init_ro_size,
+                               mod->init_size);
+
        /* Mark state as coming so strong_try_module_get() ignores us,
         * but kallsyms etc. can see us. */
        mod->state = MODULE_STATE_COMING;
+       mutex_unlock(&module_mutex);
+
+       blocking_notifier_call_chain(&module_notify_list,
+                                    MODULE_STATE_COMING, mod);
+       return 0;
 
 out:
        mutex_unlock(&module_mutex);
@@ -3190,6 +3192,7 @@ static int load_module(struct load_info *info, const char __user *uargs,
 {
        struct module *mod;
        long err;
+       char *after_dashes;
 
        err = module_sig_check(info);
        if (err)
@@ -3277,10 +3280,15 @@ static int load_module(struct load_info *info, const char __user *uargs,
                goto ddebug_cleanup;
 
        /* Module is ready to execute: parsing args may do that. */
-       err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp,
-                        -32768, 32767, unknown_module_param_cb);
-       if (err < 0)
+       after_dashes = parse_args(mod->name, mod->args, mod->kp, mod->num_kp,
+                                 -32768, 32767, unknown_module_param_cb);
+       if (IS_ERR(after_dashes)) {
+               err = PTR_ERR(after_dashes);
                goto bug_cleanup;
+       } else if (after_dashes) {
+               pr_warn("%s: parameters '%s' after `--' ignored\n",
+                      mod->name, after_dashes);
+       }
 
        /* Link in to syfs. */
        err = mod_sysfs_setup(mod, info, mod->kp, mod->num_kp);
index db4c8b08a50cef986f48f0e537f1b622d4e8b19a..4803da6eab62f182354707c10f48be35a8b54fb5 100644 (file)
@@ -71,9 +71,9 @@ static int notifier_chain_unregister(struct notifier_block **nl,
  *     @returns:       notifier_call_chain returns the value returned by the
  *                     last notifier function called.
  */
-static int __kprobes notifier_call_chain(struct notifier_block **nl,
-                                       unsigned long val, void *v,
-                                       int nr_to_call, int *nr_calls)
+static int notifier_call_chain(struct notifier_block **nl,
+                              unsigned long val, void *v,
+                              int nr_to_call, int *nr_calls)
 {
        int ret = NOTIFY_DONE;
        struct notifier_block *nb, *next_nb;
@@ -102,6 +102,7 @@ static int __kprobes notifier_call_chain(struct notifier_block **nl,
        }
        return ret;
 }
+NOKPROBE_SYMBOL(notifier_call_chain);
 
 /*
  *     Atomic notifier chain routines.  Registration and unregistration
@@ -172,9 +173,9 @@ EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister);
  *     Otherwise the return value is the return value
  *     of the last notifier function called.
  */
-int __kprobes __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
-                                       unsigned long val, void *v,
-                                       int nr_to_call, int *nr_calls)
+int __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
+                                unsigned long val, void *v,
+                                int nr_to_call, int *nr_calls)
 {
        int ret;
 
@@ -184,13 +185,15 @@ int __kprobes __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
        return ret;
 }
 EXPORT_SYMBOL_GPL(__atomic_notifier_call_chain);
+NOKPROBE_SYMBOL(__atomic_notifier_call_chain);
 
-int __kprobes atomic_notifier_call_chain(struct atomic_notifier_head *nh,
-               unsigned long val, void *v)
+int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
+                              unsigned long val, void *v)
 {
        return __atomic_notifier_call_chain(nh, val, v, -1, NULL);
 }
 EXPORT_SYMBOL_GPL(atomic_notifier_call_chain);
+NOKPROBE_SYMBOL(atomic_notifier_call_chain);
 
 /*
  *     Blocking notifier chain routines.  All access to the chain is
@@ -527,7 +530,7 @@ EXPORT_SYMBOL_GPL(srcu_init_notifier_head);
 
 static ATOMIC_NOTIFIER_HEAD(die_chain);
 
-int notrace __kprobes notify_die(enum die_val val, const char *str,
+int notrace notify_die(enum die_val val, const char *str,
               struct pt_regs *regs, long err, int trap, int sig)
 {
        struct die_args args = {
@@ -540,6 +543,7 @@ int notrace __kprobes notify_die(enum die_val val, const char *str,
        };
        return atomic_notifier_call_chain(&die_chain, val, &args);
 }
+NOKPROBE_SYMBOL(notify_die);
 
 int register_die_notifier(struct notifier_block *nb)
 {
index b00142e7f3ba18d52f41e19427f145eb6e95cf2d..1e52ca233fd9a45484418f14820c40b95e89d6ba 100644 (file)
@@ -177,13 +177,13 @@ static char *next_arg(char *args, char **param, char **val)
 }
 
 /* Args looks like "foo=bar,bar2 baz=fuz wiz". */
-int parse_args(const char *doing,
-              char *args,
-              const struct kernel_param *params,
-              unsigned num,
-              s16 min_level,
-              s16 max_level,
-              int (*unknown)(char *param, char *val, const char *doing))
+char *parse_args(const char *doing,
+                char *args,
+                const struct kernel_param *params,
+                unsigned num,
+                s16 min_level,
+                s16 max_level,
+                int (*unknown)(char *param, char *val, const char *doing))
 {
        char *param, *val;
 
@@ -198,6 +198,9 @@ int parse_args(const char *doing,
                int irq_was_disabled;
 
                args = next_arg(args, &param, &val);
+               /* Stop at -- */
+               if (!val && strcmp(param, "--") == 0)
+                       return args;
                irq_was_disabled = irqs_disabled();
                ret = parse_one(param, val, doing, params, num,
                                min_level, max_level, unknown);
@@ -208,22 +211,22 @@ int parse_args(const char *doing,
                switch (ret) {
                case -ENOENT:
                        pr_err("%s: Unknown parameter `%s'\n", doing, param);
-                       return ret;
+                       return ERR_PTR(ret);
                case -ENOSPC:
                        pr_err("%s: `%s' too large for parameter `%s'\n",
                               doing, val ?: "", param);
-                       return ret;
+                       return ERR_PTR(ret);
                case 0:
                        break;
                default:
                        pr_err("%s: `%s' invalid for parameter `%s'\n",
                               doing, val ?: "", param);
-                       return ret;
+                       return ERR_PTR(ret);
                }
        }
 
        /* All parsed OK. */
-       return 0;
+       return NULL;
 }
 
 /* Lazy bastard, eh? */
index df88d55dc4363965caa32ab1ac5c479c40e858e0..49e0a20fd01043eb3cf21b5394758571e0b4e7aa 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/syscore_ops.h>
 #include <linux/ctype.h>
 #include <linux/genhd.h>
+#include <trace/events/power.h>
 
 #include "power.h"
 
@@ -292,7 +293,9 @@ static int create_image(int platform_mode)
 
        in_suspend = 1;
        save_processor_state();
+       trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, true);
        error = swsusp_arch_suspend();
+       trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, false);
        if (error)
                printk(KERN_ERR "PM: Error %d creating hibernation image\n",
                        error);
index 06ec8869dbf1629f2c7cabc63f2edc96426ea87d..0ca8d83e2369e253706ff787c2e4243928fbcca0 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/delay.h>
 #include <linux/workqueue.h>
 #include <linux/kmod.h>
+#include <trace/events/power.h>
 
 /* 
  * Timeout for stopping processes
@@ -175,6 +176,7 @@ void thaw_processes(void)
        struct task_struct *g, *p;
        struct task_struct *curr = current;
 
+       trace_suspend_resume(TPS("thaw_processes"), 0, true);
        if (pm_freezing)
                atomic_dec(&system_freezing_cnt);
        pm_freezing = false;
@@ -201,6 +203,7 @@ void thaw_processes(void)
 
        schedule();
        printk("done.\n");
+       trace_suspend_resume(TPS("thaw_processes"), 0, false);
 }
 
 void thaw_kernel_threads(void)
index 963e6d0f050bd8ac2c62a0fee2838118e2993943..4dd8822f732a2a23835fca1f8f9a991ca756c3dd 100644 (file)
@@ -177,7 +177,9 @@ static int suspend_prepare(suspend_state_t state)
        if (error)
                goto Finish;
 
+       trace_suspend_resume(TPS("freeze_processes"), 0, true);
        error = suspend_freeze_processes();
+       trace_suspend_resume(TPS("freeze_processes"), 0, false);
        if (!error)
                return 0;
 
@@ -240,7 +242,9 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
         * all the devices are suspended.
         */
        if (state == PM_SUSPEND_FREEZE) {
+               trace_suspend_resume(TPS("machine_suspend"), state, true);
                freeze_enter();
+               trace_suspend_resume(TPS("machine_suspend"), state, false);
                goto Platform_wake;
        }
 
@@ -256,7 +260,11 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
        if (!error) {
                *wakeup = pm_wakeup_pending();
                if (!(suspend_test(TEST_CORE) || *wakeup)) {
+                       trace_suspend_resume(TPS("machine_suspend"),
+                               state, true);
                        error = suspend_ops->enter(state);
+                       trace_suspend_resume(TPS("machine_suspend"),
+                               state, false);
                        events_check_enabled = false;
                }
                syscore_resume();
@@ -294,7 +302,6 @@ int suspend_devices_and_enter(suspend_state_t state)
        if (need_suspend_ops(state) && !suspend_ops)
                return -ENOSYS;
 
-       trace_machine_suspend(state);
        if (need_suspend_ops(state) && suspend_ops->begin) {
                error = suspend_ops->begin(state);
                if (error)
@@ -331,7 +338,6 @@ int suspend_devices_and_enter(suspend_state_t state)
        else if (state == PM_SUSPEND_FREEZE && freeze_ops->end)
                freeze_ops->end();
 
-       trace_machine_suspend(PWR_EVENT_EXIT);
        return error;
 
  Recover_platform:
@@ -365,6 +371,7 @@ static int enter_state(suspend_state_t state)
 {
        int error;
 
+       trace_suspend_resume(TPS("suspend_enter"), state, true);
        if (state == PM_SUSPEND_FREEZE) {
 #ifdef CONFIG_PM_DEBUG
                if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
@@ -382,9 +389,11 @@ static int enter_state(suspend_state_t state)
        if (state == PM_SUSPEND_FREEZE)
                freeze_begin();
 
+       trace_suspend_resume(TPS("sync_filesystems"), 0, true);
        printk(KERN_INFO "PM: Syncing filesystems ... ");
        sys_sync();
        printk("done.\n");
+       trace_suspend_resume(TPS("sync_filesystems"), 0, false);
 
        pr_debug("PM: Preparing system for %s sleep\n", pm_states[state].label);
        error = suspend_prepare(state);
@@ -394,6 +403,7 @@ static int enter_state(suspend_state_t state)
        if (suspend_test(TEST_FREEZER))
                goto Finish;
 
+       trace_suspend_resume(TPS("suspend_enter"), state, false);
        pr_debug("PM: Entering %s sleep\n", pm_states[state].label);
        pm_restrict_gfp_mask();
        error = suspend_devices_and_enter(state);
index c6b98793d6477fd60fe8122745c9db75007d3080..3bdf01b494fe29c267a0abe73828b02a799a737d 100644 (file)
@@ -535,7 +535,7 @@ static inline void init_hrtick(void)
        __old;                                                          \
 })
 
-#ifdef TIF_POLLING_NRFLAG
+#if defined(CONFIG_SMP) && defined(TIF_POLLING_NRFLAG)
 /*
  * Atomically set TIF_NEED_RESCHED and test for TIF_POLLING_NRFLAG,
  * this avoids any races wrt polling state changes and thereby avoids
@@ -546,12 +546,44 @@ static bool set_nr_and_not_polling(struct task_struct *p)
        struct thread_info *ti = task_thread_info(p);
        return !(fetch_or(&ti->flags, _TIF_NEED_RESCHED) & _TIF_POLLING_NRFLAG);
 }
+
+/*
+ * Atomically set TIF_NEED_RESCHED if TIF_POLLING_NRFLAG is set.
+ *
+ * If this returns true, then the idle task promises to call
+ * sched_ttwu_pending() and reschedule soon.
+ */
+static bool set_nr_if_polling(struct task_struct *p)
+{
+       struct thread_info *ti = task_thread_info(p);
+       typeof(ti->flags) old, val = ACCESS_ONCE(ti->flags);
+
+       for (;;) {
+               if (!(val & _TIF_POLLING_NRFLAG))
+                       return false;
+               if (val & _TIF_NEED_RESCHED)
+                       return true;
+               old = cmpxchg(&ti->flags, val, val | _TIF_NEED_RESCHED);
+               if (old == val)
+                       break;
+               val = old;
+       }
+       return true;
+}
+
 #else
 static bool set_nr_and_not_polling(struct task_struct *p)
 {
        set_tsk_need_resched(p);
        return true;
 }
+
+#ifdef CONFIG_SMP
+static bool set_nr_if_polling(struct task_struct *p)
+{
+       return false;
+}
+#endif
 #endif
 
 /*
@@ -580,6 +612,8 @@ void resched_task(struct task_struct *p)
 
        if (set_nr_and_not_polling(p))
                smp_send_reschedule(cpu);
+       else
+               trace_sched_wake_idle_without_ipi(cpu);
 }
 
 void resched_cpu(int cpu)
@@ -642,27 +676,10 @@ static void wake_up_idle_cpu(int cpu)
        if (cpu == smp_processor_id())
                return;
 
-       /*
-        * This is safe, as this function is called with the timer
-        * wheel base lock of (cpu) held. When the CPU is on the way
-        * to idle and has not yet set rq->curr to idle then it will
-        * be serialized on the timer wheel base lock and take the new
-        * timer into account automatically.
-        */
-       if (rq->curr != rq->idle)
-               return;
-
-       /*
-        * We can set TIF_RESCHED on the idle task of the other CPU
-        * lockless. The worst case is that the other CPU runs the
-        * idle task through an additional NOOP schedule()
-        */
-       set_tsk_need_resched(rq->idle);
-
-       /* NEED_RESCHED must be visible before we test polling */
-       smp_mb();
-       if (!tsk_is_polling(rq->idle))
+       if (set_nr_and_not_polling(rq->idle))
                smp_send_reschedule(cpu);
+       else
+               trace_sched_wake_idle_without_ipi(cpu);
 }
 
 static bool wake_up_full_nohz_cpu(int cpu)
@@ -888,7 +905,7 @@ static void update_rq_clock_task(struct rq *rq, s64 delta)
        rq->clock_task += delta;
 
 #if defined(CONFIG_IRQ_TIME_ACCOUNTING) || defined(CONFIG_PARAVIRT_TIME_ACCOUNTING)
-       if ((irq_delta + steal) && sched_feat(NONTASK_POWER))
+       if ((irq_delta + steal) && sched_feat(NONTASK_CAPACITY))
                sched_rt_avg_update(rq, irq_delta + steal);
 #endif
 }
@@ -1521,13 +1538,17 @@ static int ttwu_remote(struct task_struct *p, int wake_flags)
 }
 
 #ifdef CONFIG_SMP
-static void sched_ttwu_pending(void)
+void sched_ttwu_pending(void)
 {
        struct rq *rq = this_rq();
        struct llist_node *llist = llist_del_all(&rq->wake_list);
        struct task_struct *p;
+       unsigned long flags;
 
-       raw_spin_lock(&rq->lock);
+       if (!llist)
+               return;
+
+       raw_spin_lock_irqsave(&rq->lock, flags);
 
        while (llist) {
                p = llist_entry(llist, struct task_struct, wake_entry);
@@ -1535,7 +1556,7 @@ static void sched_ttwu_pending(void)
                ttwu_do_activate(rq, p, 0);
        }
 
-       raw_spin_unlock(&rq->lock);
+       raw_spin_unlock_irqrestore(&rq->lock, flags);
 }
 
 void scheduler_ipi(void)
@@ -1581,8 +1602,14 @@ void scheduler_ipi(void)
 
 static void ttwu_queue_remote(struct task_struct *p, int cpu)
 {
-       if (llist_add(&p->wake_entry, &cpu_rq(cpu)->wake_list))
-               smp_send_reschedule(cpu);
+       struct rq *rq = cpu_rq(cpu);
+
+       if (llist_add(&p->wake_entry, &cpu_rq(cpu)->wake_list)) {
+               if (!set_nr_if_polling(rq->idle))
+                       smp_send_reschedule(cpu);
+               else
+                       trace_sched_wake_idle_without_ipi(cpu);
+       }
 }
 
 bool cpus_share_cache(int this_cpu, int that_cpu)
@@ -2527,7 +2554,7 @@ notrace unsigned long get_parent_ip(unsigned long addr)
 #if defined(CONFIG_PREEMPT) && (defined(CONFIG_DEBUG_PREEMPT) || \
                                defined(CONFIG_PREEMPT_TRACER))
 
-void __kprobes preempt_count_add(int val)
+void preempt_count_add(int val)
 {
 #ifdef CONFIG_DEBUG_PREEMPT
        /*
@@ -2553,8 +2580,9 @@ void __kprobes preempt_count_add(int val)
        }
 }
 EXPORT_SYMBOL(preempt_count_add);
+NOKPROBE_SYMBOL(preempt_count_add);
 
-void __kprobes preempt_count_sub(int val)
+void preempt_count_sub(int val)
 {
 #ifdef CONFIG_DEBUG_PREEMPT
        /*
@@ -2575,6 +2603,7 @@ void __kprobes preempt_count_sub(int val)
        __preempt_count_sub(val);
 }
 EXPORT_SYMBOL(preempt_count_sub);
+NOKPROBE_SYMBOL(preempt_count_sub);
 
 #endif
 
@@ -2857,6 +2886,7 @@ asmlinkage __visible void __sched notrace preempt_schedule(void)
                barrier();
        } while (need_resched());
 }
+NOKPROBE_SYMBOL(preempt_schedule);
 EXPORT_SYMBOL(preempt_schedule);
 #endif /* CONFIG_PREEMPT */
 
@@ -4216,7 +4246,7 @@ EXPORT_SYMBOL(yield);
  *     false (0) if we failed to boost the target.
  *     -ESRCH if there's no task to yield to.
  */
-bool __sched yield_to(struct task_struct *p, bool preempt)
+int __sched yield_to(struct task_struct *p, bool preempt)
 {
        struct task_struct *curr = current;
        struct rq *rq, *p_rq;
@@ -5242,14 +5272,13 @@ static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level,
                }
 
                /*
-                * Even though we initialize ->power to something semi-sane,
-                * we leave power_orig unset. This allows us to detect if
+                * Even though we initialize ->capacity to something semi-sane,
+                * we leave capacity_orig unset. This allows us to detect if
                 * domain iteration is still funny without causing /0 traps.
                 */
-               if (!group->sgp->power_orig) {
+               if (!group->sgc->capacity_orig) {
                        printk(KERN_CONT "\n");
-                       printk(KERN_ERR "ERROR: domain->cpu_power not "
-                                       "set\n");
+                       printk(KERN_ERR "ERROR: domain->cpu_capacity not set\n");
                        break;
                }
 
@@ -5271,9 +5300,9 @@ static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level,
                cpulist_scnprintf(str, sizeof(str), sched_group_cpus(group));
 
                printk(KERN_CONT " %s", str);
-               if (group->sgp->power != SCHED_POWER_SCALE) {
-                       printk(KERN_CONT " (cpu_power = %d)",
-                               group->sgp->power);
+               if (group->sgc->capacity != SCHED_CAPACITY_SCALE) {
+                       printk(KERN_CONT " (cpu_capacity = %d)",
+                               group->sgc->capacity);
                }
 
                group = group->next;
@@ -5331,7 +5360,7 @@ static int sd_degenerate(struct sched_domain *sd)
                         SD_BALANCE_NEWIDLE |
                         SD_BALANCE_FORK |
                         SD_BALANCE_EXEC |
-                        SD_SHARE_CPUPOWER |
+                        SD_SHARE_CPUCAPACITY |
                         SD_SHARE_PKG_RESOURCES |
                         SD_SHARE_POWERDOMAIN)) {
                if (sd->groups != sd->groups->next)
@@ -5362,7 +5391,7 @@ sd_parent_degenerate(struct sched_domain *sd, struct sched_domain *parent)
                                SD_BALANCE_NEWIDLE |
                                SD_BALANCE_FORK |
                                SD_BALANCE_EXEC |
-                               SD_SHARE_CPUPOWER |
+                               SD_SHARE_CPUCAPACITY |
                                SD_SHARE_PKG_RESOURCES |
                                SD_PREFER_SIBLING |
                                SD_SHARE_POWERDOMAIN);
@@ -5487,7 +5516,7 @@ static struct root_domain *alloc_rootdomain(void)
        return rd;
 }
 
-static void free_sched_groups(struct sched_group *sg, int free_sgp)
+static void free_sched_groups(struct sched_group *sg, int free_sgc)
 {
        struct sched_group *tmp, *first;
 
@@ -5498,8 +5527,8 @@ static void free_sched_groups(struct sched_group *sg, int free_sgp)
        do {
                tmp = sg->next;
 
-               if (free_sgp && atomic_dec_and_test(&sg->sgp->ref))
-                       kfree(sg->sgp);
+               if (free_sgc && atomic_dec_and_test(&sg->sgc->ref))
+                       kfree(sg->sgc);
 
                kfree(sg);
                sg = tmp;
@@ -5517,7 +5546,7 @@ static void free_sched_domain(struct rcu_head *rcu)
        if (sd->flags & SD_OVERLAP) {
                free_sched_groups(sd->groups, 1);
        } else if (atomic_dec_and_test(&sd->groups->ref)) {
-               kfree(sd->groups->sgp);
+               kfree(sd->groups->sgc);
                kfree(sd->groups);
        }
        kfree(sd);
@@ -5728,17 +5757,17 @@ build_overlap_sched_groups(struct sched_domain *sd, int cpu)
 
                cpumask_or(covered, covered, sg_span);
 
-               sg->sgp = *per_cpu_ptr(sdd->sgp, i);
-               if (atomic_inc_return(&sg->sgp->ref) == 1)
+               sg->sgc = *per_cpu_ptr(sdd->sgc, i);
+               if (atomic_inc_return(&sg->sgc->ref) == 1)
                        build_group_mask(sd, sg);
 
                /*
-                * Initialize sgp->power such that even if we mess up the
+                * Initialize sgc->capacity such that even if we mess up the
                 * domains and no possible iteration will get us here, we won't
                 * die on a /0 trap.
                 */
-               sg->sgp->power = SCHED_POWER_SCALE * cpumask_weight(sg_span);
-               sg->sgp->power_orig = sg->sgp->power;
+               sg->sgc->capacity = SCHED_CAPACITY_SCALE * cpumask_weight(sg_span);
+               sg->sgc->capacity_orig = sg->sgc->capacity;
 
                /*
                 * Make sure the first group of this domain contains the
@@ -5776,8 +5805,8 @@ static int get_group(int cpu, struct sd_data *sdd, struct sched_group **sg)
 
        if (sg) {
                *sg = *per_cpu_ptr(sdd->sg, cpu);
-               (*sg)->sgp = *per_cpu_ptr(sdd->sgp, cpu);
-               atomic_set(&(*sg)->sgp->ref, 1); /* for claim_allocations */
+               (*sg)->sgc = *per_cpu_ptr(sdd->sgc, cpu);
+               atomic_set(&(*sg)->sgc->ref, 1); /* for claim_allocations */
        }
 
        return cpu;
@@ -5786,7 +5815,7 @@ static int get_group(int cpu, struct sd_data *sdd, struct sched_group **sg)
 /*
  * build_sched_groups will build a circular linked list of the groups
  * covered by the given span, and will set each group's ->cpumask correctly,
- * and ->cpu_power to 0.
+ * and ->cpu_capacity to 0.
  *
  * Assumes the sched_domain tree is fully constructed
  */
@@ -5840,16 +5869,16 @@ build_sched_groups(struct sched_domain *sd, int cpu)
 }
 
 /*
- * Initialize sched groups cpu_power.
+ * Initialize sched groups cpu_capacity.
  *
- * cpu_power indicates the capacity of sched group, which is used while
+ * cpu_capacity indicates the capacity of sched group, which is used while
  * distributing the load between different sched groups in a sched domain.
- * Typically cpu_power for all the groups in a sched domain will be same unless
- * there are asymmetries in the topology. If there are asymmetries, group
- * having more cpu_power will pickup more load compared to the group having
- * less cpu_power.
+ * Typically cpu_capacity for all the groups in a sched domain will be same
+ * unless there are asymmetries in the topology. If there are asymmetries,
+ * group having more cpu_capacity will pickup more load compared to the
+ * group having less cpu_capacity.
  */
-static void init_sched_groups_power(int cpu, struct sched_domain *sd)
+static void init_sched_groups_capacity(int cpu, struct sched_domain *sd)
 {
        struct sched_group *sg = sd->groups;
 
@@ -5863,8 +5892,8 @@ static void init_sched_groups_power(int cpu, struct sched_domain *sd)
        if (cpu != group_balance_cpu(sg))
                return;
 
-       update_group_power(sd, cpu);
-       atomic_set(&sg->sgp->nr_busy_cpus, sg->group_weight);
+       update_group_capacity(sd, cpu);
+       atomic_set(&sg->sgc->nr_busy_cpus, sg->group_weight);
 }
 
 /*
@@ -5955,8 +5984,8 @@ static void claim_allocations(int cpu, struct sched_domain *sd)
        if (atomic_read(&(*per_cpu_ptr(sdd->sg, cpu))->ref))
                *per_cpu_ptr(sdd->sg, cpu) = NULL;
 
-       if (atomic_read(&(*per_cpu_ptr(sdd->sgp, cpu))->ref))
-               *per_cpu_ptr(sdd->sgp, cpu) = NULL;
+       if (atomic_read(&(*per_cpu_ptr(sdd->sgc, cpu))->ref))
+               *per_cpu_ptr(sdd->sgc, cpu) = NULL;
 }
 
 #ifdef CONFIG_NUMA
@@ -5969,7 +5998,7 @@ static int sched_domains_curr_level;
 /*
  * SD_flags allowed in topology descriptions.
  *
- * SD_SHARE_CPUPOWER      - describes SMT topologies
+ * SD_SHARE_CPUCAPACITY      - describes SMT topologies
  * SD_SHARE_PKG_RESOURCES - describes shared caches
  * SD_NUMA                - describes NUMA topologies
  * SD_SHARE_POWERDOMAIN   - describes shared power domain
@@ -5978,7 +6007,7 @@ static int sched_domains_curr_level;
  * SD_ASYM_PACKING        - describes SMT quirks
  */
 #define TOPOLOGY_SD_FLAGS              \
-       (SD_SHARE_CPUPOWER |            \
+       (SD_SHARE_CPUCAPACITY |         \
         SD_SHARE_PKG_RESOURCES |       \
         SD_NUMA |                      \
         SD_ASYM_PACKING |              \
@@ -6024,7 +6053,7 @@ sd_init(struct sched_domain_topology_level *tl, int cpu)
                                        | 1*SD_BALANCE_FORK
                                        | 0*SD_BALANCE_WAKE
                                        | 1*SD_WAKE_AFFINE
-                                       | 0*SD_SHARE_CPUPOWER
+                                       | 0*SD_SHARE_CPUCAPACITY
                                        | 0*SD_SHARE_PKG_RESOURCES
                                        | 0*SD_SERIALIZE
                                        | 0*SD_PREFER_SIBLING
@@ -6046,7 +6075,7 @@ sd_init(struct sched_domain_topology_level *tl, int cpu)
         * Convert topological properties into behaviour.
         */
 
-       if (sd->flags & SD_SHARE_CPUPOWER) {
+       if (sd->flags & SD_SHARE_CPUCAPACITY) {
                sd->imbalance_pct = 110;
                sd->smt_gain = 1178; /* ~15% */
 
@@ -6358,14 +6387,14 @@ static int __sdt_alloc(const struct cpumask *cpu_map)
                if (!sdd->sg)
                        return -ENOMEM;
 
-               sdd->sgp = alloc_percpu(struct sched_group_power *);
-               if (!sdd->sgp)
+               sdd->sgc = alloc_percpu(struct sched_group_capacity *);
+               if (!sdd->sgc)
                        return -ENOMEM;
 
                for_each_cpu(j, cpu_map) {
                        struct sched_domain *sd;
                        struct sched_group *sg;
-                       struct sched_group_power *sgp;
+                       struct sched_group_capacity *sgc;
 
                        sd = kzalloc_node(sizeof(struct sched_domain) + cpumask_size(),
                                        GFP_KERNEL, cpu_to_node(j));
@@ -6383,12 +6412,12 @@ static int __sdt_alloc(const struct cpumask *cpu_map)
 
                        *per_cpu_ptr(sdd->sg, j) = sg;
 
-                       sgp = kzalloc_node(sizeof(struct sched_group_power) + cpumask_size(),
+                       sgc = kzalloc_node(sizeof(struct sched_group_capacity) + cpumask_size(),
                                        GFP_KERNEL, cpu_to_node(j));
-                       if (!sgp)
+                       if (!sgc)
                                return -ENOMEM;
 
-                       *per_cpu_ptr(sdd->sgp, j) = sgp;
+                       *per_cpu_ptr(sdd->sgc, j) = sgc;
                }
        }
 
@@ -6415,15 +6444,15 @@ static void __sdt_free(const struct cpumask *cpu_map)
 
                        if (sdd->sg)
                                kfree(*per_cpu_ptr(sdd->sg, j));
-                       if (sdd->sgp)
-                               kfree(*per_cpu_ptr(sdd->sgp, j));
+                       if (sdd->sgc)
+                               kfree(*per_cpu_ptr(sdd->sgc, j));
                }
                free_percpu(sdd->sd);
                sdd->sd = NULL;
                free_percpu(sdd->sg);
                sdd->sg = NULL;
-               free_percpu(sdd->sgp);
-               sdd->sgp = NULL;
+               free_percpu(sdd->sgc);
+               sdd->sgc = NULL;
        }
 }
 
@@ -6493,14 +6522,14 @@ static int build_sched_domains(const struct cpumask *cpu_map,
                }
        }
 
-       /* Calculate CPU power for physical packages and nodes */
+       /* Calculate CPU capacity for physical packages and nodes */
        for (i = nr_cpumask_bits-1; i >= 0; i--) {
                if (!cpumask_test_cpu(i, cpu_map))
                        continue;
 
                for (sd = *per_cpu_ptr(d.sd, i); sd; sd = sd->parent) {
                        claim_allocations(i, sd);
-                       init_sched_groups_power(i, sd);
+                       init_sched_groups_capacity(i, sd);
                }
        }
 
@@ -6943,7 +6972,7 @@ void __init sched_init(void)
 #ifdef CONFIG_SMP
                rq->sd = NULL;
                rq->rd = NULL;
-               rq->cpu_power = SCHED_POWER_SCALE;
+               rq->cpu_capacity = SCHED_CAPACITY_SCALE;
                rq->post_schedule = 0;
                rq->active_balance = 0;
                rq->next_balance = jiffies;
index 2b8cbf09d1a4add6fe837be0080a71a35f145164..fc4f98b1258f66cbbf3cf1fc1082cb909c0f1144 100644 (file)
@@ -57,8 +57,6 @@ void init_dl_bandwidth(struct dl_bandwidth *dl_b, u64 period, u64 runtime)
        dl_b->dl_runtime = runtime;
 }
 
-extern unsigned long to_ratio(u64 period, u64 runtime);
-
 void init_dl_bw(struct dl_bw *dl_b)
 {
        raw_spin_lock_init(&dl_b->lock);
index 9855e87d671a54982238d325f014162327a974e8..fea7d3335e1fdf3502fc72f5d64b9181bc7e4243 100644 (file)
@@ -1017,7 +1017,7 @@ bool should_numa_migrate_memory(struct task_struct *p, struct page * page,
 static unsigned long weighted_cpuload(const int cpu);
 static unsigned long source_load(int cpu, int type);
 static unsigned long target_load(int cpu, int type);
-static unsigned long power_of(int cpu);
+static unsigned long capacity_of(int cpu);
 static long effective_load(struct task_group *tg, int cpu, long wl, long wg);
 
 /* Cached statistics for all CPUs within a node */
@@ -1026,11 +1026,11 @@ struct numa_stats {
        unsigned long load;
 
        /* Total compute capacity of CPUs on a node */
-       unsigned long power;
+       unsigned long compute_capacity;
 
        /* Approximate capacity in terms of runnable tasks on a node */
-       unsigned long capacity;
-       int has_capacity;
+       unsigned long task_capacity;
+       int has_free_capacity;
 };
 
 /*
@@ -1046,7 +1046,7 @@ static void update_numa_stats(struct numa_stats *ns, int nid)
 
                ns->nr_running += rq->nr_running;
                ns->load += weighted_cpuload(cpu);
-               ns->power += power_of(cpu);
+               ns->compute_capacity += capacity_of(cpu);
 
                cpus++;
        }
@@ -1056,15 +1056,16 @@ static void update_numa_stats(struct numa_stats *ns, int nid)
         * the @ns structure is NULL'ed and task_numa_compare() will
         * not find this node attractive.
         *
-        * We'll either bail at !has_capacity, or we'll detect a huge imbalance
-        * and bail there.
+        * We'll either bail at !has_free_capacity, or we'll detect a huge
+        * imbalance and bail there.
         */
        if (!cpus)
                return;
 
-       ns->load = (ns->load * SCHED_POWER_SCALE) / ns->power;
-       ns->capacity = DIV_ROUND_CLOSEST(ns->power, SCHED_POWER_SCALE);
-       ns->has_capacity = (ns->nr_running < ns->capacity);
+       ns->load = (ns->load * SCHED_CAPACITY_SCALE) / ns->compute_capacity;
+       ns->task_capacity =
+               DIV_ROUND_CLOSEST(ns->compute_capacity, SCHED_CAPACITY_SCALE);
+       ns->has_free_capacity = (ns->nr_running < ns->task_capacity);
 }
 
 struct task_numa_env {
@@ -1195,8 +1196,8 @@ static void task_numa_compare(struct task_numa_env *env,
 
        if (!cur) {
                /* Is there capacity at our destination? */
-               if (env->src_stats.has_capacity &&
-                   !env->dst_stats.has_capacity)
+               if (env->src_stats.has_free_capacity &&
+                   !env->dst_stats.has_free_capacity)
                        goto unlock;
 
                goto balance;
@@ -1213,7 +1214,7 @@ balance:
        orig_dst_load = env->dst_stats.load;
        orig_src_load = env->src_stats.load;
 
-       /* XXX missing power terms */
+       /* XXX missing capacity terms */
        load = task_h_load(env->p);
        dst_load = orig_dst_load + load;
        src_load = orig_src_load - load;
@@ -1301,8 +1302,8 @@ static int task_numa_migrate(struct task_struct *p)
        groupimp = group_weight(p, env.dst_nid) - groupweight;
        update_numa_stats(&env.dst_stats, env.dst_nid);
 
-       /* If the preferred nid has capacity, try to use it. */
-       if (env.dst_stats.has_capacity)
+       /* If the preferred nid has free capacity, try to use it. */
+       if (env.dst_stats.has_free_capacity)
                task_numa_find_cpu(&env, taskimp, groupimp);
 
        /* No space available on the preferred nid. Look elsewhere. */
@@ -3225,10 +3226,12 @@ static void expire_cfs_rq_runtime(struct cfs_rq *cfs_rq)
         * has not truly expired.
         *
         * Fortunately we can check determine whether this the case by checking
-        * whether the global deadline has advanced.
+        * whether the global deadline has advanced. It is valid to compare
+        * cfs_b->runtime_expires without any locks since we only care about
+        * exact equality, so a partial write will still work.
         */
 
-       if ((s64)(cfs_rq->runtime_expires - cfs_b->runtime_expires) >= 0) {
+       if (cfs_rq->runtime_expires != cfs_b->runtime_expires) {
                /* extend local deadline, drift is bounded above by 2 ticks */
                cfs_rq->runtime_expires += TICK_NSEC;
        } else {
@@ -3457,21 +3460,21 @@ next:
 static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun)
 {
        u64 runtime, runtime_expires;
-       int idle = 1, throttled;
+       int throttled;
 
-       raw_spin_lock(&cfs_b->lock);
        /* no need to continue the timer with no bandwidth constraint */
        if (cfs_b->quota == RUNTIME_INF)
-               goto out_unlock;
+               goto out_deactivate;
 
        throttled = !list_empty(&cfs_b->throttled_cfs_rq);
-       /* idle depends on !throttled (for the case of a large deficit) */
-       idle = cfs_b->idle && !throttled;
        cfs_b->nr_periods += overrun;
 
-       /* if we're going inactive then everything else can be deferred */
-       if (idle)
-               goto out_unlock;
+       /*
+        * idle depends on !throttled (for the case of a large deficit), and if
+        * we're going inactive then everything else can be deferred
+        */
+       if (cfs_b->idle && !throttled)
+               goto out_deactivate;
 
        /*
         * if we have relooped after returning idle once, we need to update our
@@ -3485,7 +3488,7 @@ static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun)
        if (!throttled) {
                /* mark as potentially idle for the upcoming period */
                cfs_b->idle = 1;
-               goto out_unlock;
+               return 0;
        }
 
        /* account preceding periods in which throttling occurred */
@@ -3525,12 +3528,12 @@ static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun)
         * timer to remain active while there are any throttled entities.)
         */
        cfs_b->idle = 0;
-out_unlock:
-       if (idle)
-               cfs_b->timer_active = 0;
-       raw_spin_unlock(&cfs_b->lock);
 
-       return idle;
+       return 0;
+
+out_deactivate:
+       cfs_b->timer_active = 0;
+       return 1;
 }
 
 /* a cfs_rq won't donate quota below this amount */
@@ -3707,6 +3710,7 @@ static enum hrtimer_restart sched_cfs_period_timer(struct hrtimer *timer)
        int overrun;
        int idle = 0;
 
+       raw_spin_lock(&cfs_b->lock);
        for (;;) {
                now = hrtimer_cb_get_time(timer);
                overrun = hrtimer_forward(timer, now, cfs_b->period);
@@ -3716,6 +3720,7 @@ static enum hrtimer_restart sched_cfs_period_timer(struct hrtimer *timer)
 
                idle = do_sched_cfs_period_timer(cfs_b, overrun);
        }
+       raw_spin_unlock(&cfs_b->lock);
 
        return idle ? HRTIMER_NORESTART : HRTIMER_RESTART;
 }
@@ -3775,8 +3780,6 @@ static void __maybe_unused unthrottle_offline_cfs_rqs(struct rq *rq)
        struct cfs_rq *cfs_rq;
 
        for_each_leaf_cfs_rq(rq, cfs_rq) {
-               struct cfs_bandwidth *cfs_b = tg_cfs_bandwidth(cfs_rq->tg);
-
                if (!cfs_rq->runtime_enabled)
                        continue;
 
@@ -3784,7 +3787,7 @@ static void __maybe_unused unthrottle_offline_cfs_rqs(struct rq *rq)
                 * clock_task is not advancing so we just need to make sure
                 * there's some valid quota amount
                 */
-               cfs_rq->runtime_remaining = cfs_b->quota;
+               cfs_rq->runtime_remaining = 1;
                if (cfs_rq_throttled(cfs_rq))
                        unthrottle_cfs_rq(cfs_rq);
        }
@@ -4041,9 +4044,9 @@ static unsigned long target_load(int cpu, int type)
        return max(rq->cpu_load[type-1], total);
 }
 
-static unsigned long power_of(int cpu)
+static unsigned long capacity_of(int cpu)
 {
-       return cpu_rq(cpu)->cpu_power;
+       return cpu_rq(cpu)->cpu_capacity;
 }
 
 static unsigned long cpu_avg_load_per_task(int cpu)
@@ -4065,7 +4068,7 @@ static void record_wakee(struct task_struct *p)
         * about the boundary, really active task won't care
         * about the loss.
         */
-       if (jiffies > current->wakee_flip_decay_ts + HZ) {
+       if (time_after(jiffies, current->wakee_flip_decay_ts + HZ)) {
                current->wakee_flips >>= 1;
                current->wakee_flip_decay_ts = jiffies;
        }
@@ -4286,12 +4289,12 @@ static int wake_affine(struct sched_domain *sd, struct task_struct *p, int sync)
                s64 this_eff_load, prev_eff_load;
 
                this_eff_load = 100;
-               this_eff_load *= power_of(prev_cpu);
+               this_eff_load *= capacity_of(prev_cpu);
                this_eff_load *= this_load +
                        effective_load(tg, this_cpu, weight, weight);
 
                prev_eff_load = 100 + (sd->imbalance_pct - 100) / 2;
-               prev_eff_load *= power_of(this_cpu);
+               prev_eff_load *= capacity_of(this_cpu);
                prev_eff_load *= load + effective_load(tg, prev_cpu, 0, weight);
 
                balanced = this_eff_load <= prev_eff_load;
@@ -4367,8 +4370,8 @@ find_idlest_group(struct sched_domain *sd, struct task_struct *p,
                        avg_load += load;
                }
 
-               /* Adjust by relative CPU power of the group */
-               avg_load = (avg_load * SCHED_POWER_SCALE) / group->sgp->power;
+               /* Adjust by relative CPU capacity of the group */
+               avg_load = (avg_load * SCHED_CAPACITY_SCALE) / group->sgc->capacity;
 
                if (local_group) {
                        this_load = avg_load;
@@ -4948,14 +4951,14 @@ static bool yield_to_task_fair(struct rq *rq, struct task_struct *p, bool preemp
  *
  *   W'_i,n = (2^n - 1) / 2^n * W_i,n + 1 / 2^n * W_i,0               (3)
  *
- * P_i is the cpu power (or compute capacity) of cpu i, typically it is the
+ * C_i is the compute capacity of cpu i, typically it is the
  * fraction of 'recent' time available for SCHED_OTHER task execution. But it
  * can also include other factors [XXX].
  *
  * To achieve this balance we define a measure of imbalance which follows
  * directly from (1):
  *
- *   imb_i,j = max{ avg(W/P), W_i/P_i } - min{ avg(W/P), W_j/P_j }    (4)
+ *   imb_i,j = max{ avg(W/C), W_i/C_i } - min{ avg(W/C), W_j/C_j }    (4)
  *
  * We them move tasks around to minimize the imbalance. In the continuous
  * function space it is obvious this converges, in the discrete case we get
@@ -5530,13 +5533,13 @@ struct sg_lb_stats {
        unsigned long group_load; /* Total load over the CPUs of the group */
        unsigned long sum_weighted_load; /* Weighted load of group's tasks */
        unsigned long load_per_task;
-       unsigned long group_power;
+       unsigned long group_capacity;
        unsigned int sum_nr_running; /* Nr tasks running in the group */
-       unsigned int group_capacity;
+       unsigned int group_capacity_factor;
        unsigned int idle_cpus;
        unsigned int group_weight;
        int group_imb; /* Is there an imbalance in the group ? */
-       int group_has_capacity; /* Is there extra capacity in the group? */
+       int group_has_free_capacity;
 #ifdef CONFIG_NUMA_BALANCING
        unsigned int nr_numa_running;
        unsigned int nr_preferred_running;
@@ -5551,7 +5554,7 @@ struct sd_lb_stats {
        struct sched_group *busiest;    /* Busiest group in this sd */
        struct sched_group *local;      /* Local group in this sd */
        unsigned long total_load;       /* Total load of all groups in sd */
-       unsigned long total_pwr;        /* Total power of all groups in sd */
+       unsigned long total_capacity;   /* Total capacity of all groups in sd */
        unsigned long avg_load; /* Average load across all groups in sd */
 
        struct sg_lb_stats busiest_stat;/* Statistics of the busiest group */
@@ -5570,7 +5573,7 @@ static inline void init_sd_lb_stats(struct sd_lb_stats *sds)
                .busiest = NULL,
                .local = NULL,
                .total_load = 0UL,
-               .total_pwr = 0UL,
+               .total_capacity = 0UL,
                .busiest_stat = {
                        .avg_load = 0UL,
                },
@@ -5605,17 +5608,17 @@ static inline int get_sd_load_idx(struct sched_domain *sd,
        return load_idx;
 }
 
-static unsigned long default_scale_freq_power(struct sched_domain *sd, int cpu)
+static unsigned long default_scale_capacity(struct sched_domain *sd, int cpu)
 {
-       return SCHED_POWER_SCALE;
+       return SCHED_CAPACITY_SCALE;
 }
 
-unsigned long __weak arch_scale_freq_power(struct sched_domain *sd, int cpu)
+unsigned long __weak arch_scale_freq_capacity(struct sched_domain *sd, int cpu)
 {
-       return default_scale_freq_power(sd, cpu);
+       return default_scale_capacity(sd, cpu);
 }
 
-static unsigned long default_scale_smt_power(struct sched_domain *sd, int cpu)
+static unsigned long default_scale_smt_capacity(struct sched_domain *sd, int cpu)
 {
        unsigned long weight = sd->span_weight;
        unsigned long smt_gain = sd->smt_gain;
@@ -5625,12 +5628,12 @@ static unsigned long default_scale_smt_power(struct sched_domain *sd, int cpu)
        return smt_gain;
 }
 
-unsigned long __weak arch_scale_smt_power(struct sched_domain *sd, int cpu)
+unsigned long __weak arch_scale_smt_capacity(struct sched_domain *sd, int cpu)
 {
-       return default_scale_smt_power(sd, cpu);
+       return default_scale_smt_capacity(sd, cpu);
 }
 
-static unsigned long scale_rt_power(int cpu)
+static unsigned long scale_rt_capacity(int cpu)
 {
        struct rq *rq = cpu_rq(cpu);
        u64 total, available, age_stamp, avg;
@@ -5650,71 +5653,71 @@ static unsigned long scale_rt_power(int cpu)
        total = sched_avg_period() + delta;
 
        if (unlikely(total < avg)) {
-               /* Ensures that power won't end up being negative */
+               /* Ensures that capacity won't end up being negative */
                available = 0;
        } else {
                available = total - avg;
        }
 
-       if (unlikely((s64)total < SCHED_POWER_SCALE))
-               total = SCHED_POWER_SCALE;
+       if (unlikely((s64)total < SCHED_CAPACITY_SCALE))
+               total = SCHED_CAPACITY_SCALE;
 
-       total >>= SCHED_POWER_SHIFT;
+       total >>= SCHED_CAPACITY_SHIFT;
 
        return div_u64(available, total);
 }
 
-static void update_cpu_power(struct sched_domain *sd, int cpu)
+static void update_cpu_capacity(struct sched_domain *sd, int cpu)
 {
        unsigned long weight = sd->span_weight;
-       unsigned long power = SCHED_POWER_SCALE;
+       unsigned long capacity = SCHED_CAPACITY_SCALE;
        struct sched_group *sdg = sd->groups;
 
-       if ((sd->flags & SD_SHARE_CPUPOWER) && weight > 1) {
-               if (sched_feat(ARCH_POWER))
-                       power *= arch_scale_smt_power(sd, cpu);
+       if ((sd->flags & SD_SHARE_CPUCAPACITY) && weight > 1) {
+               if (sched_feat(ARCH_CAPACITY))
+                       capacity *= arch_scale_smt_capacity(sd, cpu);
                else
-                       power *= default_scale_smt_power(sd, cpu);
+                       capacity *= default_scale_smt_capacity(sd, cpu);
 
-               power >>= SCHED_POWER_SHIFT;
+               capacity >>= SCHED_CAPACITY_SHIFT;
        }
 
-       sdg->sgp->power_orig = power;
+       sdg->sgc->capacity_orig = capacity;
 
-       if (sched_feat(ARCH_POWER))
-               power *= arch_scale_freq_power(sd, cpu);
+       if (sched_feat(ARCH_CAPACITY))
+               capacity *= arch_scale_freq_capacity(sd, cpu);
        else
-               power *= default_scale_freq_power(sd, cpu);
+               capacity *= default_scale_capacity(sd, cpu);
 
-       power >>= SCHED_POWER_SHIFT;
+       capacity >>= SCHED_CAPACITY_SHIFT;
 
-       power *= scale_rt_power(cpu);
-       power >>= SCHED_POWER_SHIFT;
+       capacity *= scale_rt_capacity(cpu);
+       capacity >>= SCHED_CAPACITY_SHIFT;
 
-       if (!power)
-               power = 1;
+       if (!capacity)
+               capacity = 1;
 
-       cpu_rq(cpu)->cpu_power = power;
-       sdg->sgp->power = power;
+       cpu_rq(cpu)->cpu_capacity = capacity;
+       sdg->sgc->capacity = capacity;
 }
 
-void update_group_power(struct sched_domain *sd, int cpu)
+void update_group_capacity(struct sched_domain *sd, int cpu)
 {
        struct sched_domain *child = sd->child;
        struct sched_group *group, *sdg = sd->groups;
-       unsigned long power, power_orig;
+       unsigned long capacity, capacity_orig;
        unsigned long interval;
 
        interval = msecs_to_jiffies(sd->balance_interval);
        interval = clamp(interval, 1UL, max_load_balance_interval);
-       sdg->sgp->next_update = jiffies + interval;
+       sdg->sgc->next_update = jiffies + interval;
 
        if (!child) {
-               update_cpu_power(sd, cpu);
+               update_cpu_capacity(sd, cpu);
                return;
        }
 
-       power_orig = power = 0;
+       capacity_orig = capacity = 0;
 
        if (child->flags & SD_OVERLAP) {
                /*
@@ -5723,31 +5726,31 @@ void update_group_power(struct sched_domain *sd, int cpu)
                 */
 
                for_each_cpu(cpu, sched_group_cpus(sdg)) {
-                       struct sched_group_power *sgp;
+                       struct sched_group_capacity *sgc;
                        struct rq *rq = cpu_rq(cpu);
 
                        /*
-                        * build_sched_domains() -> init_sched_groups_power()
+                        * build_sched_domains() -> init_sched_groups_capacity()
                         * gets here before we've attached the domains to the
                         * runqueues.
                         *
-                        * Use power_of(), which is set irrespective of domains
-                        * in update_cpu_power().
+                        * Use capacity_of(), which is set irrespective of domains
+                        * in update_cpu_capacity().
                         *
-                        * This avoids power/power_orig from being 0 and
+                        * This avoids capacity/capacity_orig from being 0 and
                         * causing divide-by-zero issues on boot.
                         *
-                        * Runtime updates will correct power_orig.
+                        * Runtime updates will correct capacity_orig.
                         */
                        if (unlikely(!rq->sd)) {
-                               power_orig += power_of(cpu);
-                               power += power_of(cpu);
+                               capacity_orig += capacity_of(cpu);
+                               capacity += capacity_of(cpu);
                                continue;
                        }
 
-                       sgp = rq->sd->groups->sgp;
-                       power_orig += sgp->power_orig;
-                       power += sgp->power;
+                       sgc = rq->sd->groups->sgc;
+                       capacity_orig += sgc->capacity_orig;
+                       capacity += sgc->capacity;
                }
        } else  {
                /*
@@ -5757,14 +5760,14 @@ void update_group_power(struct sched_domain *sd, int cpu)
 
                group = child->groups;
                do {
-                       power_orig += group->sgp->power_orig;
-                       power += group->sgp->power;
+                       capacity_orig += group->sgc->capacity_orig;
+                       capacity += group->sgc->capacity;
                        group = group->next;
                } while (group != child->groups);
        }
 
-       sdg->sgp->power_orig = power_orig;
-       sdg->sgp->power = power;
+       sdg->sgc->capacity_orig = capacity_orig;
+       sdg->sgc->capacity = capacity;
 }
 
 /*
@@ -5778,15 +5781,15 @@ static inline int
 fix_small_capacity(struct sched_domain *sd, struct sched_group *group)
 {
        /*
-        * Only siblings can have significantly less than SCHED_POWER_SCALE
+        * Only siblings can have significantly less than SCHED_CAPACITY_SCALE
         */
-       if (!(sd->flags & SD_SHARE_CPUPOWER))
+       if (!(sd->flags & SD_SHARE_CPUCAPACITY))
                return 0;
 
        /*
-        * If ~90% of the cpu_power is still there, we're good.
+        * If ~90% of the cpu_capacity is still there, we're good.
         */
-       if (group->sgp->power * 32 > group->sgp->power_orig * 29)
+       if (group->sgc->capacity * 32 > group->sgc->capacity_orig * 29)
                return 1;
 
        return 0;
@@ -5823,34 +5826,35 @@ fix_small_capacity(struct sched_domain *sd, struct sched_group *group)
 
 static inline int sg_imbalanced(struct sched_group *group)
 {
-       return group->sgp->imbalance;
+       return group->sgc->imbalance;
 }
 
 /*
- * Compute the group capacity.
+ * Compute the group capacity factor.
  *
- * Avoid the issue where N*frac(smt_power) >= 1 creates 'phantom' cores by
+ * Avoid the issue where N*frac(smt_capacity) >= 1 creates 'phantom' cores by
  * first dividing out the smt factor and computing the actual number of cores
- * and limit power unit capacity with that.
+ * and limit unit capacity with that.
  */
-static inline int sg_capacity(struct lb_env *env, struct sched_group *group)
+static inline int sg_capacity_factor(struct lb_env *env, struct sched_group *group)
 {
-       unsigned int capacity, smt, cpus;
-       unsigned int power, power_orig;
+       unsigned int capacity_factor, smt, cpus;
+       unsigned int capacity, capacity_orig;
 
-       power = group->sgp->power;
-       power_orig = group->sgp->power_orig;
+       capacity = group->sgc->capacity;
+       capacity_orig = group->sgc->capacity_orig;
        cpus = group->group_weight;
 
-       /* smt := ceil(cpus / power), assumes: 1 < smt_power < 2 */
-       smt = DIV_ROUND_UP(SCHED_POWER_SCALE * cpus, power_orig);
-       capacity = cpus / smt; /* cores */
+       /* smt := ceil(cpus / capacity), assumes: 1 < smt_capacity < 2 */
+       smt = DIV_ROUND_UP(SCHED_CAPACITY_SCALE * cpus, capacity_orig);
+       capacity_factor = cpus / smt; /* cores */
 
-       capacity = min_t(unsigned, capacity, DIV_ROUND_CLOSEST(power, SCHED_POWER_SCALE));
-       if (!capacity)
-               capacity = fix_small_capacity(env->sd, group);
+       capacity_factor = min_t(unsigned,
+               capacity_factor, DIV_ROUND_CLOSEST(capacity, SCHED_CAPACITY_SCALE));
+       if (!capacity_factor)
+               capacity_factor = fix_small_capacity(env->sd, group);
 
-       return capacity;
+       return capacity_factor;
 }
 
 /**
@@ -5890,9 +5894,9 @@ static inline void update_sg_lb_stats(struct lb_env *env,
                        sgs->idle_cpus++;
        }
 
-       /* Adjust by relative CPU power of the group */
-       sgs->group_power = group->sgp->power;
-       sgs->avg_load = (sgs->group_load*SCHED_POWER_SCALE) / sgs->group_power;
+       /* Adjust by relative CPU capacity of the group */
+       sgs->group_capacity = group->sgc->capacity;
+       sgs->avg_load = (sgs->group_load*SCHED_CAPACITY_SCALE) / sgs->group_capacity;
 
        if (sgs->sum_nr_running)
                sgs->load_per_task = sgs->sum_weighted_load / sgs->sum_nr_running;
@@ -5900,10 +5904,10 @@ static inline void update_sg_lb_stats(struct lb_env *env,
        sgs->group_weight = group->group_weight;
 
        sgs->group_imb = sg_imbalanced(group);
-       sgs->group_capacity = sg_capacity(env, group);
+       sgs->group_capacity_factor = sg_capacity_factor(env, group);
 
-       if (sgs->group_capacity > sgs->sum_nr_running)
-               sgs->group_has_capacity = 1;
+       if (sgs->group_capacity_factor > sgs->sum_nr_running)
+               sgs->group_has_free_capacity = 1;
 }
 
 /**
@@ -5927,7 +5931,7 @@ static bool update_sd_pick_busiest(struct lb_env *env,
        if (sgs->avg_load <= sds->busiest_stat.avg_load)
                return false;
 
-       if (sgs->sum_nr_running > sgs->group_capacity)
+       if (sgs->sum_nr_running > sgs->group_capacity_factor)
                return true;
 
        if (sgs->group_imb)
@@ -6007,8 +6011,8 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd
                        sgs = &sds->local_stat;
 
                        if (env->idle != CPU_NEWLY_IDLE ||
-                           time_after_eq(jiffies, sg->sgp->next_update))
-                               update_group_power(env->sd, env->dst_cpu);
+                           time_after_eq(jiffies, sg->sgc->next_update))
+                               update_group_capacity(env->sd, env->dst_cpu);
                }
 
                update_sg_lb_stats(env, sg, load_idx, local_group, sgs);
@@ -6018,17 +6022,17 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd
 
                /*
                 * In case the child domain prefers tasks go to siblings
-                * first, lower the sg capacity to one so that we'll try
+                * first, lower the sg capacity factor to one so that we'll try
                 * and move all the excess tasks away. We lower the capacity
                 * of a group only if the local group has the capacity to fit
-                * these excess tasks, i.e. nr_running < group_capacity. The
+                * these excess tasks, i.e. nr_running < group_capacity_factor. The
                 * extra check prevents the case where you always pull from the
                 * heaviest group when it is already under-utilized (possible
                 * with a large weight task outweighs the tasks on the system).
                 */
                if (prefer_sibling && sds->local &&
-                   sds->local_stat.group_has_capacity)
-                       sgs->group_capacity = min(sgs->group_capacity, 1U);
+                   sds->local_stat.group_has_free_capacity)
+                       sgs->group_capacity_factor = min(sgs->group_capacity_factor, 1U);
 
                if (update_sd_pick_busiest(env, sds, sg, sgs)) {
                        sds->busiest = sg;
@@ -6038,7 +6042,7 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd
 next_group:
                /* Now, start updating sd_lb_stats */
                sds->total_load += sgs->group_load;
-               sds->total_pwr += sgs->group_power;
+               sds->total_capacity += sgs->group_capacity;
 
                sg = sg->next;
        } while (sg != env->sd->groups);
@@ -6085,8 +6089,8 @@ static int check_asym_packing(struct lb_env *env, struct sd_lb_stats *sds)
                return 0;
 
        env->imbalance = DIV_ROUND_CLOSEST(
-               sds->busiest_stat.avg_load * sds->busiest_stat.group_power,
-               SCHED_POWER_SCALE);
+               sds->busiest_stat.avg_load * sds->busiest_stat.group_capacity,
+               SCHED_CAPACITY_SCALE);
 
        return 1;
 }
@@ -6101,7 +6105,7 @@ static int check_asym_packing(struct lb_env *env, struct sd_lb_stats *sds)
 static inline
 void fix_small_imbalance(struct lb_env *env, struct sd_lb_stats *sds)
 {
-       unsigned long tmp, pwr_now = 0, pwr_move = 0;
+       unsigned long tmp, capa_now = 0, capa_move = 0;
        unsigned int imbn = 2;
        unsigned long scaled_busy_load_per_task;
        struct sg_lb_stats *local, *busiest;
@@ -6115,8 +6119,8 @@ void fix_small_imbalance(struct lb_env *env, struct sd_lb_stats *sds)
                imbn = 1;
 
        scaled_busy_load_per_task =
-               (busiest->load_per_task * SCHED_POWER_SCALE) /
-               busiest->group_power;
+               (busiest->load_per_task * SCHED_CAPACITY_SCALE) /
+               busiest->group_capacity;
 
        if (busiest->avg_load + scaled_busy_load_per_task >=
            local->avg_load + (scaled_busy_load_per_task * imbn)) {
@@ -6126,38 +6130,38 @@ void fix_small_imbalance(struct lb_env *env, struct sd_lb_stats *sds)
 
        /*
         * OK, we don't have enough imbalance to justify moving tasks,
-        * however we may be able to increase total CPU power used by
+        * however we may be able to increase total CPU capacity used by
         * moving them.
         */
 
-       pwr_now += busiest->group_power *
+       capa_now += busiest->group_capacity *
                        min(busiest->load_per_task, busiest->avg_load);
-       pwr_now += local->group_power *
+       capa_now += local->group_capacity *
                        min(local->load_per_task, local->avg_load);
-       pwr_now /= SCHED_POWER_SCALE;
+       capa_now /= SCHED_CAPACITY_SCALE;
 
        /* Amount of load we'd subtract */
        if (busiest->avg_load > scaled_busy_load_per_task) {
-               pwr_move += busiest->group_power *
+               capa_move += busiest->group_capacity *
                            min(busiest->load_per_task,
                                busiest->avg_load - scaled_busy_load_per_task);
        }
 
        /* Amount of load we'd add */
-       if (busiest->avg_load * busiest->group_power <
-           busiest->load_per_task * SCHED_POWER_SCALE) {
-               tmp = (busiest->avg_load * busiest->group_power) /
-                     local->group_power;
+       if (busiest->avg_load * busiest->group_capacity <
+           busiest->load_per_task * SCHED_CAPACITY_SCALE) {
+               tmp = (busiest->avg_load * busiest->group_capacity) /
+                     local->group_capacity;
        } else {
-               tmp = (busiest->load_per_task * SCHED_POWER_SCALE) /
-                     local->group_power;
+               tmp = (busiest->load_per_task * SCHED_CAPACITY_SCALE) /
+                     local->group_capacity;
        }
-       pwr_move += local->group_power *
+       capa_move += local->group_capacity *
                    min(local->load_per_task, local->avg_load + tmp);
-       pwr_move /= SCHED_POWER_SCALE;
+       capa_move /= SCHED_CAPACITY_SCALE;
 
        /* Move if we gain throughput */
-       if (pwr_move > pwr_now)
+       if (capa_move > capa_now)
                env->imbalance = busiest->load_per_task;
 }
 
@@ -6187,7 +6191,7 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s
        /*
         * In the presence of smp nice balancing, certain scenarios can have
         * max load less than avg load(as we skip the groups at or below
-        * its cpu_power, while calculating max_load..)
+        * its cpu_capacity, while calculating max_load..)
         */
        if (busiest->avg_load <= sds->avg_load ||
            local->avg_load >= sds->avg_load) {
@@ -6202,10 +6206,10 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s
                 * have to drop below capacity to reach cpu-load equilibrium.
                 */
                load_above_capacity =
-                       (busiest->sum_nr_running - busiest->group_capacity);
+                       (busiest->sum_nr_running - busiest->group_capacity_factor);
 
-               load_above_capacity *= (SCHED_LOAD_SCALE * SCHED_POWER_SCALE);
-               load_above_capacity /= busiest->group_power;
+               load_above_capacity *= (SCHED_LOAD_SCALE * SCHED_CAPACITY_SCALE);
+               load_above_capacity /= busiest->group_capacity;
        }
 
        /*
@@ -6220,9 +6224,9 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s
 
        /* How much load to actually move to equalise the imbalance */
        env->imbalance = min(
-               max_pull * busiest->group_power,
-               (sds->avg_load - local->avg_load) * local->group_power
-       ) / SCHED_POWER_SCALE;
+               max_pull * busiest->group_capacity,
+               (sds->avg_load - local->avg_load) * local->group_capacity
+       ) / SCHED_CAPACITY_SCALE;
 
        /*
         * if *imbalance is less than the average load per runnable task
@@ -6276,7 +6280,8 @@ static struct sched_group *find_busiest_group(struct lb_env *env)
        if (!sds.busiest || busiest->sum_nr_running == 0)
                goto out_balanced;
 
-       sds.avg_load = (SCHED_POWER_SCALE * sds.total_load) / sds.total_pwr;
+       sds.avg_load = (SCHED_CAPACITY_SCALE * sds.total_load)
+                                               / sds.total_capacity;
 
        /*
         * If the busiest group is imbalanced the below checks don't
@@ -6287,8 +6292,8 @@ static struct sched_group *find_busiest_group(struct lb_env *env)
                goto force_balance;
 
        /* SD_BALANCE_NEWIDLE trumps SMP nice when underutilized */
-       if (env->idle == CPU_NEWLY_IDLE && local->group_has_capacity &&
-           !busiest->group_has_capacity)
+       if (env->idle == CPU_NEWLY_IDLE && local->group_has_free_capacity &&
+           !busiest->group_has_free_capacity)
                goto force_balance;
 
        /*
@@ -6342,11 +6347,11 @@ static struct rq *find_busiest_queue(struct lb_env *env,
                                     struct sched_group *group)
 {
        struct rq *busiest = NULL, *rq;
-       unsigned long busiest_load = 0, busiest_power = 1;
+       unsigned long busiest_load = 0, busiest_capacity = 1;
        int i;
 
        for_each_cpu_and(i, sched_group_cpus(group), env->cpus) {
-               unsigned long power, capacity, wl;
+               unsigned long capacity, capacity_factor, wl;
                enum fbq_type rt;
 
                rq = cpu_rq(i);
@@ -6374,34 +6379,34 @@ static struct rq *find_busiest_queue(struct lb_env *env,
                if (rt > env->fbq_type)
                        continue;
 
-               power = power_of(i);
-               capacity = DIV_ROUND_CLOSEST(power, SCHED_POWER_SCALE);
-               if (!capacity)
-                       capacity = fix_small_capacity(env->sd, group);
+               capacity = capacity_of(i);
+               capacity_factor = DIV_ROUND_CLOSEST(capacity, SCHED_CAPACITY_SCALE);
+               if (!capacity_factor)
+                       capacity_factor = fix_small_capacity(env->sd, group);
 
                wl = weighted_cpuload(i);
 
                /*
                 * When comparing with imbalance, use weighted_cpuload()
-                * which is not scaled with the cpu power.
+                * which is not scaled with the cpu capacity.
                 */
-               if (capacity && rq->nr_running == 1 && wl > env->imbalance)
+               if (capacity_factor && rq->nr_running == 1 && wl > env->imbalance)
                        continue;
 
                /*
                 * For the load comparisons with the other cpu's, consider
-                * the weighted_cpuload() scaled with the cpu power, so that
-                * the load can be moved away from the cpu that is potentially
-                * running at a lower capacity.
+                * the weighted_cpuload() scaled with the cpu capacity, so
+                * that the load can be moved away from the cpu that is
+                * potentially running at a lower capacity.
                 *
-                * Thus we're looking for max(wl_i / power_i), crosswise
+                * Thus we're looking for max(wl_i / capacity_i), crosswise
                 * multiplication to rid ourselves of the division works out
-                * to: wl_i * power_j > wl_j * power_i;  where j is our
-                * previous maximum.
+                * to: wl_i * capacity_j > wl_j * capacity_i;  where j is
+                * our previous maximum.
                 */
-               if (wl * busiest_power > busiest_load * power) {
+               if (wl * busiest_capacity > busiest_load * capacity) {
                        busiest_load = wl;
-                       busiest_power = power;
+                       busiest_capacity = capacity;
                        busiest = rq;
                }
        }
@@ -6609,7 +6614,7 @@ more_balance:
                 * We failed to reach balance because of affinity.
                 */
                if (sd_parent) {
-                       int *group_imbalance = &sd_parent->groups->sgp->imbalance;
+                       int *group_imbalance = &sd_parent->groups->sgc->imbalance;
 
                        if ((env.flags & LBF_SOME_PINNED) && env.imbalance > 0) {
                                *group_imbalance = 1;
@@ -6996,7 +7001,7 @@ static inline void set_cpu_sd_state_busy(void)
                goto unlock;
        sd->nohz_idle = 0;
 
-       atomic_inc(&sd->groups->sgp->nr_busy_cpus);
+       atomic_inc(&sd->groups->sgc->nr_busy_cpus);
 unlock:
        rcu_read_unlock();
 }
@@ -7013,7 +7018,7 @@ void set_cpu_sd_state_idle(void)
                goto unlock;
        sd->nohz_idle = 1;
 
-       atomic_dec(&sd->groups->sgp->nr_busy_cpus);
+       atomic_dec(&sd->groups->sgc->nr_busy_cpus);
 unlock:
        rcu_read_unlock();
 }
@@ -7192,12 +7197,17 @@ static void nohz_idle_balance(struct rq *this_rq, enum cpu_idle_type idle)
 
                rq = cpu_rq(balance_cpu);
 
-               raw_spin_lock_irq(&rq->lock);
-               update_rq_clock(rq);
-               update_idle_cpu_load(rq);
-               raw_spin_unlock_irq(&rq->lock);
-
-               rebalance_domains(rq, CPU_IDLE);
+               /*
+                * If time for next balance is due,
+                * do the balance.
+                */
+               if (time_after_eq(jiffies, rq->next_balance)) {
+                       raw_spin_lock_irq(&rq->lock);
+                       update_rq_clock(rq);
+                       update_idle_cpu_load(rq);
+                       raw_spin_unlock_irq(&rq->lock);
+                       rebalance_domains(rq, CPU_IDLE);
+               }
 
                if (time_after(this_rq->next_balance, rq->next_balance))
                        this_rq->next_balance = rq->next_balance;
@@ -7212,7 +7222,7 @@ end:
  * of an idle cpu is the system.
  *   - This rq has more than one task.
  *   - At any scheduler domain level, this cpu's scheduler group has multiple
- *     busy cpu's exceeding the group's power.
+ *     busy cpu's exceeding the group's capacity.
  *   - For SD_ASYM_PACKING, if the lower numbered cpu's in the scheduler
  *     domain span are idle.
  */
@@ -7220,7 +7230,7 @@ static inline int nohz_kick_needed(struct rq *rq)
 {
        unsigned long now = jiffies;
        struct sched_domain *sd;
-       struct sched_group_power *sgp;
+       struct sched_group_capacity *sgc;
        int nr_busy, cpu = rq->cpu;
 
        if (unlikely(rq->idle_balance))
@@ -7250,8 +7260,8 @@ static inline int nohz_kick_needed(struct rq *rq)
        sd = rcu_dereference(per_cpu(sd_busy, cpu));
 
        if (sd) {
-               sgp = sd->groups->sgp;
-               nr_busy = atomic_read(&sgp->nr_busy_cpus);
+               sgc = sd->groups->sgc;
+               nr_busy = atomic_read(&sgc->nr_busy_cpus);
 
                if (nr_busy > 1)
                        goto need_kick_unlock;
index 5716929a2e3a4ad2d492ad2de0c3431291ea81a1..90284d117fe65ffc7ee1de7127995a750c84df92 100644 (file)
@@ -37,18 +37,18 @@ SCHED_FEAT(CACHE_HOT_BUDDY, true)
 SCHED_FEAT(WAKEUP_PREEMPTION, true)
 
 /*
- * Use arch dependent cpu power functions
+ * Use arch dependent cpu capacity functions
  */
-SCHED_FEAT(ARCH_POWER, true)
+SCHED_FEAT(ARCH_CAPACITY, true)
 
 SCHED_FEAT(HRTICK, false)
 SCHED_FEAT(DOUBLE_TICK, false)
 SCHED_FEAT(LB_BIAS, true)
 
 /*
- * Decrement CPU power based on time not spent running tasks
+ * Decrement CPU capacity based on time not spent running tasks
  */
-SCHED_FEAT(NONTASK_POWER, true)
+SCHED_FEAT(NONTASK_CAPACITY, true)
 
 /*
  * Queue remote wakeups on the target CPU and process them
index 25b9423abce9fa54052abb49946ad6561f1dfed7..cf009fb0bc25b1427683d614a3a40ad98ad0c958 100644 (file)
@@ -12,6 +12,8 @@
 
 #include <trace/events/power.h>
 
+#include "sched.h"
+
 static int __read_mostly cpu_idle_force_poll;
 
 void cpu_idle_poll_ctrl(bool enable)
@@ -67,6 +69,10 @@ void __weak arch_cpu_idle(void)
  * cpuidle_idle_call - the main idle function
  *
  * NOTE: no locks or semaphores should be used here
+ *
+ * On archs that support TIF_POLLING_NRFLAG, is called with polling
+ * set, and it returns with polling set.  If it ever stops polling, it
+ * must clear the polling bit.
  */
 static void cpuidle_idle_call(void)
 {
@@ -175,10 +181,22 @@ exit_idle:
 
 /*
  * Generic idle loop implementation
+ *
+ * Called with polling cleared.
  */
 static void cpu_idle_loop(void)
 {
        while (1) {
+               /*
+                * If the arch has a polling bit, we maintain an invariant:
+                *
+                * Our polling bit is clear if we're not scheduled (i.e. if
+                * rq->curr != rq->idle).  This means that, if rq->idle has
+                * the polling bit set, then setting need_resched is
+                * guaranteed to cause the cpu to reschedule.
+                */
+
+               __current_set_polling();
                tick_nohz_idle_enter();
 
                while (!need_resched()) {
@@ -218,6 +236,17 @@ static void cpu_idle_loop(void)
                 */
                preempt_set_need_resched();
                tick_nohz_idle_exit();
+               __current_clr_polling();
+
+               /*
+                * We promise to call sched_ttwu_pending and reschedule
+                * if need_resched is set while polling is set.  That
+                * means that clearing polling needs to be visible
+                * before doing these things.
+                */
+               smp_mb__after_atomic();
+
+               sched_ttwu_pending();
                schedule_preempt_disabled();
        }
 }
@@ -239,7 +268,6 @@ void cpu_startup_entry(enum cpuhp_state state)
         */
        boot_init_stack_canary();
 #endif
-       __current_set_polling();
        arch_cpu_idle_prepare();
        cpu_idle_loop();
 }
index b3512f1afce9361588a8ccb50f7ad75540122426..a49083192c64c306952c752ec6b3a05a0df7205d 100644 (file)
@@ -918,7 +918,6 @@ static void update_curr_rt(struct rq *rq)
 {
        struct task_struct *curr = rq->curr;
        struct sched_rt_entity *rt_se = &curr->rt;
-       struct rt_rq *rt_rq = rt_rq_of_se(rt_se);
        u64 delta_exec;
 
        if (curr->sched_class != &rt_sched_class)
@@ -943,7 +942,7 @@ static void update_curr_rt(struct rq *rq)
                return;
 
        for_each_sched_rt_entity(rt_se) {
-               rt_rq = rt_rq_of_se(rt_se);
+               struct rt_rq *rt_rq = rt_rq_of_se(rt_se);
 
                if (sched_rt_runtime(rt_rq) != RUNTIME_INF) {
                        raw_spin_lock(&rt_rq->rt_runtime_lock);
index e47679b04d167b8cdc2abf91326f7d52243b3996..31cc02ebc54ed82f5bf3f62fae879a1c0343a97d 100644 (file)
@@ -567,7 +567,7 @@ struct rq {
        struct root_domain *rd;
        struct sched_domain *sd;
 
-       unsigned long cpu_power;
+       unsigned long cpu_capacity;
 
        unsigned char idle_balance;
        /* For active balancing */
@@ -670,6 +670,8 @@ extern int migrate_swap(struct task_struct *, struct task_struct *);
 
 #ifdef CONFIG_SMP
 
+extern void sched_ttwu_pending(void);
+
 #define rcu_dereference_check_sched_domain(p) \
        rcu_dereference_check((p), \
                              lockdep_is_held(&sched_domains_mutex))
@@ -728,15 +730,15 @@ DECLARE_PER_CPU(struct sched_domain *, sd_numa);
 DECLARE_PER_CPU(struct sched_domain *, sd_busy);
 DECLARE_PER_CPU(struct sched_domain *, sd_asym);
 
-struct sched_group_power {
+struct sched_group_capacity {
        atomic_t ref;
        /*
-        * CPU power of this group, SCHED_LOAD_SCALE being max power for a
-        * single CPU.
+        * CPU capacity of this group, SCHED_LOAD_SCALE being max capacity
+        * for a single CPU.
         */
-       unsigned int power, power_orig;
+       unsigned int capacity, capacity_orig;
        unsigned long next_update;
-       int imbalance; /* XXX unrelated to power but shared group state */
+       int imbalance; /* XXX unrelated to capacity but shared group state */
        /*
         * Number of busy cpus in this group.
         */
@@ -750,7 +752,7 @@ struct sched_group {
        atomic_t ref;
 
        unsigned int group_weight;
-       struct sched_group_power *sgp;
+       struct sched_group_capacity *sgc;
 
        /*
         * The CPUs this group covers.
@@ -773,7 +775,7 @@ static inline struct cpumask *sched_group_cpus(struct sched_group *sg)
  */
 static inline struct cpumask *sched_group_mask(struct sched_group *sg)
 {
-       return to_cpumask(sg->sgp->cpumask);
+       return to_cpumask(sg->sgc->cpumask);
 }
 
 /**
@@ -787,6 +789,10 @@ static inline unsigned int group_first_cpu(struct sched_group *group)
 
 extern int group_balance_cpu(struct sched_group *sg);
 
+#else
+
+static inline void sched_ttwu_pending(void) { }
+
 #endif /* CONFIG_SMP */
 
 #include "stats.h"
@@ -1167,7 +1173,7 @@ extern const struct sched_class idle_sched_class;
 
 #ifdef CONFIG_SMP
 
-extern void update_group_power(struct sched_domain *sd, int cpu);
+extern void update_group_capacity(struct sched_domain *sd, int cpu);
 
 extern void trigger_load_balance(struct rq *rq);
 
index f6d76bebe69f0f1adae6507d6c0d0ed0e034cb61..301bbc24739c9ff8fd58fe95149ad242da8e50b7 100644 (file)
@@ -54,8 +54,7 @@
 struct seccomp_filter {
        atomic_t usage;
        struct seccomp_filter *prev;
-       unsigned short len;  /* Instruction count */
-       struct sock_filter_int insnsi[];
+       struct sk_filter *prog;
 };
 
 /* Limit any path through the tree to 256KB worth of instructions. */
@@ -104,60 +103,59 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen)
                u32 k = ftest->k;
 
                switch (code) {
-               case BPF_S_LD_W_ABS:
+               case BPF_LD | BPF_W | BPF_ABS:
                        ftest->code = BPF_LDX | BPF_W | BPF_ABS;
                        /* 32-bit aligned and not out of bounds. */
                        if (k >= sizeof(struct seccomp_data) || k & 3)
                                return -EINVAL;
                        continue;
-               case BPF_S_LD_W_LEN:
+               case BPF_LD | BPF_W | BPF_LEN:
                        ftest->code = BPF_LD | BPF_IMM;
                        ftest->k = sizeof(struct seccomp_data);
                        continue;
-               case BPF_S_LDX_W_LEN:
+               case BPF_LDX | BPF_W | BPF_LEN:
                        ftest->code = BPF_LDX | BPF_IMM;
                        ftest->k = sizeof(struct seccomp_data);
                        continue;
                /* Explicitly include allowed calls. */
-               case BPF_S_RET_K:
-               case BPF_S_RET_A:
-               case BPF_S_ALU_ADD_K:
-               case BPF_S_ALU_ADD_X:
-               case BPF_S_ALU_SUB_K:
-               case BPF_S_ALU_SUB_X:
-               case BPF_S_ALU_MUL_K:
-               case BPF_S_ALU_MUL_X:
-               case BPF_S_ALU_DIV_X:
-               case BPF_S_ALU_AND_K:
-               case BPF_S_ALU_AND_X:
-               case BPF_S_ALU_OR_K:
-               case BPF_S_ALU_OR_X:
-               case BPF_S_ALU_XOR_K:
-               case BPF_S_ALU_XOR_X:
-               case BPF_S_ALU_LSH_K:
-               case BPF_S_ALU_LSH_X:
-               case BPF_S_ALU_RSH_K:
-               case BPF_S_ALU_RSH_X:
-               case BPF_S_ALU_NEG:
-               case BPF_S_LD_IMM:
-               case BPF_S_LDX_IMM:
-               case BPF_S_MISC_TAX:
-               case BPF_S_MISC_TXA:
-               case BPF_S_ALU_DIV_K:
-               case BPF_S_LD_MEM:
-               case BPF_S_LDX_MEM:
-               case BPF_S_ST:
-               case BPF_S_STX:
-               case BPF_S_JMP_JA:
-               case BPF_S_JMP_JEQ_K:
-               case BPF_S_JMP_JEQ_X:
-               case BPF_S_JMP_JGE_K:
-               case BPF_S_JMP_JGE_X:
-               case BPF_S_JMP_JGT_K:
-               case BPF_S_JMP_JGT_X:
-               case BPF_S_JMP_JSET_K:
-               case BPF_S_JMP_JSET_X:
-                       sk_decode_filter(ftest, ftest);
+               case BPF_RET | BPF_K:
+               case BPF_RET | BPF_A:
+               case BPF_ALU | BPF_ADD | BPF_K:
+               case BPF_ALU | BPF_ADD | BPF_X:
+               case BPF_ALU | BPF_SUB | BPF_K:
+               case BPF_ALU | BPF_SUB | BPF_X:
+               case BPF_ALU | BPF_MUL | BPF_K:
+               case BPF_ALU | BPF_MUL | BPF_X:
+               case BPF_ALU | BPF_DIV | BPF_K:
+               case BPF_ALU | BPF_DIV | BPF_X:
+               case BPF_ALU | BPF_AND | BPF_K:
+               case BPF_ALU | BPF_AND | BPF_X:
+               case BPF_ALU | BPF_OR | BPF_K:
+               case BPF_ALU | BPF_OR | BPF_X:
+               case BPF_ALU | BPF_XOR | BPF_K:
+               case BPF_ALU | BPF_XOR | BPF_X:
+               case BPF_ALU | BPF_LSH | BPF_K:
+               case BPF_ALU | BPF_LSH | BPF_X:
+               case BPF_ALU | BPF_RSH | BPF_K:
+               case BPF_ALU | BPF_RSH | BPF_X:
+               case BPF_ALU | BPF_NEG:
+               case BPF_LD | BPF_IMM:
+               case BPF_LDX | BPF_IMM:
+               case BPF_MISC | BPF_TAX:
+               case BPF_MISC | BPF_TXA:
+               case BPF_LD | BPF_MEM:
+               case BPF_LDX | BPF_MEM:
+               case BPF_ST:
+               case BPF_STX:
+               case BPF_JMP | BPF_JA:
+               case BPF_JMP | BPF_JEQ | BPF_K:
+               case BPF_JMP | BPF_JEQ | BPF_X:
+               case BPF_JMP | BPF_JGE | BPF_K:
+               case BPF_JMP | BPF_JGE | BPF_X:
+               case BPF_JMP | BPF_JGT | BPF_K:
+               case BPF_JMP | BPF_JGT | BPF_X:
+               case BPF_JMP | BPF_JSET | BPF_K:
+               case BPF_JMP | BPF_JSET | BPF_X:
                        continue;
                default:
                        return -EINVAL;
@@ -189,7 +187,8 @@ static u32 seccomp_run_filters(int syscall)
         * value always takes priority (ignoring the DATA).
         */
        for (f = current->seccomp.filter; f; f = f->prev) {
-               u32 cur_ret = sk_run_filter_int_seccomp(&sd, f->insnsi);
+               u32 cur_ret = SK_RUN_FILTER(f->prog, (void *)&sd);
+
                if ((cur_ret & SECCOMP_RET_ACTION) < (ret & SECCOMP_RET_ACTION))
                        ret = cur_ret;
        }
@@ -215,7 +214,7 @@ static long seccomp_attach_filter(struct sock_fprog *fprog)
                return -EINVAL;
 
        for (filter = current->seccomp.filter; filter; filter = filter->prev)
-               total_insns += filter->len + 4;  /* include a 4 instr penalty */
+               total_insns += filter->prog->len + 4;  /* include a 4 instr penalty */
        if (total_insns > MAX_INSNS_PER_PATH)
                return -ENOMEM;
 
@@ -256,19 +255,25 @@ static long seccomp_attach_filter(struct sock_fprog *fprog)
 
        /* Allocate a new seccomp_filter */
        ret = -ENOMEM;
-       filter = kzalloc(sizeof(struct seccomp_filter) +
-                        sizeof(struct sock_filter_int) * new_len,
+       filter = kzalloc(sizeof(struct seccomp_filter),
                         GFP_KERNEL|__GFP_NOWARN);
        if (!filter)
                goto free_prog;
 
-       ret = sk_convert_filter(fp, fprog->len, filter->insnsi, &new_len);
-       if (ret)
+       filter->prog = kzalloc(sk_filter_size(new_len),
+                              GFP_KERNEL|__GFP_NOWARN);
+       if (!filter->prog)
                goto free_filter;
+
+       ret = sk_convert_filter(fp, fprog->len, filter->prog->insnsi, &new_len);
+       if (ret)
+               goto free_filter_prog;
        kfree(fp);
 
        atomic_set(&filter->usage, 1);
-       filter->len = new_len;
+       filter->prog->len = new_len;
+
+       sk_filter_select_runtime(filter->prog);
 
        /*
         * If there is an existing filter, make it the prev and don't drop its
@@ -278,6 +283,8 @@ static long seccomp_attach_filter(struct sock_fprog *fprog)
        current->seccomp.filter = filter;
        return 0;
 
+free_filter_prog:
+       kfree(filter->prog);
 free_filter:
        kfree(filter);
 free_prog:
@@ -330,6 +337,7 @@ void put_seccomp_filter(struct task_struct *tsk)
        while (orig && atomic_dec_and_test(&orig->usage)) {
                struct seccomp_filter *freeme = orig;
                orig = orig->prev;
+               sk_filter_free(freeme->prog);
                kfree(freeme);
        }
 }
index db19e3e2aa4bd000d96af8d3380af60148a01b10..ba9ed453c4ed497d62537b019b153fbf47171084 100644 (file)
@@ -2568,11 +2568,11 @@ int proc_do_large_bitmap(struct ctl_table *table, int write,
        bool first = 1;
        size_t left = *lenp;
        unsigned long bitmap_len = table->maxlen;
-       unsigned long *bitmap = (unsigned long *) table->data;
+       unsigned long *bitmap = *(unsigned long **) table->data;
        unsigned long *tmp_bitmap = NULL;
        char tr_a[] = { '-', ',', '\n' }, tr_b[] = { ',', '\n', 0 }, c;
 
-       if (!bitmap_len || !left || (*ppos && !write)) {
+       if (!bitmap || !bitmap_len || !left || (*ppos && !write)) {
                *lenp = 0;
                return 0;
        }
index c634868c2921ca39837c3a36773c36fa4a3273d5..7c56c3d06943060d13ed611da638a09ccfa70ef4 100644 (file)
@@ -543,7 +543,7 @@ static void rb_wake_up_waiters(struct irq_work *work)
  * as data is added to any of the @buffer's cpu buffers. Otherwise
  * it will wait for data to be added to a specific cpu buffer.
  */
-void ring_buffer_wait(struct ring_buffer *buffer, int cpu)
+int ring_buffer_wait(struct ring_buffer *buffer, int cpu)
 {
        struct ring_buffer_per_cpu *cpu_buffer;
        DEFINE_WAIT(wait);
@@ -557,6 +557,8 @@ void ring_buffer_wait(struct ring_buffer *buffer, int cpu)
        if (cpu == RING_BUFFER_ALL_CPUS)
                work = &buffer->irq_work;
        else {
+               if (!cpumask_test_cpu(cpu, buffer->cpumask))
+                       return -ENODEV;
                cpu_buffer = buffer->buffers[cpu];
                work = &cpu_buffer->irq_work;
        }
@@ -591,6 +593,7 @@ void ring_buffer_wait(struct ring_buffer *buffer, int cpu)
                schedule();
 
        finish_wait(&work->waiters, &wait);
+       return 0;
 }
 
 /**
index 16f7038d1f4d7a245a3350cf9b647992072e94e0..384ede3117172fa9e6582ead5c479f42c8f98590 100644 (file)
@@ -1085,13 +1085,13 @@ update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu)
 }
 #endif /* CONFIG_TRACER_MAX_TRACE */
 
-static void wait_on_pipe(struct trace_iterator *iter)
+static int wait_on_pipe(struct trace_iterator *iter)
 {
        /* Iterators are static, they should be filled or empty */
        if (trace_buffer_iter(iter, iter->cpu_file))
-               return;
+               return 0;
 
-       ring_buffer_wait(iter->trace_buffer->buffer, iter->cpu_file);
+       return ring_buffer_wait(iter->trace_buffer->buffer, iter->cpu_file);
 }
 
 #ifdef CONFIG_FTRACE_STARTUP_TEST
@@ -1338,7 +1338,7 @@ static int trace_create_savedcmd(void)
 {
        int ret;
 
-       savedcmd = kmalloc(sizeof(struct saved_cmdlines_buffer), GFP_KERNEL);
+       savedcmd = kmalloc(sizeof(*savedcmd), GFP_KERNEL);
        if (!savedcmd)
                return -ENOMEM;
 
@@ -3840,7 +3840,7 @@ tracing_saved_cmdlines_size_read(struct file *filp, char __user *ubuf,
        int r;
 
        arch_spin_lock(&trace_cmdline_lock);
-       r = sprintf(buf, "%u\n", savedcmd->cmdline_num);
+       r = scnprintf(buf, sizeof(buf), "%u\n", savedcmd->cmdline_num);
        arch_spin_unlock(&trace_cmdline_lock);
 
        return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
@@ -3857,7 +3857,7 @@ static int tracing_resize_saved_cmdlines(unsigned int val)
 {
        struct saved_cmdlines_buffer *s, *savedcmd_temp;
 
-       s = kmalloc(sizeof(struct saved_cmdlines_buffer), GFP_KERNEL);
+       s = kmalloc(sizeof(*s), GFP_KERNEL);
        if (!s)
                return -ENOMEM;
 
@@ -4378,6 +4378,7 @@ tracing_poll_pipe(struct file *filp, poll_table *poll_table)
 static int tracing_wait_pipe(struct file *filp)
 {
        struct trace_iterator *iter = filp->private_data;
+       int ret;
 
        while (trace_empty(iter)) {
 
@@ -4399,10 +4400,13 @@ static int tracing_wait_pipe(struct file *filp)
 
                mutex_unlock(&iter->mutex);
 
-               wait_on_pipe(iter);
+               ret = wait_on_pipe(iter);
 
                mutex_lock(&iter->mutex);
 
+               if (ret)
+                       return ret;
+
                if (signal_pending(current))
                        return -EINTR;
        }
@@ -5327,8 +5331,12 @@ tracing_buffers_read(struct file *filp, char __user *ubuf,
                                goto out_unlock;
                        }
                        mutex_unlock(&trace_types_lock);
-                       wait_on_pipe(iter);
+                       ret = wait_on_pipe(iter);
                        mutex_lock(&trace_types_lock);
+                       if (ret) {
+                               size = ret;
+                               goto out_unlock;
+                       }
                        if (signal_pending(current)) {
                                size = -EINTR;
                                goto out_unlock;
@@ -5538,8 +5546,10 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos,
                        goto out;
                }
                mutex_unlock(&trace_types_lock);
-               wait_on_pipe(iter);
+               ret = wait_on_pipe(iter);
                mutex_lock(&trace_types_lock);
+               if (ret)
+                       goto out;
                if (signal_pending(current)) {
                        ret = -EINTR;
                        goto out;
@@ -6232,22 +6242,25 @@ static int allocate_trace_buffers(struct trace_array *tr, int size)
        return 0;
 }
 
+static void free_trace_buffer(struct trace_buffer *buf)
+{
+       if (buf->buffer) {
+               ring_buffer_free(buf->buffer);
+               buf->buffer = NULL;
+               free_percpu(buf->data);
+               buf->data = NULL;
+       }
+}
+
 static void free_trace_buffers(struct trace_array *tr)
 {
        if (!tr)
                return;
 
-       if (tr->trace_buffer.buffer) {
-               ring_buffer_free(tr->trace_buffer.buffer);
-               tr->trace_buffer.buffer = NULL;
-               free_percpu(tr->trace_buffer.data);
-       }
+       free_trace_buffer(&tr->trace_buffer);
 
 #ifdef CONFIG_TRACER_MAX_TRACE
-       if (tr->max_buffer.buffer) {
-               ring_buffer_free(tr->max_buffer.buffer);
-               tr->max_buffer.buffer = NULL;
-       }
+       free_trace_buffer(&tr->max_buffer);
 #endif
 }
 
index 9e82551dd566057c61a862e7b27b395939d270e3..9258f5a815db4d4fe4a8d314a2593218b3c4e5ce 100644 (file)
@@ -252,7 +252,7 @@ static inline struct trace_array *top_trace_array(void)
 {
        struct trace_array *tr;
 
-       if (list_empty(ftrace_trace_arrays.prev))
+       if (list_empty(&ftrace_trace_arrays))
                return NULL;
 
        tr = list_entry(ftrace_trace_arrays.prev,
index c894614de14d8efdbbc145141d19f9a56af9a984..5d12bb407b44290fb6a8abad49ca5deef36691a0 100644 (file)
@@ -248,8 +248,8 @@ void perf_trace_del(struct perf_event *p_event, int flags)
        tp_event->class->reg(tp_event, TRACE_REG_PERF_DEL, p_event);
 }
 
-__kprobes void *perf_trace_buf_prepare(int size, unsigned short type,
-                                      struct pt_regs *regs, int *rctxp)
+void *perf_trace_buf_prepare(int size, unsigned short type,
+                            struct pt_regs *regs, int *rctxp)
 {
        struct trace_entry *entry;
        unsigned long flags;
@@ -281,6 +281,7 @@ __kprobes void *perf_trace_buf_prepare(int size, unsigned short type,
        return raw_data;
 }
 EXPORT_SYMBOL_GPL(perf_trace_buf_prepare);
+NOKPROBE_SYMBOL(perf_trace_buf_prepare);
 
 #ifdef CONFIG_FUNCTION_TRACER
 static void
index ef2fba1f46b598eae5c7462601a96d98f65ef322..282f6e4e553988bb6b75a974bb62742d0a80a7e2 100644 (file)
@@ -40,27 +40,27 @@ struct trace_kprobe {
        (sizeof(struct probe_arg) * (n)))
 
 
-static __kprobes bool trace_kprobe_is_return(struct trace_kprobe *tk)
+static nokprobe_inline bool trace_kprobe_is_return(struct trace_kprobe *tk)
 {
        return tk->rp.handler != NULL;
 }
 
-static __kprobes const char *trace_kprobe_symbol(struct trace_kprobe *tk)
+static nokprobe_inline const char *trace_kprobe_symbol(struct trace_kprobe *tk)
 {
        return tk->symbol ? tk->symbol : "unknown";
 }
 
-static __kprobes unsigned long trace_kprobe_offset(struct trace_kprobe *tk)
+static nokprobe_inline unsigned long trace_kprobe_offset(struct trace_kprobe *tk)
 {
        return tk->rp.kp.offset;
 }
 
-static __kprobes bool trace_kprobe_has_gone(struct trace_kprobe *tk)
+static nokprobe_inline bool trace_kprobe_has_gone(struct trace_kprobe *tk)
 {
        return !!(kprobe_gone(&tk->rp.kp));
 }
 
-static __kprobes bool trace_kprobe_within_module(struct trace_kprobe *tk,
+static nokprobe_inline bool trace_kprobe_within_module(struct trace_kprobe *tk,
                                                 struct module *mod)
 {
        int len = strlen(mod->name);
@@ -68,7 +68,7 @@ static __kprobes bool trace_kprobe_within_module(struct trace_kprobe *tk,
        return strncmp(mod->name, name, len) == 0 && name[len] == ':';
 }
 
-static __kprobes bool trace_kprobe_is_on_module(struct trace_kprobe *tk)
+static nokprobe_inline bool trace_kprobe_is_on_module(struct trace_kprobe *tk)
 {
        return !!strchr(trace_kprobe_symbol(tk), ':');
 }
@@ -132,19 +132,21 @@ struct symbol_cache *alloc_symbol_cache(const char *sym, long offset)
  * Kprobes-specific fetch functions
  */
 #define DEFINE_FETCH_stack(type)                                       \
-static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\
+static void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,         \
                                          void *offset, void *dest)     \
 {                                                                      \
        *(type *)dest = (type)regs_get_kernel_stack_nth(regs,           \
                                (unsigned int)((unsigned long)offset)); \
-}
+}                                                                      \
+NOKPROBE_SYMBOL(FETCH_FUNC_NAME(stack, type));
+
 DEFINE_BASIC_FETCH_FUNCS(stack)
 /* No string on the stack entry */
 #define fetch_stack_string     NULL
 #define fetch_stack_string_size        NULL
 
 #define DEFINE_FETCH_memory(type)                                      \
-static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\
+static void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,                \
                                          void *addr, void *dest)       \
 {                                                                      \
        type retval;                                                    \
@@ -152,14 +154,16 @@ static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\
                *(type *)dest = 0;                                      \
        else                                                            \
                *(type *)dest = retval;                                 \
-}
+}                                                                      \
+NOKPROBE_SYMBOL(FETCH_FUNC_NAME(memory, type));
+
 DEFINE_BASIC_FETCH_FUNCS(memory)
 /*
  * Fetch a null-terminated string. Caller MUST set *(u32 *)dest with max
  * length and relative data location.
  */
-static __kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,
-                                                     void *addr, void *dest)
+static void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,
+                                           void *addr, void *dest)
 {
        long ret;
        int maxlen = get_rloc_len(*(u32 *)dest);
@@ -193,10 +197,11 @@ static __kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,
                                              get_rloc_offs(*(u32 *)dest));
        }
 }
+NOKPROBE_SYMBOL(FETCH_FUNC_NAME(memory, string));
 
 /* Return the length of string -- including null terminal byte */
-static __kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs,
-                                                       void *addr, void *dest)
+static void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs,
+                                                void *addr, void *dest)
 {
        mm_segment_t old_fs;
        int ret, len = 0;
@@ -219,17 +224,19 @@ static __kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs,
        else
                *(u32 *)dest = len;
 }
+NOKPROBE_SYMBOL(FETCH_FUNC_NAME(memory, string_size));
 
 #define DEFINE_FETCH_symbol(type)                                      \
-__kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs,     \
-                                         void *data, void *dest)       \
+void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs, void *data, void *dest)\
 {                                                                      \
        struct symbol_cache *sc = data;                                 \
        if (sc->addr)                                                   \
                fetch_memory_##type(regs, (void *)sc->addr, dest);      \
        else                                                            \
                *(type *)dest = 0;                                      \
-}
+}                                                                      \
+NOKPROBE_SYMBOL(FETCH_FUNC_NAME(symbol, type));
+
 DEFINE_BASIC_FETCH_FUNCS(symbol)
 DEFINE_FETCH_symbol(string)
 DEFINE_FETCH_symbol(string_size)
@@ -907,7 +914,7 @@ static const struct file_operations kprobe_profile_ops = {
 };
 
 /* Kprobe handler */
-static __kprobes void
+static nokprobe_inline void
 __kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs,
                    struct ftrace_event_file *ftrace_file)
 {
@@ -943,7 +950,7 @@ __kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs,
                                         entry, irq_flags, pc, regs);
 }
 
-static __kprobes void
+static void
 kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs)
 {
        struct event_file_link *link;
@@ -951,9 +958,10 @@ kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs)
        list_for_each_entry_rcu(link, &tk->tp.files, list)
                __kprobe_trace_func(tk, regs, link->file);
 }
+NOKPROBE_SYMBOL(kprobe_trace_func);
 
 /* Kretprobe handler */
-static __kprobes void
+static nokprobe_inline void
 __kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
                       struct pt_regs *regs,
                       struct ftrace_event_file *ftrace_file)
@@ -991,7 +999,7 @@ __kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
                                         entry, irq_flags, pc, regs);
 }
 
-static __kprobes void
+static void
 kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
                     struct pt_regs *regs)
 {
@@ -1000,6 +1008,7 @@ kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
        list_for_each_entry_rcu(link, &tk->tp.files, list)
                __kretprobe_trace_func(tk, ri, regs, link->file);
 }
+NOKPROBE_SYMBOL(kretprobe_trace_func);
 
 /* Event entry printers */
 static enum print_line_t
@@ -1131,7 +1140,7 @@ static int kretprobe_event_define_fields(struct ftrace_event_call *event_call)
 #ifdef CONFIG_PERF_EVENTS
 
 /* Kprobe profile handler */
-static __kprobes void
+static void
 kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs)
 {
        struct ftrace_event_call *call = &tk->tp.call;
@@ -1158,9 +1167,10 @@ kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs)
        store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize);
        perf_trace_buf_submit(entry, size, rctx, 0, 1, regs, head, NULL);
 }
+NOKPROBE_SYMBOL(kprobe_perf_func);
 
 /* Kretprobe profile handler */
-static __kprobes void
+static void
 kretprobe_perf_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
                    struct pt_regs *regs)
 {
@@ -1188,6 +1198,7 @@ kretprobe_perf_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
        store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize);
        perf_trace_buf_submit(entry, size, rctx, 0, 1, regs, head, NULL);
 }
+NOKPROBE_SYMBOL(kretprobe_perf_func);
 #endif /* CONFIG_PERF_EVENTS */
 
 /*
@@ -1196,9 +1207,8 @@ kretprobe_perf_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
  * kprobe_trace_self_tests_init() does enable_trace_probe/disable_trace_probe
  * lockless, but we can't race with this __init function.
  */
-static __kprobes
-int kprobe_register(struct ftrace_event_call *event,
-                   enum trace_reg type, void *data)
+static int kprobe_register(struct ftrace_event_call *event,
+                          enum trace_reg type, void *data)
 {
        struct trace_kprobe *tk = (struct trace_kprobe *)event->data;
        struct ftrace_event_file *file = data;
@@ -1224,8 +1234,7 @@ int kprobe_register(struct ftrace_event_call *event,
        return 0;
 }
 
-static __kprobes
-int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs)
+static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs)
 {
        struct trace_kprobe *tk = container_of(kp, struct trace_kprobe, rp.kp);
 
@@ -1239,9 +1248,10 @@ int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs)
 #endif
        return 0;       /* We don't tweek kernel, so just return 0 */
 }
+NOKPROBE_SYMBOL(kprobe_dispatcher);
 
-static __kprobes
-int kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs)
+static int
+kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs)
 {
        struct trace_kprobe *tk = container_of(ri->rp, struct trace_kprobe, rp);
 
@@ -1255,6 +1265,7 @@ int kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs)
 #endif
        return 0;       /* We don't tweek kernel, so just return 0 */
 }
+NOKPROBE_SYMBOL(kretprobe_dispatcher);
 
 static struct trace_event_functions kretprobe_funcs = {
        .trace          = print_kretprobe_event
index 8364a421b4dfc7bb8935cf4e9c128aa99de0776f..d4b9fc22cd27fb0a87029424afde729a2b843a4d 100644 (file)
@@ -37,13 +37,13 @@ const char *reserved_field_names[] = {
 
 /* Printing  in basic type function template */
 #define DEFINE_BASIC_PRINT_TYPE_FUNC(type, fmt)                                \
-__kprobes int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s,  \
-                                               const char *name,       \
-                                               void *data, void *ent)  \
+int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s, const char *name,  \
+                               void *data, void *ent)                  \
 {                                                                      \
        return trace_seq_printf(s, " %s=" fmt, name, *(type *)data);    \
 }                                                                      \
-const char PRINT_TYPE_FMT_NAME(type)[] = fmt;
+const char PRINT_TYPE_FMT_NAME(type)[] = fmt;                          \
+NOKPROBE_SYMBOL(PRINT_TYPE_FUNC_NAME(type));
 
 DEFINE_BASIC_PRINT_TYPE_FUNC(u8 , "0x%x")
 DEFINE_BASIC_PRINT_TYPE_FUNC(u16, "0x%x")
@@ -55,9 +55,8 @@ DEFINE_BASIC_PRINT_TYPE_FUNC(s32, "%d")
 DEFINE_BASIC_PRINT_TYPE_FUNC(s64, "%Ld")
 
 /* Print type function for string type */
-__kprobes int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s,
-                                                 const char *name,
-                                                 void *data, void *ent)
+int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s, const char *name,
+                                void *data, void *ent)
 {
        int len = *(u32 *)data >> 16;
 
@@ -67,6 +66,7 @@ __kprobes int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s,
                return trace_seq_printf(s, " %s=\"%s\"", name,
                                        (const char *)get_loc_data(data, ent));
 }
+NOKPROBE_SYMBOL(PRINT_TYPE_FUNC_NAME(string));
 
 const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\"";
 
@@ -81,23 +81,24 @@ const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\"";
 
 /* Data fetch function templates */
 #define DEFINE_FETCH_reg(type)                                         \
-__kprobes void FETCH_FUNC_NAME(reg, type)(struct pt_regs *regs,                \
-                                       void *offset, void *dest)       \
+void FETCH_FUNC_NAME(reg, type)(struct pt_regs *regs, void *offset, void *dest)        \
 {                                                                      \
        *(type *)dest = (type)regs_get_register(regs,                   \
                                (unsigned int)((unsigned long)offset)); \
-}
+}                                                                      \
+NOKPROBE_SYMBOL(FETCH_FUNC_NAME(reg, type));
 DEFINE_BASIC_FETCH_FUNCS(reg)
 /* No string on the register */
 #define fetch_reg_string       NULL
 #define fetch_reg_string_size  NULL
 
 #define DEFINE_FETCH_retval(type)                                      \
-__kprobes void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs,     \
-                                         void *dummy, void *dest)      \
+void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs,               \
+                                  void *dummy, void *dest)             \
 {                                                                      \
        *(type *)dest = (type)regs_return_value(regs);                  \
-}
+}                                                                      \
+NOKPROBE_SYMBOL(FETCH_FUNC_NAME(retval, type));
 DEFINE_BASIC_FETCH_FUNCS(retval)
 /* No string on the retval */
 #define fetch_retval_string            NULL
@@ -112,8 +113,8 @@ struct deref_fetch_param {
 };
 
 #define DEFINE_FETCH_deref(type)                                       \
-__kprobes void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs,      \
-                                           void *data, void *dest)     \
+void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs,                        \
+                                 void *data, void *dest)               \
 {                                                                      \
        struct deref_fetch_param *dprm = data;                          \
        unsigned long addr;                                             \
@@ -123,12 +124,13 @@ __kprobes void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs, \
                dprm->fetch(regs, (void *)addr, dest);                  \
        } else                                                          \
                *(type *)dest = 0;                                      \
-}
+}                                                                      \
+NOKPROBE_SYMBOL(FETCH_FUNC_NAME(deref, type));
 DEFINE_BASIC_FETCH_FUNCS(deref)
 DEFINE_FETCH_deref(string)
 
-__kprobes void FETCH_FUNC_NAME(deref, string_size)(struct pt_regs *regs,
-                                                  void *data, void *dest)
+void FETCH_FUNC_NAME(deref, string_size)(struct pt_regs *regs,
+                                        void *data, void *dest)
 {
        struct deref_fetch_param *dprm = data;
        unsigned long addr;
@@ -140,16 +142,18 @@ __kprobes void FETCH_FUNC_NAME(deref, string_size)(struct pt_regs *regs,
        } else
                *(string_size *)dest = 0;
 }
+NOKPROBE_SYMBOL(FETCH_FUNC_NAME(deref, string_size));
 
-static __kprobes void update_deref_fetch_param(struct deref_fetch_param *data)
+static void update_deref_fetch_param(struct deref_fetch_param *data)
 {
        if (CHECK_FETCH_FUNCS(deref, data->orig.fn))
                update_deref_fetch_param(data->orig.data);
        else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn))
                update_symbol_cache(data->orig.data);
 }
+NOKPROBE_SYMBOL(update_deref_fetch_param);
 
-static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data)
+static void free_deref_fetch_param(struct deref_fetch_param *data)
 {
        if (CHECK_FETCH_FUNCS(deref, data->orig.fn))
                free_deref_fetch_param(data->orig.data);
@@ -157,6 +161,7 @@ static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data)
                free_symbol_cache(data->orig.data);
        kfree(data);
 }
+NOKPROBE_SYMBOL(free_deref_fetch_param);
 
 /* Bitfield fetch function */
 struct bitfield_fetch_param {
@@ -166,8 +171,8 @@ struct bitfield_fetch_param {
 };
 
 #define DEFINE_FETCH_bitfield(type)                                    \
-__kprobes void FETCH_FUNC_NAME(bitfield, type)(struct pt_regs *regs,   \
-                                           void *data, void *dest)     \
+void FETCH_FUNC_NAME(bitfield, type)(struct pt_regs *regs,             \
+                                    void *data, void *dest)            \
 {                                                                      \
        struct bitfield_fetch_param *bprm = data;                       \
        type buf = 0;                                                   \
@@ -177,13 +182,13 @@ __kprobes void FETCH_FUNC_NAME(bitfield, type)(struct pt_regs *regs,      \
                buf >>= bprm->low_shift;                                \
        }                                                               \
        *(type *)dest = buf;                                            \
-}
-
+}                                                                      \
+NOKPROBE_SYMBOL(FETCH_FUNC_NAME(bitfield, type));
 DEFINE_BASIC_FETCH_FUNCS(bitfield)
 #define fetch_bitfield_string          NULL
 #define fetch_bitfield_string_size     NULL
 
-static __kprobes void
+static void
 update_bitfield_fetch_param(struct bitfield_fetch_param *data)
 {
        /*
@@ -196,7 +201,7 @@ update_bitfield_fetch_param(struct bitfield_fetch_param *data)
                update_symbol_cache(data->orig.data);
 }
 
-static __kprobes void
+static void
 free_bitfield_fetch_param(struct bitfield_fetch_param *data)
 {
        /*
@@ -255,17 +260,17 @@ fail:
 }
 
 /* Special function : only accept unsigned long */
-static __kprobes void fetch_kernel_stack_address(struct pt_regs *regs,
-                                                void *dummy, void *dest)
+static void fetch_kernel_stack_address(struct pt_regs *regs, void *dummy, void *dest)
 {
        *(unsigned long *)dest = kernel_stack_pointer(regs);
 }
+NOKPROBE_SYMBOL(fetch_kernel_stack_address);
 
-static __kprobes void fetch_user_stack_address(struct pt_regs *regs,
-                                              void *dummy, void *dest)
+static void fetch_user_stack_address(struct pt_regs *regs, void *dummy, void *dest)
 {
        *(unsigned long *)dest = user_stack_pointer(regs);
 }
+NOKPROBE_SYMBOL(fetch_user_stack_address);
 
 static fetch_func_t get_fetch_size_function(const struct fetch_type *type,
                                            fetch_func_t orig_fn,
index fb1ab5dfbd42f67c3125ebc0688f26eb60f8d529..4f815fbce16d26eefbc8e3b572ec9f2fdca7879b 100644 (file)
  */
 #define convert_rloc_to_loc(dl, offs)  ((u32)(dl) + (offs))
 
-static inline void *get_rloc_data(u32 *dl)
+static nokprobe_inline void *get_rloc_data(u32 *dl)
 {
        return (u8 *)dl + get_rloc_offs(*dl);
 }
 
 /* For data_loc conversion */
-static inline void *get_loc_data(u32 *dl, void *ent)
+static nokprobe_inline void *get_loc_data(u32 *dl, void *ent)
 {
        return (u8 *)ent + get_rloc_offs(*dl);
 }
@@ -136,9 +136,8 @@ typedef u32 string_size;
 
 /* Printing  in basic type function template */
 #define DECLARE_BASIC_PRINT_TYPE_FUNC(type)                            \
-__kprobes int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s,          \
-                                        const char *name,              \
-                                        void *data, void *ent);        \
+int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s, const char *name,  \
+                               void *data, void *ent);                 \
 extern const char PRINT_TYPE_FMT_NAME(type)[]
 
 DECLARE_BASIC_PRINT_TYPE_FUNC(u8);
@@ -303,7 +302,7 @@ static inline bool trace_probe_is_registered(struct trace_probe *tp)
        return !!(tp->flags & TP_FLAG_REGISTERED);
 }
 
-static inline __kprobes void call_fetch(struct fetch_param *fprm,
+static nokprobe_inline void call_fetch(struct fetch_param *fprm,
                                 struct pt_regs *regs, void *dest)
 {
        return fprm->fn(regs, fprm->data, dest);
@@ -351,7 +350,7 @@ extern ssize_t traceprobe_probes_write(struct file *file,
 extern int traceprobe_command(const char *buf, int (*createfn)(int, char**));
 
 /* Sum up total data length for dynamic arraies (strings) */
-static inline __kprobes int
+static nokprobe_inline int
 __get_data_size(struct trace_probe *tp, struct pt_regs *regs)
 {
        int i, ret = 0;
@@ -367,7 +366,7 @@ __get_data_size(struct trace_probe *tp, struct pt_regs *regs)
 }
 
 /* Store the value of each argument */
-static inline __kprobes void
+static nokprobe_inline void
 store_trace_args(int ent_size, struct trace_probe *tp, struct pt_regs *regs,
                 u8 *data, int maxlen)
 {
index c082a74413455da972d2c9c115001b58fe55e5a7..04fdb5de823c5db150aa8d15f82e2b70acb34060 100644 (file)
@@ -108,8 +108,8 @@ static unsigned long get_user_stack_nth(struct pt_regs *regs, unsigned int n)
  * Uprobes-specific fetch functions
  */
 #define DEFINE_FETCH_stack(type)                                       \
-static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\
-                                         void *offset, void *dest)     \
+static void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,         \
+                                        void *offset, void *dest)      \
 {                                                                      \
        *(type *)dest = (type)get_user_stack_nth(regs,                  \
                                              ((unsigned long)offset)); \
@@ -120,8 +120,8 @@ DEFINE_BASIC_FETCH_FUNCS(stack)
 #define fetch_stack_string_size        NULL
 
 #define DEFINE_FETCH_memory(type)                                      \
-static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\
-                                               void *addr, void *dest) \
+static void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,                \
+                                         void *addr, void *dest)       \
 {                                                                      \
        type retval;                                                    \
        void __user *vaddr = (void __force __user *) addr;              \
@@ -136,8 +136,8 @@ DEFINE_BASIC_FETCH_FUNCS(memory)
  * Fetch a null-terminated string. Caller MUST set *(u32 *)dest with max
  * length and relative data location.
  */
-static __kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,
-                                                     void *addr, void *dest)
+static void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,
+                                           void *addr, void *dest)
 {
        long ret;
        u32 rloc = *(u32 *)dest;
@@ -158,8 +158,8 @@ static __kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,
        }
 }
 
-static __kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs,
-                                                     void *addr, void *dest)
+static void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs,
+                                                void *addr, void *dest)
 {
        int len;
        void __user *vaddr = (void __force __user *) addr;
@@ -184,8 +184,8 @@ static unsigned long translate_user_vaddr(void *file_offset)
 }
 
 #define DEFINE_FETCH_file_offset(type)                                 \
-static __kprobes void FETCH_FUNC_NAME(file_offset, type)(struct pt_regs *regs,\
-                                       void *offset, void *dest)       \
+static void FETCH_FUNC_NAME(file_offset, type)(struct pt_regs *regs,   \
+                                              void *offset, void *dest)\
 {                                                                      \
        void *vaddr = (void *)translate_user_vaddr(offset);             \
                                                                        \
@@ -1009,56 +1009,60 @@ uprobe_filter_event(struct trace_uprobe *tu, struct perf_event *event)
        return __uprobe_perf_filter(&tu->filter, event->hw.tp_target->mm);
 }
 
-static int uprobe_perf_open(struct trace_uprobe *tu, struct perf_event *event)
+static int uprobe_perf_close(struct trace_uprobe *tu, struct perf_event *event)
 {
        bool done;
 
        write_lock(&tu->filter.rwlock);
        if (event->hw.tp_target) {
-               /*
-                * event->parent != NULL means copy_process(), we can avoid
-                * uprobe_apply(). current->mm must be probed and we can rely
-                * on dup_mmap() which preserves the already installed bp's.
-                *
-                * attr.enable_on_exec means that exec/mmap will install the
-                * breakpoints we need.
-                */
+               list_del(&event->hw.tp_list);
                done = tu->filter.nr_systemwide ||
-                       event->parent || event->attr.enable_on_exec ||
+                       (event->hw.tp_target->flags & PF_EXITING) ||
                        uprobe_filter_event(tu, event);
-               list_add(&event->hw.tp_list, &tu->filter.perf_events);
        } else {
+               tu->filter.nr_systemwide--;
                done = tu->filter.nr_systemwide;
-               tu->filter.nr_systemwide++;
        }
        write_unlock(&tu->filter.rwlock);
 
        if (!done)
-               uprobe_apply(tu->inode, tu->offset, &tu->consumer, true);
+               return uprobe_apply(tu->inode, tu->offset, &tu->consumer, false);
 
        return 0;
 }
 
-static int uprobe_perf_close(struct trace_uprobe *tu, struct perf_event *event)
+static int uprobe_perf_open(struct trace_uprobe *tu, struct perf_event *event)
 {
        bool done;
+       int err;
 
        write_lock(&tu->filter.rwlock);
        if (event->hw.tp_target) {
-               list_del(&event->hw.tp_list);
+               /*
+                * event->parent != NULL means copy_process(), we can avoid
+                * uprobe_apply(). current->mm must be probed and we can rely
+                * on dup_mmap() which preserves the already installed bp's.
+                *
+                * attr.enable_on_exec means that exec/mmap will install the
+                * breakpoints we need.
+                */
                done = tu->filter.nr_systemwide ||
-                       (event->hw.tp_target->flags & PF_EXITING) ||
+                       event->parent || event->attr.enable_on_exec ||
                        uprobe_filter_event(tu, event);
+               list_add(&event->hw.tp_list, &tu->filter.perf_events);
        } else {
-               tu->filter.nr_systemwide--;
                done = tu->filter.nr_systemwide;
+               tu->filter.nr_systemwide++;
        }
        write_unlock(&tu->filter.rwlock);
 
-       if (!done)
-               uprobe_apply(tu->inode, tu->offset, &tu->consumer, false);
-
-       return 0;
+       err = 0;
+       if (!done) {
+               err = uprobe_apply(tu->inode, tu->offset, &tu->consumer, true);
+               if (err)
+                       uprobe_perf_close(tu, event);
+       }
+       return err;
 }
 
 static bool uprobe_perf_filter(struct uprobe_consumer *uc,
index 4771fb3f4da4deafdab8b26c76df9c0bec618300..334f7722a999232aa46b706d8cda61b57c854708 100644 (file)
@@ -331,6 +331,20 @@ config TEXTSEARCH_FSM
 config BTREE
        boolean
 
+config INTERVAL_TREE
+       boolean
+       help
+         Simple, embeddable, interval-tree. Can find the start of an
+         overlapping range in log(n) time and then iterate over all
+         overlapping nodes. The algorithm is implemented as an
+         augmented rbtree.
+
+         See:
+
+               Documentation/rbtree.txt
+
+         for more information.
+
 config ASSOCIATIVE_ARRAY
        bool
        help
index ccca32264748dd631df882d8d2845fc40465440a..7cfcc1b8e1017006f1d1868cc26d63350c2df3b9 100644 (file)
@@ -1511,6 +1511,7 @@ config RBTREE_TEST
 config INTERVAL_TREE_TEST
        tristate "Interval tree test"
        depends on m && DEBUG_KERNEL
+       select INTERVAL_TREE
        help
          A benchmark measuring the performance of the interval tree library
 
@@ -1635,6 +1636,19 @@ config TEST_USER_COPY
 
          If unsure, say N.
 
+config TEST_BPF
+       tristate "Test BPF filter functionality"
+       default n
+       depends on m && NET
+       help
+         This builds the "test_bpf" module that runs various test vectors
+         against the BPF interpreter or BPF JIT compiler depending on the
+         current setting. This is in particular useful for BPF JIT compiler
+         development, but also to run regression tests against changes in
+         the interpreter code.
+
+         If unsure, say N.
+
 source "samples/Kconfig"
 
 source "lib/Kconfig.kgdb"
index 74a32dc49a93dcb8774eed057df7e1c1e14db29b..ba967a19edbae1d97da6d301f65d04c8bf44f6a5 100644 (file)
@@ -33,6 +33,7 @@ obj-y += kstrtox.o
 obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o
 obj-$(CONFIG_TEST_MODULE) += test_module.o
 obj-$(CONFIG_TEST_USER_COPY) += test_user_copy.o
+obj-$(CONFIG_TEST_BPF) += test_bpf.o
 
 ifeq ($(CONFIG_DEBUG_KOBJECT),y)
 CFLAGS_kobject.o += -DDEBUG
@@ -50,6 +51,7 @@ CFLAGS_hweight.o = $(subst $(quote),,$(CONFIG_ARCH_HWEIGHT_CFLAGS))
 obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o
 
 obj-$(CONFIG_BTREE) += btree.o
+obj-$(CONFIG_INTERVAL_TREE) += interval_tree.o
 obj-$(CONFIG_ASSOCIATIVE_ARRAY) += assoc_array.o
 obj-$(CONFIG_DEBUG_PREEMPT) += smp_processor_id.o
 obj-$(CONFIG_DEBUG_LIST) += list_debug.o
@@ -157,8 +159,6 @@ lib-$(CONFIG_LIBFDT) += $(libfdt_files)
 obj-$(CONFIG_RBTREE_TEST) += rbtree_test.o
 obj-$(CONFIG_INTERVAL_TREE_TEST) += interval_tree_test.o
 
-interval_tree_test-objs := interval_tree_test_main.o interval_tree.o
-
 obj-$(CONFIG_PERCPU_TEST) += percpu_test.o
 
 obj-$(CONFIG_ASN1) += asn1_decoder.o
index b810b753c607500a9aab3aedd3060cb6c35189fd..c101230658ebc9af1f7daf35252e293e0ce8d3f0 100644 (file)
@@ -164,3 +164,66 @@ void __init free_bootmem_cpumask_var(cpumask_var_t mask)
        memblock_free_early(__pa(mask), cpumask_size());
 }
 #endif
+
+/**
+ * cpumask_set_cpu_local_first - set i'th cpu with local numa cpu's first
+ *
+ * @i: index number
+ * @numa_node: local numa_node
+ * @dstp: cpumask with the relevant cpu bit set according to the policy
+ *
+ * This function sets the cpumask according to a numa aware policy.
+ * cpumask could be used as an affinity hint for the IRQ related to a
+ * queue. When the policy is to spread queues across cores - local cores
+ * first.
+ *
+ * Returns 0 on success, -ENOMEM for no memory, and -EAGAIN when failed to set
+ * the cpu bit and need to re-call the function.
+ */
+int cpumask_set_cpu_local_first(int i, int numa_node, cpumask_t *dstp)
+{
+       cpumask_var_t mask;
+       int cpu;
+       int ret = 0;
+
+       if (!zalloc_cpumask_var(&mask, GFP_KERNEL))
+               return -ENOMEM;
+
+       i %= num_online_cpus();
+
+       if (!cpumask_of_node(numa_node)) {
+               /* Use all online cpu's for non numa aware system */
+               cpumask_copy(mask, cpu_online_mask);
+       } else {
+               int n;
+
+               cpumask_and(mask,
+                           cpumask_of_node(numa_node), cpu_online_mask);
+
+               n = cpumask_weight(mask);
+               if (i >= n) {
+                       i -= n;
+
+                       /* If index > number of local cpu's, mask out local
+                        * cpu's
+                        */
+                       cpumask_andnot(mask, cpu_online_mask, mask);
+               }
+       }
+
+       for_each_cpu(cpu, mask) {
+               if (--i < 0)
+                       goto out;
+       }
+
+       ret = -EAGAIN;
+
+out:
+       free_cpumask_var(mask);
+
+       if (!ret)
+               cpumask_set_cpu(cpu, dstp);
+
+       return ret;
+}
+EXPORT_SYMBOL(cpumask_set_cpu_local_first);
index f1c3a144cec123d73bee9897922bca0fa4dace48..bf6255e239193159afb1fc6fef402b6b011f1b22 100644 (file)
 #include <linux/crc7.h>
 
 
-/* Table for CRC-7 (polynomial x^7 + x^3 + 1) */
-const u8 crc7_syndrome_table[256] = {
-       0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f,
-       0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
-       0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26,
-       0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e,
-       0x32, 0x3b, 0x20, 0x29, 0x16, 0x1f, 0x04, 0x0d,
-       0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45,
-       0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14,
-       0x63, 0x6a, 0x71, 0x78, 0x47, 0x4e, 0x55, 0x5c,
-       0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b,
-       0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13,
-       0x7d, 0x74, 0x6f, 0x66, 0x59, 0x50, 0x4b, 0x42,
-       0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a,
-       0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69,
-       0x1e, 0x17, 0x0c, 0x05, 0x3a, 0x33, 0x28, 0x21,
-       0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70,
-       0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38,
-       0x41, 0x48, 0x53, 0x5a, 0x65, 0x6c, 0x77, 0x7e,
-       0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36,
-       0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67,
-       0x10, 0x19, 0x02, 0x0b, 0x34, 0x3d, 0x26, 0x2f,
-       0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
-       0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04,
-       0x6a, 0x63, 0x78, 0x71, 0x4e, 0x47, 0x5c, 0x55,
-       0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d,
-       0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a,
-       0x6d, 0x64, 0x7f, 0x76, 0x49, 0x40, 0x5b, 0x52,
-       0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03,
-       0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b,
-       0x17, 0x1e, 0x05, 0x0c, 0x33, 0x3a, 0x21, 0x28,
-       0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60,
-       0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31,
-       0x46, 0x4f, 0x54, 0x5d, 0x62, 0x6b, 0x70, 0x79
+/*
+ * Table for CRC-7 (polynomial x^7 + x^3 + 1).
+ * This is a big-endian CRC (msbit is highest power of x),
+ * aligned so the msbit of the byte is the x^6 coefficient
+ * and the lsbit is not used.
+ */
+const u8 crc7_be_syndrome_table[256] = {
+       0x00, 0x12, 0x24, 0x36, 0x48, 0x5a, 0x6c, 0x7e,
+       0x90, 0x82, 0xb4, 0xa6, 0xd8, 0xca, 0xfc, 0xee,
+       0x32, 0x20, 0x16, 0x04, 0x7a, 0x68, 0x5e, 0x4c,
+       0xa2, 0xb0, 0x86, 0x94, 0xea, 0xf8, 0xce, 0xdc,
+       0x64, 0x76, 0x40, 0x52, 0x2c, 0x3e, 0x08, 0x1a,
+       0xf4, 0xe6, 0xd0, 0xc2, 0xbc, 0xae, 0x98, 0x8a,
+       0x56, 0x44, 0x72, 0x60, 0x1e, 0x0c, 0x3a, 0x28,
+       0xc6, 0xd4, 0xe2, 0xf0, 0x8e, 0x9c, 0xaa, 0xb8,
+       0xc8, 0xda, 0xec, 0xfe, 0x80, 0x92, 0xa4, 0xb6,
+       0x58, 0x4a, 0x7c, 0x6e, 0x10, 0x02, 0x34, 0x26,
+       0xfa, 0xe8, 0xde, 0xcc, 0xb2, 0xa0, 0x96, 0x84,
+       0x6a, 0x78, 0x4e, 0x5c, 0x22, 0x30, 0x06, 0x14,
+       0xac, 0xbe, 0x88, 0x9a, 0xe4, 0xf6, 0xc0, 0xd2,
+       0x3c, 0x2e, 0x18, 0x0a, 0x74, 0x66, 0x50, 0x42,
+       0x9e, 0x8c, 0xba, 0xa8, 0xd6, 0xc4, 0xf2, 0xe0,
+       0x0e, 0x1c, 0x2a, 0x38, 0x46, 0x54, 0x62, 0x70,
+       0x82, 0x90, 0xa6, 0xb4, 0xca, 0xd8, 0xee, 0xfc,
+       0x12, 0x00, 0x36, 0x24, 0x5a, 0x48, 0x7e, 0x6c,
+       0xb0, 0xa2, 0x94, 0x86, 0xf8, 0xea, 0xdc, 0xce,
+       0x20, 0x32, 0x04, 0x16, 0x68, 0x7a, 0x4c, 0x5e,
+       0xe6, 0xf4, 0xc2, 0xd0, 0xae, 0xbc, 0x8a, 0x98,
+       0x76, 0x64, 0x52, 0x40, 0x3e, 0x2c, 0x1a, 0x08,
+       0xd4, 0xc6, 0xf0, 0xe2, 0x9c, 0x8e, 0xb8, 0xaa,
+       0x44, 0x56, 0x60, 0x72, 0x0c, 0x1e, 0x28, 0x3a,
+       0x4a, 0x58, 0x6e, 0x7c, 0x02, 0x10, 0x26, 0x34,
+       0xda, 0xc8, 0xfe, 0xec, 0x92, 0x80, 0xb6, 0xa4,
+       0x78, 0x6a, 0x5c, 0x4e, 0x30, 0x22, 0x14, 0x06,
+       0xe8, 0xfa, 0xcc, 0xde, 0xa0, 0xb2, 0x84, 0x96,
+       0x2e, 0x3c, 0x0a, 0x18, 0x66, 0x74, 0x42, 0x50,
+       0xbe, 0xac, 0x9a, 0x88, 0xf6, 0xe4, 0xd2, 0xc0,
+       0x1c, 0x0e, 0x38, 0x2a, 0x54, 0x46, 0x70, 0x62,
+       0x8c, 0x9e, 0xa8, 0xba, 0xc4, 0xd6, 0xe0, 0xf2
 };
-EXPORT_SYMBOL(crc7_syndrome_table);
+EXPORT_SYMBOL(crc7_be_syndrome_table);
 
 /**
  * crc7 - update the CRC7 for the data buffer
@@ -55,14 +60,17 @@ EXPORT_SYMBOL(crc7_syndrome_table);
  * Context: any
  *
  * Returns the updated CRC7 value.
+ * The CRC7 is left-aligned in the byte (the lsbit is always 0), as that
+ * makes the computation easier, and all callers want it in that form.
+ *
  */
-u8 crc7(u8 crc, const u8 *buffer, size_t len)
+u8 crc7_be(u8 crc, const u8 *buffer, size_t len)
 {
        while (len--)
-               crc = crc7_byte(crc, *buffer++);
+               crc = crc7_be_byte(crc, *buffer++);
        return crc;
 }
-EXPORT_SYMBOL(crc7);
+EXPORT_SYMBOL(crc7_be);
 
 MODULE_DESCRIPTION("CRC7 calculations");
 MODULE_LICENSE("GPL");
index e6eb406f2d65dd3c4e22520239ec10afdd42dc30..f367f9ad544c3901a0ee5bcdfe451ac8b9b2aea3 100644 (file)
@@ -1,6 +1,7 @@
 #include <linux/init.h>
 #include <linux/interval_tree.h>
 #include <linux/interval_tree_generic.h>
+#include <linux/module.h>
 
 #define START(node) ((node)->start)
 #define LAST(node)  ((node)->last)
@@ -8,3 +9,8 @@
 INTERVAL_TREE_DEFINE(struct interval_tree_node, rb,
                     unsigned long, __subtree_last,
                     START, LAST,, interval_tree)
+
+EXPORT_SYMBOL_GPL(interval_tree_insert);
+EXPORT_SYMBOL_GPL(interval_tree_remove);
+EXPORT_SYMBOL_GPL(interval_tree_iter_first);
+EXPORT_SYMBOL_GPL(interval_tree_iter_next);
diff --git a/lib/test_bpf.c b/lib/test_bpf.c
new file mode 100644 (file)
index 0000000..c579e0f
--- /dev/null
@@ -0,0 +1,1929 @@
+/*
+ * Testsuite for BPF interpreter and BPF JIT compiler
+ *
+ * Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/filter.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/if_vlan.h>
+
+/* General test specific settings */
+#define MAX_SUBTESTS   3
+#define MAX_TESTRUNS   10000
+#define MAX_DATA       128
+#define MAX_INSNS      512
+#define MAX_K          0xffffFFFF
+
+/* Few constants used to init test 'skb' */
+#define SKB_TYPE       3
+#define SKB_MARK       0x1234aaaa
+#define SKB_HASH       0x1234aaab
+#define SKB_QUEUE_MAP  123
+#define SKB_VLAN_TCI   0xffff
+#define SKB_DEV_IFINDEX        577
+#define SKB_DEV_TYPE   588
+
+/* Redefine REGs to make tests less verbose */
+#define R0             BPF_REG_0
+#define R1             BPF_REG_1
+#define R2             BPF_REG_2
+#define R3             BPF_REG_3
+#define R4             BPF_REG_4
+#define R5             BPF_REG_5
+#define R6             BPF_REG_6
+#define R7             BPF_REG_7
+#define R8             BPF_REG_8
+#define R9             BPF_REG_9
+#define R10            BPF_REG_10
+
+/* Flags that can be passed to test cases */
+#define FLAG_NO_DATA           BIT(0)
+#define FLAG_EXPECTED_FAIL     BIT(1)
+
+enum {
+       CLASSIC  = BIT(6),      /* Old BPF instructions only. */
+       INTERNAL = BIT(7),      /* Extended instruction set.  */
+};
+
+#define TEST_TYPE_MASK         (CLASSIC | INTERNAL)
+
+struct bpf_test {
+       const char *descr;
+       union {
+               struct sock_filter insns[MAX_INSNS];
+               struct sock_filter_int insns_int[MAX_INSNS];
+       } u;
+       __u8 aux;
+       __u8 data[MAX_DATA];
+       struct {
+               int data_size;
+               __u32 result;
+       } test[MAX_SUBTESTS];
+};
+
+static struct bpf_test tests[] = {
+       {
+               "TAX",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IMM, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_IMM, 2),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_ALU | BPF_NEG, 0), /* A == -3 */
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_LEN, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0), /* X == len - 3 */
+                       BPF_STMT(BPF_LD | BPF_B | BPF_IND, 1),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { 10, 20, 30, 40, 50 },
+               { { 2, 10 }, { 3, 20 }, { 4, 30 } },
+       },
+       {
+               "TXA",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_LEN, 0),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0) /* A == len * 2 */
+               },
+               CLASSIC,
+               { 10, 20, 30, 40, 50 },
+               { { 1, 2 }, { 3, 6 }, { 4, 8 } },
+       },
+       {
+               "ADD_SUB_MUL_K",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IMM, 1),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 2),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 3),
+                       BPF_STMT(BPF_ALU | BPF_SUB | BPF_X, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 0xffffffff),
+                       BPF_STMT(BPF_ALU | BPF_MUL | BPF_K, 3),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC | FLAG_NO_DATA,
+               { },
+               { { 0, 0xfffffffd } }
+       },
+       {
+               "DIV_KX",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IMM, 8),
+                       BPF_STMT(BPF_ALU | BPF_DIV | BPF_K, 2),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_IMM, 0xffffffff),
+                       BPF_STMT(BPF_ALU | BPF_DIV | BPF_X, 0),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_IMM, 0xffffffff),
+                       BPF_STMT(BPF_ALU | BPF_DIV | BPF_K, 0x70000000),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC | FLAG_NO_DATA,
+               { },
+               { { 0, 0x40000001 } }
+       },
+       {
+               "AND_OR_LSH_K",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IMM, 0xff),
+                       BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xf0),
+                       BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 27),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_IMM, 0xf),
+                       BPF_STMT(BPF_ALU | BPF_OR | BPF_K, 0xf0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC | FLAG_NO_DATA,
+               { },
+               { { 0, 0x800000ff }, { 1, 0x800000ff } },
+       },
+       {
+               "LD_IMM_0",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IMM, 0), /* ld #0 */
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 1),
+               },
+               CLASSIC,
+               { },
+               { { 1, 1 } },
+       },
+       {
+               "LD_IND",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_LEN, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_IND, MAX_K),
+                       BPF_STMT(BPF_RET | BPF_K, 1)
+               },
+               CLASSIC,
+               { },
+               { { 1, 0 }, { 10, 0 }, { 60, 0 } },
+       },
+       {
+               "LD_ABS",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 1000),
+                       BPF_STMT(BPF_RET | BPF_K, 1)
+               },
+               CLASSIC,
+               { },
+               { { 1, 0 }, { 10, 0 }, { 60, 0 } },
+       },
+       {
+               "LD_ABS_LL",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, SKF_LL_OFF),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, SKF_LL_OFF + 1),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { 1, 2, 3 },
+               { { 1, 0 }, { 2, 3 } },
+       },
+       {
+               "LD_IND_LL",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IMM, SKF_LL_OFF - 1),
+                       BPF_STMT(BPF_LDX | BPF_LEN, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { 1, 2, 3, 0xff },
+               { { 1, 1 }, { 3, 3 }, { 4, 0xff } },
+       },
+       {
+               "LD_ABS_NET",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, SKF_NET_OFF),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, SKF_NET_OFF + 1),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3 },
+               { { 15, 0 }, { 16, 3 } },
+       },
+       {
+               "LD_IND_NET",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IMM, SKF_NET_OFF - 15),
+                       BPF_STMT(BPF_LDX | BPF_LEN, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3 },
+               { { 14, 0 }, { 15, 1 }, { 17, 3 } },
+       },
+       {
+               "LD_PKTTYPE",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_PKTTYPE),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SKB_TYPE, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 1),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_PKTTYPE),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SKB_TYPE, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 1),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_PKTTYPE),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SKB_TYPE, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 1),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               { { 1, 3 }, { 10, 3 } },
+       },
+       {
+               "LD_MARK",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_MARK),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               { { 1, SKB_MARK}, { 10, SKB_MARK} },
+       },
+       {
+               "LD_RXHASH",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_RXHASH),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               { { 1, SKB_HASH}, { 10, SKB_HASH} },
+       },
+       {
+               "LD_QUEUE",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_QUEUE),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               { { 1, SKB_QUEUE_MAP }, { 10, SKB_QUEUE_MAP } },
+       },
+       {
+               "LD_PROTOCOL",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 1),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 20, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 0),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_PROTOCOL),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 30, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 0),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { 10, 20, 30 },
+               { { 10, ETH_P_IP }, { 100, ETH_P_IP } },
+       },
+       {
+               "LD_VLAN_TAG",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_VLAN_TAG),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               {
+                       { 1, SKB_VLAN_TCI & ~VLAN_TAG_PRESENT },
+                       { 10, SKB_VLAN_TCI & ~VLAN_TAG_PRESENT }
+               },
+       },
+       {
+               "LD_VLAN_TAG_PRESENT",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               {
+                       { 1, !!(SKB_VLAN_TCI & VLAN_TAG_PRESENT) },
+                       { 10, !!(SKB_VLAN_TCI & VLAN_TAG_PRESENT) }
+               },
+       },
+       {
+               "LD_IFINDEX",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_IFINDEX),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               { { 1, SKB_DEV_IFINDEX }, { 10, SKB_DEV_IFINDEX } },
+       },
+       {
+               "LD_HATYPE",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_HATYPE),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               { { 1, SKB_DEV_TYPE }, { 10, SKB_DEV_TYPE } },
+       },
+       {
+               "LD_CPU",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_CPU),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_CPU),
+                       BPF_STMT(BPF_ALU | BPF_SUB | BPF_X, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               { { 1, 0 }, { 10, 0 } },
+       },
+       {
+               "LD_NLATTR",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 2),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 3),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_NLATTR),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+#ifdef __BIG_ENDIAN
+               { 0xff, 0xff, 0, 4, 0, 2, 0, 4, 0, 3 },
+#else
+               { 0xff, 0xff, 4, 0, 2, 0, 4, 0, 3, 0 },
+#endif
+               { { 4, 0 }, { 20, 6 } },
+       },
+       {
+               "LD_NLATTR_NEST",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IMM, 2),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 3),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_NLATTR_NEST),
+                       BPF_STMT(BPF_LD | BPF_IMM, 2),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_NLATTR_NEST),
+                       BPF_STMT(BPF_LD | BPF_IMM, 2),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_NLATTR_NEST),
+                       BPF_STMT(BPF_LD | BPF_IMM, 2),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_NLATTR_NEST),
+                       BPF_STMT(BPF_LD | BPF_IMM, 2),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_NLATTR_NEST),
+                       BPF_STMT(BPF_LD | BPF_IMM, 2),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_NLATTR_NEST),
+                       BPF_STMT(BPF_LD | BPF_IMM, 2),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_NLATTR_NEST),
+                       BPF_STMT(BPF_LD | BPF_IMM, 2),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_NLATTR_NEST),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+#ifdef __BIG_ENDIAN
+               { 0xff, 0xff, 0, 12, 0, 1, 0, 4, 0, 2, 0, 4, 0, 3 },
+#else
+               { 0xff, 0xff, 12, 0, 1, 0, 4, 0, 2, 0, 4, 0, 3, 0 },
+#endif
+               { { 4, 0 }, { 20, 10 } },
+       },
+       {
+               "LD_PAYLOAD_OFF",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_PAY_OFFSET),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_PAY_OFFSET),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_PAY_OFFSET),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_PAY_OFFSET),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_PAY_OFFSET),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               /* 00:00:00:00:00:00 > 00:00:00:00:00:00, ethtype IPv4 (0x0800),
+                * length 98: 127.0.0.1 > 127.0.0.1: ICMP echo request,
+                * id 9737, seq 1, length 64
+                */
+               { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                 0x08, 0x00,
+                 0x45, 0x00, 0x00, 0x54, 0xac, 0x8b, 0x40, 0x00, 0x40,
+                 0x01, 0x90, 0x1b, 0x7f, 0x00, 0x00, 0x01 },
+               { { 30, 0 }, { 100, 42 } },
+       },
+       {
+               "LD_ANC_XOR",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IMM, 10),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 300),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_ALU_XOR_X),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               { { 4, 10 ^ 300 }, { 20, 10 ^ 300 } },
+       },
+       {
+               "SPILL_FILL",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_LEN, 0),
+                       BPF_STMT(BPF_LD | BPF_IMM, 2),
+                       BPF_STMT(BPF_ALU | BPF_RSH, 1),
+                       BPF_STMT(BPF_ALU | BPF_XOR | BPF_X, 0),
+                       BPF_STMT(BPF_ST, 1), /* M1 = 1 ^ len */
+                       BPF_STMT(BPF_ALU | BPF_XOR | BPF_K, 0x80000000),
+                       BPF_STMT(BPF_ST, 2), /* M2 = 1 ^ len ^ 0x80000000 */
+                       BPF_STMT(BPF_STX, 15), /* M3 = len */
+                       BPF_STMT(BPF_LDX | BPF_MEM, 1),
+                       BPF_STMT(BPF_LD | BPF_MEM, 2),
+                       BPF_STMT(BPF_ALU | BPF_XOR | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 15),
+                       BPF_STMT(BPF_ALU | BPF_XOR | BPF_X, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               { { 1, 0x80000001 }, { 2, 0x80000002 }, { 60, 0x80000000 ^ 60 } }
+       },
+       {
+               "JEQ",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_LEN, 0),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, 0, 1),
+                       BPF_STMT(BPF_RET | BPF_K, 1),
+                       BPF_STMT(BPF_RET | BPF_K, MAX_K)
+               },
+               CLASSIC,
+               { 3, 3, 3, 3, 3 },
+               { { 1, 0 }, { 3, 1 }, { 4, MAX_K } },
+       },
+       {
+               "JGT",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_LEN, 0),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2),
+                       BPF_JUMP(BPF_JMP | BPF_JGT | BPF_X, 0, 0, 1),
+                       BPF_STMT(BPF_RET | BPF_K, 1),
+                       BPF_STMT(BPF_RET | BPF_K, MAX_K)
+               },
+               CLASSIC,
+               { 4, 4, 4, 3, 3 },
+               { { 2, 0 }, { 3, 1 }, { 4, MAX_K } },
+       },
+       {
+               "JGE",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_LEN, 0),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_IND, MAX_K),
+                       BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 1, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 10),
+                       BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 2, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 20),
+                       BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 3, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 30),
+                       BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 4, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 40),
+                       BPF_STMT(BPF_RET | BPF_K, MAX_K)
+               },
+               CLASSIC,
+               { 1, 2, 3, 4, 5 },
+               { { 1, 20 }, { 3, 40 }, { 5, MAX_K } },
+       },
+       {
+               "JSET",
+               .u.insns = {
+                       BPF_JUMP(BPF_JMP | BPF_JA, 0, 0, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JA, 1, 1, 1),
+                       BPF_JUMP(BPF_JMP | BPF_JA, 0, 0, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JA, 0, 0, 0),
+                       BPF_STMT(BPF_LDX | BPF_LEN, 0),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_SUB | BPF_K, 4),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_IND, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 1, 0, 1),
+                       BPF_STMT(BPF_RET | BPF_K, 10),
+                       BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0x80000000, 0, 1),
+                       BPF_STMT(BPF_RET | BPF_K, 20),
+                       BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0xffffff, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 30),
+                       BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0xffffff, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 30),
+                       BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0xffffff, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 30),
+                       BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0xffffff, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 30),
+                       BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0xffffff, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 30),
+                       BPF_STMT(BPF_RET | BPF_K, MAX_K)
+               },
+               CLASSIC,
+               { 0, 0xAA, 0x55, 1 },
+               { { 4, 10 }, { 5, 20 }, { 6, MAX_K } },
+       },
+       {
+               "tcpdump port 22",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 12),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x86dd, 0, 8), /* IPv6 */
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 20),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x84, 2, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x6, 1, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x11, 0, 17),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 54),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 22, 14, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 56),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 22, 12, 13),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0800, 0, 12), /* IPv4 */
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 23),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x84, 2, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x6, 1, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x11, 0, 8),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 20),
+                       BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0x1fff, 6, 0),
+                       BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 14),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_IND, 14),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 22, 2, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_IND, 16),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 22, 0, 1),
+                       BPF_STMT(BPF_RET | BPF_K, 0xffff),
+                       BPF_STMT(BPF_RET | BPF_K, 0),
+               },
+               CLASSIC,
+               /* 3c:07:54:43:e5:76 > 10:bf:48:d6:43:d6, ethertype IPv4(0x0800)
+                * length 114: 10.1.1.149.49700 > 10.1.2.10.22: Flags [P.],
+                * seq 1305692979:1305693027, ack 3650467037, win 65535,
+                * options [nop,nop,TS val 2502645400 ecr 3971138], length 48
+                */
+               { 0x10, 0xbf, 0x48, 0xd6, 0x43, 0xd6,
+                 0x3c, 0x07, 0x54, 0x43, 0xe5, 0x76,
+                 0x08, 0x00,
+                 0x45, 0x10, 0x00, 0x64, 0x75, 0xb5,
+                 0x40, 0x00, 0x40, 0x06, 0xad, 0x2e, /* IP header */
+                 0x0a, 0x01, 0x01, 0x95, /* ip src */
+                 0x0a, 0x01, 0x02, 0x0a, /* ip dst */
+                 0xc2, 0x24,
+                 0x00, 0x16 /* dst port */ },
+               { { 10, 0 }, { 30, 0 }, { 100, 65535 } },
+       },
+       {
+               "tcpdump complex",
+               .u.insns = {
+                       /* tcpdump -nei eth0 'tcp port 22 and (((ip[2:2] -
+                        * ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0) and
+                        * (len > 115 or len < 30000000000)' -d
+                        */
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 12),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x86dd, 30, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x800, 0, 29),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 23),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x6, 0, 27),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 20),
+                       BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0x1fff, 25, 0),
+                       BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 14),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_IND, 14),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 22, 2, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_IND, 16),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 22, 0, 20),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 16),
+                       BPF_STMT(BPF_ST, 1),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 14),
+                       BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xf),
+                       BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 2),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0x5), /* libpcap emits K on TAX */
+                       BPF_STMT(BPF_LD | BPF_MEM, 1),
+                       BPF_STMT(BPF_ALU | BPF_SUB | BPF_X, 0),
+                       BPF_STMT(BPF_ST, 5),
+                       BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 14),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_IND, 26),
+                       BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xf0),
+                       BPF_STMT(BPF_ALU | BPF_RSH | BPF_K, 2),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0x9), /* libpcap emits K on TAX */
+                       BPF_STMT(BPF_LD | BPF_MEM, 5),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, 4, 0),
+                       BPF_STMT(BPF_LD | BPF_LEN, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, 0x73, 1, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 0xfc23ac00, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 0xffff),
+                       BPF_STMT(BPF_RET | BPF_K, 0),
+               },
+               CLASSIC,
+               { 0x10, 0xbf, 0x48, 0xd6, 0x43, 0xd6,
+                 0x3c, 0x07, 0x54, 0x43, 0xe5, 0x76,
+                 0x08, 0x00,
+                 0x45, 0x10, 0x00, 0x64, 0x75, 0xb5,
+                 0x40, 0x00, 0x40, 0x06, 0xad, 0x2e, /* IP header */
+                 0x0a, 0x01, 0x01, 0x95, /* ip src */
+                 0x0a, 0x01, 0x02, 0x0a, /* ip dst */
+                 0xc2, 0x24,
+                 0x00, 0x16 /* dst port */ },
+               { { 10, 0 }, { 30, 0 }, { 100, 65535 } },
+       },
+       {
+               "RET_A",
+               .u.insns = {
+                       /* check that unitialized X and A contain zeros */
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               { {1, 0}, {2, 0} },
+       },
+       {
+               "INT: ADD trivial",
+               .u.insns_int = {
+                       BPF_ALU64_IMM(BPF_MOV, R1, 1),
+                       BPF_ALU64_IMM(BPF_ADD, R1, 2),
+                       BPF_ALU64_IMM(BPF_MOV, R2, 3),
+                       BPF_ALU64_REG(BPF_SUB, R1, R2),
+                       BPF_ALU64_IMM(BPF_ADD, R1, -1),
+                       BPF_ALU64_IMM(BPF_MUL, R1, 3),
+                       BPF_ALU64_REG(BPF_MOV, R0, R1),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { },
+               { { 0, 0xfffffffd } }
+       },
+       {
+               "INT: MUL_X",
+               .u.insns_int = {
+                       BPF_ALU64_IMM(BPF_MOV, R0, -1),
+                       BPF_ALU64_IMM(BPF_MOV, R1, -1),
+                       BPF_ALU64_IMM(BPF_MOV, R2, 3),
+                       BPF_ALU64_REG(BPF_MUL, R1, R2),
+                       BPF_JMP_IMM(BPF_JEQ, R1, 0xfffffffd, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_IMM(BPF_MOV, R0, 1),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { },
+               { { 0, 1 } }
+       },
+       {
+               "INT: MUL_X2",
+               .u.insns_int = {
+                       BPF_ALU32_IMM(BPF_MOV, R0, -1),
+                       BPF_ALU32_IMM(BPF_MOV, R1, -1),
+                       BPF_ALU32_IMM(BPF_MOV, R2, 3),
+                       BPF_ALU64_REG(BPF_MUL, R1, R2),
+                       BPF_ALU64_IMM(BPF_RSH, R1, 8),
+                       BPF_JMP_IMM(BPF_JEQ, R1, 0x2ffffff, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_IMM(BPF_MOV, R0, 1),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { },
+               { { 0, 1 } }
+       },
+       {
+               "INT: MUL32_X",
+               .u.insns_int = {
+                       BPF_ALU32_IMM(BPF_MOV, R0, -1),
+                       BPF_ALU64_IMM(BPF_MOV, R1, -1),
+                       BPF_ALU32_IMM(BPF_MOV, R2, 3),
+                       BPF_ALU32_REG(BPF_MUL, R1, R2),
+                       BPF_ALU64_IMM(BPF_RSH, R1, 8),
+                       BPF_JMP_IMM(BPF_JEQ, R1, 0xffffff, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_IMM(BPF_MOV, R0, 1),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { },
+               { { 0, 1 } }
+       },
+       {
+               /* Have to test all register combinations, since
+                * JITing of different registers will produce
+                * different asm code.
+                */
+               "INT: ADD 64-bit",
+               .u.insns_int = {
+                       BPF_ALU64_IMM(BPF_MOV, R0, 0),
+                       BPF_ALU64_IMM(BPF_MOV, R1, 1),
+                       BPF_ALU64_IMM(BPF_MOV, R2, 2),
+                       BPF_ALU64_IMM(BPF_MOV, R3, 3),
+                       BPF_ALU64_IMM(BPF_MOV, R4, 4),
+                       BPF_ALU64_IMM(BPF_MOV, R5, 5),
+                       BPF_ALU64_IMM(BPF_MOV, R6, 6),
+                       BPF_ALU64_IMM(BPF_MOV, R7, 7),
+                       BPF_ALU64_IMM(BPF_MOV, R8, 8),
+                       BPF_ALU64_IMM(BPF_MOV, R9, 9),
+                       BPF_ALU64_IMM(BPF_ADD, R0, 20),
+                       BPF_ALU64_IMM(BPF_ADD, R1, 20),
+                       BPF_ALU64_IMM(BPF_ADD, R2, 20),
+                       BPF_ALU64_IMM(BPF_ADD, R3, 20),
+                       BPF_ALU64_IMM(BPF_ADD, R4, 20),
+                       BPF_ALU64_IMM(BPF_ADD, R5, 20),
+                       BPF_ALU64_IMM(BPF_ADD, R6, 20),
+                       BPF_ALU64_IMM(BPF_ADD, R7, 20),
+                       BPF_ALU64_IMM(BPF_ADD, R8, 20),
+                       BPF_ALU64_IMM(BPF_ADD, R9, 20),
+                       BPF_ALU64_IMM(BPF_SUB, R0, 10),
+                       BPF_ALU64_IMM(BPF_SUB, R1, 10),
+                       BPF_ALU64_IMM(BPF_SUB, R2, 10),
+                       BPF_ALU64_IMM(BPF_SUB, R3, 10),
+                       BPF_ALU64_IMM(BPF_SUB, R4, 10),
+                       BPF_ALU64_IMM(BPF_SUB, R5, 10),
+                       BPF_ALU64_IMM(BPF_SUB, R6, 10),
+                       BPF_ALU64_IMM(BPF_SUB, R7, 10),
+                       BPF_ALU64_IMM(BPF_SUB, R8, 10),
+                       BPF_ALU64_IMM(BPF_SUB, R9, 10),
+                       BPF_ALU64_REG(BPF_ADD, R0, R0),
+                       BPF_ALU64_REG(BPF_ADD, R0, R1),
+                       BPF_ALU64_REG(BPF_ADD, R0, R2),
+                       BPF_ALU64_REG(BPF_ADD, R0, R3),
+                       BPF_ALU64_REG(BPF_ADD, R0, R4),
+                       BPF_ALU64_REG(BPF_ADD, R0, R5),
+                       BPF_ALU64_REG(BPF_ADD, R0, R6),
+                       BPF_ALU64_REG(BPF_ADD, R0, R7),
+                       BPF_ALU64_REG(BPF_ADD, R0, R8),
+                       BPF_ALU64_REG(BPF_ADD, R0, R9), /* R0 == 155 */
+                       BPF_JMP_IMM(BPF_JEQ, R0, 155, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_ADD, R1, R0),
+                       BPF_ALU64_REG(BPF_ADD, R1, R1),
+                       BPF_ALU64_REG(BPF_ADD, R1, R2),
+                       BPF_ALU64_REG(BPF_ADD, R1, R3),
+                       BPF_ALU64_REG(BPF_ADD, R1, R4),
+                       BPF_ALU64_REG(BPF_ADD, R1, R5),
+                       BPF_ALU64_REG(BPF_ADD, R1, R6),
+                       BPF_ALU64_REG(BPF_ADD, R1, R7),
+                       BPF_ALU64_REG(BPF_ADD, R1, R8),
+                       BPF_ALU64_REG(BPF_ADD, R1, R9), /* R1 == 456 */
+                       BPF_JMP_IMM(BPF_JEQ, R1, 456, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_ADD, R2, R0),
+                       BPF_ALU64_REG(BPF_ADD, R2, R1),
+                       BPF_ALU64_REG(BPF_ADD, R2, R2),
+                       BPF_ALU64_REG(BPF_ADD, R2, R3),
+                       BPF_ALU64_REG(BPF_ADD, R2, R4),
+                       BPF_ALU64_REG(BPF_ADD, R2, R5),
+                       BPF_ALU64_REG(BPF_ADD, R2, R6),
+                       BPF_ALU64_REG(BPF_ADD, R2, R7),
+                       BPF_ALU64_REG(BPF_ADD, R2, R8),
+                       BPF_ALU64_REG(BPF_ADD, R2, R9), /* R2 == 1358 */
+                       BPF_JMP_IMM(BPF_JEQ, R2, 1358, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_ADD, R3, R0),
+                       BPF_ALU64_REG(BPF_ADD, R3, R1),
+                       BPF_ALU64_REG(BPF_ADD, R3, R2),
+                       BPF_ALU64_REG(BPF_ADD, R3, R3),
+                       BPF_ALU64_REG(BPF_ADD, R3, R4),
+                       BPF_ALU64_REG(BPF_ADD, R3, R5),
+                       BPF_ALU64_REG(BPF_ADD, R3, R6),
+                       BPF_ALU64_REG(BPF_ADD, R3, R7),
+                       BPF_ALU64_REG(BPF_ADD, R3, R8),
+                       BPF_ALU64_REG(BPF_ADD, R3, R9), /* R3 == 4063 */
+                       BPF_JMP_IMM(BPF_JEQ, R3, 4063, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_ADD, R4, R0),
+                       BPF_ALU64_REG(BPF_ADD, R4, R1),
+                       BPF_ALU64_REG(BPF_ADD, R4, R2),
+                       BPF_ALU64_REG(BPF_ADD, R4, R3),
+                       BPF_ALU64_REG(BPF_ADD, R4, R4),
+                       BPF_ALU64_REG(BPF_ADD, R4, R5),
+                       BPF_ALU64_REG(BPF_ADD, R4, R6),
+                       BPF_ALU64_REG(BPF_ADD, R4, R7),
+                       BPF_ALU64_REG(BPF_ADD, R4, R8),
+                       BPF_ALU64_REG(BPF_ADD, R4, R9), /* R4 == 12177 */
+                       BPF_JMP_IMM(BPF_JEQ, R4, 12177, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_ADD, R5, R0),
+                       BPF_ALU64_REG(BPF_ADD, R5, R1),
+                       BPF_ALU64_REG(BPF_ADD, R5, R2),
+                       BPF_ALU64_REG(BPF_ADD, R5, R3),
+                       BPF_ALU64_REG(BPF_ADD, R5, R4),
+                       BPF_ALU64_REG(BPF_ADD, R5, R5),
+                       BPF_ALU64_REG(BPF_ADD, R5, R6),
+                       BPF_ALU64_REG(BPF_ADD, R5, R7),
+                       BPF_ALU64_REG(BPF_ADD, R5, R8),
+                       BPF_ALU64_REG(BPF_ADD, R5, R9), /* R5 == 36518 */
+                       BPF_JMP_IMM(BPF_JEQ, R5, 36518, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_ADD, R6, R0),
+                       BPF_ALU64_REG(BPF_ADD, R6, R1),
+                       BPF_ALU64_REG(BPF_ADD, R6, R2),
+                       BPF_ALU64_REG(BPF_ADD, R6, R3),
+                       BPF_ALU64_REG(BPF_ADD, R6, R4),
+                       BPF_ALU64_REG(BPF_ADD, R6, R5),
+                       BPF_ALU64_REG(BPF_ADD, R6, R6),
+                       BPF_ALU64_REG(BPF_ADD, R6, R7),
+                       BPF_ALU64_REG(BPF_ADD, R6, R8),
+                       BPF_ALU64_REG(BPF_ADD, R6, R9), /* R6 == 109540 */
+                       BPF_JMP_IMM(BPF_JEQ, R6, 109540, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_ADD, R7, R0),
+                       BPF_ALU64_REG(BPF_ADD, R7, R1),
+                       BPF_ALU64_REG(BPF_ADD, R7, R2),
+                       BPF_ALU64_REG(BPF_ADD, R7, R3),
+                       BPF_ALU64_REG(BPF_ADD, R7, R4),
+                       BPF_ALU64_REG(BPF_ADD, R7, R5),
+                       BPF_ALU64_REG(BPF_ADD, R7, R6),
+                       BPF_ALU64_REG(BPF_ADD, R7, R7),
+                       BPF_ALU64_REG(BPF_ADD, R7, R8),
+                       BPF_ALU64_REG(BPF_ADD, R7, R9), /* R7 == 328605 */
+                       BPF_JMP_IMM(BPF_JEQ, R7, 328605, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_ADD, R8, R0),
+                       BPF_ALU64_REG(BPF_ADD, R8, R1),
+                       BPF_ALU64_REG(BPF_ADD, R8, R2),
+                       BPF_ALU64_REG(BPF_ADD, R8, R3),
+                       BPF_ALU64_REG(BPF_ADD, R8, R4),
+                       BPF_ALU64_REG(BPF_ADD, R8, R5),
+                       BPF_ALU64_REG(BPF_ADD, R8, R6),
+                       BPF_ALU64_REG(BPF_ADD, R8, R7),
+                       BPF_ALU64_REG(BPF_ADD, R8, R8),
+                       BPF_ALU64_REG(BPF_ADD, R8, R9), /* R8 == 985799 */
+                       BPF_JMP_IMM(BPF_JEQ, R8, 985799, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_ADD, R9, R0),
+                       BPF_ALU64_REG(BPF_ADD, R9, R1),
+                       BPF_ALU64_REG(BPF_ADD, R9, R2),
+                       BPF_ALU64_REG(BPF_ADD, R9, R3),
+                       BPF_ALU64_REG(BPF_ADD, R9, R4),
+                       BPF_ALU64_REG(BPF_ADD, R9, R5),
+                       BPF_ALU64_REG(BPF_ADD, R9, R6),
+                       BPF_ALU64_REG(BPF_ADD, R9, R7),
+                       BPF_ALU64_REG(BPF_ADD, R9, R8),
+                       BPF_ALU64_REG(BPF_ADD, R9, R9), /* R9 == 2957380 */
+                       BPF_ALU64_REG(BPF_MOV, R0, R9),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { },
+               { { 0, 2957380 } }
+       },
+       {
+               "INT: ADD 32-bit",
+               .u.insns_int = {
+                       BPF_ALU32_IMM(BPF_MOV, R0, 20),
+                       BPF_ALU32_IMM(BPF_MOV, R1, 1),
+                       BPF_ALU32_IMM(BPF_MOV, R2, 2),
+                       BPF_ALU32_IMM(BPF_MOV, R3, 3),
+                       BPF_ALU32_IMM(BPF_MOV, R4, 4),
+                       BPF_ALU32_IMM(BPF_MOV, R5, 5),
+                       BPF_ALU32_IMM(BPF_MOV, R6, 6),
+                       BPF_ALU32_IMM(BPF_MOV, R7, 7),
+                       BPF_ALU32_IMM(BPF_MOV, R8, 8),
+                       BPF_ALU32_IMM(BPF_MOV, R9, 9),
+                       BPF_ALU64_IMM(BPF_ADD, R1, 10),
+                       BPF_ALU64_IMM(BPF_ADD, R2, 10),
+                       BPF_ALU64_IMM(BPF_ADD, R3, 10),
+                       BPF_ALU64_IMM(BPF_ADD, R4, 10),
+                       BPF_ALU64_IMM(BPF_ADD, R5, 10),
+                       BPF_ALU64_IMM(BPF_ADD, R6, 10),
+                       BPF_ALU64_IMM(BPF_ADD, R7, 10),
+                       BPF_ALU64_IMM(BPF_ADD, R8, 10),
+                       BPF_ALU64_IMM(BPF_ADD, R9, 10),
+                       BPF_ALU32_REG(BPF_ADD, R0, R1),
+                       BPF_ALU32_REG(BPF_ADD, R0, R2),
+                       BPF_ALU32_REG(BPF_ADD, R0, R3),
+                       BPF_ALU32_REG(BPF_ADD, R0, R4),
+                       BPF_ALU32_REG(BPF_ADD, R0, R5),
+                       BPF_ALU32_REG(BPF_ADD, R0, R6),
+                       BPF_ALU32_REG(BPF_ADD, R0, R7),
+                       BPF_ALU32_REG(BPF_ADD, R0, R8),
+                       BPF_ALU32_REG(BPF_ADD, R0, R9), /* R0 == 155 */
+                       BPF_JMP_IMM(BPF_JEQ, R0, 155, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_REG(BPF_ADD, R1, R0),
+                       BPF_ALU32_REG(BPF_ADD, R1, R1),
+                       BPF_ALU32_REG(BPF_ADD, R1, R2),
+                       BPF_ALU32_REG(BPF_ADD, R1, R3),
+                       BPF_ALU32_REG(BPF_ADD, R1, R4),
+                       BPF_ALU32_REG(BPF_ADD, R1, R5),
+                       BPF_ALU32_REG(BPF_ADD, R1, R6),
+                       BPF_ALU32_REG(BPF_ADD, R1, R7),
+                       BPF_ALU32_REG(BPF_ADD, R1, R8),
+                       BPF_ALU32_REG(BPF_ADD, R1, R9), /* R1 == 456 */
+                       BPF_JMP_IMM(BPF_JEQ, R1, 456, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_REG(BPF_ADD, R2, R0),
+                       BPF_ALU32_REG(BPF_ADD, R2, R1),
+                       BPF_ALU32_REG(BPF_ADD, R2, R2),
+                       BPF_ALU32_REG(BPF_ADD, R2, R3),
+                       BPF_ALU32_REG(BPF_ADD, R2, R4),
+                       BPF_ALU32_REG(BPF_ADD, R2, R5),
+                       BPF_ALU32_REG(BPF_ADD, R2, R6),
+                       BPF_ALU32_REG(BPF_ADD, R2, R7),
+                       BPF_ALU32_REG(BPF_ADD, R2, R8),
+                       BPF_ALU32_REG(BPF_ADD, R2, R9), /* R2 == 1358 */
+                       BPF_JMP_IMM(BPF_JEQ, R2, 1358, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_REG(BPF_ADD, R3, R0),
+                       BPF_ALU32_REG(BPF_ADD, R3, R1),
+                       BPF_ALU32_REG(BPF_ADD, R3, R2),
+                       BPF_ALU32_REG(BPF_ADD, R3, R3),
+                       BPF_ALU32_REG(BPF_ADD, R3, R4),
+                       BPF_ALU32_REG(BPF_ADD, R3, R5),
+                       BPF_ALU32_REG(BPF_ADD, R3, R6),
+                       BPF_ALU32_REG(BPF_ADD, R3, R7),
+                       BPF_ALU32_REG(BPF_ADD, R3, R8),
+                       BPF_ALU32_REG(BPF_ADD, R3, R9), /* R3 == 4063 */
+                       BPF_JMP_IMM(BPF_JEQ, R3, 4063, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_REG(BPF_ADD, R4, R0),
+                       BPF_ALU32_REG(BPF_ADD, R4, R1),
+                       BPF_ALU32_REG(BPF_ADD, R4, R2),
+                       BPF_ALU32_REG(BPF_ADD, R4, R3),
+                       BPF_ALU32_REG(BPF_ADD, R4, R4),
+                       BPF_ALU32_REG(BPF_ADD, R4, R5),
+                       BPF_ALU32_REG(BPF_ADD, R4, R6),
+                       BPF_ALU32_REG(BPF_ADD, R4, R7),
+                       BPF_ALU32_REG(BPF_ADD, R4, R8),
+                       BPF_ALU32_REG(BPF_ADD, R4, R9), /* R4 == 12177 */
+                       BPF_JMP_IMM(BPF_JEQ, R4, 12177, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_REG(BPF_ADD, R5, R0),
+                       BPF_ALU32_REG(BPF_ADD, R5, R1),
+                       BPF_ALU32_REG(BPF_ADD, R5, R2),
+                       BPF_ALU32_REG(BPF_ADD, R5, R3),
+                       BPF_ALU32_REG(BPF_ADD, R5, R4),
+                       BPF_ALU32_REG(BPF_ADD, R5, R5),
+                       BPF_ALU32_REG(BPF_ADD, R5, R6),
+                       BPF_ALU32_REG(BPF_ADD, R5, R7),
+                       BPF_ALU32_REG(BPF_ADD, R5, R8),
+                       BPF_ALU32_REG(BPF_ADD, R5, R9), /* R5 == 36518 */
+                       BPF_JMP_IMM(BPF_JEQ, R5, 36518, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_REG(BPF_ADD, R6, R0),
+                       BPF_ALU32_REG(BPF_ADD, R6, R1),
+                       BPF_ALU32_REG(BPF_ADD, R6, R2),
+                       BPF_ALU32_REG(BPF_ADD, R6, R3),
+                       BPF_ALU32_REG(BPF_ADD, R6, R4),
+                       BPF_ALU32_REG(BPF_ADD, R6, R5),
+                       BPF_ALU32_REG(BPF_ADD, R6, R6),
+                       BPF_ALU32_REG(BPF_ADD, R6, R7),
+                       BPF_ALU32_REG(BPF_ADD, R6, R8),
+                       BPF_ALU32_REG(BPF_ADD, R6, R9), /* R6 == 109540 */
+                       BPF_JMP_IMM(BPF_JEQ, R6, 109540, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_REG(BPF_ADD, R7, R0),
+                       BPF_ALU32_REG(BPF_ADD, R7, R1),
+                       BPF_ALU32_REG(BPF_ADD, R7, R2),
+                       BPF_ALU32_REG(BPF_ADD, R7, R3),
+                       BPF_ALU32_REG(BPF_ADD, R7, R4),
+                       BPF_ALU32_REG(BPF_ADD, R7, R5),
+                       BPF_ALU32_REG(BPF_ADD, R7, R6),
+                       BPF_ALU32_REG(BPF_ADD, R7, R7),
+                       BPF_ALU32_REG(BPF_ADD, R7, R8),
+                       BPF_ALU32_REG(BPF_ADD, R7, R9), /* R7 == 328605 */
+                       BPF_JMP_IMM(BPF_JEQ, R7, 328605, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_REG(BPF_ADD, R8, R0),
+                       BPF_ALU32_REG(BPF_ADD, R8, R1),
+                       BPF_ALU32_REG(BPF_ADD, R8, R2),
+                       BPF_ALU32_REG(BPF_ADD, R8, R3),
+                       BPF_ALU32_REG(BPF_ADD, R8, R4),
+                       BPF_ALU32_REG(BPF_ADD, R8, R5),
+                       BPF_ALU32_REG(BPF_ADD, R8, R6),
+                       BPF_ALU32_REG(BPF_ADD, R8, R7),
+                       BPF_ALU32_REG(BPF_ADD, R8, R8),
+                       BPF_ALU32_REG(BPF_ADD, R8, R9), /* R8 == 985799 */
+                       BPF_JMP_IMM(BPF_JEQ, R8, 985799, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_REG(BPF_ADD, R9, R0),
+                       BPF_ALU32_REG(BPF_ADD, R9, R1),
+                       BPF_ALU32_REG(BPF_ADD, R9, R2),
+                       BPF_ALU32_REG(BPF_ADD, R9, R3),
+                       BPF_ALU32_REG(BPF_ADD, R9, R4),
+                       BPF_ALU32_REG(BPF_ADD, R9, R5),
+                       BPF_ALU32_REG(BPF_ADD, R9, R6),
+                       BPF_ALU32_REG(BPF_ADD, R9, R7),
+                       BPF_ALU32_REG(BPF_ADD, R9, R8),
+                       BPF_ALU32_REG(BPF_ADD, R9, R9), /* R9 == 2957380 */
+                       BPF_ALU32_REG(BPF_MOV, R0, R9),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { },
+               { { 0, 2957380 } }
+       },
+       {       /* Mainly checking JIT here. */
+               "INT: SUB",
+               .u.insns_int = {
+                       BPF_ALU64_IMM(BPF_MOV, R0, 0),
+                       BPF_ALU64_IMM(BPF_MOV, R1, 1),
+                       BPF_ALU64_IMM(BPF_MOV, R2, 2),
+                       BPF_ALU64_IMM(BPF_MOV, R3, 3),
+                       BPF_ALU64_IMM(BPF_MOV, R4, 4),
+                       BPF_ALU64_IMM(BPF_MOV, R5, 5),
+                       BPF_ALU64_IMM(BPF_MOV, R6, 6),
+                       BPF_ALU64_IMM(BPF_MOV, R7, 7),
+                       BPF_ALU64_IMM(BPF_MOV, R8, 8),
+                       BPF_ALU64_IMM(BPF_MOV, R9, 9),
+                       BPF_ALU64_REG(BPF_SUB, R0, R0),
+                       BPF_ALU64_REG(BPF_SUB, R0, R1),
+                       BPF_ALU64_REG(BPF_SUB, R0, R2),
+                       BPF_ALU64_REG(BPF_SUB, R0, R3),
+                       BPF_ALU64_REG(BPF_SUB, R0, R4),
+                       BPF_ALU64_REG(BPF_SUB, R0, R5),
+                       BPF_ALU64_REG(BPF_SUB, R0, R6),
+                       BPF_ALU64_REG(BPF_SUB, R0, R7),
+                       BPF_ALU64_REG(BPF_SUB, R0, R8),
+                       BPF_ALU64_REG(BPF_SUB, R0, R9),
+                       BPF_ALU64_IMM(BPF_SUB, R0, 10),
+                       BPF_JMP_IMM(BPF_JEQ, R0, -55, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_SUB, R1, R0),
+                       BPF_ALU64_REG(BPF_SUB, R1, R2),
+                       BPF_ALU64_REG(BPF_SUB, R1, R3),
+                       BPF_ALU64_REG(BPF_SUB, R1, R4),
+                       BPF_ALU64_REG(BPF_SUB, R1, R5),
+                       BPF_ALU64_REG(BPF_SUB, R1, R6),
+                       BPF_ALU64_REG(BPF_SUB, R1, R7),
+                       BPF_ALU64_REG(BPF_SUB, R1, R8),
+                       BPF_ALU64_REG(BPF_SUB, R1, R9),
+                       BPF_ALU64_IMM(BPF_SUB, R1, 10),
+                       BPF_ALU64_REG(BPF_SUB, R2, R0),
+                       BPF_ALU64_REG(BPF_SUB, R2, R1),
+                       BPF_ALU64_REG(BPF_SUB, R2, R3),
+                       BPF_ALU64_REG(BPF_SUB, R2, R4),
+                       BPF_ALU64_REG(BPF_SUB, R2, R5),
+                       BPF_ALU64_REG(BPF_SUB, R2, R6),
+                       BPF_ALU64_REG(BPF_SUB, R2, R7),
+                       BPF_ALU64_REG(BPF_SUB, R2, R8),
+                       BPF_ALU64_REG(BPF_SUB, R2, R9),
+                       BPF_ALU64_IMM(BPF_SUB, R2, 10),
+                       BPF_ALU64_REG(BPF_SUB, R3, R0),
+                       BPF_ALU64_REG(BPF_SUB, R3, R1),
+                       BPF_ALU64_REG(BPF_SUB, R3, R2),
+                       BPF_ALU64_REG(BPF_SUB, R3, R4),
+                       BPF_ALU64_REG(BPF_SUB, R3, R5),
+                       BPF_ALU64_REG(BPF_SUB, R3, R6),
+                       BPF_ALU64_REG(BPF_SUB, R3, R7),
+                       BPF_ALU64_REG(BPF_SUB, R3, R8),
+                       BPF_ALU64_REG(BPF_SUB, R3, R9),
+                       BPF_ALU64_IMM(BPF_SUB, R3, 10),
+                       BPF_ALU64_REG(BPF_SUB, R4, R0),
+                       BPF_ALU64_REG(BPF_SUB, R4, R1),
+                       BPF_ALU64_REG(BPF_SUB, R4, R2),
+                       BPF_ALU64_REG(BPF_SUB, R4, R3),
+                       BPF_ALU64_REG(BPF_SUB, R4, R5),
+                       BPF_ALU64_REG(BPF_SUB, R4, R6),
+                       BPF_ALU64_REG(BPF_SUB, R4, R7),
+                       BPF_ALU64_REG(BPF_SUB, R4, R8),
+                       BPF_ALU64_REG(BPF_SUB, R4, R9),
+                       BPF_ALU64_IMM(BPF_SUB, R4, 10),
+                       BPF_ALU64_REG(BPF_SUB, R5, R0),
+                       BPF_ALU64_REG(BPF_SUB, R5, R1),
+                       BPF_ALU64_REG(BPF_SUB, R5, R2),
+                       BPF_ALU64_REG(BPF_SUB, R5, R3),
+                       BPF_ALU64_REG(BPF_SUB, R5, R4),
+                       BPF_ALU64_REG(BPF_SUB, R5, R6),
+                       BPF_ALU64_REG(BPF_SUB, R5, R7),
+                       BPF_ALU64_REG(BPF_SUB, R5, R8),
+                       BPF_ALU64_REG(BPF_SUB, R5, R9),
+                       BPF_ALU64_IMM(BPF_SUB, R5, 10),
+                       BPF_ALU64_REG(BPF_SUB, R6, R0),
+                       BPF_ALU64_REG(BPF_SUB, R6, R1),
+                       BPF_ALU64_REG(BPF_SUB, R6, R2),
+                       BPF_ALU64_REG(BPF_SUB, R6, R3),
+                       BPF_ALU64_REG(BPF_SUB, R6, R4),
+                       BPF_ALU64_REG(BPF_SUB, R6, R5),
+                       BPF_ALU64_REG(BPF_SUB, R6, R7),
+                       BPF_ALU64_REG(BPF_SUB, R6, R8),
+                       BPF_ALU64_REG(BPF_SUB, R6, R9),
+                       BPF_ALU64_IMM(BPF_SUB, R6, 10),
+                       BPF_ALU64_REG(BPF_SUB, R7, R0),
+                       BPF_ALU64_REG(BPF_SUB, R7, R1),
+                       BPF_ALU64_REG(BPF_SUB, R7, R2),
+                       BPF_ALU64_REG(BPF_SUB, R7, R3),
+                       BPF_ALU64_REG(BPF_SUB, R7, R4),
+                       BPF_ALU64_REG(BPF_SUB, R7, R5),
+                       BPF_ALU64_REG(BPF_SUB, R7, R6),
+                       BPF_ALU64_REG(BPF_SUB, R7, R8),
+                       BPF_ALU64_REG(BPF_SUB, R7, R9),
+                       BPF_ALU64_IMM(BPF_SUB, R7, 10),
+                       BPF_ALU64_REG(BPF_SUB, R8, R0),
+                       BPF_ALU64_REG(BPF_SUB, R8, R1),
+                       BPF_ALU64_REG(BPF_SUB, R8, R2),
+                       BPF_ALU64_REG(BPF_SUB, R8, R3),
+                       BPF_ALU64_REG(BPF_SUB, R8, R4),
+                       BPF_ALU64_REG(BPF_SUB, R8, R5),
+                       BPF_ALU64_REG(BPF_SUB, R8, R6),
+                       BPF_ALU64_REG(BPF_SUB, R8, R7),
+                       BPF_ALU64_REG(BPF_SUB, R8, R9),
+                       BPF_ALU64_IMM(BPF_SUB, R8, 10),
+                       BPF_ALU64_REG(BPF_SUB, R9, R0),
+                       BPF_ALU64_REG(BPF_SUB, R9, R1),
+                       BPF_ALU64_REG(BPF_SUB, R9, R2),
+                       BPF_ALU64_REG(BPF_SUB, R9, R3),
+                       BPF_ALU64_REG(BPF_SUB, R9, R4),
+                       BPF_ALU64_REG(BPF_SUB, R9, R5),
+                       BPF_ALU64_REG(BPF_SUB, R9, R6),
+                       BPF_ALU64_REG(BPF_SUB, R9, R7),
+                       BPF_ALU64_REG(BPF_SUB, R9, R8),
+                       BPF_ALU64_IMM(BPF_SUB, R9, 10),
+                       BPF_ALU64_IMM(BPF_SUB, R0, 10),
+                       BPF_ALU64_IMM(BPF_NEG, R0, 0),
+                       BPF_ALU64_REG(BPF_SUB, R0, R1),
+                       BPF_ALU64_REG(BPF_SUB, R0, R2),
+                       BPF_ALU64_REG(BPF_SUB, R0, R3),
+                       BPF_ALU64_REG(BPF_SUB, R0, R4),
+                       BPF_ALU64_REG(BPF_SUB, R0, R5),
+                       BPF_ALU64_REG(BPF_SUB, R0, R6),
+                       BPF_ALU64_REG(BPF_SUB, R0, R7),
+                       BPF_ALU64_REG(BPF_SUB, R0, R8),
+                       BPF_ALU64_REG(BPF_SUB, R0, R9),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { },
+               { { 0, 11 } }
+       },
+       {       /* Mainly checking JIT here. */
+               "INT: XOR",
+               .u.insns_int = {
+                       BPF_ALU64_REG(BPF_SUB, R0, R0),
+                       BPF_ALU64_REG(BPF_XOR, R1, R1),
+                       BPF_JMP_REG(BPF_JEQ, R0, R1, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_IMM(BPF_MOV, R0, 10),
+                       BPF_ALU64_IMM(BPF_MOV, R1, -1),
+                       BPF_ALU64_REG(BPF_SUB, R1, R1),
+                       BPF_ALU64_REG(BPF_XOR, R2, R2),
+                       BPF_JMP_REG(BPF_JEQ, R1, R2, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_SUB, R2, R2),
+                       BPF_ALU64_REG(BPF_XOR, R3, R3),
+                       BPF_ALU64_IMM(BPF_MOV, R0, 10),
+                       BPF_ALU64_IMM(BPF_MOV, R1, -1),
+                       BPF_JMP_REG(BPF_JEQ, R2, R3, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_SUB, R3, R3),
+                       BPF_ALU64_REG(BPF_XOR, R4, R4),
+                       BPF_ALU64_IMM(BPF_MOV, R2, 1),
+                       BPF_ALU64_IMM(BPF_MOV, R5, -1),
+                       BPF_JMP_REG(BPF_JEQ, R3, R4, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_SUB, R4, R4),
+                       BPF_ALU64_REG(BPF_XOR, R5, R5),
+                       BPF_ALU64_IMM(BPF_MOV, R3, 1),
+                       BPF_ALU64_IMM(BPF_MOV, R7, -1),
+                       BPF_JMP_REG(BPF_JEQ, R5, R4, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_IMM(BPF_MOV, R5, 1),
+                       BPF_ALU64_REG(BPF_SUB, R5, R5),
+                       BPF_ALU64_REG(BPF_XOR, R6, R6),
+                       BPF_ALU64_IMM(BPF_MOV, R1, 1),
+                       BPF_ALU64_IMM(BPF_MOV, R8, -1),
+                       BPF_JMP_REG(BPF_JEQ, R5, R6, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_SUB, R6, R6),
+                       BPF_ALU64_REG(BPF_XOR, R7, R7),
+                       BPF_JMP_REG(BPF_JEQ, R7, R6, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_SUB, R7, R7),
+                       BPF_ALU64_REG(BPF_XOR, R8, R8),
+                       BPF_JMP_REG(BPF_JEQ, R7, R8, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_SUB, R8, R8),
+                       BPF_ALU64_REG(BPF_XOR, R9, R9),
+                       BPF_JMP_REG(BPF_JEQ, R9, R8, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_SUB, R9, R9),
+                       BPF_ALU64_REG(BPF_XOR, R0, R0),
+                       BPF_JMP_REG(BPF_JEQ, R9, R0, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_SUB, R1, R1),
+                       BPF_ALU64_REG(BPF_XOR, R0, R0),
+                       BPF_JMP_REG(BPF_JEQ, R9, R0, 2),
+                       BPF_ALU64_IMM(BPF_MOV, R0, 0),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_IMM(BPF_MOV, R0, 1),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { },
+               { { 0, 1 } }
+       },
+       {       /* Mainly checking JIT here. */
+               "INT: MUL",
+               .u.insns_int = {
+                       BPF_ALU64_IMM(BPF_MOV, R0, 11),
+                       BPF_ALU64_IMM(BPF_MOV, R1, 1),
+                       BPF_ALU64_IMM(BPF_MOV, R2, 2),
+                       BPF_ALU64_IMM(BPF_MOV, R3, 3),
+                       BPF_ALU64_IMM(BPF_MOV, R4, 4),
+                       BPF_ALU64_IMM(BPF_MOV, R5, 5),
+                       BPF_ALU64_IMM(BPF_MOV, R6, 6),
+                       BPF_ALU64_IMM(BPF_MOV, R7, 7),
+                       BPF_ALU64_IMM(BPF_MOV, R8, 8),
+                       BPF_ALU64_IMM(BPF_MOV, R9, 9),
+                       BPF_ALU64_REG(BPF_MUL, R0, R0),
+                       BPF_ALU64_REG(BPF_MUL, R0, R1),
+                       BPF_ALU64_REG(BPF_MUL, R0, R2),
+                       BPF_ALU64_REG(BPF_MUL, R0, R3),
+                       BPF_ALU64_REG(BPF_MUL, R0, R4),
+                       BPF_ALU64_REG(BPF_MUL, R0, R5),
+                       BPF_ALU64_REG(BPF_MUL, R0, R6),
+                       BPF_ALU64_REG(BPF_MUL, R0, R7),
+                       BPF_ALU64_REG(BPF_MUL, R0, R8),
+                       BPF_ALU64_REG(BPF_MUL, R0, R9),
+                       BPF_ALU64_IMM(BPF_MUL, R0, 10),
+                       BPF_JMP_IMM(BPF_JEQ, R0, 439084800, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_MUL, R1, R0),
+                       BPF_ALU64_REG(BPF_MUL, R1, R2),
+                       BPF_ALU64_REG(BPF_MUL, R1, R3),
+                       BPF_ALU64_REG(BPF_MUL, R1, R4),
+                       BPF_ALU64_REG(BPF_MUL, R1, R5),
+                       BPF_ALU64_REG(BPF_MUL, R1, R6),
+                       BPF_ALU64_REG(BPF_MUL, R1, R7),
+                       BPF_ALU64_REG(BPF_MUL, R1, R8),
+                       BPF_ALU64_REG(BPF_MUL, R1, R9),
+                       BPF_ALU64_IMM(BPF_MUL, R1, 10),
+                       BPF_ALU64_REG(BPF_MOV, R2, R1),
+                       BPF_ALU64_IMM(BPF_RSH, R2, 32),
+                       BPF_JMP_IMM(BPF_JEQ, R2, 0x5a924, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_IMM(BPF_LSH, R1, 32),
+                       BPF_ALU64_IMM(BPF_ARSH, R1, 32),
+                       BPF_JMP_IMM(BPF_JEQ, R1, 0xebb90000, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_MUL, R2, R0),
+                       BPF_ALU64_REG(BPF_MUL, R2, R1),
+                       BPF_ALU64_REG(BPF_MUL, R2, R3),
+                       BPF_ALU64_REG(BPF_MUL, R2, R4),
+                       BPF_ALU64_REG(BPF_MUL, R2, R5),
+                       BPF_ALU64_REG(BPF_MUL, R2, R6),
+                       BPF_ALU64_REG(BPF_MUL, R2, R7),
+                       BPF_ALU64_REG(BPF_MUL, R2, R8),
+                       BPF_ALU64_REG(BPF_MUL, R2, R9),
+                       BPF_ALU64_IMM(BPF_MUL, R2, 10),
+                       BPF_ALU64_IMM(BPF_RSH, R2, 32),
+                       BPF_ALU64_REG(BPF_MOV, R0, R2),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { },
+               { { 0, 0x35d97ef2 } }
+       },
+       {
+               "INT: ALU MIX",
+               .u.insns_int = {
+                       BPF_ALU64_IMM(BPF_MOV, R0, 11),
+                       BPF_ALU64_IMM(BPF_ADD, R0, -1),
+                       BPF_ALU64_IMM(BPF_MOV, R2, 2),
+                       BPF_ALU64_IMM(BPF_XOR, R2, 3),
+                       BPF_ALU64_REG(BPF_DIV, R0, R2),
+                       BPF_JMP_IMM(BPF_JEQ, R0, 10, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_IMM(BPF_MOD, R0, 3),
+                       BPF_JMP_IMM(BPF_JEQ, R0, 1, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_IMM(BPF_MOV, R0, -1),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { },
+               { { 0, -1 } }
+       },
+       {
+               "INT: DIV + ABS",
+               .u.insns_int = {
+                       BPF_ALU64_REG(BPF_MOV, R6, R1),
+                       BPF_LD_ABS(BPF_B, 3),
+                       BPF_ALU64_IMM(BPF_MOV, R2, 2),
+                       BPF_ALU32_REG(BPF_DIV, R0, R2),
+                       BPF_ALU64_REG(BPF_MOV, R8, R0),
+                       BPF_LD_ABS(BPF_B, 4),
+                       BPF_ALU64_REG(BPF_ADD, R8, R0),
+                       BPF_LD_IND(BPF_B, R8, -70),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { 10, 20, 30, 40, 50 },
+               { { 4, 0 }, { 5, 10 } }
+       },
+       {
+               "INT: DIV by zero",
+               .u.insns_int = {
+                       BPF_ALU64_REG(BPF_MOV, R6, R1),
+                       BPF_ALU64_IMM(BPF_MOV, R7, 0),
+                       BPF_LD_ABS(BPF_B, 3),
+                       BPF_ALU32_REG(BPF_DIV, R0, R7),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { 10, 20, 30, 40, 50 },
+               { { 3, 0 }, { 4, 0 } }
+       },
+       {
+               "check: missing ret",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IMM, 1),
+               },
+               CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
+               { },
+               { }
+       },
+       {
+               "check: div_k_0",
+               .u.insns = {
+                       BPF_STMT(BPF_ALU | BPF_DIV | BPF_K, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 0)
+               },
+               CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
+               { },
+               { }
+       },
+       {
+               "check: unknown insn",
+               .u.insns = {
+                       /* seccomp insn, rejected in socket filter */
+                       BPF_STMT(BPF_LDX | BPF_W | BPF_ABS, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 0)
+               },
+               CLASSIC | FLAG_EXPECTED_FAIL,
+               { },
+               { }
+       },
+       {
+               "check: out of range spill/fill",
+               .u.insns = {
+                       BPF_STMT(BPF_STX, 16),
+                       BPF_STMT(BPF_RET | BPF_K, 0)
+               },
+               CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
+               { },
+               { }
+       },
+       {
+               "JUMPS + HOLES",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JGE, 0, 13, 15),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ, 0x90c2894d, 3, 4),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ, 0x90c2894d, 1, 2),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JGE, 0, 14, 15),
+                       BPF_JUMP(BPF_JMP | BPF_JGE, 0, 13, 14),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ, 0x2ac28349, 2, 3),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ, 0x2ac28349, 1, 2),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JGE, 0, 14, 15),
+                       BPF_JUMP(BPF_JMP | BPF_JGE, 0, 13, 14),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ, 0x90d2ff41, 2, 3),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ, 0x90d2ff41, 1, 2),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0),
+               },
+               CLASSIC,
+               { 0x00, 0x1b, 0x21, 0x3c, 0x9d, 0xf8,
+                 0x90, 0xe2, 0xba, 0x0a, 0x56, 0xb4,
+                 0x08, 0x00,
+                 0x45, 0x00, 0x00, 0x28, 0x00, 0x00,
+                 0x20, 0x00, 0x40, 0x11, 0x00, 0x00, /* IP header */
+                 0xc0, 0xa8, 0x33, 0x01,
+                 0xc0, 0xa8, 0x33, 0x02,
+                 0xbb, 0xb6,
+                 0xa9, 0xfa,
+                 0x00, 0x14, 0x00, 0x00,
+                 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+                 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+                 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+                 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+                 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+                 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+                 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+                 0xcc, 0xcc, 0xcc, 0xcc },
+               { { 88, 0x001b } }
+       },
+       {
+               "check: RET X",
+               .u.insns = {
+                       BPF_STMT(BPF_RET | BPF_X, 0),
+               },
+               CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
+               { },
+               { },
+       },
+       {
+               "check: LDX + RET X",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 42),
+                       BPF_STMT(BPF_RET | BPF_X, 0),
+               },
+               CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
+               { },
+               { },
+       },
+       {       /* Mainly checking JIT here. */
+               "M[]: alt STX + LDX",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 100),
+                       BPF_STMT(BPF_STX, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 0),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 1),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 1),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 2),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 2),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 3),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 3),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 4),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 4),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 5),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 5),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 6),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 6),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 7),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 7),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 8),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 8),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 9),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 9),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 10),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 10),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 11),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 11),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 12),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 12),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 13),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 13),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 14),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 14),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 15),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 15),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0),
+               },
+               CLASSIC | FLAG_NO_DATA,
+               { },
+               { { 0, 116 } },
+       },
+       {       /* Mainly checking JIT here. */
+               "M[]: full STX + full LDX",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xbadfeedb),
+                       BPF_STMT(BPF_STX, 0),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xecabedae),
+                       BPF_STMT(BPF_STX, 1),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xafccfeaf),
+                       BPF_STMT(BPF_STX, 2),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xbffdcedc),
+                       BPF_STMT(BPF_STX, 3),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xfbbbdccb),
+                       BPF_STMT(BPF_STX, 4),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xfbabcbda),
+                       BPF_STMT(BPF_STX, 5),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xaedecbdb),
+                       BPF_STMT(BPF_STX, 6),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xadebbade),
+                       BPF_STMT(BPF_STX, 7),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xfcfcfaec),
+                       BPF_STMT(BPF_STX, 8),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xbcdddbdc),
+                       BPF_STMT(BPF_STX, 9),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xfeefdfac),
+                       BPF_STMT(BPF_STX, 10),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xcddcdeea),
+                       BPF_STMT(BPF_STX, 11),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xaccfaebb),
+                       BPF_STMT(BPF_STX, 12),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xbdcccdcf),
+                       BPF_STMT(BPF_STX, 13),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xaaedecde),
+                       BPF_STMT(BPF_STX, 14),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xfaeacdad),
+                       BPF_STMT(BPF_STX, 15),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 0),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 1),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 2),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 3),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 4),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 5),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 6),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 7),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 8),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 9),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 10),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 11),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 12),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 13),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 14),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 15),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0),
+               },
+               CLASSIC | FLAG_NO_DATA,
+               { },
+               { { 0, 0x2a5a5e5 } },
+       },
+       {
+               "check: SKF_AD_MAX",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_MAX),
+                       BPF_STMT(BPF_RET | BPF_A, 0),
+               },
+               CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
+               { },
+               { },
+       },
+       {       /* Passes checker but fails during runtime. */
+               "LD [SKF_AD_OFF-1]",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF - 1),
+                       BPF_STMT(BPF_RET | BPF_K, 1),
+               },
+               CLASSIC,
+               { },
+               { { 1, 0 } },
+       },
+};
+
+static struct net_device dev;
+
+static struct sk_buff *populate_skb(char *buf, int size)
+{
+       struct sk_buff *skb;
+
+       if (size >= MAX_DATA)
+               return NULL;
+
+       skb = alloc_skb(MAX_DATA, GFP_KERNEL);
+       if (!skb)
+               return NULL;
+
+       memcpy(__skb_put(skb, size), buf, size);
+
+       /* Initialize a fake skb with test pattern. */
+       skb_reset_mac_header(skb);
+       skb->protocol = htons(ETH_P_IP);
+       skb->pkt_type = SKB_TYPE;
+       skb->mark = SKB_MARK;
+       skb->hash = SKB_HASH;
+       skb->queue_mapping = SKB_QUEUE_MAP;
+       skb->vlan_tci = SKB_VLAN_TCI;
+       skb->dev = &dev;
+       skb->dev->ifindex = SKB_DEV_IFINDEX;
+       skb->dev->type = SKB_DEV_TYPE;
+       skb_set_network_header(skb, min(size, ETH_HLEN));
+
+       return skb;
+}
+
+static void *generate_test_data(struct bpf_test *test, int sub)
+{
+       if (test->aux & FLAG_NO_DATA)
+               return NULL;
+
+       /* Test case expects an skb, so populate one. Various
+        * subtests generate skbs of different sizes based on
+        * the same data.
+        */
+       return populate_skb(test->data, test->test[sub].data_size);
+}
+
+static void release_test_data(const struct bpf_test *test, void *data)
+{
+       if (test->aux & FLAG_NO_DATA)
+               return;
+
+       kfree_skb(data);
+}
+
+static int probe_filter_length(struct sock_filter *fp)
+{
+       int len = 0;
+
+       for (len = MAX_INSNS - 1; len > 0; --len)
+               if (fp[len].code != 0 || fp[len].k != 0)
+                       break;
+
+       return len + 1;
+}
+
+static struct sk_filter *generate_filter(int which, int *err)
+{
+       struct sk_filter *fp;
+       struct sock_fprog_kern fprog;
+       unsigned int flen = probe_filter_length(tests[which].u.insns);
+       __u8 test_type = tests[which].aux & TEST_TYPE_MASK;
+
+       switch (test_type) {
+       case CLASSIC:
+               fprog.filter = tests[which].u.insns;
+               fprog.len = flen;
+
+               *err = sk_unattached_filter_create(&fp, &fprog);
+               if (tests[which].aux & FLAG_EXPECTED_FAIL) {
+                       if (*err == -EINVAL) {
+                               pr_cont("PASS\n");
+                               /* Verifier rejected filter as expected. */
+                               *err = 0;
+                               return NULL;
+                       } else {
+                               pr_cont("UNEXPECTED_PASS\n");
+                               /* Verifier didn't reject the test that's
+                                * bad enough, just return!
+                                */
+                               *err = -EINVAL;
+                               return NULL;
+                       }
+               }
+               /* We don't expect to fail. */
+               if (*err) {
+                       pr_cont("FAIL to attach err=%d len=%d\n",
+                               *err, fprog.len);
+                       return NULL;
+               }
+               break;
+
+       case INTERNAL:
+               fp = kzalloc(sk_filter_size(flen), GFP_KERNEL);
+               if (fp == NULL) {
+                       pr_cont("UNEXPECTED_FAIL no memory left\n");
+                       *err = -ENOMEM;
+                       return NULL;
+               }
+
+               fp->len = flen;
+               memcpy(fp->insnsi, tests[which].u.insns_int,
+                      fp->len * sizeof(struct sock_filter_int));
+
+               sk_filter_select_runtime(fp);
+               break;
+       }
+
+       *err = 0;
+       return fp;
+}
+
+static void release_filter(struct sk_filter *fp, int which)
+{
+       __u8 test_type = tests[which].aux & TEST_TYPE_MASK;
+
+       switch (test_type) {
+       case CLASSIC:
+               sk_unattached_filter_destroy(fp);
+               break;
+       case INTERNAL:
+               sk_filter_free(fp);
+               break;
+       }
+}
+
+static int __run_one(const struct sk_filter *fp, const void *data,
+                    int runs, u64 *duration)
+{
+       u64 start, finish;
+       int ret, i;
+
+       start = ktime_to_us(ktime_get());
+
+       for (i = 0; i < runs; i++)
+               ret = SK_RUN_FILTER(fp, data);
+
+       finish = ktime_to_us(ktime_get());
+
+       *duration = (finish - start) * 1000ULL;
+       do_div(*duration, runs);
+
+       return ret;
+}
+
+static int run_one(const struct sk_filter *fp, struct bpf_test *test)
+{
+       int err_cnt = 0, i, runs = MAX_TESTRUNS;
+
+       for (i = 0; i < MAX_SUBTESTS; i++) {
+               void *data;
+               u64 duration;
+               u32 ret;
+
+               if (test->test[i].data_size == 0 &&
+                   test->test[i].result == 0)
+                       break;
+
+               data = generate_test_data(test, i);
+               ret = __run_one(fp, data, runs, &duration);
+               release_test_data(test, data);
+
+               if (ret == test->test[i].result) {
+                       pr_cont("%lld ", duration);
+               } else {
+                       pr_cont("ret %d != %d ", ret,
+                               test->test[i].result);
+                       err_cnt++;
+               }
+       }
+
+       return err_cnt;
+}
+
+static __init int test_bpf(void)
+{
+       int i, err_cnt = 0, pass_cnt = 0;
+
+       for (i = 0; i < ARRAY_SIZE(tests); i++) {
+               struct sk_filter *fp;
+               int err;
+
+               pr_info("#%d %s ", i, tests[i].descr);
+
+               fp = generate_filter(i, &err);
+               if (fp == NULL) {
+                       if (err == 0) {
+                               pass_cnt++;
+                               continue;
+                       }
+
+                       return err;
+               }
+               err = run_one(fp, &tests[i]);
+               release_filter(fp, i);
+
+               if (err) {
+                       pr_cont("FAIL (%d times)\n", err);
+                       err_cnt++;
+               } else {
+                       pr_cont("PASS\n");
+                       pass_cnt++;
+               }
+       }
+
+       pr_info("Summary: %d PASSED, %d FAILED\n", pass_cnt, err_cnt);
+       return err_cnt ? -EINVAL : 0;
+}
+
+static int __init test_bpf_init(void)
+{
+       return test_bpf();
+}
+
+static void __exit test_bpf_exit(void)
+{
+}
+
+module_init(test_bpf_init);
+module_exit(test_bpf_exit);
+
+MODULE_LICENSE("GPL");
index 7fadf1c6283844f07727a68fe12ce5f554f2fff6..dafb06f70a09dd97b1fa690969a638f596714091 100644 (file)
@@ -1665,96 +1665,42 @@ out:
        return written ? written : error;
 }
 
-/*
- * Performs necessary checks before doing a write
- * @iov:       io vector request
- * @nr_segs:   number of segments in the iovec
- * @count:     number of bytes to write
- * @access_flags: type of access: %VERIFY_READ or %VERIFY_WRITE
- *
- * Adjust number of segments and amount of bytes to write (nr_segs should be
- * properly initialized first). Returns appropriate error code that caller
- * should return or zero in case that write should be allowed.
- */
-int generic_segment_checks(const struct iovec *iov,
-                       unsigned long *nr_segs, size_t *count, int access_flags)
-{
-       unsigned long   seg;
-       size_t cnt = 0;
-       for (seg = 0; seg < *nr_segs; seg++) {
-               const struct iovec *iv = &iov[seg];
-
-               /*
-                * If any segment has a negative length, or the cumulative
-                * length ever wraps negative then return -EINVAL.
-                */
-               cnt += iv->iov_len;
-               if (unlikely((ssize_t)(cnt|iv->iov_len) < 0))
-                       return -EINVAL;
-               if (access_ok(access_flags, iv->iov_base, iv->iov_len))
-                       continue;
-               if (seg == 0)
-                       return -EFAULT;
-               *nr_segs = seg;
-               cnt -= iv->iov_len;     /* This segment is no good */
-               break;
-       }
-       *count = cnt;
-       return 0;
-}
-EXPORT_SYMBOL(generic_segment_checks);
-
 /**
- * generic_file_aio_read - generic filesystem read routine
+ * generic_file_read_iter - generic filesystem read routine
  * @iocb:      kernel I/O control block
- * @iov:       io vector request
- * @nr_segs:   number of segments in the iovec
- * @pos:       current file position
+ * @iter:      destination for the data read
  *
- * This is the "read()" routine for all filesystems
+ * This is the "read_iter()" routine for all filesystems
  * that can use the page cache directly.
  */
 ssize_t
-generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
-               unsigned long nr_segs, loff_t pos)
+generic_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
 {
-       struct file *filp = iocb->ki_filp;
-       ssize_t retval;
-       size_t count;
+       struct file *file = iocb->ki_filp;
+       ssize_t retval = 0;
        loff_t *ppos = &iocb->ki_pos;
-       struct iov_iter i;
-
-       count = 0;
-       retval = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE);
-       if (retval)
-               return retval;
-       iov_iter_init(&i, iov, nr_segs, count, 0);
+       loff_t pos = *ppos;
 
        /* coalesce the iovecs and go direct-to-BIO for O_DIRECT */
-       if (filp->f_flags & O_DIRECT) {
+       if (file->f_flags & O_DIRECT) {
+               struct address_space *mapping = file->f_mapping;
+               struct inode *inode = mapping->host;
+               size_t count = iov_iter_count(iter);
                loff_t size;
-               struct address_space *mapping;
-               struct inode *inode;
 
-               mapping = filp->f_mapping;
-               inode = mapping->host;
                if (!count)
                        goto out; /* skip atime */
                size = i_size_read(inode);
                retval = filemap_write_and_wait_range(mapping, pos,
-                                       pos + iov_length(iov, nr_segs) - 1);
+                                       pos + count - 1);
                if (!retval) {
-                       retval = mapping->a_ops->direct_IO(READ, iocb,
-                                                          iov, pos, nr_segs);
+                       struct iov_iter data = *iter;
+                       retval = mapping->a_ops->direct_IO(READ, iocb, &data, pos);
                }
+
                if (retval > 0) {
                        *ppos = pos + retval;
-                       count -= retval;
-                       /*
-                        * If we did a short DIO read we need to skip the
-                        * section of the iov that we've already read data into.
-                        */
-                       iov_iter_advance(&i, retval);
+                       iov_iter_advance(iter, retval);
                }
 
                /*
@@ -1765,17 +1711,17 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
                 * and return.  Otherwise fallthrough to buffered io for
                 * the rest of the read.
                 */
-               if (retval < 0 || !count || *ppos >= size) {
-                       file_accessed(filp);
+               if (retval < 0 || !iov_iter_count(iter) || *ppos >= size) {
+                       file_accessed(file);
                        goto out;
                }
        }
 
-       retval = do_generic_file_read(filp, ppos, &i, retval);
+       retval = do_generic_file_read(file, ppos, iter, retval);
 out:
        return retval;
 }
-EXPORT_SYMBOL(generic_file_aio_read);
+EXPORT_SYMBOL(generic_file_read_iter);
 
 #ifdef CONFIG_MMU
 /**
@@ -2386,9 +2332,7 @@ int pagecache_write_end(struct file *file, struct address_space *mapping,
 EXPORT_SYMBOL(pagecache_write_end);
 
 ssize_t
-generic_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
-               unsigned long *nr_segs, loff_t pos,
-               size_t count, size_t ocount)
+generic_file_direct_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos)
 {
        struct file     *file = iocb->ki_filp;
        struct address_space *mapping = file->f_mapping;
@@ -2396,11 +2340,9 @@ generic_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
        ssize_t         written;
        size_t          write_len;
        pgoff_t         end;
+       struct iov_iter data;
 
-       if (count != ocount)
-               *nr_segs = iov_shorten((struct iovec *)iov, *nr_segs, count);
-
-       write_len = iov_length(iov, *nr_segs);
+       write_len = iov_iter_count(from);
        end = (pos + write_len - 1) >> PAGE_CACHE_SHIFT;
 
        written = filemap_write_and_wait_range(mapping, pos, pos + write_len - 1);
@@ -2427,7 +2369,8 @@ generic_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
                }
        }
 
-       written = mapping->a_ops->direct_IO(WRITE, iocb, iov, pos, *nr_segs);
+       data = *from;
+       written = mapping->a_ops->direct_IO(WRITE, iocb, &data, pos);
 
        /*
         * Finally, try again to invalidate clean pages which might have been
@@ -2444,6 +2387,7 @@ generic_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
 
        if (written > 0) {
                pos += written;
+               iov_iter_advance(from, written);
                if (pos > i_size_read(inode) && !S_ISBLK(inode->i_mode)) {
                        i_size_write(inode, pos);
                        mark_inode_dirty(inode);
@@ -2568,10 +2512,9 @@ again:
 EXPORT_SYMBOL(generic_perform_write);
 
 /**
- * __generic_file_aio_write - write data to a file
+ * __generic_file_write_iter - write data to a file
  * @iocb:      IO state structure (file, offset, etc.)
- * @iov:       vector with data to write
- * @nr_segs:   number of segments in the vector
+ * @from:      iov_iter with data to write
  *
  * This function does all the work needed for actually writing data to a
  * file. It does all basic checks, removes SUID from the file, updates
@@ -2585,26 +2528,16 @@ EXPORT_SYMBOL(generic_perform_write);
  * A caller has to handle it. This is mainly due to the fact that we want to
  * avoid syncing under i_mutex.
  */
-ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
-                                unsigned long nr_segs)
+ssize_t __generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
 {
        struct file *file = iocb->ki_filp;
        struct address_space * mapping = file->f_mapping;
-       size_t ocount;          /* original count */
-       size_t count;           /* after file limit checks */
        struct inode    *inode = mapping->host;
        loff_t          pos = iocb->ki_pos;
        ssize_t         written = 0;
        ssize_t         err;
        ssize_t         status;
-       struct iov_iter from;
-
-       ocount = 0;
-       err = generic_segment_checks(iov, &nr_segs, &ocount, VERIFY_READ);
-       if (err)
-               return err;
-
-       count = ocount;
+       size_t          count = iov_iter_count(from);
 
        /* We can write back this queue in page reclaim */
        current->backing_dev_info = mapping->backing_dev_info;
@@ -2615,6 +2548,8 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
        if (count == 0)
                goto out;
 
+       iov_iter_truncate(from, count);
+
        err = file_remove_suid(file);
        if (err)
                goto out;
@@ -2623,17 +2558,13 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
        if (err)
                goto out;
 
-       iov_iter_init(&from, iov, nr_segs, count, 0);
-
        /* coalesce the iovecs and go direct-to-BIO for O_DIRECT */
        if (unlikely(file->f_flags & O_DIRECT)) {
                loff_t endbyte;
 
-               written = generic_file_direct_write(iocb, iov, &from.nr_segs, pos,
-                                                       count, ocount);
+               written = generic_file_direct_write(iocb, from, pos);
                if (written < 0 || written == count)
                        goto out;
-               iov_iter_advance(&from, written);
 
                /*
                 * direct-io write to a hole: fall through to buffered I/O
@@ -2642,7 +2573,7 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
                pos += written;
                count -= written;
 
-               status = generic_perform_write(file, &from, pos);
+               status = generic_perform_write(file, from, pos);
                /*
                 * If generic_perform_write() returned a synchronous error
                 * then we want to return the number of bytes which were
@@ -2674,7 +2605,7 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
                         */
                }
        } else {
-               written = generic_perform_write(file, &from, pos);
+               written = generic_perform_write(file, from, pos);
                if (likely(written >= 0))
                        iocb->ki_pos = pos + written;
        }
@@ -2682,30 +2613,25 @@ out:
        current->backing_dev_info = NULL;
        return written ? written : err;
 }
-EXPORT_SYMBOL(__generic_file_aio_write);
+EXPORT_SYMBOL(__generic_file_write_iter);
 
 /**
- * generic_file_aio_write - write data to a file
+ * generic_file_write_iter - write data to a file
  * @iocb:      IO state structure
- * @iov:       vector with data to write
- * @nr_segs:   number of segments in the vector
- * @pos:       position in file where to write
+ * @from:      iov_iter with data to write
  *
- * This is a wrapper around __generic_file_aio_write() to be used by most
+ * This is a wrapper around __generic_file_write_iter() to be used by most
  * filesystems. It takes care of syncing the file in case of O_SYNC file
  * and acquires i_mutex as needed.
  */
-ssize_t generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
-               unsigned long nr_segs, loff_t pos)
+ssize_t generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
 {
        struct file *file = iocb->ki_filp;
        struct inode *inode = file->f_mapping->host;
        ssize_t ret;
 
-       BUG_ON(iocb->ki_pos != pos);
-
        mutex_lock(&inode->i_mutex);
-       ret = __generic_file_aio_write(iocb, iov, nr_segs);
+       ret = __generic_file_write_iter(iocb, from);
        mutex_unlock(&inode->i_mutex);
 
        if (ret > 0) {
@@ -2717,7 +2643,7 @@ ssize_t generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
        }
        return ret;
 }
-EXPORT_SYMBOL(generic_file_aio_write);
+EXPORT_SYMBOL(generic_file_write_iter);
 
 /**
  * try_to_release_page() - release old fs-specific metadata on a page
index 10e46cd721de5876d0016aab5969d0b2c6ebdeb3..7b5dbd1517b5594b05d5590cae29c3eb3a1dada2 100644 (file)
@@ -1,8 +1,10 @@
 #include <linux/export.h>
 #include <linux/uio.h>
 #include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
 
-size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
+static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t bytes,
                         struct iov_iter *i)
 {
        size_t skip, copy, left, wanted;
@@ -72,13 +74,97 @@ size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
        }
        kunmap(page);
 done:
+       if (skip == iov->iov_len) {
+               iov++;
+               skip = 0;
+       }
+       i->count -= wanted - bytes;
+       i->nr_segs -= iov - i->iov;
+       i->iov = iov;
+       i->iov_offset = skip;
+       return wanted - bytes;
+}
+
+static size_t copy_page_from_iter_iovec(struct page *page, size_t offset, size_t bytes,
+                        struct iov_iter *i)
+{
+       size_t skip, copy, left, wanted;
+       const struct iovec *iov;
+       char __user *buf;
+       void *kaddr, *to;
+
+       if (unlikely(bytes > i->count))
+               bytes = i->count;
+
+       if (unlikely(!bytes))
+               return 0;
+
+       wanted = bytes;
+       iov = i->iov;
+       skip = i->iov_offset;
+       buf = iov->iov_base + skip;
+       copy = min(bytes, iov->iov_len - skip);
+
+       if (!fault_in_pages_readable(buf, copy)) {
+               kaddr = kmap_atomic(page);
+               to = kaddr + offset;
+
+               /* first chunk, usually the only one */
+               left = __copy_from_user_inatomic(to, buf, copy);
+               copy -= left;
+               skip += copy;
+               to += copy;
+               bytes -= copy;
+
+               while (unlikely(!left && bytes)) {
+                       iov++;
+                       buf = iov->iov_base;
+                       copy = min(bytes, iov->iov_len);
+                       left = __copy_from_user_inatomic(to, buf, copy);
+                       copy -= left;
+                       skip = copy;
+                       to += copy;
+                       bytes -= copy;
+               }
+               if (likely(!bytes)) {
+                       kunmap_atomic(kaddr);
+                       goto done;
+               }
+               offset = to - kaddr;
+               buf += copy;
+               kunmap_atomic(kaddr);
+               copy = min(bytes, iov->iov_len - skip);
+       }
+       /* Too bad - revert to non-atomic kmap */
+       kaddr = kmap(page);
+       to = kaddr + offset;
+       left = __copy_from_user(to, buf, copy);
+       copy -= left;
+       skip += copy;
+       to += copy;
+       bytes -= copy;
+       while (unlikely(!left && bytes)) {
+               iov++;
+               buf = iov->iov_base;
+               copy = min(bytes, iov->iov_len);
+               left = __copy_from_user(to, buf, copy);
+               copy -= left;
+               skip = copy;
+               to += copy;
+               bytes -= copy;
+       }
+       kunmap(page);
+done:
+       if (skip == iov->iov_len) {
+               iov++;
+               skip = 0;
+       }
        i->count -= wanted - bytes;
        i->nr_segs -= iov - i->iov;
        i->iov = iov;
        i->iov_offset = skip;
        return wanted - bytes;
 }
-EXPORT_SYMBOL(copy_page_to_iter);
 
 static size_t __iovec_copy_from_user_inatomic(char *vaddr,
                        const struct iovec *iov, size_t base, size_t bytes)
@@ -107,7 +193,7 @@ static size_t __iovec_copy_from_user_inatomic(char *vaddr,
  * were successfully copied.  If a fault is encountered then return the number of
  * bytes which were copied.
  */
-size_t iov_iter_copy_from_user_atomic(struct page *page,
+static size_t copy_from_user_atomic_iovec(struct page *page,
                struct iov_iter *i, unsigned long offset, size_t bytes)
 {
        char *kaddr;
@@ -127,36 +213,8 @@ size_t iov_iter_copy_from_user_atomic(struct page *page,
 
        return copied;
 }
-EXPORT_SYMBOL(iov_iter_copy_from_user_atomic);
-
-/*
- * This has the same sideeffects and return value as
- * iov_iter_copy_from_user_atomic().
- * The difference is that it attempts to resolve faults.
- * Page must not be locked.
- */
-size_t iov_iter_copy_from_user(struct page *page,
-               struct iov_iter *i, unsigned long offset, size_t bytes)
-{
-       char *kaddr;
-       size_t copied;
-
-       kaddr = kmap(page);
-       if (likely(i->nr_segs == 1)) {
-               int left;
-               char __user *buf = i->iov->iov_base + i->iov_offset;
-               left = __copy_from_user(kaddr + offset, buf, bytes);
-               copied = bytes - left;
-       } else {
-               copied = __iovec_copy_from_user_inatomic(kaddr + offset,
-                                               i->iov, i->iov_offset, bytes);
-       }
-       kunmap(page);
-       return copied;
-}
-EXPORT_SYMBOL(iov_iter_copy_from_user);
 
-void iov_iter_advance(struct iov_iter *i, size_t bytes)
+static void advance_iovec(struct iov_iter *i, size_t bytes)
 {
        BUG_ON(i->count < bytes);
 
@@ -191,7 +249,6 @@ void iov_iter_advance(struct iov_iter *i, size_t bytes)
                i->nr_segs = nr_segs;
        }
 }
-EXPORT_SYMBOL(iov_iter_advance);
 
 /*
  * Fault in the first iovec of the given iov_iter, to a maximum length
@@ -204,21 +261,483 @@ EXPORT_SYMBOL(iov_iter_advance);
  */
 int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes)
 {
-       char __user *buf = i->iov->iov_base + i->iov_offset;
-       bytes = min(bytes, i->iov->iov_len - i->iov_offset);
-       return fault_in_pages_readable(buf, bytes);
+       if (!(i->type & ITER_BVEC)) {
+               char __user *buf = i->iov->iov_base + i->iov_offset;
+               bytes = min(bytes, i->iov->iov_len - i->iov_offset);
+               return fault_in_pages_readable(buf, bytes);
+       }
+       return 0;
 }
 EXPORT_SYMBOL(iov_iter_fault_in_readable);
 
+static unsigned long alignment_iovec(const struct iov_iter *i)
+{
+       const struct iovec *iov = i->iov;
+       unsigned long res;
+       size_t size = i->count;
+       size_t n;
+
+       if (!size)
+               return 0;
+
+       res = (unsigned long)iov->iov_base + i->iov_offset;
+       n = iov->iov_len - i->iov_offset;
+       if (n >= size)
+               return res | size;
+       size -= n;
+       res |= n;
+       while (size > (++iov)->iov_len) {
+               res |= (unsigned long)iov->iov_base | iov->iov_len;
+               size -= iov->iov_len;
+       }
+       res |= (unsigned long)iov->iov_base | size;
+       return res;
+}
+
+void iov_iter_init(struct iov_iter *i, int direction,
+                       const struct iovec *iov, unsigned long nr_segs,
+                       size_t count)
+{
+       /* It will get better.  Eventually... */
+       if (segment_eq(get_fs(), KERNEL_DS))
+               direction |= ITER_KVEC;
+       i->type = direction;
+       i->iov = iov;
+       i->nr_segs = nr_segs;
+       i->iov_offset = 0;
+       i->count = count;
+}
+EXPORT_SYMBOL(iov_iter_init);
+
+static ssize_t get_pages_iovec(struct iov_iter *i,
+                  struct page **pages, size_t maxsize,
+                  size_t *start)
+{
+       size_t offset = i->iov_offset;
+       const struct iovec *iov = i->iov;
+       size_t len;
+       unsigned long addr;
+       int n;
+       int res;
+
+       len = iov->iov_len - offset;
+       if (len > i->count)
+               len = i->count;
+       if (len > maxsize)
+               len = maxsize;
+       addr = (unsigned long)iov->iov_base + offset;
+       len += *start = addr & (PAGE_SIZE - 1);
+       addr &= ~(PAGE_SIZE - 1);
+       n = (len + PAGE_SIZE - 1) / PAGE_SIZE;
+       res = get_user_pages_fast(addr, n, (i->type & WRITE) != WRITE, pages);
+       if (unlikely(res < 0))
+               return res;
+       return (res == n ? len : res * PAGE_SIZE) - *start;
+}
+
+static ssize_t get_pages_alloc_iovec(struct iov_iter *i,
+                  struct page ***pages, size_t maxsize,
+                  size_t *start)
+{
+       size_t offset = i->iov_offset;
+       const struct iovec *iov = i->iov;
+       size_t len;
+       unsigned long addr;
+       void *p;
+       int n;
+       int res;
+
+       len = iov->iov_len - offset;
+       if (len > i->count)
+               len = i->count;
+       if (len > maxsize)
+               len = maxsize;
+       addr = (unsigned long)iov->iov_base + offset;
+       len += *start = addr & (PAGE_SIZE - 1);
+       addr &= ~(PAGE_SIZE - 1);
+       n = (len + PAGE_SIZE - 1) / PAGE_SIZE;
+       
+       p = kmalloc(n * sizeof(struct page *), GFP_KERNEL);
+       if (!p)
+               p = vmalloc(n * sizeof(struct page *));
+       if (!p)
+               return -ENOMEM;
+
+       res = get_user_pages_fast(addr, n, (i->type & WRITE) != WRITE, p);
+       if (unlikely(res < 0)) {
+               kvfree(p);
+               return res;
+       }
+       *pages = p;
+       return (res == n ? len : res * PAGE_SIZE) - *start;
+}
+
+static int iov_iter_npages_iovec(const struct iov_iter *i, int maxpages)
+{
+       size_t offset = i->iov_offset;
+       size_t size = i->count;
+       const struct iovec *iov = i->iov;
+       int npages = 0;
+       int n;
+
+       for (n = 0; size && n < i->nr_segs; n++, iov++) {
+               unsigned long addr = (unsigned long)iov->iov_base + offset;
+               size_t len = iov->iov_len - offset;
+               offset = 0;
+               if (unlikely(!len))     /* empty segment */
+                       continue;
+               if (len > size)
+                       len = size;
+               npages += (addr + len + PAGE_SIZE - 1) / PAGE_SIZE
+                         - addr / PAGE_SIZE;
+               if (npages >= maxpages) /* don't bother going further */
+                       return maxpages;
+               size -= len;
+               offset = 0;
+       }
+       return min(npages, maxpages);
+}
+
+static void memcpy_from_page(char *to, struct page *page, size_t offset, size_t len)
+{
+       char *from = kmap_atomic(page);
+       memcpy(to, from + offset, len);
+       kunmap_atomic(from);
+}
+
+static void memcpy_to_page(struct page *page, size_t offset, char *from, size_t len)
+{
+       char *to = kmap_atomic(page);
+       memcpy(to + offset, from, len);
+       kunmap_atomic(to);
+}
+
+static size_t copy_page_to_iter_bvec(struct page *page, size_t offset, size_t bytes,
+                        struct iov_iter *i)
+{
+       size_t skip, copy, wanted;
+       const struct bio_vec *bvec;
+       void *kaddr, *from;
+
+       if (unlikely(bytes > i->count))
+               bytes = i->count;
+
+       if (unlikely(!bytes))
+               return 0;
+
+       wanted = bytes;
+       bvec = i->bvec;
+       skip = i->iov_offset;
+       copy = min_t(size_t, bytes, bvec->bv_len - skip);
+
+       kaddr = kmap_atomic(page);
+       from = kaddr + offset;
+       memcpy_to_page(bvec->bv_page, skip + bvec->bv_offset, from, copy);
+       skip += copy;
+       from += copy;
+       bytes -= copy;
+       while (bytes) {
+               bvec++;
+               copy = min(bytes, (size_t)bvec->bv_len);
+               memcpy_to_page(bvec->bv_page, bvec->bv_offset, from, copy);
+               skip = copy;
+               from += copy;
+               bytes -= copy;
+       }
+       kunmap_atomic(kaddr);
+       if (skip == bvec->bv_len) {
+               bvec++;
+               skip = 0;
+       }
+       i->count -= wanted - bytes;
+       i->nr_segs -= bvec - i->bvec;
+       i->bvec = bvec;
+       i->iov_offset = skip;
+       return wanted - bytes;
+}
+
+static size_t copy_page_from_iter_bvec(struct page *page, size_t offset, size_t bytes,
+                        struct iov_iter *i)
+{
+       size_t skip, copy, wanted;
+       const struct bio_vec *bvec;
+       void *kaddr, *to;
+
+       if (unlikely(bytes > i->count))
+               bytes = i->count;
+
+       if (unlikely(!bytes))
+               return 0;
+
+       wanted = bytes;
+       bvec = i->bvec;
+       skip = i->iov_offset;
+
+       kaddr = kmap_atomic(page);
+
+       to = kaddr + offset;
+
+       copy = min(bytes, bvec->bv_len - skip);
+
+       memcpy_from_page(to, bvec->bv_page, bvec->bv_offset + skip, copy);
+
+       to += copy;
+       skip += copy;
+       bytes -= copy;
+
+       while (bytes) {
+               bvec++;
+               copy = min(bytes, (size_t)bvec->bv_len);
+               memcpy_from_page(to, bvec->bv_page, bvec->bv_offset, copy);
+               skip = copy;
+               to += copy;
+               bytes -= copy;
+       }
+       kunmap_atomic(kaddr);
+       if (skip == bvec->bv_len) {
+               bvec++;
+               skip = 0;
+       }
+       i->count -= wanted;
+       i->nr_segs -= bvec - i->bvec;
+       i->bvec = bvec;
+       i->iov_offset = skip;
+       return wanted;
+}
+
+static size_t copy_from_user_bvec(struct page *page,
+               struct iov_iter *i, unsigned long offset, size_t bytes)
+{
+       char *kaddr;
+       size_t left;
+       const struct bio_vec *bvec;
+       size_t base = i->iov_offset;
+
+       kaddr = kmap_atomic(page);
+       for (left = bytes, bvec = i->bvec; left; bvec++, base = 0) {
+               size_t copy = min(left, bvec->bv_len - base);
+               if (!bvec->bv_len)
+                       continue;
+               memcpy_from_page(kaddr + offset, bvec->bv_page,
+                                bvec->bv_offset + base, copy);
+               offset += copy;
+               left -= copy;
+       }
+       kunmap_atomic(kaddr);
+       return bytes;
+}
+
+static void advance_bvec(struct iov_iter *i, size_t bytes)
+{
+       BUG_ON(i->count < bytes);
+
+       if (likely(i->nr_segs == 1)) {
+               i->iov_offset += bytes;
+               i->count -= bytes;
+       } else {
+               const struct bio_vec *bvec = i->bvec;
+               size_t base = i->iov_offset;
+               unsigned long nr_segs = i->nr_segs;
+
+               /*
+                * The !iov->iov_len check ensures we skip over unlikely
+                * zero-length segments (without overruning the iovec).
+                */
+               while (bytes || unlikely(i->count && !bvec->bv_len)) {
+                       int copy;
+
+                       copy = min(bytes, bvec->bv_len - base);
+                       BUG_ON(!i->count || i->count < copy);
+                       i->count -= copy;
+                       bytes -= copy;
+                       base += copy;
+                       if (bvec->bv_len == base) {
+                               bvec++;
+                               nr_segs--;
+                               base = 0;
+                       }
+               }
+               i->bvec = bvec;
+               i->iov_offset = base;
+               i->nr_segs = nr_segs;
+       }
+}
+
+static unsigned long alignment_bvec(const struct iov_iter *i)
+{
+       const struct bio_vec *bvec = i->bvec;
+       unsigned long res;
+       size_t size = i->count;
+       size_t n;
+
+       if (!size)
+               return 0;
+
+       res = bvec->bv_offset + i->iov_offset;
+       n = bvec->bv_len - i->iov_offset;
+       if (n >= size)
+               return res | size;
+       size -= n;
+       res |= n;
+       while (size > (++bvec)->bv_len) {
+               res |= bvec->bv_offset | bvec->bv_len;
+               size -= bvec->bv_len;
+       }
+       res |= bvec->bv_offset | size;
+       return res;
+}
+
+static ssize_t get_pages_bvec(struct iov_iter *i,
+                  struct page **pages, size_t maxsize,
+                  size_t *start)
+{
+       const struct bio_vec *bvec = i->bvec;
+       size_t len = bvec->bv_len - i->iov_offset;
+       if (len > i->count)
+               len = i->count;
+       if (len > maxsize)
+               len = maxsize;
+       *start = bvec->bv_offset + i->iov_offset;
+
+       get_page(*pages = bvec->bv_page);
+
+       return len;
+}
+
+static ssize_t get_pages_alloc_bvec(struct iov_iter *i,
+                  struct page ***pages, size_t maxsize,
+                  size_t *start)
+{
+       const struct bio_vec *bvec = i->bvec;
+       size_t len = bvec->bv_len - i->iov_offset;
+       if (len > i->count)
+               len = i->count;
+       if (len > maxsize)
+               len = maxsize;
+       *start = bvec->bv_offset + i->iov_offset;
+
+       *pages = kmalloc(sizeof(struct page *), GFP_KERNEL);
+       if (!*pages)
+               return -ENOMEM;
+
+       get_page(**pages = bvec->bv_page);
+
+       return len;
+}
+
+static int iov_iter_npages_bvec(const struct iov_iter *i, int maxpages)
+{
+       size_t offset = i->iov_offset;
+       size_t size = i->count;
+       const struct bio_vec *bvec = i->bvec;
+       int npages = 0;
+       int n;
+
+       for (n = 0; size && n < i->nr_segs; n++, bvec++) {
+               size_t len = bvec->bv_len - offset;
+               offset = 0;
+               if (unlikely(!len))     /* empty segment */
+                       continue;
+               if (len > size)
+                       len = size;
+               npages++;
+               if (npages >= maxpages) /* don't bother going further */
+                       return maxpages;
+               size -= len;
+               offset = 0;
+       }
+       return min(npages, maxpages);
+}
+
+size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
+                        struct iov_iter *i)
+{
+       if (i->type & ITER_BVEC)
+               return copy_page_to_iter_bvec(page, offset, bytes, i);
+       else
+               return copy_page_to_iter_iovec(page, offset, bytes, i);
+}
+EXPORT_SYMBOL(copy_page_to_iter);
+
+size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes,
+                        struct iov_iter *i)
+{
+       if (i->type & ITER_BVEC)
+               return copy_page_from_iter_bvec(page, offset, bytes, i);
+       else
+               return copy_page_from_iter_iovec(page, offset, bytes, i);
+}
+EXPORT_SYMBOL(copy_page_from_iter);
+
+size_t iov_iter_copy_from_user_atomic(struct page *page,
+               struct iov_iter *i, unsigned long offset, size_t bytes)
+{
+       if (i->type & ITER_BVEC)
+               return copy_from_user_bvec(page, i, offset, bytes);
+       else
+               return copy_from_user_atomic_iovec(page, i, offset, bytes);
+}
+EXPORT_SYMBOL(iov_iter_copy_from_user_atomic);
+
+void iov_iter_advance(struct iov_iter *i, size_t size)
+{
+       if (i->type & ITER_BVEC)
+               advance_bvec(i, size);
+       else
+               advance_iovec(i, size);
+}
+EXPORT_SYMBOL(iov_iter_advance);
+
 /*
  * Return the count of just the current iov_iter segment.
  */
 size_t iov_iter_single_seg_count(const struct iov_iter *i)
 {
-       const struct iovec *iov = i->iov;
        if (i->nr_segs == 1)
                return i->count;
+       else if (i->type & ITER_BVEC)
+               return min(i->count, i->iov->iov_len - i->iov_offset);
        else
-               return min(i->count, iov->iov_len - i->iov_offset);
+               return min(i->count, i->bvec->bv_len - i->iov_offset);
 }
 EXPORT_SYMBOL(iov_iter_single_seg_count);
+
+unsigned long iov_iter_alignment(const struct iov_iter *i)
+{
+       if (i->type & ITER_BVEC)
+               return alignment_bvec(i);
+       else
+               return alignment_iovec(i);
+}
+EXPORT_SYMBOL(iov_iter_alignment);
+
+ssize_t iov_iter_get_pages(struct iov_iter *i,
+                  struct page **pages, size_t maxsize,
+                  size_t *start)
+{
+       if (i->type & ITER_BVEC)
+               return get_pages_bvec(i, pages, maxsize, start);
+       else
+               return get_pages_iovec(i, pages, maxsize, start);
+}
+EXPORT_SYMBOL(iov_iter_get_pages);
+
+ssize_t iov_iter_get_pages_alloc(struct iov_iter *i,
+                  struct page ***pages, size_t maxsize,
+                  size_t *start)
+{
+       if (i->type & ITER_BVEC)
+               return get_pages_alloc_bvec(i, pages, maxsize, start);
+       else
+               return get_pages_alloc_iovec(i, pages, maxsize, start);
+}
+EXPORT_SYMBOL(iov_iter_get_pages_alloc);
+
+int iov_iter_npages(const struct iov_iter *i, int maxpages)
+{
+       if (i->type & ITER_BVEC)
+               return iov_iter_npages_bvec(i, maxpages);
+       else
+               return iov_iter_npages_iovec(i, maxpages);
+}
+EXPORT_SYMBOL(iov_iter_npages);
index 58b50d2901fe2a43a916bae15daffb5c8c6f1cbe..955db8b0d4970aa38b0ada56ac5f221618ed74aa 100644 (file)
@@ -264,10 +264,18 @@ int __swap_writepage(struct page *page, struct writeback_control *wbc,
                struct kiocb kiocb;
                struct file *swap_file = sis->swap_file;
                struct address_space *mapping = swap_file->f_mapping;
-               struct iovec iov = {
-                       .iov_base = kmap(page),
-                       .iov_len  = PAGE_SIZE,
+               struct bio_vec bv = {
+                       .bv_page = page,
+                       .bv_len  = PAGE_SIZE,
+                       .bv_offset = 0
                };
+               struct iov_iter from = {
+                       .type = ITER_BVEC | WRITE,
+                       .count = PAGE_SIZE,
+                       .iov_offset = 0,
+                       .nr_segs = 1,
+               };
+               from.bvec = &bv;        /* older gcc versions are broken */
 
                init_sync_kiocb(&kiocb, swap_file);
                kiocb.ki_pos = page_file_offset(page);
@@ -275,10 +283,9 @@ int __swap_writepage(struct page *page, struct writeback_control *wbc,
 
                set_page_writeback(page);
                unlock_page(page);
-               ret = mapping->a_ops->direct_IO(KERNEL_WRITE,
-                                               &kiocb, &iov,
-                                               kiocb.ki_pos, 1);
-               kunmap(page);
+               ret = mapping->a_ops->direct_IO(ITER_BVEC | WRITE,
+                                               &kiocb, &from,
+                                               kiocb.ki_pos);
                if (ret == PAGE_SIZE) {
                        count_vm_event(PSWPOUT);
                        ret = 0;
index 8505c9262b35853e22580c6c9b74c4d12bc86acc..5077afcd9e116b16b17c7b0ed51930d570f88701 100644 (file)
@@ -46,11 +46,7 @@ static int process_vm_rw_pages(struct page **pages,
                        copy = len;
 
                if (vm_write) {
-                       if (copy > iov_iter_count(iter))
-                               copy = iov_iter_count(iter);
-                       copied = iov_iter_copy_from_user(page, iter,
-                                       offset, copy);
-                       iov_iter_advance(iter, copied);
+                       copied = copy_page_from_iter(page, offset, copy, iter);
                        set_page_dirty_lock(page);
                } else {
                        copied = copy_page_to_iter(page, offset, copy, iter);
@@ -278,7 +274,7 @@ static ssize_t process_vm_rw(pid_t pid,
        if (rc <= 0)
                goto free_iovecs;
 
-       iov_iter_init(&iter, iov_l, liovcnt, rc, 0);
+       iov_iter_init(&iter, vm_write ? WRITE : READ, iov_l, liovcnt, rc);
 
        rc = rw_copy_check_uvector(CHECK_IOVEC_ONLY, rvec, riovcnt, UIO_FASTIOV,
                                   iovstack_r, &iov_r);
@@ -341,7 +337,7 @@ compat_process_vm_rw(compat_pid_t pid,
                                                  &iov_l);
        if (rc <= 0)
                goto free_iovecs;
-       iov_iter_init(&iter, iov_l, liovcnt, rc, 0);
+       iov_iter_init(&iter, vm_write ? WRITE : READ, iov_l, liovcnt, rc);
        rc = compat_rw_copy_check_uvector(CHECK_IOVEC_ONLY, rvec, riovcnt,
                                          UIO_FASTIOV, iovstack_r,
                                          &iov_r);
index 5402481c28d190a83718f6b1897eec78df4c0209..f484c276e994923a5c05577b42d5a9dcc58ae7cc 100644 (file)
@@ -1406,8 +1406,7 @@ shmem_write_end(struct file *file, struct address_space *mapping,
        return copied;
 }
 
-static ssize_t shmem_file_aio_read(struct kiocb *iocb,
-               const struct iovec *iov, unsigned long nr_segs, loff_t pos)
+static ssize_t shmem_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
 {
        struct file *file = iocb->ki_filp;
        struct inode *inode = file_inode(file);
@@ -1416,15 +1415,8 @@ static ssize_t shmem_file_aio_read(struct kiocb *iocb,
        unsigned long offset;
        enum sgp_type sgp = SGP_READ;
        int error = 0;
-       ssize_t retval;
-       size_t count;
+       ssize_t retval = 0;
        loff_t *ppos = &iocb->ki_pos;
-       struct iov_iter iter;
-
-       retval = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE);
-       if (retval)
-               return retval;
-       iov_iter_init(&iter, iov, nr_segs, count, 0);
 
        /*
         * Might this read be for a stacking filesystem?  Then when reading
@@ -1500,14 +1492,14 @@ static ssize_t shmem_file_aio_read(struct kiocb *iocb,
                 * Ok, we have the page, and it's up-to-date, so
                 * now we can copy it to user space...
                 */
-               ret = copy_page_to_iter(page, offset, nr, &iter);
+               ret = copy_page_to_iter(page, offset, nr, to);
                retval += ret;
                offset += ret;
                index += offset >> PAGE_CACHE_SHIFT;
                offset &= ~PAGE_CACHE_MASK;
 
                page_cache_release(page);
-               if (!iov_iter_count(&iter))
+               if (!iov_iter_count(to))
                        break;
                if (ret < nr) {
                        error = -EFAULT;
@@ -2629,13 +2621,13 @@ static const struct file_operations shmem_file_operations = {
        .mmap           = shmem_mmap,
 #ifdef CONFIG_TMPFS
        .llseek         = shmem_file_llseek,
-       .read           = do_sync_read,
-       .write          = do_sync_write,
-       .aio_read       = shmem_file_aio_read,
-       .aio_write      = generic_file_aio_write,
+       .read           = new_sync_read,
+       .write          = new_sync_write,
+       .read_iter      = shmem_file_read_iter,
+       .write_iter     = generic_file_write_iter,
        .fsync          = noop_fsync,
        .splice_read    = shmem_file_splice_read,
-       .splice_write   = generic_file_splice_write,
+       .splice_write   = iter_file_splice_write,
        .fallocate      = shmem_fallocate,
 #endif
 };
index e01ded365440704dbec95f0ec8f56326d646b9c2..0f16ffe8eb67c6fcd0350add4a5a4b6092cb6905 100644 (file)
@@ -464,7 +464,7 @@ static pageout_t pageout(struct page *page, struct address_space *mapping,
         * stalls if we need to run get_block().  We could test
         * PagePrivate for that.
         *
-        * If this process is currently in __generic_file_aio_write() against
+        * If this process is currently in __generic_file_write_iter() against
         * this page's queue, we can perform writeback even if that
         * will block.
         *
index 3c32bd257b73975a33ba104c1c3b3797d9f29843..9012b1c922b61acd28fffb7f50b4968da9293b2f 100644 (file)
@@ -63,7 +63,7 @@ bool vlan_do_receive(struct sk_buff **skbp)
 }
 
 /* Must be invoked with rcu_read_lock. */
-struct net_device *__vlan_find_dev_deep(struct net_device *dev,
+struct net_device *__vlan_find_dev_deep_rcu(struct net_device *dev,
                                        __be16 vlan_proto, u16 vlan_id)
 {
        struct vlan_info *vlan_info = rcu_dereference(dev->vlan_info);
@@ -81,13 +81,13 @@ struct net_device *__vlan_find_dev_deep(struct net_device *dev,
 
                upper_dev = netdev_master_upper_dev_get_rcu(dev);
                if (upper_dev)
-                       return __vlan_find_dev_deep(upper_dev,
+                       return __vlan_find_dev_deep_rcu(upper_dev,
                                                    vlan_proto, vlan_id);
        }
 
        return NULL;
 }
-EXPORT_SYMBOL(__vlan_find_dev_deep);
+EXPORT_SYMBOL(__vlan_find_dev_deep_rcu);
 
 struct net_device *vlan_dev_real_dev(const struct net_device *dev)
 {
index 019efb79708f81976bc6484cc371a8fa6c0c080e..ad2ac3c003988741c066c2bb467c2abc4a523c5a 100644 (file)
@@ -643,9 +643,9 @@ static netdev_features_t vlan_dev_fix_features(struct net_device *dev,
        struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;
        netdev_features_t old_features = features;
 
-       features &= real_dev->vlan_features;
+       features = netdev_intersect_features(features, real_dev->vlan_features);
        features |= NETIF_F_RXCSUM;
-       features &= real_dev->features;
+       features = netdev_intersect_features(features, real_dev->features);
 
        features |= old_features & NETIF_F_SOFT_FEATURES;
        features |= NETIF_F_LLTX;
@@ -671,38 +671,36 @@ static void vlan_ethtool_get_drvinfo(struct net_device *dev,
 
 static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
+       struct vlan_pcpu_stats *p;
+       u32 rx_errors = 0, tx_dropped = 0;
+       int i;
 
-       if (vlan_dev_priv(dev)->vlan_pcpu_stats) {
-               struct vlan_pcpu_stats *p;
-               u32 rx_errors = 0, tx_dropped = 0;
-               int i;
-
-               for_each_possible_cpu(i) {
-                       u64 rxpackets, rxbytes, rxmulticast, txpackets, txbytes;
-                       unsigned int start;
-
-                       p = per_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats, i);
-                       do {
-                               start = u64_stats_fetch_begin_irq(&p->syncp);
-                               rxpackets       = p->rx_packets;
-                               rxbytes         = p->rx_bytes;
-                               rxmulticast     = p->rx_multicast;
-                               txpackets       = p->tx_packets;
-                               txbytes         = p->tx_bytes;
-                       } while (u64_stats_fetch_retry_irq(&p->syncp, start));
-
-                       stats->rx_packets       += rxpackets;
-                       stats->rx_bytes         += rxbytes;
-                       stats->multicast        += rxmulticast;
-                       stats->tx_packets       += txpackets;
-                       stats->tx_bytes         += txbytes;
-                       /* rx_errors & tx_dropped are u32 */
-                       rx_errors       += p->rx_errors;
-                       tx_dropped      += p->tx_dropped;
-               }
-               stats->rx_errors  = rx_errors;
-               stats->tx_dropped = tx_dropped;
+       for_each_possible_cpu(i) {
+               u64 rxpackets, rxbytes, rxmulticast, txpackets, txbytes;
+               unsigned int start;
+
+               p = per_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats, i);
+               do {
+                       start = u64_stats_fetch_begin_irq(&p->syncp);
+                       rxpackets       = p->rx_packets;
+                       rxbytes         = p->rx_bytes;
+                       rxmulticast     = p->rx_multicast;
+                       txpackets       = p->tx_packets;
+                       txbytes         = p->tx_bytes;
+               } while (u64_stats_fetch_retry_irq(&p->syncp, start));
+
+               stats->rx_packets       += rxpackets;
+               stats->rx_bytes         += rxbytes;
+               stats->multicast        += rxmulticast;
+               stats->tx_packets       += txpackets;
+               stats->tx_bytes         += txbytes;
+               /* rx_errors & tx_dropped are u32 */
+               rx_errors       += p->rx_errors;
+               tx_dropped      += p->tx_dropped;
        }
+       stats->rx_errors  = rx_errors;
+       stats->tx_dropped = tx_dropped;
+
        return stats;
 }
 
index 786ee2f83d5fea1dbfd6bb2660544d7f88e1eff2..01a1082e02b3157b3abc84e6b23844ed3a26f2f2 100644 (file)
@@ -1669,7 +1669,7 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
                goto out;
        }
 
-       if (sk->sk_no_check == 1)
+       if (sk->sk_no_check_tx)
                ddp->deh_sum = 0;
        else
                ddp->deh_sum = atalk_checksum(skb, len + sizeof(*ddp));
index 1281049c135f269f9ab0553eb51a681cd97b2079..d8e5d0c2ebbc2acb9a084581f5fa9f99fbd904da 100644 (file)
@@ -263,17 +263,11 @@ static int svc_connect(struct socket *sock, struct sockaddr *sockaddr,
                        goto out;
                }
        }
-/*
- * Not supported yet
- *
- * #ifndef CONFIG_SINGLE_SIGITF
- */
+
        vcc->qos.txtp.max_pcr = SELECT_TOP_PCR(vcc->qos.txtp);
        vcc->qos.txtp.pcr = 0;
        vcc->qos.txtp.min_pcr = 0;
-/*
- * #endif
- */
+
        error = vcc_connect(sock, vcc->itf, vcc->vpi, vcc->vci);
        if (!error)
                sock->state = SS_CONNECTED;
index b758881be108c84bfa059cf61f15d44a9008c1c8..a12e25efaf6ff055094f843c7c5536ce861f593a 100644 (file)
@@ -245,6 +245,7 @@ static int batadv_algorithms_open(struct inode *inode, struct file *file)
 static int batadv_originators_open(struct inode *inode, struct file *file)
 {
        struct net_device *net_dev = (struct net_device *)inode->i_private;
+
        return single_open(file, batadv_orig_seq_print_text, net_dev);
 }
 
@@ -258,18 +259,21 @@ static int batadv_originators_hardif_open(struct inode *inode,
                                          struct file *file)
 {
        struct net_device *net_dev = (struct net_device *)inode->i_private;
+
        return single_open(file, batadv_orig_hardif_seq_print_text, net_dev);
 }
 
 static int batadv_gateways_open(struct inode *inode, struct file *file)
 {
        struct net_device *net_dev = (struct net_device *)inode->i_private;
+
        return single_open(file, batadv_gw_client_seq_print_text, net_dev);
 }
 
 static int batadv_transtable_global_open(struct inode *inode, struct file *file)
 {
        struct net_device *net_dev = (struct net_device *)inode->i_private;
+
        return single_open(file, batadv_tt_global_seq_print_text, net_dev);
 }
 
@@ -277,6 +281,7 @@ static int batadv_transtable_global_open(struct inode *inode, struct file *file)
 static int batadv_bla_claim_table_open(struct inode *inode, struct file *file)
 {
        struct net_device *net_dev = (struct net_device *)inode->i_private;
+
        return single_open(file, batadv_bla_claim_table_seq_print_text,
                           net_dev);
 }
@@ -285,6 +290,7 @@ static int batadv_bla_backbone_table_open(struct inode *inode,
                                          struct file *file)
 {
        struct net_device *net_dev = (struct net_device *)inode->i_private;
+
        return single_open(file, batadv_bla_backbone_table_seq_print_text,
                           net_dev);
 }
@@ -300,6 +306,7 @@ static int batadv_bla_backbone_table_open(struct inode *inode,
 static int batadv_dat_cache_open(struct inode *inode, struct file *file)
 {
        struct net_device *net_dev = (struct net_device *)inode->i_private;
+
        return single_open(file, batadv_dat_cache_seq_print_text, net_dev);
 }
 #endif
@@ -307,6 +314,7 @@ static int batadv_dat_cache_open(struct inode *inode, struct file *file)
 static int batadv_transtable_local_open(struct inode *inode, struct file *file)
 {
        struct net_device *net_dev = (struct net_device *)inode->i_private;
+
        return single_open(file, batadv_tt_local_seq_print_text, net_dev);
 }
 
@@ -319,6 +327,7 @@ struct batadv_debuginfo {
 static int batadv_nc_nodes_open(struct inode *inode, struct file *file)
 {
        struct net_device *net_dev = (struct net_device *)inode->i_private;
+
        return single_open(file, batadv_nc_nodes_seq_print_text, net_dev);
 }
 #endif
@@ -333,7 +342,7 @@ struct batadv_debuginfo batadv_debuginfo_##_name = {        \
                  .llseek = seq_lseek,                  \
                  .release = single_release,            \
                }                                       \
-};
+}
 
 /* the following attributes are general and therefore they will be directly
  * placed in the BATADV_DEBUGFS_SUBDIR subdirectory of debugfs
@@ -395,7 +404,7 @@ struct batadv_debuginfo batadv_hardif_debuginfo_##_name = { \
                .llseek = seq_lseek,                            \
                .release = single_release,                      \
        },                                                      \
-};
+}
 static BATADV_HARDIF_DEBUGINFO(originators, S_IRUGO,
                               batadv_originators_hardif_open);
 
index aa5d4946d0d784d32fdcdce217c4f2bb482502f0..f2c066b2171640c4092a60bc6fc8ef603baa5c09 100644 (file)
@@ -594,7 +594,7 @@ static bool batadv_dat_send_data(struct batadv_priv *bat_priv,
                if (!neigh_node)
                        goto free_orig;
 
-               tmp_skb = pskb_copy(skb, GFP_ATOMIC);
+               tmp_skb = pskb_copy_for_clone(skb, GFP_ATOMIC);
                if (!batadv_send_skb_prepare_unicast_4addr(bat_priv, tmp_skb,
                                                           cand[i].orig_node,
                                                           packet_subtype)) {
@@ -662,6 +662,7 @@ static void batadv_dat_tvlv_container_update(struct batadv_priv *bat_priv)
 void batadv_dat_status_update(struct net_device *net_dev)
 {
        struct batadv_priv *bat_priv = netdev_priv(net_dev);
+
        batadv_dat_tvlv_container_update(bat_priv);
 }
 
index 770dc890ceefdb712f254b378c825cbeab255742..118b990bae25d7ecc7221330546b59a5391cd21e 100644 (file)
@@ -24,7 +24,7 @@
 #define BATADV_DRIVER_DEVICE "batman-adv"
 
 #ifndef BATADV_SOURCE_VERSION
-#define BATADV_SOURCE_VERSION "2014.2.0"
+#define BATADV_SOURCE_VERSION "2014.3.0"
 #endif
 
 /* B.A.T.M.A.N. parameters */
index a9546fe541ebb0ff8905fd3fe82d48e48302ce9e..8d04d174669ed29c467436d33b099ad5d0ca13c4 100644 (file)
@@ -86,6 +86,7 @@ static void batadv_nc_tvlv_container_update(struct batadv_priv *bat_priv)
 void batadv_nc_status_update(struct net_device *net_dev)
 {
        struct batadv_priv *bat_priv = netdev_priv(net_dev);
+
        batadv_nc_tvlv_container_update(bat_priv);
 }
 
@@ -1343,7 +1344,7 @@ static void batadv_nc_skb_store_before_coding(struct batadv_priv *bat_priv,
        struct ethhdr *ethhdr;
 
        /* Copy skb header to change the mac header */
-       skb = pskb_copy(skb, GFP_ATOMIC);
+       skb = pskb_copy_for_clone(skb, GFP_ATOMIC);
        if (!skb)
                return;
 
index 744a59b85e15ded75f61da8a9fa5a8a87cdb7b8d..e7ee65dc20bf4f25a1a8d0134c66b0bfaef25bd3 100644 (file)
@@ -884,7 +884,7 @@ static void batadv_softif_init_early(struct net_device *dev)
        /* generate random address */
        eth_hw_addr_random(dev);
 
-       SET_ETHTOOL_OPS(dev, &batadv_ethtool_ops);
+       dev->ethtool_ops = &batadv_ethtool_ops;
 
        memset(priv, 0, sizeof(*priv));
 }
index 1ebb0d9e2ea547d1c263a6b09d30d81214e4ba33..fc47baa888c54896c6ccde6352202736d3c9ba6b 100644 (file)
 static struct net_device *batadv_kobj_to_netdev(struct kobject *obj)
 {
        struct device *dev = container_of(obj->parent, struct device, kobj);
+
        return to_net_dev(dev);
 }
 
 static struct batadv_priv *batadv_kobj_to_batpriv(struct kobject *obj)
 {
        struct net_device *net_dev = batadv_kobj_to_netdev(obj);
+
        return netdev_priv(net_dev);
 }
 
@@ -106,7 +108,7 @@ struct batadv_attribute batadv_attr_vlan_##_name = {        \
                 .mode = _mode },                       \
        .show   = _show,                                \
        .store  = _store,                               \
-};
+}
 
 /* Use this, if you have customized show and store functions */
 #define BATADV_ATTR(_name, _mode, _show, _store)       \
@@ -115,7 +117,7 @@ struct batadv_attribute batadv_attr_##_name = {             \
                 .mode = _mode },                       \
        .show   = _show,                                \
        .store  = _store,                               \
-};
+}
 
 #define BATADV_ATTR_SIF_STORE_BOOL(_name, _post_func)                  \
 ssize_t batadv_store_##_name(struct kobject *kobj,                     \
@@ -124,6 +126,7 @@ ssize_t batadv_store_##_name(struct kobject *kobj,                  \
 {                                                                      \
        struct net_device *net_dev = batadv_kobj_to_netdev(kobj);       \
        struct batadv_priv *bat_priv = netdev_priv(net_dev);            \
+                                                                       \
        return __batadv_store_bool_attr(buff, count, _post_func, attr,  \
                                        &bat_priv->_name, net_dev);     \
 }
@@ -133,6 +136,7 @@ ssize_t batadv_show_##_name(struct kobject *kobj,                   \
                            struct attribute *attr, char *buff)         \
 {                                                                      \
        struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);    \
+                                                                       \
        return sprintf(buff, "%s\n",                                    \
                       atomic_read(&bat_priv->_name) == 0 ?             \
                       "disabled" : "enabled");                         \
@@ -155,6 +159,7 @@ ssize_t batadv_store_##_name(struct kobject *kobj,                  \
 {                                                                      \
        struct net_device *net_dev = batadv_kobj_to_netdev(kobj);       \
        struct batadv_priv *bat_priv = netdev_priv(net_dev);            \
+                                                                       \
        return __batadv_store_uint_attr(buff, count, _min, _max,        \
                                        _post_func, attr,               \
                                        &bat_priv->_name, net_dev);     \
@@ -165,6 +170,7 @@ ssize_t batadv_show_##_name(struct kobject *kobj,                   \
                            struct attribute *attr, char *buff)         \
 {                                                                      \
        struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);    \
+                                                                       \
        return sprintf(buff, "%i\n", atomic_read(&bat_priv->_name));    \
 }                                                                      \
 
@@ -188,6 +194,7 @@ ssize_t batadv_store_vlan_##_name(struct kobject *kobj,                     \
        size_t res = __batadv_store_bool_attr(buff, count, _post_func,  \
                                              attr, &vlan->_name,       \
                                              bat_priv->soft_iface);    \
+                                                                       \
        batadv_softif_vlan_free_ref(vlan);                              \
        return res;                                                     \
 }
@@ -202,6 +209,7 @@ ssize_t batadv_show_vlan_##_name(struct kobject *kobj,                      \
        size_t res = sprintf(buff, "%s\n",                              \
                             atomic_read(&vlan->_name) == 0 ?           \
                             "disabled" : "enabled");                   \
+                                                                       \
        batadv_softif_vlan_free_ref(vlan);                              \
        return res;                                                     \
 }
@@ -324,12 +332,14 @@ static ssize_t batadv_show_bat_algo(struct kobject *kobj,
                                    struct attribute *attr, char *buff)
 {
        struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);
+
        return sprintf(buff, "%s\n", bat_priv->bat_algo_ops->name);
 }
 
 static void batadv_post_gw_reselect(struct net_device *net_dev)
 {
        struct batadv_priv *bat_priv = netdev_priv(net_dev);
+
        batadv_gw_reselect(bat_priv);
 }
 
index 73492b91105ac0aba10aec738415128d0582bfc5..8796ffa08b43b4f57ae1485f4a5867c0f532807c 100644 (file)
@@ -420,12 +420,18 @@ static int conn_send(struct l2cap_conn *conn,
        return 0;
 }
 
-static void get_dest_bdaddr(struct in6_addr *ip6_daddr,
-                           bdaddr_t *addr, u8 *addr_type)
+static u8 get_addr_type_from_eui64(u8 byte)
 {
-       u8 *eui64;
+       /* Is universal(0) or local(1) bit,  */
+       if (byte & 0x02)
+               return ADDR_LE_DEV_RANDOM;
 
-       eui64 = ip6_daddr->s6_addr + 8;
+       return ADDR_LE_DEV_PUBLIC;
+}
+
+static void copy_to_bdaddr(struct in6_addr *ip6_daddr, bdaddr_t *addr)
+{
+       u8 *eui64 = ip6_daddr->s6_addr + 8;
 
        addr->b[0] = eui64[7];
        addr->b[1] = eui64[6];
@@ -433,16 +439,19 @@ static void get_dest_bdaddr(struct in6_addr *ip6_daddr,
        addr->b[3] = eui64[2];
        addr->b[4] = eui64[1];
        addr->b[5] = eui64[0];
+}
 
-       addr->b[5] ^= 2;
+static void convert_dest_bdaddr(struct in6_addr *ip6_daddr,
+                               bdaddr_t *addr, u8 *addr_type)
+{
+       copy_to_bdaddr(ip6_daddr, addr);
 
-       /* Set universal/local bit to 0 */
-       if (addr->b[5] & 1) {
-               addr->b[5] &= ~1;
-               *addr_type = ADDR_LE_DEV_PUBLIC;
-       } else {
-               *addr_type = ADDR_LE_DEV_RANDOM;
-       }
+       /* We need to toggle the U/L bit that we got from IPv6 address
+        * so that we get the proper address and type of the BD address.
+        */
+       addr->b[5] ^= 0x02;
+
+       *addr_type = get_addr_type_from_eui64(addr->b[5]);
 }
 
 static int header_create(struct sk_buff *skb, struct net_device *netdev,
@@ -473,9 +482,11 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev,
                /* Get destination BT device from skb.
                 * If there is no such peer then discard the packet.
                 */
-               get_dest_bdaddr(&hdr->daddr, &addr, &addr_type);
+               convert_dest_bdaddr(&hdr->daddr, &addr, &addr_type);
 
-               BT_DBG("dest addr %pMR type %d", &addr, addr_type);
+               BT_DBG("dest addr %pMR type %s IP %pI6c", &addr,
+                      addr_type == ADDR_LE_DEV_PUBLIC ? "PUBLIC" : "RANDOM",
+                      &hdr->daddr);
 
                read_lock_irqsave(&devices_lock, flags);
                peer = peer_lookup_ba(dev, &addr, addr_type);
@@ -556,7 +567,7 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
        } else {
                unsigned long flags;
 
-               get_dest_bdaddr(&lowpan_cb(skb)->addr, &addr, &addr_type);
+               convert_dest_bdaddr(&lowpan_cb(skb)->addr, &addr, &addr_type);
                eui64_addr = lowpan_cb(skb)->addr.s6_addr + 8;
                dev = lowpan_dev(netdev);
 
@@ -564,8 +575,10 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
                peer = peer_lookup_ba(dev, &addr, addr_type);
                read_unlock_irqrestore(&devices_lock, flags);
 
-               BT_DBG("xmit from %s to %pMR (%pI6c) peer %p", netdev->name,
-                      &addr, &lowpan_cb(skb)->addr, peer);
+               BT_DBG("xmit %s to %pMR type %s IP %pI6c peer %p",
+                      netdev->name, &addr,
+                      addr_type == ADDR_LE_DEV_PUBLIC ? "PUBLIC" : "RANDOM",
+                      &lowpan_cb(skb)->addr, peer);
 
                if (peer && peer->conn)
                        err = send_pkt(peer->conn, netdev->dev_addr,
@@ -620,13 +633,13 @@ static void set_addr(u8 *eui, u8 *addr, u8 addr_type)
        eui[6] = addr[1];
        eui[7] = addr[0];
 
-       eui[0] ^= 2;
-
-       /* Universal/local bit set, RFC 4291 */
+       /* Universal/local bit set, BT 6lowpan draft ch. 3.2.1 */
        if (addr_type == ADDR_LE_DEV_PUBLIC)
-               eui[0] |= 1;
+               eui[0] &= ~0x02;
        else
-               eui[0] &= ~1;
+               eui[0] |= 0x02;
+
+       BT_DBG("type %d addr %*phC", addr_type, 8, eui);
 }
 
 static void set_dev_addr(struct net_device *netdev, bdaddr_t *addr,
@@ -634,7 +647,6 @@ static void set_dev_addr(struct net_device *netdev, bdaddr_t *addr,
 {
        netdev->addr_assign_type = NET_ADDR_PERM;
        set_addr(netdev->dev_addr, addr->b, addr_type);
-       netdev->dev_addr[0] ^= 2;
 }
 
 static void ifup(struct net_device *netdev)
@@ -684,13 +696,6 @@ static int add_peer_conn(struct l2cap_conn *conn, struct lowpan_dev *dev)
 
        memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8,
               EUI64_ADDR_LEN);
-       peer->eui64_addr[0] ^= 2; /* second bit-flip (Universe/Local)
-                                  * is done according RFC2464
-                                  */
-
-       raw_dump_inline(__func__, "peer IPv6 address",
-                       (unsigned char *)&peer->peer_addr, 16);
-       raw_dump_inline(__func__, "peer EUI64 address", peer->eui64_addr, 8);
 
        write_lock_irqsave(&devices_lock, flags);
        INIT_LIST_HEAD(&peer->list);
index 521fd4f3985e11aef05880be5307b632a46afc53..8671bc79a35bebe23a2f6b269c582bfcfc4add37 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
 
 #include "smp.h"
 #include "a2mp.h"
@@ -367,9 +368,23 @@ static void le_conn_timeout(struct work_struct *work)
 {
        struct hci_conn *conn = container_of(work, struct hci_conn,
                                             le_conn_timeout.work);
+       struct hci_dev *hdev = conn->hdev;
 
        BT_DBG("");
 
+       /* We could end up here due to having done directed advertising,
+        * so clean up the state if necessary. This should however only
+        * happen with broken hardware or if low duty cycle was used
+        * (which doesn't have a timeout of its own).
+        */
+       if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
+               u8 enable = 0x00;
+               hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable),
+                            &enable);
+               hci_le_conn_failed(conn, HCI_ERROR_ADVERTISING_TIMEOUT);
+               return;
+       }
+
        hci_le_create_connection_cancel(conn);
 }
 
@@ -393,6 +408,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
        conn->io_capability = hdev->io_capability;
        conn->remote_auth = 0xff;
        conn->key_type = 0xff;
+       conn->tx_power = HCI_TX_POWER_INVALID;
+       conn->max_tx_power = HCI_TX_POWER_INVALID;
 
        set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
        conn->disc_timeout = HCI_DISCONN_TIMEOUT;
@@ -401,6 +418,10 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
        case ACL_LINK:
                conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK;
                break;
+       case LE_LINK:
+               /* conn->src should reflect the local identity address */
+               hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
+               break;
        case SCO_LINK:
                if (lmp_esco_capable(hdev))
                        conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) |
@@ -545,6 +566,11 @@ void hci_le_conn_failed(struct hci_conn *conn, u8 status)
         * favor of connection establishment, we should restart it.
         */
        hci_update_background_scan(hdev);
+
+       /* Re-enable advertising in case this was a failed connection
+        * attempt as a peripheral.
+        */
+       mgmt_reenable_advertising(hdev);
 }
 
 static void create_le_conn_complete(struct hci_dev *hdev, u8 status)
@@ -605,6 +631,45 @@ static void hci_req_add_le_create_conn(struct hci_request *req,
        conn->state = BT_CONNECT;
 }
 
+static void hci_req_directed_advertising(struct hci_request *req,
+                                        struct hci_conn *conn)
+{
+       struct hci_dev *hdev = req->hdev;
+       struct hci_cp_le_set_adv_param cp;
+       u8 own_addr_type;
+       u8 enable;
+
+       enable = 0x00;
+       hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
+
+       /* Clear the HCI_ADVERTISING bit temporarily so that the
+        * hci_update_random_address knows that it's safe to go ahead
+        * and write a new random address. The flag will be set back on
+        * as soon as the SET_ADV_ENABLE HCI command completes.
+        */
+       clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+
+       /* Set require_privacy to false so that the remote device has a
+        * chance of identifying us.
+        */
+       if (hci_update_random_address(req, false, &own_addr_type) < 0)
+               return;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.type = LE_ADV_DIRECT_IND;
+       cp.own_address_type = own_addr_type;
+       cp.direct_addr_type = conn->dst_type;
+       bacpy(&cp.direct_addr, &conn->dst);
+       cp.channel_map = hdev->le_adv_channel_map;
+
+       hci_req_add(req, HCI_OP_LE_SET_ADV_PARAM, sizeof(cp), &cp);
+
+       enable = 0x01;
+       hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
+
+       conn->state = BT_CONNECT;
+}
+
 struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
                                u8 dst_type, u8 sec_level, u8 auth_type)
 {
@@ -614,9 +679,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
        struct hci_request req;
        int err;
 
-       if (test_bit(HCI_ADVERTISING, &hdev->flags))
-               return ERR_PTR(-ENOTSUPP);
-
        /* Some devices send ATT messages as soon as the physical link is
         * established. To be able to handle these ATT messages, the user-
         * space first establishes the connection and then starts the pairing
@@ -664,13 +726,20 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
                return ERR_PTR(-ENOMEM);
 
        conn->dst_type = dst_type;
-
-       conn->out = true;
-       conn->link_mode |= HCI_LM_MASTER;
        conn->sec_level = BT_SECURITY_LOW;
        conn->pending_sec_level = sec_level;
        conn->auth_type = auth_type;
 
+       hci_req_init(&req, hdev);
+
+       if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
+               hci_req_directed_advertising(&req, conn);
+               goto create_conn;
+       }
+
+       conn->out = true;
+       conn->link_mode |= HCI_LM_MASTER;
+
        params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
        if (params) {
                conn->le_conn_min_interval = params->conn_min_interval;
@@ -680,8 +749,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
                conn->le_conn_max_interval = hdev->le_conn_max_interval;
        }
 
-       hci_req_init(&req, hdev);
-
        /* If controller is scanning, we stop it since some controllers are
         * not able to scan and connect at the same time. Also set the
         * HCI_LE_SCAN_INTERRUPTED flag so that the command complete
@@ -695,6 +762,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
 
        hci_req_add_le_create_conn(&req, conn);
 
+create_conn:
        err = hci_req_run(&req, create_le_conn_complete);
        if (err) {
                hci_conn_del(conn);
index 1c6ffaa8902f5e9fe32a86f2a19cf9073d6f0dae..0a43cce9a914b84613c7ee2d6fc30fdfdb2a0bc5 100644 (file)
@@ -34,6 +34,7 @@
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
 
 #include "smp.h"
 
@@ -579,6 +580,62 @@ static int sniff_max_interval_get(void *data, u64 *val)
 DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get,
                        sniff_max_interval_set, "%llu\n");
 
+static int conn_info_min_age_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val == 0 || val > hdev->conn_info_max_age)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->conn_info_min_age = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int conn_info_min_age_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->conn_info_min_age;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(conn_info_min_age_fops, conn_info_min_age_get,
+                       conn_info_min_age_set, "%llu\n");
+
+static int conn_info_max_age_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val == 0 || val < hdev->conn_info_min_age)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->conn_info_max_age = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int conn_info_max_age_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->conn_info_max_age;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(conn_info_max_age_fops, conn_info_max_age_get,
+                       conn_info_max_age_set, "%llu\n");
+
 static int identity_show(struct seq_file *f, void *p)
 {
        struct hci_dev *hdev = f->private;
@@ -955,14 +1012,9 @@ static ssize_t le_auto_conn_write(struct file *file, const char __user *data,
        if (count < 3)
                return -EINVAL;
 
-       buf = kzalloc(count, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
-       if (copy_from_user(buf, data, count)) {
-               err = -EFAULT;
-               goto done;
-       }
+       buf = memdup_user(data, count);
+       if (IS_ERR(buf))
+               return PTR_ERR(buf);
 
        if (memcmp(buf, "add", 3) == 0) {
                n = sscanf(&buf[4], "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu %hhu",
@@ -1759,6 +1811,11 @@ static int __hci_init(struct hci_dev *hdev)
                            &blacklist_fops);
        debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
 
+       debugfs_create_file("conn_info_min_age", 0644, hdev->debugfs, hdev,
+                           &conn_info_min_age_fops);
+       debugfs_create_file("conn_info_max_age", 0644, hdev->debugfs, hdev,
+                           &conn_info_max_age_fops);
+
        if (lmp_bredr_capable(hdev)) {
                debugfs_create_file("inquiry_cache", 0444, hdev->debugfs,
                                    hdev, &inquiry_cache_fops);
@@ -1828,6 +1885,9 @@ static int __hci_init(struct hci_dev *hdev)
                                    &lowpan_debugfs_fops);
                debugfs_create_file("le_auto_conn", 0644, hdev->debugfs, hdev,
                                    &le_auto_conn_fops);
+               debugfs_create_u16("discov_interleaved_timeout", 0644,
+                                  hdev->debugfs,
+                                  &hdev->discov_interleaved_timeout);
        }
 
        return 0;
@@ -2033,12 +2093,11 @@ bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
 
        hci_remove_remote_oob_data(hdev, &data->bdaddr);
 
-       if (ssp)
-               *ssp = data->ssp_mode;
+       *ssp = data->ssp_mode;
 
        ie = hci_inquiry_cache_lookup(hdev, &data->bdaddr);
        if (ie) {
-               if (ie->data.ssp_mode && ssp)
+               if (ie->data.ssp_mode)
                        *ssp = true;
 
                if (ie->name_state == NAME_NEEDED &&
@@ -3791,6 +3850,9 @@ struct hci_dev *hci_alloc_dev(void)
        hdev->le_conn_max_interval = 0x0038;
 
        hdev->rpa_timeout = HCI_DEFAULT_RPA_TIMEOUT;
+       hdev->discov_interleaved_timeout = DISCOV_INTERLEAVED_TIMEOUT;
+       hdev->conn_info_min_age = DEFAULT_CONN_INFO_MIN_AGE;
+       hdev->conn_info_max_age = DEFAULT_CONN_INFO_MAX_AGE;
 
        mutex_init(&hdev->lock);
        mutex_init(&hdev->req_lock);
index 682f33a383660fac1ce141aa6bd6c54026be973e..21e5913d12e03f4c8410a9464cd5794c9e5a0c70 100644 (file)
@@ -991,10 +991,25 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb)
        if (!sent)
                return;
 
+       if (status)
+               return;
+
        hci_dev_lock(hdev);
 
-       if (!status)
-               mgmt_advertising(hdev, *sent);
+       /* If we're doing connection initation as peripheral. Set a
+        * timeout in case something goes wrong.
+        */
+       if (*sent) {
+               struct hci_conn *conn;
+
+               conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
+               if (conn)
+                       queue_delayed_work(hdev->workqueue,
+                                          &conn->le_conn_timeout,
+                                          HCI_LE_CONN_TIMEOUT);
+       }
+
+       mgmt_advertising(hdev, *sent);
 
        hci_dev_unlock(hdev);
 }
@@ -1018,6 +1033,33 @@ static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb)
        hci_dev_unlock(hdev);
 }
 
+static bool has_pending_adv_report(struct hci_dev *hdev)
+{
+       struct discovery_state *d = &hdev->discovery;
+
+       return bacmp(&d->last_adv_addr, BDADDR_ANY);
+}
+
+static void clear_pending_adv_report(struct hci_dev *hdev)
+{
+       struct discovery_state *d = &hdev->discovery;
+
+       bacpy(&d->last_adv_addr, BDADDR_ANY);
+       d->last_adv_data_len = 0;
+}
+
+static void store_pending_adv_report(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                    u8 bdaddr_type, s8 rssi, u8 *data, u8 len)
+{
+       struct discovery_state *d = &hdev->discovery;
+
+       bacpy(&d->last_adv_addr, bdaddr);
+       d->last_adv_addr_type = bdaddr_type;
+       d->last_adv_rssi = rssi;
+       memcpy(d->last_adv_data, data, len);
+       d->last_adv_data_len = len;
+}
+
 static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
                                      struct sk_buff *skb)
 {
@@ -1036,9 +1078,25 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
        switch (cp->enable) {
        case LE_SCAN_ENABLE:
                set_bit(HCI_LE_SCAN, &hdev->dev_flags);
+               if (hdev->le_scan_type == LE_SCAN_ACTIVE)
+                       clear_pending_adv_report(hdev);
                break;
 
        case LE_SCAN_DISABLE:
+               /* We do this here instead of when setting DISCOVERY_STOPPED
+                * since the latter would potentially require waiting for
+                * inquiry to stop too.
+                */
+               if (has_pending_adv_report(hdev)) {
+                       struct discovery_state *d = &hdev->discovery;
+
+                       mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK,
+                                         d->last_adv_addr_type, NULL,
+                                         d->last_adv_rssi, 0, 1,
+                                         d->last_adv_data,
+                                         d->last_adv_data_len, NULL, 0);
+               }
+
                /* Cancel this timer so that we don't try to disable scanning
                 * when it's already disabled.
                 */
@@ -1187,6 +1245,59 @@ static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev,
        amp_write_rem_assoc_continue(hdev, rp->phy_handle);
 }
 
+static void hci_cc_read_rssi(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_rp_read_rssi *rp = (void *) skb->data;
+       struct hci_conn *conn;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+       if (rp->status)
+               return;
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
+       if (conn)
+               conn->rssi = rp->rssi;
+
+       hci_dev_unlock(hdev);
+}
+
+static void hci_cc_read_tx_power(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_cp_read_tx_power *sent;
+       struct hci_rp_read_tx_power *rp = (void *) skb->data;
+       struct hci_conn *conn;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+       if (rp->status)
+               return;
+
+       sent = hci_sent_cmd_data(hdev, HCI_OP_READ_TX_POWER);
+       if (!sent)
+               return;
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
+       if (!conn)
+               goto unlock;
+
+       switch (sent->type) {
+       case 0x00:
+               conn->tx_power = rp->tx_power;
+               break;
+       case 0x01:
+               conn->max_tx_power = rp->tx_power;
+               break;
+       }
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
 static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
 {
        BT_DBG("%s status 0x%2.2x", hdev->name, status);
@@ -1342,6 +1453,7 @@ static int hci_outgoing_auth_needed(struct hci_dev *hdev,
         * is requested.
         */
        if (!hci_conn_ssp_enabled(conn) && !(conn->auth_type & 0x01) &&
+           conn->pending_sec_level != BT_SECURITY_FIPS &&
            conn->pending_sec_level != BT_SECURITY_HIGH &&
            conn->pending_sec_level != BT_SECURITY_MEDIUM)
                return 0;
@@ -1827,7 +1939,7 @@ static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
                name_known = hci_inquiry_cache_update(hdev, &data, false, &ssp);
                mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
                                  info->dev_class, 0, !name_known, ssp, NULL,
-                                 0);
+                                 0, NULL, 0);
        }
 
        hci_dev_unlock(hdev);
@@ -2579,6 +2691,14 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cc_write_remote_amp_assoc(hdev, skb);
                break;
 
+       case HCI_OP_READ_RSSI:
+               hci_cc_read_rssi(hdev, skb);
+               break;
+
+       case HCI_OP_READ_TX_POWER:
+               hci_cc_read_tx_power(hdev, skb);
+               break;
+
        default:
                BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
                break;
@@ -2957,7 +3077,8 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
                }
 
                if (key->type == HCI_LK_COMBINATION && key->pin_len < 16 &&
-                   conn->pending_sec_level == BT_SECURITY_HIGH) {
+                   (conn->pending_sec_level == BT_SECURITY_HIGH ||
+                    conn->pending_sec_level == BT_SECURITY_FIPS)) {
                        BT_DBG("%s ignoring key unauthenticated for high security",
                               hdev->name);
                        goto not_found;
@@ -3102,7 +3223,7 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev,
                                                              false, &ssp);
                        mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
                                          info->dev_class, info->rssi,
-                                         !name_known, ssp, NULL, 0);
+                                         !name_known, ssp, NULL, 0, NULL, 0);
                }
        } else {
                struct inquiry_info_with_rssi *info = (void *) (skb->data + 1);
@@ -3120,7 +3241,7 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev,
                                                              false, &ssp);
                        mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
                                          info->dev_class, info->rssi,
-                                         !name_known, ssp, NULL, 0);
+                                         !name_known, ssp, NULL, 0, NULL, 0);
                }
        }
 
@@ -3309,7 +3430,7 @@ static void hci_extended_inquiry_result_evt(struct hci_dev *hdev,
                eir_len = eir_get_length(info->data, sizeof(info->data));
                mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
                                  info->dev_class, info->rssi, !name_known,
-                                 ssp, info->data, eir_len);
+                                 ssp, info->data, eir_len, NULL, 0);
        }
 
        hci_dev_unlock(hdev);
@@ -3367,24 +3488,20 @@ unlock:
 
 static u8 hci_get_auth_req(struct hci_conn *conn)
 {
-       /* If remote requests dedicated bonding follow that lead */
-       if (conn->remote_auth == HCI_AT_DEDICATED_BONDING ||
-           conn->remote_auth == HCI_AT_DEDICATED_BONDING_MITM) {
-               /* If both remote and local IO capabilities allow MITM
-                * protection then require it, otherwise don't */
-               if (conn->remote_cap == HCI_IO_NO_INPUT_OUTPUT ||
-                   conn->io_capability == HCI_IO_NO_INPUT_OUTPUT)
-                       return HCI_AT_DEDICATED_BONDING;
-               else
-                       return HCI_AT_DEDICATED_BONDING_MITM;
-       }
-
        /* If remote requests no-bonding follow that lead */
        if (conn->remote_auth == HCI_AT_NO_BONDING ||
            conn->remote_auth == HCI_AT_NO_BONDING_MITM)
                return conn->remote_auth | (conn->auth_type & 0x01);
 
-       return conn->auth_type;
+       /* If both remote and local have enough IO capabilities, require
+        * MITM protection
+        */
+       if (conn->remote_cap != HCI_IO_NO_INPUT_OUTPUT &&
+           conn->io_capability != HCI_IO_NO_INPUT_OUTPUT)
+               return conn->remote_auth | 0x01;
+
+       /* No MITM protection possible so ignore remote requirement */
+       return (conn->remote_auth & ~0x01) | (conn->auth_type & 0x01);
 }
 
 static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
@@ -3414,8 +3531,21 @@ static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
                 * to DisplayYesNo as it is not supported by BT spec. */
                cp.capability = (conn->io_capability == 0x04) ?
                                HCI_IO_DISPLAY_YESNO : conn->io_capability;
-               conn->auth_type = hci_get_auth_req(conn);
-               cp.authentication = conn->auth_type;
+
+               /* If we are initiators, there is no remote information yet */
+               if (conn->remote_auth == 0xff) {
+                       cp.authentication = conn->auth_type;
+
+                       /* Request MITM protection if our IO caps allow it
+                        * except for the no-bonding case
+                        */
+                       if (conn->io_capability != HCI_IO_NO_INPUT_OUTPUT &&
+                           cp.authentication != HCI_AT_NO_BONDING)
+                               cp.authentication |= 0x01;
+               } else {
+                       conn->auth_type = hci_get_auth_req(conn);
+                       cp.authentication = conn->auth_type;
+               }
 
                if (hci_find_remote_oob_data(hdev, &conn->dst) &&
                    (conn->out || test_bit(HCI_CONN_REMOTE_OOB, &conn->flags)))
@@ -3483,12 +3613,9 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev,
        rem_mitm = (conn->remote_auth & 0x01);
 
        /* If we require MITM but the remote device can't provide that
-        * (it has NoInputNoOutput) then reject the confirmation
-        * request. The only exception is when we're dedicated bonding
-        * initiators (connect_cfm_cb set) since then we always have the MITM
-        * bit set. */
-       if (!conn->connect_cfm_cb && loc_mitm &&
-           conn->remote_cap == HCI_IO_NO_INPUT_OUTPUT) {
+        * (it has NoInputNoOutput) then reject the confirmation request
+        */
+       if (loc_mitm && conn->remote_cap == HCI_IO_NO_INPUT_OUTPUT) {
                BT_DBG("Rejecting request: remote device can't provide MITM");
                hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_NEG_REPLY,
                             sizeof(ev->bdaddr), &ev->bdaddr);
@@ -3846,17 +3973,6 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 
                conn->dst_type = ev->bdaddr_type;
 
-               /* The advertising parameters for own address type
-                * define which source address and source address
-                * type this connections has.
-                */
-               if (bacmp(&conn->src, BDADDR_ANY)) {
-                       conn->src_type = ADDR_LE_DEV_PUBLIC;
-               } else {
-                       bacpy(&conn->src, &hdev->static_addr);
-                       conn->src_type = ADDR_LE_DEV_RANDOM;
-               }
-
                if (ev->role == LE_CONN_ROLE_MASTER) {
                        conn->out = true;
                        conn->link_mode |= HCI_LM_MASTER;
@@ -3881,27 +3997,24 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                                                          &conn->init_addr,
                                                          &conn->init_addr_type);
                        }
-               } else {
-                       /* Set the responder (our side) address type based on
-                        * the advertising address type.
-                        */
-                       conn->resp_addr_type = hdev->adv_addr_type;
-                       if (hdev->adv_addr_type == ADDR_LE_DEV_RANDOM)
-                               bacpy(&conn->resp_addr, &hdev->random_addr);
-                       else
-                               bacpy(&conn->resp_addr, &hdev->bdaddr);
-
-                       conn->init_addr_type = ev->bdaddr_type;
-                       bacpy(&conn->init_addr, &ev->bdaddr);
                }
        } else {
                cancel_delayed_work(&conn->le_conn_timeout);
        }
 
-       /* Ensure that the hci_conn contains the identity address type
-        * regardless of which address the connection was made with.
-        */
-       hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
+       if (!conn->out) {
+               /* Set the responder (our side) address type based on
+                * the advertising address type.
+                */
+               conn->resp_addr_type = hdev->adv_addr_type;
+               if (hdev->adv_addr_type == ADDR_LE_DEV_RANDOM)
+                       bacpy(&conn->resp_addr, &hdev->random_addr);
+               else
+                       bacpy(&conn->resp_addr, &hdev->bdaddr);
+
+               conn->init_addr_type = ev->bdaddr_type;
+               bacpy(&conn->init_addr, &ev->bdaddr);
+       }
 
        /* Lookup the identity address from the stored connection
         * address and address type.
@@ -3981,25 +4094,97 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
        }
 }
 
+static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
+                              u8 bdaddr_type, s8 rssi, u8 *data, u8 len)
+{
+       struct discovery_state *d = &hdev->discovery;
+       bool match;
+
+       /* Passive scanning shouldn't trigger any device found events */
+       if (hdev->le_scan_type == LE_SCAN_PASSIVE) {
+               if (type == LE_ADV_IND || type == LE_ADV_DIRECT_IND)
+                       check_pending_le_conn(hdev, bdaddr, bdaddr_type);
+               return;
+       }
+
+       /* If there's nothing pending either store the data from this
+        * event or send an immediate device found event if the data
+        * should not be stored for later.
+        */
+       if (!has_pending_adv_report(hdev)) {
+               /* If the report will trigger a SCAN_REQ store it for
+                * later merging.
+                */
+               if (type == LE_ADV_IND || type == LE_ADV_SCAN_IND) {
+                       store_pending_adv_report(hdev, bdaddr, bdaddr_type,
+                                                rssi, data, len);
+                       return;
+               }
+
+               mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL,
+                                 rssi, 0, 1, data, len, NULL, 0);
+               return;
+       }
+
+       /* Check if the pending report is for the same device as the new one */
+       match = (!bacmp(bdaddr, &d->last_adv_addr) &&
+                bdaddr_type == d->last_adv_addr_type);
+
+       /* If the pending data doesn't match this report or this isn't a
+        * scan response (e.g. we got a duplicate ADV_IND) then force
+        * sending of the pending data.
+        */
+       if (type != LE_ADV_SCAN_RSP || !match) {
+               /* Send out whatever is in the cache, but skip duplicates */
+               if (!match)
+                       mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK,
+                                         d->last_adv_addr_type, NULL,
+                                         d->last_adv_rssi, 0, 1,
+                                         d->last_adv_data,
+                                         d->last_adv_data_len, NULL, 0);
+
+               /* If the new report will trigger a SCAN_REQ store it for
+                * later merging.
+                */
+               if (type == LE_ADV_IND || type == LE_ADV_SCAN_IND) {
+                       store_pending_adv_report(hdev, bdaddr, bdaddr_type,
+                                                rssi, data, len);
+                       return;
+               }
+
+               /* The advertising reports cannot be merged, so clear
+                * the pending report and send out a device found event.
+                */
+               clear_pending_adv_report(hdev);
+               mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL,
+                                 rssi, 0, 1, data, len, NULL, 0);
+               return;
+       }
+
+       /* If we get here we've got a pending ADV_IND or ADV_SCAN_IND and
+        * the new event is a SCAN_RSP. We can therefore proceed with
+        * sending a merged device found event.
+        */
+       mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK,
+                         d->last_adv_addr_type, NULL, rssi, 0, 1, data, len,
+                         d->last_adv_data, d->last_adv_data_len);
+       clear_pending_adv_report(hdev);
+}
+
 static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        u8 num_reports = skb->data[0];
        void *ptr = &skb->data[1];
-       s8 rssi;
 
        hci_dev_lock(hdev);
 
        while (num_reports--) {
                struct hci_ev_le_advertising_info *ev = ptr;
-
-               if (ev->evt_type == LE_ADV_IND ||
-                   ev->evt_type == LE_ADV_DIRECT_IND)
-                       check_pending_le_conn(hdev, &ev->bdaddr,
-                                             ev->bdaddr_type);
+               s8 rssi;
 
                rssi = ev->data[ev->length];
-               mgmt_device_found(hdev, &ev->bdaddr, LE_LINK, ev->bdaddr_type,
-                                 NULL, rssi, 0, 1, ev->data, ev->length);
+               process_adv_report(hdev, ev->evt_type, &ev->bdaddr,
+                                  ev->bdaddr_type, rssi, ev->data, ev->length);
 
                ptr += sizeof(*ev) + ev->length + 1;
        }
index b9a418e578e0000ec4ad68734b3e34575cd759b5..80d25c150a653b57977c5b56c227908e20f4fa32 100644 (file)
@@ -143,7 +143,7 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
 
                if (!skb_copy) {
                        /* Create a private copy with headroom */
-                       skb_copy = __pskb_copy(skb, 1, GFP_ATOMIC);
+                       skb_copy = __pskb_copy_fclone(skb, 1, GFP_ATOMIC, true);
                        if (!skb_copy)
                                continue;
 
@@ -247,8 +247,8 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
                        struct hci_mon_hdr *hdr;
 
                        /* Create a private copy with headroom */
-                       skb_copy = __pskb_copy(skb, HCI_MON_HDR_SIZE,
-                                              GFP_ATOMIC);
+                       skb_copy = __pskb_copy_fclone(skb, HCI_MON_HDR_SIZE,
+                                                     GFP_ATOMIC, true);
                        if (!skb_copy)
                                continue;
 
@@ -524,16 +524,7 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
        case HCISETRAW:
                if (!capable(CAP_NET_ADMIN))
                        return -EPERM;
-
-               if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
-                       return -EPERM;
-
-               if (arg)
-                       set_bit(HCI_RAW, &hdev->flags);
-               else
-                       clear_bit(HCI_RAW, &hdev->flags);
-
-               return 0;
+               return -EOPNOTSUPP;
 
        case HCIGETCONNINFO:
                return hci_get_conn_info(hdev, (void __user *) arg);
index dc4d301d3a728ccd0af8efe4e9648b843d598406..6eabbe05fe54fe8ecc39707a05350439d99ce830 100644 (file)
@@ -471,8 +471,14 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan)
        chan->max_tx = L2CAP_DEFAULT_MAX_TX;
        chan->tx_win = L2CAP_DEFAULT_TX_WINDOW;
        chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW;
+       chan->remote_max_tx = chan->max_tx;
+       chan->remote_tx_win = chan->tx_win;
        chan->ack_win = L2CAP_DEFAULT_TX_WINDOW;
        chan->sec_level = BT_SECURITY_LOW;
+       chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
+       chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
+       chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
+       chan->conf_state = 0;
 
        set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
 }
index ef5e5b04f34fbd3c130c74dc2a41ffe775870b3b..ade3fb4c23bce81aa054e2bdd064f74019767dae 100644 (file)
@@ -1180,13 +1180,16 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
        /* Check for backlog size */
        if (sk_acceptq_is_full(parent)) {
                BT_DBG("backlog full %d", parent->sk_ack_backlog);
+               release_sock(parent);
                return NULL;
        }
 
        sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,
                              GFP_ATOMIC);
-       if (!sk)
+       if (!sk) {
+               release_sock(parent);
                return NULL;
+        }
 
        bt_sock_reclassify_lock(sk, BTPROTO_L2CAP);
 
index b3fbc73516c415ee1654eb6f2aa38d5e390fb00b..941ad7530eda48f21dcf7faef9167cbce6574a8e 100644 (file)
@@ -58,6 +58,7 @@ int bt_to_errno(__u16 code)
                return EIO;
 
        case 0x04:
+       case 0x3c:
                return EHOSTDOWN;
 
        case 0x05:
index d2d4e0d5aed017366668bf263538baf332255d20..0fce54412ffdc077f6d337eadc4cfbe20b51ed26 100644 (file)
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
 #include <net/bluetooth/mgmt.h>
 
 #include "smp.h"
 
 #define MGMT_VERSION   1
-#define MGMT_REVISION  5
+#define MGMT_REVISION  6
 
 static const u16 mgmt_commands[] = {
        MGMT_OP_READ_INDEX_LIST,
@@ -83,6 +84,7 @@ static const u16 mgmt_commands[] = {
        MGMT_OP_SET_DEBUG_KEYS,
        MGMT_OP_SET_PRIVACY,
        MGMT_OP_LOAD_IRKS,
+       MGMT_OP_GET_CONN_INFO,
 };
 
 static const u16 mgmt_events[] = {
@@ -2850,10 +2852,7 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
        }
 
        sec_level = BT_SECURITY_MEDIUM;
-       if (cp->io_cap == 0x03)
-               auth_type = HCI_AT_DEDICATED_BONDING;
-       else
-               auth_type = HCI_AT_DEDICATED_BONDING_MITM;
+       auth_type = HCI_AT_DEDICATED_BONDING;
 
        if (cp->addr.type == BDADDR_BREDR) {
                conn = hci_connect_acl(hdev, &cp->addr.bdaddr, sec_level,
@@ -3351,6 +3350,8 @@ static int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
 
 static void start_discovery_complete(struct hci_dev *hdev, u8 status)
 {
+       unsigned long timeout = 0;
+
        BT_DBG("status %d", status);
 
        if (status) {
@@ -3366,13 +3367,11 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status)
 
        switch (hdev->discovery.type) {
        case DISCOV_TYPE_LE:
-               queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable,
-                                  DISCOV_LE_TIMEOUT);
+               timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
                break;
 
        case DISCOV_TYPE_INTERLEAVED:
-               queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable,
-                                  DISCOV_INTERLEAVED_TIMEOUT);
+               timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout);
                break;
 
        case DISCOV_TYPE_BREDR:
@@ -3381,6 +3380,11 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status)
        default:
                BT_ERR("Invalid discovery type %d", hdev->discovery.type);
        }
+
+       if (!timeout)
+               return;
+
+       queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable, timeout);
 }
 
 static int start_discovery(struct sock *sk, struct hci_dev *hdev,
@@ -4530,7 +4534,7 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
 
        for (i = 0; i < key_count; i++) {
                struct mgmt_ltk_info *key = &cp->keys[i];
-               u8 type, addr_type;
+               u8 type, addr_type, authenticated;
 
                if (key->addr.type == BDADDR_LE_PUBLIC)
                        addr_type = ADDR_LE_DEV_PUBLIC;
@@ -4542,8 +4546,19 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
                else
                        type = HCI_SMP_LTK_SLAVE;
 
+               switch (key->type) {
+               case MGMT_LTK_UNAUTHENTICATED:
+                       authenticated = 0x00;
+                       break;
+               case MGMT_LTK_AUTHENTICATED:
+                       authenticated = 0x01;
+                       break;
+               default:
+                       continue;
+               }
+
                hci_add_ltk(hdev, &key->addr.bdaddr, addr_type, type,
-                           key->type, key->val, key->enc_size, key->ediv,
+                           authenticated, key->val, key->enc_size, key->ediv,
                            key->rand);
        }
 
@@ -4555,6 +4570,218 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
        return err;
 }
 
+struct cmd_conn_lookup {
+       struct hci_conn *conn;
+       bool valid_tx_power;
+       u8 mgmt_status;
+};
+
+static void get_conn_info_complete(struct pending_cmd *cmd, void *data)
+{
+       struct cmd_conn_lookup *match = data;
+       struct mgmt_cp_get_conn_info *cp;
+       struct mgmt_rp_get_conn_info rp;
+       struct hci_conn *conn = cmd->user_data;
+
+       if (conn != match->conn)
+               return;
+
+       cp = (struct mgmt_cp_get_conn_info *) cmd->param;
+
+       memset(&rp, 0, sizeof(rp));
+       bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+       rp.addr.type = cp->addr.type;
+
+       if (!match->mgmt_status) {
+               rp.rssi = conn->rssi;
+
+               if (match->valid_tx_power) {
+                       rp.tx_power = conn->tx_power;
+                       rp.max_tx_power = conn->max_tx_power;
+               } else {
+                       rp.tx_power = HCI_TX_POWER_INVALID;
+                       rp.max_tx_power = HCI_TX_POWER_INVALID;
+               }
+       }
+
+       cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO,
+                    match->mgmt_status, &rp, sizeof(rp));
+
+       hci_conn_drop(conn);
+
+       mgmt_pending_remove(cmd);
+}
+
+static void conn_info_refresh_complete(struct hci_dev *hdev, u8 status)
+{
+       struct hci_cp_read_rssi *cp;
+       struct hci_conn *conn;
+       struct cmd_conn_lookup match;
+       u16 handle;
+
+       BT_DBG("status 0x%02x", status);
+
+       hci_dev_lock(hdev);
+
+       /* TX power data is valid in case request completed successfully,
+        * otherwise we assume it's not valid. At the moment we assume that
+        * either both or none of current and max values are valid to keep code
+        * simple.
+        */
+       match.valid_tx_power = !status;
+
+       /* Commands sent in request are either Read RSSI or Read Transmit Power
+        * Level so we check which one was last sent to retrieve connection
+        * handle.  Both commands have handle as first parameter so it's safe to
+        * cast data on the same command struct.
+        *
+        * First command sent is always Read RSSI and we fail only if it fails.
+        * In other case we simply override error to indicate success as we
+        * already remembered if TX power value is actually valid.
+        */
+       cp = hci_sent_cmd_data(hdev, HCI_OP_READ_RSSI);
+       if (!cp) {
+               cp = hci_sent_cmd_data(hdev, HCI_OP_READ_TX_POWER);
+               status = 0;
+       }
+
+       if (!cp) {
+               BT_ERR("invalid sent_cmd in response");
+               goto unlock;
+       }
+
+       handle = __le16_to_cpu(cp->handle);
+       conn = hci_conn_hash_lookup_handle(hdev, handle);
+       if (!conn) {
+               BT_ERR("unknown handle (%d) in response", handle);
+               goto unlock;
+       }
+
+       match.conn = conn;
+       match.mgmt_status = mgmt_status(status);
+
+       /* Cache refresh is complete, now reply for mgmt request for given
+        * connection only.
+        */
+       mgmt_pending_foreach(MGMT_OP_GET_CONN_INFO, hdev,
+                            get_conn_info_complete, &match);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
+static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
+                        u16 len)
+{
+       struct mgmt_cp_get_conn_info *cp = data;
+       struct mgmt_rp_get_conn_info rp;
+       struct hci_conn *conn;
+       unsigned long conn_info_age;
+       int err = 0;
+
+       BT_DBG("%s", hdev->name);
+
+       memset(&rp, 0, sizeof(rp));
+       bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+       rp.addr.type = cp->addr.type;
+
+       if (!bdaddr_type_is_valid(cp->addr.type))
+               return cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+                                   MGMT_STATUS_INVALID_PARAMS,
+                                   &rp, sizeof(rp));
+
+       hci_dev_lock(hdev);
+
+       if (!hdev_is_powered(hdev)) {
+               err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+                                  MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
+               goto unlock;
+       }
+
+       if (cp->addr.type == BDADDR_BREDR)
+               conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
+                                              &cp->addr.bdaddr);
+       else
+               conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
+
+       if (!conn || conn->state != BT_CONNECTED) {
+               err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+                                  MGMT_STATUS_NOT_CONNECTED, &rp, sizeof(rp));
+               goto unlock;
+       }
+
+       /* To avoid client trying to guess when to poll again for information we
+        * calculate conn info age as random value between min/max set in hdev.
+        */
+       conn_info_age = hdev->conn_info_min_age +
+                       prandom_u32_max(hdev->conn_info_max_age -
+                                       hdev->conn_info_min_age);
+
+       /* Query controller to refresh cached values if they are too old or were
+        * never read.
+        */
+       if (time_after(jiffies, conn->conn_info_timestamp +
+                      msecs_to_jiffies(conn_info_age)) ||
+           !conn->conn_info_timestamp) {
+               struct hci_request req;
+               struct hci_cp_read_tx_power req_txp_cp;
+               struct hci_cp_read_rssi req_rssi_cp;
+               struct pending_cmd *cmd;
+
+               hci_req_init(&req, hdev);
+               req_rssi_cp.handle = cpu_to_le16(conn->handle);
+               hci_req_add(&req, HCI_OP_READ_RSSI, sizeof(req_rssi_cp),
+                           &req_rssi_cp);
+
+               /* For LE links TX power does not change thus we don't need to
+                * query for it once value is known.
+                */
+               if (!bdaddr_type_is_le(cp->addr.type) ||
+                   conn->tx_power == HCI_TX_POWER_INVALID) {
+                       req_txp_cp.handle = cpu_to_le16(conn->handle);
+                       req_txp_cp.type = 0x00;
+                       hci_req_add(&req, HCI_OP_READ_TX_POWER,
+                                   sizeof(req_txp_cp), &req_txp_cp);
+               }
+
+               /* Max TX power needs to be read only once per connection */
+               if (conn->max_tx_power == HCI_TX_POWER_INVALID) {
+                       req_txp_cp.handle = cpu_to_le16(conn->handle);
+                       req_txp_cp.type = 0x01;
+                       hci_req_add(&req, HCI_OP_READ_TX_POWER,
+                                   sizeof(req_txp_cp), &req_txp_cp);
+               }
+
+               err = hci_req_run(&req, conn_info_refresh_complete);
+               if (err < 0)
+                       goto unlock;
+
+               cmd = mgmt_pending_add(sk, MGMT_OP_GET_CONN_INFO, hdev,
+                                      data, len);
+               if (!cmd) {
+                       err = -ENOMEM;
+                       goto unlock;
+               }
+
+               hci_conn_hold(conn);
+               cmd->user_data = conn;
+
+               conn->conn_info_timestamp = jiffies;
+       } else {
+               /* Cache is valid, just reply with values cached in hci_conn */
+               rp.rssi = conn->rssi;
+               rp.tx_power = conn->tx_power;
+               rp.max_tx_power = conn->max_tx_power;
+
+               err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+                                  MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
+       }
+
+unlock:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
 static const struct mgmt_handler {
        int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
                     u16 data_len);
@@ -4610,6 +4837,7 @@ static const struct mgmt_handler {
        { set_debug_keys,         false, MGMT_SETTING_SIZE },
        { set_privacy,            false, MGMT_SET_PRIVACY_SIZE },
        { load_irks,              true,  MGMT_LOAD_IRKS_SIZE },
+       { get_conn_info,          false, MGMT_GET_CONN_INFO_SIZE },
 };
 
 
@@ -5005,6 +5233,14 @@ void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
        mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
 }
 
+static u8 mgmt_ltk_type(struct smp_ltk *ltk)
+{
+       if (ltk->authenticated)
+               return MGMT_LTK_AUTHENTICATED;
+
+       return MGMT_LTK_UNAUTHENTICATED;
+}
+
 void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent)
 {
        struct mgmt_ev_new_long_term_key ev;
@@ -5030,7 +5266,7 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent)
 
        bacpy(&ev.key.addr.bdaddr, &key->bdaddr);
        ev.key.addr.type = link_to_bdaddr(LE_LINK, key->bdaddr_type);
-       ev.key.type = key->authenticated;
+       ev.key.type = mgmt_ltk_type(key);
        ev.key.enc_size = key->enc_size;
        ev.key.ediv = key->ediv;
        ev.key.rand = key->rand;
@@ -5668,8 +5904,9 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
 }
 
 void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                      u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8
-                      ssp, u8 *eir, u16 eir_len)
+                      u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name,
+                      u8 ssp, u8 *eir, u16 eir_len, u8 *scan_rsp,
+                      u8 scan_rsp_len)
 {
        char buf[512];
        struct mgmt_ev_device_found *ev = (void *) buf;
@@ -5679,8 +5916,10 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
        if (!hci_discovery_active(hdev))
                return;
 
-       /* Leave 5 bytes for a potential CoD field */
-       if (sizeof(*ev) + eir_len + 5 > sizeof(buf))
+       /* Make sure that the buffer is big enough. The 5 extra bytes
+        * are for the potential CoD field.
+        */
+       if (sizeof(*ev) + eir_len + scan_rsp_len + 5 > sizeof(buf))
                return;
 
        memset(buf, 0, sizeof(buf));
@@ -5707,8 +5946,11 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
                                          dev_class, 3);
 
-       ev->eir_len = cpu_to_le16(eir_len);
-       ev_size = sizeof(*ev) + eir_len;
+       if (scan_rsp_len > 0)
+               memcpy(ev->eir + eir_len, scan_rsp, scan_rsp_len);
+
+       ev->eir_len = cpu_to_le16(eir_len + scan_rsp_len);
+       ev_size = sizeof(*ev) + eir_len + scan_rsp_len;
 
        mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL);
 }
index cf620260affaea75c4c1e9875ca338afb04f7b2e..754b6fe4f742af8ce4662a25065c6caba242295b 100644 (file)
@@ -307,7 +307,7 @@ struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio)
        setup_timer(&d->timer, rfcomm_dlc_timeout, (unsigned long)d);
 
        skb_queue_head_init(&d->tx_queue);
-       spin_lock_init(&d->lock);
+       mutex_init(&d->lock);
        atomic_set(&d->refcnt, 1);
 
        rfcomm_dlc_clear_state(d);
index 403ec09f480a2a72983b0b1b9db0222fd6e86742..8e385a0ae60e0bd6e4b1ed7615905a7170c4a8bc 100644 (file)
@@ -70,7 +70,7 @@ struct rfcomm_dev {
 };
 
 static LIST_HEAD(rfcomm_dev_list);
-static DEFINE_SPINLOCK(rfcomm_dev_lock);
+static DEFINE_MUTEX(rfcomm_dev_lock);
 
 static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb);
 static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err);
@@ -96,9 +96,9 @@ static void rfcomm_dev_destruct(struct tty_port *port)
        if (dev->tty_dev)
                tty_unregister_device(rfcomm_tty_driver, dev->id);
 
-       spin_lock(&rfcomm_dev_lock);
+       mutex_lock(&rfcomm_dev_lock);
        list_del(&dev->list);
-       spin_unlock(&rfcomm_dev_lock);
+       mutex_unlock(&rfcomm_dev_lock);
 
        kfree(dev);
 
@@ -161,14 +161,14 @@ static struct rfcomm_dev *rfcomm_dev_get(int id)
 {
        struct rfcomm_dev *dev;
 
-       spin_lock(&rfcomm_dev_lock);
+       mutex_lock(&rfcomm_dev_lock);
 
        dev = __rfcomm_dev_lookup(id);
 
        if (dev && !tty_port_get(&dev->port))
                dev = NULL;
 
-       spin_unlock(&rfcomm_dev_lock);
+       mutex_unlock(&rfcomm_dev_lock);
 
        return dev;
 }
@@ -224,7 +224,7 @@ static struct rfcomm_dev *__rfcomm_dev_add(struct rfcomm_dev_req *req,
        if (!dev)
                return ERR_PTR(-ENOMEM);
 
-       spin_lock(&rfcomm_dev_lock);
+       mutex_lock(&rfcomm_dev_lock);
 
        if (req->dev_id < 0) {
                dev->id = 0;
@@ -305,11 +305,11 @@ static struct rfcomm_dev *__rfcomm_dev_add(struct rfcomm_dev_req *req,
           holds reference to this module. */
        __module_get(THIS_MODULE);
 
-       spin_unlock(&rfcomm_dev_lock);
+       mutex_unlock(&rfcomm_dev_lock);
        return dev;
 
 out:
-       spin_unlock(&rfcomm_dev_lock);
+       mutex_unlock(&rfcomm_dev_lock);
        kfree(dev);
        return ERR_PTR(err);
 }
@@ -524,7 +524,7 @@ static int rfcomm_get_dev_list(void __user *arg)
 
        di = dl->dev_info;
 
-       spin_lock(&rfcomm_dev_lock);
+       mutex_lock(&rfcomm_dev_lock);
 
        list_for_each_entry(dev, &rfcomm_dev_list, list) {
                if (!tty_port_get(&dev->port))
@@ -540,7 +540,7 @@ static int rfcomm_get_dev_list(void __user *arg)
                        break;
        }
 
-       spin_unlock(&rfcomm_dev_lock);
+       mutex_unlock(&rfcomm_dev_lock);
 
        dl->dev_num = n;
        size = sizeof(*dl) + n * sizeof(*di);
index dfb4e1161c10fbb62b6ac43220949992a5075dfc..3d1cc164557de1750e4768f229aaede55b03e80c 100644 (file)
 
 #define AUTH_REQ_MASK   0x07
 
+#define SMP_FLAG_TK_VALID      1
+#define SMP_FLAG_CFM_PENDING   2
+#define SMP_FLAG_MITM_AUTH     3
+#define SMP_FLAG_COMPLETE      4
+#define SMP_FLAG_INITIATOR     5
+
+struct smp_chan {
+       struct l2cap_conn *conn;
+       u8              preq[7]; /* SMP Pairing Request */
+       u8              prsp[7]; /* SMP Pairing Response */
+       u8              prnd[16]; /* SMP Pairing Random (local) */
+       u8              rrnd[16]; /* SMP Pairing Random (remote) */
+       u8              pcnf[16]; /* SMP Pairing Confirm */
+       u8              tk[16]; /* SMP Temporary Key */
+       u8              enc_key_size;
+       u8              remote_key_dist;
+       bdaddr_t        id_addr;
+       u8              id_addr_type;
+       u8              irk[16];
+       struct smp_csrk *csrk;
+       struct smp_csrk *slave_csrk;
+       struct smp_ltk  *ltk;
+       struct smp_ltk  *slave_ltk;
+       struct smp_irk  *remote_irk;
+       unsigned long   flags;
+};
+
 static inline void swap128(const u8 src[16], u8 dst[16])
 {
        int i;
@@ -369,7 +396,7 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
 
        /* Initialize key for JUST WORKS */
        memset(smp->tk, 0, sizeof(smp->tk));
-       clear_bit(SMP_FLAG_TK_VALID, &smp->smp_flags);
+       clear_bit(SMP_FLAG_TK_VALID, &smp->flags);
 
        BT_DBG("tk_request: auth:%d lcl:%d rem:%d", auth, local_io, remote_io);
 
@@ -388,19 +415,18 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
                method = JUST_WORKS;
 
        /* Don't confirm locally initiated pairing attempts */
-       if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR,
-                                          &smp->smp_flags))
+       if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR, &smp->flags))
                method = JUST_WORKS;
 
        /* If Just Works, Continue with Zero TK */
        if (method == JUST_WORKS) {
-               set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags);
+               set_bit(SMP_FLAG_TK_VALID, &smp->flags);
                return 0;
        }
 
        /* Not Just Works/Confirm results in MITM Authentication */
        if (method != JUST_CFM)
-               set_bit(SMP_FLAG_MITM_AUTH, &smp->smp_flags);
+               set_bit(SMP_FLAG_MITM_AUTH, &smp->flags);
 
        /* If both devices have Keyoard-Display I/O, the master
         * Confirms and the slave Enters the passkey.
@@ -419,7 +445,7 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
                passkey %= 1000000;
                put_unaligned_le32(passkey, smp->tk);
                BT_DBG("PassKey: %d", passkey);
-               set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags);
+               set_bit(SMP_FLAG_TK_VALID, &smp->flags);
        }
 
        hci_dev_lock(hcon->hdev);
@@ -441,15 +467,13 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
        return ret;
 }
 
-static void confirm_work(struct work_struct *work)
+static u8 smp_confirm(struct smp_chan *smp)
 {
-       struct smp_chan *smp = container_of(work, struct smp_chan, confirm);
        struct l2cap_conn *conn = smp->conn;
        struct hci_dev *hdev = conn->hcon->hdev;
        struct crypto_blkcipher *tfm = hdev->tfm_aes;
        struct smp_cmd_pairing_confirm cp;
        int ret;
-       u8 reason;
 
        BT_DBG("conn %p", conn);
 
@@ -463,35 +487,27 @@ static void confirm_work(struct work_struct *work)
 
        hci_dev_unlock(hdev);
 
-       if (ret) {
-               reason = SMP_UNSPECIFIED;
-               goto error;
-       }
+       if (ret)
+               return SMP_UNSPECIFIED;
 
-       clear_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags);
+       clear_bit(SMP_FLAG_CFM_PENDING, &smp->flags);
 
        smp_send_cmd(smp->conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
 
-       return;
-
-error:
-       smp_failure(conn, reason);
+       return 0;
 }
 
-static void random_work(struct work_struct *work)
+static u8 smp_random(struct smp_chan *smp)
 {
-       struct smp_chan *smp = container_of(work, struct smp_chan, random);
        struct l2cap_conn *conn = smp->conn;
        struct hci_conn *hcon = conn->hcon;
        struct hci_dev *hdev = hcon->hdev;
        struct crypto_blkcipher *tfm = hdev->tfm_aes;
-       u8 reason, confirm[16];
+       u8 confirm[16];
        int ret;
 
-       if (IS_ERR_OR_NULL(tfm)) {
-               reason = SMP_UNSPECIFIED;
-               goto error;
-       }
+       if (IS_ERR_OR_NULL(tfm))
+               return SMP_UNSPECIFIED;
 
        BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
 
@@ -504,15 +520,12 @@ static void random_work(struct work_struct *work)
 
        hci_dev_unlock(hdev);
 
-       if (ret) {
-               reason = SMP_UNSPECIFIED;
-               goto error;
-       }
+       if (ret)
+               return SMP_UNSPECIFIED;
 
        if (memcmp(smp->pcnf, confirm, sizeof(smp->pcnf)) != 0) {
                BT_ERR("Pairing failed (confirmation values mismatch)");
-               reason = SMP_CONFIRM_FAILED;
-               goto error;
+               return SMP_CONFIRM_FAILED;
        }
 
        if (hcon->out) {
@@ -525,10 +538,8 @@ static void random_work(struct work_struct *work)
                memset(stk + smp->enc_key_size, 0,
                       SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
 
-               if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags)) {
-                       reason = SMP_UNSPECIFIED;
-                       goto error;
-               }
+               if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags))
+                       return SMP_UNSPECIFIED;
 
                hci_le_start_enc(hcon, ediv, rand, stk);
                hcon->enc_key_size = smp->enc_key_size;
@@ -550,10 +561,7 @@ static void random_work(struct work_struct *work)
                            ediv, rand);
        }
 
-       return;
-
-error:
-       smp_failure(conn, reason);
+       return 0;
 }
 
 static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
@@ -564,9 +572,6 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
        if (!smp)
                return NULL;
 
-       INIT_WORK(&smp->confirm, confirm_work);
-       INIT_WORK(&smp->random, random_work);
-
        smp->conn = conn;
        conn->smp_chan = smp;
        conn->hcon->smp_conn = conn;
@@ -583,7 +588,7 @@ void smp_chan_destroy(struct l2cap_conn *conn)
 
        BUG_ON(!smp);
 
-       complete = test_bit(SMP_FLAG_COMPLETE, &smp->smp_flags);
+       complete = test_bit(SMP_FLAG_COMPLETE, &smp->flags);
        mgmt_smp_complete(conn->hcon, complete);
 
        kfree(smp->csrk);
@@ -634,7 +639,7 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
                put_unaligned_le32(value, smp->tk);
                /* Fall Through */
        case MGMT_OP_USER_CONFIRM_REPLY:
-               set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags);
+               set_bit(SMP_FLAG_TK_VALID, &smp->flags);
                break;
        case MGMT_OP_USER_PASSKEY_NEG_REPLY:
        case MGMT_OP_USER_CONFIRM_NEG_REPLY:
@@ -646,8 +651,11 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
        }
 
        /* If it is our turn to send Pairing Confirm, do so now */
-       if (test_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags))
-               queue_work(hcon->hdev->workqueue, &smp->confirm);
+       if (test_bit(SMP_FLAG_CFM_PENDING, &smp->flags)) {
+               u8 rsp = smp_confirm(smp);
+               if (rsp)
+                       smp_failure(conn, rsp);
+       }
 
        return 0;
 }
@@ -656,14 +664,13 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct smp_cmd_pairing rsp, *req = (void *) skb->data;
        struct smp_chan *smp;
-       u8 key_size;
-       u8 auth = SMP_AUTH_NONE;
+       u8 key_size, auth;
        int ret;
 
        BT_DBG("conn %p", conn);
 
        if (skb->len < sizeof(*req))
-               return SMP_UNSPECIFIED;
+               return SMP_INVALID_PARAMS;
 
        if (conn->hcon->link_mode & HCI_LM_MASTER)
                return SMP_CMD_NOTSUPP;
@@ -681,8 +688,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
        skb_pull(skb, sizeof(*req));
 
        /* We didn't start the pairing, so match remote */
-       if (req->auth_req & SMP_AUTH_BONDING)
-               auth = req->auth_req;
+       auth = req->auth_req;
 
        conn->hcon->pending_sec_level = authreq_to_seclevel(auth);
 
@@ -704,7 +710,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
        if (ret)
                return SMP_UNSPECIFIED;
 
-       clear_bit(SMP_FLAG_INITIATOR, &smp->smp_flags);
+       clear_bit(SMP_FLAG_INITIATOR, &smp->flags);
 
        return 0;
 }
@@ -713,14 +719,13 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct smp_cmd_pairing *req, *rsp = (void *) skb->data;
        struct smp_chan *smp = conn->smp_chan;
-       struct hci_dev *hdev = conn->hcon->hdev;
        u8 key_size, auth = SMP_AUTH_NONE;
        int ret;
 
        BT_DBG("conn %p", conn);
 
        if (skb->len < sizeof(*rsp))
-               return SMP_UNSPECIFIED;
+               return SMP_INVALID_PARAMS;
 
        if (!(conn->hcon->link_mode & HCI_LM_MASTER))
                return SMP_CMD_NOTSUPP;
@@ -753,11 +758,11 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
        if (ret)
                return SMP_UNSPECIFIED;
 
-       set_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags);
+       set_bit(SMP_FLAG_CFM_PENDING, &smp->flags);
 
        /* Can't compose response until we have been confirmed */
-       if (test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags))
-               queue_work(hdev->workqueue, &smp->confirm);
+       if (test_bit(SMP_FLAG_TK_VALID, &smp->flags))
+               return smp_confirm(smp);
 
        return 0;
 }
@@ -765,12 +770,11 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
 static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct smp_chan *smp = conn->smp_chan;
-       struct hci_dev *hdev = conn->hcon->hdev;
 
        BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
 
        if (skb->len < sizeof(smp->pcnf))
-               return SMP_UNSPECIFIED;
+               return SMP_INVALID_PARAMS;
 
        memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf));
        skb_pull(skb, sizeof(smp->pcnf));
@@ -778,10 +782,10 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
        if (conn->hcon->out)
                smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
                             smp->prnd);
-       else if (test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags))
-               queue_work(hdev->workqueue, &smp->confirm);
+       else if (test_bit(SMP_FLAG_TK_VALID, &smp->flags))
+               return smp_confirm(smp);
        else
-               set_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags);
+               set_bit(SMP_FLAG_CFM_PENDING, &smp->flags);
 
        return 0;
 }
@@ -789,19 +793,16 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
 static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct smp_chan *smp = conn->smp_chan;
-       struct hci_dev *hdev = conn->hcon->hdev;
 
        BT_DBG("conn %p", conn);
 
        if (skb->len < sizeof(smp->rrnd))
-               return SMP_UNSPECIFIED;
+               return SMP_INVALID_PARAMS;
 
        memcpy(smp->rrnd, skb->data, sizeof(smp->rrnd));
        skb_pull(skb, sizeof(smp->rrnd));
 
-       queue_work(hdev->workqueue, &smp->random);
-
-       return 0;
+       return smp_random(smp);
 }
 
 static u8 smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
@@ -836,7 +837,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
        BT_DBG("conn %p", conn);
 
        if (skb->len < sizeof(*rp))
-               return SMP_UNSPECIFIED;
+               return SMP_INVALID_PARAMS;
 
        if (!(conn->hcon->link_mode & HCI_LM_MASTER))
                return SMP_CMD_NOTSUPP;
@@ -861,7 +862,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
 
        smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
 
-       clear_bit(SMP_FLAG_INITIATOR, &smp->smp_flags);
+       clear_bit(SMP_FLAG_INITIATOR, &smp->flags);
 
        return 0;
 }
@@ -908,10 +909,11 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
 
        authreq = seclevel_to_authreq(sec_level);
 
-       /* hcon->auth_type is set by pair_device in mgmt.c. If the MITM
-        * flag is set we should also set it for the SMP request.
+       /* Require MITM if IO Capability allows or the security level
+        * requires it.
         */
-       if ((hcon->auth_type & 0x01))
+       if (hcon->io_capability != HCI_IO_NO_INPUT_OUTPUT ||
+           sec_level > BT_SECURITY_MEDIUM)
                authreq |= SMP_AUTH_MITM;
 
        if (hcon->link_mode & HCI_LM_MASTER) {
@@ -928,7 +930,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
                smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp);
        }
 
-       set_bit(SMP_FLAG_INITIATOR, &smp->smp_flags);
+       set_bit(SMP_FLAG_INITIATOR, &smp->flags);
 
 done:
        hcon->pending_sec_level = sec_level;
@@ -944,7 +946,7 @@ static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb)
        BT_DBG("conn %p", conn);
 
        if (skb->len < sizeof(*rp))
-               return SMP_UNSPECIFIED;
+               return SMP_INVALID_PARAMS;
 
        /* Ignore this PDU if it wasn't requested */
        if (!(smp->remote_key_dist & SMP_DIST_ENC_KEY))
@@ -969,7 +971,7 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
        BT_DBG("conn %p", conn);
 
        if (skb->len < sizeof(*rp))
-               return SMP_UNSPECIFIED;
+               return SMP_INVALID_PARAMS;
 
        /* Ignore this PDU if it wasn't requested */
        if (!(smp->remote_key_dist & SMP_DIST_ENC_KEY))
@@ -1001,7 +1003,7 @@ static int smp_cmd_ident_info(struct l2cap_conn *conn, struct sk_buff *skb)
        BT_DBG("");
 
        if (skb->len < sizeof(*info))
-               return SMP_UNSPECIFIED;
+               return SMP_INVALID_PARAMS;
 
        /* Ignore this PDU if it wasn't requested */
        if (!(smp->remote_key_dist & SMP_DIST_ID_KEY))
@@ -1025,7 +1027,7 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
        BT_DBG("");
 
        if (skb->len < sizeof(*info))
-               return SMP_UNSPECIFIED;
+               return SMP_INVALID_PARAMS;
 
        /* Ignore this PDU if it wasn't requested */
        if (!(smp->remote_key_dist & SMP_DIST_ID_KEY))
@@ -1075,7 +1077,7 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
        BT_DBG("conn %p", conn);
 
        if (skb->len < sizeof(*rp))
-               return SMP_UNSPECIFIED;
+               return SMP_INVALID_PARAMS;
 
        /* Ignore this PDU if it wasn't requested */
        if (!(smp->remote_key_dist & SMP_DIST_SIGN))
@@ -1358,7 +1360,7 @@ int smp_distribute_keys(struct l2cap_conn *conn)
 
        clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags);
        cancel_delayed_work_sync(&conn->security_timer);
-       set_bit(SMP_FLAG_COMPLETE, &smp->smp_flags);
+       set_bit(SMP_FLAG_COMPLETE, &smp->flags);
        smp_notify_keys(conn);
 
        smp_chan_destroy(conn);
index 1277147a915070e3a5256debdd98316fa1d916c5..5a8dc36460a1b7e3a61b08d18407708673a7d1a5 100644 (file)
@@ -111,39 +111,11 @@ struct smp_cmd_security_req {
 #define SMP_CMD_NOTSUPP                        0x07
 #define SMP_UNSPECIFIED                        0x08
 #define SMP_REPEATED_ATTEMPTS          0x09
+#define SMP_INVALID_PARAMS             0x0a
 
 #define SMP_MIN_ENC_KEY_SIZE           7
 #define SMP_MAX_ENC_KEY_SIZE           16
 
-#define SMP_FLAG_TK_VALID      1
-#define SMP_FLAG_CFM_PENDING   2
-#define SMP_FLAG_MITM_AUTH     3
-#define SMP_FLAG_COMPLETE      4
-#define SMP_FLAG_INITIATOR     5
-
-struct smp_chan {
-       struct l2cap_conn *conn;
-       u8              preq[7]; /* SMP Pairing Request */
-       u8              prsp[7]; /* SMP Pairing Response */
-       u8              prnd[16]; /* SMP Pairing Random (local) */
-       u8              rrnd[16]; /* SMP Pairing Random (remote) */
-       u8              pcnf[16]; /* SMP Pairing Confirm */
-       u8              tk[16]; /* SMP Temporary Key */
-       u8              enc_key_size;
-       u8              remote_key_dist;
-       bdaddr_t        id_addr;
-       u8              id_addr_type;
-       u8              irk[16];
-       struct smp_csrk *csrk;
-       struct smp_csrk *slave_csrk;
-       struct smp_ltk  *ltk;
-       struct smp_ltk  *slave_ltk;
-       struct smp_irk  *remote_irk;
-       unsigned long   smp_flags;
-       struct work_struct confirm;
-       struct work_struct random;
-};
-
 /* SMP Commands */
 bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level);
 int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
index e85498b2f1669f7dae3b7f5f5e1c0970b1be65c6..8590b942bffa62a11d23c4e7a0666c9564d310c1 100644 (file)
@@ -5,7 +5,7 @@
 obj-$(CONFIG_BRIDGE) += bridge.o
 
 bridge-y       := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
-                       br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+                       br_ioctl.o br_stp.o br_stp_bpdu.o \
                        br_stp_if.o br_stp_timer.o br_netlink.o
 
 bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o
@@ -16,4 +16,4 @@ bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o
 
 bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o
 
-obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
+obj-$(CONFIG_NETFILTER) += netfilter/
index 19311aafcf5a06e6ae7abaaf82527711573dc6d6..1a755a1e54101d924e88ea240a82c154dcb7bbe5 100644 (file)
 
 #include "br_private.h"
 
+/*
+ * Handle changes in state of network devices enslaved to a bridge.
+ *
+ * Note: don't care about up/down if bridge itself is down, because
+ *     port state is checked when bridge is brought up.
+ */
+static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
+{
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+       struct net_bridge_port *p;
+       struct net_bridge *br;
+       bool changed_addr;
+       int err;
+
+       /* register of bridge completed, add sysfs entries */
+       if ((dev->priv_flags & IFF_EBRIDGE) && event == NETDEV_REGISTER) {
+               br_sysfs_addbr(dev);
+               return NOTIFY_DONE;
+       }
+
+       /* not a port of a bridge */
+       p = br_port_get_rtnl(dev);
+       if (!p)
+               return NOTIFY_DONE;
+
+       br = p->br;
+
+       switch (event) {
+       case NETDEV_CHANGEMTU:
+               dev_set_mtu(br->dev, br_min_mtu(br));
+               break;
+
+       case NETDEV_CHANGEADDR:
+               spin_lock_bh(&br->lock);
+               br_fdb_changeaddr(p, dev->dev_addr);
+               changed_addr = br_stp_recalculate_bridge_id(br);
+               spin_unlock_bh(&br->lock);
+
+               if (changed_addr)
+                       call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);
+
+               break;
+
+       case NETDEV_CHANGE:
+               br_port_carrier_check(p);
+               break;
+
+       case NETDEV_FEAT_CHANGE:
+               netdev_update_features(br->dev);
+               break;
+
+       case NETDEV_DOWN:
+               spin_lock_bh(&br->lock);
+               if (br->dev->flags & IFF_UP)
+                       br_stp_disable_port(p);
+               spin_unlock_bh(&br->lock);
+               break;
+
+       case NETDEV_UP:
+               if (netif_running(br->dev) && netif_oper_up(dev)) {
+                       spin_lock_bh(&br->lock);
+                       br_stp_enable_port(p);
+                       spin_unlock_bh(&br->lock);
+               }
+               break;
+
+       case NETDEV_UNREGISTER:
+               br_del_if(br, dev);
+               break;
+
+       case NETDEV_CHANGENAME:
+               err = br_sysfs_renameif(p);
+               if (err)
+                       return notifier_from_errno(err);
+               break;
+
+       case NETDEV_PRE_TYPE_CHANGE:
+               /* Forbid underlaying device to change its type. */
+               return NOTIFY_BAD;
+
+       case NETDEV_RESEND_IGMP:
+               /* Propagate to master device */
+               call_netdevice_notifiers(event, br->dev);
+               break;
+       }
+
+       /* Events that may cause spanning tree to refresh */
+       if (event == NETDEV_CHANGEADDR || event == NETDEV_UP ||
+           event == NETDEV_CHANGE || event == NETDEV_DOWN)
+               br_ifinfo_notify(RTM_NEWLINK, p);
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block br_device_notifier = {
+       .notifier_call = br_device_event
+};
+
 static void __net_exit br_net_exit(struct net *net)
 {
        struct net_device *dev;
index 3e2da2cb72db1725f064ec21d3ce6ab8765532c1..568cccd39a3d8a25716ef13e14b2f4e4850c1db5 100644 (file)
@@ -112,6 +112,12 @@ static void br_dev_set_multicast_list(struct net_device *dev)
 {
 }
 
+static void br_dev_change_rx_flags(struct net_device *dev, int change)
+{
+       if (change & IFF_PROMISC)
+               br_manage_promisc(netdev_priv(dev));
+}
+
 static int br_dev_stop(struct net_device *dev)
 {
        struct net_bridge *br = netdev_priv(dev);
@@ -309,6 +315,7 @@ static const struct net_device_ops br_netdev_ops = {
        .ndo_get_stats64         = br_get_stats64,
        .ndo_set_mac_address     = br_set_mac_address,
        .ndo_set_rx_mode         = br_dev_set_multicast_list,
+       .ndo_change_rx_flags     = br_dev_change_rx_flags,
        .ndo_change_mtu          = br_change_mtu,
        .ndo_do_ioctl            = br_dev_ioctl,
 #ifdef CONFIG_NET_POLL_CONTROLLER
@@ -348,14 +355,15 @@ void br_dev_setup(struct net_device *dev)
 
        dev->netdev_ops = &br_netdev_ops;
        dev->destructor = br_dev_free;
-       SET_ETHTOOL_OPS(dev, &br_ethtool_ops);
+       dev->ethtool_ops = &br_ethtool_ops;
        SET_NETDEV_DEVTYPE(dev, &br_type);
        dev->tx_queue_len = 0;
        dev->priv_flags = IFF_EBRIDGE;
 
        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;
+                       NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX;
+       dev->hw_features = COMMON_FEATURES | NETIF_F_HW_VLAN_CTAG_TX |
+                          NETIF_F_HW_VLAN_STAG_TX;
        dev->vlan_features = COMMON_FEATURES;
 
        br->dev = dev;
@@ -370,6 +378,7 @@ void br_dev_setup(struct net_device *dev)
 
        br->stp_enabled = BR_NO_STP;
        br->group_fwd_mask = BR_GROUPFWD_DEFAULT;
+       br->group_fwd_mask_required = BR_GROUPFWD_DEFAULT;
 
        br->designated_root = br->bridge_id;
        br->bridge_max_age = br->max_age = 20 * HZ;
@@ -380,4 +389,5 @@ void br_dev_setup(struct net_device *dev)
        br_netfilter_rtable_init(br);
        br_stp_timer_init(br);
        br_multicast_init(br);
+       br_vlan_init(br);
 }
index 474d36f93342ea5c3a7cfcb210d3520b2fe78c6c..b524c36c12731b6c84a7587125fdf2db6417c830 100644 (file)
@@ -85,8 +85,58 @@ static void fdb_rcu_free(struct rcu_head *head)
        kmem_cache_free(br_fdb_cache, ent);
 }
 
+/* When a static FDB entry is added, the mac address from the entry is
+ * added to the bridge private HW address list and all required ports
+ * are then updated with the new information.
+ * Called under RTNL.
+ */
+static void fdb_add_hw(struct net_bridge *br, const unsigned char *addr)
+{
+       int err;
+       struct net_bridge_port *p, *tmp;
+
+       ASSERT_RTNL();
+
+       list_for_each_entry(p, &br->port_list, list) {
+               if (!br_promisc_port(p)) {
+                       err = dev_uc_add(p->dev, addr);
+                       if (err)
+                               goto undo;
+               }
+       }
+
+       return;
+undo:
+       list_for_each_entry(tmp, &br->port_list, list) {
+               if (tmp == p)
+                       break;
+               if (!br_promisc_port(tmp))
+                       dev_uc_del(tmp->dev, addr);
+       }
+}
+
+/* When a static FDB entry is deleted, the HW address from that entry is
+ * also removed from the bridge private HW address list and updates all
+ * the ports with needed information.
+ * Called under RTNL.
+ */
+static void fdb_del_hw(struct net_bridge *br, const unsigned char *addr)
+{
+       struct net_bridge_port *p;
+
+       ASSERT_RTNL();
+
+       list_for_each_entry(p, &br->port_list, list) {
+               if (!br_promisc_port(p))
+                       dev_uc_del(p->dev, addr);
+       }
+}
+
 static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
 {
+       if (f->is_static)
+               fdb_del_hw(br, f->addr.addr);
+
        hlist_del_rcu(&f->hlist);
        fdb_notify(br, f, RTM_DELNEIGH);
        call_rcu(&f->rcu, fdb_rcu_free);
@@ -466,6 +516,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
                return -ENOMEM;
 
        fdb->is_local = fdb->is_static = 1;
+       fdb_add_hw(br, addr);
        fdb_notify(br, fdb, RTM_NEWNEIGH);
        return 0;
 }
@@ -571,6 +622,8 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,
 
        if (nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->addr))
                goto nla_put_failure;
+       if (nla_put_u32(skb, NDA_MASTER, br->dev->ifindex))
+               goto nla_put_failure;
        ci.ndm_used      = jiffies_to_clock_t(now - fdb->used);
        ci.ndm_confirmed = 0;
        ci.ndm_updated   = jiffies_to_clock_t(now - fdb->updated);
@@ -592,6 +645,7 @@ static inline size_t fdb_nlmsg_size(void)
 {
        return NLMSG_ALIGN(sizeof(struct ndmsg))
                + nla_total_size(ETH_ALEN) /* NDA_LLADDR */
+               + nla_total_size(sizeof(u32)) /* NDA_MASTER */
                + nla_total_size(sizeof(u16)) /* NDA_VLAN */
                + nla_total_size(sizeof(struct nda_cacheinfo));
 }
@@ -684,13 +738,25 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
        }
 
        if (fdb_to_nud(fdb) != state) {
-               if (state & NUD_PERMANENT)
-                       fdb->is_local = fdb->is_static = 1;
-               else if (state & NUD_NOARP) {
+               if (state & NUD_PERMANENT) {
+                       fdb->is_local = 1;
+                       if (!fdb->is_static) {
+                               fdb->is_static = 1;
+                               fdb_add_hw(br, addr);
+                       }
+               } else if (state & NUD_NOARP) {
                        fdb->is_local = 0;
-                       fdb->is_static = 1;
-               } else
-                       fdb->is_local = fdb->is_static = 0;
+                       if (!fdb->is_static) {
+                               fdb->is_static = 1;
+                               fdb_add_hw(br, addr);
+                       }
+               } else {
+                       fdb->is_local = 0;
+                       if (fdb->is_static) {
+                               fdb->is_static = 0;
+                               fdb_del_hw(br, addr);
+                       }
+               }
 
                modified = true;
        }
@@ -880,3 +946,59 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
 out:
        return err;
 }
+
+int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p)
+{
+       struct net_bridge_fdb_entry *fdb, *tmp;
+       int i;
+       int err;
+
+       ASSERT_RTNL();
+
+       for (i = 0; i < BR_HASH_SIZE; i++) {
+               hlist_for_each_entry(fdb, &br->hash[i], hlist) {
+                       /* We only care for static entries */
+                       if (!fdb->is_static)
+                               continue;
+
+                       err = dev_uc_add(p->dev, fdb->addr.addr);
+                       if (err)
+                               goto rollback;
+               }
+       }
+       return 0;
+
+rollback:
+       for (i = 0; i < BR_HASH_SIZE; i++) {
+               hlist_for_each_entry(tmp, &br->hash[i], hlist) {
+                       /* If we reached the fdb that failed, we can stop */
+                       if (tmp == fdb)
+                               break;
+
+                       /* We only care for static entries */
+                       if (!tmp->is_static)
+                               continue;
+
+                       dev_uc_del(p->dev, tmp->addr.addr);
+               }
+       }
+       return err;
+}
+
+void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p)
+{
+       struct net_bridge_fdb_entry *fdb;
+       int i;
+
+       ASSERT_RTNL();
+
+       for (i = 0; i < BR_HASH_SIZE; i++) {
+               hlist_for_each_entry_rcu(fdb, &br->hash[i], hlist) {
+                       /* We only care for static entries */
+                       if (!fdb->is_static)
+                               continue;
+
+                       dev_uc_del(p->dev, fdb->addr.addr);
+               }
+       }
+}
index 5262b8617eb9cc21b1070e48d1c1efda584aef6f..3eca3fdf8fe1c7563150f735991e66380c4312ca 100644 (file)
@@ -85,6 +85,111 @@ void br_port_carrier_check(struct net_bridge_port *p)
        spin_unlock_bh(&br->lock);
 }
 
+static void br_port_set_promisc(struct net_bridge_port *p)
+{
+       int err = 0;
+
+       if (br_promisc_port(p))
+               return;
+
+       err = dev_set_promiscuity(p->dev, 1);
+       if (err)
+               return;
+
+       br_fdb_unsync_static(p->br, p);
+       p->flags |= BR_PROMISC;
+}
+
+static void br_port_clear_promisc(struct net_bridge_port *p)
+{
+       int err;
+
+       /* Check if the port is already non-promisc or if it doesn't
+        * support UNICAST filtering.  Without unicast filtering support
+        * we'll end up re-enabling promisc mode anyway, so just check for
+        * it here.
+        */
+       if (!br_promisc_port(p) || !(p->dev->priv_flags & IFF_UNICAST_FLT))
+               return;
+
+       /* Since we'll be clearing the promisc mode, program the port
+        * first so that we don't have interruption in traffic.
+        */
+       err = br_fdb_sync_static(p->br, p);
+       if (err)
+               return;
+
+       dev_set_promiscuity(p->dev, -1);
+       p->flags &= ~BR_PROMISC;
+}
+
+/* When a port is added or removed or when certain port flags
+ * change, this function is called to automatically manage
+ * promiscuity setting of all the bridge ports.  We are always called
+ * under RTNL so can skip using rcu primitives.
+ */
+void br_manage_promisc(struct net_bridge *br)
+{
+       struct net_bridge_port *p;
+       bool set_all = false;
+
+       /* If vlan filtering is disabled or bridge interface is placed
+        * into promiscuous mode, place all ports in promiscuous mode.
+        */
+       if ((br->dev->flags & IFF_PROMISC) || !br_vlan_enabled(br))
+               set_all = true;
+
+       list_for_each_entry(p, &br->port_list, list) {
+               if (set_all) {
+                       br_port_set_promisc(p);
+               } else {
+                       /* If the number of auto-ports is <= 1, then all other
+                        * ports will have their output configuration
+                        * statically specified through fdbs.  Since ingress
+                        * on the auto-port becomes forwarding/egress to other
+                        * ports and egress configuration is statically known,
+                        * we can say that ingress configuration of the
+                        * auto-port is also statically known.
+                        * This lets us disable promiscuous mode and write
+                        * this config to hw.
+                        */
+                       if (br->auto_cnt == 0 ||
+                           (br->auto_cnt == 1 && br_auto_port(p)))
+                               br_port_clear_promisc(p);
+                       else
+                               br_port_set_promisc(p);
+               }
+       }
+}
+
+static void nbp_update_port_count(struct net_bridge *br)
+{
+       struct net_bridge_port *p;
+       u32 cnt = 0;
+
+       list_for_each_entry(p, &br->port_list, list) {
+               if (br_auto_port(p))
+                       cnt++;
+       }
+       if (br->auto_cnt != cnt) {
+               br->auto_cnt = cnt;
+               br_manage_promisc(br);
+       }
+}
+
+static void nbp_delete_promisc(struct net_bridge_port *p)
+{
+       /* If port is currently promiscuous, unset promiscuity.
+        * Otherwise, it is a static port so remove all addresses
+        * from it.
+        */
+       dev_set_allmulti(p->dev, -1);
+       if (br_promisc_port(p))
+               dev_set_promiscuity(p->dev, -1);
+       else
+               br_fdb_unsync_static(p->br, p);
+}
+
 static void release_nbp(struct kobject *kobj)
 {
        struct net_bridge_port *p
@@ -133,7 +238,7 @@ static void del_nbp(struct net_bridge_port *p)
 
        sysfs_remove_link(br->ifobj, p->dev->name);
 
-       dev_set_promiscuity(dev, -1);
+       nbp_delete_promisc(p);
 
        spin_lock_bh(&br->lock);
        br_stp_disable_port(p);
@@ -141,10 +246,11 @@ static void del_nbp(struct net_bridge_port *p)
 
        br_ifinfo_notify(RTM_DELLINK, p);
 
+       list_del_rcu(&p->list);
+
        nbp_vlan_flush(p);
        br_fdb_delete_by_port(br, p, 1);
-
-       list_del_rcu(&p->list);
+       nbp_update_port_count(br);
 
        dev->priv_flags &= ~IFF_BRIDGE_PORT;
 
@@ -353,7 +459,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
 
        call_netdevice_notifiers(NETDEV_JOIN, dev);
 
-       err = dev_set_promiscuity(dev, 1);
+       err = dev_set_allmulti(dev, 1);
        if (err)
                goto put_back;
 
@@ -384,6 +490,8 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
 
        list_add_rcu(&p->list, &br->port_list);
 
+       nbp_update_port_count(br);
+
        netdev_update_features(br->dev);
 
        if (br->dev->needed_headroom < dev->needed_headroom)
@@ -421,7 +529,7 @@ err2:
        kobject_put(&p->kobj);
        p = NULL; /* kobject_put frees */
 err1:
-       dev_set_promiscuity(dev, -1);
+       dev_set_allmulti(dev, -1);
 put_back:
        dev_put(dev);
        kfree(p);
@@ -455,3 +563,11 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
 
        return 0;
 }
+
+void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
+{
+       struct net_bridge *br = p->br;
+
+       if (mask & BR_AUTO_MASK)
+               nbp_update_port_count(br);
+}
index 04d6348fd530f854769bf2e0220ba4b4a4489833..366c43649079d9bdef66063af2ab848965ca8197 100644 (file)
@@ -177,6 +177,8 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
        p = br_port_get_rcu(skb->dev);
 
        if (unlikely(is_link_local_ether_addr(dest))) {
+               u16 fwd_mask = p->br->group_fwd_mask_required;
+
                /*
                 * See IEEE 802.1D Table 7-10 Reserved addresses
                 *
@@ -194,7 +196,8 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
                case 0x00:      /* Bridge Group Address */
                        /* If STP is turned off,
                           then must forward to keep loop detection */
-                       if (p->br->stp_enabled == BR_NO_STP)
+                       if (p->br->stp_enabled == BR_NO_STP ||
+                           fwd_mask & (1u << dest[5]))
                                goto forward;
                        break;
 
@@ -203,7 +206,8 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
 
                default:
                        /* Allow selective forwarding for most other protocols */
-                       if (p->br->group_fwd_mask & (1u << dest[5]))
+                       fwd_mask |= p->br->group_fwd_mask;
+                       if (fwd_mask & (1u << dest[5]))
                                goto forward;
                }
 
index b7b1914dfa252a3731a26e2bcf2be3afc5171661..5df05269d17a6f9b478aa6d90d5c322f0708e0c4 100644 (file)
@@ -418,13 +418,13 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
 
        ip.proto = entry->addr.proto;
        if (ip.proto == htons(ETH_P_IP)) {
-               if (timer_pending(&br->ip4_querier.timer))
+               if (timer_pending(&br->ip4_other_query.timer))
                        return -EBUSY;
 
                ip.u.ip4 = entry->addr.u.ip4;
 #if IS_ENABLED(CONFIG_IPV6)
        } else {
-               if (timer_pending(&br->ip6_querier.timer))
+               if (timer_pending(&br->ip6_other_query.timer))
                        return -EBUSY;
 
                ip.u.ip6 = entry->addr.u.ip6;
index 7b757b5dc773fc2dcaedfa8f7c97e7884a622d89..abfa0b65a1118eb0abae4dd69f78e7285537acd8 100644 (file)
@@ -11,6 +11,7 @@
  */
 
 #include <linux/err.h>
+#include <linux/export.h>
 #include <linux/if_ether.h>
 #include <linux/igmp.h>
 #include <linux/jhash.h>
@@ -35,7 +36,7 @@
 #include "br_private.h"
 
 static void br_multicast_start_querier(struct net_bridge *br,
-                                      struct bridge_mcast_query *query);
+                                      struct bridge_mcast_own_query *query);
 unsigned int br_mdb_rehash_seq;
 
 static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b)
@@ -761,7 +762,7 @@ static void br_multicast_local_router_expired(unsigned long data)
 }
 
 static void br_multicast_querier_expired(struct net_bridge *br,
-                                        struct bridge_mcast_query *query)
+                                        struct bridge_mcast_own_query *query)
 {
        spin_lock(&br->multicast_lock);
        if (!netif_running(br->dev) || br->multicast_disabled)
@@ -777,7 +778,7 @@ static void br_ip4_multicast_querier_expired(unsigned long data)
 {
        struct net_bridge *br = (void *)data;
 
-       br_multicast_querier_expired(br, &br->ip4_query);
+       br_multicast_querier_expired(br, &br->ip4_own_query);
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
@@ -785,10 +786,22 @@ static void br_ip6_multicast_querier_expired(unsigned long data)
 {
        struct net_bridge *br = (void *)data;
 
-       br_multicast_querier_expired(br, &br->ip6_query);
+       br_multicast_querier_expired(br, &br->ip6_own_query);
 }
 #endif
 
+static void br_multicast_select_own_querier(struct net_bridge *br,
+                                           struct br_ip *ip,
+                                           struct sk_buff *skb)
+{
+       if (ip->proto == htons(ETH_P_IP))
+               br->ip4_querier.addr.u.ip4 = ip_hdr(skb)->saddr;
+#if IS_ENABLED(CONFIG_IPV6)
+       else
+               br->ip6_querier.addr.u.ip6 = ipv6_hdr(skb)->saddr;
+#endif
+}
+
 static void __br_multicast_send_query(struct net_bridge *br,
                                      struct net_bridge_port *port,
                                      struct br_ip *ip)
@@ -804,17 +817,19 @@ static void __br_multicast_send_query(struct net_bridge *br,
                skb->dev = port->dev;
                NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
                        dev_queue_xmit);
-       } else
+       } else {
+               br_multicast_select_own_querier(br, ip, skb);
                netif_rx(skb);
+       }
 }
 
 static void br_multicast_send_query(struct net_bridge *br,
                                    struct net_bridge_port *port,
-                                   struct bridge_mcast_query *query)
+                                   struct bridge_mcast_own_query *own_query)
 {
        unsigned long time;
        struct br_ip br_group;
-       struct bridge_mcast_querier *querier = NULL;
+       struct bridge_mcast_other_query *other_query = NULL;
 
        if (!netif_running(br->dev) || br->multicast_disabled ||
            !br->multicast_querier)
@@ -822,31 +837,32 @@ static void br_multicast_send_query(struct net_bridge *br,
 
        memset(&br_group.u, 0, sizeof(br_group.u));
 
-       if (port ? (query == &port->ip4_query) :
-                  (query == &br->ip4_query)) {
-               querier = &br->ip4_querier;
+       if (port ? (own_query == &port->ip4_own_query) :
+                  (own_query == &br->ip4_own_query)) {
+               other_query = &br->ip4_other_query;
                br_group.proto = htons(ETH_P_IP);
 #if IS_ENABLED(CONFIG_IPV6)
        } else {
-               querier = &br->ip6_querier;
+               other_query = &br->ip6_other_query;
                br_group.proto = htons(ETH_P_IPV6);
 #endif
        }
 
-       if (!querier || timer_pending(&querier->timer))
+       if (!other_query || timer_pending(&other_query->timer))
                return;
 
        __br_multicast_send_query(br, port, &br_group);
 
        time = jiffies;
-       time += query->startup_sent < br->multicast_startup_query_count ?
+       time += own_query->startup_sent < br->multicast_startup_query_count ?
                br->multicast_startup_query_interval :
                br->multicast_query_interval;
-       mod_timer(&query->timer, time);
+       mod_timer(&own_query->timer, time);
 }
 
-static void br_multicast_port_query_expired(struct net_bridge_port *port,
-                                           struct bridge_mcast_query *query)
+static void
+br_multicast_port_query_expired(struct net_bridge_port *port,
+                               struct bridge_mcast_own_query *query)
 {
        struct net_bridge *br = port->br;
 
@@ -868,7 +884,7 @@ static void br_ip4_multicast_port_query_expired(unsigned long data)
 {
        struct net_bridge_port *port = (void *)data;
 
-       br_multicast_port_query_expired(port, &port->ip4_query);
+       br_multicast_port_query_expired(port, &port->ip4_own_query);
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
@@ -876,7 +892,7 @@ static void br_ip6_multicast_port_query_expired(unsigned long data)
 {
        struct net_bridge_port *port = (void *)data;
 
-       br_multicast_port_query_expired(port, &port->ip6_query);
+       br_multicast_port_query_expired(port, &port->ip6_own_query);
 }
 #endif
 
@@ -886,11 +902,11 @@ void br_multicast_add_port(struct net_bridge_port *port)
 
        setup_timer(&port->multicast_router_timer, br_multicast_router_expired,
                    (unsigned long)port);
-       setup_timer(&port->ip4_query.timer, br_ip4_multicast_port_query_expired,
-                   (unsigned long)port);
+       setup_timer(&port->ip4_own_query.timer,
+                   br_ip4_multicast_port_query_expired, (unsigned long)port);
 #if IS_ENABLED(CONFIG_IPV6)
-       setup_timer(&port->ip6_query.timer, br_ip6_multicast_port_query_expired,
-                   (unsigned long)port);
+       setup_timer(&port->ip6_own_query.timer,
+                   br_ip6_multicast_port_query_expired, (unsigned long)port);
 #endif
 }
 
@@ -899,7 +915,7 @@ void br_multicast_del_port(struct net_bridge_port *port)
        del_timer_sync(&port->multicast_router_timer);
 }
 
-static void br_multicast_enable(struct bridge_mcast_query *query)
+static void br_multicast_enable(struct bridge_mcast_own_query *query)
 {
        query->startup_sent = 0;
 
@@ -916,9 +932,9 @@ void br_multicast_enable_port(struct net_bridge_port *port)
        if (br->multicast_disabled || !netif_running(br->dev))
                goto out;
 
-       br_multicast_enable(&port->ip4_query);
+       br_multicast_enable(&port->ip4_own_query);
 #if IS_ENABLED(CONFIG_IPV6)
-       br_multicast_enable(&port->ip6_query);
+       br_multicast_enable(&port->ip6_own_query);
 #endif
 
 out:
@@ -938,9 +954,9 @@ void br_multicast_disable_port(struct net_bridge_port *port)
        if (!hlist_unhashed(&port->rlist))
                hlist_del_init_rcu(&port->rlist);
        del_timer(&port->multicast_router_timer);
-       del_timer(&port->ip4_query.timer);
+       del_timer(&port->ip4_own_query.timer);
 #if IS_ENABLED(CONFIG_IPV6)
-       del_timer(&port->ip6_query.timer);
+       del_timer(&port->ip6_own_query.timer);
 #endif
        spin_unlock(&br->multicast_lock);
 }
@@ -1064,15 +1080,80 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
 }
 #endif
 
+static bool br_ip4_multicast_select_querier(struct net_bridge *br,
+                                           struct net_bridge_port *port,
+                                           __be32 saddr)
+{
+       if (!timer_pending(&br->ip4_own_query.timer) &&
+           !timer_pending(&br->ip4_other_query.timer))
+               goto update;
+
+       if (!br->ip4_querier.addr.u.ip4)
+               goto update;
+
+       if (ntohl(saddr) <= ntohl(br->ip4_querier.addr.u.ip4))
+               goto update;
+
+       return false;
+
+update:
+       br->ip4_querier.addr.u.ip4 = saddr;
+
+       /* update protected by general multicast_lock by caller */
+       rcu_assign_pointer(br->ip4_querier.port, port);
+
+       return true;
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static bool br_ip6_multicast_select_querier(struct net_bridge *br,
+                                           struct net_bridge_port *port,
+                                           struct in6_addr *saddr)
+{
+       if (!timer_pending(&br->ip6_own_query.timer) &&
+           !timer_pending(&br->ip6_other_query.timer))
+               goto update;
+
+       if (ipv6_addr_cmp(saddr, &br->ip6_querier.addr.u.ip6) <= 0)
+               goto update;
+
+       return false;
+
+update:
+       br->ip6_querier.addr.u.ip6 = *saddr;
+
+       /* update protected by general multicast_lock by caller */
+       rcu_assign_pointer(br->ip6_querier.port, port);
+
+       return true;
+}
+#endif
+
+static bool br_multicast_select_querier(struct net_bridge *br,
+                                       struct net_bridge_port *port,
+                                       struct br_ip *saddr)
+{
+       switch (saddr->proto) {
+       case htons(ETH_P_IP):
+               return br_ip4_multicast_select_querier(br, port, saddr->u.ip4);
+#if IS_ENABLED(CONFIG_IPV6)
+       case htons(ETH_P_IPV6):
+               return br_ip6_multicast_select_querier(br, port, &saddr->u.ip6);
+#endif
+       }
+
+       return false;
+}
+
 static void
-br_multicast_update_querier_timer(struct net_bridge *br,
-                                 struct bridge_mcast_querier *querier,
-                                 unsigned long max_delay)
+br_multicast_update_query_timer(struct net_bridge *br,
+                               struct bridge_mcast_other_query *query,
+                               unsigned long max_delay)
 {
-       if (!timer_pending(&querier->timer))
-               querier->delay_time = jiffies + max_delay;
+       if (!timer_pending(&query->timer))
+               query->delay_time = jiffies + max_delay;
 
-       mod_timer(&querier->timer, jiffies + br->multicast_querier_interval);
+       mod_timer(&query->timer, jiffies + br->multicast_querier_interval);
 }
 
 /*
@@ -1125,16 +1206,14 @@ timer:
 
 static void br_multicast_query_received(struct net_bridge *br,
                                        struct net_bridge_port *port,
-                                       struct bridge_mcast_querier *querier,
-                                       int saddr,
-                                       bool is_general_query,
+                                       struct bridge_mcast_other_query *query,
+                                       struct br_ip *saddr,
                                        unsigned long max_delay)
 {
-       if (saddr && is_general_query)
-               br_multicast_update_querier_timer(br, querier, max_delay);
-       else if (timer_pending(&querier->timer))
+       if (!br_multicast_select_querier(br, port, saddr))
                return;
 
+       br_multicast_update_query_timer(br, query, max_delay);
        br_multicast_mark_router(br, port);
 }
 
@@ -1149,6 +1228,7 @@ static int br_ip4_multicast_query(struct net_bridge *br,
        struct igmpv3_query *ih3;
        struct net_bridge_port_group *p;
        struct net_bridge_port_group __rcu **pp;
+       struct br_ip saddr;
        unsigned long max_delay;
        unsigned long now = jiffies;
        __be32 group;
@@ -1190,11 +1270,14 @@ static int br_ip4_multicast_query(struct net_bridge *br,
                goto out;
        }
 
-       br_multicast_query_received(br, port, &br->ip4_querier, !!iph->saddr,
-                                   !group, max_delay);
+       if (!group) {
+               saddr.proto = htons(ETH_P_IP);
+               saddr.u.ip4 = iph->saddr;
 
-       if (!group)
+               br_multicast_query_received(br, port, &br->ip4_other_query,
+                                           &saddr, max_delay);
                goto out;
+       }
 
        mp = br_mdb_ip4_get(mlock_dereference(br->mdb, br), group, vid);
        if (!mp)
@@ -1234,6 +1317,7 @@ static int br_ip6_multicast_query(struct net_bridge *br,
        struct mld2_query *mld2q;
        struct net_bridge_port_group *p;
        struct net_bridge_port_group __rcu **pp;
+       struct br_ip saddr;
        unsigned long max_delay;
        unsigned long now = jiffies;
        const struct in6_addr *group = NULL;
@@ -1282,12 +1366,16 @@ static int br_ip6_multicast_query(struct net_bridge *br,
                goto out;
        }
 
-       br_multicast_query_received(br, port, &br->ip6_querier,
-                                   !ipv6_addr_any(&ip6h->saddr),
-                                   is_general_query, max_delay);
+       if (is_general_query) {
+               saddr.proto = htons(ETH_P_IPV6);
+               saddr.u.ip6 = ip6h->saddr;
 
-       if (!group)
+               br_multicast_query_received(br, port, &br->ip6_other_query,
+                                           &saddr, max_delay);
+               goto out;
+       } else if (!group) {
                goto out;
+       }
 
        mp = br_mdb_ip6_get(mlock_dereference(br->mdb, br), group, vid);
        if (!mp)
@@ -1315,11 +1403,12 @@ out:
 }
 #endif
 
-static void br_multicast_leave_group(struct net_bridge *br,
-                                    struct net_bridge_port *port,
-                                    struct br_ip *group,
-                                    struct bridge_mcast_querier *querier,
-                                    struct bridge_mcast_query *query)
+static void
+br_multicast_leave_group(struct net_bridge *br,
+                        struct net_bridge_port *port,
+                        struct br_ip *group,
+                        struct bridge_mcast_other_query *other_query,
+                        struct bridge_mcast_own_query *own_query)
 {
        struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
@@ -1330,7 +1419,7 @@ static void br_multicast_leave_group(struct net_bridge *br,
        spin_lock(&br->multicast_lock);
        if (!netif_running(br->dev) ||
            (port && port->state == BR_STATE_DISABLED) ||
-           timer_pending(&querier->timer))
+           timer_pending(&other_query->timer))
                goto out;
 
        mdb = mlock_dereference(br->mdb, br);
@@ -1344,7 +1433,7 @@ static void br_multicast_leave_group(struct net_bridge *br,
                time = jiffies + br->multicast_last_member_count *
                                 br->multicast_last_member_interval;
 
-               mod_timer(&query->timer, time);
+               mod_timer(&own_query->timer, time);
 
                for (p = mlock_dereference(mp->ports, br);
                     p != NULL;
@@ -1425,17 +1514,19 @@ static void br_ip4_multicast_leave_group(struct net_bridge *br,
                                         __u16 vid)
 {
        struct br_ip br_group;
-       struct bridge_mcast_query *query = port ? &port->ip4_query :
-                                                 &br->ip4_query;
+       struct bridge_mcast_own_query *own_query;
 
        if (ipv4_is_local_multicast(group))
                return;
 
+       own_query = port ? &port->ip4_own_query : &br->ip4_own_query;
+
        br_group.u.ip4 = group;
        br_group.proto = htons(ETH_P_IP);
        br_group.vid = vid;
 
-       br_multicast_leave_group(br, port, &br_group, &br->ip4_querier, query);
+       br_multicast_leave_group(br, port, &br_group, &br->ip4_other_query,
+                                own_query);
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
@@ -1445,18 +1536,19 @@ static void br_ip6_multicast_leave_group(struct net_bridge *br,
                                         __u16 vid)
 {
        struct br_ip br_group;
-       struct bridge_mcast_query *query = port ? &port->ip6_query :
-                                                 &br->ip6_query;
-
+       struct bridge_mcast_own_query *own_query;
 
        if (ipv6_addr_is_ll_all_nodes(group))
                return;
 
+       own_query = port ? &port->ip6_own_query : &br->ip6_own_query;
+
        br_group.u.ip6 = *group;
        br_group.proto = htons(ETH_P_IPV6);
        br_group.vid = vid;
 
-       br_multicast_leave_group(br, port, &br_group, &br->ip6_querier, query);
+       br_multicast_leave_group(br, port, &br_group, &br->ip6_other_query,
+                                own_query);
 }
 #endif
 
@@ -1723,12 +1815,14 @@ int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port,
 }
 
 static void br_multicast_query_expired(struct net_bridge *br,
-                                      struct bridge_mcast_query *query)
+                                      struct bridge_mcast_own_query *query,
+                                      struct bridge_mcast_querier *querier)
 {
        spin_lock(&br->multicast_lock);
        if (query->startup_sent < br->multicast_startup_query_count)
                query->startup_sent++;
 
+       rcu_assign_pointer(querier, NULL);
        br_multicast_send_query(br, NULL, query);
        spin_unlock(&br->multicast_lock);
 }
@@ -1737,7 +1831,7 @@ static void br_ip4_multicast_query_expired(unsigned long data)
 {
        struct net_bridge *br = (void *)data;
 
-       br_multicast_query_expired(br, &br->ip4_query);
+       br_multicast_query_expired(br, &br->ip4_own_query, &br->ip4_querier);
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
@@ -1745,7 +1839,7 @@ static void br_ip6_multicast_query_expired(unsigned long data)
 {
        struct net_bridge *br = (void *)data;
 
-       br_multicast_query_expired(br, &br->ip6_query);
+       br_multicast_query_expired(br, &br->ip6_own_query, &br->ip6_querier);
 }
 #endif
 
@@ -1767,28 +1861,30 @@ void br_multicast_init(struct net_bridge *br)
        br->multicast_querier_interval = 255 * HZ;
        br->multicast_membership_interval = 260 * HZ;
 
-       br->ip4_querier.delay_time = 0;
+       br->ip4_other_query.delay_time = 0;
+       br->ip4_querier.port = NULL;
 #if IS_ENABLED(CONFIG_IPV6)
-       br->ip6_querier.delay_time = 0;
+       br->ip6_other_query.delay_time = 0;
+       br->ip6_querier.port = NULL;
 #endif
 
        spin_lock_init(&br->multicast_lock);
        setup_timer(&br->multicast_router_timer,
                    br_multicast_local_router_expired, 0);
-       setup_timer(&br->ip4_querier.timer, br_ip4_multicast_querier_expired,
-                   (unsigned long)br);
-       setup_timer(&br->ip4_query.timer, br_ip4_multicast_query_expired,
+       setup_timer(&br->ip4_other_query.timer,
+                   br_ip4_multicast_querier_expired, (unsigned long)br);
+       setup_timer(&br->ip4_own_query.timer, br_ip4_multicast_query_expired,
                    (unsigned long)br);
 #if IS_ENABLED(CONFIG_IPV6)
-       setup_timer(&br->ip6_querier.timer, br_ip6_multicast_querier_expired,
-                   (unsigned long)br);
-       setup_timer(&br->ip6_query.timer, br_ip6_multicast_query_expired,
+       setup_timer(&br->ip6_other_query.timer,
+                   br_ip6_multicast_querier_expired, (unsigned long)br);
+       setup_timer(&br->ip6_own_query.timer, br_ip6_multicast_query_expired,
                    (unsigned long)br);
 #endif
 }
 
 static void __br_multicast_open(struct net_bridge *br,
-                               struct bridge_mcast_query *query)
+                               struct bridge_mcast_own_query *query)
 {
        query->startup_sent = 0;
 
@@ -1800,9 +1896,9 @@ static void __br_multicast_open(struct net_bridge *br,
 
 void br_multicast_open(struct net_bridge *br)
 {
-       __br_multicast_open(br, &br->ip4_query);
+       __br_multicast_open(br, &br->ip4_own_query);
 #if IS_ENABLED(CONFIG_IPV6)
-       __br_multicast_open(br, &br->ip6_query);
+       __br_multicast_open(br, &br->ip6_own_query);
 #endif
 }
 
@@ -1815,11 +1911,11 @@ void br_multicast_stop(struct net_bridge *br)
        int i;
 
        del_timer_sync(&br->multicast_router_timer);
-       del_timer_sync(&br->ip4_querier.timer);
-       del_timer_sync(&br->ip4_query.timer);
+       del_timer_sync(&br->ip4_other_query.timer);
+       del_timer_sync(&br->ip4_own_query.timer);
 #if IS_ENABLED(CONFIG_IPV6)
-       del_timer_sync(&br->ip6_querier.timer);
-       del_timer_sync(&br->ip6_query.timer);
+       del_timer_sync(&br->ip6_other_query.timer);
+       del_timer_sync(&br->ip6_own_query.timer);
 #endif
 
        spin_lock_bh(&br->multicast_lock);
@@ -1923,7 +2019,7 @@ unlock:
 }
 
 static void br_multicast_start_querier(struct net_bridge *br,
-                                      struct bridge_mcast_query *query)
+                                      struct bridge_mcast_own_query *query)
 {
        struct net_bridge_port *port;
 
@@ -1934,11 +2030,11 @@ static void br_multicast_start_querier(struct net_bridge *br,
                    port->state == BR_STATE_BLOCKING)
                        continue;
 
-               if (query == &br->ip4_query)
-                       br_multicast_enable(&port->ip4_query);
+               if (query == &br->ip4_own_query)
+                       br_multicast_enable(&port->ip4_own_query);
 #if IS_ENABLED(CONFIG_IPV6)
                else
-                       br_multicast_enable(&port->ip6_query);
+                       br_multicast_enable(&port->ip6_own_query);
 #endif
        }
 }
@@ -1974,9 +2070,9 @@ rollback:
                        goto rollback;
        }
 
-       br_multicast_start_querier(br, &br->ip4_query);
+       br_multicast_start_querier(br, &br->ip4_own_query);
 #if IS_ENABLED(CONFIG_IPV6)
-       br_multicast_start_querier(br, &br->ip6_query);
+       br_multicast_start_querier(br, &br->ip6_own_query);
 #endif
 
 unlock:
@@ -2001,16 +2097,16 @@ int br_multicast_set_querier(struct net_bridge *br, unsigned long val)
 
        max_delay = br->multicast_query_response_interval;
 
-       if (!timer_pending(&br->ip4_querier.timer))
-               br->ip4_querier.delay_time = jiffies + max_delay;
+       if (!timer_pending(&br->ip4_other_query.timer))
+               br->ip4_other_query.delay_time = jiffies + max_delay;
 
-       br_multicast_start_querier(br, &br->ip4_query);
+       br_multicast_start_querier(br, &br->ip4_own_query);
 
 #if IS_ENABLED(CONFIG_IPV6)
-       if (!timer_pending(&br->ip6_querier.timer))
-               br->ip6_querier.delay_time = jiffies + max_delay;
+       if (!timer_pending(&br->ip6_other_query.timer))
+               br->ip6_other_query.delay_time = jiffies + max_delay;
 
-       br_multicast_start_querier(br, &br->ip6_query);
+       br_multicast_start_querier(br, &br->ip6_own_query);
 #endif
 
 unlock:
@@ -2061,3 +2157,109 @@ unlock:
 
        return err;
 }
+
+/**
+ * br_multicast_list_adjacent - Returns snooped multicast addresses
+ * @dev:       The bridge port adjacent to which to retrieve addresses
+ * @br_ip_list:        The list to store found, snooped multicast IP addresses in
+ *
+ * Creates a list of IP addresses (struct br_ip_list) sensed by the multicast
+ * snooping feature on all bridge ports of dev's bridge device, excluding
+ * the addresses from dev itself.
+ *
+ * Returns the number of items added to br_ip_list.
+ *
+ * Notes:
+ * - br_ip_list needs to be initialized by caller
+ * - br_ip_list might contain duplicates in the end
+ *   (needs to be taken care of by caller)
+ * - br_ip_list needs to be freed by caller
+ */
+int br_multicast_list_adjacent(struct net_device *dev,
+                              struct list_head *br_ip_list)
+{
+       struct net_bridge *br;
+       struct net_bridge_port *port;
+       struct net_bridge_port_group *group;
+       struct br_ip_list *entry;
+       int count = 0;
+
+       rcu_read_lock();
+       if (!br_ip_list || !br_port_exists(dev))
+               goto unlock;
+
+       port = br_port_get_rcu(dev);
+       if (!port || !port->br)
+               goto unlock;
+
+       br = port->br;
+
+       list_for_each_entry_rcu(port, &br->port_list, list) {
+               if (!port->dev || port->dev == dev)
+                       continue;
+
+               hlist_for_each_entry_rcu(group, &port->mglist, mglist) {
+                       entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+                       if (!entry)
+                               goto unlock;
+
+                       entry->addr = group->addr;
+                       list_add(&entry->list, br_ip_list);
+                       count++;
+               }
+       }
+
+unlock:
+       rcu_read_unlock();
+       return count;
+}
+EXPORT_SYMBOL_GPL(br_multicast_list_adjacent);
+
+/**
+ * br_multicast_has_querier_adjacent - Checks for a querier behind a bridge port
+ * @dev: The bridge port adjacent to which to check for a querier
+ * @proto: The protocol family to check for: IGMP -> ETH_P_IP, MLD -> ETH_P_IPV6
+ *
+ * Checks whether the given interface has a bridge on top and if so returns
+ * true if a selected querier is behind one of the other ports of this
+ * bridge. Otherwise returns false.
+ */
+bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto)
+{
+       struct net_bridge *br;
+       struct net_bridge_port *port;
+       bool ret = false;
+
+       rcu_read_lock();
+       if (!br_port_exists(dev))
+               goto unlock;
+
+       port = br_port_get_rcu(dev);
+       if (!port || !port->br)
+               goto unlock;
+
+       br = port->br;
+
+       switch (proto) {
+       case ETH_P_IP:
+               if (!timer_pending(&br->ip4_other_query.timer) ||
+                   rcu_dereference(br->ip4_querier.port) == port)
+                       goto unlock;
+               break;
+#if IS_ENABLED(CONFIG_IPV6)
+       case ETH_P_IPV6:
+               if (!timer_pending(&br->ip6_other_query.timer) ||
+                   rcu_dereference(br->ip6_querier.port) == port)
+                       goto unlock;
+               break;
+#endif
+       default:
+               goto unlock;
+       }
+
+       ret = true;
+unlock:
+       rcu_read_unlock();
+       return ret;
+}
+EXPORT_SYMBOL_GPL(br_multicast_has_querier_adjacent);
index 2acf7fa1fec6c2309123dc189ea964f66f230d07..a615264cf01a950aafd894109d43f55cfd8dff91 100644 (file)
@@ -535,7 +535,7 @@ static struct net_device *brnf_get_logical_dev(struct sk_buff *skb, const struct
        if (brnf_pass_vlan_indev == 0 || !vlan_tx_tag_present(skb))
                return br;
 
-       vlan = __vlan_find_dev_deep(br, skb->vlan_proto,
+       vlan = __vlan_find_dev_deep_rcu(br, skb->vlan_proto,
                                    vlan_tx_tag_get(skb) & VLAN_VID_MASK);
 
        return vlan ? vlan : br;
index e8844d975b321ac4e3f6a0cca76035918ec93a1b..26edb518b839b38240ab1de7cb619cd5ba924b6f 100644 (file)
@@ -328,6 +328,7 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
 static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
 {
        int err;
+       unsigned long old_flags = p->flags;
 
        br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
        br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
@@ -353,6 +354,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
                if (err)
                        return err;
        }
+
+       br_port_flags_change(p, old_flags ^ p->flags);
        return 0;
 }
 
diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c
deleted file mode 100644 (file)
index 2998dd1..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- *     Device event handling
- *     Linux ethernet bridge
- *
- *     Authors:
- *     Lennert Buytenhek               <buytenh@gnu.org>
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License
- *     as published by the Free Software Foundation; either version
- *     2 of the License, or (at your option) any later version.
- */
-
-#include <linux/kernel.h>
-#include <linux/rtnetlink.h>
-#include <net/net_namespace.h>
-
-#include "br_private.h"
-
-static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr);
-
-struct notifier_block br_device_notifier = {
-       .notifier_call = br_device_event
-};
-
-/*
- * Handle changes in state of network devices enslaved to a bridge.
- *
- * Note: don't care about up/down if bridge itself is down, because
- *     port state is checked when bridge is brought up.
- */
-static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
-{
-       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
-       struct net_bridge_port *p;
-       struct net_bridge *br;
-       bool changed_addr;
-       int err;
-
-       /* register of bridge completed, add sysfs entries */
-       if ((dev->priv_flags & IFF_EBRIDGE) && event == NETDEV_REGISTER) {
-               br_sysfs_addbr(dev);
-               return NOTIFY_DONE;
-       }
-
-       /* not a port of a bridge */
-       p = br_port_get_rtnl(dev);
-       if (!p)
-               return NOTIFY_DONE;
-
-       br = p->br;
-
-       switch (event) {
-       case NETDEV_CHANGEMTU:
-               dev_set_mtu(br->dev, br_min_mtu(br));
-               break;
-
-       case NETDEV_CHANGEADDR:
-               spin_lock_bh(&br->lock);
-               br_fdb_changeaddr(p, dev->dev_addr);
-               changed_addr = br_stp_recalculate_bridge_id(br);
-               spin_unlock_bh(&br->lock);
-
-               if (changed_addr)
-                       call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);
-
-               break;
-
-       case NETDEV_CHANGE:
-               br_port_carrier_check(p);
-               break;
-
-       case NETDEV_FEAT_CHANGE:
-               netdev_update_features(br->dev);
-               break;
-
-       case NETDEV_DOWN:
-               spin_lock_bh(&br->lock);
-               if (br->dev->flags & IFF_UP)
-                       br_stp_disable_port(p);
-               spin_unlock_bh(&br->lock);
-               break;
-
-       case NETDEV_UP:
-               if (netif_running(br->dev) && netif_oper_up(dev)) {
-                       spin_lock_bh(&br->lock);
-                       br_stp_enable_port(p);
-                       spin_unlock_bh(&br->lock);
-               }
-               break;
-
-       case NETDEV_UNREGISTER:
-               br_del_if(br, dev);
-               break;
-
-       case NETDEV_CHANGENAME:
-               err = br_sysfs_renameif(p);
-               if (err)
-                       return notifier_from_errno(err);
-               break;
-
-       case NETDEV_PRE_TYPE_CHANGE:
-               /* Forbid underlaying device to change its type. */
-               return NOTIFY_BAD;
-
-       case NETDEV_RESEND_IGMP:
-               /* Propagate to master device */
-               call_netdevice_notifiers(event, br->dev);
-               break;
-       }
-
-       /* Events that may cause spanning tree to refresh */
-       if (event == NETDEV_CHANGEADDR || event == NETDEV_UP ||
-           event == NETDEV_CHANGE || event == NETDEV_DOWN)
-               br_ifinfo_notify(RTM_NEWLINK, p);
-
-       return NOTIFY_DONE;
-}
index 59d3a85c58736b14d5fb43d214d4af1ee7e29954..23caf5b0309efe9e37b5d2b7bace5e1a9a75bf82 100644 (file)
@@ -35,6 +35,8 @@
 #define BR_GROUPFWD_DEFAULT    0
 /* Don't allow forwarding control protocols like STP and LLDP */
 #define BR_GROUPFWD_RESTRICTED 0x4007u
+/* The Nearest Customer Bridge Group Address, 01-80-C2-00-00-[00,0B,0C,0D,0F] */
+#define BR_GROUPFWD_8021AD     0xB801u
 
 /* Path to usermode spanning tree program */
 #define BR_STP_PROG    "/sbin/bridge-stp"
@@ -54,30 +56,24 @@ struct mac_addr
        unsigned char   addr[ETH_ALEN];
 };
 
-struct br_ip
-{
-       union {
-               __be32  ip4;
-#if IS_ENABLED(CONFIG_IPV6)
-               struct in6_addr ip6;
-#endif
-       } u;
-       __be16          proto;
-       __u16           vid;
-};
-
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
 /* our own querier */
-struct bridge_mcast_query {
+struct bridge_mcast_own_query {
        struct timer_list       timer;
        u32                     startup_sent;
 };
 
 /* other querier */
-struct bridge_mcast_querier {
+struct bridge_mcast_other_query {
        struct timer_list               timer;
        unsigned long                   delay_time;
 };
+
+/* selected querier */
+struct bridge_mcast_querier {
+       struct br_ip addr;
+       struct net_bridge_port __rcu    *port;
+};
 #endif
 
 struct net_port_vlans {
@@ -174,11 +170,13 @@ struct net_bridge_port
 #define BR_ADMIN_COST          0x00000010
 #define BR_LEARNING            0x00000020
 #define BR_FLOOD               0x00000040
+#define BR_AUTO_MASK (BR_FLOOD | BR_LEARNING)
+#define BR_PROMISC             0x00000080
 
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
-       struct bridge_mcast_query       ip4_query;
+       struct bridge_mcast_own_query   ip4_own_query;
 #if IS_ENABLED(CONFIG_IPV6)
-       struct bridge_mcast_query       ip6_query;
+       struct bridge_mcast_own_query   ip6_own_query;
 #endif /* IS_ENABLED(CONFIG_IPV6) */
        unsigned char                   multicast_router;
        struct timer_list               multicast_router_timer;
@@ -198,6 +196,9 @@ struct net_bridge_port
 #endif
 };
 
+#define br_auto_port(p) ((p)->flags & BR_AUTO_MASK)
+#define br_promisc_port(p) ((p)->flags & BR_PROMISC)
+
 #define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT)
 
 static inline struct net_bridge_port *br_port_get_rcu(const struct net_device *dev)
@@ -227,6 +228,7 @@ struct net_bridge
        bool                            nf_call_arptables;
 #endif
        u16                             group_fwd_mask;
+       u16                             group_fwd_mask_required;
 
        /* STP */
        bridge_id                       designated_root;
@@ -241,6 +243,7 @@ struct net_bridge
        unsigned long                   bridge_forward_delay;
 
        u8                              group_addr[ETH_ALEN];
+       bool                            group_addr_set;
        u16                             root_port;
 
        enum {
@@ -277,11 +280,13 @@ struct net_bridge
        struct hlist_head               router_list;
 
        struct timer_list               multicast_router_timer;
+       struct bridge_mcast_other_query ip4_other_query;
+       struct bridge_mcast_own_query   ip4_own_query;
        struct bridge_mcast_querier     ip4_querier;
-       struct bridge_mcast_query       ip4_query;
 #if IS_ENABLED(CONFIG_IPV6)
+       struct bridge_mcast_other_query ip6_other_query;
+       struct bridge_mcast_own_query   ip6_own_query;
        struct bridge_mcast_querier     ip6_querier;
-       struct bridge_mcast_query       ip6_query;
 #endif /* IS_ENABLED(CONFIG_IPV6) */
 #endif
 
@@ -290,8 +295,10 @@ struct net_bridge
        struct timer_list               topology_change_timer;
        struct timer_list               gc_timer;
        struct kobject                  *ifobj;
+       u32                             auto_cnt;
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
        u8                              vlan_enabled;
+       __be16                          vlan_proto;
        struct net_port_vlans __rcu     *vlan_info;
 #endif
 };
@@ -327,8 +334,6 @@ struct br_input_skb_cb {
 #define br_debug(br, format, args...)                  \
        pr_debug("%s: " format,  (br)->dev->name, ##args)
 
-extern struct notifier_block br_device_notifier;
-
 /* called under bridge lock */
 static inline int br_is_root_bridge(const struct net_bridge *br)
 {
@@ -395,6 +400,8 @@ int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
               const unsigned char *addr, u16 nlh_flags);
 int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
                struct net_device *dev, int idx);
+int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p);
+void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p);
 
 /* br_forward.c */
 void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
@@ -415,6 +422,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev);
 int br_min_mtu(const struct net_bridge *br);
 netdev_features_t br_features_recompute(struct net_bridge *br,
                                        netdev_features_t features);
+void br_port_flags_change(struct net_bridge_port *port, unsigned long mask);
+void br_manage_promisc(struct net_bridge *br);
 
 /* br_input.c */
 int br_handle_frame_finish(struct sk_buff *skb);
@@ -485,7 +494,7 @@ static inline bool br_multicast_is_router(struct net_bridge *br)
 
 static inline bool
 __br_multicast_querier_exists(struct net_bridge *br,
-                             struct bridge_mcast_querier *querier)
+                             struct bridge_mcast_other_query *querier)
 {
        return time_is_before_jiffies(querier->delay_time) &&
               (br->multicast_querier || timer_pending(&querier->timer));
@@ -496,10 +505,10 @@ static inline bool br_multicast_querier_exists(struct net_bridge *br,
 {
        switch (eth->h_proto) {
        case (htons(ETH_P_IP)):
-               return __br_multicast_querier_exists(br, &br->ip4_querier);
+               return __br_multicast_querier_exists(br, &br->ip4_other_query);
 #if IS_ENABLED(CONFIG_IPV6)
        case (htons(ETH_P_IPV6)):
-               return __br_multicast_querier_exists(br, &br->ip6_querier);
+               return __br_multicast_querier_exists(br, &br->ip6_other_query);
 #endif
        default:
                return false;
@@ -589,7 +598,10 @@ int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags);
 int br_vlan_delete(struct net_bridge *br, u16 vid);
 void br_vlan_flush(struct net_bridge *br);
 bool br_vlan_find(struct net_bridge *br, u16 vid);
+void br_recalculate_fwd_mask(struct net_bridge *br);
 int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
+int br_vlan_set_proto(struct net_bridge *br, unsigned long val);
+void br_vlan_init(struct net_bridge *br);
 int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags);
 int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
 void nbp_vlan_flush(struct net_bridge_port *port);
@@ -633,6 +645,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
        return v->pvid ?: VLAN_N_VID;
 }
 
+static inline int br_vlan_enabled(struct net_bridge *br)
+{
+       return br->vlan_enabled;
+}
 #else
 static inline bool br_allowed_ingress(struct net_bridge *br,
                                      struct net_port_vlans *v,
@@ -681,6 +697,14 @@ static inline bool br_vlan_find(struct net_bridge *br, u16 vid)
        return false;
 }
 
+static inline void br_recalculate_fwd_mask(struct net_bridge *br)
+{
+}
+
+static inline void br_vlan_init(struct net_bridge *br)
+{
+}
+
 static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)
 {
        return -EOPNOTSUPP;
@@ -719,6 +743,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
 {
        return VLAN_N_VID;      /* Returns invalid vid */
 }
+
+static inline int br_vlan_enabled(struct net_bridge *br)
+{
+       return 0;
+}
 #endif
 
 /* br_netfilter.c */
index 8dac65552f1901817489421c2042553a663cbaf9..c9e2572b15f400f9183606922a87f024ffe6570f 100644 (file)
@@ -312,10 +312,19 @@ static ssize_t group_addr_store(struct device *d,
            new_addr[5] == 3)           /* 802.1X PAE address */
                return -EINVAL;
 
+       if (!rtnl_trylock())
+               return restart_syscall();
+
        spin_lock_bh(&br->lock);
        for (i = 0; i < 6; i++)
                br->group_addr[i] = new_addr[i];
        spin_unlock_bh(&br->lock);
+
+       br->group_addr_set = true;
+       br_recalculate_fwd_mask(br);
+
+       rtnl_unlock();
+
        return len;
 }
 
@@ -700,6 +709,22 @@ static ssize_t vlan_filtering_store(struct device *d,
        return store_bridge_parm(d, buf, len, br_vlan_filter_toggle);
 }
 static DEVICE_ATTR_RW(vlan_filtering);
+
+static ssize_t vlan_protocol_show(struct device *d,
+                                 struct device_attribute *attr,
+                                 char *buf)
+{
+       struct net_bridge *br = to_bridge(d);
+       return sprintf(buf, "%#06x\n", ntohs(br->vlan_proto));
+}
+
+static ssize_t vlan_protocol_store(struct device *d,
+                                  struct device_attribute *attr,
+                                  const char *buf, size_t len)
+{
+       return store_bridge_parm(d, buf, len, br_vlan_set_proto);
+}
+static DEVICE_ATTR_RW(vlan_protocol);
 #endif
 
 static struct attribute *bridge_attrs[] = {
@@ -745,6 +770,7 @@ static struct attribute *bridge_attrs[] = {
 #endif
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
        &dev_attr_vlan_filtering.attr,
+       &dev_attr_vlan_protocol.attr,
 #endif
        NULL
 };
index dd595bd7fa820444f6e8c424eb7dd4f307998af2..e561cd59b8a6ef0e764b3028d350b13954deac05 100644 (file)
@@ -41,20 +41,30 @@ static ssize_t show_##_name(struct net_bridge_port *p, char *buf) \
 }                                                              \
 static int store_##_name(struct net_bridge_port *p, unsigned long v) \
 {                                                              \
-       unsigned long flags = p->flags;                         \
-       if (v)                                                  \
-               flags |= _mask;                                 \
-       else                                                    \
-               flags &= ~_mask;                                \
-       if (flags != p->flags) {                                \
-               p->flags = flags;                               \
-               br_ifinfo_notify(RTM_NEWLINK, p);               \
-       }                                                       \
-       return 0;                                               \
+       return store_flag(p, v, _mask);                         \
 }                                                              \
 static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,                   \
                   show_##_name, store_##_name)
 
+static int store_flag(struct net_bridge_port *p, unsigned long v,
+                     unsigned long mask)
+{
+       unsigned long flags;
+
+       flags = p->flags;
+
+       if (v)
+               flags |= mask;
+       else
+               flags &= ~mask;
+
+       if (flags != p->flags) {
+               p->flags = flags;
+               br_port_flags_change(p, mask);
+               br_ifinfo_notify(RTM_NEWLINK, p);
+       }
+       return 0;
+}
 
 static ssize_t show_path_cost(struct net_bridge_port *p, char *buf)
 {
index 5fee2feaf292fa76562a6e62ef2f29cb776d7b0c..2b2774fe0703871e7e4e65ee1d5816375c398e11 100644 (file)
@@ -60,7 +60,7 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
                 * that ever changes this code will allow tagged
                 * traffic to enter the bridge.
                 */
-               err = vlan_vid_add(dev, htons(ETH_P_8021Q), vid);
+               err = vlan_vid_add(dev, br->vlan_proto, vid);
                if (err)
                        return err;
        }
@@ -80,7 +80,7 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
 
 out_filt:
        if (p)
-               vlan_vid_del(dev, htons(ETH_P_8021Q), vid);
+               vlan_vid_del(dev, br->vlan_proto, vid);
        return err;
 }
 
@@ -92,8 +92,10 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid)
        __vlan_delete_pvid(v, vid);
        clear_bit(vid, v->untagged_bitmap);
 
-       if (v->port_idx)
-               vlan_vid_del(v->parent.port->dev, htons(ETH_P_8021Q), vid);
+       if (v->port_idx) {
+               struct net_bridge_port *p = v->parent.port;
+               vlan_vid_del(p->dev, p->br->vlan_proto, vid);
+       }
 
        clear_bit(vid, v->vlan_bitmap);
        v->num_vlans--;
@@ -158,7 +160,8 @@ out:
 bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
                        struct sk_buff *skb, u16 *vid)
 {
-       int err;
+       bool tagged;
+       __be16 proto;
 
        /* If VLAN filtering is disabled on the bridge, all packets are
         * permitted.
@@ -172,19 +175,41 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
        if (!v)
                goto drop;
 
+       proto = br->vlan_proto;
+
        /* If vlan tx offload is disabled on bridge device and frame was
         * sent from vlan device on the bridge device, it does not have
         * HW accelerated vlan tag.
         */
        if (unlikely(!vlan_tx_tag_present(skb) &&
-                    (skb->protocol == htons(ETH_P_8021Q) ||
-                     skb->protocol == htons(ETH_P_8021AD)))) {
+                    skb->protocol == proto)) {
                skb = vlan_untag(skb);
                if (unlikely(!skb))
                        return false;
        }
 
-       err = br_vlan_get_tag(skb, vid);
+       if (!br_vlan_get_tag(skb, vid)) {
+               /* Tagged frame */
+               if (skb->vlan_proto != proto) {
+                       /* Protocol-mismatch, empty out vlan_tci for new tag */
+                       skb_push(skb, ETH_HLEN);
+                       skb = __vlan_put_tag(skb, skb->vlan_proto,
+                                            vlan_tx_tag_get(skb));
+                       if (unlikely(!skb))
+                               return false;
+
+                       skb_pull(skb, ETH_HLEN);
+                       skb_reset_mac_len(skb);
+                       *vid = 0;
+                       tagged = false;
+               } else {
+                       tagged = true;
+               }
+       } else {
+               /* Untagged frame */
+               tagged = false;
+       }
+
        if (!*vid) {
                u16 pvid = br_get_pvid(v);
 
@@ -199,9 +224,9 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
                 * ingress frame is considered to belong to this vlan.
                 */
                *vid = pvid;
-               if (likely(err))
+               if (likely(!tagged))
                        /* Untagged Frame. */
-                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), pvid);
+                       __vlan_hwaccel_put_tag(skb, proto, pvid);
                else
                        /* Priority-tagged Frame.
                         * At this point, We know that skb->vlan_tci had
@@ -254,7 +279,9 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid)
        if (!v)
                return false;
 
-       br_vlan_get_tag(skb, vid);
+       if (!br_vlan_get_tag(skb, vid) && skb->vlan_proto != br->vlan_proto)
+               *vid = 0;
+
        if (!*vid) {
                *vid = br_get_pvid(v);
                if (*vid == VLAN_N_VID)
@@ -351,6 +378,33 @@ out:
        return found;
 }
 
+/* Must be protected by RTNL. */
+static void recalculate_group_addr(struct net_bridge *br)
+{
+       if (br->group_addr_set)
+               return;
+
+       spin_lock_bh(&br->lock);
+       if (!br->vlan_enabled || br->vlan_proto == htons(ETH_P_8021Q)) {
+               /* Bridge Group Address */
+               br->group_addr[5] = 0x00;
+       } else { /* vlan_enabled && ETH_P_8021AD */
+               /* Provider Bridge Group Address */
+               br->group_addr[5] = 0x08;
+       }
+       spin_unlock_bh(&br->lock);
+}
+
+/* Must be protected by RTNL. */
+void br_recalculate_fwd_mask(struct net_bridge *br)
+{
+       if (!br->vlan_enabled || br->vlan_proto == htons(ETH_P_8021Q))
+               br->group_fwd_mask_required = BR_GROUPFWD_DEFAULT;
+       else /* vlan_enabled && ETH_P_8021AD */
+               br->group_fwd_mask_required = BR_GROUPFWD_8021AD &
+                                             ~(1u << br->group_addr[5]);
+}
+
 int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
 {
        if (!rtnl_trylock())
@@ -360,12 +414,88 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
                goto unlock;
 
        br->vlan_enabled = val;
+       br_manage_promisc(br);
+       recalculate_group_addr(br);
+       br_recalculate_fwd_mask(br);
 
 unlock:
        rtnl_unlock();
        return 0;
 }
 
+int br_vlan_set_proto(struct net_bridge *br, unsigned long val)
+{
+       int err = 0;
+       struct net_bridge_port *p;
+       struct net_port_vlans *pv;
+       __be16 proto, oldproto;
+       u16 vid, errvid;
+
+       if (val != ETH_P_8021Q && val != ETH_P_8021AD)
+               return -EPROTONOSUPPORT;
+
+       if (!rtnl_trylock())
+               return restart_syscall();
+
+       proto = htons(val);
+       if (br->vlan_proto == proto)
+               goto unlock;
+
+       /* Add VLANs for the new proto to the device filter. */
+       list_for_each_entry(p, &br->port_list, list) {
+               pv = rtnl_dereference(p->vlan_info);
+               if (!pv)
+                       continue;
+
+               for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
+                       err = vlan_vid_add(p->dev, proto, vid);
+                       if (err)
+                               goto err_filt;
+               }
+       }
+
+       oldproto = br->vlan_proto;
+       br->vlan_proto = proto;
+
+       recalculate_group_addr(br);
+       br_recalculate_fwd_mask(br);
+
+       /* Delete VLANs for the old proto from the device filter. */
+       list_for_each_entry(p, &br->port_list, list) {
+               pv = rtnl_dereference(p->vlan_info);
+               if (!pv)
+                       continue;
+
+               for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID)
+                       vlan_vid_del(p->dev, oldproto, vid);
+       }
+
+unlock:
+       rtnl_unlock();
+       return err;
+
+err_filt:
+       errvid = vid;
+       for_each_set_bit(vid, pv->vlan_bitmap, errvid)
+               vlan_vid_del(p->dev, proto, vid);
+
+       list_for_each_entry_continue_reverse(p, &br->port_list, list) {
+               pv = rtnl_dereference(p->vlan_info);
+               if (!pv)
+                       continue;
+
+               for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID)
+                       vlan_vid_del(p->dev, proto, vid);
+       }
+
+       goto unlock;
+}
+
+void br_vlan_init(struct net_bridge *br)
+{
+       br->vlan_proto = htons(ETH_P_8021Q);
+}
+
 /* Must be protected by RTNL.
  * Must be called with vid in range from 1 to 4094 inclusive.
  */
@@ -432,7 +562,7 @@ void nbp_vlan_flush(struct net_bridge_port *port)
                return;
 
        for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID)
-               vlan_vid_del(port->dev, htons(ETH_P_8021Q), vid);
+               vlan_vid_del(port->dev, port->br->vlan_proto, vid);
 
        __vlan_flush(pv);
 }
index 5ca74a0e595fe5ce782985f6b2e17f185b5a52ec..629dc77874a9975feed56bb7b6abefad76cfad94 100644 (file)
@@ -2,14 +2,23 @@
 # Bridge netfilter configuration
 #
 #
-config NF_TABLES_BRIDGE
-       depends on NF_TABLES
+menuconfig NF_TABLES_BRIDGE
+       depends on BRIDGE && NETFILTER && NF_TABLES
        tristate "Ethernet Bridge nf_tables support"
 
+if NF_TABLES_BRIDGE
+
+config NFT_BRIDGE_META
+       tristate "Netfilter nf_table bridge meta support"
+       depends on NFT_META
+       help
+         Add support for bridge dedicated meta key.
+
+endif # NF_TABLES_BRIDGE
+
 menuconfig BRIDGE_NF_EBTABLES
        tristate "Ethernet Bridge tables (ebtables) support"
-       depends on BRIDGE && NETFILTER
-       select NETFILTER_XTABLES
+       depends on BRIDGE && NETFILTER && NETFILTER_XTABLES
        help
          ebtables is a general, extensible frame/packet identification
          framework. Say 'Y' or 'M' here if you want to do Ethernet
index ea7629f58b3d1c44e28524df8a0937de3a18546b..6f2f3943d66f34b43c72be21b603bbf51ba0d289 100644 (file)
@@ -3,6 +3,7 @@
 #
 
 obj-$(CONFIG_NF_TABLES_BRIDGE) += nf_tables_bridge.o
+obj-$(CONFIG_NFT_BRIDGE_META)  += nft_meta_bridge.o
 
 obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
 
diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c
new file mode 100644 (file)
index 0000000..4f02109
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2014 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nft_meta.h>
+
+#include "../br_private.h"
+
+static void nft_meta_bridge_get_eval(const struct nft_expr *expr,
+                                    struct nft_data data[NFT_REG_MAX + 1],
+                                    const struct nft_pktinfo *pkt)
+{
+       const struct nft_meta *priv = nft_expr_priv(expr);
+       const struct net_device *in = pkt->in, *out = pkt->out;
+       struct nft_data *dest = &data[priv->dreg];
+       const struct net_bridge_port *p;
+
+       switch (priv->key) {
+       case NFT_META_BRI_IIFNAME:
+               if (in == NULL || (p = br_port_get_rcu(in)) == NULL)
+                       goto err;
+               break;
+       case NFT_META_BRI_OIFNAME:
+               if (out == NULL || (p = br_port_get_rcu(out)) == NULL)
+                       goto err;
+               break;
+       default:
+               goto out;
+       }
+
+       strncpy((char *)dest->data, p->br->dev->name, sizeof(dest->data));
+       return;
+out:
+       return nft_meta_get_eval(expr, data, pkt);
+err:
+       data[NFT_REG_VERDICT].verdict = NFT_BREAK;
+}
+
+static int nft_meta_bridge_get_init(const struct nft_ctx *ctx,
+                                   const struct nft_expr *expr,
+                                   const struct nlattr * const tb[])
+{
+       struct nft_meta *priv = nft_expr_priv(expr);
+       int err;
+
+       priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
+       switch (priv->key) {
+       case NFT_META_BRI_IIFNAME:
+       case NFT_META_BRI_OIFNAME:
+               break;
+       default:
+               return nft_meta_get_init(ctx, expr, tb);
+       }
+
+       priv->dreg = ntohl(nla_get_be32(tb[NFTA_META_DREG]));
+       err = nft_validate_output_register(priv->dreg);
+       if (err < 0)
+               return err;
+
+       err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static struct nft_expr_type nft_meta_bridge_type;
+static const struct nft_expr_ops nft_meta_bridge_get_ops = {
+       .type           = &nft_meta_bridge_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
+       .eval           = nft_meta_bridge_get_eval,
+       .init           = nft_meta_bridge_get_init,
+       .dump           = nft_meta_get_dump,
+};
+
+static const struct nft_expr_ops nft_meta_bridge_set_ops = {
+       .type           = &nft_meta_bridge_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
+       .eval           = nft_meta_set_eval,
+       .init           = nft_meta_set_init,
+       .dump           = nft_meta_set_dump,
+};
+
+static const struct nft_expr_ops *
+nft_meta_bridge_select_ops(const struct nft_ctx *ctx,
+                          const struct nlattr * const tb[])
+{
+       if (tb[NFTA_META_KEY] == NULL)
+               return ERR_PTR(-EINVAL);
+
+       if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG])
+               return ERR_PTR(-EINVAL);
+
+       if (tb[NFTA_META_DREG])
+               return &nft_meta_bridge_get_ops;
+
+       if (tb[NFTA_META_SREG])
+               return &nft_meta_bridge_set_ops;
+
+       return ERR_PTR(-EINVAL);
+}
+
+static struct nft_expr_type nft_meta_bridge_type __read_mostly = {
+       .family         = NFPROTO_BRIDGE,
+       .name           = "meta",
+       .select_ops     = &nft_meta_bridge_select_ops,
+       .policy         = nft_meta_policy,
+       .maxattr        = NFTA_META_MAX,
+       .owner          = THIS_MODULE,
+};
+
+static int __init nft_meta_bridge_module_init(void)
+{
+       return nft_register_expr(&nft_meta_bridge_type);
+}
+
+static void __exit nft_meta_bridge_module_exit(void)
+{
+       nft_unregister_expr(&nft_meta_bridge_type);
+}
+
+module_init(nft_meta_bridge_module_init);
+module_exit(nft_meta_bridge_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>");
+MODULE_ALIAS_NFT_AF_EXPR(AF_BRIDGE, "meta");
index a27f8aad9e991f95cc5366bce3e975bff4f16bdd..ce82337521f665c5847819402d8a9c167452fb90 100644 (file)
@@ -337,6 +337,29 @@ static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
                return (struct dev_rcv_lists *)dev->ml_priv;
 }
 
+/**
+ * effhash - hash function for 29 bit CAN identifier reduction
+ * @can_id: 29 bit CAN identifier
+ *
+ * Description:
+ *  To reduce the linear traversal in one linked list of _single_ EFF CAN
+ *  frame subscriptions the 29 bit identifier is mapped to 10 bits.
+ *  (see CAN_EFF_RCV_HASH_BITS definition)
+ *
+ * Return:
+ *  Hash value from 0x000 - 0x3FF ( enforced by CAN_EFF_RCV_HASH_BITS mask )
+ */
+static unsigned int effhash(canid_t can_id)
+{
+       unsigned int hash;
+
+       hash = can_id;
+       hash ^= can_id >> CAN_EFF_RCV_HASH_BITS;
+       hash ^= can_id >> (2 * CAN_EFF_RCV_HASH_BITS);
+
+       return hash & ((1 << CAN_EFF_RCV_HASH_BITS) - 1);
+}
+
 /**
  * find_rcv_list - determine optimal filterlist inside device filter struct
  * @can_id: pointer to CAN identifier of a given can_filter
@@ -400,10 +423,8 @@ static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,
            !(*can_id & CAN_RTR_FLAG)) {
 
                if (*can_id & CAN_EFF_FLAG) {
-                       if (*mask == (CAN_EFF_MASK | CAN_EFF_RTR_FLAGS)) {
-                               /* RFC: a future use-case for hash-tables? */
-                               return &d->rx[RX_EFF];
-                       }
+                       if (*mask == (CAN_EFF_MASK | CAN_EFF_RTR_FLAGS))
+                               return &d->rx_eff[effhash(*can_id)];
                } else {
                        if (*mask == (CAN_SFF_MASK | CAN_EFF_RTR_FLAGS))
                                return &d->rx_sff[*can_id];
@@ -632,7 +653,7 @@ static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
                return matches;
 
        if (can_id & CAN_EFF_FLAG) {
-               hlist_for_each_entry_rcu(r, &d->rx[RX_EFF], list) {
+               hlist_for_each_entry_rcu(r, &d->rx_eff[effhash(can_id)], list) {
                        if (r->can_id == can_id) {
                                deliver(skb, r);
                                matches++;
index 6de58b40535cc309e59f1a61ed624c930b7c7ffe..fca0fe9fc45a497cdf3da82d5414e846e7cc61b7 100644 (file)
@@ -59,12 +59,17 @@ struct receiver {
        char *ident;
 };
 
-enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX };
+#define CAN_SFF_RCV_ARRAY_SZ (1 << CAN_SFF_ID_BITS)
+#define CAN_EFF_RCV_HASH_BITS 10
+#define CAN_EFF_RCV_ARRAY_SZ (1 << CAN_EFF_RCV_HASH_BITS)
+
+enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_MAX };
 
 /* per device receive filters linked at dev->ml_priv */
 struct dev_rcv_lists {
        struct hlist_head rx[RX_MAX];
-       struct hlist_head rx_sff[0x800];
+       struct hlist_head rx_sff[CAN_SFF_RCV_ARRAY_SZ];
+       struct hlist_head rx_eff[CAN_EFF_RCV_ARRAY_SZ];
        int remove_on_zero_entries;
        int entries;
 };
index b543470c8f8b5ef7e5196b7f895cf30a9b0db90e..1a19b985a8685b0aff4450acda4a8d4f429568bc 100644 (file)
@@ -80,7 +80,6 @@ static const char rx_list_name[][8] = {
        [RX_ALL] = "rx_all",
        [RX_FIL] = "rx_fil",
        [RX_INV] = "rx_inv",
-       [RX_EFF] = "rx_eff",
 };
 
 /*
@@ -389,25 +388,26 @@ static const struct file_operations can_rcvlist_proc_fops = {
        .release        = single_release,
 };
 
-static inline void can_rcvlist_sff_proc_show_one(struct seq_file *m,
-                                                struct net_device *dev,
-                                                struct dev_rcv_lists *d)
+static inline void can_rcvlist_proc_show_array(struct seq_file *m,
+                                              struct net_device *dev,
+                                              struct hlist_head *rcv_array,
+                                              unsigned int rcv_array_sz)
 {
-       int i;
+       unsigned int i;
        int all_empty = 1;
 
        /* check whether at least one list is non-empty */
-       for (i = 0; i < 0x800; i++)
-               if (!hlist_empty(&d->rx_sff[i])) {
+       for (i = 0; i < rcv_array_sz; i++)
+               if (!hlist_empty(&rcv_array[i])) {
                        all_empty = 0;
                        break;
                }
 
        if (!all_empty) {
                can_print_recv_banner(m);
-               for (i = 0; i < 0x800; i++) {
-                       if (!hlist_empty(&d->rx_sff[i]))
-                               can_print_rcvlist(m, &d->rx_sff[i], dev);
+               for (i = 0; i < rcv_array_sz; i++) {
+                       if (!hlist_empty(&rcv_array[i]))
+                               can_print_rcvlist(m, &rcv_array[i], dev);
                }
        } else
                seq_printf(m, "  (%s: no entry)\n", DNAME(dev));
@@ -425,12 +425,15 @@ static int can_rcvlist_sff_proc_show(struct seq_file *m, void *v)
 
        /* sff receive list for 'all' CAN devices (dev == NULL) */
        d = &can_rx_alldev_list;
-       can_rcvlist_sff_proc_show_one(m, NULL, d);
+       can_rcvlist_proc_show_array(m, NULL, d->rx_sff, ARRAY_SIZE(d->rx_sff));
 
        /* sff receive list for registered CAN devices */
        for_each_netdev_rcu(&init_net, dev) {
-               if (dev->type == ARPHRD_CAN && dev->ml_priv)
-                       can_rcvlist_sff_proc_show_one(m, dev, dev->ml_priv);
+               if (dev->type == ARPHRD_CAN && dev->ml_priv) {
+                       d = dev->ml_priv;
+                       can_rcvlist_proc_show_array(m, dev, d->rx_sff,
+                                                   ARRAY_SIZE(d->rx_sff));
+               }
        }
 
        rcu_read_unlock();
@@ -452,6 +455,49 @@ static const struct file_operations can_rcvlist_sff_proc_fops = {
        .release        = single_release,
 };
 
+
+static int can_rcvlist_eff_proc_show(struct seq_file *m, void *v)
+{
+       struct net_device *dev;
+       struct dev_rcv_lists *d;
+
+       /* RX_EFF */
+       seq_puts(m, "\nreceive list 'rx_eff':\n");
+
+       rcu_read_lock();
+
+       /* eff receive list for 'all' CAN devices (dev == NULL) */
+       d = &can_rx_alldev_list;
+       can_rcvlist_proc_show_array(m, NULL, d->rx_eff, ARRAY_SIZE(d->rx_eff));
+
+       /* eff receive list for registered CAN devices */
+       for_each_netdev_rcu(&init_net, dev) {
+               if (dev->type == ARPHRD_CAN && dev->ml_priv) {
+                       d = dev->ml_priv;
+                       can_rcvlist_proc_show_array(m, dev, d->rx_eff,
+                                                   ARRAY_SIZE(d->rx_eff));
+               }
+       }
+
+       rcu_read_unlock();
+
+       seq_putc(m, '\n');
+       return 0;
+}
+
+static int can_rcvlist_eff_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, can_rcvlist_eff_proc_show, NULL);
+}
+
+static const struct file_operations can_rcvlist_eff_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = can_rcvlist_eff_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
 /*
  * proc utility functions
  */
@@ -491,8 +537,8 @@ void can_init_proc(void)
                                           &can_rcvlist_proc_fops, (void *)RX_FIL);
        pde_rcvlist_inv = proc_create_data(CAN_PROC_RCVLIST_INV, 0644, can_dir,
                                           &can_rcvlist_proc_fops, (void *)RX_INV);
-       pde_rcvlist_eff = proc_create_data(CAN_PROC_RCVLIST_EFF, 0644, can_dir,
-                                          &can_rcvlist_proc_fops, (void *)RX_EFF);
+       pde_rcvlist_eff = proc_create(CAN_PROC_RCVLIST_EFF, 0644, can_dir,
+                                     &can_rcvlist_eff_proc_fops);
        pde_rcvlist_sff = proc_create(CAN_PROC_RCVLIST_SFF, 0644, can_dir,
                                      &can_rcvlist_sff_proc_fops);
 }
index 67d7721d237e1638c21bc62da98f7275a9991026..1675021d8c12cf6e941720802553f942171eb79b 100644 (file)
@@ -72,6 +72,8 @@ const char *ceph_msg_type_name(int type)
        case CEPH_MSG_MON_SUBSCRIBE_ACK: return "mon_subscribe_ack";
        case CEPH_MSG_STATFS: return "statfs";
        case CEPH_MSG_STATFS_REPLY: return "statfs_reply";
+       case CEPH_MSG_MON_GET_VERSION: return "mon_get_version";
+       case CEPH_MSG_MON_GET_VERSION_REPLY: return "mon_get_version_reply";
        case CEPH_MSG_MDS_MAP: return "mds_map";
        case CEPH_MSG_CLIENT_SESSION: return "client_session";
        case CEPH_MSG_CLIENT_RECONNECT: return "client_reconnect";
index 10421a4b76f8710def7328e45dabf227cc3cdb3b..d1a62c69a9f4fd1cff25ca458a66d8e2c6093099 100644 (file)
@@ -126,9 +126,13 @@ static int monc_show(struct seq_file *s, void *p)
                req = rb_entry(rp, struct ceph_mon_generic_request, node);
                op = le16_to_cpu(req->request->hdr.type);
                if (op == CEPH_MSG_STATFS)
-                       seq_printf(s, "%lld statfs\n", req->tid);
+                       seq_printf(s, "%llu statfs\n", req->tid);
+               else if (op == CEPH_MSG_POOLOP)
+                       seq_printf(s, "%llu poolop\n", req->tid);
+               else if (op == CEPH_MSG_MON_GET_VERSION)
+                       seq_printf(s, "%llu mon_get_version", req->tid);
                else
-                       seq_printf(s, "%lld unknown\n", req->tid);
+                       seq_printf(s, "%llu unknown\n", req->tid);
        }
 
        mutex_unlock(&monc->mutex);
index 2ac9ef35110b3e9ea07363d6f2d3c0eb2e4ee27b..067d3af2eaf61be41d601dc4324fc1a1565ab27d 100644 (file)
@@ -296,6 +296,33 @@ void ceph_monc_request_next_osdmap(struct ceph_mon_client *monc)
                __send_subscribe(monc);
        mutex_unlock(&monc->mutex);
 }
+EXPORT_SYMBOL(ceph_monc_request_next_osdmap);
+
+int ceph_monc_wait_osdmap(struct ceph_mon_client *monc, u32 epoch,
+                         unsigned long timeout)
+{
+       unsigned long started = jiffies;
+       int ret;
+
+       mutex_lock(&monc->mutex);
+       while (monc->have_osdmap < epoch) {
+               mutex_unlock(&monc->mutex);
+
+               if (timeout != 0 && time_after_eq(jiffies, started + timeout))
+                       return -ETIMEDOUT;
+
+               ret = wait_event_interruptible_timeout(monc->client->auth_wq,
+                                        monc->have_osdmap >= epoch, timeout);
+               if (ret < 0)
+                       return ret;
+
+               mutex_lock(&monc->mutex);
+       }
+
+       mutex_unlock(&monc->mutex);
+       return 0;
+}
+EXPORT_SYMBOL(ceph_monc_wait_osdmap);
 
 /*
  *
@@ -477,14 +504,13 @@ static struct ceph_msg *get_generic_reply(struct ceph_connection *con,
        return m;
 }
 
-static int do_generic_request(struct ceph_mon_client *monc,
-                             struct ceph_mon_generic_request *req)
+static int __do_generic_request(struct ceph_mon_client *monc, u64 tid,
+                               struct ceph_mon_generic_request *req)
 {
        int err;
 
        /* register request */
-       mutex_lock(&monc->mutex);
-       req->tid = ++monc->last_tid;
+       req->tid = tid != 0 ? tid : ++monc->last_tid;
        req->request->hdr.tid = cpu_to_le64(req->tid);
        __insert_generic_request(monc, req);
        monc->num_generic_requests++;
@@ -496,13 +522,24 @@ static int do_generic_request(struct ceph_mon_client *monc,
        mutex_lock(&monc->mutex);
        rb_erase(&req->node, &monc->generic_request_tree);
        monc->num_generic_requests--;
-       mutex_unlock(&monc->mutex);
 
        if (!err)
                err = req->result;
        return err;
 }
 
+static int do_generic_request(struct ceph_mon_client *monc,
+                             struct ceph_mon_generic_request *req)
+{
+       int err;
+
+       mutex_lock(&monc->mutex);
+       err = __do_generic_request(monc, 0, req);
+       mutex_unlock(&monc->mutex);
+
+       return err;
+}
+
 /*
  * statfs
  */
@@ -579,6 +616,96 @@ out:
 }
 EXPORT_SYMBOL(ceph_monc_do_statfs);
 
+static void handle_get_version_reply(struct ceph_mon_client *monc,
+                                    struct ceph_msg *msg)
+{
+       struct ceph_mon_generic_request *req;
+       u64 tid = le64_to_cpu(msg->hdr.tid);
+       void *p = msg->front.iov_base;
+       void *end = p + msg->front_alloc_len;
+       u64 handle;
+
+       dout("%s %p tid %llu\n", __func__, msg, tid);
+
+       ceph_decode_need(&p, end, 2*sizeof(u64), bad);
+       handle = ceph_decode_64(&p);
+       if (tid != 0 && tid != handle)
+               goto bad;
+
+       mutex_lock(&monc->mutex);
+       req = __lookup_generic_req(monc, handle);
+       if (req) {
+               *(u64 *)req->buf = ceph_decode_64(&p);
+               req->result = 0;
+               get_generic_request(req);
+       }
+       mutex_unlock(&monc->mutex);
+       if (req) {
+               complete_all(&req->completion);
+               put_generic_request(req);
+       }
+
+       return;
+bad:
+       pr_err("corrupt mon_get_version reply\n");
+       ceph_msg_dump(msg);
+}
+
+/*
+ * Send MMonGetVersion and wait for the reply.
+ *
+ * @what: one of "mdsmap", "osdmap" or "monmap"
+ */
+int ceph_monc_do_get_version(struct ceph_mon_client *monc, const char *what,
+                            u64 *newest)
+{
+       struct ceph_mon_generic_request *req;
+       void *p, *end;
+       u64 tid;
+       int err;
+
+       req = kzalloc(sizeof(*req), GFP_NOFS);
+       if (!req)
+               return -ENOMEM;
+
+       kref_init(&req->kref);
+       req->buf = newest;
+       req->buf_len = sizeof(*newest);
+       init_completion(&req->completion);
+
+       req->request = ceph_msg_new(CEPH_MSG_MON_GET_VERSION,
+                                   sizeof(u64) + sizeof(u32) + strlen(what),
+                                   GFP_NOFS, true);
+       if (!req->request) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       req->reply = ceph_msg_new(CEPH_MSG_MON_GET_VERSION_REPLY, 1024,
+                                 GFP_NOFS, true);
+       if (!req->reply) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       p = req->request->front.iov_base;
+       end = p + req->request->front_alloc_len;
+
+       /* fill out request */
+       mutex_lock(&monc->mutex);
+       tid = ++monc->last_tid;
+       ceph_encode_64(&p, tid); /* handle */
+       ceph_encode_string(&p, end, what, strlen(what));
+
+       err = __do_generic_request(monc, tid, req);
+
+       mutex_unlock(&monc->mutex);
+out:
+       kref_put(&req->kref, release_generic_request);
+       return err;
+}
+EXPORT_SYMBOL(ceph_monc_do_get_version);
+
 /*
  * pool ops
  */
@@ -981,6 +1108,10 @@ static void dispatch(struct ceph_connection *con, struct ceph_msg *msg)
                handle_statfs_reply(monc, msg);
                break;
 
+       case CEPH_MSG_MON_GET_VERSION_REPLY:
+               handle_get_version_reply(monc, msg);
+               break;
+
        case CEPH_MSG_POOLOP_REPLY:
                handle_poolop_reply(monc, msg);
                break;
@@ -1029,6 +1160,15 @@ static struct ceph_msg *mon_alloc_msg(struct ceph_connection *con,
        case CEPH_MSG_AUTH_REPLY:
                m = ceph_msg_get(monc->m_auth_reply);
                break;
+       case CEPH_MSG_MON_GET_VERSION_REPLY:
+               if (le64_to_cpu(hdr->tid) != 0)
+                       return get_generic_reply(con, hdr, skip);
+
+               /*
+                * Older OSDs don't set reply tid even if the orignal
+                * request had a non-zero tid.  Workaround this weirdness
+                * by falling through to the allocate case.
+                */
        case CEPH_MSG_MON_MAP:
        case CEPH_MSG_MDS_MAP:
        case CEPH_MSG_OSD_MAP:
index b0dfce77656a0ba9c43d6d6616be1d28fb4dc137..05be0c1816958b0d0db6b2c319631d41f273e3d0 100644 (file)
@@ -2491,7 +2491,7 @@ EXPORT_SYMBOL(ceph_osdc_sync);
  * Call all pending notify callbacks - for use after a watch is
  * unregistered, to make sure no more callbacks for it will be invoked
  */
-extern void ceph_osdc_flush_notifies(struct ceph_osd_client *osdc)
+void ceph_osdc_flush_notifies(struct ceph_osd_client *osdc)
 {
        flush_workqueue(osdc->notify_wq);
 }
index 815a2249cfa9371f1f9505c7016b509b83d50100..555013034f7a899e2d03268a6571e01d096e8cee 100644 (file)
@@ -53,7 +53,10 @@ void ceph_put_page_vector(struct page **pages, int num_pages, bool dirty)
                        set_page_dirty_lock(pages[i]);
                put_page(pages[i]);
        }
-       kfree(pages);
+       if (is_vmalloc_addr(pages))
+               vfree(pages);
+       else
+               kfree(pages);
 }
 EXPORT_SYMBOL(ceph_put_page_vector);
 
@@ -164,36 +167,6 @@ void ceph_copy_from_page_vector(struct page **pages,
 }
 EXPORT_SYMBOL(ceph_copy_from_page_vector);
 
-/*
- * copy user data from a page vector into a user pointer
- */
-int ceph_copy_page_vector_to_user(struct page **pages,
-                                        void __user *data,
-                                        loff_t off, size_t len)
-{
-       int i = 0;
-       int po = off & ~PAGE_CACHE_MASK;
-       int left = len;
-       int l, bad;
-
-       while (left > 0) {
-               l = min_t(int, left, PAGE_CACHE_SIZE-po);
-               bad = copy_to_user(data, page_address(pages[i]) + po, l);
-               if (bad == l)
-                       return -EFAULT;
-               data += l - bad;
-               left -= l - bad;
-               if (po) {
-                       po += l - bad;
-                       if (po == PAGE_CACHE_SIZE)
-                               po = 0;
-               }
-               i++;
-       }
-       return len;
-}
-EXPORT_SYMBOL(ceph_copy_page_vector_to_user);
-
 /*
  * Zero an extent within a page vector.  Offset is relative to the
  * start of the first page.
index 826b925aa4530a0de280b7b01442beb191cf8b5c..71093d94ad2bb22e01e09676c482abd3d8d37ab5 100644 (file)
@@ -9,7 +9,7 @@ obj-$(CONFIG_SYSCTL) += sysctl_net_core.o
 
 obj-y               += dev.o ethtool.o dev_addr_lists.o dst.o netevent.o \
                        neighbour.o rtnetlink.o utils.o link_watch.o filter.o \
-                       sock_diag.o dev_ioctl.o
+                       sock_diag.o dev_ioctl.o tso.o
 
 obj-$(CONFIG_XFRM) += flow.o
 obj-y += net-sysfs.o
index a16ed7bbe37689007e1fabed938076619c65a2e2..488dd1a825c05b704a26d8c88582454f1745be4c 100644 (file)
@@ -740,17 +740,37 @@ __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len)
 
        sum = csum_fold(skb_checksum(skb, 0, len, skb->csum));
        if (likely(!sum)) {
-               if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE))
+               if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
+                   !skb->csum_complete_sw)
                        netdev_rx_csum_fault(skb->dev);
-               skb->ip_summed = CHECKSUM_UNNECESSARY;
        }
+       skb->csum_valid = !sum;
        return sum;
 }
 EXPORT_SYMBOL(__skb_checksum_complete_head);
 
 __sum16 __skb_checksum_complete(struct sk_buff *skb)
 {
-       return __skb_checksum_complete_head(skb, skb->len);
+       __wsum csum;
+       __sum16 sum;
+
+       csum = skb_checksum(skb, 0, skb->len, 0);
+
+       /* skb->csum holds pseudo checksum */
+       sum = csum_fold(csum_add(skb->csum, csum));
+       if (likely(!sum)) {
+               if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
+                   !skb->csum_complete_sw)
+                       netdev_rx_csum_fault(skb->dev);
+       }
+
+       /* Save full packet checksum */
+       skb->csum = csum;
+       skb->ip_summed = CHECKSUM_COMPLETE;
+       skb->csum_complete_sw = 1;
+       skb->csum_valid = !sum;
+
+       return sum;
 }
 EXPORT_SYMBOL(__skb_checksum_complete);
 
index 8908a68db44921c91a4f359b90e575c4a7190468..30eedf6779138d77ae9c54ca5efa8bf57587e7ac 100644 (file)
@@ -1661,6 +1661,29 @@ bool is_skb_forwardable(struct net_device *dev, struct sk_buff *skb)
 }
 EXPORT_SYMBOL_GPL(is_skb_forwardable);
 
+int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb)
+{
+       if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) {
+               if (skb_copy_ubufs(skb, GFP_ATOMIC)) {
+                       atomic_long_inc(&dev->rx_dropped);
+                       kfree_skb(skb);
+                       return NET_RX_DROP;
+               }
+       }
+
+       if (unlikely(!is_skb_forwardable(dev, skb))) {
+               atomic_long_inc(&dev->rx_dropped);
+               kfree_skb(skb);
+               return NET_RX_DROP;
+       }
+
+       skb_scrub_packet(skb, true);
+       skb->protocol = eth_type_trans(skb, dev);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(__dev_forward_skb);
+
 /**
  * dev_forward_skb - loopback an skb to another netif
  *
@@ -1681,24 +1704,7 @@ EXPORT_SYMBOL_GPL(is_skb_forwardable);
  */
 int dev_forward_skb(struct net_device *dev, struct sk_buff *skb)
 {
-       if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) {
-               if (skb_copy_ubufs(skb, GFP_ATOMIC)) {
-                       atomic_long_inc(&dev->rx_dropped);
-                       kfree_skb(skb);
-                       return NET_RX_DROP;
-               }
-       }
-
-       if (unlikely(!is_skb_forwardable(dev, skb))) {
-               atomic_long_inc(&dev->rx_dropped);
-               kfree_skb(skb);
-               return NET_RX_DROP;
-       }
-
-       skb_scrub_packet(skb, true);
-       skb->protocol = eth_type_trans(skb, dev);
-
-       return netif_rx_internal(skb);
+       return __dev_forward_skb(dev, skb) ?: netif_rx_internal(skb);
 }
 EXPORT_SYMBOL_GPL(dev_forward_skb);
 
@@ -2507,13 +2513,39 @@ static int dev_gso_segment(struct sk_buff *skb, netdev_features_t features)
        return 0;
 }
 
+/* If MPLS offload request, verify we are testing hardware MPLS features
+ * instead of standard features for the netdev.
+ */
+#ifdef CONFIG_NET_MPLS_GSO
+static netdev_features_t net_mpls_features(struct sk_buff *skb,
+                                          netdev_features_t features,
+                                          __be16 type)
+{
+       if (type == htons(ETH_P_MPLS_UC) || type == htons(ETH_P_MPLS_MC))
+               features &= skb->dev->mpls_features;
+
+       return features;
+}
+#else
+static netdev_features_t net_mpls_features(struct sk_buff *skb,
+                                          netdev_features_t features,
+                                          __be16 type)
+{
+       return features;
+}
+#endif
+
 static netdev_features_t harmonize_features(struct sk_buff *skb,
        netdev_features_t features)
 {
        int tmp;
+       __be16 type;
+
+       type = skb_network_protocol(skb, &tmp);
+       features = net_mpls_features(skb, features, type);
 
        if (skb->ip_summed != CHECKSUM_NONE &&
-           !can_checksum_protocol(features, skb_network_protocol(skb, &tmp))) {
+           !can_checksum_protocol(features, type)) {
                features &= ~NETIF_F_ALL_CSUM;
        } else if (illegal_highdma(skb->dev, skb)) {
                features &= ~NETIF_F_SG;
@@ -5689,10 +5721,6 @@ static void rollback_registered_many(struct list_head *head)
                */
                call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
 
-               if (!dev->rtnl_link_ops ||
-                   dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
-                       rtmsg_ifinfo(RTM_DELLINK, dev, ~0U, GFP_KERNEL);
-
                /*
                 *      Flush the unicast and multicast chains
                 */
@@ -5702,6 +5730,10 @@ static void rollback_registered_many(struct list_head *head)
                if (dev->netdev_ops->ndo_uninit)
                        dev->netdev_ops->ndo_uninit(dev);
 
+               if (!dev->rtnl_link_ops ||
+                   dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
+                       rtmsg_ifinfo(RTM_DELLINK, dev, ~0U, GFP_KERNEL);
+
                /* Notifier chain MUST detach us all upper devices. */
                WARN_ON(netdev_has_any_upper_dev(dev));
 
@@ -5927,10 +5959,7 @@ static void netdev_init_one_queue(struct net_device *dev,
 
 static void netif_free_tx_queues(struct net_device *dev)
 {
-       if (is_vmalloc_addr(dev->_tx))
-               vfree(dev->_tx);
-       else
-               kfree(dev->_tx);
+       kvfree(dev->_tx);
 }
 
 static int netif_alloc_netdev_queues(struct net_device *dev)
@@ -6404,10 +6433,7 @@ void netdev_freemem(struct net_device *dev)
 {
        char *addr = (char *)dev - dev->padded;
 
-       if (is_vmalloc_addr(addr))
-               vfree(addr);
-       else
-               kfree(addr);
+       kvfree(addr);
 }
 
 /**
@@ -6512,11 +6538,6 @@ free_all:
 
 free_pcpu:
        free_percpu(dev->pcpu_refcnt);
-       netif_free_tx_queues(dev);
-#ifdef CONFIG_SYSFS
-       kfree(dev->_rx);
-#endif
-
 free_dev:
        netdev_freemem(dev);
        return NULL;
@@ -6613,6 +6634,9 @@ EXPORT_SYMBOL(unregister_netdevice_queue);
 /**
  *     unregister_netdevice_many - unregister many devices
  *     @head: list of devices
+ *
+ *  Note: As most callers use a stack allocated list_head,
+ *  we force a list_del() to make sure stack wont be corrupted later.
  */
 void unregister_netdevice_many(struct list_head *head)
 {
@@ -6622,6 +6646,7 @@ void unregister_netdevice_many(struct list_head *head)
                rollback_registered_many(head);
                list_for_each_entry(dev, head, unreg_list)
                        net_set_todo(dev);
+               list_del(head);
        }
 }
 EXPORT_SYMBOL(unregister_netdevice_many);
@@ -7077,7 +7102,6 @@ static void __net_exit default_device_exit_batch(struct list_head *net_list)
                }
        }
        unregister_netdevice_many(&dev_kill_list);
-       list_del(&dev_kill_list);
        rtnl_unlock();
 }
 
index 329d5794e7dca8bf7535800c2becc9bb3883e583..b6b230600b974ab908679fb62d225ed084ea5276 100644 (file)
@@ -225,6 +225,91 @@ void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
 }
 EXPORT_SYMBOL(__hw_addr_unsync);
 
+/**
+ *  __hw_addr_sync_dev - Synchonize device's multicast list
+ *  @list: address list to syncronize
+ *  @dev:  device to sync
+ *  @sync: function to call if address should be added
+ *  @unsync: function to call if address should be removed
+ *
+ *  This funciton is intended to be called from the ndo_set_rx_mode
+ *  function of devices that require explicit address add/remove
+ *  notifications.  The unsync function may be NULL in which case
+ *  the addresses requiring removal will simply be removed without
+ *  any notification to the device.
+ **/
+int __hw_addr_sync_dev(struct netdev_hw_addr_list *list,
+                      struct net_device *dev,
+                      int (*sync)(struct net_device *, const unsigned char *),
+                      int (*unsync)(struct net_device *,
+                                    const unsigned char *))
+{
+       struct netdev_hw_addr *ha, *tmp;
+       int err;
+
+       /* first go through and flush out any stale entries */
+       list_for_each_entry_safe(ha, tmp, &list->list, list) {
+               if (!ha->sync_cnt || ha->refcount != 1)
+                       continue;
+
+               /* if unsync is defined and fails defer unsyncing address */
+               if (unsync && unsync(dev, ha->addr))
+                       continue;
+
+               ha->sync_cnt--;
+               __hw_addr_del_entry(list, ha, false, false);
+       }
+
+       /* go through and sync new entries to the list */
+       list_for_each_entry_safe(ha, tmp, &list->list, list) {
+               if (ha->sync_cnt)
+                       continue;
+
+               err = sync(dev, ha->addr);
+               if (err)
+                       return err;
+
+               ha->sync_cnt++;
+               ha->refcount++;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(__hw_addr_sync_dev);
+
+/**
+ *  __hw_addr_unsync_dev - Remove synchonized addresses from device
+ *  @list: address list to remove syncronized addresses from
+ *  @dev:  device to sync
+ *  @unsync: function to call if address should be removed
+ *
+ *  Remove all addresses that were added to the device by __hw_addr_sync_dev().
+ *  This function is intended to be called from the ndo_stop or ndo_open
+ *  functions on devices that require explicit address add/remove
+ *  notifications.  If the unsync function pointer is NULL then this function
+ *  can be used to just reset the sync_cnt for the addresses in the list.
+ **/
+void __hw_addr_unsync_dev(struct netdev_hw_addr_list *list,
+                         struct net_device *dev,
+                         int (*unsync)(struct net_device *,
+                                       const unsigned char *))
+{
+       struct netdev_hw_addr *ha, *tmp;
+
+       list_for_each_entry_safe(ha, tmp, &list->list, list) {
+               if (!ha->sync_cnt)
+                       continue;
+
+               /* if unsync is defined and fails defer unsyncing address */
+               if (unsync && unsync(dev, ha->addr))
+                       continue;
+
+               ha->sync_cnt--;
+               __hw_addr_del_entry(list, ha, false, false);
+       }
+}
+EXPORT_SYMBOL(__hw_addr_unsync_dev);
+
 static void __hw_addr_flush(struct netdev_hw_addr_list *list)
 {
        struct netdev_hw_addr *ha, *tmp;
index 640ba0e5831ce0334a9cbf03983c16a022e85065..17cb912793fa5ef0d221abda0dfb447b216b4268 100644 (file)
@@ -557,6 +557,23 @@ err_out:
        return ret;
 }
 
+static int ethtool_copy_validate_indir(u32 *indir, void __user *useraddr,
+                                       struct ethtool_rxnfc *rx_rings,
+                                       u32 size)
+{
+       int i;
+
+       if (copy_from_user(indir, useraddr, size * sizeof(indir[0])))
+               return -EFAULT;
+
+       /* Validate ring indices */
+       for (i = 0; i < size; i++)
+               if (indir[i] >= rx_rings->data)
+                       return -EINVAL;
+
+       return 0;
+}
+
 static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
                                                     void __user *useraddr)
 {
@@ -565,7 +582,7 @@ static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
        int ret;
 
        if (!dev->ethtool_ops->get_rxfh_indir_size ||
-           !dev->ethtool_ops->get_rxfh_indir)
+           !dev->ethtool_ops->get_rxfh)
                return -EOPNOTSUPP;
        dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev);
        if (dev_size == 0)
@@ -591,7 +608,7 @@ static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
        if (!indir)
                return -ENOMEM;
 
-       ret = dev->ethtool_ops->get_rxfh_indir(dev, indir);
+       ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL);
        if (ret)
                goto out;
 
@@ -613,8 +630,9 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
        u32 *indir;
        const struct ethtool_ops *ops = dev->ethtool_ops;
        int ret;
+       u32 ringidx_offset = offsetof(struct ethtool_rxfh_indir, ring_index[0]);
 
-       if (!ops->get_rxfh_indir_size || !ops->set_rxfh_indir ||
+       if (!ops->get_rxfh_indir_size || !ops->set_rxfh ||
            !ops->get_rxnfc)
                return -EOPNOTSUPP;
 
@@ -643,28 +661,184 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
                for (i = 0; i < dev_size; i++)
                        indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data);
        } else {
-               if (copy_from_user(indir,
-                                 useraddr +
-                                 offsetof(struct ethtool_rxfh_indir,
-                                          ring_index[0]),
-                                 dev_size * sizeof(indir[0]))) {
+               ret = ethtool_copy_validate_indir(indir,
+                                                 useraddr + ringidx_offset,
+                                                 &rx_rings,
+                                                 dev_size);
+               if (ret)
+                       goto out;
+       }
+
+       ret = ops->set_rxfh(dev, indir, NULL);
+
+out:
+       kfree(indir);
+       return ret;
+}
+
+static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
+                                              void __user *useraddr)
+{
+       int ret;
+       const struct ethtool_ops *ops = dev->ethtool_ops;
+       u32 user_indir_size, user_key_size;
+       u32 dev_indir_size = 0, dev_key_size = 0;
+       struct ethtool_rxfh rxfh;
+       u32 total_size;
+       u32 indir_bytes;
+       u32 *indir = NULL;
+       u8 *hkey = NULL;
+       u8 *rss_config;
+
+       if (!(dev->ethtool_ops->get_rxfh_indir_size ||
+             dev->ethtool_ops->get_rxfh_key_size) ||
+             !dev->ethtool_ops->get_rxfh)
+               return -EOPNOTSUPP;
+
+       if (ops->get_rxfh_indir_size)
+               dev_indir_size = ops->get_rxfh_indir_size(dev);
+       if (ops->get_rxfh_key_size)
+               dev_key_size = ops->get_rxfh_key_size(dev);
+
+       if ((dev_key_size + dev_indir_size) == 0)
+               return -EOPNOTSUPP;
+
+       if (copy_from_user(&rxfh, useraddr, sizeof(rxfh)))
+               return -EFAULT;
+       user_indir_size = rxfh.indir_size;
+       user_key_size = rxfh.key_size;
+
+       /* Check that reserved fields are 0 for now */
+       if (rxfh.rss_context || rxfh.rsvd[0] || rxfh.rsvd[1])
+               return -EINVAL;
+
+       rxfh.indir_size = dev_indir_size;
+       rxfh.key_size = dev_key_size;
+       if (copy_to_user(useraddr, &rxfh, sizeof(rxfh)))
+               return -EFAULT;
+
+       /* If the user buffer size is 0, this is just a query for the
+        * device table size and key size.  Otherwise, if the User size is
+        * not equal to device table size or key size it's an error.
+        */
+       if (!user_indir_size && !user_key_size)
+               return 0;
+
+       if ((user_indir_size && (user_indir_size != dev_indir_size)) ||
+           (user_key_size && (user_key_size != dev_key_size)))
+               return -EINVAL;
+
+       indir_bytes = user_indir_size * sizeof(indir[0]);
+       total_size = indir_bytes + user_key_size;
+       rss_config = kzalloc(total_size, GFP_USER);
+       if (!rss_config)
+               return -ENOMEM;
+
+       if (user_indir_size)
+               indir = (u32 *)rss_config;
+
+       if (user_key_size)
+               hkey = rss_config + indir_bytes;
+
+       ret = dev->ethtool_ops->get_rxfh(dev, indir, hkey);
+       if (!ret) {
+               if (copy_to_user(useraddr +
+                                offsetof(struct ethtool_rxfh, rss_config[0]),
+                                rss_config, total_size))
                        ret = -EFAULT;
+       }
+
+       kfree(rss_config);
+
+       return ret;
+}
+
+static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
+                                              void __user *useraddr)
+{
+       int ret;
+       const struct ethtool_ops *ops = dev->ethtool_ops;
+       struct ethtool_rxnfc rx_rings;
+       struct ethtool_rxfh rxfh;
+       u32 dev_indir_size = 0, dev_key_size = 0, i;
+       u32 *indir = NULL, indir_bytes = 0;
+       u8 *hkey = NULL;
+       u8 *rss_config;
+       u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]);
+
+       if (!(ops->get_rxfh_indir_size || ops->get_rxfh_key_size) ||
+           !ops->get_rxnfc || !ops->set_rxfh)
+               return -EOPNOTSUPP;
+
+       if (ops->get_rxfh_indir_size)
+               dev_indir_size = ops->get_rxfh_indir_size(dev);
+       if (ops->get_rxfh_key_size)
+               dev_key_size = dev->ethtool_ops->get_rxfh_key_size(dev);
+       if ((dev_key_size + dev_indir_size) == 0)
+               return -EOPNOTSUPP;
+
+       if (copy_from_user(&rxfh, useraddr, sizeof(rxfh)))
+               return -EFAULT;
+
+       /* Check that reserved fields are 0 for now */
+       if (rxfh.rss_context || rxfh.rsvd[0] || rxfh.rsvd[1])
+               return -EINVAL;
+
+       /* If either indir or hash key is valid, proceed further.
+        * It is not valid to request that both be unchanged.
+        */
+       if ((rxfh.indir_size &&
+            rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE &&
+            rxfh.indir_size != dev_indir_size) ||
+           (rxfh.key_size && (rxfh.key_size != dev_key_size)) ||
+           (rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE &&
+            rxfh.key_size == 0))
+               return -EINVAL;
+
+       if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
+               indir_bytes = dev_indir_size * sizeof(indir[0]);
+
+       rss_config = kzalloc(indir_bytes + rxfh.key_size, GFP_USER);
+       if (!rss_config)
+               return -ENOMEM;
+
+       rx_rings.cmd = ETHTOOL_GRXRINGS;
+       ret = ops->get_rxnfc(dev, &rx_rings, NULL);
+       if (ret)
+               goto out;
+
+       /* rxfh.indir_size == 0 means reset the indir table to default.
+        * rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE means leave it unchanged.
+        */
+       if (rxfh.indir_size &&
+           rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE) {
+               indir = (u32 *)rss_config;
+               ret = ethtool_copy_validate_indir(indir,
+                                                 useraddr + rss_cfg_offset,
+                                                 &rx_rings,
+                                                 rxfh.indir_size);
+               if (ret)
                        goto out;
-               }
+       } else if (rxfh.indir_size == 0) {
+               indir = (u32 *)rss_config;
+               for (i = 0; i < dev_indir_size; i++)
+                       indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data);
+       }
 
-               /* Validate ring indices */
-               for (i = 0; i < dev_size; i++) {
-                       if (indir[i] >= rx_rings.data) {
-                               ret = -EINVAL;
-                               goto out;
-                       }
+       if (rxfh.key_size) {
+               hkey = rss_config + indir_bytes;
+               if (copy_from_user(hkey,
+                                  useraddr + rss_cfg_offset + indir_bytes,
+                                  rxfh.key_size)) {
+                       ret = -EFAULT;
+                       goto out;
                }
        }
 
-       ret = ops->set_rxfh_indir(dev, indir);
+       ret = ops->set_rxfh(dev, indir, hkey);
 
 out:
-       kfree(indir);
+       kfree(rss_config);
        return ret;
 }
 
@@ -1491,6 +1665,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
        case ETHTOOL_GRXCLSRULE:
        case ETHTOOL_GRXCLSRLALL:
        case ETHTOOL_GRXFHINDIR:
+       case ETHTOOL_GRSSH:
        case ETHTOOL_GFEATURES:
        case ETHTOOL_GCHANNELS:
        case ETHTOOL_GET_TS_INFO:
@@ -1628,6 +1803,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
        case ETHTOOL_SRXFHINDIR:
                rc = ethtool_set_rxfh_indir(dev, useraddr);
                break;
+       case ETHTOOL_GRSSH:
+               rc = ethtool_get_rxfh(dev, useraddr);
+               break;
+       case ETHTOOL_SRSSH:
+               rc = ethtool_set_rxfh(dev, useraddr);
+               break;
        case ETHTOOL_GFEATURES:
                rc = ethtool_get_features(dev, useraddr);
                break;
index 4aec7b93f1a9cd2f7695d2056acff0e9dec6f80a..735fad89749630b71b2b64d6c82d59fec60a7c7f 100644 (file)
 #include <linux/seccomp.h>
 #include <linux/if_vlan.h>
 
+/* Registers */
+#define BPF_R0 regs[BPF_REG_0]
+#define BPF_R1 regs[BPF_REG_1]
+#define BPF_R2 regs[BPF_REG_2]
+#define BPF_R3 regs[BPF_REG_3]
+#define BPF_R4 regs[BPF_REG_4]
+#define BPF_R5 regs[BPF_REG_5]
+#define BPF_R6 regs[BPF_REG_6]
+#define BPF_R7 regs[BPF_REG_7]
+#define BPF_R8 regs[BPF_REG_8]
+#define BPF_R9 regs[BPF_REG_9]
+#define BPF_R10        regs[BPF_REG_10]
+
+/* Named registers */
+#define DST    regs[insn->dst_reg]
+#define SRC    regs[insn->src_reg]
+#define FP     regs[BPF_REG_FP]
+#define ARG1   regs[BPF_REG_ARG1]
+#define CTX    regs[BPF_REG_CTX]
+#define IMM    insn->imm
+
 /* No hurry in this branch
  *
  * Exported for the bpf jit load helper.
@@ -57,9 +78,9 @@ void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb, int k, uns
                ptr = skb_network_header(skb) + k - SKF_NET_OFF;
        else if (k >= SKF_LL_OFF)
                ptr = skb_mac_header(skb) + k - SKF_LL_OFF;
-
        if (ptr >= skb->head && ptr + size <= skb_tail_pointer(skb))
                return ptr;
+
        return NULL;
 }
 
@@ -68,6 +89,7 @@ static inline void *load_pointer(const struct sk_buff *skb, int k,
 {
        if (k >= 0)
                return skb_header_pointer(skb, k, size, buffer);
+
        return bpf_internal_load_pointer_neg_helper(skb, k, size);
 }
 
@@ -122,13 +144,6 @@ noinline u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
        return 0;
 }
 
-/* Register mappings for user programs. */
-#define A_REG          0
-#define X_REG          7
-#define TMP_REG                8
-#define ARG2_REG       2
-#define ARG3_REG       3
-
 /**
  *     __sk_run_filter - run a filter on a given context
  *     @ctx: buffer to run the filter on
@@ -138,447 +153,442 @@ noinline u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
  * keep, 0 for none. @ctx is the data we are operating on, @insn is the
  * array of filter instructions.
  */
-unsigned int __sk_run_filter(void *ctx, const struct sock_filter_int *insn)
+static unsigned int __sk_run_filter(void *ctx, const struct sock_filter_int *insn)
 {
        u64 stack[MAX_BPF_STACK / sizeof(u64)];
        u64 regs[MAX_BPF_REG], tmp;
-       void *ptr;
-       int off;
-
-#define K  insn->imm
-#define A  regs[insn->a_reg]
-#define X  regs[insn->x_reg]
-#define R0 regs[0]
-
-#define CONT    ({insn++; goto select_insn; })
-#define CONT_JMP ({insn++; goto select_insn; })
-
        static const void *jumptable[256] = {
                [0 ... 255] = &&default_label,
                /* Now overwrite non-defaults ... */
-#define DL(A, B, C)    [A|B|C] = &&A##_##B##_##C
-               DL(BPF_ALU, BPF_ADD, BPF_X),
-               DL(BPF_ALU, BPF_ADD, BPF_K),
-               DL(BPF_ALU, BPF_SUB, BPF_X),
-               DL(BPF_ALU, BPF_SUB, BPF_K),
-               DL(BPF_ALU, BPF_AND, BPF_X),
-               DL(BPF_ALU, BPF_AND, BPF_K),
-               DL(BPF_ALU, BPF_OR, BPF_X),
-               DL(BPF_ALU, BPF_OR, BPF_K),
-               DL(BPF_ALU, BPF_LSH, BPF_X),
-               DL(BPF_ALU, BPF_LSH, BPF_K),
-               DL(BPF_ALU, BPF_RSH, BPF_X),
-               DL(BPF_ALU, BPF_RSH, BPF_K),
-               DL(BPF_ALU, BPF_XOR, BPF_X),
-               DL(BPF_ALU, BPF_XOR, BPF_K),
-               DL(BPF_ALU, BPF_MUL, BPF_X),
-               DL(BPF_ALU, BPF_MUL, BPF_K),
-               DL(BPF_ALU, BPF_MOV, BPF_X),
-               DL(BPF_ALU, BPF_MOV, BPF_K),
-               DL(BPF_ALU, BPF_DIV, BPF_X),
-               DL(BPF_ALU, BPF_DIV, BPF_K),
-               DL(BPF_ALU, BPF_MOD, BPF_X),
-               DL(BPF_ALU, BPF_MOD, BPF_K),
-               DL(BPF_ALU, BPF_NEG, 0),
-               DL(BPF_ALU, BPF_END, BPF_TO_BE),
-               DL(BPF_ALU, BPF_END, BPF_TO_LE),
-               DL(BPF_ALU64, BPF_ADD, BPF_X),
-               DL(BPF_ALU64, BPF_ADD, BPF_K),
-               DL(BPF_ALU64, BPF_SUB, BPF_X),
-               DL(BPF_ALU64, BPF_SUB, BPF_K),
-               DL(BPF_ALU64, BPF_AND, BPF_X),
-               DL(BPF_ALU64, BPF_AND, BPF_K),
-               DL(BPF_ALU64, BPF_OR, BPF_X),
-               DL(BPF_ALU64, BPF_OR, BPF_K),
-               DL(BPF_ALU64, BPF_LSH, BPF_X),
-               DL(BPF_ALU64, BPF_LSH, BPF_K),
-               DL(BPF_ALU64, BPF_RSH, BPF_X),
-               DL(BPF_ALU64, BPF_RSH, BPF_K),
-               DL(BPF_ALU64, BPF_XOR, BPF_X),
-               DL(BPF_ALU64, BPF_XOR, BPF_K),
-               DL(BPF_ALU64, BPF_MUL, BPF_X),
-               DL(BPF_ALU64, BPF_MUL, BPF_K),
-               DL(BPF_ALU64, BPF_MOV, BPF_X),
-               DL(BPF_ALU64, BPF_MOV, BPF_K),
-               DL(BPF_ALU64, BPF_ARSH, BPF_X),
-               DL(BPF_ALU64, BPF_ARSH, BPF_K),
-               DL(BPF_ALU64, BPF_DIV, BPF_X),
-               DL(BPF_ALU64, BPF_DIV, BPF_K),
-               DL(BPF_ALU64, BPF_MOD, BPF_X),
-               DL(BPF_ALU64, BPF_MOD, BPF_K),
-               DL(BPF_ALU64, BPF_NEG, 0),
-               DL(BPF_JMP, BPF_CALL, 0),
-               DL(BPF_JMP, BPF_JA, 0),
-               DL(BPF_JMP, BPF_JEQ, BPF_X),
-               DL(BPF_JMP, BPF_JEQ, BPF_K),
-               DL(BPF_JMP, BPF_JNE, BPF_X),
-               DL(BPF_JMP, BPF_JNE, BPF_K),
-               DL(BPF_JMP, BPF_JGT, BPF_X),
-               DL(BPF_JMP, BPF_JGT, BPF_K),
-               DL(BPF_JMP, BPF_JGE, BPF_X),
-               DL(BPF_JMP, BPF_JGE, BPF_K),
-               DL(BPF_JMP, BPF_JSGT, BPF_X),
-               DL(BPF_JMP, BPF_JSGT, BPF_K),
-               DL(BPF_JMP, BPF_JSGE, BPF_X),
-               DL(BPF_JMP, BPF_JSGE, BPF_K),
-               DL(BPF_JMP, BPF_JSET, BPF_X),
-               DL(BPF_JMP, BPF_JSET, BPF_K),
-               DL(BPF_JMP, BPF_EXIT, 0),
-               DL(BPF_STX, BPF_MEM, BPF_B),
-               DL(BPF_STX, BPF_MEM, BPF_H),
-               DL(BPF_STX, BPF_MEM, BPF_W),
-               DL(BPF_STX, BPF_MEM, BPF_DW),
-               DL(BPF_STX, BPF_XADD, BPF_W),
-               DL(BPF_STX, BPF_XADD, BPF_DW),
-               DL(BPF_ST, BPF_MEM, BPF_B),
-               DL(BPF_ST, BPF_MEM, BPF_H),
-               DL(BPF_ST, BPF_MEM, BPF_W),
-               DL(BPF_ST, BPF_MEM, BPF_DW),
-               DL(BPF_LDX, BPF_MEM, BPF_B),
-               DL(BPF_LDX, BPF_MEM, BPF_H),
-               DL(BPF_LDX, BPF_MEM, BPF_W),
-               DL(BPF_LDX, BPF_MEM, BPF_DW),
-               DL(BPF_LD, BPF_ABS, BPF_W),
-               DL(BPF_LD, BPF_ABS, BPF_H),
-               DL(BPF_LD, BPF_ABS, BPF_B),
-               DL(BPF_LD, BPF_IND, BPF_W),
-               DL(BPF_LD, BPF_IND, BPF_H),
-               DL(BPF_LD, BPF_IND, BPF_B),
-#undef DL
+               /* 32 bit ALU operations */
+               [BPF_ALU | BPF_ADD | BPF_X] = &&ALU_ADD_X,
+               [BPF_ALU | BPF_ADD | BPF_K] = &&ALU_ADD_K,
+               [BPF_ALU | BPF_SUB | BPF_X] = &&ALU_SUB_X,
+               [BPF_ALU | BPF_SUB | BPF_K] = &&ALU_SUB_K,
+               [BPF_ALU | BPF_AND | BPF_X] = &&ALU_AND_X,
+               [BPF_ALU | BPF_AND | BPF_K] = &&ALU_AND_K,
+               [BPF_ALU | BPF_OR | BPF_X]  = &&ALU_OR_X,
+               [BPF_ALU | BPF_OR | BPF_K]  = &&ALU_OR_K,
+               [BPF_ALU | BPF_LSH | BPF_X] = &&ALU_LSH_X,
+               [BPF_ALU | BPF_LSH | BPF_K] = &&ALU_LSH_K,
+               [BPF_ALU | BPF_RSH | BPF_X] = &&ALU_RSH_X,
+               [BPF_ALU | BPF_RSH | BPF_K] = &&ALU_RSH_K,
+               [BPF_ALU | BPF_XOR | BPF_X] = &&ALU_XOR_X,
+               [BPF_ALU | BPF_XOR | BPF_K] = &&ALU_XOR_K,
+               [BPF_ALU | BPF_MUL | BPF_X] = &&ALU_MUL_X,
+               [BPF_ALU | BPF_MUL | BPF_K] = &&ALU_MUL_K,
+               [BPF_ALU | BPF_MOV | BPF_X] = &&ALU_MOV_X,
+               [BPF_ALU | BPF_MOV | BPF_K] = &&ALU_MOV_K,
+               [BPF_ALU | BPF_DIV | BPF_X] = &&ALU_DIV_X,
+               [BPF_ALU | BPF_DIV | BPF_K] = &&ALU_DIV_K,
+               [BPF_ALU | BPF_MOD | BPF_X] = &&ALU_MOD_X,
+               [BPF_ALU | BPF_MOD | BPF_K] = &&ALU_MOD_K,
+               [BPF_ALU | BPF_NEG] = &&ALU_NEG,
+               [BPF_ALU | BPF_END | BPF_TO_BE] = &&ALU_END_TO_BE,
+               [BPF_ALU | BPF_END | BPF_TO_LE] = &&ALU_END_TO_LE,
+               /* 64 bit ALU operations */
+               [BPF_ALU64 | BPF_ADD | BPF_X] = &&ALU64_ADD_X,
+               [BPF_ALU64 | BPF_ADD | BPF_K] = &&ALU64_ADD_K,
+               [BPF_ALU64 | BPF_SUB | BPF_X] = &&ALU64_SUB_X,
+               [BPF_ALU64 | BPF_SUB | BPF_K] = &&ALU64_SUB_K,
+               [BPF_ALU64 | BPF_AND | BPF_X] = &&ALU64_AND_X,
+               [BPF_ALU64 | BPF_AND | BPF_K] = &&ALU64_AND_K,
+               [BPF_ALU64 | BPF_OR | BPF_X] = &&ALU64_OR_X,
+               [BPF_ALU64 | BPF_OR | BPF_K] = &&ALU64_OR_K,
+               [BPF_ALU64 | BPF_LSH | BPF_X] = &&ALU64_LSH_X,
+               [BPF_ALU64 | BPF_LSH | BPF_K] = &&ALU64_LSH_K,
+               [BPF_ALU64 | BPF_RSH | BPF_X] = &&ALU64_RSH_X,
+               [BPF_ALU64 | BPF_RSH | BPF_K] = &&ALU64_RSH_K,
+               [BPF_ALU64 | BPF_XOR | BPF_X] = &&ALU64_XOR_X,
+               [BPF_ALU64 | BPF_XOR | BPF_K] = &&ALU64_XOR_K,
+               [BPF_ALU64 | BPF_MUL | BPF_X] = &&ALU64_MUL_X,
+               [BPF_ALU64 | BPF_MUL | BPF_K] = &&ALU64_MUL_K,
+               [BPF_ALU64 | BPF_MOV | BPF_X] = &&ALU64_MOV_X,
+               [BPF_ALU64 | BPF_MOV | BPF_K] = &&ALU64_MOV_K,
+               [BPF_ALU64 | BPF_ARSH | BPF_X] = &&ALU64_ARSH_X,
+               [BPF_ALU64 | BPF_ARSH | BPF_K] = &&ALU64_ARSH_K,
+               [BPF_ALU64 | BPF_DIV | BPF_X] = &&ALU64_DIV_X,
+               [BPF_ALU64 | BPF_DIV | BPF_K] = &&ALU64_DIV_K,
+               [BPF_ALU64 | BPF_MOD | BPF_X] = &&ALU64_MOD_X,
+               [BPF_ALU64 | BPF_MOD | BPF_K] = &&ALU64_MOD_K,
+               [BPF_ALU64 | BPF_NEG] = &&ALU64_NEG,
+               /* Call instruction */
+               [BPF_JMP | BPF_CALL] = &&JMP_CALL,
+               /* Jumps */
+               [BPF_JMP | BPF_JA] = &&JMP_JA,
+               [BPF_JMP | BPF_JEQ | BPF_X] = &&JMP_JEQ_X,
+               [BPF_JMP | BPF_JEQ | BPF_K] = &&JMP_JEQ_K,
+               [BPF_JMP | BPF_JNE | BPF_X] = &&JMP_JNE_X,
+               [BPF_JMP | BPF_JNE | BPF_K] = &&JMP_JNE_K,
+               [BPF_JMP | BPF_JGT | BPF_X] = &&JMP_JGT_X,
+               [BPF_JMP | BPF_JGT | BPF_K] = &&JMP_JGT_K,
+               [BPF_JMP | BPF_JGE | BPF_X] = &&JMP_JGE_X,
+               [BPF_JMP | BPF_JGE | BPF_K] = &&JMP_JGE_K,
+               [BPF_JMP | BPF_JSGT | BPF_X] = &&JMP_JSGT_X,
+               [BPF_JMP | BPF_JSGT | BPF_K] = &&JMP_JSGT_K,
+               [BPF_JMP | BPF_JSGE | BPF_X] = &&JMP_JSGE_X,
+               [BPF_JMP | BPF_JSGE | BPF_K] = &&JMP_JSGE_K,
+               [BPF_JMP | BPF_JSET | BPF_X] = &&JMP_JSET_X,
+               [BPF_JMP | BPF_JSET | BPF_K] = &&JMP_JSET_K,
+               /* Program return */
+               [BPF_JMP | BPF_EXIT] = &&JMP_EXIT,
+               /* Store instructions */
+               [BPF_STX | BPF_MEM | BPF_B] = &&STX_MEM_B,
+               [BPF_STX | BPF_MEM | BPF_H] = &&STX_MEM_H,
+               [BPF_STX | BPF_MEM | BPF_W] = &&STX_MEM_W,
+               [BPF_STX | BPF_MEM | BPF_DW] = &&STX_MEM_DW,
+               [BPF_STX | BPF_XADD | BPF_W] = &&STX_XADD_W,
+               [BPF_STX | BPF_XADD | BPF_DW] = &&STX_XADD_DW,
+               [BPF_ST | BPF_MEM | BPF_B] = &&ST_MEM_B,
+               [BPF_ST | BPF_MEM | BPF_H] = &&ST_MEM_H,
+               [BPF_ST | BPF_MEM | BPF_W] = &&ST_MEM_W,
+               [BPF_ST | BPF_MEM | BPF_DW] = &&ST_MEM_DW,
+               /* Load instructions */
+               [BPF_LDX | BPF_MEM | BPF_B] = &&LDX_MEM_B,
+               [BPF_LDX | BPF_MEM | BPF_H] = &&LDX_MEM_H,
+               [BPF_LDX | BPF_MEM | BPF_W] = &&LDX_MEM_W,
+               [BPF_LDX | BPF_MEM | BPF_DW] = &&LDX_MEM_DW,
+               [BPF_LD | BPF_ABS | BPF_W] = &&LD_ABS_W,
+               [BPF_LD | BPF_ABS | BPF_H] = &&LD_ABS_H,
+               [BPF_LD | BPF_ABS | BPF_B] = &&LD_ABS_B,
+               [BPF_LD | BPF_IND | BPF_W] = &&LD_IND_W,
+               [BPF_LD | BPF_IND | BPF_H] = &&LD_IND_H,
+               [BPF_LD | BPF_IND | BPF_B] = &&LD_IND_B,
        };
+       void *ptr;
+       int off;
+
+#define CONT    ({ insn++; goto select_insn; })
+#define CONT_JMP ({ insn++; goto select_insn; })
 
-       regs[FP_REG]  = (u64) (unsigned long) &stack[ARRAY_SIZE(stack)];
-       regs[ARG1_REG] = (u64) (unsigned long) ctx;
-       regs[A_REG] = 0;
-       regs[X_REG] = 0;
+       FP = (u64) (unsigned long) &stack[ARRAY_SIZE(stack)];
+       ARG1 = (u64) (unsigned long) ctx;
+
+       /* Registers used in classic BPF programs need to be reset first. */
+       regs[BPF_REG_A] = 0;
+       regs[BPF_REG_X] = 0;
 
 select_insn:
        goto *jumptable[insn->code];
 
        /* ALU */
 #define ALU(OPCODE, OP)                        \
-       BPF_ALU64_##OPCODE##_BPF_X:     \
-               A = A OP X;             \
+       ALU64_##OPCODE##_X:             \
+               DST = DST OP SRC;       \
                CONT;                   \
-       BPF_ALU_##OPCODE##_BPF_X:       \
-               A = (u32) A OP (u32) X; \
+       ALU_##OPCODE##_X:               \
+               DST = (u32) DST OP (u32) SRC;   \
                CONT;                   \
-       BPF_ALU64_##OPCODE##_BPF_K:     \
-               A = A OP K;             \
+       ALU64_##OPCODE##_K:             \
+               DST = DST OP IMM;               \
                CONT;                   \
-       BPF_ALU_##OPCODE##_BPF_K:       \
-               A = (u32) A OP (u32) K; \
+       ALU_##OPCODE##_K:               \
+               DST = (u32) DST OP (u32) IMM;   \
                CONT;
 
-       ALU(BPF_ADD,  +)
-       ALU(BPF_SUB,  -)
-       ALU(BPF_AND,  &)
-       ALU(BPF_OR,   |)
-       ALU(BPF_LSH, <<)
-       ALU(BPF_RSH, >>)
-       ALU(BPF_XOR,  ^)
-       ALU(BPF_MUL,  *)
+       ALU(ADD,  +)
+       ALU(SUB,  -)
+       ALU(AND,  &)
+       ALU(OR,   |)
+       ALU(LSH, <<)
+       ALU(RSH, >>)
+       ALU(XOR,  ^)
+       ALU(MUL,  *)
 #undef ALU
-       BPF_ALU_BPF_NEG_0:
-               A = (u32) -A;
+       ALU_NEG:
+               DST = (u32) -DST;
                CONT;
-       BPF_ALU64_BPF_NEG_0:
-               A = -A;
+       ALU64_NEG:
+               DST = -DST;
                CONT;
-       BPF_ALU_BPF_MOV_BPF_X:
-               A = (u32) X;
+       ALU_MOV_X:
+               DST = (u32) SRC;
                CONT;
-       BPF_ALU_BPF_MOV_BPF_K:
-               A = (u32) K;
+       ALU_MOV_K:
+               DST = (u32) IMM;
                CONT;
-       BPF_ALU64_BPF_MOV_BPF_X:
-               A = X;
+       ALU64_MOV_X:
+               DST = SRC;
                CONT;
-       BPF_ALU64_BPF_MOV_BPF_K:
-               A = K;
+       ALU64_MOV_K:
+               DST = IMM;
                CONT;
-       BPF_ALU64_BPF_ARSH_BPF_X:
-               (*(s64 *) &A) >>= X;
+       ALU64_ARSH_X:
+               (*(s64 *) &DST) >>= SRC;
                CONT;
-       BPF_ALU64_BPF_ARSH_BPF_K:
-               (*(s64 *) &A) >>= K;
+       ALU64_ARSH_K:
+               (*(s64 *) &DST) >>= IMM;
                CONT;
-       BPF_ALU64_BPF_MOD_BPF_X:
-               if (unlikely(X == 0))
+       ALU64_MOD_X:
+               if (unlikely(SRC == 0))
                        return 0;
-               tmp = A;
-               A = do_div(tmp, X);
+               tmp = DST;
+               DST = do_div(tmp, SRC);
                CONT;
-       BPF_ALU_BPF_MOD_BPF_X:
-               if (unlikely(X == 0))
+       ALU_MOD_X:
+               if (unlikely(SRC == 0))
                        return 0;
-               tmp = (u32) A;
-               A = do_div(tmp, (u32) X);
+               tmp = (u32) DST;
+               DST = do_div(tmp, (u32) SRC);
                CONT;
-       BPF_ALU64_BPF_MOD_BPF_K:
-               tmp = A;
-               A = do_div(tmp, K);
+       ALU64_MOD_K:
+               tmp = DST;
+               DST = do_div(tmp, IMM);
                CONT;
-       BPF_ALU_BPF_MOD_BPF_K:
-               tmp = (u32) A;
-               A = do_div(tmp, (u32) K);
+       ALU_MOD_K:
+               tmp = (u32) DST;
+               DST = do_div(tmp, (u32) IMM);
                CONT;
-       BPF_ALU64_BPF_DIV_BPF_X:
-               if (unlikely(X == 0))
+       ALU64_DIV_X:
+               if (unlikely(SRC == 0))
                        return 0;
-               do_div(A, X);
+               do_div(DST, SRC);
                CONT;
-       BPF_ALU_BPF_DIV_BPF_X:
-               if (unlikely(X == 0))
+       ALU_DIV_X:
+               if (unlikely(SRC == 0))
                        return 0;
-               tmp = (u32) A;
-               do_div(tmp, (u32) X);
-               A = (u32) tmp;
+               tmp = (u32) DST;
+               do_div(tmp, (u32) SRC);
+               DST = (u32) tmp;
                CONT;
-       BPF_ALU64_BPF_DIV_BPF_K:
-               do_div(A, K);
+       ALU64_DIV_K:
+               do_div(DST, IMM);
                CONT;
-       BPF_ALU_BPF_DIV_BPF_K:
-               tmp = (u32) A;
-               do_div(tmp, (u32) K);
-               A = (u32) tmp;
+       ALU_DIV_K:
+               tmp = (u32) DST;
+               do_div(tmp, (u32) IMM);
+               DST = (u32) tmp;
                CONT;
-       BPF_ALU_BPF_END_BPF_TO_BE:
-               switch (K) {
+       ALU_END_TO_BE:
+               switch (IMM) {
                case 16:
-                       A = (__force u16) cpu_to_be16(A);
+                       DST = (__force u16) cpu_to_be16(DST);
                        break;
                case 32:
-                       A = (__force u32) cpu_to_be32(A);
+                       DST = (__force u32) cpu_to_be32(DST);
                        break;
                case 64:
-                       A = (__force u64) cpu_to_be64(A);
+                       DST = (__force u64) cpu_to_be64(DST);
                        break;
                }
                CONT;
-       BPF_ALU_BPF_END_BPF_TO_LE:
-               switch (K) {
+       ALU_END_TO_LE:
+               switch (IMM) {
                case 16:
-                       A = (__force u16) cpu_to_le16(A);
+                       DST = (__force u16) cpu_to_le16(DST);
                        break;
                case 32:
-                       A = (__force u32) cpu_to_le32(A);
+                       DST = (__force u32) cpu_to_le32(DST);
                        break;
                case 64:
-                       A = (__force u64) cpu_to_le64(A);
+                       DST = (__force u64) cpu_to_le64(DST);
                        break;
                }
                CONT;
 
        /* CALL */
-       BPF_JMP_BPF_CALL_0:
-               /* Function call scratches R1-R5 registers, preserves R6-R9,
-                * and stores return value into R0.
+       JMP_CALL:
+               /* Function call scratches BPF_R1-BPF_R5 registers,
+                * preserves BPF_R6-BPF_R9, and stores return value
+                * into BPF_R0.
                 */
-               R0 = (__bpf_call_base + insn->imm)(regs[1], regs[2], regs[3],
-                                                  regs[4], regs[5]);
+               BPF_R0 = (__bpf_call_base + insn->imm)(BPF_R1, BPF_R2, BPF_R3,
+                                                      BPF_R4, BPF_R5);
                CONT;
 
        /* JMP */
-       BPF_JMP_BPF_JA_0:
+       JMP_JA:
                insn += insn->off;
                CONT;
-       BPF_JMP_BPF_JEQ_BPF_X:
-               if (A == X) {
+       JMP_JEQ_X:
+               if (DST == SRC) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JEQ_BPF_K:
-               if (A == K) {
+       JMP_JEQ_K:
+               if (DST == IMM) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JNE_BPF_X:
-               if (A != X) {
+       JMP_JNE_X:
+               if (DST != SRC) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JNE_BPF_K:
-               if (A != K) {
+       JMP_JNE_K:
+               if (DST != IMM) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JGT_BPF_X:
-               if (A > X) {
+       JMP_JGT_X:
+               if (DST > SRC) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JGT_BPF_K:
-               if (A > K) {
+       JMP_JGT_K:
+               if (DST > IMM) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JGE_BPF_X:
-               if (A >= X) {
+       JMP_JGE_X:
+               if (DST >= SRC) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JGE_BPF_K:
-               if (A >= K) {
+       JMP_JGE_K:
+               if (DST >= IMM) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JSGT_BPF_X:
-               if (((s64)A) > ((s64)X)) {
+       JMP_JSGT_X:
+               if (((s64) DST) > ((s64) SRC)) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JSGT_BPF_K:
-               if (((s64)A) > ((s64)K)) {
+       JMP_JSGT_K:
+               if (((s64) DST) > ((s64) IMM)) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JSGE_BPF_X:
-               if (((s64)A) >= ((s64)X)) {
+       JMP_JSGE_X:
+               if (((s64) DST) >= ((s64) SRC)) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JSGE_BPF_K:
-               if (((s64)A) >= ((s64)K)) {
+       JMP_JSGE_K:
+               if (((s64) DST) >= ((s64) IMM)) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JSET_BPF_X:
-               if (A & X) {
+       JMP_JSET_X:
+               if (DST & SRC) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JSET_BPF_K:
-               if (A & K) {
+       JMP_JSET_K:
+               if (DST & IMM) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_EXIT_0:
-               return R0;
+       JMP_EXIT:
+               return BPF_R0;
 
        /* STX and ST and LDX*/
-#define LDST(SIZEOP, SIZE)                                     \
-       BPF_STX_BPF_MEM_##SIZEOP:                               \
-               *(SIZE *)(unsigned long) (A + insn->off) = X;   \
-               CONT;                                           \
-       BPF_ST_BPF_MEM_##SIZEOP:                                \
-               *(SIZE *)(unsigned long) (A + insn->off) = K;   \
-               CONT;                                           \
-       BPF_LDX_BPF_MEM_##SIZEOP:                               \
-               A = *(SIZE *)(unsigned long) (X + insn->off);   \
+#define LDST(SIZEOP, SIZE)                                             \
+       STX_MEM_##SIZEOP:                                               \
+               *(SIZE *)(unsigned long) (DST + insn->off) = SRC;       \
+               CONT;                                                   \
+       ST_MEM_##SIZEOP:                                                \
+               *(SIZE *)(unsigned long) (DST + insn->off) = IMM;       \
+               CONT;                                                   \
+       LDX_MEM_##SIZEOP:                                               \
+               DST = *(SIZE *)(unsigned long) (SRC + insn->off);       \
                CONT;
 
-       LDST(BPF_B,   u8)
-       LDST(BPF_H,  u16)
-       LDST(BPF_W,  u32)
-       LDST(BPF_DW, u64)
+       LDST(B,   u8)
+       LDST(H,  u16)
+       LDST(W,  u32)
+       LDST(DW, u64)
 #undef LDST
-       BPF_STX_BPF_XADD_BPF_W: /* lock xadd *(u32 *)(A + insn->off) += X */
-               atomic_add((u32) X, (atomic_t *)(unsigned long)
-                          (A + insn->off));
+       STX_XADD_W: /* lock xadd *(u32 *)(dst_reg + off16) += src_reg */
+               atomic_add((u32) SRC, (atomic_t *)(unsigned long)
+                          (DST + insn->off));
                CONT;
-       BPF_STX_BPF_XADD_BPF_DW: /* lock xadd *(u64 *)(A + insn->off) += X */
-               atomic64_add((u64) X, (atomic64_t *)(unsigned long)
-                            (A + insn->off));
+       STX_XADD_DW: /* lock xadd *(u64 *)(dst_reg + off16) += src_reg */
+               atomic64_add((u64) SRC, (atomic64_t *)(unsigned long)
+                            (DST + insn->off));
                CONT;
-       BPF_LD_BPF_ABS_BPF_W: /* R0 = ntohl(*(u32 *) (skb->data + K)) */
-               off = K;
+       LD_ABS_W: /* BPF_R0 = ntohl(*(u32 *) (skb->data + imm32)) */
+               off = IMM;
 load_word:
-               /* BPF_LD + BPD_ABS and BPF_LD + BPF_IND insns are only
-                * appearing in the programs where ctx == skb. All programs
-                * keep 'ctx' in regs[CTX_REG] == R6, sk_convert_filter()
-                * saves it in R6, internal BPF verifier will check that
-                * R6 == ctx.
+               /* BPF_LD + BPD_ABS and BPF_LD + BPF_IND insns are
+                * only appearing in the programs where ctx ==
+                * skb. All programs keep 'ctx' in regs[BPF_REG_CTX]
+                * == BPF_R6, sk_convert_filter() saves it in BPF_R6,
+                * internal BPF verifier will check that BPF_R6 ==
+                * ctx.
                 *
-                * BPF_ABS and BPF_IND are wrappers of function calls, so
-                * they scratch R1-R5 registers, preserve R6-R9, and store
-                * return value into R0.
+                * BPF_ABS and BPF_IND are wrappers of function calls,
+                * so they scratch BPF_R1-BPF_R5 registers, preserve
+                * BPF_R6-BPF_R9, and store return value into BPF_R0.
                 *
                 * Implicit input:
-                *   ctx
+                *   ctx == skb == BPF_R6 == CTX
                 *
                 * Explicit input:
-                *   X == any register
-                *   K == 32-bit immediate
+                *   SRC == any register
+                *   IMM == 32-bit immediate
                 *
                 * Output:
-                *   R0 - 8/16/32-bit skb data converted to cpu endianness
+                *   BPF_R0 - 8/16/32-bit skb data converted to cpu endianness
                 */
-               ptr = load_pointer((struct sk_buff *) ctx, off, 4, &tmp);
+
+               ptr = load_pointer((struct sk_buff *) (unsigned long) CTX, off, 4, &tmp);
                if (likely(ptr != NULL)) {
-                       R0 = get_unaligned_be32(ptr);
+                       BPF_R0 = get_unaligned_be32(ptr);
                        CONT;
                }
+
                return 0;
-       BPF_LD_BPF_ABS_BPF_H: /* R0 = ntohs(*(u16 *) (skb->data + K)) */
-               off = K;
+       LD_ABS_H: /* BPF_R0 = ntohs(*(u16 *) (skb->data + imm32)) */
+               off = IMM;
 load_half:
-               ptr = load_pointer((struct sk_buff *) ctx, off, 2, &tmp);
+               ptr = load_pointer((struct sk_buff *) (unsigned long) CTX, off, 2, &tmp);
                if (likely(ptr != NULL)) {
-                       R0 = get_unaligned_be16(ptr);
+                       BPF_R0 = get_unaligned_be16(ptr);
                        CONT;
                }
+
                return 0;
-       BPF_LD_BPF_ABS_BPF_B: /* R0 = *(u8 *) (ctx + K) */
-               off = K;
+       LD_ABS_B: /* BPF_R0 = *(u8 *) (skb->data + imm32) */
+               off = IMM;
 load_byte:
-               ptr = load_pointer((struct sk_buff *) ctx, off, 1, &tmp);
+               ptr = load_pointer((struct sk_buff *) (unsigned long) CTX, off, 1, &tmp);
                if (likely(ptr != NULL)) {
-                       R0 = *(u8 *)ptr;
+                       BPF_R0 = *(u8 *)ptr;
                        CONT;
                }
+
                return 0;
-       BPF_LD_BPF_IND_BPF_W: /* R0 = ntohl(*(u32 *) (skb->data + X + K)) */
-               off = K + X;
+       LD_IND_W: /* BPF_R0 = ntohl(*(u32 *) (skb->data + src_reg + imm32)) */
+               off = IMM + SRC;
                goto load_word;
-       BPF_LD_BPF_IND_BPF_H: /* R0 = ntohs(*(u16 *) (skb->data + X + K)) */
-               off = K + X;
+       LD_IND_H: /* BPF_R0 = ntohs(*(u16 *) (skb->data + src_reg + imm32)) */
+               off = IMM + SRC;
                goto load_half;
-       BPF_LD_BPF_IND_BPF_B: /* R0 = *(u8 *) (skb->data + X + K) */
-               off = K + X;
+       LD_IND_B: /* BPF_R0 = *(u8 *) (skb->data + src_reg + imm32) */
+               off = IMM + SRC;
                goto load_byte;
 
        default_label:
                /* If we ever reach this, we have a bug somewhere. */
                WARN_RATELIMIT(1, "unknown opcode %02x\n", insn->code);
                return 0;
-#undef CONT_JMP
-#undef CONT
-
-#undef R0
-#undef X
-#undef A
-#undef K
 }
 
-u32 sk_run_filter_int_seccomp(const struct seccomp_data *ctx,
-                             const struct sock_filter_int *insni)
-    __attribute__ ((alias ("__sk_run_filter")));
-
-u32 sk_run_filter_int_skb(const struct sk_buff *ctx,
-                         const struct sock_filter_int *insni)
-    __attribute__ ((alias ("__sk_run_filter")));
-EXPORT_SYMBOL_GPL(sk_run_filter_int_skb);
-
 /* Helper to find the offset of pkt_type in sk_buff structure. We want
  * to make sure its still a 3bit field starting at a byte boundary;
  * taken from arch/x86/net/bpf_jit_comp.c.
  */
+#ifdef __BIG_ENDIAN_BITFIELD
+#define PKT_TYPE_MAX   (7 << 5)
+#else
 #define PKT_TYPE_MAX   7
+#endif
 static unsigned int pkt_type_offset(void)
 {
        struct sk_buff skb_probe = { .pkt_type = ~0, };
@@ -594,16 +604,14 @@ static unsigned int pkt_type_offset(void)
        return -1;
 }
 
-static u64 __skb_get_pay_offset(u64 ctx, u64 A, u64 X, u64 r4, u64 r5)
+static u64 __skb_get_pay_offset(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
 {
-       struct sk_buff *skb = (struct sk_buff *)(long) ctx;
-
-       return __skb_get_poff(skb);
+       return __skb_get_poff((struct sk_buff *)(unsigned long) ctx);
 }
 
-static u64 __skb_get_nlattr(u64 ctx, u64 A, u64 X, u64 r4, u64 r5)
+static u64 __skb_get_nlattr(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
 {
-       struct sk_buff *skb = (struct sk_buff *)(long) ctx;
+       struct sk_buff *skb = (struct sk_buff *)(unsigned long) ctx;
        struct nlattr *nla;
 
        if (skb_is_nonlinear(skb))
@@ -612,19 +620,19 @@ static u64 __skb_get_nlattr(u64 ctx, u64 A, u64 X, u64 r4, u64 r5)
        if (skb->len < sizeof(struct nlattr))
                return 0;
 
-       if (A > skb->len - sizeof(struct nlattr))
+       if (a > skb->len - sizeof(struct nlattr))
                return 0;
 
-       nla = nla_find((struct nlattr *) &skb->data[A], skb->len - A, X);
+       nla = nla_find((struct nlattr *) &skb->data[a], skb->len - a, x);
        if (nla)
                return (void *) nla - (void *) skb->data;
 
        return 0;
 }
 
-static u64 __skb_get_nlattr_nest(u64 ctx, u64 A, u64 X, u64 r4, u64 r5)
+static u64 __skb_get_nlattr_nest(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
 {
-       struct sk_buff *skb = (struct sk_buff *)(long) ctx;
+       struct sk_buff *skb = (struct sk_buff *)(unsigned long) ctx;
        struct nlattr *nla;
 
        if (skb_is_nonlinear(skb))
@@ -633,25 +641,31 @@ static u64 __skb_get_nlattr_nest(u64 ctx, u64 A, u64 X, u64 r4, u64 r5)
        if (skb->len < sizeof(struct nlattr))
                return 0;
 
-       if (A > skb->len - sizeof(struct nlattr))
+       if (a > skb->len - sizeof(struct nlattr))
                return 0;
 
-       nla = (struct nlattr *) &skb->data[A];
-       if (nla->nla_len > skb->len - A)
+       nla = (struct nlattr *) &skb->data[a];
+       if (nla->nla_len > skb->len - a)
                return 0;
 
-       nla = nla_find_nested(nla, X);
+       nla = nla_find_nested(nla, x);
        if (nla)
                return (void *) nla - (void *) skb->data;
 
        return 0;
 }
 
-static u64 __get_raw_cpu_id(u64 ctx, u64 A, u64 X, u64 r4, u64 r5)
+static u64 __get_raw_cpu_id(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
 {
        return raw_smp_processor_id();
 }
 
+/* note that this only generates 32-bit random numbers */
+static u64 __get_random_u32(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
+{
+       return prandom_u32();
+}
+
 static bool convert_bpf_extensions(struct sock_filter *fp,
                                   struct sock_filter_int **insnp)
 {
@@ -661,119 +675,83 @@ static bool convert_bpf_extensions(struct sock_filter *fp,
        case SKF_AD_OFF + SKF_AD_PROTOCOL:
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2);
 
-               insn->code = BPF_LDX | BPF_MEM | BPF_H;
-               insn->a_reg = A_REG;
-               insn->x_reg = CTX_REG;
-               insn->off = offsetof(struct sk_buff, protocol);
-               insn++;
-
+               /* A = *(u16 *) (CTX + offsetof(protocol)) */
+               *insn++ = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX,
+                                     offsetof(struct sk_buff, protocol));
                /* A = ntohs(A) [emitting a nop or swap16] */
-               insn->code = BPF_ALU | BPF_END | BPF_FROM_BE;
-               insn->a_reg = A_REG;
-               insn->imm = 16;
+               *insn = BPF_ENDIAN(BPF_FROM_BE, BPF_REG_A, 16);
                break;
 
        case SKF_AD_OFF + SKF_AD_PKTTYPE:
-               insn->code = BPF_LDX | BPF_MEM | BPF_B;
-               insn->a_reg = A_REG;
-               insn->x_reg = CTX_REG;
-               insn->off = pkt_type_offset();
+               *insn = BPF_LDX_MEM(BPF_B, BPF_REG_A, BPF_REG_CTX,
+                                   pkt_type_offset());
                if (insn->off < 0)
                        return false;
                insn++;
-
-               insn->code = BPF_ALU | BPF_AND | BPF_K;
-               insn->a_reg = A_REG;
-               insn->imm = PKT_TYPE_MAX;
+               *insn = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, PKT_TYPE_MAX);
+#ifdef __BIG_ENDIAN_BITFIELD
+               insn++;
+                *insn = BPF_ALU32_IMM(BPF_RSH, BPF_REG_A, 5);
+#endif
                break;
 
        case SKF_AD_OFF + SKF_AD_IFINDEX:
        case SKF_AD_OFF + SKF_AD_HATYPE:
-               if (FIELD_SIZEOF(struct sk_buff, dev) == 8)
-                       insn->code = BPF_LDX | BPF_MEM | BPF_DW;
-               else
-                       insn->code = BPF_LDX | BPF_MEM | BPF_W;
-               insn->a_reg = TMP_REG;
-               insn->x_reg = CTX_REG;
-               insn->off = offsetof(struct sk_buff, dev);
-               insn++;
-
-               insn->code = BPF_JMP | BPF_JNE | BPF_K;
-               insn->a_reg = TMP_REG;
-               insn->imm = 0;
-               insn->off = 1;
-               insn++;
-
-               insn->code = BPF_JMP | BPF_EXIT;
-               insn++;
-
                BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, ifindex) != 4);
                BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, type) != 2);
-
-               insn->a_reg = A_REG;
-               insn->x_reg = TMP_REG;
-
-               if (fp->k == SKF_AD_OFF + SKF_AD_IFINDEX) {
-                       insn->code = BPF_LDX | BPF_MEM | BPF_W;
-                       insn->off = offsetof(struct net_device, ifindex);
-               } else {
-                       insn->code = BPF_LDX | BPF_MEM | BPF_H;
-                       insn->off = offsetof(struct net_device, type);
-               }
+               BUILD_BUG_ON(bytes_to_bpf_size(FIELD_SIZEOF(struct sk_buff, dev)) < 0);
+
+               *insn++ = BPF_LDX_MEM(bytes_to_bpf_size(FIELD_SIZEOF(struct sk_buff, dev)),
+                                     BPF_REG_TMP, BPF_REG_CTX,
+                                     offsetof(struct sk_buff, dev));
+               /* if (tmp != 0) goto pc + 1 */
+               *insn++ = BPF_JMP_IMM(BPF_JNE, BPF_REG_TMP, 0, 1);
+               *insn++ = BPF_EXIT_INSN();
+               if (fp->k == SKF_AD_OFF + SKF_AD_IFINDEX)
+                       *insn = BPF_LDX_MEM(BPF_W, BPF_REG_A, BPF_REG_TMP,
+                                           offsetof(struct net_device, ifindex));
+               else
+                       *insn = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_TMP,
+                                           offsetof(struct net_device, type));
                break;
 
        case SKF_AD_OFF + SKF_AD_MARK:
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4);
 
-               insn->code = BPF_LDX | BPF_MEM | BPF_W;
-               insn->a_reg = A_REG;
-               insn->x_reg = CTX_REG;
-               insn->off = offsetof(struct sk_buff, mark);
+               *insn = BPF_LDX_MEM(BPF_W, BPF_REG_A, BPF_REG_CTX,
+                                   offsetof(struct sk_buff, mark));
                break;
 
        case SKF_AD_OFF + SKF_AD_RXHASH:
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4);
 
-               insn->code = BPF_LDX | BPF_MEM | BPF_W;
-               insn->a_reg = A_REG;
-               insn->x_reg = CTX_REG;
-               insn->off = offsetof(struct sk_buff, hash);
+               *insn = BPF_LDX_MEM(BPF_W, BPF_REG_A, BPF_REG_CTX,
+                                   offsetof(struct sk_buff, hash));
                break;
 
        case SKF_AD_OFF + SKF_AD_QUEUE:
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2);
 
-               insn->code = BPF_LDX | BPF_MEM | BPF_H;
-               insn->a_reg = A_REG;
-               insn->x_reg = CTX_REG;
-               insn->off = offsetof(struct sk_buff, queue_mapping);
+               *insn = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX,
+                                   offsetof(struct sk_buff, queue_mapping));
                break;
 
        case SKF_AD_OFF + SKF_AD_VLAN_TAG:
        case SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT:
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2);
-
-               insn->code = BPF_LDX | BPF_MEM | BPF_H;
-               insn->a_reg = A_REG;
-               insn->x_reg = CTX_REG;
-               insn->off = offsetof(struct sk_buff, vlan_tci);
-               insn++;
-
                BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000);
 
+               /* A = *(u16 *) (CTX + offsetof(vlan_tci)) */
+               *insn++ = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX,
+                                     offsetof(struct sk_buff, vlan_tci));
                if (fp->k == SKF_AD_OFF + SKF_AD_VLAN_TAG) {
-                       insn->code = BPF_ALU | BPF_AND | BPF_K;
-                       insn->a_reg = A_REG;
-                       insn->imm = ~VLAN_TAG_PRESENT;
+                       *insn = BPF_ALU32_IMM(BPF_AND, BPF_REG_A,
+                                             ~VLAN_TAG_PRESENT);
                } else {
-                       insn->code = BPF_ALU | BPF_RSH | BPF_K;
-                       insn->a_reg = A_REG;
-                       insn->imm = 12;
-                       insn++;
-
-                       insn->code = BPF_ALU | BPF_AND | BPF_K;
-                       insn->a_reg = A_REG;
-                       insn->imm = 1;
+                       /* A >>= 12 */
+                       *insn++ = BPF_ALU32_IMM(BPF_RSH, BPF_REG_A, 12);
+                       /* A &= 1 */
+                       *insn = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, 1);
                }
                break;
 
@@ -781,46 +759,36 @@ static bool convert_bpf_extensions(struct sock_filter *fp,
        case SKF_AD_OFF + SKF_AD_NLATTR:
        case SKF_AD_OFF + SKF_AD_NLATTR_NEST:
        case SKF_AD_OFF + SKF_AD_CPU:
-               /* arg1 = ctx */
-               insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
-               insn->a_reg = ARG1_REG;
-               insn->x_reg = CTX_REG;
-               insn++;
-
+       case SKF_AD_OFF + SKF_AD_RANDOM:
+               /* arg1 = CTX */
+               *insn++ = BPF_MOV64_REG(BPF_REG_ARG1, BPF_REG_CTX);
                /* arg2 = A */
-               insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
-               insn->a_reg = ARG2_REG;
-               insn->x_reg = A_REG;
-               insn++;
-
+               *insn++ = BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_A);
                /* arg3 = X */
-               insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
-               insn->a_reg = ARG3_REG;
-               insn->x_reg = X_REG;
-               insn++;
-
-               /* Emit call(ctx, arg2=A, arg3=X) */
-               insn->code = BPF_JMP | BPF_CALL;
+               *insn++ = BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_X);
+               /* Emit call(arg1=CTX, arg2=A, arg3=X) */
                switch (fp->k) {
                case SKF_AD_OFF + SKF_AD_PAY_OFFSET:
-                       insn->imm = __skb_get_pay_offset - __bpf_call_base;
+                       *insn = BPF_EMIT_CALL(__skb_get_pay_offset);
                        break;
                case SKF_AD_OFF + SKF_AD_NLATTR:
-                       insn->imm = __skb_get_nlattr - __bpf_call_base;
+                       *insn = BPF_EMIT_CALL(__skb_get_nlattr);
                        break;
                case SKF_AD_OFF + SKF_AD_NLATTR_NEST:
-                       insn->imm = __skb_get_nlattr_nest - __bpf_call_base;
+                       *insn = BPF_EMIT_CALL(__skb_get_nlattr_nest);
                        break;
                case SKF_AD_OFF + SKF_AD_CPU:
-                       insn->imm = __get_raw_cpu_id - __bpf_call_base;
+                       *insn = BPF_EMIT_CALL(__get_raw_cpu_id);
+                       break;
+               case SKF_AD_OFF + SKF_AD_RANDOM:
+                       *insn = BPF_EMIT_CALL(__get_random_u32);
                        break;
                }
                break;
 
        case SKF_AD_OFF + SKF_AD_ALU_XOR_X:
-               insn->code = BPF_ALU | BPF_XOR | BPF_X;
-               insn->a_reg = A_REG;
-               insn->x_reg = X_REG;
+               /* A ^= X */
+               *insn = BPF_ALU32_REG(BPF_XOR, BPF_REG_A, BPF_REG_X);
                break;
 
        default:
@@ -870,7 +838,7 @@ int sk_convert_filter(struct sock_filter *prog, int len,
        u8 bpf_src;
 
        BUILD_BUG_ON(BPF_MEMWORDS * sizeof(u32) > MAX_BPF_STACK);
-       BUILD_BUG_ON(FP_REG + 1 != MAX_BPF_REG);
+       BUILD_BUG_ON(BPF_REG_FP + 1 != MAX_BPF_REG);
 
        if (len <= 0 || len >= BPF_MAXINSNS)
                return -EINVAL;
@@ -885,11 +853,8 @@ do_pass:
        new_insn = new_prog;
        fp = prog;
 
-       if (new_insn) {
-               new_insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
-               new_insn->a_reg = CTX_REG;
-               new_insn->x_reg = ARG1_REG;
-       }
+       if (new_insn)
+               *new_insn = BPF_MOV64_REG(BPF_REG_CTX, BPF_REG_ARG1);
        new_insn++;
 
        for (i = 0; i < len; fp++, i++) {
@@ -937,17 +902,16 @@ do_pass:
                            convert_bpf_extensions(fp, &insn))
                                break;
 
-                       insn->code = fp->code;
-                       insn->a_reg = A_REG;
-                       insn->x_reg = X_REG;
-                       insn->imm = fp->k;
+                       *insn = BPF_RAW_INSN(fp->code, BPF_REG_A, BPF_REG_X, 0, fp->k);
                        break;
 
-               /* Jump opcodes map as-is, but offsets need adjustment. */
-               case BPF_JMP | BPF_JA:
-                       target = i + fp->k + 1;
-                       insn->code = fp->code;
-#define EMIT_JMP                                                       \
+               /* Jump transformation cannot use BPF block macros
+                * everywhere as offset calculation and target updates
+                * require a bit more work than the rest, i.e. jump
+                * opcodes map as-is, but offsets need adjustment.
+                */
+
+#define BPF_EMIT_JMP                                                   \
        do {                                                            \
                if (target >= len || target < 0)                        \
                        goto err;                                       \
@@ -956,7 +920,10 @@ do_pass:
                insn->off -= insn - tmp_insns;                          \
        } while (0)
 
-                       EMIT_JMP;
+               case BPF_JMP | BPF_JA:
+                       target = i + fp->k + 1;
+                       insn->code = fp->code;
+                       BPF_EMIT_JMP;
                        break;
 
                case BPF_JMP | BPF_JEQ | BPF_K:
@@ -972,17 +939,14 @@ do_pass:
                                 * immediate into tmp register and use it
                                 * in compare insn.
                                 */
-                               insn->code = BPF_ALU | BPF_MOV | BPF_K;
-                               insn->a_reg = TMP_REG;
-                               insn->imm = fp->k;
-                               insn++;
+                               *insn++ = BPF_MOV32_IMM(BPF_REG_TMP, fp->k);
 
-                               insn->a_reg = A_REG;
-                               insn->x_reg = TMP_REG;
+                               insn->dst_reg = BPF_REG_A;
+                               insn->src_reg = BPF_REG_TMP;
                                bpf_src = BPF_X;
                        } else {
-                               insn->a_reg = A_REG;
-                               insn->x_reg = X_REG;
+                               insn->dst_reg = BPF_REG_A;
+                               insn->src_reg = BPF_REG_X;
                                insn->imm = fp->k;
                                bpf_src = BPF_SRC(fp->code);
                        }
@@ -991,7 +955,7 @@ do_pass:
                        if (fp->jf == 0) {
                                insn->code = BPF_JMP | BPF_OP(fp->code) | bpf_src;
                                target = i + fp->jt + 1;
-                               EMIT_JMP;
+                               BPF_EMIT_JMP;
                                break;
                        }
 
@@ -999,127 +963,94 @@ do_pass:
                        if (fp->jt == 0 && BPF_OP(fp->code) == BPF_JEQ) {
                                insn->code = BPF_JMP | BPF_JNE | bpf_src;
                                target = i + fp->jf + 1;
-                               EMIT_JMP;
+                               BPF_EMIT_JMP;
                                break;
                        }
 
                        /* Other jumps are mapped into two insns: Jxx and JA. */
                        target = i + fp->jt + 1;
                        insn->code = BPF_JMP | BPF_OP(fp->code) | bpf_src;
-                       EMIT_JMP;
+                       BPF_EMIT_JMP;
                        insn++;
 
                        insn->code = BPF_JMP | BPF_JA;
                        target = i + fp->jf + 1;
-                       EMIT_JMP;
+                       BPF_EMIT_JMP;
                        break;
 
                /* ldxb 4 * ([14] & 0xf) is remaped into 6 insns. */
                case BPF_LDX | BPF_MSH | BPF_B:
-                       insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
-                       insn->a_reg = TMP_REG;
-                       insn->x_reg = A_REG;
-                       insn++;
-
-                       insn->code = BPF_LD | BPF_ABS | BPF_B;
-                       insn->a_reg = A_REG;
-                       insn->imm = fp->k;
-                       insn++;
-
-                       insn->code = BPF_ALU | BPF_AND | BPF_K;
-                       insn->a_reg = A_REG;
-                       insn->imm = 0xf;
-                       insn++;
-
-                       insn->code = BPF_ALU | BPF_LSH | BPF_K;
-                       insn->a_reg = A_REG;
-                       insn->imm = 2;
-                       insn++;
-
-                       insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
-                       insn->a_reg = X_REG;
-                       insn->x_reg = A_REG;
-                       insn++;
-
-                       insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
-                       insn->a_reg = A_REG;
-                       insn->x_reg = TMP_REG;
+                       /* tmp = A */
+                       *insn++ = BPF_MOV64_REG(BPF_REG_TMP, BPF_REG_A);
+                       /* A = BPF_R0 = *(u8 *) (skb->data + K) */
+                       *insn++ = BPF_LD_ABS(BPF_B, fp->k);
+                       /* A &= 0xf */
+                       *insn++ = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, 0xf);
+                       /* A <<= 2 */
+                       *insn++ = BPF_ALU32_IMM(BPF_LSH, BPF_REG_A, 2);
+                       /* X = A */
+                       *insn++ = BPF_MOV64_REG(BPF_REG_X, BPF_REG_A);
+                       /* A = tmp */
+                       *insn = BPF_MOV64_REG(BPF_REG_A, BPF_REG_TMP);
                        break;
 
                /* RET_K, RET_A are remaped into 2 insns. */
                case BPF_RET | BPF_A:
                case BPF_RET | BPF_K:
-                       insn->code = BPF_ALU | BPF_MOV |
-                                    (BPF_RVAL(fp->code) == BPF_K ?
-                                     BPF_K : BPF_X);
-                       insn->a_reg = 0;
-                       insn->x_reg = A_REG;
-                       insn->imm = fp->k;
-                       insn++;
-
-                       insn->code = BPF_JMP | BPF_EXIT;
+                       *insn++ = BPF_MOV32_RAW(BPF_RVAL(fp->code) == BPF_K ?
+                                               BPF_K : BPF_X, BPF_REG_0,
+                                               BPF_REG_A, fp->k);
+                       *insn = BPF_EXIT_INSN();
                        break;
 
                /* Store to stack. */
                case BPF_ST:
                case BPF_STX:
-                       insn->code = BPF_STX | BPF_MEM | BPF_W;
-                       insn->a_reg = FP_REG;
-                       insn->x_reg = fp->code == BPF_ST ? A_REG : X_REG;
-                       insn->off = -(BPF_MEMWORDS - fp->k) * 4;
+                       *insn = BPF_STX_MEM(BPF_W, BPF_REG_FP, BPF_CLASS(fp->code) ==
+                                           BPF_ST ? BPF_REG_A : BPF_REG_X,
+                                           -(BPF_MEMWORDS - fp->k) * 4);
                        break;
 
                /* Load from stack. */
                case BPF_LD | BPF_MEM:
                case BPF_LDX | BPF_MEM:
-                       insn->code = BPF_LDX | BPF_MEM | BPF_W;
-                       insn->a_reg = BPF_CLASS(fp->code) == BPF_LD ?
-                                     A_REG : X_REG;
-                       insn->x_reg = FP_REG;
-                       insn->off = -(BPF_MEMWORDS - fp->k) * 4;
+                       *insn = BPF_LDX_MEM(BPF_W, BPF_CLASS(fp->code) == BPF_LD  ?
+                                           BPF_REG_A : BPF_REG_X, BPF_REG_FP,
+                                           -(BPF_MEMWORDS - fp->k) * 4);
                        break;
 
                /* A = K or X = K */
                case BPF_LD | BPF_IMM:
                case BPF_LDX | BPF_IMM:
-                       insn->code = BPF_ALU | BPF_MOV | BPF_K;
-                       insn->a_reg = BPF_CLASS(fp->code) == BPF_LD ?
-                                     A_REG : X_REG;
-                       insn->imm = fp->k;
+                       *insn = BPF_MOV32_IMM(BPF_CLASS(fp->code) == BPF_LD ?
+                                             BPF_REG_A : BPF_REG_X, fp->k);
                        break;
 
                /* X = A */
                case BPF_MISC | BPF_TAX:
-                       insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
-                       insn->a_reg = X_REG;
-                       insn->x_reg = A_REG;
+                       *insn = BPF_MOV64_REG(BPF_REG_X, BPF_REG_A);
                        break;
 
                /* A = X */
                case BPF_MISC | BPF_TXA:
-                       insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
-                       insn->a_reg = A_REG;
-                       insn->x_reg = X_REG;
+                       *insn = BPF_MOV64_REG(BPF_REG_A, BPF_REG_X);
                        break;
 
                /* A = skb->len or X = skb->len */
                case BPF_LD | BPF_W | BPF_LEN:
                case BPF_LDX | BPF_W | BPF_LEN:
-                       insn->code = BPF_LDX | BPF_MEM | BPF_W;
-                       insn->a_reg = BPF_CLASS(fp->code) == BPF_LD ?
-                                     A_REG : X_REG;
-                       insn->x_reg = CTX_REG;
-                       insn->off = offsetof(struct sk_buff, len);
+                       *insn = BPF_LDX_MEM(BPF_W, BPF_CLASS(fp->code) == BPF_LD ?
+                                           BPF_REG_A : BPF_REG_X, BPF_REG_CTX,
+                                           offsetof(struct sk_buff, len));
                        break;
 
-               /* access seccomp_data fields */
+               /* Access seccomp_data fields. */
                case BPF_LDX | BPF_ABS | BPF_W:
-                       insn->code = BPF_LDX | BPF_MEM | BPF_W;
-                       insn->a_reg = A_REG;
-                       insn->x_reg = CTX_REG;
-                       insn->off = fp->k;
+                       /* A = *(u32 *) (ctx + K) */
+                       *insn = BPF_LDX_MEM(BPF_W, BPF_REG_A, BPF_REG_CTX, fp->k);
                        break;
 
+               /* Unkown instruction. */
                default:
                        goto err;
                }
@@ -1128,7 +1059,6 @@ do_pass:
                if (new_prog)
                        memcpy(new_insn, tmp_insns,
                               sizeof(*insn) * (insn - tmp_insns));
-
                new_insn += insn - tmp_insns;
        }
 
@@ -1143,7 +1073,6 @@ do_pass:
                new_flen = new_insn - new_prog;
                if (pass > 2)
                        goto err;
-
                goto do_pass;
        }
 
@@ -1167,44 +1096,46 @@ err:
  */
 static int check_load_and_stores(struct sock_filter *filter, int flen)
 {
-       u16 *masks, memvalid = 0; /* one bit per cell, 16 cells */
+       u16 *masks, memvalid = 0; /* One bit per cell, 16 cells */
        int pc, ret = 0;
 
        BUILD_BUG_ON(BPF_MEMWORDS > 16);
+
        masks = kmalloc(flen * sizeof(*masks), GFP_KERNEL);
        if (!masks)
                return -ENOMEM;
+
        memset(masks, 0xff, flen * sizeof(*masks));
 
        for (pc = 0; pc < flen; pc++) {
                memvalid &= masks[pc];
 
                switch (filter[pc].code) {
-               case BPF_S_ST:
-               case BPF_S_STX:
+               case BPF_ST:
+               case BPF_STX:
                        memvalid |= (1 << filter[pc].k);
                        break;
-               case BPF_S_LD_MEM:
-               case BPF_S_LDX_MEM:
+               case BPF_LD | BPF_MEM:
+               case BPF_LDX | BPF_MEM:
                        if (!(memvalid & (1 << filter[pc].k))) {
                                ret = -EINVAL;
                                goto error;
                        }
                        break;
-               case BPF_S_JMP_JA:
-                       /* a jump must set masks on target */
+               case BPF_JMP | BPF_JA:
+                       /* A jump must set masks on target */
                        masks[pc + 1 + filter[pc].k] &= memvalid;
                        memvalid = ~0;
                        break;
-               case BPF_S_JMP_JEQ_K:
-               case BPF_S_JMP_JEQ_X:
-               case BPF_S_JMP_JGE_K:
-               case BPF_S_JMP_JGE_X:
-               case BPF_S_JMP_JGT_K:
-               case BPF_S_JMP_JGT_X:
-               case BPF_S_JMP_JSET_X:
-               case BPF_S_JMP_JSET_K:
-                       /* a jump must set masks on targets */
+               case BPF_JMP | BPF_JEQ | BPF_K:
+               case BPF_JMP | BPF_JEQ | BPF_X:
+               case BPF_JMP | BPF_JGE | BPF_K:
+               case BPF_JMP | BPF_JGE | BPF_X:
+               case BPF_JMP | BPF_JGT | BPF_K:
+               case BPF_JMP | BPF_JGT | BPF_X:
+               case BPF_JMP | BPF_JSET | BPF_K:
+               case BPF_JMP | BPF_JSET | BPF_X:
+                       /* A jump must set masks on targets */
                        masks[pc + 1 + filter[pc].jt] &= memvalid;
                        masks[pc + 1 + filter[pc].jf] &= memvalid;
                        memvalid = ~0;
@@ -1216,6 +1147,72 @@ error:
        return ret;
 }
 
+static bool chk_code_allowed(u16 code_to_probe)
+{
+       static const bool codes[] = {
+               /* 32 bit ALU operations */
+               [BPF_ALU | BPF_ADD | BPF_K] = true,
+               [BPF_ALU | BPF_ADD | BPF_X] = true,
+               [BPF_ALU | BPF_SUB | BPF_K] = true,
+               [BPF_ALU | BPF_SUB | BPF_X] = true,
+               [BPF_ALU | BPF_MUL | BPF_K] = true,
+               [BPF_ALU | BPF_MUL | BPF_X] = true,
+               [BPF_ALU | BPF_DIV | BPF_K] = true,
+               [BPF_ALU | BPF_DIV | BPF_X] = true,
+               [BPF_ALU | BPF_MOD | BPF_K] = true,
+               [BPF_ALU | BPF_MOD | BPF_X] = true,
+               [BPF_ALU | BPF_AND | BPF_K] = true,
+               [BPF_ALU | BPF_AND | BPF_X] = true,
+               [BPF_ALU | BPF_OR | BPF_K] = true,
+               [BPF_ALU | BPF_OR | BPF_X] = true,
+               [BPF_ALU | BPF_XOR | BPF_K] = true,
+               [BPF_ALU | BPF_XOR | BPF_X] = true,
+               [BPF_ALU | BPF_LSH | BPF_K] = true,
+               [BPF_ALU | BPF_LSH | BPF_X] = true,
+               [BPF_ALU | BPF_RSH | BPF_K] = true,
+               [BPF_ALU | BPF_RSH | BPF_X] = true,
+               [BPF_ALU | BPF_NEG] = true,
+               /* Load instructions */
+               [BPF_LD | BPF_W | BPF_ABS] = true,
+               [BPF_LD | BPF_H | BPF_ABS] = true,
+               [BPF_LD | BPF_B | BPF_ABS] = true,
+               [BPF_LD | BPF_W | BPF_LEN] = true,
+               [BPF_LD | BPF_W | BPF_IND] = true,
+               [BPF_LD | BPF_H | BPF_IND] = true,
+               [BPF_LD | BPF_B | BPF_IND] = true,
+               [BPF_LD | BPF_IMM] = true,
+               [BPF_LD | BPF_MEM] = true,
+               [BPF_LDX | BPF_W | BPF_LEN] = true,
+               [BPF_LDX | BPF_B | BPF_MSH] = true,
+               [BPF_LDX | BPF_IMM] = true,
+               [BPF_LDX | BPF_MEM] = true,
+               /* Store instructions */
+               [BPF_ST] = true,
+               [BPF_STX] = true,
+               /* Misc instructions */
+               [BPF_MISC | BPF_TAX] = true,
+               [BPF_MISC | BPF_TXA] = true,
+               /* Return instructions */
+               [BPF_RET | BPF_K] = true,
+               [BPF_RET | BPF_A] = true,
+               /* Jump instructions */
+               [BPF_JMP | BPF_JA] = true,
+               [BPF_JMP | BPF_JEQ | BPF_K] = true,
+               [BPF_JMP | BPF_JEQ | BPF_X] = true,
+               [BPF_JMP | BPF_JGE | BPF_K] = true,
+               [BPF_JMP | BPF_JGE | BPF_X] = true,
+               [BPF_JMP | BPF_JGT | BPF_K] = true,
+               [BPF_JMP | BPF_JGT | BPF_X] = true,
+               [BPF_JMP | BPF_JSET | BPF_K] = true,
+               [BPF_JMP | BPF_JSET | BPF_X] = true,
+       };
+
+       if (code_to_probe >= ARRAY_SIZE(codes))
+               return false;
+
+       return codes[code_to_probe];
+}
+
 /**
  *     sk_chk_filter - verify socket filter code
  *     @filter: filter to verify
@@ -1232,153 +1229,76 @@ error:
  */
 int sk_chk_filter(struct sock_filter *filter, unsigned int flen)
 {
-       /*
-        * Valid instructions are initialized to non-0.
-        * Invalid instructions are initialized to 0.
-        */
-       static const u8 codes[] = {
-               [BPF_ALU|BPF_ADD|BPF_K]  = BPF_S_ALU_ADD_K,
-               [BPF_ALU|BPF_ADD|BPF_X]  = BPF_S_ALU_ADD_X,
-               [BPF_ALU|BPF_SUB|BPF_K]  = BPF_S_ALU_SUB_K,
-               [BPF_ALU|BPF_SUB|BPF_X]  = BPF_S_ALU_SUB_X,
-               [BPF_ALU|BPF_MUL|BPF_K]  = BPF_S_ALU_MUL_K,
-               [BPF_ALU|BPF_MUL|BPF_X]  = BPF_S_ALU_MUL_X,
-               [BPF_ALU|BPF_DIV|BPF_X]  = BPF_S_ALU_DIV_X,
-               [BPF_ALU|BPF_MOD|BPF_K]  = BPF_S_ALU_MOD_K,
-               [BPF_ALU|BPF_MOD|BPF_X]  = BPF_S_ALU_MOD_X,
-               [BPF_ALU|BPF_AND|BPF_K]  = BPF_S_ALU_AND_K,
-               [BPF_ALU|BPF_AND|BPF_X]  = BPF_S_ALU_AND_X,
-               [BPF_ALU|BPF_OR|BPF_K]   = BPF_S_ALU_OR_K,
-               [BPF_ALU|BPF_OR|BPF_X]   = BPF_S_ALU_OR_X,
-               [BPF_ALU|BPF_XOR|BPF_K]  = BPF_S_ALU_XOR_K,
-               [BPF_ALU|BPF_XOR|BPF_X]  = BPF_S_ALU_XOR_X,
-               [BPF_ALU|BPF_LSH|BPF_K]  = BPF_S_ALU_LSH_K,
-               [BPF_ALU|BPF_LSH|BPF_X]  = BPF_S_ALU_LSH_X,
-               [BPF_ALU|BPF_RSH|BPF_K]  = BPF_S_ALU_RSH_K,
-               [BPF_ALU|BPF_RSH|BPF_X]  = BPF_S_ALU_RSH_X,
-               [BPF_ALU|BPF_NEG]        = BPF_S_ALU_NEG,
-               [BPF_LD|BPF_W|BPF_ABS]   = BPF_S_LD_W_ABS,
-               [BPF_LD|BPF_H|BPF_ABS]   = BPF_S_LD_H_ABS,
-               [BPF_LD|BPF_B|BPF_ABS]   = BPF_S_LD_B_ABS,
-               [BPF_LD|BPF_W|BPF_LEN]   = BPF_S_LD_W_LEN,
-               [BPF_LD|BPF_W|BPF_IND]   = BPF_S_LD_W_IND,
-               [BPF_LD|BPF_H|BPF_IND]   = BPF_S_LD_H_IND,
-               [BPF_LD|BPF_B|BPF_IND]   = BPF_S_LD_B_IND,
-               [BPF_LD|BPF_IMM]         = BPF_S_LD_IMM,
-               [BPF_LDX|BPF_W|BPF_LEN]  = BPF_S_LDX_W_LEN,
-               [BPF_LDX|BPF_B|BPF_MSH]  = BPF_S_LDX_B_MSH,
-               [BPF_LDX|BPF_IMM]        = BPF_S_LDX_IMM,
-               [BPF_MISC|BPF_TAX]       = BPF_S_MISC_TAX,
-               [BPF_MISC|BPF_TXA]       = BPF_S_MISC_TXA,
-               [BPF_RET|BPF_K]          = BPF_S_RET_K,
-               [BPF_RET|BPF_A]          = BPF_S_RET_A,
-               [BPF_ALU|BPF_DIV|BPF_K]  = BPF_S_ALU_DIV_K,
-               [BPF_LD|BPF_MEM]         = BPF_S_LD_MEM,
-               [BPF_LDX|BPF_MEM]        = BPF_S_LDX_MEM,
-               [BPF_ST]                 = BPF_S_ST,
-               [BPF_STX]                = BPF_S_STX,
-               [BPF_JMP|BPF_JA]         = BPF_S_JMP_JA,
-               [BPF_JMP|BPF_JEQ|BPF_K]  = BPF_S_JMP_JEQ_K,
-               [BPF_JMP|BPF_JEQ|BPF_X]  = BPF_S_JMP_JEQ_X,
-               [BPF_JMP|BPF_JGE|BPF_K]  = BPF_S_JMP_JGE_K,
-               [BPF_JMP|BPF_JGE|BPF_X]  = BPF_S_JMP_JGE_X,
-               [BPF_JMP|BPF_JGT|BPF_K]  = BPF_S_JMP_JGT_K,
-               [BPF_JMP|BPF_JGT|BPF_X]  = BPF_S_JMP_JGT_X,
-               [BPF_JMP|BPF_JSET|BPF_K] = BPF_S_JMP_JSET_K,
-               [BPF_JMP|BPF_JSET|BPF_X] = BPF_S_JMP_JSET_X,
-       };
-       int pc;
        bool anc_found;
+       int pc;
 
        if (flen == 0 || flen > BPF_MAXINSNS)
                return -EINVAL;
 
-       /* check the filter code now */
+       /* Check the filter code now */
        for (pc = 0; pc < flen; pc++) {
                struct sock_filter *ftest = &filter[pc];
-               u16 code = ftest->code;
 
-               if (code >= ARRAY_SIZE(codes))
-                       return -EINVAL;
-               code = codes[code];
-               if (!code)
+               /* May we actually operate on this code? */
+               if (!chk_code_allowed(ftest->code))
                        return -EINVAL;
+
                /* Some instructions need special checks */
-               switch (code) {
-               case BPF_S_ALU_DIV_K:
-               case BPF_S_ALU_MOD_K:
-                       /* check for division by zero */
+               switch (ftest->code) {
+               case BPF_ALU | BPF_DIV | BPF_K:
+               case BPF_ALU | BPF_MOD | BPF_K:
+                       /* Check for division by zero */
                        if (ftest->k == 0)
                                return -EINVAL;
                        break;
-               case BPF_S_LD_MEM:
-               case BPF_S_LDX_MEM:
-               case BPF_S_ST:
-               case BPF_S_STX:
-                       /* check for invalid memory addresses */
+               case BPF_LD | BPF_MEM:
+               case BPF_LDX | BPF_MEM:
+               case BPF_ST:
+               case BPF_STX:
+                       /* Check for invalid memory addresses */
                        if (ftest->k >= BPF_MEMWORDS)
                                return -EINVAL;
                        break;
-               case BPF_S_JMP_JA:
-                       /*
-                        * Note, the large ftest->k might cause loops.
+               case BPF_JMP | BPF_JA:
+                       /* Note, the large ftest->k might cause loops.
                         * Compare this with conditional jumps below,
                         * where offsets are limited. --ANK (981016)
                         */
-                       if (ftest->k >= (unsigned int)(flen-pc-1))
+                       if (ftest->k >= (unsigned int)(flen - pc - 1))
                                return -EINVAL;
                        break;
-               case BPF_S_JMP_JEQ_K:
-               case BPF_S_JMP_JEQ_X:
-               case BPF_S_JMP_JGE_K:
-               case BPF_S_JMP_JGE_X:
-               case BPF_S_JMP_JGT_K:
-               case BPF_S_JMP_JGT_X:
-               case BPF_S_JMP_JSET_X:
-               case BPF_S_JMP_JSET_K:
-                       /* for conditionals both must be safe */
+               case BPF_JMP | BPF_JEQ | BPF_K:
+               case BPF_JMP | BPF_JEQ | BPF_X:
+               case BPF_JMP | BPF_JGE | BPF_K:
+               case BPF_JMP | BPF_JGE | BPF_X:
+               case BPF_JMP | BPF_JGT | BPF_K:
+               case BPF_JMP | BPF_JGT | BPF_X:
+               case BPF_JMP | BPF_JSET | BPF_K:
+               case BPF_JMP | BPF_JSET | BPF_X:
+                       /* Both conditionals must be safe */
                        if (pc + ftest->jt + 1 >= flen ||
                            pc + ftest->jf + 1 >= flen)
                                return -EINVAL;
                        break;
-               case BPF_S_LD_W_ABS:
-               case BPF_S_LD_H_ABS:
-               case BPF_S_LD_B_ABS:
+               case BPF_LD | BPF_W | BPF_ABS:
+               case BPF_LD | BPF_H | BPF_ABS:
+               case BPF_LD | BPF_B | BPF_ABS:
                        anc_found = false;
-#define ANCILLARY(CODE) case SKF_AD_OFF + SKF_AD_##CODE:       \
-                               code = BPF_S_ANC_##CODE;        \
-                               anc_found = true;               \
-                               break
-                       switch (ftest->k) {
-                       ANCILLARY(PROTOCOL);
-                       ANCILLARY(PKTTYPE);
-                       ANCILLARY(IFINDEX);
-                       ANCILLARY(NLATTR);
-                       ANCILLARY(NLATTR_NEST);
-                       ANCILLARY(MARK);
-                       ANCILLARY(QUEUE);
-                       ANCILLARY(HATYPE);
-                       ANCILLARY(RXHASH);
-                       ANCILLARY(CPU);
-                       ANCILLARY(ALU_XOR_X);
-                       ANCILLARY(VLAN_TAG);
-                       ANCILLARY(VLAN_TAG_PRESENT);
-                       ANCILLARY(PAY_OFFSET);
-                       }
-
-                       /* ancillary operation unknown or unsupported */
+                       if (bpf_anc_helper(ftest) & BPF_ANC)
+                               anc_found = true;
+                       /* Ancillary operation unknown or unsupported */
                        if (anc_found == false && ftest->k >= SKF_AD_OFF)
                                return -EINVAL;
                }
-               ftest->code = code;
        }
 
-       /* last instruction must be a RET code */
+       /* Last instruction must be a RET code */
        switch (filter[flen - 1].code) {
-       case BPF_S_RET_K:
-       case BPF_S_RET_A:
+       case BPF_RET | BPF_K:
+       case BPF_RET | BPF_A:
                return check_load_and_stores(filter, flen);
        }
+
        return -EINVAL;
 }
 EXPORT_SYMBOL(sk_chk_filter);
@@ -1423,7 +1343,7 @@ static void sk_filter_release_rcu(struct rcu_head *rcu)
        struct sk_filter *fp = container_of(rcu, struct sk_filter, rcu);
 
        sk_release_orig_filter(fp);
-       bpf_jit_free(fp);
+       sk_filter_free(fp);
 }
 
 /**
@@ -1461,7 +1381,7 @@ static struct sk_filter *__sk_migrate_realloc(struct sk_filter *fp,
 
        fp_new = sock_kmalloc(sk, len, GFP_KERNEL);
        if (fp_new) {
-               memcpy(fp_new, fp, sizeof(struct sk_filter));
+               *fp_new = *fp;
                /* As we're kepping orig_prog in fp_new along,
                 * we need to make sure we're not evicting it
                 * from the old fp.
@@ -1478,7 +1398,7 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp,
 {
        struct sock_filter *old_prog;
        struct sk_filter *old_fp;
-       int i, err, new_len, old_len = fp->len;
+       int err, new_len, old_len = fp->len;
 
        /* We are free to overwrite insns et al right here as it
         * won't be used at this point in time anymore internally
@@ -1488,13 +1408,6 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp,
        BUILD_BUG_ON(sizeof(struct sock_filter) !=
                     sizeof(struct sock_filter_int));
 
-       /* For now, we need to unfiddle BPF_S_* identifiers in place.
-        * This can sooner or later on be subject to removal, e.g. when
-        * JITs have been converted.
-        */
-       for (i = 0; i < fp->len; i++)
-               sk_decode_filter(&fp->insns[i], &fp->insns[i]);
-
        /* Conversion cannot happen on overlapping memory areas,
         * so we need to keep the user BPF around until the 2nd
         * pass. At this time, the user BPF is stored in fp->insns.
@@ -1523,7 +1436,6 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp,
                goto out_err_free;
        }
 
-       fp->bpf_func = sk_run_filter_int_skb;
        fp->len = new_len;
 
        /* 2nd pass: remap sock_filter insns into sock_filter_int insns. */
@@ -1536,6 +1448,8 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp,
                 */
                goto out_err_free;
 
+       sk_filter_select_runtime(fp);
+
        kfree(old_prog);
        return fp;
 
@@ -1550,6 +1464,33 @@ out_err:
        return ERR_PTR(err);
 }
 
+void __weak bpf_int_jit_compile(struct sk_filter *prog)
+{
+}
+
+/**
+ *     sk_filter_select_runtime - select execution runtime for BPF program
+ *     @fp: sk_filter populated with internal BPF program
+ *
+ * try to JIT internal BPF program, if JIT is not available select interpreter
+ * BPF program will be executed via SK_RUN_FILTER() macro
+ */
+void sk_filter_select_runtime(struct sk_filter *fp)
+{
+       fp->bpf_func = (void *) __sk_run_filter;
+
+       /* Probe if internal BPF can be JITed */
+       bpf_int_jit_compile(fp);
+}
+EXPORT_SYMBOL_GPL(sk_filter_select_runtime);
+
+/* free internal BPF program */
+void sk_filter_free(struct sk_filter *fp)
+{
+       bpf_jit_free(fp);
+}
+EXPORT_SYMBOL_GPL(sk_filter_free);
+
 static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp,
                                             struct sock *sk)
 {
@@ -1592,7 +1533,7 @@ static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp,
  * a negative errno code is returned. On success the return is zero.
  */
 int sk_unattached_filter_create(struct sk_filter **pfp,
-                               struct sock_fprog *fprog)
+                               struct sock_fprog_kern *fprog)
 {
        unsigned int fsize = sk_filter_proglen(fprog);
        struct sk_filter *fp;
@@ -1713,83 +1654,6 @@ int sk_detach_filter(struct sock *sk)
 }
 EXPORT_SYMBOL_GPL(sk_detach_filter);
 
-void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to)
-{
-       static const u16 decodes[] = {
-               [BPF_S_ALU_ADD_K]       = BPF_ALU|BPF_ADD|BPF_K,
-               [BPF_S_ALU_ADD_X]       = BPF_ALU|BPF_ADD|BPF_X,
-               [BPF_S_ALU_SUB_K]       = BPF_ALU|BPF_SUB|BPF_K,
-               [BPF_S_ALU_SUB_X]       = BPF_ALU|BPF_SUB|BPF_X,
-               [BPF_S_ALU_MUL_K]       = BPF_ALU|BPF_MUL|BPF_K,
-               [BPF_S_ALU_MUL_X]       = BPF_ALU|BPF_MUL|BPF_X,
-               [BPF_S_ALU_DIV_X]       = BPF_ALU|BPF_DIV|BPF_X,
-               [BPF_S_ALU_MOD_K]       = BPF_ALU|BPF_MOD|BPF_K,
-               [BPF_S_ALU_MOD_X]       = BPF_ALU|BPF_MOD|BPF_X,
-               [BPF_S_ALU_AND_K]       = BPF_ALU|BPF_AND|BPF_K,
-               [BPF_S_ALU_AND_X]       = BPF_ALU|BPF_AND|BPF_X,
-               [BPF_S_ALU_OR_K]        = BPF_ALU|BPF_OR|BPF_K,
-               [BPF_S_ALU_OR_X]        = BPF_ALU|BPF_OR|BPF_X,
-               [BPF_S_ALU_XOR_K]       = BPF_ALU|BPF_XOR|BPF_K,
-               [BPF_S_ALU_XOR_X]       = BPF_ALU|BPF_XOR|BPF_X,
-               [BPF_S_ALU_LSH_K]       = BPF_ALU|BPF_LSH|BPF_K,
-               [BPF_S_ALU_LSH_X]       = BPF_ALU|BPF_LSH|BPF_X,
-               [BPF_S_ALU_RSH_K]       = BPF_ALU|BPF_RSH|BPF_K,
-               [BPF_S_ALU_RSH_X]       = BPF_ALU|BPF_RSH|BPF_X,
-               [BPF_S_ALU_NEG]         = BPF_ALU|BPF_NEG,
-               [BPF_S_LD_W_ABS]        = BPF_LD|BPF_W|BPF_ABS,
-               [BPF_S_LD_H_ABS]        = BPF_LD|BPF_H|BPF_ABS,
-               [BPF_S_LD_B_ABS]        = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_PROTOCOL]    = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_PKTTYPE]     = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_IFINDEX]     = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_NLATTR]      = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_NLATTR_NEST] = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_MARK]        = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_QUEUE]       = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_HATYPE]      = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_RXHASH]      = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_CPU]         = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_ALU_XOR_X]   = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_VLAN_TAG]    = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_VLAN_TAG_PRESENT] = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_PAY_OFFSET]  = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_LD_W_LEN]        = BPF_LD|BPF_W|BPF_LEN,
-               [BPF_S_LD_W_IND]        = BPF_LD|BPF_W|BPF_IND,
-               [BPF_S_LD_H_IND]        = BPF_LD|BPF_H|BPF_IND,
-               [BPF_S_LD_B_IND]        = BPF_LD|BPF_B|BPF_IND,
-               [BPF_S_LD_IMM]          = BPF_LD|BPF_IMM,
-               [BPF_S_LDX_W_LEN]       = BPF_LDX|BPF_W|BPF_LEN,
-               [BPF_S_LDX_B_MSH]       = BPF_LDX|BPF_B|BPF_MSH,
-               [BPF_S_LDX_IMM]         = BPF_LDX|BPF_IMM,
-               [BPF_S_MISC_TAX]        = BPF_MISC|BPF_TAX,
-               [BPF_S_MISC_TXA]        = BPF_MISC|BPF_TXA,
-               [BPF_S_RET_K]           = BPF_RET|BPF_K,
-               [BPF_S_RET_A]           = BPF_RET|BPF_A,
-               [BPF_S_ALU_DIV_K]       = BPF_ALU|BPF_DIV|BPF_K,
-               [BPF_S_LD_MEM]          = BPF_LD|BPF_MEM,
-               [BPF_S_LDX_MEM]         = BPF_LDX|BPF_MEM,
-               [BPF_S_ST]              = BPF_ST,
-               [BPF_S_STX]             = BPF_STX,
-               [BPF_S_JMP_JA]          = BPF_JMP|BPF_JA,
-               [BPF_S_JMP_JEQ_K]       = BPF_JMP|BPF_JEQ|BPF_K,
-               [BPF_S_JMP_JEQ_X]       = BPF_JMP|BPF_JEQ|BPF_X,
-               [BPF_S_JMP_JGE_K]       = BPF_JMP|BPF_JGE|BPF_K,
-               [BPF_S_JMP_JGE_X]       = BPF_JMP|BPF_JGE|BPF_X,
-               [BPF_S_JMP_JGT_K]       = BPF_JMP|BPF_JGT|BPF_K,
-               [BPF_S_JMP_JGT_X]       = BPF_JMP|BPF_JGT|BPF_X,
-               [BPF_S_JMP_JSET_K]      = BPF_JMP|BPF_JSET|BPF_K,
-               [BPF_S_JMP_JSET_X]      = BPF_JMP|BPF_JSET|BPF_X,
-       };
-       u16 code;
-
-       code = filt->code;
-
-       to->code = decodes[code];
-       to->jt = filt->jt;
-       to->jf = filt->jf;
-       to->k = filt->k;
-}
-
 int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf,
                  unsigned int len)
 {
index 7c8ffd97496175c60d3947019bfd138bb9746938..85b62691f4f2d18b9b39bc9c610d1916f8f09273 100644 (file)
@@ -273,7 +273,7 @@ static void cleanup_net(struct work_struct *work)
 {
        const struct pernet_operations *ops;
        struct net *net, *tmp;
-       LIST_HEAD(net_kill_list);
+       struct list_head net_kill_list;
        LIST_HEAD(net_exit_list);
 
        /* Atomically snapshot the list of namespaces to cleanup */
index 0304f981f7ffa5f005b28f08f17ff8264c41bb65..fc17a9d309ac028fc61ac7db6e35a05c77513a24 100644 (file)
@@ -573,7 +573,7 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
                   is_zero_ether_addr(pkt_dev->src_mac) ?
                             pkt_dev->odev->dev_addr : pkt_dev->src_mac);
 
-       seq_printf(seq, "dst_mac: ");
+       seq_puts(seq, "dst_mac: ");
        seq_printf(seq, "%pM\n", pkt_dev->dst_mac);
 
        seq_printf(seq,
@@ -588,7 +588,7 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
 
        if (pkt_dev->nr_labels) {
                unsigned int i;
-               seq_printf(seq, "     mpls: ");
+               seq_puts(seq, "     mpls: ");
                for (i = 0; i < pkt_dev->nr_labels; i++)
                        seq_printf(seq, "%08x%s", ntohl(pkt_dev->labels[i]),
                                   i == pkt_dev->nr_labels-1 ? "\n" : ", ");
@@ -613,67 +613,67 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
        if (pkt_dev->node >= 0)
                seq_printf(seq, "     node: %d\n", pkt_dev->node);
 
-       seq_printf(seq, "     Flags: ");
+       seq_puts(seq, "     Flags: ");
 
        if (pkt_dev->flags & F_IPV6)
-               seq_printf(seq, "IPV6  ");
+               seq_puts(seq, "IPV6  ");
 
        if (pkt_dev->flags & F_IPSRC_RND)
-               seq_printf(seq, "IPSRC_RND  ");
+               seq_puts(seq, "IPSRC_RND  ");
 
        if (pkt_dev->flags & F_IPDST_RND)
-               seq_printf(seq, "IPDST_RND  ");
+               seq_puts(seq, "IPDST_RND  ");
 
        if (pkt_dev->flags & F_TXSIZE_RND)
-               seq_printf(seq, "TXSIZE_RND  ");
+               seq_puts(seq, "TXSIZE_RND  ");
 
        if (pkt_dev->flags & F_UDPSRC_RND)
-               seq_printf(seq, "UDPSRC_RND  ");
+               seq_puts(seq, "UDPSRC_RND  ");
 
        if (pkt_dev->flags & F_UDPDST_RND)
-               seq_printf(seq, "UDPDST_RND  ");
+               seq_puts(seq, "UDPDST_RND  ");
 
        if (pkt_dev->flags & F_UDPCSUM)
-               seq_printf(seq, "UDPCSUM  ");
+               seq_puts(seq, "UDPCSUM  ");
 
        if (pkt_dev->flags & F_MPLS_RND)
-               seq_printf(seq,  "MPLS_RND  ");
+               seq_puts(seq,  "MPLS_RND  ");
 
        if (pkt_dev->flags & F_QUEUE_MAP_RND)
-               seq_printf(seq,  "QUEUE_MAP_RND  ");
+               seq_puts(seq,  "QUEUE_MAP_RND  ");
 
        if (pkt_dev->flags & F_QUEUE_MAP_CPU)
-               seq_printf(seq,  "QUEUE_MAP_CPU  ");
+               seq_puts(seq,  "QUEUE_MAP_CPU  ");
 
        if (pkt_dev->cflows) {
                if (pkt_dev->flags & F_FLOW_SEQ)
-                       seq_printf(seq,  "FLOW_SEQ  "); /*in sequence flows*/
+                       seq_puts(seq,  "FLOW_SEQ  "); /*in sequence flows*/
                else
-                       seq_printf(seq,  "FLOW_RND  ");
+                       seq_puts(seq,  "FLOW_RND  ");
        }
 
 #ifdef CONFIG_XFRM
        if (pkt_dev->flags & F_IPSEC_ON) {
-               seq_printf(seq,  "IPSEC  ");
+               seq_puts(seq,  "IPSEC  ");
                if (pkt_dev->spi)
                        seq_printf(seq, "spi:%u", pkt_dev->spi);
        }
 #endif
 
        if (pkt_dev->flags & F_MACSRC_RND)
-               seq_printf(seq, "MACSRC_RND  ");
+               seq_puts(seq, "MACSRC_RND  ");
 
        if (pkt_dev->flags & F_MACDST_RND)
-               seq_printf(seq, "MACDST_RND  ");
+               seq_puts(seq, "MACDST_RND  ");
 
        if (pkt_dev->flags & F_VID_RND)
-               seq_printf(seq, "VID_RND  ");
+               seq_puts(seq, "VID_RND  ");
 
        if (pkt_dev->flags & F_SVID_RND)
-               seq_printf(seq, "SVID_RND  ");
+               seq_puts(seq, "SVID_RND  ");
 
        if (pkt_dev->flags & F_NODE)
-               seq_printf(seq, "NODE_ALLOC  ");
+               seq_puts(seq, "NODE_ALLOC  ");
 
        seq_puts(seq, "\n");
 
@@ -716,7 +716,7 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
        if (pkt_dev->result[0])
                seq_printf(seq, "Result: %s\n", pkt_dev->result);
        else
-               seq_printf(seq, "Result: Idle\n");
+               seq_puts(seq, "Result: Idle\n");
 
        return 0;
 }
@@ -1735,14 +1735,14 @@ static int pktgen_thread_show(struct seq_file *seq, void *v)
 
        BUG_ON(!t);
 
-       seq_printf(seq, "Running: ");
+       seq_puts(seq, "Running: ");
 
        if_lock(t);
        list_for_each_entry(pkt_dev, &t->if_list, list)
                if (pkt_dev->running)
                        seq_printf(seq, "%s ", pkt_dev->odevname);
 
-       seq_printf(seq, "\nStopped: ");
+       seq_puts(seq, "\nStopped: ");
 
        list_for_each_entry(pkt_dev, &t->if_list, list)
                if (!pkt_dev->running)
@@ -1751,7 +1751,7 @@ static int pktgen_thread_show(struct seq_file *seq, void *v)
        if (t->result[0])
                seq_printf(seq, "\nResult: %s\n", t->result);
        else
-               seq_printf(seq, "\nResult: NA\n");
+               seq_puts(seq, "\nResult: NA\n");
 
        if_unlock(t);
 
index eaba0f68f8608618870a7009f27352523bee108f..d3027a73fd4bbc152f13011cb09335af365f19dc 100644 (file)
@@ -88,7 +88,7 @@ EXPORT_SYMBOL_GPL(ptp_classify_raw);
 
 void __init ptp_classifier_init(void)
 {
-       static struct sock_filter ptp_filter[] = {
+       static struct sock_filter ptp_filter[] __initdata = {
                { 0x28,  0,  0, 0x0000000c },
                { 0x15,  0, 12, 0x00000800 },
                { 0x30,  0,  0, 0x00000017 },
@@ -133,7 +133,7 @@ void __init ptp_classifier_init(void)
                { 0x16,  0,  0, 0x00000000 },
                { 0x06,  0,  0, 0x00000000 },
        };
-       struct sock_fprog ptp_prog = {
+       struct sock_fprog_kern ptp_prog = {
                .len = ARRAY_SIZE(ptp_filter), .filter = ptp_filter,
        };
 
index 2d8d8fcfa060c51e2f6cbe240aff3d01b3f964cc..1063996f8317fb95fea964e095ae2e372ceb74be 100644 (file)
@@ -798,8 +798,8 @@ static inline int rtnl_vfinfo_size(const struct net_device *dev,
                size += num_vfs *
                        (nla_total_size(sizeof(struct ifla_vf_mac)) +
                         nla_total_size(sizeof(struct ifla_vf_vlan)) +
-                        nla_total_size(sizeof(struct ifla_vf_tx_rate)) +
-                        nla_total_size(sizeof(struct ifla_vf_spoofchk)));
+                        nla_total_size(sizeof(struct ifla_vf_spoofchk)) +
+                        nla_total_size(sizeof(struct ifla_vf_rate)));
                return size;
        } else
                return 0;
@@ -1065,6 +1065,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                        struct ifla_vf_info ivi;
                        struct ifla_vf_mac vf_mac;
                        struct ifla_vf_vlan vf_vlan;
+                       struct ifla_vf_rate vf_rate;
                        struct ifla_vf_tx_rate vf_tx_rate;
                        struct ifla_vf_spoofchk vf_spoofchk;
                        struct ifla_vf_link_state vf_linkstate;
@@ -1085,6 +1086,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                                break;
                        vf_mac.vf =
                                vf_vlan.vf =
+                               vf_rate.vf =
                                vf_tx_rate.vf =
                                vf_spoofchk.vf =
                                vf_linkstate.vf = ivi.vf;
@@ -1092,7 +1094,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                        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_tx_rate.rate = ivi.max_tx_rate;
+                       vf_rate.min_tx_rate = ivi.min_tx_rate;
+                       vf_rate.max_tx_rate = ivi.max_tx_rate;
                        vf_spoofchk.setting = ivi.spoofchk;
                        vf_linkstate.link_state = ivi.linkstate;
                        vf = nla_nest_start(skb, IFLA_VF_INFO);
@@ -1102,6 +1106,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                        }
                        if (nla_put(skb, IFLA_VF_MAC, sizeof(vf_mac), &vf_mac) ||
                            nla_put(skb, IFLA_VF_VLAN, sizeof(vf_vlan), &vf_vlan) ||
+                           nla_put(skb, IFLA_VF_RATE, sizeof(vf_rate),
+                                   &vf_rate) ||
                            nla_put(skb, IFLA_VF_TX_RATE, sizeof(vf_tx_rate),
                                    &vf_tx_rate) ||
                            nla_put(skb, IFLA_VF_SPOOFCHK, sizeof(vf_spoofchk),
@@ -1208,6 +1214,10 @@ static const struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = {
                                    .len = sizeof(struct ifla_vf_tx_rate) },
        [IFLA_VF_SPOOFCHK]      = { .type = NLA_BINARY,
                                    .len = sizeof(struct ifla_vf_spoofchk) },
+       [IFLA_VF_RATE]          = { .type = NLA_BINARY,
+                                   .len = sizeof(struct ifla_vf_rate) },
+       [IFLA_VF_LINK_STATE]    = { .type = NLA_BINARY,
+                                   .len = sizeof(struct ifla_vf_link_state) },
 };
 
 static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = {
@@ -1234,6 +1244,7 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
        struct nlattr *tb[IFLA_MAX+1];
        u32 ext_filter_mask = 0;
        int err;
+       int hdrlen;
 
        s_h = cb->args[0];
        s_idx = cb->args[1];
@@ -1241,8 +1252,17 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
        rcu_read_lock();
        cb->seq = net->dev_base_seq;
 
-       if (nlmsg_parse(cb->nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX,
-                       ifla_policy) >= 0) {
+       /* A hack to preserve kernel<->userspace interface.
+        * The correct header is ifinfomsg. It is consistent with rtnl_getlink.
+        * However, before Linux v3.9 the code here assumed rtgenmsg and that's
+        * what iproute2 < v3.9.0 used.
+        * We can detect the old iproute2. Even including the IFLA_EXT_MASK
+        * attribute, its netlink message is shorter than struct ifinfomsg.
+        */
+       hdrlen = nlmsg_len(cb->nlh) < sizeof(struct ifinfomsg) ?
+                sizeof(struct rtgenmsg) : sizeof(struct ifinfomsg);
+
+       if (nlmsg_parse(cb->nlh, hdrlen, tb, IFLA_MAX, ifla_policy) >= 0) {
 
                if (tb[IFLA_EXT_MASK])
                        ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
@@ -1367,11 +1387,29 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr *attr)
                }
                case IFLA_VF_TX_RATE: {
                        struct ifla_vf_tx_rate *ivt;
+                       struct ifla_vf_info ivf;
                        ivt = nla_data(vf);
                        err = -EOPNOTSUPP;
-                       if (ops->ndo_set_vf_tx_rate)
-                               err = ops->ndo_set_vf_tx_rate(dev, ivt->vf,
-                                                             ivt->rate);
+                       if (ops->ndo_get_vf_config)
+                               err = ops->ndo_get_vf_config(dev, ivt->vf,
+                                                            &ivf);
+                       if (err)
+                               break;
+                       err = -EOPNOTSUPP;
+                       if (ops->ndo_set_vf_rate)
+                               err = ops->ndo_set_vf_rate(dev, ivt->vf,
+                                                          ivf.min_tx_rate,
+                                                          ivt->rate);
+                       break;
+               }
+               case IFLA_VF_RATE: {
+                       struct ifla_vf_rate *ivt;
+                       ivt = nla_data(vf);
+                       err = -EOPNOTSUPP;
+                       if (ops->ndo_set_vf_rate)
+                               err = ops->ndo_set_vf_rate(dev, ivt->vf,
+                                                          ivt->min_tx_rate,
+                                                          ivt->max_tx_rate);
                        break;
                }
                case IFLA_VF_SPOOFCHK: {
@@ -1744,7 +1782,6 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh)
 
        ops->dellink(dev, &list_kill);
        unregister_netdevice_many(&list_kill);
-       list_del(&list_kill);
        return 0;
 }
 
@@ -2019,11 +2056,15 @@ replay:
                if (ops->newlink) {
                        err = ops->newlink(net, dev, tb, data);
                        /* Drivers should call free_netdev() in ->destructor
-                        * and unregister it on failure so that device could be
-                        * finally freed in rtnl_unlock.
+                        * and unregister it on failure after registration
+                        * so that device could be finally freed in rtnl_unlock.
                         */
-                       if (err < 0)
+                       if (err < 0) {
+                               /* If device is not registered at all, free it now */
+                               if (dev->reg_state == NETREG_UNINITIALIZED)
+                                       free_netdev(dev);
                                goto out;
+                       }
                } else {
                        err = register_netdevice(dev);
                        if (err < 0) {
@@ -2095,9 +2136,13 @@ static u16 rtnl_calcit(struct sk_buff *skb, struct nlmsghdr *nlh)
        struct nlattr *tb[IFLA_MAX+1];
        u32 ext_filter_mask = 0;
        u16 min_ifinfo_dump_size = 0;
+       int hdrlen;
+
+       /* Same kernel<->userspace interface hack as in rtnl_dump_ifinfo. */
+       hdrlen = nlmsg_len(nlh) < sizeof(struct ifinfomsg) ?
+                sizeof(struct rtgenmsg) : sizeof(struct ifinfomsg);
 
-       if (nlmsg_parse(nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX,
-                       ifla_policy) >= 0) {
+       if (nlmsg_parse(nlh, hdrlen, tb, IFLA_MAX, ifla_policy) >= 0) {
                if (tb[IFLA_EXT_MASK])
                        ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
        }
index 897da56f3affae13161d4610989667a82244c308..ba71212f0251218c3c41599838e36aa8e169fea7 100644 (file)
@@ -85,31 +85,6 @@ EXPORT_SYMBOL(secure_ipv6_port_ephemeral);
 #endif
 
 #ifdef CONFIG_INET
-__u32 secure_ip_id(__be32 daddr)
-{
-       u32 hash[MD5_DIGEST_WORDS];
-
-       net_secret_init();
-       hash[0] = (__force __u32) daddr;
-       hash[1] = net_secret[13];
-       hash[2] = net_secret[14];
-       hash[3] = net_secret[15];
-
-       md5_transform(hash, net_secret);
-
-       return hash[0];
-}
-
-__u32 secure_ipv6_id(const __be32 daddr[4])
-{
-       __u32 hash[4];
-
-       net_secret_init();
-       memcpy(hash, daddr, 16);
-       md5_transform(hash, net_secret);
-
-       return hash[0];
-}
 
 __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
                                 __be16 sport, __be16 dport)
index 8383b2bddeb923bd629da7d9eac2cdd1ff1d81bd..9cd5344fad73466e66c63f04efd4a2600074db4e 100644 (file)
@@ -689,12 +689,15 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
        new->ooo_okay           = old->ooo_okay;
        new->no_fcs             = old->no_fcs;
        new->encapsulation      = old->encapsulation;
+       new->encap_hdr_csum     = old->encap_hdr_csum;
+       new->csum_valid         = old->csum_valid;
+       new->csum_complete_sw   = old->csum_complete_sw;
 #ifdef CONFIG_XFRM
        new->sp                 = secpath_get(old->sp);
 #endif
        memcpy(new->cb, old->cb, sizeof(old->cb));
        new->csum               = old->csum;
-       new->local_df           = old->local_df;
+       new->ignore_df          = old->ignore_df;
        new->pkt_type           = old->pkt_type;
        new->ip_summed          = old->ip_summed;
        skb_copy_queue_mapping(new, old);
@@ -951,10 +954,13 @@ struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask)
 EXPORT_SYMBOL(skb_copy);
 
 /**
- *     __pskb_copy     -       create copy of an sk_buff with private head.
+ *     __pskb_copy_fclone      -  create copy of an sk_buff with private head.
  *     @skb: buffer to copy
  *     @headroom: headroom of new skb
  *     @gfp_mask: allocation priority
+ *     @fclone: if true allocate the copy of the skb from the fclone
+ *     cache instead of the head cache; it is recommended to set this
+ *     to true for the cases where the copy will likely be cloned
  *
  *     Make a copy of both an &sk_buff and part of its data, located
  *     in header. Fragmented data remain shared. This is used when
@@ -964,11 +970,12 @@ EXPORT_SYMBOL(skb_copy);
  *     The returned buffer has a reference count of 1.
  */
 
-struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask)
+struct sk_buff *__pskb_copy_fclone(struct sk_buff *skb, int headroom,
+                                  gfp_t gfp_mask, bool fclone)
 {
        unsigned int size = skb_headlen(skb) + headroom;
-       struct sk_buff *n = __alloc_skb(size, gfp_mask,
-                                       skb_alloc_rx_flag(skb), NUMA_NO_NODE);
+       int flags = skb_alloc_rx_flag(skb) | (fclone ? SKB_ALLOC_FCLONE : 0);
+       struct sk_buff *n = __alloc_skb(size, gfp_mask, flags, NUMA_NO_NODE);
 
        if (!n)
                goto out;
@@ -1008,7 +1015,7 @@ struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask)
 out:
        return n;
 }
-EXPORT_SYMBOL(__pskb_copy);
+EXPORT_SYMBOL(__pskb_copy_fclone);
 
 /**
  *     pskb_expand_head - reallocate header of &sk_buff
@@ -2881,12 +2888,14 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
        int pos;
        int dummy;
 
+       __skb_push(head_skb, doffset);
        proto = skb_network_protocol(head_skb, &dummy);
        if (unlikely(!proto))
                return ERR_PTR(-EINVAL);
 
-       csum = !!can_checksum_protocol(features, proto);
-       __skb_push(head_skb, doffset);
+       csum = !head_skb->encap_hdr_csum &&
+           !!can_checksum_protocol(features, proto);
+
        headroom = skb_headroom(head_skb);
        pos = skb_headlen(head_skb);
 
@@ -2983,6 +2992,8 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
                        nskb->csum = skb_copy_and_csum_bits(head_skb, offset,
                                                            skb_put(nskb, len),
                                                            len, 0);
+                       SKB_GSO_CB(nskb)->csum_start =
+                           skb_headroom(nskb) + offset;
                        continue;
                }
 
@@ -3052,6 +3063,8 @@ perform_csum_check:
                        nskb->csum = skb_checksum(nskb, doffset,
                                                  nskb->len - doffset, 0);
                        nskb->ip_summed = CHECKSUM_NONE;
+                       SKB_GSO_CB(nskb)->csum_start =
+                           skb_headroom(nskb) + doffset;
                }
        } while ((offset += len) < head_skb->len);
 
@@ -3913,7 +3926,7 @@ void skb_scrub_packet(struct sk_buff *skb, bool xnet)
        skb->tstamp.tv64 = 0;
        skb->pkt_type = PACKET_HOST;
        skb->skb_iif = 0;
-       skb->local_df = 0;
+       skb->ignore_df = 0;
        skb_dst_drop(skb);
        skb->mark = 0;
        secpath_reset(skb);
index 664ee4295b6f6ec38fb4f89d11c52eaa383a15d1..026e01f70274f8a5550af6a7a06fd6846735e4ae 100644 (file)
@@ -784,7 +784,7 @@ set_rcvbuf:
                break;
 
        case SO_NO_CHECK:
-               sk->sk_no_check = valbool;
+               sk->sk_no_check_tx = valbool;
                break;
 
        case SO_PRIORITY:
@@ -1064,7 +1064,7 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
                break;
 
        case SO_NO_CHECK:
-               v.val = sk->sk_no_check;
+               v.val = sk->sk_no_check_tx;
                break;
 
        case SO_PRIORITY:
diff --git a/net/core/tso.c b/net/core/tso.c
new file mode 100644 (file)
index 0000000..8c3203c
--- /dev/null
@@ -0,0 +1,77 @@
+#include <linux/export.h>
+#include <net/ip.h>
+#include <net/tso.h>
+
+/* Calculate expected number of TX descriptors */
+int tso_count_descs(struct sk_buff *skb)
+{
+       /* The Marvell Way */
+       return skb_shinfo(skb)->gso_segs * 2 + skb_shinfo(skb)->nr_frags;
+}
+EXPORT_SYMBOL(tso_count_descs);
+
+void tso_build_hdr(struct sk_buff *skb, char *hdr, struct tso_t *tso,
+                  int size, bool is_last)
+{
+       struct iphdr *iph;
+       struct tcphdr *tcph;
+       int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+       int mac_hdr_len = skb_network_offset(skb);
+
+       memcpy(hdr, skb->data, hdr_len);
+       iph = (struct iphdr *)(hdr + mac_hdr_len);
+       iph->id = htons(tso->ip_id);
+       iph->tot_len = htons(size + hdr_len - mac_hdr_len);
+       tcph = (struct tcphdr *)(hdr + skb_transport_offset(skb));
+       tcph->seq = htonl(tso->tcp_seq);
+       tso->ip_id++;
+
+       if (!is_last) {
+               /* Clear all special flags for not last packet */
+               tcph->psh = 0;
+               tcph->fin = 0;
+               tcph->rst = 0;
+       }
+}
+EXPORT_SYMBOL(tso_build_hdr);
+
+void tso_build_data(struct sk_buff *skb, struct tso_t *tso, int size)
+{
+       tso->tcp_seq += size;
+       tso->size -= size;
+       tso->data += size;
+
+       if ((tso->size == 0) &&
+           (tso->next_frag_idx < skb_shinfo(skb)->nr_frags)) {
+               skb_frag_t *frag = &skb_shinfo(skb)->frags[tso->next_frag_idx];
+
+               /* Move to next segment */
+               tso->size = frag->size;
+               tso->data = page_address(frag->page.p) + frag->page_offset;
+               tso->next_frag_idx++;
+       }
+}
+EXPORT_SYMBOL(tso_build_data);
+
+void tso_start(struct sk_buff *skb, struct tso_t *tso)
+{
+       int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+
+       tso->ip_id = ntohs(ip_hdr(skb)->id);
+       tso->tcp_seq = ntohl(tcp_hdr(skb)->seq);
+       tso->next_frag_idx = 0;
+
+       /* Build first data */
+       tso->size = skb_headlen(skb) - hdr_len;
+       tso->data = skb->data + hdr_len;
+       if ((tso->size == 0) &&
+           (tso->next_frag_idx < skb_shinfo(skb)->nr_frags)) {
+               skb_frag_t *frag = &skb_shinfo(skb)->frags[tso->next_frag_idx];
+
+               /* Move to next segment */
+               tso->size = frag->size;
+               tso->data = page_address(frag->page.p) + frag->page_offset;
+               tso->next_frag_idx++;
+       }
+}
+EXPORT_SYMBOL(tso_start);
index 22b5d818b2001b177b765cbb67eb2551e87502ce..6ca645c4b48e8b56a88f7d549b073fb6dc82eeb7 100644 (file)
@@ -1024,7 +1024,6 @@ static struct inet_protosw dccp_v4_protosw = {
        .protocol       = IPPROTO_DCCP,
        .prot           = &dccp_v4_prot,
        .ops            = &inet_dccp_ops,
-       .no_check       = 0,
        .flags          = INET_PROTOSW_ICSK,
 };
 
index eb892b4f48144966e47f386108942f51b8b85e50..de2c1e7193057dee2e994386f5bb685e05b47163 100644 (file)
@@ -1084,14 +1084,15 @@ EXPORT_SYMBOL_GPL(dccp_shutdown);
 
 static inline int dccp_mib_init(void)
 {
-       return snmp_mib_init((void __percpu **)dccp_statistics,
-                            sizeof(struct dccp_mib),
-                            __alignof__(struct dccp_mib));
+       dccp_statistics = alloc_percpu(struct dccp_mib);
+       if (!dccp_statistics)
+               return -ENOMEM;
+       return 0;
 }
 
 static inline void dccp_mib_exit(void)
 {
-       snmp_mib_free((void __percpu **)dccp_statistics);
+       free_percpu(dccp_statistics);
 }
 
 static int thash_entries;
index 607ab71b5a0cb3af65067e7d69badb862d5bfb5a..53731e45403c83ba2edf48da01e71aeda48359b0 100644 (file)
@@ -20,6 +20,7 @@
 
 /* Boundary values */
 static int             zero     = 0,
+                       one      = 1,
                        u8_max   = 0xFF;
 static unsigned long   seqw_min = DCCPF_SEQ_WMIN,
                        seqw_max = 0xFFFFFFFF;          /* maximum on 32 bit */
@@ -58,7 +59,7 @@ static struct ctl_table dccp_default_table[] = {
                .maxlen         = sizeof(sysctl_dccp_request_retries),
                .mode           = 0644,
                .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &zero,
+               .extra1         = &one,
                .extra2         = &u8_max,
        },
        {
index 16f0b223102e6619b3b3430a108be740e2e7b373..1cd46a345cb04387a50843a251637b6e3cbd7501 100644 (file)
@@ -280,7 +280,7 @@ static ktime_t dccp_timestamp_seed;
  */
 u32 dccp_timestamp(void)
 {
-       s64 delta = ktime_us_delta(ktime_get_real(), dccp_timestamp_seed);
+       u64 delta = (u64)ktime_us_delta(ktime_get_real(), dccp_timestamp_seed);
 
        do_div(delta, 10);
        return delta;
index 4c04848953bdb4caddeae9debd195ea3d004ee0d..ae011b46c0710fc96204deda6bddb1d912d78f22 100644 (file)
@@ -481,7 +481,7 @@ static struct sock *dn_alloc_sock(struct net *net, struct socket *sock, gfp_t gf
 
        sk->sk_backlog_rcv = dn_nsp_backlog_rcv;
        sk->sk_destruct    = dn_destruct;
-       sk->sk_no_check    = 1;
+       sk->sk_no_check_tx = 1;
        sk->sk_family      = PF_DECnet;
        sk->sk_protocol    = 0;
        sk->sk_allocation  = gfp;
index e7b6d53eef88d250d6a93e3a75f26c3a6cad4cba..9acec61f54334f146d87711f145f1992dbf2c360 100644 (file)
@@ -93,8 +93,8 @@ int dns_query(const char *type, const char *name, size_t namelen,
        }
 
        if (!namelen)
-               namelen = strlen(name);
-       if (namelen < 3)
+               namelen = strnlen(name, 256);
+       if (namelen < 3 || namelen > 255)
                return -EINVAL;
        desclen += namelen + 1;
 
@@ -149,7 +149,9 @@ int dns_query(const char *type, const char *name, size_t namelen,
        if (!*_result)
                goto put;
 
-       memcpy(*_result, upayload->data, len + 1);
+       memcpy(*_result, upayload->data, len);
+       *_result[len] = '\0';
+
        if (_expiry)
                *_expiry = rkey->expiry;
 
index 02c0e1716f641c947a5b906ee82c755e568a4a7c..64c5af0a10dd82169ccada3d82baa866a5b82cc8 100644 (file)
@@ -346,7 +346,7 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
                return slave_dev;
 
        slave_dev->features = master->vlan_features;
-       SET_ETHTOOL_OPS(slave_dev, &dsa_slave_ethtool_ops);
+       slave_dev->ethtool_ops = &dsa_slave_ethtool_ops;
        eth_hw_addr_inherit(slave_dev, master);
        slave_dev->tx_queue_len = 0;
 
index 0f5a69ed746d8384891ff9e3fd1aa570f04f91d5..fe6bd7a7108169138faf198fefcab7f3217e9c55 100644 (file)
@@ -92,6 +92,7 @@ static int lowpan_header_create(struct sk_buff *skb,
        const u8 *saddr = _saddr;
        const u8 *daddr = _daddr;
        struct ieee802154_addr sa, da;
+       struct ieee802154_mac_cb *cb = mac_cb_init(skb);
 
        /* TODO:
         * if this package isn't ipv6 one, where should it be routed?
@@ -115,8 +116,7 @@ static int lowpan_header_create(struct sk_buff *skb,
         * from MAC subif of the 'dev' and 'real_dev' network devices, but
         * this isn't implemented in mainline yet, so currently we assign 0xff
         */
-       mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA;
-       mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
+       cb->type = IEEE802154_FC_TYPE_DATA;
 
        /* prepare wpan address data */
        sa.mode = IEEE802154_ADDR_LONG;
@@ -135,11 +135,10 @@ static int lowpan_header_create(struct sk_buff *skb,
        } else {
                da.mode = IEEE802154_ADDR_LONG;
                da.extended_addr = ieee802154_devaddr_from_raw(daddr);
-
-               /* request acknowledgment */
-               mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ;
        }
 
+       cb->ackreq = !lowpan_is_addr_broadcast(daddr);
+
        return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev,
                        type, (void *)&da, (void *)&sa, 0);
 }
@@ -221,139 +220,149 @@ static int lowpan_set_address(struct net_device *dev, void *p)
        return 0;
 }
 
-static int
-lowpan_fragment_xmit(struct sk_buff *skb, u8 *head,
-                    int mlen, int plen, int offset, int type)
+static struct sk_buff*
+lowpan_alloc_frag(struct sk_buff *skb, int size,
+                 const struct ieee802154_hdr *master_hdr)
 {
+       struct net_device *real_dev = lowpan_dev_info(skb->dev)->real_dev;
        struct sk_buff *frag;
-       int hlen;
-
-       hlen = (type == LOWPAN_DISPATCH_FRAG1) ?
-                       LOWPAN_FRAG1_HEAD_SIZE : LOWPAN_FRAGN_HEAD_SIZE;
-
-       raw_dump_inline(__func__, "6lowpan fragment header", head, hlen);
+       int rc;
+
+       frag = alloc_skb(real_dev->hard_header_len +
+                        real_dev->needed_tailroom + size,
+                        GFP_ATOMIC);
+
+       if (likely(frag)) {
+               frag->dev = real_dev;
+               frag->priority = skb->priority;
+               skb_reserve(frag, real_dev->hard_header_len);
+               skb_reset_network_header(frag);
+               *mac_cb(frag) = *mac_cb(skb);
+
+               rc = dev_hard_header(frag, real_dev, 0, &master_hdr->dest,
+                                    &master_hdr->source, size);
+               if (rc < 0) {
+                       kfree_skb(frag);
+                       return ERR_PTR(-rc);
+               }
+       } else {
+               frag = ERR_PTR(ENOMEM);
+       }
 
-       frag = netdev_alloc_skb(skb->dev,
-                               hlen + mlen + plen + IEEE802154_MFR_SIZE);
-       if (!frag)
-               return -ENOMEM;
+       return frag;
+}
 
-       frag->priority = skb->priority;
+static int
+lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr,
+                    u8 *frag_hdr, int frag_hdrlen,
+                    int offset, int len)
+{
+       struct sk_buff *frag;
 
-       /* copy header, MFR and payload */
-       skb_put(frag, mlen);
-       skb_copy_to_linear_data(frag, skb_mac_header(skb), mlen);
+       raw_dump_inline(__func__, " fragment header", frag_hdr, frag_hdrlen);
 
-       skb_put(frag, hlen);
-       skb_copy_to_linear_data_offset(frag, mlen, head, hlen);
+       frag = lowpan_alloc_frag(skb, frag_hdrlen + len, wpan_hdr);
+       if (IS_ERR(frag))
+               return -PTR_ERR(frag);
 
-       skb_put(frag, plen);
-       skb_copy_to_linear_data_offset(frag, mlen + hlen,
-                                      skb_network_header(skb) + offset, plen);
+       memcpy(skb_put(frag, frag_hdrlen), frag_hdr, frag_hdrlen);
+       memcpy(skb_put(frag, len), skb_network_header(skb) + offset, len);
 
-       raw_dump_table(__func__, " raw fragment dump", frag->data, frag->len);
+       raw_dump_table(__func__, " fragment dump", frag->data, frag->len);
 
        return dev_queue_xmit(frag);
 }
 
 static int
-lowpan_skb_fragmentation(struct sk_buff *skb, struct net_device *dev)
+lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
+                      const struct ieee802154_hdr *wpan_hdr)
 {
-       int err;
-       u16 dgram_offset, dgram_size, payload_length, header_length,
-           lowpan_size, frag_plen, offset;
-       __be16 tag;
-       u8 head[5];
-
-       header_length = skb->mac_len;
-       payload_length = skb->len - header_length;
-       tag = lowpan_dev_info(dev)->fragment_tag++;
-       lowpan_size = skb_network_header_len(skb);
+       u16 dgram_size, dgram_offset;
+       __be16 frag_tag;
+       u8 frag_hdr[5];
+       int frag_cap, frag_len, payload_cap, rc;
+       int skb_unprocessed, skb_offset;
+
        dgram_size = lowpan_uncompress_size(skb, &dgram_offset) -
-                    header_length;
+                    skb->mac_len;
+       frag_tag = lowpan_dev_info(dev)->fragment_tag++;
 
-       /* first fragment header */
-       head[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x7);
-       head[1] = dgram_size & 0xff;
-       memcpy(head + 2, &tag, sizeof(tag));
+       frag_hdr[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x07);
+       frag_hdr[1] = dgram_size & 0xff;
+       memcpy(frag_hdr + 2, &frag_tag, sizeof(frag_tag));
 
-       /* calc the nearest payload length(divided to 8) for first fragment
-        * which fits into a IEEE802154_MTU
-        */
-       frag_plen = round_down(IEEE802154_MTU - header_length -
-                              LOWPAN_FRAG1_HEAD_SIZE - lowpan_size -
-                              IEEE802154_MFR_SIZE, 8);
-
-       err = lowpan_fragment_xmit(skb, head, header_length,
-                                  frag_plen + lowpan_size, 0,
-                                  LOWPAN_DISPATCH_FRAG1);
-       if (err) {
-               pr_debug("%s unable to send FRAG1 packet (tag: %d)",
-                        __func__, tag);
-               goto exit;
-       }
+       payload_cap = ieee802154_max_payload(wpan_hdr);
 
-       offset = lowpan_size + frag_plen;
-       dgram_offset += frag_plen;
+       frag_len = round_down(payload_cap - LOWPAN_FRAG1_HEAD_SIZE -
+                             skb_network_header_len(skb), 8);
 
-       /* next fragment header */
-       head[0] &= ~LOWPAN_DISPATCH_FRAG1;
-       head[0] |= LOWPAN_DISPATCH_FRAGN;
+       skb_offset = skb_network_header_len(skb);
+       skb_unprocessed = skb->len - skb->mac_len - skb_offset;
 
-       frag_plen = round_down(IEEE802154_MTU - header_length -
-                              LOWPAN_FRAGN_HEAD_SIZE - IEEE802154_MFR_SIZE, 8);
+       rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr,
+                                 LOWPAN_FRAG1_HEAD_SIZE, 0,
+                                 frag_len + skb_network_header_len(skb));
+       if (rc) {
+               pr_debug("%s unable to send FRAG1 packet (tag: %d)",
+                        __func__, frag_tag);
+               goto err;
+       }
 
-       while (payload_length - offset > 0) {
-               int len = frag_plen;
+       frag_hdr[0] &= ~LOWPAN_DISPATCH_FRAG1;
+       frag_hdr[0] |= LOWPAN_DISPATCH_FRAGN;
+       frag_cap = round_down(payload_cap - LOWPAN_FRAGN_HEAD_SIZE, 8);
 
-               head[4] = dgram_offset >> 3;
+       do {
+               dgram_offset += frag_len;
+               skb_offset += frag_len;
+               skb_unprocessed -= frag_len;
+               frag_len = min(frag_cap, skb_unprocessed);
 
-               if (payload_length - offset < len)
-                       len = payload_length - offset;
+               frag_hdr[4] = dgram_offset >> 3;
 
-               err = lowpan_fragment_xmit(skb, head, header_length, len,
-                                          offset, LOWPAN_DISPATCH_FRAGN);
-               if (err) {
+               rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr,
+                                         LOWPAN_FRAGN_HEAD_SIZE, skb_offset,
+                                         frag_len);
+               if (rc) {
                        pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n",
-                                __func__, tag, offset);
-                       goto exit;
+                                __func__, frag_tag, skb_offset);
+                       goto err;
                }
+       } while (skb_unprocessed > frag_cap);
 
-               offset += len;
-               dgram_offset += len;
-       }
+       consume_skb(skb);
+       return NET_XMIT_SUCCESS;
 
-exit:
-       return err;
+err:
+       kfree_skb(skb);
+       return rc;
 }
 
 static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-       int err = -1;
+       struct ieee802154_hdr wpan_hdr;
+       int max_single;
 
        pr_debug("package xmit\n");
 
-       skb->dev = lowpan_dev_info(dev)->real_dev;
-       if (skb->dev == NULL) {
-               pr_debug("ERROR: no real wpan device found\n");
-               goto error;
+       if (ieee802154_hdr_peek(skb, &wpan_hdr) < 0) {
+               kfree_skb(skb);
+               return NET_XMIT_DROP;
        }
 
-       /* Send directly if less than the MTU minus the 2 checksum bytes. */
-       if (skb->len <= IEEE802154_MTU - IEEE802154_MFR_SIZE) {
-               err = dev_queue_xmit(skb);
-               goto out;
-       }
+       max_single = ieee802154_max_payload(&wpan_hdr);
 
-       pr_debug("frame is too big, fragmentation is needed\n");
-       err = lowpan_skb_fragmentation(skb, dev);
-error:
-       dev_kfree_skb(skb);
-out:
-       if (err)
-               pr_debug("ERROR: xmit failed\n");
+       if (skb_tail_pointer(skb) - skb_network_header(skb) <= max_single) {
+               skb->dev = lowpan_dev_info(dev)->real_dev;
+               return dev_queue_xmit(skb);
+       } else {
+               netdev_tx_t rc;
+
+               pr_debug("frame is too big, fragmentation is needed\n");
+               rc = lowpan_xmit_fragmented(skb, dev, &wpan_hdr);
 
-       return (err < 0) ? NET_XMIT_DROP : err;
+               return rc < 0 ? NET_XMIT_DROP : rc;
+       }
 }
 
 static struct wpan_phy *lowpan_get_phy(const struct net_device *dev)
index 786437bc0c08531785d3f5fa1deb5bff8efe9e62..4f0ed8780194502465f0d5b60383bf6794bfadf0 100644 (file)
@@ -21,6 +21,7 @@
  * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
  */
 
+#include <linux/capability.h>
 #include <linux/net.h>
 #include <linux/module.h>
 #include <linux/if_arp.h>
@@ -45,7 +46,12 @@ struct dgram_sock {
        struct ieee802154_addr dst_addr;
 
        unsigned int bound:1;
+       unsigned int connected:1;
        unsigned int want_ack:1;
+       unsigned int secen:1;
+       unsigned int secen_override:1;
+       unsigned int seclevel:3;
+       unsigned int seclevel_override:1;
 };
 
 static inline struct dgram_sock *dgram_sk(const struct sock *sk)
@@ -73,10 +79,7 @@ static int dgram_init(struct sock *sk)
 {
        struct dgram_sock *ro = dgram_sk(sk);
 
-       ro->dst_addr.mode = IEEE802154_ADDR_LONG;
-       ro->dst_addr.pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
        ro->want_ack = 1;
-       memset(&ro->dst_addr.extended_addr, 0xff, IEEE802154_ADDR_LEN);
        return 0;
 }
 
@@ -183,6 +186,7 @@ static int dgram_connect(struct sock *sk, struct sockaddr *uaddr,
        }
 
        ieee802154_addr_from_sa(&ro->dst_addr, &addr->addr);
+       ro->connected = 1;
 
 out:
        release_sock(sk);
@@ -194,10 +198,7 @@ static int dgram_disconnect(struct sock *sk, int flags)
        struct dgram_sock *ro = dgram_sk(sk);
 
        lock_sock(sk);
-
-       ro->dst_addr.mode = IEEE802154_ADDR_LONG;
-       memset(&ro->dst_addr.extended_addr, 0xff, IEEE802154_ADDR_LEN);
-
+       ro->connected = 0;
        release_sock(sk);
 
        return 0;
@@ -209,7 +210,9 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,
        struct net_device *dev;
        unsigned int mtu;
        struct sk_buff *skb;
+       struct ieee802154_mac_cb *cb;
        struct dgram_sock *ro = dgram_sk(sk);
+       struct ieee802154_addr dst_addr;
        int hlen, tlen;
        int err;
 
@@ -218,6 +221,11 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,
                return -EOPNOTSUPP;
        }
 
+       if (!ro->connected && !msg->msg_name)
+               return -EDESTADDRREQ;
+       else if (ro->connected && msg->msg_name)
+               return -EISCONN;
+
        if (!ro->bound)
                dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154);
        else
@@ -249,18 +257,28 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,
 
        skb_reset_network_header(skb);
 
-       mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA;
-       if (ro->want_ack)
-               mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ;
+       cb = mac_cb_init(skb);
+       cb->type = IEEE802154_FC_TYPE_DATA;
+       cb->ackreq = ro->want_ack;
+
+       if (msg->msg_name) {
+               DECLARE_SOCKADDR(struct sockaddr_ieee802154*, daddr, msg->msg_name);
 
-       mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
-       err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &ro->dst_addr,
-                       ro->bound ? &ro->src_addr : NULL, size);
+               ieee802154_addr_from_sa(&dst_addr, &daddr->addr);
+       } else {
+               dst_addr = ro->dst_addr;
+       }
+
+       cb->secen = ro->secen;
+       cb->secen_override = ro->secen_override;
+       cb->seclevel = ro->seclevel;
+       cb->seclevel_override = ro->seclevel_override;
+
+       err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &dst_addr,
+                             ro->bound ? &ro->src_addr : NULL, size);
        if (err < 0)
                goto out_skb;
 
-       skb_reset_mac_header(skb);
-
        err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
        if (err < 0)
                goto out_skb;
@@ -419,6 +437,20 @@ static int dgram_getsockopt(struct sock *sk, int level, int optname,
        case WPAN_WANTACK:
                val = ro->want_ack;
                break;
+       case WPAN_SECURITY:
+               if (!ro->secen_override)
+                       val = WPAN_SECURITY_DEFAULT;
+               else if (ro->secen)
+                       val = WPAN_SECURITY_ON;
+               else
+                       val = WPAN_SECURITY_OFF;
+               break;
+       case WPAN_SECURITY_LEVEL:
+               if (!ro->seclevel_override)
+                       val = WPAN_SECURITY_LEVEL_DEFAULT;
+               else
+                       val = ro->seclevel;
+               break;
        default:
                return -ENOPROTOOPT;
        }
@@ -434,6 +466,7 @@ static int dgram_setsockopt(struct sock *sk, int level, int optname,
                    char __user *optval, unsigned int optlen)
 {
        struct dgram_sock *ro = dgram_sk(sk);
+       struct net *net = sock_net(sk);
        int val;
        int err = 0;
 
@@ -449,6 +482,47 @@ static int dgram_setsockopt(struct sock *sk, int level, int optname,
        case WPAN_WANTACK:
                ro->want_ack = !!val;
                break;
+       case WPAN_SECURITY:
+               if (!ns_capable(net->user_ns, CAP_NET_ADMIN) &&
+                   !ns_capable(net->user_ns, CAP_NET_RAW)) {
+                       err = -EPERM;
+                       break;
+               }
+
+               switch (val) {
+               case WPAN_SECURITY_DEFAULT:
+                       ro->secen_override = 0;
+                       break;
+               case WPAN_SECURITY_ON:
+                       ro->secen_override = 1;
+                       ro->secen = 1;
+                       break;
+               case WPAN_SECURITY_OFF:
+                       ro->secen_override = 1;
+                       ro->secen = 0;
+                       break;
+               default:
+                       err = -EINVAL;
+                       break;
+               }
+               break;
+       case WPAN_SECURITY_LEVEL:
+               if (!ns_capable(net->user_ns, CAP_NET_ADMIN) &&
+                   !ns_capable(net->user_ns, CAP_NET_RAW)) {
+                       err = -EPERM;
+                       break;
+               }
+
+               if (val < WPAN_SECURITY_LEVEL_DEFAULT ||
+                   val > IEEE802154_SCF_SECLEVEL_ENC_MIC128) {
+                       err = -EINVAL;
+               } else if (val == WPAN_SECURITY_LEVEL_DEFAULT) {
+                       ro->seclevel_override = 0;
+               } else {
+                       ro->seclevel_override = 1;
+                       ro->seclevel = val;
+               }
+               break;
        default:
                err = -ENOPROTOOPT;
                break;
index bed42a48408c6cc71cf4a47ba5bd897603cfaf7b..c09294e39ca60326d5b40c8431bf202ce5559225 100644 (file)
@@ -195,15 +195,16 @@ ieee802154_hdr_get_sechdr(const u8 *buf, struct ieee802154_sechdr *hdr)
        return pos;
 }
 
+static int ieee802154_sechdr_lengths[4] = {
+       [IEEE802154_SCF_KEY_IMPLICIT] = 5,
+       [IEEE802154_SCF_KEY_INDEX] = 6,
+       [IEEE802154_SCF_KEY_SHORT_INDEX] = 10,
+       [IEEE802154_SCF_KEY_HW_INDEX] = 14,
+};
+
 static int ieee802154_hdr_sechdr_len(u8 sc)
 {
-       switch (IEEE802154_SCF_KEY_ID_MODE(sc)) {
-       case IEEE802154_SCF_KEY_IMPLICIT: return 5;
-       case IEEE802154_SCF_KEY_INDEX: return 6;
-       case IEEE802154_SCF_KEY_SHORT_INDEX: return 10;
-       case IEEE802154_SCF_KEY_HW_INDEX: return 14;
-       default: return -EINVAL;
-       }
+       return ieee802154_sechdr_lengths[IEEE802154_SCF_KEY_ID_MODE(sc)];
 }
 
 static int ieee802154_hdr_minlen(const struct ieee802154_hdr *hdr)
@@ -285,3 +286,40 @@ ieee802154_hdr_peek_addrs(const struct sk_buff *skb, struct ieee802154_hdr *hdr)
        return pos;
 }
 EXPORT_SYMBOL_GPL(ieee802154_hdr_peek_addrs);
+
+int
+ieee802154_hdr_peek(const struct sk_buff *skb, struct ieee802154_hdr *hdr)
+{
+       const u8 *buf = skb_mac_header(skb);
+       int pos;
+
+       pos = ieee802154_hdr_peek_addrs(skb, hdr);
+       if (pos < 0)
+               return -EINVAL;
+
+       if (hdr->fc.security_enabled) {
+               u8 key_id_mode = IEEE802154_SCF_KEY_ID_MODE(*(buf + pos));
+               int want = pos + ieee802154_sechdr_lengths[key_id_mode];
+
+               if (buf + want > skb_tail_pointer(skb))
+                       return -EINVAL;
+
+               pos += ieee802154_hdr_get_sechdr(buf + pos, &hdr->sec);
+       }
+
+       return pos;
+}
+EXPORT_SYMBOL_GPL(ieee802154_hdr_peek);
+
+int ieee802154_max_payload(const struct ieee802154_hdr *hdr)
+{
+       int hlen = ieee802154_hdr_minlen(hdr);
+
+       if (hdr->fc.security_enabled) {
+               hlen += ieee802154_sechdr_lengths[hdr->sec.key_id_mode] - 1;
+               hlen += ieee802154_sechdr_authtag_len(&hdr->sec);
+       }
+
+       return IEEE802154_MTU - hlen - IEEE802154_MFR_SIZE;
+}
+EXPORT_SYMBOL_GPL(ieee802154_max_payload);
index 6693a5cf01ce5e5fc39fcd33a64547dc4bdb5748..8b83a231299e46a0668b3fe329803fa1a6154791 100644 (file)
@@ -68,4 +68,23 @@ int ieee802154_list_iface(struct sk_buff *skb, struct genl_info *info);
 int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb);
 int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info);
 
+int ieee802154_llsec_getparams(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_llsec_setparams(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_llsec_add_key(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_llsec_del_key(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_llsec_dump_keys(struct sk_buff *skb,
+                              struct netlink_callback *cb);
+int ieee802154_llsec_add_dev(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_llsec_del_dev(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_llsec_dump_devs(struct sk_buff *skb,
+                              struct netlink_callback *cb);
+int ieee802154_llsec_add_devkey(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_llsec_del_devkey(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_llsec_dump_devkeys(struct sk_buff *skb,
+                                 struct netlink_callback *cb);
+int ieee802154_llsec_add_seclevel(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_llsec_del_seclevel(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_llsec_dump_seclevels(struct sk_buff *skb,
+                                   struct netlink_callback *cb);
+
 #endif
index 04b20589d97ab91eeb203ba0527122184936dc06..26efcf4fd2ff72079a678ef3a4dbd0ae887848e1 100644 (file)
@@ -124,6 +124,26 @@ static const struct genl_ops ieee8021154_ops[] = {
        IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface,
                        ieee802154_dump_iface),
        IEEE802154_OP(IEEE802154_SET_MACPARAMS, ieee802154_set_macparams),
+       IEEE802154_OP(IEEE802154_LLSEC_GETPARAMS, ieee802154_llsec_getparams),
+       IEEE802154_OP(IEEE802154_LLSEC_SETPARAMS, ieee802154_llsec_setparams),
+       IEEE802154_DUMP(IEEE802154_LLSEC_LIST_KEY, NULL,
+                       ieee802154_llsec_dump_keys),
+       IEEE802154_OP(IEEE802154_LLSEC_ADD_KEY, ieee802154_llsec_add_key),
+       IEEE802154_OP(IEEE802154_LLSEC_DEL_KEY, ieee802154_llsec_del_key),
+       IEEE802154_DUMP(IEEE802154_LLSEC_LIST_DEV, NULL,
+                       ieee802154_llsec_dump_devs),
+       IEEE802154_OP(IEEE802154_LLSEC_ADD_DEV, ieee802154_llsec_add_dev),
+       IEEE802154_OP(IEEE802154_LLSEC_DEL_DEV, ieee802154_llsec_del_dev),
+       IEEE802154_DUMP(IEEE802154_LLSEC_LIST_DEVKEY, NULL,
+                       ieee802154_llsec_dump_devkeys),
+       IEEE802154_OP(IEEE802154_LLSEC_ADD_DEVKEY, ieee802154_llsec_add_devkey),
+       IEEE802154_OP(IEEE802154_LLSEC_DEL_DEVKEY, ieee802154_llsec_del_devkey),
+       IEEE802154_DUMP(IEEE802154_LLSEC_LIST_SECLEVEL, NULL,
+                       ieee802154_llsec_dump_seclevels),
+       IEEE802154_OP(IEEE802154_LLSEC_ADD_SECLEVEL,
+                     ieee802154_llsec_add_seclevel),
+       IEEE802154_OP(IEEE802154_LLSEC_DEL_SECLEVEL,
+                     ieee802154_llsec_del_seclevel),
 };
 
 static const struct genl_multicast_group ieee802154_mcgrps[] = {
index 5d285498c0f691a906de5d9c82c64f867a857418..a3281b8bfd5bf1fa24bd05a351b476031776797f 100644 (file)
@@ -715,3 +715,812 @@ out:
        dev_put(dev);
        return rc;
 }
+
+
+
+static int
+ieee802154_llsec_parse_key_id(struct genl_info *info,
+                             struct ieee802154_llsec_key_id *desc)
+{
+       memset(desc, 0, sizeof(*desc));
+
+       if (!info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE])
+               return -EINVAL;
+
+       desc->mode = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE]);
+
+       if (desc->mode == IEEE802154_SCF_KEY_IMPLICIT) {
+               if (!info->attrs[IEEE802154_ATTR_PAN_ID] &&
+                   !(info->attrs[IEEE802154_ATTR_SHORT_ADDR] ||
+                     info->attrs[IEEE802154_ATTR_HW_ADDR]))
+                       return -EINVAL;
+
+               desc->device_addr.pan_id = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_PAN_ID]);
+
+               if (info->attrs[IEEE802154_ATTR_SHORT_ADDR]) {
+                       desc->device_addr.mode = IEEE802154_ADDR_SHORT;
+                       desc->device_addr.short_addr = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_SHORT_ADDR]);
+               } else {
+                       desc->device_addr.mode = IEEE802154_ADDR_LONG;
+                       desc->device_addr.extended_addr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
+               }
+       }
+
+       if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT &&
+           !info->attrs[IEEE802154_ATTR_LLSEC_KEY_ID])
+               return -EINVAL;
+
+       if (desc->mode == IEEE802154_SCF_KEY_SHORT_INDEX &&
+           !info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT])
+               return -EINVAL;
+
+       if (desc->mode == IEEE802154_SCF_KEY_HW_INDEX &&
+           !info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED])
+               return -EINVAL;
+
+       if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT)
+               desc->id = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_ID]);
+
+       switch (desc->mode) {
+       case IEEE802154_SCF_KEY_SHORT_INDEX:
+       {
+               u32 source = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT]);
+               desc->short_source = cpu_to_le32(source);
+               break;
+       }
+       case IEEE802154_SCF_KEY_HW_INDEX:
+               desc->extended_source = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED]);
+               break;
+       }
+
+       return 0;
+}
+
+static int
+ieee802154_llsec_fill_key_id(struct sk_buff *msg,
+                            const struct ieee802154_llsec_key_id *desc)
+{
+       if (nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_MODE, desc->mode))
+               return -EMSGSIZE;
+
+       if (desc->mode == IEEE802154_SCF_KEY_IMPLICIT) {
+               if (nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID,
+                                     desc->device_addr.pan_id))
+                       return -EMSGSIZE;
+
+               if (desc->device_addr.mode == IEEE802154_ADDR_SHORT &&
+                   nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR,
+                                     desc->device_addr.short_addr))
+                       return -EMSGSIZE;
+
+               if (desc->device_addr.mode == IEEE802154_ADDR_LONG &&
+                   nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR,
+                                  desc->device_addr.extended_addr))
+                       return -EMSGSIZE;
+       }
+
+       if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT &&
+           nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_ID, desc->id))
+               return -EMSGSIZE;
+
+       if (desc->mode == IEEE802154_SCF_KEY_SHORT_INDEX &&
+           nla_put_u32(msg, IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT,
+                       le32_to_cpu(desc->short_source)))
+               return -EMSGSIZE;
+
+       if (desc->mode == IEEE802154_SCF_KEY_HW_INDEX &&
+           nla_put_hwaddr(msg, IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED,
+                          desc->extended_source))
+               return -EMSGSIZE;
+
+       return 0;
+}
+
+int ieee802154_llsec_getparams(struct sk_buff *skb, struct genl_info *info)
+{
+       struct sk_buff *msg;
+       struct net_device *dev = NULL;
+       int rc = -ENOBUFS;
+       struct ieee802154_mlme_ops *ops;
+       void *hdr;
+       struct ieee802154_llsec_params params;
+
+       pr_debug("%s\n", __func__);
+
+       dev = ieee802154_nl_get_dev(info);
+       if (!dev)
+               return -ENODEV;
+
+       ops = ieee802154_mlme_ops(dev);
+       if (!ops->llsec) {
+               rc = -EOPNOTSUPP;
+               goto out_dev;
+       }
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               goto out_dev;
+
+       hdr = genlmsg_put(msg, 0, info->snd_seq, &nl802154_family, 0,
+               IEEE802154_LLSEC_GETPARAMS);
+       if (!hdr)
+               goto out_free;
+
+       rc = ops->llsec->get_params(dev, &params);
+       if (rc < 0)
+               goto out_free;
+
+       if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
+           nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
+           nla_put_u8(msg, IEEE802154_ATTR_LLSEC_ENABLED, params.enabled) ||
+           nla_put_u8(msg, IEEE802154_ATTR_LLSEC_SECLEVEL, params.out_level) ||
+           nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER,
+                       be32_to_cpu(params.frame_counter)) ||
+           ieee802154_llsec_fill_key_id(msg, &params.out_key))
+               goto out_free;
+
+       dev_put(dev);
+
+       return ieee802154_nl_reply(msg, info);
+out_free:
+       nlmsg_free(msg);
+out_dev:
+       dev_put(dev);
+       return rc;
+}
+
+int ieee802154_llsec_setparams(struct sk_buff *skb, struct genl_info *info)
+{
+       struct net_device *dev = NULL;
+       int rc = -EINVAL;
+       struct ieee802154_mlme_ops *ops;
+       struct ieee802154_llsec_params params;
+       int changed = 0;
+
+       pr_debug("%s\n", __func__);
+
+       dev = ieee802154_nl_get_dev(info);
+       if (!dev)
+               return -ENODEV;
+
+       if (!info->attrs[IEEE802154_ATTR_LLSEC_ENABLED] &&
+           !info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE] &&
+           !info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL])
+               goto out;
+
+       ops = ieee802154_mlme_ops(dev);
+       if (!ops->llsec) {
+               rc = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL] &&
+           nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]) > 7)
+               goto out;
+
+       if (info->attrs[IEEE802154_ATTR_LLSEC_ENABLED]) {
+               params.enabled = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_ENABLED]);
+               changed |= IEEE802154_LLSEC_PARAM_ENABLED;
+       }
+
+       if (info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE]) {
+               if (ieee802154_llsec_parse_key_id(info, &params.out_key))
+                       goto out;
+
+               changed |= IEEE802154_LLSEC_PARAM_OUT_KEY;
+       }
+
+       if (info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]) {
+               params.out_level = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]);
+               changed |= IEEE802154_LLSEC_PARAM_OUT_LEVEL;
+       }
+
+       if (info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]) {
+               u32 fc = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]);
+
+               params.frame_counter = cpu_to_be32(fc);
+               changed |= IEEE802154_LLSEC_PARAM_FRAME_COUNTER;
+       }
+
+       rc = ops->llsec->set_params(dev, &params, changed);
+
+       dev_put(dev);
+
+       return rc;
+out:
+       dev_put(dev);
+       return rc;
+}
+
+
+
+struct llsec_dump_data {
+       struct sk_buff *skb;
+       int s_idx, s_idx2;
+       int portid;
+       int nlmsg_seq;
+       struct net_device *dev;
+       struct ieee802154_mlme_ops *ops;
+       struct ieee802154_llsec_table *table;
+};
+
+static int
+ieee802154_llsec_dump_table(struct sk_buff *skb, struct netlink_callback *cb,
+                           int (*step)(struct llsec_dump_data*))
+{
+       struct net *net = sock_net(skb->sk);
+       struct net_device *dev;
+       struct llsec_dump_data data;
+       int idx = 0;
+       int first_dev = cb->args[0];
+       int rc;
+
+       for_each_netdev(net, dev) {
+               if (idx < first_dev || dev->type != ARPHRD_IEEE802154)
+                       goto skip;
+
+               data.ops = ieee802154_mlme_ops(dev);
+               if (!data.ops->llsec)
+                       goto skip;
+
+               data.skb = skb;
+               data.s_idx = cb->args[1];
+               data.s_idx2 = cb->args[2];
+               data.dev = dev;
+               data.portid = NETLINK_CB(cb->skb).portid;
+               data.nlmsg_seq = cb->nlh->nlmsg_seq;
+
+               data.ops->llsec->lock_table(dev);
+               data.ops->llsec->get_table(data.dev, &data.table);
+               rc = step(&data);
+               data.ops->llsec->unlock_table(dev);
+
+               if (rc < 0)
+                       break;
+
+skip:
+               idx++;
+       }
+       cb->args[0] = idx;
+
+       return skb->len;
+}
+
+static int
+ieee802154_nl_llsec_change(struct sk_buff *skb, struct genl_info *info,
+                          int (*fn)(struct net_device*, struct genl_info*))
+{
+       struct net_device *dev = NULL;
+       int rc = -EINVAL;
+
+       dev = ieee802154_nl_get_dev(info);
+       if (!dev)
+               return -ENODEV;
+
+       if (!ieee802154_mlme_ops(dev)->llsec)
+               rc = -EOPNOTSUPP;
+       else
+               rc = fn(dev, info);
+
+       dev_put(dev);
+       return rc;
+}
+
+
+
+static int
+ieee802154_llsec_parse_key(struct genl_info *info,
+                          struct ieee802154_llsec_key *key)
+{
+       u8 frames;
+       u32 commands[256 / 32];
+
+       memset(key, 0, sizeof(*key));
+
+       if (!info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES] ||
+           !info->attrs[IEEE802154_ATTR_LLSEC_KEY_BYTES])
+               return -EINVAL;
+
+       frames = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES]);
+       if ((frames & BIT(IEEE802154_FC_TYPE_MAC_CMD)) &&
+           !info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS])
+               return -EINVAL;
+
+       if (info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS]) {
+               nla_memcpy(commands,
+                          info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS],
+                          256 / 8);
+
+               if (commands[0] || commands[1] || commands[2] || commands[3] ||
+                   commands[4] || commands[5] || commands[6] ||
+                   commands[7] >= BIT(IEEE802154_CMD_GTS_REQ + 1))
+                       return -EINVAL;
+
+               key->cmd_frame_ids = commands[7];
+       }
+
+       key->frame_types = frames;
+
+       nla_memcpy(key->key, info->attrs[IEEE802154_ATTR_LLSEC_KEY_BYTES],
+                  IEEE802154_LLSEC_KEY_SIZE);
+
+       return 0;
+}
+
+static int llsec_add_key(struct net_device *dev, struct genl_info *info)
+{
+       struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       struct ieee802154_llsec_key key;
+       struct ieee802154_llsec_key_id id;
+
+       if (ieee802154_llsec_parse_key(info, &key) ||
+           ieee802154_llsec_parse_key_id(info, &id))
+               return -EINVAL;
+
+       return ops->llsec->add_key(dev, &id, &key);
+}
+
+int ieee802154_llsec_add_key(struct sk_buff *skb, struct genl_info *info)
+{
+       if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) !=
+           (NLM_F_CREATE | NLM_F_EXCL))
+               return -EINVAL;
+
+       return ieee802154_nl_llsec_change(skb, info, llsec_add_key);
+}
+
+static int llsec_remove_key(struct net_device *dev, struct genl_info *info)
+{
+       struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       struct ieee802154_llsec_key_id id;
+
+       if (ieee802154_llsec_parse_key_id(info, &id))
+               return -EINVAL;
+
+       return ops->llsec->del_key(dev, &id);
+}
+
+int ieee802154_llsec_del_key(struct sk_buff *skb, struct genl_info *info)
+{
+       return ieee802154_nl_llsec_change(skb, info, llsec_remove_key);
+}
+
+static int
+ieee802154_nl_fill_key(struct sk_buff *msg, u32 portid, u32 seq,
+                      const struct ieee802154_llsec_key_entry *key,
+                      const struct net_device *dev)
+{
+       void *hdr;
+       u32 commands[256 / 32];
+
+       hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI,
+                         IEEE802154_LLSEC_LIST_KEY);
+       if (!hdr)
+               goto out;
+
+       if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
+           nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
+           ieee802154_llsec_fill_key_id(msg, &key->id) ||
+           nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES,
+                      key->key->frame_types))
+               goto nla_put_failure;
+
+       if (key->key->frame_types & BIT(IEEE802154_FC_TYPE_MAC_CMD)) {
+               memset(commands, 0, sizeof(commands));
+               commands[7] = key->key->cmd_frame_ids;
+               if (nla_put(msg, IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS,
+                           sizeof(commands), commands))
+                       goto nla_put_failure;
+       }
+
+       if (nla_put(msg, IEEE802154_ATTR_LLSEC_KEY_BYTES,
+                   IEEE802154_LLSEC_KEY_SIZE, key->key->key))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+out:
+       return -EMSGSIZE;
+}
+
+static int llsec_iter_keys(struct llsec_dump_data *data)
+{
+       struct ieee802154_llsec_key_entry *pos;
+       int rc = 0, idx = 0;
+
+       list_for_each_entry(pos, &data->table->keys, list) {
+               if (idx++ < data->s_idx)
+                       continue;
+
+               if (ieee802154_nl_fill_key(data->skb, data->portid,
+                                          data->nlmsg_seq, pos, data->dev)) {
+                       rc = -EMSGSIZE;
+                       break;
+               }
+
+               data->s_idx++;
+       }
+
+       return rc;
+}
+
+int ieee802154_llsec_dump_keys(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       return ieee802154_llsec_dump_table(skb, cb, llsec_iter_keys);
+}
+
+
+
+static int
+llsec_parse_dev(struct genl_info *info,
+               struct ieee802154_llsec_device *dev)
+{
+       memset(dev, 0, sizeof(*dev));
+
+       if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER] ||
+           !info->attrs[IEEE802154_ATTR_HW_ADDR] ||
+           !info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE] ||
+           !info->attrs[IEEE802154_ATTR_LLSEC_DEV_KEY_MODE] ||
+           (!!info->attrs[IEEE802154_ATTR_PAN_ID] !=
+            !!info->attrs[IEEE802154_ATTR_SHORT_ADDR]))
+               return -EINVAL;
+
+       if (info->attrs[IEEE802154_ATTR_PAN_ID]) {
+               dev->pan_id = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_PAN_ID]);
+               dev->short_addr = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_SHORT_ADDR]);
+       } else {
+               dev->short_addr = cpu_to_le16(IEEE802154_ADDR_UNDEF);
+       }
+
+       dev->hwaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
+       dev->frame_counter = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]);
+       dev->seclevel_exempt = !!nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE]);
+       dev->key_mode = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_KEY_MODE]);
+
+       if (dev->key_mode >= __IEEE802154_LLSEC_DEVKEY_MAX)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int llsec_add_dev(struct net_device *dev, struct genl_info *info)
+{
+       struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       struct ieee802154_llsec_device desc;
+
+       if (llsec_parse_dev(info, &desc))
+               return -EINVAL;
+
+       return ops->llsec->add_dev(dev, &desc);
+}
+
+int ieee802154_llsec_add_dev(struct sk_buff *skb, struct genl_info *info)
+{
+       if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) !=
+           (NLM_F_CREATE | NLM_F_EXCL))
+               return -EINVAL;
+
+       return ieee802154_nl_llsec_change(skb, info, llsec_add_dev);
+}
+
+static int llsec_del_dev(struct net_device *dev, struct genl_info *info)
+{
+       struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       __le64 devaddr;
+
+       if (!info->attrs[IEEE802154_ATTR_HW_ADDR])
+               return -EINVAL;
+
+       devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
+
+       return ops->llsec->del_dev(dev, devaddr);
+}
+
+int ieee802154_llsec_del_dev(struct sk_buff *skb, struct genl_info *info)
+{
+       return ieee802154_nl_llsec_change(skb, info, llsec_del_dev);
+}
+
+static int
+ieee802154_nl_fill_dev(struct sk_buff *msg, u32 portid, u32 seq,
+                      const struct ieee802154_llsec_device *desc,
+                      const struct net_device *dev)
+{
+       void *hdr;
+
+       hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI,
+                         IEEE802154_LLSEC_LIST_DEV);
+       if (!hdr)
+               goto out;
+
+       if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
+           nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
+           nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID, desc->pan_id) ||
+           nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR,
+                             desc->short_addr) ||
+           nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR, desc->hwaddr) ||
+           nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER,
+                       desc->frame_counter) ||
+           nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_OVERRIDE,
+                      desc->seclevel_exempt) ||
+           nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_KEY_MODE, desc->key_mode))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+out:
+       return -EMSGSIZE;
+}
+
+static int llsec_iter_devs(struct llsec_dump_data *data)
+{
+       struct ieee802154_llsec_device *pos;
+       int rc = 0, idx = 0;
+
+       list_for_each_entry(pos, &data->table->devices, list) {
+               if (idx++ < data->s_idx)
+                       continue;
+
+               if (ieee802154_nl_fill_dev(data->skb, data->portid,
+                                          data->nlmsg_seq, pos, data->dev)) {
+                       rc = -EMSGSIZE;
+                       break;
+               }
+
+               data->s_idx++;
+       }
+
+       return rc;
+}
+
+int ieee802154_llsec_dump_devs(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devs);
+}
+
+
+
+static int llsec_add_devkey(struct net_device *dev, struct genl_info *info)
+{
+       struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       struct ieee802154_llsec_device_key key;
+       __le64 devaddr;
+
+       if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER] ||
+           !info->attrs[IEEE802154_ATTR_HW_ADDR] ||
+           ieee802154_llsec_parse_key_id(info, &key.key_id))
+               return -EINVAL;
+
+       devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
+       key.frame_counter = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]);
+
+       return ops->llsec->add_devkey(dev, devaddr, &key);
+}
+
+int ieee802154_llsec_add_devkey(struct sk_buff *skb, struct genl_info *info)
+{
+       if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) !=
+           (NLM_F_CREATE | NLM_F_EXCL))
+               return -EINVAL;
+
+       return ieee802154_nl_llsec_change(skb, info, llsec_add_devkey);
+}
+
+static int llsec_del_devkey(struct net_device *dev, struct genl_info *info)
+{
+       struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       struct ieee802154_llsec_device_key key;
+       __le64 devaddr;
+
+       if (!info->attrs[IEEE802154_ATTR_HW_ADDR] ||
+           ieee802154_llsec_parse_key_id(info, &key.key_id))
+               return -EINVAL;
+
+       devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
+
+       return ops->llsec->del_devkey(dev, devaddr, &key);
+}
+
+int ieee802154_llsec_del_devkey(struct sk_buff *skb, struct genl_info *info)
+{
+       return ieee802154_nl_llsec_change(skb, info, llsec_del_devkey);
+}
+
+static int
+ieee802154_nl_fill_devkey(struct sk_buff *msg, u32 portid, u32 seq,
+                         __le64 devaddr,
+                         const struct ieee802154_llsec_device_key *devkey,
+                         const struct net_device *dev)
+{
+       void *hdr;
+
+       hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI,
+                         IEEE802154_LLSEC_LIST_DEVKEY);
+       if (!hdr)
+               goto out;
+
+       if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
+           nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
+           nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR, devaddr) ||
+           nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER,
+                       devkey->frame_counter) ||
+           ieee802154_llsec_fill_key_id(msg, &devkey->key_id))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+out:
+       return -EMSGSIZE;
+}
+
+static int llsec_iter_devkeys(struct llsec_dump_data *data)
+{
+       struct ieee802154_llsec_device *dpos;
+       struct ieee802154_llsec_device_key *kpos;
+       int rc = 0, idx = 0, idx2;
+
+       list_for_each_entry(dpos, &data->table->devices, list) {
+               if (idx++ < data->s_idx)
+                       continue;
+
+               idx2 = 0;
+
+               list_for_each_entry(kpos, &dpos->keys, list) {
+                       if (idx2++ < data->s_idx2)
+                               continue;
+
+                       if (ieee802154_nl_fill_devkey(data->skb, data->portid,
+                                                     data->nlmsg_seq,
+                                                     dpos->hwaddr, kpos,
+                                                     data->dev)) {
+                               return rc = -EMSGSIZE;
+                       }
+
+                       data->s_idx2++;
+               }
+
+               data->s_idx++;
+       }
+
+       return rc;
+}
+
+int ieee802154_llsec_dump_devkeys(struct sk_buff *skb,
+                                 struct netlink_callback *cb)
+{
+       return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devkeys);
+}
+
+
+
+static int
+llsec_parse_seclevel(struct genl_info *info,
+                    struct ieee802154_llsec_seclevel *sl)
+{
+       memset(sl, 0, sizeof(*sl));
+
+       if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_TYPE] ||
+           !info->attrs[IEEE802154_ATTR_LLSEC_SECLEVELS] ||
+           !info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE])
+               return -EINVAL;
+
+       sl->frame_type = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_TYPE]);
+       if (sl->frame_type == IEEE802154_FC_TYPE_MAC_CMD) {
+               if (!info->attrs[IEEE802154_ATTR_LLSEC_CMD_FRAME_ID])
+                       return -EINVAL;
+
+               sl->cmd_frame_id = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_CMD_FRAME_ID]);
+       }
+
+       sl->sec_levels = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVELS]);
+       sl->device_override = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE]);
+
+       return 0;
+}
+
+static int llsec_add_seclevel(struct net_device *dev, struct genl_info *info)
+{
+       struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       struct ieee802154_llsec_seclevel sl;
+
+       if (llsec_parse_seclevel(info, &sl))
+               return -EINVAL;
+
+       return ops->llsec->add_seclevel(dev, &sl);
+}
+
+int ieee802154_llsec_add_seclevel(struct sk_buff *skb, struct genl_info *info)
+{
+       if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) !=
+           (NLM_F_CREATE | NLM_F_EXCL))
+               return -EINVAL;
+
+       return ieee802154_nl_llsec_change(skb, info, llsec_add_seclevel);
+}
+
+static int llsec_del_seclevel(struct net_device *dev, struct genl_info *info)
+{
+       struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       struct ieee802154_llsec_seclevel sl;
+
+       if (llsec_parse_seclevel(info, &sl))
+               return -EINVAL;
+
+       return ops->llsec->del_seclevel(dev, &sl);
+}
+
+int ieee802154_llsec_del_seclevel(struct sk_buff *skb, struct genl_info *info)
+{
+       return ieee802154_nl_llsec_change(skb, info, llsec_del_seclevel);
+}
+
+static int
+ieee802154_nl_fill_seclevel(struct sk_buff *msg, u32 portid, u32 seq,
+                           const struct ieee802154_llsec_seclevel *sl,
+                           const struct net_device *dev)
+{
+       void *hdr;
+
+       hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI,
+                         IEEE802154_LLSEC_LIST_SECLEVEL);
+       if (!hdr)
+               goto out;
+
+       if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
+           nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
+           nla_put_u8(msg, IEEE802154_ATTR_LLSEC_FRAME_TYPE, sl->frame_type) ||
+           nla_put_u8(msg, IEEE802154_ATTR_LLSEC_SECLEVELS, sl->sec_levels) ||
+           nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_OVERRIDE,
+                      sl->device_override))
+               goto nla_put_failure;
+
+       if (sl->frame_type == IEEE802154_FC_TYPE_MAC_CMD &&
+           nla_put_u8(msg, IEEE802154_ATTR_LLSEC_CMD_FRAME_ID,
+                      sl->cmd_frame_id))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+out:
+       return -EMSGSIZE;
+}
+
+static int llsec_iter_seclevels(struct llsec_dump_data *data)
+{
+       struct ieee802154_llsec_seclevel *pos;
+       int rc = 0, idx = 0;
+
+       list_for_each_entry(pos, &data->table->security_levels, list) {
+               if (idx++ < data->s_idx)
+                       continue;
+
+               if (ieee802154_nl_fill_seclevel(data->skb, data->portid,
+                                               data->nlmsg_seq, pos,
+                                               data->dev)) {
+                       rc = -EMSGSIZE;
+                       break;
+               }
+
+               data->s_idx++;
+       }
+
+       return rc;
+}
+
+int ieee802154_llsec_dump_seclevels(struct sk_buff *skb,
+                                   struct netlink_callback *cb)
+{
+       return ieee802154_llsec_dump_table(skb, cb, llsec_iter_seclevels);
+}
index fd7be5e45cefb99d37df9d173c9bf7c0ab1d7dc3..3a703ab88348fc38481ec583ff5d0885ae8c9655 100644 (file)
@@ -62,5 +62,21 @@ const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = {
        [IEEE802154_ATTR_CSMA_MAX_BE] = { .type = NLA_U8, },
 
        [IEEE802154_ATTR_FRAME_RETRIES] = { .type = NLA_S8, },
+
+       [IEEE802154_ATTR_LLSEC_ENABLED] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_LLSEC_SECLEVEL] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_LLSEC_KEY_MODE] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT] = { .type = NLA_U32, },
+       [IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED] = { .type = NLA_HW_ADDR, },
+       [IEEE802154_ATTR_LLSEC_KEY_ID] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_LLSEC_FRAME_COUNTER] = { .type = NLA_U32 },
+       [IEEE802154_ATTR_LLSEC_KEY_BYTES] = { .len = 16, },
+       [IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS] = { .len = 258 / 8 },
+       [IEEE802154_ATTR_LLSEC_FRAME_TYPE] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_LLSEC_CMD_FRAME_ID] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_LLSEC_SECLEVELS] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_LLSEC_DEV_OVERRIDE] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_LLSEC_DEV_KEY_MODE] = { .type = NLA_U8, },
 };
 
index ef2d54372b134330d0fa7c51c2049379ce69e85e..6f1428c4870b11e9bab087c54d414079514caf8a 100644 (file)
@@ -36,7 +36,7 @@ struct lowpan_frag_info {
        u8 d_offset;
 };
 
-struct lowpan_frag_info *lowpan_cb(struct sk_buff *skb)
+static struct lowpan_frag_info *lowpan_cb(struct sk_buff *skb)
 {
        return (struct lowpan_frag_info *)skb->cb;
 }
@@ -120,6 +120,8 @@ fq_find(struct net *net, const struct lowpan_frag_info *frag_info,
        struct inet_frag_queue *q;
        struct lowpan_create_arg arg;
        unsigned int hash;
+       struct netns_ieee802154_lowpan *ieee802154_lowpan =
+               net_ieee802154_lowpan(net);
 
        arg.tag = frag_info->d_tag;
        arg.d_size = frag_info->d_size;
@@ -129,7 +131,7 @@ fq_find(struct net *net, const struct lowpan_frag_info *frag_info,
        read_lock(&lowpan_frags.lock);
        hash = lowpan_hash_frag(frag_info->d_tag, frag_info->d_size, src, dst);
 
-       q = inet_frag_find(&net->ieee802154_lowpan.frags,
+       q = inet_frag_find(&ieee802154_lowpan->frags,
                           &lowpan_frags, &arg, hash);
        if (IS_ERR_OR_NULL(q)) {
                inet_frag_maybe_warn_overflow(q, pr_fmt());
@@ -357,6 +359,8 @@ int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type)
        struct net *net = dev_net(skb->dev);
        struct lowpan_frag_info *frag_info = lowpan_cb(skb);
        struct ieee802154_addr source, dest;
+       struct netns_ieee802154_lowpan *ieee802154_lowpan =
+               net_ieee802154_lowpan(net);
        int err;
 
        source = mac_cb(skb)->source;
@@ -366,10 +370,10 @@ int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type)
        if (err < 0)
                goto err;
 
-       if (frag_info->d_size > net->ieee802154_lowpan.max_dsize)
+       if (frag_info->d_size > ieee802154_lowpan->max_dsize)
                goto err;
 
-       inet_frag_evictor(&net->ieee802154_lowpan.frags, &lowpan_frags, false);
+       inet_frag_evictor(&ieee802154_lowpan->frags, &lowpan_frags, false);
 
        fq = fq_find(net, frag_info, &source, &dest);
        if (fq != NULL) {
@@ -436,6 +440,8 @@ static int __net_init lowpan_frags_ns_sysctl_register(struct net *net)
 {
        struct ctl_table *table;
        struct ctl_table_header *hdr;
+       struct netns_ieee802154_lowpan *ieee802154_lowpan =
+               net_ieee802154_lowpan(net);
 
        table = lowpan_frags_ns_ctl_table;
        if (!net_eq(net, &init_net)) {
@@ -444,10 +450,10 @@ static int __net_init lowpan_frags_ns_sysctl_register(struct net *net)
                if (table == NULL)
                        goto err_alloc;
 
-               table[0].data = &net->ieee802154_lowpan.frags.high_thresh;
-               table[1].data = &net->ieee802154_lowpan.frags.low_thresh;
-               table[2].data = &net->ieee802154_lowpan.frags.timeout;
-               table[3].data = &net->ieee802154_lowpan.max_dsize;
+               table[0].data = &ieee802154_lowpan->frags.high_thresh;
+               table[1].data = &ieee802154_lowpan->frags.low_thresh;
+               table[2].data = &ieee802154_lowpan->frags.timeout;
+               table[3].data = &ieee802154_lowpan->max_dsize;
 
                /* Don't export sysctls to unprivileged users */
                if (net->user_ns != &init_user_ns)
@@ -458,7 +464,7 @@ static int __net_init lowpan_frags_ns_sysctl_register(struct net *net)
        if (hdr == NULL)
                goto err_reg;
 
-       net->ieee802154_lowpan.sysctl.frags_hdr = hdr;
+       ieee802154_lowpan->sysctl.frags_hdr = hdr;
        return 0;
 
 err_reg:
@@ -471,9 +477,11 @@ err_alloc:
 static void __net_exit lowpan_frags_ns_sysctl_unregister(struct net *net)
 {
        struct ctl_table *table;
+       struct netns_ieee802154_lowpan *ieee802154_lowpan =
+               net_ieee802154_lowpan(net);
 
-       table = net->ieee802154_lowpan.sysctl.frags_hdr->ctl_table_arg;
-       unregister_net_sysctl_table(net->ieee802154_lowpan.sysctl.frags_hdr);
+       table = ieee802154_lowpan->sysctl.frags_hdr->ctl_table_arg;
+       unregister_net_sysctl_table(ieee802154_lowpan->sysctl.frags_hdr);
        if (!net_eq(net, &init_net))
                kfree(table);
 }
@@ -514,20 +522,26 @@ static inline void lowpan_frags_sysctl_unregister(void)
 
 static int __net_init lowpan_frags_init_net(struct net *net)
 {
-       net->ieee802154_lowpan.frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
-       net->ieee802154_lowpan.frags.low_thresh = IPV6_FRAG_LOW_THRESH;
-       net->ieee802154_lowpan.frags.timeout = IPV6_FRAG_TIMEOUT;
-       net->ieee802154_lowpan.max_dsize = 0xFFFF;
+       struct netns_ieee802154_lowpan *ieee802154_lowpan =
+               net_ieee802154_lowpan(net);
 
-       inet_frags_init_net(&net->ieee802154_lowpan.frags);
+       ieee802154_lowpan->frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
+       ieee802154_lowpan->frags.low_thresh = IPV6_FRAG_LOW_THRESH;
+       ieee802154_lowpan->frags.timeout = IPV6_FRAG_TIMEOUT;
+       ieee802154_lowpan->max_dsize = 0xFFFF;
+
+       inet_frags_init_net(&ieee802154_lowpan->frags);
 
        return lowpan_frags_ns_sysctl_register(net);
 }
 
 static void __net_exit lowpan_frags_exit_net(struct net *net)
 {
+       struct netns_ieee802154_lowpan *ieee802154_lowpan =
+               net_ieee802154_lowpan(net);
+
        lowpan_frags_ns_sysctl_unregister(net);
-       inet_frags_exit_net(&net->ieee802154_lowpan.frags, &lowpan_frags);
+       inet_frags_exit_net(&ieee802154_lowpan->frags, &lowpan_frags);
 }
 
 static struct pernet_operations lowpan_frags_ops = {
index 6d6dd345bc4d89dea7dac7b179e4ef0f391b3954..d5e6836cf772d677271c46681ffa442baa9d0c99 100644 (file)
@@ -254,7 +254,6 @@ static int inet_create(struct net *net, struct socket *sock, int protocol,
        struct inet_sock *inet;
        struct proto *answer_prot;
        unsigned char answer_flags;
-       char answer_no_check;
        int try_loading_module = 0;
        int err;
 
@@ -312,7 +311,6 @@ lookup_protocol:
 
        sock->ops = answer->ops;
        answer_prot = answer->prot;
-       answer_no_check = answer->no_check;
        answer_flags = answer->flags;
        rcu_read_unlock();
 
@@ -324,7 +322,6 @@ lookup_protocol:
                goto out;
 
        err = 0;
-       sk->sk_no_check = answer_no_check;
        if (INET_PROTOSW_REUSE & answer_flags)
                sk->sk_reuse = SK_CAN_REUSE;
 
@@ -1002,7 +999,6 @@ static struct inet_protosw inetsw_array[] =
                .protocol =   IPPROTO_TCP,
                .prot =       &tcp_prot,
                .ops =        &inet_stream_ops,
-               .no_check =   0,
                .flags =      INET_PROTOSW_PERMANENT |
                              INET_PROTOSW_ICSK,
        },
@@ -1012,7 +1008,6 @@ static struct inet_protosw inetsw_array[] =
                .protocol =   IPPROTO_UDP,
                .prot =       &udp_prot,
                .ops =        &inet_dgram_ops,
-               .no_check =   UDP_CSUM_DEFAULT,
                .flags =      INET_PROTOSW_PERMANENT,
        },
 
@@ -1021,7 +1016,6 @@ static struct inet_protosw inetsw_array[] =
                .protocol =   IPPROTO_ICMP,
                .prot =       &ping_prot,
                .ops =        &inet_dgram_ops,
-               .no_check =   UDP_CSUM_DEFAULT,
                .flags =      INET_PROTOSW_REUSE,
        },
 
@@ -1030,7 +1024,6 @@ static struct inet_protosw inetsw_array[] =
               .protocol =   IPPROTO_IP,        /* wild card */
               .prot =       &raw_prot,
               .ops =        &inet_sockraw_ops,
-              .no_check =   UDP_CSUM_DEFAULT,
               .flags =      INET_PROTOSW_REUSE,
        }
 };
@@ -1261,10 +1254,12 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
                       SKB_GSO_DODGY |
                       SKB_GSO_TCP_ECN |
                       SKB_GSO_GRE |
+                      SKB_GSO_GRE_CSUM |
                       SKB_GSO_IPIP |
                       SKB_GSO_SIT |
                       SKB_GSO_TCPV6 |
                       SKB_GSO_UDP_TUNNEL |
+                      SKB_GSO_UDP_TUNNEL_CSUM |
                       SKB_GSO_MPLS |
                       0)))
                goto out;
@@ -1476,22 +1471,20 @@ int inet_ctl_sock_create(struct sock **sk, unsigned short family,
 }
 EXPORT_SYMBOL_GPL(inet_ctl_sock_create);
 
-unsigned long snmp_fold_field(void __percpu *mib[], int offt)
+unsigned long snmp_fold_field(void __percpu *mib, int offt)
 {
        unsigned long res = 0;
-       int i, j;
+       int i;
 
-       for_each_possible_cpu(i) {
-               for (j = 0; j < SNMP_ARRAY_SZ; j++)
-                       res += *(((unsigned long *) per_cpu_ptr(mib[j], i)) + offt);
-       }
+       for_each_possible_cpu(i)
+               res += *(((unsigned long *) per_cpu_ptr(mib, i)) + offt);
        return res;
 }
 EXPORT_SYMBOL_GPL(snmp_fold_field);
 
 #if BITS_PER_LONG==32
 
-u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t syncp_offset)
+u64 snmp_fold_field64(void __percpu *mib, int offt, size_t syncp_offset)
 {
        u64 res = 0;
        int cpu;
@@ -1502,7 +1495,7 @@ u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t syncp_offset)
                u64 v;
                unsigned int start;
 
-               bhptr = per_cpu_ptr(mib[0], cpu);
+               bhptr = per_cpu_ptr(mib, cpu);
                syncp = (struct u64_stats_sync *)(bhptr + syncp_offset);
                do {
                        start = u64_stats_fetch_begin_irq(syncp);
@@ -1516,25 +1509,6 @@ u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t syncp_offset)
 EXPORT_SYMBOL_GPL(snmp_fold_field64);
 #endif
 
-int snmp_mib_init(void __percpu *ptr[2], size_t mibsize, size_t align)
-{
-       BUG_ON(ptr == NULL);
-       ptr[0] = __alloc_percpu(mibsize, align);
-       if (!ptr[0])
-               return -ENOMEM;
-
-#if SNMP_ARRAY_SZ == 2
-       ptr[1] = __alloc_percpu(mibsize, align);
-       if (!ptr[1]) {
-               free_percpu(ptr[0]);
-               ptr[0] = NULL;
-               return -ENOMEM;
-       }
-#endif
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snmp_mib_init);
-
 #ifdef CONFIG_IP_MULTICAST
 static const struct net_protocol igmp_protocol = {
        .handler =      igmp_rcv,
@@ -1570,40 +1544,30 @@ static __net_init int ipv4_mib_init_net(struct net *net)
 {
        int i;
 
-       if (snmp_mib_init((void __percpu **)net->mib.tcp_statistics,
-                         sizeof(struct tcp_mib),
-                         __alignof__(struct tcp_mib)) < 0)
+       net->mib.tcp_statistics = alloc_percpu(struct tcp_mib);
+       if (!net->mib.tcp_statistics)
                goto err_tcp_mib;
-       if (snmp_mib_init((void __percpu **)net->mib.ip_statistics,
-                         sizeof(struct ipstats_mib),
-                         __alignof__(struct ipstats_mib)) < 0)
+       net->mib.ip_statistics = alloc_percpu(struct ipstats_mib);
+       if (!net->mib.ip_statistics)
                goto err_ip_mib;
 
        for_each_possible_cpu(i) {
                struct ipstats_mib *af_inet_stats;
-               af_inet_stats = per_cpu_ptr(net->mib.ip_statistics[0], i);
+               af_inet_stats = per_cpu_ptr(net->mib.ip_statistics, i);
                u64_stats_init(&af_inet_stats->syncp);
-#if SNMP_ARRAY_SZ == 2
-               af_inet_stats = per_cpu_ptr(net->mib.ip_statistics[1], i);
-               u64_stats_init(&af_inet_stats->syncp);
-#endif
        }
 
-       if (snmp_mib_init((void __percpu **)net->mib.net_statistics,
-                         sizeof(struct linux_mib),
-                         __alignof__(struct linux_mib)) < 0)
+       net->mib.net_statistics = alloc_percpu(struct linux_mib);
+       if (!net->mib.net_statistics)
                goto err_net_mib;
-       if (snmp_mib_init((void __percpu **)net->mib.udp_statistics,
-                         sizeof(struct udp_mib),
-                         __alignof__(struct udp_mib)) < 0)
+       net->mib.udp_statistics = alloc_percpu(struct udp_mib);
+       if (!net->mib.udp_statistics)
                goto err_udp_mib;
-       if (snmp_mib_init((void __percpu **)net->mib.udplite_statistics,
-                         sizeof(struct udp_mib),
-                         __alignof__(struct udp_mib)) < 0)
+       net->mib.udplite_statistics = alloc_percpu(struct udp_mib);
+       if (!net->mib.udplite_statistics)
                goto err_udplite_mib;
-       if (snmp_mib_init((void __percpu **)net->mib.icmp_statistics,
-                         sizeof(struct icmp_mib),
-                         __alignof__(struct icmp_mib)) < 0)
+       net->mib.icmp_statistics = alloc_percpu(struct icmp_mib);
+       if (!net->mib.icmp_statistics)
                goto err_icmp_mib;
        net->mib.icmpmsg_statistics = kzalloc(sizeof(struct icmpmsg_mib),
                                              GFP_KERNEL);
@@ -1614,17 +1578,17 @@ static __net_init int ipv4_mib_init_net(struct net *net)
        return 0;
 
 err_icmpmsg_mib:
-       snmp_mib_free((void __percpu **)net->mib.icmp_statistics);
+       free_percpu(net->mib.icmp_statistics);
 err_icmp_mib:
-       snmp_mib_free((void __percpu **)net->mib.udplite_statistics);
+       free_percpu(net->mib.udplite_statistics);
 err_udplite_mib:
-       snmp_mib_free((void __percpu **)net->mib.udp_statistics);
+       free_percpu(net->mib.udp_statistics);
 err_udp_mib:
-       snmp_mib_free((void __percpu **)net->mib.net_statistics);
+       free_percpu(net->mib.net_statistics);
 err_net_mib:
-       snmp_mib_free((void __percpu **)net->mib.ip_statistics);
+       free_percpu(net->mib.ip_statistics);
 err_ip_mib:
-       snmp_mib_free((void __percpu **)net->mib.tcp_statistics);
+       free_percpu(net->mib.tcp_statistics);
 err_tcp_mib:
        return -ENOMEM;
 }
@@ -1632,12 +1596,12 @@ err_tcp_mib:
 static __net_exit void ipv4_mib_exit_net(struct net *net)
 {
        kfree(net->mib.icmpmsg_statistics);
-       snmp_mib_free((void __percpu **)net->mib.icmp_statistics);
-       snmp_mib_free((void __percpu **)net->mib.udplite_statistics);
-       snmp_mib_free((void __percpu **)net->mib.udp_statistics);
-       snmp_mib_free((void __percpu **)net->mib.net_statistics);
-       snmp_mib_free((void __percpu **)net->mib.ip_statistics);
-       snmp_mib_free((void __percpu **)net->mib.tcp_statistics);
+       free_percpu(net->mib.icmp_statistics);
+       free_percpu(net->mib.udplite_statistics);
+       free_percpu(net->mib.udp_statistics);
+       free_percpu(net->mib.net_statistics);
+       free_percpu(net->mib.ip_statistics);
+       free_percpu(net->mib.tcp_statistics);
 }
 
 static __net_initdata struct pernet_operations ipv4_mib_ops = {
@@ -1736,13 +1700,9 @@ static int __init inet_init(void)
 
        BUILD_BUG_ON(sizeof(struct inet_skb_parm) > FIELD_SIZEOF(struct sk_buff, cb));
 
-       sysctl_local_reserved_ports = kzalloc(65536 / 8, GFP_KERNEL);
-       if (!sysctl_local_reserved_ports)
-               goto out;
-
        rc = proto_register(&tcp_prot, 1);
        if (rc)
-               goto out_free_reserved_ports;
+               goto out;
 
        rc = proto_register(&udp_prot, 1);
        if (rc)
@@ -1852,8 +1812,6 @@ out_unregister_udp_proto:
        proto_unregister(&udp_prot);
 out_unregister_tcp_proto:
        proto_unregister(&tcp_prot);
-out_free_reserved_ports:
-       kfree(sysctl_local_reserved_ports);
        goto out;
 }
 
index 8b5134c582f147a94938c34a01dd0691c288bb78..a3095fdefbed98ed4e320ac6c44ea3e18241d1a4 100644 (file)
@@ -86,18 +86,26 @@ out:
 }
 EXPORT_SYMBOL(ip4_datagram_connect);
 
+/* Because UDP xmit path can manipulate sk_dst_cache without holding
+ * socket lock, we need to use sk_dst_set() here,
+ * even if we own the socket lock.
+ */
 void ip4_datagram_release_cb(struct sock *sk)
 {
        const struct inet_sock *inet = inet_sk(sk);
        const struct ip_options_rcu *inet_opt;
        __be32 daddr = inet->inet_daddr;
+       struct dst_entry *dst;
        struct flowi4 fl4;
        struct rtable *rt;
 
-       if (! __sk_dst_get(sk) || __sk_dst_check(sk, 0))
-               return;
-
        rcu_read_lock();
+
+       dst = __sk_dst_get(sk);
+       if (!dst || !dst->obsolete || dst->ops->check(dst, 0)) {
+               rcu_read_unlock();
+               return;
+       }
        inet_opt = rcu_dereference(inet->inet_opt);
        if (inet_opt && inet_opt->opt.srr)
                daddr = inet_opt->opt.faddr;
@@ -105,8 +113,10 @@ void ip4_datagram_release_cb(struct sock *sk)
                                   inet->inet_saddr, inet->inet_dport,
                                   inet->inet_sport, sk->sk_protocol,
                                   RT_CONN_FLAGS(sk), sk->sk_bound_dev_if);
-       if (!IS_ERR(rt))
-               __sk_dst_set(sk, &rt->dst);
+
+       dst = !IS_ERR(rt) ? &rt->dst : NULL;
+       sk_dst_set(sk, dst);
+
        rcu_read_unlock();
 }
 EXPORT_SYMBOL_GPL(ip4_datagram_release_cb);
index bdbf68bb2e2d194fcdf94553bd41a4ac9f184d7c..e9449376b58e4293b7735362912e1ea1191a8815 100644 (file)
@@ -106,7 +106,6 @@ static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
 #define IN4_ADDR_HSIZE         (1U << IN4_ADDR_HSIZE_SHIFT)
 
 static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
-static DEFINE_SPINLOCK(inet_addr_hash_lock);
 
 static u32 inet_addr_hash(struct net *net, __be32 addr)
 {
@@ -119,16 +118,14 @@ static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
 {
        u32 hash = inet_addr_hash(net, ifa->ifa_local);
 
-       spin_lock(&inet_addr_hash_lock);
+       ASSERT_RTNL();
        hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
-       spin_unlock(&inet_addr_hash_lock);
 }
 
 static void inet_hash_remove(struct in_ifaddr *ifa)
 {
-       spin_lock(&inet_addr_hash_lock);
+       ASSERT_RTNL();
        hlist_del_init_rcu(&ifa->hash);
-       spin_unlock(&inet_addr_hash_lock);
 }
 
 /**
@@ -830,7 +827,7 @@ static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
        ifa_existing = find_matching_ifa(ifa);
        if (!ifa_existing) {
                /* It would be best to check for !NLM_F_CREATE here but
-                * userspace alreay relies on not having to provide this.
+                * userspace already relies on not having to provide this.
                 */
                set_ifa_lifetime(ifa, valid_lft, prefered_lft);
                return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid);
index 250be7421ab36c50ce00a25dcd3c659ca1c97f18..4e9619bca732b869a70ac0db32c8b1285c9446fa 100644 (file)
@@ -84,7 +84,8 @@ void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
                        ptr--;
                }
                if (tpi->flags&TUNNEL_CSUM &&
-                   !(skb_shinfo(skb)->gso_type & SKB_GSO_GRE)) {
+                   !(skb_shinfo(skb)->gso_type &
+                     (SKB_GSO_GRE|SKB_GSO_GRE_CSUM))) {
                        *ptr = 0;
                        *(__sum16 *)ptr = csum_fold(skb_checksum(skb, 0,
                                                                 skb->len, 0));
@@ -93,28 +94,6 @@ void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
 }
 EXPORT_SYMBOL_GPL(gre_build_header);
 
-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)
 {
@@ -141,7 +120,7 @@ static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
 
        options = (__be32 *)(greh + 1);
        if (greh->flags & GRE_CSUM) {
-               if (check_checksum(skb)) {
+               if (skb_checksum_simple_validate(skb)) {
                        *csum_err = true;
                        return -EINVAL;
                }
index f1d32280cb54d596691ac7173e4f3151dc4b11b1..eb92deb12666fb56b6a289c55b8205fc27117933 100644 (file)
@@ -42,6 +42,7 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
                                  SKB_GSO_DODGY |
                                  SKB_GSO_TCP_ECN |
                                  SKB_GSO_GRE |
+                                 SKB_GSO_GRE_CSUM |
                                  SKB_GSO_IPIP)))
                goto out;
 
@@ -55,6 +56,8 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
                goto out;
 
        csum = !!(greh->flags & GRE_CSUM);
+       if (csum)
+               skb->encap_hdr_csum = 1;
 
        if (unlikely(!pskb_may_pull(skb, ghl)))
                goto out;
@@ -94,10 +97,13 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
                                }
                        }
 
-                       greh = (struct gre_base_hdr *)(skb->data);
+                       skb_reset_transport_header(skb);
+
+                       greh = (struct gre_base_hdr *)
+                           skb_transport_header(skb);
                        pcsum = (__be32 *)(greh + 1);
                        *pcsum = 0;
-                       *(__sum16 *)pcsum = csum_fold(skb_checksum(skb, 0, skb->len, 0));
+                       *(__sum16 *)pcsum = gso_make_checksum(skb, 0);
                }
                __skb_push(skb, tnl_hlen - ghl);
 
@@ -125,10 +131,12 @@ static __sum16 gro_skb_checksum(struct sk_buff *skb)
                csum_partial(skb->data, skb_gro_offset(skb), 0));
        sum = csum_fold(NAPI_GRO_CB(skb)->csum);
        if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE)) {
-               if (unlikely(!sum))
+               if (unlikely(!sum) && !skb->csum_complete_sw)
                        netdev_rx_csum_fault(skb->dev);
-       } else
+       } else {
                skb->ip_summed = CHECKSUM_COMPLETE;
+               skb->csum_complete_sw = 1;
+       }
 
        return sum;
 }
index 0134663fdbce86f6da39d8f6c9d27ce6c404687c..79c3d947a48128a8a58b58776e99f3a6602d868c 100644 (file)
@@ -337,6 +337,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
        struct sock *sk;
        struct inet_sock *inet;
        __be32 daddr, saddr;
+       u32 mark = IP4_REPLY_MARK(net, skb->mark);
 
        if (ip_options_echo(&icmp_param->replyopts.opt.opt, skb))
                return;
@@ -349,6 +350,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
        icmp_param->data.icmph.checksum = 0;
 
        inet->tos = ip_hdr(skb)->tos;
+       sk->sk_mark = mark;
        daddr = ipc.addr = ip_hdr(skb)->saddr;
        saddr = fib_compute_spec_dst(skb);
        ipc.opt = NULL;
@@ -364,6 +366,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
        memset(&fl4, 0, sizeof(fl4));
        fl4.daddr = daddr;
        fl4.saddr = saddr;
+       fl4.flowi4_mark = mark;
        fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos);
        fl4.flowi4_proto = IPPROTO_ICMP;
        security_skb_classify_flow(skb, flowi4_to_flowi(&fl4));
@@ -382,7 +385,7 @@ static struct rtable *icmp_route_lookup(struct net *net,
                                        struct flowi4 *fl4,
                                        struct sk_buff *skb_in,
                                        const struct iphdr *iph,
-                                       __be32 saddr, u8 tos,
+                                       __be32 saddr, u8 tos, u32 mark,
                                        int type, int code,
                                        struct icmp_bxm *param)
 {
@@ -394,6 +397,7 @@ static struct rtable *icmp_route_lookup(struct net *net,
        fl4->daddr = (param->replyopts.opt.opt.srr ?
                      param->replyopts.opt.opt.faddr : iph->saddr);
        fl4->saddr = saddr;
+       fl4->flowi4_mark = mark;
        fl4->flowi4_tos = RT_TOS(tos);
        fl4->flowi4_proto = IPPROTO_ICMP;
        fl4->fl4_icmp_type = type;
@@ -491,6 +495,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
        struct flowi4 fl4;
        __be32 saddr;
        u8  tos;
+       u32 mark;
        struct net *net;
        struct sock *sk;
 
@@ -592,6 +597,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
        tos = icmp_pointers[type].error ? ((iph->tos & IPTOS_TOS_MASK) |
                                           IPTOS_PREC_INTERNETCONTROL) :
                                          iph->tos;
+       mark = IP4_REPLY_MARK(net, skb_in->mark);
 
        if (ip_options_echo(&icmp_param->replyopts.opt.opt, skb_in))
                goto out_unlock;
@@ -608,13 +614,14 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
        icmp_param->skb   = skb_in;
        icmp_param->offset = skb_network_offset(skb_in);
        inet_sk(sk)->tos = tos;
+       sk->sk_mark = mark;
        ipc.addr = iph->saddr;
        ipc.opt = &icmp_param->replyopts.opt;
        ipc.tx_flags = 0;
        ipc.ttl = 0;
        ipc.tos = -1;
 
-       rt = icmp_route_lookup(net, &fl4, skb_in, iph, saddr, tos,
+       rt = icmp_route_lookup(net, &fl4, skb_in, iph, saddr, tos, mark,
                               type, code, icmp_param);
        if (IS_ERR(rt))
                goto out_unlock;
@@ -908,16 +915,8 @@ int icmp_rcv(struct sk_buff *skb)
 
        ICMP_INC_STATS_BH(net, ICMP_MIB_INMSGS);
 
-       switch (skb->ip_summed) {
-       case CHECKSUM_COMPLETE:
-               if (!csum_fold(skb->csum))
-                       break;
-               /* fall through */
-       case CHECKSUM_NONE:
-               skb->csum = 0;
-               if (__skb_checksum_complete(skb))
-                       goto csum_error;
-       }
+       if (skb_checksum_simple_validate(skb))
+               goto csum_error;
 
        if (!pskb_pull(skb, sizeof(*icmph)))
                goto error;
index 97e4d1655d26bb65121c1a8954af2d7565c288a6..6748d420f714f4add6acd23a4aefd1f35c0c067b 100644 (file)
@@ -369,7 +369,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
        pip->saddr    = fl4.saddr;
        pip->protocol = IPPROTO_IGMP;
        pip->tot_len  = 0;      /* filled in later */
-       ip_select_ident(skb, &rt->dst, NULL);
+       ip_select_ident(skb, NULL);
        ((u8 *)&pip[1])[0] = IPOPT_RA;
        ((u8 *)&pip[1])[1] = 4;
        ((u8 *)&pip[1])[2] = 0;
@@ -714,7 +714,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc,
        iph->daddr    = dst;
        iph->saddr    = fl4.saddr;
        iph->protocol = IPPROTO_IGMP;
-       ip_select_ident(skb, &rt->dst, NULL);
+       ip_select_ident(skb, NULL);
        ((u8 *)&iph[1])[0] = IPOPT_RA;
        ((u8 *)&iph[1])[1] = 4;
        ((u8 *)&iph[1])[2] = 0;
@@ -988,16 +988,8 @@ int igmp_rcv(struct sk_buff *skb)
        if (!pskb_may_pull(skb, sizeof(struct igmphdr)))
                goto drop;
 
-       switch (skb->ip_summed) {
-       case CHECKSUM_COMPLETE:
-               if (!csum_fold(skb->csum))
-                       break;
-               /* fall through */
-       case CHECKSUM_NONE:
-               skb->csum = 0;
-               if (__skb_checksum_complete(skb))
-                       goto drop;
-       }
+       if (skb_checksum_simple_validate(skb))
+               goto drop;
 
        ih = igmp_hdr(skb);
        switch (ih->type) {
index a56b8e6e866a8c4327f86111adac947e9ffc2445..14d02ea905b6bea37240f88054f0cd42db73c4c2 100644 (file)
@@ -29,9 +29,6 @@ const char inet_csk_timer_bug_msg[] = "inet_csk BUG: unknown timer value\n";
 EXPORT_SYMBOL(inet_csk_timer_bug_msg);
 #endif
 
-unsigned long *sysctl_local_reserved_ports;
-EXPORT_SYMBOL(sysctl_local_reserved_ports);
-
 void inet_get_local_port_range(struct net *net, int *low, int *high)
 {
        unsigned int seq;
@@ -113,7 +110,7 @@ again:
 
                smallest_size = -1;
                do {
-                       if (inet_is_reserved_local_port(rover))
+                       if (inet_is_local_reserved_port(net, rover))
                                goto next_nolock;
                        head = &hashinfo->bhash[inet_bhashfn(net, rover,
                                        hashinfo->bhash_size)];
@@ -408,7 +405,7 @@ struct dst_entry *inet_csk_route_req(struct sock *sk,
        struct net *net = sock_net(sk);
        int flags = inet_sk_flowi_flags(sk);
 
-       flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark,
+       flowi4_init_output(fl4, sk->sk_bound_dev_if, ireq->ir_mark,
                           RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE,
                           sk->sk_protocol,
                           flags,
@@ -445,7 +442,7 @@ struct dst_entry *inet_csk_route_child_sock(struct sock *sk,
 
        rcu_read_lock();
        opt = rcu_dereference(newinet->inet_opt);
-       flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark,
+       flowi4_init_output(fl4, sk->sk_bound_dev_if, inet_rsk(req)->ir_mark,
                           RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE,
                           sk->sk_protocol, inet_sk_flowi_flags(sk),
                           (opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr,
@@ -680,6 +677,8 @@ struct sock *inet_csk_clone_lock(const struct sock *sk,
                inet_sk(newsk)->inet_sport = htons(inet_rsk(req)->ir_num);
                newsk->sk_write_space = sk_stream_write_space;
 
+               newsk->sk_mark = inet_rsk(req)->ir_mark;
+
                newicsk->icsk_retransmits = 0;
                newicsk->icsk_backoff     = 0;
                newicsk->icsk_probes_out  = 0;
index 8b9cf279450d6cf0c24e64a20fb0d05b9fb89a82..43116e8c8e1323cdd8d902c5b88e42187c8df991 100644 (file)
@@ -274,7 +274,7 @@ struct sock *__inet_lookup_established(struct net *net,
                                  const __be32 daddr, const u16 hnum,
                                  const int dif)
 {
-       INET_ADDR_COOKIE(acookie, saddr, daddr)
+       INET_ADDR_COOKIE(acookie, saddr, daddr);
        const __portpair ports = INET_COMBINED_PORTS(sport, hnum);
        struct sock *sk;
        const struct hlist_nulls_node *node;
@@ -327,7 +327,7 @@ static int __inet_check_established(struct inet_timewait_death_row *death_row,
        __be32 daddr = inet->inet_rcv_saddr;
        __be32 saddr = inet->inet_daddr;
        int dif = sk->sk_bound_dev_if;
-       INET_ADDR_COOKIE(acookie, saddr, daddr)
+       INET_ADDR_COOKIE(acookie, saddr, daddr);
        const __portpair ports = INET_COMBINED_PORTS(inet->inet_dport, lport);
        struct net *net = sock_net(sk);
        unsigned int hash = inet_ehashfn(net, daddr, lport,
@@ -500,7 +500,7 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row,
                local_bh_disable();
                for (i = 1; i <= remaining; i++) {
                        port = low + (i + offset) % remaining;
-                       if (inet_is_reserved_local_port(port))
+                       if (inet_is_local_reserved_port(net, port))
                                continue;
                        head = &hinfo->bhash[inet_bhashfn(net, port,
                                        hinfo->bhash_size)];
index 56cd458a1b8c461f060b7018ed6843124d75a0ca..bd5f5928167dbd44c72fbd1a5eefd475bd29f260 100644 (file)
  *  Theory of operations.
  *  We keep one entry for each peer IP address.  The nodes contains long-living
  *  information about the peer which doesn't depend on routes.
- *  At this moment this information consists only of ID field for the next
- *  outgoing IP packet.  This field is incremented with each packet as encoded
- *  in inet_getid() function (include/net/inetpeer.h).
- *  At the moment of writing this notes identifier of IP packets is generated
- *  to be unpredictable using this code only for packets subjected
- *  (actually or potentially) to defragmentation.  I.e. DF packets less than
- *  PMTU in size when local fragmentation is disabled use a constant ID and do
- *  not use this code (see ip_select_ident() in include/net/ip.h).
  *
- *  Route cache entries hold references to our nodes.
- *  New cache entries get references via lookup by destination IP address in
- *  the avl tree.  The reference is grabbed only when it's needed i.e. only
- *  when we try to output IP packet which needs an unpredictable ID (see
- *  __ip_select_ident() in net/ipv4/route.c).
  *  Nodes are removed only when reference counter goes to 0.
  *  When it's happened the node may be removed when a sufficient amount of
  *  time has been passed since its last use.  The less-recently-used entry can
@@ -62,7 +49,6 @@
  *             refcnt: atomically against modifications on other CPU;
  *                usually under some other lock to prevent node disappearing
  *             daddr: unchangeable
- *             ip_id_count: atomic value (no lock needed)
  */
 
 static struct kmem_cache *peer_cachep __read_mostly;
@@ -120,7 +106,7 @@ int inet_peer_maxttl __read_mostly = 10 * 60 * HZ;  /* usual time to live: 10 min
 static void inetpeer_gc_worker(struct work_struct *work)
 {
        struct inet_peer *p, *n, *c;
-       LIST_HEAD(list);
+       struct list_head list;
 
        spin_lock_bh(&gc_lock);
        list_replace_init(&gc_list, &list);
@@ -497,10 +483,6 @@ relookup:
                p->daddr = *daddr;
                atomic_set(&p->refcnt, 1);
                atomic_set(&p->rid, 0);
-               atomic_set(&p->ip_id_count,
-                               (daddr->family == AF_INET) ?
-                                       secure_ip_id(daddr->addr.a4) :
-                                       secure_ipv6_id(daddr->addr.a6));
                p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW;
                p->rate_tokens = 0;
                /* 60*HZ is arbitrary, but chosen enough high so that the first
index 6f111e48e11c15a19ffd60d345b1399fd3c66953..3a83ce5efa80e3fc2c062ec08465840018159b14 100644 (file)
@@ -42,7 +42,7 @@
 static bool ip_may_fragment(const struct sk_buff *skb)
 {
        return unlikely((ip_hdr(skb)->frag_off & htons(IP_DF)) == 0) ||
-               skb->local_df;
+               skb->ignore_df;
 }
 
 static bool ip_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)
index 94213c89156511d86682b2c3e034f9e5b6ef5ccb..9b842544aea3d8155a28a4b9f8030901bb712a9c 100644 (file)
@@ -410,7 +410,7 @@ static int ipgre_open(struct net_device *dev)
                struct flowi4 fl4;
                struct rtable *rt;
 
-               rt = ip_route_output_gre(dev_net(dev), &fl4,
+               rt = ip_route_output_gre(t->net, &fl4,
                                         t->parms.iph.daddr,
                                         t->parms.iph.saddr,
                                         t->parms.o_key,
@@ -434,7 +434,7 @@ static int ipgre_close(struct net_device *dev)
 
        if (ipv4_is_multicast(t->parms.iph.daddr) && t->mlink) {
                struct in_device *in_dev;
-               in_dev = inetdev_by_index(dev_net(dev), t->mlink);
+               in_dev = inetdev_by_index(t->net, t->mlink);
                if (in_dev)
                        ip_mc_dec_group(in_dev, t->parms.iph.daddr);
        }
@@ -478,7 +478,7 @@ static void __gre_tunnel_init(struct net_device *dev)
        dev->needed_headroom    = LL_MAX_HEADER + sizeof(struct iphdr) + 4;
        dev->mtu                = ETH_DATA_LEN - sizeof(struct iphdr) - 4;
 
-       dev->features           |= NETIF_F_NETNS_LOCAL | GRE_FEATURES;
+       dev->features           |= GRE_FEATURES;
        dev->hw_features        |= GRE_FEATURES;
 
        if (!(tunnel->parms.o_flags & TUNNEL_SEQ)) {
@@ -649,6 +649,7 @@ static void ipgre_tap_setup(struct net_device *dev)
 {
        ether_setup(dev);
        dev->netdev_ops         = &gre_tap_netdev_ops;
+       dev->priv_flags         |= IFF_LIVE_ADDR_CHANGE;
        ip_tunnel_setup(dev, gre_tap_net_id);
 }
 
index f4ab72e19af923536656e734996f4b9201684b58..5e7aecea05cd2afbd3e3e13f417e26687517b468 100644 (file)
@@ -364,7 +364,7 @@ int ip_options_compile(struct net *net,
                        }
                        if (optptr[2] <= optlen) {
                                unsigned char *timeptr = NULL;
-                               if (optptr[2]+3 > optptr[1]) {
+                               if (optptr[2]+3 > optlen) {
                                        pp_ptr = optptr + 2;
                                        goto error;
                                }
@@ -376,7 +376,7 @@ int ip_options_compile(struct net *net,
                                        optptr[2] += 4;
                                        break;
                                case IPOPT_TS_TSANDADDR:
-                                       if (optptr[2]+7 > optptr[1]) {
+                                       if (optptr[2]+7 > optlen) {
                                                pp_ptr = optptr + 2;
                                                goto error;
                                        }
@@ -390,7 +390,7 @@ int ip_options_compile(struct net *net,
                                        optptr[2] += 8;
                                        break;
                                case IPOPT_TS_PRESPEC:
-                                       if (optptr[2]+7 > optptr[1]) {
+                                       if (optptr[2]+7 > optlen) {
                                                pp_ptr = optptr + 2;
                                                goto error;
                                        }
index a52f50187b5495a1157c2ef8e0785da730414022..8d3b6b0e98574b5507f05972cf7fbac8ba32c465 100644 (file)
@@ -148,7 +148,7 @@ int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
        iph->daddr    = (opt && opt->opt.srr ? opt->opt.faddr : daddr);
        iph->saddr    = saddr;
        iph->protocol = sk->sk_protocol;
-       ip_select_ident(skb, &rt->dst, sk);
+       ip_select_ident(skb, sk);
 
        if (opt && opt->opt.optlen) {
                iph->ihl += opt->opt.optlen>>2;
@@ -415,7 +415,7 @@ packet_routed:
        skb_reset_network_header(skb);
        iph = ip_hdr(skb);
        *((__be16 *)iph) = htons((4 << 12) | (5 << 8) | (inet->tos & 0xff));
-       if (ip_dont_fragment(sk, &rt->dst) && !skb->local_df)
+       if (ip_dont_fragment(sk, &rt->dst) && !skb->ignore_df)
                iph->frag_off = htons(IP_DF);
        else
                iph->frag_off = 0;
@@ -430,8 +430,7 @@ packet_routed:
                ip_options_build(skb, &inet_opt->opt, inet->inet_daddr, rt, 0);
        }
 
-       ip_select_ident_more(skb, &rt->dst, sk,
-                            (skb_shinfo(skb)->gso_segs ?: 1) - 1);
+       ip_select_ident_segs(skb, sk, skb_shinfo(skb)->gso_segs ?: 1);
 
        /* TODO : should we use skb->sk here instead of sk ? */
        skb->priority = sk->sk_priority;
@@ -501,7 +500,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
        iph = ip_hdr(skb);
 
        mtu = ip_skb_dst_mtu(skb);
-       if (unlikely(((iph->frag_off & htons(IP_DF)) && !skb->local_df) ||
+       if (unlikely(((iph->frag_off & htons(IP_DF)) && !skb->ignore_df) ||
                     (IPCB(skb)->frag_max_size &&
                      IPCB(skb)->frag_max_size > mtu))) {
                IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
@@ -866,7 +865,7 @@ static int __ip_append_data(struct sock *sk,
 
        fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0);
        maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen;
-       maxnonfragsize = ip_sk_local_df(sk) ? 0xFFFF : mtu;
+       maxnonfragsize = ip_sk_ignore_df(sk) ? 0xFFFF : mtu;
 
        if (cork->length + length > maxnonfragsize - fragheaderlen) {
                ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport,
@@ -1189,7 +1188,7 @@ ssize_t   ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page,
 
        fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0);
        maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen;
-       maxnonfragsize = ip_sk_local_df(sk) ? 0xFFFF : mtu;
+       maxnonfragsize = ip_sk_ignore_df(sk) ? 0xFFFF : mtu;
 
        if (cork->length + size > maxnonfragsize - fragheaderlen) {
                ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport,
@@ -1350,10 +1349,10 @@ struct sk_buff *__ip_make_skb(struct sock *sk,
         * to fragment the frame generated here. No matter, what transforms
         * how transforms change size of the packet, it will come out.
         */
-       skb->local_df = ip_sk_local_df(sk);
+       skb->ignore_df = ip_sk_ignore_df(sk);
 
        /* DF bit is set when we want to see DF on outgoing frames.
-        * If local_df is set too, we still allow to fragment this frame
+        * If ignore_df is set too, we still allow to fragment this frame
         * locally. */
        if (inet->pmtudisc == IP_PMTUDISC_DO ||
            inet->pmtudisc == IP_PMTUDISC_PROBE ||
@@ -1379,7 +1378,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk,
        iph->ttl = ttl;
        iph->protocol = sk->sk_protocol;
        ip_copy_addrs(iph, fl4);
-       ip_select_ident(skb, &rt->dst, sk);
+       ip_select_ident(skb, sk);
 
        if (opt) {
                iph->ihl += opt->optlen>>2;
@@ -1546,7 +1545,8 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
                        daddr = replyopts.opt.opt.faddr;
        }
 
-       flowi4_init_output(&fl4, arg->bound_dev_if, 0,
+       flowi4_init_output(&fl4, arg->bound_dev_if,
+                          IP4_REPLY_MARK(net, skb->mark),
                           RT_TOS(arg->tos),
                           RT_SCOPE_UNIVERSE, ip_hdr(skb)->protocol,
                           ip_reply_arg_flowi_flags(arg),
index 2acc2337d38bfe7f35517e13952cfa8a306c24fd..097b3e7c1e8f89052f6dd686d519a3a9f0624209 100644 (file)
@@ -268,6 +268,7 @@ static struct ip_tunnel *ip_tunnel_find(struct ip_tunnel_net *itn,
        __be32 remote = parms->iph.daddr;
        __be32 local = parms->iph.saddr;
        __be32 key = parms->i_key;
+       __be16 flags = parms->i_flags;
        int link = parms->link;
        struct ip_tunnel *t = NULL;
        struct hlist_head *head = ip_bucket(itn, parms);
@@ -275,9 +276,9 @@ static struct ip_tunnel *ip_tunnel_find(struct ip_tunnel_net *itn,
        hlist_for_each_entry_rcu(t, head, hash_node) {
                if (local == t->parms.iph.saddr &&
                    remote == t->parms.iph.daddr &&
-                   key == t->parms.i_key &&
                    link == t->parms.link &&
-                   type == t->dev->type)
+                   type == t->dev->type &&
+                   ip_tunnel_key_match(&t->parms, flags, key))
                        break;
        }
        return t;
@@ -395,11 +396,10 @@ static struct ip_tunnel *ip_tunnel_create(struct net *net,
                                          struct ip_tunnel_net *itn,
                                          struct ip_tunnel_parm *parms)
 {
-       struct ip_tunnel *nt, *fbt;
+       struct ip_tunnel *nt;
        struct net_device *dev;
 
        BUG_ON(!itn->fb_tunnel_dev);
-       fbt = netdev_priv(itn->fb_tunnel_dev);
        dev = __ip_tunnel_create(net, itn->fb_tunnel_dev->rtnl_link_ops, parms);
        if (IS_ERR(dev))
                return ERR_CAST(dev);
@@ -668,6 +668,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
                dev->needed_headroom = max_headroom;
 
        if (skb_cow_head(skb, dev->needed_headroom)) {
+               ip_rt_put(rt);
                dev->stats.tx_dropped++;
                kfree_skb(skb);
                return;
@@ -747,19 +748,19 @@ int ip_tunnel_ioctl(struct net_device *dev, struct ip_tunnel_parm *p, int cmd)
                        goto done;
                if (p->iph.ttl)
                        p->iph.frag_off |= htons(IP_DF);
-               if (!(p->i_flags&TUNNEL_KEY))
-                       p->i_key = 0;
-               if (!(p->o_flags&TUNNEL_KEY))
-                       p->o_key = 0;
+               if (!(p->i_flags & VTI_ISVTI)) {
+                       if (!(p->i_flags & TUNNEL_KEY))
+                               p->i_key = 0;
+                       if (!(p->o_flags & TUNNEL_KEY))
+                               p->o_key = 0;
+               }
 
                t = ip_tunnel_find(itn, p, itn->fb_tunnel_dev->type);
 
                if (!t && (cmd == SIOCADDTUNNEL)) {
                        t = ip_tunnel_create(net, itn, p);
-                       if (IS_ERR(t)) {
-                               err = PTR_ERR(t);
-                               break;
-                       }
+                       err = PTR_ERR_OR_ZERO(t);
+                       break;
                }
                if (dev != itn->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
                        if (t != NULL) {
index bcf206c79005de251e3c71e387d17e0f928e4aef..f4c987bb7e94a45fd72c245a323fb4cedade4078 100644 (file)
@@ -74,7 +74,7 @@ int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
        iph->daddr      =       dst;
        iph->saddr      =       src;
        iph->ttl        =       ttl;
-       __ip_select_ident(iph, &rt->dst, (skb_shinfo(skb)->gso_segs ?: 1) - 1);
+       __ip_select_ident(iph, skb_shinfo(skb)->gso_segs ?: 1);
 
        err = ip_local_out_sk(sk, skb);
        if (unlikely(net_xmit_eval(err)))
@@ -135,6 +135,14 @@ struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb,
                return skb;
        }
 
+       /* If packet is not gso and we are resolving any partial checksum,
+        * clear encapsulation flag. This allows setting CHECKSUM_PARTIAL
+        * on the outer header without confusing devices that implement
+        * NETIF_F_IP_CSUM with encapsulation.
+        */
+       if (csum_help)
+               skb->encapsulation = 0;
+
        if (skb->ip_summed == CHECKSUM_PARTIAL && csum_help) {
                err = skb_checksum_help(skb);
                if (unlikely(err))
index 13ef00f1e17b88943ddee9ae9ba52b7d0efe7832..b8960f3527f366525088ca930647c9f0c803ad74 100644 (file)
@@ -313,7 +313,13 @@ vti_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                        return -EINVAL;
        }
 
-       p.i_flags |= VTI_ISVTI;
+       if (!(p.i_flags & GRE_KEY))
+               p.i_key = 0;
+       if (!(p.o_flags & GRE_KEY))
+               p.o_key = 0;
+
+       p.i_flags = VTI_ISVTI;
+
        err = ip_tunnel_ioctl(dev, &p, cmd);
        if (err)
                return err;
index 812b1835146255fe065264dd236e901fe6937578..62eaa005e14610237f9e8a6016c366ba6d6b09cf 100644 (file)
@@ -149,13 +149,13 @@ static int ipip_err(struct sk_buff *skb, u32 info)
 
        if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
                ipv4_update_pmtu(skb, dev_net(skb->dev), info,
-                                t->dev->ifindex, 0, IPPROTO_IPIP, 0);
+                                t->parms.link, 0, IPPROTO_IPIP, 0);
                err = 0;
                goto out;
        }
 
        if (type == ICMP_REDIRECT) {
-               ipv4_redirect(skb, dev_net(skb->dev), t->dev->ifindex, 0,
+               ipv4_redirect(skb, dev_net(skb->dev), t->parms.link, 0,
                              IPPROTO_IPIP, 0);
                err = 0;
                goto out;
@@ -486,4 +486,5 @@ static void __exit ipip_fini(void)
 module_init(ipip_init);
 module_exit(ipip_fini);
 MODULE_LICENSE("GPL");
+MODULE_ALIAS_RTNL_LINK("ipip");
 MODULE_ALIAS_NETDEV("tunl0");
index d84dc8d4c916e7f50260a2b29df5d79f4adb4c1e..65bcaa7890436df5ad2ee16f4465af51e141357b 100644 (file)
@@ -484,7 +484,7 @@ static void reg_vif_setup(struct net_device *dev)
        dev->type               = ARPHRD_PIMREG;
        dev->mtu                = ETH_DATA_LEN - sizeof(struct iphdr) - 8;
        dev->flags              = IFF_NOARP;
-       dev->netdev_ops         = &reg_vif_netdev_ops,
+       dev->netdev_ops         = &reg_vif_netdev_ops;
        dev->destructor         = free_netdev;
        dev->features           |= NETIF_F_NETNS_LOCAL;
 }
@@ -1663,7 +1663,7 @@ static void ip_encap(struct sk_buff *skb, __be32 saddr, __be32 daddr)
        iph->protocol   =       IPPROTO_IPIP;
        iph->ihl        =       5;
        iph->tot_len    =       htons(skb->len);
-       ip_select_ident(skb, skb_dst(skb), NULL);
+       ip_select_ident(skb, NULL);
        ip_send_check(iph);
 
        memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
index ee2886126e3dfad44e7c801d82ad2ec4bad621d3..f1787c04a4ddfe06f70cd09c4b4c2573c434042d 100644 (file)
@@ -91,17 +91,9 @@ nf_nat_ipv4_fn(const struct nf_hook_ops *ops,
        if (nf_ct_is_untracked(ct))
                return NF_ACCEPT;
 
-       nat = nfct_nat(ct);
-       if (!nat) {
-               /* NAT module was loaded late. */
-               if (nf_ct_is_confirmed(ct))
-                       return NF_ACCEPT;
-               nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
-               if (nat == NULL) {
-                       pr_debug("failed to add NAT extension\n");
-                       return NF_ACCEPT;
-               }
-       }
+       nat = nf_ct_nat_ext_add(ct);
+       if (nat == NULL)
+               return NF_ACCEPT;
 
        switch (ctinfo) {
        case IP_CT_RELATED:
index f40f321b41fc2e30b21019333efdc5757404fe0a..b8f6381c7d0b15f49973a3748937ea23325bee03 100644 (file)
@@ -34,7 +34,7 @@ static int nf_ct_ipv4_gather_frags(struct sk_buff *skb, u_int32_t user)
 
        if (!err) {
                ip_send_check(ip_hdr(skb));
-               skb->local_df = 1;
+               skb->ignore_df = 1;
        }
 
        return err;
index b5b256d45e67b7f2dd288017b9f728edf72d32c3..3964157d826c197e27ce0578c2da409bbc6e4dec 100644 (file)
@@ -48,15 +48,9 @@ static unsigned int nf_nat_fn(const struct nf_hook_ops *ops,
 
        NF_CT_ASSERT(!(ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)));
 
-       nat = nfct_nat(ct);
-       if (nat == NULL) {
-               /* Conntrack module was loaded late, can't add extension. */
-               if (nf_ct_is_confirmed(ct))
-                       return NF_ACCEPT;
-               nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
-               if (nat == NULL)
-                       return NF_ACCEPT;
-       }
+       nat = nf_ct_nat_ext_add(ct);
+       if (nat == NULL)
+               return NF_ACCEPT;
 
        switch (ctinfo) {
        case IP_CT_RELATED:
index ad737fad6d8b82dec74fab1260015e539647271d..ae0af9386f7ccf4cabafbcc8aff6c37309e820ea 100644 (file)
@@ -345,15 +345,15 @@ static void icmp_put(struct seq_file *seq)
        for (i = 0; icmpmibmap[i].name != NULL; i++)
                seq_printf(seq, " Out%s", icmpmibmap[i].name);
        seq_printf(seq, "\nIcmp: %lu %lu %lu",
-               snmp_fold_field((void __percpu **) net->mib.icmp_statistics, ICMP_MIB_INMSGS),
-               snmp_fold_field((void __percpu **) net->mib.icmp_statistics, ICMP_MIB_INERRORS),
-               snmp_fold_field((void __percpu **) net->mib.icmp_statistics, ICMP_MIB_CSUMERRORS));
+               snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_INMSGS),
+               snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_INERRORS),
+               snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_CSUMERRORS));
        for (i = 0; icmpmibmap[i].name != NULL; i++)
                seq_printf(seq, " %lu",
                           atomic_long_read(ptr + icmpmibmap[i].index));
        seq_printf(seq, " %lu %lu",
-               snmp_fold_field((void __percpu **) net->mib.icmp_statistics, ICMP_MIB_OUTMSGS),
-               snmp_fold_field((void __percpu **) net->mib.icmp_statistics, ICMP_MIB_OUTERRORS));
+               snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_OUTMSGS),
+               snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_OUTERRORS));
        for (i = 0; icmpmibmap[i].name != NULL; i++)
                seq_printf(seq, " %lu",
                           atomic_long_read(ptr + (icmpmibmap[i].index | 0x100)));
@@ -379,7 +379,7 @@ static int snmp_seq_show(struct seq_file *seq, void *v)
        BUILD_BUG_ON(offsetof(struct ipstats_mib, mibs) != 0);
        for (i = 0; snmp4_ipstats_list[i].name != NULL; i++)
                seq_printf(seq, " %llu",
-                          snmp_fold_field64((void __percpu **)net->mib.ip_statistics,
+                          snmp_fold_field64(net->mib.ip_statistics,
                                             snmp4_ipstats_list[i].entry,
                                             offsetof(struct ipstats_mib, syncp)));
 
@@ -395,11 +395,11 @@ static int snmp_seq_show(struct seq_file *seq, void *v)
                /* MaxConn field is signed, RFC 2012 */
                if (snmp4_tcp_list[i].entry == TCP_MIB_MAXCONN)
                        seq_printf(seq, " %ld",
-                                  snmp_fold_field((void __percpu **)net->mib.tcp_statistics,
+                                  snmp_fold_field(net->mib.tcp_statistics,
                                                   snmp4_tcp_list[i].entry));
                else
                        seq_printf(seq, " %lu",
-                                  snmp_fold_field((void __percpu **)net->mib.tcp_statistics,
+                                  snmp_fold_field(net->mib.tcp_statistics,
                                                   snmp4_tcp_list[i].entry));
        }
 
@@ -410,7 +410,7 @@ static int snmp_seq_show(struct seq_file *seq, void *v)
        seq_puts(seq, "\nUdp:");
        for (i = 0; snmp4_udp_list[i].name != NULL; i++)
                seq_printf(seq, " %lu",
-                          snmp_fold_field((void __percpu **)net->mib.udp_statistics,
+                          snmp_fold_field(net->mib.udp_statistics,
                                           snmp4_udp_list[i].entry));
 
        /* the UDP and UDP-Lite MIBs are the same */
@@ -421,7 +421,7 @@ static int snmp_seq_show(struct seq_file *seq, void *v)
        seq_puts(seq, "\nUdpLite:");
        for (i = 0; snmp4_udp_list[i].name != NULL; i++)
                seq_printf(seq, " %lu",
-                          snmp_fold_field((void __percpu **)net->mib.udplite_statistics,
+                          snmp_fold_field(net->mib.udplite_statistics,
                                           snmp4_udp_list[i].entry));
 
        seq_putc(seq, '\n');
@@ -458,7 +458,7 @@ static int netstat_seq_show(struct seq_file *seq, void *v)
        seq_puts(seq, "\nTcpExt:");
        for (i = 0; snmp4_net_list[i].name != NULL; i++)
                seq_printf(seq, " %lu",
-                          snmp_fold_field((void __percpu **)net->mib.net_statistics,
+                          snmp_fold_field(net->mib.net_statistics,
                                           snmp4_net_list[i].entry));
 
        seq_puts(seq, "\nIpExt:");
@@ -468,7 +468,7 @@ static int netstat_seq_show(struct seq_file *seq, void *v)
        seq_puts(seq, "\nIpExt:");
        for (i = 0; snmp4_ipextstats_list[i].name != NULL; i++)
                seq_printf(seq, " %llu",
-                          snmp_fold_field64((void __percpu **)net->mib.ip_statistics,
+                          snmp_fold_field64(net->mib.ip_statistics,
                                             snmp4_ipextstats_list[i].entry,
                                             offsetof(struct ipstats_mib, syncp)));
 
index a9dbe58bdfe767e62642db954b89532dab928a96..2c65160565e1a084b5ff13832cfdb80d50ba7d6c 100644 (file)
@@ -389,7 +389,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
                iph->check   = 0;
                iph->tot_len = htons(length);
                if (!iph->id)
-                       ip_select_ident(skb, &rt->dst, NULL);
+                       ip_select_ident(skb, NULL);
 
                iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
        }
index 5e676be3daeb19e5f48df5e767a66b83ac73b432..082239ffe34a1f42e62e9ac7901f9fe5219fdca6 100644 (file)
@@ -89,6 +89,7 @@
 #include <linux/rcupdate.h>
 #include <linux/times.h>
 #include <linux/slab.h>
+#include <linux/jhash.h>
 #include <net/dst.h>
 #include <net/net_namespace.h>
 #include <net/protocol.h>
@@ -456,39 +457,19 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,
        return neigh_create(&arp_tbl, pkey, dev);
 }
 
-/*
- * Peer allocation may fail only in serious out-of-memory conditions.  However
- * we still can generate some output.
- * Random ID selection looks a bit dangerous because we have no chances to
- * select ID being unique in a reasonable period of time.
- * But broken packet identifier may be better than no packet at all.
- */
-static void ip_select_fb_ident(struct iphdr *iph)
-{
-       static DEFINE_SPINLOCK(ip_fb_id_lock);
-       static u32 ip_fallback_id;
-       u32 salt;
-
-       spin_lock_bh(&ip_fb_id_lock);
-       salt = secure_ip_id((__force __be32)ip_fallback_id ^ iph->daddr);
-       iph->id = htons(salt & 0xFFFF);
-       ip_fallback_id = salt;
-       spin_unlock_bh(&ip_fb_id_lock);
-}
+atomic_t *ip_idents __read_mostly;
+EXPORT_SYMBOL(ip_idents);
 
-void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst, int more)
+void __ip_select_ident(struct iphdr *iph, int segs)
 {
-       struct net *net = dev_net(dst->dev);
-       struct inet_peer *peer;
+       static u32 ip_idents_hashrnd __read_mostly;
+       u32 hash, id;
 
-       peer = inet_getpeer_v4(net->ipv4.peers, iph->daddr, 1);
-       if (peer) {
-               iph->id = htons(inet_getid(peer, more));
-               inet_putpeer(peer);
-               return;
-       }
+       net_get_random_once(&ip_idents_hashrnd, sizeof(ip_idents_hashrnd));
 
-       ip_select_fb_ident(iph);
+       hash = jhash_1word((__force u32)iph->daddr, ip_idents_hashrnd);
+       id = ip_idents_reserve(hash, segs);
+       iph->id = htons(id);
 }
 EXPORT_SYMBOL(__ip_select_ident);
 
@@ -993,6 +974,9 @@ void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu,
        struct flowi4 fl4;
        struct rtable *rt;
 
+       if (!mark)
+               mark = IP4_REPLY_MARK(net, skb->mark);
+
        __build_flow_key(&fl4, NULL, iph, oif,
                         RT_TOS(iph->tos), protocol, mark, flow_flags);
        rt = __ip_route_output_key(net, &fl4);
@@ -1010,6 +994,10 @@ static void __ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu)
        struct rtable *rt;
 
        __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0);
+
+       if (!fl4.flowi4_mark)
+               fl4.flowi4_mark = IP4_REPLY_MARK(sock_net(sk), skb->mark);
+
        rt = __ip_route_output_key(sock_net(sk), &fl4);
        if (!IS_ERR(rt)) {
                __ip_rt_update_pmtu(rt, &fl4, mtu);
@@ -2704,6 +2692,12 @@ int __init ip_rt_init(void)
 {
        int rc = 0;
 
+       ip_idents = kmalloc(IP_IDENTS_SZ * sizeof(*ip_idents), GFP_KERNEL);
+       if (!ip_idents)
+               panic("IP: failed to allocate ip_idents\n");
+
+       prandom_bytes(ip_idents, IP_IDENTS_SZ * sizeof(*ip_idents));
+
 #ifdef CONFIG_IP_ROUTE_CLASSID
        ip_rt_acct = __alloc_percpu(256 * sizeof(struct ip_rt_acct), __alignof__(struct ip_rt_acct));
        if (!ip_rt_acct)
index f2ed13c2125f7d34820c9e92a3080678f30f46fd..c86624b36a62ece1dd34bf39561d52e34f467bd3 100644 (file)
@@ -303,6 +303,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
        ireq->ir_rmt_port       = th->source;
        ireq->ir_loc_addr       = ip_hdr(skb)->daddr;
        ireq->ir_rmt_addr       = ip_hdr(skb)->saddr;
+       ireq->ir_mark           = inet_request_mark(sk, skb);
        ireq->ecn_ok            = ecn_ok;
        ireq->snd_wscale        = tcp_opt.snd_wscale;
        ireq->sack_ok           = tcp_opt.sack_ok;
@@ -339,7 +340,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
         * hasn't changed since we received the original syn, but I see
         * no easy way to do this.
         */
-       flowi4_init_output(&fl4, sk->sk_bound_dev_if, sk->sk_mark,
+       flowi4_init_output(&fl4, sk->sk_bound_dev_if, ireq->ir_mark,
                           RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, IPPROTO_TCP,
                           inet_sk_flowi_flags(sk),
                           (opt && opt->srr) ? opt->faddr : ireq->ir_rmt_addr,
index 5cde8f263d40c0eb0204d310fee99146dabb7a87..79a007c5255883f9d96011682c67ea8aac15a835 100644 (file)
@@ -436,13 +436,6 @@ static struct ctl_table ipv4_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec
        },
-       {
-               .procname       = "ip_local_reserved_ports",
-               .data           = NULL, /* initialized in sysctl_ipv4_init */
-               .maxlen         = 65536,
-               .mode           = 0644,
-               .proc_handler   = proc_do_large_bitmap,
-       },
        {
                .procname       = "igmp_max_memberships",
                .data           = &sysctl_igmp_max_memberships,
@@ -824,6 +817,13 @@ static struct ctl_table ipv4_net_table[] = {
                .mode           = 0644,
                .proc_handler   = ipv4_local_port_range,
        },
+       {
+               .procname       = "ip_local_reserved_ports",
+               .data           = &init_net.ipv4.sysctl_local_reserved_ports,
+               .maxlen         = 65536,
+               .mode           = 0644,
+               .proc_handler   = proc_do_large_bitmap,
+       },
        {
                .procname       = "ip_no_pmtu_disc",
                .data           = &init_net.ipv4.sysctl_ip_no_pmtu_disc,
@@ -838,6 +838,20 @@ static struct ctl_table ipv4_net_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec,
        },
+       {
+               .procname       = "fwmark_reflect",
+               .data           = &init_net.ipv4.sysctl_fwmark_reflect,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "tcp_fwmark_accept",
+               .data           = &init_net.ipv4.sysctl_tcp_fwmark_accept,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
        { }
 };
 
@@ -862,8 +876,14 @@ static __net_init int ipv4_sysctl_init_net(struct net *net)
        if (net->ipv4.ipv4_hdr == NULL)
                goto err_reg;
 
+       net->ipv4.sysctl_local_reserved_ports = kzalloc(65536 / 8, GFP_KERNEL);
+       if (!net->ipv4.sysctl_local_reserved_ports)
+               goto err_ports;
+
        return 0;
 
+err_ports:
+       unregister_net_sysctl_table(net->ipv4.ipv4_hdr);
 err_reg:
        if (!net_eq(net, &init_net))
                kfree(table);
@@ -875,6 +895,7 @@ static __net_exit void ipv4_sysctl_exit_net(struct net *net)
 {
        struct ctl_table *table;
 
+       kfree(net->ipv4.sysctl_local_reserved_ports);
        table = net->ipv4.ipv4_hdr->ctl_table_arg;
        unregister_net_sysctl_table(net->ipv4.ipv4_hdr);
        kfree(table);
@@ -888,16 +909,6 @@ static __net_initdata struct pernet_operations ipv4_sysctl_ops = {
 static __init int sysctl_ipv4_init(void)
 {
        struct ctl_table_header *hdr;
-       struct ctl_table *i;
-
-       for (i = ipv4_table; i->procname; i++) {
-               if (strcmp(i->procname, "ip_local_reserved_ports") == 0) {
-                       i->data = sysctl_local_reserved_ports;
-                       break;
-               }
-       }
-       if (!i->procname)
-               return -EINVAL;
 
        hdr = register_net_sysctl(&init_net, "net/ipv4", ipv4_table);
        if (hdr == NULL)
index 4bd6d52eeffb6c7ddc3e98054a7070826a4c4dcd..eb1dde37e678f6bb1570bfb452c019b88eb1a76c 100644 (file)
@@ -2916,6 +2916,14 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
        case TCP_USER_TIMEOUT:
                val = jiffies_to_msecs(icsk->icsk_user_timeout);
                break;
+
+       case TCP_FASTOPEN:
+               if (icsk->icsk_accept_queue.fastopenq != NULL)
+                       val = icsk->icsk_accept_queue.fastopenq->max_qlen;
+               else
+                       val = 0;
+               break;
+
        case TCP_TIMESTAMP:
                val = tcp_time_stamp + tp->tsoffset;
                break;
index 821846fb0a7e211fc870b1afce5991bc3de28494..d5de69bc04f581ac589ae0f6fe7fcc3646b2cc3e 100644 (file)
@@ -140,13 +140,12 @@ static inline void bictcp_update(struct bictcp *ca, u32 cwnd)
                ca->cnt = 1;
 }
 
-static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked,
-                             u32 in_flight)
+static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct bictcp *ca = inet_csk_ca(sk);
 
-       if (!tcp_is_cwnd_limited(sk, in_flight))
+       if (!tcp_is_cwnd_limited(sk))
                return;
 
        if (tp->snd_cwnd <= tp->snd_ssthresh)
index 2b9464c93b8859fcbef0f900f70a4bed2dc6e617..7b09d8b49fa51271cc0fa99a3fcda9f017c0f469 100644 (file)
@@ -276,26 +276,6 @@ int tcp_set_congestion_control(struct sock *sk, const char *name)
        return err;
 }
 
-/* RFC2861 Check whether we are limited by application or congestion window
- * This is the inverse of cwnd check in tcp_tso_should_defer
- */
-bool tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight)
-{
-       const struct tcp_sock *tp = tcp_sk(sk);
-       u32 left;
-
-       if (in_flight >= tp->snd_cwnd)
-               return true;
-
-       left = tp->snd_cwnd - in_flight;
-       if (sk_can_gso(sk) &&
-           left * sysctl_tcp_tso_win_divisor < tp->snd_cwnd &&
-           left < tp->xmit_size_goal_segs)
-               return true;
-       return left <= tcp_max_tso_deferred_mss(tp);
-}
-EXPORT_SYMBOL_GPL(tcp_is_cwnd_limited);
-
 /* Slow start is used when congestion window is no greater than the slow start
  * threshold. We base on RFC2581 and also handle stretch ACKs properly.
  * We do not implement RFC3465 Appropriate Byte Counting (ABC) per se but
@@ -337,11 +317,11 @@ EXPORT_SYMBOL_GPL(tcp_cong_avoid_ai);
 /* This is Jacobson's slow start and congestion avoidance.
  * SIGCOMM '88, p. 328.
  */
-void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight)
+void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
-       if (!tcp_is_cwnd_limited(sk, in_flight))
+       if (!tcp_is_cwnd_limited(sk))
                return;
 
        /* In "safe" area, increase. */
index b4f1b29b08bdf14acfc6153cd443c9e799eeb141..a9bd8a4828a9e5c2da275626c11c6c953a1b3dd6 100644 (file)
@@ -304,13 +304,12 @@ static inline void bictcp_update(struct bictcp *ca, u32 cwnd)
                ca->cnt = 1;
 }
 
-static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked,
-                             u32 in_flight)
+static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct bictcp *ca = inet_csk_ca(sk);
 
-       if (!tcp_is_cwnd_limited(sk, in_flight))
+       if (!tcp_is_cwnd_limited(sk))
                return;
 
        if (tp->snd_cwnd <= tp->snd_ssthresh) {
index f195d9316e55d3d7ef6dc5285606ea9de0f52a51..62e48cf84e602a005ab2ce61c058ca709702098a 100644 (file)
@@ -72,25 +72,224 @@ error:             kfree(ctx);
        return err;
 }
 
-/* Computes the fastopen cookie for the IP path.
- * The path is a 128 bits long (pad with zeros for IPv4).
- *
- * The caller must check foc->len to determine if a valid cookie
- * has been generated successfully.
-*/
-void tcp_fastopen_cookie_gen(__be32 src, __be32 dst,
-                            struct tcp_fastopen_cookie *foc)
+static bool __tcp_fastopen_cookie_gen(const void *path,
+                                     struct tcp_fastopen_cookie *foc)
 {
-       __be32 path[4] = { src, dst, 0, 0 };
        struct tcp_fastopen_context *ctx;
+       bool ok = false;
 
        tcp_fastopen_init_key_once(true);
 
        rcu_read_lock();
        ctx = rcu_dereference(tcp_fastopen_ctx);
        if (ctx) {
-               crypto_cipher_encrypt_one(ctx->tfm, foc->val, (__u8 *)path);
+               crypto_cipher_encrypt_one(ctx->tfm, foc->val, path);
                foc->len = TCP_FASTOPEN_COOKIE_SIZE;
+               ok = true;
        }
        rcu_read_unlock();
+       return ok;
+}
+
+/* Generate the fastopen cookie by doing aes128 encryption on both
+ * the source and destination addresses. Pad 0s for IPv4 or IPv4-mapped-IPv6
+ * addresses. For the longer IPv6 addresses use CBC-MAC.
+ *
+ * XXX (TFO) - refactor when TCP_FASTOPEN_COOKIE_SIZE != AES_BLOCK_SIZE.
+ */
+static bool tcp_fastopen_cookie_gen(struct request_sock *req,
+                                   struct sk_buff *syn,
+                                   struct tcp_fastopen_cookie *foc)
+{
+       if (req->rsk_ops->family == AF_INET) {
+               const struct iphdr *iph = ip_hdr(syn);
+
+               __be32 path[4] = { iph->saddr, iph->daddr, 0, 0 };
+               return __tcp_fastopen_cookie_gen(path, foc);
+       }
+
+#if IS_ENABLED(CONFIG_IPV6)
+       if (req->rsk_ops->family == AF_INET6) {
+               const struct ipv6hdr *ip6h = ipv6_hdr(syn);
+               struct tcp_fastopen_cookie tmp;
+
+               if (__tcp_fastopen_cookie_gen(&ip6h->saddr, &tmp)) {
+                       struct in6_addr *buf = (struct in6_addr *) tmp.val;
+                       int i = 4;
+
+                       for (i = 0; i < 4; i++)
+                               buf->s6_addr32[i] ^= ip6h->daddr.s6_addr32[i];
+                       return __tcp_fastopen_cookie_gen(buf, foc);
+               }
+       }
+#endif
+       return false;
+}
+
+static bool tcp_fastopen_create_child(struct sock *sk,
+                                     struct sk_buff *skb,
+                                     struct dst_entry *dst,
+                                     struct request_sock *req)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+       struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue;
+       struct sock *child;
+
+       req->num_retrans = 0;
+       req->num_timeout = 0;
+       req->sk = NULL;
+
+       child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL);
+       if (child == NULL)
+               return false;
+
+       spin_lock(&queue->fastopenq->lock);
+       queue->fastopenq->qlen++;
+       spin_unlock(&queue->fastopenq->lock);
+
+       /* Initialize the child socket. Have to fix some values to take
+        * into account the child is a Fast Open socket and is created
+        * only out of the bits carried in the SYN packet.
+        */
+       tp = tcp_sk(child);
+
+       tp->fastopen_rsk = req;
+       /* Do a hold on the listner sk so that if the listener is being
+        * closed, the child that has been accepted can live on and still
+        * access listen_lock.
+        */
+       sock_hold(sk);
+       tcp_rsk(req)->listener = sk;
+
+       /* RFC1323: The window in SYN & SYN/ACK segments is never
+        * scaled. So correct it appropriately.
+        */
+       tp->snd_wnd = ntohs(tcp_hdr(skb)->window);
+
+       /* Activate the retrans timer so that SYNACK can be retransmitted.
+        * The request socket is not added to the SYN table of the parent
+        * because it's been added to the accept queue directly.
+        */
+       inet_csk_reset_xmit_timer(child, ICSK_TIME_RETRANS,
+                                 TCP_TIMEOUT_INIT, TCP_RTO_MAX);
+
+       /* Add the child socket directly into the accept queue */
+       inet_csk_reqsk_queue_add(sk, req, child);
+
+       /* Now finish processing the fastopen child socket. */
+       inet_csk(child)->icsk_af_ops->rebuild_header(child);
+       tcp_init_congestion_control(child);
+       tcp_mtup_init(child);
+       tcp_init_metrics(child);
+       tcp_init_buffer_space(child);
+
+       /* Queue the data carried in the SYN packet. We need to first
+        * bump skb's refcnt because the caller will attempt to free it.
+        *
+        * XXX (TFO) - we honor a zero-payload TFO request for now,
+        * (any reason not to?) but no need to queue the skb since
+        * there is no data. How about SYN+FIN?
+        */
+       if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1) {
+               skb = skb_get(skb);
+               skb_dst_drop(skb);
+               __skb_pull(skb, tcp_hdr(skb)->doff * 4);
+               skb_set_owner_r(skb, child);
+               __skb_queue_tail(&child->sk_receive_queue, skb);
+               tp->syn_data_acked = 1;
+       }
+       tcp_rsk(req)->rcv_nxt = tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
+       sk->sk_data_ready(sk);
+       bh_unlock_sock(child);
+       sock_put(child);
+       WARN_ON(req->sk == NULL);
+       return true;
+}
+EXPORT_SYMBOL(tcp_fastopen_create_child);
+
+static bool tcp_fastopen_queue_check(struct sock *sk)
+{
+       struct fastopen_queue *fastopenq;
+
+       /* Make sure the listener has enabled fastopen, and we don't
+        * exceed the max # of pending TFO requests allowed before trying
+        * to validating the cookie in order to avoid burning CPU cycles
+        * unnecessarily.
+        *
+        * XXX (TFO) - The implication of checking the max_qlen before
+        * processing a cookie request is that clients can't differentiate
+        * between qlen overflow causing Fast Open to be disabled
+        * temporarily vs a server not supporting Fast Open at all.
+        */
+       fastopenq = inet_csk(sk)->icsk_accept_queue.fastopenq;
+       if (fastopenq == NULL || fastopenq->max_qlen == 0)
+               return false;
+
+       if (fastopenq->qlen >= fastopenq->max_qlen) {
+               struct request_sock *req1;
+               spin_lock(&fastopenq->lock);
+               req1 = fastopenq->rskq_rst_head;
+               if ((req1 == NULL) || time_after(req1->expires, jiffies)) {
+                       spin_unlock(&fastopenq->lock);
+                       NET_INC_STATS_BH(sock_net(sk),
+                                        LINUX_MIB_TCPFASTOPENLISTENOVERFLOW);
+                       return false;
+               }
+               fastopenq->rskq_rst_head = req1->dl_next;
+               fastopenq->qlen--;
+               spin_unlock(&fastopenq->lock);
+               reqsk_free(req1);
+       }
+       return true;
+}
+
+/* Returns true if we should perform Fast Open on the SYN. The cookie (foc)
+ * may be updated and return the client in the SYN-ACK later. E.g., Fast Open
+ * cookie request (foc->len == 0).
+ */
+bool tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
+                     struct request_sock *req,
+                     struct tcp_fastopen_cookie *foc,
+                     struct dst_entry *dst)
+{
+       struct tcp_fastopen_cookie valid_foc = { .len = -1 };
+       bool syn_data = TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1;
+
+       if (!((sysctl_tcp_fastopen & TFO_SERVER_ENABLE) &&
+             (syn_data || foc->len >= 0) &&
+             tcp_fastopen_queue_check(sk))) {
+               foc->len = -1;
+               return false;
+       }
+
+       if (syn_data && (sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_REQD))
+               goto fastopen;
+
+       if (tcp_fastopen_cookie_gen(req, skb, &valid_foc) &&
+           foc->len == TCP_FASTOPEN_COOKIE_SIZE &&
+           foc->len == valid_foc.len &&
+           !memcmp(foc->val, valid_foc.val, foc->len)) {
+               /* Cookie is valid. Create a (full) child socket to accept
+                * the data in SYN before returning a SYN-ACK to ack the
+                * data. If we fail to create the socket, fall back and
+                * ack the ISN only but includes the same cookie.
+                *
+                * Note: Data-less SYN with valid cookie is allowed to send
+                * data in SYN_RECV state.
+                */
+fastopen:
+               if (tcp_fastopen_create_child(sk, skb, dst, req)) {
+                       foc->len = -1;
+                       NET_INC_STATS_BH(sock_net(sk),
+                                        LINUX_MIB_TCPFASTOPENPASSIVE);
+                       return true;
+               }
+       }
+
+       NET_INC_STATS_BH(sock_net(sk), foc->len ?
+                        LINUX_MIB_TCPFASTOPENPASSIVEFAIL :
+                        LINUX_MIB_TCPFASTOPENCOOKIEREQD);
+       *foc = valid_foc;
+       return false;
 }
+EXPORT_SYMBOL(tcp_try_fastopen);
index 8b9e7bad77c09a0c07706b955c29b81d4e71a542..1c4908280d921fbe9a9e21e4fd55d1a87f2db706 100644 (file)
@@ -109,12 +109,12 @@ static void hstcp_init(struct sock *sk)
        tp->snd_cwnd_clamp = min_t(u32, tp->snd_cwnd_clamp, 0xffffffff/128);
 }
 
-static void hstcp_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight)
+static void hstcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct hstcp *ca = inet_csk_ca(sk);
 
-       if (!tcp_is_cwnd_limited(sk, in_flight))
+       if (!tcp_is_cwnd_limited(sk))
                return;
 
        if (tp->snd_cwnd <= tp->snd_ssthresh)
index 4a194acfd9237f1aaadfe9f9831358f5c9037cf7..031361311a8b92b1f7ab1172fb00e3bbb0503129 100644 (file)
@@ -227,12 +227,12 @@ static u32 htcp_recalc_ssthresh(struct sock *sk)
        return max((tp->snd_cwnd * ca->beta) >> 7, 2U);
 }
 
-static void htcp_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight)
+static void htcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct htcp *ca = inet_csk_ca(sk);
 
-       if (!tcp_is_cwnd_limited(sk, in_flight))
+       if (!tcp_is_cwnd_limited(sk))
                return;
 
        if (tp->snd_cwnd <= tp->snd_ssthresh)
index a15a799bf76888f3633b27cf57e8e05f30339601..d8f8f05a49516ec2d9213f10af77b82fbf32bff7 100644 (file)
@@ -87,8 +87,7 @@ static inline u32 hybla_fraction(u32 odds)
  *     o Give cwnd a new value based on the model proposed
  *     o remember increments <1
  */
-static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 acked,
-                            u32 in_flight)
+static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct hybla *ca = inet_csk_ca(sk);
@@ -101,11 +100,11 @@ static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 acked,
                ca->minrtt_us = tp->srtt_us;
        }
 
-       if (!tcp_is_cwnd_limited(sk, in_flight))
+       if (!tcp_is_cwnd_limited(sk))
                return;
 
        if (!ca->hybla_en) {
-               tcp_reno_cong_avoid(sk, ack, acked, in_flight);
+               tcp_reno_cong_avoid(sk, ack, acked);
                return;
        }
 
index 863d105e30150391e9ac3ce8545c0411e4d083ab..5999b3972e6449d616facb628d27116ab8876ac2 100644 (file)
@@ -255,8 +255,7 @@ static void tcp_illinois_state(struct sock *sk, u8 new_state)
 /*
  * Increase window in response to successful acknowledgment.
  */
-static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 acked,
-                                   u32 in_flight)
+static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct illinois *ca = inet_csk_ca(sk);
@@ -265,7 +264,7 @@ static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 acked,
                update_params(sk);
 
        /* RFC2861 only increase cwnd if fully utilized */
-       if (!tcp_is_cwnd_limited(sk, in_flight))
+       if (!tcp_is_cwnd_limited(sk))
                return;
 
        /* In slow start */
index 3a26b3b23f1614340c0c1fe54204fe0999eb0691..40661fc1e233a28594e62640de835b8f44f7794d 100644 (file)
@@ -1167,7 +1167,7 @@ static int tcp_match_skb_to_sack(struct sock *sk, struct sk_buff *skb,
                        }
                        pkt_len = new_len;
                }
-               err = tcp_fragment(sk, skb, pkt_len, mss);
+               err = tcp_fragment(sk, skb, pkt_len, mss, GFP_ATOMIC);
                if (err < 0)
                        return err;
        }
@@ -2241,7 +2241,8 @@ static void tcp_mark_head_lost(struct sock *sk, int packets, int mark_head)
                                break;
 
                        mss = skb_shinfo(skb)->gso_size;
-                       err = tcp_fragment(sk, skb, (packets - oldcnt) * mss, mss);
+                       err = tcp_fragment(sk, skb, (packets - oldcnt) * mss,
+                                          mss, GFP_ATOMIC);
                        if (err < 0)
                                break;
                        cnt = packets;
@@ -2937,10 +2938,11 @@ static void tcp_synack_rtt_meas(struct sock *sk, const u32 synack_stamp)
                tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, seq_rtt_us, -1L);
 }
 
-static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight)
+static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        const struct inet_connection_sock *icsk = inet_csk(sk);
-       icsk->icsk_ca_ops->cong_avoid(sk, ack, acked, in_flight);
+
+       icsk->icsk_ca_ops->cong_avoid(sk, ack, acked);
        tcp_sk(sk)->snd_cwnd_stamp = tcp_time_stamp;
 }
 
@@ -3363,7 +3365,6 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
        u32 ack_seq = TCP_SKB_CB(skb)->seq;
        u32 ack = TCP_SKB_CB(skb)->ack_seq;
        bool is_dupack = false;
-       u32 prior_in_flight;
        u32 prior_fackets;
        int prior_packets = tp->packets_out;
        const int prior_unsacked = tp->packets_out - tp->sacked_out;
@@ -3396,7 +3397,6 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
                flag |= FLAG_SND_UNA_ADVANCED;
 
        prior_fackets = tp->fackets_out;
-       prior_in_flight = tcp_packets_in_flight(tp);
 
        /* ts_recent update must be made after we are sure that the packet
         * is in window.
@@ -3451,7 +3451,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
 
        /* Advance cwnd if state allows */
        if (tcp_may_raise_cwnd(sk, flag))
-               tcp_cong_avoid(sk, ack, acked, prior_in_flight);
+               tcp_cong_avoid(sk, ack, acked);
 
        if (tcp_ack_is_dubious(sk, flag)) {
                is_dupack = !(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP));
@@ -4702,28 +4702,6 @@ static int tcp_prune_queue(struct sock *sk)
        return -1;
 }
 
-/* RFC2861, slow part. Adjust cwnd, after it was not full during one rto.
- * As additional protections, we do not touch cwnd in retransmission phases,
- * and if application hit its sndbuf limit recently.
- */
-void tcp_cwnd_application_limited(struct sock *sk)
-{
-       struct tcp_sock *tp = tcp_sk(sk);
-
-       if (inet_csk(sk)->icsk_ca_state == TCP_CA_Open &&
-           sk->sk_socket && !test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) {
-               /* Limited by application or receiver window. */
-               u32 init_win = tcp_init_cwnd(tp, __sk_dst_get(sk));
-               u32 win_used = max(tp->snd_cwnd_used, init_win);
-               if (win_used < tp->snd_cwnd) {
-                       tp->snd_ssthresh = tcp_current_ssthresh(sk);
-                       tp->snd_cwnd = (tp->snd_cwnd + win_used) >> 1;
-               }
-               tp->snd_cwnd_used = 0;
-       }
-       tp->snd_cwnd_stamp = tcp_time_stamp;
-}
-
 static bool tcp_should_expand_sndbuf(const struct sock *sk)
 {
        const struct tcp_sock *tp = tcp_sk(sk);
index 438f3b95143df0bffa01322c62b44c8fcd6d6b8b..77cccda1ad0c6dc62c8cb70d932eca2322304c81 100644 (file)
@@ -336,8 +336,8 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
        const int code = icmp_hdr(icmp_skb)->code;
        struct sock *sk;
        struct sk_buff *skb;
-       struct request_sock *req;
-       __u32 seq;
+       struct request_sock *fastopen;
+       __u32 seq, snd_una;
        __u32 remaining;
        int err;
        struct net *net = dev_net(icmp_skb->dev);
@@ -378,12 +378,12 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
 
        icsk = inet_csk(sk);
        tp = tcp_sk(sk);
-       req = tp->fastopen_rsk;
        seq = ntohl(th->seq);
+       /* XXX (TFO) - tp->snd_una should be ISN (tcp_create_openreq_child() */
+       fastopen = tp->fastopen_rsk;
+       snd_una = fastopen ? tcp_rsk(fastopen)->snt_isn : tp->snd_una;
        if (sk->sk_state != TCP_LISTEN &&
-           !between(seq, tp->snd_una, tp->snd_nxt) &&
-           (req == NULL || seq != tcp_rsk(req)->snt_isn)) {
-               /* For a Fast Open socket, allow seq to be snt_isn. */
+           !between(seq, snd_una, tp->snd_nxt)) {
                NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
                goto out;
        }
@@ -426,11 +426,9 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
                if (code != ICMP_NET_UNREACH && code != ICMP_HOST_UNREACH)
                        break;
                if (seq != tp->snd_una  || !icsk->icsk_retransmits ||
-                   !icsk->icsk_backoff)
+                   !icsk->icsk_backoff || fastopen)
                        break;
 
-               /* XXX (TFO) - revisit the following logic for TFO */
-
                if (sock_owned_by_user(sk))
                        break;
 
@@ -462,14 +460,6 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
                goto out;
        }
 
-       /* XXX (TFO) - if it's a TFO socket and has been accepted, rather
-        * than following the TCP_SYN_RECV case and closing the socket,
-        * we ignore the ICMP error and keep trying like a fully established
-        * socket. Is this the right thing to do?
-        */
-       if (req && req->sk == NULL)
-               goto out;
-
        switch (sk->sk_state) {
                struct request_sock *req, **prev;
        case TCP_LISTEN:
@@ -502,10 +492,13 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
                goto out;
 
        case TCP_SYN_SENT:
-       case TCP_SYN_RECV:  /* Cannot happen.
-                              It can f.e. if SYNs crossed,
-                              or Fast Open.
-                            */
+       case TCP_SYN_RECV:
+               /* Only in fast or simultaneous open. If a fast open socket is
+                * is already accepted it is treated as a connected one below.
+                */
+               if (fastopen && fastopen->sk == NULL)
+                       break;
+
                if (!sock_owned_by_user(sk)) {
                        sk->sk_err = err;
 
@@ -822,7 +815,8 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
  */
 static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,
                              struct request_sock *req,
-                             u16 queue_mapping)
+                             u16 queue_mapping,
+                             struct tcp_fastopen_cookie *foc)
 {
        const struct inet_request_sock *ireq = inet_rsk(req);
        struct flowi4 fl4;
@@ -833,7 +827,7 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,
        if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL)
                return -1;
 
-       skb = tcp_make_synack(sk, dst, req, NULL);
+       skb = tcp_make_synack(sk, dst, req, foc);
 
        if (skb) {
                __tcp_v4_send_check(skb, ireq->ir_loc_addr, ireq->ir_rmt_addr);
@@ -852,7 +846,7 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,
 
 static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req)
 {
-       int res = tcp_v4_send_synack(sk, NULL, req, 0);
+       int res = tcp_v4_send_synack(sk, NULL, req, 0, NULL);
 
        if (!res) {
                TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
@@ -1260,187 +1254,6 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
 };
 #endif
 
-static bool tcp_fastopen_check(struct sock *sk, struct sk_buff *skb,
-                              struct request_sock *req,
-                              struct tcp_fastopen_cookie *foc,
-                              struct tcp_fastopen_cookie *valid_foc)
-{
-       bool skip_cookie = false;
-       struct fastopen_queue *fastopenq;
-
-       if (likely(!fastopen_cookie_present(foc))) {
-               /* See include/net/tcp.h for the meaning of these knobs */
-               if ((sysctl_tcp_fastopen & TFO_SERVER_ALWAYS) ||
-                   ((sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_REQD) &&
-                   (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1)))
-                       skip_cookie = true; /* no cookie to validate */
-               else
-                       return false;
-       }
-       fastopenq = inet_csk(sk)->icsk_accept_queue.fastopenq;
-       /* A FO option is present; bump the counter. */
-       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVE);
-
-       /* Make sure the listener has enabled fastopen, and we don't
-        * exceed the max # of pending TFO requests allowed before trying
-        * to validating the cookie in order to avoid burning CPU cycles
-        * unnecessarily.
-        *
-        * XXX (TFO) - The implication of checking the max_qlen before
-        * processing a cookie request is that clients can't differentiate
-        * between qlen overflow causing Fast Open to be disabled
-        * temporarily vs a server not supporting Fast Open at all.
-        */
-       if ((sysctl_tcp_fastopen & TFO_SERVER_ENABLE) == 0 ||
-           fastopenq == NULL || fastopenq->max_qlen == 0)
-               return false;
-
-       if (fastopenq->qlen >= fastopenq->max_qlen) {
-               struct request_sock *req1;
-               spin_lock(&fastopenq->lock);
-               req1 = fastopenq->rskq_rst_head;
-               if ((req1 == NULL) || time_after(req1->expires, jiffies)) {
-                       spin_unlock(&fastopenq->lock);
-                       NET_INC_STATS_BH(sock_net(sk),
-                           LINUX_MIB_TCPFASTOPENLISTENOVERFLOW);
-                       /* Avoid bumping LINUX_MIB_TCPFASTOPENPASSIVEFAIL*/
-                       foc->len = -1;
-                       return false;
-               }
-               fastopenq->rskq_rst_head = req1->dl_next;
-               fastopenq->qlen--;
-               spin_unlock(&fastopenq->lock);
-               reqsk_free(req1);
-       }
-       if (skip_cookie) {
-               tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
-               return true;
-       }
-
-       if (foc->len == TCP_FASTOPEN_COOKIE_SIZE) {
-               if ((sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_CHKED) == 0) {
-                       tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr,
-                                               ip_hdr(skb)->daddr, valid_foc);
-                       if ((valid_foc->len != TCP_FASTOPEN_COOKIE_SIZE) ||
-                           memcmp(&foc->val[0], &valid_foc->val[0],
-                           TCP_FASTOPEN_COOKIE_SIZE) != 0)
-                               return false;
-                       valid_foc->len = -1;
-               }
-               /* Acknowledge the data received from the peer. */
-               tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
-               return true;
-       } else if (foc->len == 0) { /* Client requesting a cookie */
-               tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr,
-                                       ip_hdr(skb)->daddr, valid_foc);
-               NET_INC_STATS_BH(sock_net(sk),
-                   LINUX_MIB_TCPFASTOPENCOOKIEREQD);
-       } else {
-               /* Client sent a cookie with wrong size. Treat it
-                * the same as invalid and return a valid one.
-                */
-               tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr,
-                                       ip_hdr(skb)->daddr, valid_foc);
-       }
-       return false;
-}
-
-static int tcp_v4_conn_req_fastopen(struct sock *sk,
-                                   struct sk_buff *skb,
-                                   struct sk_buff *skb_synack,
-                                   struct request_sock *req)
-{
-       struct tcp_sock *tp = tcp_sk(sk);
-       struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue;
-       const struct inet_request_sock *ireq = inet_rsk(req);
-       struct sock *child;
-       int err;
-
-       req->num_retrans = 0;
-       req->num_timeout = 0;
-       req->sk = NULL;
-
-       child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL);
-       if (child == NULL) {
-               NET_INC_STATS_BH(sock_net(sk),
-                                LINUX_MIB_TCPFASTOPENPASSIVEFAIL);
-               kfree_skb(skb_synack);
-               return -1;
-       }
-       err = ip_build_and_send_pkt(skb_synack, sk, ireq->ir_loc_addr,
-                                   ireq->ir_rmt_addr, ireq->opt);
-       err = net_xmit_eval(err);
-       if (!err)
-               tcp_rsk(req)->snt_synack = tcp_time_stamp;
-       /* XXX (TFO) - is it ok to ignore error and continue? */
-
-       spin_lock(&queue->fastopenq->lock);
-       queue->fastopenq->qlen++;
-       spin_unlock(&queue->fastopenq->lock);
-
-       /* Initialize the child socket. Have to fix some values to take
-        * into account the child is a Fast Open socket and is created
-        * only out of the bits carried in the SYN packet.
-        */
-       tp = tcp_sk(child);
-
-       tp->fastopen_rsk = req;
-       /* Do a hold on the listner sk so that if the listener is being
-        * closed, the child that has been accepted can live on and still
-        * access listen_lock.
-        */
-       sock_hold(sk);
-       tcp_rsk(req)->listener = sk;
-
-       /* RFC1323: The window in SYN & SYN/ACK segments is never
-        * scaled. So correct it appropriately.
-        */
-       tp->snd_wnd = ntohs(tcp_hdr(skb)->window);
-
-       /* Activate the retrans timer so that SYNACK can be retransmitted.
-        * The request socket is not added to the SYN table of the parent
-        * because it's been added to the accept queue directly.
-        */
-       inet_csk_reset_xmit_timer(child, ICSK_TIME_RETRANS,
-           TCP_TIMEOUT_INIT, TCP_RTO_MAX);
-
-       /* Add the child socket directly into the accept queue */
-       inet_csk_reqsk_queue_add(sk, req, child);
-
-       /* Now finish processing the fastopen child socket. */
-       inet_csk(child)->icsk_af_ops->rebuild_header(child);
-       tcp_init_congestion_control(child);
-       tcp_mtup_init(child);
-       tcp_init_metrics(child);
-       tcp_init_buffer_space(child);
-
-       /* Queue the data carried in the SYN packet. We need to first
-        * bump skb's refcnt because the caller will attempt to free it.
-        *
-        * XXX (TFO) - we honor a zero-payload TFO request for now.
-        * (Any reason not to?)
-        */
-       if (TCP_SKB_CB(skb)->end_seq == TCP_SKB_CB(skb)->seq + 1) {
-               /* Don't queue the skb if there is no payload in SYN.
-                * XXX (TFO) - How about SYN+FIN?
-                */
-               tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
-       } else {
-               skb = skb_get(skb);
-               skb_dst_drop(skb);
-               __skb_pull(skb, tcp_hdr(skb)->doff * 4);
-               skb_set_owner_r(skb, child);
-               __skb_queue_tail(&child->sk_receive_queue, skb);
-               tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
-               tp->syn_data_acked = 1;
-       }
-       sk->sk_data_ready(sk);
-       bh_unlock_sock(child);
-       sock_put(child);
-       WARN_ON(req->sk == NULL);
-       return 0;
-}
-
 int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
 {
        struct tcp_options_received tmp_opt;
@@ -1451,12 +1264,10 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
        __be32 saddr = ip_hdr(skb)->saddr;
        __be32 daddr = ip_hdr(skb)->daddr;
        __u32 isn = TCP_SKB_CB(skb)->when;
-       bool want_cookie = false;
+       bool want_cookie = false, fastopen;
        struct flowi4 fl4;
        struct tcp_fastopen_cookie foc = { .len = -1 };
-       struct tcp_fastopen_cookie valid_foc = { .len = -1 };
-       struct sk_buff *skb_synack;
-       int do_fastopen;
+       int err;
 
        /* Never answer to SYNs send to broadcast or multicast */
        if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
@@ -1507,6 +1318,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
        ireq->ir_rmt_addr = saddr;
        ireq->no_srccheck = inet_sk(sk)->transparent;
        ireq->opt = tcp_v4_save_options(skb);
+       ireq->ir_mark = inet_request_mark(sk, skb);
 
        if (security_inet_conn_request(sk, skb, req))
                goto drop_and_free;
@@ -1555,52 +1367,24 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
 
                isn = tcp_v4_init_sequence(skb);
        }
-       tcp_rsk(req)->snt_isn = isn;
-
-       if (dst == NULL) {
-               dst = inet_csk_route_req(sk, &fl4, req);
-               if (dst == NULL)
-                       goto drop_and_free;
-       }
-       do_fastopen = tcp_fastopen_check(sk, skb, req, &foc, &valid_foc);
-
-       /* We don't call tcp_v4_send_synack() directly because we need
-        * to make sure a child socket can be created successfully before
-        * sending back synack!
-        *
-        * XXX (TFO) - Ideally one would simply call tcp_v4_send_synack()
-        * (or better yet, call tcp_send_synack() in the child context
-        * directly, but will have to fix bunch of other code first)
-        * after syn_recv_sock() except one will need to first fix the
-        * latter to remove its dependency on the current implementation
-        * of tcp_v4_send_synack()->tcp_select_initial_window().
-        */
-       skb_synack = tcp_make_synack(sk, dst, req,
-           fastopen_cookie_present(&valid_foc) ? &valid_foc : NULL);
-
-       if (skb_synack) {
-               __tcp_v4_send_check(skb_synack, ireq->ir_loc_addr, ireq->ir_rmt_addr);
-               skb_set_queue_mapping(skb_synack, skb_get_queue_mapping(skb));
-       } else
+       if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL)
                goto drop_and_free;
 
-       if (likely(!do_fastopen)) {
-               int err;
-               err = ip_build_and_send_pkt(skb_synack, sk, ireq->ir_loc_addr,
-                    ireq->ir_rmt_addr, ireq->opt);
-               err = net_xmit_eval(err);
+       tcp_rsk(req)->snt_isn = isn;
+       tcp_rsk(req)->snt_synack = tcp_time_stamp;
+       tcp_openreq_init_rwin(req, sk, dst);
+       fastopen = !want_cookie &&
+                  tcp_try_fastopen(sk, skb, req, &foc, dst);
+       err = tcp_v4_send_synack(sk, dst, req,
+                                skb_get_queue_mapping(skb), &foc);
+       if (!fastopen) {
                if (err || want_cookie)
                        goto drop_and_free;
 
                tcp_rsk(req)->snt_synack = tcp_time_stamp;
                tcp_rsk(req)->listener = NULL;
-               /* Add the request_sock to the SYN table */
                inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
-               if (fastopen_cookie_present(&foc) && foc.len != 0)
-                       NET_INC_STATS_BH(sock_net(sk),
-                           LINUX_MIB_TCPFASTOPENPASSIVEFAIL);
-       } else if (tcp_v4_conn_req_fastopen(sk, skb, skb_synack, req))
-               goto drop_and_free;
+       }
 
        return 0;
 
@@ -1744,28 +1528,6 @@ static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
        return sk;
 }
 
-static __sum16 tcp_v4_checksum_init(struct sk_buff *skb)
-{
-       const struct iphdr *iph = ip_hdr(skb);
-
-       if (skb->ip_summed == CHECKSUM_COMPLETE) {
-               if (!tcp_v4_check(skb->len, iph->saddr,
-                                 iph->daddr, skb->csum)) {
-                       skb->ip_summed = CHECKSUM_UNNECESSARY;
-                       return 0;
-               }
-       }
-
-       skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
-                                      skb->len, IPPROTO_TCP, 0);
-
-       if (skb->len <= 76) {
-               return __skb_checksum_complete(skb);
-       }
-       return 0;
-}
-
-
 /* The socket must have it's spinlock held when we get
  * here.
  *
@@ -1960,7 +1722,8 @@ int tcp_v4_rcv(struct sk_buff *skb)
         * Packet length and doff are validated by header prediction,
         * provided case of th->doff==0 is eliminated.
         * So, we defer the checks. */
-       if (!skb_csum_unnecessary(skb) && tcp_v4_checksum_init(skb))
+
+       if (skb_checksum_init(skb, IPPROTO_TCP, inet_compute_pseudo))
                goto csum_error;
 
        th = tcp_hdr(skb);
index c9aecae313276d134ef56d1385ac44266c200a51..1e70fa8fa793fdbca3ca782231f8b775ed96f205 100644 (file)
@@ -115,13 +115,12 @@ static void tcp_lp_init(struct sock *sk)
  * Will only call newReno CA when away from inference.
  * From TCP-LP's paper, this will be handled in additive increasement.
  */
-static void tcp_lp_cong_avoid(struct sock *sk, u32 ack, u32 acked,
-                             u32 in_flight)
+static void tcp_lp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct lp *lp = inet_csk_ca(sk);
 
        if (!(lp->flag & LP_WITHIN_INF))
-               tcp_reno_cong_avoid(sk, ack, acked, in_flight);
+               tcp_reno_cong_avoid(sk, ack, acked);
 }
 
 /**
index dcaf72f10216c22f00ef918963d260d4ce472b99..4fe04180598969e653a01db2c459edd2c8d8acce 100644 (file)
@@ -1159,10 +1159,7 @@ static void __net_exit tcp_net_metrics_exit(struct net *net)
                        tm = next;
                }
        }
-       if (is_vmalloc_addr(net->ipv4.tcp_metrics_hash))
-               vfree(net->ipv4.tcp_metrics_hash);
-       else
-               kfree(net->ipv4.tcp_metrics_hash);
+       kvfree(net->ipv4.tcp_metrics_hash);
 }
 
 static __net_initdata struct pernet_operations tcp_net_metrics_ops = {
index 05c1b155251d39d2e559d050f1e191f51f32b3e0..e68e0d4af6c97bcd0f8c983890ba555adbfb3a00 100644 (file)
@@ -362,6 +362,37 @@ void tcp_twsk_destructor(struct sock *sk)
 }
 EXPORT_SYMBOL_GPL(tcp_twsk_destructor);
 
+void tcp_openreq_init_rwin(struct request_sock *req,
+                          struct sock *sk, struct dst_entry *dst)
+{
+       struct inet_request_sock *ireq = inet_rsk(req);
+       struct tcp_sock *tp = tcp_sk(sk);
+       __u8 rcv_wscale;
+       int mss = dst_metric_advmss(dst);
+
+       if (tp->rx_opt.user_mss && tp->rx_opt.user_mss < mss)
+               mss = tp->rx_opt.user_mss;
+
+       /* Set this up on the first call only */
+       req->window_clamp = tp->window_clamp ? : dst_metric(dst, RTAX_WINDOW);
+
+       /* limit the window selection if the user enforce a smaller rx buffer */
+       if (sk->sk_userlocks & SOCK_RCVBUF_LOCK &&
+           (req->window_clamp > tcp_full_space(sk) || req->window_clamp == 0))
+               req->window_clamp = tcp_full_space(sk);
+
+       /* tcp_full_space because it is guaranteed to be the first packet */
+       tcp_select_initial_window(tcp_full_space(sk),
+               mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0),
+               &req->rcv_wnd,
+               &req->window_clamp,
+               ireq->wscale_ok,
+               &rcv_wscale,
+               dst_metric(dst, RTAX_INITRWND));
+       ireq->rcv_wscale = rcv_wscale;
+}
+EXPORT_SYMBOL(tcp_openreq_init_rwin);
+
 static inline void TCP_ECN_openreq_child(struct tcp_sock *tp,
                                         struct request_sock *req)
 {
index b92b81718ca42bc9e47b415c321eb143d7266852..4e86c59ec7f7f07fe06c6db20d17851da6f1563f 100644 (file)
@@ -57,10 +57,12 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
                               SKB_GSO_TCP_ECN |
                               SKB_GSO_TCPV6 |
                               SKB_GSO_GRE |
+                              SKB_GSO_GRE_CSUM |
                               SKB_GSO_IPIP |
                               SKB_GSO_SIT |
                               SKB_GSO_MPLS |
                               SKB_GSO_UDP_TUNNEL |
+                              SKB_GSO_UDP_TUNNEL_CSUM |
                               0) ||
                             !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))))
                        goto out;
@@ -97,9 +99,7 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
                th->check = newcheck;
 
                if (skb->ip_summed != CHECKSUM_PARTIAL)
-                       th->check =
-                            csum_fold(csum_partial(skb_transport_header(skb),
-                                                   thlen, skb->csum));
+                       th->check = gso_make_checksum(skb, ~th->check);
 
                seq += mss;
                if (copy_destructor) {
@@ -133,8 +133,7 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
        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));
+               th->check = gso_make_checksum(skb, ~th->check);
 out:
        return segs;
 }
index 2d340bd2cd3d50d97da3e86b77de8117eabe0c10..d92bce0ea24ec54fb559941979906337d68881ad 100644 (file)
@@ -627,7 +627,7 @@ static unsigned int tcp_synack_options(struct sock *sk,
                if (unlikely(!ireq->tstamp_ok))
                        remaining -= TCPOLEN_SACKPERM_ALIGNED;
        }
-       if (foc != NULL) {
+       if (foc != NULL && foc->len >= 0) {
                u32 need = TCPOLEN_EXP_FASTOPEN_BASE + foc->len;
                need = (need + 3) & ~3U;  /* Align to 32 bits */
                if (remaining >= need) {
@@ -878,15 +878,8 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
        BUG_ON(!skb || !tcp_skb_pcount(skb));
 
        if (clone_it) {
-               const struct sk_buff *fclone = skb + 1;
-
                skb_mstamp_get(&skb->skb_mstamp);
 
-               if (unlikely(skb->fclone == SKB_FCLONE_ORIG &&
-                            fclone->fclone == SKB_FCLONE_CLONE))
-                       NET_INC_STATS(sock_net(sk),
-                                     LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES);
-
                if (unlikely(skb_cloned(skb)))
                        skb = pskb_copy(skb, gfp_mask);
                else
@@ -1081,7 +1074,7 @@ static void tcp_adjust_pcount(struct sock *sk, const struct sk_buff *skb, int de
  * Remember, these are still headerless SKBs at this point.
  */
 int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len,
-                unsigned int mss_now)
+                unsigned int mss_now, gfp_t gfp)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct sk_buff *buff;
@@ -1096,11 +1089,11 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len,
        if (nsize < 0)
                nsize = 0;
 
-       if (skb_unclone(skb, GFP_ATOMIC))
+       if (skb_unclone(skb, gfp))
                return -ENOMEM;
 
        /* Get a new skb... force flag on. */
-       buff = sk_stream_alloc_skb(sk, nsize, GFP_ATOMIC);
+       buff = sk_stream_alloc_skb(sk, nsize, gfp);
        if (buff == NULL)
                return -ENOMEM; /* We'll just try again later. */
 
@@ -1387,12 +1380,43 @@ unsigned int tcp_current_mss(struct sock *sk)
        return mss_now;
 }
 
-/* Congestion window validation. (RFC2861) */
-static void tcp_cwnd_validate(struct sock *sk)
+/* RFC2861, slow part. Adjust cwnd, after it was not full during one rto.
+ * As additional protections, we do not touch cwnd in retransmission phases,
+ * and if application hit its sndbuf limit recently.
+ */
+static void tcp_cwnd_application_limited(struct sock *sk)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+
+       if (inet_csk(sk)->icsk_ca_state == TCP_CA_Open &&
+           sk->sk_socket && !test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) {
+               /* Limited by application or receiver window. */
+               u32 init_win = tcp_init_cwnd(tp, __sk_dst_get(sk));
+               u32 win_used = max(tp->snd_cwnd_used, init_win);
+               if (win_used < tp->snd_cwnd) {
+                       tp->snd_ssthresh = tcp_current_ssthresh(sk);
+                       tp->snd_cwnd = (tp->snd_cwnd + win_used) >> 1;
+               }
+               tp->snd_cwnd_used = 0;
+       }
+       tp->snd_cwnd_stamp = tcp_time_stamp;
+}
+
+static void tcp_cwnd_validate(struct sock *sk, bool is_cwnd_limited)
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
-       if (tp->packets_out >= tp->snd_cwnd) {
+       /* Track the maximum number of outstanding packets in each
+        * window, and remember whether we were cwnd-limited then.
+        */
+       if (!before(tp->snd_una, tp->max_packets_seq) ||
+           tp->packets_out > tp->max_packets_out) {
+               tp->max_packets_out = tp->packets_out;
+               tp->max_packets_seq = tp->snd_nxt;
+               tp->is_cwnd_limited = is_cwnd_limited;
+       }
+
+       if (tcp_is_cwnd_limited(sk)) {
                /* Network is feed fully. */
                tp->snd_cwnd_used = 0;
                tp->snd_cwnd_stamp = tcp_time_stamp;
@@ -1601,7 +1625,7 @@ static int tso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len,
 
        /* All of a TSO frame must be composed of paged data.  */
        if (skb->len != skb->data_len)
-               return tcp_fragment(sk, skb, len, mss_now);
+               return tcp_fragment(sk, skb, len, mss_now, gfp);
 
        buff = sk_stream_alloc_skb(sk, 0, gfp);
        if (unlikely(buff == NULL))
@@ -1644,7 +1668,8 @@ static int tso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len,
  *
  * This algorithm is from John Heffner.
  */
-static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb)
+static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb,
+                                bool *is_cwnd_limited)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        const struct inet_connection_sock *icsk = inet_csk(sk);
@@ -1708,6 +1733,9 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb)
        if (!tp->tso_deferred)
                tp->tso_deferred = 1 | (jiffies << 1);
 
+       if (cong_win < send_win && cong_win < skb->len)
+               *is_cwnd_limited = true;
+
        return true;
 
 send_now:
@@ -1868,6 +1896,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
        unsigned int tso_segs, sent_pkts;
        int cwnd_quota;
        int result;
+       bool is_cwnd_limited = false;
 
        sent_pkts = 0;
 
@@ -1892,6 +1921,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
 
                cwnd_quota = tcp_cwnd_test(tp, skb);
                if (!cwnd_quota) {
+                       is_cwnd_limited = true;
                        if (push_one == 2)
                                /* Force out a loss probe pkt. */
                                cwnd_quota = 1;
@@ -1908,7 +1938,8 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
                                                      nonagle : TCP_NAGLE_PUSH))))
                                break;
                } else {
-                       if (!push_one && tcp_tso_should_defer(sk, skb))
+                       if (!push_one &&
+                           tcp_tso_should_defer(sk, skb, &is_cwnd_limited))
                                break;
                }
 
@@ -1973,7 +2004,7 @@ repair:
                /* Send one loss probe per tail loss episode. */
                if (push_one != 2)
                        tcp_schedule_loss_probe(sk);
-               tcp_cwnd_validate(sk);
+               tcp_cwnd_validate(sk, is_cwnd_limited);
                return false;
        }
        return (push_one == 2) || (!tp->packets_out && tcp_send_head(sk));
@@ -2037,6 +2068,25 @@ bool tcp_schedule_loss_probe(struct sock *sk)
        return true;
 }
 
+/* Thanks to skb fast clones, we can detect if a prior transmit of
+ * a packet is still in a qdisc or driver queue.
+ * In this case, there is very little point doing a retransmit !
+ * Note: This is called from BH context only.
+ */
+static bool skb_still_in_host_queue(const struct sock *sk,
+                                   const struct sk_buff *skb)
+{
+       const struct sk_buff *fclone = skb + 1;
+
+       if (unlikely(skb->fclone == SKB_FCLONE_ORIG &&
+                    fclone->fclone == SKB_FCLONE_CLONE)) {
+               NET_INC_STATS_BH(sock_net(sk),
+                                LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES);
+               return true;
+       }
+       return false;
+}
+
 /* When probe timeout (PTO) fires, send a new segment if one exists, else
  * retransmit the last segment.
  */
@@ -2062,12 +2112,16 @@ void tcp_send_loss_probe(struct sock *sk)
        if (WARN_ON(!skb))
                goto rearm_timer;
 
+       if (skb_still_in_host_queue(sk, skb))
+               goto rearm_timer;
+
        pcount = tcp_skb_pcount(skb);
        if (WARN_ON(!pcount))
                goto rearm_timer;
 
        if ((pcount > 1) && (skb->len > (pcount - 1) * mss)) {
-               if (unlikely(tcp_fragment(sk, skb, (pcount - 1) * mss, mss)))
+               if (unlikely(tcp_fragment(sk, skb, (pcount - 1) * mss, mss,
+                                         GFP_ATOMIC)))
                        goto rearm_timer;
                skb = tcp_write_queue_tail(sk);
        }
@@ -2075,9 +2129,7 @@ void tcp_send_loss_probe(struct sock *sk)
        if (WARN_ON(!skb || !tcp_skb_pcount(skb)))
                goto rearm_timer;
 
-       /* Probe with zero data doesn't trigger fast recovery. */
-       if (skb->len > 0)
-               err = __tcp_retransmit_skb(sk, skb);
+       err = __tcp_retransmit_skb(sk, skb);
 
        /* Record snd_nxt for loss detection. */
        if (likely(!err))
@@ -2383,6 +2435,9 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
            min(sk->sk_wmem_queued + (sk->sk_wmem_queued >> 2), sk->sk_sndbuf))
                return -EAGAIN;
 
+       if (skb_still_in_host_queue(sk, skb))
+               return -EBUSY;
+
        if (before(TCP_SKB_CB(skb)->seq, tp->snd_una)) {
                if (before(TCP_SKB_CB(skb)->end_seq, tp->snd_una))
                        BUG();
@@ -2405,7 +2460,7 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
                return -EAGAIN;
 
        if (skb->len > cur_mss) {
-               if (tcp_fragment(sk, skb, cur_mss, cur_mss))
+               if (tcp_fragment(sk, skb, cur_mss, cur_mss, GFP_ATOMIC))
                        return -ENOMEM; /* We'll try again later. */
        } else {
                int oldpcount = tcp_skb_pcount(skb);
@@ -2476,7 +2531,7 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
                 * see tcp_input.c tcp_sacktag_write_queue().
                 */
                TCP_SKB_CB(skb)->ack_seq = tp->snd_nxt;
-       } else {
+       } else if (err != -EBUSY) {
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPRETRANSFAIL);
        }
        return err;
@@ -2754,27 +2809,6 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
        if (tp->rx_opt.user_mss && tp->rx_opt.user_mss < mss)
                mss = tp->rx_opt.user_mss;
 
-       if (req->rcv_wnd == 0) { /* ignored for retransmitted syns */
-               __u8 rcv_wscale;
-               /* Set this up on the first call only */
-               req->window_clamp = tp->window_clamp ? : dst_metric(dst, RTAX_WINDOW);
-
-               /* limit the window selection if the user enforce a smaller rx buffer */
-               if (sk->sk_userlocks & SOCK_RCVBUF_LOCK &&
-                   (req->window_clamp > tcp_full_space(sk) || req->window_clamp == 0))
-                       req->window_clamp = tcp_full_space(sk);
-
-               /* tcp_full_space because it is guaranteed to be the first packet */
-               tcp_select_initial_window(tcp_full_space(sk),
-                       mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0),
-                       &req->rcv_wnd,
-                       &req->window_clamp,
-                       ireq->wscale_ok,
-                       &rcv_wscale,
-                       dst_metric(dst, RTAX_INITRWND));
-               ireq->rcv_wscale = rcv_wscale;
-       }
-
        memset(&opts, 0, sizeof(opts));
 #ifdef CONFIG_SYN_COOKIES
        if (unlikely(req->cookie_ts))
@@ -3207,7 +3241,7 @@ int tcp_write_wakeup(struct sock *sk)
                    skb->len > mss) {
                        seg_size = min(seg_size, mss);
                        TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_PSH;
-                       if (tcp_fragment(sk, skb, seg_size, mss))
+                       if (tcp_fragment(sk, skb, seg_size, mss, GFP_ATOMIC))
                                return -1;
                } else if (!tcp_skb_pcount(skb))
                        tcp_set_skb_tso_segs(sk, skb, mss);
index 0ac50836da4d42832f3aa35c9a4cebbf79f69981..8250949b88538db4e0f2d318050518855e74cf3d 100644 (file)
 #define TCP_SCALABLE_AI_CNT    50U
 #define TCP_SCALABLE_MD_SCALE  3
 
-static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 acked,
-                                   u32 in_flight)
+static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
-       if (!tcp_is_cwnd_limited(sk, in_flight))
+       if (!tcp_is_cwnd_limited(sk))
                return;
 
        if (tp->snd_cwnd <= tp->snd_ssthresh)
index 48539fff6357a4e778c537b99bb9a7fd49eb43b3..9a5e05f27f4f7cce16cb285698cb0fc73ef49a91 100644 (file)
@@ -163,14 +163,13 @@ static inline u32 tcp_vegas_ssthresh(struct tcp_sock *tp)
        return  min(tp->snd_ssthresh, tp->snd_cwnd-1);
 }
 
-static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked,
-                                u32 in_flight)
+static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct vegas *vegas = inet_csk_ca(sk);
 
        if (!vegas->doing_vegas_now) {
-               tcp_reno_cong_avoid(sk, ack, acked, in_flight);
+               tcp_reno_cong_avoid(sk, ack, acked);
                return;
        }
 
@@ -195,7 +194,7 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked,
                        /* We don't have enough RTT samples to do the Vegas
                         * calculation, so we'll behave like Reno.
                         */
-                       tcp_reno_cong_avoid(sk, ack, acked, in_flight);
+                       tcp_reno_cong_avoid(sk, ack, acked);
                } else {
                        u32 rtt, diff;
                        u64 target_cwnd;
index 1b8e28fcd7e1cab3edd586db1b716742a4402fd7..27b9825753d15d89717d7b76e1c38fae4758cfbc 100644 (file)
@@ -114,19 +114,18 @@ static void tcp_veno_cwnd_event(struct sock *sk, enum tcp_ca_event event)
                tcp_veno_init(sk);
 }
 
-static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 acked,
-                               u32 in_flight)
+static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct veno *veno = inet_csk_ca(sk);
 
        if (!veno->doing_veno_now) {
-               tcp_reno_cong_avoid(sk, ack, acked, in_flight);
+               tcp_reno_cong_avoid(sk, ack, acked);
                return;
        }
 
        /* limited by applications */
-       if (!tcp_is_cwnd_limited(sk, in_flight))
+       if (!tcp_is_cwnd_limited(sk))
                return;
 
        /* We do the Veno calculations only if we got enough rtt samples */
@@ -134,7 +133,7 @@ static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 acked,
                /* We don't have enough rtt samples to do the Veno
                 * calculation, so we'll behave like Reno.
                 */
-               tcp_reno_cong_avoid(sk, ack, acked, in_flight);
+               tcp_reno_cong_avoid(sk, ack, acked);
        } else {
                u64 target_cwnd;
                u32 rtt;
index 5ede0e727945add71904a2d3c57d334e77e94baf..599b79b8eac07298a34cff43ae87e546bdb36847 100644 (file)
@@ -69,13 +69,12 @@ static void tcp_yeah_pkts_acked(struct sock *sk, u32 pkts_acked, s32 rtt_us)
        tcp_vegas_pkts_acked(sk, pkts_acked, rtt_us);
 }
 
-static void tcp_yeah_cong_avoid(struct sock *sk, u32 ack, u32 acked,
-                               u32 in_flight)
+static void tcp_yeah_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct yeah *yeah = inet_csk_ca(sk);
 
-       if (!tcp_is_cwnd_limited(sk, in_flight))
+       if (!tcp_is_cwnd_limited(sk))
                return;
 
        if (tp->snd_cwnd <= tp->snd_ssthresh)
index 4468e1adc094a1f6f12eb20cf7c79a9b9187826d..d92f94b7e4025dd4779e75e6a75f2de560713778 100644 (file)
@@ -246,7 +246,7 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum,
                        do {
                                if (low <= snum && snum <= high &&
                                    !test_bit(snum >> udptable->log, bitmap) &&
-                                   !inet_is_reserved_local_port(snum))
+                                   !inet_is_local_reserved_port(net, snum))
                                        goto found;
                                snum += rand;
                        } while (snum != first);
@@ -727,13 +727,12 @@ EXPORT_SYMBOL(udp_flush_pending_frames);
 void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst)
 {
        struct udphdr *uh = udp_hdr(skb);
-       struct sk_buff *frags = skb_shinfo(skb)->frag_list;
        int offset = skb_transport_offset(skb);
        int len = skb->len - offset;
        int hlen = len;
        __wsum csum = 0;
 
-       if (!frags) {
+       if (!skb_has_frag_list(skb)) {
                /*
                 * Only one fragment on the socket.
                 */
@@ -742,15 +741,17 @@ void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst)
                uh->check = ~csum_tcpudp_magic(src, dst, len,
                                               IPPROTO_UDP, 0);
        } else {
+               struct sk_buff *frags;
+
                /*
                 * HW-checksum won't work as there are two or more
                 * fragments on the socket so that all csums of sk_buffs
                 * should be together
                 */
-               do {
+               skb_walk_frags(skb, frags) {
                        csum = csum_add(csum, frags->csum);
                        hlen -= frags->len;
-               } while ((frags = frags->next));
+               }
 
                csum = skb_checksum(skb, offset, hlen, csum);
                skb->ip_summed = CHECKSUM_NONE;
@@ -762,6 +763,43 @@ void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst)
 }
 EXPORT_SYMBOL_GPL(udp4_hwcsum);
 
+/* Function to set UDP checksum for an IPv4 UDP packet. This is intended
+ * for the simple case like when setting the checksum for a UDP tunnel.
+ */
+void udp_set_csum(bool nocheck, struct sk_buff *skb,
+                 __be32 saddr, __be32 daddr, int len)
+{
+       struct udphdr *uh = udp_hdr(skb);
+
+       if (nocheck)
+               uh->check = 0;
+       else if (skb_is_gso(skb))
+               uh->check = ~udp_v4_check(len, saddr, daddr, 0);
+       else if (skb_dst(skb) && skb_dst(skb)->dev &&
+                (skb_dst(skb)->dev->features & NETIF_F_V4_CSUM)) {
+
+               BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL);
+
+               skb->ip_summed = CHECKSUM_PARTIAL;
+               skb->csum_start = skb_transport_header(skb) - skb->head;
+               skb->csum_offset = offsetof(struct udphdr, check);
+               uh->check = ~udp_v4_check(len, saddr, daddr, 0);
+       } else {
+               __wsum csum;
+
+               BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL);
+
+               uh->check = 0;
+               csum = skb_checksum(skb, 0, len, 0);
+               uh->check = udp_v4_check(len, saddr, daddr, csum);
+               if (uh->check == 0)
+                       uh->check = CSUM_MANGLED_0;
+
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+       }
+}
+EXPORT_SYMBOL(udp_set_csum);
+
 static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4)
 {
        struct sock *sk = skb->sk;
@@ -785,7 +823,7 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4)
        if (is_udplite)                                  /*     UDP-Lite      */
                csum = udplite_csum(skb);
 
-       else if (sk->sk_no_check == UDP_CSUM_NOXMIT) {   /* UDP csum disabled */
+       else if (sk->sk_no_check_tx) {   /* UDP csum disabled */
 
                skb->ip_summed = CHECKSUM_NONE;
                goto send;
@@ -1495,6 +1533,10 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
                if (skb->len > sizeof(struct udphdr) && encap_rcv != NULL) {
                        int ret;
 
+                       /* Verify checksum before giving to encap */
+                       if (udp_lib_checksum_complete(skb))
+                               goto csum_error;
+
                        ret = encap_rcv(sk, skb);
                        if (ret <= 0) {
                                UDP_INC_STATS_BH(sock_net(sk),
@@ -1672,7 +1714,6 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
 static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh,
                                 int proto)
 {
-       const struct iphdr *iph;
        int err;
 
        UDP_SKB_CB(skb)->partial_cov = 0;
@@ -1684,22 +1725,8 @@ static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh,
                        return err;
        }
 
-       iph = ip_hdr(skb);
-       if (uh->check == 0) {
-               skb->ip_summed = CHECKSUM_UNNECESSARY;
-       } else if (skb->ip_summed == CHECKSUM_COMPLETE) {
-               if (!csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len,
-                                     proto, skb->csum))
-                       skb->ip_summed = CHECKSUM_UNNECESSARY;
-       }
-       if (!skb_csum_unnecessary(skb))
-               skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
-                                              skb->len, proto, 0);
-       /* Probably, we should checksum udp header (it should be in cache
-        * in any case) and data in tiny packets (< rx copybreak).
-        */
-
-       return 0;
+       return skb_checksum_init_zero_check(skb, proto, uh->check,
+                                           inet_compute_pseudo);
 }
 
 /*
@@ -1834,6 +1861,10 @@ static struct sock *__udp4_lib_mcast_demux_lookup(struct net *net,
        unsigned int count, slot = udp_hashfn(net, hnum, udp_table.mask);
        struct udp_hslot *hslot = &udp_table.hash[slot];
 
+       /* Do not bother scanning a too big list */
+       if (hslot->count > 10)
+               return NULL;
+
        rcu_read_lock();
 begin:
        count = 0;
@@ -1886,7 +1917,7 @@ static struct sock *__udp4_lib_demux_lookup(struct net *net,
        unsigned int hash2 = udp4_portaddr_hash(net, loc_addr, hnum);
        unsigned int slot2 = hash2 & udp_table.mask;
        struct udp_hslot *hslot2 = &udp_table.hash2[slot2];
-       INET_ADDR_COOKIE(acookie, rmt_addr, loc_addr)
+       INET_ADDR_COOKIE(acookie, rmt_addr, loc_addr);
        const __portpair ports = INET_COMBINED_PORTS(rmt_port, hnum);
 
        rcu_read_lock();
@@ -1979,7 +2010,7 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
                       int (*push_pending_frames)(struct sock *))
 {
        struct udp_sock *up = udp_sk(sk);
-       int val;
+       int val, valbool;
        int err = 0;
        int is_udplite = IS_UDPLITE(sk);
 
@@ -1989,6 +2020,8 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
        if (get_user(val, (int __user *)optval))
                return -EFAULT;
 
+       valbool = val ? 1 : 0;
+
        switch (optname) {
        case UDP_CORK:
                if (val != 0) {
@@ -2018,6 +2051,14 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
                }
                break;
 
+       case UDP_NO_CHECK6_TX:
+               up->no_check6_tx = valbool;
+               break;
+
+       case UDP_NO_CHECK6_RX:
+               up->no_check6_rx = valbool;
+               break;
+
        /*
         *      UDP-Lite's partial checksum coverage (RFC 3828).
         */
@@ -2100,6 +2141,14 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname,
                val = up->encap_type;
                break;
 
+       case UDP_NO_CHECK6_TX:
+               val = up->no_check6_tx;
+               break;
+
+       case UDP_NO_CHECK6_RX:
+               val = up->no_check6_rx;
+               break;
+
        /* The following two cannot be changed on UDP sockets, the return is
         * always 0 (which corresponds to the full checksum coverage of UDP). */
        case UDPLITE_SEND_CSCOV:
@@ -2484,7 +2533,11 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
        int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb);
        __be16 protocol = skb->protocol;
        netdev_features_t enc_features;
-       int outer_hlen;
+       int udp_offset, outer_hlen;
+       unsigned int oldlen;
+       bool need_csum;
+
+       oldlen = (u16)~skb->len;
 
        if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
                goto out;
@@ -2496,6 +2549,10 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
        skb->mac_len = skb_inner_network_offset(skb);
        skb->protocol = htons(ETH_P_TEB);
 
+       need_csum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM);
+       if (need_csum)
+               skb->encap_hdr_csum = 1;
+
        /* segment inner packet. */
        enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
        segs = skb_mac_gso_segment(skb, enc_features);
@@ -2506,10 +2563,11 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
        }
 
        outer_hlen = skb_tnl_header_len(skb);
+       udp_offset = outer_hlen - tnl_hlen;
        skb = segs;
        do {
                struct udphdr *uh;
-               int udp_offset = outer_hlen - tnl_hlen;
+               int len;
 
                skb_reset_inner_headers(skb);
                skb->encapsulation = 1;
@@ -2520,31 +2578,20 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
                skb_reset_mac_header(skb);
                skb_set_network_header(skb, mac_len);
                skb_set_transport_header(skb, udp_offset);
+               len = skb->len - udp_offset;
                uh = udp_hdr(skb);
-               uh->len = htons(skb->len - udp_offset);
-
-               /* csum segment if tunnel sets skb with csum. */
-               if (protocol == htons(ETH_P_IP) && unlikely(uh->check)) {
-                       struct iphdr *iph = ip_hdr(skb);
+               uh->len = htons(len);
 
-                       uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
-                                                      skb->len - udp_offset,
-                                                      IPPROTO_UDP, 0);
-                       uh->check = csum_fold(skb_checksum(skb, udp_offset,
-                                                          skb->len - udp_offset, 0));
-                       if (uh->check == 0)
-                               uh->check = CSUM_MANGLED_0;
+               if (need_csum) {
+                       __be32 delta = htonl(oldlen + len);
 
-               } else if (protocol == htons(ETH_P_IPV6)) {
-                       struct ipv6hdr *ipv6h = ipv6_hdr(skb);
-                       u32 len = skb->len - udp_offset;
+                       uh->check = ~csum_fold((__force __wsum)
+                                              ((__force u32)uh->check +
+                                               (__force u32)delta));
+                       uh->check = gso_make_checksum(skb, ~uh->check);
 
-                       uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
-                                                    len, IPPROTO_UDP, 0);
-                       uh->check = csum_fold(skb_checksum(skb, udp_offset, len, 0));
                        if (uh->check == 0)
                                uh->check = CSUM_MANGLED_0;
-                       skb->ip_summed = CHECKSUM_NONE;
                }
 
                skb->protocol = protocol;
index 88b4023ecfcfc85df907ff7472354084b3b16264..546d2d439dda65a195f7e635b7dc564def1da077 100644 (file)
@@ -56,7 +56,8 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
        __wsum csum;
 
        if (skb->encapsulation &&
-           skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL) {
+           (skb_shinfo(skb)->gso_type &
+            (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM))) {
                segs = skb_udp_tunnel_segment(skb, features);
                goto out;
        }
@@ -71,8 +72,10 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
 
                if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY |
                                      SKB_GSO_UDP_TUNNEL |
+                                     SKB_GSO_UDP_TUNNEL_CSUM |
                                      SKB_GSO_IPIP |
-                                     SKB_GSO_GRE | SKB_GSO_MPLS) ||
+                                     SKB_GSO_GRE | SKB_GSO_GRE_CSUM |
+                                     SKB_GSO_MPLS) ||
                             !(type & (SKB_GSO_UDP))))
                        goto out;
 
@@ -197,6 +200,7 @@ unflush:
        }
 
        skb_gro_pull(skb, sizeof(struct udphdr)); /* pull encapsulating udp header */
+       skb_gro_postpull_rcsum(skb, uh, sizeof(struct udphdr));
        pp = uo_priv->offload->callbacks.gro_receive(head, skb);
 
 out_unlock:
index 2c46acd4cc3636e33fb240a963367ec629b482b5..3b3efbda48e13941b35388238508706844c043aa 100644 (file)
@@ -70,7 +70,6 @@ static struct inet_protosw udplite4_protosw = {
        .protocol       =  IPPROTO_UDPLITE,
        .prot           =  &udplite_prot,
        .ops            =  &inet_dgram_ops,
-       .no_check       =  0,           /* must checksum (RFC 3828) */
        .flags          =  INET_PROTOSW_PERMANENT,
 };
 
index 05f2b484954feda957d04ff2f0300eedf9c97263..91771a7c802f828c33429413674c5f78ca393b6f 100644 (file)
@@ -58,12 +58,12 @@ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
 
        top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ?
                0 : (XFRM_MODE_SKB_CB(skb)->frag_off & htons(IP_DF));
-       ip_select_ident(skb, dst->child, NULL);
 
        top_iph->ttl = ip4_dst_hoplimit(dst->child);
 
        top_iph->saddr = x->props.saddr.a4;
        top_iph->daddr = x->id.daddr.a4;
+       ip_select_ident(skb, NULL);
 
        return 0;
 }
index 186a8ecf92fa84bda9d0f6b050131da603c7694a..d5f6bd9a210ab93ed27177ecffc9499259dbe999 100644 (file)
@@ -25,7 +25,7 @@ static int xfrm4_tunnel_check_size(struct sk_buff *skb)
        if (IPCB(skb)->flags & IPSKB_XFRM_TUNNEL_SIZE)
                goto out;
 
-       if (!(ip_hdr(skb)->frag_off & htons(IP_DF)) || skb->local_df)
+       if (!(ip_hdr(skb)->frag_off & htons(IP_DF)) || skb->ignore_df)
                goto out;
 
        mtu = dst_mtu(skb_dst(skb));
index 6c7fa0853fc74ef179b00de52d78aecee342e18b..5667b3003af9b51779ff322717e999282113c4b7 100644 (file)
@@ -275,19 +275,14 @@ static int snmp6_alloc_dev(struct inet6_dev *idev)
 {
        int i;
 
-       if (snmp_mib_init((void __percpu **)idev->stats.ipv6,
-                         sizeof(struct ipstats_mib),
-                         __alignof__(struct ipstats_mib)) < 0)
+       idev->stats.ipv6 = alloc_percpu(struct ipstats_mib);
+       if (!idev->stats.ipv6)
                goto err_ip;
 
        for_each_possible_cpu(i) {
                struct ipstats_mib *addrconf_stats;
-               addrconf_stats = per_cpu_ptr(idev->stats.ipv6[0], i);
+               addrconf_stats = per_cpu_ptr(idev->stats.ipv6, i);
                u64_stats_init(&addrconf_stats->syncp);
-#if SNMP_ARRAY_SZ == 2
-               addrconf_stats = per_cpu_ptr(idev->stats.ipv6[1], i);
-               u64_stats_init(&addrconf_stats->syncp);
-#endif
        }
 
 
@@ -305,7 +300,7 @@ static int snmp6_alloc_dev(struct inet6_dev *idev)
 err_icmpmsg:
        kfree(idev->stats.icmpv6dev);
 err_icmp:
-       snmp_mib_free((void __percpu **)idev->stats.ipv6);
+       free_percpu(idev->stats.ipv6);
 err_ip:
        return -ENOMEM;
 }
@@ -2504,8 +2499,8 @@ static int inet6_addr_add(struct net *net, int ifindex,
        return PTR_ERR(ifp);
 }
 
-static int inet6_addr_del(struct net *net, int ifindex, const struct in6_addr *pfx,
-                         unsigned int plen)
+static int inet6_addr_del(struct net *net, int ifindex, u32 ifa_flags,
+                         const struct in6_addr *pfx, unsigned int plen)
 {
        struct inet6_ifaddr *ifp;
        struct inet6_dev *idev;
@@ -2528,7 +2523,12 @@ static int inet6_addr_del(struct net *net, int ifindex, const struct in6_addr *p
                        in6_ifa_hold(ifp);
                        read_unlock_bh(&idev->lock);
 
+                       if (!(ifp->flags & IFA_F_TEMPORARY) &&
+                           (ifa_flags & IFA_F_MANAGETEMPADDR))
+                               manage_tempaddrs(idev, ifp, 0, 0, false,
+                                                jiffies);
                        ipv6_del_addr(ifp);
+                       addrconf_verify_rtnl();
                        return 0;
                }
        }
@@ -2568,7 +2568,7 @@ int addrconf_del_ifaddr(struct net *net, void __user *arg)
                return -EFAULT;
 
        rtnl_lock();
-       err = inet6_addr_del(net, ireq.ifr6_ifindex, &ireq.ifr6_addr,
+       err = inet6_addr_del(net, ireq.ifr6_ifindex, 0, &ireq.ifr6_addr,
                             ireq.ifr6_prefixlen);
        rtnl_unlock();
        return err;
@@ -2813,18 +2813,6 @@ static void addrconf_gre_config(struct net_device *dev)
 }
 #endif
 
-static inline int
-ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
-{
-       struct in6_addr lladdr;
-
-       if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
-               addrconf_add_linklocal(idev, &lladdr);
-               return 0;
-       }
-       return -1;
-}
-
 static int addrconf_notify(struct notifier_block *this, unsigned long event,
                           void *ptr)
 {
@@ -3743,6 +3731,7 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
        struct ifaddrmsg *ifm;
        struct nlattr *tb[IFA_MAX+1];
        struct in6_addr *pfx, *peer_pfx;
+       u32 ifa_flags;
        int err;
 
        err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy);
@@ -3754,7 +3743,13 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
        if (pfx == NULL)
                return -EINVAL;
 
-       return inet6_addr_del(net, ifm->ifa_index, pfx, ifm->ifa_prefixlen);
+       ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifm->ifa_flags;
+
+       /* We ignore other flags so far. */
+       ifa_flags &= IFA_F_MANAGETEMPADDR;
+
+       return inet6_addr_del(net, ifm->ifa_index, ifa_flags, pfx,
+                             ifm->ifa_prefixlen);
 }
 
 static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags,
@@ -4363,7 +4358,7 @@ static inline void __snmp6_fill_statsdev(u64 *stats, atomic_long_t *mib,
        memset(&stats[items], 0, pad);
 }
 
-static inline void __snmp6_fill_stats64(u64 *stats, void __percpu **mib,
+static inline void __snmp6_fill_stats64(u64 *stats, void __percpu *mib,
                                      int items, int bytes, size_t syncpoff)
 {
        int i;
@@ -4383,7 +4378,7 @@ static void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype,
 {
        switch (attrtype) {
        case IFLA_INET6_STATS:
-               __snmp6_fill_stats64(stats, (void __percpu **)idev->stats.ipv6,
+               __snmp6_fill_stats64(stats, idev->stats.ipv6,
                                     IPSTATS_MIB_MAX, bytes, offsetof(struct ipstats_mib, syncp));
                break;
        case IFLA_INET6_ICMP6STATS:
index 4c11cbcf83089152052b60072dbe11d147d10803..e6960457f62582c4ca41c674cc531d64ca76b038 100644 (file)
@@ -123,7 +123,7 @@ static void snmp6_free_dev(struct inet6_dev *idev)
 {
        kfree(idev->stats.icmpv6msgdev);
        kfree(idev->stats.icmpv6dev);
-       snmp_mib_free((void __percpu **)idev->stats.ipv6);
+       free_percpu(idev->stats.ipv6);
 }
 
 /* Nobody refers to this device, we may destroy it. */
index d935889f1008ae93ff1efe52badeedf41352f257..7cb4392690dd614b1672ad51cc57bbe36218e34c 100644 (file)
@@ -106,7 +106,6 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol,
        struct inet_protosw *answer;
        struct proto *answer_prot;
        unsigned char answer_flags;
-       char answer_no_check;
        int try_loading_module = 0;
        int err;
 
@@ -162,7 +161,6 @@ lookup_protocol:
 
        sock->ops = answer->ops;
        answer_prot = answer->prot;
-       answer_no_check = answer->no_check;
        answer_flags = answer->flags;
        rcu_read_unlock();
 
@@ -176,7 +174,6 @@ lookup_protocol:
        sock_init_data(sock, sk);
 
        err = 0;
-       sk->sk_no_check = answer_no_check;
        if (INET_PROTOSW_REUSE & answer_flags)
                sk->sk_reuse = SK_CAN_REUSE;
 
@@ -715,33 +712,25 @@ static int __net_init ipv6_init_mibs(struct net *net)
 {
        int i;
 
-       if (snmp_mib_init((void __percpu **)net->mib.udp_stats_in6,
-                         sizeof(struct udp_mib),
-                         __alignof__(struct udp_mib)) < 0)
+       net->mib.udp_stats_in6 = alloc_percpu(struct udp_mib);
+       if (!net->mib.udp_stats_in6)
                return -ENOMEM;
-       if (snmp_mib_init((void __percpu **)net->mib.udplite_stats_in6,
-                         sizeof(struct udp_mib),
-                         __alignof__(struct udp_mib)) < 0)
+       net->mib.udplite_stats_in6 = alloc_percpu(struct udp_mib);
+       if (!net->mib.udplite_stats_in6)
                goto err_udplite_mib;
-       if (snmp_mib_init((void __percpu **)net->mib.ipv6_statistics,
-                         sizeof(struct ipstats_mib),
-                         __alignof__(struct ipstats_mib)) < 0)
+       net->mib.ipv6_statistics = alloc_percpu(struct ipstats_mib);
+       if (!net->mib.ipv6_statistics)
                goto err_ip_mib;
 
        for_each_possible_cpu(i) {
                struct ipstats_mib *af_inet6_stats;
-               af_inet6_stats = per_cpu_ptr(net->mib.ipv6_statistics[0], i);
+               af_inet6_stats = per_cpu_ptr(net->mib.ipv6_statistics, i);
                u64_stats_init(&af_inet6_stats->syncp);
-#if SNMP_ARRAY_SZ == 2
-               af_inet6_stats = per_cpu_ptr(net->mib.ipv6_statistics[1], i);
-               u64_stats_init(&af_inet6_stats->syncp);
-#endif
        }
 
 
-       if (snmp_mib_init((void __percpu **)net->mib.icmpv6_statistics,
-                         sizeof(struct icmpv6_mib),
-                         __alignof__(struct icmpv6_mib)) < 0)
+       net->mib.icmpv6_statistics = alloc_percpu(struct icmpv6_mib);
+       if (!net->mib.icmpv6_statistics)
                goto err_icmp_mib;
        net->mib.icmpv6msg_statistics = kzalloc(sizeof(struct icmpv6msg_mib),
                                                GFP_KERNEL);
@@ -750,22 +739,22 @@ static int __net_init ipv6_init_mibs(struct net *net)
        return 0;
 
 err_icmpmsg_mib:
-       snmp_mib_free((void __percpu **)net->mib.icmpv6_statistics);
+       free_percpu(net->mib.icmpv6_statistics);
 err_icmp_mib:
-       snmp_mib_free((void __percpu **)net->mib.ipv6_statistics);
+       free_percpu(net->mib.ipv6_statistics);
 err_ip_mib:
-       snmp_mib_free((void __percpu **)net->mib.udplite_stats_in6);
+       free_percpu(net->mib.udplite_stats_in6);
 err_udplite_mib:
-       snmp_mib_free((void __percpu **)net->mib.udp_stats_in6);
+       free_percpu(net->mib.udp_stats_in6);
        return -ENOMEM;
 }
 
 static void ipv6_cleanup_mibs(struct net *net)
 {
-       snmp_mib_free((void __percpu **)net->mib.udp_stats_in6);
-       snmp_mib_free((void __percpu **)net->mib.udplite_stats_in6);
-       snmp_mib_free((void __percpu **)net->mib.ipv6_statistics);
-       snmp_mib_free((void __percpu **)net->mib.icmpv6_statistics);
+       free_percpu(net->mib.udp_stats_in6);
+       free_percpu(net->mib.udplite_stats_in6);
+       free_percpu(net->mib.ipv6_statistics);
+       free_percpu(net->mib.icmpv6_statistics);
        kfree(net->mib.icmpv6msg_statistics);
 }
 
index 7b326529e6a2cba57695697cfff436357c347b2c..f6c84a6eb2389c55f4abd55fefbd073c73743a2a 100644 (file)
@@ -400,6 +400,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
        int len;
        int hlimit;
        int err = 0;
+       u32 mark = IP6_REPLY_MARK(net, skb->mark);
 
        if ((u8 *)hdr < skb->head ||
            (skb_network_header(skb) + sizeof(*hdr)) > skb_tail_pointer(skb))
@@ -466,6 +467,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
        fl6.daddr = hdr->saddr;
        if (saddr)
                fl6.saddr = *saddr;
+       fl6.flowi6_mark = mark;
        fl6.flowi6_oif = iif;
        fl6.fl6_icmp_type = type;
        fl6.fl6_icmp_code = code;
@@ -474,6 +476,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
        sk = icmpv6_xmit_lock(net);
        if (sk == NULL)
                return;
+       sk->sk_mark = mark;
        np = inet6_sk(sk);
 
        if (!icmpv6_xrlim_allow(sk, type, &fl6))
@@ -493,12 +496,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
        if (IS_ERR(dst))
                goto out;
 
-       if (ipv6_addr_is_multicast(&fl6.daddr))
-               hlimit = np->mcast_hops;
-       else
-               hlimit = np->hop_limit;
-       if (hlimit < 0)
-               hlimit = ip6_dst_hoplimit(dst);
+       hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
 
        msg.skb = skb;
        msg.offset = skb_network_offset(skb);
@@ -556,6 +554,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
        int err = 0;
        int hlimit;
        u8 tclass;
+       u32 mark = IP6_REPLY_MARK(net, skb->mark);
 
        saddr = &ipv6_hdr(skb)->daddr;
 
@@ -574,11 +573,13 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
                fl6.saddr = *saddr;
        fl6.flowi6_oif = skb->dev->ifindex;
        fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY;
+       fl6.flowi6_mark = mark;
        security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
 
        sk = icmpv6_xmit_lock(net);
        if (sk == NULL)
                return;
+       sk->sk_mark = mark;
        np = inet6_sk(sk);
 
        if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
@@ -593,12 +594,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
        if (IS_ERR(dst))
                goto out;
 
-       if (ipv6_addr_is_multicast(&fl6.daddr))
-               hlimit = np->mcast_hops;
-       else
-               hlimit = np->hop_limit;
-       if (hlimit < 0)
-               hlimit = ip6_dst_hoplimit(dst);
+       hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
 
        idev = __in6_dev_get(skb->dev);
 
@@ -702,22 +698,11 @@ static int icmpv6_rcv(struct sk_buff *skb)
        saddr = &ipv6_hdr(skb)->saddr;
        daddr = &ipv6_hdr(skb)->daddr;
 
-       /* Perform checksum. */
-       switch (skb->ip_summed) {
-       case CHECKSUM_COMPLETE:
-               if (!csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_ICMPV6,
-                                    skb->csum))
-                       break;
-               /* fall through */
-       case CHECKSUM_NONE:
-               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 [%pI6c > %pI6c]\n",
-                                      saddr, daddr);
-                       goto csum_error;
-               }
+       if (skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo)) {
+               LIMIT_NETDEBUG(KERN_DEBUG
+                              "ICMPv6 checksum failed [%pI6c > %pI6c]\n",
+                              saddr, daddr);
+               goto csum_error;
        }
 
        if (!pskb_pull(skb, sizeof(*hdr)))
index d4ade34ab37566d8cca9e164f5fde5fb5a762fe6..a245e5ddffbd0450968c44de7d3fcd8a1dd055cf 100644 (file)
@@ -81,7 +81,7 @@ struct dst_entry *inet6_csk_route_req(struct sock *sk,
        final_p = fl6_update_dst(fl6, np->opt, &final);
        fl6->saddr = ireq->ir_v6_loc_addr;
        fl6->flowi6_oif = ireq->ir_iif;
-       fl6->flowi6_mark = sk->sk_mark;
+       fl6->flowi6_mark = ireq->ir_mark;
        fl6->fl6_dport = ireq->ir_rmt_port;
        fl6->fl6_sport = htons(ireq->ir_num);
        security_req_classify_flow(req, flowi6_to_flowi(fl6));
index ee7a97f510cbd9f94fa24eafa43ddba201c75f3e..9a4d7322fb22234c5d697c384a74949beb2e50c5 100644 (file)
@@ -75,25 +75,50 @@ int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto)
                        return err;
        }
 
-       if (uh->check == 0) {
-               /* RFC 2460 section 8.1 says that we SHOULD log
-                  this error. Well, it is reasonable.
-                */
-               LIMIT_NETDEBUG(KERN_INFO "IPv6: udp checksum is 0 for [%pI6c]:%u->[%pI6c]:%u\n",
-                              &ipv6_hdr(skb)->saddr, ntohs(uh->source),
-                              &ipv6_hdr(skb)->daddr, ntohs(uh->dest));
-               return 1;
-       }
-       if (skb->ip_summed == CHECKSUM_COMPLETE &&
-           !csum_ipv6_magic(&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
-                            skb->len, proto, skb->csum))
-               skb->ip_summed = CHECKSUM_UNNECESSARY;
+       /* To support RFC 6936 (allow zero checksum in UDP/IPV6 for tunnels)
+        * we accept a checksum of zero here. When we find the socket
+        * for the UDP packet we'll check if that socket allows zero checksum
+        * for IPv6 (set by socket option).
+        */
+       return skb_checksum_init_zero_check(skb, proto, uh->check,
+                                          ip6_compute_pseudo);
+}
+EXPORT_SYMBOL(udp6_csum_init);
+
+/* Function to set UDP checksum for an IPv6 UDP packet. This is intended
+ * for the simple case like when setting the checksum for a UDP tunnel.
+ */
+void udp6_set_csum(bool nocheck, struct sk_buff *skb,
+                  const struct in6_addr *saddr,
+                  const struct in6_addr *daddr, int len)
+{
+       struct udphdr *uh = udp_hdr(skb);
+
+       if (nocheck)
+               uh->check = 0;
+       else if (skb_is_gso(skb))
+               uh->check = ~udp_v6_check(len, saddr, daddr, 0);
+       else if (skb_dst(skb) && skb_dst(skb)->dev &&
+                (skb_dst(skb)->dev->features & NETIF_F_IPV6_CSUM)) {
 
-       if (!skb_csum_unnecessary(skb))
-               skb->csum = ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
-                                                        &ipv6_hdr(skb)->daddr,
-                                                        skb->len, proto, 0));
+               BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL);
 
-       return 0;
+               skb->ip_summed = CHECKSUM_PARTIAL;
+               skb->csum_start = skb_transport_header(skb) - skb->head;
+               skb->csum_offset = offsetof(struct udphdr, check);
+               uh->check = ~udp_v6_check(len, saddr, daddr, 0);
+       } else {
+               __wsum csum;
+
+               BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL);
+
+               uh->check = 0;
+               csum = skb_checksum(skb, 0, len, 0);
+               uh->check = udp_v6_check(len, saddr, daddr, csum);
+               if (uh->check == 0)
+                       uh->check = CSUM_MANGLED_0;
+
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+       }
 }
-EXPORT_SYMBOL(udp6_csum_init);
+EXPORT_SYMBOL(udp6_set_csum);
index 87891f5f57b5786725321d2fc301941531064bf2..cb4459bd1d294d1901cc03b9c203fbcbf761f53f 100644 (file)
@@ -71,8 +71,7 @@ static DEFINE_RWLOCK(fib6_walker_lock);
 #define FWS_INIT FWS_L
 #endif
 
-static void fib6_prune_clones(struct net *net, struct fib6_node *fn,
-                             struct rt6_info *rt);
+static void fib6_prune_clones(struct net *net, struct fib6_node *fn);
 static struct rt6_info *fib6_find_prefix(struct net *net, struct fib6_node *fn);
 static struct fib6_node *fib6_repair_tree(struct net *net, struct fib6_node *fn);
 static int fib6_walk(struct fib6_walker_t *w);
@@ -941,7 +940,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info,
        if (!err) {
                fib6_start_gc(info->nl_net, rt);
                if (!(rt->rt6i_flags & RTF_CACHE))
-                       fib6_prune_clones(info->nl_net, pn, rt);
+                       fib6_prune_clones(info->nl_net, pn);
        }
 
 out:
@@ -1375,7 +1374,7 @@ int fib6_del(struct rt6_info *rt, struct nl_info *info)
                        pn = pn->parent;
                }
 #endif
-               fib6_prune_clones(info->nl_net, pn, rt);
+               fib6_prune_clones(info->nl_net, pn);
        }
 
        /*
@@ -1601,10 +1600,9 @@ static int fib6_prune_clone(struct rt6_info *rt, void *arg)
        return 0;
 }
 
-static void fib6_prune_clones(struct net *net, struct fib6_node *fn,
-                             struct rt6_info *rt)
+static void fib6_prune_clones(struct net *net, struct fib6_node *fn)
 {
-       fib6_clean_tree(net, fn, fib6_prune_clone, 1, rt);
+       fib6_clean_tree(net, fn, fib6_prune_clone, 1, NULL);
 }
 
 /*
index 0961b5ef866d04803cf91243aec32bb9e2ea8cf7..4052694c6f2cb196b54ef7d5235d61d74e4ea69c 100644 (file)
@@ -26,7 +26,6 @@
 #include <net/sock.h>
 
 #include <net/ipv6.h>
-#include <net/addrconf.h>
 #include <net/rawv6.h>
 #include <net/transp_v6.h>
 
index 9d921462b57f293f9f49f6ec78936c84a3f756a0..3873181ed85614a28f9857d7d53acf2be9d2b9fb 100644 (file)
@@ -72,6 +72,7 @@ struct ip6gre_net {
 };
 
 static struct rtnl_link_ops ip6gre_link_ops __read_mostly;
+static struct rtnl_link_ops ip6gre_tap_ops __read_mostly;
 static int ip6gre_tunnel_init(struct net_device *dev);
 static void ip6gre_tunnel_setup(struct net_device *dev);
 static void ip6gre_tunnel_link(struct ip6gre_net *ign, struct ip6_tnl *t);
@@ -353,10 +354,10 @@ failed_free:
 
 static void ip6gre_tunnel_uninit(struct net_device *dev)
 {
-       struct net *net = dev_net(dev);
-       struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+       struct ip6_tnl *t = netdev_priv(dev);
+       struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id);
 
-       ip6gre_tunnel_unlink(ign, netdev_priv(dev));
+       ip6gre_tunnel_unlink(ign, t);
        dev_put(dev);
 }
 
@@ -467,17 +468,7 @@ static int ip6gre_rcv(struct sk_buff *skb)
                        goto drop;
 
                if (flags&GRE_CSUM) {
-                       switch (skb->ip_summed) {
-                       case CHECKSUM_COMPLETE:
-                               csum = csum_fold(skb->csum);
-                               if (!csum)
-                                       break;
-                               /* fall through */
-                       case CHECKSUM_NONE:
-                               skb->csum = 0;
-                               csum = __skb_checksum_complete(skb);
-                               skb->ip_summed = CHECKSUM_COMPLETE;
-                       }
+                       csum = skb_checksum_simple_validate(skb);
                        offset += 4;
                }
                if (flags&GRE_KEY) {
@@ -611,8 +602,8 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
                         int encap_limit,
                         __u32 *pmtu)
 {
-       struct net *net = dev_net(dev);
        struct ip6_tnl *tunnel = netdev_priv(dev);
+       struct net *net = tunnel->net;
        struct net_device *tdev;    /* Device to other host */
        struct ipv6hdr  *ipv6h;     /* Our new IP header */
        unsigned int max_headroom = 0; /* The extra header space needed */
@@ -979,7 +970,7 @@ static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
                int strict = (ipv6_addr_type(&p->raddr) &
                              (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL));
 
-               struct rt6_info *rt = rt6_lookup(dev_net(dev),
+               struct rt6_info *rt = rt6_lookup(t->net,
                                                 &p->raddr, &p->laddr,
                                                 p->link, strict);
 
@@ -1063,13 +1054,12 @@ static int ip6gre_tunnel_ioctl(struct net_device *dev,
        int err = 0;
        struct ip6_tnl_parm2 p;
        struct __ip6_tnl_parm p1;
-       struct ip6_tnl *t;
-       struct net *net = dev_net(dev);
+       struct ip6_tnl *t = netdev_priv(dev);
+       struct net *net = t->net;
        struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
 
        switch (cmd) {
        case SIOCGETTUNNEL:
-               t = NULL;
                if (dev == ign->fb_tunnel_dev) {
                        if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
                                err = -EFAULT;
@@ -1077,9 +1067,9 @@ static int ip6gre_tunnel_ioctl(struct net_device *dev,
                        }
                        ip6gre_tnl_parm_from_user(&p1, &p);
                        t = ip6gre_tunnel_locate(net, &p1, 0);
+                       if (t == NULL)
+                               t = netdev_priv(dev);
                }
-               if (t == NULL)
-                       t = netdev_priv(dev);
                memset(&p, 0, sizeof(p));
                ip6gre_tnl_parm_to_user(&p, &t->parms);
                if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
@@ -1242,7 +1232,6 @@ static void ip6gre_tunnel_setup(struct net_device *dev)
        dev->flags |= IFF_NOARP;
        dev->iflink = 0;
        dev->addr_len = sizeof(struct in6_addr);
-       dev->features |= NETIF_F_NETNS_LOCAL;
        dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
 }
 
@@ -1297,11 +1286,17 @@ static struct inet6_protocol ip6gre_protocol __read_mostly = {
        .flags       = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
 };
 
-static void ip6gre_destroy_tunnels(struct ip6gre_net *ign,
-       struct list_head *head)
+static void ip6gre_destroy_tunnels(struct net *net, struct list_head *head)
 {
+       struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+       struct net_device *dev, *aux;
        int prio;
 
+       for_each_netdev_safe(net, dev, aux)
+               if (dev->rtnl_link_ops == &ip6gre_link_ops ||
+                   dev->rtnl_link_ops == &ip6gre_tap_ops)
+                       unregister_netdevice_queue(dev, head);
+
        for (prio = 0; prio < 4; prio++) {
                int h;
                for (h = 0; h < HASH_SIZE; h++) {
@@ -1310,7 +1305,12 @@ static void ip6gre_destroy_tunnels(struct ip6gre_net *ign,
                        t = rtnl_dereference(ign->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 (!net_eq(dev_net(t->dev), net))
+                                       unregister_netdevice_queue(t->dev,
+                                                                  head);
                                t = rtnl_dereference(t->next);
                        }
                }
@@ -1329,6 +1329,11 @@ static int __net_init ip6gre_init_net(struct net *net)
                goto err_alloc_dev;
        }
        dev_net_set(ign->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.
+        */
+       ign->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL;
+
 
        ip6gre_fb_tunnel_init(ign->fb_tunnel_dev);
        ign->fb_tunnel_dev->rtnl_link_ops = &ip6gre_link_ops;
@@ -1349,12 +1354,10 @@ err_alloc_dev:
 
 static void __net_exit ip6gre_exit_net(struct net *net)
 {
-       struct ip6gre_net *ign;
        LIST_HEAD(list);
 
-       ign = net_generic(net, ip6gre_net_id);
        rtnl_lock();
-       ip6gre_destroy_tunnels(ign, &list);
+       ip6gre_destroy_tunnels(net, &list);
        unregister_netdevice_many(&list);
        rtnl_unlock();
 }
@@ -1531,15 +1534,14 @@ out:
 static int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[],
                            struct nlattr *data[])
 {
-       struct ip6_tnl *t, *nt;
-       struct net *net = dev_net(dev);
+       struct ip6_tnl *t, *nt = netdev_priv(dev);
+       struct net *net = nt->net;
        struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
        struct __ip6_tnl_parm p;
 
        if (dev == ign->fb_tunnel_dev)
                return -EINVAL;
 
-       nt = netdev_priv(dev);
        ip6gre_netlink_parms(data, &p);
 
        t = ip6gre_tunnel_locate(net, &p, 0);
index b2f091566f88453bce5eb3c33b47e1c9c040447c..65eda2a8af48280e0c068f24d8ef7adc8c7d8c9e 100644 (file)
@@ -97,9 +97,11 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
                       SKB_GSO_DODGY |
                       SKB_GSO_TCP_ECN |
                       SKB_GSO_GRE |
+                      SKB_GSO_GRE_CSUM |
                       SKB_GSO_IPIP |
                       SKB_GSO_SIT |
                       SKB_GSO_UDP_TUNNEL |
+                      SKB_GSO_UDP_TUNNEL_CSUM |
                       SKB_GSO_MPLS |
                       SKB_GSO_TCPV6 |
                       0)))
index fbf11562b54c1a7c8809f33cc969c66319517377..cb9df0eb40237065e696dd1013180b6c82a9c2dc 100644 (file)
@@ -219,7 +219,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
        skb->mark = sk->sk_mark;
 
        mtu = dst_mtu(dst);
-       if ((skb->len <= mtu) || skb->local_df || skb_is_gso(skb)) {
+       if ((skb->len <= mtu) || skb->ignore_df || skb_is_gso(skb)) {
                IP6_UPD_PO_STATS(net, ip6_dst_idev(skb_dst(skb)),
                              IPSTATS_MIB_OUT, skb->len);
                return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL,
@@ -347,11 +347,11 @@ static bool ip6_pkt_too_big(const struct sk_buff *skb, unsigned int mtu)
        if (skb->len <= mtu)
                return false;
 
-       /* ipv6 conntrack defrag sets max_frag_size + local_df */
+       /* ipv6 conntrack defrag sets max_frag_size + ignore_df */
        if (IP6CB(skb)->frag_max_size && IP6CB(skb)->frag_max_size > mtu)
                return true;
 
-       if (skb->local_df)
+       if (skb->ignore_df)
                return false;
 
        if (skb_is_gso(skb) && skb_gso_network_seglen(skb) <= mtu)
@@ -537,6 +537,18 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from)
        skb_copy_secmark(to, from);
 }
 
+static void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt)
+{
+       static u32 ip6_idents_hashrnd __read_mostly;
+       u32 hash, id;
+
+       net_get_random_once(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd));
+
+       hash = __ipv6_addr_jhash(&rt->rt6i_dst.addr, ip6_idents_hashrnd);
+       id = ip_idents_reserve(hash, 1);
+       fhdr->identification = htonl(id);
+}
+
 int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
 {
        struct sk_buff *frag;
@@ -559,7 +571,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
        /* We must not fragment if the socket is set to force MTU discovery
         * or if the skb it not generated by a local socket.
         */
-       if (unlikely(!skb->local_df && skb->len > mtu) ||
+       if (unlikely(!skb->ignore_df && skb->len > mtu) ||
                     (IP6CB(skb)->frag_max_size &&
                      IP6CB(skb)->frag_max_size > mtu)) {
                if (skb->sk && dst_allfrag(skb_dst(skb)))
@@ -1234,7 +1246,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
                              sizeof(struct frag_hdr) : 0) +
                             rt->rt6i_nfheader_len;
 
-               if (ip6_sk_local_df(sk))
+               if (ip6_sk_ignore_df(sk))
                        maxnonfragsize = sizeof(struct ipv6hdr) + IPV6_MAXPLEN;
                else
                        maxnonfragsize = mtu;
@@ -1544,7 +1556,7 @@ int ip6_push_pending_frames(struct sock *sk)
        }
 
        /* Allow local fragmentation. */
-       skb->local_df = ip6_sk_local_df(sk);
+       skb->ignore_df = ip6_sk_ignore_df(sk);
 
        *final_dst = fl6->daddr;
        __skb_pull(skb, skb_network_header_len(skb));
index f6a66bb4114db3af0f23ee1f950acf8b9720304d..afa082458360216ff33012ee43e93354888e44b9 100644 (file)
@@ -61,6 +61,7 @@
 MODULE_AUTHOR("Ville Nuorvala");
 MODULE_DESCRIPTION("IPv6 tunneling device");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS_RTNL_LINK("ip6tnl");
 MODULE_ALIAS_NETDEV("ip6tnl0");
 
 #ifdef IP6_TNL_DEBUG
index 6cc9f9371cc57cd6b0233815a73134dddfa6207c..9aaa6bb229e485fd657a5ca4bd30b6ebb9e90c5f 100644 (file)
@@ -795,15 +795,12 @@ static const struct net_device_ops vti6_netdev_ops = {
  **/
 static void vti6_dev_setup(struct net_device *dev)
 {
-       struct ip6_tnl *t;
-
        dev->netdev_ops = &vti6_netdev_ops;
        dev->destructor = vti6_dev_free;
 
        dev->type = ARPHRD_TUNNEL6;
        dev->hard_header_len = LL_MAX_HEADER + sizeof(struct ipv6hdr);
        dev->mtu = ETH_DATA_LEN;
-       t = netdev_priv(dev);
        dev->flags |= IFF_NOARP;
        dev->addr_len = sizeof(struct in6_addr);
        dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
index 84c7f33d0cf858115abdb5f359a658b574637d57..387d8b8fc18db9744426e6f8c258a71d3f3d06fb 100644 (file)
@@ -90,17 +90,9 @@ nf_nat_ipv6_fn(const struct nf_hook_ops *ops,
        if (nf_ct_is_untracked(ct))
                return NF_ACCEPT;
 
-       nat = nfct_nat(ct);
-       if (!nat) {
-               /* NAT module was loaded late. */
-               if (nf_ct_is_confirmed(ct))
-                       return NF_ACCEPT;
-               nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
-               if (nat == NULL) {
-                       pr_debug("failed to add NAT extension\n");
-                       return NF_ACCEPT;
-               }
-       }
+       nat = nf_ct_nat_ext_add(ct);
+       if (nat == NULL)
+               return NF_ACCEPT;
 
        switch (ctinfo) {
        case IP_CT_RELATED:
index 767ab8da82189479456c632ecec95eef67da4bae..0d5279fd852a48643b5b0b54834b2ee68a893116 100644 (file)
@@ -451,7 +451,7 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
        }
        sub_frag_mem_limit(&fq->q, head->truesize);
 
-       head->local_df = 1;
+       head->ignore_df = 1;
        head->next = NULL;
        head->dev = dev;
        head->tstamp = fq->q.stamp;
index 9c3297a768fd1f0bb0aae5f784f2482b03a8ce15..d189fcb437feb9de2f43d5217389c90e029dbd21 100644 (file)
@@ -47,15 +47,9 @@ static unsigned int nf_nat_ipv6_fn(const struct nf_hook_ops *ops,
        if (ct == NULL || nf_ct_is_untracked(ct))
                return NF_ACCEPT;
 
-       nat = nfct_nat(ct);
-       if (nat == NULL) {
-               /* Conntrack module was loaded late, can't add extension. */
-               if (nf_ct_is_confirmed(ct))
-                       return NF_ACCEPT;
-               nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
-               if (nat == NULL)
-                       return NF_ACCEPT;
-       }
+       nat = nf_ct_nat_ext_add(ct);
+       if (nat == NULL)
+               return NF_ACCEPT;
 
        switch (ctinfo) {
        case IP_CT_RELATED:
index 56596ce390a19783fa20819aeb5110195fb7d484..5ec867e4a8b74fb23d8fdd2bcf9e0dce52f94455 100644 (file)
@@ -8,32 +8,6 @@
 #include <net/addrconf.h>
 #include <net/secure_seq.h>
 
-void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt)
-{
-       static atomic_t ipv6_fragmentation_id;
-       struct in6_addr addr;
-       int ident;
-
-#if IS_ENABLED(CONFIG_IPV6)
-       struct inet_peer *peer;
-       struct net *net;
-
-       net = dev_net(rt->dst.dev);
-       peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1);
-       if (peer) {
-               fhdr->identification = htonl(inet_getid(peer, 0));
-               inet_putpeer(peer);
-               return;
-       }
-#endif
-       ident = atomic_inc_return(&ipv6_fragmentation_id);
-
-       addr = rt->rt6i_dst.addr;
-       addr.s6_addr32[0] ^= (__force __be32)ident;
-       fhdr->identification = htonl(secure_ipv6_id(addr.s6_addr32));
-}
-EXPORT_SYMBOL(ipv6_select_ident);
-
 int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
 {
        u16 offset = sizeof(struct ipv6hdr);
@@ -104,6 +78,7 @@ int __ip6_local_out(struct sk_buff *skb)
        if (len > IPV6_MAXPLEN)
                len = 0;
        ipv6_hdr(skb)->payload_len = htons(len);
+       IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
 
        return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL,
                       skb_dst(skb)->dev, dst_output);
index bda74291c3e0d09c94ff5961cf393cb7695ee518..5b7a1ed2aba95a837a0063b96cb9803ff8925f0c 100644 (file)
@@ -51,7 +51,6 @@ static struct inet_protosw pingv6_protosw = {
        .protocol =  IPPROTO_ICMPV6,
        .prot =      &pingv6_prot,
        .ops =       &inet6_dgram_ops,
-       .no_check =  UDP_CSUM_DEFAULT,
        .flags =     INET_PROTOSW_REUSE,
 };
 
@@ -168,12 +167,7 @@ int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        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);
+       hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
 
        lock_sock(sk);
        err = ip6_append_data(sk, ping_getfrag, &pfh, len,
index 091d066a57b3711c5bd0eb5a352573d0aa2f3776..3317440ea34174032a7b0bee73d8f5b36cfb08ee 100644 (file)
@@ -186,7 +186,7 @@ static void snmp6_seq_show_icmpv6msg(struct seq_file *seq, atomic_long_t *smib)
 /* can be called either with percpu mib (pcpumib != NULL),
  * or shared one (smib != NULL)
  */
-static void snmp6_seq_show_item(struct seq_file *seq, void __percpu **pcpumib,
+static void snmp6_seq_show_item(struct seq_file *seq, void __percpu *pcpumib,
                                atomic_long_t *smib,
                                const struct snmp_mib *itemlist)
 {
@@ -201,7 +201,7 @@ static void snmp6_seq_show_item(struct seq_file *seq, void __percpu **pcpumib,
        }
 }
 
-static void snmp6_seq_show_item64(struct seq_file *seq, void __percpu **mib,
+static void snmp6_seq_show_item64(struct seq_file *seq, void __percpu *mib,
                                  const struct snmp_mib *itemlist, size_t syncpoff)
 {
        int i;
@@ -215,14 +215,14 @@ static int snmp6_seq_show(struct seq_file *seq, void *v)
 {
        struct net *net = (struct net *)seq->private;
 
-       snmp6_seq_show_item64(seq, (void __percpu **)net->mib.ipv6_statistics,
+       snmp6_seq_show_item64(seq, net->mib.ipv6_statistics,
                            snmp6_ipstats_list, offsetof(struct ipstats_mib, syncp));
-       snmp6_seq_show_item(seq, (void __percpu **)net->mib.icmpv6_statistics,
+       snmp6_seq_show_item(seq, net->mib.icmpv6_statistics,
                            NULL, snmp6_icmp6_list);
        snmp6_seq_show_icmpv6msg(seq, net->mib.icmpv6msg_statistics->mibs);
-       snmp6_seq_show_item(seq, (void __percpu **)net->mib.udp_stats_in6,
+       snmp6_seq_show_item(seq, net->mib.udp_stats_in6,
                            NULL, snmp6_udp6_list);
-       snmp6_seq_show_item(seq, (void __percpu **)net->mib.udplite_stats_in6,
+       snmp6_seq_show_item(seq, net->mib.udplite_stats_in6,
                            NULL, snmp6_udplite6_list);
        return 0;
 }
@@ -245,7 +245,7 @@ static int snmp6_dev_seq_show(struct seq_file *seq, void *v)
        struct inet6_dev *idev = (struct inet6_dev *)seq->private;
 
        seq_printf(seq, "%-32s\t%u\n", "ifIndex", idev->dev->ifindex);
-       snmp6_seq_show_item64(seq, (void __percpu **)idev->stats.ipv6,
+       snmp6_seq_show_item64(seq, idev->stats.ipv6,
                            snmp6_ipstats_list, offsetof(struct ipstats_mib, syncp));
        snmp6_seq_show_item(seq, NULL, idev->stats.icmpv6dev->mibs,
                            snmp6_icmp6_list);
index 1f29996e368a23e67bfe09d9b2ff10bd9b61805d..b2dc60b0c76403d38a6a96c8a1c299977ecb7bad 100644 (file)
@@ -873,14 +873,8 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
                err = PTR_ERR(dst);
                goto out;
        }
-       if (hlimit < 0) {
-               if (ipv6_addr_is_multicast(&fl6.daddr))
-                       hlimit = np->mcast_hops;
-               else
-                       hlimit = np->hop_limit;
-               if (hlimit < 0)
-                       hlimit = ip6_dst_hoplimit(dst);
-       }
+       if (hlimit < 0)
+               hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
 
        if (tclass < 0)
                tclass = np->tclass;
@@ -1328,7 +1322,6 @@ static struct inet_protosw rawv6_protosw = {
        .protocol       = IPPROTO_IP,   /* wild card */
        .prot           = &rawv6_prot,
        .ops            = &inet6_sockraw_ops,
-       .no_check       = UDP_CSUM_DEFAULT,
        .flags          = INET_PROTOSW_REUSE,
 };
 
index 6ebdb7b6744cc933801dcf4c6b3dea6ddce8a89c..f23fbd28a501ed5c3438abb7f1cbbec85233688b 100644 (file)
@@ -1176,7 +1176,7 @@ void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
 
        memset(&fl6, 0, sizeof(fl6));
        fl6.flowi6_oif = oif;
-       fl6.flowi6_mark = mark;
+       fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark);
        fl6.daddr = iph->daddr;
        fl6.saddr = iph->saddr;
        fl6.flowlabel = ip6_flowinfo(iph);
@@ -1455,7 +1455,7 @@ static int ip6_dst_gc(struct dst_ops *ops)
                goto out;
 
        net->ipv6.ip6_rt_gc_expire++;
-       fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, entries > rt_max_size);
+       fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
        entries = dst_entries_get_slow(ops);
        if (entries < ops->gc_thresh)
                net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
index e5a453ca302e1e55e4d8e6ca7069f97fbb2b6347..4f408176dc64eeb306e25e5bde275581080fa523 100644 (file)
@@ -560,12 +560,12 @@ static int ipip6_err(struct sk_buff *skb, u32 info)
 
        if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
                ipv4_update_pmtu(skb, dev_net(skb->dev), info,
-                                t->dev->ifindex, 0, IPPROTO_IPV6, 0);
+                                t->parms.link, 0, IPPROTO_IPV6, 0);
                err = 0;
                goto out;
        }
        if (type == ICMP_REDIRECT) {
-               ipv4_redirect(skb, dev_net(skb->dev), t->dev->ifindex, 0,
+               ipv4_redirect(skb, dev_net(skb->dev), t->parms.link, 0,
                              IPPROTO_IPV6, 0);
                err = 0;
                goto out;
@@ -1828,4 +1828,5 @@ xfrm_tunnel_failed:
 module_init(sit_init);
 module_exit(sit_cleanup);
 MODULE_LICENSE("GPL");
+MODULE_ALIAS_RTNL_LINK("sit");
 MODULE_ALIAS_NETDEV("sit0");
index bb53a5e73c1ab67c7a11430488b8418c4edbf98b..a822b880689b5fea5adeed30956afd2328a9c8b9 100644 (file)
@@ -216,6 +216,8 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
            ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL)
                ireq->ir_iif = inet6_iif(skb);
 
+       ireq->ir_mark = inet_request_mark(sk, skb);
+
        req->expires = 0UL;
        req->num_retrans = 0;
        ireq->ecn_ok            = ecn_ok;
@@ -242,7 +244,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
                final_p = fl6_update_dst(&fl6, np->opt, &final);
                fl6.saddr = ireq->ir_v6_loc_addr;
                fl6.flowi6_oif = sk->sk_bound_dev_if;
-               fl6.flowi6_mark = sk->sk_mark;
+               fl6.flowi6_mark = ireq->ir_mark;
                fl6.fl6_dport = ireq->ir_rmt_port;
                fl6.fl6_sport = inet_sk(sk)->inet_sport;
                security_req_classify_flow(req, flowi6_to_flowi(&fl6));
index 7f405a168822afab4fa5349317ef43f2ed8e3a0f..058f3eca2e53efd1fe016cfe8450d3ab0a9c13b1 100644 (file)
@@ -38,6 +38,13 @@ static struct ctl_table ipv6_table_template[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec
        },
+       {
+               .procname       = "fwmark_reflect",
+               .data           = &init_net.ipv6.sysctl.fwmark_reflect,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec
+       },
        { }
 };
 
index e289830ed6e35a3be4feda700b5b6789dac20292..229239ad96b1645de84bfc5b0ad76311e295f82c 100644 (file)
@@ -340,7 +340,8 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        struct sock *sk;
        int err;
        struct tcp_sock *tp;
-       __u32 seq;
+       struct request_sock *fastopen;
+       __u32 seq, snd_una;
        struct net *net = dev_net(skb->dev);
 
        sk = inet6_lookup(net, &tcp_hashinfo, &hdr->daddr,
@@ -371,8 +372,11 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 
        tp = tcp_sk(sk);
        seq = ntohl(th->seq);
+       /* XXX (TFO) - tp->snd_una should be ISN (tcp_create_openreq_child() */
+       fastopen = tp->fastopen_rsk;
+       snd_una = fastopen ? tcp_rsk(fastopen)->snt_isn : tp->snd_una;
        if (sk->sk_state != TCP_LISTEN &&
-           !between(seq, tp->snd_una, tp->snd_nxt)) {
+           !between(seq, snd_una, tp->snd_nxt)) {
                NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
                goto out;
        }
@@ -436,8 +440,13 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                goto out;
 
        case TCP_SYN_SENT:
-       case TCP_SYN_RECV:  /* Cannot happen.
-                              It can, it SYNs are crossed. --ANK */
+       case TCP_SYN_RECV:
+               /* Only in fast or simultaneous open. If a fast open socket is
+                * is already accepted it is treated as a connected one below.
+                */
+               if (fastopen && fastopen->sk == NULL)
+                       break;
+
                if (!sock_owned_by_user(sk)) {
                        sk->sk_err = err;
                        sk->sk_error_report(sk);                /* Wake people up to see the error (see connect in sock.c) */
@@ -463,7 +472,8 @@ out:
 static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst,
                              struct flowi6 *fl6,
                              struct request_sock *req,
-                             u16 queue_mapping)
+                             u16 queue_mapping,
+                             struct tcp_fastopen_cookie *foc)
 {
        struct inet_request_sock *ireq = inet_rsk(req);
        struct ipv6_pinfo *np = inet6_sk(sk);
@@ -474,7 +484,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst,
        if (!dst && (dst = inet6_csk_route_req(sk, fl6, req)) == NULL)
                goto done;
 
-       skb = tcp_make_synack(sk, dst, req, NULL);
+       skb = tcp_make_synack(sk, dst, req, foc);
 
        if (skb) {
                __tcp_v6_send_check(skb, &ireq->ir_v6_loc_addr,
@@ -498,7 +508,7 @@ static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req)
        struct flowi6 fl6;
        int res;
 
-       res = tcp_v6_send_synack(sk, NULL, &fl6, req, 0);
+       res = tcp_v6_send_synack(sk, NULL, &fl6, req, 0, NULL);
        if (!res) {
                TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNRETRANS);
@@ -802,6 +812,7 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,
                fl6.flowi6_oif = inet6_iif(skb);
        else
                fl6.flowi6_oif = oif;
+       fl6.flowi6_mark = IP6_REPLY_MARK(net, skb->mark);
        fl6.fl6_dport = t1->dest;
        fl6.fl6_sport = t1->source;
        security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
@@ -917,7 +928,12 @@ static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb)
 static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
                                  struct request_sock *req)
 {
-       tcp_v6_send_ack(skb, tcp_rsk(req)->snt_isn + 1, tcp_rsk(req)->rcv_isn + 1,
+       /* sk->sk_state == TCP_LISTEN -> for regular TCP_SYN_RECV
+        * sk->sk_state == TCP_SYN_RECV -> for Fast Open.
+        */
+       tcp_v6_send_ack(skb, (sk->sk_state == TCP_LISTEN) ?
+                       tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt,
+                       tcp_rsk(req)->rcv_nxt,
                        req->rcv_wnd, tcp_time_stamp, req->ts_recent, sk->sk_bound_dev_if,
                        tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr),
                        0, 0);
@@ -969,8 +985,10 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
        struct tcp_sock *tp = tcp_sk(sk);
        __u32 isn = TCP_SKB_CB(skb)->when;
        struct dst_entry *dst = NULL;
+       struct tcp_fastopen_cookie foc = { .len = -1 };
+       bool want_cookie = false, fastopen;
        struct flowi6 fl6;
-       bool want_cookie = false;
+       int err;
 
        if (skb->protocol == htons(ETH_P_IP))
                return tcp_v4_conn_request(sk, skb);
@@ -1001,7 +1019,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
        tcp_clear_options(&tmp_opt);
        tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);
        tmp_opt.user_mss = tp->rx_opt.user_mss;
-       tcp_parse_options(skb, &tmp_opt, 0, NULL);
+       tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc);
 
        if (want_cookie && !tmp_opt.saw_tstamp)
                tcp_clear_options(&tmp_opt);
@@ -1016,6 +1034,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
                TCP_ECN_create_request(req, skb, sock_net(sk));
 
        ireq->ir_iif = sk->sk_bound_dev_if;
+       ireq->ir_mark = inet_request_mark(sk, skb);
 
        /* So that link locals have meaning */
        if (!sk->sk_bound_dev_if &&
@@ -1074,19 +1093,27 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
                isn = tcp_v6_init_sequence(skb);
        }
 have_isn:
-       tcp_rsk(req)->snt_isn = isn;
 
        if (security_inet_conn_request(sk, skb, req))
                goto drop_and_release;
 
-       if (tcp_v6_send_synack(sk, dst, &fl6, req,
-                              skb_get_queue_mapping(skb)) ||
-           want_cookie)
+       if (!dst && (dst = inet6_csk_route_req(sk, &fl6, req)) == NULL)
                goto drop_and_free;
 
+       tcp_rsk(req)->snt_isn = isn;
        tcp_rsk(req)->snt_synack = tcp_time_stamp;
-       tcp_rsk(req)->listener = NULL;
-       inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
+       tcp_openreq_init_rwin(req, sk, dst);
+       fastopen = !want_cookie &&
+                  tcp_try_fastopen(sk, skb, req, &foc, dst);
+       err = tcp_v6_send_synack(sk, dst, &fl6, req,
+                                skb_get_queue_mapping(skb), &foc);
+       if (!fastopen) {
+               if (err || want_cookie)
+                       goto drop_and_free;
+
+               tcp_rsk(req)->listener = NULL;
+               inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
+       }
        return 0;
 
 drop_and_release:
@@ -1294,25 +1321,6 @@ out:
        return NULL;
 }
 
-static __sum16 tcp_v6_checksum_init(struct sk_buff *skb)
-{
-       if (skb->ip_summed == CHECKSUM_COMPLETE) {
-               if (!tcp_v6_check(skb->len, &ipv6_hdr(skb)->saddr,
-                                 &ipv6_hdr(skb)->daddr, skb->csum)) {
-                       skb->ip_summed = CHECKSUM_UNNECESSARY;
-                       return 0;
-               }
-       }
-
-       skb->csum = ~csum_unfold(tcp_v6_check(skb->len,
-                                             &ipv6_hdr(skb)->saddr,
-                                             &ipv6_hdr(skb)->daddr, 0));
-
-       if (skb->len <= 76)
-               return __skb_checksum_complete(skb);
-       return 0;
-}
-
 /* The socket must have it's spinlock held when we get
  * here.
  *
@@ -1486,7 +1494,7 @@ static int tcp_v6_rcv(struct sk_buff *skb)
        if (!pskb_may_pull(skb, th->doff*4))
                goto discard_it;
 
-       if (!skb_csum_unnecessary(skb) && tcp_v6_checksum_init(skb))
+       if (skb_checksum_init(skb, IPPROTO_TCP, ip6_compute_pseudo))
                goto csum_error;
 
        th = tcp_hdr(skb);
@@ -1779,6 +1787,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
        const struct inet_sock *inet = inet_sk(sp);
        const struct tcp_sock *tp = tcp_sk(sp);
        const struct inet_connection_sock *icsk = inet_csk(sp);
+       struct fastopen_queue *fastopenq = icsk->icsk_accept_queue.fastopenq;
 
        dest  = &sp->sk_v6_daddr;
        src   = &sp->sk_v6_rcv_saddr;
@@ -1821,7 +1830,9 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
                   jiffies_to_clock_t(icsk->icsk_ack.ato),
                   (icsk->icsk_ack.quick << 1) | icsk->icsk_ack.pingpong,
                   tp->snd_cwnd,
-                  tcp_in_initial_slowstart(tp) ? -1 : tp->snd_ssthresh
+                  sp->sk_state == TCP_LISTEN ?
+                       (fastopenq ? fastopenq->max_qlen : 0) :
+                       (tcp_in_initial_slowstart(tp) ? -1 : tp->snd_ssthresh)
                   );
 }
 
@@ -1981,7 +1992,6 @@ static struct inet_protosw tcpv6_protosw = {
        .protocol       =       IPPROTO_TCP,
        .prot           =       &tcpv6_prot,
        .ops            =       &inet6_stream_ops,
-       .no_check       =       0,
        .flags          =       INET_PROTOSW_PERMANENT |
                                INET_PROTOSW_ICSK,
 };
index 1e586d92260e1e75957060b896a8748e9beaa61a..95c8347992882e5cbef7719b0ae940fc659af057 100644 (file)
@@ -634,6 +634,10 @@ int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
                if (skb->len > sizeof(struct udphdr) && encap_rcv != NULL) {
                        int ret;
 
+                       /* Verify checksum before giving to encap */
+                       if (udp_lib_checksum_complete(skb))
+                               goto csum_error;
+
                        ret = encap_rcv(sk, skb);
                        if (ret <= 0) {
                                UDP_INC_STATS_BH(sock_net(sk),
@@ -701,17 +705,16 @@ static struct sock *udp_v6_mcast_next(struct net *net, struct sock *sk,
                                      int dif)
 {
        struct hlist_nulls_node *node;
-       struct sock *s = sk;
        unsigned short num = ntohs(loc_port);
 
-       sk_nulls_for_each_from(s, node) {
-               struct inet_sock *inet = inet_sk(s);
+       sk_nulls_for_each_from(sk, node) {
+               struct inet_sock *inet = inet_sk(sk);
 
-               if (!net_eq(sock_net(s), net))
+               if (!net_eq(sock_net(sk), net))
                        continue;
 
-               if (udp_sk(s)->udp_port_hash == num &&
-                   s->sk_family == PF_INET6) {
+               if (udp_sk(sk)->udp_port_hash == num &&
+                   sk->sk_family == PF_INET6) {
                        if (inet->inet_dport) {
                                if (inet->inet_dport != rmt_port)
                                        continue;
@@ -720,16 +723,16 @@ static struct sock *udp_v6_mcast_next(struct net *net, struct sock *sk,
                            !ipv6_addr_equal(&sk->sk_v6_daddr, rmt_addr))
                                continue;
 
-                       if (s->sk_bound_dev_if && s->sk_bound_dev_if != dif)
+                       if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)
                                continue;
 
                        if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) {
                                if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, loc_addr))
                                        continue;
                        }
-                       if (!inet6_mc_check(s, loc_addr, rmt_addr))
+                       if (!inet6_mc_check(sk, loc_addr, rmt_addr))
                                continue;
-                       return s;
+                       return sk;
                }
        }
        return NULL;
@@ -760,6 +763,17 @@ static void flush_stack(struct sock **stack, unsigned int count,
        if (unlikely(skb1))
                kfree_skb(skb1);
 }
+
+static void udp6_csum_zero_error(struct sk_buff *skb)
+{
+       /* RFC 2460 section 8.1 says that we SHOULD log
+        * this error. Well, it is reasonable.
+        */
+       LIMIT_NETDEBUG(KERN_INFO "IPv6: udp checksum is 0 for [%pI6c]:%u->[%pI6c]:%u\n",
+                      &ipv6_hdr(skb)->saddr, ntohs(udp_hdr(skb)->source),
+                      &ipv6_hdr(skb)->daddr, ntohs(udp_hdr(skb)->dest));
+}
+
 /*
  * Note: called only from the BH handler context,
  * so we don't need to lock the hashes.
@@ -779,7 +793,12 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
        dif = inet6_iif(skb);
        sk = udp_v6_mcast_next(net, sk, uh->dest, daddr, uh->source, saddr, dif);
        while (sk) {
-               stack[count++] = sk;
+               /* If zero checksum and no_check is not on for
+                * the socket then skip it.
+                */
+               if (uh->check || udp_sk(sk)->no_check6_rx)
+                       stack[count++] = sk;
+
                sk = udp_v6_mcast_next(net, sk_nulls_next(sk), uh->dest, daddr,
                                       uh->source, saddr, dif);
                if (unlikely(count == ARRAY_SIZE(stack))) {
@@ -867,6 +886,12 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
        if (sk != NULL) {
                int ret;
 
+               if (!uh->check && !udp_sk(sk)->no_check6_rx) {
+                       sock_put(sk);
+                       udp6_csum_zero_error(skb);
+                       goto csum_error;
+               }
+
                ret = udpv6_queue_rcv_skb(sk, skb);
                sock_put(sk);
 
@@ -879,6 +904,11 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
                return 0;
        }
 
+       if (!uh->check) {
+               udp6_csum_zero_error(skb);
+               goto csum_error;
+       }
+
        if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
                goto discard;
 
@@ -1006,7 +1036,10 @@ static int udp_v6_push_pending_frames(struct sock *sk)
 
        if (is_udplite)
                csum = udplite_csum_outgoing(sk, skb);
-       else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */
+       else if (up->no_check6_tx) {   /* UDP csum disabled */
+               skb->ip_summed = CHECKSUM_NONE;
+               goto send;
+       } else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */
                udp6_hwcsum_outgoing(sk, skb, &fl6->saddr, &fl6->daddr,
                                     up->len);
                goto send;
@@ -1232,14 +1265,8 @@ do_udp_sendmsg:
                goto out;
        }
 
-       if (hlimit < 0) {
-               if (ipv6_addr_is_multicast(&fl6.daddr))
-                       hlimit = np->mcast_hops;
-               else
-                       hlimit = np->hop_limit;
-               if (hlimit < 0)
-                       hlimit = ip6_dst_hoplimit(dst);
-       }
+       if (hlimit < 0)
+               hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
 
        if (tclass < 0)
                tclass = np->tclass;
@@ -1479,7 +1506,6 @@ static struct inet_protosw udpv6_protosw = {
        .protocol =  IPPROTO_UDP,
        .prot =      &udpv6_prot,
        .ops =       &inet6_dgram_ops,
-       .no_check =  UDP_CSUM_DEFAULT,
        .flags =     INET_PROTOSW_PERMANENT,
 };
 
index b261ee8b83fc87ddabcb447d1235ea8b67429de2..0ae3d98f83e00533c14e6f8c23ab78a5a6a72625 100644 (file)
@@ -63,7 +63,9 @@ 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_UDP_TUNNEL_CSUM |
                                      SKB_GSO_GRE |
+                                     SKB_GSO_GRE_CSUM |
                                      SKB_GSO_IPIP |
                                      SKB_GSO_SIT |
                                      SKB_GSO_MPLS) ||
@@ -76,7 +78,8 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
                goto out;
        }
 
-       if (skb->encapsulation && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL)
+       if (skb->encapsulation && skb_shinfo(skb)->gso_type &
+           (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM))
                segs = skb_udp_tunnel_segment(skb, features);
        else {
                /* Do software UFO. Complete and fill in the UDP checksum as HW cannot
index dfcc4be46898281f09038bbd7638edfefa04ea3e..9cf097e206e931c6e3c184f22b8adcabedc4c03a 100644 (file)
@@ -64,7 +64,6 @@ static struct inet_protosw udplite6_protosw = {
        .protocol       = IPPROTO_UDPLITE,
        .prot           = &udplitev6_prot,
        .ops            = &inet6_dgram_ops,
-       .no_check       = 0,
        .flags          = INET_PROTOSW_PERMANENT,
 };
 
index b930d080c66f231f338c7ea860c8c1d798d91fea..433672d07d0b55e1e436be704780e8c6f5777447 100644 (file)
@@ -78,7 +78,7 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb)
        if (mtu < IPV6_MIN_MTU)
                mtu = IPV6_MIN_MTU;
 
-       if (!skb->local_df && skb->len > mtu) {
+       if (!skb->ignore_df && skb->len > mtu) {
                skb->dev = dst->dev;
 
                if (xfrm6_local_dontfrag(skb))
@@ -114,7 +114,7 @@ int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
        if (err)
                return err;
 
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        return x->outer_mode->output2(x, skb);
 }
@@ -153,7 +153,7 @@ static int __xfrm6_output(struct sk_buff *skb)
        if (skb->len > mtu && xfrm6_local_dontfrag(skb)) {
                xfrm6_local_rxpmtu(skb, mtu);
                return -EMSGSIZE;
-       } else if (!skb->local_df && skb->len > mtu && skb->sk) {
+       } else if (!skb->ignore_df && skb->len > mtu && skb->sk) {
                xfrm_local_error(skb, mtu);
                return -EMSGSIZE;
        }
index 41e4e93cb3aae37df41ff419ef34be9c2255b5cc..91729b807c7d041ae379e89df335acefe5218635 100644 (file)
@@ -1353,7 +1353,7 @@ static int ipx_create(struct net *net, struct socket *sock, int protocol,
 
        sk_refcnt_debug_inc(sk);
        sock_init_data(sock, sk);
-       sk->sk_no_check = 1;            /* Checksum off by default */
+       sk->sk_no_check_tx = 1;         /* Checksum off by default */
        sock->ops = &ipx_dgram_ops;
        rc = 0;
 out:
index c1f03185c5e115ffe359e39a0bfb17efe8d4c38a..67e7ad3d46b1fb4489a175836351607e7f5ae741 100644 (file)
@@ -236,7 +236,8 @@ int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx,
        }
 
        /* Apply checksum. Not allowed on 802.3 links. */
-       if (sk->sk_no_check || intrfc->if_dlink_type == htons(IPX_FRAME_8023))
+       if (sk->sk_no_check_tx ||
+           intrfc->if_dlink_type == htons(IPX_FRAME_8023))
                ipx->ipx_checksum = htons(0xFFFF);
        else
                ipx->ipx_checksum = ipx_cksum(ipx, len + sizeof(struct ipxhdr));
index 8c9d7302c84682f4eec438405cf312da02cf9aab..7a95fa4a3de1e558a07485bd8f6dbb3b4dcf32b3 100644 (file)
@@ -682,6 +682,18 @@ struct sock *iucv_accept_dequeue(struct sock *parent, struct socket *newsock)
        return NULL;
 }
 
+static void __iucv_auto_name(struct iucv_sock *iucv)
+{
+       char name[12];
+
+       sprintf(name, "%08x", atomic_inc_return(&iucv_sk_list.autobind_name));
+       while (__iucv_get_sock_by_name(name)) {
+               sprintf(name, "%08x",
+                       atomic_inc_return(&iucv_sk_list.autobind_name));
+       }
+       memcpy(iucv->src_name, name, 8);
+}
+
 /* Bind an unbound socket */
 static int iucv_sock_bind(struct socket *sock, struct sockaddr *addr,
                          int addr_len)
@@ -724,8 +736,12 @@ static int iucv_sock_bind(struct socket *sock, struct sockaddr *addr,
        rcu_read_lock();
        for_each_netdev_rcu(&init_net, dev) {
                if (!memcmp(dev->perm_addr, uid, 8)) {
-                       memcpy(iucv->src_name, sa->siucv_name, 8);
                        memcpy(iucv->src_user_id, sa->siucv_user_id, 8);
+                       /* Check for unitialized siucv_name */
+                       if (strncmp(sa->siucv_name, "        ", 8) == 0)
+                               __iucv_auto_name(iucv);
+                       else
+                               memcpy(iucv->src_name, sa->siucv_name, 8);
                        sk->sk_bound_dev_if = dev->ifindex;
                        iucv->hs_dev = dev;
                        dev_hold(dev);
@@ -763,7 +779,6 @@ done:
 static int iucv_sock_autobind(struct sock *sk)
 {
        struct iucv_sock *iucv = iucv_sk(sk);
-       char name[12];
        int err = 0;
 
        if (unlikely(!pr_iucv))
@@ -772,17 +787,9 @@ static int iucv_sock_autobind(struct sock *sk)
        memcpy(iucv->src_user_id, iucv_userid, 8);
 
        write_lock_bh(&iucv_sk_list.lock);
-
-       sprintf(name, "%08x", atomic_inc_return(&iucv_sk_list.autobind_name));
-       while (__iucv_get_sock_by_name(name)) {
-               sprintf(name, "%08x",
-                       atomic_inc_return(&iucv_sk_list.autobind_name));
-       }
-
+       __iucv_auto_name(iucv);
        write_unlock_bh(&iucv_sk_list.lock);
 
-       memcpy(&iucv->src_name, name, 8);
-
        if (!iucv->msglimit)
                iucv->msglimit = IUCV_QUEUELEN_DEFAULT;
 
@@ -1936,11 +1943,10 @@ static int afiucv_hs_callback_syn(struct sock *sk, struct sk_buff *skb)
            sk_acceptq_is_full(sk) ||
            !nsk) {
                /* error on server socket - connection refused */
-               if (nsk)
-                       sk_free(nsk);
                afiucv_swap_src_dest(skb);
                trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_FIN;
                err = dev_queue_xmit(skb);
+               iucv_sock_kill(nsk);
                bh_unlock_sock(sk);
                goto out;
        }
index f3c83073afc49f7aad98262cc5776b3edddc7f87..ba2a2f95911c99732dc2e3fb1ae37b9f977d1f5a 100644 (file)
@@ -1476,9 +1476,7 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, const struct sadb_msg
        else
                err = xfrm_state_update(x);
 
-       xfrm_audit_state_add(x, err ? 0 : 1,
-                            audit_get_loginuid(current),
-                            audit_get_sessionid(current), 0);
+       xfrm_audit_state_add(x, err ? 0 : 1, true);
 
        if (err < 0) {
                x->km.state = XFRM_STATE_DEAD;
@@ -1532,9 +1530,7 @@ static int pfkey_delete(struct sock *sk, struct sk_buff *skb, const struct sadb_
        c.event = XFRM_MSG_DELSA;
        km_state_notify(x, &c);
 out:
-       xfrm_audit_state_delete(x, err ? 0 : 1,
-                               audit_get_loginuid(current),
-                               audit_get_sessionid(current), 0);
+       xfrm_audit_state_delete(x, err ? 0 : 1, true);
        xfrm_state_put(x);
 
        return err;
@@ -1726,17 +1722,13 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, const struct sadb_m
        struct net *net = sock_net(sk);
        unsigned int proto;
        struct km_event c;
-       struct xfrm_audit audit_info;
        int err, err2;
 
        proto = pfkey_satype2proto(hdr->sadb_msg_satype);
        if (proto == 0)
                return -EINVAL;
 
-       audit_info.loginuid = audit_get_loginuid(current);
-       audit_info.sessionid = audit_get_sessionid(current);
-       audit_info.secid = 0;
-       err = xfrm_state_flush(net, proto, &audit_info);
+       err = xfrm_state_flush(net, proto, true);
        err2 = unicast_flush_resp(sk, hdr);
        if (err || err2) {
                if (err == -ESRCH) /* empty table - go quietly */
@@ -2288,9 +2280,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, const struct sadb_
        err = xfrm_policy_insert(pol->sadb_x_policy_dir-1, xp,
                                 hdr->sadb_msg_type != SADB_X_SPDUPDATE);
 
-       xfrm_audit_policy_add(xp, err ? 0 : 1,
-                             audit_get_loginuid(current),
-                             audit_get_sessionid(current), 0);
+       xfrm_audit_policy_add(xp, err ? 0 : 1, true);
 
        if (err)
                goto out;
@@ -2372,9 +2362,7 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, const struct sa
        if (xp == NULL)
                return -ENOENT;
 
-       xfrm_audit_policy_delete(xp, err ? 0 : 1,
-                                audit_get_loginuid(current),
-                                audit_get_sessionid(current), 0);
+       xfrm_audit_policy_delete(xp, err ? 0 : 1, true);
 
        if (err)
                goto out;
@@ -2553,7 +2541,7 @@ static int pfkey_migrate(struct sock *sk, struct sk_buff *skb,
                sel.sport_mask = htons(0xffff);
 
        /* set destination address info of selector */
-       sa = ext_hdrs[SADB_EXT_ADDRESS_DST - 1],
+       sa = ext_hdrs[SADB_EXT_ADDRESS_DST - 1];
        pfkey_sadb_addr2xfrm_addr(sa, &sel.daddr);
        sel.prefixlen_d = sa->sadb_address_prefixlen;
        sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
@@ -2622,9 +2610,7 @@ static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, const struct sadb_
                return -ENOENT;
 
        if (delete) {
-               xfrm_audit_policy_delete(xp, err ? 0 : 1,
-                               audit_get_loginuid(current),
-                               audit_get_sessionid(current), 0);
+               xfrm_audit_policy_delete(xp, err ? 0 : 1, true);
 
                if (err)
                        goto out;
@@ -2733,13 +2719,9 @@ static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, const struct sad
 {
        struct net *net = sock_net(sk);
        struct km_event c;
-       struct xfrm_audit audit_info;
        int err, err2;
 
-       audit_info.loginuid = audit_get_loginuid(current);
-       audit_info.sessionid = audit_get_sessionid(current);
-       audit_info.secid = 0;
-       err = xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, &audit_info);
+       err = xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, true);
        err2 = unicast_flush_resp(sk, hdr);
        if (err || err2) {
                if (err == -ESRCH) /* empty table - old silent behavior */
index a4e37d7158dcca42455a04eaf0460a48e39242fd..bea259043205ebe3605c34ed35da504807792af1 100644 (file)
@@ -495,52 +495,6 @@ out:
        spin_unlock_bh(&session->reorder_q.lock);
 }
 
-static inline int l2tp_verify_udp_checksum(struct sock *sk,
-                                          struct sk_buff *skb)
-{
-       struct udphdr *uh = udp_hdr(skb);
-       u16 ulen = ntohs(uh->len);
-       __wsum psum;
-
-       if (sk->sk_no_check || skb_csum_unnecessary(skb))
-               return 0;
-
-#if IS_ENABLED(CONFIG_IPV6)
-       if (sk->sk_family == PF_INET6 && !l2tp_tunnel(sk)->v4mapped) {
-               if (!uh->check) {
-                       LIMIT_NETDEBUG(KERN_INFO "L2TP: IPv6: checksum is 0\n");
-                       return 1;
-               }
-               if ((skb->ip_summed == CHECKSUM_COMPLETE) &&
-                   !csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
-                                    &ipv6_hdr(skb)->daddr, ulen,
-                                    IPPROTO_UDP, skb->csum)) {
-                       skb->ip_summed = CHECKSUM_UNNECESSARY;
-                       return 0;
-               }
-               skb->csum = ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
-                                                        &ipv6_hdr(skb)->daddr,
-                                                        skb->len, IPPROTO_UDP,
-                                                        0));
-       } else
-#endif
-       {
-               struct inet_sock *inet;
-               if (!uh->check)
-                       return 0;
-               inet = inet_sk(sk);
-               psum = csum_tcpudp_nofold(inet->inet_saddr, inet->inet_daddr,
-                                         ulen, IPPROTO_UDP, 0);
-
-               if ((skb->ip_summed == CHECKSUM_COMPLETE) &&
-                   !csum_fold(csum_add(psum, skb->csum)))
-                       return 0;
-               skb->csum = psum;
-       }
-
-       return __skb_checksum_complete(skb);
-}
-
 static int l2tp_seq_check_rx_window(struct l2tp_session *session, u32 nr)
 {
        u32 nws;
@@ -895,8 +849,7 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,
        u16 version;
        int length;
 
-       if (tunnel->sock && l2tp_verify_udp_checksum(tunnel->sock, skb))
-               goto discard_bad_csum;
+       /* UDP has verifed checksum */
 
        /* UDP always verifies the packet length. */
        __skb_pull(skb, sizeof(struct udphdr));
@@ -979,14 +932,6 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,
 
        return 0;
 
-discard_bad_csum:
-       LIMIT_NETDEBUG("%s: UDP: bad checksum\n", tunnel->name);
-       UDP_INC_STATS_USER(tunnel->l2tp_net, UDP_MIB_INERRORS, 0);
-       atomic_long_inc(&tunnel->stats.rx_errors);
-       kfree_skb(skb);
-
-       return 0;
-
 error:
        /* Put UDP header back */
        __skb_push(skb, sizeof(struct udphdr));
@@ -1128,7 +1073,7 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb,
        }
 
        /* Queue the packet to IP for output */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 #if IS_ENABLED(CONFIG_IPV6)
        if (tunnel->sock->sk_family == PF_INET6 && !tunnel->v4mapped)
                error = inet6_csk_xmit(tunnel->sock, skb, NULL);
@@ -1150,31 +1095,6 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb,
        return 0;
 }
 
-#if IS_ENABLED(CONFIG_IPV6)
-static void l2tp_xmit_ipv6_csum(struct sock *sk, struct sk_buff *skb,
-                               int udp_len)
-{
-       struct ipv6_pinfo *np = inet6_sk(sk);
-       struct udphdr *uh = udp_hdr(skb);
-
-       if (!skb_dst(skb) || !skb_dst(skb)->dev ||
-           !(skb_dst(skb)->dev->features & NETIF_F_IPV6_CSUM)) {
-               __wsum csum = skb_checksum(skb, 0, udp_len, 0);
-               skb->ip_summed = CHECKSUM_UNNECESSARY;
-               uh->check = csum_ipv6_magic(&np->saddr, &sk->sk_v6_daddr, udp_len,
-                                           IPPROTO_UDP, csum);
-               if (uh->check == 0)
-                       uh->check = CSUM_MANGLED_0;
-       } else {
-               skb->ip_summed = CHECKSUM_PARTIAL;
-               skb->csum_start = skb_transport_header(skb) - skb->head;
-               skb->csum_offset = offsetof(struct udphdr, check);
-               uh->check = ~csum_ipv6_magic(&np->saddr, &sk->sk_v6_daddr,
-                                            udp_len, IPPROTO_UDP, 0);
-       }
-}
-#endif
-
 /* If caller requires the skb to have a ppp header, the header must be
  * inserted in the skb data before calling this function.
  */
@@ -1186,7 +1106,6 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
        struct flowi *fl;
        struct udphdr *uh;
        struct inet_sock *inet;
-       __wsum csum;
        int headroom;
        int uhlen = (tunnel->encap == L2TP_ENCAPTYPE_UDP) ? sizeof(struct udphdr) : 0;
        int udp_len;
@@ -1235,33 +1154,17 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
                uh->dest = inet->inet_dport;
                udp_len = uhlen + hdr_len + data_len;
                uh->len = htons(udp_len);
-               uh->check = 0;
 
                /* Calculate UDP checksum if configured to do so */
 #if IS_ENABLED(CONFIG_IPV6)
                if (sk->sk_family == PF_INET6 && !tunnel->v4mapped)
-                       l2tp_xmit_ipv6_csum(sk, skb, udp_len);
+                       udp6_set_csum(udp_get_no_check6_tx(sk),
+                                     skb, &inet6_sk(sk)->saddr,
+                                     &sk->sk_v6_daddr, udp_len);
                else
 #endif
-               if (sk->sk_no_check == UDP_CSUM_NOXMIT)
-                       skb->ip_summed = CHECKSUM_NONE;
-               else if ((skb_dst(skb) && skb_dst(skb)->dev) &&
-                        (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM))) {
-                       skb->ip_summed = CHECKSUM_COMPLETE;
-                       csum = skb_checksum(skb, 0, udp_len, 0);
-                       uh->check = csum_tcpudp_magic(inet->inet_saddr,
-                                                     inet->inet_daddr,
-                                                     udp_len, IPPROTO_UDP, csum);
-                       if (uh->check == 0)
-                               uh->check = CSUM_MANGLED_0;
-               } else {
-                       skb->ip_summed = CHECKSUM_PARTIAL;
-                       skb->csum_start = skb_transport_header(skb) - skb->head;
-                       skb->csum_offset = offsetof(struct udphdr, check);
-                       uh->check = ~csum_tcpudp_magic(inet->inet_saddr,
-                                                      inet->inet_daddr,
-                                                      udp_len, IPPROTO_UDP, 0);
-               }
+               udp_set_csum(sk->sk_no_check_tx, skb, inet->inet_saddr,
+                            inet->inet_daddr, udp_len);
                break;
 
        case L2TP_ENCAPTYPE_IP:
@@ -1490,6 +1393,11 @@ static int l2tp_tunnel_sock_create(struct net *net,
                                             sizeof(udp6_addr), 0);
                        if (err < 0)
                                goto out;
+
+                       if (cfg->udp6_zero_tx_checksums)
+                               udp_set_no_check6_tx(sock->sk, true);
+                       if (cfg->udp6_zero_rx_checksums)
+                               udp_set_no_check6_rx(sock->sk, true);
                } else
 #endif
                {
@@ -1518,7 +1426,7 @@ static int l2tp_tunnel_sock_create(struct net *net,
                }
 
                if (!cfg->use_udp_checksums)
-                       sock->sk->sk_no_check = UDP_CSUM_NOXMIT;
+                       sock->sk->sk_no_check_tx = 1;
 
                break;
 
index 3f93ccd6ba9768fe171f4e35e79d613226c1dbc7..68aa9ffd4ae4d972cdd57ea742ddf3b78ba497d6 100644 (file)
@@ -162,7 +162,9 @@ struct l2tp_tunnel_cfg {
 #endif
        u16                     local_udp_port;
        u16                     peer_udp_port;
-       unsigned int            use_udp_checksums:1;
+       unsigned int            use_udp_checksums:1,
+                               udp6_zero_tx_checksums:1,
+                               udp6_zero_rx_checksums:1;
 };
 
 struct l2tp_tunnel {
index 3397fe6897c0326d3efb2277a2824f5c2038d3c4..369a9822488c45fc10be28ca00300e9382645a9e 100644 (file)
@@ -606,7 +606,6 @@ static struct inet_protosw l2tp_ip_protosw = {
        .protocol       = IPPROTO_L2TP,
        .prot           = &l2tp_ip_prot,
        .ops            = &l2tp_ip_ops,
-       .no_check       = 0,
 };
 
 static struct net_protocol l2tp_ip_protocol __read_mostly = {
index 7704ea9502fdc9e49a2b8bb7fda4b9b9ec8d1722..f3f98a156ceed8ebc5843368aa0d1988175e5fc7 100644 (file)
@@ -605,14 +605,8 @@ static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk,
                goto out;
        }
 
-       if (hlimit < 0) {
-               if (ipv6_addr_is_multicast(&fl6.daddr))
-                       hlimit = np->mcast_hops;
-               else
-                       hlimit = np->hop_limit;
-               if (hlimit < 0)
-                       hlimit = ip6_dst_hoplimit(dst);
-       }
+       if (hlimit < 0)
+               hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
 
        if (tclass < 0)
                tclass = np->tclass;
@@ -761,7 +755,6 @@ static struct inet_protosw l2tp_ip6_protosw = {
        .protocol       = IPPROTO_L2TP,
        .prot           = &l2tp_ip6_prot,
        .ops            = &l2tp_ip6_ops,
-       .no_check       = 0,
 };
 
 static struct inet6_protocol l2tp_ip6_protocol __read_mostly = {
index bd7387adea9eff25ce7ffd0683589dcdcc020ad0..0ac907adb2f472c0d49508ca6843363db27129b1 100644 (file)
@@ -161,6 +161,13 @@ static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info
                        cfg.peer_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_DPORT]);
                if (info->attrs[L2TP_ATTR_UDP_CSUM])
                        cfg.use_udp_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_CSUM]);
+
+#if IS_ENABLED(CONFIG_IPV6)
+               if (info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX])
+                       cfg.udp6_zero_tx_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX]);
+               if (info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX])
+                       cfg.udp6_zero_rx_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX]);
+#endif
        }
 
        if (info->attrs[L2TP_ATTR_DEBUG])
@@ -297,8 +304,7 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int fla
        case L2TP_ENCAPTYPE_UDP:
                if (nla_put_u16(skb, L2TP_ATTR_UDP_SPORT, ntohs(inet->inet_sport)) ||
                    nla_put_u16(skb, L2TP_ATTR_UDP_DPORT, ntohs(inet->inet_dport)) ||
-                   nla_put_u8(skb, L2TP_ATTR_UDP_CSUM,
-                              (sk->sk_no_check != UDP_CSUM_NOXMIT)))
+                   nla_put_u8(skb, L2TP_ATTR_UDP_CSUM, !sk->sk_no_check_tx))
                        goto nla_put_failure;
                /* NOBREAK */
        case L2TP_ENCAPTYPE_IP:
index 9d7d840aac6d11630ef902f4ce57db7a8491d1d4..1e46ffa69167973921b795f8757f903234a61b94 100644 (file)
@@ -25,7 +25,8 @@ mac80211-y := \
        wme.o \
        event.o \
        chan.o \
-       trace.o mlme.o
+       trace.o mlme.o \
+       tdls.o
 
 mac80211-$(CONFIG_MAC80211_LEDS) += led.o
 mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
index 7c7df475a401693dd44cb1a6b9d68dd255b2acd5..ec24378caaafaf333152e856aa0e2e920ddbb13f 100644 (file)
@@ -23,12 +23,13 @@ void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
                               u8 *data, size_t data_len, u8 *mic)
 {
        struct scatterlist assoc, pt, ct[2];
-       struct {
-               struct aead_request     req;
-               u8                      priv[crypto_aead_reqsize(tfm)];
-       } aead_req;
 
-       memset(&aead_req, 0, sizeof(aead_req));
+       char aead_req_data[sizeof(struct aead_request) +
+                          crypto_aead_reqsize(tfm)]
+               __aligned(__alignof__(struct aead_request));
+       struct aead_request *aead_req = (void *) aead_req_data;
+
+       memset(aead_req, 0, sizeof(aead_req_data));
 
        sg_init_one(&pt, data, data_len);
        sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
@@ -36,23 +37,23 @@ void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
        sg_set_buf(&ct[0], data, data_len);
        sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN);
 
-       aead_request_set_tfm(&aead_req.req, tfm);
-       aead_request_set_assoc(&aead_req.req, &assoc, assoc.length);
-       aead_request_set_crypt(&aead_req.req, &pt, ct, data_len, b_0);
+       aead_request_set_tfm(aead_req, tfm);
+       aead_request_set_assoc(aead_req, &assoc, assoc.length);
+       aead_request_set_crypt(aead_req, &pt, ct, data_len, b_0);
 
-       crypto_aead_encrypt(&aead_req.req);
+       crypto_aead_encrypt(aead_req);
 }
 
 int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
                              u8 *data, size_t data_len, u8 *mic)
 {
        struct scatterlist assoc, pt, ct[2];
-       struct {
-               struct aead_request     req;
-               u8                      priv[crypto_aead_reqsize(tfm)];
-       } aead_req;
+       char aead_req_data[sizeof(struct aead_request) +
+                          crypto_aead_reqsize(tfm)]
+               __aligned(__alignof__(struct aead_request));
+       struct aead_request *aead_req = (void *) aead_req_data;
 
-       memset(&aead_req, 0, sizeof(aead_req));
+       memset(aead_req, 0, sizeof(aead_req_data));
 
        sg_init_one(&pt, data, data_len);
        sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
@@ -60,12 +61,12 @@ int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
        sg_set_buf(&ct[0], data, data_len);
        sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN);
 
-       aead_request_set_tfm(&aead_req.req, tfm);
-       aead_request_set_assoc(&aead_req.req, &assoc, assoc.length);
-       aead_request_set_crypt(&aead_req.req, ct, &pt,
+       aead_request_set_tfm(aead_req, tfm);
+       aead_request_set_assoc(aead_req, &assoc, assoc.length);
+       aead_request_set_crypt(aead_req, ct, &pt,
                               data_len + IEEE80211_CCMP_MIC_LEN, b_0);
 
-       return crypto_aead_decrypt(&aead_req.req);
+       return crypto_aead_decrypt(aead_req);
 }
 
 struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[])
index aaa59d719592c0b7dc6ef3ddb4df8aaa578bc45c..d7513a503be11b180031342dcf316450fd6c69d3 100644 (file)
@@ -109,6 +109,15 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
 static int ieee80211_start_p2p_device(struct wiphy *wiphy,
                                      struct wireless_dev *wdev)
 {
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+       int ret;
+
+       mutex_lock(&sdata->local->chanctx_mtx);
+       ret = ieee80211_check_combinations(sdata, NULL, 0, 0);
+       mutex_unlock(&sdata->local->chanctx_mtx);
+       if (ret < 0)
+               return ret;
+
        return ieee80211_do_open(wdev, true);
 }
 
@@ -463,8 +472,10 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
 {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        struct ieee80211_local *local = sdata->local;
+       struct rate_control_ref *ref = local->rate_ctrl;
        struct timespec uptime;
        u64 packets = 0;
+       u32 thr = 0;
        int i, ac;
 
        sinfo->generation = sdata->local->sta_generation;
@@ -578,6 +589,17 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
        if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
                sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
+
+       /* check if the driver has a SW RC implementation */
+       if (ref && ref->ops->get_expected_throughput)
+               thr = ref->ops->get_expected_throughput(sta->rate_ctrl_priv);
+       else
+               thr = drv_get_expected_throughput(local, &sta->sta);
+
+       if (thr != 0) {
+               sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT;
+               sinfo->expected_throughput = thr;
+       }
 }
 
 static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = {
@@ -768,7 +790,7 @@ static void ieee80211_get_et_strings(struct wiphy *wiphy,
 }
 
 static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
-                                int idx, u8 *mac, struct station_info *sinfo)
+                                 int idx, u8 *mac, struct station_info *sinfo)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
@@ -798,7 +820,7 @@ static int ieee80211_dump_survey(struct wiphy *wiphy, struct net_device *dev,
 }
 
 static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
-                                u8 *mac, struct station_info *sinfo)
+                                const u8 *mac, struct station_info *sinfo)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
@@ -972,13 +994,13 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
        sdata->needed_rx_chains = sdata->local->rx_chains;
 
        mutex_lock(&local->mtx);
-       sdata->radar_required = params->radar_required;
        err = ieee80211_vif_use_channel(sdata, &params->chandef,
                                        IEEE80211_CHANCTX_SHARED);
+       if (!err)
+               ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
        mutex_unlock(&local->mtx);
        if (err)
                return err;
-       ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
 
        /*
         * Apply control port protocol, this allows us to
@@ -1075,6 +1097,31 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
        return 0;
 }
 
+bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       lockdep_assert_held(&local->mtx);
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+               if (!ieee80211_sdata_running(sdata))
+                       continue;
+
+               if (!sdata->vif.csa_active)
+                       continue;
+
+               if (!sdata->csa_block_tx)
+                       continue;
+
+               rcu_read_unlock();
+               return true;
+       }
+       rcu_read_unlock();
+
+       return false;
+}
+
 static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -1092,7 +1139,14 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        old_probe_resp = sdata_dereference(sdata->u.ap.probe_resp, sdata);
 
        /* abort any running channel switch */
+       mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
+       if (!ieee80211_csa_needs_block_tx(local))
+               ieee80211_wake_queues_by_reason(&local->hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+       mutex_unlock(&local->mtx);
+
        kfree(sdata->u.ap.next_beacon);
        sdata->u.ap.next_beacon = NULL;
 
@@ -1131,8 +1185,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
        skb_queue_purge(&sdata->u.ap.ps.bc_buf);
 
-       ieee80211_vif_copy_chanctx_to_vlans(sdata, true);
        mutex_lock(&local->mtx);
+       ieee80211_vif_copy_chanctx_to_vlans(sdata, true);
        ieee80211_vif_release_channel(sdata);
        mutex_unlock(&local->mtx);
 
@@ -1416,7 +1470,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
 }
 
 static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
-                                u8 *mac, struct station_parameters *params)
+                                const u8 *mac,
+                                struct station_parameters *params)
 {
        struct ieee80211_local *local = wiphy_priv(wiphy);
        struct sta_info *sta;
@@ -1450,6 +1505,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
        if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) {
                sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
                sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
+       } else {
+               sta->sta.tdls = true;
        }
 
        err = sta_apply_parameters(local, sta, params);
@@ -1483,7 +1540,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
 }
 
 static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
-                                u8 *mac)
+                                const u8 *mac)
 {
        struct ieee80211_sub_if_data *sdata;
 
@@ -1497,7 +1554,7 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
 }
 
 static int ieee80211_change_station(struct wiphy *wiphy,
-                                   struct net_device *dev, u8 *mac,
+                                   struct net_device *dev, const u8 *mac,
                                    struct station_parameters *params)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -1566,7 +1623,7 @@ static int ieee80211_change_station(struct wiphy *wiphy,
 
                if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
                    sta->sdata->u.vlan.sta) {
-                       rcu_assign_pointer(sta->sdata->u.vlan.sta, NULL);
+                       RCU_INIT_POINTER(sta->sdata->u.vlan.sta, NULL);
                        prev_4addr = true;
                }
 
@@ -1622,7 +1679,7 @@ out_err:
 
 #ifdef CONFIG_MAC80211_MESH
 static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
-                                u8 *dst, u8 *next_hop)
+                              const u8 *dst, const u8 *next_hop)
 {
        struct ieee80211_sub_if_data *sdata;
        struct mesh_path *mpath;
@@ -1650,7 +1707,7 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
 }
 
 static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *dst)
+                              const u8 *dst)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
@@ -1661,9 +1718,8 @@ static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
        return 0;
 }
 
-static int ieee80211_change_mpath(struct wiphy *wiphy,
-                                   struct net_device *dev,
-                                   u8 *dst, u8 *next_hop)
+static int ieee80211_change_mpath(struct wiphy *wiphy, struct net_device *dev,
+                                 const u8 *dst, const u8 *next_hop)
 {
        struct ieee80211_sub_if_data *sdata;
        struct mesh_path *mpath;
@@ -1755,8 +1811,8 @@ static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
 }
 
 static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
-                                int idx, u8 *dst, u8 *next_hop,
-                                struct mpath_info *pinfo)
+                               int idx, u8 *dst, u8 *next_hop,
+                               struct mpath_info *pinfo)
 {
        struct ieee80211_sub_if_data *sdata;
        struct mesh_path *mpath;
@@ -2930,7 +2986,6 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
        /* whatever, but channel contexts should not complain about that one */
        sdata->smps_mode = IEEE80211_SMPS_OFF;
        sdata->needed_rx_chains = local->rx_chains;
-       sdata->radar_required = true;
 
        err = ieee80211_vif_use_channel(sdata, chandef,
                                        IEEE80211_CHANCTX_SHARED);
@@ -3011,26 +3066,11 @@ void ieee80211_csa_finish(struct ieee80211_vif *vif)
 }
 EXPORT_SYMBOL(ieee80211_csa_finish);
 
-static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
+static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata,
+                                         u32 *changed)
 {
-       struct ieee80211_local *local = sdata->local;
-       int err, changed = 0;
-
-       sdata_assert_lock(sdata);
-
-       mutex_lock(&local->mtx);
-       sdata->radar_required = sdata->csa_radar_required;
-       err = ieee80211_vif_change_channel(sdata, &changed);
-       mutex_unlock(&local->mtx);
-       if (WARN_ON(err < 0))
-               return;
-
-       if (!local->use_chanctx) {
-               local->_oper_chandef = sdata->csa_chandef;
-               ieee80211_hw_config(local, 0);
-       }
+       int err;
 
-       sdata->vif.csa_active = false;
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
                err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
@@ -3038,35 +3078,74 @@ static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
                sdata->u.ap.next_beacon = NULL;
 
                if (err < 0)
-                       return;
-               changed |= err;
+                       return err;
+               *changed |= err;
                break;
        case NL80211_IFTYPE_ADHOC:
                err = ieee80211_ibss_finish_csa(sdata);
                if (err < 0)
-                       return;
-               changed |= err;
+                       return err;
+               *changed |= err;
                break;
 #ifdef CONFIG_MAC80211_MESH
        case NL80211_IFTYPE_MESH_POINT:
                err = ieee80211_mesh_finish_csa(sdata);
                if (err < 0)
-                       return;
-               changed |= err;
+                       return err;
+               *changed |= err;
                break;
 #endif
        default:
                WARN_ON(1);
-               return;
+               return -EINVAL;
        }
 
+       return 0;
+}
+
+static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       u32 changed = 0;
+       int err;
+
+       sdata_assert_lock(sdata);
+       lockdep_assert_held(&local->mtx);
+
+       sdata->radar_required = sdata->csa_radar_required;
+       err = ieee80211_vif_change_channel(sdata, &changed);
+       if (err < 0)
+               return err;
+
+       if (!local->use_chanctx) {
+               local->_oper_chandef = sdata->csa_chandef;
+               ieee80211_hw_config(local, 0);
+       }
+
+       sdata->vif.csa_active = false;
+
+       err = ieee80211_set_after_csa_beacon(sdata, &changed);
+       if (err)
+               return err;
+
        ieee80211_bss_info_change_notify(sdata, changed);
+       cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
 
-       ieee80211_wake_queues_by_reason(&sdata->local->hw,
+       if (!ieee80211_csa_needs_block_tx(local))
+               ieee80211_wake_queues_by_reason(&local->hw,
                                        IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
 
-       cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
+       return 0;
+}
+
+static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
+{
+       if (__ieee80211_csa_finalize(sdata)) {
+               sdata_info(sdata, "failed to finalize CSA, disconnecting\n");
+               cfg80211_stop_iface(sdata->local->hw.wiphy, &sdata->wdev,
+                                   GFP_KERNEL);
+       }
 }
 
 void ieee80211_csa_finalize_work(struct work_struct *work)
@@ -3074,8 +3153,11 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
        struct ieee80211_sub_if_data *sdata =
                container_of(work, struct ieee80211_sub_if_data,
                             csa_finalize_work);
+       struct ieee80211_local *local = sdata->local;
 
        sdata_lock(sdata);
+       mutex_lock(&local->mtx);
+
        /* AP might have been stopped while waiting for the lock. */
        if (!sdata->vif.csa_active)
                goto unlock;
@@ -3086,6 +3168,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
        ieee80211_csa_finalize(sdata);
 
 unlock:
+       mutex_unlock(&local->mtx);
        sdata_unlock(sdata);
 }
 
@@ -3121,9 +3204,25 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
                if (params->count <= 1)
                        break;
 
-               sdata->csa_counter_offset_beacon =
-                       params->counter_offset_beacon;
-               sdata->csa_counter_offset_presp = params->counter_offset_presp;
+               if ((params->n_counter_offsets_beacon >
+                    IEEE80211_MAX_CSA_COUNTERS_NUM) ||
+                   (params->n_counter_offsets_presp >
+                    IEEE80211_MAX_CSA_COUNTERS_NUM))
+                       return -EINVAL;
+
+               /* make sure we don't have garbage in other counters */
+               memset(sdata->csa_counter_offset_beacon, 0,
+                      sizeof(sdata->csa_counter_offset_beacon));
+               memset(sdata->csa_counter_offset_presp, 0,
+                      sizeof(sdata->csa_counter_offset_presp));
+
+               memcpy(sdata->csa_counter_offset_beacon,
+                      params->counter_offsets_beacon,
+                      params->n_counter_offsets_beacon * sizeof(u16));
+               memcpy(sdata->csa_counter_offset_presp,
+                      params->counter_offsets_presp,
+                      params->n_counter_offsets_presp * sizeof(u16));
+
                err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
                if (err < 0) {
                        kfree(sdata->u.ap.next_beacon);
@@ -3212,16 +3311,18 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
        return 0;
 }
 
-int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
-                            struct cfg80211_csa_settings *params)
+static int
+__ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+                          struct cfg80211_csa_settings *params)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
-       struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_chanctx_conf *conf;
        struct ieee80211_chanctx *chanctx;
        int err, num_chanctx, changed = 0;
 
        sdata_assert_lock(sdata);
+       lockdep_assert_held(&local->mtx);
 
        if (!list_empty(&local->roc_list) || local->scanning)
                return -EBUSY;
@@ -3233,23 +3334,24 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                                       &sdata->vif.bss_conf.chandef))
                return -EINVAL;
 
-       rcu_read_lock();
-       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
-       if (!chanctx_conf) {
-               rcu_read_unlock();
+       mutex_lock(&local->chanctx_mtx);
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
+       if (!conf) {
+               mutex_unlock(&local->chanctx_mtx);
                return -EBUSY;
        }
 
        /* don't handle for multi-VIF cases */
-       chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
-       if (chanctx->refcount > 1) {
-               rcu_read_unlock();
+       chanctx = container_of(conf, struct ieee80211_chanctx, conf);
+       if (ieee80211_chanctx_refcount(local, chanctx) > 1) {
+               mutex_unlock(&local->chanctx_mtx);
                return -EBUSY;
        }
        num_chanctx = 0;
        list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
                num_chanctx++;
-       rcu_read_unlock();
+       mutex_unlock(&local->chanctx_mtx);
 
        if (num_chanctx > 1)
                return -EBUSY;
@@ -3263,15 +3365,16 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                return err;
 
        sdata->csa_radar_required = params->radar_required;
-
-       if (params->block_tx)
-               ieee80211_stop_queues_by_reason(&local->hw,
-                               IEEE80211_MAX_QUEUE_MAP,
-                               IEEE80211_QUEUE_STOP_REASON_CSA);
-
        sdata->csa_chandef = params->chandef;
+       sdata->csa_block_tx = params->block_tx;
+       sdata->csa_current_counter = params->count;
        sdata->vif.csa_active = true;
 
+       if (sdata->csa_block_tx)
+               ieee80211_stop_queues_by_reason(&local->hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+
        if (changed) {
                ieee80211_bss_info_change_notify(sdata, changed);
                drv_channel_switch_beacon(sdata, &params->chandef);
@@ -3283,6 +3386,20 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        return 0;
 }
 
+int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+                            struct cfg80211_csa_settings *params)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+       int err;
+
+       mutex_lock(&local->mtx);
+       err = __ieee80211_channel_switch(wiphy, dev, params);
+       mutex_unlock(&local->mtx);
+
+       return err;
+}
+
 static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                             struct cfg80211_mgmt_tx_params *params,
                             u64 *cookie)
@@ -3295,6 +3412,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
        bool need_offchan = false;
        u32 flags;
        int ret;
+       u8 *data;
 
        if (params->dont_wait_for_ack)
                flags = IEEE80211_TX_CTL_NO_ACK;
@@ -3388,7 +3506,20 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
        }
        skb_reserve(skb, local->hw.extra_tx_headroom);
 
-       memcpy(skb_put(skb, params->len), params->buf, params->len);
+       data = skb_put(skb, params->len);
+       memcpy(data, params->buf, params->len);
+
+       /* Update CSA counters */
+       if (sdata->vif.csa_active &&
+           (sdata->vif.type == NL80211_IFTYPE_AP ||
+            sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
+           params->n_csa_offsets) {
+               int i;
+               u8 c = sdata->csa_current_counter;
+
+               for (i = 0; i < params->n_csa_offsets; i++)
+                       data[params->csa_offsets[i]] = c;
+       }
 
        IEEE80211_SKB_CB(skb)->flags = flags;
 
@@ -3497,320 +3628,6 @@ static int ieee80211_set_rekey_data(struct wiphy *wiphy,
        return 0;
 }
 
-static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
-{
-       u8 *pos = (void *)skb_put(skb, 7);
-
-       *pos++ = WLAN_EID_EXT_CAPABILITY;
-       *pos++ = 5; /* len */
-       *pos++ = 0x0;
-       *pos++ = 0x0;
-       *pos++ = 0x0;
-       *pos++ = 0x0;
-       *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
-}
-
-static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
-{
-       struct ieee80211_local *local = sdata->local;
-       u16 capab;
-
-       capab = 0;
-       if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ)
-               return capab;
-
-       if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
-               capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
-       if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE))
-               capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
-
-       return capab;
-}
-
-static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr,
-                                      u8 *peer, u8 *bssid)
-{
-       struct ieee80211_tdls_lnkie *lnkid;
-
-       lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
-
-       lnkid->ie_type = WLAN_EID_LINK_ID;
-       lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
-
-       memcpy(lnkid->bssid, bssid, ETH_ALEN);
-       memcpy(lnkid->init_sta, src_addr, ETH_ALEN);
-       memcpy(lnkid->resp_sta, peer, ETH_ALEN);
-}
-
-static int
-ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *peer, u8 action_code, u8 dialog_token,
-                              u16 status_code, struct sk_buff *skb)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
-       struct ieee80211_tdls_data *tf;
-
-       tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
-
-       memcpy(tf->da, peer, ETH_ALEN);
-       memcpy(tf->sa, sdata->vif.addr, ETH_ALEN);
-       tf->ether_type = cpu_to_be16(ETH_P_TDLS);
-       tf->payload_type = WLAN_TDLS_SNAP_RFTYPE;
-
-       switch (action_code) {
-       case WLAN_TDLS_SETUP_REQUEST:
-               tf->category = WLAN_CATEGORY_TDLS;
-               tf->action_code = WLAN_TDLS_SETUP_REQUEST;
-
-               skb_put(skb, sizeof(tf->u.setup_req));
-               tf->u.setup_req.dialog_token = dialog_token;
-               tf->u.setup_req.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
-               ieee80211_add_srates_ie(sdata, skb, false, band);
-               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
-               ieee80211_tdls_add_ext_capab(skb);
-               break;
-       case WLAN_TDLS_SETUP_RESPONSE:
-               tf->category = WLAN_CATEGORY_TDLS;
-               tf->action_code = WLAN_TDLS_SETUP_RESPONSE;
-
-               skb_put(skb, sizeof(tf->u.setup_resp));
-               tf->u.setup_resp.status_code = cpu_to_le16(status_code);
-               tf->u.setup_resp.dialog_token = dialog_token;
-               tf->u.setup_resp.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
-               ieee80211_add_srates_ie(sdata, skb, false, band);
-               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
-               ieee80211_tdls_add_ext_capab(skb);
-               break;
-       case WLAN_TDLS_SETUP_CONFIRM:
-               tf->category = WLAN_CATEGORY_TDLS;
-               tf->action_code = WLAN_TDLS_SETUP_CONFIRM;
-
-               skb_put(skb, sizeof(tf->u.setup_cfm));
-               tf->u.setup_cfm.status_code = cpu_to_le16(status_code);
-               tf->u.setup_cfm.dialog_token = dialog_token;
-               break;
-       case WLAN_TDLS_TEARDOWN:
-               tf->category = WLAN_CATEGORY_TDLS;
-               tf->action_code = WLAN_TDLS_TEARDOWN;
-
-               skb_put(skb, sizeof(tf->u.teardown));
-               tf->u.teardown.reason_code = cpu_to_le16(status_code);
-               break;
-       case WLAN_TDLS_DISCOVERY_REQUEST:
-               tf->category = WLAN_CATEGORY_TDLS;
-               tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST;
-
-               skb_put(skb, sizeof(tf->u.discover_req));
-               tf->u.discover_req.dialog_token = dialog_token;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int
-ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
-                          u8 *peer, u8 action_code, u8 dialog_token,
-                          u16 status_code, struct sk_buff *skb)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
-       struct ieee80211_mgmt *mgmt;
-
-       mgmt = (void *)skb_put(skb, 24);
-       memset(mgmt, 0, 24);
-       memcpy(mgmt->da, peer, ETH_ALEN);
-       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
-       memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
-
-       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-                                         IEEE80211_STYPE_ACTION);
-
-       switch (action_code) {
-       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
-               skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp));
-               mgmt->u.action.category = WLAN_CATEGORY_PUBLIC;
-               mgmt->u.action.u.tdls_discover_resp.action_code =
-                       WLAN_PUB_ACTION_TDLS_DISCOVER_RES;
-               mgmt->u.action.u.tdls_discover_resp.dialog_token =
-                       dialog_token;
-               mgmt->u.action.u.tdls_discover_resp.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
-               ieee80211_add_srates_ie(sdata, skb, false, band);
-               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
-               ieee80211_tdls_add_ext_capab(skb);
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *peer, u8 action_code, u8 dialog_token,
-                              u16 status_code, u32 peer_capability,
-                              const u8 *extra_ies, size_t extra_ies_len)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       struct ieee80211_local *local = sdata->local;
-       struct sk_buff *skb = NULL;
-       bool send_direct;
-       int ret;
-
-       if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
-               return -ENOTSUPP;
-
-       /* make sure we are in managed mode, and associated */
-       if (sdata->vif.type != NL80211_IFTYPE_STATION ||
-           !sdata->u.mgd.associated)
-               return -EINVAL;
-
-       tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n",
-                action_code, peer);
-
-       skb = dev_alloc_skb(local->hw.extra_tx_headroom +
-                           max(sizeof(struct ieee80211_mgmt),
-                               sizeof(struct ieee80211_tdls_data)) +
-                           50 + /* supported rates */
-                           7 + /* ext capab */
-                           extra_ies_len +
-                           sizeof(struct ieee80211_tdls_lnkie));
-       if (!skb)
-               return -ENOMEM;
-
-       skb_reserve(skb, local->hw.extra_tx_headroom);
-
-       switch (action_code) {
-       case WLAN_TDLS_SETUP_REQUEST:
-       case WLAN_TDLS_SETUP_RESPONSE:
-       case WLAN_TDLS_SETUP_CONFIRM:
-       case WLAN_TDLS_TEARDOWN:
-       case WLAN_TDLS_DISCOVERY_REQUEST:
-               ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer,
-                                                    action_code, dialog_token,
-                                                    status_code, skb);
-               send_direct = false;
-               break;
-       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
-               ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code,
-                                                dialog_token, status_code,
-                                                skb);
-               send_direct = true;
-               break;
-       default:
-               ret = -ENOTSUPP;
-               break;
-       }
-
-       if (ret < 0)
-               goto fail;
-
-       if (extra_ies_len)
-               memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
-
-       /* the TDLS link IE is always added last */
-       switch (action_code) {
-       case WLAN_TDLS_SETUP_REQUEST:
-       case WLAN_TDLS_SETUP_CONFIRM:
-       case WLAN_TDLS_TEARDOWN:
-       case WLAN_TDLS_DISCOVERY_REQUEST:
-               /* we are the initiator */
-               ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer,
-                                          sdata->u.mgd.bssid);
-               break;
-       case WLAN_TDLS_SETUP_RESPONSE:
-       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
-               /* we are the responder */
-               ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr,
-                                          sdata->u.mgd.bssid);
-               break;
-       default:
-               ret = -ENOTSUPP;
-               goto fail;
-       }
-
-       if (send_direct) {
-               ieee80211_tx_skb(sdata, skb);
-               return 0;
-       }
-
-       /*
-        * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise
-        * we should default to AC_VI.
-        */
-       switch (action_code) {
-       case WLAN_TDLS_SETUP_REQUEST:
-       case WLAN_TDLS_SETUP_RESPONSE:
-               skb_set_queue_mapping(skb, IEEE80211_AC_BK);
-               skb->priority = 2;
-               break;
-       default:
-               skb_set_queue_mapping(skb, IEEE80211_AC_VI);
-               skb->priority = 5;
-               break;
-       }
-
-       /* disable bottom halves when entering the Tx path */
-       local_bh_disable();
-       ret = ieee80211_subif_start_xmit(skb, dev);
-       local_bh_enable();
-
-       return ret;
-
-fail:
-       dev_kfree_skb(skb);
-       return ret;
-}
-
-static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *peer, enum nl80211_tdls_operation oper)
-{
-       struct sta_info *sta;
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
-       if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
-               return -ENOTSUPP;
-
-       if (sdata->vif.type != NL80211_IFTYPE_STATION)
-               return -EINVAL;
-
-       tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer);
-
-       switch (oper) {
-       case NL80211_TDLS_ENABLE_LINK:
-               rcu_read_lock();
-               sta = sta_info_get(sdata, peer);
-               if (!sta) {
-                       rcu_read_unlock();
-                       return -ENOLINK;
-               }
-
-               set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
-               rcu_read_unlock();
-               break;
-       case NL80211_TDLS_DISABLE_LINK:
-               return sta_info_destroy_addr(sdata, peer);
-       case NL80211_TDLS_TEARDOWN:
-       case NL80211_TDLS_SETUP:
-       case NL80211_TDLS_DISCOVERY_REQ:
-               /* We don't support in-driver setup/teardown/discovery */
-               return -ENOTSUPP;
-       default:
-               return -ENOTSUPP;
-       }
-
-       return 0;
-}
-
 static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
                                  const u8 *peer, u64 *cookie)
 {
@@ -3949,6 +3766,21 @@ static int ieee80211_set_qos_map(struct wiphy *wiphy,
        return 0;
 }
 
+static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy,
+                                     struct net_device *dev,
+                                     struct cfg80211_chan_def *chandef)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       int ret;
+       u32 changed = 0;
+
+       ret = ieee80211_vif_change_bandwidth(sdata, chandef, &changed);
+       if (ret == 0)
+               ieee80211_bss_info_change_notify(sdata, changed);
+
+       return ret;
+}
+
 const struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
@@ -4029,4 +3861,5 @@ const struct cfg80211_ops mac80211_config_ops = {
        .start_radar_detection = ieee80211_start_radar_detection,
        .channel_switch = ieee80211_channel_switch,
        .set_qos_map = ieee80211_set_qos_map,
+       .set_ap_chanwidth = ieee80211_set_ap_chanwidth,
 };
index 75b5dd2c9267f10e8cb0e5680c3aa11c94a5dbe4..a310e33972de8881bf4dd71bdff36d55fa966226 100644 (file)
@@ -9,6 +9,170 @@
 #include "ieee80211_i.h"
 #include "driver-ops.h"
 
+static int ieee80211_chanctx_num_assigned(struct ieee80211_local *local,
+                                         struct ieee80211_chanctx *ctx)
+{
+       struct ieee80211_sub_if_data *sdata;
+       int num = 0;
+
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       list_for_each_entry(sdata, &ctx->assigned_vifs, assigned_chanctx_list)
+               num++;
+
+       return num;
+}
+
+static int ieee80211_chanctx_num_reserved(struct ieee80211_local *local,
+                                         struct ieee80211_chanctx *ctx)
+{
+       struct ieee80211_sub_if_data *sdata;
+       int num = 0;
+
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       list_for_each_entry(sdata, &ctx->reserved_vifs, reserved_chanctx_list)
+               num++;
+
+       return num;
+}
+
+int ieee80211_chanctx_refcount(struct ieee80211_local *local,
+                              struct ieee80211_chanctx *ctx)
+{
+       return ieee80211_chanctx_num_assigned(local, ctx) +
+              ieee80211_chanctx_num_reserved(local, ctx);
+}
+
+static int ieee80211_num_chanctx(struct ieee80211_local *local)
+{
+       struct ieee80211_chanctx *ctx;
+       int num = 0;
+
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       list_for_each_entry(ctx, &local->chanctx_list, list)
+               num++;
+
+       return num;
+}
+
+static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local)
+{
+       lockdep_assert_held(&local->chanctx_mtx);
+       return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local);
+}
+
+static const struct cfg80211_chan_def *
+ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local,
+                                  struct ieee80211_chanctx *ctx,
+                                  const struct cfg80211_chan_def *compat)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       list_for_each_entry(sdata, &ctx->reserved_vifs,
+                           reserved_chanctx_list) {
+               if (!compat)
+                       compat = &sdata->reserved_chandef;
+
+               compat = cfg80211_chandef_compatible(&sdata->reserved_chandef,
+                                                    compat);
+               if (!compat)
+                       break;
+       }
+
+       return compat;
+}
+
+static const struct cfg80211_chan_def *
+ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local,
+                                      struct ieee80211_chanctx *ctx,
+                                      const struct cfg80211_chan_def *compat)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       list_for_each_entry(sdata, &ctx->assigned_vifs,
+                           assigned_chanctx_list) {
+               if (sdata->reserved_chanctx != NULL)
+                       continue;
+
+               if (!compat)
+                       compat = &sdata->vif.bss_conf.chandef;
+
+               compat = cfg80211_chandef_compatible(
+                               &sdata->vif.bss_conf.chandef, compat);
+               if (!compat)
+                       break;
+       }
+
+       return compat;
+}
+
+static const struct cfg80211_chan_def *
+ieee80211_chanctx_combined_chandef(struct ieee80211_local *local,
+                                  struct ieee80211_chanctx *ctx,
+                                  const struct cfg80211_chan_def *compat)
+{
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       compat = ieee80211_chanctx_reserved_chandef(local, ctx, compat);
+       if (!compat)
+               return NULL;
+
+       compat = ieee80211_chanctx_non_reserved_chandef(local, ctx, compat);
+       if (!compat)
+               return NULL;
+
+       return compat;
+}
+
+static bool
+ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local *local,
+                                     struct ieee80211_chanctx *ctx,
+                                     const struct cfg80211_chan_def *def)
+{
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       if (ieee80211_chanctx_combined_chandef(local, ctx, def))
+               return true;
+
+       if (!list_empty(&ctx->reserved_vifs) &&
+           ieee80211_chanctx_reserved_chandef(local, ctx, def))
+               return true;
+
+       return false;
+}
+
+static struct ieee80211_chanctx *
+ieee80211_find_reservation_chanctx(struct ieee80211_local *local,
+                                  const struct cfg80211_chan_def *chandef,
+                                  enum ieee80211_chanctx_mode mode)
+{
+       struct ieee80211_chanctx *ctx;
+
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
+               return NULL;
+
+       list_for_each_entry(ctx, &local->chanctx_list, list) {
+               if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
+                       continue;
+
+               if (!ieee80211_chanctx_can_reserve_chandef(local, ctx,
+                                                          chandef))
+                       continue;
+
+               return ctx;
+       }
+
+       return NULL;
+}
+
 static enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta)
 {
        switch (sta->bandwidth) {
@@ -190,6 +354,11 @@ ieee80211_find_chanctx(struct ieee80211_local *local,
                if (!compat)
                        continue;
 
+               compat = ieee80211_chanctx_reserved_chandef(local, ctx,
+                                                           compat);
+               if (!compat)
+                       continue;
+
                ieee80211_change_chanctx(local, ctx, compat);
 
                return ctx;
@@ -217,62 +386,91 @@ static bool ieee80211_is_radar_required(struct ieee80211_local *local)
 }
 
 static struct ieee80211_chanctx *
-ieee80211_new_chanctx(struct ieee80211_local *local,
-                     const struct cfg80211_chan_def *chandef,
-                     enum ieee80211_chanctx_mode mode)
+ieee80211_alloc_chanctx(struct ieee80211_local *local,
+                       const struct cfg80211_chan_def *chandef,
+                       enum ieee80211_chanctx_mode mode)
 {
        struct ieee80211_chanctx *ctx;
-       u32 changed;
-       int err;
 
        lockdep_assert_held(&local->chanctx_mtx);
 
        ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
        if (!ctx)
-               return ERR_PTR(-ENOMEM);
+               return NULL;
 
+       INIT_LIST_HEAD(&ctx->assigned_vifs);
+       INIT_LIST_HEAD(&ctx->reserved_vifs);
        ctx->conf.def = *chandef;
        ctx->conf.rx_chains_static = 1;
        ctx->conf.rx_chains_dynamic = 1;
        ctx->mode = mode;
        ctx->conf.radar_enabled = ieee80211_is_radar_required(local);
        ieee80211_recalc_chanctx_min_def(local, ctx);
+
+       return ctx;
+}
+
+static int ieee80211_add_chanctx(struct ieee80211_local *local,
+                                struct ieee80211_chanctx *ctx)
+{
+       u32 changed;
+       int err;
+
+       lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
+
        if (!local->use_chanctx)
                local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
 
-       /* we hold the mutex to prevent idle from changing */
-       lockdep_assert_held(&local->mtx);
        /* turn idle off *before* setting channel -- some drivers need that */
        changed = ieee80211_idle_off(local);
        if (changed)
                ieee80211_hw_config(local, changed);
 
        if (!local->use_chanctx) {
-               local->_oper_chandef = *chandef;
+               local->_oper_chandef = ctx->conf.def;
                ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
        } else {
                err = drv_add_chanctx(local, ctx);
                if (err) {
-                       kfree(ctx);
                        ieee80211_recalc_idle(local);
-                       return ERR_PTR(err);
+                       return err;
                }
        }
 
-       /* and keep the mutex held until the new chanctx is on the list */
-       list_add_rcu(&ctx->list, &local->chanctx_list);
+       return 0;
+}
 
+static struct ieee80211_chanctx *
+ieee80211_new_chanctx(struct ieee80211_local *local,
+                     const struct cfg80211_chan_def *chandef,
+                     enum ieee80211_chanctx_mode mode)
+{
+       struct ieee80211_chanctx *ctx;
+       int err;
+
+       lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       ctx = ieee80211_alloc_chanctx(local, chandef, mode);
+       if (!ctx)
+               return ERR_PTR(-ENOMEM);
+
+       err = ieee80211_add_chanctx(local, ctx);
+       if (err) {
+               kfree(ctx);
+               return ERR_PTR(err);
+       }
+
+       list_add_rcu(&ctx->list, &local->chanctx_list);
        return ctx;
 }
 
-static void ieee80211_free_chanctx(struct ieee80211_local *local,
-                                  struct ieee80211_chanctx *ctx)
+static void ieee80211_del_chanctx(struct ieee80211_local *local,
+                                 struct ieee80211_chanctx *ctx)
 {
-       bool check_single_channel = false;
        lockdep_assert_held(&local->chanctx_mtx);
 
-       WARN_ON_ONCE(ctx->refcount != 0);
-
        if (!local->use_chanctx) {
                struct cfg80211_chan_def *chandef = &local->_oper_chandef;
                chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
@@ -282,8 +480,9 @@ static void ieee80211_free_chanctx(struct ieee80211_local *local,
                /* NOTE: Disabling radar is only valid here for
                 * single channel context. To be sure, check it ...
                 */
-               if (local->hw.conf.radar_enabled)
-                       check_single_channel = true;
+               WARN_ON(local->hw.conf.radar_enabled &&
+                       !list_empty(&local->chanctx_list));
+
                local->hw.conf.radar_enabled = false;
 
                ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
@@ -291,39 +490,19 @@ static void ieee80211_free_chanctx(struct ieee80211_local *local,
                drv_remove_chanctx(local, ctx);
        }
 
-       list_del_rcu(&ctx->list);
-       kfree_rcu(ctx, rcu_head);
-
-       /* throw a warning if this wasn't the only channel context. */
-       WARN_ON(check_single_channel && !list_empty(&local->chanctx_list));
-
        ieee80211_recalc_idle(local);
 }
 
-static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
-                                       struct ieee80211_chanctx *ctx)
+static void ieee80211_free_chanctx(struct ieee80211_local *local,
+                                  struct ieee80211_chanctx *ctx)
 {
-       struct ieee80211_local *local = sdata->local;
-       int ret;
-
        lockdep_assert_held(&local->chanctx_mtx);
 
-       ret = drv_assign_vif_chanctx(local, sdata, ctx);
-       if (ret)
-               return ret;
+       WARN_ON_ONCE(ieee80211_chanctx_refcount(local, ctx) != 0);
 
-       rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
-       ctx->refcount++;
-
-       ieee80211_recalc_txpower(sdata);
-       ieee80211_recalc_chanctx_min_def(local, ctx);
-       sdata->vif.bss_conf.idle = false;
-
-       if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
-           sdata->vif.type != NL80211_IFTYPE_MONITOR)
-               ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
-
-       return 0;
+       list_del_rcu(&ctx->list);
+       ieee80211_del_chanctx(local, ctx);
+       kfree_rcu(ctx, rcu_head);
 }
 
 static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
@@ -384,30 +563,58 @@ static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
        drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);
 }
 
-static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
-                                          struct ieee80211_chanctx *ctx)
+static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
+                                       struct ieee80211_chanctx *new_ctx)
 {
        struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx_conf *conf;
+       struct ieee80211_chanctx *curr_ctx = NULL;
+       int ret = 0;
 
-       lockdep_assert_held(&local->chanctx_mtx);
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
 
-       ctx->refcount--;
-       rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
+       if (conf) {
+               curr_ctx = container_of(conf, struct ieee80211_chanctx, conf);
 
-       sdata->vif.bss_conf.idle = true;
+               drv_unassign_vif_chanctx(local, sdata, curr_ctx);
+               conf = NULL;
+               list_del(&sdata->assigned_chanctx_list);
+       }
 
-       if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
-           sdata->vif.type != NL80211_IFTYPE_MONITOR)
-               ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
+       if (new_ctx) {
+               ret = drv_assign_vif_chanctx(local, sdata, new_ctx);
+               if (ret)
+                       goto out;
 
-       drv_unassign_vif_chanctx(local, sdata, ctx);
+               conf = &new_ctx->conf;
+               list_add(&sdata->assigned_chanctx_list,
+                        &new_ctx->assigned_vifs);
+       }
+
+out:
+       rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
+
+       sdata->vif.bss_conf.idle = !conf;
+
+       if (curr_ctx && ieee80211_chanctx_num_assigned(local, curr_ctx) > 0) {
+               ieee80211_recalc_chanctx_chantype(local, curr_ctx);
+               ieee80211_recalc_smps_chanctx(local, curr_ctx);
+               ieee80211_recalc_radar_chanctx(local, curr_ctx);
+               ieee80211_recalc_chanctx_min_def(local, curr_ctx);
+       }
 
-       if (ctx->refcount > 0) {
-               ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
-               ieee80211_recalc_smps_chanctx(local, ctx);
-               ieee80211_recalc_radar_chanctx(local, ctx);
-               ieee80211_recalc_chanctx_min_def(local, ctx);
+       if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) {
+               ieee80211_recalc_txpower(sdata);
+               ieee80211_recalc_chanctx_min_def(local, new_ctx);
        }
+
+       if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
+           sdata->vif.type != NL80211_IFTYPE_MONITOR)
+               ieee80211_bss_info_change_notify(sdata,
+                                                BSS_CHANGED_IDLE);
+
+       return ret;
 }
 
 static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
@@ -425,8 +632,11 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
 
        ctx = container_of(conf, struct ieee80211_chanctx, conf);
 
-       ieee80211_unassign_vif_chanctx(sdata, ctx);
-       if (ctx->refcount == 0)
+       if (sdata->reserved_chanctx)
+               ieee80211_vif_unreserve_chanctx(sdata);
+
+       ieee80211_assign_vif_chanctx(sdata, NULL);
+       if (ieee80211_chanctx_refcount(local, ctx) == 0)
                ieee80211_free_chanctx(local, ctx);
 }
 
@@ -526,6 +736,7 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx *ctx;
+       u8 radar_detect_width = 0;
        int ret;
 
        lockdep_assert_held(&local->mtx);
@@ -533,6 +744,22 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
        WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
 
        mutex_lock(&local->chanctx_mtx);
+
+       ret = cfg80211_chandef_dfs_required(local->hw.wiphy,
+                                           chandef,
+                                           sdata->wdev.iftype);
+       if (ret < 0)
+               goto out;
+       if (ret > 0)
+               radar_detect_width = BIT(chandef->width);
+
+       sdata->radar_required = ret;
+
+       ret = ieee80211_check_combinations(sdata, chandef, mode,
+                                          radar_detect_width);
+       if (ret < 0)
+               goto out;
+
        __ieee80211_vif_release_channel(sdata);
 
        ctx = ieee80211_find_chanctx(local, chandef, mode);
@@ -548,7 +775,7 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
        ret = ieee80211_assign_vif_chanctx(sdata, ctx);
        if (ret) {
                /* if assign fails refcount stays the same */
-               if (ctx->refcount == 0)
+               if (ieee80211_chanctx_refcount(local, ctx) == 0)
                        ieee80211_free_chanctx(local, ctx);
                goto out;
        }
@@ -560,15 +787,47 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
        return ret;
 }
 
+static int __ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
+                                         struct ieee80211_chanctx *ctx,
+                                         u32 *changed)
+{
+       struct ieee80211_local *local = sdata->local;
+       const struct cfg80211_chan_def *chandef = &sdata->csa_chandef;
+       u32 chanctx_changed = 0;
+
+       if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
+                                    IEEE80211_CHAN_DISABLED))
+               return -EINVAL;
+
+       if (ieee80211_chanctx_refcount(local, ctx) != 1)
+               return -EINVAL;
+
+       if (sdata->vif.bss_conf.chandef.width != chandef->width) {
+               chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
+               *changed |= BSS_CHANGED_BANDWIDTH;
+       }
+
+       sdata->vif.bss_conf.chandef = *chandef;
+       ctx->conf.def = *chandef;
+
+       chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
+       drv_change_chanctx(local, ctx, chanctx_changed);
+
+       ieee80211_recalc_chanctx_chantype(local, ctx);
+       ieee80211_recalc_smps_chanctx(local, ctx);
+       ieee80211_recalc_radar_chanctx(local, ctx);
+       ieee80211_recalc_chanctx_min_def(local, ctx);
+
+       return 0;
+}
+
 int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
                                 u32 *changed)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx_conf *conf;
        struct ieee80211_chanctx *ctx;
-       const struct cfg80211_chan_def *chandef = &sdata->csa_chandef;
        int ret;
-       u32 chanctx_changed = 0;
 
        lockdep_assert_held(&local->mtx);
 
@@ -576,11 +835,94 @@ int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
        if (WARN_ON(!sdata->vif.csa_active))
                return -EINVAL;
 
-       if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
-                                    IEEE80211_CHAN_DISABLED))
+       mutex_lock(&local->chanctx_mtx);
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
+       if (!conf) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ctx = container_of(conf, struct ieee80211_chanctx, conf);
+
+       ret = __ieee80211_vif_change_channel(sdata, ctx, changed);
+ out:
+       mutex_unlock(&local->chanctx_mtx);
+       return ret;
+}
+
+static void
+__ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
+                                     bool clear)
+{
+       struct ieee80211_local *local __maybe_unused = sdata->local;
+       struct ieee80211_sub_if_data *vlan;
+       struct ieee80211_chanctx_conf *conf;
+
+       if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
+               return;
+
+       lockdep_assert_held(&local->mtx);
+
+       /* Check that conf exists, even when clearing this function
+        * must be called with the AP's channel context still there
+        * as it would otherwise cause VLANs to have an invalid
+        * channel context pointer for a while, possibly pointing
+        * to a channel context that has already been freed.
+        */
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
+       WARN_ON(!conf);
+
+       if (clear)
+               conf = NULL;
+
+       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+               rcu_assign_pointer(vlan->vif.chanctx_conf, conf);
+}
+
+void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
+                                        bool clear)
+{
+       struct ieee80211_local *local = sdata->local;
+
+       mutex_lock(&local->chanctx_mtx);
+
+       __ieee80211_vif_copy_chanctx_to_vlans(sdata, clear);
+
+       mutex_unlock(&local->chanctx_mtx);
+}
+
+int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_chanctx *ctx = sdata->reserved_chanctx;
+
+       lockdep_assert_held(&sdata->local->chanctx_mtx);
+
+       if (WARN_ON(!ctx))
                return -EINVAL;
 
+       list_del(&sdata->reserved_chanctx_list);
+       sdata->reserved_chanctx = NULL;
+
+       if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0)
+               ieee80211_free_chanctx(sdata->local, ctx);
+
+       return 0;
+}
+
+int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
+                                 const struct cfg80211_chan_def *chandef,
+                                 enum ieee80211_chanctx_mode mode,
+                                 bool radar_required)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx_conf *conf;
+       struct ieee80211_chanctx *new_ctx, *curr_ctx;
+       int ret = 0;
+
        mutex_lock(&local->chanctx_mtx);
+
        conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
                                         lockdep_is_held(&local->chanctx_mtx));
        if (!conf) {
@@ -588,30 +930,108 @@ int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
                goto out;
        }
 
-       ctx = container_of(conf, struct ieee80211_chanctx, conf);
-       if (ctx->refcount != 1) {
+       curr_ctx = container_of(conf, struct ieee80211_chanctx, conf);
+
+       new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode);
+       if (!new_ctx) {
+               if (ieee80211_chanctx_refcount(local, curr_ctx) == 1 &&
+                   (local->hw.flags & IEEE80211_HW_CHANGE_RUNNING_CHANCTX)) {
+                       /* if we're the only users of the chanctx and
+                        * the driver supports changing a running
+                        * context, reserve our current context
+                        */
+                       new_ctx = curr_ctx;
+               } else if (ieee80211_can_create_new_chanctx(local)) {
+                       /* create a new context and reserve it */
+                       new_ctx = ieee80211_new_chanctx(local, chandef, mode);
+                       if (IS_ERR(new_ctx)) {
+                               ret = PTR_ERR(new_ctx);
+                               goto out;
+                       }
+               } else {
+                       ret = -EBUSY;
+                       goto out;
+               }
+       }
+
+       list_add(&sdata->reserved_chanctx_list, &new_ctx->reserved_vifs);
+       sdata->reserved_chanctx = new_ctx;
+       sdata->reserved_chandef = *chandef;
+       sdata->reserved_radar_required = radar_required;
+out:
+       mutex_unlock(&local->chanctx_mtx);
+       return ret;
+}
+
+int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata,
+                                      u32 *changed)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx *ctx;
+       struct ieee80211_chanctx *old_ctx;
+       struct ieee80211_chanctx_conf *conf;
+       int ret;
+       u32 tmp_changed = *changed;
+
+       /* TODO: need to recheck if the chandef is usable etc.? */
+
+       lockdep_assert_held(&local->mtx);
+
+       mutex_lock(&local->chanctx_mtx);
+
+       ctx = sdata->reserved_chanctx;
+       if (WARN_ON(!ctx)) {
                ret = -EINVAL;
                goto out;
        }
 
-       if (sdata->vif.bss_conf.chandef.width != chandef->width) {
-               chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
-               *changed |= BSS_CHANGED_BANDWIDTH;
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
+       if (!conf) {
+               ret = -EINVAL;
+               goto out;
        }
 
-       sdata->vif.bss_conf.chandef = *chandef;
-       ctx->conf.def = *chandef;
+       old_ctx = container_of(conf, struct ieee80211_chanctx, conf);
 
-       chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
-       drv_change_chanctx(local, ctx, chanctx_changed);
+       if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width)
+               tmp_changed |= BSS_CHANGED_BANDWIDTH;
+
+       sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
+
+       /* unref our reservation */
+       sdata->reserved_chanctx = NULL;
+       sdata->radar_required = sdata->reserved_radar_required;
+       list_del(&sdata->reserved_chanctx_list);
+
+       if (old_ctx == ctx) {
+               /* This is our own context, just change it */
+               ret = __ieee80211_vif_change_channel(sdata, old_ctx,
+                                                    &tmp_changed);
+               if (ret)
+                       goto out;
+       } else {
+               ret = ieee80211_assign_vif_chanctx(sdata, ctx);
+               if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
+                       ieee80211_free_chanctx(local, old_ctx);
+               if (ret) {
+                       /* if assign fails refcount stays the same */
+                       if (ieee80211_chanctx_refcount(local, ctx) == 0)
+                               ieee80211_free_chanctx(local, ctx);
+                       goto out;
+               }
+
+               if (sdata->vif.type == NL80211_IFTYPE_AP)
+                       __ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
+       }
+
+       *changed = tmp_changed;
 
        ieee80211_recalc_chanctx_chantype(local, ctx);
        ieee80211_recalc_smps_chanctx(local, ctx);
        ieee80211_recalc_radar_chanctx(local, ctx);
        ieee80211_recalc_chanctx_min_def(local, ctx);
-
-       ret = 0;
- out:
+out:
        mutex_unlock(&local->chanctx_mtx);
        return ret;
 }
@@ -695,40 +1115,6 @@ void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata)
        mutex_unlock(&local->chanctx_mtx);
 }
 
-void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
-                                        bool clear)
-{
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_sub_if_data *vlan;
-       struct ieee80211_chanctx_conf *conf;
-
-       ASSERT_RTNL();
-
-       if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
-               return;
-
-       mutex_lock(&local->chanctx_mtx);
-
-       /*
-        * Check that conf exists, even when clearing this function
-        * must be called with the AP's channel context still there
-        * as it would otherwise cause VLANs to have an invalid
-        * channel context pointer for a while, possibly pointing
-        * to a channel context that has already been freed.
-        */
-       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
-                               lockdep_is_held(&local->chanctx_mtx));
-       WARN_ON(!conf);
-
-       if (clear)
-               conf = NULL;
-
-       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
-               rcu_assign_pointer(vlan->vif.chanctx_conf, conf);
-
-       mutex_unlock(&local->chanctx_mtx);
-}
-
 void ieee80211_iter_chan_contexts_atomic(
        struct ieee80211_hw *hw,
        void (*iter)(struct ieee80211_hw *hw,
index fa16e54980a1d3e76ce2f85fcb3253eb2599e838..0e963bc1ceac3109f378431a617b853a7166df37 100644 (file)
@@ -128,7 +128,7 @@ static ssize_t sta_tx_latency_stat_write(struct file *file,
        if (!strcmp(buf, TX_LATENCY_DISABLED)) {
                if (!tx_latency)
                        goto unlock;
-               rcu_assign_pointer(local->tx_latency, NULL);
+               RCU_INIT_POINTER(local->tx_latency, NULL);
                synchronize_rcu();
                kfree(tx_latency);
                goto unlock;
index 214ed4ecd739f10ae201e6dfa9112c0dd943f5b8..60c35afee29d551727a2969b25b84998ca5501c4 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef __MAC80211_DEBUGFS_H
 #define __MAC80211_DEBUGFS_H
 
+#include "ieee80211_i.h"
+
 #ifdef CONFIG_MAC80211_DEBUGFS
 void debugfs_hw_add(struct ieee80211_local *local);
 int __printf(4, 5) mac80211_format_buffer(char __user *userbuf, size_t count,
index 40a648938985db178b6997f1541bb47a3247b5a8..e205ebabfa5015f5375a60cb79af9b2bfdbd9f80 100644 (file)
@@ -34,8 +34,7 @@ static ssize_t ieee80211_if_read(
        ssize_t ret = -EINVAL;
 
        read_lock(&dev_base_lock);
-       if (sdata->dev->reg_state == NETREG_REGISTERED)
-               ret = (*format)(sdata, buf, sizeof(buf));
+       ret = (*format)(sdata, buf, sizeof(buf));
        read_unlock(&dev_base_lock);
 
        if (ret >= 0)
@@ -62,8 +61,7 @@ static ssize_t ieee80211_if_write(
 
        ret = -ENODEV;
        rtnl_lock();
-       if (sdata->dev->reg_state == NETREG_REGISTERED)
-               ret = (*write)(sdata, buf, count);
+       ret = (*write)(sdata, buf, count);
        rtnl_unlock();
 
        return ret;
index 79025e79f4d6459dd99de5ad496e351e123f53b7..9f5501a9a79506266decdb83d0ad78c9d6e9bdf9 100644 (file)
@@ -3,6 +3,8 @@
 #ifndef __IEEE80211_DEBUGFS_NETDEV_H
 #define __IEEE80211_DEBUGFS_NETDEV_H
 
+#include "ieee80211_i.h"
+
 #ifdef CONFIG_MAC80211_DEBUGFS
 void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata);
 void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata);
index fc689f5d971e259381f0a26e13079f1a727fe704..bd782dcffcc7b81478277bcb2b5545dfef545da8 100644 (file)
@@ -5,11 +5,11 @@
 #include "ieee80211_i.h"
 #include "trace.h"
 
-static inline void check_sdata_in_driver(struct ieee80211_sub_if_data *sdata)
+static inline bool check_sdata_in_driver(struct ieee80211_sub_if_data *sdata)
 {
-       WARN(!(sdata->flags & IEEE80211_SDATA_IN_DRIVER),
-            "%s:  Failed check-sdata-in-driver check, flags: 0x%x\n",
-            sdata->dev ? sdata->dev->name : sdata->name, sdata->flags);
+       return !WARN(!(sdata->flags & IEEE80211_SDATA_IN_DRIVER),
+                    "%s:  Failed check-sdata-in-driver check, flags: 0x%x\n",
+                    sdata->dev ? sdata->dev->name : sdata->name, sdata->flags);
 }
 
 static inline struct ieee80211_sub_if_data *
@@ -168,7 +168,8 @@ static inline int drv_change_interface(struct ieee80211_local *local,
 
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_change_interface(local, sdata, type, p2p);
        ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p);
@@ -181,7 +182,8 @@ static inline void drv_remove_interface(struct ieee80211_local *local,
 {
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_remove_interface(local, sdata);
        local->ops->remove_interface(&local->hw, &sdata->vif);
@@ -219,7 +221,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
                         sdata->vif.type == NL80211_IFTYPE_MONITOR))
                return;
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_bss_info_changed(local, sdata, info, changed);
        if (local->ops->bss_info_changed)
@@ -278,7 +281,8 @@ static inline int drv_set_key(struct ieee80211_local *local,
        might_sleep();
 
        sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_set_key(local, cmd, sdata, sta, key);
        ret = local->ops->set_key(&local->hw, cmd, &sdata->vif, sta, key);
@@ -298,7 +302,8 @@ static inline void drv_update_tkip_key(struct ieee80211_local *local,
                ista = &sta->sta;
 
        sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_update_tkip_key(local, sdata, conf, ista, iv32);
        if (local->ops->update_tkip_key)
@@ -315,7 +320,8 @@ static inline int drv_hw_scan(struct ieee80211_local *local,
 
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_hw_scan(local, sdata);
        ret = local->ops->hw_scan(&local->hw, &sdata->vif, req);
@@ -328,7 +334,8 @@ static inline void drv_cancel_hw_scan(struct ieee80211_local *local,
 {
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_cancel_hw_scan(local, sdata);
        local->ops->cancel_hw_scan(&local->hw, &sdata->vif);
@@ -345,7 +352,8 @@ drv_sched_scan_start(struct ieee80211_local *local,
 
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_sched_scan_start(local, sdata);
        ret = local->ops->sched_scan_start(&local->hw, &sdata->vif,
@@ -361,7 +369,8 @@ static inline int drv_sched_scan_stop(struct ieee80211_local *local,
 
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_sched_scan_stop(local, sdata);
        ret = local->ops->sched_scan_stop(&local->hw, &sdata->vif);
@@ -462,7 +471,8 @@ static inline void drv_sta_notify(struct ieee80211_local *local,
                                  struct ieee80211_sta *sta)
 {
        sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_sta_notify(local, sdata, cmd, sta);
        if (local->ops->sta_notify)
@@ -479,7 +489,8 @@ static inline int drv_sta_add(struct ieee80211_local *local,
        might_sleep();
 
        sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_sta_add(local, sdata, sta);
        if (local->ops->sta_add)
@@ -497,7 +508,8 @@ static inline void drv_sta_remove(struct ieee80211_local *local,
        might_sleep();
 
        sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_sta_remove(local, sdata, sta);
        if (local->ops->sta_remove)
@@ -515,7 +527,8 @@ static inline void drv_sta_add_debugfs(struct ieee80211_local *local,
        might_sleep();
 
        sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        if (local->ops->sta_add_debugfs)
                local->ops->sta_add_debugfs(&local->hw, &sdata->vif,
@@ -545,7 +558,8 @@ static inline void drv_sta_pre_rcu_remove(struct ieee80211_local *local,
        might_sleep();
 
        sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_sta_pre_rcu_remove(local, sdata, &sta->sta);
        if (local->ops->sta_pre_rcu_remove)
@@ -566,7 +580,8 @@ int drv_sta_state(struct ieee80211_local *local,
        might_sleep();
 
        sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state);
        if (local->ops->sta_state) {
@@ -590,7 +605,8 @@ static inline void drv_sta_rc_update(struct ieee80211_local *local,
                                     struct ieee80211_sta *sta, u32 changed)
 {
        sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED &&
                (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
@@ -612,7 +628,8 @@ static inline int drv_conf_tx(struct ieee80211_local *local,
 
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_conf_tx(local, sdata, ac, params);
        if (local->ops->conf_tx)
@@ -629,7 +646,8 @@ static inline u64 drv_get_tsf(struct ieee80211_local *local,
 
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return ret;
 
        trace_drv_get_tsf(local, sdata);
        if (local->ops->get_tsf)
@@ -644,7 +662,8 @@ static inline void drv_set_tsf(struct ieee80211_local *local,
 {
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_set_tsf(local, sdata, tsf);
        if (local->ops->set_tsf)
@@ -657,7 +676,8 @@ static inline void drv_reset_tsf(struct ieee80211_local *local,
 {
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_reset_tsf(local, sdata);
        if (local->ops->reset_tsf)
@@ -689,7 +709,8 @@ static inline int drv_ampdu_action(struct ieee80211_local *local,
        might_sleep();
 
        sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn, buf_size);
 
@@ -726,13 +747,19 @@ static inline void drv_rfkill_poll(struct ieee80211_local *local)
 }
 
 static inline void drv_flush(struct ieee80211_local *local,
+                            struct ieee80211_sub_if_data *sdata,
                             u32 queues, bool drop)
 {
+       struct ieee80211_vif *vif = sdata ? &sdata->vif : NULL;
+
        might_sleep();
 
+       if (sdata && !check_sdata_in_driver(sdata))
+               return;
+
        trace_drv_flush(local, queues, drop);
        if (local->ops->flush)
-               local->ops->flush(&local->hw, queues, drop);
+               local->ops->flush(&local->hw, vif, queues, drop);
        trace_drv_return_void(local);
 }
 
@@ -848,7 +875,8 @@ static inline int drv_set_bitrate_mask(struct ieee80211_local *local,
 
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_set_bitrate_mask(local, sdata, mask);
        if (local->ops->set_bitrate_mask)
@@ -863,7 +891,8 @@ static inline void drv_set_rekey_data(struct ieee80211_local *local,
                                      struct ieee80211_sub_if_data *sdata,
                                      struct cfg80211_gtk_rekey_data *data)
 {
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_set_rekey_data(local, sdata, data);
        if (local->ops->set_rekey_data)
@@ -931,7 +960,8 @@ static inline void drv_mgd_prepare_tx(struct ieee80211_local *local,
 {
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
        WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION);
 
        trace_drv_mgd_prepare_tx(local, sdata);
@@ -958,6 +988,9 @@ static inline int drv_add_chanctx(struct ieee80211_local *local,
 static inline void drv_remove_chanctx(struct ieee80211_local *local,
                                      struct ieee80211_chanctx *ctx)
 {
+       if (WARN_ON(!ctx->driver_present))
+               return;
+
        trace_drv_remove_chanctx(local, ctx);
        if (local->ops->remove_chanctx)
                local->ops->remove_chanctx(&local->hw, &ctx->conf);
@@ -983,7 +1016,8 @@ static inline int drv_assign_vif_chanctx(struct ieee80211_local *local,
 {
        int ret = 0;
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_assign_vif_chanctx(local, sdata, ctx);
        if (local->ops->assign_vif_chanctx) {
@@ -1001,7 +1035,8 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local,
                                            struct ieee80211_sub_if_data *sdata,
                                            struct ieee80211_chanctx *ctx)
 {
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_unassign_vif_chanctx(local, sdata, ctx);
        if (local->ops->unassign_vif_chanctx) {
@@ -1013,12 +1048,66 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local,
        trace_drv_return_void(local);
 }
 
+static inline int
+drv_switch_vif_chanctx(struct ieee80211_local *local,
+                      struct ieee80211_vif_chanctx_switch *vifs,
+                      int n_vifs,
+                      enum ieee80211_chanctx_switch_mode mode)
+{
+       int ret = 0;
+       int i;
+
+       if (!local->ops->switch_vif_chanctx)
+               return -EOPNOTSUPP;
+
+       for (i = 0; i < n_vifs; i++) {
+               struct ieee80211_chanctx *new_ctx =
+                       container_of(vifs[i].new_ctx,
+                                    struct ieee80211_chanctx,
+                                    conf);
+               struct ieee80211_chanctx *old_ctx =
+                       container_of(vifs[i].old_ctx,
+                                    struct ieee80211_chanctx,
+                                    conf);
+
+               WARN_ON_ONCE(!old_ctx->driver_present);
+               WARN_ON_ONCE((mode == CHANCTX_SWMODE_SWAP_CONTEXTS &&
+                             new_ctx->driver_present) ||
+                            (mode == CHANCTX_SWMODE_REASSIGN_VIF &&
+                             !new_ctx->driver_present));
+       }
+
+       trace_drv_switch_vif_chanctx(local, vifs, n_vifs, mode);
+       ret = local->ops->switch_vif_chanctx(&local->hw,
+                                            vifs, n_vifs, mode);
+       trace_drv_return_int(local, ret);
+
+       if (!ret && mode == CHANCTX_SWMODE_SWAP_CONTEXTS) {
+               for (i = 0; i < n_vifs; i++) {
+                       struct ieee80211_chanctx *new_ctx =
+                               container_of(vifs[i].new_ctx,
+                                            struct ieee80211_chanctx,
+                                            conf);
+                       struct ieee80211_chanctx *old_ctx =
+                               container_of(vifs[i].old_ctx,
+                                            struct ieee80211_chanctx,
+                                            conf);
+
+                       new_ctx->driver_present = true;
+                       old_ctx->driver_present = false;
+               }
+       }
+
+       return ret;
+}
+
 static inline int drv_start_ap(struct ieee80211_local *local,
                               struct ieee80211_sub_if_data *sdata)
 {
        int ret = 0;
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_start_ap(local, sdata, &sdata->vif.bss_conf);
        if (local->ops->start_ap)
@@ -1030,7 +1119,8 @@ static inline int drv_start_ap(struct ieee80211_local *local,
 static inline void drv_stop_ap(struct ieee80211_local *local,
                               struct ieee80211_sub_if_data *sdata)
 {
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_stop_ap(local, sdata);
        if (local->ops->stop_ap)
@@ -1053,7 +1143,8 @@ drv_set_default_unicast_key(struct ieee80211_local *local,
                            struct ieee80211_sub_if_data *sdata,
                            int key_idx)
 {
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        WARN_ON_ONCE(key_idx < -1 || key_idx > 3);
 
@@ -1095,7 +1186,8 @@ static inline int drv_join_ibss(struct ieee80211_local *local,
        int ret = 0;
 
        might_sleep();
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_join_ibss(local, sdata, &sdata->vif.bss_conf);
        if (local->ops->join_ibss)
@@ -1108,7 +1200,8 @@ static inline void drv_leave_ibss(struct ieee80211_local *local,
                                  struct ieee80211_sub_if_data *sdata)
 {
        might_sleep();
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_leave_ibss(local, sdata);
        if (local->ops->leave_ibss)
@@ -1116,4 +1209,17 @@ static inline void drv_leave_ibss(struct ieee80211_local *local,
        trace_drv_return_void(local);
 }
 
+static inline u32 drv_get_expected_throughput(struct ieee80211_local *local,
+                                             struct ieee80211_sta *sta)
+{
+       u32 ret = 0;
+
+       trace_drv_get_expected_throughput(sta);
+       if (local->ops->get_expected_throughput)
+               ret = local->ops->get_expected_throughput(sta);
+       trace_drv_return_u32(local, ret);
+
+       return ret;
+}
+
 #endif /* __MAC80211_DRIVER_OPS */
index c150b68436d78ada5bfbb0825d128d8e89f916e3..15702ff64a4c89fb89541f620b9da7dcccc2a7bd 100644 (file)
@@ -31,6 +31,18 @@ static void __check_htcap_disable(struct ieee80211_ht_cap *ht_capa,
        }
 }
 
+static void __check_htcap_enable(struct ieee80211_ht_cap *ht_capa,
+                                 struct ieee80211_ht_cap *ht_capa_mask,
+                                 struct ieee80211_sta_ht_cap *ht_cap,
+                                 u16 flag)
+{
+       __le16 le_flag = cpu_to_le16(flag);
+
+       if ((ht_capa_mask->cap_info & le_flag) &&
+           (ht_capa->cap_info & le_flag))
+               ht_cap->cap |= flag;
+}
+
 void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
                                     struct ieee80211_sta_ht_cap *ht_cap)
 {
@@ -59,7 +71,7 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
        smask = (u8 *)(&ht_capa_mask->mcs.rx_mask);
 
        /* NOTE:  If you add more over-rides here, update register_hw
-        * ht_capa_mod_msk logic in main.c as well.
+        * ht_capa_mod_mask logic in main.c as well.
         * And, if this method can ever change ht_cap.ht_supported, fix
         * the check in ieee80211_add_ht_ie.
         */
@@ -86,6 +98,14 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
        __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
                              IEEE80211_HT_CAP_MAX_AMSDU);
 
+       /* Allow user to disable LDPC */
+       __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+                             IEEE80211_HT_CAP_LDPC_CODING);
+
+       /* Allow user to enable 40 MHz intolerant bit. */
+       __check_htcap_enable(ht_capa, ht_capa_mask, ht_cap,
+                            IEEE80211_HT_CAP_40MHZ_INTOLERANT);
+
        /* Allow user to decrease AMPDU factor */
        if (ht_capa_mask->ampdu_params_info &
            IEEE80211_HT_AMPDU_PARM_FACTOR) {
index 06d28787945b513e6672457a1e6990da0fd644d8..18ee0a256b1e300d5fc4805d110af12ca1a77308 100644 (file)
@@ -143,7 +143,7 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
                *pos++ = csa_settings->block_tx ? 1 : 0;
                *pos++ = ieee80211_frequency_to_channel(
                                csa_settings->chandef.chan->center_freq);
-               sdata->csa_counter_offset_beacon = (pos - presp->head);
+               sdata->csa_counter_offset_beacon[0] = (pos - presp->head);
                *pos++ = csa_settings->count;
        }
 
@@ -228,7 +228,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        struct beacon_data *presp;
        enum nl80211_bss_scan_width scan_width;
        bool have_higher_than_11mbit;
-       bool radar_required = false;
+       bool radar_required;
        int err;
 
        sdata_assert_lock(sdata);
@@ -253,7 +253,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 
        presp = rcu_dereference_protected(ifibss->presp,
                                          lockdep_is_held(&sdata->wdev.mtx));
-       rcu_assign_pointer(ifibss->presp, NULL);
+       RCU_INIT_POINTER(ifibss->presp, NULL);
        if (presp)
                kfree_rcu(presp, rcu_head);
 
@@ -262,7 +262,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        /* make a copy of the chandef, it could be modified below. */
        chandef = *req_chandef;
        chan = chandef.chan;
-       if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
+       if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef,
+                                    NL80211_IFTYPE_ADHOC)) {
                if (chandef.width == NL80211_CHAN_WIDTH_5 ||
                    chandef.width == NL80211_CHAN_WIDTH_10 ||
                    chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
@@ -274,7 +275,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                chandef.width = NL80211_CHAN_WIDTH_20;
                chandef.center_freq1 = chan->center_freq;
                /* check again for downgraded chandef */
-               if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
+               if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef,
+                                            NL80211_IFTYPE_ADHOC)) {
                        sdata_info(sdata,
                                   "Failed to join IBSS, beacons forbidden\n");
                        return;
@@ -282,21 +284,20 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        }
 
        err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
-                                           &chandef);
+                                           &chandef, NL80211_IFTYPE_ADHOC);
        if (err < 0) {
                sdata_info(sdata,
                           "Failed to join IBSS, invalid chandef\n");
                return;
        }
-       if (err > 0) {
-               if (!ifibss->userspace_handles_dfs) {
-                       sdata_info(sdata,
-                                  "Failed to join IBSS, DFS channel without control program\n");
-                       return;
-               }
-               radar_required = true;
+       if (err > 0 && !ifibss->userspace_handles_dfs) {
+               sdata_info(sdata,
+                          "Failed to join IBSS, DFS channel without control program\n");
+               return;
        }
 
+       radar_required = err;
+
        mutex_lock(&local->mtx);
        if (ieee80211_vif_use_channel(sdata, &chandef,
                                      ifibss->fixed_channel ?
@@ -775,7 +776,8 @@ static void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata)
         * unavailable.
         */
        err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
-                                           &ifibss->chandef);
+                                           &ifibss->chandef,
+                                           NL80211_IFTYPE_ADHOC);
        if (err > 0)
                cfg80211_radar_event(sdata->local->hw.wiphy, &ifibss->chandef,
                                     GFP_ATOMIC);
@@ -861,7 +863,8 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                goto disconnect;
        }
 
-       if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, &params.chandef)) {
+       if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, &params.chandef,
+                                    NL80211_IFTYPE_ADHOC)) {
                sdata_info(sdata,
                           "IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
                           ifibss->bssid,
@@ -873,17 +876,17 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        }
 
        err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
-                                           &params.chandef);
+                                           &params.chandef,
+                                           NL80211_IFTYPE_ADHOC);
        if (err < 0)
                goto disconnect;
-       if (err) {
+       if (err > 0 && !ifibss->userspace_handles_dfs) {
                /* IBSS-DFS only allowed with a control program */
-               if (!ifibss->userspace_handles_dfs)
-                       goto disconnect;
-
-               params.radar_required = true;
+               goto disconnect;
        }
 
+       params.radar_required = err;
+
        if (cfg80211_chandef_identical(&params.chandef,
                                       &sdata->vif.bss_conf.chandef)) {
                ibss_dbg(sdata,
@@ -1636,7 +1639,33 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
        u32 changed = 0;
        u32 rate_flags;
        struct ieee80211_supported_band *sband;
+       enum ieee80211_chanctx_mode chanmode;
+       struct ieee80211_local *local = sdata->local;
+       int radar_detect_width = 0;
        int i;
+       int ret;
+
+       ret = cfg80211_chandef_dfs_required(local->hw.wiphy,
+                                           &params->chandef,
+                                           sdata->wdev.iftype);
+       if (ret < 0)
+               return ret;
+
+       if (ret > 0) {
+               if (!params->userspace_handles_dfs)
+                       return -EINVAL;
+               radar_detect_width = BIT(params->chandef.width);
+       }
+
+       chanmode = (params->channel_fixed && !ret) ?
+               IEEE80211_CHANCTX_SHARED : IEEE80211_CHANCTX_EXCLUSIVE;
+
+       mutex_lock(&local->chanctx_mtx);
+       ret = ieee80211_check_combinations(sdata, &params->chandef, chanmode,
+                                          radar_detect_width);
+       mutex_unlock(&local->chanctx_mtx);
+       if (ret < 0)
+               return ret;
 
        if (params->bssid) {
                memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN);
@@ -1648,10 +1677,11 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
        sdata->u.ibss.control_port = params->control_port;
        sdata->u.ibss.userspace_handles_dfs = params->userspace_handles_dfs;
        sdata->u.ibss.basic_rates = params->basic_rates;
+       sdata->u.ibss.last_scan_completed = jiffies;
 
        /* fix basic_rates if channel does not support these rates */
        rate_flags = ieee80211_chandef_rate_flags(&params->chandef);
-       sband = sdata->local->hw.wiphy->bands[params->chandef.chan->band];
+       sband = local->hw.wiphy->bands[params->chandef.chan->band];
        for (i = 0; i < sband->n_bitrates; i++) {
                if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
                        sdata->u.ibss.basic_rates &= ~BIT(i);
@@ -1700,9 +1730,9 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
        ieee80211_bss_info_change_notify(sdata, changed);
 
        sdata->smps_mode = IEEE80211_SMPS_OFF;
-       sdata->needed_rx_chains = sdata->local->rx_chains;
+       sdata->needed_rx_chains = local->rx_chains;
 
-       ieee80211_queue_work(&sdata->local->hw, &sdata->work);
+       ieee80211_queue_work(&local->hw, &sdata->work);
 
        return 0;
 }
index f169b6ee94ee8d6ee9c6daa5dc047c5f8b1d2740..ac9836e0aab335ddf75295b5ad020d77e0e3dd0a 100644 (file)
@@ -260,7 +260,7 @@ struct ieee80211_if_ap {
 
        /* to be used after channel switch. */
        struct cfg80211_beacon_data *next_beacon;
-       struct list_head vlans;
+       struct list_head vlans; /* write-protected with RTNL and local->mtx */
 
        struct ps_data ps;
        atomic_t num_mcast_sta; /* number of stations receiving multicast */
@@ -276,7 +276,7 @@ struct ieee80211_if_wds {
 };
 
 struct ieee80211_if_vlan {
-       struct list_head list;
+       struct list_head list; /* write-protected with RTNL and local->mtx */
 
        /* used for all tx if the VLAN is configured to 4-addr mode */
        struct sta_info __rcu *sta;
@@ -692,8 +692,10 @@ struct ieee80211_chanctx {
        struct list_head list;
        struct rcu_head rcu_head;
 
+       struct list_head assigned_vifs;
+       struct list_head reserved_vifs;
+
        enum ieee80211_chanctx_mode mode;
-       int refcount;
        bool driver_present;
 
        struct ieee80211_chanctx_conf conf;
@@ -752,11 +754,21 @@ struct ieee80211_sub_if_data {
        struct mac80211_qos_map __rcu *qos_map;
 
        struct work_struct csa_finalize_work;
-       int csa_counter_offset_beacon;
-       int csa_counter_offset_presp;
+       u16 csa_counter_offset_beacon[IEEE80211_MAX_CSA_COUNTERS_NUM];
+       u16 csa_counter_offset_presp[IEEE80211_MAX_CSA_COUNTERS_NUM];
        bool csa_radar_required;
+       bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */
        struct cfg80211_chan_def csa_chandef;
 
+       struct list_head assigned_chanctx_list; /* protected by chanctx_mtx */
+       struct list_head reserved_chanctx_list; /* protected by chanctx_mtx */
+
+       /* context reservation -- protected with chanctx_mtx */
+       struct ieee80211_chanctx *reserved_chanctx;
+       struct cfg80211_chan_def reserved_chandef;
+       bool reserved_radar_required;
+       u8 csa_current_counter;
+
        /* used to reconfigure hardware SM PS */
        struct work_struct recalc_smps;
 
@@ -1449,6 +1461,7 @@ __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
 int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
                                       struct cfg80211_sched_scan_request *req);
 int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata);
+void ieee80211_sched_scan_end(struct ieee80211_local *local);
 void ieee80211_sched_scan_stopped_work(struct work_struct *work);
 
 /* off-channel helpers */
@@ -1463,6 +1476,7 @@ void ieee80211_sw_roc_work(struct work_struct *work);
 void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
 
 /* channel switch handling */
+bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local);
 void ieee80211_csa_finalize_work(struct work_struct *work);
 int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                             struct cfg80211_csa_settings *params);
@@ -1771,6 +1785,16 @@ int __must_check
 ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
                          const struct cfg80211_chan_def *chandef,
                          enum ieee80211_chanctx_mode mode);
+int __must_check
+ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
+                             const struct cfg80211_chan_def *chandef,
+                             enum ieee80211_chanctx_mode mode,
+                             bool radar_required);
+int __must_check
+ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata,
+                                  u32 *changed);
+int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata);
+
 int __must_check
 ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
                               const struct cfg80211_chan_def *chandef,
@@ -1783,6 +1807,8 @@ void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
 void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata);
 void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
                                         bool clear);
+int ieee80211_chanctx_refcount(struct ieee80211_local *local,
+                              struct ieee80211_chanctx *ctx);
 
 void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
                                   struct ieee80211_chanctx *chanctx);
@@ -1806,6 +1832,20 @@ int ieee80211_cs_headroom(struct ieee80211_local *local,
                          enum nl80211_iftype iftype);
 void ieee80211_recalc_dtim(struct ieee80211_local *local,
                           struct ieee80211_sub_if_data *sdata);
+int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
+                                const struct cfg80211_chan_def *chandef,
+                                enum ieee80211_chanctx_mode chanmode,
+                                u8 radar_detect);
+int ieee80211_max_num_channels(struct ieee80211_local *local);
+
+/* TDLS */
+int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
+                       const u8 *peer, u8 action_code, u8 dialog_token,
+                       u16 status_code, u32 peer_capability,
+                       const u8 *extra_ies, size_t extra_ies_len);
+int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
+                       const u8 *peer, enum nl80211_tdls_operation oper);
+
 
 #ifdef CONFIG_MAC80211_NOINLINE
 #define debug_noinline noinline
index b8d331e7d883d50fd4fc3adf12c2869ac3beedb7..388b863e821c6beedbcdb01a602c0d18db701d2f 100644 (file)
@@ -250,6 +250,7 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_sub_if_data *nsdata;
+       int ret;
 
        ASSERT_RTNL();
 
@@ -300,7 +301,10 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
                }
        }
 
-       return 0;
+       mutex_lock(&local->chanctx_mtx);
+       ret = ieee80211_check_combinations(sdata, NULL, 0, 0);
+       mutex_unlock(&local->chanctx_mtx);
+       return ret;
 }
 
 static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata,
@@ -395,6 +399,7 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
        sdata->vif.type = NL80211_IFTYPE_MONITOR;
        snprintf(sdata->name, IFNAMSIZ, "%s-monitor",
                 wiphy_name(local->hw.wiphy));
+       sdata->wdev.iftype = NL80211_IFTYPE_MONITOR;
 
        sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
 
@@ -423,7 +428,7 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
        mutex_unlock(&local->mtx);
        if (ret) {
                mutex_lock(&local->iflist_mtx);
-               rcu_assign_pointer(local->monitor_sdata, NULL);
+               RCU_INIT_POINTER(local->monitor_sdata, NULL);
                mutex_unlock(&local->iflist_mtx);
                synchronize_net();
                drv_remove_interface(local, sdata);
@@ -452,7 +457,7 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local)
                return;
        }
 
-       rcu_assign_pointer(local->monitor_sdata, NULL);
+       RCU_INIT_POINTER(local->monitor_sdata, NULL);
        mutex_unlock(&local->iflist_mtx);
 
        synchronize_net();
@@ -492,7 +497,9 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
                if (!sdata->bss)
                        return -ENOLINK;
 
+               mutex_lock(&local->mtx);
                list_add(&sdata->u.vlan.list, &sdata->bss->vlans);
+               mutex_unlock(&local->mtx);
 
                master = container_of(sdata->bss,
                                      struct ieee80211_sub_if_data, u.ap);
@@ -722,8 +729,11 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
                drv_stop(local);
  err_del_bss:
        sdata->bss = NULL;
-       if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+       if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+               mutex_lock(&local->mtx);
                list_del(&sdata->u.vlan.list);
+               mutex_unlock(&local->mtx);
+       }
        /* might already be clear but that doesn't matter */
        clear_bit(SDATA_STATE_RUNNING, &sdata->state);
        return res;
@@ -829,8 +839,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
 
        cancel_work_sync(&sdata->recalc_smps);
        sdata_lock(sdata);
+       mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
+       if (!ieee80211_csa_needs_block_tx(local))
+               ieee80211_wake_queues_by_reason(&local->hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+       mutex_unlock(&local->mtx);
        sdata_unlock(sdata);
+
        cancel_work_sync(&sdata->csa_finalize_work);
 
        cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
@@ -875,8 +892,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP_VLAN:
+               mutex_lock(&local->mtx);
                list_del(&sdata->u.vlan.list);
-               rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
+               mutex_unlock(&local->mtx);
+               RCU_INIT_POINTER(sdata->vif.chanctx_conf, NULL);
                /* no need to tell driver */
                break;
        case NL80211_IFTYPE_MONITOR:
@@ -895,7 +914,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
                break;
        case NL80211_IFTYPE_P2P_DEVICE:
                /* relies on synchronize_rcu() below */
-               rcu_assign_pointer(local->p2p_sdata, NULL);
+               RCU_INIT_POINTER(local->p2p_sdata, NULL);
                /* fall through */
        default:
                cancel_work_sync(&sdata->work);
@@ -1267,6 +1286,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
        sdata->control_port_protocol = cpu_to_be16(ETH_P_PAE);
        sdata->control_port_no_encrypt = false;
        sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
+       sdata->vif.bss_conf.idle = true;
 
        sdata->noack_map = 0;
 
@@ -1280,6 +1300,8 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
        INIT_WORK(&sdata->work, ieee80211_iface_work);
        INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
        INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work);
+       INIT_LIST_HEAD(&sdata->assigned_chanctx_list);
+       INIT_LIST_HEAD(&sdata->reserved_chanctx_list);
 
        switch (type) {
        case NL80211_IFTYPE_P2P_GO:
@@ -1758,7 +1780,6 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local)
        }
        mutex_unlock(&local->iflist_mtx);
        unregister_netdevice_many(&unreg_list);
-       list_del(&unreg_list);
 
        list_for_each_entry_safe(sdata, tmp, &wdev_list, list) {
                list_del(&sdata->list);
@@ -1774,20 +1795,19 @@ static int netdev_notify(struct notifier_block *nb,
        struct ieee80211_sub_if_data *sdata;
 
        if (state != NETDEV_CHANGENAME)
-               return 0;
+               return NOTIFY_DONE;
 
        if (!dev->ieee80211_ptr || !dev->ieee80211_ptr->wiphy)
-               return 0;
+               return NOTIFY_DONE;
 
        if (dev->ieee80211_ptr->wiphy->privid != mac80211_wiphy_privid)
-               return 0;
+               return NOTIFY_DONE;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
        memcpy(sdata->name, dev->name, IFNAMSIZ);
-
        ieee80211_debugfs_rename_netdev(sdata);
-       return 0;
+
+       return NOTIFY_OK;
 }
 
 static struct notifier_block mac80211_netdev_notifier = {
index 6ff65a1ebaa905ffecfbdedebc7428882aecc551..16d97f044a202e61020f3ef563941e4c9adace0a 100644 (file)
@@ -325,7 +325,8 @@ ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
        struct ieee80211_key *key;
        int i, j, err;
 
-       BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS);
+       if (WARN_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS))
+               return ERR_PTR(-EINVAL);
 
        key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL);
        if (!key)
@@ -481,8 +482,8 @@ int ieee80211_key_link(struct ieee80211_key *key,
        int idx, ret;
        bool pairwise;
 
-       BUG_ON(!sdata);
-       BUG_ON(!key);
+       if (WARN_ON(!sdata || !key))
+               return -EINVAL;
 
        pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE;
        idx = key->conf.keyidx;
index 4c1bf61bc778683dc352ebb32a585d45649168a6..d17c26d6e369f8db71061f3c73f4f27196ed9e3d 100644 (file)
@@ -340,7 +340,7 @@ static int ieee80211_ifa_changed(struct notifier_block *nb,
 
        sdata_unlock(sdata);
 
-       return NOTIFY_DONE;
+       return NOTIFY_OK;
 }
 #endif
 
@@ -371,7 +371,7 @@ static int ieee80211_ifa6_changed(struct notifier_block *nb,
 
        drv_ipv6_addr_change(local, sdata, idev);
 
-       return NOTIFY_DONE;
+       return NOTIFY_OK;
 }
 #endif
 
@@ -446,7 +446,9 @@ static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = {
        .cap_info = cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
                                IEEE80211_HT_CAP_MAX_AMSDU |
                                IEEE80211_HT_CAP_SGI_20 |
-                               IEEE80211_HT_CAP_SGI_40),
+                               IEEE80211_HT_CAP_SGI_40 |
+                               IEEE80211_HT_CAP_LDPC_CODING |
+                               IEEE80211_HT_CAP_40MHZ_INTOLERANT),
        .mcs = {
                .rx_mask = { 0xff, 0xff, 0xff, 0xff, 0xff,
                             0xff, 0xff, 0xff, 0xff, 0xff, },
@@ -954,6 +956,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
        if (local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)
                local->hw.wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
 
+       local->hw.wiphy->max_num_csa_counters = IEEE80211_MAX_CSA_COUNTERS_NUM;
+
        result = wiphy_register(local->hw.wiphy);
        if (result < 0)
                goto fail_wiphy_register;
index f70e9cd10552dac6729d703edd2e9ae12750a3a6..6495a3f0428dae6a93bafea04da26b7390f45599 100644 (file)
@@ -366,20 +366,15 @@ int mesh_add_rsn_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
                return 0;
 
        /* find RSN IE */
-       data = ifmsh->ie;
-       while (data < ifmsh->ie + ifmsh->ie_len) {
-               if (*data == WLAN_EID_RSN) {
-                       len = data[1] + 2;
-                       break;
-               }
-               data++;
-       }
+       data = cfg80211_find_ie(WLAN_EID_RSN, ifmsh->ie, ifmsh->ie_len);
+       if (!data)
+               return 0;
 
-       if (len) {
-               if (skb_tailroom(skb) < len)
-                       return -ENOMEM;
-               memcpy(skb_put(skb, len), data, len);
-       }
+       len = data[1] + 2;
+
+       if (skb_tailroom(skb) < len)
+               return -ENOMEM;
+       memcpy(skb_put(skb, len), data, len);
 
        return 0;
 }
@@ -684,7 +679,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
                *pos++ = 0x0;
                *pos++ = ieee80211_frequency_to_channel(
                                csa->settings.chandef.chan->center_freq);
-               sdata->csa_counter_offset_beacon = hdr_len + 6;
+               sdata->csa_counter_offset_beacon[0] = hdr_len + 6;
                *pos++ = csa->settings.count;
                *pos++ = WLAN_EID_CHAN_SWITCH_PARAM;
                *pos++ = 6;
@@ -829,7 +824,7 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
        bcn = rcu_dereference_protected(ifmsh->beacon,
                                        lockdep_is_held(&sdata->wdev.mtx));
-       rcu_assign_pointer(ifmsh->beacon, NULL);
+       RCU_INIT_POINTER(ifmsh->beacon, NULL);
        kfree_rcu(bcn, rcu_head);
 
        /* flush STAs and mpaths on this iface */
@@ -903,14 +898,15 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
        }
 
        err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
-                                           &params.chandef);
+                                           &params.chandef,
+                                           NL80211_IFTYPE_MESH_POINT);
        if (err < 0)
                return false;
-       if (err) {
-               params.radar_required = true;
+       if (err > 0)
                /* TODO: DFS not (yet) supported */
                return false;
-       }
+
+       params.radar_required = err;
 
        if (cfg80211_chandef_identical(&params.chandef,
                                       &sdata->vif.bss_conf.chandef)) {
@@ -1068,7 +1064,7 @@ int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata)
 
        /* Remove the CSA and MCSP elements from the beacon */
        tmp_csa_settings = rcu_dereference(ifmsh->csa);
-       rcu_assign_pointer(ifmsh->csa, NULL);
+       RCU_INIT_POINTER(ifmsh->csa, NULL);
        if (tmp_csa_settings)
                kfree_rcu(tmp_csa_settings, rcu_head);
        ret = ieee80211_mesh_rebuild_beacon(sdata);
@@ -1102,7 +1098,7 @@ int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
        ret = ieee80211_mesh_rebuild_beacon(sdata);
        if (ret) {
                tmp_csa_settings = rcu_dereference(ifmsh->csa);
-               rcu_assign_pointer(ifmsh->csa, NULL);
+               RCU_INIT_POINTER(ifmsh->csa, NULL);
                kfree_rcu(tmp_csa_settings, rcu_head);
                return ret;
        }
index f9514685d45a54802cb0f21dce0953ccbe9b78f4..94758b9c9ed48a5d1ea9921f4f9f0da40e34bd0b 100644 (file)
@@ -37,7 +37,7 @@ static inline u32 u32_field_get(const u8 *preq_elem, int offset, bool ae)
        return get_unaligned_le32(preq_elem + offset);
 }
 
-static inline u32 u16_field_get(const u8 *preq_elem, int offset, bool ae)
+static inline u16 u16_field_get(const u8 *preq_elem, int offset, bool ae)
 {
        if (ae)
                offset += 6;
@@ -544,9 +544,10 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
                if (time_after(jiffies, ifmsh->last_sn_update +
                                        net_traversal_jiffies(sdata)) ||
                    time_before(jiffies, ifmsh->last_sn_update)) {
-                       target_sn = ++ifmsh->sn;
+                       ++ifmsh->sn;
                        ifmsh->last_sn_update = jiffies;
                }
+               target_sn = ifmsh->sn;
        } else if (is_broadcast_ether_addr(target_addr) &&
                   (target_flags & IEEE80211_PREQ_TO_FLAG)) {
                rcu_read_lock();
index 7d050ed6fe5a4ec879bc9711c088db8360154448..cf032a8db9d78e9c13fe9df1ee2f2c35dbe8ec62 100644 (file)
@@ -287,8 +287,10 @@ static void mesh_path_move_to_queue(struct mesh_path *gate_mpath,
        struct sk_buff_head failq;
        unsigned long flags;
 
-       BUG_ON(gate_mpath == from_mpath);
-       BUG_ON(!gate_mpath->next_hop);
+       if (WARN_ON(gate_mpath == from_mpath))
+               return;
+       if (WARN_ON(!gate_mpath->next_hop))
+               return;
 
        __skb_queue_head_init(&failq);
 
index 2bc5dc25d5adc79a92cd29bc94d058e141d011c6..09625d6205c31418edba53a62ee027245f232050 100644 (file)
@@ -171,7 +171,7 @@ static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata,
        u8 cap;
 
        WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET);
-       BUG_ON(!rcu_read_lock_held());
+       WARN_ON(!rcu_read_lock_held());
        cap = beacon->meshconf->meshconf_cap;
 
        spin_lock_bh(&ifmsh->sync_offset_lock);
index 3b848dad958762ccfc48732623d9ddf1f56aa8ae..0e4886f881f1e49f438fa0f61d5c2b021877bf30 100644 (file)
@@ -11,6 +11,7 @@
 #define MICHAEL_H
 
 #include <linux/types.h>
+#include <linux/ieee80211.h>
 
 #define MICHAEL_MIC_LEN 8
 
index 27600a9808baeaa31be82ecb0f4218225b28665e..3345401be1b3c26744cb2ab6e384672a0cab0d6b 100644 (file)
@@ -975,16 +975,23 @@ static void ieee80211_chswitch_work(struct work_struct *work)
        /* XXX: shouldn't really modify cfg80211-owned data! */
        ifmgd->associated->channel = sdata->csa_chandef.chan;
 
+       ieee80211_bss_info_change_notify(sdata, changed);
+
+       mutex_lock(&local->mtx);
+       sdata->vif.csa_active = false;
        /* XXX: wait for a beacon first? */
-       ieee80211_wake_queues_by_reason(&local->hw,
+       if (!ieee80211_csa_needs_block_tx(local))
+               ieee80211_wake_queues_by_reason(&local->hw,
                                        IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
+       mutex_unlock(&local->mtx);
 
-       ieee80211_bss_info_change_notify(sdata, changed);
-
- out:
-       sdata->vif.csa_active = false;
        ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
+
+       ieee80211_sta_reset_beacon_monitor(sdata);
+       ieee80211_sta_reset_conn_monitor(sdata);
+
+out:
        sdata_unlock(sdata);
 }
 
@@ -1089,7 +1096,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        }
        chanctx = container_of(rcu_access_pointer(sdata->vif.chanctx_conf),
                               struct ieee80211_chanctx, conf);
-       if (chanctx->refcount > 1) {
+       if (ieee80211_chanctx_refcount(local, chanctx) > 1) {
                sdata_info(sdata,
                           "channel switch with multiple interfaces on the same channel, disconnecting\n");
                ieee80211_queue_work(&local->hw,
@@ -1100,12 +1107,16 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        mutex_unlock(&local->chanctx_mtx);
 
        sdata->csa_chandef = csa_ie.chandef;
+
+       mutex_lock(&local->mtx);
        sdata->vif.csa_active = true;
+       sdata->csa_block_tx = csa_ie.mode;
 
-       if (csa_ie.mode)
+       if (sdata->csa_block_tx)
                ieee80211_stop_queues_by_reason(&local->hw,
-                               IEEE80211_MAX_QUEUE_MAP,
-                               IEEE80211_QUEUE_STOP_REASON_CSA);
+                                       IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+       mutex_unlock(&local->mtx);
 
        if (local->ops->channel_switch) {
                /* use driver's channel switch callback */
@@ -1817,6 +1828,12 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        ifmgd->flags = 0;
        mutex_lock(&local->mtx);
        ieee80211_vif_release_channel(sdata);
+
+       sdata->vif.csa_active = false;
+       if (!ieee80211_csa_needs_block_tx(local))
+               ieee80211_wake_queues_by_reason(&local->hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_CSA);
        mutex_unlock(&local->mtx);
 
        sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
@@ -2045,6 +2062,7 @@ EXPORT_SYMBOL(ieee80211_ap_probereq_get);
 
 static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
 {
+       struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
@@ -2058,10 +2076,14 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
                               WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
                               true, frame_buf);
        ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
+
+       mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
-       ieee80211_wake_queues_by_reason(&sdata->local->hw,
+       if (!ieee80211_csa_needs_block_tx(local))
+               ieee80211_wake_queues_by_reason(&local->hw,
                                        IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
+       mutex_unlock(&local->mtx);
 
        cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
                              IEEE80211_DEAUTH_FRAME_LEN);
@@ -3546,6 +3568,9 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
        if (local->quiescing)
                return;
 
+       if (sdata->vif.csa_active)
+               return;
+
        sdata->u.mgd.connection_loss = false;
        ieee80211_queue_work(&sdata->local->hw,
                             &sdata->u.mgd.beacon_connection_loss_work);
@@ -3561,6 +3586,9 @@ static void ieee80211_sta_conn_mon_timer(unsigned long data)
        if (local->quiescing)
                return;
 
+       if (sdata->vif.csa_active)
+               return;
+
        ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);
 }
 
@@ -3707,7 +3735,7 @@ int ieee80211_max_network_latency(struct notifier_block *nb,
        ieee80211_recalc_ps(local, latency_usec);
        mutex_unlock(&local->iflist_mtx);
 
-       return 0;
+       return NOTIFY_OK;
 }
 
 static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
index 26fd94fa0aedb86e43382781f791b1f36de44954..1c1469c36dca05cbdf55e2aba55dc296962aba55 100644 (file)
@@ -657,6 +657,17 @@ minstrel_free(void *priv)
        kfree(priv);
 }
 
+static u32 minstrel_get_expected_throughput(void *priv_sta)
+{
+       struct minstrel_sta_info *mi = priv_sta;
+       int idx = mi->max_tp_rate[0];
+
+       /* convert pkt per sec in kbps (1200 is the average pkt size used for
+        * computing cur_tp
+        */
+       return MINSTREL_TRUNC(mi->r[idx].cur_tp) * 1200 * 8 / 1024;
+}
+
 const struct rate_control_ops mac80211_minstrel = {
        .name = "minstrel",
        .tx_status = minstrel_tx_status,
@@ -670,6 +681,7 @@ const struct rate_control_ops mac80211_minstrel = {
        .add_sta_debugfs = minstrel_add_sta_debugfs,
        .remove_sta_debugfs = minstrel_remove_sta_debugfs,
 #endif
+       .get_expected_throughput = minstrel_get_expected_throughput,
 };
 
 int __init
index bccaf854a309e9434fb9044d0e98082e3de287de..85c1e74b7714c2160ffd6976bbec4322bb7e428d 100644 (file)
@@ -22,7 +22,7 @@
 #define MCS_NBITS (AVG_PKT_SIZE << 3)
 
 /* Number of symbols for a packet with (bps) bits per symbol */
-#define MCS_NSYMS(bps) ((MCS_NBITS + (bps) - 1) / (bps))
+#define MCS_NSYMS(bps) DIV_ROUND_UP(MCS_NBITS, (bps))
 
 /* Transmission time (nanoseconds) for a packet containing (syms) symbols */
 #define MCS_SYMBOL_TIME(sgi, syms)                                     \
@@ -226,8 +226,9 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)
                nsecs = 1000 * mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len);
 
        nsecs += minstrel_mcs_groups[group].duration[rate];
-       tp = 1000000 * ((prob * 1000) / nsecs);
 
+       /* prob is scaled - see MINSTREL_FRAC above */
+       tp = 1000000 * ((prob * 1000) / nsecs);
        mr->cur_tp = MINSTREL_TRUNC(tp);
 }
 
@@ -1031,6 +1032,22 @@ minstrel_ht_free(void *priv)
        mac80211_minstrel.free(priv);
 }
 
+static u32 minstrel_ht_get_expected_throughput(void *priv_sta)
+{
+       struct minstrel_ht_sta_priv *msp = priv_sta;
+       struct minstrel_ht_sta *mi = &msp->ht;
+       int i, j;
+
+       if (!msp->is_ht)
+               return mac80211_minstrel.get_expected_throughput(priv_sta);
+
+       i = mi->max_tp_rate / MCS_GROUP_RATES;
+       j = mi->max_tp_rate % MCS_GROUP_RATES;
+
+       /* convert cur_tp from pkt per second in kbps */
+       return mi->groups[i].rates[j].cur_tp * AVG_PKT_SIZE * 8 / 1024;
+}
+
 static const struct rate_control_ops mac80211_minstrel_ht = {
        .name = "minstrel_ht",
        .tx_status = minstrel_ht_tx_status,
@@ -1045,6 +1062,7 @@ static const struct rate_control_ops mac80211_minstrel_ht = {
        .add_sta_debugfs = minstrel_ht_add_sta_debugfs,
        .remove_sta_debugfs = minstrel_ht_remove_sta_debugfs,
 #endif
+       .get_expected_throughput = minstrel_ht_get_expected_throughput,
 };
 
 
index 2b608b2b70ece8a6ed0cb8f8d8eed5b1d44e1536..394e201cde6d3b6d4375f973937df55395547fea 100644 (file)
@@ -54,24 +54,25 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
        return skb;
 }
 
-static inline int should_drop_frame(struct sk_buff *skb, int present_fcs_len)
+static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len)
 {
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-       struct ieee80211_hdr *hdr;
-
-       hdr = (void *)(skb->data);
+       struct ieee80211_hdr *hdr = (void *)skb->data;
 
        if (status->flag & (RX_FLAG_FAILED_FCS_CRC |
                            RX_FLAG_FAILED_PLCP_CRC |
                            RX_FLAG_AMPDU_IS_ZEROLEN))
-               return 1;
+               return true;
+
        if (unlikely(skb->len < 16 + present_fcs_len))
-               return 1;
+               return true;
+
        if (ieee80211_is_ctl(hdr->frame_control) &&
            !ieee80211_is_pspoll(hdr->frame_control) &&
            !ieee80211_is_back_req(hdr->frame_control))
-               return 1;
-       return 0;
+               return true;
+
+       return false;
 }
 
 static int
@@ -3191,7 +3192,7 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
 }
 
 /*
- * This is the actual Rx frames handler. as it blongs to Rx path it must
+ * This is the actual Rx frames handler. as it belongs to Rx path it must
  * be called with rcu_read_lock protection.
  */
 static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
index 3ce7f2c8539a1f626f7488833ee966fb3af5d502..f40661eb75b578dd2e409757331c085d36461723 100644 (file)
@@ -309,7 +309,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
        if (local->scan_req != local->int_scan_req)
                cfg80211_scan_done(local->scan_req, aborted);
        local->scan_req = NULL;
-       rcu_assign_pointer(local->scan_sdata, NULL);
+       RCU_INIT_POINTER(local->scan_sdata, NULL);
 
        local->scanning = 0;
        local->scan_chandef.chan = NULL;
@@ -559,7 +559,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
                ieee80211_recalc_idle(local);
 
                local->scan_req = NULL;
-               rcu_assign_pointer(local->scan_sdata, NULL);
+               RCU_INIT_POINTER(local->scan_sdata, NULL);
        }
 
        return rc;
@@ -773,7 +773,7 @@ void ieee80211_scan_work(struct work_struct *work)
                int rc;
 
                local->scan_req = NULL;
-               rcu_assign_pointer(local->scan_sdata, NULL);
+               RCU_INIT_POINTER(local->scan_sdata, NULL);
 
                rc = __ieee80211_start_scan(sdata, req);
                if (rc) {
@@ -1014,7 +1014,7 @@ out_free:
 
        if (ret) {
                /* Clean in case of failure after HW restart or upon resume. */
-               rcu_assign_pointer(local->sched_scan_sdata, NULL);
+               RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
                local->sched_scan_req = NULL;
        }
 
@@ -1076,12 +1076,8 @@ void ieee80211_sched_scan_results(struct ieee80211_hw *hw)
 }
 EXPORT_SYMBOL(ieee80211_sched_scan_results);
 
-void ieee80211_sched_scan_stopped_work(struct work_struct *work)
+void ieee80211_sched_scan_end(struct ieee80211_local *local)
 {
-       struct ieee80211_local *local =
-               container_of(work, struct ieee80211_local,
-                            sched_scan_stopped_work);
-
        mutex_lock(&local->mtx);
 
        if (!rcu_access_pointer(local->sched_scan_sdata)) {
@@ -1089,7 +1085,7 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work)
                return;
        }
 
-       rcu_assign_pointer(local->sched_scan_sdata, NULL);
+       RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
 
        /* If sched scan was aborted by the driver. */
        local->sched_scan_req = NULL;
@@ -1099,6 +1095,15 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work)
        cfg80211_sched_scan_stopped(local->hw.wiphy);
 }
 
+void ieee80211_sched_scan_stopped_work(struct work_struct *work)
+{
+       struct ieee80211_local *local =
+               container_of(work, struct ieee80211_local,
+                            sched_scan_stopped_work);
+
+       ieee80211_sched_scan_end(local);
+}
+
 void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw)
 {
        struct ieee80211_local *local = hw_to_local(hw);
index 847d92f6bef60856be53ad13f0534695e4c6dac7..a9b46d8ea22ff696623cec4cad55949fc5afd2a1 100644 (file)
@@ -240,6 +240,7 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
 
        sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr);
 
+       kfree(rcu_dereference_raw(sta->sta.rates));
        kfree(sta);
 }
 
@@ -552,7 +553,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
 int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
 {
        struct ieee80211_local *local = sta->local;
-       int err = 0;
+       int err;
 
        might_sleep();
 
@@ -570,7 +571,6 @@ int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
 
        return 0;
  out_free:
-       BUG_ON(!err);
        sta_info_free(local, sta);
        return err;
 }
index 60cb7a665976e10e7a909a9545b7643cf34e67a4..ba29ebc8614121ea90ff5ff1cec44f137ac85a69 100644 (file)
@@ -541,6 +541,23 @@ static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local,
  */
 #define STA_LOST_PKT_THRESHOLD 50
 
+static void ieee80211_lost_packet(struct sta_info *sta, struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+       /* This packet was aggregated but doesn't carry status info */
+       if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
+           !(info->flags & IEEE80211_TX_STAT_AMPDU))
+               return;
+
+       if (++sta->lost_packets < STA_LOST_PKT_THRESHOLD)
+               return;
+
+       cfg80211_cqm_pktloss_notify(sta->sdata->dev, sta->sta.addr,
+                                   sta->lost_packets, GFP_ATOMIC);
+       sta->lost_packets = 0;
+}
+
 void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
        struct sk_buff *skb2;
@@ -680,12 +697,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
                        if (info->flags & IEEE80211_TX_STAT_ACK) {
                                if (sta->lost_packets)
                                        sta->lost_packets = 0;
-                       } else if (++sta->lost_packets >= STA_LOST_PKT_THRESHOLD) {
-                               cfg80211_cqm_pktloss_notify(sta->sdata->dev,
-                                                           sta->sta.addr,
-                                                           sta->lost_packets,
-                                                           GFP_ATOMIC);
-                               sta->lost_packets = 0;
+                       } else {
+                               ieee80211_lost_packet(sta, skb);
                        }
                }
 
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
new file mode 100644 (file)
index 0000000..652813b
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * mac80211 TDLS handling code
+ *
+ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2014, Intel Corporation
+ *
+ * This file is GPLv2 as found in COPYING.
+ */
+
+#include <linux/ieee80211.h>
+#include "ieee80211_i.h"
+
+static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
+{
+       u8 *pos = (void *)skb_put(skb, 7);
+
+       *pos++ = WLAN_EID_EXT_CAPABILITY;
+       *pos++ = 5; /* len */
+       *pos++ = 0x0;
+       *pos++ = 0x0;
+       *pos++ = 0x0;
+       *pos++ = 0x0;
+       *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
+}
+
+static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       u16 capab;
+
+       capab = 0;
+       if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ)
+               return capab;
+
+       if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
+               capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
+       if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE))
+               capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+
+       return capab;
+}
+
+static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr,
+                                      const u8 *peer, const u8 *bssid)
+{
+       struct ieee80211_tdls_lnkie *lnkid;
+
+       lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
+
+       lnkid->ie_type = WLAN_EID_LINK_ID;
+       lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
+
+       memcpy(lnkid->bssid, bssid, ETH_ALEN);
+       memcpy(lnkid->init_sta, src_addr, ETH_ALEN);
+       memcpy(lnkid->resp_sta, peer, ETH_ALEN);
+}
+
+static int
+ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
+                              const u8 *peer, u8 action_code, u8 dialog_token,
+                              u16 status_code, struct sk_buff *skb)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
+       struct ieee80211_tdls_data *tf;
+
+       tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
+
+       memcpy(tf->da, peer, ETH_ALEN);
+       memcpy(tf->sa, sdata->vif.addr, ETH_ALEN);
+       tf->ether_type = cpu_to_be16(ETH_P_TDLS);
+       tf->payload_type = WLAN_TDLS_SNAP_RFTYPE;
+
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+               tf->category = WLAN_CATEGORY_TDLS;
+               tf->action_code = WLAN_TDLS_SETUP_REQUEST;
+
+               skb_put(skb, sizeof(tf->u.setup_req));
+               tf->u.setup_req.dialog_token = dialog_token;
+               tf->u.setup_req.capability =
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
+
+               ieee80211_add_srates_ie(sdata, skb, false, band);
+               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
+               ieee80211_tdls_add_ext_capab(skb);
+               break;
+       case WLAN_TDLS_SETUP_RESPONSE:
+               tf->category = WLAN_CATEGORY_TDLS;
+               tf->action_code = WLAN_TDLS_SETUP_RESPONSE;
+
+               skb_put(skb, sizeof(tf->u.setup_resp));
+               tf->u.setup_resp.status_code = cpu_to_le16(status_code);
+               tf->u.setup_resp.dialog_token = dialog_token;
+               tf->u.setup_resp.capability =
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
+
+               ieee80211_add_srates_ie(sdata, skb, false, band);
+               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
+               ieee80211_tdls_add_ext_capab(skb);
+               break;
+       case WLAN_TDLS_SETUP_CONFIRM:
+               tf->category = WLAN_CATEGORY_TDLS;
+               tf->action_code = WLAN_TDLS_SETUP_CONFIRM;
+
+               skb_put(skb, sizeof(tf->u.setup_cfm));
+               tf->u.setup_cfm.status_code = cpu_to_le16(status_code);
+               tf->u.setup_cfm.dialog_token = dialog_token;
+               break;
+       case WLAN_TDLS_TEARDOWN:
+               tf->category = WLAN_CATEGORY_TDLS;
+               tf->action_code = WLAN_TDLS_TEARDOWN;
+
+               skb_put(skb, sizeof(tf->u.teardown));
+               tf->u.teardown.reason_code = cpu_to_le16(status_code);
+               break;
+       case WLAN_TDLS_DISCOVERY_REQUEST:
+               tf->category = WLAN_CATEGORY_TDLS;
+               tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST;
+
+               skb_put(skb, sizeof(tf->u.discover_req));
+               tf->u.discover_req.dialog_token = dialog_token;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
+                          const u8 *peer, u8 action_code, u8 dialog_token,
+                          u16 status_code, struct sk_buff *skb)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
+       struct ieee80211_mgmt *mgmt;
+
+       mgmt = (void *)skb_put(skb, 24);
+       memset(mgmt, 0, 24);
+       memcpy(mgmt->da, peer, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
+       memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
+
+       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                         IEEE80211_STYPE_ACTION);
+
+       switch (action_code) {
+       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+               skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp));
+               mgmt->u.action.category = WLAN_CATEGORY_PUBLIC;
+               mgmt->u.action.u.tdls_discover_resp.action_code =
+                       WLAN_PUB_ACTION_TDLS_DISCOVER_RES;
+               mgmt->u.action.u.tdls_discover_resp.dialog_token =
+                       dialog_token;
+               mgmt->u.action.u.tdls_discover_resp.capability =
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
+
+               ieee80211_add_srates_ie(sdata, skb, false, band);
+               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
+               ieee80211_tdls_add_ext_capab(skb);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
+                       const u8 *peer, u8 action_code, u8 dialog_token,
+                       u16 status_code, u32 peer_capability,
+                       const u8 *extra_ies, size_t extra_ies_len)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+       struct sk_buff *skb = NULL;
+       bool send_direct;
+       int ret;
+
+       if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+               return -ENOTSUPP;
+
+       /* make sure we are in managed mode, and associated */
+       if (sdata->vif.type != NL80211_IFTYPE_STATION ||
+           !sdata->u.mgd.associated)
+               return -EINVAL;
+
+       tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n",
+                action_code, peer);
+
+       skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+                           max(sizeof(struct ieee80211_mgmt),
+                               sizeof(struct ieee80211_tdls_data)) +
+                           50 + /* supported rates */
+                           7 + /* ext capab */
+                           extra_ies_len +
+                           sizeof(struct ieee80211_tdls_lnkie));
+       if (!skb)
+               return -ENOMEM;
+
+       skb_reserve(skb, local->hw.extra_tx_headroom);
+
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+       case WLAN_TDLS_SETUP_RESPONSE:
+       case WLAN_TDLS_SETUP_CONFIRM:
+       case WLAN_TDLS_TEARDOWN:
+       case WLAN_TDLS_DISCOVERY_REQUEST:
+               ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer,
+                                                    action_code, dialog_token,
+                                                    status_code, skb);
+               send_direct = false;
+               break;
+       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+               ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code,
+                                                dialog_token, status_code,
+                                                skb);
+               send_direct = true;
+               break;
+       default:
+               ret = -ENOTSUPP;
+               break;
+       }
+
+       if (ret < 0)
+               goto fail;
+
+       if (extra_ies_len)
+               memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
+
+       /* the TDLS link IE is always added last */
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+       case WLAN_TDLS_SETUP_CONFIRM:
+       case WLAN_TDLS_TEARDOWN:
+       case WLAN_TDLS_DISCOVERY_REQUEST:
+               /* we are the initiator */
+               ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer,
+                                          sdata->u.mgd.bssid);
+               break;
+       case WLAN_TDLS_SETUP_RESPONSE:
+       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+               /* we are the responder */
+               ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr,
+                                          sdata->u.mgd.bssid);
+               break;
+       default:
+               ret = -ENOTSUPP;
+               goto fail;
+       }
+
+       if (send_direct) {
+               ieee80211_tx_skb(sdata, skb);
+               return 0;
+       }
+
+       /*
+        * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise
+        * we should default to AC_VI.
+        */
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+       case WLAN_TDLS_SETUP_RESPONSE:
+               skb_set_queue_mapping(skb, IEEE80211_AC_BK);
+               skb->priority = 2;
+               break;
+       default:
+               skb_set_queue_mapping(skb, IEEE80211_AC_VI);
+               skb->priority = 5;
+               break;
+       }
+
+       /* disable bottom halves when entering the Tx path */
+       local_bh_disable();
+       ret = ieee80211_subif_start_xmit(skb, dev);
+       local_bh_enable();
+
+       return ret;
+
+fail:
+       dev_kfree_skb(skb);
+       return ret;
+}
+
+int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
+                       const u8 *peer, enum nl80211_tdls_operation oper)
+{
+       struct sta_info *sta;
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+               return -ENOTSUPP;
+
+       if (sdata->vif.type != NL80211_IFTYPE_STATION)
+               return -EINVAL;
+
+       tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer);
+
+       switch (oper) {
+       case NL80211_TDLS_ENABLE_LINK:
+               rcu_read_lock();
+               sta = sta_info_get(sdata, peer);
+               if (!sta) {
+                       rcu_read_unlock();
+                       return -ENOLINK;
+               }
+
+               set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
+               rcu_read_unlock();
+               break;
+       case NL80211_TDLS_DISABLE_LINK:
+               return sta_info_destroy_addr(sdata, peer);
+       case NL80211_TDLS_TEARDOWN:
+       case NL80211_TDLS_SETUP:
+       case NL80211_TDLS_DISCOVERY_REQ:
+               /* We don't support in-driver setup/teardown/discovery */
+               return -ENOTSUPP;
+       default:
+               return -ENOTSUPP;
+       }
+
+       return 0;
+}
index cec5b60487a4032e2b805df374616ea9ad038669..cfe1a0688b5ce2846eb906b3f196c5cf51a4590b 100644 (file)
@@ -184,6 +184,20 @@ TRACE_EVENT(drv_return_bool,
                  "true" : "false")
 );
 
+TRACE_EVENT(drv_return_u32,
+       TP_PROTO(struct ieee80211_local *local, u32 ret),
+       TP_ARGS(local, ret),
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               __field(u32, ret)
+       ),
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               __entry->ret = ret;
+       ),
+       TP_printk(LOCAL_PR_FMT " - %u", LOCAL_PR_ARG, __entry->ret)
+);
+
 TRACE_EVENT(drv_return_u64,
        TP_PROTO(struct ieee80211_local *local, u64 ret),
        TP_ARGS(local, ret),
@@ -1375,6 +1389,91 @@ TRACE_EVENT(drv_change_chanctx,
        )
 );
 
+#if !defined(__TRACE_VIF_ENTRY)
+#define __TRACE_VIF_ENTRY
+struct trace_vif_entry {
+       enum nl80211_iftype vif_type;
+       bool p2p;
+       char vif_name[IFNAMSIZ];
+} __packed;
+
+struct trace_chandef_entry {
+       u32 control_freq;
+       u32 chan_width;
+       u32 center_freq1;
+       u32 center_freq2;
+} __packed;
+
+struct trace_switch_entry {
+       struct trace_vif_entry vif;
+       struct trace_chandef_entry old_chandef;
+       struct trace_chandef_entry new_chandef;
+} __packed;
+
+#define SWITCH_ENTRY_ASSIGN(to, from) local_vifs[i].to = vifs[i].from
+#endif
+
+TRACE_EVENT(drv_switch_vif_chanctx,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_vif_chanctx_switch *vifs,
+                int n_vifs, enum ieee80211_chanctx_switch_mode mode),
+           TP_ARGS(local, vifs, n_vifs, mode),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               __field(int, n_vifs)
+               __field(u32, mode)
+               __dynamic_array(u8, vifs,
+                               sizeof(struct trace_switch_entry) * n_vifs)
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               __entry->n_vifs = n_vifs;
+               __entry->mode = mode;
+               {
+                       struct trace_switch_entry *local_vifs =
+                               __get_dynamic_array(vifs);
+                       int i;
+
+                       for (i = 0; i < n_vifs; i++) {
+                               struct ieee80211_sub_if_data *sdata;
+
+                               sdata = container_of(vifs[i].vif,
+                                               struct ieee80211_sub_if_data,
+                                               vif);
+
+                               SWITCH_ENTRY_ASSIGN(vif.vif_type, vif->type);
+                               SWITCH_ENTRY_ASSIGN(vif.p2p, vif->p2p);
+                               strncpy(local_vifs[i].vif.vif_name,
+                                       sdata->name,
+                                       sizeof(local_vifs[i].vif.vif_name));
+                               SWITCH_ENTRY_ASSIGN(old_chandef.control_freq,
+                                               old_ctx->def.chan->center_freq);
+                               SWITCH_ENTRY_ASSIGN(old_chandef.chan_width,
+                                                   old_ctx->def.width);
+                               SWITCH_ENTRY_ASSIGN(old_chandef.center_freq1,
+                                                   old_ctx->def.center_freq1);
+                               SWITCH_ENTRY_ASSIGN(old_chandef.center_freq2,
+                                                   old_ctx->def.center_freq2);
+                               SWITCH_ENTRY_ASSIGN(new_chandef.control_freq,
+                                               new_ctx->def.chan->center_freq);
+                               SWITCH_ENTRY_ASSIGN(new_chandef.chan_width,
+                                                   new_ctx->def.width);
+                               SWITCH_ENTRY_ASSIGN(new_chandef.center_freq1,
+                                                   new_ctx->def.center_freq1);
+                               SWITCH_ENTRY_ASSIGN(new_chandef.center_freq2,
+                                                   new_ctx->def.center_freq2);
+                       }
+               }
+       ),
+
+       TP_printk(
+               LOCAL_PR_FMT " n_vifs:%d mode:%d",
+               LOCAL_PR_ARG, __entry->n_vifs, __entry->mode
+       )
+);
+
 DECLARE_EVENT_CLASS(local_sdata_chanctx,
        TP_PROTO(struct ieee80211_local *local,
                 struct ieee80211_sub_if_data *sdata,
@@ -1499,6 +1598,24 @@ DEFINE_EVENT(local_sdata_evt, drv_leave_ibss,
        TP_ARGS(local, sdata)
 );
 
+TRACE_EVENT(drv_get_expected_throughput,
+       TP_PROTO(struct ieee80211_sta *sta),
+
+       TP_ARGS(sta),
+
+       TP_STRUCT__entry(
+               STA_ENTRY
+       ),
+
+       TP_fast_assign(
+               STA_ASSIGN;
+       ),
+
+       TP_printk(
+               STA_PR_FMT, STA_PR_ARG
+       )
+);
+
 /*
  * Tracing for API calls that drivers call.
  */
index 19d36d4117e0da0b5524b7e3f8102a86810dceed..5214686d9fd1ec9ab4bc1e2a466532bd3c829c10 100644 (file)
@@ -2328,7 +2328,8 @@ void ieee80211_tx_pending(unsigned long data)
 /* functions for drivers to get certain frames */
 
 static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
-                                      struct ps_data *ps, struct sk_buff *skb)
+                                      struct ps_data *ps, struct sk_buff *skb,
+                                      bool is_template)
 {
        u8 *pos, *tim;
        int aid0 = 0;
@@ -2341,11 +2342,12 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
                 * checking byte-for-byte */
                have_bits = !bitmap_empty((unsigned long *)ps->tim,
                                          IEEE80211_MAX_AID+1);
-
-       if (ps->dtim_count == 0)
-               ps->dtim_count = sdata->vif.bss_conf.dtim_period - 1;
-       else
-               ps->dtim_count--;
+       if (!is_template) {
+               if (ps->dtim_count == 0)
+                       ps->dtim_count = sdata->vif.bss_conf.dtim_period - 1;
+               else
+                       ps->dtim_count--;
+       }
 
        tim = pos = (u8 *) skb_put(skb, 6);
        *pos++ = WLAN_EID_TIM;
@@ -2391,7 +2393,8 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
 }
 
 static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
-                                   struct ps_data *ps, struct sk_buff *skb)
+                                   struct ps_data *ps, struct sk_buff *skb,
+                                   bool is_template)
 {
        struct ieee80211_local *local = sdata->local;
 
@@ -2403,24 +2406,24 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
         * of the tim bitmap in mac80211 and the driver.
         */
        if (local->tim_in_locked_section) {
-               __ieee80211_beacon_add_tim(sdata, ps, skb);
+               __ieee80211_beacon_add_tim(sdata, ps, skb, is_template);
        } else {
                spin_lock_bh(&local->tim_lock);
-               __ieee80211_beacon_add_tim(sdata, ps, skb);
+               __ieee80211_beacon_add_tim(sdata, ps, skb, is_template);
                spin_unlock_bh(&local->tim_lock);
        }
 
        return 0;
 }
 
-static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
-                                struct beacon_data *beacon)
+static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata,
+                             struct beacon_data *beacon)
 {
        struct probe_resp *resp;
-       int counter_offset_beacon = sdata->csa_counter_offset_beacon;
-       int counter_offset_presp = sdata->csa_counter_offset_presp;
        u8 *beacon_data;
        size_t beacon_data_len;
+       int i;
+       u8 count = sdata->csa_current_counter;
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
@@ -2438,40 +2441,57 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
        default:
                return;
        }
-       if (WARN_ON(counter_offset_beacon >= beacon_data_len))
-               return;
 
-       /* Warn if the driver did not check for/react to csa
-        * completeness.  A beacon with CSA counter set to 0 should
-        * never occur, because a counter of 1 means switch just
-        * before the next beacon.
-        */
-       if (WARN_ON(beacon_data[counter_offset_beacon] == 1))
-               return;
+       for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; ++i) {
+               u16 counter_offset_beacon =
+                       sdata->csa_counter_offset_beacon[i];
+               u16 counter_offset_presp = sdata->csa_counter_offset_presp[i];
 
-       beacon_data[counter_offset_beacon]--;
+               if (counter_offset_beacon) {
+                       if (WARN_ON(counter_offset_beacon >= beacon_data_len))
+                               return;
 
-       if (sdata->vif.type == NL80211_IFTYPE_AP && counter_offset_presp) {
-               rcu_read_lock();
-               resp = rcu_dereference(sdata->u.ap.probe_resp);
+                       beacon_data[counter_offset_beacon] = count;
+               }
+
+               if (sdata->vif.type == NL80211_IFTYPE_AP &&
+                   counter_offset_presp) {
+                       rcu_read_lock();
+                       resp = rcu_dereference(sdata->u.ap.probe_resp);
 
-               /* if nl80211 accepted the offset, this should not happen. */
-               if (WARN_ON(!resp)) {
+                       /* If nl80211 accepted the offset, this should
+                        * not happen.
+                        */
+                       if (WARN_ON(!resp)) {
+                               rcu_read_unlock();
+                               return;
+                       }
+                       resp->data[counter_offset_presp] = count;
                        rcu_read_unlock();
-                       return;
                }
-               resp->data[counter_offset_presp]--;
-               rcu_read_unlock();
        }
 }
 
+u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+       sdata->csa_current_counter--;
+
+       /* the counter should never reach 0 */
+       WARN_ON(!sdata->csa_current_counter);
+
+       return sdata->csa_current_counter;
+}
+EXPORT_SYMBOL(ieee80211_csa_update_counter);
+
 bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
 {
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
        struct beacon_data *beacon = NULL;
        u8 *beacon_data;
        size_t beacon_data_len;
-       int counter_beacon = sdata->csa_counter_offset_beacon;
+       int counter_beacon = sdata->csa_counter_offset_beacon[0];
        int ret = false;
 
        if (!ieee80211_sdata_running(sdata))
@@ -2521,9 +2541,11 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
 }
 EXPORT_SYMBOL(ieee80211_csa_is_complete);
 
-struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
-                                        struct ieee80211_vif *vif,
-                                        u16 *tim_offset, u16 *tim_length)
+static struct sk_buff *
+__ieee80211_beacon_get(struct ieee80211_hw *hw,
+                      struct ieee80211_vif *vif,
+                      struct ieee80211_mutable_offsets *offs,
+                      bool is_template)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        struct sk_buff *skb = NULL;
@@ -2532,6 +2554,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
        enum ieee80211_band band;
        struct ieee80211_tx_rate_control txrc;
        struct ieee80211_chanctx_conf *chanctx_conf;
+       int csa_off_base = 0;
 
        rcu_read_lock();
 
@@ -2541,18 +2564,20 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
        if (!ieee80211_sdata_running(sdata) || !chanctx_conf)
                goto out;
 
-       if (tim_offset)
-               *tim_offset = 0;
-       if (tim_length)
-               *tim_length = 0;
+       if (offs)
+               memset(offs, 0, sizeof(*offs));
 
        if (sdata->vif.type == NL80211_IFTYPE_AP) {
                struct ieee80211_if_ap *ap = &sdata->u.ap;
                struct beacon_data *beacon = rcu_dereference(ap->beacon);
 
                if (beacon) {
-                       if (sdata->vif.csa_active)
-                               ieee80211_update_csa(sdata, beacon);
+                       if (sdata->vif.csa_active) {
+                               if (!is_template)
+                                       ieee80211_csa_update_counter(vif);
+
+                               ieee80211_set_csa(sdata, beacon);
+                       }
 
                        /*
                         * headroom, head length,
@@ -2569,12 +2594,16 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
                        memcpy(skb_put(skb, beacon->head_len), beacon->head,
                               beacon->head_len);
 
-                       ieee80211_beacon_add_tim(sdata, &ap->ps, skb);
+                       ieee80211_beacon_add_tim(sdata, &ap->ps, skb,
+                                                is_template);
 
-                       if (tim_offset)
-                               *tim_offset = beacon->head_len;
-                       if (tim_length)
-                               *tim_length = skb->len - beacon->head_len;
+                       if (offs) {
+                               offs->tim_offset = beacon->head_len;
+                               offs->tim_length = skb->len - beacon->head_len;
+
+                               /* for AP the csa offsets are from tail */
+                               csa_off_base = skb->len;
+                       }
 
                        if (beacon->tail)
                                memcpy(skb_put(skb, beacon->tail_len),
@@ -2589,9 +2618,12 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
                if (!presp)
                        goto out;
 
-               if (sdata->vif.csa_active)
-                       ieee80211_update_csa(sdata, presp);
+               if (sdata->vif.csa_active) {
+                       if (!is_template)
+                               ieee80211_csa_update_counter(vif);
 
+                       ieee80211_set_csa(sdata, presp);
+               }
 
                skb = dev_alloc_skb(local->tx_headroom + presp->head_len +
                                    local->hw.extra_beacon_tailroom);
@@ -2611,8 +2643,17 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
                if (!bcn)
                        goto out;
 
-               if (sdata->vif.csa_active)
-                       ieee80211_update_csa(sdata, bcn);
+               if (sdata->vif.csa_active) {
+                       if (!is_template)
+                               /* TODO: For mesh csa_counter is in TU, so
+                                * decrementing it by one isn't correct, but
+                                * for now we leave it consistent with overall
+                                * mac80211's behavior.
+                                */
+                               ieee80211_csa_update_counter(vif);
+
+                       ieee80211_set_csa(sdata, bcn);
+               }
 
                if (ifmsh->sync_ops)
                        ifmsh->sync_ops->adjust_tbtt(sdata, bcn);
@@ -2626,13 +2667,33 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
                        goto out;
                skb_reserve(skb, local->tx_headroom);
                memcpy(skb_put(skb, bcn->head_len), bcn->head, bcn->head_len);
-               ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb);
+               ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb, is_template);
+
+               if (offs) {
+                       offs->tim_offset = bcn->head_len;
+                       offs->tim_length = skb->len - bcn->head_len;
+               }
+
                memcpy(skb_put(skb, bcn->tail_len), bcn->tail, bcn->tail_len);
        } else {
                WARN_ON(1);
                goto out;
        }
 
+       /* CSA offsets */
+       if (offs) {
+               int i;
+
+               for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; i++) {
+                       u16 csa_off = sdata->csa_counter_offset_beacon[i];
+
+                       if (!csa_off)
+                               continue;
+
+                       offs->csa_counter_offs[i] = csa_off_base + csa_off;
+               }
+       }
+
        band = chanctx_conf->def.chan->band;
 
        info = IEEE80211_SKB_CB(skb);
@@ -2663,6 +2724,32 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
  out:
        rcu_read_unlock();
        return skb;
+
+}
+
+struct sk_buff *
+ieee80211_beacon_get_template(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif,
+                             struct ieee80211_mutable_offsets *offs)
+{
+       return __ieee80211_beacon_get(hw, vif, offs, true);
+}
+EXPORT_SYMBOL(ieee80211_beacon_get_template);
+
+struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
+                                        struct ieee80211_vif *vif,
+                                        u16 *tim_offset, u16 *tim_length)
+{
+       struct ieee80211_mutable_offsets offs = {};
+       struct sk_buff *bcn = __ieee80211_beacon_get(hw, vif, &offs, false);
+
+       if (tim_offset)
+               *tim_offset = offs.tim_offset;
+
+       if (tim_length)
+               *tim_length = offs.tim_length;
+
+       return bcn;
 }
 EXPORT_SYMBOL(ieee80211_beacon_get_tim);
 
index 3c365837e910edce60ac2348b0002361c4fca6ea..6886601afe1c731c3cc7b5409745307b2f48e67c 100644 (file)
@@ -554,7 +554,7 @@ void ieee80211_flush_queues(struct ieee80211_local *local,
        ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_FLUSH);
 
-       drv_flush(local, queues, false);
+       drv_flush(local, sdata, queues, false);
 
        ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_FLUSH);
@@ -1457,6 +1457,44 @@ void ieee80211_stop_device(struct ieee80211_local *local)
        drv_stop(local);
 }
 
+static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local)
+{
+       struct ieee80211_sub_if_data *sdata;
+       struct ieee80211_chanctx *ctx;
+
+       /*
+        * We get here if during resume the device can't be restarted properly.
+        * We might also get here if this happens during HW reset, which is a
+        * slightly different situation and we need to drop all connections in
+        * the latter case.
+        *
+        * Ask cfg80211 to turn off all interfaces, this will result in more
+        * warnings but at least we'll then get into a clean stopped state.
+        */
+
+       local->resuming = false;
+       local->suspended = false;
+       local->started = false;
+
+       /* scheduled scan clearly can't be running any more, but tell
+        * cfg80211 and clear local state
+        */
+       ieee80211_sched_scan_end(local);
+
+       list_for_each_entry(sdata, &local->interfaces, list)
+               sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER;
+
+       /* Mark channel contexts as not being in the driver any more to avoid
+        * removing them from the driver during the shutdown process...
+        */
+       mutex_lock(&local->chanctx_mtx);
+       list_for_each_entry(ctx, &local->chanctx_list, list)
+               ctx->driver_present = false;
+       mutex_unlock(&local->chanctx_mtx);
+
+       cfg80211_shutdown_all_interfaces(local->hw.wiphy);
+}
+
 static void ieee80211_assign_chanctx(struct ieee80211_local *local,
                                     struct ieee80211_sub_if_data *sdata)
 {
@@ -1520,9 +1558,11 @@ int ieee80211_reconfig(struct ieee80211_local *local)
         */
        res = drv_start(local);
        if (res) {
-               WARN(local->suspended, "Hardware became unavailable "
-                    "upon resume. This could be a software issue "
-                    "prior to suspend or a hardware issue.\n");
+               if (local->suspended)
+                       WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n");
+               else
+                       WARN(1, "Hardware became unavailable during restart.\n");
+               ieee80211_handle_reconfig_failure(local);
                return res;
        }
 
@@ -1546,7 +1586,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                WARN_ON(local->resuming);
                res = drv_add_interface(local, sdata);
                if (WARN_ON(res)) {
-                       rcu_assign_pointer(local->monitor_sdata, NULL);
+                       RCU_INIT_POINTER(local->monitor_sdata, NULL);
                        synchronize_net();
                        kfree(sdata);
                }
@@ -1565,17 +1605,17 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                list_for_each_entry(ctx, &local->chanctx_list, list)
                        WARN_ON(drv_add_chanctx(local, ctx));
                mutex_unlock(&local->chanctx_mtx);
-       }
 
-       list_for_each_entry(sdata, &local->interfaces, list) {
-               if (!ieee80211_sdata_running(sdata))
-                       continue;
-               ieee80211_assign_chanctx(local, sdata);
-       }
+               list_for_each_entry(sdata, &local->interfaces, list) {
+                       if (!ieee80211_sdata_running(sdata))
+                               continue;
+                       ieee80211_assign_chanctx(local, sdata);
+               }
 
-       sdata = rtnl_dereference(local->monitor_sdata);
-       if (sdata && ieee80211_sdata_running(sdata))
-               ieee80211_assign_chanctx(local, sdata);
+               sdata = rtnl_dereference(local->monitor_sdata);
+               if (sdata && ieee80211_sdata_running(sdata))
+                       ieee80211_assign_chanctx(local, sdata);
+       }
 
        /* add STAs back */
        mutex_lock(&local->sta_mtx);
@@ -1671,13 +1711,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                        }
                        break;
                case NL80211_IFTYPE_WDS:
-                       break;
                case NL80211_IFTYPE_AP_VLAN:
                case NL80211_IFTYPE_MONITOR:
-                       /* ignore virtual */
-                       break;
                case NL80211_IFTYPE_P2P_DEVICE:
-                       changed = BSS_CHANGED_IDLE;
+                       /* nothing to do */
                        break;
                case NL80211_IFTYPE_UNSPECIFIED:
                case NUM_NL80211_IFTYPES:
@@ -2797,3 +2834,121 @@ void ieee80211_recalc_dtim(struct ieee80211_local *local,
 
        ps->dtim_count = dtim_count;
 }
+
+int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
+                                const struct cfg80211_chan_def *chandef,
+                                enum ieee80211_chanctx_mode chanmode,
+                                u8 radar_detect)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_sub_if_data *sdata_iter;
+       enum nl80211_iftype iftype = sdata->wdev.iftype;
+       int num[NUM_NL80211_IFTYPES];
+       struct ieee80211_chanctx *ctx;
+       int num_different_channels = 0;
+       int total = 1;
+
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       if (WARN_ON(hweight32(radar_detect) > 1))
+               return -EINVAL;
+
+       if (WARN_ON(chandef && chanmode == IEEE80211_CHANCTX_SHARED &&
+                   !chandef->chan))
+               return -EINVAL;
+
+       if (chandef)
+               num_different_channels = 1;
+
+       if (WARN_ON(iftype >= NUM_NL80211_IFTYPES))
+               return -EINVAL;
+
+       /* Always allow software iftypes */
+       if (local->hw.wiphy->software_iftypes & BIT(iftype)) {
+               if (radar_detect)
+                       return -EINVAL;
+               return 0;
+       }
+
+       memset(num, 0, sizeof(num));
+
+       if (iftype != NL80211_IFTYPE_UNSPECIFIED)
+               num[iftype] = 1;
+
+       list_for_each_entry(ctx, &local->chanctx_list, list) {
+               if (ctx->conf.radar_enabled)
+                       radar_detect |= BIT(ctx->conf.def.width);
+               if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) {
+                       num_different_channels++;
+                       continue;
+               }
+               if (chandef && chanmode == IEEE80211_CHANCTX_SHARED &&
+                   cfg80211_chandef_compatible(chandef,
+                                               &ctx->conf.def))
+                       continue;
+               num_different_channels++;
+       }
+
+       list_for_each_entry_rcu(sdata_iter, &local->interfaces, list) {
+               struct wireless_dev *wdev_iter;
+
+               wdev_iter = &sdata_iter->wdev;
+
+               if (sdata_iter == sdata ||
+                   rcu_access_pointer(sdata_iter->vif.chanctx_conf) == NULL ||
+                   local->hw.wiphy->software_iftypes & BIT(wdev_iter->iftype))
+                       continue;
+
+               num[wdev_iter->iftype]++;
+               total++;
+       }
+
+       if (total == 1 && !radar_detect)
+               return 0;
+
+       return cfg80211_check_combinations(local->hw.wiphy,
+                                          num_different_channels,
+                                          radar_detect, num);
+}
+
+static void
+ieee80211_iter_max_chans(const struct ieee80211_iface_combination *c,
+                        void *data)
+{
+       u32 *max_num_different_channels = data;
+
+       *max_num_different_channels = max(*max_num_different_channels,
+                                         c->num_different_channels);
+}
+
+int ieee80211_max_num_channels(struct ieee80211_local *local)
+{
+       struct ieee80211_sub_if_data *sdata;
+       int num[NUM_NL80211_IFTYPES] = {};
+       struct ieee80211_chanctx *ctx;
+       int num_different_channels = 0;
+       u8 radar_detect = 0;
+       u32 max_num_different_channels = 1;
+       int err;
+
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       list_for_each_entry(ctx, &local->chanctx_list, list) {
+               num_different_channels++;
+
+               if (ctx->conf.radar_enabled)
+                       radar_detect |= BIT(ctx->conf.def.width);
+       }
+
+       list_for_each_entry_rcu(sdata, &local->interfaces, list)
+               num[sdata->wdev.iftype]++;
+
+       err = cfg80211_iter_combinations(local->hw.wiphy,
+                                        num_different_channels, radar_detect,
+                                        num, ieee80211_iter_max_chans,
+                                        &max_num_different_channels);
+       if (err < 0)
+               return err;
+
+       return max_num_different_channels;
+}
index b8600e3c29c828d918b3676397f73a4d0fe7892c..9b3dcc201145dd3942bf30771e1638ea973759f7 100644 (file)
@@ -406,7 +406,10 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
 
        if (info->control.hw_key &&
            !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
-           !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) {
+           !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) &&
+           !((info->control.hw_key->flags &
+              IEEE80211_KEY_FLAG_GENERATE_IV_MGMT) &&
+             ieee80211_is_mgmt(hdr->frame_control))) {
                /*
                 * hwaccel has no need for preallocated room for CCMP
                 * header or MIC fields
index b33dd76d4307309bb02477606f8e7ccdefaa870d..1818a99b3081e5a87a5c1ed72e6dba1760f2c1bc 100644 (file)
@@ -2,6 +2,10 @@ config MAC802154
        tristate "Generic IEEE 802.15.4 Soft Networking Stack (mac802154)"
        depends on IEEE802154
        select CRC_CCITT
+       select CRYPTO_AUTHENC
+       select CRYPTO_CCM
+       select CRYPTO_CTR
+       select CRYPTO_AES
        ---help---
          This option enables the hardware independent IEEE 802.15.4
          networking stack for SoftMAC devices (the ones implementing
index 15d62df521825c8581fc25ec25a9caebc8902a72..9723d6f3f3e5b742e1d105b2667627949f1d7c7d 100644 (file)
@@ -1,4 +1,5 @@
 obj-$(CONFIG_MAC802154)        += mac802154.o
-mac802154-objs         := ieee802154_dev.o rx.o tx.o mac_cmd.o mib.o monitor.o wpan.o
+mac802154-objs         := ieee802154_dev.o rx.o tx.o mac_cmd.o mib.o \
+                          monitor.o wpan.o llsec.o
 
 ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c
new file mode 100644 (file)
index 0000000..1456f73
--- /dev/null
@@ -0,0 +1,1070 @@
+/*
+ * Copyright (C) 2014 Fraunhofer ITWM
+ *
+ * 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.
+ *
+ * Written by:
+ * Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de>
+ */
+
+#include <linux/err.h>
+#include <linux/bug.h>
+#include <linux/completion.h>
+#include <net/ieee802154.h>
+#include <crypto/algapi.h>
+
+#include "mac802154.h"
+#include "llsec.h"
+
+static void llsec_key_put(struct mac802154_llsec_key *key);
+static bool llsec_key_id_equal(const struct ieee802154_llsec_key_id *a,
+                              const struct ieee802154_llsec_key_id *b);
+
+static void llsec_dev_free(struct mac802154_llsec_device *dev);
+
+void mac802154_llsec_init(struct mac802154_llsec *sec)
+{
+       memset(sec, 0, sizeof(*sec));
+
+       memset(&sec->params.default_key_source, 0xFF, IEEE802154_ADDR_LEN);
+
+       INIT_LIST_HEAD(&sec->table.security_levels);
+       INIT_LIST_HEAD(&sec->table.devices);
+       INIT_LIST_HEAD(&sec->table.keys);
+       hash_init(sec->devices_short);
+       hash_init(sec->devices_hw);
+       rwlock_init(&sec->lock);
+}
+
+void mac802154_llsec_destroy(struct mac802154_llsec *sec)
+{
+       struct ieee802154_llsec_seclevel *sl, *sn;
+       struct ieee802154_llsec_device *dev, *dn;
+       struct ieee802154_llsec_key_entry *key, *kn;
+
+       list_for_each_entry_safe(sl, sn, &sec->table.security_levels, list) {
+               struct mac802154_llsec_seclevel *msl;
+
+               msl = container_of(sl, struct mac802154_llsec_seclevel, level);
+               list_del(&sl->list);
+               kfree(msl);
+       }
+
+       list_for_each_entry_safe(dev, dn, &sec->table.devices, list) {
+               struct mac802154_llsec_device *mdev;
+
+               mdev = container_of(dev, struct mac802154_llsec_device, dev);
+               list_del(&dev->list);
+               llsec_dev_free(mdev);
+       }
+
+       list_for_each_entry_safe(key, kn, &sec->table.keys, list) {
+               struct mac802154_llsec_key *mkey;
+
+               mkey = container_of(key->key, struct mac802154_llsec_key, key);
+               list_del(&key->list);
+               llsec_key_put(mkey);
+               kfree(key);
+       }
+}
+
+
+
+int mac802154_llsec_get_params(struct mac802154_llsec *sec,
+                              struct ieee802154_llsec_params *params)
+{
+       read_lock_bh(&sec->lock);
+       *params = sec->params;
+       read_unlock_bh(&sec->lock);
+
+       return 0;
+}
+
+int mac802154_llsec_set_params(struct mac802154_llsec *sec,
+                              const struct ieee802154_llsec_params *params,
+                              int changed)
+{
+       write_lock_bh(&sec->lock);
+
+       if (changed & IEEE802154_LLSEC_PARAM_ENABLED)
+               sec->params.enabled = params->enabled;
+       if (changed & IEEE802154_LLSEC_PARAM_FRAME_COUNTER)
+               sec->params.frame_counter = params->frame_counter;
+       if (changed & IEEE802154_LLSEC_PARAM_OUT_LEVEL)
+               sec->params.out_level = params->out_level;
+       if (changed & IEEE802154_LLSEC_PARAM_OUT_KEY)
+               sec->params.out_key = params->out_key;
+       if (changed & IEEE802154_LLSEC_PARAM_KEY_SOURCE)
+               sec->params.default_key_source = params->default_key_source;
+       if (changed & IEEE802154_LLSEC_PARAM_PAN_ID)
+               sec->params.pan_id = params->pan_id;
+       if (changed & IEEE802154_LLSEC_PARAM_HWADDR)
+               sec->params.hwaddr = params->hwaddr;
+       if (changed & IEEE802154_LLSEC_PARAM_COORD_HWADDR)
+               sec->params.coord_hwaddr = params->coord_hwaddr;
+       if (changed & IEEE802154_LLSEC_PARAM_COORD_SHORTADDR)
+               sec->params.coord_shortaddr = params->coord_shortaddr;
+
+       write_unlock_bh(&sec->lock);
+
+       return 0;
+}
+
+
+
+static struct mac802154_llsec_key*
+llsec_key_alloc(const struct ieee802154_llsec_key *template)
+{
+       const int authsizes[3] = { 4, 8, 16 };
+       struct mac802154_llsec_key *key;
+       int i;
+
+       key = kzalloc(sizeof(*key), GFP_KERNEL);
+       if (!key)
+               return NULL;
+
+       kref_init(&key->ref);
+       key->key = *template;
+
+       BUILD_BUG_ON(ARRAY_SIZE(authsizes) != ARRAY_SIZE(key->tfm));
+
+       for (i = 0; i < ARRAY_SIZE(key->tfm); i++) {
+               key->tfm[i] = crypto_alloc_aead("ccm(aes)", 0,
+                                               CRYPTO_ALG_ASYNC);
+               if (!key->tfm[i])
+                       goto err_tfm;
+               if (crypto_aead_setkey(key->tfm[i], template->key,
+                                      IEEE802154_LLSEC_KEY_SIZE))
+                       goto err_tfm;
+               if (crypto_aead_setauthsize(key->tfm[i], authsizes[i]))
+                       goto err_tfm;
+       }
+
+       key->tfm0 = crypto_alloc_blkcipher("ctr(aes)", 0, CRYPTO_ALG_ASYNC);
+       if (!key->tfm0)
+               goto err_tfm;
+
+       if (crypto_blkcipher_setkey(key->tfm0, template->key,
+                                   IEEE802154_LLSEC_KEY_SIZE))
+               goto err_tfm0;
+
+       return key;
+
+err_tfm0:
+       crypto_free_blkcipher(key->tfm0);
+err_tfm:
+       for (i = 0; i < ARRAY_SIZE(key->tfm); i++)
+               if (key->tfm[i])
+                       crypto_free_aead(key->tfm[i]);
+
+       kfree(key);
+       return NULL;
+}
+
+static void llsec_key_release(struct kref *ref)
+{
+       struct mac802154_llsec_key *key;
+       int i;
+
+       key = container_of(ref, struct mac802154_llsec_key, ref);
+
+       for (i = 0; i < ARRAY_SIZE(key->tfm); i++)
+               crypto_free_aead(key->tfm[i]);
+
+       crypto_free_blkcipher(key->tfm0);
+       kfree(key);
+}
+
+static struct mac802154_llsec_key*
+llsec_key_get(struct mac802154_llsec_key *key)
+{
+       kref_get(&key->ref);
+       return key;
+}
+
+static void llsec_key_put(struct mac802154_llsec_key *key)
+{
+       kref_put(&key->ref, llsec_key_release);
+}
+
+static bool llsec_key_id_equal(const struct ieee802154_llsec_key_id *a,
+                              const struct ieee802154_llsec_key_id *b)
+{
+       if (a->mode != b->mode)
+               return false;
+
+       if (a->mode == IEEE802154_SCF_KEY_IMPLICIT)
+               return ieee802154_addr_equal(&a->device_addr, &b->device_addr);
+
+       if (a->id != b->id)
+               return false;
+
+       switch (a->mode) {
+       case IEEE802154_SCF_KEY_INDEX:
+               return true;
+       case IEEE802154_SCF_KEY_SHORT_INDEX:
+               return a->short_source == b->short_source;
+       case IEEE802154_SCF_KEY_HW_INDEX:
+               return a->extended_source == b->extended_source;
+       }
+
+       return false;
+}
+
+int mac802154_llsec_key_add(struct mac802154_llsec *sec,
+                           const struct ieee802154_llsec_key_id *id,
+                           const struct ieee802154_llsec_key *key)
+{
+       struct mac802154_llsec_key *mkey = NULL;
+       struct ieee802154_llsec_key_entry *pos, *new;
+
+       if (!(key->frame_types & (1 << IEEE802154_FC_TYPE_MAC_CMD)) &&
+           key->cmd_frame_ids)
+               return -EINVAL;
+
+       list_for_each_entry(pos, &sec->table.keys, list) {
+               if (llsec_key_id_equal(&pos->id, id))
+                       return -EEXIST;
+
+               if (memcmp(pos->key->key, key->key,
+                          IEEE802154_LLSEC_KEY_SIZE))
+                       continue;
+
+               mkey = container_of(pos->key, struct mac802154_llsec_key, key);
+
+               /* Don't allow multiple instances of the same AES key to have
+                * different allowed frame types/command frame ids, as this is
+                * not possible in the 802.15.4 PIB.
+                */
+               if (pos->key->frame_types != key->frame_types ||
+                   pos->key->cmd_frame_ids != key->cmd_frame_ids)
+                       return -EEXIST;
+
+               break;
+       }
+
+       new = kzalloc(sizeof(*new), GFP_KERNEL);
+       if (!new)
+               return -ENOMEM;
+
+       if (!mkey)
+               mkey = llsec_key_alloc(key);
+       else
+               mkey = llsec_key_get(mkey);
+
+       if (!mkey)
+               goto fail;
+
+       new->id = *id;
+       new->key = &mkey->key;
+
+       list_add_rcu(&new->list, &sec->table.keys);
+
+       return 0;
+
+fail:
+       kfree(new);
+       return -ENOMEM;
+}
+
+int mac802154_llsec_key_del(struct mac802154_llsec *sec,
+                           const struct ieee802154_llsec_key_id *key)
+{
+       struct ieee802154_llsec_key_entry *pos;
+
+       list_for_each_entry(pos, &sec->table.keys, list) {
+               struct mac802154_llsec_key *mkey;
+
+               mkey = container_of(pos->key, struct mac802154_llsec_key, key);
+
+               if (llsec_key_id_equal(&pos->id, key)) {
+                       list_del_rcu(&pos->list);
+                       llsec_key_put(mkey);
+                       return 0;
+               }
+       }
+
+       return -ENOENT;
+}
+
+
+
+static bool llsec_dev_use_shortaddr(__le16 short_addr)
+{
+       return short_addr != cpu_to_le16(IEEE802154_ADDR_UNDEF) &&
+               short_addr != cpu_to_le16(0xffff);
+}
+
+static u32 llsec_dev_hash_short(__le16 short_addr, __le16 pan_id)
+{
+       return ((__force u16) short_addr) << 16 | (__force u16) pan_id;
+}
+
+static u64 llsec_dev_hash_long(__le64 hwaddr)
+{
+       return (__force u64) hwaddr;
+}
+
+static struct mac802154_llsec_device*
+llsec_dev_find_short(struct mac802154_llsec *sec, __le16 short_addr,
+                    __le16 pan_id)
+{
+       struct mac802154_llsec_device *dev;
+       u32 key = llsec_dev_hash_short(short_addr, pan_id);
+
+       hash_for_each_possible_rcu(sec->devices_short, dev, bucket_s, key) {
+               if (dev->dev.short_addr == short_addr &&
+                   dev->dev.pan_id == pan_id)
+                       return dev;
+       }
+
+       return NULL;
+}
+
+static struct mac802154_llsec_device*
+llsec_dev_find_long(struct mac802154_llsec *sec, __le64 hwaddr)
+{
+       struct mac802154_llsec_device *dev;
+       u64 key = llsec_dev_hash_long(hwaddr);
+
+       hash_for_each_possible_rcu(sec->devices_hw, dev, bucket_hw, key) {
+               if (dev->dev.hwaddr == hwaddr)
+                       return dev;
+       }
+
+       return NULL;
+}
+
+static void llsec_dev_free(struct mac802154_llsec_device *dev)
+{
+       struct ieee802154_llsec_device_key *pos, *pn;
+       struct mac802154_llsec_device_key *devkey;
+
+       list_for_each_entry_safe(pos, pn, &dev->dev.keys, list) {
+               devkey = container_of(pos, struct mac802154_llsec_device_key,
+                                     devkey);
+
+               list_del(&pos->list);
+               kfree(devkey);
+       }
+
+       kfree(dev);
+}
+
+int mac802154_llsec_dev_add(struct mac802154_llsec *sec,
+                           const struct ieee802154_llsec_device *dev)
+{
+       struct mac802154_llsec_device *entry;
+       u32 skey = llsec_dev_hash_short(dev->short_addr, dev->pan_id);
+       u64 hwkey = llsec_dev_hash_long(dev->hwaddr);
+
+       BUILD_BUG_ON(sizeof(hwkey) != IEEE802154_ADDR_LEN);
+
+       if ((llsec_dev_use_shortaddr(dev->short_addr) &&
+            llsec_dev_find_short(sec, dev->short_addr, dev->pan_id)) ||
+            llsec_dev_find_long(sec, dev->hwaddr))
+               return -EEXIST;
+
+       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry)
+               return -ENOMEM;
+
+       entry->dev = *dev;
+       spin_lock_init(&entry->lock);
+       INIT_LIST_HEAD(&entry->dev.keys);
+
+       if (llsec_dev_use_shortaddr(dev->short_addr))
+               hash_add_rcu(sec->devices_short, &entry->bucket_s, skey);
+       else
+               INIT_HLIST_NODE(&entry->bucket_s);
+
+       hash_add_rcu(sec->devices_hw, &entry->bucket_hw, hwkey);
+       list_add_tail_rcu(&entry->dev.list, &sec->table.devices);
+
+       return 0;
+}
+
+static void llsec_dev_free_rcu(struct rcu_head *rcu)
+{
+       llsec_dev_free(container_of(rcu, struct mac802154_llsec_device, rcu));
+}
+
+int mac802154_llsec_dev_del(struct mac802154_llsec *sec, __le64 device_addr)
+{
+       struct mac802154_llsec_device *pos;
+
+       pos = llsec_dev_find_long(sec, device_addr);
+       if (!pos)
+               return -ENOENT;
+
+       hash_del_rcu(&pos->bucket_s);
+       hash_del_rcu(&pos->bucket_hw);
+       call_rcu(&pos->rcu, llsec_dev_free_rcu);
+
+       return 0;
+}
+
+
+
+static struct mac802154_llsec_device_key*
+llsec_devkey_find(struct mac802154_llsec_device *dev,
+                 const struct ieee802154_llsec_key_id *key)
+{
+       struct ieee802154_llsec_device_key *devkey;
+
+       list_for_each_entry_rcu(devkey, &dev->dev.keys, list) {
+               if (!llsec_key_id_equal(key, &devkey->key_id))
+                       continue;
+
+               return container_of(devkey, struct mac802154_llsec_device_key,
+                                   devkey);
+       }
+
+       return NULL;
+}
+
+int mac802154_llsec_devkey_add(struct mac802154_llsec *sec,
+                              __le64 dev_addr,
+                              const struct ieee802154_llsec_device_key *key)
+{
+       struct mac802154_llsec_device *dev;
+       struct mac802154_llsec_device_key *devkey;
+
+       dev = llsec_dev_find_long(sec, dev_addr);
+
+       if (!dev)
+               return -ENOENT;
+
+       if (llsec_devkey_find(dev, &key->key_id))
+               return -EEXIST;
+
+       devkey = kmalloc(sizeof(*devkey), GFP_KERNEL);
+       if (!devkey)
+               return -ENOMEM;
+
+       devkey->devkey = *key;
+       list_add_tail_rcu(&devkey->devkey.list, &dev->dev.keys);
+       return 0;
+}
+
+int mac802154_llsec_devkey_del(struct mac802154_llsec *sec,
+                              __le64 dev_addr,
+                              const struct ieee802154_llsec_device_key *key)
+{
+       struct mac802154_llsec_device *dev;
+       struct mac802154_llsec_device_key *devkey;
+
+       dev = llsec_dev_find_long(sec, dev_addr);
+
+       if (!dev)
+               return -ENOENT;
+
+       devkey = llsec_devkey_find(dev, &key->key_id);
+       if (!devkey)
+               return -ENOENT;
+
+       list_del_rcu(&devkey->devkey.list);
+       kfree_rcu(devkey, rcu);
+       return 0;
+}
+
+
+
+static struct mac802154_llsec_seclevel*
+llsec_find_seclevel(const struct mac802154_llsec *sec,
+                   const struct ieee802154_llsec_seclevel *sl)
+{
+       struct ieee802154_llsec_seclevel *pos;
+
+       list_for_each_entry(pos, &sec->table.security_levels, list) {
+               if (pos->frame_type != sl->frame_type ||
+                   (pos->frame_type == IEEE802154_FC_TYPE_MAC_CMD &&
+                    pos->cmd_frame_id != sl->cmd_frame_id) ||
+                   pos->device_override != sl->device_override ||
+                   pos->sec_levels != sl->sec_levels)
+                       continue;
+
+               return container_of(pos, struct mac802154_llsec_seclevel,
+                                   level);
+       }
+
+       return NULL;
+}
+
+int mac802154_llsec_seclevel_add(struct mac802154_llsec *sec,
+                                const struct ieee802154_llsec_seclevel *sl)
+{
+       struct mac802154_llsec_seclevel *entry;
+
+       if (llsec_find_seclevel(sec, sl))
+               return -EEXIST;
+
+       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry)
+               return -ENOMEM;
+
+       entry->level = *sl;
+
+       list_add_tail_rcu(&entry->level.list, &sec->table.security_levels);
+
+       return 0;
+}
+
+int mac802154_llsec_seclevel_del(struct mac802154_llsec *sec,
+                                const struct ieee802154_llsec_seclevel *sl)
+{
+       struct mac802154_llsec_seclevel *pos;
+
+       pos = llsec_find_seclevel(sec, sl);
+       if (!pos)
+               return -ENOENT;
+
+       list_del_rcu(&pos->level.list);
+       kfree_rcu(pos, rcu);
+
+       return 0;
+}
+
+
+
+static int llsec_recover_addr(struct mac802154_llsec *sec,
+                             struct ieee802154_addr *addr)
+{
+       __le16 caddr = sec->params.coord_shortaddr;
+       addr->pan_id = sec->params.pan_id;
+
+       if (caddr == cpu_to_le16(IEEE802154_ADDR_BROADCAST)) {
+               return -EINVAL;
+       } else if (caddr == cpu_to_le16(IEEE802154_ADDR_UNDEF)) {
+               addr->extended_addr = sec->params.coord_hwaddr;
+               addr->mode = IEEE802154_ADDR_LONG;
+       } else {
+               addr->short_addr = sec->params.coord_shortaddr;
+               addr->mode = IEEE802154_ADDR_SHORT;
+       }
+
+       return 0;
+}
+
+static struct mac802154_llsec_key*
+llsec_lookup_key(struct mac802154_llsec *sec,
+                const struct ieee802154_hdr *hdr,
+                const struct ieee802154_addr *addr,
+                struct ieee802154_llsec_key_id *key_id)
+{
+       struct ieee802154_addr devaddr = *addr;
+       u8 key_id_mode = hdr->sec.key_id_mode;
+       struct ieee802154_llsec_key_entry *key_entry;
+       struct mac802154_llsec_key *key;
+
+       if (key_id_mode == IEEE802154_SCF_KEY_IMPLICIT &&
+           devaddr.mode == IEEE802154_ADDR_NONE) {
+               if (hdr->fc.type == IEEE802154_FC_TYPE_BEACON) {
+                       devaddr.extended_addr = sec->params.coord_hwaddr;
+                       devaddr.mode = IEEE802154_ADDR_LONG;
+               } else if (llsec_recover_addr(sec, &devaddr) < 0) {
+                       return NULL;
+               }
+       }
+
+       list_for_each_entry_rcu(key_entry, &sec->table.keys, list) {
+               const struct ieee802154_llsec_key_id *id = &key_entry->id;
+
+               if (!(key_entry->key->frame_types & BIT(hdr->fc.type)))
+                       continue;
+
+               if (id->mode != key_id_mode)
+                       continue;
+
+               if (key_id_mode == IEEE802154_SCF_KEY_IMPLICIT) {
+                       if (ieee802154_addr_equal(&devaddr, &id->device_addr))
+                               goto found;
+               } else {
+                       if (id->id != hdr->sec.key_id)
+                               continue;
+
+                       if ((key_id_mode == IEEE802154_SCF_KEY_INDEX) ||
+                           (key_id_mode == IEEE802154_SCF_KEY_SHORT_INDEX &&
+                            id->short_source == hdr->sec.short_src) ||
+                           (key_id_mode == IEEE802154_SCF_KEY_HW_INDEX &&
+                            id->extended_source == hdr->sec.extended_src))
+                               goto found;
+               }
+       }
+
+       return NULL;
+
+found:
+       key = container_of(key_entry->key, struct mac802154_llsec_key, key);
+       if (key_id)
+               *key_id = key_entry->id;
+       return llsec_key_get(key);
+}
+
+
+static void llsec_geniv(u8 iv[16], __le64 addr,
+                       const struct ieee802154_sechdr *sec)
+{
+       __be64 addr_bytes = (__force __be64) swab64((__force u64) addr);
+       __be32 frame_counter = (__force __be32) swab32((__force u32) sec->frame_counter);
+
+       iv[0] = 1; /* L' = L - 1 = 1 */
+       memcpy(iv + 1, &addr_bytes, sizeof(addr_bytes));
+       memcpy(iv + 9, &frame_counter, sizeof(frame_counter));
+       iv[13] = sec->level;
+       iv[14] = 0;
+       iv[15] = 1;
+}
+
+static int
+llsec_do_encrypt_unauth(struct sk_buff *skb, const struct mac802154_llsec *sec,
+                       const struct ieee802154_hdr *hdr,
+                       struct mac802154_llsec_key *key)
+{
+       u8 iv[16];
+       struct scatterlist src;
+       struct blkcipher_desc req = {
+               .tfm = key->tfm0,
+               .info = iv,
+               .flags = 0,
+       };
+
+       llsec_geniv(iv, sec->params.hwaddr, &hdr->sec);
+       sg_init_one(&src, skb->data, skb->len);
+       return crypto_blkcipher_encrypt_iv(&req, &src, &src, skb->len);
+}
+
+static struct crypto_aead*
+llsec_tfm_by_len(struct mac802154_llsec_key *key, int authlen)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(key->tfm); i++)
+               if (crypto_aead_authsize(key->tfm[i]) == authlen)
+                       return key->tfm[i];
+
+       BUG();
+}
+
+static int
+llsec_do_encrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec,
+                     const struct ieee802154_hdr *hdr,
+                     struct mac802154_llsec_key *key)
+{
+       u8 iv[16];
+       unsigned char *data;
+       int authlen, assoclen, datalen, rc;
+       struct scatterlist src, assoc[2], dst[2];
+       struct aead_request *req;
+
+       authlen = ieee802154_sechdr_authtag_len(&hdr->sec);
+       llsec_geniv(iv, sec->params.hwaddr, &hdr->sec);
+
+       req = aead_request_alloc(llsec_tfm_by_len(key, authlen), GFP_ATOMIC);
+       if (!req)
+               return -ENOMEM;
+
+       sg_init_table(assoc, 2);
+       sg_set_buf(&assoc[0], skb_mac_header(skb), skb->mac_len);
+       assoclen = skb->mac_len;
+
+       data = skb_mac_header(skb) + skb->mac_len;
+       datalen = skb_tail_pointer(skb) - data;
+
+       if (hdr->sec.level & IEEE802154_SCF_SECLEVEL_ENC) {
+               sg_set_buf(&assoc[1], data, 0);
+       } else {
+               sg_set_buf(&assoc[1], data, datalen);
+               assoclen += datalen;
+               datalen = 0;
+       }
+
+       sg_init_one(&src, data, datalen);
+
+       sg_init_table(dst, 2);
+       sg_set_buf(&dst[0], data, datalen);
+       sg_set_buf(&dst[1], skb_put(skb, authlen), authlen);
+
+       aead_request_set_callback(req, 0, NULL, NULL);
+       aead_request_set_assoc(req, assoc, assoclen);
+       aead_request_set_crypt(req, &src, dst, datalen, iv);
+
+       rc = crypto_aead_encrypt(req);
+
+       kfree(req);
+
+       return rc;
+}
+
+static int llsec_do_encrypt(struct sk_buff *skb,
+                           const struct mac802154_llsec *sec,
+                           const struct ieee802154_hdr *hdr,
+                           struct mac802154_llsec_key *key)
+{
+       if (hdr->sec.level == IEEE802154_SCF_SECLEVEL_ENC)
+               return llsec_do_encrypt_unauth(skb, sec, hdr, key);
+       else
+               return llsec_do_encrypt_auth(skb, sec, hdr, key);
+}
+
+int mac802154_llsec_encrypt(struct mac802154_llsec *sec, struct sk_buff *skb)
+{
+       struct ieee802154_hdr hdr;
+       int rc, authlen, hlen;
+       struct mac802154_llsec_key *key;
+       u32 frame_ctr;
+
+       hlen = ieee802154_hdr_pull(skb, &hdr);
+
+       if (hlen < 0 || hdr.fc.type != IEEE802154_FC_TYPE_DATA)
+               return -EINVAL;
+
+       if (!hdr.fc.security_enabled || hdr.sec.level == 0) {
+               skb_push(skb, hlen);
+               return 0;
+       }
+
+       authlen = ieee802154_sechdr_authtag_len(&hdr.sec);
+
+       if (skb->len + hlen + authlen + IEEE802154_MFR_SIZE > IEEE802154_MTU)
+               return -EMSGSIZE;
+
+       rcu_read_lock();
+
+       read_lock_bh(&sec->lock);
+
+       if (!sec->params.enabled) {
+               rc = -EINVAL;
+               goto fail_read;
+       }
+
+       key = llsec_lookup_key(sec, &hdr, &hdr.dest, NULL);
+       if (!key) {
+               rc = -ENOKEY;
+               goto fail_read;
+       }
+
+       read_unlock_bh(&sec->lock);
+
+       write_lock_bh(&sec->lock);
+
+       frame_ctr = be32_to_cpu(sec->params.frame_counter);
+       hdr.sec.frame_counter = cpu_to_le32(frame_ctr);
+       if (frame_ctr == 0xFFFFFFFF) {
+               write_unlock_bh(&sec->lock);
+               llsec_key_put(key);
+               rc = -EOVERFLOW;
+               goto fail;
+       }
+
+       sec->params.frame_counter = cpu_to_be32(frame_ctr + 1);
+
+       write_unlock_bh(&sec->lock);
+
+       rcu_read_unlock();
+
+       skb->mac_len = ieee802154_hdr_push(skb, &hdr);
+       skb_reset_mac_header(skb);
+
+       rc = llsec_do_encrypt(skb, sec, &hdr, key);
+       llsec_key_put(key);
+
+       return rc;
+
+fail_read:
+       read_unlock_bh(&sec->lock);
+fail:
+       rcu_read_unlock();
+       return rc;
+}
+
+
+
+static struct mac802154_llsec_device*
+llsec_lookup_dev(struct mac802154_llsec *sec,
+                const struct ieee802154_addr *addr)
+{
+       struct ieee802154_addr devaddr = *addr;
+       struct mac802154_llsec_device *dev = NULL;
+
+       if (devaddr.mode == IEEE802154_ADDR_NONE &&
+           llsec_recover_addr(sec, &devaddr) < 0)
+               return NULL;
+
+       if (devaddr.mode == IEEE802154_ADDR_SHORT) {
+               u32 key = llsec_dev_hash_short(devaddr.short_addr,
+                                              devaddr.pan_id);
+
+               hash_for_each_possible_rcu(sec->devices_short, dev,
+                                          bucket_s, key) {
+                       if (dev->dev.pan_id == devaddr.pan_id &&
+                           dev->dev.short_addr == devaddr.short_addr)
+                               return dev;
+               }
+       } else {
+               u64 key = llsec_dev_hash_long(devaddr.extended_addr);
+
+               hash_for_each_possible_rcu(sec->devices_hw, dev,
+                                          bucket_hw, key) {
+                       if (dev->dev.hwaddr == devaddr.extended_addr)
+                               return dev;
+               }
+       }
+
+       return NULL;
+}
+
+static int
+llsec_lookup_seclevel(const struct mac802154_llsec *sec,
+                     u8 frame_type, u8 cmd_frame_id,
+                     struct ieee802154_llsec_seclevel *rlevel)
+{
+       struct ieee802154_llsec_seclevel *level;
+
+       list_for_each_entry_rcu(level, &sec->table.security_levels, list) {
+               if (level->frame_type == frame_type &&
+                   (frame_type != IEEE802154_FC_TYPE_MAC_CMD ||
+                    level->cmd_frame_id == cmd_frame_id)) {
+                       *rlevel = *level;
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int
+llsec_do_decrypt_unauth(struct sk_buff *skb, const struct mac802154_llsec *sec,
+                       const struct ieee802154_hdr *hdr,
+                       struct mac802154_llsec_key *key, __le64 dev_addr)
+{
+       u8 iv[16];
+       unsigned char *data;
+       int datalen;
+       struct scatterlist src;
+       struct blkcipher_desc req = {
+               .tfm = key->tfm0,
+               .info = iv,
+               .flags = 0,
+       };
+
+       llsec_geniv(iv, dev_addr, &hdr->sec);
+       data = skb_mac_header(skb) + skb->mac_len;
+       datalen = skb_tail_pointer(skb) - data;
+
+       sg_init_one(&src, data, datalen);
+
+       return crypto_blkcipher_decrypt_iv(&req, &src, &src, datalen);
+}
+
+static int
+llsec_do_decrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec,
+                     const struct ieee802154_hdr *hdr,
+                     struct mac802154_llsec_key *key, __le64 dev_addr)
+{
+       u8 iv[16];
+       unsigned char *data;
+       int authlen, datalen, assoclen, rc;
+       struct scatterlist src, assoc[2];
+       struct aead_request *req;
+
+       authlen = ieee802154_sechdr_authtag_len(&hdr->sec);
+       llsec_geniv(iv, dev_addr, &hdr->sec);
+
+       req = aead_request_alloc(llsec_tfm_by_len(key, authlen), GFP_ATOMIC);
+       if (!req)
+               return -ENOMEM;
+
+       sg_init_table(assoc, 2);
+       sg_set_buf(&assoc[0], skb_mac_header(skb), skb->mac_len);
+       assoclen = skb->mac_len;
+
+       data = skb_mac_header(skb) + skb->mac_len;
+       datalen = skb_tail_pointer(skb) - data;
+
+       if (hdr->sec.level & IEEE802154_SCF_SECLEVEL_ENC) {
+               sg_set_buf(&assoc[1], data, 0);
+       } else {
+               sg_set_buf(&assoc[1], data, datalen - authlen);
+               assoclen += datalen - authlen;
+               data += datalen - authlen;
+               datalen = authlen;
+       }
+
+       sg_init_one(&src, data, datalen);
+
+       aead_request_set_callback(req, 0, NULL, NULL);
+       aead_request_set_assoc(req, assoc, assoclen);
+       aead_request_set_crypt(req, &src, &src, datalen, iv);
+
+       rc = crypto_aead_decrypt(req);
+
+       kfree(req);
+       skb_trim(skb, skb->len - authlen);
+
+       return rc;
+}
+
+static int
+llsec_do_decrypt(struct sk_buff *skb, const struct mac802154_llsec *sec,
+                const struct ieee802154_hdr *hdr,
+                struct mac802154_llsec_key *key, __le64 dev_addr)
+{
+       if (hdr->sec.level == IEEE802154_SCF_SECLEVEL_ENC)
+               return llsec_do_decrypt_unauth(skb, sec, hdr, key, dev_addr);
+       else
+               return llsec_do_decrypt_auth(skb, sec, hdr, key, dev_addr);
+}
+
+static int
+llsec_update_devkey_record(struct mac802154_llsec_device *dev,
+                          const struct ieee802154_llsec_key_id *in_key)
+{
+       struct mac802154_llsec_device_key *devkey;
+
+       devkey = llsec_devkey_find(dev, in_key);
+
+       if (!devkey) {
+               struct mac802154_llsec_device_key *next;
+
+               next = kzalloc(sizeof(*devkey), GFP_ATOMIC);
+               if (!next)
+                       return -ENOMEM;
+
+               next->devkey.key_id = *in_key;
+
+               spin_lock_bh(&dev->lock);
+
+               devkey = llsec_devkey_find(dev, in_key);
+               if (!devkey)
+                       list_add_rcu(&next->devkey.list, &dev->dev.keys);
+               else
+                       kfree(next);
+
+               spin_unlock_bh(&dev->lock);
+       }
+
+       return 0;
+}
+
+static int
+llsec_update_devkey_info(struct mac802154_llsec_device *dev,
+                        const struct ieee802154_llsec_key_id *in_key,
+                        u32 frame_counter)
+{
+       struct mac802154_llsec_device_key *devkey = NULL;
+
+       if (dev->dev.key_mode == IEEE802154_LLSEC_DEVKEY_RESTRICT) {
+               devkey = llsec_devkey_find(dev, in_key);
+               if (!devkey)
+                       return -ENOENT;
+       }
+
+       if (dev->dev.key_mode == IEEE802154_LLSEC_DEVKEY_RECORD) {
+               int rc = llsec_update_devkey_record(dev, in_key);
+
+               if (rc < 0)
+                       return rc;
+       }
+
+       spin_lock_bh(&dev->lock);
+
+       if ((!devkey && frame_counter < dev->dev.frame_counter) ||
+           (devkey && frame_counter < devkey->devkey.frame_counter)) {
+               spin_unlock_bh(&dev->lock);
+               return -EINVAL;
+       }
+
+       if (devkey)
+               devkey->devkey.frame_counter = frame_counter + 1;
+       else
+               dev->dev.frame_counter = frame_counter + 1;
+
+       spin_unlock_bh(&dev->lock);
+
+       return 0;
+}
+
+int mac802154_llsec_decrypt(struct mac802154_llsec *sec, struct sk_buff *skb)
+{
+       struct ieee802154_hdr hdr;
+       struct mac802154_llsec_key *key;
+       struct ieee802154_llsec_key_id key_id;
+       struct mac802154_llsec_device *dev;
+       struct ieee802154_llsec_seclevel seclevel;
+       int err;
+       __le64 dev_addr;
+       u32 frame_ctr;
+
+       if (ieee802154_hdr_peek(skb, &hdr) < 0)
+               return -EINVAL;
+       if (!hdr.fc.security_enabled)
+               return 0;
+       if (hdr.fc.version == 0)
+               return -EINVAL;
+
+       read_lock_bh(&sec->lock);
+       if (!sec->params.enabled) {
+               read_unlock_bh(&sec->lock);
+               return -EINVAL;
+       }
+       read_unlock_bh(&sec->lock);
+
+       rcu_read_lock();
+
+       key = llsec_lookup_key(sec, &hdr, &hdr.source, &key_id);
+       if (!key) {
+               err = -ENOKEY;
+               goto fail;
+       }
+
+       dev = llsec_lookup_dev(sec, &hdr.source);
+       if (!dev) {
+               err = -EINVAL;
+               goto fail_dev;
+       }
+
+       if (llsec_lookup_seclevel(sec, hdr.fc.type, 0, &seclevel) < 0) {
+               err = -EINVAL;
+               goto fail_dev;
+       }
+
+       if (!(seclevel.sec_levels & BIT(hdr.sec.level)) &&
+           (hdr.sec.level == 0 && seclevel.device_override &&
+            !dev->dev.seclevel_exempt)) {
+               err = -EINVAL;
+               goto fail_dev;
+       }
+
+       frame_ctr = le32_to_cpu(hdr.sec.frame_counter);
+
+       if (frame_ctr == 0xffffffff) {
+               err = -EOVERFLOW;
+               goto fail_dev;
+       }
+
+       err = llsec_update_devkey_info(dev, &key_id, frame_ctr);
+       if (err)
+               goto fail_dev;
+
+       dev_addr = dev->dev.hwaddr;
+
+       rcu_read_unlock();
+
+       err = llsec_do_decrypt(skb, sec, &hdr, key, dev_addr);
+       llsec_key_put(key);
+       return err;
+
+fail_dev:
+       llsec_key_put(key);
+fail:
+       rcu_read_unlock();
+       return err;
+}
diff --git a/net/mac802154/llsec.h b/net/mac802154/llsec.h
new file mode 100644 (file)
index 0000000..950578e
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014 Fraunhofer ITWM
+ *
+ * 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.
+ *
+ * Written by:
+ * Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de>
+ */
+
+#ifndef MAC802154_LLSEC_H
+#define MAC802154_LLSEC_H
+
+#include <linux/slab.h>
+#include <linux/hashtable.h>
+#include <linux/crypto.h>
+#include <linux/kref.h>
+#include <linux/spinlock.h>
+#include <net/af_ieee802154.h>
+#include <net/ieee802154_netdev.h>
+
+struct mac802154_llsec_key {
+       struct ieee802154_llsec_key key;
+
+       /* one tfm for each authsize (4/8/16) */
+       struct crypto_aead *tfm[3];
+       struct crypto_blkcipher *tfm0;
+
+       struct kref ref;
+};
+
+struct mac802154_llsec_device_key {
+       struct ieee802154_llsec_device_key devkey;
+
+       struct rcu_head rcu;
+};
+
+struct mac802154_llsec_device {
+       struct ieee802154_llsec_device dev;
+
+       struct hlist_node bucket_s;
+       struct hlist_node bucket_hw;
+
+       /* protects dev.frame_counter and the elements of dev.keys */
+       spinlock_t lock;
+
+       struct rcu_head rcu;
+};
+
+struct mac802154_llsec_seclevel {
+       struct ieee802154_llsec_seclevel level;
+
+       struct rcu_head rcu;
+};
+
+struct mac802154_llsec {
+       struct ieee802154_llsec_params params;
+       struct ieee802154_llsec_table table;
+
+       DECLARE_HASHTABLE(devices_short, 6);
+       DECLARE_HASHTABLE(devices_hw, 6);
+
+       /* protects params, all other fields are fine with RCU */
+       rwlock_t lock;
+};
+
+void mac802154_llsec_init(struct mac802154_llsec *sec);
+void mac802154_llsec_destroy(struct mac802154_llsec *sec);
+
+int mac802154_llsec_get_params(struct mac802154_llsec *sec,
+                              struct ieee802154_llsec_params *params);
+int mac802154_llsec_set_params(struct mac802154_llsec *sec,
+                              const struct ieee802154_llsec_params *params,
+                              int changed);
+
+int mac802154_llsec_key_add(struct mac802154_llsec *sec,
+                           const struct ieee802154_llsec_key_id *id,
+                           const struct ieee802154_llsec_key *key);
+int mac802154_llsec_key_del(struct mac802154_llsec *sec,
+                           const struct ieee802154_llsec_key_id *key);
+
+int mac802154_llsec_dev_add(struct mac802154_llsec *sec,
+                           const struct ieee802154_llsec_device *dev);
+int mac802154_llsec_dev_del(struct mac802154_llsec *sec,
+                           __le64 device_addr);
+
+int mac802154_llsec_devkey_add(struct mac802154_llsec *sec,
+                              __le64 dev_addr,
+                              const struct ieee802154_llsec_device_key *key);
+int mac802154_llsec_devkey_del(struct mac802154_llsec *sec,
+                              __le64 dev_addr,
+                              const struct ieee802154_llsec_device_key *key);
+
+int mac802154_llsec_seclevel_add(struct mac802154_llsec *sec,
+                                const struct ieee802154_llsec_seclevel *sl);
+int mac802154_llsec_seclevel_del(struct mac802154_llsec *sec,
+                                const struct ieee802154_llsec_seclevel *sl);
+
+int mac802154_llsec_encrypt(struct mac802154_llsec *sec, struct sk_buff *skb);
+int mac802154_llsec_decrypt(struct mac802154_llsec *sec, struct sk_buff *skb);
+
+#endif /* MAC802154_LLSEC_H */
index 28ef59c566e6ee78df73edd1246d84d4c2c4d912..762a6f849c6b7d3edf3e8677ee6f2081c10c8fac 100644 (file)
 #ifndef MAC802154_H
 #define MAC802154_H
 
+#include <linux/mutex.h>
+#include <net/mac802154.h>
 #include <net/ieee802154_netdev.h>
 
+#include "llsec.h"
+
 /* mac802154 device private data */
 struct mac802154_priv {
        struct ieee802154_dev hw;
@@ -90,6 +94,13 @@ struct mac802154_sub_if_data {
        u8 bsn;
        /* MAC DSN field */
        u8 dsn;
+
+       /* protects sec from concurrent access by netlink. access by
+        * encrypt/decrypt/header_create safe without additional protection.
+        */
+       struct mutex sec_mtx;
+
+       struct mac802154_llsec sec;
 };
 
 #define mac802154_to_priv(_hw) container_of(_hw, struct mac802154_priv, hw)
@@ -125,4 +136,37 @@ int mac802154_set_mac_params(struct net_device *dev,
 void mac802154_get_mac_params(struct net_device *dev,
                              struct ieee802154_mac_params *params);
 
+int mac802154_get_params(struct net_device *dev,
+                        struct ieee802154_llsec_params *params);
+int mac802154_set_params(struct net_device *dev,
+                        const struct ieee802154_llsec_params *params,
+                        int changed);
+
+int mac802154_add_key(struct net_device *dev,
+                     const struct ieee802154_llsec_key_id *id,
+                     const struct ieee802154_llsec_key *key);
+int mac802154_del_key(struct net_device *dev,
+                     const struct ieee802154_llsec_key_id *id);
+
+int mac802154_add_dev(struct net_device *dev,
+                     const struct ieee802154_llsec_device *llsec_dev);
+int mac802154_del_dev(struct net_device *dev, __le64 dev_addr);
+
+int mac802154_add_devkey(struct net_device *dev,
+                        __le64 device_addr,
+                        const struct ieee802154_llsec_device_key *key);
+int mac802154_del_devkey(struct net_device *dev,
+                        __le64 device_addr,
+                        const struct ieee802154_llsec_device_key *key);
+
+int mac802154_add_seclevel(struct net_device *dev,
+                          const struct ieee802154_llsec_seclevel *sl);
+int mac802154_del_seclevel(struct net_device *dev,
+                          const struct ieee802154_llsec_seclevel *sl);
+
+void mac802154_lock_table(struct net_device *dev);
+void mac802154_get_table(struct net_device *dev,
+                        struct ieee802154_llsec_table **t);
+void mac802154_unlock_table(struct net_device *dev);
+
 #endif /* MAC802154_H */
index d40c0928bc622d5802c9dcd1bdb740150ee06744..bf809131eef776209040a8496ed1e2214e6bdebe 100644 (file)
@@ -40,6 +40,9 @@ static int mac802154_mlme_start_req(struct net_device *dev,
                                    u8 pan_coord, u8 blx,
                                    u8 coord_realign)
 {
+       struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       int rc = 0;
+
        BUG_ON(addr->mode != IEEE802154_ADDR_SHORT);
 
        mac802154_dev_set_pan_id(dev, addr->pan_id);
@@ -47,12 +50,31 @@ static int mac802154_mlme_start_req(struct net_device *dev,
        mac802154_dev_set_ieee_addr(dev);
        mac802154_dev_set_page_channel(dev, page, channel);
 
+       if (ops->llsec) {
+               struct ieee802154_llsec_params params;
+               int changed = 0;
+
+               params.coord_shortaddr = addr->short_addr;
+               changed |= IEEE802154_LLSEC_PARAM_COORD_SHORTADDR;
+
+               params.pan_id = addr->pan_id;
+               changed |= IEEE802154_LLSEC_PARAM_PAN_ID;
+
+               params.hwaddr = ieee802154_devaddr_from_raw(dev->dev_addr);
+               changed |= IEEE802154_LLSEC_PARAM_HWADDR;
+
+               params.coord_hwaddr = params.hwaddr;
+               changed |= IEEE802154_LLSEC_PARAM_COORD_HWADDR;
+
+               rc = ops->llsec->set_params(dev, &params, changed);
+       }
+
        /* FIXME: add validation for unused parameters to be sane
         * for SoftMAC
         */
        ieee802154_nl_start_confirm(dev, IEEE802154_SUCCESS);
 
-       return 0;
+       return rc;
 }
 
 static struct wpan_phy *mac802154_get_phy(const struct net_device *dev)
@@ -64,6 +86,22 @@ static struct wpan_phy *mac802154_get_phy(const struct net_device *dev)
        return to_phy(get_device(&priv->hw->phy->dev));
 }
 
+static struct ieee802154_llsec_ops mac802154_llsec_ops = {
+       .get_params = mac802154_get_params,
+       .set_params = mac802154_set_params,
+       .add_key = mac802154_add_key,
+       .del_key = mac802154_del_key,
+       .add_dev = mac802154_add_dev,
+       .del_dev = mac802154_del_dev,
+       .add_devkey = mac802154_add_devkey,
+       .del_devkey = mac802154_del_devkey,
+       .add_seclevel = mac802154_add_seclevel,
+       .del_seclevel = mac802154_del_seclevel,
+       .lock_table = mac802154_lock_table,
+       .get_table = mac802154_get_table,
+       .unlock_table = mac802154_unlock_table,
+};
+
 struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced = {
        .get_phy = mac802154_get_phy,
 };
@@ -75,6 +113,8 @@ struct ieee802154_mlme_ops mac802154_mlme_wpan = {
        .get_short_addr = mac802154_dev_get_short_addr,
        .get_dsn = mac802154_dev_get_dsn,
 
+       .llsec = &mac802154_llsec_ops,
+
        .set_mac_params = mac802154_set_mac_params,
        .get_mac_params = mac802154_get_mac_params,
 };
index f0991f2344d403f3b1ed3a1f44fc2aebf50c72b4..15aa2f2b03a78c29138db43c08073c4ba2817e54 100644 (file)
@@ -213,3 +213,190 @@ void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan)
        } else
                mutex_unlock(&priv->hw->phy->pib_lock);
 }
+
+
+int mac802154_get_params(struct net_device *dev,
+                        struct ieee802154_llsec_params *params)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       int res;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+       res = mac802154_llsec_get_params(&priv->sec, params);
+       mutex_unlock(&priv->sec_mtx);
+
+       return res;
+}
+
+int mac802154_set_params(struct net_device *dev,
+                        const struct ieee802154_llsec_params *params,
+                        int changed)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       int res;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+       res = mac802154_llsec_set_params(&priv->sec, params, changed);
+       mutex_unlock(&priv->sec_mtx);
+
+       return res;
+}
+
+
+int mac802154_add_key(struct net_device *dev,
+                     const struct ieee802154_llsec_key_id *id,
+                     const struct ieee802154_llsec_key *key)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       int res;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+       res = mac802154_llsec_key_add(&priv->sec, id, key);
+       mutex_unlock(&priv->sec_mtx);
+
+       return res;
+}
+
+int mac802154_del_key(struct net_device *dev,
+                     const struct ieee802154_llsec_key_id *id)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       int res;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+       res = mac802154_llsec_key_del(&priv->sec, id);
+       mutex_unlock(&priv->sec_mtx);
+
+       return res;
+}
+
+
+int mac802154_add_dev(struct net_device *dev,
+                     const struct ieee802154_llsec_device *llsec_dev)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       int res;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+       res = mac802154_llsec_dev_add(&priv->sec, llsec_dev);
+       mutex_unlock(&priv->sec_mtx);
+
+       return res;
+}
+
+int mac802154_del_dev(struct net_device *dev, __le64 dev_addr)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       int res;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+       res = mac802154_llsec_dev_del(&priv->sec, dev_addr);
+       mutex_unlock(&priv->sec_mtx);
+
+       return res;
+}
+
+
+int mac802154_add_devkey(struct net_device *dev,
+                        __le64 device_addr,
+                        const struct ieee802154_llsec_device_key *key)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       int res;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+       res = mac802154_llsec_devkey_add(&priv->sec, device_addr, key);
+       mutex_unlock(&priv->sec_mtx);
+
+       return res;
+}
+
+int mac802154_del_devkey(struct net_device *dev,
+                        __le64 device_addr,
+                        const struct ieee802154_llsec_device_key *key)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       int res;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+       res = mac802154_llsec_devkey_del(&priv->sec, device_addr, key);
+       mutex_unlock(&priv->sec_mtx);
+
+       return res;
+}
+
+
+int mac802154_add_seclevel(struct net_device *dev,
+                          const struct ieee802154_llsec_seclevel *sl)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       int res;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+       res = mac802154_llsec_seclevel_add(&priv->sec, sl);
+       mutex_unlock(&priv->sec_mtx);
+
+       return res;
+}
+
+int mac802154_del_seclevel(struct net_device *dev,
+                          const struct ieee802154_llsec_seclevel *sl)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       int res;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+       res = mac802154_llsec_seclevel_del(&priv->sec, sl);
+       mutex_unlock(&priv->sec_mtx);
+
+       return res;
+}
+
+
+void mac802154_lock_table(struct net_device *dev)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+}
+
+void mac802154_get_table(struct net_device *dev,
+                        struct ieee802154_llsec_table **t)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       *t = &priv->sec.table;
+}
+
+void mac802154_unlock_table(struct net_device *dev)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_unlock(&priv->sec_mtx);
+}
index 434a26f76a80f69c058d9c2a61181bede5333147..a68230e2b25f53b14907dac5bfd12d5cb09d1cab 100644 (file)
@@ -70,7 +70,8 @@ void mac802154_monitors_rx(struct mac802154_priv *priv, struct sk_buff *skb)
 
        rcu_read_lock();
        list_for_each_entry_rcu(sdata, &priv->slaves, list) {
-               if (sdata->type != IEEE802154_DEV_MONITOR)
+               if (sdata->type != IEEE802154_DEV_MONITOR ||
+                   !netif_running(sdata->dev))
                        continue;
 
                skb2 = skb_clone(skb, GFP_ATOMIC);
index 03855b0677ccf8efcb0819591bae63bd8609c693..7f820a108a9cf3461a46a57522251ea4edc51003 100644 (file)
@@ -59,27 +59,28 @@ mac802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb, u8 lqi)
        skb->protocol = htons(ETH_P_IEEE802154);
        skb_reset_mac_header(skb);
 
-       BUILD_BUG_ON(sizeof(struct ieee802154_mac_cb) > sizeof(skb->cb));
-
        if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) {
                u16 crc;
 
                if (skb->len < 2) {
                        pr_debug("got invalid frame\n");
-                       goto out;
+                       goto fail;
                }
                crc = crc_ccitt(0, skb->data, skb->len);
                if (crc) {
                        pr_debug("CRC mismatch\n");
-                       goto out;
+                       goto fail;
                }
                skb_trim(skb, skb->len - 2); /* CRC */
        }
 
        mac802154_monitors_rx(priv, skb);
        mac802154_wpans_rx(priv, skb);
-out:
-       dev_kfree_skb(skb);
+
+       return;
+
+fail:
+       kfree_skb(skb);
 }
 
 static void mac802154_rx_worker(struct work_struct *work)
index 1df7a6a573865b4add87261300cc1d7ff01fa243..3c3069fd69718277fc719e82d7bf2c51bb9747db 100644 (file)
 
 #include "mac802154.h"
 
+static int mac802154_wpan_update_llsec(struct net_device *dev)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       int rc = 0;
+
+       if (ops->llsec) {
+               struct ieee802154_llsec_params params;
+               int changed = 0;
+
+               params.pan_id = priv->pan_id;
+               changed |= IEEE802154_LLSEC_PARAM_PAN_ID;
+
+               params.hwaddr = priv->extended_addr;
+               changed |= IEEE802154_LLSEC_PARAM_HWADDR;
+
+               rc = ops->llsec->set_params(dev, &params, changed);
+       }
+
+       return rc;
+}
+
 static int
 mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
@@ -81,7 +103,7 @@ mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                priv->pan_id = cpu_to_le16(sa->addr.pan_id);
                priv->short_addr = cpu_to_le16(sa->addr.short_addr);
 
-               err = 0;
+               err = mac802154_wpan_update_llsec(dev);
                break;
        }
 
@@ -99,7 +121,7 @@ static int mac802154_wpan_mac_addr(struct net_device *dev, void *p)
        /* FIXME: validate addr */
        memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
        mac802154_dev_set_ieee_addr(dev);
-       return 0;
+       return mac802154_wpan_update_llsec(dev);
 }
 
 int mac802154_set_mac_params(struct net_device *dev,
@@ -124,7 +146,7 @@ void mac802154_get_mac_params(struct net_device *dev,
        mutex_unlock(&priv->hw->slaves_mtx);
 }
 
-int mac802154_wpan_open(struct net_device *dev)
+static int mac802154_wpan_open(struct net_device *dev)
 {
        int rc;
        struct mac802154_sub_if_data *priv = netdev_priv(dev);
@@ -183,6 +205,38 @@ out:
        return rc;
 }
 
+static int mac802154_set_header_security(struct mac802154_sub_if_data *priv,
+                                        struct ieee802154_hdr *hdr,
+                                        const struct ieee802154_mac_cb *cb)
+{
+       struct ieee802154_llsec_params params;
+       u8 level;
+
+       mac802154_llsec_get_params(&priv->sec, &params);
+
+       if (!params.enabled && cb->secen_override && cb->secen)
+               return -EINVAL;
+       if (!params.enabled ||
+           (cb->secen_override && !cb->secen) ||
+           !params.out_level)
+               return 0;
+       if (cb->seclevel_override && !cb->seclevel)
+               return -EINVAL;
+
+       level = cb->seclevel_override ? cb->seclevel : params.out_level;
+
+       hdr->fc.security_enabled = 1;
+       hdr->sec.level = level;
+       hdr->sec.key_id_mode = params.out_key.mode;
+       if (params.out_key.mode == IEEE802154_SCF_KEY_SHORT_INDEX)
+               hdr->sec.short_src = params.out_key.short_source;
+       else if (params.out_key.mode == IEEE802154_SCF_KEY_HW_INDEX)
+               hdr->sec.extended_src = params.out_key.extended_source;
+       hdr->sec.key_id = params.out_key.id;
+
+       return 0;
+}
+
 static int mac802154_header_create(struct sk_buff *skb,
                                   struct net_device *dev,
                                   unsigned short type,
@@ -192,15 +246,20 @@ static int mac802154_header_create(struct sk_buff *skb,
 {
        struct ieee802154_hdr hdr;
        struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       struct ieee802154_mac_cb *cb = mac_cb(skb);
        int hlen;
 
        if (!daddr)
                return -EINVAL;
 
        memset(&hdr.fc, 0, sizeof(hdr.fc));
-       hdr.fc.type = mac_cb_type(skb);
-       hdr.fc.security_enabled = mac_cb_is_secen(skb);
-       hdr.fc.ack_request = mac_cb_is_ackreq(skb);
+       hdr.fc.type = cb->type;
+       hdr.fc.security_enabled = cb->secen;
+       hdr.fc.ack_request = cb->ackreq;
+       hdr.seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
+
+       if (mac802154_set_header_security(priv, &hdr, cb) < 0)
+               return -EINVAL;
 
        if (!saddr) {
                spin_lock_bh(&priv->mib_lock);
@@ -231,7 +290,7 @@ static int mac802154_header_create(struct sk_buff *skb,
        skb_reset_mac_header(skb);
        skb->mac_len = hlen;
 
-       if (hlen + len + 2 > dev->mtu)
+       if (len > ieee802154_max_payload(&hdr))
                return -EMSGSIZE;
 
        return hlen;
@@ -257,6 +316,7 @@ mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct mac802154_sub_if_data *priv;
        u8 chan, page;
+       int rc;
 
        priv = netdev_priv(dev);
 
@@ -272,6 +332,13 @@ mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev)
                return NETDEV_TX_OK;
        }
 
+       rc = mac802154_llsec_encrypt(&priv->sec, skb);
+       if (rc) {
+               pr_warn("encryption failed: %i\n", rc);
+               kfree_skb(skb);
+               return NETDEV_TX_OK;
+       }
+
        skb->skb_iif = dev->ifindex;
        dev->stats.tx_packets++;
        dev->stats.tx_bytes += skb->len;
@@ -292,6 +359,15 @@ static const struct net_device_ops mac802154_wpan_ops = {
        .ndo_set_mac_address    = mac802154_wpan_mac_addr,
 };
 
+static void mac802154_wpan_free(struct net_device *dev)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+
+       mac802154_llsec_destroy(&priv->sec);
+
+       free_netdev(dev);
+}
+
 void mac802154_wpan_setup(struct net_device *dev)
 {
        struct mac802154_sub_if_data *priv;
@@ -301,14 +377,14 @@ void mac802154_wpan_setup(struct net_device *dev)
 
        dev->hard_header_len    = MAC802154_FRAME_HARD_HEADER_LEN;
        dev->header_ops         = &mac802154_header_ops;
-       dev->needed_tailroom    = 2; /* FCS */
+       dev->needed_tailroom    = 2 + 16; /* FCS + MIC */
        dev->mtu                = IEEE802154_MTU;
        dev->tx_queue_len       = 300;
        dev->type               = ARPHRD_IEEE802154;
        dev->flags              = IFF_NOARP | IFF_BROADCAST;
        dev->watchdog_timeo     = 0;
 
-       dev->destructor         = free_netdev;
+       dev->destructor         = mac802154_wpan_free;
        dev->netdev_ops         = &mac802154_wpan_ops;
        dev->ml_priv            = &mac802154_mlme_wpan;
 
@@ -319,6 +395,7 @@ void mac802154_wpan_setup(struct net_device *dev)
        priv->page = 0;
 
        spin_lock_init(&priv->mib_lock);
+       mutex_init(&priv->sec_mtx);
 
        get_random_bytes(&priv->bsn, 1);
        get_random_bytes(&priv->dsn, 1);
@@ -331,6 +408,8 @@ void mac802154_wpan_setup(struct net_device *dev)
 
        priv->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
        priv->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
+
+       mac802154_llsec_init(&priv->sec);
 }
 
 static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb)
@@ -339,9 +418,11 @@ static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb)
 }
 
 static int
-mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb)
+mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb,
+                     const struct ieee802154_hdr *hdr)
 {
        __le16 span, sshort;
+       int rc;
 
        pr_debug("getting packet via slave interface %s\n", sdata->dev->name);
 
@@ -388,15 +469,22 @@ mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb)
 
        skb->dev = sdata->dev;
 
+       rc = mac802154_llsec_decrypt(&sdata->sec, skb);
+       if (rc) {
+               pr_debug("decryption failed: %i\n", rc);
+               kfree_skb(skb);
+               return NET_RX_DROP;
+       }
+
        sdata->dev->stats.rx_packets++;
        sdata->dev->stats.rx_bytes += skb->len;
 
-       switch (mac_cb_type(skb)) {
+       switch (mac_cb(skb)->type) {
        case IEEE802154_FC_TYPE_DATA:
                return mac802154_process_data(sdata->dev, skb);
        default:
                pr_warn("ieee802154: bad frame received (type = %d)\n",
-                       mac_cb_type(skb));
+                       mac_cb(skb)->type);
                kfree_skb(skb);
                return NET_RX_DROP;
        }
@@ -419,62 +507,58 @@ static void mac802154_print_addr(const char *name,
        }
 }
 
-static int mac802154_parse_frame_start(struct sk_buff *skb)
+static int mac802154_parse_frame_start(struct sk_buff *skb,
+                                      struct ieee802154_hdr *hdr)
 {
        int hlen;
-       struct ieee802154_hdr hdr;
+       struct ieee802154_mac_cb *cb = mac_cb_init(skb);
 
-       hlen = ieee802154_hdr_pull(skb, &hdr);
+       hlen = ieee802154_hdr_pull(skb, hdr);
        if (hlen < 0)
                return -EINVAL;
 
        skb->mac_len = hlen;
 
-       pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr.fc),
-                hdr.seq);
-
-       mac_cb(skb)->flags = hdr.fc.type;
+       pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr->fc),
+                hdr->seq);
 
-       if (hdr.fc.ack_request)
-               mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ;
-       if (hdr.fc.security_enabled)
-               mac_cb(skb)->flags |= MAC_CB_FLAG_SECEN;
+       cb->type = hdr->fc.type;
+       cb->ackreq = hdr->fc.ack_request;
+       cb->secen = hdr->fc.security_enabled;
 
-       mac802154_print_addr("destination", &hdr.dest);
-       mac802154_print_addr("source", &hdr.source);
+       mac802154_print_addr("destination", &hdr->dest);
+       mac802154_print_addr("source", &hdr->source);
 
-       mac_cb(skb)->source = hdr.source;
-       mac_cb(skb)->dest = hdr.dest;
+       cb->source = hdr->source;
+       cb->dest = hdr->dest;
 
-       if (hdr.fc.security_enabled) {
+       if (hdr->fc.security_enabled) {
                u64 key;
 
-               pr_debug("seclevel %i\n", hdr.sec.level);
+               pr_debug("seclevel %i\n", hdr->sec.level);
 
-               switch (hdr.sec.key_id_mode) {
+               switch (hdr->sec.key_id_mode) {
                case IEEE802154_SCF_KEY_IMPLICIT:
                        pr_debug("implicit key\n");
                        break;
 
                case IEEE802154_SCF_KEY_INDEX:
-                       pr_debug("key %02x\n", hdr.sec.key_id);
+                       pr_debug("key %02x\n", hdr->sec.key_id);
                        break;
 
                case IEEE802154_SCF_KEY_SHORT_INDEX:
                        pr_debug("key %04x:%04x %02x\n",
-                                le32_to_cpu(hdr.sec.short_src) >> 16,
-                                le32_to_cpu(hdr.sec.short_src) & 0xffff,
-                                hdr.sec.key_id);
+                                le32_to_cpu(hdr->sec.short_src) >> 16,
+                                le32_to_cpu(hdr->sec.short_src) & 0xffff,
+                                hdr->sec.key_id);
                        break;
 
                case IEEE802154_SCF_KEY_HW_INDEX:
-                       key = swab64((__force u64) hdr.sec.extended_src);
+                       key = swab64((__force u64) hdr->sec.extended_src);
                        pr_debug("key source %8phC %02x\n", &key,
-                                hdr.sec.key_id);
+                                hdr->sec.key_id);
                        break;
                }
-
-               return -EINVAL;
        }
 
        return 0;
@@ -483,10 +567,10 @@ static int mac802154_parse_frame_start(struct sk_buff *skb)
 void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb)
 {
        int ret;
-       struct sk_buff *sskb;
        struct mac802154_sub_if_data *sdata;
+       struct ieee802154_hdr hdr;
 
-       ret = mac802154_parse_frame_start(skb);
+       ret = mac802154_parse_frame_start(skb, &hdr);
        if (ret) {
                pr_debug("got invalid frame\n");
                return;
@@ -494,12 +578,16 @@ void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb)
 
        rcu_read_lock();
        list_for_each_entry_rcu(sdata, &priv->slaves, list) {
-               if (sdata->type != IEEE802154_DEV_WPAN)
+               if (sdata->type != IEEE802154_DEV_WPAN ||
+                   !netif_running(sdata->dev))
                        continue;
 
-               sskb = skb_clone(skb, GFP_ATOMIC);
-               if (sskb)
-                       mac802154_subif_frame(sdata, sskb);
+               mac802154_subif_frame(sdata, skb, &hdr);
+               skb = NULL;
+               break;
        }
        rcu_read_unlock();
+
+       if (skb)
+               kfree_skb(skb);
 }
index 851cd880b0c048c4dcd45e1b6652888338490cf9..6b38d083e1c973c55e18914748d49fe4968a7188 100644 (file)
@@ -33,6 +33,7 @@ static struct sk_buff *mpls_gso_segment(struct sk_buff *skb,
                                  SKB_GSO_DODGY |
                                  SKB_GSO_TCP_ECN |
                                  SKB_GSO_GRE |
+                                 SKB_GSO_GRE_CSUM |
                                  SKB_GSO_IPIP |
                                  SKB_GSO_MPLS)))
                goto out;
index 117208321f16997af9f24bee1f86519f8f8fa26e..ec8114fae50b297f56760c3ac22d378c31957cd4 100644 (file)
@@ -271,10 +271,7 @@ ip_set_free(void *members)
 {
        pr_debug("%p: free with %s\n", members,
                 is_vmalloc_addr(members) ? "vfree" : "kfree");
-       if (is_vmalloc_addr(members))
-               vfree(members);
-       else
-               kfree(members);
+       kvfree(members);
 }
 EXPORT_SYMBOL_GPL(ip_set_free);
 
index 3d2d2c8108cac8d681e1e0840c89eb2312fee6d6..e6836755c45d4168bb6ccc9ab6f12708e9e17f3d 100644 (file)
@@ -97,7 +97,7 @@ const char *ip_vs_proto_name(unsigned int proto)
                return "ICMPv6";
 #endif
        default:
-               sprintf(buf, "IP_%d", proto);
+               sprintf(buf, "IP_%u", proto);
                return buf;
        }
 }
index c47444e4cf8ccc9977fa0622689b4fb55799ff4b..73ba1cc7a88dd6a1126a0aaae3525ee2ce1f0fda 100644 (file)
@@ -562,7 +562,7 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
        ip_send_check(iph);
 
        /* Another hack: avoid icmp_send in ip_fragment */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 0);
        rcu_read_unlock();
@@ -590,7 +590,7 @@ ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
                goto tx_error;
 
        /* Another hack: avoid icmp_send in ip_fragment */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 0);
        rcu_read_unlock();
@@ -684,7 +684,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
           MTU problem. */
 
        /* Another hack: avoid icmp_send in ip_fragment */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        rc = ip_vs_nat_send_or_cont(NFPROTO_IPV4, skb, cp, local);
        rcu_read_unlock();
@@ -774,7 +774,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
           MTU problem. */
 
        /* Another hack: avoid icmp_send in ip_fragment */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        rc = ip_vs_nat_send_or_cont(NFPROTO_IPV6, skb, cp, local);
        rcu_read_unlock();
@@ -883,10 +883,10 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
        iph->daddr              =       cp->daddr.ip;
        iph->saddr              =       saddr;
        iph->ttl                =       old_iph->ttl;
-       ip_select_ident(skb, &rt->dst, NULL);
+       ip_select_ident(skb, NULL);
 
        /* Another hack: avoid icmp_send in ip_fragment */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        ret = ip_vs_tunnel_xmit_prepare(skb, cp);
        if (ret == NF_ACCEPT)
@@ -974,7 +974,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
        iph->hop_limit          =       old_iph->hop_limit;
 
        /* Another hack: avoid icmp_send in ip_fragment */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        ret = ip_vs_tunnel_xmit_prepare(skb, cp);
        if (ret == NF_ACCEPT)
@@ -1023,7 +1023,7 @@ ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
        ip_send_check(ip_hdr(skb));
 
        /* Another hack: avoid icmp_send in ip_fragment */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 0);
        rcu_read_unlock();
@@ -1060,7 +1060,7 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
        }
 
        /* Another hack: avoid icmp_send in ip_fragment */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 0);
        rcu_read_unlock();
@@ -1157,7 +1157,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
        ip_vs_nat_icmp(skb, pp, cp, 0);
 
        /* Another hack: avoid icmp_send in ip_fragment */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        rc = ip_vs_nat_send_or_cont(NFPROTO_IPV4, skb, cp, local);
        rcu_read_unlock();
@@ -1249,7 +1249,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
        ip_vs_nat_icmp_v6(skb, pp, cp, 0);
 
        /* Another hack: avoid icmp_send in ip_fragment */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        rc = ip_vs_nat_send_or_cont(NFPROTO_IPV6, skb, cp, local);
        rcu_read_unlock();
index 52ca952b802c5e3ea41c83823b90f74365b8fc76..09096a670c45b6da72fcef70646f58139ce2af39 100644 (file)
@@ -358,6 +358,19 @@ out:
        rcu_read_unlock();
 }
 
+struct nf_conn_nat *nf_ct_nat_ext_add(struct nf_conn *ct)
+{
+       struct nf_conn_nat *nat = nfct_nat(ct);
+       if (nat)
+               return nat;
+
+       if (!nf_ct_is_confirmed(ct))
+               nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
+
+       return nat;
+}
+EXPORT_SYMBOL_GPL(nf_ct_nat_ext_add);
+
 unsigned int
 nf_nat_setup_info(struct nf_conn *ct,
                  const struct nf_nat_range *range,
@@ -368,14 +381,9 @@ nf_nat_setup_info(struct nf_conn *ct,
        struct nf_conn_nat *nat;
 
        /* nat helper or nfctnetlink also setup binding */
-       nat = nfct_nat(ct);
-       if (!nat) {
-               nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
-               if (nat == NULL) {
-                       pr_debug("failed to add NAT extension\n");
-                       return NF_ACCEPT;
-               }
-       }
+       nat = nf_ct_nat_ext_add(ct);
+       if (nat == NULL)
+               return NF_ACCEPT;
 
        NF_CT_ASSERT(maniptype == NF_NAT_MANIP_SRC ||
                     maniptype == NF_NAT_MANIP_DST);
index 3fd159db9f06bc31d3da81d8cb3e5e8f2bb3ebc2..624e083125b93b8755819f868c90307f04cd74b8 100644 (file)
@@ -88,6 +88,45 @@ nf_tables_afinfo_lookup(struct net *net, int family, bool autoload)
        return ERR_PTR(-EAFNOSUPPORT);
 }
 
+static void nft_ctx_init(struct nft_ctx *ctx,
+                        const struct sk_buff *skb,
+                        const struct nlmsghdr *nlh,
+                        struct nft_af_info *afi,
+                        struct nft_table *table,
+                        struct nft_chain *chain,
+                        const struct nlattr * const *nla)
+{
+       ctx->net        = sock_net(skb->sk);
+       ctx->afi        = afi;
+       ctx->table      = table;
+       ctx->chain      = chain;
+       ctx->nla        = nla;
+       ctx->portid     = NETLINK_CB(skb).portid;
+       ctx->report     = nlmsg_report(nlh);
+       ctx->seq        = nlh->nlmsg_seq;
+}
+
+static struct nft_trans *nft_trans_alloc(struct nft_ctx *ctx, int msg_type,
+                                        u32 size)
+{
+       struct nft_trans *trans;
+
+       trans = kzalloc(sizeof(struct nft_trans) + size, GFP_KERNEL);
+       if (trans == NULL)
+               return NULL;
+
+       trans->msg_type = msg_type;
+       trans->ctx      = *ctx;
+
+       return trans;
+}
+
+static void nft_trans_destroy(struct nft_trans *trans)
+{
+       list_del(&trans->list);
+       kfree(trans);
+}
+
 /*
  * Tables
  */
@@ -197,20 +236,13 @@ nla_put_failure:
        return -1;
 }
 
-static int nf_tables_table_notify(const struct sk_buff *oskb,
-                                 const struct nlmsghdr *nlh,
-                                 const struct nft_table *table,
-                                 int event, int family)
+static int nf_tables_table_notify(const struct nft_ctx *ctx, int event)
 {
        struct sk_buff *skb;
-       u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
-       u32 seq = nlh ? nlh->nlmsg_seq : 0;
-       struct net *net = oskb ? sock_net(oskb->sk) : &init_net;
-       bool report;
        int err;
 
-       report = nlh ? nlmsg_report(nlh) : false;
-       if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
+       if (!ctx->report &&
+           !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
                return 0;
 
        err = -ENOBUFS;
@@ -218,18 +250,20 @@ static int nf_tables_table_notify(const struct sk_buff *oskb,
        if (skb == NULL)
                goto err;
 
-       err = nf_tables_fill_table_info(skb, portid, seq, event, 0,
-                                       family, table);
+       err = nf_tables_fill_table_info(skb, ctx->portid, ctx->seq, event, 0,
+                                       ctx->afi->family, ctx->table);
        if (err < 0) {
                kfree_skb(skb);
                goto err;
        }
 
-       err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report,
-                            GFP_KERNEL);
+       err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
+                            ctx->report, GFP_KERNEL);
 err:
-       if (err < 0)
-               nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
+       if (err < 0) {
+               nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES,
+                                 err);
+       }
        return err;
 }
 
@@ -269,6 +303,9 @@ done:
        return skb->len;
 }
 
+/* Internal table flags */
+#define NFT_TABLE_INACTIVE     (1 << 15)
+
 static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
                              const struct nlmsghdr *nlh,
                              const struct nlattr * const nla[])
@@ -295,6 +332,8 @@ static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
        table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
        if (IS_ERR(table))
                return PTR_ERR(table);
+       if (table->flags & NFT_TABLE_INACTIVE)
+               return -ENOENT;
 
        skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
        if (!skb2)
@@ -343,7 +382,7 @@ err:
        return err;
 }
 
-static int nf_tables_table_disable(const struct nft_af_info *afi,
+static void nf_tables_table_disable(const struct nft_af_info *afi,
                                   struct nft_table *table)
 {
        struct nft_chain *chain;
@@ -353,45 +392,63 @@ static int nf_tables_table_disable(const struct nft_af_info *afi,
                        nf_unregister_hooks(nft_base_chain(chain)->ops,
                                            afi->nops);
        }
-
-       return 0;
 }
 
-static int nf_tables_updtable(struct sock *nlsk, struct sk_buff *skb,
-                             const struct nlmsghdr *nlh,
-                             const struct nlattr * const nla[],
-                             struct nft_af_info *afi, struct nft_table *table)
+static int nf_tables_updtable(struct nft_ctx *ctx)
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       int family = nfmsg->nfgen_family, ret = 0;
+       struct nft_trans *trans;
+       u32 flags;
+       int ret = 0;
 
-       if (nla[NFTA_TABLE_FLAGS]) {
-               u32 flags;
+       if (!ctx->nla[NFTA_TABLE_FLAGS])
+               return 0;
 
-               flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS]));
-               if (flags & ~NFT_TABLE_F_DORMANT)
-                       return -EINVAL;
+       flags = ntohl(nla_get_be32(ctx->nla[NFTA_TABLE_FLAGS]));
+       if (flags & ~NFT_TABLE_F_DORMANT)
+               return -EINVAL;
+
+       trans = nft_trans_alloc(ctx, NFT_MSG_NEWTABLE,
+                               sizeof(struct nft_trans_table));
+       if (trans == NULL)
+               return -ENOMEM;
 
-               if ((flags & NFT_TABLE_F_DORMANT) &&
-                   !(table->flags & NFT_TABLE_F_DORMANT)) {
-                       ret = nf_tables_table_disable(afi, table);
-                       if (ret >= 0)
-                               table->flags |= NFT_TABLE_F_DORMANT;
-               } else if (!(flags & NFT_TABLE_F_DORMANT) &&
-                          table->flags & NFT_TABLE_F_DORMANT) {
-                       ret = nf_tables_table_enable(afi, table);
-                       if (ret >= 0)
-                               table->flags &= ~NFT_TABLE_F_DORMANT;
+       if ((flags & NFT_TABLE_F_DORMANT) &&
+           !(ctx->table->flags & NFT_TABLE_F_DORMANT)) {
+               nft_trans_table_enable(trans) = false;
+       } else if (!(flags & NFT_TABLE_F_DORMANT) &&
+                  ctx->table->flags & NFT_TABLE_F_DORMANT) {
+               ret = nf_tables_table_enable(ctx->afi, ctx->table);
+               if (ret >= 0) {
+                       ctx->table->flags &= ~NFT_TABLE_F_DORMANT;
+                       nft_trans_table_enable(trans) = true;
                }
-               if (ret < 0)
-                       goto err;
        }
+       if (ret < 0)
+               goto err;
 
-       nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family);
+       nft_trans_table_update(trans) = true;
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       return 0;
 err:
+       nft_trans_destroy(trans);
        return ret;
 }
 
+static int nft_trans_table_add(struct nft_ctx *ctx, int msg_type)
+{
+       struct nft_trans *trans;
+
+       trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_table));
+       if (trans == NULL)
+               return -ENOMEM;
+
+       if (msg_type == NFT_MSG_NEWTABLE)
+               ctx->table->flags |= NFT_TABLE_INACTIVE;
+
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       return 0;
+}
+
 static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
                              const struct nlmsghdr *nlh,
                              const struct nlattr * const nla[])
@@ -403,6 +460,8 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
        struct net *net = sock_net(skb->sk);
        int family = nfmsg->nfgen_family;
        u32 flags = 0;
+       struct nft_ctx ctx;
+       int err;
 
        afi = nf_tables_afinfo_lookup(net, family, true);
        if (IS_ERR(afi))
@@ -417,11 +476,15 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
        }
 
        if (table != NULL) {
+               if (table->flags & NFT_TABLE_INACTIVE)
+                       return -ENOENT;
                if (nlh->nlmsg_flags & NLM_F_EXCL)
                        return -EEXIST;
                if (nlh->nlmsg_flags & NLM_F_REPLACE)
                        return -EOPNOTSUPP;
-               return nf_tables_updtable(nlsk, skb, nlh, nla, afi, table);
+
+               nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
+               return nf_tables_updtable(&ctx);
        }
 
        if (nla[NFTA_TABLE_FLAGS]) {
@@ -444,8 +507,14 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
        INIT_LIST_HEAD(&table->sets);
        table->flags = flags;
 
+       nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
+       err = nft_trans_table_add(&ctx, NFT_MSG_NEWTABLE);
+       if (err < 0) {
+               kfree(table);
+               module_put(afi->owner);
+               return err;
+       }
        list_add_tail(&table->list, &afi->tables);
-       nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family);
        return 0;
 }
 
@@ -457,7 +526,8 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
        struct nft_af_info *afi;
        struct nft_table *table;
        struct net *net = sock_net(skb->sk);
-       int family = nfmsg->nfgen_family;
+       int family = nfmsg->nfgen_family, err;
+       struct nft_ctx ctx;
 
        afi = nf_tables_afinfo_lookup(net, family, false);
        if (IS_ERR(afi))
@@ -466,17 +536,28 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
        table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
        if (IS_ERR(table))
                return PTR_ERR(table);
-
-       if (!list_empty(&table->chains) || !list_empty(&table->sets))
+       if (table->flags & NFT_TABLE_INACTIVE)
+               return -ENOENT;
+       if (table->use > 0)
                return -EBUSY;
 
+       nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
+       err = nft_trans_table_add(&ctx, NFT_MSG_DELTABLE);
+       if (err < 0)
+               return err;
+
        list_del(&table->list);
-       nf_tables_table_notify(skb, nlh, table, NFT_MSG_DELTABLE, family);
-       kfree(table);
-       module_put(afi->owner);
        return 0;
 }
 
+static void nf_tables_table_destroy(struct nft_ctx *ctx)
+{
+       BUG_ON(ctx->table->use > 0);
+
+       kfree(ctx->table);
+       module_put(ctx->afi->owner);
+}
+
 int nft_register_chain_type(const struct nf_chain_type *ctype)
 {
        int err = 0;
@@ -541,7 +622,7 @@ static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = {
                                    .len = NFT_CHAIN_MAXNAMELEN - 1 },
        [NFTA_CHAIN_HOOK]       = { .type = NLA_NESTED },
        [NFTA_CHAIN_POLICY]     = { .type = NLA_U32 },
-       [NFTA_CHAIN_TYPE]       = { .type = NLA_NUL_STRING },
+       [NFTA_CHAIN_TYPE]       = { .type = NLA_STRING },
        [NFTA_CHAIN_COUNTERS]   = { .type = NLA_NESTED },
 };
 
@@ -637,21 +718,13 @@ nla_put_failure:
        return -1;
 }
 
-static int nf_tables_chain_notify(const struct sk_buff *oskb,
-                                 const struct nlmsghdr *nlh,
-                                 const struct nft_table *table,
-                                 const struct nft_chain *chain,
-                                 int event, int family)
+static int nf_tables_chain_notify(const struct nft_ctx *ctx, int event)
 {
        struct sk_buff *skb;
-       u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
-       struct net *net = oskb ? sock_net(oskb->sk) : &init_net;
-       u32 seq = nlh ? nlh->nlmsg_seq : 0;
-       bool report;
        int err;
 
-       report = nlh ? nlmsg_report(nlh) : false;
-       if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
+       if (!ctx->report &&
+           !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
                return 0;
 
        err = -ENOBUFS;
@@ -659,18 +732,21 @@ static int nf_tables_chain_notify(const struct sk_buff *oskb,
        if (skb == NULL)
                goto err;
 
-       err = nf_tables_fill_chain_info(skb, portid, seq, event, 0, family,
-                                       table, chain);
+       err = nf_tables_fill_chain_info(skb, ctx->portid, ctx->seq, event, 0,
+                                       ctx->afi->family, ctx->table,
+                                       ctx->chain);
        if (err < 0) {
                kfree_skb(skb);
                goto err;
        }
 
-       err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report,
-                            GFP_KERNEL);
+       err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
+                            ctx->report, GFP_KERNEL);
 err:
-       if (err < 0)
-               nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
+       if (err < 0) {
+               nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES,
+                                 err);
+       }
        return err;
 }
 
@@ -740,10 +816,14 @@ static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
        table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
        if (IS_ERR(table))
                return PTR_ERR(table);
+       if (table->flags & NFT_TABLE_INACTIVE)
+               return -ENOENT;
 
        chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
        if (IS_ERR(chain))
                return PTR_ERR(chain);
+       if (chain->flags & NFT_CHAIN_INACTIVE)
+               return -ENOENT;
 
        skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
        if (!skb2)
@@ -767,8 +847,7 @@ static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = {
        [NFTA_COUNTER_BYTES]    = { .type = NLA_U64 },
 };
 
-static int
-nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr)
+static struct nft_stats __percpu *nft_stats_alloc(const struct nlattr *attr)
 {
        struct nlattr *tb[NFTA_COUNTER_MAX+1];
        struct nft_stats __percpu *newstats;
@@ -777,14 +856,14 @@ nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr)
 
        err = nla_parse_nested(tb, NFTA_COUNTER_MAX, attr, nft_counter_policy);
        if (err < 0)
-               return err;
+               return ERR_PTR(err);
 
        if (!tb[NFTA_COUNTER_BYTES] || !tb[NFTA_COUNTER_PACKETS])
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
 
        newstats = alloc_percpu(struct nft_stats);
        if (newstats == NULL)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
 
        /* Restore old counters on this cpu, no problem. Per-cpu statistics
         * are not exposed to userspace.
@@ -793,6 +872,12 @@ nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr)
        stats->bytes = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES]));
        stats->pkts = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS]));
 
+       return newstats;
+}
+
+static void nft_chain_stats_replace(struct nft_base_chain *chain,
+                                   struct nft_stats __percpu *newstats)
+{
        if (chain->stats) {
                struct nft_stats __percpu *oldstats =
                                nft_dereference(chain->stats);
@@ -802,17 +887,43 @@ nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr)
                free_percpu(oldstats);
        } else
                rcu_assign_pointer(chain->stats, newstats);
+}
+
+static int nft_trans_chain_add(struct nft_ctx *ctx, int msg_type)
+{
+       struct nft_trans *trans;
+
+       trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_chain));
+       if (trans == NULL)
+               return -ENOMEM;
 
+       if (msg_type == NFT_MSG_NEWCHAIN)
+               ctx->chain->flags |= NFT_CHAIN_INACTIVE;
+
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
        return 0;
 }
 
+static void nf_tables_chain_destroy(struct nft_chain *chain)
+{
+       BUG_ON(chain->use > 0);
+
+       if (chain->flags & NFT_BASE_CHAIN) {
+               module_put(nft_base_chain(chain)->type->owner);
+               free_percpu(nft_base_chain(chain)->stats);
+               kfree(nft_base_chain(chain));
+       } else {
+               kfree(chain);
+       }
+}
+
 static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
                              const struct nlmsghdr *nlh,
                              const struct nlattr * const nla[])
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        const struct nlattr * uninitialized_var(name);
-       const struct nft_af_info *afi;
+       struct nft_af_info *afi;
        struct nft_table *table;
        struct nft_chain *chain;
        struct nft_base_chain *basechain = NULL;
@@ -822,8 +933,10 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
        u8 policy = NF_ACCEPT;
        u64 handle = 0;
        unsigned int i;
+       struct nft_stats __percpu *stats;
        int err;
        bool create;
+       struct nft_ctx ctx;
 
        create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
 
@@ -869,6 +982,11 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
        }
 
        if (chain != NULL) {
+               struct nft_stats *stats = NULL;
+               struct nft_trans *trans;
+
+               if (chain->flags & NFT_CHAIN_INACTIVE)
+                       return -ENOENT;
                if (nlh->nlmsg_flags & NLM_F_EXCL)
                        return -EEXIST;
                if (nlh->nlmsg_flags & NLM_F_REPLACE)
@@ -882,19 +1000,31 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
                        if (!(chain->flags & NFT_BASE_CHAIN))
                                return -EOPNOTSUPP;
 
-                       err = nf_tables_counters(nft_base_chain(chain),
-                                                nla[NFTA_CHAIN_COUNTERS]);
-                       if (err < 0)
-                               return err;
+                       stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]);
+                       if (IS_ERR(stats))
+                               return PTR_ERR(stats);
                }
 
-               if (nla[NFTA_CHAIN_POLICY])
-                       nft_base_chain(chain)->policy = policy;
+               nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
+               trans = nft_trans_alloc(&ctx, NFT_MSG_NEWCHAIN,
+                                       sizeof(struct nft_trans_chain));
+               if (trans == NULL)
+                       return -ENOMEM;
+
+               nft_trans_chain_stats(trans) = stats;
+               nft_trans_chain_update(trans) = true;
 
-               if (nla[NFTA_CHAIN_HANDLE] && name)
-                       nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN);
+               if (nla[NFTA_CHAIN_POLICY])
+                       nft_trans_chain_policy(trans) = policy;
+               else
+                       nft_trans_chain_policy(trans) = -1;
 
-               goto notify;
+               if (nla[NFTA_CHAIN_HANDLE] && name) {
+                       nla_strlcpy(nft_trans_chain_name(trans), name,
+                                   NFT_CHAIN_MAXNAMELEN);
+               }
+               list_add_tail(&trans->list, &net->nft.commit_list);
+               return 0;
        }
 
        if (table->use == UINT_MAX)
@@ -939,23 +1069,21 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
                        return -ENOMEM;
 
                if (nla[NFTA_CHAIN_COUNTERS]) {
-                       err = nf_tables_counters(basechain,
-                                                nla[NFTA_CHAIN_COUNTERS]);
-                       if (err < 0) {
+                       stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]);
+                       if (IS_ERR(stats)) {
                                module_put(type->owner);
                                kfree(basechain);
-                               return err;
+                               return PTR_ERR(stats);
                        }
+                       basechain->stats = stats;
                } else {
-                       struct nft_stats __percpu *newstats;
-
-                       newstats = alloc_percpu(struct nft_stats);
-                       if (newstats == NULL) {
+                       stats = alloc_percpu(struct nft_stats);
+                       if (IS_ERR(stats)) {
                                module_put(type->owner);
                                kfree(basechain);
-                               return -ENOMEM;
+                               return PTR_ERR(stats);
                        }
-                       rcu_assign_pointer(basechain->stats, newstats);
+                       rcu_assign_pointer(basechain->stats, stats);
                }
 
                basechain->type = type;
@@ -992,31 +1120,27 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
        if (!(table->flags & NFT_TABLE_F_DORMANT) &&
            chain->flags & NFT_BASE_CHAIN) {
                err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops);
-               if (err < 0) {
-                       module_put(basechain->type->owner);
-                       free_percpu(basechain->stats);
-                       kfree(basechain);
-                       return err;
-               }
+               if (err < 0)
+                       goto err1;
        }
-       list_add_tail(&chain->list, &table->chains);
-       table->use++;
-notify:
-       nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_NEWCHAIN,
-                              family);
-       return 0;
-}
 
-static void nf_tables_chain_destroy(struct nft_chain *chain)
-{
-       BUG_ON(chain->use > 0);
+       nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
+       err = nft_trans_chain_add(&ctx, NFT_MSG_NEWCHAIN);
+       if (err < 0)
+               goto err2;
 
-       if (chain->flags & NFT_BASE_CHAIN) {
-               module_put(nft_base_chain(chain)->type->owner);
-               free_percpu(nft_base_chain(chain)->stats);
-               kfree(nft_base_chain(chain));
-       } else
-               kfree(chain);
+       table->use++;
+       list_add_tail(&chain->list, &table->chains);
+       return 0;
+err2:
+       if (!(table->flags & NFT_TABLE_F_DORMANT) &&
+           chain->flags & NFT_BASE_CHAIN) {
+               nf_unregister_hooks(nft_base_chain(chain)->ops,
+                                   afi->nops);
+       }
+err1:
+       nf_tables_chain_destroy(chain);
+       return err;
 }
 
 static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
@@ -1024,11 +1148,13 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
                              const struct nlattr * const nla[])
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       const struct nft_af_info *afi;
+       struct nft_af_info *afi;
        struct nft_table *table;
        struct nft_chain *chain;
        struct net *net = sock_net(skb->sk);
        int family = nfmsg->nfgen_family;
+       struct nft_ctx ctx;
+       int err;
 
        afi = nf_tables_afinfo_lookup(net, family, false);
        if (IS_ERR(afi))
@@ -1037,48 +1163,27 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
        table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
        if (IS_ERR(table))
                return PTR_ERR(table);
+       if (table->flags & NFT_TABLE_INACTIVE)
+               return -ENOENT;
 
        chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
        if (IS_ERR(chain))
                return PTR_ERR(chain);
-
-       if (!list_empty(&chain->rules) || chain->use > 0)
+       if (chain->flags & NFT_CHAIN_INACTIVE)
+               return -ENOENT;
+       if (chain->use > 0)
                return -EBUSY;
 
-       list_del(&chain->list);
-       table->use--;
-
-       if (!(table->flags & NFT_TABLE_F_DORMANT) &&
-           chain->flags & NFT_BASE_CHAIN)
-               nf_unregister_hooks(nft_base_chain(chain)->ops, afi->nops);
-
-       nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN,
-                              family);
-
-       /* Make sure all rule references are gone before this is released */
-       synchronize_rcu();
+       nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
+       err = nft_trans_chain_add(&ctx, NFT_MSG_DELCHAIN);
+       if (err < 0)
+               return err;
 
-       nf_tables_chain_destroy(chain);
+       table->use--;
+       list_del(&chain->list);
        return 0;
 }
 
-static void nft_ctx_init(struct nft_ctx *ctx,
-                        const struct sk_buff *skb,
-                        const struct nlmsghdr *nlh,
-                        const struct nft_af_info *afi,
-                        const struct nft_table *table,
-                        const struct nft_chain *chain,
-                        const struct nlattr * const *nla)
-{
-       ctx->net   = sock_net(skb->sk);
-       ctx->skb   = skb;
-       ctx->nlh   = nlh;
-       ctx->afi   = afi;
-       ctx->table = table;
-       ctx->chain = chain;
-       ctx->nla   = nla;
-}
-
 /*
  * Expressions
  */
@@ -1093,7 +1198,10 @@ static void nft_ctx_init(struct nft_ctx *ctx,
 int nft_register_expr(struct nft_expr_type *type)
 {
        nfnl_lock(NFNL_SUBSYS_NFTABLES);
-       list_add_tail(&type->list, &nf_tables_expressions);
+       if (type->family == NFPROTO_UNSPEC)
+               list_add_tail(&type->list, &nf_tables_expressions);
+       else
+               list_add(&type->list, &nf_tables_expressions);
        nfnl_unlock(NFNL_SUBSYS_NFTABLES);
        return 0;
 }
@@ -1361,22 +1469,15 @@ nla_put_failure:
        return -1;
 }
 
-static int nf_tables_rule_notify(const struct sk_buff *oskb,
-                                const struct nlmsghdr *nlh,
-                                const struct nft_table *table,
-                                const struct nft_chain *chain,
+static int nf_tables_rule_notify(const struct nft_ctx *ctx,
                                 const struct nft_rule *rule,
-                                int event, u32 flags, int family)
+                                int event)
 {
        struct sk_buff *skb;
-       u32 portid = NETLINK_CB(oskb).portid;
-       struct net *net = oskb ? sock_net(oskb->sk) : &init_net;
-       u32 seq = nlh->nlmsg_seq;
-       bool report;
        int err;
 
-       report = nlmsg_report(nlh);
-       if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
+       if (!ctx->report &&
+           !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
                return 0;
 
        err = -ENOBUFS;
@@ -1384,18 +1485,21 @@ static int nf_tables_rule_notify(const struct sk_buff *oskb,
        if (skb == NULL)
                goto err;
 
-       err = nf_tables_fill_rule_info(skb, portid, seq, event, flags,
-                                      family, table, chain, rule);
+       err = nf_tables_fill_rule_info(skb, ctx->portid, ctx->seq, event, 0,
+                                      ctx->afi->family, ctx->table,
+                                      ctx->chain, rule);
        if (err < 0) {
                kfree_skb(skb);
                goto err;
        }
 
-       err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report,
-                            GFP_KERNEL);
+       err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
+                            ctx->report, GFP_KERNEL);
 err:
-       if (err < 0)
-               nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
+       if (err < 0) {
+               nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES,
+                                 err);
+       }
        return err;
 }
 
@@ -1511,10 +1615,14 @@ static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb,
        table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
        if (IS_ERR(table))
                return PTR_ERR(table);
+       if (table->flags & NFT_TABLE_INACTIVE)
+               return -ENOENT;
 
        chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
        if (IS_ERR(chain))
                return PTR_ERR(chain);
+       if (chain->flags & NFT_CHAIN_INACTIVE)
+               return -ENOENT;
 
        rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]);
        if (IS_ERR(rule))
@@ -1554,37 +1662,36 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
        kfree(rule);
 }
 
-#define NFT_RULE_MAXEXPRS      128
-
-static struct nft_expr_info *info;
-
-static struct nft_rule_trans *
-nf_tables_trans_add(struct nft_ctx *ctx, struct nft_rule *rule)
+static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx, int msg_type,
+                                           struct nft_rule *rule)
 {
-       struct nft_rule_trans *rupd;
+       struct nft_trans *trans;
 
-       rupd = kmalloc(sizeof(struct nft_rule_trans), GFP_KERNEL);
-       if (rupd == NULL)
-              return NULL;
+       trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_rule));
+       if (trans == NULL)
+               return NULL;
 
-       rupd->ctx = *ctx;
-       rupd->rule = rule;
-       list_add_tail(&rupd->list, &ctx->net->nft.commit_list);
+       nft_trans_rule(trans) = rule;
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
 
-       return rupd;
+       return trans;
 }
 
+#define NFT_RULE_MAXEXPRS      128
+
+static struct nft_expr_info *info;
+
 static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
                             const struct nlmsghdr *nlh,
                             const struct nlattr * const nla[])
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       const struct nft_af_info *afi;
+       struct nft_af_info *afi;
        struct net *net = sock_net(skb->sk);
        struct nft_table *table;
        struct nft_chain *chain;
        struct nft_rule *rule, *old_rule = NULL;
-       struct nft_rule_trans *repl = NULL;
+       struct nft_trans *trans = NULL;
        struct nft_expr *expr;
        struct nft_ctx ctx;
        struct nlattr *tmp;
@@ -1682,8 +1789,9 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
 
        if (nlh->nlmsg_flags & NLM_F_REPLACE) {
                if (nft_rule_is_active_next(net, old_rule)) {
-                       repl = nf_tables_trans_add(&ctx, old_rule);
-                       if (repl == NULL) {
+                       trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE,
+                                                  old_rule);
+                       if (trans == NULL) {
                                err = -ENOMEM;
                                goto err2;
                        }
@@ -1705,19 +1813,19 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
                        list_add_rcu(&rule->list, &chain->rules);
        }
 
-       if (nf_tables_trans_add(&ctx, rule) == NULL) {
+       if (nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule) == NULL) {
                err = -ENOMEM;
                goto err3;
        }
+       chain->use++;
        return 0;
 
 err3:
        list_del_rcu(&rule->list);
-       if (repl) {
-               list_del_rcu(&repl->rule->list);
-               list_del(&repl->list);
-               nft_rule_clear(net, repl->rule);
-               kfree(repl);
+       if (trans) {
+               list_del_rcu(&nft_trans_rule(trans)->list);
+               nft_rule_clear(net, nft_trans_rule(trans));
+               nft_trans_destroy(trans);
        }
 err2:
        nf_tables_rule_destroy(&ctx, rule);
@@ -1734,9 +1842,10 @@ nf_tables_delrule_one(struct nft_ctx *ctx, struct nft_rule *rule)
 {
        /* You cannot delete the same rule twice */
        if (nft_rule_is_active_next(ctx->net, rule)) {
-               if (nf_tables_trans_add(ctx, rule) == NULL)
+               if (nft_trans_rule_add(ctx, NFT_MSG_DELRULE, rule) == NULL)
                        return -ENOMEM;
                nft_rule_disactivate_next(ctx->net, rule);
+               ctx->chain->use--;
                return 0;
        }
        return -ENOENT;
@@ -1760,9 +1869,9 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
                             const struct nlattr * const nla[])
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       const struct nft_af_info *afi;
+       struct nft_af_info *afi;
        struct net *net = sock_net(skb->sk);
-       const struct nft_table *table;
+       struct nft_table *table;
        struct nft_chain *chain = NULL;
        struct nft_rule *rule;
        int family = nfmsg->nfgen_family, err = 0;
@@ -1775,6 +1884,8 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
        table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
        if (IS_ERR(table))
                return PTR_ERR(table);
+       if (table->flags & NFT_TABLE_INACTIVE)
+               return -ENOENT;
 
        if (nla[NFTA_RULE_CHAIN]) {
                chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
@@ -1807,88 +1918,6 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
        return err;
 }
 
-static int nf_tables_commit(struct sk_buff *skb)
-{
-       struct net *net = sock_net(skb->sk);
-       struct nft_rule_trans *rupd, *tmp;
-
-       /* Bump generation counter, invalidate any dump in progress */
-       net->nft.genctr++;
-
-       /* A new generation has just started */
-       net->nft.gencursor = gencursor_next(net);
-
-       /* Make sure all packets have left the previous generation before
-        * purging old rules.
-        */
-       synchronize_rcu();
-
-       list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
-               /* This rule was inactive in the past and just became active.
-                * Clear the next bit of the genmask since its meaning has
-                * changed, now it is the future.
-                */
-               if (nft_rule_is_active(net, rupd->rule)) {
-                       nft_rule_clear(net, rupd->rule);
-                       nf_tables_rule_notify(skb, rupd->ctx.nlh,
-                                             rupd->ctx.table, rupd->ctx.chain,
-                                             rupd->rule, NFT_MSG_NEWRULE, 0,
-                                             rupd->ctx.afi->family);
-                       list_del(&rupd->list);
-                       kfree(rupd);
-                       continue;
-               }
-
-               /* This rule is in the past, get rid of it */
-               list_del_rcu(&rupd->rule->list);
-               nf_tables_rule_notify(skb, rupd->ctx.nlh,
-                                     rupd->ctx.table, rupd->ctx.chain,
-                                     rupd->rule, NFT_MSG_DELRULE, 0,
-                                     rupd->ctx.afi->family);
-       }
-
-       /* Make sure we don't see any packet traversing old rules */
-       synchronize_rcu();
-
-       /* Now we can safely release unused old rules */
-       list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
-               nf_tables_rule_destroy(&rupd->ctx, rupd->rule);
-               list_del(&rupd->list);
-               kfree(rupd);
-       }
-
-       return 0;
-}
-
-static int nf_tables_abort(struct sk_buff *skb)
-{
-       struct net *net = sock_net(skb->sk);
-       struct nft_rule_trans *rupd, *tmp;
-
-       list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
-               if (!nft_rule_is_active_next(net, rupd->rule)) {
-                       nft_rule_clear(net, rupd->rule);
-                       list_del(&rupd->list);
-                       kfree(rupd);
-                       continue;
-               }
-
-               /* This rule is inactive, get rid of it */
-               list_del_rcu(&rupd->rule->list);
-       }
-
-       /* Make sure we don't see any packet accessing aborted rules */
-       synchronize_rcu();
-
-       list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
-               nf_tables_rule_destroy(&rupd->ctx, rupd->rule);
-               list_del(&rupd->list);
-               kfree(rupd);
-       }
-
-       return 0;
-}
-
 /*
  * Sets
  */
@@ -1912,9 +1941,18 @@ void nft_unregister_set(struct nft_set_ops *ops)
 }
 EXPORT_SYMBOL_GPL(nft_unregister_set);
 
-static const struct nft_set_ops *nft_select_set_ops(const struct nlattr * const nla[])
+/*
+ * Select a set implementation based on the data characteristics and the
+ * given policy. The total memory use might not be known if no size is
+ * given, in that case the amount of memory per element is used.
+ */
+static const struct nft_set_ops *
+nft_select_set_ops(const struct nlattr * const nla[],
+                  const struct nft_set_desc *desc,
+                  enum nft_set_policies policy)
 {
-       const struct nft_set_ops *ops;
+       const struct nft_set_ops *ops, *bops;
+       struct nft_set_estimate est, best;
        u32 features;
 
 #ifdef CONFIG_MODULES
@@ -1932,27 +1970,64 @@ static const struct nft_set_ops *nft_select_set_ops(const struct nlattr * const
                features &= NFT_SET_INTERVAL | NFT_SET_MAP;
        }
 
-       // FIXME: implement selection properly
+       bops       = NULL;
+       best.size  = ~0;
+       best.class = ~0;
+
        list_for_each_entry(ops, &nf_tables_set_ops, list) {
                if ((ops->features & features) != features)
                        continue;
-               if (!try_module_get(ops->owner))
+               if (!ops->estimate(desc, features, &est))
                        continue;
-               return ops;
-       }
-
-       return ERR_PTR(-EOPNOTSUPP);
-}
 
-static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
-       [NFTA_SET_TABLE]                = { .type = NLA_STRING },
-       [NFTA_SET_NAME]                 = { .type = NLA_STRING,
-                                           .len = IFNAMSIZ - 1 },
+               switch (policy) {
+               case NFT_SET_POL_PERFORMANCE:
+                       if (est.class < best.class)
+                               break;
+                       if (est.class == best.class && est.size < best.size)
+                               break;
+                       continue;
+               case NFT_SET_POL_MEMORY:
+                       if (est.size < best.size)
+                               break;
+                       if (est.size == best.size && est.class < best.class)
+                               break;
+                       continue;
+               default:
+                       break;
+               }
+
+               if (!try_module_get(ops->owner))
+                       continue;
+               if (bops != NULL)
+                       module_put(bops->owner);
+
+               bops = ops;
+               best = est;
+       }
+
+       if (bops != NULL)
+               return bops;
+
+       return ERR_PTR(-EOPNOTSUPP);
+}
+
+static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
+       [NFTA_SET_TABLE]                = { .type = NLA_STRING },
+       [NFTA_SET_NAME]                 = { .type = NLA_STRING,
+                                           .len = IFNAMSIZ - 1 },
        [NFTA_SET_FLAGS]                = { .type = NLA_U32 },
        [NFTA_SET_KEY_TYPE]             = { .type = NLA_U32 },
        [NFTA_SET_KEY_LEN]              = { .type = NLA_U32 },
        [NFTA_SET_DATA_TYPE]            = { .type = NLA_U32 },
        [NFTA_SET_DATA_LEN]             = { .type = NLA_U32 },
+       [NFTA_SET_POLICY]               = { .type = NLA_U32 },
+       [NFTA_SET_DESC]                 = { .type = NLA_NESTED },
+       [NFTA_SET_ID]                   = { .type = NLA_U32 },
+};
+
+static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = {
+       [NFTA_SET_DESC_SIZE]            = { .type = NLA_U32 },
 };
 
 static int nft_ctx_init_from_setattr(struct nft_ctx *ctx,
@@ -1962,8 +2037,8 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx,
 {
        struct net *net = sock_net(skb->sk);
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       const struct nft_af_info *afi = NULL;
-       const struct nft_table *table = NULL;
+       struct nft_af_info *afi = NULL;
+       struct nft_table *table = NULL;
 
        if (nfmsg->nfgen_family != NFPROTO_UNSPEC) {
                afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false);
@@ -1978,6 +2053,8 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx,
                table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]);
                if (IS_ERR(table))
                        return PTR_ERR(table);
+               if (table->flags & NFT_TABLE_INACTIVE)
+                       return -ENOENT;
        }
 
        nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
@@ -1999,13 +2076,27 @@ struct nft_set *nf_tables_set_lookup(const struct nft_table *table,
        return ERR_PTR(-ENOENT);
 }
 
+struct nft_set *nf_tables_set_lookup_byid(const struct net *net,
+                                         const struct nlattr *nla)
+{
+       struct nft_trans *trans;
+       u32 id = ntohl(nla_get_be32(nla));
+
+       list_for_each_entry(trans, &net->nft.commit_list, list) {
+               if (trans->msg_type == NFT_MSG_NEWSET &&
+                   id == nft_trans_set_id(trans))
+                       return nft_trans_set(trans);
+       }
+       return ERR_PTR(-ENOENT);
+}
+
 static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set,
                                    const char *name)
 {
        const struct nft_set *i;
        const char *p;
        unsigned long *inuse;
-       unsigned int n = 0;
+       unsigned int n = 0, min = 0;
 
        p = strnchr(name, IFNAMSIZ, '%');
        if (p != NULL) {
@@ -2015,23 +2106,28 @@ static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set,
                inuse = (unsigned long *)get_zeroed_page(GFP_KERNEL);
                if (inuse == NULL)
                        return -ENOMEM;
-
+cont:
                list_for_each_entry(i, &ctx->table->sets, list) {
                        int tmp;
 
                        if (!sscanf(i->name, name, &tmp))
                                continue;
-                       if (tmp < 0 || tmp >= BITS_PER_BYTE * PAGE_SIZE)
+                       if (tmp < min || tmp >= min + BITS_PER_BYTE * PAGE_SIZE)
                                continue;
 
-                       set_bit(tmp, inuse);
+                       set_bit(tmp - min, inuse);
                }
 
                n = find_first_zero_bit(inuse, BITS_PER_BYTE * PAGE_SIZE);
+               if (n >= BITS_PER_BYTE * PAGE_SIZE) {
+                       min += BITS_PER_BYTE * PAGE_SIZE;
+                       memset(inuse, 0, PAGE_SIZE);
+                       goto cont;
+               }
                free_page((unsigned long)inuse);
        }
 
-       snprintf(set->name, sizeof(set->name), name, n);
+       snprintf(set->name, sizeof(set->name), name, min + n);
        list_for_each_entry(i, &ctx->table->sets, list) {
                if (!strcmp(set->name, i->name))
                        return -ENFILE;
@@ -2044,8 +2140,9 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
 {
        struct nfgenmsg *nfmsg;
        struct nlmsghdr *nlh;
-       u32 portid = NETLINK_CB(ctx->skb).portid;
-       u32 seq = ctx->nlh->nlmsg_seq;
+       struct nlattr *desc;
+       u32 portid = ctx->portid;
+       u32 seq = ctx->seq;
 
        event |= NFNL_SUBSYS_NFTABLES << 8;
        nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
@@ -2077,6 +2174,14 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
                        goto nla_put_failure;
        }
 
+       desc = nla_nest_start(skb, NFTA_SET_DESC);
+       if (desc == NULL)
+               goto nla_put_failure;
+       if (set->size &&
+           nla_put_be32(skb, NFTA_SET_DESC_SIZE, htonl(set->size)))
+               goto nla_put_failure;
+       nla_nest_end(skb, desc);
+
        return nlmsg_end(skb, nlh);
 
 nla_put_failure:
@@ -2086,19 +2191,18 @@ nla_put_failure:
 
 static int nf_tables_set_notify(const struct nft_ctx *ctx,
                                const struct nft_set *set,
-                               int event)
+                               int event, gfp_t gfp_flags)
 {
        struct sk_buff *skb;
-       u32 portid = NETLINK_CB(ctx->skb).portid;
-       bool report;
+       u32 portid = ctx->portid;
        int err;
 
-       report = nlmsg_report(ctx->nlh);
-       if (!report && !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
+       if (!ctx->report &&
+           !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
                return 0;
 
        err = -ENOBUFS;
-       skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       skb = nlmsg_new(NLMSG_GOODSIZE, gfp_flags);
        if (skb == NULL)
                goto err;
 
@@ -2108,8 +2212,8 @@ static int nf_tables_set_notify(const struct nft_ctx *ctx,
                goto err;
        }
 
-       err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES, report,
-                            GFP_KERNEL);
+       err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES,
+                            ctx->report, gfp_flags);
 err:
        if (err < 0)
                nfnetlink_set_err(ctx->net, portid, NFNLGRP_NFTABLES, err);
@@ -2183,7 +2287,7 @@ static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb,
 {
        const struct nft_set *set;
        unsigned int idx, s_idx = cb->args[0];
-       const struct nft_af_info *afi;
+       struct nft_af_info *afi;
        struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2];
        struct net *net = sock_net(skb->sk);
        int cur_family = cb->args[3];
@@ -2260,6 +2364,8 @@ static int nf_tables_dump_sets(struct sk_buff *skb, struct netlink_callback *cb)
        return ret;
 }
 
+#define NFT_SET_INACTIVE       (1 << 15)       /* Internal set flag */
+
 static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
                            const struct nlmsghdr *nlh,
                            const struct nlattr * const nla[])
@@ -2289,6 +2395,8 @@ static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
        set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
        if (IS_ERR(set))
                return PTR_ERR(set);
+       if (set->flags & NFT_SET_INACTIVE)
+               return -ENOENT;
 
        skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
        if (skb2 == NULL)
@@ -2305,13 +2413,50 @@ err:
        return err;
 }
 
+static int nf_tables_set_desc_parse(const struct nft_ctx *ctx,
+                                   struct nft_set_desc *desc,
+                                   const struct nlattr *nla)
+{
+       struct nlattr *da[NFTA_SET_DESC_MAX + 1];
+       int err;
+
+       err = nla_parse_nested(da, NFTA_SET_DESC_MAX, nla, nft_set_desc_policy);
+       if (err < 0)
+               return err;
+
+       if (da[NFTA_SET_DESC_SIZE] != NULL)
+               desc->size = ntohl(nla_get_be32(da[NFTA_SET_DESC_SIZE]));
+
+       return 0;
+}
+
+static int nft_trans_set_add(struct nft_ctx *ctx, int msg_type,
+                            struct nft_set *set)
+{
+       struct nft_trans *trans;
+
+       trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_set));
+       if (trans == NULL)
+               return -ENOMEM;
+
+       if (msg_type == NFT_MSG_NEWSET && ctx->nla[NFTA_SET_ID] != NULL) {
+               nft_trans_set_id(trans) =
+                       ntohl(nla_get_be32(ctx->nla[NFTA_SET_ID]));
+               set->flags |= NFT_SET_INACTIVE;
+       }
+       nft_trans_set(trans) = set;
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+
+       return 0;
+}
+
 static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
                            const struct nlmsghdr *nlh,
                            const struct nlattr * const nla[])
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
        const struct nft_set_ops *ops;
-       const struct nft_af_info *afi;
+       struct nft_af_info *afi;
        struct net *net = sock_net(skb->sk);
        struct nft_table *table;
        struct nft_set *set;
@@ -2319,14 +2464,18 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
        char name[IFNAMSIZ];
        unsigned int size;
        bool create;
-       u32 ktype, klen, dlen, dtype, flags;
+       u32 ktype, dtype, flags, policy;
+       struct nft_set_desc desc;
        int err;
 
        if (nla[NFTA_SET_TABLE] == NULL ||
            nla[NFTA_SET_NAME] == NULL ||
-           nla[NFTA_SET_KEY_LEN] == NULL)
+           nla[NFTA_SET_KEY_LEN] == NULL ||
+           nla[NFTA_SET_ID] == NULL)
                return -EINVAL;
 
+       memset(&desc, 0, sizeof(desc));
+
        ktype = NFT_DATA_VALUE;
        if (nla[NFTA_SET_KEY_TYPE] != NULL) {
                ktype = ntohl(nla_get_be32(nla[NFTA_SET_KEY_TYPE]));
@@ -2334,8 +2483,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
                        return -EINVAL;
        }
 
-       klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN]));
-       if (klen == 0 || klen > FIELD_SIZEOF(struct nft_data, data))
+       desc.klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN]));
+       if (desc.klen == 0 || desc.klen > FIELD_SIZEOF(struct nft_data, data))
                return -EINVAL;
 
        flags = 0;
@@ -2347,7 +2496,6 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
        }
 
        dtype = 0;
-       dlen  = 0;
        if (nla[NFTA_SET_DATA_TYPE] != NULL) {
                if (!(flags & NFT_SET_MAP))
                        return -EINVAL;
@@ -2360,15 +2508,25 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
                if (dtype != NFT_DATA_VERDICT) {
                        if (nla[NFTA_SET_DATA_LEN] == NULL)
                                return -EINVAL;
-                       dlen = ntohl(nla_get_be32(nla[NFTA_SET_DATA_LEN]));
-                       if (dlen == 0 ||
-                           dlen > FIELD_SIZEOF(struct nft_data, data))
+                       desc.dlen = ntohl(nla_get_be32(nla[NFTA_SET_DATA_LEN]));
+                       if (desc.dlen == 0 ||
+                           desc.dlen > FIELD_SIZEOF(struct nft_data, data))
                                return -EINVAL;
                } else
-                       dlen = sizeof(struct nft_data);
+                       desc.dlen = sizeof(struct nft_data);
        } else if (flags & NFT_SET_MAP)
                return -EINVAL;
 
+       policy = NFT_SET_POL_PERFORMANCE;
+       if (nla[NFTA_SET_POLICY] != NULL)
+               policy = ntohl(nla_get_be32(nla[NFTA_SET_POLICY]));
+
+       if (nla[NFTA_SET_DESC] != NULL) {
+               err = nf_tables_set_desc_parse(&ctx, &desc, nla[NFTA_SET_DESC]);
+               if (err < 0)
+                       return err;
+       }
+
        create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
 
        afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create);
@@ -2399,7 +2557,7 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
        if (!(nlh->nlmsg_flags & NLM_F_CREATE))
                return -ENOENT;
 
-       ops = nft_select_set_ops(nla);
+       ops = nft_select_set_ops(nla, &desc, policy);
        if (IS_ERR(ops))
                return PTR_ERR(ops);
 
@@ -2420,17 +2578,22 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
        INIT_LIST_HEAD(&set->bindings);
        set->ops   = ops;
        set->ktype = ktype;
-       set->klen  = klen;
+       set->klen  = desc.klen;
        set->dtype = dtype;
-       set->dlen  = dlen;
+       set->dlen  = desc.dlen;
        set->flags = flags;
+       set->size  = desc.size;
 
-       err = ops->init(set, nla);
+       err = ops->init(set, &desc, nla);
+       if (err < 0)
+               goto err2;
+
+       err = nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set);
        if (err < 0)
                goto err2;
 
        list_add_tail(&set->list, &table->sets);
-       nf_tables_set_notify(&ctx, set, NFT_MSG_NEWSET);
+       table->use++;
        return 0;
 
 err2:
@@ -2440,16 +2603,20 @@ err1:
        return err;
 }
 
-static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
+static void nft_set_destroy(struct nft_set *set)
 {
-       list_del(&set->list);
-       nf_tables_set_notify(ctx, set, NFT_MSG_DELSET);
-
        set->ops->destroy(set);
        module_put(set->ops->owner);
        kfree(set);
 }
 
+static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
+{
+       list_del(&set->list);
+       nf_tables_set_notify(ctx, set, NFT_MSG_DELSET, GFP_ATOMIC);
+       nft_set_destroy(set);
+}
+
 static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
                            const struct nlmsghdr *nlh,
                            const struct nlattr * const nla[])
@@ -2471,10 +2638,17 @@ static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
        set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
        if (IS_ERR(set))
                return PTR_ERR(set);
+       if (set->flags & NFT_SET_INACTIVE)
+               return -ENOENT;
        if (!list_empty(&set->bindings))
                return -EBUSY;
 
-       nf_tables_set_destroy(&ctx, set);
+       err = nft_trans_set_add(&ctx, NFT_MSG_DELSET, set);
+       if (err < 0)
+               return err;
+
+       list_del(&set->list);
+       ctx.table->use--;
        return 0;
 }
 
@@ -2534,7 +2708,8 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
 {
        list_del(&binding->list);
 
-       if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS)
+       if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS &&
+           !(set->flags & NFT_SET_INACTIVE))
                nf_tables_set_destroy(ctx, set);
 }
 
@@ -2552,16 +2727,18 @@ static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX +
        [NFTA_SET_ELEM_LIST_TABLE]      = { .type = NLA_STRING },
        [NFTA_SET_ELEM_LIST_SET]        = { .type = NLA_STRING },
        [NFTA_SET_ELEM_LIST_ELEMENTS]   = { .type = NLA_NESTED },
+       [NFTA_SET_ELEM_LIST_SET_ID]     = { .type = NLA_U32 },
 };
 
 static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx,
                                      const struct sk_buff *skb,
                                      const struct nlmsghdr *nlh,
-                                     const struct nlattr * const nla[])
+                                     const struct nlattr * const nla[],
+                                     bool trans)
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       const struct nft_af_info *afi;
-       const struct nft_table *table;
+       struct nft_af_info *afi;
+       struct nft_table *table;
        struct net *net = sock_net(skb->sk);
 
        afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false);
@@ -2571,6 +2748,8 @@ static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx,
        table = nf_tables_table_lookup(afi, nla[NFTA_SET_ELEM_LIST_TABLE]);
        if (IS_ERR(table))
                return PTR_ERR(table);
+       if (!trans && (table->flags & NFT_TABLE_INACTIVE))
+               return -ENOENT;
 
        nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
        return 0;
@@ -2644,13 +2823,16 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
        if (err < 0)
                return err;
 
-       err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla);
+       err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla,
+                                        false);
        if (err < 0)
                return err;
 
        set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
        if (IS_ERR(set))
                return PTR_ERR(set);
+       if (set->flags & NFT_SET_INACTIVE)
+               return -ENOENT;
 
        event  = NFT_MSG_NEWSETELEM;
        event |= NFNL_SUBSYS_NFTABLES << 8;
@@ -2707,13 +2889,15 @@ static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb,
        struct nft_ctx ctx;
        int err;
 
-       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla);
+       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false);
        if (err < 0)
                return err;
 
        set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
        if (IS_ERR(set))
                return PTR_ERR(set);
+       if (set->flags & NFT_SET_INACTIVE)
+               return -ENOENT;
 
        if (nlh->nlmsg_flags & NLM_F_DUMP) {
                struct netlink_dump_control c = {
@@ -2724,7 +2908,98 @@ static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb,
        return -EOPNOTSUPP;
 }
 
-static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set,
+static int nf_tables_fill_setelem_info(struct sk_buff *skb,
+                                      const struct nft_ctx *ctx, u32 seq,
+                                      u32 portid, int event, u16 flags,
+                                      const struct nft_set *set,
+                                      const struct nft_set_elem *elem)
+{
+       struct nfgenmsg *nfmsg;
+       struct nlmsghdr *nlh;
+       struct nlattr *nest;
+       int err;
+
+       event |= NFNL_SUBSYS_NFTABLES << 8;
+       nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
+                       flags);
+       if (nlh == NULL)
+               goto nla_put_failure;
+
+       nfmsg = nlmsg_data(nlh);
+       nfmsg->nfgen_family     = ctx->afi->family;
+       nfmsg->version          = NFNETLINK_V0;
+       nfmsg->res_id           = 0;
+
+       if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name))
+               goto nla_put_failure;
+       if (nla_put_string(skb, NFTA_SET_NAME, set->name))
+               goto nla_put_failure;
+
+       nest = nla_nest_start(skb, NFTA_SET_ELEM_LIST_ELEMENTS);
+       if (nest == NULL)
+               goto nla_put_failure;
+
+       err = nf_tables_fill_setelem(skb, set, elem);
+       if (err < 0)
+               goto nla_put_failure;
+
+       nla_nest_end(skb, nest);
+
+       return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+       nlmsg_trim(skb, nlh);
+       return -1;
+}
+
+static int nf_tables_setelem_notify(const struct nft_ctx *ctx,
+                                   const struct nft_set *set,
+                                   const struct nft_set_elem *elem,
+                                   int event, u16 flags)
+{
+       struct net *net = ctx->net;
+       u32 portid = ctx->portid;
+       struct sk_buff *skb;
+       int err;
+
+       if (!ctx->report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
+               return 0;
+
+       err = -ENOBUFS;
+       skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (skb == NULL)
+               goto err;
+
+       err = nf_tables_fill_setelem_info(skb, ctx, 0, portid, event, flags,
+                                         set, elem);
+       if (err < 0) {
+               kfree_skb(skb);
+               goto err;
+       }
+
+       err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, ctx->report,
+                            GFP_KERNEL);
+err:
+       if (err < 0)
+               nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
+       return err;
+}
+
+static struct nft_trans *nft_trans_elem_alloc(struct nft_ctx *ctx,
+                                             int msg_type,
+                                             struct nft_set *set)
+{
+       struct nft_trans *trans;
+
+       trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_elem));
+       if (trans == NULL)
+               return NULL;
+
+       nft_trans_elem_set(trans) = set;
+       return trans;
+}
+
+static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                            const struct nlattr *attr)
 {
        struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
@@ -2732,8 +3007,12 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set,
        struct nft_set_elem elem;
        struct nft_set_binding *binding;
        enum nft_registers dreg;
+       struct nft_trans *trans;
        int err;
 
+       if (set->size && set->nelems == set->size)
+               return -ENFILE;
+
        err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr,
                               nft_set_elem_policy);
        if (err < 0)
@@ -2786,7 +3065,7 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set,
                        struct nft_ctx bind_ctx = {
                                .afi    = ctx->afi,
                                .table  = ctx->table,
-                               .chain  = binding->chain,
+                               .chain  = (struct nft_chain *)binding->chain,
                        };
 
                        err = nft_validate_data_load(&bind_ctx, dreg,
@@ -2796,12 +3075,20 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set,
                }
        }
 
+       trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
+       if (trans == NULL)
+               goto err3;
+
        err = set->ops->insert(set, &elem);
        if (err < 0)
-               goto err3;
+               goto err4;
 
+       nft_trans_elem(trans) = elem;
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
        return 0;
 
+err4:
+       kfree(trans);
 err3:
        if (nla[NFTA_SET_ELEM_DATA] != NULL)
                nft_data_uninit(&elem.data, d2.type);
@@ -2815,35 +3102,46 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb,
                                const struct nlmsghdr *nlh,
                                const struct nlattr * const nla[])
 {
+       struct net *net = sock_net(skb->sk);
        const struct nlattr *attr;
        struct nft_set *set;
        struct nft_ctx ctx;
-       int rem, err;
+       int rem, err = 0;
 
-       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla);
+       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, true);
        if (err < 0)
                return err;
 
        set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
-       if (IS_ERR(set))
-               return PTR_ERR(set);
+       if (IS_ERR(set)) {
+               if (nla[NFTA_SET_ELEM_LIST_SET_ID]) {
+                       set = nf_tables_set_lookup_byid(net,
+                                       nla[NFTA_SET_ELEM_LIST_SET_ID]);
+               }
+               if (IS_ERR(set))
+                       return PTR_ERR(set);
+       }
+
        if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
                return -EBUSY;
 
        nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
                err = nft_add_set_elem(&ctx, set, attr);
                if (err < 0)
-                       return err;
+                       break;
+
+               set->nelems++;
        }
-       return 0;
+       return err;
 }
 
-static int nft_del_setelem(const struct nft_ctx *ctx, struct nft_set *set,
+static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
                           const struct nlattr *attr)
 {
        struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
        struct nft_data_desc desc;
        struct nft_set_elem elem;
+       struct nft_trans *trans;
        int err;
 
        err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr,
@@ -2867,7 +3165,12 @@ static int nft_del_setelem(const struct nft_ctx *ctx, struct nft_set *set,
        if (err < 0)
                goto err2;
 
-       set->ops->remove(set, &elem);
+       trans = nft_trans_elem_alloc(ctx, NFT_MSG_DELSETELEM, set);
+       if (trans == NULL)
+               goto err2;
+
+       nft_trans_elem(trans) = elem;
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
 
        nft_data_uninit(&elem.key, NFT_DATA_VALUE);
        if (set->flags & NFT_SET_MAP)
@@ -2886,9 +3189,9 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
        const struct nlattr *attr;
        struct nft_set *set;
        struct nft_ctx ctx;
-       int rem, err;
+       int rem, err = 0;
 
-       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla);
+       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false);
        if (err < 0)
                return err;
 
@@ -2901,14 +3204,16 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
        nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
                err = nft_del_setelem(&ctx, set, attr);
                if (err < 0)
-                       return err;
+                       break;
+
+               set->nelems--;
        }
-       return 0;
+       return err;
 }
 
 static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
        [NFT_MSG_NEWTABLE] = {
-               .call           = nf_tables_newtable,
+               .call_batch     = nf_tables_newtable,
                .attr_count     = NFTA_TABLE_MAX,
                .policy         = nft_table_policy,
        },
@@ -2918,12 +3223,12 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
                .policy         = nft_table_policy,
        },
        [NFT_MSG_DELTABLE] = {
-               .call           = nf_tables_deltable,
+               .call_batch     = nf_tables_deltable,
                .attr_count     = NFTA_TABLE_MAX,
                .policy         = nft_table_policy,
        },
        [NFT_MSG_NEWCHAIN] = {
-               .call           = nf_tables_newchain,
+               .call_batch     = nf_tables_newchain,
                .attr_count     = NFTA_CHAIN_MAX,
                .policy         = nft_chain_policy,
        },
@@ -2933,7 +3238,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
                .policy         = nft_chain_policy,
        },
        [NFT_MSG_DELCHAIN] = {
-               .call           = nf_tables_delchain,
+               .call_batch     = nf_tables_delchain,
                .attr_count     = NFTA_CHAIN_MAX,
                .policy         = nft_chain_policy,
        },
@@ -2953,7 +3258,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
                .policy         = nft_rule_policy,
        },
        [NFT_MSG_NEWSET] = {
-               .call           = nf_tables_newset,
+               .call_batch     = nf_tables_newset,
                .attr_count     = NFTA_SET_MAX,
                .policy         = nft_set_policy,
        },
@@ -2963,12 +3268,12 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
                .policy         = nft_set_policy,
        },
        [NFT_MSG_DELSET] = {
-               .call           = nf_tables_delset,
+               .call_batch     = nf_tables_delset,
                .attr_count     = NFTA_SET_MAX,
                .policy         = nft_set_policy,
        },
        [NFT_MSG_NEWSETELEM] = {
-               .call           = nf_tables_newsetelem,
+               .call_batch     = nf_tables_newsetelem,
                .attr_count     = NFTA_SET_ELEM_LIST_MAX,
                .policy         = nft_set_elem_list_policy,
        },
@@ -2978,12 +3283,282 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
                .policy         = nft_set_elem_list_policy,
        },
        [NFT_MSG_DELSETELEM] = {
-               .call           = nf_tables_delsetelem,
+               .call_batch     = nf_tables_delsetelem,
                .attr_count     = NFTA_SET_ELEM_LIST_MAX,
                .policy         = nft_set_elem_list_policy,
        },
 };
 
+static void nft_chain_commit_update(struct nft_trans *trans)
+{
+       struct nft_base_chain *basechain;
+
+       if (nft_trans_chain_name(trans)[0])
+               strcpy(trans->ctx.chain->name, nft_trans_chain_name(trans));
+
+       if (!(trans->ctx.chain->flags & NFT_BASE_CHAIN))
+               return;
+
+       basechain = nft_base_chain(trans->ctx.chain);
+       nft_chain_stats_replace(basechain, nft_trans_chain_stats(trans));
+
+       switch (nft_trans_chain_policy(trans)) {
+       case NF_DROP:
+       case NF_ACCEPT:
+               basechain->policy = nft_trans_chain_policy(trans);
+               break;
+       }
+}
+
+/* Schedule objects for release via rcu to make sure no packets are accesing
+ * removed rules.
+ */
+static void nf_tables_commit_release_rcu(struct rcu_head *rt)
+{
+       struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);
+
+       switch (trans->msg_type) {
+       case NFT_MSG_DELTABLE:
+               nf_tables_table_destroy(&trans->ctx);
+               break;
+       case NFT_MSG_DELCHAIN:
+               nf_tables_chain_destroy(trans->ctx.chain);
+               break;
+       case NFT_MSG_DELRULE:
+               nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
+               break;
+       case NFT_MSG_DELSET:
+               nft_set_destroy(nft_trans_set(trans));
+               break;
+       }
+       kfree(trans);
+}
+
+static int nf_tables_commit(struct sk_buff *skb)
+{
+       struct net *net = sock_net(skb->sk);
+       struct nft_trans *trans, *next;
+       struct nft_set *set;
+
+       /* Bump generation counter, invalidate any dump in progress */
+       net->nft.genctr++;
+
+       /* A new generation has just started */
+       net->nft.gencursor = gencursor_next(net);
+
+       /* Make sure all packets have left the previous generation before
+        * purging old rules.
+        */
+       synchronize_rcu();
+
+       list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
+               switch (trans->msg_type) {
+               case NFT_MSG_NEWTABLE:
+                       if (nft_trans_table_update(trans)) {
+                               if (!nft_trans_table_enable(trans)) {
+                                       nf_tables_table_disable(trans->ctx.afi,
+                                                               trans->ctx.table);
+                                       trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
+                               }
+                       } else {
+                               trans->ctx.table->flags &= ~NFT_TABLE_INACTIVE;
+                       }
+                       nf_tables_table_notify(&trans->ctx, NFT_MSG_NEWTABLE);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_DELTABLE:
+                       nf_tables_table_notify(&trans->ctx, NFT_MSG_DELTABLE);
+                       break;
+               case NFT_MSG_NEWCHAIN:
+                       if (nft_trans_chain_update(trans))
+                               nft_chain_commit_update(trans);
+                       else
+                               trans->ctx.chain->flags &= ~NFT_CHAIN_INACTIVE;
+
+                       nf_tables_chain_notify(&trans->ctx, NFT_MSG_NEWCHAIN);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_DELCHAIN:
+                       nf_tables_chain_notify(&trans->ctx, NFT_MSG_DELCHAIN);
+                       if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) &&
+                           trans->ctx.chain->flags & NFT_BASE_CHAIN) {
+                               nf_unregister_hooks(nft_base_chain(trans->ctx.chain)->ops,
+                                                   trans->ctx.afi->nops);
+                       }
+                       break;
+               case NFT_MSG_NEWRULE:
+                       nft_rule_clear(trans->ctx.net, nft_trans_rule(trans));
+                       nf_tables_rule_notify(&trans->ctx,
+                                             nft_trans_rule(trans),
+                                             NFT_MSG_NEWRULE);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_DELRULE:
+                       list_del_rcu(&nft_trans_rule(trans)->list);
+                       nf_tables_rule_notify(&trans->ctx,
+                                             nft_trans_rule(trans),
+                                             NFT_MSG_DELRULE);
+                       break;
+               case NFT_MSG_NEWSET:
+                       nft_trans_set(trans)->flags &= ~NFT_SET_INACTIVE;
+                       /* This avoids hitting -EBUSY when deleting the table
+                        * from the transaction.
+                        */
+                       if (nft_trans_set(trans)->flags & NFT_SET_ANONYMOUS &&
+                           !list_empty(&nft_trans_set(trans)->bindings))
+                               trans->ctx.table->use--;
+
+                       nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
+                                            NFT_MSG_NEWSET, GFP_KERNEL);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_DELSET:
+                       nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
+                                            NFT_MSG_DELSET, GFP_KERNEL);
+                       break;
+               case NFT_MSG_NEWSETELEM:
+                       nf_tables_setelem_notify(&trans->ctx,
+                                                nft_trans_elem_set(trans),
+                                                &nft_trans_elem(trans),
+                                                NFT_MSG_NEWSETELEM, 0);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_DELSETELEM:
+                       nf_tables_setelem_notify(&trans->ctx,
+                                                nft_trans_elem_set(trans),
+                                                &nft_trans_elem(trans),
+                                                NFT_MSG_DELSETELEM, 0);
+                       set = nft_trans_elem_set(trans);
+                       set->ops->get(set, &nft_trans_elem(trans));
+                       set->ops->remove(set, &nft_trans_elem(trans));
+                       nft_trans_destroy(trans);
+                       break;
+               }
+       }
+
+       list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
+               list_del(&trans->list);
+               trans->ctx.nla = NULL;
+               call_rcu(&trans->rcu_head, nf_tables_commit_release_rcu);
+       }
+
+       return 0;
+}
+
+/* Schedule objects for release via rcu to make sure no packets are accesing
+ * aborted rules.
+ */
+static void nf_tables_abort_release_rcu(struct rcu_head *rt)
+{
+       struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);
+
+       switch (trans->msg_type) {
+       case NFT_MSG_NEWTABLE:
+               nf_tables_table_destroy(&trans->ctx);
+               break;
+       case NFT_MSG_NEWCHAIN:
+               nf_tables_chain_destroy(trans->ctx.chain);
+               break;
+       case NFT_MSG_NEWRULE:
+               nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
+               break;
+       case NFT_MSG_NEWSET:
+               nft_set_destroy(nft_trans_set(trans));
+               break;
+       }
+       kfree(trans);
+}
+
+static int nf_tables_abort(struct sk_buff *skb)
+{
+       struct net *net = sock_net(skb->sk);
+       struct nft_trans *trans, *next;
+       struct nft_set *set;
+
+       list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
+               switch (trans->msg_type) {
+               case NFT_MSG_NEWTABLE:
+                       if (nft_trans_table_update(trans)) {
+                               if (nft_trans_table_enable(trans)) {
+                                       nf_tables_table_disable(trans->ctx.afi,
+                                                               trans->ctx.table);
+                                       trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
+                               }
+                               nft_trans_destroy(trans);
+                       } else {
+                               list_del(&trans->ctx.table->list);
+                       }
+                       break;
+               case NFT_MSG_DELTABLE:
+                       list_add_tail(&trans->ctx.table->list,
+                                     &trans->ctx.afi->tables);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_NEWCHAIN:
+                       if (nft_trans_chain_update(trans)) {
+                               if (nft_trans_chain_stats(trans))
+                                       free_percpu(nft_trans_chain_stats(trans));
+
+                               nft_trans_destroy(trans);
+                       } else {
+                               trans->ctx.table->use--;
+                               list_del(&trans->ctx.chain->list);
+                               if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) &&
+                                   trans->ctx.chain->flags & NFT_BASE_CHAIN) {
+                                       nf_unregister_hooks(nft_base_chain(trans->ctx.chain)->ops,
+                                                           trans->ctx.afi->nops);
+                               }
+                       }
+                       break;
+               case NFT_MSG_DELCHAIN:
+                       trans->ctx.table->use++;
+                       list_add_tail(&trans->ctx.chain->list,
+                                     &trans->ctx.table->chains);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_NEWRULE:
+                       trans->ctx.chain->use--;
+                       list_del_rcu(&nft_trans_rule(trans)->list);
+                       break;
+               case NFT_MSG_DELRULE:
+                       trans->ctx.chain->use++;
+                       nft_rule_clear(trans->ctx.net, nft_trans_rule(trans));
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_NEWSET:
+                       trans->ctx.table->use--;
+                       list_del(&nft_trans_set(trans)->list);
+                       break;
+               case NFT_MSG_DELSET:
+                       trans->ctx.table->use++;
+                       list_add_tail(&nft_trans_set(trans)->list,
+                                     &trans->ctx.table->sets);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_NEWSETELEM:
+                       nft_trans_elem_set(trans)->nelems--;
+                       set = nft_trans_elem_set(trans);
+                       set->ops->get(set, &nft_trans_elem(trans));
+                       set->ops->remove(set, &nft_trans_elem(trans));
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_DELSETELEM:
+                       nft_trans_elem_set(trans)->nelems++;
+                       nft_trans_destroy(trans);
+                       break;
+               }
+       }
+
+       list_for_each_entry_safe_reverse(trans, next,
+                                        &net->nft.commit_list, list) {
+               list_del(&trans->list);
+               trans->ctx.nla = NULL;
+               call_rcu(&trans->rcu_head, nf_tables_abort_release_rcu);
+       }
+
+       return 0;
+}
+
 static const struct nfnetlink_subsystem nf_tables_subsys = {
        .name           = "nf_tables",
        .subsys_id      = NFNL_SUBSYS_NFTABLES,
index 23ef77c60fffc60a1678f79dfbb769fe9be6a274..c138b8fbe280af6886693421a7fe8d9a288156cf 100644 (file)
@@ -399,19 +399,17 @@ static void nfnetlink_rcv(struct sk_buff *skb)
 }
 
 #ifdef CONFIG_MODULES
-static void nfnetlink_bind(int group)
+static int nfnetlink_bind(int group)
 {
        const struct nfnetlink_subsystem *ss;
        int type = nfnl_group2type[group];
 
        rcu_read_lock();
        ss = nfnetlink_get_subsys(type);
-       if (!ss) {
-               rcu_read_unlock();
-               request_module("nfnetlink-subsys-%d", type);
-               return;
-       }
        rcu_read_unlock();
+       if (!ss)
+               request_module("nfnetlink-subsys-%d", type);
+       return 0;
 }
 #endif
 
index c7b6d466a66247c3fa18b9a9a6f3e174a18d40da..2baa125c2e8dbcee194f8e8d8c98d6574a5b67b8 100644 (file)
@@ -32,18 +32,24 @@ static LIST_HEAD(nfnl_acct_list);
 struct nf_acct {
        atomic64_t              pkts;
        atomic64_t              bytes;
+       unsigned long           flags;
        struct list_head        head;
        atomic_t                refcnt;
        char                    name[NFACCT_NAME_MAX];
        struct rcu_head         rcu_head;
+       char                    data[0];
 };
 
+#define NFACCT_F_QUOTA (NFACCT_F_QUOTA_PKTS | NFACCT_F_QUOTA_BYTES)
+
 static int
 nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb,
             const struct nlmsghdr *nlh, const struct nlattr * const tb[])
 {
        struct nf_acct *nfacct, *matching = NULL;
        char *acct_name;
+       unsigned int size = 0;
+       u32 flags = 0;
 
        if (!tb[NFACCT_NAME])
                return -EINVAL;
@@ -68,15 +74,38 @@ nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb,
                        /* reset counters if you request a replacement. */
                        atomic64_set(&matching->pkts, 0);
                        atomic64_set(&matching->bytes, 0);
+                       smp_mb__before_atomic();
+                       /* reset overquota flag if quota is enabled. */
+                       if ((matching->flags & NFACCT_F_QUOTA))
+                               clear_bit(NFACCT_F_OVERQUOTA, &matching->flags);
                        return 0;
                }
                return -EBUSY;
        }
 
-       nfacct = kzalloc(sizeof(struct nf_acct), GFP_KERNEL);
+       if (tb[NFACCT_FLAGS]) {
+               flags = ntohl(nla_get_be32(tb[NFACCT_FLAGS]));
+               if (flags & ~NFACCT_F_QUOTA)
+                       return -EOPNOTSUPP;
+               if ((flags & NFACCT_F_QUOTA) == NFACCT_F_QUOTA)
+                       return -EINVAL;
+               if (flags & NFACCT_F_OVERQUOTA)
+                       return -EINVAL;
+
+               size += sizeof(u64);
+       }
+
+       nfacct = kzalloc(sizeof(struct nf_acct) + size, GFP_KERNEL);
        if (nfacct == NULL)
                return -ENOMEM;
 
+       if (flags & NFACCT_F_QUOTA) {
+               u64 *quota = (u64 *)nfacct->data;
+
+               *quota = be64_to_cpu(nla_get_be64(tb[NFACCT_QUOTA]));
+               nfacct->flags = flags;
+       }
+
        strncpy(nfacct->name, nla_data(tb[NFACCT_NAME]), NFACCT_NAME_MAX);
 
        if (tb[NFACCT_BYTES]) {
@@ -117,6 +146,9 @@ nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
        if (type == NFNL_MSG_ACCT_GET_CTRZERO) {
                pkts = atomic64_xchg(&acct->pkts, 0);
                bytes = atomic64_xchg(&acct->bytes, 0);
+               smp_mb__before_atomic();
+               if (acct->flags & NFACCT_F_QUOTA)
+                       clear_bit(NFACCT_F_OVERQUOTA, &acct->flags);
        } else {
                pkts = atomic64_read(&acct->pkts);
                bytes = atomic64_read(&acct->bytes);
@@ -125,7 +157,13 @@ nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
            nla_put_be64(skb, NFACCT_BYTES, cpu_to_be64(bytes)) ||
            nla_put_be32(skb, NFACCT_USE, htonl(atomic_read(&acct->refcnt))))
                goto nla_put_failure;
+       if (acct->flags & NFACCT_F_QUOTA) {
+               u64 *quota = (u64 *)acct->data;
 
+               if (nla_put_be32(skb, NFACCT_FLAGS, htonl(acct->flags)) ||
+                   nla_put_be64(skb, NFACCT_QUOTA, cpu_to_be64(*quota)))
+                       goto nla_put_failure;
+       }
        nlmsg_end(skb, nlh);
        return skb->len;
 
@@ -270,6 +308,8 @@ static const struct nla_policy nfnl_acct_policy[NFACCT_MAX+1] = {
        [NFACCT_NAME] = { .type = NLA_NUL_STRING, .len = NFACCT_NAME_MAX-1 },
        [NFACCT_BYTES] = { .type = NLA_U64 },
        [NFACCT_PKTS] = { .type = NLA_U64 },
+       [NFACCT_FLAGS] = { .type = NLA_U32 },
+       [NFACCT_QUOTA] = { .type = NLA_U64 },
 };
 
 static const struct nfnl_callback nfnl_acct_cb[NFNL_MSG_ACCT_MAX] = {
@@ -336,6 +376,50 @@ void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct)
 }
 EXPORT_SYMBOL_GPL(nfnl_acct_update);
 
+static void nfnl_overquota_report(struct nf_acct *nfacct)
+{
+       int ret;
+       struct sk_buff *skb;
+
+       skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
+       if (skb == NULL)
+               return;
+
+       ret = nfnl_acct_fill_info(skb, 0, 0, NFNL_MSG_ACCT_OVERQUOTA, 0,
+                                 nfacct);
+       if (ret <= 0) {
+               kfree_skb(skb);
+               return;
+       }
+       netlink_broadcast(init_net.nfnl, skb, 0, NFNLGRP_ACCT_QUOTA,
+                         GFP_ATOMIC);
+}
+
+int nfnl_acct_overquota(const struct sk_buff *skb, struct nf_acct *nfacct)
+{
+       u64 now;
+       u64 *quota;
+       int ret = NFACCT_UNDERQUOTA;
+
+       /* no place here if we don't have a quota */
+       if (!(nfacct->flags & NFACCT_F_QUOTA))
+               return NFACCT_NO_QUOTA;
+
+       quota = (u64 *)nfacct->data;
+       now = (nfacct->flags & NFACCT_F_QUOTA_PKTS) ?
+              atomic64_read(&nfacct->pkts) : atomic64_read(&nfacct->bytes);
+
+       ret = now > *quota;
+
+       if (now >= *quota &&
+           !test_and_set_bit(NFACCT_F_OVERQUOTA, &nfacct->flags)) {
+               nfnl_overquota_report(nfacct);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(nfnl_acct_overquota);
+
 static int __init nfnl_acct_init(void)
 {
        int ret;
index bd0d41e693416167b4f149f64117e440a5134496..cc5603016242ea8e1f5cdce1d633e3a2687276ae 100644 (file)
@@ -215,22 +215,14 @@ static void nft_ct_l3proto_module_put(uint8_t family)
                nf_ct_l3proto_module_put(family);
 }
 
-static int nft_ct_init_validate_get(const struct nft_expr *expr,
-                                   const struct nlattr * const tb[])
+static int nft_ct_get_init(const struct nft_ctx *ctx,
+                          const struct nft_expr *expr,
+                          const struct nlattr * const tb[])
 {
        struct nft_ct *priv = nft_expr_priv(expr);
+       int err;
 
-       if (tb[NFTA_CT_DIRECTION] != NULL) {
-               priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]);
-               switch (priv->dir) {
-               case IP_CT_DIR_ORIGINAL:
-               case IP_CT_DIR_REPLY:
-                       break;
-               default:
-                       return -EINVAL;
-               }
-       }
-
+       priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
        switch (priv->key) {
        case NFT_CT_STATE:
        case NFT_CT_DIRECTION:
@@ -262,55 +254,55 @@ static int nft_ct_init_validate_get(const struct nft_expr *expr,
                return -EOPNOTSUPP;
        }
 
-       return 0;
-}
-
-static int nft_ct_init_validate_set(uint32_t key)
-{
-       switch (key) {
-       case NFT_CT_MARK:
-               break;
-       default:
-               return -EOPNOTSUPP;
+       if (tb[NFTA_CT_DIRECTION] != NULL) {
+               priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]);
+               switch (priv->dir) {
+               case IP_CT_DIR_ORIGINAL:
+               case IP_CT_DIR_REPLY:
+                       break;
+               default:
+                       return -EINVAL;
+               }
        }
 
+       priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG]));
+       err = nft_validate_output_register(priv->dreg);
+       if (err < 0)
+               return err;
+
+       err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
+       if (err < 0)
+               return err;
+
+       err = nft_ct_l3proto_try_module_get(ctx->afi->family);
+       if (err < 0)
+               return err;
+
        return 0;
 }
 
-static int nft_ct_init(const struct nft_ctx *ctx,
-                      const struct nft_expr *expr,
-                      const struct nlattr * const tb[])
+static int nft_ct_set_init(const struct nft_ctx *ctx,
+                          const struct nft_expr *expr,
+                          const struct nlattr * const tb[])
 {
        struct nft_ct *priv = nft_expr_priv(expr);
        int err;
 
        priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
-
-       if (tb[NFTA_CT_DREG]) {
-               err = nft_ct_init_validate_get(expr, tb);
-               if (err < 0)
-                       return err;
-
-               priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG]));
-               err = nft_validate_output_register(priv->dreg);
-               if (err < 0)
-                       return err;
-
-               err = nft_validate_data_load(ctx, priv->dreg, NULL,
-                                            NFT_DATA_VALUE);
-               if (err < 0)
-                       return err;
-       } else {
-               err = nft_ct_init_validate_set(priv->key);
-               if (err < 0)
-                       return err;
-
-               priv->sreg = ntohl(nla_get_be32(tb[NFTA_CT_SREG]));
-               err = nft_validate_input_register(priv->sreg);
-               if (err < 0)
-                       return err;
+       switch (priv->key) {
+#ifdef CONFIG_NF_CONNTRACK_MARK
+       case NFT_CT_MARK:
+               break;
+#endif
+       default:
+               return -EOPNOTSUPP;
        }
 
+       priv->sreg = ntohl(nla_get_be32(tb[NFTA_CT_SREG]));
+       err = nft_validate_input_register(priv->sreg);
+       if (err < 0)
+               return err;
+
        err = nft_ct_l3proto_try_module_get(ctx->afi->family);
        if (err < 0)
                return err;
@@ -370,7 +362,7 @@ static const struct nft_expr_ops nft_ct_get_ops = {
        .type           = &nft_ct_type,
        .size           = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
        .eval           = nft_ct_get_eval,
-       .init           = nft_ct_init,
+       .init           = nft_ct_get_init,
        .destroy        = nft_ct_destroy,
        .dump           = nft_ct_get_dump,
 };
@@ -379,7 +371,7 @@ static const struct nft_expr_ops nft_ct_set_ops = {
        .type           = &nft_ct_type,
        .size           = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
        .eval           = nft_ct_set_eval,
-       .init           = nft_ct_init,
+       .init           = nft_ct_set_init,
        .destroy        = nft_ct_destroy,
        .dump           = nft_ct_set_dump,
 };
index 3b1ad876d6b028f987ccf8e78ceb9639d767e6bf..4080ed6a072bf7c4bb414e19a38f9a663557198a 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/list.h>
+#include <linux/log2.h>
 #include <linux/jhash.h>
 #include <linux/netlink.h>
 #include <linux/vmalloc.h>
@@ -19,7 +20,7 @@
 #include <linux/netfilter/nf_tables.h>
 #include <net/netfilter/nf_tables.h>
 
-#define NFT_HASH_MIN_SIZE      4
+#define NFT_HASH_MIN_SIZE      4UL
 
 struct nft_hash {
        struct nft_hash_table __rcu     *tbl;
@@ -27,7 +28,6 @@ struct nft_hash {
 
 struct nft_hash_table {
        unsigned int                    size;
-       unsigned int                    elements;
        struct nft_hash_elem __rcu      *buckets[];
 };
 
@@ -76,10 +76,12 @@ static bool nft_hash_lookup(const struct nft_set *set,
 
 static void nft_hash_tbl_free(const struct nft_hash_table *tbl)
 {
-       if (is_vmalloc_addr(tbl))
-               vfree(tbl);
-       else
-               kfree(tbl);
+       kvfree(tbl);
+}
+
+static unsigned int nft_hash_tbl_size(unsigned int nelem)
+{
+       return max(roundup_pow_of_two(nelem * 4 / 3), NFT_HASH_MIN_SIZE);
 }
 
 static struct nft_hash_table *nft_hash_tbl_alloc(unsigned int nbuckets)
@@ -161,7 +163,6 @@ static int nft_hash_tbl_expand(const struct nft_set *set, struct nft_hash *priv)
                        break;
                }
        }
-       ntbl->elements = tbl->elements;
 
        /* Publish new table */
        rcu_assign_pointer(priv->tbl, ntbl);
@@ -201,7 +202,6 @@ static int nft_hash_tbl_shrink(const struct nft_set *set, struct nft_hash *priv)
                        ;
                RCU_INIT_POINTER(*pprev, tbl->buckets[i + ntbl->size]);
        }
-       ntbl->elements = tbl->elements;
 
        /* Publish new table */
        rcu_assign_pointer(priv->tbl, ntbl);
@@ -237,10 +237,9 @@ static int nft_hash_insert(const struct nft_set *set,
        h = nft_hash_data(&he->key, tbl->size, set->klen);
        RCU_INIT_POINTER(he->next, tbl->buckets[h]);
        rcu_assign_pointer(tbl->buckets[h], he);
-       tbl->elements++;
 
        /* Expand table when exceeding 75% load */
-       if (tbl->elements > tbl->size / 4 * 3)
+       if (set->nelems + 1 > tbl->size / 4 * 3)
                nft_hash_tbl_expand(set, priv);
 
        return 0;
@@ -268,10 +267,9 @@ static void nft_hash_remove(const struct nft_set *set,
        RCU_INIT_POINTER(*pprev, he->next);
        synchronize_rcu();
        kfree(he);
-       tbl->elements--;
 
        /* Shrink table beneath 30% load */
-       if (tbl->elements < tbl->size * 3 / 10 &&
+       if (set->nelems - 1 < tbl->size * 3 / 10 &&
            tbl->size > NFT_HASH_MIN_SIZE)
                nft_hash_tbl_shrink(set, priv);
 }
@@ -335,17 +333,23 @@ static unsigned int nft_hash_privsize(const struct nlattr * const nla[])
 }
 
 static int nft_hash_init(const struct nft_set *set,
+                        const struct nft_set_desc *desc,
                         const struct nlattr * const tb[])
 {
        struct nft_hash *priv = nft_set_priv(set);
        struct nft_hash_table *tbl;
+       unsigned int size;
 
        if (unlikely(!nft_hash_rnd_initted)) {
                get_random_bytes(&nft_hash_rnd, 4);
                nft_hash_rnd_initted = true;
        }
 
-       tbl = nft_hash_tbl_alloc(NFT_HASH_MIN_SIZE);
+       size = NFT_HASH_MIN_SIZE;
+       if (desc->size)
+               size = nft_hash_tbl_size(desc->size);
+
+       tbl = nft_hash_tbl_alloc(size);
        if (tbl == NULL)
                return -ENOMEM;
        RCU_INIT_POINTER(priv->tbl, tbl);
@@ -369,8 +373,37 @@ static void nft_hash_destroy(const struct nft_set *set)
        kfree(tbl);
 }
 
+static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features,
+                             struct nft_set_estimate *est)
+{
+       unsigned int esize;
+
+       esize = sizeof(struct nft_hash_elem);
+       if (features & NFT_SET_MAP)
+               esize += FIELD_SIZEOF(struct nft_hash_elem, data[0]);
+
+       if (desc->size) {
+               est->size = sizeof(struct nft_hash) +
+                           nft_hash_tbl_size(desc->size) *
+                           sizeof(struct nft_hash_elem *) +
+                           desc->size * esize;
+       } else {
+               /* Resizing happens when the load drops below 30% or goes
+                * above 75%. The average of 52.5% load (approximated by 50%)
+                * is used for the size estimation of the hash buckets,
+                * meaning we calculate two buckets per element.
+                */
+               est->size = esize + 2 * sizeof(struct nft_hash_elem *);
+       }
+
+       est->class = NFT_SET_CLASS_O_1;
+
+       return true;
+}
+
 static struct nft_set_ops nft_hash_ops __read_mostly = {
        .privsize       = nft_hash_privsize,
+       .estimate       = nft_hash_estimate,
        .init           = nft_hash_init,
        .destroy        = nft_hash_destroy,
        .get            = nft_hash_get,
index 7fd2bea8aa239f347dc461c7bc45869dac405573..6404a726d17b78fc6db6f411216195e68db63950 100644 (file)
@@ -56,8 +56,14 @@ static int nft_lookup_init(const struct nft_ctx *ctx,
                return -EINVAL;
 
        set = nf_tables_set_lookup(ctx->table, tb[NFTA_LOOKUP_SET]);
-       if (IS_ERR(set))
-               return PTR_ERR(set);
+       if (IS_ERR(set)) {
+               if (tb[NFTA_LOOKUP_SET_ID]) {
+                       set = nf_tables_set_lookup_byid(ctx->net,
+                                                       tb[NFTA_LOOKUP_SET_ID]);
+               }
+               if (IS_ERR(set))
+                       return PTR_ERR(set);
+       }
 
        priv->sreg = ntohl(nla_get_be32(tb[NFTA_LOOKUP_SREG]));
        err = nft_validate_input_register(priv->sreg);
index 425cf39af8907f1d0618b0904121073ea4dc116a..852b178c6ae7fa2f7dbd6f7887404033da45971f 100644 (file)
 #include <net/sock.h>
 #include <net/tcp_states.h> /* for TCP_TIME_WAIT */
 #include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nft_meta.h>
 
-struct nft_meta {
-       enum nft_meta_keys      key:8;
-       union {
-               enum nft_registers      dreg:8;
-               enum nft_registers      sreg:8;
-       };
-};
-
-static void nft_meta_get_eval(const struct nft_expr *expr,
-                             struct nft_data data[NFT_REG_MAX + 1],
-                             const struct nft_pktinfo *pkt)
+void nft_meta_get_eval(const struct nft_expr *expr,
+                      struct nft_data data[NFT_REG_MAX + 1],
+                      const struct nft_pktinfo *pkt)
 {
        const struct nft_meta *priv = nft_expr_priv(expr);
        const struct sk_buff *skb = pkt->skb;
@@ -140,10 +133,11 @@ static void nft_meta_get_eval(const struct nft_expr *expr,
 err:
        data[NFT_REG_VERDICT].verdict = NFT_BREAK;
 }
+EXPORT_SYMBOL_GPL(nft_meta_get_eval);
 
-static void nft_meta_set_eval(const struct nft_expr *expr,
-                             struct nft_data data[NFT_REG_MAX + 1],
-                             const struct nft_pktinfo *pkt)
+void nft_meta_set_eval(const struct nft_expr *expr,
+                      struct nft_data data[NFT_REG_MAX + 1],
+                      const struct nft_pktinfo *pkt)
 {
        const struct nft_meta *meta = nft_expr_priv(expr);
        struct sk_buff *skb = pkt->skb;
@@ -163,28 +157,24 @@ static void nft_meta_set_eval(const struct nft_expr *expr,
                WARN_ON(1);
        }
 }
+EXPORT_SYMBOL_GPL(nft_meta_set_eval);
 
-static const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = {
+const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = {
        [NFTA_META_DREG]        = { .type = NLA_U32 },
        [NFTA_META_KEY]         = { .type = NLA_U32 },
        [NFTA_META_SREG]        = { .type = NLA_U32 },
 };
+EXPORT_SYMBOL_GPL(nft_meta_policy);
 
-static int nft_meta_init_validate_set(uint32_t key)
+int nft_meta_get_init(const struct nft_ctx *ctx,
+                     const struct nft_expr *expr,
+                     const struct nlattr * const tb[])
 {
-       switch (key) {
-       case NFT_META_MARK:
-       case NFT_META_PRIORITY:
-       case NFT_META_NFTRACE:
-               return 0;
-       default:
-               return -EOPNOTSUPP;
-       }
-}
+       struct nft_meta *priv = nft_expr_priv(expr);
+       int err;
 
-static int nft_meta_init_validate_get(uint32_t key)
-{
-       switch (key) {
+       priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
+       switch (priv->key) {
        case NFT_META_LEN:
        case NFT_META_PROTOCOL:
        case NFT_META_NFPROTO:
@@ -205,39 +195,41 @@ static int nft_meta_init_validate_get(uint32_t key)
 #ifdef CONFIG_NETWORK_SECMARK
        case NFT_META_SECMARK:
 #endif
-               return 0;
+               break;
        default:
                return -EOPNOTSUPP;
        }
 
+       priv->dreg = ntohl(nla_get_be32(tb[NFTA_META_DREG]));
+       err = nft_validate_output_register(priv->dreg);
+       if (err < 0)
+               return err;
+
+       err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
+       if (err < 0)
+               return err;
+
+       return 0;
 }
+EXPORT_SYMBOL_GPL(nft_meta_get_init);
 
-static int nft_meta_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
-                        const struct nlattr * const tb[])
+int nft_meta_set_init(const struct nft_ctx *ctx,
+                     const struct nft_expr *expr,
+                     const struct nlattr * const tb[])
 {
        struct nft_meta *priv = nft_expr_priv(expr);
        int err;
 
        priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
-
-       if (tb[NFTA_META_DREG]) {
-               err = nft_meta_init_validate_get(priv->key);
-               if (err < 0)
-                       return err;
-
-               priv->dreg = ntohl(nla_get_be32(tb[NFTA_META_DREG]));
-               err = nft_validate_output_register(priv->dreg);
-               if (err < 0)
-                       return err;
-
-               return nft_validate_data_load(ctx, priv->dreg, NULL,
-                                             NFT_DATA_VALUE);
+       switch (priv->key) {
+       case NFT_META_MARK:
+       case NFT_META_PRIORITY:
+       case NFT_META_NFTRACE:
+               break;
+       default:
+               return -EOPNOTSUPP;
        }
 
-       err = nft_meta_init_validate_set(priv->key);
-       if (err < 0)
-               return err;
-
        priv->sreg = ntohl(nla_get_be32(tb[NFTA_META_SREG]));
        err = nft_validate_input_register(priv->sreg);
        if (err < 0)
@@ -245,9 +237,10 @@ static int nft_meta_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(nft_meta_set_init);
 
-static int nft_meta_get_dump(struct sk_buff *skb,
-                            const struct nft_expr *expr)
+int nft_meta_get_dump(struct sk_buff *skb,
+                     const struct nft_expr *expr)
 {
        const struct nft_meta *priv = nft_expr_priv(expr);
 
@@ -260,9 +253,10 @@ static int nft_meta_get_dump(struct sk_buff *skb,
 nla_put_failure:
        return -1;
 }
+EXPORT_SYMBOL_GPL(nft_meta_get_dump);
 
-static int nft_meta_set_dump(struct sk_buff *skb,
-                            const struct nft_expr *expr)
+int nft_meta_set_dump(struct sk_buff *skb,
+                     const struct nft_expr *expr)
 {
        const struct nft_meta *priv = nft_expr_priv(expr);
 
@@ -276,13 +270,14 @@ static int nft_meta_set_dump(struct sk_buff *skb,
 nla_put_failure:
        return -1;
 }
+EXPORT_SYMBOL_GPL(nft_meta_set_dump);
 
 static struct nft_expr_type nft_meta_type;
 static const struct nft_expr_ops nft_meta_get_ops = {
        .type           = &nft_meta_type,
        .size           = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
        .eval           = nft_meta_get_eval,
-       .init           = nft_meta_init,
+       .init           = nft_meta_get_init,
        .dump           = nft_meta_get_dump,
 };
 
@@ -290,7 +285,7 @@ static const struct nft_expr_ops nft_meta_set_ops = {
        .type           = &nft_meta_type,
        .size           = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
        .eval           = nft_meta_set_eval,
-       .init           = nft_meta_init,
+       .init           = nft_meta_set_init,
        .dump           = nft_meta_set_dump,
 };
 
index e21d69d13506b95946820f24641fe7e48d885866..e1836ff881994dc08e957d414c6e14a0fbbff919 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/netfilter/nf_tables.h>
 #include <net/netfilter/nf_tables.h>
 
+static DEFINE_SPINLOCK(nft_rbtree_lock);
+
 struct nft_rbtree {
        struct rb_root          root;
 };
@@ -38,6 +40,7 @@ static bool nft_rbtree_lookup(const struct nft_set *set,
        const struct rb_node *parent = priv->root.rb_node;
        int d;
 
+       spin_lock_bh(&nft_rbtree_lock);
        while (parent != NULL) {
                rbe = rb_entry(parent, struct nft_rbtree_elem, node);
 
@@ -53,6 +56,8 @@ found:
                                goto out;
                        if (set->flags & NFT_SET_MAP)
                                nft_data_copy(data, rbe->data);
+
+                       spin_unlock_bh(&nft_rbtree_lock);
                        return true;
                }
        }
@@ -62,6 +67,7 @@ found:
                goto found;
        }
 out:
+       spin_unlock_bh(&nft_rbtree_lock);
        return false;
 }
 
@@ -124,9 +130,12 @@ static int nft_rbtree_insert(const struct nft_set *set,
            !(rbe->flags & NFT_SET_ELEM_INTERVAL_END))
                nft_data_copy(rbe->data, &elem->data);
 
+       spin_lock_bh(&nft_rbtree_lock);
        err = __nft_rbtree_insert(set, rbe);
        if (err < 0)
                kfree(rbe);
+
+       spin_unlock_bh(&nft_rbtree_lock);
        return err;
 }
 
@@ -136,7 +145,9 @@ static void nft_rbtree_remove(const struct nft_set *set,
        struct nft_rbtree *priv = nft_set_priv(set);
        struct nft_rbtree_elem *rbe = elem->cookie;
 
+       spin_lock_bh(&nft_rbtree_lock);
        rb_erase(&rbe->node, &priv->root);
+       spin_unlock_bh(&nft_rbtree_lock);
        kfree(rbe);
 }
 
@@ -147,6 +158,7 @@ static int nft_rbtree_get(const struct nft_set *set, struct nft_set_elem *elem)
        struct nft_rbtree_elem *rbe;
        int d;
 
+       spin_lock_bh(&nft_rbtree_lock);
        while (parent != NULL) {
                rbe = rb_entry(parent, struct nft_rbtree_elem, node);
 
@@ -161,9 +173,11 @@ static int nft_rbtree_get(const struct nft_set *set, struct nft_set_elem *elem)
                            !(rbe->flags & NFT_SET_ELEM_INTERVAL_END))
                                nft_data_copy(&elem->data, rbe->data);
                        elem->flags = rbe->flags;
+                       spin_unlock_bh(&nft_rbtree_lock);
                        return 0;
                }
        }
+       spin_unlock_bh(&nft_rbtree_lock);
        return -ENOENT;
 }
 
@@ -176,6 +190,7 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx,
        struct nft_set_elem elem;
        struct rb_node *node;
 
+       spin_lock_bh(&nft_rbtree_lock);
        for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) {
                if (iter->count < iter->skip)
                        goto cont;
@@ -188,11 +203,14 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx,
                elem.flags = rbe->flags;
 
                iter->err = iter->fn(ctx, set, iter, &elem);
-               if (iter->err < 0)
+               if (iter->err < 0) {
+                       spin_unlock_bh(&nft_rbtree_lock);
                        return;
+               }
 cont:
                iter->count++;
        }
+       spin_unlock_bh(&nft_rbtree_lock);
 }
 
 static unsigned int nft_rbtree_privsize(const struct nlattr * const nla[])
@@ -201,6 +219,7 @@ static unsigned int nft_rbtree_privsize(const struct nlattr * const nla[])
 }
 
 static int nft_rbtree_init(const struct nft_set *set,
+                          const struct nft_set_desc *desc,
                           const struct nlattr * const nla[])
 {
        struct nft_rbtree *priv = nft_set_priv(set);
@@ -215,15 +234,37 @@ static void nft_rbtree_destroy(const struct nft_set *set)
        struct nft_rbtree_elem *rbe;
        struct rb_node *node;
 
+       spin_lock_bh(&nft_rbtree_lock);
        while ((node = priv->root.rb_node) != NULL) {
                rb_erase(node, &priv->root);
                rbe = rb_entry(node, struct nft_rbtree_elem, node);
                nft_rbtree_elem_destroy(set, rbe);
        }
+       spin_unlock_bh(&nft_rbtree_lock);
+}
+
+static bool nft_rbtree_estimate(const struct nft_set_desc *desc, u32 features,
+                               struct nft_set_estimate *est)
+{
+       unsigned int nsize;
+
+       nsize = sizeof(struct nft_rbtree_elem);
+       if (features & NFT_SET_MAP)
+               nsize += FIELD_SIZEOF(struct nft_rbtree_elem, data[0]);
+
+       if (desc->size)
+               est->size = sizeof(struct nft_rbtree) + desc->size * nsize;
+       else
+               est->size = nsize;
+
+       est->class = NFT_SET_CLASS_O_LOG_N;
+
+       return true;
 }
 
 static struct nft_set_ops nft_rbtree_ops __read_mostly = {
        .privsize       = nft_rbtree_privsize,
+       .estimate       = nft_rbtree_estimate,
        .init           = nft_rbtree_init,
        .destroy        = nft_rbtree_destroy,
        .insert         = nft_rbtree_insert,
index 12d4da8e6c7728ed6bcc723f5b583fbfd0dc895c..bbffdbdaf6031bef784042c064dded271d6a406f 100644 (file)
@@ -23,10 +23,11 @@ MODULE_ALIAS("ip6t_bpf");
 static int bpf_mt_check(const struct xt_mtchk_param *par)
 {
        struct xt_bpf_info *info = par->matchinfo;
-       struct sock_fprog program;
+       struct sock_fprog_kern program;
 
        program.len = info->bpf_program_num_elem;
-       program.filter = (struct sock_filter __user *) info->bpf_program;
+       program.filter = info->bpf_program;
+
        if (sk_unattached_filter_create(&info->filter, &program)) {
                pr_info("bpf: check failed: parse error\n");
                return -EINVAL;
index b3be0ef21f198ca8c7b025e1a775af9bd662d20e..8c646ed9c921bca1fbf507c1aa97c1dca60d8df1 100644 (file)
@@ -21,11 +21,14 @@ MODULE_ALIAS("ip6t_nfacct");
 
 static bool nfacct_mt(const struct sk_buff *skb, struct xt_action_param *par)
 {
+       int overquota;
        const struct xt_nfacct_match_info *info = par->targinfo;
 
        nfnl_acct_update(skb, info->nfacct);
 
-       return true;
+       overquota = nfnl_acct_overquota(skb, info->nfacct);
+
+       return overquota == NFACCT_UNDERQUOTA ? false : true;
 }
 
 static int
index 1e657cf715c478d9a9ec77411bf7536b14278d14..a9faae89f95533a53da4651efb2f737e3a0952b4 100644 (file)
@@ -313,10 +313,7 @@ out:
 
 static void recent_table_free(void *addr)
 {
-       if (is_vmalloc_addr(addr))
-               vfree(addr);
-       else
-               kfree(addr);
+       kvfree(addr);
 }
 
 static int recent_mt_check(const struct xt_mtchk_param *par,
index f22757a29cd054e02e40372535a6407735a12ddc..15c731f03fa664a64f7bf3cdde36cf1a8e4150b6 100644 (file)
@@ -1206,7 +1206,8 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol,
        struct module *module = NULL;
        struct mutex *cb_mutex;
        struct netlink_sock *nlk;
-       void (*bind)(int group);
+       int (*bind)(int group);
+       void (*unbind)(int group);
        int err = 0;
 
        sock->state = SS_UNCONNECTED;
@@ -1232,6 +1233,7 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol,
                err = -EPROTONOSUPPORT;
        cb_mutex = nl_table[protocol].cb_mutex;
        bind = nl_table[protocol].bind;
+       unbind = nl_table[protocol].unbind;
        netlink_unlock_table();
 
        if (err < 0)
@@ -1248,6 +1250,7 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol,
        nlk = nlk_sk(sock->sk);
        nlk->module = module;
        nlk->netlink_bind = bind;
+       nlk->netlink_unbind = unbind;
 out:
        return err;
 
@@ -1301,6 +1304,7 @@ static int netlink_release(struct socket *sock)
                        kfree_rcu(old, rcu);
                        nl_table[sk->sk_protocol].module = NULL;
                        nl_table[sk->sk_protocol].bind = NULL;
+                       nl_table[sk->sk_protocol].unbind = NULL;
                        nl_table[sk->sk_protocol].flags = 0;
                        nl_table[sk->sk_protocol].registered = 0;
                }
@@ -1478,6 +1482,19 @@ static int netlink_realloc_groups(struct sock *sk)
        return err;
 }
 
+static void netlink_unbind(int group, long unsigned int groups,
+                          struct netlink_sock *nlk)
+{
+       int undo;
+
+       if (!nlk->netlink_unbind)
+               return;
+
+       for (undo = 0; undo < group; undo++)
+               if (test_bit(group, &groups))
+                       nlk->netlink_unbind(undo);
+}
+
 static int netlink_bind(struct socket *sock, struct sockaddr *addr,
                        int addr_len)
 {
@@ -1486,6 +1503,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,
        struct netlink_sock *nlk = nlk_sk(sk);
        struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr;
        int err;
+       long unsigned int groups = nladdr->nl_groups;
 
        if (addr_len < sizeof(struct sockaddr_nl))
                return -EINVAL;
@@ -1494,7 +1512,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,
                return -EINVAL;
 
        /* Only superuser is allowed to listen multicasts */
-       if (nladdr->nl_groups) {
+       if (groups) {
                if (!netlink_allowed(sock, NL_CFG_F_NONROOT_RECV))
                        return -EPERM;
                err = netlink_realloc_groups(sk);
@@ -1502,37 +1520,45 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,
                        return err;
        }
 
-       if (nlk->portid) {
+       if (nlk->portid)
                if (nladdr->nl_pid != nlk->portid)
                        return -EINVAL;
-       } else {
+
+       if (nlk->netlink_bind && groups) {
+               int group;
+
+               for (group = 0; group < nlk->ngroups; group++) {
+                       if (!test_bit(group, &groups))
+                               continue;
+                       err = nlk->netlink_bind(group);
+                       if (!err)
+                               continue;
+                       netlink_unbind(group, groups, nlk);
+                       return err;
+               }
+       }
+
+       if (!nlk->portid) {
                err = nladdr->nl_pid ?
                        netlink_insert(sk, net, nladdr->nl_pid) :
                        netlink_autobind(sock);
-               if (err)
+               if (err) {
+                       netlink_unbind(nlk->ngroups - 1, groups, nlk);
                        return err;
+               }
        }
 
-       if (!nladdr->nl_groups && (nlk->groups == NULL || !(u32)nlk->groups[0]))
+       if (!groups && (nlk->groups == NULL || !(u32)nlk->groups[0]))
                return 0;
 
        netlink_table_grab();
        netlink_update_subscriptions(sk, nlk->subscriptions +
-                                        hweight32(nladdr->nl_groups) -
+                                        hweight32(groups) -
                                         hweight32(nlk->groups[0]));
-       nlk->groups[0] = (nlk->groups[0] & ~0xffffffffUL) | nladdr->nl_groups;
+       nlk->groups[0] = (nlk->groups[0] & ~0xffffffffUL) | groups;
        netlink_update_listeners(sk);
        netlink_table_ungrab();
 
-       if (nlk->netlink_bind && nlk->groups[0]) {
-               int i;
-
-               for (i = 0; i < nlk->ngroups; i++) {
-                       if (test_bit(i, nlk->groups))
-                               nlk->netlink_bind(i);
-               }
-       }
-
        return 0;
 }
 
@@ -2170,13 +2196,17 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname,
                        return err;
                if (!val || val - 1 >= nlk->ngroups)
                        return -EINVAL;
+               if (optname == NETLINK_ADD_MEMBERSHIP && nlk->netlink_bind) {
+                       err = nlk->netlink_bind(val);
+                       if (err)
+                               return err;
+               }
                netlink_table_grab();
                netlink_update_socket_mc(nlk, val,
                                         optname == NETLINK_ADD_MEMBERSHIP);
                netlink_table_ungrab();
-
-               if (nlk->netlink_bind)
-                       nlk->netlink_bind(val);
+               if (optname == NETLINK_DROP_MEMBERSHIP && nlk->netlink_unbind)
+                       nlk->netlink_unbind(val);
 
                err = 0;
                break;
index ed13a790b00e1684215e04c6a68f71f9491cb3c9..0b59d441f5b6bcff8d03b08211e5a901c170dfe8 100644 (file)
@@ -38,7 +38,8 @@ struct netlink_sock {
        struct mutex            *cb_mutex;
        struct mutex            cb_def_mutex;
        void                    (*netlink_rcv)(struct sk_buff *skb);
-       void                    (*netlink_bind)(int group);
+       int                     (*netlink_bind)(int group);
+       void                    (*netlink_unbind)(int group);
        struct module           *module;
 #ifdef CONFIG_NETLINK_MMAP
        struct mutex            pg_vec_lock;
@@ -74,7 +75,8 @@ struct netlink_table {
        unsigned int            groups;
        struct mutex            *cb_mutex;
        struct module           *module;
-       void                    (*bind)(int group);
+       int                     (*bind)(int group);
+       void                    (*unbind)(int group);
        bool                    (*compare)(struct net *net, struct sock *sock);
        int                     registered;
 };
index a3ba3ca0ff9281dec15c0b4d42002394583c1d8d..76393f2f4b225713b9c85ee5aae722e109881080 100644 (file)
@@ -317,7 +317,7 @@ static void genl_unregister_mc_groups(struct genl_family *family)
        }
 }
 
-static int genl_validate_ops(struct genl_family *family)
+static int genl_validate_ops(const struct genl_family *family)
 {
        const struct genl_ops *ops = family->ops;
        unsigned int n_ops = family->n_ops;
@@ -337,10 +337,6 @@ static int genl_validate_ops(struct genl_family *family)
                                return -EINVAL;
        }
 
-       /* family is not registered yet, so no locking needed */
-       family->ops = ops;
-       family->n_ops = n_ops;
-
        return 0;
 }
 
index 3759add68b1b8af4b4eae50af6eb4a3cd98fd345..71ad7eefddd4dfa69319ca06b3595e1bebca459f 100644 (file)
@@ -71,6 +71,7 @@ static inline int digital_in_send_cmd(struct nfc_digital_dev *ddev,
 void digital_poll_next_tech(struct nfc_digital_dev *ddev);
 
 int digital_in_send_sens_req(struct nfc_digital_dev *ddev, u8 rf_tech);
+int digital_in_send_sensb_req(struct nfc_digital_dev *ddev, u8 rf_tech);
 int digital_in_send_sensf_req(struct nfc_digital_dev *ddev, u8 rf_tech);
 int digital_in_send_iso15693_inv_req(struct nfc_digital_dev *ddev, u8 rf_tech);
 
index e01e15dbf1abe1541aeb2643cf5521e8e1419e75..a6ce3c627e4e40ff4a6fcdb0bb0a2704dd0872b0 100644 (file)
@@ -22,6 +22,8 @@
 #define DIGITAL_PROTO_NFCA_RF_TECH \
        (NFC_PROTO_JEWEL_MASK | NFC_PROTO_MIFARE_MASK | NFC_PROTO_NFC_DEP_MASK)
 
+#define DIGITAL_PROTO_NFCB_RF_TECH     NFC_PROTO_ISO14443_B_MASK
+
 #define DIGITAL_PROTO_NFCF_RF_TECH \
        (NFC_PROTO_FELICA_MASK | NFC_PROTO_NFC_DEP_MASK)
 
@@ -345,6 +347,12 @@ int digital_target_found(struct nfc_digital_dev *ddev,
                add_crc = digital_skb_add_crc_a;
                break;
 
+       case NFC_PROTO_ISO14443_B:
+               framing = NFC_DIGITAL_FRAMING_NFCB_T4T;
+               check_crc = digital_skb_check_crc_b;
+               add_crc = digital_skb_add_crc_b;
+               break;
+
        default:
                pr_err("Invalid protocol %d\n", protocol);
                return -EINVAL;
@@ -378,6 +386,8 @@ int digital_target_found(struct nfc_digital_dev *ddev,
 
 void digital_poll_next_tech(struct nfc_digital_dev *ddev)
 {
+       u8 rand_mod;
+
        digital_switch_rf(ddev, 0);
 
        mutex_lock(&ddev->poll_lock);
@@ -387,8 +397,8 @@ void digital_poll_next_tech(struct nfc_digital_dev *ddev)
                return;
        }
 
-       ddev->poll_tech_index = (ddev->poll_tech_index + 1) %
-                               ddev->poll_tech_count;
+       get_random_bytes(&rand_mod, sizeof(rand_mod));
+       ddev->poll_tech_index = rand_mod % ddev->poll_tech_count;
 
        mutex_unlock(&ddev->poll_lock);
 
@@ -475,6 +485,10 @@ static int digital_start_poll(struct nfc_dev *nfc_dev, __u32 im_protocols,
                digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_106A,
                                      digital_in_send_sens_req);
 
+       if (matching_im_protocols & DIGITAL_PROTO_NFCB_RF_TECH)
+               digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_106B,
+                                     digital_in_send_sensb_req);
+
        if (matching_im_protocols & DIGITAL_PROTO_NFCF_RF_TECH) {
                digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_212F,
                                      digital_in_send_sensf_req);
@@ -635,7 +649,8 @@ static void digital_in_send_complete(struct nfc_digital_dev *ddev, void *arg,
                goto done;
        }
 
-       if (ddev->curr_protocol == NFC_PROTO_ISO14443) {
+       if ((ddev->curr_protocol == NFC_PROTO_ISO14443) ||
+           (ddev->curr_protocol == NFC_PROTO_ISO14443_B)) {
                rc = digital_in_iso_dep_pull_sod(ddev, resp);
                if (rc)
                        goto done;
@@ -676,7 +691,8 @@ static int digital_in_send(struct nfc_dev *nfc_dev, struct nfc_target *target,
                goto exit;
        }
 
-       if (ddev->curr_protocol == NFC_PROTO_ISO14443) {
+       if ((ddev->curr_protocol == NFC_PROTO_ISO14443) ||
+           (ddev->curr_protocol == NFC_PROTO_ISO14443_B)) {
                rc = digital_in_iso_dep_push_sod(ddev, skb);
                if (rc)
                        goto exit;
@@ -747,6 +763,8 @@ struct nfc_digital_dev *nfc_digital_allocate_device(struct nfc_digital_ops *ops,
                ddev->protocols |= NFC_PROTO_ISO15693_MASK;
        if (supported_protocols & NFC_PROTO_ISO14443_MASK)
                ddev->protocols |= NFC_PROTO_ISO14443_MASK;
+       if (supported_protocols & NFC_PROTO_ISO14443_B_MASK)
+               ddev->protocols |= NFC_PROTO_ISO14443_B_MASK;
 
        ddev->tx_headroom = tx_headroom + DIGITAL_MAX_HEADER_LEN;
        ddev->tx_tailroom = tx_tailroom + DIGITAL_CRC_LEN;
index d4ed25ff723fd9ace06d03444d7fcc3f9b3b6b27..171cb9949ab58560bc3d1c997d963e2928cf5aa5 100644 (file)
@@ -224,9 +224,8 @@ int digital_in_send_atr_req(struct nfc_digital_dev *ddev,
 
        ddev->skb_add_crc(skb);
 
-       digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res, target);
-
-       return 0;
+       return digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res,
+                                  target);
 }
 
 static int digital_in_send_rtox(struct nfc_digital_dev *ddev,
index 278c3fed27e01f255374713ab2c0a545752671ee..c2c1c0189b7cbe931c279bc039df4d26801902d4 100644 (file)
 #define DIGITAL_MIFARE_READ_RES_LEN 16
 #define DIGITAL_MIFARE_ACK_RES 0x0A
 
+#define DIGITAL_CMD_SENSB_REQ                  0x05
+#define DIGITAL_SENSB_ADVANCED                 BIT(5)
+#define DIGITAL_SENSB_EXTENDED                 BIT(4)
+#define DIGITAL_SENSB_ALLB_REQ                 BIT(3)
+#define DIGITAL_SENSB_N(n)                     ((n) & 0x7)
+
+#define DIGITAL_CMD_SENSB_RES                  0x50
+
+#define DIGITAL_CMD_ATTRIB_REQ                 0x1D
+#define DIGITAL_ATTRIB_P1_TR0_DEFAULT          (0x0 << 6)
+#define DIGITAL_ATTRIB_P1_TR1_DEFAULT          (0x0 << 4)
+#define DIGITAL_ATTRIB_P1_SUPRESS_EOS          BIT(3)
+#define DIGITAL_ATTRIB_P1_SUPRESS_SOS          BIT(2)
+#define DIGITAL_ATTRIB_P2_LISTEN_POLL_1                (0x0 << 6)
+#define DIGITAL_ATTRIB_P2_POLL_LISTEN_1                (0x0 << 4)
+#define DIGITAL_ATTRIB_P2_MAX_FRAME_256                0x8
+#define DIGITAL_ATTRIB_P4_DID(n)               ((n) & 0xf)
+
 #define DIGITAL_CMD_SENSF_REQ  0x00
 #define DIGITAL_CMD_SENSF_RES  0x01
 
@@ -75,6 +93,7 @@ static const u8 digital_ats_fsc[] = {
 };
 
 #define DIGITAL_ATS_FSCI(t0) ((t0) & 0x0F)
+#define DIGITAL_SENSB_FSCI(pi2) (((pi2) & 0xF0) >> 4)
 #define DIGITAL_ATS_MAX_FSC  256
 
 #define DIGITAL_RATS_BYTE1 0xE0
@@ -92,6 +111,32 @@ struct digital_sel_req {
        u8 bcc;
 } __packed;
 
+struct digital_sensb_req {
+       u8 cmd;
+       u8 afi;
+       u8 param;
+} __packed;
+
+struct digital_sensb_res {
+       u8 cmd;
+       u8 nfcid0[4];
+       u8 app_data[4];
+       u8 proto_info[3];
+} __packed;
+
+struct digital_attrib_req {
+       u8 cmd;
+       u8 nfcid0[4];
+       u8 param1;
+       u8 param2;
+       u8 param3;
+       u8 param4;
+} __packed;
+
+struct digital_attrib_res {
+       u8 mbli_did;
+} __packed;
+
 struct digital_sensf_req {
        u8 cmd;
        u8 sc1;
@@ -531,6 +576,175 @@ int digital_in_recv_mifare_res(struct sk_buff *resp)
        return -EIO;
 }
 
+static void digital_in_recv_attrib_res(struct nfc_digital_dev *ddev, void *arg,
+                                      struct sk_buff *resp)
+{
+       struct nfc_target *target = arg;
+       struct digital_attrib_res *attrib_res;
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       if (resp->len < sizeof(*attrib_res)) {
+               PROTOCOL_ERR("12.6.2");
+               rc = -EIO;
+               goto exit;
+       }
+
+       attrib_res = (struct digital_attrib_res *)resp->data;
+
+       if (attrib_res->mbli_did & 0x0f) {
+               PROTOCOL_ERR("12.6.2.1");
+               rc = -EIO;
+               goto exit;
+       }
+
+       rc = digital_target_found(ddev, target, NFC_PROTO_ISO14443_B);
+
+exit:
+       dev_kfree_skb(resp);
+       kfree(target);
+
+       if (rc)
+               digital_poll_next_tech(ddev);
+}
+
+static int digital_in_send_attrib_req(struct nfc_digital_dev *ddev,
+                              struct nfc_target *target,
+                              struct digital_sensb_res *sensb_res)
+{
+       struct digital_attrib_req *attrib_req;
+       struct sk_buff *skb;
+       int rc;
+
+       skb = digital_skb_alloc(ddev, sizeof(*attrib_req));
+       if (!skb)
+               return -ENOMEM;
+
+       attrib_req = (struct digital_attrib_req *)skb_put(skb,
+                                                         sizeof(*attrib_req));
+
+       attrib_req->cmd = DIGITAL_CMD_ATTRIB_REQ;
+       memcpy(attrib_req->nfcid0, sensb_res->nfcid0,
+              sizeof(attrib_req->nfcid0));
+       attrib_req->param1 = DIGITAL_ATTRIB_P1_TR0_DEFAULT |
+                            DIGITAL_ATTRIB_P1_TR1_DEFAULT;
+       attrib_req->param2 = DIGITAL_ATTRIB_P2_LISTEN_POLL_1 |
+                            DIGITAL_ATTRIB_P2_POLL_LISTEN_1 |
+                            DIGITAL_ATTRIB_P2_MAX_FRAME_256;
+       attrib_req->param3 = sensb_res->proto_info[1] & 0x07;
+       attrib_req->param4 = DIGITAL_ATTRIB_P4_DID(0);
+
+       rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_attrib_res,
+                                target);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+static void digital_in_recv_sensb_res(struct nfc_digital_dev *ddev, void *arg,
+                                     struct sk_buff *resp)
+{
+       struct nfc_target *target = NULL;
+       struct digital_sensb_res *sensb_res;
+       u8 fsci;
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       if (resp->len != sizeof(*sensb_res)) {
+               PROTOCOL_ERR("5.6.2.1");
+               rc = -EIO;
+               goto exit;
+       }
+
+       sensb_res = (struct digital_sensb_res *)resp->data;
+
+       if (sensb_res->cmd != DIGITAL_CMD_SENSB_RES) {
+               PROTOCOL_ERR("5.6.2");
+               rc = -EIO;
+               goto exit;
+       }
+
+       if (!(sensb_res->proto_info[1] & BIT(0))) {
+               PROTOCOL_ERR("5.6.2.12");
+               rc = -EIO;
+               goto exit;
+       }
+
+       if (sensb_res->proto_info[1] & BIT(3)) {
+               PROTOCOL_ERR("5.6.2.16");
+               rc = -EIO;
+               goto exit;
+       }
+
+       fsci = DIGITAL_SENSB_FSCI(sensb_res->proto_info[1]);
+       if (fsci >= 8)
+               ddev->target_fsc = DIGITAL_ATS_MAX_FSC;
+       else
+               ddev->target_fsc = digital_ats_fsc[fsci];
+
+       target = kzalloc(sizeof(struct nfc_target), GFP_KERNEL);
+       if (!target) {
+               rc = -ENOMEM;
+               goto exit;
+       }
+
+       rc = digital_in_send_attrib_req(ddev, target, sensb_res);
+
+exit:
+       dev_kfree_skb(resp);
+
+       if (rc) {
+               kfree(target);
+               digital_poll_next_tech(ddev);
+       }
+}
+
+int digital_in_send_sensb_req(struct nfc_digital_dev *ddev, u8 rf_tech)
+{
+       struct digital_sensb_req *sensb_req;
+       struct sk_buff *skb;
+       int rc;
+
+       rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH,
+                                    NFC_DIGITAL_RF_TECH_106B);
+       if (rc)
+               return rc;
+
+       rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+                                    NFC_DIGITAL_FRAMING_NFCB);
+       if (rc)
+               return rc;
+
+       skb = digital_skb_alloc(ddev, sizeof(*sensb_req));
+       if (!skb)
+               return -ENOMEM;
+
+       sensb_req = (struct digital_sensb_req *)skb_put(skb,
+                                                       sizeof(*sensb_req));
+
+       sensb_req->cmd = DIGITAL_CMD_SENSB_REQ;
+       sensb_req->afi = 0x00; /* All families and sub-families */
+       sensb_req->param = DIGITAL_SENSB_N(0);
+
+       rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sensb_res,
+                                NULL);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
 static void digital_in_recv_sensf_res(struct nfc_digital_dev *ddev, void *arg,
                                   struct sk_buff *resp)
 {
@@ -877,6 +1091,18 @@ exit:
        dev_kfree_skb(resp);
 }
 
+static void digital_tg_recv_atr_or_sensf_req(struct nfc_digital_dev *ddev,
+               void *arg, struct sk_buff *resp)
+{
+       if (!IS_ERR(resp) && (resp->len >= 2) &&
+                       (resp->data[1] == DIGITAL_CMD_SENSF_REQ))
+               digital_tg_recv_sensf_req(ddev, arg, resp);
+       else
+               digital_tg_recv_atr_req(ddev, arg, resp);
+
+       return;
+}
+
 static int digital_tg_send_sensf_res(struct nfc_digital_dev *ddev,
                              struct digital_sensf_req *sensf_req)
 {
@@ -887,7 +1113,7 @@ static int digital_tg_send_sensf_res(struct nfc_digital_dev *ddev,
 
        size = sizeof(struct digital_sensf_res);
 
-       if (sensf_req->rc != DIGITAL_SENSF_REQ_RC_NONE)
+       if (sensf_req->rc == DIGITAL_SENSF_REQ_RC_NONE)
                size -= sizeof(sensf_res->rd);
 
        skb = digital_skb_alloc(ddev, size);
@@ -922,7 +1148,7 @@ static int digital_tg_send_sensf_res(struct nfc_digital_dev *ddev,
                digital_skb_add_crc_f(skb);
 
        rc = digital_tg_send_cmd(ddev, skb, 300,
-                                digital_tg_recv_atr_req, NULL);
+                                digital_tg_recv_atr_or_sensf_req, NULL);
        if (rc)
                kfree_skb(skb);
 
index a9f4d2e62d8de267a94a689fe3fab53a1a79339d..677d24bb70f8af24569ef574404ba4b1b9e1c9f0 100644 (file)
@@ -26,6 +26,8 @@
 
 #include "hci.h"
 
+#define MAX_FWI 4949
+
 static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
                               const u8 *param, size_t param_len,
                               data_exchange_cb_t cb, void *cb_context)
@@ -37,7 +39,7 @@ static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
         * for all commands?
         */
        return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_COMMAND, cmd,
-                                     param, param_len, cb, cb_context, 3000);
+                                     param, param_len, cb, cb_context, MAX_FWI);
 }
 
 /*
@@ -82,7 +84,7 @@ static int nfc_hci_execute_cmd(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
                                                    NFC_HCI_HCP_COMMAND, cmd,
                                                    param, param_len,
                                                    nfc_hci_execute_cb, &hcp_ew,
-                                                   3000);
+                                                   MAX_FWI);
        if (hcp_ew.exec_result < 0)
                return hcp_ew.exec_result;
 
index d45b638e77c78ec25b8228eacf2ea0ac754ad8d7..47403705197e85930d9f3e480d2e2d5dbd2012fa 100644 (file)
@@ -225,7 +225,7 @@ int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate)
                        goto exit;
                }
 
-               targets->sens_res = be16_to_cpu(*(u16 *)atqa_skb->data);
+               targets->sens_res = be16_to_cpu(*(__be16 *)atqa_skb->data);
                targets->sel_res = sak_skb->data[0];
 
                r = nfc_hci_get_param(hdev, NFC_HCI_RF_READER_A_GATE,
@@ -380,34 +380,31 @@ static int hci_dev_session_init(struct nfc_hci_dev *hdev)
        if (r < 0)
                goto disconnect_all;
 
-       if (skb->len && skb->len == strlen(hdev->init_data.session_id))
-               if (memcmp(hdev->init_data.session_id, skb->data,
-                          skb->len) == 0) {
-                       /* TODO ELa: restore gate<->pipe table from
-                        * some TBD location.
-                        * note: it doesn't seem possible to get the chip
-                        * currently open gate/pipe table.
-                        * It is only possible to obtain the supported
-                        * gate list.
-                        */
+       if (skb->len && skb->len == strlen(hdev->init_data.session_id) &&
+               (memcmp(hdev->init_data.session_id, skb->data,
+                          skb->len) == 0) && hdev->ops->load_session) {
+               /* Restore gate<->pipe table from some proprietary location. */
 
-                       /* goto exit
-                        * For now, always do a full initialization */
-               }
+               r = hdev->ops->load_session(hdev);
 
-       r = nfc_hci_disconnect_all_gates(hdev);
-       if (r < 0)
-               goto exit;
+               if (r < 0)
+                       goto disconnect_all;
+       } else {
 
-       r = hci_dev_connect_gates(hdev, hdev->init_data.gate_count,
-                                 hdev->init_data.gates);
-       if (r < 0)
-               goto disconnect_all;
+               r = nfc_hci_disconnect_all_gates(hdev);
+               if (r < 0)
+                       goto exit;
 
-       r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE,
-                             NFC_HCI_ADMIN_SESSION_IDENTITY,
-                             hdev->init_data.session_id,
-                             strlen(hdev->init_data.session_id));
+               r = hci_dev_connect_gates(hdev, hdev->init_data.gate_count,
+                                         hdev->init_data.gates);
+               if (r < 0)
+                       goto disconnect_all;
+
+               r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE,
+                               NFC_HCI_ADMIN_SESSION_IDENTITY,
+                               hdev->init_data.session_id,
+                               strlen(hdev->init_data.session_id));
+       }
        if (r == 0)
                goto exit;
 
index bec6ed15f5037ef9ffbc75f93e450e9ef1fd81b7..a3ad69a4c648c76779bf496153bfa7eb01f5354c 100644 (file)
@@ -387,7 +387,7 @@ int nfc_llcp_send_symm(struct nfc_dev *dev)
 
        __net_timestamp(skb);
 
-       nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_TX);
+       nfc_llcp_send_to_raw_sock(local, skb, NFC_DIRECTION_TX);
 
        return nfc_data_exchange(dev, local->target_idx, skb,
                                 nfc_llcp_recv, local);
index b4671958fcf935c3c3d984afc18f473bc882eae4..51e7887973171f999eb7c77984d7dddf081a5b75 100644 (file)
@@ -680,16 +680,17 @@ void nfc_llcp_send_to_raw_sock(struct nfc_llcp_local *local,
                        continue;
 
                if (skb_copy == NULL) {
-                       skb_copy = __pskb_copy(skb, NFC_LLCP_RAW_HEADER_SIZE,
-                                              GFP_ATOMIC);
+                       skb_copy = __pskb_copy_fclone(skb, NFC_RAW_HEADER_SIZE,
+                                                     GFP_ATOMIC, true);
 
                        if (skb_copy == NULL)
                                continue;
 
-                       data = skb_push(skb_copy, NFC_LLCP_RAW_HEADER_SIZE);
+                       data = skb_push(skb_copy, NFC_RAW_HEADER_SIZE);
 
                        data[0] = local->dev ? local->dev->idx : 0xFF;
-                       data[1] = direction;
+                       data[1] = direction & 0x01;
+                       data[1] |= (RAW_PAYLOAD_LLCP << 1);
                }
 
                nskb = skb_clone(skb_copy, GFP_ATOMIC);
@@ -747,7 +748,7 @@ static void nfc_llcp_tx_work(struct work_struct *work)
                        __net_timestamp(skb);
 
                        nfc_llcp_send_to_raw_sock(local, skb,
-                                                 NFC_LLCP_DIRECTION_TX);
+                                                 NFC_DIRECTION_TX);
 
                        ret = nfc_data_exchange(local->dev, local->target_idx,
                                                skb, nfc_llcp_recv, local);
@@ -1476,7 +1477,7 @@ static void nfc_llcp_rx_work(struct work_struct *work)
 
        __net_timestamp(skb);
 
-       nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_RX);
+       nfc_llcp_send_to_raw_sock(local, skb, NFC_DIRECTION_RX);
 
        nfc_llcp_rx_skb(local, skb);
 
index 6c34ac978501705a0c743289b34b2f62750bc441..2b400e1a869522b9ecbca787a0ff2938f6109c26 100644 (file)
@@ -861,6 +861,10 @@ static int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb)
        /* Get rid of skb owner, prior to sending to the driver. */
        skb_orphan(skb);
 
+       /* Send copy to sniffer */
+       nfc_send_to_raw_sock(ndev->nfc_dev, skb,
+                            RAW_PAYLOAD_NCI, NFC_DIRECTION_TX);
+
        return ndev->ops->send(ndev, skb);
 }
 
@@ -935,6 +939,11 @@ static void nci_rx_work(struct work_struct *work)
        struct sk_buff *skb;
 
        while ((skb = skb_dequeue(&ndev->rx_q))) {
+
+               /* Send copy to sniffer */
+               nfc_send_to_raw_sock(ndev->nfc_dev, skb,
+                                    RAW_PAYLOAD_NCI, NFC_DIRECTION_RX);
+
                /* Process frame */
                switch (nci_mt(skb->data)) {
                case NCI_MT_RSP_PKT:
index 1e905097456b77501bc0a950aa932c45205a8eb1..f8f6af231381b336b111a9c01ce0ccb2dfc72856 100644 (file)
@@ -366,7 +366,6 @@ static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev,
                        struct nci_rf_intf_activated_ntf *ntf, __u8 *data)
 {
        struct activation_params_poll_nfc_dep *poll;
-       int i;
 
        switch (ntf->activation_rf_tech_and_mode) {
        case NCI_NFC_A_PASSIVE_POLL_MODE:
@@ -374,10 +373,8 @@ static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev,
                poll = &ntf->activation_params.poll_nfc_dep;
                poll->atr_res_len = min_t(__u8, *data++, 63);
                pr_debug("atr_res_len %d\n", poll->atr_res_len);
-               if (poll->atr_res_len > 0) {
-                       for (i = 0; i < poll->atr_res_len; i++)
-                               poll->atr_res[poll->atr_res_len-1-i] = data[i];
-               }
+               if (poll->atr_res_len > 0)
+                       memcpy(poll->atr_res, data, poll->atr_res_len);
                break;
 
        default:
index 9d6e74f7e6b34ba5ed0d9022a96fdc8061b479c7..88d60064890e3a9ca93db4c88fdcf840e077fabe 100644 (file)
@@ -40,6 +40,12 @@ struct nfc_rawsock {
        struct work_struct tx_work;
        bool tx_work_scheduled;
 };
+
+struct nfc_sock_list {
+       struct hlist_head head;
+       rwlock_t          lock;
+};
+
 #define nfc_rawsock(sk) ((struct nfc_rawsock *) sk)
 #define to_rawsock_sk(_tx_work) \
        ((struct sock *) container_of(_tx_work, struct nfc_rawsock, tx_work))
index c27a6e86cae459f0f70c2c4875614edac3518f98..11c3544ea5466f54f8038ca19f500a831d1ca79f 100644 (file)
 
 #include "nfc.h"
 
+static struct nfc_sock_list raw_sk_list = {
+       .lock = __RW_LOCK_UNLOCKED(raw_sk_list.lock)
+};
+
+static void nfc_sock_link(struct nfc_sock_list *l, struct sock *sk)
+{
+       write_lock(&l->lock);
+       sk_add_node(sk, &l->head);
+       write_unlock(&l->lock);
+}
+
+static void nfc_sock_unlink(struct nfc_sock_list *l, struct sock *sk)
+{
+       write_lock(&l->lock);
+       sk_del_node_init(sk);
+       write_unlock(&l->lock);
+}
+
 static void rawsock_write_queue_purge(struct sock *sk)
 {
        pr_debug("sk=%p\n", sk);
@@ -57,6 +75,9 @@ static int rawsock_release(struct socket *sock)
        if (!sk)
                return 0;
 
+       if (sock->type == SOCK_RAW)
+               nfc_sock_unlink(&raw_sk_list, sk);
+
        sock_orphan(sk);
        sock_put(sk);
 
@@ -275,6 +296,26 @@ static const struct proto_ops rawsock_ops = {
        .mmap           = sock_no_mmap,
 };
 
+static const struct proto_ops rawsock_raw_ops = {
+       .family         = PF_NFC,
+       .owner          = THIS_MODULE,
+       .release        = rawsock_release,
+       .bind           = sock_no_bind,
+       .connect        = sock_no_connect,
+       .socketpair     = sock_no_socketpair,
+       .accept         = sock_no_accept,
+       .getname        = sock_no_getname,
+       .poll           = datagram_poll,
+       .ioctl          = sock_no_ioctl,
+       .listen         = sock_no_listen,
+       .shutdown       = sock_no_shutdown,
+       .setsockopt     = sock_no_setsockopt,
+       .getsockopt     = sock_no_getsockopt,
+       .sendmsg        = sock_no_sendmsg,
+       .recvmsg        = rawsock_recvmsg,
+       .mmap           = sock_no_mmap,
+};
+
 static void rawsock_destruct(struct sock *sk)
 {
        pr_debug("sk=%p\n", sk);
@@ -300,10 +341,13 @@ static int rawsock_create(struct net *net, struct socket *sock,
 
        pr_debug("sock=%p\n", sock);
 
-       if (sock->type != SOCK_SEQPACKET)
+       if ((sock->type != SOCK_SEQPACKET) && (sock->type != SOCK_RAW))
                return -ESOCKTNOSUPPORT;
 
-       sock->ops = &rawsock_ops;
+       if (sock->type == SOCK_RAW)
+               sock->ops = &rawsock_raw_ops;
+       else
+               sock->ops = &rawsock_ops;
 
        sk = sk_alloc(net, PF_NFC, GFP_ATOMIC, nfc_proto->proto);
        if (!sk)
@@ -313,13 +357,53 @@ static int rawsock_create(struct net *net, struct socket *sock,
        sk->sk_protocol = nfc_proto->id;
        sk->sk_destruct = rawsock_destruct;
        sock->state = SS_UNCONNECTED;
-
-       INIT_WORK(&nfc_rawsock(sk)->tx_work, rawsock_tx_work);
-       nfc_rawsock(sk)->tx_work_scheduled = false;
+       if (sock->type == SOCK_RAW)
+               nfc_sock_link(&raw_sk_list, sk);
+       else {
+               INIT_WORK(&nfc_rawsock(sk)->tx_work, rawsock_tx_work);
+               nfc_rawsock(sk)->tx_work_scheduled = false;
+       }
 
        return 0;
 }
 
+void nfc_send_to_raw_sock(struct nfc_dev *dev, struct sk_buff *skb,
+                         u8 payload_type, u8 direction)
+{
+       struct sk_buff *skb_copy = NULL, *nskb;
+       struct sock *sk;
+       u8 *data;
+
+       read_lock(&raw_sk_list.lock);
+
+       sk_for_each(sk, &raw_sk_list.head) {
+               if (!skb_copy) {
+                       skb_copy = __pskb_copy_fclone(skb, NFC_RAW_HEADER_SIZE,
+                                                     GFP_ATOMIC, true);
+                       if (!skb_copy)
+                               continue;
+
+                       data = skb_push(skb_copy, NFC_RAW_HEADER_SIZE);
+
+                       data[0] = dev ? dev->idx : 0xFF;
+                       data[1] = direction & 0x01;
+                       data[1] |= (payload_type << 1);
+               }
+
+               nskb = skb_clone(skb_copy, GFP_ATOMIC);
+               if (!nskb)
+                       continue;
+
+               if (sock_queue_rcv_skb(sk, nskb))
+                       kfree_skb(nskb);
+       }
+
+       read_unlock(&raw_sk_list.lock);
+
+       kfree_skb(skb_copy);
+}
+EXPORT_SYMBOL(nfc_send_to_raw_sock);
+
 static struct proto rawsock_proto = {
        .name     = "NFC_RAW",
        .owner    = THIS_MODULE,
index 2c77e7b1a913241d85f11d19c25b5e182b5757f1..c36856a457ca963c735e89e36478a53ba60bb453 100644 (file)
@@ -134,8 +134,8 @@ static int set_eth_addr(struct sk_buff *skb,
 
        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);
+       ether_addr_copy(eth_hdr(skb)->h_source, eth_key->eth_src);
+       ether_addr_copy(eth_hdr(skb)->h_dest, eth_key->eth_dst);
 
        ovs_skb_postpush_rcsum(skb, eth_hdr(skb), ETH_ALEN * 2);
 
index a3276e3c4feb065278b5195652378157619ff94c..0d407bca81e3573983bc47791dddf5561d4f8ee1 100644 (file)
 #include <linux/netfilter_ipv4.h>
 #include <linux/inetdevice.h>
 #include <linux/list.h>
-#include <linux/lockdep.h>
 #include <linux/openvswitch.h>
 #include <linux/rculist.h>
 #include <linux/dmi.h>
-#include <linux/workqueue.h>
+#include <linux/genetlink.h>
+#include <net/genetlink.h>
 #include <net/genetlink.h>
 #include <net/net_namespace.h>
 #include <net/netns/generic.h>
 
 int ovs_net_id __read_mostly;
 
+static struct genl_family dp_packet_genl_family;
+static struct genl_family dp_flow_genl_family;
+static struct genl_family dp_datapath_genl_family;
+
+static struct genl_multicast_group ovs_dp_flow_multicast_group = {
+       .name = OVS_FLOW_MCGROUP
+};
+
+static struct genl_multicast_group ovs_dp_datapath_multicast_group = {
+       .name = OVS_DATAPATH_MCGROUP
+};
+
+struct genl_multicast_group ovs_dp_vport_multicast_group = {
+       .name = OVS_VPORT_MCGROUP
+};
+
+/* Check if need to build a reply message.
+ * OVS userspace sets the NLM_F_ECHO flag if it needs the reply. */
+static bool ovs_must_notify(struct genl_info *info,
+                           const struct genl_multicast_group *grp)
+{
+       return info->nlhdr->nlmsg_flags & NLM_F_ECHO ||
+               netlink_has_listeners(genl_info_net(info)->genl_sock, 0);
+}
+
 static void ovs_notify(struct genl_family *family,
                       struct sk_buff *skb, struct genl_info *info)
 {
@@ -173,6 +198,7 @@ static struct hlist_head *vport_hash_bucket(const struct datapath *dp,
        return &dp->ports[port_no & (DP_VPORT_HASH_BUCKETS - 1)];
 }
 
+/* Called with ovs_mutex or RCU read lock. */
 struct vport *ovs_lookup_vport(const struct datapath *dp, u16 port_no)
 {
        struct vport *vport;
@@ -262,16 +288,6 @@ out:
        u64_stats_update_end(&stats->syncp);
 }
 
-static struct genl_family dp_packet_genl_family = {
-       .id = GENL_ID_GENERATE,
-       .hdrsize = sizeof(struct ovs_header),
-       .name = OVS_PACKET_FAMILY,
-       .version = OVS_PACKET_VERSION,
-       .maxattr = OVS_PACKET_ATTR_MAX,
-       .netnsok = true,
-       .parallel_ops = true,
-};
-
 int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
                  const struct dp_upcall_info *upcall_info)
 {
@@ -524,7 +540,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
                packet->protocol = htons(ETH_P_802_2);
 
        /* Build an sw_flow for sending this packet. */
-       flow = ovs_flow_alloc(false);
+       flow = ovs_flow_alloc();
        err = PTR_ERR(flow);
        if (IS_ERR(flow))
                goto err_kfree_skb;
@@ -590,6 +606,18 @@ static const struct genl_ops dp_packet_genl_ops[] = {
        }
 };
 
+static struct genl_family dp_packet_genl_family = {
+       .id = GENL_ID_GENERATE,
+       .hdrsize = sizeof(struct ovs_header),
+       .name = OVS_PACKET_FAMILY,
+       .version = OVS_PACKET_VERSION,
+       .maxattr = OVS_PACKET_ATTR_MAX,
+       .netnsok = true,
+       .parallel_ops = true,
+       .ops = dp_packet_genl_ops,
+       .n_ops = ARRAY_SIZE(dp_packet_genl_ops),
+};
+
 static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats,
                         struct ovs_dp_megaflow_stats *mega_stats)
 {
@@ -621,26 +649,6 @@ static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats,
        }
 }
 
-static const struct nla_policy flow_policy[OVS_FLOW_ATTR_MAX + 1] = {
-       [OVS_FLOW_ATTR_KEY] = { .type = NLA_NESTED },
-       [OVS_FLOW_ATTR_ACTIONS] = { .type = NLA_NESTED },
-       [OVS_FLOW_ATTR_CLEAR] = { .type = NLA_FLAG },
-};
-
-static struct genl_family dp_flow_genl_family = {
-       .id = GENL_ID_GENERATE,
-       .hdrsize = sizeof(struct ovs_header),
-       .name = OVS_FLOW_FAMILY,
-       .version = OVS_FLOW_VERSION,
-       .maxattr = OVS_FLOW_ATTR_MAX,
-       .netnsok = true,
-       .parallel_ops = true,
-};
-
-static struct genl_multicast_group ovs_dp_flow_multicast_group = {
-       .name = OVS_FLOW_MCGROUP
-};
-
 static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts)
 {
        return NLMSG_ALIGN(sizeof(struct ovs_header))
@@ -652,8 +660,8 @@ static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts)
                + nla_total_size(acts->actions_len); /* OVS_FLOW_ATTR_ACTIONS */
 }
 
-/* Called with ovs_mutex. */
-static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
+/* Called with ovs_mutex or RCU read lock. */
+static int ovs_flow_cmd_fill_info(const struct sw_flow *flow, int dp_ifindex,
                                  struct sk_buff *skb, u32 portid,
                                  u32 seq, u32 flags, u8 cmd)
 {
@@ -670,7 +678,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
        if (!ovs_header)
                return -EMSGSIZE;
 
-       ovs_header->dp_ifindex = get_dpifindex(dp);
+       ovs_header->dp_ifindex = dp_ifindex;
 
        /* Fill flow key. */
        nla = nla_nest_start(skb, OVS_FLOW_ATTR_KEY);
@@ -693,6 +701,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
        nla_nest_end(skb, nla);
 
        ovs_flow_stats_get(flow, &stats, &used, &tcp_flags);
+
        if (used &&
            nla_put_u64(skb, OVS_FLOW_ATTR_USED, ovs_flow_used_time(used)))
                goto nla_put_failure;
@@ -720,9 +729,9 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
                const struct sw_flow_actions *sf_acts;
 
                sf_acts = rcu_dereference_ovsl(flow->sf_acts);
-
                err = ovs_nla_put_actions(sf_acts->actions,
                                          sf_acts->actions_len, skb);
+
                if (!err)
                        nla_nest_end(skb, start);
                else {
@@ -743,113 +752,128 @@ error:
        return err;
 }
 
-static struct sk_buff *ovs_flow_cmd_alloc_info(struct sw_flow *flow,
-                                              struct genl_info *info)
+/* May not be called with RCU read lock. */
+static struct sk_buff *ovs_flow_cmd_alloc_info(const struct sw_flow_actions *acts,
+                                              struct genl_info *info,
+                                              bool always)
 {
-       size_t len;
+       struct sk_buff *skb;
+
+       if (!always && !ovs_must_notify(info, &ovs_dp_flow_multicast_group))
+               return NULL;
 
-       len = ovs_flow_cmd_msg_size(ovsl_dereference(flow->sf_acts));
+       skb = genlmsg_new_unicast(ovs_flow_cmd_msg_size(acts), info, GFP_KERNEL);
+       if (!skb)
+               return ERR_PTR(-ENOMEM);
 
-       return genlmsg_new_unicast(len, info, GFP_KERNEL);
+       return skb;
 }
 
-static struct sk_buff *ovs_flow_cmd_build_info(struct sw_flow *flow,
-                                              struct datapath *dp,
-                                              struct genl_info *info,
-                                              u8 cmd)
+/* Called with ovs_mutex. */
+static struct sk_buff *ovs_flow_cmd_build_info(const struct sw_flow *flow,
+                                              int dp_ifindex,
+                                              struct genl_info *info, u8 cmd,
+                                              bool always)
 {
        struct sk_buff *skb;
        int retval;
 
-       skb = ovs_flow_cmd_alloc_info(flow, info);
-       if (!skb)
-               return ERR_PTR(-ENOMEM);
+       skb = ovs_flow_cmd_alloc_info(ovsl_dereference(flow->sf_acts), info,
+                                     always);
+       if (!skb || IS_ERR(skb))
+               return skb;
 
-       retval = ovs_flow_cmd_fill_info(flow, dp, skb, info->snd_portid,
-                                       info->snd_seq, 0, cmd);
+       retval = ovs_flow_cmd_fill_info(flow, dp_ifindex, skb,
+                                       info->snd_portid, info->snd_seq, 0,
+                                       cmd);
        BUG_ON(retval < 0);
        return skb;
 }
 
-static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
+static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
 {
        struct nlattr **a = info->attrs;
        struct ovs_header *ovs_header = info->userhdr;
-       struct sw_flow_key key, masked_key;
-       struct sw_flow *flow = NULL;
+       struct sw_flow *flow, *new_flow;
        struct sw_flow_mask mask;
        struct sk_buff *reply;
        struct datapath *dp;
-       struct sw_flow_actions *acts = NULL;
+       struct sw_flow_actions *acts;
        struct sw_flow_match match;
-       bool exact_5tuple;
        int error;
 
-       /* Extract key. */
+       /* Must have key and actions. */
        error = -EINVAL;
        if (!a[OVS_FLOW_ATTR_KEY])
                goto error;
+       if (!a[OVS_FLOW_ATTR_ACTIONS])
+               goto error;
 
-       ovs_match_init(&match, &key, &mask);
-       error = ovs_nla_get_match(&match, &exact_5tuple,
+       /* Most of the time we need to allocate a new flow, do it before
+        * locking.
+        */
+       new_flow = ovs_flow_alloc();
+       if (IS_ERR(new_flow)) {
+               error = PTR_ERR(new_flow);
+               goto error;
+       }
+
+       /* Extract key. */
+       ovs_match_init(&match, &new_flow->unmasked_key, &mask);
+       error = ovs_nla_get_match(&match,
                                  a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]);
        if (error)
-               goto error;
+               goto err_kfree_flow;
+
+       ovs_flow_mask_key(&new_flow->key, &new_flow->unmasked_key, &mask);
 
        /* Validate actions. */
-       if (a[OVS_FLOW_ATTR_ACTIONS]) {
-               acts = ovs_nla_alloc_flow_actions(nla_len(a[OVS_FLOW_ATTR_ACTIONS]));
-               error = PTR_ERR(acts);
-               if (IS_ERR(acts))
-                       goto error;
+       acts = ovs_nla_alloc_flow_actions(nla_len(a[OVS_FLOW_ATTR_ACTIONS]));
+       error = PTR_ERR(acts);
+       if (IS_ERR(acts))
+               goto err_kfree_flow;
 
-               ovs_flow_mask_key(&masked_key, &key, &mask);
-               error = ovs_nla_copy_actions(a[OVS_FLOW_ATTR_ACTIONS],
-                                            &masked_key, 0, &acts);
-               if (error) {
-                       OVS_NLERR("Flow actions may not be safe on all matching packets.\n");
-                       goto err_kfree;
-               }
-       } else if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW) {
-               error = -EINVAL;
-               goto error;
+       error = ovs_nla_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], &new_flow->key,
+                                    0, &acts);
+       if (error) {
+               OVS_NLERR("Flow actions may not be safe on all matching packets.\n");
+               goto err_kfree_acts;
+       }
+
+       reply = ovs_flow_cmd_alloc_info(acts, info, false);
+       if (IS_ERR(reply)) {
+               error = PTR_ERR(reply);
+               goto err_kfree_acts;
        }
 
        ovs_lock();
        dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
-       error = -ENODEV;
-       if (!dp)
+       if (unlikely(!dp)) {
+               error = -ENODEV;
                goto err_unlock_ovs;
-
+       }
        /* Check if this is a duplicate flow */
-       flow = ovs_flow_tbl_lookup(&dp->table, &key);
-       if (!flow) {
-               /* Bail out if we're not allowed to create a new flow. */
-               error = -ENOENT;
-               if (info->genlhdr->cmd == OVS_FLOW_CMD_SET)
-                       goto err_unlock_ovs;
-
-               /* Allocate flow. */
-               flow = ovs_flow_alloc(!exact_5tuple);
-               if (IS_ERR(flow)) {
-                       error = PTR_ERR(flow);
-                       goto err_unlock_ovs;
-               }
-
-               flow->key = masked_key;
-               flow->unmasked_key = key;
-               rcu_assign_pointer(flow->sf_acts, acts);
+       flow = ovs_flow_tbl_lookup(&dp->table, &new_flow->unmasked_key);
+       if (likely(!flow)) {
+               rcu_assign_pointer(new_flow->sf_acts, acts);
 
                /* Put flow in bucket. */
-               error = ovs_flow_tbl_insert(&dp->table, flow, &mask);
-               if (error) {
+               error = ovs_flow_tbl_insert(&dp->table, new_flow, &mask);
+               if (unlikely(error)) {
                        acts = NULL;
-                       goto err_flow_free;
+                       goto err_unlock_ovs;
                }
 
-               reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW);
+               if (unlikely(reply)) {
+                       error = ovs_flow_cmd_fill_info(new_flow,
+                                                      ovs_header->dp_ifindex,
+                                                      reply, info->snd_portid,
+                                                      info->snd_seq, 0,
+                                                      OVS_FLOW_CMD_NEW);
+                       BUG_ON(error < 0);
+               }
+               ovs_unlock();
        } else {
-               /* We found a matching flow. */
                struct sw_flow_actions *old_acts;
 
                /* Bail out if we're not allowed to modify an existing flow.
@@ -858,40 +882,154 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                 * request.  We also accept NLM_F_EXCL in case that bug ever
                 * gets fixed.
                 */
-               error = -EEXIST;
-               if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW &&
-                   info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL))
+               if (unlikely(info->nlhdr->nlmsg_flags & (NLM_F_CREATE
+                                                        | NLM_F_EXCL))) {
+                       error = -EEXIST;
                        goto err_unlock_ovs;
-
+               }
                /* The unmasked key has to be the same for flow updates. */
-               if (!ovs_flow_cmp_unmasked_key(flow, &match))
+               if (unlikely(!ovs_flow_cmp_unmasked_key(flow, &match))) {
+                       error = -EEXIST;
                        goto err_unlock_ovs;
-
+               }
                /* Update actions. */
                old_acts = ovsl_dereference(flow->sf_acts);
                rcu_assign_pointer(flow->sf_acts, acts);
+
+               if (unlikely(reply)) {
+                       error = ovs_flow_cmd_fill_info(flow,
+                                                      ovs_header->dp_ifindex,
+                                                      reply, info->snd_portid,
+                                                      info->snd_seq, 0,
+                                                      OVS_FLOW_CMD_NEW);
+                       BUG_ON(error < 0);
+               }
+               ovs_unlock();
+
                ovs_nla_free_flow_actions(old_acts);
+               ovs_flow_free(new_flow, false);
+       }
+
+       if (reply)
+               ovs_notify(&dp_flow_genl_family, reply, info);
+       return 0;
+
+err_unlock_ovs:
+       ovs_unlock();
+       kfree_skb(reply);
+err_kfree_acts:
+       kfree(acts);
+err_kfree_flow:
+       ovs_flow_free(new_flow, false);
+error:
+       return error;
+}
+
+static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nlattr **a = info->attrs;
+       struct ovs_header *ovs_header = info->userhdr;
+       struct sw_flow_key key, masked_key;
+       struct sw_flow *flow;
+       struct sw_flow_mask mask;
+       struct sk_buff *reply = NULL;
+       struct datapath *dp;
+       struct sw_flow_actions *old_acts = NULL, *acts = NULL;
+       struct sw_flow_match match;
+       int error;
+
+       /* Extract key. */
+       error = -EINVAL;
+       if (!a[OVS_FLOW_ATTR_KEY])
+               goto error;
+
+       ovs_match_init(&match, &key, &mask);
+       error = ovs_nla_get_match(&match,
+                                 a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]);
+       if (error)
+               goto error;
+
+       /* Validate actions. */
+       if (a[OVS_FLOW_ATTR_ACTIONS]) {
+               acts = ovs_nla_alloc_flow_actions(nla_len(a[OVS_FLOW_ATTR_ACTIONS]));
+               error = PTR_ERR(acts);
+               if (IS_ERR(acts))
+                       goto error;
+
+               ovs_flow_mask_key(&masked_key, &key, &mask);
+               error = ovs_nla_copy_actions(a[OVS_FLOW_ATTR_ACTIONS],
+                                            &masked_key, 0, &acts);
+               if (error) {
+                       OVS_NLERR("Flow actions may not be safe on all matching packets.\n");
+                       goto err_kfree_acts;
+               }
+       }
+
+       /* Can allocate before locking if have acts. */
+       if (acts) {
+               reply = ovs_flow_cmd_alloc_info(acts, info, false);
+               if (IS_ERR(reply)) {
+                       error = PTR_ERR(reply);
+                       goto err_kfree_acts;
+               }
+       }
 
-               reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW);
+       ovs_lock();
+       dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
+       if (unlikely(!dp)) {
+               error = -ENODEV;
+               goto err_unlock_ovs;
+       }
+       /* Check that the flow exists. */
+       flow = ovs_flow_tbl_lookup(&dp->table, &key);
+       if (unlikely(!flow)) {
+               error = -ENOENT;
+               goto err_unlock_ovs;
+       }
+       /* The unmasked key has to be the same for flow updates. */
+       if (unlikely(!ovs_flow_cmp_unmasked_key(flow, &match))) {
+               error = -EEXIST;
+               goto err_unlock_ovs;
+       }
+       /* Update actions, if present. */
+       if (likely(acts)) {
+               old_acts = ovsl_dereference(flow->sf_acts);
+               rcu_assign_pointer(flow->sf_acts, acts);
 
-               /* Clear stats. */
-               if (a[OVS_FLOW_ATTR_CLEAR])
-                       ovs_flow_stats_clear(flow);
+               if (unlikely(reply)) {
+                       error = ovs_flow_cmd_fill_info(flow,
+                                                      ovs_header->dp_ifindex,
+                                                      reply, info->snd_portid,
+                                                      info->snd_seq, 0,
+                                                      OVS_FLOW_CMD_NEW);
+                       BUG_ON(error < 0);
+               }
+       } else {
+               /* Could not alloc without acts before locking. */
+               reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex,
+                                               info, OVS_FLOW_CMD_NEW, false);
+               if (unlikely(IS_ERR(reply))) {
+                       error = PTR_ERR(reply);
+                       goto err_unlock_ovs;
+               }
        }
+
+       /* Clear stats. */
+       if (a[OVS_FLOW_ATTR_CLEAR])
+               ovs_flow_stats_clear(flow);
        ovs_unlock();
 
-       if (!IS_ERR(reply))
+       if (reply)
                ovs_notify(&dp_flow_genl_family, reply, info);
-       else
-               genl_set_err(&dp_flow_genl_family, sock_net(skb->sk), 0,
-                            0, PTR_ERR(reply));
+       if (old_acts)
+               ovs_nla_free_flow_actions(old_acts);
+
        return 0;
 
-err_flow_free:
-       ovs_flow_free(flow, false);
 err_unlock_ovs:
        ovs_unlock();
-err_kfree:
+       kfree_skb(reply);
+err_kfree_acts:
        kfree(acts);
 error:
        return error;
@@ -914,7 +1052,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
        }
 
        ovs_match_init(&match, &key, NULL);
-       err = ovs_nla_get_match(&match, NULL, a[OVS_FLOW_ATTR_KEY], NULL);
+       err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL);
        if (err)
                return err;
 
@@ -931,7 +1069,8 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
                goto unlock;
        }
 
-       reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW);
+       reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex, info,
+                                       OVS_FLOW_CMD_NEW, true);
        if (IS_ERR(reply)) {
                err = PTR_ERR(reply);
                goto unlock;
@@ -955,45 +1094,53 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
        struct sw_flow_match match;
        int err;
 
+       if (likely(a[OVS_FLOW_ATTR_KEY])) {
+               ovs_match_init(&match, &key, NULL);
+               err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL);
+               if (unlikely(err))
+                       return err;
+       }
+
        ovs_lock();
        dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
-       if (!dp) {
+       if (unlikely(!dp)) {
                err = -ENODEV;
                goto unlock;
        }
 
-       if (!a[OVS_FLOW_ATTR_KEY]) {
+       if (unlikely(!a[OVS_FLOW_ATTR_KEY])) {
                err = ovs_flow_tbl_flush(&dp->table);
                goto unlock;
        }
 
-       ovs_match_init(&match, &key, NULL);
-       err = ovs_nla_get_match(&match, NULL, a[OVS_FLOW_ATTR_KEY], NULL);
-       if (err)
-               goto unlock;
-
        flow = ovs_flow_tbl_lookup(&dp->table, &key);
-       if (!flow || !ovs_flow_cmp_unmasked_key(flow, &match)) {
+       if (unlikely(!flow || !ovs_flow_cmp_unmasked_key(flow, &match))) {
                err = -ENOENT;
                goto unlock;
        }
 
-       reply = ovs_flow_cmd_alloc_info(flow, info);
-       if (!reply) {
-               err = -ENOMEM;
-               goto unlock;
-       }
-
        ovs_flow_tbl_remove(&dp->table, flow);
+       ovs_unlock();
 
-       err = ovs_flow_cmd_fill_info(flow, dp, reply, info->snd_portid,
-                                    info->snd_seq, 0, OVS_FLOW_CMD_DEL);
-       BUG_ON(err < 0);
+       reply = ovs_flow_cmd_alloc_info((const struct sw_flow_actions __force *) flow->sf_acts,
+                                       info, false);
+       if (likely(reply)) {
+               if (likely(!IS_ERR(reply))) {
+                       rcu_read_lock();        /*To keep RCU checker happy. */
+                       err = ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex,
+                                                    reply, info->snd_portid,
+                                                    info->snd_seq, 0,
+                                                    OVS_FLOW_CMD_DEL);
+                       rcu_read_unlock();
+                       BUG_ON(err < 0);
+
+                       ovs_notify(&dp_flow_genl_family, reply, info);
+               } else {
+                       netlink_set_err(sock_net(skb->sk)->genl_sock, 0, 0, PTR_ERR(reply));
+               }
+       }
 
        ovs_flow_free(flow, true);
-       ovs_unlock();
-
-       ovs_notify(&dp_flow_genl_family, reply, info);
        return 0;
 unlock:
        ovs_unlock();
@@ -1024,7 +1171,7 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
                if (!flow)
                        break;
 
-               if (ovs_flow_cmd_fill_info(flow, dp, skb,
+               if (ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex, skb,
                                           NETLINK_CB(cb->skb).portid,
                                           cb->nlh->nlmsg_seq, NLM_F_MULTI,
                                           OVS_FLOW_CMD_NEW) < 0)
@@ -1037,11 +1184,17 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
        return skb->len;
 }
 
-static const struct genl_ops dp_flow_genl_ops[] = {
+static const struct nla_policy flow_policy[OVS_FLOW_ATTR_MAX + 1] = {
+       [OVS_FLOW_ATTR_KEY] = { .type = NLA_NESTED },
+       [OVS_FLOW_ATTR_ACTIONS] = { .type = NLA_NESTED },
+       [OVS_FLOW_ATTR_CLEAR] = { .type = NLA_FLAG },
+};
+
+static struct genl_ops dp_flow_genl_ops[] = {
        { .cmd = OVS_FLOW_CMD_NEW,
          .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
          .policy = flow_policy,
-         .doit = ovs_flow_cmd_new_or_set
+         .doit = ovs_flow_cmd_new
        },
        { .cmd = OVS_FLOW_CMD_DEL,
          .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
@@ -1057,28 +1210,22 @@ static const struct genl_ops dp_flow_genl_ops[] = {
        { .cmd = OVS_FLOW_CMD_SET,
          .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
          .policy = flow_policy,
-         .doit = ovs_flow_cmd_new_or_set,
+         .doit = ovs_flow_cmd_set,
        },
 };
 
-static const struct nla_policy datapath_policy[OVS_DP_ATTR_MAX + 1] = {
-       [OVS_DP_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
-       [OVS_DP_ATTR_UPCALL_PID] = { .type = NLA_U32 },
-       [OVS_DP_ATTR_USER_FEATURES] = { .type = NLA_U32 },
-};
-
-static struct genl_family dp_datapath_genl_family = {
+static struct genl_family dp_flow_genl_family = {
        .id = GENL_ID_GENERATE,
        .hdrsize = sizeof(struct ovs_header),
-       .name = OVS_DATAPATH_FAMILY,
-       .version = OVS_DATAPATH_VERSION,
-       .maxattr = OVS_DP_ATTR_MAX,
+       .name = OVS_FLOW_FAMILY,
+       .version = OVS_FLOW_VERSION,
+       .maxattr = OVS_FLOW_ATTR_MAX,
        .netnsok = true,
        .parallel_ops = true,
-};
-
-static struct genl_multicast_group ovs_dp_datapath_multicast_group = {
-       .name = OVS_DATAPATH_MCGROUP
+       .ops = dp_flow_genl_ops,
+       .n_ops = ARRAY_SIZE(dp_flow_genl_ops),
+       .mcgrps = &ovs_dp_flow_multicast_group,
+       .n_mcgrps = 1,
 };
 
 static size_t ovs_dp_cmd_msg_size(void)
@@ -1093,6 +1240,7 @@ static size_t ovs_dp_cmd_msg_size(void)
        return msgsize;
 }
 
+/* Called with ovs_mutex or RCU read lock. */
 static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb,
                                u32 portid, u32 seq, u32 flags, u8 cmd)
 {
@@ -1108,9 +1256,7 @@ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb,
 
        ovs_header->dp_ifindex = get_dpifindex(dp);
 
-       rcu_read_lock();
        err = nla_put_string(skb, OVS_DP_ATTR_NAME, ovs_dp_name(dp));
-       rcu_read_unlock();
        if (err)
                goto nla_put_failure;
 
@@ -1135,25 +1281,12 @@ error:
        return -EMSGSIZE;
 }
 
-static struct sk_buff *ovs_dp_cmd_build_info(struct datapath *dp,
-                                            struct genl_info *info, u8 cmd)
+static struct sk_buff *ovs_dp_cmd_alloc_info(struct genl_info *info)
 {
-       struct sk_buff *skb;
-       int retval;
-
-       skb = genlmsg_new_unicast(ovs_dp_cmd_msg_size(), info, GFP_KERNEL);
-       if (!skb)
-               return ERR_PTR(-ENOMEM);
-
-       retval = ovs_dp_cmd_fill_info(dp, skb, info->snd_portid, info->snd_seq, 0, cmd);
-       if (retval < 0) {
-               kfree_skb(skb);
-               return ERR_PTR(retval);
-       }
-       return skb;
+       return genlmsg_new_unicast(ovs_dp_cmd_msg_size(), info, GFP_KERNEL);
 }
 
-/* Called with ovs_mutex. */
+/* Called with rcu_read_lock or ovs_mutex. */
 static struct datapath *lookup_datapath(struct net *net,
                                        struct ovs_header *ovs_header,
                                        struct nlattr *a[OVS_DP_ATTR_MAX + 1])
@@ -1165,10 +1298,8 @@ static struct datapath *lookup_datapath(struct net *net,
        else {
                struct vport *vport;
 
-               rcu_read_lock();
                vport = ovs_vport_locate(net, nla_data(a[OVS_DP_ATTR_NAME]));
                dp = vport && vport->port_no == OVSP_LOCAL ? vport->dp : NULL;
-               rcu_read_unlock();
        }
        return dp ? dp : ERR_PTR(-ENODEV);
 }
@@ -1205,12 +1336,14 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
        if (!a[OVS_DP_ATTR_NAME] || !a[OVS_DP_ATTR_UPCALL_PID])
                goto err;
 
-       ovs_lock();
+       reply = ovs_dp_cmd_alloc_info(info);
+       if (!reply)
+               return -ENOMEM;
 
        err = -ENOMEM;
        dp = kzalloc(sizeof(*dp), GFP_KERNEL);
        if (dp == NULL)
-               goto err_unlock_ovs;
+               goto err_free_reply;
 
        ovs_dp_set_net(dp, hold_net(sock_net(skb->sk)));
 
@@ -1245,6 +1378,9 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
 
        ovs_dp_change(dp, a);
 
+       /* So far only local changes have been made, now need the lock. */
+       ovs_lock();
+
        vport = new_vport(&parms);
        if (IS_ERR(vport)) {
                err = PTR_ERR(vport);
@@ -1263,10 +1399,9 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
                goto err_destroy_ports_array;
        }
 
-       reply = ovs_dp_cmd_build_info(dp, info, OVS_DP_CMD_NEW);
-       err = PTR_ERR(reply);
-       if (IS_ERR(reply))
-               goto err_destroy_local_port;
+       err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid,
+                                  info->snd_seq, 0, OVS_DP_CMD_NEW);
+       BUG_ON(err < 0);
 
        ovs_net = net_generic(ovs_dp_get_net(dp), ovs_net_id);
        list_add_tail_rcu(&dp->list_node, &ovs_net->dps);
@@ -1276,9 +1411,8 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
        ovs_notify(&dp_datapath_genl_family, reply, info);
        return 0;
 
-err_destroy_local_port:
-       ovs_dp_detach_port(ovs_vport_ovsl(dp, OVSP_LOCAL));
 err_destroy_ports_array:
+       ovs_unlock();
        kfree(dp->ports);
 err_destroy_percpu:
        free_percpu(dp->stats_percpu);
@@ -1287,8 +1421,8 @@ err_destroy_table:
 err_free_dp:
        release_net(ovs_dp_get_net(dp));
        kfree(dp);
-err_unlock_ovs:
-       ovs_unlock();
+err_free_reply:
+       kfree_skb(reply);
 err:
        return err;
 }
@@ -1326,16 +1460,19 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info)
        struct datapath *dp;
        int err;
 
+       reply = ovs_dp_cmd_alloc_info(info);
+       if (!reply)
+               return -ENOMEM;
+
        ovs_lock();
        dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
        err = PTR_ERR(dp);
        if (IS_ERR(dp))
-               goto unlock;
+               goto err_unlock_free;
 
-       reply = ovs_dp_cmd_build_info(dp, info, OVS_DP_CMD_DEL);
-       err = PTR_ERR(reply);
-       if (IS_ERR(reply))
-               goto unlock;
+       err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid,
+                                  info->snd_seq, 0, OVS_DP_CMD_DEL);
+       BUG_ON(err < 0);
 
        __dp_destroy(dp);
        ovs_unlock();
@@ -1343,8 +1480,10 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info)
        ovs_notify(&dp_datapath_genl_family, reply, info);
 
        return 0;
-unlock:
+
+err_unlock_free:
        ovs_unlock();
+       kfree_skb(reply);
        return err;
 }
 
@@ -1354,29 +1493,30 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
        struct datapath *dp;
        int err;
 
+       reply = ovs_dp_cmd_alloc_info(info);
+       if (!reply)
+               return -ENOMEM;
+
        ovs_lock();
        dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
        err = PTR_ERR(dp);
        if (IS_ERR(dp))
-               goto unlock;
+               goto err_unlock_free;
 
        ovs_dp_change(dp, info->attrs);
 
-       reply = ovs_dp_cmd_build_info(dp, info, OVS_DP_CMD_NEW);
-       if (IS_ERR(reply)) {
-               err = PTR_ERR(reply);
-               genl_set_err(&dp_datapath_genl_family, sock_net(skb->sk), 0,
-                            0, err);
-               err = 0;
-               goto unlock;
-       }
+       err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid,
+                                  info->snd_seq, 0, OVS_DP_CMD_NEW);
+       BUG_ON(err < 0);
 
        ovs_unlock();
        ovs_notify(&dp_datapath_genl_family, reply, info);
 
        return 0;
-unlock:
+
+err_unlock_free:
        ovs_unlock();
+       kfree_skb(reply);
        return err;
 }
 
@@ -1386,24 +1526,26 @@ static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info)
        struct datapath *dp;
        int err;
 
-       ovs_lock();
+       reply = ovs_dp_cmd_alloc_info(info);
+       if (!reply)
+               return -ENOMEM;
+
+       rcu_read_lock();
        dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
        if (IS_ERR(dp)) {
                err = PTR_ERR(dp);
-               goto unlock;
-       }
-
-       reply = ovs_dp_cmd_build_info(dp, info, OVS_DP_CMD_NEW);
-       if (IS_ERR(reply)) {
-               err = PTR_ERR(reply);
-               goto unlock;
+               goto err_unlock_free;
        }
+       err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid,
+                                  info->snd_seq, 0, OVS_DP_CMD_NEW);
+       BUG_ON(err < 0);
+       rcu_read_unlock();
 
-       ovs_unlock();
        return genlmsg_reply(reply, info);
 
-unlock:
-       ovs_unlock();
+err_unlock_free:
+       rcu_read_unlock();
+       kfree_skb(reply);
        return err;
 }
 
@@ -1430,7 +1572,13 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
        return skb->len;
 }
 
-static const struct genl_ops dp_datapath_genl_ops[] = {
+static const struct nla_policy datapath_policy[OVS_DP_ATTR_MAX + 1] = {
+       [OVS_DP_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
+       [OVS_DP_ATTR_UPCALL_PID] = { .type = NLA_U32 },
+       [OVS_DP_ATTR_USER_FEATURES] = { .type = NLA_U32 },
+};
+
+static struct genl_ops dp_datapath_genl_ops[] = {
        { .cmd = OVS_DP_CMD_NEW,
          .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
          .policy = datapath_policy,
@@ -1454,27 +1602,18 @@ static const struct genl_ops dp_datapath_genl_ops[] = {
        },
 };
 
-static const struct nla_policy vport_policy[OVS_VPORT_ATTR_MAX + 1] = {
-       [OVS_VPORT_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
-       [OVS_VPORT_ATTR_STATS] = { .len = sizeof(struct ovs_vport_stats) },
-       [OVS_VPORT_ATTR_PORT_NO] = { .type = NLA_U32 },
-       [OVS_VPORT_ATTR_TYPE] = { .type = NLA_U32 },
-       [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NLA_U32 },
-       [OVS_VPORT_ATTR_OPTIONS] = { .type = NLA_NESTED },
-};
-
-struct genl_family dp_vport_genl_family = {
+static struct genl_family dp_datapath_genl_family = {
        .id = GENL_ID_GENERATE,
        .hdrsize = sizeof(struct ovs_header),
-       .name = OVS_VPORT_FAMILY,
-       .version = OVS_VPORT_VERSION,
-       .maxattr = OVS_VPORT_ATTR_MAX,
+       .name = OVS_DATAPATH_FAMILY,
+       .version = OVS_DATAPATH_VERSION,
+       .maxattr = OVS_DP_ATTR_MAX,
        .netnsok = true,
        .parallel_ops = true,
-};
-
-static struct genl_multicast_group ovs_dp_vport_multicast_group = {
-       .name = OVS_VPORT_MCGROUP
+       .ops = dp_datapath_genl_ops,
+       .n_ops = ARRAY_SIZE(dp_datapath_genl_ops),
+       .mcgrps = &ovs_dp_datapath_multicast_group,
+       .n_mcgrps = 1,
 };
 
 /* Called with ovs_mutex or RCU read lock. */
@@ -1516,7 +1655,12 @@ error:
        return err;
 }
 
-/* Called with ovs_mutex or RCU read lock. */
+static struct sk_buff *ovs_vport_cmd_alloc_info(void)
+{
+       return nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+}
+
+/* Called with ovs_mutex, only via ovs_dp_notify_wq(). */
 struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 portid,
                                         u32 seq, u8 cmd)
 {
@@ -1578,33 +1722,35 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
        u32 port_no;
        int err;
 
-       err = -EINVAL;
        if (!a[OVS_VPORT_ATTR_NAME] || !a[OVS_VPORT_ATTR_TYPE] ||
            !a[OVS_VPORT_ATTR_UPCALL_PID])
-               goto exit;
+               return -EINVAL;
+
+       port_no = a[OVS_VPORT_ATTR_PORT_NO]
+               ? nla_get_u32(a[OVS_VPORT_ATTR_PORT_NO]) : 0;
+       if (port_no >= DP_MAX_PORTS)
+               return -EFBIG;
+
+       reply = ovs_vport_cmd_alloc_info();
+       if (!reply)
+               return -ENOMEM;
 
        ovs_lock();
        dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
        err = -ENODEV;
        if (!dp)
-               goto exit_unlock;
-
-       if (a[OVS_VPORT_ATTR_PORT_NO]) {
-               port_no = nla_get_u32(a[OVS_VPORT_ATTR_PORT_NO]);
-
-               err = -EFBIG;
-               if (port_no >= DP_MAX_PORTS)
-                       goto exit_unlock;
+               goto exit_unlock_free;
 
+       if (port_no) {
                vport = ovs_vport_ovsl(dp, port_no);
                err = -EBUSY;
                if (vport)
-                       goto exit_unlock;
+                       goto exit_unlock_free;
        } else {
                for (port_no = 1; ; port_no++) {
                        if (port_no >= DP_MAX_PORTS) {
                                err = -EFBIG;
-                               goto exit_unlock;
+                               goto exit_unlock_free;
                        }
                        vport = ovs_vport_ovsl(dp, port_no);
                        if (!vport)
@@ -1622,22 +1768,19 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
        vport = new_vport(&parms);
        err = PTR_ERR(vport);
        if (IS_ERR(vport))
-               goto exit_unlock;
+               goto exit_unlock_free;
 
-       err = 0;
-       reply = ovs_vport_cmd_build_info(vport, info->snd_portid, info->snd_seq,
-                                        OVS_VPORT_CMD_NEW);
-       if (IS_ERR(reply)) {
-               err = PTR_ERR(reply);
-               ovs_dp_detach_port(vport);
-               goto exit_unlock;
-       }
+       err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid,
+                                     info->snd_seq, 0, OVS_VPORT_CMD_NEW);
+       BUG_ON(err < 0);
+       ovs_unlock();
 
        ovs_notify(&dp_vport_genl_family, reply, info);
+       return 0;
 
-exit_unlock:
+exit_unlock_free:
        ovs_unlock();
-exit:
+       kfree_skb(reply);
        return err;
 }
 
@@ -1648,28 +1791,26 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
        struct vport *vport;
        int err;
 
+       reply = ovs_vport_cmd_alloc_info();
+       if (!reply)
+               return -ENOMEM;
+
        ovs_lock();
        vport = lookup_vport(sock_net(skb->sk), info->userhdr, a);
        err = PTR_ERR(vport);
        if (IS_ERR(vport))
-               goto exit_unlock;
+               goto exit_unlock_free;
 
        if (a[OVS_VPORT_ATTR_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) {
-               err = -ENOMEM;
-               goto exit_unlock;
+               goto exit_unlock_free;
        }
 
        if (a[OVS_VPORT_ATTR_OPTIONS]) {
                err = ovs_vport_set_options(vport, a[OVS_VPORT_ATTR_OPTIONS]);
                if (err)
-                       goto exit_free;
+                       goto exit_unlock_free;
        }
 
        if (a[OVS_VPORT_ATTR_UPCALL_PID])
@@ -1683,10 +1824,9 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
        ovs_notify(&dp_vport_genl_family, reply, info);
        return 0;
 
-exit_free:
-       kfree_skb(reply);
-exit_unlock:
+exit_unlock_free:
        ovs_unlock();
+       kfree_skb(reply);
        return err;
 }
 
@@ -1697,30 +1837,33 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
        struct vport *vport;
        int err;
 
+       reply = ovs_vport_cmd_alloc_info();
+       if (!reply)
+               return -ENOMEM;
+
        ovs_lock();
        vport = lookup_vport(sock_net(skb->sk), info->userhdr, a);
        err = PTR_ERR(vport);
        if (IS_ERR(vport))
-               goto exit_unlock;
+               goto exit_unlock_free;
 
        if (vport->port_no == OVSP_LOCAL) {
                err = -EINVAL;
-               goto exit_unlock;
+               goto exit_unlock_free;
        }
 
-       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;
-
-       err = 0;
+       err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid,
+                                     info->snd_seq, 0, OVS_VPORT_CMD_DEL);
+       BUG_ON(err < 0);
        ovs_dp_detach_port(vport);
+       ovs_unlock();
 
        ovs_notify(&dp_vport_genl_family, reply, info);
+       return 0;
 
-exit_unlock:
+exit_unlock_free:
        ovs_unlock();
+       kfree_skb(reply);
        return err;
 }
 
@@ -1732,24 +1875,25 @@ static int ovs_vport_cmd_get(struct sk_buff *skb, struct genl_info *info)
        struct vport *vport;
        int err;
 
+       reply = ovs_vport_cmd_alloc_info();
+       if (!reply)
+               return -ENOMEM;
+
        rcu_read_lock();
        vport = lookup_vport(sock_net(skb->sk), ovs_header, a);
        err = PTR_ERR(vport);
        if (IS_ERR(vport))
-               goto exit_unlock;
-
-       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;
-
+               goto exit_unlock_free;
+       err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid,
+                                     info->snd_seq, 0, OVS_VPORT_CMD_NEW);
+       BUG_ON(err < 0);
        rcu_read_unlock();
 
        return genlmsg_reply(reply, info);
 
-exit_unlock:
+exit_unlock_free:
        rcu_read_unlock();
+       kfree_skb(reply);
        return err;
 }
 
@@ -1792,7 +1936,16 @@ out:
        return skb->len;
 }
 
-static const struct genl_ops dp_vport_genl_ops[] = {
+static const struct nla_policy vport_policy[OVS_VPORT_ATTR_MAX + 1] = {
+       [OVS_VPORT_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
+       [OVS_VPORT_ATTR_STATS] = { .len = sizeof(struct ovs_vport_stats) },
+       [OVS_VPORT_ATTR_PORT_NO] = { .type = NLA_U32 },
+       [OVS_VPORT_ATTR_TYPE] = { .type = NLA_U32 },
+       [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NLA_U32 },
+       [OVS_VPORT_ATTR_OPTIONS] = { .type = NLA_NESTED },
+};
+
+static struct genl_ops dp_vport_genl_ops[] = {
        { .cmd = OVS_VPORT_CMD_NEW,
          .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
          .policy = vport_policy,
@@ -1816,26 +1969,25 @@ static const struct genl_ops dp_vport_genl_ops[] = {
        },
 };
 
-struct genl_family_and_ops {
-       struct genl_family *family;
-       const struct genl_ops *ops;
-       int n_ops;
-       const struct genl_multicast_group *group;
+struct genl_family dp_vport_genl_family = {
+       .id = GENL_ID_GENERATE,
+       .hdrsize = sizeof(struct ovs_header),
+       .name = OVS_VPORT_FAMILY,
+       .version = OVS_VPORT_VERSION,
+       .maxattr = OVS_VPORT_ATTR_MAX,
+       .netnsok = true,
+       .parallel_ops = true,
+       .ops = dp_vport_genl_ops,
+       .n_ops = ARRAY_SIZE(dp_vport_genl_ops),
+       .mcgrps = &ovs_dp_vport_multicast_group,
+       .n_mcgrps = 1,
 };
 
-static const struct genl_family_and_ops dp_genl_families[] = {
-       { &dp_datapath_genl_family,
-         dp_datapath_genl_ops, ARRAY_SIZE(dp_datapath_genl_ops),
-         &ovs_dp_datapath_multicast_group },
-       { &dp_vport_genl_family,
-         dp_vport_genl_ops, ARRAY_SIZE(dp_vport_genl_ops),
-         &ovs_dp_vport_multicast_group },
-       { &dp_flow_genl_family,
-         dp_flow_genl_ops, ARRAY_SIZE(dp_flow_genl_ops),
-         &ovs_dp_flow_multicast_group },
-       { &dp_packet_genl_family,
-         dp_packet_genl_ops, ARRAY_SIZE(dp_packet_genl_ops),
-         NULL },
+static struct genl_family * const dp_genl_families[] = {
+       &dp_datapath_genl_family,
+       &dp_vport_genl_family,
+       &dp_flow_genl_family,
+       &dp_packet_genl_family,
 };
 
 static void dp_unregister_genl(int n_families)
@@ -1843,33 +1995,25 @@ static void dp_unregister_genl(int n_families)
        int i;
 
        for (i = 0; i < n_families; i++)
-               genl_unregister_family(dp_genl_families[i].family);
+               genl_unregister_family(dp_genl_families[i]);
 }
 
 static int dp_register_genl(void)
 {
-       int n_registered;
        int err;
        int i;
 
-       n_registered = 0;
        for (i = 0; i < ARRAY_SIZE(dp_genl_families); i++) {
-               const struct genl_family_and_ops *f = &dp_genl_families[i];
 
-               f->family->ops = f->ops;
-               f->family->n_ops = f->n_ops;
-               f->family->mcgrps = f->group;
-               f->family->n_mcgrps = f->group ? 1 : 0;
-               err = genl_register_family(f->family);
+               err = genl_register_family(dp_genl_families[i]);
                if (err)
                        goto error;
-               n_registered++;
        }
 
        return 0;
 
 error:
-       dp_unregister_genl(n_registered);
+       dp_unregister_genl(i);
        return err;
 }
 
index 05317380fc03a03af708716f162738727cab5fff..7ede507500d7daa1cca3d352f9923122f5e63709 100644 (file)
@@ -194,7 +194,9 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq,
 int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb);
 void ovs_dp_notify_wq(struct work_struct *work);
 
-#define OVS_NLERR(fmt, ...) \
-       pr_info_once("netlink: " fmt, ##__VA_ARGS__)
-
+#define OVS_NLERR(fmt, ...)                                    \
+do {                                                           \
+       if (net_ratelimit())                                    \
+               pr_info("netlink: " fmt, ##__VA_ARGS__);        \
+} while (0)
 #endif /* datapath.h */
index 2998989e76db0a7ccb8e25ef11aa393180956593..334751cb15289c4f0ca00bedec960ee1cfe19ba0 100644 (file)
@@ -64,88 +64,110 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies)
 void ovs_flow_stats_update(struct sw_flow *flow, struct sk_buff *skb)
 {
        struct flow_stats *stats;
-       __be16 tcp_flags = 0;
-
-       if (!flow->stats.is_percpu)
-               stats = flow->stats.stat;
-       else
-               stats = this_cpu_ptr(flow->stats.cpu_stats);
-
-       if ((flow->key.eth.type == htons(ETH_P_IP) ||
-            flow->key.eth.type == htons(ETH_P_IPV6)) &&
-           flow->key.ip.frag != OVS_FRAG_TYPE_LATER &&
-           flow->key.ip.proto == IPPROTO_TCP &&
-           likely(skb->len >= skb_transport_offset(skb) + sizeof(struct tcphdr))) {
-               tcp_flags = TCP_FLAGS_BE16(tcp_hdr(skb));
+       __be16 tcp_flags = flow->key.tp.flags;
+       int node = numa_node_id();
+
+       stats = rcu_dereference(flow->stats[node]);
+
+       /* Check if already have node-specific stats. */
+       if (likely(stats)) {
+               spin_lock(&stats->lock);
+               /* Mark if we write on the pre-allocated stats. */
+               if (node == 0 && unlikely(flow->stats_last_writer != node))
+                       flow->stats_last_writer = node;
+       } else {
+               stats = rcu_dereference(flow->stats[0]); /* Pre-allocated. */
+               spin_lock(&stats->lock);
+
+               /* If the current NUMA-node is the only writer on the
+                * pre-allocated stats keep using them.
+                */
+               if (unlikely(flow->stats_last_writer != node)) {
+                       /* A previous locker may have already allocated the
+                        * stats, so we need to check again.  If node-specific
+                        * stats were already allocated, we update the pre-
+                        * allocated stats as we have already locked them.
+                        */
+                       if (likely(flow->stats_last_writer != NUMA_NO_NODE)
+                           && likely(!rcu_dereference(flow->stats[node]))) {
+                               /* Try to allocate node-specific stats. */
+                               struct flow_stats *new_stats;
+
+                               new_stats =
+                                       kmem_cache_alloc_node(flow_stats_cache,
+                                                             GFP_THISNODE |
+                                                             __GFP_NOMEMALLOC,
+                                                             node);
+                               if (likely(new_stats)) {
+                                       new_stats->used = jiffies;
+                                       new_stats->packet_count = 1;
+                                       new_stats->byte_count = skb->len;
+                                       new_stats->tcp_flags = tcp_flags;
+                                       spin_lock_init(&new_stats->lock);
+
+                                       rcu_assign_pointer(flow->stats[node],
+                                                          new_stats);
+                                       goto unlock;
+                               }
+                       }
+                       flow->stats_last_writer = node;
+               }
        }
 
-       spin_lock(&stats->lock);
        stats->used = jiffies;
        stats->packet_count++;
        stats->byte_count += skb->len;
        stats->tcp_flags |= tcp_flags;
+unlock:
        spin_unlock(&stats->lock);
 }
 
-static void stats_read(struct flow_stats *stats,
-                      struct ovs_flow_stats *ovs_stats,
-                      unsigned long *used, __be16 *tcp_flags)
-{
-       spin_lock(&stats->lock);
-       if (!*used || time_after(stats->used, *used))
-               *used = stats->used;
-       *tcp_flags |= stats->tcp_flags;
-       ovs_stats->n_packets += stats->packet_count;
-       ovs_stats->n_bytes += stats->byte_count;
-       spin_unlock(&stats->lock);
-}
-
-void ovs_flow_stats_get(struct sw_flow *flow, struct ovs_flow_stats *ovs_stats,
+/* Must be called with rcu_read_lock or ovs_mutex. */
+void ovs_flow_stats_get(const struct sw_flow *flow,
+                       struct ovs_flow_stats *ovs_stats,
                        unsigned long *used, __be16 *tcp_flags)
 {
-       int cpu;
+       int node;
 
        *used = 0;
        *tcp_flags = 0;
        memset(ovs_stats, 0, sizeof(*ovs_stats));
 
-       local_bh_disable();
-       if (!flow->stats.is_percpu) {
-               stats_read(flow->stats.stat, ovs_stats, used, tcp_flags);
-       } else {
-               for_each_possible_cpu(cpu) {
-                       struct flow_stats *stats;
+       for_each_node(node) {
+               struct flow_stats *stats = rcu_dereference_ovsl(flow->stats[node]);
 
-                       stats = per_cpu_ptr(flow->stats.cpu_stats, cpu);
-                       stats_read(stats, ovs_stats, used, tcp_flags);
+               if (stats) {
+                       /* Local CPU may write on non-local stats, so we must
+                        * block bottom-halves here.
+                        */
+                       spin_lock_bh(&stats->lock);
+                       if (!*used || time_after(stats->used, *used))
+                               *used = stats->used;
+                       *tcp_flags |= stats->tcp_flags;
+                       ovs_stats->n_packets += stats->packet_count;
+                       ovs_stats->n_bytes += stats->byte_count;
+                       spin_unlock_bh(&stats->lock);
                }
        }
-       local_bh_enable();
-}
-
-static void stats_reset(struct flow_stats *stats)
-{
-       spin_lock(&stats->lock);
-       stats->used = 0;
-       stats->packet_count = 0;
-       stats->byte_count = 0;
-       stats->tcp_flags = 0;
-       spin_unlock(&stats->lock);
 }
 
+/* Called with ovs_mutex. */
 void ovs_flow_stats_clear(struct sw_flow *flow)
 {
-       int cpu;
-
-       local_bh_disable();
-       if (!flow->stats.is_percpu) {
-               stats_reset(flow->stats.stat);
-       } else {
-               for_each_possible_cpu(cpu) {
-                       stats_reset(per_cpu_ptr(flow->stats.cpu_stats, cpu));
+       int node;
+
+       for_each_node(node) {
+               struct flow_stats *stats = ovsl_dereference(flow->stats[node]);
+
+               if (stats) {
+                       spin_lock_bh(&stats->lock);
+                       stats->used = 0;
+                       stats->packet_count = 0;
+                       stats->byte_count = 0;
+                       stats->tcp_flags = 0;
+                       spin_unlock_bh(&stats->lock);
                }
        }
-       local_bh_enable();
 }
 
 static int check_header(struct sk_buff *skb, int len)
@@ -332,8 +354,8 @@ static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key,
        /* The ICMPv6 type and code fields use the 16-bit transport port
         * fields, so we need to store them in 16-bit network byte order.
         */
-       key->ipv6.tp.src = htons(icmp->icmp6_type);
-       key->ipv6.tp.dst = htons(icmp->icmp6_code);
+       key->tp.src = htons(icmp->icmp6_type);
+       key->tp.dst = htons(icmp->icmp6_code);
 
        if (icmp->icmp6_code == 0 &&
            (icmp->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION ||
@@ -372,14 +394,14 @@ static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key,
                            && opt_len == 8) {
                                if (unlikely(!is_zero_ether_addr(key->ipv6.nd.sll)))
                                        goto invalid;
-                               memcpy(key->ipv6.nd.sll,
-                                   &nd->opt[offset+sizeof(*nd_opt)], ETH_ALEN);
+                               ether_addr_copy(key->ipv6.nd.sll,
+                                               &nd->opt[offset+sizeof(*nd_opt)]);
                        } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LL_ADDR
                                   && opt_len == 8) {
                                if (unlikely(!is_zero_ether_addr(key->ipv6.nd.tll)))
                                        goto invalid;
-                               memcpy(key->ipv6.nd.tll,
-                                   &nd->opt[offset+sizeof(*nd_opt)], ETH_ALEN);
+                               ether_addr_copy(key->ipv6.nd.tll,
+                                               &nd->opt[offset+sizeof(*nd_opt)]);
                        }
 
                        icmp_len -= opt_len;
@@ -439,8 +461,8 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
         * header in the linear data area.
         */
        eth = eth_hdr(skb);
-       memcpy(key->eth.src, eth->h_source, ETH_ALEN);
-       memcpy(key->eth.dst, eth->h_dest, ETH_ALEN);
+       ether_addr_copy(key->eth.src, eth->h_source);
+       ether_addr_copy(key->eth.dst, eth->h_dest);
 
        __skb_pull(skb, 2 * ETH_ALEN);
        /* We are going to push all headers that we pull, so no need to
@@ -495,21 +517,21 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
                if (key->ip.proto == IPPROTO_TCP) {
                        if (tcphdr_ok(skb)) {
                                struct tcphdr *tcp = tcp_hdr(skb);
-                               key->ipv4.tp.src = tcp->source;
-                               key->ipv4.tp.dst = tcp->dest;
-                               key->ipv4.tp.flags = TCP_FLAGS_BE16(tcp);
+                               key->tp.src = tcp->source;
+                               key->tp.dst = tcp->dest;
+                               key->tp.flags = TCP_FLAGS_BE16(tcp);
                        }
                } else if (key->ip.proto == IPPROTO_UDP) {
                        if (udphdr_ok(skb)) {
                                struct udphdr *udp = udp_hdr(skb);
-                               key->ipv4.tp.src = udp->source;
-                               key->ipv4.tp.dst = udp->dest;
+                               key->tp.src = udp->source;
+                               key->tp.dst = udp->dest;
                        }
                } else if (key->ip.proto == IPPROTO_SCTP) {
                        if (sctphdr_ok(skb)) {
                                struct sctphdr *sctp = sctp_hdr(skb);
-                               key->ipv4.tp.src = sctp->source;
-                               key->ipv4.tp.dst = sctp->dest;
+                               key->tp.src = sctp->source;
+                               key->tp.dst = sctp->dest;
                        }
                } else if (key->ip.proto == IPPROTO_ICMP) {
                        if (icmphdr_ok(skb)) {
@@ -517,8 +539,8 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
                                /* The ICMP type and code fields use the 16-bit
                                 * transport port fields, so we need to store
                                 * them in 16-bit network byte order. */
-                               key->ipv4.tp.src = htons(icmp->type);
-                               key->ipv4.tp.dst = htons(icmp->code);
+                               key->tp.src = htons(icmp->type);
+                               key->tp.dst = htons(icmp->code);
                        }
                }
 
@@ -538,8 +560,8 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
                                key->ip.proto = ntohs(arp->ar_op);
                        memcpy(&key->ipv4.addr.src, arp->ar_sip, sizeof(key->ipv4.addr.src));
                        memcpy(&key->ipv4.addr.dst, arp->ar_tip, sizeof(key->ipv4.addr.dst));
-                       memcpy(key->ipv4.arp.sha, arp->ar_sha, ETH_ALEN);
-                       memcpy(key->ipv4.arp.tha, arp->ar_tha, ETH_ALEN);
+                       ether_addr_copy(key->ipv4.arp.sha, arp->ar_sha);
+                       ether_addr_copy(key->ipv4.arp.tha, arp->ar_tha);
                }
        } else if (key->eth.type == htons(ETH_P_IPV6)) {
                int nh_len;             /* IPv6 Header + Extensions */
@@ -564,21 +586,21 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
                if (key->ip.proto == NEXTHDR_TCP) {
                        if (tcphdr_ok(skb)) {
                                struct tcphdr *tcp = tcp_hdr(skb);
-                               key->ipv6.tp.src = tcp->source;
-                               key->ipv6.tp.dst = tcp->dest;
-                               key->ipv6.tp.flags = TCP_FLAGS_BE16(tcp);
+                               key->tp.src = tcp->source;
+                               key->tp.dst = tcp->dest;
+                               key->tp.flags = TCP_FLAGS_BE16(tcp);
                        }
                } else if (key->ip.proto == NEXTHDR_UDP) {
                        if (udphdr_ok(skb)) {
                                struct udphdr *udp = udp_hdr(skb);
-                               key->ipv6.tp.src = udp->source;
-                               key->ipv6.tp.dst = udp->dest;
+                               key->tp.src = udp->source;
+                               key->tp.dst = udp->dest;
                        }
                } else if (key->ip.proto == NEXTHDR_SCTP) {
                        if (sctphdr_ok(skb)) {
                                struct sctphdr *sctp = sctp_hdr(skb);
-                               key->ipv6.tp.src = sctp->source;
-                               key->ipv6.tp.dst = sctp->dest;
+                               key->tp.src = sctp->source;
+                               key->tp.dst = sctp->dest;
                        }
                } else if (key->ip.proto == NEXTHDR_ICMP) {
                        if (icmp6hdr_ok(skb)) {
index 2d770e28a3a396f7da0a4c8e7baf4b29709ceb0c..ac395d2cd821631898116b89438af7eba5d40b2f 100644 (file)
@@ -47,7 +47,7 @@ struct ovs_key_ipv4_tunnel {
        __be16 tun_flags;
        u8   ipv4_tos;
        u8   ipv4_ttl;
-};
+} __packed __aligned(4); /* Minimize padding. */
 
 static inline void ovs_flow_tun_key_init(struct ovs_key_ipv4_tunnel *tun_key,
                                         const struct iphdr *iph, __be64 tun_id,
@@ -71,7 +71,7 @@ struct sw_flow_key {
                u32     priority;       /* Packet QoS priority. */
                u32     skb_mark;       /* SKB mark. */
                u16     in_port;        /* Input switch port (or DP_MAX_PORTS). */
-       } phy;
+       } __packed phy; /* Safe when right after 'tun_key'. */
        struct {
                u8     src[ETH_ALEN];   /* Ethernet source address. */
                u8     dst[ETH_ALEN];   /* Ethernet destination address. */
@@ -84,23 +84,21 @@ struct sw_flow_key {
                u8     ttl;             /* IP TTL/hop limit. */
                u8     frag;            /* One of OVS_FRAG_TYPE_*. */
        } ip;
+       struct {
+               __be16 src;             /* TCP/UDP/SCTP source port. */
+               __be16 dst;             /* TCP/UDP/SCTP destination port. */
+               __be16 flags;           /* TCP flags. */
+       } tp;
        union {
                struct {
                        struct {
                                __be32 src;     /* IP source address. */
                                __be32 dst;     /* IP destination address. */
                        } addr;
-                       union {
-                               struct {
-                                       __be16 src;             /* TCP/UDP/SCTP source port. */
-                                       __be16 dst;             /* TCP/UDP/SCTP destination port. */
-                                       __be16 flags;           /* TCP flags. */
-                               } tp;
-                               struct {
-                                       u8 sha[ETH_ALEN];       /* ARP source hardware address. */
-                                       u8 tha[ETH_ALEN];       /* ARP target hardware address. */
-                               } arp;
-                       };
+                       struct {
+                               u8 sha[ETH_ALEN];       /* ARP source hardware address. */
+                               u8 tha[ETH_ALEN];       /* ARP target hardware address. */
+                       } arp;
                } ipv4;
                struct {
                        struct {
@@ -108,11 +106,6 @@ struct sw_flow_key {
                                struct in6_addr dst;    /* IPv6 destination address. */
                        } addr;
                        __be32 label;                   /* IPv6 flow label. */
-                       struct {
-                               __be16 src;             /* TCP/UDP/SCTP source port. */
-                               __be16 dst;             /* TCP/UDP/SCTP destination port. */
-                               __be16 flags;           /* TCP flags. */
-                       } tp;
                        struct {
                                struct in6_addr target; /* ND target address. */
                                u8 sll[ETH_ALEN];       /* ND source link layer address. */
@@ -155,24 +148,22 @@ struct flow_stats {
        __be16 tcp_flags;               /* Union of seen TCP flags. */
 };
 
-struct sw_flow_stats {
-       bool is_percpu;
-       union {
-               struct flow_stats *stat;
-               struct flow_stats __percpu *cpu_stats;
-       };
-};
-
 struct sw_flow {
        struct rcu_head rcu;
        struct hlist_node hash_node[2];
        u32 hash;
-
+       int stats_last_writer;          /* NUMA-node id of the last writer on
+                                        * 'stats[0]'.
+                                        */
        struct sw_flow_key key;
        struct sw_flow_key unmasked_key;
        struct sw_flow_mask *mask;
        struct sw_flow_actions __rcu *sf_acts;
-       struct sw_flow_stats stats;
+       struct flow_stats __rcu *stats[]; /* One for each NUMA node.  First one
+                                          * is allocated at flow creation time,
+                                          * the rest are allocated on demand
+                                          * while holding the 'stats[0].lock'.
+                                          */
 };
 
 struct arp_eth_header {
@@ -189,10 +180,10 @@ struct arp_eth_header {
        unsigned char       ar_tip[4];          /* target IP address        */
 } __packed;
 
-void ovs_flow_stats_update(struct sw_flow *flow, struct sk_buff *skb);
-void ovs_flow_stats_get(struct sw_flow *flow, struct ovs_flow_stats *stats,
+void ovs_flow_stats_update(struct sw_flow *, struct sk_buff *);
+void ovs_flow_stats_get(const struct sw_flow *, struct ovs_flow_stats *,
                        unsigned long *used, __be16 *tcp_flags);
-void ovs_flow_stats_clear(struct sw_flow *flow);
+void ovs_flow_stats_clear(struct sw_flow *);
 u64 ovs_flow_used_time(unsigned long flow_jiffies);
 
 int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *);
index 4d000acaed0db5cc2052ae55f5b0cb7be6472b79..d757848da89ca734ac95cef806c2dd314f881bf6 100644 (file)
@@ -16,6 +16,8 @@
  * 02110-1301, USA
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include "flow.h"
 #include "datapath.h"
 #include <linux/uaccess.h>
@@ -202,11 +204,11 @@ static bool match_validate(const struct sw_flow_match *match,
                                if (match->mask && (match->mask->key.ip.proto == 0xff))
                                        mask_allowed |= 1 << OVS_KEY_ATTR_ICMPV6;
 
-                               if (match->key->ipv6.tp.src ==
+                               if (match->key->tp.src ==
                                                htons(NDISC_NEIGHBOUR_SOLICITATION) ||
-                                   match->key->ipv6.tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) {
+                                   match->key->tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) {
                                        key_expected |= 1 << OVS_KEY_ATTR_ND;
-                                       if (match->mask && (match->mask->key.ipv6.tp.src == htons(0xffff)))
+                                       if (match->mask && (match->mask->key.tp.src == htons(0xffff)))
                                                mask_allowed |= 1 << OVS_KEY_ATTR_ND;
                                }
                        }
@@ -216,14 +218,14 @@ static bool match_validate(const struct sw_flow_match *match,
        if ((key_attrs & key_expected) != key_expected) {
                /* Key attributes check failed. */
                OVS_NLERR("Missing expected key attributes (key_attrs=%llx, expected=%llx).\n",
-                               key_attrs, key_expected);
+                               (unsigned long long)key_attrs, (unsigned long long)key_expected);
                return false;
        }
 
        if ((mask_attrs & mask_allowed) != mask_attrs) {
                /* Mask attributes check failed. */
                OVS_NLERR("Contain more than allowed mask fields (mask_attrs=%llx, mask_allowed=%llx).\n",
-                               mask_attrs, mask_allowed);
+                               (unsigned long long)mask_attrs, (unsigned long long)mask_allowed);
                return false;
        }
 
@@ -266,20 +268,6 @@ static bool is_all_zero(const u8 *fp, size_t size)
        return true;
 }
 
-static bool is_all_set(const u8 *fp, size_t size)
-{
-       int i;
-
-       if (!fp)
-               return false;
-
-       for (i = 0; i < size; i++)
-               if (fp[i] != 0xff)
-                       return false;
-
-       return true;
-}
-
 static int __parse_flow_nlattrs(const struct nlattr *attr,
                                const struct nlattr *a[],
                                u64 *attrsp, bool nz)
@@ -501,9 +489,8 @@ static int metadata_from_nlattrs(struct sw_flow_match *match,  u64 *attrs,
        return 0;
 }
 
-static int ovs_key_from_nlattrs(struct sw_flow_match *match,  bool *exact_5tuple,
-                               u64 attrs, const struct nlattr **a,
-                               bool is_mask)
+static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
+                               const struct nlattr **a, bool is_mask)
 {
        int err;
        u64 orig_attrs = attrs;
@@ -560,11 +547,6 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match,  bool *exact_5tuple
                SW_FLOW_KEY_PUT(match, eth.type, htons(ETH_P_802_2), is_mask);
        }
 
-       if (is_mask && exact_5tuple) {
-               if (match->mask->key.eth.type != htons(0xffff))
-                       *exact_5tuple = false;
-       }
-
        if (attrs & (1 << OVS_KEY_ATTR_IPV4)) {
                const struct ovs_key_ipv4 *ipv4_key;
 
@@ -587,13 +569,6 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match,  bool *exact_5tuple
                SW_FLOW_KEY_PUT(match, ipv4.addr.dst,
                                ipv4_key->ipv4_dst, is_mask);
                attrs &= ~(1 << OVS_KEY_ATTR_IPV4);
-
-               if (is_mask && exact_5tuple && *exact_5tuple) {
-                       if (ipv4_key->ipv4_proto != 0xff ||
-                           ipv4_key->ipv4_src != htonl(0xffffffff) ||
-                           ipv4_key->ipv4_dst != htonl(0xffffffff))
-                               *exact_5tuple = false;
-               }
        }
 
        if (attrs & (1 << OVS_KEY_ATTR_IPV6)) {
@@ -625,13 +600,6 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match,  bool *exact_5tuple
                                is_mask);
 
                attrs &= ~(1 << OVS_KEY_ATTR_IPV6);
-
-               if (is_mask && exact_5tuple && *exact_5tuple) {
-                       if (ipv6_key->ipv6_proto != 0xff ||
-                           !is_all_set((u8 *)ipv6_key->ipv6_src, sizeof(match->key->ipv6.addr.src)) ||
-                           !is_all_set((u8 *)ipv6_key->ipv6_dst, sizeof(match->key->ipv6.addr.dst)))
-                               *exact_5tuple = false;
-               }
        }
 
        if (attrs & (1 << OVS_KEY_ATTR_ARP)) {
@@ -662,32 +630,18 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match,  bool *exact_5tuple
                const struct ovs_key_tcp *tcp_key;
 
                tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]);
-               if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) {
-                       SW_FLOW_KEY_PUT(match, ipv4.tp.src,
-                                       tcp_key->tcp_src, is_mask);
-                       SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
-                                       tcp_key->tcp_dst, is_mask);
-               } else {
-                       SW_FLOW_KEY_PUT(match, ipv6.tp.src,
-                                       tcp_key->tcp_src, is_mask);
-                       SW_FLOW_KEY_PUT(match, ipv6.tp.dst,
-                                       tcp_key->tcp_dst, is_mask);
-               }
+               SW_FLOW_KEY_PUT(match, tp.src, tcp_key->tcp_src, is_mask);
+               SW_FLOW_KEY_PUT(match, tp.dst, tcp_key->tcp_dst, is_mask);
                attrs &= ~(1 << OVS_KEY_ATTR_TCP);
-
-               if (is_mask && exact_5tuple && *exact_5tuple &&
-                   (tcp_key->tcp_src != htons(0xffff) ||
-                    tcp_key->tcp_dst != htons(0xffff)))
-                       *exact_5tuple = false;
        }
 
        if (attrs & (1 << OVS_KEY_ATTR_TCP_FLAGS)) {
                if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) {
-                       SW_FLOW_KEY_PUT(match, ipv4.tp.flags,
+                       SW_FLOW_KEY_PUT(match, tp.flags,
                                        nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]),
                                        is_mask);
                } else {
-                       SW_FLOW_KEY_PUT(match, ipv6.tp.flags,
+                       SW_FLOW_KEY_PUT(match, tp.flags,
                                        nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]),
                                        is_mask);
                }
@@ -698,40 +652,17 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match,  bool *exact_5tuple
                const struct ovs_key_udp *udp_key;
 
                udp_key = nla_data(a[OVS_KEY_ATTR_UDP]);
-               if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) {
-                       SW_FLOW_KEY_PUT(match, ipv4.tp.src,
-                                       udp_key->udp_src, is_mask);
-                       SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
-                                       udp_key->udp_dst, is_mask);
-               } else {
-                       SW_FLOW_KEY_PUT(match, ipv6.tp.src,
-                                       udp_key->udp_src, is_mask);
-                       SW_FLOW_KEY_PUT(match, ipv6.tp.dst,
-                                       udp_key->udp_dst, is_mask);
-               }
+               SW_FLOW_KEY_PUT(match, tp.src, udp_key->udp_src, is_mask);
+               SW_FLOW_KEY_PUT(match, tp.dst, udp_key->udp_dst, is_mask);
                attrs &= ~(1 << OVS_KEY_ATTR_UDP);
-
-               if (is_mask && exact_5tuple && *exact_5tuple &&
-                   (udp_key->udp_src != htons(0xffff) ||
-                    udp_key->udp_dst != htons(0xffff)))
-                       *exact_5tuple = false;
        }
 
        if (attrs & (1 << OVS_KEY_ATTR_SCTP)) {
                const struct ovs_key_sctp *sctp_key;
 
                sctp_key = nla_data(a[OVS_KEY_ATTR_SCTP]);
-               if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) {
-                       SW_FLOW_KEY_PUT(match, ipv4.tp.src,
-                                       sctp_key->sctp_src, is_mask);
-                       SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
-                                       sctp_key->sctp_dst, is_mask);
-               } else {
-                       SW_FLOW_KEY_PUT(match, ipv6.tp.src,
-                                       sctp_key->sctp_src, is_mask);
-                       SW_FLOW_KEY_PUT(match, ipv6.tp.dst,
-                                       sctp_key->sctp_dst, is_mask);
-               }
+               SW_FLOW_KEY_PUT(match, tp.src, sctp_key->sctp_src, is_mask);
+               SW_FLOW_KEY_PUT(match, tp.dst, sctp_key->sctp_dst, is_mask);
                attrs &= ~(1 << OVS_KEY_ATTR_SCTP);
        }
 
@@ -739,9 +670,9 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match,  bool *exact_5tuple
                const struct ovs_key_icmp *icmp_key;
 
                icmp_key = nla_data(a[OVS_KEY_ATTR_ICMP]);
-               SW_FLOW_KEY_PUT(match, ipv4.tp.src,
+               SW_FLOW_KEY_PUT(match, tp.src,
                                htons(icmp_key->icmp_type), is_mask);
-               SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
+               SW_FLOW_KEY_PUT(match, tp.dst,
                                htons(icmp_key->icmp_code), is_mask);
                attrs &= ~(1 << OVS_KEY_ATTR_ICMP);
        }
@@ -750,9 +681,9 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match,  bool *exact_5tuple
                const struct ovs_key_icmpv6 *icmpv6_key;
 
                icmpv6_key = nla_data(a[OVS_KEY_ATTR_ICMPV6]);
-               SW_FLOW_KEY_PUT(match, ipv6.tp.src,
+               SW_FLOW_KEY_PUT(match, tp.src,
                                htons(icmpv6_key->icmpv6_type), is_mask);
-               SW_FLOW_KEY_PUT(match, ipv6.tp.dst,
+               SW_FLOW_KEY_PUT(match, tp.dst,
                                htons(icmpv6_key->icmpv6_code), is_mask);
                attrs &= ~(1 << OVS_KEY_ATTR_ICMPV6);
        }
@@ -800,7 +731,6 @@ static void sw_flow_mask_set(struct sw_flow_mask *mask,
  * attribute specifies the mask field of the wildcarded flow.
  */
 int ovs_nla_get_match(struct sw_flow_match *match,
-                     bool *exact_5tuple,
                      const struct nlattr *key,
                      const struct nlattr *mask)
 {
@@ -848,13 +778,10 @@ int ovs_nla_get_match(struct sw_flow_match *match,
                }
        }
 
-       err = ovs_key_from_nlattrs(match, NULL, key_attrs, a, false);
+       err = ovs_key_from_nlattrs(match, key_attrs, a, false);
        if (err)
                return err;
 
-       if (exact_5tuple)
-               *exact_5tuple = true;
-
        if (mask) {
                err = parse_flow_mask_nlattrs(mask, a, &mask_attrs);
                if (err)
@@ -892,7 +819,7 @@ int ovs_nla_get_match(struct sw_flow_match *match,
                        }
                }
 
-               err = ovs_key_from_nlattrs(match, exact_5tuple, mask_attrs, a, true);
+               err = ovs_key_from_nlattrs(match, mask_attrs, a, true);
                if (err)
                        return err;
        } else {
@@ -982,8 +909,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
                goto nla_put_failure;
 
        eth_key = nla_data(nla);
-       memcpy(eth_key->eth_src, output->eth.src, ETH_ALEN);
-       memcpy(eth_key->eth_dst, output->eth.dst, ETH_ALEN);
+       ether_addr_copy(eth_key->eth_src, output->eth.src);
+       ether_addr_copy(eth_key->eth_dst, output->eth.dst);
 
        if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) {
                __be16 eth_type;
@@ -1055,8 +982,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
                arp_key->arp_sip = output->ipv4.addr.src;
                arp_key->arp_tip = output->ipv4.addr.dst;
                arp_key->arp_op = htons(output->ip.proto);
-               memcpy(arp_key->arp_sha, output->ipv4.arp.sha, ETH_ALEN);
-               memcpy(arp_key->arp_tha, output->ipv4.arp.tha, ETH_ALEN);
+               ether_addr_copy(arp_key->arp_sha, output->ipv4.arp.sha);
+               ether_addr_copy(arp_key->arp_tha, output->ipv4.arp.tha);
        }
 
        if ((swkey->eth.type == htons(ETH_P_IP) ||
@@ -1070,19 +997,11 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
                        if (!nla)
                                goto nla_put_failure;
                        tcp_key = nla_data(nla);
-                       if (swkey->eth.type == htons(ETH_P_IP)) {
-                               tcp_key->tcp_src = output->ipv4.tp.src;
-                               tcp_key->tcp_dst = output->ipv4.tp.dst;
-                               if (nla_put_be16(skb, OVS_KEY_ATTR_TCP_FLAGS,
-                                                output->ipv4.tp.flags))
-                                       goto nla_put_failure;
-                       } else if (swkey->eth.type == htons(ETH_P_IPV6)) {
-                               tcp_key->tcp_src = output->ipv6.tp.src;
-                               tcp_key->tcp_dst = output->ipv6.tp.dst;
-                               if (nla_put_be16(skb, OVS_KEY_ATTR_TCP_FLAGS,
-                                                output->ipv6.tp.flags))
-                                       goto nla_put_failure;
-                       }
+                       tcp_key->tcp_src = output->tp.src;
+                       tcp_key->tcp_dst = output->tp.dst;
+                       if (nla_put_be16(skb, OVS_KEY_ATTR_TCP_FLAGS,
+                                        output->tp.flags))
+                               goto nla_put_failure;
                } else if (swkey->ip.proto == IPPROTO_UDP) {
                        struct ovs_key_udp *udp_key;
 
@@ -1090,13 +1009,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
                        if (!nla)
                                goto nla_put_failure;
                        udp_key = nla_data(nla);
-                       if (swkey->eth.type == htons(ETH_P_IP)) {
-                               udp_key->udp_src = output->ipv4.tp.src;
-                               udp_key->udp_dst = output->ipv4.tp.dst;
-                       } else if (swkey->eth.type == htons(ETH_P_IPV6)) {
-                               udp_key->udp_src = output->ipv6.tp.src;
-                               udp_key->udp_dst = output->ipv6.tp.dst;
-                       }
+                       udp_key->udp_src = output->tp.src;
+                       udp_key->udp_dst = output->tp.dst;
                } else if (swkey->ip.proto == IPPROTO_SCTP) {
                        struct ovs_key_sctp *sctp_key;
 
@@ -1104,13 +1018,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
                        if (!nla)
                                goto nla_put_failure;
                        sctp_key = nla_data(nla);
-                       if (swkey->eth.type == htons(ETH_P_IP)) {
-                               sctp_key->sctp_src = swkey->ipv4.tp.src;
-                               sctp_key->sctp_dst = swkey->ipv4.tp.dst;
-                       } else if (swkey->eth.type == htons(ETH_P_IPV6)) {
-                               sctp_key->sctp_src = swkey->ipv6.tp.src;
-                               sctp_key->sctp_dst = swkey->ipv6.tp.dst;
-                       }
+                       sctp_key->sctp_src = output->tp.src;
+                       sctp_key->sctp_dst = output->tp.dst;
                } else if (swkey->eth.type == htons(ETH_P_IP) &&
                           swkey->ip.proto == IPPROTO_ICMP) {
                        struct ovs_key_icmp *icmp_key;
@@ -1119,8 +1028,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
                        if (!nla)
                                goto nla_put_failure;
                        icmp_key = nla_data(nla);
-                       icmp_key->icmp_type = ntohs(output->ipv4.tp.src);
-                       icmp_key->icmp_code = ntohs(output->ipv4.tp.dst);
+                       icmp_key->icmp_type = ntohs(output->tp.src);
+                       icmp_key->icmp_code = ntohs(output->tp.dst);
                } else if (swkey->eth.type == htons(ETH_P_IPV6) &&
                           swkey->ip.proto == IPPROTO_ICMPV6) {
                        struct ovs_key_icmpv6 *icmpv6_key;
@@ -1130,8 +1039,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
                        if (!nla)
                                goto nla_put_failure;
                        icmpv6_key = nla_data(nla);
-                       icmpv6_key->icmpv6_type = ntohs(output->ipv6.tp.src);
-                       icmpv6_key->icmpv6_code = ntohs(output->ipv6.tp.dst);
+                       icmpv6_key->icmpv6_type = ntohs(output->tp.src);
+                       icmpv6_key->icmpv6_code = ntohs(output->tp.dst);
 
                        if (icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_SOLICITATION ||
                            icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_ADVERTISEMENT) {
@@ -1143,8 +1052,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
                                nd_key = nla_data(nla);
                                memcpy(nd_key->nd_target, &output->ipv6.nd.target,
                                                        sizeof(nd_key->nd_target));
-                               memcpy(nd_key->nd_sll, output->ipv6.nd.sll, ETH_ALEN);
-                               memcpy(nd_key->nd_tll, output->ipv6.nd.tll, ETH_ALEN);
+                               ether_addr_copy(nd_key->nd_sll, output->ipv6.nd.sll);
+                               ether_addr_copy(nd_key->nd_tll, output->ipv6.nd.tll);
                        }
                }
        }
@@ -1309,13 +1218,10 @@ static int validate_and_copy_sample(const struct nlattr *attr,
 
 static int validate_tp_port(const struct sw_flow_key *flow_key)
 {
-       if (flow_key->eth.type == htons(ETH_P_IP)) {
-               if (flow_key->ipv4.tp.src || flow_key->ipv4.tp.dst)
-                       return 0;
-       } else if (flow_key->eth.type == htons(ETH_P_IPV6)) {
-               if (flow_key->ipv6.tp.src || flow_key->ipv6.tp.dst)
-                       return 0;
-       }
+       if ((flow_key->eth.type == htons(ETH_P_IP) ||
+            flow_key->eth.type == htons(ETH_P_IPV6)) &&
+           (flow_key->tp.src || flow_key->tp.dst))
+               return 0;
 
        return -EINVAL;
 }
index b31fbe28bc7a81a0640546b6e55be8841c32f8a5..440151045d3946329bf01e4fd5a1c81f0fd4e906 100644 (file)
@@ -45,7 +45,6 @@ int ovs_nla_put_flow(const struct sw_flow_key *,
 int ovs_nla_get_flow_metadata(struct sw_flow *flow,
                              const struct nlattr *attr);
 int ovs_nla_get_match(struct sw_flow_match *match,
-                     bool *exact_5tuple,
                      const struct nlattr *,
                      const struct nlattr *);
 
index 3c268b3d71c34baa0a8c70888823b37da454fffd..574c3abc9b307ef6609f8f8dc09ade4b3253b814 100644 (file)
@@ -48,6 +48,7 @@
 #define REHASH_INTERVAL                (10 * 60 * HZ)
 
 static struct kmem_cache *flow_cache;
+struct kmem_cache *flow_stats_cache __read_mostly;
 
 static u16 range_n_bytes(const struct sw_flow_key_range *range)
 {
@@ -57,8 +58,10 @@ static u16 range_n_bytes(const struct sw_flow_key_range *range)
 void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src,
                       const struct sw_flow_mask *mask)
 {
-       const long *m = (long *)((u8 *)&mask->key + mask->range.start);
-       const long *s = (long *)((u8 *)src + mask->range.start);
+       const long *m = (const long *)((const u8 *)&mask->key +
+                               mask->range.start);
+       const long *s = (const long *)((const u8 *)src +
+                               mask->range.start);
        long *d = (long *)((u8 *)dst + mask->range.start);
        int i;
 
@@ -70,10 +73,11 @@ void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src,
                *d++ = *s++ & *m++;
 }
 
-struct sw_flow *ovs_flow_alloc(bool percpu_stats)
+struct sw_flow *ovs_flow_alloc(void)
 {
        struct sw_flow *flow;
-       int cpu;
+       struct flow_stats *stats;
+       int node;
 
        flow = kmem_cache_alloc(flow_cache, GFP_KERNEL);
        if (!flow)
@@ -81,27 +85,22 @@ struct sw_flow *ovs_flow_alloc(bool percpu_stats)
 
        flow->sf_acts = NULL;
        flow->mask = NULL;
+       flow->stats_last_writer = NUMA_NO_NODE;
 
-       flow->stats.is_percpu = percpu_stats;
+       /* Initialize the default stat node. */
+       stats = kmem_cache_alloc_node(flow_stats_cache,
+                                     GFP_KERNEL | __GFP_ZERO, 0);
+       if (!stats)
+               goto err;
 
-       if (!percpu_stats) {
-               flow->stats.stat = kzalloc(sizeof(*flow->stats.stat), GFP_KERNEL);
-               if (!flow->stats.stat)
-                       goto err;
+       spin_lock_init(&stats->lock);
 
-               spin_lock_init(&flow->stats.stat->lock);
-       } else {
-               flow->stats.cpu_stats = alloc_percpu(struct flow_stats);
-               if (!flow->stats.cpu_stats)
-                       goto err;
+       RCU_INIT_POINTER(flow->stats[0], stats);
 
-               for_each_possible_cpu(cpu) {
-                       struct flow_stats *cpu_stats;
+       for_each_node(node)
+               if (node != 0)
+                       RCU_INIT_POINTER(flow->stats[node], NULL);
 
-                       cpu_stats = per_cpu_ptr(flow->stats.cpu_stats, cpu);
-                       spin_lock_init(&cpu_stats->lock);
-               }
-       }
        return flow;
 err:
        kmem_cache_free(flow_cache, flow);
@@ -138,11 +137,13 @@ static struct flex_array *alloc_buckets(unsigned int n_buckets)
 
 static void flow_free(struct sw_flow *flow)
 {
-       kfree((struct sf_flow_acts __force *)flow->sf_acts);
-       if (flow->stats.is_percpu)
-               free_percpu(flow->stats.cpu_stats);
-       else
-               kfree(flow->stats.stat);
+       int node;
+
+       kfree((struct sw_flow_actions __force *)flow->sf_acts);
+       for_each_node(node)
+               if (flow->stats[node])
+                       kmem_cache_free(flow_stats_cache,
+                                       (struct flow_stats __force *)flow->stats[node]);
        kmem_cache_free(flow_cache, flow);
 }
 
@@ -158,25 +159,6 @@ void ovs_flow_free(struct sw_flow *flow, bool deferred)
        if (!flow)
                return;
 
-       if (flow->mask) {
-               struct sw_flow_mask *mask = flow->mask;
-
-               /* ovs-lock is required to protect mask-refcount and
-                * mask list.
-                */
-               ASSERT_OVSL();
-               BUG_ON(!mask->ref_count);
-               mask->ref_count--;
-
-               if (!mask->ref_count) {
-                       list_del_rcu(&mask->list);
-                       if (deferred)
-                               kfree_rcu(mask, rcu);
-                       else
-                               kfree(mask);
-               }
-       }
-
        if (deferred)
                call_rcu(&flow->rcu, rcu_free_flow_callback);
        else
@@ -375,7 +357,7 @@ int ovs_flow_tbl_flush(struct flow_table *flow_table)
 static u32 flow_hash(const struct sw_flow_key *key, int key_start,
                     int key_end)
 {
-       u32 *hash_key = (u32 *)((u8 *)key + key_start);
+       const u32 *hash_key = (const u32 *)((const u8 *)key + key_start);
        int hash_u32s = (key_end - key_start) >> 2;
 
        /* Make sure number of hash bytes are multiple of u32. */
@@ -397,8 +379,8 @@ static bool cmp_key(const struct sw_flow_key *key1,
                    const struct sw_flow_key *key2,
                    int key_start, int key_end)
 {
-       const long *cp1 = (long *)((u8 *)key1 + key_start);
-       const long *cp2 = (long *)((u8 *)key2 + key_start);
+       const long *cp1 = (const long *)((const u8 *)key1 + key_start);
+       const long *cp2 = (const long *)((const u8 *)key2 + key_start);
        long diffs = 0;
        int i;
 
@@ -490,6 +472,25 @@ static struct table_instance *table_instance_expand(struct table_instance *ti)
        return table_instance_rehash(ti, ti->n_buckets * 2);
 }
 
+/* Remove 'mask' from the mask list, if it is not needed any more. */
+static void flow_mask_remove(struct flow_table *tbl, struct sw_flow_mask *mask)
+{
+       if (mask) {
+               /* ovs-lock is required to protect mask-refcount and
+                * mask list.
+                */
+               ASSERT_OVSL();
+               BUG_ON(!mask->ref_count);
+               mask->ref_count--;
+
+               if (!mask->ref_count) {
+                       list_del_rcu(&mask->list);
+                       kfree_rcu(mask, rcu);
+               }
+       }
+}
+
+/* Must be called with OVS mutex held. */
 void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow)
 {
        struct table_instance *ti = ovsl_dereference(table->ti);
@@ -497,6 +498,11 @@ void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow)
        BUG_ON(table->count == 0);
        hlist_del_rcu(&flow->hash_node[ti->node_ver]);
        table->count--;
+
+       /* RCU delete the mask. 'flow->mask' is not NULLed, as it should be
+        * accessible as long as the RCU read lock is held.
+        */
+       flow_mask_remove(table, flow->mask);
 }
 
 static struct sw_flow_mask *mask_alloc(void)
@@ -513,8 +519,8 @@ static struct sw_flow_mask *mask_alloc(void)
 static bool mask_equal(const struct sw_flow_mask *a,
                       const struct sw_flow_mask *b)
 {
-       u8 *a_ = (u8 *)&a->key + a->range.start;
-       u8 *b_ = (u8 *)&b->key + b->range.start;
+       const u8 *a_ = (const u8 *)&a->key + a->range.start;
+       const u8 *b_ = (const u8 *)&b->key + b->range.start;
 
        return  (a->range.end == b->range.end)
                && (a->range.start == b->range.start)
@@ -559,6 +565,7 @@ static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow,
        return 0;
 }
 
+/* Must be called with OVS mutex held. */
 int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow,
                        struct sw_flow_mask *mask)
 {
@@ -597,16 +604,28 @@ int ovs_flow_init(void)
        BUILD_BUG_ON(__alignof__(struct sw_flow_key) % __alignof__(long));
        BUILD_BUG_ON(sizeof(struct sw_flow_key) % sizeof(long));
 
-       flow_cache = kmem_cache_create("sw_flow", sizeof(struct sw_flow), 0,
-                                       0, NULL);
+       flow_cache = kmem_cache_create("sw_flow", sizeof(struct sw_flow)
+                                      + (num_possible_nodes()
+                                         * sizeof(struct flow_stats *)),
+                                      0, 0, NULL);
        if (flow_cache == NULL)
                return -ENOMEM;
 
+       flow_stats_cache
+               = kmem_cache_create("sw_flow_stats", sizeof(struct flow_stats),
+                                   0, SLAB_HWCACHE_ALIGN, NULL);
+       if (flow_stats_cache == NULL) {
+               kmem_cache_destroy(flow_cache);
+               flow_cache = NULL;
+               return -ENOMEM;
+       }
+
        return 0;
 }
 
 /* Uninitializes the flow module. */
 void ovs_flow_exit(void)
 {
+       kmem_cache_destroy(flow_stats_cache);
        kmem_cache_destroy(flow_cache);
 }
index baaeb101924d81a4beb373be93e07287ed9be020..ca8a5820f6153f67fb9ad987c4c156be882c92ea 100644 (file)
@@ -52,10 +52,12 @@ struct flow_table {
        unsigned int count;
 };
 
+extern struct kmem_cache *flow_stats_cache;
+
 int ovs_flow_init(void);
 void ovs_flow_exit(void);
 
-struct sw_flow *ovs_flow_alloc(bool percpu_stats);
+struct sw_flow *ovs_flow_alloc(void);
 void ovs_flow_free(struct sw_flow *, bool deferred);
 
 int ovs_flow_tbl_init(struct flow_table *);
index ebb6e2442554c89fa2b02f1f8f06b0a3b8acf39e..35ec4fed09e228c7e6fe889d2701cb3a3de7748a 100644 (file)
@@ -172,7 +172,7 @@ static int gre_tnl_send(struct vport *vport, struct sk_buff *skb)
        df = OVS_CB(skb)->tun_key->tun_flags & TUNNEL_DONT_FRAGMENT ?
                htons(IP_DF) : 0;
 
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        return iptunnel_xmit(skb->sk, rt, skb, fl.saddr,
                             OVS_CB(skb)->tun_key->ipv4_dst, IPPROTO_GRE,
@@ -256,7 +256,7 @@ static void gre_tnl_destroy(struct vport *vport)
 
        ovs_net = net_generic(net, ovs_net_id);
 
-       rcu_assign_pointer(ovs_net->vport_net.gre_vport, NULL);
+       RCU_INIT_POINTER(ovs_net->vport_net.gre_vport, NULL);
        ovs_vport_deferred_free(vport);
        gre_exit();
 }
index 729c68763fe70d150793e1f01a023d4d8f1b78c0..789af9280e77264b4d7f65ddb6c333e96fa4147f 100644 (file)
@@ -130,7 +130,7 @@ static void do_setup(struct net_device *netdev)
        netdev->priv_flags &= ~IFF_TX_SKB_SHARING;
        netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
        netdev->destructor = internal_dev_destructor;
-       SET_ETHTOOL_OPS(netdev, &internal_dev_ethtool_ops);
+       netdev->ethtool_ops = &internal_dev_ethtool_ops;
        netdev->tx_queue_len = 0;
 
        netdev->features = NETIF_F_LLTX | NETIF_F_SG | NETIF_F_FRAGLIST |
index e797a50ac2beec3a019bacf15e2e7c593355a46f..0edbd95c60e73abfba45a145978ac1d829bb1321 100644 (file)
@@ -122,7 +122,7 @@ static struct vport *vxlan_tnl_create(const struct vport_parms *parms)
        vxlan_port = vxlan_vport(vport);
        strncpy(vxlan_port->name, parms->name, IFNAMSIZ);
 
-       vs = vxlan_sock_add(net, htons(dst_port), vxlan_rcv, vport, true, false);
+       vs = vxlan_sock_add(net, htons(dst_port), vxlan_rcv, vport, true, 0);
        if (IS_ERR(vs)) {
                ovs_vport_free(vport);
                return (void *)vs;
@@ -170,7 +170,7 @@ static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb)
        df = OVS_CB(skb)->tun_key->tun_flags & TUNNEL_DONT_FRAGMENT ?
                htons(IP_DF) : 0;
 
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        inet_get_local_port_range(net, &port_min, &port_max);
        src_port = vxlan_src_port(port_min, port_max, skb);
@@ -180,7 +180,8 @@ static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb)
                             OVS_CB(skb)->tun_key->ipv4_tos,
                             OVS_CB(skb)->tun_key->ipv4_ttl, df,
                             src_port, dst_port,
-                            htonl(be64_to_cpu(OVS_CB(skb)->tun_key->tun_id) << 8));
+                            htonl(be64_to_cpu(OVS_CB(skb)->tun_key->tun_id) << 8),
+                            false);
        if (err < 0)
                ip_rt_put(rt);
 error:
index d7e50a17396c5563c778ca9e0e07f9c9a730b56a..8d721e62f388d9990b7850942196440550a2a6bb 100644 (file)
@@ -172,7 +172,7 @@ void ovs_vport_deferred_free(struct vport *vport);
  */
 static inline void *vport_priv(const struct vport *vport)
 {
-       return (u8 *)vport + ALIGN(sizeof(struct vport), VPORT_ALIGN);
+       return (u8 *)(uintptr_t)vport + ALIGN(sizeof(struct vport), VPORT_ALIGN);
 }
 
 /**
@@ -185,9 +185,9 @@ static inline void *vport_priv(const struct vport *vport)
  * the result of a hash table lookup.  @priv must point to the start of the
  * private data area.
  */
-static inline struct vport *vport_from_priv(const void *priv)
+static inline struct vport *vport_from_priv(void *priv)
 {
-       return (struct vport *)(priv - ALIGN(sizeof(struct vport), VPORT_ALIGN));
+       return (struct vport *)((u8 *)priv - ALIGN(sizeof(struct vport), VPORT_ALIGN));
 }
 
 void ovs_vport_receive(struct vport *, struct sk_buff *,
index 37be6e226d1b46fefe8e0cf554580b1faf19cfb8..1dde91e3dc7033c575dcfc041a23402f98e52239 100644 (file)
@@ -298,7 +298,7 @@ void rds_ib_send_cq_comp_handler(struct ib_cq *cq, void *context)
                rds_ib_stats_inc(s_ib_tx_cq_event);
 
                if (wc.wr_id == RDS_IB_ACK_WR_ID) {
-                       if (ic->i_ack_queued + HZ/2 < jiffies)
+                       if (time_after(jiffies, ic->i_ack_queued + HZ/2))
                                rds_ib_stats_inc(s_ib_tx_stalled);
                        rds_ib_ack_send_complete(ic);
                        continue;
@@ -315,7 +315,7 @@ void rds_ib_send_cq_comp_handler(struct ib_cq *cq, void *context)
 
                        rm = rds_ib_send_unmap_op(ic, send, wc.status);
 
-                       if (send->s_queued + HZ/2 < jiffies)
+                       if (time_after(jiffies, send->s_queued + HZ/2))
                                rds_ib_stats_inc(s_ib_tx_stalled);
 
                        if (send->s_op) {
index e40c3c5db2c41e543abed12149c791fa9edd2ec6..9105ea03aec5dc05bad0221eb00c4794eb6e463a 100644 (file)
@@ -232,7 +232,7 @@ void rds_iw_send_cq_comp_handler(struct ib_cq *cq, void *context)
                }
 
                if (wc.wr_id == RDS_IW_ACK_WR_ID) {
-                       if (ic->i_ack_queued + HZ/2 < jiffies)
+                       if (time_after(jiffies, ic->i_ack_queued + HZ/2))
                                rds_iw_stats_inc(s_iw_tx_stalled);
                        rds_iw_ack_send_complete(ic);
                        continue;
@@ -267,7 +267,7 @@ void rds_iw_send_cq_comp_handler(struct ib_cq *cq, void *context)
 
                        send->s_wr.opcode = 0xdead;
                        send->s_wr.num_sge = 1;
-                       if (send->s_queued + HZ/2 < jiffies)
+                       if (time_after(jiffies, send->s_queued + HZ/2))
                                rds_iw_stats_inc(s_iw_tx_stalled);
 
                        /* If a RDMA operation produced an error, signal this right
index 89c91515ed0c605b6ee63996d0c0a9692ea8fe91..139239d2cb228438e29f347b33035da15d5396c0 100644 (file)
@@ -111,8 +111,7 @@ static struct ctl_table rds_iw_sysctl_table[] = {
 
 void rds_iw_sysctl_exit(void)
 {
-       if (rds_iw_sysctl_hdr)
-               unregister_net_sysctl_table(rds_iw_sysctl_hdr);
+       unregister_net_sysctl_table(rds_iw_sysctl_hdr);
 }
 
 int rds_iw_sysctl_init(void)
index c2be901d19ee133b60c1b77006f02c88d8b1ec42..6cd9d1deafc395d6573b7e3b801e666106d1f257 100644 (file)
@@ -168,7 +168,7 @@ static int rds_rdma_listen_init(void)
                return ret;
        }
 
-       sin.sin_family = AF_INET,
+       sin.sin_family = AF_INET;
        sin.sin_addr.s_addr = (__force u32)htonl(INADDR_ANY);
        sin.sin_port = (__force u16)htons(RDS_PORT);
 
index b5cb2aa08f33aa62ac5bff73684fc228f74dce7e..c3b0cd43eb56689e395581c4757402bad531e271 100644 (file)
@@ -94,8 +94,7 @@ static struct ctl_table rds_sysctl_rds_table[] = {
 
 void rds_sysctl_exit(void)
 {
-       if (rds_sysctl_reg_table)
-               unregister_net_sysctl_table(rds_sysctl_reg_table);
+       unregister_net_sysctl_table(rds_sysctl_reg_table);
 }
 
 int rds_sysctl_init(void)
index 4e638f85118595d59c888917a9c3f072050c5780..23ab4dcd1d9f03942aa4d70bc7f6d9aa401f7707 100644 (file)
@@ -153,7 +153,7 @@ int rds_tcp_listen_init(void)
        sock->sk->sk_data_ready = rds_tcp_listen_data_ready;
        write_unlock_bh(&sock->sk->sk_callback_lock);
 
-       sin.sin_family = PF_INET,
+       sin.sin_family = PF_INET;
        sin.sin_addr.s_addr = (__force u32)htonl(INADDR_ANY);
        sin.sin_port = (__force u16)htons(RDS_TCP_PORT);
 
index bd2a5b90400cdf2688f249c1bcaaf382a4e918fd..14c98e48f261ee49656eabb198d62648890b63b3 100644 (file)
@@ -36,8 +36,6 @@ struct rfkill_gpio_data {
        struct gpio_desc        *shutdown_gpio;
 
        struct rfkill           *rfkill_dev;
-       char                    *reset_name;
-       char                    *shutdown_name;
        struct clk              *clk;
 
        bool                    clk_enabled;
@@ -47,17 +45,14 @@ static int rfkill_gpio_set_power(void *data, bool blocked)
 {
        struct rfkill_gpio_data *rfkill = data;
 
-       if (blocked) {
-               gpiod_set_value(rfkill->shutdown_gpio, 0);
-               gpiod_set_value(rfkill->reset_gpio, 0);
-               if (!IS_ERR(rfkill->clk) && rfkill->clk_enabled)
-                       clk_disable(rfkill->clk);
-       } else {
-               if (!IS_ERR(rfkill->clk) && !rfkill->clk_enabled)
-                       clk_enable(rfkill->clk);
-               gpiod_set_value(rfkill->reset_gpio, 1);
-               gpiod_set_value(rfkill->shutdown_gpio, 1);
-       }
+       if (!blocked && !IS_ERR(rfkill->clk) && !rfkill->clk_enabled)
+               clk_enable(rfkill->clk);
+
+       gpiod_set_value_cansleep(rfkill->shutdown_gpio, !blocked);
+       gpiod_set_value_cansleep(rfkill->reset_gpio, !blocked);
+
+       if (blocked && !IS_ERR(rfkill->clk) && rfkill->clk_enabled)
+               clk_disable(rfkill->clk);
 
        rfkill->clk_enabled = blocked;
 
@@ -87,10 +82,8 @@ static int rfkill_gpio_probe(struct platform_device *pdev)
 {
        struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data;
        struct rfkill_gpio_data *rfkill;
-       const char *clk_name = NULL;
        struct gpio_desc *gpio;
        int ret;
-       int len;
 
        rfkill = devm_kzalloc(&pdev->dev, sizeof(*rfkill), GFP_KERNEL);
        if (!rfkill)
@@ -101,28 +94,15 @@ static int rfkill_gpio_probe(struct platform_device *pdev)
                if (ret)
                        return ret;
        } else if (pdata) {
-               clk_name = pdata->power_clk_name;
                rfkill->name = pdata->name;
                rfkill->type = pdata->type;
        } else {
                return -ENODEV;
        }
 
-       len = strlen(rfkill->name);
-       rfkill->reset_name = devm_kzalloc(&pdev->dev, len + 7, GFP_KERNEL);
-       if (!rfkill->reset_name)
-               return -ENOMEM;
-
-       rfkill->shutdown_name = devm_kzalloc(&pdev->dev, len + 10, GFP_KERNEL);
-       if (!rfkill->shutdown_name)
-               return -ENOMEM;
+       rfkill->clk = devm_clk_get(&pdev->dev, NULL);
 
-       snprintf(rfkill->reset_name, len + 6 , "%s_reset", rfkill->name);
-       snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", rfkill->name);
-
-       rfkill->clk = devm_clk_get(&pdev->dev, clk_name);
-
-       gpio = devm_gpiod_get_index(&pdev->dev, rfkill->reset_name, 0);
+       gpio = devm_gpiod_get_index(&pdev->dev, "reset", 0);
        if (!IS_ERR(gpio)) {
                ret = gpiod_direction_output(gpio, 0);
                if (ret)
@@ -130,7 +110,7 @@ static int rfkill_gpio_probe(struct platform_device *pdev)
                rfkill->reset_gpio = gpio;
        }
 
-       gpio = devm_gpiod_get_index(&pdev->dev, rfkill->shutdown_name, 1);
+       gpio = devm_gpiod_get_index(&pdev->dev, "shutdown", 1);
        if (!IS_ERR(gpio)) {
                ret = gpiod_direction_output(gpio, 0);
                if (ret)
@@ -146,14 +126,6 @@ static int rfkill_gpio_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       if (pdata && pdata->gpio_runtime_setup) {
-               ret = pdata->gpio_runtime_setup(pdev);
-               if (ret) {
-                       dev_err(&pdev->dev, "can't set up gpio\n");
-                       return ret;
-               }
-       }
-
        rfkill->rfkill_dev = rfkill_alloc(rfkill->name, &pdev->dev,
                                          rfkill->type, &rfkill_gpio_ops,
                                          rfkill);
@@ -174,20 +146,23 @@ static int rfkill_gpio_probe(struct platform_device *pdev)
 static int rfkill_gpio_remove(struct platform_device *pdev)
 {
        struct rfkill_gpio_data *rfkill = platform_get_drvdata(pdev);
-       struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data;
 
-       if (pdata && pdata->gpio_runtime_close)
-               pdata->gpio_runtime_close(pdev);
        rfkill_unregister(rfkill->rfkill_dev);
        rfkill_destroy(rfkill->rfkill_dev);
 
        return 0;
 }
 
+#ifdef CONFIG_ACPI
 static const struct acpi_device_id rfkill_acpi_match[] = {
+       { "BCM2E1A", RFKILL_TYPE_BLUETOOTH },
+       { "BCM2E39", RFKILL_TYPE_BLUETOOTH },
+       { "BCM2E3D", RFKILL_TYPE_BLUETOOTH },
        { "BCM4752", RFKILL_TYPE_GPS },
+       { "LNV4752", RFKILL_TYPE_GPS },
        { },
 };
+#endif
 
 static struct platform_driver rfkill_gpio_driver = {
        .probe = rfkill_gpio_probe,
index bdbdb1a7920af99c1829ae62716caddafb114fef..45527e6b52dbf396cbb7415bb0613152a8320096 100644 (file)
@@ -134,7 +134,8 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
        int err;
        int tp_created = 0;
 
-       if ((n->nlmsg_type != RTM_GETTFILTER) && !netlink_capable(skb, CAP_NET_ADMIN))
+       if ((n->nlmsg_type != RTM_GETTFILTER) &&
+           !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
                return -EPERM;
 
 replay:
@@ -317,7 +318,8 @@ replay:
                }
        }
 
-       err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh);
+       err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh,
+                             n->nlmsg_flags & NLM_F_CREATE ? TCA_ACT_NOREPLACE : TCA_ACT_REPLACE);
        if (err == 0) {
                if (tp_created) {
                        spin_lock_bh(root_lock);
@@ -504,7 +506,7 @@ void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts)
 EXPORT_SYMBOL(tcf_exts_destroy);
 
 int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
-                 struct nlattr *rate_tlv, struct tcf_exts *exts)
+                 struct nlattr *rate_tlv, struct tcf_exts *exts, bool ovr)
 {
 #ifdef CONFIG_NET_CLS_ACT
        {
@@ -513,7 +515,7 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
                INIT_LIST_HEAD(&exts->actions);
                if (exts->police && tb[exts->police]) {
                        act = tcf_action_init_1(net, tb[exts->police], rate_tlv,
-                                               "police", TCA_ACT_NOREPLACE,
+                                               "police", ovr,
                                                TCA_ACT_BIND);
                        if (IS_ERR(act))
                                return PTR_ERR(act);
@@ -523,7 +525,7 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
                } else if (exts->action && tb[exts->action]) {
                        int err;
                        err = tcf_action_init(net, tb[exts->action], rate_tlv,
-                                             NULL, TCA_ACT_NOREPLACE,
+                                             NULL, ovr,
                                              TCA_ACT_BIND, &exts->actions);
                        if (err)
                                return err;
@@ -543,14 +545,12 @@ void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
                     struct tcf_exts *src)
 {
 #ifdef CONFIG_NET_CLS_ACT
-       if (!list_empty(&src->actions)) {
-               LIST_HEAD(tmp);
-               tcf_tree_lock(tp);
-               list_splice_init(&dst->actions, &tmp);
-               list_splice(&src->actions, &dst->actions);
-               tcf_tree_unlock(tp);
-               tcf_action_destroy(&tmp, TCA_ACT_UNBIND);
-       }
+       LIST_HEAD(tmp);
+       tcf_tree_lock(tp);
+       list_splice_init(&dst->actions, &tmp);
+       list_splice(&src->actions, &dst->actions);
+       tcf_tree_unlock(tp);
+       tcf_action_destroy(&tmp, TCA_ACT_UNBIND);
 #endif
 }
 EXPORT_SYMBOL(tcf_exts_change);
index e98ca99c202bb5af6db77260f0996e6cacb098cf..0ae1813e3e90d55a1bf5993364502cebd58c1b22 100644 (file)
@@ -130,14 +130,14 @@ static const struct nla_policy basic_policy[TCA_BASIC_MAX + 1] = {
 static int basic_set_parms(struct net *net, struct tcf_proto *tp,
                           struct basic_filter *f, unsigned long base,
                           struct nlattr **tb,
-                          struct nlattr *est)
+                          struct nlattr *est, bool ovr)
 {
        int err;
        struct tcf_exts e;
        struct tcf_ematch_tree t;
 
        tcf_exts_init(&e, TCA_BASIC_ACT, TCA_BASIC_POLICE);
-       err = tcf_exts_validate(net, tp, tb, est, &e);
+       err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
        if (err < 0)
                return err;
 
@@ -161,7 +161,7 @@ errout:
 
 static int basic_change(struct net *net, struct sk_buff *in_skb,
                        struct tcf_proto *tp, unsigned long base, u32 handle,
-                       struct nlattr **tca, unsigned long *arg)
+                       struct nlattr **tca, unsigned long *arg, bool ovr)
 {
        int err;
        struct basic_head *head = tp->root;
@@ -179,7 +179,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
        if (f != NULL) {
                if (handle && f->handle != handle)
                        return -EINVAL;
-               return basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE]);
+               return basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE], ovr);
        }
 
        err = -ENOBUFS;
@@ -206,7 +206,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
                f->handle = head->hgenerator;
        }
 
-       err = basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE]);
+       err = basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE], ovr);
        if (err < 0)
                goto errout;
 
index 8e3cf49118e3a2297214de12313bb499cbfe9054..13f64df2c710663f2753df92632c5575bc2fe317 100644 (file)
@@ -156,11 +156,11 @@ static void cls_bpf_put(struct tcf_proto *tp, unsigned long f)
 static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp,
                                   struct cls_bpf_prog *prog,
                                   unsigned long base, struct nlattr **tb,
-                                  struct nlattr *est)
+                                  struct nlattr *est, bool ovr)
 {
        struct sock_filter *bpf_ops, *bpf_old;
        struct tcf_exts exts;
-       struct sock_fprog tmp;
+       struct sock_fprog_kern tmp;
        struct sk_filter *fp, *fp_old;
        u16 bpf_size, bpf_len;
        u32 classid;
@@ -170,7 +170,7 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp,
                return -EINVAL;
 
        tcf_exts_init(&exts, TCA_BPF_ACT, TCA_BPF_POLICE);
-       ret = tcf_exts_validate(net, tp, tb, est, &exts);
+       ret = tcf_exts_validate(net, tp, tb, est, &exts, ovr);
        if (ret < 0)
                return ret;
 
@@ -191,7 +191,7 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp,
        memcpy(bpf_ops, nla_data(tb[TCA_BPF_OPS]), bpf_size);
 
        tmp.len = bpf_len;
-       tmp.filter = (struct sock_filter __user *) bpf_ops;
+       tmp.filter = bpf_ops;
 
        ret = sk_unattached_filter_create(&fp, &tmp);
        if (ret)
@@ -242,7 +242,7 @@ static u32 cls_bpf_grab_new_handle(struct tcf_proto *tp,
 static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
                          struct tcf_proto *tp, unsigned long base,
                          u32 handle, struct nlattr **tca,
-                         unsigned long *arg)
+                         unsigned long *arg, bool ovr)
 {
        struct cls_bpf_head *head = tp->root;
        struct cls_bpf_prog *prog = (struct cls_bpf_prog *) *arg;
@@ -260,7 +260,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
                if (handle && prog->handle != handle)
                        return -EINVAL;
                return cls_bpf_modify_existing(net, tp, prog, base, tb,
-                                              tca[TCA_RATE]);
+                                              tca[TCA_RATE], ovr);
        }
 
        prog = kzalloc(sizeof(*prog), GFP_KERNEL);
@@ -277,7 +277,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
                goto errout;
        }
 
-       ret = cls_bpf_modify_existing(net, tp, prog, base, tb, tca[TCA_RATE]);
+       ret = cls_bpf_modify_existing(net, tp, prog, base, tb, tca[TCA_RATE], ovr);
        if (ret < 0)
                goto errout;
 
index 8e2158ab551c0c9d7258ffae3e474788bfc64565..cacf01bd04f0a96660050c0e71da0840c8af0f95 100644 (file)
@@ -83,7 +83,7 @@ static const struct nla_policy cgroup_policy[TCA_CGROUP_MAX + 1] = {
 static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
                             struct tcf_proto *tp, unsigned long base,
                             u32 handle, struct nlattr **tca,
-                            unsigned long *arg)
+                            unsigned long *arg, bool ovr)
 {
        struct nlattr *tb[TCA_CGROUP_MAX + 1];
        struct cls_cgroup_head *head = tp->root;
@@ -119,7 +119,7 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
                return err;
 
        tcf_exts_init(&e, TCA_CGROUP_ACT, TCA_CGROUP_POLICE);
-       err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e);
+       err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
        if (err < 0)
                return err;
 
index 257029c5433298f62101533ada3a81c246eb0d7a..35be16f7c192dc8d5d2ebdd38c2b27a144c41076 100644 (file)
@@ -349,7 +349,7 @@ static const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = {
 static int flow_change(struct net *net, struct sk_buff *in_skb,
                       struct tcf_proto *tp, unsigned long base,
                       u32 handle, struct nlattr **tca,
-                      unsigned long *arg)
+                      unsigned long *arg, bool ovr)
 {
        struct flow_head *head = tp->root;
        struct flow_filter *f;
@@ -393,7 +393,7 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
        }
 
        tcf_exts_init(&e, TCA_FLOW_ACT, TCA_FLOW_POLICE);
-       err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e);
+       err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
        if (err < 0)
                return err;
 
index 63a3ce75c02ee959fe67d6b203e01420d86b03ad..861b03ccfed0a55007ceb001a297b05906b36ed3 100644 (file)
@@ -169,7 +169,7 @@ static const struct nla_policy fw_policy[TCA_FW_MAX + 1] = {
 
 static int
 fw_change_attrs(struct net *net, struct tcf_proto *tp, struct fw_filter *f,
-       struct nlattr **tb, struct nlattr **tca, unsigned long base)
+       struct nlattr **tb, struct nlattr **tca, unsigned long base, bool ovr)
 {
        struct fw_head *head = tp->root;
        struct tcf_exts e;
@@ -177,7 +177,7 @@ fw_change_attrs(struct net *net, struct tcf_proto *tp, struct fw_filter *f,
        int err;
 
        tcf_exts_init(&e, TCA_FW_ACT, TCA_FW_POLICE);
-       err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e);
+       err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
        if (err < 0)
                return err;
 
@@ -218,7 +218,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb,
                     struct tcf_proto *tp, unsigned long base,
                     u32 handle,
                     struct nlattr **tca,
-                    unsigned long *arg)
+                    unsigned long *arg, bool ovr)
 {
        struct fw_head *head = tp->root;
        struct fw_filter *f = (struct fw_filter *) *arg;
@@ -236,7 +236,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb,
        if (f != NULL) {
                if (f->id != handle && handle)
                        return -EINVAL;
-               return fw_change_attrs(net, tp, f, tb, tca, base);
+               return fw_change_attrs(net, tp, f, tb, tca, base, ovr);
        }
 
        if (!handle)
@@ -264,7 +264,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb,
        tcf_exts_init(&f->exts, TCA_FW_ACT, TCA_FW_POLICE);
        f->id = handle;
 
-       err = fw_change_attrs(net, tp, f, tb, tca, base);
+       err = fw_change_attrs(net, tp, f, tb, tca, base, ovr);
        if (err < 0)
                goto errout;
 
index 1ad3068f2ce16e2c6ba15985c40cf899a7030ba7..dd9fc2523c76a2b0b9fe4c5225328cc29c1649f6 100644 (file)
@@ -333,7 +333,8 @@ static const struct nla_policy route4_policy[TCA_ROUTE4_MAX + 1] = {
 static int route4_set_parms(struct net *net, struct tcf_proto *tp,
                            unsigned long base, struct route4_filter *f,
                            u32 handle, struct route4_head *head,
-                           struct nlattr **tb, struct nlattr *est, int new)
+                           struct nlattr **tb, struct nlattr *est, int new,
+                           bool ovr)
 {
        int err;
        u32 id = 0, to = 0, nhandle = 0x8000;
@@ -343,7 +344,7 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp,
        struct tcf_exts e;
 
        tcf_exts_init(&e, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE);
-       err = tcf_exts_validate(net, tp, tb, est, &e);
+       err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
        if (err < 0)
                return err;
 
@@ -428,7 +429,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
                       struct tcf_proto *tp, unsigned long base,
                       u32 handle,
                       struct nlattr **tca,
-                      unsigned long *arg)
+                      unsigned long *arg, bool ovr)
 {
        struct route4_head *head = tp->root;
        struct route4_filter *f, *f1, **fp;
@@ -455,7 +456,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
                        old_handle = f->handle;
 
                err = route4_set_parms(net, tp, base, f, handle, head, tb,
-                       tca[TCA_RATE], 0);
+                       tca[TCA_RATE], 0, ovr);
                if (err < 0)
                        return err;
 
@@ -479,7 +480,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
 
        tcf_exts_init(&f->exts, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE);
        err = route4_set_parms(net, tp, base, f, handle, head, tb,
-               tca[TCA_RATE], 1);
+               tca[TCA_RATE], 1, ovr);
        if (err < 0)
                goto errout;
 
index 19f8e5dfa8bdaebcd9ab903050047e7e49690530..1020e233a5d6c74092fb153133b1bfed7f4177a9 100644 (file)
@@ -415,7 +415,7 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb,
                       struct tcf_proto *tp, unsigned long base,
                       u32 handle,
                       struct nlattr **tca,
-                      unsigned long *arg)
+                      unsigned long *arg, bool ovr)
 {
        struct rsvp_head *data = tp->root;
        struct rsvp_filter *f, **fp;
@@ -436,7 +436,7 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb,
                return err;
 
        tcf_exts_init(&e, TCA_RSVP_ACT, TCA_RSVP_POLICE);
-       err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e);
+       err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
        if (err < 0)
                return err;
 
index f435a88d899afff7c132085ccece5427461e5490..c721cd4a469fe39f42186d82ec7ab04f17bcba26 100644 (file)
@@ -198,7 +198,7 @@ static int
 tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
                  u32 handle, struct tcindex_data *p,
                  struct tcindex_filter_result *r, struct nlattr **tb,
-                struct nlattr *est)
+                 struct nlattr *est, bool ovr)
 {
        int err, balloc = 0;
        struct tcindex_filter_result new_filter_result, *old_r = r;
@@ -208,7 +208,7 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
        struct tcf_exts e;
 
        tcf_exts_init(&e, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE);
-       err = tcf_exts_validate(net, tp, tb, est, &e);
+       err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
        if (err < 0)
                return err;
 
@@ -341,7 +341,7 @@ errout:
 static int
 tcindex_change(struct net *net, struct sk_buff *in_skb,
               struct tcf_proto *tp, unsigned long base, u32 handle,
-              struct nlattr **tca, unsigned long *arg)
+              struct nlattr **tca, unsigned long *arg, bool ovr)
 {
        struct nlattr *opt = tca[TCA_OPTIONS];
        struct nlattr *tb[TCA_TCINDEX_MAX + 1];
@@ -361,7 +361,7 @@ tcindex_change(struct net *net, struct sk_buff *in_skb,
                return err;
 
        return tcindex_set_parms(net, tp, base, handle, p, r, tb,
-                                tca[TCA_RATE]);
+                                tca[TCA_RATE], ovr);
 }
 
 
index 84c28daff8484f643e5bed4176f6b225eec34e66..c39b583ace3229d4bae6a7b3774593e5eebd7141 100644 (file)
@@ -486,13 +486,13 @@ static const struct nla_policy u32_policy[TCA_U32_MAX + 1] = {
 static int u32_set_parms(struct net *net, struct tcf_proto *tp,
                         unsigned long base, struct tc_u_hnode *ht,
                         struct tc_u_knode *n, struct nlattr **tb,
-                        struct nlattr *est)
+                        struct nlattr *est, bool ovr)
 {
        int err;
        struct tcf_exts e;
 
        tcf_exts_init(&e, TCA_U32_ACT, TCA_U32_POLICE);
-       err = tcf_exts_validate(net, tp, tb, est, &e);
+       err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
        if (err < 0)
                return err;
 
@@ -545,7 +545,7 @@ errout:
 static int u32_change(struct net *net, struct sk_buff *in_skb,
                      struct tcf_proto *tp, unsigned long base, u32 handle,
                      struct nlattr **tca,
-                     unsigned long *arg)
+                     unsigned long *arg, bool ovr)
 {
        struct tc_u_common *tp_c = tp->data;
        struct tc_u_hnode *ht;
@@ -569,7 +569,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
                        return -EINVAL;
 
                return u32_set_parms(net, tp, base, n->ht_up, n, tb,
-                                    tca[TCA_RATE]);
+                                    tca[TCA_RATE], ovr);
        }
 
        if (tb[TCA_U32_DIVISOR]) {
@@ -656,7 +656,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
        }
 #endif
 
-       err = u32_set_parms(net, tp, base, ht, n, tb, tca[TCA_RATE]);
+       err = u32_set_parms(net, tp, base, ht, n, tb, tca[TCA_RATE], ovr);
        if (err == 0) {
                struct tc_u_knode **ins;
                for (ins = &ht->ht[TC_U32_HASH(handle)]; *ins; ins = &(*ins)->next)
index 400769014bbde8d4e9032600524e97f549dcff31..58bed7599db7d9f5538f01ff5057e7aa79d0a915 100644 (file)
@@ -563,7 +563,7 @@ out:
 }
 EXPORT_SYMBOL(__qdisc_calculate_pkt_len);
 
-void qdisc_warn_nonwc(char *txt, struct Qdisc *qdisc)
+void qdisc_warn_nonwc(const char *txt, struct Qdisc *qdisc)
 {
        if (!(qdisc->flags & TCQ_F_WARN_NONWC)) {
                pr_warn("%s: %s qdisc %X: is non-work-conserving?\n",
@@ -1084,7 +1084,8 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n)
        struct Qdisc *p = NULL;
        int err;
 
-       if ((n->nlmsg_type != RTM_GETQDISC) && !netlink_capable(skb, CAP_NET_ADMIN))
+       if ((n->nlmsg_type != RTM_GETQDISC) &&
+           !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
                return -EPERM;
 
        err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL);
@@ -1151,7 +1152,7 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n)
        struct Qdisc *q, *p;
        int err;
 
-       if (!netlink_capable(skb, CAP_NET_ADMIN))
+       if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
                return -EPERM;
 
 replay:
@@ -1490,7 +1491,8 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n)
        u32 qid;
        int err;
 
-       if ((n->nlmsg_type != RTM_GETTCLASS) && !netlink_capable(skb, CAP_NET_ADMIN))
+       if ((n->nlmsg_type != RTM_GETTCLASS) &&
+           !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
                return -EPERM;
 
        err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL);
index 2aee02802c2760fafabb47d6bf4229d08d8ef907..ed30e436128bff7c556e0c334e4cb5ae8d088e5e 100644 (file)
@@ -391,12 +391,7 @@ static const struct nla_policy choke_policy[TCA_CHOKE_MAX + 1] = {
 
 static void choke_free(void *addr)
 {
-       if (addr) {
-               if (is_vmalloc_addr(addr))
-                       vfree(addr);
-               else
-                       kfree(addr);
-       }
+       kvfree(addr);
 }
 
 static int choke_change(struct Qdisc *sch, struct nlattr *opt)
index 8302717ea3034247bc27597e067ac8771dc973b5..7bbbfe1121920a126537d43a0443ab4b98e340fa 100644 (file)
@@ -391,8 +391,10 @@ static struct sk_buff *drr_dequeue(struct Qdisc *sch)
        while (1) {
                cl = list_first_entry(&q->active, struct drr_class, alist);
                skb = cl->qdisc->ops->peek(cl->qdisc);
-               if (skb == NULL)
+               if (skb == NULL) {
+                       qdisc_warn_nonwc(__func__, cl->qdisc);
                        goto out;
+               }
 
                len = qdisc_pkt_len(skb);
                if (len <= cl->deficit) {
index 23c682b42f99ecb86ca8a103005a52d692a21d38..ba32c2b005d0821788f656e8ae05d4d47a862351 100644 (file)
@@ -591,10 +591,7 @@ static void *fq_alloc_node(size_t sz, int node)
 
 static void fq_free(void *addr)
 {
-       if (addr && is_vmalloc_addr(addr))
-               vfree(addr);
-       else
-               kfree(addr);
+       kvfree(addr);
 }
 
 static int fq_resize(struct Qdisc *sch, u32 log)
index 0bf432c782c1f17cdb8bbb1b8ece16848e63c27e..063b726bf1f8636e3529a2e61d1b43fced918d9c 100644 (file)
@@ -365,12 +365,7 @@ static void *fq_codel_zalloc(size_t sz)
 
 static void fq_codel_free(void *addr)
 {
-       if (addr) {
-               if (is_vmalloc_addr(addr))
-                       vfree(addr);
-               else
-                       kfree(addr);
-       }
+       kvfree(addr);
 }
 
 static void fq_codel_destroy(struct Qdisc *sch)
index 6e957c3b9854acc025e43d9028b94f0af7f4d6c1..d85b6812a7d4c6bd0794decd872406e6b810e779 100644 (file)
@@ -414,7 +414,7 @@ static int hhf_enqueue(struct sk_buff *skb, struct Qdisc *sch)
                }
                bucket->deficit = weight * q->quantum;
        }
-       if (++sch->q.qlen < sch->limit)
+       if (++sch->q.qlen <= sch->limit)
                return NET_XMIT_SUCCESS;
 
        q->drop_overlimit++;
@@ -494,12 +494,7 @@ static void *hhf_zalloc(size_t sz)
 
 static void hhf_free(void *addr)
 {
-       if (addr) {
-               if (is_vmalloc_addr(addr))
-                       vfree(addr);
-               else
-                       kfree(addr);
-       }
+       kvfree(addr);
 }
 
 static void hhf_destroy(struct Qdisc *sch)
index f1669a00f5710c297310f4a1da4a1b4e72cd3cab..111d70fddaea97242ac4a85115e969efcd11dfcd 100644 (file)
@@ -648,12 +648,7 @@ static void netem_reset(struct Qdisc *sch)
 
 static void dist_free(struct disttable *d)
 {
-       if (d) {
-               if (is_vmalloc_addr(d))
-                       vfree(d);
-               else
-                       kfree(d);
-       }
+       kvfree(d);
 }
 
 /*
index 87317ff0b4ec4699dae7898405666d2bde6c9897..1af2f73906d07aa5bf6f428ffe37b81523178ee4 100644 (file)
@@ -716,12 +716,7 @@ static void *sfq_alloc(size_t sz)
 
 static void sfq_free(void *addr)
 {
-       if (addr) {
-               if (is_vmalloc_addr(addr))
-                       vfree(addr);
-               else
-                       kfree(addr);
-       }
+       kvfree(addr);
 }
 
 static void sfq_destroy(struct Qdisc *sch)
index 39579c3e0d14c12f165e420be064d7fbf248c0ad..9de23a222d3f7b9f33e650483d749d3341c51dc0 100644 (file)
@@ -55,6 +55,7 @@
 #include <net/sctp/sm.h>
 
 /* Forward declarations for internal functions. */
+static void sctp_select_active_and_retran_path(struct sctp_association *asoc);
 static void sctp_assoc_bh_rcv(struct work_struct *work);
 static void sctp_assoc_free_asconf_acks(struct sctp_association *asoc);
 static void sctp_assoc_free_asconf_queue(struct sctp_association *asoc);
@@ -330,7 +331,7 @@ void sctp_association_free(struct sctp_association *asoc)
        /* Only real associations count against the endpoint, so
         * don't bother for if this is a temporary association.
         */
-       if (!asoc->temp) {
+       if (!list_empty(&asoc->asocs)) {
                list_del(&asoc->asocs);
 
                /* Decrement the backlog value for a TCP-style listening
@@ -774,9 +775,6 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
                                  sctp_transport_cmd_t command,
                                  sctp_sn_error_t error)
 {
-       struct sctp_transport *t = NULL;
-       struct sctp_transport *first;
-       struct sctp_transport *second;
        struct sctp_ulpevent *event;
        struct sockaddr_storage addr;
        int spc_state = 0;
@@ -829,13 +827,14 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
                return;
        }
 
-       /* Generate and send a SCTP_PEER_ADDR_CHANGE notification to the
-        * user.
+       /* Generate and send a SCTP_PEER_ADDR_CHANGE notification
+        * to the user.
         */
        if (ulp_notify) {
                memset(&addr, 0, sizeof(struct sockaddr_storage));
                memcpy(&addr, &transport->ipaddr,
                       transport->af_specific->sockaddr_len);
+
                event = sctp_ulpevent_make_peer_addr_change(asoc, &addr,
                                        0, spc_state, error, GFP_ATOMIC);
                if (event)
@@ -843,60 +842,7 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
        }
 
        /* Select new active and retran paths. */
-
-       /* Look for the two most recently used active transports.
-        *
-        * This code produces the wrong ordering whenever jiffies
-        * rolls over, but we still get usable transports, so we don't
-        * worry about it.
-        */
-       first = NULL; second = NULL;
-
-       list_for_each_entry(t, &asoc->peer.transport_addr_list,
-                       transports) {
-
-               if ((t->state == SCTP_INACTIVE) ||
-                   (t->state == SCTP_UNCONFIRMED) ||
-                   (t->state == SCTP_PF))
-                       continue;
-               if (!first || t->last_time_heard > first->last_time_heard) {
-                       second = first;
-                       first = t;
-               } else if (!second ||
-                          t->last_time_heard > second->last_time_heard)
-                       second = t;
-       }
-
-       /* RFC 2960 6.4 Multi-Homed SCTP Endpoints
-        *
-        * By default, an endpoint should always transmit to the
-        * primary path, unless the SCTP user explicitly specifies the
-        * destination transport address (and possibly source
-        * transport address) to use.
-        *
-        * [If the primary is active but not most recent, bump the most
-        * recently used transport.]
-        */
-       if (((asoc->peer.primary_path->state == SCTP_ACTIVE) ||
-            (asoc->peer.primary_path->state == SCTP_UNKNOWN)) &&
-           first != asoc->peer.primary_path) {
-               second = first;
-               first = asoc->peer.primary_path;
-       }
-
-       if (!second)
-               second = first;
-       /* If we failed to find a usable transport, just camp on the
-        * primary, even if it is inactive.
-        */
-       if (!first) {
-               first = asoc->peer.primary_path;
-               second = asoc->peer.primary_path;
-       }
-
-       /* Set the active and retran transports.  */
-       asoc->peer.active_path = first;
-       asoc->peer.retran_path = second;
+       sctp_select_active_and_retran_path(asoc);
 }
 
 /* Hold a reference to an association. */
@@ -1090,7 +1036,7 @@ static void sctp_assoc_bh_rcv(struct work_struct *work)
                }
 
                if (chunk->transport)
-                       chunk->transport->last_time_heard = jiffies;
+                       chunk->transport->last_time_heard = ktime_get();
 
                /* Run through the state machine. */
                error = sctp_do_sm(net, SCTP_EVENT_T_CHUNK, subtype,
@@ -1278,13 +1224,41 @@ static u8 sctp_trans_score(const struct sctp_transport *trans)
        return sctp_trans_state_to_prio_map[trans->state];
 }
 
+static struct sctp_transport *sctp_trans_elect_tie(struct sctp_transport *trans1,
+                                                  struct sctp_transport *trans2)
+{
+       if (trans1->error_count > trans2->error_count) {
+               return trans2;
+       } else if (trans1->error_count == trans2->error_count &&
+                  ktime_after(trans2->last_time_heard,
+                              trans1->last_time_heard)) {
+               return trans2;
+       } else {
+               return trans1;
+       }
+}
+
 static struct sctp_transport *sctp_trans_elect_best(struct sctp_transport *curr,
                                                    struct sctp_transport *best)
 {
+       u8 score_curr, score_best;
+
        if (best == NULL)
                return curr;
 
-       return sctp_trans_score(curr) > sctp_trans_score(best) ? curr : best;
+       score_curr = sctp_trans_score(curr);
+       score_best = sctp_trans_score(best);
+
+       /* First, try a score-based selection if both transport states
+        * differ. If we're in a tie, lets try to make a more clever
+        * decision here based on error counts and last time heard.
+        */
+       if (score_curr > score_best)
+               return curr;
+       else if (score_curr == score_best)
+               return sctp_trans_elect_tie(curr, best);
+       else
+               return best;
 }
 
 void sctp_assoc_update_retran_path(struct sctp_association *asoc)
@@ -1325,6 +1299,76 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc)
                 __func__, asoc, &asoc->peer.retran_path->ipaddr.sa);
 }
 
+static void sctp_select_active_and_retran_path(struct sctp_association *asoc)
+{
+       struct sctp_transport *trans, *trans_pri = NULL, *trans_sec = NULL;
+       struct sctp_transport *trans_pf = NULL;
+
+       /* Look for the two most recently used active transports. */
+       list_for_each_entry(trans, &asoc->peer.transport_addr_list,
+                           transports) {
+               /* Skip uninteresting transports. */
+               if (trans->state == SCTP_INACTIVE ||
+                   trans->state == SCTP_UNCONFIRMED)
+                       continue;
+               /* Keep track of the best PF transport from our
+                * list in case we don't find an active one.
+                */
+               if (trans->state == SCTP_PF) {
+                       trans_pf = sctp_trans_elect_best(trans, trans_pf);
+                       continue;
+               }
+               /* For active transports, pick the most recent ones. */
+               if (trans_pri == NULL ||
+                   ktime_after(trans->last_time_heard,
+                               trans_pri->last_time_heard)) {
+                       trans_sec = trans_pri;
+                       trans_pri = trans;
+               } else if (trans_sec == NULL ||
+                          ktime_after(trans->last_time_heard,
+                                      trans_sec->last_time_heard)) {
+                       trans_sec = trans;
+               }
+       }
+
+       /* RFC 2960 6.4 Multi-Homed SCTP Endpoints
+        *
+        * By default, an endpoint should always transmit to the primary
+        * path, unless the SCTP user explicitly specifies the
+        * destination transport address (and possibly source transport
+        * address) to use. [If the primary is active but not most recent,
+        * bump the most recently used transport.]
+        */
+       if ((asoc->peer.primary_path->state == SCTP_ACTIVE ||
+            asoc->peer.primary_path->state == SCTP_UNKNOWN) &&
+            asoc->peer.primary_path != trans_pri) {
+               trans_sec = trans_pri;
+               trans_pri = asoc->peer.primary_path;
+       }
+
+       /* We did not find anything useful for a possible retransmission
+        * path; either primary path that we found is the the same as
+        * the current one, or we didn't generally find an active one.
+        */
+       if (trans_sec == NULL)
+               trans_sec = trans_pri;
+
+       /* If we failed to find a usable transport, just camp on the
+        * primary or retran, even if they are inactive, if possible
+        * pick a PF iff it's the better choice.
+        */
+       if (trans_pri == NULL) {
+               trans_pri = sctp_trans_elect_best(asoc->peer.primary_path,
+                                                 asoc->peer.retran_path);
+               trans_pri = sctp_trans_elect_best(trans_pri, trans_pf);
+               trans_sec = asoc->peer.primary_path;
+       }
+
+       /* Set the active and retran transports. */
+       asoc->peer.active_path = trans_pri;
+       asoc->peer.retran_path = trans_sec;
+}
+
 struct sctp_transport *
 sctp_assoc_choose_alter_transport(struct sctp_association *asoc,
                                  struct sctp_transport *last_sent_to)
@@ -1547,7 +1591,7 @@ int sctp_assoc_lookup_laddr(struct sctp_association *asoc,
 /* Set an association id for a given association */
 int sctp_assoc_set_id(struct sctp_association *asoc, gfp_t gfp)
 {
-       bool preload = gfp & __GFP_WAIT;
+       bool preload = !!(gfp & __GFP_WAIT);
        int ret;
 
        /* If the id is already assigned, keep it. */
index 3d9f429858dca24f6262996bbec03ca59491b0c0..9da76ba4d10fe316884973448c19e5b9b8310a79 100644 (file)
@@ -481,7 +481,7 @@ normal:
                }
 
                if (chunk->transport)
-                       chunk->transport->last_time_heard = jiffies;
+                       chunk->transport->last_time_heard = ktime_get();
 
                error = sctp_do_sm(net, SCTP_EVENT_T_CHUNK, subtype, state,
                                   ep, asoc, chunk, GFP_ATOMIC);
index 2b1738ef9394537589b403f7d299181e18fb2315..1999592ba88c9f3d574428e1ce076c10f724c13a 100644 (file)
@@ -216,7 +216,7 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport)
        IP6_ECN_flow_xmit(sk, fl6->flowlabel);
 
        if (!(transport->param_flags & SPP_PMTUD_ENABLE))
-               skb->local_df = 1;
+               skb->ignore_df = 1;
 
        SCTP_INC_STATS(sock_net(sk), SCTP_MIB_OUTSCTPPACKS);
 
@@ -943,7 +943,6 @@ static struct inet_protosw sctpv6_seqpacket_protosw = {
        .protocol      = IPPROTO_SCTP,
        .prot          = &sctpv6_prot,
        .ops           = &inet6_seqpacket_ops,
-       .no_check      = 0,
        .flags         = SCTP_PROTOSW_FLAG
 };
 static struct inet_protosw sctpv6_stream_protosw = {
@@ -951,7 +950,6 @@ static struct inet_protosw sctpv6_stream_protosw = {
        .protocol      = IPPROTO_SCTP,
        .prot          = &sctpv6_prot,
        .ops           = &inet6_seqpacket_ops,
-       .no_check      = 0,
        .flags         = SCTP_PROTOSW_FLAG,
 };
 
index 0f4d15fc2627bcccb546ee2da883f812daa4e4a1..01ab8e0723f04ea845ba81045228786ef07c97ad 100644 (file)
@@ -591,7 +591,7 @@ int sctp_packet_transmit(struct sctp_packet *packet)
 
        pr_debug("***sctp_transmit_packet*** skb->len:%d\n", nskb->len);
 
-       nskb->local_df = packet->ipfragok;
+       nskb->ignore_df = packet->ipfragok;
        tp->af_specific->sctp_xmit(nskb, tp);
 
 out:
index 0947f1e15eb88a0381434a05da082bf3d6fd9797..34229ee7f379902b16a7659f82545f10e5908b17 100644 (file)
@@ -78,7 +78,7 @@ static int sctp_snmp_seq_show(struct seq_file *seq, void *v)
 
        for (i = 0; sctp_snmp_list[i].name != NULL; i++)
                seq_printf(seq, "%-32s\t%ld\n", sctp_snmp_list[i].name,
-                          snmp_fold_field((void __percpu **)net->sctp.sctp_statistics,
+                          snmp_fold_field(net->sctp.sctp_statistics,
                                      sctp_snmp_list[i].entry));
 
        return 0;
index 44cbb54c85746a586407015c952e7f8d60ab4d91..6789d785e698325afc7c8cfd65d1b69c2e50047a 100644 (file)
@@ -1017,7 +1017,6 @@ static struct inet_protosw sctp_seqpacket_protosw = {
        .protocol   = IPPROTO_SCTP,
        .prot       = &sctp_prot,
        .ops        = &inet_seqpacket_ops,
-       .no_check   = 0,
        .flags      = SCTP_PROTOSW_FLAG
 };
 static struct inet_protosw sctp_stream_protosw = {
@@ -1025,7 +1024,6 @@ static struct inet_protosw sctp_stream_protosw = {
        .protocol   = IPPROTO_SCTP,
        .prot       = &sctp_prot,
        .ops        = &inet_seqpacket_ops,
-       .no_check   = 0,
        .flags      = SCTP_PROTOSW_FLAG
 };
 
@@ -1105,14 +1103,15 @@ int sctp_register_pf(struct sctp_pf *pf, sa_family_t family)
 
 static inline int init_sctp_mibs(struct net *net)
 {
-       return snmp_mib_init((void __percpu **)net->sctp.sctp_statistics,
-                            sizeof(struct sctp_mib),
-                            __alignof__(struct sctp_mib));
+       net->sctp.sctp_statistics = alloc_percpu(struct sctp_mib);
+       if (!net->sctp.sctp_statistics)
+               return -ENOMEM;
+       return 0;
 }
 
 static inline void cleanup_sctp_mibs(struct net *net)
 {
-       snmp_mib_free((void __percpu **)net->sctp.sctp_statistics);
+       free_percpu(net->sctp.sctp_statistics);
 }
 
 static void sctp_v4_pf_init(void)
index fee5552ddf929e40f702e1d0b86ead2cb0630f97..ae0e616a7ca5ed64ee9e7e09cab08a13caf7bff7 100644 (file)
@@ -1782,7 +1782,7 @@ no_hmac:
        else
                kt = ktime_get();
 
-       if (!asoc && ktime_compare(bear_cookie->expiration, kt) < 0) {
+       if (!asoc && ktime_before(bear_cookie->expiration, kt)) {
                /*
                 * Section 3.3.10.3 Stale Cookie Error (3)
                 *
index fee06b99a4da8dab4d2b094196af08e23d1f22d4..429899689408caec64cf5c36b4eb0b00369f8e48 100644 (file)
@@ -71,6 +71,7 @@
 #include <net/route.h>
 #include <net/ipv6.h>
 #include <net/inet_common.h>
+#include <net/busy_poll.h>
 
 #include <linux/socket.h> /* for sa_family_t */
 #include <linux/export.h>
@@ -5945,8 +5946,9 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
                /* Search for an available port. */
                int low, high, remaining, index;
                unsigned int rover;
+               struct net *net = sock_net(sk);
 
-               inet_get_local_port_range(sock_net(sk), &low, &high);
+               inet_get_local_port_range(net, &low, &high);
                remaining = (high - low) + 1;
                rover = prandom_u32() % remaining + low;
 
@@ -5954,7 +5956,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
                        rover++;
                        if ((rover < low) || (rover > high))
                                rover = low;
-                       if (inet_is_reserved_local_port(rover))
+                       if (inet_is_local_reserved_port(net, rover))
                                continue;
                        index = sctp_phashfn(sock_net(sk), rover);
                        head = &sctp_port_hashtable[index];
@@ -6557,6 +6559,10 @@ static struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags,
                if (sk->sk_shutdown & RCV_SHUTDOWN)
                        break;
 
+               if (sk_can_busy_loop(sk) &&
+                   sk_busy_loop(sk, noblock))
+                       continue;
+
                /* User doesn't want to wait.  */
                error = -EAGAIN;
                if (!timeo)
@@ -6940,7 +6946,8 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk,
        newsk->sk_type = sk->sk_type;
        newsk->sk_bound_dev_if = sk->sk_bound_dev_if;
        newsk->sk_flags = sk->sk_flags;
-       newsk->sk_no_check = sk->sk_no_check;
+       newsk->sk_no_check_tx = sk->sk_no_check_tx;
+       newsk->sk_no_check_rx = sk->sk_no_check_rx;
        newsk->sk_reuse = sk->sk_reuse;
 
        newsk->sk_shutdown = sk->sk_shutdown;
index c82fdc1eab7c359dbee3812db3f3707fa5c65560..dcb19592761e3b80b92057ba5b834e568a338ab9 100644 (file)
@@ -34,6 +34,8 @@
  *    Sridhar Samudrala     <sri@us.ibm.com>
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <net/sctp/structs.h>
 #include <net/sctp/sctp.h>
 #include <linux/sysctl.h>
@@ -46,6 +48,11 @@ static int sack_timer_min = 1;
 static int sack_timer_max = 500;
 static int addr_scope_max = 3; /* check sctp_scope_policy_t in include/net/sctp/constants.h for max entries */
 static int rwnd_scale_max = 16;
+static int rto_alpha_min = 0;
+static int rto_beta_min = 0;
+static int rto_alpha_max = 1000;
+static int rto_beta_max = 1000;
+
 static unsigned long max_autoclose_min = 0;
 static unsigned long max_autoclose_max =
        (MAX_SCHEDULE_TIMEOUT / HZ > UINT_MAX)
@@ -64,6 +71,9 @@ static int proc_sctp_do_rto_min(struct ctl_table *ctl, int write,
 static int proc_sctp_do_rto_max(struct ctl_table *ctl, int write,
                                void __user *buffer, size_t *lenp,
                                loff_t *ppos);
+static int proc_sctp_do_alpha_beta(struct ctl_table *ctl, int write,
+                                  void __user *buffer, size_t *lenp,
+                                  loff_t *ppos);
 static int proc_sctp_do_auth(struct ctl_table *ctl, int write,
                             void __user *buffer, size_t *lenp,
                             loff_t *ppos);
@@ -126,15 +136,19 @@ static struct ctl_table sctp_net_table[] = {
                .procname       = "rto_alpha_exp_divisor",
                .data           = &init_net.sctp.rto_alpha,
                .maxlen         = sizeof(int),
-               .mode           = 0444,
-               .proc_handler   = proc_dointvec,
+               .mode           = 0644,
+               .proc_handler   = proc_sctp_do_alpha_beta,
+               .extra1         = &rto_alpha_min,
+               .extra2         = &rto_alpha_max,
        },
        {
                .procname       = "rto_beta_exp_divisor",
                .data           = &init_net.sctp.rto_beta,
                .maxlen         = sizeof(int),
-               .mode           = 0444,
-               .proc_handler   = proc_dointvec,
+               .mode           = 0644,
+               .proc_handler   = proc_sctp_do_alpha_beta,
+               .extra1         = &rto_beta_min,
+               .extra2         = &rto_beta_max,
        },
        {
                .procname       = "max_burst",
@@ -403,6 +417,16 @@ static int proc_sctp_do_rto_max(struct ctl_table *ctl, int write,
        return ret;
 }
 
+static int proc_sctp_do_alpha_beta(struct ctl_table *ctl, int write,
+                                  void __user *buffer, size_t *lenp,
+                                  loff_t *ppos)
+{
+       pr_warn_once("Changing rto_alpha or rto_beta may lead to "
+                    "suboptimal rtt/srtt estimations!\n");
+
+       return proc_dointvec_minmax(ctl, write, buffer, lenp, ppos);
+}
+
 static int proc_sctp_do_auth(struct ctl_table *ctl, int write,
                             void __user *buffer, size_t *lenp,
                             loff_t *ppos)
@@ -436,20 +460,21 @@ static int proc_sctp_do_auth(struct ctl_table *ctl, int write,
 
 int sctp_sysctl_net_register(struct net *net)
 {
-       struct ctl_table *table = sctp_net_table;
-
-       if (!net_eq(net, &init_net)) {
-               int i;
+       struct ctl_table *table;
+       int i;
 
-               table = kmemdup(sctp_net_table, sizeof(sctp_net_table), GFP_KERNEL);
-               if (!table)
-                       return -ENOMEM;
+       table = kmemdup(sctp_net_table, sizeof(sctp_net_table), GFP_KERNEL);
+       if (!table)
+               return -ENOMEM;
 
-               for (i = 0; table[i].data; i++)
-                       table[i].data += (char *)(&net->sctp) - (char *)&init_net.sctp;
-       }
+       for (i = 0; table[i].data; i++)
+               table[i].data += (char *)(&net->sctp) - (char *)&init_net.sctp;
 
        net->sctp.sysctl_header = register_net_sysctl(net, "net/sctp", table);
+       if (net->sctp.sysctl_header == NULL) {
+               kfree(table);
+               return -ENOMEM;
+       }
        return 0;
 }
 
index 1d348d15b33de4c3b20f7f071442c05058e7dcc3..7dd672fa651f979ae6b93578fc678254a7317b6b 100644 (file)
@@ -72,7 +72,7 @@ static struct sctp_transport *sctp_transport_init(struct net *net,
         */
        peer->rto = msecs_to_jiffies(net->sctp.rto_initial);
 
-       peer->last_time_heard = jiffies;
+       peer->last_time_heard = ktime_get();
        peer->last_time_ecne_reduced = jiffies;
 
        peer->param_flags = SPP_HB_DISABLE |
index 7144eb6a1b95ccad39f9376f723756e0b2c75a2c..d49dc2ed30adb97a809eb37902b9956c366a2862 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/types.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
+#include <net/busy_poll.h>
 #include <net/sctp/structs.h>
 #include <net/sctp/sctp.h>
 #include <net/sctp/sm.h>
@@ -204,6 +205,9 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event)
        if (sock_flag(sk, SOCK_DEAD) || (sk->sk_shutdown & RCV_SHUTDOWN))
                goto out_free;
 
+       if (!sctp_ulpevent_is_notification(event))
+               sk_mark_napi_id(sk, skb);
+
        /* Check if the user wishes to receive this event.  */
        if (!sctp_ulpevent_is_enabled(event, &sctp_sk(sk)->subscribe))
                goto out_free;
index 0a648c502fc35e1f905fdd81667b4553d255932d..2df87f78e518eab4f4fedd37eb38de1ae6c8aaea 100644 (file)
@@ -173,7 +173,8 @@ int csum_partial_copy_to_xdr(struct xdr_buf *xdr, struct sk_buff *skb)
                return -1;
        if (csum_fold(desc.csum))
                return -1;
-       if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE))
+       if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
+           !skb->csum_complete_sw)
                netdev_rx_csum_fault(skb->dev);
        return 0;
 no_checksum:
index 402a7e9a16b7cdf05aa5d23462b3113b1b96c4d3..be8bbd5d65ec6914f6178816556ce31ee7ef0883 100644 (file)
@@ -866,8 +866,6 @@ static void xs_reset_transport(struct sock_xprt *transport)
        xs_restore_old_callbacks(transport, sk);
        write_unlock_bh(&sk->sk_callback_lock);
 
-       sk->sk_no_check = 0;
-
        trace_rpc_socket_close(&transport->xprt, sock);
        sock_release(sock);
 }
@@ -2046,7 +2044,6 @@ static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
                sk->sk_user_data = xprt;
                sk->sk_data_ready = xs_udp_data_ready;
                sk->sk_write_space = xs_udp_write_space;
-               sk->sk_no_check = UDP_CSUM_NORCV;
                sk->sk_allocation = GFP_ATOMIC;
 
                xprt_set_connected(xprt);
index b282f7130d2bb51f0dee12e4800a9a0ad54a33a7..a080c66d819a032233a963512d849f757cc979e2 100644 (file)
@@ -5,7 +5,7 @@
 obj-$(CONFIG_TIPC) := tipc.o
 
 tipc-y += addr.o bcast.o bearer.o config.o \
-          core.o handler.o link.o discover.o msg.o  \
+          core.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 server.o
index 95ab5ef92920fddf34c02478973b98ebbe96ba59..26631679a1faa4bb7b8c720dda1e48b5c68ab40e 100644 (file)
@@ -71,7 +71,7 @@ struct tipc_bcbearer_pair {
  * Note: The fields labelled "temporary" are incorporated into the bearer
  * to avoid consuming potentially limited stack space through the use of
  * large local variables within multicast routines.  Concurrent access is
- * prevented through use of the spinlock "bc_lock".
+ * prevented through use of the spinlock "bclink_lock".
  */
 struct tipc_bcbearer {
        struct tipc_bearer bearer;
@@ -84,34 +84,64 @@ struct tipc_bcbearer {
 
 /**
  * struct tipc_bclink - link used for broadcast messages
+ * @lock: spinlock governing access to structure
  * @link: (non-standard) broadcast link structure
  * @node: (non-standard) node structure representing b'cast link's peer node
+ * @flags: represent bclink states
  * @bcast_nodes: map of broadcast-capable nodes
  * @retransmit_to: node that most recently requested a retransmit
  *
  * Handles sequence numbering, fragmentation, bundling, etc.
  */
 struct tipc_bclink {
+       spinlock_t lock;
        struct tipc_link link;
        struct tipc_node node;
+       unsigned int flags;
        struct tipc_node_map bcast_nodes;
        struct tipc_node *retransmit_to;
 };
 
-static struct tipc_bcbearer bcast_bearer;
-static struct tipc_bclink bcast_link;
-
-static struct tipc_bcbearer *bcbearer = &bcast_bearer;
-static struct tipc_bclink *bclink = &bcast_link;
-static struct tipc_link *bcl = &bcast_link.link;
-
-static DEFINE_SPINLOCK(bc_lock);
+static struct tipc_bcbearer *bcbearer;
+static struct tipc_bclink *bclink;
+static struct tipc_link *bcl;
 
 const char tipc_bclink_name[] = "broadcast-link";
 
 static void tipc_nmap_diff(struct tipc_node_map *nm_a,
                           struct tipc_node_map *nm_b,
                           struct tipc_node_map *nm_diff);
+static void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node);
+static void tipc_nmap_remove(struct tipc_node_map *nm_ptr, u32 node);
+
+static void tipc_bclink_lock(void)
+{
+       spin_lock_bh(&bclink->lock);
+}
+
+static void tipc_bclink_unlock(void)
+{
+       struct tipc_node *node = NULL;
+
+       if (likely(!bclink->flags)) {
+               spin_unlock_bh(&bclink->lock);
+               return;
+       }
+
+       if (bclink->flags & TIPC_BCLINK_RESET) {
+               bclink->flags &= ~TIPC_BCLINK_RESET;
+               node = tipc_bclink_retransmit_to();
+       }
+       spin_unlock_bh(&bclink->lock);
+
+       if (node)
+               tipc_link_reset_all(node);
+}
+
+void tipc_bclink_set_flags(unsigned int flags)
+{
+       bclink->flags |= flags;
+}
 
 static u32 bcbuf_acks(struct sk_buff *buf)
 {
@@ -130,16 +160,16 @@ static void bcbuf_decr_acks(struct sk_buff *buf)
 
 void tipc_bclink_add_node(u32 addr)
 {
-       spin_lock_bh(&bc_lock);
+       tipc_bclink_lock();
        tipc_nmap_add(&bclink->bcast_nodes, addr);
-       spin_unlock_bh(&bc_lock);
+       tipc_bclink_unlock();
 }
 
 void tipc_bclink_remove_node(u32 addr)
 {
-       spin_lock_bh(&bc_lock);
+       tipc_bclink_lock();
        tipc_nmap_remove(&bclink->bcast_nodes, addr);
-       spin_unlock_bh(&bc_lock);
+       tipc_bclink_unlock();
 }
 
 static void bclink_set_last_sent(void)
@@ -165,7 +195,7 @@ static void bclink_update_last_sent(struct tipc_node *node, u32 seqno)
 /**
  * tipc_bclink_retransmit_to - get most recent node to request retransmission
  *
- * Called with bc_lock locked
+ * Called with bclink_lock locked
  */
 struct tipc_node *tipc_bclink_retransmit_to(void)
 {
@@ -177,7 +207,7 @@ struct tipc_node *tipc_bclink_retransmit_to(void)
  * @after: sequence number of last packet to *not* retransmit
  * @to: sequence number of last packet to retransmit
  *
- * Called with bc_lock locked
+ * Called with bclink_lock locked
  */
 static void bclink_retransmit_pkt(u32 after, u32 to)
 {
@@ -194,7 +224,7 @@ static void bclink_retransmit_pkt(u32 after, u32 to)
  * @n_ptr: node that sent acknowledgement info
  * @acked: broadcast sequence # that has been acknowledged
  *
- * Node is locked, bc_lock unlocked.
+ * Node is locked, bclink_lock unlocked.
  */
 void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked)
 {
@@ -202,8 +232,7 @@ void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked)
        struct sk_buff *next;
        unsigned int released = 0;
 
-       spin_lock_bh(&bc_lock);
-
+       tipc_bclink_lock();
        /* Bail out if tx queue is empty (no clean up is required) */
        crs = bcl->first_out;
        if (!crs)
@@ -267,13 +296,13 @@ void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked)
        if (unlikely(released && !list_empty(&bcl->waiting_ports)))
                tipc_link_wakeup_ports(bcl, 0);
 exit:
-       spin_unlock_bh(&bc_lock);
+       tipc_bclink_unlock();
 }
 
 /**
  * tipc_bclink_update_link_state - update broadcast link state
  *
- * tipc_net_lock and node lock set
+ * RCU and node lock set
  */
 void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent)
 {
@@ -320,10 +349,10 @@ void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent)
                                 ? buf_seqno(n_ptr->bclink.deferred_head) - 1
                                 : n_ptr->bclink.last_sent);
 
-               spin_lock_bh(&bc_lock);
-               tipc_bearer_send(&bcbearer->bearer, buf, NULL);
+               tipc_bclink_lock();
+               tipc_bearer_send(MAX_BEARERS, buf, NULL);
                bcl->stats.sent_nacks++;
-               spin_unlock_bh(&bc_lock);
+               tipc_bclink_unlock();
                kfree_skb(buf);
 
                n_ptr->bclink.oos_state++;
@@ -335,8 +364,6 @@ void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent)
  *
  * Delay any upcoming NACK by this node if another node has already
  * requested the first message this node is going to ask for.
- *
- * Only tipc_net_lock set.
  */
 static void bclink_peek_nack(struct tipc_msg *msg)
 {
@@ -362,7 +389,7 @@ int tipc_bclink_xmit(struct sk_buff *buf)
 {
        int res;
 
-       spin_lock_bh(&bc_lock);
+       tipc_bclink_lock();
 
        if (!bclink->bcast_nodes.count) {
                res = msg_data_sz(buf_msg(buf));
@@ -377,14 +404,14 @@ int tipc_bclink_xmit(struct sk_buff *buf)
                bcl->stats.accu_queue_sz += bcl->out_queue_size;
        }
 exit:
-       spin_unlock_bh(&bc_lock);
+       tipc_bclink_unlock();
        return res;
 }
 
 /**
  * bclink_accept_pkt - accept an incoming, in-sequence broadcast packet
  *
- * Called with both sending node's lock and bc_lock taken.
+ * Called with both sending node's lock and bclink_lock taken.
  */
 static void bclink_accept_pkt(struct tipc_node *node, u32 seqno)
 {
@@ -408,7 +435,7 @@ static void bclink_accept_pkt(struct tipc_node *node, u32 seqno)
 /**
  * tipc_bclink_rcv - receive a broadcast packet, and deliver upwards
  *
- * tipc_net_lock is read_locked, no other locks set
+ * RCU is locked, no other locks set
  */
 void tipc_bclink_rcv(struct sk_buff *buf)
 {
@@ -439,12 +466,12 @@ void tipc_bclink_rcv(struct sk_buff *buf)
                if (msg_destnode(msg) == tipc_own_addr) {
                        tipc_bclink_acknowledge(node, msg_bcast_ack(msg));
                        tipc_node_unlock(node);
-                       spin_lock_bh(&bc_lock);
+                       tipc_bclink_lock();
                        bcl->stats.recv_nacks++;
                        bclink->retransmit_to = node;
                        bclink_retransmit_pkt(msg_bcgap_after(msg),
                                              msg_bcgap_to(msg));
-                       spin_unlock_bh(&bc_lock);
+                       tipc_bclink_unlock();
                } else {
                        tipc_node_unlock(node);
                        bclink_peek_nack(msg);
@@ -462,51 +489,47 @@ receive:
                /* Deliver message to destination */
 
                if (likely(msg_isdata(msg))) {
-                       spin_lock_bh(&bc_lock);
+                       tipc_bclink_lock();
                        bclink_accept_pkt(node, seqno);
-                       spin_unlock_bh(&bc_lock);
+                       tipc_bclink_unlock();
                        tipc_node_unlock(node);
                        if (likely(msg_mcast(msg)))
                                tipc_port_mcast_rcv(buf, NULL);
                        else
                                kfree_skb(buf);
                } else if (msg_user(msg) == MSG_BUNDLER) {
-                       spin_lock_bh(&bc_lock);
+                       tipc_bclink_lock();
                        bclink_accept_pkt(node, seqno);
                        bcl->stats.recv_bundles++;
                        bcl->stats.recv_bundled += msg_msgcnt(msg);
-                       spin_unlock_bh(&bc_lock);
+                       tipc_bclink_unlock();
                        tipc_node_unlock(node);
                        tipc_link_bundle_rcv(buf);
                } else if (msg_user(msg) == MSG_FRAGMENTER) {
-                       int ret;
-                       ret = tipc_link_frag_rcv(&node->bclink.reasm_head,
-                                                &node->bclink.reasm_tail,
-                                                &buf);
-                       if (ret == LINK_REASM_ERROR)
+                       tipc_buf_append(&node->bclink.reasm_buf, &buf);
+                       if (unlikely(!buf && !node->bclink.reasm_buf))
                                goto unlock;
-                       spin_lock_bh(&bc_lock);
+                       tipc_bclink_lock();
                        bclink_accept_pkt(node, seqno);
                        bcl->stats.recv_fragments++;
-                       if (ret == LINK_REASM_COMPLETE) {
+                       if (buf) {
                                bcl->stats.recv_fragmented++;
-                               /* Point msg to inner header */
                                msg = buf_msg(buf);
-                               spin_unlock_bh(&bc_lock);
+                               tipc_bclink_unlock();
                                goto receive;
                        }
-                       spin_unlock_bh(&bc_lock);
+                       tipc_bclink_unlock();
                        tipc_node_unlock(node);
                } else if (msg_user(msg) == NAME_DISTRIBUTOR) {
-                       spin_lock_bh(&bc_lock);
+                       tipc_bclink_lock();
                        bclink_accept_pkt(node, seqno);
-                       spin_unlock_bh(&bc_lock);
+                       tipc_bclink_unlock();
                        tipc_node_unlock(node);
                        tipc_named_rcv(buf);
                } else {
-                       spin_lock_bh(&bc_lock);
+                       tipc_bclink_lock();
                        bclink_accept_pkt(node, seqno);
-                       spin_unlock_bh(&bc_lock);
+                       tipc_bclink_unlock();
                        tipc_node_unlock(node);
                        kfree_skb(buf);
                }
@@ -552,14 +575,14 @@ receive:
        } else
                deferred = 0;
 
-       spin_lock_bh(&bc_lock);
+       tipc_bclink_lock();
 
        if (deferred)
                bcl->stats.deferred_recv++;
        else
                bcl->stats.duplicates++;
 
-       spin_unlock_bh(&bc_lock);
+       tipc_bclink_unlock();
 
 unlock:
        tipc_node_unlock(node);
@@ -627,13 +650,13 @@ static int tipc_bcbearer_send(struct sk_buff *buf, struct tipc_bearer *unused1,
 
                if (bp_index == 0) {
                        /* Use original buffer for first bearer */
-                       tipc_bearer_send(b, buf, &b->bcast_addr);
+                       tipc_bearer_send(b->identity, buf, &b->bcast_addr);
                } else {
                        /* Avoid concurrent buffer access */
-                       tbuf = pskb_copy(buf, GFP_ATOMIC);
+                       tbuf = pskb_copy_for_clone(buf, GFP_ATOMIC);
                        if (!tbuf)
                                break;
-                       tipc_bearer_send(b, tbuf, &b->bcast_addr);
+                       tipc_bearer_send(b->identity, tbuf, &b->bcast_addr);
                        kfree_skb(tbuf); /* Bearer keeps a clone */
                }
 
@@ -655,20 +678,27 @@ static int tipc_bcbearer_send(struct sk_buff *buf, struct tipc_bearer *unused1,
 /**
  * tipc_bcbearer_sort - create sets of bearer pairs used by broadcast bearer
  */
-void tipc_bcbearer_sort(void)
+void tipc_bcbearer_sort(struct tipc_node_map *nm_ptr, u32 node, bool action)
 {
        struct tipc_bcbearer_pair *bp_temp = bcbearer->bpairs_temp;
        struct tipc_bcbearer_pair *bp_curr;
+       struct tipc_bearer *b;
        int b_index;
        int pri;
 
-       spin_lock_bh(&bc_lock);
+       tipc_bclink_lock();
+
+       if (action)
+               tipc_nmap_add(nm_ptr, node);
+       else
+               tipc_nmap_remove(nm_ptr, node);
 
        /* Group bearers by priority (can assume max of two per priority) */
        memset(bp_temp, 0, sizeof(bcbearer->bpairs_temp));
 
+       rcu_read_lock();
        for (b_index = 0; b_index < MAX_BEARERS; b_index++) {
-               struct tipc_bearer *b = bearer_list[b_index];
+               b = rcu_dereference_rtnl(bearer_list[b_index]);
                if (!b || !b->nodes.count)
                        continue;
 
@@ -677,6 +707,7 @@ void tipc_bcbearer_sort(void)
                else
                        bp_temp[b->priority].secondary = b;
        }
+       rcu_read_unlock();
 
        /* Create array of bearer pairs for broadcasting */
        bp_curr = bcbearer->bpairs;
@@ -702,7 +733,7 @@ void tipc_bcbearer_sort(void)
                bp_curr++;
        }
 
-       spin_unlock_bh(&bc_lock);
+       tipc_bclink_unlock();
 }
 
 
@@ -714,7 +745,7 @@ int tipc_bclink_stats(char *buf, const u32 buf_size)
        if (!bcl)
                return 0;
 
-       spin_lock_bh(&bc_lock);
+       tipc_bclink_lock();
 
        s = &bcl->stats;
 
@@ -743,7 +774,7 @@ int tipc_bclink_stats(char *buf, const u32 buf_size)
                             s->queue_sz_counts ?
                             (s->accu_queue_sz / s->queue_sz_counts) : 0);
 
-       spin_unlock_bh(&bc_lock);
+       tipc_bclink_unlock();
        return ret;
 }
 
@@ -752,9 +783,9 @@ int tipc_bclink_reset_stats(void)
        if (!bcl)
                return -ENOPROTOOPT;
 
-       spin_lock_bh(&bc_lock);
+       tipc_bclink_lock();
        memset(&bcl->stats, 0, sizeof(bcl->stats));
-       spin_unlock_bh(&bc_lock);
+       tipc_bclink_unlock();
        return 0;
 }
 
@@ -765,46 +796,59 @@ int tipc_bclink_set_queue_limits(u32 limit)
        if ((limit < TIPC_MIN_LINK_WIN) || (limit > TIPC_MAX_LINK_WIN))
                return -EINVAL;
 
-       spin_lock_bh(&bc_lock);
+       tipc_bclink_lock();
        tipc_link_set_queue_limits(bcl, limit);
-       spin_unlock_bh(&bc_lock);
+       tipc_bclink_unlock();
        return 0;
 }
 
-void tipc_bclink_init(void)
+int tipc_bclink_init(void)
 {
+       bcbearer = kzalloc(sizeof(*bcbearer), GFP_ATOMIC);
+       if (!bcbearer)
+               return -ENOMEM;
+
+       bclink = kzalloc(sizeof(*bclink), GFP_ATOMIC);
+       if (!bclink) {
+               kfree(bcbearer);
+               return -ENOMEM;
+       }
+
+       bcl = &bclink->link;
        bcbearer->bearer.media = &bcbearer->media;
        bcbearer->media.send_msg = tipc_bcbearer_send;
        sprintf(bcbearer->media.name, "tipc-broadcast");
 
+       spin_lock_init(&bclink->lock);
        INIT_LIST_HEAD(&bcl->waiting_ports);
        bcl->next_out_no = 1;
        spin_lock_init(&bclink->node.lock);
        bcl->owner = &bclink->node;
        bcl->max_pkt = MAX_PKT_DEFAULT_MCAST;
        tipc_link_set_queue_limits(bcl, BCLINK_WIN_DEFAULT);
-       bcl->b_ptr = &bcbearer->bearer;
-       bearer_list[BCBEARER] = &bcbearer->bearer;
+       bcl->bearer_id = MAX_BEARERS;
+       rcu_assign_pointer(bearer_list[MAX_BEARERS], &bcbearer->bearer);
        bcl->state = WORKING_WORKING;
        strlcpy(bcl->name, tipc_bclink_name, TIPC_MAX_LINK_NAME);
+       return 0;
 }
 
 void tipc_bclink_stop(void)
 {
-       spin_lock_bh(&bc_lock);
+       tipc_bclink_lock();
        tipc_link_purge_queues(bcl);
-       spin_unlock_bh(&bc_lock);
+       tipc_bclink_unlock();
 
-       bearer_list[BCBEARER] = NULL;
-       memset(bclink, 0, sizeof(*bclink));
-       memset(bcbearer, 0, sizeof(*bcbearer));
+       RCU_INIT_POINTER(bearer_list[BCBEARER], NULL);
+       synchronize_net();
+       kfree(bcbearer);
+       kfree(bclink);
 }
 
-
 /**
  * tipc_nmap_add - add a node to a node map
  */
-void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node)
+static void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node)
 {
        int n = tipc_node(node);
        int w = n / WSIZE;
@@ -819,7 +863,7 @@ void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node)
 /**
  * tipc_nmap_remove - remove a node from a node map
  */
-void tipc_nmap_remove(struct tipc_node_map *nm_ptr, u32 node)
+static void tipc_nmap_remove(struct tipc_node_map *nm_ptr, u32 node)
 {
        int n = tipc_node(node);
        int w = n / WSIZE;
index a80ef54b818e221a98bd9bd69a3adf28d0001f88..00330c45df3e04d03626a31d5f2ee6ba66569298 100644 (file)
@@ -39,6 +39,7 @@
 
 #define MAX_NODES 4096
 #define WSIZE 32
+#define TIPC_BCLINK_RESET 1
 
 /**
  * struct tipc_node_map - set of node identifiers
@@ -69,9 +70,6 @@ struct tipc_node;
 
 extern const char tipc_bclink_name[];
 
-void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node);
-void tipc_nmap_remove(struct tipc_node_map *nm_ptr, u32 node);
-
 /**
  * tipc_nmap_equal - test for equality of node maps
  */
@@ -84,8 +82,9 @@ static inline int tipc_nmap_equal(struct tipc_node_map *nm_a,
 void tipc_port_list_add(struct tipc_port_list *pl_ptr, u32 port);
 void tipc_port_list_free(struct tipc_port_list *pl_ptr);
 
-void tipc_bclink_init(void);
+int tipc_bclink_init(void);
 void tipc_bclink_stop(void);
+void tipc_bclink_set_flags(unsigned int flags);
 void tipc_bclink_add_node(u32 addr);
 void tipc_bclink_remove_node(u32 addr);
 struct tipc_node *tipc_bclink_retransmit_to(void);
@@ -98,6 +97,6 @@ void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent);
 int  tipc_bclink_stats(char *stats_buf, const u32 buf_size);
 int  tipc_bclink_reset_stats(void);
 int  tipc_bclink_set_queue_limits(u32 limit);
-void tipc_bcbearer_sort(void);
+void tipc_bcbearer_sort(struct tipc_node_map *nm_ptr, u32 node, bool action);
 
 #endif
index 3fef7eb776dc12934654b2bfa7500cfa75138ad4..264474394f9f75994205b751e734b632d21fbc02 100644 (file)
@@ -49,7 +49,7 @@ static struct tipc_media * const media_info_array[] = {
        NULL
 };
 
-struct tipc_bearer *bearer_list[MAX_BEARERS + 1];
+struct tipc_bearer __rcu *bearer_list[MAX_BEARERS + 1];
 
 static void bearer_disable(struct tipc_bearer *b_ptr, bool shutting_down);
 
@@ -178,7 +178,7 @@ struct tipc_bearer *tipc_bearer_find(const char *name)
        u32 i;
 
        for (i = 0; i < MAX_BEARERS; i++) {
-               b_ptr = bearer_list[i];
+               b_ptr = rtnl_dereference(bearer_list[i]);
                if (b_ptr && (!strcmp(b_ptr->name, name)))
                        return b_ptr;
        }
@@ -198,10 +198,9 @@ struct sk_buff *tipc_bearer_get_names(void)
        if (!buf)
                return NULL;
 
-       read_lock_bh(&tipc_net_lock);
        for (i = 0; media_info_array[i] != NULL; i++) {
                for (j = 0; j < MAX_BEARERS; j++) {
-                       b = bearer_list[j];
+                       b = rtnl_dereference(bearer_list[j]);
                        if (!b)
                                continue;
                        if (b->media == media_info_array[i]) {
@@ -211,22 +210,33 @@ struct sk_buff *tipc_bearer_get_names(void)
                        }
                }
        }
-       read_unlock_bh(&tipc_net_lock);
        return buf;
 }
 
-void tipc_bearer_add_dest(struct tipc_bearer *b_ptr, u32 dest)
+void tipc_bearer_add_dest(u32 bearer_id, u32 dest)
 {
-       tipc_nmap_add(&b_ptr->nodes, dest);
-       tipc_bcbearer_sort();
-       tipc_disc_add_dest(b_ptr->link_req);
+       struct tipc_bearer *b_ptr;
+
+       rcu_read_lock();
+       b_ptr = rcu_dereference_rtnl(bearer_list[bearer_id]);
+       if (b_ptr) {
+               tipc_bcbearer_sort(&b_ptr->nodes, dest, true);
+               tipc_disc_add_dest(b_ptr->link_req);
+       }
+       rcu_read_unlock();
 }
 
-void tipc_bearer_remove_dest(struct tipc_bearer *b_ptr, u32 dest)
+void tipc_bearer_remove_dest(u32 bearer_id, u32 dest)
 {
-       tipc_nmap_remove(&b_ptr->nodes, dest);
-       tipc_bcbearer_sort();
-       tipc_disc_remove_dest(b_ptr->link_req);
+       struct tipc_bearer *b_ptr;
+
+       rcu_read_lock();
+       b_ptr = rcu_dereference_rtnl(bearer_list[bearer_id]);
+       if (b_ptr) {
+               tipc_bcbearer_sort(&b_ptr->nodes, dest, false);
+               tipc_disc_remove_dest(b_ptr->link_req);
+       }
+       rcu_read_unlock();
 }
 
 /**
@@ -271,13 +281,11 @@ int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority)
                return -EINVAL;
        }
 
-       write_lock_bh(&tipc_net_lock);
-
        m_ptr = tipc_media_find(b_names.media_name);
        if (!m_ptr) {
                pr_warn("Bearer <%s> rejected, media <%s> not registered\n",
                        name, b_names.media_name);
-               goto exit;
+               return -EINVAL;
        }
 
        if (priority == TIPC_MEDIA_LINK_PRI)
@@ -287,7 +295,7 @@ restart:
        bearer_id = MAX_BEARERS;
        with_this_prio = 1;
        for (i = MAX_BEARERS; i-- != 0; ) {
-               b_ptr = bearer_list[i];
+               b_ptr = rtnl_dereference(bearer_list[i]);
                if (!b_ptr) {
                        bearer_id = i;
                        continue;
@@ -295,14 +303,14 @@ restart:
                if (!strcmp(name, b_ptr->name)) {
                        pr_warn("Bearer <%s> rejected, already enabled\n",
                                name);
-                       goto exit;
+                       return -EINVAL;
                }
                if ((b_ptr->priority == priority) &&
                    (++with_this_prio > 2)) {
                        if (priority-- == 0) {
                                pr_warn("Bearer <%s> rejected, duplicate priority\n",
                                        name);
-                               goto exit;
+                               return -EINVAL;
                        }
                        pr_warn("Bearer <%s> priority adjustment required %u->%u\n",
                                name, priority + 1, priority);
@@ -312,21 +320,20 @@ restart:
        if (bearer_id >= MAX_BEARERS) {
                pr_warn("Bearer <%s> rejected, bearer limit reached (%u)\n",
                        name, MAX_BEARERS);
-               goto exit;
+               return -EINVAL;
        }
 
        b_ptr = kzalloc(sizeof(*b_ptr), GFP_ATOMIC);
-       if (!b_ptr) {
-               res = -ENOMEM;
-               goto exit;
-       }
+       if (!b_ptr)
+               return -ENOMEM;
+
        strcpy(b_ptr->name, name);
        b_ptr->media = m_ptr;
        res = m_ptr->enable_media(b_ptr);
        if (res) {
                pr_warn("Bearer <%s> rejected, enable failure (%d)\n",
                        name, -res);
-               goto exit;
+               return -EINVAL;
        }
 
        b_ptr->identity = bearer_id;
@@ -341,16 +348,14 @@ restart:
                bearer_disable(b_ptr, false);
                pr_warn("Bearer <%s> rejected, discovery object creation failed\n",
                        name);
-               goto exit;
+               return -EINVAL;
        }
 
-       bearer_list[bearer_id] = b_ptr;
+       rcu_assign_pointer(bearer_list[bearer_id], b_ptr);
 
        pr_info("Enabled bearer <%s>, discovery domain %s, priority %u\n",
                name,
                tipc_addr_string_fill(addr_string, disc_domain), priority);
-exit:
-       write_unlock_bh(&tipc_net_lock);
        return res;
 }
 
@@ -359,19 +364,16 @@ exit:
  */
 static int tipc_reset_bearer(struct tipc_bearer *b_ptr)
 {
-       read_lock_bh(&tipc_net_lock);
        pr_info("Resetting bearer <%s>\n", b_ptr->name);
-       tipc_disc_delete(b_ptr->link_req);
        tipc_link_reset_list(b_ptr->identity);
-       tipc_disc_create(b_ptr, &b_ptr->bcast_addr);
-       read_unlock_bh(&tipc_net_lock);
+       tipc_disc_reset(b_ptr);
        return 0;
 }
 
 /**
  * bearer_disable
  *
- * Note: This routine assumes caller holds tipc_net_lock.
+ * Note: This routine assumes caller holds RTNL lock.
  */
 static void bearer_disable(struct tipc_bearer *b_ptr, bool shutting_down)
 {
@@ -385,12 +387,12 @@ static void bearer_disable(struct tipc_bearer *b_ptr, bool shutting_down)
                tipc_disc_delete(b_ptr->link_req);
 
        for (i = 0; i < MAX_BEARERS; i++) {
-               if (b_ptr == bearer_list[i]) {
-                       bearer_list[i] = NULL;
+               if (b_ptr == rtnl_dereference(bearer_list[i])) {
+                       RCU_INIT_POINTER(bearer_list[i], NULL);
                        break;
                }
        }
-       kfree(b_ptr);
+       kfree_rcu(b_ptr, rcu);
 }
 
 int tipc_disable_bearer(const char *name)
@@ -398,7 +400,6 @@ int tipc_disable_bearer(const char *name)
        struct tipc_bearer *b_ptr;
        int res;
 
-       write_lock_bh(&tipc_net_lock);
        b_ptr = tipc_bearer_find(name);
        if (b_ptr == NULL) {
                pr_warn("Attempt to disable unknown bearer <%s>\n", name);
@@ -407,32 +408,9 @@ int tipc_disable_bearer(const char *name)
                bearer_disable(b_ptr, false);
                res = 0;
        }
-       write_unlock_bh(&tipc_net_lock);
        return res;
 }
 
-
-/* tipc_l2_media_addr_set - initialize Ethernet media address structure
- *
- * Media-dependent "value" field stores MAC address in first 6 bytes
- * and zeroes out the remaining bytes.
- */
-void tipc_l2_media_addr_set(const struct tipc_bearer *b,
-                           struct tipc_media_addr *a, char *mac)
-{
-       int len = b->media->hwaddr_len;
-
-       if (unlikely(sizeof(a->value) < len)) {
-               WARN_ONCE(1, "Media length invalid\n");
-               return;
-       }
-
-       memcpy(a->value, mac, len);
-       memset(a->value + len, 0, sizeof(a->value) - len);
-       a->media_id = b->media->type_id;
-       a->broadcast = !memcmp(mac, b->bcast_addr.value, len);
-}
-
 int tipc_enable_l2_media(struct tipc_bearer *b)
 {
        struct net_device *dev;
@@ -443,33 +421,37 @@ int tipc_enable_l2_media(struct tipc_bearer *b)
        if (!dev)
                return -ENODEV;
 
-       /* Associate TIPC bearer with Ethernet bearer */
-       b->media_ptr = dev;
-       memset(b->bcast_addr.value, 0, sizeof(b->bcast_addr.value));
+       /* Associate TIPC bearer with L2 bearer */
+       rcu_assign_pointer(b->media_ptr, dev);
+       memset(&b->bcast_addr, 0, sizeof(b->bcast_addr));
        memcpy(b->bcast_addr.value, dev->broadcast, b->media->hwaddr_len);
        b->bcast_addr.media_id = b->media->type_id;
        b->bcast_addr.broadcast = 1;
        b->mtu = dev->mtu;
-       tipc_l2_media_addr_set(b, &b->addr, (char *)dev->dev_addr);
+       b->media->raw2addr(b, &b->addr, (char *)dev->dev_addr);
        rcu_assign_pointer(dev->tipc_ptr, b);
        return 0;
 }
 
-/* tipc_disable_l2_media - detach TIPC bearer from an Ethernet interface
+/* tipc_disable_l2_media - detach TIPC bearer from an L2 interface
  *
- * Mark Ethernet bearer as inactive so that incoming buffers are thrown away,
+ * Mark L2 bearer as inactive so that incoming buffers are thrown away,
  * then get worker thread to complete bearer cleanup.  (Can't do cleanup
  * here because cleanup code needs to sleep and caller holds spinlocks.)
  */
 void tipc_disable_l2_media(struct tipc_bearer *b)
 {
-       struct net_device *dev = (struct net_device *)b->media_ptr;
+       struct net_device *dev;
+
+       dev = (struct net_device *)rtnl_dereference(b->media_ptr);
+       RCU_INIT_POINTER(b->media_ptr, NULL);
        RCU_INIT_POINTER(dev->tipc_ptr, NULL);
+       synchronize_net();
        dev_put(dev);
 }
 
 /**
- * tipc_l2_send_msg - send a TIPC packet out over an Ethernet interface
+ * tipc_l2_send_msg - send a TIPC packet out over an L2 interface
  * @buf: the packet to be sent
  * @b_ptr: the bearer through which the packet is to be sent
  * @dest: peer destination address
@@ -478,8 +460,12 @@ int tipc_l2_send_msg(struct sk_buff *buf, struct tipc_bearer *b,
                     struct tipc_media_addr *dest)
 {
        struct sk_buff *clone;
+       struct net_device *dev;
        int delta;
-       struct net_device *dev = (struct net_device *)b->media_ptr;
+
+       dev = (struct net_device *)rcu_dereference_rtnl(b->media_ptr);
+       if (!dev)
+               return 0;
 
        clone = skb_clone(buf, GFP_ATOMIC);
        if (!clone)
@@ -507,10 +493,16 @@ int tipc_l2_send_msg(struct sk_buff *buf, struct tipc_bearer *b,
  * The media send routine must not alter the buffer being passed in
  * as it may be needed for later retransmission!
  */
-void tipc_bearer_send(struct tipc_bearer *b, struct sk_buff *buf,
+void tipc_bearer_send(u32 bearer_id, struct sk_buff *buf,
                      struct tipc_media_addr *dest)
 {
-       b->media->send_msg(buf, b, dest);
+       struct tipc_bearer *b_ptr;
+
+       rcu_read_lock();
+       b_ptr = rcu_dereference_rtnl(bearer_list[bearer_id]);
+       if (likely(b_ptr))
+               b_ptr->media->send_msg(buf, b_ptr, dest);
+       rcu_read_unlock();
 }
 
 /**
@@ -535,7 +527,7 @@ static int tipc_l2_rcv_msg(struct sk_buff *buf, struct net_device *dev,
        }
 
        rcu_read_lock();
-       b_ptr = rcu_dereference(dev->tipc_ptr);
+       b_ptr = rcu_dereference_rtnl(dev->tipc_ptr);
        if (likely(b_ptr)) {
                if (likely(buf->pkt_type <= PACKET_BROADCAST)) {
                        buf->next = NULL;
@@ -568,12 +560,9 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt,
        if (!net_eq(dev_net(dev), &init_net))
                return NOTIFY_DONE;
 
-       rcu_read_lock();
-       b_ptr = rcu_dereference(dev->tipc_ptr);
-       if (!b_ptr) {
-               rcu_read_unlock();
+       b_ptr = rtnl_dereference(dev->tipc_ptr);
+       if (!b_ptr)
                return NOTIFY_DONE;
-       }
 
        b_ptr->mtu = dev->mtu;
 
@@ -586,17 +575,15 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt,
                tipc_reset_bearer(b_ptr);
                break;
        case NETDEV_CHANGEADDR:
-               tipc_l2_media_addr_set(b_ptr, &b_ptr->addr,
+               b_ptr->media->raw2addr(b_ptr, &b_ptr->addr,
                                       (char *)dev->dev_addr);
                tipc_reset_bearer(b_ptr);
                break;
        case NETDEV_UNREGISTER:
        case NETDEV_CHANGENAME:
-               tipc_disable_bearer(b_ptr->name);
+               bearer_disable(b_ptr, false);
                break;
        }
-       rcu_read_unlock();
-
        return NOTIFY_OK;
 }
 
@@ -633,7 +620,7 @@ void tipc_bearer_stop(void)
        u32 i;
 
        for (i = 0; i < MAX_BEARERS; i++) {
-               b_ptr = bearer_list[i];
+               b_ptr = rtnl_dereference(bearer_list[i]);
                if (b_ptr) {
                        bearer_disable(b_ptr, true);
                        bearer_list[i] = NULL;
index ba48145e871dd8dcd357a193b61e9234e5ad7f0d..78fccc49de23c1e6dc20c1799d629c4083f6f80c 100644 (file)
 #define MAX_BEARERS    2
 #define MAX_MEDIA      2
 
-/*
- * Identifiers associated with TIPC message header media address info
- *
- * - address info field is 20 bytes long
- * - media type identifier located at offset 3
- * - remaining bytes vary according to media type
+/* Identifiers associated with TIPC message header media address info
+ * - address info field is 32 bytes long
+ * - the field's actual content and length is defined per media
+ * - remaining unused bytes in the field are set to zero
  */
-#define TIPC_MEDIA_ADDR_SIZE   20
+#define TIPC_MEDIA_ADDR_SIZE   32
 #define TIPC_MEDIA_TYPE_OFFSET 3
 
 /*
@@ -77,9 +75,10 @@ struct tipc_bearer;
  * @send_msg: routine which handles buffer transmission
  * @enable_media: routine which enables a media
  * @disable_media: routine which disables a media
- * @addr2str: routine which converts media address to string
- * @addr2msg: routine which converts media address to protocol message area
- * @msg2addr: routine which converts media address from protocol message area
+ * @addr2str: convert media address format to string
+ * @addr2msg: convert from media addr format to discovery msg addr format
+ * @msg2addr: convert from discovery msg addr format to media addr format
+ * @raw2addr: convert from raw addr format to media addr format
  * @priority: default link (and bearer) priority
  * @tolerance: default time (in ms) before declaring link failure
  * @window: default window (in packets) before declaring link congestion
@@ -93,10 +92,16 @@ struct tipc_media {
                        struct tipc_media_addr *dest);
        int (*enable_media)(struct tipc_bearer *b_ptr);
        void (*disable_media)(struct tipc_bearer *b_ptr);
-       int (*addr2str)(struct tipc_media_addr *a, char *str_buf, int str_size);
-       int (*addr2msg)(struct tipc_media_addr *a, char *msg_area);
-       int (*msg2addr)(const struct tipc_bearer *b_ptr,
-                       struct tipc_media_addr *a, char *msg_area);
+       int (*addr2str)(struct tipc_media_addr *addr,
+                       char *strbuf,
+                       int bufsz);
+       int (*addr2msg)(char *msg, struct tipc_media_addr *addr);
+       int (*msg2addr)(struct tipc_bearer *b,
+                       struct tipc_media_addr *addr,
+                       char *msg);
+       int (*raw2addr)(struct tipc_bearer *b,
+                       struct tipc_media_addr *addr,
+                       char *raw);
        u32 priority;
        u32 tolerance;
        u32 window;
@@ -113,6 +118,7 @@ struct tipc_media {
  * @name: bearer name (format = media:interface)
  * @media: ptr to media structure associated with bearer
  * @bcast_addr: media address used in broadcasting
+ * @rcu: rcu struct for tipc_bearer
  * @priority: default link priority for bearer
  * @window: default window size for bearer
  * @tolerance: default link tolerance for bearer
@@ -127,12 +133,13 @@ struct tipc_media {
  * care of initializing all other fields.
  */
 struct tipc_bearer {
-       void *media_ptr;                        /* initalized by media */
+       void __rcu *media_ptr;                  /* initalized by media */
        u32 mtu;                                /* initalized by media */
        struct tipc_media_addr addr;            /* initalized by media */
        char name[TIPC_MAX_BEARER_NAME];
        struct tipc_media *media;
        struct tipc_media_addr bcast_addr;
+       struct rcu_head rcu;
        u32 priority;
        u32 window;
        u32 tolerance;
@@ -150,7 +157,7 @@ struct tipc_bearer_names {
 
 struct tipc_link;
 
-extern struct tipc_bearer *bearer_list[];
+extern struct tipc_bearer __rcu *bearer_list[];
 
 /*
  * TIPC routines available to supported media types
@@ -173,22 +180,20 @@ int tipc_media_set_priority(const char *name, u32 new_value);
 int tipc_media_set_window(const char *name, u32 new_value);
 void tipc_media_addr_printf(char *buf, int len, struct tipc_media_addr *a);
 struct sk_buff *tipc_media_get_names(void);
-void tipc_l2_media_addr_set(const struct tipc_bearer *b,
-                           struct tipc_media_addr *a, char *mac);
 int tipc_enable_l2_media(struct tipc_bearer *b);
 void tipc_disable_l2_media(struct tipc_bearer *b);
 int tipc_l2_send_msg(struct sk_buff *buf, struct tipc_bearer *b,
                     struct tipc_media_addr *dest);
 
 struct sk_buff *tipc_bearer_get_names(void);
-void tipc_bearer_add_dest(struct tipc_bearer *b_ptr, u32 dest);
-void tipc_bearer_remove_dest(struct tipc_bearer *b_ptr, u32 dest);
+void tipc_bearer_add_dest(u32 bearer_id, u32 dest);
+void tipc_bearer_remove_dest(u32 bearer_id, u32 dest);
 struct tipc_bearer *tipc_bearer_find(const char *name);
 struct tipc_media *tipc_media_find(const char *name);
 int tipc_bearer_setup(void);
 void tipc_bearer_cleanup(void);
 void tipc_bearer_stop(void);
-void tipc_bearer_send(struct tipc_bearer *b, struct sk_buff *buf,
+void tipc_bearer_send(u32 bearer_id, struct sk_buff *buf,
                      struct tipc_media_addr *dest);
 
 #endif /* _TIPC_BEARER_H */
index 4b981c053823e90cc31963277aedd8c3682bc1a0..2b42403ad33a690221456ff25fb4be50a2235255 100644 (file)
@@ -42,8 +42,6 @@
 
 #define REPLY_TRUNCATED "<truncated>\n"
 
-static DEFINE_MUTEX(config_mutex);
-
 static const void *req_tlv_area;       /* request message TLV area */
 static int req_tlv_space;              /* request message TLV area size */
 static int rep_headroom;               /* reply message headroom to use */
@@ -179,8 +177,10 @@ 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)");
-       tipc_net_start(addr);
-       return tipc_cfg_reply_none();
+       if (!tipc_net_start(addr))
+               return tipc_cfg_reply_none();
+
+       return tipc_cfg_reply_error_string("cannot change to network mode");
 }
 
 static struct sk_buff *cfg_set_max_ports(void)
@@ -223,7 +223,7 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area
 {
        struct sk_buff *rep_tlv_buf;
 
-       mutex_lock(&config_mutex);
+       rtnl_lock();
 
        /* Save request and reply details in a well-known location */
        req_tlv_area = request_area;
@@ -337,6 +337,6 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area
 
        /* Return reply buffer */
 exit:
-       mutex_unlock(&config_mutex);
+       rtnl_unlock();
        return rep_tlv_buf;
 }
index 50d57429ebcaf82b8d36bcf49f6fa1585664180a..676d18015dd82efa0346f6bed2bf0d7f5489f1f6 100644 (file)
@@ -80,7 +80,6 @@ struct sk_buff *tipc_buf_acquire(u32 size)
  */
 static void tipc_core_stop(void)
 {
-       tipc_handler_stop();
        tipc_net_stop();
        tipc_bearer_cleanup();
        tipc_netlink_stop();
@@ -100,10 +99,6 @@ static int tipc_core_start(void)
 
        get_random_bytes(&tipc_random, sizeof(tipc_random));
 
-       err = tipc_handler_start();
-       if (err)
-               goto out_handler;
-
        err = tipc_ref_table_init(tipc_max_ports, tipc_random);
        if (err)
                goto out_reftbl;
@@ -146,8 +141,6 @@ out_netlink:
 out_nametbl:
        tipc_ref_table_stop();
 out_reftbl:
-       tipc_handler_stop();
-out_handler:
        return err;
 }
 
@@ -161,10 +154,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 <<
+       sysctl_tipc_rmem[0] = TIPC_CONN_OVERLOAD_LIMIT >> 4 <<
+                             TIPC_LOW_IMPORTANCE;
+       sysctl_tipc_rmem[1] = TIPC_CONN_OVERLOAD_LIMIT >> 4 <<
                              TIPC_CRITICAL_IMPORTANCE;
-       sysctl_tipc_rmem[2] = CONN_OVERLOAD_LIMIT;
+       sysctl_tipc_rmem[2] = TIPC_CONN_OVERLOAD_LIMIT;
 
        res = tipc_core_start();
        if (res)
index 8985bbcb942bdb3d6ef839c3249d4e547c2f75ce..bb26ed1ee966c84c66fc7322877d80f763047e5a 100644 (file)
@@ -56,7 +56,8 @@
 #include <linux/list.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
-
+#include <linux/rtnetlink.h>
+#include <linux/etherdevice.h>
 
 #define TIPC_MOD_VER "2.0.0"
 
@@ -89,8 +90,6 @@ extern int tipc_random __read_mostly;
 /*
  * Routines available to privileged subsystems
  */
-int tipc_handler_start(void);
-void tipc_handler_stop(void);
 int tipc_netlink_start(void);
 void tipc_netlink_stop(void);
 int tipc_socket_init(void);
@@ -109,12 +108,10 @@ void tipc_unregister_sysctl(void);
 #endif
 
 /*
- * TIPC timer and signal code
+ * TIPC timer code
  */
 typedef void (*Handler) (unsigned long);
 
-u32 tipc_k_signal(Handler routine, unsigned long argument);
-
 /**
  * k_init_timer - initialize a timer
  * @timer: pointer to timer structure
@@ -191,6 +188,7 @@ static inline void k_term_timer(struct timer_list *timer)
 struct tipc_skb_cb {
        void *handle;
        bool deferred;
+       struct sk_buff *tail;
 };
 
 #define TIPC_SKB_CB(__skb) ((struct tipc_skb_cb *)&((__skb)->cb[0]))
index 542fe3413dc4e8d06d97eb2d5a409d2a7b26459a..aa722a42ef8b03b4d840e31bbf7582d51948fbae 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * net/tipc/discover.c
  *
- * Copyright (c) 2003-2006, Ericsson AB
+ * Copyright (c) 2003-2006, 2014, Ericsson AB
  * Copyright (c) 2005-2006, 2010-2011, Wind River Systems
  * All rights reserved.
  *
@@ -46,8 +46,9 @@
 
 /**
  * struct tipc_link_req - information about an ongoing link setup request
- * @bearer: bearer issuing requests
+ * @bearer_id: identity of bearer issuing requests
  * @dest: destination address for request messages
+ * @domain: network domain to which links can be established
  * @num_nodes: number of nodes currently discovered (i.e. with an active link)
  * @lock: spinlock for controlling access to requests
  * @buf: request message to be (repeatedly) sent
@@ -55,8 +56,9 @@
  * @timer_intv: current interval between requests (in ms)
  */
 struct tipc_link_req {
-       struct tipc_bearer *bearer;
+       u32 bearer_id;
        struct tipc_media_addr dest;
+       u32 domain;
        int num_nodes;
        spinlock_t lock;
        struct sk_buff *buf;
@@ -69,22 +71,19 @@ struct tipc_link_req {
  * @type: message type (request or response)
  * @b_ptr: ptr to bearer issuing message
  */
-static struct sk_buff *tipc_disc_init_msg(u32 type, struct tipc_bearer *b_ptr)
+static void tipc_disc_init_msg(struct sk_buff *buf, u32 type,
+                              struct tipc_bearer *b_ptr)
 {
-       struct sk_buff *buf = tipc_buf_acquire(INT_H_SIZE);
        struct tipc_msg *msg;
        u32 dest_domain = b_ptr->domain;
 
-       if (buf) {
-               msg = buf_msg(buf);
-               tipc_msg_init(msg, LINK_CONFIG, type, INT_H_SIZE, dest_domain);
-               msg_set_non_seq(msg, 1);
-               msg_set_node_sig(msg, tipc_random);
-               msg_set_dest_domain(msg, dest_domain);
-               msg_set_bc_netid(msg, tipc_net_id);
-               b_ptr->media->addr2msg(&b_ptr->addr, msg_media_addr(msg));
-       }
-       return buf;
+       msg = buf_msg(buf);
+       tipc_msg_init(msg, LINK_CONFIG, type, INT_H_SIZE, dest_domain);
+       msg_set_non_seq(msg, 1);
+       msg_set_node_sig(msg, tipc_random);
+       msg_set_dest_domain(msg, dest_domain);
+       msg_set_bc_netid(msg, tipc_net_id);
+       b_ptr->media->addr2msg(msg_media_addr(msg), &b_ptr->addr);
 }
 
 /**
@@ -107,146 +106,150 @@ static void disc_dupl_alert(struct tipc_bearer *b_ptr, u32 node_addr,
 }
 
 /**
- * tipc_disc_rcv - handle incoming link setup message (request or response)
+ * tipc_disc_rcv - handle incoming discovery message (request or response)
  * @buf: buffer containing message
- * @b_ptr: bearer that message arrived on
+ * @bearer: bearer that message arrived on
  */
-void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *b_ptr)
+void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *bearer)
 {
-       struct tipc_node *n_ptr;
+       struct tipc_node *node;
        struct tipc_link *link;
-       struct tipc_media_addr media_addr;
+       struct tipc_media_addr maddr;
        struct sk_buff *rbuf;
        struct tipc_msg *msg = buf_msg(buf);
-       u32 dest = msg_dest_domain(msg);
-       u32 orig = msg_prevnode(msg);
+       u32 ddom = msg_dest_domain(msg);
+       u32 onode = msg_prevnode(msg);
        u32 net_id = msg_bc_netid(msg);
-       u32 type = msg_type(msg);
+       u32 mtyp = msg_type(msg);
        u32 signature = msg_node_sig(msg);
-       int addr_mismatch;
-       int link_fully_up;
-
-       media_addr.broadcast = 1;
-       b_ptr->media->msg2addr(b_ptr, &media_addr, msg_media_addr(msg));
+       bool addr_match = false;
+       bool sign_match = false;
+       bool link_up = false;
+       bool accept_addr = false;
+       bool accept_sign = false;
+       bool respond = false;
+
+       bearer->media->msg2addr(bearer, &maddr, msg_media_addr(msg));
        kfree_skb(buf);
 
        /* Ensure message from node is valid and communication is permitted */
        if (net_id != tipc_net_id)
                return;
-       if (media_addr.broadcast)
+       if (maddr.broadcast)
                return;
-       if (!tipc_addr_domain_valid(dest))
+       if (!tipc_addr_domain_valid(ddom))
                return;
-       if (!tipc_addr_node_valid(orig))
+       if (!tipc_addr_node_valid(onode))
                return;
-       if (orig == tipc_own_addr) {
-               if (memcmp(&media_addr, &b_ptr->addr, sizeof(media_addr)))
-                       disc_dupl_alert(b_ptr, tipc_own_addr, &media_addr);
+
+       if (in_own_node(onode)) {
+               if (memcmp(&maddr, &bearer->addr, sizeof(maddr)))
+                       disc_dupl_alert(bearer, tipc_own_addr, &maddr);
                return;
        }
-       if (!tipc_in_scope(dest, tipc_own_addr))
+       if (!tipc_in_scope(ddom, tipc_own_addr))
                return;
-       if (!tipc_in_scope(b_ptr->domain, orig))
+       if (!tipc_in_scope(bearer->domain, onode))
                return;
 
-       /* Locate structure corresponding to requesting node */
-       n_ptr = tipc_node_find(orig);
-       if (!n_ptr) {
-               n_ptr = tipc_node_create(orig);
-               if (!n_ptr)
-                       return;
-       }
-       tipc_node_lock(n_ptr);
+       /* Locate, or if necessary, create, node: */
+       node = tipc_node_find(onode);
+       if (!node)
+               node = tipc_node_create(onode);
+       if (!node)
+               return;
 
-       /* Prepare to validate requesting node's signature and media address */
-       link = n_ptr->links[b_ptr->identity];
-       addr_mismatch = (link != NULL) &&
-               memcmp(&link->media_addr, &media_addr, sizeof(media_addr));
+       tipc_node_lock(node);
+       link = node->links[bearer->identity];
 
-       /*
-        * Ensure discovery message's signature is correct
-        *
-        * If signature is incorrect and there is no working link to the node,
-        * accept the new signature but invalidate all existing links to the
-        * node so they won't re-activate without a new discovery message.
-        *
-        * If signature is incorrect and the requested link to the node is
-        * working, accept the new signature. (This is an instance of delayed
-        * rediscovery, where a link endpoint was able to re-establish contact
-        * with its peer endpoint on a node that rebooted before receiving a
-        * discovery message from that node.)
-        *
-        * If signature is incorrect and there is a working link to the node
-        * that is not the requested link, reject the request (must be from
-        * a duplicate node).
-        */
-       if (signature != n_ptr->signature) {
-               if (n_ptr->working_links == 0) {
-                       struct tipc_link *curr_link;
-                       int i;
-
-                       for (i = 0; i < MAX_BEARERS; i++) {
-                               curr_link = n_ptr->links[i];
-                               if (curr_link) {
-                                       memset(&curr_link->media_addr, 0,
-                                              sizeof(media_addr));
-                                       tipc_link_reset(curr_link);
-                               }
-                       }
-                       addr_mismatch = (link != NULL);
-               } else if (tipc_link_is_up(link) && !addr_mismatch) {
-                       /* delayed rediscovery */
-               } else {
-                       disc_dupl_alert(b_ptr, orig, &media_addr);
-                       tipc_node_unlock(n_ptr);
-                       return;
-               }
-               n_ptr->signature = signature;
+       /* Prepare to validate requesting node's signature and media address */
+       sign_match = (signature == node->signature);
+       addr_match = link && !memcmp(&link->media_addr, &maddr, sizeof(maddr));
+       link_up = link && tipc_link_is_up(link);
+
+
+       /* These three flags give us eight permutations: */
+
+       if (sign_match && addr_match && link_up) {
+               /* All is fine. Do nothing. */
+       } else if (sign_match && addr_match && !link_up) {
+               /* Respond. The link will come up in due time */
+               respond = true;
+       } else if (sign_match && !addr_match && link_up) {
+               /* Peer has changed i/f address without rebooting.
+                * If so, the link will reset soon, and the next
+                * discovery will be accepted. So we can ignore it.
+                * It may also be an cloned or malicious peer having
+                * chosen the same node address and signature as an
+                * existing one.
+                * Ignore requests until the link goes down, if ever.
+                */
+               disc_dupl_alert(bearer, onode, &maddr);
+       } else if (sign_match && !addr_match && !link_up) {
+               /* Peer link has changed i/f address without rebooting.
+                * It may also be a cloned or malicious peer; we can't
+                * distinguish between the two.
+                * The signature is correct, so we must accept.
+                */
+               accept_addr = true;
+               respond = true;
+       } else if (!sign_match && addr_match && link_up) {
+               /* Peer node rebooted. Two possibilities:
+                *  - Delayed re-discovery; this link endpoint has already
+                *    reset and re-established contact with the peer, before
+                *    receiving a discovery message from that node.
+                *    (The peer happened to receive one from this node first).
+                *  - The peer came back so fast that our side has not
+                *    discovered it yet. Probing from this side will soon
+                *    reset the link, since there can be no working link
+                *    endpoint at the peer end, and the link will re-establish.
+                *  Accept the signature, since it comes from a known peer.
+                */
+               accept_sign = true;
+       } else if (!sign_match && addr_match && !link_up) {
+               /*  The peer node has rebooted.
+                *  Accept signature, since it is a known peer.
+                */
+               accept_sign = true;
+               respond = true;
+       } else if (!sign_match && !addr_match && link_up) {
+               /* Peer rebooted with new address, or a new/duplicate peer.
+                * Ignore until the link goes down, if ever.
+                */
+               disc_dupl_alert(bearer, onode, &maddr);
+       } else if (!sign_match && !addr_match && !link_up) {
+               /* Peer rebooted with new address, or it is a new peer.
+                * Accept signature and address.
+               */
+               accept_sign = true;
+               accept_addr = true;
+               respond = true;
        }
 
-       /*
-        * Ensure requesting node's media address is correct
-        *
-        * If media address doesn't match and the link is working, reject the
-        * request (must be from a duplicate node).
-        *
-        * If media address doesn't match and the link is not working, accept
-        * the new media address and reset the link to ensure it starts up
-        * cleanly.
-        */
-       if (addr_mismatch) {
-               if (tipc_link_is_up(link)) {
-                       disc_dupl_alert(b_ptr, orig, &media_addr);
-                       tipc_node_unlock(n_ptr);
-                       return;
-               } else {
-                       memcpy(&link->media_addr, &media_addr,
-                              sizeof(media_addr));
-                       tipc_link_reset(link);
-               }
-       }
+       if (accept_sign)
+               node->signature = signature;
 
-       /* Create a link endpoint for this bearer, if necessary */
-       if (!link) {
-               link = tipc_link_create(n_ptr, b_ptr, &media_addr);
-               if (!link) {
-                       tipc_node_unlock(n_ptr);
-                       return;
+       if (accept_addr) {
+               if (!link)
+                       link = tipc_link_create(node, bearer, &maddr);
+               if (link) {
+                       memcpy(&link->media_addr, &maddr, sizeof(maddr));
+                       tipc_link_reset(link);
+               } else {
+                       respond = false;
                }
        }
 
-       /* Accept discovery message & send response, if necessary */
-       link_fully_up = link_working_working(link);
-
-       if ((type == DSC_REQ_MSG) && !link_fully_up) {
-               rbuf = tipc_disc_init_msg(DSC_RESP_MSG, b_ptr);
+       /* Send response, if necessary */
+       if (respond && (mtyp == DSC_REQ_MSG)) {
+               rbuf = tipc_buf_acquire(INT_H_SIZE);
                if (rbuf) {
-                       tipc_bearer_send(b_ptr, rbuf, &media_addr);
+                       tipc_disc_init_msg(rbuf, DSC_RESP_MSG, bearer);
+                       tipc_bearer_send(bearer->identity, rbuf, &maddr);
                        kfree_skb(rbuf);
                }
        }
-
-       tipc_node_unlock(n_ptr);
+       tipc_node_unlock(node);
 }
 
 /**
@@ -303,7 +306,7 @@ static void disc_timeout(struct tipc_link_req *req)
        spin_lock_bh(&req->lock);
 
        /* Stop searching if only desired node has been found */
-       if (tipc_node(req->bearer->domain) && req->num_nodes) {
+       if (tipc_node(req->domain) && req->num_nodes) {
                req->timer_intv = TIPC_LINK_REQ_INACTIVE;
                goto exit;
        }
@@ -315,7 +318,7 @@ static void disc_timeout(struct tipc_link_req *req)
         * hold at fast polling rate if don't have any associated nodes,
         * otherwise hold at slow polling rate
         */
-       tipc_bearer_send(req->bearer, req->buf, &req->dest);
+       tipc_bearer_send(req->bearer_id, req->buf, &req->dest);
 
 
        req->timer_intv *= 2;
@@ -347,21 +350,23 @@ int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest)
        if (!req)
                return -ENOMEM;
 
-       req->buf = tipc_disc_init_msg(DSC_REQ_MSG, b_ptr);
+       req->buf = tipc_buf_acquire(INT_H_SIZE);
        if (!req->buf) {
                kfree(req);
-               return -ENOMSG;
+               return -ENOMEM;
        }
 
+       tipc_disc_init_msg(req->buf, DSC_REQ_MSG, b_ptr);
        memcpy(&req->dest, dest, sizeof(*dest));
-       req->bearer = b_ptr;
+       req->bearer_id = b_ptr->identity;
+       req->domain = b_ptr->domain;
        req->num_nodes = 0;
        req->timer_intv = TIPC_LINK_REQ_INIT;
        spin_lock_init(&req->lock);
        k_init_timer(&req->timer, (Handler)disc_timeout, (unsigned long)req);
        k_start_timer(&req->timer, req->timer_intv);
        b_ptr->link_req = req;
-       tipc_bearer_send(req->bearer, req->buf, &req->dest);
+       tipc_bearer_send(req->bearer_id, req->buf, &req->dest);
        return 0;
 }
 
@@ -376,3 +381,23 @@ void tipc_disc_delete(struct tipc_link_req *req)
        kfree_skb(req->buf);
        kfree(req);
 }
+
+/**
+ * tipc_disc_reset - reset object to send periodic link setup requests
+ * @b_ptr: ptr to bearer issuing requests
+ * @dest_domain: network domain to which links can be established
+ */
+void tipc_disc_reset(struct tipc_bearer *b_ptr)
+{
+       struct tipc_link_req *req = b_ptr->link_req;
+
+       spin_lock_bh(&req->lock);
+       tipc_disc_init_msg(req->buf, DSC_REQ_MSG, b_ptr);
+       req->bearer_id = b_ptr->identity;
+       req->domain = b_ptr->domain;
+       req->num_nodes = 0;
+       req->timer_intv = TIPC_LINK_REQ_INIT;
+       k_start_timer(&req->timer, req->timer_intv);
+       tipc_bearer_send(req->bearer_id, req->buf, &req->dest);
+       spin_unlock_bh(&req->lock);
+}
index 07f34729459dcacb93b71d8a56c69263db5f563b..515b57392f4d881b567d6d29cc7677bfece5e4c7 100644 (file)
@@ -41,6 +41,7 @@ struct tipc_link_req;
 
 int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest);
 void tipc_disc_delete(struct tipc_link_req *req);
+void tipc_disc_reset(struct tipc_bearer *b_ptr);
 void tipc_disc_add_dest(struct tipc_link_req *req);
 void tipc_disc_remove_dest(struct tipc_link_req *req);
 void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *b_ptr);
index 67cf3f935dba0a9e4d0141fc0406a93b1aeb60d6..5e1426f1751f146cf3350983e9d0c04d218da850 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * net/tipc/eth_media.c: Ethernet bearer support for TIPC
  *
- * Copyright (c) 2001-2007, 2013, Ericsson AB
+ * Copyright (c) 2001-2007, 2013-2014, Ericsson AB
  * Copyright (c) 2005-2008, 2011-2013, Wind River Systems
  * All rights reserved.
  *
 #include "core.h"
 #include "bearer.h"
 
-#define ETH_ADDR_OFFSET        4       /* message header offset of MAC address */
+#define ETH_ADDR_OFFSET  4  /* MAC addr position inside address field */
 
-/* convert Ethernet address to string */
-static int tipc_eth_addr2str(struct tipc_media_addr *a, char *str_buf,
-                            int str_size)
+/* Convert Ethernet address (media address format) to string */
+static int tipc_eth_addr2str(struct tipc_media_addr *addr,
+                            char *strbuf, int bufsz)
 {
-       if (str_size < 18)      /* 18 = strlen("aa:bb:cc:dd:ee:ff\0") */
+       if (bufsz < 18) /* 18 = strlen("aa:bb:cc:dd:ee:ff\0") */
                return 1;
 
-       sprintf(str_buf, "%pM", a->value);
+       sprintf(strbuf, "%pM", addr->value);
        return 0;
 }
 
-/* convert Ethernet address format to message header format */
-static int tipc_eth_addr2msg(struct tipc_media_addr *a, char *msg_area)
+/* Convert from media address format to discovery message addr format */
+static int tipc_eth_addr2msg(char *msg, struct tipc_media_addr *addr)
 {
-       memset(msg_area, 0, TIPC_MEDIA_ADDR_SIZE);
-       msg_area[TIPC_MEDIA_TYPE_OFFSET] = TIPC_MEDIA_TYPE_ETH;
-       memcpy(msg_area + ETH_ADDR_OFFSET, a->value, ETH_ALEN);
+       memset(msg, 0, TIPC_MEDIA_ADDR_SIZE);
+       msg[TIPC_MEDIA_TYPE_OFFSET] = TIPC_MEDIA_TYPE_ETH;
+       memcpy(msg + ETH_ADDR_OFFSET, addr->value, ETH_ALEN);
        return 0;
 }
 
-/* convert message header address format to Ethernet format */
-static int tipc_eth_msg2addr(const struct tipc_bearer *tb_ptr,
-                            struct tipc_media_addr *a, char *msg_area)
+/* Convert raw mac address format to media addr format */
+static int tipc_eth_raw2addr(struct tipc_bearer *b,
+                            struct tipc_media_addr *addr,
+                            char *msg)
 {
-       if (msg_area[TIPC_MEDIA_TYPE_OFFSET] != TIPC_MEDIA_TYPE_ETH)
-               return 1;
+       char bcast_mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
 
-       tipc_l2_media_addr_set(tb_ptr, a, msg_area + ETH_ADDR_OFFSET);
+       memset(addr, 0, sizeof(*addr));
+       ether_addr_copy(addr->value, msg);
+       addr->media_id = TIPC_MEDIA_TYPE_ETH;
+       addr->broadcast = !memcmp(addr->value, bcast_mac, ETH_ALEN);
        return 0;
 }
 
+/* Convert discovery msg addr format to Ethernet media addr format */
+static int tipc_eth_msg2addr(struct tipc_bearer *b,
+                            struct tipc_media_addr *addr,
+                            char *msg)
+{
+       /* Skip past preamble: */
+       msg += ETH_ADDR_OFFSET;
+       return tipc_eth_raw2addr(b, addr, msg);
+}
+
 /* Ethernet media registration info */
 struct tipc_media eth_media_info = {
        .send_msg       = tipc_l2_send_msg,
@@ -78,6 +91,7 @@ struct tipc_media eth_media_info = {
        .addr2str       = tipc_eth_addr2str,
        .addr2msg       = tipc_eth_addr2msg,
        .msg2addr       = tipc_eth_msg2addr,
+       .raw2addr       = tipc_eth_raw2addr,
        .priority       = TIPC_DEF_LINK_PRI,
        .tolerance      = TIPC_DEF_LINK_TOL,
        .window         = TIPC_DEF_LINK_WIN,
@@ -85,4 +99,3 @@ struct tipc_media eth_media_info = {
        .hwaddr_len     = ETH_ALEN,
        .name           = "eth"
 };
-
diff --git a/net/tipc/handler.c b/net/tipc/handler.c
deleted file mode 100644 (file)
index 1fabf16..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * net/tipc/handler.c: TIPC signal handling
- *
- * Copyright (c) 2000-2006, Ericsson AB
- * Copyright (c) 2005, 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"
-
-struct queue_item {
-       struct list_head next_signal;
-       void (*handler) (unsigned long);
-       unsigned long data;
-};
-
-static struct kmem_cache *tipc_queue_item_cache;
-static struct list_head signal_queue_head;
-static DEFINE_SPINLOCK(qitem_lock);
-static int handler_enabled __read_mostly;
-
-static void process_signal_queue(unsigned long dummy);
-
-static DECLARE_TASKLET_DISABLED(tipc_tasklet, process_signal_queue, 0);
-
-
-unsigned int tipc_k_signal(Handler routine, unsigned long argument)
-{
-       struct queue_item *item;
-
-       spin_lock_bh(&qitem_lock);
-       if (!handler_enabled) {
-               spin_unlock_bh(&qitem_lock);
-               return -ENOPROTOOPT;
-       }
-
-       item = kmem_cache_alloc(tipc_queue_item_cache, GFP_ATOMIC);
-       if (!item) {
-               pr_err("Signal queue out of memory\n");
-               spin_unlock_bh(&qitem_lock);
-               return -ENOMEM;
-       }
-       item->handler = routine;
-       item->data = argument;
-       list_add_tail(&item->next_signal, &signal_queue_head);
-       spin_unlock_bh(&qitem_lock);
-       tasklet_schedule(&tipc_tasklet);
-       return 0;
-}
-
-static void process_signal_queue(unsigned long dummy)
-{
-       struct queue_item *__volatile__ item;
-       struct list_head *l, *n;
-
-       spin_lock_bh(&qitem_lock);
-       list_for_each_safe(l, n, &signal_queue_head) {
-               item = list_entry(l, struct queue_item, next_signal);
-               list_del(&item->next_signal);
-               spin_unlock_bh(&qitem_lock);
-               item->handler(item->data);
-               spin_lock_bh(&qitem_lock);
-               kmem_cache_free(tipc_queue_item_cache, item);
-       }
-       spin_unlock_bh(&qitem_lock);
-}
-
-int tipc_handler_start(void)
-{
-       tipc_queue_item_cache =
-               kmem_cache_create("tipc_queue_items", sizeof(struct queue_item),
-                                 0, SLAB_HWCACHE_ALIGN, NULL);
-       if (!tipc_queue_item_cache)
-               return -ENOMEM;
-
-       INIT_LIST_HEAD(&signal_queue_head);
-       tasklet_enable(&tipc_tasklet);
-       handler_enabled = 1;
-       return 0;
-}
-
-void tipc_handler_stop(void)
-{
-       struct list_head *l, *n;
-       struct queue_item *item;
-
-       spin_lock_bh(&qitem_lock);
-       if (!handler_enabled) {
-               spin_unlock_bh(&qitem_lock);
-               return;
-       }
-       handler_enabled = 0;
-       spin_unlock_bh(&qitem_lock);
-
-       tasklet_kill(&tipc_tasklet);
-
-       spin_lock_bh(&qitem_lock);
-       list_for_each_safe(l, n, &signal_queue_head) {
-               item = list_entry(l, struct queue_item, next_signal);
-               list_del(&item->next_signal);
-               kmem_cache_free(tipc_queue_item_cache, item);
-       }
-       spin_unlock_bh(&qitem_lock);
-
-       kmem_cache_destroy(tipc_queue_item_cache);
-}
index 844a77e2582856ae8cff4618c5e1de23f4220e0b..8522eef9c136bc25d39e166b32dfc459881d77c9 100644 (file)
@@ -42,7 +42,7 @@
 #include "core.h"
 #include "bearer.h"
 
-/* convert InfiniBand address to string */
+/* convert InfiniBand address (media address format) media address to string */
 static int tipc_ib_addr2str(struct tipc_media_addr *a, char *str_buf,
                            int str_size)
 {
@@ -54,23 +54,35 @@ static int tipc_ib_addr2str(struct tipc_media_addr *a, char *str_buf,
        return 0;
 }
 
-/* convert InfiniBand address format to message header format */
-static int tipc_ib_addr2msg(struct tipc_media_addr *a, char *msg_area)
+/* Convert from media address format to discovery message addr format */
+static int tipc_ib_addr2msg(char *msg, struct tipc_media_addr *addr)
 {
-       memset(msg_area, 0, TIPC_MEDIA_ADDR_SIZE);
-       msg_area[TIPC_MEDIA_TYPE_OFFSET] = TIPC_MEDIA_TYPE_IB;
-       memcpy(msg_area, a->value, INFINIBAND_ALEN);
+       memset(msg, 0, TIPC_MEDIA_ADDR_SIZE);
+       memcpy(msg, addr->value, INFINIBAND_ALEN);
        return 0;
 }
 
-/* convert message header address format to InfiniBand format */
-static int tipc_ib_msg2addr(const struct tipc_bearer *tb_ptr,
-                           struct tipc_media_addr *a, char *msg_area)
+/* Convert raw InfiniBand address format to media addr format */
+static int tipc_ib_raw2addr(struct tipc_bearer *b,
+                           struct tipc_media_addr *addr,
+                           char *msg)
 {
-       tipc_l2_media_addr_set(tb_ptr, a, msg_area);
+       memset(addr, 0, sizeof(*addr));
+       memcpy(addr->value, msg, INFINIBAND_ALEN);
+       addr->media_id = TIPC_MEDIA_TYPE_IB;
+       addr->broadcast = !memcmp(msg, b->bcast_addr.value,
+                                 INFINIBAND_ALEN);
        return 0;
 }
 
+/* Convert discovery msg addr format to InfiniBand media addr format */
+static int tipc_ib_msg2addr(struct tipc_bearer *b,
+                           struct tipc_media_addr *addr,
+                           char *msg)
+{
+       return tipc_ib_raw2addr(b, addr, msg);
+}
+
 /* InfiniBand media registration info */
 struct tipc_media ib_media_info = {
        .send_msg       = tipc_l2_send_msg,
@@ -79,6 +91,7 @@ struct tipc_media ib_media_info = {
        .addr2str       = tipc_ib_addr2str,
        .addr2msg       = tipc_ib_addr2msg,
        .msg2addr       = tipc_ib_msg2addr,
+       .raw2addr       = tipc_ib_raw2addr,
        .priority       = TIPC_DEF_LINK_PRI,
        .tolerance      = TIPC_DEF_LINK_TOL,
        .window         = TIPC_DEF_LINK_WIN,
@@ -86,4 +99,3 @@ struct tipc_media ib_media_info = {
        .hwaddr_len     = INFINIBAND_ALEN,
        .name           = "ib"
 };
-
index c5190ab75290d04202b99a3e923a69fe1a9dad38..ad2c57f5868dafe28fbf4204f9fc2189c1156d25 100644 (file)
@@ -37,6 +37,7 @@
 #include "core.h"
 #include "link.h"
 #include "port.h"
+#include "socket.h"
 #include "name_distr.h"
 #include "discover.h"
 #include "config.h"
@@ -101,9 +102,18 @@ static unsigned int align(unsigned int i)
 
 static void link_init_max_pkt(struct tipc_link *l_ptr)
 {
+       struct tipc_bearer *b_ptr;
        u32 max_pkt;
 
-       max_pkt = (l_ptr->b_ptr->mtu & ~3);
+       rcu_read_lock();
+       b_ptr = rcu_dereference_rtnl(bearer_list[l_ptr->bearer_id]);
+       if (!b_ptr) {
+               rcu_read_unlock();
+               return;
+       }
+       max_pkt = (b_ptr->mtu & ~3);
+       rcu_read_unlock();
+
        if (max_pkt > MAX_MSG_SIZE)
                max_pkt = MAX_MSG_SIZE;
 
@@ -248,7 +258,7 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,
        l_ptr->owner = n_ptr;
        l_ptr->checkpoint = 1;
        l_ptr->peer_session = INVALID_SESSION;
-       l_ptr->b_ptr = b_ptr;
+       l_ptr->bearer_id = b_ptr->identity;
        link_set_supervision_props(l_ptr, b_ptr->tolerance);
        l_ptr->state = RESET_UNKNOWN;
 
@@ -263,6 +273,7 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,
        l_ptr->priority = b_ptr->priority;
        tipc_link_set_queue_limits(l_ptr, b_ptr->window);
 
+       l_ptr->net_plane = b_ptr->net_plane;
        link_init_max_pkt(l_ptr);
 
        l_ptr->next_out_no = 1;
@@ -287,14 +298,14 @@ void tipc_link_delete_list(unsigned int bearer_id, bool shutting_down)
 
        rcu_read_lock();
        list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) {
-               spin_lock_bh(&n_ptr->lock);
+               tipc_node_lock(n_ptr);
                l_ptr = n_ptr->links[bearer_id];
                if (l_ptr) {
                        tipc_link_reset(l_ptr);
                        if (shutting_down || !tipc_node_is_up(n_ptr)) {
                                tipc_node_detach_link(l_ptr->owner, l_ptr);
                                tipc_link_reset_fragments(l_ptr);
-                               spin_unlock_bh(&n_ptr->lock);
+                               tipc_node_unlock(n_ptr);
 
                                /* Nobody else can access this link now: */
                                del_timer_sync(&l_ptr->timer);
@@ -302,12 +313,12 @@ void tipc_link_delete_list(unsigned int bearer_id, bool shutting_down)
                        } else {
                                /* Detach/delete when failover is finished: */
                                l_ptr->flags |= LINK_STOPPED;
-                               spin_unlock_bh(&n_ptr->lock);
+                               tipc_node_unlock(n_ptr);
                                del_timer_sync(&l_ptr->timer);
                        }
                        continue;
                }
-               spin_unlock_bh(&n_ptr->lock);
+               tipc_node_unlock(n_ptr);
        }
        rcu_read_unlock();
 }
@@ -388,9 +399,8 @@ static void link_release_outqueue(struct tipc_link *l_ptr)
  */
 void tipc_link_reset_fragments(struct tipc_link *l_ptr)
 {
-       kfree_skb(l_ptr->reasm_head);
-       l_ptr->reasm_head = NULL;
-       l_ptr->reasm_tail = NULL;
+       kfree_skb(l_ptr->reasm_buf);
+       l_ptr->reasm_buf = NULL;
 }
 
 /**
@@ -426,7 +436,7 @@ void tipc_link_reset(struct tipc_link *l_ptr)
                return;
 
        tipc_node_link_down(l_ptr->owner, l_ptr);
-       tipc_bearer_remove_dest(l_ptr->b_ptr, l_ptr->addr);
+       tipc_bearer_remove_dest(l_ptr->bearer_id, l_ptr->addr);
 
        if (was_active_link && tipc_node_active_links(l_ptr->owner)) {
                l_ptr->reset_checkpoint = checkpoint;
@@ -464,11 +474,11 @@ void tipc_link_reset_list(unsigned int bearer_id)
 
        rcu_read_lock();
        list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) {
-               spin_lock_bh(&n_ptr->lock);
+               tipc_node_lock(n_ptr);
                l_ptr = n_ptr->links[bearer_id];
                if (l_ptr)
                        tipc_link_reset(l_ptr);
-               spin_unlock_bh(&n_ptr->lock);
+               tipc_node_unlock(n_ptr);
        }
        rcu_read_unlock();
 }
@@ -477,7 +487,7 @@ static void link_activate(struct tipc_link *l_ptr)
 {
        l_ptr->next_in_no = l_ptr->stats.recv_info = 1;
        tipc_node_link_up(l_ptr->owner, l_ptr);
-       tipc_bearer_add_dest(l_ptr->b_ptr, l_ptr->addr);
+       tipc_bearer_add_dest(l_ptr->bearer_id, l_ptr->addr);
 }
 
 /**
@@ -777,7 +787,7 @@ int __tipc_link_xmit(struct tipc_link *l_ptr, struct sk_buff *buf)
        if (likely(!link_congested(l_ptr))) {
                link_add_to_outqueue(l_ptr, buf, msg);
 
-               tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr);
+               tipc_bearer_send(l_ptr->bearer_id, buf, &l_ptr->media_addr);
                l_ptr->unacked_window = 0;
                return dsz;
        }
@@ -825,7 +835,6 @@ int tipc_link_xmit(struct sk_buff *buf, u32 dest, u32 selector)
        struct tipc_node *n_ptr;
        int res = -ELINKCONG;
 
-       read_lock_bh(&tipc_net_lock);
        n_ptr = tipc_node_find(dest);
        if (n_ptr) {
                tipc_node_lock(n_ptr);
@@ -838,7 +847,6 @@ int tipc_link_xmit(struct sk_buff *buf, u32 dest, u32 selector)
        } else {
                kfree_skb(buf);
        }
-       read_unlock_bh(&tipc_net_lock);
        return res;
 }
 
@@ -902,7 +910,6 @@ void tipc_link_names_xmit(struct list_head *message_list, u32 dest)
        if (list_empty(message_list))
                return;
 
-       read_lock_bh(&tipc_net_lock);
        n_ptr = tipc_node_find(dest);
        if (n_ptr) {
                tipc_node_lock(n_ptr);
@@ -917,7 +924,6 @@ void tipc_link_names_xmit(struct list_head *message_list, u32 dest)
                }
                tipc_node_unlock(n_ptr);
        }
-       read_unlock_bh(&tipc_net_lock);
 
        /* discard the messages if they couldn't be sent */
        list_for_each_safe(buf, temp_buf, ((struct sk_buff *)message_list)) {
@@ -941,7 +947,7 @@ static int tipc_link_xmit_fast(struct tipc_link *l_ptr, struct sk_buff *buf,
        if (likely(!link_congested(l_ptr))) {
                if (likely(msg_size(msg) <= l_ptr->max_pkt)) {
                        link_add_to_outqueue(l_ptr, buf, msg);
-                       tipc_bearer_send(l_ptr->b_ptr, buf,
+                       tipc_bearer_send(l_ptr->bearer_id, buf,
                                         &l_ptr->media_addr);
                        l_ptr->unacked_window = 0;
                        return res;
@@ -979,7 +985,6 @@ again:
        if (unlikely(res < 0))
                return res;
 
-       read_lock_bh(&tipc_net_lock);
        node = tipc_node_find(destaddr);
        if (likely(node)) {
                tipc_node_lock(node);
@@ -990,7 +995,6 @@ again:
                                                          &sender->max_pkt);
 exit:
                                tipc_node_unlock(node);
-                               read_unlock_bh(&tipc_net_lock);
                                return res;
                        }
 
@@ -1007,7 +1011,6 @@ exit:
                         */
                        sender->max_pkt = l_ptr->max_pkt;
                        tipc_node_unlock(node);
-                       read_unlock_bh(&tipc_net_lock);
 
 
                        if ((msg_hdr_sz(hdr) + res) <= sender->max_pkt)
@@ -1018,7 +1021,6 @@ exit:
                }
                tipc_node_unlock(node);
        }
-       read_unlock_bh(&tipc_net_lock);
 
        /* Couldn't find a link to the destination node */
        kfree_skb(buf);
@@ -1204,7 +1206,7 @@ static u32 tipc_link_push_packet(struct tipc_link *l_ptr)
        if (r_q_size && buf) {
                msg_set_ack(buf_msg(buf), mod(l_ptr->next_in_no - 1));
                msg_set_bcast_ack(buf_msg(buf), l_ptr->owner->bclink.last_in);
-               tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr);
+               tipc_bearer_send(l_ptr->bearer_id, buf, &l_ptr->media_addr);
                l_ptr->retransm_queue_head = mod(++r_q_head);
                l_ptr->retransm_queue_size = --r_q_size;
                l_ptr->stats.retransmitted++;
@@ -1216,7 +1218,7 @@ static u32 tipc_link_push_packet(struct tipc_link *l_ptr)
        if (buf) {
                msg_set_ack(buf_msg(buf), mod(l_ptr->next_in_no - 1));
                msg_set_bcast_ack(buf_msg(buf), l_ptr->owner->bclink.last_in);
-               tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr);
+               tipc_bearer_send(l_ptr->bearer_id, buf, &l_ptr->media_addr);
                l_ptr->unacked_window = 0;
                kfree_skb(buf);
                l_ptr->proto_msg_queue = NULL;
@@ -1233,7 +1235,8 @@ static u32 tipc_link_push_packet(struct tipc_link *l_ptr)
                if (mod(next - first) < l_ptr->queue_limit[0]) {
                        msg_set_ack(msg, mod(l_ptr->next_in_no - 1));
                        msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in);
-                       tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr);
+                       tipc_bearer_send(l_ptr->bearer_id, buf,
+                                        &l_ptr->media_addr);
                        if (msg_user(msg) == MSG_BUNDLER)
                                msg_set_type(msg, CLOSED_MSG);
                        l_ptr->next_out = buf->next;
@@ -1256,33 +1259,24 @@ void tipc_link_push_queue(struct tipc_link *l_ptr)
        } while (!res);
 }
 
-static void link_reset_all(unsigned long addr)
+void tipc_link_reset_all(struct tipc_node *node)
 {
-       struct tipc_node *n_ptr;
        char addr_string[16];
        u32 i;
 
-       read_lock_bh(&tipc_net_lock);
-       n_ptr = tipc_node_find((u32)addr);
-       if (!n_ptr) {
-               read_unlock_bh(&tipc_net_lock);
-               return; /* node no longer exists */
-       }
-
-       tipc_node_lock(n_ptr);
+       tipc_node_lock(node);
 
        pr_warn("Resetting all links to %s\n",
-               tipc_addr_string_fill(addr_string, n_ptr->addr));
+               tipc_addr_string_fill(addr_string, node->addr));
 
        for (i = 0; i < MAX_BEARERS; i++) {
-               if (n_ptr->links[i]) {
-                       link_print(n_ptr->links[i], "Resetting link\n");
-                       tipc_link_reset(n_ptr->links[i]);
+               if (node->links[i]) {
+                       link_print(node->links[i], "Resetting link\n");
+                       tipc_link_reset(node->links[i]);
                }
        }
 
-       tipc_node_unlock(n_ptr);
-       read_unlock_bh(&tipc_net_lock);
+       tipc_node_unlock(node);
 }
 
 static void link_retransmit_failure(struct tipc_link *l_ptr,
@@ -1319,10 +1313,9 @@ static void link_retransmit_failure(struct tipc_link *l_ptr,
                        n_ptr->bclink.oos_state,
                        n_ptr->bclink.last_sent);
 
-               tipc_k_signal((Handler)link_reset_all, (unsigned long)n_ptr->addr);
-
                tipc_node_unlock(n_ptr);
 
+               tipc_bclink_set_flags(TIPC_BCLINK_RESET);
                l_ptr->stale_count = 0;
        }
 }
@@ -1352,7 +1345,7 @@ void tipc_link_retransmit(struct tipc_link *l_ptr, struct sk_buff *buf,
                msg = buf_msg(buf);
                msg_set_ack(msg, mod(l_ptr->next_in_no - 1));
                msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in);
-               tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr);
+               tipc_bearer_send(l_ptr->bearer_id, buf, &l_ptr->media_addr);
                buf = buf->next;
                retransmits--;
                l_ptr->stats.retransmitted++;
@@ -1440,14 +1433,13 @@ static int link_recv_buf_validate(struct sk_buff *buf)
 /**
  * tipc_rcv - process TIPC packets/messages arriving from off-node
  * @head: pointer to message buffer chain
- * @tb_ptr: pointer to bearer message arrived on
+ * @b_ptr: pointer to bearer message arrived on
  *
  * Invoked with no locks held.  Bearer pointer must point to a valid bearer
  * structure (i.e. cannot be NULL), but bearer can be inactive.
  */
 void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr)
 {
-       read_lock_bh(&tipc_net_lock);
        while (head) {
                struct tipc_node *n_ptr;
                struct tipc_link *l_ptr;
@@ -1497,14 +1489,14 @@ void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr)
                        goto unlock_discard;
 
                /* Verify that communication with node is currently allowed */
-               if ((n_ptr->block_setup & WAIT_PEER_DOWN) &&
-                       msg_user(msg) == LINK_PROTOCOL &&
-                       (msg_type(msg) == RESET_MSG ||
-                        msg_type(msg) == ACTIVATE_MSG) &&
-                       !msg_redundant_link(msg))
-                       n_ptr->block_setup &= ~WAIT_PEER_DOWN;
-
-               if (n_ptr->block_setup)
+               if ((n_ptr->action_flags & TIPC_WAIT_PEER_LINKS_DOWN) &&
+                   msg_user(msg) == LINK_PROTOCOL &&
+                   (msg_type(msg) == RESET_MSG ||
+                   msg_type(msg) == ACTIVATE_MSG) &&
+                   !msg_redundant_link(msg))
+                       n_ptr->action_flags &= ~TIPC_WAIT_PEER_LINKS_DOWN;
+
+               if (tipc_node_blocked(n_ptr))
                        goto unlock_discard;
 
                /* Validate message sequence number info */
@@ -1581,17 +1573,12 @@ void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr)
                        }
                        msg = buf_msg(buf);
                } else if (msg_user(msg) == MSG_FRAGMENTER) {
-                       int rc;
-
                        l_ptr->stats.recv_fragments++;
-                       rc = tipc_link_frag_rcv(&l_ptr->reasm_head,
-                                               &l_ptr->reasm_tail,
-                                               &buf);
-                       if (rc == LINK_REASM_COMPLETE) {
+                       if (tipc_buf_append(&l_ptr->reasm_buf, &buf)) {
                                l_ptr->stats.recv_fragmented++;
                                msg = buf_msg(buf);
                        } else {
-                               if (rc == LINK_REASM_ERROR)
+                               if (!l_ptr->reasm_buf)
                                        tipc_link_reset(l_ptr);
                                tipc_node_unlock(n_ptr);
                                continue;
@@ -1604,7 +1591,7 @@ void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr)
                case TIPC_HIGH_IMPORTANCE:
                case TIPC_CRITICAL_IMPORTANCE:
                        tipc_node_unlock(n_ptr);
-                       tipc_port_rcv(buf);
+                       tipc_sk_rcv(buf);
                        continue;
                case MSG_BUNDLER:
                        l_ptr->stats.recv_bundles++;
@@ -1635,7 +1622,6 @@ unlock_discard:
 discard:
                kfree_skb(buf);
        }
-       read_unlock_bh(&tipc_net_lock);
 }
 
 /**
@@ -1747,12 +1733,12 @@ void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg,
                return;
 
        /* Abort non-RESET send if communication with node is prohibited */
-       if ((l_ptr->owner->block_setup) && (msg_typ != RESET_MSG))
+       if ((tipc_node_blocked(l_ptr->owner)) && (msg_typ != RESET_MSG))
                return;
 
        /* Create protocol message with "out-of-sequence" sequence number */
        msg_set_type(msg, msg_typ);
-       msg_set_net_plane(msg, l_ptr->b_ptr->net_plane);
+       msg_set_net_plane(msg, l_ptr->net_plane);
        msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in);
        msg_set_last_bcast(msg, tipc_bclink_get_last_sent());
 
@@ -1818,7 +1804,7 @@ void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg,
        skb_copy_to_linear_data(buf, msg, sizeof(l_ptr->proto_msg));
        buf->priority = TC_PRIO_CONTROL;
 
-       tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr);
+       tipc_bearer_send(l_ptr->bearer_id, buf, &l_ptr->media_addr);
        l_ptr->unacked_window = 0;
        kfree_skb(buf);
 }
@@ -1840,12 +1826,9 @@ static void tipc_link_proto_rcv(struct tipc_link *l_ptr, struct sk_buff *buf)
        if (l_ptr->exp_msg_count)
                goto exit;
 
-       /* record unnumbered packet arrival (force mismatch on next timeout) */
-       l_ptr->checkpoint--;
-
-       if (l_ptr->b_ptr->net_plane != msg_net_plane(msg))
+       if (l_ptr->net_plane != msg_net_plane(msg))
                if (tipc_own_addr > msg_prevnode(msg))
-                       l_ptr->b_ptr->net_plane = msg_net_plane(msg);
+                       l_ptr->net_plane = msg_net_plane(msg);
 
        switch (msg_type(msg)) {
 
@@ -1862,7 +1845,7 @@ static void tipc_link_proto_rcv(struct tipc_link *l_ptr, struct sk_buff *buf)
                         * peer has lost contact -- don't allow peer's links
                         * to reactivate before we recognize loss & clean up
                         */
-                       l_ptr->owner->block_setup = WAIT_NODE_DOWN;
+                       l_ptr->owner->action_flags |= TIPC_WAIT_OWN_LINKS_DOWN;
                }
 
                link_state_event(l_ptr, RESET_MSG);
@@ -1918,6 +1901,10 @@ static void tipc_link_proto_rcv(struct tipc_link *l_ptr, struct sk_buff *buf)
                        tipc_link_reset(l_ptr); /* Enforce change to take effect */
                        break;
                }
+
+               /* Record reception; force mismatch at next timeout: */
+               l_ptr->checkpoint--;
+
                link_state_event(l_ptr, TRAFFIC_MSG_EVT);
                l_ptr->stats.recv_states++;
                if (link_reset_unknown(l_ptr))
@@ -2177,9 +2164,7 @@ static struct sk_buff *tipc_link_failover_rcv(struct tipc_link *l_ptr,
                }
                if (msg_user(msg) == MSG_FRAGMENTER) {
                        l_ptr->stats.recv_fragments++;
-                       tipc_link_frag_rcv(&l_ptr->reasm_head,
-                                          &l_ptr->reasm_tail,
-                                          &buf);
+                       tipc_buf_append(&l_ptr->reasm_buf, &buf);
                }
        }
 exit:
@@ -2317,53 +2302,6 @@ static int tipc_link_frag_xmit(struct tipc_link *l_ptr, struct sk_buff *buf)
        return dsz;
 }
 
-/* tipc_link_frag_rcv(): Called with node lock on. Returns
- * the reassembled buffer if message is complete.
- */
-int tipc_link_frag_rcv(struct sk_buff **head, struct sk_buff **tail,
-                      struct sk_buff **fbuf)
-{
-       struct sk_buff *frag = *fbuf;
-       struct tipc_msg *msg = buf_msg(frag);
-       u32 fragid = msg_type(msg);
-       bool headstolen;
-       int delta;
-
-       skb_pull(frag, msg_hdr_sz(msg));
-       if (fragid == FIRST_FRAGMENT) {
-               if (*head || skb_unclone(frag, GFP_ATOMIC))
-                       goto out_free;
-               *head = frag;
-               skb_frag_list_init(*head);
-               *fbuf = NULL;
-               return 0;
-       } else if (*head &&
-                  skb_try_coalesce(*head, frag, &headstolen, &delta)) {
-               kfree_skb_partial(frag, headstolen);
-       } else {
-               if (!*head)
-                       goto out_free;
-               if (!skb_has_frag_list(*head))
-                       skb_shinfo(*head)->frag_list = frag;
-               else
-                       (*tail)->next = frag;
-               *tail = frag;
-               (*head)->truesize += frag->truesize;
-       }
-       if (fragid == LAST_FRAGMENT) {
-               *fbuf = *head;
-               *tail = *head = NULL;
-               return LINK_REASM_COMPLETE;
-       }
-       *fbuf = NULL;
-       return 0;
-out_free:
-       pr_warn_ratelimited("Link unable to reassemble fragmented message\n");
-       kfree_skb(*fbuf);
-       *fbuf = NULL;
-       return LINK_REASM_ERROR;
-}
-
 static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tolerance)
 {
        if ((tolerance < TIPC_MIN_LINK_TOL) || (tolerance > TIPC_MAX_LINK_TOL))
@@ -2397,8 +2335,6 @@ void tipc_link_set_queue_limits(struct tipc_link *l_ptr, u32 window)
 /* tipc_link_find_owner - locate owner node of link by link's name
  * @name: pointer to link name string
  * @bearer_id: pointer to index in 'node->links' array where the link was found.
- * Caller must hold 'tipc_net_lock' to ensure node and bearer are not deleted;
- * this also prevents link deletion.
  *
  * Returns pointer to node owning the link, or 0 if no matching link is found.
  */
@@ -2460,7 +2396,7 @@ static int link_value_is_valid(u16 cmd, u32 new_value)
  * @new_value: new value of link, bearer, or media setting
  * @cmd: which link, bearer, or media attribute to set (TIPC_CMD_SET_LINK_*)
  *
- * Caller must hold 'tipc_net_lock' to ensure link/bearer/media is not deleted.
+ * Caller must hold RTNL lock to ensure link/bearer/media is not deleted.
  *
  * Returns 0 if value updated and negative value on error.
  */
@@ -2566,9 +2502,7 @@ struct sk_buff *tipc_link_cmd_config(const void *req_tlv_area, int req_tlv_space
                                                   " (cannot change setting on broadcast link)");
        }
 
-       read_lock_bh(&tipc_net_lock);
        res = link_cmd_set_value(args->name, new_value, cmd);
-       read_unlock_bh(&tipc_net_lock);
        if (res)
                return tipc_cfg_reply_error_string("cannot change link setting");
 
@@ -2602,22 +2536,18 @@ struct sk_buff *tipc_link_cmd_reset_stats(const void *req_tlv_area, int req_tlv_
                        return tipc_cfg_reply_error_string("link not found");
                return tipc_cfg_reply_none();
        }
-       read_lock_bh(&tipc_net_lock);
        node = tipc_link_find_owner(link_name, &bearer_id);
-       if (!node) {
-               read_unlock_bh(&tipc_net_lock);
+       if (!node)
                return tipc_cfg_reply_error_string("link not found");
-       }
+
        tipc_node_lock(node);
        l_ptr = node->links[bearer_id];
        if (!l_ptr) {
                tipc_node_unlock(node);
-               read_unlock_bh(&tipc_net_lock);
                return tipc_cfg_reply_error_string("link not found");
        }
        link_reset_statistics(l_ptr);
        tipc_node_unlock(node);
-       read_unlock_bh(&tipc_net_lock);
        return tipc_cfg_reply_none();
 }
 
@@ -2650,18 +2580,15 @@ static int tipc_link_stats(const char *name, char *buf, const u32 buf_size)
        if (!strcmp(name, tipc_bclink_name))
                return tipc_bclink_stats(buf, buf_size);
 
-       read_lock_bh(&tipc_net_lock);
        node = tipc_link_find_owner(name, &bearer_id);
-       if (!node) {
-               read_unlock_bh(&tipc_net_lock);
+       if (!node)
                return 0;
-       }
+
        tipc_node_lock(node);
 
        l = node->links[bearer_id];
        if (!l) {
                tipc_node_unlock(node);
-               read_unlock_bh(&tipc_net_lock);
                return 0;
        }
 
@@ -2727,7 +2654,6 @@ static int tipc_link_stats(const char *name, char *buf, const u32 buf_size)
                             (s->accu_queue_sz / s->queue_sz_counts) : 0);
 
        tipc_node_unlock(node);
-       read_unlock_bh(&tipc_net_lock);
        return ret;
 }
 
@@ -2778,7 +2704,6 @@ u32 tipc_link_get_max_pkt(u32 dest, u32 selector)
        if (dest == tipc_own_addr)
                return MAX_MSG_SIZE;
 
-       read_lock_bh(&tipc_net_lock);
        n_ptr = tipc_node_find(dest);
        if (n_ptr) {
                tipc_node_lock(n_ptr);
@@ -2787,13 +2712,18 @@ u32 tipc_link_get_max_pkt(u32 dest, u32 selector)
                        res = l_ptr->max_pkt;
                tipc_node_unlock(n_ptr);
        }
-       read_unlock_bh(&tipc_net_lock);
        return res;
 }
 
 static void link_print(struct tipc_link *l_ptr, const char *str)
 {
-       pr_info("%s Link %x<%s>:", str, l_ptr->addr, l_ptr->b_ptr->name);
+       struct tipc_bearer *b_ptr;
+
+       rcu_read_lock();
+       b_ptr = rcu_dereference_rtnl(bearer_list[l_ptr->bearer_id]);
+       if (b_ptr)
+               pr_info("%s Link %x<%s>:", str, l_ptr->addr, b_ptr->name);
+       rcu_read_unlock();
 
        if (link_working_unknown(l_ptr))
                pr_cont(":WU\n");
index 8c0b49b5b2ee6b0751f248cf740254a33e424c6a..200d518b218ede4e0ad7c210c863c546f2362947 100644 (file)
 #include "msg.h"
 #include "node.h"
 
-/* Link reassembly status codes
- */
-#define LINK_REASM_ERROR       -1
-#define LINK_REASM_COMPLETE    1
-
 /* Out-of-range value for link sequence numbers
  */
 #define INVALID_LINK_SEQ 0x10000
@@ -107,7 +102,7 @@ struct tipc_stats {
  * @checkpoint: reference point for triggering link continuity checking
  * @peer_session: link session # being used by peer end of link
  * @peer_bearer_id: bearer id used by link's peer endpoint
- * @b_ptr: pointer to bearer used by link
+ * @bearer_id: local bearer id used by link
  * @tolerance: minimum link continuity loss needed to reset link [in ms]
  * @continuity_interval: link continuity testing interval [in ms]
  * @abort_limit: # of unacknowledged continuity probes needed to reset link
@@ -116,6 +111,7 @@ struct tipc_stats {
  * @proto_msg: template for control messages generated by link
  * @pmsg: convenience pointer to "proto_msg" field
  * @priority: current link priority
+ * @net_plane: current link network plane ('A' through 'H')
  * @queue_limit: outbound message queue congestion thresholds (indexed by user)
  * @exp_msg_count: # of tunnelled messages expected during link changeover
  * @reset_checkpoint: seq # of last acknowledged message at time of link reset
@@ -139,8 +135,7 @@ struct tipc_stats {
  * @next_out: ptr to first unsent outbound message in queue
  * @waiting_ports: linked list of ports waiting for link congestion to abate
  * @long_msg_seq_no: next identifier to use for outbound fragmented messages
- * @reasm_head: list head of partially reassembled inbound message fragments
- * @reasm_tail: last fragment received
+ * @reasm_buf: head of partially reassembled inbound message fragments
  * @stats: collects statistics regarding link activity
  */
 struct tipc_link {
@@ -155,7 +150,7 @@ struct tipc_link {
        u32 checkpoint;
        u32 peer_session;
        u32 peer_bearer_id;
-       struct tipc_bearer *b_ptr;
+       u32 bearer_id;
        u32 tolerance;
        u32 continuity_interval;
        u32 abort_limit;
@@ -167,6 +162,7 @@ struct tipc_link {
        } proto_msg;
        struct tipc_msg *pmsg;
        u32 priority;
+       char net_plane;
        u32 queue_limit[15];    /* queue_limit[0]==window limit */
 
        /* Changeover */
@@ -202,8 +198,7 @@ struct tipc_link {
 
        /* Fragmentation/reassembly */
        u32 long_msg_seq_no;
-       struct sk_buff *reasm_head;
-       struct sk_buff *reasm_tail;
+       struct sk_buff *reasm_buf;
 
        /* Statistics */
        struct tipc_stats stats;
@@ -228,6 +223,7 @@ struct sk_buff *tipc_link_cmd_show_stats(const void *req_tlv_area,
                                         int req_tlv_space);
 struct sk_buff *tipc_link_cmd_reset_stats(const void *req_tlv_area,
                                          int req_tlv_space);
+void tipc_link_reset_all(struct tipc_node *node);
 void tipc_link_reset(struct tipc_link *l_ptr);
 void tipc_link_reset_list(unsigned int bearer_id);
 int tipc_link_xmit(struct sk_buff *buf, u32 dest, u32 selector);
@@ -239,9 +235,6 @@ int tipc_link_iovec_xmit_fast(struct tipc_port *sender,
                              struct iovec const *msg_sect,
                              unsigned int len, u32 destnode);
 void tipc_link_bundle_rcv(struct sk_buff *buf);
-int tipc_link_frag_rcv(struct sk_buff **reasm_head,
-                      struct sk_buff **reasm_tail,
-                      struct sk_buff **fbuf);
 void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int prob,
                          u32 gap, u32 tolerance, u32 priority, u32 acked_mtu);
 void tipc_link_push_queue(struct tipc_link *l_ptr);
index e525f8ce1dee09ce0d9baf214bd3dd8e13daa9d8..8be6e94a1ca9790dbbde757b6bd70fe9c5abb428 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * net/tipc/msg.c: TIPC message header routines
  *
- * Copyright (c) 2000-2006, Ericsson AB
+ * Copyright (c) 2000-2006, 2014, Ericsson AB
  * Copyright (c) 2005, 2010-2011, Wind River Systems
  * All rights reserved.
  *
@@ -99,3 +99,56 @@ int tipc_msg_build(struct tipc_msg *hdr, struct iovec const *msg_sect,
        }
        return dsz;
 }
+
+/* tipc_buf_append(): Append a buffer to the fragment list of another buffer
+ * Let first buffer become head buffer
+ * Returns 1 and sets *buf to headbuf if chain is complete, otherwise 0
+ * Leaves headbuf pointer at NULL if failure
+ */
+int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf)
+{
+       struct sk_buff *head = *headbuf;
+       struct sk_buff *frag = *buf;
+       struct sk_buff *tail;
+       struct tipc_msg *msg = buf_msg(frag);
+       u32 fragid = msg_type(msg);
+       bool headstolen;
+       int delta;
+
+       skb_pull(frag, msg_hdr_sz(msg));
+
+       if (fragid == FIRST_FRAGMENT) {
+               if (head || skb_unclone(frag, GFP_ATOMIC))
+                       goto out_free;
+               head = *headbuf = frag;
+               skb_frag_list_init(head);
+               return 0;
+       }
+       if (!head)
+               goto out_free;
+       tail = TIPC_SKB_CB(head)->tail;
+       if (skb_try_coalesce(head, frag, &headstolen, &delta)) {
+               kfree_skb_partial(frag, headstolen);
+       } else {
+               if (!skb_has_frag_list(head))
+                       skb_shinfo(head)->frag_list = frag;
+               else
+                       tail->next = frag;
+               head->truesize += frag->truesize;
+               head->data_len += frag->len;
+               head->len += frag->len;
+               TIPC_SKB_CB(head)->tail = frag;
+       }
+       if (fragid == LAST_FRAGMENT) {
+               *buf = head;
+               TIPC_SKB_CB(head)->tail = NULL;
+               *headbuf = NULL;
+               return 1;
+       }
+       *buf = NULL;
+       return 0;
+out_free:
+       pr_warn_ratelimited("Unable to build fragment list\n");
+       kfree_skb(*buf);
+       return 0;
+}
index 76d1269b944361076855876219d6e59b16c3b23e..503511903d1d25c9c4106f669da0a7def7c9dd46 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * net/tipc/msg.h: Include file for TIPC message header routines
  *
- * Copyright (c) 2000-2007, Ericsson AB
+ * Copyright (c) 2000-2007, 2014, Ericsson AB
  * Copyright (c) 2005-2008, 2010-2011, Wind River Systems
  * All rights reserved.
  *
@@ -711,4 +711,7 @@ 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,
                   unsigned int len, int max_size, struct sk_buff **buf);
+
+int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf);
+
 #endif
index aff8041dc1573e3fea829e286e2675e322350b2e..8ce730984aa1f0d429b2325d58862e1ef7799d4d 100644 (file)
 #include "link.h"
 #include "name_distr.h"
 
-#define ITEM_SIZE sizeof(struct distr_item)
-
-/**
- * struct distr_item - publication info distributed to other nodes
- * @type: name sequence type
- * @lower: name sequence lower bound
- * @upper: name sequence upper bound
- * @ref: publishing port reference
- * @key: publication key
- *
- * ===> All fields are stored in network byte order. <===
- *
- * First 3 fields identify (name or) name sequence being published.
- * Reference field uniquely identifies port that published name sequence.
- * Key field uniquely identifies publication, in the event a port has
- * multiple publications of the same name sequence.
- *
- * Note: There is no field that identifies the publishing node because it is
- * the same for all items contained within a publication message.
- */
-struct distr_item {
-       __be32 type;
-       __be32 lower;
-       __be32 upper;
-       __be32 ref;
-       __be32 key;
-};
-
 /**
  * struct publ_list - list of publications made by this node
  * @list: circular list of publications
@@ -127,7 +99,7 @@ static struct sk_buff *named_prepare_buf(u32 type, u32 size, u32 dest)
        return buf;
 }
 
-static void named_cluster_distribute(struct sk_buff *buf)
+void named_cluster_distribute(struct sk_buff *buf)
 {
        struct sk_buff *buf_copy;
        struct tipc_node *n_ptr;
@@ -135,18 +107,18 @@ static void named_cluster_distribute(struct sk_buff *buf)
 
        rcu_read_lock();
        list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) {
-               spin_lock_bh(&n_ptr->lock);
+               tipc_node_lock(n_ptr);
                l_ptr = n_ptr->active_links[n_ptr->addr & 1];
                if (l_ptr) {
                        buf_copy = skb_copy(buf, GFP_ATOMIC);
                        if (!buf_copy) {
-                               spin_unlock_bh(&n_ptr->lock);
+                               tipc_node_unlock(n_ptr);
                                break;
                        }
                        msg_set_destnode(buf_msg(buf_copy), n_ptr->addr);
                        __tipc_link_xmit(l_ptr, buf_copy);
                }
-               spin_unlock_bh(&n_ptr->lock);
+               tipc_node_unlock(n_ptr);
        }
        rcu_read_unlock();
 
@@ -156,7 +128,7 @@ static void named_cluster_distribute(struct sk_buff *buf)
 /**
  * tipc_named_publish - tell other nodes about a new publication by this node
  */
-void tipc_named_publish(struct publication *publ)
+struct sk_buff *tipc_named_publish(struct publication *publ)
 {
        struct sk_buff *buf;
        struct distr_item *item;
@@ -165,23 +137,23 @@ void tipc_named_publish(struct publication *publ)
        publ_lists[publ->scope]->size++;
 
        if (publ->scope == TIPC_NODE_SCOPE)
-               return;
+               return NULL;
 
        buf = named_prepare_buf(PUBLICATION, ITEM_SIZE, 0);
        if (!buf) {
                pr_warn("Publication distribution failure\n");
-               return;
+               return NULL;
        }
 
        item = (struct distr_item *)msg_data(buf_msg(buf));
        publ_to_item(item, publ);
-       named_cluster_distribute(buf);
+       return buf;
 }
 
 /**
  * tipc_named_withdraw - tell other nodes about a withdrawn publication by this node
  */
-void tipc_named_withdraw(struct publication *publ)
+struct sk_buff *tipc_named_withdraw(struct publication *publ)
 {
        struct sk_buff *buf;
        struct distr_item *item;
@@ -190,17 +162,17 @@ void tipc_named_withdraw(struct publication *publ)
        publ_lists[publ->scope]->size--;
 
        if (publ->scope == TIPC_NODE_SCOPE)
-               return;
+               return NULL;
 
        buf = named_prepare_buf(WITHDRAWAL, ITEM_SIZE, 0);
        if (!buf) {
                pr_warn("Withdrawal distribution failure\n");
-               return;
+               return NULL;
        }
 
        item = (struct distr_item *)msg_data(buf_msg(buf));
        publ_to_item(item, publ);
-       named_cluster_distribute(buf);
+       return buf;
 }
 
 /*
@@ -239,31 +211,9 @@ static void named_distribute(struct list_head *message_list, u32 node,
 /**
  * tipc_named_node_up - tell specified node about all publications by this node
  */
-void tipc_named_node_up(unsigned long nodearg)
+void tipc_named_node_up(u32 max_item_buf, u32 node)
 {
-       struct tipc_node *n_ptr;
-       struct tipc_link *l_ptr;
-       struct list_head message_list;
-       u32 node = (u32)nodearg;
-       u32 max_item_buf = 0;
-
-       /* compute maximum amount of publication data to send per message */
-       read_lock_bh(&tipc_net_lock);
-       n_ptr = tipc_node_find(node);
-       if (n_ptr) {
-               tipc_node_lock(n_ptr);
-               l_ptr = n_ptr->active_links[0];
-               if (l_ptr)
-                       max_item_buf = ((l_ptr->max_pkt - INT_H_SIZE) /
-                               ITEM_SIZE) * ITEM_SIZE;
-               tipc_node_unlock(n_ptr);
-       }
-       read_unlock_bh(&tipc_net_lock);
-       if (!max_item_buf)
-               return;
-
-       /* create list of publication messages, then send them as a unit */
-       INIT_LIST_HEAD(&message_list);
+       LIST_HEAD(message_list);
 
        read_lock_bh(&tipc_nametbl_lock);
        named_distribute(&message_list, node, &publ_cluster, max_item_buf);
index 9b312ccfd43e7da41bcab4ca33d5f0f4d5be86cf..b2eed4ec1526a34efd8b191e4b952bf9718f7a2e 100644 (file)
 
 #include "name_table.h"
 
-void tipc_named_publish(struct publication *publ);
-void tipc_named_withdraw(struct publication *publ);
-void tipc_named_node_up(unsigned long node);
+#define ITEM_SIZE sizeof(struct distr_item)
+
+/**
+ * struct distr_item - publication info distributed to other nodes
+ * @type: name sequence type
+ * @lower: name sequence lower bound
+ * @upper: name sequence upper bound
+ * @ref: publishing port reference
+ * @key: publication key
+ *
+ * ===> All fields are stored in network byte order. <===
+ *
+ * First 3 fields identify (name or) name sequence being published.
+ * Reference field uniquely identifies port that published name sequence.
+ * Key field uniquely identifies publication, in the event a port has
+ * multiple publications of the same name sequence.
+ *
+ * Note: There is no field that identifies the publishing node because it is
+ * the same for all items contained within a publication message.
+ */
+struct distr_item {
+       __be32 type;
+       __be32 lower;
+       __be32 upper;
+       __be32 ref;
+       __be32 key;
+};
+
+struct sk_buff *tipc_named_publish(struct publication *publ);
+struct sk_buff *tipc_named_withdraw(struct publication *publ);
+void named_cluster_distribute(struct sk_buff *buf);
+void tipc_named_node_up(u32 max_item_buf, u32 node);
 void tipc_named_rcv(struct sk_buff *buf);
 void tipc_named_reinit(void);
 
index 042e8e3cabc09f84aa5dce626c57a30faf3ca32d..9d7d37d95187c77d9d7490ce7aec4de147a9f2fd 100644 (file)
@@ -664,6 +664,7 @@ struct publication *tipc_nametbl_publish(u32 type, u32 lower, u32 upper,
                                         u32 scope, u32 port_ref, u32 key)
 {
        struct publication *publ;
+       struct sk_buff *buf = NULL;
 
        if (table.local_publ_count >= TIPC_MAX_PUBLICATIONS) {
                pr_warn("Publication failed, local publication limit reached (%u)\n",
@@ -676,9 +677,12 @@ struct publication *tipc_nametbl_publish(u32 type, u32 lower, u32 upper,
                                   tipc_own_addr, port_ref, key);
        if (likely(publ)) {
                table.local_publ_count++;
-               tipc_named_publish(publ);
+               buf = tipc_named_publish(publ);
        }
        write_unlock_bh(&tipc_nametbl_lock);
+
+       if (buf)
+               named_cluster_distribute(buf);
        return publ;
 }
 
@@ -688,15 +692,19 @@ struct publication *tipc_nametbl_publish(u32 type, u32 lower, u32 upper,
 int tipc_nametbl_withdraw(u32 type, u32 lower, u32 ref, u32 key)
 {
        struct publication *publ;
+       struct sk_buff *buf;
 
        write_lock_bh(&tipc_nametbl_lock);
        publ = tipc_nametbl_remove_publ(type, lower, tipc_own_addr, ref, key);
        if (likely(publ)) {
                table.local_publ_count--;
-               tipc_named_withdraw(publ);
+               buf = tipc_named_withdraw(publ);
                write_unlock_bh(&tipc_nametbl_lock);
                list_del_init(&publ->pport_list);
                kfree(publ);
+
+               if (buf)
+                       named_cluster_distribute(buf);
                return 1;
        }
        write_unlock_bh(&tipc_nametbl_lock);
@@ -961,6 +969,7 @@ static void tipc_purge_publications(struct name_seq *seq)
        list_for_each_entry_safe(publ, safe, &info->zone_list, zone_list) {
                tipc_nametbl_remove_publ(publ->type, publ->lower, publ->node,
                                         publ->ref, publ->key);
+               kfree(publ);
        }
 }
 
@@ -982,7 +991,6 @@ void tipc_nametbl_stop(void)
                hlist_for_each_entry_safe(seq, safe, seq_head, ns_list) {
                        tipc_purge_publications(seq);
                }
-               continue;
        }
        kfree(table.types);
        table.types = NULL;
index 4c564eb69e1ad9bd2695e78eb91464a0112e663c..f64375e7f99fa4081ce2a30629071ca3e546aa05 100644 (file)
 #include "name_distr.h"
 #include "subscr.h"
 #include "port.h"
+#include "socket.h"
 #include "node.h"
 #include "config.h"
 
 /*
  * The TIPC locking policy is designed to ensure a very fine locking
  * granularity, permitting complete parallel access to individual
- * port and node/link instances. The code consists of three major
+ * port and node/link instances. The code consists of four major
  * locking domains, each protected with their own disjunct set of locks.
  *
- * 1: The routing hierarchy.
- *    Comprises the structures 'zone', 'cluster', 'node', 'link'
- *    and 'bearer'. The whole hierarchy is protected by a big
- *    read/write lock, tipc_net_lock, to enssure that nothing is added
- *    or removed while code is accessing any of these structures.
- *    This layer must not be called from the two others while they
- *    hold any of their own locks.
- *    Neither must it itself do any upcalls to the other two before
- *    it has released tipc_net_lock and other protective locks.
+ * 1: The bearer level.
+ *    RTNL lock is used to serialize the process of configuring bearer
+ *    on update side, and RCU lock is applied on read side to make
+ *    bearer instance valid on both paths of message transmission and
+ *    reception.
  *
- *   Within the tipc_net_lock domain there are two sub-domains;'node' and
- *   'bearer', where local write operations are permitted,
- *   provided that those are protected by individual spin_locks
- *   per instance. Code holding tipc_net_lock(read) and a node spin_lock
- *   is permitted to poke around in both the node itself and its
- *   subordinate links. I.e, it can update link counters and queues,
- *   change link state, send protocol messages, and alter the
- *   "active_links" array in the node; but it can _not_ remove a link
- *   or a node from the overall structure.
- *   Correspondingly, individual bearers may change status within a
- *   tipc_net_lock(read), protected by an individual spin_lock ber bearer
- *   instance, but it needs tipc_net_lock(write) to remove/add any bearers.
+ * 2: The node and link level.
+ *    All node instances are saved into two tipc_node_list and node_htable
+ *    lists. The two lists are protected by node_list_lock on write side,
+ *    and they are guarded with RCU lock on read side. Especially node
+ *    instance is destroyed only when TIPC module is removed, and we can
+ *    confirm that there has no any user who is accessing the node at the
+ *    moment. Therefore, Except for iterating the two lists within RCU
+ *    protection, it's no needed to hold RCU that we access node instance
+ *    in other places.
  *
+ *    In addition, all members in node structure including link instances
+ *    are protected by node spin lock.
  *
- *  2: The transport level of the protocol.
- *     This consists of the structures port, (and its user level
- *     representations, such as user_port and tipc_sock), reference and
- *     tipc_user (port.c, reg.c, socket.c).
+ * 3: The transport level of the protocol.
+ *    This consists of the structures port, (and its user level
+ *    representations, such as user_port and tipc_sock), reference and
+ *    tipc_user (port.c, reg.c, socket.c).
  *
- *     This layer has four different locks:
+ *    This layer has four different locks:
  *     - The tipc_port spin_lock. This is protecting each port instance
  *       from parallel data access and removal. Since we can not place
  *       this lock in the port itself, it has been placed in the
@@ -96,7 +92,7 @@
  *       There are two such lists; 'port_list', which is used for management,
  *       and 'wait_list', which is used to queue ports during congestion.
  *
- *  3: The name table (name_table.c, name_distr.c, subscription.c)
+ *  4: The name table (name_table.c, name_distr.c, subscription.c)
  *     - There is one big read/write-lock (tipc_nametbl_lock) protecting the
  *       overall name table structure. Nothing must be added/removed to
  *       this structure without holding write access to it.
  *     - A local spin_lock protecting the queue of subscriber events.
 */
 
-DEFINE_RWLOCK(tipc_net_lock);
-
 static void net_route_named_msg(struct sk_buff *buf)
 {
        struct tipc_msg *msg = buf_msg(buf);
@@ -148,7 +142,7 @@ void tipc_net_route_msg(struct sk_buff *buf)
                        if (msg_mcast(msg))
                                tipc_port_mcast_rcv(buf, NULL);
                        else if (msg_destport(msg))
-                               tipc_port_rcv(buf);
+                               tipc_sk_rcv(buf);
                        else
                                net_route_named_msg(buf);
                        return;
@@ -171,22 +165,25 @@ void tipc_net_route_msg(struct sk_buff *buf)
        tipc_link_xmit(buf, dnode, msg_link_selector(msg));
 }
 
-void tipc_net_start(u32 addr)
+int tipc_net_start(u32 addr)
 {
        char addr_string[16];
+       int res;
 
-       write_lock_bh(&tipc_net_lock);
        tipc_own_addr = addr;
        tipc_named_reinit();
        tipc_port_reinit();
-       tipc_bclink_init();
-       write_unlock_bh(&tipc_net_lock);
+       res = tipc_bclink_init();
+       if (res)
+               return res;
 
        tipc_nametbl_publish(TIPC_CFG_SRV, tipc_own_addr, tipc_own_addr,
                             TIPC_ZONE_SCOPE, 0, tipc_own_addr);
+
        pr_info("Started in network mode\n");
        pr_info("Own node address %s, network identity %u\n",
                tipc_addr_string_fill(addr_string, tipc_own_addr), tipc_net_id);
+       return 0;
 }
 
 void tipc_net_stop(void)
@@ -195,11 +192,11 @@ void tipc_net_stop(void)
                return;
 
        tipc_nametbl_withdraw(TIPC_CFG_SRV, tipc_own_addr, 0, tipc_own_addr);
-       write_lock_bh(&tipc_net_lock);
+       rtnl_lock();
        tipc_bearer_stop();
        tipc_bclink_stop();
        tipc_node_stop();
-       write_unlock_bh(&tipc_net_lock);
+       rtnl_unlock();
 
        pr_info("Left network mode\n");
 }
index 079daadb3f7286471cd5146798f6b06328bf99ad..c6c2b46f7c283095c4e29c7e0b11c5cdea2bcc01 100644 (file)
 #ifndef _TIPC_NET_H
 #define _TIPC_NET_H
 
-extern rwlock_t tipc_net_lock;
-
 void tipc_net_route_msg(struct sk_buff *buf);
 
-void tipc_net_start(u32 addr);
+int tipc_net_start(u32 addr);
 void tipc_net_stop(void);
 
 #endif
index 1d3a4999a70ff96a751f3908d0d8e274af63a962..5b44c3041be431955094de87f1815122deeea369 100644 (file)
@@ -108,7 +108,7 @@ struct tipc_node *tipc_node_create(u32 addr)
                        break;
        }
        list_add_tail_rcu(&n_ptr->list, &temp_node->list);
-       n_ptr->block_setup = WAIT_PEER_DOWN;
+       n_ptr->action_flags = TIPC_WAIT_PEER_LINKS_DOWN;
        n_ptr->signature = INVALID_NODE_SIG;
 
        tipc_num_nodes++;
@@ -144,11 +144,13 @@ void tipc_node_stop(void)
 void tipc_node_link_up(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
 {
        struct tipc_link **active = &n_ptr->active_links[0];
+       u32 addr = n_ptr->addr;
 
        n_ptr->working_links++;
-
+       tipc_nametbl_publish(TIPC_LINK_STATE, addr, addr, TIPC_NODE_SCOPE,
+                            l_ptr->bearer_id, addr);
        pr_info("Established link <%s> on network plane %c\n",
-               l_ptr->name, l_ptr->b_ptr->net_plane);
+               l_ptr->name, l_ptr->net_plane);
 
        if (!active[0]) {
                active[0] = active[1] = l_ptr;
@@ -203,16 +205,18 @@ static void node_select_active_links(struct tipc_node *n_ptr)
 void tipc_node_link_down(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
 {
        struct tipc_link **active;
+       u32 addr = n_ptr->addr;
 
        n_ptr->working_links--;
+       tipc_nametbl_withdraw(TIPC_LINK_STATE, addr, l_ptr->bearer_id, addr);
 
        if (!tipc_link_is_active(l_ptr)) {
                pr_info("Lost standby link <%s> on network plane %c\n",
-                       l_ptr->name, l_ptr->b_ptr->net_plane);
+                       l_ptr->name, l_ptr->net_plane);
                return;
        }
        pr_info("Lost link <%s> on network plane %c\n",
-               l_ptr->name, l_ptr->b_ptr->net_plane);
+               l_ptr->name, l_ptr->net_plane);
 
        active = &n_ptr->active_links[0];
        if (active[0] == l_ptr)
@@ -239,7 +243,7 @@ int tipc_node_is_up(struct tipc_node *n_ptr)
 
 void tipc_node_attach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
 {
-       n_ptr->links[l_ptr->b_ptr->identity] = l_ptr;
+       n_ptr->links[l_ptr->bearer_id] = l_ptr;
        spin_lock_bh(&node_list_lock);
        tipc_num_links++;
        spin_unlock_bh(&node_list_lock);
@@ -263,26 +267,12 @@ void tipc_node_detach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
 
 static void node_established_contact(struct tipc_node *n_ptr)
 {
-       tipc_k_signal((Handler)tipc_named_node_up, n_ptr->addr);
+       n_ptr->action_flags |= TIPC_NOTIFY_NODE_UP;
        n_ptr->bclink.oos_state = 0;
        n_ptr->bclink.acked = tipc_bclink_get_last_sent();
        tipc_bclink_add_node(n_ptr->addr);
 }
 
-static void node_name_purge_complete(unsigned long node_addr)
-{
-       struct tipc_node *n_ptr;
-
-       read_lock_bh(&tipc_net_lock);
-       n_ptr = tipc_node_find(node_addr);
-       if (n_ptr) {
-               tipc_node_lock(n_ptr);
-               n_ptr->block_setup &= ~WAIT_NAMES_GONE;
-               tipc_node_unlock(n_ptr);
-       }
-       read_unlock_bh(&tipc_net_lock);
-}
-
 static void node_lost_contact(struct tipc_node *n_ptr)
 {
        char addr_string[16];
@@ -296,10 +286,9 @@ static void node_lost_contact(struct tipc_node *n_ptr)
                kfree_skb_list(n_ptr->bclink.deferred_head);
                n_ptr->bclink.deferred_size = 0;
 
-               if (n_ptr->bclink.reasm_head) {
-                       kfree_skb(n_ptr->bclink.reasm_head);
-                       n_ptr->bclink.reasm_head = NULL;
-                       n_ptr->bclink.reasm_tail = NULL;
+               if (n_ptr->bclink.reasm_buf) {
+                       kfree_skb(n_ptr->bclink.reasm_buf);
+                       n_ptr->bclink.reasm_buf = NULL;
                }
 
                tipc_bclink_remove_node(n_ptr->addr);
@@ -318,12 +307,13 @@ static void node_lost_contact(struct tipc_node *n_ptr)
                tipc_link_reset_fragments(l_ptr);
        }
 
-       /* Notify subscribers */
-       tipc_nodesub_notify(n_ptr);
+       n_ptr->action_flags &= ~TIPC_WAIT_OWN_LINKS_DOWN;
 
-       /* Prevent re-contact with node until cleanup is done */
-       n_ptr->block_setup = WAIT_PEER_DOWN | WAIT_NAMES_GONE;
-       tipc_k_signal((Handler)node_name_purge_complete, n_ptr->addr);
+       /* Notify subscribers and prevent re-contact with node until
+        * cleanup is done.
+        */
+       n_ptr->action_flags |= TIPC_WAIT_PEER_LINKS_DOWN |
+                              TIPC_NOTIFY_NODE_DOWN;
 }
 
 struct sk_buff *tipc_node_get_nodes(const void *req_tlv_area, int req_tlv_space)
@@ -436,3 +426,63 @@ struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space)
        rcu_read_unlock();
        return buf;
 }
+
+/**
+ * tipc_node_get_linkname - get the name of a link
+ *
+ * @bearer_id: id of the bearer
+ * @node: peer node address
+ * @linkname: link name output buffer
+ *
+ * Returns 0 on success
+ */
+int tipc_node_get_linkname(u32 bearer_id, u32 addr, char *linkname, size_t len)
+{
+       struct tipc_link *link;
+       struct tipc_node *node = tipc_node_find(addr);
+
+       if ((bearer_id >= MAX_BEARERS) || !node)
+               return -EINVAL;
+       tipc_node_lock(node);
+       link = node->links[bearer_id];
+       if (link) {
+               strncpy(linkname, link->name, len);
+               tipc_node_unlock(node);
+               return 0;
+       }
+       tipc_node_unlock(node);
+       return -EINVAL;
+}
+
+void tipc_node_unlock(struct tipc_node *node)
+{
+       LIST_HEAD(nsub_list);
+       struct tipc_link *link;
+       int pkt_sz = 0;
+       u32 addr = 0;
+
+       if (likely(!node->action_flags)) {
+               spin_unlock_bh(&node->lock);
+               return;
+       }
+
+       if (node->action_flags & TIPC_NOTIFY_NODE_DOWN) {
+               list_replace_init(&node->nsub, &nsub_list);
+               node->action_flags &= ~TIPC_NOTIFY_NODE_DOWN;
+       }
+       if (node->action_flags & TIPC_NOTIFY_NODE_UP) {
+               link = node->active_links[0];
+               node->action_flags &= ~TIPC_NOTIFY_NODE_UP;
+               if (link) {
+                       pkt_sz = ((link->max_pkt - INT_H_SIZE) / ITEM_SIZE) *
+                                 ITEM_SIZE;
+                       addr = node->addr;
+               }
+       }
+       spin_unlock_bh(&node->lock);
+
+       if (!list_empty(&nsub_list))
+               tipc_nodesub_notify(&nsub_list);
+       if (pkt_sz)
+               tipc_named_node_up(pkt_sz, addr);
+}
index 7cbb8cec1a932f881cd636a71edb0342070530ae..9087063793f26eb352f9b848a14b27545c8b02be 100644 (file)
  */
 #define INVALID_NODE_SIG 0x10000
 
-/* Flags used to block (re)establishment of contact with a neighboring node */
-#define WAIT_PEER_DOWN 0x0001  /* wait to see that peer's links are down */
-#define WAIT_NAMES_GONE        0x0002  /* wait for peer's publications to be purged */
-#define WAIT_NODE_DOWN 0x0004  /* wait until peer node is declared down */
+/* Flags used to take different actions according to flag type
+ * TIPC_WAIT_PEER_LINKS_DOWN: wait to see that peer's links are down
+ * TIPC_WAIT_OWN_LINKS_DOWN: wait until peer node is declared down
+ * TIPC_NOTIFY_NODE_DOWN: notify node is down
+ * TIPC_NOTIFY_NODE_UP: notify node is up
+ */
+enum {
+       TIPC_WAIT_PEER_LINKS_DOWN       = (1 << 1),
+       TIPC_WAIT_OWN_LINKS_DOWN        = (1 << 2),
+       TIPC_NOTIFY_NODE_DOWN           = (1 << 3),
+       TIPC_NOTIFY_NODE_UP             = (1 << 4)
+};
+
+/**
+ * struct tipc_node_bclink - TIPC node bclink structure
+ * @acked: sequence # of last outbound b'cast message acknowledged by node
+ * @last_in: sequence # of last in-sequence b'cast message received from node
+ * @last_sent: sequence # of last b'cast message sent by node
+ * @oos_state: state tracker for handling OOS b'cast messages
+ * @deferred_size: number of OOS b'cast messages in deferred queue
+ * @deferred_head: oldest OOS b'cast message received from node
+ * @deferred_tail: newest OOS b'cast message received from node
+ * @reasm_buf: broadcast reassembly queue head from node
+ * @recv_permitted: true if node is allowed to receive b'cast messages
+ */
+struct tipc_node_bclink {
+       u32 acked;
+       u32 last_in;
+       u32 last_sent;
+       u32 oos_state;
+       u32 deferred_size;
+       struct sk_buff *deferred_head;
+       struct sk_buff *deferred_tail;
+       struct sk_buff *reasm_buf;
+       bool recv_permitted;
+};
 
 /**
  * struct tipc_node - TIPC node structure
  * @addr: network address of node
  * @lock: spinlock governing access to structure
  * @hash: links to adjacent nodes in unsorted hash chain
- * @list: links to adjacent nodes in sorted list of cluster's nodes
- * @nsub: list of "node down" subscriptions monitoring node
  * @active_links: pointers to active links to node
  * @links: pointers to all links to node
+ * @action_flags: bit mask of different types of node actions
+ * @bclink: broadcast-related info
+ * @list: links to adjacent nodes in sorted list of cluster's nodes
  * @working_links: number of working links to node (both active and standby)
- * @block_setup: bit mask of conditions preventing link establishment to node
  * @link_cnt: number of links to node
  * @signature: node instance identifier
- * @bclink: broadcast-related info
+ * @nsub: list of "node down" subscriptions monitoring node
  * @rcu: rcu struct for tipc_node
- *    @acked: sequence # of last outbound b'cast message acknowledged by node
- *    @last_in: sequence # of last in-sequence b'cast message received from node
- *    @last_sent: sequence # of last b'cast message sent by node
- *    @oos_state: state tracker for handling OOS b'cast messages
- *    @deferred_size: number of OOS b'cast messages in deferred queue
- *    @deferred_head: oldest OOS b'cast message received from node
- *    @deferred_tail: newest OOS b'cast message received from node
- *    @reasm_head: broadcast reassembly queue head from node
- *    @reasm_tail: last broadcast fragment received from node
- *    @recv_permitted: true if node is allowed to receive b'cast messages
  */
 struct tipc_node {
        u32 addr;
        spinlock_t lock;
        struct hlist_node hash;
-       struct list_head list;
-       struct list_head nsub;
        struct tipc_link *active_links[2];
        struct tipc_link *links[MAX_BEARERS];
+       unsigned int action_flags;
+       struct tipc_node_bclink bclink;
+       struct list_head list;
        int link_cnt;
        int working_links;
-       int block_setup;
        u32 signature;
+       struct list_head nsub;
        struct rcu_head rcu;
-       struct {
-               u32 acked;
-               u32 last_in;
-               u32 last_sent;
-               u32 oos_state;
-               u32 deferred_size;
-               struct sk_buff *deferred_head;
-               struct sk_buff *deferred_tail;
-               struct sk_buff *reasm_head;
-               struct sk_buff *reasm_tail;
-               bool recv_permitted;
-       } bclink;
 };
 
 extern struct list_head tipc_node_list;
@@ -118,15 +129,18 @@ int tipc_node_active_links(struct tipc_node *n_ptr);
 int tipc_node_is_up(struct tipc_node *n_ptr);
 struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space);
 struct sk_buff *tipc_node_get_nodes(const void *req_tlv_area, int req_tlv_space);
+int tipc_node_get_linkname(u32 bearer_id, u32 node, char *linkname, size_t len);
+void tipc_node_unlock(struct tipc_node *node);
 
-static inline void tipc_node_lock(struct tipc_node *n_ptr)
+static inline void tipc_node_lock(struct tipc_node *node)
 {
-       spin_lock_bh(&n_ptr->lock);
+       spin_lock_bh(&node->lock);
 }
 
-static inline void tipc_node_unlock(struct tipc_node *n_ptr)
+static inline bool tipc_node_blocked(struct tipc_node *node)
 {
-       spin_unlock_bh(&n_ptr->lock);
+       return (node->action_flags & (TIPC_WAIT_PEER_LINKS_DOWN |
+               TIPC_NOTIFY_NODE_DOWN | TIPC_WAIT_OWN_LINKS_DOWN));
 }
 
 #endif
index 8a7384c04add4bdc6db6ae4451ebb2232e4b338b..7c59ab1d6ecb3dc26c4efb77cd243a00341c7b5b 100644 (file)
@@ -81,14 +81,13 @@ void tipc_nodesub_unsubscribe(struct tipc_node_subscr *node_sub)
  *
  * Note: node is locked by caller
  */
-void tipc_nodesub_notify(struct tipc_node *node)
+void tipc_nodesub_notify(struct list_head *nsub_list)
 {
-       struct tipc_node_subscr *ns;
+       struct tipc_node_subscr *ns, *safe;
 
-       list_for_each_entry(ns, &node->nsub, nodesub_list) {
+       list_for_each_entry_safe(ns, safe, nsub_list, nodesub_list) {
                if (ns->handle_node_down) {
-                       tipc_k_signal((Handler)ns->handle_node_down,
-                                     (unsigned long)ns->usr_handle);
+                       ns->handle_node_down(ns->usr_handle);
                        ns->handle_node_down = NULL;
                }
        }
index c95d20727ded3ea70cf9532a82cd8ff20fde415d..d91b8cc81e3d786948bd4ed9ac622689c4218ea1 100644 (file)
@@ -58,6 +58,6 @@ struct tipc_node_subscr {
 void tipc_nodesub_subscribe(struct tipc_node_subscr *node_sub, u32 addr,
                            void *usr_handle, net_ev_handler handle_down);
 void tipc_nodesub_unsubscribe(struct tipc_node_subscr *node_sub);
-void tipc_nodesub_notify(struct tipc_node *node);
+void tipc_nodesub_notify(struct list_head *nsub_list);
 
 #endif
index 5c14c7801ee65095d809d502cab9f78d33d51e5e..5fd7acce01ea339b7ffe2873956e9513eb40bb49 100644 (file)
@@ -165,7 +165,7 @@ void tipc_port_mcast_rcv(struct sk_buff *buf, struct tipc_port_list *dp)
                msg_set_destnode(msg, tipc_own_addr);
                if (dp->count == 1) {
                        msg_set_destport(msg, dp->ports[0]);
-                       tipc_port_rcv(buf);
+                       tipc_sk_rcv(buf);
                        tipc_port_list_free(dp);
                        return;
                }
@@ -180,7 +180,7 @@ void tipc_port_mcast_rcv(struct sk_buff *buf, struct tipc_port_list *dp)
                        if ((index == 0) && (cnt != 0))
                                item = item->next;
                        msg_set_destport(buf_msg(b), item->ports[index]);
-                       tipc_port_rcv(b);
+                       tipc_sk_rcv(b);
                }
        }
 exit:
@@ -343,7 +343,7 @@ int tipc_reject_msg(struct sk_buff *buf, u32 err)
        /* send returned message & dispose of rejected message */
        src_node = msg_prevnode(msg);
        if (in_own_node(src_node))
-               tipc_port_rcv(rbuf);
+               tipc_sk_rcv(rbuf);
        else
                tipc_link_xmit(rbuf, src_node, msg_link_selector(rmsg));
 exit:
@@ -754,37 +754,6 @@ int tipc_port_shutdown(u32 ref)
        return tipc_port_disconnect(ref);
 }
 
-/**
- * tipc_port_rcv - receive message from lower layer and deliver to port user
- */
-int tipc_port_rcv(struct sk_buff *buf)
-{
-       struct tipc_port *p_ptr;
-       struct tipc_msg *msg = buf_msg(buf);
-       u32 destport = msg_destport(msg);
-       u32 dsz = msg_data_sz(msg);
-       u32 err;
-
-       /* forward unresolved named message */
-       if (unlikely(!destport)) {
-               tipc_net_route_msg(buf);
-               return dsz;
-       }
-
-       /* validate destination & pass to port, otherwise reject message */
-       p_ptr = tipc_port_lock(destport);
-       if (likely(p_ptr)) {
-               err = tipc_sk_rcv(&tipc_port_to_sock(p_ptr)->sk, buf);
-               tipc_port_unlock(p_ptr);
-               if (likely(!err))
-                       return dsz;
-       } else {
-               err = TIPC_ERR_NO_PORT;
-       }
-
-       return tipc_reject_msg(buf, err);
-}
-
 /*
  *  tipc_port_iovec_rcv: Concatenate and deliver sectioned
  *                       message for this node.
@@ -798,7 +767,7 @@ static int tipc_port_iovec_rcv(struct tipc_port *sender,
 
        res = tipc_msg_build(&sender->phdr, msg_sect, len, MAX_MSG_SIZE, &buf);
        if (likely(buf))
-               tipc_port_rcv(buf);
+               tipc_sk_rcv(buf);
        return res;
 }
 
index a00397393bd1d9179bd779339500e34bd710aa5b..cf4ca5b1d9a48ae7752f9f476cad079e3f115da8 100644 (file)
 #include "msg.h"
 #include "node_subscr.h"
 
-#define TIPC_FLOW_CONTROL_WIN 512
-#define CONN_OVERLOAD_LIMIT    ((TIPC_FLOW_CONTROL_WIN * 2 + 1) * \
-                               SKB_TRUESIZE(TIPC_MAX_USER_MSG_SIZE))
+#define TIPC_CONNACK_INTV         256
+#define TIPC_FLOWCTRL_WIN        (TIPC_CONNACK_INTV * 2)
+#define TIPC_CONN_OVERLOAD_LIMIT ((TIPC_FLOWCTRL_WIN * 2 + 1) * \
+                                 SKB_TRUESIZE(TIPC_MAX_USER_MSG_SIZE))
 
 /**
  * struct tipc_port - TIPC port structure
@@ -134,7 +135,6 @@ int tipc_port_peer_msg(struct tipc_port *p_ptr, struct tipc_msg *msg);
 /*
  * TIPC messaging routines
  */
-int tipc_port_rcv(struct sk_buff *buf);
 
 int tipc_send(struct tipc_port *port,
              struct iovec const *msg_sect,
@@ -187,7 +187,7 @@ static inline void tipc_port_unlock(struct tipc_port *p_ptr)
 
 static inline int tipc_port_congested(struct tipc_port *p_ptr)
 {
-       return (p_ptr->sent - p_ptr->acked) >= (TIPC_FLOW_CONTROL_WIN * 2);
+       return ((p_ptr->sent - p_ptr->acked) >= TIPC_FLOWCTRL_WIN);
 }
 
 
index 3c0256962f7dafa4ee3b11d69aed963822c888c2..ef0475568f9e39cfa3aa24d1d4d7a7132ea059a4 100644 (file)
@@ -36,6 +36,7 @@
 
 #include "core.h"
 #include "port.h"
+#include "node.h"
 
 #include <linux/export.h>
 
@@ -44,7 +45,7 @@
 
 #define CONN_TIMEOUT_DEFAULT   8000    /* default connect timeout = 8s */
 
-static int backlog_rcv(struct sock *sk, struct sk_buff *skb);
+static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *skb);
 static void tipc_data_ready(struct sock *sk);
 static void tipc_write_space(struct sock *sk);
 static int tipc_release(struct socket *sock);
@@ -195,11 +196,12 @@ static int tipc_sk_create(struct net *net, struct socket *sock,
        sock->state = state;
 
        sock_init_data(sock, sk);
-       sk->sk_backlog_rcv = backlog_rcv;
+       sk->sk_backlog_rcv = tipc_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)->conn_timeout = CONN_TIMEOUT_DEFAULT;
+       tsk->conn_timeout = CONN_TIMEOUT_DEFAULT;
+       atomic_set(&tsk->dupl_rcvcnt, 0);
        tipc_port_unlock(port);
 
        if (sock->state == SS_READY) {
@@ -983,10 +985,11 @@ static int anc_data_recv(struct msghdr *m, struct tipc_msg *msg,
        return 0;
 }
 
-static int tipc_wait_for_rcvmsg(struct socket *sock, long timeo)
+static int tipc_wait_for_rcvmsg(struct socket *sock, long *timeop)
 {
        struct sock *sk = sock->sk;
        DEFINE_WAIT(wait);
+       long timeo = *timeop;
        int err;
 
        for (;;) {
@@ -1011,6 +1014,7 @@ static int tipc_wait_for_rcvmsg(struct socket *sock, long timeo)
                        break;
        }
        finish_wait(sk_sleep(sk), &wait);
+       *timeop = timeo;
        return err;
 }
 
@@ -1054,7 +1058,7 @@ static int tipc_recvmsg(struct kiocb *iocb, struct socket *sock,
 restart:
 
        /* Look for a message in receive queue; wait if necessary */
-       res = tipc_wait_for_rcvmsg(sock, timeo);
+       res = tipc_wait_for_rcvmsg(sock, &timeo);
        if (res)
                goto exit;
 
@@ -1100,7 +1104,7 @@ restart:
        /* Consume received message (optional) */
        if (likely(!(flags & MSG_PEEK))) {
                if ((sock->state != SS_READY) &&
-                   (++port->conn_unacked >= TIPC_FLOW_CONTROL_WIN))
+                   (++port->conn_unacked >= TIPC_CONNACK_INTV))
                        tipc_acknowledge(port->ref, port->conn_unacked);
                advance_rx_queue(sk);
        }
@@ -1152,7 +1156,7 @@ static int tipc_recv_stream(struct kiocb *iocb, struct socket *sock,
 
 restart:
        /* Look for a message in receive queue; wait if necessary */
-       res = tipc_wait_for_rcvmsg(sock, timeo);
+       res = tipc_wait_for_rcvmsg(sock, &timeo);
        if (res)
                goto exit;
 
@@ -1209,7 +1213,7 @@ restart:
 
        /* Consume received message (optional) */
        if (likely(!(flags & MSG_PEEK))) {
-               if (unlikely(++port->conn_unacked >= TIPC_FLOW_CONTROL_WIN))
+               if (unlikely(++port->conn_unacked >= TIPC_CONNACK_INTV))
                        tipc_acknowledge(port->ref, port->conn_unacked);
                advance_rx_queue(sk);
        }
@@ -1415,7 +1419,7 @@ static u32 filter_rcv(struct sock *sk, struct sk_buff *buf)
 }
 
 /**
- * backlog_rcv - handle incoming message from backlog queue
+ * tipc_backlog_rcv - handle incoming message from backlog queue
  * @sk: socket
  * @buf: message
  *
@@ -1423,47 +1427,74 @@ static u32 filter_rcv(struct sock *sk, struct sk_buff *buf)
  *
  * Returns 0
  */
-static int backlog_rcv(struct sock *sk, struct sk_buff *buf)
+static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *buf)
 {
        u32 res;
+       struct tipc_sock *tsk = tipc_sk(sk);
+       uint truesize = buf->truesize;
 
        res = filter_rcv(sk, buf);
-       if (res)
+       if (unlikely(res))
                tipc_reject_msg(buf, res);
+
+       if (atomic_read(&tsk->dupl_rcvcnt) < TIPC_CONN_OVERLOAD_LIMIT)
+               atomic_add(truesize, &tsk->dupl_rcvcnt);
+
        return 0;
 }
 
 /**
  * tipc_sk_rcv - handle incoming message
- * @sk:  socket receiving message
- * @buf: message
- *
- * Called with port lock already taken.
- *
- * Returns TIPC error status code (TIPC_OK if message is not to be rejected)
+ * @buf: buffer containing arriving message
+ * Consumes buffer
+ * Returns 0 if success, or errno: -EHOSTUNREACH
  */
-u32 tipc_sk_rcv(struct sock *sk, struct sk_buff *buf)
+int tipc_sk_rcv(struct sk_buff *buf)
 {
-       u32 res;
+       struct tipc_sock *tsk;
+       struct tipc_port *port;
+       struct sock *sk;
+       u32 dport = msg_destport(buf_msg(buf));
+       int err = TIPC_OK;
+       uint limit;
 
-       /*
-        * Process message if socket is unlocked; otherwise add to backlog queue
-        *
-        * This code is based on sk_receive_skb(), but must be distinct from it
-        * since a TIPC-specific filter/reject mechanism is utilized
-        */
+       /* Forward unresolved named message */
+       if (unlikely(!dport)) {
+               tipc_net_route_msg(buf);
+               return 0;
+       }
+
+       /* Validate destination */
+       port = tipc_port_lock(dport);
+       if (unlikely(!port)) {
+               err = TIPC_ERR_NO_PORT;
+               goto exit;
+       }
+
+       tsk = tipc_port_to_sock(port);
+       sk = &tsk->sk;
+
+       /* Queue message */
        bh_lock_sock(sk);
+
        if (!sock_owned_by_user(sk)) {
-               res = filter_rcv(sk, buf);
+               err = filter_rcv(sk, buf);
        } else {
-               if (sk_add_backlog(sk, buf, rcvbuf_limit(sk, buf)))
-                       res = TIPC_ERR_OVERLOAD;
-               else
-                       res = TIPC_OK;
+               if (sk->sk_backlog.len == 0)
+                       atomic_set(&tsk->dupl_rcvcnt, 0);
+               limit = rcvbuf_limit(sk, buf) + atomic_read(&tsk->dupl_rcvcnt);
+               if (sk_add_backlog(sk, buf, limit))
+                       err = TIPC_ERR_OVERLOAD;
        }
+
        bh_unlock_sock(sk);
+       tipc_port_unlock(port);
 
-       return res;
+       if (likely(!err))
+               return 0;
+exit:
+       tipc_reject_msg(buf, err);
+       return -EHOSTUNREACH;
 }
 
 static int tipc_wait_for_connect(struct socket *sock, long *timeo_p)
@@ -1905,6 +1936,28 @@ static int tipc_getsockopt(struct socket *sock, int lvl, int opt,
        return put_user(sizeof(value), ol);
 }
 
+int tipc_ioctl(struct socket *sk, unsigned int cmd, unsigned long arg)
+{
+       struct tipc_sioc_ln_req lnr;
+       void __user *argp = (void __user *)arg;
+
+       switch (cmd) {
+       case SIOCGETLINKNAME:
+               if (copy_from_user(&lnr, argp, sizeof(lnr)))
+                       return -EFAULT;
+               if (!tipc_node_get_linkname(lnr.bearer_id, lnr.peer,
+                                           lnr.linkname, TIPC_MAX_LINK_NAME)) {
+                       if (copy_to_user(argp, &lnr, sizeof(lnr)))
+                               return -EFAULT;
+                       return 0;
+               }
+               return -EADDRNOTAVAIL;
+               break;
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
 /* Protocol switches for the various types of TIPC sockets */
 
 static const struct proto_ops msg_ops = {
@@ -1917,7 +1970,7 @@ static const struct proto_ops msg_ops = {
        .accept         = sock_no_accept,
        .getname        = tipc_getname,
        .poll           = tipc_poll,
-       .ioctl          = sock_no_ioctl,
+       .ioctl          = tipc_ioctl,
        .listen         = sock_no_listen,
        .shutdown       = tipc_shutdown,
        .setsockopt     = tipc_setsockopt,
@@ -1938,7 +1991,7 @@ static const struct proto_ops packet_ops = {
        .accept         = tipc_accept,
        .getname        = tipc_getname,
        .poll           = tipc_poll,
-       .ioctl          = sock_no_ioctl,
+       .ioctl          = tipc_ioctl,
        .listen         = tipc_listen,
        .shutdown       = tipc_shutdown,
        .setsockopt     = tipc_setsockopt,
@@ -1959,7 +2012,7 @@ static const struct proto_ops stream_ops = {
        .accept         = tipc_accept,
        .getname        = tipc_getname,
        .poll           = tipc_poll,
-       .ioctl          = sock_no_ioctl,
+       .ioctl          = tipc_ioctl,
        .listen         = tipc_listen,
        .shutdown       = tipc_shutdown,
        .setsockopt     = tipc_setsockopt,
index 74e5c7f195a660d6a1e88d1b506cf4e7371566f1..3afcd2a70b313c21d67752606b839e613d3cf9df 100644 (file)
  * @port: port - interacts with 'sk' and with the rest of the TIPC stack
  * @peer_name: the peer of the connection, if any
  * @conn_timeout: the time we can wait for an unresponded setup request
+ * @dupl_rcvcnt: number of bytes counted twice, in both backlog and rcv queue
  */
 
 struct tipc_sock {
        struct sock sk;
        struct tipc_port port;
        unsigned int conn_timeout;
+       atomic_t dupl_rcvcnt;
 };
 
 static inline struct tipc_sock *tipc_sk(const struct sock *sk)
@@ -67,6 +69,6 @@ static inline void tipc_sock_wakeup(struct tipc_sock *tsk)
        tsk->sk.sk_write_space(&tsk->sk);
 }
 
-u32 tipc_sk_rcv(struct sock *sk, struct sk_buff *buf);
+int tipc_sk_rcv(struct sk_buff *buf);
 
 #endif
index 749f80c21e220b4222a57c376499a3ad18f12ad9..e968843807320ea66a8b6421974fa182ab9d58aa 100644 (file)
@@ -1492,10 +1492,14 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
        if (len > sk->sk_sndbuf - 32)
                goto out;
 
-       if (len > SKB_MAX_ALLOC)
+       if (len > SKB_MAX_ALLOC) {
                data_len = min_t(size_t,
                                 len - SKB_MAX_ALLOC,
                                 MAX_SKB_FRAGS * PAGE_SIZE);
+               data_len = PAGE_ALIGN(data_len);
+
+               BUILD_BUG_ON(SKB_MAX_ALLOC < PAGE_SIZE);
+       }
 
        skb = sock_alloc_send_pskb(sk, len - data_len, data_len,
                                   msg->msg_flags & MSG_DONTWAIT, &err,
@@ -1670,6 +1674,8 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
 
                data_len = max_t(int, 0, size - SKB_MAX_HEAD(0));
 
+               data_len = min_t(size_t, size, PAGE_ALIGN(data_len));
+
                skb = sock_alloc_send_pskb(sk, size - data_len, data_len,
                                           msg->msg_flags & MSG_DONTWAIT, &err,
                                           get_order(UNIX_SKB_FRAGS_SZ));
index 16d08b39921071479456e2033c5e371d381175b7..405f3c4cf70ca3617a4101e1bad278b93d7ae1b7 100644 (file)
@@ -95,6 +95,43 @@ config CFG80211_CERTIFICATION_ONUS
          you are a wireless researcher and are working in a controlled
          and approved environment by your local regulatory agency.
 
+config CFG80211_REG_CELLULAR_HINTS
+       bool "cfg80211 regulatory support for cellular base station hints"
+       depends on CFG80211_CERTIFICATION_ONUS
+       ---help---
+         This option enables support for parsing regulatory hints
+         from cellular base stations. If enabled and at least one driver
+         claims support for parsing cellular base station hints the
+         regulatory core will allow and parse these regulatory hints.
+         The regulatory core will only apply these regulatory hints on
+         drivers that support this feature. You should only enable this
+         feature if you have tested and validated this feature on your
+         systems.
+
+config CFG80211_REG_RELAX_NO_IR
+       bool "cfg80211 support for NO_IR relaxation"
+       depends on CFG80211_CERTIFICATION_ONUS
+       ---help---
+        This option enables support for relaxation of the NO_IR flag for
+        situations that certain regulatory bodies have provided clarifications
+        on how relaxation can occur. This feature has an inherent dependency on
+        userspace features which must have been properly tested and as such is
+        not enabled by default.
+
+        A relaxation feature example is allowing the operation of a P2P group
+        owner (GO) on channels marked with NO_IR if there is an additional BSS
+        interface which associated to an AP which userspace assumes or confirms
+        to be an authorized master, i.e., with radar detection support and DFS
+        capabilities. However, note that in order to not create daisy chain
+        scenarios, this relaxation is not allowed in cases that the BSS client
+        is associated to P2P GO and in addition the P2P GO instantiated on
+        a channel due to this relaxation should not allow connection from
+        non P2P clients.
+
+        The regulatory core will apply these relaxations only for drivers that
+        support this feature by declaring the appropriate channel flags and
+        capabilities in their registration flow.
+
 config CFG80211_DEFAULT_PS
        bool "enable powersave by default"
        depends on CFG80211
index 3e02ade508d8804d4158a03ad5de474d64bf7594..bdad1f951561b3d8b7c75d8992e1c3c1a623f146 100644 (file)
@@ -6,8 +6,8 @@
 #include "rdev-ops.h"
 
 
-static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
-                             struct net_device *dev, bool notify)
+int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
+                      struct net_device *dev, bool notify)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        int err;
index 9c9501a35fb5c6a43a142132306998405e086774..992b34070bcb16f9fe4323bae293996524f1373c 100644 (file)
@@ -326,28 +326,57 @@ static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
 
 
 int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
-                                 const struct cfg80211_chan_def *chandef)
+                                 const struct cfg80211_chan_def *chandef,
+                                 enum nl80211_iftype iftype)
 {
        int width;
-       int r;
+       int ret;
 
        if (WARN_ON(!cfg80211_chandef_valid(chandef)))
                return -EINVAL;
 
-       width = cfg80211_chandef_get_width(chandef);
-       if (width < 0)
-               return -EINVAL;
+       switch (iftype) {
+       case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_P2P_GO:
+       case NL80211_IFTYPE_MESH_POINT:
+               width = cfg80211_chandef_get_width(chandef);
+               if (width < 0)
+                       return -EINVAL;
 
-       r = cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq1,
-                                           width);
-       if (r)
-               return r;
+               ret = cfg80211_get_chans_dfs_required(wiphy,
+                                                     chandef->center_freq1,
+                                                     width);
+               if (ret < 0)
+                       return ret;
+               else if (ret > 0)
+                       return BIT(chandef->width);
 
-       if (!chandef->center_freq2)
-               return 0;
+               if (!chandef->center_freq2)
+                       return 0;
+
+               ret = cfg80211_get_chans_dfs_required(wiphy,
+                                                     chandef->center_freq2,
+                                                     width);
+               if (ret < 0)
+                       return ret;
+               else if (ret > 0)
+                       return BIT(chandef->width);
 
-       return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2,
-                                              width);
+               break;
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_P2P_CLIENT:
+       case NL80211_IFTYPE_MONITOR:
+       case NL80211_IFTYPE_AP_VLAN:
+       case NL80211_IFTYPE_WDS:
+       case NL80211_IFTYPE_P2P_DEVICE:
+               break;
+       case NL80211_IFTYPE_UNSPECIFIED:
+       case NUM_NL80211_IFTYPES:
+               WARN_ON(1);
+       }
+
+       return 0;
 }
 EXPORT_SYMBOL(cfg80211_chandef_dfs_required);
 
@@ -587,12 +616,14 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
                width = 5;
                break;
        case NL80211_CHAN_WIDTH_10:
+               prohibited_flags |= IEEE80211_CHAN_NO_10MHZ;
                width = 10;
                break;
        case NL80211_CHAN_WIDTH_20:
                if (!ht_cap->ht_supported)
                        return false;
        case NL80211_CHAN_WIDTH_20_NOHT:
+               prohibited_flags |= IEEE80211_CHAN_NO_20MHZ;
                width = 20;
                break;
        case NL80211_CHAN_WIDTH_40:
@@ -661,17 +692,111 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
 }
 EXPORT_SYMBOL(cfg80211_chandef_usable);
 
+/*
+ * For GO only, check if the channel can be used under permissive conditions
+ * mandated by the some regulatory bodies, i.e., the channel is marked with
+ * IEEE80211_CHAN_GO_CONCURRENT and there is an additional station interface
+ * associated to an AP on the same channel or on the same UNII band
+ * (assuming that the AP is an authorized master).
+ * In addition allow the GO to operate on a channel on which indoor operation is
+ * allowed, iff we are currently operating in an indoor environment.
+ */
+static bool cfg80211_go_permissive_chan(struct cfg80211_registered_device *rdev,
+                                       struct ieee80211_channel *chan)
+{
+       struct wireless_dev *wdev_iter;
+       struct wiphy *wiphy = wiphy_idx_to_wiphy(rdev->wiphy_idx);
+
+       ASSERT_RTNL();
+
+       if (!config_enabled(CONFIG_CFG80211_REG_RELAX_NO_IR) ||
+           !(wiphy->regulatory_flags & REGULATORY_ENABLE_RELAX_NO_IR))
+               return false;
+
+       if (regulatory_indoor_allowed() &&
+           (chan->flags & IEEE80211_CHAN_INDOOR_ONLY))
+               return true;
+
+       if (!(chan->flags & IEEE80211_CHAN_GO_CONCURRENT))
+               return false;
+
+       /*
+        * Generally, it is possible to rely on another device/driver to allow
+        * the GO concurrent relaxation, however, since the device can further
+        * enforce the relaxation (by doing a similar verifications as this),
+        * and thus fail the GO instantiation, consider only the interfaces of
+        * the current registered device.
+        */
+       list_for_each_entry(wdev_iter, &rdev->wdev_list, list) {
+               struct ieee80211_channel *other_chan = NULL;
+               int r1, r2;
+
+               if (wdev_iter->iftype != NL80211_IFTYPE_STATION ||
+                   !netif_running(wdev_iter->netdev))
+                       continue;
+
+               wdev_lock(wdev_iter);
+               if (wdev_iter->current_bss)
+                       other_chan = wdev_iter->current_bss->pub.channel;
+               wdev_unlock(wdev_iter);
+
+               if (!other_chan)
+                       continue;
+
+               if (chan == other_chan)
+                       return true;
+
+               if (chan->band != IEEE80211_BAND_5GHZ)
+                       continue;
+
+               r1 = cfg80211_get_unii(chan->center_freq);
+               r2 = cfg80211_get_unii(other_chan->center_freq);
+
+               if (r1 != -EINVAL && r1 == r2) {
+                       /*
+                        * At some locations channels 149-165 are considered a
+                        * bundle, but at other locations, e.g., Indonesia,
+                        * channels 149-161 are considered a bundle while
+                        * channel 165 is left out and considered to be in a
+                        * different bundle. Thus, in case that there is a
+                        * station interface connected to an AP on channel 165,
+                        * it is assumed that channels 149-161 are allowed for
+                        * GO operations. However, having a station interface
+                        * connected to an AP on channels 149-161, does not
+                        * allow GO operation on channel 165.
+                        */
+                       if (chan->center_freq == 5825 &&
+                           other_chan->center_freq != 5825)
+                               continue;
+                       return true;
+               }
+       }
+
+       return false;
+}
+
 bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
-                            struct cfg80211_chan_def *chandef)
+                            struct cfg80211_chan_def *chandef,
+                            enum nl80211_iftype iftype)
 {
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        bool res;
        u32 prohibited_flags = IEEE80211_CHAN_DISABLED |
-                              IEEE80211_CHAN_NO_IR |
                               IEEE80211_CHAN_RADAR;
 
-       trace_cfg80211_reg_can_beacon(wiphy, chandef);
+       trace_cfg80211_reg_can_beacon(wiphy, chandef, iftype);
 
-       if (cfg80211_chandef_dfs_required(wiphy, chandef) > 0 &&
+       /*
+        * Under certain conditions suggested by the some regulatory bodies
+        * a GO can operate on channels marked with IEEE80211_NO_IR
+        * so set this flag only if such relaxations are not enabled and
+        * the conditions are not met.
+        */
+       if (iftype != NL80211_IFTYPE_P2P_GO ||
+           !cfg80211_go_permissive_chan(rdev, chandef->chan))
+               prohibited_flags |= IEEE80211_CHAN_NO_IR;
+
+       if (cfg80211_chandef_dfs_required(wiphy, chandef, iftype) > 0 &&
            cfg80211_chandef_dfs_available(wiphy, chandef)) {
                /* We can skip IEEE80211_CHAN_NO_IR if chandef dfs available */
                prohibited_flags = IEEE80211_CHAN_DISABLED;
@@ -701,6 +826,8 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
                        enum cfg80211_chan_mode *chanmode,
                        u8 *radar_detect)
 {
+       int ret;
+
        *chan = NULL;
        *chanmode = CHAN_MODE_UNDEFINED;
 
@@ -743,8 +870,11 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
                        *chan = wdev->chandef.chan;
                        *chanmode = CHAN_MODE_SHARED;
 
-                       if (cfg80211_chandef_dfs_required(wdev->wiphy,
-                                                         &wdev->chandef))
+                       ret = cfg80211_chandef_dfs_required(wdev->wiphy,
+                                                           &wdev->chandef,
+                                                           wdev->iftype);
+                       WARN_ON(ret < 0);
+                       if (ret > 0)
                                *radar_detect |= BIT(wdev->chandef.width);
                }
                return;
@@ -753,8 +883,11 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
                        *chan = wdev->chandef.chan;
                        *chanmode = CHAN_MODE_SHARED;
 
-                       if (cfg80211_chandef_dfs_required(wdev->wiphy,
-                                                         &wdev->chandef))
+                       ret = cfg80211_chandef_dfs_required(wdev->wiphy,
+                                                           &wdev->chandef,
+                                                           wdev->iftype);
+                       WARN_ON(ret < 0);
+                       if (ret > 0)
                                *radar_detect |= BIT(wdev->chandef.width);
                }
                return;
index 086cddd03ba6edd79d1609ecf713146bc756c1ff..a1c40654dd9b1ca9b47bbaef6682ee6d49147e66 100644 (file)
@@ -69,7 +69,7 @@ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx)
 
 int get_wiphy_idx(struct wiphy *wiphy)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        return rdev->wiphy_idx;
 }
@@ -130,7 +130,7 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
                            newname))
                pr_err("failed to rename debugfs dir to %s!\n", newname);
 
-       nl80211_notify_dev_rename(rdev);
+       nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
 
        return 0;
 }
@@ -210,15 +210,12 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
        }
 }
 
-static int cfg80211_rfkill_set_block(void *data, bool blocked)
+void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy)
 {
-       struct cfg80211_registered_device *rdev = data;
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct wireless_dev *wdev;
 
-       if (!blocked)
-               return 0;
-
-       rtnl_lock();
+       ASSERT_RTNL();
 
        list_for_each_entry(wdev, &rdev->wdev_list, list) {
                if (wdev->netdev) {
@@ -234,7 +231,18 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked)
                        break;
                }
        }
+}
+EXPORT_SYMBOL_GPL(cfg80211_shutdown_all_interfaces);
+
+static int cfg80211_rfkill_set_block(void *data, bool blocked)
+{
+       struct cfg80211_registered_device *rdev = data;
+
+       if (!blocked)
+               return 0;
 
+       rtnl_lock();
+       cfg80211_shutdown_all_interfaces(&rdev->wiphy);
        rtnl_unlock();
 
        return 0;
@@ -260,6 +268,45 @@ static void cfg80211_event_work(struct work_struct *work)
        rtnl_unlock();
 }
 
+void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev)
+{
+       struct cfg80211_iface_destroy *item;
+
+       ASSERT_RTNL();
+
+       spin_lock_irq(&rdev->destroy_list_lock);
+       while ((item = list_first_entry_or_null(&rdev->destroy_list,
+                                               struct cfg80211_iface_destroy,
+                                               list))) {
+               struct wireless_dev *wdev, *tmp;
+               u32 nlportid = item->nlportid;
+
+               list_del(&item->list);
+               kfree(item);
+               spin_unlock_irq(&rdev->destroy_list_lock);
+
+               list_for_each_entry_safe(wdev, tmp, &rdev->wdev_list, list) {
+                       if (nlportid == wdev->owner_nlportid)
+                               rdev_del_virtual_intf(rdev, wdev);
+               }
+
+               spin_lock_irq(&rdev->destroy_list_lock);
+       }
+       spin_unlock_irq(&rdev->destroy_list_lock);
+}
+
+static void cfg80211_destroy_iface_wk(struct work_struct *work)
+{
+       struct cfg80211_registered_device *rdev;
+
+       rdev = container_of(work, struct cfg80211_registered_device,
+                           destroy_work);
+
+       rtnl_lock();
+       cfg80211_destroy_ifaces(rdev);
+       rtnl_unlock();
+}
+
 /* exported functions */
 
 struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
@@ -318,6 +365,10 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
        rdev->wiphy.dev.class = &ieee80211_class;
        rdev->wiphy.dev.platform_data = rdev;
 
+       INIT_LIST_HEAD(&rdev->destroy_list);
+       spin_lock_init(&rdev->destroy_list_lock);
+       INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk);
+
 #ifdef CONFIG_CFG80211_DEFAULT_PS
        rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
 #endif
@@ -351,6 +402,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
        rdev->wiphy.rts_threshold = (u32) -1;
        rdev->wiphy.coverage_class = 0;
 
+       rdev->wiphy.max_num_csa_counters = 1;
+
        return &rdev->wiphy;
 }
 EXPORT_SYMBOL(wiphy_new);
@@ -396,10 +449,7 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)
                for (j = 0; j < c->n_limits; j++) {
                        u16 types = c->limits[j].types;
 
-                       /*
-                        * interface types shouldn't overlap, this is
-                        * used in cfg80211_can_change_interface()
-                        */
+                       /* interface types shouldn't overlap */
                        if (WARN_ON(types & all_iftypes))
                                return -EINVAL;
                        all_iftypes |= types;
@@ -435,7 +485,7 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)
 
 int wiphy_register(struct wiphy *wiphy)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        int res;
        enum ieee80211_band band;
        struct ieee80211_supported_band *sband;
@@ -610,13 +660,15 @@ int wiphy_register(struct wiphy *wiphy)
                return res;
        }
 
+       nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
+
        return 0;
 }
 EXPORT_SYMBOL(wiphy_register);
 
 void wiphy_rfkill_start_polling(struct wiphy *wiphy)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        if (!rdev->ops->rfkill_poll)
                return;
@@ -627,7 +679,7 @@ EXPORT_SYMBOL(wiphy_rfkill_start_polling);
 
 void wiphy_rfkill_stop_polling(struct wiphy *wiphy)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        rfkill_pause_polling(rdev->rfkill);
 }
@@ -635,7 +687,7 @@ EXPORT_SYMBOL(wiphy_rfkill_stop_polling);
 
 void wiphy_unregister(struct wiphy *wiphy)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        wait_event(rdev->dev_wait, ({
                int __count;
@@ -648,9 +700,10 @@ void wiphy_unregister(struct wiphy *wiphy)
                rfkill_unregister(rdev->rfkill);
 
        rtnl_lock();
+       nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);
        rdev->wiphy.registered = false;
 
-       BUG_ON(!list_empty(&rdev->wdev_list));
+       WARN_ON(!list_empty(&rdev->wdev_list));
 
        /*
         * First remove the hardware from everywhere, this makes
@@ -675,6 +728,7 @@ void wiphy_unregister(struct wiphy *wiphy)
        cancel_work_sync(&rdev->conn_work);
        flush_work(&rdev->event_work);
        cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
+       flush_work(&rdev->destroy_work);
 
 #ifdef CONFIG_PM
        if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
@@ -707,7 +761,7 @@ EXPORT_SYMBOL(wiphy_free);
 
 void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        if (rfkill_set_hw_state(rdev->rfkill, blocked))
                schedule_work(&rdev->rfkill_sync);
@@ -716,7 +770,7 @@ EXPORT_SYMBOL(wiphy_rfkill_set_hw_state);
 
 void cfg80211_unregister_wdev(struct wireless_dev *wdev)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
        ASSERT_RTNL();
 
@@ -751,23 +805,23 @@ void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
                rdev->num_running_monitor_ifaces += num;
 }
 
-void cfg80211_leave(struct cfg80211_registered_device *rdev,
-                   struct wireless_dev *wdev)
+void __cfg80211_leave(struct cfg80211_registered_device *rdev,
+                     struct wireless_dev *wdev)
 {
        struct net_device *dev = wdev->netdev;
 
        ASSERT_RTNL();
+       ASSERT_WDEV_LOCK(wdev);
 
        switch (wdev->iftype) {
        case NL80211_IFTYPE_ADHOC:
-               cfg80211_leave_ibss(rdev, dev, true);
+               __cfg80211_leave_ibss(rdev, dev, true);
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_STATION:
                if (rdev->sched_scan_req && dev == rdev->sched_scan_req->dev)
                        __cfg80211_stop_sched_scan(rdev, false);
 
-               wdev_lock(wdev);
 #ifdef CONFIG_CFG80211_WEXT
                kfree(wdev->wext.ie);
                wdev->wext.ie = NULL;
@@ -776,32 +830,60 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev,
 #endif
                cfg80211_disconnect(rdev, dev,
                                    WLAN_REASON_DEAUTH_LEAVING, true);
-               wdev_unlock(wdev);
                break;
        case NL80211_IFTYPE_MESH_POINT:
-               cfg80211_leave_mesh(rdev, dev);
+               __cfg80211_leave_mesh(rdev, dev);
                break;
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_P2P_GO:
-               cfg80211_stop_ap(rdev, dev, true);
+               __cfg80211_stop_ap(rdev, dev, true);
                break;
        default:
                break;
        }
 }
 
+void cfg80211_leave(struct cfg80211_registered_device *rdev,
+                   struct wireless_dev *wdev)
+{
+       wdev_lock(wdev);
+       __cfg80211_leave(rdev, wdev);
+       wdev_unlock(wdev);
+}
+
+void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev,
+                        gfp_t gfp)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+       struct cfg80211_event *ev;
+       unsigned long flags;
+
+       trace_cfg80211_stop_iface(wiphy, wdev);
+
+       ev = kzalloc(sizeof(*ev), gfp);
+       if (!ev)
+               return;
+
+       ev->type = EVENT_STOPPED;
+
+       spin_lock_irqsave(&wdev->event_lock, flags);
+       list_add_tail(&ev->list, &wdev->event_list);
+       spin_unlock_irqrestore(&wdev->event_lock, flags);
+       queue_work(cfg80211_wq, &rdev->event_work);
+}
+EXPORT_SYMBOL(cfg80211_stop_iface);
+
 static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                                         unsigned long state, void *ptr)
 {
        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_registered_device *rdev;
-       int ret;
 
        if (!wdev)
                return NOTIFY_DONE;
 
-       rdev = wiphy_to_dev(wdev->wiphy);
+       rdev = wiphy_to_rdev(wdev->wiphy);
 
        WARN_ON(wdev->iftype == NL80211_IFTYPE_UNSPECIFIED);
 
@@ -959,13 +1041,14 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
        case NETDEV_PRE_UP:
                if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype)))
                        return notifier_from_errno(-EOPNOTSUPP);
-               ret = cfg80211_can_add_interface(rdev, wdev->iftype);
-               if (ret)
-                       return notifier_from_errno(ret);
+               if (rfkill_blocked(rdev->rfkill))
+                       return notifier_from_errno(-ERFKILL);
                break;
+       default:
+               return NOTIFY_DONE;
        }
 
-       return NOTIFY_DONE;
+       return NOTIFY_OK;
 }
 
 static struct notifier_block cfg80211_netdev_notifier = {
index 5b1fdcadd46985548f4a04f4f64ddaccbc935661..e9afbf10e756bd3a1ec81a6b6b7aa229d0688be7 100644 (file)
@@ -80,13 +80,17 @@ struct cfg80211_registered_device {
 
        struct cfg80211_coalesce *coalesce;
 
+       spinlock_t destroy_list_lock;
+       struct list_head destroy_list;
+       struct work_struct destroy_work;
+
        /* must be last because of the way we do wiphy_priv(),
         * and it should at least be aligned to NETDEV_ALIGN */
        struct wiphy wiphy __aligned(NETDEV_ALIGN);
 };
 
 static inline
-struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy)
+struct cfg80211_registered_device *wiphy_to_rdev(struct wiphy *wiphy)
 {
        BUG_ON(!wiphy);
        return container_of(wiphy, struct cfg80211_registered_device, wiphy);
@@ -181,6 +185,7 @@ enum cfg80211_event_type {
        EVENT_ROAMED,
        EVENT_DISCONNECTED,
        EVENT_IBSS_JOINED,
+       EVENT_STOPPED,
 };
 
 struct cfg80211_event {
@@ -232,6 +237,13 @@ struct cfg80211_beacon_registration {
        u32 nlportid;
 };
 
+struct cfg80211_iface_destroy {
+       struct list_head list;
+       u32 nlportid;
+};
+
+void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev);
+
 /* free object */
 void cfg80211_dev_free(struct cfg80211_registered_device *rdev);
 
@@ -240,8 +252,8 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
 
 void ieee80211_set_bitrate_flags(struct wiphy *wiphy);
 
-void cfg80211_bss_expire(struct cfg80211_registered_device *dev);
-void cfg80211_bss_age(struct cfg80211_registered_device *dev,
+void cfg80211_bss_expire(struct cfg80211_registered_device *rdev);
+void cfg80211_bss_age(struct cfg80211_registered_device *rdev,
                       unsigned long age_secs);
 
 /* IBSS */
@@ -270,6 +282,8 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
                       struct net_device *dev,
                       struct mesh_setup *setup,
                       const struct mesh_config *conf);
+int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
+                         struct net_device *dev);
 int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
                        struct net_device *dev);
 int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
@@ -277,6 +291,8 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
                              struct cfg80211_chan_def *chandef);
 
 /* AP */
+int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
+                      struct net_device *dev, bool notify);
 int cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
                     struct net_device *dev, bool notify);
 
@@ -401,35 +417,6 @@ unsigned int
 cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
                              const struct cfg80211_chan_def *chandef);
 
-static inline int
-cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
-                             struct wireless_dev *wdev,
-                             enum nl80211_iftype iftype)
-{
-       return cfg80211_can_use_iftype_chan(rdev, wdev, iftype, NULL,
-                                           CHAN_MODE_UNDEFINED, 0);
-}
-
-static inline int
-cfg80211_can_add_interface(struct cfg80211_registered_device *rdev,
-                          enum nl80211_iftype iftype)
-{
-       if (rfkill_blocked(rdev->rfkill))
-               return -ERFKILL;
-
-       return cfg80211_can_change_interface(rdev, NULL, iftype);
-}
-
-static inline int
-cfg80211_can_use_chan(struct cfg80211_registered_device *rdev,
-                     struct wireless_dev *wdev,
-                     struct ieee80211_channel *chan,
-                     enum cfg80211_chan_mode chanmode)
-{
-       return cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
-                                           chan, chanmode, 0);
-}
-
 static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
 {
        unsigned long end = jiffies;
@@ -459,6 +446,8 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
 void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
                               enum nl80211_iftype iftype, int num);
 
+void __cfg80211_leave(struct cfg80211_registered_device *rdev,
+                     struct wireless_dev *wdev);
 void cfg80211_leave(struct cfg80211_registered_device *rdev,
                    struct wireless_dev *wdev);
 
index e37862f1b1270d8e2056fb6289f01bf69b583720..d4860bfc020e5a1c43758e8cca6e9508908344be 100644 (file)
@@ -43,7 +43,7 @@ static void cfg80211_get_ringparam(struct net_device *dev,
                                   struct ethtool_ringparam *rp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
        memset(rp, 0, sizeof(*rp));
 
@@ -56,7 +56,7 @@ static int cfg80211_set_ringparam(struct net_device *dev,
                                  struct ethtool_ringparam *rp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
        if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0)
                return -EINVAL;
@@ -70,7 +70,7 @@ static int cfg80211_set_ringparam(struct net_device *dev,
 static int cfg80211_get_sset_count(struct net_device *dev, int sset)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        if (rdev->ops->get_et_sset_count)
                return rdev_get_et_sset_count(rdev, dev, sset);
        return -EOPNOTSUPP;
@@ -80,7 +80,7 @@ static void cfg80211_get_stats(struct net_device *dev,
                               struct ethtool_stats *stats, u64 *data)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        if (rdev->ops->get_et_stats)
                rdev_get_et_stats(rdev, dev, stats, data);
 }
@@ -88,7 +88,7 @@ static void cfg80211_get_stats(struct net_device *dev,
 static void cfg80211_get_strings(struct net_device *dev, u32 sset, u8 *data)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        if (rdev->ops->get_et_strings)
                rdev_get_et_strings(rdev, dev, sset, data);
 }
index b35da8dc85deff4640ecb0975cc829326b68034c..40c37fc5b67cb76325dcd4f0041c1ad3cf5ebf66 100644 (file)
@@ -68,17 +68,7 @@ function parse_reg_rule()
        sub(/,/, "", units)
        dfs_cac = $9
        if (units == "mW") {
-               if (power == 100) {
-                       power = 20
-               } else if (power == 200) {
-                       power = 23
-               } else if (power == 500) {
-                       power = 27
-               } else if (power == 1000) {
-                       power = 30
-               } else {
-                       print "Unknown power value in database!"
-               }
+               power = 10 * log(power)/log(10)
        } else {
                dfs_cac = $8
        }
@@ -117,7 +107,7 @@ function parse_reg_rule()
 
        }
        flags = flags "0"
-       printf "\t\tREG_RULE_EXT(%d, %d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, dfs_cac, flags
+       printf "\t\tREG_RULE_EXT(%d, %d, %d, %d, %.0f, %d, %s),\n", start, end, bw, gain, power, dfs_cac, flags
        rules++
 }
 
index a6b5bdad039c7450f276d1e56994661e2e41952c..8f345da3ea5f4e0767ae6b1de539f18954707148 100644 (file)
@@ -45,7 +45,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
 
        cfg80211_upload_connect_keys(wdev);
 
-       nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid,
+       nl80211_send_ibss_bssid(wiphy_to_rdev(wdev->wiphy), dev, bssid,
                                GFP_KERNEL);
 #ifdef CONFIG_CFG80211_WEXT
        memset(&wrqu, 0, sizeof(wrqu));
@@ -58,7 +58,7 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
                          struct ieee80211_channel *channel, gfp_t gfp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_event *ev;
        unsigned long flags;
 
@@ -88,8 +88,6 @@ static int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
                                struct cfg80211_cached_keys *connkeys)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct ieee80211_channel *check_chan;
-       u8 radar_detect_width = 0;
        int err;
 
        ASSERT_WDEV_LOCK(wdev);
@@ -126,28 +124,6 @@ static int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
 #ifdef CONFIG_CFG80211_WEXT
        wdev->wext.ibss.chandef = params->chandef;
 #endif
-       check_chan = params->chandef.chan;
-       if (params->userspace_handles_dfs) {
-               /* Check for radar even if the current channel is not
-                * a radar channel - it might decide to change to DFS
-                * channel later.
-                */
-               radar_detect_width = BIT(params->chandef.width);
-       }
-
-       err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
-                                          check_chan,
-                                          (params->channel_fixed &&
-                                           !radar_detect_width)
-                                          ? CHAN_MODE_SHARED
-                                          : CHAN_MODE_EXCLUSIVE,
-                                          radar_detect_width);
-
-       if (err) {
-               wdev->connect_keys = NULL;
-               return err;
-       }
-
        err = rdev_join_ibss(rdev, dev, params);
        if (err) {
                wdev->connect_keys = NULL;
@@ -180,7 +156,7 @@ int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
 static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        int i;
 
        ASSERT_WDEV_LOCK(wdev);
@@ -335,7 +311,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
                               struct iw_freq *wextfreq, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct ieee80211_channel *chan = NULL;
        int err, freq;
 
@@ -346,7 +322,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
        if (!rdev->ops->join_ibss)
                return -EOPNOTSUPP;
 
-       freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
+       freq = cfg80211_wext_freq(wextfreq);
        if (freq < 0)
                return freq;
 
@@ -420,7 +396,7 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev,
                                struct iw_point *data, char *ssid)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        size_t len = data->length;
        int err;
 
@@ -444,8 +420,8 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev,
        if (len > 0 && ssid[len - 1] == '\0')
                len--;
 
+       memcpy(wdev->ssid, ssid, len);
        wdev->wext.ibss.ssid = wdev->ssid;
-       memcpy(wdev->wext.ibss.ssid, ssid, len);
        wdev->wext.ibss.ssid_len = len;
 
        wdev_lock(wdev);
@@ -487,7 +463,7 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev,
                             struct sockaddr *ap_addr, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        u8 *bssid = ap_addr->sa_data;
        int err;
 
@@ -505,6 +481,9 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev,
        if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
                bssid = NULL;
 
+       if (bssid && !is_valid_ether_addr(bssid))
+               return -EINVAL;
+
        /* both automatic */
        if (!bssid && !wdev->wext.ibss.bssid)
                return 0;
index 5af5cc6b2c4c2406475a3063a69eef80cc14691f..092300b30c372ddc03115638a83bc65ce7a49997 100644 (file)
@@ -99,7 +99,6 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
                         const struct mesh_config *conf)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       u8 radar_detect_width = 0;
        int err;
 
        BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN);
@@ -175,22 +174,10 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
                                                               scan_width);
        }
 
-       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef))
+       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef,
+                                    NL80211_IFTYPE_MESH_POINT))
                return -EINVAL;
 
-       err = cfg80211_chandef_dfs_required(wdev->wiphy, &setup->chandef);
-       if (err < 0)
-               return err;
-       if (err)
-               radar_detect_width = BIT(setup->chandef.width);
-
-       err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
-                                          setup->chandef.chan,
-                                          CHAN_MODE_SHARED,
-                                          radar_detect_width);
-       if (err)
-               return err;
-
        err = rdev_join_mesh(rdev, dev, conf, setup);
        if (!err) {
                memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len);
@@ -236,17 +223,6 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
                if (!netif_running(wdev->netdev))
                        return -ENETDOWN;
 
-               /* cfg80211_can_use_chan() calls
-                * cfg80211_can_use_iftype_chan() with no radar
-                * detection, so if we're trying to use a radar
-                * channel here, something is wrong.
-                */
-               WARN_ON_ONCE(chandef->chan->flags & IEEE80211_CHAN_RADAR);
-               err = cfg80211_can_use_chan(rdev, wdev, chandef->chan,
-                                           CHAN_MODE_SHARED);
-               if (err)
-                       return err;
-
                err = rdev_libertas_set_mesh_channel(rdev, wdev->netdev,
                                                     chandef->chan);
                if (!err)
@@ -262,8 +238,8 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
        return 0;
 }
 
-static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
-                                struct net_device *dev)
+int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
+                         struct net_device *dev)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        int err;
index c52ff59a3e96d7cabb892bff220b86c580069a43..266766b8d80b61455565cc43779a6e229ed7710d 100644 (file)
@@ -23,7 +23,7 @@ void cfg80211_rx_assoc_resp(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);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(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);
@@ -54,7 +54,7 @@ EXPORT_SYMBOL(cfg80211_rx_assoc_resp);
 static void cfg80211_process_auth(struct wireless_dev *wdev,
                                  const u8 *buf, size_t len)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
        nl80211_send_rx_auth(rdev, wdev->netdev, buf, len, GFP_KERNEL);
        cfg80211_sme_rx_auth(wdev, buf, len);
@@ -63,7 +63,7 @@ static void cfg80211_process_auth(struct wireless_dev *wdev,
 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 cfg80211_registered_device *rdev = wiphy_to_rdev(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);
@@ -82,7 +82,7 @@ static void cfg80211_process_deauth(struct wireless_dev *wdev,
 static void cfg80211_process_disassoc(struct wireless_dev *wdev,
                                      const u8 *buf, size_t len)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(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);
@@ -123,7 +123,7 @@ 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);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        trace_cfg80211_send_auth_timeout(dev, addr);
 
@@ -136,7 +136,7 @@ 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);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        trace_cfg80211_send_assoc_timeout(dev, bss->bssid);
 
@@ -172,7 +172,7 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
                                  const u8 *tsc, gfp_t gfp)
 {
        struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 #ifdef CONFIG_CFG80211_WEXT
        union iwreq_data wrqu;
        char *buf = kmalloc(128, gfp);
@@ -233,14 +233,8 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
        if (!req.bss)
                return -ENOENT;
 
-       err = cfg80211_can_use_chan(rdev, wdev, req.bss->channel,
-                                   CHAN_MODE_SHARED);
-       if (err)
-               goto out;
-
        err = rdev_auth(rdev, dev, &req);
 
-out:
        cfg80211_put_bss(&rdev->wiphy, req.bss);
        return err;
 }
@@ -306,16 +300,10 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
        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)
+       else
                cfg80211_put_bss(&rdev->wiphy, req->bss);
 
        return err;
@@ -414,7 +402,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
                                int match_len)
 {
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct cfg80211_mgmt_registration *reg, *nreg;
        int err = 0;
        u16 mgmt_type;
@@ -473,7 +461,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
 void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
 {
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct cfg80211_mgmt_registration *reg, *tmp;
 
        spin_lock_bh(&wdev->mgmt_registrations_lock);
@@ -620,7 +608,7 @@ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,
                      const u8 *buf, size_t len, u32 flags, gfp_t gfp)
 {
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct cfg80211_mgmt_registration *reg;
        const struct ieee80211_txrx_stypes *stypes =
                &wiphy->mgmt_stypes[wdev->iftype];
@@ -739,7 +727,7 @@ void cfg80211_radar_event(struct wiphy *wiphy,
                          struct cfg80211_chan_def *chandef,
                          gfp_t gfp)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        unsigned long timeout;
 
        trace_cfg80211_radar_event(wiphy, chandef);
@@ -764,7 +752,7 @@ void cfg80211_cac_event(struct net_device *netdev,
 {
        struct wireless_dev *wdev = netdev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        unsigned long timeout;
 
        trace_cfg80211_cac_event(netdev, event);
index 052c1bf8ffaceb92d3f117231a46fca78ed30216..ba4f1723c83ad2eb094c15a8794fedb7b6f7a404 100644 (file)
@@ -168,8 +168,8 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
                netdev = __dev_get_by_index(netns, ifindex);
                if (netdev) {
                        if (netdev->ieee80211_ptr)
-                               tmp = wiphy_to_dev(
-                                               netdev->ieee80211_ptr->wiphy);
+                               tmp = wiphy_to_rdev(
+                                       netdev->ieee80211_ptr->wiphy);
                        else
                                tmp = NULL;
 
@@ -371,8 +371,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_CH_SWITCH_COUNT] = { .type = NLA_U32 },
        [NL80211_ATTR_CH_SWITCH_BLOCK_TX] = { .type = NLA_FLAG },
        [NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED },
-       [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 },
-       [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 },
+       [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_BINARY },
+       [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_BINARY },
        [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY },
        [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
        [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG },
@@ -385,6 +385,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN },
        [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 },
        [NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 },
+       [NL80211_ATTR_IFACE_SOCKET_OWNER] = { .type = NLA_FLAG },
+       [NL80211_ATTR_CSA_C_OFFSETS_TX] = { .type = NLA_BINARY },
 };
 
 /* policy for the key attributes */
@@ -484,7 +486,7 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
                        err = PTR_ERR(*wdev);
                        goto out_unlock;
                }
-               *rdev = wiphy_to_dev((*wdev)->wiphy);
+               *rdev = wiphy_to_rdev((*wdev)->wiphy);
                /* 0 is the first index - add 1 to parse only once */
                cb->args[0] = (*rdev)->wiphy_idx + 1;
                cb->args[1] = (*wdev)->identifier;
@@ -497,7 +499,7 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
                        err = -ENODEV;
                        goto out_unlock;
                }
-               *rdev = wiphy_to_dev(wiphy);
+               *rdev = wiphy_to_rdev(wiphy);
                *wdev = NULL;
 
                list_for_each_entry(tmp, &(*rdev)->wdev_list, list) {
@@ -566,6 +568,13 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
                                   struct ieee80211_channel *chan,
                                   bool large)
 {
+       /* Some channels must be completely excluded from the
+        * list to protect old user-space tools from breaking
+        */
+       if (!large && chan->flags &
+           (IEEE80211_CHAN_NO_10MHZ | IEEE80211_CHAN_NO_20MHZ))
+               return 0;
+
        if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_FREQ,
                        chan->center_freq))
                goto nla_put_failure;
@@ -613,6 +622,18 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
                if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) &&
                    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_160MHZ))
                        goto nla_put_failure;
+               if ((chan->flags & IEEE80211_CHAN_INDOOR_ONLY) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_INDOOR_ONLY))
+                       goto nla_put_failure;
+               if ((chan->flags & IEEE80211_CHAN_GO_CONCURRENT) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_GO_CONCURRENT))
+                       goto nla_put_failure;
+               if ((chan->flags & IEEE80211_CHAN_NO_20MHZ) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_20MHZ))
+                       goto nla_put_failure;
+               if ((chan->flags & IEEE80211_CHAN_NO_10MHZ) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_10MHZ))
+                       goto nla_put_failure;
        }
 
        if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
@@ -950,8 +971,10 @@ static int nl80211_put_iface_combinations(struct wiphy *wiphy,
                                c->max_interfaces))
                        goto nla_put_failure;
                if (large &&
-                   nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
-                               c->radar_detect_widths))
+                   (nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
+                               c->radar_detect_widths) ||
+                    nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_REGIONS,
+                               c->radar_detect_regions)))
                        goto nla_put_failure;
 
                nla_nest_end(msg, nl_combi);
@@ -1006,42 +1029,42 @@ static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev,
 }
 
 static int nl80211_send_wowlan(struct sk_buff *msg,
-                              struct cfg80211_registered_device *dev,
+                              struct cfg80211_registered_device *rdev,
                               bool large)
 {
        struct nlattr *nl_wowlan;
 
-       if (!dev->wiphy.wowlan)
+       if (!rdev->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 (((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_ANY) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
-           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_DISCONNECT) &&
+           ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_DISCONNECT) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
-           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT) &&
+           ((rdev->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) &&
+           ((rdev->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) &&
+           ((rdev->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) &&
+           ((rdev->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) &&
+           ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
-           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
+           ((rdev->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 (rdev->wiphy.wowlan->n_patterns) {
                struct nl80211_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 = rdev->wiphy.wowlan->n_patterns,
+                       .min_pattern_len = rdev->wiphy.wowlan->pattern_min_len,
+                       .max_pattern_len = rdev->wiphy.wowlan->pattern_max_len,
+                       .max_pkt_offset = rdev->wiphy.wowlan->max_pkt_offset,
                };
 
                if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
@@ -1049,7 +1072,7 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
                        return -ENOBUFS;
        }
 
-       if (large && nl80211_send_wowlan_tcp_caps(dev, msg))
+       if (large && nl80211_send_wowlan_tcp_caps(rdev, msg))
                return -ENOBUFS;
 
        nla_nest_end(msg, nl_wowlan);
@@ -1059,19 +1082,19 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
 #endif
 
 static int nl80211_send_coalesce(struct sk_buff *msg,
-                                struct cfg80211_registered_device *dev)
+                                struct cfg80211_registered_device *rdev)
 {
        struct nl80211_coalesce_rule_support rule;
 
-       if (!dev->wiphy.coalesce)
+       if (!rdev->wiphy.coalesce)
                return 0;
 
-       rule.max_rules = dev->wiphy.coalesce->n_rules;
-       rule.max_delay = dev->wiphy.coalesce->max_delay;
-       rule.pat.max_patterns = dev->wiphy.coalesce->n_patterns;
-       rule.pat.min_pattern_len = dev->wiphy.coalesce->pattern_min_len;
-       rule.pat.max_pattern_len = dev->wiphy.coalesce->pattern_max_len;
-       rule.pat.max_pkt_offset = dev->wiphy.coalesce->max_pkt_offset;
+       rule.max_rules = rdev->wiphy.coalesce->n_rules;
+       rule.max_delay = rdev->wiphy.coalesce->max_delay;
+       rule.pat.max_patterns = rdev->wiphy.coalesce->n_patterns;
+       rule.pat.min_pattern_len = rdev->wiphy.coalesce->pattern_min_len;
+       rule.pat.max_pattern_len = rdev->wiphy.coalesce->pattern_max_len;
+       rule.pat.max_pkt_offset = rdev->wiphy.coalesce->max_pkt_offset;
 
        if (nla_put(msg, NL80211_ATTR_COALESCE_RULE, sizeof(rule), &rule))
                return -ENOBUFS;
@@ -1202,7 +1225,8 @@ struct nl80211_dump_wiphy_state {
        bool split;
 };
 
-static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
+static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
+                             enum nl80211_commands cmd,
                              struct sk_buff *msg, u32 portid, u32 seq,
                              int flags, struct nl80211_dump_wiphy_state *state)
 {
@@ -1214,63 +1238,66 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
        struct ieee80211_channel *chan;
        int i;
        const struct ieee80211_txrx_stypes *mgmt_stypes =
-                               dev->wiphy.mgmt_stypes;
+                               rdev->wiphy.mgmt_stypes;
        u32 features;
 
-       hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY);
+       hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
        if (!hdr)
                return -ENOBUFS;
 
        if (WARN_ON(!state))
                return -EINVAL;
 
-       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) ||
+       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
            nla_put_string(msg, NL80211_ATTR_WIPHY_NAME,
-                          wiphy_name(&dev->wiphy)) ||
+                          wiphy_name(&rdev->wiphy)) ||
            nla_put_u32(msg, NL80211_ATTR_GENERATION,
                        cfg80211_rdev_list_generation))
                goto nla_put_failure;
 
+       if (cmd != NL80211_CMD_NEW_WIPHY)
+               goto finish;
+
        switch (state->split_start) {
        case 0:
                if (nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
-                              dev->wiphy.retry_short) ||
+                              rdev->wiphy.retry_short) ||
                    nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
-                              dev->wiphy.retry_long) ||
+                              rdev->wiphy.retry_long) ||
                    nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
-                               dev->wiphy.frag_threshold) ||
+                               rdev->wiphy.frag_threshold) ||
                    nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
-                               dev->wiphy.rts_threshold) ||
+                               rdev->wiphy.rts_threshold) ||
                    nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
-                              dev->wiphy.coverage_class) ||
+                              rdev->wiphy.coverage_class) ||
                    nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
-                              dev->wiphy.max_scan_ssids) ||
+                              rdev->wiphy.max_scan_ssids) ||
                    nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
-                              dev->wiphy.max_sched_scan_ssids) ||
+                              rdev->wiphy.max_sched_scan_ssids) ||
                    nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
-                               dev->wiphy.max_scan_ie_len) ||
+                               rdev->wiphy.max_scan_ie_len) ||
                    nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
-                               dev->wiphy.max_sched_scan_ie_len) ||
+                               rdev->wiphy.max_sched_scan_ie_len) ||
                    nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS,
-                              dev->wiphy.max_match_sets))
+                              rdev->wiphy.max_match_sets))
                        goto nla_put_failure;
 
-               if ((dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
                    nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN))
                        goto nla_put_failure;
-               if ((dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
                    nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH))
                        goto nla_put_failure;
-               if ((dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
                    nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD))
                        goto nla_put_failure;
-               if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
                    nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT))
                        goto nla_put_failure;
-               if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
                    nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT))
                        goto nla_put_failure;
-               if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
                    nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
                        goto nla_put_failure;
                state->split_start++;
@@ -1278,35 +1305,35 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                        break;
        case 1:
                if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
-                           sizeof(u32) * dev->wiphy.n_cipher_suites,
-                           dev->wiphy.cipher_suites))
+                           sizeof(u32) * rdev->wiphy.n_cipher_suites,
+                           rdev->wiphy.cipher_suites))
                        goto nla_put_failure;
 
                if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
-                              dev->wiphy.max_num_pmkids))
+                              rdev->wiphy.max_num_pmkids))
                        goto nla_put_failure;
 
-               if ((dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
                    nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE))
                        goto nla_put_failure;
 
                if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
-                               dev->wiphy.available_antennas_tx) ||
+                               rdev->wiphy.available_antennas_tx) ||
                    nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
-                               dev->wiphy.available_antennas_rx))
+                               rdev->wiphy.available_antennas_rx))
                        goto nla_put_failure;
 
-               if ((dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&
                    nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
-                               dev->wiphy.probe_resp_offload))
+                               rdev->wiphy.probe_resp_offload))
                        goto nla_put_failure;
 
-               if ((dev->wiphy.available_antennas_tx ||
-                    dev->wiphy.available_antennas_rx) &&
-                   dev->ops->get_antenna) {
+               if ((rdev->wiphy.available_antennas_tx ||
+                    rdev->wiphy.available_antennas_rx) &&
+                   rdev->ops->get_antenna) {
                        u32 tx_ant = 0, rx_ant = 0;
                        int res;
-                       res = rdev_get_antenna(dev, &tx_ant, &rx_ant);
+                       res = rdev_get_antenna(rdev, &tx_ant, &rx_ant);
                        if (!res) {
                                if (nla_put_u32(msg,
                                                NL80211_ATTR_WIPHY_ANTENNA_TX,
@@ -1323,7 +1350,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                        break;
        case 2:
                if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
-                                       dev->wiphy.interface_modes))
+                                       rdev->wiphy.interface_modes))
                                goto nla_put_failure;
                state->split_start++;
                if (state->split)
@@ -1337,7 +1364,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                     band < IEEE80211_NUM_BANDS; band++) {
                        struct ieee80211_supported_band *sband;
 
-                       sband = dev->wiphy.bands[band];
+                       sband = rdev->wiphy.bands[band];
 
                        if (!sband)
                                continue;
@@ -1414,7 +1441,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                i = 0;
 #define CMD(op, n)                                                     \
                 do {                                                   \
-                       if (dev->ops->op) {                             \
+                       if (rdev->ops->op) {                            \
                                i++;                                    \
                                if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \
                                        goto nla_put_failure;           \
@@ -1438,32 +1465,32 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                CMD(set_pmksa, SET_PMKSA);
                CMD(del_pmksa, DEL_PMKSA);
                CMD(flush_pmksa, FLUSH_PMKSA);
-               if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
+               if (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
                        CMD(remain_on_channel, REMAIN_ON_CHANNEL);
                CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
                CMD(mgmt_tx, FRAME);
                CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
-               if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
+               if (rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
                        i++;
                        if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS))
                                goto nla_put_failure;
                }
-               if (dev->ops->set_monitor_channel || dev->ops->start_ap ||
-                   dev->ops->join_mesh) {
+               if (rdev->ops->set_monitor_channel || rdev->ops->start_ap ||
+                   rdev->ops->join_mesh) {
                        i++;
                        if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))
                                goto nla_put_failure;
                }
                CMD(set_wds_peer, SET_WDS_PEER);
-               if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
+               if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
                        CMD(tdls_mgmt, TDLS_MGMT);
                        CMD(tdls_oper, TDLS_OPER);
                }
-               if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
+               if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
                        CMD(sched_scan_start, START_SCHED_SCAN);
                CMD(probe_client, PROBE_CLIENT);
                CMD(set_noack_map, SET_NOACK_MAP);
-               if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
+               if (rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
                        i++;
                        if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS))
                                goto nla_put_failure;
@@ -1473,7 +1500,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                if (state->split) {
                        CMD(crit_proto_start, CRIT_PROTOCOL_START);
                        CMD(crit_proto_stop, CRIT_PROTOCOL_STOP);
-                       if (dev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
+                       if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
                                CMD(channel_switch, CHANNEL_SWITCH);
                }
                CMD(set_qos_map, SET_QOS_MAP);
@@ -1484,13 +1511,13 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
 
 #undef CMD
 
-               if (dev->ops->connect || dev->ops->auth) {
+               if (rdev->ops->connect || rdev->ops->auth) {
                        i++;
                        if (nla_put_u32(msg, i, NL80211_CMD_CONNECT))
                                goto nla_put_failure;
                }
 
-               if (dev->ops->disconnect || dev->ops->deauth) {
+               if (rdev->ops->disconnect || rdev->ops->deauth) {
                        i++;
                        if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT))
                                goto nla_put_failure;
@@ -1501,14 +1528,14 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                if (state->split)
                        break;
        case 5:
-               if (dev->ops->remain_on_channel &&
-                   (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
+               if (rdev->ops->remain_on_channel &&
+                   (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
                    nla_put_u32(msg,
                                NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
-                               dev->wiphy.max_remain_on_channel_duration))
+                               rdev->wiphy.max_remain_on_channel_duration))
                        goto nla_put_failure;
 
-               if ((dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) &&
                    nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK))
                        goto nla_put_failure;
 
@@ -1519,7 +1546,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                        break;
        case 6:
 #ifdef CONFIG_PM
-               if (nl80211_send_wowlan(msg, dev, state->split))
+               if (nl80211_send_wowlan(msg, rdev, state->split))
                        goto nla_put_failure;
                state->split_start++;
                if (state->split)
@@ -1529,10 +1556,10 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
 #endif
        case 7:
                if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
-                                       dev->wiphy.software_iftypes))
+                                       rdev->wiphy.software_iftypes))
                        goto nla_put_failure;
 
-               if (nl80211_put_iface_combinations(&dev->wiphy, msg,
+               if (nl80211_put_iface_combinations(&rdev->wiphy, msg,
                                                   state->split))
                        goto nla_put_failure;
 
@@ -1540,12 +1567,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                if (state->split)
                        break;
        case 8:
-               if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
                    nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME,
-                               dev->wiphy.ap_sme_capa))
+                               rdev->wiphy.ap_sme_capa))
                        goto nla_put_failure;
 
-               features = dev->wiphy.features;
+               features = rdev->wiphy.features;
                /*
                 * We can only add the per-channel limit information if the
                 * dump is split, otherwise it makes it too big. Therefore
@@ -1556,16 +1583,16 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS, features))
                        goto nla_put_failure;
 
-               if (dev->wiphy.ht_capa_mod_mask &&
+               if (rdev->wiphy.ht_capa_mod_mask &&
                    nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
-                           sizeof(*dev->wiphy.ht_capa_mod_mask),
-                           dev->wiphy.ht_capa_mod_mask))
+                           sizeof(*rdev->wiphy.ht_capa_mod_mask),
+                           rdev->wiphy.ht_capa_mod_mask))
                        goto nla_put_failure;
 
-               if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
-                   dev->wiphy.max_acl_mac_addrs &&
+               if (rdev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
+                   rdev->wiphy.max_acl_mac_addrs &&
                    nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX,
-                               dev->wiphy.max_acl_mac_addrs))
+                               rdev->wiphy.max_acl_mac_addrs))
                        goto nla_put_failure;
 
                /*
@@ -1581,41 +1608,41 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                state->split_start++;
                break;
        case 9:
-               if (dev->wiphy.extended_capabilities &&
+               if (rdev->wiphy.extended_capabilities &&
                    (nla_put(msg, NL80211_ATTR_EXT_CAPA,
-                            dev->wiphy.extended_capabilities_len,
-                            dev->wiphy.extended_capabilities) ||
+                            rdev->wiphy.extended_capabilities_len,
+                            rdev->wiphy.extended_capabilities) ||
                     nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK,
-                            dev->wiphy.extended_capabilities_len,
-                            dev->wiphy.extended_capabilities_mask)))
+                            rdev->wiphy.extended_capabilities_len,
+                            rdev->wiphy.extended_capabilities_mask)))
                        goto nla_put_failure;
 
-               if (dev->wiphy.vht_capa_mod_mask &&
+               if (rdev->wiphy.vht_capa_mod_mask &&
                    nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK,
-                           sizeof(*dev->wiphy.vht_capa_mod_mask),
-                           dev->wiphy.vht_capa_mod_mask))
+                           sizeof(*rdev->wiphy.vht_capa_mod_mask),
+                           rdev->wiphy.vht_capa_mod_mask))
                        goto nla_put_failure;
 
                state->split_start++;
                break;
        case 10:
-               if (nl80211_send_coalesce(msg, dev))
+               if (nl80211_send_coalesce(msg, rdev))
                        goto nla_put_failure;
 
-               if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) &&
                    (nla_put_flag(msg, NL80211_ATTR_SUPPORT_5_MHZ) ||
                     nla_put_flag(msg, NL80211_ATTR_SUPPORT_10_MHZ)))
                        goto nla_put_failure;
 
-               if (dev->wiphy.max_ap_assoc_sta &&
+               if (rdev->wiphy.max_ap_assoc_sta &&
                    nla_put_u32(msg, NL80211_ATTR_MAX_AP_ASSOC_STA,
-                               dev->wiphy.max_ap_assoc_sta))
+                               rdev->wiphy.max_ap_assoc_sta))
                        goto nla_put_failure;
 
                state->split_start++;
                break;
        case 11:
-               if (dev->wiphy.n_vendor_commands) {
+               if (rdev->wiphy.n_vendor_commands) {
                        const struct nl80211_vendor_cmd_info *info;
                        struct nlattr *nested;
 
@@ -1623,15 +1650,15 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                        if (!nested)
                                goto nla_put_failure;
 
-                       for (i = 0; i < dev->wiphy.n_vendor_commands; i++) {
-                               info = &dev->wiphy.vendor_commands[i].info;
+                       for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) {
+                               info = &rdev->wiphy.vendor_commands[i].info;
                                if (nla_put(msg, i + 1, sizeof(*info), info))
                                        goto nla_put_failure;
                        }
                        nla_nest_end(msg, nested);
                }
 
-               if (dev->wiphy.n_vendor_events) {
+               if (rdev->wiphy.n_vendor_events) {
                        const struct nl80211_vendor_cmd_info *info;
                        struct nlattr *nested;
 
@@ -1640,18 +1667,26 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                        if (!nested)
                                goto nla_put_failure;
 
-                       for (i = 0; i < dev->wiphy.n_vendor_events; i++) {
-                               info = &dev->wiphy.vendor_events[i];
+                       for (i = 0; i < rdev->wiphy.n_vendor_events; i++) {
+                               info = &rdev->wiphy.vendor_events[i];
                                if (nla_put(msg, i + 1, sizeof(*info), info))
                                        goto nla_put_failure;
                        }
                        nla_nest_end(msg, nested);
                }
+               state->split_start++;
+               break;
+       case 12:
+               if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH &&
+                   nla_put_u8(msg, NL80211_ATTR_MAX_CSA_COUNTERS,
+                              rdev->wiphy.max_num_csa_counters))
+                       goto nla_put_failure;
 
                /* done */
                state->split_start = 0;
                break;
        }
+ finish:
        return genlmsg_end(msg, hdr);
 
  nla_put_failure:
@@ -1684,7 +1719,7 @@ static int nl80211_dump_wiphy_parse(struct sk_buff *skb,
                if (!netdev)
                        return -ENODEV;
                if (netdev->ieee80211_ptr) {
-                       rdev = wiphy_to_dev(
+                       rdev = wiphy_to_rdev(
                                netdev->ieee80211_ptr->wiphy);
                        state->filter_wiphy = rdev->wiphy_idx;
                }
@@ -1697,7 +1732,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
 {
        int idx = 0, ret;
        struct nl80211_dump_wiphy_state *state = (void *)cb->args[0];
-       struct cfg80211_registered_device *dev;
+       struct cfg80211_registered_device *rdev;
 
        rtnl_lock();
        if (!state) {
@@ -1716,17 +1751,18 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
                cb->args[0] = (long)state;
        }
 
-       list_for_each_entry(dev, &cfg80211_rdev_list, list) {
-               if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
+       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+               if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
                        continue;
                if (++idx <= state->start)
                        continue;
                if (state->filter_wiphy != -1 &&
-                   state->filter_wiphy != dev->wiphy_idx)
+                   state->filter_wiphy != rdev->wiphy_idx)
                        continue;
                /* attempt to fit multiple wiphy data chunks into the skb */
                do {
-                       ret = nl80211_send_wiphy(dev, skb,
+                       ret = nl80211_send_wiphy(rdev, NL80211_CMD_NEW_WIPHY,
+                                                skb,
                                                 NETLINK_CB(cb->skb).portid,
                                                 cb->nlh->nlmsg_seq,
                                                 NLM_F_MULTI, state);
@@ -1774,14 +1810,15 @@ static int nl80211_dump_wiphy_done(struct netlink_callback *cb)
 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 cfg80211_registered_device *rdev = 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,
+       if (nl80211_send_wiphy(rdev, NL80211_CMD_NEW_WIPHY, msg,
+                              info->snd_portid, info->snd_seq, 0,
                               &state) < 0) {
                nlmsg_free(msg);
                return -ENOBUFS;
@@ -1908,18 +1945,20 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
 }
 
 static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
-                                struct wireless_dev *wdev,
+                                struct net_device *dev,
                                 struct genl_info *info)
 {
        struct cfg80211_chan_def chandef;
        int result;
        enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR;
+       struct wireless_dev *wdev = NULL;
 
-       if (wdev)
-               iftype = wdev->iftype;
-
+       if (dev)
+               wdev = dev->ieee80211_ptr;
        if (!nl80211_can_set_dev_channel(wdev))
                return -EOPNOTSUPP;
+       if (wdev)
+               iftype = wdev->iftype;
 
        result = nl80211_parse_chandef(rdev, info, &chandef);
        if (result)
@@ -1928,14 +1967,27 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
        switch (iftype) {
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_P2P_GO:
-               if (wdev->beacon_interval) {
-                       result = -EBUSY;
-                       break;
-               }
-               if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef)) {
+               if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, iftype)) {
                        result = -EINVAL;
                        break;
                }
+               if (wdev->beacon_interval) {
+                       if (!dev || !rdev->ops->set_ap_chanwidth ||
+                           !(rdev->wiphy.features &
+                             NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)) {
+                               result = -EBUSY;
+                               break;
+                       }
+
+                       /* Only allow dynamic channel width changes */
+                       if (chandef.chan != wdev->preset_chandef.chan) {
+                               result = -EBUSY;
+                               break;
+                       }
+                       result = rdev_set_ap_chanwidth(rdev, dev, &chandef);
+                       if (result)
+                               break;
+               }
                wdev->preset_chandef = chandef;
                result = 0;
                break;
@@ -1957,7 +2009,7 @@ static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *netdev = info->user_ptr[1];
 
-       return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
+       return __nl80211_set_channel(rdev, netdev, info);
 }
 
 static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
@@ -2013,7 +2065,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 
                netdev = __dev_get_by_index(genl_info_net(info), ifindex);
                if (netdev && netdev->ieee80211_ptr)
-                       rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
+                       rdev = wiphy_to_rdev(netdev->ieee80211_ptr->wiphy);
                else
                        netdev = NULL;
        }
@@ -2079,9 +2131,10 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
-               result = __nl80211_set_channel(rdev,
-                               nl80211_can_set_dev_channel(wdev) ? wdev : NULL,
-                               info);
+               result = __nl80211_set_channel(
+                       rdev,
+                       nl80211_can_set_dev_channel(wdev) ? netdev : NULL,
+                       info);
                if (result)
                        return result;
        }
@@ -2229,7 +2282,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 static inline u64 wdev_id(struct wireless_dev *wdev)
 {
        return (u64)wdev->identifier |
-              ((u64)wiphy_to_dev(wdev->wiphy)->wiphy_idx << 32);
+              ((u64)wiphy_to_rdev(wdev->wiphy)->wiphy_idx << 32);
 }
 
 static int nl80211_send_chandef(struct sk_buff *msg,
@@ -2355,7 +2408,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
 static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
 {
        struct sk_buff *msg;
-       struct cfg80211_registered_device *dev = info->user_ptr[0];
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct wireless_dev *wdev = info->user_ptr[1];
 
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
@@ -2363,7 +2416,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
                return -ENOMEM;
 
        if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
-                              dev, wdev) < 0) {
+                              rdev, wdev) < 0) {
                nlmsg_free(msg);
                return -ENOBUFS;
        }
@@ -2514,6 +2567,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
        enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
        u32 flags;
 
+       /* to avoid failing a new interface creation due to pending removal */
+       cfg80211_destroy_ifaces(rdev);
+
        memset(&params, 0, sizeof(params));
 
        if (!info->attrs[NL80211_ATTR_IFNAME])
@@ -2563,6 +2619,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
                return PTR_ERR(wdev);
        }
 
+       if (info->attrs[NL80211_ATTR_IFACE_SOCKET_OWNER])
+               wdev->owner_nlportid = info->snd_portid;
+
        switch (type) {
        case NL80211_IFTYPE_MESH_POINT:
                if (!info->attrs[NL80211_ATTR_MESH_ID])
@@ -3142,7 +3201,6 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_ap_settings params;
        int err;
-       u8 radar_detect_width = 0;
 
        if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
            dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
@@ -3258,24 +3316,10 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
        } else if (!nl80211_get_ap_channel(rdev, &params))
                return -EINVAL;
 
-       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
+       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef,
+                                    wdev->iftype))
                return -EINVAL;
 
-       err = cfg80211_chandef_dfs_required(wdev->wiphy, &params.chandef);
-       if (err < 0)
-               return err;
-       if (err) {
-               radar_detect_width = BIT(params.chandef.width);
-               params.radar_required = true;
-       }
-
-       err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
-                                          params.chandef.chan,
-                                          CHAN_MODE_SHARED,
-                                          radar_detect_width);
-       if (err)
-               return err;
-
        if (info->attrs[NL80211_ATTR_ACL_POLICY]) {
                params.acl = parse_acl_data(&rdev->wiphy, info);
                if (IS_ERR(params.acl))
@@ -3613,6 +3657,10 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
            nla_put_u32(msg, NL80211_STA_INFO_TX_FAILED,
                        sinfo->tx_failed))
                goto nla_put_failure;
+       if ((sinfo->filled & STATION_INFO_EXPECTED_THROUGHPUT) &&
+           nla_put_u32(msg, NL80211_STA_INFO_EXPECTED_THROUGHPUT,
+                       sinfo->expected_throughput))
+               goto nla_put_failure;
        if ((sinfo->filled & STATION_INFO_BEACON_LOSS_COUNT) &&
            nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS,
                        sinfo->beacon_loss_count))
@@ -3675,13 +3723,13 @@ static int nl80211_dump_station(struct sk_buff *skb,
                                struct netlink_callback *cb)
 {
        struct station_info sinfo;
-       struct cfg80211_registered_device *dev;
+       struct cfg80211_registered_device *rdev;
        struct wireless_dev *wdev;
        u8 mac_addr[ETH_ALEN];
        int sta_idx = cb->args[2];
        int err;
 
-       err = nl80211_prepare_wdev_dump(skb, cb, &dev, &wdev);
+       err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
        if (err)
                return err;
 
@@ -3690,14 +3738,14 @@ static int nl80211_dump_station(struct sk_buff *skb,
                goto out_err;
        }
 
-       if (!dev->ops->dump_station) {
+       if (!rdev->ops->dump_station) {
                err = -EOPNOTSUPP;
                goto out_err;
        }
 
        while (1) {
                memset(&sinfo, 0, sizeof(sinfo));
-               err = rdev_dump_station(dev, wdev->netdev, sta_idx,
+               err = rdev_dump_station(rdev, wdev->netdev, sta_idx,
                                        mac_addr, &sinfo);
                if (err == -ENOENT)
                        break;
@@ -3707,7 +3755,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
                if (nl80211_send_station(skb,
                                NETLINK_CB(cb->skb).portid,
                                cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                               dev, wdev->netdev, mac_addr,
+                               rdev, wdev->netdev, mac_addr,
                                &sinfo) < 0)
                        goto out;
 
@@ -3719,7 +3767,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
        cb->args[2] = sta_idx;
        err = skb->len;
  out_err:
-       nl80211_finish_wdev_dump(dev);
+       nl80211_finish_wdev_dump(rdev);
 
        return err;
 }
@@ -4380,18 +4428,18 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
                              struct netlink_callback *cb)
 {
        struct mpath_info pinfo;
-       struct cfg80211_registered_device *dev;
+       struct cfg80211_registered_device *rdev;
        struct wireless_dev *wdev;
        u8 dst[ETH_ALEN];
        u8 next_hop[ETH_ALEN];
        int path_idx = cb->args[2];
        int err;
 
-       err = nl80211_prepare_wdev_dump(skb, cb, &dev, &wdev);
+       err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
        if (err)
                return err;
 
-       if (!dev->ops->dump_mpath) {
+       if (!rdev->ops->dump_mpath) {
                err = -EOPNOTSUPP;
                goto out_err;
        }
@@ -4402,7 +4450,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
        }
 
        while (1) {
-               err = rdev_dump_mpath(dev, wdev->netdev, path_idx, dst,
+               err = rdev_dump_mpath(rdev, wdev->netdev, path_idx, dst,
                                      next_hop, &pinfo);
                if (err == -ENOENT)
                        break;
@@ -4423,7 +4471,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
        cb->args[2] = path_idx;
        err = skb->len;
  out_err:
-       nl80211_finish_wdev_dump(dev);
+       nl80211_finish_wdev_dump(rdev);
        return err;
 }
 
@@ -4663,7 +4711,6 @@ static int parse_reg_rule(struct nlattr *tb[],
 
 static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
 {
-       int r;
        char *data = NULL;
        enum nl80211_user_reg_hint_type user_reg_hint_type;
 
@@ -4676,11 +4723,6 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
        if (unlikely(!rcu_access_pointer(cfg80211_regdomain)))
                return -EINPROGRESS;
 
-       if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
-               return -EINVAL;
-
-       data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
-
        if (info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE])
                user_reg_hint_type =
                  nla_get_u32(info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]);
@@ -4690,14 +4732,16 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
        switch (user_reg_hint_type) {
        case NL80211_USER_REG_HINT_USER:
        case NL80211_USER_REG_HINT_CELL_BASE:
-               break;
+               if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
+                       return -EINVAL;
+
+               data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
+               return regulatory_hint_user(data, user_reg_hint_type);
+       case NL80211_USER_REG_HINT_INDOOR:
+               return regulatory_hint_indoor_user();
        default:
                return -EINVAL;
        }
-
-       r = regulatory_hint_user(data, user_reg_hint_type);
-
-       return r;
 }
 
 static int nl80211_get_mesh_config(struct sk_buff *skb,
@@ -5796,7 +5840,8 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
        if (wdev->cac_started)
                return -EBUSY;
 
-       err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef);
+       err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef,
+                                           wdev->iftype);
        if (err < 0)
                return err;
 
@@ -5809,12 +5854,6 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
        if (!rdev->ops->start_radar_detection)
                return -EOPNOTSUPP;
 
-       err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
-                                          chandef.chan, CHAN_MODE_SHARED,
-                                          BIT(chandef.width));
-       if (err)
-               return err;
-
        cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, &chandef);
        if (WARN_ON(!cac_time_ms))
                cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
@@ -5843,6 +5882,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
        u8 radar_detect_width = 0;
        int err;
        bool need_new_beacon = false;
+       int len, i;
 
        if (!rdev->ops->channel_switch ||
            !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
@@ -5901,26 +5941,55 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
        if (!csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON])
                return -EINVAL;
 
-       params.counter_offset_beacon =
-               nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
-       if (params.counter_offset_beacon >= params.beacon_csa.tail_len)
+       len = nla_len(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
+       if (!len || (len % sizeof(u16)))
                return -EINVAL;
 
-       /* sanity check - counters should be the same */
-       if (params.beacon_csa.tail[params.counter_offset_beacon] !=
-           params.count)
+       params.n_counter_offsets_beacon = len / sizeof(u16);
+       if (rdev->wiphy.max_num_csa_counters &&
+           (params.n_counter_offsets_beacon >
+            rdev->wiphy.max_num_csa_counters))
                return -EINVAL;
 
+       params.counter_offsets_beacon =
+               nla_data(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
+
+       /* sanity checks - counters should fit and be the same */
+       for (i = 0; i < params.n_counter_offsets_beacon; i++) {
+               u16 offset = params.counter_offsets_beacon[i];
+
+               if (offset >= params.beacon_csa.tail_len)
+                       return -EINVAL;
+
+               if (params.beacon_csa.tail[offset] != params.count)
+                       return -EINVAL;
+       }
+
        if (csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]) {
-               params.counter_offset_presp =
-                       nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
-               if (params.counter_offset_presp >=
-                   params.beacon_csa.probe_resp_len)
+               len = nla_len(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
+               if (!len || (len % sizeof(u16)))
                        return -EINVAL;
 
-               if (params.beacon_csa.probe_resp[params.counter_offset_presp] !=
-                   params.count)
+               params.n_counter_offsets_presp = len / sizeof(u16);
+               if (rdev->wiphy.max_num_csa_counters &&
+                   (params.n_counter_offsets_beacon >
+                    rdev->wiphy.max_num_csa_counters))
                        return -EINVAL;
+
+               params.counter_offsets_presp =
+                       nla_data(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
+
+               /* sanity checks - counters should fit and be the same */
+               for (i = 0; i < params.n_counter_offsets_presp; i++) {
+                       u16 offset = params.counter_offsets_presp[i];
+
+                       if (offset >= params.beacon_csa.probe_resp_len)
+                               return -EINVAL;
+
+                       if (params.beacon_csa.probe_resp[offset] !=
+                           params.count)
+                               return -EINVAL;
+               }
        }
 
 skip_beacons:
@@ -5928,27 +5997,25 @@ skip_beacons:
        if (err)
                return err;
 
-       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
+       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef,
+                                    wdev->iftype))
                return -EINVAL;
 
-       switch (dev->ieee80211_ptr->iftype) {
-       case NL80211_IFTYPE_AP:
-       case NL80211_IFTYPE_P2P_GO:
-       case NL80211_IFTYPE_ADHOC:
-       case NL80211_IFTYPE_MESH_POINT:
-               err = cfg80211_chandef_dfs_required(wdev->wiphy,
-                                                   &params.chandef);
-               if (err < 0)
-                       return err;
-               if (err) {
-                       radar_detect_width = BIT(params.chandef.width);
-                       params.radar_required = true;
-               }
-               break;
-       default:
-               break;
+       err = cfg80211_chandef_dfs_required(wdev->wiphy,
+                                           &params.chandef,
+                                           wdev->iftype);
+       if (err < 0)
+               return err;
+
+       if (err > 0) {
+               radar_detect_width = BIT(params.chandef.width);
+               params.radar_required = true;
        }
 
+       /* TODO: I left this here for now.  With channel switch, the
+        * verification is a bit more complicated, because we only do
+        * it later when the channel switch really happens.
+        */
        err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
                                           params.chandef.chan,
                                           CHAN_MODE_SHARED,
@@ -6175,12 +6242,12 @@ static int nl80211_dump_survey(struct sk_buff *skb,
                        struct netlink_callback *cb)
 {
        struct survey_info survey;
-       struct cfg80211_registered_device *dev;
+       struct cfg80211_registered_device *rdev;
        struct wireless_dev *wdev;
        int survey_idx = cb->args[2];
        int res;
 
-       res = nl80211_prepare_wdev_dump(skb, cb, &dev, &wdev);
+       res = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
        if (res)
                return res;
 
@@ -6189,7 +6256,7 @@ static int nl80211_dump_survey(struct sk_buff *skb,
                goto out_err;
        }
 
-       if (!dev->ops->dump_survey) {
+       if (!rdev->ops->dump_survey) {
                res = -EOPNOTSUPP;
                goto out_err;
        }
@@ -6197,7 +6264,7 @@ static int nl80211_dump_survey(struct sk_buff *skb,
        while (1) {
                struct ieee80211_channel *chan;
 
-               res = rdev_dump_survey(dev, wdev->netdev, survey_idx, &survey);
+               res = rdev_dump_survey(rdev, wdev->netdev, survey_idx, &survey);
                if (res == -ENOENT)
                        break;
                if (res)
@@ -6209,7 +6276,7 @@ static int nl80211_dump_survey(struct sk_buff *skb,
                        goto out;
                }
 
-               chan = ieee80211_get_channel(&dev->wiphy,
+               chan = ieee80211_get_channel(&rdev->wiphy,
                                             survey.channel->center_freq);
                if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
                        survey_idx++;
@@ -6228,7 +6295,7 @@ static int nl80211_dump_survey(struct sk_buff *skb,
        cb->args[2] = survey_idx;
        res = skb->len;
  out_err:
-       nl80211_finish_wdev_dump(dev);
+       nl80211_finish_wdev_dump(rdev);
        return res;
 }
 
@@ -6704,7 +6771,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
        if (err)
                return err;
 
-       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef))
+       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef,
+                                    NL80211_IFTYPE_ADHOC))
                return -EINVAL;
 
        switch (ibss.chandef.width) {
@@ -6879,7 +6947,7 @@ struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy,
                                           int vendor_event_idx,
                                           int approxlen, gfp_t gfp)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        const struct nl80211_vendor_cmd_info *info;
 
        switch (cmd) {
@@ -7767,6 +7835,27 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
        if (!chandef.chan && params.offchan)
                return -EINVAL;
 
+       params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
+       params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
+
+       if (info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]) {
+               int len = nla_len(info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]);
+               int i;
+
+               if (len % sizeof(u16))
+                       return -EINVAL;
+
+               params.n_csa_offsets = len / sizeof(u16);
+               params.csa_offsets =
+                       nla_data(info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]);
+
+               /* check that all the offsets fit the frame */
+               for (i = 0; i < params.n_csa_offsets; i++) {
+                       if (params.csa_offsets[i] >= params.len)
+                               return -EINVAL;
+               }
+       }
+
        if (!params.dont_wait_for_ack) {
                msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
                if (!msg)
@@ -7780,8 +7869,6 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
                }
        }
 
-       params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
-       params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
        params.chan = chandef.chan;
        err = cfg80211_mlme_mgmt_tx(rdev, wdev, &params, &cookie);
        if (err)
@@ -8478,6 +8565,8 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
 
                nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
                                    rem) {
+                       u8 *mask_pat;
+
                        nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
                                  nla_len(pat), NULL);
                        err = -EINVAL;
@@ -8501,19 +8590,18 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
                                goto error;
                        new_triggers.patterns[i].pkt_offset = pkt_offset;
 
-                       new_triggers.patterns[i].mask =
-                               kmalloc(mask_len + pat_len, GFP_KERNEL);
-                       if (!new_triggers.patterns[i].mask) {
+                       mask_pat = kmalloc(mask_len + pat_len, GFP_KERNEL);
+                       if (!mask_pat) {
                                err = -ENOMEM;
                                goto error;
                        }
-                       new_triggers.patterns[i].pattern =
-                               new_triggers.patterns[i].mask + mask_len;
-                       memcpy(new_triggers.patterns[i].mask,
-                              nla_data(pat_tb[NL80211_PKTPAT_MASK]),
+                       new_triggers.patterns[i].mask = mask_pat;
+                       memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_MASK]),
                               mask_len);
+                       mask_pat += mask_len;
+                       new_triggers.patterns[i].pattern = mask_pat;
                        new_triggers.patterns[i].pattern_len = pat_len;
-                       memcpy(new_triggers.patterns[i].pattern,
+                       memcpy(mask_pat,
                               nla_data(pat_tb[NL80211_PKTPAT_PATTERN]),
                               pat_len);
                        i++;
@@ -8705,6 +8793,8 @@ static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev,
 
        nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
                            rem) {
+               u8 *mask_pat;
+
                nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
                          nla_len(pat), NULL);
                if (!pat_tb[NL80211_PKTPAT_MASK] ||
@@ -8726,17 +8816,19 @@ static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev,
                        return -EINVAL;
                new_rule->patterns[i].pkt_offset = pkt_offset;
 
-               new_rule->patterns[i].mask =
-                       kmalloc(mask_len + pat_len, GFP_KERNEL);
-               if (!new_rule->patterns[i].mask)
+               mask_pat = kmalloc(mask_len + pat_len, GFP_KERNEL);
+               if (!mask_pat)
                        return -ENOMEM;
-               new_rule->patterns[i].pattern =
-                       new_rule->patterns[i].mask + mask_len;
-               memcpy(new_rule->patterns[i].mask,
-                      nla_data(pat_tb[NL80211_PKTPAT_MASK]), mask_len);
+
+               new_rule->patterns[i].mask = mask_pat;
+               memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_MASK]),
+                      mask_len);
+
+               mask_pat += mask_len;
+               new_rule->patterns[i].pattern = mask_pat;
                new_rule->patterns[i].pattern_len = pat_len;
-               memcpy(new_rule->patterns[i].pattern,
-                      nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), pat_len);
+               memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_PATTERN]),
+                      pat_len);
                i++;
        }
 
@@ -8981,9 +9073,8 @@ static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)
        if (wdev->p2p_started)
                return 0;
 
-       err = cfg80211_can_add_interface(rdev, wdev->iftype);
-       if (err)
-               return err;
+       if (rfkill_blocked(rdev->rfkill))
+               return -ERFKILL;
 
        err = rdev_start_p2p_device(rdev, wdev);
        if (err)
@@ -9192,7 +9283,7 @@ struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy,
                                           enum nl80211_attrs attr,
                                           int approxlen)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        if (WARN_ON(!rdev->cur_cmd_info))
                return NULL;
@@ -9316,7 +9407,7 @@ static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
                }
 
                dev = wdev->netdev;
-               rdev = wiphy_to_dev(wdev->wiphy);
+               rdev = wiphy_to_rdev(wdev->wiphy);
 
                if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
                        if (!dev) {
@@ -10017,16 +10108,20 @@ static const struct genl_ops nl80211_ops[] = {
 
 /* notification functions */
 
-void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
+void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev,
+                         enum nl80211_commands cmd)
 {
        struct sk_buff *msg;
        struct nl80211_dump_wiphy_state state = {};
 
+       WARN_ON(cmd != NL80211_CMD_NEW_WIPHY &&
+               cmd != NL80211_CMD_DEL_WIPHY);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return;
 
-       if (nl80211_send_wiphy(rdev, msg, 0, 0, 0, &state) < 0) {
+       if (nl80211_send_wiphy(rdev, cmd, msg, 0, 0, 0, &state) < 0) {
                nlmsg_free(msg);
                return;
        }
@@ -10345,7 +10440,7 @@ void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf,
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        const struct ieee80211_mgmt *mgmt = (void *)buf;
        u32 cmd;
 
@@ -10567,7 +10662,7 @@ void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
                                        const u8* ie, u8 ie_len, gfp_t gfp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
 
@@ -10747,7 +10842,7 @@ void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie,
                               unsigned int duration, gfp_t gfp)
 {
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration);
        nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
@@ -10761,7 +10856,7 @@ void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,
                                        gfp_t gfp)
 {
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan);
        nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
@@ -10773,7 +10868,7 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
                      struct station_info *sinfo, gfp_t gfp)
 {
        struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct sk_buff *msg;
 
        trace_cfg80211_new_sta(dev, mac_addr, sinfo);
@@ -10796,7 +10891,7 @@ EXPORT_SYMBOL(cfg80211_new_sta);
 void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
 {
        struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct sk_buff *msg;
        void *hdr;
 
@@ -10833,7 +10928,7 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
                          gfp_t gfp)
 {
        struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct sk_buff *msg;
        void *hdr;
 
@@ -10868,7 +10963,7 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
                                       const u8 *addr, gfp_t gfp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
        u32 nlportid = ACCESS_ONCE(wdev->ap_unexpected_nlportid);
@@ -10988,7 +11083,7 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
                             const u8 *buf, size_t len, bool ack, gfp_t gfp)
 {
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct net_device *netdev = wdev->netdev;
        struct sk_buff *msg;
        void *hdr;
@@ -11032,7 +11127,7 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct sk_buff *msg;
        struct nlattr *pinfoattr;
        void *hdr;
@@ -11124,7 +11219,7 @@ void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        trace_cfg80211_gtk_rekey_notify(dev, bssid);
        nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp);
@@ -11182,7 +11277,7 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        trace_cfg80211_pmksa_candidate_notify(dev, index, bssid, preauth);
        nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
@@ -11229,7 +11324,7 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        ASSERT_WDEV_LOCK(wdev);
 
@@ -11253,7 +11348,7 @@ void cfg80211_cqm_txe_notify(struct net_device *dev,
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct sk_buff *msg;
        struct nlattr *pinfoattr;
        void *hdr;
@@ -11353,7 +11448,7 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev,
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct sk_buff *msg;
        struct nlattr *pinfoattr;
        void *hdr;
@@ -11400,7 +11495,7 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
                           u64 cookie, bool acked, gfp_t gfp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
 
@@ -11440,7 +11535,7 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy,
                                 const u8 *frame, size_t len,
                                 int freq, int sig_dbm)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct sk_buff *msg;
        void *hdr;
        struct cfg80211_beacon_registration *reg;
@@ -11487,7 +11582,7 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
                                   struct cfg80211_wowlan_wakeup *wakeup,
                                   gfp_t gfp)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
        int size = 200;
@@ -11597,7 +11692,7 @@ void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
                                u16 reason_code, gfp_t gfp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
 
@@ -11649,9 +11744,15 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
        rcu_read_lock();
 
        list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
-               list_for_each_entry_rcu(wdev, &rdev->wdev_list, list)
+               bool schedule_destroy_work = false;
+
+               list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) {
                        cfg80211_mlme_unregister_socket(wdev, notify->portid);
 
+                       if (wdev->owner_nlportid == notify->portid)
+                               schedule_destroy_work = true;
+               }
+
                spin_lock_bh(&rdev->beacon_registrations_lock);
                list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations,
                                         list) {
@@ -11662,11 +11763,24 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
                        }
                }
                spin_unlock_bh(&rdev->beacon_registrations_lock);
+
+               if (schedule_destroy_work) {
+                       struct cfg80211_iface_destroy *destroy;
+
+                       destroy = kzalloc(sizeof(*destroy), GFP_ATOMIC);
+                       if (destroy) {
+                               destroy->nlportid = notify->portid;
+                               spin_lock(&rdev->destroy_list_lock);
+                               list_add(&destroy->list, &rdev->destroy_list);
+                               spin_unlock(&rdev->destroy_list_lock);
+                               schedule_work(&rdev->destroy_work);
+                       }
+               }
        }
 
        rcu_read_unlock();
 
-       return NOTIFY_DONE;
+       return NOTIFY_OK;
 }
 
 static struct notifier_block nl80211_netlink_notifier = {
@@ -11677,7 +11791,7 @@ void cfg80211_ft_event(struct net_device *netdev,
                       struct cfg80211_ft_event_params *ft_event)
 {
        struct wiphy *wiphy = netdev->ieee80211_ptr->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct sk_buff *msg;
        void *hdr;
 
@@ -11724,7 +11838,7 @@ void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp)
        void *hdr;
        u32 nlportid;
 
-       rdev = wiphy_to_dev(wdev->wiphy);
+       rdev = wiphy_to_rdev(wdev->wiphy);
        if (!rdev->crit_proto_nlportid)
                return;
 
@@ -11759,7 +11873,7 @@ EXPORT_SYMBOL(cfg80211_crit_proto_stopped);
 void nl80211_send_ap_stopped(struct wireless_dev *wdev)
 {
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct sk_buff *msg;
        void *hdr;
 
index 1e6df9630f42f11f815578a4bbc73fc7b037017d..49c9a482dd1248565f0b7f333ecacf5f027ff650 100644 (file)
@@ -5,7 +5,8 @@
 
 int nl80211_init(void);
 void nl80211_exit(void);
-void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev);
+void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev,
+                         enum nl80211_commands cmd);
 void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
                             struct wireless_dev *wdev);
 struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev,
index 74d97d33c938e8250ef300c2c3fb82d2a39b63b6..d95bbe34813833d04ed618fcd6ca85700718216b 100644 (file)
@@ -199,7 +199,7 @@ static inline int rdev_change_station(struct cfg80211_registered_device *rdev,
 }
 
 static inline int rdev_get_station(struct cfg80211_registered_device *rdev,
-                                  struct net_device *dev, u8 *mac,
+                                  struct net_device *dev, const u8 *mac,
                                   struct station_info *sinfo)
 {
        int ret;
@@ -950,4 +950,17 @@ static inline int rdev_set_qos_map(struct cfg80211_registered_device *rdev,
        return ret;
 }
 
+static inline int
+rdev_set_ap_chanwidth(struct cfg80211_registered_device *rdev,
+                     struct net_device *dev, struct cfg80211_chan_def *chandef)
+{
+       int ret;
+
+       trace_rdev_set_ap_chanwidth(&rdev->wiphy, dev, chandef);
+       ret = rdev->ops->set_ap_chanwidth(&rdev->wiphy, dev, chandef);
+       trace_rdev_return_int(&rdev->wiphy, ret);
+
+       return ret;
+}
+
 #endif /* __CFG80211_RDEV_OPS */
index f59aaac586f8cf10905135324c3913646910a662..558b0e3a02d8284c49de58d14833c13b444db5a2 100644 (file)
 #define REG_DBG_PRINT(args...)
 #endif
 
+/**
+ * enum reg_request_treatment - regulatory request treatment
+ *
+ * @REG_REQ_OK: continue processing the regulatory request
+ * @REG_REQ_IGNORE: ignore the regulatory request
+ * @REG_REQ_INTERSECT: the regulatory domain resulting from this request should
+ *     be intersected with the current one.
+ * @REG_REQ_ALREADY_SET: the regulatory request will not change the current
+ *     regulatory settings, and no further processing is required.
+ * @REG_REQ_USER_HINT_HANDLED: a non alpha2  user hint was handled and no
+ *     further processing is required, i.e., not need to update last_request
+ *     etc. This should be used for user hints that do not provide an alpha2
+ *     but some other type of regulatory hint, i.e., indoor operation.
+ */
 enum reg_request_treatment {
        REG_REQ_OK,
        REG_REQ_IGNORE,
        REG_REQ_INTERSECT,
        REG_REQ_ALREADY_SET,
+       REG_REQ_USER_HINT_HANDLED,
 };
 
 static struct regulatory_request core_request_world = {
@@ -106,6 +121,14 @@ const struct ieee80211_regdomain __rcu *cfg80211_regdomain;
  */
 static int reg_num_devs_support_basehint;
 
+/*
+ * State variable indicating if the platform on which the devices
+ * are attached is operating in an indoor environment. The state variable
+ * is relevant for all registered devices.
+ * (protected by RTNL)
+ */
+static bool reg_is_indoor;
+
 static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
 {
        return rtnl_dereference(cfg80211_regdomain);
@@ -240,8 +263,16 @@ static char user_alpha2[2];
 module_param(ieee80211_regdom, charp, 0444);
 MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
 
-static void reg_free_request(struct regulatory_request *lr)
+static void reg_free_request(struct regulatory_request *request)
 {
+       if (request != get_last_request())
+               kfree(request);
+}
+
+static void reg_free_last_request(void)
+{
+       struct regulatory_request *lr = get_last_request();
+
        if (lr != &core_request_world && lr)
                kfree_rcu(lr, rcu_head);
 }
@@ -254,7 +285,7 @@ static void reg_update_last_request(struct regulatory_request *request)
        if (lr == request)
                return;
 
-       reg_free_request(lr);
+       reg_free_last_request();
        rcu_assign_pointer(last_request, request);
 }
 
@@ -873,6 +904,8 @@ static u32 map_regdom_flags(u32 rd_flags)
                channel_flags |= IEEE80211_CHAN_RADAR;
        if (rd_flags & NL80211_RRF_NO_OFDM)
                channel_flags |= IEEE80211_CHAN_NO_OFDM;
+       if (rd_flags & NL80211_RRF_NO_OUTDOOR)
+               channel_flags |= IEEE80211_CHAN_INDOOR_ONLY;
        return channel_flags;
 }
 
@@ -902,7 +935,7 @@ freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
                if (!band_rule_found)
                        band_rule_found = freq_in_rule_band(fr, center_freq);
 
-               bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(20));
+               bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(5));
 
                if (band_rule_found && bw_fits)
                        return rr;
@@ -986,10 +1019,10 @@ static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd,
 }
 #endif
 
-/*
- * Note that right now we assume the desired channel bandwidth
- * is always 20 MHz for each individual channel (HT40 uses 20 MHz
- * per channel, the primary and the extension channel).
+/* Find an ieee80211_reg_rule such that a 5MHz channel with frequency
+ * chan->center_freq fits there.
+ * If there is no such reg_rule, disable the channel, otherwise set the
+ * flags corresponding to the bandwidths allowed in the particular reg_rule
  */
 static void handle_channel(struct wiphy *wiphy,
                           enum nl80211_reg_initiator initiator,
@@ -1050,8 +1083,12 @@ static void handle_channel(struct wiphy *wiphy,
        if (reg_rule->flags & NL80211_RRF_AUTO_BW)
                max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
 
+       if (max_bandwidth_khz < MHZ_TO_KHZ(10))
+               bw_flags = IEEE80211_CHAN_NO_10MHZ;
+       if (max_bandwidth_khz < MHZ_TO_KHZ(20))
+               bw_flags |= IEEE80211_CHAN_NO_20MHZ;
        if (max_bandwidth_khz < MHZ_TO_KHZ(40))
-               bw_flags = IEEE80211_CHAN_NO_HT40;
+               bw_flags |= IEEE80211_CHAN_NO_HT40;
        if (max_bandwidth_khz < MHZ_TO_KHZ(80))
                bw_flags |= IEEE80211_CHAN_NO_80MHZ;
        if (max_bandwidth_khz < MHZ_TO_KHZ(160))
@@ -1071,6 +1108,13 @@ static void handle_channel(struct wiphy *wiphy,
                        (int) MBI_TO_DBI(power_rule->max_antenna_gain);
                chan->max_reg_power = chan->max_power = chan->orig_mpwr =
                        (int) MBM_TO_DBM(power_rule->max_eirp);
+
+               if (chan->flags & IEEE80211_CHAN_RADAR) {
+                       chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+                       if (reg_rule->dfs_cac_ms)
+                               chan->dfs_cac_ms = reg_rule->dfs_cac_ms;
+               }
+
                return;
        }
 
@@ -1126,12 +1170,19 @@ static bool reg_request_cell_base(struct regulatory_request *request)
        return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE;
 }
 
+static bool reg_request_indoor(struct regulatory_request *request)
+{
+       if (request->initiator != NL80211_REGDOM_SET_BY_USER)
+               return false;
+       return request->user_reg_hint_type == NL80211_USER_REG_HINT_INDOOR;
+}
+
 bool reg_last_request_cell_base(void)
 {
        return reg_request_cell_base(get_last_request());
 }
 
-#ifdef CONFIG_CFG80211_CERTIFICATION_ONUS
+#ifdef CONFIG_CFG80211_REG_CELLULAR_HINTS
 /* Core specific check */
 static enum reg_request_treatment
 reg_ignore_cell_hint(struct regulatory_request *pending_request)
@@ -1471,8 +1522,12 @@ static void handle_channel_custom(struct wiphy *wiphy,
        if (reg_rule->flags & NL80211_RRF_AUTO_BW)
                max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
 
+       if (max_bandwidth_khz < MHZ_TO_KHZ(10))
+               bw_flags = IEEE80211_CHAN_NO_10MHZ;
+       if (max_bandwidth_khz < MHZ_TO_KHZ(20))
+               bw_flags |= IEEE80211_CHAN_NO_20MHZ;
        if (max_bandwidth_khz < MHZ_TO_KHZ(40))
-               bw_flags = IEEE80211_CHAN_NO_HT40;
+               bw_flags |= IEEE80211_CHAN_NO_HT40;
        if (max_bandwidth_khz < MHZ_TO_KHZ(80))
                bw_flags |= IEEE80211_CHAN_NO_80MHZ;
        if (max_bandwidth_khz < MHZ_TO_KHZ(160))
@@ -1568,6 +1623,11 @@ __reg_process_hint_user(struct regulatory_request *user_request)
 {
        struct regulatory_request *lr = get_last_request();
 
+       if (reg_request_indoor(user_request)) {
+               reg_is_indoor = true;
+               return REG_REQ_USER_HINT_HANDLED;
+       }
+
        if (reg_request_cell_base(user_request))
                return reg_ignore_cell_hint(user_request);
 
@@ -1615,8 +1675,9 @@ reg_process_hint_user(struct regulatory_request *user_request)
 
        treatment = __reg_process_hint_user(user_request);
        if (treatment == REG_REQ_IGNORE ||
-           treatment == REG_REQ_ALREADY_SET) {
-               kfree(user_request);
+           treatment == REG_REQ_ALREADY_SET ||
+           treatment == REG_REQ_USER_HINT_HANDLED) {
+               reg_free_request(user_request);
                return treatment;
        }
 
@@ -1676,14 +1737,15 @@ reg_process_hint_driver(struct wiphy *wiphy,
        case REG_REQ_OK:
                break;
        case REG_REQ_IGNORE:
-               kfree(driver_request);
+       case REG_REQ_USER_HINT_HANDLED:
+               reg_free_request(driver_request);
                return treatment;
        case REG_REQ_INTERSECT:
                /* fall through */
        case REG_REQ_ALREADY_SET:
                regd = reg_copy_regd(get_cfg80211_regdom());
                if (IS_ERR(regd)) {
-                       kfree(driver_request);
+                       reg_free_request(driver_request);
                        return REG_REQ_IGNORE;
                }
                rcu_assign_pointer(wiphy->regd, regd);
@@ -1775,12 +1837,13 @@ reg_process_hint_country_ie(struct wiphy *wiphy,
        case REG_REQ_OK:
                break;
        case REG_REQ_IGNORE:
+       case REG_REQ_USER_HINT_HANDLED:
                /* fall through */
        case REG_REQ_ALREADY_SET:
-               kfree(country_ie_request);
+               reg_free_request(country_ie_request);
                return treatment;
        case REG_REQ_INTERSECT:
-               kfree(country_ie_request);
+               reg_free_request(country_ie_request);
                /*
                 * This doesn't happen yet, not sure we
                 * ever want to support it for this case.
@@ -1813,7 +1876,8 @@ static void reg_process_hint(struct regulatory_request *reg_request)
        case NL80211_REGDOM_SET_BY_USER:
                treatment = reg_process_hint_user(reg_request);
                if (treatment == REG_REQ_IGNORE ||
-                   treatment == REG_REQ_ALREADY_SET)
+                   treatment == REG_REQ_ALREADY_SET ||
+                   treatment == REG_REQ_USER_HINT_HANDLED)
                        return;
                queue_delayed_work(system_power_efficient_wq,
                                   &reg_timeout, msecs_to_jiffies(3142));
@@ -1841,7 +1905,7 @@ static void reg_process_hint(struct regulatory_request *reg_request)
        return;
 
 out_free:
-       kfree(reg_request);
+       reg_free_request(reg_request);
 }
 
 /*
@@ -1857,7 +1921,7 @@ static void reg_process_pending_hints(void)
 
        /* 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");
+               reg_process_hint(lr);
                return;
        }
 
@@ -1967,6 +2031,22 @@ int regulatory_hint_user(const char *alpha2,
        return 0;
 }
 
+int regulatory_hint_indoor_user(void)
+{
+       struct regulatory_request *request;
+
+       request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
+       if (!request)
+               return -ENOMEM;
+
+       request->wiphy_idx = WIPHY_IDX_INVALID;
+       request->initiator = NL80211_REGDOM_SET_BY_USER;
+       request->user_reg_hint_type = NL80211_USER_REG_HINT_INDOOR;
+       queue_regulatory_request(request);
+
+       return 0;
+}
+
 /* Driver hints */
 int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
 {
@@ -2134,6 +2214,8 @@ static void restore_regulatory_settings(bool reset_user)
 
        ASSERT_RTNL();
 
+       reg_is_indoor = false;
+
        reset_regdomains(true, &world_regdom);
        restore_alpha2(alpha2, reset_user);
 
@@ -2594,7 +2676,7 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy)
                reg_num_devs_support_basehint--;
 
        rcu_free_regdom(get_wiphy_regdom(wiphy));
-       rcu_assign_pointer(wiphy->regd, NULL);
+       RCU_INIT_POINTER(wiphy->regd, NULL);
 
        if (lr)
                request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
@@ -2614,6 +2696,40 @@ static void reg_timeout_work(struct work_struct *work)
        rtnl_unlock();
 }
 
+/*
+ * See http://www.fcc.gov/document/5-ghz-unlicensed-spectrum-unii, for
+ * UNII band definitions
+ */
+int cfg80211_get_unii(int freq)
+{
+       /* UNII-1 */
+       if (freq >= 5150 && freq <= 5250)
+               return 0;
+
+       /* UNII-2A */
+       if (freq > 5250 && freq <= 5350)
+               return 1;
+
+       /* UNII-2B */
+       if (freq > 5350 && freq <= 5470)
+               return 2;
+
+       /* UNII-2C */
+       if (freq > 5470 && freq <= 5725)
+               return 3;
+
+       /* UNII-3 */
+       if (freq > 5725 && freq <= 5825)
+               return 4;
+
+       return -EINVAL;
+}
+
+bool regulatory_indoor_allowed(void)
+{
+       return reg_is_indoor;
+}
+
 int __init regulatory_init(void)
 {
        int err = 0;
index 37c180df34b72a1195aacb6d72b7b07ddc44a9ef..5e48031ccb9afc33a41c36221e3bef924625ebe8 100644 (file)
@@ -25,6 +25,7 @@ enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy);
 
 int regulatory_hint_user(const char *alpha2,
                         enum nl80211_user_reg_hint_type user_reg_hint_type);
+int regulatory_hint_indoor_user(void);
 
 void wiphy_regulatory_register(struct wiphy *wiphy);
 void wiphy_regulatory_deregister(struct wiphy *wiphy);
@@ -104,4 +105,21 @@ void regulatory_hint_country_ie(struct wiphy *wiphy,
  */
 void regulatory_hint_disconnect(void);
 
+/**
+ * cfg80211_get_unii - get the U-NII band for the frequency
+ * @freq: the frequency for which we want to get the UNII band.
+
+ * Get a value specifying the U-NII band frequency belongs to.
+ * U-NII bands are defined by the FCC in C.F.R 47 part 15.
+ *
+ * Returns -EINVAL if freq is invalid, 0 for UNII-1, 1 for UNII-2A,
+ * 2 for UNII-2B, 3 for UNII-2C and 4 for UNII-3.
+ */
+int cfg80211_get_unii(int freq);
+
+/**
+ * regulatory_indoor_allowed - is indoor operation allowed
+ */
+bool regulatory_indoor_allowed(void);
+
 #endif  /* __NET_WIRELESS_REG_H */
index 88f108edfb586ef3b2d17d15cf66b00da84f93f0..0798c62e60858cb81d1df3d4829414665808268b 100644 (file)
@@ -81,10 +81,10 @@ static void bss_free(struct cfg80211_internal_bss *bss)
        kfree(bss);
 }
 
-static inline void bss_ref_get(struct cfg80211_registered_device *dev,
+static inline void bss_ref_get(struct cfg80211_registered_device *rdev,
                               struct cfg80211_internal_bss *bss)
 {
-       lockdep_assert_held(&dev->bss_lock);
+       lockdep_assert_held(&rdev->bss_lock);
 
        bss->refcount++;
        if (bss->pub.hidden_beacon_bss) {
@@ -95,10 +95,10 @@ static inline void bss_ref_get(struct cfg80211_registered_device *dev,
        }
 }
 
-static inline void bss_ref_put(struct cfg80211_registered_device *dev,
+static inline void bss_ref_put(struct cfg80211_registered_device *rdev,
                               struct cfg80211_internal_bss *bss)
 {
-       lockdep_assert_held(&dev->bss_lock);
+       lockdep_assert_held(&rdev->bss_lock);
 
        if (bss->pub.hidden_beacon_bss) {
                struct cfg80211_internal_bss *hbss;
@@ -114,10 +114,10 @@ static inline void bss_ref_put(struct cfg80211_registered_device *dev,
                bss_free(bss);
 }
 
-static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *dev,
+static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *rdev,
                                  struct cfg80211_internal_bss *bss)
 {
-       lockdep_assert_held(&dev->bss_lock);
+       lockdep_assert_held(&rdev->bss_lock);
 
        if (!list_empty(&bss->hidden_list)) {
                /*
@@ -134,31 +134,31 @@ static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *dev,
        }
 
        list_del_init(&bss->list);
-       rb_erase(&bss->rbn, &dev->bss_tree);
-       bss_ref_put(dev, bss);
+       rb_erase(&bss->rbn, &rdev->bss_tree);
+       bss_ref_put(rdev, bss);
        return true;
 }
 
-static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev,
+static void __cfg80211_bss_expire(struct cfg80211_registered_device *rdev,
                                  unsigned long expire_time)
 {
        struct cfg80211_internal_bss *bss, *tmp;
        bool expired = false;
 
-       lockdep_assert_held(&dev->bss_lock);
+       lockdep_assert_held(&rdev->bss_lock);
 
-       list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) {
+       list_for_each_entry_safe(bss, tmp, &rdev->bss_list, list) {
                if (atomic_read(&bss->hold))
                        continue;
                if (!time_after(expire_time, bss->ts))
                        continue;
 
-               if (__cfg80211_unlink_bss(dev, bss))
+               if (__cfg80211_unlink_bss(rdev, bss))
                        expired = true;
        }
 
        if (expired)
-               dev->bss_generation++;
+               rdev->bss_generation++;
 }
 
 void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
@@ -238,11 +238,11 @@ void __cfg80211_scan_done(struct work_struct *wk)
 void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
 {
        trace_cfg80211_scan_done(request, aborted);
-       WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);
+       WARN_ON(request != wiphy_to_rdev(request->wiphy)->scan_req);
 
        request->aborted = aborted;
        request->notified = true;
-       queue_work(cfg80211_wq, &wiphy_to_dev(request->wiphy)->scan_done_wk);
+       queue_work(cfg80211_wq, &wiphy_to_rdev(request->wiphy)->scan_done_wk);
 }
 EXPORT_SYMBOL(cfg80211_scan_done);
 
@@ -278,15 +278,15 @@ void cfg80211_sched_scan_results(struct wiphy *wiphy)
 {
        trace_cfg80211_sched_scan_results(wiphy);
        /* ignore if we're not scanning */
-       if (wiphy_to_dev(wiphy)->sched_scan_req)
+       if (wiphy_to_rdev(wiphy)->sched_scan_req)
                queue_work(cfg80211_wq,
-                          &wiphy_to_dev(wiphy)->sched_scan_results_wk);
+                          &wiphy_to_rdev(wiphy)->sched_scan_results_wk);
 }
 EXPORT_SYMBOL(cfg80211_sched_scan_results);
 
 void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        ASSERT_RTNL();
 
@@ -330,21 +330,21 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
        return 0;
 }
 
-void cfg80211_bss_age(struct cfg80211_registered_device *dev,
+void cfg80211_bss_age(struct cfg80211_registered_device *rdev,
                       unsigned long age_secs)
 {
        struct cfg80211_internal_bss *bss;
        unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC);
 
-       spin_lock_bh(&dev->bss_lock);
-       list_for_each_entry(bss, &dev->bss_list, list)
+       spin_lock_bh(&rdev->bss_lock);
+       list_for_each_entry(bss, &rdev->bss_list, list)
                bss->ts -= age_jiffies;
-       spin_unlock_bh(&dev->bss_lock);
+       spin_unlock_bh(&rdev->bss_lock);
 }
 
-void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
+void cfg80211_bss_expire(struct cfg80211_registered_device *rdev)
 {
-       __cfg80211_bss_expire(dev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE);
+       __cfg80211_bss_expire(rdev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE);
 }
 
 const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len)
@@ -534,32 +534,34 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
                                      const u8 *ssid, size_t ssid_len,
                                      u16 capa_mask, u16 capa_val)
 {
-       struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct cfg80211_internal_bss *bss, *res = NULL;
        unsigned long now = jiffies;
 
        trace_cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, capa_mask,
                               capa_val);
 
-       spin_lock_bh(&dev->bss_lock);
+       spin_lock_bh(&rdev->bss_lock);
 
-       list_for_each_entry(bss, &dev->bss_list, list) {
+       list_for_each_entry(bss, &rdev->bss_list, list) {
                if ((bss->pub.capability & capa_mask) != capa_val)
                        continue;
                if (channel && bss->pub.channel != channel)
                        continue;
+               if (!is_valid_ether_addr(bss->pub.bssid))
+                       continue;
                /* Don't get expired BSS structs */
                if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) &&
                    !atomic_read(&bss->hold))
                        continue;
                if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
                        res = bss;
-                       bss_ref_get(dev, res);
+                       bss_ref_get(rdev, res);
                        break;
                }
        }
 
-       spin_unlock_bh(&dev->bss_lock);
+       spin_unlock_bh(&rdev->bss_lock);
        if (!res)
                return NULL;
        trace_cfg80211_return_bss(&res->pub);
@@ -567,10 +569,10 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
 }
 EXPORT_SYMBOL(cfg80211_get_bss);
 
-static void rb_insert_bss(struct cfg80211_registered_device *dev,
+static void rb_insert_bss(struct cfg80211_registered_device *rdev,
                          struct cfg80211_internal_bss *bss)
 {
-       struct rb_node **p = &dev->bss_tree.rb_node;
+       struct rb_node **p = &rdev->bss_tree.rb_node;
        struct rb_node *parent = NULL;
        struct cfg80211_internal_bss *tbss;
        int cmp;
@@ -593,15 +595,15 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev,
        }
 
        rb_link_node(&bss->rbn, parent, p);
-       rb_insert_color(&bss->rbn, &dev->bss_tree);
+       rb_insert_color(&bss->rbn, &rdev->bss_tree);
 }
 
 static struct cfg80211_internal_bss *
-rb_find_bss(struct cfg80211_registered_device *dev,
+rb_find_bss(struct cfg80211_registered_device *rdev,
            struct cfg80211_internal_bss *res,
            enum bss_compare_mode mode)
 {
-       struct rb_node *n = dev->bss_tree.rb_node;
+       struct rb_node *n = rdev->bss_tree.rb_node;
        struct cfg80211_internal_bss *bss;
        int r;
 
@@ -620,7 +622,7 @@ rb_find_bss(struct cfg80211_registered_device *dev,
        return NULL;
 }
 
-static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev,
+static bool cfg80211_combine_bsses(struct cfg80211_registered_device *rdev,
                                   struct cfg80211_internal_bss *new)
 {
        const struct cfg80211_bss_ies *ies;
@@ -650,7 +652,7 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev,
 
        /* This is the bad part ... */
 
-       list_for_each_entry(bss, &dev->bss_list, list) {
+       list_for_each_entry(bss, &rdev->bss_list, list) {
                if (!ether_addr_equal(bss->pub.bssid, new->pub.bssid))
                        continue;
                if (bss->pub.channel != new->pub.channel)
@@ -684,7 +686,7 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev,
 
 /* Returned bss is reference counted and must be cleaned up appropriately. */
 static struct cfg80211_internal_bss *
-cfg80211_bss_update(struct cfg80211_registered_device *dev,
+cfg80211_bss_update(struct cfg80211_registered_device *rdev,
                    struct cfg80211_internal_bss *tmp,
                    bool signal_valid)
 {
@@ -695,14 +697,14 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
 
        tmp->ts = jiffies;
 
-       spin_lock_bh(&dev->bss_lock);
+       spin_lock_bh(&rdev->bss_lock);
 
        if (WARN_ON(!rcu_access_pointer(tmp->pub.ies))) {
-               spin_unlock_bh(&dev->bss_lock);
+               spin_unlock_bh(&rdev->bss_lock);
                return NULL;
        }
 
-       found = rb_find_bss(dev, tmp, BSS_CMP_REGULAR);
+       found = rb_find_bss(rdev, tmp, BSS_CMP_REGULAR);
 
        if (found) {
                /* Update IEs */
@@ -789,7 +791,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
                 * is allocated on the stack since it's not needed in the
                 * more common case of an update
                 */
-               new = kzalloc(sizeof(*new) + dev->wiphy.bss_priv_size,
+               new = kzalloc(sizeof(*new) + rdev->wiphy.bss_priv_size,
                              GFP_ATOMIC);
                if (!new) {
                        ies = (void *)rcu_dereference(tmp->pub.beacon_ies);
@@ -805,9 +807,9 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
                INIT_LIST_HEAD(&new->hidden_list);
 
                if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
-                       hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_ZLEN);
+                       hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN);
                        if (!hidden)
-                               hidden = rb_find_bss(dev, tmp,
+                               hidden = rb_find_bss(rdev, tmp,
                                                     BSS_CMP_HIDE_NUL);
                        if (hidden) {
                                new->pub.hidden_beacon_bss = &hidden->pub;
@@ -824,24 +826,24 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
                         * expensive search for any probe responses that should
                         * be grouped with this beacon for updates ...
                         */
-                       if (!cfg80211_combine_bsses(dev, new)) {
+                       if (!cfg80211_combine_bsses(rdev, new)) {
                                kfree(new);
                                goto drop;
                        }
                }
 
-               list_add_tail(&new->list, &dev->bss_list);
-               rb_insert_bss(dev, new);
+               list_add_tail(&new->list, &rdev->bss_list);
+               rb_insert_bss(rdev, new);
                found = new;
        }
 
-       dev->bss_generation++;
-       bss_ref_get(dev, found);
-       spin_unlock_bh(&dev->bss_lock);
+       rdev->bss_generation++;
+       bss_ref_get(rdev, found);
+       spin_unlock_bh(&rdev->bss_lock);
 
        return found;
  drop:
-       spin_unlock_bh(&dev->bss_lock);
+       spin_unlock_bh(&rdev->bss_lock);
        return NULL;
 }
 
@@ -889,6 +891,7 @@ cfg80211_inform_bss_width(struct wiphy *wiphy,
        struct cfg80211_bss_ies *ies;
        struct ieee80211_channel *channel;
        struct cfg80211_internal_bss tmp = {}, *res;
+       bool signal_valid;
 
        if (WARN_ON(!wiphy))
                return NULL;
@@ -925,8 +928,9 @@ cfg80211_inform_bss_width(struct wiphy *wiphy,
        rcu_assign_pointer(tmp.pub.beacon_ies, ies);
        rcu_assign_pointer(tmp.pub.ies, ies);
 
-       res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp,
-                                 rx_channel == channel);
+       signal_valid = abs(rx_channel->center_freq - channel->center_freq) <=
+               wiphy->max_adj_channel_rssi_comp;
+       res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid);
        if (!res)
                return NULL;
 
@@ -950,6 +954,7 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
        struct cfg80211_internal_bss tmp = {}, *res;
        struct cfg80211_bss_ies *ies;
        struct ieee80211_channel *channel;
+       bool signal_valid;
        size_t ielen = len - offsetof(struct ieee80211_mgmt,
                                      u.probe_resp.variable);
 
@@ -997,8 +1002,9 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
        tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
        tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
 
-       res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp,
-                                 rx_channel == channel);
+       signal_valid = abs(rx_channel->center_freq - channel->center_freq) <=
+               wiphy->max_adj_channel_rssi_comp;
+       res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid);
        if (!res)
                return NULL;
 
@@ -1013,7 +1019,7 @@ EXPORT_SYMBOL(cfg80211_inform_bss_width_frame);
 
 void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 {
-       struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct cfg80211_internal_bss *bss;
 
        if (!pub)
@@ -1021,15 +1027,15 @@ void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 
        bss = container_of(pub, struct cfg80211_internal_bss, pub);
 
-       spin_lock_bh(&dev->bss_lock);
-       bss_ref_get(dev, bss);
-       spin_unlock_bh(&dev->bss_lock);
+       spin_lock_bh(&rdev->bss_lock);
+       bss_ref_get(rdev, bss);
+       spin_unlock_bh(&rdev->bss_lock);
 }
 EXPORT_SYMBOL(cfg80211_ref_bss);
 
 void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 {
-       struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct cfg80211_internal_bss *bss;
 
        if (!pub)
@@ -1037,15 +1043,15 @@ void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 
        bss = container_of(pub, struct cfg80211_internal_bss, pub);
 
-       spin_lock_bh(&dev->bss_lock);
-       bss_ref_put(dev, bss);
-       spin_unlock_bh(&dev->bss_lock);
+       spin_lock_bh(&rdev->bss_lock);
+       bss_ref_put(rdev, bss);
+       spin_unlock_bh(&rdev->bss_lock);
 }
 EXPORT_SYMBOL(cfg80211_put_bss);
 
 void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 {
-       struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct cfg80211_internal_bss *bss;
 
        if (WARN_ON(!pub))
@@ -1053,12 +1059,12 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 
        bss = container_of(pub, struct cfg80211_internal_bss, pub);
 
-       spin_lock_bh(&dev->bss_lock);
+       spin_lock_bh(&rdev->bss_lock);
        if (!list_empty(&bss->list)) {
-               if (__cfg80211_unlink_bss(dev, bss))
-                       dev->bss_generation++;
+               if (__cfg80211_unlink_bss(rdev, bss))
+                       rdev->bss_generation++;
        }
-       spin_unlock_bh(&dev->bss_lock);
+       spin_unlock_bh(&rdev->bss_lock);
 }
 EXPORT_SYMBOL(cfg80211_unlink_bss);
 
@@ -1075,7 +1081,7 @@ cfg80211_get_dev_from_ifindex(struct net *net, int ifindex)
        if (!dev)
                return ERR_PTR(-ENODEV);
        if (dev->ieee80211_ptr)
-               rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy);
+               rdev = wiphy_to_rdev(dev->ieee80211_ptr->wiphy);
        else
                rdev = ERR_PTR(-ENODEV);
        dev_put(dev);
@@ -1155,7 +1161,11 @@ int cfg80211_wext_siwscan(struct net_device *dev,
                                int k;
                                int wiphy_freq = wiphy->bands[band]->channels[j].center_freq;
                                for (k = 0; k < wreq->num_channels; k++) {
-                                       int wext_freq = cfg80211_wext_freq(wiphy, &wreq->channel_list[k]);
+                                       struct iw_freq *freq =
+                                               &wreq->channel_list[k];
+                                       int wext_freq =
+                                               cfg80211_wext_freq(freq);
+
                                        if (wext_freq == wiphy_freq)
                                                goto wext_freq_found;
                                }
@@ -1467,7 +1477,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
 }
 
 
-static int ieee80211_scan_results(struct cfg80211_registered_device *dev,
+static int ieee80211_scan_results(struct cfg80211_registered_device *rdev,
                                  struct iw_request_info *info,
                                  char *buf, size_t len)
 {
@@ -1475,18 +1485,18 @@ static int ieee80211_scan_results(struct cfg80211_registered_device *dev,
        char *end_buf = buf + len;
        struct cfg80211_internal_bss *bss;
 
-       spin_lock_bh(&dev->bss_lock);
-       cfg80211_bss_expire(dev);
+       spin_lock_bh(&rdev->bss_lock);
+       cfg80211_bss_expire(rdev);
 
-       list_for_each_entry(bss, &dev->bss_list, list) {
+       list_for_each_entry(bss, &rdev->bss_list, list) {
                if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
-                       spin_unlock_bh(&dev->bss_lock);
+                       spin_unlock_bh(&rdev->bss_lock);
                        return -E2BIG;
                }
-               current_ev = ieee80211_bss(&dev->wiphy, info, bss,
+               current_ev = ieee80211_bss(&rdev->wiphy, info, bss,
                                           current_ev, end_buf);
        }
-       spin_unlock_bh(&dev->bss_lock);
+       spin_unlock_bh(&rdev->bss_lock);
        return current_ev - buf;
 }
 
index 3546a77033de30d0d2593c6ac4fbac8f58659025..8bbeeb302216223a260085634bf6b5627c209783 100644 (file)
@@ -59,7 +59,7 @@ static void cfg80211_sme_free(struct wireless_dev *wdev)
 
 static int cfg80211_conn_scan(struct wireless_dev *wdev)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_scan_request *request;
        int n_channels, err;
 
@@ -130,7 +130,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
 
 static int cfg80211_conn_do_work(struct wireless_dev *wdev)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_connect_params *params;
        struct cfg80211_assoc_request req = {};
        int err;
@@ -149,7 +149,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
        case CFG80211_CONN_SCAN_AGAIN:
                return cfg80211_conn_scan(wdev);
        case CFG80211_CONN_AUTHENTICATE_NEXT:
-               BUG_ON(!rdev->ops->auth);
+               if (WARN_ON(!rdev->ops->auth))
+                       return -EOPNOTSUPP;
                wdev->conn->state = CFG80211_CONN_AUTHENTICATING;
                return cfg80211_mlme_auth(rdev, wdev->netdev,
                                          params->channel, params->auth_type,
@@ -161,7 +162,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
        case CFG80211_CONN_AUTH_FAILED:
                return -ENOTCONN;
        case CFG80211_CONN_ASSOCIATE_NEXT:
-               BUG_ON(!rdev->ops->assoc);
+               if (WARN_ON(!rdev->ops->assoc))
+                       return -EOPNOTSUPP;
                wdev->conn->state = CFG80211_CONN_ASSOCIATING;
                if (wdev->conn->prev_bssid_valid)
                        req.prev_bssid = wdev->conn->prev_bssid;
@@ -244,7 +246,7 @@ void cfg80211_conn_work(struct work_struct *work)
 /* 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);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_bss *bss;
        u16 capa = WLAN_CAPABILITY_ESS;
 
@@ -274,7 +276,7 @@ static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev)
 static void __cfg80211_sme_scan_done(struct net_device *dev)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_bss *bss;
 
        ASSERT_WDEV_LOCK(wdev);
@@ -305,7 +307,7 @@ void cfg80211_sme_scan_done(struct net_device *dev)
 void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len)
 {
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
        u16 status_code = le16_to_cpu(mgmt->u.auth.status_code);
 
@@ -351,7 +353,7 @@ void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len)
 
 bool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
        if (!wdev->conn)
                return false;
@@ -385,7 +387,7 @@ void cfg80211_sme_deauth(struct wireless_dev *wdev)
 
 void cfg80211_sme_auth_timeout(struct wireless_dev *wdev)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
        if (!wdev->conn)
                return;
@@ -396,7 +398,7 @@ void cfg80211_sme_auth_timeout(struct wireless_dev *wdev)
 
 void cfg80211_sme_disassoc(struct wireless_dev *wdev)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
        if (!wdev->conn)
                return;
@@ -407,7 +409,7 @@ void cfg80211_sme_disassoc(struct wireless_dev *wdev)
 
 void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
        if (!wdev->conn)
                return;
@@ -420,7 +422,7 @@ 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_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_bss *bss;
        int err;
 
@@ -467,7 +469,7 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev,
        }
 
        wdev->conn->params.ssid = wdev->ssid;
-       wdev->conn->params.ssid_len = connect->ssid_len;
+       wdev->conn->params.ssid_len = wdev->ssid_len;
 
        /* see if we have the bss already */
        bss = cfg80211_get_conn_bss(wdev);
@@ -479,7 +481,6 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev,
 
        /* 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 {
@@ -505,7 +506,7 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev,
 
 static int cfg80211_sme_disconnect(struct wireless_dev *wdev, u16 reason)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        int err;
 
        if (!wdev->conn)
@@ -593,7 +594,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                return;
        }
 
-       nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev,
+       nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev,
                                    bssid, req_ie, req_ie_len,
                                    resp_ie, resp_ie_len,
                                    status, GFP_KERNEL);
@@ -624,7 +625,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
 #endif
 
        if (!bss && (status == WLAN_STATUS_SUCCESS)) {
-               WARN_ON_ONCE(!wiphy_to_dev(wdev->wiphy)->ops->connect);
+               WARN_ON_ONCE(!wiphy_to_rdev(wdev->wiphy)->ops->connect);
                bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
                                       wdev->ssid, wdev->ssid_len,
                                       WLAN_CAPABILITY_ESS,
@@ -687,7 +688,7 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                             u16 status, gfp_t gfp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_event *ev;
        unsigned long flags;
 
@@ -742,7 +743,8 @@ void __cfg80211_roamed(struct wireless_dev *wdev,
        cfg80211_hold_bss(bss_from_pub(bss));
        wdev->current_bss = bss_from_pub(bss);
 
-       nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bss->bssid,
+       nl80211_send_roamed(wiphy_to_rdev(wdev->wiphy),
+                           wdev->netdev, bss->bssid,
                            req_ie, req_ie_len, resp_ie, resp_ie_len,
                            GFP_KERNEL);
 
@@ -801,7 +803,7 @@ void cfg80211_roamed_bss(struct net_device *dev,
                         size_t resp_ie_len, gfp_t gfp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_event *ev;
        unsigned long flags;
 
@@ -834,7 +836,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
                             size_t ie_len, u16 reason, bool from_ap)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        int i;
 #ifdef CONFIG_CFG80211_WEXT
        union iwreq_data wrqu;
@@ -877,10 +879,10 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
 }
 
 void cfg80211_disconnected(struct net_device *dev, u16 reason,
-                          u8 *ie, size_t ie_len, gfp_t gfp)
+                          const u8 *ie, size_t ie_len, gfp_t gfp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_event *ev;
        unsigned long flags;
 
index aabccf13e07b6860ef92ddc637a7879a8f961aab..560ed77084e92b52cae0f299ca383eef240a42e6 100644 (file)
@@ -1876,29 +1876,33 @@ TRACE_EVENT(rdev_channel_switch,
                WIPHY_ENTRY
                NETDEV_ENTRY
                CHAN_DEF_ENTRY
-               __field(u16, counter_offset_beacon)
-               __field(u16, counter_offset_presp)
                __field(bool, radar_required)
                __field(bool, block_tx)
                __field(u8, count)
+               __dynamic_array(u16, bcn_ofs, params->n_counter_offsets_beacon)
+               __dynamic_array(u16, pres_ofs, params->n_counter_offsets_presp)
        ),
        TP_fast_assign(
                WIPHY_ASSIGN;
                NETDEV_ASSIGN;
                CHAN_DEF_ASSIGN(&params->chandef);
-               __entry->counter_offset_beacon = params->counter_offset_beacon;
-               __entry->counter_offset_presp = params->counter_offset_presp;
                __entry->radar_required = params->radar_required;
                __entry->block_tx = params->block_tx;
                __entry->count = params->count;
+               memcpy(__get_dynamic_array(bcn_ofs),
+                      params->counter_offsets_beacon,
+                      params->n_counter_offsets_beacon * sizeof(u16));
+
+               /* probe response offsets are optional */
+               if (params->n_counter_offsets_presp)
+                       memcpy(__get_dynamic_array(pres_ofs),
+                              params->counter_offsets_presp,
+                              params->n_counter_offsets_presp * sizeof(u16));
        ),
        TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT
-                 ", block_tx: %d, count: %u, radar_required: %d"
-                 ", counter offsets (beacon/presp): %u/%u",
+                 ", block_tx: %d, count: %u, radar_required: %d",
                  WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG,
-                 __entry->block_tx, __entry->count, __entry->radar_required,
-                 __entry->counter_offset_beacon,
-                 __entry->counter_offset_presp)
+                 __entry->block_tx, __entry->count, __entry->radar_required)
 );
 
 TRACE_EVENT(rdev_set_qos_map,
@@ -1919,6 +1923,24 @@ TRACE_EVENT(rdev_set_qos_map,
                  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->num_des)
 );
 
+TRACE_EVENT(rdev_set_ap_chanwidth,
+       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+                struct cfg80211_chan_def *chandef),
+       TP_ARGS(wiphy, netdev, chandef),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               NETDEV_ENTRY
+               CHAN_DEF_ENTRY
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               NETDEV_ASSIGN;
+               CHAN_DEF_ASSIGN(chandef);
+       ),
+       TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT,
+                 WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
+);
+
 /*************************************************************
  *          cfg80211 exported functions traces              *
  *************************************************************/
@@ -2193,18 +2215,21 @@ TRACE_EVENT(cfg80211_cqm_rssi_notify,
 );
 
 TRACE_EVENT(cfg80211_reg_can_beacon,
-       TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
-       TP_ARGS(wiphy, chandef),
+       TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef,
+                enum nl80211_iftype iftype),
+       TP_ARGS(wiphy, chandef, iftype),
        TP_STRUCT__entry(
                WIPHY_ENTRY
                CHAN_DEF_ENTRY
+               __field(enum nl80211_iftype, iftype)
        ),
        TP_fast_assign(
                WIPHY_ASSIGN;
                CHAN_DEF_ASSIGN(chandef);
+               __entry->iftype = iftype;
        ),
-       TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
-                 WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
+       TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", iftype=%d",
+                 WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->iftype)
 );
 
 TRACE_EVENT(cfg80211_chandef_dfs_required,
@@ -2615,6 +2640,21 @@ TRACE_EVENT(cfg80211_ft_event,
                  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(target_ap))
 );
 
+TRACE_EVENT(cfg80211_stop_iface,
+       TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+       TP_ARGS(wiphy, wdev),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               WDEV_ENTRY
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               WDEV_ASSIGN;
+       ),
+       TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT,
+                 WIPHY_PR_ARG, WDEV_PR_ARG)
+);
+
 #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
 
 #undef TRACE_INCLUDE_PATH
index e5872ff2c27ca8989ca6da7cfdf4d7041c29a72e..728f1c0dc70dbdb7c85558b84b83dc9b66e4083f 100644 (file)
@@ -476,7 +476,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
 EXPORT_SYMBOL(ieee80211_data_to_8023);
 
 int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
-                            enum nl80211_iftype iftype, u8 *bssid, bool qos)
+                            enum nl80211_iftype iftype,
+                            const u8 *bssid, bool qos)
 {
        struct ieee80211_hdr hdr;
        u16 hdrlen, ethertype;
@@ -770,7 +771,7 @@ EXPORT_SYMBOL(ieee80211_bss_get_ie);
 
 void cfg80211_upload_connect_keys(struct wireless_dev *wdev)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct net_device *dev = wdev->netdev;
        int i;
 
@@ -839,6 +840,9 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev)
                        __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid,
                                               ev->ij.channel);
                        break;
+               case EVENT_STOPPED:
+                       __cfg80211_leave(wiphy_to_rdev(wdev->wiphy), wdev);
+                       break;
                }
                wdev_unlock(wdev);
 
@@ -888,11 +892,6 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
                return -EBUSY;
 
        if (ntype != otype && netif_running(dev)) {
-               err = cfg80211_can_change_interface(rdev, dev->ieee80211_ptr,
-                                                   ntype);
-               if (err)
-                       return err;
-
                dev->ieee80211_ptr->use_4addr = false;
                dev->ieee80211_ptr->mesh_id_up_len = 0;
                wdev_lock(dev->ieee80211_ptr);
@@ -1268,6 +1267,120 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
        return res;
 }
 
+int cfg80211_iter_combinations(struct wiphy *wiphy,
+                              const int num_different_channels,
+                              const u8 radar_detect,
+                              const int iftype_num[NUM_NL80211_IFTYPES],
+                              void (*iter)(const struct ieee80211_iface_combination *c,
+                                           void *data),
+                              void *data)
+{
+       const struct ieee80211_regdomain *regdom;
+       enum nl80211_dfs_regions region = 0;
+       int i, j, iftype;
+       int num_interfaces = 0;
+       u32 used_iftypes = 0;
+
+       if (radar_detect) {
+               rcu_read_lock();
+               regdom = rcu_dereference(cfg80211_regdomain);
+               if (regdom)
+                       region = regdom->dfs_region;
+               rcu_read_unlock();
+       }
+
+       for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) {
+               num_interfaces += iftype_num[iftype];
+               if (iftype_num[iftype] > 0 &&
+                   !(wiphy->software_iftypes & BIT(iftype)))
+                       used_iftypes |= BIT(iftype);
+       }
+
+       for (i = 0; i < wiphy->n_iface_combinations; i++) {
+               const struct ieee80211_iface_combination *c;
+               struct ieee80211_iface_limit *limits;
+               u32 all_iftypes = 0;
+
+               c = &wiphy->iface_combinations[i];
+
+               if (num_interfaces > c->max_interfaces)
+                       continue;
+               if (num_different_channels > c->num_different_channels)
+                       continue;
+
+               limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits,
+                                GFP_KERNEL);
+               if (!limits)
+                       return -ENOMEM;
+
+               for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) {
+                       if (wiphy->software_iftypes & BIT(iftype))
+                               continue;
+                       for (j = 0; j < c->n_limits; j++) {
+                               all_iftypes |= limits[j].types;
+                               if (!(limits[j].types & BIT(iftype)))
+                                       continue;
+                               if (limits[j].max < iftype_num[iftype])
+                                       goto cont;
+                               limits[j].max -= iftype_num[iftype];
+                       }
+               }
+
+               if (radar_detect != (c->radar_detect_widths & radar_detect))
+                       goto cont;
+
+               if (radar_detect && c->radar_detect_regions &&
+                   !(c->radar_detect_regions & BIT(region)))
+                       goto cont;
+
+               /* Finally check that all iftypes that we're currently
+                * using are actually part of this combination. If they
+                * aren't then we can't use this combination and have
+                * to continue to the next.
+                */
+               if ((all_iftypes & used_iftypes) != used_iftypes)
+                       goto cont;
+
+               /* This combination covered all interface types and
+                * supported the requested numbers, so we're good.
+                */
+
+               (*iter)(c, data);
+ cont:
+               kfree(limits);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(cfg80211_iter_combinations);
+
+static void
+cfg80211_iter_sum_ifcombs(const struct ieee80211_iface_combination *c,
+                         void *data)
+{
+       int *num = data;
+       (*num)++;
+}
+
+int cfg80211_check_combinations(struct wiphy *wiphy,
+                               const int num_different_channels,
+                               const u8 radar_detect,
+                               const int iftype_num[NUM_NL80211_IFTYPES])
+{
+       int err, num = 0;
+
+       err = cfg80211_iter_combinations(wiphy, num_different_channels,
+                                        radar_detect, iftype_num,
+                                        cfg80211_iter_sum_ifcombs, &num);
+       if (err)
+               return err;
+       if (num == 0)
+               return -EBUSY;
+
+       return 0;
+}
+EXPORT_SYMBOL(cfg80211_check_combinations);
+
 int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
                                 struct wireless_dev *wdev,
                                 enum nl80211_iftype iftype,
@@ -1276,7 +1389,6 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
                                 u8 radar_detect)
 {
        struct wireless_dev *wdev_iter;
-       u32 used_iftypes = BIT(iftype);
        int num[NUM_NL80211_IFTYPES];
        struct ieee80211_channel
                        *used_channels[CFG80211_MAX_NUM_DIFFERENT_CHANNELS];
@@ -1284,7 +1396,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
        enum cfg80211_chan_mode chmode;
        int num_different_channels = 0;
        int total = 1;
-       int i, j;
+       int i;
 
        ASSERT_RTNL();
 
@@ -1306,6 +1418,11 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
 
        num[iftype] = 1;
 
+       /* TODO: We'll probably not need this anymore, since this
+        * should only be called with CHAN_MODE_UNDEFINED. There are
+        * still a couple of pending calls where other chanmodes are
+        * used, but we should get rid of them.
+        */
        switch (chanmode) {
        case CHAN_MODE_UNDEFINED:
                break;
@@ -1369,65 +1486,13 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
 
                num[wdev_iter->iftype]++;
                total++;
-               used_iftypes |= BIT(wdev_iter->iftype);
        }
 
        if (total == 1 && !radar_detect)
                return 0;
 
-       for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) {
-               const struct ieee80211_iface_combination *c;
-               struct ieee80211_iface_limit *limits;
-               u32 all_iftypes = 0;
-
-               c = &rdev->wiphy.iface_combinations[i];
-
-               if (total > c->max_interfaces)
-                       continue;
-               if (num_different_channels > c->num_different_channels)
-                       continue;
-
-               limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits,
-                                GFP_KERNEL);
-               if (!limits)
-                       return -ENOMEM;
-
-               for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) {
-                       if (rdev->wiphy.software_iftypes & BIT(iftype))
-                               continue;
-                       for (j = 0; j < c->n_limits; j++) {
-                               all_iftypes |= limits[j].types;
-                               if (!(limits[j].types & BIT(iftype)))
-                                       continue;
-                               if (limits[j].max < num[iftype])
-                                       goto cont;
-                               limits[j].max -= num[iftype];
-                       }
-               }
-
-               if (radar_detect && !(c->radar_detect_widths & radar_detect))
-                       goto cont;
-
-               /*
-                * Finally check that all iftypes that we're currently
-                * using are actually part of this combination. If they
-                * aren't then we can't use this combination and have
-                * to continue to the next.
-                */
-               if ((all_iftypes & used_iftypes) != used_iftypes)
-                       goto cont;
-
-               /*
-                * This combination covered all interface types and
-                * supported the requested numbers, so we're good.
-                */
-               kfree(limits);
-               return 0;
- cont:
-               kfree(limits);
-       }
-
-       return -EBUSY;
+       return cfg80211_check_combinations(&rdev->wiphy, num_different_channels,
+                                          radar_detect, num);
 }
 
 int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,
@@ -1481,6 +1546,24 @@ unsigned int ieee80211_get_num_supported_channels(struct wiphy *wiphy)
 }
 EXPORT_SYMBOL(ieee80211_get_num_supported_channels);
 
+int cfg80211_get_station(struct net_device *dev, const u8 *mac_addr,
+                        struct station_info *sinfo)
+{
+       struct cfg80211_registered_device *rdev;
+       struct wireless_dev *wdev;
+
+       wdev = dev->ieee80211_ptr;
+       if (!wdev)
+               return -EOPNOTSUPP;
+
+       rdev = wiphy_to_rdev(wdev->wiphy);
+       if (!rdev->ops->get_station)
+               return -EOPNOTSUPP;
+
+       return rdev_get_station(rdev, dev, mac_addr, sinfo);
+}
+EXPORT_SYMBOL(cfg80211_get_station);
+
 /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
 /* Ethernet-II snap header (RFC1042 for most EtherTypes) */
 const unsigned char rfc1042_header[] __aligned(2) =
index 5661a54ac7ee4ed1c1865d855e1b2681c67d07cc..11120bb14162505043579628bed2ad131ba41f7d 100644 (file)
@@ -73,7 +73,7 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
        struct vif_params vifparams;
        enum nl80211_iftype type;
 
-       rdev = wiphy_to_dev(wdev->wiphy);
+       rdev = wiphy_to_rdev(wdev->wiphy);
 
        switch (*mode) {
        case IW_MODE_INFRA:
@@ -253,12 +253,12 @@ EXPORT_SYMBOL_GPL(cfg80211_wext_giwrange);
 
 /**
  * cfg80211_wext_freq - get wext frequency for non-"auto"
- * @wiphy: the wiphy
+ * @dev: the net device
  * @freq: the wext freq encoding
  *
  * Returns a frequency, or a negative error code, or 0 for auto.
  */
-int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq)
+int cfg80211_wext_freq(struct iw_freq *freq)
 {
        /*
         * Parse frequency - return 0 for auto and
@@ -286,7 +286,7 @@ int cfg80211_wext_siwrts(struct net_device *dev,
                         struct iw_param *rts, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        u32 orts = wdev->wiphy->rts_threshold;
        int err;
 
@@ -324,7 +324,7 @@ int cfg80211_wext_siwfrag(struct net_device *dev,
                          struct iw_param *frag, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        u32 ofrag = wdev->wiphy->frag_threshold;
        int err;
 
@@ -364,7 +364,7 @@ static int cfg80211_wext_siwretry(struct net_device *dev,
                                  struct iw_param *retry, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        u32 changed = 0;
        u8 olong = wdev->wiphy->retry_long;
        u8 oshort = wdev->wiphy->retry_short;
@@ -587,7 +587,7 @@ static int cfg80211_wext_siwencode(struct net_device *dev,
                                   struct iw_point *erq, char *keybuf)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        int idx, err;
        bool remove = false;
        struct key_params params;
@@ -647,7 +647,7 @@ static int cfg80211_wext_siwencodeext(struct net_device *dev,
                                      struct iw_point *erq, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;
        const u8 *addr;
        int idx;
@@ -775,7 +775,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
                                 struct iw_freq *wextfreq, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_chan_def chandef = {
                .width = NL80211_CHAN_WIDTH_20_NOHT,
        };
@@ -787,7 +787,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
        case NL80211_IFTYPE_ADHOC:
                return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra);
        case NL80211_IFTYPE_MONITOR:
-               freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
+               freq = cfg80211_wext_freq(wextfreq);
                if (freq < 0)
                        return freq;
                if (freq == 0)
@@ -798,7 +798,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
                        return -EINVAL;
                return cfg80211_set_monitor_channel(rdev, &chandef);
        case NL80211_IFTYPE_MESH_POINT:
-               freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
+               freq = cfg80211_wext_freq(wextfreq);
                if (freq < 0)
                        return freq;
                if (freq == 0)
@@ -818,7 +818,7 @@ static int cfg80211_wext_giwfreq(struct net_device *dev,
                                 struct iw_freq *freq, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_chan_def chandef;
        int ret;
 
@@ -847,7 +847,7 @@ static int cfg80211_wext_siwtxpower(struct net_device *dev,
                                    union iwreq_data *data, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        enum nl80211_tx_power_setting type;
        int dbm = 0;
 
@@ -899,7 +899,7 @@ static int cfg80211_wext_giwtxpower(struct net_device *dev,
                                    union iwreq_data *data, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        int err, val;
 
        if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM)
@@ -1119,7 +1119,7 @@ static int cfg80211_wext_siwpower(struct net_device *dev,
                                  struct iw_param *wrq, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        bool ps = wdev->ps;
        int timeout = wdev->ps_timeout;
        int err;
@@ -1177,7 +1177,7 @@ static int cfg80211_wds_wext_siwap(struct net_device *dev,
                                   struct sockaddr *addr, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        int err;
 
        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_WDS))
@@ -1221,7 +1221,7 @@ static int cfg80211_wext_siwrate(struct net_device *dev,
                                 struct iw_param *rate, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_bitrate_mask mask;
        u32 fixed, maxrate;
        struct ieee80211_supported_band *sband;
@@ -1272,7 +1272,7 @@ static int cfg80211_wext_giwrate(struct net_device *dev,
                                 struct iw_param *rate, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        /* we are under RTNL - globally locked - so can use a static struct */
        static struct station_info sinfo;
        u8 addr[ETH_ALEN];
@@ -1310,7 +1310,7 @@ static int cfg80211_wext_giwrate(struct net_device *dev,
 static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        /* we are under RTNL - globally locked - so can use static structs */
        static struct iw_statistics wstats;
        static struct station_info sinfo;
@@ -1449,7 +1449,7 @@ static int cfg80211_wext_siwpmksa(struct net_device *dev,
                                  struct iw_point *data, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_pmksa cfg_pmksa;
        struct iw_pmksa *pmksa = (struct iw_pmksa *)extra;
 
index 5d766b0118e81969ff4f24c59b88bffdaa6496ff..ebcacca2f731941123efb22c8067a6c34aaa9ea1 100644 (file)
@@ -50,7 +50,7 @@ int cfg80211_wext_siwgenie(struct net_device *dev,
                           struct iw_point *data, char *extra);
 
 
-int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq);
+int cfg80211_wext_freq(struct iw_freq *freq);
 
 
 extern const struct iw_handler_def cfg80211_wext_handler;
index 86c331a65664a77bfe6c083224b7eae2c91f5b9c..c7e5c8eb4f24708a27d90ae298a85c825ee81e38 100644 (file)
@@ -67,7 +67,7 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
                              struct iw_freq *wextfreq, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct ieee80211_channel *chan = NULL;
        int err, freq;
 
@@ -75,7 +75,7 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
                return -EINVAL;
 
-       freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
+       freq = cfg80211_wext_freq(wextfreq);
        if (freq < 0)
                return freq;
 
@@ -169,7 +169,7 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev,
                               struct iw_point *data, char *ssid)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        size_t len = data->length;
        int err;
 
@@ -260,7 +260,7 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev,
                            struct sockaddr *ap_addr, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        u8 *bssid = ap_addr->sa_data;
        int err;
 
@@ -333,7 +333,7 @@ int cfg80211_wext_siwgenie(struct net_device *dev,
                           struct iw_point *data, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        u8 *ie = extra;
        int ie_len = data->length, err;
 
@@ -390,7 +390,7 @@ int cfg80211_wext_siwmlme(struct net_device *dev,
        if (!wdev)
                return -EOPNOTSUPP;
 
-       rdev = wiphy_to_dev(wdev->wiphy);
+       rdev = wiphy_to_rdev(wdev->wiphy);
 
        if (wdev->iftype != NL80211_IFTYPE_STATION)
                return -EINVAL;
index 3bb2cdc13b46e1468743e1130d6c4c25e08921e0..c51e8f7b8653cb167aba13f61c2d7520d615c31c 100644 (file)
@@ -199,6 +199,7 @@ int xfrm_output(struct sk_buff *skb)
 
        return xfrm_output2(skb);
 }
+EXPORT_SYMBOL_GPL(xfrm_output);
 
 int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb)
 {
@@ -213,6 +214,7 @@ int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb)
                return -EAFNOSUPPORT;
        return inner_mode->afinfo->extract_output(x, skb);
 }
+EXPORT_SYMBOL_GPL(xfrm_inner_extract_output);
 
 void xfrm_local_error(struct sk_buff *skb, int mtu)
 {
@@ -233,7 +235,4 @@ void xfrm_local_error(struct sk_buff *skb, int mtu)
        afinfo->local_error(skb, mtu);
        xfrm_state_put_afinfo(afinfo);
 }
-
-EXPORT_SYMBOL_GPL(xfrm_output);
-EXPORT_SYMBOL_GPL(xfrm_inner_extract_output);
 EXPORT_SYMBOL_GPL(xfrm_local_error);
index c08fbd11ceff52ee145316a33758feb30667f02d..a8ef5108e0d86cbc5c411f3db378fde5a0d54f18 100644 (file)
@@ -769,7 +769,7 @@ EXPORT_SYMBOL(xfrm_policy_byid);
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
 static inline int
-xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audit_info)
+xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid)
 {
        int dir, err = 0;
 
@@ -783,10 +783,7 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audi
                                continue;
                        err = security_xfrm_policy_delete(pol->security);
                        if (err) {
-                               xfrm_audit_policy_delete(pol, 0,
-                                                        audit_info->loginuid,
-                                                        audit_info->sessionid,
-                                                        audit_info->secid);
+                               xfrm_audit_policy_delete(pol, 0, task_valid);
                                return err;
                        }
                }
@@ -800,9 +797,7 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audi
                                                                pol->security);
                                if (err) {
                                        xfrm_audit_policy_delete(pol, 0,
-                                                       audit_info->loginuid,
-                                                       audit_info->sessionid,
-                                                       audit_info->secid);
+                                                                task_valid);
                                        return err;
                                }
                        }
@@ -812,19 +807,19 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audi
 }
 #else
 static inline int
-xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audit_info)
+xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid)
 {
        return 0;
 }
 #endif
 
-int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info)
+int xfrm_policy_flush(struct net *net, u8 type, bool task_valid)
 {
        int dir, err = 0, cnt = 0;
 
        write_lock_bh(&net->xfrm.xfrm_policy_lock);
 
-       err = xfrm_policy_flush_secctx_check(net, type, audit_info);
+       err = xfrm_policy_flush_secctx_check(net, type, task_valid);
        if (err)
                goto out;
 
@@ -841,9 +836,7 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info)
                        write_unlock_bh(&net->xfrm.xfrm_policy_lock);
                        cnt++;
 
-                       xfrm_audit_policy_delete(pol, 1, audit_info->loginuid,
-                                                audit_info->sessionid,
-                                                audit_info->secid);
+                       xfrm_audit_policy_delete(pol, 1, task_valid);
 
                        xfrm_policy_kill(pol);
 
@@ -862,10 +855,7 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info)
                                write_unlock_bh(&net->xfrm.xfrm_policy_lock);
                                cnt++;
 
-                               xfrm_audit_policy_delete(pol, 1,
-                                                        audit_info->loginuid,
-                                                        audit_info->sessionid,
-                                                        audit_info->secid);
+                               xfrm_audit_policy_delete(pol, 1, task_valid);
                                xfrm_policy_kill(pol);
 
                                write_lock_bh(&net->xfrm.xfrm_policy_lock);
@@ -2783,21 +2773,19 @@ static struct notifier_block xfrm_dev_notifier = {
 static int __net_init xfrm_statistics_init(struct net *net)
 {
        int rv;
-
-       if (snmp_mib_init((void __percpu **)net->mib.xfrm_statistics,
-                         sizeof(struct linux_xfrm_mib),
-                         __alignof__(struct linux_xfrm_mib)) < 0)
+       net->mib.xfrm_statistics = alloc_percpu(struct linux_xfrm_mib);
+       if (!net->mib.xfrm_statistics)
                return -ENOMEM;
        rv = xfrm_proc_init(net);
        if (rv < 0)
-               snmp_mib_free((void __percpu **)net->mib.xfrm_statistics);
+               free_percpu(net->mib.xfrm_statistics);
        return rv;
 }
 
 static void xfrm_statistics_fini(struct net *net)
 {
        xfrm_proc_fini(net);
-       snmp_mib_free((void __percpu **)net->mib.xfrm_statistics);
+       free_percpu(net->mib.xfrm_statistics);
 }
 #else
 static int __net_init xfrm_statistics_init(struct net *net)
@@ -2862,21 +2850,14 @@ out_byidx:
 
 static void xfrm_policy_fini(struct net *net)
 {
-       struct xfrm_audit audit_info;
        unsigned int sz;
        int dir;
 
        flush_work(&net->xfrm.policy_hash_work);
 #ifdef CONFIG_XFRM_SUB_POLICY
-       audit_info.loginuid = INVALID_UID;
-       audit_info.sessionid = (unsigned int)-1;
-       audit_info.secid = 0;
-       xfrm_policy_flush(net, XFRM_POLICY_TYPE_SUB, &audit_info);
+       xfrm_policy_flush(net, XFRM_POLICY_TYPE_SUB, false);
 #endif
-       audit_info.loginuid = INVALID_UID;
-       audit_info.sessionid = (unsigned int)-1;
-       audit_info.secid = 0;
-       xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, &audit_info);
+       xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, false);
 
        WARN_ON(!list_empty(&net->xfrm.policy_all));
 
@@ -2991,15 +2972,14 @@ static void xfrm_audit_common_policyinfo(struct xfrm_policy *xp,
        }
 }
 
-void xfrm_audit_policy_add(struct xfrm_policy *xp, int result,
-                          kuid_t auid, unsigned int sessionid, u32 secid)
+void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, bool task_valid)
 {
        struct audit_buffer *audit_buf;
 
        audit_buf = xfrm_audit_start("SPD-add");
        if (audit_buf == NULL)
                return;
-       xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
+       xfrm_audit_helper_usrinfo(task_valid, audit_buf);
        audit_log_format(audit_buf, " res=%u", result);
        xfrm_audit_common_policyinfo(xp, audit_buf);
        audit_log_end(audit_buf);
@@ -3007,14 +2987,14 @@ void xfrm_audit_policy_add(struct xfrm_policy *xp, int result,
 EXPORT_SYMBOL_GPL(xfrm_audit_policy_add);
 
 void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result,
-                             kuid_t auid, unsigned int sessionid, u32 secid)
+                             bool task_valid)
 {
        struct audit_buffer *audit_buf;
 
        audit_buf = xfrm_audit_start("SPD-delete");
        if (audit_buf == NULL)
                return;
-       xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
+       xfrm_audit_helper_usrinfo(task_valid, audit_buf);
        audit_log_format(audit_buf, " res=%u", result);
        xfrm_audit_common_policyinfo(xp, audit_buf);
        audit_log_end(audit_buf);
index fc5abd0b456f3a3abf8163821f22577b8d700f6f..9c4fbd8935f48e28c3c86d9da443904f55f93ec3 100644 (file)
@@ -54,8 +54,7 @@ static int xfrm_statistics_seq_show(struct seq_file *seq, void *v)
        int i;
        for (i = 0; xfrm_mib_list[i].name; i++)
                seq_printf(seq, "%-24s\t%lu\n", xfrm_mib_list[i].name,
-                          snmp_fold_field((void __percpu **)
-                                          net->mib.xfrm_statistics,
+                          snmp_fold_field(net->mib.xfrm_statistics,
                                           xfrm_mib_list[i].entry));
        return 0;
 }
index 8e9c781a6bbaaba83e4af4a31ac7a07a70dd6c71..0ab54134bb40b84cf825df6035a9acf7cb3cf760 100644 (file)
@@ -463,9 +463,7 @@ expired:
        if (!err)
                km_state_expired(x, 1, 0);
 
-       xfrm_audit_state_delete(x, err ? 0 : 1,
-                               audit_get_loginuid(current),
-                               audit_get_sessionid(current), 0);
+       xfrm_audit_state_delete(x, err ? 0 : 1, true);
 
 out:
        spin_unlock(&x->lock);
@@ -562,7 +560,7 @@ EXPORT_SYMBOL(xfrm_state_delete);
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
 static inline int
-xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audit_info)
+xfrm_state_flush_secctx_check(struct net *net, u8 proto, bool task_valid)
 {
        int i, err = 0;
 
@@ -572,10 +570,7 @@ xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audi
                hlist_for_each_entry(x, net->xfrm.state_bydst+i, bydst) {
                        if (xfrm_id_proto_match(x->id.proto, proto) &&
                           (err = security_xfrm_state_delete(x)) != 0) {
-                               xfrm_audit_state_delete(x, 0,
-                                                       audit_info->loginuid,
-                                                       audit_info->sessionid,
-                                                       audit_info->secid);
+                               xfrm_audit_state_delete(x, 0, task_valid);
                                return err;
                        }
                }
@@ -585,18 +580,18 @@ xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audi
 }
 #else
 static inline int
-xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audit_info)
+xfrm_state_flush_secctx_check(struct net *net, u8 proto, bool task_valid)
 {
        return 0;
 }
 #endif
 
-int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info)
+int xfrm_state_flush(struct net *net, u8 proto, bool task_valid)
 {
        int i, err = 0, cnt = 0;
 
        spin_lock_bh(&net->xfrm.xfrm_state_lock);
-       err = xfrm_state_flush_secctx_check(net, proto, audit_info);
+       err = xfrm_state_flush_secctx_check(net, proto, task_valid);
        if (err)
                goto out;
 
@@ -612,9 +607,7 @@ restart:
 
                                err = xfrm_state_delete(x);
                                xfrm_audit_state_delete(x, err ? 0 : 1,
-                                                       audit_info->loginuid,
-                                                       audit_info->sessionid,
-                                                       audit_info->secid);
+                                                       task_valid);
                                xfrm_state_put(x);
                                if (!err)
                                        cnt++;
@@ -2128,14 +2121,10 @@ out_bydst:
 
 void xfrm_state_fini(struct net *net)
 {
-       struct xfrm_audit audit_info;
        unsigned int sz;
 
        flush_work(&net->xfrm.state_hash_work);
-       audit_info.loginuid = INVALID_UID;
-       audit_info.sessionid = (unsigned int)-1;
-       audit_info.secid = 0;
-       xfrm_state_flush(net, IPSEC_PROTO_ANY, &audit_info);
+       xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
        flush_work(&net->xfrm.state_gc_work);
 
        WARN_ON(!list_empty(&net->xfrm.state_all));
@@ -2198,30 +2187,28 @@ static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
        }
 }
 
-void xfrm_audit_state_add(struct xfrm_state *x, int result,
-                         kuid_t auid, unsigned int sessionid, u32 secid)
+void xfrm_audit_state_add(struct xfrm_state *x, int result, bool task_valid)
 {
        struct audit_buffer *audit_buf;
 
        audit_buf = xfrm_audit_start("SAD-add");
        if (audit_buf == NULL)
                return;
-       xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
+       xfrm_audit_helper_usrinfo(task_valid, audit_buf);
        xfrm_audit_helper_sainfo(x, audit_buf);
        audit_log_format(audit_buf, " res=%u", result);
        audit_log_end(audit_buf);
 }
 EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
 
-void xfrm_audit_state_delete(struct xfrm_state *x, int result,
-                            kuid_t auid, unsigned int sessionid, u32 secid)
+void xfrm_audit_state_delete(struct xfrm_state *x, int result, bool task_valid)
 {
        struct audit_buffer *audit_buf;
 
        audit_buf = xfrm_audit_start("SAD-delete");
        if (audit_buf == NULL)
                return;
-       xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
+       xfrm_audit_helper_usrinfo(task_valid, audit_buf);
        xfrm_audit_helper_sainfo(x, audit_buf);
        audit_log_format(audit_buf, " res=%u", result);
        audit_log_end(audit_buf);
index 51398ae6cda85000b5edc416e9569659495a6614..412d9dc3a8731ec580d4b6596fec491fb10a2a48 100644 (file)
@@ -597,9 +597,6 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
        struct xfrm_state *x;
        int err;
        struct km_event c;
-       kuid_t loginuid = audit_get_loginuid(current);
-       unsigned int sessionid = audit_get_sessionid(current);
-       u32 sid;
 
        err = verify_newsa_info(p, attrs);
        if (err)
@@ -615,8 +612,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
        else
                err = xfrm_state_update(x);
 
-       security_task_getsecid(current, &sid);
-       xfrm_audit_state_add(x, err ? 0 : 1, loginuid, sessionid, sid);
+       xfrm_audit_state_add(x, err ? 0 : 1, true);
 
        if (err < 0) {
                x->km.state = XFRM_STATE_DEAD;
@@ -676,9 +672,6 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
        int err = -ESRCH;
        struct km_event c;
        struct xfrm_usersa_id *p = nlmsg_data(nlh);
-       kuid_t loginuid = audit_get_loginuid(current);
-       unsigned int sessionid = audit_get_sessionid(current);
-       u32 sid;
 
        x = xfrm_user_state_lookup(net, p, attrs, &err);
        if (x == NULL)
@@ -703,8 +696,7 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
        km_state_notify(x, &c);
 
 out:
-       security_task_getsecid(current, &sid);
-       xfrm_audit_state_delete(x, err ? 0 : 1, loginuid, sessionid, sid);
+       xfrm_audit_state_delete(x, err ? 0 : 1, true);
        xfrm_state_put(x);
        return err;
 }
@@ -955,6 +947,20 @@ static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb,
        return skb;
 }
 
+/* A wrapper for nlmsg_multicast() checking that nlsk is still available.
+ * Must be called with RCU read lock.
+ */
+static inline int xfrm_nlmsg_multicast(struct net *net, struct sk_buff *skb,
+                                      u32 pid, unsigned int group)
+{
+       struct sock *nlsk = rcu_dereference(net->xfrm.nlsk);
+
+       if (nlsk)
+               return nlmsg_multicast(nlsk, skb, pid, group, GFP_ATOMIC);
+       else
+               return -1;
+}
+
 static inline size_t xfrm_spdinfo_msgsize(void)
 {
        return NLMSG_ALIGN(4)
@@ -1414,9 +1420,6 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
        struct km_event c;
        int err;
        int excl;
-       kuid_t loginuid = audit_get_loginuid(current);
-       unsigned int sessionid = audit_get_sessionid(current);
-       u32 sid;
 
        err = verify_newpolicy_info(p);
        if (err)
@@ -1435,8 +1438,7 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
         * a type XFRM_MSG_UPDPOLICY - JHS */
        excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY;
        err = xfrm_policy_insert(p->dir, xp, excl);
-       security_task_getsecid(current, &sid);
-       xfrm_audit_policy_add(xp, err ? 0 : 1, loginuid, sessionid, sid);
+       xfrm_audit_policy_add(xp, err ? 0 : 1, true);
 
        if (err) {
                security_xfrm_policy_free(xp->security);
@@ -1673,13 +1675,7 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
                                            NETLINK_CB(skb).portid);
                }
        } else {
-               kuid_t loginuid = audit_get_loginuid(current);
-               unsigned int sessionid = audit_get_sessionid(current);
-               u32 sid;
-
-               security_task_getsecid(current, &sid);
-               xfrm_audit_policy_delete(xp, err ? 0 : 1, loginuid, sessionid,
-                                        sid);
+               xfrm_audit_policy_delete(xp, err ? 0 : 1, true);
 
                if (err != 0)
                        goto out;
@@ -1704,13 +1700,9 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
        struct net *net = sock_net(skb->sk);
        struct km_event c;
        struct xfrm_usersa_flush *p = nlmsg_data(nlh);
-       struct xfrm_audit audit_info;
        int err;
 
-       audit_info.loginuid = audit_get_loginuid(current);
-       audit_info.sessionid = audit_get_sessionid(current);
-       security_task_getsecid(current, &audit_info.secid);
-       err = xfrm_state_flush(net, p->proto, &audit_info);
+       err = xfrm_state_flush(net, p->proto, true);
        if (err) {
                if (err == -ESRCH) /* empty table */
                        return 0;
@@ -1894,16 +1886,12 @@ static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
        struct km_event c;
        u8 type = XFRM_POLICY_TYPE_MAIN;
        int err;
-       struct xfrm_audit audit_info;
 
        err = copy_from_user_policy_type(&type, attrs);
        if (err)
                return err;
 
-       audit_info.loginuid = audit_get_loginuid(current);
-       audit_info.sessionid = audit_get_sessionid(current);
-       security_task_getsecid(current, &audit_info.secid);
-       err = xfrm_policy_flush(net, type, &audit_info);
+       err = xfrm_policy_flush(net, type, true);
        if (err) {
                if (err == -ESRCH) /* empty table */
                        return 0;
@@ -1969,14 +1957,8 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
 
        err = 0;
        if (up->hard) {
-               kuid_t loginuid = audit_get_loginuid(current);
-               unsigned int sessionid = audit_get_sessionid(current);
-               u32 sid;
-
-               security_task_getsecid(current, &sid);
                xfrm_policy_delete(xp, p->dir);
-               xfrm_audit_policy_delete(xp, 1, loginuid, sessionid, sid);
-
+               xfrm_audit_policy_delete(xp, 1, true);
        } else {
                // reset the timers here?
                WARN(1, "Dont know what to do with soft policy expire\n");
@@ -2012,13 +1994,8 @@ static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
        km_state_expired(x, ue->hard, nlh->nlmsg_pid);
 
        if (ue->hard) {
-               kuid_t loginuid = audit_get_loginuid(current);
-               unsigned int sessionid = audit_get_sessionid(current);
-               u32 sid;
-
-               security_task_getsecid(current, &sid);
                __xfrm_state_delete(x);
-               xfrm_audit_state_delete(x, 1, loginuid, sessionid, sid);
+               xfrm_audit_state_delete(x, 1, true);
        }
        err = 0;
 out:
@@ -2265,7 +2242,7 @@ static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
        if (build_migrate(skb, m, num_migrate, k, sel, dir, type) < 0)
                BUG();
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MIGRATE, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MIGRATE);
 }
 #else
 static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
@@ -2456,7 +2433,7 @@ static int xfrm_exp_state_notify(struct xfrm_state *x, const struct km_event *c)
                return -EMSGSIZE;
        }
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE);
 }
 
 static int xfrm_aevent_state_notify(struct xfrm_state *x, const struct km_event *c)
@@ -2471,7 +2448,7 @@ static int xfrm_aevent_state_notify(struct xfrm_state *x, const struct km_event
        if (build_aevent(skb, x, c) < 0)
                BUG();
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_AEVENTS, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_AEVENTS);
 }
 
 static int xfrm_notify_sa_flush(const struct km_event *c)
@@ -2497,7 +2474,7 @@ static int xfrm_notify_sa_flush(const struct km_event *c)
 
        nlmsg_end(skb, nlh);
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_SA);
 }
 
 static inline size_t xfrm_sa_len(struct xfrm_state *x)
@@ -2584,7 +2561,7 @@ static int xfrm_notify_sa(struct xfrm_state *x, const struct km_event *c)
 
        nlmsg_end(skb, nlh);
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_SA);
 
 out_free_skb:
        kfree_skb(skb);
@@ -2675,7 +2652,7 @@ static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt,
        if (build_acquire(skb, x, xt, xp) < 0)
                BUG();
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_ACQUIRE, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_ACQUIRE);
 }
 
 /* User gives us xfrm_user_policy_info followed by an array of 0
@@ -2789,7 +2766,7 @@ static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, const struct
        if (build_polexpire(skb, xp, dir, c) < 0)
                BUG();
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE);
 }
 
 static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c)
@@ -2851,7 +2828,7 @@ static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_e
 
        nlmsg_end(skb, nlh);
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_POLICY);
 
 out_free_skb:
        kfree_skb(skb);
@@ -2879,7 +2856,7 @@ static int xfrm_notify_policy_flush(const struct km_event *c)
 
        nlmsg_end(skb, nlh);
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_POLICY);
 
 out_free_skb:
        kfree_skb(skb);
@@ -2948,7 +2925,7 @@ static int xfrm_send_report(struct net *net, u8 proto,
        if (build_report(skb, proto, sel, addr) < 0)
                BUG();
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_REPORT, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_REPORT);
 }
 
 static inline size_t xfrm_mapping_msgsize(void)
@@ -3000,7 +2977,7 @@ static int xfrm_send_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr,
        if (build_mapping(skb, x, ipaddr, sport) < 0)
                BUG();
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MAPPING, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MAPPING);
 }
 
 static bool xfrm_is_alive(const struct km_event *c)
index 86ea0c3ad97511e02a75003dc3c8556990b0e9a9..01562e0d499202a0005c61443f36bdf11a33160c 100644 (file)
@@ -40,8 +40,9 @@ static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr,
        return count;
 }
 
+/* Sysfs attributes cannot be world-writable. */
 static struct kobj_attribute foo_attribute =
-       __ATTR(foo, 0666, foo_show, foo_store);
+       __ATTR(foo, 0664, foo_show, foo_store);
 
 /*
  * More complex function where we determine which variable is being accessed by
@@ -73,9 +74,9 @@ static ssize_t b_store(struct kobject *kobj, struct kobj_attribute *attr,
 }
 
 static struct kobj_attribute baz_attribute =
-       __ATTR(baz, 0666, b_show, b_store);
+       __ATTR(baz, 0664, b_show, b_store);
 static struct kobj_attribute bar_attribute =
-       __ATTR(bar, 0666, b_show, b_store);
+       __ATTR(bar, 0664, b_show, b_store);
 
 
 /*
index 5dce351f131f8c154048124bc1bf3a6a32cb32c7..ab5e447ec23871b66b46049129dfaafc6f3f287d 100644 (file)
@@ -124,8 +124,9 @@ static ssize_t foo_store(struct foo_obj *foo_obj, struct foo_attribute *attr,
        return count;
 }
 
+/* Sysfs attributes cannot be world-writable. */
 static struct foo_attribute foo_attribute =
-       __ATTR(foo, 0666, foo_show, foo_store);
+       __ATTR(foo, 0664, foo_show, foo_store);
 
 /*
  * More complex function where we determine which variable is being accessed by
@@ -157,9 +158,9 @@ static ssize_t b_store(struct foo_obj *foo_obj, struct foo_attribute *attr,
 }
 
 static struct foo_attribute baz_attribute =
-       __ATTR(baz, 0666, b_show, b_store);
+       __ATTR(baz, 0664, b_show, b_store);
 static struct foo_attribute bar_attribute =
-       __ATTR(bar, 0666, b_show, b_store);
+       __ATTR(bar, 0664, b_show, b_store);
 
 /*
  * Create a group of attributes so that we can create and destroy them all
index 1d07860f6c423661417d11fcfde21a8207f6de43..890df5c6adfbc16769a4abcb27bb3860fc625e71 100644 (file)
@@ -39,4 +39,4 @@ subdir-$(CONFIG_SECURITY_SELINUX) += selinux
 subdir-$(CONFIG_DTC)         += dtc
 
 # Let clean descend into subdirs
-subdir-        += basic kconfig package selinux
+subdir-        += basic kconfig package
index d17e0ea911edd568a039c905330420810223d07f..045e0098e96244be631ae8272c1d94968da3d7e9 100644 (file)
@@ -21,4 +21,3 @@ all: $(patsubst %, $(obj)/%, $(generic-y))
 
 $(obj)/%.h:
        $(call cmd,wrap)
-
index 003bc263105ae3fc5a92e671faa8654ac6809fa5..bf3e6778cd71addc1884b895a65cde06af11525e 100644 (file)
@@ -50,67 +50,6 @@ ifeq ($(KBUILD_NOPEDANTIC),)
         endif
 endif
 
-#
-# make W=... settings
-#
-# W=1 - warnings that may be relevant and does not occur too often
-# W=2 - warnings that occur quite often but may still be relevant
-# W=3 - the more obscure warnings, can most likely be ignored
-#
-# $(call cc-option, -W...) handles gcc -W.. options which
-# are not supported by all versions of the compiler
-ifdef KBUILD_ENABLE_EXTRA_GCC_CHECKS
-warning-  := $(empty)
-
-warning-1 := -Wextra -Wunused -Wno-unused-parameter
-warning-1 += -Wmissing-declarations
-warning-1 += -Wmissing-format-attribute
-warning-1 += $(call cc-option, -Wmissing-prototypes)
-warning-1 += -Wold-style-definition
-warning-1 += $(call cc-option, -Wmissing-include-dirs)
-warning-1 += $(call cc-option, -Wunused-but-set-variable)
-warning-1 += $(call cc-disable-warning, missing-field-initializers)
-
-# Clang
-warning-1 += $(call cc-disable-warning, initializer-overrides)
-warning-1 += $(call cc-disable-warning, unused-value)
-warning-1 += $(call cc-disable-warning, format)
-warning-1 += $(call cc-disable-warning, unknown-warning-option)
-warning-1 += $(call cc-disable-warning, sign-compare)
-warning-1 += $(call cc-disable-warning, format-zero-length)
-warning-1 += $(call cc-disable-warning, uninitialized)
-warning-1 += $(call cc-option, -fcatch-undefined-behavior)
-
-warning-2 := -Waggregate-return
-warning-2 += -Wcast-align
-warning-2 += -Wdisabled-optimization
-warning-2 += -Wnested-externs
-warning-2 += -Wshadow
-warning-2 += $(call cc-option, -Wlogical-op)
-warning-2 += $(call cc-option, -Wmissing-field-initializers)
-
-warning-3 := -Wbad-function-cast
-warning-3 += -Wcast-qual
-warning-3 += -Wconversion
-warning-3 += -Wpacked
-warning-3 += -Wpadded
-warning-3 += -Wpointer-arith
-warning-3 += -Wredundant-decls
-warning-3 += -Wswitch-default
-warning-3 += $(call cc-option, -Wpacked-bitfield-compat)
-warning-3 += $(call cc-option, -Wvla)
-
-warning := $(warning-$(findstring 1, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS)))
-warning += $(warning-$(findstring 2, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS)))
-warning += $(warning-$(findstring 3, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS)))
-
-ifeq ("$(strip $(warning))","")
-        $(error W=$(KBUILD_ENABLE_EXTRA_GCC_CHECKS) is unknown)
-endif
-
-KBUILD_CFLAGS += $(warning)
-endif
-
 include scripts/Makefile.lib
 
 ifdef host-progs
@@ -342,7 +281,7 @@ $(real-objs-m)      : modkern_aflags := $(KBUILD_AFLAGS_MODULE) $(AFLAGS_MODULE)
 $(real-objs-m:.o=.s): modkern_aflags := $(KBUILD_AFLAGS_MODULE) $(AFLAGS_MODULE)
 
 quiet_cmd_as_s_S = CPP $(quiet_modtag) $@
-cmd_as_s_S       = $(CPP) $(a_flags)   -o $@ $< 
+cmd_as_s_S       = $(CPP) $(a_flags)   -o $@ $<
 
 $(obj)/%.s: $(src)/%.S FORCE
        $(call if_changed_dep,as_s_S)
@@ -436,7 +375,7 @@ link_multi_deps =                     \
 $(filter $(addprefix $(obj)/,         \
 $($(subst $(obj)/,,$(@:.o=-objs)))    \
 $($(subst $(obj)/,,$(@:.o=-y)))), $^)
+
 quiet_cmd_link_multi-y = LD      $@
 cmd_link_multi-y = $(LD) $(ld_flags) -r -o $@ $(link_multi_deps) $(cmd_secanalysis)
 
diff --git a/scripts/Makefile.extrawarn b/scripts/Makefile.extrawarn
new file mode 100644 (file)
index 0000000..6564350
--- /dev/null
@@ -0,0 +1,67 @@
+# ==========================================================================
+#
+# make W=... settings
+#
+# W=1 - warnings that may be relevant and does not occur too often
+# W=2 - warnings that occur quite often but may still be relevant
+# W=3 - the more obscure warnings, can most likely be ignored
+#
+# $(call cc-option, -W...) handles gcc -W.. options which
+# are not supported by all versions of the compiler
+# ==========================================================================
+
+ifeq ("$(origin W)", "command line")
+  export KBUILD_ENABLE_EXTRA_GCC_CHECKS := $(W)
+endif
+
+ifdef KBUILD_ENABLE_EXTRA_GCC_CHECKS
+warning-  := $(empty)
+
+warning-1 := -Wextra -Wunused -Wno-unused-parameter
+warning-1 += -Wmissing-declarations
+warning-1 += -Wmissing-format-attribute
+warning-1 += $(call cc-option, -Wmissing-prototypes)
+warning-1 += -Wold-style-definition
+warning-1 += $(call cc-option, -Wmissing-include-dirs)
+warning-1 += $(call cc-option, -Wunused-but-set-variable)
+warning-1 += $(call cc-disable-warning, missing-field-initializers)
+
+# Clang
+warning-1 += $(call cc-disable-warning, initializer-overrides)
+warning-1 += $(call cc-disable-warning, unused-value)
+warning-1 += $(call cc-disable-warning, format)
+warning-1 += $(call cc-disable-warning, unknown-warning-option)
+warning-1 += $(call cc-disable-warning, sign-compare)
+warning-1 += $(call cc-disable-warning, format-zero-length)
+warning-1 += $(call cc-disable-warning, uninitialized)
+warning-1 += $(call cc-option, -fcatch-undefined-behavior)
+
+warning-2 := -Waggregate-return
+warning-2 += -Wcast-align
+warning-2 += -Wdisabled-optimization
+warning-2 += -Wnested-externs
+warning-2 += -Wshadow
+warning-2 += $(call cc-option, -Wlogical-op)
+warning-2 += $(call cc-option, -Wmissing-field-initializers)
+
+warning-3 := -Wbad-function-cast
+warning-3 += -Wcast-qual
+warning-3 += -Wconversion
+warning-3 += -Wpacked
+warning-3 += -Wpadded
+warning-3 += -Wpointer-arith
+warning-3 += -Wredundant-decls
+warning-3 += -Wswitch-default
+warning-3 += $(call cc-option, -Wpacked-bitfield-compat)
+warning-3 += $(call cc-option, -Wvla)
+
+warning := $(warning-$(findstring 1, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS)))
+warning += $(warning-$(findstring 2, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS)))
+warning += $(warning-$(findstring 3, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS)))
+
+ifeq ("$(strip $(warning))","")
+        $(error W=$(KBUILD_ENABLE_EXTRA_GCC_CHECKS) is unknown)
+endif
+
+KBUILD_CFLAGS += $(warning)
+endif
index 4d908d16c035c4e6c582e254e531998e7f6156ae..d8e335eed22632a495c341c21d995a076ef7cb57 100644 (file)
@@ -18,31 +18,29 @@ include $(srctree)/$(obj)/Makefile
 include scripts/Makefile.host
 
 mod-fw := $(fw-shipped-m)
-# If CONFIG_FIRMWARE_IN_KERNEL isn't set, then install the 
+# If CONFIG_FIRMWARE_IN_KERNEL isn't set, then install the
 # firmware for in-kernel drivers too.
 ifndef CONFIG_FIRMWARE_IN_KERNEL
 mod-fw += $(fw-shipped-y)
 endif
 
+ifneq ($(KBUILD_SRC),)
+# Create output directory if not already present
+_dummy := $(shell [ -d $(obj) ] || mkdir -p $(obj))
+
+firmware-dirs := $(sort $(addprefix $(objtree)/$(obj)/,$(dir $(fw-external-y) $(fw-shipped-all))))
+# Create directories for firmware in subdirectories
+_dummy := $(foreach d,$(firmware-dirs), $(shell [ -d $(d) ] || mkdir -p $(d)))
+endif
+
 installed-mod-fw := $(addprefix $(INSTALL_FW_PATH)/,$(mod-fw))
 
 installed-fw := $(addprefix $(INSTALL_FW_PATH)/,$(fw-shipped-all))
-installed-fw-dirs := $(sort $(dir $(installed-fw))) $(INSTALL_FW_PATH)/./
-
-# Workaround for make < 3.81, where .SECONDEXPANSION doesn't work.
-PHONY += $(INSTALL_FW_PATH)/$$(%) install-all-dirs
-$(INSTALL_FW_PATH)/$$(%): install-all-dirs
-       @true
-install-all-dirs: $(installed-fw-dirs)
-       @true
 
 quiet_cmd_install = INSTALL $(subst $(srctree)/,,$@)
-      cmd_install = $(INSTALL) -m0644 $< $@
-
-$(installed-fw-dirs):
-       $(call cmd,mkdir)
+      cmd_install = mkdir -p $(@D); $(INSTALL) -m0644 $< $@
 
-$(installed-fw): $(INSTALL_FW_PATH)/%: $(obj)/% | $(INSTALL_FW_PATH)/$$(dir %)
+$(installed-fw): $(INSTALL_FW_PATH)/%: $(obj)/%
        $(call cmd,install)
 
 PHONY +=  __fw_install __fw_modinst FORCE
index 1ac414fd50300384663248c0ac651c9887bfe45d..66893643fd7d14a6f59bb52a17b0393180e7051d 100644 (file)
@@ -166,5 +166,4 @@ $(host-cshlib): $(obj)/%: $(host-cshobjs) FORCE
        $(call if_changed,host-cshlib)
 
 targets += $(host-csingle)  $(host-cmulti) $(host-cobjs)\
-          $(host-cxxmulti) $(host-cxxobjs) $(host-cshlib) $(host-cshobjs) 
-
+          $(host-cxxmulti) $(host-cxxobjs) $(host-cshlib) $(host-cshobjs)
index 6a5b0decb797227326acb4f8511256afb003b989..260bf8acfce96cfcbae0eb2c3c6754e96e649317 100644 (file)
@@ -27,7 +27,7 @@ lib-y := $(filter-out $(obj-y), $(sort $(lib-y) $(lib-m)))
 # ---------------------------------------------------------------------------
 # o if we encounter foo/ in $(obj-y), replace it by foo/built-in.o
 #   and add the directory to the list of dirs to descend into: $(subdir-y)
-# o if we encounter foo/ in $(obj-m), remove it from $(obj-m) 
+# o if we encounter foo/ in $(obj-m), remove it from $(obj-m)
 #   and add the directory to the list of dirs to descend into: $(subdir-m)
 
 # Determine modorder.
@@ -46,7 +46,7 @@ obj-m         := $(filter-out %/, $(obj-m))
 
 subdir-ym      := $(sort $(subdir-y) $(subdir-m))
 
-# if $(foo-objs) exists, foo.o is a composite object 
+# if $(foo-objs) exists, foo.o is a composite object
 multi-used-y := $(sort $(foreach m,$(obj-y), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))), $(m))))
 multi-used-m := $(sort $(foreach m,$(obj-m), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))), $(m))))
 multi-used   := $(multi-used-y) $(multi-used-m)
@@ -91,7 +91,7 @@ obj-dirs      := $(addprefix $(obj)/,$(obj-dirs))
 
 # These flags are needed for modversions and compiling, so we define them here
 # already
-# $(modname_flags) #defines KBUILD_MODNAME as the name of the module it will 
+# $(modname_flags) #defines KBUILD_MODNAME as the name of the module it will
 # end up in (or would, if it gets compiled in)
 # Note: Files that end up in two or more modules are compiled without the
 #       KBUILD_MODNAME definition. The reason is that any made-up name would
@@ -212,7 +212,7 @@ $(obj)/%: $(src)/%_shipped
 
 # Commands useful for building a boot image
 # ===========================================================================
-# 
+#
 #      Use as following:
 #
 #      target: source(s) FORCE
@@ -226,7 +226,7 @@ $(obj)/%: $(src)/%_shipped
 
 quiet_cmd_ld = LD      $@
 cmd_ld = $(LD) $(LDFLAGS) $(ldflags-y) $(LDFLAGS_$(@F)) \
-              $(filter-out FORCE,$^) -o $@ 
+              $(filter-out FORCE,$^) -o $@
 
 # Objcopy
 # ---------------------------------------------------------------------------
index 078fe1d64e7df3e38e94dcd595bb834e5bd9da9c..b30406860b7397881e4e6ffeedcda1f5b2959cc0 100644 (file)
@@ -409,10 +409,10 @@ static void print_deps(void)
                exit(2);
        }
        if (fstat(fd, &st) < 0) {
-                fprintf(stderr, "fixdep: error fstat'ing depfile: ");
-                perror(depfile);
-                exit(2);
-        }
+               fprintf(stderr, "fixdep: error fstat'ing depfile: ");
+               perror(depfile);
+               exit(2);
+       }
        if (st.st_size == 0) {
                fprintf(stderr,"fixdep: %s is empty\n",depfile);
                close(fd);
index 544aa56b6200a0610a1b0841ce34ed312955c3f0..c05d586b1feeddd92d9d030d77e0b31b9ea0eb58 100755 (executable)
@@ -173,4 +173,3 @@ while (my $line = <STDIN>) {
 
 # Sort output by size (last field)
 print sort { ($b =~ /:\t*(\d+)$/)[0] <=> ($a =~ /:\t*(\d+)$/)[0] } @stack;
-
diff --git a/scripts/coccinelle/misc/of_table.cocci b/scripts/coccinelle/misc/of_table.cocci
new file mode 100644 (file)
index 0000000..3c93404
--- /dev/null
@@ -0,0 +1,62 @@
+/// Make sure of_device_id tables are NULL terminated
+//
+// Keywords: of_table
+// Confidence: Medium
+// Options: --include-headers
+
+virtual patch
+virtual context
+virtual org
+virtual report
+
+@depends on context@
+identifier var, arr;
+expression E;
+@@
+struct of_device_id arr[] = {
+       ...,
+       {
+       .var = E,
+*      }
+};
+
+@depends on patch@
+identifier var, arr;
+expression E;
+@@
+struct of_device_id arr[] = {
+       ...,
+       {
+       .var = E,
+-      }
++      },
++      { }
+};
+
+@r depends on org || report@
+position p1;
+identifier var, arr;
+expression E;
+@@
+struct of_device_id arr[] = {
+       ...,
+       {
+       .var = E,
+       }
+       @p1
+};
+
+@script:python depends on org@
+p1 << r.p1;
+arr << r.arr;
+@@
+
+cocci.print_main(arr,p1)
+
+@script:python depends on report@
+p1 << r.p1;
+arr << r.arr;
+@@
+
+msg = "%s is not NULL terminated at line %s" % (arr, p1[0].line)
+coccilib.report.print_report(p1[0],msg)
diff --git a/scripts/coccinelle/misc/returnvar.cocci b/scripts/coccinelle/misc/returnvar.cocci
new file mode 100644 (file)
index 0000000..605955a
--- /dev/null
@@ -0,0 +1,66 @@
+///
+/// Removes unneeded variable used to store return value.
+///
+// Confidence: Moderate
+// Copyright: (C) 2012 Peter Senna Tschudin, INRIA/LIP6.  GPLv2.
+// URL: http://coccinelle.lip6.fr/
+// Comments: Comments on code can be deleted if near code that is removed.
+//           "when strict" can be removed to get more hits, but adds false
+//           positives
+// Options: --no-includes --include-headers
+
+virtual patch
+virtual report
+virtual context
+virtual org
+
+@depends on patch@
+type T;
+constant C;
+identifier ret;
+@@
+- T ret = C;
+... when != ret
+    when strict
+return
+- ret
++ C
+;
+
+@depends on context@
+type T;
+constant C;
+identifier ret;
+@@
+* T ret = C;
+... when != ret
+    when strict
+* return ret;
+
+@r1 depends on report || org@
+type T;
+constant C;
+identifier ret;
+position p1, p2;
+@@
+T ret@p1 = C;
+... when != ret
+    when strict
+return ret@p2;
+
+@script:python depends on report@
+p1 << r1.p1;
+p2 << r1.p2;
+C << r1.C;
+ret << r1.ret;
+@@
+coccilib.report.print_report(p1[0], "Unneeded variable: \"" + ret + "\". Return \"" + C + "\" on line " + p2[0].line)
+
+@script:python depends on org@
+p1 << r1.p1;
+p2 << r1.p2;
+C << r1.C;
+ret << r1.ret;
+@@
+cocci.print_main("unneeded \"" + ret + "\" variable", p1)
+cocci.print_sec("return " + C + " here", p2)
index 68041793698c5f22099782129b03c81b8b4bca54..026aeb4f32ee3fa056e57c84c1283e2d04181f15 100755 (executable)
@@ -223,4 +223,3 @@ while [ "$1" != "" ] ; do
                ;;
        esac
 done
-
index 263a44d57fa95dea88411e7df0527eb1528cb474..61bbda54cf13ead802ba096fe8dad55bd5241488 100644 (file)
@@ -104,7 +104,7 @@ int main(int argc, char *argv[])
        }
     }
 
-  /* For now we assume the default font is always 256 characters. */    
+  /* For now we assume the default font is always 256 characters. */
   fontlen = 256;
 
   /* Initialize table */
@@ -236,15 +236,15 @@ int main(int argc, char *argv[])
     }
 
   /* Okay, we hit EOF, now output hash table */
-  
+
   fclose(ctbl);
-  
+
 
   /* Compute total size of Unicode list */
   nuni = 0;
   for ( i = 0 ; i < fontlen ; i++ )
     nuni += unicount[i];
-  
+
   printf("\
 /*\n\
  * Do not edit this file; it was automatically generated by\n\
@@ -268,9 +268,9 @@ u8 dfont_unicount[%d] = \n\
       else
         printf(", ");
     }
-  
+
   printf("\nu16 dfont_unitable[%d] = \n{\n\t", nuni);
-  
+
   fp0 = 0;
   nent = 0;
   for ( i = 0 ; i < nuni ; i++ )
index 2b69eaf5b646e309d95fc9fda3c5a94efce236f6..e267e621431a6b83768ac5efa5d37eba6fb2f55c 100644 (file)
@@ -154,7 +154,7 @@ int symfilecnt = 0;
 static void add_new_symbol(struct symfile *sym, char * symname)
 {
        sym->symbollist =
-          realloc(sym->symbollist, (sym->symbolcnt + 1) * sizeof(char *));
+         realloc(sym->symbollist, (sym->symbolcnt + 1) * sizeof(char *));
        sym->symbollist[sym->symbolcnt++].name = strdup(symname);
 }
 
@@ -215,7 +215,7 @@ static void find_export_symbols(char * filename)
                        char *p;
                        char *e;
                        if (((p = strstr(line, "EXPORT_SYMBOL_GPL")) != NULL) ||
-                            ((p = strstr(line, "EXPORT_SYMBOL")) != NULL)) {
+                           ((p = strstr(line, "EXPORT_SYMBOL")) != NULL)) {
                                /* Skip EXPORT_SYMBOL{_GPL} */
                                while (isalnum(*p) || *p == '_')
                                        p++;
@@ -291,28 +291,28 @@ static void extfunc(char * filename) { docfunctions(filename, FUNCTION);   }
 static void singfunc(char * filename, char * line)
 {
        char *vec[200]; /* Enough for specific functions */
-        int i, idx = 0;
-        int startofsym = 1;
+       int i, idx = 0;
+       int startofsym = 1;
        vec[idx++] = KERNELDOC;
        vec[idx++] = DOCBOOK;
        vec[idx++] = SHOWNOTFOUND;
 
-        /* Split line up in individual parameters preceded by FUNCTION */
-        for (i=0; line[i]; i++) {
-                if (isspace(line[i])) {
-                        line[i] = '\0';
-                        startofsym = 1;
-                        continue;
-                }
-                if (startofsym) {
-                        startofsym = 0;
-                        vec[idx++] = FUNCTION;
-                        vec[idx++] = &line[i];
-                }
-        }
+       /* Split line up in individual parameters preceded by FUNCTION */
+       for (i=0; line[i]; i++) {
+               if (isspace(line[i])) {
+                       line[i] = '\0';
+                       startofsym = 1;
+                       continue;
+               }
+               if (startofsym) {
+                       startofsym = 0;
+                       vec[idx++] = FUNCTION;
+                       vec[idx++] = &line[i];
+               }
+       }
        for (i = 0; i < idx; i++) {
-               if (strcmp(vec[i], FUNCTION))
-                       continue;
+               if (strcmp(vec[i], FUNCTION))
+                       continue;
                consume_symbol(vec[i + 1]);
        }
        vec[idx++] = filename;
@@ -460,14 +460,14 @@ static void parse_file(FILE *infile)
                                        break;
                                case 'D':
                                        while (*s && !isspace(*s)) s++;
-                                        *s = '\0';
-                                        symbolsonly(line+2);
-                                        break;
+                                       *s = '\0';
+                                       symbolsonly(line+2);
+                                       break;
                                case 'F':
                                        /* filename */
                                        while (*s && !isspace(*s)) s++;
                                        *s++ = '\0';
-                                        /* function names */
+                                       /* function names */
                                        while (isspace(*s))
                                                s++;
                                        singlefunctions(line +2, s);
@@ -515,11 +515,11 @@ int main(int argc, char *argv[])
        }
        /* Open file, exit on error */
        infile = fopen(argv[2], "r");
-        if (infile == NULL) {
-                fprintf(stderr, "docproc: ");
-                perror(argv[2]);
-                exit(2);
-        }
+       if (infile == NULL) {
+               fprintf(stderr, "docproc: ");
+               perror(argv[2]);
+               exit(2);
+       }
 
        if (strcmp("doc", argv[1]) == 0) {
                /* Need to do this in two passes.
index 095acb49a374bb228efc6c10665bde0640b7bca7..cdabdc95a6e741b506aedccb5cc2d014c8ad348b 100644 (file)
@@ -2,4 +2,3 @@ dtc
 dtc-lexer.lex.c
 dtc-parser.tab.c
 dtc-parser.tab.h
-
index f3774530170ab6ac7465ae31cb5777bbe48614d0..e464727c8808cbcd06e184b004fb7261c4472288 100644 (file)
@@ -88,4 +88,3 @@ struct boot_info *dt_from_fs(const char *dirname)
 
        return build_boot_info(NULL, tree, guess_boot_cpuid(tree));
 }
-
index f72d13b1d19c0bce7a27658e18a5f9fe26b2453c..f2ae9b77c285733e50b4b496407471149650d290 100644 (file)
@@ -81,4 +81,3 @@ int fdt_create_empty_tree(void *buf, int bufsize)
 
        return fdt_open_into(buf, buf, bufsize);
 }
-
index 33eeba55fb4dd6482193902e784dbb1a62368f3e..5740e6992d37c157d5395572341a8d53cd5d10d8 100644 (file)
@@ -281,4 +281,3 @@ void dt_to_source(FILE *f, struct boot_info *bi)
 
        write_tree_source_node(f, bi->dt, 0);
 }
-
index 978b42b3acd7e59519bdc74540a180da25e8c607..95ece06599a58b78f5f4e3955c33acecda34af10 100755 (executable)
@@ -28,5 +28,3 @@ for arch in ${archs}; do
                ;;
        esac
 done
-
-
index 1237dd7fb4cac7c2f863b413399df17a9e0c8beb..dc7aa45e80ce5e1acd4031e9774aabff7217b492 100644 (file)
@@ -123,7 +123,7 @@ static int read_symbol(FILE *in, struct sym_entry *s)
        }
        if (strlen(str) > KSYM_NAME_LEN) {
                fprintf(stderr, "Symbol %s too long for kallsyms (%zu vs %d).\n"
-                                "Please increase KSYM_NAME_LEN both in kernel and kallsyms.c\n",
+                               "Please increase KSYM_NAME_LEN both in kernel and kallsyms.c\n",
                        str, strlen(str), KSYM_NAME_LEN);
                return -1;
        }
index 844bc9da08dacf866e9f769318faa15f90e5c136..9c4d2412fb724e8b8d93cd02b70488ba3f779cb9 100644 (file)
@@ -33,11 +33,11 @@ oldconfig: $(obj)/conf
        $< --$@ $(Kconfig)
 
 silentoldconfig: $(obj)/conf
-       $(Q)mkdir -p include/generated
+       $(Q)mkdir -p include/config include/generated
        $< --$@ $(Kconfig)
 
 localyesconfig localmodconfig: $(obj)/streamline_config.pl $(obj)/conf
-       $(Q)mkdir -p include/generated
+       $(Q)mkdir -p include/config include/generated
        $(Q)perl $< --$@ $(srctree) $(Kconfig) > .tmp.config
        $(Q)if [ -f .config ]; then                                     \
                        cmp -s .tmp.config .config ||                   \
@@ -319,4 +319,3 @@ $(obj)/%.moc: $(src)/%.h $(obj)/.tmp_qtcheck
 $(obj)/gconf.glade.h: $(obj)/gconf.glade
        $(Q)intltool-extract --type=gettext/glade --srcdir=$(srctree) \
        $(obj)/gconf.glade
-
index 854d9c7c675ca4782702e83e1fa04b17aa47d6f8..55b79ba1ba2a5e4c84cb6cc3bf5f25dbb92a3e89 100755 (executable)
@@ -11,4 +11,3 @@ EOF
 if [ ! "$?" -eq "0"  ]; then
        echo -DKBUILD_NO_NLS;
 fi
-
index d19944f9c3acb9e6004ca13a1f6e4444174a5359..fef75fc756f40bfa5ae89c942e49f80719204bb9 100644 (file)
@@ -696,7 +696,7 @@ int main(int ac, char **av)
        } else if (input_mode == savedefconfig) {
                if (conf_write_defconfig(defconfig_file)) {
                        fprintf(stderr, _("n*** Error while saving defconfig to: %s\n\n"),
-                               defconfig_file);
+                               defconfig_file);
                        return 1;
                }
        } else if (input_mode != listnewconfig) {
index f2bee70e26f4940061c97add6dafbf273b272dcd..d0a35b21f3087283c286885c26caa58630925231 100644 (file)
@@ -1404,7 +1404,7 @@ static void display_tree(struct menu *menu)
                    && (tree == tree2))
                        continue;
 /*
-                if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT))
+               if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT))
                    || (view_mode == FULL_VIEW)
                    || (view_mode == SPLIT_VIEW))*/
 
index 3b15c08ec1fac369f9b6e0b01c738e46fa3efb2d..8d016faa28d704be886cb1c6195a3bdca78d29b0 100644 (file)
@@ -168,13 +168,13 @@ do_resize:
 
        /* create new window for the list */
        list = subwin(dialog, list_height, list_width, y + box_y + 1,
-                     x + box_x + 1);
+                     x + box_x + 1);
 
        keypad(list, TRUE);
 
        /* draw a box around the list items */
        draw_box(dialog, box_y, box_x, list_height + 2, list_width + 2,
-                dlg.menubox_border.atr, dlg.menubox.atr);
+                dlg.menubox_border.atr, dlg.menubox.atr);
 
        /* Find length of longest item in order to center checklist */
        check_x = 0;
index 447a582198c9bc70c37c4d2d034b128b5a62080d..d58de1dc5360dacf90fce2c177e3fb6c3ed8c83e 100644 (file)
@@ -42,7 +42,7 @@ static void print_buttons(WINDOW * dialog, int height, int width, int selected)
  * Display a dialog box for inputing a string
  */
 int dialog_inputbox(const char *title, const char *prompt, int height, int width,
-                    const char *init)
+                   const char *init)
 {
        int i, x, y, box_y, box_x, box_width;
        int input_x = 0, key = 0, button = -1;
index c93de0b2faca28525588bceb023f99bdd8a14acf..11ae9ad7ac7b992e2244ae88c0f576977157c254 100644 (file)
@@ -64,7 +64,7 @@ static int menu_width, item_x;
  * Print menu item
  */
 static void do_print_item(WINDOW * win, const char *item, int line_y,
-                          int selected, int hotkey)
+                         int selected, int hotkey)
 {
        int j;
        char *menu_item = malloc(menu_width + 1);
@@ -182,7 +182,7 @@ static void do_scroll(WINDOW *win, int *scroll, int n)
  * Display a menu for choosing among a number of options
  */
 int dialog_menu(const char *title, const char *prompt,
-                const void *selected, int *s_scroll)
+               const void *selected, int *s_scroll)
 {
        int i, j, x, y, box_x, box_y;
        int height, width, menu_height;
index 58a8289dd650281c29f8e34827c153e9b4d4ac74..f7abdeb92af02188a48712ce48ab76f6b32f1d27 100644 (file)
@@ -623,7 +623,7 @@ void item_make(const char *fmt, ...)
 void item_add_str(const char *fmt, ...)
 {
        va_list ap;
-        size_t avail;
+       size_t avail;
 
        avail = sizeof(item_cur->node.str) - strlen(item_cur->node.str);
 
index 59184bb41ef81054384bcd641097f95e9e5426a2..14cea7463a621b33c6c0b8487781db107d764841 100644 (file)
@@ -299,7 +299,7 @@ static void set_config_filename(const char *config_filename)
        int size;
 
        size = snprintf(menu_backtitle, sizeof(menu_backtitle),
-                       "%s - %s", config_filename, rootmenu.prompt->text);
+                       "%s - %s", config_filename, rootmenu.prompt->text);
        if (size >= sizeof(menu_backtitle))
                menu_backtitle[sizeof(menu_backtitle)-1] = '\0';
        set_dialog_backtitle(menu_backtitle);
@@ -1034,4 +1034,3 @@ int main(int ac, char **av)
 
        return res;
 }
-
index 3ac2c9c6e280300275920d2ea1cadb0c7be66746..a26cc5d2a9b0217d9c3d52bf0dab21b7337e95d2 100644 (file)
@@ -258,8 +258,8 @@ static void sym_check_prop(struct symbol *sym)
                                    "config symbol '%s' uses select, but is "
                                    "not boolean or tristate", sym->name);
                        else if (sym2->type != S_UNKNOWN &&
-                                sym2->type != S_BOOLEAN &&
-                                sym2->type != S_TRISTATE)
+                                sym2->type != S_BOOLEAN &&
+                                sym2->type != S_TRISTATE)
                                prop_warn(prop,
                                    "'%s' has wrong type. 'select' only "
                                    "accept arguments of boolean and "
@@ -268,7 +268,7 @@ static void sym_check_prop(struct symbol *sym)
                case P_RANGE:
                        if (sym->type != S_INT && sym->type != S_HEX)
                                prop_warn(prop, "range is only allowed "
-                                               "for int or hex symbols");
+                                               "for int or hex symbols");
                        if (!menu_validate_number(sym, prop->expr->left.sym) ||
                            !menu_validate_number(sym, prop->expr->right.sym))
                                prop_warn(prop, "range is invalid");
index 4fbecd2473bc51904629056eaa8bacb7b42fb660..984489ef2b46e12549eedd74a2d8691ba0589489 100644 (file)
@@ -1554,4 +1554,3 @@ int main(int ac, char **av)
        endwin();
        return 0;
 }
-
index 31331723e810746d08f46c01928c20b2555018a8..9cb8522d8d22a826caaec968e82c77516339ca01 100644 (file)
@@ -589,7 +589,7 @@ while ($repeat) {
 
     # Now we need to see if we have to check selects;
     loop_select;
-}          
+}
 
 my %setconfigs;
 
index 6e7fbf1968090463f37ec1af70d65fb0af28a391..94f9c83e324f4a72b79b6fbf8ca6b3999d046702 100644 (file)
@@ -155,5 +155,3 @@ void *xcalloc(size_t nmemb, size_t size)
        fprintf(stderr, "Out of memory.\n");
        exit(1);
 }
-
-
index 1a9f53e535ca8ba87adc9b1af2bc761f128e56a0..6c62d93b4ffbd018807993788fb78848d176d67d 100644 (file)
@@ -27,8 +27,8 @@ static char *text;
 static int text_size, text_asize;
 
 struct buffer {
-        struct buffer *parent;
-        YY_BUFFER_STATE state;
+       struct buffer *parent;
+       YY_BUFFER_STATE state;
 };
 
 struct buffer *current_buf;
index a0521aa5974b514e7bfcb1811746c283702862f2..349a7f24315b1d1c4887962626365fd749fdae02 100644 (file)
@@ -789,8 +789,8 @@ static char *text;
 static int text_size, text_asize;
 
 struct buffer {
-        struct buffer *parent;
-        YY_BUFFER_STATE state;
+       struct buffer *parent;
+       YY_BUFFER_STATE state;
 };
 
 struct buffer *current_buf;
index 25ae16ac75c85853e24733eb754bc514ba6eb419..de5e84ed3f96f824b63a1e5bd2e4da597996747f 100644 (file)
@@ -2314,7 +2314,7 @@ void conf_parse(const char *name)
        for_all_symbols(i, sym) {
                if (sym_check_deps(sym))
                        zconfnerrs++;
-        }
+       }
        if (zconfnerrs)
                exit(1);
        sym_set_change_count(1);
index 0653886fac4810b0989af918708f4dbfd39f145b..0f683cfa53e9abf9aecc032e03334fca182c0d20 100644 (file)
@@ -510,7 +510,7 @@ void conf_parse(const char *name)
        for_all_symbols(i, sym) {
                if (sym_check_deps(sym))
                        zconfnerrs++;
-        }
+       }
        if (zconfnerrs)
                exit(1);
        sym_set_change_count(1);
index 827896f56501aaebba885e70adc24a477fd31884..c21d16328d3f2bae0ccaba40029a7e1f83229b6a 100644 (file)
@@ -367,4 +367,3 @@ OPTION:
 EOT
        exit;
 }
-
index cfb8440cc0b2cf5611db59014d4b83fd27af2e9d..6fdc97ef6023da95f708dcbe9b1cc0634a99f0f2 100755 (executable)
@@ -68,7 +68,7 @@ UTS_TRUNCATE="cut -b -$UTS_LEN"
 
 ( echo /\* This file is auto generated, version $VERSION \*/
   if [ -n "$CONFIG_FLAGS" ] ; then echo "/* $CONFIG_FLAGS */"; fi
-  
+
   echo \#define UTS_MACHINE \"$ARCH\"
 
   echo \#define UTS_VERSION \"`echo $UTS_VERSION | $UTS_TRUNCATE`\"
@@ -84,7 +84,7 @@ UTS_TRUNCATE="cut -b -$UTS_LEN"
 # recompilations.
 # We don't consider the file changed if only the date/time changed.
 # A kernel config change will increase the generation number, thus
-# causing compile.h to be updated (including date/time) due to the 
+# causing compile.h to be updated (including date/time) due to the
 # changed comment in the
 # first line.
 
index 0cc04426074403458583b73339754c2a588bfa13..84af27bf0f99fc8983564eebaf63aed7797e7a30 100644 (file)
@@ -42,18 +42,11 @@ MAKEARGS += O=\$(if \$(patsubst /%,,\$(makedir)),\$(CURDIR)/)\$(patsubst %/,%,\$
 
 MAKEFLAGS += --no-print-directory
 
-.PHONY: all \$(MAKECMDGOALS)
+.PHONY: __sub-make \$(MAKECMDGOALS)
 
-all    := \$(filter-out all Makefile,\$(MAKECMDGOALS))
+__sub-make:
+       \$(Q)\$(MAKE) \$(MAKEARGS) \$(MAKECMDGOALS)
 
-all:
-       \$(Q)\$(MAKE) \$(MAKEARGS) \$(all)
-
-Makefile:;
-
-\$(all): all
-       @:
-
-%/: all
+\$(filter-out __sub-make, \$(MAKECMDGOALS)): __sub-make
        @:
 EOF
index c1b6191ef8792a48da0805f711a49b56aaefd3c9..7ada35a0f47802e64ea360a04d0b5b0afd7aae6f 100644 (file)
@@ -42,4 +42,3 @@
 # (At least sparc64 has __crc_ in the middle).
 
 $NM -n $1 | grep -v '\( [aNUw] \)\|\(__crc_\)\|\( \$[adt]\)' > $2
-
index 33bae0df4de5bb5750e75247c641e12c8098d736..3bd11b6031734a0bf0e12b9427387a74149cdfbf 100644 (file)
@@ -2,4 +2,3 @@ elfconfig.h
 mk_elfconfig
 modpost
 devicetable-offsets.h
-
index 1924990a737f36c61820bd35470c9adf90f49888..e614ef689eee1f16dc19daf1c00be4460a184f63 100644 (file)
@@ -644,28 +644,26 @@ ADD_TO_DEVTABLE("pcmcia", pcmcia_device_id, do_pcmcia_entry);
 
 static int do_of_entry (const char *filename, void *symval, char *alias)
 {
-    int len;
-    char *tmp;
-    DEF_FIELD_ADDR(symval, of_device_id, name);
-    DEF_FIELD_ADDR(symval, of_device_id, type);
-    DEF_FIELD_ADDR(symval, of_device_id, compatible);
-
-    len = sprintf (alias, "of:N%sT%s",
-                    (*name)[0] ? *name : "*",
-                    (*type)[0] ? *type : "*");
-
-    if (compatible[0])
-        sprintf (&alias[len], "%sC%s",
-                     (*type)[0] ? "*" : "",
-                     *compatible);
-
-    /* Replace all whitespace with underscores */
-    for (tmp = alias; tmp && *tmp; tmp++)
-        if (isspace (*tmp))
-            *tmp = '_';
-
-    add_wildcard(alias);
-    return 1;
+       int len;
+       char *tmp;
+       DEF_FIELD_ADDR(symval, of_device_id, name);
+       DEF_FIELD_ADDR(symval, of_device_id, type);
+       DEF_FIELD_ADDR(symval, of_device_id, compatible);
+
+       len = sprintf(alias, "of:N%sT%s", (*name)[0] ? *name : "*",
+                     (*type)[0] ? *type : "*");
+
+       if (compatible[0])
+               sprintf(&alias[len], "%sC%s", (*type)[0] ? "*" : "",
+                       *compatible);
+
+       /* Replace all whitespace with underscores */
+       for (tmp = alias; tmp && *tmp; tmp++)
+               if (isspace (*tmp))
+                       *tmp = '_';
+
+       add_wildcard(alias);
+       return 1;
 }
 ADD_TO_DEVTABLE("of", of_device_id, do_of_entry);
 
index 639bca7ba559198f4b3013e72556e0e24ba557c6..a4fd71d71d65951756f476dceef3fefa7c1d465c 100644 (file)
@@ -54,4 +54,3 @@ main(int argc, char **argv)
 
        return 0;
 }
-
index ea7f9530afa2a3256bbc51e8bc2c4c43039300ca..9d9c5b905b359e58b9eb98eac457ef81d7f8179b 100644 (file)
@@ -862,7 +862,7 @@ static const char *section_white_list[] =
  * without "ax" / "aw".
  */
 static void check_section(const char *modname, struct elf_info *elf,
-                          Elf_Shdr *sechdr)
+                         Elf_Shdr *sechdr)
 {
        const char *sec = sech_name(elf, sechdr);
 
@@ -1296,12 +1296,12 @@ static void print_section_list(const char * const list[20])
  */
 static void report_sec_mismatch(const char *modname,
                                const struct sectioncheck *mismatch,
-                                const char *fromsec,
-                                unsigned long long fromaddr,
-                                const char *fromsym,
-                                int from_is_func,
-                                const char *tosec, const char *tosym,
-                                int to_is_func)
+                               const char *fromsec,
+                               unsigned long long fromaddr,
+                               const char *fromsym,
+                               int from_is_func,
+                               const char *tosec, const char *tosym,
+                               int to_is_func)
 {
        const char *from, *from_p;
        const char *to, *to_p;
@@ -1441,7 +1441,7 @@ static void report_sec_mismatch(const char *modname,
 }
 
 static void check_section_mismatch(const char *modname, struct elf_info *elf,
-                                   Elf_Rela *r, Elf_Sym *sym, const char *fromsec)
+                                  Elf_Rela *r, Elf_Sym *sym, const char *fromsec)
 {
        const char *tosec;
        const struct sectioncheck *mismatch;
@@ -1528,7 +1528,7 @@ static int addend_arm_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r)
        case R_ARM_ABS32:
                /* From ARM ABI: (S + A) | T */
                r->r_addend = (int)(long)
-                             (elf->symtab_start + ELF_R_SYM(r->r_info));
+                             (elf->symtab_start + ELF_R_SYM(r->r_info));
                break;
        case R_ARM_PC24:
        case R_ARM_CALL:
@@ -1538,8 +1538,8 @@ static int addend_arm_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r)
        case R_ARM_THM_JUMP19:
                /* From ARM ABI: ((S + A) | T) - P */
                r->r_addend = (int)(long)(elf->hdr +
-                             sechdr->sh_offset +
-                             (r->r_offset - sechdr->sh_addr));
+                             sechdr->sh_offset +
+                             (r->r_offset - sechdr->sh_addr));
                break;
        default:
                return 1;
@@ -1571,7 +1571,7 @@ static int addend_mips_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r)
 }
 
 static void section_rela(const char *modname, struct elf_info *elf,
-                         Elf_Shdr *sechdr)
+                        Elf_Shdr *sechdr)
 {
        Elf_Sym  *sym;
        Elf_Rela *rela;
@@ -1615,7 +1615,7 @@ static void section_rela(const char *modname, struct elf_info *elf,
 }
 
 static void section_rel(const char *modname, struct elf_info *elf,
-                        Elf_Shdr *sechdr)
+                       Elf_Shdr *sechdr)
 {
        Elf_Sym *sym;
        Elf_Rel *rel;
@@ -1685,7 +1685,7 @@ static void section_rel(const char *modname, struct elf_info *elf,
  * be discarded and warns about it.
  **/
 static void check_sec_ref(struct module *mod, const char *modname,
-                          struct elf_info *elf)
+                         struct elf_info *elf)
 {
        int i;
        Elf_Shdr *sechdrs = elf->sechdrs;
@@ -1945,7 +1945,7 @@ static int add_versions(struct buffer *b, struct module *mod)
                                             s->name, mod->name);
                                } else {
                                        merror("\"%s\" [%s.ko] undefined!\n",
-                                                 s->name, mod->name);
+                                              s->name, mod->name);
                                        err = 1;
                                }
                        }
@@ -2113,8 +2113,10 @@ static void read_dump(const char *fname, unsigned int kernel)
                s->preloaded = 1;
                sym_update_crc(symname, mod, crc, export_no(export));
        }
+       release_file(file, size);
        return;
 fail:
+       release_file(file, size);
        fatal("parse error in symbol dump file\n");
 }
 
index deb2994b04c4952d48300e8c4c38a43cf462828b..944418da9fe3369efd596e44fd53ffaffc0f80bd 100644 (file)
@@ -214,7 +214,7 @@ static void md4_final_ascii(struct md4_ctx *mctx, char *out, unsigned int len)
        mctx->block[14] = mctx->byte_count << 3;
        mctx->block[15] = mctx->byte_count >> 29;
        le32_to_cpu_array(mctx->block, (sizeof(mctx->block) -
-                         sizeof(uint64_t)) / sizeof(uint32_t));
+                         sizeof(uint64_t)) / sizeof(uint32_t));
        md4_transform(mctx->hash, mctx->block);
        cpu_to_le32_array(mctx->hash, sizeof(mctx->hash) / sizeof(uint32_t));
 
@@ -367,7 +367,7 @@ static int parse_source_files(const char *objfile, struct md4_ctx *md)
                        break;
                /* Terminate line at first space, to get rid of final ' \' */
                while (*p) {
-                       if (isspace(*p)) {
+                       if (isspace(*p)) {
                                *p = '\0';
                                break;
                        }
index b3e4f10bfc3edac5a5119d31f432371f8e75feee..62e51dae2138db450555f3d15dbd14cc53c53652 100755 (executable)
 #
 # Note: 'make mrproper' will also remove .tmp_objdiff
 
-GIT_DIR="`git rev-parse --git-dir`"
+SRCTREE=$(cd $(git rev-parse --show-toplevel 2>/dev/null); pwd)
 
-if [ -d "$GIT_DIR" ]; then
-       TMPD="${GIT_DIR%git}tmp_objdiff"
-
-       [ -d "$TMPD" ] || mkdir "$TMPD"
-else
-       echo "ERROR: git directory not found."
+if [ -z "$SRCTREE" ]; then
+       echo >&2 "ERROR: Not a git repository."
        exit 1
 fi
 
+TMPD=$SRCTREE/.tmp_objdiff
+
 usage() {
-       echo "Usage: $0 <command> <args>"
-       echo "  record    <list of object files>"
-       echo "  diff      <commitA> <commitB>"
-       echo "  clean     all | <commit>"
+       echo >&2 "Usage: $0 <command> <args>"
+       echo >&2 "  record    <list of object files or directories>"
+       echo >&2 "  diff      <commitA> <commitB>"
+       echo >&2 "  clean     all | <commit>"
        exit 1
 }
 
+get_output_dir() {
+       dir=${1%/*}
+
+       if [ "$dir" = "$1" ]; then
+               dir=.
+       fi
+
+       dir=$(cd $dir; pwd)
+
+       echo $TMPD/$CMT${dir#$SRCTREE}
+}
+
+do_objdump() {
+       dir=$(get_output_dir $1)
+       base=${1##*/}
+       dis=$dir/${base%.o}.dis
+
+       [ ! -d "$dir" ] && mkdir -p $dir
+
+       # remove addresses for a cleaner diff
+       # http://dummdida.tumblr.com/post/60924060451/binary-diff-between-libc-from-scientificlinux-and
+       $OBJDUMP -D $1 | sed "s/^[[:space:]]\+[0-9a-f]\+//" > $dis
+}
+
 dorecord() {
        [ $# -eq 0 ] && usage
 
@@ -52,20 +74,16 @@ dorecord() {
        CMT="`git rev-parse --short HEAD`"
 
        OBJDUMP="${CROSS_COMPILE}objdump"
-       OBJDIFFD="$TMPD/$CMT"
-
-       [ ! -d "$OBJDIFFD" ] && mkdir -p "$OBJDIFFD"
 
-       for f in $FILES; do
-               dn="${f%/*}"
-               bn="${f##*/}"
-
-               [ ! -d "$OBJDIFFD/$dn" ] && mkdir -p "$OBJDIFFD/$dn"
-
-               # remove addresses for a more clear diff
-               # http://dummdida.tumblr.com/post/60924060451/binary-diff-between-libc-from-scientificlinux-and
-               $OBJDUMP -D "$f" | sed "s/^[[:space:]]\+[0-9a-f]\+//" \
-                       >"$OBJDIFFD/$dn/$bn"
+       for d in $FILES; do
+               if [ -d "$d" ]; then
+                       for f in $(find $d -name '*.o')
+                       do
+                               do_objdump $f
+                       done
+               else
+                       do_objdump $d
+               fi
        done
 }
 
@@ -90,12 +108,12 @@ dodiff() {
        DSTD="$TMPD/$DST"
 
        if [ ! -d "$SRCD" ]; then
-               echo "ERROR: $SRCD doesn't exist"
+               echo >&2 "ERROR: $SRCD doesn't exist"
                exit 1
        fi
 
        if [ ! -d "$DSTD" ]; then
-               echo "ERROR: $DSTD doesn't exist"
+               echo >&2 "ERROR: $DSTD doesn't exist"
                exit 1
        fi
 
@@ -114,7 +132,7 @@ doclean() {
                if [ -d "$TMPD/$CMT" ]; then
                        rm -rf $TMPD/$CMT
                else
-                       echo "$CMT not found"
+                       echo >&2 "$CMT not found"
                fi
        fi
 }
@@ -135,7 +153,7 @@ case "$1" in
                doclean $*
                ;;
        *)
-               echo "Unrecognized command '$1'"
+               echo >&2 "Unrecognized command '$1'"
                exit 1
                ;;
 esac
index c5d473393816f971ec43135bd7dd3a27cc660beb..99ca6e76eb0a532ffafd0918dca8fa217c88f571 100644 (file)
@@ -143,4 +143,3 @@ help: FORCE
        @echo '  perf-targz-src-pkg  - Build $(perf-tar).tar.gz source tarball'
        @echo '  perf-tarbz2-src-pkg - Build $(perf-tar).tar.bz2 source tarball'
        @echo '  perf-tarxz-src-pkg  - Build $(perf-tar).tar.xz source tarball'
-
index f46e4dd0558da4582b38c75d4342456cceefd920..b5f08f7278686c4b66ba9a581b8e70f13f0fed8d 100644 (file)
@@ -35,13 +35,15 @@ create_package() {
        sparc*)
                debarch=sparc ;;
        s390*)
-               debarch=s390 ;;
+               debarch=s390$(grep -q CONFIG_64BIT=y $KCONFIG_CONFIG && echo x || true) ;;
        ppc*)
                debarch=powerpc ;;
        parisc*)
                debarch=hppa ;;
        mips*)
                debarch=mips$(grep -q CPU_LITTLE_ENDIAN=y $KCONFIG_CONFIG && echo el || true) ;;
+       arm64)
+               debarch=arm64 ;;
        arm*)
                debarch=arm$(grep -q CONFIG_AEABI=y $KCONFIG_CONFIG && echo el || true) ;;
        *)
@@ -130,7 +132,7 @@ if [ "$ARCH" = "um" ] ; then
        cp System.map "$tmpdir/usr/lib/uml/modules/$version/System.map"
        cp $KCONFIG_CONFIG "$tmpdir/usr/share/doc/$packagename/config"
        gzip "$tmpdir/usr/share/doc/$packagename/config"
-else 
+else
        cp System.map "$tmpdir/boot/System.map-$version"
        cp $KCONFIG_CONFIG "$tmpdir/boot/config-$version"
 fi
@@ -155,11 +157,11 @@ if grep -q '^CONFIG_MODULES=y' $KCONFIG_CONFIG ; then
                        for module in $(find lib/modules/ -name *.ko); do
                                mkdir -p $(dirname $dbg_dir/usr/lib/debug/$module)
                                # only keep debug symbols in the debug file
-                               objcopy --only-keep-debug $module $dbg_dir/usr/lib/debug/$module
+                               $OBJCOPY --only-keep-debug $module $dbg_dir/usr/lib/debug/$module
                                # strip original module from debug symbols
-                               objcopy --strip-debug $module
+                               $OBJCOPY --strip-debug $module
                                # then add a link to those
-                               objcopy --add-gnu-debuglink=$dbg_dir/usr/lib/debug/$module $module
+                               $OBJCOPY --add-gnu-debuglink=$dbg_dir/usr/lib/debug/$module $module
                        done
                )
        fi
index aa22f9447ddc2492bfee4241a1380bc4fb91a735..995c1eafaff6156862ad340aa11ff79b3dc96f46 100644 (file)
@@ -136,4 +136,3 @@ esac
 echo "Tarball successfully created in ${tarball}${file_ext}"
 
 exit 0
-
index d000ea3a41fdb7f202a34e439cf954bd5353b74f..49b4241e814a90c159cc272e655daa6294c7a2ea 100755 (executable)
@@ -27,7 +27,7 @@
 #       Nick Holloway <Nick.Holloway@alfie.demon.co.uk>, 2nd January 1995.
 #
 # Added support for handling multiple types of compression. What includes
-# gzip, bzip, bzip2, zip, compress, and plaintext. 
+# gzip, bzip, bzip2, zip, compress, and plaintext.
 #
 #       Adam Sulmicki <adam@cfar.umd.edu>, 1st January 1997.
 #
@@ -159,7 +159,7 @@ applyPatch () {
   fi
   # Remove backup files
   find $sourcedir/ '(' -name '*.orig' -o -name '.*.orig' ')' -exec rm -f {} \;
+
   return 0;
 }
 
index 68bb4efc5af429a2f5d1ec359a06d3f2e7af8bb4..4718d7895f0b451e4a529a4e1fcce6a85ec5a4c5 100644 (file)
@@ -512,4 +512,3 @@ int main(int argc, char *argv[])
     }
     exit(0);
 }
-
index e11aa4a156d2d26df48706d0da0a7970813e9798..650ecc83d7d72f89b012ec2f1ee41ef895aa398f 100644 (file)
@@ -487,5 +487,3 @@ main(int argc, char *argv[])
        }
        return !!n_error;
 }
-
-
index 43098afe74311669fd7cea7ca342415ba47d5bae..6b5c83baf1488729cf386ff56211e55b20e589fa 100644 (file)
@@ -19,4 +19,3 @@ testit t3-l2-pi.tst
 testit t4-l2-pi-deboost.tst
 testit t5-l4-pi-boost-deboost.tst
 testit t5-l4-pi-boost-deboost-setsched.tst
-
index 34186cac1d2ffaff30bca6e0065be69f9e50c7f5..6d916c2a45a550e925efd0bd0f99cb7cdca742ad 100644 (file)
@@ -216,5 +216,3 @@ while 1:
 # Normal exit pass
 print "Pass"
 sys.exit(0)
-
-
index 7b9ccf61f8f96fd9d6f6fd3e20f25f61738717a2..f6a0ce71015faa4109597ff002e2b6d31e028a77 100644 (file)
@@ -66,4 +66,3 @@ if [ "eq$dodev" != "eq" ]; then
        $SF file_contexts /dev
        mount --move /mnt /dev
 fi
-
index e25732b5d701127d904da3b608b825a520fce3ca..5b365009e6a398bed2aa9c13b575bf651aff21eb 100755 (executable)
@@ -126,4 +126,3 @@ def main():
                print (convert_line(line, base_time),)
 
 main()
-
index f2c5b006a3d73534d12fcb8c213ae140db4bae4e..e6b011fe1d0d1dfd850f345f55f0a7f057d6435d 100755 (executable)
@@ -25,6 +25,9 @@ else
        tree=${srctree}/
 fi
 
+# ignore userspace tools
+ignore="$ignore ( -path ${tree}tools ) -prune -o"
+
 # Find all available archs
 find_all_archs()
 {
@@ -47,7 +50,8 @@ find_arch_sources()
        for i in $archincludedir; do
                prune="$prune -wholename $i -prune -o"
        done
-       find ${tree}arch/$1 $ignore $subarchprune $prune -name "$2" -print;
+       find ${tree}arch/$1 $ignore $subarchprune $prune -name "$2" \
+               -not -type l -print;
 }
 
 # find sources in arch/$1/include
@@ -57,14 +61,15 @@ find_arch_include_sources()
                                        -name include -type d -print);
        if [ -n "$include" ]; then
                archincludedir="$archincludedir $include"
-               find $include $ignore -name "$2" -print;
+               find $include $ignore -name "$2" -not -type l -print;
        fi
 }
 
 # find sources in include/
 find_include_sources()
 {
-       find ${tree}include $ignore -name config -prune -o -name "$1" -print;
+       find ${tree}include $ignore -name config -prune -o -name "$1" \
+               -not -type l -print;
 }
 
 # find sources in rest of tree
@@ -73,7 +78,7 @@ find_other_sources()
 {
        find ${tree}* $ignore \
             \( -name include -o -name arch -o -name '.tmp_*' \) -prune -o \
-              -name "$1" -print;
+              -name "$1" -not -type l -print;
 }
 
 find_sources()
@@ -187,6 +192,10 @@ exuberant()
        --regex-c++='/TESTCLEARFLAG_FALSE\(([^,)]*).*/TestClearPage\1/' \
        --regex-c++='/__TESTCLEARFLAG_FALSE\(([^,)]*).*/__TestClearPage\1/' \
        --regex-c++='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/'                \
+       --regex-c++='/TESTPCGFLAG\(([^,)]*).*/PageCgroup\1/'            \
+       --regex-c++='/SETPCGFLAG\(([^,)]*).*/SetPageCgroup\1/'          \
+       --regex-c++='/CLEARPCGFLAG\(([^,)]*).*/ClearPageCgroup\1/'      \
+       --regex-c++='/TESTCLEARPCGFLAG\(([^,)]*).*/TestClearPageCgroup\1/' \
        --regex-c='/PCI_OP_READ\((\w*).*[1-4]\)/pci_bus_read_config_\1/' \
        --regex-c='/PCI_OP_WRITE\((\w*).*[1-4]\)/pci_bus_write_config_\1/' \
        --regex-c='/DEFINE_(MUTEX|SEMAPHORE|SPINLOCK)\((\w*)/\2/v/'     \
@@ -201,7 +210,8 @@ exuberant()
        --regex-c='/DECLARE_(TASKLET|WORK|DELAYED_WORK)\((\w*)/\2/v/'   \
        --regex-c='/DEFINE_PCI_DEVICE_TABLE\((\w*)/\1/v/'               \
        --regex-c='/(^\s)OFFSET\((\w*)/\2/v/'                           \
-       --regex-c='/(^\s)DEFINE\((\w*)/\2/v/'
+       --regex-c='/(^\s)DEFINE\((\w*)/\2/v/'                           \
+       --regex-c='/DEFINE_HASHTABLE\((\w*)/\1/v/'
 
        all_kconfigs | xargs $1 -a                              \
        --langdef=kconfig --language-force=kconfig              \
@@ -244,9 +254,14 @@ emacs()
        --regex='/__CLEARPAGEFLAG_NOOP(\([^,)]*\).*/__ClearPage\1/' \
        --regex='/TESTCLEARFLAG_FALSE(\([^,)]*\).*/TestClearPage\1/' \
        --regex='/__TESTCLEARFLAG_FALSE(\([^,)]*\).*/__TestClearPage\1/' \
+       --regex='/TESTPCGFLAG\(([^,)]*).*/PageCgroup\1/'        \
+       --regex='/SETPCGFLAG\(([^,)]*).*/SetPageCgroup\1/'      \
+       --regex='/CLEARPCGFLAG\(([^,)]*).*/ClearPageCgroup\1/'  \
+       --regex='/TESTCLEARPCGFLAG\(([^,)]*).*/TestClearPageCgroup\1/' \
        --regex='/_PE(\([^,)]*\).*/PEVENT_ERRNO__\1/'           \
        --regex='/PCI_OP_READ(\([a-z]*[a-z]\).*[1-4])/pci_bus_read_config_\1/' \
-       --regex='/PCI_OP_WRITE(\([a-z]*[a-z]\).*[1-4])/pci_bus_write_config_\1/'
+       --regex='/PCI_OP_WRITE(\([a-z]*[a-z]\).*[1-4])/pci_bus_write_config_\1/'\
+       --regex='/DEFINE_HASHTABLE\((\w*)/\1/v/'
 
        all_kconfigs | xargs $1 -a                              \
        --regex='/^[ \t]*\(\(menu\)*config\)[ \t]+\([a-zA-Z0-9_]+\)/\3/'
@@ -266,7 +281,7 @@ xtags()
                emacs $1
        else
                all_target_sources | xargs $1 -a
-        fi
+       fi
 }
 
 # Support um (which uses SUBARCH)
index d35b4915b00d4722e76464baba6b272ce86332f0..d606f3d12d6bfb8f27d849a3b4fa9fc9ee411875 100644 (file)
@@ -12,15 +12,41 @@ config EVM
 
          If you are unsure how to answer this question, answer N.
 
-config EVM_HMAC_VERSION
-       int "EVM HMAC version"
+if EVM
+
+menu "EVM options"
+
+config EVM_ATTR_FSUUID
+       bool "FSUUID (version 2)"
+       default y
        depends on EVM
-       default 2
        help
-         This options adds EVM HMAC version support.
-         1 - original version
-         2 - add per filesystem unique identifier (UUID) (default)
+         Include filesystem UUID for HMAC calculation.
+
+         Default value is 'selected', which is former version 2.
+         if 'not selected', it is former version 1
 
-         WARNING: changing the HMAC calculation method or adding 
+         WARNING: changing the HMAC calculation method or adding
          additional info to the calculation, requires existing EVM
-         labeled file systems to be relabeled.  
+         labeled file systems to be relabeled.
+
+config EVM_EXTRA_SMACK_XATTRS
+       bool "Additional SMACK xattrs"
+       depends on EVM && SECURITY_SMACK
+       default n
+       help
+         Include additional SMACK xattrs for HMAC calculation.
+
+         In addition to the original security xattrs (eg. security.selinux,
+         security.SMACK64, security.capability, and security.ima) included
+         in the HMAC calculation, enabling this option includes newly defined
+         Smack xattrs: security.SMACK64EXEC, security.SMACK64TRANSMUTE and
+         security.SMACK64MMAP.
+
+         WARNING: changing the HMAC calculation method or adding
+         additional info to the calculation, requires existing EVM
+         labeled file systems to be relabeled.
+
+endmenu
+
+endif
index 37c88ddb3cfe459e88822742e3cd25b15fb6f992..88bfe77efa1cf10ccdcb5bb50e6e949f05f017bf 100644 (file)
 extern int evm_initialized;
 extern char *evm_hmac;
 extern char *evm_hash;
-extern int evm_hmac_version;
+
+#define EVM_ATTR_FSUUID                0x0001
+
+extern int evm_hmac_attrs;
 
 extern struct crypto_shash *hmac_tfm;
 extern struct crypto_shash *hash_tfm;
index 6b540f1822e0b43c175466615ff78db50b0df0f5..5e9687f02e1b14e56e56d3a303afbef23ea79dca 100644 (file)
@@ -112,7 +112,7 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
        hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid);
        hmac_misc.mode = inode->i_mode;
        crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc));
-       if (evm_hmac_version > 1)
+       if (evm_hmac_attrs & EVM_ATTR_FSUUID)
                crypto_shash_update(desc, inode->i_sb->s_uuid,
                                    sizeof(inode->i_sb->s_uuid));
        crypto_shash_final(desc, digest);
index 6e0bd933b6a9a8a815f5d57c147f1d18dfbfec36..3bcb80df4d01f1ca30927c7e61c6b2b19b5ca15f 100644 (file)
@@ -32,7 +32,7 @@ static char *integrity_status_msg[] = {
 };
 char *evm_hmac = "hmac(sha1)";
 char *evm_hash = "sha1";
-int evm_hmac_version = CONFIG_EVM_HMAC_VERSION;
+int evm_hmac_attrs;
 
 char *evm_config_xattrnames[] = {
 #ifdef CONFIG_SECURITY_SELINUX
@@ -40,6 +40,11 @@ char *evm_config_xattrnames[] = {
 #endif
 #ifdef CONFIG_SECURITY_SMACK
        XATTR_NAME_SMACK,
+#ifdef CONFIG_EVM_EXTRA_SMACK_XATTRS
+       XATTR_NAME_SMACKEXEC,
+       XATTR_NAME_SMACKTRANSMUTE,
+       XATTR_NAME_SMACKMMAP,
+#endif
 #endif
 #ifdef CONFIG_IMA_APPRAISE
        XATTR_NAME_IMA,
@@ -57,6 +62,14 @@ static int __init evm_set_fixmode(char *str)
 }
 __setup("evm=", evm_set_fixmode);
 
+static void __init evm_init_config(void)
+{
+#ifdef CONFIG_EVM_ATTR_FSUUID
+       evm_hmac_attrs |= EVM_ATTR_FSUUID;
+#endif
+       pr_info("HMAC attrs: 0x%x\n", evm_hmac_attrs);
+}
+
 static int evm_find_protected_xattrs(struct dentry *dentry)
 {
        struct inode *inode = dentry->d_inode;
@@ -287,12 +300,20 @@ out:
  * @xattr_value: pointer to the new extended attribute value
  * @xattr_value_len: pointer to the new extended attribute value length
  *
- * Updating 'security.evm' requires CAP_SYS_ADMIN privileges and that
- * the current value is valid.
+ * Before allowing the 'security.evm' protected xattr to be updated,
+ * verify the existing value is valid.  As only the kernel should have
+ * access to the EVM encrypted key needed to calculate the HMAC, prevent
+ * userspace from writing HMAC value.  Writing 'security.evm' requires
+ * requires CAP_SYS_ADMIN privileges.
  */
 int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name,
                       const void *xattr_value, size_t xattr_value_len)
 {
+       const struct evm_ima_xattr_data *xattr_data = xattr_value;
+
+       if ((strcmp(xattr_name, XATTR_NAME_EVM) == 0)
+           && (xattr_data->type == EVM_XATTR_HMAC))
+               return -EPERM;
        return evm_protect_xattr(dentry, xattr_name, xattr_value,
                                 xattr_value_len);
 }
@@ -432,6 +453,8 @@ static int __init init_evm(void)
 {
        int error;
 
+       evm_init_config();
+
        error = evm_init_secfs();
        if (error < 0) {
                pr_info("Error registering secfs\n");
index 291bf0f3a46d7989c1d4cf77fdba0237a6d0fb1f..d3113d4aaa3c3f41d0395f4b0862f37389b8997f 100644 (file)
@@ -341,7 +341,7 @@ static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,
        return 0;
 }
 
-static void ima_reset_appraise_flags(struct inode *inode)
+static void ima_reset_appraise_flags(struct inode *inode, int digsig)
 {
        struct integrity_iint_cache *iint;
 
@@ -353,18 +353,22 @@ static void ima_reset_appraise_flags(struct inode *inode)
                return;
 
        iint->flags &= ~IMA_DONE_MASK;
+       if (digsig)
+               iint->flags |= IMA_DIGSIG;
        return;
 }
 
 int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
                       const void *xattr_value, size_t xattr_value_len)
 {
+       const struct evm_ima_xattr_data *xvalue = xattr_value;
        int result;
 
        result = ima_protect_xattr(dentry, xattr_name, xattr_value,
                                   xattr_value_len);
        if (result == 1) {
-               ima_reset_appraise_flags(dentry->d_inode);
+               ima_reset_appraise_flags(dentry->d_inode,
+                        (xvalue->type == EVM_IMA_XATTR_DIGSIG) ? 1 : 0);
                result = 0;
        }
        return result;
@@ -376,7 +380,7 @@ int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
 
        result = ima_protect_xattr(dentry, xattr_name, NULL, 0);
        if (result == 1) {
-               ima_reset_appraise_flags(dentry->d_inode);
+               ima_reset_appraise_flags(dentry->d_inode, 0);
                result = 0;
        }
        return result;
index 1bde8e62776620e6e5ed2cb2900d39c91df992f8..ccd0ac8fa9a0b5db445b901fb2c643ba8801edf2 100644 (file)
 
 static struct crypto_shash *ima_shash_tfm;
 
+/**
+ * ima_kernel_read - read file content
+ *
+ * This is a function for reading file content instead of kernel_read().
+ * It does not perform locking checks to ensure it cannot be blocked.
+ * It does not perform security checks because it is irrelevant for IMA.
+ *
+ */
+static int ima_kernel_read(struct file *file, loff_t offset,
+                          char *addr, unsigned long count)
+{
+       mm_segment_t old_fs;
+       char __user *buf = addr;
+       ssize_t ret;
+
+       if (!(file->f_mode & FMODE_READ))
+               return -EBADF;
+       if (!file->f_op->read && !file->f_op->aio_read)
+               return -EINVAL;
+
+       old_fs = get_fs();
+       set_fs(get_ds());
+       if (file->f_op->read)
+               ret = file->f_op->read(file, buf, count, &offset);
+       else
+               ret = do_sync_read(file, buf, count, &offset);
+       set_fs(old_fs);
+       return ret;
+}
+
 int ima_init_crypto(void)
 {
        long rc;
@@ -104,7 +134,7 @@ static int ima_calc_file_hash_tfm(struct file *file,
        while (offset < i_size) {
                int rbuf_len;
 
-               rbuf_len = kernel_read(file, offset, rbuf, PAGE_SIZE);
+               rbuf_len = ima_kernel_read(file, offset, rbuf, PAGE_SIZE);
                if (rbuf_len < 0) {
                        rc = rbuf_len;
                        break;
index dcc98cf542d83fb4768ff3bd2ec113c204c7ecb6..09baa335ebc79ed9c7054272dc38534e02c85175 100644 (file)
@@ -81,7 +81,6 @@ static void ima_rdwr_violation_check(struct file *file)
 {
        struct inode *inode = file_inode(file);
        fmode_t mode = file->f_mode;
-       int must_measure;
        bool send_tomtou = false, send_writers = false;
        char *pathbuf = NULL;
        const char *pathname;
@@ -92,18 +91,19 @@ static void ima_rdwr_violation_check(struct file *file)
        mutex_lock(&inode->i_mutex);    /* file metadata: permissions, xattr */
 
        if (mode & FMODE_WRITE) {
-               if (atomic_read(&inode->i_readcount) && IS_IMA(inode))
-                       send_tomtou = true;
-               goto out;
+               if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) {
+                       struct integrity_iint_cache *iint;
+                       iint = integrity_iint_find(inode);
+                       /* IMA_MEASURE is set from reader side */
+                       if (iint && (iint->flags & IMA_MEASURE))
+                               send_tomtou = true;
+               }
+       } else {
+               if ((atomic_read(&inode->i_writecount) > 0) &&
+                   ima_must_measure(inode, MAY_READ, FILE_CHECK))
+                       send_writers = true;
        }
 
-       must_measure = ima_must_measure(inode, MAY_READ, FILE_CHECK);
-       if (!must_measure)
-               goto out;
-
-       if (atomic_read(&inode->i_writecount) > 0)
-               send_writers = true;
-out:
        mutex_unlock(&inode->i_mutex);
 
        if (!send_tomtou && !send_writers)
index 14d04e63b1f0e09ef15b4cf64057bd7cc861ed1d..be491a74c1edc4a279f76121dab615305c3a47f2 100644 (file)
@@ -147,7 +147,7 @@ struct security_class_mapping secclass_map[] = {
        { "peer", { "recv", NULL } },
        { "capability2",
          { "mac_override", "mac_admin", "syslog", "wake_alarm", "block_suspend",
-           NULL } },
+           "audit_read", NULL } },
        { "kernel_service", { "use_as_override", "create_files_as", NULL } },
        { "tun_socket",
          { COMMON_SOCK_PERMS, "attach_queue", NULL } },
index 9ca5e647e54bf80a7a68552b0ade15ae4f5599cb..225c73152ee9e5896c06a3b7f1fbb16095bb5bd2 100644 (file)
@@ -660,7 +660,7 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
                                  int atomic, int hop)
 {
        struct snd_seq_subscribers *subs;
-       int err = 0, num_ev = 0;
+       int err, result = 0, num_ev = 0;
        struct snd_seq_event event_saved;
        struct snd_seq_client_port *src_port;
        struct snd_seq_port_subs_info *grp;
@@ -685,8 +685,12 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
                                                  subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL);
                err = snd_seq_deliver_single_event(client, event,
                                                   0, atomic, hop);
-               if (err < 0)
-                       break;
+               if (err < 0) {
+                       /* save first error that occurs and continue */
+                       if (!result)
+                               result = err;
+                       continue;
+               }
                num_ev++;
                /* restore original event record */
                *event = event_saved;
@@ -697,7 +701,7 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
                up_read(&grp->list_mutex);
        *event = event_saved; /* restore */
        snd_seq_port_unlock(src_port);
-       return (err < 0) ? err : num_ev;
+       return (result < 0) ? result : num_ev;
 }
 
 
@@ -709,7 +713,7 @@ static int port_broadcast_event(struct snd_seq_client *client,
                                struct snd_seq_event *event,
                                int atomic, int hop)
 {
-       int num_ev = 0, err = 0;
+       int num_ev = 0, err, result = 0;
        struct snd_seq_client *dest_client;
        struct snd_seq_client_port *port;
 
@@ -724,14 +728,18 @@ static int port_broadcast_event(struct snd_seq_client *client,
                err = snd_seq_deliver_single_event(NULL, event,
                                                   SNDRV_SEQ_FILTER_BROADCAST,
                                                   atomic, hop);
-               if (err < 0)
-                       break;
+               if (err < 0) {
+                       /* save first error that occurs and continue */
+                       if (!result)
+                               result = err;
+                       continue;
+               }
                num_ev++;
        }
        read_unlock(&dest_client->ports_lock);
        snd_seq_client_unlock(dest_client);
        event->dest.port = SNDRV_SEQ_ADDRESS_BROADCAST; /* restore */
-       return (err < 0) ? err : num_ev;
+       return (result < 0) ? result : num_ev;
 }
 
 /*
@@ -741,7 +749,7 @@ static int port_broadcast_event(struct snd_seq_client *client,
 static int broadcast_event(struct snd_seq_client *client,
                           struct snd_seq_event *event, int atomic, int hop)
 {
-       int err = 0, num_ev = 0;
+       int err, result = 0, num_ev = 0;
        int dest;
        struct snd_seq_addr addr;
 
@@ -760,12 +768,16 @@ static int broadcast_event(struct snd_seq_client *client,
                        err = snd_seq_deliver_single_event(NULL, event,
                                                           SNDRV_SEQ_FILTER_BROADCAST,
                                                           atomic, hop);
-               if (err < 0)
-                       break;
+               if (err < 0) {
+                       /* save first error that occurs and continue */
+                       if (!result)
+                               result = err;
+                       continue;
+               }
                num_ev += err;
        }
        event->dest = addr; /* restore */
-       return (err < 0) ? err : num_ev;
+       return (result < 0) ? result : num_ev;
 }
 
 
index 559989992bef6ba7f35f7c63731bb6d40b5bd463..53a403e17c5bba08a755da454dfee3d5829c2cc9 100644 (file)
@@ -124,7 +124,7 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f,
        snd_use_lock_use(&f->use_lock);
        err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */
        if (err < 0) {
-               if (err == -ENOMEM)
+               if ((err == -ENOMEM) || (err == -EAGAIN))
                        atomic_inc(&f->overflow);
                snd_use_lock_free(&f->use_lock);
                return err;
index cfd455a8ac1af366fb1ed63ae05497035b528b29..777a45e08e539aa3c7320f31c5d1329db1ea0944 100644 (file)
@@ -390,7 +390,7 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
        struct timespec tstamp;
 
        if (timer_tstamp_monotonic)
-               do_posix_clock_monotonic_gettime(&tstamp);
+               ktime_get_ts(&tstamp);
        else
                getnstimeofday(&tstamp);
        if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START ||
@@ -1203,7 +1203,7 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
        }
        if (tu->last_resolution != resolution || ticks > 0) {
                if (timer_tstamp_monotonic)
-                       do_posix_clock_monotonic_gettime(&tstamp);
+                       ktime_get_ts(&tstamp);
                else
                        getnstimeofday(&tstamp);
        }
index d1c93a1e0978a6b5b8e1cbc57c2e4ea4c07b7710..e13eef99c27afe22fc7a07cdabde724ee1734166 100644 (file)
@@ -208,8 +208,6 @@ int snd_bebob_stream_set_rate(struct snd_bebob *bebob, unsigned int rate);
 int snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob,
                                          bool *internal);
 int snd_bebob_stream_discover(struct snd_bebob *bebob);
-int snd_bebob_stream_map(struct snd_bebob *bebob,
-                        struct amdtp_stream *stream);
 int snd_bebob_stream_init_duplex(struct snd_bebob *bebob);
 int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate);
 void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob);
index bc4f82776fdaf690e296aa11a27c5fa77b44c19e..ef4d0c9f65781a2e2684a143264a8b3aad12d8be 100644 (file)
@@ -655,8 +655,6 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
        struct amdtp_stream *master, *slave;
        atomic_t *master_substreams, *slave_substreams;
 
-       mutex_lock(&bebob->mutex);
-
        if (bebob->master == &bebob->rx_stream) {
                slave  = &bebob->tx_stream;
                master = &bebob->rx_stream;
@@ -669,6 +667,8 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
                master_substreams = &bebob->capture_substreams;
        }
 
+       mutex_lock(&bebob->mutex);
+
        if (atomic_read(slave_substreams) == 0) {
                amdtp_stream_pcm_abort(slave);
                amdtp_stream_stop(slave);
index 996fdc44c83c504efa2e2ad28a76895c6598154a..3e2ed8e82cbc49b4699c82ad30776ef8a4300253 100644 (file)
@@ -346,7 +346,6 @@ static void __exit snd_efw_exit(void)
 {
        snd_efw_transaction_unregister();
        driver_unregister(&efw_driver.driver);
-       mutex_destroy(&devices_mutex);
 }
 
 module_init(snd_efw_init);
index d2b36be4d2f86a1b996259417fe4165f0ce996fd..4f0201a95222a2502ec438199fe9e78cad33b647 100644 (file)
@@ -162,7 +162,6 @@ enum snd_efw_grp_type {
        SND_EFW_CH_TYPE_GUITAR                  = 7,
        SND_EFW_CH_TYPE_PIEZO_GUITAR            = 8,
        SND_EFW_CH_TYPE_GUITAR_STRING           = 9,
-       SND_EFW_CH_TYPE_VIRTUAL                 = 0x10000,
        SND_EFW_CH_TYPE_DUMMY
 };
 struct snd_efw_phys_meters {
index 4f8216fb6b62fe4321658e7cbab2215b76fa3470..33df8655fe81f28872e01bbecd817ba7c37f8ede 100644 (file)
@@ -58,7 +58,7 @@ hwdep_read_resp_buf(struct snd_efw *efw, char __user *buf, long remained,
                        efw->pull_ptr += till_end;
                        if (efw->pull_ptr >= efw->resp_buf +
                                             snd_efw_resp_buf_size)
-                               efw->pull_ptr = efw->resp_buf;
+                               efw->pull_ptr -= snd_efw_resp_buf_size;
 
                        length -= till_end;
                        buf += till_end;
index 541569022a7c319fff4464ec57dab44d65993f58..b985fc5ebdc6b9cc490e41695d76b579a7b3fe3d 100644 (file)
@@ -284,8 +284,6 @@ void snd_efw_stream_stop_duplex(struct snd_efw *efw)
        struct amdtp_stream *master, *slave;
        atomic_t *master_substreams, *slave_substreams;
 
-       mutex_lock(&efw->mutex);
-
        if (efw->master == &efw->rx_stream) {
                slave  = &efw->tx_stream;
                master = &efw->rx_stream;
@@ -298,6 +296,8 @@ void snd_efw_stream_stop_duplex(struct snd_efw *efw)
                master_substreams = &efw->capture_substreams;
        }
 
+       mutex_lock(&efw->mutex);
+
        if (atomic_read(slave_substreams) == 0) {
                stop_stream(efw, slave);
 
index aa56b8ac537ca71d1db675e0dbc06fdcb66bcf48..255dabc6fc3313debc4b943d02e80a15b9e7f70a 100644 (file)
@@ -8,19 +8,19 @@
 
 /*
  * Fireworks have its own transaction. The transaction can be delivered by AV/C
- * Vendor Specific command. But at least Windows driver and firmware version 5.5
- * or later don't use it.
+ * Vendor Specific command frame or usual asynchronous transaction. At least,
+ * Windows driver and firmware version 5.5 or later don't use AV/C command.
  *
  * Transaction substance:
- *  At first, 6 data exist. Following to the 6 data, parameters for each
- *  commands exists. All of parameters are 32 bit alighed to big endian.
+ *  At first, 6 data exist. Following to the data, parameters for each command
+ *  exist. All of the parameters are 32 bit alighed to big endian.
  *   data[0]:  Length of transaction substance
  *   data[1]:  Transaction version
  *   data[2]:  Sequence number. This is incremented by the device
- *   data[3]:  transaction category
- *   data[4]:  transaction command
- *   data[5]:  return value in response.
- *   data[6-]: parameters
+ *   data[3]:  Transaction category
+ *   data[4]:  Transaction command
+ *   data[5]:  Return value in response.
+ *   data[6-]: Parameters
  *
  * Transaction address:
  *  command:   0xecc000000000
@@ -148,7 +148,7 @@ copy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode)
 
                efw->push_ptr += till_end;
                if (efw->push_ptr >= efw->resp_buf + snd_efw_resp_buf_size)
-                       efw->push_ptr = efw->resp_buf;
+                       efw->push_ptr -= snd_efw_resp_buf_size;
 
                length -= till_end;
                data += till_end;
index cd77b9b19b738975ecea8a53263efa51fb2bf19b..bb65a124e00689657cc7674a25202f4df3b60249 100644 (file)
@@ -237,6 +237,12 @@ enum {
         AZX_DCAPS_COUNT_LPIB_DELAY | AZX_DCAPS_PM_RUNTIME | \
         AZX_DCAPS_I915_POWERWELL)
 
+/* Broadwell HDMI can't use position buffer reliably, force to use LPIB */
+#define AZX_DCAPS_INTEL_BROADWELL \
+       (AZX_DCAPS_SCH_SNOOP | AZX_DCAPS_ALIGN_BUFSIZE | \
+        AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_PM_RUNTIME | \
+        AZX_DCAPS_I915_POWERWELL)
+
 /* quirks for ATI SB / AMD Hudson */
 #define AZX_DCAPS_PRESET_ATI_SB \
        (AZX_DCAPS_ATI_SNOOP | AZX_DCAPS_NO_TCSEL | \
@@ -1367,12 +1373,6 @@ static int azx_first_init(struct azx *chip)
        /* initialize streams */
        azx_init_stream(chip);
 
-       /* workaround for Broadwell HDMI: the first stream is broken,
-        * so mask it by keeping it as if opened
-        */
-       if (pci->vendor == 0x8086 && pci->device == 0x160c)
-               chip->azx_dev[0].opened = 1;
-
        /* initialize chip */
        azx_init_pci(chip);
        azx_init_chip(chip, (probe_only[dev] & 2) == 0);
@@ -1769,7 +1769,7 @@ static const struct pci_device_id azx_ids[] = {
          .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
        /* Broadwell */
        { PCI_DEVICE(0x8086, 0x160c),
-         .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
+         .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_BROADWELL },
        /* 5 Series/3400 */
        { PCI_DEVICE(0x8086, 0x3b56),
          .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },
index be0a9ee0b8049afc617d36a8074d01d9953405a8..3e4417b0ddbe7a1eec6af39d072a993483ae34b4 100644 (file)
@@ -1594,10 +1594,18 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
                 * Re-setup pin and infoframe. This is needed e.g. when
                 * - sink is first plugged-in (infoframe is not set up if !monitor_present)
                 * - transcoder can change during stream playback on Haswell
+                *   and this can make HW reset converter selection on a pin.
                 */
-               if (eld->eld_valid && !old_eld_valid && per_pin->setup)
+               if (eld->eld_valid && !old_eld_valid && per_pin->setup) {
+                       if (is_haswell_plus(codec) || is_valleyview(codec)) {
+                               intel_verify_pin_cvt_connect(codec, per_pin);
+                               intel_not_share_assigned_cvt(codec, pin_nid,
+                                                       per_pin->mux_idx);
+                       }
+
                        hdmi_setup_audio_infoframe(codec, per_pin,
                                                   per_pin->non_pcm);
+               }
        }
 
        if (eld_changed)
index 12fb411adf772f04d2041f710a8fe80e5cf35a53..af76995fa966f620edd5ae59293efccf7383e54f 100644 (file)
@@ -929,6 +929,7 @@ struct alc_codec_rename_pci_table {
 };
 
 static struct alc_codec_rename_table rename_tbl[] = {
+       { 0x10ec0221, 0xf00f, 0x1003, "ALC231" },
        { 0x10ec0269, 0xfff0, 0x3010, "ALC277" },
        { 0x10ec0269, 0xf0f0, 0x2010, "ALC259" },
        { 0x10ec0269, 0xf0f0, 0x3010, "ALC258" },
@@ -937,6 +938,7 @@ static struct alc_codec_rename_table rename_tbl[] = {
        { 0x10ec0269, 0xffff, 0x6023, "ALC281X" },
        { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" },
        { 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" },
+       { 0x10ec0662, 0xffff, 0x4020, "ALC656" },
        { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" },
        { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" },
        { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" },
@@ -956,6 +958,19 @@ static struct alc_codec_rename_pci_table rename_pci_tbl[] = {
        { 0x10ec0293, 0x1028, 0, "ALC3235" },
        { 0x10ec0255, 0x1028, 0, "ALC3234" },
        { 0x10ec0668, 0x1028, 0, "ALC3661" },
+       { 0x10ec0275, 0x1028, 0, "ALC3260" },
+       { 0x10ec0899, 0x1028, 0, "ALC3861" },
+       { 0x10ec0670, 0x1025, 0, "ALC669X" },
+       { 0x10ec0676, 0x1025, 0, "ALC679X" },
+       { 0x10ec0282, 0x1043, 0, "ALC3229" },
+       { 0x10ec0233, 0x1043, 0, "ALC3236" },
+       { 0x10ec0280, 0x103c, 0, "ALC3228" },
+       { 0x10ec0282, 0x103c, 0, "ALC3227" },
+       { 0x10ec0286, 0x103c, 0, "ALC3242" },
+       { 0x10ec0290, 0x103c, 0, "ALC3241" },
+       { 0x10ec0668, 0x103c, 0, "ALC3662" },
+       { 0x10ec0283, 0x17aa, 0, "ALC3239" },
+       { 0x10ec0292, 0x17aa, 0, "ALC3232" },
        { } /* terminator */
 };
 
@@ -1412,6 +1427,7 @@ static const struct snd_pci_quirk alc880_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS W5A", ALC880_FIXUP_ASUS_W5A),
        SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V),
        SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_FIXUP_GPIO1),
+       SND_PCI_QUIRK(0x147b, 0x1045, "ABit AA8XE", ALC880_FIXUP_6ST_AUTOMUTE),
        SND_PCI_QUIRK(0x1558, 0x5401, "Clevo GPIO2", ALC880_FIXUP_GPIO2),
        SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF),
        SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_FIXUP_UNIWILL_DIG),
@@ -4230,6 +4246,7 @@ enum {
        ALC269_FIXUP_HEADSET_MIC,
        ALC269_FIXUP_QUANTA_MUTE,
        ALC269_FIXUP_LIFEBOOK,
+       ALC269_FIXUP_LIFEBOOK_EXTMIC,
        ALC269_FIXUP_AMIC,
        ALC269_FIXUP_DMIC,
        ALC269VB_FIXUP_AMIC,
@@ -4367,6 +4384,13 @@ static const struct hda_fixup alc269_fixups[] = {
                .chained = true,
                .chain_id = ALC269_FIXUP_QUANTA_MUTE
        },
+       [ALC269_FIXUP_LIFEBOOK_EXTMIC] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x19, 0x01a1903c }, /* headset mic, with jack detect */
+                       { }
+               },
+       },
        [ALC269_FIXUP_AMIC] = {
                .type = HDA_FIXUP_PINS,
                .v.pins = (const struct hda_pintbl[]) {
@@ -4741,18 +4765,12 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1028, 0x0614, "Dell Inspiron 3135", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
        SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
-       SND_PCI_QUIRK(0x1028, 0x062c, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK),
        SND_PCI_QUIRK(0x1028, 0x063f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x064b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x064d, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0668, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0669, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0674, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x067e, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x067f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0680, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0684, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x15cc, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x15cd, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
@@ -4764,14 +4782,24 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x1983, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x218b, "HP", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED),
        /* ALC282 */
+       SND_PCI_QUIRK(0x103c, 0x220d, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+       SND_PCI_QUIRK(0x103c, 0x220e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x220f, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+       SND_PCI_QUIRK(0x103c, 0x2210, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+       SND_PCI_QUIRK(0x103c, 0x2211, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+       SND_PCI_QUIRK(0x103c, 0x2212, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x2213, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+       SND_PCI_QUIRK(0x103c, 0x2214, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x2266, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x2267, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x2268, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x2269, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x226a, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x226b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+       SND_PCI_QUIRK(0x103c, 0x226c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+       SND_PCI_QUIRK(0x103c, 0x226d, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+       SND_PCI_QUIRK(0x103c, 0x226e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+       SND_PCI_QUIRK(0x103c, 0x226f, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x227a, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x227b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x229e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
@@ -4811,6 +4839,10 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x22c8, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x22c3, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x22c4, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+       SND_PCI_QUIRK(0x103c, 0x2334, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+       SND_PCI_QUIRK(0x103c, 0x2335, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+       SND_PCI_QUIRK(0x103c, 0x2336, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+       SND_PCI_QUIRK(0x103c, 0x2337, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED),
        SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
        SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
@@ -4834,6 +4866,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x104d, 0x9099, "Sony VAIO S13", ALC275_FIXUP_SONY_DISABLE_AAMIX),
        SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
        SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK),
+       SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC),
        SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
        SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE),
        SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE),
@@ -4974,6 +5007,26 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
                .subvendor = 0x1028,
 #ifdef CONFIG_SND_DEBUG_VERBOSE
                .name = "Dell",
+#endif
+               .pins = (const struct hda_pintbl[]) {
+                       {0x12, 0x90a60160},
+                       {0x14, 0x90170120},
+                       {0x17, 0x90170140},
+                       {0x18, 0x40000000},
+                       {0x19, 0x411111f0},
+                       {0x1a, 0x411111f0},
+                       {0x1b, 0x411111f0},
+                       {0x1d, 0x41163b05},
+                       {0x1e, 0x411111f0},
+                       {0x21, 0x0321102f},
+               },
+               .value = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+       },
+       {
+               .codec = 0x10ec0255,
+               .subvendor = 0x1028,
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+               .name = "Dell",
 #endif
                .pins = (const struct hda_pintbl[]) {
                        {0x12, 0x90a60160},
@@ -5129,7 +5182,7 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
                        {0x1d, 0x40700001},
                        {0x1e, 0x411111f0},
                },
-               .value = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+               .value = ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
        },
        {}
 };
@@ -6012,6 +6065,27 @@ static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = {
                .subvendor = 0x1028,
 #ifdef CONFIG_SND_DEBUG_VERBOSE
                .name = "Dell",
+#endif
+               .pins = (const struct hda_pintbl[]) {
+                       {0x12, 0x99a30140},
+                       {0x14, 0x90170110},
+                       {0x15, 0x0321101f},
+                       {0x16, 0x03011020},
+                       {0x18, 0x40000008},
+                       {0x19, 0x411111f0},
+                       {0x1a, 0x411111f0},
+                       {0x1b, 0x411111f0},
+                       {0x1d, 0x41000001},
+                       {0x1e, 0x411111f0},
+                       {0x1f, 0x411111f0},
+               },
+               .value = ALC668_FIXUP_AUTO_MUTE,
+       },
+       {
+               .codec = 0x10ec0668,
+               .subvendor = 0x1028,
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+               .name = "Dell",
 #endif
                .pins = (const struct hda_pintbl[]) {
                        {0x12, 0x99a30150},
@@ -6190,6 +6264,7 @@ static const struct hda_codec_preset snd_hda_preset_realtek[] = {
        { .id = 0x10ec0221, .name = "ALC221", .patch = patch_alc269 },
        { .id = 0x10ec0231, .name = "ALC231", .patch = patch_alc269 },
        { .id = 0x10ec0233, .name = "ALC233", .patch = patch_alc269 },
+       { .id = 0x10ec0235, .name = "ALC233", .patch = patch_alc269 },
        { .id = 0x10ec0255, .name = "ALC255", .patch = patch_alc269 },
        { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
        { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
@@ -6223,10 +6298,12 @@ static const struct hda_codec_preset snd_hda_preset_realtek[] = {
          .patch = patch_alc662 },
        { .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
        { .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 },
+       { .id = 0x10ec0667, .name = "ALC667", .patch = patch_alc662 },
        { .id = 0x10ec0668, .name = "ALC668", .patch = patch_alc662 },
        { .id = 0x10ec0670, .name = "ALC670", .patch = patch_alc662 },
        { .id = 0x10ec0671, .name = "ALC671", .patch = patch_alc662 },
        { .id = 0x10ec0680, .name = "ALC680", .patch = patch_alc680 },
+       { .id = 0x10ec0867, .name = "ALC891", .patch = patch_alc882 },
        { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
        { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
        { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },
index 68340d7df76d14cc85bfa534127c60c20db3fada..c91860e0a28d03020ec73f58884f66c1f88d5adb 100644 (file)
@@ -2779,7 +2779,7 @@ static void intel8x0_measure_ac97_clock(struct intel8x0 *chip)
        unsigned long port;
        unsigned long pos, pos1, t;
        int civ, timeout = 1000, attempt = 1;
-       struct timespec start_time, stop_time;
+       ktime_t start_time, stop_time;
 
        if (chip->ac97_bus->clock != 48000)
                return; /* specified in module option */
@@ -2813,7 +2813,7 @@ static void intel8x0_measure_ac97_clock(struct intel8x0 *chip)
                iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE);
                iputdword(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot);
        }
-       do_posix_clock_monotonic_gettime(&start_time);
+       start_time = ktime_get();
        spin_unlock_irq(&chip->reg_lock);
        msleep(50);
        spin_lock_irq(&chip->reg_lock);
@@ -2837,7 +2837,7 @@ static void intel8x0_measure_ac97_clock(struct intel8x0 *chip)
                pos += ichdev->position;
        }
        chip->in_measurement = 0;
-       do_posix_clock_monotonic_gettime(&stop_time);
+       stop_time = ktime_get();
        /* stop */
        if (chip->device_type == DEVICE_ALI) {
                iputdword(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 16));
@@ -2865,9 +2865,7 @@ static void intel8x0_measure_ac97_clock(struct intel8x0 *chip)
        }
 
        pos /= 4;
-       t = stop_time.tv_sec - start_time.tv_sec;
-       t *= 1000000;
-       t += (stop_time.tv_nsec - start_time.tv_nsec) / 1000;
+       t = ktime_us_delta(stop_time, start_time);
        dev_info(chip->card->dev,
                 "%s: measured %lu usecs (%lu samples)\n", __func__, t, pos);
        if (t == 0) {
index 5b5eb788996e1de2f105eeedffdc1bb0cb72caba..c1b49c36a951d74b1c12b417b20428b0f4326e70 100644 (file)
@@ -1,8 +1,10 @@
 /* TODO merge/factor in debugfs.c here */
 
+#include <ctype.h>
 #include <errno.h>
 #include <stdbool.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <sys/vfs.h>
 
@@ -96,12 +98,51 @@ static bool fs__check_mounts(struct fs *fs)
        return false;
 }
 
+static void mem_toupper(char *f, size_t len)
+{
+       while (len) {
+               *f = toupper(*f);
+               f++;
+               len--;
+       }
+}
+
+/*
+ * Check for "NAME_PATH" environment variable to override fs location (for
+ * testing). This matches the recommendation in Documentation/sysfs-rules.txt
+ * for SYSFS_PATH.
+ */
+static bool fs__env_override(struct fs *fs)
+{
+       char *override_path;
+       size_t name_len = strlen(fs->name);
+       /* name + "_PATH" + '\0' */
+       char upper_name[name_len + 5 + 1];
+       memcpy(upper_name, fs->name, name_len);
+       mem_toupper(upper_name, name_len);
+       strcpy(&upper_name[name_len], "_PATH");
+
+       override_path = getenv(upper_name);
+       if (!override_path)
+               return false;
+
+       fs->found = true;
+       strncpy(fs->path, override_path, sizeof(fs->path));
+       return true;
+}
+
 static const char *fs__get_mountpoint(struct fs *fs)
 {
+       if (fs__env_override(fs))
+               return fs->path;
+
        if (fs__check_mounts(fs))
                return fs->path;
 
-       return fs__read_mounts(fs) ? fs->path : NULL;
+       if (fs__read_mounts(fs))
+               return fs->path;
+
+       return NULL;
 }
 
 static const char *fs__mountpoint(int idx)
index bf7be77ddd621238aa2a26ca2103b309bdcd089f..833a96611da6d042e089294990801817d0f1ade9 100644 (file)
@@ -92,6 +92,7 @@ extern void yyerror(const char *str);
 "#"?("cpu")    { return K_CPU; }
 "#"?("vlan_tci") { return K_VLANT; }
 "#"?("vlan_pr")        { return K_VLANP; }
+"#"?("rand")   { return K_RAND; }
 
 ":"            { return ':'; }
 ","            { return ','; }
index d15efc989ef500ac3f9998cf660d8e2bd22627e7..e6306c51c26f9e7cb53342ad088a9db1e4e32423 100644 (file)
@@ -56,7 +56,7 @@ static void bpf_set_jmp_label(char *label, enum jmp_type type);
 %token OP_LDXI
 
 %token K_PKT_LEN K_PROTO K_TYPE K_NLATTR K_NLATTR_NEST K_MARK K_QUEUE K_HATYPE
-%token K_RXHASH K_CPU K_IFIDX K_VLANT K_VLANP K_POFF
+%token K_RXHASH K_CPU K_IFIDX K_VLANT K_VLANP K_POFF K_RAND
 
 %token ':' ',' '[' ']' '(' ')' 'x' 'a' '+' 'M' '*' '&' '#' '%'
 
@@ -164,6 +164,9 @@ ldb
        | OP_LDB K_POFF {
                bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0,
                                   SKF_AD_OFF + SKF_AD_PAY_OFFSET); }
+       | OP_LDB K_RAND {
+               bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0,
+                                  SKF_AD_OFF + SKF_AD_RANDOM); }
        ;
 
 ldh
@@ -212,6 +215,9 @@ ldh
        | OP_LDH K_POFF {
                bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0,
                                   SKF_AD_OFF + SKF_AD_PAY_OFFSET); }
+       | OP_LDH K_RAND {
+               bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0,
+                                  SKF_AD_OFF + SKF_AD_RANDOM); }
        ;
 
 ldi
@@ -265,6 +271,9 @@ ld
        | OP_LD K_POFF {
                bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0,
                                   SKF_AD_OFF + SKF_AD_PAY_OFFSET); }
+       | OP_LD K_RAND {
+               bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0,
+                                  SKF_AD_OFF + SKF_AD_RANDOM); }
        | OP_LD 'M' '[' number ']' {
                bpf_set_curr_instr(BPF_LD | BPF_MEM, 0, 0, $4); }
        | OP_LD '[' 'x' '+' number ']' {
index cfe0cdcda3de990793b70d128e2267f53baac5ba..c5baf9c591b7bb5a2c280e173f4e0a2b561285fa 100644 (file)
@@ -43,8 +43,7 @@ static void get_exec_path(char *tpath, size_t size)
        free(path);
 }
 
-static void get_asm_insns(uint8_t *image, size_t len, unsigned long base,
-                         int opcodes)
+static void get_asm_insns(uint8_t *image, size_t len, int opcodes)
 {
        int count, i, pc = 0;
        char tpath[256];
@@ -107,13 +106,13 @@ static void put_klog_buff(char *buff)
 }
 
 static int get_last_jit_image(char *haystack, size_t hlen,
-                             uint8_t *image, size_t ilen,
-                             unsigned long *base)
+                             uint8_t *image, size_t ilen)
 {
        char *ptr, *pptr, *tmp;
        off_t off = 0;
        int ret, flen, proglen, pass, ulen = 0;
        regmatch_t pmatch[1];
+       unsigned long base;
        regex_t regex;
 
        if (hlen == 0)
@@ -136,7 +135,7 @@ static int get_last_jit_image(char *haystack, size_t hlen,
 
        ptr = haystack + off - (pmatch[0].rm_eo - pmatch[0].rm_so);
        ret = sscanf(ptr, "flen=%d proglen=%d pass=%d image=%lx",
-                    &flen, &proglen, &pass, base);
+                    &flen, &proglen, &pass, &base);
        if (ret != 4)
                return 0;
 
@@ -162,7 +161,7 @@ static int get_last_jit_image(char *haystack, size_t hlen,
        assert(ulen == proglen);
        printf("%d bytes emitted from JIT compiler (pass:%d, flen:%d)\n",
               proglen, pass, flen);
-       printf("%lx + <x>:\n", *base);
+       printf("%lx + <x>:\n", base);
 
        regfree(&regex);
        return ulen;
@@ -172,8 +171,7 @@ int main(int argc, char **argv)
 {
        int len, klen, opcodes = 0;
        char *kbuff;
-       unsigned long base;
-       uint8_t image[4096];
+       static uint8_t image[32768];
 
        if (argc > 1) {
                if (!strncmp("-o", argv[argc - 1], 2)) {
@@ -189,9 +187,9 @@ int main(int argc, char **argv)
 
        kbuff = get_klog_buff(&klen);
 
-       len = get_last_jit_image(kbuff, klen, image, sizeof(image), &base);
-       if (len > 0 && base > 0)
-               get_asm_insns(image, len, base, opcodes);
+       len = get_last_jit_image(kbuff, klen, image, sizeof(image));
+       if (len > 0)
+               get_asm_insns(image, len, opcodes);
 
        put_klog_buff(kbuff);
 
index c71b0f36d9e8d45285a5b2b1256876f4d6f27799..d460049cae8e7c798dcad7bcd8b0cea795dfb29b 100644 (file)
@@ -184,9 +184,10 @@ following filters are defined:
        - in_tx: only when the target is in a hardware transaction
        - no_tx: only when the target is not in a hardware transaction
        - abort_tx: only when the target is a hardware transaction abort
+       - cond: conditional branches
 
 +
-The option requires at least one branch type among any, any_call, any_ret, ind_call.
+The option requires at least one branch type among any, any_call, any_ret, ind_call, cond.
 The privilege levels may be omitted, in which case, the privilege levels of the associated
 event are applied to the branch filter. Both kernel (k) and hypervisor (hv) privilege
 levels are subject to permissions.  When sampling on multiple events, branch stack sampling
index a1b5185402d5d2721a9c5aefe7fd9c26630d1a32..cefdf430d1b4066624431fb57f1efc25c04e3fd4 100644 (file)
@@ -111,7 +111,7 @@ OPTIONS
 --fields=::
        Specify output field - multiple keys can be specified in CSV format.
        Following fields are available:
-       overhead, overhead_sys, overhead_us, sample and period.
+       overhead, overhead_sys, overhead_us, overhead_children, sample and period.
        Also it can contain any sort key(s).
 
        By default, every sort keys not specified in -F will be appended
@@ -163,6 +163,11 @@ OPTIONS
 
        Default: fractal,0.5,callee,function.
 
+--children::
+       Accumulate callchain of children to parent entry so that then can
+       show up in the output.  The output will have a new "Children" column
+       and will be sorted on the data.  It requires callchains are recorded.
+
 --max-stack::
        Set the stack depth limit when parsing the callchain, anything
        beyond the specified depth will be ignored. This is a trade-off
index dcfa54c851e9d6ee1dcd1b0ffed15a6863ee881e..180ae02137a519acf1b3d779a92eb651db6b4aff 100644 (file)
@@ -119,7 +119,7 @@ Default is to monitor all CPUS.
 --fields=::
        Specify output field - multiple keys can be specified in CSV format.
        Following fields are available:
-       overhead, overhead_sys, overhead_us, sample and period.
+       overhead, overhead_sys, overhead_us, overhead_children, sample and period.
        Also it can contain any sort key(s).
 
        By default, every sort keys not specified in --field will be appended
@@ -161,6 +161,12 @@ Default is to monitor all CPUS.
        Setup and enable call-graph (stack chain/backtrace) recording,
        implies -g.
 
+--children::
+       Accumulate callchain of children to parent entry so that then can
+       show up in the output.  The output will have a new "Children" column
+       and will be sorted on the data.  It requires -g/--call-graph option
+       enabled.
+
 --max-stack::
        Set the stack depth limit when parsing the callchain, anything
        beyond the specified depth will be ignored. This is a trade-off
index 02f0a4dd1a80f3d06a9e532503b75cd3230d0aa2..ae20edfcc3f7e3a61b683a60882ed4239a4bb581 100644 (file)
@@ -400,6 +400,7 @@ LIB_OBJS += $(OUTPUT)tests/hists_common.o
 LIB_OBJS += $(OUTPUT)tests/hists_link.o
 LIB_OBJS += $(OUTPUT)tests/hists_filter.o
 LIB_OBJS += $(OUTPUT)tests/hists_output.o
+LIB_OBJS += $(OUTPUT)tests/hists_cumulate.o
 LIB_OBJS += $(OUTPUT)tests/python-use.o
 LIB_OBJS += $(OUTPUT)tests/bp_signal.o
 LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o
@@ -788,8 +789,8 @@ help:
        @echo ''
        @echo 'Perf install targets:'
        @echo '  NOTE: documentation build requires asciidoc, xmlto packages to be installed'
-       @echo '  HINT: use "make prefix=<path> <install target>" to install to a particular'
-       @echo '        path like make prefix=/usr/local install install-doc'
+       @echo '  HINT: use "prefix" or "DESTDIR" to install to a particular'
+       @echo '        path like "make prefix=/usr/local install install-doc"'
        @echo '  install        - install compiled binaries'
        @echo '  install-doc    - install *all* documentation'
        @echo '  install-man    - install manpage documentation'
@@ -814,17 +815,20 @@ INSTALL_DOC_TARGETS += quick-install-doc quick-install-man quick-install-html
 $(DOC_TARGETS):
        $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:doc=all)
 
+TAG_FOLDERS= . ../lib/traceevent ../lib/api ../lib/symbol
+TAG_FILES= ../../include/uapi/linux/perf_event.h
+
 TAGS:
        $(RM) TAGS
-       $(FIND) . -name '*.[hcS]' -print | xargs etags -a
+       $(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs etags -a $(TAG_FILES)
 
 tags:
        $(RM) tags
-       $(FIND) . -name '*.[hcS]' -print | xargs ctags -a
+       $(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs ctags -a $(TAG_FILES)
 
 cscope:
        $(RM) cscope*
-       $(FIND) . -name '*.[hcS]' -print | xargs cscope -b
+       $(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs cscope -b $(TAG_FILES)
 
 ### Detect prefix changes
 TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):\
index d30d2c2e2a7a30d79846afde2487656aa8cdd92f..1ec429fef2be9354d79400efe074e707229bef2f 100644 (file)
@@ -65,12 +65,13 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,
                return 0;
        }
 
-       he = __hists__add_entry(&evsel->hists, al, NULL, NULL, NULL, 1, 1, 0);
+       he = __hists__add_entry(&evsel->hists, al, NULL, NULL, NULL, 1, 1, 0,
+                               true);
        if (he == NULL)
                return -ENOMEM;
 
        ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
-       hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
+       hists__inc_nr_samples(&evsel->hists, true);
        return ret;
 }
 
index 8bff543acaab7a093ed3579b42b24bc15e8d14ab..9a5a035cb4262afcf1e74986276df8763f1036b9 100644 (file)
@@ -315,7 +315,7 @@ static int hists__add_entry(struct hists *hists,
                            u64 weight, u64 transaction)
 {
        if (__hists__add_entry(hists, al, NULL, NULL, NULL, period, weight,
-                              transaction) != NULL)
+                              transaction, true) != NULL)
                return 0;
        return -ENOMEM;
 }
index e4c85b8f46c29fd7526e0bea3ef419ba7884acd3..378b85b731a72fede65d41318107272649edfbe5 100644 (file)
@@ -454,7 +454,11 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
                        if (done)
                                break;
                        err = poll(rec->evlist->pollfd, rec->evlist->nr_fds, -1);
-                       if (err < 0 && errno == EINTR)
+                       /*
+                        * Propagate error, only if there's any. Ignore positive
+                        * number of returned events and interrupt error.
+                        */
+                       if (err > 0 || (err < 0 && errno == EINTR))
                                err = 0;
                        waking++;
                }
@@ -544,6 +548,7 @@ static const struct branch_mode branch_modes[] = {
        BRANCH_OPT("abort_tx", PERF_SAMPLE_BRANCH_ABORT_TX),
        BRANCH_OPT("in_tx", PERF_SAMPLE_BRANCH_IN_TX),
        BRANCH_OPT("no_tx", PERF_SAMPLE_BRANCH_NO_TX),
+       BRANCH_OPT("cond", PERF_SAMPLE_BRANCH_COND),
        BRANCH_END
 };
 
index bc0eec1ce4beaba37509f731c79f8e1855796c08..21d830bafff32aaa4b38ec0eb2e63ec66dfc00f8 100644 (file)
@@ -72,6 +72,10 @@ static int report__config(const char *var, const char *value, void *cb)
                rep->min_percent = strtof(value, NULL);
                return 0;
        }
+       if (!strcmp(var, "report.children")) {
+               symbol_conf.cumulate_callchain = perf_config_bool(var, value);
+               return 0;
+       }
 
        return perf_default_config(var, value, cb);
 }
@@ -85,156 +89,52 @@ static void report__inc_stats(struct report *rep, struct hist_entry *he)
         */
        if (he->stat.nr_events == 1)
                rep->nr_entries++;
-
-       /*
-        * Only counts number of samples at this stage as it's more
-        * natural to do it here and non-sample events are also
-        * counted in perf_session_deliver_event().  The dump_trace
-        * requires this info is ready before going to the output tree.
-        */
-       hists__inc_nr_events(he->hists, PERF_RECORD_SAMPLE);
-       if (!he->filtered)
-               he->hists->stats.nr_non_filtered_samples++;
 }
 
-static int report__add_mem_hist_entry(struct report *rep, struct addr_location *al,
-                                     struct perf_sample *sample, struct perf_evsel *evsel)
+static int hist_iter__report_callback(struct hist_entry_iter *iter,
+                                     struct addr_location *al, bool single,
+                                     void *arg)
 {
-       struct symbol *parent = NULL;
-       struct hist_entry *he;
-       struct mem_info *mi, *mx;
-       uint64_t cost;
-       int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack);
-
-       if (err)
-               return err;
+       int err = 0;
+       struct report *rep = arg;
+       struct hist_entry *he = iter->he;
+       struct perf_evsel *evsel = iter->evsel;
+       struct mem_info *mi;
+       struct branch_info *bi;
 
-       mi = sample__resolve_mem(sample, al);
-       if (!mi)
-               return -ENOMEM;
+       report__inc_stats(rep, he);
 
-       if (rep->hide_unresolved && !al->sym)
+       if (!ui__has_annotation())
                return 0;
 
-       cost = sample->weight;
-       if (!cost)
-               cost = 1;
-
-       /*
-        * must pass period=weight in order to get the correct
-        * sorting from hists__collapse_resort() which is solely
-        * based on periods. We want sorting be done on nr_events * weight
-        * and this is indirectly achieved by passing period=weight here
-        * and the he_stat__add_period() function.
-        */
-       he = __hists__add_entry(&evsel->hists, al, parent, NULL, mi,
-                               cost, cost, 0);
-       if (!he)
-               return -ENOMEM;
-
-       if (ui__has_annotation()) {
-               err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
-               if (err)
-                       goto out;
-
-               mx = he->mem_info;
-               err = addr_map_symbol__inc_samples(&mx->daddr, evsel->idx);
+       if (sort__mode == SORT_MODE__BRANCH) {
+               bi = he->branch_info;
+               err = addr_map_symbol__inc_samples(&bi->from, evsel->idx);
                if (err)
                        goto out;
-       }
-
-       report__inc_stats(rep, he);
-
-       err = hist_entry__append_callchain(he, sample);
-out:
-       return err;
-}
-
-static int report__add_branch_hist_entry(struct report *rep, struct addr_location *al,
-                                        struct perf_sample *sample, struct perf_evsel *evsel)
-{
-       struct symbol *parent = NULL;
-       unsigned i;
-       struct hist_entry *he;
-       struct branch_info *bi, *bx;
-       int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack);
 
-       if (err)
-               return err;
-
-       bi = sample__resolve_bstack(sample, al);
-       if (!bi)
-               return -ENOMEM;
-
-       for (i = 0; i < sample->branch_stack->nr; i++) {
-               if (rep->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym))
-                       continue;
+               err = addr_map_symbol__inc_samples(&bi->to, evsel->idx);
 
-               err = -ENOMEM;
-
-               /* overwrite the 'al' to branch-to info */
-               al->map = bi[i].to.map;
-               al->sym = bi[i].to.sym;
-               al->addr = bi[i].to.addr;
-               /*
-                * The report shows the percentage of total branches captured
-                * and not events sampled. Thus we use a pseudo period of 1.
-                */
-               he = __hists__add_entry(&evsel->hists, al, parent, &bi[i], NULL,
-                                       1, 1, 0);
-               if (he) {
-                       if (ui__has_annotation()) {
-                               bx = he->branch_info;
-                               err = addr_map_symbol__inc_samples(&bx->from,
-                                                                  evsel->idx);
-                               if (err)
-                                       goto out;
-
-                               err = addr_map_symbol__inc_samples(&bx->to,
-                                                                  evsel->idx);
-                               if (err)
-                                       goto out;
-                       }
-                       report__inc_stats(rep, he);
-               } else
+       } else if (rep->mem_mode) {
+               mi = he->mem_info;
+               err = addr_map_symbol__inc_samples(&mi->daddr, evsel->idx);
+               if (err)
                        goto out;
-       }
-       err = 0;
-out:
-       free(bi);
-       return err;
-}
-
-static int report__add_hist_entry(struct report *rep, struct perf_evsel *evsel,
-                                 struct addr_location *al, struct perf_sample *sample)
-{
-       struct symbol *parent = NULL;
-       struct hist_entry *he;
-       int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack);
-
-       if (err)
-               return err;
 
-       he = __hists__add_entry(&evsel->hists, al, parent, NULL, NULL,
-                               sample->period, sample->weight,
-                               sample->transaction);
-       if (he == NULL)
-               return -ENOMEM;
-
-       err = hist_entry__append_callchain(he, sample);
-       if (err)
-               goto out;
-
-       if (ui__has_annotation())
                err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
 
-       report__inc_stats(rep, he);
+       } else if (symbol_conf.cumulate_callchain) {
+               if (single)
+                       err = hist_entry__inc_addr_samples(he, evsel->idx,
+                                                          al->addr);
+       } else {
+               err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
+       }
 
 out:
        return err;
 }
 
-
 static int process_sample_event(struct perf_tool *tool,
                                union perf_event *event,
                                struct perf_sample *sample,
@@ -243,6 +143,10 @@ static int process_sample_event(struct perf_tool *tool,
 {
        struct report *rep = container_of(tool, struct report, tool);
        struct addr_location al;
+       struct hist_entry_iter iter = {
+               .hide_unresolved = rep->hide_unresolved,
+               .add_entry_cb = hist_iter__report_callback,
+       };
        int ret;
 
        if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
@@ -257,22 +161,23 @@ static int process_sample_event(struct perf_tool *tool,
        if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap))
                return 0;
 
-       if (sort__mode == SORT_MODE__BRANCH) {
-               ret = report__add_branch_hist_entry(rep, &al, sample, evsel);
-               if (ret < 0)
-                       pr_debug("problem adding lbr entry, skipping event\n");
-       } else if (rep->mem_mode == 1) {
-               ret = report__add_mem_hist_entry(rep, &al, sample, evsel);
-               if (ret < 0)
-                       pr_debug("problem adding mem entry, skipping event\n");
-       } else {
-               if (al.map != NULL)
-                       al.map->dso->hit = 1;
+       if (sort__mode == SORT_MODE__BRANCH)
+               iter.ops = &hist_iter_branch;
+       else if (rep->mem_mode)
+               iter.ops = &hist_iter_mem;
+       else if (symbol_conf.cumulate_callchain)
+               iter.ops = &hist_iter_cumulative;
+       else
+               iter.ops = &hist_iter_normal;
+
+       if (al.map != NULL)
+               al.map->dso->hit = 1;
+
+       ret = hist_entry_iter__add(&iter, &al, evsel, sample, rep->max_stack,
+                                  rep);
+       if (ret < 0)
+               pr_debug("problem adding hist entry, skipping event\n");
 
-               ret = report__add_hist_entry(rep, evsel, &al, sample);
-               if (ret < 0)
-                       pr_debug("problem incrementing symbol period, skipping event\n");
-       }
        return ret;
 }
 
@@ -329,6 +234,14 @@ static int report__setup_sample_type(struct report *rep)
                        }
        }
 
+       if (symbol_conf.cumulate_callchain) {
+               /* Silently ignore if callchain is missing */
+               if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) {
+                       symbol_conf.cumulate_callchain = false;
+                       perf_hpp__cancel_cumulate();
+               }
+       }
+
        if (sort__mode == SORT_MODE__BRANCH) {
                if (!is_pipe &&
                    !(sample_type & PERF_SAMPLE_BRANCH_STACK)) {
@@ -712,6 +625,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
        OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order",
                     "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). "
                     "Default: fractal,0.5,callee,function", &report_parse_callchain_opt, callchain_default_opt),
+       OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain,
+                   "Accumulate callchains of children and show total overhead as well"),
        OPT_INTEGER(0, "max-stack", &report.max_stack,
                    "Set the maximum stack depth when parsing the callchain, "
                    "anything beyond the specified depth will be ignored. "
@@ -804,8 +719,10 @@ repeat:
        has_br_stack = perf_header__has_feat(&session->header,
                                             HEADER_BRANCH_STACK);
 
-       if (branch_mode == -1 && has_br_stack)
+       if (branch_mode == -1 && has_br_stack) {
                sort__mode = SORT_MODE__BRANCH;
+               symbol_conf.cumulate_callchain = false;
+       }
 
        if (report.mem_mode) {
                if (sort__mode == SORT_MODE__BRANCH) {
@@ -813,6 +730,7 @@ repeat:
                        goto error;
                }
                sort__mode = SORT_MODE__MEMORY;
+               symbol_conf.cumulate_callchain = false;
        }
 
        if (setup_sorting() < 0) {
index d7176830b9b2fb789a61565acf113987ac59fd99..c38d06c047757281b03992522723af980b54412b 100644 (file)
@@ -1428,7 +1428,7 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_
        int err = 0;
 
        evsel->hists.stats.total_period += sample->period;
-       hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
+       hists__inc_nr_samples(&evsel->hists, true);
 
        if (evsel->handler != NULL) {
                tracepoint_handler f = evsel->handler;
index 5b389ce4cd15d03b656185f83d58f10aca94fd81..377971dc89a3b26ffcd25b9eb3e8c0c01338ea24 100644 (file)
@@ -196,6 +196,12 @@ static void perf_top__record_precise_ip(struct perf_top *top,
 
        pthread_mutex_unlock(&notes->lock);
 
+       /*
+        * This function is now called with he->hists->lock held.
+        * Release it before going to sleep.
+        */
+       pthread_mutex_unlock(&he->hists->lock);
+
        if (err == -ERANGE && !he->ms.map->erange_warned)
                ui__warn_map_erange(he->ms.map, sym, ip);
        else if (err == -ENOMEM) {
@@ -203,6 +209,8 @@ static void perf_top__record_precise_ip(struct perf_top *top,
                       sym->name);
                sleep(1);
        }
+
+       pthread_mutex_lock(&he->hists->lock);
 }
 
 static void perf_top__show_details(struct perf_top *top)
@@ -238,27 +246,6 @@ out_unlock:
        pthread_mutex_unlock(&notes->lock);
 }
 
-static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel,
-                                                    struct addr_location *al,
-                                                    struct perf_sample *sample)
-{
-       struct hist_entry *he;
-
-       pthread_mutex_lock(&evsel->hists.lock);
-       he = __hists__add_entry(&evsel->hists, al, NULL, NULL, NULL,
-                               sample->period, sample->weight,
-                               sample->transaction);
-       pthread_mutex_unlock(&evsel->hists.lock);
-       if (he == NULL)
-               return NULL;
-
-       hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
-       if (!he->filtered)
-               evsel->hists.stats.nr_non_filtered_samples++;
-
-       return he;
-}
-
 static void perf_top__print_sym_table(struct perf_top *top)
 {
        char bf[160];
@@ -662,6 +649,26 @@ static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym)
        return 0;
 }
 
+static int hist_iter__top_callback(struct hist_entry_iter *iter,
+                                  struct addr_location *al, bool single,
+                                  void *arg)
+{
+       struct perf_top *top = arg;
+       struct hist_entry *he = iter->he;
+       struct perf_evsel *evsel = iter->evsel;
+
+       if (sort__has_sym && single) {
+               u64 ip = al->addr;
+
+               if (al->map)
+                       ip = al->map->unmap_ip(al->map, ip);
+
+               perf_top__record_precise_ip(top, he, evsel->idx, ip);
+       }
+
+       return 0;
+}
+
 static void perf_event__process_sample(struct perf_tool *tool,
                                       const union perf_event *event,
                                       struct perf_evsel *evsel,
@@ -669,8 +676,6 @@ static void perf_event__process_sample(struct perf_tool *tool,
                                       struct machine *machine)
 {
        struct perf_top *top = container_of(tool, struct perf_top, tool);
-       struct symbol *parent = NULL;
-       u64 ip = sample->ip;
        struct addr_location al;
        int err;
 
@@ -745,25 +750,23 @@ static void perf_event__process_sample(struct perf_tool *tool,
        }
 
        if (al.sym == NULL || !al.sym->ignore) {
-               struct hist_entry *he;
+               struct hist_entry_iter iter = {
+                       .add_entry_cb = hist_iter__top_callback,
+               };
 
-               err = sample__resolve_callchain(sample, &parent, evsel, &al,
-                                               top->max_stack);
-               if (err)
-                       return;
+               if (symbol_conf.cumulate_callchain)
+                       iter.ops = &hist_iter_cumulative;
+               else
+                       iter.ops = &hist_iter_normal;
 
-               he = perf_evsel__add_hist_entry(evsel, &al, sample);
-               if (he == NULL) {
-                       pr_err("Problem incrementing symbol period, skipping event\n");
-                       return;
-               }
+               pthread_mutex_lock(&evsel->hists.lock);
 
-               err = hist_entry__append_callchain(he, sample);
-               if (err)
-                       return;
+               err = hist_entry_iter__add(&iter, &al, evsel, sample,
+                                          top->max_stack, top);
+               if (err < 0)
+                       pr_err("Problem incrementing symbol period, skipping event\n");
 
-               if (sort__has_sym)
-                       perf_top__record_precise_ip(top, he, evsel->idx, ip);
+               pthread_mutex_unlock(&evsel->hists.lock);
        }
 
        return;
@@ -1001,6 +1004,10 @@ static int perf_top_config(const char *var, const char *value, void *cb)
 
        if (!strcmp(var, "top.call-graph"))
                return record_parse_callchain(value, &top->record_opts);
+       if (!strcmp(var, "top.children")) {
+               symbol_conf.cumulate_callchain = perf_config_bool(var, value);
+               return 0;
+       }
 
        return perf_default_config(var, value, cb);
 }
@@ -1095,6 +1102,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
        OPT_CALLBACK(0, "call-graph", &top.record_opts,
                     "mode[,dump_size]", record_callchain_help,
                     &parse_callchain_opt),
+       OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain,
+                   "Accumulate callchains of children and show total overhead as well"),
        OPT_INTEGER(0, "max-stack", &top.max_stack,
                    "Set the maximum stack depth when parsing the callchain. "
                    "Default: " __stringify(PERF_MAX_STACK_DEPTH)),
@@ -1200,6 +1209,11 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
 
        top.sym_evsel = perf_evlist__first(top.evlist);
 
+       if (!symbol_conf.use_callchain) {
+               symbol_conf.cumulate_callchain = false;
+               perf_hpp__cancel_cumulate();
+       }
+
        symbol_conf.priv_size = sizeof(struct annotation);
 
        symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
index 729bbdf5cec7c663cda5d678c9960e471bb17c77..4f100b54ba8bf0a352c9db0ac2f9d7b6f719343d 100644 (file)
@@ -447,6 +447,7 @@ else
   ifneq ($(feature-libperl), 1)
     CFLAGS += -DNO_LIBPERL
     NO_LIBPERL := 1
+    msg := $(warning Missing perl devel files. Disabling perl scripting support, consider installing perl-ExtUtils-Embed);
   else
     LDFLAGS += $(PERL_EMBED_LDFLAGS)
     EXTLIBS += $(PERL_EMBED_LIBADD)
@@ -599,7 +600,7 @@ endif
 
 # Make the path relative to DESTDIR, not to prefix
 ifndef DESTDIR
-prefix = $(HOME)
+prefix ?= $(HOME)
 endif
 bindir_relative = bin
 bindir = $(prefix)/$(bindir_relative)
index 431798a4110d6a63a9e802ddeef2b2a701bea2a2..78f7b920e5483a1982dd4d164588b689e1b29e6d 100644 (file)
@@ -481,14 +481,18 @@ int main(int argc, const char **argv)
                fprintf(stderr, "cannot handle %s internally", cmd);
                goto out;
        }
-#ifdef HAVE_LIBAUDIT_SUPPORT
        if (!prefixcmp(cmd, "trace")) {
+#ifdef HAVE_LIBAUDIT_SUPPORT
                set_buildid_dir();
                setup_path();
                argv[0] = "trace";
                return cmd_trace(argc, argv, NULL);
-       }
+#else
+               fprintf(stderr,
+                       "trace command not available: missing audit-libs devel package at build time.\n");
+               goto out;
 #endif
+       }
        /* Look for flags.. */
        argv++;
        argc--;
index 831f52cae1972eb8d019808d505ca2e9dfd4a423..802e3cd50f6fb7ecb7100b2dc5ef577e48846918 100644 (file)
@@ -139,6 +139,10 @@ static struct test {
                .desc = "Test output sorting of hist entries",
                .func = test__hists_output,
        },
+       {
+               .desc = "Test cumulation of child hist entries",
+               .func = test__hists_cumulate,
+       },
        {
                .func = NULL,
        },
index e4e01aadc3be4c4b6f13b291006f4eef86625b8f..a62c091345163f70ea72ad2cb9dbc2297ae2da1a 100644 (file)
@@ -12,9 +12,9 @@ static struct {
        u32 pid;
        const char *comm;
 } fake_threads[] = {
-       { 100, "perf" },
-       { 200, "perf" },
-       { 300, "bash" },
+       { FAKE_PID_PERF1, "perf" },
+       { FAKE_PID_PERF2, "perf" },
+       { FAKE_PID_BASH,  "bash" },
 };
 
 static struct {
@@ -22,15 +22,15 @@ static struct {
        u64 start;
        const char *filename;
 } fake_mmap_info[] = {
-       { 100, 0x40000, "perf" },
-       { 100, 0x50000, "libc" },
-       { 100, 0xf0000, "[kernel]" },
-       { 200, 0x40000, "perf" },
-       { 200, 0x50000, "libc" },
-       { 200, 0xf0000, "[kernel]" },
-       { 300, 0x40000, "bash" },
-       { 300, 0x50000, "libc" },
-       { 300, 0xf0000, "[kernel]" },
+       { FAKE_PID_PERF1, FAKE_MAP_PERF,   "perf" },
+       { FAKE_PID_PERF1, FAKE_MAP_LIBC,   "libc" },
+       { FAKE_PID_PERF1, FAKE_MAP_KERNEL, "[kernel]" },
+       { FAKE_PID_PERF2, FAKE_MAP_PERF,   "perf" },
+       { FAKE_PID_PERF2, FAKE_MAP_LIBC,   "libc" },
+       { FAKE_PID_PERF2, FAKE_MAP_KERNEL, "[kernel]" },
+       { FAKE_PID_BASH,  FAKE_MAP_BASH,   "bash" },
+       { FAKE_PID_BASH,  FAKE_MAP_LIBC,   "libc" },
+       { FAKE_PID_BASH,  FAKE_MAP_KERNEL, "[kernel]" },
 };
 
 struct fake_sym {
@@ -40,27 +40,30 @@ struct fake_sym {
 };
 
 static struct fake_sym perf_syms[] = {
-       { 700, 100, "main" },
-       { 800, 100, "run_command" },
-       { 900, 100, "cmd_record" },
+       { FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "main" },
+       { FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "run_command" },
+       { FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "cmd_record" },
 };
 
 static struct fake_sym bash_syms[] = {
-       { 700, 100, "main" },
-       { 800, 100, "xmalloc" },
-       { 900, 100, "xfree" },
+       { FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "main" },
+       { FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "xmalloc" },
+       { FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "xfree" },
 };
 
 static struct fake_sym libc_syms[] = {
        { 700, 100, "malloc" },
        { 800, 100, "free" },
        { 900, 100, "realloc" },
+       { FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "malloc" },
+       { FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "free" },
+       { FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "realloc" },
 };
 
 static struct fake_sym kernel_syms[] = {
-       { 700, 100, "schedule" },
-       { 800, 100, "page_fault" },
-       { 900, 100, "sys_perf_event_open" },
+       { FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "schedule" },
+       { FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "page_fault" },
+       { FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "sys_perf_event_open" },
 };
 
 static struct {
@@ -102,7 +105,7 @@ struct machine *setup_fake_machine(struct machines *machines)
                                .pid = fake_mmap_info[i].pid,
                                .tid = fake_mmap_info[i].pid,
                                .start = fake_mmap_info[i].start,
-                               .len = 0x1000ULL,
+                               .len = FAKE_MAP_LENGTH,
                                .pgoff = 0ULL,
                        },
                };
@@ -193,10 +196,11 @@ void print_hists_out(struct hists *hists)
                he = rb_entry(node, struct hist_entry, rb_node);
 
                if (!he->filtered) {
-                       pr_info("%2d: entry: %8s:%5d [%-8s] %20s: period = %"PRIu64"\n",
+                       pr_info("%2d: entry: %8s:%5d [%-8s] %20s: period = %"PRIu64"/%"PRIu64"\n",
                                i, thread__comm_str(he->thread), he->thread->tid,
                                he->ms.map->dso->short_name,
-                               he->ms.sym->name, he->stat.period);
+                               he->ms.sym->name, he->stat.period,
+                               he->stat_acc ? he->stat_acc->period : 0);
                }
 
                i++;
index 1415ae69d7b6fa40235cce03a8e02874da39aa10..888254e8665c41ce0c904f28857047c69e816467 100644 (file)
@@ -4,6 +4,34 @@
 struct machine;
 struct machines;
 
+#define FAKE_PID_PERF1  100
+#define FAKE_PID_PERF2  200
+#define FAKE_PID_BASH   300
+
+#define FAKE_MAP_PERF    0x400000
+#define FAKE_MAP_BASH    0x400000
+#define FAKE_MAP_LIBC    0x500000
+#define FAKE_MAP_KERNEL  0xf00000
+#define FAKE_MAP_LENGTH  0x100000
+
+#define FAKE_SYM_OFFSET1  700
+#define FAKE_SYM_OFFSET2  800
+#define FAKE_SYM_OFFSET3  900
+#define FAKE_SYM_LENGTH   100
+
+#define FAKE_IP_PERF_MAIN  FAKE_MAP_PERF + FAKE_SYM_OFFSET1
+#define FAKE_IP_PERF_RUN_COMMAND  FAKE_MAP_PERF + FAKE_SYM_OFFSET2
+#define FAKE_IP_PERF_CMD_RECORD  FAKE_MAP_PERF + FAKE_SYM_OFFSET3
+#define FAKE_IP_BASH_MAIN  FAKE_MAP_BASH + FAKE_SYM_OFFSET1
+#define FAKE_IP_BASH_XMALLOC  FAKE_MAP_BASH + FAKE_SYM_OFFSET2
+#define FAKE_IP_BASH_XFREE  FAKE_MAP_BASH + FAKE_SYM_OFFSET3
+#define FAKE_IP_LIBC_MALLOC  FAKE_MAP_LIBC + FAKE_SYM_OFFSET1
+#define FAKE_IP_LIBC_FREE  FAKE_MAP_LIBC + FAKE_SYM_OFFSET2
+#define FAKE_IP_LIBC_REALLOC  FAKE_MAP_LIBC + FAKE_SYM_OFFSET3
+#define FAKE_IP_KERNEL_SCHEDULE  FAKE_MAP_KERNEL + FAKE_SYM_OFFSET1
+#define FAKE_IP_KERNEL_PAGE_FAULT  FAKE_MAP_KERNEL + FAKE_SYM_OFFSET2
+#define FAKE_IP_KERNEL_SYS_PERF_EVENT_OPEN  FAKE_MAP_KERNEL + FAKE_SYM_OFFSET3
+
 /*
  * The setup_fake_machine() provides a test environment which consists
  * of 3 processes that have 3 mappings and in turn, have 3 symbols
@@ -13,7 +41,7 @@ struct machines;
  * .............  .............  ...................
  *    perf:  100           perf  main
  *    perf:  100           perf  run_command
- *    perf:  100           perf  comd_record
+ *    perf:  100           perf  cmd_record
  *    perf:  100           libc  malloc
  *    perf:  100           libc  free
  *    perf:  100           libc  realloc
@@ -22,7 +50,7 @@ struct machines;
  *    perf:  100       [kernel]  sys_perf_event_open
  *    perf:  200           perf  main
  *    perf:  200           perf  run_command
- *    perf:  200           perf  comd_record
+ *    perf:  200           perf  cmd_record
  *    perf:  200           libc  malloc
  *    perf:  200           libc  free
  *    perf:  200           libc  realloc
diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c
new file mode 100644 (file)
index 0000000..0ac240d
--- /dev/null
@@ -0,0 +1,726 @@
+#include "perf.h"
+#include "util/debug.h"
+#include "util/symbol.h"
+#include "util/sort.h"
+#include "util/evsel.h"
+#include "util/evlist.h"
+#include "util/machine.h"
+#include "util/thread.h"
+#include "util/parse-events.h"
+#include "tests/tests.h"
+#include "tests/hists_common.h"
+
+struct sample {
+       u32 pid;
+       u64 ip;
+       struct thread *thread;
+       struct map *map;
+       struct symbol *sym;
+};
+
+/* For the numbers, see hists_common.c */
+static struct sample fake_samples[] = {
+       /* perf [kernel] schedule() */
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, },
+       /* perf [perf]   main() */
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, },
+       /* perf [perf]   cmd_record() */
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_CMD_RECORD, },
+       /* perf [libc]   malloc() */
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, },
+       /* perf [libc]   free() */
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_FREE, },
+       /* perf [perf]   main() */
+       { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, },
+       /* perf [kernel] page_fault() */
+       { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
+       /* bash [bash]   main() */
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_MAIN, },
+       /* bash [bash]   xmalloc() */
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_XMALLOC, },
+       /* bash [kernel] page_fault() */
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
+};
+
+/*
+ * Will be casted to struct ip_callchain which has all 64 bit entries
+ * of nr and ips[].
+ */
+static u64 fake_callchains[][10] = {
+       /*   schedule => run_command => main */
+       { 3, FAKE_IP_KERNEL_SCHEDULE, FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, },
+       /*   main  */
+       { 1, FAKE_IP_PERF_MAIN, },
+       /*   cmd_record => run_command => main */
+       { 3, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, },
+       /*   malloc => cmd_record => run_command => main */
+       { 4, FAKE_IP_LIBC_MALLOC, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND,
+            FAKE_IP_PERF_MAIN, },
+       /*   free => cmd_record => run_command => main */
+       { 4, FAKE_IP_LIBC_FREE, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND,
+            FAKE_IP_PERF_MAIN, },
+       /*   main */
+       { 1, FAKE_IP_PERF_MAIN, },
+       /*   page_fault => sys_perf_event_open => run_command => main */
+       { 4, FAKE_IP_KERNEL_PAGE_FAULT, FAKE_IP_KERNEL_SYS_PERF_EVENT_OPEN,
+            FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, },
+       /*   main */
+       { 1, FAKE_IP_BASH_MAIN, },
+       /*   xmalloc => malloc => xmalloc => malloc => xmalloc => main */
+       { 6, FAKE_IP_BASH_XMALLOC, FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_XMALLOC,
+            FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_XMALLOC, FAKE_IP_BASH_MAIN, },
+       /*   page_fault => malloc => main */
+       { 3, FAKE_IP_KERNEL_PAGE_FAULT, FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_MAIN, },
+};
+
+static int add_hist_entries(struct hists *hists, struct machine *machine)
+{
+       struct addr_location al;
+       struct perf_evsel *evsel = hists_to_evsel(hists);
+       struct perf_sample sample = { .period = 1000, };
+       size_t i;
+
+       for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
+               const union perf_event event = {
+                       .header = {
+                               .misc = PERF_RECORD_MISC_USER,
+                       },
+               };
+               struct hist_entry_iter iter = {
+                       .hide_unresolved = false,
+               };
+
+               if (symbol_conf.cumulate_callchain)
+                       iter.ops = &hist_iter_cumulative;
+               else
+                       iter.ops = &hist_iter_normal;
+
+               sample.pid = fake_samples[i].pid;
+               sample.tid = fake_samples[i].pid;
+               sample.ip = fake_samples[i].ip;
+               sample.callchain = (struct ip_callchain *)fake_callchains[i];
+
+               if (perf_event__preprocess_sample(&event, machine, &al,
+                                                 &sample) < 0)
+                       goto out;
+
+               if (hist_entry_iter__add(&iter, &al, evsel, &sample,
+                                        PERF_MAX_STACK_DEPTH, NULL) < 0)
+                       goto out;
+
+               fake_samples[i].thread = al.thread;
+               fake_samples[i].map = al.map;
+               fake_samples[i].sym = al.sym;
+       }
+
+       return TEST_OK;
+
+out:
+       pr_debug("Not enough memory for adding a hist entry\n");
+       return TEST_FAIL;
+}
+
+static void del_hist_entries(struct hists *hists)
+{
+       struct hist_entry *he;
+       struct rb_root *root_in;
+       struct rb_root *root_out;
+       struct rb_node *node;
+
+       if (sort__need_collapse)
+               root_in = &hists->entries_collapsed;
+       else
+               root_in = hists->entries_in;
+
+       root_out = &hists->entries;
+
+       while (!RB_EMPTY_ROOT(root_out)) {
+               node = rb_first(root_out);
+
+               he = rb_entry(node, struct hist_entry, rb_node);
+               rb_erase(node, root_out);
+               rb_erase(&he->rb_node_in, root_in);
+               hist_entry__free(he);
+       }
+}
+
+typedef int (*test_fn_t)(struct perf_evsel *, struct machine *);
+
+#define COMM(he)  (thread__comm_str(he->thread))
+#define DSO(he)   (he->ms.map->dso->short_name)
+#define SYM(he)   (he->ms.sym->name)
+#define CPU(he)   (he->cpu)
+#define PID(he)   (he->thread->tid)
+#define DEPTH(he) (he->callchain->max_depth)
+#define CDSO(cl)  (cl->ms.map->dso->short_name)
+#define CSYM(cl)  (cl->ms.sym->name)
+
+struct result {
+       u64 children;
+       u64 self;
+       const char *comm;
+       const char *dso;
+       const char *sym;
+};
+
+struct callchain_result {
+       u64 nr;
+       struct {
+               const char *dso;
+               const char *sym;
+       } node[10];
+};
+
+static int do_test(struct hists *hists, struct result *expected, size_t nr_expected,
+                  struct callchain_result *expected_callchain, size_t nr_callchain)
+{
+       char buf[32];
+       size_t i, c;
+       struct hist_entry *he;
+       struct rb_root *root;
+       struct rb_node *node;
+       struct callchain_node *cnode;
+       struct callchain_list *clist;
+
+       /*
+        * adding and deleting hist entries must be done outside of this
+        * function since TEST_ASSERT_VAL() returns in case of failure.
+        */
+       hists__collapse_resort(hists, NULL);
+       hists__output_resort(hists);
+
+       if (verbose > 2) {
+               pr_info("use callchain: %d, cumulate callchain: %d\n",
+                       symbol_conf.use_callchain,
+                       symbol_conf.cumulate_callchain);
+               print_hists_out(hists);
+       }
+
+       root = &hists->entries;
+       for (node = rb_first(root), i = 0;
+            node && (he = rb_entry(node, struct hist_entry, rb_node));
+            node = rb_next(node), i++) {
+               scnprintf(buf, sizeof(buf), "Invalid hist entry #%zd", i);
+
+               TEST_ASSERT_VAL("Incorrect number of hist entry",
+                               i < nr_expected);
+               TEST_ASSERT_VAL(buf, he->stat.period == expected[i].self &&
+                               !strcmp(COMM(he), expected[i].comm) &&
+                               !strcmp(DSO(he), expected[i].dso) &&
+                               !strcmp(SYM(he), expected[i].sym));
+
+               if (symbol_conf.cumulate_callchain)
+                       TEST_ASSERT_VAL(buf, he->stat_acc->period == expected[i].children);
+
+               if (!symbol_conf.use_callchain)
+                       continue;
+
+               /* check callchain entries */
+               root = &he->callchain->node.rb_root;
+               cnode = rb_entry(rb_first(root), struct callchain_node, rb_node);
+
+               c = 0;
+               list_for_each_entry(clist, &cnode->val, list) {
+                       scnprintf(buf, sizeof(buf), "Invalid callchain entry #%zd/%zd", i, c);
+
+                       TEST_ASSERT_VAL("Incorrect number of callchain entry",
+                                       c < expected_callchain[i].nr);
+                       TEST_ASSERT_VAL(buf,
+                               !strcmp(CDSO(clist), expected_callchain[i].node[c].dso) &&
+                               !strcmp(CSYM(clist), expected_callchain[i].node[c].sym));
+                       c++;
+               }
+               /* TODO: handle multiple child nodes properly */
+               TEST_ASSERT_VAL("Incorrect number of callchain entry",
+                               c <= expected_callchain[i].nr);
+       }
+       TEST_ASSERT_VAL("Incorrect number of hist entry",
+                       i == nr_expected);
+       TEST_ASSERT_VAL("Incorrect number of callchain entry",
+                       !symbol_conf.use_callchain || nr_expected == nr_callchain);
+       return 0;
+}
+
+/* NO callchain + NO children */
+static int test1(struct perf_evsel *evsel, struct machine *machine)
+{
+       int err;
+       struct hists *hists = &evsel->hists;
+       /*
+        * expected output:
+        *
+        * Overhead  Command  Shared Object          Symbol
+        * ========  =======  =============  ==============
+        *   20.00%     perf  perf           [.] main
+        *   10.00%     bash  [kernel]       [k] page_fault
+        *   10.00%     bash  bash           [.] main
+        *   10.00%     bash  bash           [.] xmalloc
+        *   10.00%     perf  [kernel]       [k] page_fault
+        *   10.00%     perf  [kernel]       [k] schedule
+        *   10.00%     perf  libc           [.] free
+        *   10.00%     perf  libc           [.] malloc
+        *   10.00%     perf  perf           [.] cmd_record
+        */
+       struct result expected[] = {
+               { 0, 2000, "perf", "perf",     "main" },
+               { 0, 1000, "bash", "[kernel]", "page_fault" },
+               { 0, 1000, "bash", "bash",     "main" },
+               { 0, 1000, "bash", "bash",     "xmalloc" },
+               { 0, 1000, "perf", "[kernel]", "page_fault" },
+               { 0, 1000, "perf", "[kernel]", "schedule" },
+               { 0, 1000, "perf", "libc",     "free" },
+               { 0, 1000, "perf", "libc",     "malloc" },
+               { 0, 1000, "perf", "perf",     "cmd_record" },
+       };
+
+       symbol_conf.use_callchain = false;
+       symbol_conf.cumulate_callchain = false;
+
+       setup_sorting();
+       callchain_register_param(&callchain_param);
+
+       err = add_hist_entries(hists, machine);
+       if (err < 0)
+               goto out;
+
+       err = do_test(hists, expected, ARRAY_SIZE(expected), NULL, 0);
+
+out:
+       del_hist_entries(hists);
+       reset_output_field();
+       return err;
+}
+
+/* callcain + NO children */
+static int test2(struct perf_evsel *evsel, struct machine *machine)
+{
+       int err;
+       struct hists *hists = &evsel->hists;
+       /*
+        * expected output:
+        *
+        * Overhead  Command  Shared Object          Symbol
+        * ========  =======  =============  ==============
+        *   20.00%     perf  perf           [.] main
+        *              |
+        *              --- main
+        *
+        *   10.00%     bash  [kernel]       [k] page_fault
+        *              |
+        *              --- page_fault
+        *                  malloc
+        *                  main
+        *
+        *   10.00%     bash  bash           [.] main
+        *              |
+        *              --- main
+        *
+        *   10.00%     bash  bash           [.] xmalloc
+        *              |
+        *              --- xmalloc
+        *                  malloc
+        *                  xmalloc     <--- NOTE: there's a cycle
+        *                  malloc
+        *                  xmalloc
+        *                  main
+        *
+        *   10.00%     perf  [kernel]       [k] page_fault
+        *              |
+        *              --- page_fault
+        *                  sys_perf_event_open
+        *                  run_command
+        *                  main
+        *
+        *   10.00%     perf  [kernel]       [k] schedule
+        *              |
+        *              --- schedule
+        *                  run_command
+        *                  main
+        *
+        *   10.00%     perf  libc           [.] free
+        *              |
+        *              --- free
+        *                  cmd_record
+        *                  run_command
+        *                  main
+        *
+        *   10.00%     perf  libc           [.] malloc
+        *              |
+        *              --- malloc
+        *                  cmd_record
+        *                  run_command
+        *                  main
+        *
+        *   10.00%     perf  perf           [.] cmd_record
+        *              |
+        *              --- cmd_record
+        *                  run_command
+        *                  main
+        *
+        */
+       struct result expected[] = {
+               { 0, 2000, "perf", "perf",     "main" },
+               { 0, 1000, "bash", "[kernel]", "page_fault" },
+               { 0, 1000, "bash", "bash",     "main" },
+               { 0, 1000, "bash", "bash",     "xmalloc" },
+               { 0, 1000, "perf", "[kernel]", "page_fault" },
+               { 0, 1000, "perf", "[kernel]", "schedule" },
+               { 0, 1000, "perf", "libc",     "free" },
+               { 0, 1000, "perf", "libc",     "malloc" },
+               { 0, 1000, "perf", "perf",     "cmd_record" },
+       };
+       struct callchain_result expected_callchain[] = {
+               {
+                       1, {    { "perf",     "main" }, },
+               },
+               {
+                       3, {    { "[kernel]", "page_fault" },
+                               { "libc",     "malloc" },
+                               { "bash",     "main" }, },
+               },
+               {
+                       1, {    { "bash",     "main" }, },
+               },
+               {
+                       6, {    { "bash",     "xmalloc" },
+                               { "libc",     "malloc" },
+                               { "bash",     "xmalloc" },
+                               { "libc",     "malloc" },
+                               { "bash",     "xmalloc" },
+                               { "bash",     "main" }, },
+               },
+               {
+                       4, {    { "[kernel]", "page_fault" },
+                               { "[kernel]", "sys_perf_event_open" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       3, {    { "[kernel]", "schedule" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       4, {    { "libc",     "free" },
+                               { "perf",     "cmd_record" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       4, {    { "libc",     "malloc" },
+                               { "perf",     "cmd_record" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       3, {    { "perf",     "cmd_record" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+       };
+
+       symbol_conf.use_callchain = true;
+       symbol_conf.cumulate_callchain = false;
+
+       setup_sorting();
+       callchain_register_param(&callchain_param);
+
+       err = add_hist_entries(hists, machine);
+       if (err < 0)
+               goto out;
+
+       err = do_test(hists, expected, ARRAY_SIZE(expected),
+                     expected_callchain, ARRAY_SIZE(expected_callchain));
+
+out:
+       del_hist_entries(hists);
+       reset_output_field();
+       return err;
+}
+
+/* NO callchain + children */
+static int test3(struct perf_evsel *evsel, struct machine *machine)
+{
+       int err;
+       struct hists *hists = &evsel->hists;
+       /*
+        * expected output:
+        *
+        * Children      Self  Command  Shared Object                   Symbol
+        * ========  ========  =======  =============  =======================
+        *   70.00%    20.00%     perf  perf           [.] main
+        *   50.00%     0.00%     perf  perf           [.] run_command
+        *   30.00%    10.00%     bash  bash           [.] main
+        *   30.00%    10.00%     perf  perf           [.] cmd_record
+        *   20.00%     0.00%     bash  libc           [.] malloc
+        *   10.00%    10.00%     bash  [kernel]       [k] page_fault
+        *   10.00%    10.00%     perf  [kernel]       [k] schedule
+        *   10.00%     0.00%     perf  [kernel]       [k] sys_perf_event_open
+        *   10.00%    10.00%     perf  [kernel]       [k] page_fault
+        *   10.00%    10.00%     perf  libc           [.] free
+        *   10.00%    10.00%     perf  libc           [.] malloc
+        *   10.00%    10.00%     bash  bash           [.] xmalloc
+        */
+       struct result expected[] = {
+               { 7000, 2000, "perf", "perf",     "main" },
+               { 5000,    0, "perf", "perf",     "run_command" },
+               { 3000, 1000, "bash", "bash",     "main" },
+               { 3000, 1000, "perf", "perf",     "cmd_record" },
+               { 2000,    0, "bash", "libc",     "malloc" },
+               { 1000, 1000, "bash", "[kernel]", "page_fault" },
+               { 1000, 1000, "perf", "[kernel]", "schedule" },
+               { 1000,    0, "perf", "[kernel]", "sys_perf_event_open" },
+               { 1000, 1000, "perf", "[kernel]", "page_fault" },
+               { 1000, 1000, "perf", "libc",     "free" },
+               { 1000, 1000, "perf", "libc",     "malloc" },
+               { 1000, 1000, "bash", "bash",     "xmalloc" },
+       };
+
+       symbol_conf.use_callchain = false;
+       symbol_conf.cumulate_callchain = true;
+
+       setup_sorting();
+       callchain_register_param(&callchain_param);
+
+       err = add_hist_entries(hists, machine);
+       if (err < 0)
+               goto out;
+
+       err = do_test(hists, expected, ARRAY_SIZE(expected), NULL, 0);
+
+out:
+       del_hist_entries(hists);
+       reset_output_field();
+       return err;
+}
+
+/* callchain + children */
+static int test4(struct perf_evsel *evsel, struct machine *machine)
+{
+       int err;
+       struct hists *hists = &evsel->hists;
+       /*
+        * expected output:
+        *
+        * Children      Self  Command  Shared Object                   Symbol
+        * ========  ========  =======  =============  =======================
+        *   70.00%    20.00%     perf  perf           [.] main
+        *              |
+        *              --- main
+        *
+        *   50.00%     0.00%     perf  perf           [.] run_command
+        *              |
+        *              --- run_command
+        *                  main
+        *
+        *   30.00%    10.00%     bash  bash           [.] main
+        *              |
+        *              --- main
+        *
+        *   30.00%    10.00%     perf  perf           [.] cmd_record
+        *              |
+        *              --- cmd_record
+        *                  run_command
+        *                  main
+        *
+        *   20.00%     0.00%     bash  libc           [.] malloc
+        *              |
+        *              --- malloc
+        *                 |
+        *                 |--50.00%-- xmalloc
+        *                 |           main
+        *                  --50.00%-- main
+        *
+        *   10.00%    10.00%     bash  [kernel]       [k] page_fault
+        *              |
+        *              --- page_fault
+        *                  malloc
+        *                  main
+        *
+        *   10.00%    10.00%     perf  [kernel]       [k] schedule
+        *              |
+        *              --- schedule
+        *                  run_command
+        *                  main
+        *
+        *   10.00%     0.00%     perf  [kernel]       [k] sys_perf_event_open
+        *              |
+        *              --- sys_perf_event_open
+        *                  run_command
+        *                  main
+        *
+        *   10.00%    10.00%     perf  [kernel]       [k] page_fault
+        *              |
+        *              --- page_fault
+        *                  sys_perf_event_open
+        *                  run_command
+        *                  main
+        *
+        *   10.00%    10.00%     perf  libc           [.] free
+        *              |
+        *              --- free
+        *                  cmd_record
+        *                  run_command
+        *                  main
+        *
+        *   10.00%    10.00%     perf  libc           [.] malloc
+        *              |
+        *              --- malloc
+        *                  cmd_record
+        *                  run_command
+        *                  main
+        *
+        *   10.00%    10.00%     bash  bash           [.] xmalloc
+        *              |
+        *              --- xmalloc
+        *                  malloc
+        *                  xmalloc     <--- NOTE: there's a cycle
+        *                  malloc
+        *                  xmalloc
+        *                  main
+        *
+        */
+       struct result expected[] = {
+               { 7000, 2000, "perf", "perf",     "main" },
+               { 5000,    0, "perf", "perf",     "run_command" },
+               { 3000, 1000, "bash", "bash",     "main" },
+               { 3000, 1000, "perf", "perf",     "cmd_record" },
+               { 2000,    0, "bash", "libc",     "malloc" },
+               { 1000, 1000, "bash", "[kernel]", "page_fault" },
+               { 1000, 1000, "perf", "[kernel]", "schedule" },
+               { 1000,    0, "perf", "[kernel]", "sys_perf_event_open" },
+               { 1000, 1000, "perf", "[kernel]", "page_fault" },
+               { 1000, 1000, "perf", "libc",     "free" },
+               { 1000, 1000, "perf", "libc",     "malloc" },
+               { 1000, 1000, "bash", "bash",     "xmalloc" },
+       };
+       struct callchain_result expected_callchain[] = {
+               {
+                       1, {    { "perf",     "main" }, },
+               },
+               {
+                       2, {    { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       1, {    { "bash",     "main" }, },
+               },
+               {
+                       3, {    { "perf",     "cmd_record" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       4, {    { "libc",     "malloc" },
+                               { "bash",     "xmalloc" },
+                               { "bash",     "main" },
+                               { "bash",     "main" }, },
+               },
+               {
+                       3, {    { "[kernel]", "page_fault" },
+                               { "libc",     "malloc" },
+                               { "bash",     "main" }, },
+               },
+               {
+                       3, {    { "[kernel]", "schedule" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       3, {    { "[kernel]", "sys_perf_event_open" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       4, {    { "[kernel]", "page_fault" },
+                               { "[kernel]", "sys_perf_event_open" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       4, {    { "libc",     "free" },
+                               { "perf",     "cmd_record" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       4, {    { "libc",     "malloc" },
+                               { "perf",     "cmd_record" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       6, {    { "bash",     "xmalloc" },
+                               { "libc",     "malloc" },
+                               { "bash",     "xmalloc" },
+                               { "libc",     "malloc" },
+                               { "bash",     "xmalloc" },
+                               { "bash",     "main" }, },
+               },
+       };
+
+       symbol_conf.use_callchain = true;
+       symbol_conf.cumulate_callchain = true;
+
+       setup_sorting();
+       callchain_register_param(&callchain_param);
+
+       err = add_hist_entries(hists, machine);
+       if (err < 0)
+               goto out;
+
+       err = do_test(hists, expected, ARRAY_SIZE(expected),
+                     expected_callchain, ARRAY_SIZE(expected_callchain));
+
+out:
+       del_hist_entries(hists);
+       reset_output_field();
+       return err;
+}
+
+int test__hists_cumulate(void)
+{
+       int err = TEST_FAIL;
+       struct machines machines;
+       struct machine *machine;
+       struct perf_evsel *evsel;
+       struct perf_evlist *evlist = perf_evlist__new();
+       size_t i;
+       test_fn_t testcases[] = {
+               test1,
+               test2,
+               test3,
+               test4,
+       };
+
+       TEST_ASSERT_VAL("No memory", evlist);
+
+       err = parse_events(evlist, "cpu-clock");
+       if (err)
+               goto out;
+
+       machines__init(&machines);
+
+       /* setup threads/dso/map/symbols also */
+       machine = setup_fake_machine(&machines);
+       if (!machine)
+               goto out;
+
+       if (verbose > 1)
+               machine__fprintf(machine, stderr);
+
+       evsel = perf_evlist__first(evlist);
+
+       for (i = 0; i < ARRAY_SIZE(testcases); i++) {
+               err = testcases[i](evsel, machine);
+               if (err < 0)
+                       break;
+       }
+
+out:
+       /* tear down everything */
+       perf_evlist__delete(evlist);
+       machines__exit(&machines);
+
+       return err;
+}
index c5ba924a3581cbda11e86b4627025e7f1a53a227..821f581fd9303623ed5cf5730030381ccce66263 100644 (file)
@@ -21,33 +21,33 @@ struct sample {
 /* For the numbers, see hists_common.c */
 static struct sample fake_samples[] = {
        /* perf [kernel] schedule() */
-       { .pid = 100, .ip = 0xf0000 + 700, },
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, },
        /* perf [perf]   main() */
-       { .pid = 100, .ip = 0x40000 + 700, },
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, },
        /* perf [libc]   malloc() */
-       { .pid = 100, .ip = 0x50000 + 700, },
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, },
        /* perf [perf]   main() */
-       { .pid = 200, .ip = 0x40000 + 700, }, /* will be merged */
+       { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, }, /* will be merged */
        /* perf [perf]   cmd_record() */
-       { .pid = 200, .ip = 0x40000 + 900, },
+       { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_CMD_RECORD, },
        /* perf [kernel] page_fault() */
-       { .pid = 200, .ip = 0xf0000 + 800, },
+       { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
        /* bash [bash]   main() */
-       { .pid = 300, .ip = 0x40000 + 700, },
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_MAIN, },
        /* bash [bash]   xmalloc() */
-       { .pid = 300, .ip = 0x40000 + 800, },
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_XMALLOC, },
        /* bash [libc]   malloc() */
-       { .pid = 300, .ip = 0x50000 + 700, },
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_LIBC_MALLOC, },
        /* bash [kernel] page_fault() */
-       { .pid = 300, .ip = 0xf0000 + 800, },
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
 };
 
-static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
+static int add_hist_entries(struct perf_evlist *evlist,
+                           struct machine *machine __maybe_unused)
 {
        struct perf_evsel *evsel;
        struct addr_location al;
-       struct hist_entry *he;
-       struct perf_sample sample = { .cpu = 0, };
+       struct perf_sample sample = { .period = 100, };
        size_t i;
 
        /*
@@ -62,6 +62,10 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
                                        .misc = PERF_RECORD_MISC_USER,
                                },
                        };
+                       struct hist_entry_iter iter = {
+                               .ops = &hist_iter_normal,
+                               .hide_unresolved = false,
+                       };
 
                        /* make sure it has no filter at first */
                        evsel->hists.thread_filter = NULL;
@@ -76,18 +80,13 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
                                                          &sample) < 0)
                                goto out;
 
-                       he = __hists__add_entry(&evsel->hists, &al, NULL,
-                                               NULL, NULL, 100, 1, 0);
-                       if (he == NULL)
+                       if (hist_entry_iter__add(&iter, &al, evsel, &sample,
+                                                PERF_MAX_STACK_DEPTH, NULL) < 0)
                                goto out;
 
                        fake_samples[i].thread = al.thread;
                        fake_samples[i].map = al.map;
                        fake_samples[i].sym = al.sym;
-
-                       hists__inc_nr_events(he->hists, PERF_RECORD_SAMPLE);
-                       if (!he->filtered)
-                               he->hists->stats.nr_non_filtered_samples++;
                }
        }
 
index 5ffa2c3eb77d69b68f4d01b96375775613718c6a..d4b34b0f50a228afe694813359b0ca33fef9e83f 100644 (file)
@@ -21,41 +21,41 @@ struct sample {
 /* For the numbers, see hists_common.c */
 static struct sample fake_common_samples[] = {
        /* perf [kernel] schedule() */
-       { .pid = 100, .ip = 0xf0000 + 700, },
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, },
        /* perf [perf]   main() */
-       { .pid = 200, .ip = 0x40000 + 700, },
+       { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, },
        /* perf [perf]   cmd_record() */
-       { .pid = 200, .ip = 0x40000 + 900, },
+       { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_CMD_RECORD, },
        /* bash [bash]   xmalloc() */
-       { .pid = 300, .ip = 0x40000 + 800, },
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_XMALLOC, },
        /* bash [libc]   malloc() */
-       { .pid = 300, .ip = 0x50000 + 700, },
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_LIBC_MALLOC, },
 };
 
 static struct sample fake_samples[][5] = {
        {
                /* perf [perf]   run_command() */
-               { .pid = 100, .ip = 0x40000 + 800, },
+               { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_RUN_COMMAND, },
                /* perf [libc]   malloc() */
-               { .pid = 100, .ip = 0x50000 + 700, },
+               { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, },
                /* perf [kernel] page_fault() */
-               { .pid = 100, .ip = 0xf0000 + 800, },
+               { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
                /* perf [kernel] sys_perf_event_open() */
-               { .pid = 200, .ip = 0xf0000 + 900, },
+               { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_SYS_PERF_EVENT_OPEN, },
                /* bash [libc]   free() */
-               { .pid = 300, .ip = 0x50000 + 800, },
+               { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_LIBC_FREE, },
        },
        {
                /* perf [libc]   free() */
-               { .pid = 200, .ip = 0x50000 + 800, },
+               { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_LIBC_FREE, },
                /* bash [libc]   malloc() */
-               { .pid = 300, .ip = 0x50000 + 700, }, /* will be merged */
+               { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_LIBC_MALLOC, }, /* will be merged */
                /* bash [bash]   xfee() */
-               { .pid = 300, .ip = 0x40000 + 900, },
+               { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_XFREE, },
                /* bash [libc]   realloc() */
-               { .pid = 300, .ip = 0x50000 + 900, },
+               { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_LIBC_REALLOC, },
                /* bash [kernel] page_fault() */
-               { .pid = 300, .ip = 0xf0000 + 800, },
+               { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
        },
 };
 
@@ -64,7 +64,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
        struct perf_evsel *evsel;
        struct addr_location al;
        struct hist_entry *he;
-       struct perf_sample sample = { .cpu = 0, };
+       struct perf_sample sample = { .period = 1, };
        size_t i = 0, k;
 
        /*
@@ -88,7 +88,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
                                goto out;
 
                        he = __hists__add_entry(&evsel->hists, &al, NULL,
-                                               NULL, NULL, 1, 1, 0);
+                                               NULL, NULL, 1, 1, 0, true);
                        if (he == NULL)
                                goto out;
 
@@ -112,7 +112,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
                                goto out;
 
                        he = __hists__add_entry(&evsel->hists, &al, NULL,
-                                               NULL, NULL, 1, 1, 0);
+                                               NULL, NULL, 1, 1, 0, true);
                        if (he == NULL)
                                goto out;
 
index a16850551797ea13fbf5d0dfc9cb93a136c5ec7d..e3bbd6c54c1b023f7f7168ab8ed77dae33c8c8d3 100644 (file)
@@ -22,31 +22,31 @@ struct sample {
 /* For the numbers, see hists_common.c */
 static struct sample fake_samples[] = {
        /* perf [kernel] schedule() */
-       { .cpu = 0, .pid = 100, .ip = 0xf0000 + 700, },
+       { .cpu = 0, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, },
        /* perf [perf]   main() */
-       { .cpu = 1, .pid = 100, .ip = 0x40000 + 700, },
+       { .cpu = 1, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, },
        /* perf [perf]   cmd_record() */
-       { .cpu = 1, .pid = 100, .ip = 0x40000 + 900, },
+       { .cpu = 1, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_CMD_RECORD, },
        /* perf [libc]   malloc() */
-       { .cpu = 1, .pid = 100, .ip = 0x50000 + 700, },
+       { .cpu = 1, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, },
        /* perf [libc]   free() */
-       { .cpu = 2, .pid = 100, .ip = 0x50000 + 800, },
+       { .cpu = 2, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_FREE, },
        /* perf [perf]   main() */
-       { .cpu = 2, .pid = 200, .ip = 0x40000 + 700, },
+       { .cpu = 2, .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, },
        /* perf [kernel] page_fault() */
-       { .cpu = 2, .pid = 200, .ip = 0xf0000 + 800, },
+       { .cpu = 2, .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
        /* bash [bash]   main() */
-       { .cpu = 3, .pid = 300, .ip = 0x40000 + 700, },
+       { .cpu = 3, .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_MAIN, },
        /* bash [bash]   xmalloc() */
-       { .cpu = 0, .pid = 300, .ip = 0x40000 + 800, },
+       { .cpu = 0, .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_XMALLOC, },
        /* bash [kernel] page_fault() */
-       { .cpu = 1, .pid = 300, .ip = 0xf0000 + 800, },
+       { .cpu = 1, .pid = FAKE_PID_BASH,  .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
 };
 
 static int add_hist_entries(struct hists *hists, struct machine *machine)
 {
        struct addr_location al;
-       struct hist_entry *he;
+       struct perf_evsel *evsel = hists_to_evsel(hists);
        struct perf_sample sample = { .period = 100, };
        size_t i;
 
@@ -56,6 +56,10 @@ static int add_hist_entries(struct hists *hists, struct machine *machine)
                                .misc = PERF_RECORD_MISC_USER,
                        },
                };
+               struct hist_entry_iter iter = {
+                       .ops = &hist_iter_normal,
+                       .hide_unresolved = false,
+               };
 
                sample.cpu = fake_samples[i].cpu;
                sample.pid = fake_samples[i].pid;
@@ -66,9 +70,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine)
                                                  &sample) < 0)
                        goto out;
 
-               he = __hists__add_entry(hists, &al, NULL, NULL, NULL,
-                                       sample.period, 1, 0);
-               if (he == NULL)
+               if (hist_entry_iter__add(&iter, &al, evsel, &sample,
+                                        PERF_MAX_STACK_DEPTH, NULL) < 0)
                        goto out;
 
                fake_samples[i].thread = al.thread;
index d76c0e2e6635024afd2c31b2f20cb8179268175b..022bb68fd9c75350e35b48e26523563595eb66d8 100644 (file)
@@ -45,6 +45,7 @@ int test__hists_filter(void);
 int test__mmap_thread_lookup(void);
 int test__thread_mg_share(void);
 int test__hists_output(void);
+int test__hists_cumulate(void);
 
 #if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
 #ifdef HAVE_DWARF_UNWIND_SUPPORT
index d11541d4d7d7ffcddad8d093b2e79f697967292a..3ccf6e14f89bfe425fa9467c02f028e932642b72 100644 (file)
@@ -194,7 +194,7 @@ int ui_browser__warning(struct ui_browser *browser, int timeout,
                ui_helpline__vpush(format, args);
                va_end(args);
        } else {
-               while ((key == ui__question_window("Warning!", text,
+               while ((key = ui__question_window("Warning!", text,
                                                   "Press any key...",
                                                   timeout)) == K_RESIZE)
                        ui_browser__handle_resize(browser);
index 1c331b934ffc50747c1b6df0ba6658e81fe8971d..52c03fbbba1774b7eb8b4a4695dcf90b439a59e9 100644 (file)
@@ -37,7 +37,6 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size,
 static void hist_browser__update_nr_entries(struct hist_browser *hb);
 
 static struct rb_node *hists__filter_entries(struct rb_node *nd,
-                                            struct hists *hists,
                                             float min_pcnt);
 
 static bool hist_browser__has_filter(struct hist_browser *hb)
@@ -319,7 +318,7 @@ __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
        struct hists *hists = browser->hists;
 
        for (nd = rb_first(&hists->entries);
-            (nd = hists__filter_entries(nd, hists, browser->min_pcnt)) != NULL;
+            (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
             nd = rb_next(nd)) {
                struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
                hist_entry__set_folding(he, unfold);
@@ -651,13 +650,36 @@ hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
                          __hpp__slsmg_color_printf, true);             \
 }
 
+#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)                      \
+static u64 __hpp_get_acc_##_field(struct hist_entry *he)               \
+{                                                                      \
+       return he->stat_acc->_field;                                    \
+}                                                                      \
+                                                                       \
+static int                                                             \
+hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
+                               struct perf_hpp *hpp,                   \
+                               struct hist_entry *he)                  \
+{                                                                      \
+       if (!symbol_conf.cumulate_callchain) {                          \
+               int ret = scnprintf(hpp->buf, hpp->size, "%8s", "N/A"); \
+               slsmg_printf("%s", hpp->buf);                           \
+                                                                       \
+               return ret;                                             \
+       }                                                               \
+       return __hpp__fmt(hpp, he, __hpp_get_acc_##_field, " %6.2f%%",  \
+                         __hpp__slsmg_color_printf, true);             \
+}
+
 __HPP_COLOR_PERCENT_FN(overhead, period)
 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
+__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
 
 #undef __HPP_COLOR_PERCENT_FN
+#undef __HPP_COLOR_ACC_PERCENT_FN
 
 void hist_browser__init_hpp(void)
 {
@@ -671,6 +693,8 @@ void hist_browser__init_hpp(void)
                                hist_browser__hpp_color_overhead_guest_sys;
        perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
                                hist_browser__hpp_color_overhead_guest_us;
+       perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
+                               hist_browser__hpp_color_overhead_acc;
 }
 
 static int hist_browser__show_entry(struct hist_browser *browser,
@@ -783,15 +807,12 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)
 
        for (nd = browser->top; nd; nd = rb_next(nd)) {
                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
-               u64 total = hists__total_period(h->hists);
-               float percent = 0.0;
+               float percent;
 
                if (h->filtered)
                        continue;
 
-               if (total)
-                       percent = h->stat.period * 100.0 / total;
-
+               percent = hist_entry__get_percent_limit(h);
                if (percent < hb->min_pcnt)
                        continue;
 
@@ -804,16 +825,11 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)
 }
 
 static struct rb_node *hists__filter_entries(struct rb_node *nd,
-                                            struct hists *hists,
                                             float min_pcnt)
 {
        while (nd != NULL) {
                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
-               u64 total = hists__total_period(hists);
-               float percent = 0.0;
-
-               if (total)
-                       percent = h->stat.period * 100.0 / total;
+               float percent = hist_entry__get_percent_limit(h);
 
                if (!h->filtered && percent >= min_pcnt)
                        return nd;
@@ -825,16 +841,11 @@ static struct rb_node *hists__filter_entries(struct rb_node *nd,
 }
 
 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
-                                                 struct hists *hists,
                                                  float min_pcnt)
 {
        while (nd != NULL) {
                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
-               u64 total = hists__total_period(hists);
-               float percent = 0.0;
-
-               if (total)
-                       percent = h->stat.period * 100.0 / total;
+               float percent = hist_entry__get_percent_limit(h);
 
                if (!h->filtered && percent >= min_pcnt)
                        return nd;
@@ -863,14 +874,14 @@ static void ui_browser__hists_seek(struct ui_browser *browser,
        switch (whence) {
        case SEEK_SET:
                nd = hists__filter_entries(rb_first(browser->entries),
-                                          hb->hists, hb->min_pcnt);
+                                          hb->min_pcnt);
                break;
        case SEEK_CUR:
                nd = browser->top;
                goto do_offset;
        case SEEK_END:
                nd = hists__filter_prev_entries(rb_last(browser->entries),
-                                               hb->hists, hb->min_pcnt);
+                                               hb->min_pcnt);
                first = false;
                break;
        default:
@@ -913,8 +924,7 @@ do_offset:
                                        break;
                                }
                        }
-                       nd = hists__filter_entries(rb_next(nd), hb->hists,
-                                                  hb->min_pcnt);
+                       nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
                        if (nd == NULL)
                                break;
                        --offset;
@@ -947,7 +957,7 @@ do_offset:
                                }
                        }
 
-                       nd = hists__filter_prev_entries(rb_prev(nd), hb->hists,
+                       nd = hists__filter_prev_entries(rb_prev(nd),
                                                        hb->min_pcnt);
                        if (nd == NULL)
                                break;
@@ -1126,7 +1136,6 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
 {
        struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
-                                                  browser->hists,
                                                   browser->min_pcnt);
        int printed = 0;
 
@@ -1134,8 +1143,7 @@ static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 
                printed += hist_browser__fprintf_entry(browser, h, fp);
-               nd = hists__filter_entries(rb_next(nd), browser->hists,
-                                          browser->min_pcnt);
+               nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
        }
 
        return printed;
@@ -1372,8 +1380,7 @@ static void hist_browser__update_nr_entries(struct hist_browser *hb)
                return;
        }
 
-       while ((nd = hists__filter_entries(nd, hb->hists,
-                                          hb->min_pcnt)) != NULL) {
+       while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
                nr_entries++;
                nd = rb_next(nd);
        }
@@ -1699,14 +1706,14 @@ zoom_dso:
 zoom_out_dso:
                                ui_helpline__pop();
                                browser->hists->dso_filter = NULL;
-                               sort_dso.elide = false;
+                               perf_hpp__set_elide(HISTC_DSO, false);
                        } else {
                                if (dso == NULL)
                                        continue;
                                ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
                                                   dso->kernel ? "the Kernel" : dso->short_name);
                                browser->hists->dso_filter = dso;
-                               sort_dso.elide = true;
+                               perf_hpp__set_elide(HISTC_DSO, true);
                                pstack__push(fstack, &browser->hists->dso_filter);
                        }
                        hists__filter_by_dso(hists);
@@ -1718,13 +1725,13 @@ zoom_thread:
 zoom_out_thread:
                                ui_helpline__pop();
                                browser->hists->thread_filter = NULL;
-                               sort_thread.elide = false;
+                               perf_hpp__set_elide(HISTC_THREAD, false);
                        } else {
                                ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
                                                   thread->comm_set ? thread__comm_str(thread) : "",
                                                   thread->tid);
                                browser->hists->thread_filter = thread;
-                               sort_thread.elide = true;
+                               perf_hpp__set_elide(HISTC_THREAD, false);
                                pstack__push(fstack, &browser->hists->thread_filter);
                        }
                        hists__filter_by_thread(hists);
index 9d90683914d46cc28c1aa88db2e2d54e863f5ae9..6ca60e482cdc2594382466bbaecdde12462dfbec 100644 (file)
@@ -47,11 +47,26 @@ static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,
                          __percent_color_snprintf, true);                      \
 }
 
+#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)                              \
+static u64 he_get_acc_##_field(struct hist_entry *he)                          \
+{                                                                              \
+       return he->stat_acc->_field;                                            \
+}                                                                              \
+                                                                               \
+static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,        \
+                                      struct perf_hpp *hpp,                    \
+                                      struct hist_entry *he)                   \
+{                                                                              \
+       return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, " %6.2f%%",         \
+                             __percent_color_snprintf, true);                  \
+}
+
 __HPP_COLOR_PERCENT_FN(overhead, period)
 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
+__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
 
 #undef __HPP_COLOR_PERCENT_FN
 
@@ -68,6 +83,8 @@ void perf_gtk__init_hpp(void)
                                perf_gtk__hpp_color_overhead_guest_sys;
        perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
                                perf_gtk__hpp_color_overhead_guest_us;
+       perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
+                               perf_gtk__hpp_color_overhead_acc;
 }
 
 static void callchain_list__sym_name(struct callchain_list *cl,
@@ -181,6 +198,13 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
                if (perf_hpp__should_skip(fmt))
                        continue;
 
+               /*
+                * XXX no way to determine where symcol column is..
+                *     Just use last column for now.
+                */
+               if (perf_hpp__is_sort_entry(fmt))
+                       sym_col = col_idx;
+
                fmt->header(fmt, &hpp, hists_to_evsel(hists));
 
                gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
@@ -209,14 +233,12 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
                GtkTreeIter iter;
                u64 total = hists__total_period(h->hists);
-               float percent = 0.0;
+               float percent;
 
                if (h->filtered)
                        continue;
 
-               if (total)
-                       percent = h->stat.period * 100.0 / total;
-
+               percent = hist_entry__get_percent_limit(h);
                if (percent < min_pcnt)
                        continue;
 
@@ -238,7 +260,8 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
 
                if (symbol_conf.use_callchain && sort__has_sym) {
                        if (callchain_param.mode == CHAIN_GRAPH_REL)
-                               total = h->stat.period;
+                               total = symbol_conf.cumulate_callchain ?
+                                       h->stat_acc->period : h->stat.period;
 
                        perf_gtk__add_callchain(&h->sorted_chain, store, &iter,
                                                sym_col, total);
index 4484f5bd1b14a6065637605b745b3aff2e08bbbc..498adb23c02ef9992f82c840aca5aa31220af9bf 100644 (file)
@@ -104,6 +104,18 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
        return ret;
 }
 
+int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he,
+                  hpp_field_fn get_field, const char *fmt,
+                  hpp_snprint_fn print_fn, bool fmt_percent)
+{
+       if (!symbol_conf.cumulate_callchain) {
+               return snprintf(hpp->buf, hpp->size, "%*s",
+                               fmt_percent ? 8 : 12, "N/A");
+       }
+
+       return __hpp__fmt(hpp, he, get_field, fmt, print_fn, fmt_percent);
+}
+
 static int field_cmp(u64 field_a, u64 field_b)
 {
        if (field_a > field_b)
@@ -160,6 +172,24 @@ out:
        return ret;
 }
 
+static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b,
+                          hpp_field_fn get_field)
+{
+       s64 ret = 0;
+
+       if (symbol_conf.cumulate_callchain) {
+               /*
+                * Put caller above callee when they have equal period.
+                */
+               ret = field_cmp(get_field(a), get_field(b));
+               if (ret)
+                       return ret;
+
+               ret = b->callchain->max_depth - a->callchain->max_depth;
+       }
+       return ret;
+}
+
 #define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width)          \
 static int hpp__header_##_type(struct perf_hpp_fmt *fmt __maybe_unused,        \
                               struct perf_hpp *hpp,                    \
@@ -242,6 +272,34 @@ static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b)       \
        return __hpp__sort(a, b, he_get_##_field);                              \
 }
 
+#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)                              \
+static u64 he_get_acc_##_field(struct hist_entry *he)                          \
+{                                                                              \
+       return he->stat_acc->_field;                                            \
+}                                                                              \
+                                                                               \
+static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,         \
+                             struct perf_hpp *hpp, struct hist_entry *he)      \
+{                                                                              \
+       return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, " %6.2f%%",         \
+                             hpp_color_scnprintf, true);                       \
+}
+
+#define __HPP_ENTRY_ACC_PERCENT_FN(_type, _field)                              \
+static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused,                \
+                             struct perf_hpp *hpp, struct hist_entry *he)      \
+{                                                                              \
+       const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%";         \
+       return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, fmt,                \
+                             hpp_entry_scnprintf, true);                       \
+}
+
+#define __HPP_SORT_ACC_FN(_type, _field)                                       \
+static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b)   \
+{                                                                              \
+       return __hpp__sort_acc(a, b, he_get_acc_##_field);                      \
+}
+
 #define __HPP_ENTRY_RAW_FN(_type, _field)                                      \
 static u64 he_get_raw_##_field(struct hist_entry *he)                          \
 {                                                                              \
@@ -270,18 +328,27 @@ __HPP_COLOR_PERCENT_FN(_type, _field)                                     \
 __HPP_ENTRY_PERCENT_FN(_type, _field)                                  \
 __HPP_SORT_FN(_type, _field)
 
+#define HPP_PERCENT_ACC_FNS(_type, _str, _field, _min_width, _unit_width)\
+__HPP_HEADER_FN(_type, _str, _min_width, _unit_width)                  \
+__HPP_WIDTH_FN(_type, _min_width, _unit_width)                         \
+__HPP_COLOR_ACC_PERCENT_FN(_type, _field)                              \
+__HPP_ENTRY_ACC_PERCENT_FN(_type, _field)                              \
+__HPP_SORT_ACC_FN(_type, _field)
+
 #define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width)      \
 __HPP_HEADER_FN(_type, _str, _min_width, _unit_width)                  \
 __HPP_WIDTH_FN(_type, _min_width, _unit_width)                         \
 __HPP_ENTRY_RAW_FN(_type, _field)                                      \
 __HPP_SORT_RAW_FN(_type, _field)
 
+__HPP_HEADER_FN(overhead_self, "Self", 8, 8)
 
 HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8)
 HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8)
 HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8)
 HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8)
 HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8)
+HPP_PERCENT_ACC_FNS(overhead_acc, "Children", period, 8, 8)
 
 HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12)
 HPP_RAW_FNS(period, "Period", period, 12, 12)
@@ -303,6 +370,17 @@ static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused,
                .sort   = hpp__sort_ ## _name,          \
        }
 
+#define HPP__COLOR_ACC_PRINT_FNS(_name)                        \
+       {                                               \
+               .header = hpp__header_ ## _name,        \
+               .width  = hpp__width_ ## _name,         \
+               .color  = hpp__color_ ## _name,         \
+               .entry  = hpp__entry_ ## _name,         \
+               .cmp    = hpp__nop_cmp,                 \
+               .collapse = hpp__nop_cmp,               \
+               .sort   = hpp__sort_ ## _name,          \
+       }
+
 #define HPP__PRINT_FNS(_name)                          \
        {                                               \
                .header = hpp__header_ ## _name,        \
@@ -319,6 +397,7 @@ struct perf_hpp_fmt perf_hpp__format[] = {
        HPP__COLOR_PRINT_FNS(overhead_us),
        HPP__COLOR_PRINT_FNS(overhead_guest_sys),
        HPP__COLOR_PRINT_FNS(overhead_guest_us),
+       HPP__COLOR_ACC_PRINT_FNS(overhead_acc),
        HPP__PRINT_FNS(samples),
        HPP__PRINT_FNS(period)
 };
@@ -328,16 +407,23 @@ LIST_HEAD(perf_hpp__sort_list);
 
 
 #undef HPP__COLOR_PRINT_FNS
+#undef HPP__COLOR_ACC_PRINT_FNS
 #undef HPP__PRINT_FNS
 
 #undef HPP_PERCENT_FNS
+#undef HPP_PERCENT_ACC_FNS
 #undef HPP_RAW_FNS
 
 #undef __HPP_HEADER_FN
 #undef __HPP_WIDTH_FN
 #undef __HPP_COLOR_PERCENT_FN
 #undef __HPP_ENTRY_PERCENT_FN
+#undef __HPP_COLOR_ACC_PERCENT_FN
+#undef __HPP_ENTRY_ACC_PERCENT_FN
 #undef __HPP_ENTRY_RAW_FN
+#undef __HPP_SORT_FN
+#undef __HPP_SORT_ACC_FN
+#undef __HPP_SORT_RAW_FN
 
 
 void perf_hpp__init(void)
@@ -361,6 +447,13 @@ void perf_hpp__init(void)
        if (field_order)
                return;
 
+       if (symbol_conf.cumulate_callchain) {
+               perf_hpp__column_enable(PERF_HPP__OVERHEAD_ACC);
+
+               perf_hpp__format[PERF_HPP__OVERHEAD].header =
+                                               hpp__header_overhead_self;
+       }
+
        perf_hpp__column_enable(PERF_HPP__OVERHEAD);
 
        if (symbol_conf.show_cpu_utilization) {
@@ -383,6 +476,12 @@ void perf_hpp__init(void)
        list = &perf_hpp__format[PERF_HPP__OVERHEAD].sort_list;
        if (list_empty(list))
                list_add(list, &perf_hpp__sort_list);
+
+       if (symbol_conf.cumulate_callchain) {
+               list = &perf_hpp__format[PERF_HPP__OVERHEAD_ACC].sort_list;
+               if (list_empty(list))
+                       list_add(list, &perf_hpp__sort_list);
+       }
 }
 
 void perf_hpp__column_register(struct perf_hpp_fmt *format)
@@ -390,6 +489,11 @@ void perf_hpp__column_register(struct perf_hpp_fmt *format)
        list_add_tail(&format->list, &perf_hpp__list);
 }
 
+void perf_hpp__column_unregister(struct perf_hpp_fmt *format)
+{
+       list_del(&format->list);
+}
+
 void perf_hpp__register_sort_field(struct perf_hpp_fmt *format)
 {
        list_add_tail(&format->sort_list, &perf_hpp__sort_list);
@@ -401,6 +505,21 @@ void perf_hpp__column_enable(unsigned col)
        perf_hpp__column_register(&perf_hpp__format[col]);
 }
 
+void perf_hpp__column_disable(unsigned col)
+{
+       BUG_ON(col >= PERF_HPP__MAX_INDEX);
+       perf_hpp__column_unregister(&perf_hpp__format[col]);
+}
+
+void perf_hpp__cancel_cumulate(void)
+{
+       if (field_order)
+               return;
+
+       perf_hpp__column_disable(PERF_HPP__OVERHEAD_ACC);
+       perf_hpp__format[PERF_HPP__OVERHEAD].header = hpp__header_overhead;
+}
+
 void perf_hpp__setup_output_field(void)
 {
        struct perf_hpp_fmt *fmt;
index 9f57991025a90fd2648f6d54832dedd9a4c8ab10..90122abd372133fdb0358488e09810030c234b6c 100644 (file)
@@ -271,7 +271,9 @@ static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
 {
        switch (callchain_param.mode) {
        case CHAIN_GRAPH_REL:
-               return callchain__fprintf_graph(fp, &he->sorted_chain, he->stat.period,
+               return callchain__fprintf_graph(fp, &he->sorted_chain,
+                                               symbol_conf.cumulate_callchain ?
+                                               he->stat_acc->period : he->stat.period,
                                                left_margin);
                break;
        case CHAIN_GRAPH_ABS:
@@ -461,12 +463,12 @@ print_entries:
 
        for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
-               float percent = h->stat.period * 100.0 /
-                                       hists->stats.total_period;
+               float percent;
 
                if (h->filtered)
                        continue;
 
+               percent = hist_entry__get_percent_limit(h);
                if (percent < min_pcnt)
                        continue;
 
index 9a42382b3921a76a531d97481a32df2e8f29542f..48b6d3f500123162df296ff63514351c4aa1b991 100644 (file)
@@ -616,7 +616,8 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent
        if (sample->callchain == NULL)
                return 0;
 
-       if (symbol_conf.use_callchain || sort__has_parent) {
+       if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain ||
+           sort__has_parent) {
                return machine__resolve_callchain(al->machine, evsel, al->thread,
                                                  sample, parent, al, max_stack);
        }
@@ -629,3 +630,45 @@ int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *samp
                return 0;
        return callchain_append(he->callchain, &callchain_cursor, sample->period);
 }
+
+int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node,
+                       bool hide_unresolved)
+{
+       al->map = node->map;
+       al->sym = node->sym;
+       if (node->map)
+               al->addr = node->map->map_ip(node->map, node->ip);
+       else
+               al->addr = node->ip;
+
+       if (al->sym == NULL) {
+               if (hide_unresolved)
+                       return 0;
+               if (al->map == NULL)
+                       goto out;
+       }
+
+       if (al->map->groups == &al->machine->kmaps) {
+               if (machine__is_host(al->machine)) {
+                       al->cpumode = PERF_RECORD_MISC_KERNEL;
+                       al->level = 'k';
+               } else {
+                       al->cpumode = PERF_RECORD_MISC_GUEST_KERNEL;
+                       al->level = 'g';
+               }
+       } else {
+               if (machine__is_host(al->machine)) {
+                       al->cpumode = PERF_RECORD_MISC_USER;
+                       al->level = '.';
+               } else if (perf_guest) {
+                       al->cpumode = PERF_RECORD_MISC_GUEST_USER;
+                       al->level = 'u';
+               } else {
+                       al->cpumode = PERF_RECORD_MISC_HYPERVISOR;
+                       al->level = 'H';
+               }
+       }
+
+out:
+       return 1;
+}
index bde2b0cc24cf473c463cc4a030d7ff50036d08dd..8f84423a75da74db5695c060cb13d15a8b503203 100644 (file)
@@ -162,7 +162,18 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent
                              struct perf_evsel *evsel, struct addr_location *al,
                              int max_stack);
 int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample);
+int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node,
+                       bool hide_unresolved);
 
 extern const char record_callchain_help[];
 int parse_callchain_report_opt(const char *arg);
+
+static inline void callchain_cursor_snapshot(struct callchain_cursor *dest,
+                                            struct callchain_cursor *src)
+{
+       *dest = *src;
+
+       dest->first = src->curr;
+       dest->nr -= src->pos;
+}
 #endif /* __PERF_CALLCHAIN_H */
index b262b44b7a656d8dca4fcab7488e43e3c49ebb8c..5a0a4b2cadc4574dce4c23239ac9a14e4e13b6fe 100644 (file)
@@ -4,6 +4,7 @@
 #include "session.h"
 #include "sort.h"
 #include "evsel.h"
+#include "annotate.h"
 #include <math.h>
 
 static bool hists__filter_entry_by_dso(struct hists *hists,
@@ -231,6 +232,8 @@ static bool hists__decay_entry(struct hists *hists, struct hist_entry *he)
                return true;
 
        he_stat__decay(&he->stat);
+       if (symbol_conf.cumulate_callchain)
+               he_stat__decay(he->stat_acc);
 
        diff = prev_period - he->stat.period;
 
@@ -276,14 +279,31 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel)
  * histogram, sorted on item, collects periods
  */
 
-static struct hist_entry *hist_entry__new(struct hist_entry *template)
+static struct hist_entry *hist_entry__new(struct hist_entry *template,
+                                         bool sample_self)
 {
-       size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0;
-       struct hist_entry *he = zalloc(sizeof(*he) + callchain_size);
+       size_t callchain_size = 0;
+       struct hist_entry *he;
+
+       if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain)
+               callchain_size = sizeof(struct callchain_root);
+
+       he = zalloc(sizeof(*he) + callchain_size);
 
        if (he != NULL) {
                *he = *template;
 
+               if (symbol_conf.cumulate_callchain) {
+                       he->stat_acc = malloc(sizeof(he->stat));
+                       if (he->stat_acc == NULL) {
+                               free(he);
+                               return NULL;
+                       }
+                       memcpy(he->stat_acc, &he->stat, sizeof(he->stat));
+                       if (!sample_self)
+                               memset(&he->stat, 0, sizeof(he->stat));
+               }
+
                if (he->ms.map)
                        he->ms.map->referenced = true;
 
@@ -295,6 +315,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
                         */
                        he->branch_info = malloc(sizeof(*he->branch_info));
                        if (he->branch_info == NULL) {
+                               free(he->stat_acc);
                                free(he);
                                return NULL;
                        }
@@ -333,7 +354,8 @@ static u8 symbol__parent_filter(const struct symbol *parent)
 
 static struct hist_entry *add_hist_entry(struct hists *hists,
                                         struct hist_entry *entry,
-                                        struct addr_location *al)
+                                        struct addr_location *al,
+                                        bool sample_self)
 {
        struct rb_node **p;
        struct rb_node *parent = NULL;
@@ -357,7 +379,10 @@ static struct hist_entry *add_hist_entry(struct hists *hists,
                cmp = hist_entry__cmp(he, entry);
 
                if (!cmp) {
-                       he_stat__add_period(&he->stat, period, weight);
+                       if (sample_self)
+                               he_stat__add_period(&he->stat, period, weight);
+                       if (symbol_conf.cumulate_callchain)
+                               he_stat__add_period(he->stat_acc, period, weight);
 
                        /*
                         * This mem info was allocated from sample__resolve_mem
@@ -385,14 +410,17 @@ static struct hist_entry *add_hist_entry(struct hists *hists,
                        p = &(*p)->rb_right;
        }
 
-       he = hist_entry__new(entry);
+       he = hist_entry__new(entry, sample_self);
        if (!he)
                return NULL;
 
        rb_link_node(&he->rb_node_in, parent, p);
        rb_insert_color(&he->rb_node_in, hists->entries_in);
 out:
-       he_stat__add_cpumode_period(&he->stat, al->cpumode, period);
+       if (sample_self)
+               he_stat__add_cpumode_period(&he->stat, al->cpumode, period);
+       if (symbol_conf.cumulate_callchain)
+               he_stat__add_cpumode_period(he->stat_acc, al->cpumode, period);
        return he;
 }
 
@@ -401,7 +429,8 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
                                      struct symbol *sym_parent,
                                      struct branch_info *bi,
                                      struct mem_info *mi,
-                                     u64 period, u64 weight, u64 transaction)
+                                     u64 period, u64 weight, u64 transaction,
+                                     bool sample_self)
 {
        struct hist_entry entry = {
                .thread = al->thread,
@@ -426,7 +455,429 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
                .transaction = transaction,
        };
 
-       return add_hist_entry(hists, &entry, al);
+       return add_hist_entry(hists, &entry, al, sample_self);
+}
+
+static int
+iter_next_nop_entry(struct hist_entry_iter *iter __maybe_unused,
+                   struct addr_location *al __maybe_unused)
+{
+       return 0;
+}
+
+static int
+iter_add_next_nop_entry(struct hist_entry_iter *iter __maybe_unused,
+                       struct addr_location *al __maybe_unused)
+{
+       return 0;
+}
+
+static int
+iter_prepare_mem_entry(struct hist_entry_iter *iter, struct addr_location *al)
+{
+       struct perf_sample *sample = iter->sample;
+       struct mem_info *mi;
+
+       mi = sample__resolve_mem(sample, al);
+       if (mi == NULL)
+               return -ENOMEM;
+
+       iter->priv = mi;
+       return 0;
+}
+
+static int
+iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al)
+{
+       u64 cost;
+       struct mem_info *mi = iter->priv;
+       struct hist_entry *he;
+
+       if (mi == NULL)
+               return -EINVAL;
+
+       cost = iter->sample->weight;
+       if (!cost)
+               cost = 1;
+
+       /*
+        * must pass period=weight in order to get the correct
+        * sorting from hists__collapse_resort() which is solely
+        * based on periods. We want sorting be done on nr_events * weight
+        * and this is indirectly achieved by passing period=weight here
+        * and the he_stat__add_period() function.
+        */
+       he = __hists__add_entry(&iter->evsel->hists, al, iter->parent, NULL, mi,
+                               cost, cost, 0, true);
+       if (!he)
+               return -ENOMEM;
+
+       iter->he = he;
+       return 0;
+}
+
+static int
+iter_finish_mem_entry(struct hist_entry_iter *iter,
+                     struct addr_location *al __maybe_unused)
+{
+       struct perf_evsel *evsel = iter->evsel;
+       struct hist_entry *he = iter->he;
+       int err = -EINVAL;
+
+       if (he == NULL)
+               goto out;
+
+       hists__inc_nr_samples(&evsel->hists, he->filtered);
+
+       err = hist_entry__append_callchain(he, iter->sample);
+
+out:
+       /*
+        * We don't need to free iter->priv (mem_info) here since
+        * the mem info was either already freed in add_hist_entry() or
+        * passed to a new hist entry by hist_entry__new().
+        */
+       iter->priv = NULL;
+
+       iter->he = NULL;
+       return err;
+}
+
+static int
+iter_prepare_branch_entry(struct hist_entry_iter *iter, struct addr_location *al)
+{
+       struct branch_info *bi;
+       struct perf_sample *sample = iter->sample;
+
+       bi = sample__resolve_bstack(sample, al);
+       if (!bi)
+               return -ENOMEM;
+
+       iter->curr = 0;
+       iter->total = sample->branch_stack->nr;
+
+       iter->priv = bi;
+       return 0;
+}
+
+static int
+iter_add_single_branch_entry(struct hist_entry_iter *iter __maybe_unused,
+                            struct addr_location *al __maybe_unused)
+{
+       /* to avoid calling callback function */
+       iter->he = NULL;
+
+       return 0;
+}
+
+static int
+iter_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al)
+{
+       struct branch_info *bi = iter->priv;
+       int i = iter->curr;
+
+       if (bi == NULL)
+               return 0;
+
+       if (iter->curr >= iter->total)
+               return 0;
+
+       al->map = bi[i].to.map;
+       al->sym = bi[i].to.sym;
+       al->addr = bi[i].to.addr;
+       return 1;
+}
+
+static int
+iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al)
+{
+       struct branch_info *bi;
+       struct perf_evsel *evsel = iter->evsel;
+       struct hist_entry *he = NULL;
+       int i = iter->curr;
+       int err = 0;
+
+       bi = iter->priv;
+
+       if (iter->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym))
+               goto out;
+
+       /*
+        * The report shows the percentage of total branches captured
+        * and not events sampled. Thus we use a pseudo period of 1.
+        */
+       he = __hists__add_entry(&evsel->hists, al, iter->parent, &bi[i], NULL,
+                               1, 1, 0, true);
+       if (he == NULL)
+               return -ENOMEM;
+
+       hists__inc_nr_samples(&evsel->hists, he->filtered);
+
+out:
+       iter->he = he;
+       iter->curr++;
+       return err;
+}
+
+static int
+iter_finish_branch_entry(struct hist_entry_iter *iter,
+                        struct addr_location *al __maybe_unused)
+{
+       zfree(&iter->priv);
+       iter->he = NULL;
+
+       return iter->curr >= iter->total ? 0 : -1;
+}
+
+static int
+iter_prepare_normal_entry(struct hist_entry_iter *iter __maybe_unused,
+                         struct addr_location *al __maybe_unused)
+{
+       return 0;
+}
+
+static int
+iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location *al)
+{
+       struct perf_evsel *evsel = iter->evsel;
+       struct perf_sample *sample = iter->sample;
+       struct hist_entry *he;
+
+       he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL,
+                               sample->period, sample->weight,
+                               sample->transaction, true);
+       if (he == NULL)
+               return -ENOMEM;
+
+       iter->he = he;
+       return 0;
+}
+
+static int
+iter_finish_normal_entry(struct hist_entry_iter *iter,
+                        struct addr_location *al __maybe_unused)
+{
+       struct hist_entry *he = iter->he;
+       struct perf_evsel *evsel = iter->evsel;
+       struct perf_sample *sample = iter->sample;
+
+       if (he == NULL)
+               return 0;
+
+       iter->he = NULL;
+
+       hists__inc_nr_samples(&evsel->hists, he->filtered);
+
+       return hist_entry__append_callchain(he, sample);
+}
+
+static int
+iter_prepare_cumulative_entry(struct hist_entry_iter *iter __maybe_unused,
+                             struct addr_location *al __maybe_unused)
+{
+       struct hist_entry **he_cache;
+
+       callchain_cursor_commit(&callchain_cursor);
+
+       /*
+        * This is for detecting cycles or recursions so that they're
+        * cumulated only one time to prevent entries more than 100%
+        * overhead.
+        */
+       he_cache = malloc(sizeof(*he_cache) * (PERF_MAX_STACK_DEPTH + 1));
+       if (he_cache == NULL)
+               return -ENOMEM;
+
+       iter->priv = he_cache;
+       iter->curr = 0;
+
+       return 0;
+}
+
+static int
+iter_add_single_cumulative_entry(struct hist_entry_iter *iter,
+                                struct addr_location *al)
+{
+       struct perf_evsel *evsel = iter->evsel;
+       struct perf_sample *sample = iter->sample;
+       struct hist_entry **he_cache = iter->priv;
+       struct hist_entry *he;
+       int err = 0;
+
+       he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL,
+                               sample->period, sample->weight,
+                               sample->transaction, true);
+       if (he == NULL)
+               return -ENOMEM;
+
+       iter->he = he;
+       he_cache[iter->curr++] = he;
+
+       callchain_append(he->callchain, &callchain_cursor, sample->period);
+
+       /*
+        * We need to re-initialize the cursor since callchain_append()
+        * advanced the cursor to the end.
+        */
+       callchain_cursor_commit(&callchain_cursor);
+
+       hists__inc_nr_samples(&evsel->hists, he->filtered);
+
+       return err;
+}
+
+static int
+iter_next_cumulative_entry(struct hist_entry_iter *iter,
+                          struct addr_location *al)
+{
+       struct callchain_cursor_node *node;
+
+       node = callchain_cursor_current(&callchain_cursor);
+       if (node == NULL)
+               return 0;
+
+       return fill_callchain_info(al, node, iter->hide_unresolved);
+}
+
+static int
+iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
+                              struct addr_location *al)
+{
+       struct perf_evsel *evsel = iter->evsel;
+       struct perf_sample *sample = iter->sample;
+       struct hist_entry **he_cache = iter->priv;
+       struct hist_entry *he;
+       struct hist_entry he_tmp = {
+               .cpu = al->cpu,
+               .thread = al->thread,
+               .comm = thread__comm(al->thread),
+               .ip = al->addr,
+               .ms = {
+                       .map = al->map,
+                       .sym = al->sym,
+               },
+               .parent = iter->parent,
+       };
+       int i;
+       struct callchain_cursor cursor;
+
+       callchain_cursor_snapshot(&cursor, &callchain_cursor);
+
+       callchain_cursor_advance(&callchain_cursor);
+
+       /*
+        * Check if there's duplicate entries in the callchain.
+        * It's possible that it has cycles or recursive calls.
+        */
+       for (i = 0; i < iter->curr; i++) {
+               if (hist_entry__cmp(he_cache[i], &he_tmp) == 0) {
+                       /* to avoid calling callback function */
+                       iter->he = NULL;
+                       return 0;
+               }
+       }
+
+       he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL,
+                               sample->period, sample->weight,
+                               sample->transaction, false);
+       if (he == NULL)
+               return -ENOMEM;
+
+       iter->he = he;
+       he_cache[iter->curr++] = he;
+
+       callchain_append(he->callchain, &cursor, sample->period);
+       return 0;
+}
+
+static int
+iter_finish_cumulative_entry(struct hist_entry_iter *iter,
+                            struct addr_location *al __maybe_unused)
+{
+       zfree(&iter->priv);
+       iter->he = NULL;
+
+       return 0;
+}
+
+const struct hist_iter_ops hist_iter_mem = {
+       .prepare_entry          = iter_prepare_mem_entry,
+       .add_single_entry       = iter_add_single_mem_entry,
+       .next_entry             = iter_next_nop_entry,
+       .add_next_entry         = iter_add_next_nop_entry,
+       .finish_entry           = iter_finish_mem_entry,
+};
+
+const struct hist_iter_ops hist_iter_branch = {
+       .prepare_entry          = iter_prepare_branch_entry,
+       .add_single_entry       = iter_add_single_branch_entry,
+       .next_entry             = iter_next_branch_entry,
+       .add_next_entry         = iter_add_next_branch_entry,
+       .finish_entry           = iter_finish_branch_entry,
+};
+
+const struct hist_iter_ops hist_iter_normal = {
+       .prepare_entry          = iter_prepare_normal_entry,
+       .add_single_entry       = iter_add_single_normal_entry,
+       .next_entry             = iter_next_nop_entry,
+       .add_next_entry         = iter_add_next_nop_entry,
+       .finish_entry           = iter_finish_normal_entry,
+};
+
+const struct hist_iter_ops hist_iter_cumulative = {
+       .prepare_entry          = iter_prepare_cumulative_entry,
+       .add_single_entry       = iter_add_single_cumulative_entry,
+       .next_entry             = iter_next_cumulative_entry,
+       .add_next_entry         = iter_add_next_cumulative_entry,
+       .finish_entry           = iter_finish_cumulative_entry,
+};
+
+int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
+                        struct perf_evsel *evsel, struct perf_sample *sample,
+                        int max_stack_depth, void *arg)
+{
+       int err, err2;
+
+       err = sample__resolve_callchain(sample, &iter->parent, evsel, al,
+                                       max_stack_depth);
+       if (err)
+               return err;
+
+       iter->evsel = evsel;
+       iter->sample = sample;
+
+       err = iter->ops->prepare_entry(iter, al);
+       if (err)
+               goto out;
+
+       err = iter->ops->add_single_entry(iter, al);
+       if (err)
+               goto out;
+
+       if (iter->he && iter->add_entry_cb) {
+               err = iter->add_entry_cb(iter, al, true, arg);
+               if (err)
+                       goto out;
+       }
+
+       while (iter->ops->next_entry(iter, al)) {
+               err = iter->ops->add_next_entry(iter, al);
+               if (err)
+                       break;
+
+               if (iter->he && iter->add_entry_cb) {
+                       err = iter->add_entry_cb(iter, al, false, arg);
+                       if (err)
+                               goto out;
+               }
+       }
+
+out:
+       err2 = iter->ops->finish_entry(iter, al);
+       if (!err)
+               err = err2;
+
+       return err;
 }
 
 int64_t
@@ -469,6 +920,7 @@ void hist_entry__free(struct hist_entry *he)
 {
        zfree(&he->branch_info);
        zfree(&he->mem_info);
+       zfree(&he->stat_acc);
        free_srcline(he->srcline);
        free(he);
 }
@@ -494,6 +946,8 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,
 
                if (!cmp) {
                        he_stat__add_stat(&iter->stat, &he->stat);
+                       if (symbol_conf.cumulate_callchain)
+                               he_stat__add_stat(iter->stat_acc, he->stat_acc);
 
                        if (symbol_conf.use_callchain) {
                                callchain_cursor_reset(&callchain_cursor);
@@ -800,6 +1254,13 @@ void hists__inc_nr_events(struct hists *hists, u32 type)
        events_stats__inc(&hists->stats, type);
 }
 
+void hists__inc_nr_samples(struct hists *hists, bool filtered)
+{
+       events_stats__inc(&hists->stats, PERF_RECORD_SAMPLE);
+       if (!filtered)
+               hists->stats.nr_non_filtered_samples++;
+}
+
 static struct hist_entry *hists__add_dummy_entry(struct hists *hists,
                                                 struct hist_entry *pair)
 {
@@ -831,7 +1292,7 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists,
                        p = &(*p)->rb_right;
        }
 
-       he = hist_entry__new(pair);
+       he = hist_entry__new(pair, true);
        if (he) {
                memset(&he->stat, 0, sizeof(he->stat));
                he->hists = hists;
index a8418d19808dc29e50cc683ea6da088062af55a4..d2bf03575d5f08ccb22f6c550ac5f18b0fdf5e08 100644 (file)
@@ -96,12 +96,50 @@ struct hists {
        u16                     col_len[HISTC_NR_COLS];
 };
 
+struct hist_entry_iter;
+
+struct hist_iter_ops {
+       int (*prepare_entry)(struct hist_entry_iter *, struct addr_location *);
+       int (*add_single_entry)(struct hist_entry_iter *, struct addr_location *);
+       int (*next_entry)(struct hist_entry_iter *, struct addr_location *);
+       int (*add_next_entry)(struct hist_entry_iter *, struct addr_location *);
+       int (*finish_entry)(struct hist_entry_iter *, struct addr_location *);
+};
+
+struct hist_entry_iter {
+       int total;
+       int curr;
+
+       bool hide_unresolved;
+
+       struct perf_evsel *evsel;
+       struct perf_sample *sample;
+       struct hist_entry *he;
+       struct symbol *parent;
+       void *priv;
+
+       const struct hist_iter_ops *ops;
+       /* user-defined callback function (optional) */
+       int (*add_entry_cb)(struct hist_entry_iter *iter,
+                           struct addr_location *al, bool single, void *arg);
+};
+
+extern const struct hist_iter_ops hist_iter_normal;
+extern const struct hist_iter_ops hist_iter_branch;
+extern const struct hist_iter_ops hist_iter_mem;
+extern const struct hist_iter_ops hist_iter_cumulative;
+
 struct hist_entry *__hists__add_entry(struct hists *hists,
                                      struct addr_location *al,
                                      struct symbol *parent,
                                      struct branch_info *bi,
                                      struct mem_info *mi, u64 period,
-                                     u64 weight, u64 transaction);
+                                     u64 weight, u64 transaction,
+                                     bool sample_self);
+int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
+                        struct perf_evsel *evsel, struct perf_sample *sample,
+                        int max_stack_depth, void *arg);
+
 int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right);
 int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right);
 int hist_entry__transaction_len(void);
@@ -119,6 +157,7 @@ u64 hists__total_period(struct hists *hists);
 void hists__reset_stats(struct hists *hists);
 void hists__inc_stats(struct hists *hists, struct hist_entry *h);
 void hists__inc_nr_events(struct hists *hists, u32 type);
+void hists__inc_nr_samples(struct hists *hists, bool filtered);
 void events_stats__inc(struct events_stats *stats, u32 type);
 size_t events_stats__fprintf(struct events_stats *stats, FILE *fp);
 
@@ -166,6 +205,7 @@ struct perf_hpp_fmt {
 
        struct list_head list;
        struct list_head sort_list;
+       bool elide;
 };
 
 extern struct list_head perf_hpp__list;
@@ -192,6 +232,7 @@ enum {
        PERF_HPP__OVERHEAD_US,
        PERF_HPP__OVERHEAD_GUEST_SYS,
        PERF_HPP__OVERHEAD_GUEST_US,
+       PERF_HPP__OVERHEAD_ACC,
        PERF_HPP__SAMPLES,
        PERF_HPP__PERIOD,
 
@@ -200,7 +241,11 @@ enum {
 
 void perf_hpp__init(void);
 void perf_hpp__column_register(struct perf_hpp_fmt *format);
+void perf_hpp__column_unregister(struct perf_hpp_fmt *format);
 void perf_hpp__column_enable(unsigned col);
+void perf_hpp__column_disable(unsigned col);
+void perf_hpp__cancel_cumulate(void);
+
 void perf_hpp__register_sort_field(struct perf_hpp_fmt *format);
 void perf_hpp__setup_output_field(void);
 void perf_hpp__reset_output_field(void);
@@ -208,7 +253,12 @@ void perf_hpp__append_sort_keys(void);
 
 bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format);
 bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b);
-bool perf_hpp__should_skip(struct perf_hpp_fmt *format);
+
+static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format)
+{
+       return format->elide;
+}
+
 void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists);
 
 typedef u64 (*hpp_field_fn)(struct hist_entry *he);
@@ -218,6 +268,9 @@ typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...);
 int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
               hpp_field_fn get_field, const char *fmt,
               hpp_snprint_fn print_fn, bool fmt_percent);
+int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he,
+                  hpp_field_fn get_field, const char *fmt,
+                  hpp_snprint_fn print_fn, bool fmt_percent);
 
 static inline void advance_hpp(struct perf_hpp *hpp, int inc)
 {
index 901b9bece2ee439ebbc9e9100471cd03883b55d8..45512baaab672706d1c0fd4c858630fed4176364 100644 (file)
@@ -1061,6 +1061,7 @@ static struct hpp_dimension hpp_sort_dimensions[] = {
        DIM(PERF_HPP__OVERHEAD_US, "overhead_us"),
        DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"),
        DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"),
+       DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"),
        DIM(PERF_HPP__SAMPLES, "sample"),
        DIM(PERF_HPP__PERIOD, "period"),
 };
@@ -1156,6 +1157,7 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd)
 
        INIT_LIST_HEAD(&hse->hpp.list);
        INIT_LIST_HEAD(&hse->hpp.sort_list);
+       hse->hpp.elide = false;
 
        return hse;
 }
@@ -1363,27 +1365,64 @@ static int __setup_sorting(void)
        return ret;
 }
 
-bool perf_hpp__should_skip(struct perf_hpp_fmt *format)
+void perf_hpp__set_elide(int idx, bool elide)
 {
-       if (perf_hpp__is_sort_entry(format)) {
-               struct hpp_sort_entry *hse;
+       struct perf_hpp_fmt *fmt;
+       struct hpp_sort_entry *hse;
+
+       perf_hpp__for_each_format(fmt) {
+               if (!perf_hpp__is_sort_entry(fmt))
+                       continue;
 
-               hse = container_of(format, struct hpp_sort_entry, hpp);
-               return hse->se->elide;
+               hse = container_of(fmt, struct hpp_sort_entry, hpp);
+               if (hse->se->se_width_idx == idx) {
+                       fmt->elide = elide;
+                       break;
+               }
        }
-       return false;
 }
 
-static void sort_entry__setup_elide(struct sort_entry *se,
-                                   struct strlist *list,
-                                   const char *list_name, FILE *fp)
+static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp)
 {
        if (list && strlist__nr_entries(list) == 1) {
                if (fp != NULL)
                        fprintf(fp, "# %s: %s\n", list_name,
                                strlist__entry(list, 0)->s);
-               se->elide = true;
+               return true;
        }
+       return false;
+}
+
+static bool get_elide(int idx, FILE *output)
+{
+       switch (idx) {
+       case HISTC_SYMBOL:
+               return __get_elide(symbol_conf.sym_list, "symbol", output);
+       case HISTC_DSO:
+               return __get_elide(symbol_conf.dso_list, "dso", output);
+       case HISTC_COMM:
+               return __get_elide(symbol_conf.comm_list, "comm", output);
+       default:
+               break;
+       }
+
+       if (sort__mode != SORT_MODE__BRANCH)
+               return false;
+
+       switch (idx) {
+       case HISTC_SYMBOL_FROM:
+               return __get_elide(symbol_conf.sym_from_list, "sym_from", output);
+       case HISTC_SYMBOL_TO:
+               return __get_elide(symbol_conf.sym_to_list, "sym_to", output);
+       case HISTC_DSO_FROM:
+               return __get_elide(symbol_conf.dso_from_list, "dso_from", output);
+       case HISTC_DSO_TO:
+               return __get_elide(symbol_conf.dso_to_list, "dso_to", output);
+       default:
+               break;
+       }
+
+       return false;
 }
 
 void sort__setup_elide(FILE *output)
@@ -1391,39 +1430,12 @@ void sort__setup_elide(FILE *output)
        struct perf_hpp_fmt *fmt;
        struct hpp_sort_entry *hse;
 
-       sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
-                               "dso", output);
-       sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list,
-                               "comm", output);
-       sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list,
-                               "symbol", output);
-
-       if (sort__mode == SORT_MODE__BRANCH) {
-               sort_entry__setup_elide(&sort_dso_from,
-                                       symbol_conf.dso_from_list,
-                                       "dso_from", output);
-               sort_entry__setup_elide(&sort_dso_to,
-                                       symbol_conf.dso_to_list,
-                                       "dso_to", output);
-               sort_entry__setup_elide(&sort_sym_from,
-                                       symbol_conf.sym_from_list,
-                                       "sym_from", output);
-               sort_entry__setup_elide(&sort_sym_to,
-                                       symbol_conf.sym_to_list,
-                                       "sym_to", output);
-       } else if (sort__mode == SORT_MODE__MEMORY) {
-               sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
-                                       "symbol_daddr", output);
-               sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
-                                       "dso_daddr", output);
-               sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
-                                       "mem", output);
-               sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
-                                       "local_weight", output);
-               sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
-                                       "tlb", output);
-               sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
-                                       "snoop", output);
+       perf_hpp__for_each_format(fmt) {
+               if (!perf_hpp__is_sort_entry(fmt))
+                       continue;
+
+               hse = container_of(fmt, struct hpp_sort_entry, hpp);
+               fmt->elide = get_elide(hse->se->se_width_idx, output);
        }
 
        /*
@@ -1434,8 +1446,7 @@ void sort__setup_elide(FILE *output)
                if (!perf_hpp__is_sort_entry(fmt))
                        continue;
 
-               hse = container_of(fmt, struct hpp_sort_entry, hpp);
-               if (!hse->se->elide)
+               if (!fmt->elide)
                        return;
        }
 
@@ -1443,8 +1454,7 @@ void sort__setup_elide(FILE *output)
                if (!perf_hpp__is_sort_entry(fmt))
                        continue;
 
-               hse = container_of(fmt, struct hpp_sort_entry, hpp);
-               hse->se->elide = false;
+               fmt->elide = false;
        }
 }
 
@@ -1581,6 +1591,9 @@ void reset_output_field(void)
        sort__has_sym = 0;
        sort__has_dso = 0;
 
+       field_order = NULL;
+       sort_order = NULL;
+
        reset_dimensions();
        perf_hpp__reset_output_field();
 }
index 5f38d925e92f4f20917c11c44a9bb6274b7762fd..5bf0098d6b068921190c4b98256bf355cb092333 100644 (file)
@@ -20,7 +20,7 @@
 
 #include "parse-options.h"
 #include "parse-events.h"
-
+#include "hist.h"
 #include "thread.h"
 
 extern regex_t parent_regex;
@@ -82,6 +82,7 @@ struct hist_entry {
                struct list_head head;
        } pairs;
        struct he_stat          stat;
+       struct he_stat          *stat_acc;
        struct map_symbol       ms;
        struct thread           *thread;
        struct comm             *comm;
@@ -130,6 +131,21 @@ static inline void hist_entry__add_pair(struct hist_entry *pair,
        list_add_tail(&pair->pairs.node, &he->pairs.head);
 }
 
+static inline float hist_entry__get_percent_limit(struct hist_entry *he)
+{
+       u64 period = he->stat.period;
+       u64 total_period = hists__total_period(he->hists);
+
+       if (unlikely(total_period == 0))
+               return 0;
+
+       if (symbol_conf.cumulate_callchain)
+               period = he->stat_acc->period;
+
+       return period * 100.0 / total_period;
+}
+
+
 enum sort_mode {
        SORT_MODE__NORMAL,
        SORT_MODE__BRANCH,
@@ -186,7 +202,6 @@ struct sort_entry {
        int     (*se_snprintf)(struct hist_entry *he, char *bf, size_t size,
                               unsigned int width);
        u8      se_width_idx;
-       bool    elide;
 };
 
 extern struct sort_entry sort_thread;
@@ -197,6 +212,7 @@ int setup_output_field(void);
 void reset_output_field(void);
 extern int sort_dimension__add(const char *);
 void sort__setup_elide(FILE *fp);
+void perf_hpp__set_elide(int idx, bool elide);
 
 int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset);
 
index 95e249779931216f5cbeb5b09c26ea0655a0fcf7..7b9096f29cdbf7f8c2afabb041e2563b0e70c0da 100644 (file)
@@ -29,11 +29,12 @@ int vmlinux_path__nr_entries;
 char **vmlinux_path;
 
 struct symbol_conf symbol_conf = {
-       .use_modules      = true,
-       .try_vmlinux_path = true,
-       .annotate_src     = true,
-       .demangle         = true,
-       .symfs            = "",
+       .use_modules            = true,
+       .try_vmlinux_path       = true,
+       .annotate_src           = true,
+       .demangle               = true,
+       .cumulate_callchain     = true,
+       .symfs                  = "",
 };
 
 static enum dso_binary_type binary_type_symtab[] = {
index 33ede53fa6b9944940bec3047ec1789c375f11a0..615c752dd7673600b33f8e051f35ba85ce1cd866 100644 (file)
@@ -109,6 +109,7 @@ struct symbol_conf {
                        show_nr_samples,
                        show_total_period,
                        use_callchain,
+                       cumulate_callchain,
                        exclude_other,
                        show_cpu_utilization,
                        initialized,
index 750512ba2c8846d2d1275abb796b425fdd4a987d..c7493b8f9b0ee5b951d73d64e7502e809be2d5ed 100644 (file)
@@ -14,6 +14,12 @@ all: $(NET_PROGS)
 run_tests: all
        @/bin/sh ./run_netsocktests || echo "sockettests: [FAIL]"
        @/bin/sh ./run_afpackettests || echo "afpackettests: [FAIL]"
-
+       @if /sbin/modprobe test_bpf ; then \
+               /sbin/rmmod test_bpf; \
+               echo "test_bpf: ok"; \
+       else \
+               echo "test_bpf: [FAIL]"; \
+               exit 1; \
+       fi
 clean:
        $(RM) $(NET_PROGS)
index b3dbe9ef1a40ac0580905ed2812d22026850f449..54833a791a441d11b30c6fbb8bbcb8afafe5aa48 100644 (file)
@@ -13,7 +13,7 @@ CFLAGS := -Wall -O2 -flto -Wall -Werror -DGIT_VERSION='"$(GIT_VERSION)"' -I$(CUR
 
 export CC CFLAGS
 
-TARGETS = pmu copyloops mm
+TARGETS = pmu copyloops mm tm
 
 endif
 
index e80c42a584fe52d6cd73d3c39ee43174057bccb8..8ebc58a09311da9a591a7c8efd1fb2025049c2fe 100644 (file)
@@ -30,12 +30,15 @@ int run_test(int (test_function)(void), char *name)
 
        pid = fork();
        if (pid == 0) {
+               setpgid(0, 0);
                exit(test_function());
        } else if (pid == -1) {
                perror("fork");
                return 1;
        }
 
+       setpgid(pid, pid);
+
        /* Wake us up in timeout seconds */
        alarm(TIMEOUT);
        terminated = false;
@@ -50,17 +53,20 @@ wait:
 
                if (terminated) {
                        printf("!! force killing %s\n", name);
-                       kill(pid, SIGKILL);
+                       kill(-pid, SIGKILL);
                        return 1;
                } else {
                        printf("!! killing %s\n", name);
-                       kill(pid, SIGTERM);
+                       kill(-pid, SIGTERM);
                        terminated = true;
                        alarm(KILL_TIMEOUT);
                        goto wait;
                }
        }
 
+       /* Kill anything else in the process group that is still running */
+       kill(-pid, SIGTERM);
+
        if (WIFEXITED(status))
                status = WEXITSTATUS(status);
        else {
@@ -99,7 +105,10 @@ int test_harness(int (test_function)(void), char *name)
 
        rc = run_test(test_function, name);
 
-       test_finish(name, rc);
+       if (rc == MAGIC_SKIP_RETURN_VALUE)
+               test_skip(name);
+       else
+               test_finish(name, rc);
 
        return rc;
 }
index 7216f009165537053a198bf905de14e80bab3b2e..b9ff0db42c79a1e35ce9116bb73103b154419411 100644 (file)
@@ -4,7 +4,7 @@ noarg:
 PROGS := count_instructions
 EXTRA_SOURCES := ../harness.c event.c
 
-all: $(PROGS)
+all: $(PROGS) sub_all
 
 $(PROGS): $(EXTRA_SOURCES)
 
@@ -12,12 +12,30 @@ $(PROGS): $(EXTRA_SOURCES)
 count_instructions: loop.S count_instructions.c $(EXTRA_SOURCES)
        $(CC) $(CFLAGS) -m64 -o $@ $^
 
-run_tests: all
+run_tests: all sub_run_tests
        @-for PROG in $(PROGS); do \
                ./$$PROG; \
        done;
 
-clean:
+clean: sub_clean
        rm -f $(PROGS) loop.o
 
-.PHONY: all run_tests clean
+
+SUB_TARGETS = ebb
+
+sub_all:
+       @for TARGET in $(SUB_TARGETS); do \
+               $(MAKE) -C $$TARGET all; \
+       done;
+
+sub_run_tests: all
+       @for TARGET in $(SUB_TARGETS); do \
+               $(MAKE) -C $$TARGET run_tests; \
+       done;
+
+sub_clean:
+       @for TARGET in $(SUB_TARGETS); do \
+               $(MAKE) -C $$TARGET clean; \
+       done;
+
+.PHONY: all run_tests clean sub_all sub_run_tests sub_clean
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/Makefile b/tools/testing/selftests/powerpc/pmu/ebb/Makefile
new file mode 100644 (file)
index 0000000..edbba2a
--- /dev/null
@@ -0,0 +1,32 @@
+noarg:
+       $(MAKE) -C ../../
+
+# The EBB handler is 64-bit code and everything links against it
+CFLAGS += -m64
+
+PROGS := reg_access_test event_attributes_test cycles_test     \
+        cycles_with_freeze_test pmc56_overflow_test            \
+        ebb_vs_cpu_event_test cpu_event_vs_ebb_test            \
+        cpu_event_pinned_vs_ebb_test task_event_vs_ebb_test    \
+        task_event_pinned_vs_ebb_test multi_ebb_procs_test     \
+        multi_counter_test pmae_handling_test                  \
+        close_clears_pmcc_test instruction_count_test          \
+        fork_cleanup_test ebb_on_child_test                    \
+        ebb_on_willing_child_test back_to_back_ebbs_test       \
+        lost_exception_test no_handler_test
+
+all: $(PROGS)
+
+$(PROGS): ../../harness.c ../event.c ../lib.c ebb.c ebb_handler.S trace.c
+
+instruction_count_test: ../loop.S
+
+lost_exception_test: ../lib.c
+
+run_tests: all
+       @-for PROG in $(PROGS); do \
+               ./$$PROG; \
+       done;
+
+clean:
+       rm -f $(PROGS)
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c b/tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c
new file mode 100644 (file)
index 0000000..66ea765
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ebb.h"
+
+
+#define NUMBER_OF_EBBS 50
+
+/*
+ * Test that if we overflow the counter while in the EBB handler, we take
+ * another EBB on exiting from the handler.
+ *
+ * We do this by counting with a stupidly low sample period, causing us to
+ * overflow the PMU while we're still in the EBB handler, leading to another
+ * EBB.
+ *
+ * We get out of what would otherwise be an infinite loop by leaving the
+ * counter frozen once we've taken enough EBBs.
+ */
+
+static void ebb_callee(void)
+{
+       uint64_t siar, val;
+
+       val = mfspr(SPRN_BESCR);
+       if (!(val & BESCR_PMEO)) {
+               ebb_state.stats.spurious++;
+               goto out;
+       }
+
+       ebb_state.stats.ebb_count++;
+       trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count);
+
+       /* Resets the PMC */
+       count_pmc(1, sample_period);
+
+out:
+       if (ebb_state.stats.ebb_count == NUMBER_OF_EBBS)
+               /* Reset but leave counters frozen */
+               reset_ebb_with_clear_mask(MMCR0_PMAO);
+       else
+               /* Unfreezes */
+               reset_ebb();
+
+       /* Do some stuff to chew some cycles and pop the counter */
+       siar = mfspr(SPRN_SIAR);
+       trace_log_reg(ebb_state.trace, SPRN_SIAR, siar);
+
+       val = mfspr(SPRN_PMC1);
+       trace_log_reg(ebb_state.trace, SPRN_PMC1, val);
+
+       val = mfspr(SPRN_MMCR0);
+       trace_log_reg(ebb_state.trace, SPRN_MMCR0, val);
+}
+
+int back_to_back_ebbs(void)
+{
+       struct event event;
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open(&event));
+
+       setup_ebb_handler(ebb_callee);
+
+       FAIL_IF(ebb_event_enable(&event));
+
+       sample_period = 5;
+
+       ebb_freeze_pmcs();
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+       ebb_global_enable();
+       ebb_unfreeze_pmcs();
+
+       while (ebb_state.stats.ebb_count < NUMBER_OF_EBBS)
+               FAIL_IF(core_busy_loop());
+
+       ebb_global_disable();
+       ebb_freeze_pmcs();
+
+       count_pmc(1, sample_period);
+
+       dump_ebb_state();
+
+       event_close(&event);
+
+       FAIL_IF(ebb_state.stats.ebb_count != NUMBER_OF_EBBS);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(back_to_back_ebbs, "back_to_back_ebbs");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/close_clears_pmcc_test.c b/tools/testing/selftests/powerpc/pmu/ebb/close_clears_pmcc_test.c
new file mode 100644 (file)
index 0000000..0f0423d
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <signal.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test that closing the EBB event clears MMCR0_PMCC, preventing further access
+ * by userspace to the PMU hardware.
+ */
+
+int close_clears_pmcc(void)
+{
+       struct event event;
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       FAIL_IF(event_open(&event));
+
+       ebb_enable_pmc_counting(1);
+       setup_ebb_handler(standard_ebb_callee);
+       ebb_global_enable();
+       FAIL_IF(ebb_event_enable(&event));
+
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+       while (ebb_state.stats.ebb_count < 1)
+               FAIL_IF(core_busy_loop());
+
+       ebb_global_disable();
+       event_close(&event);
+
+       FAIL_IF(ebb_state.stats.ebb_count == 0);
+
+       /* The real test is here, do we take a SIGILL when writing PMU regs now
+        * that we have closed the event. We expect that we will. */
+
+       FAIL_IF(catch_sigill(write_pmc1));
+
+       /* We should still be able to read EBB regs though */
+       mfspr(SPRN_EBBHR);
+       mfspr(SPRN_EBBRR);
+       mfspr(SPRN_BESCR);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(close_clears_pmcc, "close_clears_pmcc");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c
new file mode 100644 (file)
index 0000000..d3ed64d
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests a pinned cpu event vs an EBB - in that order. The pinned cpu event
+ * should remain and the EBB event should fail to enable.
+ */
+
+static int setup_cpu_event(struct event *event, int cpu)
+{
+       event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
+
+       event->attr.pinned = 1;
+
+       event->attr.exclude_kernel = 1;
+       event->attr.exclude_hv = 1;
+       event->attr.exclude_idle = 1;
+
+       SKIP_IF(require_paranoia_below(1));
+       FAIL_IF(event_open_with_cpu(event, cpu));
+       FAIL_IF(event_enable(event));
+
+       return 0;
+}
+
+int cpu_event_pinned_vs_ebb(void)
+{
+       union pipe read_pipe, write_pipe;
+       struct event event;
+       int cpu, rc;
+       pid_t pid;
+
+       cpu = pick_online_cpu();
+       FAIL_IF(cpu < 0);
+       FAIL_IF(bind_to_cpu(cpu));
+
+       FAIL_IF(pipe(read_pipe.fds) == -1);
+       FAIL_IF(pipe(write_pipe.fds) == -1);
+
+       pid = fork();
+       if (pid == 0) {
+               /* NB order of pipes looks reversed */
+               exit(ebb_child(write_pipe, read_pipe));
+       }
+
+       /* We setup the cpu event first */
+       rc = setup_cpu_event(&event, cpu);
+       if (rc) {
+               kill_child_and_wait(pid);
+               return rc;
+       }
+
+       /* Signal the child to install its EBB event and wait */
+       if (sync_with_child(read_pipe, write_pipe))
+               /* If it fails, wait for it to exit */
+               goto wait;
+
+       /* Signal the child to run */
+       FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+wait:
+       /* We expect it to fail to read the event */
+       FAIL_IF(wait_for_child(pid) != 2);
+
+       FAIL_IF(event_disable(&event));
+       FAIL_IF(event_read(&event));
+
+       event_report(&event);
+
+       /* The cpu event should have run */
+       FAIL_IF(event.result.value == 0);
+       FAIL_IF(event.result.enabled != event.result.running);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(cpu_event_pinned_vs_ebb, "cpu_event_pinned_vs_ebb");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c
new file mode 100644 (file)
index 0000000..8b972c2
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests a cpu event vs an EBB - in that order. The EBB should force the cpu
+ * event off the PMU.
+ */
+
+static int setup_cpu_event(struct event *event, int cpu)
+{
+       event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
+
+       event->attr.exclude_kernel = 1;
+       event->attr.exclude_hv = 1;
+       event->attr.exclude_idle = 1;
+
+       SKIP_IF(require_paranoia_below(1));
+       FAIL_IF(event_open_with_cpu(event, cpu));
+       FAIL_IF(event_enable(event));
+
+       return 0;
+}
+
+int cpu_event_vs_ebb(void)
+{
+       union pipe read_pipe, write_pipe;
+       struct event event;
+       int cpu, rc;
+       pid_t pid;
+
+       cpu = pick_online_cpu();
+       FAIL_IF(cpu < 0);
+       FAIL_IF(bind_to_cpu(cpu));
+
+       FAIL_IF(pipe(read_pipe.fds) == -1);
+       FAIL_IF(pipe(write_pipe.fds) == -1);
+
+       pid = fork();
+       if (pid == 0) {
+               /* NB order of pipes looks reversed */
+               exit(ebb_child(write_pipe, read_pipe));
+       }
+
+       /* We setup the cpu event first */
+       rc = setup_cpu_event(&event, cpu);
+       if (rc) {
+               kill_child_and_wait(pid);
+               return rc;
+       }
+
+       /* Signal the child to install its EBB event and wait */
+       if (sync_with_child(read_pipe, write_pipe))
+               /* If it fails, wait for it to exit */
+               goto wait;
+
+       /* Signal the child to run */
+       FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+wait:
+       /* We expect the child to succeed */
+       FAIL_IF(wait_for_child(pid));
+
+       FAIL_IF(event_disable(&event));
+       FAIL_IF(event_read(&event));
+
+       event_report(&event);
+
+       /* The cpu event may have run */
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(cpu_event_vs_ebb, "cpu_event_vs_ebb");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c
new file mode 100644 (file)
index 0000000..8590fc1
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ebb.h"
+
+
+/*
+ * Basic test that counts user cycles and takes EBBs.
+ */
+int cycles(void)
+{
+       struct event event;
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open(&event));
+
+       ebb_enable_pmc_counting(1);
+       setup_ebb_handler(standard_ebb_callee);
+       ebb_global_enable();
+       FAIL_IF(ebb_event_enable(&event));
+
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+       while (ebb_state.stats.ebb_count < 10) {
+               FAIL_IF(core_busy_loop());
+               FAIL_IF(ebb_check_mmcr0());
+       }
+
+       ebb_global_disable();
+       ebb_freeze_pmcs();
+
+       count_pmc(1, sample_period);
+
+       dump_ebb_state();
+
+       event_close(&event);
+
+       FAIL_IF(ebb_state.stats.ebb_count == 0);
+       FAIL_IF(!ebb_check_count(1, sample_period, 100));
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(cycles, "cycles");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c
new file mode 100644 (file)
index 0000000..754b3f2
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test of counting cycles while using MMCR0_FC (freeze counters) to only count
+ * parts of the code. This is complicated by the fact that FC is set by the
+ * hardware when the event overflows. We may take the EBB after we have set FC,
+ * so we have to be careful about whether we clear FC at the end of the EBB
+ * handler or not.
+ */
+
+static bool counters_frozen = false;
+static int ebbs_while_frozen = 0;
+
+static void ebb_callee(void)
+{
+       uint64_t mask, val;
+
+       mask = MMCR0_PMAO | MMCR0_FC;
+
+       val = mfspr(SPRN_BESCR);
+       if (!(val & BESCR_PMEO)) {
+               ebb_state.stats.spurious++;
+               goto out;
+       }
+
+       ebb_state.stats.ebb_count++;
+       trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count);
+
+       val = mfspr(SPRN_MMCR0);
+       trace_log_reg(ebb_state.trace, SPRN_MMCR0, val);
+
+       if (counters_frozen) {
+               trace_log_string(ebb_state.trace, "frozen");
+               ebbs_while_frozen++;
+               mask &= ~MMCR0_FC;
+       }
+
+       count_pmc(1, sample_period);
+out:
+       reset_ebb_with_clear_mask(mask);
+}
+
+int cycles_with_freeze(void)
+{
+       struct event event;
+       uint64_t val;
+       bool fc_cleared;
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open(&event));
+
+       setup_ebb_handler(ebb_callee);
+       ebb_global_enable();
+       FAIL_IF(ebb_event_enable(&event));
+
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+       fc_cleared = false;
+
+       /* Make sure we loop until we take at least one EBB */
+       while ((ebb_state.stats.ebb_count < 20 && !fc_cleared) ||
+               ebb_state.stats.ebb_count < 1)
+       {
+               counters_frozen = false;
+               mb();
+               mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
+
+               FAIL_IF(core_busy_loop());
+
+               counters_frozen = true;
+               mb();
+               mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) |  MMCR0_FC);
+
+               val = mfspr(SPRN_MMCR0);
+               if (! (val & MMCR0_FC)) {
+                       printf("Outside of loop, FC NOT set MMCR0 0x%lx\n", val);
+                       fc_cleared = true;
+               }
+       }
+
+       ebb_global_disable();
+       ebb_freeze_pmcs();
+
+       count_pmc(1, sample_period);
+
+       dump_ebb_state();
+
+       printf("EBBs while frozen %d\n", ebbs_while_frozen);
+
+       event_close(&event);
+
+       FAIL_IF(ebb_state.stats.ebb_count == 0);
+       FAIL_IF(fc_cleared);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(cycles_with_freeze, "cycles_with_freeze");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb.c
new file mode 100644 (file)
index 0000000..1b46be9
--- /dev/null
@@ -0,0 +1,727 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#define _GNU_SOURCE    /* For CPU_ZERO etc. */
+
+#include <sched.h>
+#include <sys/wait.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include "trace.h"
+#include "reg.h"
+#include "ebb.h"
+
+
+void (*ebb_user_func)(void);
+
+void ebb_hook(void)
+{
+       if (ebb_user_func)
+               ebb_user_func();
+}
+
+struct ebb_state ebb_state;
+
+u64 sample_period = 0x40000000ull;
+
+void reset_ebb_with_clear_mask(unsigned long mmcr0_clear_mask)
+{
+       u64 val;
+
+       /* 2) clear MMCR0[PMAO] - docs say BESCR[PMEO] should do this */
+       /* 3) set MMCR0[PMAE]   - docs say BESCR[PME] should do this */
+       val = mfspr(SPRN_MMCR0);
+       mtspr(SPRN_MMCR0, (val & ~mmcr0_clear_mask) | MMCR0_PMAE);
+
+       /* 4) clear BESCR[PMEO] */
+       mtspr(SPRN_BESCRR, BESCR_PMEO);
+
+       /* 5) set BESCR[PME] */
+       mtspr(SPRN_BESCRS, BESCR_PME);
+
+       /* 6) rfebb 1 - done in our caller */
+}
+
+void reset_ebb(void)
+{
+       reset_ebb_with_clear_mask(MMCR0_PMAO | MMCR0_FC);
+}
+
+/* Called outside of the EBB handler to check MMCR0 is sane */
+int ebb_check_mmcr0(void)
+{
+       u64 val;
+
+       val = mfspr(SPRN_MMCR0);
+       if ((val & (MMCR0_FC | MMCR0_PMAO)) == MMCR0_FC) {
+               /* It's OK if we see FC & PMAO, but not FC by itself */
+               printf("Outside of loop, only FC set 0x%llx\n", val);
+               return 1;
+       }
+
+       return 0;
+}
+
+bool ebb_check_count(int pmc, u64 sample_period, int fudge)
+{
+       u64 count, upper, lower;
+
+       count = ebb_state.stats.pmc_count[PMC_INDEX(pmc)];
+
+       lower = ebb_state.stats.ebb_count * (sample_period - fudge);
+
+       if (count < lower) {
+               printf("PMC%d count (0x%llx) below lower limit 0x%llx (-0x%llx)\n",
+                       pmc, count, lower, lower - count);
+               return false;
+       }
+
+       upper = ebb_state.stats.ebb_count * (sample_period + fudge);
+
+       if (count > upper) {
+               printf("PMC%d count (0x%llx) above upper limit 0x%llx (+0x%llx)\n",
+                       pmc, count, upper, count - upper);
+               return false;
+       }
+
+       printf("PMC%d count (0x%llx) is between 0x%llx and 0x%llx delta +0x%llx/-0x%llx\n",
+               pmc, count, lower, upper, count - lower, upper - count);
+
+       return true;
+}
+
+void standard_ebb_callee(void)
+{
+       int found, i;
+       u64 val;
+
+       val = mfspr(SPRN_BESCR);
+       if (!(val & BESCR_PMEO)) {
+               ebb_state.stats.spurious++;
+               goto out;
+       }
+
+       ebb_state.stats.ebb_count++;
+       trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count);
+
+       val = mfspr(SPRN_MMCR0);
+       trace_log_reg(ebb_state.trace, SPRN_MMCR0, val);
+
+       found = 0;
+       for (i = 1; i <= 6; i++) {
+               if (ebb_state.pmc_enable[PMC_INDEX(i)])
+                       found += count_pmc(i, sample_period);
+       }
+
+       if (!found)
+               ebb_state.stats.no_overflow++;
+
+out:
+       reset_ebb();
+}
+
+extern void ebb_handler(void);
+
+void setup_ebb_handler(void (*callee)(void))
+{
+       u64 entry;
+
+#if defined(_CALL_ELF) && _CALL_ELF == 2
+       entry = (u64)ebb_handler;
+#else
+       struct opd
+       {
+           u64 entry;
+           u64 toc;
+       } *opd;
+
+       opd = (struct opd *)ebb_handler;
+       entry = opd->entry;
+#endif
+       printf("EBB Handler is at %#llx\n", entry);
+
+       ebb_user_func = callee;
+
+       /* Ensure ebb_user_func is set before we set the handler */
+       mb();
+       mtspr(SPRN_EBBHR, entry);
+
+       /* Make sure the handler is set before we return */
+       mb();
+}
+
+void clear_ebb_stats(void)
+{
+       memset(&ebb_state.stats, 0, sizeof(ebb_state.stats));
+}
+
+void dump_summary_ebb_state(void)
+{
+       printf("ebb_state:\n"                   \
+              "  ebb_count    = %d\n"          \
+              "  spurious     = %d\n"          \
+              "  negative     = %d\n"          \
+              "  no_overflow  = %d\n"          \
+              "  pmc[1] count = 0x%llx\n"      \
+              "  pmc[2] count = 0x%llx\n"      \
+              "  pmc[3] count = 0x%llx\n"      \
+              "  pmc[4] count = 0x%llx\n"      \
+              "  pmc[5] count = 0x%llx\n"      \
+              "  pmc[6] count = 0x%llx\n",
+               ebb_state.stats.ebb_count, ebb_state.stats.spurious,
+               ebb_state.stats.negative, ebb_state.stats.no_overflow,
+               ebb_state.stats.pmc_count[0], ebb_state.stats.pmc_count[1],
+               ebb_state.stats.pmc_count[2], ebb_state.stats.pmc_count[3],
+               ebb_state.stats.pmc_count[4], ebb_state.stats.pmc_count[5]);
+}
+
+static char *decode_mmcr0(u32 value)
+{
+       static char buf[16];
+
+       buf[0] = '\0';
+
+       if (value & (1 << 31))
+               strcat(buf, "FC ");
+       if (value & (1 << 26))
+               strcat(buf, "PMAE ");
+       if (value & (1 << 7))
+               strcat(buf, "PMAO ");
+
+       return buf;
+}
+
+static char *decode_bescr(u64 value)
+{
+       static char buf[16];
+
+       buf[0] = '\0';
+
+       if (value & (1ull << 63))
+               strcat(buf, "GE ");
+       if (value & (1ull << 32))
+               strcat(buf, "PMAE ");
+       if (value & 1)
+               strcat(buf, "PMAO ");
+
+       return buf;
+}
+
+void dump_ebb_hw_state(void)
+{
+       u64 bescr;
+       u32 mmcr0;
+
+       mmcr0 = mfspr(SPRN_MMCR0);
+       bescr = mfspr(SPRN_BESCR);
+
+       printf("HW state:\n"            \
+              "MMCR0 0x%016x %s\n"     \
+              "EBBHR 0x%016lx\n"       \
+              "BESCR 0x%016llx %s\n"   \
+              "PMC1  0x%016lx\n"       \
+              "PMC2  0x%016lx\n"       \
+              "PMC3  0x%016lx\n"       \
+              "PMC4  0x%016lx\n"       \
+              "PMC5  0x%016lx\n"       \
+              "PMC6  0x%016lx\n"       \
+              "SIAR  0x%016lx\n",
+              mmcr0, decode_mmcr0(mmcr0), mfspr(SPRN_EBBHR), bescr,
+              decode_bescr(bescr), mfspr(SPRN_PMC1), mfspr(SPRN_PMC2),
+              mfspr(SPRN_PMC3), mfspr(SPRN_PMC4), mfspr(SPRN_PMC5),
+              mfspr(SPRN_PMC6), mfspr(SPRN_SIAR));
+}
+
+void dump_ebb_state(void)
+{
+       dump_summary_ebb_state();
+
+       dump_ebb_hw_state();
+
+       trace_buffer_print(ebb_state.trace);
+}
+
+int count_pmc(int pmc, uint32_t sample_period)
+{
+       uint32_t start_value;
+       u64 val;
+
+       /* 0) Read PMC */
+       start_value = pmc_sample_period(sample_period);
+
+       val = read_pmc(pmc);
+       if (val < start_value)
+               ebb_state.stats.negative++;
+       else
+               ebb_state.stats.pmc_count[PMC_INDEX(pmc)] += val - start_value;
+
+       trace_log_reg(ebb_state.trace, SPRN_PMC1 + pmc - 1, val);
+
+       /* 1) Reset PMC */
+       write_pmc(pmc, start_value);
+
+       /* Report if we overflowed */
+       return val >= COUNTER_OVERFLOW;
+}
+
+int ebb_event_enable(struct event *e)
+{
+       int rc;
+
+       /* Ensure any SPR writes are ordered vs us */
+       mb();
+
+       rc = ioctl(e->fd, PERF_EVENT_IOC_ENABLE);
+       if (rc)
+               return rc;
+
+       rc = event_read(e);
+
+       /* Ditto */
+       mb();
+
+       return rc;
+}
+
+void ebb_freeze_pmcs(void)
+{
+       mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC);
+       mb();
+}
+
+void ebb_unfreeze_pmcs(void)
+{
+       /* Unfreeze counters */
+       mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
+       mb();
+}
+
+void ebb_global_enable(void)
+{
+       /* Enable EBBs globally and PMU EBBs */
+       mtspr(SPRN_BESCR, 0x8000000100000000ull);
+       mb();
+}
+
+void ebb_global_disable(void)
+{
+       /* Disable EBBs & freeze counters, events are still scheduled */
+       mtspr(SPRN_BESCRR, BESCR_PME);
+       mb();
+}
+
+void event_ebb_init(struct event *e)
+{
+       e->attr.config |= (1ull << 63);
+}
+
+void event_bhrb_init(struct event *e, unsigned ifm)
+{
+       e->attr.config |= (1ull << 62) | ((u64)ifm << 60);
+}
+
+void event_leader_ebb_init(struct event *e)
+{
+       event_ebb_init(e);
+
+       e->attr.exclusive = 1;
+       e->attr.pinned = 1;
+}
+
+int core_busy_loop(void)
+{
+       int rc;
+
+       asm volatile (
+               "li  3,  0x3030\n"
+               "std 3,  -96(1)\n"
+               "li  4,  0x4040\n"
+               "std 4,  -104(1)\n"
+               "li  5,  0x5050\n"
+               "std 5,  -112(1)\n"
+               "li  6,  0x6060\n"
+               "std 6,  -120(1)\n"
+               "li  7,  0x7070\n"
+               "std 7,  -128(1)\n"
+               "li  8,  0x0808\n"
+               "std 8,  -136(1)\n"
+               "li  9,  0x0909\n"
+               "std 9,  -144(1)\n"
+               "li  10, 0x1010\n"
+               "std 10, -152(1)\n"
+               "li  11, 0x1111\n"
+               "std 11, -160(1)\n"
+               "li  14, 0x1414\n"
+               "std 14, -168(1)\n"
+               "li  15, 0x1515\n"
+               "std 15, -176(1)\n"
+               "li  16, 0x1616\n"
+               "std 16, -184(1)\n"
+               "li  17, 0x1717\n"
+               "std 17, -192(1)\n"
+               "li  18, 0x1818\n"
+               "std 18, -200(1)\n"
+               "li  19, 0x1919\n"
+               "std 19, -208(1)\n"
+               "li  20, 0x2020\n"
+               "std 20, -216(1)\n"
+               "li  21, 0x2121\n"
+               "std 21, -224(1)\n"
+               "li  22, 0x2222\n"
+               "std 22, -232(1)\n"
+               "li  23, 0x2323\n"
+               "std 23, -240(1)\n"
+               "li  24, 0x2424\n"
+               "std 24, -248(1)\n"
+               "li  25, 0x2525\n"
+               "std 25, -256(1)\n"
+               "li  26, 0x2626\n"
+               "std 26, -264(1)\n"
+               "li  27, 0x2727\n"
+               "std 27, -272(1)\n"
+               "li  28, 0x2828\n"
+               "std 28, -280(1)\n"
+               "li  29, 0x2929\n"
+               "std 29, -288(1)\n"
+               "li  30, 0x3030\n"
+               "li  31, 0x3131\n"
+
+               "li    3,  0\n"
+               "0: "
+               "addi  3, 3, 1\n"
+               "cmpwi 3, 100\n"
+               "blt   0b\n"
+
+               /* Return 1 (fail) unless we get through all the checks */
+               "li     0, 1\n"
+
+               /* Check none of our registers have been corrupted */
+               "cmpwi  4,  0x4040\n"
+               "bne    1f\n"
+               "cmpwi  5,  0x5050\n"
+               "bne    1f\n"
+               "cmpwi  6,  0x6060\n"
+               "bne    1f\n"
+               "cmpwi  7,  0x7070\n"
+               "bne    1f\n"
+               "cmpwi  8,  0x0808\n"
+               "bne    1f\n"
+               "cmpwi  9,  0x0909\n"
+               "bne    1f\n"
+               "cmpwi  10, 0x1010\n"
+               "bne    1f\n"
+               "cmpwi  11, 0x1111\n"
+               "bne    1f\n"
+               "cmpwi  14, 0x1414\n"
+               "bne    1f\n"
+               "cmpwi  15, 0x1515\n"
+               "bne    1f\n"
+               "cmpwi  16, 0x1616\n"
+               "bne    1f\n"
+               "cmpwi  17, 0x1717\n"
+               "bne    1f\n"
+               "cmpwi  18, 0x1818\n"
+               "bne    1f\n"
+               "cmpwi  19, 0x1919\n"
+               "bne    1f\n"
+               "cmpwi  20, 0x2020\n"
+               "bne    1f\n"
+               "cmpwi  21, 0x2121\n"
+               "bne    1f\n"
+               "cmpwi  22, 0x2222\n"
+               "bne    1f\n"
+               "cmpwi  23, 0x2323\n"
+               "bne    1f\n"
+               "cmpwi  24, 0x2424\n"
+               "bne    1f\n"
+               "cmpwi  25, 0x2525\n"
+               "bne    1f\n"
+               "cmpwi  26, 0x2626\n"
+               "bne    1f\n"
+               "cmpwi  27, 0x2727\n"
+               "bne    1f\n"
+               "cmpwi  28, 0x2828\n"
+               "bne    1f\n"
+               "cmpwi  29, 0x2929\n"
+               "bne    1f\n"
+               "cmpwi  30, 0x3030\n"
+               "bne    1f\n"
+               "cmpwi  31, 0x3131\n"
+               "bne    1f\n"
+
+               /* Load junk into all our registers before we reload them from the stack. */
+               "li  3,  0xde\n"
+               "li  4,  0xad\n"
+               "li  5,  0xbe\n"
+               "li  6,  0xef\n"
+               "li  7,  0xde\n"
+               "li  8,  0xad\n"
+               "li  9,  0xbe\n"
+               "li  10, 0xef\n"
+               "li  11, 0xde\n"
+               "li  14, 0xad\n"
+               "li  15, 0xbe\n"
+               "li  16, 0xef\n"
+               "li  17, 0xde\n"
+               "li  18, 0xad\n"
+               "li  19, 0xbe\n"
+               "li  20, 0xef\n"
+               "li  21, 0xde\n"
+               "li  22, 0xad\n"
+               "li  23, 0xbe\n"
+               "li  24, 0xef\n"
+               "li  25, 0xde\n"
+               "li  26, 0xad\n"
+               "li  27, 0xbe\n"
+               "li  28, 0xef\n"
+               "li  29, 0xdd\n"
+
+               "ld     3,  -96(1)\n"
+               "cmpwi  3,  0x3030\n"
+               "bne    1f\n"
+               "ld     4,  -104(1)\n"
+               "cmpwi  4,  0x4040\n"
+               "bne    1f\n"
+               "ld     5,  -112(1)\n"
+               "cmpwi  5,  0x5050\n"
+               "bne    1f\n"
+               "ld     6,  -120(1)\n"
+               "cmpwi  6,  0x6060\n"
+               "bne    1f\n"
+               "ld     7,  -128(1)\n"
+               "cmpwi  7,  0x7070\n"
+               "bne    1f\n"
+               "ld     8,  -136(1)\n"
+               "cmpwi  8,  0x0808\n"
+               "bne    1f\n"
+               "ld     9,  -144(1)\n"
+               "cmpwi  9,  0x0909\n"
+               "bne    1f\n"
+               "ld     10, -152(1)\n"
+               "cmpwi  10, 0x1010\n"
+               "bne    1f\n"
+               "ld     11, -160(1)\n"
+               "cmpwi  11, 0x1111\n"
+               "bne    1f\n"
+               "ld     14, -168(1)\n"
+               "cmpwi  14, 0x1414\n"
+               "bne    1f\n"
+               "ld     15, -176(1)\n"
+               "cmpwi  15, 0x1515\n"
+               "bne    1f\n"
+               "ld     16, -184(1)\n"
+               "cmpwi  16, 0x1616\n"
+               "bne    1f\n"
+               "ld     17, -192(1)\n"
+               "cmpwi  17, 0x1717\n"
+               "bne    1f\n"
+               "ld     18, -200(1)\n"
+               "cmpwi  18, 0x1818\n"
+               "bne    1f\n"
+               "ld     19, -208(1)\n"
+               "cmpwi  19, 0x1919\n"
+               "bne    1f\n"
+               "ld     20, -216(1)\n"
+               "cmpwi  20, 0x2020\n"
+               "bne    1f\n"
+               "ld     21, -224(1)\n"
+               "cmpwi  21, 0x2121\n"
+               "bne    1f\n"
+               "ld     22, -232(1)\n"
+               "cmpwi  22, 0x2222\n"
+               "bne    1f\n"
+               "ld     23, -240(1)\n"
+               "cmpwi  23, 0x2323\n"
+               "bne    1f\n"
+               "ld     24, -248(1)\n"
+               "cmpwi  24, 0x2424\n"
+               "bne    1f\n"
+               "ld     25, -256(1)\n"
+               "cmpwi  25, 0x2525\n"
+               "bne    1f\n"
+               "ld     26, -264(1)\n"
+               "cmpwi  26, 0x2626\n"
+               "bne    1f\n"
+               "ld     27, -272(1)\n"
+               "cmpwi  27, 0x2727\n"
+               "bne    1f\n"
+               "ld     28, -280(1)\n"
+               "cmpwi  28, 0x2828\n"
+               "bne    1f\n"
+               "ld     29, -288(1)\n"
+               "cmpwi  29, 0x2929\n"
+               "bne    1f\n"
+
+               /* Load 0 (success) to return */
+               "li     0, 0\n"
+
+               "1:     mr %0, 0\n"
+
+               : "=r" (rc)
+               : /* no inputs */
+               : "3", "4", "5", "6", "7", "8", "9", "10", "11", "14",
+                 "15", "16", "17", "18", "19", "20", "21", "22", "23",
+                  "24", "25", "26", "27", "28", "29", "30", "31",
+                  "memory"
+       );
+
+       return rc;
+}
+
+int core_busy_loop_with_freeze(void)
+{
+       int rc;
+
+       mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
+       rc = core_busy_loop();
+       mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) |  MMCR0_FC);
+
+       return rc;
+}
+
+int ebb_child(union pipe read_pipe, union pipe write_pipe)
+{
+       struct event event;
+       uint64_t val;
+
+       FAIL_IF(wait_for_parent(read_pipe));
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open(&event));
+
+       ebb_enable_pmc_counting(1);
+       setup_ebb_handler(standard_ebb_callee);
+       ebb_global_enable();
+
+       FAIL_IF(event_enable(&event));
+
+       if (event_read(&event)) {
+               /*
+                * Some tests expect to fail here, so don't report an error on
+                * this line, and return a distinguisable error code. Tell the
+                * parent an error happened.
+                */
+               notify_parent_of_error(write_pipe);
+               return 2;
+       }
+
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+       FAIL_IF(notify_parent(write_pipe));
+       FAIL_IF(wait_for_parent(read_pipe));
+       FAIL_IF(notify_parent(write_pipe));
+
+       while (ebb_state.stats.ebb_count < 20) {
+               FAIL_IF(core_busy_loop());
+
+               /* To try and hit SIGILL case */
+               val  = mfspr(SPRN_MMCRA);
+               val |= mfspr(SPRN_MMCR2);
+               val |= mfspr(SPRN_MMCR0);
+       }
+
+       ebb_global_disable();
+       ebb_freeze_pmcs();
+
+       count_pmc(1, sample_period);
+
+       dump_ebb_state();
+
+       event_close(&event);
+
+       FAIL_IF(ebb_state.stats.ebb_count == 0);
+
+       return 0;
+}
+
+static jmp_buf setjmp_env;
+
+static void sigill_handler(int signal)
+{
+       printf("Took sigill\n");
+       longjmp(setjmp_env, 1);
+}
+
+static struct sigaction sigill_action = {
+       .sa_handler = sigill_handler,
+};
+
+int catch_sigill(void (*func)(void))
+{
+       if (sigaction(SIGILL, &sigill_action, NULL)) {
+               perror("sigaction");
+               return 1;
+       }
+
+       if (setjmp(setjmp_env) == 0) {
+               func();
+               return 1;
+       }
+
+       return 0;
+}
+
+void write_pmc1(void)
+{
+       mtspr(SPRN_PMC1, 0);
+}
+
+void write_pmc(int pmc, u64 value)
+{
+       switch (pmc) {
+               case 1: mtspr(SPRN_PMC1, value); break;
+               case 2: mtspr(SPRN_PMC2, value); break;
+               case 3: mtspr(SPRN_PMC3, value); break;
+               case 4: mtspr(SPRN_PMC4, value); break;
+               case 5: mtspr(SPRN_PMC5, value); break;
+               case 6: mtspr(SPRN_PMC6, value); break;
+       }
+}
+
+u64 read_pmc(int pmc)
+{
+       switch (pmc) {
+               case 1: return mfspr(SPRN_PMC1);
+               case 2: return mfspr(SPRN_PMC2);
+               case 3: return mfspr(SPRN_PMC3);
+               case 4: return mfspr(SPRN_PMC4);
+               case 5: return mfspr(SPRN_PMC5);
+               case 6: return mfspr(SPRN_PMC6);
+       }
+
+       return 0;
+}
+
+static void term_handler(int signal)
+{
+       dump_summary_ebb_state();
+       dump_ebb_hw_state();
+       abort();
+}
+
+struct sigaction term_action = {
+       .sa_handler = term_handler,
+};
+
+static void __attribute__((constructor)) ebb_init(void)
+{
+       clear_ebb_stats();
+
+       if (sigaction(SIGTERM, &term_action, NULL))
+               perror("sigaction");
+
+       ebb_state.trace = trace_buffer_allocate(1 * 1024 * 1024);
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb.h b/tools/testing/selftests/powerpc/pmu/ebb/ebb.h
new file mode 100644 (file)
index 0000000..e62bde0
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#ifndef _SELFTESTS_POWERPC_PMU_EBB_EBB_H
+#define _SELFTESTS_POWERPC_PMU_EBB_EBB_H
+
+#include "../event.h"
+#include "../lib.h"
+#include "trace.h"
+#include "reg.h"
+
+#define PMC_INDEX(pmc) ((pmc)-1)
+
+#define NUM_PMC_VALUES 128
+
+struct ebb_state
+{
+       struct {
+               u64 pmc_count[6];
+               volatile int ebb_count;
+               int spurious;
+               int negative;
+               int no_overflow;
+       } stats;
+
+       bool pmc_enable[6];
+       struct trace_buffer *trace;
+};
+
+extern struct ebb_state ebb_state;
+
+#define COUNTER_OVERFLOW 0x80000000ull
+
+static inline uint32_t pmc_sample_period(uint32_t value)
+{
+       return COUNTER_OVERFLOW - value;
+}
+
+static inline void ebb_enable_pmc_counting(int pmc)
+{
+       ebb_state.pmc_enable[PMC_INDEX(pmc)] = true;
+}
+
+bool ebb_check_count(int pmc, u64 sample_period, int fudge);
+void event_leader_ebb_init(struct event *e);
+void event_ebb_init(struct event *e);
+void event_bhrb_init(struct event *e, unsigned ifm);
+void setup_ebb_handler(void (*callee)(void));
+void standard_ebb_callee(void);
+int ebb_event_enable(struct event *e);
+void ebb_global_enable(void);
+void ebb_global_disable(void);
+void ebb_freeze_pmcs(void);
+void ebb_unfreeze_pmcs(void);
+void event_ebb_init(struct event *e);
+void event_leader_ebb_init(struct event *e);
+int count_pmc(int pmc, uint32_t sample_period);
+void dump_ebb_state(void);
+void dump_summary_ebb_state(void);
+void dump_ebb_hw_state(void);
+void clear_ebb_stats(void);
+void write_pmc(int pmc, u64 value);
+u64 read_pmc(int pmc);
+void reset_ebb_with_clear_mask(unsigned long mmcr0_clear_mask);
+void reset_ebb(void);
+int ebb_check_mmcr0(void);
+
+extern u64 sample_period;
+
+int core_busy_loop(void);
+int core_busy_loop_with_freeze(void);
+int ebb_child(union pipe read_pipe, union pipe write_pipe);
+int catch_sigill(void (*func)(void));
+void write_pmc1(void);
+
+#endif /* _SELFTESTS_POWERPC_PMU_EBB_EBB_H */
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_handler.S b/tools/testing/selftests/powerpc/pmu/ebb/ebb_handler.S
new file mode 100644 (file)
index 0000000..14274ea
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <ppc-asm.h>
+#include "reg.h"
+
+
+/* ppc-asm.h defines most of the reg aliases, but not r1/r2. */
+#define r1 1
+#define r2 2
+
+#define RFEBB   .long 0x4c000924
+
+/* Stack layout:
+ *
+ *                   ^
+ *  User stack       |
+ *  Back chain ------+ <- r1           <-------+
+ *  ...                                                |
+ *  Red zone / ABI Gap                         |
+ *  ...                                                |
+ *  vr63       <+                              |
+ *  vr0                 |                              |
+ *  VSCR        |                              |
+ *  FSCR        |                              |
+ *  r31                 | Save area                    |
+ *  r0          |                              |
+ *  XER                 |                              |
+ *  CTR                 |                              |
+ *  LR          |                              |
+ *  CCR                <+                              |
+ *  ...                <+                              |
+ *  LR          | Caller frame                 |
+ *  CCR                 |                              |
+ *  Back chain <+      <- updated r1   --------+
+ *
+ */
+
+#if defined(_CALL_ELF) && _CALL_ELF == 2
+#define ABIGAP         512
+#else
+#define ABIGAP         288
+#endif
+
+#define NR_GPR         32
+#define NR_SPR         6
+#define NR_VSR         64
+
+#define SAVE_AREA      ((NR_GPR + NR_SPR) * 8 + (NR_VSR * 16))
+#define CALLER_FRAME   112
+
+#define STACK_FRAME    (ABIGAP + SAVE_AREA + CALLER_FRAME)
+
+#define CCR_SAVE       (CALLER_FRAME)
+#define LR_SAVE                (CCR_SAVE + 8)
+#define CTR_SAVE       (LR_SAVE  + 8)
+#define XER_SAVE       (CTR_SAVE + 8)
+#define GPR_SAVE(n)    (XER_SAVE + 8 + (8 * n))
+#define FSCR_SAVE      (GPR_SAVE(31) + 8)
+#define VSCR_SAVE      (FSCR_SAVE + 8)
+#define VSR_SAVE(n)    (VSCR_SAVE + 8 + (16 * n))
+
+#define SAVE_GPR(n)    std n,GPR_SAVE(n)(r1)
+#define REST_GPR(n)    ld  n,GPR_SAVE(n)(r1)
+#define TRASH_GPR(n)   lis n,0xaaaa
+
+#define SAVE_VSR(n, b) li b, VSR_SAVE(n); stxvd2x n,b,r1
+#define LOAD_VSR(n, b) li b, VSR_SAVE(n); lxvd2x  n,b,r1
+
+#define LOAD_REG_IMMEDIATE(reg,expr)   \
+       lis     reg,(expr)@highest;     \
+       ori     reg,reg,(expr)@higher;  \
+       rldicr  reg,reg,32,31;          \
+       oris    reg,reg,(expr)@h;       \
+       ori     reg,reg,(expr)@l;
+
+
+#if defined(_CALL_ELF) && _CALL_ELF == 2
+#define ENTRY_POINT(name) \
+       .type FUNC_NAME(name),@function; \
+       .globl FUNC_NAME(name); \
+       FUNC_NAME(name):
+
+#define RESTORE_TOC(name)      \
+       /* Restore our TOC pointer using our entry point */     \
+       LOAD_REG_IMMEDIATE(r12, name)                           \
+0:     addis   r2,r12,(.TOC.-0b)@ha;                           \
+       addi    r2,r2,(.TOC.-0b)@l;
+
+#else
+#define ENTRY_POINT(name) FUNC_START(name)
+#define RESTORE_TOC(name)      \
+       /* Restore our TOC pointer via our opd entry */ \
+       LOAD_REG_IMMEDIATE(r2, name)                    \
+       ld      r2,8(r2);
+#endif
+
+    .text
+
+ENTRY_POINT(ebb_handler)
+    stdu    r1,-STACK_FRAME(r1)
+    SAVE_GPR(0)
+    mflr    r0
+    std     r0,LR_SAVE(r1)
+    mfcr    r0
+    std     r0,CCR_SAVE(r1)
+    mfctr   r0
+    std     r0,CTR_SAVE(r1)
+    mfxer   r0
+    std     r0,XER_SAVE(r1)
+    SAVE_GPR(2)
+    SAVE_GPR(3)
+    SAVE_GPR(4)
+    SAVE_GPR(5)
+    SAVE_GPR(6)
+    SAVE_GPR(7)
+    SAVE_GPR(8)
+    SAVE_GPR(9)
+    SAVE_GPR(10)
+    SAVE_GPR(11)
+    SAVE_GPR(12)
+    SAVE_GPR(13)
+    SAVE_GPR(14)
+    SAVE_GPR(15)
+    SAVE_GPR(16)
+    SAVE_GPR(17)
+    SAVE_GPR(18)
+    SAVE_GPR(19)
+    SAVE_GPR(20)
+    SAVE_GPR(21)
+    SAVE_GPR(22)
+    SAVE_GPR(23)
+    SAVE_GPR(24)
+    SAVE_GPR(25)
+    SAVE_GPR(26)
+    SAVE_GPR(27)
+    SAVE_GPR(28)
+    SAVE_GPR(29)
+    SAVE_GPR(30)
+    SAVE_GPR(31)
+    SAVE_VSR(0, r3)
+    mffs     f0
+    stfd     f0, FSCR_SAVE(r1)
+    mfvscr   f0
+    stfd     f0, VSCR_SAVE(r1)
+    SAVE_VSR(1,  r3)
+    SAVE_VSR(2,  r3)
+    SAVE_VSR(3,  r3)
+    SAVE_VSR(4,  r3)
+    SAVE_VSR(5,  r3)
+    SAVE_VSR(6,  r3)
+    SAVE_VSR(7,  r3)
+    SAVE_VSR(8,  r3)
+    SAVE_VSR(9,  r3)
+    SAVE_VSR(10, r3)
+    SAVE_VSR(11, r3)
+    SAVE_VSR(12, r3)
+    SAVE_VSR(13, r3)
+    SAVE_VSR(14, r3)
+    SAVE_VSR(15, r3)
+    SAVE_VSR(16, r3)
+    SAVE_VSR(17, r3)
+    SAVE_VSR(18, r3)
+    SAVE_VSR(19, r3)
+    SAVE_VSR(20, r3)
+    SAVE_VSR(21, r3)
+    SAVE_VSR(22, r3)
+    SAVE_VSR(23, r3)
+    SAVE_VSR(24, r3)
+    SAVE_VSR(25, r3)
+    SAVE_VSR(26, r3)
+    SAVE_VSR(27, r3)
+    SAVE_VSR(28, r3)
+    SAVE_VSR(29, r3)
+    SAVE_VSR(30, r3)
+    SAVE_VSR(31, r3)
+    SAVE_VSR(32, r3)
+    SAVE_VSR(33, r3)
+    SAVE_VSR(34, r3)
+    SAVE_VSR(35, r3)
+    SAVE_VSR(36, r3)
+    SAVE_VSR(37, r3)
+    SAVE_VSR(38, r3)
+    SAVE_VSR(39, r3)
+    SAVE_VSR(40, r3)
+    SAVE_VSR(41, r3)
+    SAVE_VSR(42, r3)
+    SAVE_VSR(43, r3)
+    SAVE_VSR(44, r3)
+    SAVE_VSR(45, r3)
+    SAVE_VSR(46, r3)
+    SAVE_VSR(47, r3)
+    SAVE_VSR(48, r3)
+    SAVE_VSR(49, r3)
+    SAVE_VSR(50, r3)
+    SAVE_VSR(51, r3)
+    SAVE_VSR(52, r3)
+    SAVE_VSR(53, r3)
+    SAVE_VSR(54, r3)
+    SAVE_VSR(55, r3)
+    SAVE_VSR(56, r3)
+    SAVE_VSR(57, r3)
+    SAVE_VSR(58, r3)
+    SAVE_VSR(59, r3)
+    SAVE_VSR(60, r3)
+    SAVE_VSR(61, r3)
+    SAVE_VSR(62, r3)
+    SAVE_VSR(63, r3)
+
+    TRASH_GPR(2)
+    TRASH_GPR(3)
+    TRASH_GPR(4)
+    TRASH_GPR(5)
+    TRASH_GPR(6)
+    TRASH_GPR(7)
+    TRASH_GPR(8)
+    TRASH_GPR(9)
+    TRASH_GPR(10)
+    TRASH_GPR(11)
+    TRASH_GPR(12)
+    TRASH_GPR(14)
+    TRASH_GPR(15)
+    TRASH_GPR(16)
+    TRASH_GPR(17)
+    TRASH_GPR(18)
+    TRASH_GPR(19)
+    TRASH_GPR(20)
+    TRASH_GPR(21)
+    TRASH_GPR(22)
+    TRASH_GPR(23)
+    TRASH_GPR(24)
+    TRASH_GPR(25)
+    TRASH_GPR(26)
+    TRASH_GPR(27)
+    TRASH_GPR(28)
+    TRASH_GPR(29)
+    TRASH_GPR(30)
+    TRASH_GPR(31)
+
+    RESTORE_TOC(ebb_handler)
+
+    /*
+     * r13 is our TLS pointer. We leave whatever value was in there when the
+     * EBB fired. That seems to be OK because once set the TLS pointer is not
+     * changed - but presumably that could change in future.
+     */
+
+    bl      ebb_hook
+    nop
+
+    /* r2 may be changed here but we don't care */
+
+    lfd      f0, FSCR_SAVE(r1)
+    mtfsf    0xff,f0
+    lfd      f0, VSCR_SAVE(r1)
+    mtvscr   f0
+    LOAD_VSR(0, r3)
+    LOAD_VSR(1,  r3)
+    LOAD_VSR(2,  r3)
+    LOAD_VSR(3,  r3)
+    LOAD_VSR(4,  r3)
+    LOAD_VSR(5,  r3)
+    LOAD_VSR(6,  r3)
+    LOAD_VSR(7,  r3)
+    LOAD_VSR(8,  r3)
+    LOAD_VSR(9,  r3)
+    LOAD_VSR(10, r3)
+    LOAD_VSR(11, r3)
+    LOAD_VSR(12, r3)
+    LOAD_VSR(13, r3)
+    LOAD_VSR(14, r3)
+    LOAD_VSR(15, r3)
+    LOAD_VSR(16, r3)
+    LOAD_VSR(17, r3)
+    LOAD_VSR(18, r3)
+    LOAD_VSR(19, r3)
+    LOAD_VSR(20, r3)
+    LOAD_VSR(21, r3)
+    LOAD_VSR(22, r3)
+    LOAD_VSR(23, r3)
+    LOAD_VSR(24, r3)
+    LOAD_VSR(25, r3)
+    LOAD_VSR(26, r3)
+    LOAD_VSR(27, r3)
+    LOAD_VSR(28, r3)
+    LOAD_VSR(29, r3)
+    LOAD_VSR(30, r3)
+    LOAD_VSR(31, r3)
+    LOAD_VSR(32, r3)
+    LOAD_VSR(33, r3)
+    LOAD_VSR(34, r3)
+    LOAD_VSR(35, r3)
+    LOAD_VSR(36, r3)
+    LOAD_VSR(37, r3)
+    LOAD_VSR(38, r3)
+    LOAD_VSR(39, r3)
+    LOAD_VSR(40, r3)
+    LOAD_VSR(41, r3)
+    LOAD_VSR(42, r3)
+    LOAD_VSR(43, r3)
+    LOAD_VSR(44, r3)
+    LOAD_VSR(45, r3)
+    LOAD_VSR(46, r3)
+    LOAD_VSR(47, r3)
+    LOAD_VSR(48, r3)
+    LOAD_VSR(49, r3)
+    LOAD_VSR(50, r3)
+    LOAD_VSR(51, r3)
+    LOAD_VSR(52, r3)
+    LOAD_VSR(53, r3)
+    LOAD_VSR(54, r3)
+    LOAD_VSR(55, r3)
+    LOAD_VSR(56, r3)
+    LOAD_VSR(57, r3)
+    LOAD_VSR(58, r3)
+    LOAD_VSR(59, r3)
+    LOAD_VSR(60, r3)
+    LOAD_VSR(61, r3)
+    LOAD_VSR(62, r3)
+    LOAD_VSR(63, r3)
+
+    ld      r0,XER_SAVE(r1)
+    mtxer   r0
+    ld      r0,CTR_SAVE(r1)
+    mtctr   r0
+    ld      r0,LR_SAVE(r1)
+    mtlr    r0
+    ld      r0,CCR_SAVE(r1)
+    mtcr    r0
+    REST_GPR(0)
+    REST_GPR(2)
+    REST_GPR(3)
+    REST_GPR(4)
+    REST_GPR(5)
+    REST_GPR(6)
+    REST_GPR(7)
+    REST_GPR(8)
+    REST_GPR(9)
+    REST_GPR(10)
+    REST_GPR(11)
+    REST_GPR(12)
+    REST_GPR(13)
+    REST_GPR(14)
+    REST_GPR(15)
+    REST_GPR(16)
+    REST_GPR(17)
+    REST_GPR(18)
+    REST_GPR(19)
+    REST_GPR(20)
+    REST_GPR(21)
+    REST_GPR(22)
+    REST_GPR(23)
+    REST_GPR(24)
+    REST_GPR(25)
+    REST_GPR(26)
+    REST_GPR(27)
+    REST_GPR(28)
+    REST_GPR(29)
+    REST_GPR(30)
+    REST_GPR(31)
+    addi    r1,r1,STACK_FRAME
+    RFEBB
+FUNC_END(ebb_handler)
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_child_test.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_child_test.c
new file mode 100644 (file)
index 0000000..c45f948
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests we can setup an EBB on our child. Nothing interesting happens, because
+ * even though the event is enabled and running the child hasn't enabled the
+ * actual delivery of the EBBs.
+ */
+
+static int victim_child(union pipe read_pipe, union pipe write_pipe)
+{
+       int i;
+
+       FAIL_IF(wait_for_parent(read_pipe));
+       FAIL_IF(notify_parent(write_pipe));
+
+       /* Parent creates EBB event */
+
+       FAIL_IF(wait_for_parent(read_pipe));
+       FAIL_IF(notify_parent(write_pipe));
+
+       /* Check the EBB is enabled by writing PMC1 */
+       write_pmc1();
+
+       /* EBB event is enabled here */
+       for (i = 0; i < 1000000; i++) ;
+
+       return 0;
+}
+
+int ebb_on_child(void)
+{
+       union pipe read_pipe, write_pipe;
+       struct event event;
+       pid_t pid;
+
+       FAIL_IF(pipe(read_pipe.fds) == -1);
+       FAIL_IF(pipe(write_pipe.fds) == -1);
+
+       pid = fork();
+       if (pid == 0) {
+               /* NB order of pipes looks reversed */
+               exit(victim_child(write_pipe, read_pipe));
+       }
+
+       FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+       /* Child is running now */
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open_with_pid(&event, pid));
+       FAIL_IF(ebb_event_enable(&event));
+
+       FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+       /* Child should just exit happily */
+       FAIL_IF(wait_for_child(pid));
+
+       event_close(&event);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(ebb_on_child, "ebb_on_child");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c
new file mode 100644 (file)
index 0000000..11acf1d
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests we can setup an EBB on our child. The child expects this and enables
+ * EBBs, which are then delivered to the child, even though the event is
+ * created by the parent.
+ */
+
+static int victim_child(union pipe read_pipe, union pipe write_pipe)
+{
+       FAIL_IF(wait_for_parent(read_pipe));
+
+       /* Setup our EBB handler, before the EBB event is created */
+       ebb_enable_pmc_counting(1);
+       setup_ebb_handler(standard_ebb_callee);
+       ebb_global_enable();
+
+       FAIL_IF(notify_parent(write_pipe));
+
+       while (ebb_state.stats.ebb_count < 20) {
+               FAIL_IF(core_busy_loop());
+       }
+
+       ebb_global_disable();
+       ebb_freeze_pmcs();
+
+       count_pmc(1, sample_period);
+
+       dump_ebb_state();
+
+       FAIL_IF(ebb_state.stats.ebb_count == 0);
+
+       return 0;
+}
+
+/* Tests we can setup an EBB on our child - if it's expecting it */
+int ebb_on_willing_child(void)
+{
+       union pipe read_pipe, write_pipe;
+       struct event event;
+       pid_t pid;
+
+       FAIL_IF(pipe(read_pipe.fds) == -1);
+       FAIL_IF(pipe(write_pipe.fds) == -1);
+
+       pid = fork();
+       if (pid == 0) {
+               /* NB order of pipes looks reversed */
+               exit(victim_child(write_pipe, read_pipe));
+       }
+
+       /* Signal the child to setup its EBB handler */
+       FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+       /* Child is running now */
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open_with_pid(&event, pid));
+       FAIL_IF(ebb_event_enable(&event));
+
+       /* Child show now take EBBs and then exit */
+       FAIL_IF(wait_for_child(pid));
+
+       event_close(&event);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(ebb_on_willing_child, "ebb_on_willing_child");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c
new file mode 100644 (file)
index 0000000..be4dd5a
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests an EBB vs a cpu event - in that order. The EBB should force the cpu
+ * event off the PMU.
+ */
+
+static int setup_cpu_event(struct event *event, int cpu)
+{
+       event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
+
+       event->attr.exclude_kernel = 1;
+       event->attr.exclude_hv = 1;
+       event->attr.exclude_idle = 1;
+
+       SKIP_IF(require_paranoia_below(1));
+       FAIL_IF(event_open_with_cpu(event, cpu));
+       FAIL_IF(event_enable(event));
+
+       return 0;
+}
+
+int ebb_vs_cpu_event(void)
+{
+       union pipe read_pipe, write_pipe;
+       struct event event;
+       int cpu, rc;
+       pid_t pid;
+
+       cpu = pick_online_cpu();
+       FAIL_IF(cpu < 0);
+       FAIL_IF(bind_to_cpu(cpu));
+
+       FAIL_IF(pipe(read_pipe.fds) == -1);
+       FAIL_IF(pipe(write_pipe.fds) == -1);
+
+       pid = fork();
+       if (pid == 0) {
+               /* NB order of pipes looks reversed */
+               exit(ebb_child(write_pipe, read_pipe));
+       }
+
+       /* Signal the child to install its EBB event and wait */
+       FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+       /* Now try to install our CPU event */
+       rc = setup_cpu_event(&event, cpu);
+       if (rc) {
+               kill_child_and_wait(pid);
+               return rc;
+       }
+
+       /* Signal the child to run */
+       FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+       /* .. and wait for it to complete */
+       FAIL_IF(wait_for_child(pid));
+       FAIL_IF(event_disable(&event));
+       FAIL_IF(event_read(&event));
+
+       event_report(&event);
+
+       /* The cpu event may have run, but we don't expect 100% */
+       FAIL_IF(event.result.enabled >= event.result.running);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(ebb_vs_cpu_event, "ebb_vs_cpu_event");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/event_attributes_test.c b/tools/testing/selftests/powerpc/pmu/ebb/event_attributes_test.c
new file mode 100644 (file)
index 0000000..7e78153
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test various attributes of the EBB event are enforced.
+ */
+int event_attributes(void)
+{
+       struct event event, leader;
+
+       event_init(&event, 0x1001e);
+       event_leader_ebb_init(&event);
+       /* Expected to succeed */
+       FAIL_IF(event_open(&event));
+       event_close(&event);
+
+
+       event_init(&event, 0x001e); /* CYCLES - no PMC specified */
+       event_leader_ebb_init(&event);
+       /* Expected to fail, no PMC specified */
+       FAIL_IF(event_open(&event) == 0);
+
+
+       event_init(&event, 0x2001e);
+       event_leader_ebb_init(&event);
+       event.attr.exclusive = 0;
+       /* Expected to fail, not exclusive */
+       FAIL_IF(event_open(&event) == 0);
+
+
+       event_init(&event, 0x3001e);
+       event_leader_ebb_init(&event);
+       event.attr.freq = 1;
+       /* Expected to fail, sets freq */
+       FAIL_IF(event_open(&event) == 0);
+
+
+       event_init(&event, 0x4001e);
+       event_leader_ebb_init(&event);
+       event.attr.sample_period = 1;
+       /* Expected to fail, sets sample_period */
+       FAIL_IF(event_open(&event) == 0);
+
+
+       event_init(&event, 0x1001e);
+       event_leader_ebb_init(&event);
+       event.attr.enable_on_exec = 1;
+       /* Expected to fail, sets enable_on_exec */
+       FAIL_IF(event_open(&event) == 0);
+
+
+       event_init(&event, 0x1001e);
+       event_leader_ebb_init(&event);
+       event.attr.inherit = 1;
+       /* Expected to fail, sets inherit */
+       FAIL_IF(event_open(&event) == 0);
+
+
+       event_init(&leader, 0x1001e);
+       event_leader_ebb_init(&leader);
+       FAIL_IF(event_open(&leader));
+
+       event_init(&event, 0x20002);
+       event_ebb_init(&event);
+
+       /* Expected to succeed */
+       FAIL_IF(event_open_with_group(&event, leader.fd));
+       event_close(&leader);
+       event_close(&event);
+
+
+       event_init(&leader, 0x1001e);
+       event_leader_ebb_init(&leader);
+       FAIL_IF(event_open(&leader));
+
+       event_init(&event, 0x20002);
+
+       /* Expected to fail, event doesn't request EBB, leader does */
+       FAIL_IF(event_open_with_group(&event, leader.fd) == 0);
+       event_close(&leader);
+
+
+       event_init(&leader, 0x1001e);
+       event_leader_ebb_init(&leader);
+       /* Clear the EBB flag */
+       leader.attr.config &= ~(1ull << 63);
+
+       FAIL_IF(event_open(&leader));
+
+       event_init(&event, 0x20002);
+       event_ebb_init(&event);
+
+       /* Expected to fail, leader doesn't request EBB */
+       FAIL_IF(event_open_with_group(&event, leader.fd) == 0);
+       event_close(&leader);
+
+
+       event_init(&leader, 0x1001e);
+       event_leader_ebb_init(&leader);
+       leader.attr.exclusive = 0;
+       /* Expected to fail, leader isn't exclusive */
+       FAIL_IF(event_open(&leader) == 0);
+
+
+       event_init(&leader, 0x1001e);
+       event_leader_ebb_init(&leader);
+       leader.attr.pinned = 0;
+       /* Expected to fail, leader isn't pinned */
+       FAIL_IF(event_open(&leader) == 0);
+
+       event_init(&event, 0x1001e);
+       event_leader_ebb_init(&event);
+       /* Expected to fail, not a task event */
+       SKIP_IF(require_paranoia_below(1));
+       FAIL_IF(event_open_with_cpu(&event, 0) == 0);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(event_attributes, "event_attributes");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/fixed_instruction_loop.S b/tools/testing/selftests/powerpc/pmu/ebb/fixed_instruction_loop.S
new file mode 100644 (file)
index 0000000..b866a05
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <ppc-asm.h>
+
+       .text
+
+FUNC_START(thirty_two_instruction_loop)
+       cmpwi   r3,0
+       beqlr
+       addi    r4,r3,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1 # 28 addi's
+       subi    r3,r3,1
+       b       FUNC_NAME(thirty_two_instruction_loop)
+FUNC_END(thirty_two_instruction_loop)
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/fork_cleanup_test.c b/tools/testing/selftests/powerpc/pmu/ebb/fork_cleanup_test.c
new file mode 100644 (file)
index 0000000..9e7af6e
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <setjmp.h>
+#include <signal.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test that a fork clears the PMU state of the child. eg. BESCR/EBBHR/EBBRR
+ * are cleared, and MMCR0_PMCC is reset, preventing the child from accessing
+ * the PMU.
+ */
+
+static struct event event;
+
+static int child(void)
+{
+       /* Even though we have EBE=0 we can still see the EBB regs */
+       FAIL_IF(mfspr(SPRN_BESCR) != 0);
+       FAIL_IF(mfspr(SPRN_EBBHR) != 0);
+       FAIL_IF(mfspr(SPRN_EBBRR) != 0);
+
+       FAIL_IF(catch_sigill(write_pmc1));
+
+       /* We can still read from the event, though it is on our parent */
+       FAIL_IF(event_read(&event));
+
+       return 0;
+}
+
+/* Tests that fork clears EBB state */
+int fork_cleanup(void)
+{
+       pid_t pid;
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       FAIL_IF(event_open(&event));
+
+       ebb_enable_pmc_counting(1);
+       setup_ebb_handler(standard_ebb_callee);
+       ebb_global_enable();
+
+       FAIL_IF(ebb_event_enable(&event));
+
+       mtspr(SPRN_MMCR0, MMCR0_FC);
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+       /* Don't need to actually take any EBBs */
+
+       pid = fork();
+       if (pid == 0)
+               exit(child());
+
+       /* Child does the actual testing */
+       FAIL_IF(wait_for_child(pid));
+
+       /* After fork */
+       event_close(&event);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(fork_cleanup, "fork_cleanup");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c b/tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c
new file mode 100644 (file)
index 0000000..f8190fa
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/prctl.h>
+
+#include "ebb.h"
+
+
+/*
+ * Run a calibrated instruction loop and count instructions executed using
+ * EBBs. Make sure the counts look right.
+ */
+
+extern void thirty_two_instruction_loop(uint64_t loops);
+
+static bool counters_frozen = true;
+
+static int do_count_loop(struct event *event, uint64_t instructions,
+                        uint64_t overhead, bool report)
+{
+       int64_t difference, expected;
+       double percentage;
+
+       clear_ebb_stats();
+
+       counters_frozen = false;
+       mb();
+       mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
+
+       thirty_two_instruction_loop(instructions >> 5);
+
+       counters_frozen = true;
+       mb();
+       mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC);
+
+       count_pmc(4, sample_period);
+
+       event->result.value = ebb_state.stats.pmc_count[4-1];
+       expected = instructions + overhead;
+       difference = event->result.value - expected;
+       percentage = (double)difference / event->result.value * 100;
+
+       if (report) {
+               printf("Looped for %lu instructions, overhead %lu\n", instructions, overhead);
+               printf("Expected %lu\n", expected);
+               printf("Actual   %llu\n", event->result.value);
+               printf("Error    %ld, %f%%\n", difference, percentage);
+               printf("Took %d EBBs\n", ebb_state.stats.ebb_count);
+       }
+
+       if (difference < 0)
+               difference = -difference;
+
+       /* Tolerate a difference of up to 0.0001 % */
+       difference *= 10000 * 100;
+       if (difference / event->result.value)
+               return -1;
+
+       return 0;
+}
+
+/* Count how many instructions it takes to do a null loop */
+static uint64_t determine_overhead(struct event *event)
+{
+       uint64_t current, overhead;
+       int i;
+
+       do_count_loop(event, 0, 0, false);
+       overhead = event->result.value;
+
+       for (i = 0; i < 100; i++) {
+               do_count_loop(event, 0, 0, false);
+               current = event->result.value;
+               if (current < overhead) {
+                       printf("Replacing overhead %lu with %lu\n", overhead, current);
+                       overhead = current;
+               }
+       }
+
+       return overhead;
+}
+
+static void pmc4_ebb_callee(void)
+{
+       uint64_t val;
+
+       val = mfspr(SPRN_BESCR);
+       if (!(val & BESCR_PMEO)) {
+               ebb_state.stats.spurious++;
+               goto out;
+       }
+
+       ebb_state.stats.ebb_count++;
+       count_pmc(4, sample_period);
+out:
+       if (counters_frozen)
+               reset_ebb_with_clear_mask(MMCR0_PMAO);
+       else
+               reset_ebb();
+}
+
+int instruction_count(void)
+{
+       struct event event;
+       uint64_t overhead;
+
+       event_init_named(&event, 0x400FA, "PM_RUN_INST_CMPL");
+       event_leader_ebb_init(&event);
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open(&event));
+       FAIL_IF(ebb_event_enable(&event));
+
+       sample_period = COUNTER_OVERFLOW;
+
+       setup_ebb_handler(pmc4_ebb_callee);
+       mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
+       ebb_global_enable();
+
+       overhead = determine_overhead(&event);
+       printf("Overhead of null loop: %lu instructions\n", overhead);
+
+       /* Run for 1M instructions */
+       FAIL_IF(do_count_loop(&event, 0x100000, overhead, true));
+
+       /* Run for 10M instructions */
+       FAIL_IF(do_count_loop(&event, 0xa00000, overhead, true));
+
+       /* Run for 100M instructions */
+       FAIL_IF(do_count_loop(&event, 0x6400000, overhead, true));
+
+       /* Run for 1G instructions */
+       FAIL_IF(do_count_loop(&event, 0x40000000, overhead, true));
+
+       /* Run for 16G instructions */
+       FAIL_IF(do_count_loop(&event, 0x400000000, overhead, true));
+
+       /* Run for 64G instructions */
+       FAIL_IF(do_count_loop(&event, 0x1000000000, overhead, true));
+
+       /* Run for 128G instructions */
+       FAIL_IF(do_count_loop(&event, 0x2000000000, overhead, true));
+
+       ebb_global_disable();
+       event_close(&event);
+
+       printf("Finished OK\n");
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(instruction_count, "instruction_count");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c b/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c
new file mode 100644 (file)
index 0000000..0c9dd9b
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test that tries to trigger CPU_FTR_PMAO_BUG. Which is a hardware defect
+ * where an exception triggers but we context switch before it is delivered and
+ * lose the exception.
+ */
+
+static int test_body(void)
+{
+       int i, orig_period, max_period;
+       struct event event;
+
+       /* We use PMC4 to make sure the kernel switches all counters correctly */
+       event_init_named(&event, 0x40002, "instructions");
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open(&event));
+
+       ebb_enable_pmc_counting(4);
+       setup_ebb_handler(standard_ebb_callee);
+       ebb_global_enable();
+       FAIL_IF(ebb_event_enable(&event));
+
+       /*
+        * We want a low sample period, but we also want to get out of the EBB
+        * handler without tripping up again.
+        *
+        * This value picked after much experimentation.
+        */
+       orig_period = max_period = sample_period = 400;
+
+       mtspr(SPRN_PMC4, pmc_sample_period(sample_period));
+
+       while (ebb_state.stats.ebb_count < 1000000) {
+               /*
+                * We are trying to get the EBB exception to race exactly with
+                * us entering the kernel to do the syscall. We then need the
+                * kernel to decide our timeslice is up and context switch to
+                * the other thread. When we come back our EBB will have been
+                * lost and we'll spin in this while loop forever.
+                */
+
+               for (i = 0; i < 100000; i++)
+                       sched_yield();
+
+               /* Change the sample period slightly to try and hit the race */
+               if (sample_period >= (orig_period + 200))
+                       sample_period = orig_period;
+               else
+                       sample_period++;
+
+               if (sample_period > max_period)
+                       max_period = sample_period;
+       }
+
+       ebb_freeze_pmcs();
+       ebb_global_disable();
+
+       count_pmc(4, sample_period);
+       mtspr(SPRN_PMC4, 0xdead);
+
+       dump_summary_ebb_state();
+       dump_ebb_hw_state();
+
+       event_close(&event);
+
+       FAIL_IF(ebb_state.stats.ebb_count == 0);
+
+       /* We vary our sample period so we need extra fudge here */
+       FAIL_IF(!ebb_check_count(4, orig_period, 2 * (max_period - orig_period)));
+
+       return 0;
+}
+
+static int lost_exception(void)
+{
+       return eat_cpu(test_body);
+}
+
+int main(void)
+{
+       return test_harness(lost_exception, "lost_exception");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c b/tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c
new file mode 100644 (file)
index 0000000..67d78af
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test counting multiple events using EBBs.
+ */
+int multi_counter(void)
+{
+       struct event events[6];
+       int i, group_fd;
+
+       event_init_named(&events[0], 0x1001C, "PM_CMPLU_STALL_THRD");
+       event_init_named(&events[1], 0x2D016, "PM_CMPLU_STALL_FXU");
+       event_init_named(&events[2], 0x30006, "PM_CMPLU_STALL_OTHER_CMPL");
+       event_init_named(&events[3], 0x4000A, "PM_CMPLU_STALL");
+       event_init_named(&events[4], 0x600f4, "PM_RUN_CYC");
+       event_init_named(&events[5], 0x500fa, "PM_RUN_INST_CMPL");
+
+       event_leader_ebb_init(&events[0]);
+       for (i = 1; i < 6; i++)
+               event_ebb_init(&events[i]);
+
+       group_fd = -1;
+       for (i = 0; i < 6; i++) {
+               events[i].attr.exclude_kernel = 1;
+               events[i].attr.exclude_hv = 1;
+               events[i].attr.exclude_idle = 1;
+
+               FAIL_IF(event_open_with_group(&events[i], group_fd));
+               if (group_fd == -1)
+                       group_fd = events[0].fd;
+       }
+
+       ebb_enable_pmc_counting(1);
+       ebb_enable_pmc_counting(2);
+       ebb_enable_pmc_counting(3);
+       ebb_enable_pmc_counting(4);
+       ebb_enable_pmc_counting(5);
+       ebb_enable_pmc_counting(6);
+       setup_ebb_handler(standard_ebb_callee);
+
+       FAIL_IF(ioctl(events[0].fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP));
+       FAIL_IF(event_read(&events[0]));
+
+       ebb_global_enable();
+
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+       mtspr(SPRN_PMC2, pmc_sample_period(sample_period));
+       mtspr(SPRN_PMC3, pmc_sample_period(sample_period));
+       mtspr(SPRN_PMC4, pmc_sample_period(sample_period));
+       mtspr(SPRN_PMC5, pmc_sample_period(sample_period));
+       mtspr(SPRN_PMC6, pmc_sample_period(sample_period));
+
+       while (ebb_state.stats.ebb_count < 50) {
+               FAIL_IF(core_busy_loop());
+               FAIL_IF(ebb_check_mmcr0());
+       }
+
+       ebb_global_disable();
+       ebb_freeze_pmcs();
+
+       count_pmc(1, sample_period);
+       count_pmc(2, sample_period);
+       count_pmc(3, sample_period);
+       count_pmc(4, sample_period);
+       count_pmc(5, sample_period);
+       count_pmc(6, sample_period);
+
+       dump_ebb_state();
+
+       for (i = 0; i < 6; i++)
+               event_close(&events[i]);
+
+       FAIL_IF(ebb_state.stats.ebb_count == 0);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(multi_counter, "multi_counter");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c b/tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c
new file mode 100644 (file)
index 0000000..b8dc371
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test running multiple EBB using processes at once on a single CPU. They
+ * should all run happily without interfering with each other.
+ */
+
+static bool child_should_exit;
+
+static void sigint_handler(int signal)
+{
+       child_should_exit = true;
+}
+
+struct sigaction sigint_action = {
+       .sa_handler = sigint_handler,
+};
+
+static int cycles_child(void)
+{
+       struct event event;
+
+       if (sigaction(SIGINT, &sigint_action, NULL)) {
+               perror("sigaction");
+               return 1;
+       }
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open(&event));
+
+       ebb_enable_pmc_counting(1);
+       setup_ebb_handler(standard_ebb_callee);
+       ebb_global_enable();
+
+       FAIL_IF(ebb_event_enable(&event));
+
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+       while (!child_should_exit) {
+               FAIL_IF(core_busy_loop());
+               FAIL_IF(ebb_check_mmcr0());
+       }
+
+       ebb_global_disable();
+       ebb_freeze_pmcs();
+
+       count_pmc(1, sample_period);
+
+       dump_summary_ebb_state();
+
+       event_close(&event);
+
+       FAIL_IF(ebb_state.stats.ebb_count == 0);
+
+       return 0;
+}
+
+#define NR_CHILDREN    4
+
+int multi_ebb_procs(void)
+{
+       pid_t pids[NR_CHILDREN];
+       int cpu, rc, i;
+
+       cpu = pick_online_cpu();
+       FAIL_IF(cpu < 0);
+       FAIL_IF(bind_to_cpu(cpu));
+
+       for (i = 0; i < NR_CHILDREN; i++) {
+               pids[i] = fork();
+               if (pids[i] == 0)
+                       exit(cycles_child());
+       }
+
+       /* Have them all run for "a while" */
+       sleep(10);
+
+       rc = 0;
+       for (i = 0; i < NR_CHILDREN; i++) {
+               /* Tell them to stop */
+               kill(pids[i], SIGINT);
+               /* And wait */
+               rc |= wait_for_child(pids[i]);
+       }
+
+       return rc;
+}
+
+int main(void)
+{
+       return test_harness(multi_ebb_procs, "multi_ebb_procs");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/no_handler_test.c b/tools/testing/selftests/powerpc/pmu/ebb/no_handler_test.c
new file mode 100644 (file)
index 0000000..2f9bf8e
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <signal.h>
+
+#include "ebb.h"
+
+
+/* Test that things work sanely if we have no handler */
+
+static int no_handler_test(void)
+{
+       struct event event;
+       u64 val;
+       int i;
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open(&event));
+       FAIL_IF(ebb_event_enable(&event));
+
+       val = mfspr(SPRN_EBBHR);
+       FAIL_IF(val != 0);
+
+       /* Make sure it overflows quickly */
+       sample_period = 1000;
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+       /* Spin to make sure the event has time to overflow */
+       for (i = 0; i < 1000; i++)
+               mb();
+
+       dump_ebb_state();
+
+       /* We expect to see the PMU frozen & PMAO set */
+       val = mfspr(SPRN_MMCR0);
+       FAIL_IF(val != 0x0000000080000080);
+
+       event_close(&event);
+
+       dump_ebb_state();
+
+       /* The real test is that we never took an EBB at 0x0 */
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(no_handler_test,"no_handler_test");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c b/tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c
new file mode 100644 (file)
index 0000000..986500f
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <sched.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test that the kernel properly handles PMAE across context switches.
+ *
+ * We test this by calling into the kernel inside our EBB handler, where PMAE
+ * is clear. A cpu eater companion thread is running on the same CPU as us to
+ * encourage the scheduler to switch us.
+ *
+ * The kernel must make sure that when it context switches us back in, it
+ * honours the fact that we had PMAE clear.
+ *
+ * Observed to hit the failing case on the first EBB with a broken kernel.
+ */
+
+static bool mmcr0_mismatch;
+static uint64_t before, after;
+
+static void syscall_ebb_callee(void)
+{
+       uint64_t val;
+
+       val = mfspr(SPRN_BESCR);
+       if (!(val & BESCR_PMEO)) {
+               ebb_state.stats.spurious++;
+               goto out;
+       }
+
+       ebb_state.stats.ebb_count++;
+       count_pmc(1, sample_period);
+
+       before = mfspr(SPRN_MMCR0);
+
+       /* Try and get ourselves scheduled, to force a PMU context switch */
+       sched_yield();
+
+       after = mfspr(SPRN_MMCR0);
+       if (before != after)
+               mmcr0_mismatch = true;
+
+out:
+       reset_ebb();
+}
+
+static int test_body(void)
+{
+       struct event event;
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open(&event));
+
+       setup_ebb_handler(syscall_ebb_callee);
+       ebb_global_enable();
+
+       FAIL_IF(ebb_event_enable(&event));
+
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+       while (ebb_state.stats.ebb_count < 20 && !mmcr0_mismatch)
+               FAIL_IF(core_busy_loop());
+
+       ebb_global_disable();
+       ebb_freeze_pmcs();
+
+       count_pmc(1, sample_period);
+
+       dump_ebb_state();
+
+       if (mmcr0_mismatch)
+               printf("Saw MMCR0 before 0x%lx after 0x%lx\n", before, after);
+
+       event_close(&event);
+
+       FAIL_IF(ebb_state.stats.ebb_count == 0);
+       FAIL_IF(mmcr0_mismatch);
+
+       return 0;
+}
+
+int pmae_handling(void)
+{
+       return eat_cpu(test_body);
+}
+
+int main(void)
+{
+       return test_harness(pmae_handling, "pmae_handling");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c b/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c
new file mode 100644 (file)
index 0000000..a503fa7
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test that PMC5 & 6 are frozen (ie. don't overflow) when they are not being
+ * used. Tests the MMCR0_FC56 logic in the kernel.
+ */
+
+static int pmc56_overflowed;
+
+static void ebb_callee(void)
+{
+       uint64_t val;
+
+       val = mfspr(SPRN_BESCR);
+       if (!(val & BESCR_PMEO)) {
+               ebb_state.stats.spurious++;
+               goto out;
+       }
+
+       ebb_state.stats.ebb_count++;
+       count_pmc(2, sample_period);
+
+       val = mfspr(SPRN_PMC5);
+       if (val >= COUNTER_OVERFLOW)
+               pmc56_overflowed++;
+
+       count_pmc(5, COUNTER_OVERFLOW);
+
+       val = mfspr(SPRN_PMC6);
+       if (val >= COUNTER_OVERFLOW)
+               pmc56_overflowed++;
+
+       count_pmc(6, COUNTER_OVERFLOW);
+
+out:
+       reset_ebb();
+}
+
+int pmc56_overflow(void)
+{
+       struct event event;
+
+       /* Use PMC2 so we set PMCjCE, which enables PMC5/6 */
+       event_init(&event, 0x2001e);
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open(&event));
+
+       setup_ebb_handler(ebb_callee);
+       ebb_global_enable();
+
+       FAIL_IF(ebb_event_enable(&event));
+
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+       mtspr(SPRN_PMC5, 0);
+       mtspr(SPRN_PMC6, 0);
+
+       while (ebb_state.stats.ebb_count < 10)
+               FAIL_IF(core_busy_loop());
+
+       ebb_global_disable();
+       ebb_freeze_pmcs();
+
+       count_pmc(2, sample_period);
+
+       dump_ebb_state();
+
+       printf("PMC5/6 overflow %d\n", pmc56_overflowed);
+
+       event_close(&event);
+
+       FAIL_IF(ebb_state.stats.ebb_count == 0 || pmc56_overflowed != 0);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(pmc56_overflow, "pmc56_overflow");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/reg.h b/tools/testing/selftests/powerpc/pmu/ebb/reg.h
new file mode 100644 (file)
index 0000000..5921b0d
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#ifndef _SELFTESTS_POWERPC_REG_H
+#define _SELFTESTS_POWERPC_REG_H
+
+#define __stringify_1(x)        #x
+#define __stringify(x)          __stringify_1(x)
+
+#define mfspr(rn)       ({unsigned long rval; \
+                         asm volatile("mfspr %0," __stringify(rn) \
+                                 : "=r" (rval)); rval; })
+#define mtspr(rn, v)    asm volatile("mtspr " __stringify(rn) ",%0" : \
+                                    : "r" ((unsigned long)(v)) \
+                                    : "memory")
+
+#define mb()           asm volatile("sync" : : : "memory");
+
+#define SPRN_MMCR2     769
+#define SPRN_MMCRA     770
+#define SPRN_MMCR0     779
+#define   MMCR0_PMAO   0x00000080
+#define   MMCR0_PMAE   0x04000000
+#define   MMCR0_FC     0x80000000
+#define SPRN_EBBHR     804
+#define SPRN_EBBRR     805
+#define SPRN_BESCR     806     /* Branch event status & control register */
+#define SPRN_BESCRS    800     /* Branch event status & control set (1 bits set to 1) */
+#define SPRN_BESCRSU   801     /* Branch event status & control set upper */
+#define SPRN_BESCRR    802     /* Branch event status & control REset (1 bits set to 0) */
+#define SPRN_BESCRRU   803     /* Branch event status & control REset upper */
+
+#define BESCR_PMEO     0x1     /* PMU Event-based exception Occurred */
+#define BESCR_PME      (0x1ul << 32) /* PMU Event-based exception Enable */
+
+#define SPRN_PMC1      771
+#define SPRN_PMC2      772
+#define SPRN_PMC3      773
+#define SPRN_PMC4      774
+#define SPRN_PMC5      775
+#define SPRN_PMC6      776
+
+#define SPRN_SIAR      780
+#define SPRN_SDAR      781
+#define SPRN_SIER      768
+
+#endif /* _SELFTESTS_POWERPC_REG_H */
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/reg_access_test.c b/tools/testing/selftests/powerpc/pmu/ebb/reg_access_test.c
new file mode 100644 (file)
index 0000000..0cae66f
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ebb.h"
+#include "reg.h"
+
+
+/*
+ * Test basic access to the EBB regs, they should be user accessible with no
+ * kernel interaction required.
+ */
+int reg_access(void)
+{
+       uint64_t val, expected;
+
+       expected = 0x8000000100000000ull;
+       mtspr(SPRN_BESCR, expected);
+       val = mfspr(SPRN_BESCR);
+
+       FAIL_IF(val != expected);
+
+       expected = 0x0000000001000000ull;
+       mtspr(SPRN_EBBHR, expected);
+       val = mfspr(SPRN_EBBHR);
+
+       FAIL_IF(val != expected);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(reg_access, "reg_access");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/task_event_pinned_vs_ebb_test.c b/tools/testing/selftests/powerpc/pmu/ebb/task_event_pinned_vs_ebb_test.c
new file mode 100644 (file)
index 0000000..d56607e
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests a pinned per-task event vs an EBB - in that order. The pinned per-task
+ * event should prevent the EBB event from being enabled.
+ */
+
+static int setup_child_event(struct event *event, pid_t child_pid)
+{
+       event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
+
+       event->attr.pinned = 1;
+
+       event->attr.exclude_kernel = 1;
+       event->attr.exclude_hv = 1;
+       event->attr.exclude_idle = 1;
+
+       FAIL_IF(event_open_with_pid(event, child_pid));
+       FAIL_IF(event_enable(event));
+
+       return 0;
+}
+
+int task_event_pinned_vs_ebb(void)
+{
+       union pipe read_pipe, write_pipe;
+       struct event event;
+       pid_t pid;
+       int rc;
+
+       FAIL_IF(pipe(read_pipe.fds) == -1);
+       FAIL_IF(pipe(write_pipe.fds) == -1);
+
+       pid = fork();
+       if (pid == 0) {
+               /* NB order of pipes looks reversed */
+               exit(ebb_child(write_pipe, read_pipe));
+       }
+
+       /* We setup the task event first */
+       rc = setup_child_event(&event, pid);
+       if (rc) {
+               kill_child_and_wait(pid);
+               return rc;
+       }
+
+       /* Signal the child to install its EBB event and wait */
+       if (sync_with_child(read_pipe, write_pipe))
+               /* If it fails, wait for it to exit */
+               goto wait;
+
+       /* Signal the child to run */
+       FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+wait:
+       /* We expect it to fail to read the event */
+       FAIL_IF(wait_for_child(pid) != 2);
+       FAIL_IF(event_disable(&event));
+       FAIL_IF(event_read(&event));
+
+       event_report(&event);
+
+       FAIL_IF(event.result.value == 0);
+       /*
+        * For reasons I don't understand enabled is usually just slightly
+        * lower than running. Would be good to confirm why.
+        */
+       FAIL_IF(event.result.enabled == 0);
+       FAIL_IF(event.result.running == 0);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(task_event_pinned_vs_ebb, "task_event_pinned_vs_ebb");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/task_event_vs_ebb_test.c b/tools/testing/selftests/powerpc/pmu/ebb/task_event_vs_ebb_test.c
new file mode 100644 (file)
index 0000000..eba3219
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests a per-task event vs an EBB - in that order. The EBB should push the
+ * per-task event off the PMU.
+ */
+
+static int setup_child_event(struct event *event, pid_t child_pid)
+{
+       event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
+
+       event->attr.exclude_kernel = 1;
+       event->attr.exclude_hv = 1;
+       event->attr.exclude_idle = 1;
+
+       FAIL_IF(event_open_with_pid(event, child_pid));
+       FAIL_IF(event_enable(event));
+
+       return 0;
+}
+
+int task_event_vs_ebb(void)
+{
+       union pipe read_pipe, write_pipe;
+       struct event event;
+       pid_t pid;
+       int rc;
+
+       FAIL_IF(pipe(read_pipe.fds) == -1);
+       FAIL_IF(pipe(write_pipe.fds) == -1);
+
+       pid = fork();
+       if (pid == 0) {
+               /* NB order of pipes looks reversed */
+               exit(ebb_child(write_pipe, read_pipe));
+       }
+
+       /* We setup the task event first */
+       rc = setup_child_event(&event, pid);
+       if (rc) {
+               kill_child_and_wait(pid);
+               return rc;
+       }
+
+       /* Signal the child to install its EBB event and wait */
+       if (sync_with_child(read_pipe, write_pipe))
+               /* If it fails, wait for it to exit */
+               goto wait;
+
+       /* Signal the child to run */
+       FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+wait:
+       /* The EBB event should push the task event off so the child should succeed */
+       FAIL_IF(wait_for_child(pid));
+       FAIL_IF(event_disable(&event));
+       FAIL_IF(event_read(&event));
+
+       event_report(&event);
+
+       /* The task event may have run, or not so we can't assert anything about it */
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(task_event_vs_ebb, "task_event_vs_ebb");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/trace.c b/tools/testing/selftests/powerpc/pmu/ebb/trace.c
new file mode 100644 (file)
index 0000000..251e66a
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include "trace.h"
+
+
+struct trace_buffer *trace_buffer_allocate(u64 size)
+{
+       struct trace_buffer *tb;
+
+       if (size < sizeof(*tb)) {
+               fprintf(stderr, "Error: trace buffer too small\n");
+               return NULL;
+       }
+
+       tb = mmap(NULL, size, PROT_READ | PROT_WRITE,
+                 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+       if (tb == MAP_FAILED) {
+               perror("mmap");
+               return NULL;
+       }
+
+       tb->size = size;
+       tb->tail = tb->data;
+       tb->overflow = false;
+
+       return tb;
+}
+
+static bool trace_check_bounds(struct trace_buffer *tb, void *p)
+{
+       return p < ((void *)tb + tb->size);
+}
+
+static bool trace_check_alloc(struct trace_buffer *tb, void *p)
+{
+       /*
+        * If we ever overflowed don't allow any more input. This prevents us
+        * from dropping a large item and then later logging a small one. The
+        * buffer should just stop when overflow happened, not be patchy. If
+        * you're overflowing, make your buffer bigger.
+        */
+       if (tb->overflow)
+               return false;
+
+       if (!trace_check_bounds(tb, p)) {
+               tb->overflow = true;
+               return false;
+       }
+
+       return true;
+}
+
+static void *trace_alloc(struct trace_buffer *tb, int bytes)
+{
+       void *p, *newtail;
+
+       p = tb->tail;
+       newtail = tb->tail + bytes;
+       if (!trace_check_alloc(tb, newtail))
+               return NULL;
+
+       tb->tail = newtail;
+
+       return p;
+}
+
+static struct trace_entry *trace_alloc_entry(struct trace_buffer *tb, int payload_size)
+{
+       struct trace_entry *e;
+
+       e = trace_alloc(tb, sizeof(*e) + payload_size);
+       if (e)
+               e->length = payload_size;
+
+       return e;
+}
+
+int trace_log_reg(struct trace_buffer *tb, u64 reg, u64 value)
+{
+       struct trace_entry *e;
+       u64 *p;
+
+       e = trace_alloc_entry(tb, sizeof(reg) + sizeof(value));
+       if (!e)
+               return -ENOSPC;
+
+       e->type = TRACE_TYPE_REG;
+       p = (u64 *)e->data;
+       *p++ = reg;
+       *p++ = value;
+
+       return 0;
+}
+
+int trace_log_counter(struct trace_buffer *tb, u64 value)
+{
+       struct trace_entry *e;
+       u64 *p;
+
+       e = trace_alloc_entry(tb, sizeof(value));
+       if (!e)
+               return -ENOSPC;
+
+       e->type = TRACE_TYPE_COUNTER;
+       p = (u64 *)e->data;
+       *p++ = value;
+
+       return 0;
+}
+
+int trace_log_string(struct trace_buffer *tb, char *str)
+{
+       struct trace_entry *e;
+       char *p;
+       int len;
+
+       len = strlen(str);
+
+       /* We NULL terminate to make printing easier */
+       e = trace_alloc_entry(tb, len + 1);
+       if (!e)
+               return -ENOSPC;
+
+       e->type = TRACE_TYPE_STRING;
+       p = (char *)e->data;
+       memcpy(p, str, len);
+       p += len;
+       *p = '\0';
+
+       return 0;
+}
+
+int trace_log_indent(struct trace_buffer *tb)
+{
+       struct trace_entry *e;
+
+       e = trace_alloc_entry(tb, 0);
+       if (!e)
+               return -ENOSPC;
+
+       e->type = TRACE_TYPE_INDENT;
+
+       return 0;
+}
+
+int trace_log_outdent(struct trace_buffer *tb)
+{
+       struct trace_entry *e;
+
+       e = trace_alloc_entry(tb, 0);
+       if (!e)
+               return -ENOSPC;
+
+       e->type = TRACE_TYPE_OUTDENT;
+
+       return 0;
+}
+
+static void trace_print_header(int seq, int prefix)
+{
+       printf("%*s[%d]: ", prefix, "", seq);
+}
+
+static char *trace_decode_reg(int reg)
+{
+       switch (reg) {
+               case 769: return "SPRN_MMCR2"; break;
+               case 770: return "SPRN_MMCRA"; break;
+               case 779: return "SPRN_MMCR0"; break;
+               case 804: return "SPRN_EBBHR"; break;
+               case 805: return "SPRN_EBBRR"; break;
+               case 806: return "SPRN_BESCR"; break;
+               case 800: return "SPRN_BESCRS"; break;
+               case 801: return "SPRN_BESCRSU"; break;
+               case 802: return "SPRN_BESCRR"; break;
+               case 803: return "SPRN_BESCRRU"; break;
+               case 771: return "SPRN_PMC1"; break;
+               case 772: return "SPRN_PMC2"; break;
+               case 773: return "SPRN_PMC3"; break;
+               case 774: return "SPRN_PMC4"; break;
+               case 775: return "SPRN_PMC5"; break;
+               case 776: return "SPRN_PMC6"; break;
+               case 780: return "SPRN_SIAR"; break;
+               case 781: return "SPRN_SDAR"; break;
+               case 768: return "SPRN_SIER"; break;
+       }
+
+       return NULL;
+}
+
+static void trace_print_reg(struct trace_entry *e)
+{
+       u64 *p, *reg, *value;
+       char *name;
+
+       p = (u64 *)e->data;
+       reg = p++;
+       value = p;
+
+       name = trace_decode_reg(*reg);
+       if (name)
+               printf("register %-10s = 0x%016llx\n", name, *value);
+       else
+               printf("register %lld = 0x%016llx\n", *reg, *value);
+}
+
+static void trace_print_counter(struct trace_entry *e)
+{
+       u64 *value;
+
+       value = (u64 *)e->data;
+       printf("counter = %lld\n", *value);
+}
+
+static void trace_print_string(struct trace_entry *e)
+{
+       char *str;
+
+       str = (char *)e->data;
+       puts(str);
+}
+
+#define BASE_PREFIX    2
+#define PREFIX_DELTA   8
+
+static void trace_print_entry(struct trace_entry *e, int seq, int *prefix)
+{
+       switch (e->type) {
+       case TRACE_TYPE_REG:
+               trace_print_header(seq, *prefix);
+               trace_print_reg(e);
+               break;
+       case TRACE_TYPE_COUNTER:
+               trace_print_header(seq, *prefix);
+               trace_print_counter(e);
+               break;
+       case TRACE_TYPE_STRING:
+               trace_print_header(seq, *prefix);
+               trace_print_string(e);
+               break;
+       case TRACE_TYPE_INDENT:
+               trace_print_header(seq, *prefix);
+               puts("{");
+               *prefix += PREFIX_DELTA;
+               break;
+       case TRACE_TYPE_OUTDENT:
+               *prefix -= PREFIX_DELTA;
+               if (*prefix < BASE_PREFIX)
+                       *prefix = BASE_PREFIX;
+               trace_print_header(seq, *prefix);
+               puts("}");
+               break;
+       default:
+               trace_print_header(seq, *prefix);
+               printf("entry @ %p type %d\n", e, e->type);
+               break;
+       }
+}
+
+void trace_buffer_print(struct trace_buffer *tb)
+{
+       struct trace_entry *e;
+       int i, prefix;
+       void *p;
+
+       printf("Trace buffer dump:\n");
+       printf("  address  %p \n", tb);
+       printf("  tail     %p\n", tb->tail);
+       printf("  size     %llu\n", tb->size);
+       printf("  overflow %s\n", tb->overflow ? "TRUE" : "false");
+       printf("  Content:\n");
+
+       p = tb->data;
+
+       i = 0;
+       prefix = BASE_PREFIX;
+
+       while (trace_check_bounds(tb, p) && p < tb->tail) {
+               e = p;
+
+               trace_print_entry(e, i, &prefix);
+
+               i++;
+               p = (void *)e + sizeof(*e) + e->length;
+       }
+}
+
+void trace_print_location(struct trace_buffer *tb)
+{
+       printf("Trace buffer 0x%llx bytes @ %p\n", tb->size, tb);
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/trace.h b/tools/testing/selftests/powerpc/pmu/ebb/trace.h
new file mode 100644 (file)
index 0000000..926458e
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#ifndef _SELFTESTS_POWERPC_PMU_EBB_TRACE_H
+#define _SELFTESTS_POWERPC_PMU_EBB_TRACE_H
+
+#include "utils.h"
+
+#define TRACE_TYPE_REG         1
+#define TRACE_TYPE_COUNTER     2
+#define TRACE_TYPE_STRING      3
+#define TRACE_TYPE_INDENT      4
+#define TRACE_TYPE_OUTDENT     5
+
+struct trace_entry
+{
+       u8 type;
+       u8 length;
+       u8 data[0];
+};
+
+struct trace_buffer
+{
+       u64  size;
+       bool overflow;
+       void *tail;
+       u8   data[0];
+};
+
+struct trace_buffer *trace_buffer_allocate(u64 size);
+int trace_log_reg(struct trace_buffer *tb, u64 reg, u64 value);
+int trace_log_counter(struct trace_buffer *tb, u64 value);
+int trace_log_string(struct trace_buffer *tb, char *str);
+int trace_log_indent(struct trace_buffer *tb);
+int trace_log_outdent(struct trace_buffer *tb);
+void trace_buffer_print(struct trace_buffer *tb);
+void trace_print_location(struct trace_buffer *tb);
+
+#endif /* _SELFTESTS_POWERPC_PMU_EBB_TRACE_H */
index 2b2d11df24508cfb61ea8e0920f28c2f3ba0ac37..184b36807d48c197d4f6471c58bd74f62c9fd4e2 100644 (file)
@@ -39,7 +39,13 @@ void event_init_named(struct event *e, u64 config, char *name)
        event_init_opts(e, config, PERF_TYPE_RAW, name);
 }
 
+void event_init(struct event *e, u64 config)
+{
+       event_init_opts(e, config, PERF_TYPE_RAW, "event");
+}
+
 #define PERF_CURRENT_PID       0
+#define PERF_NO_PID            -1
 #define PERF_NO_CPU            -1
 #define PERF_NO_GROUP          -1
 
@@ -59,6 +65,16 @@ int event_open_with_group(struct event *e, int group_fd)
        return event_open_with_options(e, PERF_CURRENT_PID, PERF_NO_CPU, group_fd);
 }
 
+int event_open_with_pid(struct event *e, pid_t pid)
+{
+       return event_open_with_options(e, pid, PERF_NO_CPU, PERF_NO_GROUP);
+}
+
+int event_open_with_cpu(struct event *e, int cpu)
+{
+       return event_open_with_options(e, PERF_NO_PID, cpu, PERF_NO_GROUP);
+}
+
 int event_open(struct event *e)
 {
        return event_open_with_options(e, PERF_CURRENT_PID, PERF_NO_CPU, PERF_NO_GROUP);
@@ -69,6 +85,16 @@ void event_close(struct event *e)
        close(e->fd);
 }
 
+int event_enable(struct event *e)
+{
+       return ioctl(e->fd, PERF_EVENT_IOC_ENABLE);
+}
+
+int event_disable(struct event *e)
+{
+       return ioctl(e->fd, PERF_EVENT_IOC_DISABLE);
+}
+
 int event_reset(struct event *e)
 {
        return ioctl(e->fd, PERF_EVENT_IOC_RESET);
index e6993192ff34b4726b76ec84440fa18d5a91f31e..a0ea6b1eef73464fa0af44c091068c7ea231a66d 100644 (file)
@@ -29,8 +29,12 @@ void event_init_named(struct event *e, u64 config, char *name);
 void event_init_opts(struct event *e, u64 config, int type, char *name);
 int event_open_with_options(struct event *e, pid_t pid, int cpu, int group_fd);
 int event_open_with_group(struct event *e, int group_fd);
+int event_open_with_pid(struct event *e, pid_t pid);
+int event_open_with_cpu(struct event *e, int cpu);
 int event_open(struct event *e);
 void event_close(struct event *e);
+int event_enable(struct event *e);
+int event_disable(struct event *e);
 int event_reset(struct event *e);
 int event_read(struct event *e);
 void event_report_justified(struct event *e, int name_width, int result_width);
diff --git a/tools/testing/selftests/powerpc/pmu/lib.c b/tools/testing/selftests/powerpc/pmu/lib.c
new file mode 100644 (file)
index 0000000..0f6a473
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#define _GNU_SOURCE    /* For CPU_ZERO etc. */
+
+#include <errno.h>
+#include <sched.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+
+#include "utils.h"
+#include "lib.h"
+
+
+int pick_online_cpu(void)
+{
+       cpu_set_t mask;
+       int cpu;
+
+       CPU_ZERO(&mask);
+
+       if (sched_getaffinity(0, sizeof(mask), &mask)) {
+               perror("sched_getaffinity");
+               return -1;
+       }
+
+       /* We prefer a primary thread, but skip 0 */
+       for (cpu = 8; cpu < CPU_SETSIZE; cpu += 8)
+               if (CPU_ISSET(cpu, &mask))
+                       return cpu;
+
+       /* Search for anything, but in reverse */
+       for (cpu = CPU_SETSIZE - 1; cpu >= 0; cpu--)
+               if (CPU_ISSET(cpu, &mask))
+                       return cpu;
+
+       printf("No cpus in affinity mask?!\n");
+       return -1;
+}
+
+int bind_to_cpu(int cpu)
+{
+       cpu_set_t mask;
+
+       printf("Binding to cpu %d\n", cpu);
+
+       CPU_ZERO(&mask);
+       CPU_SET(cpu, &mask);
+
+       return sched_setaffinity(0, sizeof(mask), &mask);
+}
+
+#define PARENT_TOKEN   0xAA
+#define CHILD_TOKEN    0x55
+
+int sync_with_child(union pipe read_pipe, union pipe write_pipe)
+{
+       char c = PARENT_TOKEN;
+
+       FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
+       FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
+       if (c != CHILD_TOKEN) /* sometimes expected */
+               return 1;
+
+       return 0;
+}
+
+int wait_for_parent(union pipe read_pipe)
+{
+       char c;
+
+       FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
+       FAIL_IF(c != PARENT_TOKEN);
+
+       return 0;
+}
+
+int notify_parent(union pipe write_pipe)
+{
+       char c = CHILD_TOKEN;
+
+       FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
+
+       return 0;
+}
+
+int notify_parent_of_error(union pipe write_pipe)
+{
+       char c = ~CHILD_TOKEN;
+
+       FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
+
+       return 0;
+}
+
+int wait_for_child(pid_t child_pid)
+{
+       int rc;
+
+       if (waitpid(child_pid, &rc, 0) == -1) {
+               perror("waitpid");
+               return 1;
+       }
+
+       if (WIFEXITED(rc))
+               rc = WEXITSTATUS(rc);
+       else
+               rc = 1; /* Signal or other */
+
+       return rc;
+}
+
+int kill_child_and_wait(pid_t child_pid)
+{
+       kill(child_pid, SIGTERM);
+
+       return wait_for_child(child_pid);
+}
+
+static int eat_cpu_child(union pipe read_pipe, union pipe write_pipe)
+{
+       volatile int i = 0;
+
+       /*
+        * We are just here to eat cpu and die. So make sure we can be killed,
+        * and also don't do any custom SIGTERM handling.
+        */
+       signal(SIGTERM, SIG_DFL);
+
+       notify_parent(write_pipe);
+       wait_for_parent(read_pipe);
+
+       /* Soak up cpu forever */
+       while (1) i++;
+
+       return 0;
+}
+
+pid_t eat_cpu(int (test_function)(void))
+{
+       union pipe read_pipe, write_pipe;
+       int cpu, rc;
+       pid_t pid;
+
+       cpu = pick_online_cpu();
+       FAIL_IF(cpu < 0);
+       FAIL_IF(bind_to_cpu(cpu));
+
+       if (pipe(read_pipe.fds) == -1)
+               return -1;
+
+       if (pipe(write_pipe.fds) == -1)
+               return -1;
+
+       pid = fork();
+       if (pid == 0)
+               exit(eat_cpu_child(write_pipe, read_pipe));
+
+       if (sync_with_child(read_pipe, write_pipe)) {
+               rc = -1;
+               goto out;
+       }
+
+       printf("main test running as pid %d\n", getpid());
+
+       rc = test_function();
+out:
+       kill(pid, SIGKILL);
+
+       return rc;
+}
+
+struct addr_range libc, vdso;
+
+int parse_proc_maps(void)
+{
+       char execute, name[128];
+       uint64_t start, end;
+       FILE *f;
+       int rc;
+
+       f = fopen("/proc/self/maps", "r");
+       if (!f) {
+               perror("fopen");
+               return -1;
+       }
+
+       do {
+               /* This skips line with no executable which is what we want */
+               rc = fscanf(f, "%lx-%lx %*c%*c%c%*c %*x %*d:%*d %*d %127s\n",
+                           &start, &end, &execute, name);
+               if (rc <= 0)
+                       break;
+
+               if (execute != 'x')
+                       continue;
+
+               if (strstr(name, "libc")) {
+                       libc.first = start;
+                       libc.last = end - 1;
+               } else if (strstr(name, "[vdso]")) {
+                       vdso.first = start;
+                       vdso.last = end - 1;
+               }
+       } while(1);
+
+       fclose(f);
+
+       return 0;
+}
+
+#define PARANOID_PATH  "/proc/sys/kernel/perf_event_paranoid"
+
+bool require_paranoia_below(int level)
+{
+       unsigned long current;
+       char *end, buf[16];
+       FILE *f;
+       int rc;
+
+       rc = -1;
+
+       f = fopen(PARANOID_PATH, "r");
+       if (!f) {
+               perror("fopen");
+               goto out;
+       }
+
+       if (!fgets(buf, sizeof(buf), f)) {
+               printf("Couldn't read " PARANOID_PATH "?\n");
+               goto out_close;
+       }
+
+       current = strtoul(buf, &end, 10);
+
+       if (end == buf) {
+               printf("Couldn't parse " PARANOID_PATH "?\n");
+               goto out_close;
+       }
+
+       if (current >= level)
+               goto out;
+
+       rc = 0;
+out_close:
+       fclose(f);
+out:
+       return rc;
+}
diff --git a/tools/testing/selftests/powerpc/pmu/lib.h b/tools/testing/selftests/powerpc/pmu/lib.h
new file mode 100644 (file)
index 0000000..ca5d72a
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#ifndef __SELFTESTS_POWERPC_PMU_LIB_H
+#define __SELFTESTS_POWERPC_PMU_LIB_H
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+union pipe {
+       struct {
+               int read_fd;
+               int write_fd;
+       };
+       int fds[2];
+};
+
+extern int pick_online_cpu(void);
+extern int bind_to_cpu(int cpu);
+extern int kill_child_and_wait(pid_t child_pid);
+extern int wait_for_child(pid_t child_pid);
+extern int sync_with_child(union pipe read_pipe, union pipe write_pipe);
+extern int wait_for_parent(union pipe read_pipe);
+extern int notify_parent(union pipe write_pipe);
+extern int notify_parent_of_error(union pipe write_pipe);
+extern pid_t eat_cpu(int (test_function)(void));
+extern bool require_paranoia_below(int level);
+
+struct addr_range {
+       uint64_t first, last;
+};
+
+extern struct addr_range libc, vdso;
+
+int parse_proc_maps(void);
+
+#endif /* __SELFTESTS_POWERPC_PMU_LIB_H */
index 8820e3df144460e3dc44d60af83c25443755bd4b..20c1f0876c4772492f34b6f4215257586f73aff9 100644 (file)
@@ -3,44 +3,41 @@
  * Licensed under GPLv2.
  */
 
+#include <ppc-asm.h>
+
        .text
 
-       .global thirty_two_instruction_loop
-       .type .thirty_two_instruction_loop,@function
-       .section ".opd","aw",@progbits
-thirty_two_instruction_loop:
-       .quad .thirty_two_instruction_loop, .TOC.@tocbase, 0
-       .previous
-.thirty_two_instruction_loop:
-       cmpwi   %r3,0
+FUNC_START(thirty_two_instruction_loop)
+       cmpdi   r3,0
        beqlr
-       addi    %r4,%r3,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1       # 28 addi's
-       subi    %r3,%r3,1
-       b       .thirty_two_instruction_loop
+       addi    r4,r3,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1 # 28 addi's
+       subi    r3,r3,1
+       b       FUNC_NAME(thirty_two_instruction_loop)
+FUNC_END(thirty_two_instruction_loop)
index 98a22920792d812b931fe7c5e7b86af58ba4ccec..9c6c4e901ab617671c669315bcc62280362b9104 100644 (file)
@@ -26,6 +26,11 @@ static inline void test_error(char *name)
        printf("error: %s\n", name);
 }
 
+static inline void test_skip(char *name)
+{
+       printf("skip: %s\n", name);
+}
+
 static inline void test_success(char *name)
 {
        printf("success: %s\n", name);
diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile
new file mode 100644 (file)
index 0000000..51267f4
--- /dev/null
@@ -0,0 +1,15 @@
+PROGS := tm-resched-dscr
+
+all: $(PROGS)
+
+$(PROGS):
+
+run_tests: all
+       @-for PROG in $(PROGS); do \
+               ./$$PROG; \
+       done;
+
+clean:
+       rm -f $(PROGS) *.o
+
+.PHONY: all run_tests clean
diff --git a/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c b/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c
new file mode 100644 (file)
index 0000000..ee98e38
--- /dev/null
@@ -0,0 +1,90 @@
+/* Test context switching to see if the DSCR SPR is correctly preserved
+ * when within a transaction.
+ *
+ * Note: We assume that the DSCR has been left at the default value (0)
+ * for all CPUs.
+ *
+ * Method:
+ *
+ * Set a value into the DSCR.
+ *
+ * Start a transaction, and suspend it (*).
+ *
+ * Hard loop checking to see if the transaction has become doomed.
+ *
+ * Now that we *may* have been preempted, record the DSCR and TEXASR SPRS.
+ *
+ * If the abort was because of a context switch, check the DSCR value.
+ * Otherwise, try again.
+ *
+ * (*) If the transaction is not suspended we can't see the problem because
+ * the transaction abort handler will restore the DSCR to it's checkpointed
+ * value before we regain control.
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <asm/tm.h>
+
+#define TBEGIN          ".long 0x7C00051D ;"
+#define TEND            ".long 0x7C00055D ;"
+#define TCHECK          ".long 0x7C00059C ;"
+#define TSUSPEND        ".long 0x7C0005DD ;"
+#define TRESUME         ".long 0x7C2005DD ;"
+#define SPRN_TEXASR     0x82
+#define SPRN_DSCR       0x03
+
+int main(void) {
+       uint64_t rv, dscr1 = 1, dscr2, texasr;
+
+       printf("Check DSCR TM context switch: ");
+       fflush(stdout);
+       for (;;) {
+               rv = 1;
+               asm __volatile__ (
+                       /* set a known value into the DSCR */
+                       "ld      3, %[dscr1];"
+                       "mtspr   %[sprn_dscr], 3;"
+
+                       /* start and suspend a transaction */
+                       TBEGIN
+                       "beq     1f;"
+                       TSUSPEND
+
+                       /* hard loop until the transaction becomes doomed */
+                       "2: ;"
+                       TCHECK
+                       "bc      4, 0, 2b;"
+
+                       /* record DSCR and TEXASR */
+                       "mfspr   3, %[sprn_dscr];"
+                       "std     3, %[dscr2];"
+                       "mfspr   3, %[sprn_texasr];"
+                       "std     3, %[texasr];"
+
+                       TRESUME
+                       TEND
+                       "li      %[rv], 0;"
+                       "1: ;"
+                       : [rv]"=r"(rv), [dscr2]"=m"(dscr2), [texasr]"=m"(texasr)
+                       : [dscr1]"m"(dscr1)
+                       , [sprn_dscr]"i"(SPRN_DSCR), [sprn_texasr]"i"(SPRN_TEXASR)
+                       : "memory", "r3"
+               );
+               assert(rv); /* make sure the transaction aborted */
+               if ((texasr >> 56) != TM_CAUSE_RESCHED) {
+                       putchar('.');
+                       fflush(stdout);
+                       continue;
+               }
+               if (dscr2 != dscr1) {
+                       printf(" FAIL\n");
+                       exit(EXIT_FAILURE);
+               } else {
+                       printf(" OK\n");
+                       exit(EXIT_SUCCESS);
+               }
+       }
+}
index 0de064406dabf5d7589f9cc094f133e12b1798a1..a93777ae06846a4df47e5fbb5241af92cb6d6928 100644 (file)
@@ -31,6 +31,18 @@ do {                                                         \
        }                                                       \
 } while (0)
 
+/* The test harness uses this, yes it's gross */
+#define MAGIC_SKIP_RETURN_VALUE        99
+
+#define SKIP_IF(x)                                             \
+do {                                                           \
+       if ((x)) {                                              \
+               fprintf(stderr,                                 \
+               "[SKIP] Test skipped on line %d\n", __LINE__);  \
+               return MAGIC_SKIP_RETURN_VALUE;                 \
+       }                                                       \
+} while (0)
+
 #define _str(s) #s
 #define str(s) _str(s)
 
index c86be0f983db706c81cfe60aedfb77f4a315e20f..4b6c01b477f9cf86df4a69504de6940c5f6bebbf 100644 (file)
@@ -1714,11 +1714,11 @@ void kvm_vcpu_kick(struct kvm_vcpu *vcpu)
 EXPORT_SYMBOL_GPL(kvm_vcpu_kick);
 #endif /* !CONFIG_S390 */
 
-bool kvm_vcpu_yield_to(struct kvm_vcpu *target)
+int kvm_vcpu_yield_to(struct kvm_vcpu *target)
 {
        struct pid *pid;
        struct task_struct *task = NULL;
-       bool ret = false;
+       int ret = 0;
 
        rcu_read_lock();
        pid = rcu_dereference(target->pid);